feat(v4.0.0): a node is handed a line -- ENGINE.md step 1
A v4 node no longer reads its own command line or prints a prompt. Its host puts a line of text in the node's input buffer and starts it at (LINE); the node interprets it and stops at (IDLE), leaving in (LINE-STATUS) how it ended: completed, an error, or QUIT. The host says " ok" or " ERROR" and prompts, as the kernel's REPL does for a v3 VM. A line may be 1024 characters, a block, as v3's. Ruled 2026-10-05 (V3-PARITY.md 1b); design ENGINE.md 3.1. - quit.v4: (REPL), the node's prompt loop, is gone; (LINE) (IDLE) (DONE) - image.h/.c: v4_line_begin, v4_line_done, v4_line_status; the node is idle at switch-on - boot.c: v4_boot_line, the one loop the hosted binary, the kernel and the capsule loader hand a line with; the code that took " ok" and the prompt back out of the node's output is gone - hosted.c, sk_v4.c: the prompt and the line editing are the host's - test_host_quit.c: the tests are the node's host; two tests of the old 80-character prompt line now test a whole line, 1024 and 1025 characters Verified: make -C v4 test passes at both widths; hosted-check passes on three ISAs; clean qemu with STARFORTH_V4=1 on amd64, aarch64 and riscv64 passes POST (550 of 550) with the same hashes as hosted, and three lines typed at each bare-metal prompt through the serial port are answered correctly (logs/20261005-180922, -181152, -181541). Still the lone node: kernel_main.c starts it before the fleet tables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ab7a9bf06f
commit
01c447f9ab
+29
-63
@@ -24,7 +24,7 @@ static const char *const boot_capsules[] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
#define LINE_MAX 80u /* QUERY takes 80 characters, the new-line among them */
|
||||
#define LINE_MAX 1025u /* a line is at most 1024 characters, a block */
|
||||
#define STEP_LIMIT 4000000000ULL /* instruction words one line may take */
|
||||
|
||||
/* ---- printing ------------------------------------------------------------ */
|
||||
@@ -104,17 +104,7 @@ static uint32_t word_count(const v4_node *n, const v4_image *im)
|
||||
return count;
|
||||
}
|
||||
|
||||
/* ---- running the node ---------------------------------------------------- */
|
||||
|
||||
/* The node ends every line it has taken with " ok" and the next prompt.
|
||||
* Those eight characters are held back from the console, so that what is
|
||||
* shown is only what the line itself printed. */
|
||||
static const char accepted[8] = { ' ', 'o', 'k', '\n', 'o', 'k', '>', ' ' };
|
||||
|
||||
typedef struct {
|
||||
char held[8];
|
||||
unsigned len;
|
||||
} tail;
|
||||
/* ---- handing the node a line ---------------------------------------------- */
|
||||
|
||||
/* POST's verdict. The POST capsule ends by printing one line,
|
||||
* PARITY:V4_POST tests=N pass=N fail=N
|
||||
@@ -136,62 +126,42 @@ static int text_at(const char *line, unsigned len, unsigned at, const char *want
|
||||
static void post_watch(char c)
|
||||
{
|
||||
static const char clean[] = " fail=0";
|
||||
unsigned i;
|
||||
if (c != '\n') {
|
||||
if (post_len < sizeof post_line) post_line[post_len++] = c;
|
||||
return;
|
||||
}
|
||||
/* the node's prompt comes before it on the line, so look along the line */
|
||||
for (i = 0; i < post_len; i++)
|
||||
if (text_at(post_line, post_len, i, "PARITY:V4_POST ")) {
|
||||
post_seen = 1;
|
||||
post_clean = post_len >= sizeof clean - 1 && text_at(post_line, post_len, post_len - (unsigned)(sizeof clean - 1), clean);
|
||||
break;
|
||||
}
|
||||
if (text_at(post_line, post_len, 0, "PARITY:V4_POST ")) {
|
||||
post_seen = 1;
|
||||
post_clean = post_len >= sizeof clean - 1 && text_at(post_line, post_len, post_len - (unsigned)(sizeof clean - 1), clean);
|
||||
}
|
||||
post_len = 0;
|
||||
}
|
||||
|
||||
static void tail_put(const v4_boot *b, tail *t, int show, char c)
|
||||
{
|
||||
unsigned i;
|
||||
if (t->len == sizeof t->held) {
|
||||
if (show) b->out(t->held, 1);
|
||||
for (i = 1; i < sizeof t->held; i++) t->held[i - 1] = t->held[i];
|
||||
t->len--;
|
||||
}
|
||||
t->held[t->len++] = c;
|
||||
}
|
||||
|
||||
/* Run until the node waits for a character. Returns 1 if what it printed
|
||||
* ended with " ok" and the prompt; 0 if not -- the rest is then shown too --
|
||||
* or if the node stopped or never came back. */
|
||||
static int run_to_prompt(const v4_boot *b, int show)
|
||||
int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
|
||||
{
|
||||
v4_node *n = b->n;
|
||||
uint64_t steps = 0;
|
||||
unsigned i;
|
||||
tail t;
|
||||
|
||||
t.len = 0;
|
||||
if (!v4_line_begin(n, b->im, text, len)) return V4_BOOT_LINE_TOO_LONG;
|
||||
for (;;) {
|
||||
(void)v4_exec_step_word(n, b->es, b->h);
|
||||
if (n->console_len) {
|
||||
for (i = 0; i < n->console_len; i++) {
|
||||
post_watch((char)n->console[i]);
|
||||
tail_put(b, &t, show, (char)n->console[i]);
|
||||
}
|
||||
for (i = 0; i < n->console_len; i++) post_watch((char)n->console[i]);
|
||||
b->out((const char *)n->console, n->console_len);
|
||||
n->console_len = 0;
|
||||
}
|
||||
if (n->stopped) { say(b, "\nV4: the node stopped on a fault\n"); return 0; }
|
||||
if (v4_image_waiting(n, b->im)) break;
|
||||
if (++steps > STEP_LIMIT) { say(b, "\nV4: the node did not come back to its prompt\n"); return 0; }
|
||||
if (n->stopped) return V4_BOOT_LINE_STOPPED;
|
||||
if (v4_line_done(n, b->im)) return v4_line_status(n, b->im);
|
||||
if (v4_image_waiting(n, b->im)) { /* the line is reading the keyboard */
|
||||
int c = b->key ? b->key() : V4_BOOT_KEY_END;
|
||||
unsigned char ch = (unsigned char)c;
|
||||
if (c == V4_BOOT_KEY_END) return V4_BOOT_LINE_NO_INPUT;
|
||||
if (c >= 0) (void)v4_node_console_feed(n, &ch, 1u);
|
||||
continue;
|
||||
}
|
||||
if (++steps > STEP_LIMIT) return V4_BOOT_LINE_STOPPED;
|
||||
}
|
||||
if (t.len == sizeof t.held) {
|
||||
for (i = 0; i < sizeof t.held && t.held[i] == accepted[i]; i++) { }
|
||||
if (i == sizeof t.held) return 1;
|
||||
}
|
||||
if (show) b->out(t.held, t.len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- one capsule --------------------------------------------------------- */
|
||||
@@ -253,13 +223,13 @@ static int load_capsule(const v4_boot *b, const char *name)
|
||||
if (len >= LINE_MAX) { say_where(b, name, block, line); say(b, " is too long\n"); return 0; }
|
||||
if (len) {
|
||||
for (i = 0; i < len; i++) text[i] = (char)p[i];
|
||||
text[len] = '\n';
|
||||
if (v4_node_console_feed(b->n, text, len + 1u) != len + 1u) {
|
||||
say_where(b, name, block, line); say(b, ": the node's input is full\n");
|
||||
return 0;
|
||||
}
|
||||
if (!run_to_prompt(b, 1)) {
|
||||
say_where(b, name, block, line); say(b, " was not accepted: ");
|
||||
int how = v4_boot_line(b, text, len);
|
||||
if (how != V4_LINE_COMPLETED) {
|
||||
say_where(b, name, block, line);
|
||||
say(b, how == V4_LINE_ERROR ? " ended in an error: "
|
||||
: how == V4_LINE_QUIT ? " ended with QUIT: "
|
||||
: how == V4_BOOT_LINE_NO_INPUT ? " reads the keyboard: "
|
||||
: " stopped the node: ");
|
||||
b->out(text, len); say(b, "\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -291,10 +261,6 @@ int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks)
|
||||
say(b, " image_hash="); say_hex(b, image_hash(b->im));
|
||||
say(b, "\n");
|
||||
|
||||
/* the node's own first prompt is not shown: the boot prints one at the end */
|
||||
(void)run_to_prompt(b, 0);
|
||||
if (b->n->stopped) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; }
|
||||
|
||||
for (i = 0; i < sizeof boot_capsules / sizeof boot_capsules[0]; i++)
|
||||
if (!load_capsule(b, boot_capsules[i])) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; }
|
||||
#if V4_POST_AT_BOOT
|
||||
@@ -306,9 +272,9 @@ int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks)
|
||||
#endif
|
||||
|
||||
#if V4_POST_AT_BOOT
|
||||
say(b, "PARITY:OK\nPOST: PASSED\nok> ");
|
||||
say(b, "PARITY:OK\nPOST: PASSED\n");
|
||||
#else
|
||||
say(b, "PARITY:OK\nok> ");
|
||||
say(b, "PARITY:OK\n");
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user