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

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

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

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-13 20:02:33 -04:00
co-authored by Claude Opus 5.5
parent 06da5700bc
commit 66b26f823c
22 changed files with 1206 additions and 103 deletions
Binary file not shown.
+175 -46
View File
@@ -336,9 +336,10 @@ longer notes a NACK it owes and pays it later, because it keeps nothing:
the NACK is put on the wire at once or counted in `(LOST)` (7d.4); no
room is kept in a node for NACK and GONE, there being no queue in a node;
what ends a wait is looked for on the wires, not among messages a node
has taken in; and the blocked write of 7b.6 cannot happen. The limit of
7b.7, that a stuck node holds up its neighbours, is closed: 7d.6 says
what the limits are now.)*
has taken in; and no message is written a word at a time, so the blocked
write of 7b.6 does not happen to a message (a word written with `PORT!`
is another matter: 7d.6). The limit of 7b.7, that a stuck node holds up
its neighbours, is closed but for what 7d.6 says.)*
**The rule of this section (reworded 2026-10-07, after the review of step
6c): a node is never left waiting, or going round, on a node that is
@@ -474,8 +475,9 @@ Two types, beside text (1), output (2) and done (3):
*(Removed at step 6e, 2026-10-13: no node writes a message a word at a
time, so none is blocked writing one. `v4_fabric_interrupt_error`, a
device's `pending` and `v4_node_words_since_look` are gone from the
engine. Hera can be typed to whatever any node is doing:
`v4/tests/test_host_unit.c`.)*
engine. Hera can be typed to while another node is stuck
(`v4/tests/test_host_unit.c`), unless she is herself in a text that
cannot end: 7d.6.)*
- **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
@@ -484,10 +486,12 @@ Two types, beside text (1), output (2) and done (3):
### 7b.7 Limits
*(**Closed at step 6e, 2026-10-13**, but for the last of these, which
stands. A node that is stuck holds up no other node, and neither does
one that is asleep: what is for it waits on its wire. A node that waits
for an answer passes on what is for other nodes while it waits. What a
stuck node costs now is in section 7d.6.)*
stands, and for what section 7d.6 lists. A node that is stuck no longer
stops its neighbours passing on, taking text or being typed to, and
neither does one that is asleep: what is for it waits on its wire. A
node that waits for an answer passes on what is for other nodes while it
waits. A text whose printing must go by a stuck node still waits for
that node to be killed.)*
- The room kept for NACK and GONE is finite, and so is the list of NACKs
a node owes. Past them one is dropped and counted in `(LOST)`. A sender
@@ -793,7 +797,11 @@ neighbour, which is what a grid of nodes needs (ruling 1).
wire has a queue each way. A node holds nothing in transit.
3. **What a text prints waits for room; everything else is refused at
once.** `SEND`, an answer, a NACK and a GONE either go or are refused.
Printing waits, so that output is not lost.
Printing waits, so that output is not lost. *(As built, after fix
round 1 of tasks 2 and 3: at the node that did the text its answer has
room kept for it and neither waits nor is refused; at a node passing
it on, what a node printed and how its text ended both wait for room,
and nothing else does. 7d.4.)*
4. **A wire holds 1,024 cells each way**, a build parameter, as the number
of ports and the sizes of the stacks are.
5. **The wires' memory is the fabric's own, for now, behind one
@@ -846,7 +854,7 @@ the engine and happens whole.
| 6, first | The mark is put at the message at the front of the wire. |
| 7, next | The mark is put at the message after the one it is at. |
| 8, sleep | The node is blocked until a message has come to one of its wires since it last fetched "which wires have a message". |
| 9, sleep for room | If the wire has A cells free it answers 0 at once. If not, the node is blocked until a message has come as in 8, or a message has come off any of the wires that lead from it since it was last woken that way: it is then answered 0 if the wire has the A cells and 1 if not, and asks which wire has what (10). *(Changed 2026-10-08, fix round 1 of tasks 2 and 3: it was woken by room on the named wire only. A node may be waiting to put on several wires -- output it is passing on, the answers of texts it has not begun -- and can name one.)* |
| 9, sleep for room | If the wire has A cells free it answers 0 at once. If not, the node is blocked until a message has come as in 8, or a message has come off any of the wires that lead from it since it was last woken that way: it is then answered 0 if the wire has the A cells and 1 if not, and asks which wire has what (10). It is woken the same way when any of its wires is cut *(added 2026-10-13, after the final review: it may be keeping something to put there)*. *(Changed 2026-10-08, fix round 1 of tasks 2 and 3: it was woken by room on the named wire only. A node may be waiting to put on several wires -- output it is passing on, the answers of texts it has not begun -- and can name one.)* |
| 10, room | Has the wire A cells free? It answers at once and changes nothing. |
- Each wire has a mark for the node that reads it: the message that
@@ -869,7 +877,9 @@ the engine and happens whole.
while it waits for an answer -- and must still sleep.
- The ports themselves stay as they are, a word at a time, for the two
things that are not messages: a newborn node being sent its nucleus
(section 5), and a node's requests to the kernel (section 8).
(section 5), and a node's requests to the kernel (section 8). A word
written to a node that speaks whole messages, or to a device that
takes none, is refused: 7d.6.
- The looking words of section 7a, and their lower-number rule, are no
longer used by the nucleus.
@@ -901,14 +911,25 @@ waiting, the words that turned it, the list of NACKs owed, and
*(Ruled 2026-10-08, fix round 1 of tasks 2 and 3:)* **but what a
node printed (type 2) and its word of how text ended (type 3) are
not dropped for want of room.** The message is left on the wire it
came by and gone past with next; what is behind it is served, but
for what goes the same way, which is left too so that the order is
kept; and it is moved when room appears. The node then sleeps for
came by and gone past with next; what is behind it is served. What
is behind it and goes the same way is treated as if that wire were
full, though it be short and would fit: output and how text ended
are left too, so that their order is kept, and text, a NACK or a
GONE is refused *(ruled 2026-10-13, after the final review found the
code and this section apart: only printing waits)*. The held message
is moved when room appears. The node then sleeps for
room (9), which room on any of its wires ends. The code before step
6e waited here; letting output go while the line said ok was a
step back.
- *For itself:* it is taken and dealt with as now: text is done; a
NACK is counted; a GONE from its centre is believed.
**Texts whose answers go by the same port are begun in the order
they came.** When one is left on its wire because there is no room
for its answer, every text behind it on that pass whose answer goes
the same way is left too, though room has come since. *(Fixed
2026-10-13, found by the final review: only the room was asked, each
time, and room that came between the look at one text and at the
next began the second first.)*
- *`SEND`, how text ended, a NACK, a GONE* are built in the node's own
memory and put. Refused, a `SEND` is error 19, "Message refused"; the
others are counted in `(LOST)`.
@@ -923,9 +944,11 @@ waiting, the words that turned it, the list of NACKs owed, and
`AWAIT` only.)*
- *`AWAIT`* sleeps (8) and, woken, goes through each wire with first and
next: what is for other nodes is passed on; what would end its wait is
taken -- the answer, a NACK or a believed GONE about the node waited
for, or text from its console; other messages for itself stay on the
wire, in their order.
taken -- the answer, or a NACK or a believed GONE about the node waited
for. Text from its console ends the wait too, and is not taken: it
stays on its wire and is done when the text that waited has been
finished with. Other messages for itself stay on the wire, in their
order.
- *Room for the answer is kept.* Only a node puts messages on the wires
that lead from it, so room it has found there stays until it uses it.
A node does not begin text unless the wire back toward the sender has
@@ -947,6 +970,16 @@ asked by words written to its port, as before, and takes no messages. A
message for that port is refused at once -- error 18, "No one on that
port".
**A line there is no room for** *(fixed 2026-10-13, found by the final
review)*. The lone node's console wire fills if the node takes nothing
from it: it waits for an answer and does not know the lines typed for
its console's, because `(CONSOLE)` was set to another node. The line is
then refused where it is typed, "Message refused", and the prompt comes
back (`V4_BOOT_LINE_REFUSED`, `v4/include/v4/boot.h`). Before, the
hosted program said the node had stopped on a fault and ended, and the
bare-metal one would have stopped for good; no fault had happened. The
node goes on waiting: on a lone node nothing but a restart ends that.
### 7d.6 What will be limits
- A burst to a node that is busy, beyond what its wire holds, is refused
@@ -961,6 +994,47 @@ port".
takes it. A node that is stuck never does: its wires fill, and from
then on whoever sends to it is told at once.
*Added 2026-10-13, after the final review:*
- While a text waits for room to print, its node takes no text, its
console's included: it cannot be typed to until the room comes.
- Text, a NACK or a GONE that arrives at a node behind output that node
is holding for the same port is refused, though the wire has room for
it (7d.4).
- Removing a node tells no one. A node waiting for its answer waits on
until Hera's `KILL` says it is gone, or its console types a line. So
does one whose answer was on a wire that was cut.
- **A word written to a port, `PORT!`.** A node that speaks whole
messages does not read words. A word written to one -- to any node
that has asked for a whole-message operation since it was born,
whether it is asleep at its wires, running, or stuck in a loop -- or
to a console's port, is error 18, "No one on that port", and the
writer goes on (`v4/src/fabric.c`, `v4/src/node.c`; fixed 2026-10-13:
before, the writer was blocked for ever and every node beyond it cut
off, and had it been Hera she could not have been typed to).
Words are for a newborn node being sent its nucleus, which has asked
nothing of a wire. **Still a limit:** if such a node stops reading
before its nucleus is all sent, whoever is sending it is blocked until
it is removed.
- A text of one node's left on its wire for want of room for its answer
holds back every later text whose answer goes by the same port, so
that they are begun in order (7d.4).
*What the products do that they did not before step 6e, beside the
above* (found by the final review, comparing the hosted program with the
one before on typed sessions):
- `5 1 PORT!`, a word to the console's port, is "No one on that port";
it was taken, and the line said ok.
- `SEND-ON` to a port with no one on it leaves nothing on the stack; it
left its four arguments.
- `SEND-ON` with a length above 1,024 or below 0 is error 12; it was
accepted.
- Printing and `GONE` work from one call deeper than they did.
- Two lines that hung now come back to the prompt: a 400-number loop with the
way to the console set to port 0, and a `SEND` with the default way
set to port 0.
### 7d.7 Acceptance
1. *Whole or not at all.* At no step of the engine is part of a message
@@ -1664,8 +1738,7 @@ Each is tested, committed and pushed before the next.
and `-06*` are of the wait as built and are kept as its record.
**6e. Messages on the wire (section 7d).** Ruled 2026-10-08: the
second attempt at 6d. **Built and verified 2026-10-13; its final
review is still to come** (last item below).
second attempt at 6d. **Built, reviewed and mended 2026-10-13.**
- *The wire* (`v4/include/v4/wire.h`, `v4/src/wire.c`, `d5cd6460`,
`83694b18`): a queue of whole messages, one way; put, look, take,
move and drop, each all or nothing. `v4/tests/test_wire.c`, 112
@@ -1673,7 +1746,7 @@ Each is tested, committed and pushed before the next.
- *The engine* (`v4/src/node.c`, `v4/src/fabric.c`): the six addresses
of 7d.3 and the ten operations; the wires' queues given by the
fabric's owner; a device that puts and takes whole messages; the
kernel's port, which has no queue. `v4/tests/test_fabric.c`, 306
kernel's port, which has no queue. `v4/tests/test_fabric.c`, 334
checks.
- *The nucleus* (`v4/capsule/core.v4`, `quit.v4`, `b1d09af0`): `(DO)`,
`(WAY)`, `(ROOM?)`, `(SERVE)`, `(REFUSE)`; `(IDLE)`, `SEND`, `AWAIT`,
@@ -1683,25 +1756,35 @@ Each is tested, committed and pushed before the next.
run by, and the tests' consoles) speak whole messages.
- *Two reviews of the nucleus, each mended* (`6a7d0aa2`, `f1419e73`):
7d.3 and 7d.4 carry what they changed, marked "fix round".
- *A node removed at every step* (acceptance 6):
`v4/tests/test_host_mesh.c`, 220 checks. Three exchanges on the row
- *A node removed, or a wire cut, at every step* (acceptance 6):
`v4/tests/test_host_mesh.c`, 241 checks. Three exchanges on the row
of three -- text from the console for node 12, what it prints and how
it ended; node 11 `SEND`ing node 12 text and waiting for its answer;
a message node 12 has no way for, and its NACK to node 10 -- are each
played whole, and then once for every engine step they take (3,579,
4,214 and 1,025 at 64 bits) with node 11 removed at that step, and
again with node 12: 17,642 removals. After each, every node left came
to rest asleep, blocked at no port and in no operation, and answered
played whole, and then once for every engine step they take (about
3,600, 4,200 and 1,000 at 64 bits) with node 11 removed at that
step, again with node 12 removed, and again with the wire between
them cut and made again. A fourth, in which what node 12 prints is
held at node 11, asleep for room, because the wire to a busy node 10
is full, is disturbed in the same three ways at every step at which
anything on a wire, or what a node asks of one, has changed. 26,814
disturbances in all. After each, every node left came to rest --
asleep for a message, and blocked at no port -- and answered
`1 2 + .`; with node 11 gone, node 12 is wired to node 10 first, as
whoever removed it would, so that it can be asked. None failed. The
test was shown to fail, in 4,376 of them, with the engine changed so
that a wait for room on a cut wire never ends.
whoever removed it would, so that it can be asked. None failed.
*What "answered" allows:* a node that was waiting for an answer from
a node now removed is still waiting when the mesh comes to rest
(7d.6), and the line it is then typed ends that wait, "Interrupted",
before it answers.
The test was shown to fail with the engine changed so that a wait
for room on a cut wire never ends: 2,821 removals of node 11 in the
first exchange, 1,555 in the second, and one in the fourth.
*What is typed in that test is a word defined beforehand, not a line
of several words:* a line is mostly the dictionary being searched,
tens of thousands of steps in which no message moves, and each would
be a removal played to its end. The first exchange as the line
`7 8 * .` takes 38,234 steps; removal was tried at its first 300 and
none failed, and it is not in the suite.
be a removal played to its end. The final review ran the three as
lines of several words, disturbed at every change on a wire, and
found nothing; that is not in the suite.
- *Removed from the engine, nothing using them:*
`v4_fabric_interrupt_error`, a device's `pending`,
`v4_node_words_since_look`, and `V4_ERROR_INTERRUPTED` (the nucleus
@@ -1711,23 +1794,69 @@ Each is tested, committed and pushed before the next.
- *Other tests.* `v4/tests/test_host_unit.c`, 81 checks (Hera and
four); `v4/tests/test_host_depth.c`, 397 checks (acceptance 7 on the
mesh); `v4/tools/depthsweep.py`, 230 runs on the hosted product.
- *Verified.* `make -C v4 test` and `sanitize` at both widths,
`hosted-check`, and `mkcapsule --lint capsules/`: no failures. Three
bare-metal boots, `logs/20261013-165124` (amd64), `-165428`
(aarch64), `-165818` (riscv64), each typing the session of step 6d's
last boots and a loop that prints 400 numbers: 29 values on a line
("Stack overflow"); `WORDS` with 26 on the stack; `AWAIT` with 27,
and `5 AWAIT` ended by the next line ("Interrupted"); all 400
numbers, in order. On all six systems: POST 538 of 538,
`word_count=317`, `dict_hash=0x0951d7e02e251554`; and what the three
boots answered at the prompt is the same, character for character.
- *First verified* at `06da5700`: `make -C v4 test` and `sanitize` at
both widths, `hosted-check`, lint; three bare-metal boots,
`logs/20261013-165124` (amd64), `-165428` (aarch64), `-165818`
(riscv64); `dict_hash=0x0951d7e02e251554` on all six.
- *Seen in the older logs, and not a fault of the node.* In the boots
of 2026-10-08 `WORDS` shows a character twice here and there on
amd64 and riscv64 (`LOG-INFFO-STR`, `QQ.-`). The script that typed
those sessions read QEMU's serial socket only between lines; with
the socket read as it comes, as these three were, there is none.
- **Not done:** the final review of the whole step by a fresh
reviewer (the plan's last step).
the socket read as it comes, as every boot of 2026-10-13 was, there
is none.
- **The final review (2026-10-13)**, by a fresh reviewer told to break
it, and Captain Bob's ruling: "fix them all, test first". Each mend
below has a test that failed before it:
- *Two texts from one node could be begun in the wrong order*, when
room for their answers came between the node's look at the first
and at the second. Texts whose answers go the same way are now
begun in the order they came (7d.4; `(D-MASK)`, `quit.v4`). The
test lets the room come at each of 700 steps in turn; 102 of them
gave the wrong order before.
- *A word written with `PORT!` to a node that speaks whole messages,
or to a console's port, blocked its writer for ever*, and cut off
every node beyond it. It is error 18 now, whether that node is
asleep, running or stuck (7d.6; `fabric.c`, `node.c`).
- *A node asleep for room on one wire was not woken when another of
its wires was cut.* It is (7d.3).
- *On the two products a line there was no room for was reported as
a fault*, and the hosted program ended. It is "Message refused"
and the prompt comes back (7d.5); `hosted-check` has the case.
The bare-metal host has the same mend (`kernel/src/v4/sk_v4.c`)
but **no boot has typed that case.**
- *Text behind held output for the same port is refused though it
would fit.* Ruled: that is what is meant, and the document was
wrong (7d.4, 7d.6).
- *The removal test proved less than it claimed.* It now cuts and
re-makes a wire as well, and has an exchange in which output is
held at a full wire (above).
- *Statements here and in `v4/README.md` that said too much* are
reworded: that a stuck node holds up no other node, that only
printing waits, that no node is left waiting on one that is gone,
and that `AWAIT` takes its console's text (7b, 7b.6, 7b.7, 7d.1,
7d.4, 7d.6).
- *What the products do that they did not before*, and 7d.6 did not
say, is listed there.
- *Not mended:* `ROUTE_MAX` in `v4/tests/host_map.h` is used by
nothing, as it was before this step.
- *What the review did not cover:* the aarch64 and riscv64 products
and bare metal; its own sweeps at 32 bits; the unit of five with
wires cut and re-made.
- *Verified after the mends.* `make -C v4 test` and `sanitize` at both
widths, `hosted-check` (230 depth-sweep runs, and the new case),
and `mkcapsule --lint capsules/`: no failures. `test_fabric.c` 334
checks, `test_host_mesh.c` 241, `test_host_unit.c` 81,
`test_host_depth.c` 397, `test_wire.c` 112. Three bare-metal boots,
`logs/20261013-194827` (amd64), `-195131` (aarch64), `-195518`
(riscv64), with the session of the boots before: 29 values on a line
("Stack overflow"); `WORDS` with 26 on the stack; `AWAIT` with 27,
and `5 AWAIT` ended by the next line ("Interrupted"); a loop that
prints 400 numbers, all of them, in order. On all six systems: POST
538 of 538, `word_count=317`, `dict_hash=0xe5a57f3eb72ff94a` (the
nucleus changed, so the hash did); and what the three boots answered
at the prompt is the same, character for character, as each other
and as the boots before the mends.
- **Not done:** a second review, of the mends.
7. **A second unit; scaling while running; sleep, wake and kill by
command.**
+1
View File
@@ -127,6 +127,7 @@ void sk_v4_run(void (*chain)(void))
}
if (how == V4_TEXT_COMPLETED) console_puts(" ok\n");
else if (how == V4_TEXT_ERROR) console_puts(" ERROR\n");
else if (how == V4_BOOT_LINE_REFUSED) console_puts("Message refused\n ERROR\n"); /* the node's wire is full: error 19's words */
else if (how == V4_BOOT_LINE_STOPPED) {
console_println("StarForth v4: the node stopped on a fault");
for (;;) { }
@@ -0,0 +1,217 @@
[=3hBdsDxe: 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)
[=3hStarKernel 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 amd64
Architecture: amd64
Build: Oct 13 2026 19:47:59
UEFI BootServices: EXITED
=== StarKernel Boot Information ===
Memory map entries: 131
Total memory: 1023 MB
Usable memory: 966 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 248734
Free pages : 247211
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=2093272242 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=1004031420, tick_hz=100, initial_count=10040314
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=0x3b4a82485b10bfb7 capsule_hash=0x3b4a82485b10bfb7 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=0x6b56e15226ee3719
PARITY:V4_POST tests=538 pass=538 fail=0
PARITY:V4_SYSTEM word_count=317 dict_hash=0xe5a57f3eb72ff94a
PARITY:OK
POST: PASSED
[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
PCI: init...
PCI: ECAM mapped (amd64)
virtio-blk: found device
[HADES][INFO ] [KRELTSC: 77784357] 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> : N4 400 0 DO I . LOOP ;
ok
ok> N4
0 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 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
N4 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> : AW 5 AWAIT ;
ok
ok> 1
ok
ok> AW
Stack overflow
ERROR
ok> 4321 .
4321 ok
ok> HERE .
8326 ok
ok> BYE
BYE: cold restart
@@ -0,0 +1,190 @@
UEFI firmware (version 2025.11-3ubuntu7.3 built at 15:40:26 on Sep 23 2026)
[=3hBdsDxe: 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)
[=3hStarKernel 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 aarch64
Architecture: aarch64
Build: Oct 13 2026 19:51:04
UEFI BootServices: EXITED
=== StarKernel Boot Information ===
Memory map entries: 108
Total memory: 4093 MB
Usable memory: 4054 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 1039347
Free pages : 1037936
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=0x3b4a82485b10bfb7 capsule_hash=0x3b4a82485b10bfb7 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=0x6b56e15226ee3719
PARITY:V4_POST tests=538 pass=538 fail=0
PARITY:V4_SYSTEM word_count=317 dict_hash=0xe5a57f3eb72ff94a
PARITY:OK
POST: PASSED
[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
PCI: init...
virtio-blk: found device
[HADES][INFO ] [KRELTSC: 1840537] 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> : N4 400 0 DO I . LOOP ;
ok
ok> N4
0 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 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
N4 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> : AW 5 AWAIT ;
ok
ok> 1
ok
ok> AW
Stack overflow
ERROR
ok> 4321 .
4321 ok
ok> HERE .
8326 ok
ok> BYE
BYE: cold restart
@@ -0,0 +1,260 @@
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
[=3hRISC-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
[=3hStarKernel 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 = 0x00000000bdc95abe
riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a)
_____ _ _ __ _
/ ____| | | |/ / | |
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
____) | || (_| | | | . \ __/ | | | | | __/ |
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
LithosAnanke v2.1.0 riscv64
Architecture: riscv64
Build: Oct 13 2026 19:54:38
UEFI BootServices: EXITED
=== StarKernel Boot Information ===
Memory map entries: 100
Total memory: 1020 MB
Usable memory: 975 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 250264
Free pages : 249612
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=0x3b4a82485b10bfb7 capsule_hash=0x3b4a82485b10bfb7 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=0x6b56e15226ee3719
PARITY:V4_POST tests=538 pass=538 fail=0
PARITY:V4_SYSTEM word_count=317 dict_hash=0xe5a57f3eb72ff94a
PARITY:OK
POST: PASSED
[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
PCI: init...
virtio-blk: found device
[HADES][INFO ] [KRELTSC: 30767037] 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> : N4 400 0 DO I . LOOP ;
ok
ok> N4
0 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 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
N4 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> : AW 5 AWAIT ;
ok
ok> 1
ok
ok> AW
Stack overflow
ERROR
ok> 4321 .
4321 ok
ok> HERE .
8326 ok
ok> BYE
BYE: cold restart
+2
View File
@@ -210,6 +210,8 @@ hosted-check: $(foreach i,$(HOSTED_ISAS),$(BINDIR)/boot-$(i).txt) $(BINDIR)/star
@test $$(grep -c '^ok> ERROR$$' $(BINDIR)/kport-amd64.txt) -eq 2 && grep -q '^ok> 3 ok$$' $(BINDIR)/kport-amd64.txt && grep -q '^ok> 0 ok$$' $(BINDIR)/kport-amd64.txt || { echo "hosted-check: with the way to the console set to the kernel's port, a line that prints did not end in an error at the prompt, or left something on the stack, or the node did not come back"; tail -6 $(BINDIR)/kport-amd64.txt; exit 1; }
@printf ': S0 S" 1 DROP" 0 0 SEND-ON ; S0\n1 2 + .\n65 1 BLOCK C! 1 BLOCK C@ .\n' | timeout 120 $(BINDIR)/starforth4-amd64 > $(BINDIR)/ksend-amd64.txt 2>&1; true
@grep -q '^ok> No one on that port$$' $(BINDIR)/ksend-amd64.txt && grep -q '^ok> 3 ok$$' $(BINDIR)/ksend-amd64.txt && grep -q '^ok> 65 ok$$' $(BINDIR)/ksend-amd64.txt || { echo "hosted-check: a message sent to the kernel's port was not refused with No one on that port, or the kernel's requests no longer work"; tail -6 $(BINDIR)/ksend-amd64.txt; exit 1; }
@{ printf '5 (CONSOLE) !\n7 AWAIT\n'; i=0; while [ $$i -lt 200 ]; do echo '1 DROP 1 DROP 1 DROP 1 DROP 1 DROP 1 DROP 1 DROP 1 DROP'; i=$$((i+1)); done; } | timeout 120 $(BINDIR)/starforth4-amd64 > $(BINDIR)/fullwire-amd64.txt 2>&1; test $$? -eq 0 || { echo "hosted-check: lines typed to a node that takes none, until its wire was full, ended the program"; tail -3 $(BINDIR)/fullwire-amd64.txt; exit 1; }
@grep -q '^ok> Message refused$$' $(BINDIR)/fullwire-amd64.txt && ! grep -q 'stopped on a fault' $(BINDIR)/fullwire-amd64.txt || { echo "hosted-check: a line there was no room for on the node's wire was not refused, and said so"; tail -3 $(BINDIR)/fullwire-amd64.txt; exit 1; }
@python3 $(HERE)/tools/depthsweep.py $(BINDIR)/starforth4-amd64 $$(( $(HOST_DATA_RING) + 2 )) $$(( $(HOST_RET_RING) + 1 )) || { echo "hosted-check: at some depth of a stack a line was not answered, or the node did not come back"; exit 1; }
@$(BINDIR)/starforth4-postfail < /dev/null > $(BINDIR)/postfail.txt; test $$? -ne 0 || { echo "hosted-check: a boot with a failing case did not fail"; exit 1; }
@grep -q '^POST FAIL: fail.wrong out<5 5 $$' $(BINDIR)/postfail.txt || { echo "hosted-check: the failing case was not named"; cat $(BINDIR)/postfail.txt; exit 1; }
+17 -14
View File
@@ -129,9 +129,9 @@ The two products are not yet this: they become the five nodes at steps 8
and 9.
A node she births is not POSTed: POST is the kernel's, once, on Hera.
**Refusals and waits** (`MESH.md` 7b, step 6c). A node is never left
waiting on a node that is gone, and a wait on a node that is alive and
stuck is ended by Hera's killing it, or from the console. A node with no
**Refusals and waits** (`MESH.md` 7b, step 6c). A wait on a node that
never answers -- stuck, or gone -- is ended by Hera's killing it, which
tells the others it is gone, or from the console. A node with no
room for a message, or no way to pass it on, or that would be the
sixteenth to pass it on, sends a NACK to whoever waits on it; `(REFUSED)`
counts them. `AWAIT` ends in an error if the answer is not going to come:
@@ -140,17 +140,20 @@ the console. `n KILL` on Hera removes node n and tells every other node it
is gone; a node believes that only from its centre.
**Messages on the wire** (`MESH.md` 7d, step 6e). A node that is stuck
holds up no other node. The fabric keeps a queue of whole messages each
way on every wire (`v4/src/wire.c`, 1,024 cells), and a node asks it,
through six addresses after its ports, to look at, take, move, drop or
put a whole message: each is one engine step, all of it or none. A node
holds nothing in transit. `SEND`, an answer, a NACK and a GONE go at once
or are refused at once; only what a text prints waits for room, so that
output is not lost. A node with nothing to do sleeps until a message
comes. A node removed at any step of an exchange leaves the others at
rest and answering (`v4/tests/test_host_mesh.c`). What it costs is in
`MESH.md` 7d.6: a burst beyond what a wire holds is refused, and text for
a node that is stuck fills its wire and is refused from then on.
no longer stops its neighbours passing on, taking text or being typed
to. The fabric keeps a queue of whole messages each way on every wire
(`v4/src/wire.c`, 1,024 cells), and a node asks it, through six addresses
after its ports, to look at, take, move, drop or put a whole message:
each is one engine step, all of it or none. A node holds nothing in
transit. `SEND`, a NACK and a GONE go at once or are refused at once.
What a text prints, and its word of how the text ended, wait for room, so
that output is not lost. A node with nothing to do sleeps until a message
comes. A node removed, or a wire cut, at any step of an exchange leaves
the others at rest and answering (`v4/tests/test_host_mesh.c`). What it
costs is in `MESH.md` 7d.6: a burst beyond what a wire holds is refused;
text for a node that is stuck fills its wire and is refused from then on;
and a text whose printing must go by a stuck node waits, and cannot be
typed to, until that node is killed.
**Blocks** (`MESH.md` step 6). The block words are the kernel's, as they
are for a v3 VM: `BLOCK`, `BUFFER`, `UPDATE`, `SAVE-BUFFERS` and
+15 -2
View File
@@ -105,6 +105,14 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
\ whose answers go different ways, and each is begun when
\ its own has the room. That room is kept from then until
\ the answer has gone.
\ TEXTS WHOSE ANSWERS GO THE SAME WAY ARE BEGUN IN THE ORDER
\ THEY CAME. Once one has been left for want of room, every
\ text behind it on this pass whose answer goes by that port
\ is left too, though the room has come since: (D-MASK) has
\ a bit for each such port. (As first built only the room
\ was asked, each time; room that came between the look at
\ one text and at the next began the second first. Found
\ by the final review of step 6e.)
\ a NACK is taken and counted in (REFUSED)
\ a GONE is taken, and believed if this node's centre sent it
\ an answer no one is waiting for is dropped: a SEND that is not waited
@@ -128,7 +136,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
\ for no one.
: (IDLE)
L: 1 (QUIET) a! ! \ nothing here is any text's doing: see (RAISED)
-1 (KEEP) a! ! 0 (AWAIT-FROM) a! ! -1 (DEFER) a! !
-1 (KEEP) a! ! 0 (AWAIT-FROM) a! ! -1 (DEFER) a! ! 0 (D-MASK) a! !
(SERVE) (MINE0)
M: (MINE) if REST
drop (IN)+2 a! @ -1 + if TEXT -3 + if NACK -1 + if GONE
@@ -145,10 +153,15 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
TEXT: drop
(IN)+1 a! @ (WAY) -if T1 drop (S-WIRE) a! @ \ the way back; with none known, the wire it came on
T1: (DONE-PORT) a! !
(DONE-PORT) a! @ (WBIT) (D-MASK) a! @ and if FREE1 jump FULL \ text ahead of it waits for room on that wire
FREE1: drop
8 (DONE-PORT) a! @ (ROOM?) if START -1 + if FULL
drop (S-WIRE) a! @ (DONE-PORT) a! ! \ no one is on that port: back by the wire it came on,
(S-WIRE) a! @ (WBIT) (D-MASK) a! @ and if FREE2 jump FULL
FREE2: drop
8 (S-WIRE) a! @ (ROOM?) -1 + if FULL jump START \ if that has the room -- or has no one on it either
FULL: drop (DEFER) a! @ -if LATER drop (DONE-PORT) a! @ (DEFER) a! ! (SKIP) jump M
FULL: drop (DONE-PORT) a! @ (WBIT) dup inv (D-MASK) a! @ and + !
(DEFER) a! @ -if LATER drop (DONE-PORT) a! @ (DEFER) a! ! (SKIP) jump M
LATER: drop (SKIP) jump M
REST: drop
(DEFER) a! @ -if ROOM drop (SLEEP) jump L
+3
View File
@@ -78,6 +78,9 @@ int v4_boot_run(const v4_boot *b);
#define V4_BOOT_LINE_STOPPED (-2) /* the node stopped on a fault, or never finished */
#define V4_BOOT_LINE_NO_INPUT (-3) /* the line read the keyboard and there is no more input */
#define V4_BOOT_LINE_WAITING (-4) /* the line is waiting for an answer (AWAIT) that no one here can send: see below */
#define V4_BOOT_LINE_REFUSED (-5) /* the node's wire has no room for the line: it was not put, and nothing was run. The node
* is taking nothing -- it waits for an answer and does not know these lines for its
* console's -- and its wire has filled (MESH.md 7d.6). It has not stopped. */
int v4_boot_line(const v4_boot *b, const char *text, unsigned len);
/* A LINE THAT WAITS (docs/v4.0.0/MESH.md 7b.6). V4_BOOT_LINE_WAITING means
+21 -6
View File
@@ -3,9 +3,10 @@
*
* On the fabric every node runs at the same time and a node with nothing to
* do is blocked at a port. Here that is: every node that is awake and not
* blocked executes one instruction word, and then every write that has a
* reader waiting on the other end of its wire is handed over. There is no
* choice of whose turn it is.
* blocked executes one instruction word; then every operation on a whole
* message that a node asked for is done (WHOLE MESSAGES, below); and then
* every word written that has a reader waiting on the other end of its wire
* is handed over. There is no choice of whose turn it is.
*
* THE GEOMETRY IS DATA. Which port connects to what is a table, and it can
* be changed while the nodes run; nodes can be added and removed while they
@@ -50,6 +51,7 @@ typedef struct {
typedef struct {
v4_fabric_node *node; /* 0: no node here */
int asleep;
int cut; /* not 0: a wire of this node's has been cut since it was last woken by that (operation 9) */
v4_wire wire[V4_PORTS];
} v4_place;
@@ -97,6 +99,19 @@ int v4_fabric_device_take(v4_fabric *f, unsigned node, unsigned port, v4_cell *h
* is how a fabric begins, nothing is raised. */
void v4_fabric_gone_error(v4_fabric *f, v4_cell code);
/* A WORD NO ONE WILL READ. The same error is raised on a node that can take
* one and is blocked writing a word to a port whose other end will not read
* it: a device that takes no words (its `take` is 0: it speaks whole
* messages only), or a node that speaks whole messages -- one that has
* asked for a whole-message operation (below) since it was born, whatever
* it is doing now: asleep at its wires, running, stuck in a loop, or put to
* sleep by the fabric. Such a node does not read words, and before this a
* word written to one blocked its writer for ever (found by the final
* review of step 6e). A node that has asked for none yet may still read:
* the writer waits for it. That is a newborn node being sent its nucleus
* a word at a time, which executes from its ports and has asked nothing of
* a wire. */
/* More room: `places`, of `capacity` no less than before, takes over; every
* node keeps its number and its wiring. */
void v4_fabric_grow(v4_fabric *f, v4_place *places, unsigned capacity);
@@ -149,9 +164,9 @@ void v4_fabric_wake(v4_fabric *f, unsigned id);
* and 1 at once if it can never have it. Otherwise the node sleeps until a
* message has come, as for 8, or a message has come off ANY of the wires
* that lead from it -- taken, moved on or dropped by whoever reads that wire
* -- since it was last woken that way; then it is answered 0 if the wire it
* named now has the room and 1 if not, and it asks which wire has what
* (10). A node may be waiting to put on several wires at once and can name
* -- since it was last woken that way, or one of its wires has been cut;
* then it is answered 0 if the wire it named now has the room and 1 if not,
* and it asks which wire has what (10). A node may be waiting to put on several wires at once and can name
* only one. A wake-up that finds nothing to do is answered by asking
* again.
*
+5 -2
View File
@@ -125,6 +125,8 @@ typedef struct {
unsigned wire; /* the port that A, B and Do are about; V4_PORTS if what was stored is not a port */
v4_cell wire_a, wire_b; /* A and B */
int doing; /* non-zero: blocked, having stored `do_op` to Do */
int spoke; /* non-zero: it has asked for a whole-message operation since it was born: it is a node that
* speaks whole messages, and not one that words are written to (fabric.h) */
v4_cell do_op; /* the operation */
v4_cell how; /* how the last one went */
unsigned have; /* which wires had a message waiting, as the fabric last said: bit k for port k */
@@ -217,8 +219,9 @@ void v4_node_console_attach(v4_node *n, v4_cell addr);
* Have fetch: which wires have a message
* waiting, bit k for port k, as the fabric
* last said (v4_node_have)
* The two before them are how a node looks before it writes (MESH.md 7a), as the
* F18's io register lets it. They are what whoever connects the ports last
* The two before them let a node that writes a word at a time see whether a
* neighbour is waiting (MESH.md 7a), as the F18's io register does; a node that
* speaks whole messages has no use for them. They are what whoever connects the ports last
* said, v4_node_port_status; fetching them waits for nothing and changes
* nothing. Of the whole-message addresses How and Have are read only: a
* store to either is an address fault, as to "any port".
+18 -3
View File
@@ -9,6 +9,7 @@ static void clear_place(v4_place *p)
unsigned k;
p->node = 0;
p->asleep = 0;
p->cut = 0;
for (k = 0; k < V4_PORTS; k++) {
p->wire[k].kind = V4_WIRE_NONE; p->wire[k].node = 0; p->wire[k].port = 0; p->wire[k].device = 0;
p->wire[k].rx = 0; p->wire[k].tx = 0;
@@ -126,8 +127,10 @@ static void cut(v4_fabric *f, unsigned a, unsigned pa)
if (other->kind == V4_WIRE_NODE && other->node == a && other->port == pa) {
other->kind = V4_WIRE_NONE; other->device = 0;
other->rx = 0; other->tx = 0; /* the same two queues as w's */
f->place[w->node].cut = 1;
}
}
if (w->kind != V4_WIRE_NONE) f->place[a].cut = 1; /* a node asleep for room on another wire is woken (operation 9) */
if (f->queue) { give_queue(f, w->rx); give_queue(f, w->tx); }
w->kind = V4_WIRE_NONE;
w->device = 0;
@@ -226,6 +229,16 @@ static int awake(const v4_fabric *f, unsigned id)
return id < f->capacity && f->place[id].node && !f->place[id].asleep;
}
/* 1 if a word written to this end of a wire will not be read: the other end
* is a device that takes no words, or a node that speaks whole messages --
* one that has asked for a whole-message operation since it was born. */
static int no_reader(const v4_fabric *f, const v4_wire *w)
{
if (w->kind == V4_WIRE_DEVICE) return w->device->take == 0;
if (w->kind == V4_WIRE_NODE && w->node < f->capacity && f->place[w->node].node) return f->place[w->node].node->n.spoke != 0;
return 0;
}
/* What node `a` wrote to one of its ports, handed to whatever is there. */
static unsigned hand_over_write(v4_fabric *f, unsigned a)
{
@@ -386,7 +399,8 @@ static int operate(v4_fabric *f, unsigned id, v4_cell *how)
if (!tx) { *how = V4_WIRE_NO_ONE; break; }
if (a < 0 || a > (v4_cell)V4_WIRE_CELLS) { *how = V4_WIRE_NO_ROOM; break; } /* no waiting can make that room */
if (v4_wire_room(tx, (unsigned)a) == V4_WIRE_DONE) { *how = V4_WIRE_DONE; break; }
if (!arrived(f, id) && !freed(f, id)) return 0;
if (!arrived(f, id) && !freed(f, id) && !f->place[id].cut) return 0;
f->place[id].cut = 0;
for (cells = 0; cells < V4_PORTS; cells++) /* woken: the room that has come on any of its wires is seen */
if (f->place[id].wire[cells].tx) f->place[id].wire[cells].tx->freed = 0;
*how = V4_WIRE_NO_ROOM;
@@ -457,13 +471,14 @@ unsigned v4_fabric_step(v4_fabric *f)
for (i = 0; i < f->capacity; i++)
if (awake(f, i) && f->place[i].node->n.reading && !f->place[i].node->n.given) done += hand_over_read(f, i);
/* a node blocked at one port that nothing is wired to */
/* a node blocked at one port that nothing is wired to; or blocked writing a
* word to what will not read one (fabric.h, A WORD NO ONE WILL READ) */
if (f->gone_error != 0)
for (i = 0; i < f->capacity; i++) {
v4_node *n;
if (!awake(f, i)) continue;
n = &f->place[i].node->n;
if ((n->asking && f->place[i].wire[n->ask_port].kind == V4_WIRE_NONE)
if ((n->asking && (f->place[i].wire[n->ask_port].kind == V4_WIRE_NONE || no_reader(f, &f->place[i].wire[n->ask_port])))
|| (n->reading && !n->given && n->read_port != V4_PORT_ANY && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE))
done += (unsigned)v4_node_port_gone(n, f->gone_error);
}
+2 -1
View File
@@ -90,7 +90,7 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value)
if (port == (int)V4_PORT_WIRE) { n->wire = (value >= 0 && value < (v4_cell)V4_PORTS) ? (unsigned)value : V4_PORTS; return; }
if (port == (int)V4_PORT_A) { n->wire_a = value; return; }
if (port == (int)V4_PORT_B) { n->wire_b = value; return; }
if (port == (int)V4_PORT_DO) { n->do_op = value; n->doing = 1; return; }
if (port == (int)V4_PORT_DO) { n->do_op = value; n->doing = 1; n->spoke = 1; return; }
if (port >= 0 && port < (int)V4_PORTS) {
n->request = value;
n->ask_port = (unsigned)port;
@@ -193,6 +193,7 @@ void v4_node_port_attach(v4_node *n, v4_cell base)
n->wire_a = 0;
n->wire_b = 0;
n->doing = 0;
n->spoke = 0;
n->do_op = 0;
n->how = 0;
n->have = 0;
+1 -1
View File
@@ -225,7 +225,7 @@ static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep
if (text) { /* with none, the node has a line waiting on its wire (boot.h) */
if (!v4_message_text(&out, 0, CONSOLE_ID, V4_MSG_TEXT, text, len)) return V4_BOOT_LINE_TOO_LONG;
if (v4_wire_put(TO_NODE, out.word, out.word + V4_MSG_HEADER) != V4_WIRE_DONE) return V4_BOOT_LINE_STOPPED;
if (v4_wire_put(TO_NODE, out.word, out.word + V4_MSG_HEADER) != V4_WIRE_DONE) return V4_BOOT_LINE_REFUSED; /* no room: refused at once, as any message is */
}
for (;;) {
int awake = boot_step(b);
+3 -1
View File
@@ -90,6 +90,7 @@
#define S_ON (BUF0_W - 38) /* and whether its mark has been put at its first message */
#define KEEP (BUF0_W - 40) /* the port the answer to the text being done goes by, on whose wire 8 cells are kept for it; -1: none */
#define DEFER (BUF0_W - 41) /* a port whose wire had no room for the answer to text that waits to be begun; -1: none */
#define D_MASK (BUF0_W - 8) /* and a bit for each such port: text whose answer goes by one is not begun on this pass */
#define IN_HDR (BUF0_W - 56) /* the seven words of the message a wire's mark is at */
#define ONE (BUF0_W - 49) /* the one word of text of a NACK, a GONE or an answer, coming or going */
#define OUT_HDR (BUF0_W - 112) /* the seven words of a message this node is putting on a wire */
@@ -104,7 +105,7 @@
#define SEND_PTR (BUF0_W - 101) /* where the next word of a text being made ready to send goes */
#define OUT_TEXT (OUT_W - 64) /* what was printed, four characters to a cell, as it goes on the wire: 64 cells */
#define SEND_TEXT (OUT_W - 400) /* the text of a message SEND is putting, four characters to a cell: 256 cells */
/* FREE, plain memory: BUF0_W - 8; - 32 .. - 21; - 122 .. - 113; BUF0_W - 144 .. - 141 (what a flush makes zero after
/* FREE, plain memory: BUF0_W - 32 .. - 21; - 122 .. - 113; BUF0_W - 144 .. - 141 (what a flush makes zero after
* a full output buffer); and OUT_W - 144 .. OUT_W - 65. They and
* the cells above held the messages waiting, the refusals owed and what went with them (MESH.md 7a, 7b), which a
* node no longer keeps. */
@@ -227,6 +228,7 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
v4_text_constant(tx, "(S-ON)", S_ON);
v4_text_constant(tx, "(KEEP)", KEEP);
v4_text_constant(tx, "(DEFER)", DEFER);
v4_text_constant(tx, "(D-MASK)", D_MASK);
v4_text_constant(tx, "(IN)", IN_HDR);
v4_text_constant(tx, "(ONE)", ONE);
v4_text_constant(tx, "(OUT-HDR)", OUT_HDR);
+126
View File
@@ -350,6 +350,98 @@ int main(void)
"a node that can take an error is let go, and the error is raised on it: its P is its handler");
CHECK(!v4_node_port_gone(&ND(c)->n, 18), "a node that is not blocked at a port is not touched");
/* ---- A WORD WRITTEN TO WHAT WILL NOT READ ONE (found by the final review of step 6e) ----
* A node that writes a word to a port is blocked until it is taken. Two things will never take it: a
* device that takes no words -- one that speaks whole messages only -- and a node that is asleep at its
* wires, which executes nothing until a message comes. The writer is let go, as from a port with no one
* on it. A neighbour that is doing something else, or that the fabric has put to sleep, may yet read:
* the writer waits. */
{
static v4_wire_queue wq[4];
static const v4_device mute = { 0, 0, 0 }; /* it takes and gives no words */
static const v4_cell one[1] = { 5 };
static stream s;
static v4_device giver;
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, wq, 4);
a = loaded(0); /* writes a word to its port 0, and then counts in OUT */
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B);
LIT(OUT); O(BANG_A);
{
v4_cell again = v4_asm_label(&as);
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
v4_asm_branch(&as, V4_OP_JUMP, again);
}
CHECK(v4_asm_ok(&as) && v4_fabric_wire_device(&f, a, 0, &mute), "a writer, wired to a device that takes no words");
(void)settle(100);
CHECK(ND(a)->n.asking && ND(a)->n.mem[OUT] == 0, "it is blocked writing");
v4_fabric_gone_error(&f, 18);
(void)settle(100);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "a bare node, which cannot take an error, waits there");
v4_node_error_attach(&ND(a)->n, OUT + 20);
v4_node_fault_attach(&ND(a)->n, 900);
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18 && ND(a)->n.mem[OUT + 20] == 18,
"a node that can take one is let go: no one on that port");
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, wq, 4);
v4_fabric_gone_error(&f, 18);
a = loaded(0);
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B);
wait_for_ever();
b = loaded(1); /* reads a word from its port 5, and then sleeps at its wires */
LIT(PB + 5); O(BANG_B); O(FETCH_B); O(DROP);
LIT(P_DO); O(BANG_B); LIT(8); O(STORE_B);
wait_for_ever();
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a writer wired to a node that is waiting to read another port");
v4_node_error_attach(&ND(a)->n, OUT + 20);
v4_node_fault_attach(&ND(a)->n, 900);
(void)settle(200);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0 && ND(b)->n.reading, "the neighbour is not asleep at its wires and may yet read: the writer waits");
v4_fabric_sleep(&f, b);
memset(&s, 0, sizeof s); s.words = one; s.count = 1;
giver.give = stream_give; giver.take = 0; giver.self = &s;
CHECK(v4_fabric_wire_device(&f, b, 5, &giver), "the neighbour is put to sleep by the fabric, and a device with a word for it is wired to its port 5");
(void)settle(200);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0 && !v4_node_doing(&ND(b)->n), "a neighbour the fabric has put to sleep takes nothing and does nothing: the writer waits");
v4_fabric_wake(&f, b);
for (i = 0; i < 200 && ND(a)->n.asking; i++) (void)v4_fabric_step(&f);
CHECK(v4_node_doing(&ND(b)->n) && ND(b)->n.do_op == 8, "woken, the neighbour takes its word and sleeps at its wires");
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18, "and the writer is let go: no one on that port will read a word");
/* nor does it matter that such a node is awake and running: once a node has asked for a whole message
* it is not one that words are written to. A stuck node does not hold whoever writes it a word. */
v4_fabric_init(&f, places, 4);
v4_fabric_queues(&f, wq, 4);
v4_fabric_gone_error(&f, 18);
a = loaded(0);
LIT(PB + 0); O(BANG_B); LIT(7); O(STORE_B);
wait_for_ever();
b = loaded(1); /* reads a word from its port 5, asks whether its wire 3 has room, and then counts for ever */
LIT(PB + 5); O(BANG_B); O(FETCH_B); O(DROP);
LIT(P_WIRE); O(BANG_B); LIT(3); O(STORE_B);
LIT(P_A); O(BANG_B); LIT(8); O(STORE_B);
LIT(P_DO); O(BANG_B); LIT(10); O(STORE_B);
LIT(OUT); O(BANG_A);
{
v4_cell again = v4_asm_label(&as);
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
v4_asm_branch(&as, V4_OP_JUMP, again);
}
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a writer wired to a node that has not yet asked anything of a wire");
v4_node_error_attach(&ND(a)->n, OUT + 20);
v4_node_fault_attach(&ND(a)->n, 900);
(void)settle(200);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "the writer waits");
memset(&s, 0, sizeof s); s.words = one; s.count = 1;
CHECK(v4_fabric_wire_device(&f, b, 5, &giver), "the neighbour is given its word");
for (i = 0; i < 200 && ND(a)->n.asking; i++) (void)v4_fabric_step(&f);
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 18, "it asks for room, and the writer is let go");
for (i = 0; i < 20; i++) (void)v4_fabric_step(&f);
CHECK(!v4_node_doing(&ND(b)->n) && ND(b)->n.mem[OUT] > 0, "though that node is not asleep: it goes on counting");
}
/* ---- asleep, awake, and removed, while looping; and a node born while others run ---- */
v4_fabric_init(&f, places, 4);
a = loaded(0); /* counts in OUT for ever */
@@ -710,6 +802,40 @@ int main(void)
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1 && f.place[a].wire[0].tx->used + 200 > V4_WIRE_CELLS, "it wakes, and how it went says 1: no room, but a message");
/* A node asleep for room on one wire is woken when another of its wires is cut, or the node on it
* removed: it may be keeping something to put there, and must find out that it cannot (found by the
* final review of step 6e: before, it slept on until something else happened). */
v4_fabric_init(&f, more_places, 8);
v4_fabric_queues(&f, qpool, 8);
a = (unsigned)v4_fabric_add(&f, &pool[0], PB);
b = (unsigned)v4_fabric_add(&f, &pool[1], PB);
c = (unsigned)v4_fabric_add(&f, &pool[2], PB);
CHECK(v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, a, 1, c, 2), "a node with two wires");
fill(f.place[a].wire[0].tx, 0);
fill(f.place[a].wire[1].tx, 0);
(void)v4_node_fetch(&ND(a)->n, P_HAVE);
(void)v4_fabric_step(&f);
CHECK(ask(a, 0, 100, 0, 9) == -99, "both full: asleep for room on wire 0");
CHECK(v4_fabric_unwire(&f, a, 1), "its wire 1 is cut");
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "it wakes, told 1: the wire it named has no room, but something has changed");
CHECK(ask(a, 1, 100, 0, 10) == 2, "it asks, and finds no one on port 1");
CHECK(ask(a, 0, 100, 0, 9) == -99, "asleep for room on wire 0 again");
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "and it is not woken twice for the one cut");
CHECK(v4_fabric_wire(&f, a, 1, c, 2), "the wire is made again");
fill(f.place[a].wire[1].tx, 0);
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "a wire being made does not wake it");
CHECK(v4_fabric_remove(&f, c) == &pool[2], "the node on wire 1 is removed");
(void)v4_fabric_step(&f);
CHECK(!ND(a)->n.doing && ND(a)->n.how == 1, "it wakes, told 1");
ND(a)->n.doing = 0;
v4_node_store(&ND(a)->n, P_DO, 8);
CHECK(v4_fabric_wire(&f, a, 1, b, 4) && v4_fabric_unwire(&f, a, 1), "asleep for a message (8), with a wire made and cut");
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.doing, "a node asleep for a message is not woken by a cut: it keeps nothing");
/* SLEEP FOR ROOM IS WOKEN BY ROOM ON ANY OF THE NODE'S WIRES (MESH.md 7d.3): a node may be waiting to
* put on more than one, and names only one */
v4_fabric_init(&f, more_places, 8);
+149 -27
View File
@@ -262,35 +262,76 @@ static int answers(v4_cell node, unsigned id)
size_t len = strlen(got);
return len >= 2 && strcmp(got + len - 2, "3 ") == 0 && ended >= 1 && ended_from == node && at_rest(id);
}
/* The exchange that `text`, sent to `node`, begins is played from the mesh as kept, once for every step it
* takes, with the node at place `gone` removed at that step. Every node left must come to rest, blocked in
* nothing, and answer. With the middle node gone the far one is wired to the first, as whoever removed it
* would, so that it can be asked. How many steps the exchange takes; how many removals went wrong, and
* the first of them. */
/* AN EXCHANGE, DISTURBED AT EVERY STEP. The exchange that start() begins is played from the mesh as kept:
* once whole, to see how many steps it takes, and then once for every one of those steps with the mesh
* disturbed at that step --
* 1 node 11 removed, and node 12 then wired to node 10, as whoever removed it would, so that it can be asked;
* 2 node 12 removed;
* 3 the wire between nodes 11 and 12 cut and made again at once: what waited on it is let go.
* After each, every node left must come to rest, asleep and blocked at no port, and answer. With `moments`
* only the steps at which something the fabric keeps had changed are tried -- what is on a wire, or what a
* node asks of one: for an exchange too long to disturb at every step. A removal between two such steps
* finds the mesh as the one before it did.
* How many steps the exchange takes; how many disturbances were tried; how many went wrong, and the first. */
static v4_fabric_node now_pool[3];
static v4_place now_places[3];
static v4_wire_queue now_queues[QUEUES];
static v4_fabric now_f;
static unsigned removed_at_every_step(v4_cell node, const char *text, unsigned gone, unsigned *wrong, unsigned *first_wrong)
static v4_cell start_node;
static const char *start_text;
static int start_plain(void) { return put_text(start_node, start_text); }
static int hera_busy(void) { return pool[hera].n.mem[QUIET] == 0; }
/* Text for node 12 that prints, with node 10 made busy and the wire from node 11 to node 10 full but for 7
* cells: what node 12 prints, and how its text ended, are held by node 11, asleep for room (MESH.md 7d.4). */
static int start_held(void)
{
unsigned long before;
static v4_cell big[V4_WIRE_HEADER + V4_MSG_MAX_CHARS / 4u];
v4_wire_queue *q = places[mid].wire[2].tx;
unsigned k;
int ok = put_text(12, "A") && put_text(10, "SLOW") && run_until(hera_busy, 5000); /* node 10 has passed the one on and begun the other */
memset(big, 0, sizeof big);
big[0] = CONSOLE_ID; big[1] = 12; big[2] = V4_MSG_OUTPUT; big[3] = 16; big[6] = 1024;
for (k = 0; k < 3; k++) ok = ok && v4_wire_put(q, big, big + V4_WIRE_HEADER) == V4_WIRE_DONE;
big[6] = 884;
return ok && v4_wire_put(q, big, big + V4_WIRE_HEADER) == V4_WIRE_DONE && V4_WIRE_CELLS - q->used == 7;
}
/* what the fabric keeps, in a number: what every wire holds and what every node asks of one */
static unsigned long fabric_mark(void)
{
unsigned long h = 1469598103UL;
unsigned k;
for (k = 0; k < 3; k++) if (v4_fabric_node_at(&f, k)) h = (h ^ (unsigned long)(pool[k].n.doing * 16 + (int)pool[k].n.do_op)) * 16777619UL;
for (k = 0; k < QUEUES; k++) h = ((h ^ queues[k].used) * 16777619UL ^ queues[k].head) * 16777619UL;
return h;
}
static unsigned disturbed_at_every_step(int (*start)(void), unsigned how, int moments, unsigned *tried, unsigned *wrong, unsigned *first_wrong)
{
unsigned long before, mark, last = 0;
unsigned whole, s;
*wrong = 0; *first_wrong = 0;
*tried = 0; *wrong = 0; *first_wrong = 0;
put_back();
before = steps_run;
if (!put_text(node, text) || !run()) { *wrong = 1; return 0; }
saw_mid_hold = 0; watch = watch_mid_holds;
if (!start() || !run()) { watch = 0; *wrong = 1; return 0; }
watch = 0;
whole = (unsigned)(steps_run - before);
put_back();
if (!put_text(node, text)) { *wrong = 1; return 0; }
if (!start()) { *wrong = 1; return 0; }
for (s = 0; s <= whole; s++) {
int ok;
memcpy(now_pool, pool, sizeof pool); memcpy(now_places, places, sizeof places); memcpy(now_queues, queues, sizeof queues); now_f = f; /* the exchange, s steps on */
ok = v4_fabric_remove(&f, gone) == &pool[gone] && run();
ok = ok && at_rest(hera) && (gone == mid ? at_rest(far) : at_rest(mid));
if (ok && gone == mid) ok = v4_fabric_wire(&f, hera, 2, far, 2) && answers(12, far) && answers(10, hera);
else if (ok) ok = answers(11, mid) && answers(10, hera);
if (!ok) { if (!*wrong) *first_wrong = s; (*wrong)++; }
memcpy(pool, now_pool, sizeof pool); memcpy(places, now_places, sizeof places); memcpy(queues, now_queues, sizeof queues); f = now_f;
mark = fabric_mark();
if (!moments || s == 0 || mark != last) {
int ok;
memcpy(now_pool, pool, sizeof pool); memcpy(now_places, places, sizeof places); memcpy(now_queues, queues, sizeof queues); now_f = f; /* the exchange, s steps on */
if (how == 1) ok = v4_fabric_remove(&f, mid) == &pool[mid] && run() && at_rest(hera) && at_rest(far)
&& v4_fabric_wire(&f, hera, 2, far, 2) && answers(12, far) && answers(10, hera);
else if (how == 2) ok = v4_fabric_remove(&f, far) == &pool[far] && run() && at_rest(hera) && at_rest(mid) && answers(11, mid) && answers(10, hera);
else ok = v4_fabric_unwire(&f, mid, 3) && v4_fabric_wire(&f, mid, 3, far, 2) && run() && at_rest(hera) && at_rest(mid) && at_rest(far)
&& answers(12, far) && answers(11, mid) && answers(10, hera);
(*tried)++;
if (!ok) { if (!*wrong) *first_wrong = s; (*wrong)++; }
memcpy(pool, now_pool, sizeof pool); memcpy(places, now_places, sizeof places); memcpy(queues, now_queues, sizeof queues); f = now_f;
}
last = mark;
(void)v4_fabric_step(&f); (void)console_takes();
}
return whole;
@@ -995,32 +1036,113 @@ int main(void)
CHECK(between_data <= 4 && between_ret <= 7, "between texts: four cells of the data stack, and seven return entries");
/* ---- A NODE REMOVED AT EVERY STEP (MESH.md 7d.7, acceptance 6) ----
* Three exchanges, each played once whole and then once for every step it takes, with node 11 removed
* at that step, and again with node 12: the nodes left come to rest, blocked in nothing, and answer.
/* ---- A WORD WRITTEN TO A PORT, PORT!, HOLDS NO ONE (found by the final review of step 6e) ----
* A node's neighbours speak whole messages and are asleep at their wires; so is its console. A word
* written to one of them will never be read. The line that wrote it ends in an error and the node goes
* on; before, it was blocked for ever, and every node beyond it was cut off. */
CHECK(row(), "a row made afresh");
step_cap = 2000000;
CHECK(strstr(tell(10, "5 2 PORT! 65 EMIT"), "No one on that port") != NULL && strchr(printed, 'A') == NULL && ended == 1 && ended_how == V4_TEXT_ERROR && asleep(hera),
"node 10 writes a word to node 11's port: its line ends No one on that port: \"%s\"", printed);
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && strcmp(tell(11, "20 22 + ."), "42 ") == 0 && strcmp(tell(12, "7 8 * ."), "56 ") == 0, "and all three answer");
CHECK(strstr(tell(10, "5 1 PORT!"), "No one on that port") != NULL && ended_how == V4_TEXT_ERROR && strcmp(tell(10, "1 2 + ."), "3 ") == 0,
"the same for a word written to the console's port: \"%s\"", printed);
CHECK(strstr(tell(11, "5 3 PORT!"), "No one on that port") != NULL && ended_from == 11 && strcmp(tell(12, "7 8 * ."), "56 ") == 0 && strcmp(tell(11, "20 22 + ."), "42 ") == 0,
"and for node 11 writing to node 12's: node 12 is not cut off");
stuck = 1u << mid;
(void)tell(11, ": C BEGIN 0 UNTIL ; C");
CHECK(ended == 0 && !asleep(mid), "node 11 is stuck in a loop: it does not sleep");
CHECK(strstr(tell(10, "5 2 PORT!"), "No one on that port") != NULL && ended_how == V4_TEXT_ERROR && strcmp(tell(10, "1 2 + ."), "3 ") == 0,
"a word node 10 writes to it is refused all the same, and node 10 can be typed to: \"%s\"", printed);
stuck = 0;
step_cap = 20000000;
/* ---- TWO TEXTS FROM ONE NODE ARE BEGUN IN THE ORDER THEY CAME (acceptance 8; found by the final review) ----
* Node 12 has two texts from node 10 on its wire, and the wire their answers go by has 7 cells free:
* neither can be begun. Node 11, which was not taking, then takes a message off that wire -- at every
* step in turn, so that the room comes before node 12 looks at the first text, between its look at the
* first and at the second, and after both. Whenever it comes, the first is begun first. */
{
static v4_cell big[V4_WIRE_HEADER + V4_MSG_MAX_CHARS / 4u], hd[V4_WIRE_HEADER];
static v4_message m;
unsigned k, wrong = 0, first_wrong = 0, undone = 0;
v4_wire_queue *out, *in;
CHECK(row() && told(12, ": X1 65 EMIT ; : X2 66 EMIT ;"), "a row made afresh; node 12 has two words that print");
v4_fabric_sleep(&f, mid); /* node 11 takes nothing for now */
out = places[far].wire[2].tx; in = places[far].wire[2].rx;
memset(big, 0, sizeof big);
big[0] = 1; big[1] = 12; big[2] = V4_MSG_OUTPUT; big[3] = 16;
big[6] = 1024; for (i = 0; i < 3; i++) (void)v4_wire_put(out, big, big + V4_WIRE_HEADER);
big[6] = 884; (void)v4_wire_put(out, big, big + V4_WIRE_HEADER);
CHECK(V4_WIRE_CELLS - out->used == 7, "the wire from node 12 to node 11 has 7 cells free: %u", V4_WIRE_CELLS - out->used);
CHECK(v4_message_text(&m, 12, 10, V4_MSG_TEXT, "X1", 2) && (m.word[3] = 16, m.word[5] = 1, v4_wire_put(in, m.word, m.word + V4_MSG_HEADER) == V4_WIRE_DONE)
&& v4_message_text(&m, 12, 10, V4_MSG_TEXT, "X2", 2) && (m.word[3] = 16, m.word[5] = 2, v4_wire_put(in, m.word, m.word + V4_MSG_HEADER) == V4_WIRE_DONE),
"two texts from node 10 are on the wire to node 12, numbered 1 and 2");
keep();
for (k = 0; k < 700; k++) {
unsigned began = 0, seq[2] = { 0, 0 };
put_back();
for (i = 0; i < 100000 && began < 2; i++) {
const v4_node *n = &pool[far].n;
int kept = n->mem[KEEP] >= 0;
if (i == k) { v4_wire_first(out); (void)v4_wire_look(out, hd); (void)v4_wire_drop(out); }
(void)v4_fabric_step(&f); (void)console_takes();
if (!kept && n->mem[KEEP] >= 0) seq[began++] = (unsigned)n->mem[MSG + 5];
}
if (began < 2) undone++;
else if (seq[0] != 1 || seq[1] != 2) { if (!wrong) first_wrong = k; wrong++; }
}
CHECK(undone == 0 && wrong == 0, "whenever the room came, both texts were done, the first first: %u not done, %u in the wrong order, the first when it came at step %u", undone, wrong, first_wrong);
}
/* ---- A NODE REMOVED, OR A WIRE CUT, AT EVERY STEP (MESH.md 7d.7, acceptance 6) ----
* Three exchanges, each played once whole and then once for every step it takes with node 11 removed at
* that step, again with node 12 removed, and again with the wire between them cut and made again: the
* nodes left come to rest, blocked at nothing, and answer.
* What is typed is a word defined beforehand, and so is what a node left is asked (Q, which is
* `1 2 + .`): a line of several words is mostly the dictionary searched, thousands of steps in which the
* mesh does nothing, and every one of them would be a removal played to the end. */
* mesh does nothing, and every one of them would be a removal played to the end.
* WHAT "ANSWERS" MEANS HERE. A node that was waiting for an answer from a node now removed, or for one
* that was on a wire now cut, is still waiting when the mesh comes to rest: removing a node tells no one
* (Hera's KILL does: test_host_unit.c). Its wait is ended by the line it is then typed, "Interrupted",
* and it answers. */
{
static const struct { const char *what, *set10, *set11, *set12; v4_cell node; const char *text; } ex[3] = {
{ "text from the console for node 12, what it prints and how it ended", "1 DROP", "1 DROP", ": A 7 8 * . ;", 12, "A" },
{ "node 11 SENDs node 12 text and waits for its answer", "1 DROP", ": T S\" Q\" 12 SEND 12 AWAIT . ;", "1 DROP", 11, "T" },
{ "a message node 12 has no way for, and its NACK to node 10", "55 2 ROUTE : N S\" 1 DROP\" 55 SEND ;", "55 3 ROUTE", "NO-ROUTES 1 2 ROUTE 10 2 ROUTE 11 2 ROUTE", 10, "N" }
};
unsigned e, g, whole, wrong, first;
static const char *const how_name[4] = { "", "node 11 removed", "node 12 removed", "the wire between nodes 11 and 12 cut and made again" };
unsigned e, g, whole, tried, wrong, first, all = 0;
step_cap = 2000000;
for (e = 0; e < 3; e++) {
CHECK(row() && told(10, ex[e].set10) && told(11, ex[e].set11) && told(12, ex[e].set12) && told(10, ASK) && told(11, ASK) && told(12, ASK), "a row made afresh for: %s", ex[e].what);
step_cap = 2000000;
keep();
for (g = 1; g <= 2; g++) {
whole = removed_at_every_step(ex[e].node, ex[e].text, g == 1 ? mid : far, &wrong, &first);
CHECK(whole > 0 && wrong == 0, "%s, in %u steps: with node %u removed at each of them the rest come to rest and answer: %u did not, the first at step %u",
ex[e].what, whole, 10u + g, wrong, first);
start_node = ex[e].node; start_text = ex[e].text;
for (g = 1; g <= 3; g++) {
whole = disturbed_at_every_step(start_plain, g, 0, &tried, &wrong, &first);
all += tried;
CHECK(whole > 0 && wrong == 0, "%s, in %u steps: with %s at each of them the rest come to rest and answer: %u did not, the first at step %u",
ex[e].what, whole, how_name[g], wrong, first);
}
}
put_back();
CHECK(is_empty(tell(10, "N")) && pool[hera].n.mem[REFUSED] == 1 && pool[far].n.mem[LOST] == 1, "the last of them, whole: node 12 let the message go and node 10 was told it was refused");
/* and with what node 12 prints held at node 11, which sleeps for room: at every step at which
* anything on a wire, or what a node asks of one, has changed */
CHECK(row() && told(12, ": A 7 8 * . ;") && told(10, ": SLOW 3000 0 DO LOOP ;") && told(10, ASK) && told(11, ASK) && told(12, ASK), "a row made afresh for output held at a full wire");
step_cap = 2000000;
keep();
for (g = 1; g <= 3; g++) {
whole = disturbed_at_every_step(start_held, g, 1, &tried, &wrong, &first);
all += tried;
CHECK(whole > 0 && saw_mid_hold > 0, "text for node 12 that prints, node 10 busy and the wire to it full: node 11 held what was printed, asleep for room, for %u of %u steps", saw_mid_hold, whole);
CHECK(tried > 20 && wrong == 0, "with %s at each of the %u steps at which a wire or a node's asking changed, the rest come to rest and answer: %u did not, the first at step %u",
how_name[g], tried, wrong, first);
}
printf(" a node removed or a wire cut: %u times in all\n", all);
step_cap = 20000000;
}
+1
View File
@@ -94,6 +94,7 @@ int main(void)
if (leaving) { fflush(stdout); return 0; }
else if (how == V4_TEXT_ERROR) fputs(" ERROR\n", stdout);
else if (how == V4_BOOT_LINE_TOO_LONG) fputs("Line too long\n ERROR\n", stdout);
else if (how == V4_BOOT_LINE_REFUSED) fputs("Message refused\n ERROR\n", stdout); /* the node's wire is full: error 19's words */
else if (how == V4_BOOT_LINE_NO_INPUT) { putchar('\n'); return 0; }
else if (how == V4_BOOT_LINE_STOPPED) { fflush(stdout); fprintf(stderr, "starforth4: the node stopped on a fault\n"); return 1; }
}