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>
140 lines
5.3 KiB
C
140 lines
5.3 KiB
C
/* sk_v4.c -- StarForth v4 on bare metal: one host node at a prompt.
|
|
*
|
|
* The node is the golden model of the F18-derived engine (v4/src). It comes
|
|
* up as the hosted v4 system does, by v4_boot_run (v4/include/v4/boot.h):
|
|
* the machine's ROM assembles the system from its source, then the capsules
|
|
* from the directory baked into this kernel are loaded, each checked and its
|
|
* parity line printed, then the prompt.
|
|
* docs/v4.0.0/NUCLEUS.md. This file is all the kernel adds:
|
|
*
|
|
* - what the node prints goes to the kernel's console;
|
|
* - the prompt, and the line: it is read and edited here and handed to the
|
|
* node whole (docs/v4.0.0/ENGINE.md 3.1), and how it ended is said here;
|
|
* - blocks are kept in memory, and are gone at power-off.
|
|
*
|
|
* This is the lone node, started before the kernel's fleet and beside it.
|
|
* It goes when the node is brought up in the VM's place (ENGINE.md step 4).
|
|
*
|
|
* Nothing of the v3 VM is started. docs/v4.0.0/DECOMPOSITION.md.
|
|
*/
|
|
#include "starkernel/v4/sk_v4.h"
|
|
#include "starkernel/console.h"
|
|
#include "starkernel/arch.h"
|
|
#include "v4/boot.h"
|
|
#include "block_subsystem.h"
|
|
|
|
#define SK_V4_LINE 255u /* what the line editor here takes */
|
|
|
|
static v4_node sk_v4_node;
|
|
static v4_exec_state sk_v4_es;
|
|
static v4_heat sk_v4_heat;
|
|
static v4_image sk_v4_image;
|
|
|
|
static char sk_v4_text[SK_V4_LINE + 1u];
|
|
|
|
/* POST's block RAM, as the v3 path has (sk_vm_bootstrap.c, sk_post_blk_ram):
|
|
* the node boots and is POSTed with these blocks, 1 to 2047 and nothing
|
|
* above them, and the kernel's real chain is set up afterwards. */
|
|
static uint8_t sk_v4_post_blk_ram[BLK_RAM_BLOCKS * BLK_FORTH_SIZE];
|
|
|
|
static void sk_v4_out(const char *text, unsigned len)
|
|
{
|
|
unsigned i;
|
|
for (i = 0; i < len; i++) console_putc(text[i]);
|
|
}
|
|
|
|
/* a character for a line that reads the keyboard, when there is one */
|
|
static int sk_v4_key(void)
|
|
{
|
|
int c = console_getc();
|
|
return c < 0 ? V4_BOOT_KEY_NONE : c;
|
|
}
|
|
|
|
/* THE KERNEL'S WORDS for the lone node. BYE is v3's (mama_word_bye): a cold
|
|
* restart. v3's reaps Hera's children first; the lone node is started
|
|
* before there is a fleet, so there are none. The rest of the kernel's
|
|
* words come when the node is brought up in the VM's place (ENGINE.md
|
|
* step 4), where v3's own functions serve them. */
|
|
static const char *const sk_v4_words[] = { "BYE" };
|
|
|
|
static void sk_v4_serve(v4_node *node, unsigned request)
|
|
{
|
|
(void)node;
|
|
if (request == 1) {
|
|
console_println("BYE: cold restart");
|
|
arch_cold_reset();
|
|
}
|
|
}
|
|
|
|
/* Read a line from the keyboard: characters are echoed, backspace rubs one
|
|
* out, Enter ends it. Returns its length. */
|
|
static unsigned sk_v4_readline(void)
|
|
{
|
|
unsigned len = 0;
|
|
for (;;) {
|
|
int c = console_getc();
|
|
if (c < 0) continue;
|
|
if (c == '\r' || c == '\n') { console_putc('\n'); return len; }
|
|
if (c == 8 || c == 127) {
|
|
if (len > 0) { len--; console_putc(8); console_putc(' '); console_putc(8); }
|
|
} else if (c >= 32 && c < 127 && len < SK_V4_LINE) {
|
|
sk_v4_text[len++] = (char)c;
|
|
console_putc((char)c);
|
|
}
|
|
}
|
|
}
|
|
|
|
void sk_v4_run(void (*chain)(void))
|
|
{
|
|
v4_boot boot;
|
|
|
|
console_println("StarForth v4: one host node, the F18-derived engine");
|
|
sk_v4_image = v4_map_image;
|
|
boot.n = &sk_v4_node; boot.es = &sk_v4_es; boot.h = &sk_v4_heat; boot.im = &sk_v4_image;
|
|
boot.out = sk_v4_out; boot.key = sk_v4_key;
|
|
boot.words = sk_v4_words; boot.word_count = sizeof sk_v4_words / sizeof sk_v4_words[0]; boot.serve = sk_v4_serve;
|
|
blk_subsys_init(sk_v4_post_blk_ram, sizeof(sk_v4_post_blk_ram));
|
|
if (!v4_boot_run(&boot)) {
|
|
console_println("StarForth v4: not started");
|
|
for (;;) { }
|
|
}
|
|
if (chain) {
|
|
/* the blocks are other blocks now: what the node has in its two
|
|
* buffers is POST's, and is let go */
|
|
chain();
|
|
if (v4_boot_line(&boot, "EMPTY-BUFFERS", 13) != V4_TEXT_COMPLETED) {
|
|
console_println("StarForth v4: the node did not let go of POST's blocks");
|
|
for (;;) { }
|
|
}
|
|
}
|
|
|
|
/* The prompt is the host's: read a line, hand it to the node, say how it
|
|
* ended. */
|
|
for (;;) {
|
|
unsigned len;
|
|
int how;
|
|
|
|
console_puts("ok> ");
|
|
len = sk_v4_readline();
|
|
how = v4_boot_line(&boot, sk_v4_text, len);
|
|
/* The line is waiting for an answer no one here can send. The next
|
|
* line typed breaks the wait: the line that was waiting ends in an
|
|
* error, and then the line just typed is done (v4/boot.h). */
|
|
while (how == V4_BOOT_LINE_WAITING) {
|
|
len = sk_v4_readline();
|
|
how = v4_boot_line(&boot, sk_v4_text, len);
|
|
if (how == V4_TEXT_ERROR) console_puts(" ERROR\nok> ");
|
|
else if (how == V4_TEXT_COMPLETED) console_puts(" ok\nok> ");
|
|
else break;
|
|
how = v4_boot_line(&boot, 0, 0);
|
|
}
|
|
if (how == V4_TEXT_COMPLETED) console_puts(" ok\n");
|
|
else if (how == V4_TEXT_ERROR) console_puts(" ERROR\n");
|
|
else if (how == V4_BOOT_LINE_REFUSED) console_puts("Message refused\n ERROR\n"); /* the node's wire is full: error 19's words */
|
|
else if (how == V4_BOOT_LINE_STOPPED) {
|
|
console_println("StarForth v4: the node stopped on a fault");
|
|
for (;;) { }
|
|
}
|
|
}
|
|
}
|