Skip to content

Multi-Resolution Support & Drop-Down REPL — Design

Update 2026-04-21: The display geometry side of this plan was finalized in ADR-0014. Logical framebuffer is 960×600 (80 cols × 12 px cell width, 25 rows × 24 px cell height), integer-scaled 1:1 onto the Elecrow 1024×600 with a 32 px horizontal letterbox per side. Refer to ADR-0014 for current canonical geometry; this plan document describes design intent and is otherwise unchanged.


Three forces converge on a single design:

  1. Resolution unified. The Elecrow 7” IPS display (1024×600) is now the standard for the Pi Zero 2 W prototype. It matches the target logical grid (80×25) and offers excellent visibility. The production device will use a smaller amber LCD with the same logical resolution.
  2. A missing dev affordance. The emulator has no way to inspect nOSh internals at runtime — cell pool state, deck state, phase chain, forced transitions. Every debug session requires print-statement archaeology. A Quake-style drop-down REPL would collapse this to seconds.
  3. A fiction-layer opportunity. A diagnostic shell on the KN-86 itself is strongly on-theme. Operators summoning op.* commands to interrogate the device fits the Deckline’s cyberpunk-utility aesthetic without breaking immersion.

Tackled together, these let us introduce a display-profile abstraction (for the new panel), establish a reserved overlay zone (for the REPL toast), and expand the key matrix by one key (for the TERM summon). All three changes share the same surface area in nOSh.


DecisionResolution
Scaling policyElecrow 1024×600 is the canonical frame; every target emulates that exact output. The logical canvas is integer-scaled inside the frame with a centered letterbox. Window oversize via --scale multiplies the whole composited frame by a whole number without changing the canvas or letterbox.
Canvas abstractionCartridges author to logical coords (char cells + logical pixels), never physical pixels. nOSh compositor maps logical → physical per profile.
REPL scopeBoth dev-mode (sys.* commands, build-flag gated) and fiction-mode (op.* commands, always available). Same UI, different command palettes.
REPL summonDedicated TERM key at matrix position (3,3), gray legend (firmware/system family). Instant toggle; single key.
Key matrix31 keys total. Original 30 + TERM. ENTER serves as = in REPL/arithmetic contexts.
Landscape vs portraitDSI profile orientation deferred to spike S2 (see §6).

A display_profile_t struct in nOSh describes the mapping from logical canvas to physical panel:

typedef struct {
const char *name; /* "prototype", "desktop_emu" */
uint16_t phys_w, phys_h; /* native panel pixels */
uint16_t logical_w, logical_h; /* canvas the compositor draws */
uint8_t cols, rows; /* char cells (8x8 font) */
uint8_t scale; /* integer scale factor (1, 2, 3, ...) */
int16_t viewport_x, viewport_y; /* top-left of viewport in phys coords */
chrome_layout_t chrome; /* status_bar, soft_keys, repl zones */
bool has_touch; /* capacitive digitizer present */
} display_profile_t;

All framebuffer writes route through a compositor that applies scale and viewport offset. Cartridges see logical_w × logical_h and never know the physical dimensions.

Physical frame and logical canvas values track CLAUDE.md’s Canonical Hardware Specification — see that table for authoritative dimensions. Each profile renders the same 1024×600 frame with the same centered letterbox; the desktop emulator exists to reproduce the prototype pixel-for-pixel on a developer workstation.

ProfileRoleChromeTouch
PROFILE_PROTOTYPEPi Zero 2 W + Elecrow 7” IPSnoneno
PROFILE_DESKTOP_EMUSDL window emulating the same framenoneno

Profiles declare zones as first-class rectangles:

  • viewport — cartridge draws here, logical coords
  • status_bar — nOSh-owned; clock, cartridge name, deck status
  • soft_keys — nOSh-owned; on-screen 31-key grid (DSI only)
  • repl_toast — reserved overlay; see §4
  • overlay — cartridge-requestable overlay panel (future)

Cartridges can only draw in viewport. All other zones are nOSh’s.

The .kn86 manifest gains a canvas_shapes field:

CART_MANIFEST {
.canvas_shapes = CANVAS_LANDSCAPE_80x25,
/* or | CANVAS_PORTRAIT_60x30 to declare portrait support */
};

