Actually implement disassembler (#1)

This commit is contained in:
Patrick Stevens
2023-04-09 09:13:43 +01:00
committed by GitHub
parent 62126f5ba4
commit c1a525d0ac
2 changed files with 156 additions and 1 deletions

View File

@@ -108,6 +108,48 @@ impl Instruction {
}
}
}
fn consume<I>(bytes: &mut I) -> Option<Instruction>
where
I: Iterator<Item = u8>,
{
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<T>
@@ -118,6 +160,30 @@ where
instructions: T,
}
impl<T> PartialEq for Program<T>
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<T> Eq for Program<T> where T: AsRef<[Instruction]> {}
impl<T> Display for Program<T>
where
T: AsRef<[Instruction]>,
@@ -147,6 +213,24 @@ where
}
}
impl Program<Vec<Instruction>> {
fn of_bytes<I>(mut bytes: I) -> Program<Vec<Instruction>>
where
I: Iterator<Item = u8>,
{
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<P>(path: P) -> Vec<u8>
where
P: AsRef<Path>,
@@ -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
);
}
}
}

View File

@@ -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),
)
}
}
}