ft(interpreter): impl repz/repnz and string ops
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
//! The main dissembling logic.
|
//! The main dissembling logic.
|
||||||
|
|
||||||
use crate::aout::Aout;
|
use crate::aout::Aout;
|
||||||
|
use crate::instructions::RepeatableStringOperation;
|
||||||
use crate::operands::{
|
use crate::operands::{
|
||||||
Byte, DWord, IByte, IWord, ImmediateOperand, MemoryIndex, ModRmTarget, Pointer16, Pointer32,
|
Byte, DWord, IByte, IWord, ImmediateOperand, MemoryIndex, ModRmTarget, Pointer16, Pointer32,
|
||||||
Word,
|
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
|
/// Parse a single ModRM byte, calculate the [`ModRmTarget`] (Memory or
|
||||||
/// Register) from that byte and advance the offset.
|
/// Register) from that byte and advance the offset.
|
||||||
/// It is always just a single byte, even for word-width instructions.
|
/// It is always just a single byte, even for word-width instructions.
|
||||||
@@ -861,8 +887,8 @@ impl Disassembler {
|
|||||||
0xF0 => Mnemonic::LOCK,
|
0xF0 => Mnemonic::LOCK,
|
||||||
0xF1 => return Err(DisasmError::OpcodeUndefined(opcode)),
|
0xF1 => return Err(DisasmError::OpcodeUndefined(opcode)),
|
||||||
|
|
||||||
0xF2 => Mnemonic::REPNZ,
|
0xF2 => Mnemonic::REPNZ(Box::new(self.parse_rep_op()?)),
|
||||||
0xF3 => Mnemonic::REPZ,
|
0xF3 => Mnemonic::REPZ(Box::new(self.parse_rep_op()?)),
|
||||||
|
|
||||||
0xF4 => Mnemonic::HLT,
|
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)]
|
#[allow(non_camel_case_types)]
|
||||||
/// All possible mnemonic variantions.
|
/// All possible mnemonic variantions.
|
||||||
/// These are sorted by type and are not in hex-encoding order.
|
/// These are sorted by type and are not in hex-encoding order.
|
||||||
@@ -288,8 +317,8 @@ pub enum Mnemonic {
|
|||||||
STD,
|
STD,
|
||||||
CMC,
|
CMC,
|
||||||
// Repeat prefix
|
// Repeat prefix
|
||||||
REPNZ,
|
REPNZ(Box<RepeatableStringOperation>),
|
||||||
REPZ,
|
REPZ(Box<RepeatableStringOperation>),
|
||||||
// Adjust
|
// Adjust
|
||||||
AAM(Byte),
|
AAM(Byte),
|
||||||
AAD(Byte),
|
AAD(Byte),
|
||||||
@@ -580,8 +609,8 @@ impl fmt::Display for Mnemonic {
|
|||||||
Self::STD => write!(f, "std"),
|
Self::STD => write!(f, "std"),
|
||||||
Self::CMC => write!(f, "cmc"),
|
Self::CMC => write!(f, "cmc"),
|
||||||
|
|
||||||
Self::REPNZ => write!(f, "repnz"),
|
Self::REPNZ(i) => write!(f, "repnz {}", *i),
|
||||||
Self::REPZ => write!(f, "repz"),
|
Self::REPZ(i) => write!(f, "repz {}", *i),
|
||||||
|
|
||||||
Self::AAM(byte) => write!(f, "aam {byte:#04x}"),
|
Self::AAM(byte) => write!(f, "aam {byte:#04x}"),
|
||||||
Self::AAD(byte) => write!(f, "aad {byte:#04x}"),
|
Self::AAD(byte) => write!(f, "aad {byte:#04x}"),
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
use crate::operands::{
|
use crate::{
|
||||||
Byte, ImmediateOperand, ImmediateOperandSigned, MemoryIndex, ModRmTarget, Word,
|
instructions::{Mnemonic, RepeatableStringOperation},
|
||||||
|
operands::{Byte, ImmediateOperand, ImmediateOperandSigned, MemoryIndex, ModRmTarget, Word},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@@ -257,7 +258,9 @@ impl Computer {
|
|||||||
ImmediateOperand::Word(lhsw) => match rhs {
|
ImmediateOperand::Word(lhsw) => match rhs {
|
||||||
ImmediateOperand::Byte(_) => {
|
ImmediateOperand::Byte(_) => {
|
||||||
let [low, _] = lhsw.to_le_bytes();
|
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;
|
return ImmediateOperand::Byte(low) - rhs;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -425,12 +428,21 @@ impl Computer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read into memory, index by a [`MemoryIndex`], accessed at `DS:idx`
|
/// Read into memory, index by a [`MemoryIndex`], accessed at `DS:idx`
|
||||||
|
/// Accessing at `DS` should be the normal case.
|
||||||
/// Always reads a whole word.
|
/// Always reads a whole word.
|
||||||
pub fn read(&self, idx: MemoryIndex) -> Result<Word, InterpreterError> {
|
pub fn read(&self, idx: MemoryIndex) -> Result<Word, InterpreterError> {
|
||||||
let addr = self.mem_addr(idx, &crate::register::SegmentRegister::DS);
|
let addr = self.mem_addr(idx, &crate::register::SegmentRegister::DS);
|
||||||
self.memory.read_raw(addr)
|
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`].
|
/// Write an [`ImmediateOperand`] into [`Self::memory`] or [`Self::regs`].
|
||||||
pub fn write_modrm(
|
pub fn write_modrm(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -453,6 +465,81 @@ impl Computer {
|
|||||||
Ok(imm)
|
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`].
|
/// Shift bits of data, pointed to by a [`ModRmTarget`].
|
||||||
/// SHL: Direction::Left, false
|
/// SHL: Direction::Left, false
|
||||||
/// SHR: Direction::Right, false
|
/// SHR: Direction::Right, false
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ pub enum InterpreterError {
|
|||||||
InvalidSyscall(Byte),
|
InvalidSyscall(Byte),
|
||||||
MemoryOutOfBound(Word),
|
MemoryOutOfBound(Word),
|
||||||
FetchError(String),
|
FetchError(String),
|
||||||
|
IllegalOperation,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for InterpreterError {
|
impl fmt::Display for InterpreterError {
|
||||||
@@ -42,6 +43,7 @@ impl fmt::Display for InterpreterError {
|
|||||||
InterpreterError::FetchError(s) => {
|
InterpreterError::FetchError(s) => {
|
||||||
write!(f, "Error during fetch phase: {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
|
Mnemonic::MOV_FromSReg(target, sreg) => self
|
||||||
.computer
|
.computer
|
||||||
.write_modrm(target, self.computer.sregs.read(sreg).into())?,
|
.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_Ib(target, val) => self.computer.write_modrm(target, val.into())?,
|
||||||
Mnemonic::MOV_Iv(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
|
* String Byte Operations
|
||||||
*/
|
*/
|
||||||
Mnemonic::MOVSB => todo!(),
|
|
||||||
Mnemonic::MOVSW => todo!(),
|
|
||||||
Mnemonic::CMPSB => todo!(),
|
|
||||||
Mnemonic::CMPSW => todo!(),
|
|
||||||
Mnemonic::STOSB => todo!(),
|
Mnemonic::STOSB => todo!(),
|
||||||
Mnemonic::STOSW => todo!(),
|
Mnemonic::STOSW => todo!(),
|
||||||
Mnemonic::LODSB => todo!(),
|
Mnemonic::LODSB => todo!(),
|
||||||
Mnemonic::LODSW => 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
|
* RET
|
||||||
@@ -881,8 +887,8 @@ impl Interpreter {
|
|||||||
/*
|
/*
|
||||||
* Repeat prefixes
|
* Repeat prefixes
|
||||||
*/
|
*/
|
||||||
Mnemonic::REPNZ => todo!(),
|
Mnemonic::REPNZ(rep_op) => self.computer.repz(*rep_op, true)?,
|
||||||
Mnemonic::REPZ => todo!(),
|
Mnemonic::REPZ(rep_op) => self.computer.repz(*rep_op, false)?,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adjust
|
* Adjust
|
||||||
|
|||||||
Reference in New Issue
Block a user