Skip to content

ADR-0019: Cartridge Storage and Physical Form Factor (SD-Card Sled)

Partially supersedes: ADR-0017 — removes the DMG cartridge bus responsibility from the Pi Pico 2 coprocessor (ADR-0017 §“Decision item 2” cart-bus row, §“Decision item 3” cart-bus controller subsection, and the cart-related UART frame types in §“Decision item 4”). The Pico 2’s other two responsibilities — YM2149 PSG synthesis with I2S out to MAX98357A and the SSD1322 CIPHER-LINE OLED driver — are forced by audio-underrun and ticker-cadence considerations independent of the cart bus and remain in scope under ADR-0017. The Pico 2 stays in the canonical hardware; only the cart-bus role moves. Related: ADR-0006 (open question #2 re-resolved here; closes via SD filesystem rather than via on-cart SRAM), ADR-0018 (USB hub topology — the SD card reader bridge IC lives on this hub), ADR-0017 (Pico 2 retains audio + OLED responsibilities; cart-bus role removed by this ADR), ADR-0011 (USB-MSC prior art on the Pi OTG path), CLAUDE.md (Canonical Hardware Specification — see hygiene rule 1)

Notion task: GWP-223


ADR-0013 (Accepted 2026-04-22) committed the KN-86 to a retro-literal cartridge: Game Boy DMG 32-pin edge connector, MBC5 mapper, CR2032-backed on-cart SRAM for per-cartridge save state, and a 58 × 65 × 8 mm custom-molded shell dimensionally compatible with legacy DMG cartridges so the slot physically accepts them (the Phase-2 ghost_cartridge legacy-read easter-egg layer). The decision was earnest about the cartridge being hardware — silicon ROM, custom PCB, mapper IC, battery backup, precision edge connector — and a great deal of the rationale in ADR-0013’s Trade-off Analysis turned on what that hardware buys (capacity headroom, native per-cart save, legacy compatibility, sister-deck inheritance).

Stepping back two days later: KN-86 cartridges are not ROMs. They are bytecode files. Every authored cartridge is a .kn86 container (ADR-0006) of Fe VM bytecode plus tagged static data, executed inside an arena-allocated VM (ADR-0004) on the Pi Zero 2 W. The bytecode is indifferent to whether it was loaded from MBC5 ROM, a microSD partition, a USB mass-storage drive, or a TCP socket — it is bytes addressed by a loader. The cartridge’s job in the product, then, is ritual, collectibility, tactile identity, and homebrew-friendliness — not “be hardware.” Storage is a commodity implementation detail. Once that frame is taken seriously, ADR-0013’s expensive answers (MBC5 silicon, CR2032 cells, custom cartridge PCBs, edge connector tooling) are paying for benefits the bytecode does not need. This ADR replaces the storage and form-factor decisions with a commodity path that preserves the ritual. The fiction layer, the Lisp authoring story, the capability model, and the .kn86 format are unchanged — only the chunk of plastic the operator inserts and the bridge IC behind the slot are different.

  • ADR-0013’s industrial design and BOM commitments are imminent (90-day window from 2026-04-22). Recommitting the storage path before tooling money is spent is a 2-day ADR exercise; recommitting after is a 6-figure rework.
  • ADR-0006’s open question #2 (per-cartridge save) was just closed by ADR-0013 via on-cart MBC5 SRAM (amendment 2026-04-22). Re-superseding within the week is awkward but cleanly bounded: the resolution moves from “battery-backed SRAM on cartridge” to “file on SD filesystem” without touching the .kn86 container or the cart_save / cart_load FFI surface.
  • ADR-0018 (Accepted 2026-04-24) commits to an internal USB hub IC fanning the Pi OTG port out to the keyboard controller and a “cartridge bridge” downstream slot. The cartridge bridge in ADR-0018 was conceived as a CDC-class adapter speaking the MBC5 mapper protocol; substituting a USB-to-SD card reader bridge IC into that same downstream slot is mechanically and electrically free.
  • The Pi Zero 2 W’s primary SDIO controller is consumed by the boot SD slot; the auxiliary SDIO is shared with onboard Wi-Fi and not practically free. SPI-to-SD is slow and driver-fiddly on Raspberry Pi OS. USB mass storage works with zero custom driver codeudev surfaces insertion events, the mounted filesystem is plain vfat / exfat, and the cartridge loader becomes a read() call.
  • Single hardware target: Raspberry Pi Zero 2 W. See CLAUDE.md Canonical Hardware Specification.
  • The internal USB hub topology in ADR-0018 has finite downstream ports. The card reader bridge IC must occupy the existing “cartridge bridge” slot; this ADR does not request a new USB port.
  • Pelican 1170 interior depth budget is 80 mm (see CLAUDE.md Case row). Slot depth = shell depth + SD socket depth + PCB clearance. Verify against battery, speaker, and other components during bring-up.
  • The .kn86 container (ADR-0006), capability model (ADR-0001), Universal Deck State, cartridge history bitfield, and NoshAPI (ADR-0005) are out of scope. This ADR changes only the physical container around the bytecode and the bridge IC behind the slot.
  • Row 0 / Row 24 firmware-row layout, CIPHER-LINE Cipher-exclusivity, and primary-display semantics are untouched. Nothing in this ADR affects rendering.

The KN-86 cartridge is a standard full-size SD card carried in a custom two-piece clamshell shell. The shell is the operator-facing artifact (ritual, collectibility, label surface, tactile mass); the SD card is the underlying storage. The card is read by the Pi Zero 2 W as USB mass storage through a card reader bridge IC on the internal USB hub planned in ADR-0018. Per-cartridge save data lives as a file on the SD filesystem.

  1. Storage medium. Full-size SD card. Full-size (not microSD) is chosen for tactile grip; the operator handles the shell, but a slipped-out card needs to feel like an artifact, not a sliver. Capacity floor TBD (see Open Questions §5).
  2. Interface to the Pi. USB mass storage via a card reader bridge IC. Candidate IC families include Genesys GL3224 / GL3232 and Realtek RTS5129; the specific part is selected during breadboard prototype (see Open Questions §1). The bridge IC occupies the downstream “cartridge bridge” slot already planned on the internal USB hub in ADR-0018; no new USB port is requested, and the keyboard controller’s downstream slot is unchanged. The Pi mounts the card as a standard block device (/dev/sd*); the cartridge loader becomes a read() against the mounted filesystem.
  3. SD socket. Full-size, push-push, surface-mount, with a card-detect switch surfaced for insertion/removal events. Candidate part families include Hirose DM1 series and Molex full-size SD push-push family; the specific part is selected during bring-up (see Open Questions §2). The socket mounts on a small internal PCB at the back of the slot.
  4. Cartridge form factor — sled architecture. The shell is a mechanical carrier around a standard full-size SD card. When the operator inserts the shell, the SD card’s contacts engage the push-push socket inside the device; retention is handled by the socket’s push-push mechanism, not by the shell. The user can open the shell to replace or re-flash the SD card (reloadable; supports homebrew).
  5. Shell dimensions. Retain the ~58 × 65 × 8 mm footprint from the superseded ADR-0013 (the Game Boy form-factor sweet spot — substantial enough to hold, label surface large enough for artwork, fits the existing industrial-design language). Two-piece clamshell, screw-closed. Wall thickness: 1.5–2.5 mm for production (ABS injection); 2–3 mm for prototypes (FDM PLA / PETG). Internal locating features (ribs, pockets) keep the SD card positioned during insertion; SD card lateral tolerance is ±0.3 mm.
  6. Per-cartridge save data. A file on the SD filesystem (e.g., /save/<cart_id>.sav). Supersedes ADR-0013’s battery-backed on-cart SRAM. Universal Deck State remains on the device, on the Pi’s microSD per ADR-0011 (unchanged). Save payload format continues to be ADR-0006 tagged static data (unchanged). The cart_save / cart_load NoshAPI contract is unchanged at the FFI surface; the implementation moves from MBC5 SRAM bank-switching to filesystem read/write underneath. Cartridge code does not observe the difference.
  7. Hot-swap semantics. Physical insertion / removal generates udev events; the nOSh runtime subscribes. The capability-model Hot Swap (per the ICE BREAKER gameplay spec) now means: the operator physically ejects the cart, the nOSh runtime sees the mass-storage device disappear, cart state is preserved in the phase chain (Universal Deck State); the operator inserts a new cart, the nOSh runtime mounts it and loads the new module. The ~5-second Hot Swap timing in the ICE BREAKER spec still applies — it is now “how long the physical swap actually takes,” driven by socket push-push mechanics and udev enumeration latency rather than cartridge-firmware-managed state migration.
  8. Device-side mechanical. A Pelican 1170 inset panel carries a cartridge slot opening sized for the shell (~60 × 10 mm). The SD socket mounts on an internal PCB at the back of the slot. Slot depth budget: shell depth (~65 mm) + SD socket depth + PCB clearance. The Pelican 1170 interior is 80 mm deep (CLAUDE.md Case row); verify against battery, speaker, amp, Pi mounting pocket, and CIPHER-LINE module during bring-up. Dust-door mechanism is deferred (see Open Questions §8).

The card-detect switch on the SD socket is part of the mechanism for surfacing insertion/removal events to the nOSh runtime. The signaling path — whether the USB hub’s mass-storage device-disappear event alone is enough, or whether the card-detect switch needs a separate GPIO line into the Pi — is deferred to bring-up (Open Questions §9). Either path lands at the same nOSh-runtime-visible event (“cart present” / “cart absent”); the difference is latency and reliability under fast eject + reinsert.

  • .kn86 container (ADR-0006) — header, bytecode section, static data subsections, CART_CAPABILITIES block, debug section, CRC-32. Unchanged.
  • Fe VM and bytecode (ADR-0004, ADR-0001). Unchanged.
  • NoshAPI FFI surface (ADR-0005) — 54 primitives, three tiers. cart_save / cart_load keep their signatures; only the storage backend changes underneath. Unchanged.
  • Capability model — mission board, phase chain, Universal Deck State, cartridge history bitfield, Cipher voice, hot-swap-as-designed-mechanic. Unchanged.
  • Universal Deck State location — on the Pi’s microSD /home/shared partition per ADR-0011. Unchanged.
  • Row 0 / Row 24 layout, CIPHER-LINE auxiliary display, primary display geometry, font cell, color palette. Unchanged. See CLAUDE.md Canonical Hardware Specification.
  • Sister-deck inheritance. Toneline, Gridline, Statline, etc. inherit the SD-sled cartridge format the same way they would have inherited DMG-pinout carts under ADR-0013.
  • Pi Pico 2 coprocessor — audio (YM2149 + I2S → MAX98357A) and CIPHER-LINE OLED driver responsibilities (ADR-0017). The Pico 2 stays in the canonical hardware. Only the DMG cartridge bus responsibility, which assumed ADR-0013 as its forcing function, is removed by this ADR. The cart bus moves to a USB-mass-storage SD card via the bridge IC on the internal USB hub (per Decision §2 above) — the Pico 2 does not see the cartridge.

  • Eliminates bespoke cartridge electronics. No MBC5 silicon, no custom cartridge PCB, no precision edge connector tooling, no CR2032 cells, no battery-backed SRAM ICs. The cartridge BOM is shell + SD card + label.
  • Per-cart save is a file. No bank-switching protocol, no SRAM corruption window, no battery shelf-life. cart_save writes to the filesystem; the SD’s wear-leveling controller handles the underlying flash.
  • Homebrew is one screwdriver away. Open the shell, swap the SD, close the shell. No board-level rework, no ROM burner, no MBC5 chip to reprogram. The community ships .kn86 files; the operator drops them onto an SD with a desktop card reader.
  • Reuses commodity infrastructure. USB mass storage is the most-tested storage path in the Linux kernel. Zero custom driver code on the Pi side. The bridge IC families on the table are mature, in volume production, and well-supported.
  • Fits cleanly into ADR-0018’s hub topology. The “cartridge bridge” downstream slot was already planned and budgeted; substituting a card reader bridge IC for the previously-imagined MBC5 CDC adapter is a part-substitution, not a topology change.
  • .kn86 container size headroom is functionally unlimited. Where ADR-0013’s MBC5 ROM ceiling was 8 MB per cart (already luxurious against the 12–16 KB typical cart), the SD ceiling is the SD’s capacity (gigabytes). Cartridge content can grow without re-architecting the storage.
  • Sister decks inherit a cleaner shared standard. Toneline / Gridline / Statline get the same shell, the same socket family, the same bridge IC, the same filesystem layout. Cross-deck cartridge swaps “just work” at the physical layer.
  • The ghost_cartridge Phase-2 subsystem (ADR-0013 §“Legacy-Cartridge Read Support”) cannot operate as designed. The slot no longer accepts legacy DMG cartridges — physically or electrically — so the ~20–30-title editorial easter-egg layer ADR-0013 promised does not exist on the SD-sled platform. Whether to retire it cleanly, reimagine it on the SD platform (e.g., SHA-1 of the entire SD image as the trigger; “alien cartridge” SD images traded by collectors), or build a separate accessory port for legacy cart reads is a deferred decision and is not answered by this ADR. Flagged as Open Questions §10 below.
  • The “Kinoshita licensed the DMG cartridge format from Nintendo in 1989” fiction beat from ADR-0013 §Action Items #5 is no longer load-bearing — the physical cartridge is not a DMG. The fiction is free to take a different beat (e.g., “Kinoshita standardized on a proprietary clamshell format for the consortium catalog”). Fiction update is downstream of this ADR; not in scope here.
  • Tooling cost for the shell remains — two-piece clamshell injection-mold tooling at the same ~$15K–30K-per-color band ADR-0013 estimated for the DMG-shape shell. Lower than ADR-0013’s “shell + cartridge PCB + MBC5 + ROM + SRAM + battery” total, but not zero. The tooling is still a real budget line.
  • The cartridge is more obviously “an SD card in a case” than the ADR-0013 design. A curious operator who opens the shell finds a commodity SD card. This is by design (homebrew-friendliness) and matches the ethos of the platform, but it does sand off some of the “this is bespoke silicon” texture that the DMG-shell pitch carried. Industrial design will need to compensate via shell finish, label artistry, and internal architecture (e.g., decorative SD card carrier or branded card wrap).
  • Hot Swap timing depends on udev and USB-MSC enumeration, both of which are well-tested but variable. Bring-up will need to characterize end-to-end “physical eject → nOSh runtime sees absence → physical insert → nOSh runtime sees presence → cart loaded” latency against the ICE BREAKER spec’s ~5-second budget.
  • CART-01 (Hardware): Breadboard the card reader bridge IC + SD push-push socket + USB hub harness. Deliverable: working USB-MSC enumeration through the ADR-0018 hub topology with a known-good SD card. Resolves Open Questions §1 and §2.
  • CART-02 (Hardware): Industrial design pass on the two-piece clamshell shell. Confirm wall thickness, screw count and type, internal locating features against an SD card sample. Deliverable: STEP file + assembly drawing + first FDM print. Resolves Open Questions §3 and §6.
  • CART-03 (Hardware): Pelican 1170 inset panel revision — slot cutout dimensions (~60 × 10 mm), internal SD-socket-PCB mounting pocket, dust-door provision (or open slot, pending Open Questions §8). Deliverable: panel CAD update + clearance check against existing component layout.
  • CART-04 (Platform Engineering): udev rule + nOSh runtime subscriber for SD-cart insertion / removal. Hot-swap event plumbing into the phase chain. Deliverable: working physical-swap event flow through to the existing capability-model Hot Swap entry point. Resolves Open Questions §9 if breadboard reveals USB-event-only is insufficient.
  • CART-05 (C Engineer): Migrate cart_save / cart_load implementation from the MBC5-SRAM-shaped stub to the filesystem path. The FFI signatures stay; only the storage backend changes. Deliverable: implementation + tests + update to cart-load semantics in ADR-0006 §Loading Semantics step list (the existing “save cartridge-specific save data … to EEPROM” step becomes “save to SD filesystem path”).
  • CART-06 (Gameplay Design + PM): Decision on ghost_cartridge disposition (retire, reimagine, defer to accessory). Deliverable: a follow-up ADR or a documented retirement note. Owns Open Questions §10.
  • CART-07 (PM + Marketing): Decide whether the Launch-4 cartridges (ICE BREAKER, NeonGrid, Black Ledger, Depthcharge) ship as physical SD-sled carts or pre-installed on the device. Independent of this ADR; flagged as Open Questions §7.

These are deferred to bring-up, future ADRs, or downstream design passes. None is answered in this ADR.

  1. Card reader bridge IC selection. Genesys GL3224, GL3232, Realtek RTS5129, or another candidate. Decided during breadboard prototype (CART-01).
  2. SD socket specific part number. Hirose DM1-xxxx, Molex SD-xxxx, or another full-size push-push family member with card-detect. Decided during breadboard prototype (CART-01).
  3. Shell closure hardware. Screw type (Phillips, tri-wing, hex), screw count (2 vs. 4), screw size (M1.6 vs. M2). Decided during industrial design pass (CART-02).
  4. Production shell material. ABS (period-accurate), PC/ABS (tougher), or PC (clarity option for special-edition variants). Decided during industrial design pass.
  5. Minimum SD capacity floor. The smallest supported card for a .kn86 cart (2 GB? 4 GB? 8 GB?). Driven by SD market availability and the smallest card the cart packager wants to provision.
  6. Label / artwork system. Sticker, water-slide decal, UV print, or in-mold decoration. Decided during industrial design + first cartridge SKU pass.
  7. Launch-4 on-board vs. physical carts. RELATED BUT SEPARATE DECISION. A pending future ADR. If the Launch-4 (ICE BREAKER, NeonGrid, Black Ledger, Depthcharge) eventually ship pre-installed (not as physical carts), the physical cart format defined here still applies to Phase 2–4 modules. Not decided here.
  8. Dust-door mechanism. Spring-loaded flap, sliding cover, or open slot. Period-appropriate and aesthetically resolvable; defer to industrial design (CART-03).
  9. Cartridge-present detect signaling path. USB mass-storage device-disappear event alone vs. a separate GPIO line carrying the SD socket’s card-detect switch. Driven by measured udev latency and reliability under fast eject + reinsert sequences.
  10. ghost_cartridge disposition. ADR-0013’s Phase-2 legacy-DMG-read easter-egg subsystem cannot operate as designed under SD-sled storage. Whether to retire cleanly, reimagine on the SD platform (alternate triggers — SHA-1 of full SD image, special “alien” SD images traded by collectors, etc.), or build a separate accessory port for legacy reads is a follow-up gameplay-design decision. CART-06.

Documentation Updates (REQUIRED — part of the decision, not aspirational)

Section titled “Documentation Updates (REQUIRED — part of the decision, not aspirational)”

The PR landing this ADR ticks every box in this list, per CLAUDE.md Spec Hygiene Rule 3.

  • CLAUDE.md — Canonical Hardware Specification table: rewrite Cartridge pinout row, rewrite Cartridge shell row, remove the Cartridge mapper row (no longer applicable), rewrite Per-cartridge save row.
  • docs/architecture/adr/ADR-0013-cartridge-physical-format.md — Status changed to Superseded by ADR-0019. Supersession banner added at the top pointing to this ADR. Body retained as historical artifact.
  • docs/architecture/adr/ADR-0017-realtime-io-coprocessor.md — Status updated to note partial supersession (cart-bus role removed; audio + OLED remain). Banner added at the top enumerating which sections of ADR-0017 are obsolete (cart-bus row in §“Decision item 2”, cart-bus controller in §“Decision item 3”, cart-related UART frame types in §“Decision item 4”) and which remain (PSG, OLED). Body retained as historical artifact for the obsoleted sections and as the source of truth for the still-in-scope responsibilities.
  • docs/architecture/adr/ADR-0006-cart-format-v2.md — Amendment Log appended with a 2026-04-24 entry that re-resolves Known Unknown #2 (per-cartridge save) by pointing to ADR-0019. The 2026-04-22 amendment’s body is preserved as history; the new amendment supersedes its specific resolution mechanism (file on SD filesystem, not on-cart MBC5 SRAM).
  • docs/architecture/adr/README.md — index row added for ADR-0019; ADR-0013 row marked Superseded.
  • docs/hardware/KN-86-Pi-Zero-Build-Specification.mdRelated: cross-references add ADR-0019 and update the ADR-0013 reference. Hardware Topology subsystem table: cartridge interface row added (or updated). BOM lines for DMG connector / MBC5 / CR2032 are removed; SD push-push socket + USB-to-SD card reader bridge IC lines are added. Mechanical section: cartridge slot dimensions and internal PCB location.
  • docs/hardware/KN-86-Pi-Zero-Sourcing-Guide.mdRelated: cross-references add ADR-0019. Remove DMG / MBC5 / CR2032 sourcing entries (none currently exist as concrete BOM lines under v0.1; cross-references in narrative text are updated). Add candidate-family entries for SD push-push socket (Hirose DM1, Molex full-size SD) and USB-to-SD card reader bridge IC (Genesys GL3224 / GL3232, Realtek RTS5129) with specific part numbers TBD. Internal-USB-hub line item (5c) updated to reflect the card reader bridge IC as the second downstream device.
  • docs/KN-86-Definitive-Guide.md — Part 2 (Hardware) and Part 8 (Cartridge source anatomy / cart-save semantics) cross-references checked; cartridge-storage references updated to point at ADR-0019. Part 16 (Decision log) row added for ADR-0019.
  • docs/KN-86-Platform-Design-Master-Index.md — ADR table: ADR-0013 row marked Superseded; ADR-0019 row added.
  • docs/architecture/KN-86-Capability-Model-Spec.md — Cartridge contribution model table row for “Per-cartridge save data” updated: “battery-backed SRAM on cart (ADR-0013)” → “file on SD filesystem (ADR-0019)”.
  • docs/business/KN-86-PR-FAQ.md — Cartridge manufacturing economics paragraph (the “Custom DMG-compatible shells” reference) updated to reflect the SD-sled cartridge BOM shape.
  • docs/build/compile.py — ADR build manifest: ADR-0019 entry added.

A PR that lands this ADR without ticking these boxes fails review. The grep-sweep report in the PR description enumerates every other reference to DMG / MBC5 / CR2032 / battery-backed SRAM / ghost_cartridge / ADR-0013 found in the tree and categorizes each as Fix in this PR, Flag only (historical artifact, archived doc, pre-existing stale reference outside this ADR’s scope), or Skip (unrelated subject — e.g., macOS .dmg disk images in CI scripts).


ADR-0013 was an earnest commitment to the cartridge being hardware: silicon ROM, mapper IC, edge connector, battery-backed SRAM, custom shell. Two days later we noticed the bytecode does not care — it is bytes addressed by a loader, and every authored cartridge is a .kn86 file already. The expensive answers (MBC5, CR2032, custom cartridge PCBs) were paying for benefits a bytecode cart does not need. The cartridge’s real job is ritual, collectibility, label surface, tactile mass — the physical artifact the operator handles, branded and sized to feel substantial. So we keep the shell at GBA dimensions, keep the two-piece clamshell ergonomics, keep the slot ceremony, and put a commodity SD card inside a commodity USB card-reader bridge inside a commodity USB hub on a commodity Linux machine. The fiction layer, the Lisp authoring story, the capability model, and the .kn86 format are unchanged. Only the bridge IC behind the slot and the chunk of plastic the operator inserts are different, and they are different in the direction of “less bespoke silicon, more plastic-and-label” — which matches the ethos of a deck whose distinctive thing is that the fiction is load-bearing, not that the silicon is.