kvisc/vm/pc/decd.c

393 lines
7.6 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>
//
// 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;
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, 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 + (long)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_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: %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);
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 to an IMM64");
else
{
assert(ACC_IS_MEM(p1));
writemem(ctx, ret, p1->addr, p1->mlen);
}
}
if (rep && rcx > 0)
{
rcx--;
goto do_rep;
}
}