diff --git a/src/main.rs b/src/main.rs index 6650c5a..b859ac2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -108,6 +108,48 @@ impl Instruction { } } } + + fn consume(bytes: &mut I) -> Option + where + I: Iterator, + { + if let Some(b) = bytes.next() { + if (b & 0b11111100u8) == 0b10001000u8 { + let d = b % 2; + let is_wide = (b / 2) % 2 == 1; + if let Some(mod_reg_rm) = bytes.next() { + let mode = (mod_reg_rm & 0b11000000) / 64; + let reg = (mod_reg_rm & 0b00111000) / 8; + let rm = mod_reg_rm & 0b00000111; + + if mode == 3 { + let rm = Register::of_id(rm, is_wide); + let reg = Register::of_id(reg, is_wide); + let instruction = if d == 0 { + MoveInstruction { + source: reg, + dest: rm, + } + } else { + MoveInstruction { + source: rm, + dest: reg, + } + }; + Some(Instruction::Move(instruction)) + } else { + panic!("Unimplemented mode {} for instruction {}, we only know register-register mov", mode, b) + } + } else { + panic!("mov required a second byte") + } + } else { + panic!("Unrecognised instruction byte: {}", b) + } + } else { + None + } + } } pub struct Program @@ -118,6 +160,30 @@ where instructions: T, } +impl PartialEq for Program +where + T: AsRef<[Instruction]>, +{ + fn eq(&self, other: &Self) -> bool { + if self.bits != other.bits { + return false; + }; + let iter1 = self.instructions.as_ref(); + let iter2 = self.instructions.as_ref(); + if iter1.len() != iter2.len() { + return false; + } + + if !iter1.iter().zip(iter2.iter()).all(|(x, y)| x == y) { + return false; + } + + true + } +} + +impl Eq for Program where T: AsRef<[Instruction]> {} + impl Display for Program where T: AsRef<[Instruction]>, @@ -147,6 +213,24 @@ where } } +impl Program> { + fn of_bytes(mut bytes: I) -> Program> + where + I: Iterator, + { + let mut output = Vec::new(); + + while let Some(i) = Instruction::consume(&mut bytes) { + output.push(i); + } + + Program { + bits: 16, + instructions: output, + } + } +} + fn load_machine_code

(path: P) -> Vec where P: AsRef, @@ -185,14 +269,23 @@ fn main() { ); std::process::exit(1) } + + let disassembled = Program::of_bytes(expected_bytecode.iter().cloned()); + + if disassembled != compiled { + println!("Program failed to disassemble back to the compiled version. Compiled:\n{}\nDisassembled again:\n{}", compiled, disassembled); + std::process::exit(3) + } } #[cfg(test)] mod test_program { + use crate::Program; + use super::assembly::program; #[test] - fn test_parser() { + fn test_register_register_mov_parser() { let input_asm = include_str!( "../computer_enhance/perfaware/part1/listing_0037_single_register_mov.asm" ); @@ -215,4 +308,24 @@ mod test_program { } } } + + #[test] + fn test_register_register_mov_disassembler() { + let bytecode = + include_bytes!("../computer_enhance/perfaware/part1/listing_0037_single_register_mov"); + let asm = include_str!( + "../computer_enhance/perfaware/part1/listing_0037_single_register_mov.asm" + ); + let (remaining, pre_compiled) = program(&asm).unwrap(); + assert_eq!(remaining, ""); + + let disassembled = Program::of_bytes(bytecode.iter().cloned()); + + if disassembled != pre_compiled { + panic!( + "Failed assertion. Our disassembly:\n{}\nReference:\n{}", + disassembled, pre_compiled + ); + } + } } diff --git a/src/register.rs b/src/register.rs index 1d56bbe..ded0d3e 100644 --- a/src/register.rs +++ b/src/register.rs @@ -17,6 +17,16 @@ impl GeneralRegister { GeneralRegister::D => 0b10, } } + + pub fn of_id(id: u8) -> GeneralRegister { + match id { + 0 => GeneralRegister::A, + 1 => GeneralRegister::C, + 2 => GeneralRegister::D, + 3 => GeneralRegister::B, + _ => panic!("Not a register: {}", id), + } + } } impl Display for GeneralRegister { @@ -48,6 +58,16 @@ impl SpecialRegister { SpecialRegister::DestIndex => 3, } } + + pub fn of_id(id: u8) -> SpecialRegister { + match id { + 4 => SpecialRegister::StackPointer, + 5 => SpecialRegister::BasePointer, + 6 => SpecialRegister::SourceIndex, + 7 => SpecialRegister::DestIndex, + _ => panic!("Not a special register ID: {}", id), + } + } } impl Display for SpecialRegister { @@ -97,3 +117,25 @@ impl Display for Register { } } } + +impl Register { + pub fn of_id(id: u8, is_wide: bool) -> Register { + if is_wide { + if id >= 4 { + Register::Special(SpecialRegister::of_id(id)) + } else { + Register::General(GeneralRegister::of_id(id), RegisterSubset::All) + } + } else { + let subset = if id >= 4 { + ByteRegisterSubset::High + } else { + ByteRegisterSubset::Low + }; + Register::General( + GeneralRegister::of_id(id % 4), + RegisterSubset::Subset(subset), + ) + } + } +}