feat(v4): the mesh boot to spec -- Hera born empty in the kernel's fabric; births, kills, every node stepping together (#189)
ENGINE step 4 Phase 1, Tasks 5b-5d, and the edge cases (Captain Bob, 2026-11-13: "Build it to proper spec"; "Check for the edge cases"; "Yes, add it to v4-checks"). - v4/system/mesh.c, v4/include/v4/mesh.h: the nucleus assembled by the ROM and given to Hera as F18 cells; Hera born empty at place 0, the kernel on port 0, the console on port 1; told she is 10, in the registry, the kernel's words, FORTH-79, sealed, POST, the kernel's capsules. NODE-BORN / NODE-KILL in the owner's room. - No scheduler: v4_mesh_tell returns once the node told says how its text ended, and v4_mesh_step / v4_mesh_busy let the host step the fabric while it waits, so nodes run between lines as during them. A text left "waiting" is owed its end: the next line ends it "Interrupted" and is then run (MESH.md 7b.6). - kernel: sk_v4.c boots through the mesh; its idle steps the fabric while a node can run, and halts only when none can (#151 kept). stadium_return_quota and stadium_ops_forget on a kill. - Tests: v4/tests/test_mesh_boot.c (32 checks: the boot, births, kills, rebirth, a live number, a fault, no such node, Hera not killing herself, no room, a line too long, a node running for ever, a wait broken by the next line), run by `make -C v4 test` and under ASan+UBSan by `sanitize` (v4-checks); kernel/tests quota return; scripts/qemu_session.py births() and the edges on bare metal. - Docs: ENGINE.md 6 "who runs next" struck (settled: no scheduler); the Phase 1 plan; FABRIC-3 XLVIII.23; .claude/memory/rules.md: alpha.5 closed to new work. - Evidence: v4-checks 80 programs under the sanitizers, 0 failures; the hosted builds alike; two stopped v4-checks runs labelled STOPPED; three development amd64 boots labelled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017RzZgrf7V7Zk2iczBXDRbm
This commit is contained in:
+34
-3
@@ -29,7 +29,9 @@ CFLAGS ?= $(CSTD) $(WARN) $(OPT)
|
||||
HERE := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
|
||||
|
||||
SRCS := $(wildcard $(HERE)/src/*.c)
|
||||
TESTS := $(wildcard $(HERE)/tests/test_*.c)
|
||||
# test_mesh_boot.c is the kernel's boot of the fabric, linked as the hosted product is (its own rule,
|
||||
# `mesh-boot`, below), not one of the per-width tests.
|
||||
TESTS := $(filter-out %/test_mesh_boot.c,$(wildcard $(HERE)/tests/test_*.c))
|
||||
|
||||
# Cell widths to build and verify. 32 is the F18/Zynq case, 64 the aarch64
|
||||
# host case; see DECOMPOSITION.md D-5.
|
||||
@@ -344,12 +346,41 @@ $(foreach w,$(WIDTHS),$(foreach t,$(TESTS),$(eval $(call TEST_RULE,$(w),$(t)))))
|
||||
$(foreach w,$(WIDTHS),$(foreach t,$(TESTS),$(eval $(call RUN_RULE,$(w),$(t)))))
|
||||
$(foreach w,$(WIDTHS),$(foreach t,$(TESTS),$(eval $(call SAN_RULE,$(w),$(t)))))
|
||||
|
||||
sanitize: $(foreach w,$(WIDTHS),$(foreach t,$(TESTS),san-$(w)-$(notdir $(t))))
|
||||
sanitize: $(foreach w,$(WIDTHS),$(foreach t,$(TESTS),san-$(w)-$(notdir $(t)))) san-mesh-boot
|
||||
@echo "all v4 tests passed under ASan+UBSan"
|
||||
|
||||
.PHONY: $(foreach w,$(WIDTHS),$(foreach t,$(TESTS),run-$(w)-$(notdir $(t)) san-$(w)-$(notdir $(t))))
|
||||
|
||||
test: $(addprefix test-,$(WIDTHS))
|
||||
# THE MESH BOOT (system/mesh.c; #189, ENGINE step 4 Phase 1, Task 5b): the kernel's boot of the
|
||||
# fabric, tested on the host with tests/test_mesh_boot.c as its owner. Linked as the hosted product
|
||||
# is, with the registry (src/manage.c) and a mesh node's ROM; 64-bit cells, as the capsules are.
|
||||
MESH_BOOT_SRCS := $(HERE)/system/mesh.c $(HERE)/src/manage.c $(BINDIR)/mesh-rom-64.c
|
||||
$(BINDIR)/test_mesh_boot: $(HERE)/tests/test_mesh_boot.c $(MESH_BOOT_SRCS) $(MACHINE_64) $(CAPSULE_DIR_C) $(ENGINE_SRCS) $(SYSTEM_SRCS) $(BLOCKS_SRC) $(V3_BLOCK_SRCS) $(wildcard $(HERE)/include/v4/*.h) $(HERE)/Makefile
|
||||
$(CC) $(CFLAGS) -D_POSIX_C_SOURCE=200809L -I$(HERE)/include $(V3_INC) -DV4_CELL_BITS=64 $(HOST_DEFS) -c $(HERE)/tests/test_mesh_boot.c -o $(BINDIR)/test_mesh_boot.o
|
||||
$(CC) $(SYSTEM_CFLAGS) -c $(HERE)/system/mesh.c -o $(BINDIR)/mesh-64.o
|
||||
@rm -rf $(BINDIR)/v3o-mesh && mkdir -p $(BINDIR)/v3o-mesh
|
||||
cd $(BINDIR)/v3o-mesh && $(CC) $(V3_CFLAGS) -I$(HERE)/include -DV4_CELL_BITS=64 $(HOST_DEFS) -c $(V3_BLOCK_SRCS) $(BLOCKS_SRC)
|
||||
$(CC) $(SYSTEM_CFLAGS) $(BINDIR)/test_mesh_boot.o $(BINDIR)/mesh-64.o $(HERE)/src/manage.c $(BINDIR)/mesh-rom-64.c $(MACHINE_64) $(CAPSULE_DIR_C) $(ENGINE_SRCS) $(SYSTEM_SRCS) $(BINDIR)/v3o-mesh/*.o -o $@
|
||||
|
||||
.PHONY: mesh-boot
|
||||
mesh-boot: $(BINDIR)/test_mesh_boot
|
||||
@echo " [mesh boot, V4_CELL_BITS=64]"
|
||||
@$(BINDIR)/test_mesh_boot
|
||||
|
||||
# The same, under ASan+UBSan (Captain Bob, 2026-11-13: "Yes, add it to v4-checks"): run by `sanitize`.
|
||||
MESH_SAN := -O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined -fno-sanitize-recover=all
|
||||
$(BINDIR)/san-test_mesh_boot: $(BINDIR)/test_mesh_boot
|
||||
$(CC) $(CFLAGS) $(MESH_SAN) -D_POSIX_C_SOURCE=200809L -I$(HERE)/include $(V3_INC) -DV4_CELL_BITS=64 $(HOST_DEFS) -c $(HERE)/tests/test_mesh_boot.c -o $(BINDIR)/san-test_mesh_boot.o
|
||||
@rm -rf $(BINDIR)/san-v3o-mesh && mkdir -p $(BINDIR)/san-v3o-mesh
|
||||
cd $(BINDIR)/san-v3o-mesh && $(CC) $(V3_CFLAGS) $(MESH_SAN) -I$(HERE)/include -DV4_CELL_BITS=64 $(HOST_DEFS) -c $(V3_BLOCK_SRCS) $(BLOCKS_SRC)
|
||||
$(CC) $(SYSTEM_CFLAGS) $(MESH_SAN) $(BINDIR)/san-test_mesh_boot.o $(HERE)/system/mesh.c $(HERE)/src/manage.c $(BINDIR)/mesh-rom-64.c $(MACHINE_64) $(CAPSULE_DIR_C) $(ENGINE_SRCS) $(SYSTEM_SRCS) $(BINDIR)/san-v3o-mesh/*.o -o $@
|
||||
|
||||
.PHONY: san-mesh-boot
|
||||
san-mesh-boot: $(BINDIR)/san-test_mesh_boot
|
||||
@echo " [ASan+UBSan mesh boot, V4_CELL_BITS=64]"
|
||||
@$(BINDIR)/san-test_mesh_boot
|
||||
|
||||
test: $(addprefix test-,$(WIDTHS)) mesh-boot
|
||||
@echo "all v4 tests passed"
|
||||
|
||||
clean:
|
||||
|
||||
+21
-3
@@ -5,9 +5,13 @@
|
||||
* 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:
|
||||
* The hosted binary (tools/hosted.c) calls v4_boot_run: one node, which its
|
||||
* ROM brings up and which is handed its lines. The bare-metal kernel
|
||||
* (kernel/src/v4/sk_v4.c) did too, until 2026-11-13; it now boots the fabric
|
||||
* (v4/mesh.h; #189, ENGINE step 4 Phase 1): the ROM assembles the nucleus by
|
||||
* v4_boot_assemble, and Hera is born empty and given it. Corrected
|
||||
* 2026-11-13 -- this said the two "differ only in where the text goes".
|
||||
* Both print the same parity lines, in the same order:
|
||||
*
|
||||
* 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)
|
||||
@@ -117,4 +121,18 @@ int v4_boot_line(const v4_boot *b, const char *text, unsigned len);
|
||||
/* The dictionary hash of a node started from `im`. */
|
||||
uint64_t v4_boot_dict_hash(const v4_node *n, const v4_image *im);
|
||||
|
||||
/* WHAT THE MESH BOOT SHARES (v4/mesh.h; #189). v4_boot_assemble: the node
|
||||
* `b->n` is born empty and its ROM assembles the system from its source,
|
||||
* the PARITY:V4_F18 and PARITY:V4_ROM lines printed, `b->im` completed from
|
||||
* the image record; 1, or 0 with the failure said -- what v4_boot_run does
|
||||
* first. v4_boot_capsule: the capsule `name` found in the directory, its
|
||||
* hash and signature checked as the boot checks them, a failure said; its
|
||||
* payload, or 0. And the boot's own way of printing numbers, so the mesh
|
||||
* prints the same parity lines. */
|
||||
int v4_boot_assemble(const v4_boot *b);
|
||||
uint32_t v4_boot_word_count(const v4_node *n, const v4_image *im);
|
||||
const uint8_t *v4_boot_capsule(const v4_boot *b, const char *name, uint32_t *length, uint64_t *id, uint64_t *hash);
|
||||
void v4_boot_say_dec(const v4_boot *b, uint32_t v);
|
||||
void v4_boot_say_hex(const v4_boot *b, uint64_t v);
|
||||
|
||||
#endif /* V4_BOOT_H */
|
||||
|
||||
@@ -49,6 +49,7 @@ typedef struct {
|
||||
v4_cell dstack_reg, rstack_reg;
|
||||
v4_cell node_error;
|
||||
v4_cell heat_reg; /* HEAT-OP[0]; ANTICLOCK is 32 after it (node.h, #189) */
|
||||
v4_cell me; /* (ME): the node's number, which messages go by (v4/mesh.h, #189) */
|
||||
|
||||
/* the dictionary's two variables: DP holds HERE, a byte address, and
|
||||
* LATEST the newest word of FORTH. What lies below HERE, and LATEST,
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/* SPDX-License-Identifier: LicenseRef-Starship-1.0
|
||||
* Copyright (c) 2023-2026 Robert A. James
|
||||
*/
|
||||
/* mesh.h -- the kernel's boot of the fabric: Hera born empty, given the
|
||||
* system, and every node stepped together. docs/v4.0.0/MESH.md 9,
|
||||
* MACHINE.md 3; #189, ENGINE step 4 Phase 1, Task 5b (Captain Bob,
|
||||
* 2026-11-13: "Build it to proper spec").
|
||||
*
|
||||
* There is no scheduler (JUSTIFICATION.md 4: "There is no scheduler to
|
||||
* write"): every awake node executes an instruction word at each step of
|
||||
* the fabric (fabric.h), and a node with nothing to do waits at a port.
|
||||
*
|
||||
* THE BOOT, as v4/tests/test_host_unit.c does it on the host:
|
||||
* 1. the nucleus: the machine's ROM assembles the system's source on a
|
||||
* node of its own (v4/boot.h, v4_boot_assemble), and the system, as
|
||||
* assembled, is a capsule of F18 cells -- a node that is born is given
|
||||
* assembled code, never the source (Captain Bob, 2026-10-15);
|
||||
* 2. Hera is born empty at place 0, with the machine's ROM: the kernel is
|
||||
* on her port 0 -- it gives her the nucleus a cell at a time, and takes
|
||||
* her requests: blocks, the kernel's words, the registry (manage.h) --
|
||||
* and the console on her port 1, which speaks whole messages;
|
||||
* 3. she is told she is node 10 and where the console is, entered in the
|
||||
* registry, made the kernel's words, given FORTH-79, sealed, POSTed,
|
||||
* given the kernel's capsules, and sealed;
|
||||
* printing the same parity lines as v4_boot_run (boot.h).
|
||||
*
|
||||
* A NODE IS BORN (NODE-BORN): in storage the owner gives, with a mesh
|
||||
* node's ROM, the kernel on its port 0 for its blocks; it is given the
|
||||
* nucleus by whoever birthed it (centre.4th). NODE-KILL gives the storage
|
||||
* back.
|
||||
*
|
||||
* Nothing here uses the C library. The owner supplies all the memory.
|
||||
*/
|
||||
#ifndef V4_MESH_H
|
||||
#define V4_MESH_H
|
||||
|
||||
#include "v4/boot.h"
|
||||
#include "v4/blocks.h"
|
||||
#include "v4/fabric.h"
|
||||
#include "v4/manage.h"
|
||||
|
||||
#define V4_MESH_HERA 10 /* Hera's number, as the host mesh numbers her */
|
||||
#define V4_MESH_CONSOLE 1 /* the console's number as a sender, and her port to it */
|
||||
#define V4_MESH_NUCLEUS_CELLS (2u * V4_NODE_WORDS + 64u)
|
||||
|
||||
typedef struct v4_mesh v4_mesh;
|
||||
struct v4_mesh {
|
||||
/* ---- the owner's: set before v4_mesh_boot ---- */
|
||||
v4_place *places; /* the fabric's room: `capacity` places */
|
||||
unsigned capacity;
|
||||
v4_wire_queue *queues; /* the wires' memory (fabric.h): two for every wire at once */
|
||||
unsigned queue_count;
|
||||
v4_manage_entry *reg; /* the registry: `capacity` entries */
|
||||
v4_blocks *blocks; /* each place's block window, kept by the kernel: `capacity` of them */
|
||||
v4_fabric_node *nucleus; /* where the ROM assembles the system: a node of its own */
|
||||
v4_fabric_node *hera; /* Hera's storage */
|
||||
v4_device *kernel; /* the kernel on each place's port 0: `capacity` of them */
|
||||
/* storage for a node to be born, or 0: none; and given back */
|
||||
v4_fabric_node *(*room)(void *self);
|
||||
void (*unroom)(void *self, v4_fabric_node *x);
|
||||
/* a node has been born at `place`, numbered `number` once it is told; is gone (may be 0) */
|
||||
void (*born)(void *self, unsigned place);
|
||||
void (*gone)(void *self, unsigned place);
|
||||
/* a node the asker birthed has its system: its parity (may be 0) */
|
||||
void (*parity)(void *self, unsigned place, v4_cell number, const v4_node *n);
|
||||
/* the console, the keyboard, the kernel's words and capsules: as v4_boot's (boot.h) */
|
||||
void (*out)(const char *text, unsigned len);
|
||||
int (*key)(void);
|
||||
const char *const *words;
|
||||
unsigned word_count;
|
||||
void (*serve)(v4_node *n, unsigned request);
|
||||
const char *const *capsules;
|
||||
unsigned capsule_count;
|
||||
v4_image *im; /* the map's description (v4_map_image), which the boot completes */
|
||||
void *self;
|
||||
|
||||
/* ---- the mesh's own ---- */
|
||||
v4_fabric f;
|
||||
v4_manage m;
|
||||
unsigned hera_place;
|
||||
v4_cell cells[V4_MESH_NUCLEUS_CELLS]; /* the nucleus as a capsule of F18 cells */
|
||||
unsigned cell_count, given; /* how many; how many given to Hera */
|
||||
unsigned reading_nucleus[64]; /* by place: the next nucleus cell a CAPSULE-CELL gives; 0: not open */
|
||||
unsigned dones; /* how many texts have said how they ended, ever */
|
||||
v4_cell done_from, done_how; /* the last of them: from which node, and how */
|
||||
v4_cell owed_from; /* a node told texts that were left "waiting" ... */
|
||||
unsigned owed; /* ... and how many of their ends are still to come */
|
||||
};
|
||||
|
||||
/* The boot: the nucleus, Hera, her system, POST. 1, Hera asleep at her
|
||||
* wires waiting for a line; or 0, the failure printed. */
|
||||
int v4_mesh_boot(v4_mesh *ms);
|
||||
|
||||
/* Text for node `node`, from the console, and the fabric stepped until that node says how the text
|
||||
* ended: what any node prints goes to `out`; returns how it ended (V4_TEXT_*), or
|
||||
* V4_BOOT_LINE_STOPPED / _TOO_LONG / _REFUSED / _NO_INPUT (boot.h). Other nodes may run on after it
|
||||
* returns (v4_mesh_step). V4_BOOT_LINE_WAITING: Hera is asleep awaiting an answer that has not come
|
||||
* -- nothing else can run, or other nodes have run a long while and not given it; the next text from
|
||||
* the console breaks the wait (MESH.md 7b.6), and if the answer comes first, `dones` says so. */
|
||||
int v4_mesh_tell(v4_mesh *ms, v4_cell node, const char *text, unsigned len);
|
||||
|
||||
/* One step of the fabric, as the host's idle: every awake node executes an instruction word, and what
|
||||
* comes back to the console is printed (or counted, for how a text ended: `dones`). Returns how many
|
||||
* instructions were retired. The host calls it while it waits, whenever v4_mesh_busy says a node can
|
||||
* run: every node steps together, between lines as during them. */
|
||||
unsigned v4_mesh_step(v4_mesh *ms);
|
||||
int v4_mesh_busy(const v4_mesh *ms);
|
||||
|
||||
/* The node numbered `number`, or 0. */
|
||||
v4_fabric_node *v4_mesh_node(const v4_mesh *ms, v4_cell number);
|
||||
|
||||
#endif
|
||||
+27
-5
@@ -568,19 +568,41 @@ static int load_system(const v4_boot *b)
|
||||
|
||||
/* ---- the whole boot ------------------------------------------------------ */
|
||||
|
||||
int v4_boot_run(const v4_boot *b)
|
||||
{
|
||||
unsigned i;
|
||||
/* ---- what the mesh boot shares (v4/mesh.h; #189) ---------------------------- */
|
||||
|
||||
int v4_boot_assemble(const v4_boot *b)
|
||||
{
|
||||
/* a node is born with nothing in it, and its ROM assembles the system */
|
||||
if (!v4_image_born(b->n, b->es, b->h, b->im)) {
|
||||
say(b, "V4: the memory map is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
say(b, "V4: the memory map is not for this build of the engine\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_system(b)) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; }
|
||||
return load_system(b);
|
||||
}
|
||||
|
||||
uint32_t v4_boot_word_count(const v4_node *n, const v4_image *im) { return word_count(n, im); }
|
||||
void v4_boot_say_dec(const v4_boot *b, uint32_t v) { say_dec(b, v); }
|
||||
void v4_boot_say_hex(const v4_boot *b, uint64_t v) { say_hex(b, v); }
|
||||
|
||||
const uint8_t *v4_boot_capsule(const v4_boot *b, const char *name, uint32_t *length, uint64_t *id, uint64_t *hash)
|
||||
{
|
||||
const CapsuleDesc *cap;
|
||||
const uint8_t *p = open_capsule(b, name, &cap);
|
||||
if (!p) return 0;
|
||||
*length = (uint32_t)cap->length;
|
||||
*id = cap->capsule_id;
|
||||
*hash = cap->content_hash;
|
||||
return p;
|
||||
}
|
||||
|
||||
int v4_boot_run(const v4_boot *b)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (!v4_boot_assemble(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++) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,170 @@
|
||||
/* SPDX-License-Identifier: LicenseRef-Starship-1.0
|
||||
* Copyright (c) 2023-2026 Robert A. James
|
||||
*/
|
||||
/* test_mesh_boot.c -- the kernel's boot of the fabric (v4/mesh.h), on the host, with this program as
|
||||
* its owner, as the kernel will be (#189, ENGINE step 4 Phase 1, Task 5b; Captain Bob, 2026-11-13:
|
||||
* "Build it to proper spec"). No scheduler: every node steps together.
|
||||
*
|
||||
* The boot: the nucleus assembled by the ROM, Hera born empty and given it, FORTH-79, POST, the
|
||||
* kernel's capsules -- and the same parity lines v4_boot_run prints. Then: a line; NODE-ME; a node
|
||||
* born (11 2 2 BIRTH), given the nucleus and FORTH-79 by Hera, answering a line of its own; and
|
||||
* killed, its room given back.
|
||||
*
|
||||
* Linked as the hosted product is (v4/Makefile), with mesh.c, manage.c and a mesh node's ROM.
|
||||
*/
|
||||
#include "v4/mesh.h"
|
||||
#include "v4/message.h"
|
||||
#include "block_subsystem.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static int failures = 0, checks = 0;
|
||||
#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0)
|
||||
|
||||
#define PLACES 8u
|
||||
static v4_place places[PLACES];
|
||||
static v4_wire_queue queues[32];
|
||||
static v4_manage_entry reg[PLACES];
|
||||
static v4_blocks blocks[PLACES];
|
||||
static v4_device kernel[PLACES];
|
||||
static v4_fabric_node nucleus, hera, pool[PLACES];
|
||||
static int used[PLACES];
|
||||
static unsigned roomed, given_back, borns, gones, parities;
|
||||
static v4_image im;
|
||||
static v4_mesh ms;
|
||||
|
||||
static int no_room; /* the owner has no room to give */
|
||||
static v4_fabric_node *room(void *self) { unsigned k; (void)self; if (no_room) return 0; for (k = 0; k < PLACES; k++) if (!used[k]) { used[k] = 1; roomed++; return &pool[k]; } return 0; }
|
||||
static void unroom(void *self, v4_fabric_node *x) { (void)self; used[x - pool] = 0; given_back++; }
|
||||
static void born(void *self, unsigned place) { (void)self; (void)place; borns++; }
|
||||
static void gone(void *self, unsigned place) { (void)self; (void)place; gones++; }
|
||||
static void parity(void *self, unsigned place, v4_cell number, const v4_node *n) { (void)self; (void)place; (void)number; (void)n; parities++; }
|
||||
|
||||
static char said[1u << 16];
|
||||
static unsigned said_len;
|
||||
static void out(const char *text, unsigned len)
|
||||
{
|
||||
if (said_len + len + 1 < sizeof said) { memcpy(said + said_len, text, len); said_len += len; said[said_len] = 0; }
|
||||
}
|
||||
static int key(void) { return V4_BOOT_KEY_END; }
|
||||
static const char *const words[] = { "BYE" };
|
||||
static unsigned served;
|
||||
static void serve(v4_node *n, unsigned request) { (void)n; if (request == 1) served++; }
|
||||
static const char *const capsules[] = { "v4:centre.4th", "v4:hera.4th" };
|
||||
|
||||
static unsigned char ram[(size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE];
|
||||
extern void sf_time_init(void);
|
||||
extern void log_set_level(int level);
|
||||
|
||||
/* what a line printed, and how it ended */
|
||||
static const char *line(v4_cell node, const char *text, int *how)
|
||||
{
|
||||
said_len = 0; said[0] = 0;
|
||||
*how = v4_mesh_tell(&ms, node, text, (unsigned)strlen(text));
|
||||
return said;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int how;
|
||||
const char *r;
|
||||
|
||||
printf("v4 mesh boot: V4_CELL_BITS=%d\n", V4_CELL_BITS);
|
||||
sf_time_init();
|
||||
log_set_level(-1);
|
||||
CHECK(blk_subsys_init(ram, sizeof ram) == BLK_OK, "the kernel's blocks");
|
||||
|
||||
im = v4_map_image;
|
||||
ms.places = places; ms.capacity = PLACES; ms.queues = queues; ms.queue_count = sizeof queues / sizeof queues[0];
|
||||
ms.reg = reg; ms.blocks = blocks; ms.nucleus = &nucleus; ms.hera = &hera; ms.kernel = kernel;
|
||||
ms.room = room; ms.unroom = unroom; ms.born = born; ms.gone = gone; ms.parity = parity;
|
||||
ms.out = out; ms.key = key; ms.words = words; ms.word_count = 1; ms.serve = serve;
|
||||
ms.capsules = capsules; ms.capsule_count = 2; ms.im = &im; ms.self = 0;
|
||||
|
||||
/* ---- the boot ---- */
|
||||
CHECK(v4_mesh_boot(&ms) == 1, "the boot: %s", said);
|
||||
CHECK(strstr(said, "PARITY:V4_ROM ") != 0, "the ROM assembled the nucleus, and said so");
|
||||
CHECK(strstr(said, "PARITY:V4_CAPSULE name=v4:forth79.4th ") && strstr(said, "PARITY:V4_CAPSULE name=v4:centre.4th ")
|
||||
&& strstr(said, "PARITY:V4_CAPSULE name=v4:hera.4th "), "FORTH-79 and the kernel's capsules, each with its parity line");
|
||||
CHECK(strstr(said, "PARITY:V4_POST tests=539 pass=539 fail=0") != 0, "POST passes on Hera, born in the fabric");
|
||||
CHECK(strstr(said, "PARITY:V4_SYSTEM word_count=") && strstr(said, "PARITY:OK\nPOST: PASSED\n"), "the system line, and POST: PASSED");
|
||||
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
|
||||
|
||||
/* ---- lines, as messages ---- */
|
||||
r = line(V4_MESH_HERA, "3 4 + .", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && strcmp(r, "7 ") == 0, "a line to Hera: \"%s\" (%d)", r, how);
|
||||
r = line(V4_MESH_HERA, "NODE-ME .", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && strcmp(r, "0 ") == 0, "NODE-ME: Hera is at place 0: \"%s\"", r);
|
||||
r = line(V4_MESH_HERA, "NOSUCH", &how);
|
||||
CHECK(how == V4_TEXT_ERROR && strstr(r, "UNKNOWN WORD") != 0, "an unknown word ends in an error: \"%s\"", r);
|
||||
r = line(V4_MESH_HERA, "BYE", &how);
|
||||
CHECK(served == 1, "a kernel word is served by the owner");
|
||||
|
||||
/* ---- a birth, and a kill ---- */
|
||||
r = line(V4_MESH_HERA, "11 2 2 BIRTH .", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && roomed == 1 && borns == 1 && parities == 1,
|
||||
"11 2 2 BIRTH: a node is born in the owner's room, given the system, and its parity recorded: \"%s\" (%d)", r, how);
|
||||
CHECK(v4_mesh_node(&ms, 11) != 0, "node 11 is in the fabric");
|
||||
r = line(11, "6 7 * .", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && strcmp(r, "42 ") == 0, "node 11 answers a line of its own: \"%s\" (%d)", r, how);
|
||||
r = line(V4_MESH_HERA, "S\" 6 7 * .\" 11 SEND 11 AWAIT .", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && strstr(r, "42 ") && strstr(r, "1 "),
|
||||
"and through Hera, as the prompt reaches it: she sends node 11 a line and awaits it: \"%s\" (%d)", r, how);
|
||||
r = line(V4_MESH_HERA, "11 KILL", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && v4_mesh_node(&ms, 11) == 0 && given_back == 1 && gones == 1,
|
||||
"11 KILL: the node is gone, and its room given back: \"%s\" (%d)", r, how);
|
||||
r = line(V4_MESH_HERA, "3 4 + .", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && strcmp(r, "7 ") == 0, "and Hera goes on: \"%s\"", r);
|
||||
|
||||
/* ---- the edges (Captain Bob, 2026-11-13: "Check for the edge cases") ---- */
|
||||
r = line(V4_MESH_HERA, "11 2 2 BIRTH .", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && v4_mesh_node(&ms, 11) != 0, "after a kill, 11 is born again, in the room given back (%d)", how);
|
||||
r = line(V4_MESH_HERA, "11 3 2 BIRTH .", &how);
|
||||
CHECK(how == V4_TEXT_ERROR, "a number that is live is not born again: an error (%d) \"%s\"", how, r);
|
||||
r = line(11, "1 0 /", &how);
|
||||
CHECK(how == V4_TEXT_ERROR, "a fault on node 11 ends its line in an error (%d) \"%s\"", how, r);
|
||||
r = line(11, "2 3 + .", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && strcmp(r, "5 ") == 0, "and node 11 goes on: \"%s\"", r);
|
||||
r = line(V4_MESH_HERA, "99 KILL", &how);
|
||||
CHECK(how == V4_TEXT_ERROR && strstr(r, "no such node"), "KILL of a node there is not: an error, \"%s\"", r);
|
||||
r = line(V4_MESH_HERA, "10 KILL", &how);
|
||||
CHECK(how == V4_TEXT_ERROR && v4_mesh_node(&ms, 10) != 0, "Hera does not kill herself: \"%s\"", r);
|
||||
no_room = 1;
|
||||
r = line(V4_MESH_HERA, "12 3 2 BIRTH .", &how);
|
||||
CHECK(how == V4_TEXT_ERROR && v4_mesh_node(&ms, 12) == 0, "no room: the birth fails in an error, \"%s\"", r);
|
||||
no_room = 0;
|
||||
r = line(V4_MESH_HERA, "3 4 + .", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && strcmp(r, "7 ") == 0, "and Hera goes on: \"%s\"", r);
|
||||
{
|
||||
char big[V4_MSG_MAX_CHARS + 8];
|
||||
memset(big, ' ', sizeof big - 1); big[sizeof big - 1] = 0;
|
||||
r = line(V4_MESH_HERA, big, &how);
|
||||
CHECK(how == V4_BOOT_LINE_TOO_LONG, "a line too long for a message is not run (%d)", how);
|
||||
}
|
||||
/* a node set going for ever does not hold the console: Hera's line ends, and node 11 goes on
|
||||
* running between lines -- every node steps together, not only while a line runs */
|
||||
r = line(V4_MESH_HERA, "S\" VARIABLE N1 : SPIN BEGIN 1 N1 +! AGAIN ;\" 11 SEND 11 AWAIT .", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && strstr(r, "1 "), "node 11 is given SPIN, which runs for ever (%d) \"%s\"", how, r);
|
||||
r = line(V4_MESH_HERA, "S\" SPIN\" 11 SEND", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED, "Hera's line ends, though node 11 now runs for ever (%d) \"%s\"", how, r);
|
||||
{
|
||||
v4_fabric_node *x = v4_mesh_node(&ms, 11);
|
||||
v4_uheat_t before = x ? x->es.anticlock : 0;
|
||||
unsigned k;
|
||||
for (k = 0; k < 1000; k++) v4_mesh_step(&ms); /* the host's idle: the fabric steps on */
|
||||
CHECK(x && x->es.anticlock > before + 1000u, "between lines node 11 runs on: every node steps together");
|
||||
}
|
||||
r = line(V4_MESH_HERA, "11 KILL", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && v4_mesh_node(&ms, 11) == 0, "and a node running for ever is killed (%d) \"%s\"", how, r);
|
||||
/* a wait no one will answer: the next line breaks it */
|
||||
r = line(V4_MESH_HERA, "11 2 2 BIRTH DROP 12 AWAIT", &how);
|
||||
CHECK(how == V4_BOOT_LINE_WAITING, "AWAIT on a node that will never say: the line waits (%d)", how);
|
||||
r = line(V4_MESH_HERA, "3 4 + .", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && strstr(r, "Interrupted") && strstr(r, "7 ") > strstr(r, "Interrupted"),
|
||||
"and the next line ends the wait \"Interrupted\", and is then run (MESH.md 7b.6): \"%s\" (%d)", r, how);
|
||||
r = line(V4_MESH_HERA, "3 4 + .", &how);
|
||||
CHECK(how == V4_TEXT_COMPLETED && strcmp(r, "7 ") == 0 && ms.owed == 0, "and nothing more is owed: \"%s\"", r);
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
+2
-2
@@ -53,9 +53,9 @@ static int image_description(void)
|
||||
printf("#include \"v4/image.h\"\n\nconst v4_image v4_map_image = {\n");
|
||||
printf(" %uu, %uu, %uu, %uu,\n", (unsigned)V4_CELL_BITS, (unsigned)V4_NODE_WORDS, (unsigned)V4_DATA_RING, (unsigned)V4_RET_RING);
|
||||
printf(" -1,\n -1, %ld,\n -1, -1,\n", (long)PORT);
|
||||
printf(" %ld, %ld, %ld,\n %ld, %ld,\n %ld,\n %ld,\n %ld, %ld,\n %ld, %ld,\n %ld,\n",
|
||||
printf(" %ld, %ld, %ld,\n %ld, %ld,\n %ld,\n %ld, %ld,\n %ld, %ld,\n %ld, %ld,\n %ld,\n",
|
||||
(long)CONSOLE_TX, (long)CONSOLE_RX, (long)CONSOLE_ST, (long)DSTACK_REG, (long)RSTACK_REG,
|
||||
(long)NODE_ERROR, (long)HEAT_REG, (long)DP, (long)LATEST, (long)BOOT_CELLS, (long)FENCE, (long)BUF0_W);
|
||||
(long)NODE_ERROR, (long)HEAT_REG, (long)ME, (long)DP, (long)LATEST, (long)BOOT_CELLS, (long)FENCE, (long)BUF0_W);
|
||||
printf(" %ld, %ld, %ld\n};\n", (long)ROM_IMG, (long)ROM_FAILED, (long)ROM_ERRLINE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user