nrom refactor

This commit is contained in:
Theron 2020-01-09 22:04:10 -06:00
parent 9e9f4b6c51
commit 709e351832
7 changed files with 124 additions and 56 deletions

View File

@ -12,8 +12,18 @@ pub struct Mmc1 {
}
impl Mmc1 {
fn new(cart: Cartridge) -> Self {
Mmc1
pub fn new(cart: Cartridge) -> Self {
let m = cart.mirroring;
Mmc1 {
cart: cart,
step: 0,
shift_register: 0,
prg_low_bank: 0,
prg_high_bank: 0,
chr_low_bank: 0,
chr_high_bank: 0,
mirroring: m,
}
}
}
@ -25,4 +35,8 @@ impl Mapper for Mmc1 {
fn write(&mut self, address: usize, value: u8) {
}
fn get_mirroring(&mut self) -> Mirror {
self.mirroring
}
}

View File

@ -1,12 +1,20 @@
mod nrom;
mod mmc1;
use nrom::Nrom;
use mmc1::Mmc1;
use std::cell::RefCell;
use std::rc::Rc;
use std::io::Read;
pub trait Mapper {
fn read(&mut self, address: usize) -> u8;
fn write(&mut self, address: usize, value: u8);
fn get_mirroring(&mut self) -> Mirror;
}
#[derive(Copy, Clone, PartialEq)]
pub enum Mirror {
LowBank,
HighBank,
@ -19,10 +27,20 @@ pub enum Mirror {
pub type CpuMapperFunc = fn(&mut crate::cpu::Cpu, usize, bool) -> Option<&mut u8>;
pub type PpuMapperFunc = fn(&mut crate::ppu::Ppu, usize, bool) -> Option<&mut u8>;
pub fn get_mapper() -> Rc<RefCell<dyn Mapper>> {
let cart = Cartridge::new();
let num = cart.mapper_num;
match num {
0 => Rc::new(RefCell::new(Nrom::new(cart))),
1 => Rc::new(RefCell::new(Mmc1::new(cart))),
_ => panic!("unimplemented mapper: {}", num),
}
}
pub struct Cartridge {
prg_rom_size: usize,
chr_rom_size: usize,
pub mirroring: u8, // 0 horizontal, 1 vertical
pub mirroring: Mirror, // 0 horizontal, 1 vertical
_bb_prg_ram_present: u8, // 1: Cartridge contains battery-backed PRG RAM ($6000-7FFF) or other persistent memory
trainer_present: u8, // 1: 512-byte trainer at $7000-$71FF (stored before PRG data)
_four_screen_vram: u8, // 1: Ignore mirroring control or above mirroring bit; instead provide four-screen VRAM
@ -32,8 +50,9 @@ pub struct Cartridge {
pub chr_rom: Vec<Vec<u8>>, // 8 KiB chunks for PPU
all_data: Vec<u8>,
pub cpu_mapper_func: CpuMapperFunc,
pub ppu_mapper_func: PpuMapperFunc,
mapper_num: u8,
// pub cpu_mapper_func: CpuMapperFunc,
// pub ppu_mapper_func: PpuMapperFunc,
}
impl Cartridge {
@ -45,20 +64,21 @@ impl Cartridge {
let mut data = vec![];
f.read_to_end(&mut data).unwrap();
assert!(data[0..4] == [0x4E, 0x45, 0x53, 0x1A], "signature mismatch, not an iNES file");
let mapper = ((data[7] >> 4) << 4) + (data[6] >> 4);
let (cpu_mapper_func, ppu_mapper_func) = get_mapper_funcs(mapper);
let mapper_num = ((data[7] >> 4) << 4) + (data[6] >> 4);
// let (cpu_mapper_func, ppu_mapper_func) = get_mapper_funcs(mapper);
let mut cart = Cartridge {
prg_rom_size: data[4] as usize,
chr_rom_size: data[5] as usize,
mirroring: (data[6] & (1 << 0) != 0) as u8,
mirroring: if data[6] & (1 << 0) == 0 {Mirror::Horizontal} else {Mirror::Vertical},
_bb_prg_ram_present: (data[6] & (1 << 1) != 0) as u8,
trainer_present: (data[6] & (1 << 2) != 0) as u8,
_four_screen_vram: (data[6] & (1 << 3) != 0) as u8,
prg_rom: Vec::new(),
chr_rom: Vec::new(),
all_data: data,
cpu_mapper_func: cpu_mapper_func,
ppu_mapper_func: ppu_mapper_func,
mapper_num: mapper_num,
// cpu_mapper_func: cpu_mapper_func,
// ppu_mapper_func: ppu_mapper_func,
};
cart.fill();
cart

View File

@ -1,37 +1,63 @@
use super::Cartridge;
use super::{Cartridge, Mapper, Mirror};
pub struct Nrom {
cart: Cartridge,
// mirroring: Mirror,
}
pub fn nrom_cpu(cpu: &mut crate::cpu::Cpu, address: usize, writing: bool) -> Option<&mut u8> {
// PRG-ROM, not -RAM
if writing { return None };
// CPU $8000-$BFFF: First 16 KB of ROM.
// CPU $C000-$FFFF: Last 16 KB of ROM (NROM-256) or mirror of $8000-$BFFF (NROM-128).
let l = cpu.prg_rom.len();
match address {
0x8000..=0xBFFF => Some(&mut cpu.prg_rom[0][address % 0x4000]),
0xC000..=0xFFFF => Some(&mut cpu.prg_rom[l - 1][address % 0x4000]),
_ => panic!("bad cpu address passed to nrom mapper: 0x{:04x}", address),
impl Nrom {
pub fn new(cart: Cartridge) -> Self {
Nrom{
cart: cart,
// mirroring: cart.mirroring,
}
}
}
pub fn nrom_ppu(ppu: &mut crate::ppu::Ppu, address: usize, writing: bool) -> Option<&mut u8> {
let l = ppu.pattern_tables.len();
// NROM/mapper 0 doesn't allow writes to CHR-ROM
if writing || l == 0 { return None };
match address {
0x0000..=0x1FFF => Some(&mut ppu.pattern_tables[l-1][address]),
_ => panic!("bad ppu address passed to nrom mapper: 0x{:04x}", address),
impl Mapper for Nrom {
fn read(&mut self, address: usize) -> u8 {
let cl = self.cart.chr_rom.len();
let pl = self.cart.prg_rom.len();
let addr = address % 0x4000;
match address {
0x0000..=0x1FFF => {
if cl > 0 {
self.cart.chr_rom[cl-1][address]
} else {
0
}
},
0x8000..=0xBFFF => {
self.cart.prg_rom[0][addr]
},
0xC000..=0xFFFF => {
self.cart.prg_rom[pl-1][addr]
},
_ => panic!("bad address sent to NROM mapper: 0x{:X}", address),
}
}
}
pub fn get_mapper_funcs(mapper: u8) -> (super::CpuMapperFunc, super::PpuMapperFunc) {
match mapper {
0 => (nrom_cpu, nrom_ppu),
_ => panic!("unimplemented mapper: {}", mapper),
fn write(&mut self, address: usize, value: u8) {
let cl = self.cart.chr_rom.len();
let pl = self.cart.prg_rom.len();
let addr = address % 0x4000;
match address {
0x0000..=0x1FFF => {
if cl > 0 {
self.cart.chr_rom[cl-1][address] = value;
}
},
0x8000..=0xBFFF => {
self.cart.prg_rom[0][addr] = value;
},
0xC000..=0xFFFF => {
self.cart.prg_rom[pl-1][addr] = value;
},
_ => panic!("bad address sent to NROM mapper: 0x{:X}", address),
}
}
fn get_mirroring(&mut self) -> Mirror {
self.cart.mirroring
}
}

View File

@ -198,7 +198,8 @@ impl Cpu {
0x4000..=0x4017 => 0, // can't read from these APU registers
0x4018..=0x401F => 0, // APU and I/O functionality that is normally disabled. See CPU Test Mode.
0x4020..=0xFFFF => { // Cartridge space: PRG ROM, PRG RAM, and mapper registers
*(self.mapper_func)(self, address, false).unwrap() // unwrapping because mapper funcs won't return None for reads.
// *(self.mapper_func)(self, address, false).unwrap() // unwrapping because mapper funcs won't return None for reads.
self.mapper.borrow_mut().read(address)
},
_ => panic!("invalid read from 0x{:02x}", address),
};
@ -215,10 +216,11 @@ impl Cpu {
0x4000..=0x4017 => self.apu.write_reg(address, val), // APU stuff
0x4018..=0x401F => (), // APU and I/O functionality that is normally disabled. See CPU Test Mode.
0x4020..=0xFFFF => { // Cartridge space: PRG ROM, PRG RAM, and mapper registers
match (self.mapper_func)(self, address, true) {
Some(loc) => *loc = val,
None => (),
};
self.mapper.borrow_mut().write(address, val)
// match (self.mapper_func)(self, address, true) {
// Some(loc) => *loc = val,
// None => (),
// };
},
_ => panic!("invalid write to {:02x}", address),
}

View File

@ -11,10 +11,12 @@ mod audio;
use cpu::Cpu;
use ppu::Ppu;
use apu::Apu;
use cartridge::Cartridge;
use cartridge::{get_mapper, Cartridge, Mapper};
use input::poll_buttons;
use screen::{init_window, draw_pixel, draw_to_window};
use std::cell::RefCell;
use std::rc::Rc;
use sdl2::keyboard::Keycode;
use sdl2::event::Event;
use sdl2::pixels::PixelFormatEnum;
@ -39,10 +41,10 @@ fn main() -> Result<(), String> {
audio_device.resume();
// Initialize hardware components
let cart = Cartridge::new();
let ppu = Ppu::new(&cart);
let mapper = get_mapper();
let ppu = Ppu::new(mapper.clone());
let apu = Apu::new();
let mut cpu = Cpu::new(&cart, ppu, apu);
let mut cpu = Cpu::new(mapper.clone(), ppu, apu);
// For throttling to 60 FPS
let mut timer = Instant::now();

View File

@ -2,7 +2,7 @@ use std::rc::Rc;
use std::cell::RefCell;
fn main() {
let mapper = Rc::new(RefCell::new(Mmc1{a:32}));
let mapper = Rc::new(RefCell::new(Box::new(Mmc1{a:32})));
let ppu = Ppu{mapper: mapper.clone()};
let cpu = Cpu{mapper: mapper.clone()};

View File

@ -1,14 +1,17 @@
use crate::cartridge::Mirror;
impl super::Ppu {
pub fn read(&mut self, addr: usize) -> u8 {
let address = addr % 0x4000;
match addr {
0x0000..=0x1FFF => {
if self.pattern_tables.len() > 0 {
*(self.mapper_func)(self, address, false).unwrap() // unwrapping because mapper funcs won't return None for reads
} else {
0
}
self.mapper.borrow_mut().read(address)
// if self.pattern_tables.len() > 0 {
// *(self.mapper_func)(self, address, false).unwrap() // unwrapping because mapper funcs won't return None for reads
// } else {
// 0
// }
},
0x2000..=0x3EFF => self.read_nametable(address),
0x3F00..=0x3FFF => {
@ -24,10 +27,11 @@ impl super::Ppu {
let address = addr % 0x4000;
match addr {
0x0000..=0x1FFF => {
match (self.mapper_func)(self, address, true) {
Some(loc) => *loc = value,
None => (),
}
self.mapper.borrow_mut().write(address, value);
// match (self.mapper_func)(self, address, true) {
// Some(loc) => *loc = value,
// None => (),
// }
},
0x2000..=0x3EFF => self.write_nametable(address, value),
0x3F00..=0x3FFF => {
@ -63,7 +67,7 @@ impl super::Ppu {
fn read_nametable(&mut self, address: usize) -> u8 {
let base = address % 0x1000;
let offset = base % 0x0400;
if self.mirroring == 0 { // horizontal
if self.mapper.borrow_mut().get_mirroring() == Mirror::Horizontal {
match base {
0x0000..=0x07FF => {
self.nametable_0[offset]
@ -89,7 +93,7 @@ impl super::Ppu {
fn write_nametable(&mut self, address: usize, value: u8) {
let base = address % 0x1000;
let offset = base % 0x0400;
if self.mirroring == 0 { // horizontal
if self.mapper.borrow_mut().get_mirroring() == Mirror::Horizontal { // horizontal
match base {
0x0000..=0x07FF => {
self.nametable_0[offset] = value;