Files
LithosAnanake/v4/include/v4/boot.h
T
rajamesandClaude Opus 5.5 a73580e949 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>
2026-10-15 14:26:02 -04:00

102 lines
5.5 KiB
C

/* 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_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)
* PARITY:OK
* POST: PASSED
*
* or, if anything is wrong, what was wrong and then
*
* PARITY:FAIL
* POST: FAILED
*
* A capsule is found by name in the directory tools/mkcapsule.c baked into
* the binary, its hash is recomputed, its signature is checked -- one that
* does not verify refuses the capsule, a missing one is only reported, as
* for v3's capsules -- and its blocks are handed to the node a line at a
* time (docs/v4.0.0/ENGINE.md 3.1). Every line must complete; the first
* that ends in an error, or in QUIT, ends the boot. What a line prints is
* shown.
*
* The dictionary hash is FNV-1a over the node's memory below HERE, a cell at
* a time, low byte first, then LATEST. It does not depend on the machine:
* every build of one commit, with one cell width, prints the same hashes.
*/
#ifndef V4_BOOT_H
#define V4_BOOT_H
#include "v4/image.h"
#include "v4/message.h"
typedef struct {
v4_node *n;
v4_exec_state *es;
v4_heat *h;
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, 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
* there, and when it returns the node goes on. The kernel's two block
* requests (v4/blocks.h) are served for every node and are not in the
* list. Any other request is an error on the node: 12, Argument out of
* range. */
const char *const *words;
unsigned word_count;
void (*serve)(v4_node *n, unsigned request);
} v4_boot;
/* What `key` returns when it has no character. */
#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); 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. */
int v4_boot_run(const v4_boot *b);
/* Hand the node a line and run it to its end, sending what it prints to
* `out`. The line is put on the node's wire as a message, whole, and what
* it prints comes back as messages (message.h; docs/v4.0.0/MESH.md 7d.5). Returns how it ended -- V4_TEXT_QUIT,
* V4_TEXT_COMPLETED or V4_TEXT_ERROR -- or one of these: */
#define V4_BOOT_LINE_TOO_LONG (-1) /* more than the node's input buffer holds; nothing was run */
#define V4_BOOT_LINE_STOPPED (-2) /* the node stopped on a fault, or never finished */
#define V4_BOOT_LINE_NO_INPUT (-3) /* the line read the keyboard and there is no more input */
#define V4_BOOT_LINE_WAITING (-4) /* the line is waiting for an answer (AWAIT) that no one here can send: see below */
#define V4_BOOT_LINE_REFUSED (-5) /* the node's wire has no room for the line: it was not put, and nothing was run. The node
* is taking nothing -- it waits for an answer and does not know these lines for its
* console's -- and its wire has filled (MESH.md 7d.6). It has not stopped. */
int v4_boot_line(const v4_boot *b, const char *text, unsigned len);
/* A LINE THAT WAITS (docs/v4.0.0/MESH.md 7b.6). V4_BOOT_LINE_WAITING means
* the line has not ended: the node is asleep, waiting for an answer,
* and here there is no one but the console to end that. The host reads the
* next line and hands it over as usual: that breaks the wait, and what is
* returned is how the line that was waiting ended -- in an error,
* Interrupted. The line just handed over is still on the node's wire; the host
* then calls v4_boot_line with no text (0, 0) to have it done, and that returns
* how it ended, which may be V4_BOOT_LINE_WAITING again. */
/* The dictionary hash of a node started from `im`. */
uint64_t v4_boot_dict_hash(const v4_node *n, const v4_image *im);
#endif /* V4_BOOT_H */