mirror of
https://github.com/italicsjenga/agb.git
synced 2025-01-27 01:26:36 +11:00
Reintegrate SRAM save media reader.
This commit is contained in:
parent
d50413a3cc
commit
ec41db2fc9
4 changed files with 302 additions and 195 deletions
|
@ -224,6 +224,8 @@ pub struct Gba {
|
||||||
pub sound: sound::dmg::Sound,
|
pub sound: sound::dmg::Sound,
|
||||||
/// Manages access to the Game Boy Advance's direct sound mixer for playing raw wav files.
|
/// Manages access to the Game Boy Advance's direct sound mixer for playing raw wav files.
|
||||||
pub mixer: sound::mixer::MixerController,
|
pub mixer: sound::mixer::MixerController,
|
||||||
|
/// Manages access to the Game Boy Advance cartridge's save chip.
|
||||||
|
pub save: save::SaveManager,
|
||||||
/// Manages access to the Game Boy Advance's 4 timers.
|
/// Manages access to the Game Boy Advance's 4 timers.
|
||||||
pub timers: timer::TimerController,
|
pub timers: timer::TimerController,
|
||||||
}
|
}
|
||||||
|
@ -240,6 +242,7 @@ impl Gba {
|
||||||
display: display::Display::new(),
|
display: display::Display::new(),
|
||||||
sound: sound::dmg::Sound::new(),
|
sound: sound::dmg::Sound::new(),
|
||||||
mixer: sound::mixer::MixerController::new(),
|
mixer: sound::mixer::MixerController::new(),
|
||||||
|
save: save::SaveManager::new(),
|
||||||
timers: timer::TimerController::new(),
|
timers: timer::TimerController::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,18 +107,14 @@ use crate::sync::{Mutex, RawMutexGuard};
|
||||||
use crate::timer::Timer;
|
use crate::timer::Timer;
|
||||||
|
|
||||||
mod asm_utils;
|
mod asm_utils;
|
||||||
//mod setup;
|
|
||||||
mod utils;
|
|
||||||
|
|
||||||
//pub use asm_utils::*;
|
|
||||||
//pub use setup::*;
|
|
||||||
|
|
||||||
//pub mod eeprom;
|
//pub mod eeprom;
|
||||||
//pub mod flash;
|
//pub mod flash;
|
||||||
//pub mod sram;
|
mod sram;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
/// A list of save media types.
|
/// A list of save media types.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum MediaType {
|
pub enum MediaType {
|
||||||
/// 32KiB Battery-Backed SRAM or FRAM
|
/// 32KiB Battery-Backed SRAM or FRAM
|
||||||
Sram32K,
|
Sram32K,
|
||||||
|
@ -130,8 +126,6 @@ pub enum MediaType {
|
||||||
Flash64K,
|
Flash64K,
|
||||||
/// 128KiB flash chip
|
/// 128KiB flash chip
|
||||||
Flash128K,
|
Flash128K,
|
||||||
/// A user-defined save media type
|
|
||||||
Custom,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The type used for errors encountered while reading or writing save media.
|
/// The type used for errors encountered while reading or writing save media.
|
||||||
|
@ -194,7 +188,6 @@ fn get_save_implementation() -> Option<&'static dyn RawSaveAccess> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows reading and writing of save media.
|
/// Allows reading and writing of save media.
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct SaveData {
|
pub struct SaveData {
|
||||||
lock: RawMutexGuard<'static>,
|
lock: RawMutexGuard<'static>,
|
||||||
access: &'static dyn RawSaveAccess,
|
access: &'static dyn RawSaveAccess,
|
||||||
|
@ -332,12 +325,67 @@ impl<'a> SavePreparedBlock<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod marker {
|
||||||
|
#[repr(align(4))]
|
||||||
|
struct Align<T>(T);
|
||||||
|
|
||||||
|
static EEPROM: Align<[u8; 12]> = Align(*b"EEPROM_Vnnn\0");
|
||||||
|
static SRAM: Align<[u8; 12]> = Align(*b"SRAM_Vnnn\0\0\0");
|
||||||
|
static FLASH512K: Align<[u8; 16]> = Align(*b"FLASH512_Vnnn\0\0\0");
|
||||||
|
static FLASH1M: Align<[u8; 16]> = Align(*b"FLASH1M_Vnnn\0\0\0\0");
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn emit_eeprom_marker() {
|
||||||
|
crate::sync::memory_read_hint(&EEPROM);
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn emit_sram_marker() {
|
||||||
|
crate::sync::memory_read_hint(&SRAM);
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn emit_flash_512k_marker() {
|
||||||
|
crate::sync::memory_read_hint(&FLASH512K);
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn emit_flash_1m_marker() {
|
||||||
|
crate::sync::memory_read_hint(&FLASH1M);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Allows access to the cartridge's save data.
|
/// Allows access to the cartridge's save data.
|
||||||
pub struct SaveManager;
|
#[non_exhaustive]
|
||||||
|
pub struct SaveManager {}
|
||||||
impl SaveManager {
|
impl SaveManager {
|
||||||
|
pub(crate) const fn new() -> Self {
|
||||||
|
SaveManager {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Declares that the ROM uses battery backed SRAM/FRAM.
|
||||||
|
///
|
||||||
|
/// Battery Backed SRAM is generally very fast, but limited in size compared
|
||||||
|
/// to flash chips.
|
||||||
|
///
|
||||||
|
/// This creates a marker in the ROM that allows emulators to understand what
|
||||||
|
/// save type the Game Pak uses, and configures the save manager to use the
|
||||||
|
/// given save type.
|
||||||
|
///
|
||||||
|
/// Only one `init_*` function may be called in the lifetime of the program.
|
||||||
|
pub fn init_sram() {
|
||||||
|
marker::emit_sram_marker();
|
||||||
|
set_save_implementation(&sram::BatteryBackedAccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new accessor to the save data.
|
||||||
|
///
|
||||||
|
/// You must have initialized the save manager beforehand to use a specific
|
||||||
|
/// type of media before calling this method.
|
||||||
pub fn access() -> Result<SaveData, Error> {
|
pub fn access() -> Result<SaveData, Error> {
|
||||||
SaveData::new(None)
|
SaveData::new(None)
|
||||||
}
|
}
|
||||||
|
/// Creates a new accessor to the save data that uses the given timer for timeouts.
|
||||||
|
///
|
||||||
|
/// You must have initialized the save manager beforehand to use a specific
|
||||||
|
/// type of media before calling this method.
|
||||||
pub fn access_with_timer(timer: Timer) -> Result<SaveData, Error> {
|
pub fn access_with_timer(timer: Timer) -> Result<SaveData, Error> {
|
||||||
SaveData::new(Some(timer))
|
SaveData::new(Some(timer))
|
||||||
}
|
}
|
||||||
|
|
56
agb/src/save/sram.rs
Normal file
56
agb/src/save/sram.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
//! Module for battery backed SRAM save media support.
|
||||||
|
//!
|
||||||
|
//! SRAM acts as ordinary memory mapped into the memory space, and as such
|
||||||
|
//! is accessed using normal memory read/write commands.
|
||||||
|
|
||||||
|
use crate::save::{Error, MediaInfo, MediaType, RawSaveAccess};
|
||||||
|
use crate::save::asm_utils::*;
|
||||||
|
|
||||||
|
const SRAM_SIZE: usize = 32 * 1024; // 32 KiB
|
||||||
|
|
||||||
|
/// Checks whether an offset is contained within the bounds of the SRAM.
|
||||||
|
fn check_bounds(offset: usize, len: usize) -> Result<(), Error> {
|
||||||
|
if offset.checked_add(len).is_none() || offset + len > SRAM_SIZE {
|
||||||
|
return Err(Error::OutOfBounds);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [`RawSaveAccess`] used for battery backed SRAM.
|
||||||
|
pub struct BatteryBackedAccess;
|
||||||
|
impl RawSaveAccess for BatteryBackedAccess {
|
||||||
|
fn info(&self) -> Result<&'static MediaInfo, Error> {
|
||||||
|
Ok(&MediaInfo {
|
||||||
|
media_type: MediaType::Sram32K,
|
||||||
|
sector_shift: 0,
|
||||||
|
sector_count: SRAM_SIZE,
|
||||||
|
uses_prepare_write: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&self, offset: usize, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
|
check_bounds(offset, buffer.len())?;
|
||||||
|
unsafe {
|
||||||
|
read_raw_buf(buffer, 0x0E000000 + offset);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify(&self, offset: usize, buffer: &[u8]) -> Result<bool, Error> {
|
||||||
|
check_bounds(offset, buffer.len())?;
|
||||||
|
let val = unsafe { verify_raw_buf(buffer, 0x0E000000 + offset) };
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_write(&self, _: usize, _: usize) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&self, offset: usize, buffer: &[u8]) -> Result<(), Error> {
|
||||||
|
check_bounds(offset, buffer.len())?;
|
||||||
|
unsafe {
|
||||||
|
write_raw_buf(0x0E000000 + offset, buffer);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue