From 77838fec27dededbf28d140c330687bd8cb7c73c Mon Sep 17 00:00:00 2001 From: Alex Janka Date: Sat, 11 Mar 2023 08:44:23 +1100 Subject: [PATCH] save state beginning work --- Cargo.lock | 190 ++++++++++++++++++ lib/Cargo.toml | 2 + lib/src/lib.rs | 6 +- lib/src/processor/memory.rs | 48 ++++- lib/src/processor/memory/interrupts.rs | 5 +- lib/src/processor/memory/mmio/apu.rs | 26 +++ lib/src/processor/memory/mmio/apu/channels.rs | 17 +- lib/src/processor/memory/mmio/apu/types.rs | 10 +- lib/src/processor/memory/mmio/gpu.rs | 50 +++++ lib/src/processor/memory/mmio/gpu/types.rs | 23 ++- lib/src/processor/memory/mmio/joypad.rs | 5 +- lib/src/processor/memory/mmio/mod.rs | 2 +- lib/src/processor/memory/mmio/serial.rs | 6 + lib/src/processor/memory/mmio/timer.rs | 5 + lib/src/processor/memory/rom.rs | 32 ++- lib/src/processor/memory/rom/mbcs.rs | 11 +- lib/src/processor/memory/rom/mbcs/mbc1.rs | 34 +++- lib/src/processor/memory/rom/mbcs/mbc2.rs | 24 ++- lib/src/processor/memory/rom/mbcs/mbc3.rs | 32 ++- lib/src/processor/memory/rom/mbcs/mbc5.rs | 32 ++- lib/src/processor/memory/rom/mbcs/none.rs | 6 +- lib/src/processor/mod.rs | 29 ++- 22 files changed, 562 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b28101..7e20779 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,6 +140,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "baseview" version = "0.1.0" @@ -270,6 +276,19 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "num-integer", + "num-traits", + "serde", + "winapi", +] + [[package]] name = "clang-sys" version = "1.6.0" @@ -581,6 +600,50 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" +[[package]] +name = "cxx" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "d3d12" version = "0.5.0" @@ -592,6 +655,41 @@ dependencies = [ "winapi", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "dasp_sample" version = "0.11.0" @@ -790,6 +888,8 @@ dependencies = [ "itertools", "once_cell", "rand", + "serde", + "serde_with", ] [[package]] @@ -940,12 +1040,48 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hexf-parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys 0.8.3", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "indexmap" version = "1.9.2" @@ -954,6 +1090,7 @@ checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", + "serde", ] [[package]] @@ -1154,6 +1291,15 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + [[package]] name = "linux-raw-sys" version = "0.1.4" @@ -1485,6 +1631,16 @@ dependencies = [ "syn", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -1923,6 +2079,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + [[package]] name = "scroll" version = "0.11.0" @@ -2008,6 +2170,34 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ea48c9627169d206b35905699f513f513c303ab9d964a59b44fdcf66c1d1ab7" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e6b7e52858f9f06c25e1c566bbb4ab428200cb3b30053ea09dc50837de7538b" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "shlex" version = "1.1.0" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 3094c46..6ece3f1 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -15,3 +15,5 @@ async-ringbuf = "0.1.2" futures = "0.3.26" once_cell = "1.17.1" itertools = "0.10.5" +serde = { version = "1.0", features = ["derive"] } +serde_with = "2.3.0" diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 0adeb07..c93e6d4 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -5,7 +5,7 @@ use connect::{AudioOutput, EmulatorMessage, Renderer, RomFile}; use once_cell::sync::OnceCell; use processor::{ memory::{mmio::gpu::Colour, Rom}, - Cpu, + Cpu, CpuSaveState, }; use std::{ fs::{self}, @@ -162,4 +162,8 @@ impl + Clone> EmulatorCore { } } } + + pub fn get_save_state(&self) -> CpuSaveState { + CpuSaveState::create(&self.cpu) + } } diff --git a/lib/src/processor/memory.rs b/lib/src/processor/memory.rs index f8e6331..22998a7 100644 --- a/lib/src/processor/memory.rs +++ b/lib/src/processor/memory.rs @@ -1,5 +1,12 @@ -use self::mmio::{gpu::Colour, Apu, Gpu, Joypad, Serial, Timer}; pub use self::rom::Rom; +use self::{ + mmio::{ + apu::ApuSaveState, + gpu::{Colour, GpuSaveState}, + Apu, Gpu, Joypad, Serial, Timer, + }, + rom::RomSaveState, +}; use crate::{ connect::{AudioOutput, JoypadState, Renderer}, processor::SplitRegister, @@ -8,6 +15,7 @@ use crate::{ mod interrupts; pub use interrupts::{Interrupt, Interrupts}; +use serde::{Deserialize, Serialize}; pub mod mmio; pub(crate) mod rom; @@ -29,6 +37,44 @@ pub struct Memory + Clone> { timers: Timer, } +#[serde_with::serde_as] +#[derive(Serialize, Deserialize)] +pub struct MemorySaveState + Clone> { + rom: RomSaveState, + #[serde_as(as = "[_; 8192]")] + ram: [u8; 8192], + #[serde_as(as = "[_; 128]")] + cpu_ram: [u8; 128], + pub(super) interrupts: Interrupts, + pub(super) ime: bool, + pub(super) ime_scheduled: u8, + dma_addr: u8, + joypad: Joypad, + gpu: GpuSaveState, + apu: ApuSaveState, + serial: Serial, + timers: Timer, +} + +impl + Clone> MemorySaveState { + pub fn create(memory: &Memory) -> Self { + Self { + rom: RomSaveState::create(&memory.rom), + ram: memory.ram, + cpu_ram: memory.cpu_ram, + interrupts: memory.interrupts, + ime: memory.ime, + ime_scheduled: memory.ime_scheduled, + dma_addr: memory.dma_addr, + joypad: memory.joypad, + gpu: GpuSaveState::create(&memory.gpu), + apu: ApuSaveState::create(&memory.apu), + serial: memory.serial, + timers: memory.timers, + } + } +} + impl + Clone> Memory { pub fn init( bootrom: Option>, diff --git a/lib/src/processor/memory/interrupts.rs b/lib/src/processor/memory/interrupts.rs index 69e9f42..26787cd 100644 --- a/lib/src/processor/memory/interrupts.rs +++ b/lib/src/processor/memory/interrupts.rs @@ -1,6 +1,8 @@ +use serde::{Deserialize, Serialize}; + use crate::util::get_bit; -#[derive(Default, Debug)] +#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)] struct InterruptRegister { vblank: bool, lcd_stat: bool, @@ -40,6 +42,7 @@ pub enum Interrupt { Joypad, } +#[derive(Copy, Clone, Serialize, Deserialize)] pub struct Interrupts { // 0xFFFF enable_register: InterruptRegister, diff --git a/lib/src/processor/memory/mmio/apu.rs b/lib/src/processor/memory/mmio/apu.rs index 4894504..4e6294c 100644 --- a/lib/src/processor/memory/mmio/apu.rs +++ b/lib/src/processor/memory/mmio/apu.rs @@ -9,6 +9,7 @@ use crate::{ }; use futures::executor; use itertools::izip; +use serde::{Deserialize, Serialize}; mod channels; mod downsampler; @@ -47,6 +48,31 @@ pub struct Apu { output: AudioOutput, } +#[derive(Serialize, Deserialize)] +pub struct ApuSaveState { + apu_enable: bool, + channels: Channels, + vin: VinEnable, + mixer: Mixer, + div_apu: u8, + buffer: Vec, + out_buffer: Vec<[f32; 2]>, +} + +impl ApuSaveState { + pub fn create(apu: &Apu) -> Self { + Self { + apu_enable: apu.apu_enable, + channels: apu.channels, + vin: apu.vin, + mixer: apu.mixer, + div_apu: apu.div_apu, + buffer: apu.buffer.clone(), + out_buffer: apu.out_buffer.clone(), + } + } +} + const CYCLES_PER_FRAME: usize = 70224; impl Apu { diff --git a/lib/src/processor/memory/mmio/apu/channels.rs b/lib/src/processor/memory/mmio/apu/channels.rs index dc839a3..fea2095 100644 --- a/lib/src/processor/memory/mmio/apu/channels.rs +++ b/lib/src/processor/memory/mmio/apu/channels.rs @@ -1,14 +1,17 @@ +use serde::{Deserialize, Serialize}; + use crate::{ processor::memory::Address, util::{get_bit, set_or_clear_bit, Nibbles}, }; -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)] pub(super) enum EnvelopeMode { Increase, Decrease, } +#[derive(Serialize, Deserialize, Clone, Copy)] struct Sweep { pace: u8, mode: EnvelopeMode, @@ -27,7 +30,7 @@ impl Default for Sweep { } } -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)] struct Envelope { initial_volume: u8, current_volume: u8, @@ -72,6 +75,7 @@ impl Default for Envelope { } } +#[derive(Serialize, Deserialize, Clone, Copy)] enum DutyCycle { // 0b00 TwelvePointFive, @@ -120,6 +124,7 @@ impl DutyCycle { } } +#[derive(Serialize, Deserialize, Clone, Copy)] pub(super) struct PwmChannel { pub(super) enabled: bool, sweep: Sweep, @@ -297,7 +302,7 @@ impl PwmChannel { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Copy)] enum ShiftVolumePercent { Zero, TwentyFive, @@ -316,6 +321,7 @@ impl ShiftVolumePercent { } } +#[derive(Serialize, Deserialize, Clone, Copy)] pub(super) struct WaveRam { pub(super) data: [u8; 16], } @@ -330,6 +336,7 @@ impl WaveRam { } } +#[derive(Serialize, Deserialize, Clone, Copy)] pub(super) struct WaveChannel { pub(super) enabled: bool, dac_enabled: bool, @@ -471,12 +478,13 @@ impl WaveChannel { } } -#[derive(PartialEq)] +#[derive(PartialEq, Serialize, Deserialize, Clone, Copy)] enum LfsrWidth { FifteenBit, SevenBit, } +#[derive(Serialize, Deserialize, Clone, Copy)] struct Lfsr { clock_shift: u8, width: LfsrWidth, @@ -534,6 +542,7 @@ impl Default for Lfsr { } } +#[derive(Serialize, Deserialize, Clone, Copy)] pub(super) struct NoiseChannel { pub(super) enabled: bool, length_enable: bool, diff --git a/lib/src/processor/memory/mmio/apu/types.rs b/lib/src/processor/memory/mmio/apu/types.rs index 1e71038..bc66005 100644 --- a/lib/src/processor/memory/mmio/apu/types.rs +++ b/lib/src/processor/memory/mmio/apu/types.rs @@ -1,5 +1,8 @@ +use serde::{Deserialize, Serialize}; + use super::channels::{NoiseChannel, PwmChannel, WaveChannel}; +#[derive(Serialize, Deserialize, Clone, Copy)] pub(super) struct Channels { pub(super) one: PwmChannel, pub(super) two: PwmChannel, @@ -18,12 +21,13 @@ impl Default for Channels { } } -#[derive(Default)] +#[derive(Default, Serialize, Deserialize, Clone, Copy)] pub(super) struct VinEnable { pub(super) left: bool, pub(super) right: bool, } +#[derive(Serialize, Deserialize, Clone, Copy)] pub(super) enum Volume { Muted, Enabled, @@ -53,6 +57,7 @@ impl Volume { } } +#[derive(Serialize, Deserialize, Clone, Copy)] pub(super) struct ChannelVol { pub(super) left: Volume, pub(super) right: Volume, @@ -67,6 +72,7 @@ impl Default for ChannelVol { } } +#[derive(Serialize, Deserialize, Clone, Copy)] pub(super) struct Mixer { pub(super) vol_left: u8, pub(super) vol_right: u8, @@ -89,7 +95,7 @@ impl Default for Mixer { } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Serialize, Deserialize)] pub(super) struct DacSample { pub(super) one: f32, pub(super) two: f32, diff --git a/lib/src/processor/memory/mmio/gpu.rs b/lib/src/processor/memory/mmio/gpu.rs index 8d22f57..3ae431d 100644 --- a/lib/src/processor/memory/mmio/gpu.rs +++ b/lib/src/processor/memory/mmio/gpu.rs @@ -11,6 +11,7 @@ use crate::{ util::{clear_bit, get_bit}, HEIGHT, WIDTH, }; +use serde::{Deserialize, Serialize}; pub use types::Colour; mod addresses; @@ -44,6 +45,55 @@ pub struct Gpu> { prev_stat: bool, } +#[derive(Serialize, Deserialize)] +pub struct GpuSaveState> { + buffer: Vec, + vram: Vram, + oam: Oam, + is_bg_zero: Vec, + lcdc: Lcdc, + stat: Stat, + mode_clock: usize, + scanline: u8, + lyc: u8, + window_lc: u8, + has_window_been_enabled: bool, + bg_palette: Palette, + obj_palette_0: Palette, + obj_palette_1: Palette, + scx: u8, + scy: u8, + wx: u8, + wy: u8, + prev_stat: bool, +} + +impl + Clone> GpuSaveState { + pub fn create(gpu: &Gpu) -> Self { + Self { + buffer: gpu.buffer.clone(), + vram: gpu.vram, + oam: gpu.oam, + is_bg_zero: gpu.is_bg_zero.clone(), + lcdc: gpu.lcdc, + stat: gpu.stat, + mode_clock: gpu.mode_clock, + scanline: gpu.scanline, + lyc: gpu.lyc, + window_lc: gpu.window_lc, + has_window_been_enabled: gpu.has_window_been_enabled, + bg_palette: gpu.bg_palette, + obj_palette_0: gpu.obj_palette_0, + obj_palette_1: gpu.obj_palette_1, + scx: gpu.scx, + scy: gpu.scy, + wx: gpu.wx, + wy: gpu.wy, + prev_stat: gpu.prev_stat, + } + } +} + impl + Clone> Gpu { pub fn new( window: Box>, diff --git a/lib/src/processor/memory/mmio/gpu/types.rs b/lib/src/processor/memory/mmio/gpu/types.rs index ba1eccd..ae22c94 100644 --- a/lib/src/processor/memory/mmio/gpu/types.rs +++ b/lib/src/processor/memory/mmio/gpu/types.rs @@ -1,9 +1,11 @@ +use serde::{Deserialize, Serialize}; + use crate::{ processor::memory::Address, util::{as_signed, get_bit}, }; -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] pub(super) enum DrawMode { HBlank, VBlank, @@ -11,7 +13,7 @@ pub(super) enum DrawMode { Mode3, } -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] pub(super) enum TilemapArea { T9800, T9C00, @@ -26,7 +28,7 @@ impl TilemapArea { } } -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] pub(super) enum TiledataArea { D8000, D9000, @@ -41,7 +43,7 @@ impl TiledataArea { } } -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] pub(super) enum ObjSize { S8x8, S8x16, @@ -56,7 +58,7 @@ impl ObjSize { } } -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] pub(super) struct Lcdc { pub(super) enable: bool, pub(super) window_tilemap: TilemapArea, @@ -83,7 +85,7 @@ impl Default for Lcdc { } } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub enum Colour { White, LightGray, @@ -138,7 +140,7 @@ impl Colour { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub(super) struct Palette { pub(super) zero: Colour, pub(super) one: Colour, @@ -193,6 +195,7 @@ pub(super) struct Object { pub(super) oam_location: u8, } +#[derive(Serialize, Deserialize, Clone, Copy)] pub(super) struct Stat { pub(super) lyc_eq_ly_interrupt_enabled: bool, pub(super) mode_2_interrupt_enabled: bool, @@ -213,7 +216,10 @@ impl Default for Stat { } } +#[serde_with::serde_as] +#[derive(Serialize, Deserialize, Clone, Copy)] pub struct Vram { + #[serde_as(as = "[_; 8192]")] data: [u8; 8192], } @@ -233,7 +239,10 @@ impl Default for Vram { } } +#[serde_with::serde_as] +#[derive(Serialize, Deserialize, Clone, Copy)] pub struct Oam { + #[serde_as(as = "[_; 160]")] pub data: [u8; 160], } diff --git a/lib/src/processor/memory/mmio/joypad.rs b/lib/src/processor/memory/mmio/joypad.rs index 4e93fe2..2a761e3 100644 --- a/lib/src/processor/memory/mmio/joypad.rs +++ b/lib/src/processor/memory/mmio/joypad.rs @@ -1,9 +1,12 @@ +use serde::{Deserialize, Serialize}; + use crate::util::{clear_bit, get_bit}; -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub struct Joypad { sel_action: bool, sel_direction: bool, + #[serde(skip)] state: JoypadState, } diff --git a/lib/src/processor/memory/mmio/mod.rs b/lib/src/processor/memory/mmio/mod.rs index ea93165..20e7425 100644 --- a/lib/src/processor/memory/mmio/mod.rs +++ b/lib/src/processor/memory/mmio/mod.rs @@ -1,4 +1,4 @@ -mod apu; +pub(crate) mod apu; pub(crate) mod gpu; pub(crate) mod joypad; mod serial; diff --git a/lib/src/processor/memory/mmio/serial.rs b/lib/src/processor/memory/mmio/serial.rs index 94d807b..6e7925c 100644 --- a/lib/src/processor/memory/mmio/serial.rs +++ b/lib/src/processor/memory/mmio/serial.rs @@ -1,19 +1,24 @@ use std::io::{stdout, Write}; +use serde::{Deserialize, Serialize}; + use crate::util::get_bit; +#[derive(Copy, Clone, Serialize, Deserialize)] enum ClockSource { Internal, External, } #[allow(dead_code)] +#[derive(Copy, Clone, Serialize, Deserialize)] enum ClockSpeed { Normal, Fast, } #[allow(dead_code)] +#[derive(Copy, Clone, Serialize, Deserialize)] struct SerialControl { transfer_in_progress: bool, clock_speed: ClockSpeed, @@ -30,6 +35,7 @@ impl Default for SerialControl { } } +#[derive(Copy, Clone, Serialize, Deserialize)] pub struct Serial { byte: u8, output_byte: u8, diff --git a/lib/src/processor/memory/mmio/timer.rs b/lib/src/processor/memory/mmio/timer.rs index 5a4736f..94409bf 100644 --- a/lib/src/processor/memory/mmio/timer.rs +++ b/lib/src/processor/memory/mmio/timer.rs @@ -1,5 +1,8 @@ +use serde::{Deserialize, Serialize}; + use crate::util::{get_bit, set_or_clear_bit}; +#[derive(Copy, Clone, Serialize, Deserialize)] enum TimerRate { Sixteen, SixtyFour, @@ -36,6 +39,7 @@ impl TimerRate { } } +#[derive(Copy, Clone, Serialize, Deserialize)] struct TimerControl { enable: bool, rate: TimerRate, @@ -56,6 +60,7 @@ pub struct TimerReturn { pub timer_interrupt: bool, } +#[derive(Copy, Clone, Serialize, Deserialize)] pub struct Timer { // 0xFF04 div: u8, diff --git a/lib/src/processor/memory/rom.rs b/lib/src/processor/memory/rom.rs index d8857be..ff7b4fa 100644 --- a/lib/src/processor/memory/rom.rs +++ b/lib/src/processor/memory/rom.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::processor::memory::Address; use std::{ fs::{File, OpenOptions}, @@ -6,13 +8,17 @@ use std::{ str::from_utf8_unchecked, }; -use self::mbcs::{Mbc, Mbc1, Mbc2, Mbc3, Mbc5, None}; +use self::mbcs::{ + Mbc, Mbc1, Mbc1SaveState, Mbc2, Mbc2SaveState, Mbc3, Mbc3SaveState, Mbc5, Mbc5SaveState, None, +}; mod mbcs; +#[derive(Serialize, Deserialize)] struct MaybeBufferedSram { buf: Vec, length: usize, + #[serde(skip)] inner: Option, unbuffered_writes: usize, } @@ -97,6 +103,30 @@ pub struct Rom { mbc: Box, } +#[derive(Serialize, Deserialize)] +pub struct RomSaveState { + title: String, + mbc: MbcSaveState, +} + +impl RomSaveState { + pub fn create(rom: &Rom) -> Self { + Self { + title: rom.title.clone(), + mbc: rom.mbc.get_savestate(), + } + } +} + +#[derive(Serialize, Deserialize)] +enum MbcSaveState { + Mbc1(Mbc1SaveState), + Mbc2(Mbc2SaveState), + Mbc3(Mbc3SaveState), + Mbc5(Mbc5SaveState), + None, +} + impl Rom { pub fn load(data: Vec, save_path: Option) -> Self { let mut title_length = 0x143; diff --git a/lib/src/processor/memory/rom/mbcs.rs b/lib/src/processor/memory/rom/mbcs.rs index 18dc4af..8cfa501 100644 --- a/lib/src/processor/memory/rom/mbcs.rs +++ b/lib/src/processor/memory/rom/mbcs.rs @@ -5,12 +5,14 @@ mod mbc2; mod mbc3; mod mbc5; mod none; -pub use mbc1::Mbc1; -pub use mbc2::Mbc2; -pub use mbc3::Mbc3; -pub use mbc5::Mbc5; +pub use mbc1::{Mbc1, Mbc1SaveState}; +pub use mbc2::{Mbc2, Mbc2SaveState}; +pub use mbc3::{Mbc3, Mbc3SaveState}; +pub use mbc5::{Mbc5, Mbc5SaveState}; pub use none::None; +use super::MbcSaveState; + pub(super) const KB: usize = 1024; const ROM_BANK_SIZE: usize = 16 * KB; const RAM_BANK_SIZE: usize = 8 * KB; @@ -23,6 +25,7 @@ pub(super) trait Mbc: Send { fn set(&mut self, address: Address, data: u8); fn set_ram(&mut self, address: Address, data: u8); fn mbc_type(&self) -> String; + fn get_savestate(&self) -> MbcSaveState; fn is_rumbling(&self) -> bool { false } diff --git a/lib/src/processor/memory/rom/mbcs/mbc1.rs b/lib/src/processor/memory/rom/mbcs/mbc1.rs index 7d93b6b..5ee57a9 100644 --- a/lib/src/processor/memory/rom/mbcs/mbc1.rs +++ b/lib/src/processor/memory/rom/mbcs/mbc1.rs @@ -1,9 +1,14 @@ use std::path::PathBuf; -use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE}; -use crate::processor::memory::{rom::MaybeBufferedSram, Address}; +use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy)] +use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE}; +use crate::processor::memory::{ + rom::{MaybeBufferedSram, MbcSaveState}, + Address, +}; + +#[derive(Clone, Copy, Serialize, Deserialize)] enum BankingMode { Simple, Advanced, @@ -20,6 +25,17 @@ pub struct Mbc1 { bank_mode: BankingMode, } +#[derive(Serialize, Deserialize)] +pub struct Mbc1SaveState { + rom_len: usize, + rom_bank: u8, + ram_enabled: bool, + ram: Option>, + ram_bank: u8, + upper_banks: u8, + bank_mode: BankingMode, +} + impl Mbc1 { pub fn init(data: Vec, rom_size: u8, ram_size: u8, save_file: Option) -> Self { let rom_len = rom_banks(rom_size) * ROM_BANK_SIZE; @@ -102,6 +118,18 @@ impl Mbc for Mbc1 { ram.flush(); } } + + fn get_savestate(&self) -> MbcSaveState { + MbcSaveState::Mbc1(Mbc1SaveState { + rom_len: self.rom_len, + rom_bank: self.rom_bank, + ram_enabled: self.ram_enabled, + ram: self.ram.as_ref().map(|v| v.buf.clone()), + ram_bank: self.ram_bank, + upper_banks: self.upper_banks, + bank_mode: self.bank_mode, + }) + } } impl Mbc1 { diff --git a/lib/src/processor/memory/rom/mbcs/mbc2.rs b/lib/src/processor/memory/rom/mbcs/mbc2.rs index 079dc28..d0fcb5f 100644 --- a/lib/src/processor/memory/rom/mbcs/mbc2.rs +++ b/lib/src/processor/memory/rom/mbcs/mbc2.rs @@ -1,6 +1,11 @@ use std::path::PathBuf; -use crate::processor::memory::{rom::MaybeBufferedSram, Address}; +use serde::{Deserialize, Serialize}; + +use crate::processor::memory::{ + rom::{MaybeBufferedSram, MbcSaveState}, + Address, +}; use super::{rom_banks, Mbc, ROM_BANK_SIZE}; @@ -12,6 +17,14 @@ pub struct Mbc2 { ram_enabled: bool, } +#[derive(Serialize, Deserialize)] +pub struct Mbc2SaveState { + rom_len: usize, + rom_bank: u8, + ram: Vec, + ram_enabled: bool, +} + impl Mbc2 { pub fn init(data: Vec, rom_size: u8, save_file: Option) -> Self { let rom_len = rom_banks(rom_size) * ROM_BANK_SIZE; @@ -75,4 +88,13 @@ impl Mbc for Mbc2 { fn flush(&mut self) { self.ram.flush(); } + + fn get_savestate(&self) -> MbcSaveState { + MbcSaveState::Mbc2(Mbc2SaveState { + rom_len: self.rom_len, + rom_bank: self.rom_bank, + ram: self.ram.buf.clone(), + ram_enabled: self.ram_enabled, + }) + } } diff --git a/lib/src/processor/memory/rom/mbcs/mbc3.rs b/lib/src/processor/memory/rom/mbcs/mbc3.rs index fa635bb..103430d 100644 --- a/lib/src/processor/memory/rom/mbcs/mbc3.rs +++ b/lib/src/processor/memory/rom/mbcs/mbc3.rs @@ -1,6 +1,11 @@ +use serde::{Deserialize, Serialize}; + use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE}; use crate::{ - processor::memory::{rom::MaybeBufferedSram, Address}, + processor::memory::{ + rom::{MaybeBufferedSram, MbcSaveState}, + Address, + }, util::set_or_clear_bit, }; use std::{ @@ -8,7 +13,7 @@ use std::{ time::{Duration, Instant}, }; -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize, Clone, Copy)] enum RtcRegister { Seconds, Minutes, @@ -17,6 +22,7 @@ enum RtcRegister { Misc, } +#[derive(Serialize, Deserialize, Clone, Copy)] enum RamBank { Ram(u8), Rtc(RtcRegister), @@ -117,6 +123,17 @@ pub struct Mbc3 { ram_size: usize, ram_enabled: bool, rtc: Option, + // TODO - save/load rtc!! +} + +#[derive(Serialize, Deserialize)] +pub struct Mbc3SaveState { + rom_bank: u8, + rom_size: usize, + ram: Option>, + ram_bank: RamBank, + ram_size: usize, + ram_enabled: bool, } impl Mbc3 { @@ -257,4 +274,15 @@ impl Mbc for Mbc3 { ram.flush(); } } + + fn get_savestate(&self) -> MbcSaveState { + MbcSaveState::Mbc3(Mbc3SaveState { + rom_bank: self.rom_bank, + rom_size: self.rom_size, + ram: self.ram.as_ref().map(|v| v.buf.clone()), + ram_bank: self.ram_bank, + ram_size: self.ram_size, + ram_enabled: self.ram_enabled, + }) + } } diff --git a/lib/src/processor/memory/rom/mbcs/mbc5.rs b/lib/src/processor/memory/rom/mbcs/mbc5.rs index c18f28d..fdb20b8 100644 --- a/lib/src/processor/memory/rom/mbcs/mbc5.rs +++ b/lib/src/processor/memory/rom/mbcs/mbc5.rs @@ -1,7 +1,12 @@ use std::path::PathBuf; +use serde::{Deserialize, Serialize}; + use crate::{ - processor::memory::{rom::MaybeBufferedSram, Address}, + processor::memory::{ + rom::{MaybeBufferedSram, MbcSaveState}, + Address, + }, util::get_bit, }; @@ -19,6 +24,18 @@ pub struct Mbc5 { is_rumbling: bool, } +#[derive(Serialize, Deserialize)] +pub struct Mbc5SaveState { + rom_bank: u16, + rom_size: usize, + ram: Option>, + ram_bank: u8, + ram_size: usize, + ram_enabled: bool, + rumble: bool, + is_rumbling: bool, +} + impl Mbc5 { pub fn init( data: Vec, @@ -127,4 +144,17 @@ impl Mbc for Mbc5 { ram.flush(); } } + + fn get_savestate(&self) -> MbcSaveState { + MbcSaveState::Mbc5(Mbc5SaveState { + rom_bank: self.rom_bank, + rom_size: self.rom_size, + ram: self.ram.as_ref().map(|v| v.buf.clone()), + ram_bank: self.ram_bank, + ram_size: self.ram_size, + ram_enabled: self.ram_enabled, + rumble: self.rumble, + is_rumbling: self.is_rumbling, + }) + } } diff --git a/lib/src/processor/memory/rom/mbcs/none.rs b/lib/src/processor/memory/rom/mbcs/none.rs index 246b817..1198047 100644 --- a/lib/src/processor/memory/rom/mbcs/none.rs +++ b/lib/src/processor/memory/rom/mbcs/none.rs @@ -1,5 +1,5 @@ use super::Mbc; -use crate::processor::memory::Address; +use crate::processor::memory::{rom::MbcSaveState, Address}; pub struct None { data: Vec, @@ -27,4 +27,8 @@ impl Mbc for None { fn mbc_type(&self) -> String { String::from("None") } + + fn get_savestate(&self) -> MbcSaveState { + MbcSaveState::None + } } diff --git a/lib/src/processor/mod.rs b/lib/src/processor/mod.rs index b05c03e..58c7f04 100644 --- a/lib/src/processor/mod.rs +++ b/lib/src/processor/mod.rs @@ -1,4 +1,6 @@ -use self::memory::{mmio::gpu::Colour, Interrupt, Memory}; +use serde::{Deserialize, Serialize}; + +use self::memory::{mmio::gpu::Colour, Interrupt, Memory, MemorySaveState}; use crate::verbose_println; mod instructions; @@ -27,6 +29,29 @@ pub struct Cpu + Clone> { should_halt_bug: bool, } +#[derive(Serialize, Deserialize)] +pub struct CpuSaveState + Clone> { + memory: MemorySaveState, + reg: Registers, + last_instruction: u8, + last_instruction_addr: u16, + halted: bool, + should_halt_bug: bool, +} + +impl + Clone> CpuSaveState { + pub fn create(cpu: &Cpu) -> Self { + Self { + memory: MemorySaveState::create(&cpu.memory), + reg: cpu.reg, + last_instruction: cpu.last_instruction, + last_instruction_addr: cpu.last_instruction_addr, + halted: cpu.halted, + should_halt_bug: cpu.should_halt_bug, + } + } +} + impl + Clone> Cpu { pub fn new(mut memory: Memory, run_bootrom: bool) -> Self { if !run_bootrom { @@ -138,7 +163,7 @@ pub enum Reg8 { L, } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Serialize, Deserialize)] pub struct Registers { pub af: u16, pub bc: u16,