s32x.rsannotateds32x.rssource172 lines · 6.3 KB · raw

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}