ft(interpreter): impl repz/repnz and string ops

This commit is contained in:
2025-07-15 12:44:28 +09:00
parent d5d6834a94
commit 75e9df9be9
4 changed files with 167 additions and 19 deletions

View File

@@ -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<RepeatableStringOperation, DisasmError> {
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,

View File

@@ -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<RepeatableStringOperation>),
REPZ(Box<RepeatableStringOperation>),
// 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}"),

View File

@@ -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<Word, InterpreterError> {
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<Word, InterpreterError> {
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

View File

@@ -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