bus.rsannotatedbus.rssource940 lines · 33.9 KB · raw
1use crate::api::debug::{DebugPendingWrite, GenesisDebuggerWithCpus, genesis_components};
2use crate::input::InputState;
3use crate::timing::CycleCounters;
4use bincode::{Decode, Encode};
5use genesis_components::cartridge::{Cartridge, GenesisRegionExt};
6use genesis_components::memory::Memory;
7use genesis_components::vdp::{Vdp, VdpBusView, VdpTickEffect};
8use genesis_components::ym2612::Ym2612;
9use genesis_components::{GenesisEmulatorConfigExt, vdp};
10use genesis_config::{GenesisEmulatorConfig, GenesisRegion};
11use jgenesis_common::frontend::{PartialClone, TimingMode};
12use jgenesis_common::num::{GetBit, U16Ext};
13use m68000_emu::debug::DummyM68000Debugger;
14use s32x_core::api::debug::Dummy32XDebugger;
15use s32x_core::api::{GenesisVdpInfo, Sega32X, Sega32XAudioOutput};
16use segacd_core::api::debug::DummySegaCdDebugger;
17use segacd_core::api::{SegaCd, SegaCdAudioOutput, SegaCdLoadResult};
18use std::{cmp, mem};
19use ti_sn76489::{Sn76489, Sn76489TickEffect, Sn76489Version};
20use z80_emu::debug::DummyZ80Debugger;
21use z80_emu::traits::InterruptLine;
22
23pub mod debug;
24
25const MARS: [u8; 4] = *b"MARS";
26
27pub trait GenesisAudioOutput: SegaCdAudioOutput + Sega32XAudioOutput {
28    fn collect_ym2612(&mut self, sample: (f64, f64));
29
30    fn collect_psg(&mut self, sample: f64);
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
34pub struct Z80BankRegister {
35    bank_number: u32,
36    current_bit: u8,
37}
38
39impl Z80BankRegister {
40    const BITS: u8 = 9;
41
42    pub fn value(self) -> u32 {
43        self.bank_number
44    }
45
46    pub fn map_to_68k_address(self, z80_address: u16) -> u32 {
47        (self.bank_number << 15) | u32::from(z80_address & 0x7FFF)
48    }
49
50    pub fn write_bit(&mut self, bit: bool) {
51        self.bank_number = (self.bank_number >> 1) | (u32::from(bit) << (Self::BITS - 1));
52    }
53}
54
55#[derive(Debug, Clone, Copy, Encode, Decode)]
56pub struct Z80Signals {
57    busreq: bool,
58    reset: bool,
59}
60
61impl Default for Z80Signals {
62    fn default() -> Self {
63        Self { busreq: false, reset: true }
64    }
65}
66
67impl Z80Signals {
68    fn busack(self) -> bool {
69        self.busreq && !self.reset
70    }
71}
72
73#[derive(Debug, Clone, Default, Encode, Decode)]
74pub struct PendingWrites {
75    byte: Vec<(u32, u8)>,
76    word: Vec<(u32, u16)>,
77}
78
79impl PendingWrites {
80    #[inline]
81    #[must_use]
82    pub fn new() -> Self {
83        Self { byte: Vec::with_capacity(20), word: Vec::with_capacity(20) }
84    }
85
86    fn clear(&mut self) {
87        self.byte.clear();
88        self.word.clear();
89    }
90
91    pub fn to_debug_vec(&self) -> Vec<DebugPendingWrite> {
92        self.byte
93            .iter()
94            .map(|&(address, value)| DebugPendingWrite::Byte { address, value })
95            .chain(
96                self.word
97                    .iter()
98                    .map(|&(address, value)| DebugPendingWrite::Word { address, value }),
99            )
100            .collect()
101    }
102}
103
104struct VdpView<'bus> {
105    sega_cd: Option<&'bus mut SegaCd>,
106    sega_32x: Option<&'bus mut Sega32X>,
107    cartridge: Option<&'bus mut Cartridge>,
108    memory: &'bus mut Memory,
109    open_bus: &'bus mut u16,
110}
111
112impl VdpBusView for VdpView<'_> {
113    fn read_word_for_dma(&mut self, address: u32) -> u16 {
114        match address {
115            0x000000..=0x3FFFFF => {
116                if let Some(sega_32x) = &mut self.sega_32x
117                    && sega_32x.cartridge_mut().is_some()
118                {
119                    // 32X or Sega CD 32X Mode 1
120                    // $000000-$3FFFFF is only accessible while adapter is disabled or RV=1
121                    if (!sega_32x.adapter_enabled() || sega_32x.rom_to_vram_dma())
122                        && let Some(cartridge) = sega_32x.cartridge_mut()
123                    {
124                        cartridge.read_word_for_dma(address, self.open_bus)
125                    } else {
126                        *self.open_bus
127                    }
128                } else if let Some(cartridge) = &mut self.cartridge {
129                    // Standalone Genesis or Sega CD Mode 1
130                    cartridge.read_word_for_dma(address, self.open_bus)
131                } else if let Some(sega_cd) = &mut self.sega_cd {
132                    // Sega CD [32X] Mode 2
133                    sega_cd.read_word_for_dma(address, self.open_bus)
134                } else {
135                    *self.open_bus
136                }
137            }
138            0xE00000..=0xFFFFFF => self.memory.read_main_ram_word(address),
139            _ => *self.open_bus,
140        }
141    }
142}
143
144#[derive(Debug, Encode, Decode, PartialClone)]
145pub struct GenesisBus {
146    #[partial_clone(partial)]
147    pub sega_cd: Option<SegaCd>,
148    #[partial_clone(partial)]
149    pub sega_32x: Option<Sega32X>,
150    #[partial_clone(partial)]
151    pub cartridge: Option<Cartridge>,
152    pub memory: Memory,
153    pub vdp: Vdp,
154    pub psg: Sn76489,
155    pub ym2612: Ym2612,
156    pub input: InputState,
157    pub z80_bank: Z80BankRegister,
158    pub z80_signals: Z80Signals,
159    pub pending_writes: PendingWrites,
160    pub cycles: CycleCounters,
161    pub timing_mode: TimingMode,
162    pub open_bus: u16,
163    pub m68k_opcode: u16,
164    pub m68k_reset: bool,
165}
166
167impl GenesisBus {
168    pub fn new(
169        timing_mode: TimingMode,
170        cartridge: Option<Cartridge>,
171        sega_cd: Option<SegaCd>,
172        sega_32x: Option<Sega32X>,
173        config: &GenesisEmulatorConfig,
174    ) -> Self {
175        // At most one of Genesis+32X should have a cartridge
176        debug_assert!(
177            cartridge.is_none()
178                || sega_32x.as_ref().is_none_or(|sega_32x| sega_32x.cartridge().is_none())
179        );
180
181        let memory = Memory::new(config);
182        let vdp = Vdp::new(timing_mode, config.to_vdp_config(sega_32x.is_some()));
183        let psg = Sn76489::new(Sn76489Version::Standard);
184        let ym2612 = Ym2612::new(config);
185
186        let six_button_incompatible = {
187            let cartridge = sega_32x.as_ref().and_then(Sega32X::cartridge).or(cartridge.as_ref());
188
189            cartridge
190                .map(|cartridge| cartridge.metadata().six_button_incompatible)
191                .or_else(|| sega_cd.as_ref().map(SegaCd::has_six_button_incompatible_game))
192                .unwrap_or(false)
193        };
194        let input = InputState::new(config, six_button_incompatible);
195
196        let cycles = CycleCounters::new(config.clamped_m68k_divider());
197
198        Self {
199            sega_cd,
200            sega_32x,
201            cartridge,
202            memory,
203            vdp,
204            psg,
205            ym2612,
206            input,
207            z80_bank: Z80BankRegister::default(),
208            z80_signals: Z80Signals::default(),
209            pending_writes: PendingWrites::new(),
210            cycles,
211            timing_mode,
212            open_bus: 0,
213            m68k_opcode: 0,
214            m68k_reset: true,
215        }
216    }
217
218    #[inline]
219    pub fn tick_components<const DEBUG: bool>(
220        &mut self,
221        m68k_cycles: u32,
222        mclk_cycles: u64,
223        m68k_wait: bool,
224        audio_output: &mut impl GenesisAudioOutput,
225        mut debugger: Option<GenesisDebuggerWithCpus<'_, '_>>,
226    ) -> SegaCdLoadResult<VdpTickEffect> {
227        if let Some(cartridge) = &mut self.cartridge {
228            cartridge.tick(m68k_cycles);
229        }
230
231        self.input.tick(m68k_cycles);
232
233        while self.cycles.should_tick_psg() {
234            if self.psg.tick() == Sn76489TickEffect::Clocked {
235                // PSG only has mono output in the Genesis; stereo output is only for Game Gear
236                let (psg_sample, _) = self.psg.sample();
237                audio_output.collect_psg(psg_sample);
238            }
239
240            self.cycles.psg_cycle();
241        }
242
243        let vdp_tick_effect = self.vdp.tick(
244            mclk_cycles,
245            &mut VdpView {
246                sega_cd: self.sega_cd.as_mut(),
247                sega_32x: self.sega_32x.as_mut(),
248                cartridge: self.cartridge.as_mut(),
249                memory: &mut self.memory,
250                open_bus: &mut self.open_bus,
251            },
252        );
253
254        if self.cycles.ym2612_sync_needed() || vdp_tick_effect == VdpTickEffect::FrameComplete {
255            self.sync_ym2612();
256
257            for sample in self.ym2612.drain_output_samples() {
258                audio_output.collect_ym2612(sample);
259            }
260        }
261
262        if let Some(sega_cd) = &mut self.sega_cd {
263            if DEBUG && let Some(debugger) = &mut debugger {
264                let mut sega_cd_debugger = debugger.for_sega_cd(
265                    self.sega_32x.as_mut(),
266                    genesis_components!(self),
267                    self.cartridge.as_mut(),
268                );
269                sega_cd.tick::<true>(mclk_cycles, audio_output, &mut sega_cd_debugger)?;
270            } else {
271                sega_cd.tick::<false>(mclk_cycles, audio_output, &mut DummySegaCdDebugger)?;
272            }
273        }
274
275        if let Some(sega_32x) = &mut self.sega_32x {
276            let genesis_vdp_info = GenesisVdpInfo {
277                scanline: self.vdp.scanline(),
278                scanline_mclk: self.vdp.scanline_mclk(),
279                frame_size: self.vdp.frame_size(),
280                border_size: self.vdp.border_size(),
281                scanlines_in_current_frame: self.vdp.scanlines_in_current_frame(),
282            };
283
284            if DEBUG && let Some(debugger) = &mut debugger {
285                let mut s32x_debugger =
286                    debugger.for_32x(self.sega_cd.as_mut(), genesis_components!(self));
287                sega_32x.tick::<true>(
288                    mclk_cycles,
289                    genesis_vdp_info,
290                    audio_output,
291                    &mut *s32x_debugger,
292                );
293            } else {
294                sega_32x.tick::<false>(
295                    mclk_cycles,
296                    genesis_vdp_info,
297                    audio_output,
298                    &mut Dummy32XDebugger,
299                );
300            }
301        }
302
303        check_for_long_dma_skip(&self.vdp, &mut self.cycles);
304
305        if !m68k_wait {
306            self.vdp.update_interrupt_latches();
307        }
308
309        self.apply_pending_writes();
310
311        Ok(vdp_tick_effect)
312    }
313
314    pub(crate) fn cartridge(&self) -> Option<&Cartridge> {
315        self.sega_32x.as_ref().and_then(Sega32X::cartridge).or(self.cartridge.as_ref())
316    }
317
318    pub(crate) fn cartridge_mut(&mut self) -> Option<&mut Cartridge> {
319        self.sega_32x.as_mut().and_then(Sega32X::cartridge_mut).or(self.cartridge.as_mut())
320    }
321
322    pub fn game_title(&mut self) -> Option<String> {
323        self.cartridge()
324            .map(|cartridge| cartridge.program_title().to_owned())
325            .or_else(|| self.sega_cd.as_ref().and_then(SegaCd::disc_title))
326    }
327
328    pub fn has_persistent_ram(&self) -> bool {
329        self.sega_cd.is_some() || self.cartridge().is_some_and(Cartridge::is_ram_persistent)
330    }
331
332    pub fn get_and_clear_persistent_ram_dirty(&mut self) -> bool {
333        let mut dirty = self.sega_cd.as_mut().is_some_and(SegaCd::take_backup_ram_dirty);
334
335        dirty |= self.cartridge_mut().is_some_and(Cartridge::get_and_clear_ram_dirty);
336
337        dirty
338    }
339
340    pub fn reload_config(&mut self, config: &GenesisEmulatorConfig) {
341        if let Some(sega_cd) = &mut self.sega_cd {
342            sega_cd.reload_config(config);
343        }
344
345        if let Some(sega_32x) = &mut self.sega_32x {
346            sega_32x.reload_config(config);
347        }
348
349        if let Some(cartridge) = &mut self.cartridge {
350            cartridge.reload_config(config);
351        }
352
353        self.memory.reload_config(config);
354        self.vdp.reload_config(config.to_vdp_config(self.sega_32x.is_some()));
355        self.ym2612.reload_config(config);
356        self.input.reload_config(config);
357        self.cycles.update_m68k_divider(config.clamped_m68k_divider());
358    }
359
360    pub fn reset(&mut self) {
361        if let Some(sega_cd) = &mut self.sega_cd {
362            sega_cd.reset();
363        }
364
365        if let Some(sega_32x) = &mut self.sega_32x {
366            sega_32x.reset();
367        }
368
369        self.z80_signals = Z80Signals::default();
370        self.ym2612.reset();
371    }
372
373    fn apply_pending_writes(&mut self) {
374        let mut pending_writes = mem::take(&mut self.pending_writes);
375
376        for &(address, value) in &pending_writes.byte {
377            self.apply_write::<false>(address, value.into());
378        }
379
380        for &(address, value) in &pending_writes.word {
381            self.apply_write::<true>(address, value);
382        }
383
384        pending_writes.clear();
385        self.pending_writes = pending_writes;
386    }
387
388    fn m68k_read<const WORD: bool>(&mut self, address: u32) -> u16 {
389        let address = address & 0xFFFFFF;
390        log::trace!("Main bus {} read, address={address:06X}", if WORD { "word" } else { "byte" });
391
392        let value = match address {
393            0x000000..=0x3FFFFF => {
394                // If cartridge is present, maps to cartridge
395                // Otherwise maps to Sega CD BIOS ROM / PRG RAM / Word RAM
396                if let Some(sega_32x) = &mut self.sega_32x
397                    && sega_32x.cartridge_mut().is_some()
398                {
399                    if sega_32x.adapter_enabled() {
400                        sega_32x.m68k_read_cartridge::<WORD>(address, self.open_bus)
401                    } else if let Some(cartridge) = sega_32x.cartridge_mut() {
402                        cartridge.read::<WORD>(address, self.open_bus)
403                    } else {
404                        self.read_open_bus::<WORD>(address)
405                    }
406                } else if let Some(cartridge) = &mut self.cartridge {
407                    cartridge.read::<WORD>(address, self.open_bus)
408                } else if let Some(sega_cd) = &mut self.sega_cd {
409                    sega_cd.main_read_memory::<WORD>(address)
410                } else {
411                    self.read_open_bus::<WORD>(address)
412                }
413            }
414            0x400000..=0x7FFFFF => {
415                // If cartridge is present, maps to Sega CD if present, otherwise let it go through to the cartridge
416                // Otherwise, assuming Sega CD is present, maps to Sega CD RAM cartridge
417                let cartridge = self
418                    .sega_32x
419                    .as_mut()
420                    .and_then(Sega32X::cartridge_mut)
421                    .or(self.cartridge.as_mut());
422
423                match (cartridge, &mut self.sega_cd) {
424                    (Some(_cartridge), Some(sega_cd)) => sega_cd.main_read_memory::<WORD>(address),
425                    (Some(cartridge), None) => cartridge.read::<WORD>(address, self.open_bus),
426                    (None, Some(sega_cd)) => sega_cd.read_ram_cartridge::<WORD>(address),
427                    (None, None) => self.read_open_bus::<WORD>(address),
428                }
429            }
430            0x800000..=0x9FFFFF
431                if let Some(sega_32x) = &mut self.sega_32x
432                    && sega_32x.adapter_enabled() =>
433            {
434                sega_32x.m68k_read_memory::<WORD>(address, self.open_bus)
435            }
436            0xA00000..=0xA0FFFF if self.z80_signals.busack() => {
437                // Z80 memory map; 68k can only access when the Z80 is running and removed from the bus
438                self.cycles.record_68k_z80_bus_access();
439
440                // For 68k access, $8000-$FFFF mirrors $0000-$7FFF
441                let byte =
442                    <Self as z80_emu::BusInterface>::read_memory(self, (address & 0x7FFF) as u16);
443                if WORD {
444                    // All Z80 access is byte-size; word reads mirror the byte in both MSB and LSB
445                    u16::from_ne_bytes([byte; 2])
446                } else {
447                    byte.into()
448                }
449            }
450            0xA10000..=0xA1001F => {
451                // I/O ports, even addresses mirror odd addresses
452                let byte = self.read_io_register(address | 1);
453                if WORD { u16::from_ne_bytes([byte; 2]) } else { byte.into() }
454            }
455            0xA11100 => {
456                // Bit 8 is Z80 BUSACK (active low), other bits are unused
457                let busack_bit = u16::from(!self.z80_signals.busack()) << 8;
458
459                // Unused bits should read open bus; Danny Sullivan's Indy Heat (Proto) and Time Killers
460                // depend on this or they will fail to boot
461                let busack_word = busack_bit | (self.open_bus & !(1 << 8));
462                if WORD { busack_word } else { busack_word.msb().into() }
463            }
464            0xA12000..=0xA12FFF if let Some(sega_cd) = &mut self.sega_cd => {
465                sega_cd.main_read_register::<WORD>(address)
466            }
467            0xA130EC..=0xA130EF if self.sega_32x.is_some() => {
468                // Always reads the ASCII string "MARS"; used by 32X games to detect 32X hardware
469                let idx = (address & 3) as usize;
470                if WORD {
471                    u16::from_be_bytes(MARS[idx..idx + 2].try_into().unwrap())
472                } else {
473                    MARS[idx].into()
474                }
475            }
476            0xA15000..=0xA15FFF => {
477                if let Some(sega_32x) = &mut self.sega_32x {
478                    sega_32x.m68k_read_register::<WORD>(address, self.open_bus)
479                } else if let Some(cartridge) = &mut self.cartridge {
480                    cartridge.read::<WORD>(address, self.open_bus)
481                } else {
482                    self.read_open_bus::<WORD>(address)
483                }
484            }
485            0xC00000..=0xC0001F => self.read_vdp::<WORD>(address),
486            0xE00000..=0xFFFFFF => self.memory.read_main_ram::<WORD>(address),
487            _ => self.read_open_bus::<WORD>(address),
488        };
489
490        if WORD {
491            self.open_bus = value;
492        } else {
493            // TODO this is probably not right, probably depends on memory region
494            self.open_bus.set_msb(value as u8);
495        }
496
497        value
498    }
499
500    #[inline(always)]
501    fn read_open_bus<const WORD: bool>(&self, address: u32) -> u16 {
502        if WORD { self.open_bus } else { self.open_bus.be_byte(address & 1).into() }
503    }
504
505    fn apply_write<const WORD: bool>(&mut self, address: u32, value: u16) {
506        let address = address & 0xFFFFFF;
507
508        if WORD {
509            log::trace!("Main bus word write: address={address:06X}, value={value:04X}");
510        } else {
511            log::trace!("Main bus byte write: address={address:06X}, value={:02X}", value & 0xFF);
512        }
513
514        match address {
515            0x000000..=0x3FFFFF => {
516                // If cartridge is present, maps to cartridge
517                // Otherwise maps to Sega CD BIOS ROM / PRG RAM / Word RAM
518                if let Some(sega_32x) = &mut self.sega_32x
519                    && sega_32x.cartridge_mut().is_some()
520                {
521                    if sega_32x.adapter_enabled() {
522                        sega_32x.m68k_write_cartridge::<WORD>(address, value);
523                    } else if let Some(cartridge) = sega_32x.cartridge_mut() {
524                        cartridge.write::<WORD>(address, value);
525                    }
526                } else if let Some(cartridge) = &mut self.cartridge {
527                    cartridge.write::<WORD>(address, value);
528                } else if let Some(sega_cd) = &mut self.sega_cd {
529                    sega_cd.main_write_memory::<WORD>(address, value);
530                }
531            }
532            0x400000..=0x7FFFFF => {
533                // If cartridge is present, maps to Sega CD if present, otherwise let it go through to the cartridge
534                // Otherwise, assuming Sega CD is present, maps to Sega CD RAM cartridge
535                let cartridge = self
536                    .sega_32x
537                    .as_mut()
538                    .and_then(Sega32X::cartridge_mut)
539                    .or(self.cartridge.as_mut());
540
541                match (cartridge, &mut self.sega_cd) {
542                    (Some(_cartridge), Some(sega_cd)) => {
543                        sega_cd.main_write_memory::<WORD>(address, value);
544                    }
545                    (Some(cartridge), None) => {
546                        cartridge.write::<WORD>(address, value);
547                    }
548                    (None, Some(sega_cd)) => {
549                        sega_cd.write_ram_cartridge::<WORD>(address, value);
550                    }
551                    (None, None) => {}
552                }
553            }
554            0x800000..=0x9FFFFF
555                if let Some(sega_32x) = &mut self.sega_32x
556                    && sega_32x.adapter_enabled() =>
557            {
558                sega_32x.m68k_write_memory::<WORD>(address, value);
559            }
560            0xA00000..=0xA0FFFF if self.z80_signals.busack() => {
561                // Z80 memory map; writable by the 68k only when the Z80 is removed from the bus
562                // and not reset
563                self.cycles.record_68k_z80_bus_access();
564
565                // Word-size writes write the MSB as a byte-size write
566                let byte = if WORD { value.msb() } else { value as u8 };
567
568                // For 68k access, $8000-$FFFF mirrors $0000-$7FFF
569                <Self as z80_emu::BusInterface>::write_memory(
570                    self,
571                    (address & 0x7FFF) as u16,
572                    byte,
573                );
574            }
575            0xA10000..=0xA1001F => {
576                // I/O ports, even addresses mirror odd addresses
577                self.write_io_register(address | 1, value as u8);
578            }
579            0xA11100 => {
580                self.z80_signals.busreq = if WORD { value.bit(8) } else { value.bit(0) };
581                log::trace!("Set Z80 BUSREQ to {}", self.z80_signals.busreq);
582            }
583            0xA11200 => {
584                let z80_reset = if WORD { !value.bit(8) } else { !value.bit(0) };
585                self.set_z80_reset(z80_reset);
586            }
587            0xA12000..=0xA12FFF if let Some(sega_cd) = &mut self.sega_cd => {
588                sega_cd.main_write_register::<WORD>(address, value);
589            }
590            0xA13000..=0xA13FFF if let Some(cartridge) = self.cartridge_mut() => {
591                // TIME signal; used by various cartridge mapper registers
592                cartridge.write::<WORD>(address, value);
593            }
594            0xA15000..=0xA15FFF => {
595                if let Some(sega_32x) = &mut self.sega_32x {
596                    sega_32x.m68k_write_register::<WORD>(address, value);
597                } else if let Some(cartridge) = &mut self.cartridge {
598                    // SVP maps registers to $A15xxx
599                    cartridge.write::<WORD>(address, value);
600                }
601            }
602            0xC00000..=0xC0001F => {
603                self.write_vdp_psg::<WORD>(address, value);
604            }
605            0xE00000..=0xFFFFFF => {
606                self.memory.write_main_ram::<WORD>(address, value);
607            }
608            _ => {}
609        }
610    }
611
612    fn read_io_register(&mut self, address: u32) -> u8 {
613        match address {
614            // Version register
615            0xA10001 => {
616                let region = self
617                    .cartridge()
618                    .map(Cartridge::region)
619                    .or_else(|| self.sega_cd.as_ref().map(SegaCd::region))
620                    .unwrap_or(GenesisRegion::Americas);
621
622                // TODO version (lowest 4 bits) hardcoded to 0
623                (u8::from(region.version_bit()) << 7)
624                    | (u8::from(self.timing_mode == TimingMode::Pal) << 6)
625                    | (u8::from(self.sega_cd.is_none()) << 5)
626            }
627            0xA10003 => self.input.read_p1_data(),
628            0xA10005 => self.input.read_p2_data(),
629            0xA10007 => self.input.read_ext_data(),
630            0xA10009 => self.input.read_p1_ctrl(),
631            0xA1000B => self.input.read_p2_ctrl(),
632            0xA1000D => self.input.read_ext_ctrl(),
633            0xA1000F => self.input.read_p1_tx_data(),
634            0xA10015 => self.input.read_p2_tx_data(),
635            0xA1001B => self.input.read_ext_tx_data(),
636            // Other I/O registers return 0x00 by default
637            _ => 0x00,
638        }
639    }
640
641    fn write_io_register(&mut self, address: u32, value: u8) {
642        match address {
643            0xA10003 => self.input.write_p1_data(value),
644            0xA10005 => self.input.write_p2_data(value),
645            0xA10007 => self.input.write_ext_data(value),
646            0xA10009 => self.input.write_p1_ctrl(value),
647            0xA1000B => self.input.write_p2_ctrl(value),
648            0xA1000D => self.input.write_ext_ctrl(value),
649            0xA1000F => self.input.write_p1_tx_data(value),
650            0xA10015 => self.input.write_p2_tx_data(value),
651            0xA1001B => self.input.write_ext_tx_data(value),
652            _ => {}
653        }
654    }
655
656    fn read_vdp<const WORD: bool>(&mut self, address: u32) -> u16 {
657        let word = match address & 0x1F {
658            0x00..=0x03 => self.vdp.read_data(),
659            0x04..=0x07 => {
660                // Highest 6 bits of VDP status register are open bus; VDPFIFOTesting DMA busy flag tests
661                // depend on this
662                self.vdp.read_status(self.m68k_opcode, self.cycles.m68k_divider.get())
663                    | (self.open_bus & 0xFC00)
664            }
665            0x08..=0x0B => self.vdp.hv_counter(self.m68k_opcode, self.cycles.m68k_divider.get()),
666            0x0C..=0x1F => {
667                // PSG / unused space; PSG is not readable
668                self.open_bus
669            }
670            _ => unreachable!("address & 0x1F is always <= 0x1F"),
671        };
672
673        if WORD { word } else { word.to_be_bytes()[(address & 1) as usize].into() }
674    }
675
676    fn write_vdp_psg<const WORD: bool>(&mut self, address: u32, value: u16) {
677        // Byte-size VDP writes duplicate the byte into a word
678        let vdp_word = if WORD { value } else { (value & 0xFF) | (value << 8) };
679
680        match address & 0x1F {
681            0x00..=0x03 => self.vdp.write_data(vdp_word),
682            0x04..=0x07 => self.vdp.write_control(vdp_word),
683            0x10..=0x17 if WORD || address.bit(0) => self.psg.write(value as u8),
684            0x1C..=0x1D => self.vdp.write_debug_register(vdp_word),
685            _ => {}
686        }
687    }
688
689    fn set_z80_reset(&mut self, z80_reset: bool) {
690        if !self.z80_signals.reset && z80_reset {
691            // Z80 RESET also resets the YM2612
692            // Fantastic Dizzy depends on this or music will not mute correctly when you pause the game
693            self.ym2612.reset();
694        }
695
696        self.z80_signals.reset = z80_reset;
697        log::trace!("Set Z80 RESET to {}", self.z80_signals.reset);
698    }
699
700    fn sync_ym2612(&mut self) {
701        if self.cycles.has_ym2612_ticks() {
702            let ticks = self.cycles.take_ym2612_ticks();
703            self.ym2612.tick(ticks);
704        }
705    }
706}

