debug.rsannotateddebug.rssource292 lines · 8.9 KB · raw
1use crate::Sh2;
2use crate::bus::{AccessContext, BusInterface, OpSize};
3use crate::cache::CacheMode;
4use crate::divu::DivisionUnit;
5use crate::dma::DmaController;
6
7pub trait Sh2Debugger {

Called for all reads, including reads to internal SH-2/SH7604 addresses and reads that hit in cache

9    fn check_read<const SIZE: u8>(&mut self, address: u32, cpu: &mut Sh2);

Called for reads that access the external bus; should apply the read

12    fn apply_read<const SIZE: u8>(
13        &mut self,
14        address: u32,
15        ctx: AccessContext,
16        cpu: &mut Sh2,
17    ) -> u32;

Called for all writes, including writes to internal SH-2/SH7604 addresses

20    fn check_write<const SIZE: u8>(&mut self, address: u32, value: u32, cpu: &mut Sh2);

Called for writes that access the external bus; should apply the write

23    fn apply_write<const SIZE: u8>(
24        &mut self,
25        address: u32,
26        value: u32,
27        ctx: AccessContext,
28        cpu: &mut Sh2,
29    );

Called on cache miss reads; should apply the read

32    fn apply_read_cache_line(
33        &mut self,
34        address: u32,
35        ctx: AccessContext,
36        cpu: &mut Sh2,
37    ) -> [u16; 8];

Called for each instruction before it executes

40    fn check_execute(&mut self, pc: u32, opcode: u16, cpu: &mut Sh2);

Called for each interrupt right before handling it

43    fn check_interrupt(&mut self, interrupt_level: u8, cpu: &mut Sh2);
44}

Dummy [Sh2Debugger] implementation that exists only to satisfy type constraints. Will panic if any methods are actually invoked

