Krolyxon
Tag v0.2.0

Branches

Tags

Refs →

bench51cratesbench51-coresrcsimdisasm.rs

91 lines · 3.0 KB · Rust

1use crate::isa::{self, Operand};
2use crate::sfr::{format_bit, format_direct, hex8, hex16};
3
4pub struct Disasm {
5 pub text: String,
6 pub size: u8,
7}
8
9/// Disassembles the instruction at `addr`.
10pub fn disassemble(code: &[u8; 0x10000], addr: u16) -> Disasm {
11 let byte = |i: u16| code[addr.wrapping_add(i) as usize];
12 let op = byte(0);
13 let Some(info) = isa::info(op) else {
14 return Disasm {
15 text: format!("DB {}", hex8(op)),
16 size: 1,
17 };
18 };
19 let next = addr.wrapping_add(info.size as u16);
20 let mut pos = 1u16;
21 let mut parts: Vec<String> = Vec::new();
22 for &o in info.operands {
23 let s = match o {
24 Operand::A => "A".into(),
25 Operand::AB => "AB".into(),
26 Operand::C => "C".into(),
27 Operand::Dptr => "DPTR".into(),
28 Operand::Reg(n) => format!("R{n}"),
29 Operand::AtReg(n) => format!("@R{n}"),
30 Operand::AtDptr => "@DPTR".into(),
31 Operand::AtAPlusDptr => "@A+DPTR".into(),
32 Operand::AtAPlusPc => "@A+PC".into(),
33 Operand::Imm8 => format!("#{}", hex8(byte(pos))),
34 Operand::Imm16 => format!("#{}", hex16(u16::from_be_bytes([byte(pos), byte(pos + 1)]))),
35 Operand::Direct => format_direct(byte(pos)),
36 Operand::Bit => format_bit(byte(pos)),
37 Operand::NotBit => format!("/{}", format_bit(byte(pos))),
38 Operand::Addr11 => {
39 hex16((next & 0xF800) | (((op >> 5) as u16) << 8) | byte(pos) as u16)
40 }
41 Operand::Addr16 => hex16(u16::from_be_bytes([byte(pos), byte(pos + 1)])),
42 Operand::Rel => hex16(next.wrapping_add(byte(pos) as i8 as u16)),
43 };
44 pos += o.len() as u16;
45 parts.push(s);
46 }
47 if op == 0x85 {
48 // Encoded as source, destination.
49 parts.swap(0, 1);
50 }
51 let text = if parts.is_empty() {
52 info.mnemonic.to_string()
53 } else {
54 format!("{:<5} {}", info.mnemonic, parts.join(","))
55 };
56 Disasm {
57 text,
58 size: info.size,
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 use crate::asm::assemble;
66
67 /// Disassembling and re-assembling every opcode gives the same bytes.
68 #[test]
69 fn round_trips_every_opcode() {
70 for op in 0..=255u8 {
71 if op == 0xA5 {
72 continue;
73 }
74 let mut code = Box::new([0u8; 0x10000]);
75 code[0x100] = op;
76 code[0x101] = 0x30;
77 code[0x102] = 0x40;
78 let d = disassemble(&code, 0x100);
79 let src = format!("ORG 100h\n{}", d.text);
80 let out = assemble(&src);
81 assert!(out.ok(), "{op:02X} '{}': {:?}", d.text, out.diagnostics);
82 let bytes: Vec<u8> = out.bytes().map(|b| b.1).collect();
83 assert_eq!(
84 bytes,
85 code[0x100..0x100 + d.size as usize],
86 "opcode {op:02X} '{}'",
87 d.text
88 );
89 }
90 }
91}