- The result of every take is looked at: a wire cut between the look and the take leaves nothing acted on (the text before run again, a refusal counted, a wait ended by an old answer, a way forgotten). - A node with no room to pass on what another printed, or its word of how text ended, leaves it on the wire it came by and moves it when there is room; what is behind it for the same port waits too. Sleep for room (9) is woken by room on any of the node's wires (engine; MESH.md 7d.3). - The lone node's host keeps no queue for the kernel's port: a message to it is error 18. The step limit is counted while the node waits for room. - A text waiting for room to print is not interrupted by console text; AWAIT is, as before. (BREAK?) is gone. - A GONE is believed only by the centre's port; unawaited answers let go are counted; nothing past a message's last character goes on a wire; AWAIT sets (AWAIT-FROM) only once the stack has been tried. - test_host_depth.c: ten paths, each run works or ends in one named stack error; it fails with (ROOM) or (ROOM-SERVE) taken out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
542 lines
23 KiB
C
542 lines
23 KiB
C
/* boot.c -- the nucleus, then its capsules, then the prompt. See boot.h.
|
|
*
|
|
* Nothing here uses the C library: the bare-metal kernel links this file.
|
|
* It is not part of the engine (v4/src): it needs the capsule directory,
|
|
* which only a whole system has.
|
|
*/
|
|
#include "v4/post.h"
|
|
#include "v4/blocks.h"
|
|
#include "v4/boot.h"
|
|
#include "v4/message.h"
|
|
#include "v4/wire.h"
|
|
#include "starkernel/capsule.h"
|
|
#include "starkernel/capsule_generated.h"
|
|
#include "starkernel/capsule_sig.h"
|
|
#include "starkernel/capsule_blocks.h"
|
|
|
|
/* The capsules, in the order they are loaded. */
|
|
static const char *const boot_capsules[] = {
|
|
"v4:forth79.4th",
|
|
};
|
|
|
|
/* POST is part of the boot, and is the kernel's (v4/include/v4/post.h;
|
|
* docs/v4.0.0/NUCLEUS.md 6.3): when the capsules are in and the system is
|
|
* sealed, the kernel feeds its cases to the node and judges them, puts the
|
|
* node back to the system as sealed, and the boot passes only if none
|
|
* failed. -DV4_POST_AT_BOOT=0 leaves it out, for work on a vocabulary
|
|
* that does not pass yet. */
|
|
#ifndef V4_POST_AT_BOOT
|
|
#define V4_POST_AT_BOOT 1
|
|
#endif
|
|
|
|
#define LINE_MAX 1025u /* a line is at most 1024 characters, a block */
|
|
#define STEP_LIMIT 4000000000ULL /* instruction words one line may take */
|
|
|
|
/* ---- printing ------------------------------------------------------------ */
|
|
|
|
static void say(const v4_boot *b, const char *s)
|
|
{
|
|
unsigned len = 0;
|
|
while (s[len]) len++;
|
|
b->out(s, len);
|
|
}
|
|
|
|
static void say_dec(const v4_boot *b, uint32_t v)
|
|
{
|
|
char buf[10];
|
|
unsigned i = sizeof buf;
|
|
do { buf[--i] = (char)('0' + v % 10u); v /= 10u; } while (v);
|
|
b->out(buf + i, (unsigned)sizeof buf - i);
|
|
}
|
|
|
|
static void say_hex(const v4_boot *b, uint64_t v)
|
|
{
|
|
char buf[18];
|
|
unsigned i;
|
|
buf[0] = '0'; buf[1] = 'x';
|
|
for (i = 0; i < 16; i++) buf[2 + i] = "0123456789abcdef"[(v >> (60 - 4 * i)) & 15u];
|
|
b->out(buf, sizeof buf);
|
|
}
|
|
|
|
/* ---- hashing: FNV-1a, 64 bits, as kernel/src/vm/parity.c ----------------- */
|
|
|
|
uint64_t v4_boot_dict_hash(const v4_node *n, const v4_image *im)
|
|
{
|
|
return v4_image_dict_hash(n, im);
|
|
}
|
|
|
|
/* how many words FORTH holds: the list from LATEST, each entry's link in the
|
|
* cell before its code */
|
|
static uint32_t word_count(const v4_node *n, const v4_image *im)
|
|
{
|
|
v4_cell xt = n->mem[im->latest];
|
|
uint32_t count = 0;
|
|
while (xt > 0 && xt < (v4_cell)V4_NODE_WORDS && count < (uint32_t)V4_NODE_WORDS) {
|
|
count++;
|
|
xt = n->mem[xt - 1];
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/* ---- handing the node a line ---------------------------------------------- */
|
|
|
|
/* The boot is the node's neighbour on two of its ports. On port 0 it is
|
|
* the kernel: the node writes the number of a request there, a word
|
|
* (ENGINE.md 3.3) -- for a block (blocks.h), or for one of the host's
|
|
* words. On port 1 it is the console: it puts a message of text on the
|
|
* node's wire and takes off it the messages of what the node printed and
|
|
* of how the text ended (message.h). */
|
|
#define KERNEL_PORT 0u
|
|
#define CONSOLE_PORT 1u
|
|
#define CONSOLE_ID 1 /* the console's number as a sender; the node's is 0 until it is given one */
|
|
|
|
/* What the kernel keeps of the node's block window (blocks.h). */
|
|
static v4_blocks boot_blocks;
|
|
|
|
/* THE WIRE (docs/v4.0.0/MESH.md 7d.5). The lone node has no fabric: the
|
|
* boot keeps the two queues of its console's wire, one each way (wire.h),
|
|
* and does what the node asks of them, as a fabric does for a node of a
|
|
* mesh (fabric.c).
|
|
* THE KERNEL'S PORT HAS NO QUEUE (ruled 2026-10-08). The kernel is asked by
|
|
* words written to port 0 and takes no messages: a message for that port
|
|
* -- SEND-ON to it, or what a text prints when the way to its console has
|
|
* been set there -- is refused at once, as on any port with no one to take
|
|
* messages: the node's error 18, "No one on that port". */
|
|
static v4_wire_queue boot_wire[2]; /* the console's: [0] to the node, [1] from it */
|
|
#define TO_NODE (&boot_wire[0])
|
|
#define FROM_NODE (&boot_wire[1])
|
|
|
|
/* 1 if `count` words from `addr` are the node's memory and not its ports
|
|
* or the addresses after them: they may be copied to and from. */
|
|
static int span_ok(const v4_node *n, v4_cell addr, unsigned count)
|
|
{
|
|
if (count == 0) return 1;
|
|
if (count > V4_NODE_WORDS || addr < 0 || (v4_ucell)addr > (v4_ucell)(V4_NODE_WORDS - count)) return 0;
|
|
if (n->port >= 0 && addr <= n->port + (v4_cell)V4_PORT_HAVE && addr + (v4_cell)count > n->port) return 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* The node is blocked at an operation on a wire (node.h, v4_node_doing):
|
|
* do it, whole, and let the node go on; the operations and their answers
|
|
* are MESH.md 7d.3's, as fabric.c does them. Returns 0 if the node sleeps
|
|
* on: it asked to sleep (8), and no message has come since it last asked
|
|
* which wires have one; or for room (9) that is not there, and nothing
|
|
* has come off its wire either. */
|
|
static int boot_do(const v4_boot *bt)
|
|
{
|
|
v4_node *n = bt->n;
|
|
v4_wire_queue *rx = n->wire == CONSOLE_PORT ? TO_NODE : 0, *tx = n->wire == CONSOLE_PORT ? FROM_NODE : 0;
|
|
v4_cell a = n->wire_a, b = n->wire_b, h[V4_WIRE_HEADER], how = V4_WIRE_NO_ONE;
|
|
unsigned cells;
|
|
|
|
switch (n->do_op) {
|
|
case 1: /* look */
|
|
if (!rx) break;
|
|
if (rx->mark >= rx->used) how = V4_WIRE_NO_MESSAGE;
|
|
else how = span_ok(n, a, V4_WIRE_HEADER) ? v4_wire_look(rx, &n->mem[a]) : V4_WIRE_NOT_A_MESSAGE;
|
|
break;
|
|
case 2: /* take */
|
|
if (!rx) break;
|
|
if (v4_wire_look(rx, h) != V4_WIRE_DONE) { how = V4_WIRE_NO_MESSAGE; break; }
|
|
cells = v4_wire_cells(h[6]) - V4_WIRE_HEADER;
|
|
if (!span_ok(n, a, V4_WIRE_HEADER) || !span_ok(n, b, cells)) how = V4_WIRE_NOT_A_MESSAGE;
|
|
else how = v4_wire_take(rx, &n->mem[a], cells ? &n->mem[b] : &n->mem[0], cells);
|
|
break;
|
|
case 3: /* move */
|
|
if (!rx) break;
|
|
if (rx->mark >= rx->used) how = V4_WIRE_NO_MESSAGE;
|
|
else how = a == (v4_cell)CONSOLE_PORT ? v4_wire_move(rx, FROM_NODE) : V4_WIRE_NO_ONE;
|
|
break;
|
|
case 4: /* drop */
|
|
if (rx) how = v4_wire_drop(rx);
|
|
break;
|
|
case 5: /* put */
|
|
if (!tx) break;
|
|
cells = span_ok(n, a, V4_WIRE_HEADER) ? v4_wire_cells(n->mem[a + 6]) : 0;
|
|
if (cells == 0 || !span_ok(n, b, cells - V4_WIRE_HEADER)) how = V4_WIRE_NOT_A_MESSAGE;
|
|
else how = v4_wire_put(tx, &n->mem[a], cells > V4_WIRE_HEADER ? &n->mem[b] : &n->mem[0]);
|
|
break;
|
|
case 6: /* first */
|
|
if (rx) { v4_wire_first(rx); how = V4_WIRE_DONE; }
|
|
break;
|
|
case 7: /* next */
|
|
if (rx) how = v4_wire_next(rx);
|
|
break;
|
|
case 8: /* sleep */
|
|
if (!TO_NODE->arrived) return 0;
|
|
how = V4_WIRE_DONE;
|
|
break;
|
|
case 9: /* sleep for room */
|
|
if (!tx) break;
|
|
if (a < 0 || a > (v4_cell)V4_WIRE_CELLS) { how = V4_WIRE_NO_ROOM; break; }
|
|
if (v4_wire_room(tx, (unsigned)a) == V4_WIRE_DONE) { how = V4_WIRE_DONE; break; }
|
|
if (!TO_NODE->arrived && !FROM_NODE->freed) return 0; /* a message has come, or the console has taken one */
|
|
FROM_NODE->freed = 0;
|
|
how = V4_WIRE_NO_ROOM;
|
|
break;
|
|
case 10: /* room */
|
|
if (!tx) break;
|
|
how = a < 0 ? V4_WIRE_NOT_A_MESSAGE : a <= (v4_cell)V4_WIRE_CELLS ? v4_wire_room(tx, (unsigned)a) : V4_WIRE_NO_ROOM;
|
|
break;
|
|
default: /* not an operation */
|
|
how = V4_WIRE_NOT_A_MESSAGE;
|
|
break;
|
|
}
|
|
v4_node_done(n, how);
|
|
return 1;
|
|
}
|
|
|
|
/* The node is told which of its wires have a message and executes one
|
|
* instruction word; if it fetched which wires have one, it has seen what
|
|
* had come. The first half of a fabric's step (fabric.c). */
|
|
static void boot_exec(const v4_boot *b)
|
|
{
|
|
v4_node *n = b->n;
|
|
v4_node_have(n, TO_NODE->used ? 1u << CONSOLE_PORT : 0u);
|
|
(void)v4_exec_step_word(n, b->es, b->h);
|
|
if (n->have_fetched) {
|
|
TO_NODE->arrived = 0;
|
|
n->have_fetched = 0;
|
|
}
|
|
}
|
|
|
|
/* Time passes for the node, in the order a fabric's step has: that, and
|
|
* then what it asked of a wire is done. Returns 0 if it is asleep at a
|
|
* wire and sleeps on. */
|
|
static int boot_step(const v4_boot *b)
|
|
{
|
|
boot_exec(b);
|
|
return v4_node_doing(b->n) ? boot_do(b) : 1;
|
|
}
|
|
|
|
/* Hand the node a line and run it to its end. What it prints goes to the
|
|
* console; or, if `keep` is given, into `keep`, at most `cap` characters,
|
|
* and none of it is shown: *printed is then how many it printed in all.
|
|
* The console takes every message off the node's wire each time round, so
|
|
* what the node prints never waits for room for long. */
|
|
static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep, unsigned cap, unsigned *printed)
|
|
{
|
|
static v4_message out; /* one line at a time: the boot is not re-entered */
|
|
static v4_cell in[V4_WIRE_HEADER], in_text[V4_MSG_MAX_CHARS / 4u];
|
|
v4_node *n = b->n;
|
|
uint64_t steps = 0;
|
|
unsigned i;
|
|
|
|
if (text) { /* with none, the node has a line waiting on its wire (boot.h) */
|
|
if (!v4_message_text(&out, 0, CONSOLE_ID, V4_MSG_TEXT, text, len)) return V4_BOOT_LINE_TOO_LONG;
|
|
if (v4_wire_put(TO_NODE, out.word, out.word + V4_MSG_HEADER) != V4_WIRE_DONE) return V4_BOOT_LINE_STOPPED;
|
|
}
|
|
for (;;) {
|
|
int awake = boot_step(b);
|
|
if (n->stopped) return V4_BOOT_LINE_STOPPED;
|
|
|
|
v4_wire_first(FROM_NODE); /* what the node has sent its console, in the order it was sent */
|
|
while (v4_wire_take(FROM_NODE, in, in_text, V4_MSG_MAX_CHARS / 4u) == V4_WIRE_DONE) {
|
|
if (in[2] == V4_MSG_OUTPUT) {
|
|
for (i = 0; i < (unsigned)in[6]; i++) {
|
|
char c = (char)(((v4_ucell)in_text[i / 4u] >> (8u * (i % 4u))) & 0xFFu);
|
|
if (!keep) b->out(&c, 1);
|
|
else { if (*printed < cap) keep[*printed] = c; (*printed)++; }
|
|
}
|
|
} else if (in[2] == V4_MSG_DONE) {
|
|
/* The text has ended. The node goes on to where it next asks something of a wire, with
|
|
* nothing of its own on either stack: whoever called may then look at its stacks, or
|
|
* empty them. */
|
|
for (i = 0; i < 4096u && !v4_node_doing(n) && !n->stopped && !n->asking; i++) boot_exec(b);
|
|
return (int)in_text[0];
|
|
}
|
|
}
|
|
if (!awake) {
|
|
if (n->do_op == 9) { /* waiting for room, which the console has just made */
|
|
if (++steps > STEP_LIMIT) return V4_BOOT_LINE_STOPPED;
|
|
continue;
|
|
}
|
|
return V4_BOOT_LINE_WAITING; /* waiting for an answer: only the console can end that */
|
|
}
|
|
|
|
if (n->asking && n->ask_port == KERNEL_PORT) { /* a request: the kernel's turn */
|
|
if (v4_blocks_serve(&boot_blocks, n, n->request)) { /* a block, as for any node */
|
|
v4_node_port_served(n);
|
|
} else if (b->serve && n->request >= 1 && n->request <= (v4_cell)b->word_count) {
|
|
b->serve(n, (unsigned)n->request);
|
|
v4_node_port_served(n);
|
|
} else {
|
|
/* a request no one serves is an error, as any other (D-18):
|
|
* 12, Argument out of range */
|
|
v4_node_port_served(n);
|
|
v4_node_store(n, n->error_reg, 12); /* a store to NODE-ERROR raises it */
|
|
}
|
|
continue;
|
|
}
|
|
if (n->asking) { /* a word written to a port with no one on it: an error on the node */
|
|
if (!v4_node_port_gone(n, V4_ERROR_NO_ONE)) return V4_BOOT_LINE_STOPPED;
|
|
continue;
|
|
}
|
|
if (n->reading && !n->given) { /* a word read from a port: no one here writes one */
|
|
if (n->read_port != V4_PORT_ANY && v4_node_port_gone(n, V4_ERROR_NO_ONE)) continue;
|
|
return V4_BOOT_LINE_STOPPED; /* waiting for what will not come */
|
|
}
|
|
if (v4_image_waiting(n, b->im)) { /* the text is reading the keyboard */
|
|
int c = b->key ? b->key() : V4_BOOT_KEY_END;
|
|
unsigned char ch = (unsigned char)c;
|
|
if (c == V4_BOOT_KEY_END) return V4_BOOT_LINE_NO_INPUT;
|
|
if (c >= 0) (void)v4_node_console_feed(n, &ch, 1u);
|
|
continue;
|
|
}
|
|
if (++steps > STEP_LIMIT) return V4_BOOT_LINE_STOPPED;
|
|
}
|
|
}
|
|
|
|
int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
|
|
{
|
|
return run_line(b, text, len, 0, 0u, 0);
|
|
}
|
|
|
|
#if V4_POST_AT_BOOT
|
|
/* POST's host (post.h): a line is handed to the node as any line is, and
|
|
* what the node prints is kept for the runner and not shown. */
|
|
static int post_line(void *self, const char *text, unsigned text_len, char *out, unsigned cap, unsigned *len)
|
|
{
|
|
int how;
|
|
*len = 0;
|
|
how = run_line((const v4_boot *)self, text, text_len, out, cap, len);
|
|
return (how == V4_TEXT_QUIT || how == V4_TEXT_COMPLETED || how == V4_TEXT_ERROR) ? how : -1;
|
|
}
|
|
static void post_say(void *self, const char *text, unsigned len)
|
|
{
|
|
((const v4_boot *)self)->out(text, len);
|
|
}
|
|
#endif
|
|
|
|
/* ---- one capsule --------------------------------------------------------- */
|
|
|
|
static void say_where(const v4_boot *b, const char *name, uint32_t block, uint32_t line)
|
|
{
|
|
say(b, "\nV4: capsule "); say(b, name);
|
|
say(b, " block "); say_dec(b, block);
|
|
say(b, " line "); say_dec(b, line);
|
|
}
|
|
|
|
static int load_capsule(const v4_boot *b, const char *name)
|
|
{
|
|
const CapsuleDirHeader *dir = capsule_get_directory();
|
|
const CapsuleDesc *descs = capsule_get_descriptors();
|
|
const CapsuleNameEntry *names = capsule_get_names();
|
|
const uint8_t *arena = capsule_get_arena();
|
|
const CapsuleDesc *cap;
|
|
const uint8_t *p, *end;
|
|
CapsuleValidateResult vr;
|
|
CapsuleSigResult sr;
|
|
uint32_t block = 0, line = 0;
|
|
int in_block = 0;
|
|
|
|
cap = capsule_find_by_name(dir, descs, names, name);
|
|
if (!cap) { say(b, "V4: capsule "); say(b, name); say(b, " is not in this binary\n"); return 0; }
|
|
|
|
vr = capsule_validate(cap, arena, dir->arena_size, 1);
|
|
if (vr != CAPSULE_VALID) {
|
|
say(b, "V4: capsule "); say(b, name); say(b, ": "); say(b, capsule_validate_result_str(vr)); say(b, "\n");
|
|
return 0;
|
|
}
|
|
|
|
sr = capsule_verify_signature(descs, names, capsule_get_signatures(), arena, dir->desc_count, (int)(cap - descs));
|
|
if (sr != CAPSULE_SIG_OK) {
|
|
say(b, "V4: capsule "); say(b, name); say(b, " signature: "); say(b, capsule_sig_result_str(sr)); say(b, "\n");
|
|
if (sr == CAPSULE_SIG_INVALID) return 0;
|
|
}
|
|
|
|
p = capsule_get_payload(cap, arena);
|
|
if (!p) { say(b, "V4: capsule "); say(b, name); say(b, " has no payload\n"); return 0; }
|
|
end = p + cap->length;
|
|
|
|
while (p < end) {
|
|
const uint8_t *after, *nl;
|
|
uint32_t num;
|
|
unsigned len, i;
|
|
char text[LINE_MAX];
|
|
|
|
if (capsule_block_header(p, end, &num, &after)) {
|
|
block = num; line = 0; in_block = 1; p = after;
|
|
continue;
|
|
}
|
|
for (nl = p; nl < end && *nl != '\n'; nl++) { }
|
|
len = (unsigned)(nl - p);
|
|
if (len && p[len - 1] == '\r') len--;
|
|
if (in_block) {
|
|
line++;
|
|
if (len >= LINE_MAX) { say_where(b, name, block, line); say(b, " is too long\n"); return 0; }
|
|
if (len) {
|
|
for (i = 0; i < len; i++) text[i] = (char)p[i];
|
|
int how = v4_boot_line(b, text, len);
|
|
if (how != V4_TEXT_COMPLETED) {
|
|
say_where(b, name, block, line);
|
|
say(b, how == V4_TEXT_ERROR ? " ended in an error: "
|
|
: how == V4_TEXT_QUIT ? " ended with QUIT: "
|
|
: how == V4_BOOT_LINE_NO_INPUT ? " reads the keyboard: "
|
|
: " stopped the node: ");
|
|
b->out(text, len); say(b, "\n");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
p = (nl < end) ? nl + 1 : end;
|
|
}
|
|
|
|
say(b, "PARITY:V4_CAPSULE name="); say(b, name);
|
|
say(b, " capsule_id="); say_hex(b, cap->capsule_id);
|
|
say(b, " capsule_hash="); say_hex(b, cap->content_hash);
|
|
say(b, " dict_hash="); say_hex(b, v4_boot_dict_hash(b->n, b->im));
|
|
say(b, "\n");
|
|
return 1;
|
|
}
|
|
|
|
/* ---- the nucleus ----------------------------------------------------------- */
|
|
|
|
/* The nucleus is a capsule of F18 code (v4/include/v4/capsule.h): the words
|
|
* themselves, to be written to the port of a node that has nothing in it.
|
|
* The boot is that node's neighbour. It finds the capsule, checks its hash
|
|
* and its signature as it does any capsule's, and gives the node a word
|
|
* each time it reads its port, until the node has taken them all and is
|
|
* where the nucleus waits to be handed a line. */
|
|
#define NUCLEUS_NAME_(bits) "v4:nucleus-" #bits ".f18"
|
|
#define NUCLEUS_NAME__(bits) NUCLEUS_NAME_(bits)
|
|
#define NUCLEUS_NAME NUCLEUS_NAME__(V4_CELL_BITS)
|
|
#define CELL_BYTES (V4_CELL_BITS / 8)
|
|
|
|
static int load_nucleus(const v4_boot *b)
|
|
{
|
|
const CapsuleDirHeader *dir = capsule_get_directory();
|
|
const CapsuleDesc *descs = capsule_get_descriptors();
|
|
const CapsuleNameEntry *names = capsule_get_names();
|
|
const uint8_t *arena = capsule_get_arena();
|
|
const CapsuleDesc *cap;
|
|
const uint8_t *p;
|
|
CapsuleValidateResult vr;
|
|
CapsuleSigResult sr;
|
|
v4_node *n = b->n;
|
|
uint64_t words, at = 0, steps = 0;
|
|
|
|
cap = capsule_find_by_name(dir, descs, names, NUCLEUS_NAME);
|
|
if (!cap) { say(b, "V4: capsule " NUCLEUS_NAME " is not in this binary\n"); return 0; }
|
|
vr = capsule_validate(cap, arena, dir->arena_size, 1);
|
|
if (vr != CAPSULE_VALID) {
|
|
say(b, "V4: capsule " NUCLEUS_NAME ": "); say(b, capsule_validate_result_str(vr)); say(b, "\n");
|
|
return 0;
|
|
}
|
|
sr = capsule_verify_signature(descs, names, capsule_get_signatures(), arena, dir->desc_count, (int)(cap - descs));
|
|
if (sr != CAPSULE_SIG_OK) {
|
|
say(b, "V4: capsule " NUCLEUS_NAME " signature: "); say(b, capsule_sig_result_str(sr)); say(b, "\n");
|
|
if (sr == CAPSULE_SIG_INVALID) return 0;
|
|
}
|
|
p = capsule_get_payload(cap, arena);
|
|
if (!p || cap->length % CELL_BYTES != 0) { say(b, "V4: capsule " NUCLEUS_NAME " is not whole words\n"); return 0; }
|
|
words = cap->length / CELL_BYTES;
|
|
|
|
/* until it has taken every word and is where the nucleus waits for a
|
|
* message: asleep, no message having come */
|
|
for (;;) {
|
|
if (!boot_step(b)) {
|
|
if (at >= words && n->do_op == 8) break;
|
|
say(b, "V4: the node did not take the nucleus in\n");
|
|
return 0;
|
|
}
|
|
if (n->stopped || n->asking || ++steps > STEP_LIMIT) {
|
|
say(b, "V4: the node did not take the nucleus in\n");
|
|
return 0;
|
|
}
|
|
if (n->reading && !n->given) {
|
|
v4_ucell word = 0;
|
|
unsigned i;
|
|
if (at >= words) {
|
|
say(b, "V4: the node wants more than the nucleus holds\n");
|
|
return 0;
|
|
}
|
|
for (i = 0; i < CELL_BYTES; i++) word |= (v4_ucell)p[at * CELL_BYTES + i] << (8 * i);
|
|
v4_node_port_give(n, 0, (v4_cell)word);
|
|
at++;
|
|
}
|
|
}
|
|
|
|
say(b, "PARITY:V4_NUCLEUS name=" NUCLEUS_NAME);
|
|
say(b, " capsule_id="); say_hex(b, cap->capsule_id);
|
|
say(b, " capsule_hash="); say_hex(b, cap->content_hash);
|
|
say(b, " words="); say_dec(b, word_count(n, b->im));
|
|
say(b, "\n");
|
|
return 1;
|
|
}
|
|
|
|
/* ---- the whole boot ------------------------------------------------------ */
|
|
|
|
int v4_boot_run(const v4_boot *b)
|
|
{
|
|
unsigned i;
|
|
|
|
/* a node is born with nothing in it, and is sent the nucleus */
|
|
if (!v4_image_born(b->n, b->es, b->h, b->im)) {
|
|
say(b, "V4: the nucleus is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n");
|
|
return 0;
|
|
}
|
|
/* its console's wire, with nothing on it */
|
|
v4_wire_reset(TO_NODE); v4_wire_reset(FROM_NODE);
|
|
v4_blocks_init(&boot_blocks, b->im->block_window);
|
|
if (!load_nucleus(b)) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; }
|
|
|
|
/* the kernel's words, before any capsule that might use one */
|
|
for (i = 0; i < b->word_count; i++) {
|
|
char text[64];
|
|
unsigned at = 0, k, num = i + 1, div = 1;
|
|
while (num / div >= 10) div *= 10;
|
|
for (; div; div /= 10) text[at++] = (char)('0' + num / div % 10);
|
|
for (k = 0; " KERNEL-WORD "[k]; k++) text[at++] = " KERNEL-WORD "[k];
|
|
for (k = 0; b->words[i][k] && at < sizeof text; k++) text[at++] = b->words[i][k];
|
|
if (b->words[i][k] || v4_boot_line(b, text, at) != V4_TEXT_COMPLETED) {
|
|
say(b, "V4: kernel word "); say(b, b->words[i]); say(b, " could not be made\nPARITY:FAIL\nPOST: FAILED\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < sizeof boot_capsules / sizeof boot_capsules[0]; i++)
|
|
if (!load_capsule(b, boot_capsules[i])) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; }
|
|
/* The kernel's words and the capsules are the system: COLD comes back
|
|
* to here, and FORGET will not go below it. */
|
|
v4_image_seal(b->n, b->im);
|
|
|
|
#if V4_POST_AT_BOOT
|
|
{
|
|
static const char cold[] = "COLD";
|
|
v4_post_host host;
|
|
v4_post_tally tally;
|
|
unsigned printed = 0;
|
|
int how;
|
|
|
|
host.self = (void *)b; host.n = b->n; host.line = post_line; host.say = post_say;
|
|
if (!v4_post_run(&host, v4_post_cases, v4_post_case_count, &tally)) {
|
|
say(b, "V4: POST reported failures\nPARITY:FAIL\nPOST: FAILED\n");
|
|
return 0;
|
|
}
|
|
/* POST leaves nothing: the node is put back to the system as it was
|
|
* sealed, as v3 puts the dictionary back after each of its suites
|
|
* (v3/src/test_runner/test_common.c, run_test_suite). What COLD
|
|
* prints is not shown. */
|
|
v4_dstack_reset(&b->n->ds);
|
|
how = run_line(b, cold, (unsigned)sizeof cold - 1u, (char *)cold, 0u, &printed);
|
|
if (how != V4_TEXT_COMPLETED && how != V4_TEXT_QUIT) {
|
|
say(b, "V4: the node did not come back to the system after POST\nPARITY:FAIL\nPOST: FAILED\n");
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
say(b, "PARITY:V4_SYSTEM word_count="); say_dec(b, word_count(b->n, b->im));
|
|
say(b, " dict_hash="); say_hex(b, v4_boot_dict_hash(b->n, b->im));
|
|
say(b, "\n");
|
|
|
|
#if V4_POST_AT_BOOT
|
|
say(b, "PARITY:OK\nPOST: PASSED\n");
|
|
#else
|
|
say(b, "PARITY:OK\n");
|
|
#endif
|
|
return 1;
|
|
}
|