// 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 // // Read the "DECD" file before reading this code // // // Instruction fetch // static uchar fetchb(ctx_t *ctx) { uchar v = *(ctx->mp + R(RIP) + R(CR1) - MEMOFF); R(RIP) += 1; return v; } static ushort fetchw(ctx_t *ctx) { ushort v = *(ushort *)(ctx->mp + R(RIP) + R(CR1) - MEMOFF); R(RIP) += 2; return v; } static uint fetchd(ctx_t *ctx) { uint v = *(uint *)(ctx->mp + R(RIP) + R(CR1) - MEMOFF); R(RIP) += 4; return v; } static ulong fetchq(ctx_t *ctx) { ulong v = *(ulong *)(ctx->mp + R(RIP) + R(CR1) - MEMOFF); R(RIP) += 8; return v; } // // Verify that access to a certain register is legal // static void checkreg(ctx_t *ctx, uint reg) { if (reg >= NREGS) _except(ctx, E_ILL, "Inexistent register: %u", reg); if (ctx->r[reg].flags & RES) _except(ctx, E_ACC, "Reserved REG: %u", reg); if (ctx->r[reg].flags & SYS) if (R(CR0) & UF) _except(ctx, E_SYS, "User access to SYS REG: %u", reg); } // // Verify that given access length is that of a byte/word/dword/qword // static void checklength(ctx_t *ctx, instr_t *in, uint len) { static const int lentab[9] = { 0, 1, 1, 0, 1, 0, 0, 0, 1 }; if (len > 8 || lentab[len] == 0) _except(ctx, E_ILL, "%s: Invalid memory access length: %u", in->name, len); } // // Extracts one operand // static void extract_param(ctx_t *ctx, instr_t *in, acc_t *p) { // Get next operand ModRM-like byte uchar fmt = fetchb(ctx); uchar hi = (fmt & 0b11100000) >> 5; uchar lo = fmt & 0b00011111; // // Registers // if (hi == 0) { p->reg = lo; p->type = A_REG; checkreg(ctx, p->reg); p->val = R(p->reg); return; } // // Immediates // if (hi == 7) { switch (lo) { case 1: p->type = A_IMM8; p->val = fetchb(ctx); break; case 2: p->type = A_IMM16; p->val = fetchw(ctx); break; case 4: p->type = A_IMM32; p->val = fetchd(ctx); break; case 8: p->type = A_IMM64; p->val = fetchq(ctx); break; default: _except(ctx, E_ILL, "%s: Invalid immediate length: %hhu", in->name, lo); } return; } // // Memory operands // checklength(ctx, in, lo); p->mlen = lo; switch (hi) { case 1: p->type = AM_RR; p->reg1 = fetchb(ctx); p->reg2 = p->imm1 = p->imm2 = 0; break; case 2: p->type = AM_RR; p->reg1 = fetchb(ctx); p->reg2 = fetchb(ctx); p->imm1 = 1; p->imm2 = 0; break; case 3: p->type = AM_RRI; p->reg1 = fetchb(ctx); p->reg2 = fetchb(ctx); p->imm2 = (short)fetchw(ctx); p->imm1 = 1; break; case 4: p->type = AM_RRI; p->reg1 = fetchb(ctx); p->reg2 = fetchb(ctx); p->imm2 = (int)fetchd(ctx); p->imm1 = 1; break; case 5: p->type = AM_RRII; p->reg1 = fetchb(ctx); p->reg2 = fetchb(ctx); p->imm1 = fetchb(ctx); p->imm2 = (short)fetchd(ctx); break; case 6: p->type = AM_RRI; p->reg1 = fetchb(ctx); p->imm2 = fetchq(ctx); p->reg2 = RZX; p->imm1 = 1; break; } p->addr = R(p->reg1) + R(p->reg2) * (long)p->imm1 + p->imm2; } // // Instruction fetch & decode // void decode(ctx_t *ctx) { instr_t *in; ushort b1, b2, rep = 0, lock; acc_t p1 = { 0 }, p2 = { 0 }, p3 = { 0 }; ctx->cur_pc = R(RIP); // Address range check // (assumes max. instruction length is 32 bytes) if (R(RIP) + R(CR1) - MEMOFF >= ctx->mz - 32) _except(ctx, E_ACC, "Executing out of memory"); // Instruction bytes b1 = fetchb(ctx); // Suffixes? if (b1 & (1 << 7)) { b1 &= ~(1 << 7); b2 = fetchb(ctx); } else b2 = 0; // Renge check if (b1 >= NINSTRS) _except(ctx, E_ILL, "No such INSTR: 0x%hhX", b1); // Find instruction in = &ctx->i[b1]; ctx->cur_in = in; // Get flags/etc rep = !!(b2 & SUFF_REP); lock = !!(b2 & SUFF_LOCK); ctx->cond = b2 & SUFF_COND; // Operand 1? if (in->nprms == 0) { exec_instr(ctx, in, NULL, NULL, NULL, lock, rep); return; } extract_param(ctx, in, &p1); // Operand 2? if (in->nprms == 1) { exec_instr(ctx, in, &p1, NULL, NULL, lock, rep); return; } extract_param(ctx, in, &p2); // Operand 1? if (in->nprms == 2) { exec_instr(ctx, in, &p1, &p2, NULL, lock, rep); return; } extract_param(ctx, in, &p3); exec_instr(ctx, in, &p1, &p2, &p3, lock, rep); }