1use crate::WhichCpu; 2use crate::api::debug::{Sega32XDebugView, Sega32XDebugger}; 3use crate::bus::{OtherCpu, Sh2Bus, Sh2BusGuard}; 4use genesis_components::cartridge::Cartridge; 5use sh2_emu::Sh2; 6use sh2_emu::bus::{AccessContext, BusInterface, OpSize}; 7use sh2_emu::debug::Sh2Debugger; 8use std::marker::PhantomData; 9use std::ops::{Deref, DerefMut}; 10use std::ptr::NonNull; 11use std::{cmp, ptr}; 12 13pub struct DebugSh2Bus { 14 bus: Sh2Bus, 15 debugger: NonNull<dyn Sega32XDebugger>, 16 other_sh2: *mut Sh2, 17} 18 19impl DebugSh2Bus { 20 pub fn cycle_counter(&self) -> u64 { 21 self.bus.cycle_counter 22 } 23 24 pub fn cycle_limit(&self) -> u64 { 25 self.bus.cycle_limit 26 } 27 28 fn maybe_sync_other_cpu(&mut self, address: u32, cpu: &mut Sh2) { 29 if !self.bus.need_to_sync_other(address) { 30 return; 31 } 32 33 let Some(OtherCpu { cpu: other_cpu, cycle_counter: other_cycle_counter }) = 34 &mut self.bus.other_sh2 35 else { 36 return; 37 }; 38 39 let cycle_limit = cmp::min(self.bus.cycle_counter, self.bus.cycle_limit); 40 41 // SAFETY: Similar to Sh2Bus::maybe_sync_other_cpu but also attaches a pointer to the 42 // current CPU, created from a mutable reference here. The current CPU and the original bus 43 // are not used while the other CPU is executing against the debug bus. 44 unsafe { 45 let mut other_bus = Self { 46 bus: Sh2Bus { 47 s32x_bus: self.bus.s32x_bus, 48 other_sh2: None, 49 which: self.bus.which.other(), 50 cycle_counter: other_cycle_counter.read(), 51 cycle_limit, 52 }, 53 debugger: self.debugger, 54 other_sh2: ptr::from_mut(cpu), 55 }; 56 57 while other_bus.bus.cycle_counter < cycle_limit { 58 other_cpu.as_mut().execute(crate::api::SH2_EXECUTION_SLICE_LEN, &mut other_bus); 59 } 60 other_cycle_counter.write(other_bus.bus.cycle_counter); 61 } 62 } 63} 64 65pub struct DebugSh2BusGuard<'debug, 'bus, 'cartridge, 'other> { 66 bus: DebugSh2Bus, 67 _debug_marker: PhantomData<&'debug ()>, 68 _bus_marker: PhantomData<&'bus ()>, 69 _cartridge_marker: PhantomData<&'cartridge ()>, 70 _other_marker: PhantomData<&'other ()>, 71} 72 73impl DebugSh2Bus { 74 #[inline] 75 pub fn create<'debug, 'bus, 'cartridge, 'other, Debugger: Sega32XDebugger>( 76 bus_guard: Sh2BusGuard<'bus, 'cartridge, 'other>, 77 debugger: &'debug mut Debugger, 78 ) -> DebugSh2BusGuard<'debug, 'bus, 'cartridge, 'other> { 79 // SAFETY: Consumes an Sh2BusGuard to get the inner bus, and returns another guard that has 80 // the same lifetimes plus the debugger lifetime. 81 // Converts the debugger mutable reference to a raw pointer here and uses the debugger 82 // lifetime to ensure that the caller cannot access the debugger until after this guard is 83 // dropped. 84 let debugger: &mut dyn Sega32XDebugger = debugger; 85 let debug_bus = DebugSh2Bus { 86 bus: bus_guard.bus, 87 debugger: debugger.into(), 88 other_sh2: ptr::null_mut(), 89 }; 90 91 DebugSh2BusGuard { 92 bus: debug_bus, 93 _debug_marker: PhantomData, 94 _bus_marker: PhantomData, 95 _cartridge_marker: PhantomData, 96 _other_marker: PhantomData, 97 } 98 } 99} 100 101impl Deref for DebugSh2BusGuard<'_, '_, '_, '_> { 102 type Target = DebugSh2Bus; 103 104 fn deref(&self) -> &Self::Target { 105 &self.bus 106 } 107} 108 109impl DerefMut for DebugSh2BusGuard<'_, '_, '_, '_> { 110 fn deref_mut(&mut self) -> &mut Self::Target { 111 &mut self.bus 112 } 113} 114 115pub struct DebugSh2BusView<'bus>(&'bus mut DebugSh2Bus); 116 117impl DebugSh2BusView<'_> { 118 fn which(&self) -> WhichCpu { 119 self.0.bus.which 120 } 121 122 fn as_debug_view<'slf, 'cpu, 'ret>( 123 &'slf mut self, 124 cpu: &'cpu mut Sh2, 125 ) -> Option<(Sega32XDebugView<'ret>, Option<&'slf mut Cartridge>)> 126 where 127 'slf: 'ret, 128 'cpu: 'ret, 129 { 130 let s32x_bus = unsafe { self.0.bus.s32x_bus.as_mut() }; 131 132 // SAFETY: other_sh2 is only set on DebugSh2Bus when created in DebugSh2Bus::maybe_sync_other_cpu 133 // above, where it is created from a mutable reference. Otherwise this pulls the other SH-2 134 // out of the inner Sh2Bus, which is only ever set in Sh2Bus::create. 135 let other_sh2 = unsafe { 136 if !self.0.other_sh2.is_null() { 137 self.0.other_sh2.as_mut().unwrap() 138 } else if let Some(other_sh2) = self.0.bus.other_sh2.as_mut() { 139 other_sh2.cpu.as_mut() 140 } else { 141 // This _shouldn't_ ever happen, but don't crash if it does 142 log::error!( 143 "Invalid debugger state; no pointer to other SH-2 while {:?} is executing", 144 self.0.bus.which 145 ); 146 return None; 147 } 148 }; 149 150 let (sh2_master, sh2_slave) = match self.0.bus.which { 151 WhichCpu::Master => (cpu, other_sh2), 152 WhichCpu::Slave => (other_sh2, cpu), 153 }; 154 155 let debug_view = Sega32XDebugView { 156 sdram: s32x_bus.sdram.as_mut_slice(), 157 sh2_master, 158 sh2_slave, 159 system_registers: &mut s32x_bus.registers, 160 vdp: &mut s32x_bus.vdp, 161 pwm: &mut s32x_bus.pwm, 162 }; 163 164 let cartridge = s32x_bus.cartridge.as_mut(); 165 166 Some((debug_view, cartridge)) 167 } 168} 169 170impl Sh2Debugger for DebugSh2BusView<'_> { 171 fn check_read<const SIZE: u8>(&mut self, address: u32, cpu: &mut Sh2) { 172 let which = self.which(); 173 174 // SAFETY: Debugger was created from a mutable reference, and DebugSh2Bus is only accessible 175 // through a guard 176 unsafe { 177 let debugger = self.0.debugger.as_mut(); 178 if debugger.check_sh2_read_breakpoint(which, address, OpSize::enum_value::<SIZE>()) 179 && let Some((debug_view, cartridge)) = self.as_debug_view(cpu) 180 { 181 log::info!("SH-2 {which:?} triggered read breakpoint: {address:08X}"); 182 183 debugger.handle_sh2_breakpoint(which, cartridge, debug_view); 184 } 185 } 186 } 187 188 fn apply_read<const SIZE: u8>( 189 &mut self, 190 address: u32, 191 ctx: AccessContext, 192 cpu: &mut Sh2, 193 ) -> u32 { 194 self.0.maybe_sync_other_cpu(address, cpu); 195 self.0.bus.read::<SIZE>(address, ctx) 196 } 197 198 fn check_write<const SIZE: u8>(&mut self, address: u32, value: u32, cpu: &mut Sh2) { 199 let which = self.which(); 200 201 // SAFETY: Debugger was created from a mutable reference, and DebugSh2Bus is only accessible 202 // through a guard 203 unsafe { 204 let debugger = self.0.debugger.as_mut(); 205 if debugger.check_sh2_write_breakpoint( 206 which, 207 address, 208 value, 209 OpSize::enum_value::<SIZE>(), 210 ) && let Some((debug_view, cartridge)) = self.as_debug_view(cpu) 211 { 212 log::info!("SH-2 {which:?} triggered write breakpoint: {address:08X} {value:08X}"); 213 214 debugger.handle_sh2_breakpoint(which, cartridge, debug_view); 215 } 216 } 217 } 218 219 fn apply_write<const SIZE: u8>( 220 &mut self, 221 address: u32, 222 value: u32, 223 ctx: AccessContext, 224 cpu: &mut Sh2, 225 ) { 226 self.0.maybe_sync_other_cpu(address, cpu); 227 self.0.bus.write::<SIZE>(address, value, ctx); 228 } 229 230 fn apply_read_cache_line( 231 &mut self, 232 address: u32, 233 ctx: AccessContext, 234 cpu: &mut Sh2, 235 ) -> [u16; 8] { 236 self.0.maybe_sync_other_cpu(address, cpu); 237 self.0.bus.read_cache_line(address, ctx) 238 } 239 240 fn check_execute(&mut self, pc: u32, opcode: u16, cpu: &mut Sh2) { 241 let which = self.which(); 242 243 // SAFETY: Debugger was created from a mutable reference, and DebugSh2Bus is only accessible 244 // through a guard 245 unsafe { 246 let debugger = self.0.debugger.as_mut(); 247 if debugger.check_sh2_execute_breakpoint(which, pc, opcode) 248 && let Some((debug_view, cartridge)) = self.as_debug_view(cpu) 249 { 250 log::info!("SH-2 {which:?} triggered execute breakpoint: PC={pc:08X}"); 251 252 debugger.handle_sh2_breakpoint(which, cartridge, debug_view); 253 } 254 } 255 } 256 257 fn check_interrupt(&mut self, interrupt_level: u8, cpu: &mut Sh2) { 258 let which = self.which(); 259 260 // SAFETY: Debugger was created from a mutable reference, and DebugSh2Bus is only accessible 261 // through a guard 262 unsafe { 263 let debugger = self.0.debugger.as_mut(); 264 if debugger.check_sh2_interrupt_breakpoint(which, interrupt_level) 265 && let Some((debug_view, cartridge)) = self.as_debug_view(cpu) 266 { 267 log::info!( 268 "SH-2 {which:?} triggered interrupt breakpoint, interrupt level {interrupt_level}" 269 ); 270 271 debugger.handle_sh2_breakpoint(which, cartridge, debug_view); 272 } 273 } 274 } 275} 276 277impl BusInterface for DebugSh2Bus { 278 type DebugView<'a> 279 = DebugSh2BusView<'a> 280 where 281 Self: 'a; 282 283 fn read<const SIZE: u8>(&mut self, address: u32, ctx: AccessContext) -> u32 { 284 self.bus.read::<SIZE>(address, ctx) 285 } 286 287 fn read_cache_line(&mut self, address: u32, ctx: AccessContext) -> [u16; 8] { 288 self.bus.read_cache_line(address, ctx) 289 } 290 291 fn write<const SIZE: u8>(&mut self, address: u32, value: u32, ctx: AccessContext) { 292 self.bus.write::<SIZE>(address, value, ctx); 293 } 294 295 fn reset(&self) -> bool { 296 self.bus.reset() 297 } 298 299 fn interrupt_level(&self) -> u8 { 300 self.bus.interrupt_level() 301 } 302 303 fn dma_request_0(&self) -> bool { 304 self.bus.dma_request_0() 305 } 306 307 fn dma_request_1(&self) -> bool { 308 self.bus.dma_request_1() 309 } 310 311 fn acknowledge_dreq_1(&mut self) { 312 self.bus.acknowledge_dreq_1(); 313 } 314 315 fn serial_rx(&mut self) -> Option<u8> { 316 self.bus.serial_rx() 317 } 318 319 fn serial_tx(&mut self, value: u8) { 320 self.bus.serial_tx(value); 321 } 322 323 fn increment_cycle_counter(&mut self, cycles: u64) { 324 self.bus.increment_cycle_counter(cycles); 325 } 326 327 fn should_stop_execution(&self) -> bool { 328 self.bus.should_stop_execution() 329 } 330 331 fn debug_view(&mut self) -> Option<Self::DebugView<'_>> { 332 Some(DebugSh2BusView(self)) 333 } 334 335 sh2_emu::impl_sh2_opcode_table!(DebugSh2Bus); 336}