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:
rajames
2026-10-15 14:26:02 -04:00
co-authored by Claude Opus 5.5
parent 1bf19d6224
commit a73580e949
69 changed files with 1164 additions and 4025 deletions
+31 -1
View File
@@ -424,7 +424,7 @@ int main(void)
CHECK(refused(": T &VAR ;", "already defined", 1), "and be a word");
CHECK(refused(": &X 1 ;", "cannot be used as a name", 1), "a name cannot begin with &");
/* headers: a dictionary entry in front of a word (capsule/dict.v4) */
/* headers: a dictionary entry in front of a word (capsules/v4/sys/dict.f18) */
CHECK(ok("header DUP : (DUP) dup + ;"), "a header assembles: %s", v4_text_error(&tx));
{
v4_cell xt = v4_text_word(&tx, "(DUP)");
@@ -521,6 +521,36 @@ int main(void)
"#, and \", are reserved");
}
/* ---- `set`, `here`, `store` and `start`: the rest of the ROM's language ---- */
{
v4_cell v = 0;
CHECK(ok("set V 3 set V 4 #, V") && nb.mem[ORIGIN] == 4 && v4_text_value(&tx, "V", &v) && v == 4,
"a set variable may be set again; the newest value is its value: %s", v4_text_error(&tx));
CHECK(ok("set V 7 : T V ;") && result("T", 0, 0, 0) == 7, "a set variable in code is a literal");
CHECK(ok("constant K 2 set V K+1 #, V") && nb.mem[ORIGIN] == 3, "set takes a VALUE");
CHECK(ok(": T ; set H here #, H set H here #, H") && nb.mem[ORIGIN + 1] == ORIGIN + 1 && nb.mem[ORIGIN + 2] == ORIGIN + 2,
"here is the next word address, the word before ended");
CHECK(ok(": T dup set H here ; #, H") && nb.mem[ORIGIN + 2] == ORIGIN + 1, "here inside a word ends the instruction word");
CHECK(ok("set V 1 #, V+1") == 0, "CONST+N is for constants only");
CHECK(refused("constant K 1 set K 2", "already defined", 1), "set over a constant is refused");
CHECK(refused("set V 1 constant V 2", "already defined", 1), "a constant over a set variable is refused");
CHECK(refused("set V", "set without a value", 1), "set needs a value");
CHECK(refused(": T here ;", "here is only a value", 1), "here alone is not something to assemble");
CHECK(ok("constant A 300 store A 5 store A+1 &T : T ;") == 0, "store's &NAME needs a word already defined");
CHECK(ok(": T ; constant A 300 store A 5 store A+1 &T") && nb.mem[300] == 5 && nb.mem[301] == ORIGIN,
"store puts a VALUE anywhere in RAM");
CHECK(refused("store -1 5", "address out of range", 1), "store outside RAM is refused");
CHECK(refused("store 5", "store without a value", 1), "store needs a value");
CHECK(ok(": A ; : B ; start B") && v4_text_start(&tx) == ORIGIN + 1, "start names the word the system starts at");
CHECK(ok(": A ;") && v4_text_start(&tx) == -1, "no start: -1");
CHECK(refused(": A C ; start A", "never defined", 1), "at start every branch has its target");
CHECK(refused("constant K 1 start K", "start of something not a word", 1), "start of a constant is refused");
CHECK(refused(": A ; start A : B ;", "text after start", 1), "nothing comes after start");
CHECK(refused(": set ;", "cannot be used as a name", 1) && refused(": here ;", "cannot be used as a name", 1)
&& refused(": store ;", "cannot be used as a name", 1) && refused(": start ;", "cannot be used as a name", 1),
"set, here, store and start are reserved");
}
CHECK(v4_node_guards_intact(&na) && v4_node_guards_intact(&nb), "guards intact");
printf(" %d checks, %d failures\n", checks, failures);