fixes save states for games with cartridge RAM. not the most elegant way to serialize mappers but straightforward. was hoping to downcast dyn Mapper trait object but couldn't get it to work.

This commit is contained in:
Theron 2020-03-02 19:01:00 -06:00
parent f7cb2a3450
commit e36bfd1821
10 changed files with 248 additions and 45 deletions

View File

@ -1,4 +1,4 @@
use super::{Cartridge, Mapper, Mirror};
use super::{Cartridge, Mapper, Mirror, serialize::*};
pub struct Cnrom {
cart: Cartridge,
@ -41,4 +41,20 @@ impl Mapper for Cnrom {
fn save_battery_backed_ram(&self) {}
fn clock(&mut self) {}
fn check_irq(&mut self) -> bool {false}
fn save_state(&self) -> MapperData {
MapperData::Cnrom(
CnromData {
cart: self.cart.clone(),
chr_bank_select: self.chr_bank_select,
}
)
}
fn load_state(&mut self, mapper_data: MapperData) {
if let MapperData::Cnrom(cnrom_data) = mapper_data {
self.cart = cnrom_data.cart;
self.chr_bank_select = cnrom_data.chr_bank_select;
}
}
}

View File

@ -1,4 +1,4 @@
use super::{Cartridge, Mapper, Mirror};
use super::{Cartridge, Mapper, Mirror, serialize::*};
use std::fs::File;
use std::io::{Read, Write};
@ -210,4 +210,42 @@ impl Mapper for Mmc1 {
fn clock(&mut self) {}
fn check_irq(&mut self) -> bool {false}
fn save_state(&self) -> MapperData {
MapperData::Mmc1(
Mmc1Data {
cart: self.cart.clone(),
step: self.step,
shift_register: self.shift_register,
mirroring: self.mirroring,
control: self.control,
prg_ram_bank: self.prg_ram_bank.clone(),
prg_ram_enabled: self.prg_ram_enabled,
prg_bank_mode: self.prg_bank_mode,
prg_bank_select: self.prg_bank_select,
chr_ram_bank: self.chr_ram_bank.clone(),
chr_low_bank: self.chr_low_bank,
chr_high_bank: self.chr_high_bank,
chr_bank_mode: self.chr_bank_mode,
}
)
}
fn load_state(&mut self, mapper_data: MapperData) {
if let MapperData::Mmc1(mmc1_data) = mapper_data {
self.cart = mmc1_data.cart;
self.step = mmc1_data.step;
self.shift_register = mmc1_data.shift_register;
self.mirroring = mmc1_data.mirroring;
self.control = mmc1_data.control;
self.prg_ram_bank = mmc1_data.prg_ram_bank;
self.prg_ram_enabled = mmc1_data.prg_ram_enabled;
self.prg_bank_mode = mmc1_data.prg_bank_mode;
self.prg_bank_select = mmc1_data.prg_bank_select;
self.chr_ram_bank = mmc1_data.chr_ram_bank;
self.chr_low_bank = mmc1_data.chr_low_bank;
self.chr_high_bank = mmc1_data.chr_high_bank;
self.chr_bank_mode = mmc1_data.chr_bank_mode;
}
}
}

View File

@ -1,4 +1,4 @@
use super::{Cartridge, Mapper, Mirror};
use super::{Cartridge, Mapper, Mirror, serialize::*};
pub struct Mmc3 {
cart: Cartridge,
@ -222,4 +222,44 @@ impl Mapper for Mmc3 {
}
false
}
fn save_state(&self) -> MapperData {
MapperData::Mmc3(
Mmc3Data {
cart: self.cart.clone(),
mirroring: self.mirroring,
bank_registers: self.bank_registers.clone(),
next_bank: self.next_bank,
irq_latch: self.irq_latch,
irq_counter: self.irq_counter,
irq_enable: self.irq_enable,
trigger_irq: self.trigger_irq,
reload_counter: self.reload_counter,
irq_delay: self.irq_delay,
prg_ram_bank: self.prg_ram_bank.clone(),
prg_rom_bank_mode: self.prg_rom_bank_mode,
chr_rom_bank_mode: self.chr_rom_bank_mode,
chr_ram_bank: self.chr_ram_bank.clone(),
}
)
}
fn load_state(&mut self, mapper_data: MapperData) {
if let MapperData::Mmc3(mmc3_data) = mapper_data {
self.cart = mmc3_data.cart;
self.mirroring = mmc3_data.mirroring;
self.bank_registers = mmc3_data.bank_registers;
self.next_bank = mmc3_data.next_bank;
self.irq_latch = mmc3_data.irq_latch;
self.irq_counter = mmc3_data.irq_counter;
self.irq_enable = mmc3_data.irq_enable;
self.trigger_irq = mmc3_data.trigger_irq;
self.reload_counter = mmc3_data.reload_counter;
self.irq_delay = mmc3_data.irq_delay;
self.prg_ram_bank = mmc3_data.prg_ram_bank;
self.prg_rom_bank_mode = mmc3_data.prg_rom_bank_mode;
self.chr_rom_bank_mode = mmc3_data.chr_rom_bank_mode;
self.chr_ram_bank = mmc3_data.chr_ram_bank;
}
}
}

