feat(v4.0.0): POST is the kernel's -- the boot feeds the cases, and nothing of the harness is on the node
The boot runs POST with the runner (v4/system/post.c) after the capsules:
it feeds the 538 cases to the node and judges them from outside. A
case's printing no longer reaches the console. What the cases define
stays in the dictionary, as in v3; the system is sealed after POST and
the boot prints PARITY:V4_SYSTEM word_count=N dict_hash=..., as v3 prints
its parity after POST.
Gone: capsules/v4/post79.4th and its blocks 7000 up; the harness words;
(CATCH) and (EMIT-HOOK), with what EMIT and the prompt loop did for them.
The generator runs v3 on the lines as the kernel sends them, without the
capsule's "T| ". One expected result follows from that: >IN.initial
prints 6, not 9.
make -C v4 test, sanitize and hosted-check pass; amd64, aarch64 and
riscv64 boot, POST 538 of 538, word_count=411, dict_hash
0x6fb1d09418b189ee on all six: logs/20261007-105118, -105335, -105651.
T{ is unknown at the prompt; RS1 prints 42 42 before and after COLD. A
scratch build with one expectation changed names the case and ends
PARITY:FAIL, POST: FAILED.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
2bf958ce3a
commit
4bdb210a99
+51
-50
@@ -4,6 +4,7 @@
|
||||
* It is not part of the engine (v4/src): it needs the capsule directory,
|
||||
* which only a whole system has.
|
||||
*/
|
||||
#include "v4/post.h"
|
||||
#include "v4/blocks.h"
|
||||
#include "v4/boot.h"
|
||||
#include "v4/message.h"
|
||||
@@ -13,18 +14,18 @@
|
||||
#include "starkernel/capsule_blocks.h"
|
||||
|
||||
/* The capsules, in the order they are loaded. */
|
||||
/* POST is part of the boot: v4:post79.4th is loaded after the vocabulary it
|
||||
* tests and must end with a clean tally. -DV4_POST_AT_BOOT=0 leaves it
|
||||
* out, for work on a vocabulary that does not pass yet. */
|
||||
static const char *const boot_capsules[] = {
|
||||
"v4:forth79.4th",
|
||||
};
|
||||
|
||||
/* POST is part of the boot, and is the kernel's (v4/include/v4/post.h;
|
||||
* docs/v4.0.0/NUCLEUS.md 6.3): when the capsules are in, the kernel feeds
|
||||
* its cases to the node and judges them, and the boot passes only if none
|
||||
* failed. -DV4_POST_AT_BOOT=0 leaves it out, for work on a vocabulary
|
||||
* that does not pass yet. */
|
||||
#ifndef V4_POST_AT_BOOT
|
||||
#define V4_POST_AT_BOOT 1
|
||||
#endif
|
||||
static const char *const boot_capsules[] = {
|
||||
"v4:forth79.4th",
|
||||
#if V4_POST_AT_BOOT
|
||||
"v4:post79.4th",
|
||||
#endif
|
||||
};
|
||||
|
||||
#define LINE_MAX 1025u /* a line is at most 1024 characters, a block */
|
||||
#define STEP_LIMIT 4000000000ULL /* instruction words one line may take */
|
||||
@@ -77,37 +78,6 @@ static uint32_t word_count(const v4_node *n, const v4_image *im)
|
||||
|
||||
/* ---- 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
|
||||
* and the boot passes only if it has seen that line with fail=0. That no
|
||||
* line was refused is not enough: a POST that was broken half way would
|
||||
* refuse nothing. */
|
||||
static char post_line[96];
|
||||
static unsigned post_len;
|
||||
static int post_seen, post_clean;
|
||||
|
||||
static int text_at(const char *line, unsigned len, unsigned at, const char *want)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; want[i]; i++)
|
||||
if (at + i >= len || line[at + i] != want[i]) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void post_watch(char c)
|
||||
{
|
||||
static const char clean[] = " fail=0";
|
||||
if (c != '\n') {
|
||||
if (post_len < sizeof post_line) post_line[post_len++] = c;
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* The boot is the node's neighbour on two of its ports. On port 0 it is
|
||||
* the kernel: the node writes the number of a request there (ENGINE.md
|
||||
* 3.3) -- for a block (blocks.h), or for one of the host's words. On port
|
||||
@@ -120,7 +90,10 @@ static void post_watch(char c)
|
||||
/* What the kernel keeps of the node's block window (blocks.h). */
|
||||
static v4_blocks boot_blocks;
|
||||
|
||||
int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
|
||||
/* Hand the node a line and run it to its end. What it prints goes to the
|
||||
* console; or, if `keep` is given, into `keep`, at most `cap` characters,
|
||||
* and none of it is shown: *printed is then how many it printed in all. */
|
||||
static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep, unsigned cap, unsigned *printed)
|
||||
{
|
||||
static v4_message out, in; /* one line at a time: the boot is not re-entered */
|
||||
v4_node *n = b->n;
|
||||
@@ -155,8 +128,8 @@ int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
|
||||
if (v4_message_type(&in) == V4_MSG_OUTPUT) {
|
||||
for (i = 0; i < chars; i++) {
|
||||
char c = v4_message_char(&in, i);
|
||||
post_watch(c);
|
||||
b->out(&c, 1);
|
||||
if (!keep) b->out(&c, 1);
|
||||
else { if (*printed < cap) keep[*printed] = c; (*printed)++; }
|
||||
}
|
||||
} else if (v4_message_type(&in) == V4_MSG_DONE) {
|
||||
return (int)in.word[V4_MSG_HEADER];
|
||||
@@ -185,6 +158,27 @@ int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
|
||||
}
|
||||
}
|
||||
|
||||
int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
|
||||
{
|
||||
return run_line(b, text, len, 0, 0u, 0);
|
||||
}
|
||||
|
||||
#if V4_POST_AT_BOOT
|
||||
/* POST's host (post.h): a line is handed to the node as any line is, and
|
||||
* what the node prints is kept for the runner and not shown. */
|
||||
static int post_line(void *self, const char *text, unsigned text_len, char *out, unsigned cap, unsigned *len)
|
||||
{
|
||||
int how;
|
||||
*len = 0;
|
||||
how = run_line((const v4_boot *)self, text, text_len, out, cap, len);
|
||||
return (how == V4_TEXT_QUIT || how == V4_TEXT_COMPLETED || how == V4_TEXT_ERROR) ? how : -1;
|
||||
}
|
||||
static void post_say(void *self, const char *text, unsigned len)
|
||||
{
|
||||
((const v4_boot *)self)->out(text, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ---- one capsule --------------------------------------------------------- */
|
||||
|
||||
static void say_where(const v4_boot *b, const char *name, uint32_t block, uint32_t line)
|
||||
@@ -345,7 +339,6 @@ int v4_boot_run(const v4_boot *b)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
post_len = 0; post_seen = 0; post_clean = 0;
|
||||
/* a node is born with nothing in it, and is sent the nucleus */
|
||||
if (!v4_image_born(b->n, b->es, b->h, b->im)) {
|
||||
say(b, "V4: the nucleus is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
@@ -375,16 +368,24 @@ int v4_boot_run(const v4_boot *b)
|
||||
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
|
||||
if (!post_seen || !post_clean) {
|
||||
say(b, post_seen ? "V4: POST reported failures\n" : "V4: POST did not report\n");
|
||||
say(b, "PARITY:FAIL\nPOST: FAILED\n");
|
||||
return 0;
|
||||
{
|
||||
v4_post_host host;
|
||||
v4_post_tally tally;
|
||||
host.self = (void *)b; host.n = b->n; host.line = post_line; host.say = post_say;
|
||||
if (!v4_post_run(&host, v4_post_cases, v4_post_case_count, &tally)) {
|
||||
say(b, "V4: POST reported failures\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The kernel's words and the capsules are the system: COLD comes back
|
||||
* to here, and FORGET will not go below it. */
|
||||
/* The kernel's words, the capsules and what POST's cases defined are
|
||||
* the system, as in v3: COLD comes back to here, and FORGET will not go
|
||||
* below it. */
|
||||
v4_image_seal(b->n, b->im);
|
||||
say(b, "PARITY:V4_SYSTEM word_count="); say_dec(b, word_count(b->n, b->im));
|
||||
say(b, " dict_hash="); say_hex(b, v4_boot_dict_hash(b->n, b->im));
|
||||
say(b, "\n");
|
||||
|
||||
#if V4_POST_AT_BOOT
|
||||
say(b, "PARITY:OK\nPOST: PASSED\n");
|
||||
|
||||
@@ -877,7 +877,7 @@ const v4_post_case v4_post_cases[] = {
|
||||
{ "-TRAILING.empty", l318, 1u, V4_POST_STACK, 0, 0u, "\012", 1u },
|
||||
{ "CMOVE.basic", l319, 2u, V4_POST_STACK, 0, 0u, "65 66 \012", 7u },
|
||||
{ "CMOVE.empty", l320, 1u, V4_POST_STACK, 0, 0u, "", 0u },
|
||||
{ ">IN.initial", l321, 1u, V4_POST_STACK, 0, 0u, "9 \012", 3u },
|
||||
{ ">IN.initial", l321, 1u, V4_POST_STACK, 0, 0u, "6 \012", 3u },
|
||||
{ ">IN.advances", l322, 1u, V4_POST_STACK, 0, 0u, "0 \012", 3u },
|
||||
{ "..positive", l323, 1u, V4_POST_STACK, 0, 0u, "42 \012", 4u },
|
||||
{ "..negative", l324, 1u, V4_POST_STACK, 0, 0u, "-42 \012", 5u },
|
||||
|
||||
Reference in New Issue
Block a user