/* SPDX-License-Identifier: LicenseRef-Starship-1.0 * Copyright (c) 2023-2026 Robert A. James */ /* mesh.c -- the kernel's boot of the fabric. See v4/mesh.h (#189). * * What v4/tests/test_host_unit.c does on the host, made the kernel's: the * nucleus, Hera born empty and given it, her system, POST, and every node * stepped together by the fabric. No scheduler. * * Nothing here uses the C library: the bare-metal kernel links this file. */ #include "v4/mesh.h" #include "v4/capsule.h" #include "v4/image.h" #include "v4/message.h" #include "v4/post.h" #include "starkernel/capsule_blocks.h" #if V4_CELL_BITS == 64 extern const v4_cell v4_rom_64[]; extern const unsigned v4_rom_64_words; extern const v4_cell v4_mesh_rom_64[]; extern const unsigned v4_mesh_rom_64_words; #define HERA_ROM v4_rom_64 #define HERA_ROM_WORDS v4_rom_64_words #define NODE_ROM v4_mesh_rom_64 #define NODE_ROM_WORDS v4_mesh_rom_64_words #define NUCLEUS_NAME "v4:nucleus-64.f18" #else extern const v4_cell v4_rom_32[]; extern const unsigned v4_rom_32_words; extern const v4_cell v4_mesh_rom_32[]; extern const unsigned v4_mesh_rom_32_words; #define HERA_ROM v4_rom_32 #define HERA_ROM_WORDS v4_rom_32_words #define NODE_ROM v4_mesh_rom_32 #define NODE_ROM_WORDS v4_mesh_rom_32_words #define NUCLEUS_NAME "v4:nucleus-32.f18" #endif #define KERNEL_PORT 0u #define MAX_PLACES 64u /* mesh.h: reading_nucleus[64] */ #define STEP_LIMIT 4000000000ULL /* fabric steps one line may take */ #define WAIT_STEPS 2000000ULL /* steps Hera may sleep awaiting a node that runs on, before the line is "waiting" */ #define TOLD_FIRST "10 (ME) ! 1 (CONSOLE) ! 1 1 ROUTE" static v4_mesh *the; /* one mesh: the devices' callbacks find it here */ static v4_boot bt; /* for saying, and for the ROM's assembly of the nucleus */ /* ---- saying ---------------------------------------------------------------- */ static void say(const char *s) { unsigned len = 0; while (s[len]) len++; the->out(s, len); } static int same(const char *a, unsigned alen, const char *b) { unsigned i; for (i = 0; i < alen && b[i] && a[i] == b[i]; i++) { } return i == alen && !b[i]; } /* ---- the nucleus, as a capsule of F18 cells -------------------------------- */ static void put_cell(void *self, v4_cell w) { v4_mesh *ms = (v4_mesh *)self; if (ms->cell_count < V4_MESH_NUCLEUS_CELLS) ms->cells[ms->cell_count++] = w; } /* ---- each place's capsule reader (CAPSULE-OPEN, -CELL, -LINE) -------------- */ typedef struct { const uint8_t *p, *end; int in_block; } reader; static reader reads[MAX_PLACES]; static int open_cb(void *self, unsigned asker, const char *name, unsigned len) { char cname[V4_MANAGE_LINE + 1u]; uint32_t length; uint64_t id, hash; const uint8_t *p; unsigned i; (void)self; if (asker >= MAX_PLACES || len == 0 || len > V4_MANAGE_LINE) return 0; if (same(name, len, NUCLEUS_NAME)) { /* the system, assembled: a cell at a time */ the->reading_nucleus[asker] = 1; reads[asker].p = reads[asker].end = 0; return 1; } for (i = 0; i < len; i++) cname[i] = name[i]; cname[len] = 0; p = v4_boot_capsule(&bt, cname, &length, &id, &hash); if (!p) return 0; the->reading_nucleus[asker] = 0; reads[asker].p = p; reads[asker].end = p + length; reads[asker].in_block = 0; return 1; } static int cell_cb(void *self, unsigned asker, v4_cell *w) { unsigned k; (void)self; if (asker >= MAX_PLACES || !the->reading_nucleus[asker]) return 0; k = the->reading_nucleus[asker] - 1u; if (k >= the->cell_count) return 0; *w = the->cells[k]; the->reading_nucleus[asker]++; return 1; } /* The next line of a block of the open capsule, at most V4_MANAGE_LINE characters: its length, or -1. */ static int next_line(reader *r, char *text, unsigned cap) { while (r->p && r->p < r->end) { const uint8_t *after, *nl; uint32_t num; unsigned len, i; if (capsule_block_header(r->p, r->end, &num, &after)) { r->in_block = 1; r->p = after; continue; } for (nl = r->p; nl < r->end && *nl != '\n'; nl++) { } len = (unsigned)(nl - r->p); if (len && r->p[len - 1] == '\r') len--; if (r->in_block && len > cap) { r->p = r->end; return -1; } if (r->in_block) for (i = 0; i < len; i++) text[i] = (char)r->p[i]; r->p = nl < r->end ? nl + 1 : r->end; if (r->in_block && len) return (int)len; } return -1; } static int line_cb(void *self, unsigned asker, char *text) { (void)self; if (asker >= MAX_PLACES || the->reading_nucleus[asker]) return -1; return next_line(&reads[asker], text, V4_MANAGE_LINE); } static void words_attach(unsigned place, v4_fabric_node *x); static void parity_cb(void *self, unsigned place, v4_cell number, const v4_node *n) { (void)self; { v4_fabric_node *x = v4_fabric_node_at(&the->f, place); if (x && &x->n == n) words_attach(place, x); /* it has its system: its words are kept from now */ } if (the->parity) the->parity(the->self, place, number, n); } /* ---- the kernel on a place's port 0 ---------------------------------------- */ static unsigned place_of_device(const void *self) { return (unsigned)((const v4_device *)self - the->kernel); } /* ---- the words' accounts (#189, Phase 2; mesh.h) ----------------------------------------------------- */ #define WORD_BITS_BYTES ((unsigned)V4_NODE_WORDS / 8u) static unsigned char *bits_of(unsigned place) { return the->is_word + (unsigned long)place * WORD_BITS_BYTES; } static v4_mesh_account *accounts_of(unsigned place) { return the->accounts + (unsigned long)place * V4_MESH_WORD_IDS; } static int is_word(unsigned place, v4_cell a) { return a > 4 && a < (v4_cell)V4_NODE_WORDS && (bits_of(place)[a >> 3] >> (a & 7)) & 1u; } /* an entry has been made, its code at xt: it has a record from now */ static void word_made(unsigned place, const v4_node *n, v4_cell xt) { v4_cell id; if (!the->accounts || xt <= 4 || xt >= (v4_cell)V4_NODE_WORDS) return; bits_of(place)[xt >> 3] |= (unsigned char)(1u << (xt & 7)); id = n->mem[xt - 4]; if (id >= 0 && id < (v4_cell)V4_MESH_WORD_IDS) { v4_mesh_account *a = &accounts_of(place)[id]; a->calls = 0; a->since = 0; a->xt = xt; } } /* every entry from word address w up has gone: their records go, and the owner forgets their IDs */ static void words_gone(unsigned place, const v4_node *n, v4_cell w) { v4_cell a; if (!the->accounts) return; if (w < 5) w = 5; for (a = w; a < (v4_cell)V4_NODE_WORDS; a++) { v4_cell id; if (!is_word(place, a)) continue; bits_of(place)[a >> 3] &= (unsigned char)~(1u << (a & 7)); id = n->mem[a - 4]; if (id >= 0 && id < (v4_cell)V4_MESH_WORD_IDS && accounts_of(place)[id].xt == a) { v4_mesh_account *r = &accounts_of(place)[id]; r->calls = 0; r->since = 0; r->xt = 0; if (the->forgot) the->forgot(the->self, place, id); } } } /* at `call` (exec.h): a word's record counts one; any other target, nothing */ static void on_call(void *ctx, v4_node *n, v4_cell target) { unsigned place = place_of_device(ctx); v4_cell id; if (!is_word(place, target)) return; id = n->mem[target - 4]; if (id >= 0 && id < (v4_cell)V4_MESH_WORD_IDS) { accounts_of(place)[id].calls++; accounts_of(place)[id].since++; } } /* is the entry at xt named `name`? (dict.f18: the name's word address at xt-2, its count byte, then its * characters, four to a cell, low byte first) */ static int named(const v4_node *n, v4_cell xt, const char *name) { v4_cell nfa = n->mem[xt - 2]; unsigned len = 0, i; while (name[len]) len++; if (nfa < 0 || nfa >= xt) return 0; for (i = 0; i <= len; i++) { v4_ucell c = (v4_ucell)n->mem[nfa + (v4_cell)((i) / 4u)]; unsigned char b = (unsigned char)((c >> (8u * (i % 4u))) & 0xFFu); if (i == 0 ? b != len : b != (unsigned char)name[i - 1]) return 0; } return 1; } /* the node in `place` is sealed: its words as they are now each have a record, and from now on it * tells of entries made and gone, and every call to a word counts */ static void words_attach(unsigned place, v4_fabric_node *x) { const v4_image *im = the->im; v4_cell xt, told = 0, gone = 0; unsigned k; if (!the->accounts || !the->is_word) return; for (k = 0; k < WORD_BITS_BYTES; k++) bits_of(place)[k] = 0; for (k = 0; k < V4_MESH_WORD_IDS; k++) { accounts_of(place)[k].calls = 0; accounts_of(place)[k].since = 0; accounts_of(place)[k].xt = 0; } for (xt = x->n.mem[im->latest], k = 0; xt > 4 && xt < (v4_cell)V4_NODE_WORDS && k < V4_NODE_WORDS; xt = x->n.mem[xt - 1], k++) { word_made(place, &x->n, xt); if (!told && named(&x->n, xt, "(TELL-DEFINED)")) told = xt; if (!gone && named(&x->n, xt, "(TELL-FORGOTTEN)")) gone = xt; } if (told && gone) { x->n.mem[im->word_defined] = told; x->n.mem[im->word_forgotten] = gone; } x->es.on_call = on_call; x->es.on_call_ctx = &the->kernel[place]; } const v4_mesh_account *v4_mesh_account_at(const v4_mesh *ms, unsigned place, v4_cell xt) { v4_fabric_node *x = v4_fabric_node_at(&ms->f, place); v4_cell id; the = (v4_mesh *)ms; if (!x || !ms->accounts || place >= ms->capacity || !is_word(place, xt)) return 0; id = x->n.mem[xt - 4]; if (id < 0 || id >= (v4_cell)V4_MESH_WORD_IDS || ms->accounts[(unsigned long)place * V4_MESH_WORD_IDS + (unsigned long)id].xt != xt) return 0; return &ms->accounts[(unsigned long)place * V4_MESH_WORD_IDS + (unsigned long)id]; } void v4_mesh_charge(v4_mesh *ms, unsigned place, void (*each)(void *ctx, v4_cell id, v4_uheat_t calls), void *ctx) { unsigned k; if (!ms->accounts || place >= ms->capacity) return; for (k = 0; k < V4_MESH_WORD_IDS; k++) { v4_mesh_account *a = &ms->accounts[(unsigned long)place * V4_MESH_WORD_IDS + k]; if (a->since == 0) continue; each(ctx, (v4_cell)k, a->since); a->since = 0; } } static int kernel_take(void *self, v4_cell value) /* a request */ { unsigned place = place_of_device(self); v4_fabric_node *x = v4_fabric_node_at(&the->f, place); if (!x) return 1; if (v4_blocks_serve(&the->blocks[place], &x->n, value)) return 1; if (value == V4_REQ_WORD_DEFINED) { word_made(place, &x->n, v4_dstack_pop(&x->n.ds)); return 1; } if (value == V4_REQ_WORD_FORGOTTEN) { words_gone(place, &x->n, v4_dstack_pop(&x->n.ds)); return 1; } if (place == the->hera_place && the->serve && value >= 1 && value <= (v4_cell)the->word_count) { the->serve(&x->n, (unsigned)value); return 1; } if (v4_manage_serve(&the->m, place, value)) return 1; /* the registry's: any node may ask, the kernel checks who */ v4_node_store(&x->n, x->n.error_reg, 12); /* a request no one serves: 12, Argument out of range */ return 1; } static int kernel_give(void *self, v4_cell *value) /* the nucleus, to Hera, who has nothing in her */ { if (place_of_device(self) != the->hera_place || the->given >= the->cell_count) return 0; *value = the->cells[the->given++]; return 1; } static const v4_device console = { 0, 0, 0 }; /* it speaks whole messages only */ /* What a node's hardware gives it, in place `place`: its registers, its ROM, the kernel on port 0. */ static int fit(unsigned place, v4_fabric_node *x, const v4_cell *rom, unsigned rom_words, int gives) { const v4_image *im = the->im; v4_node_stack_regs_attach(&x->n, im->dstack_reg, im->rstack_reg); v4_node_error_attach(&x->n, im->node_error); v4_node_fault_attach(&x->n, im->fault_table); v4_node_heat_regs_attach(&x->n, im->heat_reg, x->h.op, &x->es.anticlock); v4_node_rom_attach(&x->n, rom, rom_words); v4_blocks_init(&the->blocks[place], im->block_window); the->kernel[place].take = kernel_take; the->kernel[place].give = gives ? kernel_give : 0; the->kernel[place].self = &the->kernel[place]; return v4_fabric_wire_kernel(&the->f, place, KERNEL_PORT, &the->kernel[place]); } /* ---- births ------------------------------------------------------------------ */ static int born_cb(void *self) { v4_fabric_node *x; int place; (void)self; x = the->room ? the->room(the->self) : 0; if (!x) return -1; place = v4_fabric_add(&the->f, x, the->im->port); if (place < 0 || (unsigned)place >= MAX_PLACES) { if (place >= 0) (void)v4_fabric_remove(&the->f, (unsigned)place); the->unroom(the->self, x); return -1; } if (!fit((unsigned)place, x, NODE_ROM, NODE_ROM_WORDS, 0)) { (void)v4_fabric_remove(&the->f, (unsigned)place); the->unroom(the->self, x); return -1; } the->reading_nucleus[place] = 0; if (the->born) the->born(the->self, (unsigned)place); return place; } static void gone_cb(void *self, unsigned place, v4_fabric_node *was) { (void)self; if (place < MAX_PLACES) { the->reading_nucleus[place] = 0; reads[place].p = reads[place].end = 0; } if (the->accounts && place < the->capacity) { /* its words' records go with it */ unsigned k; for (k = 0; k < WORD_BITS_BYTES; k++) bits_of(place)[k] = 0; for (k = 0; k < V4_MESH_WORD_IDS; k++) { accounts_of(place)[k].calls = 0; accounts_of(place)[k].since = 0; accounts_of(place)[k].xt = 0; } } if (the->gone) the->gone(the->self, place); if (the->unroom) the->unroom(the->self, was); } v4_fabric_node *v4_mesh_node(const v4_mesh *ms, v4_cell number) { unsigned k; for (k = 0; k < ms->capacity; k++) { v4_fabric_node *x = v4_fabric_node_at(&ms->f, k); if (x && x->n.mem[ms->im->me] == number) return x; } return 0; } /* ---- the console: text to a node, and what comes back -------------------- */ /* Does any node execute at the next step? One asleep at a wire, blocked, or stopped does not. */ static int any_running(const v4_mesh *ms) { unsigned k; for (k = 0; k < ms->capacity; k++) { const v4_fabric_node *x = v4_fabric_node_at(&ms->f, k); if (!x || ms->places[k].asleep || x->n.stopped || x->n.asking || x->n.doing || (x->n.reading && !x->n.given)) continue; return 1; } return 0; } /* What has come back to the console: printing goes to `out`, and how a text ended is kept. */ static int takes(v4_mesh *ms, v4_cell *ended_from, v4_cell *ended_how, unsigned *ended) { static v4_cell hd[V4_WIRE_HEADER], text[V4_MSG_MAX_CHARS / 4u]; int took = 0; while (v4_fabric_device_take(&ms->f, ms->hera_place, V4_MESH_CONSOLE, hd, text, V4_MSG_MAX_CHARS / 4u) == V4_WIRE_DONE) { took = 1; if (hd[2] == V4_MSG_OUTPUT) { char buf[64]; unsigned i, at = 0; for (i = 0; i < (unsigned)hd[6]; i++) { buf[at++] = (char)(((v4_ucell)text[i / 4u] >> (8u * (i % 4u))) & 0xFFu); if (at == sizeof buf) { ms->out(buf, at); at = 0; } } if (at) ms->out(buf, at); } else if (hd[2] == V4_MSG_DONE) { /* the end of a text left "waiting" (7b.6: it ends "Interrupted" when the next comes, or * with its answer): owed, not the end of the text being told now */ if (ms->owed && hd[1] == ms->owed_from) ms->owed--; else (*ended)++; *ended_from = hd[1]; *ended_how = text[0]; ms->dones++; ms->done_from = hd[1]; ms->done_how = text[0]; } } return took; } unsigned v4_mesh_step(v4_mesh *ms) { v4_cell f = -1, h = -1; unsigned e = 0, did; the = ms; did = v4_fabric_step(&ms->f); (void)takes(ms, &f, &h, &e); return did; } int v4_mesh_busy(const v4_mesh *ms) { return any_running(ms); } int v4_mesh_tell(v4_mesh *ms, v4_cell node, const char *text, unsigned len) { static v4_message msg; v4_cell ended_from = -1, ended_how = -1; unsigned ended = 0; unsigned long long steps = 0, asleep = 0; v4_fabric_node *h = v4_fabric_node_at(&ms->f, ms->hera_place); the = ms; if (len > V4_MSG_MAX_CHARS) return V4_BOOT_LINE_TOO_LONG; if (!v4_message_text(&msg, node, V4_MESH_CONSOLE, V4_MSG_TEXT, text, len)) return V4_BOOT_LINE_TOO_LONG; if (v4_fabric_device_put(&ms->f, ms->hera_place, V4_MESH_CONSOLE, msg.word, msg.word + V4_MSG_HEADER) != V4_WIRE_DONE) return V4_BOOT_LINE_REFUSED; while (steps++ < STEP_LIMIT) { unsigned did = v4_fabric_step(&ms->f); int took = takes(ms, &ended_from, &ended_how, &ended); int running; if (h && h->n.stopped) return V4_BOOT_LINE_STOPPED; /* the node it was for has said how its text ended -- or, for node 0, one not yet told its * number, whoever it became: the first line Hera is told makes her 10 */ if (ended && (node == 0 || ended_from == node)) return (int)ended_how; /* the text reads the keyboard: Hera's console registers, fed from the host's keys */ if (h && v4_image_waiting(&h->n, ms->im)) { int c = ms->key ? ms->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 != V4_BOOT_KEY_NONE) (void)v4_node_console_feed(&h->n, &ch, 1); continue; } running = any_running(ms); /* Hera asleep at her wires, her text not ended: awaiting an answer -- "waiting" when nothing * else can give it, or when other nodes have run a long while without giving it */ if (h && h->n.doing && h->n.do_op == 8) { if ((did == 0 && !took && !running) || ++asleep > WAIT_STEPS) { if (ms->owed && ms->owed_from == node) ms->owed++; else { ms->owed_from = node == 0 ? V4_MESH_HERA : node; ms->owed = 1; } return V4_BOOT_LINE_WAITING; } } else asleep = 0; if (did == 0 && !took && !running) return V4_BOOT_LINE_STOPPED; } return V4_BOOT_LINE_STOPPED; } /* ---- POST's host: a line is told to Hera, what she prints handed to the runner ---- */ static char post_out[V4_POST_OUT]; static unsigned post_len, post_cap; static void post_capture(const char *text, unsigned len) { unsigned i; for (i = 0; i < len; i++) { if (post_len < post_cap) post_out[post_len] = text[i]; post_len++; } } static int post_line(void *self, const char *text, unsigned text_len, char *out, unsigned cap, unsigned *len) { v4_mesh *ms = (v4_mesh *)self; void (*was)(const char *, unsigned) = ms->out; int how; unsigned i; post_len = 0; post_cap = cap < sizeof post_out ? cap : (unsigned)sizeof post_out; ms->out = post_capture; how = v4_mesh_tell(ms, V4_MESH_HERA, text, text_len); ms->out = was; for (i = 0; i < post_len && i < post_cap; i++) out[i] = post_out[i]; *len = post_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) { ((v4_mesh *)self)->out(text, len); } /* ---- the boot ----------------------------------------------------------------- */ /* Every line of a capsule of FORTH blocks, told to Hera; then its parity line, as v4_boot_run prints it. */ static int load(v4_mesh *ms, const char *name) { static reader r; char text[V4_MSG_MAX_CHARS + 1u]; uint32_t length; uint64_t id, hash; const uint8_t *p = v4_boot_capsule(&bt, name, &length, &id, &hash); int n; v4_fabric_node *h = v4_fabric_node_at(&ms->f, ms->hera_place); if (!p) return 0; r.p = p; r.end = p + length; r.in_block = 0; while ((n = next_line(&r, text, V4_MSG_MAX_CHARS)) >= 0) { int how = v4_mesh_tell(ms, V4_MESH_HERA, text, (unsigned)n); if (how != V4_TEXT_COMPLETED) { say("V4: "); say(name); say(" did not complete: "); ms->out(text, (unsigned)n); say("\n"); return 0; } } say("PARITY:V4_CAPSULE name="); say(name); say(" capsule_id="); v4_boot_say_hex(&bt, id); say(" capsule_hash="); v4_boot_say_hex(&bt, hash); say(" dict_hash="); v4_boot_say_hex(&bt, v4_image_dict_hash(&h->n, ms->im)); say("\n"); return 1; } static int boot(v4_mesh *ms) { v4_fabric_node *h = ms->hera; unsigned long long steps = 0; unsigned i; int place; if (ms->capacity == 0 || ms->capacity > MAX_PLACES) { say("V4: the mesh has no room, or more than it can keep\n"); return 0; } /* 1. the nucleus: the ROM assembles the system on a node of its own, written as F18 cells */ bt.n = &ms->nucleus->n; bt.es = &ms->nucleus->es; bt.h = &ms->nucleus->h; bt.im = ms->im; bt.out = ms->out; bt.key = 0; bt.words = 0; bt.word_count = 0; bt.serve = 0; bt.request = 0; bt.request_self = 0; bt.capsules = 0; bt.capsule_count = 0; if (!v4_boot_assemble(&bt)) return 0; ms->cell_count = 0; ms->given = 0; (void)v4_capsule_write(&ms->nucleus->n, ms->im->idle, put_cell, ms); if (ms->cell_count == 0 || ms->cell_count >= V4_MESH_NUCLEUS_CELLS) { say("V4: the nucleus did not fit\n"); return 0; } /* 2. the fabric, and Hera born empty at place 0: the kernel on port 0, the console on port 1 */ v4_fabric_init(&ms->f, ms->places, ms->capacity); v4_fabric_queues(&ms->f, ms->queues, ms->queue_count); v4_fabric_gone_error(&ms->f, V4_ERROR_NO_ONE); for (i = 0; i < MAX_PLACES; i++) { ms->reading_nucleus[i] = 0; reads[i].p = reads[i].end = 0; } place = v4_fabric_add(&ms->f, h, ms->im->port); if (place != 0) { say("V4: Hera is not at place 0\n"); return 0; } ms->hera_place = (unsigned)place; v4_node_console_input_attach(&h->n, ms->im->console_rx, ms->im->console_status); /* KEY reads these */ if (!fit(ms->hera_place, h, HERA_ROM, HERA_ROM_WORDS, 1) || !v4_fabric_wire_device(&ms->f, ms->hera_place, V4_MESH_CONSOLE, &console)) { say("V4: Hera's ports could not be wired\n"); return 0; } while (!(h->n.doing && h->n.do_op == 8 && ms->given == ms->cell_count)) { /* she takes it in, and sleeps */ (void)v4_fabric_step(&ms->f); if (h->n.stopped || ++steps > STEP_LIMIT) { say("V4: Hera did not take in the nucleus\n"); return 0; } } /* 3. who she is; the registry; the kernel's words; FORTH-79 and the kernel's capsules; sealed */ ms->m.f = &ms->f; ms->m.reg = ms->reg; ms->m.reg_cap = ms->capacity; ms->m.hera = ms->hera_place; ms->m.born = born_cb; ms->m.gone = gone_cb; ms->m.open = open_cb; ms->m.cell = cell_cb; ms->m.line = line_cb; ms->m.parity = parity_cb; ms->m.self = ms; { int how = v4_mesh_tell(ms, 0, TOLD_FIRST, (unsigned)(sizeof TOLD_FIRST - 1u)); if (how != V4_TEXT_COMPLETED) { say("V4: Hera was not told who she is: it ended "); if (how < 0) { say("-"); v4_boot_say_dec(&bt, (uint32_t)-how); } else v4_boot_say_dec(&bt, (uint32_t)how); say("\n"); return 0; } } if (!v4_manage_enter(&ms->m, (v4_cell)V4_MESH_HERA, ms->hera_place, 0)) { say("V4: Hera not entered in the registry\n"); return 0; } for (i = 0; i < ms->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; ms->words[i][k] && at < sizeof text; k++) text[at++] = ms->words[i][k]; if (ms->words[i][k] || v4_mesh_tell(ms, V4_MESH_HERA, text, at) != V4_TEXT_COMPLETED) { say("V4: kernel word "); say(ms->words[i]); say(" could not be made\n"); return 0; } } if (!load(ms, "v4:forth79.4th")) return 0; for (i = 0; i < ms->capsule_count; i++) if (!load(ms, ms->capsules[i])) return 0; v4_image_seal(&h->n, ms->im); words_attach(ms->hera_place, h); /* her words' accounts, from now (#189, Phase 2) */ /* POST, run on her as the kernel's, through her console; then she is put back as sealed */ { v4_post_host host; v4_post_tally tally; host.self = ms; host.n = &h->n; host.line = post_line; host.say = post_say; if (!v4_post_run(&host, v4_post_cases, v4_post_case_count, &tally)) { say("V4: POST reported failures\n"); return 0; } v4_dstack_reset(&h->n.ds); { void (*was)(const char *, unsigned) = ms->out; int how; post_len = 0; post_cap = 0; ms->out = post_capture; /* what COLD prints is not shown */ how = v4_mesh_tell(ms, V4_MESH_HERA, "COLD", 4); ms->out = was; if (how != V4_TEXT_COMPLETED && how != V4_TEXT_QUIT) { say("V4: Hera did not come back to the system after POST\n"); return 0; } } } say("PARITY:V4_SYSTEM word_count="); v4_boot_say_dec(&bt, v4_boot_word_count(&h->n, ms->im)); say(" dict_hash="); v4_boot_say_hex(&bt, v4_image_dict_hash(&h->n, ms->im)); say("\n"); return 1; } int v4_mesh_boot(v4_mesh *ms) { the = ms; if (!boot(ms)) { say("PARITY:FAIL\nPOST: FAILED\n"); return 0; } say("PARITY:OK\nPOST: PASSED\n"); return 1; }