Bweh/boy-debug/src/disassembler.rs

93 lines
3.5 KiB
Rust

//! Wraps the [boy] [Insn]-disassembler and extends it to disassemble entire instructions, rather than just single words
use boy::cpu::disasm::{Insn, Prefixed};
use std::iter::{Enumerate, Peekable};
pub trait Disassemble: Iterator<Item = u8> + Sized {
fn disassemble(self) -> Disassembler<Self>;
}
impl<T: Iterator<Item = u8> + Sized> Disassemble for T {
fn disassemble(self) -> Disassembler<Self> {
Disassembler::new(self)
}
}
/// An iterator over some bytes, which prints the disassembly to stdout
// TODO: parameterize this over a Writer
pub struct Disassembler<I: Iterator<Item = u8>> {
bytes: Peekable<Enumerate<I>>,
}
impl<I: Iterator<Item = u8>> Iterator for Disassembler<I> {
type Item = (usize, String);
fn next(&mut self) -> Option<Self::Item> {
let (index, insn) = self.bytes.next()?;
Some((index, self.print(insn.into())?))
}
}
impl<I: Iterator<Item = u8>> Disassembler<I> {
pub fn new(bytes: I) -> Self {
Disassembler { bytes: bytes.enumerate().peekable() }
}
pub fn index(&mut self) -> Option<usize> {
self.bytes.peek().map(|v| v.0)
}
pub fn imm8(&mut self) -> Option<u8> {
self.bytes.next().map(|v| v.1)
}
pub fn smm8(&mut self) -> Option<i8> {
self.imm8().map(|b| b as i8)
}
pub fn imm16(&mut self) -> Option<u16> {
let low = self.imm8()? as u16;
let high = self.imm8()? as u16;
Some(high << 8 | low)
}
pub fn smm16(&mut self) -> Option<i16> {
self.imm16().map(|w| w as i16)
}
pub fn print(&mut self, insn: Insn) -> Option<String> {
Some(match insn {
Insn::LdImm(reg) => format!("ld\t{reg}, {:02x}", self.imm8()?),
Insn::LdImm16(reg) => format!("ld\t{reg}, {:04x}", self.imm16()?),
Insn::LdAbs => format!("ld\ta, [{:04x}]", self.imm16()?),
Insn::StAbs => format!("ld\t[{:04x}], a", self.imm16()?),
Insn::StSpAbs => format!("ld\t[{:04x}], sp", self.imm16()?),
Insn::LdHlSpRel => format!("ld\thl, sp + {:02x}", self.imm8()?),
Insn::Ldh => format!("ldh\ta, [ff{:02x}]", self.imm8()?),
Insn::Sth => format!("ldh\t[ff{:02x}], a", self.imm8()?),
Insn::Add16SpI => format!("add\tsp, {:02x}", self.imm8()?),
Insn::AddI => format!("add\ta, {:02x}", self.imm8()?),
Insn::AdcI => format!("adc\ta, {:02x}", self.imm8()?),
Insn::SubI => format!("sub\ta, {:02x}", self.imm8()?),
Insn::SbcI => format!("sbc\ta, {:02x}", self.imm8()?),
Insn::AndI => format!("and\ta, {:02x}", self.imm8()?),
Insn::XorI => format!("xor\ta, {:02x}", self.imm8()?),
Insn::OrI => format!("or\ta, {:02x}", self.imm8()?),
Insn::CpI => format!("cp\ta, {:02x}", self.imm8()?),
Insn::Jr => format!("jr\t{0:02x} ({0:02})", self.smm8()?),
Insn::Jrc(cond) => format!("jr\t{cond}, {:02x} ({0:02})", self.smm8()?),
Insn::Jp => format!("jp\t{:04x}", self.imm16()?),
Insn::Jpc(cond) => format!("jp\t{cond}, {:04x}", self.imm16()?),
Insn::Call => format!("call\t{:04x}", self.imm16()?),
Insn::Callc(cond) => format!("call\t{cond}, {:04x}", self.imm16()?),
Insn::PrefixCB => self.print_prefix_cb()?,
_ => format!("{insn}"),
})
}
pub fn print_prefix_cb(&mut self) -> Option<String> {
let prefixed: Prefixed = self.imm8()?.into();
Some(format!("{prefixed}"))
}
}