summaryrefslogtreecommitdiff
path: root/src/debug.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug.rs')
-rw-r--r--src/debug.rs126
1 files changed, 126 insertions, 0 deletions
diff --git a/src/debug.rs b/src/debug.rs
new file mode 100644
index 0000000..b007d9f
--- /dev/null
+++ b/src/debug.rs
@@ -0,0 +1,126 @@
+use crate::*;
+
+use inked::{ink, InkedString};
+
+
+pub struct DebugState {
+ pub enabled: bool,
+ last_cycle: usize,
+ mark: Instant,
+ symbols: DebugSymbols,
+}
+
+impl DebugState {
+ pub fn new<P: AsRef<Path>>(enabled: bool, symbols_path: Option<P>) -> Self {
+ Self {
+ enabled,
+ last_cycle: 0,
+ mark: Instant::now(),
+ symbols: DebugSymbols::from_path(symbols_path),
+ }
+ }
+
+ pub fn debug_full(&mut self, core: &BedrockCore) {
+ if self.enabled {
+ let prev_pc = core.mem.pc.wrapping_sub(1);
+ let cycle = core.cycle;
+ let delta = core.cycle.saturating_sub(self.last_cycle);
+ let elapsed = self.mark.elapsed();
+
+ eprintln!(" PC: 0x{prev_pc:04x} Cycle: {cycle} (+{delta} in {elapsed:.2?})");
+ eprint!("WST: "); debug_stack(&core.wst);
+ eprint!("RST: "); debug_stack(&core.rst);
+ // Print information about the nearest symbol.
+ if let Some(symbol) = self.symbols.for_address(prev_pc) {
+ let name = &symbol.name;
+ let address = &symbol.address;
+ let mut string = InkedString::new();
+ string.push(ink!("SYM: "));
+ string.push(ink!("@{name}").blue());
+ string.push(ink!(" 0x{address:04X}"));
+ if let Some(location) = &symbol.location {
+ string.push(ink!(" "));
+ string.push(ink!("{location}").dim());
+ }
+ string.eprintln();
+ }
+ }
+ self.last_cycle = core.cycle;
+ self.mark = Instant::now();
+ }
+
+ pub fn debug_timing(&mut self, core: &BedrockCore) {
+ if self.enabled {
+ let cycle = core.cycle;
+ let delta = core.cycle.saturating_sub(self.last_cycle);
+ let elapsed = self.mark.elapsed();
+
+ eprintln!("Cycle: {cycle} (+{delta} in {elapsed:.2?})");
+ }
+ self.last_cycle = core.cycle;
+ self.mark = Instant::now();
+ }
+}
+
+
+fn debug_stack(stack: &Stack) {
+ for i in 0..(stack.sp as usize) {
+ eprint!("{:02X} ", stack.mem[i]);
+ }
+ eprintln!();
+}
+
+
+
+pub struct DebugSymbols {
+ pub symbols: Vec<DebugSymbol>
+}
+
+impl DebugSymbols {
+ /// Load debug symbols from a symbols file.
+ pub fn from_path<P: AsRef<Path>>(path: Option<P>) -> Self {
+ let mut symbols = Vec::new();
+ if let Some(path) = path {
+ if let Ok(string) = std::fs::read_to_string(path) {
+ for line in string.lines() {
+ if let Some(symbol) = DebugSymbol::from_line(line) {
+ symbols.push(symbol);
+ }
+ }
+ }
+ }
+ symbols.sort_by_key(|s| s.address);
+ Self { symbols }
+ }
+
+ /// Return the symbol matching a given program address.
+ pub fn for_address(&self, address: u16) -> Option<&DebugSymbol> {
+ if self.symbols.is_empty() { return None; }
+ match self.symbols.binary_search_by_key(&address, |s| s.address) {
+ Ok(index) => self.symbols.get(index),
+ Err(index) => self.symbols.get(index.checked_sub(1)?),
+ }
+ }
+}
+
+
+pub struct DebugSymbol {
+ pub address: u16,
+ pub name: String,
+ pub location: Option<String>,
+}
+
+impl DebugSymbol {
+ pub fn from_line(line: &str) -> Option<Self> {
+ let (address, line) = line.split_once(' ')?;
+ let address = u16::from_str_radix(address, 16).ok()?;
+ if let Some((name, location)) = line.split_once(' ') {
+ let name = name.to_string();
+ let location = Some(location.to_string());
+ Some( DebugSymbol { address, name, location } )
+ } else {
+ let name = line.to_string();
+ Some( DebugSymbol { address, name, location: None } )
+ }
+ }
+}