full-screen vt100 terminal 4.4v -- keyboard-to-REPL bridge, real and tested: Refactored KEY-EVENT's per-arch translation logic (keyboard_words.c) into a shared C function, sk_key_event_poll(), so the REPL bridge reuses item 4.3.5f's already-converged Linux-keycode-namespace event stream instead of building separate amd64/aarch64/riscv64 tables. repl.c's sk_kbd_getc() decodes the standard US-QWERTY printable range plus Enter/Backspace/Shift against that stream; sk_readline() polls it as a second source alongside console_getc(). Verified via QEMU monitor sendkey injection, and by Captain Bob typing directly into the live QEMU window over real emulated PS/2 hardware mid-session (1 1 + . -> 2 ok, then a clean BYE shutdown). 4.4ab -- simplify to a full-screen terminal: Captain Bob's call, reverting the 640x480 CANVAS box + independent REPL strip (4.4o/4.4t/4.4x/4.4z) in favor of the simplest shape: the entire framebuffer is one vt100 terminal, g_vt.cols/rows = fb_width()/fb_height() divided by cell size, no origin offset, no box, no strip, no border drawing. The REPL prompt is just the terminal's last scrolling line. Scrollback, TTF rendering, and SGR color are all box-agnostic and keep working unmodified. 4.4r -- reframed as a text/graphics mode toggle: "Hide/show the scroll box" stopped meaning anything once the box was removed; the underlying need survives as a whole-screen mode switch. vt100_toggle_graphics() is a two-state machine (VISIBLE/HIDDEN) -- hidden mode stops the terminal from touching the framebuffer while its logical state keeps advancing, so direct framebuffer/TTF-TEXT drawing can use the whole screen; showing again wipes and reuses scrollback_redraw() to restore the terminal exactly. Reachable two ways, one transition function: physically via Alt+TAB (4.4y revised from Ctrl+TAB) and programmatically via the new ALT+TAB FORTH word. Verified: three-arch clean QEMU boot + logs; amd64 screendump confirms full-width text with no box/strip artifacts. Punch list §25 items 4.4v/4.4r/4.4ab complete; 4.4y revised. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
190 lines
6.3 KiB
C
190 lines
6.3 KiB
C
/*
|
||
StarForth — Steady-State Virtual Machine Runtime
|
||
|
||
Copyright (c) 2023–2025 Robert A. James
|
||
All rights reserved.
|
||
|
||
Licensed under the StarForth License, Version 1.0
|
||
*/
|
||
|
||
/* keyboard_words.c — raw scancode-ring diagnostic word (FABRIC.md item
|
||
* 4.3.5). Kernel-only, amd64-only; no-op elsewhere. */
|
||
|
||
#include "include/keyboard_words.h"
|
||
#include "../../include/word_registry.h"
|
||
|
||
#if defined(__STARKERNEL__) && defined(ARCH_AMD64)
|
||
#include "starkernel/i8042.h"
|
||
#endif
|
||
|
||
#if defined(__STARKERNEL__) && (defined(ARCH_RISCV64) || defined(ARCH_AARCH64))
|
||
#include "starkernel/virtio_input.h"
|
||
#endif
|
||
|
||
#if defined(__STARKERNEL__)
|
||
#include "starkernel/console.h"
|
||
#endif
|
||
|
||
/* KBD-SCAN ( -- c -1 | 0 ) */
|
||
static void kbw_scan(VM *vm)
|
||
{
|
||
#if defined(__STARKERNEL__) && defined(ARCH_AMD64)
|
||
uint8_t sc;
|
||
if (i8042_pop_scancode(&sc)) {
|
||
vm_push(vm, (cell_t)sc);
|
||
vm_push(vm, -1);
|
||
} else {
|
||
vm_push(vm, 0);
|
||
}
|
||
#else
|
||
vm_push(vm, 0);
|
||
#endif
|
||
}
|
||
|
||
/* KBD-DEBUG ( -- isr_count spurious_count ): cheap standing diagnostic,
|
||
* not scaffolding -- confirms the interrupt path is alive (isr_count) and
|
||
* flags the failure mode item 4.3.5 found (a real IRQ misreported as
|
||
* spurious) without needing a live debugger. */
|
||
extern volatile uint32_t g_i8042_isr_count;
|
||
extern volatile uint32_t g_spurious_count;
|
||
static void kbw_debug(VM *vm)
|
||
{
|
||
#if defined(__STARKERNEL__) && defined(ARCH_AMD64)
|
||
vm_push(vm, (cell_t)g_i8042_isr_count);
|
||
vm_push(vm, (cell_t)g_spurious_count);
|
||
#else
|
||
vm_push(vm, 0);
|
||
vm_push(vm, 0);
|
||
#endif
|
||
}
|
||
|
||
/* VKBD-EVENT ( -- code value -1 | 0 ): item 4.3.5c (riscv64) / 4.3.5e
|
||
* (aarch64) virtio-input. Pop one decoded EV_KEY event off the
|
||
* interrupt-fed ring buffer. Pushes code and value and -1 (true) if one
|
||
* was available, or just 0 (false) if the buffer was empty. Distinct word
|
||
* from KBD-SCAN rather than a shared one: different device, different
|
||
* event shape (Linux-style code/value pair vs. a raw XT scancode byte) --
|
||
* convergence onto one interface is item 4.3.5f's explicit job, not this
|
||
* one's. */
|
||
static void kbw_vevent(VM *vm)
|
||
{
|
||
#if defined(__STARKERNEL__) && (defined(ARCH_RISCV64) || defined(ARCH_AARCH64))
|
||
uint16_t code;
|
||
uint32_t value;
|
||
if (virtio_input_pop_event(&code, &value)) {
|
||
vm_push(vm, (cell_t)code);
|
||
vm_push(vm, (cell_t)value);
|
||
vm_push(vm, -1);
|
||
} else {
|
||
vm_push(vm, 0);
|
||
}
|
||
#else
|
||
vm_push(vm, 0);
|
||
#endif
|
||
}
|
||
|
||
/* VKBD-DEBUG ( -- isr_count ): standing diagnostic, same shape as
|
||
* KBD-DEBUG -- confirms the virtio-input interrupt path is alive. */
|
||
extern volatile uint32_t g_virtio_input_isr_count;
|
||
static void kbw_vdebug(VM *vm)
|
||
{
|
||
#if defined(__STARKERNEL__) && (defined(ARCH_RISCV64) || defined(ARCH_AARCH64))
|
||
vm_push(vm, (cell_t)g_virtio_input_isr_count);
|
||
#else
|
||
vm_push(vm, 0);
|
||
#endif
|
||
}
|
||
|
||
/* KEY-EVENT ( -- keycode pressed -1 | 0 ): item 4.3.5f convergence
|
||
* checkpoint. One shared shape across all three architectures instead of
|
||
* KBD-SCAN's raw XT scancode byte or VKBD-EVENT's Linux code/value pair.
|
||
* keycode is the Linux input keycode namespace (linux/input-event-codes.h);
|
||
* pressed is 1 (press) or 0 (release).
|
||
*
|
||
* amd64 needs a real translation, not an invented one: XT Set-1's make
|
||
* code for a key and Linux's keycode for that same key are the *same
|
||
* number*, confirmed against this build host's own
|
||
* /usr/include/linux/input-event-codes.h and against this tree's own
|
||
* live-observed values (item 4.3.5's done note: 'a' -> 30, 'b' -> 48,
|
||
* matching KEY_A=30/KEY_B=48 exactly) -- not a coincidence, a documented
|
||
* historical property of how the Linux input keycode numbering was
|
||
* originally defined around the AT/XT scancode set. Break codes set XT's
|
||
* bit 7 (make|0x80) rather than carrying a separate press/release field,
|
||
* so `keycode = sc & 0x7F, pressed = !(sc & 0x80)` is the whole
|
||
* translation for the entire non-extended key range. Extended
|
||
* (0xE0-prefixed) scancodes are not decoded here -- this checkpoint needs
|
||
* one shared shape proven live per architecture, not full keyboard
|
||
* coverage; that belongs to the REPL wiring item (M8) this checkpoint
|
||
* unblocks.
|
||
*
|
||
* riscv64/aarch64 need no translation at all -- virtio-input's `code`
|
||
* field already lives in the same Linux keycode namespace; `value` is
|
||
* mapped 1:1 except autorepeat (value=2), folded into "still pressed"
|
||
* here since this checkpoint's shape only distinguishes press/release. */
|
||
/* FABRIC.md item 4.4v: shared C-level implementation, so the REPL
|
||
* keyboard bridge (repl.c) and the KEY-EVENT FORTH word below poll the
|
||
* exact same converged event stream rather than each re-deriving the
|
||
* per-arch translation kbw_key_event's own doc comment already explains
|
||
* (XT Set-1 make code == Linux keycode for the non-extended range; break
|
||
* codes set bit 7 instead of carrying a separate field). */
|
||
int sk_key_event_poll(uint16_t *keycode, int *pressed)
|
||
{
|
||
#if defined(__STARKERNEL__) && defined(ARCH_AMD64)
|
||
uint8_t sc;
|
||
if (i8042_pop_scancode(&sc)) {
|
||
*keycode = (uint16_t)(sc & 0x7Fu);
|
||
*pressed = (sc & 0x80u) ? 0 : 1;
|
||
return 1;
|
||
}
|
||
return 0;
|
||
#elif defined(__STARKERNEL__) && (defined(ARCH_RISCV64) || defined(ARCH_AARCH64))
|
||
uint16_t code;
|
||
uint32_t value;
|
||
if (virtio_input_pop_event(&code, &value)) {
|
||
*keycode = code;
|
||
*pressed = (value == 0u) ? 0 : 1;
|
||
return 1;
|
||
}
|
||
return 0;
|
||
#else
|
||
(void)keycode; (void)pressed;
|
||
return 0;
|
||
#endif
|
||
}
|
||
|
||
static void kbw_key_event(VM *vm)
|
||
{
|
||
uint16_t keycode;
|
||
int pressed;
|
||
|
||
if (sk_key_event_poll(&keycode, &pressed)) {
|
||
vm_push(vm, (cell_t)keycode);
|
||
vm_push(vm, (cell_t)pressed);
|
||
vm_push(vm, -1);
|
||
} else {
|
||
vm_push(vm, 0);
|
||
}
|
||
}
|
||
|
||
/* ALT+TAB ( -- ): FABRIC.md item 4.4y-revised. Programmatic equivalent of
|
||
* the physical Alt+TAB interception (repl.c's sk_kbd_getc()) -- both call
|
||
* console_fb_toggle_graphics(), so there is exactly one state-machine
|
||
* transition, invoked two ways. */
|
||
static void kbw_alt_tab(VM *vm)
|
||
{
|
||
(void)vm;
|
||
#if defined(__STARKERNEL__)
|
||
console_fb_toggle_graphics();
|
||
#endif
|
||
}
|
||
|
||
void register_keyboard_words(VM *vm)
|
||
{
|
||
register_word(vm, "KBD-SCAN", kbw_scan);
|
||
register_word(vm, "KBD-DEBUG", kbw_debug);
|
||
register_word(vm, "VKBD-EVENT", kbw_vevent);
|
||
register_word(vm, "VKBD-DEBUG", kbw_vdebug);
|
||
register_word(vm, "KEY-EVENT", kbw_key_event);
|
||
register_word(vm, "ALT+TAB", kbw_alt_tab);
|
||
}
|