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