The NoshAPI is the full-runtime FFI surface that kn86emu exposes to
cartridge Lisp. ADR-0005 enumerated 54 primitives across three
capability tiers (all-carts, mission-context, REPL-read-only);
ADR-0007 layered on the mission-script grammar; runtime evolution has
added CIPHER-LINE, nEmacs, graphics, sound, save, provenance, snippet,
and panic primitives. This page is the implementer-facing categorized
index for that full surface.
kn86-nosh-tb, the ADR-0027 termbox2 spike runtime, intentionally does
not expose this whole table. Its cartridge contract is the constrained
cell API plus five semantic callbacks documented in cell-api.md.
| Marker | Meaning |
|---|
| v1 | Stable, in ADR-0005 v1.0 enumeration. Cartridges may rely on signature and semantics. |
| v1+ | Stable, added by an accepted ADR after ADR-0005 v1.0 (e.g. ADR-0016’s emacs-extend-*). Treat as v1. |
| evolving | Specified but the signature or semantics may shift before launch. Cartridge authors should expect possible breaking changes. |
Type tags follow ADR-0005’s table — Bool, Number, String, Symbol, Cell (opaque), Bytes (byte array), Unit (returns nil).
| Host | Exposed surface | Use today |
|---|
kn86emu | Full NoshAPI listed below | SDL desktop emulator, launch-cart compatibility, full SystemState integration. |
kn86-nosh-tb | Constrained cell API and on-* callbacks | Termbox2/device spike, terminal-native carts, chrome-ownership validation. |
Do not assume a cart written for one host can run unchanged on the
other. The .kn86 container is shared, but the Fe symbols bound by the
host differ.
Primary 80×25 SDL text grid. Cartridges may not write Row 0 or Row 24
(firmware-owned). This section describes the full kn86emu surface; the
termbox2 path uses the 128×75 constrained cell API instead.
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
text-clear | () | Unit | Clear text framebuffer (does not affect bitmap area). | v1 |
text-putc | (col row char) | Unit | Place a single character at (col, row); ASCII 0–127. | v1 |
text-puts | (col row string) | Unit | Print null-terminated string at (col, row), left-aligned, clipped at grid boundary. | v1 |
text-printf | (col row fmt arg...) | Unit | Printf-style formatted text; supports %d %u %x %c %s; max 128 bytes. | v1 |
text-scroll | (lines) | Unit | Scroll text up (positive) or down (negative); range −25 to +25. | v1 |
text-cursor | (col row visible) | Unit | Show / hide text cursor at (col, row). | v1 |
text-invert | (col row len) | Unit | Invert text color for len characters; emphasis primitive. | v1 |
Per ADR-0014, the canvas is 960×600 logical pixels (the 1024×600 Elecrow has 32 px horizontal letterbox per side that cartridges never see). All bitmap primitives below are bounds-checked against 960×600.
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
gfx-clear | () | Unit | Clear bitmap framebuffer. | v1 |
gfx-pixel | (x y on) | Unit | Set / clear pixel; x 0–959, y 0–599. | v1 |
gfx-line | (x0 y0 x1 y1) | Unit | Bresenham line. | v1 |
gfx-rect | (x y w h filled) | Unit | Filled or outline rectangle. | v1 |
gfx-circle | (cx cy r) | Unit | Circle outline at center (cx, cy). | v1 |
gfx-blit | (x y bytes w h) | Unit | Blit packed 1-bpp bitmap (byte length = (w*h+7)/8). | v1 |
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
split-view | (bitmap-rows) | Unit | Set split-view: top bitmap-rows pixels are graphics, remainder text. | v1 |
display-mode | () | Symbol | Query current mode: :text, :graphics, or :split. | v1 |
YM2149 emulation via the runtime’s psg.c. Direct register access is provided for advanced use; most carts use the higher-level helpers.
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
psg-write | (reg val) | Unit | Write PSG register reg (0–14) with val (0–255). Direct hardware-style access. | v1 |
psg-read | (reg) | Number | Read current value of register. | v1 |
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
sound-tone | (channel freq vol) | Unit | Play tone on channel 0–2 at frequency (Hz) and volume 0–15. | v1 |
sound-noise | (period) | Unit | Set noise period (0–31). | v1 |
sound-envelope | (shape period) | Unit | Set envelope shape (0–15) and period. | v1 |
sound-silence | () | Unit | Mute all channels and noise. | v1 |
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
sfx-keyclick | () | Unit | Short click; key-confirmation feedback. | v1 |
sfx-boot | () | Unit | Ascending boot tones (~300 ms). | v1 |
sfx-select | () | Unit | Selection beep. | v1 |
sfx-confirm | () | Unit | Confirmation tone. | v1 |
sfx-error | () | Unit | Error buzz. | v1 |
sfx-alert | () | Unit | Alert ping. | v1 |
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
spawn-cell | (type-symbol) | Cell | Allocate cell of given type; type must be registered in cart init. | v1 |
destroy-cell | (cell) | Unit | Return cell to free pool; handle becomes invalid. | v1 |
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
drill-into | (cell) | Unit | Push cell onto nav stack; fires ON_EXIT then ON_ENTER. | v1 |
navigate-back | () | Unit | Pop nav stack; fires lifecycle handlers. | v1 |
current-cell | () | Cell | Top of nav stack, or nil if empty. | v1 |
set-root | (cell) | Unit | Reset nav stack to cell; usually called once at cart init. | v1 |
next-sibling | () | Unit | Move to next sibling in current list (no-op at last). | v1 |
prev-sibling | () | Unit | Move to previous sibling (no-op at first). | v1 |
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
list-push | (parent cell) | Unit | Append cell as child of parent. | v1 |
list-get | (parent index) | Cell | Get Nth child (0-indexed); nil if out of range. | v1 |
list-length | (parent) | Number | Count children. | v1 |
is-leaf | (cell) | Bool | True if cell has no children. | v1 |
link-cells | (parent child) | Unit | Establish parent/child relation; equivalent to list-push. | v1 |
unlink-cell | (cell) | Unit | Remove from parent + sibling chain; orphaned but not destroyed. | v1 |
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
lfsr-seed | (seed) | Unit | Initialize 32-bit LFSR. | v1 |
lfsr-next | () | Number | Advance LFSR; return next pseudo-random 32-bit value. | v1 |
lfsr-range | (min max) | Number | Pseudo-random integer in [min, max] inclusive. | v1 |
lfsr-shuffle | (array-length) | Unit | Fisher-Yates shuffle in-place. Per ADR-0005 §“Known Unknowns” #1, the Lisp surface for in-place array mutation is unresolved; treat as evolving. | evolving |
Cart-facing surface for the 2× trackpoint hardware committed in ../../../adr/ADR-0032-sweep-peripheral-commitment.md. Both physical trackpoints aggregate at the master KB2040 over QMK split-pointing-device transport and emit as one logical cursor — carts see the merged cursor in cell coordinates on the main 80×25 grid. Cursor renders on cartridge content rows (1–23) only; clamped at Row 0 / Row 24 boundaries per CLAUDE.md Spec Hygiene Rule 5; does not render on the CIPHER-LINE auxiliary OLED. Visibility defaults to visible; cart hides via the :pointer-hidden manifest flag (cart-load default) or cursor-visible! runtime FFI. Click semantics (tap-vs-drag threshold) are firmware-defined per ADR-0032 §4; right-click and middle-click are deferred to v0.2. Per-trackpoint differentiation (pointer-a / pointer-b) is a v2 question. See ../../../adr/ADR-0035-trackpoint-cart-ffi.md for the full decision record.
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
cursor-position | () | (col row) (2-element list) | Query current cursor cell on the main 80×25 grid; col 0–79, row 1–23. Logical position is independent of visibility — hidden cursor still has a position. O(1); never raises. | v1+ |
on-trackpoint-move | (handler) | Unit | Register handler invoked on cell-boundary crossing; handler signature (handler col row delta-col delta-row). One handler per cart; pass nil to unregister. Sustained against-boundary push does not re-fire the handler. | v1+ |
on-trackpoint-click | (handler) | Unit | Register handler invoked on click; handler signature (handler col row). v0.1 fires exactly when QMK emits a primary-button click — no button-index argument; right-click / middle-click deferred to v0.2. One handler per cart; pass nil to unregister. | v1+ |
cursor-visible! | (visible) | Unit | Show (#t) / hide (#f) the cursor for the lifetime of the cart load. Overrides the manifest :pointer-hidden default. Independent of position. | v1+ |
Available to all carts. Read-only on this tier; mutation requires mission context.
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
deck-state | () | Cell | Reference to Universal Deck State (record-style accessor). | v1 |
get-handle | () | String | Operator handle, max 16 chars. | v1 |
get-credits | () | Number | Current credit balance. | v1 |
get-reputation | () | Number | Current reputation points. | v1 |
has-capability | (bit-index) | Bool | Test cartridge-history capability bit (0–31). | v1 |
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
draw-threat-bar | (level max col row) | Unit | Draw level/max filled blocks; max 16 cells wide. | v1 |
draw-progress-bar | (pct col row width) | Unit | Draw percentage-filled bar; width 1–32 cells. | v1 |
draw-bordered-box | (col row w h title) | Unit | Box outline with optional centered title. | v1 |
Available only when a mission phase is active; calls outside mission context raise :not-in-mission.
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
phase-advance | () | Unit | Advance to next phase in chain; calls lifecycle handlers; auto-completes at last phase. | v1 |
mission-complete | () | Unit | Mark mission complete; award payout; pop mission context. | v1 |
award-credits | (amount) | Unit | Add (or subtract, if negative) credits to deck balance. | v1 |
modify-reputation | (delta) | Unit | Adjust reputation; clamped at [0, 32767]. | v1 |
set-phase-data | (key value) | Unit | Store key-value in current phase’s persistent data (survives phase boundaries within the mission). | v1 |
get-phase-data | (key) | Any | Retrieve phase data; nil if unset. | v1 |
set-capability | (bit-index) | Unit | Set cartridge-history bit; marks the module as completed. | v1 |
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
mission-input | () | Any | Input value provided by mission template. | v1 |
mission-context | () | Any | Local mission state (read-only). | v1 |
mission-deck-state | () | Any | Read-only deck-state snapshot (Tier 2 grant). | v1 |
current-mission-phase | () | Symbol | Phase metadata (Tier 2 grant). | v1 |
cartridge-data | (cart-id) | Any | Read from another loaded cartridge (Tier 2; requires :grants clause). | v1 |
pass | () | Unit | Acceptance contract: signal mission success. | v1 |
fail | (clause...) | Unit | Acceptance contract: signal failure with clause-level diagnostics. | v1 |
Per software/runtime/cipher-voice.md §11. CIPHER is OLED-exclusive (Spec Hygiene Rule 6). The Null cartridge has a sanctioned main-grid escape; no other cart may bypass.
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
cipher-emit | (mode-or-nil &optional :event ev) | Unit | Push a mode hint or forced silence into the next tick; optionally bind a synthetic event. | v1 |
cipher-push-event | (event-record) | Unit | Push event onto stream + memory store; runtime stamps :t and :tag. | v1 |
cipher-extend-grammar | (grammar-fragment) | Bool | Add a cipher-grammar block at runtime; only legal during :mission-brief / :bare-deck beats. | v1 |
cipher-set-mode-weights | (beat delta-list) | Unit | Temporary mode-weight bias scoped to current mission or cart unload. | v1 |
cipher-stack-head | (n) | List | Read top N entries of coherence stack. | v1 |
cipher-main-grid-escape | () | Unit | Null cartridge only — sanctioned exception to OLED-exclusive rule. Rejected for any other cart. | v1 |
aux-timer-start | (:tag :duration-ms :on-fire ev) | Number | Start runtime-managed countdown; expiry pushes event; max 600,000 ms. Returns timer handle. | v1 |
aux-timer-stop | (handle) | Bool | Cancel timer; #t if live, #f if already fired. | v1 |
aux-show-seed | (seed-bytes :label string) | Unit | Freeze CIPHER-LINE Row 1 to a captured seed; enter seed-capture mode. | v1 |
aux-status-render | (row content :priority pri) | Unit | Render to CIPHER-LINE Row 1 or Row 4; tickers if overflow; priority arbitrates concurrent writers. | v1 |
Cartridges contribute to nEmacs / REPL completion via two FFI primitives extending ADR-0009’s static ranking model:
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
emacs-extend-grammar | (sexp) | Unit | Cart contributes grammar productions for legal-form filtering in the predictive palette. | v1+ |
emacs-extend-vocabulary | (list-of-strings) | Unit | Cart contributes domain terms; each receives the +5 vocabulary boost in token ranking. | v1+ |
prompt-text | (prompt max-len) | String | Modal text-entry dialog (Nokia multi-tap on CIPHER-LINE Row 4); returns the entered string or nil on cancel. | v1+ |
Cart-issued clean self-halt. Routes through fe_error so it shares
the recovery flow with arena exhaustion and any other Fe-side abort.
The runtime captures a panic record, unloads the cart, posts a
“CART HALTED” notice on CIPHER-LINE Row 4, writes a log file, and
returns to bare-deck Terminal. See ../../runtime/panic-recovery.md.
| Lisp name | Signature | Returns | Semantics | Stability |
|---|
panic | (msg) | does not return | Halt this cart. msg is captured into the panic record’s reason field. | v1+ |
cart-panic | (msg) | does not return | Alias of panic; both bind to the same cfunc. | v1+ |
Per ADR-0005 §“Tier 3,” the player REPL exposes only the read-only subset of the above:
- Display:
display-mode.
- State:
get-handle, get-credits, get-reputation, has-capability, deck-state.
- Cells (inspect):
current-cell, list-get, list-length, is-leaf.
- Procedural:
lfsr-seed, lfsr-next, lfsr-range.
All mutating primitives — text-*, gfx-*, sound-*, spawn-cell, destroy-cell, drill-into, navigate-back, phase-advance, mission-complete, award-credits, modify-reputation, set-capability, cipher-push-event, aux-* — are forbidden in the REPL. Player REPL is for inspection and scripted automation, not for mutating mission state.
Per software/runtime/orchestration.md §“Graceful Degradation” — the runtime’s contract for partial-hardware failure. Cart authors read this table to know how each primitive behaves when a subsystem (coprocessor link, microSD, battery) is unavailable. The runtime never raises a cart-visible exception for hardware degradation; the doctrine is “show what you have, label what you don’t, never panic.” Carts that need explicit degradation handling subscribe to runtime events (e.g., low-battery-event); they do not wrap each FFI call.
| Primitive group | Subsystem | Degradation behavior |
|---|
psg-write, psg-read, sound-tone, sound-noise, sound-envelope, sound-silence, sfx-keyclick, sfx-boot, sfx-select, sfx-confirm, sfx-error, sfx-alert | Coprocessor link (audio path) | Silent no-op when the bridge is in KN86_LINK_DEGRADED. psg-read returns 0. Runtime renders a LINK? indicator on the firmware Row 0 status bar; cart sees no error. |
cipher-emit, aux-timer-start, aux-timer-stop, aux-show-seed, aux-status-render | Coprocessor link (OLED path) | Silent no-op for the render side. Runtime-side accounting continues: cipher-push-event still mutates the UDS event ring; aux-timer-start still returns a valid handle and the runtime still fires expiry, only the OLED render of the result is suppressed. |
cart_save, cart_load | Cart microSD filesystem | Return Bool per the existing signature. #f means the underlying write/read failed (cart pulled mid-write, corrupted filesystem, write-protected media). Already part of the FFI contract; degradation doctrine does not add new return shapes. |
deck-state, get-handle, get-credits, get-reputation, has-capability | Device microSD (UDS persistence) | Always succeed against the in-memory UDS struct. The runtime handles the persistence path; cart reads are always served. If device microSD becomes unreadable, runtime degrades persistence to session-only and surfaces a bare-deck status line — cart-facing reads are unchanged. |
| All primitives | Low battery | Runtime caps animation to 15 fps and dims CIPHER-LINE at the warning threshold; on the critical threshold, runtime pushes (low-battery-event :level :critical :remaining-pct N) to subscribed carts only, then begins clean shutdown. Carts that do not subscribe see no change in their event stream. |
| All primitives | Emulator window < 80×25 | Inapplicable. Emulator exits cleanly with a stderr message before reaching cart load. Device firmware cannot enter this state (fixed Elecrow 7” IPS framebuffer). |
For the full degradation-mode spec — triggers, runtime behaviors, recovery paths, and per-mode summary table — see software/runtime/orchestration.md §“Graceful Degradation”. For per-primitive C-side mechanics, see kn86-emulator/src/coproc.c (the vtable seam that drops audio/OLED calls when degraded).
The CIPHER-LINE event-record schema, the cart-format cipher-grammar block layout, and the Lisp ↔ C type marshalling table all live where they’re authoritative — not duplicated here. See:
- Event records, modes, grammar productions:
../../runtime/cipher-voice.md.
- Cart-format binary layout for grammar/vocabulary blocks:
adr/ADR-0006-cart-format-v2.md (filesystem-versioned).
- Type mapping (C ↔ Lisp): ADR-0005 §“Type Mapping.”
- Open questions (array mutation FFI, error trace depth, mission-context shape): ADR-0005 §“Known Unknowns / Follow-ups.”
- Hardware-degradation contracts (coprocessor offline, microSD absent, low battery, emulator window too small):
software/runtime/orchestration.md §“Graceful Degradation” and §“Degradation contracts” above. Panic recovery (cart-internal SIGSEGV / fe_error / (panic)) lives in software/runtime/panic-recovery.md and is distinct.