save state beginning work

This commit is contained in:
Alex Janka 2023-03-11 08:44:23 +11:00
parent 962b2af282
commit 77838fec27
22 changed files with 562 additions and 33 deletions

190
Cargo.lock generated
View file

@ -140,6 +140,12 @@ dependencies = [
"rustc-demangle", "rustc-demangle",
] ]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]] [[package]]
name = "baseview" name = "baseview"
version = "0.1.0" version = "0.1.0"
@ -270,6 +276,19 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" 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]] [[package]]
name = "clang-sys" name = "clang-sys"
version = "1.6.0" version = "1.6.0"
@ -581,6 +600,50 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" 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]] [[package]]
name = "d3d12" name = "d3d12"
version = "0.5.0" version = "0.5.0"
@ -592,6 +655,41 @@ dependencies = [
"winapi", "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]] [[package]]
name = "dasp_sample" name = "dasp_sample"
version = "0.11.0" version = "0.11.0"
@ -790,6 +888,8 @@ dependencies = [
"itertools", "itertools",
"once_cell", "once_cell",
"rand", "rand",
"serde",
"serde_with",
] ]
[[package]] [[package]]
@ -940,12 +1040,48 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "hexf-parse" name = "hexf-parse"
version = "0.2.1" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" 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]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.9.2" version = "1.9.2"
@ -954,6 +1090,7 @@ checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown", "hashbrown",
"serde",
] ]
[[package]] [[package]]
@ -1154,6 +1291,15 @@ dependencies = [
"pkg-config", "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]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.1.4" version = "0.1.4"
@ -1485,6 +1631,16 @@ dependencies = [
"syn", "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]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.15" version = "0.2.15"
@ -1923,6 +2079,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "scratch"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
[[package]] [[package]]
name = "scroll" name = "scroll"
version = "0.11.0" version = "0.11.0"
@ -2008,6 +2170,34 @@ dependencies = [
"serde", "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]] [[package]]
name = "shlex" name = "shlex"
version = "1.1.0" version = "1.1.0"

View file

@ -15,3 +15,5 @@ async-ringbuf = "0.1.2"
futures = "0.3.26" futures = "0.3.26"
once_cell = "1.17.1" once_cell = "1.17.1"
itertools = "0.10.5" itertools = "0.10.5"
serde = { version = "1.0", features = ["derive"] }
serde_with = "2.3.0"

View file

@ -5,7 +5,7 @@ use connect::{AudioOutput, EmulatorMessage, Renderer, RomFile};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use processor::{ use processor::{
memory::{mmio::gpu::Colour, Rom}, memory::{mmio::gpu::Colour, Rom},
Cpu, Cpu, CpuSaveState,
}; };
use std::{ use std::{
fs::{self}, fs::{self},
@ -162,4 +162,8 @@ impl<ColourFormat: From<Colour> + Clone> EmulatorCore<ColourFormat> {
} }
} }
} }
pub fn get_save_state(&self) -> CpuSaveState<ColourFormat> {
CpuSaveState::create(&self.cpu)
}
} }

View file

