The final review of step 6e, by a fresh reviewer told to break it, and what was ruled: fix them all, test first. Each has a test that failed before the mend. - Two texts from one node could be begun in the wrong order when room for their answers came between the node's look at the first and at the second. Texts whose answers go by the same port are begun in the order they came: (D-MASK), quit.v4. 102 of 700 timings were wrong before. - A word written with PORT! to a node that speaks whole messages, or to a console's port, blocked its writer for ever and cut off every node beyond it. It is error 18 now, whether that node is asleep, running or stuck: fabric.c, node.c. - A node asleep for room on one wire is woken when another of its wires is cut. - A line there was no room for on the lone node's wire was reported as a fault and ended the hosted program. It is Message refused, and the prompt comes back: V4_BOOT_LINE_REFUSED. hosted-check has the case; no bare-metal boot has typed it. - Ruled: text behind held output for the same port is refused though it would fit. MESH.md 7d.4 said otherwise and is corrected. - The removal test also cuts and re-makes a wire, and has an exchange in which output is held at a full wire: 26,814 disturbances in all. - MESH.md and v4/README.md no longer say that a stuck node holds up no other node, that only printing waits, or that no node is left waiting on one that is gone; 7d.6 lists the limits and what the products do that they did not before. make -C v4 test and sanitize at both widths, hosted-check, lint. Three bare-metal boots: logs/20261013-194827 (amd64), -195131 (aarch64), -195518 (riscv64). POST 538 of 538, word_count=317, dict_hash=0xe5a57f3eb72ff94a on all six; the three sessions are the same character for character. The boots were built with the working tree's uncommitted change to kernel/Makefile that adds the ISA to the kernel's version line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
102 lines
4.0 KiB
C
102 lines
4.0 KiB
C
/* hosted.c -- StarForth v4 as a Linux program: the nucleus image on the
|
|
* golden model, its capsules loaded as the system comes up (v4/include/v4/
|
|
* boot.h, the same boot the bare-metal kernel runs), the console on stdin
|
|
* and stdout.
|
|
*
|
|
* It runs until its input ends. Blocks are kept in memory and are gone
|
|
* when it stops.
|
|
*/
|
|
#include "v4/boot.h"
|
|
#include "block_subsystem.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
static v4_node n;
|
|
static v4_exec_state es;
|
|
static v4_heat h;
|
|
|
|
/* STORAGE. The node asks its kernel for its blocks (v4/blocks.h), and the
|
|
* kernel's block subsystem has them (docs/v4.0.0/MESH.md section 8). A
|
|
* Linux program is given what hosted v3 has when it is started with no
|
|
* disk: the chain's fast RAM, blocks 1 to 2047, which is gone when the
|
|
* program ends. */
|
|
static unsigned char block_ram[(size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE];
|
|
extern void sf_time_init(void);
|
|
|
|
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 KERNEL'S WORDS, for a node that is a Linux program: the words that
|
|
* are C in hosted v3 because only the host can do them. */
|
|
static const char *const kernel_words[] = { "BYE" };
|
|
static int leaving;
|
|
|
|
static void serve(v4_node *node, unsigned request)
|
|
{
|
|
(void)node;
|
|
if (request == 1) { /* BYE ( -- ): as hosted v3, say so and leave */
|
|
fputs("Goodbye!\n", stdout);
|
|
leaving = 1;
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
static char line[1026];
|
|
v4_boot boot;
|
|
|
|
boot.n = &n; boot.es = &es; boot.h = &h; boot.im = &v4_capsule_image;
|
|
boot.out = console_out; boot.key = console_key;
|
|
boot.words = kernel_words; boot.word_count = sizeof kernel_words / sizeof kernel_words[0]; boot.serve = serve;
|
|
sf_time_init();
|
|
if (blk_subsys_init(block_ram, sizeof block_ram) != BLK_OK) { fputs("V4: no block storage\n", stderr); return 1; }
|
|
if (!v4_boot_run(&boot)) { fflush(stdout); return 1; }
|
|
|
|
for (;;) {
|
|
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);
|
|
/* The line is waiting for an answer no one here can send. The next
|
|
* line typed breaks the wait: the line that was waiting ends in an
|
|
* error, and then the line just typed is done (boot.h). */
|
|
while (how == V4_BOOT_LINE_WAITING) {
|
|
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_BOOT_LINE_TOO_LONG) { fputs("Line too long\n ERROR\n", stdout); how = V4_BOOT_LINE_WAITING; continue; }
|
|
if (how == V4_TEXT_ERROR) fputs(" ERROR\nok> ", stdout);
|
|
else if (how == V4_TEXT_COMPLETED) fputs(" ok\nok> ", stdout);
|
|
else break;
|
|
how = v4_boot_line(&boot, 0, 0);
|
|
}
|
|
if (how == V4_TEXT_COMPLETED) fputs(" ok\n", stdout);
|
|
if (leaving) { fflush(stdout); return 0; }
|
|
else if (how == V4_TEXT_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_REFUSED) fputs("Message refused\n ERROR\n", stdout); /* the node's wire is full: error 19's words */
|
|
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; }
|
|
}
|
|
}
|