FABRIC.md item 4.4x: split the REPL prompt into its own bottom strip

Scope expanded from pure CANVAS-rectangle arithmetic (as originally
scoped) to also splitting the REPL prompt/input line out of the
scrollback box into an independent single-line strip, per Captain Bob's
explicit fold-in after the gap was reported (§25.0 rule 3) rather than
silently expanded.

vt100.c: VT100_BOX_ORIGIN_X/Y are no longer hardcoded per-arch literals --
both are now derived from fb_width()/fb_height() at vt100_enable_ttf()
time. New vt100_strip_draw() renders the bottom strip (gray border lines,
bright-white text) directly via the existing ttf_draw_glyph_cell()
rasterizer, independent of the box's own grid/cursor state. Border lines
are drawn after the glyph loop so an oversized cell can only be clipped
by them, never erase them.

console.c/console.h: console_fb_strip_draw() thin wrapper, matching the
existing console_fb_enable_ttf()/console_fb_scroll_*() pattern.

repl.c: builds a plain-text "[VMName] ok> <input>" mirror in
g_strip_prompt/strip_refresh(), refreshed on every keystroke (including
backspace) from sk_readline() -- already wired for item 4.4v, since
keyboard-typed characters will flow through the same console_getc() path
once that lands. Also widened sk_repl_step()/sk_repl_run()'s local input
buffer from a second, smaller 256-byte buffer to INPUT_BUFFER_SIZE
(1025), per 4.4w's decision.

Verified: three-arch clean QEMU boot + logs, amd64 screendump showing
the box and strip as two visually distinct regions with no visible
glyph/border clipping.

