diff --git a/src/disasm.rs b/src/disasm.rs index 37b8aa1..367d142 100644 --- a/src/disasm.rs +++ b/src/disasm.rs @@ -19,6 +19,7 @@ pub enum DisasmError { NoFile(Option), IoError(std::io::Error), OpcodeUndefined(u8), + IndexOutOfBounds(usize), } impl From for DisasmError { @@ -37,6 +38,7 @@ impl fmt::Display for DisasmError { "Instruction '{:#x} is considered undefined by the Spec", opcode ), + DisasmError::IndexOutOfBounds(msg) => write!(f, "Out of bounds read at {}", msg), } } } @@ -67,7 +69,7 @@ fn path_to_buf(args: &Args) -> Result, DisasmError> { } #[derive(Debug)] -struct Disassembler { +pub struct Disassembler { pub offset: usize, // the current offset in the disasm process pub text: Vec, // the aout binary pub instruction: Instruction, // the instruction, which is currently being parsed @@ -97,6 +99,7 @@ impl Disassembler { pub fn parse_word(&mut self) -> u16 { // advance to operand self.offset += 1; + // XXX: wrap in Result<> let byte1 = self.text[self.offset]; let byte2 = self.text[self.offset + 1]; // jump onto last operand @@ -327,6 +330,15 @@ impl Disassembler { } } + /// Parse an Mp Operand (Memory Pointer). + /// An Mp is a ModRM byte with the `reg` bits ignored and an additional + /// 2 words parsed for a `Pointer` type. + pub fn modrm_mp(&mut self) -> Result<(ModRmTarget, Pointer), DisasmError> { + let (target, _) = self.parse_modrm_byte(Operand::Byte(0)); + let ptr = Pointer::new(self)?; + Ok((target, ptr)) + } + /// Decode instructions from the text section of the provided binary pub fn decode_instructions(&mut self) -> Result, DisasmError> { // naive approach: @@ -602,7 +614,14 @@ impl Disassembler { 0xC2 => Mnemonic::RET_Iw(self.parse_word()), 0xC3 => Mnemonic::RET, - 0xC4..=0xC5 => todo!("LES and LDS not yet implemented"), + 0xC4 => { + let (target, ptr) = self.modrm_mp()?; + Mnemonic::LES(target, ptr) + } + 0xC5 => { + let (target, ptr) = self.modrm_mp()?; + Mnemonic::LDS(target, ptr) + } 0xC6 => { let (target, _) = self.parse_modrm_byte(Operand::Byte(0)); @@ -664,16 +683,7 @@ impl Disassembler { 0xE8 => Mnemonic::CALL_v(self.parse_j_word()), 0xE9 => Mnemonic::JMP_v(self.parse_j_word()), - 0xEA => Mnemonic::JMP_p(Pointer { - raw: DWord::from_le_bytes([ - self.text[self.offset], - self.text[self.offset + 1], - self.text[self.offset + 2], - self.text[self.offset + 3], - ]), - segment: self.parse_word(), - offset: self.parse_word(), - }), + 0xEA => Mnemonic::JMP_p(Pointer::new(self)?), 0xEB => Mnemonic::JMP_b(self.parse_j_byte()), 0xEC => Mnemonic::IN_ALDX, @@ -722,9 +732,9 @@ impl Disassembler { 0b000 => Mnemonic::INC_Mod(target), 0b001 => Mnemonic::DEC_Mod(target), 0b010 => Mnemonic::CALL_Mod(target), - 0b011 => todo!("Implement CALL Mp"), + 0b011 => Mnemonic::CALL_Mp(target, Pointer::new(self)?), 0b100 => Mnemonic::JMP_Mod(target), - 0b101 => todo!("Implement JMP Mp"), + 0b101 => Mnemonic::JMP_Mp(target, Pointer::new(self)?), 0b110 => Mnemonic::PUSH_Mod(target), // 0b111 => unused _ => panic!("Illegal Group 5 mnemonic"), diff --git a/src/instructions.rs b/src/instructions.rs index f252900..91e86f1 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -206,11 +206,13 @@ pub enum Mnemonic { CALL_p(Pointer), CALL_v(isize), CALL_Mod(ModRmTarget), + CALL_Mp(ModRmTarget, Pointer), // JUMP JMP_p(Pointer), JMP_b(isize), // parses IByte, but stores as isize JMP_v(isize), // parwses IWord, but stores as isize JMP_Mod(ModRmTarget), + JMP_Mp(ModRmTarget, Pointer), // WAIT WAIT, // Push/Pop Flags @@ -236,8 +238,8 @@ pub enum Mnemonic { RETF, IRET, // Load ES/DS Register - LES(ModRmTarget), - LDS(ModRmTarget), + LES(ModRmTarget, Pointer), + LDS(ModRmTarget, Pointer), // NOT NOT(ModRmTarget), // NEG @@ -443,11 +445,13 @@ impl fmt::Display for Mnemonic { Self::CALL_p(ptr) => write!(f, "call {ptr}"), Self::CALL_v(word) => write!(f, "call {word:#04x}"), Self::CALL_Mod(target) => write!(f, "call {target}"), + Self::CALL_Mp(target, ptr) => write!(f, "call {target}, {ptr}"), Self::JMP_p(ptr) => write!(f, "jmp {ptr}"), Self::JMP_b(byte) => write!(f, "jmp {byte:#04x}"), Self::JMP_v(word) => write!(f, "jmp {word:#04x}"), Self::JMP_Mod(target) => write!(f, "jmp {target}"), + Self::JMP_Mp(target, ptr) => write!(f, "jmp {target}, {ptr}"), Self::WAIT => write!(f, "wait"), @@ -473,8 +477,8 @@ impl fmt::Display for Mnemonic { Self::RETF => write!(f, "retf"), Self::IRET => write!(f, "iret"), - Self::LES(target) => write!(f, "les {target}"), - Self::LDS(target) => write!(f, "lds {target}"), + Self::LES(target, ptr) => write!(f, "les {target}, {ptr}"), + Self::LDS(target, ptr) => write!(f, "lds {target}, {ptr}"), Self::NOT(target) => write!(f, "not {target}"), Self::NEG(target) => write!(f, "neg {target}"), diff --git a/src/operands.rs b/src/operands.rs index 7e85868..174628b 100644 --- a/src/operands.rs +++ b/src/operands.rs @@ -1,7 +1,10 @@ //! All types which a Mnemonic can have as some kind of operand. //! This includes things such as immediates, ModRM byte targets, etc. etc. -use crate::register::Register; +use crate::{ + disasm::{DisasmError, Disassembler}, + register::Register, +}; use core::fmt; pub type Byte = u8; // b @@ -136,6 +139,33 @@ pub struct Pointer { pub offset: Word, } +impl Pointer { + pub fn new(disasm: &mut Disassembler) -> Result { + let byte0 = disasm + .text + .get(disasm.offset) + .ok_or(DisasmError::IndexOutOfBounds(disasm.offset))?; + let byte1 = disasm + .text + .get(disasm.offset + 1) + .ok_or(DisasmError::IndexOutOfBounds(disasm.offset + 1))?; + let byte2 = disasm + .text + .get(disasm.offset + 2) + .ok_or(DisasmError::IndexOutOfBounds(disasm.offset + 2))?; + let byte3 = disasm + .text + .get(disasm.offset + 3) + .ok_or(DisasmError::IndexOutOfBounds(disasm.offset + 3))?; + + Ok(Pointer { + raw: DWord::from_le_bytes([*byte0, *byte1, *byte2, *byte3]), + segment: disasm.parse_word(), + offset: disasm.parse_word(), + }) + } +} + impl std::fmt::Display for Pointer { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "[{:#04x}] ({}:{})", self.raw, self.segment, self.offset)