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 { 8 /// 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); 10 11 /// 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; 18 19 /// 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); 21 22 /// 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 ); 30 31 /// 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]; 38 39 /// Called for each instruction before it executes 40 fn check_execute(&mut self, pc: u32, opcode: u16, cpu: &mut Sh2); 41 42 /// Called for each interrupt right before handling it 43 fn check_interrupt(&mut self, interrupt_level: u8, cpu: &mut Sh2); 44} 45 46/// Dummy [`Sh2Debugger`] implementation that exists only to satisfy type constraints. 47/// Will panic if any methods are actually invoked 48pub struct DummySh2Debugger; 49 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}