feat(v4.0.0): the wire in the engine -- whole messages, put and taken in one step
Queues of whole messages on the wires between nodes (v4/wire.h), the six addresses and ten operations a node asks of the fabric (MESH.md 7d.3), and the fabric's pool of queues. Engine only: the nucleus does not use any of it yet. The ports in the host map move to BUF0_W - 140 so that the new addresses are not among the nucleus variables, with a compile-time check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
50be766e7b
commit
d5cd64605d
+37
-1
@@ -19,6 +19,7 @@
|
||||
#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. */
|
||||
@@ -35,6 +36,7 @@ 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
|
||||
@@ -55,6 +57,9 @@ typedef struct {
|
||||
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_cell interrupt_error; /* see v4_fabric_interrupt_error; 0: none */
|
||||
} v4_fabric;
|
||||
@@ -62,6 +67,27 @@ typedef struct {
|
||||
/* 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
|
||||
@@ -116,7 +142,17 @@ int v4_fabric_unwire(v4_fabric *f, unsigned a, unsigned pa);
|
||||
void v4_fabric_sleep(v4_fabric *f, unsigned id);
|
||||
void v4_fabric_wake(v4_fabric *f, unsigned id);
|
||||
|
||||
/* Time passes: every node that is awake and not blocked executes one
|
||||
/* 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).
|
||||
* 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 -- or, for 9, until its wire
|
||||
* has the room. 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.
|
||||
*
|
||||
* 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
|
||||
|
||||
+46
-3
@@ -44,6 +44,15 @@
|
||||
#endif
|
||||
#define V4_PORT_ANY V4_PORTS /* not a port: "whichever port has a neighbour writing" */
|
||||
|
||||
/* The indexes, after the ports, of what a node asks of the fabric about whole
|
||||
* messages on its wires (docs/v4.0.0/MESH.md 7d.3); see v4_node_port_attach. */
|
||||
#define V4_PORT_WIRE (V4_PORTS + 4u) /* store the number of a port: what follows is about that wire */
|
||||
#define V4_PORT_A (V4_PORTS + 5u) /* store a value an operation needs */
|
||||
#define V4_PORT_B (V4_PORTS + 6u) /* store a second */
|
||||
#define V4_PORT_DO (V4_PORTS + 7u) /* store the number of an operation: the node is blocked until it is done */
|
||||
#define V4_PORT_HOW (V4_PORTS + 8u) /* fetch how it went */
|
||||
#define V4_PORT_HAVE (V4_PORTS + 9u) /* fetch which wires have a message waiting: bit k for port k */
|
||||
|
||||
#ifndef V4_NODE_WORDS
|
||||
#define V4_NODE_WORDS 1024u
|
||||
#endif
|
||||
@@ -113,6 +122,15 @@ typedef struct {
|
||||
unsigned readers; /* bit k: the neighbour on port k is blocked reading from this node */
|
||||
unsigned since_look; /* words taken from this node since it last fetched either of those */
|
||||
|
||||
/* Whole messages on the wires (MESH.md 7d.3). See v4_node_port_attach. */
|
||||
unsigned wire; /* the port that A, B and Do are about; V4_PORTS if what was stored is not a port */
|
||||
v4_cell wire_a, wire_b; /* A and B */
|
||||
int doing; /* non-zero: blocked, having stored `do_op` to Do */
|
||||
v4_cell do_op; /* the operation */
|
||||
v4_cell how; /* how the last one went */
|
||||
unsigned have; /* which wires had a message waiting, as the fabric last said: bit k for port k */
|
||||
int have_fetched; /* non-zero: `have` has been fetched since the fabric last cleared what had arrived */
|
||||
|
||||
/* DSTACK-DEPTH and RSTACK-DEPTH (D-16). See v4_node_stack_regs_attach. */
|
||||
v4_cell dstack_reg; /* its word address, or -1 */
|
||||
v4_cell rstack_reg; /* its word address, or -1 */
|
||||
@@ -187,11 +205,24 @@ void v4_node_console_attach(v4_node *n, v4_cell addr);
|
||||
* ports have anything on them at all: bit V4_PORTS + k
|
||||
* for port k. A port with nothing on it can never
|
||||
* be read from or written to.
|
||||
* The last two are how a node looks before it writes (MESH.md 7a), as the
|
||||
* base + V4_PORTS + 4 .. + 9 whole messages on the wires (MESH.md 7d.3), the
|
||||
* indexes V4_PORT_WIRE, V4_PORT_A, V4_PORT_B, V4_PORT_DO,
|
||||
* V4_PORT_HOW and V4_PORT_HAVE:
|
||||
* Wire, A, B store: what an operation is about and
|
||||
* needs; fetch gives back what was stored
|
||||
* Do store: the number of an operation. The
|
||||
* node is blocked until whoever connects
|
||||
* its ports has done it (v4_node_doing,
|
||||
* v4_node_done); fetch gives the number
|
||||
* How fetch: how the last one went
|
||||
* Have fetch: which wires have a message
|
||||
* waiting, bit k for port k, as the fabric
|
||||
* last said (v4_node_have)
|
||||
* The two before them are how a node looks before it writes (MESH.md 7a), as the
|
||||
* F18's io register lets it. They are what whoever connects the ports last
|
||||
* said, v4_node_port_status; fetching them waits for nothing and changes
|
||||
* nothing.
|
||||
* (read only)
|
||||
* nothing. Of the whole-message addresses How and Have are read only: a
|
||||
* store to either is an address fault, as to "any port".
|
||||
* None of them is memory while the ports are attached.
|
||||
*
|
||||
* A WRITE. A store to a port does not write memory: the node keeps the
|
||||
@@ -219,6 +250,18 @@ void v4_node_port_attach(v4_node *n, v4_cell base);
|
||||
/* The neighbour has taken what the node wrote. */
|
||||
void v4_node_port_served(v4_node *n);
|
||||
|
||||
/* WHOLE MESSAGES (MESH.md 7d.3). A store to Do records the operation in
|
||||
* n->do_op and blocks the node, as a store to a port does: n->doing. For
|
||||
* whoever connects the node's ports: 1 if the node is blocked at one; the
|
||||
* operation is done, `how` is what the node will fetch from How, and the
|
||||
* node goes on from the opcode after the store; and `mask` is which wires
|
||||
* have a message waiting, as the node will fetch it from Have. Fetching
|
||||
* Have also sets n->have_fetched, for whoever connects the ports to see and
|
||||
* clear. */
|
||||
int v4_node_doing(const v4_node *n);
|
||||
void v4_node_done(v4_node *n, v4_cell how);
|
||||
void v4_node_have(v4_node *n, unsigned mask);
|
||||
|
||||
/* How many words the node has had taken since it last looked at its
|
||||
* neighbours (a fetch of either of the two status addresses). A node that
|
||||
* looks before it begins a message (MESH.md 7a) and is blocked writing with
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/* wire.h -- a queue of whole messages, one way along a wire.
|
||||
* docs/v4.0.0/MESH.md section 7d.2.
|
||||
*
|
||||
* A message is what section 6 says: seven words, of which the seventh
|
||||
* (word 6) is how many characters of text follow, 0 to 1024, and then the
|
||||
* text, four characters to a word. On a queue it takes 7 cells and one for
|
||||
* each word of text, and the queue holds V4_WIRE_CELLS of them, going round.
|
||||
*
|
||||
* ALL OF A MESSAGE OR NONE. Every function here leaves the queue exactly as
|
||||
* it found it unless it answers V4_WIRE_DONE. There is no state in which
|
||||
* part of a message has been put on, or taken off.
|
||||
*
|
||||
* THE MARK. The one that reads a queue has a mark on it: the message that
|
||||
* look, take, move and drop are about. `mark` counts the cells from the
|
||||
* front to where that message begins; equal to `used`, it is at no message,
|
||||
* and those four answer V4_WIRE_NO_MESSAGE. A message put on the back of a
|
||||
* queue whose mark is at no message is the one the mark is then at.
|
||||
*
|
||||
* Nothing here allocates, and nothing uses the C library: whoever owns the
|
||||
* queue supplies its memory.
|
||||
*/
|
||||
#ifndef V4_WIRE_H
|
||||
#define V4_WIRE_H
|
||||
|
||||
#include "v4/cell.h"
|
||||
|
||||
#ifndef V4_WIRE_CELLS
|
||||
#define V4_WIRE_CELLS 1024
|
||||
#endif
|
||||
|
||||
#define V4_WIRE_HEADER 7u /* the words of a message that are not its text */
|
||||
|
||||
typedef struct {
|
||||
v4_cell cell[V4_WIRE_CELLS]; /* the messages, one after another, going round */
|
||||
unsigned head; /* where the front message begins */
|
||||
unsigned used; /* how many cells they take */
|
||||
unsigned mark; /* how many cells from the front the reader's mark is; == used: at no message */
|
||||
unsigned arrived; /* not 0: a message has come since the reader last asked which wires have one */
|
||||
} v4_wire_queue;
|
||||
|
||||
enum { V4_WIRE_DONE = 0, V4_WIRE_NO_ROOM = 1, V4_WIRE_NO_ONE = 2, V4_WIRE_NO_MESSAGE = 3, V4_WIRE_NOT_A_MESSAGE = 4 };
|
||||
|
||||
/* Empty, and the mark at no message. */
|
||||
void v4_wire_reset(v4_wire_queue *q);
|
||||
|
||||
/* The cells a message of `length` characters takes: 7 and a cell for each
|
||||
* four characters. 0 if `length` is not 0 to 1024: it is not a message. */
|
||||
unsigned v4_wire_cells(v4_cell length);
|
||||
|
||||
/* V4_WIRE_DONE if `cells` more would fit, V4_WIRE_NO_ROOM if not. Changes
|
||||
* nothing. */
|
||||
int v4_wire_room(const v4_wire_queue *q, unsigned cells);
|
||||
|
||||
/* The message whose seven words are at `header7` and whose text, a cell to
|
||||
* four characters, is at `text` goes on the back. V4_WIRE_NOT_A_MESSAGE if
|
||||
* word 6 is not 0 to 1024, V4_WIRE_NO_ROOM if it does not fit. */
|
||||
int v4_wire_put(v4_wire_queue *q, const v4_cell *header7, const v4_cell *text);
|
||||
|
||||
/* The mark to the front message; to the one after the marked one, or
|
||||
* V4_WIRE_NO_MESSAGE if there is none (the mark is then at no message). */
|
||||
void v4_wire_first(v4_wire_queue *q);
|
||||
int v4_wire_next(v4_wire_queue *q);
|
||||
|
||||
/* The marked message: its seven words copied out and nothing taken;
|
||||
* copied out and taken off the queue, its text to `text`, which holds
|
||||
* `text_cap` cells (V4_WIRE_NOT_A_MESSAGE if that is too few); taken off and
|
||||
* gone. After a take or a drop the mark is at the message that followed.
|
||||
* V4_WIRE_NO_MESSAGE with the mark at no message. */
|
||||
int v4_wire_look(const v4_wire_queue *q, v4_cell *header7);
|
||||
int v4_wire_take(v4_wire_queue *q, v4_cell *header7, v4_cell *text, unsigned text_cap);
|
||||
int v4_wire_drop(v4_wire_queue *q);
|
||||
|
||||
/* The marked message of `from` goes to the back of `to`, with its fourth
|
||||
* word (word 3) one fewer: how many more nodes may pass it on. V4_WIRE_NO_ROOM
|
||||
* if `to` has no room for it, or is `from`. */
|
||||
int v4_wire_move(v4_wire_queue *from, v4_wire_queue *to);
|
||||
|
||||
#endif /* V4_WIRE_H */
|
||||
Reference in New Issue
Block a user