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
+30
-17
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
#include "v4/boot.h"
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BLOCKS 64u
|
||||
|
||||
@@ -22,27 +22,40 @@ static void console_out(const char *text, unsigned len)
|
||||
(void)fwrite(text, 1, len, stdout);
|
||||
}
|
||||
|
||||
/* a character for a line that reads the keyboard */
|
||||
static int console_key(void)
|
||||
{
|
||||
int c;
|
||||
fflush(stdout);
|
||||
c = getchar();
|
||||
return c == EOF ? V4_BOOT_KEY_END : c;
|
||||
}
|
||||
|
||||
/* The prompt is the host's, as it is the kernel's REPL's for a v3 VM: read a
|
||||
* line, hand it to the node, say how it ended. */
|
||||
int main(void)
|
||||
{
|
||||
const v4_image *im = &v4_capsule_image;
|
||||
unsigned char buf[256];
|
||||
static char line[1026];
|
||||
v4_boot boot;
|
||||
|
||||
boot.n = &n; boot.es = &es; boot.h = &h; boot.im = im; boot.out = console_out;
|
||||
boot.n = &n; boot.es = &es; boot.h = &h; boot.im = &v4_capsule_image;
|
||||
boot.out = console_out; boot.key = console_key;
|
||||
if (!v4_boot_run(&boot, disk, BLOCKS)) { fflush(stdout); return 1; }
|
||||
|
||||
for (;;) {
|
||||
(void)v4_exec_step_word(&n, &es, &h);
|
||||
if (n.console_len) {
|
||||
(void)fwrite(n.console, 1, n.console_len, stdout);
|
||||
n.console_len = 0;
|
||||
}
|
||||
if (n.stopped) { fflush(stdout); fprintf(stderr, "starforth4: the node stopped on a fault\n"); return 1; }
|
||||
if (v4_image_waiting(&n, im)) {
|
||||
ssize_t got;
|
||||
fflush(stdout);
|
||||
got = read(0, buf, sizeof buf);
|
||||
if (got <= 0) { putchar('\n'); return 0; }
|
||||
(void)v4_node_console_feed(&n, buf, (unsigned)got);
|
||||
}
|
||||
size_t len;
|
||||
int how;
|
||||
|
||||
fputs("ok> ", stdout);
|
||||
fflush(stdout);
|
||||
if (!fgets(line, sizeof line, stdin)) { putchar('\n'); return 0; }
|
||||
len = strlen(line);
|
||||
if (len && line[len - 1] == '\n') len--;
|
||||
how = v4_boot_line(&boot, line, (unsigned)len);
|
||||
if (how == V4_LINE_COMPLETED) fputs(" ok\n", stdout);
|
||||
else if (how == V4_LINE_ERROR) fputs(" ERROR\n", stdout);
|
||||
else if (how == V4_BOOT_LINE_TOO_LONG) fputs("Line too long\n ERROR\n", stdout);
|
||||
else if (how == V4_BOOT_LINE_NO_INPUT) { putchar('\n'); return 0; }
|
||||
else if (how == V4_BOOT_LINE_STOPPED) { fflush(stdout); fprintf(stderr, "starforth4: the node stopped on a fault\n"); return 1; }
|
||||
}
|
||||
}
|
||||
|
||||
+6
-30
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Runs on the build machine. It assembles capsule/ *.v4 -- the nucleus,
|
||||
* docs/v4.0.0/NUCLEUS.md -- onto a host node with the memory map of
|
||||
* tests/host_map.h and starts the node at its prompt. Then it writes every
|
||||
* tests/host_map.h. It runs nothing: it writes every
|
||||
* word of the node's memory that is not zero, and the addresses a loader
|
||||
* needs, as a C file. No FORTH source is compiled here: what is not in the
|
||||
* nucleus is loaded from capsules when the system boots (v4/system/boot.c).
|
||||
@@ -22,11 +22,8 @@
|
||||
#include "../tests/host_map.h"
|
||||
|
||||
static v4_node n;
|
||||
static v4_exec_state es;
|
||||
static v4_heat h;
|
||||
static v4_text tx;
|
||||
static v4_cell w_key, w_key_end;
|
||||
static unsigned char disk[4 * V4_BLOCK_BYTES];
|
||||
|
||||
static void die(const char *what, const char *detail)
|
||||
{
|
||||
@@ -34,19 +31,6 @@ static void die(const char *what, const char *detail)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* run until the node has taken all its input and is waiting in KEY */
|
||||
static void run_until_waiting(void)
|
||||
{
|
||||
long steps = 0;
|
||||
unsigned idle = 0;
|
||||
while (idle < 64) {
|
||||
if (++steps > 200000000L) die("the node did not come back to its prompt", NULL);
|
||||
if (n.stopped) die("the node stopped on a fault", NULL);
|
||||
(void)v4_exec_step_word(&n, &es, &h);
|
||||
if (n.input_pos == n.input_len && n.p >= w_key && n.p < w_key_end) idle++; else idle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* every entry from xt back: no access control fields set */
|
||||
static void clear_acl(v4_cell xt)
|
||||
{
|
||||
@@ -58,7 +42,7 @@ int main(int argc, char **argv)
|
||||
static const char *const files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4", "compile.v4", "quit.v4", "forth.v4",
|
||||
"numout.v4", "words.v4", "system.v4", "qmath.v4", "blocks.v4", "log.v4", "acl.v4" };
|
||||
FILE *out;
|
||||
v4_cell k, entry, faults, voc;
|
||||
v4_cell k, line, idle, faults, voc;
|
||||
unsigned count = 0;
|
||||
|
||||
if (argc != 2) die("usage: mkimage OUTPUT.c", NULL);
|
||||
@@ -66,7 +50,8 @@ int main(int argc, char **argv)
|
||||
if (!host_load(&tx, &n, files, (unsigned)(sizeof files / sizeof files[0]))) die("the capsule does not assemble", NULL);
|
||||
if (!v4_text_finish(&tx)) die("not everything is defined", v4_text_error(&tx));
|
||||
if (v4_text_here(&tx) >= DICT_W) die("the capsule's code runs into the dictionary space", NULL);
|
||||
entry = v4_text_word(&tx, "QUIT");
|
||||
line = v4_text_word(&tx, "(LINE)");
|
||||
idle = v4_text_word(&tx, "(IDLE)");
|
||||
faults = v4_text_word(&tx, "(FAULTS)");
|
||||
w_key = v4_text_word(&tx, "KEY");
|
||||
w_key_end = v4_text_word(&tx, "CR");
|
||||
@@ -83,16 +68,6 @@ int main(int argc, char **argv)
|
||||
n.mem[CURRENT] = LATEST;
|
||||
n.mem[SRC] = TIB;
|
||||
|
||||
v4_exec_reset(&es);
|
||||
v4_heat_reset(&h);
|
||||
v4_node_console_attach(&n, CONSOLE_TX);
|
||||
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST);
|
||||
v4_node_error_attach(&n, NODE_ERROR);
|
||||
v4_node_fault_attach(&n, faults);
|
||||
v4_node_storage_attach(&n, STORAGE_REG, disk, 4);
|
||||
n.p = entry;
|
||||
run_until_waiting();
|
||||
|
||||
/* The nucleus is the system as COLD finds it: COLD comes back to here,
|
||||
* and FORGET will not go below it. What the capsules add at boot
|
||||
* (v4/system/boot.c) is above it. */
|
||||
@@ -125,7 +100,8 @@ int main(int argc, char **argv)
|
||||
}
|
||||
fprintf(out, "};\n\nconst v4_image v4_capsule_image = {\n cells, %uu,\n %uu, %uu, %uu, %uu,\n", count,
|
||||
(unsigned)V4_CELL_BITS, (unsigned)V4_NODE_WORDS, (unsigned)V4_DATA_RING, (unsigned)V4_RET_RING);
|
||||
fprintf(out, " %ld, %ld,\n %ld, %ld,\n", (long)entry, (long)faults, (long)w_key, (long)w_key_end);
|
||||
fprintf(out, " %ld, %ld, %ld,\n %ld, %uu, %ld,\n %ld,\n %ld, %ld,\n", (long)line, (long)idle, (long)LINE_STATUS,
|
||||
(long)TIB, 1025u, (long)SPAN, (long)faults, (long)w_key, (long)w_key_end);
|
||||
fprintf(out, " %ld, %ld, %ld,\n %ld, %ld,\n %ld,\n %ld,\n %ld, %ld\n};\n", (long)CONSOLE_TX, (long)CONSOLE_RX, (long)CONSOLE_ST,
|
||||
(long)DSTACK_REG, (long)RSTACK_REG, (long)NODE_ERROR, (long)STORAGE_REG, (long)DP, (long)LATEST);
|
||||
if (fclose(out) != 0) die("cannot write", argv[1]);
|
||||
|
||||
Reference in New Issue
Block a user