Some checks failed
CI / rust (push) Has been cancelled
- Add ExRAM (modes 0-1) and fill-mode nametable routing via read_nametable_byte / write_nametable_byte mapper hooks - Separate sprite and BG CHR bank sets ($5120-$5127 vs $5128-$512B); BG banks are only active in 8x16 sprite mode - Use mapper.ppu_read_sprite() for sprite tile loads so they always use the sprite bank set regardless of PPU fetch phase - Replace CPU-cycle IRQ stub with scanline-based counter matching Mesen2 hardware behaviour: fire when counter == irq_scanline at dot 2 (start of scanline), irq_scanline=0 never fires - Add Mapper::notify_frame_start() called unconditionally at the PPU frame boundary; MMC5 uses it to hard-reset the scanline counter even when rendering is disabled (e.g. during room transitions), preventing stale counter values from shifting the CHR split by 8+ scanlines - Fix CHR bank calculation for modes 0-2: use << 3/2/1 shifts instead of & !7/3/1 masking to correctly convert bank numbers to 1KB indices - Correct $5204 read: bit 7 = IRQ pending (cleared on read), bit 6 = in-frame flag; IRQ line stays asserted until $5204 is read - Dispatch $4020-$5FFF CPU reads/writes to mapper cpu_read_low / cpu_write_low so MMC5 internal registers are accessible
78 lines
1.9 KiB
Rust
78 lines
1.9 KiB
Rust
use super::*;
|
|
|
|
pub(crate) struct InesMapper118 {
|
|
mmc3: Mmc3,
|
|
}
|
|
|
|
impl InesMapper118 {
|
|
pub(crate) fn new(rom: InesRom) -> Self {
|
|
Self {
|
|
mmc3: Mmc3::new(rom),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mapper for InesMapper118 {
|
|
fn cpu_read(&self, addr: u16) -> u8 {
|
|
self.mmc3.cpu_read(addr)
|
|
}
|
|
|
|
fn cpu_write(&mut self, addr: u16, value: u8) {
|
|
self.mmc3.cpu_write(addr, value);
|
|
}
|
|
|
|
fn cpu_read_low(&mut self, addr: u16) -> Option<u8> {
|
|
self.mmc3.cpu_read_low(addr)
|
|
}
|
|
|
|
fn cpu_write_low(&mut self, addr: u16, value: u8) -> bool {
|
|
self.mmc3.cpu_write_low(addr, value)
|
|
}
|
|
|
|
fn ppu_read(&self, addr: u16) -> u8 {
|
|
self.mmc3.ppu_read(addr)
|
|
}
|
|
|
|
fn ppu_write(&mut self, addr: u16, value: u8) {
|
|
self.mmc3.ppu_write(addr, value);
|
|
}
|
|
|
|
fn mirroring(&self) -> Mirroring {
|
|
self.mmc3.mirroring()
|
|
}
|
|
|
|
fn map_nametable_addr(&self, addr: u16) -> Option<usize> {
|
|
if !(0x2000..=0x3EFF).contains(&addr) {
|
|
return None;
|
|
}
|
|
// TxSROM-class boards route CHR bank bit 7 (A17) to CIRAM A10 for NT fetches.
|
|
// The board responds to $2000-$2FFF the same as MMC3's $0000-$0FFF CHR decode.
|
|
let rel = (addr - 0x2000) & 0x0FFF;
|
|
let page = (rel / 0x0400) as usize; // NT0..NT3 -> 1KB pages 0..3
|
|
let offset = (rel & 0x03FF) as usize;
|
|
let bank = self.mmc3.chr_bank_for_1k_page(page) as u8;
|
|
let ciram_page = ((bank >> 7) & 1) as usize;
|
|
Some(ciram_page * 0x0400 + offset)
|
|
}
|
|
|
|
fn clock_scanline(&mut self) {
|
|
self.mmc3.clock_scanline();
|
|
}
|
|
|
|
fn needs_ppu_a12_clock(&self) -> bool {
|
|
self.mmc3.needs_ppu_a12_clock()
|
|
}
|
|
|
|
fn poll_irq(&mut self) -> bool {
|
|
self.mmc3.poll_irq()
|
|
}
|
|
|
|
fn save_state(&self, out: &mut Vec<u8>) {
|
|
self.mmc3.save_state(out);
|
|
}
|
|
|
|
fn load_state(&mut self, data: &[u8]) -> Result<(), String> {
|
|
self.mmc3.load_state(data)
|
|
}
|
|
}
|