diff --git a/src/disasm.rs b/src/disasm.rs index 237d1f2..8146c3f 100644 --- a/src/disasm.rs +++ b/src/disasm.rs @@ -1,6 +1,7 @@ //! The main dissembling logic. use crate::aout::Aout; +use crate::instructions::RepeatableStringOperation; use crate::operands::{ Byte, DWord, IByte, IWord, ImmediateOperand, MemoryIndex, ModRmTarget, Pointer16, Pointer32, Word, @@ -241,6 +242,31 @@ impl Disassembler { }) } + /// Parse one of the 4 Mnemonics, which can be repeated by the REPZ/REPNZ + /// mnemonic. + /// Returns the parsed Mnemonic. + pub fn parse_rep_op(&mut self) -> Result { + log::debug!( + "Attempting to parse repeatable string mnemonic at {:#04x} ...", + self.offset + ); + let byte = self.parse_byte()?; + let mnemonic = match byte { + 0xA4 => RepeatableStringOperation::MOVSB(Mnemonic::MOVSB), + 0xA5 => RepeatableStringOperation::MOVSW(Mnemonic::MOVSW), + 0xA6 => RepeatableStringOperation::CMPSB(Mnemonic::CMPSB), + 0xA7 => RepeatableStringOperation::CMPSW(Mnemonic::CMPSW), + 0xAE => RepeatableStringOperation::SCASB(Mnemonic::SCASB), + 0xAF => RepeatableStringOperation::SCASW(Mnemonic::SCASW), + _ => { + return Err(DisasmError::IllegalOperand( + "Illegal mnemonic followed REP* instruction".to_string(), + )); + } + }; + Ok(mnemonic) + } + /// Parse a single ModRM byte, calculate the [`ModRmTarget`] (Memory or /// Register) from that byte and advance the offset. /// It is always just a single byte, even for word-width instructions. @@ -861,8 +887,8 @@ impl Disassembler { 0xF0 => Mnemonic::LOCK, 0xF1 => return Err(DisasmError::OpcodeUndefined(opcode)), - 0xF2 => Mnemonic::REPNZ, - 0xF3 => Mnemonic::REPZ, + 0xF2 => Mnemonic::REPNZ(Box::new(self.parse_rep_op()?)), + 0xF3 => Mnemonic::REPZ(Box::new(self.parse_rep_op()?)), 0xF4 => Mnemonic::HLT, diff --git a/src/instructions.rs b/src/instructions.rs index 55041c1..02f08cd 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -45,7 +45,36 @@ impl fmt::Display for Instruction { } } -#[derive(Debug, Clone, PartialEq, Eq, Copy)] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RepeatableStringOperation { + CMPSB(Mnemonic), + CMPSW(Mnemonic), + SCASB(Mnemonic), + SCASW(Mnemonic), + MOVSB(Mnemonic), + MOVSW(Mnemonic), +} + +impl RepeatableStringOperation { + pub fn get(self) -> Mnemonic { + match self { + RepeatableStringOperation::CMPSB(mnemonic) => mnemonic, + RepeatableStringOperation::CMPSW(mnemonic) => mnemonic, + RepeatableStringOperation::SCASB(mnemonic) => mnemonic, + RepeatableStringOperation::SCASW(mnemonic) => mnemonic, + RepeatableStringOperation::MOVSB(mnemonic) => mnemonic, + RepeatableStringOperation::MOVSW(mnemonic) => mnemonic, + } + } +} + +impl fmt::Display for RepeatableStringOperation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "self.0") + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] #[allow(non_camel_case_types)] /// All possible mnemonic variantions. /// These are sorted by type and are not in hex-encoding order. @@ -288,8 +317,8 @@ pub enum Mnemonic { STD, CMC, // Repeat prefix - REPNZ, - REPZ, + REPNZ(Box), + REPZ(Box), // Adjust AAM(Byte), AAD(Byte), @@ -580,8 +609,8 @@ impl fmt::Display for Mnemonic { Self::STD => write!(f, "std"), Self::CMC => write!(f, "cmc"), - Self::REPNZ => write!(f, "repnz"), - Self::REPZ => write!(f, "repz"), + Self::REPNZ(i) => write!(f, "repnz {}", *i), + Self::REPZ(i) => write!(f, "repz {}", *i), Self::AAM(byte) => write!(f, "aam {byte:#04x}"), Self::AAD(byte) => write!(f, "aad {byte:#04x}"), diff --git a/src/interpreter/computer.rs b/src/interpreter/computer.rs index 6d42b8d..e02e7a9 100644 --- a/src/interpreter/computer.rs +++ b/src/interpreter/computer.rs @@ -1,7 +1,8 @@ use core::fmt; -use crate::operands::{ - Byte, ImmediateOperand, ImmediateOperandSigned, MemoryIndex, ModRmTarget, Word, +use crate::{ + instructions::{Mnemonic, RepeatableStringOperation}, + operands::{Byte, ImmediateOperand, ImmediateOperandSigned, MemoryIndex, ModRmTarget, Word}, }; use super::{ @@ -257,7 +258,9 @@ impl Computer { ImmediateOperand::Word(lhsw) => match rhs { ImmediateOperand::Byte(_) => { let [low, _] = lhsw.to_le_bytes(); - log::debug!("lhs was word, so cmp'ing only {low:#04x} - {rhs:#04x}"); + log::debug!( + "lhs was word and rhs was byte, so cmp'ing only {low:#04x} - {rhs:#04x}" + ); return ImmediateOperand::Byte(low) - rhs; } _ => {} @@ -425,12 +428,21 @@ impl Computer { } /// Read into memory, index by a [`MemoryIndex`], accessed at `DS:idx` + /// Accessing at `DS` should be the normal case. /// Always reads a whole word. pub fn read(&self, idx: MemoryIndex) -> Result { let addr = self.mem_addr(idx, &crate::register::SegmentRegister::DS); self.memory.read_raw(addr) } + /// Read into memory, indexed by `ES:DI` + /// Accessing at `ES` is usually only done for string compare operations. + /// Always reads a whole word. + pub fn read_esdi(&self) -> Result { + let addr = self.mem_addr(self.regs.di.into(), &crate::register::SegmentRegister::ES); + self.memory.read_raw(addr) + } + /// Write an [`ImmediateOperand`] into [`Self::memory`] or [`Self::regs`]. pub fn write_modrm( &mut self, @@ -453,6 +465,81 @@ impl Computer { Ok(imm) } + /// A REPZ/REPNZ mnemonic can be followed by a string operation + pub fn repz( + &mut self, + rep_op: RepeatableStringOperation, + z: bool, + ) -> Result<(), InterpreterError> { + while self.regs.cx.read() != 0 { + self.string_ops(rep_op.clone().get())?; + + if self.flags.zf == z { + return Ok(()); + } + + self.regs.cx.write(self.regs.cx.read() - 1); + self.regs.di -= 1; + } + Ok(()) + } + + /// Execute string operations. + pub fn string_ops(&mut self, mnemonic: Mnemonic) -> Result<(), InterpreterError> { + match mnemonic { + Mnemonic::CMPSB => self.cmp( + ModRmTarget::Register(crate::register::Register::AL), + ArithmeticOperand::Immediate(ImmediateOperand::Byte(self.read_esdi()? as Byte)), + ), + Mnemonic::CMPSW => self.cmp( + ModRmTarget::Register(crate::register::Register::AL), + ArithmeticOperand::Immediate(self.read_esdi()?.into()), + ), + Mnemonic::SCASB => self.cmp( + ModRmTarget::Memory(self.regs.si.into()), + ArithmeticOperand::Immediate(ImmediateOperand::Byte(self.read_esdi()? as Byte)), + ), + Mnemonic::SCASW => self.cmp( + ModRmTarget::Memory(self.regs.si.into()), + ArithmeticOperand::Immediate(ImmediateOperand::Byte(self.read_esdi()? as Byte)), + ), + Mnemonic::MOVSB => { + let addr = + self.mem_addr(self.regs.di.into(), &crate::register::SegmentRegister::ES); + let val = self.read(self.regs.si.into())?; + self.memory + .write_raw(addr, ImmediateOperand::Byte(val as u8))?; + + if self.flags.df { + self.regs.si -= 1; + self.regs.di -= 1; + } else { + self.regs.si += 1; + self.regs.di += 1; + } + + Ok(()) + } + Mnemonic::MOVSW => { + let addr = + self.mem_addr(self.regs.di.into(), &crate::register::SegmentRegister::ES); + let val = self.read(self.regs.si.into())?; + self.memory.write_raw(addr, val.into())?; + + if self.flags.df { + self.regs.si -= 2; + self.regs.di -= 2; + } else { + self.regs.si += 2; + self.regs.di += 2; + } + + Ok(()) + } + _ => return Err(InterpreterError::IllegalOperation), + } + } + /// Shift bits of data, pointed to by a [`ModRmTarget`]. /// SHL: Direction::Left, false /// SHR: Direction::Right, false diff --git a/src/interpreter/interpreter.rs b/src/interpreter/interpreter.rs index fe43ed5..21d99ae 100644 --- a/src/interpreter/interpreter.rs +++ b/src/interpreter/interpreter.rs @@ -25,6 +25,7 @@ pub enum InterpreterError { InvalidSyscall(Byte), MemoryOutOfBound(Word), FetchError(String), + IllegalOperation, } impl fmt::Display for InterpreterError { @@ -42,6 +43,7 @@ impl fmt::Display for InterpreterError { InterpreterError::FetchError(s) => { write!(f, "Error during fetch phase: {s}") } + InterpreterError::IllegalOperation => write!(f, "Illegal operation"), } } } @@ -555,7 +557,10 @@ impl Interpreter { Mnemonic::MOV_FromSReg(target, sreg) => self .computer .write_modrm(target, self.computer.sregs.read(sreg).into())?, - Mnemonic::MOV_ToSReg(_, _) => todo!(), + Mnemonic::MOV_ToSReg(target, sreg) => self + .computer + .sregs + .write(sreg, self.computer.read_modrm(target)?), Mnemonic::MOV_Ib(target, val) => self.computer.write_modrm(target, val.into())?, Mnemonic::MOV_Iv(target, val) => self.computer.write_modrm(target, val.into())?, @@ -638,16 +643,17 @@ impl Interpreter { /* * String Byte Operations */ - Mnemonic::MOVSB => todo!(), - Mnemonic::MOVSW => todo!(), - Mnemonic::CMPSB => todo!(), - Mnemonic::CMPSW => todo!(), Mnemonic::STOSB => todo!(), Mnemonic::STOSW => todo!(), Mnemonic::LODSB => todo!(), Mnemonic::LODSW => todo!(), - Mnemonic::SCASB => todo!(), - Mnemonic::SCASW => todo!(), + + Mnemonic::SCASB => self.computer.string_ops(current_instruction.mnemonic)?, + Mnemonic::SCASW => self.computer.string_ops(current_instruction.mnemonic)?, + Mnemonic::CMPSB => self.computer.string_ops(current_instruction.mnemonic)?, + Mnemonic::CMPSW => self.computer.string_ops(current_instruction.mnemonic)?, + Mnemonic::MOVSB => self.computer.string_ops(current_instruction.mnemonic)?, + Mnemonic::MOVSW => self.computer.string_ops(current_instruction.mnemonic)?, /* * RET @@ -881,8 +887,8 @@ impl Interpreter { /* * Repeat prefixes */ - Mnemonic::REPNZ => todo!(), - Mnemonic::REPZ => todo!(), + Mnemonic::REPNZ(rep_op) => self.computer.repz(*rep_op, true)?, + Mnemonic::REPZ(rep_op) => self.computer.repz(*rep_op, false)?, /* * Adjust