If a long DMA is in progress (i.e. the DMA will not finish on this line), preemptively skip the 68000 forward by a large number of mclk cycles (up to 1250).

This function is public so that it can be used by the Sega CD core

712#[inline]
713fn check_for_long_dma_skip(vdp: &Vdp, cycles: &mut CycleCounters) {
714    if !vdp.long_halting_dma_in_progress() {
715        return;
716    }
717
718    if !cycles.z80_halt {
719        // Don't advance for very long time slices if the Z80 is still active; doing so causes
720        // video/audio desync in Overdrive 2.
721        // 8 68K cycles is slightly less than 4 Z80 cycles
722        cycles.m68k_wait_cpu_cycles = 8;
723        return;
724    }
725
726    // Skip as close as possible to the end of the current scanline
727    let wait_cycles = cmp::max(
728        cycles.m68k_wait_cpu_cycles,
729        cmp::min(
730            cycles.max_wait_cpu_cycles,
731            (vdp::MCLK_CYCLES_PER_SCANLINE - vdp.scanline_mclk()) as u32
732                / cycles.m68k_divider_u32.get(),
733        ),
734    );
735    cycles.m68k_wait_cpu_cycles = wait_cycles;
736
737    log::trace!(
738        "Skipping {wait_cycles} 68000 CPU cycles in long DMA optimization, scanline mclk is {}",
739        vdp.scanline_mclk()
740    );
741}
743impl m68000_emu::BusInterface for GenesisBus {
744    type DebugView<'a>
745        = DummyM68000Debugger
746    where
747        Self: 'a;
748
749    #[inline]
750    fn read_byte(&mut self, address: u32) -> u8 {
751        self.m68k_read::<false>(address) as u8
752    }
753
754    #[inline]
755    fn read_word(&mut self, address: u32) -> u16 {
756        self.m68k_read::<true>(address)
757    }
758
759    #[inline]
760    fn write_byte(&mut self, address: u32, value: u8) {
761        self.pending_writes.byte.push((address, value));
762    }
763
764    #[inline]
765    fn write_word(&mut self, address: u32, value: u16) {
766        self.pending_writes.word.push((address, value));
767    }
768
769    #[inline]
770    fn interrupt_level(&self) -> u8 {
771        self.vdp.m68k_interrupt_level()
772    }
773
774    #[inline]
775    fn acknowledge_interrupt(&mut self, _interrupt_level: u8) {
776        // When the 68000 acknowledges a VDP interrupt, the VDP acknowledges whatever level it is
777        // currently raising rather than paying attention to the 68000's IACK lines. This is noted
778        // in official documentation which describes this hardware bug: If both HINT are VINT are
779        // enabled and the 68000 executes a long instruction right before HINT would trigger on line
780        // 224, it's possible for the following sequence of events to happen:
781        //   1. The VDP sets IPL2 to indicate a level 4 interrupt (HINT)
782        //   2. After the 68000 finishes its long instruction, it begins to handle the level 4 interrupt
783        //   3. Before the acknowledge, VBlank begins and the VDP sets IPL2+IPL1 for level 6 interrupt (VINT)
784        //   4. The 68000 sets its VPA signal to acknowledge the interrupt, and the VDP acknowledges VINT instead of HINT
785        //   5. After the 68000 returns from its HINT handler, it immediately handles the HINT a second time (having missed VINT)
786        self.vdp.acknowledge_m68k_interrupt();
787    }
788
789    #[inline]
790    fn halt(&self) -> bool {
791        self.vdp.should_halt_cpu()
792    }
793
794    #[inline]
795    fn reset(&self) -> bool {
796        self.m68k_reset
797    }
798}
799
800impl z80_emu::BusInterface for GenesisBus {
801    type DebugView<'a>
802        = DummyZ80Debugger
803    where
804        Self: 'a;
805
806    #[inline]
807    #[allow(clippy::match_same_arms)]
808    fn read_memory(&mut self, address: u16) -> u8 {
809        log::trace!("Z80 bus read from {address:04X}");
810
811        match address {
812            0x0000..=0x3FFF => {
813                // Z80 RAM (mirrored at $2000-$3FFF)
814                self.memory.read_audio_ram(address)
815            }
816            0x4000..=0x5FFF => {
817                // YM2612 registers/ports (mirrored every 4 addresses)
818                self.sync_ym2612();
819                self.ym2612.read_register(address)
820            }
821            0x6000..=0x60FF => {
822                // Bank number register
823                // TODO what should this do on reads?
824                0xFF
825            }
826            0x6100..=0x7EFF => {
827                // Unused address space
828                0xFF
829            }
830            0x7F00..=0x7F1F => {
831                // VDP ports
832                self.cycles.record_z80_68k_bus_access();
833                self.read_vdp::<false>(address.into()) as u8
834            }
835            0x7F20..=0x7FFF => {
836                // Invalid addresses
837                0xFF
838            }
839            0x8000..=0xFFFF => {
840                self.cycles.record_z80_68k_bus_access();
841
842                let m68k_addr = self.z80_bank.map_to_68k_address(address);
843                match m68k_addr {
844                    0xA00000..=0xA0FFFF => {
845                        // TODO this should lock up the system
846                        log::error!(
847                            "Z80 attempted to read its own memory from the 68k bus; z80_addr={address:04X}, m68k_addr={m68k_addr:08X}"
848                        );
849                        0xFF
850                    }
851                    0xE00000..=0xFFFFFF => {
852                        // Z80 cannot read from 68000 working RAM
853                        // TODO should probably return Z80 open bus instead?
854                        0xFF
855                    }
856                    _ => <Self as m68000_emu::BusInterface>::read_byte(self, m68k_addr),
857                }
858            }
859        }
860    }
861
862    #[inline]
863    fn write_memory(&mut self, address: u16, value: u8) {
864        log::trace!("Z80 bus write at {address:04X}");
865
866        match address {
867            0x0000..=0x3FFF => {
868                // Z80 RAM (mirrored at $2000-$3FFF)
869                self.memory.write_audio_ram(address, value);
870            }
871            0x4000..=0x5FFF => {
872                // YM2612 registers/ports (mirrored every 4 addresses)
873                self.sync_ym2612();
874                match address & 0x03 {
875                    0x00 => self.ym2612.write_address_1(value),
876                    0x02 => self.ym2612.write_address_2(value),
877                    0x01 | 0x03 => self.ym2612.write_data(value),
878                    _ => unreachable!("value & 0x03 is always <= 0x03"),
879                }
880            }
881            0x6000..=0x60FF => {
882                self.z80_bank.write_bit(value.bit(0));
883            }
884            0x6100..=0x7EFF | 0x7F20..=0x7FFF => {
885                // Unused / invalid addresses
886                // TODO writes to $7F20-$7FFF should halt the system
887            }
888            0x7F00..=0x7F1F => {
889                // VDP addresses
890                self.cycles.record_z80_68k_bus_access();
891                self.write_vdp_psg::<false>(address.into(), value.into());
892            }
893            0x8000..=0xFFFF => {
894                self.cycles.record_z80_68k_bus_access();
895
896                let m68k_addr = self.z80_bank.map_to_68k_address(address);
897                if !(0xA00000..=0xA0FFFF).contains(&m68k_addr) {
898                    self.apply_write::<false>(m68k_addr, value.into());
899                } else {
900                    // TODO this should lock up the system
901                    log::error!(
902                        "Z80 attempted to write to its own memory from the 68k bus; z80_addr={address:04X}, m68k_addr={m68k_addr:08X}, value={value:02X}"
903                    );
904                }
905            }
906        }
907    }
908
909    #[inline]
910    fn read_io(&mut self, _address: u16) -> u8 {
911        // I/O ports are not wired up to the Z80
912        0xFF
913    }
914
915    #[inline]
916    fn write_io(&mut self, _address: u16, _value: u8) {
917        // I/O ports are not wired up to the Z80
918    }
919
920    #[inline]
921    fn nmi(&self) -> InterruptLine {
922        // The NMI line is not connected to anything
923        InterruptLine::High
924    }
925
926    #[inline]
927    fn int(&self) -> InterruptLine {
928        self.vdp.z80_interrupt_line()
929    }
930
931    #[inline]
932    fn busreq(&self) -> bool {
933        self.z80_signals.busreq
934    }
935
936    #[inline]
937    fn reset(&self) -> bool {
938        self.z80_signals.reset
939    }
940}