pak only.Expand description
The cartridge’s own RAM, usually kept alive by a battery.
SRAM is only readable and writable while the controller has it switched on, so
every access happens inside Sram::open, which switches it on for the
closure and off again after. Leaving it off otherwise protects the contents
while the console powers down or the cartridge is pulled.
§Declaring
use core::cell::Cell;
use gb_pak::{CriticalSection, sram};
use zerocopy::FromBytes;
#[repr(C)]
#[derive(FromBytes)]
struct Save {
magic: Cell<[u8; 4]>,
version: Cell<u8>,
hp: Cell<u8>,
gold: Cell<u16>,
}
/// The save file.
#[sram(0)]
static FILE: Save;
fn hurt(cs: CriticalSection<'_>) {
FILE.open(cs, |f| f.hp.set(f.hp.get().saturating_sub(1)));
}A static has no initializer: the bytes are the cartridge’s already. A bank
holds one of them, at its start (0xA000), and the attribute says which bank.
Turned down at compile time: a missing bank number, a bank the cartridge does not have, any bank when it has no SRAM, a value larger than one 8 KiB bank, and an initializer.
§Editing in place
The closure is handed a shared reference, so every field is written as a
Cell, the way a peripheral crate writes a register
block. A field that is not one can never be written.
unsafe { gb::interrupt::free(|cs| FILE.open(cs, |f| f.gold.set(f.gold.get() + 10))) };§Trusting the contents
A cartridge that has never been written holds noise, so the value is bounded
on [FromBytes]: no byte pattern may be invalid for it, which rules out
bool, char, enums, and references.
repr(C) is recommended: a repr(Rust) layout is unspecified and may differ
between toolchain versions, so a save an earlier build wrote could be read
back wrong.
That bounds the type, not the contents. The bytes are still whatever survived: noise on a new cartridge, decay on a failing battery, another build’s layout after an update. Check a magic number, a schema version, and a checksum before believing a save.
Structs§
- Sram
- A value at the base of SRAM bank
BANK.