ft: fixed modrm target calculation
While implementing some more mnemonics and testing them, it was clear that modrm parsing was wrong. Now reg to reg and immediates, together with GPR1 interpretation should work as expected. GPR1 interpretation can currently not be merged into the modrm function, as with the current abstraction the REG needs to select the correct mnemonic, for which we need to also know the second operand, which will only be parsed afterwards. But this will be incorporated at some point, this just marks the first working state.
This commit is contained in:
@@ -12,9 +12,9 @@ pub type w = u16;
|
||||
/// contains the `Opcode` that will be executed, alongside its starting offset
|
||||
/// and the raw parsed bytes
|
||||
pub struct Instruction {
|
||||
pub start: usize, // location of the instruction start
|
||||
pub raw: Vec<u8>, // raw value of instruction
|
||||
pub opcode: Opcode, // actual instruction
|
||||
pub start: usize, // location of the instruction start
|
||||
pub raw: Vec<u8>, // raw value of instruction
|
||||
pub opcode: Mnemonic, // actual instruction
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
@@ -22,7 +22,7 @@ impl Instruction {
|
||||
Instruction {
|
||||
start: 0,
|
||||
raw: Vec::new(),
|
||||
opcode: Opcode::NOP(),
|
||||
opcode: Mnemonic::NOP(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,61 +39,163 @@ impl fmt::Display for Instruction {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code, non_camel_case_types)]
|
||||
pub enum Opcode {
|
||||
/// All possible opcode variantions.
|
||||
// XXX: convert this copy and paste horror in a proc macro like
|
||||
// enum Opcode {
|
||||
// #[derive(default_variations)]
|
||||
// ADD,
|
||||
// ...
|
||||
// }
|
||||
// which then add all variants and also create the matching logic for
|
||||
// src/disasm.rs decode_instructions()
|
||||
pub enum Mnemonic {
|
||||
NOP(),
|
||||
// ADD
|
||||
ADD_EbGb(MemoryIndex, Register),
|
||||
ADD_EvGv(MemoryIndex, Register),
|
||||
ADD_GbEb(MemoryIndex, Register),
|
||||
ADD_GvEv(MemoryIndex, Register),
|
||||
ADD_FromReg(ModRmTarget, Register),
|
||||
ADD_ToReg(ModRmTarget, Register),
|
||||
ADD_Ib(ModRmTarget, ImmediateByte),
|
||||
ADD_Iv(ModRmTarget, ImmediateWord),
|
||||
ADD_ALIb(ImmediateByte),
|
||||
ADD_AXIv(ImmediateWord),
|
||||
// PUSH
|
||||
PUSH(SegmentRegister),
|
||||
PUSH_R(Register),
|
||||
PUSH_S(SegmentRegister),
|
||||
// POP
|
||||
POP(SegmentRegister),
|
||||
POP_S(SegmentRegister),
|
||||
POP_R(Register),
|
||||
// OR
|
||||
OR_EbGb(MemoryIndex, Register),
|
||||
OR_GbEb(MemoryIndex, Register),
|
||||
OR_FromReg(ModRmTarget, Register),
|
||||
OR_ToReg(ModRmTarget, Register),
|
||||
OR_Ib(ModRmTarget, ImmediateByte),
|
||||
OR_Iv(ModRmTarget, ImmediateWord),
|
||||
OR_ALIb(ImmediateByte),
|
||||
OR_AXIv(ImmediateWord),
|
||||
// ADC
|
||||
ADC_FromReg(ModRmTarget, Register),
|
||||
ADC_ToReg(ModRmTarget, Register),
|
||||
ADC_Ib(ModRmTarget, ImmediateByte),
|
||||
ADC_Iv(ModRmTarget, ImmediateWord),
|
||||
ADC_ALIb(ImmediateByte),
|
||||
ADC_AXIv(ImmediateWord),
|
||||
// SBB
|
||||
SBB_FromReg(ModRmTarget, Register),
|
||||
SBB_ToReg(ModRmTarget, Register),
|
||||
SBB_Ib(ModRmTarget, ImmediateByte),
|
||||
SBB_Iv(ModRmTarget, ImmediateWord),
|
||||
SBB_ALIb(ImmediateByte),
|
||||
SBB_AXIv(ImmediateWord),
|
||||
// AND
|
||||
AND_FromReg(ModRmTarget, Register),
|
||||
AND_ToReg(ModRmTarget, Register),
|
||||
AND_Ib(ModRmTarget, ImmediateByte),
|
||||
AND_Iv(ModRmTarget, ImmediateWord),
|
||||
AND_ALIb(ImmediateByte),
|
||||
AND_AXIv(ImmediateWord),
|
||||
// Override
|
||||
OVERRIDE(SegmentRegister),
|
||||
// Decimal Adjustment
|
||||
DAA,
|
||||
DAS,
|
||||
AAA,
|
||||
AAS,
|
||||
// SUB
|
||||
SUB_FromReg(ModRmTarget, Register),
|
||||
SUB_ToReg(ModRmTarget, Register),
|
||||
SUB_Ib(ModRmTarget, ImmediateByte),
|
||||
SUB_Iv(ModRmTarget, ImmediateWord),
|
||||
SUB_ALIb(ImmediateByte),
|
||||
SUB_AXIv(ImmediateWord),
|
||||
// XOR
|
||||
XOR_FromReg(ModRmTarget, Register),
|
||||
XOR_ToReg(ModRmTarget, Register),
|
||||
XOR_Ib(ModRmTarget, ImmediateByte),
|
||||
XOR_Iv(ModRmTarget, ImmediateWord),
|
||||
XOR_ALIb(ImmediateByte),
|
||||
XOR_AXIv(ImmediateWord),
|
||||
// CMP
|
||||
CMP_FromReg(ModRmTarget, Register),
|
||||
CMP_ToReg(ModRmTarget, Register),
|
||||
CMP_Ib(ModRmTarget, ImmediateByte),
|
||||
CMP_Iv(ModRmTarget, ImmediateWord),
|
||||
CMP_ALIb(ImmediateByte),
|
||||
CMP_AXIv(ImmediateWord),
|
||||
// INC
|
||||
INC(Register),
|
||||
// DEC
|
||||
DEC(Register),
|
||||
// Jumps
|
||||
JO(ImmediateByte),
|
||||
JNO(ImmediateByte),
|
||||
JB(ImmediateByte),
|
||||
JNB(ImmediateByte),
|
||||
JZ(ImmediateByte),
|
||||
JNZ(ImmediateByte),
|
||||
JBE(ImmediateByte),
|
||||
JA(ImmediateByte),
|
||||
JS(ImmediateByte),
|
||||
JNS(ImmediateByte),
|
||||
JPE(ImmediateByte),
|
||||
JPO(ImmediateByte),
|
||||
JL(ImmediateByte),
|
||||
JGE(ImmediateByte),
|
||||
JLE(ImmediateByte),
|
||||
JG(ImmediateByte),
|
||||
// TEST
|
||||
TEST(ModRmTarget, Register),
|
||||
//XHCG
|
||||
XHCG(ModRmTarget, Register),
|
||||
// MOV
|
||||
MOV_FromReg(ModRmTarget, Register),
|
||||
MOV_ToReg(ModRmTarget, Register),
|
||||
MOV_FromSReg(ModRmTarget, SegmentRegister),
|
||||
MOV_ToSReg(ModRmTarget, SegmentRegister),
|
||||
MOV_BXIv(ImmediateWord),
|
||||
// LEA
|
||||
LEA(ModRmTarget, Register),
|
||||
// INT
|
||||
INT(ImmediateByte),
|
||||
// MOV
|
||||
MOV_BXIv(ImmediateWord),
|
||||
}
|
||||
|
||||
impl fmt::Display for Opcode {
|
||||
impl fmt::Display for Mnemonic {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::INT(byte) => write!(f, "INT, {:x}", byte),
|
||||
Self::ADD_EbGb(mem, reg) => write!(f, "ADD {}, {}", mem, reg),
|
||||
Self::ADD_GbEb(mem, reg) => write!(f, "ADD {}, {}", reg, mem),
|
||||
Self::ADD_FromReg(mem, reg) => write!(f, "ADD {}, {}", mem, reg),
|
||||
Self::ADD_ToReg(mem, reg) => write!(f, "ADD {}, {}", reg, mem),
|
||||
Self::CMP_Iv(mem, imm) => write!(f, "CMP {}, {:04x}", mem, imm),
|
||||
Self::LEA(mem, reg) => write!(f, "LEA {}, {}", reg, mem),
|
||||
Self::MOV_BXIv(word) => write!(f, "MOV BX, {:04x}", word),
|
||||
_ => write!(f, "display not yet implemented"),
|
||||
Self::XOR_FromReg(mem, reg) => write!(f, "XOR {}, {}", mem, reg),
|
||||
_ => write!(f, "??? ??, ??"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers of a 8086 processor
|
||||
/// -x are 16bit, -l are 8bit
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub enum Register {
|
||||
AX,
|
||||
BX,
|
||||
CX,
|
||||
DX,
|
||||
AH,
|
||||
// 8 bit
|
||||
// low bytes
|
||||
AL,
|
||||
BL,
|
||||
BH,
|
||||
CH,
|
||||
CL,
|
||||
DH,
|
||||
DL,
|
||||
DI,
|
||||
SI,
|
||||
BP,
|
||||
SP,
|
||||
BL,
|
||||
// high bytes
|
||||
AH,
|
||||
CH,
|
||||
DH,
|
||||
BH,
|
||||
|
||||
// 16 bit
|
||||
AX, // accumulator
|
||||
CX, // counter
|
||||
DX, // data
|
||||
BX, // base
|
||||
SP, // stack pointer
|
||||
BP, // base pointer
|
||||
SI, // source index
|
||||
DI, // base index
|
||||
}
|
||||
|
||||
/// Selector for Register or Segment Register
|
||||
@@ -102,25 +204,30 @@ pub type RegisterId = u8;
|
||||
#[allow(dead_code)]
|
||||
impl Register {
|
||||
/// Find the register corresponding to the 8086 bytecode ID
|
||||
pub fn by_id(id: RegisterId) -> Self {
|
||||
pub fn by_id(id: OperandSize) -> Self {
|
||||
match id {
|
||||
0x00 => Self::AL,
|
||||
0x01 => Self::CL,
|
||||
0x02 => Self::DL,
|
||||
0x03 => Self::BL,
|
||||
0x04 => Self::AH,
|
||||
0x05 => Self::CH,
|
||||
0x06 => Self::DH,
|
||||
0x07 => Self::BH,
|
||||
0x10 => Self::AX,
|
||||
0x11 => Self::CX,
|
||||
0x12 => Self::DX,
|
||||
0x13 => Self::BX,
|
||||
0x14 => Self::SP,
|
||||
0x15 => Self::BP,
|
||||
0x16 => Self::SI,
|
||||
0x17 => Self::DI,
|
||||
_ => panic!("Invalid register ID encountered"),
|
||||
OperandSize::Byte(b) => match b {
|
||||
0b000 => Self::AL,
|
||||
0b001 => Self::CL,
|
||||
0b010 => Self::DL,
|
||||
0b011 => Self::BL,
|
||||
0b100 => Self::AH,
|
||||
0b101 => Self::CH,
|
||||
0b110 => Self::DH,
|
||||
0b111 => Self::BH,
|
||||
_ => panic!("Invalid 8bit register ID encountered"),
|
||||
},
|
||||
OperandSize::Word(w) => match w {
|
||||
0b000 => Self::AX,
|
||||
0b001 => Self::CX,
|
||||
0b010 => Self::DX,
|
||||
0b011 => Self::BX,
|
||||
0b100 => Self::SP,
|
||||
0b101 => Self::BP,
|
||||
0b110 => Self::SI,
|
||||
0b111 => Self::DI,
|
||||
_ => panic!("Invalid 16bit register ID encountered"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,10 +270,10 @@ impl SegmentRegister {
|
||||
/// Find the SRegister corresponding to the 8086 bytecode ID
|
||||
pub fn by_id(id: u8) -> Self {
|
||||
match id {
|
||||
0x30 => Self::ES,
|
||||
0x31 => Self::CS,
|
||||
0x32 => Self::SS,
|
||||
0x33 => Self::DS,
|
||||
0x00 => Self::ES,
|
||||
0x01 => Self::CS,
|
||||
0x10 => Self::SS,
|
||||
0x11 => Self::DS,
|
||||
_ => panic!("Invalid segment register ID encountered"),
|
||||
}
|
||||
}
|
||||
@@ -210,13 +317,29 @@ macro_rules! impl_display_and_lowerhex {
|
||||
impl_display_and_lowerhex!(ImmediateByte);
|
||||
impl_display_and_lowerhex!(ImmediateWord);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// ModRM byte can either target a memory location or some register
|
||||
pub enum ModRmTarget {
|
||||
Memory(MemoryIndex),
|
||||
Register(Register),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ModRmTarget {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Memory(idx) => write!(f, "{}", idx),
|
||||
Self::Register(reg) => write!(f, "{}", reg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A memory index operand is usually created by ModRM bytes or words.
|
||||
/// e.g. [bx+si]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MemoryIndex {
|
||||
pub base: Option<Register>,
|
||||
pub index: Option<Register>,
|
||||
pub displacement: Option<Displacement>,
|
||||
pub displacement: Option<OperandSize>,
|
||||
}
|
||||
|
||||
impl fmt::Display for MemoryIndex {
|
||||
@@ -224,18 +347,18 @@ impl fmt::Display for MemoryIndex {
|
||||
match &self.base {
|
||||
Some(base) => match &self.index {
|
||||
Some(index) => match &self.displacement {
|
||||
Some(displacement) => write!(f, "[{}+{}+{}]", base, index, displacement),
|
||||
None => write!(f, "[{}+{}]", base, index),
|
||||
Some(displacement) => write!(f, "[{} + {} + {}]", base, index, displacement),
|
||||
None => write!(f, "[{} + {}]", base, index),
|
||||
},
|
||||
None => match &self.displacement {
|
||||
Some(displacement) => write!(f, "[{}+{}]", base, displacement),
|
||||
None => write!(f, "{}", base),
|
||||
Some(displacement) => write!(f, "[{} + {}]", base, displacement),
|
||||
None => write!(f, "[{} + 0]", base),
|
||||
},
|
||||
},
|
||||
None => match &self.index {
|
||||
Some(index) => match &self.displacement {
|
||||
Some(displacement) => write!(f, "{}+{}", index, displacement),
|
||||
None => write!(f, "{}", index),
|
||||
Some(displacement) => write!(f, "{} + {}", index, displacement),
|
||||
None => write!(f, "[{} + 0]", index),
|
||||
},
|
||||
None => panic!("Invalid MemoryIndex encountered"),
|
||||
},
|
||||
@@ -245,13 +368,13 @@ impl fmt::Display for MemoryIndex {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
/// Displacement for ModRM
|
||||
pub enum Displacement {
|
||||
/// Can be used to encode either byte or word operands
|
||||
pub enum OperandSize {
|
||||
Byte(u8),
|
||||
Word(u16),
|
||||
}
|
||||
|
||||
impl fmt::Display for Displacement {
|
||||
impl fmt::Display for OperandSize {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Byte(byte) => write!(f, "{}", byte),
|
||||
|
||||
Reference in New Issue
Block a user