trapezoid_core/cpu/
debugger.rs1use std::collections::{HashMap, HashSet};
2
3use super::{
4 instruction::{Instruction, Opcode},
5 register::Registers,
6 CpuBusProvider, CpuState,
7};
8
9#[derive(Default, Clone, Copy, PartialEq, Eq)]
10struct EnabledBreakpoints {
11 step_over: bool,
12 step_out: bool,
13 normal: bool,
14}
15
16type InstructionTraceHandler = Box<dyn Fn(&Registers, &Instruction, bool)>;
17
18pub struct Debugger {
19 paused: bool,
20 last_state: CpuState,
21
22 call_stack: Vec<u32>,
23
24 instruction_breakpoints: HashMap<u32, EnabledBreakpoints>,
25 write_breakpoints: HashSet<u32>,
26 read_breakpoints: HashSet<u32>,
27 in_breakpoint: bool,
30 step: bool,
32 step_over: bool,
33
34 instruction_trace_handler: Option<InstructionTraceHandler>,
35
36 last_instruction: Instruction,
37}
38
39impl Debugger {
40 pub(crate) fn new() -> Self {
41 Self {
42 paused: false,
43 last_state: CpuState::Normal,
44
45 call_stack: Vec::new(),
46
47 instruction_breakpoints: HashMap::new(),
48 write_breakpoints: HashSet::new(),
49 read_breakpoints: HashSet::new(),
50 in_breakpoint: false,
51 step: false,
52 step_over: false,
53 instruction_trace_handler: None,
54
55 last_instruction: Instruction::from_u32(0, 0),
56 }
57 }
58
59 pub(crate) fn set_pause(&mut self, paused: bool) {
60 self.paused = paused;
61 }
62
63 pub(crate) fn paused(&self) -> bool {
64 self.paused
65 }
66
67 pub(crate) fn last_state(&self) -> CpuState {
68 self.last_state
69 }
70
71 pub(crate) fn clear_state(&mut self) {
72 self.last_state = CpuState::Normal;
73 self.paused = false;
74 }
75
76 pub(crate) fn handle_pending_processing<P: CpuBusProvider>(
86 &mut self,
87 bus: &mut P,
88 regs: &Registers,
89 jumping: bool,
90 ) {
91 if self.step_over {
93 self.step_over = false;
94
95 let offset = if jumping { 4 } else { 0 };
104
105 let instr = bus.read_u32(regs.pc - offset).unwrap();
107 let instr = Instruction::from_u32(instr, regs.pc);
108
109 if let Opcode::Jal | Opcode::Jalr = instr.opcode {
111 self.instruction_breakpoints
112 .entry(regs.pc + 8 - offset)
113 .or_default()
114 .step_over = true;
115 } else {
116 self.step = true;
117 }
118 }
119 }
120
121 pub(crate) fn trace_exception(&mut self, return_addr: u32) {
122 self.call_stack.push(return_addr);
123 }
124
125 pub(crate) fn trace_instruction(
126 &mut self,
127 regs: &Registers,
128 jumping: bool,
129 instruction: &Instruction,
130 ) -> bool {
131 if let Some(breakpoints_data) = self.instruction_breakpoints.get_mut(®s.pc) {
132 if breakpoints_data.step_over {
133 breakpoints_data.step_over = false;
134 if *breakpoints_data == EnabledBreakpoints::default() {
135 self.instruction_breakpoints.remove(®s.pc);
136 }
137 self.set_pause(true);
138 self.last_state = CpuState::StepOver;
139 return true;
140 }
141
142 if breakpoints_data.step_out {
143 breakpoints_data.step_out = false;
144 if *breakpoints_data == EnabledBreakpoints::default() {
145 self.instruction_breakpoints.remove(®s.pc);
146 }
147 self.set_pause(true);
148 self.last_state = CpuState::StepOut;
149 return true;
150 }
151
152 if !self.in_breakpoint && breakpoints_data.normal {
153 self.in_breakpoint = true;
154 self.set_pause(true);
155 self.last_state = CpuState::InstructionBreakpoint(regs.pc);
156 return true;
157 }
158 }
159
160 self.in_breakpoint = false;
164
165 if jumping {
166 match self.last_instruction.opcode {
167 Opcode::Jal | Opcode::Jalr => {
168 self.call_stack.push(self.last_instruction.pc + 8);
169 }
170 Opcode::Jr => {
171 let target = regs.read_general(self.last_instruction.rs_raw);
180
181 if !self.call_stack.is_empty() {
182 let mut c = 1;
183 for x in self.call_stack.iter().rev() {
184 if *x == target {
185 self.call_stack.truncate(self.call_stack.len() - c);
186 break;
187 }
188
189 c += 1;
190 }
191 }
192 }
193 _ => {}
194 }
195 }
196
197 if let Some(handler) = &self.instruction_trace_handler {
198 handler(regs, instruction, jumping);
199 }
200
201 if self.step {
202 self.set_pause(true);
203 self.step = false;
204 self.last_state = CpuState::Step;
205 }
206
207 self.last_instruction = instruction.clone();
208
209 false
211 }
212
213 pub(crate) fn trace_write(&mut self, addr: u32, bits: u8) {
214 if !self.write_breakpoints.is_empty() && self.write_breakpoints.contains(&addr) {
215 self.set_pause(true);
216 self.last_state = CpuState::WriteBreakpoint { addr, bits };
217 }
218 }
219
220 pub(crate) fn trace_read(&mut self, addr: u32, bits: u8) {
221 if !self.read_breakpoints.is_empty() && self.read_breakpoints.contains(&addr) {
222 self.set_pause(true);
223 self.last_state = CpuState::ReadBreakpoint { addr, bits };
224 }
225 }
226}
227
228impl Debugger {
229 pub fn single_step(&mut self) {
230 self.step = true;
231 }
232
233 pub fn step_over(&mut self) {
234 self.step_over = true;
235 }
236
237 pub fn step_out(&mut self) {
238 let Some(last_frame) = self.call_stack.last() else {
239 return;
240 };
241
242 self.instruction_breakpoints
243 .entry(*last_frame)
244 .or_default()
245 .step_out = true;
246 }
247
248 pub fn set_instruction_trace_handler(&mut self, handler: Option<InstructionTraceHandler>) {
253 self.instruction_trace_handler = handler;
254 }
255
256 pub fn add_breakpoint(&mut self, address: u32) {
257 self.instruction_breakpoints
258 .entry(address)
259 .or_default()
260 .normal = true;
261 }
262
263 pub fn remove_breakpoint(&mut self, address: u32) -> bool {
264 if let Some(v) = self.instruction_breakpoints.get_mut(&address) {
265 v.normal = false;
266 if *v == EnabledBreakpoints::default() {
268 self.instruction_breakpoints.remove(&address);
269 }
270 true
271 } else {
272 false
273 }
274 }
275
276 pub fn add_write_breakpoint(&mut self, address: u32) {
277 self.write_breakpoints.insert(address);
278 }
279
280 pub fn remove_write_breakpoint(&mut self, address: u32) -> bool {
281 self.write_breakpoints.remove(&address)
282 }
283
284 pub fn add_read_breakpoint(&mut self, address: u32) {
285 self.read_breakpoints.insert(address);
286 }
287
288 pub fn remove_read_breakpoint(&mut self, address: u32) -> bool {
289 self.read_breakpoints.remove(&address)
290 }
291
292 pub fn instruction_breakpoints(&self) -> HashSet<u32> {
293 self.instruction_breakpoints
294 .iter()
295 .filter_map(|(k, v)| if v.normal { Some(*k) } else { None })
296 .collect::<HashSet<_>>()
297 }
298
299 pub fn write_breakpoints(&self) -> &HashSet<u32> {
300 &self.write_breakpoints
301 }
302
303 pub fn read_breakpoints(&self) -> &HashSet<u32> {
304 &self.read_breakpoints
305 }
306
307 pub fn call_stack(&self) -> &[u32] {
308 &self.call_stack
309 }
310}