sim8086: Move decode into own function
This commit is contained in:
parent
4467191198
commit
7378c4349f
925
part1/sim8086.c
925
part1/sim8086.c
@ -303,6 +303,469 @@ void S86_DecodeEffectiveAddr(S86_Opcode *opcode, S86_BufferIterator *buffer_it,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
S86_Opcode S86_DecodeOpcode(S86_BufferIterator *buffer_it,
|
||||||
|
S86_OpDecode const *decode_table,
|
||||||
|
uint16_t decode_table_size,
|
||||||
|
bool *lock_prefix,
|
||||||
|
S86_MnemonicOp *seg_reg)
|
||||||
|
{
|
||||||
|
char op_code_bytes[2] = {0};
|
||||||
|
size_t op_code_size = 0;
|
||||||
|
op_code_bytes[op_code_size++] = S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
|
||||||
|
// NOTE: Match the assembly bytes to the desired instruction
|
||||||
|
// =====================================================================
|
||||||
|
S86_OpDecodeType op_decode_type = S86_OpDecodeType_Count;
|
||||||
|
S86_OpDecode const *op_decode = NULL;
|
||||||
|
for (size_t op_index = 0;
|
||||||
|
op_decode_type == S86_OpDecodeType_Count && op_index < decode_table_size;
|
||||||
|
op_index++)
|
||||||
|
{
|
||||||
|
S86_OpDecode const *item = decode_table + op_index;
|
||||||
|
|
||||||
|
// NOTE: Check first instruction byte
|
||||||
|
// =================================================================
|
||||||
|
if ((op_code_bytes[0] & item->op_mask0) != item->op_bits0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// NOTE Check multi-byte instruction
|
||||||
|
// =================================================================
|
||||||
|
// If the matched instruction has a bit mask for the 2nd byte, this
|
||||||
|
// is a multi-byte instruction. Check if the 2nd byte checks out.
|
||||||
|
bool op_match = true;
|
||||||
|
if (item->op_mask1) {
|
||||||
|
// TODO: This assumes the iterator is valid
|
||||||
|
uint8_t op_byte = S86_BufferIteratorPeekByte(buffer_it);
|
||||||
|
op_match = (op_byte & item->op_mask1) == item->op_bits1;
|
||||||
|
if (op_match) {
|
||||||
|
op_code_bytes[op_code_size++] = op_byte;
|
||||||
|
S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op_match) {
|
||||||
|
op_decode_type = op_index;
|
||||||
|
op_decode = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Disassemble bytes to assembly mnemonics
|
||||||
|
// =================================================================
|
||||||
|
S86_ASSERT(op_code_size > 0 && op_code_size <= S86_ARRAY_UCOUNT(op_code_bytes));
|
||||||
|
S86_ASSERT(op_decode_type != S86_OpDecodeType_Count && "Unknown instruction");
|
||||||
|
|
||||||
|
S86_Opcode result = {0};
|
||||||
|
result.mnemonic = op_decode->mnemonic;
|
||||||
|
result.lock_prefix = *lock_prefix;
|
||||||
|
result.seg_reg_prefix = *seg_reg;
|
||||||
|
S86_ASSERT(*seg_reg == S86_MnemonicOp_Invalid || (*seg_reg >= S86_MnemonicOp_ES && *seg_reg <= S86_MnemonicOp_DS));
|
||||||
|
switch (op_decode_type) {
|
||||||
|
// NOTE: Instruction Pattern => [0b0000'0000W | 0bAA00'0CCC | DISP-LO | DISP-HI]
|
||||||
|
// Where, W: Optional, AA: mod, CCC: R/M
|
||||||
|
case S86_OpDecodeType_JMPIndirectWithinSeg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_CALLIndirectWithinSeg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_NOT: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_SHL_SAL: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_SHR: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_SAR: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_ROL: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_ROR: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_RCL: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_RCR: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_MUL: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_IMUL: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_DIV: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_IDIV: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_INCRegOrMem: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_DECRegOrMem: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_NEG: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_POPRegOrMem: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_PUSHRegOrMem: {
|
||||||
|
S86_ASSERT(op_code_size == 2);
|
||||||
|
uint8_t mod = (op_code_bytes[1] & 0b1100'0000) >> 6;
|
||||||
|
uint8_t rm = (op_code_bytes[1] & 0b0000'0111) >> 0;
|
||||||
|
S86_ASSERT(mod < 4); S86_ASSERT(rm < 8);
|
||||||
|
|
||||||
|
uint8_t w = 1;
|
||||||
|
if (op_decode_type == S86_OpDecodeType_INCRegOrMem ||
|
||||||
|
op_decode_type == S86_OpDecodeType_DECRegOrMem ||
|
||||||
|
op_decode_type == S86_OpDecodeType_NEG ||
|
||||||
|
op_decode_type == S86_OpDecodeType_MUL ||
|
||||||
|
op_decode_type == S86_OpDecodeType_MUL ||
|
||||||
|
op_decode_type == S86_OpDecodeType_IMUL ||
|
||||||
|
op_decode_type == S86_OpDecodeType_DIV ||
|
||||||
|
op_decode_type == S86_OpDecodeType_IDIV ||
|
||||||
|
(op_decode_type >= S86_OpDecodeType_NOT &&
|
||||||
|
op_decode_type <= S86_OpDecodeType_RCR)) {
|
||||||
|
w = op_code_bytes[0] & 0b0000'0001;
|
||||||
|
}
|
||||||
|
|
||||||
|
S86_DecodeEffectiveAddr(&result, buffer_it, rm, mod, w);
|
||||||
|
result.wide_prefix = S86_WidePrefix_Dest;
|
||||||
|
|
||||||
|
// NOTE: Bit shifts use 'v' to indicate if shift distance should
|
||||||
|
// come from cl register otherwise bitshift by 1
|
||||||
|
if (op_decode_type >= S86_OpDecodeType_SHL_SAL && op_decode_type <= S86_OpDecodeType_RCR) {
|
||||||
|
uint8_t v = (op_code_bytes[0] & 0b0000'0010) >> 1;
|
||||||
|
if (v) {
|
||||||
|
result.src = S86_MnemonicOp_CL;
|
||||||
|
} else {
|
||||||
|
result.src = S86_MnemonicOp_Immediate;
|
||||||
|
result.immediate = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// NOTE: Instruction Pattern => [0b0000'0000]
|
||||||
|
// Generally handles instructions with control bits in any position in the first byte
|
||||||
|
case S86_OpDecodeType_DECReg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_INCReg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_XCHGRegWithAccum: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_PUSHReg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_POPReg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_PUSHSegReg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_POPSegReg: {
|
||||||
|
S86_ASSERT(op_code_size == 1);
|
||||||
|
if (op_decode_type == S86_OpDecodeType_PUSHReg ||
|
||||||
|
op_decode_type == S86_OpDecodeType_POPReg ||
|
||||||
|
op_decode_type == S86_OpDecodeType_INCReg ||
|
||||||
|
op_decode_type == S86_OpDecodeType_DECReg ||
|
||||||
|
op_decode_type == S86_OpDecodeType_XCHGRegWithAccum) {
|
||||||
|
uint8_t reg = (op_code_bytes[0] & 0b0000'0111) >> 0;
|
||||||
|
result.dest = S86_MnemonicOpFromWReg(1, reg);
|
||||||
|
} else {
|
||||||
|
S86_ASSERT(op_decode_type == S86_OpDecodeType_PUSHSegReg ||
|
||||||
|
op_decode_type == S86_OpDecodeType_POPSegReg);
|
||||||
|
uint8_t sr = (op_code_bytes[0] & 0b0001'1000) >> 3;
|
||||||
|
result.dest = S86_MnemonicOpFromSR(sr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op_decode_type == S86_OpDecodeType_XCHGRegWithAccum) {
|
||||||
|
result.src = result.dest;
|
||||||
|
result.dest = S86_MnemonicOp_AX;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// NOTE: Instruction Pattern => [0b0000'000DW | 0bAABB'BCCC | DISP-LO | DISP-HI | DATA-LO | DATA-HI]
|
||||||
|
// Where, D: optional, W: optional, AA: mod, BBB: reg, CCC: r/m
|
||||||
|
case S86_OpDecodeType_ADDRegOrMemToOrFromReg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_ADCRegOrMemWithRegToEither: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_SUBRegOrMemToOrFromReg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_SBBRegOrMemAndRegToEither: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_ANDRegWithMemToEither: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_TESTRegOrMemAndReg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_ORRegOrMemAndRegToEither: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_XORRegOrMemAndRegToEither: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_LEA: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_LDS: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_LES: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_XCHGRegOrMemWithReg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_CMPRegOrMemAndReg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_MOVRegOrMemToOrFromReg: {
|
||||||
|
// NOTE: Instruction does not have opcode bits in the 2nd byte
|
||||||
|
S86_ASSERT(op_code_size == 1);
|
||||||
|
op_code_bytes[op_code_size++] = S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
|
||||||
|
uint8_t w = (op_code_bytes[0] & 0b0000'0001) >> 0;
|
||||||
|
uint8_t d = (op_code_bytes[0] & 0b0000'0010) >> 1;
|
||||||
|
if (op_decode_type == S86_OpDecodeType_XCHGRegOrMemWithReg ||
|
||||||
|
op_decode_type == S86_OpDecodeType_LEA ||
|
||||||
|
op_decode_type == S86_OpDecodeType_LDS ||
|
||||||
|
op_decode_type == S86_OpDecodeType_LES) {
|
||||||
|
d = 1; // Destintation is always the register
|
||||||
|
if (op_decode_type == S86_OpDecodeType_XCHGRegOrMemWithReg) {
|
||||||
|
if (*lock_prefix) {
|
||||||
|
// NOTE: When we XCHG, NASM complains that the
|
||||||
|
// instruction is not lockable, unless, the memory
|
||||||
|
// operand comes first. Here we flip the direction
|
||||||
|
// to ensure the memory operand is the destination.
|
||||||
|
//
|
||||||
|
// listing_0042_completionist_decode_disassembled.asm|319| warning: instruction is not lockable [-w+prefix-lock]
|
||||||
|
d = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w = 1; // Always 16 bit (load into register)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mod = (op_code_bytes[1] & 0b1100'0000) >> 6;
|
||||||
|
uint8_t reg = (op_code_bytes[1] & 0b0011'1000) >> 3;
|
||||||
|
uint8_t rm = (op_code_bytes[1] & 0b0000'0111) >> 0;
|
||||||
|
S86_ASSERT(d < 2);
|
||||||
|
S86_ASSERT(w < 2);
|
||||||
|
S86_ASSERT(mod < 4);
|
||||||
|
S86_ASSERT(reg < 8);
|
||||||
|
S86_ASSERT(rm < 8);
|
||||||
|
|
||||||
|
result.wide = w;
|
||||||
|
result.src = S86_MnemonicOpFromWReg(result.wide, reg);
|
||||||
|
if (mod == 0b11) { // NOTE: Register-to-register move
|
||||||
|
result.dest = S86_MnemonicOpFromWReg(result.wide, rm);
|
||||||
|
} else { // NOTE: Memory mode w/ effective address calculation
|
||||||
|
S86_DecodeEffectiveAddr(&result, buffer_it, rm, mod, w);
|
||||||
|
result.src = S86_MnemonicOpFromWReg(w, reg);
|
||||||
|
if (d)
|
||||||
|
result.effective_addr = S86_EffectiveAddress_Src;
|
||||||
|
else
|
||||||
|
result.effective_addr = S86_EffectiveAddress_Dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d) {
|
||||||
|
S86_MnemonicOp tmp = result.src;
|
||||||
|
result.src = result.dest;
|
||||||
|
result.dest = tmp;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// NOTE: Instruction Pattern => [0b0000'00SW | 0bAAA00BBB | DISP-LO | DISP-HI | DATA-LO | DATA-HI]
|
||||||
|
// Where S: optional, W: optional, AAA: mod, BBB: rm
|
||||||
|
case S86_OpDecodeType_ADDImmediateToRegOrMem: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_ADCImmediateToRegOrMem: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_SUBImmediateFromRegOrMem: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_SBBImmediateFromRegOrMem: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_CMPImmediateWithRegOrMem: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_ANDImmediateToRegOrMem: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_TESTImmediateAndRegOrMem: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_ORImmediateToRegOrMem: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_XORImmediateToRegOrMem: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_MOVImmediateToRegOrMem: {
|
||||||
|
S86_ASSERT(op_code_size == 2);
|
||||||
|
uint8_t w = (op_code_bytes[0] & 0b0000'0001) >> 0;
|
||||||
|
uint8_t s = (op_code_bytes[0] & 0b0000'0010) >> 1;
|
||||||
|
uint8_t mod = (op_code_bytes[1] & 0b1100'0000) >> 6;
|
||||||
|
uint8_t rm = (op_code_bytes[1] & 0b0000'0111) >> 0;
|
||||||
|
S86_ASSERT(w < 2);
|
||||||
|
S86_ASSERT(mod < 4);
|
||||||
|
S86_ASSERT(rm < 8);
|
||||||
|
S86_DecodeEffectiveAddr(&result, buffer_it, rm, mod, w);
|
||||||
|
|
||||||
|
// NOTE: Parse data payload
|
||||||
|
// =============================================================
|
||||||
|
uint16_t data = S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
if (w) { // 16 bit data
|
||||||
|
if ((op_decode_type == S86_OpDecodeType_ADDImmediateToRegOrMem ||
|
||||||
|
op_decode_type == S86_OpDecodeType_ADCImmediateToRegOrMem ||
|
||||||
|
op_decode_type == S86_OpDecodeType_SUBImmediateFromRegOrMem ||
|
||||||
|
op_decode_type == S86_OpDecodeType_SBBImmediateFromRegOrMem ||
|
||||||
|
op_decode_type == S86_OpDecodeType_CMPImmediateWithRegOrMem ||
|
||||||
|
op_decode_type == S86_OpDecodeType_ANDImmediateToRegOrMem ||
|
||||||
|
op_decode_type == S86_OpDecodeType_TESTImmediateAndRegOrMem ||
|
||||||
|
op_decode_type == S86_OpDecodeType_ORImmediateToRegOrMem ||
|
||||||
|
op_decode_type == S86_OpDecodeType_XORImmediateToRegOrMem) && s) {
|
||||||
|
// NOTE: Sign extend 8 bit, since we store into a
|
||||||
|
// int32_t in opcode this is done for free for us.
|
||||||
|
} else {
|
||||||
|
uint8_t data_hi = S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
data |= (uint16_t)(data_hi) << 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op_decode_type == S86_OpDecodeType_MOVImmediateToRegOrMem) {
|
||||||
|
S86_ASSERT(mod != 0b11); // NOTE: Op is IMM->Reg, register-to-register not permitted
|
||||||
|
}
|
||||||
|
|
||||||
|
result.immediate = data;
|
||||||
|
result.src = S86_MnemonicOp_Immediate;
|
||||||
|
if (op_decode_type == S86_OpDecodeType_MOVImmediateToRegOrMem)
|
||||||
|
result.wide_prefix = S86_WidePrefix_Src;
|
||||||
|
else if (result.effective_addr_loads_mem)
|
||||||
|
result.wide_prefix = S86_WidePrefix_Dest;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// NOTE: Instruction Pattern => [0b0000'W00W | DATA-LO | DATA-HI]
|
||||||
|
case S86_OpDecodeType_ADDImmediateToAccum: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_ADCImmediateToAccum: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_SUBImmediateFromAccum: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_SBBImmediateFromAccum: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_CMPImmediateWithAccum: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_ANDImmediateToAccum: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_TESTImmediateAndAccum: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_ORImmediateToAccum: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_XORImmediateToAccum: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_MOVImmediateToReg: {
|
||||||
|
// NOTE: Parse opcode control bits
|
||||||
|
// =============================================================
|
||||||
|
S86_ASSERT(op_code_size == 1);
|
||||||
|
uint8_t w = 0;
|
||||||
|
if (op_decode_type == S86_OpDecodeType_ADDImmediateToAccum ||
|
||||||
|
op_decode_type == S86_OpDecodeType_ADCImmediateToAccum ||
|
||||||
|
op_decode_type == S86_OpDecodeType_SUBImmediateFromAccum ||
|
||||||
|
op_decode_type == S86_OpDecodeType_SBBImmediateFromAccum ||
|
||||||
|
op_decode_type == S86_OpDecodeType_CMPImmediateWithAccum ||
|
||||||
|
op_decode_type == S86_OpDecodeType_ANDImmediateToAccum ||
|
||||||
|
op_decode_type == S86_OpDecodeType_TESTImmediateAndAccum ||
|
||||||
|
op_decode_type == S86_OpDecodeType_ORImmediateToAccum ||
|
||||||
|
op_decode_type == S86_OpDecodeType_XORImmediateToAccum) {
|
||||||
|
w = (op_code_bytes[0] & 0b0000'0001) >> 0;
|
||||||
|
} else {
|
||||||
|
w = (op_code_bytes[0] & 0b0000'1000) >> 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Parse data payload
|
||||||
|
// =============================================================
|
||||||
|
uint16_t data = S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
if (w) { // 16 bit data
|
||||||
|
uint8_t data_hi = S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
data |= (uint16_t)(data_hi) << 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Disassemble
|
||||||
|
// =============================================================
|
||||||
|
result.effective_addr = S86_EffectiveAddress_Dest;
|
||||||
|
result.src = S86_MnemonicOp_Immediate;
|
||||||
|
result.wide = w;
|
||||||
|
result.src = S86_MnemonicOp_Immediate;
|
||||||
|
result.immediate = data;
|
||||||
|
if (op_decode_type == S86_OpDecodeType_MOVImmediateToReg) {
|
||||||
|
uint8_t reg = (op_code_bytes[0] & 0b0000'0111) >> 0;
|
||||||
|
result.dest = S86_MnemonicOpFromWReg(w, reg);
|
||||||
|
} else {
|
||||||
|
result.dest = result.wide ? S86_MnemonicOp_AX : S86_MnemonicOp_AL;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// NOTE: Instruction Pattern => [0b0000'000W | DATA-LO]
|
||||||
|
case S86_OpDecodeType_INFixedPort: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_INVariablePort: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_OUTFixedPort: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_OUTVariablePort: {
|
||||||
|
S86_ASSERT(op_code_size == 1);
|
||||||
|
result.wide = (op_code_bytes[0] & 0b0000'0001) >> 0;
|
||||||
|
result.dest = result.wide ? S86_MnemonicOp_AX : S86_MnemonicOp_AL;
|
||||||
|
if (op_decode_type == S86_OpDecodeType_INFixedPort ||
|
||||||
|
op_decode_type == S86_OpDecodeType_OUTFixedPort) {
|
||||||
|
result.src = S86_MnemonicOp_Immediate;
|
||||||
|
result.immediate = S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
} else {
|
||||||
|
result.src = S86_MnemonicOp_DX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op_decode_type == S86_OpDecodeType_OUTFixedPort ||
|
||||||
|
op_decode_type == S86_OpDecodeType_OUTVariablePort) {
|
||||||
|
S86_MnemonicOp tmp = result.src;
|
||||||
|
result.src = result.dest;
|
||||||
|
result.dest = tmp;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case S86_OpDecodeType_REP: {
|
||||||
|
S86_ASSERT(op_code_size == 1);
|
||||||
|
uint8_t string_op = S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
uint8_t w_mask = 0b0000'0001;
|
||||||
|
result.rep_prefix = true;
|
||||||
|
result.wide = string_op & w_mask;
|
||||||
|
switch (string_op & ~w_mask) {
|
||||||
|
case 0b1010'0100: result.dest = S86_MnemonicOp_MOVS; break;
|
||||||
|
case 0b1010'0110: result.dest = S86_MnemonicOp_CMPS; break;
|
||||||
|
case 0b1010'1110: result.dest = S86_MnemonicOp_SCAS; break;
|
||||||
|
case 0b1010'1100: result.dest = S86_MnemonicOp_LODS; break;
|
||||||
|
case 0b1010'1010: result.dest = S86_MnemonicOp_STOS; break;
|
||||||
|
default: S86_ASSERT(!"Unhandled REP string type"); break;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// NOTE: Instruction Pattern => [0b0000'0000 | DATA-LO | DATA-HI]
|
||||||
|
case S86_OpDecodeType_MOVAccumToMem: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_MOVMemToAccum: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_CALLDirectInterSeg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_CALLDirectWithinSeg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_JMPDirectInterSeg: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_RETWithinSegAddImmediateToSP: /*FALLTHRU*/
|
||||||
|
case S86_OpDecodeType_INT: {
|
||||||
|
S86_ASSERT(op_code_size == 1);
|
||||||
|
uint8_t data_lo = S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
uint16_t data = data_lo;
|
||||||
|
if (op_decode_type != S86_OpDecodeType_INT) {
|
||||||
|
uint8_t data_hi = S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
data = S86_CAST(uint16_t)data_hi << 8 | (S86_CAST(uint16_t)data_lo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op_decode_type == S86_OpDecodeType_CALLDirectWithinSeg) {
|
||||||
|
result.effective_addr = S86_EffectiveAddress_Dest;
|
||||||
|
result.dest = S86_MnemonicOp_BP;
|
||||||
|
result.displacement = -S86_CAST(int32_t)data;
|
||||||
|
} else if (op_decode_type == S86_OpDecodeType_RETWithinSegAddImmediateToSP) {
|
||||||
|
result.dest = S86_MnemonicOp_DirectAddress;
|
||||||
|
result.displacement = data;
|
||||||
|
} else if (op_decode_type == S86_OpDecodeType_CALLDirectInterSeg ||
|
||||||
|
op_decode_type == S86_OpDecodeType_JMPDirectInterSeg) {
|
||||||
|
uint8_t cs_lo = S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
uint8_t cs_hi = S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
uint16_t cs = S86_CAST(uint16_t)cs_hi << 8 | (S86_CAST(uint16_t)cs_lo);
|
||||||
|
result.displacement = (uint32_t)cs << 16 | (uint32_t)data << 0;
|
||||||
|
result.dest = S86_MnemonicOp_DirectInterSegment;
|
||||||
|
} else if (op_decode_type == S86_OpDecodeType_MOVAccumToMem) {
|
||||||
|
result.effective_addr_loads_mem = true;
|
||||||
|
result.effective_addr = S86_EffectiveAddress_Dest;
|
||||||
|
result.dest = S86_MnemonicOp_DirectAddress;
|
||||||
|
result.displacement = data;
|
||||||
|
result.src = S86_MnemonicOp_AX;
|
||||||
|
} else if (op_decode_type == S86_OpDecodeType_MOVMemToAccum) {
|
||||||
|
result.effective_addr_loads_mem = true;
|
||||||
|
result.effective_addr = S86_EffectiveAddress_Src;
|
||||||
|
result.src = S86_MnemonicOp_DirectAddress;
|
||||||
|
result.displacement = data;
|
||||||
|
result.dest = S86_MnemonicOp_AX;
|
||||||
|
} else {
|
||||||
|
result.dest = S86_MnemonicOp_Immediate;
|
||||||
|
result.immediate = data;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: {
|
||||||
|
if (op_decode_type >= S86_OpDecodeType_JE_JZ && op_decode_type <= S86_OpDecodeType_JCXZ) {
|
||||||
|
S86_ASSERT(op_code_size == 1);
|
||||||
|
result.displacement = S86_CAST(int8_t)S86_BufferIteratorNextByte(buffer_it);
|
||||||
|
result.dest = S86_MnemonicOp_Jump;
|
||||||
|
} else if (op_decode_type == S86_OpDecodeType_XLAT ||
|
||||||
|
op_decode_type == S86_OpDecodeType_LAHF ||
|
||||||
|
op_decode_type == S86_OpDecodeType_SAHF ||
|
||||||
|
op_decode_type == S86_OpDecodeType_PUSHF ||
|
||||||
|
op_decode_type == S86_OpDecodeType_POPF ||
|
||||||
|
op_decode_type == S86_OpDecodeType_DAA ||
|
||||||
|
op_decode_type == S86_OpDecodeType_AAA ||
|
||||||
|
op_decode_type == S86_OpDecodeType_DAS ||
|
||||||
|
op_decode_type == S86_OpDecodeType_AAS ||
|
||||||
|
op_decode_type == S86_OpDecodeType_AAM ||
|
||||||
|
op_decode_type == S86_OpDecodeType_AAD ||
|
||||||
|
op_decode_type == S86_OpDecodeType_CBW ||
|
||||||
|
op_decode_type == S86_OpDecodeType_CWD ||
|
||||||
|
op_decode_type == S86_OpDecodeType_RETWithinSeg ||
|
||||||
|
op_decode_type == S86_OpDecodeType_INT3 ||
|
||||||
|
op_decode_type == S86_OpDecodeType_INTO ||
|
||||||
|
op_decode_type == S86_OpDecodeType_IRET ||
|
||||||
|
op_decode_type == S86_OpDecodeType_CLC ||
|
||||||
|
op_decode_type == S86_OpDecodeType_CMC ||
|
||||||
|
op_decode_type == S86_OpDecodeType_STC ||
|
||||||
|
op_decode_type == S86_OpDecodeType_CLD ||
|
||||||
|
op_decode_type == S86_OpDecodeType_STD ||
|
||||||
|
op_decode_type == S86_OpDecodeType_CLI ||
|
||||||
|
op_decode_type == S86_OpDecodeType_STI ||
|
||||||
|
op_decode_type == S86_OpDecodeType_HLT ||
|
||||||
|
op_decode_type == S86_OpDecodeType_WAIT) {
|
||||||
|
// NOTE: Mnemonic only instruction
|
||||||
|
} else if (op_decode_type == S86_OpDecodeType_LOCK) {
|
||||||
|
*lock_prefix = true;
|
||||||
|
} else if (op_decode_type == S86_OpDecodeType_SEGMENT) {
|
||||||
|
// NOTE: Mnemonic does not generate any assembly
|
||||||
|
S86_ASSERT(op_code_size == 1);
|
||||||
|
uint8_t sr = (op_code_bytes[0] & 0b0001'1000) >> 3;
|
||||||
|
*seg_reg = S86_MnemonicOpFromSR(sr);
|
||||||
|
} else {
|
||||||
|
S86_ASSERT(!"Unhandled instruction");
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op_decode_type != S86_OpDecodeType_LOCK)
|
||||||
|
*lock_prefix = false;
|
||||||
|
|
||||||
|
if (op_decode_type != S86_OpDecodeType_SEGMENT)
|
||||||
|
*seg_reg = S86_MnemonicOp_Invalid;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
// NOTE: Argument handling
|
// NOTE: Argument handling
|
||||||
@ -321,7 +784,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
// NOTE: Sim8086
|
// NOTE: Sim8086
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
S86_OpDecode const S86_INSTRUCTIONS[] = {
|
S86_OpDecode const DECODE_TABLE[] = {
|
||||||
[S86_OpDecodeType_MOVRegOrMemToOrFromReg] = {.op_mask0 = 0b1111'1100, .op_mask1 = 0b0000'0000,
|
[S86_OpDecodeType_MOVRegOrMemToOrFromReg] = {.op_mask0 = 0b1111'1100, .op_mask1 = 0b0000'0000,
|
||||||
.op_bits0 = 0b1000'1000, .op_bits1 = 0b0000'0000, .mnemonic = S86_Mnemonic_MOV},
|
.op_bits0 = 0b1000'1000, .op_bits1 = 0b0000'0000, .mnemonic = S86_Mnemonic_MOV},
|
||||||
[S86_OpDecodeType_MOVImmediateToRegOrMem] = {.op_mask0 = 0b1111'1110, .op_mask1 = 0b0011'1000,
|
[S86_OpDecodeType_MOVImmediateToRegOrMem] = {.op_mask0 = 0b1111'1110, .op_mask1 = 0b0011'1000,
|
||||||
@ -618,462 +1081,12 @@ int main(int argc, char **argv)
|
|||||||
S86_BufferIterator buffer_it = S86_BufferIteratorInit(buffer);
|
S86_BufferIterator buffer_it = S86_BufferIteratorInit(buffer);
|
||||||
S86_MnemonicOp seg_reg = {0};
|
S86_MnemonicOp seg_reg = {0};
|
||||||
bool lock_prefix = false;
|
bool lock_prefix = false;
|
||||||
|
|
||||||
while (S86_BufferIteratorHasMoreBytes(buffer_it)) {
|
while (S86_BufferIteratorHasMoreBytes(buffer_it)) {
|
||||||
char op_code_bytes[2] = {0};
|
S86_Opcode opcode = S86_DecodeOpcode(&buffer_it,
|
||||||
size_t op_code_size = 0;
|
DECODE_TABLE,
|
||||||
op_code_bytes[op_code_size++] = S86_BufferIteratorNextByte(&buffer_it);
|
S86_ARRAY_UCOUNT(DECODE_TABLE),
|
||||||
|
&lock_prefix,
|
||||||
// NOTE: Match the assembly bytes to the desired instruction
|
&seg_reg);
|
||||||
// =====================================================================
|
|
||||||
S86_OpDecodeType op_decode_type = S86_OpDecodeType_Count;
|
|
||||||
S86_OpDecode const *op_decode = NULL;
|
|
||||||
for (size_t op_index = 0;
|
|
||||||
op_decode_type == S86_OpDecodeType_Count && op_index < S86_ARRAY_UCOUNT(S86_INSTRUCTIONS);
|
|
||||||
op_index++)
|
|
||||||
{
|
|
||||||
S86_OpDecode const *item = S86_INSTRUCTIONS + op_index;
|
|
||||||
|
|
||||||
// NOTE: Check first instruction byte
|
|
||||||
// =================================================================
|
|
||||||
if ((op_code_bytes[0] & item->op_mask0) != item->op_bits0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// NOTE Check multi-byte instruction
|
|
||||||
// =================================================================
|
|
||||||
// If the matched instruction has a bit mask for the 2nd byte, this
|
|
||||||
// is a multi-byte instruction. Check if the 2nd byte checks out.
|
|
||||||
bool op_match = true;
|
|
||||||
if (item->op_mask1) {
|
|
||||||
// TODO: This assumes the iterator is valid
|
|
||||||
uint8_t op_byte = S86_BufferIteratorPeekByte(&buffer_it);
|
|
||||||
op_match = (op_byte & item->op_mask1) == item->op_bits1;
|
|
||||||
if (op_match) {
|
|
||||||
op_code_bytes[op_code_size++] = op_byte;
|
|
||||||
S86_BufferIteratorNextByte(&buffer_it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op_match) {
|
|
||||||
op_decode_type = op_index;
|
|
||||||
op_decode = item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Disassemble bytes to assembly mnemonics
|
|
||||||
// =================================================================
|
|
||||||
S86_ASSERT(op_code_size > 0 && op_code_size <= S86_ARRAY_UCOUNT(op_code_bytes));
|
|
||||||
S86_ASSERT(op_decode_type != S86_OpDecodeType_Count && "Unknown instruction");
|
|
||||||
|
|
||||||
S86_Opcode opcode = {0};
|
|
||||||
opcode.mnemonic = op_decode->mnemonic;
|
|
||||||
opcode.lock_prefix = lock_prefix;
|
|
||||||
opcode.seg_reg_prefix = seg_reg;
|
|
||||||
S86_ASSERT(seg_reg == S86_MnemonicOp_Invalid || (seg_reg >= S86_MnemonicOp_ES && seg_reg <= S86_MnemonicOp_DS));
|
|
||||||
switch (op_decode_type) {
|
|
||||||
// NOTE: Instruction Pattern => [0b0000'0000W | 0bAA00'0CCC | DISP-LO | DISP-HI]
|
|
||||||
// Where, W: Optional, AA: mod, CCC: R/M
|
|
||||||
case S86_OpDecodeType_JMPIndirectWithinSeg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_CALLIndirectWithinSeg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_NOT: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_SHL_SAL: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_SHR: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_SAR: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_ROL: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_ROR: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_RCL: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_RCR: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_MUL: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_IMUL: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_DIV: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_IDIV: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_INCRegOrMem: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_DECRegOrMem: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_NEG: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_POPRegOrMem: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_PUSHRegOrMem: {
|
|
||||||
S86_ASSERT(op_code_size == 2);
|
|
||||||
uint8_t mod = (op_code_bytes[1] & 0b1100'0000) >> 6;
|
|
||||||
uint8_t rm = (op_code_bytes[1] & 0b0000'0111) >> 0;
|
|
||||||
S86_ASSERT(mod < 4); S86_ASSERT(rm < 8);
|
|
||||||
|
|
||||||
uint8_t w = 1;
|
|
||||||
if (op_decode_type == S86_OpDecodeType_INCRegOrMem ||
|
|
||||||
op_decode_type == S86_OpDecodeType_DECRegOrMem ||
|
|
||||||
op_decode_type == S86_OpDecodeType_NEG ||
|
|
||||||
op_decode_type == S86_OpDecodeType_MUL ||
|
|
||||||
op_decode_type == S86_OpDecodeType_MUL ||
|
|
||||||
op_decode_type == S86_OpDecodeType_IMUL ||
|
|
||||||
op_decode_type == S86_OpDecodeType_DIV ||
|
|
||||||
op_decode_type == S86_OpDecodeType_IDIV ||
|
|
||||||
(op_decode_type >= S86_OpDecodeType_NOT &&
|
|
||||||
op_decode_type <= S86_OpDecodeType_RCR)) {
|
|
||||||
w = op_code_bytes[0] & 0b0000'0001;
|
|
||||||
}
|
|
||||||
|
|
||||||
S86_DecodeEffectiveAddr(&opcode, &buffer_it, rm, mod, w);
|
|
||||||
opcode.wide_prefix = S86_WidePrefix_Dest;
|
|
||||||
|
|
||||||
// NOTE: Bit shifts use 'v' to indicate if shift distance should
|
|
||||||
// come from cl register otherwise bitshift by 1
|
|
||||||
if (op_decode_type >= S86_OpDecodeType_SHL_SAL && op_decode_type <= S86_OpDecodeType_RCR) {
|
|
||||||
uint8_t v = (op_code_bytes[0] & 0b0000'0010) >> 1;
|
|
||||||
if (v) {
|
|
||||||
opcode.src = S86_MnemonicOp_CL;
|
|
||||||
} else {
|
|
||||||
opcode.src = S86_MnemonicOp_Immediate;
|
|
||||||
opcode.immediate = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
// NOTE: Instruction Pattern => [0b0000'0000]
|
|
||||||
// Generally handles instructions with control bits in any position in the first byte
|
|
||||||
case S86_OpDecodeType_DECReg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_INCReg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_XCHGRegWithAccum: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_PUSHReg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_POPReg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_PUSHSegReg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_POPSegReg: {
|
|
||||||
S86_ASSERT(op_code_size == 1);
|
|
||||||
if (op_decode_type == S86_OpDecodeType_PUSHReg ||
|
|
||||||
op_decode_type == S86_OpDecodeType_POPReg ||
|
|
||||||
op_decode_type == S86_OpDecodeType_INCReg ||
|
|
||||||
op_decode_type == S86_OpDecodeType_DECReg ||
|
|
||||||
op_decode_type == S86_OpDecodeType_XCHGRegWithAccum) {
|
|
||||||
uint8_t reg = (op_code_bytes[0] & 0b0000'0111) >> 0;
|
|
||||||
opcode.dest = S86_MnemonicOpFromWReg(1, reg);
|
|
||||||
} else {
|
|
||||||
S86_ASSERT(op_decode_type == S86_OpDecodeType_PUSHSegReg ||
|
|
||||||
op_decode_type == S86_OpDecodeType_POPSegReg);
|
|
||||||
uint8_t sr = (op_code_bytes[0] & 0b0001'1000) >> 3;
|
|
||||||
opcode.dest = S86_MnemonicOpFromSR(sr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op_decode_type == S86_OpDecodeType_XCHGRegWithAccum) {
|
|
||||||
opcode.src = opcode.dest;
|
|
||||||
opcode.dest = S86_MnemonicOp_AX;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
// NOTE: Instruction Pattern => [0b0000'000DW | 0bAABB'BCCC | DISP-LO | DISP-HI | DATA-LO | DATA-HI]
|
|
||||||
// Where, D: optional, W: optional, AA: mod, BBB: reg, CCC: r/m
|
|
||||||
case S86_OpDecodeType_ADDRegOrMemToOrFromReg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_ADCRegOrMemWithRegToEither: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_SUBRegOrMemToOrFromReg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_SBBRegOrMemAndRegToEither: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_ANDRegWithMemToEither: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_TESTRegOrMemAndReg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_ORRegOrMemAndRegToEither: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_XORRegOrMemAndRegToEither: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_LEA: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_LDS: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_LES: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_XCHGRegOrMemWithReg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_CMPRegOrMemAndReg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_MOVRegOrMemToOrFromReg: {
|
|
||||||
// NOTE: Instruction does not have opcode bits in the 2nd byte
|
|
||||||
S86_ASSERT(op_code_size == 1);
|
|
||||||
op_code_bytes[op_code_size++] = S86_BufferIteratorNextByte(&buffer_it);
|
|
||||||
|
|
||||||
uint8_t w = (op_code_bytes[0] & 0b0000'0001) >> 0;
|
|
||||||
uint8_t d = (op_code_bytes[0] & 0b0000'0010) >> 1;
|
|
||||||
if (op_decode_type == S86_OpDecodeType_XCHGRegOrMemWithReg ||
|
|
||||||
op_decode_type == S86_OpDecodeType_LEA ||
|
|
||||||
op_decode_type == S86_OpDecodeType_LDS ||
|
|
||||||
op_decode_type == S86_OpDecodeType_LES) {
|
|
||||||
d = 1; // Destintation is always the register
|
|
||||||
if (op_decode_type == S86_OpDecodeType_XCHGRegOrMemWithReg) {
|
|
||||||
if (lock_prefix) {
|
|
||||||
// NOTE: When we XCHG, NASM complains that the
|
|
||||||
// instruction is not lockable, unless, the memory
|
|
||||||
// operand comes first. Here we flip the direction
|
|
||||||
// to ensure the memory operand is the destination.
|
|
||||||
//
|
|
||||||
// listing_0042_completionist_decode_disassembled.asm|319| warning: instruction is not lockable [-w+prefix-lock]
|
|
||||||
d = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
w = 1; // Always 16 bit (load into register)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t mod = (op_code_bytes[1] & 0b1100'0000) >> 6;
|
|
||||||
uint8_t reg = (op_code_bytes[1] & 0b0011'1000) >> 3;
|
|
||||||
uint8_t rm = (op_code_bytes[1] & 0b0000'0111) >> 0;
|
|
||||||
S86_ASSERT(d < 2);
|
|
||||||
S86_ASSERT(w < 2);
|
|
||||||
S86_ASSERT(mod < 4);
|
|
||||||
S86_ASSERT(reg < 8);
|
|
||||||
S86_ASSERT(rm < 8);
|
|
||||||
|
|
||||||
opcode.wide = w;
|
|
||||||
opcode.src = S86_MnemonicOpFromWReg(opcode.wide, reg);
|
|
||||||
if (mod == 0b11) { // NOTE: Register-to-register move
|
|
||||||
opcode.dest = S86_MnemonicOpFromWReg(opcode.wide, rm);
|
|
||||||
} else { // NOTE: Memory mode w/ effective address calculation
|
|
||||||
S86_DecodeEffectiveAddr(&opcode, &buffer_it, rm, mod, w);
|
|
||||||
opcode.src = S86_MnemonicOpFromWReg(w, reg);
|
|
||||||
if (d)
|
|
||||||
opcode.effective_addr = S86_EffectiveAddress_Src;
|
|
||||||
else
|
|
||||||
opcode.effective_addr = S86_EffectiveAddress_Dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d) {
|
|
||||||
S86_MnemonicOp tmp = opcode.src;
|
|
||||||
opcode.src = opcode.dest;
|
|
||||||
opcode.dest = tmp;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
// NOTE: Instruction Pattern => [0b0000'00SW | 0bAAA00BBB | DISP-LO | DISP-HI | DATA-LO | DATA-HI]
|
|
||||||
// Where S: optional, W: optional, AAA: mod, BBB: rm
|
|
||||||
case S86_OpDecodeType_ADDImmediateToRegOrMem: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_ADCImmediateToRegOrMem: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_SUBImmediateFromRegOrMem: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_SBBImmediateFromRegOrMem: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_CMPImmediateWithRegOrMem: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_ANDImmediateToRegOrMem: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_TESTImmediateAndRegOrMem: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_ORImmediateToRegOrMem: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_XORImmediateToRegOrMem: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_MOVImmediateToRegOrMem: {
|
|
||||||
S86_ASSERT(op_code_size == 2);
|
|
||||||
uint8_t w = (op_code_bytes[0] & 0b0000'0001) >> 0;
|
|
||||||
uint8_t s = (op_code_bytes[0] & 0b0000'0010) >> 1;
|
|
||||||
uint8_t mod = (op_code_bytes[1] & 0b1100'0000) >> 6;
|
|
||||||
uint8_t rm = (op_code_bytes[1] & 0b0000'0111) >> 0;
|
|
||||||
S86_ASSERT(w < 2);
|
|
||||||
S86_ASSERT(mod < 4);
|
|
||||||
S86_ASSERT(rm < 8);
|
|
||||||
S86_DecodeEffectiveAddr(&opcode, &buffer_it, rm, mod, w);
|
|
||||||
|
|
||||||
// NOTE: Parse data payload
|
|
||||||
// =============================================================
|
|
||||||
uint16_t data = S86_BufferIteratorNextByte(&buffer_it);
|
|
||||||
if (w) { // 16 bit data
|
|
||||||
if ((op_decode_type == S86_OpDecodeType_ADDImmediateToRegOrMem ||
|
|
||||||
op_decode_type == S86_OpDecodeType_ADCImmediateToRegOrMem ||
|
|
||||||
op_decode_type == S86_OpDecodeType_SUBImmediateFromRegOrMem ||
|
|
||||||
op_decode_type == S86_OpDecodeType_SBBImmediateFromRegOrMem ||
|
|
||||||
op_decode_type == S86_OpDecodeType_CMPImmediateWithRegOrMem ||
|
|
||||||
op_decode_type == S86_OpDecodeType_ANDImmediateToRegOrMem ||
|
|
||||||
op_decode_type == S86_OpDecodeType_TESTImmediateAndRegOrMem ||
|
|
||||||
op_decode_type == S86_OpDecodeType_ORImmediateToRegOrMem ||
|
|
||||||
op_decode_type == S86_OpDecodeType_XORImmediateToRegOrMem) && s) {
|
|
||||||
// NOTE: Sign extend 8 bit, since we store into a
|
|
||||||
// int32_t in opcode this is done for free for us.
|
|
||||||
} else {
|
|
||||||
uint8_t data_hi = S86_BufferIteratorNextByte(&buffer_it);
|
|
||||||
data |= (uint16_t)(data_hi) << 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op_decode_type == S86_OpDecodeType_MOVImmediateToRegOrMem) {
|
|
||||||
S86_ASSERT(mod != 0b11); // NOTE: Op is IMM->Reg, register-to-register not permitted
|
|
||||||
}
|
|
||||||
|
|
||||||
opcode.immediate = data;
|
|
||||||
opcode.src = S86_MnemonicOp_Immediate;
|
|
||||||
if (op_decode_type == S86_OpDecodeType_MOVImmediateToRegOrMem)
|
|
||||||
opcode.wide_prefix = S86_WidePrefix_Src;
|
|
||||||
else if (opcode.effective_addr_loads_mem)
|
|
||||||
opcode.wide_prefix = S86_WidePrefix_Dest;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
// NOTE: Instruction Pattern => [0b0000'W00W | DATA-LO | DATA-HI]
|
|
||||||
case S86_OpDecodeType_ADDImmediateToAccum: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_ADCImmediateToAccum: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_SUBImmediateFromAccum: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_SBBImmediateFromAccum: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_CMPImmediateWithAccum: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_ANDImmediateToAccum: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_TESTImmediateAndAccum: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_ORImmediateToAccum: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_XORImmediateToAccum: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_MOVImmediateToReg: {
|
|
||||||
// NOTE: Parse opcode control bits
|
|
||||||
// =============================================================
|
|
||||||
S86_ASSERT(op_code_size == 1);
|
|
||||||
uint8_t w = 0;
|
|
||||||
if (op_decode_type == S86_OpDecodeType_ADDImmediateToAccum ||
|
|
||||||
op_decode_type == S86_OpDecodeType_ADCImmediateToAccum ||
|
|
||||||
op_decode_type == S86_OpDecodeType_SUBImmediateFromAccum ||
|
|
||||||
op_decode_type == S86_OpDecodeType_SBBImmediateFromAccum ||
|
|
||||||
op_decode_type == S86_OpDecodeType_CMPImmediateWithAccum ||
|
|
||||||
op_decode_type == S86_OpDecodeType_ANDImmediateToAccum ||
|
|
||||||
op_decode_type == S86_OpDecodeType_TESTImmediateAndAccum ||
|
|
||||||
op_decode_type == S86_OpDecodeType_ORImmediateToAccum ||
|
|
||||||
op_decode_type == S86_OpDecodeType_XORImmediateToAccum) {
|
|
||||||
w = (op_code_bytes[0] & 0b0000'0001) >> 0;
|
|
||||||
} else {
|
|
||||||
w = (op_code_bytes[0] & 0b0000'1000) >> 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Parse data payload
|
|
||||||
// =============================================================
|
|
||||||
uint16_t data = S86_BufferIteratorNextByte(&buffer_it);
|
|
||||||
if (w) { // 16 bit data
|
|
||||||
uint8_t data_hi = S86_BufferIteratorNextByte(&buffer_it);
|
|
||||||
data |= (uint16_t)(data_hi) << 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Disassemble
|
|
||||||
// =============================================================
|
|
||||||
opcode.effective_addr = S86_EffectiveAddress_Dest;
|
|
||||||
opcode.src = S86_MnemonicOp_Immediate;
|
|
||||||
opcode.wide = w;
|
|
||||||
opcode.src = S86_MnemonicOp_Immediate;
|
|
||||||
opcode.immediate = data;
|
|
||||||
if (op_decode_type == S86_OpDecodeType_MOVImmediateToReg) {
|
|
||||||
uint8_t reg = (op_code_bytes[0] & 0b0000'0111) >> 0;
|
|
||||||
opcode.dest = S86_MnemonicOpFromWReg(w, reg);
|
|
||||||
} else {
|
|
||||||
opcode.dest = opcode.wide ? S86_MnemonicOp_AX : S86_MnemonicOp_AL;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
// NOTE: Instruction Pattern => [0b0000'000W | DATA-LO]
|
|
||||||
case S86_OpDecodeType_INFixedPort: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_INVariablePort: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_OUTFixedPort: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_OUTVariablePort: {
|
|
||||||
S86_ASSERT(op_code_size == 1);
|
|
||||||
opcode.wide = (op_code_bytes[0] & 0b0000'0001) >> 0;
|
|
||||||
opcode.dest = opcode.wide ? S86_MnemonicOp_AX : S86_MnemonicOp_AL;
|
|
||||||
if (op_decode_type == S86_OpDecodeType_INFixedPort ||
|
|
||||||
op_decode_type == S86_OpDecodeType_OUTFixedPort) {
|
|
||||||
opcode.src = S86_MnemonicOp_Immediate;
|
|
||||||
opcode.immediate = S86_BufferIteratorNextByte(&buffer_it);
|
|
||||||
} else {
|
|
||||||
opcode.src = S86_MnemonicOp_DX;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op_decode_type == S86_OpDecodeType_OUTFixedPort ||
|
|
||||||
op_decode_type == S86_OpDecodeType_OUTVariablePort) {
|
|
||||||
S86_MnemonicOp tmp = opcode.src;
|
|
||||||
opcode.src = opcode.dest;
|
|
||||||
opcode.dest = tmp;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case S86_OpDecodeType_REP: {
|
|
||||||
S86_ASSERT(op_code_size == 1);
|
|
||||||
uint8_t string_op = S86_BufferIteratorNextByte(&buffer_it);
|
|
||||||
uint8_t w_mask = 0b0000'0001;
|
|
||||||
opcode.rep_prefix = true;
|
|
||||||
opcode.wide = string_op & w_mask;
|
|
||||||
switch (string_op & ~w_mask) {
|
|
||||||
case 0b1010'0100: opcode.dest = S86_MnemonicOp_MOVS; break;
|
|
||||||
case 0b1010'0110: opcode.dest = S86_MnemonicOp_CMPS; break;
|
|
||||||
case 0b1010'1110: opcode.dest = S86_MnemonicOp_SCAS; break;
|
|
||||||
case 0b1010'1100: opcode.dest = S86_MnemonicOp_LODS; break;
|
|
||||||
case 0b1010'1010: opcode.dest = S86_MnemonicOp_STOS; break;
|
|
||||||
default: S86_ASSERT(!"Unhandled REP string type"); break;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
// NOTE: Instruction Pattern => [0b0000'0000 | DATA-LO | DATA-HI]
|
|
||||||
case S86_OpDecodeType_MOVAccumToMem: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_MOVMemToAccum: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_CALLDirectInterSeg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_CALLDirectWithinSeg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_JMPDirectInterSeg: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_RETWithinSegAddImmediateToSP: /*FALLTHRU*/
|
|
||||||
case S86_OpDecodeType_INT: {
|
|
||||||
S86_ASSERT(op_code_size == 1);
|
|
||||||
uint8_t data_lo = S86_BufferIteratorNextByte(&buffer_it);
|
|
||||||
uint16_t data = data_lo;
|
|
||||||
if (op_decode_type != S86_OpDecodeType_INT) {
|
|
||||||
uint8_t data_hi = S86_BufferIteratorNextByte(&buffer_it);
|
|
||||||
data = S86_CAST(uint16_t)data_hi << 8 | (S86_CAST(uint16_t)data_lo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op_decode_type == S86_OpDecodeType_CALLDirectWithinSeg) {
|
|
||||||
opcode.effective_addr = S86_EffectiveAddress_Dest;
|
|
||||||
opcode.dest = S86_MnemonicOp_BP;
|
|
||||||
opcode.displacement = -S86_CAST(int32_t)data;
|
|
||||||
} else if (op_decode_type == S86_OpDecodeType_RETWithinSegAddImmediateToSP) {
|
|
||||||
opcode.dest = S86_MnemonicOp_DirectAddress;
|
|
||||||
opcode.displacement = data;
|
|
||||||
} else if (op_decode_type == S86_OpDecodeType_CALLDirectInterSeg ||
|
|
||||||
op_decode_type == S86_OpDecodeType_JMPDirectInterSeg) {
|
|
||||||
uint8_t cs_lo = S86_BufferIteratorNextByte(&buffer_it);
|
|
||||||
uint8_t cs_hi = S86_BufferIteratorNextByte(&buffer_it);
|
|
||||||
uint16_t cs = S86_CAST(uint16_t)cs_hi << 8 | (S86_CAST(uint16_t)cs_lo);
|
|
||||||
opcode.displacement = (uint32_t)cs << 16 | (uint32_t)data << 0;
|
|
||||||
opcode.dest = S86_MnemonicOp_DirectInterSegment;
|
|
||||||
} else if (op_decode_type == S86_OpDecodeType_MOVAccumToMem) {
|
|
||||||
opcode.effective_addr_loads_mem = true;
|
|
||||||
opcode.effective_addr = S86_EffectiveAddress_Dest;
|
|
||||||
opcode.dest = S86_MnemonicOp_DirectAddress;
|
|
||||||
opcode.displacement = data;
|
|
||||||
opcode.src = S86_MnemonicOp_AX;
|
|
||||||
} else if (op_decode_type == S86_OpDecodeType_MOVMemToAccum) {
|
|
||||||
opcode.effective_addr_loads_mem = true;
|
|
||||||
opcode.effective_addr = S86_EffectiveAddress_Src;
|
|
||||||
opcode.src = S86_MnemonicOp_DirectAddress;
|
|
||||||
opcode.displacement = data;
|
|
||||||
opcode.dest = S86_MnemonicOp_AX;
|
|
||||||
} else {
|
|
||||||
opcode.dest = S86_MnemonicOp_Immediate;
|
|
||||||
opcode.immediate = data;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default: {
|
|
||||||
if (op_decode_type >= S86_OpDecodeType_JE_JZ && op_decode_type <= S86_OpDecodeType_JCXZ) {
|
|
||||||
S86_ASSERT(op_code_size == 1);
|
|
||||||
opcode.displacement = S86_CAST(int8_t)S86_BufferIteratorNextByte(&buffer_it);
|
|
||||||
opcode.dest = S86_MnemonicOp_Jump;
|
|
||||||
} else if (op_decode_type == S86_OpDecodeType_XLAT ||
|
|
||||||
op_decode_type == S86_OpDecodeType_LAHF ||
|
|
||||||
op_decode_type == S86_OpDecodeType_SAHF ||
|
|
||||||
op_decode_type == S86_OpDecodeType_PUSHF ||
|
|
||||||
op_decode_type == S86_OpDecodeType_POPF ||
|
|
||||||
op_decode_type == S86_OpDecodeType_DAA ||
|
|
||||||
op_decode_type == S86_OpDecodeType_AAA ||
|
|
||||||
op_decode_type == S86_OpDecodeType_DAS ||
|
|
||||||
op_decode_type == S86_OpDecodeType_AAS ||
|
|
||||||
op_decode_type == S86_OpDecodeType_AAM ||
|
|
||||||
op_decode_type == S86_OpDecodeType_AAD ||
|
|
||||||
op_decode_type == S86_OpDecodeType_CBW ||
|
|
||||||
op_decode_type == S86_OpDecodeType_CWD ||
|
|
||||||
op_decode_type == S86_OpDecodeType_RETWithinSeg ||
|
|
||||||
op_decode_type == S86_OpDecodeType_INT3 ||
|
|
||||||
op_decode_type == S86_OpDecodeType_INTO ||
|
|
||||||
op_decode_type == S86_OpDecodeType_IRET ||
|
|
||||||
op_decode_type == S86_OpDecodeType_CLC ||
|
|
||||||
op_decode_type == S86_OpDecodeType_CMC ||
|
|
||||||
op_decode_type == S86_OpDecodeType_STC ||
|
|
||||||
op_decode_type == S86_OpDecodeType_CLD ||
|
|
||||||
op_decode_type == S86_OpDecodeType_STD ||
|
|
||||||
op_decode_type == S86_OpDecodeType_CLI ||
|
|
||||||
op_decode_type == S86_OpDecodeType_STI ||
|
|
||||||
op_decode_type == S86_OpDecodeType_HLT ||
|
|
||||||
op_decode_type == S86_OpDecodeType_WAIT) {
|
|
||||||
// NOTE: Mnemonic only instruction
|
|
||||||
} else if (op_decode_type == S86_OpDecodeType_LOCK) {
|
|
||||||
lock_prefix = true;
|
|
||||||
opcode.lock_prefix = true;
|
|
||||||
} else if (op_decode_type == S86_OpDecodeType_SEGMENT) {
|
|
||||||
// NOTE: Mnemonic does not generate any assembly
|
|
||||||
S86_ASSERT(op_code_size == 1);
|
|
||||||
uint8_t sr = (op_code_bytes[0] & 0b0001'1000) >> 3;
|
|
||||||
seg_reg = S86_MnemonicOpFromSR(sr);
|
|
||||||
} else {
|
|
||||||
S86_ASSERT(!"Unhandled instruction");
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
|
|
||||||
S86_PrintOpcode(opcode);
|
S86_PrintOpcode(opcode);
|
||||||
if (op_decode_type != S86_OpDecodeType_LOCK)
|
|
||||||
lock_prefix = false;
|
|
||||||
|
|
||||||
if (op_decode_type != S86_OpDecodeType_SEGMENT)
|
|
||||||
seg_reg = S86_MnemonicOp_Invalid;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user