From 96c7c1e4fee261ecf386fb2fff2155f06a2f7973 Mon Sep 17 00:00:00 2001 From: Determinant Date: Mon, 8 Jan 2018 00:32:32 +0800 Subject: support console state load/save (minor bugs still present) --- src/apu.rs | 175 ++++++++++++++++++++++++++++++++++++------------------ src/bin.rs | 105 +++++++++++++++++++++++++++++--- src/cartridge.rs | 6 +- src/controller.rs | 13 ++++ src/lib.rs | 2 + src/mapper.rs | 137 ++++++++++++++++++++++++++++++++++++++---- src/memory.rs | 78 +++++++++++++++++++++--- src/mos6502.rs | 28 +++++++-- src/ppu.rs | 35 +++++++---- 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::() + + size_of::() + + size_of::() + + size_of::() + + size_of::() + + size_of::() + + size_of::() + + size_of::() + + size_of::() + + size_of::() + + 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 { + match self.0.read(buf) { + Ok(x) => Some(x), + Err(_) => None + } + } +} + +impl utils::Write for FileIO { + fn write(&mut self, buf: &[u8]) -> Option { + 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, + exit_flag: &'a Cell } 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) -> 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 = 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, reg: Cell, @@ -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, + cpu_stall: Cell, + /*-- end state --*/ + + /*-- begin sub-state --*/ + ppu_sampler: RefCell, + /*-- end sub-state --*/ + cpu: *mut CPU<'a>, ppu: *mut PPU<'a>, apu: *mut APU<'a>, - ppu_sampler: RefCell, - nmi_after_tick: Cell, - cpu_stall: Cell } +const CPUBUS_IGNORED_SIZE: usize = + size_of::>() + + 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::() + + size_of::<&RefMapper>() + + size_of::>() + + size_of::>(); + 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, + /*-- end state --*/ + + /*-- begin sub-state --*/ pub mem: CPUMemory<'a>, + /*-- end sub-state --*/ } +const CPU_IGNORED_SIZE: usize = size_of::(); + 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::() + + 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(); -- cgit v1.2.3