The header scan (Captain Bob, 2026-11-10): 788 source files -- 351 with the
SL-1.0 header, 12 with the old name (docs/working/experiments, kept by
ruling), 31 under someone else's licence (tools/kconfig, Linux's, GPL-2.0:
kept), and 394 with none. Ruled: the SPDX form; capsules (.4th, .f18) and
proofs (.thy) left for a ruling of their own (mkcapsule's 64x16 block format,
content-addressed capsules).
253 files now begin
SPDX-License-Identifier: LicenseRef-Starship-1.0
Copyright (c) 2023-2026 Robert A. James
as a /* */ block (C, headers, assembly, linker scripts) or # lines (scripts,
Makefiles, Kconfig; below a shebang or a coding line); tools/mkcapsule.c,
which had the copyright line, gets the SPDX line only.
Not touched: docs/working/ (archive and old campaign material, as the
ruling kept the experiments); v4/system/post_cases.c (generated by
v4/tools/mkpost.py, "do not edit" -- the generator has the header).
Comments only: the kernel builds on amd64, aarch64, riscv64; make -C v4
test-32: 39 programs, 0 failures; every changed script parses.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
184 lines
9.8 KiB
C
184 lines
9.8 KiB
C
/* SPDX-License-Identifier: LicenseRef-Starship-1.0
|
|
* Copyright (c) 2023-2026 Robert A. James
|
|
*/
|
|
/* fabric.h -- the nodes there are, how their ports are wired, and time
|
|
* passing for all of them at once. docs/v4.0.0/MESH.md 4.3.
|
|
*
|
|
* On the fabric every node runs at the same time and a node with nothing to
|
|
* do is blocked at a port. Here that is: every node that is awake and not
|
|
* blocked executes one instruction word; then every operation on a whole
|
|
* message that a node asked for is done (WHOLE MESSAGES, below); and then
|
|
* every word written that has a reader waiting on the other end of its wire
|
|
* is handed over. There is no choice of whose turn it is.
|
|
*
|
|
* THE GEOMETRY IS DATA. Which port connects to what is a table, and it can
|
|
* be changed while the nodes run; nodes can be added and removed while they
|
|
* run. Nothing here knows what shape the wiring makes.
|
|
*
|
|
* Nothing here knows what the nodes are for, either: no message, no
|
|
* capsule, no StarForth. And nothing here uses the C library: whoever owns
|
|
* the fabric supplies the memory for it and for each node.
|
|
*/
|
|
#ifndef V4_FABRIC_H
|
|
#define V4_FABRIC_H
|
|
|
|
#include "v4/exec.h"
|
|
#include "v4/wire.h"
|
|
|
|
/* What is on the other end of a port that is not a node: a console, a disk,
|
|
* the kernel that serves a node's requests. Either function may be 0. */
|
|
typedef struct {
|
|
int (*take)(void *self, v4_cell value); /* the node wrote `value`: 1 if it is taken, 0 if not yet */
|
|
int (*give)(void *self, v4_cell *value); /* the node is reading: 1 with a word in *value, 0 if not yet */
|
|
void *self;
|
|
} v4_device;
|
|
|
|
/* One end of a wire. */
|
|
enum { V4_WIRE_NONE = 0, V4_WIRE_NODE, V4_WIRE_DEVICE };
|
|
typedef struct {
|
|
int kind;
|
|
unsigned node, port; /* V4_WIRE_NODE: the other node and its port */
|
|
const v4_device *device; /* V4_WIRE_DEVICE */
|
|
v4_wire_queue *rx, *tx; /* the messages that come to this end of the wire, and that go from it (7d.2); 0: none */
|
|
} v4_wire;
|
|
|
|
/* A node, with what the engine needs to run it. Its owner supplies one of
|
|
* these for every node there is to be. */
|
|
typedef struct {
|
|
v4_node n;
|
|
v4_exec_state es;
|
|
v4_heat h;
|
|
} v4_fabric_node;
|
|
|
|
/* A place in the fabric: a node, or nothing. */
|
|
typedef struct {
|
|
v4_fabric_node *node; /* 0: no node here */
|
|
int asleep;
|
|
int cut; /* not 0: a wire of this node's has been cut since it was last woken by that (operation 9) */
|
|
v4_wire wire[V4_PORTS];
|
|
} v4_place;
|
|
|
|
typedef struct {
|
|
v4_place *place;
|
|
unsigned capacity;
|
|
v4_wire_queue *queue; /* see v4_fabric_queues; 0: none were given */
|
|
unsigned queue_count, queue_free; /* how many, and how many are not on a wire */
|
|
unsigned queue_first; /* the first that is not, and the index plus one; 0: none. Each links to the next by its `head`. */
|
|
v4_cell gone_error; /* see v4_fabric_gone_error; 0: none */
|
|
} v4_fabric;
|
|
|
|
/* An empty fabric with room for `capacity` nodes, in `places`. */
|
|
void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity);
|
|
|
|
/* THE WIRES' MEMORY (MESH.md 7d.2). Between two nodes that are wired, or a
|
|
* node and a device, the fabric keeps two queues of whole messages, one each
|
|
* way (wire.h). The owner gives it the queues: `count` of them, in `pool`,
|
|
* before anything is wired. Wiring a port takes two, cutting it gives them
|
|
* back, emptied, and wiring fails, changing nothing, when there are not two
|
|
* to take (counting those a port that is wired already would give back).
|
|
* The fabric allocates nothing.
|
|
*
|
|
* A fabric that was never given queues wires as it always did, and the
|
|
* operations on a node's wires answer that no one is on the port. That is
|
|
* how the fabrics of nothing that uses messages on the wire are made. */
|
|
void v4_fabric_queues(v4_fabric *f, v4_wire_queue *pool, unsigned count);
|
|
|
|
/* What is on the other end of a device's wire, put or taken as a whole
|
|
* message through the fabric. The device's side of `node`'s port `port`:
|
|
* put adds a message to those coming to the node, take removes the front
|
|
* message of those going from it. Each answers a V4_WIRE_ result, and
|
|
* V4_WIRE_NO_ONE when the port is not wired to a device. */
|
|
int v4_fabric_device_put(v4_fabric *f, unsigned node, unsigned port, const v4_cell *header7, const v4_cell *text);
|
|
int v4_fabric_device_take(v4_fabric *f, unsigned node, unsigned port, v4_cell *header7, v4_cell *text, unsigned text_cap);
|
|
|
|
/* A PORT WITH NOTHING ON IT. On the fabric a node that writes to such a
|
|
* port, or reads from it, waits for ever (docs/v4.0.0/MESH.md 4.1). That
|
|
* is so here for a bare node. But a node that can take an error is not
|
|
* left there: when it is blocked writing to, or reading from, one port that
|
|
* nothing is wired to -- because nothing ever was, or because what was
|
|
* there has been removed or the wire cut -- the error `code` is raised on
|
|
* it and it goes on (node.h, v4_node_port_gone). A node whose neighbour is
|
|
* asleep waits, and so does one reading "any port". With `code` 0, which
|
|
* is how a fabric begins, nothing is raised. */
|
|
void v4_fabric_gone_error(v4_fabric *f, v4_cell code);
|
|
|
|
/* A WORD NO ONE WILL READ. The same error is raised on a node that can take
|
|
* one and is blocked writing a word to a port whose other end will not read
|
|
* it: a device that takes no words (its `take` is 0: it speaks whole
|
|
* messages only), or a node that speaks whole messages -- one that has
|
|
* asked for a whole-message operation (below) since it was born, whatever
|
|
* it is doing now: asleep at its wires, running, stuck in a loop, or put to
|
|
* sleep by the fabric. Such a node does not read words, and before this a
|
|
* word written to one blocked its writer for ever (found by the final
|
|
* review of step 6e). A node that has asked for none yet may still read:
|
|
* the writer waits for it. That is a newborn node being sent its nucleus
|
|
* a word at a time, which executes from its ports and has asked nothing of
|
|
* a wire. */
|
|
|
|
/* More room: `places`, of `capacity` no less than before, takes over; every
|
|
* node keeps its number and its wiring. */
|
|
void v4_fabric_grow(v4_fabric *f, v4_place *places, unsigned capacity);
|
|
|
|
/* A node is born (node.h, v4_node_born): empty, its ports at `port_base`,
|
|
* wired to nothing, awake and blocked reading its ports. Returns its
|
|
* number, or -1 if there is no room. */
|
|
int v4_fabric_add(v4_fabric *f, v4_fabric_node *storage, v4_cell port_base);
|
|
|
|
/* A node is removed, whatever it was doing. Every wire to it is cut; a
|
|
* neighbour blocked on one of them stays blocked, as it would on a port
|
|
* with nothing there. Returns the storage it was given, or 0. */
|
|
v4_fabric_node *v4_fabric_remove(v4_fabric *f, unsigned id);
|
|
|
|
/* The node numbered `id`, or 0 if there is none. */
|
|
v4_fabric_node *v4_fabric_node_at(const v4_fabric *f, unsigned id);
|
|
|
|
/* Wire port `pa` of node `a` to port `pb` of node `b`, or to a device; or
|
|
* cut whatever is on it. Whatever was on a port before is cut, at both
|
|
* ends. Each returns 1, or 0 if a node or a port is not there. */
|
|
int v4_fabric_wire(v4_fabric *f, unsigned a, unsigned pa, unsigned b, unsigned pb);
|
|
int v4_fabric_wire_device(v4_fabric *f, unsigned a, unsigned pa, const v4_device *device);
|
|
/* THE KERNEL ON A PORT (MESH.md 7d.5, ruled 2026-10-08). A node asks its
|
|
* kernel by words written to a port, and the kernel takes no messages: its
|
|
* port is wired to the device as above but is given no queues. Words are
|
|
* handed over as for any device; every operation on whole messages there
|
|
* answers that no one is on the port, so that a message sent to it is
|
|
* refused at once and nothing waits for it. */
|
|
int v4_fabric_wire_kernel(v4_fabric *f, unsigned a, unsigned pa, const v4_device *device);
|
|
int v4_fabric_unwire(v4_fabric *f, unsigned a, unsigned pa);
|
|
|
|
/* A node that is asleep executes nothing, and nothing is handed to it or
|
|
* taken from it. It can be put to sleep and woken at any instruction
|
|
* word. */
|
|
void v4_fabric_sleep(v4_fabric *f, unsigned id);
|
|
void v4_fabric_wake(v4_fabric *f, unsigned id);
|
|
|
|
/* WHOLE MESSAGES. A node that has stored an operation to Do (node.h) is
|
|
* blocked, and in the step in which it did the operation is done, whole, and
|
|
* the node is let go: put, take, move and drop and the rest (MESH.md 7d.3).
|
|
* Take, move and drop are done only on the message the node last looked at
|
|
* on that wire (wire.h, THE LOOK): if the mark is at any other -- the wire
|
|
* was cut and made again since -- they answer 3 and nothing is written.
|
|
* Sleep (8) and sleep for room (9) are the two that wait: the node stays
|
|
* blocked, executing nothing, until a message has come to one of its wires
|
|
* since it last fetched which wires have one. A message has "come" when a
|
|
* put or a move or a device put put it on the back of a queue; fetching Have
|
|
* clears that for all of the node's wires.
|
|
* SLEEP FOR ROOM (9) answers 0 at once if the wire it names has the room,
|
|
* and 1 at once if it can never have it. Otherwise the node sleeps until a
|
|
* message has come, as for 8, or a message has come off ANY of the wires
|
|
* that lead from it -- taken, moved on or dropped by whoever reads that wire
|
|
* -- since it was last woken that way, or one of its wires has been cut;
|
|
* then it is answered 0 if the wire it named now has the room and 1 if not,
|
|
* and it asks which wire has what (10). A node may be waiting to put on several wires at once and can name
|
|
* only one. A wake-up that finds nothing to do is answered by asking
|
|
* again.
|
|
*
|
|
* Time passes: every node that is awake and not blocked executes one
|
|
* instruction word; then every write with a reader waiting on the other end
|
|
* of its wire is handed over, and every device is asked. Returns how many
|
|
* instructions were executed and words handed over together: 0 means
|
|
* nothing can happen until something outside changes. */
|
|
unsigned v4_fabric_step(v4_fabric *f);
|
|
|
|
#endif /* V4_FABRIC_H */
|