Genesis+32X debugger code, in a submodule to limit the blast radius of unsafe usage here
3use crate::api::debug::{ 4 GenesisComponents, GenesisCpu, GenesisDebugView, GenesisDebugger, GenesisDebuggerWithCpus, 5}; 6use crate::bus::PendingWrites; 7use genesis_components::cartridge::Cartridge; 8use genesis_components::vdp::Vdp; 9use genesis_components::ym2612::Ym2612; 10use m68000_emu::M68000; 11use s32x_core::WhichCpu; 12use s32x_core::api::debug::{Sega32XDebugView, Sega32XDebugger}; 13use segacd_core::api::SegaCd; 14use sh2_emu::bus::OpSizeEnum; 15use std::marker::PhantomData; 16use std::ops::{Deref, DerefMut}; 17use std::ptr; 18use std::ptr::NonNull; 19use ti_sn76489::Sn76489; 20use z80_emu::Z80;
22pub(crate) struct GenesisDebuggerFor32X { 23 debugger: NonNull<GenesisDebugger>, 24 m68k: NonNull<M68000>, 25 z80: NonNull<Z80>, 26 sega_cd: *mut SegaCd, 27 working_ram: NonNull<[u16]>, 28 audio_ram: NonNull<[u8]>, 29 z80_bank_number: u32, 30 pending_writes: NonNull<PendingWrites>, 31 vdp: NonNull<Vdp>, 32 ym2612: NonNull<Ym2612>, 33 psg: NonNull<Sn76489>, 34} 35 36pub(crate) struct GenesisDebuggerFor32XGuard<'debugger, 'scd, 'genesis> { 37 debugger: GenesisDebuggerFor32X, 38 _debugger_marker: PhantomData<&'debugger ()>, 39 _scd_marker: PhantomData<&'scd ()>, 40 _genesis_marker: PhantomData<&'genesis ()>, 41} 42 43impl Deref for GenesisDebuggerFor32XGuard<'_, '_, '_> { 44 type Target = GenesisDebuggerFor32X; 45 46 fn deref(&self) -> &Self::Target { 47 // SAFETY: Code outside of this module cannot do anything with a shared reference to a 48 // GenesisDebuggerFor32X; all Sega32XDebugger trait methods take a &mut self receiver, and 49 // no GenesisDebuggerFor32X fields are visible outside of this module 50 &self.debugger 51 } 52} 53 54impl DerefMut for GenesisDebuggerFor32XGuard<'_, '_, '_> { 55 fn deref_mut(&mut self) -> &mut Self::Target { 56 &mut self.debugger 57 } 58} 59 60impl GenesisDebuggerWithCpus<'_, '_> { 61 pub(crate) fn for_32x<'scd, 'genesis>( 62 &mut self, 63 sega_cd: Option<&'scd mut SegaCd>, 64 genesis_components: GenesisComponents<'genesis>, 65 ) -> GenesisDebuggerFor32XGuard<'_, 'scd, 'genesis> { 66 // SAFETY: Creates raw pointers from mutable references here. The returned GenesisDebuggerFor32X 67 // is behind a guard so that the caller cannot access any of these mutable references until 68 // after dropping the guard 69 GenesisDebuggerFor32XGuard { 70 debugger: GenesisDebuggerFor32X { 71 debugger: self.debugger.into(), 72 m68k: self.m68k.into(), 73 z80: self.z80.into(), 74 sega_cd: sega_cd.map(ptr::from_mut).unwrap_or(ptr::null_mut()), 75 working_ram: genesis_components.working_ram.into(), 76 audio_ram: genesis_components.audio_ram.into(), 77 z80_bank_number: genesis_components.z80_bank_number, 78 pending_writes: genesis_components.pending_writes.into(), 79 vdp: genesis_components.vdp.into(), 80 ym2612: genesis_components.ym2612.into(), 81 psg: genesis_components.psg.into(), 82 }, 83 _debugger_marker: PhantomData, 84 _scd_marker: PhantomData, 85 _genesis_marker: PhantomData, 86 } 87 } 88} 89 90impl Sega32XDebugger for GenesisDebuggerFor32X { 91 fn check_sh2_read_breakpoint( 92 &mut self, 93 which: WhichCpu, 94 address: u32, 95 size: OpSizeEnum, 96 ) -> bool { 97 // SAFETY: Debugger pointer was created from a mutable reference, and the debugger is only 98 // accessible through a guard 99 unsafe { 100 self.debugger.as_mut().sh2_breakpoints.breakpoints[which as usize] 101 .should_break_read(address, size) 102 } 103 } 104 105 fn check_sh2_write_breakpoint( 106 &mut self, 107 which: WhichCpu, 108 address: u32, 109 _value: u32, 110 size: OpSizeEnum, 111 ) -> bool { 112 // SAFETY: Debugger pointer was created from a mutable reference, and the debugger is only 113 // accessible through a guard 114 unsafe { 115 self.debugger.as_mut().sh2_breakpoints.breakpoints[which as usize] 116 .should_break_write(address, size) 117 } 118 } 119 120 fn check_sh2_execute_breakpoint(&mut self, which: WhichCpu, pc: u32, _opcode: u16) -> bool { 121 // SAFETY: Debugger pointer was created from a mutable reference, and the debugger is only 122 // accessible through a guard 123 unsafe { 124 let sh2_breakpoints = &mut self.debugger.as_mut().sh2_breakpoints; 125 126 let execute = sh2_breakpoints.update_pc_and_check_execute(which, pc); 127 let step = sh2_breakpoints.check_break_step(which); 128 129 execute || step 130 } 131 } 132 133 fn check_sh2_interrupt_breakpoint(&mut self, which: WhichCpu, interrupt_level: u8) -> bool { 134 // SAFETY: Debugger pointer was created from a mutable reference, and the debugger is only 135 // accessible through a guard 136 unsafe { 137 self.debugger.as_mut().sh2_breakpoints.breakpoints[which as usize] 138 .should_break_interrupt(interrupt_level) 139 } 140 } 141 142 fn handle_sh2_breakpoint( 143 &mut self, 144 which: WhichCpu, 145 cartridge: Option<&mut Cartridge>, 146 debug_view: Sega32XDebugView<'_>, 147 ) { 148 // SAFETY: All of these raw pointers were created from mutable references in 149 // GenesisDebuggerWithCpus::for_32x above, and the caller cannot access the underlying 150 // values while GenesisDebuggerFor32XGuard is alive, so it is safe to create mutable 151 // references from these pointers 152 unsafe { 153 let mut genesis_debug_view = GenesisDebugView { 154 sega_cd: self.sega_cd.as_mut().map(SegaCd::as_debug_view), 155 sega_32x: Some(debug_view), 156 m68k: self.m68k.as_mut(), 157 z80: self.z80.as_mut(), 158 cartridge, 159 working_ram: self.working_ram.as_mut(), 160 audio_ram: self.audio_ram.as_mut(), 161 z80_bank_number: self.z80_bank_number, 162 pending_writes: self.pending_writes.as_mut(), 163 vdp: self.vdp.as_mut(), 164 ym2612: self.ym2612.as_mut(), 165 psg: self.psg.as_mut(), 166 }; 167 self.debugger 168 .as_mut() 169 .handle_breakpoint(GenesisCpu::Sh2(which), &mut genesis_debug_view); 170 } 171 } 172}