gb_bank/lib.rs
1//! Compile-time-safe ROM bank switching for the Game Boy.
2//!
3//! `gb-bank` is the user-facing front end of the banking toolchain: the runtime
4//! types, plus the macros that turn ordinary functions and statics into bank-safe
5//! ones.
6//!
7//! # The problem
8//!
9//! A Game Boy cartridge maps one 16 KiB ROM **bank** at a time into the
10//! `0x4000..0x8000` window. Anything in another bank is unaddressable until it is
11//! switched in, and reading it anyway yields whichever bytes happen to be mapped,
12//! with nothing to fault on the way.
13//!
14//! This crate makes that a type error. Reaching banked code or data requires a
15//! token proving its bank is mapped, and the only way to get one is to perform the
16//! switch.
17//!
18//! # Getting started
19//!
20//! ## Put code in a bank
21//!
22//! One module is one bank group. `bank::module!()` declares it, `#[bank]` marks
23//! what goes in the bank.
24//!
25//! ```ignore
26//! mod sound {
27//! use gb_bank::*;
28//! bank::module!();
29//!
30//! #[bank]
31//! pub static NOTES: [u8; 4] = [60, 62, 64, 65];
32//!
33//! #[bank]
34//! pub fn play(i: u8) -> u8 {
35//! NOTES.local()[i as usize]
36//! }
37//! }
38//! ```
39//!
40//! `gb-bank-pack` decides at link time which ROM bank the module lands in. Naming
41//! one yourself is optional (see [Bank layout](#bank-layout)).
42//!
43//! ## Call it
44//!
45//! Your entry point is `#[bank::main]`, which lives in bank 0 and is always mapped.
46//! It stands in for `#[gb_rt::entry]`, so the signature is the one that macro
47//! wants, `fn() -> !`.
48//!
49//! ```ignore
50//! #[bank::main]
51//! pub fn main() -> ! {
52//! loop {
53//! let note = sound::play(0).drive();
54//! }
55//! }
56//! ```
57//!
58//! `sound::play(0)` does not run the body. It captures the call, and `.drive()`
59//! runs it: switch into sound's bank, call, switch back.
60//!
61//! ## Read its data
62//!
63//! `#[bank] static` gives you a [`Far`] pointer. Holding one is always fine;
64//! reading through it is what needs the bank mapped.
65//!
66//! ```ignore
67//! #[bank::main]
68//! pub fn main() -> ! {
69//! let first = sound::NOTES.there(|n| n[0]);
70//! loop {}
71//! }
72//! ```
73//!
74//! `.there()` switches, lends your closure `&[u8; 4]`, and switches back. Whatever
75//! the closure returns is copied out, so it must not be a pointer into the bank you
76//! just left; that is what [`BankSafe`] checks.
77//!
78//! ## Inside a banked function
79//!
80//! A `#[bank]` body has one restriction, and it comes from the hardware: its own
81//! code sits in the switchable window, so it cannot perform a switch. The next
82//! instruction fetch would come from whatever bank replaced it.
83//!
84//! Calls are fine, because the switch happens in a bank-0 trampoline rather than in
85//! your function:
86//!
87//! ```ignore
88//! #[bank]
89//! pub fn tick() -> u8 {
90//! NOTES.local()[0] // same bank: no switch at all
91//! + other::helper().drive() // another bank: switches in bank 0
92//! }
93//! ```
94//!
95//! `.there()` and [`scope`] are not, because they run *your* closure while the
96//! other bank is mapped. Both are a compile error in a `#[bank]` body. Route them
97//! through a `#[bank::zero]` helper, which lives in bank 0 and can be called from
98//! any bank:
99//!
100//! ```ignore
101//! #[bank::zero]
102//! fn notes() -> [u8; 4] {
103//! sound::NOTES.there(|n| *n) // bank 0: allowed
104//! }
105//!
106//! #[bank]
107//! pub fn tick() -> u8 {
108//! let n = notes().drive(); // copied out; this bank is mapped again
109//! n[0].wrapping_add(n[3])
110//! }
111//! ```
112//!
113//! ## Runtime dispatch
114//!
115//! A [`Far<T, G>`](Far) carries its bank in the type, so pointers into different
116//! banks are different types. [`erase`](Far::erase) drops the type for a runtime
117//! number, letting a table hold entries from several banks.
118//!
119//! ```ignore
120//! let table: [DynFar<fn(u8) -> u8>; 2] = [
121//! far!(enemy::ai).erase(),
122//! far!(hud_tick).erase(), // a bank-0 helper works here too
123//! ];
124//! table[i].invoke(state)
125//! ```
126//!
127//! # Where the switch happens
128//!
129//! Six operations reach banked code or data. Which one you can use depends on where
130//! you are; what it costs depends on whether it switches. The ones that do switch
131//! restore the caller's bank on the way out.
132//!
133//! | | switches | usable from |
134//! |---|---|---|
135//! | [`local`](Far::local) | no | any body, with a token for that bank |
136//! | [`near`](Warp::near) | no | any body, with a token for that bank |
137//! | [`drive`](Warp::drive) | yes | any body |
138//! | [`invoke`](FarCall::invoke) | yes | any body |
139//! | [`there`](FarWith::there) | yes | bank 0 only |
140//! | [`scope`] | yes | bank 0 only |
141//!
142//! `drive` and `invoke` run one whole function across the switch, so no code of
143//! yours is unmapped while it happens and they compile to a bank-0 trampoline.
144//! `there` and `scope` lend your closure instead, which is why they need to be in
145//! bank 0 to begin with.
146//!
147//! `local` and `near` take a token for the exact group, so they never switch, and
148//! reaching the wrong bank through one is a type error. Use them to batch: open one
149//! [`scope`] and work inside it.
150//!
151//! ```ignore
152//! let r = scope(|b| {
153//! let x = sound::play(v).near(b); // already in sound's bank
154//! sound::play(x).near(b) // still there: no second switch
155//! });
156//! ```
157//!
158//! A switch is also elided when it would be a no-op: when the target group is the
159//! caller's own, and when either side is the always-mapped [`GroupZero`].
160//!
161//! # The model
162//!
163//! A [`Group`] is the compile-time identity of one bank, a zero-sized type that
164//! `bank::module!()` generates per module. A [`Bank<G>`](Bank) is a zero-sized
165//! token, and holding one witnesses that `G`'s bank is mapped right now. The token
166//! is `!Send`, not `Clone`, and mintable only through [`scope`] or the unsafe
167//! [`assume`](Bank::assume), so it cannot be fabricated.
168//!
169//! [`scope`] is the switch primitive everything else is built on. It also takes an
170//! [`Anchor`]: a witness that the *calling* code is in bank 0, which is what makes
171//! it safe to run a closure while another bank is mapped. `#[bank::main]` and
172//! `#[bank::zero]` bodies hold one; a `#[bank]` body does not.
173//!
174//! A [`Far<T, G>`](Far) splits the address from the access. The address is a plain
175//! [`Copy`] value that survives any switch; reading it borrows a [`Bank<G>`](Bank).
176//! Since a switch needs that token by `&mut`, a reference into a bank cannot outlive
177//! the switch away from it.
178//!
179//! A [`Warp`] is a call captured but not yet run, so that the switch can take the
180//! *caller's* token at the point it is driven.
181//!
182//! ## Prior art
183//!
184//! [`Bank`] and [`Far`] follow [GhostCell], which keeps a permission token apart
185//! from the data it guards, tied by a brand. gb-bank brands with a group *type*
186//! where GhostCell uses a lifetime, because a bank's identity is fixed and reused
187//! across many functions, which a per-scope lifetime cannot express.
188//!
189//! [`Warp`] follows [`Future`]: an `async fn` returns something inert until
190//! `.await`, and a banked call is deferred for the same reason, except that
191//! [`drive`](Warp::drive) takes a token rather than an executor.
192//!
193//! [GhostCell]: https://plv.mpi-sws.org/rustbelt/ghostcell/
194//!
195//! # The macros
196//!
197//! - [`bank::module!()`](bank::module) declares the enclosing module as a bank
198//! group. `bank::module!(N)` pins it to bank `N` instead of auto-assigning.
199//! - [`bank::inherit!()`](bank::inherit) in a submodule folds it into its *parent*
200//! module's group (`super` is a keyword, hence the name).
201//! - [`#[bank]`](macro@bank) on a `fn` rewrites it to return `impl Warp`; on a
202//! `static` it exposes a [`Far`]. Also works on an `impl` or `trait`. The return
203//! type must be [`BankSafe`].
204//! - [`#[bank::main]`](bank::main) marks the entry point in bank 0. It wraps
205//! `#[gb_rt::entry]`, so that attribute must not be applied as well.
206//! - [`#[bank::zero]`](bank::zero) marks a bank-0 helper callable from any bank: it
207//! forwards the caller's token, so its own banked calls restore the caller's bank.
208//! - [`far!`](macro@far) takes a [`Far`] to a banked function for dispatch tables.
209//! It works on a `#[bank::zero]` helper too, so a table can mix the two.
210//!
211//! ## Sugar
212//!
213//! Inside any `#[bank]` / `#[bank::main]` / `#[bank::zero]` body the macro injects
214//! the ambient bank token (and, in a bank-0 body, an [`Anchor`]) as implicit leading
215//! arguments, so they never appear in your code:
216//!
217//! | you write | the macro emits | where |
218//! |---|---|---|
219//! | `enemy::ai(s).drive()` | `enemy::ai(s).drive(&mut __bank)` | any body |
220//! | `enemy::ai(s).near()` | `enemy::ai(s).near(&mut __bank)` | any body |
221//! | `table[i].invoke(s)` | `table[i].invoke(&mut __bank, (s,))` | any body |
222//! | `NOTES.local()` | `NOTES.local(&__bank)` | any body |
223//! | `NOTES.there(\|t\| ..)` | `NOTES.there(__anchor, &mut __bank, \|t\| ..)` | bank 0 only |
224//! | `scope(\|b\| ..)` | `scope(__anchor, &mut __bank, \|b\| ..)` | bank 0 only |
225//!
226//! A [`scope`] rebinds the ambient token to its own closure parameter `b`, so a
227//! nested `scope` or a `.drive()` / `.near()` / `.local()` *inside* it threads `b`,
228//! not the outer token; the `Anchor`, being [`Copy`], flows in automatically. The
229//! ambient `__bank` is hidden and cannot be named, but `b` can.
230//!
231//! ### Threading is by method name, not type
232//!
233//! The rewrite runs before type checking, so it matches on the method *name* alone
234//! and threads the token into every `.drive()` / `.near()` / `.local()` /
235//! `.invoke()` / `.there(..)` (and `scope(..)`) in the body, whatever the receiver.
236//! The names are deliberately uncommon. If one does collide with an unrelated
237//! method, call that one as `Type::method(recv, args)`: a path call is not
238//! method-call sugar, so the macro leaves it alone.
239//!
240//! ## Bank layout
241//!
242//! By default gb-bank-pack bin-packs the banked modules into 16 KiB banks. Pin each
243//! module with `bank::module!(N)` for a deterministic one-module-per-bank layout, so
244//! a call from one module to another is always a genuine switch.
245//!
246//! ```ignore
247//! mod audio { use gb_bank::*; bank::module!(1); /* ... */ }
248//! mod physics {
249//! use gb_bank::*;
250//! bank::module!(2);
251//! pub mod trig { use gb_bank::*; bank::inherit!(); /* shares physics: bank 2 */ }
252//! }
253//! ```
254//!
255//! Bank 0 is the always-mapped region and cannot be pinned to. With no MBC at all
256//! the whole 32 KiB is fixed, and a build for such a cartridge carries no
257//! switching code at all.
258//!
259//! # Panics, unwinding, and interrupts
260//!
261//! The bank restore in [`scope`] (and the [`Far`] call / borrow paths) runs *after*
262//! the closure returns, so an unwinding panic would skip it and leave the wrong
263//! bank mapped. This is sound on the Game Boy because the target aborts on panic
264//! (there is no unwinder), so a panic never resumes into a stale-bank token. These
265//! types are not designed to be unwind-safe on a hosted, unwinding target.
266//!
267//! The safety model also assumes nothing changes the mapped bank *behind the
268//! token's back*. An interrupt handler that switches banks (e.g. to read banked
269//! data) must save [`current_bank`] on entry and restore it before returning, so the
270//! interrupted code resumes with the bank its live token still claims is mapped. An
271//! ISR that is not bank-transparent breaks the invariant, just like a raw
272//! [`switch_bank`] not paired with a matching restore.
273//!
274//! # Cartridges
275//!
276//! Which banks exist at all is the cartridge's business. `cargo-gb` reads the type
277//! from `header.toml` and refuses a build that needs more banks than it can map.
278//!
279//! | Cartridge | Switchable banks | ROM | with `wide_banks = true` |
280//! |---|---|---|---|
281//! | ROM ONLY | none | 32 KiB | |
282//! | MBC1 | 1-31 | 512 KiB | 1-127 minus `0x20`, `0x40`, `0x60`, 2 MiB |
283//! | MBC2 | 1-15 | 256 KiB | |
284//! | MBC3 | 1-127 | 2 MiB | |
285//! | MBC5 | 1-255 | 4 MiB | 1-511, 8 MiB |
286//! | MBC7 | 1-127 | 2 MiB | |
287//!
288//! The ranges without `wide_banks` are what a single write to the register at
289//! `0x2000` selects, and that is all the runtime writes by default. `wide_banks`
290//! brings in the cartridge's second bank register, which only MBC1 and MBC5 have;
291//! see [`BankNumber`] for what that costs. The three banks MBC1 has to skip are the
292//! ones whose low five bits are zero, which it reads as bank 1, and its upper bits
293//! only reach the ROM in banking mode 0, so selecting mode 1 for RAM banking gives
294//! up banks 32 and above.
295//!
296//! MBC6, MMM01, HuC1, HuC3, and the Pocket Camera and TAMA5 mappers are not built
297//! in for now. A custom cartridge that selects banks its own way implements
298//! [`Mapper`] and names it with [`set_mapper!`].
299//!
300
301#![no_std]
302#![feature(asm_experimental_arch)]
303#![feature(negative_impls)]
304#![feature(fn_traits, unboxed_closures, tuple_trait, const_trait_impl, const_cmp)]
305
306// The runtime model (bank tokens, far pointers, the scope/switch primitives) lives
307// in a private module and is re-exported flat; the facade below adds the macros.
308mod model;
309pub use model::*;
310
311// `#[bank]` lives in the macro namespace; `bank::{main, module, zero}` in the
312// module namespace. The two `bank`s coexist (different namespaces).
313pub use gb_bank_macros::{bank, far};
314
315/// The types the macros name in their expansion. Not an API.
316#[doc(hidden)]
317pub mod __private {
318 pub use crate::model::warp::{BankedWarp, FixedFn, FixedWarp};
319}
320
321
322/// The banking attribute macros, namespaced as `bank::*`.
323///
324/// [`bank::module!()`](bank::module) declares a bank group,
325/// [`#[bank::main]`](macro@bank::main) the bank-0 entry point, and
326/// [`#[bank::zero]`](macro@bank::zero) a bank-0 helper. (The bare
327/// [`#[bank]`](macro@crate::bank) attribute is a sibling in the macro namespace,
328/// not in this module.)
329pub mod bank {
330 pub use gb_bank_macros::{
331 bank_inherit as inherit, bank_main as main, bank_module as module, bank_zero as zero,
332 };
333}
334
335/// The [`mod@bank`] attributes, also at the root.
336///
337/// A facade re-exporting this crate as its own `bank` module then reaches them as
338/// `bank::main` rather than `bank::bank::main`.
339pub use gb_bank_macros::{
340 bank_inherit as inherit, bank_main as main, bank_module as module, bank_zero as zero,
341};
342
343/// Everything needed to write banked code in one import.
344pub mod prelude {
345 pub use crate::bank;
346 pub use crate::{
347 far, scope, Anchor, Bank, DynFar, Far, FarCall, FarWith, Group, GroupZero, Warp,
348 BankSafe,
349 };
350}