1//! Code for emulating the bus, and more generally the NES CPU and PPU address spaces. 2//! 3//! The NES does not have a unified bus; it has two buses, a 16-bit CPU bus and a 14-bit PPU bus. 4//! The CPU can only access the PPU bus through memory-mapped I/O. 5//! 6//! CPU address mapping: 7//! * $0000-$07FF: 2KB internal RAM 8//! * $0800-$1FFF: Mirrors of internal RAM 9//! * $2000-$2007: Memory-mapped PPU registers 10//! * $2008-$3FFF: Mirrors of memory-mapped PPU registers 11//! * $4000-$4017: Memory-mapped APU and I/O registers 12//! * $4018-$401F: "Test mode" functionality that is not emulated here 13//! * $4020-$FFFF: Mapped to the cartridge board 14//! 15//! Most cartridge boards map $6000-$7FFF to PRG RAM (if present) and $8000-$FFFF to PRG ROM. Writes 16//! to $8000-$FFFF are often mapped to internal cartridge board registers. 17//! 18//! PPU address mapping: 19//! * $0000-$3EFF: Mapped to the cartridge board 20//! * $3F00-$3F1F: 32 bytes of internal palette RAM 21//! * $3F20-$3FFF: Mirrors of palette RAM 22//! 23//! While almost the entire PPU address space is controlled by the cartridge board, the PPU does 24//! expect specific address ranges to hold specific data: 25//! * $0000-$1FFF: Pattern tables (2x4KB) holding tile data 26//! * $2000-$2FFF: Nametables (4x1KB) holding background tile maps and background tile attributes 27//! * $3000-$3EFF: Mirrors of the nametables (not directly used by the PPU but the CPU can read/write here through memory-mapped I/O) 28//! 29//! Most cartridge boards contain CHR ROM or CHR RAM that is mapped into $0000-$1FFF for the pattern 30//! tables. 31//! 32//! The PPU has 2KB of internal VRAM that the cartridge board is free to map into the PPU address 33//! space however it wishes. Most boards use this VRAM for nametable data, mapping it into 34//! $2000-$2FFF (with some ranges mirrored). 35 36pub mod cartridge; 37 38use crate::api::NesEmulatorConfig; 39use crate::apu::ApuState; 40use crate::bus::cartridge::Mapper; 41use crate::graphics::TimingModeGraphicsExt; 42use crate::input::{LatchedJoypadState, NesInputDevice, NesJoypadStateExt, ZapperState}; 43use bincode::{Decode, Encode}; 44use jgenesis_common::frontend::TimingMode; 45use jgenesis_common::num::GetBit; 46use jgenesis_proc_macros::PartialClone; 47use mos6502_emu::bus::BusInterface; 48use nes_config::{NesJoypadState, Overscan}; 49use std::array; 50 51pub const CPU_RAM_START: u16 = 0x0000; 52pub const CPU_RAM_END: u16 = 0x1FFF; 53pub const CPU_RAM_MASK: u16 = 0x07FF; 54 55pub const CPU_PPU_REGISTERS_START: u16 = 0x2000; 56pub const CPU_PPU_REGISTERS_END: u16 = 0x3FFF; 57pub const CPU_PPU_REGISTERS_MASK: u16 = 0x0007; 58 59pub const CPU_IO_REGISTERS_START: u16 = 0x4000; 60pub const CPU_IO_REGISTERS_END: u16 = 0x4017; 61 62pub const CPU_IO_TEST_MODE_START: u16 = 0x4018; 63pub const CPU_IO_TEST_MODE_END: u16 = 0x401F; 64 65pub const CPU_CARTRIDGE_START: u16 = 0x4020; 66pub const CPU_CARTRIDGE_END: u16 = 0xFFFF; 67 68pub const PALETTE_RAM_MASK: u16 = 0x001F; 69 70#[derive(Debug, Clone, Copy, Default, Encode, Decode)] 71struct PendingCpuWrite { 72 address: u16, 73 value: u8, 74} 75 76#[allow(clippy::upper_case_acronyms)] 77#[derive(Debug, Clone, Copy, PartialEq, Eq)] 78pub enum PpuRegister { 79 PPUCTRL, 80 PPUMASK, 81 PPUSTATUS, 82 OAMADDR, 83 OAMDATA, 84 PPUSCROLL, 85 PPUADDR, 86 PPUDATA, 87} 88 89impl PpuRegister { 90 fn from_relative_address(relative_addr: usize) -> Option<Self> { 91 match relative_addr { 92 0x00 => Some(Self::PPUCTRL), 93 0x01 => Some(Self::PPUMASK), 94 0x02 => Some(Self::PPUSTATUS), 95 0x03 => Some(Self::OAMADDR), 96 0x04 => Some(Self::OAMDATA), 97 0x05 => Some(Self::PPUSCROLL), 98 0x06 => Some(Self::PPUADDR), 99 0x07 => Some(Self::PPUDATA), 100 _ => None, 101 } 102 } 103 104 pub const fn to_address(self) -> u16 { 105 match self { 106 Self::PPUCTRL => 0x2000, 107 Self::PPUMASK => 0x2001, 108 Self::PPUSTATUS => 0x2002, 109 Self::OAMADDR => 0x2003, 110 Self::OAMDATA => 0x2004, 111 Self::PPUSCROLL => 0x2005, 112 Self::PPUADDR => 0x2006, 113 Self::PPUDATA => 0x2007, 114 } 115 } 116} 117 118#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 119pub enum PpuWriteToggle { 120 First, 121 Second, 122} 123 124impl PpuWriteToggle { 125 fn toggle(self) -> Self { 126 match self { 127 Self::First => Self::Second, 128 Self::Second => Self::First, 129 } 130 } 131} 132 133#[allow(clippy::upper_case_acronyms)] 134#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 135pub enum PpuTrackedRegister { 136 PPUCTRL, 137 PPUSCROLL, 138 PPUADDR, 139 PPUDATA, 140} 141 142#[derive(Debug, Clone, Encode, Decode)] 143pub struct PpuRegisters { 144 ppu_ctrl: u8, 145 ppu_mask: u8, 146 ppu_status: u8, 147 oam_addr: u8, 148 ppu_data_buffer: u8, 149 ppu_status_read: bool, 150 ppu_open_bus: u8, 151 ppu_open_bus_decay_cycles: [u64; 8], 152 total_cycles: u64, 153 oam_open_bus_value: Option<u8>, 154 last_accessed_register: Option<PpuTrackedRegister>, 155 write_toggle: PpuWriteToggle, 156 reset_flag: bool, 157} 158 159impl PpuRegisters { 160 pub fn new() -> Self { 161 Self { 162 ppu_ctrl: 0, 163 ppu_mask: 0, 164 ppu_status: 0xA0, 165 oam_addr: 0, 166 ppu_data_buffer: 0, 167 ppu_status_read: false, 168 ppu_open_bus: 0, 169 ppu_open_bus_decay_cycles: [0; 8], 170 total_cycles: 0, 171 oam_open_bus_value: None, 172 last_accessed_register: None, 173 write_toggle: PpuWriteToggle::First, 174 reset_flag: false, 175 } 176 } 177 178 pub fn ppu_ctrl(&self) -> u8 { 179 self.ppu_ctrl 180 } 181 182 pub fn nmi_enabled(&self) -> bool { 183 self.ppu_ctrl.bit(7) 184 } 185 186 pub fn double_height_sprites(&self) -> bool { 187 self.ppu_ctrl.bit(5) 188 } 189 190 pub fn bg_pattern_table_address(&self) -> u16 { 191 if self.ppu_ctrl.bit(4) { 0x1000 } else { 0x0000 } 192 } 193 194 pub fn sprite_pattern_table_address(&self) -> u16 { 195 if self.ppu_ctrl.bit(3) { 0x1000 } else { 0x0000 } 196 } 197 198 pub fn ppu_data_addr_increment(&self) -> u16 { 199 if self.ppu_ctrl.bit(2) { 32 } else { 1 } 200 } 201 202 pub fn emphasize_blue(&self) -> bool { 203 self.ppu_mask.bit(7) 204 } 205 206 pub fn emphasize_green(&self, timing_mode: TimingMode) -> bool { 207 match timing_mode { 208 TimingMode::Ntsc => self.ppu_mask.bit(6), 209 TimingMode::Pal => self.ppu_mask.bit(5), 210 } 211 } 212 213 pub fn emphasize_red(&self, timing_mode: TimingMode) -> bool { 214 match timing_mode { 215 TimingMode::Ntsc => self.ppu_mask.bit(5), 216 TimingMode::Pal => self.ppu_mask.bit(6), 217 } 218 } 219 220 pub fn greyscale(&self) -> bool { 221 self.ppu_mask.bit(0) 222 } 223 224 pub fn sprites_enabled(&self) -> bool { 225 self.ppu_mask.bit(4) 226 } 227 228 pub fn bg_enabled(&self) -> bool { 229 self.ppu_mask.bit(3) 230 } 231 232 pub fn left_edge_sprites_enabled(&self) -> bool { 233 self.ppu_mask.bit(2) 234 } 235 236 pub fn left_edge_bg_enabled(&self) -> bool { 237 self.ppu_mask.bit(1) 238 } 239 240 pub fn vblank_flag(&self) -> bool { 241 self.ppu_status.bit(7) 242 } 243 244 pub fn set_vblank_flag(&mut self, vblank: bool) { 245 if vblank { 246 self.ppu_status |= 1 << 7; 247 } else { 248 self.ppu_status &= !(1 << 7); 249 } 250 } 251 252 pub fn set_sprite_0_hit(&mut self, sprite_0_hit: bool) { 253 if sprite_0_hit { 254 self.ppu_status |= 1 << 6; 255 } else { 256 self.ppu_status &= !(1 << 6); 257 } 258 } 259 260 pub fn set_sprite_overflow(&mut self, sprite_overflow: bool) { 261 if sprite_overflow { 262 self.ppu_status |= 1 << 5; 263 } else { 264 self.ppu_status &= !(1 << 5); 265 } 266 } 267 268 pub fn clear_reset_flag(&mut self) { 269 self.reset_flag = false; 270 } 271 272 pub fn take_last_accessed_register(&mut self) -> Option<PpuTrackedRegister> { 273 self.last_accessed_register.take() 274 } 275 276 pub fn get_ppu_open_bus_value(&self) -> u8 { 277 self.ppu_open_bus 278 } 279 280 pub fn set_oam_addr(&mut self, oam_addr: u8) { 281 self.oam_addr = oam_addr; 282 } 283 284 pub fn set_oam_open_bus(&mut self, value: Option<u8>) { 285 self.oam_open_bus_value = value; 286 } 287 288 pub fn get_write_toggle(&self) -> PpuWriteToggle { 289 self.write_toggle 290 } 291 292 fn tick(&mut self, interrupt_lines: &mut InterruptLines) { 293 if self.ppu_status_read { 294 self.ppu_status_read = false; 295 296 self.set_vblank_flag(false); 297 self.write_toggle = PpuWriteToggle::First; 298 } 299 300 let nmi_line = if self.vblank_flag() && self.nmi_enabled() { 301 InterruptLine::Low 302 } else { 303 InterruptLine::High 304 }; 305 interrupt_lines.ppu_set_nmi_line(nmi_line); 306 } 307} 308 309#[allow(clippy::upper_case_acronyms)] 310#[allow(non_camel_case_types)] 311#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 312pub enum IoRegister { 313 #[default] 314 SQ1_VOL, 315 SQ1_SWEEP, 316 SQ1_LO, 317 SQ1_HI, 318 SQ2_VOL, 319 SQ2_SWEEP, 320 SQ2_LO, 321 SQ2_HI, 322 TRI_LINEAR, 323 TRI_LO, 324 TRI_HI, 325 NOISE_VOL, 326 NOISE_LO, 327 NOISE_HI, 328 DMC_FREQ, 329 DMC_RAW, 330 DMC_START, 331 DMC_LEN, 332 OAMDMA, 333 SND_CHN, 334 JOY1, 335 JOY2, 336} 337 338impl IoRegister { 339 const fn to_relative_address(self) -> usize { 340 match self { 341 Self::SQ1_VOL => 0x00, 342 Self::SQ1_SWEEP => 0x01, 343 Self::SQ1_LO => 0x02, 344 Self::SQ1_HI => 0x03, 345 Self::SQ2_VOL => 0x04, 346 Self::SQ2_SWEEP => 0x05, 347 Self::SQ2_LO => 0x06, 348 Self::SQ2_HI => 0x07, 349 Self::TRI_LINEAR => 0x08, 350 Self::TRI_LO => 0x0A, 351 Self::TRI_HI => 0x0B, 352 Self::NOISE_VOL => 0x0C, 353 Self::NOISE_LO => 0x0E, 354 Self::NOISE_HI => 0x0F, 355 Self::DMC_FREQ => 0x10, 356 Self::DMC_RAW => 0x11, 357 Self::DMC_START => 0x12, 358 Self::DMC_LEN => 0x13, 359 Self::OAMDMA => 0x14, 360 Self::SND_CHN => 0x15, 361 Self::JOY1 => 0x16, 362 Self::JOY2 => 0x17, 363 } 364 } 365 366 fn from_relative_address(relative_addr: u16) -> Option<Self> { 367 match relative_addr { 368 0x00 => Some(Self::SQ1_VOL), 369 0x01 => Some(Self::SQ1_SWEEP), 370 0x02 => Some(Self::SQ1_LO), 371 0x03 => Some(Self::SQ1_HI), 372 0x04 => Some(Self::SQ2_VOL), 373 0x05 => Some(Self::SQ2_SWEEP), 374 0x06 => Some(Self::SQ2_LO), 375 0x07 => Some(Self::SQ2_HI), 376 0x08 => Some(Self::TRI_LINEAR), 377 0x0A => Some(Self::TRI_LO), 378 0x0B => Some(Self::TRI_HI), 379 0x0C => Some(Self::NOISE_VOL), 380 0x0E => Some(Self::NOISE_LO), 381 0x0F => Some(Self::NOISE_HI), 382 0x10 => Some(Self::DMC_FREQ), 383 0x11 => Some(Self::DMC_RAW), 384 0x12 => Some(Self::DMC_START), 385 0x13 => Some(Self::DMC_LEN), 386 0x14 => Some(Self::OAMDMA), 387 0x15 => Some(Self::SND_CHN), 388 0x16 => Some(Self::JOY1), 389 0x17 => Some(Self::JOY2), 390 _ => None, 391 } 392 } 393} 394 395#[derive(Debug, Clone, Encode, Decode)] 396struct ZapperBusState { 397 fire_pressed: bool, 398 position: Option<(u16, u16)>, 399 cpu_ticks_until_release: u32, 400 cpu_ticks_until_sensor_off: u32, 401} 402 403impl ZapperBusState { 404 // Roughly two frames' worth of CPU cycles 405 // In actual hardware, the bit transitions from 0 to 1 when either the trigger is full pulled or 406 // roughly 100ms after the half-pull (if the trigger is never full pulled) 407 const HALF_PULLED_CPU_TICKS: u32 = 59_661; 408 409 // Roughly 25 lines' worth of CPU cycles 410 const SENSOR_ON_CPU_TICKS: u32 = 2_841; 411 412 fn new(state: ZapperState) -> Self { 413 Self { 414 fire_pressed: state.fire, 415 position: state.position(), 416 cpu_ticks_until_release: 0, 417 cpu_ticks_until_sensor_off: 0, 418 } 419 } 420 421 fn read(&self) -> u8 { 422 let no_light_sensed_bit = u8::from(self.cpu_ticks_until_sensor_off == 0) << 3; 423 let released_bit = u8::from(self.cpu_ticks_until_release == 0) << 4; 424 425 log::trace!( 426 "Zapper read: light_sensed={} pressed={}", 427 no_light_sensed_bit == 0, 428 released_bit == 0 429 ); 430 431 no_light_sensed_bit | released_bit 432 } 433 434 fn tick_cpu(&mut self) { 435 self.cpu_ticks_until_release = self.cpu_ticks_until_release.saturating_sub(1); 436 self.cpu_ticks_until_sensor_off = self.cpu_ticks_until_sensor_off.saturating_sub(1); 437 } 438 439 fn update_buttons(&mut self, state: ZapperState) { 440 self.position = state.position(); 441 442 if !self.fire_pressed && state.fire { 443 log::debug!( 444 "Zapper fire button pressed; setting released bit to 0 for {} cycles", 445 Self::HALF_PULLED_CPU_TICKS 446 ); 447 self.cpu_ticks_until_release = Self::HALF_PULLED_CPU_TICKS; 448 } 449 450 self.fire_pressed = state.fire; 451 } 452 453 fn handle_pixel_rendered( 454 &mut self, 455 pixel: u8, 456 x: u16, 457 y: u16, 458 display_mode: TimingMode, 459 overscan: Overscan, 460 ) { 461 let Some((x, y)) = Self::adjust_frame_position(x, y, display_mode, overscan) else { 462 return; 463 }; 464 465 if self.position == Some((x, y)) { 466 log::debug!("Detected pixel {pixel:02X} at Zapper position of ({x}, {y})"); 467 468 if should_trigger_zapper_sensor(pixel) { 469 log::debug!(" Triggered Zapper light sensor"); 470 self.cpu_ticks_until_sensor_off = Self::SENSOR_ON_CPU_TICKS; 471 } 472 } 473 } 474 475 fn adjust_frame_position( 476 mut x: u16, 477 mut y: u16, 478 display_mode: TimingMode, 479 overscan: Overscan, 480 ) -> Option<(u16, u16)> { 481 let mut overflowed; 482 483 (y, overflowed) = y.overflowing_sub(display_mode.starting_row()); 484 if overflowed { 485 return None; 486 } 487 488 (y, overflowed) = y.overflowing_sub(overscan.top); 489 if overflowed { 490 return None; 491 } 492 493 (x, overflowed) = x.overflowing_sub(overscan.left); 494 if overflowed { 495 return None; 496 } 497 498 Some((x, y)) 499 } 500} 501 502fn should_trigger_zapper_sensor(pixel: u8) -> bool { 503 // Fairly arbitrary definition of what constitutes a bright enough pixel to trigger 504 // the sensor. On actual hardware this will vary by TV 505 let pixel = pixel & 0x3F; 506 pixel == 0x00 507 || (0x10..0x1D).contains(&pixel) 508 || (0x20..0x2E).contains(&pixel) 509 || (0x30..0x3E).contains(&pixel) 510} 511 512#[derive(Debug, Clone, Encode, Decode)] 513struct JoypadRegisterState { 514 joypad_state: NesJoypadState, 515 latched_bit: u8, 516 latched_joypad_state: LatchedJoypadState, 517 read_last_cycle: bool, 518 read_this_cycle: bool, 519} 520 521impl JoypadRegisterState { 522 fn new() -> Self { 523 Self { 524 joypad_state: NesJoypadState::default(), 525 latched_bit: 1, 526 latched_joypad_state: LatchedJoypadState::default(), 527 read_last_cycle: false, 528 read_this_cycle: false, 529 } 530 } 531 532 fn handle_read(&mut self) -> u8 { 533 self.read_this_cycle = true; 534 535 if !self.read_last_cycle { 536 self.latched_bit = self.latched_joypad_state.next_bit(); 537 self.latched_joypad_state = self.latched_joypad_state.shift(); 538 } 539 540 self.latched_bit 541 } 542 543 fn tick_cpu(&mut self) { 544 self.read_last_cycle = self.read_this_cycle; 545 self.read_this_cycle = false; 546 } 547 548 fn strobe(&mut self) { 549 self.latched_joypad_state = self.joypad_state.latch(); 550 } 551} 552 553#[derive(Debug, Clone, Encode, Decode)] 554pub struct IoRegisters { 555 data: [u8; 0x18], 556 dma_dirty: bool, 557 dirty_register: Option<IoRegister>, 558 snd_chn_read: bool, 559 p1: JoypadRegisterState, 560 p2: JoypadRegisterState, 561 joypad_strobe: bool, 562 zapper_state: Option<ZapperBusState>, 563 // Needed for zapper positioning 564 overscan: Overscan, 565} 566 567impl IoRegisters { 568 fn new(overscan: Overscan) -> Self { 569 Self { 570 data: [0; 0x18], 571 dma_dirty: false, 572 dirty_register: None, 573 snd_chn_read: false, 574 p1: JoypadRegisterState::new(), 575 p2: JoypadRegisterState::new(), 576 joypad_strobe: false, 577 zapper_state: None, 578 overscan, 579 } 580 } 581 582 fn read_address(&mut self, address: u16, cpu_open_bus: u8) -> Option<u8> { 583 let relative_addr = address - CPU_IO_REGISTERS_START; 584 IoRegister::from_relative_address(relative_addr) 585 .map(|register| self.read_register(register, cpu_open_bus)) 586 } 587 588 fn read_register(&mut self, register: IoRegister, cpu_open_bus: u8) -> u8 { 589 // Highest 3 bits of JOY1/JOY2 reads are open bus (normally 0x40 but not necessarily) 590 let joy_open_bus = cpu_open_bus & 0xE0; 591 592 match register { 593 IoRegister::SND_CHN => { 594 self.snd_chn_read = true; 595 // Bit 5 of SND_CHN reads is open bus 596 self.data[register.to_relative_address()] | (cpu_open_bus & (1 << 5)) 597 } 598 IoRegister::JOY1 => self.p1.handle_read() | joy_open_bus, 599 IoRegister::JOY2 => match &self.zapper_state { 600 Some(zapper_state) => zapper_state.read() | joy_open_bus, 601 None => self.p2.handle_read() | joy_open_bus, 602 }, 603 _ => { 604 // Other I/O registers are write-only 605 cpu_open_bus 606 } 607 } 608 } 609 610 fn write_address(&mut self, address: u16, value: u8) { 611 let relative_addr = address - CPU_IO_REGISTERS_START; 612 let Some(register) = IoRegister::from_relative_address(relative_addr) else { 613 return; 614 }; 615 616 self.write_register(register, value); 617 } 618 619 #[allow(clippy::manual_assert)] 620 fn write_register(&mut self, register: IoRegister, value: u8) { 621 self.data[register.to_relative_address()] = value; 622 623 if self.dirty_register.replace(register).is_some() { 624 panic!("Attempted to write an I/O register twice in the same cycle"); 625 } 626 627 match register { 628 IoRegister::JOY1 => { 629 self.joypad_strobe = value.bit(0); 630 } 631 IoRegister::OAMDMA => { 632 self.dma_dirty = true; 633 } 634 _ => {} 635 } 636 } 637 638 pub fn take_dirty_register(&mut self) -> Option<(IoRegister, u8)> { 639 self.dirty_register 640 .take() 641 .map(|register| (register, self.data[register.to_relative_address()])) 642 } 643 644 pub fn set_apu_status(&mut self, apu_status: u8) { 645 self.data[IoRegister::SND_CHN.to_relative_address()] = apu_status; 646 } 647 648 pub fn get_and_clear_snd_chn_read(&mut self) -> bool { 649 let snd_chn_read = self.snd_chn_read; 650 self.snd_chn_read = false; 651 snd_chn_read 652 } 653 654 fn tick_cpu(&mut self, apu_odd_cycle: bool) { 655 self.p1.tick_cpu(); 656 self.p2.tick_cpu(); 657 658 if self.joypad_strobe && apu_odd_cycle { 659 self.p1.strobe(); 660 self.p2.strobe(); 661 } 662 663 if let Some(zapper_state) = &mut self.zapper_state { 664 zapper_state.tick_cpu(); 665 } 666 } 667} 668 669#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 670pub enum InterruptLine { 671 High, 672 Low, 673} 674 675#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 676pub enum IrqSource { 677 ApuDmc, 678 ApuFrameCounter, 679 Mapper, 680} 681 682impl IrqSource { 683 fn to_low_pull_bit(self) -> u8 { 684 match self { 685 Self::ApuDmc => 0x01, 686 Self::ApuFrameCounter => 0x02, 687 Self::Mapper => 0x04, 688 } 689 } 690} 691 692#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 693enum IrqStatus { 694 None, 695 Pending, 696 Triggered, 697} 698 699#[derive(Debug, Clone, Encode, Decode)] 700pub struct InterruptLines { 701 nmi_line: InterruptLine, 702 next_nmi_line: InterruptLine, 703 nmi_triggered: bool, 704 irq_status: IrqStatus, 705 irq_low_pulls: u8, 706} 707 708impl InterruptLines { 709 fn new() -> Self { 710 Self { 711 nmi_line: InterruptLine::High, 712 next_nmi_line: InterruptLine::High, 713 nmi_triggered: false, 714 irq_status: IrqStatus::None, 715 irq_low_pulls: 0x00, 716 } 717 } 718 719 fn tick(&mut self) { 720 if self.nmi_line == InterruptLine::High && self.next_nmi_line == InterruptLine::Low { 721 self.nmi_triggered = true; 722 } 723 self.nmi_line = self.next_nmi_line; 724 725 let irq_line = 726 if self.irq_low_pulls != 0 { InterruptLine::Low } else { InterruptLine::High }; 727 728 match (irq_line, self.irq_status) { 729 (InterruptLine::High, _) => { 730 self.irq_status = IrqStatus::None; 731 } 732 (InterruptLine::Low, IrqStatus::None) => { 733 // IRQ interrupts need to be delayed by 1 CPU cycle 734 self.irq_status = IrqStatus::Pending; 735 } 736 (InterruptLine::Low, IrqStatus::Pending) => { 737 self.irq_status = IrqStatus::Triggered; 738 } 739 (InterruptLine::Low, IrqStatus::Triggered) => {} 740 } 741 } 742 743 pub fn nmi_triggered(&self) -> bool { 744 self.nmi_triggered 745 } 746 747 pub fn clear_nmi_triggered(&mut self) { 748 self.nmi_triggered = false; 749 } 750 751 pub fn ppu_set_nmi_line(&mut self, interrupt_line: InterruptLine) { 752 self.next_nmi_line = interrupt_line; 753 } 754 755 pub fn irq_triggered(&self) -> bool { 756 self.irq_status == IrqStatus::Triggered 757 } 758 759 pub fn set_irq_low_pull(&mut self, source: IrqSource, value: bool) { 760 if value { 761 self.irq_low_pulls |= source.to_low_pull_bit(); 762 } else { 763 self.irq_low_pulls &= !source.to_low_pull_bit(); 764 } 765 } 766} 767 768#[derive(Debug, Clone, Encode, Decode, PartialClone)] 769pub struct Bus { 770 #[partial_clone(partial)] 771 mapper: Mapper, 772 cpu_internal_ram: Box<[u8; 2048]>, 773 cpu_open_bus: u8, 774 ppu_registers: PpuRegisters, 775 io_registers: IoRegisters, 776 ppu_vram: Box<[u8; 2048]>, 777 ppu_palette_ram: [u8; 32], 778 ppu_oam: [u8; 256], 779 ppu_bus_address: u16, 780 interrupt_lines: InterruptLines, 781 pending_write: Option<PendingCpuWrite>, 782} 783 784impl Bus { 785 pub(crate) fn from_cartridge(mapper: Mapper, overscan: Overscan) -> Self { 786 const INITIAL_PALETTE_RAM: [u8; 32] = [ 787 0x09, 0x01, 0x00, 0x01, 0x00, 0x02, 0x02, 0x0D, 0x08, 0x10, 0x08, 0x24, 0x00, 0x00, 788 0x04, 0x2C, 0x09, 0x01, 0x34, 0x03, 0x00, 0x04, 0x00, 0x14, 0x08, 0x3A, 0x00, 0x02, 789 0x00, 0x20, 0x2C, 0x08, 790 ]; 791 792 Self { 793 mapper, 794 // (Somewhat) randomize initial RAM contents 795 cpu_internal_ram: Box::new(array::from_fn( 796 |_| if rand::random() { 0x00 } else { 0xFF }, 797 )), 798 cpu_open_bus: 0, 799 ppu_registers: PpuRegisters::new(), 800 io_registers: IoRegisters::new(overscan), 801 ppu_vram: Box::new(array::from_fn(|_| 0)), 802 ppu_palette_ram: INITIAL_PALETTE_RAM, 803 ppu_oam: [0; 256], 804 ppu_bus_address: 0, 805 interrupt_lines: InterruptLines::new(), 806 pending_write: None, 807 } 808 } 809 810 pub fn cpu(&mut self) -> CpuBus<'_> { 811 CpuBus(self) 812 } 813 814 pub fn ppu(&mut self) -> PpuBus<'_> { 815 PpuBus(self) 816 } 817 818 pub fn update_p1_joypad_state( 819 &mut self, 820 p1_joypad_state: NesJoypadState, 821 allow_opposing_directions: bool, 822 ) { 823 self.io_registers.p1.joypad_state = 824 p1_joypad_state.with_allow_opposing_directions(allow_opposing_directions); 825 } 826 827 pub fn update_p2_joypad_state( 828 &mut self, 829 p2_inputs: NesInputDevice, 830 allow_opposing_directions: bool, 831 ) { 832 match p2_inputs { 833 NesInputDevice::Controller(joypad_state) => { 834 self.io_registers.p2.joypad_state = 835 joypad_state.with_allow_opposing_directions(allow_opposing_directions); 836 self.io_registers.zapper_state = None; 837 } 838 NesInputDevice::Zapper(zapper_state) => { 839 match &mut self.io_registers.zapper_state { 840 Some(bus_state) => { 841 bus_state.update_buttons(zapper_state); 842 } 843 None => { 844 self.io_registers.zapper_state = Some(ZapperBusState::new(zapper_state)); 845 } 846 } 847 self.io_registers.p2.joypad_state = NesJoypadState::default(); 848 } 849 } 850 } 851 852 pub fn tick(&mut self) { 853 self.ppu_registers.tick(&mut self.interrupt_lines); 854 self.mapper.tick(self.ppu_bus_address); 855 } 856 857 pub fn tick_cpu(&mut self, apu_state: &ApuState) { 858 if let Some(write) = self.pending_write.take() { 859 self.cpu().apply_write(write.address, write.value); 860 } 861 862 self.mapper.tick_cpu(); 863 self.io_registers.tick_cpu(apu_state.is_active_cycle()); 864 865 self.ppu_registers.total_cycles += 1; 866 } 867 868 // Poll NMI/IRQ interrupt lines; this should be called once per CPU cycle, between the first 869 // and second PPU ticks 870 pub fn poll_interrupt_lines(&mut self) { 871 self.interrupt_lines.set_irq_low_pull(IrqSource::Mapper, self.mapper.interrupt_flag()); 872 873 self.interrupt_lines.tick(); 874 } 875 876 pub(crate) fn mapper(&self) -> &Mapper { 877 &self.mapper 878 } 879 880 pub(crate) fn mapper_mut(&mut self) -> &mut Mapper { 881 &mut self.mapper 882 } 883 884 pub(crate) fn move_rom_from(&mut self, other: &mut Self) { 885 self.mapper.move_rom_from(&mut other.mapper); 886 } 887 888 pub(crate) fn reload_config(&mut self, config: &NesEmulatorConfig) { 889 self.io_registers.overscan = config.overscan; 890 } 891} 892 893/// A view of the bus containing methods that are appropriate for use by the CPU and APU. 894pub struct CpuBus<'a>(&'a mut Bus); 895 896impl BusInterface for CpuBus<'_> { 897 #[inline] 898 fn read(&mut self, address: u16) -> u8 { 899 let value = match address { 900 address @ CPU_RAM_START..=CPU_RAM_END => { 901 let ram_address = address & CPU_RAM_MASK; 902 self.0.cpu_internal_ram[ram_address as usize] 903 } 904 address @ CPU_PPU_REGISTERS_START..=CPU_PPU_REGISTERS_END => { 905 let ppu_register_relative_addr = 906 (address - CPU_PPU_REGISTERS_START) & CPU_PPU_REGISTERS_MASK; 907 self.read_ppu_register_address(ppu_register_relative_addr as usize) 908 } 909 address @ CPU_IO_REGISTERS_START..=CPU_IO_REGISTERS_END => self 910 .0 911 .io_registers 912 .read_address(address, self.0.cpu_open_bus) 913 .unwrap_or(self.0.cpu_open_bus), 914 _address @ CPU_IO_TEST_MODE_START..=CPU_IO_TEST_MODE_END => self.0.cpu_open_bus, 915 address @ CPU_CARTRIDGE_START..=CPU_CARTRIDGE_END => { 916 self.0.mapper.read_cpu_address(address, self.0.cpu_open_bus) 917 } 918 }; 919 920 // $4015 reads (SND_CHN) do not update open bus (verified by AccuracyCoin open bus tests) 921 if address != 0x4015 { 922 self.0.cpu_open_bus = value; 923 } 924 925 value 926 } 927 928 #[inline] 929 #[allow(clippy::manual_assert)] 930 fn write(&mut self, address: u16, value: u8) { 931 if self.0.pending_write.replace(PendingCpuWrite { address, value }).is_some() { 932 panic!("Attempted to write twice in the same cycle"); 933 } 934 } 935 936 #[inline] 937 fn nmi(&self) -> bool { 938 self.0.interrupt_lines.nmi_triggered() 939 } 940 941 #[inline] 942 fn acknowledge_nmi(&mut self) { 943 self.0.interrupt_lines.clear_nmi_triggered(); 944 } 945 946 #[inline] 947 fn irq(&self) -> bool { 948 self.0.interrupt_lines.irq_triggered() 949 } 950} 951 952impl CpuBus<'_> { 953 fn dma_read<const DMC: bool>( 954 &mut self, 955 address: u16, 956 halted_cpu_address: u16, 957 config: &NesEmulatorConfig, 958 ) -> u8 { 959 // 2A03 register activation uses bits 0-4 from the 2A03 address bus and bits 5-15 from the 960 // 6502 address bus. 961 // 962 // For normal CPU reads, the 2A03 and 6502 addresses are obviously always the same. 963 // 964 // For DMA reads, they can be different: the 2A03 address bus is set to the DMA read address, 965 // while the 6502 address bus remains at the address that the CPU was reading from when DMA 966 // halted it. 967 // 968 // This causes bus conflicts if one of the APU registers ($4015-$4017) activates while DMA 969 // is trying to read from a different address. 970 let register_activation_addr = (address & 0x1F) | (halted_cpu_address & !0x1F); 971 if address == register_activation_addr { 972 // Bits 5-15 of the DMA address match the 6502 address 973 // Return early to prevent possible double reads of addresses with read side effects 974 return self.read(address); 975 } 976 977 if (0x4015..=0x4017).contains(®ister_activation_addr) { 978 // Bus conflict! 979 if DMC { 980 // For DMC DMA $4015 conflicts, open bus is set to the 8-bit sample, and the frame 981 // counter IRQ flag is cleared (so the $4015 read does happen). 982 // 983 // For DMC DMA $4016/$4017 conflicts, open bus is set to the highest 3 bits of the 984 // sample and the lowest 5 bits from JOY1/JOY2. 985 // 986 // In both cases, give the DMC the actual 8-bit sample to prevent possible audio 987 // corruption, even though this is probably not accurate to hardware. 988 let sample_byte = self.read(address); 989 if config.dma_dummy_joy_reads || register_activation_addr == 0x4015 { 990 self.read(register_activation_addr); 991 } 992 sample_byte 993 } else if register_activation_addr == 0x4015 { 994 // For OAM DMA $4015 conflicts, the DMA address is read (which can change open bus), 995 // but DMA gets the value from the $4015 read 996 self.read(address); 997 self.read(register_activation_addr) 998 } else { 999 // For OAM DMA $4016/$4017 conflicts, the controller port gets read, but the value 1000 // is only used if the DMA address is open bus 1001 if config.dma_dummy_joy_reads { 1002 self.read(register_activation_addr); 1003 } 1004 self.read(address) 1005 } 1006 } else if (0x4015..=0x4017).contains(&address) { 1007 // No bus conflict, but DMA tried to read from an APU register that is not activated 1008 self.0.cpu_open_bus 1009 } else { 1010 // No bus conflict and read goes through 1011 self.read(address) 1012 } 1013 } 1014 1015 pub fn oam_dma_read( 1016 &mut self, 1017 address: u16, 1018 halted_cpu_address: u16, 1019 config: &NesEmulatorConfig, 1020 ) -> u8 { 1021 self.dma_read::<false>(address, halted_cpu_address, config) 1022 } 1023 1024 pub fn dmc_dma_read( 1025 &mut self, 1026 address: u16, 1027 halted_cpu_address: u16, 1028 config: &NesEmulatorConfig, 1029 ) -> u8 { 1030 self.dma_read::<true>(address, halted_cpu_address, config) 1031 } 1032 1033 fn apply_write(&mut self, address: u16, value: u8) { 1034 self.0.cpu_open_bus = value; 1035 1036 match address { 1037 address @ CPU_RAM_START..=CPU_RAM_END => { 1038 let ram_address = address & CPU_RAM_MASK; 1039 self.0.cpu_internal_ram[ram_address as usize] = value; 1040 } 1041 address @ CPU_PPU_REGISTERS_START..=CPU_PPU_REGISTERS_END => { 1042 let ppu_register_relative_addr = 1043 (address - CPU_PPU_REGISTERS_START) & CPU_PPU_REGISTERS_MASK; 1044 self.write_ppu_register_address(ppu_register_relative_addr as usize, value); 1045 } 1046 address @ CPU_IO_REGISTERS_START..=CPU_IO_REGISTERS_END => { 1047 self.0.io_registers.write_address(address, value); 1048 } 1049 _address @ CPU_IO_TEST_MODE_START..=CPU_IO_TEST_MODE_END => {} 1050 address @ CPU_CARTRIDGE_START..=CPU_CARTRIDGE_END => { 1051 self.0.mapper.write_cpu_address(address, value); 1052 } 1053 } 1054 } 1055 1056 fn read_ppu_register_address(&mut self, relative_addr: usize) -> u8 { 1057 let Some(register) = PpuRegister::from_relative_address(relative_addr) else { 1058 panic!("invalid PPU register address: {relative_addr}"); 1059 }; 1060 1061 self.read_ppu_register(register) 1062 } 1063 1064 fn read_ppu_open_bus(&mut self) -> u8 { 1065 if self.0.ppu_registers.ppu_open_bus == 0 { 1066 return 0; 1067 } 1068 1069 for i in 0..8 { 1070 if self.0.ppu_registers.ppu_open_bus.bit(i) 1071 && self.0.ppu_registers.ppu_open_bus_decay_cycles[i as usize] 1072 <= self.0.ppu_registers.total_cycles 1073 { 1074 self.0.ppu_registers.ppu_open_bus &= !(1 << i); 1075 } 1076 } 1077 1078 self.0.ppu_registers.ppu_open_bus 1079 } 1080 1081 fn set_ppu_open_bus(&mut self, value: u8, mask: u8) { 1082 let masked = value & mask; 1083 self.0.ppu_registers.ppu_open_bus = (self.0.ppu_registers.ppu_open_bus & !mask) | masked; 1084 for i in 0..8 { 1085 if masked.bit(i) { 1086 // Decay to 0 after slightly more than half a second 1087 // Exact time varies in actual hardware but seems to always be less than a second 1088 self.0.ppu_registers.ppu_open_bus_decay_cycles[i as usize] = 1089 self.0.ppu_registers.total_cycles + 1000000; 1090 } 1091 } 1092 } 1093 1094 pub fn read_ppu_register(&mut self, register: PpuRegister) -> u8 { 1095 match register { 1096 PpuRegister::PPUCTRL 1097 | PpuRegister::PPUMASK 1098 | PpuRegister::OAMADDR 1099 | PpuRegister::PPUSCROLL 1100 | PpuRegister::PPUADDR => self.read_ppu_open_bus(), 1101 PpuRegister::PPUSTATUS => { 1102 self.0.ppu_registers.ppu_status_read = true; 1103 1104 // PPUSTATUS reads only affect bits 7-5 of open bus, bits 4-0 remain intact 1105 // and are returned as part of the read 1106 let ppu_status_high_bits = self.0.ppu_registers.ppu_status & 0xE0; 1107 let open_bus_lower_bits = self.read_ppu_open_bus() & 0x1F; 1108 self.set_ppu_open_bus(ppu_status_high_bits, 0xE0); 1109 1110 ppu_status_high_bits | open_bus_lower_bits 1111 } 1112 PpuRegister::OAMDATA => { 1113 let mut value = self 1114 .0 1115 .ppu_registers 1116 .oam_open_bus_value 1117 .unwrap_or(self.0.ppu_oam[self.0.ppu_registers.oam_addr as usize]); 1118 if self.0.ppu_registers.oam_addr & 3 == 2 { 1119 // Bits 2-4 of the third byte always read 0 1120 value &= !0x1C; 1121 } 1122 1123 self.set_ppu_open_bus(value, 0xFF); 1124 value 1125 } 1126 PpuRegister::PPUDATA => { 1127 let address = self.0.ppu_bus_address; 1128 let (data, buffer_read_address) = if address < 0x3F00 { 1129 let data = self.0.ppu_registers.ppu_data_buffer; 1130 self.set_ppu_open_bus(data, 0xFF); 1131 (data, address) 1132 } else { 1133 let palette_address = address & PALETTE_RAM_MASK; 1134 let palette_data_mask = 1135 if self.0.ppu_registers.greyscale() { 0x30 } else { 0x3F }; 1136 1137 let palette_byte = 1138 self.0.ppu_palette_ram[palette_address as usize] & palette_data_mask; 1139 let open_bus_bits = self.read_ppu_open_bus() & 0xC0; 1140 self.set_ppu_open_bus(palette_byte, 0x3F); 1141 1142 // When PPUDATA is used to read palette RAM, buffer reads mirror the nametable 1143 // data located at $2F00-$2FFF 1144 (palette_byte | open_bus_bits, address - 0x1000) 1145 }; 1146 1147 self.0.mapper.about_to_access_ppu_data(); 1148 1149 self.0.ppu_registers.ppu_data_buffer = 1150 self.0.ppu().read_address(buffer_read_address); 1151 // Reset the bus address in case the buffer read address was different from the 1152 // actual address 1153 self.0.ppu_bus_address = address; 1154 1155 self.0.ppu_registers.last_accessed_register = Some(PpuTrackedRegister::PPUDATA); 1156 1157 data 1158 } 1159 } 1160 } 1161 1162 fn write_ppu_register_address(&mut self, relative_addr: usize, value: u8) { 1163 let Some(register) = PpuRegister::from_relative_address(relative_addr) else { 1164 panic!("invalid PPU register address: {relative_addr}"); 1165 }; 1166 1167 // Writes to any memory-mapped PPU register put the value on open bus 1168 self.set_ppu_open_bus(value, 0xFF); 1169 1170 if self.0.ppu_registers.reset_flag 1171 && matches!( 1172 register, 1173 PpuRegister::PPUCTRL 1174 | PpuRegister::PPUMASK 1175 | PpuRegister::PPUSCROLL 1176 | PpuRegister::PPUADDR 1177 ) 1178 { 1179 // These registers are not writable until the first pre-render scanline after reset 1180 return; 1181 } 1182 1183 match register { 1184 PpuRegister::PPUCTRL => { 1185 self.0.ppu_registers.ppu_ctrl = value; 1186 self.0.ppu_registers.last_accessed_register = Some(PpuTrackedRegister::PPUCTRL); 1187 self.0.mapper.process_ppu_ctrl_update(value); 1188 } 1189 PpuRegister::PPUMASK => { 1190 log::trace!("BUS: PPUMASK set to {value:02X}"); 1191 self.0.ppu_registers.ppu_mask = value; 1192 self.0.mapper.process_ppu_mask_update(value); 1193 } 1194 PpuRegister::PPUSTATUS => {} 1195 PpuRegister::OAMADDR => { 1196 self.0.ppu_registers.oam_addr = value; 1197 } 1198 PpuRegister::OAMDATA => { 1199 if self.0.ppu_registers.oam_open_bus_value.is_none() { 1200 let oam_addr = self.0.ppu_registers.oam_addr; 1201 self.0.ppu_oam[oam_addr as usize] = value; 1202 self.0.ppu_registers.oam_addr = self.0.ppu_registers.oam_addr.wrapping_add(1); 1203 } 1204 } 1205 PpuRegister::PPUSCROLL => { 1206 self.0.ppu_registers.last_accessed_register = Some(PpuTrackedRegister::PPUSCROLL); 1207 self.0.ppu_registers.write_toggle = self.0.ppu_registers.write_toggle.toggle(); 1208 } 1209 PpuRegister::PPUADDR => { 1210 self.0.ppu_registers.last_accessed_register = Some(PpuTrackedRegister::PPUADDR); 1211 self.0.ppu_registers.write_toggle = self.0.ppu_registers.write_toggle.toggle(); 1212 } 1213 PpuRegister::PPUDATA => { 1214 self.0.mapper.about_to_access_ppu_data(); 1215 1216 let address = self.0.ppu_bus_address; 1217 self.0.ppu().write_address(address & 0x3FFF, value); 1218 1219 self.0.ppu_registers.last_accessed_register = Some(PpuTrackedRegister::PPUDATA); 1220 } 1221 } 1222 } 1223 1224 pub fn is_oamdma_dirty(&self) -> bool { 1225 self.0.io_registers.dma_dirty 1226 } 1227 1228 pub fn clear_oamdma_dirty(&mut self) { 1229 self.0.io_registers.dma_dirty = false; 1230 } 1231 1232 pub fn read_oamdma_for_transfer(&self) -> u8 { 1233 self.0.io_registers.data[IoRegister::OAMDMA.to_relative_address()] 1234 } 1235 1236 pub fn get_io_registers_mut(&mut self) -> &mut IoRegisters { 1237 &mut self.0.io_registers 1238 } 1239 1240 pub fn interrupt_lines(&mut self) -> &mut InterruptLines { 1241 &mut self.0.interrupt_lines 1242 } 1243} 1244 1245/// A view of the bus containing methods that are appropriate for use by the PPU. 1246pub struct PpuBus<'a>(&'a mut Bus); 1247 1248impl PpuBus<'_> { 1249 pub fn read_address(&mut self, address: u16) -> u8 { 1250 // PPU bus only has 14-bit addressing 1251 let address = address & 0x3FFF; 1252 1253 self.0.ppu_bus_address = address; 1254 1255 match address { 1256 0x0000..=0x3EFF => self.0.mapper.read_ppu_address(address, &self.0.ppu_vram), 1257 0x3F00..=0x3FFF => self.0.ppu_palette_ram[(address & PALETTE_RAM_MASK) as usize], 1258 0x4000..=0xFFFF => { 1259 unreachable!("{address} should be <= 0x3FFF after masking with 0x3FFF") 1260 } 1261 } 1262 } 1263 1264 pub fn write_address(&mut self, address: u16, value: u8) { 1265 let address = address & 0x3FFF; 1266 match address { 1267 0x0000..=0x3EFF => { 1268 self.0.mapper.write_ppu_address(address, value, &mut self.0.ppu_vram); 1269 } 1270 0x3F00..=0x3FFF => { 1271 let palette_ram_addr = (address & PALETTE_RAM_MASK) as usize; 1272 self.0.ppu_palette_ram[palette_ram_addr] = value; 1273 1274 // Sprite backdrop colors ($3F10, $3F14, $3F18, $3F1C) mirror BG backdrop colors ($3F00, $3F04, $3F08, $3F0C) 1275 // Emulate this by writing to both locations whenever either is written to. 1276 // Super Mario Bros. and Micro Machines depend on this for correct colors 1277 if palette_ram_addr & 3 == 0 { 1278 self.0.ppu_palette_ram[palette_ram_addr ^ 0x10] = value; 1279 } 1280 } 1281 0x4000..=0xFFFF => { 1282 unreachable!("{address} should be <= 0x3FFF after masking with 0x3FFF") 1283 } 1284 } 1285 } 1286 1287 pub fn get_ppu_registers(&self) -> &PpuRegisters { 1288 &self.0.ppu_registers 1289 } 1290 1291 pub fn get_ppu_registers_mut(&mut self) -> &mut PpuRegisters { 1292 &mut self.0.ppu_registers 1293 } 1294 1295 pub fn get_oam(&self) -> &[u8; 256] { 1296 &self.0.ppu_oam 1297 } 1298 1299 pub fn get_palette_ram(&self) -> &[u8; 32] { 1300 &self.0.ppu_palette_ram 1301 } 1302 1303 pub fn set_bus_address(&mut self, address: u16) { 1304 self.0.ppu_bus_address = address; 1305 } 1306 1307 pub fn handle_pixel_rendered(&mut self, pixel: u8, x: u16, y: u16, display_mode: TimingMode) { 1308 if let Some(zapper_state) = &mut self.0.io_registers.zapper_state { 1309 let overscan = self.0.io_registers.overscan; 1310 zapper_state.handle_pixel_rendered(pixel, x, y, display_mode, overscan); 1311 } 1312 } 1313 1314 pub fn reset(&mut self) { 1315 self.0.ppu_registers.ppu_ctrl = 0x00; 1316 self.0.ppu_registers.ppu_mask = 0x00; 1317 self.0.ppu_registers.write_toggle = PpuWriteToggle::First; 1318 self.0.ppu_registers.ppu_data_buffer = 0x00; 1319 self.0.ppu_registers.ppu_open_bus = 0x00; 1320 self.0.ppu_registers.reset_flag = true; 1321 self.0.mapper.reset(); 1322 } 1323} 1324 1325#[cfg(test)] 1326mod tests { 1327 use super::*; 1328 1329 #[test] 1330 fn randomized_ram_on_startup() { 1331 let mapper = cartridge::new_mmc1(vec![0; 32768]); 1332 let bus1 = Bus::from_cartridge(mapper.clone(), Overscan::default()); 1333 let bus2 = Bus::from_cartridge(mapper, Overscan::default()); 1334 1335 assert_ne!(bus1.cpu_internal_ram, bus2.cpu_internal_ram); 1336 } 1337}