// 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: " "fmt=0x%x prm=0x%x", fmt, prm); } void decode(ctx_t *ctx) { char *illmsg; instr_t *in; acc_t p1 = { 0 }; acc_t p2 = { 0 }; bool rep = 0; uint 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; ushort temp; p->type = fmt; if (fmt == A_REG) { p->reg = ctx->get(ctx); checkreg(ctx, p->reg, 0); p->val = R(p->reg); return; } 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; return; } 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); p->reg1 = temp >> 8; p->reg2 = temp & 0xFF; checkreg(ctx, p->reg1, 1); checkreg(ctx, p->reg2, 1); if (mfmt == AM_RRI) { p->imm1 = 1; p->imm2 = ctx->get(ctx); } else if (mfmt == AM_RRII) { p->imm1 = ctx->get(ctx); p->imm2 = ctx->get(ctx); } else { p->imm1 = 1; p->imm2 = 0; } p->addr = R(p->reg1) + R(p->reg2) * p->imm1 + p->imm2; break; default: _except(ctx, E_ILL, "Invalid MFMT for access: %x", fmt); } } static bool eval_cond(ctx_t *ctx, uint cond) { bool neg = cond & (1 << 4); bool ok; cond &= ~(1 << 4); switch (cond) { case CD_NONE: ok = 1; break; 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: 0x%x", (neg?cond|(1<<4):cond)); } return neg ? !ok : !!ok; } // // 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; ulong r1 = 0, r2 = 0; // Debugging dump_instr(ctx, in, p1, p2, lock, rep, cond, pc); // // For REPs we evaluate the condition AFTER running the instruction, // in a do ... while(cond) fashion // if (!rep && !eval_cond(ctx, cond)) return; do_rep: out = in->func(ctx, p1, p2, &r1, &r2); if (out) { if (p1->type == A_REG) R(p1->reg) = r1; else if (p1->type == A_IMM64) _except(ctx, E_ACC, "Trying to output to an IMM64"); else { assert(ACC_IS_MEM(p1)); writemem(ctx, r1, p1->addr, p1->mlen); } } if (out == 2) { if (p2->type == A_REG) R(p2->reg) = r2; else if (p2->type == A_IMM64) _except(ctx, E_ACC, "Trying to output to an IMM64"); else { assert(ACC_IS_MEM(p2)); writemem(ctx, r2, p2->addr, p2->mlen); } } if (rep && rcx > 0) { // RCX remains untouched when condition fails if (!eval_cond(ctx, cond)) return; // Show that we're REP'ing dump_instr(ctx, in, p1, p2, lock, rep, cond, pc); rcx--; goto do_rep; } }