[breaking-change] Remove game ROM and bios from savestate file.
This breaks the API of GameBoyAdvanvce::save_state and restore_state methods. Currently as WIP only SDL2 frontend will adjust. Former-commit-id: 1df15c8697fef0f6adddb07a6d653947c622ba12 Former-commit-id: 2ea339dc6a0d1e7539d167c4df29694b408303da
This commit is contained in:
parent
97101d7bc1
commit
bce4456f42
|
@ -38,7 +38,9 @@ pub type SymbolTable = HashMap<String, u32>;
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct Cartridge {
|
pub struct Cartridge {
|
||||||
pub header: CartridgeHeader,
|
pub header: CartridgeHeader,
|
||||||
|
#[serde(skip)]
|
||||||
bytes: Box<[u8]>,
|
bytes: Box<[u8]>,
|
||||||
|
#[serde(skip)]
|
||||||
size: usize,
|
size: usize,
|
||||||
gpio: Option<Gpio>,
|
gpio: Option<Gpio>,
|
||||||
symbols: Option<SymbolTable>, // TODO move it somewhere else
|
symbols: Option<SymbolTable>, // TODO move it somewhere else
|
||||||
|
@ -52,6 +54,34 @@ impl Cartridge {
|
||||||
pub fn get_gpio(&self) -> &Option<Gpio> {
|
pub fn get_gpio(&self) -> &Option<Gpio> {
|
||||||
&self.gpio
|
&self.gpio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_rom_bytes(&mut self, bytes: Box<[u8]>) {
|
||||||
|
self.size = bytes.len();
|
||||||
|
self.bytes = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_rom_bytes(&self) -> &[u8] {
|
||||||
|
&self.bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'clones' the cartridge without the ROM buffer
|
||||||
|
pub fn thin_copy(&self) -> Cartridge {
|
||||||
|
Cartridge {
|
||||||
|
header: self.header.clone(),
|
||||||
|
bytes: Default::default(),
|
||||||
|
size: 0,
|
||||||
|
gpio: self.gpio.clone(),
|
||||||
|
symbols: self.symbols.clone(),
|
||||||
|
backup: self.backup.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_from(&mut self, other: Cartridge) {
|
||||||
|
self.header = other.header;
|
||||||
|
self.gpio = other.gpio;
|
||||||
|
self.symbols = other.symbols;
|
||||||
|
self.backup = other.backup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use super::sysbus::consts::*;
|
use super::sysbus::consts::*;
|
||||||
|
|
|
@ -15,14 +15,16 @@ use super::sched::{EventHandler, EventType, Scheduler, SharedScheduler};
|
||||||
use super::sound::SoundController;
|
use super::sound::SoundController;
|
||||||
use super::sysbus::SysBus;
|
use super::sysbus::SysBus;
|
||||||
use super::timer::Timers;
|
use super::timer::Timers;
|
||||||
|
use super::util::Shared;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_video_interface"))]
|
#[cfg(not(feature = "no_video_interface"))]
|
||||||
use super::VideoInterface;
|
use super::VideoInterface;
|
||||||
use super::{AudioInterface, InputInterface};
|
use super::{AudioInterface, InputInterface};
|
||||||
|
|
||||||
pub struct GameBoyAdvance {
|
pub struct GameBoyAdvance {
|
||||||
pub sysbus: Box<SysBus>,
|
|
||||||
pub cpu: arm7tdmi::Core,
|
pub cpu: arm7tdmi::Core,
|
||||||
|
pub sysbus: Box<SysBus>,
|
||||||
|
io_devs: Shared<IoDevices>,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_video_interface"))]
|
#[cfg(not(feature = "no_video_interface"))]
|
||||||
pub video_device: Rc<RefCell<dyn VideoInterface>>,
|
pub video_device: Rc<RefCell<dyn VideoInterface>>,
|
||||||
|
@ -39,8 +41,11 @@ pub struct GameBoyAdvance {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct SaveState {
|
struct SaveState {
|
||||||
sysbus: Box<SysBus>,
|
|
||||||
scheduler: Scheduler,
|
scheduler: Scheduler,
|
||||||
|
io_devs: IoDevices,
|
||||||
|
cartridge: Cartridge,
|
||||||
|
ewram: Box<[u8]>,
|
||||||
|
iwram: Box<[u8]>,
|
||||||
interrupt_flags: u16,
|
interrupt_flags: u16,
|
||||||
cpu: arm7tdmi::Core,
|
cpu: arm7tdmi::Core,
|
||||||
}
|
}
|
||||||
|
@ -83,14 +88,20 @@ impl GameBoyAdvance {
|
||||||
scheduler.clone(),
|
scheduler.clone(),
|
||||||
audio_device.borrow().get_sample_rate() as f32,
|
audio_device.borrow().get_sample_rate() as f32,
|
||||||
));
|
));
|
||||||
let io = IoDevices::new(intc, gpu, dmac, timers, sound_controller);
|
let io_devs = Shared::new(IoDevices::new(intc, gpu, dmac, timers, sound_controller));
|
||||||
let sysbus = Box::new(SysBus::new(io, bios_rom, gamepak));
|
let sysbus = Box::new(SysBus::new(
|
||||||
|
scheduler.clone(),
|
||||||
|
io_devs.clone(),
|
||||||
|
bios_rom,
|
||||||
|
gamepak,
|
||||||
|
));
|
||||||
|
|
||||||
let cpu = arm7tdmi::Core::new();
|
let cpu = arm7tdmi::Core::new();
|
||||||
|
|
||||||
let mut gba = GameBoyAdvance {
|
let mut gba = GameBoyAdvance {
|
||||||
cpu: cpu,
|
cpu,
|
||||||
sysbus: sysbus,
|
sysbus,
|
||||||
|
io_devs,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_video_interface"))]
|
#[cfg(not(feature = "no_video_interface"))]
|
||||||
video_device: video_device,
|
video_device: video_device,
|
||||||
|
@ -111,6 +122,8 @@ impl GameBoyAdvance {
|
||||||
|
|
||||||
pub fn from_saved_state(
|
pub fn from_saved_state(
|
||||||
savestate: &[u8],
|
savestate: &[u8],
|
||||||
|
bios: Box<[u8]>,
|
||||||
|
rom: Box<[u8]>,
|
||||||
#[cfg(not(feature = "no_video_interface"))] video_device: Rc<RefCell<dyn VideoInterface>>,
|
#[cfg(not(feature = "no_video_interface"))] video_device: Rc<RefCell<dyn VideoInterface>>,
|
||||||
audio_device: Rc<RefCell<dyn AudioInterface>>,
|
audio_device: Rc<RefCell<dyn AudioInterface>>,
|
||||||
input_device: Rc<RefCell<dyn InputInterface>>,
|
input_device: Rc<RefCell<dyn InputInterface>>,
|
||||||
|
@ -118,17 +131,28 @@ impl GameBoyAdvance {
|
||||||
let decoded: Box<SaveState> = bincode::deserialize_from(savestate)?;
|
let decoded: Box<SaveState> = bincode::deserialize_from(savestate)?;
|
||||||
|
|
||||||
let arm7tdmi = decoded.cpu;
|
let arm7tdmi = decoded.cpu;
|
||||||
let mut sysbus = decoded.sysbus;
|
|
||||||
let interrupts = Rc::new(Cell::new(IrqBitmask(decoded.interrupt_flags)));
|
let interrupts = Rc::new(Cell::new(IrqBitmask(decoded.interrupt_flags)));
|
||||||
let scheduler = decoded.scheduler.make_shared();
|
let scheduler = decoded.scheduler.make_shared();
|
||||||
|
let mut io_devs = Shared::new(decoded.io_devs);
|
||||||
|
let mut cartridge = decoded.cartridge;
|
||||||
|
cartridge.set_rom_bytes(rom);
|
||||||
|
io_devs.connect_irq(interrupts.clone());
|
||||||
|
io_devs.gpu.set_scheduler(scheduler.clone());
|
||||||
|
io_devs.sound.set_scheduler(scheduler.clone());
|
||||||
|
|
||||||
sysbus.io.gpu.set_scheduler(scheduler.clone());
|
let sysbus = Box::new(SysBus::new_with_memories(
|
||||||
sysbus.io.sound.set_scheduler(scheduler.clone());
|
scheduler.clone(),
|
||||||
sysbus.io.connect_irq(interrupts.clone());
|
io_devs.clone(),
|
||||||
|
cartridge,
|
||||||
|
bios,
|
||||||
|
decoded.ewram,
|
||||||
|
decoded.iwram,
|
||||||
|
));
|
||||||
|
|
||||||
Ok(GameBoyAdvance {
|
Ok(GameBoyAdvance {
|
||||||
cpu: arm7tdmi,
|
cpu: arm7tdmi,
|
||||||
sysbus: sysbus,
|
sysbus: sysbus,
|
||||||
|
io_devs,
|
||||||
|
|
||||||
interrupt_flags: interrupts,
|
interrupt_flags: interrupts,
|
||||||
|
|
||||||
|
@ -141,37 +165,44 @@ impl GameBoyAdvance {
|
||||||
|
|
||||||
overshoot_cycles: 0,
|
overshoot_cycles: 0,
|
||||||
|
|
||||||
scheduler: scheduler,
|
scheduler,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_state(&self) -> bincode::Result<Vec<u8>> {
|
pub fn save_state(&self) -> bincode::Result<Vec<u8>> {
|
||||||
let s = SaveState {
|
let s = SaveState {
|
||||||
cpu: self.cpu.clone(),
|
cpu: self.cpu.clone(),
|
||||||
sysbus: self.sysbus.clone(),
|
io_devs: self.io_devs.clone_inner(),
|
||||||
|
cartridge: self.sysbus.cartridge.thin_copy(),
|
||||||
|
iwram: Box::from(self.sysbus.get_iwram()),
|
||||||
|
ewram: Box::from(self.sysbus.get_ewram()),
|
||||||
interrupt_flags: self.interrupt_flags.get().value(),
|
interrupt_flags: self.interrupt_flags.get().value(),
|
||||||
scheduler: (*self.scheduler).clone(),
|
scheduler: self.scheduler.clone_inner(),
|
||||||
};
|
};
|
||||||
|
|
||||||
bincode::serialize(&s)
|
bincode::serialize(&s)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore_state(&mut self, bytes: &[u8]) -> bincode::Result<()> {
|
pub fn restore_state(&mut self, bytes: &[u8], bios: Box<[u8]>) -> bincode::Result<()> {
|
||||||
let decoded: Box<SaveState> = bincode::deserialize_from(bytes)?;
|
let decoded: Box<SaveState> = bincode::deserialize_from(bytes)?;
|
||||||
|
|
||||||
self.cpu = decoded.cpu;
|
self.cpu = decoded.cpu;
|
||||||
self.sysbus = decoded.sysbus;
|
|
||||||
self.scheduler = Scheduler::make_shared(decoded.scheduler);
|
self.scheduler = Scheduler::make_shared(decoded.scheduler);
|
||||||
self.interrupt_flags = Rc::new(Cell::new(IrqBitmask(decoded.interrupt_flags)));
|
self.interrupt_flags = Rc::new(Cell::new(IrqBitmask(decoded.interrupt_flags)));
|
||||||
|
self.io_devs = Shared::new(decoded.io_devs);
|
||||||
|
// Restore memory state
|
||||||
|
self.sysbus.set_bios(bios);
|
||||||
|
self.sysbus.set_iwram(decoded.iwram);
|
||||||
|
self.sysbus.set_ewram(decoded.ewram);
|
||||||
// Redistribute shared pointers
|
// Redistribute shared pointers
|
||||||
self.sysbus.io.connect_irq(self.interrupt_flags.clone());
|
self.io_devs.connect_irq(self.interrupt_flags.clone());
|
||||||
self.sysbus.io.gpu.set_scheduler(self.scheduler.clone());
|
self.io_devs.gpu.set_scheduler(self.scheduler.clone());
|
||||||
self.sysbus.io.sound.set_scheduler(self.scheduler.clone());
|
self.io_devs.sound.set_scheduler(self.scheduler.clone());
|
||||||
|
self.sysbus.set_scheduler(self.scheduler.clone());
|
||||||
self.cycles_to_next_event = 1;
|
self.sysbus.set_io_devices(self.io_devs.clone());
|
||||||
|
self.sysbus.cartridge.update_from(decoded.cartridge);
|
||||||
self.sysbus.created();
|
self.sysbus.created();
|
||||||
|
self.cycles_to_next_event = 1;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,8 +138,8 @@ pub mod prelude {
|
||||||
pub use super::gpu::{DISPLAY_HEIGHT, DISPLAY_WIDTH};
|
pub use super::gpu::{DISPLAY_HEIGHT, DISPLAY_WIDTH};
|
||||||
pub use super::util::{read_bin_file, write_bin_file};
|
pub use super::util::{read_bin_file, write_bin_file};
|
||||||
pub use super::Bus;
|
pub use super::Bus;
|
||||||
pub use super::{AudioInterface, InputInterface, StereoSample};
|
|
||||||
#[cfg(not(feature = "no_video_interface"))]
|
#[cfg(not(feature = "no_video_interface"))]
|
||||||
pub use super::VideoInterface;
|
pub use super::VideoInterface;
|
||||||
|
pub use super::{AudioInterface, InputInterface, StereoSample};
|
||||||
pub use super::{GBAError, GBAResult, GameBoyAdvance};
|
pub use super::{GBAError, GBAResult, GameBoyAdvance};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use std::cell::UnsafeCell;
|
use super::util::Shared;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -55,31 +54,7 @@ pub struct Scheduler {
|
||||||
events: Vec<Event>,
|
events: Vec<Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opt-out of runtime borrow checking by using unsafe cell
|
pub type SharedScheduler = Shared<Scheduler>;
|
||||||
// SAFETY: We need to make sure that the scheduler event queue is not modified while iterating it.
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SharedScheduler(Rc<UnsafeCell<Scheduler>>);
|
|
||||||
|
|
||||||
impl std::ops::Deref for SharedScheduler {
|
|
||||||
type Target = Scheduler;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
unsafe { &(*self.0.get()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::DerefMut for SharedScheduler {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
unsafe { &mut (*self.0.get()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for SharedScheduler {
|
|
||||||
fn clone(&self) -> SharedScheduler {
|
|
||||||
SharedScheduler(self.0.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait EventHandler {
|
pub trait EventHandler {
|
||||||
/// Handle the scheduler event
|
/// Handle the scheduler event
|
||||||
|
@ -92,11 +67,11 @@ impl Scheduler {
|
||||||
timestamp: 0,
|
timestamp: 0,
|
||||||
events: Vec::with_capacity(NUM_EVENTS),
|
events: Vec::with_capacity(NUM_EVENTS),
|
||||||
};
|
};
|
||||||
SharedScheduler(Rc::new(UnsafeCell::new(sched)))
|
SharedScheduler::new(sched)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_shared(self) -> SharedScheduler {
|
pub fn make_shared(self) -> SharedScheduler {
|
||||||
SharedScheduler(Rc::new(UnsafeCell::new(self)))
|
SharedScheduler::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn schedule(&mut self, typ: EventType, cycles: usize) {
|
pub fn schedule(&mut self, typ: EventType, cycles: usize) {
|
||||||
|
|
|
@ -6,7 +6,8 @@ use super::bus::*;
|
||||||
use super::cartridge::Cartridge;
|
use super::cartridge::Cartridge;
|
||||||
use super::dma::DmaNotifer;
|
use super::dma::DmaNotifer;
|
||||||
use super::iodev::{IoDevices, WaitControl};
|
use super::iodev::{IoDevices, WaitControl};
|
||||||
use super::util::{BoxedMemory, WeakPointer};
|
use super::sched::Scheduler;
|
||||||
|
use super::util::{BoxedMemory, Shared, WeakPointer};
|
||||||
|
|
||||||
pub mod consts {
|
pub mod consts {
|
||||||
pub const WORK_RAM_SIZE: usize = 256 * 1024;
|
pub const WORK_RAM_SIZE: usize = 256 * 1024;
|
||||||
|
@ -164,9 +165,10 @@ impl CycleLookupTables {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SysBus {
|
pub struct SysBus {
|
||||||
pub io: IoDevices,
|
pub io: Shared<IoDevices>,
|
||||||
|
scheduler: Shared<Scheduler>,
|
||||||
|
|
||||||
bios: BoxedMemory,
|
bios: BoxedMemory,
|
||||||
onboard_work_ram: BoxedMemory,
|
onboard_work_ram: BoxedMemory,
|
||||||
|
@ -181,24 +183,70 @@ pub struct SysBus {
|
||||||
pub type SysBusPtr = WeakPointer<SysBus>;
|
pub type SysBusPtr = WeakPointer<SysBus>;
|
||||||
|
|
||||||
impl SysBus {
|
impl SysBus {
|
||||||
pub fn new(io: IoDevices, bios_rom: Box<[u8]>, cartridge: Cartridge) -> SysBus {
|
pub fn new_with_memories(
|
||||||
|
scheduler: Shared<Scheduler>,
|
||||||
|
io: Shared<IoDevices>,
|
||||||
|
cartridge: Cartridge,
|
||||||
|
bios_rom: Box<[u8]>,
|
||||||
|
ewram: Box<[u8]>,
|
||||||
|
iwram: Box<[u8]>,
|
||||||
|
) -> SysBus {
|
||||||
let mut luts = CycleLookupTables::default();
|
let mut luts = CycleLookupTables::default();
|
||||||
luts.init();
|
luts.init();
|
||||||
luts.update_gamepak_waitstates(io.waitcnt);
|
luts.update_gamepak_waitstates(io.waitcnt);
|
||||||
|
|
||||||
SysBus {
|
SysBus {
|
||||||
io,
|
io,
|
||||||
|
scheduler,
|
||||||
|
cartridge,
|
||||||
|
|
||||||
bios: BoxedMemory::new(bios_rom),
|
bios: BoxedMemory::new(bios_rom),
|
||||||
onboard_work_ram: BoxedMemory::new(vec![0; WORK_RAM_SIZE].into_boxed_slice()),
|
onboard_work_ram: BoxedMemory::new(ewram),
|
||||||
internal_work_ram: BoxedMemory::new(vec![0; INTERNAL_RAM_SIZE].into_boxed_slice()),
|
internal_work_ram: BoxedMemory::new(iwram),
|
||||||
cartridge: cartridge,
|
|
||||||
|
|
||||||
cycle_luts: luts,
|
cycle_luts: luts,
|
||||||
|
|
||||||
trace_access: false,
|
trace_access: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
scheduler: Shared<Scheduler>,
|
||||||
|
io: Shared<IoDevices>,
|
||||||
|
bios_rom: Box<[u8]>,
|
||||||
|
cartridge: Cartridge,
|
||||||
|
) -> SysBus {
|
||||||
|
let ewram = vec![0; WORK_RAM_SIZE].into_boxed_slice();
|
||||||
|
let iwram = vec![0; INTERNAL_RAM_SIZE].into_boxed_slice();
|
||||||
|
SysBus::new_with_memories(scheduler, io, cartridge, bios_rom, ewram, iwram)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_bios(&mut self, buffer: Box<[u8]>) {
|
||||||
|
self.bios.mem = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_ewram(&mut self, buffer: Box<[u8]>) {
|
||||||
|
self.onboard_work_ram.mem = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_iwram(&mut self, buffer: Box<[u8]>) {
|
||||||
|
self.internal_work_ram.mem = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ewram(&self) -> &[u8] {
|
||||||
|
&self.onboard_work_ram.mem
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_iwram(&self) -> &[u8] {
|
||||||
|
&self.internal_work_ram.mem
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_scheduler(&mut self, s: Shared<Scheduler>) {
|
||||||
|
self.scheduler = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_io_devices(&mut self, io_devs: Shared<IoDevices>) {
|
||||||
|
self.io = io_devs;
|
||||||
|
}
|
||||||
|
|
||||||
/// must be called whenever this object is instanciated
|
/// must be called whenever this object is instanciated
|
||||||
pub fn created(&mut self) {
|
pub fn created(&mut self) {
|
||||||
let ptr = SysBusPtr::new(self as *mut SysBus);
|
let ptr = SysBusPtr::new(self as *mut SysBus);
|
||||||
|
|
|
@ -200,9 +200,9 @@ impl Timers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if id == 0 || id == 1 {
|
if id == 0 || id == 1 {
|
||||||
sb.io
|
let io = unsafe { sb.io.inner_unsafe() };
|
||||||
.sound
|
io.sound
|
||||||
.handle_timer_overflow(&mut sb.io.dmac, id, num_overflows);
|
.handle_timer_overflow(&mut io.dmac, id, num_overflows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,6 +201,66 @@ impl<T> Default for WeakPointer<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use std::cell::UnsafeCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// Opt-out of runtime borrow checking of RefCell by using UnsafeCell
|
||||||
|
/// SAFETY: Up to the user to make sure the usage of the shared object is safe
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Shared<T>(Rc<UnsafeCell<T>>);
|
||||||
|
|
||||||
|
impl<T> std::ops::Deref for Shared<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
unsafe { &(*self.0.get()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> std::ops::DerefMut for Shared<T> {
|
||||||
|
#[inline]
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
unsafe { &mut (*self.0.get()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Clone for Shared<T> {
|
||||||
|
#[inline]
|
||||||
|
fn clone(&self) -> Shared<T> {
|
||||||
|
Shared(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Shared<T> {
|
||||||
|
pub fn new(t: T) -> Shared<T> {
|
||||||
|
Shared(Rc::new(UnsafeCell::new(t)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn inner_unsafe(&self) -> &mut T {
|
||||||
|
&mut (*self.0.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Shared<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
pub fn clone_inner(&self) -> T {
|
||||||
|
self.deref().clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for Shared<T>
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
{
|
||||||
|
fn default() -> Shared<T> {
|
||||||
|
Shared::new(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct BoxedMemory {
|
pub struct BoxedMemory {
|
||||||
|
|
|
@ -121,7 +121,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
let bios_path = Path::new(matches.value_of("bios").unwrap_or_default());
|
let bios_path = Path::new(matches.value_of("bios").unwrap_or_default());
|
||||||
let bios_bin = match read_bin_file(bios_path) {
|
let bios_bin = match read_bin_file(bios_path) {
|
||||||
Ok(bios) => bios,
|
Ok(bios) => bios.into_boxed_slice(),
|
||||||
_ => {
|
_ => {
|
||||||
ask_download_bios();
|
ask_download_bios();
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
|
@ -203,7 +203,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let gamepak = builder.build()?;
|
let gamepak = builder.build()?;
|
||||||
|
|
||||||
let mut gba = GameBoyAdvance::new(
|
let mut gba = GameBoyAdvance::new(
|
||||||
bios_bin.into_boxed_slice(),
|
bios_bin.clone(),
|
||||||
gamepak,
|
gamepak,
|
||||||
video.clone(),
|
video.clone(),
|
||||||
audio.clone(),
|
audio.clone(),
|
||||||
|
@ -276,7 +276,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
if savestate_path.is_file() {
|
if savestate_path.is_file() {
|
||||||
let save = read_bin_file(&savestate_path)?;
|
let save = read_bin_file(&savestate_path)?;
|
||||||
info!("Restoring state from {:?}...", savestate_path);
|
info!("Restoring state from {:?}...", savestate_path);
|
||||||
gba.restore_state(&save)?;
|
gba.restore_state(&save, bios_bin.clone())?;
|
||||||
info!("Restored!");
|
info!("Restored!");
|
||||||
} else {
|
} else {
|
||||||
info!("Savestate not created, please create one by pressing F5");
|
info!("Savestate not created, please create one by pressing F5");
|
||||||
|
|
Reference in a new issue