Files
8086-rs/src/instructions.rs

245 lines
6.4 KiB
Rust

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<u8>, // 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<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 => 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),
}
}
}