some save work
This commit is contained in:
parent
85f938a45b
commit
bf4b8d4caa
10 changed files with 362 additions and 177 deletions
|
@ -7,18 +7,16 @@ use gb_emu_lib::{
|
|||
},
|
||||
EmulatorCore,
|
||||
};
|
||||
use nih_plug::midi::MidiResult::Basic;
|
||||
use nih_plug::prelude::*;
|
||||
use nih_plug::{midi::MidiResult::Basic, params::persist::PersistentField};
|
||||
use std::sync::{
|
||||
mpsc::{self, channel, Receiver, Sender},
|
||||
Arc, Mutex,
|
||||
Arc, Mutex, RwLock,
|
||||
};
|
||||
use ui::{Emulator, EmulatorRenderer};
|
||||
|
||||
#[cfg(feature = "savestate")]
|
||||
use gb_emu_lib::connect::CpuSaveState;
|
||||
#[cfg(feature = "savestate")]
|
||||
use nih_plug::params::persist::PersistentField;
|
||||
|
||||
mod ui;
|
||||
|
||||
|
@ -42,11 +40,31 @@ impl PersistentField<'_, Option<CpuSaveState<[u8; 4]>>> for SaveStateParam {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SramParam {
|
||||
state: Arc<RwLock<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl PersistentField<'_, Vec<u8>> for SramParam {
|
||||
fn set(&self, new_value: Vec<u8>) {
|
||||
*self.state.write().unwrap() = new_value;
|
||||
}
|
||||
|
||||
fn map<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: Fn(&Vec<u8>) -> R,
|
||||
{
|
||||
f(&self.state.read().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Params, Default)]
|
||||
struct EmuParams {
|
||||
#[cfg(feature = "savestate")]
|
||||
#[persist = "save_state"]
|
||||
last_save_state: SaveStateParam,
|
||||
#[persist = "sram"]
|
||||
sram_save: SramParam,
|
||||
// #[cfg(feature = "savestate")]
|
||||
// #[persist = "save_state"]
|
||||
// last_save_state: SaveStateParam,
|
||||
}
|
||||
|
||||
struct EmuVars {
|
||||
|
@ -70,7 +88,7 @@ type JoypadSender = Mutex<Option<Sender<(JoypadButtons, bool)>>>;
|
|||
const BUFFERS_PER_FRAME: usize = 1;
|
||||
const DOWNSAMPLE_TYPE: DownsampleType = DownsampleType::Linear;
|
||||
|
||||
const ROM: &[u8; 65536] = include_bytes!("../../test-roms/mGB.gb");
|
||||
const ROM: &[u8; 65536] = include_bytes!("../../test-roms/mGB-save-patch.gb");
|
||||
|
||||
impl Plugin for GameboyEmu {
|
||||
const NAME: &'static str = "Gameboy";
|
||||
|
@ -262,7 +280,7 @@ impl Plugin for GameboyEmu {
|
|||
let mut emulator_core = {
|
||||
let options = EmulatorOptions::new(window, rom, output)
|
||||
.with_serial_target(serial_target)
|
||||
.force_no_save();
|
||||
.with_sram_buffer(self.params.sram_save.state.clone());
|
||||
|
||||
EmulatorCore::init(receiver, options, NoCamera::default())
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use std::marker::PhantomData;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
pub use crate::processor::memory::mmio::gpu::Colour;
|
||||
pub use crate::processor::memory::mmio::joypad::{JoypadButtons, JoypadState};
|
||||
|
@ -151,6 +151,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SramType {
|
||||
File(String),
|
||||
RawBuffer(Arc<RwLock<Vec<u8>>>),
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct EmulatorOptions<ColourFormat, R>
|
||||
where
|
||||
|
@ -161,7 +167,7 @@ where
|
|||
pub(crate) tile_window: Option<R>,
|
||||
pub(crate) rom: RomFile,
|
||||
pub(crate) output: AudioOutput,
|
||||
pub(crate) save_path: Option<String>,
|
||||
pub(crate) save: Option<SramType>,
|
||||
|
||||
pub(crate) no_save: bool,
|
||||
pub(crate) bootrom: Option<RomFile>,
|
||||
|
@ -182,7 +188,7 @@ where
|
|||
tile_window: None,
|
||||
rom,
|
||||
output,
|
||||
save_path: None,
|
||||
save: None,
|
||||
no_save: false,
|
||||
bootrom: None,
|
||||
show_bootrom: false,
|
||||
|
@ -193,7 +199,12 @@ where
|
|||
}
|
||||
|
||||
pub fn with_save_path(mut self, path: Option<String>) -> Self {
|
||||
self.save_path = path;
|
||||
self.save = path.map(SramType::File);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_sram_buffer(mut self, buffer: Arc<RwLock<Vec<u8>>>) -> Self {
|
||||
self.save = Some(SramType::RawBuffer(buffer));
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,14 @@ use crate::{
|
|||
};
|
||||
use connect::{
|
||||
AudioOutput, CameraWrapper, CameraWrapperRef, EmulatorCoreTrait, EmulatorMessage,
|
||||
EmulatorOptions, NoCamera, PocketCamera, Renderer, RomFile, SerialTarget,
|
||||
EmulatorOptions, NoCamera, PocketCamera, Renderer, RomFile, SerialTarget, SramType,
|
||||
};
|
||||
use processor::{
|
||||
memory::{mmio::gpu::Colour, rom::CgbRomType, OutputTargets, Rom},
|
||||
memory::{
|
||||
mmio::gpu::Colour,
|
||||
rom::{sram_save::SaveDataLocation, CgbRomType},
|
||||
OutputTargets, Rom,
|
||||
},
|
||||
Cpu, CpuSaveState,
|
||||
};
|
||||
use std::{
|
||||
|
@ -54,16 +58,21 @@ where
|
|||
) -> Self {
|
||||
let camera: CameraWrapperRef<C> = Arc::new(Mutex::new(CameraWrapper::new(camera)));
|
||||
|
||||
let maybe_save = options.save.map(|i| match i {
|
||||
SramType::File(sram_path) => {
|
||||
SaveDataLocation::File(PathBuf::from_str(&sram_path).unwrap())
|
||||
}
|
||||
SramType::RawBuffer(buffer) => SaveDataLocation::Raw(buffer),
|
||||
});
|
||||
|
||||
let rom = match options.rom {
|
||||
RomFile::Path(path) => {
|
||||
let maybe_save = if options.no_save {
|
||||
None
|
||||
} else {
|
||||
Some(if let Some(path) = options.save_path {
|
||||
PathBuf::from_str(&path).unwrap()
|
||||
} else {
|
||||
PathBuf::from_str(&path).unwrap().with_extension("sav")
|
||||
})
|
||||
Some(maybe_save.unwrap_or(SaveDataLocation::File(
|
||||
PathBuf::from_str(&path).unwrap().with_extension("sav"),
|
||||
)))
|
||||
};
|
||||
|
||||
match fs::read(path) {
|
||||
|
@ -74,7 +83,7 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
RomFile::Raw(data) => Rom::load(data, None, camera.clone()),
|
||||
RomFile::Raw(data) => Rom::load(data, maybe_save, camera.clone()),
|
||||
};
|
||||
|
||||
let (bootrom, cgb) = if let Some(b) = options.bootrom {
|
||||
|
|
|
@ -1,127 +1,18 @@
|
|||
use self::{
|
||||
mbcs::{
|
||||
Mbc, Mbc1, Mbc1SaveState, Mbc2, Mbc2SaveState, Mbc3, Mbc3SaveState, Mbc5, Mbc5SaveState,
|
||||
None, PocketCamera, PocketCameraSaveState,
|
||||
},
|
||||
sram_save::SaveDataLocation,
|
||||
};
|
||||
use crate::connect::{CameraWrapperRef, PocketCamera as PocketCameraTrait};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fs::{File, OpenOptions},
|
||||
io::{Read, Seek, SeekFrom, Write},
|
||||
marker::PhantomData,
|
||||
path::PathBuf,
|
||||
str::from_utf8,
|
||||
};
|
||||
|
||||
use self::mbcs::{
|
||||
Mbc, Mbc1, Mbc1SaveState, Mbc2, Mbc2SaveState, Mbc3, Mbc3SaveState, Mbc5, Mbc5SaveState, None,
|
||||
PocketCamera, PocketCameraSaveState,
|
||||
};
|
||||
use std::{marker::PhantomData, str::from_utf8};
|
||||
|
||||
use super::addresses::{CartRamAddress, RomAddress};
|
||||
|
||||
mod mbcs;
|
||||
|
||||
struct MaybeBufferedSram {
|
||||
buf: Vec<u8>,
|
||||
length: usize,
|
||||
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 {
|
||||
fn new(path: Option<PathBuf>, length: usize) -> Self {
|
||||
let mut buf = Vec::new();
|
||||
let inner = if let Some(path) = path {
|
||||
if path.exists() {
|
||||
let mut writer = OpenOptions::new()
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open(path)
|
||||
.unwrap();
|
||||
writer.read_to_end(&mut buf).unwrap();
|
||||
Some(writer)
|
||||
} else {
|
||||
buf.resize(8 * mbcs::KB, 0);
|
||||
let writer = OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(path)
|
||||
.unwrap();
|
||||
Some(writer)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self {
|
||||
buf,
|
||||
length,
|
||||
inner,
|
||||
unbuffered_writes: 0,
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fn get(&self, addr: usize) -> u8 {
|
||||
if addr >= self.buf.len() {
|
||||
0
|
||||
} else {
|
||||
self.buf[addr]
|
||||
}
|
||||
}
|
||||
|
||||
fn set(&mut self, addr: usize, data: u8) {
|
||||
self.unbuffered_writes += 1;
|
||||
while addr >= self.buf.len() {
|
||||
self.buf.resize(self.buf.len() + (8 * mbcs::KB), 0);
|
||||
}
|
||||
self.buf[addr] = data;
|
||||
if self.unbuffered_writes >= NUM_WRITES_TO_FLUSH {
|
||||
self.flush();
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) {
|
||||
if let Some(ref mut writer) = self.inner {
|
||||
writer.seek(SeekFrom::Start(0)).unwrap();
|
||||
writer.set_len(self.buf.len() as u64).unwrap();
|
||||
writer.write_all(&self.buf).unwrap();
|
||||
self.unbuffered_writes = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MaybeBufferedSram {
|
||||
fn drop(&mut self) {
|
||||
self.flush();
|
||||
}
|
||||
}
|
||||
pub mod sram_save;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
pub(crate) enum CgbRomType {
|
||||
|
@ -190,7 +81,7 @@ where
|
|||
{
|
||||
pub(crate) fn load(
|
||||
data: Vec<u8>,
|
||||
save_path: Option<PathBuf>,
|
||||
sram_location: Option<SaveDataLocation>,
|
||||
camera: CameraWrapperRef<C>,
|
||||
) -> Self {
|
||||
let mut title_length = 0x143;
|
||||
|
@ -213,22 +104,26 @@ where
|
|||
0x00 => Box::new(None::init(data)),
|
||||
0x01 => Box::new(Mbc1::init(data, rom_size, 0, None)),
|
||||
0x02 => Box::new(Mbc1::init(data, rom_size, ram_size, None)),
|
||||
0x03 => Box::new(Mbc1::init(data, rom_size, ram_size, save_path)),
|
||||
0x03 => Box::new(Mbc1::init(data, rom_size, ram_size, sram_location)),
|
||||
0x05 => Box::new(Mbc2::init(data, rom_size, None)),
|
||||
0x06 => Box::new(Mbc2::init(data, rom_size, save_path)),
|
||||
0x0F => Box::new(Mbc3::init(data, rom_size, 0, true, save_path)),
|
||||
0x10 => Box::new(Mbc3::init(data, rom_size, ram_size, true, save_path)),
|
||||
0x06 => Box::new(Mbc2::init(data, rom_size, sram_location)),
|
||||
0x0F => Box::new(Mbc3::init(data, rom_size, 0, true, sram_location)),
|
||||
0x10 => Box::new(Mbc3::init(data, rom_size, ram_size, true, sram_location)),
|
||||
0x11 => Box::new(Mbc3::init(data, rom_size, 0, false, None)),
|
||||
0x12 => Box::new(Mbc3::init(data, rom_size, ram_size, false, None)),
|
||||
0x13 => Box::new(Mbc3::init(data, rom_size, ram_size, false, save_path)),
|
||||
0x13 => Box::new(Mbc3::init(data, rom_size, ram_size, false, sram_location)),
|
||||
0x19 => Box::new(Mbc5::init(data, rom_size, 0, false, None)),
|
||||
0x1A => Box::new(Mbc5::init(data, rom_size, ram_size, false, None)),
|
||||
0x1B => Box::new(Mbc5::init(data, rom_size, ram_size, false, save_path)),
|
||||
0x1B => Box::new(Mbc5::init(data, rom_size, ram_size, false, sram_location)),
|
||||
0x1C => Box::new(Mbc5::init(data, rom_size, 0, true, None)),
|
||||
0x1D => Box::new(Mbc5::init(data, rom_size, ram_size, true, None)),
|
||||
0x1E => Box::new(Mbc5::init(data, rom_size, ram_size, true, save_path)),
|
||||
0x1E => Box::new(Mbc5::init(data, rom_size, ram_size, true, sram_location)),
|
||||
0xFC => Box::new(PocketCamera::init(
|
||||
data, rom_size, ram_size, save_path, camera,
|
||||
data,
|
||||
rom_size,
|
||||
ram_size,
|
||||
sram_location,
|
||||
camera,
|
||||
)),
|
||||
_ => panic!("unimplemented mbc: {:#X}", data[0x147]),
|
||||
};
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
||||
use crate::processor::memory::{
|
||||
addresses::{AddressMarker, CartRamAddress, RomAddress},
|
||||
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
|
||||
rom::{
|
||||
sram_save::{BufferedSramTrait, MaybeBufferedSram, SaveDataLocation, SramSaveState},
|
||||
MbcSaveState,
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||
enum BankingMode {
|
||||
|
@ -37,7 +37,12 @@ pub struct Mbc1SaveState {
|
|||
}
|
||||
|
||||
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<SaveDataLocation>,
|
||||
) -> Self {
|
||||
let rom_len = rom_banks(rom_size) * ROM_BANK_SIZE;
|
||||
// in kb
|
||||
let ram = ram_size_kb(ram_size).map(|s| MaybeBufferedSram::new(save_file, s * KB));
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::processor::memory::{
|
||||
addresses::{AddressMarker, CartRamAddress, RomAddress},
|
||||
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
|
||||
rom::{
|
||||
sram_save::{BufferedSramTrait, MaybeBufferedSram, SaveDataLocation, SramSaveState},
|
||||
MbcSaveState,
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{rom_banks, Mbc, ROM_BANK_SIZE};
|
||||
|
||||
|
@ -26,7 +26,7 @@ pub struct Mbc2SaveState {
|
|||
}
|
||||
|
||||
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<SaveDataLocation>) -> Self {
|
||||
let rom_len = rom_banks(rom_size) * ROM_BANK_SIZE;
|
||||
let ram = MaybeBufferedSram::new(save_file, 512);
|
||||
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
||||
use crate::{
|
||||
processor::memory::{
|
||||
addresses::{AddressMarker, CartRamAddress, RomAddress},
|
||||
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
|
||||
rom::{
|
||||
sram_save::{BufferedSramTrait, MaybeBufferedSram, SaveDataLocation, SramSaveState},
|
||||
MbcSaveState,
|
||||
},
|
||||
},
|
||||
util::set_or_clear_bit,
|
||||
};
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
enum RtcRegister {
|
||||
|
@ -112,7 +111,7 @@ impl Mbc3 {
|
|||
rom_size: u8,
|
||||
ram_size: u8,
|
||||
rtc: bool,
|
||||
save_file: Option<PathBuf>,
|
||||
save_file: Option<SaveDataLocation>,
|
||||
) -> Self {
|
||||
let ram = ram_size_kb(ram_size).map(|s| MaybeBufferedSram::new(save_file, s * KB));
|
||||
Self {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
processor::memory::{
|
||||
addresses::{AddressMarker, CartRamAddress, RomAddress},
|
||||
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
|
||||
rom::{
|
||||
sram_save::{BufferedSramTrait, MaybeBufferedSram, SaveDataLocation, SramSaveState},
|
||||
MbcSaveState,
|
||||
},
|
||||
},
|
||||
util::get_bit,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
||||
|
||||
|
@ -42,7 +42,7 @@ impl Mbc5 {
|
|||
rom_size: u8,
|
||||
ram_size: u8,
|
||||
rumble: bool,
|
||||
save_file: Option<PathBuf>,
|
||||
save_file: Option<SaveDataLocation>,
|
||||
) -> Self {
|
||||
let ram = ram_size_kb(ram_size).map(|s| MaybeBufferedSram::new(save_file, s * KB));
|
||||
Self {
|
||||
|
|
|
@ -3,11 +3,13 @@ use crate::{
|
|||
connect::{CameraWrapperRef, PocketCamera as PocketCameraTrait},
|
||||
processor::memory::{
|
||||
addresses::{AddressMarker, CartRamAddress, RomAddress},
|
||||
rom::{MaybeBufferedSram, MbcSaveState},
|
||||
rom::{
|
||||
sram_save::{BufferedSramTrait, MaybeBufferedSram, SaveDataLocation},
|
||||
MbcSaveState,
|
||||
},
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
enum RamBank {
|
||||
Ram(u8),
|
||||
|
@ -41,7 +43,7 @@ where
|
|||
data: Vec<u8>,
|
||||
rom_size: u8,
|
||||
ram_size: u8,
|
||||
save_file: Option<PathBuf>,
|
||||
save_file: Option<SaveDataLocation>,
|
||||
camera: CameraWrapperRef<C>,
|
||||
) -> Self {
|
||||
let ram = ram_size_kb(ram_size).map(|s| MaybeBufferedSram::new(save_file, s * KB));
|
||||
|
|
246
lib/src/processor/memory/rom/sram_save.rs
Normal file
246
lib/src/processor/memory/rom/sram_save.rs
Normal file
|
@ -0,0 +1,246 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fs::{File, OpenOptions},
|
||||
io::{Read, Seek, SeekFrom, Write},
|
||||
path::PathBuf,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use super::mbcs;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SaveDataLocation {
|
||||
File(PathBuf),
|
||||
Raw(Arc<RwLock<Vec<u8>>>),
|
||||
}
|
||||
|
||||
pub(crate) enum MaybeBufferedSram {
|
||||
File(FileBufferedSram),
|
||||
Raw(RawBufferedSram),
|
||||
Unbuffered(UnbufferedSram),
|
||||
}
|
||||
|
||||
impl MaybeBufferedSram {
|
||||
pub(crate) fn new(save: Option<SaveDataLocation>, length: usize) -> Self {
|
||||
match save {
|
||||
Some(SaveDataLocation::File(path)) => Self::File(FileBufferedSram::new(path, length)),
|
||||
Some(SaveDataLocation::Raw(buf)) => Self::Raw(RawBufferedSram::new(buf, length)),
|
||||
None => Self::Unbuffered(UnbufferedSram::new(length)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_save_state(state: SramSaveState) -> Self {
|
||||
Self::Unbuffered(UnbufferedSram::new(state.length))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UnbufferedSram {
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
impl UnbufferedSram {
|
||||
fn new(length: usize) -> Self {
|
||||
let mut buf = Vec::new();
|
||||
buf.resize(length, 0);
|
||||
Self { buf }
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferedSramTrait for UnbufferedSram {
|
||||
fn len(&self) -> usize {
|
||||
self.buf.len()
|
||||
}
|
||||
|
||||
fn get(&self, addr: usize) -> u8 {
|
||||
if addr >= self.buf.len() {
|
||||
0
|
||||
} else {
|
||||
self.buf[addr]
|
||||
}
|
||||
}
|
||||
|
||||
fn set(&mut self, addr: usize, data: u8) {
|
||||
if addr < self.buf.len() {
|
||||
self.buf[addr] = data;
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) {}
|
||||
}
|
||||
|
||||
pub(crate) struct RawBufferedSram {
|
||||
buf: Arc<RwLock<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl RawBufferedSram {
|
||||
fn new(buf: Arc<RwLock<Vec<u8>>>, length: usize) -> Self {
|
||||
if let Ok(mut buf) = buf.write() {
|
||||
buf.resize(length, 0);
|
||||
}
|
||||
Self { buf }
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferedSramTrait for RawBufferedSram {
|
||||
fn len(&self) -> usize {
|
||||
match self.buf.read() {
|
||||
Ok(buf) => buf.len(),
|
||||
Err(e) => panic!("failed to lock sram buffer: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, addr: usize) -> u8 {
|
||||
match self.buf.read() {
|
||||
Ok(buf) => {
|
||||
if addr >= buf.len() {
|
||||
0
|
||||
} else {
|
||||
buf[addr]
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("failed to lock sram buffer: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn set(&mut self, addr: usize, data: u8) {
|
||||
match self.buf.write() {
|
||||
Ok(mut buf) => {
|
||||
if addr < buf.len() {
|
||||
buf[addr] = data;
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("failed to lock sram buffer: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) {}
|
||||
}
|
||||
|
||||
pub(crate) struct FileBufferedSram {
|
||||
buf: Vec<u8>,
|
||||
length: usize,
|
||||
inner: File,
|
||||
unbuffered_writes: usize,
|
||||
}
|
||||
|
||||
const NUM_WRITES_TO_FLUSH: usize = 256;
|
||||
|
||||
impl FileBufferedSram {
|
||||
fn new(path: PathBuf, length: usize) -> Self {
|
||||
let mut buf = Vec::new();
|
||||
let inner = {
|
||||
if path.exists() {
|
||||
let mut writer = OpenOptions::new()
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open(path)
|
||||
.unwrap();
|
||||
writer.read_to_end(&mut buf).unwrap();
|
||||
writer
|
||||
} else {
|
||||
buf.resize(8 * mbcs::KB, 0);
|
||||
let writer = OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(path)
|
||||
.unwrap();
|
||||
writer
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
buf,
|
||||
length,
|
||||
inner,
|
||||
unbuffered_writes: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferedSramTrait for FileBufferedSram {
|
||||
fn len(&self) -> usize {
|
||||
self.length
|
||||
}
|
||||
|
||||
fn get(&self, addr: usize) -> u8 {
|
||||
if addr >= self.buf.len() {
|
||||
0
|
||||
} else {
|
||||
self.buf[addr]
|
||||
}
|
||||
}
|
||||
|
||||
fn set(&mut self, addr: usize, data: u8) {
|
||||
self.unbuffered_writes += 1;
|
||||
while addr >= self.buf.len() {
|
||||
self.buf.resize(self.buf.len() + (8 * mbcs::KB), 0);
|
||||
}
|
||||
self.buf[addr] = data;
|
||||
if self.unbuffered_writes >= NUM_WRITES_TO_FLUSH {
|
||||
self.flush();
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) {
|
||||
self.inner.seek(SeekFrom::Start(0)).unwrap();
|
||||
self.inner.set_len(self.buf.len() as u64).unwrap();
|
||||
self.inner.write_all(&self.buf).unwrap();
|
||||
self.unbuffered_writes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait BufferedSramTrait {
|
||||
fn len(&self) -> usize;
|
||||
fn get(&self, addr: usize) -> u8;
|
||||
fn set(&mut self, addr: usize, data: u8);
|
||||
fn flush(&mut self);
|
||||
}
|
||||
|
||||
impl BufferedSramTrait for MaybeBufferedSram {
|
||||
fn len(&self) -> usize {
|
||||
match self {
|
||||
MaybeBufferedSram::File(f) => f.len(),
|
||||
MaybeBufferedSram::Raw(r) => r.len(),
|
||||
MaybeBufferedSram::Unbuffered(u) => u.len(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, addr: usize) -> u8 {
|
||||
match self {
|
||||
MaybeBufferedSram::File(f) => f.get(addr),
|
||||
MaybeBufferedSram::Raw(r) => r.get(addr),
|
||||
MaybeBufferedSram::Unbuffered(u) => u.get(addr),
|
||||
}
|
||||
}
|
||||
|
||||
fn set(&mut self, addr: usize, data: u8) {
|
||||
match self {
|
||||
MaybeBufferedSram::File(f) => f.set(addr, data),
|
||||
MaybeBufferedSram::Raw(r) => r.set(addr, data),
|
||||
MaybeBufferedSram::Unbuffered(u) => u.set(addr, data),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) {
|
||||
if let Self::File(ref mut f) = self {
|
||||
f.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub(crate) struct SramSaveState {
|
||||
length: usize,
|
||||
}
|
||||
|
||||
impl SramSaveState {
|
||||
pub fn create(sram: &MaybeBufferedSram) -> Self {
|
||||
Self { length: sram.len() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MaybeBufferedSram {
|
||||
fn drop(&mut self) {
|
||||
self.flush();
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue