Constrained Cell API
The nOSh runtime exposes a small KEC Lisp draw surface for terminal-native
cartridges, bound by cell_api.c and cell_api_bind.c over the native
framebuffer renderer. The C runtime owns renderer setup and teardown, input
modes, cursor state, runtime chrome, and event polling; a cart holds none of
that authority.
engineer maintaining cell_api.c.
Related:
../../../adr/ADR-0027-termbox2-display-layer.md— the decision record that defined this constrained tier, the chrome reservation, and the half-block canvas.../../../adr/ADR-0036-native-framebuffer-renderer.md— the native framebuffer renderer supersedes ADR-0027’s display layer. The cell tier’s backend moved from termbox2 torender.cand the cart-facing contract below is preserved verbatim. Thekn86-nosh-tbspike runtime was retired in the same re-flow.primitives-by-category.md— the full NoshAPI surface.../../runtime/input-dispatch.md— runtime-owned input classification.
Availability
Section titled “Availability”A cart declares (cart-manifest :render-surface :cell) to select the cell
backbuffer before cart-init. The cart-manifest macro resolves the
declaration during KEC Lisp evaluation. The loader reads the resulting host
binding and selects the cell renderer for the cart load.
| Consumer | Cart route | Frame path |
|---|---|---|
kn86emu host loop, desktop and device | Cell manifest selects CELL_RENDER_SURFACE. | nosh_host_step() calls cell_rasterize() before the host presents render_surface(). |
kn86rec cart capture (--cart and --cart-dispatch) | Cell manifest selects CELL_RENDER_SURFACE. | recorder_rasterize() calls the same cell_rasterize() function. |
ctest | Unit tests select and inspect the cell surface. | test_cell_api.c checks cell_peek_*; host and recorder tests check raster output. |
present() and yield() remain advisory calls in
cell_api.c. The host controls event
polling and frame timing. A cart without a render-surface manifest uses the
text-view route. snake.lsp and icebreaker.lsp declare the cell surface;
neongrid.lsp draws through text-puts on the text view.
Host Contract
Section titled “Host Contract”The cart tier draws on the 128×75 cell grid, the 1× cell ceiling of the canonical surface (Canonical Hardware Specification):
| Region | Rows | Owner |
|---|---|---|
| Status chrome | 0 | nOSh runtime |
| Cart/surface region | 1..73 | active cart or runtime surface |
| Action chrome | 74 | nOSh runtime |
Cartridge writes to chrome rows are dropped and counted by the runtime. Out-of-range columns are clipped. Lisp code uses color tokens; the backend’s own attribute encoding is never exposed. One foreground phosphor is active per session and the operator picks it (ADR-0036); a cart reads the active scheme rather than naming a color.
Draw Primitives
Section titled “Draw Primitives”| Lisp name | Signature | Returns | Semantics |
|---|---|---|---|
cell-set | (col row ch fg bg) | Unit | Write one cell. Rows 0 and 74 are rejected as chrome writes. |
cell-print | (col row fg bg string) | Unit | Print a string, clipped at the right edge with no wrap. |
cell-clear-cart-region | () | Unit | Clear rows 1..73; chrome rows are untouched. |
cell-cols | () | Number | Return 128. |
cell-rows-usable | () | Number | Return 73. |
half-block-set | (x y on) | Unit | Set/clear a sub-cell pixel inside the 128×146 cart canvas. |
half-block-rect | (x y w h on) | Unit | Draw a filled rectangle in half-block coordinates, clipped to bounds. |
half-block-line | (x0 y0 x1 y1 on) | Unit | Draw a line in half-block coordinates, clipped to bounds. |
half-block-clear | () | Unit | Clear the cart half-block canvas. |
present | () | Unit | Advisory frame-ready marker; the runtime may also present at frame boundaries. |
yield | () | Unit | Advisory yield point; the runtime owns actual event polling. |
Not exposed to Fe: renderer setup and teardown, whole-surface clears, backend input and output modes, cursor show/hide, raw event polling and peeking, cell inspection helpers, and unchecked chrome writes.
Semantic Callbacks
Section titled “Semantic Callbacks”Cartridges define callbacks as ordinary Fe bindings. The runtime
pre-binds each name to nil, so an omitted callback is a silent drop.
| Callback | Payload | Semantics |
|---|---|---|
on-key | (key event-type timestamp-ms) | Classified event from the 34-key board (ADR-0031, three-layer model per ADR-0062). key and event-type are numeric enum values from the runtime. |
on-multi-tap | (result committed-char timestamp-ms) | Nokia multi-tap result and current/committed character. |
on-hold | (key timestamp-ms) | Key held past the runtime hold threshold. |
on-resize | (width height) | Terminal resize notification. Mostly desktop/debug; device geometry is fixed. |
on-tick | (timestamp-ms) | Periodic runtime tick. Carts should gate expensive redraws themselves. |
Input policy remains runtime-owned. TERM context dispatch, hold timing, double-tap windows, Nokia multi-tap, global chords, and hidden recovery gestures are handled in C before cart callbacks run.
Authoring Notes
Section titled “Authoring Notes”The constrained tier matches the hardware’s cell grid and chrome reservation.
Declare (cart-manifest :render-surface :cell) for a cart that paints cells
or half blocks. kn86emu and kn86rec rasterize that surface through the
shared cell framebuffer route. The full NoshAPI in
primitives-by-category.md remains the
compatibility surface for launch carts.
Migration notes
Section titled “Migration notes”- Cell renderer, 2026-08-30.
cell_rasterize()maps glyphs, soft glyphs, and half blocks torender_surface(). A manifest-selected cart uses the route inkn86emuandkn86rec.present()andyield()leave host scheduling unchanged. - Staleness sweep, 2026-08-29. Re-pointed from the retired
kn86-nosh-tbtermbox2 spike at the shippedcell_api.c/cell_api_bind.ctier over the native framebuffer renderer (ADR-0036). Thetb_*not-exposed list replaced with the renderer and host operations it stands for. The amber-only palette claim replaced with the ADR-0036 phosphor roster.on-keyre-stated against the 34-key board.half-block-lineadded to the primitive table, matchingnosh_cell_api_bind.