bus.rsannotatedbus.rssource774 lines · 27.1 KB · raw
1//! 32X memory mapping for the 68000 and SH-2s
2
3pub mod debug;
4
5use crate::pwm::PwmChip;
6use crate::registers::{Access, SystemRegisters};
7use crate::vdp::Vdp;
8use crate::{WhichCpu, bootrom};
9use bincode::{Decode, Encode};
10use genesis_components::cartridge::Cartridge;
11use genesis_config::Sega32XEmulatorConfig;
12use jgenesis_common::boxedarray::BoxedWordArray;
13use jgenesis_common::frontend::TimingMode;
14use jgenesis_common::num::{GetBit, U16Ext};
15use jgenesis_proc_macros::PartialClone;
16use sh2_emu::Sh2;
17use sh2_emu::bus::{AccessContext, BusInterface, OpSize};
18use sh2_emu::debug::DummySh2Debugger;
19use std::marker::PhantomData;
20use std::ops::{Deref, DerefMut};
21use std::ptr::NonNull;
22use std::{array, cmp};
23
24const SDRAM_LEN_WORDS: usize = 256 * 1024 / 2;
25const SDRAM_MASK: u32 = 0x3FFFF;
26
27#[derive(Debug, Clone, Default, Encode, Decode)]
28pub struct SerialInterface {
29    pub master_to_slave: Option<u8>,
30    pub slave_to_master: Option<u8>,
31}
32
33#[derive(Debug, Clone, Encode, Decode, PartialClone)]
34pub struct Sega32XBus {
35    #[partial_clone(partial)]
36    pub cartridge: Option<Cartridge>,
37    pub vdp: Vdp,
38    pub pwm: PwmChip,
39    pub registers: SystemRegisters,
40    pub sdram: BoxedWordArray<SDRAM_LEN_WORDS>,
41    pub serial: SerialInterface,
42    pub stalled_on_cartridge_access: bool,
43}
44
45impl Sega32XBus {
46    pub fn new(
47        timing_mode: TimingMode,
48        cartridge: Option<Cartridge>,
49        config: &Sega32XEmulatorConfig,
50    ) -> Self {
51        let cartridge_present = cartridge.is_some();
52
53        Self {
54            cartridge,
55            vdp: Vdp::new(timing_mode, config),
56            pwm: PwmChip::new(timing_mode),
57            registers: SystemRegisters::new(cartridge_present),
58            sdram: BoxedWordArray::new(),
59            serial: SerialInterface::default(),
60            stalled_on_cartridge_access: false,
61        }
62    }
63}
64
65pub struct OtherCpu {
66    cpu: NonNull<Sh2>,
67    cycle_counter: NonNull<u64>,
68}
69
70// SH-2 memory map
71pub struct Sh2Bus {
72    s32x_bus: NonNull<Sega32XBus>,
73    other_sh2: Option<OtherCpu>,
74    pub which: WhichCpu,
75    pub cycle_counter: u64,
76    pub cycle_limit: u64,
77}
78
79pub struct Sh2BusGuard<'bus, 'cartridge, 'other> {
80    bus: Sh2Bus,
81    _bus_marker: PhantomData<&'bus ()>,
82    _cartridge_marker: PhantomData<&'cartridge ()>,
83    _other_marker: PhantomData<&'other ()>,
84}
85
86impl Deref for Sh2BusGuard<'_, '_, '_> {
87    type Target = Sh2Bus;
88
89    fn deref(&self) -> &Self::Target {
90        &self.bus
91    }
92}
93
94impl DerefMut for Sh2BusGuard<'_, '_, '_> {
95    fn deref_mut(&mut self) -> &mut Self::Target {
96        &mut self.bus
97    }
98}
99
100// All values are minus one because every access takes at least 1 cycle
101const SH2_CARTRIDGE_CYCLES: u64 = 7;
102const SH2_FRAME_BUFFER_READ_CYCLES: u64 = 4;
103const SH2_VDP_CYCLES: u64 = 4;
104// SDRAM burst reads take between 10 and 12 cycles; assume always 10 for simplicity
105const SH2_SDRAM_READ_CYCLES: u64 = 9;
106// SDRAM writes take between 1 and 3 cycles; assume always 1 for simplicity
107const SH2_SDRAM_WRITE_CYCLES: u64 = 0;
108
109macro_rules! invalid_size {
110    ($size:expr) => {
111        panic!("invalid size {}", $size)
112    };
113}
114
115fn sh2_read_register<const SIZE: u8>(address: u32, mut read_word: impl FnMut(u32) -> u16) -> u32 {
116    match SIZE {
117        OpSize::BYTE => {
118            let word = read_word(address & !1);
119            if !address.bit(0) { word.msb().into() } else { word.lsb().into() }
120        }
121        OpSize::WORD => read_word(address).into(),
122        OpSize::LONGWORD => {
123            let high: u32 = read_word(address).into();
124            let low: u32 = read_word(address | 2).into();
125            low | (high << 16)
126        }
127        _ => invalid_size!(SIZE),
128    }
129}
130
131fn sh2_write_register<const SIZE: u8>(
132    bus: &mut Sega32XBus,
133    address: u32,
134    value: u32,
135    read_word: impl Fn(&mut Sega32XBus, u32) -> u16,
136    write_word: impl Fn(&mut Sega32XBus, u32, u16),
137) {
138    match SIZE {
139        OpSize::BYTE => {
140            let mut word = read_word(bus, address & !1);
141            if !address.bit(0) {
142                word.set_msb(value as u8);
143            } else {
144                word.set_lsb(value as u8);
145            }
146            write_word(bus, address & !1, word);
147        }
148        OpSize::WORD => {
149            write_word(bus, address, value as u16);
150        }
151        OpSize::LONGWORD => {
152            write_word(bus, address, (value >> 16) as u16);
153            write_word(bus, address | 2, value as u16);
154        }
155        _ => invalid_size!(SIZE),
156    }
157}
158
159fn sh2_read_memory<const SIZE: u8, const N: usize>(memory: &[u8; N], address: u32) -> u32 {
160    match SIZE {
161        OpSize::BYTE => read_u8(memory, address).into(),
162        OpSize::WORD => read_u16(memory, address).into(),
163        OpSize::LONGWORD => read_u32(memory, address),
164        _ => invalid_size!(SIZE),
165    }
166}
167
168fn sh2_read_memory_u16<const SIZE: u8, const N: usize>(memory: &[u16; N], address: u32) -> u32 {
169    match SIZE {
170        OpSize::BYTE => {
171            let memory_addr = ((address >> 1) as usize) & (N - 1);
172            let word = memory[memory_addr];
173            if !address.bit(0) { word.msb().into() } else { word.lsb().into() }
174        }
175        OpSize::WORD => {
176            let memory_addr = ((address >> 1) as usize) & (N - 1);
177            memory[memory_addr].into()
178        }
179        OpSize::LONGWORD => {
180            let memory_addr = ((address >> 1) as usize) & (N - 1) & !1;
181            let high: u32 = memory[memory_addr].into();
182            let low: u32 = memory[memory_addr + 1].into();
183            low | (high << 16)
184        }
185        _ => invalid_size!(SIZE),
186    }
187}
188
189fn sh2_write_memory_u16<const SIZE: u8, const N: usize>(
190    memory: &mut [u16; N],
191    address: u32,
192    value: u32,
193) {
194    match SIZE {
195        OpSize::BYTE => {
196            let memory_addr = ((address >> 1) as usize) & (N - 1);
197            if !address.bit(0) {
198                memory[memory_addr].set_msb(value as u8);
199            } else {
200                memory[memory_addr].set_lsb(value as u8);
201            }
202        }
203        OpSize::WORD => {
204            let memory_addr = ((address >> 1) as usize) & (N - 1);
205            memory[memory_addr] = value as u16;
206        }
207        OpSize::LONGWORD => {
208            let memory_addr = ((address >> 1) as usize) & (N - 1) & !1;
209            memory[memory_addr] = (value >> 16) as u16;
210            memory[memory_addr + 1] = value as u16;
211        }
212        _ => invalid_size!(SIZE),
213    }
214}
215
216fn sh2_vdp_cycles<const SIZE: u8>() -> u64 {
217    match SIZE {
218        OpSize::BYTE | OpSize::WORD => SH2_VDP_CYCLES,
219        OpSize::LONGWORD => 2 * SH2_VDP_CYCLES,
220        _ => invalid_size!(SIZE),
221    }
222}
223
224impl Sh2Bus {
225    #[inline]
226    pub fn create<'bus, 'cartridge, 'other>(
227        s32x_bus: &'bus mut Sega32XBus,
228        which: WhichCpu,
229        cycle_counter: u64,
230        cycle_limit: u64,
231        other_sh2: Option<(&'other mut Sh2, &'other mut u64)>,
232    ) -> Sh2BusGuard<'bus, 'cartridge, 'other> {
233        // SAFETY: Sh2Bus contains raw pointers that are created from mutable references here. The
234        // returned bus is only accessible through a guard so that the caller cannot reborrow or
235        // move the underlying values until after dropping the guard.
236        let other_sh2 = other_sh2.map(|(other_cpu, other_cycles)| OtherCpu {
237            cpu: other_cpu.into(),
238            cycle_counter: other_cycles.into(),
239        });
240
241        Sh2BusGuard {
242            bus: Sh2Bus { s32x_bus: s32x_bus.into(), other_sh2, which, cycle_counter, cycle_limit },
243            _bus_marker: PhantomData,
244            _cartridge_marker: PhantomData,
245            _other_marker: PhantomData,
246        }
247    }
248
249    fn s32x_bus(&mut self) -> &mut Sega32XBus {
250        // SAFETY: Mutable reference created from a raw pointer that was originally created from a
251        // mutable reference.
252        // This method mutably borrows the Sh2Bus so only one mutable reference can be created this
253        // way at a time. Other code should not create mutable references directly, and must not
254        // touch the pointer while a mutable reference is alive.
255        unsafe { self.s32x_bus.as_mut() }
256    }
257
258    fn s32x_bus_shared(&self) -> &Sega32XBus {
259        // SAFETY: Same as above but returns a shared reference from a shared Sh2Bus borrow
260        unsafe { self.s32x_bus.as_ref() }
261    }
262
263    // Brutal Unleashed: Above the Claw requires fairly close synchronization to prevent
264    // the game from freezing due to the master SH-2 missing a communication port write from
265    // the slave SH-2. After the slave SH-2 sees a specific value from the master SH-2, it
266    // writes to the communication port twice in quick succession, and the master SH-2 must
267    // read the first value before it's overwritten
268    fn need_to_sync_other(&self, address: u32) -> bool {
269        (0x4020..0x4030).contains(&address) && self.other_sh2.is_some()
270    }
271
272    fn maybe_sync_other_cpu(&mut self, address: u32) {
273        if !self.need_to_sync_other(address) {
274            return;
275        }
276
277        let Some(OtherCpu { cpu: mut other_cpu, cycle_counter: other_cycle_counter }) =
278            self.other_sh2
279        else {
280            return;
281        };
282
283        // SAFETY: All raw pointers used here were created from mutable references and are
284        // guaranteed non-null.
285        // The original Sh2Bus instance is not used while the other CPU is executing against the
286        // bus copy.
287        unsafe {
288            let limit = cmp::min(self.cycle_limit, self.cycle_counter);
289            let mut bus = Sh2Bus {
290                s32x_bus: self.s32x_bus,
291                which: self.which.other(),
292                cycle_counter: other_cycle_counter.read(),
293                cycle_limit: limit,
294                other_sh2: None,
295            };
296
297            while bus.cycle_counter < bus.cycle_limit {
298                other_cpu.as_mut().execute(crate::api::SH2_EXECUTION_SLICE_LEN, &mut bus);
299            }
300            other_cycle_counter.write(bus.cycle_counter);
301        }
302    }
303
304    // $00000000-$01FFFFFF: Boot ROM, 32X registers, 32X CRAM
305    fn read_00<const SIZE: u8>(&mut self, address: u32, ctx: AccessContext) -> u32 {
306        self.cycle_counter += if SIZE == OpSize::LONGWORD { 2 } else { 1 };
307
308        match address {
309            0x4000..=0x402F => {
310                // 32X system registers
311                log::trace!(
312                    "SH-2 {:?} read {} {address:08X}",
313                    self.which,
314                    OpSize::display::<SIZE>()
315                );
316
317                self.maybe_sync_other_cpu(address);
318
319                let which = self.which;
320                sh2_read_register::<SIZE>(address, |address| {
321                    let bus = self.s32x_bus();
322                    bus.registers.sh2_read(address, which, &bus.vdp)
323                })
324            }
325            0x4030..=0x403F => {
326                // 32X PWM registers
327                log::trace!(
328                    "SH-2 {:?} PWM register {} read {address:08X}",
329                    self.which,
330                    OpSize::display::<SIZE>()
331                );
332
333                sh2_read_register::<SIZE>(address, |address| {
334                    self.s32x_bus().pwm.read_register(address)
335                })
336            }
337            0x4100..=0x41FF => {
338                // 32X VDP registers
339                self.cycle_counter += sh2_vdp_cycles::<SIZE>();
340
341                if self.s32x_bus().registers.vdp_access == Access::Sh2 {
342                    sh2_read_register::<SIZE>(address, |address| {
343                        self.s32x_bus().vdp.read_register(address)
344                    })
345                } else {
346                    log::warn!(
347                        "VDP register {} read with FM=0: {address:08X}",
348                        OpSize::display::<SIZE>()
349                    );
350                    // TODO open bus?
351                    OpSize::mask::<SIZE>()
352                }
353            }
354            0x4200..=0x43FF => {
355                // 32X CRAM
356                self.cycle_counter += sh2_vdp_cycles::<SIZE>();
357
358                if self.s32x_bus().registers.vdp_access == Access::Sh2 {
359                    sh2_read_register::<SIZE>(address, |address| {
360                        self.s32x_bus().vdp.read_cram(address)
361                    })
362                } else {
363                    log::warn!("CRAM {} read with FM=0: {address:08X}", OpSize::display::<SIZE>());
364                    // TODO open bus?
365                    OpSize::mask::<SIZE>()
366                }
367            }
368            0x0000..=0x3FFF => {
369                // Boot ROM
370                match self.which {
371                    WhichCpu::Master => sh2_read_memory::<SIZE, _>(bootrom::SH2_MASTER, address),
372                    WhichCpu::Slave => sh2_read_memory::<SIZE, _>(bootrom::SH2_SLAVE, address),
373                }
374            }
375            _ => {
376                log::debug!(
377                    "SH-2 {:?} invalid address {} read {address:08X}, ctx: {ctx}",
378                    self.which,
379                    OpSize::display::<SIZE>()
380                );
381
382                // TODO open bus?
383                0
384            }
385        }
386    }
387
388    // $02000000-$03FFFFFF: Cartridge
389    fn read_02<const SIZE: u8>(&mut self, address: u32) -> u32 {
390        // SH-2s stall if they access the cartridge while RV=1
391        self.s32x_bus().stalled_on_cartridge_access |= self.s32x_bus().registers.dma.rom_to_vram;
392
393        if self.s32x_bus().registers.dma.rom_to_vram {
394            log::debug!("SH-2 {:?} accessed ROM {address:08X} while RV=1", self.which);
395        }
396
397        self.cycle_counter += if SIZE == OpSize::LONGWORD {
398            2 * (1 + SH2_CARTRIDGE_CYCLES)
399        } else {
400            1 + SH2_CARTRIDGE_CYCLES
401        };
402
403        let Some(cartridge) = &mut self.s32x_bus().cartridge else {
404            // TODO open bus?
405            return !0;
406        };
407
408        // Only A21-A0 are connected to the cartridge on the SH-2 side
409        let cartridge_addr = address & 0x3FFFFF;
410
411        match SIZE {
412            OpSize::BYTE => cartridge.read_byte(cartridge_addr, !0).into(),
413            OpSize::WORD => cartridge.read_word(cartridge_addr, !0).into(),
414            OpSize::LONGWORD => {
415                let high: u32 = cartridge.read_word(cartridge_addr, !0).into();
416                let low: u32 = cartridge.read_word(cartridge_addr | 2, !0).into();
417                low | (high << 16)
418            }
419            _ => invalid_size!(SIZE),
420        }
421    }
422
423    // $04000000-$05FFFFFF: Frame buffer
424    fn read_04<const SIZE: u8>(&mut self, address: u32, ctx: AccessContext) -> u32 {
425        self.cycle_counter += if SIZE == OpSize::LONGWORD {
426            2 * (1 + SH2_FRAME_BUFFER_READ_CYCLES)
427        } else {
428            1 + SH2_FRAME_BUFFER_READ_CYCLES
429        };
430
431        if self.s32x_bus().registers.vdp_access == Access::Sh2 {
432            sh2_read_register::<SIZE>(address, |address| {
433                self.s32x_bus().vdp.read_frame_buffer(address)
434            })
435        } else {
436            log::warn!(
437                "SH-2 {:?} frame buffer {} read with FM=0: {address:08X}, ctx: {ctx}",
438                self.which,
439                OpSize::display::<SIZE>()
440            );
441
442            // TODO open bus?
443            OpSize::mask::<SIZE>()
444        }
445    }
446
447    // $06000000-$07FFFFFF: SDRAM
448    fn read_06<const SIZE: u8>(&mut self, address: u32, ctx: AccessContext) -> u32 {
449        if address >= 0x06040000 {
450            log::debug!(
451                "SH-2 {:?} invalid {} read {address:08X}, ctx: {ctx}",
452                self.which,
453                OpSize::display::<SIZE>()
454            );
455            return 0;
456        }
457
458        // SDRAM access times are not doubled for longword reads
459        self.cycle_counter += 1 + SH2_SDRAM_READ_CYCLES;
460
461        sh2_read_memory_u16::<SIZE, _>(&self.s32x_bus().sdram, address)
462    }
463
464    // $00000000-$01FFFFFF: Boot ROM, 32X registers, 32X CRAM
465    fn write_00<const SIZE: u8>(&mut self, address: u32, value: u32, ctx: AccessContext) {
466        self.cycle_counter += if SIZE == OpSize::LONGWORD { 2 } else { 1 };
467
468        match address {
469            0x4000..=0x402F => {
470                // 32X system registers
471                log::trace!(
472                    "SH-2 {:?} {} write {address:08X} {value:08X}",
473                    self.which,
474                    OpSize::display::<SIZE>()
475                );
476
477                self.maybe_sync_other_cpu(address);
478
479                let which = self.which;
480                sh2_write_register::<SIZE>(
481                    self.s32x_bus(),
482                    address,
483                    value,
484                    |bus, address| bus.registers.sh2_read(address, which, &bus.vdp),
485                    |bus, address, word| {
486                        bus.registers.sh2_write(address, word, which, &mut bus.vdp);
487                    },
488                );
489            }
490            0x4030..=0x403F => {
491                // 32X PWM registers
492                log::trace!(
493                    "SH-2 {:?} PWM register {} write {address:08X} {value:08X}",
494                    self.which,
495                    OpSize::display::<SIZE>()
496                );
497
498                sh2_write_register::<SIZE>(
499                    self.s32x_bus(),
500                    address,
501                    value,
502                    |bus, address| bus.pwm.read_register(address),
503                    |bus, address, word| bus.pwm.sh2_write_register(address, word),
504                );
505            }
506            0x4100..=0x41FF => {
507                // 32X VDP registers
508                self.cycle_counter += sh2_vdp_cycles::<SIZE>();
509
510                if self.s32x_bus().registers.vdp_access == Access::Sh2 {
511                    sh2_write_register::<SIZE>(
512                        self.s32x_bus(),
513                        address,
514                        value,
515                        |bus, address| bus.vdp.read_register(address),
516                        |bus, address, word| bus.vdp.write_register(address, word),
517                    );
518                } else {
519                    log::warn!(
520                        "VDP register {} write with FM=0: {address:08X} {value:08X}",
521                        OpSize::display::<SIZE>()
522                    );
523                }
524            }
525            0x4200..=0x43FF => {
526                // 32X CRAM
527                self.cycle_counter += sh2_vdp_cycles::<SIZE>();
528
529                if self.s32x_bus().registers.vdp_access == Access::Sh2 {
530                    sh2_write_register::<SIZE>(
531                        self.s32x_bus(),
532                        address,
533                        value,
534                        |bus, address| bus.vdp.read_cram(address),
535                        |bus, address, word| bus.vdp.write_cram(address, word),
536                    );
537                } else {
538                    log::warn!(
539                        "CRAM {} write with FM=0: {address:08X} {value:08X}",
540                        OpSize::display::<SIZE>()
541                    );
542                }
543            }
544            _ => {
545                log::debug!(
546                    "SH-2 {:?} invalid address {} write: {address:08X} {value:08X}, ctx: {ctx}",
547                    self.which,
548                    OpSize::display::<SIZE>()
549                );
550            }
551        }
552    }
553
554    // $04000000-$05FFFFFF: Frame buffer
555    fn write_04<const SIZE: u8>(&mut self, address: u32, value: u32, ctx: AccessContext) {
556        if self.s32x_bus().registers.vdp_access != Access::Sh2 {
557            log::warn!(
558                "SH-2 {:?} frame buffer {} write with FM=0: {address:08X} {value:08X}, ctx: {ctx}",
559                self.which,
560                OpSize::display::<SIZE>()
561            );
562            return;
563        }
564
565        let cycle_counter = self.cycle_counter;
566        self.cycle_counter += self.s32x_bus().vdp.frame_buffer_write_latency(cycle_counter);
567        if SIZE == OpSize::LONGWORD {
568            let cycle_counter = self.cycle_counter;
569            self.cycle_counter += self.s32x_bus().vdp.frame_buffer_write_latency(cycle_counter);
570        }
571
572        if SIZE == OpSize::BYTE {
573            // Treat normal mapping and overwrite image identically because 0 bytes are never
574            // written in either case
575            self.s32x_bus().vdp.write_frame_buffer_byte(address, value as u8);
576            return;
577        }
578
579        sh2_write_register::<SIZE>(
580            self.s32x_bus(),
581            address,
582            value,
583            |_, _| panic!("read_word should never be called for frame buffer writes"),
584            |bus, address, word| {
585                if address & 0x20000 == 0 {
586                    // Normal frame buffer mapping
587                    bus.vdp.write_frame_buffer_word(address, word);
588                } else {
589                    // Overwrite image
590                    bus.vdp.frame_buffer_overwrite_word(address, word);
591                }
592            },
593        );
594    }
595
596    // $06000000-$07FFFFFF: SDRAM
597    fn write_06<const SIZE: u8>(&mut self, address: u32, value: u32, ctx: AccessContext) {
598        if address >= 0x06040000 {
599            log::debug!(
600                "SH-2 {:?} invalid {} write {address:08X} {value:08X}, ctx: {ctx}",
601                self.which,
602                OpSize::display::<SIZE>()
603            );
604            return;
605        }
606
607        // No latency difference between 16-bit SDRAM writes and 32-bit SDRAM writes
608        self.cycle_counter += 1 + SH2_SDRAM_WRITE_CYCLES;
609
610        sh2_write_memory_u16::<SIZE, _>(&mut self.s32x_bus().sdram, address, value);
611    }
612}
613
614impl BusInterface for Sh2Bus {
615    type DebugView<'a>
616        = DummySh2Debugger
617    where
618        Self: 'a;
619
620    #[inline]
621    fn read<const SIZE: u8>(&mut self, address: u32, ctx: AccessContext) -> u32 {
622        const BYTE_FNS: [fn(&mut Sh2Bus, u32, AccessContext) -> u32; 4] = [
623            |bus, address, ctx| bus.read_00::<{ OpSize::BYTE }>(address, ctx),
624            |bus, address, _ctx| bus.read_02::<{ OpSize::BYTE }>(address),
625            |bus, address, ctx| bus.read_04::<{ OpSize::BYTE }>(address, ctx),
626            |bus, address, ctx| bus.read_06::<{ OpSize::BYTE }>(address, ctx),
627        ];
628
629        const WORD_FNS: [fn(&mut Sh2Bus, u32, AccessContext) -> u32; 4] = [
630            |bus, address, ctx| bus.read_00::<{ OpSize::WORD }>(address, ctx),
631            |bus, address, _ctx| bus.read_02::<{ OpSize::WORD }>(address),
632            |bus, address, ctx| bus.read_04::<{ OpSize::WORD }>(address, ctx),
633            |bus, address, ctx| bus.read_06::<{ OpSize::WORD }>(address, ctx),
634        ];
635
636        const LONGWORD_FNS: [fn(&mut Sh2Bus, u32, AccessContext) -> u32; 4] = [
637            |bus, address, ctx| bus.read_00::<{ OpSize::LONGWORD }>(address, ctx),
638            |bus, address, _ctx| bus.read_02::<{ OpSize::LONGWORD }>(address),
639            |bus, address, ctx| bus.read_04::<{ OpSize::LONGWORD }>(address, ctx),
640            |bus, address, ctx| bus.read_06::<{ OpSize::LONGWORD }>(address, ctx),
641        ];
642
643        let idx = ((address >> 25) & 3) as usize;
644        match SIZE {
645            OpSize::BYTE => BYTE_FNS[idx](self, address, ctx),
646            OpSize::WORD => WORD_FNS[idx](self, address, ctx),
647            OpSize::LONGWORD => LONGWORD_FNS[idx](self, address, ctx),
648            _ => invalid_size!(SIZE),
649        }
650    }
651
652    #[inline]
653    fn read_cache_line(&mut self, address: u32, ctx: AccessContext) -> [u16; 8] {
654        if (0x06000000..0x06040000).contains(&address) {
655            // The SH-2s can read a full 16-byte cache line in 12 cycles
656            self.cycle_counter += SH2_SDRAM_READ_CYCLES + 1;
657
658            let base_addr = ((address & SDRAM_MASK & !0xF) >> 1) as usize;
659            let sdram = &self.s32x_bus().sdram;
660            return array::from_fn(|i| sdram[base_addr + i]);
661        }
662
663        array::from_fn(|i| self.read_word(address | ((i as u32) << 1), ctx))
664    }
665
666    #[inline]
667    fn write<const SIZE: u8>(&mut self, address: u32, value: u32, ctx: AccessContext) {
668        const BYTE_FNS: [fn(&mut Sh2Bus, u32, u32, AccessContext); 4] = [
669            |bus, address, value, ctx| bus.write_00::<{ OpSize::BYTE }>(address, value, ctx),
670            |_, _, _, _| {}, // SH-2s cannot write to the cartridge
671            |bus, address, value, ctx| bus.write_04::<{ OpSize::BYTE }>(address, value, ctx),
672            |bus, address, value, ctx| bus.write_06::<{ OpSize::BYTE }>(address, value, ctx),
673        ];
674
675        const WORD_FNS: [fn(&mut Sh2Bus, u32, u32, AccessContext); 4] = [
676            |bus, address, value, ctx| bus.write_00::<{ OpSize::WORD }>(address, value, ctx),
677            |_, _, _, _| {}, // SH-2s cannot write to the cartridge
678            |bus, address, value, ctx| bus.write_04::<{ OpSize::WORD }>(address, value, ctx),
679            |bus, address, value, ctx| bus.write_06::<{ OpSize::WORD }>(address, value, ctx),
680        ];
681
682        const LONGWORD_FNS: [fn(&mut Sh2Bus, u32, u32, AccessContext); 4] = [
683            |bus, address, value, ctx| bus.write_00::<{ OpSize::LONGWORD }>(address, value, ctx),
684            |_, _, _, _| {}, // SH-2s cannot write to the cartridge
685            |bus, address, value, ctx| bus.write_04::<{ OpSize::LONGWORD }>(address, value, ctx),
686            |bus, address, value, ctx| bus.write_06::<{ OpSize::LONGWORD }>(address, value, ctx),
687        ];
688
689        let idx = ((address >> 25) & 3) as usize;
690        match SIZE {
691            OpSize::BYTE => BYTE_FNS[idx](self, address, value, ctx),
692            OpSize::WORD => WORD_FNS[idx](self, address, value, ctx),
693            OpSize::LONGWORD => LONGWORD_FNS[idx](self, address, value, ctx),
694            _ => invalid_size!(SIZE),
695        }
696    }
697
698    #[inline]
699    fn reset(&self) -> bool {
700        self.s32x_bus_shared().registers.reset_sh2
701    }
702
703    #[inline]
704    fn interrupt_level(&self) -> u8 {
705        match self.which {
706            WhichCpu::Master => {
707                self.s32x_bus_shared().registers.master_interrupts.current_interrupt_level
708            }
709            WhichCpu::Slave => {
710                self.s32x_bus_shared().registers.slave_interrupts.current_interrupt_level
711            }
712        }
713    }
714
715    #[inline]
716    fn dma_request_0(&self) -> bool {
717        !self.s32x_bus_shared().registers.dma.fifo.sh2_is_empty()
718    }
719
720    #[inline]
721    fn dma_request_1(&self) -> bool {
722        self.s32x_bus_shared().pwm.dma_request_1()
723    }
724
725    #[inline]
726    fn acknowledge_dreq_1(&mut self) {
727        self.s32x_bus().pwm.acknowledge_dreq_1();
728    }
729
730    #[inline]
731    fn serial_rx(&mut self) -> Option<u8> {
732        match self.which {
733            WhichCpu::Master => self.s32x_bus().serial.slave_to_master.take(),
734            WhichCpu::Slave => self.s32x_bus().serial.master_to_slave.take(),
735        }
736    }
737
738    #[inline]
739    fn serial_tx(&mut self, value: u8) {
740        match self.which {
741            WhichCpu::Master => self.s32x_bus().serial.master_to_slave = Some(value),
742            WhichCpu::Slave => self.s32x_bus().serial.slave_to_master = Some(value),
743        }
744    }
745
746    #[inline]
747    fn increment_cycle_counter(&mut self, cycles: u64) {
748        self.cycle_counter += cycles;
749    }
750
751    #[inline]
752    fn should_stop_execution(&self) -> bool {
753        self.cycle_counter >= self.cycle_limit
754    }
755
756    sh2_emu::impl_sh2_opcode_table!(Sh2Bus);
757}
758
759#[inline]
760fn read_u8<const LEN: usize>(slice: &[u8; LEN], address: u32) -> u8 {
761    slice[(address as usize) & (LEN - 1)]
762}
763
764#[inline]
765fn read_u16<const LEN: usize>(slice: &[u8; LEN], address: u32) -> u16 {
766    let address = (address as usize) & (LEN - 1) & !1;
767    u16::from_be_bytes([slice[address], slice[address + 1]])
768}
769
770#[inline]
771fn read_u32<const LEN: usize>(slice: &[u8; LEN], address: u32) -> u32 {
772    let address = (address as usize) & (LEN - 1) & !3;
773    u32::from_be_bytes(slice[address..address + 4].try_into().unwrap())
774}