From 875e493cd2ad95cbe6b3c3ebe2b5ce9db6733cff Mon Sep 17 00:00:00 2001 From: Matthew Pomes Date: Wed, 8 Apr 2026 21:15:45 -0500 Subject: [PATCH] Fix memory mapping for MMC1 --- src/mem.rs | 125 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 98 insertions(+), 27 deletions(-) diff --git a/src/mem.rs b/src/mem.rs index 4aa4f94..a43d333 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -1,8 +1,9 @@ use std::{fmt, sync::Arc}; +use bitfield::bitfield; + use crate::{ - CPUMMRegisters, PPU, apu::APU, controllers::Controllers, cpu::DmaState, - ppu::PPUMMRegisters, + CPUMMRegisters, PPU, apu::APU, controllers::Controllers, cpu::DmaState, ppu::PPUMMRegisters, }; pub trait Memory { @@ -32,7 +33,7 @@ pub enum Data { ROM(Arc<[u8]>), Mirror(u16), Reg(R), - Disabled, + // Disabled, } #[derive(Debug, Clone)] @@ -139,8 +140,7 @@ impl MemoryMap { self.read(pos + offset) // let s = &self.segments[*index]; // self.read(s.position + offset % s.size) - } - Data::Disabled => todo!(), + } // Data::Disabled => todo!(), }; } } @@ -167,8 +167,7 @@ impl MemoryMap { // let index = *index; // let s = &self.segments[index]; // self.write(s.position + offset % s.size, val) - } - Data::Disabled => todo!(), + } // Data::Disabled => todo!(), }; } } @@ -177,14 +176,14 @@ impl MemoryMap { // todo!("Open bus (${addr:04X})") } - pub fn clear(&mut self) { - for s in &mut self.segments { - match &mut s.mem { - Data::RAM(items) => items.fill(0), - _ => (), - } - } - } + // pub fn clear(&mut self) { + // for s in &mut self.segments { + // match &mut s.mem { + // Data::RAM(items) => items.fill(0), + // _ => (), + // } + // } + // } pub fn power_cycle(&mut self) -> Self { for seg in &mut self.segments { @@ -217,8 +216,7 @@ impl MemoryMap { Data::Mirror(pos) => { let offset = addr - segment.position; self.peek_val(pos + offset) - } - Data::Disabled => Err(None), + } // Data::Disabled => Err(None), }; } } @@ -244,13 +242,38 @@ impl Memory for MemoryMap { } } +bitfield! { + #[derive(Clone, Copy, PartialEq, Eq)] + struct ShiftPair(u8); + impl Debug; + high, set_high: 7, 4; + low, set_low: 3, 0; +} + +impl ShiftPair { + fn low_count(&self) -> usize { + if self.low() == 0 { 0 } else { 64 << self.low() } + } + fn high_count(&self) -> usize { + if self.high() == 0 { + 0 + } else { + 64 << self.high() + } + } +} + #[derive(Clone, Copy, PartialEq, Eq)] struct Mapper { + is_nes_2: bool, horizontal_name_table: bool, mapper: u16, sub_mapper: u8, prg_rom_size: u8, chr_rom_size: u8, + + prg_nv_ram_size: ShiftPair, + chr_nv_ram_size: ShiftPair, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -279,6 +302,8 @@ enum SegmentId { PrgBank1, ChrBank0, ChrBank1, + + NVRam, } impl fmt::Display for SegmentId { @@ -304,10 +329,11 @@ impl Mapped { // } let mapper = if nes_20 { assert_eq!(header[9], 0, "No support for larger PRG/CHR roms"); - assert_eq!(header[10], 0, "No support for PRG-RAM/EEPROM"); - assert_eq!(header[11], 0, "No support for CHR-RAM"); + // assert_eq!(header[10], 0, "No support for PRG-RAM/EEPROM"); + // assert_eq!(header[11], 0, "No support for CHR-RAM"); assert!(header[12] == 0 || header[12] == 2, "Only support NTSC NES"); Mapper { + is_nes_2: true, horizontal_name_table: header[6] & (1 << 0) == 1, mapper: ((header[6] as u16 & 0xF0) >> 4) | ((header[7] as u16 & 0xF0) >> 0) @@ -315,18 +341,39 @@ impl Mapped { sub_mapper: (header[8] & 0xF0) >> 4, prg_rom_size: header[4], chr_rom_size: header[5], + prg_nv_ram_size: ShiftPair(header[10]), + chr_nv_ram_size: ShiftPair(header[11]), } } else { Mapper { + is_nes_2: false, horizontal_name_table: header[6] & (1 << 0) == 1, mapper: ((header[6] as u16 & 0xF0) >> 4) | ((header[7] as u16 & 0xF0) >> 0), sub_mapper: 0, prg_rom_size: header[4], chr_rom_size: header[5], + prg_nv_ram_size: ShiftPair(0), + chr_nv_ram_size: ShiftPair(0), } }; let (prg_rom, rom) = rom.split_at(mapper.prg_rom_size as usize * 0x4000); - let (chr_rom, rom) = rom.split_at(mapper.chr_rom_size as usize * 0x2000); + let (chr_rom, _rom) = rom.split_at(mapper.chr_rom_size as usize * 0x2000); + println!("Mapper: {}/{}", mapper.mapper, mapper.sub_mapper); + assert_eq!( + mapper.prg_nv_ram_size.low_count(), + 0, + "No support for PRG-RAM" + ); + // assert_eq!( + // mapper.chr_nv_ram_size.low_count(), + // 0, + // "No support for CHR-RAM" + // ); + assert_eq!( + mapper.chr_nv_ram_size.high_count(), + 0, + "No support for CHR-NVRAM" + ); // assert_eq!(rom.len(), 0); // let prg_rom = &file[self.cpu_offset()..self.ppu_offset()]; let mut cpu_segments = vec![ @@ -340,6 +387,14 @@ impl Mapped { CPUMMRegisters::APU, ), ]; + // CHR-NVRAM + if mapper.prg_nv_ram_size.high_count() > 0 { + cpu_segments.push(Segment::ram( + SegmentId::NVRam, + 0x6000, + mapper.prg_nv_ram_size.high_count() as u16, + )); + } let mut ppu_segments = vec![ Segment::mirror(SegmentId::VramMirror, 0x3000, 0x0F00, 0x0000), Segment::reg( @@ -387,10 +442,22 @@ impl Mapped { 0x8000 + (0x8000 - prg_rom.len() as u16), prg_rom, )); - if chr_rom.len() == 0 { - ppu_segments.push(Segment::ram(SegmentId::PpuRam, 0, 0x2000)); + if mapper.is_nes_2 { + if mapper.chr_nv_ram_size.low_count() > 0 { + ppu_segments.push(Segment::ram( + SegmentId::PpuRam, + 0, + mapper.chr_nv_ram_size.low_count() as u16, + )); + } else { + ppu_segments.push(Segment::rom(SegmentId::PpuRom, 0, chr_rom)); + } } else { - ppu_segments.push(Segment::rom(SegmentId::PpuRom, 0, chr_rom)); + if chr_rom.len() == 0 { + ppu_segments.push(Segment::ram(SegmentId::PpuRam, 0, 0x2000)); + } else { + ppu_segments.push(Segment::rom(SegmentId::PpuRom, 0, chr_rom)); + } } Remapper::None } else if mapper.mapper == 1 && mapper.sub_mapper == 0 { @@ -550,7 +617,7 @@ impl<'a> CpuMem<'a> { *shift_reg = 0; *count = 0; } else if *count == 4 { - let val = (*shift_reg << 1) | (val & 0x01); + let val = (*shift_reg >> 1) | ((val & 0x01) << 4); if (addr & 0x6000) >> 13 == 0 { // TODO: fix mem layout if it's changed if val & 0b01100 == 0b01000 { @@ -591,6 +658,7 @@ impl<'a> CpuMem<'a> { } } else if (addr & 0x6000) >> 13 == 3 { *cur_prg_bank = (val & 0x0F) as usize; + println!("Updating ROM: new {:X}", val); match mode { MMC1Mode::Full => { let bank0 = self.mem.cpu.find(SegmentId::PrgBank0).unwrap(); @@ -600,11 +668,13 @@ impl<'a> CpuMem<'a> { } MMC1Mode::FirstFixed => { let bank1 = self.mem.cpu.find(SegmentId::PrgBank1).unwrap(); - bank1.swap_rom(prg_banks[(val & 0x0F) as usize].clone()); + // Highest bit is ignored + bank1.swap_rom(prg_banks[(val & 0x07) as usize].clone()); } MMC1Mode::LastFixed => { let bank0 = self.mem.cpu.find(SegmentId::PrgBank0).unwrap(); - bank0.swap_rom(prg_banks[(val & 0x0F) as usize].clone()); + // Highest bit is ignored + bank0.swap_rom(prg_banks[(val & 0x07) as usize].clone()); } } // TODO: handle MSB, changes some stuff... @@ -614,8 +684,9 @@ impl<'a> CpuMem<'a> { *shift_reg = 0; *count = 0; } else { - *shift_reg = (*shift_reg << 1) | (val & 0x01); + *shift_reg = (*shift_reg >> 1) | ((val & 0x01) << 4); *count += 1; + println!("Mapper SHR {:05b}", *shift_reg); } } _ => (),