// The OS/K Team licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #include // // Imperatively read the "DECD" file before reading this code // static void check_param_type(ctx_t *ctx, uint prm, uchar fmt) { bool ok; if (prm == P_REG) ok = (fmt == A_REG); else if (prm == P_IMM) ok = (fmt == A_IMM64); else /* if (prm == P_MEM */ ok = ACC_FMT_IS_MEM(fmt); if (!ok) _except(ctx, E_ILL, "FT1 or FT2 not matching INSTR's expected parameter types"); } void decode(ctx_t *ctx) { char *illmsg; instr_t *in; acc_t p1 = { 0 }; acc_t p2 = { 0 }; bool rep = 0, cond = 0; bool lock, nomore; ushort w1, w2; uchar f1 = 0, f2 = 0; ulong pc = rip; // // Process the first word of the instruction // w1 = ctx->get(ctx); // Extract first word flags lock = !!(w1 & PREF_LOCK); nomore = !!(w1 & PREF_NOMORE); w1 &= ~(PREF_LOCK|PREF_NOMORE); // Find instruction if (w1 >= NINSTRS) { illmsg = "No such INSTR"; goto ill; } in = &ctx->i[w1]; if (!nomore) goto skip_w2; // // Process second word // w2 = ctx->get(ctx); // REP and COND rep = w2 & PREF_REP; cond = (w2 & BITS_COND) >> COND_SHIFT; // F1 and F2 f1 = (w2 >> F1_SHIFT) & Fx_MASK; f2 = w2 & Fx_MASK; skip_w2: // // Deal with operand 1 // if (in->prm1 == NOPRM) { if (f1 || f2) { illmsg = "FT1 and/or FT2 filled for 0-param INSTR"; goto ill; } exec_instr(ctx, in, NULL, NULL, lock, rep, cond, pc); return; } check_param_type(ctx, in->prm1, f1); extract_param(ctx, &p1, f1); // // Deal with operand 2 // if (in->prm2 == NOPRM) { if (f2) { illmsg = "FT2 filled for 1-param INSTR"; goto ill; } exec_instr(ctx, in, &p1, NULL, lock, rep, cond, pc); return; } check_param_type(ctx, in->prm2, f2); extract_param(ctx, &p2, f2); exec_instr(ctx, in, &p1, &p2, lock, rep, cond, pc); return; ill: _except(ctx, E_ILL, illmsg); } // // Verify that access to a certain register is legal // static void checkreg(ctx_t *ctx, uint reg, bool inv_is_ok) { if (reg >= NREGS) _except(ctx, E_ILL, "Inexistent register: %u", reg); if (reg == INV) { if (!inv_is_ok) _except(ctx, E_ILL, "INV dereference"); else return; } if (ctx->r[reg].flags & (RES | CTL)) _except(ctx, E_ACC, "Reserved REG: %u", reg); if (ctx->r[reg].flags & SYS) if (cr0 & UF) _except(ctx, E_SYS, "User access to SYS REG: %u", reg); } // // Extract operand according to fmt // void extract_param(ctx_t *ctx, acc_t *p, uchar fmt) { uint mlen, mfmt, reg1, reg2, imm1 = 0, imm2 = 0; ushort temp; p->type = fmt; if (fmt == A_REG) { p->reg = ctx->get(ctx); checkreg(ctx, p->reg, 0); p->val = R(p->reg); } else if (fmt == A_IMM64) { p->val = ctx->get(ctx); p->val |= (ulong)ctx->get(ctx) << 16; p->val |= (ulong)ctx->get(ctx) << 32; p->val |= (ulong)ctx->get(ctx) << 48; } assert(ACC_FMT_IS_MEM(fmt)); // // Handle a memory access // mlen = fmt & AM_MLEN_MASK; mfmt = fmt & AM_MFMT_MASK; p->mlen = (mlen == AM_8ACC ? 1 : (mlen == AM_16ACC ? 2 : (mlen == AM_32ACC ? 4 : (mlen == AM_64ACC ? 8 : 0)))); if (p->mlen == 0) _except(ctx, E_ILL, "Invalid MLEN for access: %x", fmt); switch (mfmt) { case AM_IMM64: p->addr = ctx->get(ctx); p->addr |= (ulong)ctx->get(ctx) << 16; p->addr |= (ulong)ctx->get(ctx) << 32; p->addr |= (ulong)ctx->get(ctx) << 48; break; case AM_RR: case AM_RRI: case AM_RRII: temp = ctx->get(ctx); reg1 = temp >> 8; reg2 = temp & 0xFF; checkreg(ctx, reg1, 0); checkreg(ctx, reg2, 1); if (fmt == AM_RRI) { imm1 = 1; imm2 = ctx->get(ctx); } else if (fmt == AM_RRII) { imm1 = ctx->get(ctx); imm2 = ctx->get(ctx); } p->addr = R(reg1) + R(reg2) * imm1 + imm2; break; default: _except(ctx, E_ILL, "Invalid MFMT for access: %x", fmt); } p->val = readmem(ctx, p->addr, p->mlen); } static bool eval_cond(ctx_t *ctx, uint cond) { bool neg = cond & (1 << 5); bool ok; cond &= ~(1 << 5); switch (cond) { case CD_C: ok = flg&CF; break; case CD_O: ok = flg&OF; break; case CD_Z: ok = flg&ZF; break; case CD_S: ok = flg&SF; break; case CD_P: ok = flg&PF; break; case CD_A: ok = !(flg&CF || flg&ZF); break; case CD_AE: ok = !(flg&CF); break; case CD_B: ok = flg&CF; break; case CD_BE: ok = flg&CF || flg&ZF; break; case CD_G: ok = !(flg&ZF) && (!(flg&SF) == !(flg&OF)); break; case CD_GE: ok = !(flg&SF) == !(flg&OF); break; case CD_L: ok = !(flg&SF) != !(flg&OF); break; case CD_LE: ok = flg&ZF || (!(flg&SF) != !(flg&OF)); break; case CD_CXZ: ok = !rcx; break; default: _except(ctx, E_ILL, "Invalid COND value: %x", cond); } return neg ? !ok : !!ok; } char *cond_suffixes[] = { "", ".c" }; static void dump_acc(ctx_t *ctx, acc_t *p) { uint mfmt; if (p->type == A_REG) log("%s", ctx->r[p->reg].name); else if (p->type == A_IMM64) log("imm64:0x%016lX", p->val); else { log("%c[", getmempref(p->mlen)); mfmt = p->type & AM_MFMT_MASK; if (mfmt == AM_IMM64) log("imm64:"); else if (mfmt == AM_RR) log("rr:"); else if (mfmt == AM_RRI) log("rri:"); else if (mfmt == AM_RRII) log("rrii:"); log("0x%016lX]", p->addr); } } static void dump_instr( ctx_t *ctx, instr_t *in, acc_t *p1, acc_t *p2, bool lock, bool rep, uint cond, ulong pc) { log("0x%016lX: ", pc); if (lock) log("lock "); if (rep) log("rep "); if (cond) log("cond%u ", cond); log("%s ", in->full); if (p1) { dump_acc(ctx, p1); if (p2) { log(", "); dump_acc(ctx, p2); } } log("\n"); } // // Executes an instruction // void exec_instr( ctx_t *ctx, instr_t *in, acc_t *p1, acc_t *p2, bool lock, bool rep, uint cond, ulong pc) { bool out = 0; ulong ret = 0; // Debugging dump_instr(ctx, in, p1, p2, lock, rep, cond, pc); if (rep && !cond) cond = CD_CXZ; do_rep: if (!eval_cond(ctx, cond)) return; out = in->func(ctx, p1, p2, &ret); if (out) { if (p1->type == A_REG) R(p1->reg) = ret; else if (p1->type == A_IMM64) _except(ctx, E_ACC, "Trying to output on an IMM64"); else { assert(ACC_IS_MEM(p1)); writemem(ctx, ret, p1->addr, p1->mlen); } } if (rep) { if (cond == CD_CXZ) rcx--; goto do_rep; } }