debug.rsannotateddebug.rssource1288 lines · 39.9 KB · raw

A brief overview of the various structs involved here:

  • [GenesisDebugState] contains all data needed for the frontend to render the debugger UI, and it owns all of its data so it can be safely sent between threads

  • [GenesisDebugView] contains mutable references to everything that the backend needs to access in order to process debugger commands

  • [GenesisDebugger] stores current debugger backend state (e.g. breakpoints and current PCs)

  • [GenesisDebugCommand] is an enum of debugger commands that the UI can send to the backend

[GenesisDebugView] does not have a mutable reference to [GenesisEmulator] itself because the emulator needs to construct a [GenesisDebugView] when a breakpoint is tripped, at which point the debugger code has access to component mutable references but not the full emulator struct.

The backend can create a [GenesisDebugView] either from a [GenesisEmulator] value (done when processing debug commands between frames) or from mutable references to the individual components (done when handling a breakpoint).

[GenesisDebugView] then has a method to create a [GenesisDebugState] from itself by cloning all of the data that [GenesisDebugState] needs to own. This is done once for frame during normal execution to periodically send updated emulator state to the UI, and it's done immediately upon handling a breakpoint so that the UI sees the emulator state at the exact point where the breakpoint tripped.

[GenesisDebuggerForSegaCd] and [s32x::GenesisDebuggerFor32X] are structs that wrap a [GenesisDebugger] along with mutable references or pointers to components that are not on the named CPU's bus. The 32X version contains raw pointers due to the need to avoid putting lifetime parameters on the SH-2 bus struct, which requires [s32x::GenesisDebuggerFor32X] to have a static lifetime.

34pub mod s32x;
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}

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}
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 {

Errors

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    }
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}