nrom refactor
This commit is contained in:
parent
9e9f4b6c51
commit
709e351832
|
@ -12,8 +12,18 @@ pub struct Mmc1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mmc1 {
|
impl Mmc1 {
|
||||||
fn new(cart: Cartridge) -> Self {
|
pub fn new(cart: Cartridge) -> Self {
|
||||||
Mmc1
|
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 write(&mut self, address: usize, value: u8) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_mirroring(&mut self) -> Mirror {
|
||||||
|
self.mirroring
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
mod nrom;
|
mod nrom;
|
||||||
|
mod mmc1;
|
||||||
|
|
||||||
|
use nrom::Nrom;
|
||||||
|
use mmc1::Mmc1;
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
pub trait Mapper {
|
pub trait Mapper {
|
||||||
fn read(&mut self, address: usize) -> u8;
|
fn read(&mut self, address: usize) -> u8;
|
||||||
fn write(&mut self, address: usize, value: u8);
|
fn write(&mut self, address: usize, value: u8);
|
||||||
|
fn get_mirroring(&mut self) -> Mirror;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum Mirror {
|
pub enum Mirror {
|
||||||
LowBank,
|
LowBank,
|
||||||
HighBank,
|
HighBank,
|
||||||
|
@ -19,10 +27,20 @@ pub enum Mirror {
|
||||||
pub type CpuMapperFunc = fn(&mut crate::cpu::Cpu, usize, bool) -> Option<&mut u8>;
|
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 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 {
|
pub struct Cartridge {
|
||||||
prg_rom_size: usize,
|
prg_rom_size: usize,
|
||||||
chr_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
|
_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)
|
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
|
_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
|
pub chr_rom: Vec<Vec<u8>>, // 8 KiB chunks for PPU
|
||||||
|
|
||||||
all_data: Vec<u8>,
|
all_data: Vec<u8>,
|
||||||
pub cpu_mapper_func: CpuMapperFunc,
|
mapper_num: u8,
|
||||||
pub ppu_mapper_func: PpuMapperFunc,
|
// pub cpu_mapper_func: CpuMapperFunc,
|
||||||
|
// pub ppu_mapper_func: PpuMapperFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cartridge {
|
impl Cartridge {
|
||||||
|
@ -45,20 +64,21 @@ impl Cartridge {
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
f.read_to_end(&mut data).unwrap();
|
f.read_to_end(&mut data).unwrap();
|
||||||
assert!(data[0..4] == [0x4E, 0x45, 0x53, 0x1A], "signature mismatch, not an iNES file");
|
assert!(data[0..4] == [0x4E, 0x45, 0x53, 0x1A], "signature mismatch, not an iNES file");
|
||||||
let mapper = ((data[7] >> 4) << 4) + (data[6] >> 4);
|
let mapper_num = ((data[7] >> 4) << 4) + (data[6] >> 4);
|
||||||
let (cpu_mapper_func, ppu_mapper_func) = get_mapper_funcs(mapper);
|
// let (cpu_mapper_func, ppu_mapper_func) = get_mapper_funcs(mapper);
|
||||||
let mut cart = Cartridge {
|
let mut cart = Cartridge {
|
||||||
prg_rom_size: data[4] as usize,
|
prg_rom_size: data[4] as usize,
|
||||||
chr_rom_size: data[5] 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,
|
_bb_prg_ram_present: (data[6] & (1 << 1) != 0) as u8,
|
||||||
trainer_present: (data[6] & (1 << 2) != 0) as u8,
|
trainer_present: (data[6] & (1 << 2) != 0) as u8,
|
||||||
_four_screen_vram: (data[6] & (1 << 3) != 0) as u8,
|
_four_screen_vram: (data[6] & (1 << 3) != 0) as u8,
|
||||||
prg_rom: Vec::new(),
|
prg_rom: Vec::new(),
|
||||||
chr_rom: Vec::new(),
|
chr_rom: Vec::new(),
|
||||||
all_data: data,
|
all_data: data,
|
||||||
cpu_mapper_func: cpu_mapper_func,
|
mapper_num: mapper_num,
|
||||||
ppu_mapper_func: ppu_mapper_func,
|
// cpu_mapper_func: cpu_mapper_func,
|
||||||
|
// ppu_mapper_func: ppu_mapper_func,
|
||||||
};
|
};
|
||||||
cart.fill();
|
cart.fill();
|
||||||
cart
|
cart
|
||||||
|
|
|
@ -1,37 +1,63 @@
|
||||||
use super::Cartridge;
|
use super::{Cartridge, Mapper, Mirror};
|
||||||
|
|
||||||
pub struct Nrom {
|
pub struct Nrom {
|
||||||
cart: Cartridge,
|
cart: Cartridge,
|
||||||
|
// mirroring: Mirror,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Nrom {
|
||||||
pub fn nrom_cpu(cpu: &mut crate::cpu::Cpu, address: usize, writing: bool) -> Option<&mut u8> {
|
pub fn new(cart: Cartridge) -> Self {
|
||||||
// PRG-ROM, not -RAM
|
Nrom{
|
||||||
if writing { return None };
|
cart: cart,
|
||||||
|
// mirroring: cart.mirroring,
|
||||||
// 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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nrom_ppu(ppu: &mut crate::ppu::Ppu, address: usize, writing: bool) -> Option<&mut u8> {
|
impl Mapper for Nrom {
|
||||||
let l = ppu.pattern_tables.len();
|
fn read(&mut self, address: usize) -> u8 {
|
||||||
// NROM/mapper 0 doesn't allow writes to CHR-ROM
|
let cl = self.cart.chr_rom.len();
|
||||||
if writing || l == 0 { return None };
|
let pl = self.cart.prg_rom.len();
|
||||||
match address {
|
let addr = address % 0x4000;
|
||||||
0x0000..=0x1FFF => Some(&mut ppu.pattern_tables[l-1][address]),
|
match address {
|
||||||
_ => panic!("bad ppu address passed to nrom mapper: 0x{:04x}", 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) {
|
fn write(&mut self, address: usize, value: u8) {
|
||||||
match mapper {
|
let cl = self.cart.chr_rom.len();
|
||||||
0 => (nrom_cpu, nrom_ppu),
|
let pl = self.cart.prg_rom.len();
|
||||||
_ => panic!("unimplemented mapper: {}", mapper),
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,7 +198,8 @@ impl Cpu {
|
||||||
0x4000..=0x4017 => 0, // can't read from these APU registers
|
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.
|
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
|
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),
|
_ => panic!("invalid read from 0x{:02x}", address),
|
||||||
};
|
};
|
||||||
|
@ -215,10 +216,11 @@ impl Cpu {
|
||||||
0x4000..=0x4017 => self.apu.write_reg(address, val), // APU stuff
|
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.
|
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
|
0x4020..=0xFFFF => { // Cartridge space: PRG ROM, PRG RAM, and mapper registers
|
||||||
match (self.mapper_func)(self, address, true) {
|
self.mapper.borrow_mut().write(address, val)
|
||||||
Some(loc) => *loc = val,
|
// match (self.mapper_func)(self, address, true) {
|
||||||
None => (),
|
// Some(loc) => *loc = val,
|
||||||
};
|
// None => (),
|
||||||
|
// };
|
||||||
},
|
},
|
||||||
_ => panic!("invalid write to {:02x}", address),
|
_ => panic!("invalid write to {:02x}", address),
|
||||||
}
|
}
|
||||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -11,10 +11,12 @@ mod audio;
|
||||||
use cpu::Cpu;
|
use cpu::Cpu;
|
||||||
use ppu::Ppu;
|
use ppu::Ppu;
|
||||||
use apu::Apu;
|
use apu::Apu;
|
||||||
use cartridge::Cartridge;
|
use cartridge::{get_mapper, Cartridge, Mapper};
|
||||||
use input::poll_buttons;
|
use input::poll_buttons;
|
||||||
use screen::{init_window, draw_pixel, draw_to_window};
|
use screen::{init_window, draw_pixel, draw_to_window};
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
use sdl2::event::Event;
|
use sdl2::event::Event;
|
||||||
use sdl2::pixels::PixelFormatEnum;
|
use sdl2::pixels::PixelFormatEnum;
|
||||||
|
@ -39,10 +41,10 @@ fn main() -> Result<(), String> {
|
||||||
audio_device.resume();
|
audio_device.resume();
|
||||||
|
|
||||||
// Initialize hardware components
|
// Initialize hardware components
|
||||||
let cart = Cartridge::new();
|
let mapper = get_mapper();
|
||||||
let ppu = Ppu::new(&cart);
|
let ppu = Ppu::new(mapper.clone());
|
||||||
let apu = Apu::new();
|
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
|
// For throttling to 60 FPS
|
||||||
let mut timer = Instant::now();
|
let mut timer = Instant::now();
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
fn main() {
|
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 ppu = Ppu{mapper: mapper.clone()};
|
||||||
let cpu = Cpu{mapper: mapper.clone()};
|
let cpu = Cpu{mapper: mapper.clone()};
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
|
use crate::cartridge::Mirror;
|
||||||
|
|
||||||
impl super::Ppu {
|
impl super::Ppu {
|
||||||
|
|
||||||
pub fn read(&mut self, addr: usize) -> u8 {
|
pub fn read(&mut self, addr: usize) -> u8 {
|
||||||
let address = addr % 0x4000;
|
let address = addr % 0x4000;
|
||||||
match addr {
|
match addr {
|
||||||
0x0000..=0x1FFF => {
|
0x0000..=0x1FFF => {
|
||||||
if self.pattern_tables.len() > 0 {
|
self.mapper.borrow_mut().read(address)
|
||||||
*(self.mapper_func)(self, address, false).unwrap() // unwrapping because mapper funcs won't return None for reads
|
// if self.pattern_tables.len() > 0 {
|
||||||
} else {
|
// *(self.mapper_func)(self, address, false).unwrap() // unwrapping because mapper funcs won't return None for reads
|
||||||
0
|
// } else {
|
||||||
}
|
// 0
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
0x2000..=0x3EFF => self.read_nametable(address),
|
0x2000..=0x3EFF => self.read_nametable(address),
|
||||||
0x3F00..=0x3FFF => {
|
0x3F00..=0x3FFF => {
|
||||||
|
@ -24,10 +27,11 @@ impl super::Ppu {
|
||||||
let address = addr % 0x4000;
|
let address = addr % 0x4000;
|
||||||
match addr {
|
match addr {
|
||||||
0x0000..=0x1FFF => {
|
0x0000..=0x1FFF => {
|
||||||
match (self.mapper_func)(self, address, true) {
|
self.mapper.borrow_mut().write(address, value);
|
||||||
Some(loc) => *loc = value,
|
// match (self.mapper_func)(self, address, true) {
|
||||||
None => (),
|
// Some(loc) => *loc = value,
|
||||||
}
|
// None => (),
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
0x2000..=0x3EFF => self.write_nametable(address, value),
|
0x2000..=0x3EFF => self.write_nametable(address, value),
|
||||||
0x3F00..=0x3FFF => {
|
0x3F00..=0x3FFF => {
|
||||||
|
@ -63,7 +67,7 @@ impl super::Ppu {
|
||||||
fn read_nametable(&mut self, address: usize) -> u8 {
|
fn read_nametable(&mut self, address: usize) -> u8 {
|
||||||
let base = address % 0x1000;
|
let base = address % 0x1000;
|
||||||
let offset = base % 0x0400;
|
let offset = base % 0x0400;
|
||||||
if self.mirroring == 0 { // horizontal
|
if self.mapper.borrow_mut().get_mirroring() == Mirror::Horizontal {
|
||||||
match base {
|
match base {
|
||||||
0x0000..=0x07FF => {
|
0x0000..=0x07FF => {
|
||||||
self.nametable_0[offset]
|
self.nametable_0[offset]
|
||||||
|
@ -89,7 +93,7 @@ impl super::Ppu {
|
||||||
fn write_nametable(&mut self, address: usize, value: u8) {
|
fn write_nametable(&mut self, address: usize, value: u8) {
|
||||||
let base = address % 0x1000;
|
let base = address % 0x1000;
|
||||||
let offset = base % 0x0400;
|
let offset = base % 0x0400;
|
||||||
if self.mirroring == 0 { // horizontal
|
if self.mapper.borrow_mut().get_mirroring() == Mirror::Horizontal { // horizontal
|
||||||
match base {
|
match base {
|
||||||
0x0000..=0x07FF => {
|
0x0000..=0x07FF => {
|
||||||
self.nametable_0[offset] = value;
|
self.nametable_0[offset] = value;
|
||||||
|
|
Loading…
Reference in New Issue