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:
@@ -2,8 +2,8 @@
|
||||
|
||||
use crate::aout::Aout;
|
||||
use crate::operands::{
|
||||
Byte, DWord, Displacement, IByte, IWord, ImmediateOperand, MemoryIndex, ModRmTarget, Pointer16,
|
||||
Pointer32, Word,
|
||||
Byte, DWord, IByte, IWord, ImmediateOperand, MemoryIndex, ModRmTarget, Pointer16, Pointer32,
|
||||
Word,
|
||||
};
|
||||
use crate::register::{Register, RegisterId, SegmentRegister};
|
||||
use crate::{
|
||||
@@ -269,7 +269,7 @@ impl Disassembler {
|
||||
match mode {
|
||||
0b00 => {
|
||||
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:?}");
|
||||
displacement = Some(word);
|
||||
return Ok((
|
||||
@@ -286,12 +286,12 @@ impl Disassembler {
|
||||
}
|
||||
}
|
||||
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}.");
|
||||
displacement = Some(byte);
|
||||
}
|
||||
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}");
|
||||
displacement = Some(word);
|
||||
}
|
||||
|
||||
@@ -631,6 +631,34 @@ impl Interpreter {
|
||||
/*
|
||||
* 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
|
||||
|
||||
@@ -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 crate::interpreter::register::{Register, SegmentRegister};
|
||||
@@ -97,7 +97,7 @@ impl Memory {
|
||||
) -> Word {
|
||||
let mut base = 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 {
|
||||
base = regs.read(base_reg);
|
||||
@@ -106,7 +106,7 @@ impl Memory {
|
||||
index = regs.read(index_reg);
|
||||
};
|
||||
if let Some(displacement) = idx.displacement {
|
||||
disp = displacement;
|
||||
disp = ImmediateOperandSigned::from(displacement);
|
||||
}
|
||||
|
||||
Self::addr_offset((base + index + disp).into(), sregs)
|
||||
|
||||
@@ -165,10 +165,10 @@ gen_regs!(DX);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SegmentRegister {
|
||||
pub ds: Word,
|
||||
pub es: Word,
|
||||
pub ss: Word,
|
||||
pub cs: Word,
|
||||
pub ds: Word, // data segment
|
||||
pub es: Word, // extra segment
|
||||
pub ss: Word, // stack segment
|
||||
pub cs: Word, // code segment
|
||||
}
|
||||
|
||||
impl SegmentRegister {
|
||||
|
||||
140
src/operands.rs
140
src/operands.rs
@@ -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 {
|
||||
type Output = ImmediateOperand;
|
||||
|
||||
@@ -475,39 +450,42 @@ impl std::fmt::Display for ModRmTarget {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
/// Memory displacements are concrete signed versions of Byte and Word operands.
|
||||
/// Encodes either Byte- or Word-sized operands.
|
||||
/// Generally, a [`Displacement`] is the result of a ModRM byte parse and
|
||||
/// usually really is the signed displacement of a [`MemoryIndex`] for memory
|
||||
/// operations.
|
||||
/// 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),
|
||||
/// Just a wrapper to access QOL function, which interprets an
|
||||
/// [`ImmediateOperand`] as a signed value.
|
||||
pub enum ImmediateOperandSigned {
|
||||
Byte(IByte),
|
||||
Word(IWord),
|
||||
}
|
||||
|
||||
impl fmt::LowerHex for Displacement {
|
||||
impl fmt::LowerHex for ImmediateOperandSigned {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::IByte(b) => fmt::LowerHex::fmt(b, f),
|
||||
Self::IWord(v) => fmt::LowerHex::fmt(v, f),
|
||||
Self::Byte(b) => fmt::LowerHex::fmt(b, 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 {
|
||||
match self {
|
||||
Self::IByte(b) => {
|
||||
Self::Byte(b) => {
|
||||
if *b > 0 {
|
||||
write!(f, " + {:#x}", b)
|
||||
} else {
|
||||
write!(f, " - {:#x}", b * -1)
|
||||
}
|
||||
}
|
||||
Self::IWord(w) => {
|
||||
Self::Word(w) => {
|
||||
if *w > 0 {
|
||||
write!(f, " + {:#x}", w)
|
||||
} 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.
|
||||
/// e.g. [bx+si]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub struct MemoryIndex {
|
||||
pub base: 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 {
|
||||
@@ -533,22 +555,42 @@ impl fmt::Display for MemoryIndex {
|
||||
Some(base) => match &self.index {
|
||||
Some(index) => match &self.displacement {
|
||||
Some(displacement) => {
|
||||
write!(f, "[{} + {}{}]", base, index, displacement)
|
||||
write!(
|
||||
f,
|
||||
"[{} + {}{}]",
|
||||
base,
|
||||
index,
|
||||
ImmediateOperandSigned::from(displacement.clone().to_owned())
|
||||
)
|
||||
}
|
||||
None => write!(f, "[{} + {}]", base, index),
|
||||
},
|
||||
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 => match &self.index {
|
||||
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 => 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"),
|
||||
},
|
||||
},
|
||||
@@ -560,7 +602,7 @@ impl fmt::Display for MemoryIndex {
|
||||
/// 16-bit pointer for access, usually with a [`SegmentRegister`] as segment
|
||||
/// and [`Pointer16`] as offset.
|
||||
/// 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 word: Word,
|
||||
}
|
||||
@@ -578,7 +620,7 @@ impl TryFrom<ModRmTarget> for Pointer16 {
|
||||
match target {
|
||||
ModRmTarget::Memory(mem) => match mem.displacement {
|
||||
Some(disp) => match disp {
|
||||
Displacement::IWord(word) => Ok(Pointer16 { word: word as Word }),
|
||||
ImmediateOperand::Word(word) => Ok(Pointer16 { word }),
|
||||
_ => {
|
||||
return Err(DisasmError::IllegalOperand(
|
||||
"Tried to construct Pointer16 with Byte, when a Word is expected"
|
||||
|
||||
Reference in New Issue
Block a user