use core::fmt; // b: 8, w: 16, v: 16 -> i just treat v and w the same, if nothing blows up #[allow(non_camel_case_types)] pub type b = u8; #[allow(non_camel_case_types)] pub type w = u16; #[derive(Debug)] #[allow(dead_code)] /// A single 'line' of executable ASM is called an Instruction, which /// contains the `Opcode` that will be executed, alongside its starting offset /// and the raw parsed bytes pub struct Instruction { pub start: usize, // location of the instruction start pub raw: Vec, // raw value of instruction pub opcode: Opcode, // actual instruction } impl Instruction { pub fn new() -> Self { Instruction { start: 0, raw: Vec::new(), opcode: Opcode::NOP(), } } } impl fmt::Display for Instruction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:04x}: ", self.start).unwrap(); for b in self.raw.iter() { write!(f, "{:02x}", b).unwrap(); } write!(f, "\t{}", self.opcode) } } #[derive(Debug)] #[allow(dead_code, non_camel_case_types)] pub enum Opcode { NOP(), // ADD ADD_EbGb(MemoryIndex, Register), // MOV MOV_BXIv(Register, ImmediateWord), // INT INT(ImmediateByte), } impl fmt::Display for Opcode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::INT(byte) => write!(f, "INT, {:x}", byte), Self::ADD_EbGb(mem, reg) => write!(f, "ADD {}, {}", mem, reg), Self::MOV_BXIv(reg, word) => write!(f, "MOV {}, {:04x}", reg, word), _ => write!(f, "display not yet implemented"), } } } /// Registers of a 8086 processor #[derive(Debug)] #[allow(dead_code)] pub enum Register { AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP, } #[allow(dead_code)] impl Register { /// Find the register corresponding to the 8086 bytecode ID pub fn by_id(id: u8) -> Self { match id { 0x00 => Self::AL, 0x01 => Self::CL, 0x02 => Self::DL, 0x03 => Self::BL, 0x04 => Self::AH, 0x05 => Self::CH, 0x06 => Self::DH, 0x07 => Self::BH, 0x10 => Self::AX, 0x11 => Self::CX, 0x12 => Self::DX, 0x13 => Self::BX, 0x14 => Self::SP, 0x15 => Self::BP, 0x16 => Self::SI, 0x17 => Self::DI, _ => panic!("Invalid register ID encountered"), } } } impl fmt::Display for Register { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::AX => write!(f, "AX"), Self::BX => write!(f, "BX"), Self::CX => write!(f, "CX"), Self::DX => write!(f, "DX"), Self::AH => write!(f, "AH"), Self::AL => write!(f, "AL"), Self::BL => write!(f, "BL"), Self::BH => write!(f, "BH"), Self::CH => write!(f, "CH"), Self::CL => write!(f, "CL"), Self::DH => write!(f, "DH"), Self::DL => write!(f, "DL"), Self::DI => write!(f, "DI"), Self::SI => write!(f, "SI"), Self::BP => write!(f, "BP"), Self::SP => write!(f, "SP"), } } } /// Segment Registers of a 8086 processor #[derive(Debug)] #[allow(dead_code)] pub enum SegmentRegister { DS, ES, SS, CS, } #[allow(dead_code)] impl SegmentRegister { /// Find the SRegister corresponding to the 8086 bytecode ID pub fn by_id(id: u8) -> Self { match id { 0x30 => Self::ES, 0x31 => Self::CS, 0x32 => Self::SS, 0x33 => Self::DS, _ => panic!("Invalid segment register ID encountered"), } } } impl fmt::Display for SegmentRegister { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::DS => write!(f, "DS"), Self::ES => write!(f, "ES"), Self::SS => write!(f, "SS"), Self::CS => write!(f, "CS"), } } } /// An immediate byte value for an instruction. #[derive(Debug)] pub struct ImmediateByte(pub b); /// An immediate word value for an instruction #[derive(Debug)] pub struct ImmediateWord(pub w); macro_rules! impl_display_and_lowerhex { ($name:ident) => { impl std::fmt::Display for $name { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.0) } } impl std::fmt::LowerHex for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::LowerHex::fmt(&self.0, f) } } }; } impl_display_and_lowerhex!(ImmediateByte); impl_display_and_lowerhex!(ImmediateWord); /// A memory index operand is usually created by ModRM bytes or words. /// e.g. [bx+si] #[derive(Debug)] 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 => panic!("Invalid MemoryIndex encountered"), }, } } } #[derive(Debug)] #[allow(dead_code)] /// Displacement for ModRM pub enum Displacement { Byte(u8), Word(u16), } impl fmt::Display for Displacement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::Byte(byte) => write!(f, "{}", byte), Self::Word(word) => write!(f, "{}", word), } } }