chore(interpreter): rewrite displacement memoryindex logic

Previously, the displacement for a MemoryIndex was directly interpreted
and saved as a signed value.
Change this to the normal unsigned ImmediateOperand version to allow
for more flexible usage of this struct for general memoryaccess (future
commit) and just interpret the displacement member as signed, only when
being interpreted as such (memory access, display, ....
This commit is contained in:
2025-06-18 17:59:55 +09:00
parent 5a61eb5fd6
commit 79dc560689
5 changed files with 131 additions and 61 deletions

View File

@@ -2,8 +2,8 @@
use crate::aout::Aout; use crate::aout::Aout;
use crate::operands::{ use crate::operands::{
Byte, DWord, Displacement, IByte, IWord, ImmediateOperand, MemoryIndex, ModRmTarget, Pointer16, Byte, DWord, IByte, IWord, ImmediateOperand, MemoryIndex, ModRmTarget, Pointer16, Pointer32,
Pointer32, Word, Word,
}; };
use crate::register::{Register, RegisterId, SegmentRegister}; use crate::register::{Register, RegisterId, SegmentRegister};
use crate::{ use crate::{
@@ -269,7 +269,7 @@ impl Disassembler {
match mode { match mode {
0b00 => { 0b00 => {
if rm == 0b110 { if rm == 0b110 {
let word = Displacement::IWord(self.parse_word()? as IWord); let word = ImmediateOperand::Word(self.parse_word()?);
log::debug!("ModRM direct memory read at {word:?}"); log::debug!("ModRM direct memory read at {word:?}");
displacement = Some(word); displacement = Some(word);
return Ok(( return Ok((
@@ -286,12 +286,12 @@ impl Disassembler {
} }
} }
0b01 => { 0b01 => {
let byte = Displacement::IByte(self.parse_byte()? as IByte); let byte = ImmediateOperand::Byte(self.parse_byte()?);
log::debug!("ModRM has a single byte of displacement: {byte}."); log::debug!("ModRM has a single byte of displacement: {byte}.");
displacement = Some(byte); displacement = Some(byte);
} }
0b10 => { 0b10 => {
let word = Displacement::IWord(self.parse_word()? as IWord); let word = ImmediateOperand::Word(self.parse_word()?);
log::debug!("ModRM has a single word of displacement: {word}"); log::debug!("ModRM has a single word of displacement: {word}");
displacement = Some(word); displacement = Some(word);
} }

View File

@@ -631,6 +631,34 @@ impl Interpreter {
/* /*
* Load ES/DS Register * Load ES/DS Register
*/ */
Mnemonic::LES(reg, ptr) => {
let offset = self
.computer
.memory
.read_raw(self.computer.sregs.ds * 16 + ptr.word)?;
let segment = self
.computer
.memory
.read_raw(self.computer.sregs.ds * 16 + ptr.word + 2)?;
self.computer.sregs.es = segment;
self.computer
.regs
.write(reg, ImmediateOperand::from(offset));
}
Mnemonic::LDS(reg, ptr) => {
let offset = self
.computer
.memory
.read_raw(self.computer.sregs.ds * 16 + ptr.word)?;
let segment = self
.computer
.memory
.read_raw(self.computer.sregs.ds * 16 + ptr.word + 2)?;
self.computer.sregs.ds = segment;
self.computer
.regs
.write(reg, ImmediateOperand::from(offset));
}
/* /*
* Not/Neg * Not/Neg

View File

@@ -1,4 +1,4 @@
use crate::operands::{Byte, Displacement, ImmediateOperand, MemoryIndex, Word}; use crate::operands::{Byte, ImmediateOperand, ImmediateOperandSigned, MemoryIndex, Word};
use super::interpreter::InterpreterError; use super::interpreter::InterpreterError;
use crate::interpreter::register::{Register, SegmentRegister}; use crate::interpreter::register::{Register, SegmentRegister};
@@ -97,7 +97,7 @@ impl Memory {
) -> Word { ) -> Word {
let mut base = ImmediateOperand::Word(0); let mut base = ImmediateOperand::Word(0);
let mut index = ImmediateOperand::Word(0); let mut index = ImmediateOperand::Word(0);
let mut disp = Displacement::IWord(0); let mut disp = ImmediateOperandSigned::Word(0);
if let Some(base_reg) = idx.base { if let Some(base_reg) = idx.base {
base = regs.read(base_reg); base = regs.read(base_reg);
@@ -106,7 +106,7 @@ impl Memory {
index = regs.read(index_reg); index = regs.read(index_reg);
}; };
if let Some(displacement) = idx.displacement { if let Some(displacement) = idx.displacement {
disp = displacement; disp = ImmediateOperandSigned::from(displacement);
} }
Self::addr_offset((base + index + disp).into(), sregs) Self::addr_offset((base + index + disp).into(), sregs)

View File

@@ -165,10 +165,10 @@ gen_regs!(DX);
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct SegmentRegister { pub struct SegmentRegister {
pub ds: Word, pub ds: Word, // data segment
pub es: Word, pub es: Word, // extra segment
pub ss: Word, pub ss: Word, // stack segment
pub cs: Word, pub cs: Word, // code segment
} }
impl SegmentRegister { impl SegmentRegister {

View File

@@ -189,31 +189,6 @@ impl Add for ImmediateOperand {
} }
} }
impl Add<Displacement> for ImmediateOperand {
type Output = ImmediateOperand;
fn add(self, disp: Displacement) -> Self::Output {
// Warning: this gets rid of the sign, which is fine as long as it is
// used for a memory index, which can never be negative.
match disp {
Displacement::IByte(byte) => {
if byte < 0 {
return self - ImmediateOperand::Byte((byte * -1) as Byte);
} else {
return self + ImmediateOperand::Byte(byte as Byte);
}
}
Displacement::IWord(word) => {
if word < 0 {
return self - ImmediateOperand::Word((word * -1) as Word);
} else {
return self + ImmediateOperand::Word(word as Word);
}
}
}
}
}
impl Add<Byte> for ImmediateOperand { impl Add<Byte> for ImmediateOperand {
type Output = ImmediateOperand; type Output = ImmediateOperand;
@@ -475,39 +450,42 @@ impl std::fmt::Display for ModRmTarget {
} }
#[derive(Debug, Clone, PartialEq, Eq, Copy)] #[derive(Debug, Clone, PartialEq, Eq, Copy)]
/// Memory displacements are concrete signed versions of Byte and Word operands. /// Just a wrapper to access QOL function, which interprets an
/// Encodes either Byte- or Word-sized operands. /// [`ImmediateOperand`] as a signed value.
/// Generally, a [`Displacement`] is the result of a ModRM byte parse and pub enum ImmediateOperandSigned {
/// usually really is the signed displacement of a [`MemoryIndex`] for memory Byte(IByte),
/// operations. Word(IWord),
/// Although, some instructions use this parsed displacement as an unsigned
/// pointer, hence the [`Displacement`] will be cast to [`Pointer16`], when this
/// is the case.
pub enum Displacement {
IByte(IByte),
IWord(IWord),
} }
impl fmt::LowerHex for Displacement { impl fmt::LowerHex for ImmediateOperandSigned {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::IByte(b) => fmt::LowerHex::fmt(b, f), Self::Byte(b) => fmt::LowerHex::fmt(b, f),
Self::IWord(v) => fmt::LowerHex::fmt(v, f), Self::Word(v) => fmt::LowerHex::fmt(v, f),
} }
} }
} }
impl std::fmt::Display for Displacement { impl From<ImmediateOperand> for ImmediateOperandSigned {
fn from(value: ImmediateOperand) -> Self {
match value {
ImmediateOperand::Byte(b) => Self::Byte(b as IByte),
ImmediateOperand::Word(w) => Self::Word(w as IWord),
}
}
}
impl std::fmt::Display for ImmediateOperandSigned {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { match self {
Self::IByte(b) => { Self::Byte(b) => {
if *b > 0 { if *b > 0 {
write!(f, " + {:#x}", b) write!(f, " + {:#x}", b)
} else { } else {
write!(f, " - {:#x}", b * -1) write!(f, " - {:#x}", b * -1)
} }
} }
Self::IWord(w) => { Self::Word(w) => {
if *w > 0 { if *w > 0 {
write!(f, " + {:#x}", w) write!(f, " + {:#x}", w)
} else { } else {
@@ -518,13 +496,57 @@ impl std::fmt::Display for Displacement {
} }
} }
impl Add<ImmediateOperandSigned> for ImmediateOperand {
type Output = ImmediateOperand;
fn add(self, disp: ImmediateOperandSigned) -> Self::Output {
// Warning: this gets rid of the sign, which is fine as long as it is
// used for a memory index, which can never be negative.
// In that case, the subtract wraps.
match disp {
ImmediateOperandSigned::Byte(byte) => {
if byte < 0 {
return self - ImmediateOperand::Byte((byte * -1) as Byte);
} else {
return self + ImmediateOperand::Byte(byte as Byte);
}
}
ImmediateOperandSigned::Word(word) => {
if word < 0 {
return self - ImmediateOperand::Word((word * -1) as Word);
} else {
return self + ImmediateOperand::Word(word as Word);
}
}
}
}
}
/// A memory index operand is usually created by ModRM bytes or words. /// A memory index operand is usually created by ModRM bytes or words.
/// e.g. [bx+si] /// e.g. [bx+si]
#[derive(Debug, Clone, PartialEq, Eq, Copy)] #[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct MemoryIndex { pub struct MemoryIndex {
pub base: Option<Register>, pub base: Option<Register>,
pub index: Option<Register>, pub index: Option<Register>,
pub displacement: Option<Displacement>, pub displacement: Option<ImmediateOperand>,
}
impl MemoryIndex {
/// Creates a [`MemoryIndex`] with just a single [`Register`]
pub fn reg(reg: Register) -> Self {
Self {
base: Some(reg),
index: None,
displacement: None,
}
}
/// Creates a [`MemoryIndex`] with an [`ImmediateOperand`]
pub fn disp(disp: ImmediateOperand) -> Self {
Self {
base: None,
index: None,
displacement: Some(disp),
}
}
} }
impl fmt::Display for MemoryIndex { impl fmt::Display for MemoryIndex {
@@ -533,22 +555,42 @@ impl fmt::Display for MemoryIndex {
Some(base) => match &self.index { Some(base) => match &self.index {
Some(index) => match &self.displacement { Some(index) => match &self.displacement {
Some(displacement) => { Some(displacement) => {
write!(f, "[{} + {}{}]", base, index, displacement) write!(
f,
"[{} + {}{}]",
base,
index,
ImmediateOperandSigned::from(displacement.clone().to_owned())
)
} }
None => write!(f, "[{} + {}]", base, index), None => write!(f, "[{} + {}]", base, index),
}, },
None => match &self.displacement { None => match &self.displacement {
Some(displacement) => write!(f, "[{}{}]", base, displacement), Some(displacement) => write!(
f,
"[{}{}]",
base,
ImmediateOperandSigned::from(displacement.clone().to_owned())
),
None => write!(f, "[{}]", base), None => write!(f, "[{}]", base),
}, },
}, },
None => match &self.index { None => match &self.index {
Some(index) => match &self.displacement { Some(index) => match &self.displacement {
Some(displacement) => write!(f, "[{}{}]", index, displacement), Some(displacement) => write!(
f,
"[{}{}]",
index,
ImmediateOperandSigned::from(displacement.clone().to_owned())
),
None => write!(f, "[{}]", index), None => write!(f, "[{}]", index),
}, },
None => match &self.displacement { None => match &self.displacement {
Some(displacement) => write!(f, "[{:#x}]", displacement), Some(displacement) => write!(
f,
"[{:#x}]",
ImmediateOperandSigned::from(displacement.clone().to_owned())
),
None => panic!("Memory Index without base, index and displacement"), None => panic!("Memory Index without base, index and displacement"),
}, },
}, },
@@ -560,7 +602,7 @@ impl fmt::Display for MemoryIndex {
/// 16-bit pointer for access, usually with a [`SegmentRegister`] as segment /// 16-bit pointer for access, usually with a [`SegmentRegister`] as segment
/// and [`Pointer16`] as offset. /// and [`Pointer16`] as offset.
/// Generally, this type only gets constructed in rare scenarios, when the /// Generally, this type only gets constructed in rare scenarios, when the
/// [`Displacement`] of ModRM byte is used as a raw pointer. /// [`Displacement`] of a parsed [`ModRmTarget`] is used as a raw pointer.
pub struct Pointer16 { pub struct Pointer16 {
pub word: Word, pub word: Word,
} }
@@ -578,7 +620,7 @@ impl TryFrom<ModRmTarget> for Pointer16 {
match target { match target {
ModRmTarget::Memory(mem) => match mem.displacement { ModRmTarget::Memory(mem) => match mem.displacement {
Some(disp) => match disp { Some(disp) => match disp {
Displacement::IWord(word) => Ok(Pointer16 { word: word as Word }), ImmediateOperand::Word(word) => Ok(Pointer16 { word }),
_ => { _ => {
return Err(DisasmError::IllegalOperand( return Err(DisasmError::IllegalOperand(
"Tried to construct Pointer16 with Byte, when a Word is expected" "Tried to construct Pointer16 with Byte, when a Word is expected"