load from save state
This commit is contained in:
parent
5db1fc4127
commit
3d5ae32960
|
@ -166,4 +166,27 @@ impl<ColourFormat: From<Colour> + Clone> EmulatorCore<ColourFormat> {
|
|||
pub fn get_save_state(&self) -> CpuSaveState<ColourFormat> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -276,6 +276,29 @@ impl<ColourFormat: From<Colour> + Clone> Memory<ColourFormat> {
|
|||
pub fn replace_output(&mut self, new: AudioOutput) {
|
||||
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> {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
let mut interrupts = GpuInterrupts::default();
|
||||
if self.lcdc.enable {
|
||||
|
|
|
@ -14,15 +14,28 @@ use self::mbcs::{
|
|||
|
||||
mod mbcs;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct MaybeBufferedSram {
|
||||
buf: Vec<u8>,
|
||||
length: usize,
|
||||
#[serde(skip)]
|
||||
inner: Option<File>,
|
||||
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;
|
||||
|
||||
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<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 {
|
||||
pub fn load(data: Vec<u8>, save_path: Option<PathBuf>) -> Self {
|
||||
let mut title_length = 0x143;
|
||||
|
@ -165,6 +200,13 @@ impl Rom {
|
|||
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 {
|
||||
&self.title
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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<Vec<u8>>,
|
||||
ram: Option<SramSaveState>,
|
||||
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<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 {
|
||||
|
@ -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,
|
||||
|
|
|
@ -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<u8>,
|
||||
ram: SramSaveState,
|
||||
ram_enabled: bool,
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,16 @@ impl Mbc2 {
|
|||
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 {
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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<Vec<u8>>,
|
||||
ram: Option<SramSaveState>,
|
||||
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<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 {
|
||||
|
@ -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,
|
||||
|
|
|
@ -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<Vec<u8>>,
|
||||
ram: Option<SramSaveState>,
|
||||
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<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 {
|
||||
|
@ -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,
|
||||
|
|
|
@ -28,7 +28,7 @@ impl Mbc for None {
|
|||
String::from("None")
|
||||
}
|
||||
|
||||
fn get_savestate(&self) -> MbcSaveState {
|
||||
fn get_save_state(&self) -> MbcSaveState {
|
||||
MbcSaveState::None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ColourFormat: From<Colour> + Clone> Cpu<ColourFormat> {
|
|||
self.reg.pc = addr;
|
||||
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)]
|
||||
|
|
Loading…
Reference in a new issue