Files
8086-rs/src/operands.rs

304 lines
10 KiB
Rust

//! All types which a Mnemonic can have as some kind of operand.
//! This includes things such as immediates, ModRM byte targets, etc. etc.
// used in doc, but not code
#[allow(unused_imports)]
use crate::register::SegmentRegister;
use crate::{disasm::DisasmError, register::Register};
use core::fmt;
use std::ops::{Add, Div, Mul, Sub};
pub type Byte = u8; // b
pub type IByte = i8; // used for displacements of memory access
pub type Word = u16; // w or v
pub type IWord = i16; // used for displacement of memory access
pub type DWord = u32;
#[derive(Debug, Clone, Ord, Eq, PartialEq, PartialOrd, Copy)]
/// Encodes either Byte- or Word-sized operands.
/// Also sometimes used to decide if an instruction is Byte- or Word-sized,
/// which is usually indicated by using a value of 0 and the disregarding
/// the value when read.
pub enum ImmediateOperand {
Byte(Byte),
Word(Word),
}
impl Add for ImmediateOperand {
type Output = Self;
fn add(self, other: Self) -> Self {
match self {
ImmediateOperand::Byte(lhsb) => match other {
ImmediateOperand::Byte(rhsb) => ImmediateOperand::Byte(lhsb.wrapping_add(rhsb)),
_ => panic!("Cannot add Word to Byte"),
},
ImmediateOperand::Word(lhsw) => match other {
ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_add(rhsw)),
ImmediateOperand::Byte(rhsb) => {
ImmediateOperand::Word(lhsw.wrapping_add(rhsb as Word))
}
},
}
}
}
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 Sub for ImmediateOperand {
type Output = Self;
fn sub(self, other: Self) -> Self {
match self {
ImmediateOperand::Byte(lhsb) => match other {
ImmediateOperand::Byte(rhsb) => ImmediateOperand::Byte(lhsb.wrapping_sub(rhsb)),
_ => panic!("Cannot substract Word from Byte"),
},
ImmediateOperand::Word(lhsw) => match other {
ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_sub(rhsw)),
ImmediateOperand::Byte(rhsb) => {
ImmediateOperand::Word(lhsw.wrapping_sub(rhsb as Word))
}
},
}
}
}
impl Mul for ImmediateOperand {
type Output = Self;
fn mul(self, other: Self) -> Self {
match self {
ImmediateOperand::Byte(lhsb) => match other {
ImmediateOperand::Byte(rhsb) => ImmediateOperand::Byte(lhsb.wrapping_mul(rhsb)),
_ => panic!("Cannot multiply Byte with Word"),
},
ImmediateOperand::Word(lhsw) => match other {
ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_mul(rhsw)),
ImmediateOperand::Byte(rhsb) => {
ImmediateOperand::Word(lhsw.wrapping_mul(rhsb as Word))
}
},
}
}
}
impl Div for ImmediateOperand {
type Output = Self;
fn div(self, other: Self) -> Self {
match self {
ImmediateOperand::Byte(lhsb) => match other {
ImmediateOperand::Byte(rhsb) => ImmediateOperand::Byte(lhsb.wrapping_div(rhsb)),
_ => panic!("Cannot divide Byte with Word"),
},
ImmediateOperand::Word(lhsw) => match other {
ImmediateOperand::Word(rhsw) => ImmediateOperand::Word(lhsw.wrapping_div(rhsw)),
ImmediateOperand::Byte(rhsb) => {
ImmediateOperand::Word(lhsw.wrapping_div(rhsb as Word))
}
},
}
}
}
impl fmt::Display for ImmediateOperand {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Byte(byte) => write!(f, "{}", byte),
Self::Word(word) => write!(f, "{}", word),
}
}
}
impl fmt::LowerHex for ImmediateOperand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Byte(b) => fmt::LowerHex::fmt(b, f),
Self::Word(v) => fmt::LowerHex::fmt(v, f),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
/// ModRM byte can either target a [`MemoryIndex`] (location in memory) or some
/// [`Register`].
pub enum ModRmTarget {
Memory(MemoryIndex),
Register(Register),
}
impl std::fmt::Display for ModRmTarget {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Memory(idx) => write!(f, "{}", idx),
Self::Register(reg) => write!(f, "{}", reg),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
/// Memory displacements are 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),
}
impl fmt::LowerHex for Displacement {
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),
}
}
}
impl std::fmt::Display for Displacement {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::IByte(b) => {
if *b > 0 {
write!(f, " + {:#x}", b)
} else {
write!(f, " - {:#x}", b * -1)
}
}
Self::IWord(w) => {
if *w > 0 {
write!(f, " + {:#x}", w)
} else {
write!(f, " - {:#x}", w * -1)
}
}
}
}
}
/// 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>,
}
impl fmt::Display for MemoryIndex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.base {
Some(base) => match &self.index {
Some(index) => match &self.displacement {
Some(displacement) => {
write!(f, "[{} + {}{}]", base, index, displacement)
}
None => write!(f, "[{} + {}]", base, index),
},
None => match &self.displacement {
Some(displacement) => write!(f, "[{}{}]", base, displacement),
None => write!(f, "[{}]", base),
},
},
None => match &self.index {
Some(index) => match &self.displacement {
Some(displacement) => write!(f, "[{}{}]", index, displacement),
None => write!(f, "[{}]", index),
},
None => match &self.displacement {
Some(displacement) => write!(f, "[{:#x}]", displacement),
None => panic!("Memory Index without base, index and displacement"),
},
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
/// 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.
pub struct Pointer16 {
pub word: Word,
}
impl std::fmt::Display for Pointer16 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "ptr [{:#04x}]", self.word)
}
}
impl TryFrom<ModRmTarget> for Pointer16 {
type Error = DisasmError;
fn try_from(target: ModRmTarget) -> Result<Self, Self::Error> {
match target {
ModRmTarget::Memory(mem) => match mem.displacement {
Some(disp) => match disp {
Displacement::IWord(word) => Ok(Pointer16 { word: word as Word }),
_ => {
return Err(DisasmError::IllegalOperand(
"Tried to construct Pointer16 with Byte, when a Word is expected"
.into(),
));
}
},
_ => {
return Err(DisasmError::IllegalOperand("Tried to construct Pointer16 with Register, when a Displacement was expected".into()));
}
},
_ => {
return Err(DisasmError::IllegalOperand(
"Tried to construct Pointer16 with Register, when a MemoryIndex expected"
.into(),
));
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
/// 32-bit segment:offset pointer for long jumps.
/// Both [`Word`]s are immediately encoded after the instruction
pub struct Pointer32 {
pub raw: DWord,
pub segment: Word,
pub offset: Word,
}
impl std::fmt::Display for Pointer32 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:#04x}:{:#04x}", self.segment, self.offset)
}
}