kvisc/vm/pc/decode.c

252 lines
4.5 KiB
C

// 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 <pc/arch.h>
//
// Read the "DECD" file before reading this code
//
//
// Instruction fetch
//
static uchar fetchb(void)
{
uchar v = *(ctx->mp + R(RIP) + R(CR1) - MEMOFF);
R(RIP) += 1;
return v;
}
static ushort fetchw(void)
{
ushort v = *(ushort *)(ctx->mp + R(RIP) + R(CR1) - MEMOFF);
R(RIP) += 2;
return v;
}
static uint fetchd(void)
{
uint v = *(uint *)(ctx->mp + R(RIP) + R(CR1) - MEMOFF);
R(RIP) += 4;
return v;
}
static ulong fetchq(void)
{
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(uint reg)
{
if (reg >= NREGS)
_except(E_ILL, "Inexistent register: %u", reg);
if (ctx->r[reg].flags & RES)
_except(E_ACC, "Reserved REG: %u", reg);
if (ctx->r[reg].flags & SYS)
if (R(CR0) & UF)
_except(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(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(E_ILL, "%s: Invalid memory access length: %u",
in->name, len);
}
//
// Extracts one operand
//
static void extract_param(instr_t *in, acc_t *p)
{
// Get next operand ModRM-like byte
uchar fmt = fetchb();
uchar hi = (fmt & 0b11100000) >> 5;
uchar lo = fmt & 0b00011111;
//
// Registers
//
if (hi == 0)
{
p->reg = lo;
p->type = A_REG;
checkreg(p->reg);
p->val = R(p->reg);
return;
}
//
// Immediates
//
if (hi == 7)
{
switch (lo)
{
case 1:
p->type = A_IMM8;
p->val = fetchb();
break;
case 2:
p->type = A_IMM16;
p->val = fetchw();
break;
case 4:
p->type = A_IMM32;
p->val = fetchd();
break;
case 8:
p->type = A_IMM64;
p->val = fetchq();
break;
default:
_except(E_ILL, "%s: Invalid immediate length: %hhu",
in->name, lo);
}
return;
}
//
// Memory operands
//
checklength(in, lo);
p->mlen = lo;
switch (hi)
{
case 1:
p->type = AM_RR;
p->reg1 = fetchb();
p->reg2 = p->imm1 = p->imm2 = 0;
break;
case 2:
p->type = AM_RR;
p->reg1 = fetchb();
p->reg2 = fetchb();
p->imm1 = 1;
p->imm2 = 0;
break;
case 3:
p->type = AM_RRI;
p->reg1 = fetchb();
p->imm2 = (short)fetchw();
p->reg2 = RZX;
p->imm1 = 1;
break;
case 4:
p->type = AM_RRI;
p->reg1 = fetchb();
p->reg2 = fetchb();
p->imm2 = (int)fetchd();
p->imm1 = 1;
break;
case 5:
p->type = AM_RRII;
p->reg1 = fetchb();
p->reg2 = fetchb();
p->imm1 = fetchb();
p->imm2 = (short)fetchd();
break;
case 6:
p->type = AM_RRI;
p->reg1 = fetchb();
p->imm2 = fetchq();
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(void)
{
ushort b;
instr_t *in;
acc_t p1 = { 0 }, p2 = { 0 }, p3 = { 0 };
//logerr("decodin'\n");
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(E_ACC, "Executing out of memory");
// Instruction bytes
b = fetchb();
// Renge check
if (b >= NINSTRS)
_except(E_ILL, "No such INSTR: 0x%hhX", b);
// Find instruction
in = &ctx->i[b];
ctx->cur_in = in;
// Operand 1?
if (in->nprms == 0)
{
exec_instr(in, NULL, NULL, NULL);
return;
}
extract_param(in, &p1);
// Operand 2?
if (in->nprms == 1)
{
exec_instr(in, &p1, NULL, NULL);
return;
}
extract_param(in, &p2);
// Operand 1?
if (in->nprms == 2)
{
exec_instr(in, &p1, &p2, NULL);
return;
}
extract_param(in, &p3);
exec_instr(in, &p1, &p2, &p3);
}