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
+75
-8
@@ -5,7 +5,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
enum { KIND_WORD = 1, KIND_CONST, KIND_MACRO };
|
||||
enum { KIND_WORD = 1, KIND_CONST, KIND_MACRO, KIND_SET }; /* the ROM's kinds: 1 word, 2 constant, 3 macro, 4 set variable */
|
||||
|
||||
static const char *const mnemonic[V4_OPCODE_COUNT] = {
|
||||
";", "ex", "jump", "call", "unext", "next", "if", "-if",
|
||||
@@ -50,7 +50,7 @@ static const v4_text_symbol *find(const v4_text *t, const char *s)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The entry flags a `header` may be given (capsule/dict.v4). */
|
||||
/* The entry flags a `header` may be given (capsules/v4/sys/dict.f18). */
|
||||
static v4_cell flag_of(const char *s)
|
||||
{
|
||||
if (strcmp(s, "immediate") == 0) return 1;
|
||||
@@ -66,7 +66,8 @@ static int is_reserved(const char *s)
|
||||
|| strcmp(s, "\\") == 0 || strcmp(s, "(") == 0
|
||||
|| strcmp(s, "FOR") == 0 || strcmp(s, "NEXT") == 0 || strcmp(s, "UNEXT") == 0
|
||||
|| strcmp(s, "header") == 0 || strcmp(s, "constant") == 0
|
||||
|| strcmp(s, "#,") == 0 || strcmp(s, "\",") == 0 || flag_of(s) != 0;
|
||||
|| strcmp(s, "#,") == 0 || strcmp(s, "\",") == 0 || strcmp(s, "set") == 0 || strcmp(s, "here") == 0
|
||||
|| strcmp(s, "store") == 0 || strcmp(s, "start") == 0 || flag_of(s) != 0;
|
||||
}
|
||||
|
||||
static int is_label_def(const char *s)
|
||||
@@ -189,7 +190,7 @@ static void lay_name(v4_text *t, const char *name)
|
||||
}
|
||||
}
|
||||
|
||||
/* `header NAME`: a dictionary entry for NAME, as capsule/dict.v4 lays one
|
||||
/* `header NAME`: a dictionary entry for NAME, as capsules/v4/sys/dict.f18 lays one
|
||||
* out, in front of the word the next `:` starts:
|
||||
* the name: its count byte, then its characters, four bytes to a cell,
|
||||
* zero-padded to a whole cell
|
||||
@@ -366,12 +367,14 @@ static void constant_directive(v4_text *t, reader *r)
|
||||
define(t, name, KIND_CONST, v);
|
||||
}
|
||||
|
||||
/* A VALUE in a directive: a number, a constant, CONST+N, or &NAME of a word
|
||||
* already defined. 1 and its value, or 0 having failed. */
|
||||
/* A VALUE in a directive: a number, a constant, a set variable, CONST+N,
|
||||
* here, or &NAME of a word already defined. 1 and its value, or 0 having
|
||||
* failed. */
|
||||
static int value_of(v4_text *t, const char *tok, v4_cell *out)
|
||||
{
|
||||
const v4_text_symbol *s;
|
||||
int big;
|
||||
if (strcmp(tok, "here") == 0) { *out = v4_asm_label(&t->as); return 1; }
|
||||
if (number_of(tok, out, &big)) {
|
||||
if (big) { fail(t, "number does not fit a cell", tok); return 0; }
|
||||
return 1;
|
||||
@@ -383,7 +386,7 @@ static int value_of(v4_text *t, const char *tok, v4_cell *out)
|
||||
return 0;
|
||||
}
|
||||
s = find(t, tok);
|
||||
if (s && s->kind == KIND_CONST) { *out = s->value; return 1; }
|
||||
if (s && (s->kind == KIND_CONST || s->kind == KIND_SET)) { *out = s->value; return 1; }
|
||||
if (!s) {
|
||||
const char *plus = strrchr(tok, '+');
|
||||
if (plus && plus != tok && plus[1] != 0) {
|
||||
@@ -428,6 +431,51 @@ static void string_directive(v4_text *t, reader *r)
|
||||
check_asm(t, tok);
|
||||
}
|
||||
|
||||
/* `set NAME VALUE`: an assembler variable, set again if it is one already. */
|
||||
static void set_directive(v4_text *t, reader *r)
|
||||
{
|
||||
char name[V4_TEXT_MAX_TOKEN + 1u], tok[V4_TEXT_MAX_TOKEN + 1u];
|
||||
v4_cell v;
|
||||
v4_text_symbol *s;
|
||||
|
||||
if (r->depth != 1) { fail(t, "not allowed in a macro", "set"); return; }
|
||||
if (!next_token(t, r, name)) { if (!t->failed) fail(t, "set without a name", NULL); return; }
|
||||
if (!next_token(t, r, tok)) { if (!t->failed) fail(t, "set without a value", name); return; }
|
||||
if (!value_of(t, tok, &v)) return;
|
||||
s = (v4_text_symbol *)find(t, name);
|
||||
if (s && s->kind == KIND_SET) { s->value = v; return; }
|
||||
define(t, name, KIND_SET, v);
|
||||
}
|
||||
|
||||
/* `store ADDR VALUE`: a word of RAM is given VALUE. */
|
||||
static void store_directive(v4_text *t, reader *r)
|
||||
{
|
||||
char tok[V4_TEXT_MAX_TOKEN + 1u];
|
||||
v4_cell addr, v;
|
||||
|
||||
if (r->depth != 1) { fail(t, "not allowed in a macro", "store"); return; }
|
||||
if (!next_token(t, r, tok)) { if (!t->failed) fail(t, "store without an address", NULL); return; }
|
||||
if (!value_of(t, tok, &addr)) return;
|
||||
if (!next_token(t, r, tok)) { if (!t->failed) fail(t, "store without a value", NULL); return; }
|
||||
if (!value_of(t, tok, &v)) return;
|
||||
if (addr < 0 || (v4_ucell)addr >= V4_NODE_WORDS) { fail(t, "address out of range", tok); return; }
|
||||
v4_node_store(t->as.n, addr, v);
|
||||
}
|
||||
|
||||
/* `start NAME`: the text has ended; NAME is where the system starts. */
|
||||
static void start_directive(v4_text *t, reader *r)
|
||||
{
|
||||
char name[V4_TEXT_MAX_TOKEN + 1u];
|
||||
const v4_text_symbol *s;
|
||||
|
||||
if (r->depth != 1) { fail(t, "not allowed in a macro", "start"); return; }
|
||||
if (!next_token(t, r, name)) { if (!t->failed) fail(t, "start without a name", NULL); return; }
|
||||
if (!v4_text_finish(t)) return;
|
||||
s = find(t, name);
|
||||
if (!s || s->kind != KIND_WORD) { fail(t, "start of something not a word", name); return; }
|
||||
t->start = s->value;
|
||||
}
|
||||
|
||||
static void one_token(v4_text *t, reader *r, const char *tok)
|
||||
{
|
||||
const v4_text_symbol *s;
|
||||
@@ -435,6 +483,7 @@ static void one_token(v4_text *t, reader *r, const char *tok)
|
||||
v4_cell v;
|
||||
int op, big;
|
||||
|
||||
if (t->start >= 0) { fail(t, "text after start", tok); return; }
|
||||
if (strcmp(tok, "header") == 0) { header(t, r); return; }
|
||||
if (t->header_pending) {
|
||||
v4_cell f = flag_of(tok);
|
||||
@@ -454,6 +503,10 @@ static void one_token(v4_text *t, reader *r, const char *tok)
|
||||
if (strcmp(tok, "constant") == 0) { constant_directive(t, r); return; }
|
||||
if (strcmp(tok, "#,") == 0) { cell_directive(t, r); return; }
|
||||
if (strcmp(tok, "\",") == 0) { string_directive(t, r); return; }
|
||||
if (strcmp(tok, "set") == 0) { set_directive(t, r); return; }
|
||||
if (strcmp(tok, "store") == 0) { store_directive(t, r); return; }
|
||||
if (strcmp(tok, "start") == 0) { start_directive(t, r); return; }
|
||||
if (strcmp(tok, "here") == 0) { fail(t, "here is only a value", NULL); return; }
|
||||
if (strcmp(tok, "endmacro") == 0) { fail(t, "endmacro without macro", NULL); return; }
|
||||
|
||||
if (is_label_def(tok)) {
|
||||
@@ -536,7 +589,7 @@ static void one_token(v4_text *t, reader *r, const char *tok)
|
||||
check_asm(t, tok);
|
||||
return;
|
||||
}
|
||||
if (s && s->kind == KIND_CONST) {
|
||||
if (s && (s->kind == KIND_CONST || s->kind == KIND_SET)) {
|
||||
v4_asm_lit(as, s->value);
|
||||
check_asm(t, tok);
|
||||
return;
|
||||
@@ -572,6 +625,7 @@ void v4_text_begin(v4_text *t, v4_node *n, v4_cell origin)
|
||||
t->failed = 0;
|
||||
t->message[0] = 0;
|
||||
t->latest = 0;
|
||||
t->start = -1;
|
||||
t->header_pending = 0;
|
||||
}
|
||||
|
||||
@@ -654,6 +708,19 @@ v4_cell v4_text_latest(const v4_text *t)
|
||||
return t->latest;
|
||||
}
|
||||
|
||||
int v4_text_value(const v4_text *t, const char *name, v4_cell *value)
|
||||
{
|
||||
const v4_text_symbol *s = find(t, name);
|
||||
if (!s || (s->kind != KIND_CONST && s->kind != KIND_SET)) return 0;
|
||||
*value = s->value;
|
||||
return 1;
|
||||
}
|
||||
|
||||
v4_cell v4_text_start(const v4_text *t)
|
||||
{
|
||||
return t->start;
|
||||
}
|
||||
|
||||
v4_cell v4_text_here(v4_text *t)
|
||||
{
|
||||
return v4_asm_label(&t->as);
|
||||
|
||||
Reference in New Issue
Block a user