diff --git a/lib/src/lib.rs b/lib/src/lib.rs index c93e6d4..c87f729 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -166,4 +166,27 @@ impl + Clone> EmulatorCore { pub fn get_save_state(&self) -> CpuSaveState { CpuSaveState::create(&self.cpu) } + + pub fn from_save_state( + state: CpuSaveState, + rom: RomFile, + receiver: Receiver, + window: Box>, + output: AudioOutput, + ) -> Self { + let data = match rom { + RomFile::Path(path) => match fs::read(path) { + Ok(data) => data, + Err(e) => { + println!("Error reading ROM: {e}"); + exit(1); + } + }, + RomFile::Raw(data) => data, + }; + Self { + receiver, + cpu: Cpu::from_save_state(state, data, window, output), + } + } } diff --git a/lib/src/processor/memory.rs b/lib/src/processor/memory.rs index 22998a7..4726633 100644 --- a/lib/src/processor/memory.rs +++ b/lib/src/processor/memory.rs @@ -276,6 +276,29 @@ impl + Clone> Memory { pub fn replace_output(&mut self, new: AudioOutput) { self.apu.replace_output(new); } + + pub fn from_save_state( + state: MemorySaveState, + data: Vec, + window: Box>, + output: AudioOutput, + ) -> Self { + Self { + bootrom: None, + rom: Rom::from_save_state(state.rom, data), + ram: state.ram, + cpu_ram: state.cpu_ram, + interrupts: state.interrupts, + ime: state.ime, + ime_scheduled: state.ime_scheduled, + dma_addr: state.dma_addr, + joypad: state.joypad, + gpu: Gpu::from_save_state(state.gpu, window, None), + apu: Apu::from_save_state(state.apu, output), + serial: state.serial, + timers: state.timers, + } + } } impl + Clone> Cpu { diff --git a/lib/src/processor/memory/mmio/apu.rs b/lib/src/processor/memory/mmio/apu.rs index 4e6294c..35aee38 100644 --- a/lib/src/processor/memory/mmio/apu.rs +++ b/lib/src/processor/memory/mmio/apu.rs @@ -90,6 +90,20 @@ impl Apu { } } + pub fn from_save_state(state: ApuSaveState, output: AudioOutput) -> Self { + Self { + apu_enable: state.apu_enable, + channels: state.channels, + vin: state.vin, + mixer: state.mixer, + div_apu: state.div_apu, + buffer: state.buffer, + out_buffer: state.out_buffer, + converter: Downsampler::new(output.sample_rate, output.downsample_type), + output, + } + } + pub fn replace_output(&mut self, new: AudioOutput) { self.converter = Downsampler::new(new.sample_rate, new.downsample_type); self.output = new; diff --git a/lib/src/processor/memory/mmio/gpu.rs b/lib/src/processor/memory/mmio/gpu.rs index 3ae431d..13d3359 100644 --- a/lib/src/processor/memory/mmio/gpu.rs +++ b/lib/src/processor/memory/mmio/gpu.rs @@ -132,6 +132,43 @@ impl + Clone> Gpu { } } + pub fn from_save_state( + state: GpuSaveState, + window: Box>, + tile_window_renderer: Option>>, + ) -> Self { + let tile_window = if let Some(mut tile_window_renderer) = tile_window_renderer { + tile_window_renderer.prepare(TILE_WINDOW_WIDTH, TILE_WINDOW_HEIGHT); + Some(TileWindow::new(tile_window_renderer)) + } else { + None + }; + + Self { + buffer: state.buffer, + vram: state.vram, + oam: state.oam, + window, + is_bg_zero: state.is_bg_zero, + lcdc: state.lcdc, + stat: state.stat, + mode_clock: state.mode_clock, + scanline: state.scanline, + lyc: state.lyc, + tile_window, + window_lc: state.window_lc, + has_window_been_enabled: state.has_window_been_enabled, + bg_palette: state.bg_palette, + obj_palette_0: state.obj_palette_0, + obj_palette_1: state.obj_palette_1, + scx: state.scx, + scy: state.scy, + wx: state.wx, + wy: state.wy, + prev_stat: state.prev_stat, + } + } + pub fn tick(&mut self, steps: usize) -> GpuInterrupts { let mut interrupts = GpuInterrupts::default(); if self.lcdc.enable { diff --git a/lib/src/processor/memory/rom.rs b/lib/src/processor/memory/rom.rs index d494acf..503e1ae 100644 --- a/lib/src/processor/memory/rom.rs +++ b/lib/src/processor/memory/rom.rs @@ -14,15 +14,28 @@ use self::mbcs::{ mod mbcs; -#[derive(Serialize, Deserialize)] struct MaybeBufferedSram { buf: Vec, length: usize, - #[serde(skip)] inner: Option, unbuffered_writes: usize, } +#[derive(Serialize, Deserialize)] +struct SramSaveState { + buf: Vec, + length: usize, +} + +impl SramSaveState { + pub fn create(sram: &MaybeBufferedSram) -> Self { + Self { + buf: sram.buf.clone(), + length: sram.length, + } + } +} + const NUM_WRITES_TO_FLUSH: usize = 256; impl MaybeBufferedSram { @@ -58,6 +71,16 @@ impl MaybeBufferedSram { } } + fn from_save_state(state: SramSaveState) -> Self { + // TODO - restore file path + Self { + buf: state.buf, + length: state.length, + inner: None, + unbuffered_writes: 0, + } + } + fn len(&self) -> usize { self.length } @@ -112,7 +135,7 @@ impl RomSaveState { pub fn create(rom: &Rom) -> Self { Self { title: rom.title.clone(), - mbc: rom.mbc.get_savestate(), + mbc: rom.mbc.get_save_state(), } } } @@ -126,6 +149,18 @@ enum MbcSaveState { None, } +impl MbcSaveState { + fn get_mbc(self, data: Vec) -> Box { + match self { + MbcSaveState::Mbc1(state) => Box::new(Mbc1::from_save_state(state, data)), + MbcSaveState::Mbc2(state) => Box::new(Mbc2::from_save_state(state, data)), + MbcSaveState::Mbc3(state) => Box::new(Mbc3::from_save_state(state, data)), + MbcSaveState::Mbc5(state) => Box::new(Mbc5::from_save_state(state, data)), + MbcSaveState::None => Box::new(None::init(data)), + } + } +} + impl Rom { pub fn load(data: Vec, save_path: Option) -> Self { let mut title_length = 0x143; @@ -165,6 +200,13 @@ impl Rom { Self { title, mbc } } + pub fn from_save_state(state: RomSaveState, data: Vec) -> Self { + Self { + title: state.title, + mbc: state.mbc.get_mbc(data), + } + } + pub fn get_title(&self) -> &String { &self.title } diff --git a/lib/src/processor/memory/rom/mbcs.rs b/lib/src/processor/memory/rom/mbcs.rs index 8cfa501..3ab5c67 100644 --- a/lib/src/processor/memory/rom/mbcs.rs +++ b/lib/src/processor/memory/rom/mbcs.rs @@ -25,7 +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 get_save_state(&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 010cce7..7677105 100644 --- a/lib/src/processor/memory/rom/mbcs/mbc1.rs +++ b/lib/src/processor/memory/rom/mbcs/mbc1.rs @@ -4,7 +4,7 @@ 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, MbcSaveState}, + rom::{MaybeBufferedSram, MbcSaveState, SramSaveState}, Address, }; @@ -30,7 +30,7 @@ pub struct Mbc1SaveState { rom_len: usize, rom_bank: u8, ram_enabled: bool, - ram: Option>, + ram: Option, ram_bank: u8, upper_banks: u8, bank_mode: BankingMode, @@ -88,6 +88,19 @@ impl Mbc1 { _ => panic!("address {address} incompatible with MBC1"), } } + + pub fn from_save_state(state: Mbc1SaveState, data: Vec) -> Self { + Self { + data, + rom_len: state.rom_len, + rom_bank: state.rom_bank, + ram_enabled: state.ram_enabled, + ram: state.ram.map(MaybeBufferedSram::from_save_state), + ram_bank: state.ram_bank, + upper_banks: state.upper_banks, + bank_mode: state.bank_mode, + } + } } impl Mbc for Mbc1 { @@ -155,12 +168,12 @@ impl Mbc for Mbc1 { } } - fn get_savestate(&self) -> MbcSaveState { + fn get_save_state(&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: self.ram.as_ref().map(SramSaveState::create), ram_bank: self.ram_bank, upper_banks: self.upper_banks, bank_mode: self.bank_mode, diff --git a/lib/src/processor/memory/rom/mbcs/mbc2.rs b/lib/src/processor/memory/rom/mbcs/mbc2.rs index d0fcb5f..84279c6 100644 --- a/lib/src/processor/memory/rom/mbcs/mbc2.rs +++ b/lib/src/processor/memory/rom/mbcs/mbc2.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use serde::{Deserialize, Serialize}; use crate::processor::memory::{ - rom::{MaybeBufferedSram, MbcSaveState}, + rom::{MaybeBufferedSram, MbcSaveState, SramSaveState}, Address, }; @@ -21,7 +21,7 @@ pub struct Mbc2 { pub struct Mbc2SaveState { rom_len: usize, rom_bank: u8, - ram: Vec, + ram: SramSaveState, ram_enabled: bool, } @@ -38,6 +38,16 @@ impl Mbc2 { ram_enabled: false, } } + + pub fn from_save_state(state: Mbc2SaveState, data: Vec) -> Self { + Self { + data, + rom_len: state.rom_len, + rom_bank: state.rom_bank, + ram: MaybeBufferedSram::from_save_state(state.ram), + ram_enabled: state.ram_enabled, + } + } } impl Mbc for Mbc2 { @@ -89,11 +99,11 @@ impl Mbc for Mbc2 { self.ram.flush(); } - fn get_savestate(&self) -> MbcSaveState { + fn get_save_state(&self) -> MbcSaveState { MbcSaveState::Mbc2(Mbc2SaveState { rom_len: self.rom_len, rom_bank: self.rom_bank, - ram: self.ram.buf.clone(), + ram: SramSaveState::create(&self.ram), 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 e5ecbc6..613c85a 100644 --- a/lib/src/processor/memory/rom/mbcs/mbc3.rs +++ b/lib/src/processor/memory/rom/mbcs/mbc3.rs @@ -3,7 +3,7 @@ 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, MbcSaveState}, + rom::{MaybeBufferedSram, MbcSaveState, SramSaveState}, Address, }, util::set_or_clear_bit, @@ -100,7 +100,7 @@ pub struct Mbc3 { pub struct Mbc3SaveState { rom_bank: u8, rom_size: usize, - ram: Option>, + ram: Option, ram_bank: RamBank, ram_size: usize, ram_enabled: bool, @@ -141,6 +141,20 @@ impl Mbc3 { fn get_ram_addr(&self, address: Address, ram_bank: usize) -> usize { ((address as usize - 0xA000) + (RAM_BANK_SIZE * ram_bank)) % self.ram_size } + + pub fn from_save_state(state: Mbc3SaveState, data: Vec) -> Self { + // TODO - FIX RTC!!! + Self { + data, + rom_bank: state.rom_bank, + rom_size: state.rom_size, + ram: state.ram.map(MaybeBufferedSram::from_save_state), + ram_bank: state.ram_bank, + ram_size: state.ram_size, + ram_enabled: state.ram_enabled, + rtc: None, + } + } } impl Mbc for Mbc3 { @@ -245,11 +259,11 @@ impl Mbc for Mbc3 { } } - fn get_savestate(&self) -> MbcSaveState { + fn get_save_state(&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: self.ram.as_ref().map(SramSaveState::create), 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 fdb20b8..55a0b32 100644 --- a/lib/src/processor/memory/rom/mbcs/mbc5.rs +++ b/lib/src/processor/memory/rom/mbcs/mbc5.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::{ processor::memory::{ - rom::{MaybeBufferedSram, MbcSaveState}, + rom::{MaybeBufferedSram, MbcSaveState, SramSaveState}, Address, }, util::get_bit, @@ -28,7 +28,7 @@ pub struct Mbc5 { pub struct Mbc5SaveState { rom_bank: u16, rom_size: usize, - ram: Option>, + ram: Option, ram_bank: u8, ram_size: usize, ram_enabled: bool, @@ -72,6 +72,20 @@ impl Mbc5 { fn get_ram_addr(&self, address: Address) -> usize { ((address as usize - 0xA000) + (RAM_BANK_SIZE * self.ram_bank as usize)) % self.ram_size } + + pub fn from_save_state(state: Mbc5SaveState, data: Vec) -> Self { + Self { + data, + rom_bank: state.rom_bank, + rom_size: state.rom_size, + ram: state.ram.map(MaybeBufferedSram::from_save_state), + ram_bank: state.ram_bank, + ram_size: state.ram_size, + ram_enabled: state.ram_enabled, + rumble: state.rumble, + is_rumbling: state.is_rumbling, + } + } } impl Mbc for Mbc5 { @@ -145,11 +159,11 @@ impl Mbc for Mbc5 { } } - fn get_savestate(&self) -> MbcSaveState { + fn get_save_state(&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: self.ram.as_ref().map(SramSaveState::create), ram_bank: self.ram_bank, ram_size: self.ram_size, ram_enabled: self.ram_enabled, diff --git a/lib/src/processor/memory/rom/mbcs/none.rs b/lib/src/processor/memory/rom/mbcs/none.rs index 1198047..ba7ca79 100644 --- a/lib/src/processor/memory/rom/mbcs/none.rs +++ b/lib/src/processor/memory/rom/mbcs/none.rs @@ -28,7 +28,7 @@ impl Mbc for None { String::from("None") } - fn get_savestate(&self) -> MbcSaveState { + fn get_save_state(&self) -> MbcSaveState { MbcSaveState::None } } diff --git a/lib/src/processor/mod.rs b/lib/src/processor/mod.rs index 58c7f04..583071a 100644 --- a/lib/src/processor/mod.rs +++ b/lib/src/processor/mod.rs @@ -1,7 +1,10 @@ use serde::{Deserialize, Serialize}; use self::memory::{mmio::gpu::Colour, Interrupt, Memory, MemorySaveState}; -use crate::verbose_println; +use crate::{ + connect::{AudioOutput, Renderer}, + verbose_println, +}; mod instructions; pub mod memory; @@ -150,6 +153,22 @@ impl + Clone> Cpu { self.reg.pc = addr; self.memory.ime = false; } + + pub fn from_save_state( + state: CpuSaveState, + data: Vec, + window: Box>, + output: AudioOutput, + ) -> Self { + Self { + memory: Memory::from_save_state(state.memory, data, window, output), + reg: state.reg, + last_instruction: state.last_instruction, + last_instruction_addr: state.last_instruction_addr, + halted: state.halted, + should_halt_bug: state.should_halt_bug, + } + } } #[derive(Clone, Copy)]