Expand description
Functions whose machine code can be copied into RAM (or HRAM) and run there.
ram_fn defines a function and a handle implementing RamFn. The handle
places the function between two name-sorted markers, so its exact length is
known; RamFn::install copies the bytes into a fixed-size RAM buffer and
returns a callable pointer.
The function must be position independent: it may reference statics and
other functions by absolute address (those do not move), but it must not
branch to its own code by an absolute target. Small functions qualify (the
SM83 backend uses relative jr for short branches); a long function whose
branches become absolute jp does not, and copying it would run the wrong
code; cargo-gb rejects such a function at build time (see below).
Parameters and return values are fine: they travel in registers and on the
stack, which copying does not affect.
§Examples
Define a function, run the ROM copy in place, then copy it into a RAM buffer
and run that. Bring RamFn into scope for the handle’s methods.
use gb_ram_fn::{RamFn, ram_fn};
static mut COUNTER: u16 = 0;
#[ram_fn(max = 16)]
fn inc() {
unsafe {
let c = core::ptr::read_volatile(&raw const COUNTER);
core::ptr::write_volatile(&raw mut COUNTER, c.wrapping_add(1));
}
}
static mut BUF: [u8; 16] = [0; 16];
fn demo() {
inc.rom()(); // run in place, in ROM
let ram_inc = unsafe { inc.install(&raw mut BUF) };
ram_inc(); // run the RAM copy
}Parameters and return values work; the installed pointer keeps the signature.
The buffer must be at least max bytes, checked at compile time:
use gb_ram_fn::{RamFn, ram_fn};
#[ram_fn(max = 8)]
fn add(a: u8, b: u8) -> u8 {
a.wrapping_add(b)
}
static mut BUF: [u8; 8] = [0; 8];
fn demo() -> u8 {
let added = unsafe { add.install(&raw mut BUF) };
added(2, 3) // 5, computed from the RAM copy
}§Build-time verification
Two things keep install safe: the function fits its
declared max (a compile-time-sized buffer then always holds it), and it is
position independent (the copy runs correctly at its new address). The
compiler cannot confirm either, so cargo-gb checks them over the linked ROM:
END - run <= max, and that the code holds no absolute self-references.
These are therefore guarantees of a cargo-gb build, not of #[ram_fn]
itself. A build path that skips those checks does not provide them: install
may overflow its buffer, or copy code that breaks when run relocated.
Traits§
Attribute Macros§
- ram_fn
- Define a RAM-copyable function with any non-generic signature.