starkernel: REPL scrollback, ~1000 lines (FABRIC.md item 4.4q)

Scope decided with Captain Bob before implementation: ring buffer +
recall on today's full-screen vt100 grid, not also confining REPL text
to the 4.4o 640x480 box (that confinement stays open as its own future
item, not a third silent deferral). No keyboard input path exists yet
(M8 unstarted), so the trigger is two new FORTH words, SCROLL-BACK
( n -- ) / SCROLL-FWD ( n -- ), exercised via serial injection.

vt100.c gains a text-only 1000-line ring buffer (kmalloc'd, tens of KB
-- not pixel snapshots, which would be ~1000x larger for no benefit)
plus a shadow buffer mirroring the current screen. scroll_up() now
pushes evicted rows into the ring before the pixel scroll. History is
one continuous sequence (ring then shadow); scrolling always redraws
from that sequence -- no separate pixel-scroll path for scrollback,
decided up front to avoid retrofitting later.

New src/word_source/scroll_words.c (Module 31), thin wrappers over
console_fb_scroll_back()/_fwd() -> vt100_scroll_back()/_fwd(). Bug
caught during live testing: both words initially used an off-by-one
underflow check (dsp < 1) copied from a different, older dsp
convention elsewhere in this codebase; vm_pop() (which these words
actually call) uses dsp as a 0-based top-of-stack index, so the check
rejected every legitimate single-argument call. Fixed by removing the
separate precheck and relying on vm_pop()'s own guard.

Live-verified on all three architectures (exceeds this item's
amd64-minimum bar): generated 50+ lines via a FORTH loop, confirmed
SCROLL-BACK recovers correctly older content, and on amd64 confirmed
SCROLL-FWD returns to genuinely live state (not a frozen snapshot) by
showing the injected commands' own echo. Known limitation confirmed by
direct pixel measurement: redrawn lines lose their original SGR color
(not stored per-cell) -- text recovers exactly, color does not.

Three-arch verified: Failed: 0, dict-hashes identical across all
three (values changed correctly from prior items -- two new words
were added).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-11 21:23:25 -04:00
co-authored by Claude Sonnet 5
parent 277845e3bf
commit 1f3ec3554e
27 changed files with 73487 additions and 112 deletions
+36
View File
@@ -0,0 +1,36 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 20232025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
#ifndef SCROLL_WORDS_H
#define SCROLL_WORDS_H
#include "vm.h"
/**
* @defgroup scroll_words REPL Scrollback Words
* @{
*
* @brief `SCROLL-BACK`/`SCROLL-FWD`, the REPL scrollback controls
* (FABRIC.md item 4.4q). Kernel-only; no-op on hosted builds (no
* framebuffer console there).
*
* @par SCROLL-BACK ( n -- )
* Move the scrollback view back @c n lines (toward older output) and
* redraw. Clamped at the oldest stored line -- no error on over-request,
* it just stops there.
*
* @par SCROLL-FWD ( n -- )
* Move the scrollback view forward @c n lines (toward live output) and
* redraw. Clamped at the live view.
* @}
*/
void register_scroll_words(VM *vm);
#endif /* SCROLL_WORDS_H */
+52
View File
@@ -0,0 +1,52 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 20232025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
/* scroll_words.c — REPL scrollback entry points (FABRIC.md item 4.4q).
* Kernel-only; no-op on hosted builds. */
#include "include/scroll_words.h"
#include "../../include/log.h"
#include "../../include/word_registry.h"
#ifdef __STARKERNEL__
#include "starkernel/console.h"
/* SCROLL-BACK ( n -- ) -- move the REPL scrollback view back n lines.
* Relies on vm_pop()'s own underflow guard (sets vm->error, logs, returns
* 0) rather than a separate dsp precheck -- this codebase has more than
* one dsp convention across older word_source files (some treat it as a
* top-of-stack index, some as a raw count); vm_pop() is the one this word
* actually calls, so deferring to its own check sidesteps the mismatch
* entirely instead of risking silently picking the wrong one. */
static void scroll_word_back(VM *vm) {
cell_t n = vm_pop(vm);
if (vm->error) return;
if (n < 0) n = 0;
console_fb_scroll_back((uint32_t)n);
}
/* SCROLL-FWD ( n -- ) -- move the REPL scrollback view forward n lines
* (toward live). Same underflow-handling rationale as SCROLL-BACK above. */
static void scroll_word_fwd(VM *vm) {
cell_t n = vm_pop(vm);
if (vm->error) return;
if (n < 0) n = 0;
console_fb_scroll_fwd((uint32_t)n);
}
#endif /* __STARKERNEL__ */
void register_scroll_words(VM *vm)
{
#ifdef __STARKERNEL__
register_word(vm, "SCROLL-BACK", scroll_word_back);
register_word(vm, "SCROLL-FWD", scroll_word_fwd);
#else
(void) vm;
#endif
}