KN-86 Key Test & Remap UI Design
Overview
Section titled “Overview”The Key Test UI enables operators to:
- Verify all 30 keys respond correctly — visual feedback as keys are pressed
- Remap physical keys to KN-86 functions — live configuration without file editing
- Persist custom keymaps — runtime changes saved to
.keymapconfig file
Entry point: Operator holds SYS for 2 seconds from the SYS tab → Key Test Mode Exit: BACK returns to SYS tab / mission board
Display Constraints
Section titled “Display Constraints”- 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])
1. Key Test Mode Wireframe (Just Looking)
Section titled “1. Key Test Mode Wireframe (Just Looking)”Purpose
Section titled “Purpose”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
Layout (22 rows of content)
Section titled “Layout (22 rows of content)”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...............................READYHighlighted Key Example
Section titled “Highlighted Key Example”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...............................✓ OKGrid Cell Format
Section titled “Grid Cell Format”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 <]│2. Remap Mode Wireframe
Section titled “2. Remap Mode Wireframe”Purpose
Section titled “Purpose”Operator presses EVAL in Key Test Mode → enters Remap Mode. Cycles through KN-86 keys, selects each for remapping, reassigns SDL scancode.
Layout
Section titled “Layout”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.....................[*] unsavedROW 21:Cycling with CDR (Next)
Section titled “Cycling with CDR (Next)”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)3. Remap Confirmation Submode
Section titled “3. Remap Confirmation Submode”Purpose
Section titled “Purpose”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.
Layout
Section titled “Layout”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:Key Pressed → Mapping Updated
Section titled “Key Pressed → Mapping Updated”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!)Conflict Detection
Section titled “Conflict Detection”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.4. Save Confirmation Submode
Section titled “4. Save Confirmation Submode”Purpose
Section titled “Purpose”When operator presses EVAL in Remap Mode with unsaved changes → enter Save Confirm.
Display: SAVE? EVAL=YES BACK=NO
Layout
Section titled “Layout”ROW 0: SAVE KEYMAP CONFIGURATION ────────────────────────────────────────────ROW 1:ROW 2: Your keymap has been modified. Unsaved changes:ROW 3:ROW 4: Key.............................Old Mapping → New MappingROW 5: CONS.............................W → EROW 6: EQ..............................2 → BACKSPACEROW 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:After EVAL → Save
Section titled “After EVAL → Save”Keymap written to ~/.kn86/default.keymap:
ROW 0: SAVE KEYMAP CONFIGURATION ────────────────────────────────────────────ROW 1:ROW 2: ✓ Keymap saved to ~/.kn86/default.keymapROW 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.
5. State Machine
Section titled “5. State Machine”┌─────────────────────────────────────────────────────────────────┐│ 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) │ └──────────────────────┘Transition Rules
Section titled “Transition Rules”| State | Input | Action | Next State |
|---|---|---|---|
| Key Test | EVAL | Enter remap mode | Remap Mode |
| Key Test | BACK | Return to SYS tab | SYS Tab |
| Key Test | Any key | Highlight, show detail | Key Test |
| Remap | CDR | Cycle to next KN-86 key | Remap (next key highlighted) |
| Remap | CAR | Enter remap-this-key submode | Remap Confirm |
| Remap | EVAL | Check for unsaved changes | Save Confirm (if changes) or SYS Tab (if no changes) |
| Remap | BACK | Return to Key Test | Key Test |
| Remap Confirm | Any key (except NIL) | Update mapping, show success | Remap Mode |
| Remap Confirm | NIL | Cancel remap, return | Remap Mode |
| Remap Confirm | timeout (3 sec) | Cancel remap, timeout beep | Remap Mode |
| Save Confirm | EVAL | Write keymap to file | Saved confirmation |
| Save Confirm | BACK | Discard changes | Remap Mode |
| Saved Confirmation | Any key | Return to SYS tab | SYS Tab |
6. Action Bar (Row 22)
Section titled “6. Action Bar (Row 22)”Key Test Mode
Section titled “Key Test Mode”Row 22: EVAL=remap CDR=skip BACK=exit CAR=info INFO=hold-statusFormat: KEYNAME=action separated by 2 spaces. Right-align optional status.
Remap Mode
Section titled “Remap Mode”Row 22: CDR=next CAR=remap EVAL=save BACK=cancel NIL=clearRemap Confirm
Section titled “Remap Confirm”Row 22: PRESS ANY KEY (ESC/NIL to cancel)Save Confirm
Section titled “Save Confirm”Row 22: EVAL=save BACK=discard7. Visual Conventions
Section titled “7. Visual Conventions”Unsaved Changes Marker
Section titled “Unsaved Changes Marker”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.....................[*] unsavedStatus Indicators
Section titled “Status Indicators”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)Highlighting Strategies
Section titled “Highlighting Strategies”Option A: Inverse Video (Preferred for amber-on-black)
Selected cell shows inverse: black text on amber backgroundOption 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 │8. Audio Feedback (PSG)
Section titled “8. Audio Feedback (PSG)”| Event | Sound | Duration |
|---|---|---|
| Key pressed (detected) | 800 Hz tone | 50ms |
| Remap confirmed | Ascending tone 800→1200 Hz | 150ms |
| Conflict detected | Error buzz (noise) | 100ms |
| Keymap saved | Success chime (3-tone) | 200ms |
| Timeout or cancel | Descending tone 1200→600 Hz | 100ms |
9. Data Persistence
Section titled “9. Data Persistence”Runtime Keymap State
Section titled “Runtime Keymap State”- 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)
Saving Changes
Section titled “Saving Changes”When operator presses EVAL in Save Confirm submode:
- Call
keymap_save(path, sdl_to_kn86_array, 512)(from ADR-001) - Write INI-style
.keymapconfig to~/.kn86/default.keymap - Display confirmation: ”✓ Keymap saved”
- Return to SYS tab (does not reload firmware — changes take effect on next boot)
10. Unsupported Remapping
Section titled “10. Unsupported Remapping”Keys That Cannot Be Remapped
Section titled “Keys That Cannot Be Remapped”- 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.
Handling Unmapped Keys
Section titled “Handling Unmapped Keys”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.11. Implementation Notes
Section titled “11. Implementation Notes”Code Locations
Section titled “Code Locations”- Entry:
main.c— On SYS hold for 2 sec from SYS tab, setstate.mode = BD_MODE_SYS_KEY_TEST - Rendering: New function
render_key_test_mode()indisplay.c - Input handling: New function
handle_key_test_input()inmain.c - Keymap operations: Use
keymap.cfunctions from ADR-001keymap_load(),keymap_save()keymap_scancode_name(),keymap_kn86key_name()
State Management
Section titled “State Management”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;Grid Rendering Algorithm
Section titled “Grid Rendering Algorithm”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 / highlight12. Wireframe Summary Table
Section titled “12. Wireframe Summary Table”| Mode | Row 0 Title | Rows 3–15 | Rows 17–21 Detail | Row 22 Action Bar |
|---|---|---|---|---|
| Key Test | ”KEY TEST MODE — ALL KEYS” | Grid with all 30 keys | Last pressed: key name, SDL scancode, status | EVAL=remap CAR=info BACK=exit |
| Remap | ”REMAP MODE — SELECT KEY TO REMAP” | Grid with *selected* key highlighted | Currently selected: KN-86 name; Current mapping; [*] unsaved | CDR=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; instructions | EVAL=save BACK=discard |
Design Rationale
Section titled “Design Rationale”Why This Layout?
Section titled “Why This Layout?”- Physical grid mirrors hardware: Left/right split matches operator’s hands and the actual device topology
- Real-time feedback: Highlighting on key press provides immediate confidence
- Minimal text: ASCII grid reduces clutter, leaves space for detail lines
- Cycling via CDR: Aligns with existing firmware interaction model (CDR = traverse siblings)
- Unsaved marker:
[*]convention is standard across desktop UIs; amber-on-black reads clearly - State machine clarity: Separate submodes (Confirm, Save) isolate irreversible actions
Accessibility
Section titled “Accessibility”- 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