@ -1,5 +1,12 @@
use self::mmio::{gpu::Colour, Apu, Gpu, Joypad, Serial, Timer};
pub use self::rom::Rom; pub use self::rom::Rom;
use self::{
mmio::{
apu::ApuSaveState,
gpu::{Colour, GpuSaveState},
Apu, Gpu, Joypad, Serial, Timer,
},
rom::RomSaveState,
};
use crate::{ use crate::{
connect::{AudioOutput, JoypadState, Renderer}, connect::{AudioOutput, JoypadState, Renderer},
processor::SplitRegister, processor::SplitRegister,
@ -8,6 +15,7 @@ use crate::{
mod interrupts; mod interrupts;
pub use interrupts::{Interrupt, Interrupts}; pub use interrupts::{Interrupt, Interrupts};
use serde::{Deserialize, Serialize};
pub mod mmio; pub mod mmio;
pub(crate) mod rom; pub(crate) mod rom;
@ -29,6 +37,44 @@ pub struct Memory<ColourFormat: From<Colour> + Clone> {
timers: Timer, timers: Timer,
} }
#[serde_with::serde_as]
#[derive(Serialize, Deserialize)]
pub struct MemorySaveState<ColourFormat: From<Colour> + 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<ColourFormat>,
apu: ApuSaveState,
serial: Serial,
timers: Timer,
}
impl<ColourFormat: From<Colour> + Clone> MemorySaveState<ColourFormat> {
pub fn create(memory: &Memory<ColourFormat>) -> 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<ColourFormat: From<Colour> + Clone> Memory<ColourFormat> { impl<ColourFormat: From<Colour> + Clone> Memory<ColourFormat> {
pub fn init( pub fn init(
bootrom: Option<Vec<u8>>, bootrom: Option<Vec<u8>>,

View file

@ -1,6 +1,8 @@
use serde::{Deserialize, Serialize};
use crate::util::get_bit; use crate::util::get_bit;
#[derive(Default, Debug)] #[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)]
struct InterruptRegister { struct InterruptRegister {
vblank: bool, vblank: bool,
lcd_stat: bool, lcd_stat: bool,
@ -40,6 +42,7 @@ pub enum Interrupt {
Joypad, Joypad,
} }
#[derive(Copy, Clone, Serialize, Deserialize)]
pub struct Interrupts { pub struct Interrupts {
// 0xFFFF // 0xFFFF
enable_register: InterruptRegister, enable_register: InterruptRegister,

View file

@ -9,6 +9,7 @@ use crate::{
}; };
use futures::executor; use futures::executor;
use itertools::izip; use itertools::izip;
use serde::{Deserialize, Serialize};
mod channels; mod channels;
mod downsampler; mod downsampler;
@ -47,6 +48,31 @@ pub struct Apu {
output: AudioOutput, output: AudioOutput,
} }
#[derive(Serialize, Deserialize)]
pub struct ApuSaveState {
apu_enable: bool,
channels: Channels,
vin: VinEnable,
mixer: Mixer,
div_apu: u8,
buffer: Vec<DacSample>,
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; const CYCLES_PER_FRAME: usize = 70224;
impl Apu { impl Apu {

View file

@ -1,14 +1,17 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
processor::memory::Address, processor::memory::Address,
util::{get_bit, set_or_clear_bit, Nibbles}, util::{get_bit, set_or_clear_bit, Nibbles},
}; };
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
pub(super) enum EnvelopeMode { pub(super) enum EnvelopeMode {
Increase, Increase,
Decrease, Decrease,
} }
#[derive(Serialize, Deserialize, Clone, Copy)]
struct Sweep { struct Sweep {
pace: u8, pace: u8,
mode: EnvelopeMode, mode: EnvelopeMode,
@ -27,7 +30,7 @@ impl Default for Sweep {
} }
} }
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
struct Envelope { struct Envelope {
initial_volume: u8, initial_volume: u8,
current_volume: u8, current_volume: u8,
@ -72,6 +75,7 @@ impl Default for Envelope {
} }
} }
#[derive(Serialize, Deserialize, Clone, Copy)]
enum DutyCycle { enum DutyCycle {
// 0b00 // 0b00
TwelvePointFive, TwelvePointFive,
@ -120,6 +124,7 @@ impl DutyCycle {
} }
} }
#[derive(Serialize, Deserialize, Clone, Copy)]
pub(super) struct PwmChannel { pub(super) struct PwmChannel {
pub(super) enabled: bool, pub(super) enabled: bool,
sweep: Sweep, sweep: Sweep,
@ -297,7 +302,7 @@ impl PwmChannel {
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Copy)]
enum ShiftVolumePercent { enum ShiftVolumePercent {
Zero, Zero,
TwentyFive, TwentyFive,
@ -316,6 +321,7 @@ impl ShiftVolumePercent {
} }
} }
#[derive(Serialize, Deserialize, Clone, Copy)]
pub(super) struct WaveRam { pub(super) struct WaveRam {
pub(super) data: [u8; 16], pub(super) data: [u8; 16],
} }
@ -330,6 +336,7 @@ impl WaveRam {
} }
} }
#[derive(Serialize, Deserialize, Clone, Copy)]
pub(super) struct WaveChannel { pub(super) struct WaveChannel {
pub(super) enabled: bool, pub(super) enabled: bool,
dac_enabled: bool, dac_enabled: bool,
@ -471,12 +478,13 @@ impl WaveChannel {
} }
} }
#[derive(PartialEq)] #[derive(PartialEq, Serialize, Deserialize, Clone, Copy)]
enum LfsrWidth { enum LfsrWidth {
FifteenBit, FifteenBit,
SevenBit, SevenBit,
} }
#[derive(Serialize, Deserialize, Clone, Copy)]
struct Lfsr { struct Lfsr {
clock_shift: u8, clock_shift: u8,
width: LfsrWidth, width: LfsrWidth,
@ -534,6 +542,7 @@ impl Default for Lfsr {
} }
} }
#[derive(Serialize, Deserialize, Clone, Copy)]
pub(super) struct NoiseChannel { pub(super) struct NoiseChannel {
pub(super) enabled: bool, pub(super) enabled: bool,
length_enable: bool, length_enable: bool,

View file

@ -1,5 +1,8 @@
use serde::{Deserialize, Serialize};
use super::channels::{NoiseChannel, PwmChannel, WaveChannel}; use super::channels::{NoiseChannel, PwmChannel, WaveChannel};
#[derive(Serialize, Deserialize, Clone, Copy)]
pub(super) struct Channels { pub(super) struct Channels {
pub(super) one: PwmChannel, pub(super) one: PwmChannel,
pub(super) two: 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) struct VinEnable {
pub(super) left: bool, pub(super) left: bool,
pub(super) right: bool, pub(super) right: bool,
} }
#[derive(Serialize, Deserialize, Clone, Copy)]
pub(super) enum Volume { pub(super) enum Volume {
Muted, Muted,
Enabled, Enabled,
@ -53,6 +57,7 @@ impl Volume {
} }
} }
#[derive(Serialize, Deserialize, Clone, Copy)]
pub(super) struct ChannelVol { pub(super) struct ChannelVol {
pub(super) left: Volume, pub(super) left: Volume,
pub(super) right: Volume, pub(super) right: Volume,
@ -67,6 +72,7 @@ impl Default for ChannelVol {
} }
} }
#[derive(Serialize, Deserialize, Clone, Copy)]
pub(super) struct Mixer { pub(super) struct Mixer {
pub(super) vol_left: u8, pub(super) vol_left: u8,
pub(super) vol_right: 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) struct DacSample {
pub(super) one: f32, pub(super) one: f32,
pub(super) two: f32, pub(super) two: f32,

View file

@ -11,6 +11,7 @@ use crate::{
util::{clear_bit, get_bit}, util::{clear_bit, get_bit},
HEIGHT, WIDTH, HEIGHT, WIDTH,
}; };
use serde::{Deserialize, Serialize};
pub use types::Colour; pub use types::Colour;
mod addresses; mod addresses;
@ -44,6 +45,55 @@ pub struct Gpu<Format: From<Colour>> {
prev_stat: bool, prev_stat: bool,
} }
#[derive(Serialize, Deserialize)]
pub struct GpuSaveState<Format: From<Colour>> {
buffer: Vec<Format>,
vram: Vram,
oam: Oam,
is_bg_zero: Vec<bool>,
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<Format: From<Colour> + Clone> GpuSaveState<Format> {
pub fn create(gpu: &Gpu<Format>) -> 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<Format: From<Colour> + Clone> Gpu<Format> { impl<Format: From<Colour> + Clone> Gpu<Format> {
pub fn new( pub fn new(
window: Box<dyn Renderer<Format>>, window: Box<dyn Renderer<Format>>,

View file

@ -1,9 +1,11 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
processor::memory::Address, processor::memory::Address,
util::{as_signed, get_bit}, util::{as_signed, get_bit},
}; };
#[derive(Debug, PartialEq, Clone, Copy)] #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub(super) enum DrawMode { pub(super) enum DrawMode {
HBlank, HBlank,
VBlank, VBlank,
@ -11,7 +13,7 @@ pub(super) enum DrawMode {
Mode3, Mode3,
} }
#[derive(Debug, PartialEq, Clone, Copy)] #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub(super) enum TilemapArea { pub(super) enum TilemapArea {
T9800, T9800,
T9C00, T9C00,
@ -26,7 +28,7 @@ impl TilemapArea {
} }
} }
#[derive(Debug, PartialEq, Clone, Copy)] #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub(super) enum TiledataArea { pub(super) enum TiledataArea {
D8000, D8000,
D9000, D9000,
@ -41,7 +43,7 @@ impl TiledataArea {
} }
} }
#[derive(Debug, PartialEq, Clone, Copy)] #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub(super) enum ObjSize { pub(super) enum ObjSize {
S8x8, S8x8,
S8x16, 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) struct Lcdc {
pub(super) enable: bool, pub(super) enable: bool,
pub(super) window_tilemap: TilemapArea, 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 { pub enum Colour {
White, White,
LightGray, LightGray,
@ -138,7 +140,7 @@ impl Colour {
} }
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub(super) struct Palette { pub(super) struct Palette {
pub(super) zero: Colour, pub(super) zero: Colour,
pub(super) one: Colour, pub(super) one: Colour,
@ -193,6 +195,7 @@ pub(super) struct Object {
pub(super) oam_location: u8, pub(super) oam_location: u8,
} }
#[derive(Serialize, Deserialize, Clone, Copy)]
pub(super) struct Stat { pub(super) struct Stat {
pub(super) lyc_eq_ly_interrupt_enabled: bool, pub(super) lyc_eq_ly_interrupt_enabled: bool,
pub(super) mode_2_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 { pub struct Vram {
#[serde_as(as = "[_; 8192]")]
data: [u8; 8192], data: [u8; 8192],
} }
@ -233,7 +239,10 @@ impl Default for Vram {
} }
} }
#[serde_with::serde_as]
#[derive(Serialize, Deserialize, Clone, Copy)]
pub struct Oam { pub struct Oam {
#[serde_as(as = "[_; 160]")]
pub data: [u8; 160], pub data: [u8; 160],
} }

View file

@ -1,9 +1,12 @@
use serde::{Deserialize, Serialize};
use crate::util::{clear_bit, get_bit}; use crate::util::{clear_bit, get_bit};
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Joypad { pub struct Joypad {
sel_action: bool, sel_action: bool,
sel_direction: bool, sel_direction: bool,
#[serde(skip)]
state: JoypadState, state: JoypadState,
} }

View file

@ -1,4 +1,4 @@
mod apu; pub(crate) mod apu;
pub(crate) mod gpu; pub(crate) mod gpu;
pub(crate) mod joypad; pub(crate) mod joypad;
mod serial; mod serial;

View file

@ -1,19 +1,24 @@
use std::io::{stdout, Write}; use std::io::{stdout, Write};
use serde::{Deserialize, Serialize};
use crate::util::get_bit; use crate::util::get_bit;
#[derive(Copy, Clone, Serialize, Deserialize)]
enum ClockSource { enum ClockSource {
Internal, Internal,
External, External,
} }
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Copy, Clone, Serialize, Deserialize)]
enum ClockSpeed { enum ClockSpeed {
Normal, Normal,
Fast, Fast,
} }
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Copy, Clone, Serialize, Deserialize)]
struct SerialControl { struct SerialControl {
transfer_in_progress: bool, transfer_in_progress: bool,
clock_speed: ClockSpeed, clock_speed: ClockSpeed,
@ -30,6 +35,7 @@ impl Default for SerialControl {
} }
} }
#[derive(Copy, Clone, Serialize, Deserialize)]
pub struct Serial { pub struct Serial {
byte: u8, byte: u8,
output_byte: u8, output_byte: u8,

View file

@ -1,5 +1,8 @@
use serde::{Deserialize, Serialize};
use crate::util::{get_bit, set_or_clear_bit}; use crate::util::{get_bit, set_or_clear_bit};
#[derive(Copy, Clone, Serialize, Deserialize)]
enum TimerRate { enum TimerRate {
Sixteen, Sixteen,
SixtyFour, SixtyFour,
@ -36,6 +39,7 @@ impl TimerRate {
} }
} }
#[derive(Copy, Clone, Serialize, Deserialize)]
struct TimerControl { struct TimerControl {
enable: bool, enable: bool,
rate: TimerRate, rate: TimerRate,
@ -56,6 +60,7 @@ pub struct TimerReturn {
pub timer_interrupt: bool, pub timer_interrupt: bool,
} }
#[derive(Copy, Clone, Serialize, Deserialize)]
pub struct Timer { pub struct Timer {
// 0xFF04 // 0xFF04
div: u8, div: u8,

View file

@ -1,3 +1,5 @@
use serde::{Deserialize, Serialize};
use crate::processor::memory::Address; use crate::processor::memory::Address;
use std::{ use std::{
fs::{File, OpenOptions}, fs::{File, OpenOptions},
@ -6,13 +8,17 @@ use std::{
str::from_utf8_unchecked, 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; mod mbcs;
#[derive(Serialize, Deserialize)]
struct MaybeBufferedSram { struct MaybeBufferedSram {
buf: Vec<u8>, buf: Vec<u8>,
length: usize, length: usize,
#[serde(skip)]
inner: Option<File>, inner: Option<File>,
unbuffered_writes: usize, unbuffered_writes: usize,
} }
@ -97,6 +103,30 @@ pub struct Rom {
mbc: Box<dyn Mbc>, mbc: Box<dyn Mbc>,
} }
#[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 { impl Rom {
pub fn load(data: Vec<u8>, save_path: Option<PathBuf>) -> Self { pub fn load(data: Vec<u8>, save_path: Option<PathBuf>) -> Self {
let mut title_length = 0x143; let mut title_length = 0x143;

View file

@ -5,12 +5,14 @@ mod mbc2;
mod mbc3; mod mbc3;
mod mbc5; mod mbc5;
mod none; mod none;
pub use mbc1::Mbc1; pub use mbc1::{Mbc1, Mbc1SaveState};
pub use mbc2::Mbc2; pub use mbc2::{Mbc2, Mbc2SaveState};
pub use mbc3::Mbc3; pub use mbc3::{Mbc3, Mbc3SaveState};
pub use mbc5::Mbc5; pub use mbc5::{Mbc5, Mbc5SaveState};
pub use none::None; pub use none::None;
use super::MbcSaveState;
pub(super) const KB: usize = 1024; pub(super) const KB: usize = 1024;
const ROM_BANK_SIZE: usize = 16 * KB; const ROM_BANK_SIZE: usize = 16 * KB;
const RAM_BANK_SIZE: usize = 8 * 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(&mut self, address: Address, data: u8);
fn set_ram(&mut self, address: Address, data: u8); fn set_ram(&mut self, address: Address, data: u8);
fn mbc_type(&self) -> String; fn mbc_type(&self) -> String;
fn get_savestate(&self) -> MbcSaveState;
fn is_rumbling(&self) -> bool { fn is_rumbling(&self) -> bool {
false false
} }

View file

@ -1,9 +1,14 @@
use std::path::PathBuf; use std::path::PathBuf;
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE}; use serde::{Deserialize, Serialize};
use crate::processor::memory::{rom::MaybeBufferedSram, Address};
#[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 { enum BankingMode {
Simple, Simple,
Advanced, Advanced,
@ -20,6 +25,17 @@ pub struct Mbc1 {
bank_mode: BankingMode, bank_mode: BankingMode,
} }
#[derive(Serialize, Deserialize)]
pub struct Mbc1SaveState {
rom_len: usize,
rom_bank: u8,
ram_enabled: bool,
ram: Option<Vec<u8>>,
ram_bank: u8,
upper_banks: u8,
bank_mode: BankingMode,
}
impl Mbc1 { impl Mbc1 {
pub fn init(data: Vec<u8>, rom_size: u8, ram_size: u8, save_file: Option<PathBuf>) -> Self { pub fn init(data: Vec<u8>, rom_size: u8, ram_size: u8, save_file: Option<PathBuf>) -> Self {
let rom_len = rom_banks(rom_size) * ROM_BANK_SIZE; let rom_len = rom_banks(rom_size) * ROM_BANK_SIZE;
@ -102,6 +118,18 @@ impl Mbc for Mbc1 {
ram.flush(); 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 { impl Mbc1 {

View file

@ -1,6 +1,11 @@
use std::path::PathBuf; 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}; use super::{rom_banks, Mbc, ROM_BANK_SIZE};
@ -12,6 +17,14 @@ pub struct Mbc2 {
ram_enabled: bool, ram_enabled: bool,
} }
#[derive(Serialize, Deserialize)]
pub struct Mbc2SaveState {
rom_len: usize,
rom_bank: u8,
ram: Vec<u8>,
ram_enabled: bool,
}
impl Mbc2 { impl Mbc2 {
pub fn init(data: Vec<u8>, rom_size: u8, save_file: Option<PathBuf>) -> Self { pub fn init(data: Vec<u8>, rom_size: u8, save_file: Option<PathBuf>) -> Self {
let rom_len = rom_banks(rom_size) * ROM_BANK_SIZE; let rom_len = rom_banks(rom_size) * ROM_BANK_SIZE;
@ -75,4 +88,13 @@ impl Mbc for Mbc2 {
fn flush(&mut self) { fn flush(&mut self) {
self.ram.flush(); 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,
})
}
} }

View file

@ -1,6 +1,11 @@
use serde::{Deserialize, Serialize};
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE}; use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
use crate::{ use crate::{
processor::memory::{rom::MaybeBufferedSram, Address}, processor::memory::{
rom::{MaybeBufferedSram, MbcSaveState},
Address,
},
util::set_or_clear_bit, util::set_or_clear_bit,
}; };
use std::{ use std::{
@ -8,7 +13,7 @@ use std::{
time::{Duration, Instant}, time::{Duration, Instant},
}; };
#[derive(Debug)] #[derive(Debug, Serialize, Deserialize, Clone, Copy)]
enum RtcRegister { enum RtcRegister {
Seconds, Seconds,
Minutes, Minutes,
@ -17,6 +22,7 @@ enum RtcRegister {
Misc, Misc,
} }
#[derive(Serialize, Deserialize, Clone, Copy)]
enum RamBank { enum RamBank {
Ram(u8), Ram(u8),
Rtc(RtcRegister), Rtc(RtcRegister),
@ -117,6 +123,17 @@ pub struct Mbc3 {
ram_size: usize, ram_size: usize,
ram_enabled: bool, ram_enabled: bool,
rtc: Option<Rtc>, rtc: Option<Rtc>,
// TODO - save/load rtc!!
}
#[derive(Serialize, Deserialize)]
pub struct Mbc3SaveState {
rom_bank: u8,
rom_size: usize,
ram: Option<Vec<u8>>,
ram_bank: RamBank,
ram_size: usize,
ram_enabled: bool,
} }
impl Mbc3 { impl Mbc3 {
@ -257,4 +274,15 @@ impl Mbc for Mbc3 {
ram.flush(); 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,
})
}
} }

View file

@ -1,7 +1,12 @@
use std::path::PathBuf; use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
processor::memory::{rom::MaybeBufferedSram, Address}, processor::memory::{
rom::{MaybeBufferedSram, MbcSaveState},
Address,
},
util::get_bit, util::get_bit,
}; };
@ -19,6 +24,18 @@ pub struct Mbc5 {
is_rumbling: bool, is_rumbling: bool,
} }
#[derive(Serialize, Deserialize)]
pub struct Mbc5SaveState {
rom_bank: u16,
rom_size: usize,
ram: Option<Vec<u8>>,
ram_bank: u8,
ram_size: usize,
ram_enabled: bool,
rumble: bool,
is_rumbling: bool,
}
impl Mbc5 { impl Mbc5 {
pub fn init( pub fn init(
data: Vec<u8>, data: Vec<u8>,
@ -127,4 +144,17 @@ impl Mbc for Mbc5 {
ram.flush(); 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,
})
}
} }

View file

@ -1,5 +1,5 @@
use super::Mbc; use super::Mbc;
use crate::processor::memory::Address; use crate::processor::memory::{rom::MbcSaveState, Address};
pub struct None { pub struct None {
data: Vec<u8>, data: Vec<u8>,
@ -27,4 +27,8 @@ impl Mbc for None {
fn mbc_type(&self) -> String { fn mbc_type(&self) -> String {
String::from("None") String::from("None")
} }
fn get_savestate(&self) -> MbcSaveState {
MbcSaveState::None
}
} }

View file

@ -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; use crate::verbose_println;
mod instructions; mod instructions;
@ -27,6 +29,29 @@ pub struct Cpu<ColourFormat: From<Colour> + Clone> {
should_halt_bug: bool, should_halt_bug: bool,
} }
#[derive(Serialize, Deserialize)]
pub struct CpuSaveState<ColourFormat: From<Colour> + Clone> {
memory: MemorySaveState<ColourFormat>,
reg: Registers,
last_instruction: u8,
last_instruction_addr: u16,
halted: bool,
should_halt_bug: bool,
}
impl<ColourFormat: From<Colour> + Clone> CpuSaveState<ColourFormat> {
pub fn create(cpu: &Cpu<ColourFormat>) -> 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<ColourFormat: From<Colour> + Clone> Cpu<ColourFormat> { impl<ColourFormat: From<Colour> + Clone> Cpu<ColourFormat> {
pub fn new(mut memory: Memory<ColourFormat>, run_bootrom: bool) -> Self { pub fn new(mut memory: Memory<ColourFormat>, run_bootrom: bool) -> Self {
if !run_bootrom { if !run_bootrom {
@ -138,7 +163,7 @@ pub enum Reg8 {
L, L,
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy, Serialize, Deserialize)]
pub struct Registers { pub struct Registers {
pub af: u16, pub af: u16,
pub bc: u16, pub bc: u16,