debug.rsannotateddebug.rssource217 lines · 6.3 KB · raw
1use crate::WhichCpu;
2use crate::api::Sega32X;
3use crate::pwm::PwmChip;
4use crate::registers::SystemRegisters;
5use crate::vdp::Vdp;
6use crate::vdp::debug::VdpDebugState;
7use genesis_components::cartridge::Cartridge;
8use genesis_components::debug::CramEntry;
9use jgenesis_common::debug::{DebugMemoryView, DebugWordsView, Endian};
10use jgenesis_proc_macros::EnumAll;
11use sh2_emu::Sh2;
12use sh2_emu::bus::OpSizeEnum;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumAll)]
15pub enum S32XMemoryArea {
16    Sdram,
17    MasterSh2Cache,
18    SlaveSh2Cache,
19    FrameBuffer0,
20    FrameBuffer1,
21    PaletteRam,
22}
23
24#[derive(Debug, Clone)]
25pub struct Sega32XDebugState {
26    pub sdram: Box<[u16]>,
27    pub sh2_master: Sh2,
28    pub sh2_slave: Sh2,
29    system_registers: SystemRegisters,
30    vdp: VdpDebugState,
31    pwm: PwmChip,
32}
33
34impl Sega32XDebugState {
35    #[must_use]
36    pub fn sh2(&mut self, which: WhichCpu) -> &mut Sh2 {
37        match which {
38            WhichCpu::Master => &mut self.sh2_master,
39            WhichCpu::Slave => &mut self.sh2_slave,
40        }
41    }
42
43    #[must_use]
44    pub fn m68k_rom_bank(&self) -> u8 {
45        self.system_registers.m68k_rom_bank
46    }
47
48    pub fn copy_palette(&mut self, out: &mut [CramEntry]) {
49        self.vdp.copy_palette(out);
50    }
51
52    pub fn dump_32x_system_registers(&self, callback: impl FnMut(&str, &[(&str, &str)])) {
53        let h_interrupt_in_vblank = self.vdp.hen_bit();
54        let h_interrupt_interval = self.vdp.h_interrupt_interval();
55
56        self.system_registers.dump(h_interrupt_in_vblank, h_interrupt_interval, callback);
57    }
58
59    pub fn dump_32x_vdp_registers(&mut self, callback: impl FnMut(&str, &[(&str, &str)])) {
60        self.vdp.dump_registers(callback);
61    }
62
63    pub fn dump_pwm_registers(&mut self, callback: impl FnMut(&str, &[(&str, &str)])) {
64        self.pwm.dump_registers(callback);
65    }
66
67    #[must_use]
68    pub fn s32x_memory_view(
69        &mut self,
70        memory_area: S32XMemoryArea,
71    ) -> Box<dyn DebugMemoryView + '_> {
72        match memory_area {
73            S32XMemoryArea::Sdram => Box::new(DebugWordsView(&mut self.sdram, Endian::Big)),
74            S32XMemoryArea::MasterSh2Cache => Box::new(self.sh2_master.debug_cache_view()),
75            S32XMemoryArea::SlaveSh2Cache => Box::new(self.sh2_slave.debug_cache_view()),
76            S32XMemoryArea::FrameBuffer0 => Box::new(self.vdp.debug_frame_buffer_view(0)),
77            S32XMemoryArea::FrameBuffer1 => Box::new(self.vdp.debug_frame_buffer_view(1)),
78            S32XMemoryArea::PaletteRam => Box::new(self.vdp.debug_palette_ram_view()),
79        }
80    }
81}
82
83pub struct Sega32XDebugView<'s32x> {
84    pub(crate) sdram: &'s32x mut [u16],
85    pub(crate) sh2_master: &'s32x mut Sh2,
86    pub(crate) sh2_slave: &'s32x mut Sh2,
87    pub(crate) system_registers: &'s32x mut SystemRegisters,
88    pub(crate) vdp: &'s32x mut Vdp,
89    pub(crate) pwm: &'s32x mut PwmChip,
90}
91
92impl Sega32XDebugView<'_> {
93    #[must_use]
94    pub fn to_debug_state(&self) -> Sega32XDebugState {
95        Sega32XDebugState {
96            sdram: self.sdram.to_vec().into_boxed_slice(),
97            sh2_master: self.sh2_master.clone(),
98            sh2_slave: self.sh2_slave.clone(),
99            system_registers: self.system_registers.clone(),
100            vdp: self.vdp.to_debug_state(),
101            pwm: self.pwm.clone(),
102        }
103    }
104
105    pub fn apply_memory_edit(&mut self, memory_area: S32XMemoryArea, address: usize, value: u8) {
106        match memory_area {
107            S32XMemoryArea::Sdram => {
108                DebugWordsView(self.sdram, Endian::Big).write(address, value);
109            }
110            S32XMemoryArea::MasterSh2Cache => {
111                self.sh2_master.debug_cache_view().write(address, value);
112            }
113            S32XMemoryArea::SlaveSh2Cache => {
114                self.sh2_slave.debug_cache_view().write(address, value);
115            }
116            S32XMemoryArea::FrameBuffer0 => {
117                self.vdp.debug_frame_buffer_view(0).write(address, value);
118            }
119            S32XMemoryArea::FrameBuffer1 => {
120                self.vdp.debug_frame_buffer_view(1).write(address, value);
121            }
122            S32XMemoryArea::PaletteRam => {
123                self.vdp.debug_palette_ram_view().write(address, value);
124            }
125        }
126    }
127}
128
129impl Sega32X {
130    pub fn as_debug_view(&mut self) -> (Sega32XDebugView<'_>, Option<&mut Cartridge>) {
131        let debug_view = Sega32XDebugView {
132            sdram: self.bus.sdram.as_mut_slice(),
133            sh2_master: &mut self.sh2_master,
134            sh2_slave: &mut self.sh2_slave,
135            system_registers: &mut self.bus.registers,
136            vdp: &mut self.bus.vdp,
137            pwm: &mut self.bus.pwm,
138        };
139
140        let cartridge = self.bus.cartridge.as_mut();
141
142        (debug_view, cartridge)
143    }
144}
145
146pub trait Sega32XDebugger: 'static {
147    fn check_sh2_read_breakpoint(
148        &mut self,
149        which: WhichCpu,
150        address: u32,
151        size: OpSizeEnum,
152    ) -> bool;
153
154    fn check_sh2_write_breakpoint(
155        &mut self,
156        which: WhichCpu,
157        address: u32,
158        value: u32,
159        size: OpSizeEnum,
160    ) -> bool;
161
162    fn check_sh2_execute_breakpoint(&mut self, which: WhichCpu, pc: u32, opcode: u16) -> bool;
163
164    fn check_sh2_interrupt_breakpoint(&mut self, which: WhichCpu, interrupt_level: u8) -> bool;
165
166    fn handle_sh2_breakpoint(
167        &mut self,
168        which: WhichCpu,
169        cartridge: Option<&mut Cartridge>,
170        debug_view: Sega32XDebugView<'_>,
171    );
172}
173
174pub struct Dummy32XDebugger;
175
176#[allow(unused_variables)]
177impl Sega32XDebugger for Dummy32XDebugger {
178    #[inline(always)]
179    fn check_sh2_read_breakpoint(
180        &mut self,
181        which: WhichCpu,
182        address: u32,
183        size: OpSizeEnum,
184    ) -> bool {
185        false
186    }
187
188    #[inline(always)]
189    fn check_sh2_write_breakpoint(
190        &mut self,
191        which: WhichCpu,
192        address: u32,
193        value: u32,
194        size: OpSizeEnum,
195    ) -> bool {
196        false
197    }
198
199    #[inline(always)]
200    fn check_sh2_execute_breakpoint(&mut self, which: WhichCpu, pc: u32, opcode: u16) -> bool {
201        false
202    }
203
204    #[inline(always)]
205    fn check_sh2_interrupt_breakpoint(&mut self, which: WhichCpu, interrupt_level: u8) -> bool {
206        false
207    }
208
209    #[inline(always)]
210    fn handle_sh2_breakpoint(
211        &mut self,
212        which: WhichCpu,
213        cartridge: Option<&mut Cartridge>,
214        debug_view: Sega32XDebugView<'_>,
215    ) {
216    }
217}