nOSh refuses to load a cartridge into a profile whose canvas shape isn’t declared. Launch titles start as CANVAS_LANDSCAPE_80x25 only; portrait support is opt-in.

Physical keys, SDL scancodes, and capacitive touch hitboxes all produce identical logical events. Touch is just another source of KN86KeyCode — the DSI profile’s soft_keys chrome registers hitboxes and synthesizes keydown/keyup/hold events indistinguishable from the physical matrix.


  • Summon: TERM key, instant toggle.
  • Appearance: slides down from top of viewport over ~80ms. Occupies top 40% of viewport (or full viewport if held longer — TBD in spike).
  • Focus: while open, REPL captures all input and pauses cartridge’s clock. Cartridge framebuffer is preserved underneath; dismissal redraws it instantly (no reload).
  • Evaluation: ENTER evaluates current line. Results display in-line. History navigable with CDR/CONS (up/down).
  • Dismissal: TERM again, or BACK.
  • sys.* — dev build only, gated by KN86_DEV_BUILD. Inspect/mutate nOSh internals: sys.deck, sys.cells, sys.phase, sys.reload, sys.forcephase <n>, sys.dump.
  • op.* — always available, in-world operator commands. Diagnostic and utility: op.status, op.creds, op.rep, op.handle, op.cart. Must feel diegetic.

REPL is owned entirely by nOSh. Cartridges cannot intercept, extend, or suppress it. The repl_toast zone is never available to cartridge draw calls. This is firewall-level; the REPL is always trustworthy.


Single change: populate matrix position (3,3) with a TERM key. Gray legend, firmware family (like INFO and SYS). Matrix scan already reads this intersection — hardware impact is one switch and one keycap.

┌────────┬──────┬──────┬────────┐
│ QUOTE │ CONS │ NIL │ LAMBDA │
├────────┼──────┼──────┼────────┤
│ INFO │ CAR │ APPLY│ SYS │
├────────┼──────┼──────┼────────┤
│ LINK │ BACK │ CDR │ ATOM │
├────────┴──────┼──────┼────────┤
│ EVAL (2U) │ EQ │ TERM │ ← new key at (3,3)
└───────────────┴──────┴────────┘

ENTER (on the data grid) serves as = in arithmetic and REPL evaluate contexts. No separate = key needed.


Eight time-boxed experiments, sequenced across four weeks:

#SpikeQuestionTime
S1Profile compositor, emulator onlyInteger-scaled logical → arbitrary phys size, no font softening1d
S2Portrait canvas prototypeDoes a 60×30 re-layout of one launch title feel right?2d
S3Pi + DSI + touch bring-upTarget framerate + <50ms touch latency on Pi Zero 2 W2d (hw)
S4On-screen 31-key matrix as input sourceTouch hitboxes produce identical logical events to SDL keys1d post-S3
S5REPL toast overlay, dev-onlySlide-down terminal, sys.* command parser, inspect/mutate2d
S6REPL pause-vs-background behaviorDoes cartridge freeze or keep ticking?part of S5
S7Font hinting at non-integer scaleConfirms integer-only policy0.5d
S8Cartridge manifest canvas declaration.kn86 loader refuses/letterboxes mismatched canvas1d

Sequencing:

  • Week 1 (software): S1, S7, S8. Lands display profile abstraction on main.
  • Week 2 (dev velocity): S5, S6. REPL unlocks faster iteration for everything after.
  • Week 3 (UX decision): S2. Decides landscape vs portrait for DSI profile.
  • Week 4 (hardware): S3, S4. Requires panel + Pi in hand.

  • Fractional scaling (ever).
  • Touch gestures beyond tap-as-keypress (swipe, pinch, drag).
  • Cartridge-extensible REPL commands (REPL stays nOSh-owned).
  • Production hardware redesign for a second empty matrix slot at (3,1) — that position remains under the 2U EVAL cap.
  • A physical = key. ENTER suffices.

  • Both profiles render the same launch cartridge identically at the logical canvas level.
  • TERM key summons REPL in <100ms on all targets.
  • REPL dismissal restores cartridge framebuffer with no visible reflow.
  • DSI dev unit accepts both SDL keyboard (when on desktop) and touch input (when on Pi), producing indistinguishable event streams.
  • No cartridge changes required for existing launch titles beyond adding the manifest canvas declaration.