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}