//! 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 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, pub index: Option, pub displacement: Option, } 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 for Pointer16 { type Error = DisasmError; fn try_from(target: ModRmTarget) -> Result { 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) } }