Punch list §25 item 4.4x complete.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-12 14:59:07 -04:00
co-authored by Claude Sonnet 5
parent 763c6f2cd5
commit 91742e02f4
21 changed files with 42690 additions and 23 deletions
+78 -2
View File
@@ -35,6 +35,7 @@
#include "starkernel/timer.h"
#include "starkernel/arch.h"
#include <stdint.h>
#include <string.h>
/* FABRIC.md 4.4: "ok>" (including its trailing space) renders in bright
* cyan, 0x55FFFF -- reuses FB_ANSI_PALETTE[14]. Sent as a real SGR escape
@@ -44,6 +45,72 @@
const char lithos_version[64] = LITHOS_VERSION_STR;
/*===========================================================================
* FABRIC.md items 4.4u/4.4w/4.4x: the bottom REPL strip -- a plain-text
* (no ANSI/SGR) mirror of "[VMName] ok> <input so far>", refreshed into
* console_fb_strip_draw() on every keystroke. Built separately from
* SK_PROMPT_TEXT rather than derived from it, since the strip renders in
* one fixed color (4.4u) and has no SGR parser of its own -- the ANSI
* version keeps going to console_puts() exactly as before, unaffected,
* for serial and for the scrollback box's own history.
*===========================================================================*/
#define SK_STRIP_PROMPT_MAX 80u
static char g_strip_prompt[SK_STRIP_PROMPT_MAX] = "ok> ";
/* Builds "[VMName] ok> " or "[VMName] zuse)ok> " (no VM name -> just the
* suffix) into g_strip_prompt, bounded, no strcat -- mirrors emit_prefix()'s
* bracket format (console.c) so the strip and the box agree on what a
* prompt looks like, minus the ANSI color codes the strip doesn't use. */
static void set_strip_prompt(VM *vm)
{
const char *vn = console_get_vm_name();
char *p = g_strip_prompt;
size_t room = sizeof(g_strip_prompt) - 1u;
size_t n;
const char *suffix;
size_t used, left, sn;
if (vn && vn[0]) {
n = strlen(vn);
if (n > room) n = room;
*p++ = '[';
memcpy(p, vn, n);
p += n;
*p++ = ']';
*p++ = ' ';
}
suffix = (vm && vm->zuse_session) ? "zuse)ok> " : "ok> ";
used = (size_t)(p - g_strip_prompt);
left = (used < room) ? (room - used) : 0u;
sn = strlen(suffix);
if (sn > left) sn = left;
memcpy(p, suffix, sn);
p += sn;
*p = '\0';
}
/* Concatenates g_strip_prompt + input_so_far (bounded, no strcat) and
* hands the result to the strip renderer. Called once with an empty
* input_so_far right after set_strip_prompt(), then again on every
* keystroke from sk_readline() below. */
static void strip_refresh(const char *input_so_far)
{
char combined[INPUT_BUFFER_SIZE + SK_STRIP_PROMPT_MAX];
size_t plen = strlen(g_strip_prompt);
size_t ilen = strlen(input_so_far);
if (plen >= sizeof(combined)) plen = sizeof(combined) - 1u;
memcpy(combined, g_strip_prompt, plen);
if (plen + ilen >= sizeof(combined)) ilen = sizeof(combined) - 1u - plen;
memcpy(combined + plen, input_so_far, ilen);
combined[plen + ilen] = '\0';
console_fb_strip_draw(combined);
}
/*===========================================================================
* USE-word dispatch: which VM receives REPL input.
*
@@ -90,6 +157,9 @@ static int sk_readline(char *buf, int size)
{
int n = 0;
buf[0] = '\0';
strip_refresh(buf); /* FABRIC.md 4.4x: show the bare prompt before any input */
for (;;) {
int c = console_getc(); /* non-blocking poll */
@@ -128,10 +198,12 @@ static int sk_readline(char *buf, int size)
/* backspace: DEL (0x7F) or BS (0x08) */
if ((c == 0x7F || c == '\b') && n > 0) {
n--;
buf[n] = '\0';
/* VT100 erase: move back, overwrite with space, move back again */
console_putc('\b');
console_putc(' ');
console_putc('\b');
strip_refresh(buf);
continue;
}
@@ -139,7 +211,9 @@ static int sk_readline(char *buf, int size)
if (n >= size - 1) continue; /* buffer full — drop character */
buf[n++] = (char)c;
buf[n] = '\0';
console_putc((char)c); /* echo */
strip_refresh(buf);
}
buf[n] = '\0';
@@ -180,7 +254,7 @@ static void sk_fault_handler(VM *vm) {
int sk_repl_step(VM *vm)
{
char input[256];
char input[INPUT_BUFFER_SIZE]; /* FABRIC.md 4.4w: matches the strip's input width */
if (!vm || vm->halted) return 0;
@@ -194,6 +268,7 @@ int sk_repl_step(VM *vm)
int is_hera = (!vn || (vn[0]=='H' && vn[1]=='e' && vn[2]=='r' && vn[3]=='a' && vn[4]=='\0'));
vm->emergency_console = is_hera ? (vm->zuse_session ? 0 : 1) : 0;
console_puts(SK_PROMPT_TEXT);
set_strip_prompt(vm); /* FABRIC.md 4.4x: bottom strip mirrors the same prompt */
}
sk_readline(input, sizeof(input));
@@ -222,7 +297,7 @@ int sk_repl_step(VM *vm)
void sk_repl_run(VM *vm)
{
char input[256];
char input[INPUT_BUFFER_SIZE]; /* FABRIC.md 4.4w: matches the strip's input width */
VM *active;
vm->halted = 0;
@@ -241,6 +316,7 @@ void sk_repl_run(VM *vm)
int is_hera = (!vn || (vn[0]=='H' && vn[1]=='e' && vn[2]=='r' && vn[3]=='a' && vn[4]=='\0'));
active->emergency_console = is_hera ? (active->zuse_session ? 0 : 1) : 0;
console_puts(SK_PROMPT_TEXT);
set_strip_prompt(active); /* FABRIC.md 4.4x: bottom strip mirrors the same prompt */
}
sk_readline(input, sizeof(input));