feat(v4): the machine boots from its ROM; the prebuilt nucleus is gone
The host node starts in its ROM (entry 12), and the boot feeds it the system's source through the console: capsules/v4/sys/map-64.f18, the fourteen .f18 blobs and end.f18, each found, hash-checked and signature-checked as any capsule, then EOT. The ROM assembles the system and starts it at (IDLE); the host reads the image record (v4_image_from_node) for what only the assembled system knows. A ROM error is reported with its blob, line and meaning, then PARITY:FAIL. Deleted: v4/tools/mkimage.c, capsules/v4/nucleus-64.f18, v4/capsule/*.v4, v4/tools/v4tof18.py. The .f18 files are the source now. - text.c speaks the rest of the ROM's language (set, here, store, start; v4_text_value, v4_text_start), so the oracle reads the same source: test_host_rom_nucleus compares the ROM against text.c on the .f18 files, word for word, image record included. Host tests load the .f18 files. - mkmap image writes v4_map_image (the map's description of the system); hosted and the kernel link the ROM and the map instead of v4_image_64.c. - Hera (Captain Bob's ruling, option 1): births stream the system the ROM assembled, as assembled code; hera.4th is unchanged. test_host_unit's host builds the system with the ROM. - New parity lines: PARITY:V4_F18 per blob, PARITY:V4_ROM words=2049 rom_hash=0xa6c792b5821010b7 instructions=60725216. Acceptance: amd64, aarch64, riscv64 boot to ok> with PARITY:V4_POST tests=538 pass=538 fail=0, POST: PASSED, and PARITY:V4_SYSTEM word_count=317 dict_hash=0xe5a57f3eb72ff94a on all three -- the same hash as the prebuilt nucleus gave. make -C v4 test test-32 hosted-check: all pass. Logs: logs/20261015-141648, -141938, -142350. Startup cost under QEMU TCG, the ROM's assembly (last V4_F18 line to V4_ROM): amd64 26.7 s of 132 s to ok>; aarch64 41.0 s of 195 s; riscv64 20.5 s of 101 s. Hosted native: +0.85 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1bf19d6224
commit
a73580e949
@@ -1,13 +1,13 @@
|
||||
/* test_host_rom_nucleus.c -- the proof: the ROM rebuilds today's nucleus, word for word.
|
||||
/* test_host_rom_nucleus.c -- the proof: the ROM assembles the system as text.c does, word for word.
|
||||
*
|
||||
* Plan Task 8; MACHINE.md section 6, step 1. Node A is built as
|
||||
* v4/tools/mkimage.c builds the nucleus: text.c assembles v4/capsule/ *.v4,
|
||||
* then the switch-on state is set in C. Node B is the machine with its ROM,
|
||||
* fed the system's blobs -- capsules/v4/sys/map-<width>.f18, the fourteen
|
||||
* .f18 files made from the .v4 ones, and end.f18 -- through the console.
|
||||
* When the ROM starts the system, every word of B's RAM must be A's, except
|
||||
* the ROM's own state area and the image record (which A does not have, and
|
||||
* which must hold A's addresses). It also reports what the assembly cost:
|
||||
* Plan Task 8 and 9; MACHINE.md section 6, step 1. The system's source is
|
||||
* capsules/v4/sys: map-<width>.f18, the fourteen .f18 files and end.f18.
|
||||
* Node A is that source assembled off the node by text.c, the oracle (it
|
||||
* speaks the ROM's language: constant, set, here, #, ", store, start).
|
||||
* Node B is the machine with its ROM, fed the same source through the
|
||||
* console. When the ROM starts the system, every word of B's RAM must be
|
||||
* A's, except the ROM's own state area (which A does not have), and B must
|
||||
* start where A's start says. It also reports what the assembly cost:
|
||||
* instructions retired and seconds (Captain Bob: watch the startup cost).
|
||||
*/
|
||||
#include "rom_harness.h"
|
||||
@@ -20,118 +20,55 @@ static int failures = 0, checks = 0;
|
||||
|
||||
static v4_node a, b;
|
||||
static v4_text tx;
|
||||
static const char *const files[] = { "core", "input", "dict", "codegen", "compile", "quit", "forth",
|
||||
"numout", "words", "system", "qmath", "blocks", "log", "acl" };
|
||||
|
||||
/* node A: mkimage.c's main, as it is */
|
||||
/* node A: text.c assembles the system's source, map first and end last */
|
||||
static int build_a(void)
|
||||
{
|
||||
static const char *const v4files[] = { "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" };
|
||||
v4_cell k;
|
||||
if (!host_load(&tx, &a, v4files, 14) || !v4_text_finish(&tx)) return 0;
|
||||
a.mem[DP] = DICT_W * 4;
|
||||
a.mem[LATEST] = v4_text_latest(&tx);
|
||||
a.mem[STATE] = 0;
|
||||
a.mem[CFP] = CFS_W;
|
||||
a.mem[BASE] = 10;
|
||||
a.mem[FENCE] = DICT_W;
|
||||
a.mem[LOG_LEVEL] = 2;
|
||||
a.mem[CONTEXT] = LATEST;
|
||||
a.mem[CURRENT] = LATEST;
|
||||
a.mem[SRC] = TIB;
|
||||
a.mem[BOOT_CELLS] = a.mem[DP];
|
||||
a.mem[BOOT_CELLS + 1] = a.mem[LATEST];
|
||||
a.mem[FENCE] = (a.mem[DP] + 3) / 4;
|
||||
a.mem[NODE_ERROR] = 0;
|
||||
a.mem[BLK] = 0; a.mem[SCR] = 0; a.mem[SRC] = TIB; a.mem[SRC_HOOK] = 0;
|
||||
for (k = BVARS; k < BVARS + 6; k++) a.mem[k] = 0;
|
||||
for (k = BUF0_W; k < BUF0_W + (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS); k++) a.mem[k] = 0;
|
||||
for (k = TIB_W; k < TIB_W + 260; k++) a.mem[k] = 0;
|
||||
for (k = WBUF_W; k < WBUF_W + WBUF_CELLS; k++) a.mem[k] = 0;
|
||||
for (k = PAD_W; k < PAD_W + 21; k++) a.mem[k] = 0;
|
||||
a.mem[TO_IN] = 0; a.mem[SPAN] = 0;
|
||||
a.mem[LINE_STATUS] = 1;
|
||||
HOST_MESSAGES_EMPTY(&a);
|
||||
return 1;
|
||||
char path[512];
|
||||
unsigned i;
|
||||
v4_node_reset(&a);
|
||||
v4_text_begin(&tx, &a, ROM_ORIGIN);
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (i == 0) snprintf(path, sizeof path, "%s/map-%d.f18", V4_SYS_DIR, V4_CELL_BITS);
|
||||
else if (i <= 14) snprintf(path, sizeof path, "%s/%s.f18", V4_SYS_DIR, rom_system_files[i - 1]);
|
||||
else snprintf(path, sizeof path, "%s/end.f18", V4_SYS_DIR);
|
||||
if (!v4_text_assemble_file(&tx, path)) return 0;
|
||||
}
|
||||
return v4_text_start(&tx) >= 0;
|
||||
}
|
||||
|
||||
/* the ROM's own cells, and the image record: not compared */
|
||||
/* the ROM's own cells: not compared */
|
||||
static int excluded(v4_cell w)
|
||||
{
|
||||
return (w >= OUT_W - 144 && w < OUT_W - 64)
|
||||
|| (w >= BUF0_W - 32 && w < BUF0_W - 22)
|
||||
|| (w >= BUF0_W - 122 && w < BUF0_W - 119)
|
||||
|| (w >= ROM_IMG && w < ROM_IMG + 4);
|
||||
}
|
||||
|
||||
static char *slurp(const char *name, size_t *len)
|
||||
{
|
||||
char path[512];
|
||||
FILE *f;
|
||||
char *t;
|
||||
long n;
|
||||
snprintf(path, sizeof path, "%s/%s", V4_SYS_DIR, name);
|
||||
f = fopen(path, "rb");
|
||||
if (!f) { printf(" cannot read %s\n", path); return NULL; }
|
||||
fseek(f, 0, SEEK_END); n = ftell(f); fseek(f, 0, SEEK_SET);
|
||||
t = malloc((size_t)n + 2);
|
||||
if (fread(t, 1, (size_t)n, f) != (size_t)n) { fclose(f); free(t); return NULL; }
|
||||
fclose(f);
|
||||
t[n] = '\n'; t[n + 1] = 0; /* a blob that does not end with a new line still ends a token */
|
||||
*len = (size_t)n + 1;
|
||||
return t;
|
||||
|| (w >= BUF0_W - 122 && w < BUF0_W - 119);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
static char *text;
|
||||
size_t total = 0, cap = 1u << 20, len, fed = 0;
|
||||
char name[32];
|
||||
unsigned i;
|
||||
long words = 0;
|
||||
clock_t t0, t1;
|
||||
v4_cell w, idle_a;
|
||||
|
||||
CHECK(build_a(), "node A builds as mkimage.c builds it: %s", v4_text_error(&tx));
|
||||
idle_a = v4_text_word(&tx, "(IDLE)");
|
||||
CHECK(build_a(), "text.c assembles the system's source to its start: %s", v4_text_error(&tx));
|
||||
idle_a = v4_text_start(&tx);
|
||||
CHECK(idle_a == v4_text_word(&tx, "(IDLE)"), "the system starts at (IDLE)");
|
||||
|
||||
/* the system's source, as the loader will feed it: map, the fourteen, end, EOT */
|
||||
text = malloc(cap);
|
||||
snprintf(name, sizeof name, "map-%d.f18", V4_CELL_BITS);
|
||||
for (i = 0; i < 16; i++) {
|
||||
char *t;
|
||||
if (i == 0) t = slurp(name, &len);
|
||||
else if (i <= 14) { char f18[32]; snprintf(f18, sizeof f18, "%s.f18", files[i - 1]); t = slurp(f18, &len); }
|
||||
else t = slurp("end.f18", &len);
|
||||
if (!t) { CHECK(0, "a blob is missing"); return 1; }
|
||||
memcpy(text + total, t, len); total += len; free(t);
|
||||
}
|
||||
text[total++] = 4;
|
||||
|
||||
rom_boot(&b);
|
||||
v4_node_stack_regs_attach(&b, DSTACK_REG, RSTACK_REG);
|
||||
b.p = V4_ROM_BASE + 12;
|
||||
t0 = clock();
|
||||
while (b.p >= V4_ROM_BASE && words < 400000000L && !b.stopped) {
|
||||
if (fed < total) fed += v4_node_console_feed(&b, text + fed, (unsigned)(total - fed));
|
||||
(void)v4_exec_step_word(&b, &rom_es, &rom_h);
|
||||
words++;
|
||||
if (words % 1000 == 0 && rom_in(&b, "(ROM-FAILED)")) break;
|
||||
}
|
||||
t1 = clock();
|
||||
CHECK(b.p < V4_ROM_BASE, "the ROM started the system: P %ld, failed %ld at line %ld", (long)b.p,
|
||||
CHECK(rom_build_system(&b, &words), "the ROM started the system: P %ld, failed %ld at line %ld", (long)b.p,
|
||||
(long)b.mem[ROM_FAILED], (long)b.mem[ROM_ERRLINE] + 1);
|
||||
t1 = clock();
|
||||
printf(" the ROM assembled the system: %lu instructions, %lu instruction words, %.2f s (%d-bit cells)\n",
|
||||
(unsigned long)rom_es.anticlock, (unsigned long)words, (double)(t1 - t0) / CLOCKS_PER_SEC, V4_CELL_BITS);
|
||||
|
||||
CHECK(b.p == idle_a, "it starts at (IDLE): %ld, mkimage's %ld", (long)b.p, (long)idle_a);
|
||||
CHECK(b.p == idle_a, "it starts where text.c's start says: %ld, text.c's %ld", (long)b.p, (long)idle_a);
|
||||
CHECK(b.mem[ROM_IMG] == idle_a && b.mem[ROM_IMG + 1] == v4_text_word(&tx, "(FAULTS)")
|
||||
&& b.mem[ROM_IMG + 2] == v4_text_word(&tx, "KEY") && b.mem[ROM_IMG + 3] == v4_text_word(&tx, "CR"),
|
||||
"the image record holds mkimage's addresses");
|
||||
"the image record holds text.c's addresses");
|
||||
for (w = 0; w < (v4_cell)V4_NODE_WORDS; w++) {
|
||||
if (excluded(w) || a.mem[w] == b.mem[w]) continue;
|
||||
CHECK(0, "RAM word %ld: mkimage %lx, ROM %lx", (long)w, (unsigned long)a.mem[w], (unsigned long)b.mem[w]);
|
||||
CHECK(0, "RAM word %ld: text.c %lx, ROM %lx", (long)w, (unsigned long)a.mem[w], (unsigned long)b.mem[w]);
|
||||
break;
|
||||
}
|
||||
CHECK(w == (v4_cell)V4_NODE_WORDS, "every word of RAM is the same");
|
||||
|
||||
Reference in New Issue
Block a user