feat(v4.0.0): POST for FORTH-79, from v3's cases; not passing yet
capsules/v4/post79.4th: 537 of v3's POST cases for the FORTH-79 Required Word Set, each carrying what the hosted v3 binary did with the same line (error or not, the stack, the length and checksum of what it printed). Written by v4/tools/mkpost.py. The harness is FORTH-79 plus the two nucleus hooks. docs/v4.0.0/NUCLEUS.md section 6. - NODE-ERROR has a FORTH name: how a definition in FORTH raises an error - the boot passes POST only on seeing its tally line with fail=0 - POST is not in the boot yet (V4_POST_AT_BOOT=0); make -C v4 post runs it Result: tests=537 pass=439 fail=98. The 98 are not yet sorted into v4 defects and differences needing a ruling; v4/README.md has a first reading. Verified: make -C v4 test passes; hosted-check passes on three ISAs; clean qemu with STARFORTH_V4=1 on amd64, aarch64 and riscv64 reaches ok> with the same hashes as hosted (logs/20261005-1601xx..1604xx). Nothing was typed at a bare-metal prompt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ed6b11ad88
commit
5d6043e37e
+14
@@ -129,6 +129,20 @@ $(BINDIR)/boot-$(1).txt: $(BINDIR)/starforth4-$(1)
|
||||
endef
|
||||
$(foreach i,$(HOSTED_ISAS),$(eval $(call HOSTED_RULE,$(i))))
|
||||
|
||||
# `make post` builds the amd64 system with POST in its boot
|
||||
# (V4_POST_AT_BOOT=1, system/boot.c) and boots it: the POST capsule,
|
||||
# ../capsules/v4/post79.4th, is loaded after forth79.4th and must end with a
|
||||
# clean tally. `make post79` writes that capsule again from v3's cases
|
||||
# (tools/mkpost.py); it needs the hosted v3 binary.
|
||||
.PHONY: post post79
|
||||
$(BINDIR)/starforth4-post: $(HERE)/tools/hosted.c $(BINDIR)/v4_image_64.c $(CAPSULE_DIR_C) $(ENGINE_SRCS) $(SYSTEM_SRCS) $(wildcard $(HERE)/include/v4/*.h) $(HERE)/Makefile
|
||||
$(CC_amd64) $(CFLAGS) -D_POSIX_C_SOURCE=200809L -I$(HERE)/include -DV4_CELL_BITS=64 $(HOST_DEFS) -c $(HERE)/tools/hosted.c -o $(BINDIR)/hosted-post.o
|
||||
$(CC_amd64) $(SYSTEM_CFLAGS) -DV4_POST_AT_BOOT=1 -static $(BINDIR)/hosted-post.o $(BINDIR)/v4_image_64.c $(CAPSULE_DIR_C) $(ENGINE_SRCS) $(SYSTEM_SRCS) -o $@
|
||||
post: $(BINDIR)/starforth4-post
|
||||
@$(BINDIR)/starforth4-post < /dev/null
|
||||
post79:
|
||||
python3 $(HERE)/tools/mkpost.py
|
||||
|
||||
# `make hosted` builds the three. `make hosted-check` boots each with no
|
||||
# input and requires that all three print the same lines, ending in
|
||||
# PARITY:OK and the prompt: the same hashes on every ISA.
|
||||
|
||||
+46
-9
@@ -55,21 +55,58 @@ and fails unless their output is identical and ends in `PARITY:OK`.
|
||||
A capsule line the node does not answer ` ok` to ends the boot, naming the
|
||||
capsule, block and line, with `PARITY:FAIL` and `POST: FAILED`.
|
||||
|
||||
State, 2026-10-05: capsule loading works on all six builds. The three hosted
|
||||
binaries and the three bare-metal kernels (`clean qemu` with `STARFORTH_V4=1`)
|
||||
print the same four lines and reach `ok>`:
|
||||
State, 2026-10-05, after the POST work began: all six builds load
|
||||
`v4:forth79.4th` and reach `ok>` with the same lines:
|
||||
|
||||
```
|
||||
PARITY:V4_NUCLEUS words=292 image_hash=0x60b74e4f87adb0ac
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0xa4b74bdc7deaf204 capsule_hash=0xa4b74bdc7deaf204 dict_hash=0x0baed67626b4fac4
|
||||
PARITY:V4_NUCLEUS words=295 image_hash=0x3ed5d5fc83f0b026
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0xa4b74bdc7deaf204 capsule_hash=0xa4b74bdc7deaf204 dict_hash=0x118de9d37e682ea4
|
||||
PARITY:OK
|
||||
ok>
|
||||
```
|
||||
|
||||
Bare-metal logs: `logs/20261005-152955/amd64/`, `logs/20261005-154021/aarch64/`,
|
||||
`logs/20261005-154150/riscv64/`. Each run was ended once the prompt was in the
|
||||
Bare-metal logs: `logs/20261005-160144/amd64/`, `logs/20261005-160234/aarch64/`,
|
||||
`logs/20261005-160400/riscv64/`. Each run was ended once the prompt was in the
|
||||
log; nothing was typed at a bare-metal prompt, so keyboard input there is not
|
||||
yet verified. The capsules were unsigned (no signing key on this machine).
|
||||
|
||||
`forth79.4th` holds no definitions yet: all 292 words are still in the
|
||||
nucleus. There is no POST yet.
|
||||
`forth79.4th` holds no definitions yet: every word is still in the nucleus.
|
||||
|
||||
## POST
|
||||
|
||||
`capsules/v4/post79.4th` is v3's POST cases for the FORTH-79 Required Word
|
||||
Set, written by `v4/tools/mkpost.py` (`make -C v4 post79`): 537 cases in
|
||||
blocks 7000–7112, covering 111 of the 130 required words. Each case carries
|
||||
what the hosted v3 binary did with the same line — error or not, the stack,
|
||||
the length and checksum of what was printed — and v4 must do the same.
|
||||
|
||||
**POST does not pass yet, so it is not in the boot.** `make -C v4 post` builds
|
||||
the amd64 system with it (`-DV4_POST_AT_BOOT=1`) and boots it:
|
||||
|
||||
```
|
||||
PARITY:V4_POST tests=537 pass=439 fail=98
|
||||
PARITY:FAIL
|
||||
POST: FAILED
|
||||
```
|
||||
|
||||
The 98 are not yet sorted into v4 defects and differences that need a
|
||||
ruling. A first reading finds four kinds:
|
||||
|
||||
| Kind | About | Examples |
|
||||
|---|---|---|
|
||||
| The case prints or leaves a memory address, which differs between the two machines | 40 | `HERE`, `PAD`, `1 BLOCK`, `BL WORD`, `CONTEXT @ .`, `FIND` |
|
||||
| v3 departs from FORTH-79 and v4 follows the standard | 35 | `PICK` and `ROLL` count from 1; `<# # #S #>` take a double number; `LEAVE` ends the loop at the next `LOOP`; `>R` `R>` only inside a definition |
|
||||
| The case assumes v3's byte address units, where a v4 address unit is a cell (D-1) | 8 | `42 , HERE 8 - @`, `PAD 42 OVER !` |
|
||||
| Not yet understood | 15 | vocabulary cases, `LIST`'s layout, `16 BASE ! 255 .`, `321 EMIT`, cases v3 ends in an error and v4 does not |
|
||||
|
||||
Required words with no v3 case, and so no POST case: `U*` `U/MOD` `?`
|
||||
`EXECUTE` `KEY` `EXPECT` `QUERY` `U.` `CONVERT` `'` `."` `LITERAL` `STATE`
|
||||
`COMPILE` `[COMPILE]` `(` `QUIT` `BLK` `79-STANDARD`. `U*` and `U/MOD` are
|
||||
in neither v3 nor v4.
|
||||
|
||||
Twelve v3 cases are left out: three read the keyboard, two forget a word of
|
||||
the system, two store to the search order, and five leave v3 unable to
|
||||
continue in one session (`mkpost.py` prints the list).
|
||||
|
||||
The boot passes POST only on seeing the tally line with `fail=0`
|
||||
(`v4/system/boot.c`); that no line was refused is not enough.
|
||||
|
||||
@@ -68,6 +68,11 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
|
||||
\ FORTH-79: clear the return stack, set execution mode, return control to
|
||||
\ the terminal; no message is given. The data stack is left as it is.
|
||||
\ NODE-ERROR, by name: how a definition written in FORTH raises an error.
|
||||
\ A store of a code that is not zero is the trap of D-18 -- the word goes no
|
||||
\ further, the message for the code is printed (-1: the word has printed
|
||||
\ its own) and the line ends with ERROR. The codes are listed in core.v4.
|
||||
header NODE-ERROR inline : NODE-ERROR' NODE-ERROR ;
|
||||
header (CATCH) inline : (CATCH)' (CATCH) ;
|
||||
header (EMIT-HOOK) inline : (EMIT-HOOK)' (EMIT-HOOK) ;
|
||||
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
* the text goes. So the two start the same way and print the same lines:
|
||||
*
|
||||
* PARITY:V4_NUCLEUS words=N image_hash=0x...
|
||||
* PARITY:V4_CAPSULE name=... capsule_id=0x... capsule_hash=0x... dict_hash=0x...
|
||||
* PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x... capsule_hash=0x... dict_hash=0x...
|
||||
* PARITY:V4_POST tests=N pass=N fail=N (printed by the POST capsule)
|
||||
* PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0x... capsule_hash=0x... dict_hash=0x...
|
||||
* PARITY:OK
|
||||
* POST: PASSED
|
||||
* ok>
|
||||
*
|
||||
* or, if anything is wrong, what was wrong and then
|
||||
|
||||
+59
-2
@@ -11,7 +11,18 @@
|
||||
#include "starkernel/capsule_blocks.h"
|
||||
|
||||
/* The capsules, in the order they are loaded. */
|
||||
static const char *const boot_capsules[] = { "v4:forth79.4th" };
|
||||
/* POST is not yet part of the boot: v4:post79.4th does not pass yet
|
||||
* (v4/README.md). Build with -DV4_POST_AT_BOOT=1 to load it and to require
|
||||
* its clean tally; `make -C v4 post` does. */
|
||||
#ifndef V4_POST_AT_BOOT
|
||||
#define V4_POST_AT_BOOT 0
|
||||
#endif
|
||||
static const char *const boot_capsules[] = {
|
||||
"v4:forth79.4th",
|
||||
#if V4_POST_AT_BOOT
|
||||
"v4:post79.4th",
|
||||
#endif
|
||||
};
|
||||
|
||||
#define LINE_MAX 80u /* QUERY takes 80 characters, the new-line among them */
|
||||
#define STEP_LIMIT 4000000000ULL /* instruction words one line may take */
|
||||
@@ -105,6 +116,37 @@ typedef struct {
|
||||
unsigned len;
|
||||
} tail;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
static void tail_put(const v4_boot *b, tail *t, int show, char c)
|
||||
{
|
||||
unsigned i;
|
||||
@@ -130,7 +172,10 @@ static int run_to_prompt(const v4_boot *b, int show)
|
||||
for (;;) {
|
||||
(void)v4_exec_step_word(n, b->es, b->h);
|
||||
if (n->console_len) {
|
||||
for (i = 0; i < n->console_len; i++) tail_put(b, &t, show, (char)n->console[i]);
|
||||
for (i = 0; i < n->console_len; i++) {
|
||||
post_watch((char)n->console[i]);
|
||||
tail_put(b, &t, show, (char)n->console[i]);
|
||||
}
|
||||
n->console_len = 0;
|
||||
}
|
||||
if (n->stopped) { say(b, "\nV4: the node stopped on a fault\n"); return 0; }
|
||||
@@ -233,6 +278,7 @@ int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
post_len = 0; post_seen = 0; post_clean = 0;
|
||||
if (!v4_image_boot(b->n, b->es, b->h, b->im, disk, blocks)) {
|
||||
say(b, "V4: the nucleus image is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
return 0;
|
||||
@@ -247,7 +293,18 @@ int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks)
|
||||
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if V4_POST_AT_BOOT
|
||||
say(b, "PARITY:OK\nPOST: PASSED\nok> ");
|
||||
#else
|
||||
say(b, "PARITY:OK\nok> ");
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
Executable
+455
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user