mirror of
https://github.com/italicsjenga/agb.git
synced 2025-01-26 00:56:38 +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,
|
||||
/// Manages access to the Game Boy Advance's direct sound mixer for playing raw wav files.
|
||||
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.
|
||||
pub timers: timer::TimerController,
|
||||
}
|
||||
|
@ -240,6 +242,7 @@ impl Gba {
|
|||
display: display::Display::new(),
|
||||
sound: sound::dmg::Sound::new(),
|
||||
mixer: sound::mixer::MixerController::new(),
|
||||
save: save::SaveManager::new(),
|
||||
timers: timer::TimerController::new(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,18 +107,14 @@ use crate::sync::{Mutex, RawMutexGuard};
|
|||
use crate::timer::Timer;
|
||||
|
||||
mod asm_utils;
|
||||
//mod setup;
|
||||
mod utils;
|
||||
|
||||
//pub use asm_utils::*;
|
||||
//pub use setup::*;
|
||||
|
||||
//pub mod eeprom;
|
||||
//pub mod flash;
|
||||
//pub mod sram;
|
||||
mod sram;
|
||||
mod utils;
|
||||
|
||||
/// A list of save media types.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum MediaType {
|
||||
/// 32KiB Battery-Backed SRAM or FRAM
|
||||
Sram32K,
|
||||
|
@ -130,8 +126,6 @@ pub enum MediaType {
|
|||
Flash64K,
|
||||
/// 128KiB flash chip
|
||||
Flash128K,
|
||||
/// A user-defined save media type
|
||||
Custom,
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SaveData {
|
||||
lock: RawMutexGuard<'static>,
|
||||
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.
|
||||
pub struct SaveManager;
|
||||
#[non_exhaustive]
|
||||
pub struct 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> {
|
||||
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> {
|
||||
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