2019-05-15 20:06:45 +02:00
|
|
|
// The OS/K Team licenses this file to you under the MIT license.
|
|
|
|
// See the LICENSE file in the project root for more information.
|
2019-05-15 19:26:40 +02:00
|
|
|
|
2019-06-05 12:53:09 +02:00
|
|
|
#include <pc/arch.h>
|
2019-05-15 19:26:40 +02:00
|
|
|
|
2019-06-07 22:23:38 +02:00
|
|
|
//
|
2019-06-17 20:59:30 +02:00
|
|
|
// Read the "DECD" file before reading this code
|
2019-06-07 22:23:38 +02:00
|
|
|
//
|
2019-05-16 21:42:23 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
//
|
|
|
|
// 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)
|
2019-05-16 21:42:23 +02:00
|
|
|
{
|
2019-07-24 16:52:26 +02:00
|
|
|
ushort v = *(ushort *)(ctx->mp + R(RIP) + R(CR1) - MEMOFF);
|
|
|
|
R(RIP) += 2;
|
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
2019-05-16 21:42:23 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
static uint fetchd(ctx_t *ctx)
|
|
|
|
{
|
|
|
|
uint v = *(uint *)(ctx->mp + R(RIP) + R(CR1) - MEMOFF);
|
|
|
|
R(RIP) += 4;
|
2019-05-30 12:44:56 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
return v;
|
|
|
|
}
|
2019-05-30 12:44:56 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
static ulong fetchq(ctx_t *ctx)
|
|
|
|
{
|
|
|
|
ulong v = *(ulong *)(ctx->mp + R(RIP) + R(CR1) - MEMOFF);
|
|
|
|
R(RIP) += 8;
|
2019-05-16 21:42:23 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
return v;
|
2019-05-16 21:42:23 +02:00
|
|
|
}
|
|
|
|
|
2019-06-07 22:23:38 +02:00
|
|
|
//
|
|
|
|
// Verify that access to a certain register is legal
|
|
|
|
//
|
2019-07-17 22:25:50 +02:00
|
|
|
static void checkreg(ctx_t *ctx, uint reg)
|
2019-06-07 22:23:38 +02:00
|
|
|
{
|
|
|
|
if (reg >= NREGS)
|
|
|
|
_except(ctx, E_ILL, "Inexistent register: %u", reg);
|
|
|
|
|
2019-07-11 18:34:21 +02:00
|
|
|
if (ctx->r[reg].flags & RES)
|
2019-07-17 22:25:50 +02:00
|
|
|
_except(ctx, E_ACC, "Reserved REG: %u", reg);
|
2019-06-07 22:23:38 +02:00
|
|
|
|
|
|
|
if (ctx->r[reg].flags & SYS)
|
2019-07-11 18:34:21 +02:00
|
|
|
if (R(CR0) & UF)
|
2019-06-07 22:23:38 +02:00
|
|
|
_except(ctx, E_SYS, "User access to SYS REG: %u", reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2019-07-24 16:52:26 +02:00
|
|
|
// Verify that given access length is that of a byte/word/dword/qword
|
2019-06-07 22:23:38 +02:00
|
|
|
//
|
2019-08-03 17:41:44 +02:00
|
|
|
static void checklength(ctx_t *ctx, instr_t *in, uint len)
|
2019-06-07 22:23:38 +02:00
|
|
|
{
|
2019-07-24 16:52:26 +02:00
|
|
|
static const int lentab[9] = { 0, 1, 1, 0, 1, 0, 0, 0, 1 };
|
2019-06-07 22:23:38 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
if (len > 8 || lentab[len] == 0)
|
2019-08-03 17:41:44 +02:00
|
|
|
_except(ctx, E_ILL, "%s: Invalid memory access length: %u",
|
|
|
|
in->name, len);
|
2019-07-24 16:52:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Extracts one operand
|
|
|
|
//
|
2019-08-03 17:41:44 +02:00
|
|
|
static void extract_param(ctx_t *ctx, instr_t *in, acc_t *p)
|
2019-07-24 16:52:26 +02:00
|
|
|
{
|
|
|
|
// Get next operand ModRM-like byte
|
|
|
|
uchar fmt = fetchb(ctx);
|
|
|
|
|
2019-08-03 17:41:44 +02:00
|
|
|
uchar hi = (fmt & 0b11100000) >> 5;
|
|
|
|
uchar lo = fmt & 0b00011111;
|
2019-05-30 12:44:56 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
//
|
|
|
|
// Registers
|
|
|
|
//
|
|
|
|
if (hi == 0)
|
2019-06-07 22:23:38 +02:00
|
|
|
{
|
2019-07-24 16:52:26 +02:00
|
|
|
p->reg = lo;
|
|
|
|
p->type = A_REG;
|
2019-07-17 22:25:50 +02:00
|
|
|
checkreg(ctx, p->reg);
|
2019-06-07 22:23:38 +02:00
|
|
|
|
|
|
|
p->val = R(p->reg);
|
2019-07-24 16:52:26 +02:00
|
|
|
|
2019-06-12 15:30:35 +02:00
|
|
|
return;
|
2019-05-15 19:26:40 +02:00
|
|
|
}
|
2019-05-30 12:44:56 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
//
|
|
|
|
// Immediates
|
|
|
|
//
|
|
|
|
if (hi == 7)
|
2019-06-07 22:23:38 +02:00
|
|
|
{
|
2019-07-24 16:52:26 +02:00
|
|
|
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:
|
2019-08-03 17:41:44 +02:00
|
|
|
_except(ctx, E_ILL, "%s: Invalid immediate length: %hhu",
|
|
|
|
in->name, lo);
|
2019-07-24 16:52:26 +02:00
|
|
|
}
|
|
|
|
|
2019-06-12 15:30:35 +02:00
|
|
|
return;
|
2019-05-15 19:26:40 +02:00
|
|
|
}
|
2019-05-30 12:44:56 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
//
|
|
|
|
// Memory operands
|
|
|
|
//
|
2019-06-07 22:23:38 +02:00
|
|
|
|
2019-08-03 17:41:44 +02:00
|
|
|
checklength(ctx, in, lo);
|
2019-07-24 16:52:26 +02:00
|
|
|
p->mlen = lo;
|
2019-06-07 22:23:38 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
switch (hi)
|
2019-06-07 22:23:38 +02:00
|
|
|
{
|
2019-07-24 16:52:26 +02:00
|
|
|
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);
|
2019-07-24 17:42:42 +02:00
|
|
|
p->imm2 = (short)fetchd(ctx);
|
2019-07-24 16:52:26 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
p->type = AM_RRI;
|
|
|
|
p->reg1 = fetchb(ctx);
|
|
|
|
p->imm2 = fetchq(ctx);
|
|
|
|
p->reg2 = RZX;
|
|
|
|
p->imm1 = 1;
|
|
|
|
break;
|
2019-06-07 22:23:38 +02:00
|
|
|
}
|
2019-07-24 16:52:26 +02:00
|
|
|
|
|
|
|
p->addr = R(p->reg1) + R(p->reg2) * (long)p->imm1 + p->imm2;
|
2019-06-07 22:23:38 +02:00
|
|
|
}
|
2019-05-30 12:44:56 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
//
|
|
|
|
// Instruction fetch & decode
|
|
|
|
//
|
2019-06-17 20:59:30 +02:00
|
|
|
void decode(ctx_t *ctx)
|
2019-06-07 22:23:38 +02:00
|
|
|
{
|
2019-06-17 20:59:30 +02:00
|
|
|
instr_t *in;
|
2019-07-24 16:52:26 +02:00
|
|
|
ushort b1, b2, rep = 0, lock;
|
2019-07-17 22:25:50 +02:00
|
|
|
acc_t p1 = { 0 }, p2 = { 0 }, p3 = { 0 };
|
2019-06-17 20:59:30 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
ctx->cur_pc = R(RIP);
|
2019-06-19 21:41:22 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
// 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");
|
2019-07-01 21:46:36 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
// Instruction bytes
|
|
|
|
b1 = fetchb(ctx);
|
2019-08-03 18:20:36 +02:00
|
|
|
|
|
|
|
// Suffixes?
|
|
|
|
if (b1 & (1 << 7))
|
|
|
|
{
|
|
|
|
b1 &= ~(1 << 7);
|
|
|
|
b2 = fetchb(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
b2 = 0;
|
2019-06-07 22:23:38 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
// Renge check
|
|
|
|
if (b1 >= NINSTRS)
|
|
|
|
_except(ctx, E_ILL, "No such INSTR: 0x%hhX", b1);
|
2019-05-15 19:26:40 +02:00
|
|
|
|
2019-07-17 22:25:50 +02:00
|
|
|
// Find instruction
|
2019-07-24 16:52:26 +02:00
|
|
|
in = &ctx->i[b1];
|
2019-07-04 20:33:49 +02:00
|
|
|
ctx->cur_in = in;
|
2019-06-07 22:23:38 +02:00
|
|
|
|
2019-07-24 16:52:26 +02:00
|
|
|
// Get flags/etc
|
|
|
|
rep = !!(b2 & SUFF_REP);
|
|
|
|
lock = !!(b2 & SUFF_LOCK);
|
|
|
|
ctx->cond = b2 & SUFF_COND;
|
2019-06-07 23:25:47 +02:00
|
|
|
|
2019-07-17 22:25:50 +02:00
|
|
|
// Operand 1?
|
2019-08-03 17:41:44 +02:00
|
|
|
if (in->nprms == 0)
|
2019-06-17 20:59:30 +02:00
|
|
|
{
|
2019-07-02 20:13:05 +02:00
|
|
|
exec_instr(ctx, in, NULL, NULL, NULL, lock, rep);
|
2019-06-17 20:59:30 +02:00
|
|
|
return;
|
2019-06-12 15:47:11 +02:00
|
|
|
}
|
|
|
|
|
2019-08-03 17:41:44 +02:00
|
|
|
extract_param(ctx, in, &p1);
|
2019-06-17 20:59:30 +02:00
|
|
|
|
2019-07-17 22:25:50 +02:00
|
|
|
// Operand 2?
|
2019-08-03 17:41:44 +02:00
|
|
|
if (in->nprms == 1)
|
2019-06-17 20:59:30 +02:00
|
|
|
{
|
2019-07-02 20:13:05 +02:00
|
|
|
exec_instr(ctx, in, &p1, NULL, NULL, lock, rep);
|
2019-06-17 20:59:30 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-06-13 17:13:59 +02:00
|
|
|
|
2019-08-03 17:41:44 +02:00
|
|
|
extract_param(ctx, in, &p2);
|
2019-06-13 17:13:59 +02:00
|
|
|
|
2019-07-17 22:25:50 +02:00
|
|
|
// Operand 1?
|
2019-08-03 17:41:44 +02:00
|
|
|
if (in->nprms == 2)
|
2019-07-01 21:46:36 +02:00
|
|
|
{
|
2019-07-02 20:13:05 +02:00
|
|
|
exec_instr(ctx, in, &p1, &p2, NULL, lock, rep);
|
2019-07-01 21:46:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-03 17:41:44 +02:00
|
|
|
extract_param(ctx, in, &p3);
|
2019-06-13 15:32:24 +02:00
|
|
|
|
2019-07-02 20:13:05 +02:00
|
|
|
exec_instr(ctx, in, &p1, &p2, &p3, lock, rep);
|
2019-05-15 19:26:40 +02:00
|
|
|
}
|
|
|
|
|