ft(interpreter): impl repz/repnz and string ops
This commit is contained in:
@@ -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,
|
||||
|
||||
|
||||
@@ -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}"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user