fix(v4.0.0): step 6e final review -- texts begun in order; a word no one will read lets its writer go; a cut wire wakes a wait for room; a full console wire is a refusal, not a fault

The final review of step 6e, by a fresh reviewer told to break it, and
what was ruled: fix them all, test first.  Each has a test that failed
before the mend.

- Two texts from one node could be begun in the wrong order when room
  for their answers came between the node's look at the first and at the
  second.  Texts whose answers go by the same port are begun in the order
  they came: (D-MASK), quit.v4.  102 of 700 timings were wrong before.
- A word written with PORT! to a node that speaks whole messages, or to
  a console's port, blocked its writer for ever and cut off every node
  beyond it.  It is error 18 now, whether that node is asleep, running
  or stuck: fabric.c, node.c.
- A node asleep for room on one wire is woken when another of its wires
  is cut.
- A line there was no room for on the lone node's wire was reported as a
  fault and ended the hosted program.  It is Message refused, and the
  prompt comes back: V4_BOOT_LINE_REFUSED.  hosted-check has the case;
  no bare-metal boot has typed it.
- Ruled: text behind held output for the same port is refused though it
  would fit.  MESH.md 7d.4 said otherwise and is corrected.
- The removal test also cuts and re-makes a wire, and has an exchange in
  which output is held at a full wire: 26,814 disturbances in all.
- MESH.md and v4/README.md no longer say that a stuck node holds up no
  other node, that only printing waits, or that no node is left waiting
  on one that is gone; 7d.6 lists the limits and what the products do
  that they did not before.

