load from save state

This commit is contained in:
Alex Janka 2023-03-11 09:31:15 +11:00
parent 5db1fc4127
commit 3d5ae32960
12 changed files with 231 additions and 22 deletions

View file

@ -166,4 +166,27 @@ impl<ColourFormat: From<Colour> + Clone> EmulatorCore<ColourFormat> {
pub fn get_save_state(&self) -> CpuSaveState<ColourFormat> { pub fn get_save_state(&self) -> CpuSaveState<ColourFormat> {
CpuSaveState::create(&self.cpu) CpuSaveState::create(&self.cpu)
} }
pub fn from_save_state(
state: CpuSaveState<ColourFormat>,
rom: RomFile,
receiver: Receiver<EmulatorMessage>,
window: Box<dyn Renderer<ColourFormat>>,
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),
}
}
} }

View file

@ -276,6 +276,29 @@ impl<ColourFormat: From<Colour> + Clone> Memory<ColourFormat> {
pub fn replace_output(&mut self, new: AudioOutput) { pub fn replace_output(&mut self, new: AudioOutput) {
self.apu.replace_output(new); self.apu.replace_output(new);
} }
pub fn from_save_state(
state: MemorySaveState<ColourFormat>,
data: Vec<u8>,
window: Box<dyn Renderer<ColourFormat>>,
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<ColourFormat: From<Colour> + Clone> Cpu<ColourFormat> { impl<ColourFormat: From<Colour> + Clone> Cpu<ColourFormat> {

View file

@ -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) { pub fn replace_output(&mut self, new: AudioOutput) {
self.converter = Downsampler::new(new.sample_rate, new.downsample_type); self.converter = Downsampler::new(new.sample_rate, new.downsample_type);
self.output = new; self.output = new;

View file

@ -132,6 +132,43 @@ impl<Format: From<Colour> + Clone> Gpu<Format> {
} }
} }
pub fn from_save_state(
state: GpuSaveState<Format>,
window: Box<dyn Renderer<Format>>,
tile_window_renderer: Option<Box<dyn Renderer<Format>>>,
) -> 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 { pub fn tick(&mut self, steps: usize) -> GpuInterrupts {
let mut interrupts = GpuInterrupts::default(); let mut interrupts = GpuInterrupts::default();
if self.lcdc.enable { if self.lcdc.enable {

View file

@ -14,15 +14,28 @@ use self::mbcs::{
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,
} }
#[derive(Serialize, Deserialize)]
struct SramSaveState {
buf: Vec<u8>,
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; const NUM_WRITES_TO_FLUSH: usize = 256;
impl MaybeBufferedSram { 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 { fn len(&self) -> usize {
self.length self.length
} }
@ -112,7 +135,7 @@ impl RomSaveState {
pub fn create(rom: &Rom) -> Self { pub fn create(rom: &Rom) -> Self {
Self { Self {
title: rom.title.clone(), title: rom.title.clone(),
mbc: rom.mbc.get_savestate(), mbc: rom.mbc.get_save_state(),
} }
} }
} }
@ -126,6 +149,18 @@ enum MbcSaveState {
None, None,
} }
impl MbcSaveState {
fn get_mbc(self, data: Vec<u8>) -> Box<dyn Mbc> {
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 { 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;
@ -165,6 +200,13 @@ impl Rom {
Self { title, mbc } Self { title, mbc }
} }
pub fn from_save_state(state: RomSaveState, data: Vec<u8>) -> Self {
Self {
title: state.title,
mbc: state.mbc.get_mbc(data),
}
}
pub fn get_title(&self) -> &String { pub fn get_title(&self) -> &String {
&self.title &self.title
} }

View file

@ -25,7 +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 get_save_state(&self) -> MbcSaveState;
fn is_rumbling(&self) -> bool { fn is_rumbling(&self) -> bool {
false false
} }

View file

@ -4,7 +4,7 @@ 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::processor::memory::{ use crate::processor::memory::{
rom::{MaybeBufferedSram, MbcSaveState}, rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
Address, Address,
}; };
@ -30,7 +30,7 @@ pub struct Mbc1SaveState {
rom_len: usize, rom_len: usize,
rom_bank: u8, rom_bank: u8,
ram_enabled: bool, ram_enabled: bool,
ram: Option<Vec<u8>>, ram: Option<SramSaveState>,
ram_bank: u8, ram_bank: u8,
upper_banks: u8, upper_banks: u8,
bank_mode: BankingMode, bank_mode: BankingMode,
@ -88,6 +88,19 @@ impl Mbc1 {
_ => panic!("address {address} incompatible with MBC1"), _ => panic!("address {address} incompatible with MBC1"),
} }
} }
pub fn from_save_state(state: Mbc1SaveState, data: Vec<u8>) -> 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 { 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 { MbcSaveState::Mbc1(Mbc1SaveState {
rom_len: self.rom_len, rom_len: self.rom_len,
rom_bank: self.rom_bank, rom_bank: self.rom_bank,
ram_enabled: self.ram_enabled, 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, ram_bank: self.ram_bank,
upper_banks: self.upper_banks, upper_banks: self.upper_banks,
bank_mode: self.bank_mode, bank_mode: self.bank_mode,

View file

@ -3,7 +3,7 @@ use std::path::PathBuf;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::processor::memory::{ use crate::processor::memory::{
rom::{MaybeBufferedSram, MbcSaveState}, rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
Address, Address,
}; };
@ -21,7 +21,7 @@ pub struct Mbc2 {
pub struct Mbc2SaveState { pub struct Mbc2SaveState {
rom_len: usize, rom_len: usize,
rom_bank: u8, rom_bank: u8,
ram: Vec<u8>, ram: SramSaveState,
ram_enabled: bool, ram_enabled: bool,
} }
@ -38,6 +38,16 @@ impl Mbc2 {
ram_enabled: false, ram_enabled: false,
} }
} }
pub fn from_save_state(state: Mbc2SaveState, data: Vec<u8>) -> 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 { impl Mbc for Mbc2 {
@ -89,11 +99,11 @@ impl Mbc for Mbc2 {
self.ram.flush(); self.ram.flush();
} }
fn get_savestate(&self) -> MbcSaveState { fn get_save_state(&self) -> MbcSaveState {
MbcSaveState::Mbc2(Mbc2SaveState { MbcSaveState::Mbc2(Mbc2SaveState {
rom_len: self.rom_len, rom_len: self.rom_len,
rom_bank: self.rom_bank, rom_bank: self.rom_bank,
ram: self.ram.buf.clone(), ram: SramSaveState::create(&self.ram),
ram_enabled: self.ram_enabled, ram_enabled: self.ram_enabled,
}) })
} }

View file

@ -3,7 +3,7 @@ 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::{ processor::memory::{
rom::{MaybeBufferedSram, MbcSaveState}, rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
Address, Address,
}, },
util::set_or_clear_bit, util::set_or_clear_bit,
@ -100,7 +100,7 @@ pub struct Mbc3 {
pub struct Mbc3SaveState { pub struct Mbc3SaveState {
rom_bank: u8, rom_bank: u8,
rom_size: usize, rom_size: usize,
ram: Option<Vec<u8>>, ram: Option<SramSaveState>,
ram_bank: RamBank, ram_bank: RamBank,
ram_size: usize, ram_size: usize,
ram_enabled: bool, ram_enabled: bool,
@ -141,6 +141,20 @@ impl Mbc3 {
fn get_ram_addr(&self, address: Address, ram_bank: usize) -> usize { fn get_ram_addr(&self, address: Address, ram_bank: usize) -> usize {
((address as usize - 0xA000) + (RAM_BANK_SIZE * ram_bank)) % self.ram_size ((address as usize - 0xA000) + (RAM_BANK_SIZE * ram_bank)) % self.ram_size
} }
pub fn from_save_state(state: Mbc3SaveState, data: Vec<u8>) -> 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 { 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 { MbcSaveState::Mbc3(Mbc3SaveState {
rom_bank: self.rom_bank, rom_bank: self.rom_bank,
rom_size: self.rom_size, 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_bank: self.ram_bank,
ram_size: self.ram_size, ram_size: self.ram_size,
ram_enabled: self.ram_enabled, ram_enabled: self.ram_enabled,

View file

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
processor::memory::{ processor::memory::{
rom::{MaybeBufferedSram, MbcSaveState}, rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
Address, Address,
}, },
util::get_bit, util::get_bit,
@ -28,7 +28,7 @@ pub struct Mbc5 {
pub struct Mbc5SaveState { pub struct Mbc5SaveState {
rom_bank: u16, rom_bank: u16,
rom_size: usize, rom_size: usize,
ram: Option<Vec<u8>>, ram: Option<SramSaveState>,
ram_bank: u8, ram_bank: u8,
ram_size: usize, ram_size: usize,
ram_enabled: bool, ram_enabled: bool,
@ -72,6 +72,20 @@ impl Mbc5 {
fn get_ram_addr(&self, address: Address) -> usize { fn get_ram_addr(&self, address: Address) -> usize {
((address as usize - 0xA000) + (RAM_BANK_SIZE * self.ram_bank as usize)) % self.ram_size ((address as usize - 0xA000) + (RAM_BANK_SIZE * self.ram_bank as usize)) % self.ram_size
} }
pub fn from_save_state(state: Mbc5SaveState, data: Vec<u8>) -> 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 { 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 { MbcSaveState::Mbc5(Mbc5SaveState {
rom_bank: self.rom_bank, rom_bank: self.rom_bank,
rom_size: self.rom_size, 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_bank: self.ram_bank,
ram_size: self.ram_size, ram_size: self.ram_size,
ram_enabled: self.ram_enabled, ram_enabled: self.ram_enabled,

View file

@ -28,7 +28,7 @@ impl Mbc for None {
String::from("None") String::from("None")
} }
fn get_savestate(&self) -> MbcSaveState { fn get_save_state(&self) -> MbcSaveState {
MbcSaveState::None MbcSaveState::None
} }
} }

View file

@ -1,7 +1,10 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use self::memory::{mmio::gpu::Colour, Interrupt, Memory, MemorySaveState}; use self::memory::{mmio::gpu::Colour, Interrupt, Memory, MemorySaveState};
use crate::verbose_println; use crate::{
connect::{AudioOutput, Renderer},
verbose_println,
};
mod instructions; mod instructions;
pub mod memory; pub mod memory;
@ -150,6 +153,22 @@ impl<ColourFormat: From<Colour> + Clone> Cpu<ColourFormat> {
self.reg.pc = addr; self.reg.pc = addr;
self.memory.ime = false; self.memory.ime = false;
} }
pub fn from_save_state(
state: CpuSaveState<ColourFormat>,
data: Vec<u8>,
window: Box<dyn Renderer<ColourFormat>>,
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)] #[derive(Clone, Copy)]