summaryrefslogtreecommitdiff
path: root/src/debug.rs
blob: c01ee15ee95aa9f437212e675fc4ab76bc28f994 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use bedrock_core::*;

use std::path::Path;
use std::time::Instant;


const NORMAL: &str = "\x1b[0m";
const BOLD:   &str = "\x1b[1m";
const DIM:    &str = "\x1b[2m";
const YELLOW: &str = "\x1b[33m";
const BLUE:   &str = "\x1b[34m";


pub struct DebugState {
    pub enabled: bool,
    last_cycle: usize,
    last_mark: Instant,
    symbols: DebugSymbols,
}

impl DebugState {
    pub fn new<P: AsRef<Path>>(enabled: bool, symbols_path: Option<P>) -> Self {
        Self {
            enabled,
            last_cycle: 0,
            last_mark: Instant::now(),
            symbols: DebugSymbols::from_path_opt(symbols_path),
        }
    }

    pub fn info(&self, string: &str) {
        if self.enabled {
            eprintln!("{BOLD}{BLUE}[INFO]{NORMAL}: {string}{NORMAL}");
        }
    }

    pub fn debug_summary(&mut self, core: &BedrockCore) {
        if self.enabled {
            let prev_pc = core.mem.pc.wrapping_sub(1);
            eprintln!("\n PC: 0x{:04x}   Cycles: {} (+{} in {:.2?})",
                prev_pc, core.cycle,
                core.cycle.saturating_sub(self.last_cycle),
                self.last_mark.elapsed(),
            );
            eprint!("WST: ");
            debug_stack(&core.wst, 0x10);
            eprint!("RST: ");
            debug_stack(&core.rst, 0x10);
            // Print information about the closest symbol.
            if let Some(symbol) = self.symbols.for_address(prev_pc) {
                let name = &symbol.name;
                let address = &symbol.address;
                eprint!("SYM: {BLUE}@{name}{NORMAL} 0x{address:04x}");
                if let Some(location) = &symbol.location {
                    eprint!(" {DIM}{location}{NORMAL}");
                }
                eprintln!();
            }
        }
        self.last_cycle = core.cycle;
        self.last_mark = Instant::now();
    }
}


fn debug_stack(stack: &Stack, len: usize) {
    for i in 0..len {
        if i == stack.sp as usize { eprint!("{YELLOW}"); }
        eprint!("{:02x} ", stack.mem[i]);
    }
    eprintln!("{NORMAL}");
}


struct DebugSymbols {
    symbols: Vec<DebugSymbol>
}

impl DebugSymbols {
    /// Load debug symbols from a symbols file.
    pub fn from_path_opt<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 }
    }

    pub fn for_address(&self, address: u16) -> Option<&DebugSymbol> {
        if self.symbols.is_empty() { return None; }
        let symbol = 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)?)?,
        };
        Some(&symbol)
    }
}

struct DebugSymbol {
    address: u16,
    name: String,
    location: Option<String>,
}

impl DebugSymbol {
    pub fn from_line(line: &str) -> Option<Self> {
        if let Some((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 } )
            }
        } else {
            None
        }
    }
}