revert(v4.0.0): step 6d, the wait, is backed out -- two reviews found it unsound
Ruled 2026-10-08. The engine, the nucleus and their tests are as they
were at dfabfa46, before step 6d; the code of the wait and its tests are
in history at 075385ab..2cf37aab. The limit of MESH.md 7b.7 stands: a
node that is stuck can hold up its neighbours until Hera kills it.
MESH.md 7c is kept as the record of the design as approved, and 7c.6 says
what was built, what the two reviews found, the cause the findings share
-- a node's message machinery runs on the stacks its text is using, and a
fault abandons whatever was in progress -- and what a second attempt
must settle before anything is built.
Kept from it, in hosted-check: a line that leaves 29 values on the stack,
and WORDS with 26 values on it. Both hung the products at some commit of
the wait.
make -C v4 test and sanitize at both widths, hosted-check, lint; three
bare-metal boots typing both. POST 538 of 538, word_count=317,
dict_hash=0xc0769523a47b7dc3 on all six: the hash of step 6c's code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
10f5ffd00b
commit
6015c87157
Binary file not shown.
+132
-181
@@ -104,10 +104,6 @@ word address `base`:
|
||||
| `base` … `base + V4_PORTS − 1` | Port 0 … port `V4_PORTS − 1` |
|
||||
| `base + V4_PORTS` | Any port: a read here takes from whichever port has a neighbour writing |
|
||||
| `base + V4_PORTS + 1` | Which port the last read from "any" came from. Read only. |
|
||||
| `base + V4_PORTS + 2`, `+ 3` | Who is waiting to write to this node, and who to read from it: section 7a |
|
||||
| `base + V4_PORTS + 4 + k` | The offer on port *k*: section 7c |
|
||||
| `base + 2 * V4_PORTS + 4` | The wait: section 7c |
|
||||
| `base + 2 * V4_PORTS + 5` | The wait for an offer only: section 7c |
|
||||
|
||||
- **A write to a port blocks the node until the neighbour has read it.**
|
||||
Built 2026-10-05 for one port; the opcode after the write runs when the
|
||||
@@ -244,9 +240,9 @@ and these tables. Nothing else changes.
|
||||
|
||||
## 7a. Two neighbours writing to each other (ruled 2026-10-06)
|
||||
|
||||
*(The lower-number rule and the looking again of this section are
|
||||
replaced by the wait, section 7c, ruled 2026-10-07. The messages waiting,
|
||||
and what flooding costs, stand.)*
|
||||
*(The wait of section 7c was to replace the lower-number rule and the
|
||||
looking again of this section. It was backed out on 2026-10-08; this
|
||||
section is what the code does.)*
|
||||
|
||||
The fault is in section 10, step 4. **Ruled: a node writes only to a
|
||||
neighbour that is already reading; a node keeps the messages it takes in
|
||||
@@ -295,10 +291,8 @@ part of wiring them. Put to Captain Bob after it was built, and ruled:
|
||||
|
||||
**What it costs.** A node that is flooded loses messages, of every kind:
|
||||
text for it, answers to text it sent, and messages it was only passing
|
||||
on. In the test three nodes send each other 800 messages at once; 278
|
||||
arrive (287 as first built, before step 6d) and the nodes count some
|
||||
1,400 let go (the count is of every kind of message, and since step 6d
|
||||
counts some more than once; it includes answers and text
|
||||
on. In the test three nodes send each other 800 messages at once; 287
|
||||
arrive and 714 messages are let go (the count includes answers and text
|
||||
that was to start a node sending). Six from each to each, at once, all
|
||||
arrive. Nothing here makes a sender slow down or send again; that is
|
||||
kernel-Hermes's work in v3 and is not decided for the mesh.
|
||||
@@ -310,8 +304,7 @@ kernel-Hermes's work in v3 and is not decided for the mesh.
|
||||
gone; and a wait on a node that is alive and stuck is ended by Hera's
|
||||
killing that node, or from the console. A message that is refused is
|
||||
told to whoever waits on it, while the node that refuses it has room to
|
||||
remember that it owes the telling.** (Since step 6d, section 7c, a node
|
||||
that is stuck also holds up no other node.) As first written the rule was "no
|
||||
remember that it owes the telling.** As first written the rule was "no
|
||||
message is lost without its sender being told, and no node waits for
|
||||
ever"; the review showed that was more than what was built could keep,
|
||||
and section 7b.7 says exactly where it falls short. It closes two defects
|
||||
@@ -378,12 +371,12 @@ Two types, beside text (1), output (2) and done (3):
|
||||
- A NACK owed to this node itself -- its own message came back to it and
|
||||
was let go here -- is put with its messages waiting, as if it had come
|
||||
by a port.
|
||||
- Paying one: it is offered on the port it goes by, with everything else
|
||||
the node has to send, and the node sleeps in the wait (section 7c). One
|
||||
there is no way for, or whose way is a port with nothing on it, is
|
||||
dropped and counted. (As first mended it was written without looking to
|
||||
a higher-numbered neighbour and kept back from a lower; step 6d
|
||||
replaced that.)
|
||||
- Paying one: if the neighbour it goes by is reading, it is sent. If not,
|
||||
and that neighbour's number is the higher, it is written all the same,
|
||||
as any message to it is (section 7a). If its number is the lower, the
|
||||
NACK stays owed; the node goes on taking in what is written to it and
|
||||
tries again, and does not sleep while it owes one. One there is no way
|
||||
for, or whose way is a port with nothing on it, is dropped and counted.
|
||||
- A message is passed on by at most 16 nodes. Its fourth word, the TTL,
|
||||
carries how many more may pass it on; the node that would be the
|
||||
sixteenth refuses it as it would one it had no way for. (Ruled
|
||||
@@ -429,11 +422,14 @@ Two types, beside text (1), output (2) and done (3):
|
||||
as usual.
|
||||
- On the two products there is one node, and nobody but the console could
|
||||
ever answer it: a line typed while it waits does the same.
|
||||
- **A blocked write (ruled 2026-10-07; replaced by section 7c the same
|
||||
day).** As first mended the fabric raised error 21 on a node blocked on
|
||||
the first word of a message while its console had a line for it. Since
|
||||
step 6d no node is blocked on a first word: it offers it and sleeps,
|
||||
and a console line reaches it as any message does (7c.3).
|
||||
- **A blocked write (ruled 2026-10-07).** Hera has the lowest number, so
|
||||
she writes to a neighbour without looking and is blocked until it reads
|
||||
(section 7a); if it never does, nothing could be typed to her again. So
|
||||
when a node is blocked writing the first word of a message to another
|
||||
node, and its console has a line for it, the fabric raises error 21 on
|
||||
it: it gives that message up and goes on to read the line. Only on the
|
||||
first word, so that half a message is never left on a wire
|
||||
(`v4_fabric_interrupt_error`, `v4/include/v4/fabric.h`).
|
||||
- **A node waiting to write to a neighbour that has been removed** is told:
|
||||
the word that says which neighbours are reading also says which ports
|
||||
have anything on them at all, and with nothing there the wait ends in
|
||||
@@ -447,14 +443,32 @@ Two types, beside text (1), output (2) and done (3):
|
||||
console. v3's pool is finite too, 32 messages, and a send beyond it is
|
||||
refused.
|
||||
- A node whose neighbour is asleep waits until it is woken (section 4.1).
|
||||
- ~~A node that is stuck can hold up its neighbours until it is killed.~~
|
||||
**Closed by step 6d (section 7c).** As step 6c left it, a node that
|
||||
owed a stuck neighbour a NACK, or had a message for it, was blocked or
|
||||
went round until Hera killed the stuck node; and a node waiting for an
|
||||
answer kept back what was passing through it.
|
||||
- **A node that is stuck can hold up its neighbours until it is killed.**
|
||||
A node cannot tell a neighbour that is busy for a moment from one that
|
||||
is stuck for good, and there is no clock (ruling 2). So:
|
||||
- a node that owes a NACK to a stuck neighbour whose number is the
|
||||
higher is blocked writing it;
|
||||
- a node that owes one to a stuck neighbour whose number is the lower
|
||||
goes on taking in what lower-numbered neighbours write to it, but does
|
||||
not sleep, and a higher-numbered neighbour with something for it
|
||||
waits;
|
||||
- a node with a message for a stuck neighbour waits to write it.
|
||||
|
||||
Each ends when Hera kills the stuck node: the wire is then empty, the
|
||||
waiting node is told so (error 18) and goes on. Hera herself can always
|
||||
be typed to (7b.6). None of this can happen on a node that is gone.
|
||||
- A node that is waiting for an answer keeps what comes for other nodes
|
||||
and passes it on when its wait ends, not before.
|
||||
- A node that was never told who is on its centre's port believes no GONE.
|
||||
|
||||
## 7c. The wait (ruled 2026-10-07)
|
||||
## 7c. The wait (ruled 2026-10-07; built, found unsound, and backed out 2026-10-08)
|
||||
|
||||
**This section is a record, not what the code does.** The wait was built
|
||||
(step 6d), two independent reviews in a row found it unsound, and Captain
|
||||
Bob ruled it backed out: the engine and nucleus are as they were before
|
||||
it, and section 7b.7's limit stands. What is below is the design as it
|
||||
was approved; 7c.6 says what happened to it and what a second attempt
|
||||
must deal with first.
|
||||
|
||||
**What it closes.** Section 7b.7's limit: a node with something for a
|
||||
neighbour that was not reading could only write anyway, and be blocked, or
|
||||
@@ -514,29 +528,13 @@ a node may offer a word on each of its ports at once.)*
|
||||
the fabric has its offer taken, and the other gets the word (case 2
|
||||
for the one, case 1 for the other).
|
||||
- Whichever it is, every offer the node had is withdrawn.
|
||||
- **A store to the wait withdraws every offer** and waits for nothing.
|
||||
*(Added after the review, 2026-10-08: a node that goes round without
|
||||
sleeping must not leave an old offer standing for its next wait to
|
||||
find.)*
|
||||
- **The wait for an offer only**, `base + 2 * V4_PORTS + 5`: as the wait,
|
||||
but a word that comes for the node does not end it, and a writer to the
|
||||
node is not served. It ends when the offer is taken, or when there
|
||||
turns out to be nothing on the port. *(Added after the review: it is
|
||||
how the words of a message after the first are written where no error
|
||||
must come.)*
|
||||
- A node in the wait that is handed a word by case 2 of another node gets
|
||||
it by its own case 1.
|
||||
- A node in the wait executes nothing and spends no heat. It shows to its
|
||||
neighbours as waiting to read.
|
||||
- With nothing on a port it has an offer on -- because nothing ever was,
|
||||
or because what was there has been removed -- a node that can take an
|
||||
error is woken: the fetch gives 0 and "which port" gives
|
||||
`2 * V4_PORTS + k`. No error is raised: the node may be in the middle of
|
||||
something that has nothing to do with that port. A bare node waits. A
|
||||
neighbour that is asleep takes nothing: the offer stands. *(Changed
|
||||
while it was built: as approved, error 18 was raised. That ended the
|
||||
text of a node that merely had a message queued for a neighbour that
|
||||
was then killed.)*
|
||||
- With nothing on a port it has an offer on, a node that can take an error
|
||||
has error 18 raised on it (section 4.1); a bare node waits. A neighbour that
|
||||
is asleep takes nothing: the offer stands.
|
||||
- `v4_fabric_interrupt_error`, a device's `pending`, and
|
||||
`v4_node_words_since_look` are removed.
|
||||
- The lone node of the two products (`v4/system/boot.c`) has the same
|
||||
@@ -545,74 +543,30 @@ a node may offer a word on each of its ports at once.)*
|
||||
|
||||
### 7c.3 The nucleus
|
||||
|
||||
*(Rewritten 2026-10-08 as built. The first form had the node passing a
|
||||
message on by beginning it at once, as its own text does. That held the
|
||||
node passing on whenever the node it was for was stuck: text typed for a
|
||||
stuck node left Hera deaf. So what is passed on waits, offered.)*
|
||||
|
||||
- *Beginning a message from text* (`(GATE)`): offer its first word; if a
|
||||
word comes instead, take that message in and offer again; when the
|
||||
offer is taken, write the rest, each word waiting for the neighbour,
|
||||
which has taken the first and reads to the end. With nothing on the
|
||||
port it is error 18.
|
||||
- *Text from its console for a node whose text is waiting there* ends that
|
||||
text in error 21, "Interrupted", and is then done. This is how Hera is
|
||||
typed to while her line waits on a stuck node.
|
||||
- *A node with nothing to do* (`(IDLE)`) deals first with what is for
|
||||
itself, the oldest first. With nothing for itself it offers, all at
|
||||
once, the first message it has for each port and each NACK it owes, and
|
||||
sleeps in the wait. What is taken is written and done with; the rest
|
||||
stays with the messages waiting, in the order it came. A message for a
|
||||
stuck node waits there and holds up nothing.
|
||||
- *What a node sends other than from text* goes the same way: what a
|
||||
finished text printed (type 2), how it ended (type 3), and a GONE. Each
|
||||
is put with the messages waiting and goes when it can; with no room for
|
||||
it, it is let go and counted. So a node that did text for a sender that then stuck
|
||||
is not held by it, and `KILL` is not held by one stuck node from telling
|
||||
the next.
|
||||
- *Order.* A node's own messages keep the order they were made in. A node
|
||||
does not begin another text from a sender while what it owes that
|
||||
sender from an earlier one -- what that text printed, how it ended -- is
|
||||
still with it: so what one sender is told comes in the order its texts
|
||||
were done. A GONE it is sending holds nothing back. It can note eight
|
||||
senders so; past that, nothing more is held.
|
||||
- *No error in the middle.* The words of a message after the first are
|
||||
written, when a message is passed on or a NACK paid, through the wait
|
||||
for an offer only. If the reader has been removed the rest is let go
|
||||
and counted; no error is raised, for an error there would leave the
|
||||
messages waiting in pieces. For the same reason a text may leave no
|
||||
more than 28 values on the stack: one that leaves more ends "Stack
|
||||
overflow". And a message half taken in when its writer is removed is
|
||||
let go, not kept.
|
||||
- *A node waiting for an answer* (`AWAIT`) does all of this but begin
|
||||
text: it looks through the messages waiting for what would end its
|
||||
wait, offers what is to be passed on and what it owes, and sleeps in
|
||||
the wait. It no longer keeps back what passes through it.
|
||||
- *Beginning a message* (`(GATE)`): offer its first word; if a word comes
|
||||
instead, take that message in and offer again; when the offer is taken,
|
||||
write the rest, each word waiting for the neighbour, which has taken the
|
||||
first and reads to the end.
|
||||
- *Paying what is owed* (`(PAY)`): every NACK owed is offered at once,
|
||||
each on the port it goes by (one to a port). A node with nothing to do
|
||||
that owes one sleeps in the wait; it no longer goes round, and it no
|
||||
longer writes one without looking.
|
||||
- *Text from its console for a node that is waiting to begin a message*
|
||||
ends what the node was doing in error 21, "Interrupted", and is then
|
||||
done, as in `AWAIT` (7b.6). This is how Hera is typed to while she waits
|
||||
on a stuck node.
|
||||
- `NEIGHBOUR` stays: a node must know who is on its centre's port to
|
||||
believe a GONE (7b.4). It is no longer needed to write.
|
||||
|
||||
### 7c.4 What is still a limit
|
||||
|
||||
- A node's own text that must begin a message (`SEND`, or what it prints)
|
||||
to a stuck node waits until Hera kills that node or, on Hera, a line is
|
||||
typed. It takes in everything sent to it meanwhile, but passes nothing
|
||||
on until its text ends.
|
||||
- A line typed at Hera while her text is waiting to begin a message ends
|
||||
that text, whether the node it waits on is stuck or only busy.
|
||||
- While what a node owes a sender -- or the console -- cannot go, because
|
||||
the way there is through a stuck node, it begins no more text from that
|
||||
sender. It goes on passing on, paying, and doing text from others, and
|
||||
when the stuck node is killed it lets go what could not go and does
|
||||
what it kept back, in order.
|
||||
- What text prints while it is still running, when the output buffer
|
||||
fills, is begun as a message from text is, and waits as that does.
|
||||
- An answer that reaches a waiting node whose messages waiting are full
|
||||
is refused, and the wait ends "Message refused".
|
||||
- A node's own line that must begin a message to a stuck node waits until
|
||||
Hera kills that node or, on Hera, a line is typed. It takes in
|
||||
everything sent to it meanwhile, and holds up no one.
|
||||
- A node in `AWAIT` keeps what comes for other nodes, and what it owes,
|
||||
until its wait ends (7b.7).
|
||||
- A message once begun is written to its end. A node that takes a first
|
||||
word and is then removed leaves its writer with error 18.
|
||||
- The messages waiting are finite, 400 cells: a node that is passing on
|
||||
for a stuck node keeps what is for it until that node is killed, and
|
||||
with no room left refuses what comes, telling the sender.
|
||||
|
||||
### 7c.5 Acceptance
|
||||
|
||||
@@ -630,13 +584,68 @@ stuck node left Hera deaf. So what is passed on waits, offered.)*
|
||||
5. *Hera.* Waiting to begin a message to a stuck node, she is typed to:
|
||||
the line that waited ends "Interrupted", and the new line, which kills
|
||||
the stuck node, is done.
|
||||
6. *An empty port.* Text that begins a message on a port with nothing on
|
||||
it ends in error 18.
|
||||
6. *An empty port.* An offer to a port with nothing on it is error 18.
|
||||
7. *Nothing deadlocks.* The flood of section 7a's test, 800 messages
|
||||
among three nodes, comes to rest.
|
||||
8. *Nothing else changes.* POST 538 of 538, the existing tests, and one
|
||||
hash on all six systems; `sanitize`, `hosted-check`, lint, three boots.
|
||||
|
||||
### 7c.6 What happened (2026-10-08)
|
||||
|
||||
Built in commits `075385ab` to `2cf37aab`; the code is in history there,
|
||||
with its tests (`v4/tests/test_host_mesh.c` at `2cf37aab` has a scene for
|
||||
each defect below). Backed out by restoring the engine, nucleus and tests
|
||||
to `dfabfa46`.
|
||||
|
||||
**What it grew into.** The approved design had a node pass a message on
|
||||
by beginning it at once. That held a node passing on for a stuck node,
|
||||
so what is passed on, how text ended, what a finished text printed, and
|
||||
a GONE all came to wait with the messages waiting, offered; `AWAIT` came
|
||||
to pass on and pay while it waited; an offer on an empty port came to
|
||||
wake the node instead of raising an error; and a second wait, for an
|
||||
offer only, was added so that the words after a message's first could be
|
||||
written without an error.
|
||||
|
||||
**What the two reviews found**, after each of which it was mended and
|
||||
still not sound:
|
||||
- Passes over the messages waiting were left in pieces by any fault or
|
||||
error in the middle of one: a stack overflow, or a neighbour removed.
|
||||
A node then went round for ever.
|
||||
- Offers left standing sent a text's message to the wrong port.
|
||||
- What a finished text printed held the node when the way to the console
|
||||
was through a stuck node.
|
||||
- A node with many messages of its own waiting began no text.
|
||||
- On the products, a line leaving 29 values on the stack hung the node;
|
||||
and after that was mended, `WORDS` with 26 values on the stack did.
|
||||
- A stack fault after a neighbour had taken a message's first word left
|
||||
the two out of step for good, and hung the neighbour.
|
||||
- `AWAIT` on a deep stack, a refusal with 28 values waiting, output out
|
||||
of order, and a neighbour removed in mid-message ending an unrelated
|
||||
wait.
|
||||
|
||||
**The cause they share.** A node's message machinery runs on the node's
|
||||
own two stacks, the ones its text is using, and a fault abandons
|
||||
whatever was in progress. The wait put more of that machinery, needing
|
||||
more of the stack, into more places: between texts, inside `AWAIT`, and
|
||||
wherever text prints. The tests ran those paths on a shallow stack with
|
||||
every node present.
|
||||
|
||||
**What a second attempt must settle before anything is built.**
|
||||
1. What a node does when a fault or an error comes in the middle of
|
||||
writing or reading a message, on both ends of the wire, at every word.
|
||||
2. How much of each stack the machinery may use, and where that is
|
||||
checked, so that text which leaves too little ends in an error before
|
||||
anything is on a wire.
|
||||
3. Tests that run every message path at every depth of both stacks, and
|
||||
with a node removed at every stage of a message, on writer's and
|
||||
reader's side; and the products compared with the commit before on the
|
||||
same input.
|
||||
4. Whether to keep the messages a node passes on in the node at all, or
|
||||
to do as v3 does: the kernel queues, and a send never blocks.
|
||||
|
||||
Two guards from this are kept in `hosted-check`: a line that leaves 29
|
||||
values on the stack, and `WORDS` with 26 values on it.
|
||||
|
||||
## 8. Storage
|
||||
|
||||
**Ruled 2026-10-06 and 2026-10-07** (Captain Bob). On 2026-10-07 he found
|
||||
@@ -1249,75 +1258,17 @@ Each is tested, committed and pushed before the next.
|
||||
8. *Nothing else changes.* POST 538 of 538, the existing tests, and the
|
||||
same hashes on all six systems.
|
||||
|
||||
**6d. The wait (section 7c).** Ruled 2026-10-07. **Built 2026-10-08.**
|
||||
- *The engine* (`v4/src/node.c`, `fabric.c`): the offer addresses and
|
||||
the wait; the fabric hands an offer over in one step; a node told
|
||||
there is nothing on a port it offered on. Removed: the console
|
||||
release of 7b.6, a device's `pending`, the count of words since a
|
||||
look.
|
||||
- *The nucleus* (`v4/capsule/core.v4`, `quit.v4`): `(OFFERS)`, `(GATE)`;
|
||||
`(IDLE)` as in 7c.3, with `(MINE?)`, `(FWD-SET)`, `(FWD-GO)`,
|
||||
`(PAY-SET)`, `(PAY-GO)`; `(FINISH)` and `GONE` put what they send with
|
||||
the messages waiting; `AWAIT` goes on passing on and paying. The
|
||||
lower-number rule, the looking again, `(PASS-ON)` and `(PAY)` are
|
||||
gone. The ports moved in the nucleus's map to make room.
|
||||
- *The lone node* (`v4/system/boot.c`): an offer is served as a write.
|
||||
- *Found while it was built, and mended, each with a check that failed
|
||||
first:* a message passed on toward a stuck node held the node passing
|
||||
it on (7c.3); a node waited on a stuck sender to give it its answer;
|
||||
with answers queued, a line typed during a wait was done before the
|
||||
earlier line's answer had gone; a node with two answers it could not
|
||||
give began no text; `KILL` waited to tell a stuck node, and a typed
|
||||
line then left the rest untold; removing a node raised "No one on
|
||||
that port" on the unrelated text of a neighbour with something
|
||||
queued for it (7c.2). Two commits on the way were wrong as pushed:
|
||||
`482cc75f` (the first of these) and `1c720ab2` (pushed with
|
||||
`hosted-check` failing); `35378372` and `ec817a82` mend them.
|
||||
- *Tests.* `test_fabric.c` 104 checks: acceptance 1, 2, 3, 6, and the
|
||||
ring, the same-step and the asleep cases. `test_host_mesh.c` 127:
|
||||
acceptance 4 for a creditor of either number, two refusals owed,
|
||||
order kept through a node passing on, a waiting node passing on, a
|
||||
line that waits and is typed to, and 7. `test_host_unit.c` 84:
|
||||
acceptance 5; a node with answers for two stuck senders; `KILL` with
|
||||
another node stuck.
|
||||
- *Verified.* `make -C v4 test` and `sanitize` at both widths,
|
||||
`hosted-check`, lint; three bare-metal boots, `logs/20261008-011558`
|
||||
(amd64), `-011833` (aarch64), `-012159` (riscv64). On all six: POST
|
||||
538 of 538, `word_count=317`, `dict_hash=0x6a39c0bb9d183418`.
|
||||
- **The review (2026-10-08) found the step not sound as built**, and
|
||||
Captain Bob ruled the mends (A, B, C, D and the guard: 7c.2, 7c.3).
|
||||
Each has a scene in `test_host_mesh.c`, on a row of three begun
|
||||
again, that failed first:
|
||||
- *A line that left 29 values on the stack hung the node for ever*,
|
||||
on the products too. It ends "Stack overflow"
|
||||
(`test_host_quit.c`, `hosted-check`, and typed in the boots).
|
||||
- *A node removed while a message was being passed on to it* left the
|
||||
node passing it on going round for ever. The rest is let go.
|
||||
- *Offers left standing:* text meant for one node was done by
|
||||
another. A store to the wait withdraws them.
|
||||
- *What a finished text printed* held the whole node when the way to
|
||||
the console was through a stuck node. It is queued.
|
||||
- *More than eight of a node's own messages waiting* locked out all
|
||||
text, Hera's `KILL` included. Only what a sender is owed holds that
|
||||
sender's text back.
|
||||
- *A message half taken in when its writer was removed* was kept as
|
||||
if whole. It is let go.
|
||||
- Smaller: no value is left on the stack when a port goes in
|
||||
mid-refusal; a refused line no longer ends a waiting text
|
||||
"Interrupted"; a node's own messages may be passed on by 16 nodes.
|
||||
- *Not mended, small:* with no console told of, a second text from
|
||||
whoever sent the one being done interrupts it, as since step 6c;
|
||||
refusals to one node go newest first; `(LOST)` counts some messages
|
||||
more than once, so the flood's check is "it comes to rest", not an
|
||||
exact count; the ring of three is tested in the engine only; the
|
||||
five-node test is not run at 32 bits.
|
||||
- *Verified, on the code as mended.* `test_fabric.c` 112 checks,
|
||||
`test_host_mesh.c` 154, `test_host_unit.c` 84. `make -C v4 test` and
|
||||
`sanitize` at both widths, `hosted-check`, lint; three bare-metal
|
||||
boots, `logs/20261008-063450` (amd64), `-063738` (aarch64),
|
||||
`-064120` (riscv64). On all six: POST 538 of 538, `word_count=317`,
|
||||
`dict_hash=0xd41a6ac9448fff60`. This code has not been reviewed
|
||||
again.
|
||||
**6d. The wait (section 7c).** Ruled 2026-10-07. Built 2026-10-08,
|
||||
found unsound by two reviews, and **backed out the same day** (ruled):
|
||||
section 7c.6. The code is as it was at step 6c; the limit of 7b.7
|
||||
stands. Verified after the backing out: `make -C v4 test` and
|
||||
`sanitize` at both widths, `hosted-check` with its two new cases,
|
||||
lint; three bare-metal boots, `logs/20261008-072104` (amd64),
|
||||
`-072354` (aarch64), `-072743` (riscv64), each typing 29 values on a
|
||||
line and `WORDS` with 26 on the stack; POST 538 of 538,
|
||||
`word_count=317`, `dict_hash=0xc0769523a47b7dc3` on all six, which is
|
||||
the hash of step 6c's mended code. The boots of `logs/20261008-01*`
|
||||
and `-06*` are of the wait as built and are kept as its record.
|
||||
|
||||
7. **A second unit; scaling while running; sleep, wake and kill by
|
||||
command.**
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
[2J[001;001H[=3h[2J[001;001H[2J[001;001H[8;042;160t[2J[001;001H[2J[001;001H[8;056;240t[2J[001;001HBdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
|
||||
BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
|
||||
[2J[001;001H[=3h[2J[001;001HStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
RAW SERIAL UP
|
||||
[CKPT 002] Serial (COM1) initialized
|
||||
Monolithic build - kernel linked directly
|
||||
Collecting boot information...
|
||||
CmdLine: parsed OK
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
GOP: linear framebuffer found
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
EBS...
|
||||
EBS OK
|
||||
Calling kernel_main (monolithic)...
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: amd64
|
||||
Build: Oct 8 2026 07:20:37
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 135
|
||||
Total memory: 1023 MB
|
||||
Usable memory: 966 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 248743
|
||||
Free pages : 247220
|
||||
Used pages : 1523
|
||||
Total MB : 971
|
||||
Free MB : 965
|
||||
Used MB : 5
|
||||
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
VMM self-test complete.
|
||||
|
||||
IDT installed.
|
||||
|
||||
APIC: init...
|
||||
APIC: IA32_APIC_BASE MSR=0x00000000fee00900
|
||||
APIC: stale-ISR drain: 0 EOI(s) issued
|
||||
APIC: initialized (xAPIC MMIO, TPR=0, SIVR=0x1FF, EOI-clear)
|
||||
APIC: init done
|
||||
|
||||
I/O APIC: init...
|
||||
I/O APIC: base=0xfec00000, gsi_base=0, overrides=5
|
||||
override: source=00 gsi=2 flags=0x0000
|
||||
override: source=05 gsi=5 flags=0x000d
|
||||
override: source=09 gsi=9 flags=0x000d
|
||||
override: source=0a gsi=10 flags=0x000d
|
||||
override: source=0b gsi=11 flags=0x000d
|
||||
i8042: keyboard ACKed enable-scanning
|
||||
i8042: IRQ1 enabled in controller config byte
|
||||
I/O APIC: keyboard IRQ1 routed (masked)
|
||||
|
||||
Timer: init...
|
||||
Timer: init start
|
||||
Timer: PM_TMR_BLK discovered from FADT at port 1544
|
||||
Timer: VM mode detected (hypervisor present).
|
||||
Timer: HPET calibration disabled (VM-exit MMIO would poison timing).
|
||||
Timer: WARNING: invariant TSC not present under hypervisor.
|
||||
Timer: continuing in RELATIVE mode (no determinism guarantees).
|
||||
Timer: RDTSCP not present; using RDTSC (less serialized).
|
||||
Timer: CPUID frequency unavailable; trying PM Timer...
|
||||
Timer: trust=1 (0=NONE,1=REL,2=ABS), TSC=2096654913 Hz
|
||||
Timer: init done
|
||||
|
||||
Kernel heap initialized.
|
||||
Heap statistics:
|
||||
Total bytes: 536870872
|
||||
Free bytes: 536870872
|
||||
Used bytes: 0
|
||||
Peak bytes: 0
|
||||
Heap base addr: 24641536
|
||||
Heap end addr: 561512448
|
||||
|
||||
Heartbeat: init...
|
||||
APIC Timer: calibrating...
|
||||
APIC Timer: apic_hz=1007656006, tick_hz=100, initial_count=10076560
|
||||
APIC Timer: configured (masked, ready to start)
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x5ff3672509f6c4e5 capsule_hash=0x5ff3672509f6c4e5 words=311
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xc07eb35ca84c4aa8
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_SYSTEM word_count=317 dict_hash=0xc0769523a47b7dc3
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
|
||||
PCI: init...
|
||||
PCI: ECAM mapped (amd64)
|
||||
virtio-blk: found device
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 79653714] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
|
||||
Artemis: virtio-blk attached
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
ok> T{
|
||||
UNKNOWN WORD: 'T{'
|
||||
ERROR
|
||||
ok> RS1
|
||||
UNKNOWN WORD: 'RS1'
|
||||
ERROR
|
||||
ok> COLD
|
||||
FORTH-79 Cold Start
|
||||
System initialized.
|
||||
ok
|
||||
ok> 3 4 U* . .
|
||||
0 12 ok
|
||||
ok> FORGET U*
|
||||
Protected word
|
||||
ERROR
|
||||
ok> 9 KERNEL-WORD ASK9 ASK9
|
||||
Argument out of range
|
||||
ERROR
|
||||
ok> 1 BLOCK C@ . 2047 BLOCK C@ .
|
||||
0 0 ok
|
||||
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
|
||||
65 ok
|
||||
ok> 3072 BLOCK C@ .
|
||||
0 ok
|
||||
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
|
||||
Storage refused
|
||||
ERROR
|
||||
ok> 9999999 BLOCK
|
||||
Block out of range
|
||||
ERROR
|
||||
ok> 5 7 PORT!
|
||||
No one on that port
|
||||
ERROR
|
||||
ok> 1 2 + .
|
||||
3 ok
|
||||
ok> 5 AWAIT
|
||||
7 8 * .
|
||||
Interrupted
|
||||
ERROR
|
||||
ok> 56 ok
|
||||
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
||||
Stack overflow
|
||||
ERROR
|
||||
ok> 3 4 + .
|
||||
7 ok
|
||||
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
||||
ok
|
||||
ok> WORDS
|
||||
ASK9 U/MOD U* (UD) (NB) (BITS) BYE ACL-WORD-ID ACL-HEAT@ ACL-INIT-PRIMITIVES
|
||||
ACL-INHERIT ACL-ALLOW! ACL-TTL! ACL-MODE! ACL-PIN ACL-PINNED? ACL-ALLOW@
|
||||
ACL-TTL@ ACL-MODE@ ACL-HOOK LOG-DEBUG" LOG-TEST" LOG-INFO" LOG-WARN"
|
||||
LOG-ERROR" (LOG") LOG-DEBUG-STR LOG-TEST-STR LOG-INFFO-STR LOG-WARN-STR
|
||||
LOG-ERROR-STR LOG-LEVEL! LOG-LEVEL@ LOG-DEBUG LOG-TEST LOG-INFO
|
||||
LOG-WARN LOG-ERROR THRU --> LOAD LIST EMPTY-BUFFERS FLUSH SAVE-BUFFERS
|
||||
UPDATE BLOCK BUFFER BLK SCR Q.PRINT Q.COS Q.SIN Q.LOG Q.SQRT Q.EXP
|
||||
Q./ Q.* Q.MIN Q.MAX Q.0= Q.> Q.< Q.= Q.NEG Q.ABS QQ.- Q.+ Q.SCALE
|
||||
Q.0 Q.1 Q.TO-INT Q.FROM-INT DEFER@ IS DEFER COLD WARM PAGE VERSION
|
||||
79-STANDARD FORGET VLIST WORDS ORDER VOCABULARY DEFINITIONS FORTH
|
||||
CURRENT CONTEXT FENCE SKIP SCAN SEARCH COMPARE -TRAILING BLANK ERASE
|
||||
FILL MOVE CMOVE> ?TERMINAL DMIN DMAX D< M- M+ D2/ D2* D== D0< D0=
|
||||
DABS D- RSHIFT LSHIFT WITHIN MIN MAX ABS U> U< >= <= <> 0> 0<> -!
|
||||
2! 2@ INVERT FALSE TRUE NOP 2R@ 2R> 2>R 2ROT 2OVER 2SWAP DUMP .S
|
||||
? D. D.R U. U.R . .R OCTAL HEX DECIMAL SPACES #> #S # SIGN HOLD
|
||||
<# STATE PAD BASE SPAN >IN TIB BL > < = NOT 0< 0= OR XOR AND M/MODD
|
||||
*/ */MOD MOD / /MOD M* S>D 2/ 2* 2- 2+ 1- 1+ NEGATE * - + CELLS
|
||||
+! ! @ UNLOOP LEAVE J I R@ R> >R ROLL PICK DEPTH 2DROP 2DUP ?DUP
|
||||
-ROT ROT SWAP OVER DROP DUP S" (S") ABORT" (ABORT") ." (.") ABORT
|
||||
QUIT (WORD-FORGOTTEN) (WORD-DEFINED) (REFUSED) (LOST) (CONSOLE)
|
||||
(ME) NODE--ERROR (SEAL) PORT! AWAIT SEND-ON SEND NO-ROUTES GONE NO-ROUTE
|
||||
DEFAULT-ROUTE NEIGHBOUR ROUTE +LOOP LOOP ?DO DO ENDCASE ENDOF OF
|
||||
CASE REPEAT WHILE AGAIN UNTIL BEGIN THEN ELSE IF DOES> CONSTANT
|
||||
VARIABLE CREATE [COMPILE] COMPILE IMMEDIATE EXIT KERNEL-WORD ; :
|
||||
[LITERAL] ['] '' LITERAL ] [ INTERPRET EXECUTE FIND HIDDEN SMUDGE
|
||||
TRAVERSE >BODY PFA CFA NAME> NFA >NAME LINK> LFA >LINK LATEST 2,
|
||||
C, , ALLOT ALIGN HERE \ ( NUMBER CONVERT ENCLOSE WORD QUERY EXPECT
|
||||
SOURCE TYPE COUNT SPACE CR KEY EMIT CMOVE C! C@ DNEGATE D+ UM*
|
||||
ok
|
||||
ok> DEPTH .
|
||||
26 ok
|
||||
ok> HERE .
|
||||
8300 ok
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,177 @@
|
||||
UEFI firmware (version 2025.11-3ubuntu7.3 built at 15:40:26 on Sep 23 2026)
|
||||
[2J[001;001H[=3h[2J[001;001H[2J[001;001H[8;031;100t[2J[001;001HBdsDxe: failed to load Boot0002 "UEFI Misc Device" from PciRoot(0x0)/Pci(0x2,0x0): Not Found
|
||||
BdsDxe: failed to load Boot0003 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:04.0-1" from PciRoot(0x0)/Pci(0x4,0x0)/USB(0x0,0x0): Not Found
|
||||
BdsDxe: loading Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
|
||||
BdsDxe: starting Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
|
||||
[2J[001;001H[=3h[2J[001;001HStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
Collecting boot information...
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: aarch64
|
||||
Build: Oct 8 2026 07:23:27
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 108
|
||||
Total memory: 4093 MB
|
||||
Usable memory: 4054 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 1039354
|
||||
Free pages : 1037943
|
||||
Used pages : 1411
|
||||
Total MB : 4059
|
||||
Free MB : 4054
|
||||
Used MB : 5
|
||||
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
VMM self-test complete.
|
||||
|
||||
AArch64: running at EL1
|
||||
IDT installed.
|
||||
|
||||
APIC: init...
|
||||
PSCI: no DTB -- using HVC (QEMU virt-machine default)
|
||||
GICv2: no DTB GIC node -- using QEMU virt-machine defaults
|
||||
GICv2: distributor+CPU interface enabled, PPI 30
|
||||
APIC: init done
|
||||
|
||||
Timer: init...
|
||||
Timer: AArch64 generic timer initialised.
|
||||
Timer: init done
|
||||
|
||||
Kernel heap initialized.
|
||||
Heap statistics:
|
||||
Total bytes: 2147483608
|
||||
Free bytes: 2147483608
|
||||
Used bytes: 0
|
||||
Peak bytes: 0
|
||||
Heap base addr: 1207959552
|
||||
Heap end addr: 3355443200
|
||||
|
||||
Heartbeat: init...
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x5ff3672509f6c4e5 capsule_hash=0x5ff3672509f6c4e5 words=311
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xc07eb35ca84c4aa8
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_SYSTEM word_count=317 dict_hash=0xc0769523a47b7dc3
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
|
||||
PCI: init...
|
||||
virtio-blk: found device
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 1411419] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
|
||||
Artemis: virtio-blk attached
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
ok> T{
|
||||
UNKNOWN WORD: 'T{'
|
||||
ERROR
|
||||
ok> RS1
|
||||
UNKNOWN WORD: 'RS1'
|
||||
ERROR
|
||||
ok> COLD
|
||||
FORTH-79 Cold Start
|
||||
System initialized.
|
||||
ok
|
||||
ok> 3 4 U* . .
|
||||
0 12 ok
|
||||
ok> FORGET U*
|
||||
Protected word
|
||||
ERROR
|
||||
ok> 9 KERNEL-WORD ASK9 ASK9
|
||||
Argument out of range
|
||||
ERROR
|
||||
ok> 1 BLOCK C@ . 2047 BLOCK C@ .
|
||||
0 0 ok
|
||||
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
|
||||
65 ok
|
||||
ok> 3072 BLOCK C@ .
|
||||
0 ok
|
||||
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
|
||||
Storage refused
|
||||
ERROR
|
||||
ok> 9999999 BLOCK
|
||||
Block out of range
|
||||
ERROR
|
||||
ok> 5 7 PORT!
|
||||
No one on that port
|
||||
ERROR
|
||||
ok> 1 2 + .
|
||||
3 ok
|
||||
ok> 5 AWAIT
|
||||
7 8 * .
|
||||
Interrupted
|
||||
ERROR
|
||||
ok> 56 ok
|
||||
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
||||
Stack overflow
|
||||
ERROR
|
||||
ok> 3 4 + .
|
||||
7 ok
|
||||
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
||||
ok
|
||||
ok> WORDS
|
||||
ASK9 U/MOD U* (UD) (NB) (BITS) BYE ACL-WORD-ID ACL-HEAT@ ACL-INIT-PRIMITIVES
|
||||
ACL-INHERIT ACL-ALLOW! ACL-TTL! ACL-MODE! ACL-PIN ACL-PINNED? ACL-ALLOW@
|
||||
ACL-TTL@ ACL-MODE@ ACL-HOOK LOG-DEBUG" LOG-TEST" LOG-INFO" LOG-WARN"
|
||||
LOG-ERROR" (LOG") LOG-DEBUG-STR LOG-TEST-STR LOG-INFO-STR LOG-WARN-STR
|
||||
LOG-ERROR-STR LOG-LEVEL! LOG-LEVEL@ LOG-DEBUG LOG-TEST LOG-INFO
|
||||
LOG-WARN LOG-ERROR THRU --> LOAD LIST EMPTY-BUFFERS FLUSH SAVE-BUFFERS
|
||||
UPDATE BLOCK BUFFER BLK SCR Q.PRINT Q.COS Q.SIN Q.LOG Q.SQRT Q.EXP
|
||||
Q./ Q.* Q.MIN Q.MAX Q.0= Q.> Q.< Q.= Q.NEG Q.ABS Q.- Q.+ Q.SCALE
|
||||
Q.0 Q.1 Q.TO-INT Q.FROM-INT DEFER@ IS DEFER COLD WARM PAGE VERSION
|
||||
79-STANDARD FORGET VLIST WORDS ORDER VOCABULARY DEFINITIONS FORTH
|
||||
CURRENT CONTEXT FENCE SKIP SCAN SEARCH COMPARE -TRAILING BLANK ERASE
|
||||
FILL MOVE CMOVE> ?TERMINAL DMIN DMAX D< M- M+ D2/ D2* D= D0< D0=
|
||||
DABS D- RSHIFT LSHIFT WITHIN MIN MAX ABS U> U< >= <= <> 0> 0<> -!
|
||||
2! 2@ INVERT FALSE TRUE NOP 2R@ 2R> 2>R 2ROT 2OVER 2SWAP DUMP .S
|
||||
? D. D.R U. U.R . .R OCTAL HEX DECIMAL SPACES #> #S # SIGN HOLD
|
||||
<# STATE PAD BASE SPAN >IN TIB BL > < = NOT 0< 0= OR XOR AND M/MOD
|
||||
*/ */MOD MOD / /MOD M* S>D 2/ 2* 2- 2+ 1- 1+ NEGATE * - + CELLS
|
||||
+! ! @ UNLOOP LEAVE J I R@ R> >R ROLL PICK DEPTH 2DROP 2DUP ?DUP
|
||||
-ROT ROT SWAP OVER DROP DUP S" (S") ABORT" (ABORT") ." (.") ABORT
|
||||
QUIT (WORD-FORGOTTEN) (WORD-DEFINED) (REFUSED) (LOST) (CONSOLE)
|
||||
(ME) NODE-ERROR (SEAL) PORT! AWAIT SEND-ON SEND NO-ROUTES GONE NO-ROUTE
|
||||
DEFAULT-ROUTE NEIGHBOUR ROUTE +LOOP LOOP ?DO DO ENDCASE ENDOF OF
|
||||
CASE REPEAT WHILE AGAIN UNTIL BEGIN THEN ELSE IF DOES> CONSTANT
|
||||
VARIABLE CREATE [COMPILE] COMPILE IMMEDIATE EXIT KERNEL-WORD ; :
|
||||
[LITERAL] ['] ' LITERAL ] [ INTERPRET EXECUTE FIND HIDDEN SMUDGE
|
||||
TRAVERSE >BODY PFA CFA NAME> NFA >NAME LINK> LFA >LINK LATEST 2,
|
||||
C, , ALLOT ALIGN HERE \ ( NUMBER CONVERT ENCLOSE WORD QUERY EXPECT
|
||||
SOURCE TYPE COUNT SPACE CR KEY EMIT CMOVE C! C@ DNEGATE D+ UM*
|
||||
ok
|
||||
ok> DEPTH .
|
||||
26 ok
|
||||
ok> HERE .
|
||||
8300 ok
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,247 @@
|
||||
|
||||
OpenSBI v1.8
|
||||
____ _____ ____ _____
|
||||
/ __ \ / ____| _ \_ _|
|
||||
| | | |_ __ ___ _ __ | (___ | |_) || |
|
||||
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
|
||||
| |__| | |_) | __/ | | |____) | |_) || |_
|
||||
\____/| .__/ \___|_| |_|_____/|____/_____|
|
||||
| |
|
||||
|_|
|
||||
|
||||
Platform Name : riscv-virtio,qemu
|
||||
Platform Features : medeleg
|
||||
Platform HART Count : 1
|
||||
Platform HART Protection : pmp
|
||||
Platform IPI Device : aclint-mswi
|
||||
Platform Timer Device : aclint-mtimer @ 10000000Hz
|
||||
Platform Console Device : uart8250
|
||||
Platform HSM Device : ---
|
||||
Platform PMU Device : ---
|
||||
Platform Reboot Device : syscon-reboot
|
||||
Platform Shutdown Device : syscon-poweroff
|
||||
Platform Suspend Device : ---
|
||||
Platform CPPC Device : ---
|
||||
Firmware Base : 0x80000000
|
||||
Firmware Size : 321 KB
|
||||
Firmware RW Offset : 0x40000
|
||||
Firmware RW Size : 65 KB
|
||||
Firmware Heap Offset : 0x47000
|
||||
Firmware Heap Size : 37 KB (total), 0 KB (reserved), 12 KB (used), 23 KB (free)
|
||||
Firmware Scratch Size : 4096 B (total), 1464 B (used), 2632 B (free)
|
||||
Runtime SBI Version : 3.0
|
||||
Standard SBI Extensions : ipi,pmu,srst,sse,hsm,rfnc,fwft,time,base,legacy,dbcn,dbtr
|
||||
Experimental SBI Extensions : none
|
||||
|
||||
Domain0 Name : root
|
||||
Domain0 Boot HART : 0
|
||||
Domain0 HARTs : 0*
|
||||
Domain0 Region00 : 0x0000000080040000-0x000000008005ffff M: (F,R,W) S/U: ()
|
||||
Domain0 Region01 : 0x0000000080000000-0x000000008003ffff M: (F,R,X) S/U: ()
|
||||
Domain0 Region02 : 0x0000000000100000-0x0000000000100fff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region03 : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region04 : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
|
||||
Domain0 Region05 : 0x000000000c400000-0x000000000c5fffff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region06 : 0x000000000c000000-0x000000000c3fffff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region07 : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
|
||||
Domain0 Next Address : 0x0000000020000000
|
||||
Domain0 Next Arg1 : 0x00000000bfe00000
|
||||
Domain0 Next Mode : S-mode
|
||||
Domain0 SysReset : yes
|
||||
Domain0 SysSuspend : yes
|
||||
|
||||
Boot HART ID : 0
|
||||
Boot HART Domain : root
|
||||
Boot HART Priv Version : v1.12
|
||||
Boot HART Base ISA : rv64imafdch
|
||||
Boot HART ISA Extensions : sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu
|
||||
Boot HART PMP Count : 16
|
||||
Boot HART PMP Granularity : 2 bits
|
||||
Boot HART PMP Address Bits : 54
|
||||
Boot HART MHPM Info : 16 (0x0007fff8)
|
||||
Boot HART Debug Triggers : 2 triggers
|
||||
Boot HART MIDELEG : 0x0000000000001666
|
||||
Boot HART MEDELEG : 0x0000000000f4b509
|
||||
[2J[004D[=3h[2J[009D[2J[004D[8;031;100t[2J[016DRISC-V EDK2 firmware version 2025.11-3ubuntu7.3
|
||||
Press ESCAPE within 5 seconds for boot options ERROR: C40000002:V03051002 I0 6D33944A-EC75-4855-A54D-809C75241F6C 83FFF850
|
||||
BdsDxe: failed to load Boot0001 "UEFI Misc Device" fr
|
||||
om PciRoot(0x0)/Pci(0x1,0x0): Not Found
|
||||
[2J[001;001H[=3h[2J[009DStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
Monolithic build - kernel linked directly
|
||||
Collecting boot information...
|
||||
CmdLine: parsed OK
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
GOP: linear framebuffer found
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
Calling kernel_main (monolithic)...
|
||||
riscv64 item 4.3.5a: satp state at kernel entry (before switch)
|
||||
satp.MODE = 0x000000000000000a
|
||||
satp.PPN = 0x00000000000bf868
|
||||
__kernel_start = 0x00000000bdc9caaa
|
||||
riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a)
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: riscv64
|
||||
Build: Oct 8 2026 07:27:03
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 100
|
||||
Total memory: 1020 MB
|
||||
Usable memory: 975 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 250269
|
||||
Free pages : 249617
|
||||
Used pages : 652
|
||||
Total MB : 977
|
||||
Free MB : 975
|
||||
Used MB : 2
|
||||
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
VMM self-test complete.
|
||||
|
||||
IDT installed.
|
||||
|
||||
APIC: init...
|
||||
PLIC: no DTB PLIC node -- using QEMU virt-machine default (base=0x0c000000)
|
||||
PLIC: init (S-mode context 1, threshold=0)
|
||||
APIC: init done
|
||||
|
||||
Timer: init...
|
||||
Timer: RISC-V time CSR @ 10000000 Hz (FALLBACK, no devicetree)
|
||||
Timer: init done
|
||||
|
||||
Kernel heap initialized.
|
||||
Heap statistics:
|
||||
Total bytes: 536870872
|
||||
Free bytes: 536870872
|
||||
Used bytes: 0
|
||||
Peak bytes: 0
|
||||
Heap base addr: 2214588416
|
||||
Heap end addr: 2751459328
|
||||
|
||||
Heartbeat: init...
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x5ff3672509f6c4e5 capsule_hash=0x5ff3672509f6c4e5 words=311
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xc07eb35ca84c4aa8
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_SYSTEM word_count=317 dict_hash=0xc0769523a47b7dc3
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
|
||||
PCI: init...
|
||||
virtio-blk: found device
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 17911425] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
|
||||
Artemis: virtio-blk attached
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
ok> T{
|
||||
UNKNOWN WORD: 'T{'
|
||||
ERROR
|
||||
ok> RS1
|
||||
UNKNOWN WORD: 'RS1'
|
||||
ERROR
|
||||
ok> COLD
|
||||
FORTH-79 Cold Start
|
||||
System initialized.
|
||||
ok
|
||||
ok> 3 4 U* . .
|
||||
0 12 ok
|
||||
ok> FORGET U*
|
||||
Protected word
|
||||
ERROR
|
||||
ok> 9 KERNEL-WORD ASK9 ASK9
|
||||
Argument out of range
|
||||
ERROR
|
||||
ok> 1 BLOCK C@ . 2047 BLOCK C@ .
|
||||
0 0 ok
|
||||
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
|
||||
65 ok
|
||||
ok> 3072 BLOCK C@ .
|
||||
0 ok
|
||||
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
|
||||
Storage refused
|
||||
ERROR
|
||||
ok> 9999999 BLOCK
|
||||
Block out of range
|
||||
ERROR
|
||||
ok> 5 7 PORT!
|
||||
No one on that port
|
||||
ERROR
|
||||
ok> 1 2 + .
|
||||
3 ok
|
||||
ok> 5 AWAIT
|
||||
7 8 * .
|
||||
Interrupted
|
||||
ERROR
|
||||
ok> 56 ok
|
||||
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
||||
Stack overflow
|
||||
ERROR
|
||||
ok> 3 4 + .
|
||||
7 ok
|
||||
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
||||
ok
|
||||
ok> WORDS
|
||||
ASK9 U/MOD U* (UD) (NB) (BITS) BYE ACL-WORD-ID ACL-HEAT@ ACL-INIT-PRIMITIVES
|
||||
ACL-INHERIT ACL-ALLOW! ACL-TTL! ACL-MODE! ACL-PIN ACL-PINNED? ACL-ALLOW@
|
||||
ACL-TTL@ ACL-MODE@ ACL-HOOK LOG-DEBUG" LOG-TEST" LOG-INFO" LOG-WARN"
|
||||
LOG-ERROR" (LOG") LOG-DEBUG-STR LOG-TEST-STR LOG-INFFO-STR LOG-WARN-STR
|
||||
LOG-ERROR-STR LOG-LEVEL! LOG-LEVEL@ LOG-DEBUG LOG-TEST LOG-INFO
|
||||
LOG-WARN LOG-ERROR THRU --> LOAD LIST EMPTY-BUFFERS FLUSH SAVE-BUFFERS
|
||||
UPDATE BLOCK BUFFER BLK SCR Q.PRINT Q.COS Q.SIN Q.LOG Q.SQRT Q.EXP
|
||||
Q./ Q.* Q.MIN Q.MAX Q.0= Q.> Q.< Q.= Q.NEG Q.ABS QQ.- Q.+ Q.SCALE
|
||||
Q.0 Q.1 Q.TO-INT Q.FROM-INT DEFER@ IS DEFER COLD WARM PAGE VERSION
|
||||
79-STANDARD FORGET VLIST WORDS ORDER VOCABULARY DEFINITIONS FORTH
|
||||
CURRENT CONTEXT FENCE SKIP SCAN SEARCH COMPARE -TRAILING BLANK ERASE
|
||||
FILL MOVE CMOVE> ?TERMINAL DMIN DMAX D< M- M+ D2/ D2* DD= D0< D0=
|
||||
DABS D- RSHIFT LSHIFT WITHIN MIN MAX ABS U> U< >= <= <> 0> 0<> -!
|
||||
2! 2@ INVERT FALSE TRUE NOP 2R@ 2R> 2>R 2ROT 2OVER 2SWAP DUMP .S
|
||||
? D. D.R U. U.R . .R OCTAL HEX DECIMAL SPACES #> #S # SIGN HOLD
|
||||
<# STATE PAD BASE SPAN >IN TIB BL > < = NOT 0< 0= OR XOR AND M/MOD
|
||||
*/ */MOD MOD / /MOD M* S>D 2/ 2* 2- 2+ 1- 1+ NEGATE * - + CELLS
|
||||
+! ! @ UNLOOP LEAVE J I R@ R> >R ROLL PICK DEPTH 2DROP 2DUP ?DUP
|
||||
-ROT ROT SWAP OVER DROP DUP S" (S") ABORT" (ABORT") ." (.") ABORT
|
||||
QUIT (WORD-FORGOTTEN) (WORD-DEFINED) (REFUSED) (LOST) (CONSOLE)
|
||||
(ME) NODE-EERROR (SEAL) PORT! AWAIT SEND-ON SEND NO-ROUTES GONE NO-ROUTE
|
||||
DEFAULT-ROUTE NEIGHBOUR ROUTE +LOOP LOOP ?DO DO ENDCASE ENDOF OF
|
||||
CASE REPEAT WHILE AGAIN UNTIL BEGIN THEN ELSE IF DOES> CONSTANT
|
||||
VARIABLE CREATE [COMPILE] COMPILE IMMEDIATE EXIT KERNEL-WORD ; :
|
||||
[LITERAL] ['] ' LLITERAL ] [ INTERPRET EXECUTE FIND HIDDEN SMUDGE
|
||||
TRAVERSE >BODY PFA CFA NAME> NFA >NAME LINK> LFA >LINK LATEST 2,
|
||||
C, , ALLOT ALIGN HERE \ ( NUMBER CONVERT ENCLOSE WORD QUERY EXPECT
|
||||
SOURCE TYPE COUNT SPACE CR KEY EMIT CMOVE C! C@ DNEGATE D+ UM*
|
||||
ok
|
||||
ok> DEPTH .
|
||||
26 ok
|
||||
ok> HERE .
|
||||
8300 ok
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
@@ -204,6 +204,8 @@ hosted-check: $(foreach i,$(HOSTED_ISAS),$(BINDIR)/boot-$(i).txt) $(BINDIR)/star
|
||||
@grep -q '^ok> Interrupted$$' $(BINDIR)/await-amd64.txt && grep -q '^ok> 3 ok$$' $(BINDIR)/await-amd64.txt && grep -q '^ok> 56 ok$$' $(BINDIR)/await-amd64.txt || { echo "hosted-check: a line typed while the node waited did not break the wait and then run"; tail -6 $(BINDIR)/await-amd64.txt; exit 1; }
|
||||
@printf '1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29\n1 2 + .\n' | $(BINDIR)/starforth4-amd64 > $(BINDIR)/deep-amd64.txt 2>&1; true
|
||||
@grep -q '^ok> Stack overflow$$' $(BINDIR)/deep-amd64.txt && grep -q '^ok> 3 ok$$' $(BINDIR)/deep-amd64.txt || { echo "hosted-check: a line that left 29 values on the stack was not an error the node came back from"; tail -4 $(BINDIR)/deep-amd64.txt; exit 1; }
|
||||
@printf '1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26\nWORDS\nDEPTH .\n' | $(BINDIR)/starforth4-amd64 > $(BINDIR)/deepwords-amd64.txt 2>&1; true
|
||||
@grep -q 'DUP' $(BINDIR)/deepwords-amd64.txt && grep -q '^ok> 26 ok$$' $(BINDIR)/deepwords-amd64.txt || { echo "hosted-check: WORDS with 26 values on the stack did not print and come back"; tail -4 $(BINDIR)/deepwords-amd64.txt | cut -c1-200; 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; }
|
||||
@grep -q '^POST FAIL: fail.many out<QQQQ' $(BINDIR)/postfail.txt || { echo "hosted-check: the case that prints too much was not named"; exit 1; }
|
||||
|
||||
+2
-11
@@ -141,17 +141,8 @@ 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:
|
||||
"Message refused", "Node gone", or "Interrupted" when a line is typed at
|
||||
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.
|
||||
|
||||
**The wait** (`MESH.md` 7c, step 6d). A node does not write the first word
|
||||
of a message: it offers it, and sleeps until it is taken or a word comes
|
||||
for it. What a node has to pass on, the answers it has to give and the
|
||||
refusals it owes all wait with it, offered, and go when they can. So a
|
||||
node that is stuck holds up no other, within the limits `MESH.md` 7c.4
|
||||
states: text that must itself send to the stuck node waits, and a node
|
||||
begins no more text from a sender it cannot yet answer. A review found
|
||||
this unsound as first built; what it found is mended and listed under
|
||||
step 6d in `MESH.md` section 10, and has not been reviewed again.
|
||||
is gone; a node believes that only from its centre. A stuck node can hold
|
||||
up its neighbours until it is killed: `MESH.md` 7b.7 says how.
|
||||
|
||||
**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
|
||||
|
||||
+78
-143
@@ -131,22 +131,25 @@ header CMOVE
|
||||
NONE: drop drop (ROUTE-DEFAULT) a! @ ;
|
||||
HIT: drop drop @+ pop drop ;
|
||||
|
||||
\ BEGINNING A MESSAGE: THE WAIT (MESH.md section 7c). A write waits until
|
||||
\ the neighbour reads, and a node that is waiting to write reads nothing:
|
||||
\ two neighbours that each began to write to the other would wait for ever,
|
||||
\ and a node that wrote to one that was stuck would be stuck with it. So
|
||||
\ a node does not write the first word of a message. It offers it, (GATE):
|
||||
\ the word is stored at the port's offer address, and the node then reads
|
||||
\ the wait, where it sleeps until the offer is taken or a word comes for
|
||||
\ it, whichever is first. If a word comes, that message is taken in and
|
||||
\ kept with the messages waiting, and the node offers again. When the
|
||||
\ offer is taken the neighbour has the first word and reads to the end:
|
||||
\ the rest is written, each word waiting for it.
|
||||
\ (OFFER) is the offer address of port 0, and (WAIT) the wait. (READERS)
|
||||
\ is the node's look at its neighbours, a bit for each port that has one
|
||||
\ waiting to read, and above those a bit for each port that has anything
|
||||
\ on it at all. (NEAR) is the number of the node on each port, told by
|
||||
\ whoever wires it (NEIGHBOUR, quit.v4): it is how a node knows its centre.
|
||||
\ LOOKING BEFORE WRITING (MESH.md section 7a). A write waits until the
|
||||
\ neighbour reads, and a node that is waiting to write reads nothing: two
|
||||
\ neighbours that each began to write to the other would wait for ever. So
|
||||
\ a node does not begin a message until it has looked, (GATE):
|
||||
\ - while any neighbour is waiting to write to it, it takes that message
|
||||
\ in and keeps it with the messages waiting, to deal with when it has
|
||||
\ nothing else to do;
|
||||
\ - then, if the neighbour it means to write to is waiting to read, it
|
||||
\ writes;
|
||||
\ - and if that neighbour is not, it writes all the same when the
|
||||
\ neighbour's number is the higher of the two, and otherwise looks
|
||||
\ again. Of two neighbours one only may wait to write to the other, and
|
||||
\ it is the lower: so those that wait are waiting on ever higher
|
||||
\ numbers, and the highest of them is not waiting to write; it is
|
||||
\ looking, and takes in what is being written to it.
|
||||
\ (WRITERS) and (READERS) are the node's two looks at its neighbours, a bit
|
||||
\ for each port. (NEAR) is the number of the node on each port, told by
|
||||
\ whoever wires it (NEIGHBOUR, quit.v4); a port not told of -- a device's
|
||||
\ -- is written to only when what is there is waiting to read.
|
||||
\
|
||||
\ THE MESSAGES WAITING are kept in (MQ) .. (MQ-END), one after another,
|
||||
\ going round: for each its seven words, the port it came on, and its text.
|
||||
@@ -159,6 +162,12 @@ header CMOVE
|
||||
if Z -1 + FOR 2/ NEXT 1 and ;
|
||||
Z: drop 1 and ;
|
||||
|
||||
\ ( mask -- index ) the lowest port in it; the mask is not zero
|
||||
: (LOW)
|
||||
0 push
|
||||
L: dup 1 and if UP drop drop pop ;
|
||||
UP: drop 2/ pop 1 + push jump L
|
||||
|
||||
\ ( w -- ) one more cell of the messages waiting. B is kept.
|
||||
: (MQ!)
|
||||
(MQ-TAIL) a! @ a! !+
|
||||
@@ -182,7 +191,7 @@ header CMOVE
|
||||
\ messages in so as to be free to write, where it cannot begin a message
|
||||
\ of its own: so it notes what it owes, (OWE), in the eight pairs at
|
||||
\ (OWED) -- whom it is owed to, and the node the message was for -- and
|
||||
\ sends it when it next has nothing else to do, (PAY-SET). With eight owed
|
||||
\ sends it when it next has nothing else to do, (PAY). With eight owed
|
||||
\ already, one more is let go and counted. Nothing is owed for a NACK or
|
||||
\ for a GONE, type 5: there is no NACK for a NACK. The last 36 cells of
|
||||
\ the messages waiting are kept for those two types, so that ordinary
|
||||
@@ -239,76 +248,37 @@ header CMOVE
|
||||
KEPT-FOR: drop -MQ-ROOM +
|
||||
ROOM: -if FULL
|
||||
drop push
|
||||
(MQ-TAIL) a! @ (T-TAIL) a! ! (MQ#) a! @ (T-COUNT) a! ! 1 (TAKING) a! ! \ until all of it is here: (MQ-MEND)
|
||||
(MQ-HDR) a! @ (MQ!) (MQ-HDR)+1 a! @ (MQ!) (MQ-HDR)+2 a! @ (MQ!) (MQ-HDR)+3 a! @ (MQ!)
|
||||
(MQ-HDR)+4 a! @ (MQ!) (MQ-HDR)+5 a! @ (MQ!) (MQ-HDR)+6 a! @ (MQ!) (MQ-HDR)+7 a! @ (MQ!)
|
||||
pop if NOTEXT -1 + FOR @b (MQ!) NEXT 0 (TAKING) a! ! ;
|
||||
NOTEXT: drop 0 (TAKING) a! ! ;
|
||||
pop if NOTEXT -1 + FOR @b (MQ!) NEXT ;
|
||||
NOTEXT: drop ;
|
||||
FULL: drop if READ -1 + FOR @b drop UNEXT jump LOSE
|
||||
READ: drop
|
||||
LOSE: (LOST) a! @ 1 + !
|
||||
(MQ-HDR)+2 a! @ push 0 (MQ-HDR)+2 a! ! \ its type is taken out of (MQ-HDR): it is not here
|
||||
(MQ-HDR)+1 a! @ (MQ-HDR) a! @ pop jump (TELL-OF)
|
||||
|
||||
\ ( -- ) A MESSAGE HALF TAKEN IN IS LET GO. Whoever was writing it was
|
||||
\ removed, and the error that told this node so ended the taking in: what
|
||||
\ had come is with the messages waiting, and would be taken for a whole
|
||||
\ message. They are put back as they were before it, and it is counted.
|
||||
: (MQ-MEND)
|
||||
(TAKING) a! @ if WHOLE
|
||||
drop (T-TAIL) a! @ (MQ-TAIL) a! ! (T-COUNT) a! @ (MQ#) a! ! 0 (TAKING) a! !
|
||||
(LOST) a! @ 1 + ! ;
|
||||
WHOLE: drop ;
|
||||
(MQ-HDR)+1 a! @ (MQ-HDR) a! @ (MQ-HDR)+2 a! @ jump (TELL-OF)
|
||||
|
||||
\ ( port to -- ) take in a message and keep it
|
||||
: (TAKE) (TAKE-HDR) jump (TAKE-KEEP)
|
||||
|
||||
\ ( -- how ) THE WAIT, once. Whatever has been offered stands until
|
||||
\ this returns. 0: a word came for this node instead; that whole message
|
||||
\ has been taken in and kept, (TAKE), and its seven words are still in
|
||||
\ (MQ-HDR). -1: an offer was taken; B is at the port it was taken on, and
|
||||
\ (GATE-PORT) holds that port's address. 1: there is nothing on a port
|
||||
\ that had an offer: no one will ever take it.
|
||||
: (OFFERS)
|
||||
(WAIT) b! @b push (PORT)+9 b! @b \ ( which ) R: the word
|
||||
dup -PORTS + -if TAKEN
|
||||
drop (PORT) + dup b! pop (TAKE) 0 ; \ a word, from that port: the message's first
|
||||
TAKEN: dup -PORTS + -if NOONE \ ( which k x ) k: the port, by its number
|
||||
drop (PORT) + (GATE-PORT) a! ! drop pop drop
|
||||
(GATE-PORT) a! @ b! -1 ;
|
||||
NOONE: drop drop drop pop drop 1 ;
|
||||
|
||||
\ ( w port -- ) BEGIN A MESSAGE: the word, its first, is offered on the
|
||||
\ port, by its address, until it is taken; whatever is written to this
|
||||
\ node meanwhile is taken in. B is left at the port. The two are kept in
|
||||
\ (GATE-WORD) and (GATE-PORT), not on the stack: (GATE1), which is this
|
||||
\ with the word already in (GATE-WORD), is run with the stack as full as
|
||||
\ EMIT may be.
|
||||
\ TEXT FROM THE CONSOLE for this node, coming while text it is doing waits
|
||||
\ here, ends that text in error 21, Interrupted (MESH.md 7c.3); it is with
|
||||
\ the messages waiting and is done next. It is how a node whose text must
|
||||
\ begin a message to a node that is stuck is typed to. Not while (QUIET)
|
||||
\ is set: what waits then is no text's doing.
|
||||
\ With nothing on the port it is error 18.
|
||||
: (GATE1) ( port -- )
|
||||
\ ( port -- ) wait until a message may be begun on the port, by its
|
||||
\ address, taking in whatever is being written to this node meanwhile; and
|
||||
\ leave B at the port. The port is kept in (GATE-PORT), not on the stack:
|
||||
\ this is run with the stack as full as EMIT may be.
|
||||
: (GATE)
|
||||
(GATE-PORT) a! !
|
||||
L: 0 (WAIT) a! ! \ nothing else is offered
|
||||
(GATE-WORD) a! @ (GATE-PORT) a! @ -PORTS - 4 + a! ! \ the offer
|
||||
(OFFERS) if CAME -if NOONE drop ;
|
||||
NOONE: drop NODE-ERROR b! 18 !b ; \ no one on that port
|
||||
CAME: drop
|
||||
(QUIET) a! @ if TEXTS drop jump L
|
||||
TEXTS: drop
|
||||
(MQ-HDR) a! @ (ME) a! @ xor if MINE drop jump L
|
||||
MINE: drop
|
||||
(MQ-HDR)+2 a! @ -1 + if TEXT drop jump L
|
||||
TEXT: drop
|
||||
(CONSOLE) a! @ if C0 jump C1
|
||||
C0: drop (MSG)+1 a! @ \ no console told of: whoever sent the text being done
|
||||
C1: (MQ-HDR)+1 a! @ xor if BREAK drop jump L
|
||||
BREAK: drop NODE-ERROR b! 21 !b ;
|
||||
|
||||
: (GATE) ( w port -- ) push (GATE-WORD) a! ! pop jump (GATE1)
|
||||
L: (WRITERS) b! @b if QUIET
|
||||
(LOW) (PORT) + dup b! @b (TAKE) jump L
|
||||
QUIET: drop
|
||||
(GATE-PORT) a! @ (PORT) - (NEAR) + a! @ if ASK
|
||||
(ME) a! @ - -if GO jump ASK \ its number less this node's
|
||||
GO: drop (GATE-PORT) a! @ b! ; \ the neighbour's is the higher
|
||||
ASK: drop
|
||||
(READERS) b! @b (GATE-PORT) a! @ (PORT) - (BIT) if NOTYET
|
||||
drop (GATE-PORT) a! @ b! ; \ it is waiting to read
|
||||
NOTYET: drop
|
||||
(READERS) b! @b (GATE-PORT) a! @ (PORT) - -PORTS - (BIT) if NOONE \ is there anything on the port at all?
|
||||
drop jump L
|
||||
NOONE: drop NODE-ERROR b! 18 !b ; \ no: what waits here would wait for ever
|
||||
|
||||
\ ( type -- ) words 1 to 5 of a message this node sends: from, type, and
|
||||
\ the three that are carried and not used yet. Its caller has put B at the
|
||||
@@ -319,30 +289,14 @@ header CMOVE
|
||||
!b \ type
|
||||
16 !b 0 !b 0 !b ; \ how many nodes may pass it on (the TTL: MESH.md 7b); ACL tag; sequence
|
||||
|
||||
\ PAYING WHAT IS OWED (MESH.md 7c.3). Every refusal owed is offered at
|
||||
\ once, each on the port it goes by, one to a port, with whatever the node
|
||||
\ has to pass on (quit.v4, (IDLE)), and the node sleeps in the wait: so
|
||||
\ one owed to a node that is stuck does not keep back one owed to a node
|
||||
\ that waits for it, and the node takes in whatever is written to it
|
||||
\ meanwhile. One there is no way for, or whose way is a
|
||||
\ port with nothing on it, is let go and counted first. (PAY-K) has a
|
||||
\ cell for each port: which refusal is offered there, counted from 1; 0,
|
||||
\ none; -1, a message being passed on.
|
||||
|
||||
\ ( w -- ) WRITE A WORD OF A MESSAGE AFTER ITS FIRST, on the port
|
||||
\ (GATE-PORT) holds, from where no error must come: the passes over the
|
||||
\ messages waiting (quit.v4). It is offered, and the node waits for that
|
||||
\ offer only: the neighbour has the first word and is reading the rest.
|
||||
\ If there turns out to be nothing on the port -- the neighbour was removed
|
||||
\ -- (W-GONE) is set, and this word and every one after it is let go
|
||||
\ without waiting. Whoever begins the writing clears (W-GONE) first.
|
||||
: (!W)
|
||||
(W-GONE) a! @ if THERE drop drop ;
|
||||
THERE: drop
|
||||
(GATE-PORT) a! @ -PORTS - 4 + a! !
|
||||
(WAIT1) b! @b drop (PORT)+9 b! @b
|
||||
-PORTS + -PORTS + -if NOONE drop ;
|
||||
NOONE: drop 1 (W-GONE) a! ! ;
|
||||
\ PAYING WHAT IS OWED does not hold this node up. A refusal is sent if the
|
||||
\ neighbour it goes by is reading at this moment. If it is not, and its
|
||||
\ number is the higher, the refusal is written all the same, as any message
|
||||
\ would be (MESH.md 7a): that neighbour takes it in when it next looks. If
|
||||
\ its number is the lower, or it has not been told of, the refusal stays
|
||||
\ owed, and this node goes on with what it has to do and tries again; it
|
||||
\ does not sleep while it owes one (quit.v4, (IDLE)). One there is no way
|
||||
\ for, or whose way is a port with nothing on it, is let go and counted.
|
||||
|
||||
\ ( k -- ) the k-th refusal owed is done with: the last takes its place
|
||||
: (PAID)
|
||||
@@ -350,55 +304,36 @@ header CMOVE
|
||||
2* (OWED) + a! @+ @
|
||||
push push 2* (OWED) + a! pop !+ pop ! ;
|
||||
|
||||
\ ( k -- ) the k-th refusal owed is let go if it cannot be sent at all
|
||||
: (PAY-DROP)
|
||||
dup 2* (OWED) + a! @ \ k to
|
||||
(PORT-FOR) if NOWAY \ k port
|
||||
(THERE) if GONE drop drop ;
|
||||
GONE:
|
||||
NOWAY: drop (LOST) a! @ 1 + ! jump (PAID)
|
||||
\ ( k -- ) send the k-th refusal owed, if it can go now
|
||||
: (PAY1)
|
||||
dup 2* (OWED) + a! @+ @ \ k to about
|
||||
over (PORT-FOR) if NOWAY \ k to about port
|
||||
dup (THERE) if GONE
|
||||
drop (READERS) b! @b over (PORT) - (BIT) if BUSY
|
||||
drop
|
||||
WRITE: b! \ k to about B: the port
|
||||
SWAP !b 4 (HDR) 4 !b !b \ k to; from, type 4; four characters; about
|
||||
jump (PAID)
|
||||
BUSY: drop \ it is not reading:
|
||||
dup (PORT) - (NEAR) + a! @ if LEAVE \ k to about port near
|
||||
(ME) a! @ - -if BLIND jump LEAVE \ its number less this node's
|
||||
BLIND: drop jump WRITE \ the higher: it will take it in
|
||||
LEAVE: drop drop drop drop drop ;
|
||||
GONE: drop
|
||||
NOWAY: drop drop drop (LOST) a! @ 1 + ! jump (PAID)
|
||||
|
||||
\ ( k -- ) offer the k-th refusal owed on its port, if nothing is there yet
|
||||
: (PAY-OFFER)
|
||||
dup push 2* (OWED) + a! @ \ to R: k (the stack is kept shallow: this runs between lines)
|
||||
dup (PORT-FOR) (PORT) - \ to n the port, by its number
|
||||
dup (PAY-K) + a! @ if FREE
|
||||
drop drop drop pop drop ;
|
||||
FREE: drop
|
||||
dup push (OFFER) + a! ! \ the message's first word, whom it is to
|
||||
pop pop 1 + SWAP (PAY-K) + a! ! ;
|
||||
|
||||
\ ( -- ) let go every refusal owed that cannot be sent; no port has an
|
||||
\ offer; then offer each that is left on its port
|
||||
: (PAY-SET)
|
||||
0 (WAIT) a! ! \ what was offered before is not: (IDLE) may have gone round without waiting
|
||||
(OWED#) a! @ if NONE
|
||||
-1 + FOR pop dup push (PAY-DROP) NEXT
|
||||
jump CLEAR
|
||||
NONE: drop
|
||||
CLEAR: (PAY-K) a! PORTS-1 FOR 0 !+ NEXT
|
||||
(OWED#) a! @ if NONE2
|
||||
-1 + FOR pop dup push (PAY-OFFER) NEXT ;
|
||||
NONE2: drop ;
|
||||
|
||||
\ ( k -- ) the offer of the k-th refusal owed was taken, and B is at the
|
||||
\ port: its other words are written, and it is done with
|
||||
: (PAY-GO)
|
||||
dup (A-WORD) a! ! \ k is kept off the stack: nothing is left there if the port goes
|
||||
2* (OWED) + 1 + a! @ push \ R: about
|
||||
0 (W-GONE) a! !
|
||||
(ME) a! @ (!W) 4 (!W) 16 (!W) 0 (!W) 0 (!W) \ from, type 4, and the three carried
|
||||
4 (!W) pop (!W) \ four characters; about
|
||||
(W-GONE) a! @ if SENT drop (LOST) a! @ 1 + ! jump DONE
|
||||
SENT: drop
|
||||
DONE: (A-WORD) a! @ jump (PAID)
|
||||
\ ( -- ) send every refusal this node owes that can go now
|
||||
: (PAY)
|
||||
(OWED#) a! @ if NONE -1 +
|
||||
FOR pop dup push (PAY1) NEXT ;
|
||||
NONE: drop ;
|
||||
|
||||
\ ( -- ) send what has been printed, to where (PRINT-TO) says, by the port
|
||||
\ (PRINT-PORT): its length, then its characters, four to a word, the first
|
||||
\ lowest. quit.v4 sets those two when text for this node arrives.
|
||||
: (FLUSH-OUT)
|
||||
(OUT^) a! @ (OUT) xor if NONE drop
|
||||
(PRINT-TO) a! @ (GATE-WORD) a! ! (PRINT-PORT) a! @ (GATE1)
|
||||
(PRINT-PORT) a! @ (GATE) (PRINT-TO) a! @ !b
|
||||
2 (HDR)
|
||||
(OUT^) a! @ (OUT) - dup !b
|
||||
3 + 2/ 2/ -1 + (OUT) a!
|
||||
|
||||
+93
-256
@@ -48,55 +48,19 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
\ structures. A definition that was under way stays hidden.
|
||||
: (RESET) 0 STATE a! ! (CG-RESET) CFBASE (CFP) a! ! ;
|
||||
|
||||
\ ( -- ) WHAT WAS PRINTED AND NOT SENT is put with the messages waiting,
|
||||
\ as a message of type 2 to where (PRINT-TO) says, by
|
||||
\ the port (PRINT-PORT): its characters four to a word, the first lowest.
|
||||
: (QUEUE-OUT)
|
||||
(OUT^) a! @ (OUT) - if NONE \ ( chars )
|
||||
dup 3 + 2/ 2/ (MQ#) a! @ + -MQ-ROOM + -if FULL drop
|
||||
(PRINT-TO) a! @ (MQ!) (ME) a! @ (MQ!) 2 (MQ!) 17 (MQ!) 0 (MQ!) 0 (MQ!)
|
||||
dup (MQ!) \ its length
|
||||
0 (PRINT-PORT) a! @ - (MQ!) \ the port, kept below zero: this node's own
|
||||
3 + 2/ 2/ -1 + push (OUT) \ ( at ) R: words - 1
|
||||
pop FOR dup a! @+ @+ 8* + @+ 8* 8* + @+ 8* 8* 8* + (MQ!) 4 + NEXT
|
||||
drop (OUT) (OUT^) a! ! ;
|
||||
NONE: drop ;
|
||||
FULL: drop drop (OUT) (OUT^) a! ! (LOST) a! @ 1 + ! ;
|
||||
|
||||
\ ( s -- ) the text is done with. What it printed is sent, then a message
|
||||
\ saying how it ended -- 0 QUIT, 1 completed, 2 an error -- and the node waits
|
||||
\ for the next. (DONE) is entered with the same number and, for an error,
|
||||
\ stops compiling first; it is always jumped to, never called, by
|
||||
\ something that has emptied the return stack (or by a fault, which empties
|
||||
\ it). (LINE-STATUS) keeps how the last text ended.
|
||||
\ A TEXT MAY LEAVE 28 VALUES ON THE STACK. What a node does between texts
|
||||
\ -- (IDLE)'s passes over the messages waiting -- needs four cells of it,
|
||||
\ and a stack fault in the middle of a pass would leave those messages in
|
||||
\ pieces. So a text that leaves more ends here in "Stack overflow", with
|
||||
\ the stack emptied, as a fault would end it.
|
||||
\ THE MESSAGE SAYING HOW IT ENDED, AND WHAT THE TEXT PRINTED AND HAS NOT
|
||||
\ SENT, are not written here: whoever they are for, or a node on the way,
|
||||
\ may be stuck, and this node would wait on it. They are put with the
|
||||
\ messages waiting, (QUEUE-OUT) for what was printed, and passed on from
|
||||
\ (IDLE), as any message for another node is. With no room for one it
|
||||
\ is let go and counted. It is put before the others, and this node does not begin
|
||||
\ another text from the same sender until it has gone ((MINE?)): so what
|
||||
\ one sender is told comes in the order the texts were done.
|
||||
: (FINISH) ( s -- )
|
||||
DSTACK-DEPTH a! @ -30 + -if OVER drop \ what follows needs four cells of the stack: with
|
||||
1 (QUIET) a! ! \ from here an error is not this text's: see (RAISED)
|
||||
(LINE-STATUS) a! !
|
||||
(MQ-MEND)
|
||||
(QUEUE-OUT) \ what the text printed, and then
|
||||
1 (MQ#) a! @ + -MQ-ROOM + -if FULL \ how it ended, go with the messages waiting,
|
||||
drop \ to be passed on as any is: (IDLE)
|
||||
(MSG)+1 a! @ (MQ!) (ME) a! @ (MQ!) 3 (MQ!) \ to whoever sent the text, from this node, type 3
|
||||
17 (MQ!) 0 (MQ!) 0 (MQ!) 4 (MQ!) \ sixteen nodes may pass it on; four characters long
|
||||
0 (DONE-PORT) a! @ - (MQ!) \ by the port that leads back, kept below zero: this node's own
|
||||
(LINE-STATUS) a! @ (MQ!) \ how it ended
|
||||
(LINE-STATUS) b! dup !b
|
||||
(FLUSH-OUT)
|
||||
(DONE-PORT) a! @ (GATE) (MSG)+1 a! @ !b \ to whoever sent the text
|
||||
3 (HDR) 4 !b !b \ a message of type 3, four characters long: how it ended
|
||||
jump (IDLE)
|
||||
FULL: drop (LOST) a! @ 1 + ! jump (IDLE) \ no room at all: it is let go, and counted
|
||||
OVER: drop 0 DSTACK-DEPTH a! ! jump (D-OVER) \ more left on it than may wait, the text ends in that error
|
||||
|
||||
: (DONE)
|
||||
if GO -1 + if OK
|
||||
@@ -104,194 +68,42 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
OK: drop 1 jump (FINISH)
|
||||
GO: drop 0 jump (FINISH)
|
||||
|
||||
\ THE MESSAGES WAITING, GONE THROUGH. Three words below go through them
|
||||
\ all, as (A-QUEUE) does further down: each in turn is taken from the
|
||||
\ front, its eight cells into (MQ-HDR), and either used up or put at the
|
||||
\ back, so that those that stay are in the order they were in. (A-LEFT)
|
||||
\ is how many cells are still to be looked at, and (A-FOUND) what the pass
|
||||
\ has found.
|
||||
|
||||
\ ( -- n ) the next of them is in (MQ-HDR); n is how many words of text
|
||||
\ it has, which are still at the front
|
||||
: (MQ-NEXT)
|
||||
(A-HDR)
|
||||
(MQ-HDR)+6 a! @ -if SIZED drop 0 jump N
|
||||
SIZED: 3 + 2/ 2/
|
||||
N: (A-LEFT) a! @ over 8 + - ! ;
|
||||
|
||||
\ ( -- port | 0 ) the port the message in (MQ-HDR) goes by: for one this
|
||||
\ node made itself, (FINISH), the port kept with it, below zero; for any
|
||||
\ other, the way to the node it is for
|
||||
: (FWD-PORT)
|
||||
(MQ-HDR)+7 a! @ -if THEIRS 0 SWAP - ;
|
||||
THEIRS: drop (MQ-HDR) a! @ jump (PORT-FOR)
|
||||
|
||||
\ ( -- flag ) is the message in (MQ-HDR) one to pass on? One this node
|
||||
\ made itself is; any other is unless it is for this node, and waits here
|
||||
\ to be dealt with ((MINE?) holds text back)
|
||||
: (FWD?)
|
||||
(MQ-HDR)+7 a! @ -if THEIRS drop -1 ;
|
||||
THEIRS: drop (MQ-HDR) a! @ (ME) a! @ xor ;
|
||||
|
||||
\ ( -- flag ) IS THERE ONE FOR THIS NODE? The first that is, is taken
|
||||
\ out: its seven words into (MSG), the port it came on into (REPLY), its
|
||||
\ text into TIB.
|
||||
\ TEXT IS NOT BEGUN WHILE WHAT ITS SENDER IS OWED FROM AN EARLIER ONE IS
|
||||
\ STILL HERE: what that text printed, or how it ended -- a message of type
|
||||
\ 2 or 3 this node made itself, (FINISH). (NOTES) goes through the
|
||||
\ messages waiting first and notes whom each such message is to, in
|
||||
\ (PAY-K), which nothing else is using now; (A-WORD) is how many: as many
|
||||
\ as there are ports, and one that cannot be noted holds nothing back. A
|
||||
\ GONE this node is sending holds nothing back either, and a NACK or a
|
||||
\ GONE for this node is never held back.
|
||||
: (NOTES)
|
||||
0 (A-WORD) a! !
|
||||
(MQ#) a! @ (A-LEFT) a! !
|
||||
L: (A-LEFT) a! @ if END drop
|
||||
(MQ-NEXT)
|
||||
(MQ-HDR)+7 a! @ -if KEEP0
|
||||
drop (MQ-HDR)+2 a! @ -2 + if NOTE -1 + if NOTE drop jump KEEP
|
||||
NOTE: drop (A-WORD) a! @ -PORTS + -if KEEP0
|
||||
drop (MQ-HDR) a! @ push (A-WORD) a! @ dup 1 + ! (PAY-K) + a! pop !
|
||||
jump KEEP
|
||||
KEEP0: drop
|
||||
KEEP: (A-BACK) (A-TEXT) jump L
|
||||
END: drop ;
|
||||
|
||||
: (MINE?)
|
||||
(NOTES)
|
||||
0 (A-FOUND) a! !
|
||||
(MQ#) a! @ (A-LEFT) a! !
|
||||
L: (A-LEFT) a! @ if END drop
|
||||
(MQ-NEXT)
|
||||
(A-FOUND) a! @ if LOOK drop jump KEEP
|
||||
LOOK: drop
|
||||
(MQ-HDR)+7 a! @ -if THEIRS drop jump KEEP \ this node's own: to be passed on
|
||||
THEIRS: drop
|
||||
(MQ-HDR) a! @ (ME) a! @ xor if MINE drop jump KEEP
|
||||
MINE: drop
|
||||
(MQ-HDR)+2 a! @ -1 + if TEXT drop jump TAKE
|
||||
TEXT: drop
|
||||
(A-WORD) a! @ if TAKE0 \ nothing of this node's own is waiting to go
|
||||
(MQ-HDR)+1 a! @ SWAP -1 + (PAY-K) a! \ ( n from count-1 )
|
||||
FOR dup @+ xor if HIT drop NEXT
|
||||
drop jump TAKE \ none of it is to this text's sender
|
||||
HIT: drop drop pop drop jump KEEP
|
||||
TAKE0: drop
|
||||
TAKE: 1 (A-FOUND) a! !
|
||||
(MQ-HDR) a! @ (MSG) a! ! (MQ-HDR)+1 a! @ (MSG)+1 a! ! (MQ-HDR)+2 a! @ (MSG)+2 a! !
|
||||
(MQ-HDR)+3 a! @ (MSG)+3 a! ! (MQ-HDR)+4 a! @ (MSG)+4 a! ! (MQ-HDR)+5 a! @ (MSG)+5 a! !
|
||||
(MQ-HDR)+6 a! @ (MSG)+6 a! ! (MQ-HDR)+7 a! @ (REPLY) a! !
|
||||
TIB 2/ 2/ SWAP if EMPTY
|
||||
-1 + FOR (MQ@) over a! ! 1 + NEXT
|
||||
drop jump L
|
||||
EMPTY: drop drop jump L
|
||||
KEEP: (A-BACK) (A-TEXT) jump L
|
||||
END: drop (A-FOUND) a! @ ;
|
||||
|
||||
\ ( -- flag ) WHAT IS TO BE PASSED ON IS OFFERED (MESH.md 7c.3): for each
|
||||
\ message, the first for its port, its first word is offered there, unless
|
||||
\ a refusal owed is offered there already; (PAY-K) is set to -1 for that
|
||||
\ port. It stays with the messages waiting until the offer is taken.
|
||||
\ One that cannot go at all is let go, counted in (LOST), and whoever
|
||||
\ waits on it is told (core.v4, (TELL-OF)): one with no way known; one
|
||||
\ whose way is a port with nothing on it; and one that fifteen nodes have
|
||||
\ passed on already. The flag is not zero if any was let go.
|
||||
\ A MESSAGE IS PASSED ON BY AT MOST 16 NODES. Its fourth word says how
|
||||
\ many more may; 0, as a device sends it, is taken for 16.
|
||||
: (FWD-SET)
|
||||
0 (A-FOUND) a! !
|
||||
(MQ#) a! @ (A-LEFT) a! !
|
||||
L: (A-LEFT) a! @ if END drop
|
||||
(MQ-NEXT) (A-WORD) a! ! \ its words of text, kept off the stack: this runs between lines
|
||||
(FWD?) if STAYS drop
|
||||
(MQ-HDR)+3 a! @ if FRESH jump COUNT
|
||||
FRESH: drop 16
|
||||
COUNT: -1 + if REFUSE drop
|
||||
(FWD-PORT) if REFUSE \ ( port )
|
||||
dup (THERE) if REFUSE2 drop
|
||||
(PORT) - dup (PAY-K) + a! @ if FREE \ ( p slot ) p: the port, by its number
|
||||
drop drop jump KEEP
|
||||
FREE: drop dup push (MQ-HDR) a! @ SWAP (OFFER) + a! ! \ whom it is to, offered
|
||||
-1 pop (PAY-K) + a! !
|
||||
jump KEEP
|
||||
STAYS: drop
|
||||
KEEP: (A-BACK) (A-WORD) a! @ (A-TEXT) jump L
|
||||
REFUSE2: drop
|
||||
REFUSE: drop
|
||||
(A-WORD) a! @ if NOTEXT -1 + FOR (MQ@) drop NEXT jump TOLD
|
||||
NOTEXT: drop
|
||||
TOLD: (LOST) a! @ 1 + ! 1 (A-FOUND) a! !
|
||||
(MQ-HDR)+1 a! @ (MQ-HDR) a! @ (MQ-HDR)+2 a! @ (TELL-OF) jump L
|
||||
END: drop (A-FOUND) a! @ ;
|
||||
|
||||
\ ( -- ) THE OFFER OF ONE WAS TAKEN, on the port (GATE-PORT) holds: the
|
||||
\ first of the messages waiting that goes by that port is the one. Its
|
||||
\ other words are written, (!W), each waiting for the neighbour, which has
|
||||
\ its first and reads to the end; and it is done with. If the neighbour
|
||||
\ is removed meanwhile the rest is let go, and counted: no error comes of
|
||||
\ it, for an error here would leave the messages waiting in pieces.
|
||||
: (FWD-GO)
|
||||
0 (A-FOUND) a! !
|
||||
(MQ#) a! @ (A-LEFT) a! !
|
||||
L: (A-LEFT) a! @ if END drop
|
||||
(MQ-NEXT)
|
||||
(A-FOUND) a! @ if LOOK drop jump KEEP
|
||||
LOOK: drop
|
||||
(FWD?) if KEEP1 drop
|
||||
(FWD-PORT) (GATE-PORT) a! @ xor if THIS drop jump KEEP
|
||||
KEEP1: drop jump KEEP
|
||||
THIS: drop 1 (A-FOUND) a! ! (A-WORD) a! ! \ its words of text, kept off the stack
|
||||
0 (W-GONE) a! !
|
||||
(MQ-HDR)+1 a! @ (!W) (MQ-HDR)+2 a! @ (!W)
|
||||
(MQ-HDR)+3 a! @ if FRESH jump COUNT
|
||||
FRESH: drop 16
|
||||
COUNT: -1 + (!W) \ one fewer may pass it on
|
||||
(MQ-HDR)+4 a! @ (!W) (MQ-HDR)+5 a! @ (!W) (MQ-HDR)+6 a! @ (!W)
|
||||
(A-WORD) a! @ if NOTEXT -1 + FOR (MQ@) (!W) NEXT jump SENT
|
||||
NOTEXT: drop
|
||||
SENT: (W-GONE) a! @ if WENT drop (LOST) a! @ 1 + ! jump L \ its reader was removed: the rest was let go
|
||||
WENT: drop jump L
|
||||
KEEP: (A-BACK) (A-TEXT) jump L
|
||||
END: drop ;
|
||||
|
||||
\ ( -- ) WAITING. A node with nothing to do looks through the messages
|
||||
\ waiting (core.v4).
|
||||
\ ONE FOR THIS NODE is dealt with, the oldest first: (MINE?). A message
|
||||
\ of type 1 is text to interpret: it is done as a line was, and (FINISH)
|
||||
\ reports how it ended; one that came with a length no message has is
|
||||
\ error 12. A NACK is counted and a GONE from the node's centre believed.
|
||||
\ WITH NONE FOR IT, what it has for other nodes, and the refusals it owes,
|
||||
\ are offered, each on the port it goes by, and the node sleeps in the
|
||||
\ wait (MESH.md 7c): until one is taken, and then the rest of that one is
|
||||
\ written; or until a word comes for it, and then that message is taken in
|
||||
\ and it looks again. A node that is stuck never takes what is offered
|
||||
\ it, and holds up nothing else.
|
||||
\ WITH NOTHING WAITING AND NOTHING OWED it is blocked reading its ports
|
||||
\ (docs/v4.0.0/MESH.md 4.1): it executes nothing until a neighbour writes,
|
||||
\ and what arrives is taken in as any message is.
|
||||
\ ( -- ) WAITING. A node with nothing to do deals with the oldest of the
|
||||
\ messages waiting (core.v4). With none waiting it is blocked reading its
|
||||
\ ports (docs/v4.0.0/MESH.md 4.1): it executes nothing until a neighbour
|
||||
\ writes, and what arrives is taken in as any message is, its first word
|
||||
\ from any port and the rest from the port that came on. The message's
|
||||
\ text goes into TIB, which holds 1024 characters, the most a message
|
||||
\ carries; one that came with a length no message has is error 12. A
|
||||
\ message of type 1 for this node is text to interpret: it is done as a
|
||||
\ line was, and (FINISH) reports how it ended. A message for another node
|
||||
\ is passed on, (PASS-ON).
|
||||
: (IDLE)
|
||||
L: 1 (QUIET) a! ! \ nothing here is any text's doing: see (RAISED)
|
||||
(MINE?) if OTHERS drop jump HAVE
|
||||
OTHERS: drop
|
||||
(MQ#) a! @ (OWED#) a! @ + if BLOCK drop
|
||||
(PAY-SET) (FWD-SET) if SET drop jump L \ one was let go: what is owed has changed
|
||||
SET: drop
|
||||
(MQ#) a! @ (OWED#) a! @ + if BLOCK drop
|
||||
(OFFERS) if CAME -if CAME drop \ nothing on a port: what was for it is let go next time round
|
||||
(GATE-PORT) a! @ (PORT) - (PAY-K) + a! @ \ what was offered on the port it was taken on
|
||||
-if OWED drop (FWD-GO) jump L
|
||||
OWED: -1 + (PAY-GO) jump L
|
||||
CAME: drop jump L
|
||||
(PAY) \ what refusals can be sent now, are (core.v4)
|
||||
(MQ#) a! @ if WAIT drop jump HAVE
|
||||
WAIT: drop (OWED#) a! @ if BLOCK \ with one still owed it does not sleep:
|
||||
drop (WRITERS) b! @b if L0 \ it takes in whatever is being written to it,
|
||||
(LOW) (PORT) + dup b! @b (TAKE) jump L
|
||||
L0: drop jump L \ and tries again
|
||||
BLOCK: drop (PORT)+8 b! @b (PORT)+9 b! @b (PORT) + dup b! \ ( to port ) B: the port it came on
|
||||
SWAP (TAKE) jump L
|
||||
HAVE:
|
||||
(MQ@) (MSG) a! ! (MQ@) (MSG)+1 a! ! (MQ@) (MSG)+2 a! ! (MQ@) (MSG)+3 a! !
|
||||
(MQ@) (MSG)+4 a! ! (MQ@) (MSG)+5 a! ! (MQ@) (MSG)+6 a! ! (MQ@) (REPLY) a! !
|
||||
(MSG)+6 a! @ -if SIZED
|
||||
drop 0 (MSG)+6 a! ! 0 SPAN a! ! NODE-ERROR b! 12 !b \ no telling what it was
|
||||
SIZED: drop
|
||||
TIB 2/ 2/ (MSG)+6 a! @ 3 + 2/ 2/ if EMPTY
|
||||
-1 + FOR (MQ@) over a! ! 1 + NEXT
|
||||
drop jump READ
|
||||
EMPTY: drop drop
|
||||
READ:
|
||||
0 (MSG)+6 a! @ TIB + C! \ a zero after the text
|
||||
(MSG)+6 a! @ SPAN a! !
|
||||
(MSG)+2 a! @ -1 + if TEXT
|
||||
(MSG) a! @ (ME) a! @ xor if MINE drop jump (PASS-ON)
|
||||
MINE: drop
|
||||
(MSG)+2 a! @ -1 + if TEXT \ for this node, and
|
||||
-3 + if NACK -1 + if GONE drop jump (IDLE) \ neither text nor these: let go
|
||||
NACK: drop (REFUSED) a! @ 1 + ! jump (IDLE) \ a message of this node's was refused: counted
|
||||
GONE: drop (MSG)+6 a! @ -4 + if G4 drop jump (IDLE) \ a node is gone, if its centre says so:
|
||||
@@ -310,6 +122,25 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
Q1: (PRINT-PORT) a! !
|
||||
jump (LINE)
|
||||
|
||||
\ ( -- ) A MESSAGE FOR ANOTHER NODE is passed on (MESH.md section 7): all
|
||||
\ of it, as it came, to the port that leads toward that node. With no way
|
||||
\ known it is let go, and (LOST) counts it.
|
||||
: (PASS-ON)
|
||||
(MSG)+3 a! @ if FRESH jump COUNT \ how many more nodes may pass it on;
|
||||
FRESH: drop 16 \ from a device, which set none: sixteen
|
||||
COUNT: -1 + if SPENT (MSG)+3 a! !
|
||||
(MSG) a! @ (PORT-FOR) if NOWAY
|
||||
dup (THERE) if NOONE drop
|
||||
(GATE) (MSG) a! 6 FOR @+ !b UNEXT \ its seven words
|
||||
(MSG)+6 a! @ 3 + 2/ 2/ if SENT
|
||||
-1 + TIB 2/ 2/ a! FOR @+ !b UNEXT \ and its text
|
||||
jump (IDLE)
|
||||
SENT: drop jump (IDLE)
|
||||
NOONE: drop \ its way is a port with nothing on it
|
||||
SPENT: \ it has been passed on as often as it may be
|
||||
NOWAY: drop (LOST) a! @ 1 + !
|
||||
(MSG)+1 a! @ (MSG) a! @ (MSG)+2 a! @ (TELL-OF) jump (IDLE) \ whoever waits on it is told (core.v4)
|
||||
|
||||
\ ( -- ) THE TEXT, INTERPRETED. It is in TIB with a zero after it and its
|
||||
\ length in SPAN, and the return stack is empty. It is interpreted with one
|
||||
\ return entry under it, this word's call of INTERPRET; whatever is on the
|
||||
@@ -338,8 +169,8 @@ header ROUTE
|
||||
|
||||
\ ( node port -- ) the node on the other end of the port, by its number;
|
||||
\ 0: none, or a device. Whoever wires the node tells it, and tells it again
|
||||
\ when the wiring changes. It is how a node knows who its centre is
|
||||
\ ((CENTRE)), from whom alone it believes a GONE.
|
||||
\ when the wiring changes. It is what lets one of two neighbours, and one
|
||||
\ only, wait to write to the other (core.v4, (GATE)).
|
||||
header NEIGHBOUR
|
||||
: NEIGHBOUR
|
||||
dup -if POS jump BAD
|
||||
@@ -367,18 +198,10 @@ header NO-ROUTE
|
||||
|
||||
\ ( about node type -- ) send the node a message of one word, `about`:
|
||||
\ a NACK is type 4, a GONE type 5 (MESH.md 7b). With no way to the node
|
||||
\ it is error 12. It is put with the messages waiting and goes from
|
||||
\ (IDLE), or from AWAIT, as how text ended does ((FINISH)): so telling a
|
||||
\ node that is stuck does not hold up the telling of the next. With no
|
||||
\ room at all it is offered here.
|
||||
\ it is error 12.
|
||||
: (SEND1)
|
||||
push dup (PORT-FOR) if NOWAY \ ( about node port ) R: type
|
||||
1 (MQ#) a! @ + -MQ-ROOM + -if FULL drop
|
||||
SWAP (MQ!) (ME) a! @ (MQ!) pop (MQ!) \ to, from, type
|
||||
17 (MQ!) 0 (MQ!) 0 (MQ!) 4 (MQ!) \ sixteen nodes may pass it on; four characters long
|
||||
0 SWAP - (MQ!) \ by that port, kept below zero: this node's own
|
||||
(MQ!) ; \ about
|
||||
FULL: drop (GATE) pop (HDR) 4 !b !b ;
|
||||
push dup (PORT-FOR) if NOWAY
|
||||
(GATE) !b pop (HDR) 4 !b !b ;
|
||||
NOWAY: drop drop drop pop drop NODE-ERROR b! 12 !b ;
|
||||
|
||||
\ ( n node -- ) tell the node that node n is gone. It acts on that only
|
||||
@@ -393,10 +216,10 @@ header NO-ROUTES
|
||||
|
||||
\ ( baddr u node port -- ) send the node u characters of text to
|
||||
\ interpret, by the port given, by its address. It is on its way when this
|
||||
\ returns. Its first word is offered until the neighbour takes it
|
||||
\ (core.v4, (GATE)); the rest is written, each word waiting for it.
|
||||
\ returns. The write waits for the neighbour to read, and is not begun
|
||||
\ until it may be (core.v4, (GATE)).
|
||||
: (SEND-ON)
|
||||
(GATE) \ to
|
||||
(GATE) !b \ to
|
||||
1 (HDR)
|
||||
dup !b \ length
|
||||
3 + 2/ 2/ if NONE -1 +
|
||||
@@ -486,38 +309,52 @@ header SEND-ON
|
||||
LEFT: drop NODE-ERROR b! 20 !b ;
|
||||
|
||||
\ ( node -- how ) WAIT FOR THE NODE'S WORD OF HOW TEXT ENDED: 0 QUIT,
|
||||
\ 1 completed, 2 an error: a message of type 3 for this node from that
|
||||
\ node. (AWAIT-FROM) holds the node while it waits, and 0 otherwise.
|
||||
\ 1 completed, 2 an error. This node is blocked reading its ports until a
|
||||
\ message of type 3 comes for it from that node; every other message that
|
||||
\ comes meanwhile is kept with the messages waiting. (AWAIT-FROM) holds the
|
||||
\ node while it waits, and 0 otherwise.
|
||||
\ THE WAIT ALSO ENDS, in an error, when the answer is not going to come
|
||||
\ (MESH.md section 7b): a NACK about that node, error 19, Message refused;
|
||||
\ or a GONE about it from this node's centre, error 20, Node gone. A NACK
|
||||
\ about another node is counted, and a GONE about another has its way
|
||||
\ forgotten, as they would be if this node were not waiting. A GONE is
|
||||
\ believed only if this node's centre sent it: (CENTRE).
|
||||
\ WHAT HAS COME ALREADY is looked through first, (A-QUEUE): the answer, a
|
||||
\ NACK or a GONE may have been taken in before the wait began, while this
|
||||
\ node was waiting to write, and is then with the messages waiting.
|
||||
\ AND A LINE FROM THE CONSOLE BREAKS IT (7b.6): text for this node from its
|
||||
\ console ends the wait in error 21, Interrupted, and is then done as
|
||||
\ usual. It is the one way to end a wait on a node that is alive and
|
||||
\ never answers, short of Hera's killing that node.
|
||||
\ WHILE IT WAITS THE NODE DOES WHAT (IDLE) DOES, but for beginning text:
|
||||
\ the messages waiting are looked through for what would end the wait,
|
||||
\ (A-QUEUE); what it has for other nodes and the refusals it owes are
|
||||
\ offered, and it sleeps in the wait (MESH.md 7c); what comes is taken in
|
||||
\ and kept, and it looks again. So what passes through a node is not
|
||||
\ kept back by its waiting, and what it sent before it began to wait --
|
||||
\ a GONE, say -- goes.
|
||||
\ console is kept, as any message is, the wait ends in error 21,
|
||||
\ Interrupted, and the text is then done as usual. It is the one way to
|
||||
\ end a wait on a node that is alive and never answers, short of Hera's
|
||||
\ killing that node.
|
||||
header AWAIT
|
||||
: AWAIT
|
||||
(AWAIT-FROM) a! !
|
||||
L: (A-QUEUE)
|
||||
(A-FOUND) a! @ if WAIT jump (A-END)
|
||||
(A-QUEUE)
|
||||
(A-FOUND) a! @ if WAIT jump (A-END)
|
||||
WAIT: drop
|
||||
(PAY-SET) (FWD-SET) if SET drop jump L \ one was let go: this node may have been told so
|
||||
SET: drop
|
||||
(OFFERS) if CAME -if CAME drop
|
||||
(GATE-PORT) a! @ (PORT) - (PAY-K) + a! @
|
||||
-if OWED drop (FWD-GO) jump L
|
||||
OWED: -1 + (PAY-GO) jump L
|
||||
CAME: drop jump L
|
||||
L: (PORT)+8 b! @b (PORT)+9 b! @b (PORT) + dup b! SWAP (TAKE-HDR)
|
||||
(MQ-HDR) a! @ (ME) a! @ xor if A1 drop jump KEEP
|
||||
A1: drop (MQ-HDR)+2 a! @ \ for this node: its type
|
||||
-1 + if TEXT -2 + if ANSWER -1 + if NACK -1 + if GONE drop jump KEEP
|
||||
TEXT: drop (A-CONSOLE) (MQ-HDR)+1 a! @ xor if BREAK drop jump KEEP \ text: from the console?
|
||||
BREAK: drop (TAKE-KEEP) 4 jump (A-END) \ kept, to be done next; and the wait is over
|
||||
ANSWER: drop (MQ-HDR)+1 a! @ (AWAIT-FROM) a! @ xor if A3 drop jump KEEP
|
||||
A3: drop (MQ-HDR)+6 a! @ -4 + if A4 drop jump KEEP
|
||||
A4: drop 0 (AWAIT-FROM) a! ! @b ; \ its one word
|
||||
NACK: drop (MQ-HDR)+6 a! @ -4 + if N4 drop jump KEEP
|
||||
N4: drop @b (AWAIT-FROM) a! @ xor if REFUSED \ about the node waited for?
|
||||
drop (REFUSED) a! @ 1 + ! jump L \ another: counted
|
||||
REFUSED: drop 2 jump (A-END)
|
||||
GONE: drop (MQ-HDR)+6 a! @ -4 + if G4 drop jump KEEP
|
||||
G4: drop @b \ the node that is gone
|
||||
(CENTRE) if NOC (MQ-HDR)+1 a! @ xor if CENTRE \ only its centre is believed
|
||||
drop drop jump L
|
||||
NOC: drop drop jump L
|
||||
CENTRE: drop dup NO-ROUTE
|
||||
(AWAIT-FROM) a! @ xor if LEFT drop jump L
|
||||
LEFT: drop 3 jump (A-END)
|
||||
KEEP: (TAKE-KEEP) jump L
|
||||
|
||||
\ ( w port -- ) write a cell to the port, by its number. It waits until
|
||||
\ the neighbour has taken it. This is how a node is sent a capsule of F18
|
||||
@@ -596,7 +433,7 @@ header ABORT
|
||||
\ counted, and the node goes back to waiting.
|
||||
: (RAISED)
|
||||
(QUIET) a! @ if SPEAK
|
||||
drop (OUT) (OUT^) a! ! (LOST) a! @ 1 + ! (MQ-MEND) jump (IDLE)
|
||||
drop (OUT) (OUT^) a! ! (LOST) a! @ 1 + ! jump (IDLE)
|
||||
SPEAK: drop
|
||||
NODE-ERROR b! @b
|
||||
-if POS drop 2 jump (DONE)
|
||||
|
||||
@@ -26,6 +26,7 @@ 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;
|
||||
int (*pending)(void *self); /* 1 if it has a word to give that has not been asked for; may be 0: it never says */
|
||||
} v4_device;
|
||||
|
||||
/* One end of a wire. */
|
||||
@@ -55,6 +56,7 @@ typedef struct {
|
||||
v4_place *place;
|
||||
unsigned capacity;
|
||||
v4_cell gone_error; /* see v4_fabric_gone_error; 0: none */
|
||||
v4_cell interrupt_error; /* see v4_fabric_interrupt_error; 0: none */
|
||||
} v4_fabric;
|
||||
|
||||
/* An empty fabric with room for `capacity` nodes, in `places`. */
|
||||
@@ -71,6 +73,19 @@ void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity);
|
||||
* is how a fabric begins, nothing is raised. */
|
||||
void v4_fabric_gone_error(v4_fabric *f, v4_cell code);
|
||||
|
||||
/* A NODE BLOCKED WRITING TO A NODE THAT DOES NOT READ. Of two neighbours
|
||||
* one may write to the other without looking (docs/v4.0.0/MESH.md 7a), and
|
||||
* is then blocked until the other reads; if the other never does, it is
|
||||
* blocked for ever, and if it is the node the console is on, nothing can be
|
||||
* typed to it again. So: when a node that can take an error is blocked
|
||||
* writing to another node, on the first word of a message (node.h,
|
||||
* v4_node_words_since_look: nothing of the message has gone), and a device
|
||||
* on one of its own ports says it has something pending for it, the error
|
||||
* `code` is raised on the node: it gives up that message and goes on, to
|
||||
* read what the device has. Only a device with a `pending` function can
|
||||
* do this. With `code` 0, which is how a fabric begins, it never happens. */
|
||||
void v4_fabric_interrupt_error(v4_fabric *f, v4_cell code);
|
||||
|
||||
/* 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);
|
||||
|
||||
@@ -65,6 +65,11 @@ typedef struct {
|
||||
* port". */
|
||||
#define V4_ERROR_NO_ONE 18
|
||||
|
||||
/* The nucleus's error for a node let go of a message it was blocked on the
|
||||
* first word of, because its console has a line for it (fabric.h,
|
||||
* v4_fabric_interrupt_error): "Interrupted". */
|
||||
#define V4_ERROR_INTERRUPTED 21
|
||||
|
||||
/* The description of the nucleus built from v4/capsule (the generated file
|
||||
* defines it). */
|
||||
extern const v4_image v4_capsule_image;
|
||||
|
||||
+8
-50
@@ -43,9 +43,6 @@
|
||||
#define V4_PORTS 8u
|
||||
#endif
|
||||
#define V4_PORT_ANY V4_PORTS /* not a port: "whichever port has a neighbour writing" */
|
||||
#define V4_PORT_OFFER (V4_PORTS + 4u) /* not a port: the offer on port 0; the offer on port k is k further (the wait, below) */
|
||||
#define V4_PORT_WAIT (2u * V4_PORTS + 4u) /* not a port: the wait */
|
||||
#define V4_PORT_WAIT1 (2u * V4_PORTS + 5u) /* not a port: the wait for an offer only */
|
||||
|
||||
#ifndef V4_NODE_WORDS
|
||||
#define V4_NODE_WORDS 1024u
|
||||
@@ -114,10 +111,7 @@ typedef struct {
|
||||
unsigned last_from; /* the port the last read from "any" took its word from */
|
||||
unsigned writers; /* bit k: the neighbour on port k is blocked writing to this node */
|
||||
unsigned readers; /* bit k: the neighbour on port k is blocked reading from this node */
|
||||
unsigned offers; /* bit k: a word is offered on port k (the wait: docs/v4.0.0/MESH.md 7c) */
|
||||
v4_cell offer[V4_PORTS]; /* the words offered */
|
||||
int waiting; /* non-zero: the read it is blocked in is the wait */
|
||||
int offer_asked; /* non-zero: `asking` stands for the offer on `ask_port` (v4_node_offer_as_write) */
|
||||
unsigned since_look; /* words taken from this node since it last fetched either of those */
|
||||
|
||||
/* DSTACK-DEPTH and RSTACK-DEPTH (D-16). See v4_node_stack_regs_attach. */
|
||||
v4_cell dstack_reg; /* its word address, or -1 */
|
||||
@@ -225,6 +219,13 @@ 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);
|
||||
|
||||
/* 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
|
||||
* this at 0 is blocked on the first word of the message: nothing of it has
|
||||
* gone. */
|
||||
unsigned v4_node_words_since_look(const v4_node *n);
|
||||
|
||||
/* THERE IS NO ONE ON THE PORT the node is blocked at -- writing to it, or
|
||||
* reading from that one port -- and there will not be. A node that can
|
||||
* take an error (it has an error register and a fault table: node.h,
|
||||
@@ -235,49 +236,6 @@ void v4_node_port_served(v4_node *n);
|
||||
* that is not blocked at a port, or is reading "any port". */
|
||||
int v4_node_port_gone(v4_node *n, v4_cell code);
|
||||
|
||||
/* THE WAIT (docs/v4.0.0/MESH.md 7c). After the addresses above come
|
||||
* base + V4_PORT_OFFER + k the offer on port k: a store here is the word
|
||||
* the node offers to write to that port. It is
|
||||
* remembered and waits for nothing.
|
||||
* base + V4_PORT_WAIT the wait: a fetch here blocks the node until a
|
||||
* word comes for it on any port, or one of its
|
||||
* offers is taken, whichever is first.
|
||||
* base + V4_PORT_WAIT1 the wait for an offer only: as the wait, but
|
||||
* a word that comes for the node does not end
|
||||
* it, and a writer to the node is not served.
|
||||
* It is how the words of a message after the
|
||||
* first are written by a node that must not be
|
||||
* faulted if its reader is removed.
|
||||
* A store to the wait withdraws every offer and waits for nothing.
|
||||
* A word that came is fetched as from "any port", and "which port" says
|
||||
* where from. When an offer was taken the fetch gives 0 and "which port"
|
||||
* gives V4_PORTS + k: the offer on port k. When there turns out to be
|
||||
* nothing on a port it has an offer on, the fetch gives 0 and "which
|
||||
* port" gives 2 * V4_PORTS + k. Whichever, every offer the node had is
|
||||
* withdrawn. Whoever connects the ports does the handing over
|
||||
* (fabric.h); a node in the wait is blocked reading "any port" as far as a
|
||||
* writer to it can tell.
|
||||
*
|
||||
* 1 if the node is blocked in the wait with nothing given yet. */
|
||||
int v4_node_in_wait(const v4_node *n);
|
||||
|
||||
/* The offer on port k has been taken: the node is unblocked and will fetch
|
||||
* 0, from V4_PORTS + k. 0 if it is not in the wait or has no offer there. */
|
||||
int v4_node_offer_taken(v4_node *n, unsigned k);
|
||||
|
||||
/* There is nothing on port k, where the node has an offer: a node in the
|
||||
* wait that can take an error (v4_node_port_gone says which can) is
|
||||
* unblocked and will fetch 0, from 2 * V4_PORTS + k. No error is raised:
|
||||
* what the node does about it is its own to say. 0 if it was not done. */
|
||||
int v4_node_offer_gone(v4_node *n, unsigned k);
|
||||
|
||||
/* For a host with one node, whose devices always take what is written: if
|
||||
* the node is in the wait with an offer, it becomes a blocked write of the
|
||||
* word offered on the lowest such port -- asking, request, ask_port -- and
|
||||
* v4_node_port_served then completes the wait as v4_node_offer_taken would.
|
||||
* 1 if it did. */
|
||||
int v4_node_offer_as_write(v4_node *n);
|
||||
|
||||
/* The neighbour on `port` writes `value`: the node, blocked reading that
|
||||
* port or "any", is unblocked and will fetch it. */
|
||||
void v4_node_port_give(v4_node *n, unsigned port, v4_cell value);
|
||||
|
||||
+21
-37
@@ -18,6 +18,7 @@ void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity)
|
||||
f->place = places;
|
||||
f->capacity = capacity;
|
||||
f->gone_error = 0;
|
||||
f->interrupt_error = 0;
|
||||
for (i = 0; i < capacity; i++) clear_place(&places[i]);
|
||||
}
|
||||
|
||||
@@ -26,6 +27,10 @@ void v4_fabric_gone_error(v4_fabric *f, v4_cell code)
|
||||
f->gone_error = code;
|
||||
}
|
||||
|
||||
void v4_fabric_interrupt_error(v4_fabric *f, v4_cell code)
|
||||
{
|
||||
f->interrupt_error = code;
|
||||
}
|
||||
|
||||
void v4_fabric_grow(v4_fabric *f, v4_place *places, unsigned capacity)
|
||||
{
|
||||
@@ -147,15 +152,11 @@ static unsigned hand_over_write(v4_fabric *f, unsigned a)
|
||||
static unsigned hand_over_read(v4_fabric *f, unsigned a)
|
||||
{
|
||||
v4_node *n = &f->place[a].node->n;
|
||||
unsigned first;
|
||||
unsigned last;
|
||||
unsigned first = n->read_port == V4_PORT_ANY ? 0 : n->read_port;
|
||||
unsigned last = n->read_port == V4_PORT_ANY ? V4_PORTS - 1 : n->read_port;
|
||||
unsigned k;
|
||||
v4_cell value;
|
||||
|
||||
if (n->read_port > V4_PORT_ANY) return 0; /* the wait for an offer only: it reads no port */
|
||||
first = n->read_port == V4_PORT_ANY ? 0 : n->read_port;
|
||||
last = n->read_port == V4_PORT_ANY ? V4_PORTS - 1 : n->read_port;
|
||||
|
||||
for (k = first; k <= last; k++) {
|
||||
const v4_wire *w = &f->place[a].wire[k];
|
||||
if (w->kind == V4_WIRE_DEVICE && w->device->give && w->device->give(w->device->self, &value)) {
|
||||
@@ -208,35 +209,23 @@ 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);
|
||||
|
||||
/* THE WAIT (docs/v4.0.0/MESH.md 7c). A node in the wait that was given a
|
||||
* word above is no longer in it: a word for a node comes before its own
|
||||
* offer. Of the rest, each offer is taken by whoever is ready for it: a
|
||||
* device that takes, or a node blocked reading that port or any -- which
|
||||
* a node in the wait is, so of two that offer to each other the one at
|
||||
* the lower place writes and the other's offers are withdrawn. */
|
||||
for (i = 0; i < f->capacity; i++) {
|
||||
v4_node *n;
|
||||
unsigned k;
|
||||
if (!awake(f, i)) continue;
|
||||
n = &f->place[i].node->n;
|
||||
if (!v4_node_in_wait(n)) continue;
|
||||
for (k = 0; k < V4_PORTS; k++) {
|
||||
const v4_wire *w = &f->place[i].wire[k];
|
||||
if (!(n->offers & (1u << k))) continue;
|
||||
if (w->kind == V4_WIRE_DEVICE) {
|
||||
if (w->device->take && w->device->take(w->device->self, n->offer[k])) { (void)v4_node_offer_taken(n, k); done++; break; }
|
||||
} else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) {
|
||||
v4_node *r = &f->place[w->node].node->n;
|
||||
if (r->reading && !r->given && (r->read_port == w->port || r->read_port == V4_PORT_ANY)) {
|
||||
v4_cell word = n->offer[k];
|
||||
(void)v4_node_offer_taken(n, k);
|
||||
v4_node_port_give(r, w->port, word);
|
||||
done++;
|
||||
/* a node blocked on the first word of a message to a node that is not
|
||||
* reading, while a device of its own has something for it */
|
||||
if (f->interrupt_error != 0)
|
||||
for (i = 0; i < f->capacity; i++) {
|
||||
v4_node *n;
|
||||
unsigned k;
|
||||
if (!awake(f, i)) continue;
|
||||
n = &f->place[i].node->n;
|
||||
if (!n->asking || f->place[i].wire[n->ask_port].kind != V4_WIRE_NODE || v4_node_words_since_look(n) != 0) continue;
|
||||
for (k = 0; k < V4_PORTS; k++) {
|
||||
const v4_wire *w = &f->place[i].wire[k];
|
||||
if (w->kind == V4_WIRE_DEVICE && w->device->pending && w->device->pending(w->device->self)) {
|
||||
done += (unsigned)v4_node_port_gone(n, f->interrupt_error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* a node blocked at one port that nothing is wired to */
|
||||
if (f->gone_error != 0)
|
||||
@@ -245,13 +234,8 @@ unsigned v4_fabric_step(v4_fabric *f)
|
||||
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)
|
||||
|| (n->reading && !n->given && n->read_port < V4_PORTS && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE))
|
||||
|| (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);
|
||||
else if (v4_node_in_wait(n)) { /* an offer on a port with nothing on it */
|
||||
unsigned k;
|
||||
for (k = 0; k < V4_PORTS; k++)
|
||||
if ((n->offers & (1u << k)) && f->place[i].wire[k].kind == V4_WIRE_NONE) { done += (unsigned)v4_node_offer_gone(n, k); break; }
|
||||
}
|
||||
}
|
||||
|
||||
return done;
|
||||
|
||||
+14
-70
@@ -93,14 +93,7 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value)
|
||||
n->asking = 1;
|
||||
return;
|
||||
}
|
||||
if (port >= (int)V4_PORT_OFFER && port < (int)V4_PORT_WAIT) { /* an offer: remembered until the wait */
|
||||
unsigned k = (unsigned)port - V4_PORT_OFFER;
|
||||
n->offer[k] = value;
|
||||
n->offers |= 1u << k;
|
||||
return;
|
||||
}
|
||||
if (port == (int)V4_PORT_WAIT) { n->offers = 0; return; } /* a store to the wait: every offer is withdrawn */
|
||||
if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any", "which port" and the rest are not written to */
|
||||
if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any" and "which port" are not written to */
|
||||
}
|
||||
/* The stack registers: a store empties the stack. -1 when not attached. */
|
||||
if (addr == n->dstack_reg) { v4_dstack_clear(&n->ds); return; }
|
||||
@@ -127,16 +120,8 @@ v4_cell v4_node_fetch(v4_node *n, v4_cell addr)
|
||||
{
|
||||
int port = v4_node_port_index(n, addr);
|
||||
if (port == (int)V4_PORTS + 1) return (v4_cell)n->last_from;
|
||||
if (port == (int)V4_PORTS + 2) return (v4_cell)n->writers;
|
||||
if (port == (int)V4_PORTS + 3) return (v4_cell)n->readers;
|
||||
if (port == (int)V4_PORT_WAIT || port == (int)V4_PORT_WAIT1) {
|
||||
/* the wait: the word that came, or 0 for an offer taken */
|
||||
if (!n->given) return 0;
|
||||
n->given = 0;
|
||||
n->last_from = n->given_port;
|
||||
return n->given_value;
|
||||
}
|
||||
if (port >= (int)V4_PORT_OFFER) return 0; /* an offer is not read back */
|
||||
if (port == (int)V4_PORTS + 2) { n->since_look = 0; return (v4_cell)n->writers; }
|
||||
if (port == (int)V4_PORTS + 3) { n->since_look = 0; return (v4_cell)n->readers; }
|
||||
if (port >= 0) {
|
||||
/* the word its neighbour wrote; v4_node_read_ready has said it is there */
|
||||
if (!n->given) return 0;
|
||||
@@ -194,9 +179,7 @@ void v4_node_port_attach(v4_node *n, v4_cell base)
|
||||
n->last_from = 0;
|
||||
n->writers = 0;
|
||||
n->readers = 0;
|
||||
n->offers = 0;
|
||||
n->waiting = 0;
|
||||
n->offer_asked = 0;
|
||||
n->since_look = 0;
|
||||
}
|
||||
|
||||
void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
|
||||
@@ -207,19 +190,17 @@ void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
|
||||
|
||||
int v4_node_port_index(const v4_node *n, v4_cell addr)
|
||||
{
|
||||
if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORT_WAIT1) return -1;
|
||||
if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORTS + 3) return -1;
|
||||
return (int)(addr - n->port);
|
||||
}
|
||||
|
||||
int v4_node_read_ready(v4_node *n, v4_cell addr)
|
||||
{
|
||||
int port = v4_node_port_index(n, addr);
|
||||
if (port < 0 || (port > (int)V4_PORT_ANY && port != (int)V4_PORT_WAIT && port != (int)V4_PORT_WAIT1)) return 1;
|
||||
if (n->given && (port >= (int)V4_PORT_ANY || (unsigned)port == n->given_port)) return 1;
|
||||
if (port < 0 || port > (int)V4_PORT_ANY) return 1;
|
||||
if (n->given && (port == (int)V4_PORT_ANY || (unsigned)port == n->given_port)) return 1;
|
||||
n->reading = 1;
|
||||
n->waiting = port >= (int)V4_PORT_WAIT; /* the wait is a read of any port, with offers standing; */
|
||||
n->read_port = port == (int)V4_PORT_WAIT ? V4_PORT_ANY /* the wait for an offer only reads no port at all */
|
||||
: (unsigned)port;
|
||||
n->read_port = (unsigned)port;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -229,39 +210,6 @@ void v4_node_port_give(v4_node *n, unsigned port, v4_cell value)
|
||||
n->given_value = value;
|
||||
n->given_port = port;
|
||||
n->reading = 0;
|
||||
if (n->waiting) { n->waiting = 0; n->offers = 0; } /* the wait is over: every offer is withdrawn */
|
||||
}
|
||||
|
||||
int v4_node_in_wait(const v4_node *n)
|
||||
{
|
||||
return n->waiting && n->reading && !n->given;
|
||||
}
|
||||
|
||||
int v4_node_offer_taken(v4_node *n, unsigned k)
|
||||
{
|
||||
if (!v4_node_in_wait(n) || k >= V4_PORTS || !(n->offers & (1u << k))) return 0;
|
||||
v4_node_port_give(n, V4_PORTS + k, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int v4_node_offer_gone(v4_node *n, unsigned k)
|
||||
{
|
||||
if (n->error_reg < 0 || n->fault_vector < 0 || n->stopped) return 0;
|
||||
if (!v4_node_in_wait(n) || k >= V4_PORTS || !(n->offers & (1u << k))) return 0;
|
||||
v4_node_port_give(n, 2u * V4_PORTS + k, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int v4_node_offer_as_write(v4_node *n)
|
||||
{
|
||||
unsigned k;
|
||||
if (!v4_node_in_wait(n) || n->offers == 0 || n->asking) return 0;
|
||||
for (k = 0; !(n->offers & (1u << k)); k++) { }
|
||||
n->request = n->offer[k];
|
||||
n->ask_port = k;
|
||||
n->asking = 1;
|
||||
n->offer_asked = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void v4_node_born(v4_node *n, v4_cell base)
|
||||
@@ -274,23 +222,19 @@ void v4_node_born(v4_node *n, v4_cell base)
|
||||
void v4_node_port_served(v4_node *n)
|
||||
{
|
||||
n->asking = 0;
|
||||
if (n->offer_asked) { /* what was served was an offer: the wait is over */
|
||||
n->offer_asked = 0;
|
||||
v4_node_port_give(n, V4_PORTS + n->ask_port, 0);
|
||||
}
|
||||
n->since_look++;
|
||||
}
|
||||
|
||||
unsigned v4_node_words_since_look(const v4_node *n)
|
||||
{
|
||||
return n->since_look;
|
||||
}
|
||||
|
||||
int v4_node_port_gone(v4_node *n, v4_cell code)
|
||||
{
|
||||
if (code == 0 || n->error_reg < 0 || n->fault_vector < 0 || n->stopped) return 0;
|
||||
if (n->asking && n->offer_asked) { /* what it wrote was an offer: it is told, not faulted */
|
||||
n->asking = 0; n->offer_asked = 0;
|
||||
v4_node_port_give(n, 2u * V4_PORTS + n->ask_port, 0);
|
||||
return 1;
|
||||
}
|
||||
if (n->asking) n->asking = 0;
|
||||
else if (n->reading && !n->given && n->read_port < V4_PORTS) n->reading = 0;
|
||||
else if (n->reading && !n->given && n->read_port != V4_PORT_ANY) n->reading = 0;
|
||||
else return 0;
|
||||
v4_node_store(n, n->error_reg, code); /* raises it: P is the node's handler now */
|
||||
return 1;
|
||||
|
||||
@@ -107,7 +107,6 @@ static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep
|
||||
for (;;) {
|
||||
(void)v4_exec_step_word(n, b->es, b->h);
|
||||
if (n->stopped) return V4_BOOT_LINE_STOPPED;
|
||||
(void)v4_node_offer_as_write(n); /* in the wait (MESH.md 7c): what it offers is a write, here where whatever is on a port takes at once */
|
||||
|
||||
if (n->asking && n->ask_port == KERNEL_PORT) { /* a request: the kernel's turn */
|
||||
if (v4_blocks_serve(&boot_blocks, n, n->request)) { /* a block, as for any node */
|
||||
|
||||
+1
-19
@@ -73,7 +73,7 @@
|
||||
#define OWED (BUF0_W - 112) /* those: for each, the node it is owed to and the node the refused message was for */
|
||||
#define ACL_HOOK (BUF0_W - 6) /* the xt of the access control recheck word, or 0 */
|
||||
#define LINE_STATUS (BUF0_W - 9) /* how the last line ended: 0 QUIT, 1 completed, 2 an error */
|
||||
#define PORT (BUF0_W - 144) /* the node's ports (node.h): V4_PORTS of them, then "any port", "which port", who writes, who reads, an offer for each port, and the two waits: 2 * V4_PORTS + 6 cells, between the output buffer and A_FOUND. Port 0 is where its requests go. */
|
||||
#define PORT (BUF0_W - 32) /* the node's ports (node.h): V4_PORTS of them, then "any port" and "which port". Port 0 is where its requests go. */
|
||||
#define CONSOLE (BUF0_W - 16) /* the node what this one prints is sent to; 0: whoever sent the text being served */
|
||||
#define ROUTE_COUNT (BUF0_W - 17) /* how many entries the table of ways holds */
|
||||
#define ROUTE_DEFAULT (BUF0_W - 18) /* the port address for a node not in the table, or 0: there is none */
|
||||
@@ -87,12 +87,6 @@
|
||||
#define MQ_TAIL (BUF0_W - 36) /* where the next will go, */
|
||||
#define MQ_COUNT (BUF0_W - 37) /* and how many cells they take */
|
||||
#define AWAIT_FROM (BUF0_W - 39) /* the node whose word of how text ended is waited for */
|
||||
#define W_GONE (BUF0_W - 122) /* not 0: the port a message was being written to has nothing on it; its other words are let go */
|
||||
#define TAKING (BUF0_W - 121) /* not 0: a message is being taken in, and is only partly with the messages waiting; */
|
||||
#define T_TAIL (BUF0_W - 120) /* where they ended before it, */
|
||||
#define T_COUNT (BUF0_W - 119) /* and how many cells they took */
|
||||
#define GATE_WORD (BUF0_W - 32) /* the first word of a message that is being offered */
|
||||
#define PAY_K (BUF0_W - 31) /* for each port, which refusal owed is offered there, counted from 1; 0: none. V4_PORTS cells */
|
||||
#define GATE_PORT (BUF0_W - 38) /* the port address a message is about to be begun on */
|
||||
#define NEAR (BUF0_W - 64) /* for each port, the number of the node on the other end; 0: not told */
|
||||
#define MQ_HDR (BUF0_W - 56) /* a message being taken in: its seven words and the port it came on */
|
||||
@@ -130,8 +124,6 @@ typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1];
|
||||
#endif
|
||||
/* the messages waiting are above the dictionary */
|
||||
typedef char host_map_queue_fits[(MQ_W >= DICT_END_W) ? 1 : -1];
|
||||
/* the ports, with the offers and the wait after them, end below the cells that follow */
|
||||
typedef char host_map_port_block_fits[(PORT + 2 * (v4_cell)V4_PORTS + 6 <= W_GONE && PAY_K + (v4_cell)V4_PORTS <= PRINT_TO) ? 1 : -1];
|
||||
|
||||
/* How many slots, from slot 0, a branch may sit in on a node this size: those
|
||||
* whose address field reaches every word of it. */
|
||||
@@ -204,16 +196,6 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
v4_text_constant(tx, "-PORTS", -(v4_cell)V4_PORTS);
|
||||
v4_text_constant(tx, "(NEAR)", NEAR);
|
||||
v4_text_constant(tx, "(GATE-PORT)", GATE_PORT);
|
||||
v4_text_constant(tx, "(GATE-WORD)", GATE_WORD);
|
||||
v4_text_constant(tx, "(PAY-K)", PAY_K);
|
||||
v4_text_constant(tx, "PORTS-1", (v4_cell)V4_PORTS - 1);
|
||||
v4_text_constant(tx, "(OFFER)", PORT + (v4_cell)V4_PORT_OFFER);
|
||||
v4_text_constant(tx, "(WAIT)", PORT + (v4_cell)V4_PORT_WAIT);
|
||||
v4_text_constant(tx, "(WAIT1)", PORT + (v4_cell)V4_PORT_WAIT1);
|
||||
v4_text_constant(tx, "(W-GONE)", W_GONE);
|
||||
v4_text_constant(tx, "(TAKING)", TAKING);
|
||||
v4_text_constant(tx, "(T-TAIL)", T_TAIL);
|
||||
v4_text_constant(tx, "(T-COUNT)", T_COUNT);
|
||||
v4_text_constant(tx, "(AWAIT-FROM)", AWAIT_FROM);
|
||||
v4_text_constant(tx, "(MQ)", MQ_W);
|
||||
v4_text_constant(tx, "(MQ-END)", MQ_W + MQ_CELLS);
|
||||
|
||||
+52
-174
@@ -15,7 +15,7 @@ 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)
|
||||
|
||||
/* The memory map is open (D-4); the test chooses where the ports are. */
|
||||
#define PB ((v4_cell)(V4_NODE_WORDS - 32u)) /* port 0: the ports and what follows them end below the top of memory */
|
||||
#define PB ((v4_cell)(V4_NODE_WORDS - 16u)) /* port 0 */
|
||||
#define ANY (PB + (v4_cell)V4_PORT_ANY)
|
||||
#define ORG ((v4_cell)16) /* where a test's programme starts */
|
||||
#define OUT ((v4_cell)900) /* where it leaves what it found */
|
||||
@@ -45,36 +45,9 @@ static unsigned loaded(unsigned k)
|
||||
/* the programme ends by waiting at a port nothing is wired to */
|
||||
static void wait_for_ever(void) { LIT(PB + 7); O(BANG_B); O(FETCH_B); (void)v4_asm_label(&as); } /* and the last word is written out */
|
||||
|
||||
/* ---- the wait (MESH.md 7c) ---- */
|
||||
static void fresh(void)
|
||||
{
|
||||
unsigned k;
|
||||
v4_fabric_init(&f, places, 4);
|
||||
for (k = 0; k < 4; k++) v4_node_reset(&pool[k].n);
|
||||
}
|
||||
/* offer `word` on port k */
|
||||
static void offer(unsigned k, v4_cell word) { LIT(word); LIT(PB + (v4_cell)V4_PORT_OFFER + (v4_cell)k); O(BANG_A); O(STORE_A); }
|
||||
/* the wait: what it gives goes to `at`, and which port to `at + 1` */
|
||||
static void wait_into(v4_cell at)
|
||||
{
|
||||
LIT(PB + (v4_cell)V4_PORT_WAIT); O(BANG_B); O(FETCH_B); LIT(at); O(BANG_A); O(STORE_A);
|
||||
LIT(PB + (v4_cell)V4_PORTS + 1); O(BANG_B); O(FETCH_B); LIT(at + 1); O(BANG_A); O(STORE_A);
|
||||
}
|
||||
/* the wait for an offer only */
|
||||
static void wait1_into(v4_cell at)
|
||||
{
|
||||
LIT(PB + (v4_cell)V4_PORT_WAIT1); O(BANG_B); O(FETCH_B); LIT(at); O(BANG_A); O(STORE_A);
|
||||
LIT(PB + (v4_cell)V4_PORTS + 1); O(BANG_B); O(FETCH_B); LIT(at + 1); O(BANG_A); O(STORE_A);
|
||||
}
|
||||
static void done_mark(void) { LIT(1); LIT(OUT + 2); O(BANG_A); O(STORE_A); }
|
||||
static void count_for_ever(void)
|
||||
{
|
||||
v4_cell again;
|
||||
LIT(OUT); O(BANG_A);
|
||||
again = v4_asm_label(&as);
|
||||
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
|
||||
v4_asm_branch(&as, V4_OP_JUMP, again);
|
||||
}
|
||||
/* a device that says whether it has something pending */
|
||||
static int *dev_has;
|
||||
static int device_has(void *self) { (void)self; return *dev_has; }
|
||||
|
||||
/* Let time pass until nothing more can happen, or `max` steps. */
|
||||
static unsigned settle(unsigned max)
|
||||
@@ -394,155 +367,60 @@ int main(void)
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.request == 5 && ND(c)->n.reading && ND(c)->n.mem[OUT] == 0, "looking takes nothing and gives nothing: both wait as they did");
|
||||
CHECK(!ND(b)->n.stopped && ND(b)->n.reading, "and the one that looked did not wait to look");
|
||||
|
||||
/* ---- the wait (MESH.md 7c): a node offers a word and sleeps ---- */
|
||||
/* ---- a node blocked on the first word of a message, while a device of its own has something for it ---- */
|
||||
{
|
||||
static int has; /* what the device says it has pending */
|
||||
static stream none;
|
||||
static v4_device sink;
|
||||
v4_uheat_t clock;
|
||||
sink.take = stream_take; sink.give = 0; sink.self = &none;
|
||||
|
||||
/* taken by a reader */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to one that reads");
|
||||
(void)settle(300);
|
||||
CHECK(ND(b)->n.mem[OUT] == 7, "the reader has the word offered: %ld", (long)ND(b)->n.mem[OUT]);
|
||||
CHECK(ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0,
|
||||
"the one that offered is told its offer on port 0 was taken, and has none left: %ld from %ld", (long)ND(a)->n.mem[OUT], (long)ND(a)->n.mem[OUT + 1]);
|
||||
|
||||
/* taken by a device */
|
||||
fresh(); none.took_n = 0;
|
||||
a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
CHECK(v4_fabric_wire_device(&f, a, 0, &sink), "the same, wired to a device that takes what is written");
|
||||
(void)settle(300);
|
||||
CHECK(none.took_n == 1 && none.took[0] == 7 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1, "the device has the word, and the node is told");
|
||||
|
||||
/* a neighbour that never reads */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); count_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to one that never reads");
|
||||
for (i = 0; i < 200; i++) (void)v4_fabric_step(&f);
|
||||
clock = ND(a)->es.anticlock;
|
||||
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->es.anticlock == clock && ND(a)->n.mem[OUT + 2] == 0, "it is in the wait, and executes nothing there");
|
||||
CHECK((ND(b)->n.readers & (1u << 3)) != 0, "and shows to its neighbour as waiting to read");
|
||||
|
||||
/* withdrawn by a word that comes for it */
|
||||
c = loaded(2); LIT(PB + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, c, 0, a, 2), "a third node writes to it");
|
||||
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.mem[OUT] == 9 && ND(a)->n.mem[OUT + 1] == 2 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0,
|
||||
"it gets that word, from that port, and its offer is withdrawn: %ld from %ld", (long)ND(a)->n.mem[OUT], (long)ND(a)->n.mem[OUT + 1]);
|
||||
CHECK(!ND(b)->n.given && !ND(c)->n.asking, "the node offered to was given nothing, and the writer was served");
|
||||
|
||||
/* an offer that could be taken in the step a word comes: the word comes first */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); wait_into(OUT); offer(0, 7); wait_into(OUT + 3); done_mark(); wait_for_ever();
|
||||
b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
|
||||
c = loaded(2); LIT(PB + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, c, 0, a, 2), "one offers to a reader while a third writes to it");
|
||||
v4_fabric_sleep(&f, a);
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(b)->n.reading && ND(c)->n.asking, "the reader and the writer are both blocked");
|
||||
v4_fabric_sleep(&f, b); v4_fabric_sleep(&f, c); v4_fabric_wake(&f, a);
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&ND(a)->n), "and then the one that offers is in the wait");
|
||||
v4_fabric_wake(&f, b); v4_fabric_wake(&f, c);
|
||||
static v4_device dev;
|
||||
v4_fabric_init(&f, places, 4);
|
||||
for (i = 0; i < 3; i++) v4_node_reset(&pool[i].n);
|
||||
a = loaded(0); /* looks about, then writes two words to its port 0 */
|
||||
LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); O(DROP);
|
||||
LIT(PB + 0); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); wait_for_ever();
|
||||
b = loaded(1); /* never reads */
|
||||
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 that looks first, wired to a node that never reads");
|
||||
dev.take = stream_take; dev.give = 0; dev.self = &none; dev.pending = device_has; has = 1; dev_has = &has;
|
||||
CHECK(v4_fabric_wire_device(&f, a, 5, &dev), "and to a device, which has something for it");
|
||||
for (i = 0; i < 40; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.request == 1 && v4_node_words_since_look(&ND(a)->n) == 0, "the writer is blocked on its first word, and waits: the fabric has no error for it");
|
||||
v4_fabric_interrupt_error(&f, 21);
|
||||
for (i = 0; i < 40; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "nor is a bare node let go when it has: it cannot take an error");
|
||||
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
||||
v4_node_fault_attach(&ND(a)->n, 900);
|
||||
has = 0;
|
||||
for (i = 0; i < 40; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "a node that can take one waits all the same while the device has nothing for it");
|
||||
has = 1;
|
||||
(void)v4_fabric_step(&f);
|
||||
CHECK(!v4_node_in_wait(&ND(a)->n) && !ND(c)->n.asking && ND(b)->n.reading && !ND(b)->n.given, "in one step: the word for it comes first, and the reader is given nothing");
|
||||
(void)settle(300);
|
||||
CHECK(ND(a)->n.mem[OUT] == 9 && ND(a)->n.mem[OUT + 1] == 2 && ND(a)->n.mem[OUT + 3] == 0 && ND(a)->n.mem[OUT + 4] == (v4_cell)V4_PORTS + 0 && ND(b)->n.mem[OUT] == 7,
|
||||
"it offers again, and the reader gets the word once");
|
||||
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 21 && ND(a)->n.mem[OUT + 20] == 21,
|
||||
"when it has, the writer is let go and the error raised on it");
|
||||
|
||||
/* two nodes each offering to the other */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 5); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); offer(3, 6); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "two nodes each offer to the other");
|
||||
(void)settle(300);
|
||||
CHECK(ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0, "the one at the lower place has its offer taken");
|
||||
CHECK(ND(b)->n.mem[OUT] == 5 && ND(b)->n.mem[OUT + 1] == 3, "the other gets the word, from the port it came by");
|
||||
CHECK(ND(a)->n.offers == 0 && ND(b)->n.offers == 0 && ND(a)->n.mem[OUT + 2] == 1 && ND(b)->n.mem[OUT + 2] == 1, "and neither has an offer left");
|
||||
|
||||
/* three in a ring, each offering to the next */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 11); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); offer(0, 12); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
c = loaded(2); offer(0, 13); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 1) && v4_fabric_wire(&f, b, 0, c, 1) && v4_fabric_wire(&f, c, 0, a, 1), "three nodes in a ring, each offering to the next");
|
||||
CHECK(settle(1000) < 1000, "they come to rest");
|
||||
CHECK(ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(b)->n.mem[OUT] == 11 && ND(b)->n.mem[OUT + 1] == 1, "the first's offer was taken by the second");
|
||||
CHECK(v4_node_in_wait(&ND(c)->n) && ND(c)->n.offers == 1u && ND(c)->n.mem[OUT + 2] == 0 && !ND(a)->n.given, "the third still offers to the first, which is not reading: nothing was handed over twice");
|
||||
|
||||
/* a neighbour that is asleep */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to a reader");
|
||||
v4_fabric_sleep(&f, a);
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
v4_fabric_sleep(&f, b); v4_fabric_wake(&f, a);
|
||||
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&ND(a)->n) && ND(b)->n.reading && !ND(b)->n.given, "while the reader is asleep the offer stands");
|
||||
v4_fabric_wake(&f, b);
|
||||
(void)settle(300);
|
||||
CHECK(ND(b)->n.mem[OUT] == 7 && ND(a)->n.mem[OUT + 2] == 1, "woken, it takes it");
|
||||
|
||||
/* a port with nothing on it */
|
||||
fresh();
|
||||
a = loaded(0); offer(5, 7); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire_device(&f, a, 7, &sink), "a node that offers on a port nothing is wired to (the port it ends by reading has a device that gives nothing)");
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
v4_fabric_gone_error(&f, 18);
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.faults == 0, "a bare node waits, though the fabric has an error for it");
|
||||
/* once the first word has gone, it is not let go: the message must be finished */
|
||||
v4_fabric_init(&f, places, 4);
|
||||
for (i = 0; i < 3; i++) v4_node_reset(&pool[i].n);
|
||||
a = loaded(0);
|
||||
LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); O(DROP);
|
||||
LIT(PB + 0); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); wait_for_ever();
|
||||
b = loaded(1); /* reads one word, and no more */
|
||||
LIT(PB + 3); O(BANG_B); O(FETCH_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) && v4_fabric_wire_device(&f, a, 5, &dev), "the same, to a node that reads one word and no more");
|
||||
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
||||
v4_node_fault_attach(&ND(a)->n, 900);
|
||||
for (i = 0; i < 100 && ND(a)->n.mem[OUT + 2] == 0; i++) (void)v4_fabric_step(&f); /* until it has noted what it was told */
|
||||
CHECK(!v4_node_in_wait(&ND(a)->n) && ND(a)->n.faults == 0 && ND(a)->n.offers == 0 && ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == 2 * (v4_cell)V4_PORTS + 5 && ND(a)->n.mem[OUT + 2] == 1,
|
||||
"one that can take an error is woken and told there is nothing on port 5: no error is raised, and its offer is withdrawn: %ld", (long)ND(a)->n.mem[OUT + 1]);
|
||||
|
||||
/* offers are withdrawn by a store to the wait (found by the review of step 6d) */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); LIT(0); LIT(PB + (v4_cell)V4_PORT_WAIT); O(BANG_A); O(STORE_A); done_mark(); wait_for_ever();
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_asm_ok(&as) && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0 && ND(a)->n.faults == 0, "a node that offers and then stores to the wait has no offer left");
|
||||
|
||||
/* the wait for an offer only: a word that comes for the node does not end it */
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); wait1_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); count_for_ever();
|
||||
c = loaded(2); LIT(PB + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, c, 0, a, 2), "a node waits for its offer only, to a node that never reads, while a third writes to it");
|
||||
for (i = 0; i < 200; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.mem[OUT + 2] == 0 && ND(c)->n.asking && (ND(c)->n.readers & 1u) == 0, "it goes on waiting; the writer is not served, and does not see it as reading");
|
||||
CHECK(v4_fabric_remove(&f, b) != 0, "the node offered to is removed");
|
||||
v4_fabric_gone_error(&f, 18);
|
||||
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
||||
v4_node_fault_attach(&ND(a)->n, 900);
|
||||
CHECK(v4_fabric_wire_device(&f, a, 7, &sink), "(the port it ends by reading has a device that gives nothing)");
|
||||
for (i = 0; i < 100 && ND(a)->n.mem[OUT + 2] == 0; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.mem[OUT + 1] == 2 * (v4_cell)V4_PORTS + 0 && ND(a)->n.faults == 0 && ND(a)->n.offers == 0 && ND(c)->n.asking, "it is told there is nothing on that port; no error is raised; the writer still waits");
|
||||
fresh();
|
||||
a = loaded(0); offer(0, 7); wait1_into(OUT); done_mark(); wait_for_ever();
|
||||
b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "the same, to a node that reads");
|
||||
(void)settle(300);
|
||||
CHECK(ND(b)->n.mem[OUT] == 7 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1, "its offer is taken, and it is told");
|
||||
|
||||
/* a host with one node, whose devices always take */
|
||||
fresh();
|
||||
a = loaded(0); offer(1, 21); offer(3, 23); wait_into(OUT); done_mark(); wait_for_ever();
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.offers == ((1u << 1) | (1u << 3)), "a node offers on two ports and is in the wait");
|
||||
CHECK(v4_node_offer_as_write(&ND(a)->n) && ND(a)->n.asking && ND(a)->n.ask_port == 1 && ND(a)->n.request == 21, "its host turns that into a write of the lower port's word");
|
||||
v4_node_port_served(&ND(a)->n);
|
||||
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(!ND(a)->n.asking && ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 1 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0,
|
||||
"and when it is served the node is told that offer was taken");
|
||||
CHECK(!v4_node_offer_as_write(&ND(a)->n), "a node that is not in the wait is left alone");
|
||||
v4_fabric_interrupt_error(&f, 21);
|
||||
for (i = 0; i < 60; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.request == 2 && ND(a)->n.faults == 0, "blocked on its second word with the device still pending, the writer waits");
|
||||
}
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
|
||||
+33
-206
@@ -71,7 +71,8 @@ static int console_take(void *self, v4_cell value)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
static const v4_device console = { console_take, console_give, 0 };
|
||||
static int console_pending(void *self) { (void)self; return going_at < going.count; }
|
||||
static const v4_device console = { console_take, console_give, 0, console_pending };
|
||||
|
||||
static int is_empty(const char *s) { return s[0] == 0; }
|
||||
|
||||
@@ -111,38 +112,12 @@ static unsigned starforth_node(unsigned k)
|
||||
return (unsigned)id;
|
||||
}
|
||||
|
||||
/* The same, answering with what has been printed: for when a node is
|
||||
* spinning and the fabric never comes to rest. */
|
||||
static const char *said(v4_cell node, const char *text) { (void)tell(node, text); return printed; }
|
||||
|
||||
static int waiting(unsigned id)
|
||||
{
|
||||
const v4_node *n = &v4_fabric_node_at(&f, id)->n;
|
||||
return n->reading && !n->given && n->read_port == V4_PORT_ANY;
|
||||
}
|
||||
|
||||
/* Three nodes in a row once more, 10 at the console, 11, 12: a new fabric, each
|
||||
* told who it is and the way to the others. For the scenes at the end. */
|
||||
static unsigned starforth_node(unsigned k);
|
||||
static void row_again(unsigned *hera, unsigned *mid, unsigned *far)
|
||||
{
|
||||
unsigned i;
|
||||
v4_fabric_init(&f, places, 3);
|
||||
v4_fabric_gone_error(&f, V4_ERROR_NO_ONE);
|
||||
*hera = starforth_node(0); *mid = starforth_node(1); *far = starforth_node(2);
|
||||
(void)v4_fabric_wire_device(&f, *hera, 1, &console);
|
||||
(void)v4_fabric_wire(&f, *hera, 2, *mid, 2); (void)v4_fabric_wire(&f, *mid, 3, *far, 2);
|
||||
going.count = 0; going_at = 0;
|
||||
step_cap = 20000000;
|
||||
for (i = 0; i < 20; i++) (void)v4_fabric_step(&f);
|
||||
(void)tell(0, "10 (ME) ! 1 (CONSOLE) ! 1 1 ROUTE 0 2 ROUTE");
|
||||
(void)tell(0, "11 (ME) ! 1 (CONSOLE) ! 2 DEFAULT-ROUTE 0 3 ROUTE");
|
||||
(void)tell(0, "12 (ME) ! 1 (CONSOLE) ! 2 DEFAULT-ROUTE");
|
||||
(void)tell(10, "11 2 ROUTE 12 2 ROUTE 11 2 NEIGHBOUR");
|
||||
(void)tell(11, "12 3 ROUTE 10 2 NEIGHBOUR");
|
||||
(void)tell(12, "11 2 NEIGHBOUR");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned hera, mid, far, i;
|
||||
@@ -327,12 +302,12 @@ int main(void)
|
||||
|
||||
/* no room: a node that is waiting keeps what it is sent, and has room for only so much */
|
||||
{
|
||||
v4_cell lost = pool[1].n.mem[LOST], refused = pool[0].n.mem[REFUSED], got, not_taken, told_of;
|
||||
v4_cell lost = pool[1].n.mem[LOST], refused = pool[0].n.mem[REFUSED], got, not_taken;
|
||||
CHECK(is_empty(tell(11, "0 GOT !")) && is_empty(tell(11, "12 AWAIT .")) && ended == 0 && pool[1].n.mem[AWAIT_FROM] == 12, "node 11 waits for an answer from 12, which is not going to send one");
|
||||
CHECK(is_empty(tell(10, ": F 49 0 DO S\" HIT\" 11 SEND LOOP ; F")) && ended == 1 && ended_from == 10, "node 10 sends it 49 messages meanwhile");
|
||||
CHECK(pool[1].n.mem[MQ_COUNT] > 300 && pool[1].n.mem[MQ_COUNT] <= MQ_CELLS - 36, "node 11 has kept what it has room for, and left the room that is kept for refusals: %ld cells", (long)pool[1].n.mem[MQ_COUNT]);
|
||||
CHECK(pool[1].n.mem[OWED_COUNT] == 0 && pool[0].n.mem[REFUSED] >= refused + 8, "it owed a refusal for each of the rest, as many as it could owe at once, and paid them while it waited: node 10 was told of %ld",
|
||||
(long)(pool[0].n.mem[REFUSED] - refused));
|
||||
CHECK(pool[1].n.mem[OWED_COUNT] == 8, "it owes a refusal for each of the rest, as many as it can owe: %ld", (long)pool[1].n.mem[OWED_COUNT]);
|
||||
CHECK(pool[0].n.mem[REFUSED] == refused, "and has sent none yet: it is still waiting");
|
||||
|
||||
/* one from its centre ends the wait */
|
||||
CHECK(is_empty(tell(10, "12 NO-ROUTE")), "node 10 forgets the way to 12");
|
||||
@@ -341,10 +316,9 @@ int main(void)
|
||||
got = strtol(tell(11, "GOT @ ."), NULL, 10);
|
||||
not_taken = 49 - got;
|
||||
CHECK(got >= 30 && not_taken > 8, "it then did every message it had kept: %ld of the 49", (long)got);
|
||||
told_of = pool[0].n.mem[REFUSED] - refused;
|
||||
CHECK(told_of >= 8 && told_of <= not_taken, "node 10 was told of %ld of them", (long)told_of);
|
||||
CHECK(pool[1].n.mem[LOST] == lost + not_taken + (not_taken - told_of), "it counted each message it did not take, and each refusal it could not owe: %ld for %ld not taken, %ld told",
|
||||
(long)(pool[1].n.mem[LOST] - lost), (long)not_taken, (long)told_of);
|
||||
CHECK(pool[0].n.mem[REFUSED] == refused + 8, "and sent the eight refusals it owed: node 10 was told of %ld", (long)(pool[0].n.mem[REFUSED] - refused));
|
||||
CHECK(pool[1].n.mem[LOST] == lost + not_taken + (not_taken - 8), "it counted each message it did not take, and each refusal it could not owe: %ld for %ld not taken",
|
||||
(long)(pool[1].n.mem[LOST] - lost), (long)not_taken);
|
||||
/* the way to 12 is forgotten: what is sent there now is refused */
|
||||
refused = pool[1].n.mem[REFUSED];
|
||||
CHECK(is_empty(tell(11, ": P12 S\" 1 DROP\" 12 SEND ; P12")) && pool[1].n.mem[REFUSED] == refused + 1, "having forgotten the way to 12, what node 11 sends there goes toward its centre, which has no way either, and is refused");
|
||||
@@ -352,11 +326,11 @@ int main(void)
|
||||
}
|
||||
/* a line from the console breaks a wait (MESH.md 7b.6) */
|
||||
CHECK(is_empty(tell(10, "12 AWAIT .")) && ended == 0 && pool[0].n.mem[AWAIT_FROM] == 12 && waiting(hera), "node 10 waits for an answer from 12, which is not going to send one");
|
||||
CHECK(strcmp(tell(11, "20 22 + ."), "42 ") == 0 && ended == 1 && pool[0].n.mem[AWAIT_FROM] == 12, "text from the console for node 11 comes to node 10 on its way: node 10 passes it on, and goes on waiting: \"%s\"", printed);
|
||||
CHECK(is_empty(tell(11, "20 22 + .")) && ended == 0 && pool[0].n.mem[AWAIT_FROM] == 12, "text from the console for node 11 comes to node 10 on its way: node 10 keeps it, and goes on waiting");
|
||||
{
|
||||
const char *r = tell(10, "65 EMIT");
|
||||
CHECK(strstr(r, "Interrupted\n") != NULL && pool[0].n.mem[AWAIT_FROM] == 0, "text from the console for node 10 itself ends its wait, with an error that says so: \"%s\"", r);
|
||||
CHECK(strstr(r, "A") != NULL && ended == 2, "the line is then done as usual: \"%s\", %d ended", r, ended);
|
||||
CHECK(strstr(r, "A") != NULL && strstr(r, "42 ") != NULL && ended == 3, "the line is then done as usual, and so is what was kept for node 11: \"%s\", %d ended", r, ended);
|
||||
CHECK(strstr(r, "Interrupted\n") < strstr(r, "A"), "the wait ends before the line that ended it is done");
|
||||
}
|
||||
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && waiting(hera) && waiting(mid) && waiting(far), "and all three go on as before");
|
||||
@@ -390,10 +364,9 @@ int main(void)
|
||||
{
|
||||
CHECK(is_empty(tell(11, "0 GOT !")) && is_empty(tell(11, "12 AWAIT .")) && pool[1].n.mem[AWAIT_FROM] == 12, "node 11 waits on 12 again");
|
||||
CHECK(is_empty(tell(10, ": F40 40 0 DO S\" HIT\" 11 SEND LOOP ; F40")) && pool[1].n.mem[OWED_COUNT] == 0, "node 10 sends it as many messages as it has room for");
|
||||
/* node 11 does what it has kept before it pays what it owes (7b.3): forty answers come first */
|
||||
(void)tell(10, ": ONE S\" HIT\" 11 SEND 12 NO-ROUTE 12 11 GONE 41 0 DO 11 AWAIT DROP LOOP ; ONE");
|
||||
(void)tell(10, ": ONE S\" HIT\" 11 SEND 12 NO-ROUTE 12 11 GONE 11 AWAIT . ; ONE");
|
||||
CHECK(strstr(printed, "Message refused") != NULL && pool[0].n.mem[AWAIT_FROM] == 0,
|
||||
"it sends one more, for which there is no room, ends node 11's wait, and waits for node 11's answers: after the forty, its wait ends, the message refused: \"%s\"", printed);
|
||||
"it sends one more, for which there is no room, ends node 11's wait, and waits for node 11's answer: its own wait ends, the message refused: \"%s\"", printed);
|
||||
CHECK(strtol(tell(11, "GOT @ ."), NULL, 10) == 40 && is_empty(tell(10, "12 3 ROUTE")) && is_empty(tell(11, "12 3 ROUTE")), "node 11 did the forty it had kept; the ways are told again");
|
||||
}
|
||||
|
||||
@@ -420,180 +393,34 @@ int main(void)
|
||||
CHECK(is_empty(tell(10, "66 NO-ROUTE")) && waiting(hera) && waiting(mid) && waiting(far), "the way is forgotten, and all three are at rest");
|
||||
}
|
||||
|
||||
/* ---- what a node passes on keeps its order, however long it must be kept (MESH.md 7c) ---- */
|
||||
CHECK(is_empty(tell(11, "12 2 ROUTE")), "node 11 is told the way to node 12 is by node 10");
|
||||
CHECK(is_empty(tell(12, ": SLOW 30000 0 DO LOOP ;")), "node 12 is given a word that takes a while");
|
||||
CHECK(strcmp(tell(11, ": TRIO S\" SLOW 68 EMIT\" 12 SEND S\" 69 EMIT\" 12 SEND S\" 70 EMIT\" 12 SEND ; TRIO"), "DEF") == 0,
|
||||
"it sends node 12 three texts, the first slow to do: node 10 keeps the other two until node 12 reads again, and they are done in order: \"%s\"", printed);
|
||||
CHECK(is_empty(tell(11, "12 3 ROUTE")) && waiting(hera) && waiting(mid) && waiting(far) && pool[0].n.mem[MQ_COUNT] == 0, "the way is told again; all three are at rest, keeping nothing");
|
||||
|
||||
/* ---- a node that is stuck holds up no one (MESH.md 7c) ------------------- */
|
||||
v4_fabric_gone_error(&f, V4_ERROR_NO_ONE);
|
||||
|
||||
/* From here a node is spinning, so the fabric never comes to rest: said()
|
||||
* is tell(), answering with what was printed by the time it gave up. */
|
||||
|
||||
/* a refusal owed to a stuck node whose number is the lower */
|
||||
CHECK(is_empty(said(12, "NO-ROUTES 1 3 ROUTE 10 3 ROUTE 11 2 ROUTE")) && is_empty(said(11, "55 3 ROUTE")), "node 12 has no way for 55; node 11 is told the way to 55 is by node 12");
|
||||
CHECK(strcmp(tell(11, ": C1 S\" 1 DROP\" 55 SEND S\" 1 DROP\" 12 SEND BEGIN 0 UNTIL ; C1"), "(still running)") == 0, "node 11 sends 55 a message, and node 12 text, and is then stuck in a loop");
|
||||
/* a node that owes a refusal to a node that is stuck is not stuck itself */
|
||||
CHECK(is_empty(tell(12, "NO-ROUTES 1 3 ROUTE 10 3 ROUTE 11 2 ROUTE")) && is_empty(tell(11, "55 3 ROUTE")), "node 12 has no way for 55; node 11 is told the way to 55 is by node 12");
|
||||
CHECK(strcmp(tell(11, ": C1 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C1"), "(still running)") == 0, "node 11 sends 55 a message and is then stuck in a loop");
|
||||
CHECK(pool[2].n.mem[OWED_COUNT] == 1, "node 12 owes node 11 a refusal, and cannot give it: node 11 is not reading");
|
||||
CHECK(strcmp(said(12, "7 8 * ."), "56 ") == 0 && printed_from == 12, "node 12 goes on doing what it is sent: \"%s\"", printed);
|
||||
{
|
||||
v4_uheat_t clock = pool[2].es.anticlock;
|
||||
unsigned k;
|
||||
for (k = 0; k < 1000; k++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&pool[2].n) && pool[2].es.anticlock == clock && pool[2].n.mem[OWED_COUNT] == 1, "between times it sleeps in the wait, still owing, and executes nothing");
|
||||
}
|
||||
CHECK(strcmp(said(10, ": X12 S\" 66 EMIT\" 12 SEND ; X12"), "B") == 0 && printed_from == 12, "another node writes to it and is answered: \"%s\"", printed);
|
||||
(void)tell(12, "7 8 * .");
|
||||
CHECK(strcmp(printed, "56 ") == 0 && printed_from == 12, "node 12 goes on doing what it is sent: \"%s\"", printed);
|
||||
|
||||
/* two refusals owed: one to the stuck node, one to a node that waits for it */
|
||||
CHECK(is_empty(said(10, "77 3 ROUTE")), "node 10 is told the way to 77 is by node 12, which has none");
|
||||
CHECK(strstr(said(10, ": W77B S\" 1 DROP\" 77 SEND 77 AWAIT . ; W77B"), "Message refused") != NULL && pool[0].n.mem[AWAIT_FROM] == 0 && pool[2].n.mem[OWED_COUNT] == 1,
|
||||
"node 10 sends 77 a message and waits: node 12, which still owes the stuck node, tells node 10 it was refused: \"%s\"", printed);
|
||||
CHECK(is_empty(said(10, "77 NO-ROUTE")), "the way is forgotten");
|
||||
|
||||
/* a message passed on toward the stuck node waits with the node passing it on, which goes on serving */
|
||||
{
|
||||
v4_cell kept = pool[0].n.mem[MQ_COUNT];
|
||||
CHECK(is_empty(said(11, "1 DROP")) && ended == 0 && pool[0].n.mem[MQ_COUNT] > kept && v4_node_in_wait(&pool[0].n),
|
||||
"text from the console for the stuck node comes to node 10, which cannot hand it on: it keeps it, offered, and sleeps");
|
||||
CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && strcmp(said(12, "7 8 * ."), "56 ") == 0 && printed_from == 12,
|
||||
"node 10 goes on doing what it is sent, and passing on what is for others: \"%s\"", printed);
|
||||
CHECK(pool[0].n.mem[MQ_COUNT] > kept && v4_node_in_wait(&pool[0].n), "still keeping what is for the stuck node");
|
||||
}
|
||||
/* the stuck node's own answer: node 12 did text node 11 sent before it stuck, and cannot give it the answer */
|
||||
CHECK(pool[2].n.mem[MQ_COUNT] > 0 && v4_node_in_wait(&pool[2].n), "node 12 keeps the answer it has for node 11, offered, and sleeps");
|
||||
|
||||
/* a line that must begin a message to the stuck node waits, and the node takes in what comes */
|
||||
CHECK(is_empty(said(10, ": TO11 S\" 1 DROP\" 11 SEND 65 EMIT ; TO11")) && ended == 0 && v4_node_in_wait(&pool[0].n) && !pool[0].n.asking,
|
||||
"node 10 sends the stuck node a message: its line waits in the wait, not blocked writing");
|
||||
{
|
||||
v4_cell kept = pool[0].n.mem[MQ_COUNT];
|
||||
CHECK(is_empty(said(12, "7 8 * .")) && ended == 0 && pool[0].n.mem[MQ_COUNT] > kept && v4_node_in_wait(&pool[0].n),
|
||||
"text from the console for node 12 comes to node 10: it keeps it, and its line goes on waiting");
|
||||
}
|
||||
{
|
||||
const char *r = said(10, "66 EMIT");
|
||||
CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "A") == NULL && strstr(r, "B") != NULL && strstr(r, "Interrupted\n") < strstr(r, "B"),
|
||||
"text from the console for node 10 itself ends the line that waited, with an error that says so, and is then done: \"%s\"", r);
|
||||
CHECK(strstr(r, "56 ") != NULL && ended == 3, "and what it kept for node 12 is passed on and done: \"%s\", %d ended", r, ended);
|
||||
}
|
||||
|
||||
/* the stuck node is removed while a line waits to begin a message to it */
|
||||
CHECK(is_empty(said(10, "TO11")) && ended == 0 && v4_node_in_wait(&pool[0].n), "node 10's line waits on the stuck node again");
|
||||
/* a node that is waiting to write to a node finds out when that node is removed */
|
||||
(void)tell(12, ": TO11 S\" 1 DROP\" 11 SEND 65 EMIT ; TO11");
|
||||
CHECK(ended == 0, "node 12 sends node 11 a message: node 11 is not reading, and node 12 waits to write to it");
|
||||
CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed");
|
||||
{
|
||||
unsigned k;
|
||||
for (k = 0; k < 200000 && ended == 0; k++) (void)v4_fabric_step(&f);
|
||||
CHECK(strstr(printed, "No one on that port") != NULL && strstr(printed, "A") == NULL && ended == 1, "the line ends in an error that says no one is there: \"%s\"", printed);
|
||||
}
|
||||
(void)tell(10, "1 DROP");
|
||||
CHECK(strstr(printed, "No one on that port") != NULL && strstr(printed, "A") == NULL, "node 12's line ends in an error that says no one is there: \"%s\"", printed);
|
||||
step_cap = 20000000;
|
||||
CHECK(strcmp(said(12, "7 8 * ."), "56 ") == 0 && pool[2].n.mem[OWED_COUNT] == 0 && pool[2].n.mem[MQ_COUNT] == 0 && pool[0].n.mem[MQ_COUNT] == 0 && waiting(far) && !v4_node_in_wait(&pool[2].n) && waiting(hera),
|
||||
"node 12 goes on; what it owed node 11 and kept for it, and what node 10 kept for it, are let go: they owe and keep nothing, and are at rest");
|
||||
CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0 && pool[2].n.mem[OWED_COUNT] == 0 && waiting(far) && waiting(hera),
|
||||
"it goes on, the refusal it owed node 11 let go: it owes nothing and is at rest");
|
||||
|
||||
/* a refusal owed to a stuck node whose number is the higher */
|
||||
/* THE LIMIT (MESH.md 7b.7): a refusal owed to a neighbour whose number is the higher is written
|
||||
* without looking, as any message to it is, so a node that is stuck holds up a neighbour that owes
|
||||
* it one -- until it is removed */
|
||||
step_cap = 3000000;
|
||||
CHECK(is_empty(said(12, "55 3 ROUTE")), "node 12 is told the way to 55 is by node 10, which has none");
|
||||
CHECK(strcmp(tell(12, ": C2 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C2"), "(still running)") == 0 && pool[0].n.mem[OWED_COUNT] == 1 && !pool[0].n.asking,
|
||||
"node 12 sends 55 a message and is then stuck in a loop: node 10 owes it a refusal, and is not blocked writing it");
|
||||
step_cap = 20000000;
|
||||
CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && pool[0].n.mem[OWED_COUNT] == 1, "node 10 goes on doing what it is sent, still owing: \"%s\"", printed);
|
||||
{
|
||||
v4_uheat_t clock;
|
||||
unsigned k;
|
||||
for (k = 0; k < 1000; k++) (void)v4_fabric_step(&f);
|
||||
clock = pool[0].es.anticlock;
|
||||
for (k = 0; k < 1000; k++) (void)v4_fabric_step(&f);
|
||||
CHECK(v4_node_in_wait(&pool[0].n) && pool[0].es.anticlock == clock, "and sleeps in the wait between times");
|
||||
}
|
||||
CHECK(is_empty(tell(12, "55 3 ROUTE")), "node 12 is told the way to 55 is by node 10, which has none");
|
||||
CHECK(strcmp(tell(12, ": C2 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C2"), "(still running)") == 0 && pool[0].n.asking && pool[0].n.mem[OWED_COUNT] == 1,
|
||||
"node 12 sends 55 a message and is then stuck in a loop: node 10 owes it a refusal, writes it, and is blocked");
|
||||
v4_fabric_gone_error(&f, V4_ERROR_NO_ONE);
|
||||
CHECK(v4_fabric_remove(&f, far) == &pool[2], "node 12 is removed");
|
||||
CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && pool[0].n.mem[OWED_COUNT] == 0 && waiting(hera) && !v4_node_in_wait(&pool[0].n), "node 10 lets the refusal go and is at rest: \"%s\"", printed);
|
||||
|
||||
/* ==== what the review of step 6d found: each scene on a row of three, begun again ==== */
|
||||
|
||||
/* a node is removed while a message is being passed on to it */
|
||||
{
|
||||
static char line[240];
|
||||
unsigned steps = 0, k;
|
||||
unsigned depth;
|
||||
row_again(&hera, &mid, &far);
|
||||
CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0, "three nodes in a row again");
|
||||
depth = pool[1].n.ds.depth;
|
||||
memset(line, ' ', 200); memcpy(line, "1 DROP", 6); line[200] = 0;
|
||||
printed_len = 0; printed[0] = 0; ended = 0;
|
||||
(void)v4_message_text(&going, 12, CONSOLE_ID, V4_MSG_TEXT, line, 200); going_at = 0;
|
||||
while (steps < 2000000 && !(pool[1].n.mem[MQ_COUNT] > 0 && pool[1].n.mem[MQ_COUNT] < 40 && pool[2].n.reading && pool[2].n.read_port == 2)) { (void)v4_fabric_step(&f); steps++; }
|
||||
CHECK(steps < 2000000, "long text for node 12 is on its way through node 11, part written: %ld cells of it still with node 11", (long)pool[1].n.mem[MQ_COUNT]);
|
||||
CHECK(v4_fabric_remove(&f, far) == &pool[2], "node 12 is removed");
|
||||
for (k = 0; k < 3000000 && v4_fabric_step(&f) != 0; k++) { }
|
||||
CHECK(k < 3000000 && pool[1].n.mem[MQ_COUNT] == 0 && waiting(mid) && pool[1].n.ds.depth == depth, "node 11 comes to rest: it has let the rest go, keeps nothing, and its stack is as it was (%ld cells kept)", (long)pool[1].n.mem[MQ_COUNT]);
|
||||
CHECK(strcmp(tell(11, "20 22 + ."), "42 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0, "and it and node 10 go on");
|
||||
}
|
||||
|
||||
/* a node is removed while it is writing a message to its neighbour: the half that came is not kept */
|
||||
{
|
||||
static char line[240];
|
||||
unsigned steps = 0, k;
|
||||
row_again(&hera, &mid, &far);
|
||||
memset(line, ' ', 200); memcpy(line, "1 DROP", 6); line[200] = 0;
|
||||
printed_len = 0; printed[0] = 0; ended = 0;
|
||||
(void)v4_message_text(&going, 12, CONSOLE_ID, V4_MSG_TEXT, line, 200); going_at = 0;
|
||||
while (steps < 2000000 && !(pool[2].n.mem[MQ_COUNT] > 8 && pool[2].n.reading && pool[2].n.read_port == 2)) { (void)v4_fabric_step(&f); steps++; }
|
||||
CHECK(steps < 2000000, "node 12 has taken in part of a long text from node 11: %ld cells", (long)pool[2].n.mem[MQ_COUNT]);
|
||||
CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed");
|
||||
for (k = 0; k < 3000000 && v4_fabric_step(&f) != 0; k++) { }
|
||||
CHECK(k < 3000000 && pool[2].n.mem[MQ_COUNT] == 0 && waiting(far), "node 12 comes to rest, and has not kept the half: %ld cells", (long)pool[2].n.mem[MQ_COUNT]);
|
||||
CHECK(v4_fabric_wire(&f, hera, 3, far, 3) && is_empty(tell(10, "12 NO-ROUTE 12 3 ROUTE")), "node 12 is wired to node 10 instead");
|
||||
(void)tell(12, "NO-ROUTES 1 3 ROUTE 10 3 ROUTE");
|
||||
CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0, "and does what it is sent");
|
||||
}
|
||||
|
||||
/* what a finished text printed, with the way to the console through a stuck node */
|
||||
row_again(&hera, &mid, &far);
|
||||
CHECK(v4_fabric_wire(&f, hera, 3, far, 3) && is_empty(tell(10, "NO-ROUTES 1 1 ROUTE 11 2 ROUTE 12 3 ROUTE")) && strcmp(tell(12, "3 4 + ."), "7 ") == 0,
|
||||
"node 12 is reached straight from node 10; what it prints still goes back by node 11");
|
||||
step_cap = 400000;
|
||||
CHECK(strcmp(tell(11, ": ST BEGIN 0 UNTIL ; ST"), "(still running)") == 0, "node 11 is stuck in a loop");
|
||||
(void)said(10, ": P S\" 65 EMIT\" 12 SEND ; P");
|
||||
CHECK(v4_node_in_wait(&pool[2].n) && pool[2].n.mem[MQ_COUNT] > 0, "node 10 sends node 12 text that prints: what it printed, and how it ended, wait with node 12, offered, and it sleeps");
|
||||
(void)said(10, ": Q S\" 9 (REFUSED) !\" 12 SEND ; Q");
|
||||
(void)said(12, "8 (REFUSED) !");
|
||||
CHECK(pool[2].n.mem[REFUSED] == 0 && v4_node_in_wait(&pool[2].n),
|
||||
"the next text from node 10, and text typed at the console for it, are kept back: what it owes each from the last has not gone (MESH.md 7c.3). It sleeps in the wait");
|
||||
{ v4_cell kept = pool[2].n.mem[MQ_COUNT];
|
||||
(void)said(10, ": R S\" 7 (REFUSED) !\" 12 SEND ; R");
|
||||
CHECK(pool[2].n.mem[MQ_COUNT] > kept && v4_node_in_wait(&pool[2].n), "it is not held: it takes in what it is sent, and sleeps again"); }
|
||||
CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed");
|
||||
{ unsigned k; for (k = 0; k < 3000000 && v4_fabric_step(&f) != 0; k++) { }
|
||||
CHECK(k < 3000000 && pool[2].n.mem[REFUSED] == 7 && pool[2].n.mem[MQ_COUNT] == 0 && waiting(far), "what could not go is let go, the texts kept back are done in the order they came, and node 12 is at rest: %ld", (long)pool[2].n.mem[REFUSED]); }
|
||||
|
||||
/* offers left standing when a node goes round to begin text */
|
||||
row_again(&hera, &mid, &far);
|
||||
CHECK(v4_fabric_wire(&f, hera, 3, far, 3) && v4_fabric_wire(&f, hera, 4, far, 4), "nodes 10 and 12 are wired to each other twice over");
|
||||
(void)tell(10, "NO-ROUTES 1 1 ROUTE 11 2 ROUTE 12 3 ROUTE 77 3 ROUTE");
|
||||
(void)tell(10, ": SLOW 400000 0 DO LOOP ; : Z S\" 1 DROP\" 77 SEND SLOW ;");
|
||||
(void)tell(12, ": FWD99 S\" 67 EMIT\" 99 SEND ;");
|
||||
(void)tell(12, "NO-ROUTES 10 3 ROUTE 11 2 ROUTE 1 4 ROUTE 99 4 ROUTE");
|
||||
CHECK(strcmp(tell(12, "3 4 + ."), "7 ") == 0, "node 12's way to the console, and to 99, is by its other wire to node 10");
|
||||
step_cap = 300000;
|
||||
CHECK(strcmp(tell(11, ": C S\" 1 DROP\" 12 SEND S\" FWD99\" 12 SEND BEGIN 0 UNTIL ; C"), "(still running)") == 0, "node 11 sends node 12 two texts and is stuck");
|
||||
step_cap = 30000;
|
||||
(void)said(10, "Z");
|
||||
CHECK(pool[2].n.mem[OWED_COUNT] == 1, "node 10 sends 77 a message by node 12, which has no way, and is then busy: node 12 owes it a refusal, offered on its first wire");
|
||||
CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed: node 12 lets its answer go, and begins the text it kept back");
|
||||
printed_len = 0; printed[0] = 0; printed_from = -1;
|
||||
{ unsigned k; for (k = 0; k < 20000000 && v4_fabric_step(&f) != 0; k++) { }
|
||||
CHECK(k < 20000000, "all come to rest"); }
|
||||
CHECK(strchr(printed, 'C') == NULL, "the text node 12 sent to 99 was not done by node 10: \"%s\" from %ld", printed, (long)printed_from);
|
||||
CHECK(pool[0].n.mem[REFUSED] == 1 && pool[2].n.mem[OWED_COUNT] == 0, "node 10 was told its message to 77 was refused: %ld", (long)pool[0].n.mem[REFUSED]);
|
||||
|
||||
/* many of a node's own messages waiting for a stuck node */
|
||||
row_again(&hera, &mid, &far);
|
||||
step_cap = 300000;
|
||||
CHECK(strcmp(tell(11, ": ST BEGIN 0 UNTIL ; ST"), "(still running)") == 0, "node 11 is stuck in a loop");
|
||||
{ int k, good = 0;
|
||||
for (k = 1; k <= 12; k++) { (void)said(10, "12 11 GONE"); if (strcmp(said(10, "1 2 + ."), "3 ") == 0) good++; }
|
||||
CHECK(good == 12 && pool[0].n.mem[MQ_COUNT] == 108, "node 10 is told twelve times to tell it a node is gone, and after each still does what is typed: %d of 12, %ld cells kept", good, (long)pool[0].n.mem[MQ_COUNT]); }
|
||||
step_cap = 20000000;
|
||||
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && pool[0].n.mem[OWED_COUNT] == 0 && waiting(hera), "node 10 is let go, lets the refusal go, and goes on: \"%s\"", printed);
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures != 0;
|
||||
|
||||
@@ -63,7 +63,7 @@ static v4_heat h;
|
||||
static v4_text tx;
|
||||
static v4_cell w_key, w_key_end, w_fault, capsule_latest;
|
||||
static v4_cell w_idle; /* (IDLE): where the node waits for a message */
|
||||
static v4_cell w_idle_end; /* (LINE): the word after it */
|
||||
static v4_cell w_idle_end; /* (PASS-ON): the word after it */
|
||||
static char out[V4_CONSOLE_CAP + 1];
|
||||
|
||||
/* STORAGE (MESH.md section 8). The node asks its kernel for its blocks, and
|
||||
@@ -213,7 +213,6 @@ static int run_line(long max)
|
||||
while (steps < max && idle < 64) {
|
||||
(void)v4_exec_step_word(&n, &es, &h);
|
||||
steps++;
|
||||
(void)v4_node_offer_as_write(&n); /* in the wait: what it offers is a write, the kernel and the console taking at once */
|
||||
if (n.asking && n.ask_port == 0) { kernel_serve(); v4_node_port_served(&n); continue; } /* a request */
|
||||
if (n.asking && n.ask_port == CONSOLE_PORT) { /* a word of a message from the node */
|
||||
v4_cell word = n.request;
|
||||
@@ -871,7 +870,7 @@ int main(void)
|
||||
printf(" capsule: %ld words\n", (long)v4_text_here(&tx) - 16);
|
||||
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
|
||||
w_idle = v4_text_word(&tx, "(IDLE)");
|
||||
w_idle_end = v4_text_word(&tx, "(LINE)"); /* the word after (IDLE) in quit.v4 */
|
||||
w_idle_end = v4_text_word(&tx, "(PASS-ON)"); /* the word after (IDLE) in quit.v4 */
|
||||
w_key = v4_text_word(&tx, "KEY");
|
||||
w_key_end = v4_text_word(&tx, "CR"); /* the word after KEY in core.v4 */
|
||||
w_fault = v4_text_word(&tx, "(FAULTS)");
|
||||
@@ -1134,21 +1133,6 @@ int main(void)
|
||||
}
|
||||
printf(" %u values may wait on the stack while the next line is typed and interpreted\n", most);
|
||||
CHECK(most + 4u >= V4_DATA_DEPTH, "the prompt and the interpreter take no more than four cells of the stack");
|
||||
/* one more than may wait: the line ends in an error that says so, and the node goes on (found by the review of step 6d) */
|
||||
{
|
||||
char line[4 * V4_DATA_DEPTH + 16];
|
||||
size_t at = 0;
|
||||
unsigned m;
|
||||
const char *r;
|
||||
boot_bare();
|
||||
for (m = 0; m < most + 1; m++) at += (size_t)snprintf(line + at, sizeof line - at, "1 ");
|
||||
snprintf(line + at, sizeof line - at, "\n");
|
||||
r = say(line);
|
||||
CHECK(strstr(r, "Stack overflow") != NULL && strstr(r, " ERROR\nok> ") != NULL, "a line that leaves one value more than may wait ends in an error that says so: \"%s\"", r);
|
||||
CHECK(strcmp(say("1 2 + .\n"), "3 ok\nok> ") == 0, "and the next line is done as usual");
|
||||
for (m = 0; m < 2000 && !node_idle(); m++) (void)v4_exec_step_word(&n, &es, &h); /* it finishes what it was doing when its answer went */
|
||||
CHECK(n.mem[MQ_COUNT] == 0 && node_idle(), "with nothing left among the messages waiting, and the node at its ports");
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- how much of the data stack a line has ---- */
|
||||
|
||||
+22
-37
@@ -140,7 +140,7 @@ static int host_born(void *self)
|
||||
v4_node_fault_attach(&pool[k].n, w_fault);
|
||||
v4_blocks_init(&node_blocks[k], BUF0_W);
|
||||
if (k != 0) { /* Hera's kernel is wired where she is made */
|
||||
node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k];
|
||||
node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k]; node_kernel[k].pending = 0;
|
||||
if (!v4_fabric_wire_device(&f, (unsigned)place, 0, &node_kernel[k])) return -1;
|
||||
}
|
||||
return place;
|
||||
@@ -194,7 +194,7 @@ static int kernel_take(void *self, v4_cell value) /* a request */
|
||||
if (value < FIRST_REQUEST || !v4_manage_serve(&manage, hera_place, (unsigned)(value - FIRST_REQUEST))) refused++;
|
||||
return 1;
|
||||
}
|
||||
static const v4_device kernel = { kernel_take, kernel_give, 0 };
|
||||
static const v4_device kernel = { kernel_take, kernel_give, 0, 0 };
|
||||
|
||||
/* ---- the console: a device on Hera's port 1 ---- */
|
||||
#define CONSOLE_ID 1
|
||||
@@ -237,7 +237,8 @@ static int console_take(void *self, v4_cell value)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
static const v4_device console = { console_take, console_give, 0 };
|
||||
static int console_pending(void *self) { (void)self; return going_at < going.count; }
|
||||
static const v4_device console = { console_take, console_give, 0, console_pending };
|
||||
|
||||
/* Is there a node that will execute at the next step? A step in which a
|
||||
* node only faulted counts nothing done (exec.h), and it goes on from its
|
||||
@@ -275,8 +276,6 @@ static const char *tell(v4_cell node, const char *text)
|
||||
if (steps >= step_limit) { stuck(); return "(still running)"; }
|
||||
return printed;
|
||||
}
|
||||
/* The same, answering with what has been printed: for when a node is spinning and the fabric never rests. */
|
||||
static const char *said(v4_cell node, const char *text) { (void)tell(node, text); return printed; }
|
||||
static int told(v4_cell node, const char *text) { return tell(node, text)[0] == 0 && ended == 1 && ended_how == V4_TEXT_COMPLETED; }
|
||||
|
||||
/* POST's host, for the node numbered 10 (v4/include/v4/post.h): a line is
|
||||
@@ -383,6 +382,7 @@ int main(void)
|
||||
/* ---- Hera is born empty, and takes in the nucleus ---- */
|
||||
v4_fabric_init(&f, places, PLACES);
|
||||
v4_fabric_gone_error(&f, V4_ERROR_NO_ONE);
|
||||
v4_fabric_interrupt_error(&f, V4_ERROR_INTERRUPTED);
|
||||
place = host_born(0);
|
||||
CHECK(place == 0, "a node is born");
|
||||
hera_place = (unsigned)place;
|
||||
@@ -506,8 +506,8 @@ int main(void)
|
||||
/* a node is killed while its neighbour is writing to it */
|
||||
step_limit = 3000000ul;
|
||||
CHECK(strcmp(tell(14, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 14 is stuck in a loop that never ends");
|
||||
CHECK(strcmp(tell(12, ": TO14 S\" 65 EMIT\" 14 SEND ; TO14"), "(still running)") == 0 && v4_node_in_wait(node_numbered(12)) && !node_numbered(12)->asking,
|
||||
"node 12, beside it, sends it text: 14 never reads, and 12 waits to begin the message, in the wait");
|
||||
CHECK(strcmp(tell(12, ": TO14 S\" 65 EMIT\" 14 SEND ; TO14"), "(still running)") == 0 && node_numbered(12)->asking,
|
||||
"node 12, beside it, sends it text: 14 never reads, and 12 is blocked writing to it");
|
||||
{
|
||||
unsigned born = born_count;
|
||||
const char *r = tell(10, "14 KILL 65 EMIT");
|
||||
@@ -525,34 +525,21 @@ int main(void)
|
||||
CHECK(strstr(tell(10, ": H14 S\" 1\" 14 SEND ; H14"), "Argument out of range") != NULL, "Hera herself has no way to it");
|
||||
}
|
||||
|
||||
/* two nodes each send a third text and are then stuck: the third has an answer for each that it
|
||||
* cannot give, and goes on doing what others send it (MESH.md 7c). While a node spins the fabric
|
||||
* never rests: said() answers with what was printed. */
|
||||
/* a node waits on one that is stuck and is not its neighbour; Hera kills the stuck one */
|
||||
step_limit = 3000000ul;
|
||||
CHECK(strcmp(tell(12, ": S11 S\" 1 DROP\" 11 SEND BEGIN 0 UNTIL ; S11"), "(still running)") == 0 &&
|
||||
strcmp(tell(13, ": S11 S\" 1 DROP\" 11 SEND BEGIN 0 UNTIL ; S11"), "(still running)") == 0, "nodes 12 and 13, either side of node 11, each send it text and are then stuck in a loop");
|
||||
CHECK(node_numbered(11)->mem[MQ_COUNT] == 18 && v4_node_in_wait(node_numbered(11)), "node 11 keeps its answer to each, offered, and sleeps: %ld cells", (long)node_numbered(11)->mem[MQ_COUNT]);
|
||||
CHECK(strcmp(said(11, "11 100 * ."), "1100 ") == 0 && ended == 1 && node_numbered(11)->mem[MQ_COUNT] == 18, "text typed for node 11 is done all the same: \"%s\"", printed);
|
||||
|
||||
/* a node waits on one that is stuck; text for the stuck one waits with Hera, who goes on; Hera kills it */
|
||||
CHECK(said(11, ": W13 13 AWAIT . ; W13")[0] == 0 && ended == 0 && node_numbered(11)->mem[AWAIT_FROM] == 13 && waiting(11), "node 11 waits for node 13's answer");
|
||||
{
|
||||
v4_cell kept = node_numbered(10)->mem[MQ_COUNT];
|
||||
(void)tell(13, "1 DROP");
|
||||
CHECK(ended == 0 && v4_node_in_wait(node_numbered(10)) && !node_numbered(10)->asking && node_numbered(10)->mem[MQ_COUNT] > kept,
|
||||
"text typed for the stuck node waits with Hera, offered: she sleeps in the wait, not blocked writing");
|
||||
}
|
||||
CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && ended == 1, "and text for Hera herself is done meanwhile: \"%s\"", printed);
|
||||
(void)tell(10, "13 KILL 65 EMIT");
|
||||
CHECK(node_numbered(13) == NULL && strstr(printed, "Node gone") != NULL && node_numbered(11)->mem[AWAIT_FROM] == 0,
|
||||
"Hera kills node 13: node 11's wait ends, with an error that says the node is gone: \"%s\"", printed);
|
||||
CHECK(strstr(printed, "A") != NULL && ended == 2 && node_numbered(10)->mem[MQ_COUNT] == 9 && v4_node_in_wait(node_numbered(10)),
|
||||
"her line is not held by node 12, which is stuck and has to be told too: that message waits with her, offered, and what she kept for node 13 is let go: %ld cells",
|
||||
(long)node_numbered(10)->mem[MQ_COUNT]);
|
||||
CHECK(strcmp(said(11, "11 100 * ."), "1100 ") == 0 && strcmp(said(10, "1 2 + ."), "3 ") == 0, "and the others go on");
|
||||
CHECK(strcmp(tell(13, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 13 is stuck in a loop");
|
||||
CHECK(strcmp(tell(12, ": W13 13 AWAIT . ; W13"), "(still running)") == 0 && node_numbered(12)->mem[AWAIT_FROM] == 13 && waiting(12),
|
||||
"node 12, which is not wired to it, waits for its answer");
|
||||
(void)tell(10, "13 KILL");
|
||||
CHECK(node_numbered(13) == NULL && strstr(printed, "Node gone") != NULL && node_numbered(12)->mem[AWAIT_FROM] == 0 && waiting(12),
|
||||
"Hera kills node 13: node 12's wait ends, with an error that says the node is gone: \"%s\"", printed);
|
||||
step_limit = 30000000ul;
|
||||
CHECK(strcmp(tell(12, "12 100 * ."), "1200 ") == 0 && strcmp(tell(11, "11 100 * ."), "1100 ") == 0, "and the others go on");
|
||||
|
||||
/* Hera's own wait, on a node that is stuck: a line from the console ends it */
|
||||
CHECK(said(10, "12 AWAIT .")[0] == 0 && ended == 0 && node_numbered(10)->mem[AWAIT_FROM] == 12 && waiting(10), "Hera waits for node 12's answer, which will not come");
|
||||
step_limit = 3000000ul;
|
||||
CHECK(strcmp(tell(12, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 12 is stuck in a loop");
|
||||
CHECK(strcmp(tell(10, "12 AWAIT ."), "(still running)") == 0 && node_numbered(10)->mem[AWAIT_FROM] == 12 && waiting(10), "Hera waits for its answer, which will not come");
|
||||
{
|
||||
const char *r = tell(10, "12 KILL 66 EMIT");
|
||||
CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "B") != NULL && strstr(r, "Interrupted\n") < strstr(r, "B") && ended == 2,
|
||||
@@ -560,14 +547,12 @@ int main(void)
|
||||
CHECK(node_numbered(12) == NULL && node_numbered(10)->mem[AWAIT_FROM] == 0, "the line killed the stuck node");
|
||||
}
|
||||
step_limit = 30000000ul;
|
||||
CHECK(strcmp(tell(11, "11 100 * ."), "1100 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0 && node_numbered(11)->mem[MQ_COUNT] == 0 && node_numbered(10)->mem[MQ_COUNT] == 0 && waiting(10) && waiting(11),
|
||||
"the two that are left go on, and keep nothing for the nodes that are gone");
|
||||
|
||||
/* Hera's line must begin a message to a node that is stuck: it waits, and a line from the console ends it (MESH.md 7c.3) */
|
||||
/* Hera blocked writing to a node that is stuck: a line from the console lets her go (MESH.md 7b.7) */
|
||||
step_limit = 3000000ul;
|
||||
CHECK(strcmp(tell(11, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 11 is stuck in a loop");
|
||||
CHECK(strcmp(tell(10, ": T11 S\" 1 DROP\" 11 SEND 67 EMIT ; T11"), "(still running)") == 0 && v4_node_in_wait(node_numbered(10)) && !node_numbered(10)->asking,
|
||||
"Hera sends it text: it never reads, and she waits to begin the message, in the wait");
|
||||
CHECK(strcmp(tell(10, ": T11 S\" 1 DROP\" 11 SEND 67 EMIT ; T11"), "(still running)") == 0 && node_numbered(10)->asking,
|
||||
"Hera sends it text: its number is the higher, so she writes without looking, and is blocked");
|
||||
{
|
||||
const char *r = tell(10, "11 KILL 68 EMIT");
|
||||
CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "D") != NULL && strstr(r, "C") == NULL && strstr(r, "Interrupted\n") < strstr(r, "D"),
|
||||
|
||||
Reference in New Issue
Block a user