Skip to content

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:


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.

ConsumerCart routeFrame path
kn86emu host loop, desktop and deviceCell 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.
ctestUnit 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.


The cart tier draws on the 128×75 cell grid, the 1× cell ceiling of the canonical surface (Canonical Hardware Specification):

RegionRowsOwner
Status chrome0nOSh runtime
Cart/surface region1..73active cart or runtime surface
Action chrome74nOSh 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.


Lisp nameSignatureReturnsSemantics
cell-set(col row ch fg bg)UnitWrite one cell. Rows 0 and 74 are rejected as chrome writes.
cell-print(col row fg bg string)UnitPrint a string, clipped at the right edge with no wrap.
cell-clear-cart-region()UnitClear rows 1..73; chrome rows are untouched.
cell-cols()NumberReturn 128.
cell-rows-usable()NumberReturn 73.
half-block-set(x y on)UnitSet/clear a sub-cell pixel inside the 128×146 cart canvas.
half-block-rect(x y w h on)UnitDraw a filled rectangle in half-block coordinates, clipped to bounds.
half-block-line(x0 y0 x1 y1 on)UnitDraw a line in half-block coordinates, clipped to bounds.
half-block-clear()UnitClear the cart half-block canvas.
present()UnitAdvisory frame-ready marker; the runtime may also present at frame boundaries.
yield()UnitAdvisory 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.


Cartridges define callbacks as ordinary Fe bindings. The runtime pre-binds each name to nil, so an omitted callback is a silent drop.

CallbackPayloadSemantics
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.


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.


  • Cell renderer, 2026-08-30. cell_rasterize() maps glyphs, soft glyphs, and half blocks to render_surface(). A manifest-selected cart uses the route in kn86emu and kn86rec. present() and yield() leave host scheduling unchanged.
  • Staleness sweep, 2026-08-29. Re-pointed from the retired kn86-nosh-tb termbox2 spike at the shipped cell_api.c / cell_api_bind.c tier over the native framebuffer renderer (ADR-0036). The tb_* 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-key re-stated against the 34-key board. half-block-line added to the primitive table, matching nosh_cell_api_bind.