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
+10
-7
@@ -1,11 +1,13 @@
|
||||
/* boot.h -- how a StarForth v4 system comes up: the nucleus, then its
|
||||
* capsules, then the prompt. docs/v4.0.0/NUCLEUS.md.
|
||||
/* boot.h -- how a StarForth v4 system comes up: the machine's ROM assembles
|
||||
* the system, then its capsules are loaded, then the prompt.
|
||||
* docs/v4.0.0/MACHINE.md, NUCLEUS.md.
|
||||
*
|
||||
* The hosted binary (tools/hosted.c) and the bare-metal kernel
|
||||
* (kernel/src/v4/sk_v4.c) both call v4_boot_run and differ only in where
|
||||
* the text goes. So the two start the same way and print the same lines:
|
||||
*
|
||||
* PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x... capsule_hash=0x... words=N
|
||||
* PARITY:V4_F18 name=v4:sys:map-64.f18 capsule_hash=0x... (one for each blob of the system's source)
|
||||
* PARITY:V4_ROM words=N rom_hash=0x... instructions=N (the ROM, and what assembling the system cost)
|
||||
* 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 (POST is the kernel's: v4/post.h)
|
||||
* PARITY:V4_SYSTEM word_count=N dict_hash=0x... (the system as sealed, after POST)
|
||||
@@ -39,13 +41,13 @@ typedef struct {
|
||||
v4_node *n;
|
||||
v4_exec_state *es;
|
||||
v4_heat *h;
|
||||
const v4_image *im; /* the nucleus */
|
||||
v4_image *im; /* the system: the map's description (v4_map_image), which the boot completes */
|
||||
void (*out)(const char *text, unsigned len); /* the console */
|
||||
int (*key)(void); /* a character for a line that reads the keyboard, or 0: there is none */
|
||||
|
||||
/* THE KERNEL'S WORDS (docs/v4.0.0/ENGINE.md 3.3). `words` names them,
|
||||
* `word_count` of them; the boot makes each a word on the node (KERNEL-
|
||||
* WORD, v4/capsule/compile.v4) whose request number is its place in the
|
||||
* WORD, capsules/v4/sys/compile.f18) whose request number is its place in the
|
||||
* list, counting from 1. When the node writes a request to its port it
|
||||
* is blocked, and `serve` is called with the number: it takes the
|
||||
* word's arguments from the node's data stack and leaves its results
|
||||
@@ -63,8 +65,9 @@ typedef struct {
|
||||
#define V4_BOOT_KEY_NONE (-1) /* not yet: it is asked again */
|
||||
#define V4_BOOT_KEY_END (-2) /* and there never will be */
|
||||
|
||||
/* A node is born empty (v4_image_born); it is sent the nucleus through its
|
||||
* port; and the capsules are loaded. Its blocks are the kernel's block
|
||||
/* A node is born empty (v4_image_born); its ROM assembles the system from
|
||||
* its source, capsules/v4/sys, fed to it through its console; and the
|
||||
* capsules are loaded. Its blocks are the kernel's block
|
||||
* subsystem's, which whoever calls this has set up (v4/blocks.h). Returns 1 with the node idle,
|
||||
* waiting to be handed a line; or 0, the failure printed. It prints no
|
||||
* prompt: that is the host's. */
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
* jump start
|
||||
*
|
||||
* This is the F18's own way of loading a node through a port. Nothing here
|
||||
* knows what the programme is: StarForth's nucleus is one such capsule
|
||||
* (v4/tools/mkimage.c), and anything written for the node is another.
|
||||
* knows what the programme is: StarForth's system, as the machine's ROM
|
||||
* assembled it, is one such capsule (what a node that is born is sent:
|
||||
* tests/test_host_unit.c), and anything written for the node is another.
|
||||
*/
|
||||
#ifndef V4_CAPSULE_H
|
||||
#define V4_CAPSULE_H
|
||||
|
||||
+41
-26
@@ -1,18 +1,19 @@
|
||||
/* image.h -- the StarForth nucleus, as its host knows it: where things are
|
||||
* in a node that has taken it in, and how a line is handed to that node.
|
||||
/* image.h -- the StarForth system, as its host knows it: where things are
|
||||
* in a node that holds it, and how a line is handed to that node.
|
||||
*
|
||||
* The nucleus is text (capsule/ *.v4). tools/mkimage.c assembles it on the
|
||||
* build machine and writes two things:
|
||||
* The system is text, the .f18 blobs of capsules/v4/sys, and the machine's
|
||||
* ROM assembles it when the machine starts (docs/v4.0.0/MACHINE.md; v4/
|
||||
* system/boot.c). Its host knows two things of it:
|
||||
*
|
||||
* - the nucleus as a capsule of F18 code (capsule.h), which is put into
|
||||
* an empty node through its port. It is a capsule like any other: in
|
||||
* the capsule directory, with a name, a hash and a signature
|
||||
* (capsules/v4/nucleus-<width>.f18). docs/v4.0.0/MESH.md section 5.
|
||||
* - this description of it, a C file: the addresses a host needs.
|
||||
* - the memory map, before it is built: v4_map_image, a C file that
|
||||
* tools/mkmap.c writes from the map (`mkmap image`);
|
||||
* - what only the assembled system knows -- where it waits, its fault
|
||||
* table, KEY -- from the image record, a fixed block of RAM whose
|
||||
* address is a constant of the map and which the system's source
|
||||
* stores (capsules/v4/sys/end.f18): v4_image_from_node.
|
||||
*
|
||||
* A node that has taken the nucleus in is sent text as messages and sends
|
||||
* back what it prints (v4/include/v4/message.h); nothing here hands it a
|
||||
* line.
|
||||
* A node that holds the system is sent text as messages and sends back
|
||||
* what it prints (v4/include/v4/message.h); nothing here hands it a line.
|
||||
*
|
||||
* A description is for one cell width, one node size and one pair of stack
|
||||
* sizes; v4_image_born refuses one the engine was not built for.
|
||||
@@ -26,9 +27,11 @@ typedef struct {
|
||||
/* what the image was built for */
|
||||
unsigned cell_bits, node_words, data_ring, ret_ring;
|
||||
|
||||
/* Where the nucleus waits for a message (v4/capsule/quit.v4, (IDLE)):
|
||||
* a node that has taken the nucleus in begins there, and with nothing
|
||||
* to do it is asleep at a wire inside it (operation 8, MESH.md 7d.3). */
|
||||
/* Where the system waits for a message (capsules/v4/sys/quit.f18,
|
||||
* (IDLE)): a node that holds the system begins there, and with nothing
|
||||
* to do it is asleep at a wire inside it (operation 8, MESH.md 7d.3).
|
||||
* This, fault_table, key_start and key_end are -1 until the system is
|
||||
* built (v4_image_from_node). */
|
||||
v4_cell idle;
|
||||
v4_cell fault_table; /* (FAULTS): where a fault goes */
|
||||
v4_cell port; /* word address of the node's port (node.h) */
|
||||
@@ -38,7 +41,7 @@ typedef struct {
|
||||
v4_cell key_start, key_end;
|
||||
|
||||
/* the memory-mapped registers (DECOMPOSITION.md section 7; D-4 leaves
|
||||
* the map to the loader, and this is the map the image was built with) */
|
||||
* the map to the loader, and this is the map the system is built with) */
|
||||
v4_cell console_tx, console_rx, console_status;
|
||||
v4_cell dstack_reg, rstack_reg;
|
||||
v4_cell node_error;
|
||||
@@ -50,24 +53,36 @@ typedef struct {
|
||||
|
||||
/* What COLD returns the system to and FORGET will not go below: two
|
||||
* cells, DP and LATEST as they were when the system was complete, and
|
||||
* the variable FENCE. The image has them as the nucleus alone; a system
|
||||
* that loads capsules when it boots sets them again when it has
|
||||
* the variable FENCE. The system's source sets them; a system that
|
||||
* loads capsules when it boots sets them again when it has
|
||||
* (v4_image_seal). */
|
||||
v4_cell boot_cells, fence;
|
||||
|
||||
/* the block window: the word address of the slots the kernel copies
|
||||
* blocks into (v4/blocks.h) */
|
||||
v4_cell block_window;
|
||||
|
||||
/* the image record (capsules/v4/sys/end.f18): four cells, where the
|
||||
* system waits, its fault table, KEY and the word after KEY; and the
|
||||
* ROM's two cells that say why it stopped, if it did: the code of its
|
||||
* error (0 if none) and the line it was on, counting from 0 */
|
||||
v4_cell record, rom_failed, rom_errline;
|
||||
} v4_image;
|
||||
|
||||
/* The nucleus's error for a node blocked at a port with no one on it
|
||||
* (v4/capsule/core.v4's list; node.h, v4_node_port_gone): "No one on that
|
||||
* port". */
|
||||
/* The system's error for a node blocked at a port with no one on it
|
||||
* (capsules/v4/sys/core.f18's list; node.h, v4_node_port_gone): "No one on
|
||||
* that port". */
|
||||
#define V4_ERROR_NO_ONE 18
|
||||
|
||||
/* The description of the nucleus built from v4/capsule (the generated file
|
||||
* defines it). */
|
||||
extern const v4_image v4_capsule_image;
|
||||
/* The memory map's description of the system (the generated file defines
|
||||
* it): what is known of it before it is built. */
|
||||
extern const v4_image v4_map_image;
|
||||
|
||||
/* What only the assembled system knows, read from the image record of the
|
||||
* node `n` that holds it into `im`, which the map's description is in.
|
||||
* Returns 1, or 0 if the record does not name addresses of the node's
|
||||
* memory (nothing is then changed). */
|
||||
int v4_image_from_node(const v4_node *n, v4_image *im);
|
||||
|
||||
/* The system is complete: what is in the dictionary now is what COLD comes
|
||||
* back to, and FORGET will not go below it. */
|
||||
@@ -79,9 +94,9 @@ void v4_image_seal(v4_node *n, const v4_image *im);
|
||||
* width, gives the same hash for the same dictionary. */
|
||||
uint64_t v4_image_dict_hash(const v4_node *n, const v4_image *im);
|
||||
|
||||
/* A node newly born (node.h), with the registers the nucleus expects of the
|
||||
/* A node newly born (node.h), with the registers the system expects of the
|
||||
* node it runs on. Its memory is empty and it is blocked
|
||||
* reading its ports: the nucleus is yet to be sent to it. Returns 1, or 0
|
||||
* reading its ports: its code is yet to be sent to it, or assembled on it. Returns 1, or 0
|
||||
* if the description is not for this build of the engine (nothing is then
|
||||
* done). */
|
||||
int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* a node like any other and cannot make one: she asks, by a write to the
|
||||
* port where her requests go (ENGINE.md 3.3), and the write blocks her until
|
||||
* she is answered. Each request is a word on her, made with KERNEL-WORD
|
||||
* (v4/capsule/compile.v4); its arguments are what it finds on her data
|
||||
* (capsules/v4/sys/compile.f18); its arguments are what it finds on her data
|
||||
* stack and its results are what is left there.
|
||||
*
|
||||
* These are the requests, in order. The n-th is request `first + n`, where
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*
|
||||
* This is for whatever is on the other end of a node's port and is not a
|
||||
* node -- a console, a host -- to speak what the nodes speak (v4/capsule/
|
||||
* core.v4 and quit.v4 are the same thing in the node). Nothing here uses
|
||||
* core.f18 and quit.f18 are the same thing in the node). Nothing here uses
|
||||
* the C library.
|
||||
*/
|
||||
#ifndef V4_MESSAGE_H
|
||||
|
||||
@@ -380,7 +380,7 @@ void v4_node_rom_attach(v4_node *n, const v4_cell *rom, unsigned words);
|
||||
* chose to, after taking its own arguments off);
|
||||
* - P becomes fault_vector + fault_kind. The handler table is six words,
|
||||
* one for each kind in the order below, each a jump to that kind's
|
||||
* handler (capsule/quit.v4, (FAULTS)).
|
||||
* handler (capsules/v4/sys/quit.f18, (FAULTS)).
|
||||
* With no table attached (fault_vector -1) the node stops instead: `stopped`
|
||||
* is set and v4_exec_step_word does nothing more until the node is reset or
|
||||
* a table is attached.
|
||||
|
||||
+23
-1
@@ -56,7 +56,21 @@
|
||||
* count byte, then its characters, four to a cell, low
|
||||
* byte first, zero-padded to a whole cell.
|
||||
*
|
||||
* header NAME a dictionary entry for NAME (capsule/dict.v4), laid down
|
||||
* set NAME VALUE an assembler variable: like a constant, but it may be
|
||||
* set again (the newest value is its value). In code it
|
||||
* is a literal.
|
||||
* here as a VALUE: the next word address, the word being built
|
||||
* ended first. Not something to assemble on its own.
|
||||
* store ADDR VALUE ADDR, a VALUE that is a word of RAM, is given VALUE: the
|
||||
* state the system is switched on in.
|
||||
* start NAME the end of the text: every branch must have its target,
|
||||
* and NAME, a word, is where the system starts
|
||||
* (v4_text_start). Nothing may follow.
|
||||
* These four, constant, #, and ", are the ROM's language
|
||||
* (docs/v4.0.0/MACHINE.md): the ROM assembles the same
|
||||
* text the same way.
|
||||
*
|
||||
* header NAME a dictionary entry for NAME (capsules/v4/sys/dict.f18), laid down
|
||||
* in front of the word that the next `:` starts, and
|
||||
* linked to the header before it. NAME is taken as it
|
||||
* stands -- it may be ( or \ or + -- and is at most 31
|
||||
@@ -123,6 +137,7 @@ typedef struct {
|
||||
unsigned pool_used;
|
||||
|
||||
v4_cell latest; /* xt of the newest header, 0 if none */
|
||||
v4_cell start; /* the word `start` named, -1 if none yet */
|
||||
int header_pending; /* a header waits for its `:` */
|
||||
|
||||
unsigned line;
|
||||
@@ -155,6 +170,13 @@ v4_cell v4_text_word(const v4_text *t, const char *name);
|
||||
* dictionary's LATEST starts as. */
|
||||
v4_cell v4_text_latest(const v4_text *t);
|
||||
|
||||
/* The value of the constant or set variable NAME: 1 and *value, or 0 if
|
||||
* NAME is neither. */
|
||||
int v4_text_value(const v4_text *t, const char *name, v4_cell *value);
|
||||
|
||||
/* The word `start` named, or -1 if the text has had no start. */
|
||||
v4_cell v4_text_start(const v4_text *t);
|
||||
|
||||
/* The next free word address. */
|
||||
v4_cell v4_text_here(v4_text *t);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user