Verilog platform: keycode delivers browser keyCode not ASCII; shift state lost for digit-row keys
Summary
The documentation states:
keycode is an 8-bit value with the high bit set when a key is pressed.
This implies the low 7 bits are ASCII. In practice they are the raw browser keyCode, which differs from ASCII for all punctuation keys and loses shift state entirely for digit-row keys. This makes several characters essential to keyboard-input designs (including ( ) * % ") unreachable.
Root cause
From src/platform/verilog.ts:
poller = setKeyboardFromMap(video, switches, VERILOG_KEYCODE_MAP, (o, key, code, flags) => {
if (flags & KeyFlags.KeyDown) {
keycode = code | 0x80; // <-- code is browser keyCode, not charCode
}
}, true);
code is the browser KeyboardEvent.keyCode. This causes two distinct problems:
Problem 1 — Shift state lost for digit-row keys
Browser keyCode is identical for a digit key whether or not Shift is held. shift+9 and 9 both produce keyCode = 57. The shifted character ( is
unreachable.
Problem 2 — Punctuation keyCode ≠ ASCII
Punctuation keys have keyCode values ≥ 128 that happen to have bit 7 already set, so keyCode | 0x80 == keyCode. After the typical AND #$7F used in Apple 1 / PIA designs, the result is incorrect:
| Key typed |
keyCode |
keyCode|0x80 |
after AND #$7F |
Expected ASCII |
- |
189 (0xBD) |
0xBD |
0x3D = |
0x2D - ✗ |
= |
187 (0xBB) |
0xBB |
0x3B ; |
0x3D = ✗ |
' |
222 (0xDE) |
0xDE |
0x5E ^ |
0x27 ' ✗ |
; |
186 (0xBA) |
0xBA |
0x3A : |
0x3B ; ✗ |
/ |
191 (0xBF) |
0xBF |
0x3F ? |
0x2F / ✗ |
, |
188 (0xBC) |
0xBC |
0x3C < |
0x2C , ✗ |
. |
190 (0xBE) |
0xBE |
0x3E > |
0x2E . ✗ |
shift+9 |
57 (0x39) |
0xB9 |
0x39 9 |
0x28 ( ✗ (shift lost) |
shift+0 |
48 (0x30) |
0xB0 |
0x30 0 |
0x29 ) ✗ (shift lost) |
shift+= |
187 (0xBB) |
0xBB |
0x3B ; |
0x2B + ✗ (shift lost) |
shift+' |
222 (0xDE) |
0xDE |
0x5E ^ |
0x22 " ✗ (shift lost) |
What does work correctly:
- Letters A–Z (keyCode 65–90 = ASCII, uppercase only — acceptable)
- Digits 0–9 (keyCode 48–57 = ASCII ✓)
- Enter / CR (keyCode 13 ✓), Backspace / BS (keyCode 8 ✓)
shift+, → <, shift+. → >, shift+/ → ?, shift+; → : — these
work by coincidence because keyCode & 0x7F happens to equal the ASCII value of the shifted character
Reproducer
A minimal Verilog testbench echoes every keypress directly to a text terminal (no CPU, no memory, no address decoding) is attached
kbd_echo_test.v.txt
Load it in the IDE, open CRT view, and type punctuation and shift+digit keys and observe displayed characters.
Suggested fix
Replace event.keyCode with event.key (the printable character string) in the keyboard handler. event.key is shift-aware and returns the actual character for printable keys ("(", "+", "\"" etc.), falling back to named strings ("Enter", "Backspace", "ArrowLeft") for non-printable keys.
// current (incorrect for punctuation and shift+digit):
keycode = code | 0x80; // code = browser keyCode
// suggested:
// use event.key.length==1 for printable chars -> charCodeAt(0) | 0x80
// use event.keyCode for non-printable (Enter, BS, arrows etc.)
This would make keycode & 0x7F reliably equal to the ASCII value of the character the user visually typed, on any keyboard layout, including shifted chars.
Impact
Affects any Verilog design that uses keycode for text input — Apple 1 clones, terminal emulators, BASIC interpreters, etc. The built-in cpu6502.v preset is one example; the mango_one Apple 1 fork is another.
A documentation update is needed regardless: clarify that the low 7 bits are keyCode, not ASCII, to determien if translation needed in Verilog.
Verilog platform:
keycodedelivers browser keyCode not ASCII; shift state lost for digit-row keysSummary
The documentation states:
This implies the low 7 bits are ASCII. In practice they are the raw browser
keyCode, which differs from ASCII for all punctuation keys and loses shift state entirely for digit-row keys. This makes several characters essential to keyboard-input designs (including( ) * % ") unreachable.Root cause
From
src/platform/verilog.ts:codeis the browserKeyboardEvent.keyCode. This causes two distinct problems:Problem 1 — Shift state lost for digit-row keys
Browser
keyCodeis identical for a digit key whether or not Shift is held.shift+9and9both producekeyCode = 57. The shifted character(isunreachable.
Problem 2 — Punctuation keyCode ≠ ASCII
Punctuation keys have
keyCodevalues ≥ 128 that happen to have bit 7 already set, sokeyCode | 0x80 == keyCode. After the typicalAND #$7Fused in Apple 1 / PIA designs, the result is incorrect:keyCode|0x80AND #$7F-=-✗=;=✗'^'✗;:;✗/?/✗,<,✗.>.✗shift+99(✗ (shift lost)shift+00)✗ (shift lost)shift+=;+✗ (shift lost)shift+'^"✗ (shift lost)What does work correctly:
shift+,→<,shift+.→>,shift+/→?,shift+;→:— thesework by coincidence because
keyCode & 0x7Fhappens to equal the ASCII value of the shifted characterReproducer
A minimal Verilog testbench echoes every keypress directly to a text terminal (no CPU, no memory, no address decoding) is attached
kbd_echo_test.v.txt
Load it in the IDE, open CRT view, and type punctuation and shift+digit keys and observe displayed characters.
Suggested fix
Replace
event.keyCodewithevent.key(the printable character string) in the keyboard handler.event.keyis shift-aware and returns the actual character for printable keys ("(","+","\""etc.), falling back to named strings ("Enter","Backspace","ArrowLeft") for non-printable keys.This would make
keycode & 0x7Freliably equal to the ASCII value of the character the user visually typed, on any keyboard layout, including shifted chars.Impact
Affects any Verilog design that uses
keycodefor text input — Apple 1 clones, terminal emulators, BASIC interpreters, etc. The built-incpu6502.vpreset is one example; the mango_one Apple 1 fork is another.A documentation update is needed regardless: clarify that the low 7 bits are
keyCode, not ASCII, to determien if translation needed in Verilog.