Skip to content

Key Test & Remap UI

The Key Test UI enables operators to:

  1. Verify all 30 keys respond correctly — visual feedback as keys are pressed
  2. Remap physical keys to KN-86 functions — live configuration without file editing
  3. Persist custom keymaps — runtime changes saved to .keymap config file

Entry point: Operator holds SYS for 2 seconds from the SYS tab → Key Test Mode Exit: BACK returns to SYS tab / mission board


  • Resolution: 80 columns × 25 rows monospace text
  • Usable content: Rows 0–21 (Row 22 = action bar, Row 23 = reserved, Row 24 = firmware status)
  • Color: Amber (#E6A020) on black (#000000)
  • Box drawing: ┌ ┬ ┐ ├ ┼ ┤ └ ┴ ┘ ─ │
  • Highlight: Inverse video ([>name<]) or bracket markers ([name])

Display all 30 keys in a visual map matching the physical hardware layout. When operator presses any key, that key highlights, and a detail line shows:

  • KN-86 key name
  • SDL scancode (current mapping)
  • Visual confirmation
ROW 0: KEY TEST MODE ─────────────────────── ALL KEYS ─────────────────────
ROW 1:
ROW 2: Left Hand (Function Grid, 14 keys) Right Hand (Data Grid, 16 keys)
ROW 3: ┌────────┬────────┬────────┬────────┐ ┌────────┬────────┬────────┬────────┐
ROW 4: │QUOTE │CONS │NIL │LAMBDA │ │PAD_1 │PAD_2 │PAD_3 │PAD_DIV │
ROW 5: │ Q │ W │ E │ R │ │ KP_1 │ KP_2 │ KP_3 │ KP_/ │
ROW 6: ├────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┤
ROW 7: │INFO │CAR │APPLY │SYS │ │PAD_4 │PAD_5 │PAD_6 │PAD_MUL │
ROW 8: │ A │ S │ D │ F │ │ KP_4 │ KP_5 │ KP_6 │ KP_* │
ROW 9: ├────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┤
ROW 10: │LINK │BACK │CDR │ATOM │ │PAD_7 │PAD_8 │PAD_9 │PAD_SUB │
ROW 11: │ Z │ X │ C │ V │ │ KP_7 │ KP_8 │ KP_9 │ KP_- │
ROW 12: ├────────┴────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┤
ROW 13: │EVAL (3U) │EQ │empty │ │PAD_DOT │PAD_0 │PAD_ENT │PAD_ADD │
ROW 14: │ 1 │ 2 │ │ │ KP_. │ KP_0 │ KP_RET │ KP_+ │
ROW 15: └─────────────────┴────────┴────────┘ └────────┴────────┴────────┴────────┘
ROW 16:
ROW 17: Last Pressed: ────────────────────────────────────────────────────────────
ROW 18:
ROW 19: Key Name..............................KN86_KEY_CAR (5)
ROW 20: SDL Scancode..........................SDL_SCANCODE_S (22)
ROW 21: Status...............................READY

When operator presses CAR (physical key S), that cell in the grid inverts:

ROW 8: │INFO │[>CAR<] │APPLY │SYS │
ROW 9: │ A │[>S <] │ D │ F │

And the detail section updates:

ROW 19: Key Name..............................KN86_KEY_CAR (5)
ROW 20: SDL Scancode..........................SDL_SCANCODE_S (22)
ROW 21: Status...............................✓ OK

Each key occupies a 9-char-wide cell (80 cols ÷ 8 cells ≈ 10, minus borders = 9):

┌────────┐ ← 9 chars wide
│NAME │ ← Row 0: KN-86 name (8 chars, left-aligned)
│ SDLKEY │ ← Row 1: SDL scancode (8 chars, left-aligned, space-padded)
└────────┘

Normal:

│CONS │
│ W │

Highlighted (inverse video or bracket marker):

│[>CONS<]│
│[> W <]│

Operator presses EVAL in Key Test Mode → enters Remap Mode. Cycles through KN-86 keys, selects each for remapping, reassigns SDL scancode.

ROW 0: REMAP MODE ──────────────────────── SELECT KEY TO REMAP ───────────────
ROW 1:
ROW 2: Left Hand (Function Grid, 14 keys) Right Hand (Data Grid, 16 keys)
ROW 3: ┌────────┬────────┬────────┬────────┐ ┌────────┬────────┬────────┬────────┐
ROW 4: │QUOTE │*CONS* │NIL │LAMBDA │ │PAD_1 │PAD_2 │PAD_3 │PAD_DIV │
ROW 5: │ Q │ W │ E │ R │ │ KP_1 │ KP_2 │ KP_3 │ KP_/ │
ROW 6: ├────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┤
ROW 7: │INFO │CAR │APPLY │SYS │ │PAD_4 │PAD_5 │PAD_6 │PAD_MUL │
ROW 8: │ A │ S │ D │ F │ │ KP_4 │ KP_5 │ KP_6 │ KP_* │
ROW 9: ├────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┤
ROW 10: │LINK │BACK │CDR │ATOM │ │PAD_7 │PAD_8 │PAD_9 │PAD_SUB │
ROW 11: │ Z │ X │ C │ V │ │ KP_7 │ KP_8 │ KP_9 │ KP_- │
ROW 12: ├────────┴────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┤
ROW 13: │EVAL (3U) │EQ │empty │ │PAD_DOT │PAD_0 │PAD_ENT │PAD_ADD │
ROW 14: │ 1 │ 2 │ │ │ KP_. │ KP_0 │ KP_RET │ KP_+ │
ROW 15: └─────────────────┴────────┴────────┘ └────────┴────────┴────────┴────────┘
ROW 16:
ROW 17: Currently Selected: *CONS* (KN86_KEY_CONS)
ROW 18:
ROW 19: Current Mapping.......................W (SDL_SCANCODE_W)
ROW 20: Press a key to remap, or NIL to cancel.....................[*] unsaved
ROW 21:

Pressing CDR in remap mode cycles to the next KN-86 key in sequence (0 → 1 → 2 … → 29 → 0). The currently selected key shows an asterisk marker (*KEY*):

Step 1: CONS selected

ROW 4: │QUOTE │*CONS* │NIL │LAMBDA │
ROW 17: Currently Selected: *CONS* (KN86_KEY_CONS = 1)

Step 2: CDR pressed → NIL selected

ROW 4: │QUOTE │CONS │*NIL* │LAMBDA │
ROW 17: Currently Selected: *NIL* (KN86_KEY_NIL = 2)

When operator presses CAR on a key in Remap Mode → enter “Remap This Key” submode. Prompt appears: PRESS NEW KEY FOR [KN-86 NAME] Operator presses physical key → mapping updates, returns to Remap Mode.

ROW 0: REMAP MODE ───────────────────────── WAITING FOR NEW KEY ──────────────
ROW 1:
ROW 2: Left Hand (Function Grid, 14 keys) Right Hand (Data Grid, 16 keys)
ROW 3: ┌────────┬────────┬────────┬────────┐ ┌────────┬────────┬────────┬────────┐
ROW 4: │QUOTE │*CONS* │NIL │LAMBDA │ │PAD_1 │PAD_2 │PAD_3 │PAD_DIV │
ROW 5: │ ? │ ? │ E │ R │ │ KP_1 │ KP_2 │ KP_3 │ KP_/ │
ROW 6: ├────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┤
ROW 7: │INFO │CAR │APPLY │SYS │ │PAD_4 │PAD_5 │PAD_6 │PAD_MUL │
ROW 8: │ A │ S │ D │ F │ │ KP_4 │ KP_5 │ KP_6 │ KP_* │
ROW 9: ├────────┼────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┤
ROW 10: │LINK │BACK │CDR │ATOM │ │PAD_7 │PAD_8 │PAD_9 │PAD_SUB │
ROW 11: │ Z │ X │ C │ V │ │ KP_7 │ KP_8 │ KP_9 │ KP_- │
ROW 12: ├────────┴────────┼────────┼────────┤ ├────────┼────────┼────────┼────────┤
ROW 13: │EVAL (3U) │EQ │empty │ │PAD_DOT │PAD_0 │PAD_ENT │PAD_ADD │
ROW 14: │ 1 │ 2 │ │ │ KP_. │ KP_0 │ KP_RET │ KP_+ │
ROW 15: └─────────────────┴────────┴────────┘ └────────┴────────┴────────┴────────┘
ROW 16:
ROW 17: PRESS NEW KEY FOR [CONS]
ROW 18:
ROW 19: Old mapping.............................W (SDL_SCANCODE_W)
ROW 20: New mapping.............................? (listening...)
ROW 21:

Operator presses E:

ROW 17: PRESS NEW KEY FOR [CONS]
ROW 19: Old mapping.............................W (SDL_SCANCODE_W)
ROW 20: New mapping.............................E (SDL_SCANCODE_E)
ROW 21: ✓ Mapped! CDR to continue remapping, BACK to discard.

Then returns to Remap Mode:

ROW 4: │QUOTE │*CONS* │NIL │LAMBDA │
ROW 5: │ Q │ E │ E │ R │ ← CONS now mapped to E (duplicate!)

If operator tries to map two KN-86 keys to the same SDL scancode, show warning:

ROW 20: New mapping.............................S (SDL_SCANCODE_S)
ROW 21: ⚠ WARNING: S already assigned to CAR. EVAL=confirm override, BACK=cancel.

When operator presses EVAL in Remap Mode with unsaved changes → enter Save Confirm. Display: SAVE? EVAL=YES BACK=NO

ROW 0: SAVE KEYMAP CONFIGURATION ────────────────────────────────────────────
ROW 1:
ROW 2: Your keymap has been modified. Unsaved changes:
ROW 3:
ROW 4: Key.............................Old Mapping → New Mapping
ROW 5: CONS.............................W → E
ROW 6: EQ..............................2 → BACKSPACE
ROW 7:
ROW 8: Press EVAL to save, BACK to discard changes.
ROW 9:
ROW 10:
ROW 11:
ROW 12:
ROW 13:
ROW 14:
ROW 15:
ROW 16:
ROW 17:
ROW 18:
ROW 19:
ROW 20:
ROW 21:

Keymap written to ~/.kn86/default.keymap:

ROW 0: SAVE KEYMAP CONFIGURATION ────────────────────────────────────────────
ROW 1:
ROW 2: ✓ Keymap saved to ~/.kn86/default.keymap
ROW 3:
ROW 4: Changes will take effect on next boot.
ROW 5:
ROW 6: Press any key to continue.
ROW 7:
ROW 8:
...

Then returns to SYS menu after key press.


┌─────────────────────────────────────────────────────────────────┐
│ SYS TAB (Mission Board) │
│ Hold SYS for 2 seconds │
└────────────────────────┬────────────────────────────────────────┘
┌──────────────────────┐
│ KEY TEST MODE │
│ (Just Looking) │
│ Press keys to see │
│ mappings │
└──────────────────────┘
│ ▲
│ EVAL │ BACK → SYS Tab
│ │
▼ │
┌──────────────────────┐
│ REMAP MODE │
│ Select key w/ CDR │
│ Press CAR to remap │
└──────────────────────┘
│ ▲
│ CAR │ BACK → Key Test
│ │
▼ │
┌──────────────────────┐
│ REMAP CONFIRM │
│ PRESS NEW KEY FOR X │
│ NIL to cancel │
└──────────────────────┘
│ ▲
key │ │ NIL / timeout
pressed │ │
▼ │
┌──────────────────────┐
│ MAP UPDATED │
│ Press any key │
└──────────────────────┘
│ key
│ pressed
(return to Remap Mode)
┌──────────────────────┐
│ Unsaved changes? │
└──────────────────────┘
│ │
no │ │ yes
│ │
│ ▼
│ ┌──────────────────────┐
│ │ SAVE CONFIRM │
│ │ EVAL=YES BACK=NO │
│ └──────────────────────┘
│ │ │
│ EVAL │ │ BACK
│ BACK │ │
│ ▼ ▼
│ ┌─────────────────────┐
│ │ KEYMAP SAVED or │
│ │ CHANGES DISCARDED │
│ └─────────────────────┘
│ │
└────────┤
┌──────────────────────┐
│ Return to SYS Tab │
│ (or Key Test) │
└──────────────────────┘
StateInputActionNext State
Key TestEVALEnter remap modeRemap Mode
Key TestBACKReturn to SYS tabSYS Tab
Key TestAny keyHighlight, show detailKey Test
RemapCDRCycle to next KN-86 keyRemap (next key highlighted)
RemapCAREnter remap-this-key submodeRemap Confirm
RemapEVALCheck for unsaved changesSave Confirm (if changes) or SYS Tab (if no changes)
RemapBACKReturn to Key TestKey Test
Remap ConfirmAny key (except NIL)Update mapping, show successRemap Mode
Remap ConfirmNILCancel remap, returnRemap Mode
Remap Confirmtimeout (3 sec)Cancel remap, timeout beepRemap Mode
Save ConfirmEVALWrite keymap to fileSaved confirmation
Save ConfirmBACKDiscard changesRemap Mode
Saved ConfirmationAny keyReturn to SYS tabSYS Tab

Row 22: EVAL=remap CDR=skip BACK=exit CAR=info INFO=hold-status

Format: KEYNAME=action separated by 2 spaces. Right-align optional status.

Row 22: CDR=next CAR=remap EVAL=save BACK=cancel NIL=clear
Row 22: PRESS ANY KEY (ESC/NIL to cancel)
Row 22: EVAL=save BACK=discard

When in Remap Mode with unsaved changes, show [*] in top-right corner:

ROW 0: REMAP MODE ────────────────────────────── SELECT KEY [*] ──────────────
(asterisk = modified)

And in the detail line:

ROW 20: Press a key to remap, or NIL to cancel.....................[*] unsaved

In Key Test Mode detail section:

ROW 21: Status...............................✓ OK (key responded)
ROW 21: Status...............................⚠ CONFLICT (duplicate mapping)
ROW 21: Status...............................✗ TIMEOUT (key not received)

Option A: Inverse Video (Preferred for amber-on-black)

Selected cell shows inverse: black text on amber background

Option B: Bracket Markers (If inverse not available)

│[>CONS<]│ ← brackets around highlighted cell
│[> W <]│

Option C: Asterisk Markers (Minimal overhead)

│*CONS* │ ← asterisks for "selected for remap"
│ W │

EventSoundDuration
Key pressed (detected)800 Hz tone50ms
Remap confirmedAscending tone 800→1200 Hz150ms
Conflict detectedError buzz (noise)100ms
Keymap savedSuccess chime (3-tone)200ms
Timeout or cancelDescending tone 1200→600 Hz100ms

  • Location: SystemState.input_state.sdl_to_kn86[512]
  • Type: Array of KN86KeyCode (one entry per SDL scancode)
  • Populated at boot: Via keymap_load() from ~/.kn86/default.keymap (or hardcoded defaults)

When operator presses EVAL in Save Confirm submode:

  1. Call keymap_save(path, sdl_to_kn86_array, 512) (from ADR-001)
  2. Write INI-style .keymap config to ~/.kn86/default.keymap
  3. Display confirmation: ”✓ Keymap saved”
  4. Return to SYS tab (does not reload firmware — changes take effect on next boot)

  • SYS: Firmware-reserved for boot menu access. Attempting to remap shows: ⚠ SYS is reserved — cannot remap
  • EVAL: In some phases (missions), may be reserved for commit action. Warning shown contextually.

If operator attempts to map a KN-86 key to a nonexistent SDL scancode:

ROW 20: New mapping.............................? (invalid scancode)
ROW 21: ⚠ INVALID KEY. Try a different key.

  • Entry: main.c — On SYS hold for 2 sec from SYS tab, set state.mode = BD_MODE_SYS_KEY_TEST
  • Rendering: New function render_key_test_mode() in display.c
  • Input handling: New function handle_key_test_input() in main.c
  • Keymap operations: Use keymap.c functions from ADR-001
    • keymap_load(), keymap_save()
    • keymap_scancode_name(), keymap_kn86key_name()
typedef struct {
int current_key_index; /* 0-29 for cycle in Remap Mode */
bool is_remapping; /* In Remap Confirm submode */
int remap_key_index; /* Which KN-86 key being remapped */
bool has_unsaved_changes; /* Keymap modified since load */
uint32_t last_key_pressed; /* Last physical key timestamp */
KN86KeyCode last_key; /* Last key for highlighting */
} KeyTestState;
For each KN-86 key (0-29):
- Determine grid position (left or right, row/col)
- Look up current SDL mapping: sdl_to_kn86_reverse[kn86_key]
- Look up SDL scancode name: mapkey_scancode_name(sdl_scancode)
- Draw cell: KN86 name on top, SDL name below
- If key == last_key_pressed: inverse video / highlight

ModeRow 0 TitleRows 3–15Rows 17–21 DetailRow 22 Action Bar
Key Test”KEY TEST MODE — ALL KEYS”Grid with all 30 keysLast pressed: key name, SDL scancode, statusEVAL=remap CAR=info BACK=exit
Remap”REMAP MODE — SELECT KEY TO REMAP”Grid with *selected* key highlightedCurrently selected: KN-86 name; Current mapping; [*] unsavedCDR=next CAR=remap EVAL=save BACK=cancel
Remap Confirm”REMAP MODE — WAITING FOR NEW KEY”Grid (keys to remap show ?)“PRESS NEW KEY FOR [NAME]”; Old/New mapping”PRESS ANY KEY (NIL/ESC to cancel)“
Save Confirm”SAVE KEYMAP CONFIGURATION”List of changed keys”OLD→NEW” mappings; instructionsEVAL=save BACK=discard

  1. Physical grid mirrors hardware: Left/right split matches operator’s hands and the actual device topology
  2. Real-time feedback: Highlighting on key press provides immediate confidence
  3. Minimal text: ASCII grid reduces clutter, leaves space for detail lines
  4. Cycling via CDR: Aligns with existing firmware interaction model (CDR = traverse siblings)
  5. Unsaved marker: [*] convention is standard across desktop UIs; amber-on-black reads clearly
  6. State machine clarity: Separate submodes (Confirm, Save) isolate irreversible actions
  • All actions via key press (no timing requirements)
  • Large text (8×8 monospace, 80×25 grid)
  • Audio feedback for all state transitions
  • “Press any key to continue” after errors or saves