Actually implement disassembler (#1)
This commit is contained in:
115
src/main.rs
115
src/main.rs
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user