View File

@ -3,6 +3,7 @@ mod mmc1;
mod uxrom;
mod cnrom;
mod mmc3;
pub mod serialize;
use nrom::Nrom;
use mmc1::Mmc1;
@ -12,8 +13,6 @@ use mmc3::Mmc3;
use std::cell::RefCell;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::io::Read;
use std::rc::Rc;
@ -25,9 +24,11 @@ pub trait Mapper {
fn save_battery_backed_ram(&self);
fn clock(&mut self);
fn check_irq(&mut self) -> bool;
fn save_state(&self) -> serialize::MapperData;
fn load_state(&mut self, mapper_data: serialize::MapperData);
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Mirror {
LowBank,
HighBank,
@ -49,6 +50,7 @@ pub fn get_mapper(filename: String) -> Rc<RefCell<dyn Mapper>> {
}
}
#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct Cartridge {
filename: String,
prg_rom_size: usize,

View File

@ -1,4 +1,4 @@
use super::{Cartridge, Mapper, Mirror};
use super::{Cartridge, Mapper, Mirror, serialize::*};
pub struct Nrom {
cart: Cartridge,
@ -57,4 +57,20 @@ impl Mapper for Nrom {
fn save_battery_backed_ram(&self) {}
fn clock(&mut self) {}
fn check_irq(&mut self) -> bool {false}
fn save_state(&self) -> MapperData {
MapperData::Nrom(
NromData {
cart: self.cart.clone(),
chr_ram: self.chr_ram.clone(),
}
)
}
fn load_state(&mut self, mapper_data: MapperData) {
if let MapperData::Nrom(nrom_data) = mapper_data {
self.cart = nrom_data.cart;
self.chr_ram = nrom_data.chr_ram;
}
}
}

View File

@ -0,0 +1,65 @@
use super::{Cartridge, Mirror};
#[derive(serde::Serialize, serde::Deserialize)]
pub enum MapperData {
Nrom(NromData),
Mmc1(Mmc1Data),
Uxrom(UxromData),
Cnrom(CnromData),
Mmc3(Mmc3Data),
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct NromData {
pub cart: Cartridge,
pub chr_ram: Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Mmc1Data {
pub cart: Cartridge,
pub step: u8,
pub shift_register: u8,
pub mirroring: Mirror,
pub control: u8,
pub prg_ram_bank: Vec<u8>,
pub prg_ram_enabled: bool,
pub prg_bank_mode: u8,
pub prg_bank_select: usize,
pub chr_ram_bank: Vec<u8>,
pub chr_low_bank: usize,
pub chr_high_bank: usize,
pub chr_bank_mode: bool,
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct UxromData {
pub cart: Cartridge,
pub chr_ram: Vec<u8>,
pub bank_select: usize,
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct CnromData {
pub cart: Cartridge,
pub chr_bank_select: usize,
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Mmc3Data {
pub cart: Cartridge,
pub mirroring: Mirror,
pub bank_registers: Vec<usize>,
pub next_bank: u8,
pub irq_latch: u8,
pub irq_counter: u8,
pub irq_enable: bool,
pub trigger_irq: bool,
pub reload_counter: bool,
pub irq_delay: u8,
pub prg_ram_bank: Vec<u8>,
pub prg_rom_bank_mode: bool,
pub chr_rom_bank_mode: bool,
pub chr_ram_bank: Vec<u8>,
}

View File

@ -1,4 +1,4 @@
use super::{Cartridge, Mapper, Mirror};
use super::{Cartridge, Mapper, Mirror, serialize::*};
pub struct Uxrom {
cart: Cartridge,
@ -52,4 +52,22 @@ impl Mapper for Uxrom {
fn save_battery_backed_ram(&self) {}
fn clock(&mut self) {}
fn check_irq(&mut self) -> bool {false}
fn save_state(&self) -> MapperData {
MapperData::Uxrom(
UxromData {
cart: self.cart.clone(),
chr_ram: self.chr_ram.clone(),
bank_select: self.bank_select,
}
)
}
fn load_state(&mut self, mapper_data: MapperData) {
if let MapperData::Uxrom(uxrom_data) = mapper_data {
self.cart = uxrom_data.cart;
self.chr_ram = uxrom_data.chr_ram;
self.bank_select = uxrom_data.bank_select;
}
}
}

View File

@ -65,7 +65,7 @@ pub struct Cpu {
clock: u64, // number of ticks in current cycle
delay: usize, // for skipping cycles during OAM DMA
mapper: Rc<RefCell<dyn Mapper>>, // cartridge data
pub mapper: Rc<RefCell<dyn Mapper>>, // cartridge data
pub ppu: super::Ppu,
pub apu: super::Apu,
@ -301,36 +301,36 @@ $4020-$FFFF $BFE0 Cartridge space: PRG ROM, PRG RAM, and mapper registers (See
// For debug output
const _OPCODE_DISPLAY_NAMES: [&str; 256] = [
"BRK", "ORA", "BAD", "SLO", "NOP", "ORA", "ASL", "SLO",
"PHP", "ORA", "ASL", "ANC", "NOP", "ORA", "ASL", "SLO",
"BPL", "ORA", "BAD", "SLO", "NOP", "ORA", "ASL", "SLO",
"CLC", "ORA", "NOP", "SLO", "NOP", "ORA", "ASL", "SLO",
"JSR", "AND", "BAD", "RLA", "BIT", "AND", "ROL", "RLA",
"PLP", "AND", "ROL", "ANC", "BIT", "AND", "ROL", "RLA",
"BMI", "AND", "BAD", "RLA", "NOP", "AND", "ROL", "RLA",
"SEC", "AND", "NOP", "RLA", "NOP", "AND", "ROL", "RLA",
"RTI", "EOR", "BAD", "SRE", "NOP", "EOR", "LSR", "SRE",
"PHA", "EOR", "LSR", "ALR", "JMP", "EOR", "LSR", "SRE",
"BVC", "EOR", "BAD", "SRE", "NOP", "EOR", "LSR", "SRE",
"CLI", "EOR", "NOP", "SRE", "NOP", "EOR", "LSR", "SRE",
"RTS", "ADC", "BAD", "RRA", "NOP", "ADC", "ROR", "RRA",
"PLA", "ADC", "ROR", "ARR", "JMP", "ADC", "ROR", "RRA",
"BVS", "ADC", "BAD", "RRA", "NOP", "ADC", "ROR", "RRA",
"SEI", "ADC", "NOP", "RRA", "NOP", "ADC", "ROR", "RRA",
"NOP", "STA", "NOP", "SAX", "STY", "STA", "STX", "SAX",
"DEY", "NOP", "TXA", "XAA", "STY", "STA", "STX", "SAX",
"BCC", "STA", "BAD", "AHX", "STY", "STA", "STX", "SAX",
"TYA", "STA", "TXS", "TAS", "SHY", "STA", "SHX", "AHX",
"LDY", "LDA", "LDX", "LAX", "LDY", "LDA", "LDX", "LAX",
"TAY", "LDA", "TAX", "LAX", "LDY", "LDA", "LDX", "LAX",
"BCS", "LDA", "BAD", "LAX", "LDY", "LDA", "LDX", "LAX",
"CLV", "LDA", "TSX", "LAS", "LDY", "LDA", "LDX", "LAX",
"CPY", "CMP", "NOP", "DCP", "CPY", "CMP", "DEC", "DCP",
"INY", "CMP", "DEX", "AXS", "CPY", "CMP", "DEC", "DCP",
"BNE", "CMP", "BAD", "DCP", "NOP", "CMP", "DEC", "DCP",
"CLD", "CMP", "NOP", "DCP", "NOP", "CMP", "DEC", "DCP",
"CPX", "SBC", "NOP", "ISC", "CPX", "SBC", "INC", "ISC",
"INX", "SBC", "NOP", "SBC", "CPX", "SBC", "INC", "ISC",
"BEQ", "SBC", "BAD", "ISC", "NOP", "SBC", "INC", "ISC",
"BRK", "ORA", "BAD", "SLO", "NOP", "ORA", "ASL", "SLO",
"PHP", "ORA", "ASL", "ANC", "NOP", "ORA", "ASL", "SLO",
"BPL", "ORA", "BAD", "SLO", "NOP", "ORA", "ASL", "SLO",
"CLC", "ORA", "NOP", "SLO", "NOP", "ORA", "ASL", "SLO",
"JSR", "AND", "BAD", "RLA", "BIT", "AND", "ROL", "RLA",
"PLP", "AND", "ROL", "ANC", "BIT", "AND", "ROL", "RLA",
"BMI", "AND", "BAD", "RLA", "NOP", "AND", "ROL", "RLA",
"SEC", "AND", "NOP", "RLA", "NOP", "AND", "ROL", "RLA",
"RTI", "EOR", "BAD", "SRE", "NOP", "EOR", "LSR", "SRE",
"PHA", "EOR", "LSR", "ALR", "JMP", "EOR", "LSR", "SRE",
"BVC", "EOR", "BAD", "SRE", "NOP", "EOR", "LSR", "SRE",
"CLI", "EOR", "NOP", "SRE", "NOP", "EOR", "LSR", "SRE",
"RTS", "ADC", "BAD", "RRA", "NOP", "ADC", "ROR", "RRA",
"PLA", "ADC", "ROR", "ARR", "JMP", "ADC", "ROR", "RRA",
"BVS", "ADC", "BAD", "RRA", "NOP", "ADC", "ROR", "RRA",
"SEI", "ADC", "NOP", "RRA", "NOP", "ADC", "ROR", "RRA",
"NOP", "STA", "NOP", "SAX", "STY", "STA", "STX", "SAX",
"DEY", "NOP", "TXA", "XAA", "STY", "STA", "STX", "SAX",
"BCC", "STA", "BAD", "AHX", "STY", "STA", "STX", "SAX",
"TYA", "STA", "TXS", "TAS", "SHY", "STA", "SHX", "AHX",
"LDY", "LDA", "LDX", "LAX", "LDY", "LDA", "LDX", "LAX",
"TAY", "LDA", "TAX", "LAX", "LDY", "LDA", "LDX", "LAX",
"BCS", "LDA", "BAD", "LAX", "LDY", "LDA", "LDX", "LAX",
"CLV", "LDA", "TSX", "LAS", "LDY", "LDA", "LDX", "LAX",
"CPY", "CMP", "NOP", "DCP", "CPY", "CMP", "DEC", "DCP",
"INY", "CMP", "DEX", "AXS", "CPY", "CMP", "DEC", "DCP",
"BNE", "CMP", "BAD", "DCP", "NOP", "CMP", "DEC", "DCP",
"CLD", "CMP", "NOP", "DCP", "NOP", "CMP", "DEC", "DCP",
"CPX", "SBC", "NOP", "ISC", "CPX", "SBC", "INC", "ISC",
"INX", "SBC", "NOP", "SBC", "CPX", "SBC", "INC", "ISC",
"BEQ", "SBC", "BAD", "ISC", "NOP", "SBC", "INC", "ISC",
"SED", "SBC", "NOP", "ISC", "NOP", "SBC", "INC", "ISC",
];

View File

@ -63,8 +63,13 @@ fn main() -> Result<(), String> {
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. }
=> return Ok(()),
Event::DropFile{ filename: f, .. } => {
name = f;
break 'waiting;
match check_signature(&f) {
Ok(()) => {
name = f;
break 'waiting;
},
Err(e) => println!("{}", e),
}
},
_ => (), // println!("event: {:?}", event),
}
@ -210,7 +215,7 @@ fn process_events(event_pump: &mut EventPump, filepath: &PathBuf, cpu: &mut Cpu)
res.unwrap();
// } else if f.len() > 4 && &f[f.len()-4..] == ".nes" {
} else {
match cartridge::check_signature(&f) {
match check_signature(&f) {
Ok(()) => return GameExitMode::NewGame(f),
Err(e) => println!("{}", e),
}

View File

@ -1,17 +1,18 @@
use super::cpu;
use super::ppu;
use super::apu;
use super::cartridge;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
#[derive(serde::Serialize, serde::Deserialize)]
struct SaveState {
cpu: cpu::serialize::CpuData,
ppu: ppu::serialize::PpuData,
apu: apu::serialize::ApuData,
mapper: cartridge::serialize::MapperData,
}
pub fn save_state(cpu: &cpu::Cpu, save_file: &PathBuf) -> Result<(), String> {
@ -19,6 +20,7 @@ pub fn save_state(cpu: &cpu::Cpu, save_file: &PathBuf) -> Result<(), String> {
cpu: cpu.save_state(),
ppu: cpu.ppu.save_state(),
apu: cpu.apu.save_state(),
mapper: cpu.mapper.borrow().save_state(),
};
let serialized = serde_json::to_string(&data)
.map_err(|e| e.to_string())?;
@ -44,6 +46,7 @@ pub fn load_state(cpu: &mut cpu::Cpu, save_file: &PathBuf) -> Result<(), String>
cpu.load_state(state.cpu);
cpu.ppu.load_state(state.ppu);
cpu.apu.load_state(state.apu);
cpu.mapper.borrow_mut().load_state(state.mapper);
println!("loading save state from file: {:?}", save_file);
Ok(())
} else {