48pub struct DummySh2Debugger;
50impl Sh2Debugger for DummySh2Debugger {
51    fn check_read<const SIZE: u8>(&mut self, _address: u32, _cpu: &mut Sh2) {
52        unimplemented!("NullSh2Debugger is not a real debugger implementation")
53    }
54
55    fn apply_read<const SIZE: u8>(
56        &mut self,
57        _address: u32,
58        _ctx: AccessContext,
59        _cpu: &mut Sh2,
60    ) -> u32 {
61        unimplemented!("NullSh2Debugger is not a real debugger implementation")
62    }
63
64    fn check_write<const SIZE: u8>(&mut self, _address: u32, _value: u32, _cpu: &mut Sh2) {
65        unimplemented!("NullSh2Debugger is not a real debugger implementation")
66    }
67
68    fn apply_write<const SIZE: u8>(
69        &mut self,
70        _address: u32,
71        _value: u32,
72        _ctx: AccessContext,
73        _cpu: &mut Sh2,
74    ) {
75        unimplemented!("NullSh2Debugger is not a real debugger implementation")
76    }
77
78    fn apply_read_cache_line(
79        &mut self,
80        _address: u32,
81        _ctx: AccessContext,
82        _cpu: &mut Sh2,
83    ) -> [u16; 8] {
84        unimplemented!("NullSh2Debugger is not a real debugger implementation")
85    }
86
87    fn check_execute(&mut self, _pc: u32, _opcode: u16, _cpu: &mut Sh2) {
88        unimplemented!("NullSh2Debugger is not a real debugger implementation")
89    }
90
91    fn check_interrupt(&mut self, _interrupt_level: u8, _cpu: &mut Sh2) {
92        unimplemented!("NullSh2Debugger is not a real debugger implementation")
93    }
94}
95
96pub(crate) trait BusDebugExt {
97    fn check_read<const SIZE: u8>(&mut self, address: u32, cpu: &mut Sh2);
98
99    fn check_read_byte(&mut self, address: u32, cpu: &mut Sh2) {
100        self.check_read::<{ OpSize::BYTE }>(address, cpu);
101    }
102
103    fn check_read_word(&mut self, address: u32, cpu: &mut Sh2) {
104        self.check_read::<{ OpSize::WORD }>(address, cpu);
105    }
106
107    fn check_read_longword(&mut self, address: u32, cpu: &mut Sh2) {
108        self.check_read::<{ OpSize::LONGWORD }>(address, cpu);
109    }
110
111    fn apply_read<const SIZE: u8>(
112        &mut self,
113        address: u32,
114        ctx: AccessContext,
115        cpu: &mut Sh2,
116    ) -> u32;
117
118    fn apply_read_byte(&mut self, address: u32, ctx: AccessContext, cpu: &mut Sh2) -> u8 {
119        self.apply_read::<{ OpSize::BYTE }>(address, ctx, cpu) as u8
120    }
121
122    fn apply_read_word(&mut self, address: u32, ctx: AccessContext, cpu: &mut Sh2) -> u16 {
123        self.apply_read::<{ OpSize::WORD }>(address, ctx, cpu) as u16
124    }
125
126    fn apply_read_longword(&mut self, address: u32, ctx: AccessContext, cpu: &mut Sh2) -> u32 {
127        self.apply_read::<{ OpSize::LONGWORD }>(address, ctx, cpu)
128    }
129
130    fn check_write<const SIZE: u8>(&mut self, address: u32, value: u32, cpu: &mut Sh2);
131
132    fn check_write_byte(&mut self, address: u32, value: u8, cpu: &mut Sh2) {
133        self.check_write::<{ OpSize::BYTE }>(address, value.into(), cpu);
134    }
135
136    fn check_write_word(&mut self, address: u32, value: u16, cpu: &mut Sh2) {
137        self.check_write::<{ OpSize::WORD }>(address, value.into(), cpu);
138    }
139
140    fn check_write_longword(&mut self, address: u32, value: u32, cpu: &mut Sh2) {
141        self.check_write::<{ OpSize::LONGWORD }>(address, value, cpu);
142    }
143
144    fn apply_write<const SIZE: u8>(
145        &mut self,
146        address: u32,
147        value: u32,
148        ctx: AccessContext,
149        cpu: &mut Sh2,
150    );
151
152    fn apply_write_byte(&mut self, address: u32, value: u8, ctx: AccessContext, cpu: &mut Sh2) {
153        self.apply_write::<{ OpSize::BYTE }>(address, value.into(), ctx, cpu);
154    }
155
156    fn apply_write_word(&mut self, address: u32, value: u16, ctx: AccessContext, cpu: &mut Sh2) {
157        self.apply_write::<{ OpSize::WORD }>(address, value.into(), ctx, cpu);
158    }
159
160    fn apply_write_longword(
161        &mut self,
162        address: u32,
163        value: u32,
164        ctx: AccessContext,
165        cpu: &mut Sh2,
166    ) {
167        self.apply_write::<{ OpSize::LONGWORD }>(address, value, ctx, cpu);
168    }
169
170    fn apply_read_cache_line(
171        &mut self,
172        address: u32,
173        ctx: AccessContext,
174        cpu: &mut Sh2,
175    ) -> [u16; 8];
176
177    fn check_execute(&mut self, pc: u32, opcode: u16, cpu: &mut Sh2);
178}
179
180impl<Bus: BusInterface> BusDebugExt for Bus {
181    fn check_read<const SIZE: u8>(&mut self, address: u32, cpu: &mut Sh2) {
182        let Some(mut debugger) = self.debug_view() else { return };
183        debugger.check_read::<SIZE>(address, cpu);
184    }
185
186    fn apply_read<const SIZE: u8>(
187        &mut self,
188        address: u32,
189        ctx: AccessContext,
190        cpu: &mut Sh2,
191    ) -> u32 {
192        if let Some(mut debugger) = self.debug_view() {
193            debugger.apply_read::<SIZE>(address, ctx, cpu)
194        } else {
195            self.read::<SIZE>(address, ctx)
196        }
197    }
198
199    fn check_write<const SIZE: u8>(&mut self, address: u32, value: u32, cpu: &mut Sh2) {
200        let Some(mut debugger) = self.debug_view() else { return };
201        debugger.check_write::<SIZE>(address, value, cpu);
202    }
203
204    fn apply_write<const SIZE: u8>(
205        &mut self,
206        address: u32,
207        value: u32,
208        ctx: AccessContext,
209        cpu: &mut Sh2,
210    ) {
211        if let Some(mut debugger) = self.debug_view() {
212            debugger.apply_write::<SIZE>(address, value, ctx, cpu);
213        } else {
214            self.write::<SIZE>(address, value, ctx);
215        }
216    }
217
218    fn apply_read_cache_line(
219        &mut self,
220        address: u32,
221        ctx: AccessContext,
222        cpu: &mut Sh2,
223    ) -> [u16; 8] {
224        if let Some(mut debugger) = self.debug_view() {
225            debugger.apply_read_cache_line(address, ctx, cpu)
226        } else {
227            self.read_cache_line(address, ctx)
228        }
229    }
230
231    fn check_execute(&mut self, pc: u32, opcode: u16, cpu: &mut Sh2) {
232        let Some(mut debugger) = self.debug_view() else { return };
233        debugger.check_execute(pc, opcode, cpu);
234    }
235}
236
237#[derive(Debug, Clone)]
238pub struct CacheDebugState {
239    pub enabled: bool,
240    pub instruction_replacement_enabled: bool,
241    pub data_replacement_enabled: bool,
242    pub mode: CacheMode,
243}
244
245#[derive(Debug, Clone)]
246pub struct InterruptDebugState {
247    pub wdt_priority: u8,
248    pub dmac_priority: u8,
249    pub sci_priority: u8,
250    pub wdt_vector: u8,
251    pub dma0_vector: u8,
252    pub dma1_vector: u8,
253    pub sci_vector: u8,
254}
255
256#[derive(Debug, Clone)]
257pub struct WatchdogTimerState {
258    pub enabled: bool,
259    pub counter: u8,
260    pub system_clock_divider: u64,
261    pub overflow_flag: bool,
262}
263
264#[derive(Debug, Clone)]
265pub struct Sh7604DebugState {
266    pub cache: CacheDebugState,
267    pub interrupts: InterruptDebugState,
268    pub wdt: WatchdogTimerState,
269    pub dmac: DmaController,
270    pub divu: DivisionUnit,
271}
272
273impl Sh2 {
274    #[must_use]
275    pub fn sh7604_debug_state(&self) -> Sh7604DebugState {
276        Sh7604DebugState {
277            cache: self.cache.debug_state(),
278            interrupts: InterruptDebugState {
279                wdt_priority: self.sh7604.interrupts.wdt_priority,
280                dmac_priority: self.sh7604.interrupts.dmac_priority,
281                sci_priority: self.sh7604.interrupts.sci_priority,
282                wdt_vector: self.sh7604.interrupts.wdt_vector,
283                dma0_vector: self.sh7604.interrupts.dma0_vector,
284                dma1_vector: self.sh7604.interrupts.dma1_vector,
285                sci_vector: self.sh7604.interrupts.sci_rx_ok_vector,
286            },
287            wdt: self.watchdog_timer.debug_state(),
288            dmac: self.dmac.clone(),
289            divu: self.divu.clone(),
290        }
291    }
292}