SNES internal memory and on-chip CPU internal registers/ports
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.
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}