feat(v4.0.0): a node looks before it writes; two neighbours no longer stop each other
MESH.md step 4, the fault found there, as ruled (section 7a): a node writes only to a neighbour that is reading, keeps what it takes in meanwhile, and loses and counts what it has no room for. Engine: two more addresses after a node's ports -- which ports have a neighbour waiting to write to it, which to read from it. Nucleus: (GATE) before every message; the messages waiting, a ring of 400 cells, dealt with when the node is idle. Of two neighbours the lower number may wait to write (ruled after it was built); NEIGHBOUR tells a node who is on each port. test_host_mesh.c, 44 checks: the case that stopped the nodes passes; six messages from each node to each at once all arrive; 800 at once, the nodes come to rest and every message arrived or was counted (287 arrived, 714 of all kinds let go). test_fabric.c: the two looks. Both widths, ASan+UBSan. POST: twelve cases handed HERE, a cell address on v4, to words that take a byte address, and so wrote into or read from the nucleus's code at cell HERE/4. Ruled: left out, marked OPEN, until HERE and the byte words are made to agree as its own step. POST is 538 cases. hosted-check on three ISAs, 538/538. Bare metal: logs/20261006-134817 (amd64), -135044 (aarch64), -135440 (riscv64). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
630d03fa4e
commit
f9c034cadd
+66
-66
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-10-06T15:58:11Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-10-06T17:54:02Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
@@ -30,7 +30,7 @@
|
||||
| `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` | n/a |
|
||||
| `user-font-demo.4th` | 4200, 4201, 4202 | `0xce1fd7d1b581a56d` | n/a |
|
||||
| `v4:forth79.4th` | 6000, 6001, 6002 | `0x4055641ee17d176b` | n/a |
|
||||
| `v4:post79.4th` | 7000, 7001, 7002, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 7011, 7012, 7013, 7014, 7015, 7016, 7017, 7018, 7019, 7020, 7021, 7022, 7023, 7024, 7025, 7026, 7027, 7028, 7029, 7030, 7031, 7032, 7033, 7034, 7035, 7036, 7037, 7038, 7039, 7040, 7041, 7042, 7043, 7044, 7045, 7046, 7047, 7048, 7049, 7050, 7051, 7052, 7053, 7054, 7055, 7056, 7057, 7058, 7059, 7060, 7061, 7062, 7063 | `0xb987dbb4388990bd` | n/a |
|
||||
| `v4:post79.4th` | 7000, 7001, 7002, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 7011, 7012, 7013, 7014, 7015, 7016, 7017, 7018, 7019, 7020, 7021, 7022, 7023, 7024, 7025, 7026, 7027, 7028, 7029, 7030, 7031, 7032, 7033, 7034, 7035, 7036, 7037, 7038, 7039, 7040, 7041, 7042, 7043, 7044, 7045, 7046, 7047, 7048, 7049, 7050, 7051, 7052, 7053, 7054, 7055, 7056, 7057, 7058, 7059, 7060, 7061, 7062, 7063 | `0xb26d1b974884179d` | n/a |
|
||||
| `workload-0.4th` | 2200, 2201 | `0x93f86f60aeba8feb` | n/a |
|
||||
| `workload-1-lite.4th` | 5058, 5059 | `0x44a7a7e3176dcc8d` | n/a |
|
||||
| `workload-1.4th` | 4406, 4415, 4425, 4435 | `0x63e251adb0a03613` | n/a |
|
||||
@@ -371,70 +371,70 @@
|
||||
| 6000 | `v4:forth79.4th` | `0x4055641ee17d176b` | ok |
|
||||
| 6001 | `v4:forth79.4th` | `0x4055641ee17d176b` | ok |
|
||||
| 6002 | `v4:forth79.4th` | `0x4055641ee17d176b` | ok |
|
||||
| 7000 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7001 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7002 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7003 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7004 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7005 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7006 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7007 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7008 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7009 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7010 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7011 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7012 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7013 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7014 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7015 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7016 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7017 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7018 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7019 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7020 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7021 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7022 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7023 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7024 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7025 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7026 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7027 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7028 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7029 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7030 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7031 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7032 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7033 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7034 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7035 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7036 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7037 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7038 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7039 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7040 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7041 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7042 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7043 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7044 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7045 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7046 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7047 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7048 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7049 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7050 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7051 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7052 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7053 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7054 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7055 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7056 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7057 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7058 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7059 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7060 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7061 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7062 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7063 | `v4:post79.4th` | `0xb987dbb4388990bd` | ok |
|
||||
| 7000 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7001 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7002 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7003 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7004 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7005 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7006 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7007 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7008 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7009 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7010 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7011 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7012 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7013 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7014 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7015 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7016 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7017 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7018 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7019 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7020 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7021 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7022 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7023 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7024 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7025 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7026 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7027 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7028 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7029 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7030 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7031 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7032 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7033 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7034 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7035 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7036 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7037 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7038 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7039 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7040 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7041 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7042 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7043 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7044 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7045 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7046 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7047 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7048 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7049 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7050 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7051 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7052 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7053 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7054 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7055 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7056 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7057 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7058 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7059 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7060 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7061 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7062 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7063 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
|
||||
## Conflicts
|
||||
|
||||
|
||||
Binary file not shown.
+94
-132
File diff suppressed because it is too large
Load Diff
+83
-46
@@ -213,38 +213,60 @@ outer nodes, and for each other unit the port toward that unit's centre.
|
||||
A different geometry is a different way of filling in the wiring table
|
||||
and these tables. Nothing else changes.
|
||||
|
||||
## 7a. Two neighbours writing to each other (open; awaits a ruling)
|
||||
## 7a. Two neighbours writing to each other (ruled 2026-10-06)
|
||||
|
||||
The fault is in section 10, step 4. Three ways out, none built:
|
||||
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
|
||||
meanwhile, and one there is no room for is lost and counted.**
|
||||
|
||||
**A. A node writes only to a neighbour that is already reading.** The
|
||||
engine gives a node two more things to fetch, as the F18's `io` register
|
||||
does: which ports have a neighbour writing, and which have a neighbour
|
||||
reading. A node with a message to send looks first. If the neighbour is
|
||||
reading, it writes, and the write does not wait. If the neighbour is
|
||||
writing to it instead, it takes that message in first, and deals with it,
|
||||
and looks again. A node therefore holds messages waiting to go, in its own
|
||||
memory, and what it does when that room is full has to be decided (the
|
||||
message is lost and counted, as one with nowhere to go is now). Writes
|
||||
still block, as ruled; a node simply does not start one it cannot finish.
|
||||
This is how it is done on the chip. It is all in the nucleus but for the
|
||||
two fetches.
|
||||
**As built.**
|
||||
|
||||
**B. Passing on is not the node's programme's work.** `DECOMPOSITION.md`
|
||||
6.1 says "the router in each node forwards packets". Read as a thing apart
|
||||
from the programme -- logic beside the F18 on the FPGA, a part of the
|
||||
fabric in the model -- it takes in and sends on whatever the programme is
|
||||
doing, and holds what is for the programme until it reads. The programme's
|
||||
own writes then never wait on a neighbour's programme. This is more
|
||||
hardware per node and takes passing-on out of F18 code, and out of what a
|
||||
capsule can change.
|
||||
- *The engine.* Two more addresses follow a node's ports, as the F18's
|
||||
`io` register would give them: which ports have a neighbour waiting to
|
||||
write to this node, and which have one waiting to read from it, a bit to
|
||||
a port. Fetching them waits for nothing and changes nothing
|
||||
(`v4/src/node.c`; the fabric keeps them, `v4/src/fabric.c`). A device
|
||||
that takes what is written to it shows as waiting to read.
|
||||
- *Looking before writing* (`(GATE)`, `v4/capsule/core.v4`). Before a node
|
||||
begins any message it takes in every message a neighbour is waiting to
|
||||
write to it; then it writes if the neighbour it means to write to is
|
||||
waiting to read; and if not, it looks again. Once a message is begun it
|
||||
is written to its end: the neighbour that took its first word reads the
|
||||
rest.
|
||||
- *The messages waiting* (`(MQ)`). What a node takes in is kept in a ring
|
||||
of 400 cells of its own memory -- for each message its seven words, the
|
||||
port it came on, and its text -- and dealt with, oldest first, when the
|
||||
node has nothing else to do. A node with none waiting is blocked reading
|
||||
its ports, as before. One there is no room for is read to its end, let
|
||||
go, and counted in `(LOST)`.
|
||||
|
||||
**C. No answer to a `SEND`.** It removes the case found and leaves the
|
||||
fault: any two messages crossing still stop both nodes. Not offered as a
|
||||
way out; listed so that it is seen to have been weighed.
|
||||
**What had to be added to the ruling, and why.** As put to Captain Bob the
|
||||
rule was "write only to a neighbour that is reading, and look again if it
|
||||
is not". That is not enough: two neighbours each with a message for the
|
||||
other would each look, see the other not reading, and look again, for
|
||||
ever. No rule that treats both ends of a wire alike can get out of that.
|
||||
So one end of every wire may write without waiting for a reader, and one
|
||||
only: **the node with the lower number.** A node that waits to write is
|
||||
then always waiting on a higher number, so no ring of nodes can all be
|
||||
waiting on each other, and the highest of any that wait is not waiting to
|
||||
write: it is looking, and takes in what is being written to it.
|
||||
|
||||
Recommended: A. It keeps every ruling as it stands, keeps a node an F18
|
||||
and nothing more, and what it adds to the engine the chip already has.
|
||||
For this a node must know the number of the node on each of its ports.
|
||||
Whoever wires it tells it, as it is told the ways: `NEIGHBOUR ( node port
|
||||
-- )`, `v4/capsule/quit.v4`. A port it has not been told of -- a device's
|
||||
-- is written to only when what is there is waiting to read. **Two nodes
|
||||
wired together and not told of each other can still stop each other**,
|
||||
each looking for ever; they execute, but nothing passes. Telling them is
|
||||
part of wiring them. Put to Captain Bob after it was built, and ruled:
|
||||
"1 is fine."
|
||||
|
||||
**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; 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.
|
||||
|
||||
## 8. Storage
|
||||
|
||||
@@ -344,8 +366,7 @@ Each is tested, committed and pushed before the next.
|
||||
input registers, not a message. A message not for this node, or not
|
||||
text, is let go: passing it on is step 4.
|
||||
4. **Finding the way (section 7).**
|
||||
**Built 2026-10-06; NOT done: one fault stands, below, and it needs a
|
||||
ruling.** Each node has a table of up to 16 destinations and the port
|
||||
**Built 2026-10-06.** Each node has a table of up to 16 destinations and the port
|
||||
toward each, and one port for everything else (`ROUTE ( node port -- )`,
|
||||
`DEFAULT-ROUTE ( port -- )`, `NO-ROUTES`; `(PORT-FOR)` in
|
||||
`v4/capsule/core.v4`). A message not for this node is written, whole,
|
||||
@@ -365,23 +386,39 @@ Each is tested, committed and pushed before the next.
|
||||
unchanged: `hosted-check` on three ISAs; bare metal
|
||||
`logs/20261006-115225` (amd64), `-115501` (aarch64), `-115849`
|
||||
(riscv64).
|
||||
**THE FAULT: two neighbours that write to each other at once wait for
|
||||
ever.** A write blocks until the neighbour reads (ruling, section 4.1),
|
||||
and a node that is blocked writing is not reading. Passing a message on
|
||||
is done here by the node's own programme, so a node cannot pass
|
||||
anything on, or take anything in, while it waits to write. Found so: the
|
||||
far node `SEND`s text to the middle one and then goes to send word of
|
||||
how its own text ended, which goes by the middle one; the middle one has
|
||||
done the text and goes to answer the far one. Each is writing the first
|
||||
word of a message to the other. It is not peculiar to `SEND`: any two
|
||||
messages going opposite ways on one wire at the same moment do it, and
|
||||
so would a ring of nodes each writing to the next. The last check in
|
||||
`test_host_mesh.c` shows it and is named KNOWN FAULT; it is there so the
|
||||
fault is on record and is seen to go.
|
||||
It is not a slip in the code. It is what blocking writes and a
|
||||
programme that does one thing at a time give. Section 7 said nothing
|
||||
about it and should have. What is to be done about it is not decided:
|
||||
the choices are in section 7a.
|
||||
**The fault found here, and put right 2026-10-06 (section 7a).** Two
|
||||
neighbours that wrote to each other at once waited for ever: a write
|
||||
blocks until the neighbour reads, and a node that is blocked writing is
|
||||
not reading. Found when the far node `SEND`s text to the middle one and
|
||||
then sends word of how its own text ended, which goes by the middle
|
||||
one, while the middle one answers the far one. Ruled: a node writes only
|
||||
to a neighbour that is reading, keeps what it takes in meanwhile, and
|
||||
loses and counts what it has no room for. Built so, with one thing
|
||||
added that the ruling needs (section 7a): of two neighbours the lower
|
||||
number may wait to write, and `NEIGHBOUR` tells a node who is on each
|
||||
port. `test_host_mesh.c`, 44 checks: the case that stopped the nodes
|
||||
now passes; every node sending every other six messages at once, all
|
||||
arrive; 800 at once, the nodes come to rest and every message either
|
||||
arrived or was counted. `test_fabric.c`: a node sees who is waiting to
|
||||
write to it and who to read from it, and looking disturbs neither.
|
||||
The prompt and `EMIT` take no more of the data stack than they did.
|
||||
All v4 tests at both widths and under ASan and UBSan; `hosted-check` on
|
||||
three ISAs; bare metal, with lines typed at each prompt,
|
||||
`logs/20261006-134817` (amd64), `-135044` (aarch64), `-135440`
|
||||
(riscv64).
|
||||
**Found on the way: POST was writing into the nucleus's own code.** On
|
||||
v4 `HERE` is a cell address and `C!`, `FILL` and `CMOVE` take byte
|
||||
addresses (D-1), so cases such as `65 HERE C!` wrote their bytes into
|
||||
the nucleus at cell `HERE/4`, read them back from there, and passed.
|
||||
`65 HERE C!` was watched changing cell 2223 during POST; the other
|
||||
cases of the same form were not watched, and the boots committed
|
||||
before this were not gone back to. It showed only when this step moved
|
||||
other code onto that cell and POST stopped. Ruled: the twelve cases
|
||||
are left out, marked OPEN (`v4/tools/post79_rules.py`,
|
||||
`docs/v4.0.0/POST79.md` section 4), and how `HERE` and the byte words
|
||||
are to agree is its own step. POST is 538 cases, not 550. `C!` and
|
||||
`FILL` now have fewer cases; nothing stops any other text doing what
|
||||
those cases did.
|
||||
5. **Birth and Hera's requests (section 9); the unit of five.**
|
||||
6. **Storage (section 8).**
|
||||
7. **A second unit; scaling while running; sleep, wake and kill by
|
||||
|
||||
+17
-5
@@ -3,7 +3,7 @@
|
||||
Written by `v4/tools/mkpost.py` from `v4/tools/post79_rules.py`; do not edit.
|
||||
Design: `NUCLEUS.md` section 6. The capsule is `capsules/v4/post79.4th`.
|
||||
|
||||
POST runs 550 cases. 443 are v3's, with what the hosted v3 binary did as the
|
||||
POST runs 538 cases. 431 are v3's, with what the hosted v3 binary did as the
|
||||
expected result. This file lists every other case, and why.
|
||||
|
||||
## 1. Rewritten for FORTH-79 (59)
|
||||
@@ -60,11 +60,11 @@ standard's and not v3's.
|
||||
| `CMOVE.basic` | `HERE S" Test" DUP >R HERE 10 + SWAP CMOVE CR` | stack `325`, printed `\n` | `65 PAD C! 66 PAD 1+ C! PAD PAD 10 + 2 CMOVE PAD 10 + C@ . PAD 11 + C@ . CR` | stack `empty`, prints `65 66 \n` | v3's address unit is a byte; a v4 address unit is a cell (D-1); and the v3 case leaves a value on the return stack |
|
||||
| `TYPE.basic_string` | `HERE S" Hello" DUP >R HERE SWAP CMOVE HERE R> TYPE CR` | stack `400`, printed `Hello\n` | `72 PAD C! 105 PAD 1+ C! PAD 2 TYPE CR` | stack `empty`, prints `Hi\n` | v3's address unit is a byte; a v4 address unit is a cell (D-1); and FORTH-79: >R R> R@ are for use inside a definition; v4 refuses them at the prompt |
|
||||
| `TYPE.numbers` | `HERE S" 12345" DUP >R HERE SWAP CMOVE HERE R> TYPE CR` | stack `406`, printed `12345\n` | `49 PAD C! 50 PAD 1+ C! PAD 2 TYPE CR` | stack `empty`, prints `12\n` | v3's address unit is a byte; a v4 address unit is a cell (D-1); and FORTH-79: >R R> R@ are for use inside a definition; v4 refuses them at the prompt |
|
||||
| `>IN.initial` | `>IN @ . CR` | stack `empty`, printed `852597488 \n` | unchanged | stack `empty`, prints `9 \n` | FORTH-79: >IN is the offset into the input, 9 here; v3 prints an address |
|
||||
| `>IN.initial` | `>IN @ . CR` | stack `empty`, printed `1009040112 \n` | unchanged | stack `empty`, prints `9 \n` | FORTH-79: >IN is the offset into the input, 9 here; v3 prints an address |
|
||||
| `UPDATE.no_block` | `0 SCR ! UPDATE` | error | unchanged | stack `empty`, prints nothing | v3 raises an error FORTH-79 does not ask for |
|
||||
| `SCR.after_load` | `1 LOAD SCR @ . CR` | stack `empty`, printed `` | unchanged | stack `empty`, prints `1 \n` | v3 prints nothing after 1 LOAD; SCR is still 1 from the LIST before |
|
||||
| `FIND.existing` | `FIND DUP . CR` | stack `empty`, printed `852596848 \n` | unchanged | 0 value(s) left; output not compared | FORTH-79: FIND takes the next word, DUP here, and leaves its address, which is printed |
|
||||
| `FIND.user_word` | `: test5 44 ; FIND test5 . CR` | stack `empty`, printed `852923888 \n` | unchanged | 0 value(s) left; output not compared | the address FIND leaves is printed |
|
||||
| `FIND.existing` | `FIND DUP . CR` | stack `empty`, printed `1009039472 \n` | unchanged | 0 value(s) left; output not compared | FORTH-79: FIND takes the next word, DUP here, and leaves its address, which is printed |
|
||||
| `FIND.user_word` | `: test5 44 ; FIND test5 . CR` | stack `empty`, printed `1009366512 \n` | unchanged | 0 value(s) left; output not compared | the address FIND leaves is printed |
|
||||
| `FIND.empty` | `FIND` | error | unchanged | stack `0`, prints nothing | FORTH-79: FIND leaves 0 when there is no word; v3 raises an error |
|
||||
| `VOCABULARY.cross_vocab_access` | `VOCABULARY V1 V1 DEFINITIONS : V1WORD 11 ; FORTH V1WORD . CR` | stack `empty`, printed `11 \n` | unchanged | an error | FORTH-79: a word defined in another vocabulary is not found from FORTH; v3 finds it |
|
||||
| `VOCABULARY.duplicate` | `VOCABULARY TESTVOC VOCABULARY TESTVOC` | error | unchanged | stack `empty`, prints nothing | v3 raises an error FORTH-79 does not ask for: defining a name again |
|
||||
@@ -139,7 +139,7 @@ standard's; no v3 run stands behind them.
|
||||
| `WORD.count` | `: W3 BL WORD C@ . ; W3 ABC` | stack `empty`, prints `3 ` |
|
||||
| `WORD.skips_leading` | `: W4 BL WORD COUNT TYPE ; W4 X` | stack `empty`, prints `X` |
|
||||
|
||||
## 4. v3 cases left out (27)
|
||||
## 4. v3 cases left out (39)
|
||||
|
||||
| Case | Why |
|
||||
|---|---|
|
||||
@@ -155,7 +155,19 @@ standard's; no v3 run stands behind them.
|
||||
| `].basic` | v3 did not get through it in one session |
|
||||
| `ELSE.double` | v3 did not get through it in one session |
|
||||
| `THEN.extra` | v3 did not get through it in one session |
|
||||
| `C!.basic` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 |
|
||||
| `C!.zero` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 |
|
||||
| `C!.high_byte` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 |
|
||||
| `C!.truncation` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 |
|
||||
| `C@.after_cstore` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 |
|
||||
| `C@.zero_byte` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 |
|
||||
| `FILL.basic` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 |
|
||||
| `FILL.zero_byte` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 |
|
||||
| `COUNT.string_bounds` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 |
|
||||
| `CMOVE.overlap` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 |
|
||||
| `CMOVE.bounds` | moves 1000 bytes over the system; v3 refuses by a bounds rule FORTH-79 has not got |
|
||||
| `TYPE.single_char` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 |
|
||||
| `COUNT.zero_addr_plus_one` | OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): the case wrote to or read from the nucleus's code at cell HERE/4 |
|
||||
| `WORD.empty_input` | v3's WORD does not take the next word from the input, as FORTH-79's does; see the WORD cases written by hand |
|
||||
| `WORD.space_delim` | v3's WORD does not take the next word from the input, as FORTH-79's does; see the WORD cases written by hand |
|
||||
| `WORD.newline_delim` | v3's WORD does not take the next word from the input, as FORTH-79's does; see the WORD cases written by hand |
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
[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 6 2026 13:47:51
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 131
|
||||
Total memory: 1023 MB
|
||||
Usable memory: 968 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 249251
|
||||
Free pages : 247728
|
||||
Used pages : 1523
|
||||
Total MB : 973
|
||||
Free MB : 967
|
||||
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=2101165142 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=1007014520, tick_hz=100, initial_count=10070145
|
||||
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=0xce1adfd156a70639 capsule_hash=0xce1adfd156a70639 words=306
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x24314ed49017b4ec
|
||||
V4: capsule v4:post79.4th signature: missing (unsigned)
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0x4bf51f6674743728
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
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> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,104 @@
|
||||
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 6 2026 13:50:19
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 108
|
||||
Total memory: 4093 MB
|
||||
Usable memory: 4056 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 1039865
|
||||
Free pages : 1038454
|
||||
Used pages : 1411
|
||||
Total MB : 4061
|
||||
Free MB : 4056
|
||||
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=0xce1adfd156a70639 capsule_hash=0xce1adfd156a70639 words=306
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x24314ed49017b4ec
|
||||
V4: capsule v4:post79.4th signature: missing (unsigned)
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0x4bf51f6674743728
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
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> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,174 @@
|
||||
|
||||
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 = 0x00000000bdea78f8
|
||||
riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a)
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: riscv64
|
||||
Build: Oct 6 2026 13:54:02
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 100
|
||||
Total memory: 1020 MB
|
||||
Usable memory: 977 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 250782
|
||||
Free pages : 250130
|
||||
Used pages : 652
|
||||
Total MB : 979
|
||||
Free MB : 977
|
||||
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=0xce1adfd156a70639 capsule_hash=0xce1adfd156a70639 words=306
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x24314ed49017b4ec
|
||||
V4: capsule v4:post79.4th signature: missing (unsigned)
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0x4bf51f6674743728
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
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> BYE
|
||||
BYE: cold restart
|
||||
+9
-7
@@ -61,7 +61,7 @@ load `v4:forth79.4th`, pass POST and reach `ok>`, with the same lines:
|
||||
```
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=... capsule_hash=0x97a655081b2af559 words=298
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=... dict_hash=...
|
||||
PARITY:V4_POST tests=550 pass=550 fail=0
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=... dict_hash=...
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
@@ -115,11 +115,13 @@ steps 8 and 9.
|
||||
**Finding the way** (`MESH.md` step 4) is built: a node has a table of
|
||||
which port leads toward which node (`ROUTE`, `DEFAULT-ROUTE`, `NO-ROUTES`),
|
||||
passes on a message that is not for it, and `SEND`s text to another node;
|
||||
`v4/tests/test_host_mesh.c` runs three StarForth nodes in a row. **It has
|
||||
a fault that stands:** two neighbours that write to each other at the same
|
||||
moment wait for ever, because a write blocks until the neighbour reads and
|
||||
a node that is writing is not reading. The test shows it, named KNOWN
|
||||
FAULT. The ways out are in `MESH.md` section 7a and none is chosen yet.
|
||||
`v4/tests/test_host_mesh.c` runs three StarForth nodes in a row. A node
|
||||
looks before it begins a message (`MESH.md` section 7a): it takes in what
|
||||
its neighbours are waiting to write to it, keeps those messages until it
|
||||
has nothing else to do, and writes only to a neighbour that is waiting to
|
||||
read -- or, of two neighbours, the one with the lower number may wait.
|
||||
`NEIGHBOUR ( node port -- )` tells a node who is on each port. A node with
|
||||
no room for one more message lets it go and counts it in `(LOST)`.
|
||||
|
||||
**This is still the lone node.** On bare metal `kernel_main.c` starts it
|
||||
before the fleet tables, beside the kernel's own system and not in the VM's
|
||||
@@ -133,7 +135,7 @@ a time with POST after each, is the next step (`NUCLEUS.md` section 8).
|
||||
|
||||
## POST
|
||||
|
||||
`capsules/v4/post79.4th`: 550 cases in blocks 7000 up, covering 126 of the
|
||||
`capsules/v4/post79.4th`: 538 cases in blocks 7000 up, covering 126 of the
|
||||
130 words of the FORTH-79 Required Word Set. 443 are v3's cases with what
|
||||
the hosted v3 binary did as the expected result. The other 107, and the 27
|
||||
v3 cases left out, are listed with reasons in `docs/v4.0.0/POST79.md`.
|
||||
|
||||
+93
-1
@@ -128,6 +128,98 @@ header CMOVE
|
||||
NONE: drop drop (ROUTE-DEFAULT) a! @ ;
|
||||
HIT: drop drop @+ pop drop ;
|
||||
|
||||
\ 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.
|
||||
\ (MQ-HEAD) is where the oldest begins, (MQ-TAIL) where the next will go,
|
||||
\ (MQ#) how many cells are taken. One that there is no room for is read to
|
||||
\ its end and let go, and (LOST) counts it.
|
||||
|
||||
\ ( mask index -- bit )
|
||||
: (BIT)
|
||||
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! !+
|
||||
a (MQ-END) xor if WRAP drop a jump SET
|
||||
WRAP: drop (MQ)
|
||||
SET: (MQ-TAIL) a! !
|
||||
(MQ#) a! @ 1 + ! ;
|
||||
|
||||
\ ( -- w ) the oldest cell of the messages waiting, which goes
|
||||
: (MQ@)
|
||||
(MQ-HEAD) a! @ a! @+
|
||||
a (MQ-END) xor if WRAP drop a jump SET
|
||||
WRAP: drop (MQ)
|
||||
SET: (MQ-HEAD) a! !
|
||||
(MQ#) a! @ -1 + ! ;
|
||||
|
||||
\ ( port to -- ) take in a message. B is at the port, by its address,
|
||||
\ and `to` is the message's first word, already read from it. A length
|
||||
\ below zero, or above what a message carries, is kept as -1 with no text:
|
||||
\ a longer one is read to its end first, so that what follows it is not
|
||||
\ taken for a message.
|
||||
: (TAKE)
|
||||
(MQ-HDR) a! !+ 5 FOR @b !+ UNEXT ! \ its seven words, and the port
|
||||
(MQ-HDR)+6 a! @ -if SIZED
|
||||
drop -1 (MQ-HDR)+6 a! ! 0 jump COUNTED
|
||||
SIZED: dup -1025 + -if LONG drop 3 + 2/ 2/ jump COUNTED
|
||||
LONG: drop 3 + 2/ 2/ -1 + FOR @b drop UNEXT -1 (MQ-HDR)+6 a! ! 0
|
||||
COUNTED: \ ( n ) how many words of text follow
|
||||
dup (MQ#) a! @ + -MQ-ROOM + -if FULL
|
||||
drop push
|
||||
(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 ;
|
||||
NOTEXT: drop ;
|
||||
FULL: drop if GONE -1 + FOR @b drop UNEXT jump LOSE
|
||||
GONE: drop
|
||||
LOSE: (LOST) a! @ 1 + ! ;
|
||||
|
||||
\ ( 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: (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 jump L
|
||||
|
||||
\ ( 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
|
||||
\ port and sent word 0, whom it is to, and sends the length and the text.
|
||||
@@ -142,7 +234,7 @@ header CMOVE
|
||||
\ lowest. quit.v4 sets those two when text for this node arrives.
|
||||
: (FLUSH-OUT)
|
||||
(OUT^) a! @ (OUT) xor if NONE drop
|
||||
(PRINT-PORT) a! @ b! (PRINT-TO) a! @ !b
|
||||
(PRINT-PORT) a! @ (GATE) (PRINT-TO) a! @ !b
|
||||
2 (HDR)
|
||||
(OUT^) a! @ (OUT) - dup !b
|
||||
3 + 2/ 2/ -1 + (OUT) a!
|
||||
|
||||
+37
-27
@@ -65,7 +65,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
: (FINISH) ( s -- )
|
||||
(LINE-STATUS) b! dup !b
|
||||
(FLUSH-OUT)
|
||||
(DONE-PORT) a! @ b! (MSG)+1 a! @ !b \ to whoever sent the text
|
||||
(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)
|
||||
|
||||
@@ -78,29 +78,30 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
OK: drop 1 jump (FINISH)
|
||||
GO: drop 0 jump (FINISH)
|
||||
|
||||
\ ( -- ) WAITING. A node with nothing to do is blocked reading its ports
|
||||
\ (docs/v4.0.0/MESH.md 4.1): it executes nothing until a neighbour writes.
|
||||
\ What arrives is a message (core.v4). Its first word is taken from any
|
||||
\ port and the rest from the port that came on. Its text goes into TIB,
|
||||
\ which holds 1024 characters, the most a message carries; a longer one is
|
||||
\ read to its end, so that what follows it is not taken for a message, and
|
||||
\ 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).
|
||||
\ ( -- ) 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)
|
||||
(PORT)+8 b! @b (MSG) a! !+ \ to, from any port
|
||||
(PORT)+9 b! @b (PORT) + dup b! \ B: the port it came on
|
||||
5 FOR @b !+ UNEXT \ from, type, heat and TTL, ACL tag, sequence, length
|
||||
(REPLY) a! !
|
||||
(MSG)+6 a! @ -if SIZED jump BAD \ a length below zero: no telling what follows
|
||||
SIZED: dup -1025 + -if LONG drop jump FITS
|
||||
LONG: drop 3 + 2/ 2/ -1 + FOR @b drop UNEXT 0 \ too long: read it and let it go
|
||||
BAD: drop 0 (MSG)+6 a! ! 0 SPAN a! ! NODE-ERROR b! 12 !b
|
||||
FITS:
|
||||
3 + 2/ 2/ if EMPTY
|
||||
-1 + TIB 2/ 2/ a! FOR @b !+ UNEXT
|
||||
jump READ
|
||||
EMPTY: drop
|
||||
L: (MQ#) a! @ if WAIT drop jump HAVE
|
||||
WAIT: 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! !
|
||||
@@ -124,7 +125,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
\ known it is let go, and (LOST) counts it.
|
||||
: (PASS-ON)
|
||||
(MSG) a! @ (PORT-FOR) if NOWAY
|
||||
b! (MSG) a! 6 FOR @+ !b UNEXT \ its seven words
|
||||
(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)
|
||||
@@ -156,6 +157,16 @@ header ROUTE
|
||||
(ROUTE#) a! @ 1 + ! ;
|
||||
FULL: drop drop drop NODE-ERROR b! 12 !b ;
|
||||
|
||||
\ ( 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 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
|
||||
POS: -PORTS + -if BAD drop (NEAR) + a! ! ;
|
||||
BAD: drop drop drop NODE-ERROR b! 12 !b ;
|
||||
|
||||
\ ( port -- ) the port, by its number, leads toward every node not told of
|
||||
header DEFAULT-ROUTE
|
||||
: DEFAULT-ROUTE (PORT) + (ROUTE-DEFAULT) a! ! ;
|
||||
@@ -167,12 +178,11 @@ header NO-ROUTES
|
||||
\ ( baddr u node -- ) send the node u characters of text to interpret. It
|
||||
\ is on its way when SEND returns; what it prints there goes to that node's
|
||||
\ console. With no way to the node it is error 12. The write waits for the
|
||||
\ neighbour to read: a neighbour that is itself waiting to write to this
|
||||
\ node will wait for ever, and so will this one.
|
||||
\ neighbour to read, and is not begun until it may be (core.v4, (GATE)).
|
||||
header SEND
|
||||
: SEND
|
||||
dup (PORT-FOR) if NOWAY
|
||||
b! !b \ to
|
||||
(GATE) !b \ to
|
||||
1 (HDR)
|
||||
dup !b \ length
|
||||
3 + 2/ 2/ if NONE -1 +
|
||||
|
||||
@@ -109,6 +109,8 @@ typedef struct {
|
||||
v4_cell given_value;
|
||||
unsigned given_port;
|
||||
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 */
|
||||
|
||||
/* DSTACK-DEPTH and RSTACK-DEPTH (D-16). See v4_node_stack_regs_attach. */
|
||||
v4_cell dstack_reg; /* its word address, or -1 */
|
||||
@@ -181,6 +183,13 @@ void v4_node_console_attach(v4_node *n, v4_cell addr);
|
||||
* 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
|
||||
* base + V4_PORTS + 2 which ports have a neighbour waiting to write to this
|
||||
* node: bit k for port k
|
||||
* base + V4_PORTS + 3 which ports have a neighbour waiting to read from it
|
||||
* The last two are how a node looks before it writes (MESH.md 7a), as the
|
||||
* F18's io register lets it. They are what whoever connects the ports last
|
||||
* said, v4_node_port_status; fetching them waits for nothing and changes
|
||||
* nothing.
|
||||
* (read only)
|
||||
* None of them is memory while the ports are attached.
|
||||
*
|
||||
@@ -213,6 +222,11 @@ void v4_node_port_served(v4_node *n);
|
||||
* port or "any", is unblocked and will fetch it. */
|
||||
void v4_node_port_give(v4_node *n, unsigned port, v4_cell value);
|
||||
|
||||
/* What is on the other end of the node's ports now: bit k of `writers` if
|
||||
* the neighbour on port k is blocked writing to this node, bit k of
|
||||
* `readers` if it is blocked reading from it and would take a word. */
|
||||
void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers);
|
||||
|
||||
/* Which port `addr` is: 0 .. V4_PORTS - 1, V4_PORT_ANY, V4_PORTS + 1 for the
|
||||
* "which port" address, or -1 if it is none of them. */
|
||||
int v4_node_port_index(const v4_node *n, v4_cell addr);
|
||||
|
||||
@@ -155,10 +155,34 @@ static unsigned hand_over_read(v4_fabric *f, unsigned a)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* What node `a` can see of its neighbours before it executes: who is
|
||||
* blocked writing to it, and who would take a word from it. A device is
|
||||
* always ready to be written to; what it has to give, the node finds when
|
||||
* it reads. */
|
||||
static void look_about(v4_fabric *f, unsigned a)
|
||||
{
|
||||
unsigned k, writers = 0, readers = 0;
|
||||
|
||||
for (k = 0; k < V4_PORTS; k++) {
|
||||
const v4_wire *w = &f->place[a].wire[k];
|
||||
if (w->kind == V4_WIRE_DEVICE) {
|
||||
if (w->device->take) readers |= 1u << k;
|
||||
} else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) {
|
||||
const v4_node *o = &f->place[w->node].node->n;
|
||||
if (o->asking && o->ask_port == w->port) writers |= 1u << k;
|
||||
if (o->reading && !o->given && (o->read_port == w->port || o->read_port == V4_PORT_ANY)) readers |= 1u << k;
|
||||
}
|
||||
}
|
||||
v4_node_port_status(&f->place[a].node->n, writers, readers);
|
||||
}
|
||||
|
||||
unsigned v4_fabric_step(v4_fabric *f)
|
||||
{
|
||||
unsigned i, done = 0;
|
||||
|
||||
for (i = 0; i < f->capacity; i++)
|
||||
if (awake(f, i)) look_about(f, i);
|
||||
|
||||
for (i = 0; i < f->capacity; i++)
|
||||
if (awake(f, i)) done += v4_exec_step_word(&f->place[i].node->n, &f->place[i].node->es, &f->place[i].node->h);
|
||||
|
||||
|
||||
+12
-2
@@ -159,6 +159,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 >= 0) {
|
||||
/* the word its neighbour wrote; v4_node_read_ready has said it is there */
|
||||
if (!n->given) return 0;
|
||||
@@ -214,18 +216,26 @@ void v4_node_port_attach(v4_node *n, v4_cell base)
|
||||
n->given_value = 0;
|
||||
n->given_port = 0;
|
||||
n->last_from = 0;
|
||||
n->writers = 0;
|
||||
n->readers = 0;
|
||||
}
|
||||
|
||||
void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
|
||||
{
|
||||
n->writers = writers;
|
||||
n->readers = 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_PORTS + 1) 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_PORTS + 1) 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->read_port = (unsigned)port;
|
||||
|
||||
@@ -365,6 +365,10 @@ int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks)
|
||||
say(b, "V4: the nucleus is not for this build of the engine\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
/* what is on its two ports, the kernel and the console, takes a word
|
||||
* when it is written: the node need never wait before writing to them
|
||||
* (node.h, v4_node_port_status) */
|
||||
v4_node_port_status(b->n, 0, 1u << KERNEL_PORT | 1u << CONSOLE_PORT);
|
||||
if (!load_nucleus(b)) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; }
|
||||
|
||||
/* the kernel's words, before any capsule that might use one */
|
||||
|
||||
+31
-3
@@ -74,8 +74,18 @@
|
||||
#define ROUTE_DEFAULT (BUF0_W - 18) /* the port address for a node not in the table, or 0: there is none */
|
||||
#define LOST (BUF0_W - 19) /* how many messages have been let go for want of a way */
|
||||
#define PRINT_TO (BUF0_W - 20) /* where what the text being served prints is to go: the node, */
|
||||
#define PRINT_PORT (BUF0_W - 21) /* and the port address that leads there */
|
||||
#define DONE_PORT (BUF0_W - 22) /* the port address that leads back to whoever sent the text being served */
|
||||
#define WRITERS (PORT + (v4_cell)V4_PORTS + 2) /* which ports have a neighbour waiting to write to this node (node.h) */
|
||||
#define READERS (PORT + (v4_cell)V4_PORTS + 3) /* and which have one waiting to read from it */
|
||||
#define PRINT_PORT (BUF0_W - 33) /* and the port address that leads there */
|
||||
#define DONE_PORT (BUF0_W - 34) /* the port address that leads back to whoever sent the text being served */
|
||||
#define MQ_HEAD (BUF0_W - 35) /* the messages waiting to be dealt with: where the oldest begins, */
|
||||
#define MQ_TAIL (BUF0_W - 36) /* where the next will go, */
|
||||
#define MQ_COUNT (BUF0_W - 37) /* and how many cells they take */
|
||||
#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 */
|
||||
#define MQ_W (OUT_W - 400) /* the messages waiting: each is its seven words, the port it came on, its text */
|
||||
#define MQ_CELLS 400
|
||||
#define ROUTES (BUF0_W - 96) /* the table of ways: 16 entries of a node and the port address that leads to it */
|
||||
#define ROUTE_MAX 16
|
||||
#define ME (BUF0_W - 13) /* this node's number: a message is for it when its first word is this */
|
||||
@@ -90,6 +100,10 @@
|
||||
#define BOOT_CELLS (BVARS + 14) /* (BOOT): DP and LATEST as the loader left them, 2 cells */
|
||||
#define BUF0_W (BVARS - 2 * 256) /* the two block buffers, 256 cells each */
|
||||
#define BUF1_W (BUF0_W + 256)
|
||||
/* the ports' ten addresses and the two that follow them end below BUF0_W - 20 */
|
||||
typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1];
|
||||
/* Nothing printed and no message waiting: a node's variables for them, as at switch-on. */
|
||||
#define HOST_MESSAGES_EMPTY(n) do { (n)->mem[OUT_PTR] = OUT_W; (n)->mem[MQ_HEAD] = MQ_W; (n)->mem[MQ_TAIL] = MQ_W; (n)->mem[MQ_COUNT] = 0; } while (0)
|
||||
#define QVARS (BUF0_W - 4) /* (Q): quit.v4, system.v4 and blocks.v4, 4 cells */
|
||||
#define WBUF (WBUF_W * 4)
|
||||
#define TIB (TIB_W * 4)
|
||||
@@ -103,6 +117,8 @@
|
||||
#ifndef DICT_END_W
|
||||
#define DICT_END_W ((v4_cell)14336)
|
||||
#endif
|
||||
/* the messages waiting are above the dictionary */
|
||||
typedef char host_map_queue_fits[(MQ_W >= DICT_END_W) ? 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. */
|
||||
@@ -123,7 +139,7 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
unsigned i;
|
||||
|
||||
v4_node_reset(n);
|
||||
n->mem[OUT_PTR] = OUT_W; /* nothing has been printed: what is, is kept from here (core.v4) */
|
||||
HOST_MESSAGES_EMPTY(n); /* nothing has been printed and no message waits (core.v4) */
|
||||
v4_text_begin(tx, n, 16);
|
||||
v4_text_constant(tx, "N-1", V4_CELL_BITS - 1);
|
||||
v4_text_constant(tx, "NODE-ERROR", NODE_ERROR);
|
||||
@@ -173,6 +189,18 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
v4_text_constant(tx, "(PRINT-PORT)", PRINT_PORT);
|
||||
v4_text_constant(tx, "(DONE-PORT)", DONE_PORT);
|
||||
v4_text_constant(tx, "(ROUTES)", ROUTES);
|
||||
v4_text_constant(tx, "(WRITERS)", WRITERS);
|
||||
v4_text_constant(tx, "(READERS)", READERS);
|
||||
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, "(MQ)", MQ_W);
|
||||
v4_text_constant(tx, "(MQ-END)", MQ_W + MQ_CELLS);
|
||||
v4_text_constant(tx, "-MQ-ROOM", -(v4_cell)(MQ_CELLS - 7)); /* cells taken + a text's words + this is below zero while the message fits */
|
||||
v4_text_constant(tx, "(MQ-HEAD)", MQ_HEAD);
|
||||
v4_text_constant(tx, "(MQ-TAIL)", MQ_TAIL);
|
||||
v4_text_constant(tx, "(MQ#)", MQ_COUNT);
|
||||
v4_text_constant(tx, "(MQ-HDR)", MQ_HDR);
|
||||
v4_text_constant(tx, "(OUT^)", OUT_PTR);
|
||||
v4_text_constant(tx, "(REPLY)", REPLY);
|
||||
v4_text_constant(tx, "(MSG)", MSG);
|
||||
|
||||
@@ -331,6 +331,29 @@ int main(void)
|
||||
"a port that is not there, a node that is not there, a port to itself: refused");
|
||||
CHECK(v4_fabric_wire(&f, a, 1, a, 2), "but one port of a node may be wired to another of the same node");
|
||||
|
||||
/* ---- a node looks at its neighbours before it writes (MESH.md 7a) ---- */
|
||||
v4_fabric_init(&f, places, 4);
|
||||
a = loaded(0); LIT(PB + 0); O(BANG_B); LIT(5); O(STORE_B); wait_for_ever(); /* writes to its port 0 */
|
||||
c = loaded(1); LIT(PB + 1); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever(); /* reads its port 1 */
|
||||
for (i = 0; i < 10; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.asking && ND(c)->n.reading, "one node is waiting to write and another to read");
|
||||
b = loaded(2);
|
||||
LIT(PB + (v4_cell)V4_PORTS + 2); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A);
|
||||
LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); LIT(OUT + 1); O(BANG_A); O(STORE_A);
|
||||
wait_for_ever();
|
||||
CHECK(v4_fabric_wire(&f, b, 2, a, 0) && v4_fabric_wire(&f, b, 4, c, 1), "a third is wired to both");
|
||||
{
|
||||
static stream none;
|
||||
static v4_device sink;
|
||||
sink.take = stream_take; sink.give = 0; sink.self = &none;
|
||||
CHECK(v4_fabric_wire_device(&f, b, 6, &sink), "and to a device that takes what is written to it");
|
||||
}
|
||||
for (i = 0; i < 20; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(b)->n.mem[OUT] == (1 << 2), "it sees which of its ports has a neighbour waiting to write to it: %ld", (long)ND(b)->n.mem[OUT]);
|
||||
CHECK(ND(b)->n.mem[OUT + 1] == ((1 << 4) | (1 << 6)), "and which have one waiting to read from it, or a device: %ld", (long)ND(b)->n.mem[OUT + 1]);
|
||||
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");
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures != 0;
|
||||
}
|
||||
|
||||
+62
-15
@@ -14,6 +14,7 @@
|
||||
#include "v4/message.h"
|
||||
#include "v4/text.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "host_map.h"
|
||||
@@ -126,7 +127,7 @@ int main(void)
|
||||
nucleus.mem[CURRENT] = LATEST;
|
||||
nucleus.mem[SRC] = TIB;
|
||||
nucleus.mem[LINE_STATUS] = 1;
|
||||
nucleus.mem[OUT_PTR] = OUT_W;
|
||||
HOST_MESSAGES_EMPTY(&nucleus);
|
||||
|
||||
v4_fabric_init(&f, places, 3);
|
||||
hera = starforth_node(0);
|
||||
@@ -196,22 +197,68 @@ int main(void)
|
||||
CHECK(strcmp(tell(12, "5 5 + ."), "10 ") == 0 && pool[1].es.anticlock == before, "text for it no longer passes through the middle node, which executes nothing");
|
||||
}
|
||||
|
||||
/* ---- WHAT DOES NOT WORK: two neighbours that write to each other at once ----
|
||||
/* ---- two neighbours with something for each other at the same moment ----
|
||||
* A write blocks until the neighbour reads, and a node that is writing
|
||||
* is not reading. Here the far node sends text to the middle one and
|
||||
* then goes to send word of how its own text ended, which goes by the
|
||||
* middle one; the middle one does the text and goes to answer the far
|
||||
* one. Each waits for the other to read, for ever. It is the same
|
||||
* whenever messages go both ways on a wire at once, and it is because
|
||||
* passing a message on is done here by the node's own programme
|
||||
* (v4/capsule/quit.v4, (PASS-ON)), which cannot pass anything on while
|
||||
* it waits to write. docs/v4.0.0/MESH.md, step 4. This check is here
|
||||
* so that the fault is on record and is seen to go when it is put
|
||||
* right. */
|
||||
* is not reading: so a node looks before it begins a message, and of
|
||||
* two neighbours only the one with the lower number may wait to write
|
||||
* to the other (v4/capsule/core.v4, (GATE); docs/v4.0.0/MESH.md 7a).
|
||||
* Each node is told who is on each of its ports. */
|
||||
CHECK(is_empty(tell(10, "NO-ROUTES 1 1 ROUTE 11 2 ROUTE 12 2 ROUTE")) && is_empty(tell(12, "NO-ROUTES 2 DEFAULT-ROUTE")), "the row of three again");
|
||||
(void)tell(12, ": HI S\" 72 EMIT\" 11 SEND ; HI");
|
||||
CHECK(pool[1].n.asking && pool[1].n.ask_port == 3 && pool[2].n.asking && pool[2].n.ask_port == 2,
|
||||
"KNOWN FAULT: two neighbours each writing to the other wait for ever");
|
||||
CHECK(is_empty(tell(10, "11 2 NEIGHBOUR 0 3 NEIGHBOUR")) && is_empty(tell(11, "10 2 NEIGHBOUR 12 3 NEIGHBOUR")) && is_empty(tell(12, "11 2 NEIGHBOUR 0 3 NEIGHBOUR")),
|
||||
"each is told who is on the other end of its ports");
|
||||
CHECK(strstr(tell(10, "11 8 NEIGHBOUR"), "Argument out of range") != NULL && strstr(tell(10, "11 -1 NEIGHBOUR"), "Argument out of range") != NULL,
|
||||
"a port a node has not got is an error");
|
||||
/* the far node sends the middle one text, and then word of how its own
|
||||
* text ended, which goes by the middle one; the middle one does the
|
||||
* text and answers the far one: the two messages meet on one wire */
|
||||
CHECK(strcmp(tell(12, ": HI S\" 72 EMIT\" 11 SEND ; HI"), "H") == 0 && printed_from == 11 && ended == 1 && ended_from == 12,
|
||||
"from the far end to the middle, with the answers crossing: \"%s\" from %ld", printed, (long)printed_from);
|
||||
CHECK(waiting(hera) && waiting(mid) && waiting(far), "and all three are waiting again");
|
||||
|
||||
/* every node sends every other a number of messages, all at once: each
|
||||
* counts what it is sent */
|
||||
CHECK(is_empty(tell(10, "VARIABLE GOT : HIT 1 GOT +! ; VARIABLE WHO VARIABLE SENT : BURST WHO ! 0 DO S\" HIT\" WHO @ SEND 1 SENT +! LOOP ;")) &&
|
||||
is_empty(tell(11, "VARIABLE GOT : HIT 1 GOT +! ; VARIABLE WHO VARIABLE SENT : BURST WHO ! 0 DO S\" HIT\" WHO @ SEND 1 SENT +! LOOP ;")) &&
|
||||
is_empty(tell(12, "VARIABLE GOT : HIT 1 GOT +! ; VARIABLE WHO VARIABLE SENT : BURST WHO ! 0 DO S\" HIT\" WHO @ SEND 1 SENT +! LOOP ;")), "each node is given a word to count with and one to send with");
|
||||
{
|
||||
const char *r = tell(10, ": GO S\" 6 10 BURST 6 12 BURST\" 11 SEND S\" 6 11 BURST 6 10 BURST\" 12 SEND 6 11 BURST 6 12 BURST ; GO");
|
||||
CHECK(is_empty(r), "six messages from each node to each other, all at once: \"%s\"", r);
|
||||
CHECK(waiting(hera) && waiting(mid) && waiting(far), "all three come to rest");
|
||||
CHECK(strcmp(tell(10, "GOT @ . (LOST) @ ."), "12 1 ") == 0 && strcmp(tell(11, "GOT @ . (LOST) @ ."), "12 0 ") == 0 && strcmp(tell(12, "GOT @ . (LOST) @ ."), "12 0 ") == 0,
|
||||
"every one of them arrived, and none was let go: \"%s\"", printed);
|
||||
}
|
||||
|
||||
/* ---- more than a node has room to keep ----
|
||||
* A node that takes messages in while it waits to write keeps them, and
|
||||
* when it has no room for one more it lets that one go and counts it.
|
||||
* Far more are sent here than the nodes can keep. */
|
||||
{
|
||||
long got = 0, lost = 0, sent = 0, v;
|
||||
unsigned k;
|
||||
static const v4_cell who[3] = { 10, 11, 12 };
|
||||
const char *r;
|
||||
CHECK(is_empty(tell(10, "0 GOT ! 0 SENT !")) && is_empty(tell(11, "0 GOT ! 0 SENT !")) && is_empty(tell(12, "0 GOT ! 0 SENT !")), "the counts begin again");
|
||||
r = tell(10, ": GO2 S\" 200 10 BURST 200 12 BURST\" 11 SEND S\" 200 11 BURST 200 10 BURST\" 12 SEND 200 11 BURST 200 12 BURST ; GO2");
|
||||
CHECK(strcmp(r, "(still running)") != 0, "two hundred from each to each: it ends");
|
||||
CHECK(waiting(hera) && waiting(mid) && waiting(far), "and all three come to rest, none waiting to write");
|
||||
for (k = 0; k < 3; k++) {
|
||||
r = tell(who[k], "GOT @ .");
|
||||
v = strtol(r, NULL, 10); got += v;
|
||||
CHECK(v > 0 && v <= 400, "node %ld took %ld of the 400 for it", (long)who[k], v);
|
||||
r = tell(who[k], "(LOST) @ .");
|
||||
lost += strtol(r, NULL, 10);
|
||||
r = tell(who[k], "SENT @ .");
|
||||
sent += strtol(r, NULL, 10);
|
||||
CHECK(pool[k].n.mem[MQ_COUNT] == 0, "node %ld has none left waiting", (long)who[k]);
|
||||
}
|
||||
lost -= 1; /* the one for node 99, earlier */
|
||||
/* what is let go is of every kind: the text that would have set a
|
||||
* node sending, the answers to text, as well as what was sent */
|
||||
printf(" of %ld messages the nodes sent each other at once %ld arrived; %ld messages of all kinds were let go for want of room\n", sent, got, lost);
|
||||
CHECK(lost > 0, "there was not room for all of them");
|
||||
CHECK(got <= sent && got + lost >= sent, "every message sent either arrived or was counted: %ld sent, %ld arrived, %ld let go", sent, got, lost);
|
||||
CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0 && printed_from == 12, "and the nodes go on as before");
|
||||
}
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures != 0;
|
||||
|
||||
@@ -160,7 +160,7 @@ static int text_ended; /* how the text ended, when it has */
|
||||
/* 1 if the node is where it waits for a message, with nothing given it. */
|
||||
static int node_idle(void)
|
||||
{
|
||||
return n.reading && !n.given && n.read_port == V4_PORT_ANY && n.p > w_idle && n.p <= w_idle + 3;
|
||||
return n.reading && !n.given && n.read_port == V4_PORT_ANY && n.p > w_idle && n.p <= w_idle + 8; /* in the first words of (IDLE), where it reads */
|
||||
}
|
||||
|
||||
/* Run until the text ends, or until it has been inside KEY with nothing to
|
||||
@@ -239,20 +239,21 @@ static void boot_with(unsigned depth)
|
||||
for (i = 0; i < depth; i++) v4_dstack_push(&n.ds, (v4_cell)(0x5A000000 + i));
|
||||
v4_dstack_push(&n.ds, CANARY);
|
||||
v4_node_port_attach(&n, PORT);
|
||||
v4_node_port_status(&n, 0, 1u | 1u << CONSOLE_PORT); /* the kernel and the console take what is written when it is written */
|
||||
n.mem[WORD_DEFINED] = 0; /* no one is told of entries, until a test says so */
|
||||
n.mem[WORD_FORGOTTEN] = 0;
|
||||
known_n = 0;
|
||||
last_request = 0;
|
||||
n.mem[LINE_STATUS] = 1;
|
||||
n.mem[ME] = 0; /* it has no number yet: a message to 0 is for it */
|
||||
n.mem[OUT_PTR] = OUT_W;
|
||||
HOST_MESSAGES_EMPTY(&n);
|
||||
n.mem[REPLY] = PORT + CONSOLE_PORT;
|
||||
n.p = w_idle; /* waiting for a message */
|
||||
typed_len = 0;
|
||||
line_open = 0;
|
||||
to_node.count = 0; to_sent = 0; from_node.count = 0;
|
||||
shown_len = 0; shown_dropped = 0;
|
||||
(void)v4_exec_step_word(&n, &es, &h); /* it reads its ports, and is blocked */
|
||||
for (i = 0; i < 8 && !n.reading; i++) (void)v4_exec_step_word(&n, &es, &h); /* no message waits: it reads its ports, and is blocked */
|
||||
}
|
||||
static void boot(void) { boot_with(0); }
|
||||
/* The same with nothing at all on the data stack. */
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ int main(int argc, char **argv)
|
||||
if (!v4_node_guards_intact(&n)) die("the node's guards are damaged", NULL);
|
||||
|
||||
n.mem[LINE_STATUS] = 1; /* nothing has ended badly */
|
||||
n.mem[OUT_PTR] = OUT_W; /* nothing has been printed */
|
||||
HOST_MESSAGES_EMPTY(&n); /* nothing has been printed, no message waits */
|
||||
|
||||
/* the nucleus as a capsule of F18 code: the words a neighbour writes to
|
||||
* an empty node's port, each in V4_CELL_BITS / 8 bytes, low byte first */
|
||||
|
||||
@@ -67,6 +67,19 @@ STANDARD = {
|
||||
"MOVE.basic": dict(text="65 HERE ! 66 HERE 1+ ! HERE HERE 16 + 2 MOVE HERE 16 + @ . HERE 17 + @ . CR",
|
||||
out="65 66 \n", why="FORTH-79: MOVE moves cells; and " + D1),
|
||||
"PAD.usable": None, # see OPEN below
|
||||
# see OPEN below: HERE handed to a word that takes a byte address
|
||||
"C!.basic": None,
|
||||
"C!.zero": None,
|
||||
"C!.high_byte": None,
|
||||
"C!.truncation": None,
|
||||
"C@.after_cstore": None,
|
||||
"C@.zero_byte": None,
|
||||
"FILL.basic": None,
|
||||
"FILL.zero_byte": None,
|
||||
"CMOVE.overlap": None,
|
||||
"TYPE.single_char": None,
|
||||
"COUNT.string_bounds": None,
|
||||
"COUNT.zero_addr_plus_one": None,
|
||||
|
||||
"BASE.base_store": dict(out="255 \n", why="255 read in base 16 and printed in base 16 is 255; v3 printed 467"),
|
||||
|
||||
@@ -123,9 +136,29 @@ STANDARD = {
|
||||
"LEAVE.qdloop": dict(out="3 \n", why=LEAVE),
|
||||
}
|
||||
|
||||
# Found 2026-10-06 (docs/v4.0.0/MESH.md section 10, step 4): on v4 HERE is a
|
||||
# cell address and C! C@ FILL CMOVE COUNT TYPE take byte addresses, so these
|
||||
# cases wrote into, or read from, the nucleus's own code at cell HERE/4 --
|
||||
# and passed, reading back what they had written there. Ruled: left out
|
||||
# until how HERE and the byte words agree is settled, as its own step.
|
||||
HERE_BYTE = ("OPEN: HERE is a cell address on v4 and this word takes a byte address (D-1): "
|
||||
"the case wrote to or read from the nucleus's code at cell HERE/4")
|
||||
|
||||
# Dropped, and why. mkpost.py prints these with the cases it leaves out.
|
||||
DROPPED = {
|
||||
"PAD.usable": "OPEN: PAD 42 OVER ! faults on v4 -- PAD is a byte address and ! takes a cell address (D-1)",
|
||||
"C!.basic": HERE_BYTE,
|
||||
"C!.zero": HERE_BYTE,
|
||||
"C!.high_byte": HERE_BYTE,
|
||||
"C!.truncation": HERE_BYTE,
|
||||
"C@.after_cstore": HERE_BYTE,
|
||||
"C@.zero_byte": HERE_BYTE,
|
||||
"FILL.basic": HERE_BYTE,
|
||||
"FILL.zero_byte": HERE_BYTE,
|
||||
"CMOVE.overlap": HERE_BYTE,
|
||||
"TYPE.single_char": HERE_BYTE,
|
||||
"COUNT.string_bounds": HERE_BYTE,
|
||||
"COUNT.zero_addr_plus_one": HERE_BYTE,
|
||||
"CMOVE.bounds": "moves 1000 bytes over the system; v3 refuses by a bounds rule FORTH-79 has not got",
|
||||
}
|
||||
for _name in [k for k, v in STANDARD.items() if v is None and k.startswith("WORD.")]:
|
||||
|
||||
Reference in New Issue
Block a user