1//! A brief overview of the various structs involved here: 2//! 3//! * [`GenesisDebugState`] contains all data needed for the frontend to render the debugger UI, 4//! and it owns all of its data so it can be safely sent between threads 5//! 6//! * [`GenesisDebugView`] contains mutable references to everything that the backend needs 7//! to access in order to process debugger commands 8//! 9//! * [`GenesisDebugger`] stores current debugger backend state (e.g. breakpoints and current PCs) 10//! 11//! * [`GenesisDebugCommand`] is an enum of debugger commands that the UI can send to the backend 12//! 13//! [`GenesisDebugView`] does not have a mutable reference to [`GenesisEmulator`] itself 14//! because the emulator needs to construct a [`GenesisDebugView`] when a breakpoint is 15//! tripped, at which point the debugger code has access to component mutable references but not 16//! the full emulator struct. 17//! 18//! The backend can create a [`GenesisDebugView`] either from a [`GenesisEmulator`] value 19//! (done when processing debug commands between frames) or from mutable references to the individual 20//! components (done when handling a breakpoint). 21//! 22//! [`GenesisDebugView`] then has a method to create a [`GenesisDebugState`] from itself by 23//! cloning all of the data that [`GenesisDebugState`] needs to own. This is done once for frame 24//! during normal execution to periodically send updated emulator state to the UI, and it's done 25//! immediately upon handling a breakpoint so that the UI sees the emulator state at the exact 26//! point where the breakpoint tripped. 27//! 28//! [`GenesisDebuggerForSegaCd`] and [`s32x::GenesisDebuggerFor32X`] are structs that wrap a 29//! [`GenesisDebugger`] along with mutable references or pointers to components that are not on the 30//! named CPU's bus. The 32X version contains raw pointers due to the need to avoid putting lifetime 31//! parameters on the SH-2 bus struct, which requires [`s32x::GenesisDebuggerFor32X`] to have a static 32//! lifetime. 33 34pub mod s32x; 35 36#[cfg(test)] 37mod tests; 38 39use crate::GenesisEmulator; 40use crate::bus::PendingWrites; 41use genesis_components::cartridge::Cartridge; 42use genesis_components::vdp::Vdp; 43use genesis_components::vdp::debug::VdpDebugState; 44use genesis_components::ym2612::Ym2612; 45use jgenesis_common::debug::{ 46 DebugBytesView, DebugMemoryView, DebugWordsView, EmptyDebugView, Endian, 47}; 48use jgenesis_common::sync::SharedVarSender; 49use m68000_emu::M68000; 50use s32x_core::WhichCpu; 51use s32x_core::api::Sega32X; 52use segacd_core::api::debug::{ 53 SegaCdDebugState, SegaCdDebugView, SegaCdDebugger, SegaCdMemoryArea, 54}; 55use sh2_emu::bus::OpSizeEnum; 56use std::array; 57use std::ops::Deref; 58use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering}; 59use std::sync::mpsc::{Receiver, SendError, Sender, TryRecvError}; 60use std::sync::{Arc, mpsc}; 61use ti_sn76489::Sn76489; 62use z80_emu::Z80; 63 64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumAll)] 65pub enum GenesisMemoryArea { 66 CartridgeRom, 67 WorkingRam, 68 AudioRam, 69 Vram, 70 Cram, 71 Vsram, 72} 73 74#[derive(Debug, Clone, Copy)] 75pub enum DebugPendingWrite { 76 Word { address: u32, value: u16 }, 77 Byte { address: u32, value: u8 }, 78} 79 80#[derive(Debug, Clone)] 81pub struct GenesisDebugState { 82 pub sega_cd: Option<SegaCdDebugState>, 83 pub sega_32x: Option<Sega32XDebugState>, 84 pub m68k: M68000, 85 pub z80: Z80, 86 pub cartridge: Option<Cartridge>, 87 pub working_ram: Box<[u16]>, 88 pub audio_ram: Box<[u8]>, 89 pub z80_bank_number: u32, 90 pub pending_writes: Vec<DebugPendingWrite>, 91 pub vdp: VdpDebugState, 92 pub ym2612: Ym2612, 93 pub psg: Sn76489, 94} 95 96impl GenesisDebugState { 97 #[must_use] 98 pub fn memory_view(&mut self, memory_area: GenesisMemoryArea) -> Box<dyn DebugMemoryView + '_> { 99 match memory_area { 100 GenesisMemoryArea::CartridgeRom => match self.cartridge.as_mut() { 101 Some(cartridge) => { 102 Box::new(DebugWordsView(cartridge.debug_rom_view(), Endian::Big)) 103 } 104 None => Box::new(EmptyDebugView), 105 }, 106 GenesisMemoryArea::WorkingRam => { 107 Box::new(DebugWordsView(&mut self.working_ram, Endian::Big)) 108 } 109 GenesisMemoryArea::AudioRam => Box::new(DebugBytesView(&mut self.audio_ram)), 110 GenesisMemoryArea::Vram => Box::new(self.vdp.debug_vram_view()), 111 GenesisMemoryArea::Cram => Box::new(self.vdp.debug_cram_view()), 112 GenesisMemoryArea::Vsram => Box::new(self.vdp.debug_vsram_view()), 113 } 114 } 115} 116 117pub struct GenesisDebugView<'m68k, 'z80, 'genesis, 'scd, 's32x, 'cartridge> { 118 pub(crate) sega_cd: Option<SegaCdDebugView<'scd>>, 119 pub(crate) sega_32x: Option<Sega32XDebugView<'s32x>>, 120 pub(crate) m68k: &'m68k mut M68000, 121 pub(crate) z80: &'z80 mut Z80, 122 pub(crate) cartridge: Option<&'cartridge mut Cartridge>, 123 pub(crate) working_ram: &'genesis mut [u16], 124 pub(crate) audio_ram: &'genesis mut [u8], 125 pub(crate) z80_bank_number: u32, 126 pub(crate) pending_writes: &'genesis mut PendingWrites, 127 pub(crate) vdp: &'genesis mut Vdp, 128 pub(crate) ym2612: &'genesis mut Ym2612, 129 pub(crate) psg: &'genesis mut Sn76489, 130} 131 132impl GenesisDebugView<'_, '_, '_, '_, '_, '_> { 133 pub fn to_debug_state(&self) -> GenesisDebugState { 134 GenesisDebugState { 135 sega_cd: self.sega_cd.as_ref().map(SegaCdDebugView::to_debug_state), 136 sega_32x: self.sega_32x.as_ref().map(Sega32XDebugView::to_debug_state), 137 m68k: self.m68k.clone(), 138 z80: self.z80.clone(), 139 cartridge: self.cartridge.as_ref().map(|cartridge| cartridge.deref().clone()), 140 working_ram: self.working_ram.to_vec().into_boxed_slice(), 141 audio_ram: self.audio_ram.to_vec().into_boxed_slice(), 142 z80_bank_number: self.z80_bank_number, 143 pending_writes: self.pending_writes.to_debug_vec(), 144 vdp: self.vdp.to_debug_state(), 145 ym2612: self.ym2612.clone(), 146 psg: self.psg.clone(), 147 } 148 } 149 150 pub(crate) fn apply_memory_edit( 151 &mut self, 152 memory_area: GenesisMemoryArea, 153 address: usize, 154 value: u8, 155 ) { 156 match memory_area { 157 GenesisMemoryArea::CartridgeRom => { 158 if let Some(cartridge) = self.cartridge.as_mut() { 159 DebugWordsView(cartridge.debug_rom_view(), Endian::Big).write(address, value); 160 } 161 } 162 GenesisMemoryArea::WorkingRam => { 163 DebugWordsView(self.working_ram, Endian::Big).write(address, value); 164 } 165 GenesisMemoryArea::AudioRam => { 166 DebugBytesView(self.audio_ram).write(address, value); 167 } 168 GenesisMemoryArea::Vram => { 169 self.vdp.debug_vram_view().write(address, value); 170 } 171 GenesisMemoryArea::Cram => { 172 self.vdp.debug_cram_view().write(address, value); 173 } 174 GenesisMemoryArea::Vsram => { 175 self.vdp.debug_vsram_view().write(address, value); 176 } 177 } 178 } 179 180 pub(crate) fn apply_scd_memory_edit( 181 &mut self, 182 memory_area: SegaCdMemoryArea, 183 address: usize, 184 value: u8, 185 ) { 186 let Some(sega_cd) = &mut self.sega_cd else { return }; 187 sega_cd.apply_memory_edit(memory_area, address, value); 188 } 189 190 pub(crate) fn apply_32x_memory_edit( 191 &mut self, 192 memory_area: S32XMemoryArea, 193 address: usize, 194 value: u8, 195 ) { 196 let Some(sega_32x) = &mut self.sega_32x else { return }; 197 sega_32x.apply_memory_edit(memory_area, address, value); 198 } 199} 200 201impl GenesisEmulator { 202 pub fn as_debug_view(&mut self) -> GenesisDebugView<'_, '_, '_, '_, '_, '_> { 203 self.bus.as_debug_view(&mut self.m68k, &mut self.z80) 204 } 205} 206 207pub struct GenesisComponents<'genesis> { 208 pub(crate) working_ram: &'genesis mut [u16], 209 pub(crate) audio_ram: &'genesis mut [u8], 210 pub(crate) z80_bank_number: u32, 211 pub(crate) pending_writes: &'genesis mut PendingWrites, 212 pub(crate) vdp: &'genesis mut Vdp, 213 pub(crate) ym2612: &'genesis mut Ym2612, 214 pub(crate) psg: &'genesis mut Sn76489, 215} 216 217impl<'genesis> GenesisComponents<'genesis> { 218 pub fn as_debug_view<'m68k, 'z80, 'scd, 's32x, 'cartridge>( 219 &mut self, 220 m68k: &'m68k mut M68000, 221 z80: &'z80 mut Z80, 222 sega_cd: Option<SegaCdDebugView<'scd>>, 223 sega_32x: Option<Sega32XDebugView<'s32x>>, 224 cartridge: Option<&'cartridge mut Cartridge>, 225 ) -> GenesisDebugView<'m68k, 'z80, '_, 'scd, 's32x, 'cartridge> { 226 GenesisDebugView { 227 sega_cd, 228 sega_32x, 229 m68k, 230 z80, 231 cartridge, 232 working_ram: self.working_ram, 233 audio_ram: self.audio_ram, 234 z80_bank_number: self.z80_bank_number, 235 pending_writes: self.pending_writes, 236 vdp: self.vdp, 237 ym2612: self.ym2612, 238 psg: self.psg, 239 } 240 } 241 242 pub fn into_debug_view<'m68k, 'z80, 'scd, 's32x, 'cartridge>( 243 self, 244 m68k: &'m68k mut M68000, 245 z80: &'z80 mut Z80, 246 sega_cd: Option<SegaCdDebugView<'scd>>, 247 sega_32x: Option<Sega32XDebugView<'s32x>>, 248 cartridge: Option<&'cartridge mut Cartridge>, 249 ) -> GenesisDebugView<'m68k, 'z80, 'genesis, 'scd, 's32x, 'cartridge> { 250 GenesisDebugView { 251 sega_cd, 252 sega_32x, 253 m68k, 254 z80, 255 cartridge, 256 working_ram: self.working_ram, 257 audio_ram: self.audio_ram, 258 z80_bank_number: self.z80_bank_number, 259 pending_writes: self.pending_writes, 260 vdp: self.vdp, 261 ym2612: self.ym2612, 262 psg: self.psg, 263 } 264 } 265} 266 267// Macro so that it only borrows the needed GenesisBus fields 268macro_rules! genesis_components { 269 ($bus:expr) => {{ 270 let (working_ram, audio_ram) = $bus.memory.debug_ram_view(); 271 272 crate::api::debug::GenesisComponents { 273 working_ram, 274 audio_ram, 275 z80_bank_number: $bus.z80_bank.value(), 276 pending_writes: &mut $bus.pending_writes, 277 vdp: &mut $bus.vdp, 278 ym2612: &mut $bus.ym2612, 279 psg: &mut $bus.psg, 280 } 281 }}; 282} 283 284pub(crate) use genesis_components; 285use jgenesis_common::num::GetBit; 286use jgenesis_proc_macros::EnumAll; 287use s32x_core::api::debug::{S32XMemoryArea, Sega32XDebugState, Sega32XDebugView}; 288 289#[derive(Debug, Clone)] 290pub enum GenesisDebugCommand { 291 EditMemory(GenesisMemoryArea, usize, u8), 292 EditSegaCdMemory(SegaCdMemoryArea, usize, u8), 293 Edit32XMemory(S32XMemoryArea, usize, u8), 294 BreakResume, 295 BreakPause68k, 296 BreakStep68k, 297 BreakPauseZ80, 298 BreakStepZ80, 299 BreakPauseSub68k, 300 BreakStepSub68k, 301 BreakPauseSh2(WhichCpu), 302 BreakStepSh2(WhichCpu), 303 Update68kBreakpoints(M68000Breakpoints), 304 UpdateZ80Breakpoints(Vec<Z80Breakpoint>), 305 UpdateSub68kBreakpoints(M68000Breakpoints), 306 UpdateSh2Breakpoints(WhichCpu, Sh2Breakpoints), 307} 308 309#[derive(Debug, Clone, Copy, PartialEq, Eq)] 310pub struct M68000Breakpoint { 311 pub start_address: u32, 312 pub end_address: u32, 313 pub read: bool, 314 pub write: bool, 315 pub execute: bool, 316} 317 318#[derive(Debug, Clone, PartialEq, Eq)] 319pub struct M68000Breakpoints { 320 pub memory: Vec<M68000Breakpoint>, 321 pub interrupt: Vec<u8>, 322} 323 324impl M68000Breakpoints { 325 #[must_use] 326 pub fn new() -> Self { 327 Self { memory: vec![], interrupt: vec![] } 328 } 329} 330 331impl Default for M68000Breakpoints { 332 fn default() -> Self { 333 Self::new() 334 } 335} 336 337#[derive(Debug, Clone)] 338pub struct M68000BreakpointsParsed { 339 read_byte: Vec<(u32, u32)>, 340 read_word: Vec<(u32, u32)>, 341 write_byte: Vec<(u32, u32)>, 342 write_word: Vec<(u32, u32)>, 343 execute: Vec<(u32, u32)>, 344 interrupt_bitset: u8, 345} 346 347impl M68000BreakpointsParsed { 348 #[must_use] 349 pub fn new(breakpoints: &M68000Breakpoints) -> Self { 350 let mut read_byte = Vec::new(); 351 let mut read_word = Vec::new(); 352 let mut write_byte = Vec::new(); 353 let mut write_word = Vec::new(); 354 let mut execute = Vec::new(); 355 356 for &breakpoint in &breakpoints.memory { 357 if breakpoint.read { 358 read_byte.push((breakpoint.start_address, breakpoint.end_address)); 359 read_word.push((breakpoint.start_address & !1, breakpoint.end_address & !1)); 360 } 361 362 if breakpoint.write { 363 write_byte.push((breakpoint.start_address, breakpoint.end_address)); 364 write_word.push((breakpoint.start_address & !1, breakpoint.end_address & !1)); 365 } 366 367 if breakpoint.execute { 368 execute.push((breakpoint.start_address & !1, breakpoint.end_address & !1)); 369 } 370 } 371 372 let interrupt_bitset = breakpoints 373 .interrupt 374 .iter() 375 .map(|&interrupt_level| 1 << (interrupt_level & 7)) 376 .fold(0, |a, b| a | b); 377 378 Self { read_byte, read_word, write_byte, write_word, execute, interrupt_bitset } 379 } 380 381 #[must_use] 382 pub fn none() -> Self { 383 Self::new(&M68000Breakpoints::new()) 384 } 385 386 #[must_use] 387 pub fn check_read<const WORD: bool>(&self, address: u32) -> bool { 388 let ranges = if WORD { &self.read_word } else { &self.read_byte }; 389 ranges.iter().any(|&(start, end)| (start..=end).contains(&address)) 390 } 391 392 #[must_use] 393 pub fn check_write<const WORD: bool>(&self, address: u32) -> bool { 394 let ranges = if WORD { &self.write_word } else { &self.write_byte }; 395 ranges.iter().any(|&(start, end)| (start..=end).contains(&address)) 396 } 397 398 #[must_use] 399 pub fn check_interrupt(&self, interrupt_level: u8) -> bool { 400 self.interrupt_bitset.bit(interrupt_level & 7) 401 } 402 403 #[must_use] 404 pub fn check_execute(&self, address: u32) -> bool { 405 self.execute.iter().any(|&(start, end)| (start..=end).contains(&address)) 406 } 407} 408 409const PREV_PC_COUNT: usize = 10; 410 411#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 412pub struct M68000BreakStatus { 413 pub breaking: bool, 414 pub pc: u32, 415 pub previous_pcs: [u32; PREV_PC_COUNT], 416} 417 418pub struct M68000BreakStatusAtomic { 419 pub breaking: AtomicBool, 420 pub pc: AtomicU32, 421 pub previous_pcs: [AtomicU32; PREV_PC_COUNT], 422} 423 424impl M68000BreakStatusAtomic { 425 #[must_use] 426 pub fn new() -> Self { 427 Self { 428 breaking: AtomicBool::new(false), 429 pc: AtomicU32::new(0), 430 previous_pcs: array::from_fn(|_| AtomicU32::new(0)), 431 } 432 } 433 434 #[must_use] 435 pub fn get(&self) -> M68000BreakStatus { 436 let breaking = self.breaking.load(Ordering::Acquire); 437 let pc = self.pc.load(Ordering::Relaxed); 438 let previous_pcs = array::from_fn(|i| self.previous_pcs[i].load(Ordering::Relaxed)); 439 440 M68000BreakStatus { breaking, pc, previous_pcs } 441 } 442 443 pub fn set_breaking(&self, pcs_rev_iter: impl Iterator<Item = u32>) { 444 for (in_pc, out_pc) in pcs_rev_iter.zip(&self.previous_pcs) { 445 out_pc.store(in_pc, Ordering::Relaxed); 446 } 447 self.pc.store(self.previous_pcs[0].load(Ordering::Relaxed), Ordering::Relaxed); 448 449 self.breaking.store(true, Ordering::Release); 450 } 451 452 pub fn clear_breaking(&self) { 453 self.breaking.store(false, Ordering::Release); 454 } 455} 456 457impl Default for M68000BreakStatusAtomic { 458 fn default() -> Self { 459 Self::new() 460 } 461} 462 463pub struct RingBuffer<T, const N: usize> { 464 values: [T; N], 465 write_ptr: usize, 466} 467 468impl<T: Copy + Default, const N: usize> RingBuffer<T, N> { 469 #[must_use] 470 pub fn new() -> Self { 471 Self { values: array::from_fn(|_| T::default()), write_ptr: 0 } 472 } 473 474 pub fn write(&mut self, value: T) { 475 self.values[self.write_ptr] = value; 476 self.write_ptr = (self.write_ptr + 1) % N; 477 } 478 479 #[must_use] 480 pub fn last(&self) -> T { 481 let read_ptr = Self::ptr_minus_one(self.write_ptr); 482 self.values[read_ptr] 483 } 484 485 pub fn reverse_iter(&self) -> impl Iterator<Item = T> { 486 RingBufferIter { buffer: self, ptr: Self::ptr_minus_one(self.write_ptr), remaining: N } 487 } 488 489 fn ptr_minus_one(ptr: usize) -> usize { 490 if ptr == 0 { N - 1 } else { ptr - 1 } 491 } 492} 493 494impl<T: Copy + Default, const N: usize> Default for RingBuffer<T, N> { 495 fn default() -> Self { 496 Self::new() 497 } 498} 499 500struct RingBufferIter<'a, T, const N: usize> { 501 buffer: &'a RingBuffer<T, N>, 502 ptr: usize, 503 remaining: usize, 504} 505 506impl<T: Copy + Default, const N: usize> Iterator for RingBufferIter<'_, T, N> { 507 type Item = T; 508 509 fn next(&mut self) -> Option<Self::Item> { 510 if self.remaining == 0 { 511 return None; 512 } 513 514 let value = self.buffer.values[self.ptr]; 515 516 self.ptr = RingBuffer::<T, N>::ptr_minus_one(self.ptr); 517 self.remaining -= 1; 518 519 Some(value) 520 } 521 522 fn size_hint(&self) -> (usize, Option<usize>) { 523 (self.remaining, Some(self.remaining)) 524 } 525} 526 527pub struct M68000BreakpointManager { 528 pub breakpoints: M68000BreakpointsParsed, 529 pub last_pcs: RingBuffer<u32, PREV_PC_COUNT>, 530 pub status: Arc<M68000BreakStatusAtomic>, 531 pub step: Option<u32>, 532} 533 534impl M68000BreakpointManager { 535 #[must_use] 536 pub fn new() -> Self { 537 Self { 538 breakpoints: M68000BreakpointsParsed::none(), 539 last_pcs: RingBuffer::new(), 540 status: Arc::new(M68000BreakStatusAtomic::new()), 541 step: None, 542 } 543 } 544 545 pub fn set_break_status(&self) { 546 self.status.set_breaking(self.last_pcs.reverse_iter()); 547 } 548 549 pub fn clear_break_status(&self) { 550 self.status.clear_breaking(); 551 } 552 553 pub fn clear(&mut self) { 554 self.breakpoints = M68000BreakpointsParsed::none(); 555 self.step = None; 556 } 557 558 #[must_use] 559 pub fn check_read<const WORD: bool>(&self, address: u32) -> bool { 560 self.breakpoints.check_read::<WORD>(address) 561 } 562 563 #[must_use] 564 pub fn check_write<const WORD: bool>(&self, address: u32) -> bool { 565 self.breakpoints.check_write::<WORD>(address) 566 } 567 568 #[must_use] 569 pub fn check_interrupt(&self, interrupt_level: u8) -> bool { 570 self.breakpoints.check_interrupt(interrupt_level) 571 } 572 573 #[must_use] 574 pub fn update_pc_and_check_execute(&mut self, pc: u32) -> bool { 575 self.last_pcs.write(pc); 576 self.breakpoints.check_execute(pc) 577 } 578 579 #[must_use] 580 pub fn check_break_step(&mut self) -> bool { 581 check_break_step(&mut self.step) 582 } 583} 584 585fn check_break_step(step: &mut Option<u32>) -> bool { 586 let Some(remaining) = step else { return false }; 587 588 *remaining -= 1; 589 if *remaining == 0 { 590 *step = None; 591 true 592 } else { 593 false 594 } 595} 596 597impl Default for M68000BreakpointManager { 598 fn default() -> Self { 599 Self::new() 600 } 601} 602 603#[derive(Debug, Clone, Copy, PartialEq, Eq)] 604pub struct Z80Breakpoint { 605 pub start_address: u16, 606 pub end_address: u16, 607 pub read: bool, 608 pub write: bool, 609 pub execute: bool, 610} 611 612pub struct Z80Breakpoints { 613 read: Vec<(u16, u16)>, 614 write: Vec<(u16, u16)>, 615 execute: Vec<(u16, u16)>, 616} 617 618impl Z80Breakpoints { 619 #[must_use] 620 pub fn new(breakpoints: &[Z80Breakpoint]) -> Self { 621 let mut read = Vec::new(); 622 let mut write = Vec::new(); 623 let mut execute = Vec::new(); 624 625 for &breakpoint in breakpoints { 626 if breakpoint.read { 627 read.push((breakpoint.start_address, breakpoint.end_address)); 628 } 629 630 if breakpoint.write { 631 write.push((breakpoint.start_address, breakpoint.end_address)); 632 } 633 634 if breakpoint.execute { 635 execute.push((breakpoint.start_address, breakpoint.end_address)); 636 } 637 } 638 639 Self { read, write, execute } 640 } 641 642 #[must_use] 643 pub fn none() -> Self { 644 Self::new(&[]) 645 } 646 647 #[must_use] 648 pub fn check_read(&self, address: u16) -> bool { 649 self.read.iter().any(|&(start, end)| (start..=end).contains(&address)) 650 } 651 652 #[must_use] 653 pub fn check_write(&self, address: u16) -> bool { 654 self.write.iter().any(|&(start, end)| (start..=end).contains(&address)) 655 } 656 657 #[must_use] 658 pub fn check_execute(&self, address: u16) -> bool { 659 self.execute.iter().any(|&(start, end)| (start..=end).contains(&address)) 660 } 661} 662 663#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 664pub struct Z80BreakStatus { 665 pub breaking: bool, 666 pub pc: u16, 667 pub previous_pcs: [u16; PREV_PC_COUNT], 668} 669 670pub struct Z80BreakStatusAtomic { 671 pub breaking: AtomicBool, 672 pub pc: AtomicU16, 673 pub previous_pcs: [AtomicU16; PREV_PC_COUNT], 674} 675 676impl Z80BreakStatusAtomic { 677 #[must_use] 678 pub fn new() -> Self { 679 Self { 680 breaking: AtomicBool::new(false), 681 pc: AtomicU16::new(0), 682 previous_pcs: array::from_fn(|_| AtomicU16::new(0)), 683 } 684 } 685 686 #[must_use] 687 pub fn get(&self) -> Z80BreakStatus { 688 let breaking = self.breaking.load(Ordering::Acquire); 689 let pc = self.pc.load(Ordering::Relaxed); 690 let previous_pcs = array::from_fn(|i| self.previous_pcs[i].load(Ordering::Relaxed)); 691 692 Z80BreakStatus { breaking, pc, previous_pcs } 693 } 694 695 pub fn set_breaking(&self, pcs_rev_iter: impl Iterator<Item = u16>) { 696 for (in_pc, out_pc) in pcs_rev_iter.zip(&self.previous_pcs) { 697 out_pc.store(in_pc, Ordering::Relaxed); 698 } 699 self.pc.store(self.previous_pcs[0].load(Ordering::Relaxed), Ordering::Relaxed); 700 701 self.breaking.store(true, Ordering::Release); 702 } 703 704 pub fn clear_breaking(&self) { 705 self.breaking.store(false, Ordering::Release); 706 } 707} 708 709impl Default for Z80BreakStatusAtomic { 710 fn default() -> Self { 711 Self::new() 712 } 713} 714 715pub struct Z80BreakpointManager { 716 pub breakpoints: Z80Breakpoints, 717 pub status: Arc<Z80BreakStatusAtomic>, 718 pub previous_pcs: RingBuffer<u16, PREV_PC_COUNT>, 719 pub step: Option<u32>, 720} 721 722impl Z80BreakpointManager { 723 #[must_use] 724 pub fn new() -> Self { 725 Self { 726 breakpoints: Z80Breakpoints::none(), 727 status: Arc::new(Z80BreakStatusAtomic::new()), 728 previous_pcs: RingBuffer::new(), 729 step: None, 730 } 731 } 732 733 pub fn set_break_status(&self) { 734 self.status.set_breaking(self.previous_pcs.reverse_iter()); 735 } 736 737 pub fn clear_break_status(&self) { 738 self.status.clear_breaking(); 739 } 740 741 pub fn clear(&mut self) { 742 self.breakpoints = Z80Breakpoints::none(); 743 self.step = None; 744 } 745 746 #[must_use] 747 pub fn check_read(&self, address: u16) -> bool { 748 self.breakpoints.check_read(address) 749 } 750 751 #[must_use] 752 pub fn check_write(&self, address: u16) -> bool { 753 self.breakpoints.check_write(address) 754 } 755 756 #[must_use] 757 pub fn update_pc_and_check_execute(&mut self, pc: u16) -> bool { 758 self.previous_pcs.write(pc); 759 self.breakpoints.check_execute(pc) 760 } 761 762 #[must_use] 763 pub fn check_break_step(&mut self) -> bool { 764 check_break_step(&mut self.step) 765 } 766} 767 768impl Default for Z80BreakpointManager { 769 fn default() -> Self { 770 Self::new() 771 } 772} 773 774#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 775pub struct Sh2BreakStatus { 776 pub breaking: bool, 777 pub pc: u32, 778} 779 780#[derive(Debug, Clone, Copy)] 781pub struct S32XSh2BreakStatus { 782 pub master: Sh2BreakStatus, 783 pub slave: Sh2BreakStatus, 784} 785 786impl S32XSh2BreakStatus { 787 #[must_use] 788 pub fn get(&self, which: WhichCpu) -> Sh2BreakStatus { 789 match which { 790 WhichCpu::Master => self.master, 791 WhichCpu::Slave => self.slave, 792 } 793 } 794} 795 796pub struct Sh2BreakStatusAtomic { 797 pub breaking: [AtomicBool; 2], 798 pub break_pc: [AtomicU32; 2], 799} 800 801impl Sh2BreakStatusAtomic { 802 fn new() -> Self { 803 Self { 804 breaking: array::from_fn(|_| AtomicBool::new(false)), 805 break_pc: array::from_fn(|_| AtomicU32::new(0)), 806 } 807 } 808} 809 810#[derive(Debug, Clone, Copy, PartialEq, Eq)] 811pub struct Sh2Breakpoint { 812 pub start_address: u32, 813 pub end_address: u32, 814 pub read: bool, 815 pub write: bool, 816 pub execute: bool, 817} 818 819#[derive(Debug, Clone, PartialEq, Eq, Default)] 820pub struct Sh2Breakpoints { 821 pub memory: Vec<Sh2Breakpoint>, 822 pub interrupt: Vec<u8>, 823} 824 825#[derive(Debug, Clone)] 826pub(crate) struct Sh2BreakpointsParsed { 827 read_byte: Vec<(u32, u32)>, 828 read_word: Vec<(u32, u32)>, 829 read_longword: Vec<(u32, u32)>, 830 write_byte: Vec<(u32, u32)>, 831 write_word: Vec<(u32, u32)>, 832 write_longword: Vec<(u32, u32)>, 833 execute: Vec<(u32, u32)>, 834 interrupt_bitset: u16, 835} 836 837impl Sh2BreakpointsParsed { 838 #[must_use] 839 pub fn new(breakpoints: &Sh2Breakpoints) -> Self { 840 let mut read_byte = Vec::new(); 841 let mut read_word = Vec::new(); 842 let mut read_longword = Vec::new(); 843 let mut write_byte = Vec::new(); 844 let mut write_word = Vec::new(); 845 let mut write_longword = Vec::new(); 846 let mut execute = Vec::new(); 847 848 for &breakpoint in &breakpoints.memory { 849 if breakpoint.read { 850 read_byte.push((breakpoint.start_address, breakpoint.end_address)); 851 read_word.push((breakpoint.start_address & !1, breakpoint.end_address & !1)); 852 read_longword.push((breakpoint.start_address & !3, breakpoint.end_address & !3)); 853 } 854 855 if breakpoint.write { 856 write_byte.push((breakpoint.start_address, breakpoint.end_address)); 857 write_word.push((breakpoint.start_address & !1, breakpoint.end_address & !1)); 858 write_longword.push((breakpoint.start_address & !3, breakpoint.end_address & !4)); 859 } 860 861 if breakpoint.execute { 862 execute.push((breakpoint.start_address & !1, breakpoint.end_address & !1)); 863 } 864 } 865 866 let interrupt_bitset = 867 breakpoints.interrupt.iter().map(|&level| 1 << (level & 15)).fold(0, |a, b| a | b); 868 869 Self { 870 read_byte, 871 read_word, 872 read_longword, 873 write_byte, 874 write_word, 875 write_longword, 876 execute, 877 interrupt_bitset, 878 } 879 } 880 881 #[must_use] 882 pub fn none() -> Self { 883 Self::new(&Sh2Breakpoints::default()) 884 } 885 886 #[must_use] 887 #[allow(clippy::missing_panics_doc)] 888 pub fn should_break_read(&self, address: u32, size: OpSizeEnum) -> bool { 889 let addresses = match size { 890 OpSizeEnum::Byte => &self.read_byte, 891 OpSizeEnum::Word => &self.read_word, 892 OpSizeEnum::Longword => &self.read_longword, 893 }; 894 addresses.iter().any(|&(start, end)| (start..=end).contains(&address)) 895 } 896 897 #[must_use] 898 #[allow(clippy::missing_panics_doc)] 899 pub fn should_break_write(&self, address: u32, size: OpSizeEnum) -> bool { 900 let addresses = match size { 901 OpSizeEnum::Byte => &self.write_byte, 902 OpSizeEnum::Word => &self.write_word, 903 OpSizeEnum::Longword => &self.write_longword, 904 }; 905 addresses.iter().any(|&(start, end)| (start..=end).contains(&address)) 906 } 907 908 #[must_use] 909 pub fn should_break_execute(&self, address: u32) -> bool { 910 self.execute.iter().any(|&(start, end)| (start..=end).contains(&address)) 911 } 912 913 #[must_use] 914 pub fn should_break_interrupt(&self, interrupt_level: u8) -> bool { 915 self.interrupt_bitset.bit(interrupt_level & 15) 916 } 917} 918 919struct Sh2BreakpointsManager { 920 breakpoints: [Sh2BreakpointsParsed; 2], 921 status: Arc<Sh2BreakStatusAtomic>, 922 step: Option<(WhichCpu, u32)>, 923} 924 925impl Sh2BreakpointsManager { 926 fn new() -> Self { 927 Self { 928 breakpoints: array::from_fn(|_| Sh2BreakpointsParsed::none()), 929 status: Arc::new(Sh2BreakStatusAtomic::new()), 930 step: None, 931 } 932 } 933 934 fn clear(&mut self) { 935 self.breakpoints.fill_with(Sh2BreakpointsParsed::none); 936 self.step = None; 937 } 938 939 fn set_break_status(&mut self, which: WhichCpu) { 940 self.status.breaking[which as usize].store(true, Ordering::Release); 941 } 942 943 fn clear_break_status(&mut self, which: WhichCpu) { 944 self.status.breaking[which as usize].store(false, Ordering::Release); 945 } 946 947 fn update_pc_and_check_execute(&mut self, which: WhichCpu, pc: u32) -> bool { 948 self.status.break_pc[which as usize].store(pc, Ordering::Relaxed); 949 self.breakpoints[which as usize].should_break_execute(pc) 950 } 951 952 fn check_break_step(&mut self, which: WhichCpu) -> bool { 953 let Some((step_which, step)) = &mut self.step else { return false }; 954 955 if *step_which != which { 956 return false; 957 } 958 959 *step -= 1; 960 if *step == 0 { 961 self.step = None; 962 true 963 } else { 964 false 965 } 966 } 967} 968 969#[derive(Debug, Clone, Copy, PartialEq, Eq)] 970pub(crate) enum GenesisCpu { 971 M68k, 972 Z80, 973 Sub68k, 974 Sh2(WhichCpu), 975} 976 977pub struct GenesisDebugger { 978 command_receiver: Receiver<GenesisDebugCommand>, 979 state_sender: SharedVarSender<GenesisDebugState>, 980 m68k_breakpoints: M68000BreakpointManager, 981 z80_breakpoints: Z80BreakpointManager, 982 sub_68k_breakpoints: M68000BreakpointManager, 983 sh2_breakpoints: Sh2BreakpointsManager, 984} 985 986pub struct GenesisDebuggerHandle { 987 pub command_sender: Sender<GenesisDebugCommand>, 988 pub m68k_break_status: Arc<M68000BreakStatusAtomic>, 989 pub z80_break_status: Arc<Z80BreakStatusAtomic>, 990 pub sub_cpu_break_status: Arc<M68000BreakStatusAtomic>, 991 pub sh2_break_status: Arc<Sh2BreakStatusAtomic>, 992} 993 994impl GenesisDebugger { 995 #[must_use] 996 pub fn new(state_sender: SharedVarSender<GenesisDebugState>) -> (Self, GenesisDebuggerHandle) { 997 let (command_sender, command_receiver) = mpsc::channel(); 998 999 let debugger = Self { 1000 command_receiver, 1001 state_sender, 1002 m68k_breakpoints: M68000BreakpointManager::new(), 1003 z80_breakpoints: Z80BreakpointManager::new(), 1004 sub_68k_breakpoints: M68000BreakpointManager::new(), 1005 sh2_breakpoints: Sh2BreakpointsManager::new(), 1006 }; 1007 1008 let handle = GenesisDebuggerHandle { 1009 command_sender, 1010 m68k_break_status: Arc::clone(&debugger.m68k_breakpoints.status), 1011 z80_break_status: Arc::clone(&debugger.z80_breakpoints.status), 1012 sub_cpu_break_status: Arc::clone(&debugger.sub_68k_breakpoints.status), 1013 sh2_break_status: Arc::clone(&debugger.sh2_breakpoints.status), 1014 }; 1015 1016 (debugger, handle) 1017 } 1018 1019 pub fn process_commands(&mut self, debug_view: &mut GenesisDebugView<'_, '_, '_, '_, '_, '_>) { 1020 loop { 1021 match self.command_receiver.try_recv() { 1022 Ok(command) => self.process_command(command, debug_view), 1023 Err(TryRecvError::Empty) => break, 1024 Err(TryRecvError::Disconnected) => { 1025 self.m68k_breakpoints.clear(); 1026 self.z80_breakpoints.clear(); 1027 self.sub_68k_breakpoints.clear(); 1028 self.sh2_breakpoints.clear(); 1029 break; 1030 } 1031 } 1032 } 1033 } 1034 1035 pub fn process_command( 1036 &mut self, 1037 command: GenesisDebugCommand, 1038 debug_view: &mut GenesisDebugView<'_, '_, '_, '_, '_, '_>, 1039 ) { 1040 match command { 1041 GenesisDebugCommand::EditMemory(memory_area, address, value) => { 1042 debug_view.apply_memory_edit(memory_area, address, value); 1043 } 1044 GenesisDebugCommand::EditSegaCdMemory(memory_area, address, value) => { 1045 debug_view.apply_scd_memory_edit(memory_area, address, value); 1046 } 1047 GenesisDebugCommand::Edit32XMemory(memory_area, address, value) => { 1048 debug_view.apply_32x_memory_edit(memory_area, address, value); 1049 } 1050 GenesisDebugCommand::Update68kBreakpoints(breakpoints) => { 1051 self.m68k_breakpoints.breakpoints = M68000BreakpointsParsed::new(&breakpoints); 1052 } 1053 GenesisDebugCommand::UpdateZ80Breakpoints(breakpoints) => { 1054 self.z80_breakpoints.breakpoints = Z80Breakpoints::new(&breakpoints); 1055 } 1056 GenesisDebugCommand::UpdateSub68kBreakpoints(breakpoints) => { 1057 self.sub_68k_breakpoints.breakpoints = M68000BreakpointsParsed::new(&breakpoints); 1058 } 1059 GenesisDebugCommand::UpdateSh2Breakpoints(which, breakpoints) => { 1060 self.sh2_breakpoints.breakpoints[which as usize] = 1061 Sh2BreakpointsParsed::new(&breakpoints); 1062 } 1063 GenesisDebugCommand::BreakPause68k => { 1064 self.m68k_breakpoints.step = Some(1); 1065 } 1066 GenesisDebugCommand::BreakPauseZ80 => { 1067 self.z80_breakpoints.step = Some(1); 1068 } 1069 GenesisDebugCommand::BreakPauseSub68k => { 1070 self.sub_68k_breakpoints.step = Some(1); 1071 } 1072 GenesisDebugCommand::BreakPauseSh2(which) => { 1073 self.sh2_breakpoints.step = Some((which, 1)); 1074 } 1075 GenesisDebugCommand::BreakResume 1076 | GenesisDebugCommand::BreakStep68k 1077 | GenesisDebugCommand::BreakStepZ80 1078 | GenesisDebugCommand::BreakStepSub68k 1079 | GenesisDebugCommand::BreakStepSh2(_) => {} 1080 } 1081 } 1082 1083 pub(crate) fn handle_breakpoint( 1084 &mut self, 1085 which: GenesisCpu, 1086 debug_view: &mut GenesisDebugView<'_, '_, '_, '_, '_, '_>, 1087 ) { 1088 self.state_sender.update(debug_view.to_debug_state()); 1089 1090 match which { 1091 GenesisCpu::M68k => { 1092 self.m68k_breakpoints.set_break_status(); 1093 } 1094 GenesisCpu::Z80 => { 1095 self.z80_breakpoints.set_break_status(); 1096 } 1097 GenesisCpu::Sub68k => { 1098 self.sub_68k_breakpoints.set_break_status(); 1099 } 1100 GenesisCpu::Sh2(which) => { 1101 self.sh2_breakpoints.set_break_status(which); 1102 } 1103 } 1104 1105 self.m68k_breakpoints.step = None; 1106 self.z80_breakpoints.step = None; 1107 self.sub_68k_breakpoints.step = None; 1108 self.sh2_breakpoints.step = None; 1109 1110 loop { 1111 match self.command_receiver.recv() { 1112 Ok(GenesisDebugCommand::BreakResume) => break, 1113 Ok(GenesisDebugCommand::BreakStep68k) => { 1114 self.m68k_breakpoints.step = Some(1 + u32::from(which != GenesisCpu::M68k)); 1115 break; 1116 } 1117 Ok(GenesisDebugCommand::BreakStepZ80) => { 1118 self.z80_breakpoints.step = Some(1 + u32::from(which != GenesisCpu::Z80)); 1119 break; 1120 } 1121 Ok(GenesisDebugCommand::BreakStepSub68k) => { 1122 self.sub_68k_breakpoints.step = 1123 Some(1 + u32::from(which != GenesisCpu::Sub68k)); 1124 break; 1125 } 1126 Ok(GenesisDebugCommand::BreakStepSh2(sh2_which)) => { 1127 self.sh2_breakpoints.step = 1128 Some((sh2_which, 1 + u32::from(which != GenesisCpu::Sh2(sh2_which)))); 1129 break; 1130 } 1131 Ok(command) => self.process_command(command, debug_view), 1132 Err(_) => { 1133 // Debugger window closed 1134 self.m68k_breakpoints.clear(); 1135 self.z80_breakpoints.clear(); 1136 self.sub_68k_breakpoints.clear(); 1137 self.sh2_breakpoints.clear(); 1138 break; 1139 } 1140 } 1141 } 1142 1143 match which { 1144 GenesisCpu::M68k => { 1145 self.m68k_breakpoints.clear_break_status(); 1146 } 1147 GenesisCpu::Z80 => { 1148 self.z80_breakpoints.clear_break_status(); 1149 } 1150 GenesisCpu::Sub68k => { 1151 self.sub_68k_breakpoints.clear_break_status(); 1152 } 1153 GenesisCpu::Sh2(which) => { 1154 self.sh2_breakpoints.clear_break_status(which); 1155 } 1156 } 1157 } 1158 1159 pub(crate) fn m68k_breakpoints(&mut self) -> &mut M68000BreakpointManager { 1160 &mut self.m68k_breakpoints 1161 } 1162 1163 pub(crate) fn z80_breakpoints(&mut self) -> &mut Z80BreakpointManager { 1164 &mut self.z80_breakpoints 1165 } 1166 1167 pub(crate) fn with_cpus<'cpus>( 1168 &mut self, 1169 m68k: &'cpus mut M68000, 1170 z80: &'cpus mut Z80, 1171 ) -> GenesisDebuggerWithCpus<'_, 'cpus> { 1172 GenesisDebuggerWithCpus { debugger: self, m68k, z80 } 1173 } 1174} 1175 1176impl GenesisDebuggerHandle { 1177 /// # Errors 1178 /// 1179 /// Returns an error if the debugger backend has been closed. 1180 pub fn send_command( 1181 &self, 1182 command: GenesisDebugCommand, 1183 ) -> Result<(), SendError<GenesisDebugCommand>> { 1184 self.command_sender.send(command) 1185 } 1186 1187 #[must_use] 1188 pub fn m68k_break_status(&self) -> M68000BreakStatus { 1189 self.m68k_break_status.get() 1190 } 1191 1192 #[must_use] 1193 pub fn z80_break_status(&self) -> Z80BreakStatus { 1194 self.z80_break_status.get() 1195 } 1196 1197 #[must_use] 1198 pub fn sub_cpu_break_status(&self) -> M68000BreakStatus { 1199 self.sub_cpu_break_status.get() 1200 } 1201 1202 #[must_use] 1203 pub fn sh2_break_status(&self) -> S32XSh2BreakStatus { 1204 let master = self.sh2_break_status_one(WhichCpu::Master); 1205 let slave = self.sh2_break_status_one(WhichCpu::Slave); 1206 1207 S32XSh2BreakStatus { master, slave } 1208 } 1209 1210 fn sh2_break_status_one(&self, which: WhichCpu) -> Sh2BreakStatus { 1211 let break_idx = which as usize; 1212 let breaking = self.sh2_break_status.breaking[break_idx].load(Ordering::Acquire); 1213 let pc = self.sh2_break_status.break_pc[break_idx].load(Ordering::Relaxed); 1214 Sh2BreakStatus { breaking, pc } 1215 } 1216} 1217 1218pub(crate) struct GenesisDebuggerWithCpus<'debugger, 'cpus> { 1219 debugger: &'debugger mut GenesisDebugger, 1220 m68k: &'cpus mut M68000, 1221 z80: &'cpus mut Z80, 1222} 1223 1224pub(crate) struct GenesisDebuggerForSegaCd<'debugger, 'genesis, 's32x> { 1225 debugger: &'debugger mut GenesisDebugger, 1226 m68k: &'debugger mut M68000, 1227 z80: &'debugger mut Z80, 1228 sega_32x: Option<&'s32x mut Sega32X>, 1229 genesis_components: GenesisComponents<'genesis>, 1230 cartridge: Option<&'genesis mut Cartridge>, 1231} 1232 1233impl GenesisDebuggerWithCpus<'_, '_> { 1234 pub(crate) fn for_sega_cd<'genesis, 's32x>( 1235 &mut self, 1236 sega_32x: Option<&'s32x mut Sega32X>, 1237 genesis_components: GenesisComponents<'genesis>, 1238 cartridge: Option<&'genesis mut Cartridge>, 1239 ) -> GenesisDebuggerForSegaCd<'_, 'genesis, 's32x> { 1240 GenesisDebuggerForSegaCd { 1241 debugger: self.debugger, 1242 m68k: self.m68k, 1243 z80: self.z80, 1244 sega_32x, 1245 genesis_components, 1246 cartridge, 1247 } 1248 } 1249} 1250 1251impl SegaCdDebugger for GenesisDebuggerForSegaCd<'_, '_, '_> { 1252 fn check_sub_read_breakpoint<const WORD: bool>(&mut self, address: u32) -> bool { 1253 self.debugger.sub_68k_breakpoints.check_read::<WORD>(address) 1254 } 1255 1256 fn check_sub_write_breakpoint<const WORD: bool>(&mut self, address: u32, _value: u16) -> bool { 1257 self.debugger.sub_68k_breakpoints.check_write::<WORD>(address) 1258 } 1259 1260 fn check_sub_execute_breakpoint(&mut self, pc: u32) -> bool { 1261 let execute = self.debugger.sub_68k_breakpoints.update_pc_and_check_execute(pc); 1262 let step = self.debugger.sub_68k_breakpoints.check_break_step(); 1263 1264 execute || step 1265 } 1266 1267 fn check_sub_interrupt_breakpoint(&mut self, interrupt_level: u8) -> bool { 1268 self.debugger.sub_68k_breakpoints.check_interrupt(interrupt_level) 1269 } 1270 1271 fn handle_sub_breakpoint(&mut self, debug_view: SegaCdDebugView<'_>) { 1272 let (s32x_debug_view, s32x_cartridge) = 1273 match self.sega_32x.as_mut().map(|sega_32x| sega_32x.as_debug_view()) { 1274 Some((debug_view, cartridge)) => (Some(debug_view), cartridge), 1275 None => (None, None), 1276 }; 1277 let cartridge = s32x_cartridge.or(self.cartridge.as_deref_mut()); 1278 1279 let mut genesis_debug_view = self.genesis_components.as_debug_view( 1280 self.m68k, 1281 self.z80, 1282 Some(debug_view), 1283 s32x_debug_view, 1284 cartridge, 1285 ); 1286 self.debugger.handle_breakpoint(GenesisCpu::Sub68k, &mut genesis_debug_view); 1287 } 1288}