make -C v4 test and sanitize at both widths, hosted-check, lint.  Three
bare-metal boots: logs/20261013-194827 (amd64), -195131 (aarch64),
-195518 (riscv64).  POST 538 of 538, word_count=317,
dict_hash=0xe5a57f3eb72ff94a on all six; the three sessions are the same
character for character.  The boots were built with the working tree's
uncommitted change to kernel/Makefile that adds the ISA to the kernel's
version line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-13 20:02:33 -04:00
co-authored by Claude Opus 5.5
parent 06da5700bc
commit 66b26f823c
22 changed files with 1206 additions and 103 deletions
+2
View File
@@ -210,6 +210,8 @@ hosted-check: $(foreach i,$(HOSTED_ISAS),$(BINDIR)/boot-$(i).txt) $(BINDIR)/star
@test $$(grep -c '^ok> ERROR$$' $(BINDIR)/kport-amd64.txt) -eq 2 && grep -q '^ok> 3 ok$$' $(BINDIR)/kport-amd64.txt && grep -q '^ok> 0 ok$$' $(BINDIR)/kport-amd64.txt || { echo "hosted-check: with the way to the console set to the kernel's port, a line that prints did not end in an error at the prompt, or left something on the stack, or the node did not come back"; tail -6 $(BINDIR)/kport-amd64.txt; exit 1; }
@printf ': S0 S" 1 DROP" 0 0 SEND-ON ; S0\n1 2 + .\n65 1 BLOCK C! 1 BLOCK C@ .\n' | timeout 120 $(BINDIR)/starforth4-amd64 > $(BINDIR)/ksend-amd64.txt 2>&1; true
@grep -q '^ok> No one on that port$$' $(BINDIR)/ksend-amd64.txt && grep -q '^ok> 3 ok$$' $(BINDIR)/ksend-amd64.txt && grep -q '^ok> 65 ok$$' $(BINDIR)/ksend-amd64.txt || { echo "hosted-check: a message sent to the kernel's port was not refused with No one on that port, or the kernel's requests no longer work"; tail -6 $(BINDIR)/ksend-amd64.txt; exit 1; }
@{ printf '5 (CONSOLE) !\n7 AWAIT\n'; i=0; while [ $$i -lt 200 ]; do echo '1 DROP 1 DROP 1 DROP 1 DROP 1 DROP 1 DROP 1 DROP 1 DROP'; i=$$((i+1)); done; } | timeout 120 $(BINDIR)/starforth4-amd64 > $(BINDIR)/fullwire-amd64.txt 2>&1; test $$? -eq 0 || { echo "hosted-check: lines typed to a node that takes none, until its wire was full, ended the program"; tail -3 $(BINDIR)/fullwire-amd64.txt; exit 1; }
@grep -q '^ok> Message refused$$' $(BINDIR)/fullwire-amd64.txt && ! grep -q 'stopped on a fault' $(BINDIR)/fullwire-amd64.txt || { echo "hosted-check: a line there was no room for on the node's wire was not refused, and said so"; tail -3 $(BINDIR)/fullwire-amd64.txt; exit 1; }
@python3 $(HERE)/tools/depthsweep.py $(BINDIR)/starforth4-amd64 $$(( $(HOST_DATA_RING) + 2 )) $$(( $(HOST_RET_RING) + 1 )) || { echo "hosted-check: at some depth of a stack a line was not answered, or the node did not come back"; exit 1; }
@$(BINDIR)/starforth4-postfail < /dev/null > $(BINDIR)/postfail.txt; test $$? -ne 0 || { echo "hosted-check: a boot with a failing case did not fail"; exit 1; }
@grep -q '^POST FAIL: fail.wrong out<5 5 $$' $(BINDIR)/postfail.txt || { echo "hosted-check: the failing case was not named"; cat $(BINDIR)/postfail.txt; exit 1; }
+17 -14
View File
@@ -129,9 +129,9 @@ The two products are not yet this: they become the five nodes at steps 8
and 9.
A node she births is not POSTed: POST is the kernel's, once, on Hera.
**Refusals and waits** (`MESH.md` 7b, step 6c). A node is never left
waiting on a node that is gone, and a wait on a node that is alive and
stuck is ended by Hera's killing it, or from the console. A node with no
**Refusals and waits** (`MESH.md` 7b, step 6c). A wait on a node that
never answers -- stuck, or gone -- is ended by Hera's killing it, which
tells the others it is gone, or from the console. A node with no
room for a message, or no way to pass it on, or that would be the
sixteenth to pass it on, sends a NACK to whoever waits on it; `(REFUSED)`
counts them. `AWAIT` ends in an error if the answer is not going to come:
@@ -140,17 +140,20 @@ the console. `n KILL` on Hera removes node n and tells every other node it
is gone; a node believes that only from its centre.
**Messages on the wire** (`MESH.md` 7d, step 6e). A node that is stuck
holds up no other node. The fabric keeps a queue of whole messages each
way on every wire (`v4/src/wire.c`, 1,024 cells), and a node asks it,
through six addresses after its ports, to look at, take, move, drop or
put a whole message: each is one engine step, all of it or none. A node
holds nothing in transit. `SEND`, an answer, a NACK and a GONE go at once
or are refused at once; only what a text prints waits for room, so that
output is not lost. A node with nothing to do sleeps until a message
comes. A node removed at any step of an exchange leaves the others at
rest and answering (`v4/tests/test_host_mesh.c`). What it costs is in
`MESH.md` 7d.6: a burst beyond what a wire holds is refused, and text for
a node that is stuck fills its wire and is refused from then on.
no longer stops its neighbours passing on, taking text or being typed
to. The fabric keeps a queue of whole messages each way on every wire
(`v4/src/wire.c`, 1,024 cells), and a node asks it, through six addresses
after its ports, to look at, take, move, drop or put a whole message:
each is one engine step, all of it or none. A node holds nothing in
transit. `SEND`, a NACK and a GONE go at once or are refused at once.
What a text prints, and its word of how the text ended, wait for room, so
that output is not lost. A node with nothing to do sleeps until a message
comes. A node removed, or a wire cut, at any step of an exchange leaves
the others at rest and answering (`v4/tests/test_host_mesh.c`). What it
costs is in `MESH.md` 7d.6: a burst beyond what a wire holds is refused;
text for a node that is stuck fills its wire and is refused from then on;
and a text whose printing must go by a stuck node waits, and cannot be
typed to, until that node is killed.
**Blocks** (`MESH.md` step 6). The block words are the kernel's, as they
are for a v3 VM: `BLOCK`, `BUFFER`, `UPDATE`, `SAVE-BUFFERS` and
+15 -2
View File
@@ -105,6 +105,14 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
\ whose answers go different ways, and each is begun when
\ its own has the room. That room is kept from then until
\ the answer has gone.
\ TEXTS WHOSE ANSWERS GO THE SAME WAY ARE BEGUN IN THE ORDER
\ THEY CAME. Once one has been left for want of room, every
\ text behind it on this pass whose answer goes by that port
\ is left too, though the room has come since: (D-MASK) has
\ a bit for each such port. (As first built only the room
\ was asked, each time; room that came between the look at
\ one text and at the next began the second first. Found
\ by the final review of step 6e.)
\ a NACK is taken and counted in (REFUSED)
\ a GONE is taken, and believed if this node's centre sent it
\ an answer no one is waiting for is dropped: a SEND that is not waited
@@ -128,7 +136,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
\ for no one.
: (IDLE)
L: 1 (QUIET) a! ! \ nothing here is any text's doing: see (RAISED)
-1 (KEEP) a! ! 0 (AWAIT-FROM) a! ! -1 (DEFER) a! !
-1 (KEEP) a! ! 0 (AWAIT-FROM) a! ! -1 (DEFER) a! ! 0 (D-MASK) a! !
(SERVE) (MINE0)
M: (MINE) if REST
drop (IN)+2 a! @ -1 + if TEXT -3 + if NACK -1 + if GONE
@@ -145,10 +153,15 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
TEXT: drop
(IN)+1 a! @ (WAY) -if T1 drop (S-WIRE) a! @ \ the way back; with none known, the wire it came on
T1: (DONE-PORT) a! !
(DONE-PORT) a! @ (WBIT) (D-MASK) a! @ and if FREE1 jump FULL \ text ahead of it waits for room on that wire
FREE1: drop
8 (DONE-PORT) a! @ (ROOM?) if START -1 + if FULL
drop (S-WIRE) a! @ (DONE-PORT) a! ! \ no one is on that port: back by the wire it came on,
(S-WIRE) a! @ (WBIT) (D-MASK) a! @ and if FREE2 jump FULL
FREE2: drop
8 (S-WIRE) a! @ (ROOM?) -1 + if FULL jump START \ if that has the room -- or has no one on it either
FULL: drop (DEFER) a! @ -if LATER drop (DONE-PORT) a! @ (DEFER) a! ! (SKIP) jump M
FULL: drop (DONE-PORT) a! @ (WBIT) dup inv (D-MASK) a! @ and + !
(DEFER) a! @ -if LATER drop (DONE-PORT) a! @ (DEFER) a! ! (SKIP) jump M
LATER: drop (SKIP) jump M
REST: drop
(DEFER) a! @ -if ROOM drop (SLEEP) jump L
+3
View File
@@ -78,6 +78,9 @@ int v4_boot_run(const v4_boot *b);
#define V4_BOOT_LINE_STOPPED (-2) /* the node stopped on a fault, or never finished */
#define V4_BOOT_LINE_NO_INPUT (-3) /* the line read the keyboard and there is no more input */
#define V4_BOOT_LINE_WAITING (-4) /* the line is waiting for an answer (AWAIT) that no one here can send: see below */
#define V4_BOOT_LINE_REFUSED (-5) /* the node's wire has no room for the line: it was not put, and nothing was run. The node
* is taking nothing -- it waits for an answer and does not know these lines for its
* console's -- and its wire has filled (MESH.md 7d.6). It has not stopped. */
int v4_boot_line(const v4_boot *b, const char *text, unsigned len);
/* A LINE THAT WAITS (docs/v4.0.0/MESH.md 7b.6). V4_BOOT_LINE_WAITING means
+21 -6
View File
@@ -3,9 +3,10 @@
*
* 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, and then every write that has a
* reader waiting on the other end of its wire is handed over. There is no
* choice of whose turn it is.
* 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
@@ -50,6 +51,7 @@ typedef struct {
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;
@@ -97,6 +99,19 @@ int v4_fabric_device_take(v4_fabric *f, unsigned node, unsigned port, v4_cell *h
* 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);
@@ -149,9 +164,9 @@ void v4_fabric_wake(v4_fabric *f, unsigned id);
* 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; 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
* -- 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.
*
+5 -2
View File
@@ -125,6 +125,8 @@ typedef struct {
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 */
int spoke; /* non-zero: it has asked for a whole-message operation since it was born: it is a node that
* speaks whole messages, and not one that words are written to (fabric.h) */
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 */
@@ -217,8 +219,9 @@ void v4_node_console_attach(v4_node *n, v4_cell addr);
* 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
* The two before them let a node that writes a word at a time see whether a
* neighbour is waiting (MESH.md 7a), as the F18's io register does; a node that
* speaks whole messages has no use for them. They are what whoever connects the ports last
* said, v4_node_port_status; fetching them waits for nothing and changes
* nothing. Of the whole-message addresses How and Have are read only: a
* store to either is an address fault, as to "any port".
+18 -3
View File
@@ -9,6 +9,7 @@ static void clear_place(v4_place *p)
unsigned k;
p->node = 0;
p->asleep = 0;
p->cut = 0;
for (k = 0; k < V4_PORTS; k++) {
p->wire[k].kind = V4_WIRE_NONE; p->wire[k].node = 0; p->wire[k].port = 0; p->wire[k].device = 0;
p->wire[k].rx = 0; p->wire[k].tx = 0;
@@ -126,8 +127,10 @@ static void cut(v4_fabric *f, unsigned a, unsigned pa)
if (other->kind == V4_WIRE_NODE && other->node == a && other->port == pa) {
other->kind = V4_WIRE_NONE; other->device = 0;
other->rx = 0; other->tx = 0; /* the same two queues as w's */
f->place[w->node].cut = 1;
}
}
if (w->kind != V4_WIRE_NONE) f->place[a].cut = 1; /* a node asleep for room on another wire is woken (operation 9) */
if (f->queue) { give_queue(f, w->rx); give_queue(f, w->tx); }
w->kind = V4_WIRE_NONE;
w->device = 0;
@@ -226,6 +229,16 @@ static int awake(const v4_fabric *f, unsigned id)
return id < f->capacity && f->place[id].node && !f->place[id].asleep;
}
/* 1 if a word written to this end of a wire will not be read: the other end
* is a device that takes no words, or a node that speaks whole messages --
* one that has asked for a whole-message operation since it was born. */
static int no_reader(const v4_fabric *f, const v4_wire *w)
{
if (w->kind == V4_WIRE_DEVICE) return w->device->take == 0;
if (w->kind == V4_WIRE_NODE && w->node < f->capacity && f->place[w->node].node) return f->place[w->node].node->n.spoke != 0;
return 0;
}
/* What node `a` wrote to one of its ports, handed to whatever is there. */
static unsigned hand_over_write(v4_fabric *f, unsigned a)
{
@@ -386,7 +399,8 @@ static int operate(v4_fabric *f, unsigned id, v4_cell *how)
if (!tx) { *how = V4_WIRE_NO_ONE; break; }
if (a < 0 || a > (v4_cell)V4_WIRE_CELLS) { *how = V4_WIRE_NO_ROOM; break; } /* no waiting can make that room */
if (v4_wire_room(tx, (unsigned)a) == V4_WIRE_DONE) { *how = V4_WIRE_DONE; break; }
if (!arrived(f, id) && !freed(f, id)) return 0;
if (!arrived(f, id) && !freed(f, id) && !f->place[id].cut) return 0;
f->place[id].cut = 0;
for (cells = 0; cells < V4_PORTS; cells++) /* woken: the room that has come on any of its wires is seen */
if (f->place[id].wire[cells].tx) f->place[id].wire[cells].tx->freed = 0;
*how = V4_WIRE_NO_ROOM;
@@ -457,13 +471,14 @@ unsigned v4_fabric_step(v4_fabric *f)
for (i = 0; i < f->capacity; i++)
if (awake(f, i) && f->place[i].node->n.reading && !f->place[i].node->n.given) done += hand_over_read(f, i);
/* a node blocked at one port that nothing is wired to */
/* a node blocked at one port that nothing is wired to; or blocked writing a
* word to what will not read one (fabric.h, A WORD NO ONE WILL READ) */
if (f->gone_error != 0)
for (i = 0; i < f->capacity; i++) {
v4_node *n;
if (!awake(f, i)) continue;
n = &f->place[i].node->n;
if ((n->asking && f->place[i].wire[n->ask_port].kind == V4_WIRE_NONE)
if ((n->asking && (f->place[i].wire[n->ask_port].kind == V4_WIRE_NONE || no_reader(f, &f->place[i].wire[n->ask_port])))
|| (n->reading && !n->given && n->read_port != V4_PORT_ANY && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE))
done += (unsigned)v4_node_port_gone(n, f->gone_error);
}
+2 -1
View File
@@ -90,7 +90,7 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value)
if (port == (int)V4_PORT_WIRE) { n->wire = (value >= 0 && value < (v4_cell)V4_PORTS) ? (unsigned)value : V4_PORTS; return; }
if (port == (int)V4_PORT_A) { n->wire_a = value; return; }
if (port == (int)V4_PORT_B) { n->wire_b = value; return; }
if (port == (int)V4_PORT_DO) { n->do_op = value; n->doing = 1; return; }
if (port == (int)V4_PORT_DO) { n->do_op = value; n->doing = 1; n->spoke = 1; return; }
if (port >= 0 && port < (int)V4_PORTS) {
n->request = value;
n->ask_port = (unsigned)port;
@@ -193,6 +193,7 @@ void v4_node_port_attach(v4_node *n, v4_cell base)
n->wire_a = 0;
n->wire_b = 0;
n->doing = 0;
n->spoke = 0;
n->do_op = 0;
n->how = 0;
n->have = 0;
+1 -1
View File
@@ -225,7 +225,7 @@ static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep
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;
if (v4_wire_put(TO_NODE, out.word, out.word + V4_MSG_HEADER) != V4_WIRE_DONE) return V4_BOOT_LINE_REFUSED; /* no room: refused at once, as any message is */
}
for (;;) {
int awake = boot_step(b);
+3 -1
View File
@@ -90,6 +90,7 @@
#define S_ON (BUF0_W - 38) /* and whether its mark has been put at its first message */
#define KEEP (BUF0_W - 40) /* the port the answer to the text being done goes by, on whose wire 8 cells are kept for it; -1: none */
#define DEFER (BUF0_W - 41) /* a port whose wire had no room for the answer to text that waits to be begun; -1: none */
#define D_MASK (BUF0_W - 8) /* and a bit for each such port: text whose answer goes by one is not begun on this pass */
#define IN_HDR (BUF0_W - 56) /* the seven words of the message a wire's mark is at */
#define ONE (BUF0_W - 49) /* the one word of text of a NACK, a GONE or an answer, coming or going */
#define OUT_HDR (BUF0_W - 112) /* the seven words of a message this node is putting on a wire */
@@ -104,7 +105,7 @@
#define SEND_PTR (BUF0_W - 101) /* where the next word of a text being made ready to send goes */
#define OUT_TEXT (OUT_W - 64) /* what was printed, four characters to a cell, as it goes on the wire: 64 cells */
#define SEND_TEXT (OUT_W - 400) /* the text of a message SEND is putting, four characters to a cell: 256 cells */
/* FREE, plain memory: BUF0_W - 8; - 32 .. - 21; - 122 .. - 113; BUF0_W - 144 .. - 141 (what a flush makes zero after
/* FREE, plain memory: BUF0_W - 32 .. - 21; - 122 .. - 113; BUF0_W - 144 .. - 141 (what a flush makes zero after
* a full output buffer); and OUT_W - 144 .. OUT_W - 65. They and
* the cells above held the messages waiting, the refusals owed and what went with them (MESH.md 7a, 7b), which a
* node no longer keeps. */
@@ -227,6 +228,7 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
v4_text_constant(tx, "(S-ON)", S_ON);
v4_text_constant(tx, "(KEEP)", KEEP);
v4_text_constant(tx, "(DEFER)", DEFER);
v4_text_constant(tx, "(D-MASK)", D_MASK);
v4_text_constant(tx, "(IN)", IN_HDR);
v4_text_constant(tx, "(ONE)", ONE);
v4_text_constant(tx, "(OUT-HDR)", OUT_HDR);
+126
View File
@@ -350,6 +350,98 @@ int main(void)
"a node that can take an error is let go, and the error is raised on it: its P is its handler");
CHECK(!v4_node_port_gone(&ND(c)->n, 18), "a node that is not blocked at a port is not touched");
/* ---- A WORD WRITTEN TO WHAT WILL NOT READ ONE (found by the final review of step 6e) ----
* A node that writes a word to a port is blocked until it is taken. Two things will never take it: a
* device that takes no words -- one that speaks whole messages only -- and a node that is asleep at its
* wires, which executes nothing until a message comes. The writer is let go, as from a port with no one
* on it. A neighbour that is doing something else, or that the fabric has put to sleep, may yet read:
* the writer waits. */
{
static v4_wire_queue wq[4];
static const v4_device mute = { 0, 0, 0 }; /* it takes and gives no words */
static const v4_cell one[1] = { 5 };
static stream s;
static v4_device giver;
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, wq, 4);
a = loaded(0); /* writes a word to its port 0, and then counts in OUT */
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B);
LIT(OUT); O(BANG_A);
{
v4_cell again = v4_asm_label(&as);
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
v4_asm_branch(&as, V4_OP_JUMP, again);
}
CHECK(v4_asm_ok(&as) && v4_fabric_wire_device(&f, a, 0, &mute), "a writer, wired to a device that takes no words");
(void)settle(100);
CHECK(ND(a)->n.asking && ND(a)->n.mem[OUT] == 0, "it is blocked writing");
v4_fabric_gone_error(&f, 18);
(void)settle(100);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "a bare node, which cannot take an error, waits there");
v4_node_error_attach(&ND(a)->n, OUT + 20);
v4_node_fault_attach(&ND(a)->n, 900);
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18 && ND(a)->n.mem[OUT + 20] == 18,
"a node that can take one is let go: no one on that port");
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, wq, 4);
v4_fabric_gone_error(&f, 18);
a = loaded(0);
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B);
wait_for_ever();
b = loaded(1); /* reads a word from its port 5, and then sleeps at its wires */
LIT(PB + 5); O(BANG_B); O(FETCH_B); O(DROP);
LIT(P_DO); O(BANG_B); LIT(8); O(STORE_B);
wait_for_ever();
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a writer wired to a node that is waiting to read another port");
v4_node_error_attach(&ND(a)->n, OUT + 20);
v4_node_fault_attach(&ND(a)->n, 900);
(void)settle(200);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0 && ND(b)->n.reading, "the neighbour is not asleep at its wires and may yet read: the writer waits");
v4_fabric_sleep(&f, b);
memset(&s, 0, sizeof s); s.words = one; s.count = 1;
giver.give = stream_give; giver.take = 0; giver.self = &s;
CHECK(v4_fabric_wire_device(&f, b, 5, &giver), "the neighbour is put to sleep by the fabric, and a device with a word for it is wired to its port 5");
(void)settle(200);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0 && !v4_node_doing(&ND(b)->n), "a neighbour the fabric has put to sleep takes nothing and does nothing: the writer waits");
v4_fabric_wake(&f, b);
for (i = 0; i < 200 && ND(a)->n.asking; i++) (void)v4_fabric_step(&f);
CHECK(v4_node_doing(&ND(b)->n) && ND(b)->n.do_op == 8, "woken, the neighbour takes its word and sleeps at its wires");
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18, "and the writer is let go: no one on that port will read a word");
/* nor does it matter that such a node is awake and running: once a node has asked for a whole message
* it is not one that words are written to. A stuck node does not hold whoever writes it a word. */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, wq, 4);
v4_fabric_gone_error(&f, 18);
a = loaded(0);
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B);
wait_for_ever();
b = loaded(1); /* reads a word from its port 5, asks whether its wire 3 has room, and then counts for ever */
LIT(PB + 5); O(BANG_B); O(FETCH_B); O(DROP);
LIT(P_WIRE); O(BANG_B); LIT(3); O(STORE_B);
LIT(P_A); O(BANG_B); LIT(8); O(STORE_B);
LIT(P_DO); O(BANG_B); LIT(10); O(STORE_B);
LIT(OUT); O(BANG_A);
{
v4_cell again = v4_asm_label(&as);
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
v4_asm_branch(&as, V4_OP_JUMP, again);
}
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a writer wired to a node that has not yet asked anything of a wire");
v4_node_error_attach(&ND(a)->n, OUT + 20);
v4_node_fault_attach(&ND(a)->n, 900);
(void)settle(200);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "the writer waits");
memset(&s, 0, sizeof s); s.words = one; s.count = 1;
CHECK(v4_fabric_wire_device(&f, b, 5, &giver), "the neighbour is given its word");
for (i = 0; i < 200 && ND(a)->n.asking; i++) (void)v4_fabric_step(&f);
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18, "it asks for room, and the writer is let go");
for (i = 0; i < 20; i++) (void)v4_fabric_step(&f);
CHECK(!v4_node_doing(&ND(b)->n) && ND(b)->n.mem[OUT] > 0, "though that node is not asleep: it goes on counting");
}
/* ---- asleep, awake, and removed, while looping; and a node born while others run ---- */
v4_fabric_init(&f, places, 4);
a = loaded(0); /* counts in OUT for ever */
@@ -710,6 +802,40 @@ int main(void)
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1 && f.place[a].wire[0].tx->used + 200 > V4_WIRE_CELLS, "it wakes, and how it went says 1: no room, but a message");
/* A node asleep for room on one wire is woken when another of its wires is cut, or the node on it
* removed: it may be keeping something to put there, and must find out that it cannot (found by the
* final review of step 6e: before, it slept on until something else happened). */
v4_fabric_init(&f, more_places, 8);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, a, 1, c, 2), "a node with two wires");
fill(f.place[a].wire[0].tx, 0);
fill(f.place[a].wire[1].tx, 0);
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
(void)v4_fabric_step(&f);
CHECK(ask(a, 0, 100, 0, 9) == -99, "both full: asleep for room on wire 0");
CHECK(v4_fabric_unwire(&f, a, 1), "its wire 1 is cut");
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "it wakes, told 1: the wire it named has no room, but something has changed");
CHECK(ask(a, 1, 100, 0, 10) == 2, "it asks, and finds no one on port 1");
CHECK(ask(a, 0, 100, 0, 9) == -99, "asleep for room on wire 0 again");
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "and it is not woken twice for the one cut");
CHECK(v4_fabric_wire(&f, a, 1, c, 2), "the wire is made again");
fill(f.place[a].wire[1].tx, 0);
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "a wire being made does not wake it");
CHECK(v4_fabric_remove(&f, c) == &pool[2], "the node on wire 1 is removed");
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "it wakes, told 1");
ND(a)->n.doing = 0;
v4_node_store(&ND(a)->n, P_DO, 8);
CHECK(v4_fabric_wire(&f, a, 1, b, 4) && v4_fabric_unwire(&f, a, 1), "asleep for a message (8), with a wire made and cut");
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "a node asleep for a message is not woken by a cut: it keeps nothing");
/* SLEEP FOR ROOM IS WOKEN BY ROOM ON ANY OF THE NODE'S WIRES (MESH.md 7d.3): a node may be waiting to
* put on more than one, and names only one */
v4_fabric_init(&f, more_places, 8);
+149 -27
View File
@@ -262,35 +262,76 @@ static int answers(v4_cell node, unsigned id)
size_t len = strlen(got);
return len >= 2 && strcmp(got + len - 2, "3 ") == 0 && ended >= 1 && ended_from == node && at_rest(id);
}
/* The exchange that `text`, sent to `node`, begins is played from the mesh as kept, once for every step it
* takes, with the node at place `gone` removed at that step. Every node left must come to rest, blocked in
* nothing, and answer. With the middle node gone the far one is wired to the first, as whoever removed it
* would, so that it can be asked. How many steps the exchange takes; how many removals went wrong, and
* the first of them. */
/* AN EXCHANGE, DISTURBED AT EVERY STEP. The exchange that start() begins is played from the mesh as kept:
* once whole, to see how many steps it takes, and then once for every one of those steps with the mesh
* disturbed at that step --
* 1 node 11 removed, and node 12 then wired to node 10, as whoever removed it would, so that it can be asked;
* 2 node 12 removed;
* 3 the wire between nodes 11 and 12 cut and made again at once: what waited on it is let go.
* After each, every node left must come to rest, asleep and blocked at no port, and answer. With `moments`
* only the steps at which something the fabric keeps had changed are tried -- what is on a wire, or what a
* node asks of one: for an exchange too long to disturb at every step. A removal between two such steps
* finds the mesh as the one before it did.
* How many steps the exchange takes; how many disturbances were tried; how many went wrong, and the first. */
static v4_fabric_node now_pool[3];
static v4_place now_places[3];
static v4_wire_queue now_queues[QUEUES];
static v4_fabric now_f;
static unsigned removed_at_every_step(v4_cell node, const char *text, unsigned gone, unsigned *wrong, unsigned *first_wrong)
static v4_cell start_node;
static const char *start_text;
static int start_plain(void) { return put_text(start_node, start_text); }
static int hera_busy(void) { return pool[hera].n.mem[QUIET] == 0; }
/* Text for node 12 that prints, with node 10 made busy and the wire from node 11 to node 10 full but for 7
* cells: what node 12 prints, and how its text ended, are held by node 11, asleep for room (MESH.md 7d.4). */
static int start_held(void)
{
unsigned long before;
static v4_cell big[V4_WIRE_HEADER + V4_MSG_MAX_CHARS / 4u];
v4_wire_queue *q = places[mid].wire[2].tx;
unsigned k;
int ok = put_text(12, "A") && put_text(10, "SLOW") && run_until(hera_busy, 5000); /* node 10 has passed the one on and begun the other */
memset(big, 0, sizeof big);
big[0] = CONSOLE_ID; big[1] = 12; big[2] = V4_MSG_OUTPUT; big[3] = 16; big[6] = 1024;
for (k = 0; k < 3; k++) ok = ok && v4_wire_put(q, big, big + V4_WIRE_HEADER) == V4_WIRE_DONE;
big[6] = 884;
return ok && v4_wire_put(q, big, big + V4_WIRE_HEADER) == V4_WIRE_DONE && V4_WIRE_CELLS - q->used == 7;
}
/* what the fabric keeps, in a number: what every wire holds and what every node asks of one */
static unsigned long fabric_mark(void)
{
unsigned long h = 1469598103UL;
unsigned k;
for (k = 0; k < 3; k++) if (v4_fabric_node_at(&f, k)) h = (h ^ (unsigned long)(pool[k].n.doing * 16 + (int)pool[k].n.do_op)) * 16777619UL;
for (k = 0; k < QUEUES; k++) h = ((h ^ queues[k].used) * 16777619UL ^ queues[k].head) * 16777619UL;
return h;
}
static unsigned disturbed_at_every_step(int (*start)(void), unsigned how, int moments, unsigned *tried, unsigned *wrong, unsigned *first_wrong)
{
unsigned long before, mark, last = 0;
unsigned whole, s;
*wrong = 0; *first_wrong = 0;
*tried = 0; *wrong = 0; *first_wrong = 0;
put_back();
before = steps_run;
if (!put_text(node, text) || !run()) { *wrong = 1; return 0; }
saw_mid_hold = 0; watch = watch_mid_holds;
if (!start() || !run()) { watch = 0; *wrong = 1; return 0; }
watch = 0;
whole = (unsigned)(steps_run - before);
put_back();
if (!put_text(node, text)) { *wrong = 1; return 0; }
if (!start()) { *wrong = 1; return 0; }
for (s = 0; s <= whole; s++) {
int ok;
memcpy(now_pool, pool, sizeof pool); memcpy(now_places, places, sizeof places); memcpy(now_queues, queues, sizeof queues); now_f = f; /* the exchange, s steps on */
ok = v4_fabric_remove(&f, gone) == &pool[gone] && run();
ok = ok && at_rest(hera) && (gone == mid ? at_rest(far) : at_rest(mid));
if (ok && gone == mid) ok = v4_fabric_wire(&f, hera, 2, far, 2) && answers(12, far) && answers(10, hera);
else if (ok) ok = answers(11, mid) && answers(10, hera);
if (!ok) { if (!*wrong) *first_wrong = s; (*wrong)++; }
memcpy(pool, now_pool, sizeof pool); memcpy(places, now_places, sizeof places); memcpy(queues, now_queues, sizeof queues); f = now_f;
mark = fabric_mark();
if (!moments || s == 0 || mark != last) {
int ok;
memcpy(now_pool, pool, sizeof pool); memcpy(now_places, places, sizeof places); memcpy(now_queues, queues, sizeof queues); now_f = f; /* the exchange, s steps on */
if (how == 1) ok = v4_fabric_remove(&f, mid) == &pool[mid] && run() && at_rest(hera) && at_rest(far)
&& v4_fabric_wire(&f, hera, 2, far, 2) && answers(12, far) && answers(10, hera);
else if (how == 2) ok = v4_fabric_remove(&f, far) == &pool[far] && run() && at_rest(hera) && at_rest(mid) && answers(11, mid) && answers(10, hera);
else ok = v4_fabric_unwire(&f, mid, 3) && v4_fabric_wire(&f, mid, 3, far, 2) && run() && at_rest(hera) && at_rest(mid) && at_rest(far)
&& answers(12, far) && answers(11, mid) && answers(10, hera);
(*tried)++;
if (!ok) { if (!*wrong) *first_wrong = s; (*wrong)++; }
memcpy(pool, now_pool, sizeof pool); memcpy(places, now_places, sizeof places); memcpy(queues, now_queues, sizeof queues); f = now_f;
}
last = mark;
(void)v4_fabric_step(&f); (void)console_takes();
}
return whole;
@@ -995,32 +1036,113 @@ int main(void)
CHECK(between_data <= 4 && between_ret <= 7, "between texts: four cells of the data stack, and seven return entries");
/* ---- A NODE REMOVED AT EVERY STEP (MESH.md 7d.7, acceptance 6) ----
* Three exchanges, each played once whole and then once for every step it takes, with node 11 removed
* at that step, and again with node 12: the nodes left come to rest, blocked in nothing, and answer.
/* ---- A WORD WRITTEN TO A PORT, PORT!, HOLDS NO ONE (found by the final review of step 6e) ----
* A node's neighbours speak whole messages and are asleep at their wires; so is its console. A word
* written to one of them will never be read. The line that wrote it ends in an error and the node goes
* on; before, it was blocked for ever, and every node beyond it was cut off. */
CHECK(row(), "a row made afresh");
step_cap = 2000000;
CHECK(strstr(tell(10, "5 2 PORT! 65 EMIT"), "No one on that port") != NULL && strchr(printed, 'A') == NULL && ended == 1 && ended_how == V4_TEXT_ERROR && asleep(hera),
"node 10 writes a word to node 11's port: its line ends No one on that port: \"%s\"", printed);
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && strcmp(tell(11, "20 22 + ."), "42 ") == 0 && strcmp(tell(12, "7 8 * ."), "56 ") == 0, "and all three answer");
CHECK(strstr(tell(10, "5 1 PORT!"), "No one on that port") != NULL && ended_how == V4_TEXT_ERROR && strcmp(tell(10, "1 2 + ."), "3 ") == 0,
"the same for a word written to the console's port: \"%s\"", printed);
CHECK(strstr(tell(11, "5 3 PORT!"), "No one on that port") != NULL && ended_from == 11 && strcmp(tell(12, "7 8 * ."), "56 ") == 0 && strcmp(tell(11, "20 22 + ."), "42 ") == 0,
"and for node 11 writing to node 12's: node 12 is not cut off");
stuck = 1u << mid;
(void)tell(11, ": C BEGIN 0 UNTIL ; C");
CHECK(ended == 0 && !asleep(mid), "node 11 is stuck in a loop: it does not sleep");
CHECK(strstr(tell(10, "5 2 PORT!"), "No one on that port") != NULL && ended_how == V4_TEXT_ERROR && strcmp(tell(10, "1 2 + ."), "3 ") == 0,
"a word node 10 writes to it is refused all the same, and node 10 can be typed to: \"%s\"", printed);
stuck = 0;
step_cap = 20000000;
/* ---- TWO TEXTS FROM ONE NODE ARE BEGUN IN THE ORDER THEY CAME (acceptance 8; found by the final review) ----
* Node 12 has two texts from node 10 on its wire, and the wire their answers go by has 7 cells free:
* neither can be begun. Node 11, which was not taking, then takes a message off that wire -- at every
* step in turn, so that the room comes before node 12 looks at the first text, between its look at the
* first and at the second, and after both. Whenever it comes, the first is begun first. */
{
static v4_cell big[V4_WIRE_HEADER + V4_MSG_MAX_CHARS / 4u], hd[V4_WIRE_HEADER];
static v4_message m;
unsigned k, wrong = 0, first_wrong = 0, undone = 0;
v4_wire_queue *out, *in;
CHECK(row() && told(12, ": X1 65 EMIT ; : X2 66 EMIT ;"), "a row made afresh; node 12 has two words that print");
v4_fabric_sleep(&f, mid); /* node 11 takes nothing for now */
out = places[far].wire[2].tx; in = places[far].wire[2].rx;
memset(big, 0, sizeof big);
big[0] = 1; big[1] = 12; big[2] = V4_MSG_OUTPUT; big[3] = 16;
big[6] = 1024; for (i = 0; i < 3; i++) (void)v4_wire_put(out, big, big + V4_WIRE_HEADER);
big[6] = 884; (void)v4_wire_put(out, big, big + V4_WIRE_HEADER);
CHECK(V4_WIRE_CELLS - out->used == 7, "the wire from node 12 to node 11 has 7 cells free: %u", V4_WIRE_CELLS - out->used);
CHECK(v4_message_text(&m, 12, 10, V4_MSG_TEXT, "X1", 2) && (m.word[3] = 16, m.word[5] = 1, v4_wire_put(in, m.word, m.word + V4_MSG_HEADER) == V4_WIRE_DONE)
&& v4_message_text(&m, 12, 10, V4_MSG_TEXT, "X2", 2) && (m.word[3] = 16, m.word[5] = 2, v4_wire_put(in, m.word, m.word + V4_MSG_HEADER) == V4_WIRE_DONE),
"two texts from node 10 are on the wire to node 12, numbered 1 and 2");
keep();
for (k = 0; k < 700; k++) {
unsigned began = 0, seq[2] = { 0, 0 };
put_back();
for (i = 0; i < 100000 && began < 2; i++) {
const v4_node *n = &pool[far].n;
int kept = n->mem[KEEP] >= 0;
if (i == k) { v4_wire_first(out); (void)v4_wire_look(out, hd); (void)v4_wire_drop(out); }
(void)v4_fabric_step(&f); (void)console_takes();
if (!kept && n->mem[KEEP] >= 0) seq[began++] = (unsigned)n->mem[MSG + 5];
}
if (began < 2) undone++;
else if (seq[0] != 1 || seq[1] != 2) { if (!wrong) first_wrong = k; wrong++; }
}
CHECK(undone == 0 && wrong == 0, "whenever the room came, both texts were done, the first first: %u not done, %u in the wrong order, the first when it came at step %u", undone, wrong, first_wrong);
}
/* ---- A NODE REMOVED, OR A WIRE CUT, AT EVERY STEP (MESH.md 7d.7, acceptance 6) ----
* Three exchanges, each played once whole and then once for every step it takes with node 11 removed at
* that step, again with node 12 removed, and again with the wire between them cut and made again: the
* nodes left come to rest, blocked at nothing, and answer.
* What is typed is a word defined beforehand, and so is what a node left is asked (Q, which is
* `1 2 + .`): a line of several words is mostly the dictionary searched, thousands of steps in which the
* mesh does nothing, and every one of them would be a removal played to the end. */
* mesh does nothing, and every one of them would be a removal played to the end.
* WHAT "ANSWERS" MEANS HERE. A node that was waiting for an answer from a node now removed, or for one
* that was on a wire now cut, is still waiting when the mesh comes to rest: removing a node tells no one
* (Hera's KILL does: test_host_unit.c). Its wait is ended by the line it is then typed, "Interrupted",
* and it answers. */
{
static const struct { const char *what, *set10, *set11, *set12; v4_cell node; const char *text; } ex[3] = {
{ "text from the console for node 12, what it prints and how it ended", "1 DROP", "1 DROP", ": A 7 8 * . ;", 12, "A" },
{ "node 11 SENDs node 12 text and waits for its answer", "1 DROP", ": T S\" Q\" 12 SEND 12 AWAIT . ;", "1 DROP", 11, "T" },
{ "a message node 12 has no way for, and its NACK to node 10", "55 2 ROUTE : N S\" 1 DROP\" 55 SEND ;", "55 3 ROUTE", "NO-ROUTES 1 2 ROUTE 10 2 ROUTE 11 2 ROUTE", 10, "N" }
};
unsigned e, g, whole, wrong, first;
static const char *const how_name[4] = { "", "node 11 removed", "node 12 removed", "the wire between nodes 11 and 12 cut and made again" };
unsigned e, g, whole, tried, wrong, first, all = 0;
step_cap = 2000000;
for (e = 0; e < 3; e++) {
CHECK(row() && told(10, ex[e].set10) && told(11, ex[e].set11) && told(12, ex[e].set12) && told(10, ASK) && told(11, ASK) && told(12, ASK), "a row made afresh for: %s", ex[e].what);
step_cap = 2000000;
keep();
for (g = 1; g <= 2; g++) {
whole = removed_at_every_step(ex[e].node, ex[e].text, g == 1 ? mid : far, &wrong, &first);
CHECK(whole > 0 && wrong == 0, "%s, in %u steps: with node %u removed at each of them the rest come to rest and answer: %u did not, the first at step %u",
ex[e].what, whole, 10u + g, wrong, first);
start_node = ex[e].node; start_text = ex[e].text;
for (g = 1; g <= 3; g++) {
whole = disturbed_at_every_step(start_plain, g, 0, &tried, &wrong, &first);
all += tried;
CHECK(whole > 0 && wrong == 0, "%s, in %u steps: with %s at each of them the rest come to rest and answer: %u did not, the first at step %u",
ex[e].what, whole, how_name[g], wrong, first);
}
}
put_back();
CHECK(is_empty(tell(10, "N")) && pool[hera].n.mem[REFUSED] == 1 && pool[far].n.mem[LOST] == 1, "the last of them, whole: node 12 let the message go and node 10 was told it was refused");
/* and with what node 12 prints held at node 11, which sleeps for room: at every step at which
* anything on a wire, or what a node asks of one, has changed */
CHECK(row() && told(12, ": A 7 8 * . ;") && told(10, ": SLOW 3000 0 DO LOOP ;") && told(10, ASK) && told(11, ASK) && told(12, ASK), "a row made afresh for output held at a full wire");
step_cap = 2000000;
keep();
for (g = 1; g <= 3; g++) {
whole = disturbed_at_every_step(start_held, g, 1, &tried, &wrong, &first);
all += tried;
CHECK(whole > 0 && saw_mid_hold > 0, "text for node 12 that prints, node 10 busy and the wire to it full: node 11 held what was printed, asleep for room, for %u of %u steps", saw_mid_hold, whole);
CHECK(tried > 20 && wrong == 0, "with %s at each of the %u steps at which a wire or a node's asking changed, the rest come to rest and answer: %u did not, the first at step %u",
how_name[g], tried, wrong, first);
}
printf(" a node removed or a wire cut: %u times in all\n", all);
step_cap = 20000000;
}
+1
View File
@@ -94,6 +94,7 @@ int main(void)
if (leaving) { fflush(stdout); return 0; }
else if (how == V4_TEXT_ERROR) fputs(" ERROR\n", stdout);
else if (how == V4_BOOT_LINE_TOO_LONG) fputs("Line too long\n ERROR\n", stdout);
else if (how == V4_BOOT_LINE_REFUSED) fputs("Message refused\n ERROR\n", stdout); /* the node's wire is full: error 19's words */
else if (how == V4_BOOT_LINE_NO_INPUT) { putchar('\n'); return 0; }
else if (how == V4_BOOT_LINE_STOPPED) { fflush(stdout); fprintf(stderr, "starforth4: the node stopped on a fault\n"); return 1; }
}