aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/apu.rs175
-rw-r--r--src/bin.rs105
-rw-r--r--src/cartridge.rs6
-rw-r--r--src/controller.rs13
-rw-r--r--src/lib.rs2
-rw-r--r--src/mapper.rs137
-rw-r--r--src/memory.rs78
-rw-r--r--src/mos6502.rs28
-rw-r--r--src/ppu.rs35
9 files changed, 477 insertions, 102 deletions
diff --git a/src/apu.rs b/src/apu.rs
index 6b06b0b..7777f68 100644
--- a/src/apu.rs
+++ b/src/apu.rs
@@ -1,7 +1,11 @@
#![allow(dead_code)]
use mos6502::CPU_FREQ;
use memory::CPUBus;
+use utils::Sampler;
+use core::mem::size_of;
+use utils::{Read, Write, load_prefix, save_prefix};
+#[repr(C)]
struct LPFilter {
prev_out: i16
}
@@ -25,6 +29,14 @@ impl LPFilter {
LPFilter { prev_out: 0 }
}
+ fn load(&mut self, reader: &mut Read) -> bool {
+ load_prefix(self, 0, reader)
+ }
+
+ fn save(&self, writer: &mut Write) -> bool {
+ save_prefix(self, 0, writer)
+ }
+
fn output(&mut self, input: i16) -> i16 {
let out = cutoff(self.prev_out as i32 +
(input as i32 - self.prev_out as i32)
@@ -34,6 +46,7 @@ impl LPFilter {
}
}
+#[repr(C)]
struct HPFilter {
prev_in: i16,
prev_out: i16,
@@ -49,6 +62,14 @@ impl HPFilter {
}
}
+ fn load(&mut self, reader: &mut Read) -> bool {
+ load_prefix(self, 0, reader)
+ }
+
+ fn save(&self, writer: &mut Write) -> bool {
+ save_prefix(self, 0, writer)
+ }
+
fn output(&mut self, input: i16) -> i16 {
let out = cutoff(
self.prev_out as i32 * self.hp_factor / AUDIO_LEVEL_MAX +
@@ -137,52 +158,7 @@ const TND_TABLE: [u16; 203] = [
0xbbfe, 0xbc84, 0xbd09, 0xbd8d, 0xbe11
];
-pub struct Sampler {
- freq2: u32,
- q0: u32,
- r0: u32,
- ddl: (u32, u32),
- cnt: u32,
- sec_cnt: u32
-}
-
-impl Sampler {
- pub fn new(freq1: u32, freq2: u32) -> Self {
- let q0 = freq1 / freq2;
- let r0 = freq1 - q0 * freq2;
- Sampler {
- freq2,
- q0,
- r0,
- ddl: (q0, r0),
- cnt: 0,
- sec_cnt: 0
- }
- }
-
- pub fn tick(&mut self) -> (bool, bool) {
- let (q, r) = self.ddl;
- if self.cnt == q {
- let nr = r + self.r0;
- self.ddl = if nr > self.freq2 {
- (self.q0, nr - self.freq2)
- } else {
- (self.q0 - 1, nr)
- };
- self.cnt = 0;
- self.sec_cnt += 1;
- let sec = self.sec_cnt == self.freq2;
- if sec {
- self.sec_cnt = 0
- }
- (true, sec)
- } else {
- self.cnt += 1;
- (false, false)
- }
- }
-}
-
+#[repr(C)]
pub struct Pulse {
/* envelope */
env_period: u8,
@@ -223,6 +199,14 @@ impl Pulse {
seq_wave: 0, seq_cnt: 0, enabled: false, comple}
}
+ fn load(&mut self, reader: &mut Read) -> bool {
+ load_prefix(self, 0, reader)
+ }
+
+ fn save(&self, writer: &mut Write) -> bool {
+ save_prefix(self, 0, writer)
+ }
+
pub fn write_reg1(&mut self, data: u8) {
self.seq_wave = DUTY_TABLE[(data >> 6) as usize];
self.env_loop = data & 0x20 == 0x20;
@@ -351,6 +335,7 @@ impl Pulse {
}
}
+#[repr(C)]
pub struct Triangle {
/* linear counter */
cnt_rld: bool,
@@ -377,6 +362,14 @@ impl Triangle {
}
}
+ fn load(&mut self, reader: &mut Read) -> bool {
+ load_prefix(self, 0, reader)
+ }
+
+ fn save(&self, writer: &mut Write) -> bool {
+ save_prefix(self, 0, writer)
+ }
+
pub fn write_reg1(&mut self, data: u8) {
self.cnt_rld_val = data & 0x7f;
self.ctrl = data >> 7 == 1;
@@ -452,6 +445,7 @@ impl Triangle {
}
+#[repr(C)]
pub struct Noise {
/* envelope */
env_period: u8,
@@ -482,6 +476,14 @@ impl Noise {
enabled: false}
}
+ fn load(&mut self, reader: &mut Read) -> bool {
+ load_prefix(self, 0, reader)
+ }
+
+ fn save(&self, writer: &mut Write) -> bool {
+ save_prefix(self, 0, writer)
+ }
+
pub fn write_reg1(&mut self, data: u8) {
self.env_loop = data & 0x20 == 0x20;
self.env_const = data & 0x10 == 0x10;
@@ -564,6 +566,7 @@ impl Noise {
}
}
+#[repr(C)]
pub struct DMC {
dmc_loop: bool,
dmc_cnt: u8,
@@ -591,6 +594,14 @@ impl DMC {
}
}
+ fn load(&mut self, reader: &mut Read) -> bool {
+ load_prefix(self, 0, reader)
+ }
+
+ fn save(&self, writer: &mut Write) -> bool {
+ save_prefix(self, 0, writer)
+ }
+
pub fn write_reg1(&mut self, data: u8) {
self.irq_enabled = (data >> 7) == 1;
self.dmc_loop = data & 0x40 == 0x40;
@@ -681,27 +692,49 @@ impl DMC {
fn output(&self) -> u8 { self.level }
}
+#[repr(C)]
pub struct APU<'a> {
+ /*-- begin state --*/
+ frame_lvl: u8,
+ frame_mode: bool, /* true for 5-step mode */
+ frame_inh: bool,
+ frame_int: bool,
+ cycle_even: bool,
+ /*-- end state --*/
+
+ /*-- begin sub-state --*/
pub pulse1: Pulse,
pub pulse2: Pulse,
pub triangle: Triangle,
pub noise: Noise,
pub dmc: DMC,
- frame_lvl: u8,
- frame_mode: bool, /* true for 5-step mode */
- frame_inh: bool,
- frame_int: bool,
+
+ lp_filter: LPFilter,
+ hp_filter1: HPFilter,
+ hp_filter2: HPFilter,
+
frame_sampler: Sampler,
audio_sampler: Sampler,
- cycle_even: bool,
+ /*-- end sub-state --*/
+
spkr: &'a mut Speaker,
- lp_filter: LPFilter,
- hp_filter1: HPFilter,
- hp_filter2: HPFilter
}
+const APU_IGNORED_SIZE: usize =
+ size_of::<Pulse>() +
+ size_of::<Pulse>() +
+ size_of::<Triangle>() +
+ size_of::<Noise>() +
+ size_of::<DMC>() +
+ size_of::<LPFilter>() +
+ size_of::<HPFilter>() +
+ size_of::<HPFilter>() +
+ size_of::<Sampler>() +
+ size_of::<Sampler>() +
+ size_of::<&Speaker>();
+
impl<'a> APU<'a> {
- pub fn new(spkr: &'a mut Speaker/*, bus: &'a CPUBus<'a>*/) -> Self {
+ pub fn new(spkr: &'a mut Speaker) -> Self {
APU {
pulse1: Pulse::new(false), pulse2: Pulse::new(true),
triangle: Triangle::new(),
@@ -718,12 +751,40 @@ impl<'a> APU<'a> {
}
}
+ pub fn load(&mut self, reader: &mut Read) -> bool {
+ load_prefix(self, APU_IGNORED_SIZE, reader) &&
+ self.pulse1.load(reader) &&
+ self.pulse2.load(reader) &&
+ self.triangle.load(reader) &&
+ self.noise.load(reader) &&
+ self.dmc.load(reader) &&
+ self.lp_filter.load(reader) &&
+ self.hp_filter1.load(reader) &&
+ self.hp_filter2.load(reader) &&
+ self.frame_sampler.load(reader) &&
+ self.audio_sampler.load(reader)
+ }
+
+ pub fn save(&self, writer: &mut Write) -> bool {
+ save_prefix(self, APU_IGNORED_SIZE, writer) &&
+ self.pulse1.save(writer) &&
+ self.pulse2.save(writer) &&
+ self.triangle.save(writer) &&
+ self.noise.save(writer) &&
+ self.dmc.save(writer) &&
+ self.lp_filter.save(writer) &&
+ self.hp_filter1.save(writer) &&
+ self.hp_filter2.save(writer) &&
+ self.frame_sampler.save(writer) &&
+ self.audio_sampler.save(writer)
+ }
+
pub fn tick(&mut self, bus: &CPUBus) -> bool {
let mut irq = false;
- if let (true, _) = self.frame_sampler.tick() {
+ if self.frame_sampler.tick() {
irq = self.tick_frame_counter();
}
- if let (true, _) = self.audio_sampler.tick() {
+ if self.audio_sampler.tick() {
let sample = self.output();
self.spkr.queue(sample);
}
diff --git a/src/bin.rs b/src/bin.rs
index 839b630..d306fb4 100644
--- a/src/bin.rs
+++ b/src/bin.rs
@@ -1,10 +1,12 @@
+#![feature(const_size_of)]
extern crate core;
use std::fs::File;
use std::sync::{Mutex, Condvar};
-use std::io::Read;
+use std::io::{Read, Write};
use std::intrinsics::transmute;
use std::process::exit;
+use std::cell::Cell;
extern crate sdl2;
#[macro_use] extern crate clap;
@@ -14,6 +16,7 @@ use sdl2::keyboard::Keycode;
use sdl2::rect::Rect;
use clap::{Arg, App};
+ mod utils;
mod memory;
#[macro_use] mod mos6502;
mod ppu;
@@ -73,7 +76,17 @@ impl Cartridge for SimpleCart {
BankType::Sram => self.sram.len()
}
}
- fn get_bank<'a>(&mut self, base: usize, size: usize, kind: BankType) -> &'a mut [u8] {
+ fn get_bank<'a>(&self, base: usize, size: usize, kind: BankType) -> &'a [u8] {
+ unsafe {
+ &*((&(match kind {
+ BankType::PrgRom => &self.prg_rom,
+ BankType::ChrRom => &self.chr_rom,
+ BankType::Sram => &self.sram,
+ })[base..base + size]) as *const [u8])
+ }
+ }
+
+ fn get_bank_mut<'a>(&mut self, base: usize, size: usize, kind: BankType) -> &'a mut [u8] {
unsafe {
&mut *((&mut (match kind {
BankType::PrgRom => &mut self.prg_rom,
@@ -82,8 +95,47 @@ impl Cartridge for SimpleCart {
})[base..base + size]) as *mut [u8])
}
}
+
fn get_mirror_type(&self) -> MirrorType {self.mirror_type}
fn set_mirror_type(&mut self, mt: MirrorType) {self.mirror_type = mt}
+
+ fn load(&mut self, reader: &mut utils::Read) -> bool {
+ let len = self.sram.len();
+ (match reader.read(&mut self.sram) {
+ Some(x) => x == len,
+ None => false
+ }) &&
+ utils::load_prefix(&mut self.mirror_type, 0, reader)
+ }
+
+ fn save(&self, writer: &mut utils::Write) -> bool {
+ let len = self.sram.len();
+ (match writer.write(&self.sram) {
+ Some(x) => x == len,
+ None => false
+ }) &&
+ utils::save_prefix(&self.mirror_type, 0, writer)
+ }
+}
+
+struct FileIO(File);
+
+impl utils::Read for FileIO {
+ fn read(&mut self, buf: &mut [u8]) -> Option<usize> {
+ match self.0.read(buf) {
+ Ok(x) => Some(x),
+ Err(_) => None
+ }
+ }
+}
+
+impl utils::Write for FileIO {
+ fn write(&mut self, buf: &[u8]) -> Option<usize> {
+ match self.0.write(buf) {
+ Ok(x) => Some(x),
+ Err(_) => None
+ }
+ }
}
struct SDLWindow<'a> {
@@ -95,6 +147,7 @@ struct SDLWindow<'a> {
p1_ctl: &'a stdctl::Joystick,
p1_keymap: [u8; 256],
copy_area: Option<Rect>,
+ exit_flag: &'a Cell<bool>
}
macro_rules! gen_keymap {
@@ -107,7 +160,8 @@ impl<'a> SDLWindow<'a> {
fn new(sdl_context: &'a sdl2::Sdl,
p1_ctl: &'a stdctl::Joystick,
pixel_scale: u32,
- full_screen: bool) -> Self {
+ full_screen: bool,
+ exit_flag: &'a Cell<bool>) -> Self {
use Keycode::*;
let video_subsystem = sdl_context.video().unwrap();
let mut actual_height = PIX_HEIGHT * pixel_scale;
@@ -139,7 +193,7 @@ impl<'a> SDLWindow<'a> {
PIX_WIDTH, PIX_HEIGHT).unwrap(),
p1_button_state: 0,
p1_ctl, p1_keymap: [stdctl::NULL; 256],
- copy_area
+ copy_area, exit_flag
};
{
let keymap = &mut res.p1_keymap;
@@ -209,7 +263,11 @@ impl<'a> ppu::Screen for SDLWindow<'a> {
self.canvas.clear();
self.canvas.copy(&self.texture, self.copy_area, None).unwrap();
self.canvas.present();
- if self.poll() { exit(0) }
+ if self.poll() {
+ /*
+ */
+ self.exit_flag.set(true)
+ }
}
}
@@ -347,6 +405,11 @@ fn main() {
.help("the iNES ROM file")
.required(true)
.index(1))
+ .arg(Arg::with_name("load")
+ .short("l")
+ .long("load")
+ .required(false)
+ .takes_value(true))
.get_matches();
let scale = std::cmp::min(8,
@@ -356,6 +419,7 @@ fn main() {
/* load and parse iNES file */
let fname = matches.value_of("INPUT").unwrap();
+ let lname = matches.value_of("load");
let mut file = File::open(fname).unwrap();
let mut rheader = [0; 16];
file.read(&mut rheader[..]).unwrap();
@@ -391,7 +455,7 @@ fn main() {
let mut prg_rom = vec![0; prg_len];
let mut chr_rom = vec![0; chr_len];
- let sram = vec![0; 0x4000];
+ let sram = vec![0; 0x2000];
println!("read prg {}", file.read(&mut prg_rom[..]).unwrap());
println!("read chr {}", file.read(&mut chr_rom[..]).unwrap());
@@ -412,9 +476,10 @@ fn main() {
}).unwrap();
/* P1 controller */
let p1ctl = stdctl::Joystick::new();
- /* cartridge & mapper */
+ /* construct mapper from cartridge data */
let cart = SimpleCart::new(chr_rom, prg_rom, sram, mirror);
- let mut win = Box::new(SDLWindow::new(&sdl_context, &p1ctl, scale, full));
+ let exit_flag = Cell::new(false);
+ let mut win = Box::new(SDLWindow::new(&sdl_context, &p1ctl, scale, full, &exit_flag));
let mut m: Box<mapper::Mapper> = match mapper_id {
0 | 2 => Box::new(mapper::Mapper2::new(cart)),
1 => Box::new(mapper::Mapper1::new(cart)),
@@ -428,13 +493,35 @@ fn main() {
let mut apu = APU::new(&mut spkr);
let cpu_ptr = &mut cpu as *mut CPU;
cpu.mem.bus.attach(cpu_ptr, &mut ppu, &mut apu);
- cpu.powerup();
+ match lname {
+ Some(s) => {
+ let mut file = FileIO(File::open(s).unwrap());
+ cpu.load(&mut file);
+ ppu.load(&mut file);
+ apu.load(&mut file);
+ mapper.get_mut().load(&mut file);
+ debug_assert!(cpu.cycle == 0);
+ },
+ None => {
+ cpu.powerup()
+ }
+ }
device.resume();
loop {
/* consume the leftover cycles from the last instruction */
while cpu.cycle > 0 {
cpu.mem.bus.tick()
}
+ if exit_flag.get() {
+ {
+ let mut file = FileIO(File::create("t.dat").unwrap());
+ cpu.save(&mut file);
+ ppu.save(&mut file);
+ apu.save(&mut file);
+ mapper.save(&mut file);
+ }
+ exit(0);
+ }
//print_cpu_trace(&cpu);
cpu.step();
}
diff --git a/src/cartridge.rs b/src/cartridge.rs
index 52b43ca..ca9c31e 100644
--- a/src/cartridge.rs
+++ b/src/cartridge.rs
@@ -1,4 +1,5 @@
#![allow(dead_code)]
+use utils::{Read, Write};
#[derive(Copy, Clone)]
pub enum MirrorType {
@@ -17,7 +18,10 @@ pub enum BankType {
pub trait Cartridge {
fn get_size(&self, kind: BankType) -> usize;
- fn get_bank<'a>(&mut self, base: usize, size: usize, kind: BankType) -> &'a mut [u8];
+ fn get_bank<'a>(&self, base: usize, size: usize, kind: BankType) -> &'a [u8];
+ fn get_bank_mut<'a>(&mut self, base: usize, size: usize, kind: BankType) -> &'a mut [u8];
#[inline(always)] fn get_mirror_type(&self) -> MirrorType;
#[inline(always)] fn set_mirror_type(&mut self, mt: MirrorType);
+ fn load(&mut self, reader: &mut Read) -> bool;
+ fn save(&self, writer: &mut Write) -> bool;
}
diff --git a/src/controller.rs b/src/controller.rs
index f2ed4f7..bb78769 100644
--- a/src/controller.rs
+++ b/src/controller.rs
@@ -1,11 +1,15 @@
#![allow(dead_code)]
+use utils::{Read, Write};
pub trait Controller {
fn read(&self) -> u8;
fn write(&self, data: u8);
+ fn load(&mut self, reader: &mut Read) -> bool;
+ fn save(&self, writer: &mut Write) -> bool;
}
pub mod stdctl {
+ use utils::{Read, Write, load_prefix, save_prefix};
use core::cell::Cell;
use controller::Controller;
pub const A: u8 = 1 << 0;
@@ -18,6 +22,7 @@ pub mod stdctl {
pub const RIGHT: u8 = 1 << 7;
pub const NULL: u8 = 0;
+ #[repr(C)]
pub struct Joystick {
strobe: Cell<bool>,
reg: Cell<u8>,
@@ -48,5 +53,13 @@ pub mod stdctl {
self.strobe.set(data & 1 == 1);
self.reg.set(self.back_reg.get());
}
+
+ fn load(&mut self, reader: &mut Read) -> bool {
+ load_prefix(self, 0, reader)
+ }
+
+ fn save(&self, writer: &mut Write) -> bool {
+ save_prefix(self, 0, writer)
+ }
}
}
diff --git a/src/lib.rs b/src/lib.rs
index ade3a2d..8a634e9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,7 @@
#![no_std]
+#![feature(const_size_of)]
//extern crate core;
+mod utils;
mod memory;
#[macro_use] mod mos6502;
mod ppu;
diff --git a/src/mapper.rs b/src/mapper.rs
index 4ecebbf..3d72189 100644
--- a/src/mapper.rs
+++ b/src/mapper.rs
@@ -3,10 +3,13 @@ extern crate core;
use core::cell::UnsafeCell;
use memory::{VMem, CPUBus};
use cartridge::{Cartridge, BankType, MirrorType};
+use utils::{Read, Write, load_prefix, save_prefix};
pub trait Mapper : VMem {
fn get_cart(&self) -> &Cartridge;
fn tick(&mut self, _bus: &CPUBus) {}
+ fn load(&mut self, reader: &mut Read) -> bool;
+ fn save(&self, writer: &mut Write) -> bool;
}
pub struct RefMapper<'a> {
@@ -93,10 +96,10 @@ impl<'a, C> Mapper1<'a, C> where C: Cartridge {
c.get_bank((prg_nbank - 1) << 14, 0x4000, BankType::PrgRom)
];
m.chr_banks = [
- c.get_bank(0, 0x1000, BankType::ChrRom),
- c.get_bank(0x1000, 0x1000, BankType::ChrRom)
+ c.get_bank_mut(0, 0x1000, BankType::ChrRom),
+ c.get_bank_mut(0x1000, 0x1000, BankType::ChrRom)
];
- m.sram = c.get_bank(0, 0x2000, BankType::Sram);
+ m.sram = c.get_bank_mut(0, 0x2000, BankType::Sram);
}
m
}
@@ -128,18 +131,18 @@ impl<'a, C> Mapper1<'a, C> where C: Cartridge {
0x0 => {
let base = ((load_reg & 0xfe) as usize % self.chr_nbank) << 13;
self.chr_banks = [
- self.cart.get_bank(base, 0x1000, BankType::ChrRom),
- self.cart.get_bank(base + 0x1000, 0x1000, BankType::ChrRom)];
+ self.cart.get_bank_mut(base, 0x1000, BankType::ChrRom),
+ self.cart.get_bank_mut(base + 0x1000, 0x1000, BankType::ChrRom)];
},
_ =>
- self.chr_banks[0] = self.cart.get_bank(
+ self.chr_banks[0] = self.cart.get_bank_mut(
(load_reg as usize % (self.chr_nbank << 1)) << 12,
0x1000, BankType::ChrRom)
}
},
0x2 => {
if (self.ctl_reg >> 4) & 1 == 1 {
- self.chr_banks[1] = self.cart.get_bank(
+ self.chr_banks[1] = self.cart.get_bank_mut(
(load_reg as usize % (self.chr_nbank << 1)) << 12,
0x1000, BankType::ChrRom)
}
@@ -174,6 +177,42 @@ impl<'a, C> Mapper1<'a, C> where C: Cartridge {
impl<'a, C> Mapper for Mapper1<'a, C> where C: Cartridge {
fn get_cart(&self) -> &Cartridge {&self.cart}
+
+ fn load(&mut self, reader: &mut Read) -> bool {
+ for v in self.prg_banks.iter_mut() {
+ let mut offset: usize = 0;
+ if !load_prefix(&mut offset, 0, reader) { return false }
+ *v = self.cart.get_bank(offset, 0x4000, BankType::PrgRom);
+ }
+ for v in self.chr_banks.iter_mut() {
+ let mut offset: usize = 0;
+ if !load_prefix(&mut offset, 0, reader) { return false }
+ *v = self.cart.get_bank_mut(offset, 0x1000, BankType::ChrRom);
+ }
+ load_prefix(&mut self.ctl_reg, 0, reader) &&
+ load_prefix(&mut self.load_reg, 0, reader) &&
+ self.cart.load(reader)
+ }
+
+ fn save(&self, writer: &mut Write) -> bool {
+ let prg_base = self.cart.get_bank(0, 0, BankType::PrgRom).as_ptr();
+ let chr_base = self.cart.get_bank(0, 0, BankType::ChrRom).as_ptr();
+ for v in self.prg_banks.iter() {
+ if !save_prefix(&(v.as_ptr() as usize - prg_base as usize),
+ 0, writer) {
+ return false
+ }
+ }
+ for v in self.chr_banks.iter() {
+ if !save_prefix(&(v.as_ptr() as usize - chr_base as usize),
+ 0, writer) {
+ return false
+ }
+ }
+ save_prefix(&self.ctl_reg, 0, writer) &&
+ save_prefix(&self.load_reg, 0, writer) &&
+ self.cart.save(writer)
+ }
}
pub struct Mapper2<'a, C> where C: Cartridge {
@@ -233,8 +272,8 @@ impl<'a, C> Mapper2<'a, C> where C: Cartridge {
c.get_bank(0, 0x4000, BankType::PrgRom),
c.get_bank((nbank - 1) << 14, 0x4000, BankType::PrgRom)
];
- m.chr_bank = c.get_bank(0, 0x2000, BankType::ChrRom);
- m.sram = c.get_bank(0, 0x2000, BankType::Sram);
+ m.chr_bank = c.get_bank_mut(0, 0x2000, BankType::ChrRom);
+ m.sram = c.get_bank_mut(0, 0x2000, BankType::Sram);
}
m
}
@@ -243,6 +282,34 @@ impl<'a, C> Mapper2<'a, C> where C: Cartridge {
impl<'a, C> Mapper for Mapper2<'a, C> where C: Cartridge {
fn get_cart(&self) -> &Cartridge {&self.cart}
+
+ fn load(&mut self, reader: &mut Read) -> bool {
+ for v in self.prg_banks.iter_mut() {
+ let mut offset: usize = 0;
+ if !load_prefix(&mut offset, 0, reader) { return false }
+ *v = self.cart.get_bank(offset, 0x4000, BankType::PrgRom);
+ }
+ let mut offset: usize = 0;
+ if !load_prefix(&mut offset, 0, reader) { return false }
+ self.chr_bank = self.cart.get_bank_mut(offset, 0x2000, BankType::ChrRom);
+ self.cart.load(reader)
+ }
+
+ fn save(&self, writer: &mut Write) -> bool {
+ let prg_base = self.cart.get_bank(0, 0, BankType::PrgRom).as_ptr();
+ let chr_base = self.cart.get_bank(0, 0, BankType::ChrRom).as_ptr();
+ for v in self.prg_banks.iter() {
+ if !save_prefix(&(v.as_ptr() as usize - prg_base as usize),
+ 0, writer) {
+ return false
+ }
+ }
+ if !save_prefix(&(self.chr_bank.as_ptr() as usize - chr_base as usize),
+ 0, writer) {
+ return false
+ }
+ self.cart.save(writer)
+ }
}
pub struct Mapper4<'a, C> where C: Cartridge {
@@ -336,13 +403,13 @@ impl<'a, C> Mapper4<'a, C> where C: Cartridge {
}
#[inline(always)]
- fn get_prgbank<'b>(&mut self, idx: u8) -> &'b mut [u8] {
+ fn get_prgbank<'b>(&self, idx: u8) -> &'b [u8] {
self.cart.get_bank(0x2000 * idx as usize, 0x2000, BankType::PrgRom)
}
#[inline(always)]
fn get_chrbank<'b>(&mut self, idx: u8) -> &'b mut [u8] {
- self.cart.get_bank(0x400 * idx as usize, 0x400, BankType::ChrRom)
+ self.cart.get_bank_mut(0x400 * idx as usize, 0x400, BankType::ChrRom)
}
fn update_banks(&mut self) {
@@ -424,7 +491,7 @@ impl<'a, C> Mapper4<'a, C> where C: Cartridge {
m.get_chrbank(0)];
{
let c = &mut m.cart;
- m.sram = c.get_bank(0, 0x2000, BankType::Sram);
+ m.sram = c.get_bank_mut(0, 0x2000, BankType::Sram);
}
m
}
@@ -453,4 +520,50 @@ impl<'a, C> Mapper for Mapper4<'a, C> where C: Cartridge {
}
}
}
+
+ fn load(&mut self, reader: &mut Read) -> bool {
+ for v in self.prg_banks.iter_mut() {
+ let mut offset: usize = 0;
+ if !load_prefix(&mut offset, 0, reader) { return false }
+ *v = self.cart.get_bank(offset, 0x2000, BankType::PrgRom);
+ }
+ for v in self.chr_banks.iter_mut() {
+ let mut offset: usize = 0;
+ if !load_prefix(&mut offset, 0, reader) { return false }
+ *v = self.cart.get_bank_mut(offset, 0x400, BankType::ChrRom);
+ }
+ load_prefix(&mut self.chr_inv, 0, reader) &&
+ load_prefix(&mut self.prg_mode, 0, reader) &&
+ load_prefix(&mut self.reg_idx, 0, reader) &&
+ load_prefix(&mut self.regs, 0, reader) &&
+ load_prefix(&mut self.irq_reload, 0, reader) &&
+ load_prefix(&mut self.irq_counter, 0, reader) &&
+ load_prefix(&mut self.irq_enable, 0, reader) &&
+ self.cart.load(reader)
+ }
+
+ fn save(&self, writer: &mut Write) -> bool {
+ let prg_base = self.cart.get_bank(0, 0, BankType::PrgRom).as_ptr();
+ let chr_base = self.cart.get_bank(0, 0, BankType::ChrRom).as_ptr();
+ for v in self.prg_banks.iter() {
+ if !save_prefix(&(v.as_ptr() as usize - prg_base as usize),
+ 0, writer) {
+ return false
+ }
+ }
+ for v in self.chr_banks.iter() {
+ if !save_prefix(&(v.as_ptr() as usize - chr_base as usize),
+ 0, writer) {
+ return false
+ }
+ }
+ save_prefix(&self.chr_inv, 0, writer) &&
+ save_prefix(&self.prg_mode, 0, writer) &&
+ save_prefix(&self.reg_idx, 0, writer) &&
+ save_prefix(&self.regs, 0, writer) &&
+ save_prefix(&self.irq_reload, 0, writer) &&
+ save_prefix(&self.irq_counter, 0, writer) &&
+ save_prefix(&self.irq_enable, 0, writer) &&
+ self.cart.save(writer)
+ }
}
diff --git a/src/memory.rs b/src/memory.rs
index 20adb39..f199317 100644
--- a/src/memory.rs
+++ b/src/memory.rs
@@ -1,27 +1,42 @@
#![allow(dead_code)]
use ppu::PPU;
-use apu::{APU, Sampler};
+use apu::APU;
+use utils::{Sampler, Read, Write, load_prefix, save_prefix};
use mos6502::{CPU, CPU_FREQ};
use cartridge::MirrorType;
use mapper::RefMapper;
use controller::Controller;
use core::cell::{RefCell, Cell};
use core::ptr::null_mut;
+use core::mem::size_of;
pub trait VMem {
fn read(&self, addr: u16) -> u8;
fn write(&mut self, addr: u16, data: u8);
}
+#[repr(C)]
pub struct CPUBus<'a> {
+ /*-- begin state --*/
+ nmi_after_tick: Cell<bool>,
+ cpu_stall: Cell<u32>,
+ /*-- end state --*/
+
+ /*-- begin sub-state --*/
+ ppu_sampler: RefCell<Sampler>,
+ /*-- end sub-state --*/
+
cpu: *mut CPU<'a>,
ppu: *mut PPU<'a>,
apu: *mut APU<'a>,
- ppu_sampler: RefCell<Sampler>,
- nmi_after_tick: Cell<bool>,
- cpu_stall: Cell<u32>
}
+const CPUBUS_IGNORED_SIZE: usize =
+ size_of::<RefCell<Sampler>>() +
+ size_of::<*mut CPU>() +
+ size_of::<*mut PPU>() +
+ size_of::<*mut APU>();
+
impl<'a> CPUBus<'a> {
pub fn new() -> Self {
CPUBus {ppu: null_mut(),
@@ -33,6 +48,16 @@ impl<'a> CPUBus<'a> {
}
}
+ pub fn load(&mut self, reader: &mut Read) -> bool {
+ load_prefix(self, CPUBUS_IGNORED_SIZE, reader) &&
+ self.ppu_sampler.borrow_mut().load(reader)
+ }
+
+ pub fn save(&self, writer: &mut Write) -> bool {
+ save_prefix(self, CPUBUS_IGNORED_SIZE, writer) &&
+ self.ppu_sampler.borrow().save(writer)
+ }
+
pub fn attach(&mut self, cpu: *mut CPU<'a>,
ppu: *mut PPU<'a>,
apu: *mut APU<'a>) {
@@ -80,23 +105,35 @@ impl<'a> CPUBus<'a> {
}
self.nmi_after_tick.set(nmi_after_tick);
//println!("tick {} {}", ppu.scanline, ppu.cycle);
- if let (true, _) = self.ppu_sampler.borrow_mut().tick() {
+ if self.ppu_sampler.borrow_mut().tick() {
ppu.scr.frame()
}
}
}
+#[repr(C)]
pub struct CPUMemory<'a> {
+ /*-- begin state --*/
sram: [u8; 2048],
+ /*-- end state --*/
+
+ /*-- begin sub-state --*/
pub bus: CPUBus<'a>,
+ /*-- end sub-state --*/
+
mapper: &'a RefMapper<'a>,
ctl1: Option<&'a Controller>,
ctl2: Option<&'a Controller>
}
+const CPUMEM_IGNORED_SIZE: usize =
+ size_of::<CPUBus>() +
+ size_of::<&RefMapper>() +
+ size_of::<Option<&Controller>>() +
+ size_of::<Option<&Controller>>();
+
impl<'a> CPUMemory<'a> {
- pub fn new(
- mapper: &'a RefMapper<'a>,
+ pub fn new(mapper: &'a RefMapper<'a>,
ctl1: Option<&'a Controller>,
ctl2: Option<&'a Controller>) -> Self {
CPUMemory{sram: [0; 2048],
@@ -104,6 +141,16 @@ impl<'a> CPUMemory<'a> {
mapper, ctl1, ctl2}
}
+ pub fn load(&mut self, reader: &mut Read) -> bool {
+ load_prefix(self, CPUMEM_IGNORED_SIZE, reader) &&
+ self.bus.load(reader)
+ }
+
+ pub fn save(&self, writer: &mut Write) -> bool {
+ save_prefix(self, CPUMEM_IGNORED_SIZE, writer) &&
+ self.bus.save(writer)
+ }
+
pub fn get_bus(&'a self) -> &'a CPUBus<'a> {
&self.bus
}
@@ -225,18 +272,33 @@ impl<'a> VMem for CPUMemory<'a> {
}
}
+#[repr(C)]
pub struct PPUMemory<'a> {
+ /*-- begin state -- */
nametable: [u8; 0x800],
palette: [u8; 0x20],
+ /*-- end state --*/
+
mapper: &'a RefMapper<'a>
}
+const PPUMEM_IGNORED_SIZE: usize = size_of::<&RefMapper>();
+
impl<'a> PPUMemory<'a> {
pub fn new(mapper: &'a RefMapper<'a>) -> Self {
PPUMemory{
nametable: [0; 0x800],
palette: [0; 0x20],
- mapper}
+ mapper
+ }
+ }
+
+ pub fn load(&mut self, reader: &mut Read) -> bool {
+ load_prefix(self, PPUMEM_IGNORED_SIZE, reader)
+ }
+
+ pub fn save(&self, writer: &mut Write) -> bool {
+ save_prefix(self, PPUMEM_IGNORED_SIZE, writer)
}
}
diff --git a/src/mos6502.rs b/src/mos6502.rs
index 848dd1f..e8c471f 100644
--- a/src/mos6502.rs
+++ b/src/mos6502.rs
@@ -1,7 +1,10 @@
#![allow(dead_code)]
#![allow(unused_macros)]
+use core::mem::size_of;
use memory::{CPUMemory, VMem};
+use utils::{Read, Write, load_prefix, save_prefix};
+
pub const CPU_FREQ: u32 = 1789773;
#[macro_export]
@@ -588,7 +591,9 @@ enum IntType {
DelayedNMI
}
+#[repr(C)]
pub struct CPU<'a> {
+ /*-- begin state --*/
/* registers */
a: u8,
x: u8,
@@ -596,17 +601,22 @@ pub struct CPU<'a> {
status: u8,
pc: u16,
sp: u8,
- /* internal state */
+ /* internal latches */
acc: bool,
opr: u16,
ea: u16, /* effective address */
imm_val: u8,
pub cycle: u32,
- //pub elapsed: u32,
int: Option<IntType>,
+ /*-- end state --*/
+
+ /*-- begin sub-state --*/
pub mem: CPUMemory<'a>,
+ /*-- end sub-state --*/
}
+const CPU_IGNORED_SIZE: usize = size_of::<CPUMemory>();
+
macro_rules! make_int {
($f:ident, $v: expr) => (
#[inline(always)]
@@ -654,10 +664,19 @@ impl<'a> CPU<'a> {
int: None,
acc: false,
mem,
- //elapsed: 0
}
}
+ pub fn load(&mut self, reader: &mut Read) -> bool {
+ load_prefix(self, CPU_IGNORED_SIZE, reader) &&
+ self.mem.load(reader)
+ }
+
+ pub fn save(&self, writer: &mut Write) -> bool {
+ save_prefix(self, CPU_IGNORED_SIZE, writer) &&
+ self.mem.save(writer)
+ }
+
pub fn powerup(&mut self) {
self.cycle = 2;
self.pc = read16!(self.mem, RESET_VECTOR as u16);
@@ -693,8 +712,7 @@ impl<'a> CPU<'a> {
}
pub fn tick(&mut self) {
- self.cycle -= 1;
- //self.elapsed += 1;
+ self.cycle -= 1
}
pub fn reset(&mut self) {
diff --git a/src/ppu.rs b/src/ppu.rs
index 7c516b6..0edafba 100644
--- a/src/ppu.rs
+++ b/src/ppu.rs
@@ -1,6 +1,8 @@
#![allow(dead_code)]
use memory::{VMem, PPUMemory, CPUBus};
use core::intrinsics::transmute;
+use core::mem::size_of;
+use utils::{Read, Write, load_prefix, save_prefix};
pub trait Screen {
fn put(&mut self, x: u8, y: u8, color: u8);
@@ -17,22 +19,22 @@ struct Sprite {
x: u8
}
+#[repr(C)]
pub struct PPU<'a> {
+ /*-- begin state --*/
pub scanline: u16,
+ pub cycle: u16, /* cycle in the current scanline */
/* registers */
ppuctl: u8,
ppumask: u8,
ppustatus: u8,
oamaddr: u8,
-
reg: u8,
-
x: u8, /* fine x scroll */
v: u16, /* current vram addr */
t: u16, /* temporary vram addr */
w: bool, /* first/second write toggle */
f: bool, /* if it is an odd frame */
- pub cycle: u16, /* cycle in the current scanline */
/* rendering regs & latches */
/* background register (current two tiles) */
bg_pixel: u64,
@@ -51,12 +53,19 @@ pub struct PPU<'a> {
pub vblank_lines: bool,
buffered_read: u8,
early_read: bool,
- /* IO */
+ /*-- end state --*/
+
+ /*-- begin sub-state --*/
mem: PPUMemory<'a>,
+ /*-- end sub-state --*/
+
pub scr: &'a mut Screen,
- //pub elapsed: u32,
}
+const PPU_IGNORED_SIZE: usize =
+ size_of::<PPUMemory>() +
+ size_of::<&mut Screen>();
+
impl<'a> PPU<'a> {
#[inline]
pub fn write_ctl(&mut self, data: u8) {
@@ -191,7 +200,7 @@ impl<'a> PPU<'a> {
#[inline(always)] fn get_show_leftmost_sp(&self) -> bool { (self.ppumask >> 2) & 1 == 1}
#[inline(always)] pub fn get_show_bg(&self) -> bool { (self.ppumask >> 3) & 1 == 1}
#[inline(always)] pub fn get_show_sp(&self) -> bool { (self.ppumask >> 4) & 1 == 1}
- #[inline(always)] pub fn get_flag_vblank(&self) -> bool { (self.ppustatus >> 7) & 1 == 1 }
+ #[inline(always)] fn get_flag_vblank(&self) -> bool { (self.ppustatus >> 7) & 1 == 1 }
#[inline(always)] fn get_oam_arr(&self) -> &[[u8; 4]; 64] {
unsafe {transmute::<&[Sprite; 64], &[[u8; 4]; 64]>(&self.oam)}
}
@@ -478,10 +487,19 @@ impl<'a> PPU<'a> {
buffered_read,
early_read: false,
mem, scr,
- //elapsed: 0,
}
}
+ pub fn load(&mut self, reader: &mut Read) -> bool {
+ load_prefix(self, PPU_IGNORED_SIZE, reader) &&
+ self.mem.load(reader)
+ }
+
+ pub fn save(&self, writer: &mut Write) -> bool {
+ save_prefix(self, PPU_IGNORED_SIZE, writer) &&
+ self.mem.save(writer)
+ }
+
pub fn reset(&mut self) {
self.ppuctl = 0x00;
self.ppumask = 0x00;
@@ -505,7 +523,6 @@ impl<'a> PPU<'a> {
}
fn _tick(&mut self) -> bool {
- //self.elapsed += 1;
let cycle = self.cycle;
if cycle == 0 {
self.cycle = 1;
@@ -571,8 +588,6 @@ impl<'a> PPU<'a> {
if !self.early_read {
self.ppustatus |= PPU::FLAG_VBLANK
}
- //self.elapsed = 0;
- //println!("vbl");
self.early_read = false;
self.vblank = true;
self.scr.render();