boy: Initial public commit

- SM83 implementation kinda works
- Live disassembly, memory write tracing
- Pretty snazzy debugger with custom memory editor
- hexadecimal calculator with novel operator precedence rules
This commit is contained in:
2024-06-22 07:25:59 -05:00
commit acfbb8f1cf
16 changed files with 4646 additions and 0 deletions

9
boy-utils/Cargo.toml Normal file
View File

@@ -0,0 +1,9 @@
[package]
name = "boy-utils"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
boy = { path = ".." }

69
boy-utils/src/lib.rs Normal file
View File

@@ -0,0 +1,69 @@
/// Utilities and common behavior for the [boy] emulator
use std::io::Write;
use std::ops::Range;
use boy::memory::io::*;
/// Formats the data over a given range as a traditional hexdump
pub fn slice_hexdump(data: &[u8], range: Range<usize>) -> std::io::Result<()> {
const WIDTH: usize = 16;
const HEX: usize = 6;
const ASCII: usize = 3 * (WIDTH + 1) + HEX;
let base = range.start;
let mut out = std::io::stdout().lock();
let Some(data) = data.get(range) else {
return Ok(());
};
for (chunkno, chunk) in data.chunks(WIDTH).enumerate() {
write!(out, "{:04x}\x1b[{HEX}G", base + chunkno * WIDTH)?;
for (byteno, byte) in chunk.iter().enumerate() {
write!(out, "{}{byte:02x}", if byteno == 8 { " " } else { " " })?
}
write!(out, "\x1b[{ASCII}G|")?;
for byte in chunk.iter() {
match char::from_u32(*byte as u32) {
Some(c) if c.is_ascii_graphic() => write!(out, "{c}")?,
Some(_) | None => write!(out, "")?,
}
}
writeln!(out, "|")?
}
Ok(())
}
pub fn bus_hexdump(bus: &impl BusIO, range: Range<usize>) -> std::io::Result<()> {
use boy::memory::io::BusAux;
const WIDTH: usize = 16;
let mut out = std::io::stdout();
let upper = range.end;
for lower in range.clone().step_by(WIDTH) {
let upper = (lower + WIDTH).min(upper);
// write!(out, "{:04x}\x1b[{HEX}G", lower)?;
let _data: [u8; WIDTH] = bus.read_arr(lower).unwrap_or_else(|e| e);
write!(out, "{:04x} ", lower)?;
// write hexadecimal repr
for (idx, addr) in (lower..upper).enumerate() {
let space = if idx == WIDTH / 2 { " " } else { " " };
match bus.read(addr) {
Some(data) => write!(out, "{space}{data:02x}"),
None => write!(out, "{space}__"),
}?;
}
let padding = WIDTH - (upper - lower);
let padding = padding * 3 + if padding >= (WIDTH / 2) { 1 } else { 0 };
write!(out, "{:padding$} |", "")?;
for data in (lower..upper).flat_map(|addr| bus.read(addr)) {
match char::from_u32(data as u32) {
Some(c) if !c.is_control() => write!(out, "{c}"),
_ => write!(out, "."),
}?;
}
writeln!(out, "|")?;
}
Ok(())
}