memory.rsannotatedmemory.rssource1171 lines · 38.0 KB · raw

SNES internal memory and on-chip CPU internal registers/ports

3pub(crate) mod cartridge;
4pub(crate) mod dma;
5mod inputs;
7use crate::api::{CoprocessorRoms, SnesEmulatorConfig, SnesLoadResult};
8use crate::input::SnesInputs;
9use crate::memory::cartridge::Cartridge;
10use crate::memory::inputs::InputState;
11use crate::ppu::Ppu;
12use bincode::{Decode, Encode};
13use jgenesis_common::frontend::{SaveWriter, TimingMode};
14use jgenesis_common::num::{GetBit, U16Ext, U24Ext};
15use jgenesis_proc_macros::PartialClone;
16use std::num::NonZeroU64;
17use std::{array, iter};
18
19pub(crate) const MAIN_RAM_LEN: usize = 128 * 1024;

H=32.5

22const AUTO_JOYPAD_START_MCLK: u64 = 130;
24type MainRam = [u8; MAIN_RAM_LEN];
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
27pub enum Memory2Speed {
28    Fast,
29    #[default]
30    Slow,
31}
32
33impl Memory2Speed {
34    fn from_byte(byte: u8) -> Self {
35        if byte.bit(0) { Self::Fast } else { Self::Slow }
36    }
37}
38
39#[derive(Debug, Clone, Encode, Decode, PartialClone)]
40pub struct Memory {
41    #[partial_clone(partial)]
42    cartridge: Cartridge,
43    main_ram: Box<MainRam>,
44    wram_port_address: u32,
45    cpu_open_bus: u8,
46    cartridge_timing_mode: TimingMode,
47}
48
49impl Memory {
50    pub fn create<S: SaveWriter>(
51        rom: Vec<u8>,
52        initial_sram: Option<Vec<u8>>,
53        coprocessor_roms: &CoprocessorRoms,
54        forced_timing_mode: Option<TimingMode>,
55        gsu_overclock_factor: NonZeroU64,
56        save_writer: &mut S,
57    ) -> SnesLoadResult<Self> {
58        let (cartridge, cartridge_timing_mode) = Cartridge::create(
59            rom,
60            initial_sram,
61            coprocessor_roms,
62            forced_timing_mode,
63            gsu_overclock_factor,
64            save_writer,
65        )?;
66
67        log::info!("Cartridge has battery-backed SRAM: {}", cartridge.has_battery());
68
69        let main_ram: Vec<u8> = iter::repeat_with(rand::random).take(MAIN_RAM_LEN).collect();
70
71        Ok(Self {
72            cartridge,
73            main_ram: main_ram.into_boxed_slice().try_into().unwrap(),
74            wram_port_address: 0,
75            cpu_open_bus: 0,
76            cartridge_timing_mode,
77        })
78    }
79
80    pub fn read_cartridge(&mut self, address: u32) -> Option<u8> {
81        match self.cartridge.read(address) {
82            Some(value) => {
83                self.cpu_open_bus = value;
84                Some(value)
85            }
86            None => None,
87        }
88    }
89
90    pub fn write_cartridge(&mut self, address: u32, value: u8) {
91        self.cartridge.write(address, value);
92    }
93
94    pub fn cartridge_irq(&self) -> bool {
95        self.cartridge.irq()
96    }
97
98    pub fn cartridge_title(&mut self) -> String {
99        // Cartridge title is always at $00FFC0-$00FFD4 (inclusive)
100        let mut title_bytes = [0; 0xFFD4 - 0xFFC0 + 1];
101        for (i, byte) in title_bytes.iter_mut().enumerate() {
102            *byte = self.read_cartridge(0xFFC0 + i as u32).unwrap_or(0);
103        }
104
105        title_bytes
106            .into_iter()
107            .filter_map(|byte| {
108                (byte.is_ascii_whitespace()
109                    || byte.is_ascii_alphanumeric()
110                    || byte.is_ascii_punctuation())
111                .then_some(byte as char)
112            })
113            .collect()
114    }
115
116    pub fn cartridge_timing_mode(&self) -> TimingMode {
117        self.cartridge_timing_mode
118    }
119
120    pub fn read_wram(&self, address: u32) -> u8 {
121        self.main_ram[(address as usize) & (MAIN_RAM_LEN - 1)]
122    }

The whole of work RAM, indexed by offset from $7E0000.

125    pub fn wram(&self) -> &[u8; MAIN_RAM_LEN] {
126        &self.main_ram
127    }
129    pub fn wram_mut(&mut self) -> &mut [u8; MAIN_RAM_LEN] {
130        &mut self.main_ram
131    }
132
133    pub fn write_wram(&mut self, address: u32, value: u8) {
134        self.main_ram[(address as usize) & (MAIN_RAM_LEN - 1)] = value;
135    }
136
137    pub fn read_wram_port(&mut self) -> u8 {
138        let value = self.main_ram[self.wram_port_address as usize];
139        self.increment_wram_port_address();
140        value
141    }
142
143    pub fn write_wram_port(&mut self, value: u8) {
144        self.main_ram[self.wram_port_address as usize] = value;
145        self.increment_wram_port_address();
146    }
147
148    fn increment_wram_port_address(&mut self) {
149        self.wram_port_address = (self.wram_port_address + 1) & ((MAIN_RAM_LEN - 1) as u32);
150    }
151
152    pub fn write_wram_port_address_low(&mut self, value: u8) {
153        self.wram_port_address.set_low_byte(value);
154        log::trace!("WRAM port address: {:06X}", self.wram_port_address);
155    }
156
157    pub fn write_wram_port_address_mid(&mut self, value: u8) {
158        self.wram_port_address.set_mid_byte(value);
159        log::trace!("WRAM port address: {:06X}", self.wram_port_address);
160    }
161
162    pub fn write_wram_port_address_high(&mut self, value: u8) {
163        // Only 1 bit used from high byte
164        self.wram_port_address.set_high_byte(value & 0x01);
165        log::trace!("WRAM port address: {:06X}", self.wram_port_address);
166    }
167
168    pub fn take_rom(&mut self) -> Vec<u8> {
169        self.cartridge.take_rom()
170    }
171
172    pub fn take_rom_from(&mut self, other: &mut Self) {
173        self.cartridge.take_rom_from(&mut other.cartridge);
174    }
175
176    pub fn sram(&self) -> Option<&[u8]> {
177        self.cartridge.sram()
178    }
179
180    pub fn write_auxiliary_save_files<S: SaveWriter>(
181        &self,
182        save_writer: &mut S,
183    ) -> Result<(), S::Err> {
184        self.cartridge.write_auxiliary_save_files(save_writer)
185    }
186
187    pub fn has_battery_backed_sram(&self) -> bool {
188        self.cartridge.has_battery()
189    }
190
191    pub fn cpu_open_bus(&self) -> u8 {
192        self.cpu_open_bus
193    }
194
195    pub fn tick(&mut self, master_cycles_elapsed: u64) {
196        self.cartridge.tick(master_cycles_elapsed);
197    }
198
199    pub fn reset(&mut self) {
200        self.wram_port_address = 0;
201        self.cartridge.reset();
202    }
203
204    // Called when GPDMA begins, or when it starts on a new channel
205    pub fn notify_dma_start(&mut self, channel: u8, source_address: u32) {
206        self.cartridge.notify_dma_start(channel, source_address);
207    }
208
209    // Called when GPDMA completes (all channels done)
210    pub fn notify_dma_end(&mut self) {
211        self.cartridge.notify_dma_end();
212    }
213
214    pub fn update_gsu_overclock_factor(&mut self, overclock_factor: NonZeroU64) {
215        self.cartridge.update_gsu_overclock_factor(overclock_factor);
216    }
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
220enum IrqMode {
221    // No IRQs
222    #[default]
223    Off,
224    // IRQ at H=HTIME, every line
225    H,
226    // IRQ at V=VTIME + H=0
227    V,
228    // IRQ at V=VTIME + H=HTIME
229    HV,
230}
231
232impl IrqMode {
233    fn from_byte(byte: u8) -> Self {
234        match byte & 0x30 {
235            0x00 => Self::Off,
236            0x10 => Self::H,
237            0x20 => Self::V,
238            0x30 => Self::HV,
239            _ => unreachable!("value & 0x30 will always be one of the above values"),
240        }
241    }
242}
243
244#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
245enum DmaDirection {
246    AtoB,
247    #[default]
248    BtoA,
249}
250
251impl DmaDirection {
252    fn from_byte(byte: u8) -> Self {
253        if byte.bit(7) { Self::BtoA } else { Self::AtoB }
254    }
255
256    fn to_byte(self) -> u8 {
257        u8::from(self == Self::BtoA) << 7
258    }
259}
260
261#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
262enum HdmaAddressingMode {
263    Direct,
264    #[default]
265    Indirect,
266}
267
268impl HdmaAddressingMode {
269    fn from_byte(byte: u8) -> Self {
270        if byte.bit(6) { Self::Indirect } else { Self::Direct }
271    }
272
273    fn to_byte(self) -> u8 {
274        u8::from(self == Self::Indirect) << 6
275    }
276}
277
278#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
279enum DmaIncrementMode {
280    #[default]
281    Fixed0,
282    Fixed1,
283    Increment,
284    Decrement,
285}
286
287impl DmaIncrementMode {
288    fn from_byte(byte: u8) -> Self {
289        match byte & 0x18 {
290            0x00 => Self::Increment,
291            0x10 => Self::Decrement,
292            0x08 => Self::Fixed0,
293            0x18 => Self::Fixed1,
294            _ => unreachable!("value & 0x18 is always one of the above values"),
295        }
296    }
297
298    fn to_byte(self) -> u8 {
299        match self {
300            Self::Increment => 0x00,
301            Self::Decrement => 0x10,
302            Self::Fixed0 => 0x08,
303            Self::Fixed1 => 0x18,
304        }
305    }
306}
307
308#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)]
309struct IrqRegisters {
310    mode: IrqMode,
311    line: bool,
312    pending: bool,
313    htime: u16,
314    vtime: u16,
315    last_ppu_htime: u16,
316}

Registers/ports that are on the 5A22 chip but are not part of the 65816

319#[derive(Debug, Clone, Encode, Decode)]
320pub struct CpuInternalRegisters {
321    nmi_enabled: bool,
322    nmi_pending: bool,
323    irq: IrqRegisters,
324    auto_joypad_read_enabled: bool,
325    multiply_operand_l: u8,
326    multiply_operand_l_latch: u8,
327    multiply_operand_r: u8,
328    multiply_product: u16,
329    multiply_cycles_remaining: u8,
330    division_dividend: u16,
331    division_divisor: u8,
332    division_quotient: u16,
333    divide_cycles_remaining: u8,
334    memory_2_speed: Memory2Speed,
335    active_gpdma_channels: [bool; 8],
336    active_hdma_channels: [bool; 8],
337    dma_direction: [DmaDirection; 8],
338    hdma_addressing_mode: [HdmaAddressingMode; 8],
339    dma_increment_mode: [DmaIncrementMode; 8],
340    dma_transfer_unit: [u8; 8],
341    dmap_unused_bit: [bool; 8],
342    dma_bus_b_address: [u8; 8],
343    // GPDMA current address is also used as HDMA table start address
344    gpdma_current_address: [u16; 8],
345    dma_bank: [u8; 8],
346    // GPDMA byte counter is also used as HDMA indirect address
347    gpdma_byte_counter: [u16; 8],
348    hdma_indirect_bank: [u8; 8],
349    hdma_table_current_address: [u16; 8],
350    hdma_line_counter: [u8; 8],
351    unused_dma_register: [u8; 8],
352    vblank_flag: bool,
353    vblank_nmi_flag: bool,
354    hblank_flag: bool,
355    programmable_joypad_port: u8,
356    input_state: InputState,
357}
359impl CpuInternalRegisters {
360    pub fn new(config: &SnesEmulatorConfig) -> Self {
361        Self {
362            nmi_enabled: false,
363            nmi_pending: false,
364            irq: IrqRegisters::default(),
365            auto_joypad_read_enabled: false,
366            multiply_operand_l: 0xFF,
367            multiply_operand_l_latch: 0xFF,
368            multiply_operand_r: 0xFF,
369            multiply_product: 0,
370            multiply_cycles_remaining: 0,
371            division_dividend: 0xFFFF,
372            division_divisor: 0xFF,
373            division_quotient: 0,
374            divide_cycles_remaining: 0,
375            memory_2_speed: Memory2Speed::default(),
376            active_gpdma_channels: [false; 8],
377            active_hdma_channels: [false; 8],
378            dma_direction: [DmaDirection::default(); 8],
379            hdma_addressing_mode: [HdmaAddressingMode::default(); 8],
380            dma_increment_mode: [DmaIncrementMode::default(); 8],
381            dma_transfer_unit: [0x07; 8],
382            dmap_unused_bit: [true; 8],
383            dma_bus_b_address: [0xFF; 8],
384            gpdma_current_address: [0xFFFF; 8],
385            dma_bank: [0xFF; 8],
386            gpdma_byte_counter: [0xFFFF; 8],
387            hdma_indirect_bank: [0xFF; 8],
388            hdma_table_current_address: [0xFFFF; 8],
389            hdma_line_counter: [0xFF; 8],
390            unused_dma_register: [0xFF; 8],
391            vblank_flag: false,
392            vblank_nmi_flag: false,
393            hblank_flag: false,
394            programmable_joypad_port: 0xFF,
395            input_state: InputState::new(config),
396        }
397    }
398
399    pub fn read_register(&mut self, address: u32, cpu_open_bus: u8) -> Option<u8> {
400        log::trace!("Read CPU register: {address:06X}");
401
402        let value = match address {
403            0x4016 => {
404                // JOYA: Manual joypad register A
405                // Bits 7-2 are open bus
406                u8::from(self.input_state.next_manual_p1_bit()) | (cpu_open_bus & 0xFC)
407            }
408            0x4017 => {
409                // JOYB: Manual joypad register B
410                // Bits 2-4 always set
411                // Bits 7-5 are open bus
412                0x1C | u8::from(self.input_state.next_manual_p2_bit()) | (cpu_open_bus & 0xE0)
413            }
414            0x4210 => {
415                // RDNMI: VBlank NMI flag and CPU version number
416
417                // Reading this register clears the VBlank NMI flag
418                let vblank_nmi_flag = self.vblank_nmi_flag;
419                self.vblank_nmi_flag = false;
420
421                // Hardcode version number to 2
422                // Bits 6-4 are open bus
423                (u8::from(vblank_nmi_flag) << 7) | 0x02 | (cpu_open_bus & 0x70)
424            }
425            0x4211 => {
426                // TIMEUP: H/V IRQ flag
427
428                // Reading this register clears the IRQ flag
429                let irq_pending = self.irq.pending;
430                self.irq.pending = false;
431
432                // Bits 6-0 are open bus
433                (u8::from(irq_pending) << 7) | (cpu_open_bus & 0x7F)
434            }
435            0x4212 => {
436                // HVBJOY: H/V blank flags and auto joypad in-progress flag
437                // Bits 5-1 are open bus
438                (u8::from(self.vblank_flag) << 7)
439                    | (u8::from(self.hblank_flag) << 6)
440                    | (cpu_open_bus & 0x3E)
441                    | u8::from(self.input_state.auto_joypad_read_in_progress())
442            }
443            0x4213 => {
444                // RDIO: Programmable joypad I/O port (read)
445                self.programmable_joypad_port
446            }
447            0x4214 => {
448                // RDDIVL: Division quotient, low byte
449                self.division_quotient.lsb()
450            }
451            0x4215 => {
452                // RDDIVH: Division quotient, high byte
453                self.division_quotient.msb()
454            }
455            0x4216 => {
456                // RDMPYL: Multiply product / division remainder, low byte
457                self.multiply_product.lsb()
458            }
459            0x4217 => {
460                // RDMPYH: Multiply product / division remainder, high byte
461                self.multiply_product.msb()
462            }
463            0x4218 => {
464                // JOY1L: Joypad 1, low byte (auto read)
465                self.input_state.auto_joypad_p1_inputs().lsb()
466            }
467            0x4219 => {
468                // JOY1H: Joypad 1, high byte (auto read)
469                self.input_state.auto_joypad_p1_inputs().msb()
470            }
471            0x421A => {
472                // JOY2L: Joypad 2, low byte (auto read)
473                self.input_state.auto_joypad_p2_inputs().lsb()
474            }
475            0x421B => {
476                // JOY2H: Joypad 2, high byte (auto read)
477                self.input_state.auto_joypad_p2_inputs().msb()
478            }
479            0x421C..=0x421F => {
480                // JOY3L/JOY3H/JOY4L/JOY4H: Joypad 3/4 (not implemented)
481                0x00
482            }
483            0x4300..=0x437F => {
484                // DMA registers
485                return self.read_dma_register(address);
486            }
487            _ => {
488                // Open bus
489                return None;
490            }
491        };
492
493        Some(value)
494    }
495
496    pub fn write_register(&mut self, address: u32, value: u8) {
497        log::trace!("CPU internal register write: {address:06X} {value:02X}");
498
499        match address & 0xFFFF {
500            0x4016 => {
501                // JOYWR: Joypad output
502                self.input_state.set_strobe(value.bit(0));
503            }
504            0x4200 => {
505                // NMITIMEN: Interrupt enable and joypad request
506                self.auto_joypad_read_enabled = value.bit(0);
507                self.irq.mode = IrqMode::from_byte(value);
508                let nmi_enabled = value.bit(7);
509                if !self.nmi_enabled && nmi_enabled && self.vblank_nmi_flag {
510                    // Enabling NMIs while the VBlank NMI flag is set immediately triggers an NMI
511                    self.nmi_pending = true;
512                }
513                self.nmi_enabled = nmi_enabled;
514
515                // Disabling IRQs acknowledges any pending IRQ
516                if self.irq.mode == IrqMode::Off {
517                    self.irq.pending = false;
518                }
519
520                log::trace!("  Auto joypad read enabled: {}", self.auto_joypad_read_enabled);
521                log::trace!("  IRQ mode: {:?}", self.irq.mode);
522                log::trace!("  NMI enabled: {nmi_enabled}");
523            }
524            0x4201 => {
525                // WRIO: Joypad programmable I/O port (write)
526                self.programmable_joypad_port = value;
527
528                log::trace!("  Programmable joypad I/O port write: {value:02X}");
529            }
530            0x4202 => {
531                // WRMPYA: Multiplication 8-bit operand A
532                self.multiply_operand_l = value;
533
534                log::trace!("  Unsigned multiply operand A: {value:02X}");
535            }
536            0x4203 => {
537                // WRMPYB: Multiplication 8-bit operand B + start multiplication
538                if self.multiply_cycles_remaining == 0 && self.divide_cycles_remaining == 0 {
539                    self.multiply_operand_l_latch = self.multiply_operand_l;
540                    self.multiply_operand_r = value;
541
542                    // Multiplication takes 8 cycles, uses Booth's algorithm
543                    // Division quotient register should hold operand B when multiplication finishes
544                    self.multiply_cycles_remaining = 8;
545                    self.multiply_product = 0;
546                    self.division_quotient =
547                        u16::from_le_bytes([self.multiply_operand_l, self.multiply_operand_r]);
548                }
549
550                log::trace!("  Unsigned multiply operand B: {value:02X}");
551                log::trace!("  Unsigned multiply product: {:04X}", self.multiply_product);
552            }
553            0x4204 => {
554                // WRDIVL: Division 16-bit dividend, low byte
555                self.division_dividend.set_lsb(value);
556
557                log::trace!("  Unsigned divide dividend: {:04X}", self.division_dividend);
558            }
559            0x4205 => {
560                // WRDIVH: Division 16-bit dividend, high byte
561                self.division_dividend.set_msb(value);
562
563                log::trace!("  Unsigned divide dividend: {:04X}", self.division_dividend);
564            }
565            0x4206 => {
566                // WRDIVB: Division 8-bit divisor + start division
567                if self.multiply_cycles_remaining == 0 && self.divide_cycles_remaining == 0 {
568                    self.division_divisor = value;
569
570                    // Division takes 16 cycles, does long division bit-by-bit
571                    // Current remainder is stored in multiplication product register, which holds
572                    // the final remainder when division finishes
573                    self.divide_cycles_remaining = 16;
574                    self.multiply_product = self.division_dividend;
575                    self.division_quotient = 0;
576                }
577
578                log::trace!("  Unsigned divide divisor: {value:02X}");
579            }
580            0x4207 => {
581                // HTIMEL: H-count timer setting, low byte
582                self.irq.htime.set_lsb(value);
583
584                log::trace!("  HTIME: {:04X}", self.irq.htime);
585            }
586            0x4208 => {
587                // HTIMEH: H-count timer setting, high byte (really just highest bit)
588                self.irq.htime.set_msb(value & 0x01);
589
590                log::trace!("  HTIME: {:04X}", self.irq.htime);
591            }
592            0x4209 => {
593                // VTIMEL: V-count timer setting, low byte
594                self.irq.vtime.set_lsb(value);
595
596                log::trace!("  VTIME: {:04X}", self.irq.vtime);
597            }
598            0x420A => {
599                // VTIMEH: V-count timer setting, high byte (really just highest bit)
600                self.irq.vtime.set_msb(value & 0x01);
601
602                log::trace!("  VTIME: {:04X}", self.irq.vtime);
603            }
604            0x420B => {
605                // MDMAEN: Select general purpose DMA channels + start transfer (if non-zero)
606                self.active_gpdma_channels = array::from_fn(|i| value.bit(i as u8));
607
608                log::trace!("  GPDMA active channels: {value:02X}");
609            }
610            0x420C => {
611                // HDMAEN: Select HBlank DMA channels
612                self.active_hdma_channels = array::from_fn(|i| value.bit(i as u8));
613
614                log::trace!("  HDMA active channels: {value:02X}");
615            }
616            0x420D => {
617                // MEMSEL: Memory-2 waitstate control
618                self.memory_2_speed = Memory2Speed::from_byte(value);
619
620                log::trace!("  Memory-2 speed: {:?}", self.memory_2_speed);
621            }
622            address @ 0x4300..=0x437F => {
623                // DMA registers
624                self.write_dma_register(address, value);
625            }
626            _ => {
627                // Open bus; do nothing
628            }
629        }
630    }
631
632    fn read_dma_register(&self, address: u32) -> Option<u8> {
633        // Second-least significant nibble is channel
634        let channel = ((address >> 4) & 0x7) as usize;
635
636        let value = match address & 0xFF0F {
637            0x4300 => {
638                // DMAPx: DMA parameters 0-7
639                self.dma_transfer_unit[channel]
640                    | self.dma_increment_mode[channel].to_byte()
641                    | (u8::from(self.dmap_unused_bit[channel]) << 5)
642                    | self.hdma_addressing_mode[channel].to_byte()
643                    | self.dma_direction[channel].to_byte()
644            }
645            0x4301 => {
646                // BBADx: DMA bus B address
647                self.dma_bus_b_address[channel]
648            }
649            0x4302 => {
650                // A1TxL: GPDMA current address / HDMA table start address, low byte
651                self.gpdma_current_address[channel].lsb()
652            }
653            0x4303 => {
654                // A1TxH: GPDMA current address / HDMA table start address, high byte
655                self.gpdma_current_address[channel].msb()
656            }
657            0x4304 => {
658                // A1Bx: GPDMA current address / HDMA table start address, bank
659                self.dma_bank[channel]
660            }
661            0x4305 => {
662                // DASxL: GPDMA byte counter / HDMA indirect address, low byte
663                self.gpdma_byte_counter[channel].lsb()
664            }
665            0x4306 => {
666                // DASxH: GPDMA byte counter / HDMA indirect address, high byte
667                self.gpdma_byte_counter[channel].msb()
668            }
669            0x4307 => {
670                // DASBx: HDMA indirect address, bank
671                self.hdma_indirect_bank[channel]
672            }
673            0x4308 => {
674                // A2AxL: HDMA current table address, low byte
675                self.hdma_table_current_address[channel].lsb()
676            }
677            0x4309 => {
678                // A2AxH: HDMA current table address, high byte
679                self.hdma_table_current_address[channel].msb()
680            }
681            0x430A => {
682                // NTRLx: HDMA line counter
683                self.hdma_line_counter[channel]
684            }
685            0x430B | 0x430F => {
686                // Unused DMA registers; R/W byte
687                self.unused_dma_register[channel]
688            }
689            _ => {
690                // Open bus
691                return None;
692            }
693        };
694
695        Some(value)
696    }
697
698    fn write_dma_register(&mut self, address: u32, value: u8) {
699        // Second-least significant nibble is channel
700        let channel = ((address >> 4) & 0x7) as usize;
701
702        log::trace!("  DMA channel: {channel}");
703
704        match address & 0xFF0F {
705            0x4300 => {
706                // DMAPx: DMA parameters 0-7
707                self.dma_transfer_unit[channel] = value & 0x07;
708                self.dma_increment_mode[channel] = DmaIncrementMode::from_byte(value);
709                self.dmap_unused_bit[channel] = value.bit(5);
710                self.hdma_addressing_mode[channel] = HdmaAddressingMode::from_byte(value);
711                self.dma_direction[channel] = DmaDirection::from_byte(value);
712
713                log::trace!("  DMA transfer unit: {}", self.dma_transfer_unit[channel]);
714                log::trace!("  DMA increment mode: {:?}", self.dma_increment_mode[channel]);
715                log::trace!("  HDMA addressing mode: {:?}", self.hdma_addressing_mode[channel]);
716                log::trace!("  DMA direction: {:?}", self.dma_direction[channel]);
717            }
718            0x4301 => {
719                // BBADx: DMA bus B address
720                self.dma_bus_b_address[channel] = value;
721
722                log::trace!("  DMA bus B address: {value:02X}");
723            }
724            0x4302 => {
725                // A1TxL: GPDMA current address / HDMA table start address, low byte
726                self.gpdma_current_address[channel].set_lsb(value);
727
728                log::trace!(
729                    "  GPDMA current address / HDMA table start address: {:04X}",
730                    self.gpdma_current_address[channel]
731                );
732            }
733            0x4303 => {
734                // A1TxH: GPDMA current address / HDMA table start address, high byte
735                self.gpdma_current_address[channel].set_msb(value);
736
737                log::trace!(
738                    "  GPDMA current address / HDMA table start address: {:04X}",
739                    self.gpdma_current_address[channel]
740                );
741            }
742            0x4304 => {
743                // A1Bx: GPDMA current address / HDMA table start address, bank
744                self.dma_bank[channel] = value;
745
746                log::trace!(
747                    "  GPDMA current address bank / HDMA table start address bank: {value:02X}"
748                );
749            }
750            0x4305 => {
751                // DASxL: GPDMA byte counter / HDMA indirect address, low byte
752                self.gpdma_byte_counter[channel].set_lsb(value);
753
754                log::trace!(
755                    "  GPDMA byte counter / HDMA indirect address: {:04X}",
756                    self.gpdma_byte_counter[channel]
757                );
758            }
759            0x4306 => {
760                // DASxH: GPDMA byte counter / HDMA indirect address, high byte
761                self.gpdma_byte_counter[channel].set_msb(value);
762
763                log::trace!(
764                    "  GPDMA byte counter / HDMA indirect address: {:04X}",
765                    self.gpdma_byte_counter[channel]
766                );
767            }
768            0x4307 => {
769                // DASBx: HDMA indirect address, bank
770                self.hdma_indirect_bank[channel] = value;
771
772                log::trace!("  HDMA indirect address bank: {value:02X}");
773            }
774            0x4308 => {
775                // A2AxL: HDMA table current address, low byte
776                self.hdma_table_current_address[channel].set_lsb(value);
777
778                log::trace!(
779                    "  HDMA table current address: {:04X}",
780                    self.hdma_table_current_address[channel]
781                );
782            }
783            0x4309 => {
784                // A2AxH: HDMA table current address, high byte
785                self.hdma_table_current_address[channel].set_msb(value);
786
787                log::trace!(
788                    "  HDMA table current address: {:04X}",
789                    self.hdma_table_current_address[channel]
790                );
791            }
792            0x430A => {
793                // NTRLx: HDMA line counter
794                self.hdma_line_counter[channel] = value;
795
796                log::trace!("  HDMA line counter: {value:02X}");
797            }
798            0x430B | 0x430F => {
799                // Unused DMA registers; R/W byte
800                self.unused_dma_register[channel] = value;
801
802                log::trace!("  Unused DMA register: {value:02X}");
803            }
804            _ => {
805                // Open bus; do nothing
806            }
807        }
808    }
809
810    pub fn memory_2_speed(&self) -> Memory2Speed {
811        self.memory_2_speed
812    }
813
814    pub fn wrio_register(&self) -> u8 {
815        self.programmable_joypad_port
816    }
817
818    pub fn tick(&mut self, master_cycles_elapsed: u64, ppu: &Ppu, inputs: &SnesInputs) {
819        // Progress auto joypad read if it's running
820        self.input_state.tick(master_cycles_elapsed, *inputs);
821
822        // Update VBlank, HBlank, and NMI flags
823        self.update_hv_blank_flags(ppu);
824
825        // Check H/V IRQs
826        self.update_irq_line(ppu);
827
828        // Check if auto joypad read should start
829        if self.auto_joypad_read_enabled
830            && ppu.is_first_vblank_scanline()
831            && ppu.scanline_master_cycles() >= AUTO_JOYPAD_START_MCLK
832            && (ppu.scanline_master_cycles() - master_cycles_elapsed) < AUTO_JOYPAD_START_MCLK
833        {
834            self.input_state.start_auto_joypad_read();
835        }
836    }
837
838    pub fn tick_cpu_cycle(&mut self) {
839        if self.multiply_cycles_remaining == 0 && self.divide_cycles_remaining == 0 {
840            return;
841        }
842
843        if self.multiply_cycles_remaining != 0 {
844            self.tick_multiplication();
845            return;
846        }
847
848        self.tick_division();
849    }
850
851    fn tick_multiplication(&mut self) {
852        let bit = 8 - self.multiply_cycles_remaining;
853        assert!(bit < 8);
854
855        if self.multiply_operand_l_latch.bit(bit) {
856            self.multiply_product += u16::from(self.multiply_operand_r) << bit;
857        }
858
859        self.division_quotient >>= 1;
860
861        self.multiply_cycles_remaining -= 1;
862    }
863
864    fn tick_division(&mut self) {
865        let bit = self.divide_cycles_remaining - 1;
866        assert!(bit < 16);
867
868        self.division_quotient <<= 1;
869
870        let divisor: u16 = self.division_divisor.into();
871        if (self.multiply_product >> bit) >= divisor {
872            self.division_quotient |= 1;
873            self.multiply_product -= divisor << bit;
874        }
875
876        self.divide_cycles_remaining -= 1;
877    }
878
879    fn update_hv_blank_flags(&mut self, ppu: &Ppu) {
880        let vblank_flag = ppu.vblank_flag();
881        if !self.vblank_flag && vblank_flag {
882            // Start of VBlank
883            if self.nmi_enabled && !self.vblank_nmi_flag {
884                self.nmi_pending = true;
885            }
886            self.vblank_nmi_flag = true;
887        } else if self.vblank_flag && !vblank_flag {
888            // End of VBlank
889            self.vblank_nmi_flag = false;
890        }
891        self.vblank_flag = vblank_flag;
892
893        self.hblank_flag = ppu.hblank_flag();
894    }
895
896    fn update_irq_line(&mut self, ppu: &Ppu) {
897        let ppu_htime = ppu.htime_for_irq();
898        debug_assert_ne!(ppu_htime, self.irq.last_ppu_htime);
899
900        let check_h = || {
901            if ppu_htime < self.irq.last_ppu_htime {
902                // Just crossed a scanline boundary
903                self.irq.htime <= ppu_htime
904                    || (self.irq.last_ppu_htime + 1..ppu.previous_line_max_htime())
905                        .contains(&self.irq.htime)
906            } else {
907                (self.irq.last_ppu_htime + 1..=ppu_htime).contains(&self.irq.htime)
908            }
909        };
910
911        let check_v = || ppu.vtime_for_irq() == self.irq.vtime;
912
913        let check_hv = || {
914            if ppu_htime >= self.irq.last_ppu_htime {
915                // Did not cross scanline boundary
916                let h = (self.irq.last_ppu_htime + 1..=ppu_htime).contains(&self.irq.htime);
917                return h && check_v();
918            }
919
920            // Crossed scanline boundary
921            if self.irq.htime <= ppu_htime {
922                // Passed HTIME in start of current line
923                check_v()
924            } else if (self.irq.last_ppu_htime + 1..ppu.previous_line_max_htime())
925                .contains(&self.irq.htime)
926            {
927                // Passed HTIME in end of previous line
928                let ppu_vtime = ppu.vtime_for_irq();
929                let prev_vtime =
930                    if ppu_vtime == 0 { ppu.scanlines_per_frame() - 1 } else { ppu_vtime - 1 };
931                prev_vtime == self.irq.vtime
932            } else {
933                // Did not pass HTIME
934                false
935            }
936        };
937
938        let new_irq_line = match self.irq.mode {
939            IrqMode::Off => false,
940            IrqMode::H => check_h(),
941            IrqMode::V => check_v(),
942            IrqMode::HV => check_hv(),
943        };
944
945        self.irq.last_ppu_htime = ppu_htime;
946
947        // Raise IRQ for CPU any time IRQ line goes from 0 to 1
948        // Some games depend on a V IRQ triggering when they change VTIME to the current line
949        // e.g. F-1 Grand Prix, F-1 Grand Prix Part III, S.O.S.: Sink or Swim
950        self.irq.pending |= !self.irq.line && new_irq_line;
951        self.irq.line = new_irq_line;
952
953        // TODO TIMEUP reads should not clear IRQ pending in the 4 mclks following it getting set
954        // this is difficult to implement with how timing is currently handled
955    }
956
957    pub fn nmi_pending(&self) -> bool {
958        self.nmi_pending
959    }
960
961    pub fn acknowledge_nmi(&mut self) {
962        self.nmi_pending = false;
963    }
964
965    pub fn irq_pending(&self) -> bool {
966        self.irq.pending
967    }
968
969    pub fn reset(&mut self) {
970        // Reset NMITIMEN and clear any pending NMI
971        self.write_register(0x4200, 0x00);
972        self.vblank_nmi_flag = false;
973        self.nmi_pending = false;
974
975        // Reset WRIO
976        self.write_register(0x4201, 0xFF);
977
978        // Reset MDMAEN
979        self.write_register(0x420B, 0x00);
980
981        // Reset HDMAEN
982        self.write_register(0x420C, 0x00);
983
984        // Reset MEMSEL
985        self.write_register(0x420D, 0x00);
986    }
987
988    pub fn controller_hv_latch(&self) -> Option<(u16, u16)> {
989        // Controllers can only latch H/V when WRIO bit 7 is set
990        self.programmable_joypad_port.bit(7).then_some(self.input_state.hv_latch()).flatten()
991    }
992
993    pub fn reload_config(&mut self, config: &SnesEmulatorConfig) {
994        self.input_state.reload_config(config);
995    }
996}
997
998#[cfg(test)]
999mod tests {
1000    use super::*;
1001    use crate::api::SnesEmulatorConfig;
1002
1003    const NMITIMEN: u32 = 0x4200;
1004    const HTIME_L: u32 = 0x4207;
1005    const HTIME_H: u32 = 0x4208;
1006    const VTIME_L: u32 = 0x4209;
1007    const VTIME_H: u32 = 0x420A;
1008    const TIMEUP: u32 = 0x4211;
1009
1010    fn new_ppu() -> Ppu {
1011        Ppu::new(TimingMode::Ntsc, SnesEmulatorConfig::default())
1012    }
1013
1014    fn tick(cycles: u64, registers: &mut CpuInternalRegisters, ppu: &mut Ppu) {
1015        let _ = ppu.tick(cycles);
1016        registers.tick(cycles, ppu, &SnesInputs::default());
1017    }
1018
1019    #[test]
1020    fn irq_v() {
1021        let mut registers = CpuInternalRegisters::new(&SnesEmulatorConfig::default());
1022
1023        // V IRQs
1024        registers.write_register(NMITIMEN, 0x20);
1025
1026        // VTIME=101
1027        registers.write_register(VTIME_L, 101);
1028        registers.write_register(VTIME_H, 0);
1029
1030        let mut ppu = new_ppu();
1031        ppu.set_scanline(100);
1032        ppu.set_scanline_master_cycles(338 * 4 + 10);
1033
1034        // V=100 H=338
1035        registers.tick(4, &ppu, &SnesInputs::default());
1036        assert!(!registers.irq_pending());
1037
1038        // V=100 H=339
1039        tick(4, &mut registers, &mut ppu);
1040        assert!(!registers.irq_pending());
1041
1042        // V=100 H=340
1043        tick(4, &mut registers, &mut ppu);
1044        assert!(!registers.irq_pending());
1045
1046        // V=101 H=0
1047        tick(4, &mut registers, &mut ppu);
1048        assert!(registers.irq_pending());
1049
1050        // V=101 H=1
1051        tick(4, &mut registers, &mut ppu);
1052        assert!(registers.irq_pending());
1053
1054        // Read TIMEUP (acknowledge IRQ)
1055        registers.read_register(TIMEUP, 0);
1056        assert!(!registers.irq_pending());
1057
1058        // V=101 H=2
1059        tick(4, &mut registers, &mut ppu);
1060        assert!(!registers.irq_pending());
1061
1062        // V=101 H=3
1063        tick(4, &mut registers, &mut ppu);
1064        assert!(!registers.irq_pending());
1065
1066        // Change VTIME to a different line
1067        registers.write_register(VTIME_L, 42);
1068
1069        // V=101 H=4
1070        tick(4, &mut registers, &mut ppu);
1071        assert!(!registers.irq_pending());
1072
1073        // V=101 H=5
1074        tick(4, &mut registers, &mut ppu);
1075        assert!(!registers.irq_pending());
1076
1077        // Change VTIME back to current line (should trigger V IRQ)
1078        registers.write_register(VTIME_L, 101);
1079
1080        // V=101 H=6
1081        tick(4, &mut registers, &mut ppu);
1082        assert!(registers.irq_pending());
1083    }
1084
1085    #[test]
1086    fn irq_hv() {
1087        let mut registers = CpuInternalRegisters::new(&SnesEmulatorConfig::default());
1088
1089        // HV IRQs
1090        registers.write_register(NMITIMEN, 0x30);
1091
1092        // VTIME=101
1093        registers.write_register(VTIME_L, 101);
1094        registers.write_register(VTIME_H, 0);
1095
1096        // HTIME=50
1097        registers.write_register(HTIME_L, 50);
1098        registers.write_register(HTIME_H, 0);
1099
1100        let mut ppu = new_ppu();
1101        ppu.set_scanline(100);
1102        ppu.set_scanline_master_cycles(338 * 4 + 10);
1103
1104        // V=100 H=338
1105        registers.tick(4, &ppu, &SnesInputs::default());
1106        assert!(!registers.irq_pending());
1107
1108        // V=100 H=339
1109        tick(4, &mut registers, &mut ppu);
1110        assert!(!registers.irq_pending());
1111
1112        // V=100 H=340
1113        tick(4, &mut registers, &mut ppu);
1114        assert!(!registers.irq_pending());
1115
1116        // V=101 H=0 (should not trigger IRQ)
1117        tick(4, &mut registers, &mut ppu);
1118        assert!(!registers.irq_pending());
1119
1120        // V=101 H=1-49
1121        for _ in 0..49 {
1122            tick(4, &mut registers, &mut ppu);
1123            assert!(!registers.irq_pending());
1124        }
1125
1126        // V=101 H=50 (should trigger IRQ)
1127        tick(4, &mut registers, &mut ppu);
1128        assert!(registers.irq_pending());
1129
1130        // V=101 H=51
1131        tick(4, &mut registers, &mut ppu);
1132        assert!(registers.irq_pending());
1133
1134        // Read TIMEUP (acknowledge IRQ)
1135        registers.read_register(TIMEUP, 0);
1136        assert!(!registers.irq_pending());
1137
1138        // V=101 H=52 (should not trigger IRQ)
1139        tick(4, &mut registers, &mut ppu);
1140        assert!(!registers.irq_pending());
1141    }
1142
1143    #[test]
1144    fn irq_hv_end_of_line() {
1145        let mut registers = CpuInternalRegisters::new(&SnesEmulatorConfig::default());
1146
1147        // HV IRQs
1148        registers.write_register(NMITIMEN, 0x30);
1149
1150        // VTIME=100
1151        registers.write_register(VTIME_L, 100);
1152        registers.write_register(VTIME_H, 0);
1153
1154        // HTIME=339
1155        let htime = 339_u16.to_le_bytes();
1156        registers.write_register(HTIME_L, htime[0]);
1157        registers.write_register(HTIME_H, htime[1]);
1158
1159        let mut ppu = new_ppu();
1160        ppu.set_scanline(100);
1161        ppu.set_scanline_master_cycles(338 * 4 + 10);
1162
1163        // V=100 H=338
1164        registers.tick(4, &ppu, &SnesInputs::default());
1165        assert!(!registers.irq_pending());
1166
1167        // Advance past end of line (V=101 H=4)
1168        tick(7 * 4, &mut registers, &mut ppu);
1169        assert!(registers.irq_pending());
1170    }
1171}