fix(v4.0.0): step 6c mended after its review -- what a stuck or gone node can and cannot hold up
The review of step 6c found it did not keep its rule. Ruled: fix them all; 16 hops. - Paying a NACK no longer waits on a lower-numbered neighbour; a node that owes one does not sleep, and goes on taking in what it is sent. - AWAIT looks first through the messages already waiting. - A node waiting to write to a removed neighbour gets error 18: the word that says who is reading also says which ports have anything on them. - A console line lets Hera go when she is blocked on the first word of a message (v4_fabric_interrupt_error, a device's `pending`). - A refused answer is told to the node that waits, not the one that answered; one owed to the node itself goes to its own queue. - A message is passed on by at most 16 nodes, then refused. - A GONE is believed only when it says it is from the node's centre. - Tests: the check that tested nothing is replaced; no room for a waiting sender; the limit that remains is tested as a limit. - MESH.md 7b's rule reworded to what holds, 7b.7 says where it falls short; README likewise. make -C v4 test, sanitize, hosted-check; three bare-metal boots. POST 538 of 538, word_count=317, dict_hash=0xc0769523a47b7dc3 on all six. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d343001dea
commit
59727f762a
Binary file not shown.
+103
-8
@@ -295,9 +295,16 @@ kernel-Hermes's work in v3 and is not decided for the mesh.
|
||||
|
||||
## 7b. Refusals and waits (ruled 2026-10-07)
|
||||
|
||||
**The rule of this section: no message is lost without its sender being
|
||||
told, and no node waits for ever.** It closes two defects found in what
|
||||
was built: a message arriving at a node with no room for it was dropped
|
||||
**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
|
||||
gone; and a wait on a node that is alive and stuck is ended by Hera's
|
||||
killing that node, or from the console. A message that is refused is
|
||||
told to whoever waits on it, while the node that refuses it has room to
|
||||
remember that it owes the telling.** As first written the rule was "no
|
||||
message is lost without its sender being told, and no node waits for
|
||||
ever"; the review showed that was more than what was built could keep,
|
||||
and section 7b.7 says exactly where it falls short. It closes two defects
|
||||
found in what was built: a message arriving at a node with no room for it was dropped
|
||||
and counted, and its sender never knew; and a node waiting for an answer
|
||||
(`AWAIT`) waited for ever if none came. Captain Bob: "no bad code is ever
|
||||
released".
|
||||
@@ -354,6 +361,23 @@ Two types, beside text (1), output (2) and done (3):
|
||||
to do: the moment it finds it has no room is the moment it is taking
|
||||
messages in so as to be free to write (section 7a), and it cannot begin
|
||||
a message of its own there.
|
||||
- When the message refused is a node's answer (type 3), the NACK is owed
|
||||
to the node the answer was for, which is the one that waits, and is
|
||||
about the node that answered.
|
||||
- A NACK owed to this node itself -- its own message came back to it and
|
||||
was let go here -- is put with its messages waiting, as if it had come
|
||||
by a port.
|
||||
- Paying one: if the neighbour it goes by is reading, it is sent. If not,
|
||||
and that neighbour's number is the higher, it is written all the same,
|
||||
as any message to it is (section 7a). If its number is the lower, the
|
||||
NACK stays owed; the node goes on taking in what is written to it and
|
||||
tries again, and does not sleep while it owes one. One there is no way
|
||||
for, or whose way is a port with nothing on it, is dropped and counted.
|
||||
- A message is passed on by at most 16 nodes. Its fourth word, the TTL,
|
||||
carries how many more may pass it on; the node that would be the
|
||||
sixteenth refuses it as it would one it had no way for. (Ruled
|
||||
2026-10-07: 16.) This is what stops a message going round for ever
|
||||
between two nodes each of which believes the way is through the other.
|
||||
- A NACK or a GONE that cannot be taken or passed on is dropped and
|
||||
counted, and nothing is owed for it: there is no NACK for a NACK.
|
||||
- Part of the queue is kept for NACK and GONE alone, so that ordinary
|
||||
@@ -365,9 +389,18 @@ Two types, beside text (1), output (2) and done (3):
|
||||
in an error: 19 "Message refused", or 20 "Node gone".
|
||||
- If it is not, a NACK is counted in `(REFUSED)`, and a GONE makes it
|
||||
forget the way to that node.
|
||||
- It acts on a GONE only if it came by the port that leads to everything
|
||||
the node has no other way to — its centre — so that only Hera can
|
||||
declare a node gone. Hera herself has no such port and sends them.
|
||||
- It acts on a GONE only if the message says it is from the node's centre:
|
||||
the node on the port that leads to everything it has no other way to,
|
||||
whose number it was told (`NEIGHBOUR`). One that comes by another port,
|
||||
or by that port from another node, is not believed. Hera herself has no
|
||||
centre and sends them. (As first built a node looked only at the port
|
||||
the GONE came by, and so believed one that any node sent by way of its
|
||||
centre.) A message's "from" word is written by its sender and nothing
|
||||
yet proves it: that is the ACL tag's work, in the later step named in
|
||||
section 3.
|
||||
- What ends a wait is looked for first among the messages already
|
||||
waiting: an answer, a NACK or a GONE may have been taken in before the
|
||||
wait began.
|
||||
|
||||
### 7b.5 Hera
|
||||
|
||||
@@ -385,6 +418,18 @@ Two types, beside text (1), output (2) and done (3):
|
||||
as usual.
|
||||
- On the two products there is one node, and nobody but the console could
|
||||
ever answer it: a line typed while it waits does the same.
|
||||
- **A blocked write (ruled 2026-10-07).** Hera has the lowest number, so
|
||||
she writes to a neighbour without looking and is blocked until it reads
|
||||
(section 7a); if it never does, nothing could be typed to her again. So
|
||||
when a node is blocked writing the first word of a message to another
|
||||
node, and its console has a line for it, the fabric raises error 21 on
|
||||
it: it gives that message up and goes on to read the line. Only on the
|
||||
first word, so that half a message is never left on a wire
|
||||
(`v4_fabric_interrupt_error`, `v4/include/v4/fabric.h`).
|
||||
- **A node waiting to write to a neighbour that has been removed** is told:
|
||||
the word that says which neighbours are reading also says which ports
|
||||
have anything on them at all, and with nothing there the wait ends in
|
||||
error 18, "No one on that port".
|
||||
|
||||
### 7b.7 Limits
|
||||
|
||||
@@ -394,6 +439,23 @@ Two types, beside text (1), output (2) and done (3):
|
||||
console. v3's pool is finite too, 32 messages, and a send beyond it is
|
||||
refused.
|
||||
- A node whose neighbour is asleep waits until it is woken (section 4.1).
|
||||
- **A node that is stuck can hold up its neighbours until it is killed.**
|
||||
A node cannot tell a neighbour that is busy for a moment from one that
|
||||
is stuck for good, and there is no clock (ruling 2). So:
|
||||
- a node that owes a NACK to a stuck neighbour whose number is the
|
||||
higher is blocked writing it;
|
||||
- a node that owes one to a stuck neighbour whose number is the lower
|
||||
goes on taking in what lower-numbered neighbours write to it, but does
|
||||
not sleep, and a higher-numbered neighbour with something for it
|
||||
waits;
|
||||
- a node with a message for a stuck neighbour waits to write it.
|
||||
|
||||
Each ends when Hera kills the stuck node: the wire is then empty, the
|
||||
waiting node is told so (error 18) and goes on. Hera herself can always
|
||||
be typed to (7b.6). None of this can happen on a node that is gone.
|
||||
- A node that is waiting for an answer keeps what comes for other nodes
|
||||
and passes it on when its wait ends, not before.
|
||||
- A node that was never told who is on its centre's port believes no GONE.
|
||||
|
||||
## 8. Storage
|
||||
|
||||
@@ -920,7 +982,8 @@ Each is tested, committed and pushed before the next.
|
||||
6. *The suite.* `make -C v4 test` and `sanitize` pass at both widths;
|
||||
`mkcapsule --lint capsules/` is clean.
|
||||
|
||||
**6c. Refusals and waits (section 7b).** **Done 2026-10-07.**
|
||||
**6c. Refusals and waits (section 7b).** **Done 2026-10-07; reviewed
|
||||
and mended the same day (below).**
|
||||
- *In the nucleus* (`v4/capsule/core.v4`, `quit.v4`): `(OWE)` and
|
||||
`(PAY)`; the room kept for NACK and GONE; `NO-ROUTE`; `GONE`; `AWAIT`
|
||||
ending in errors 19, 20 and 21; `(REFUSED)`.
|
||||
@@ -939,7 +1002,39 @@ Each is tested, committed and pushed before the next.
|
||||
bare-metal boots, `logs/20261007-202737` (amd64), `-203230`
|
||||
(aarch64), `-203616` (riscv64). On all six: POST 538 of 538,
|
||||
`word_count=317`, `dict_hash=0x54520ade672566ad`.
|
||||
- **Found on the way, and not mended: for ruling.**
|
||||
- *Verified again after the review's mends.* `make -C v4 test`,
|
||||
`sanitize` and `hosted-check`; three bare-metal boots,
|
||||
`logs/20261007-220949` (amd64), `-221223` (aarch64), `-221549`
|
||||
(riscv64). On all six: POST 538 of 538, `word_count=317`,
|
||||
`dict_hash=0xc0769523a47b7dc3` (the nucleus changed, so the hash
|
||||
did).
|
||||
- **The review (2026-10-07) found the step did not keep its rule**, and
|
||||
Captain Bob ruled: "fix them all; 16 hops". Mended, each with a test
|
||||
that failed first or was shown to fail with the mend taken out:
|
||||
- *A stuck node stalled a neighbour that owed it a NACK.* Paying one
|
||||
no longer waits on a lower-numbered neighbour (7b.3). What is left
|
||||
of this is in 7b.7.
|
||||
- *`AWAIT` missed what had already come.* It looks through the
|
||||
messages waiting first (7b.4).
|
||||
- *A node waiting to write to a removed neighbour went round for
|
||||
ever.* It gets error 18 (7b.6).
|
||||
- *Hera blocked writing to a stuck node could not be typed to.* A
|
||||
console line lets her go, on the first word of a message (7b.6).
|
||||
- *A refused answer was told to the node that answered.* It is told
|
||||
to the node that waits (7b.3).
|
||||
- *A message could go round for ever.* 16 nodes may pass it on (7b.3).
|
||||
- *Any node could forge a GONE by way of the centre.* The sender is
|
||||
looked at, not only the port (7b.4).
|
||||
- *A check that tested nothing*, the "GONE not from its centre" one,
|
||||
is replaced by two that send one and see it disbelieved; and "no
|
||||
room" is now tested for a sender that waits.
|
||||
- *The rule claimed too much.* Reworded (7b); `v4/README.md` too.
|
||||
|
||||
The engine gained: the bits that say which ports have anything on
|
||||
them; `v4_node_words_since_look`; a device's `pending`; and
|
||||
`v4_fabric_interrupt_error`. Tests: `test_fabric.c` 78 checks,
|
||||
`test_host_mesh.c` 109, `test_host_unit.c` 79.
|
||||
- **Found on the way in the first pass (both mended above).**
|
||||
- *Hera blocked writing to a stuck node.* Hera has the lowest number,
|
||||
so by section 7a she writes to a neighbour without looking and is
|
||||
blocked until it reads. If that neighbour is stuck she is blocked
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
[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 7 2026 22:09:23
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 131
|
||||
Total memory: 1023 MB
|
||||
Usable memory: 966 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 248740
|
||||
Free pages : 247217
|
||||
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=2097256277 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=1005374936, tick_hz=100, initial_count=10053749
|
||||
APIC Timer: configured (masked, ready to start)
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x5ff3672509f6c4e5 capsule_hash=0x5ff3672509f6c4e5 words=311
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xc07eb35ca84c4aa8
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_SYSTEM word_count=317 dict_hash=0xc0769523a47b7dc3
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
|
||||
PCI: init...
|
||||
PCI: ECAM mapped (amd64)
|
||||
virtio-blk: found device
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 76953786] 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> HERE .
|
||||
8300 ok
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,138 @@
|
||||
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 7 2026 22:11:57
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 108
|
||||
Total memory: 4093 MB
|
||||
Usable memory: 4054 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 1039356
|
||||
Free pages : 1037945
|
||||
Used pages : 1411
|
||||
Total MB : 4059
|
||||
Free MB : 4054
|
||||
Used MB : 5
|
||||
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
VMM self-test complete.
|
||||
|
||||
AArch64: running at EL1
|
||||
IDT installed.
|
||||
|
||||
APIC: init...
|
||||
PSCI: no DTB -- using HVC (QEMU virt-machine default)
|
||||
GICv2: no DTB GIC node -- using QEMU virt-machine defaults
|
||||
GICv2: distributor+CPU interface enabled, PPI 30
|
||||
APIC: init done
|
||||
|
||||
Timer: init...
|
||||
Timer: AArch64 generic timer initialised.
|
||||
Timer: init done
|
||||
|
||||
Kernel heap initialized.
|
||||
Heap statistics:
|
||||
Total bytes: 2147483608
|
||||
Free bytes: 2147483608
|
||||
Used bytes: 0
|
||||
Peak bytes: 0
|
||||
Heap base addr: 1207959552
|
||||
Heap end addr: 3355443200
|
||||
|
||||
Heartbeat: init...
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x5ff3672509f6c4e5 capsule_hash=0x5ff3672509f6c4e5 words=311
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xc07eb35ca84c4aa8
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_SYSTEM word_count=317 dict_hash=0xc0769523a47b7dc3
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
|
||||
PCI: init...
|
||||
virtio-blk: found device
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 1193133] 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> HERE .
|
||||
8300 ok
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,208 @@
|
||||
|
||||
OpenSBI v1.8
|
||||
____ _____ ____ _____
|
||||
/ __ \ / ____| _ \_ _|
|
||||
| | | |_ __ ___ _ __ | (___ | |_) || |
|
||||
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
|
||||
| |__| | |_) | __/ | | |____) | |_) || |_
|
||||
\____/| .__/ \___|_| |_|_____/|____/_____|
|
||||
| |
|
||||
|_|
|
||||
|
||||
Platform Name : riscv-virtio,qemu
|
||||
Platform Features : medeleg
|
||||
Platform HART Count : 1
|
||||
Platform HART Protection : pmp
|
||||
Platform IPI Device : aclint-mswi
|
||||
Platform Timer Device : aclint-mtimer @ 10000000Hz
|
||||
Platform Console Device : uart8250
|
||||
Platform HSM Device : ---
|
||||
Platform PMU Device : ---
|
||||
Platform Reboot Device : syscon-reboot
|
||||
Platform Shutdown Device : syscon-poweroff
|
||||
Platform Suspend Device : ---
|
||||
Platform CPPC Device : ---
|
||||
Firmware Base : 0x80000000
|
||||
Firmware Size : 321 KB
|
||||
Firmware RW Offset : 0x40000
|
||||
Firmware RW Size : 65 KB
|
||||
Firmware Heap Offset : 0x47000
|
||||
Firmware Heap Size : 37 KB (total), 0 KB (reserved), 12 KB (used), 23 KB (free)
|
||||
Firmware Scratch Size : 4096 B (total), 1464 B (used), 2632 B (free)
|
||||
Runtime SBI Version : 3.0
|
||||
Standard SBI Extensions : ipi,pmu,srst,sse,hsm,rfnc,fwft,time,base,legacy,dbcn,dbtr
|
||||
Experimental SBI Extensions : none
|
||||
|
||||
Domain0 Name : root
|
||||
Domain0 Boot HART : 0
|
||||
Domain0 HARTs : 0*
|
||||
Domain0 Region00 : 0x0000000080040000-0x000000008005ffff M: (F,R,W) S/U: ()
|
||||
Domain0 Region01 : 0x0000000080000000-0x000000008003ffff M: (F,R,X) S/U: ()
|
||||
Domain0 Region02 : 0x0000000000100000-0x0000000000100fff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region03 : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region04 : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
|
||||
Domain0 Region05 : 0x000000000c400000-0x000000000c5fffff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region06 : 0x000000000c000000-0x000000000c3fffff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region07 : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
|
||||
Domain0 Next Address : 0x0000000020000000
|
||||
Domain0 Next Arg1 : 0x00000000bfe00000
|
||||
Domain0 Next Mode : S-mode
|
||||
Domain0 SysReset : yes
|
||||
Domain0 SysSuspend : yes
|
||||
|
||||
Boot HART ID : 0
|
||||
Boot HART Domain : root
|
||||
Boot HART Priv Version : v1.12
|
||||
Boot HART Base ISA : rv64imafdch
|
||||
Boot HART ISA Extensions : sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu
|
||||
Boot HART PMP Count : 16
|
||||
Boot HART PMP Granularity : 2 bits
|
||||
Boot HART PMP Address Bits : 54
|
||||
Boot HART MHPM Info : 16 (0x0007fff8)
|
||||
Boot HART Debug Triggers : 2 triggers
|
||||
Boot HART MIDELEG : 0x0000000000001666
|
||||
Boot HART MEDELEG : 0x0000000000f4b509
|
||||
[2J[004D[=3h[2J[009D[2J[004D[8;031;100t[2J[016DRISC-V EDK2 firmware version 2025.11-3ubuntu7.3
|
||||
Press ESCAPE within 5 seconds for boot options ERROR: C40000002:V03051002 I0 6D33944A-EC75-4855-A54D-809C75241F6C 83FFF850
|
||||
BdsDxe: failed to load Boot0001 "UEFI Misc Device" fr
|
||||
om PciRoot(0x0)/Pci(0x1,0x0): Not Found
|
||||
[2J[001;001H[=3h[2J[009DStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
Monolithic build - kernel linked directly
|
||||
Collecting boot information...
|
||||
CmdLine: parsed OK
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
GOP: linear framebuffer found
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
Calling kernel_main (monolithic)...
|
||||
riscv64 item 4.3.5a: satp state at kernel entry (before switch)
|
||||
satp.MODE = 0x000000000000000a
|
||||
satp.PPN = 0x00000000000bf868
|
||||
__kernel_start = 0x00000000bdc9caaa
|
||||
riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a)
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: riscv64
|
||||
Build: Oct 7 2026 22:15:10
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 100
|
||||
Total memory: 1020 MB
|
||||
Usable memory: 975 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 250269
|
||||
Free pages : 249617
|
||||
Used pages : 652
|
||||
Total MB : 977
|
||||
Free MB : 975
|
||||
Used MB : 2
|
||||
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
VMM self-test complete.
|
||||
|
||||
IDT installed.
|
||||
|
||||
APIC: init...
|
||||
PLIC: no DTB PLIC node -- using QEMU virt-machine default (base=0x0c000000)
|
||||
PLIC: init (S-mode context 1, threshold=0)
|
||||
APIC: init done
|
||||
|
||||
Timer: init...
|
||||
Timer: RISC-V time CSR @ 10000000 Hz (FALLBACK, no devicetree)
|
||||
Timer: init done
|
||||
|
||||
Kernel heap initialized.
|
||||
Heap statistics:
|
||||
Total bytes: 536870872
|
||||
Free bytes: 536870872
|
||||
Used bytes: 0
|
||||
Peak bytes: 0
|
||||
Heap base addr: 2214588416
|
||||
Heap end addr: 2751459328
|
||||
|
||||
Heartbeat: init...
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x5ff3672509f6c4e5 capsule_hash=0x5ff3672509f6c4e5 words=311
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xc07eb35ca84c4aa8
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_SYSTEM word_count=317 dict_hash=0xc0769523a47b7dc3
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
|
||||
PCI: init...
|
||||
virtio-blk: found device
|
||||
[32m[HADES][INFO ] [0m[KRELTSC: 25077003] 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> HERE .
|
||||
8300 ok
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
+7
-4
@@ -133,13 +133,16 @@ 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). No message is lost without
|
||||
its sender being told, and no node waits for ever. A node with no room for
|
||||
a message, or no way to pass it on, sends its sender a NACK; `(REFUSED)`
|
||||
**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
|
||||
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:
|
||||
"Message refused", "Node gone", or "Interrupted" when a line is typed at
|
||||
the console. `n KILL` on Hera removes node n and tells every other node it
|
||||
is gone.
|
||||
is gone; a node believes that only from its centre. A stuck node can hold
|
||||
up its neighbours until it is killed: `MESH.md` 7b.7 says how.
|
||||
|
||||
**Blocks** (`MESH.md` step 6). The block words are the kernel's, as they
|
||||
are for a v3 VM: `BLOCK`, `BUFFER`, `UPDATE`, `SAVE-BUFFERS` and
|
||||
|
||||
+64
-16
@@ -197,14 +197,34 @@ header CMOVE
|
||||
\ the messages waiting are kept for those two types, so that ordinary
|
||||
\ messages cannot take all the room.
|
||||
|
||||
\ ( to about -- ) a refusal is owed to the node `to`, about the node `about`
|
||||
\ ( to about -- ) a refusal is owed to the node `to`, about the node `about`.
|
||||
\ Owed to this node itself -- its own message came back to it and was let
|
||||
\ go here -- it is put with the messages waiting, as if it had come by a
|
||||
\ port, in the room kept for refusals.
|
||||
: (OWE)
|
||||
over (ME) a! @ xor if SELF drop
|
||||
(OWED#) a! @ -8 + -if NOROOM
|
||||
drop (OWED#) a! @ 2* (OWED) + a!
|
||||
push !+ pop !
|
||||
(OWED#) a! @ 1 + ! ;
|
||||
SELF: drop 1 (MQ#) a! @ + -MQ-ROOM + -if NOROOM
|
||||
drop push (MQ!) (ME) a! @ (MQ!) 4 (MQ!) 0 (MQ!) 0 (MQ!) 0 (MQ!) 4 (MQ!) 0 (MQ!) pop (MQ!) ;
|
||||
NOROOM: drop drop drop (LOST) a! @ 1 + ! ;
|
||||
|
||||
\ ( from to type -- ) a message of that type, from the one node for the
|
||||
\ other, has been let go here. Whoever is waiting on it is told: its
|
||||
\ sender; or, for a node's answer (type 3), the node the answer was for,
|
||||
\ which is the one that waits. Nothing is owed for a NACK or a GONE.
|
||||
: (TELL-OF)
|
||||
dup -4 + if NOT -1 + if NOT drop
|
||||
-3 + if ANSWER drop jump (OWE) \ to its sender, about whom it was for
|
||||
ANSWER: drop SWAP jump (OWE) \ to whom it was for, about the node that answered
|
||||
NOT: drop drop drop drop ;
|
||||
|
||||
\ ( port -- flag ) is there anything on that port, by its address?
|
||||
: (THERE)
|
||||
(PORT) - -PORTS - (READERS) b! @b SWAP jump (BIT)
|
||||
|
||||
\ ( port to -- ) the seven words of a message being taken in, and the
|
||||
\ port, go into (MQ-HDR). B is at the port, by its address, and `to` is
|
||||
\ the message's first word, already read from it.
|
||||
@@ -223,7 +243,7 @@ header CMOVE
|
||||
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! @ + \ what it would come to
|
||||
(MQ-HDR)+2 a! @ -4 + -if KEPT-FOR \ a NACK or a GONE may use the room kept for them
|
||||
(MQ-HDR)+2 a! @ -4 + if KEPT-FOR -1 + if KEPT-FOR \ a NACK or a GONE may use the room kept for them
|
||||
drop -MQ-ORDINARY + jump ROOM
|
||||
KEPT-FOR: drop -MQ-ROOM +
|
||||
ROOM: -if FULL
|
||||
@@ -235,9 +255,7 @@ header CMOVE
|
||||
FULL: drop if READ -1 + FOR @b drop UNEXT jump LOSE
|
||||
READ: drop
|
||||
LOSE: (LOST) a! @ 1 + !
|
||||
(MQ-HDR)+2 a! @ -4 + -if NOT-OWED \ nothing is owed for a NACK or a GONE
|
||||
drop (MQ-HDR)+1 a! @ (MQ-HDR) a! @ jump (OWE) \ to whoever sent it, about whom it was for
|
||||
NOT-OWED: drop ;
|
||||
(MQ-HDR)+1 a! @ (MQ-HDR) a! @ (MQ-HDR)+2 a! @ jump (TELL-OF)
|
||||
|
||||
\ ( port to -- ) take in a message and keep it
|
||||
: (TAKE) (TAKE-HDR) jump (TAKE-KEEP)
|
||||
@@ -257,7 +275,10 @@ header CMOVE
|
||||
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
|
||||
NOTYET: drop
|
||||
(READERS) b! @b (GATE-PORT) a! @ (PORT) - -PORTS - (BIT) if NOONE \ is there anything on the port at all?
|
||||
drop jump L
|
||||
NOONE: drop NODE-ERROR b! 18 !b ; \ no: what waits here would wait for ever
|
||||
|
||||
\ ( type -- ) words 1 to 5 of a message this node sends: from, type, and
|
||||
\ the three that are carried and not used yet. Its caller has put B at the
|
||||
@@ -266,18 +287,45 @@ header CMOVE
|
||||
: (HDR)
|
||||
(ME) a! @ !b \ from
|
||||
!b \ type
|
||||
0 !b 0 !b 0 !b ; \ heat and TTL, ACL tag, sequence
|
||||
16 !b 0 !b 0 !b ; \ how many nodes may pass it on (the TTL: MESH.md 7b); ACL tag; sequence
|
||||
|
||||
\ ( -- ) send one refusal this node owes, if it owes any: the one noted
|
||||
\ last. With no way to whom it is owed, it is let go and counted.
|
||||
\ PAYING WHAT IS OWED does not hold this node up. A refusal is sent if the
|
||||
\ neighbour it goes by is reading at this moment. If it is not, and its
|
||||
\ number is the higher, the refusal is written all the same, as any message
|
||||
\ would be (MESH.md 7a): that neighbour takes it in when it next looks. If
|
||||
\ its number is the lower, or it has not been told of, the refusal stays
|
||||
\ owed, and this node goes on with what it has to do and tries again; it
|
||||
\ does not sleep while it owes one (quit.v4, (IDLE)). One there is no way
|
||||
\ for, or whose way is a port with nothing on it, is let go and counted.
|
||||
|
||||
\ ( k -- ) the k-th refusal owed is done with: the last takes its place
|
||||
: (PAID)
|
||||
(OWED#) a! @ -1 + dup !
|
||||
2* (OWED) + a! @+ @
|
||||
push push 2* (OWED) + a! pop !+ pop ! ;
|
||||
|
||||
\ ( k -- ) send the k-th refusal owed, if it can go now
|
||||
: (PAY1)
|
||||
dup 2* (OWED) + a! @+ @ \ k to about
|
||||
over (PORT-FOR) if NOWAY \ k to about port
|
||||
dup (THERE) if GONE
|
||||
drop (READERS) b! @b over (PORT) - (BIT) if BUSY
|
||||
drop
|
||||
WRITE: b! \ k to about B: the port
|
||||
SWAP !b 4 (HDR) 4 !b !b \ k to; from, type 4; four characters; about
|
||||
jump (PAID)
|
||||
BUSY: drop \ it is not reading:
|
||||
dup (PORT) - (NEAR) + a! @ if LEAVE \ k to about port near
|
||||
(ME) a! @ - -if BLIND jump LEAVE \ its number less this node's
|
||||
BLIND: drop jump WRITE \ the higher: it will take it in
|
||||
LEAVE: drop drop drop drop drop ;
|
||||
GONE: drop
|
||||
NOWAY: drop drop drop (LOST) a! @ 1 + ! jump (PAID)
|
||||
|
||||
\ ( -- ) send every refusal this node owes that can go now
|
||||
: (PAY)
|
||||
(OWED#) a! @ if NONE
|
||||
-1 + dup !
|
||||
2* (OWED) + a! @+ @ \ to about
|
||||
over (PORT-FOR) if NOWAY
|
||||
(GATE) over !b \ to
|
||||
4 (HDR) 4 !b !b drop ; \ from, type 4; four characters; about
|
||||
NOWAY: drop drop drop (LOST) a! @ 1 + ! ;
|
||||
(OWED#) a! @ if NONE -1 +
|
||||
FOR pop dup push (PAY1) NEXT ;
|
||||
NONE: drop ;
|
||||
|
||||
\ ( -- ) send what has been printed, to where (PRINT-TO) says, by the port
|
||||
|
||||
+108
-16
@@ -55,6 +55,7 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
\ something that has emptied the return stack (or by a fault, which empties
|
||||
\ it). (LINE-STATUS) keeps how the last text ended.
|
||||
: (FINISH) ( s -- )
|
||||
1 (QUIET) a! ! \ from here an error is not this text's: see (RAISED)
|
||||
(LINE-STATUS) b! dup !b
|
||||
(FLUSH-OUT)
|
||||
(DONE-PORT) a! @ (GATE) (MSG)+1 a! @ !b \ to whoever sent the text
|
||||
@@ -78,9 +79,14 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
\ line was, and (FINISH) reports how it ended. A message for another node
|
||||
\ is passed on, (PASS-ON).
|
||||
: (IDLE)
|
||||
L: (PAY) \ a refusal owed is sent first (core.v4)
|
||||
L: 1 (QUIET) a! ! \ nothing here is any text's doing: see (RAISED)
|
||||
(PAY) \ what refusals can be sent now, are (core.v4)
|
||||
(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
|
||||
WAIT: drop (OWED#) a! @ if BLOCK \ with one still owed it does not sleep:
|
||||
drop (WRITERS) b! @b if L0 \ it takes in whatever is being written to it,
|
||||
(LOW) (PORT) + dup b! @b (TAKE) jump L
|
||||
L0: drop jump L \ and tries again
|
||||
BLOCK: drop (PORT)+8 b! @b (PORT)+9 b! @b (PORT) + dup b! \ ( to port ) B: the port it came on
|
||||
SWAP (TAKE) jump L
|
||||
HAVE:
|
||||
(MQ@) (MSG) a! ! (MQ@) (MSG)+1 a! ! (MQ@) (MSG)+2 a! ! (MQ@) (MSG)+3 a! !
|
||||
@@ -100,8 +106,9 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
(MSG)+2 a! @ -1 + if TEXT \ for this node, and
|
||||
-3 + if NACK -1 + if GONE drop jump (IDLE) \ neither text nor these: let go
|
||||
NACK: drop (REFUSED) a! @ 1 + ! jump (IDLE) \ a message of this node's was refused: counted
|
||||
GONE: drop (REPLY) a! @ (ROUTE-DEFAULT) a! @ xor if CENTRE \ a node is gone, if its centre says so:
|
||||
drop jump (IDLE)
|
||||
GONE: drop (MSG)+6 a! @ -4 + if G4 drop jump (IDLE) \ a node is gone, if its centre says so:
|
||||
G4: drop (CENTRE) if NOT (MSG)+1 a! @ xor if CENTRE drop jump (IDLE)
|
||||
NOT: drop jump (IDLE)
|
||||
CENTRE: drop TIB 2/ 2/ a! @ NO-ROUTE jump (IDLE) \ the way to it is forgotten
|
||||
TEXT: drop
|
||||
(MSG)+1 a! @ (PORT-FOR) if D0 jump D1 \ the way back to whoever sent it:
|
||||
@@ -119,22 +126,27 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
\ of it, as it came, to the port that leads toward that node. With no way
|
||||
\ known it is let go, and (LOST) counts it.
|
||||
: (PASS-ON)
|
||||
(MSG)+3 a! @ if FRESH jump COUNT \ how many more nodes may pass it on;
|
||||
FRESH: drop 16 \ from a device, which set none: sixteen
|
||||
COUNT: -1 + if SPENT (MSG)+3 a! !
|
||||
(MSG) a! @ (PORT-FOR) if NOWAY
|
||||
dup (THERE) if NOONE drop
|
||||
(GATE) (MSG) a! 6 FOR @+ !b UNEXT \ its seven words
|
||||
(MSG)+6 a! @ 3 + 2/ 2/ if SENT
|
||||
-1 + TIB 2/ 2/ a! FOR @+ !b UNEXT \ and its text
|
||||
jump (IDLE)
|
||||
SENT: drop jump (IDLE)
|
||||
NOONE: drop \ its way is a port with nothing on it
|
||||
SPENT: \ it has been passed on as often as it may be
|
||||
NOWAY: drop (LOST) a! @ 1 + !
|
||||
(MSG)+2 a! @ -4 + -if NOT-OWED \ its sender is told, unless it is a NACK or a GONE
|
||||
drop (MSG)+1 a! @ (MSG) a! @ (OWE) jump (IDLE)
|
||||
NOT-OWED: drop jump (IDLE)
|
||||
(MSG)+1 a! @ (MSG) a! @ (MSG)+2 a! @ (TELL-OF) jump (IDLE) \ whoever waits on it is told (core.v4)
|
||||
|
||||
\ ( -- ) THE TEXT, INTERPRETED. It is in TIB with a zero after it and its
|
||||
\ length in SPAN, and the return stack is empty. It is interpreted with one
|
||||
\ return entry under it, this word's call of INTERPRET; whatever is on the
|
||||
\ data stack stays there.
|
||||
: (LINE)
|
||||
0 (QUIET) a! ! \ an error from here on is this text's
|
||||
0 BLK a! ! \ the terminal, whatever was being loaded
|
||||
TIB (SRC) a! ! 0 >IN a! !
|
||||
0 NODE-ERROR b! !b
|
||||
@@ -232,6 +244,70 @@ header SEND-ON
|
||||
POS: -PORTS + -if BAD drop (PORT) + jump (SEND-ON)
|
||||
BAD: drop drop drop drop drop NODE-ERROR b! 12 !b ;
|
||||
|
||||
\ ( -- n ) the number of this node's centre: the node on the port that
|
||||
\ leads to everything it has no other way to. 0: it has none, or has not
|
||||
\ been told who is there (NEIGHBOUR).
|
||||
: (CENTRE)
|
||||
(ROUTE-DEFAULT) a! @ if NONE (PORT) - (NEAR) + a! @ ;
|
||||
NONE: ;
|
||||
|
||||
\ ( -- n ) the number of this node's console: the one it was told of, or
|
||||
\ with none told of, whoever sent the text it is doing
|
||||
: (A-CONSOLE)
|
||||
(CONSOLE) a! @ if C0 ;
|
||||
C0: drop (MSG)+1 a! @ ;
|
||||
|
||||
\ THE MESSAGES WAITING, LOOKED THROUGH for what would end a wait. Each in
|
||||
\ turn is taken from the front: its eight cells into (MQ-HDR), (A-HDR). If
|
||||
\ it is the answer waited for, or a NACK or a believed GONE for this node,
|
||||
\ it is used up, as it would be had it come during the wait; otherwise it
|
||||
\ goes to the back, (A-BACK) and (A-TEXT), so that when all have been
|
||||
\ looked at they are in the order they were in. (A-LEFT) is how many
|
||||
\ cells are still to be looked at. (A-FOUND) is what was found first: 0
|
||||
\ nothing, 1 the answer, its word in (A-WORD); 2 a NACK about the node
|
||||
\ waited for; 3 a GONE about it; 4 text from the console, which stays
|
||||
\ where it is to be done next.
|
||||
: (A-HDR) (MQ-HDR) a! 7 FOR a push (MQ@) pop a! !+ NEXT ;
|
||||
: (A-BACK) (MQ-HDR) a! 7 FOR @+ a push (MQ!) pop a! NEXT ;
|
||||
: (A-TEXT) if NONE -1 + FOR (MQ@) (MQ!) NEXT ; NONE: drop ;
|
||||
: (A-QUEUE)
|
||||
0 (A-FOUND) a! !
|
||||
(MQ#) a! @ (A-LEFT) a! !
|
||||
L: (A-LEFT) a! @ if END drop
|
||||
(A-HDR)
|
||||
(MQ-HDR)+6 a! @ -if SIZED drop 0 jump N
|
||||
SIZED: 3 + 2/ 2/
|
||||
N: (A-LEFT) a! @ over 8 + - ! \ n: its words of text
|
||||
(A-FOUND) a! @ if LOOK drop jump KEEP \ something is found: the rest only go round
|
||||
LOOK: drop
|
||||
(MQ-HDR) a! @ (ME) a! @ xor if MINE drop jump KEEP
|
||||
MINE: drop
|
||||
(MQ-HDR)+2 a! @ -1 + if TEXT -2 + if ANS -1 + if NACK -1 + if GONE drop jump KEEP
|
||||
TEXT: drop (A-CONSOLE) (MQ-HDR)+1 a! @ xor if BRK drop jump KEEP
|
||||
BRK: drop 4 (A-FOUND) a! ! jump KEEP
|
||||
ANS: drop (MQ-HDR)+1 a! @ (AWAIT-FROM) a! @ xor if A1 drop jump KEEP
|
||||
A1: drop dup -1 + if A2 drop jump KEEP
|
||||
A2: drop drop (MQ@) (A-WORD) a! ! 1 (A-FOUND) a! ! jump L
|
||||
NACK: drop dup -1 + if N2 drop jump KEEP
|
||||
N2: drop drop (MQ@) (AWAIT-FROM) a! @ xor if N3 drop (REFUSED) a! @ 1 + ! jump L
|
||||
N3: drop 2 (A-FOUND) a! ! jump L
|
||||
GONE: drop dup -1 + if G2 drop jump KEEP
|
||||
G2: drop (CENTRE) if NOC (MQ-HDR)+1 a! @ xor if G3 drop jump KEEP
|
||||
NOC: drop jump KEEP
|
||||
G3: drop drop (MQ@) dup NO-ROUTE (AWAIT-FROM) a! @ xor if G4 drop jump L
|
||||
G4: drop 3 (A-FOUND) a! ! jump L
|
||||
KEEP: (A-BACK) (A-TEXT) jump L
|
||||
END: drop ;
|
||||
|
||||
\ ( what -- how | ) the wait is over, by what (A-FOUND) says: the answer's
|
||||
\ word is left; anything else is an error
|
||||
: (A-END)
|
||||
0 (AWAIT-FROM) a! !
|
||||
-1 + if ANS -1 + if REF -1 + if LEFT drop NODE-ERROR b! 21 !b ;
|
||||
ANS: drop (A-WORD) a! @ ;
|
||||
REF: drop NODE-ERROR b! 19 !b ;
|
||||
LEFT: drop NODE-ERROR b! 20 !b ;
|
||||
|
||||
\ ( node -- how ) WAIT FOR THE NODE'S WORD OF HOW TEXT ENDED: 0 QUIT,
|
||||
\ 1 completed, 2 an error. This node is blocked reading its ports until a
|
||||
\ message of type 3 comes for it from that node; every other message that
|
||||
@@ -241,7 +317,11 @@ header SEND-ON
|
||||
\ (MESH.md section 7b): a NACK about that node, error 19, Message refused;
|
||||
\ or a GONE about it from this node's centre, error 20, Node gone. A NACK
|
||||
\ about another node is counted, and a GONE about another has its way
|
||||
\ forgotten, as they would be if this node were not waiting.
|
||||
\ forgotten, as they would be if this node were not waiting. A GONE is
|
||||
\ believed only if this node's centre sent it: (CENTRE).
|
||||
\ WHAT HAS COME ALREADY is looked through first, (A-QUEUE): the answer, a
|
||||
\ NACK or a GONE may have been taken in before the wait began, while this
|
||||
\ node was waiting to write, and is then with the messages waiting.
|
||||
\ AND A LINE FROM THE CONSOLE BREAKS IT (7b.6): text for this node from its
|
||||
\ console is kept, as any message is, the wait ends in error 21,
|
||||
\ Interrupted, and the text is then done as usual. It is the one way to
|
||||
@@ -250,28 +330,30 @@ header SEND-ON
|
||||
header AWAIT
|
||||
: AWAIT
|
||||
(AWAIT-FROM) a! !
|
||||
(A-QUEUE)
|
||||
(A-FOUND) a! @ if WAIT jump (A-END)
|
||||
WAIT: drop
|
||||
L: (PORT)+8 b! @b (PORT)+9 b! @b (PORT) + dup b! SWAP (TAKE-HDR)
|
||||
(MQ-HDR) a! @ (ME) a! @ xor if A1 drop jump KEEP
|
||||
A1: drop (MQ-HDR)+2 a! @ \ for this node: its type
|
||||
-1 + if TEXT -2 + if ANSWER -1 + if NACK -1 + if GONE drop jump KEEP
|
||||
TEXT: drop (CONSOLE) a! @ if C0 jump C1 \ text: from the console?
|
||||
C0: drop (MSG)+1 a! @ \ with none told of, whoever sent the text being done
|
||||
C1: (MQ-HDR)+1 a! @ xor if BREAK drop jump KEEP
|
||||
BREAK: drop (TAKE-KEEP) 0 (AWAIT-FROM) a! ! NODE-ERROR b! 21 !b ; \ kept, to be done next; and the wait is over
|
||||
TEXT: drop (A-CONSOLE) (MQ-HDR)+1 a! @ xor if BREAK drop jump KEEP \ text: from the console?
|
||||
BREAK: drop (TAKE-KEEP) 4 jump (A-END) \ kept, to be done next; and the wait is over
|
||||
ANSWER: drop (MQ-HDR)+1 a! @ (AWAIT-FROM) a! @ xor if A3 drop jump KEEP
|
||||
A3: drop (MQ-HDR)+6 a! @ -4 + if A4 drop jump KEEP
|
||||
A4: drop 0 (AWAIT-FROM) a! ! @b ; \ its one word
|
||||
NACK: drop (MQ-HDR)+6 a! @ -4 + if N4 drop jump KEEP
|
||||
N4: drop @b (AWAIT-FROM) a! @ xor if REFUSED \ about the node waited for?
|
||||
drop (REFUSED) a! @ 1 + ! jump L \ another: counted
|
||||
REFUSED: drop 0 (AWAIT-FROM) a! ! NODE-ERROR b! 19 !b ;
|
||||
REFUSED: drop 2 jump (A-END)
|
||||
GONE: drop (MQ-HDR)+6 a! @ -4 + if G4 drop jump KEEP
|
||||
G4: drop @b \ the node that is gone
|
||||
(MQ-HDR)+7 a! @ (ROUTE-DEFAULT) a! @ xor if CENTRE \ only its centre is believed
|
||||
(CENTRE) if NOC (MQ-HDR)+1 a! @ xor if CENTRE \ only its centre is believed
|
||||
drop drop jump L
|
||||
NOC: drop drop jump L
|
||||
CENTRE: drop dup NO-ROUTE
|
||||
(AWAIT-FROM) a! @ xor if LEFT drop jump L
|
||||
LEFT: drop 0 (AWAIT-FROM) a! ! NODE-ERROR b! 20 !b ;
|
||||
LEFT: drop 3 jump (A-END)
|
||||
KEEP: (TAKE-KEEP) jump L
|
||||
|
||||
\ ( w port -- ) write a cell to the port, by its number. It waits until
|
||||
@@ -343,7 +425,16 @@ header ABORT
|
||||
\ in core.v4). Print its message -- a negative code means the word printed
|
||||
\ its own -- and end the line with ERROR. The return stack has been
|
||||
\ emptied; the data stack is as the word left it.
|
||||
\ (QUIET) is 1 while the node is doing something that is no text's doing:
|
||||
\ paying a refusal, passing a message on, or sending word of how a text
|
||||
\ ended. An error then has nobody to be told to -- the usual one is that
|
||||
\ no one is on the port (18) -- and trying to tell it would raise it again
|
||||
\ without end. So it is not told: what was being sent is let go and
|
||||
\ counted, and the node goes back to waiting.
|
||||
: (RAISED)
|
||||
(QUIET) a! @ if SPEAK
|
||||
drop (OUT) (OUT^) a! ! (LOST) a! @ 1 + ! jump (IDLE)
|
||||
SPEAK: drop
|
||||
NODE-ERROR b! @b
|
||||
-if POS drop 2 jump (DONE)
|
||||
POS: -1 + if M1 -1 + if M2 -1 + if M3 -1 + if M4
|
||||
@@ -369,7 +460,8 @@ header ABORT
|
||||
M15: drop $65666544 (EMIT4) $64657272 (EMIT4) $726F7720 (EMIT4) $6F6E2064 (EMIT4) $65732074 (EMIT4) $74 (EMIT4) CR 2 jump (DONE)
|
||||
M16: drop $20746F4E (EMIT4) $6F772061 (EMIT4) $6472 (EMIT4) CR 2 jump (DONE)
|
||||
M17: drop $726F7453 (EMIT4) $20656761 (EMIT4) $75666572 (EMIT4) $646573 (EMIT4) CR 2 jump (DONE)
|
||||
M18: drop $6F206F4E (EMIT4) $6F20656E (EMIT4) $6874206E (EMIT4) $70207461 (EMIT4) $74726F (EMIT4) CR 2 jump (DONE)
|
||||
M18: drop (OUT) (OUT^) a! ! \ what was printed and not sent cannot be sent: it is let go
|
||||
$6F206F4E (EMIT4) $6F20656E (EMIT4) $6874206E (EMIT4) $70207461 (EMIT4) $74726F (EMIT4) CR 2 jump (DONE)
|
||||
M19: drop $7373654D (EMIT4) $20656761 (EMIT4) $75666572 (EMIT4) $646573 (EMIT4) CR 2 jump (DONE)
|
||||
M20: drop $65646F4E (EMIT4) $6E6F6720 (EMIT4) $65 (EMIT4) CR 2 jump (DONE)
|
||||
M21: drop $65746E49 (EMIT4) $70757272 (EMIT4) $646574 (EMIT4) CR 2 jump (DONE)
|
||||
|
||||
@@ -26,6 +26,7 @@ typedef struct {
|
||||
int (*take)(void *self, v4_cell value); /* the node wrote `value`: 1 if it is taken, 0 if not yet */
|
||||
int (*give)(void *self, v4_cell *value); /* the node is reading: 1 with a word in *value, 0 if not yet */
|
||||
void *self;
|
||||
int (*pending)(void *self); /* 1 if it has a word to give that has not been asked for; may be 0: it never says */
|
||||
} v4_device;
|
||||
|
||||
/* One end of a wire. */
|
||||
@@ -55,6 +56,7 @@ typedef struct {
|
||||
v4_place *place;
|
||||
unsigned capacity;
|
||||
v4_cell gone_error; /* see v4_fabric_gone_error; 0: none */
|
||||
v4_cell interrupt_error; /* see v4_fabric_interrupt_error; 0: none */
|
||||
} v4_fabric;
|
||||
|
||||
/* An empty fabric with room for `capacity` nodes, in `places`. */
|
||||
@@ -71,6 +73,19 @@ void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity);
|
||||
* is how a fabric begins, nothing is raised. */
|
||||
void v4_fabric_gone_error(v4_fabric *f, v4_cell code);
|
||||
|
||||
/* A NODE BLOCKED WRITING TO A NODE THAT DOES NOT READ. Of two neighbours
|
||||
* one may write to the other without looking (docs/v4.0.0/MESH.md 7a), and
|
||||
* is then blocked until the other reads; if the other never does, it is
|
||||
* blocked for ever, and if it is the node the console is on, nothing can be
|
||||
* typed to it again. So: when a node that can take an error is blocked
|
||||
* writing to another node, on the first word of a message (node.h,
|
||||
* v4_node_words_since_look: nothing of the message has gone), and a device
|
||||
* on one of its own ports says it has something pending for it, the error
|
||||
* `code` is raised on the node: it gives up that message and goes on, to
|
||||
* read what the device has. Only a device with a `pending` function can
|
||||
* do this. With `code` 0, which is how a fabric begins, it never happens. */
|
||||
void v4_fabric_interrupt_error(v4_fabric *f, v4_cell code);
|
||||
|
||||
/* More room: `places`, of `capacity` no less than before, takes over; every
|
||||
* node keeps its number and its wiring. */
|
||||
void v4_fabric_grow(v4_fabric *f, v4_place *places, unsigned capacity);
|
||||
|
||||
@@ -65,6 +65,11 @@ typedef struct {
|
||||
* port". */
|
||||
#define V4_ERROR_NO_ONE 18
|
||||
|
||||
/* The nucleus's error for a node let go of a message it was blocked on the
|
||||
* first word of, because its console has a line for it (fabric.h,
|
||||
* v4_fabric_interrupt_error): "Interrupted". */
|
||||
#define V4_ERROR_INTERRUPTED 21
|
||||
|
||||
/* The description of the nucleus built from v4/capsule (the generated file
|
||||
* defines it). */
|
||||
extern const v4_image v4_capsule_image;
|
||||
|
||||
+13
-1
@@ -111,6 +111,7 @@ typedef struct {
|
||||
unsigned last_from; /* the port the last read from "any" took its word from */
|
||||
unsigned writers; /* bit k: the neighbour on port k is blocked writing to this node */
|
||||
unsigned readers; /* bit k: the neighbour on port k is blocked reading from this node */
|
||||
unsigned since_look; /* words taken from this node since it last fetched either of those */
|
||||
|
||||
/* DSTACK-DEPTH and RSTACK-DEPTH (D-16). See v4_node_stack_regs_attach. */
|
||||
v4_cell dstack_reg; /* its word address, or -1 */
|
||||
@@ -181,7 +182,11 @@ void v4_node_console_attach(v4_node *n, v4_cell addr);
|
||||
* 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
|
||||
* base + V4_PORTS + 3 which ports have a neighbour waiting to read from it,
|
||||
* bit k for port k; and, V4_PORTS bits higher, which
|
||||
* ports have anything on them at all: bit V4_PORTS + k
|
||||
* for port k. A port with nothing on it can never
|
||||
* be read from or written to.
|
||||
* The last two are how a node looks before it writes (MESH.md 7a), as the
|
||||
* 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
|
||||
@@ -214,6 +219,13 @@ void v4_node_port_attach(v4_node *n, v4_cell base);
|
||||
/* The neighbour has taken what the node wrote. */
|
||||
void v4_node_port_served(v4_node *n);
|
||||
|
||||
/* How many words the node has had taken since it last looked at its
|
||||
* neighbours (a fetch of either of the two status addresses). A node that
|
||||
* looks before it begins a message (MESH.md 7a) and is blocked writing with
|
||||
* this at 0 is blocked on the first word of the message: nothing of it has
|
||||
* gone. */
|
||||
unsigned v4_node_words_since_look(const v4_node *n);
|
||||
|
||||
/* THERE IS NO ONE ON THE PORT the node is blocked at -- writing to it, or
|
||||
* reading from that one port -- and there will not be. A node that can
|
||||
* take an error (it has an error register and a fault table: node.h,
|
||||
|
||||
@@ -18,6 +18,7 @@ void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity)
|
||||
f->place = places;
|
||||
f->capacity = capacity;
|
||||
f->gone_error = 0;
|
||||
f->interrupt_error = 0;
|
||||
for (i = 0; i < capacity; i++) clear_place(&places[i]);
|
||||
}
|
||||
|
||||
@@ -26,6 +27,11 @@ void v4_fabric_gone_error(v4_fabric *f, v4_cell code)
|
||||
f->gone_error = code;
|
||||
}
|
||||
|
||||
void v4_fabric_interrupt_error(v4_fabric *f, v4_cell code)
|
||||
{
|
||||
f->interrupt_error = code;
|
||||
}
|
||||
|
||||
void v4_fabric_grow(v4_fabric *f, v4_place *places, unsigned capacity)
|
||||
{
|
||||
unsigned i;
|
||||
@@ -171,6 +177,7 @@ static void look_about(v4_fabric *f, unsigned a)
|
||||
|
||||
for (k = 0; k < V4_PORTS; k++) {
|
||||
const v4_wire *w = &f->place[a].wire[k];
|
||||
if (w->kind != V4_WIRE_NONE) readers |= 1u << (V4_PORTS + k); /* there is something on this port */
|
||||
if (w->kind == V4_WIRE_DEVICE) {
|
||||
if (w->device->take) readers |= 1u << k;
|
||||
} else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) {
|
||||
@@ -202,6 +209,24 @@ 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 on the first word of a message to a node that is not
|
||||
* reading, while a device of its own has something for it */
|
||||
if (f->interrupt_error != 0)
|
||||
for (i = 0; i < f->capacity; i++) {
|
||||
v4_node *n;
|
||||
unsigned k;
|
||||
if (!awake(f, i)) continue;
|
||||
n = &f->place[i].node->n;
|
||||
if (!n->asking || f->place[i].wire[n->ask_port].kind != V4_WIRE_NODE || v4_node_words_since_look(n) != 0) continue;
|
||||
for (k = 0; k < V4_PORTS; k++) {
|
||||
const v4_wire *w = &f->place[i].wire[k];
|
||||
if (w->kind == V4_WIRE_DEVICE && w->device->pending && w->device->pending(w->device->self)) {
|
||||
done += (unsigned)v4_node_port_gone(n, f->interrupt_error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* a node blocked at one port that nothing is wired to */
|
||||
if (f->gone_error != 0)
|
||||
for (i = 0; i < f->capacity; i++) {
|
||||
|
||||
+9
-2
@@ -120,8 +120,8 @@ v4_cell v4_node_fetch(v4_node *n, v4_cell addr)
|
||||
{
|
||||
int port = v4_node_port_index(n, addr);
|
||||
if (port == (int)V4_PORTS + 1) return (v4_cell)n->last_from;
|
||||
if (port == (int)V4_PORTS + 2) return (v4_cell)n->writers;
|
||||
if (port == (int)V4_PORTS + 3) return (v4_cell)n->readers;
|
||||
if (port == (int)V4_PORTS + 2) { n->since_look = 0; return (v4_cell)n->writers; }
|
||||
if (port == (int)V4_PORTS + 3) { n->since_look = 0; return (v4_cell)n->readers; }
|
||||
if (port >= 0) {
|
||||
/* the word its neighbour wrote; v4_node_read_ready has said it is there */
|
||||
if (!n->given) return 0;
|
||||
@@ -179,6 +179,7 @@ void v4_node_port_attach(v4_node *n, v4_cell base)
|
||||
n->last_from = 0;
|
||||
n->writers = 0;
|
||||
n->readers = 0;
|
||||
n->since_look = 0;
|
||||
}
|
||||
|
||||
void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
|
||||
@@ -221,6 +222,12 @@ void v4_node_born(v4_node *n, v4_cell base)
|
||||
void v4_node_port_served(v4_node *n)
|
||||
{
|
||||
n->asking = 0;
|
||||
n->since_look++;
|
||||
}
|
||||
|
||||
unsigned v4_node_words_since_look(const v4_node *n)
|
||||
{
|
||||
return n->since_look;
|
||||
}
|
||||
|
||||
int v4_node_port_gone(v4_node *n, v4_cell code)
|
||||
|
||||
+1
-1
@@ -354,7 +354,7 @@ int v4_boot_run(const v4_boot *b)
|
||||
/* 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);
|
||||
v4_node_port_status(b->n, 0, (1u << KERNEL_PORT | 1u << CONSOLE_PORT) * (1u + (1u << V4_PORTS))); /* each of them there, and taking */
|
||||
v4_blocks_init(&boot_blocks, b->im->block_window);
|
||||
if (!load_nucleus(b)) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; }
|
||||
|
||||
|
||||
@@ -64,6 +64,10 @@
|
||||
#define BLK (BVARS + 7)
|
||||
#define SRC (BVARS + 8)
|
||||
#define SRC_HOOK (BVARS + 9)
|
||||
#define QUIET (BUF0_W - 10) /* 1 while the node does something that is no text's doing: an error then is not told (quit.v4, (RAISED)) */
|
||||
#define A_WORD (BUF0_W - 113) /* AWAIT's look through the messages waiting: the answer's word, */
|
||||
#define A_LEFT (BUF0_W - 114) /* how many cells are still to be looked at, */
|
||||
#define A_FOUND (BUF0_W - 115) /* and what was found */
|
||||
#define REFUSED (BUF0_W - 7) /* how many of this node's messages it has been told were refused */
|
||||
#define OWED_COUNT (BUF0_W - 8) /* how many refusals this node owes and has not sent yet, at most 8 */
|
||||
#define OWED (BUF0_W - 112) /* those: for each, the node it is owed to and the node the refused message was for */
|
||||
@@ -196,6 +200,10 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
v4_text_constant(tx, "(MQ)", MQ_W);
|
||||
v4_text_constant(tx, "(MQ-END)", MQ_W + MQ_CELLS);
|
||||
v4_text_constant(tx, "-MQ-ORDINARY", -(v4_cell)(MQ_CELLS - 7 - 36)); /* the same for a message that is not a refusal or a GONE: 36 cells are kept for those */
|
||||
v4_text_constant(tx, "(QUIET)", QUIET);
|
||||
v4_text_constant(tx, "(A-WORD)", A_WORD);
|
||||
v4_text_constant(tx, "(A-LEFT)", A_LEFT);
|
||||
v4_text_constant(tx, "(A-FOUND)", A_FOUND);
|
||||
v4_text_constant(tx, "(REFUSED)", REFUSED);
|
||||
v4_text_constant(tx, "(OWED#)", OWED_COUNT);
|
||||
v4_text_constant(tx, "(OWED)", OWED);
|
||||
|
||||
+62
-1
@@ -45,6 +45,10 @@ static unsigned loaded(unsigned k)
|
||||
/* the programme ends by waiting at a port nothing is wired to */
|
||||
static void wait_for_ever(void) { LIT(PB + 7); O(BANG_B); O(FETCH_B); (void)v4_asm_label(&as); } /* and the last word is written out */
|
||||
|
||||
/* a device that says whether it has something pending */
|
||||
static int *dev_has;
|
||||
static int device_has(void *self) { (void)self; return *dev_has; }
|
||||
|
||||
/* Let time pass until nothing more can happen, or `max` steps. */
|
||||
static unsigned settle(unsigned max)
|
||||
{
|
||||
@@ -358,10 +362,67 @@ int main(void)
|
||||
}
|
||||
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(b)->n.mem[OUT + 1] & ((1 << V4_PORTS) - 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] & ((1 << V4_PORTS) - 1)));
|
||||
CHECK((ND(b)->n.mem[OUT + 1] >> V4_PORTS) == ((1 << 2) | (1 << 4) | (1 << 6)), "and, in the bits above those, which of its ports have anything on them at all: %ld", (long)(ND(b)->n.mem[OUT + 1] >> V4_PORTS));
|
||||
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");
|
||||
|
||||
/* ---- a node blocked on the first word of a message, while a device of its own has something for it ---- */
|
||||
{
|
||||
static int has; /* what the device says it has pending */
|
||||
static stream none;
|
||||
static v4_device dev;
|
||||
v4_fabric_init(&f, places, 4);
|
||||
for (i = 0; i < 3; i++) v4_node_reset(&pool[i].n);
|
||||
a = loaded(0); /* looks about, then writes two words to its port 0 */
|
||||
LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); O(DROP);
|
||||
LIT(PB + 0); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); wait_for_ever();
|
||||
b = loaded(1); /* never reads */
|
||||
LIT(OUT); O(BANG_A);
|
||||
{
|
||||
v4_cell again = v4_asm_label(&as);
|
||||
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
|
||||
v4_asm_branch(&as, V4_OP_JUMP, again);
|
||||
}
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a writer that looks first, wired to a node that never reads");
|
||||
dev.take = stream_take; dev.give = 0; dev.self = &none; dev.pending = device_has; has = 1; dev_has = &has;
|
||||
CHECK(v4_fabric_wire_device(&f, a, 5, &dev), "and to a device, which has something for it");
|
||||
for (i = 0; i < 40; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.request == 1 && v4_node_words_since_look(&ND(a)->n) == 0, "the writer is blocked on its first word, and waits: the fabric has no error for it");
|
||||
v4_fabric_interrupt_error(&f, 21);
|
||||
for (i = 0; i < 40; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "nor is a bare node let go when it has: it cannot take an error");
|
||||
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
||||
v4_node_fault_attach(&ND(a)->n, 900);
|
||||
has = 0;
|
||||
for (i = 0; i < 40; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "a node that can take one waits all the same while the device has nothing for it");
|
||||
has = 1;
|
||||
(void)v4_fabric_step(&f);
|
||||
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 21 && ND(a)->n.mem[OUT + 20] == 21,
|
||||
"when it has, the writer is let go and the error raised on it");
|
||||
|
||||
/* once the first word has gone, it is not let go: the message must be finished */
|
||||
v4_fabric_init(&f, places, 4);
|
||||
for (i = 0; i < 3; i++) v4_node_reset(&pool[i].n);
|
||||
a = loaded(0);
|
||||
LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); O(DROP);
|
||||
LIT(PB + 0); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); wait_for_ever();
|
||||
b = loaded(1); /* reads one word, and no more */
|
||||
LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A);
|
||||
{
|
||||
v4_cell again = v4_asm_label(&as);
|
||||
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
|
||||
v4_asm_branch(&as, V4_OP_JUMP, again);
|
||||
}
|
||||
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire_device(&f, a, 5, &dev), "the same, to a node that reads one word and no more");
|
||||
v4_node_error_attach(&ND(a)->n, OUT + 20);
|
||||
v4_node_fault_attach(&ND(a)->n, 900);
|
||||
v4_fabric_interrupt_error(&f, 21);
|
||||
for (i = 0; i < 60; i++) (void)v4_fabric_step(&f);
|
||||
CHECK(ND(a)->n.asking && ND(a)->n.request == 2 && ND(a)->n.faults == 0, "blocked on its second word with the device still pending, the writer waits");
|
||||
}
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures != 0;
|
||||
}
|
||||
|
||||
+101
-9
@@ -10,6 +10,7 @@
|
||||
* tells it. The console is a device on a port and speaks messages
|
||||
* (v4/include/v4/message.h).
|
||||
*/
|
||||
#include "v4/image.h"
|
||||
#include "v4/fabric.h"
|
||||
#include "v4/message.h"
|
||||
#include "v4/text.h"
|
||||
@@ -70,10 +71,12 @@ static int console_take(void *self, v4_cell value)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
static const v4_device console = { console_take, console_give, 0 };
|
||||
static int console_pending(void *self) { (void)self; return going_at < going.count; }
|
||||
static const v4_device console = { console_take, console_give, 0, console_pending };
|
||||
|
||||
static int is_empty(const char *s) { return s[0] == 0; }
|
||||
|
||||
static unsigned step_cap = 20000000; /* how long a line may take before it is given up on */
|
||||
/* Send text to a node and let everything that follows from it happen. */
|
||||
static const char *tell(v4_cell node, const char *text)
|
||||
{
|
||||
@@ -82,8 +85,16 @@ static const char *tell(v4_cell node, const char *text)
|
||||
ended = 0; ended_from = -1; ended_how = -1;
|
||||
if (!v4_message_text(&going, node, CONSOLE_ID, V4_MSG_TEXT, text, (unsigned)strlen(text))) return "(too long)";
|
||||
going_at = 0;
|
||||
while (steps < 20000000 && v4_fabric_step(&f) != 0) steps++;
|
||||
if (steps >= 20000000) return "(still running)";
|
||||
while (steps < step_cap && v4_fabric_step(&f) != 0) steps++;
|
||||
if (steps >= step_cap) {
|
||||
if (getenv("V4_MESH_STUCK")) {
|
||||
unsigned k;
|
||||
for (k = 0; k < 3; k++) if (v4_fabric_node_at(&f, k)) { const v4_node *n = &pool[k].n;
|
||||
printf(" stuck after \"%s\": node %ld p=%ld asking=%d port=%u reading=%d rport=%u mq=%ld owed=%ld lost=%ld fault=%u@%ld\n", text, (long)n->mem[ME], (long)n->p, n->asking, n->ask_port, n->reading, n->read_port,
|
||||
(long)n->mem[MQ_COUNT], (long)n->mem[OWED_COUNT], (long)n->mem[LOST], n->fault_kind, (long)n->fault_addr); }
|
||||
}
|
||||
return "(still running)";
|
||||
}
|
||||
return printed;
|
||||
}
|
||||
|
||||
@@ -298,8 +309,6 @@ int main(void)
|
||||
CHECK(pool[1].n.mem[OWED_COUNT] == 8, "it owes a refusal for each of the rest, as many as it can owe: %ld", (long)pool[1].n.mem[OWED_COUNT]);
|
||||
CHECK(pool[0].n.mem[REFUSED] == refused, "and has sent none yet: it is still waiting");
|
||||
|
||||
/* a GONE that does not come from its centre is not acted on */
|
||||
CHECK(is_empty(tell(12, "12 11 GONE")) && pool[1].n.mem[AWAIT_FROM] == 12, "node 12 tells it that 12 is gone: that is not its centre speaking, and it goes on waiting");
|
||||
/* one from its centre ends the wait */
|
||||
CHECK(is_empty(tell(10, "12 NO-ROUTE")), "node 10 forgets the way to 12");
|
||||
CHECK(strstr(tell(10, "12 11 GONE"), "Node gone") != NULL && printed_from == 11 && ended_from != -1, "node 10, its centre, tells it 12 is gone: its wait ends with an error that says so: \"%s\"", printed);
|
||||
@@ -308,10 +317,8 @@ int main(void)
|
||||
not_taken = 49 - got;
|
||||
CHECK(got >= 30 && not_taken > 8, "it then did every message it had kept: %ld of the 49", (long)got);
|
||||
CHECK(pool[0].n.mem[REFUSED] == refused + 8, "and sent the eight refusals it owed: node 10 was told of %ld", (long)(pool[0].n.mem[REFUSED] - refused));
|
||||
/* two more than node 10's: node 12's word of how "12 11 GONE" ended came this way for the console while
|
||||
* node 11 was full, and was not taken; nor could the refusal for it be owed */
|
||||
CHECK(pool[1].n.mem[LOST] == lost + (not_taken + 1) + (not_taken + 1 - 8), "it counted each message it did not take, and each refusal it could not owe: %ld for %ld not taken",
|
||||
(long)(pool[1].n.mem[LOST] - lost), (long)(not_taken + 1));
|
||||
CHECK(pool[1].n.mem[LOST] == lost + not_taken + (not_taken - 8), "it counted each message it did not take, and each refusal it could not owe: %ld for %ld not taken",
|
||||
(long)(pool[1].n.mem[LOST] - lost), (long)not_taken);
|
||||
/* the way to 12 is forgotten: what is sent there now is refused */
|
||||
refused = pool[1].n.mem[REFUSED];
|
||||
CHECK(is_empty(tell(11, ": P12 S\" 1 DROP\" 12 SEND ; P12")) && pool[1].n.mem[REFUSED] == refused + 1, "having forgotten the way to 12, what node 11 sends there goes toward its centre, which has no way either, and is refused");
|
||||
@@ -330,6 +337,91 @@ int main(void)
|
||||
|
||||
CHECK(strstr(tell(10, "5 NO-ROUTE"), "ERROR") == NULL && strcmp(tell(11, "20 22 + ."), "42 ") == 0, "forgetting the way to a node there was no way to changes nothing");
|
||||
|
||||
|
||||
/* ---- what the review of step 6c found (MESH.md step 6c) ------------------- */
|
||||
step_cap = 3000000;
|
||||
v4_cell ways11 = 0;
|
||||
|
||||
/* a GONE is believed only from the node's centre: not by another port, and not from another node by the centre's port */
|
||||
CHECK(is_empty(tell(10, "12 NO-ROUTE 12 3 ROUTE 12 3 NEIGHBOUR")) && is_empty(tell(12, "NO-ROUTES 3 DEFAULT-ROUTE 11 2 ROUTE 10 3 NEIGHBOUR")),
|
||||
"node 12 is reached straight from node 10 now, and has its own wire to 11");
|
||||
CHECK(is_empty(tell(11, "12 AWAIT .")) && ended == 0 && pool[1].n.mem[AWAIT_FROM] == 12, "node 11 waits for node 12's answer");
|
||||
ways11 = pool[1].n.mem[ROUTE_COUNT];
|
||||
CHECK(is_empty(tell(12, "12 11 GONE")) && ended == 1 && ended_from == 12 && pool[1].n.mem[AWAIT_FROM] == 12 && pool[1].n.mem[ROUTE_COUNT] == ways11,
|
||||
"node 12 tells it, by the wire between them, that 12 is gone: that is not by its centre's port, and it goes on waiting");
|
||||
CHECK(is_empty(tell(12, "11 NO-ROUTE 12 11 GONE")) && ended == 1 && ended_from == 12 && pool[1].n.mem[AWAIT_FROM] == 12 && pool[1].n.mem[ROUTE_COUNT] == ways11,
|
||||
"node 12 says it again by way of node 10: it comes by the centre's port but is not from the centre, and node 11 goes on waiting, its ways as they were");
|
||||
CHECK(strstr(tell(10, "12 11 GONE"), "Node gone") != NULL && pool[1].n.mem[AWAIT_FROM] == 0, "node 10, its centre, says it: the wait ends: \"%s\"", printed);
|
||||
CHECK(is_empty(tell(11, "12 3 ROUTE")) && is_empty(tell(12, "11 2 ROUTE")), "the ways are told again");
|
||||
|
||||
/* an answer that came before the wait began is in the messages waiting, and is found there */
|
||||
CHECK(strstr(tell(12, ": QF S\" 1 DROP\" 77 SEND 3000 0 DO LOOP S\" 1 DROP\" 10 SEND 77 AWAIT . ; QF"), "Message refused") != NULL && ended_how == V4_TEXT_ERROR
|
||||
&& pool[2].n.mem[AWAIT_FROM] == 0 && waiting(far),
|
||||
"node 12 sends to 77, is busy a while, sends another message -- taking in the refusal as it does -- and then waits for 77's answer: the wait ends at once: \"%s\"", printed);
|
||||
CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0, "and it goes on");
|
||||
|
||||
/* a waiting sender whose message found no room */
|
||||
{
|
||||
CHECK(is_empty(tell(11, "0 GOT !")) && is_empty(tell(11, "12 AWAIT .")) && pool[1].n.mem[AWAIT_FROM] == 12, "node 11 waits on 12 again");
|
||||
CHECK(is_empty(tell(10, ": F40 40 0 DO S\" HIT\" 11 SEND LOOP ; F40")) && pool[1].n.mem[OWED_COUNT] == 0, "node 10 sends it as many messages as it has room for");
|
||||
(void)tell(10, ": ONE S\" HIT\" 11 SEND 12 NO-ROUTE 12 11 GONE 11 AWAIT . ; ONE");
|
||||
CHECK(strstr(printed, "Message refused") != NULL && pool[0].n.mem[AWAIT_FROM] == 0,
|
||||
"it sends one more, for which there is no room, ends node 11's wait, and waits for node 11's answer: its own wait ends, the message refused: \"%s\"", printed);
|
||||
CHECK(strtol(tell(11, "GOT @ ."), NULL, 10) == 40 && is_empty(tell(10, "12 3 ROUTE")) && is_empty(tell(11, "12 3 ROUTE")), "node 11 did the forty it had kept; the ways are told again");
|
||||
}
|
||||
|
||||
/* an answer that is refused on its way back is told to the node that waits for it, not to the node that answered */
|
||||
{
|
||||
v4_cell r12 = pool[2].n.mem[REFUSED];
|
||||
CHECK(is_empty(tell(12, "10 2 ROUTE")), "node 12 is told the way to node 10 is by node 11");
|
||||
CHECK(is_empty(tell(11, "0 GOT !")) && is_empty(tell(11, "55 AWAIT .")) && pool[1].n.mem[AWAIT_FROM] == 55, "node 11 waits on a node there is not");
|
||||
CHECK(is_empty(tell(10, "F40")) && pool[1].n.mem[OWED_COUNT] == 0, "node 10 sends it as many messages as it has room for");
|
||||
(void)tell(10, ": RA S\" 1 DROP\" 12 SEND 3000 0 DO LOOP 55 11 GONE 12 AWAIT . ; RA");
|
||||
CHECK(strstr(printed, "Message refused") != NULL && pool[0].n.mem[AWAIT_FROM] == 0,
|
||||
"node 10 sends node 12 text; its answer comes back by node 11, which has no room for it; node 10 then ends node 11's wait and waits for the answer: it is told the message was refused: \"%s\"", printed);
|
||||
CHECK(pool[2].n.mem[REFUSED] == r12, "node 12, which answered, is told nothing");
|
||||
CHECK(strtol(tell(11, "GOT @ ."), NULL, 10) == 40 && is_empty(tell(12, "10 3 ROUTE")) && strcmp(tell(12, "7 8 * ."), "56 ") == 0, "node 11 did the forty it had kept; the way is told again");
|
||||
}
|
||||
|
||||
/* a message that would go round for ever is refused after 16 nodes have passed it on */
|
||||
{
|
||||
v4_cell refused = pool[0].n.mem[REFUSED], l10 = pool[0].n.mem[LOST], l11 = pool[1].n.mem[LOST];
|
||||
CHECK(is_empty(tell(10, "66 2 ROUTE")), "node 10 is told the way to 66 is by node 11, whose way for everything it has not been told of is back by node 10");
|
||||
CHECK(is_empty(tell(10, ": H66 S\" 1 DROP\" 66 SEND ; H66")) && ended == 1 && ended_how == V4_TEXT_COMPLETED, "node 10 sends 66 a message: it comes to rest");
|
||||
CHECK(pool[0].n.mem[LOST] + pool[1].n.mem[LOST] == l10 + l11 + 1 && pool[0].n.mem[REFUSED] == refused + 1,
|
||||
"one of the two let it go when it had been passed on 16 times, and node 10 was told it was refused");
|
||||
CHECK(is_empty(tell(10, "66 NO-ROUTE")) && waiting(hera) && waiting(mid) && waiting(far), "the way is forgotten, and all three are at rest");
|
||||
}
|
||||
|
||||
/* a node that owes a refusal to a node that is stuck is not stuck itself */
|
||||
CHECK(is_empty(tell(12, "NO-ROUTES 1 3 ROUTE 10 3 ROUTE 11 2 ROUTE")) && is_empty(tell(11, "55 3 ROUTE")), "node 12 has no way for 55; node 11 is told the way to 55 is by node 12");
|
||||
CHECK(strcmp(tell(11, ": C1 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C1"), "(still running)") == 0, "node 11 sends 55 a message and is then stuck in a loop");
|
||||
CHECK(pool[2].n.mem[OWED_COUNT] == 1, "node 12 owes node 11 a refusal, and cannot give it: node 11 is not reading");
|
||||
(void)tell(12, "7 8 * .");
|
||||
CHECK(strcmp(printed, "56 ") == 0 && printed_from == 12, "node 12 goes on doing what it is sent: \"%s\"", printed);
|
||||
|
||||
/* a node that is waiting to write to a node finds out when that node is removed */
|
||||
(void)tell(12, ": TO11 S\" 1 DROP\" 11 SEND 65 EMIT ; TO11");
|
||||
CHECK(ended == 0, "node 12 sends node 11 a message: node 11 is not reading, and node 12 waits to write to it");
|
||||
CHECK(v4_fabric_remove(&f, mid) == &pool[1], "node 11 is removed");
|
||||
(void)tell(10, "1 DROP");
|
||||
CHECK(strstr(printed, "No one on that port") != NULL && strstr(printed, "A") == NULL, "node 12's line ends in an error that says no one is there: \"%s\"", printed);
|
||||
step_cap = 20000000;
|
||||
CHECK(strcmp(tell(12, "7 8 * ."), "56 ") == 0 && pool[2].n.mem[OWED_COUNT] == 0 && waiting(far) && waiting(hera),
|
||||
"it goes on, the refusal it owed node 11 let go: it owes nothing and is at rest");
|
||||
|
||||
/* THE LIMIT (MESH.md 7b.7): a refusal owed to a neighbour whose number is the higher is written
|
||||
* without looking, as any message to it is, so a node that is stuck holds up a neighbour that owes
|
||||
* it one -- until it is removed */
|
||||
step_cap = 3000000;
|
||||
CHECK(is_empty(tell(12, "55 3 ROUTE")), "node 12 is told the way to 55 is by node 10, which has none");
|
||||
CHECK(strcmp(tell(12, ": C2 S\" 1 DROP\" 55 SEND BEGIN 0 UNTIL ; C2"), "(still running)") == 0 && pool[0].n.asking && pool[0].n.mem[OWED_COUNT] == 1,
|
||||
"node 12 sends 55 a message and is then stuck in a loop: node 10 owes it a refusal, writes it, and is blocked");
|
||||
v4_fabric_gone_error(&f, V4_ERROR_NO_ONE);
|
||||
CHECK(v4_fabric_remove(&f, far) == &pool[2], "node 12 is removed");
|
||||
step_cap = 20000000;
|
||||
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && pool[0].n.mem[OWED_COUNT] == 0 && waiting(hera), "node 10 is let go, lets the refusal go, and goes on: \"%s\"", printed);
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures != 0;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ static v4_heat h;
|
||||
static v4_text tx;
|
||||
static v4_cell w_key, w_key_end, w_fault, capsule_latest;
|
||||
static v4_cell w_idle; /* (IDLE): where the node waits for a message */
|
||||
static v4_cell w_idle_end; /* (PASS-ON): the word after it */
|
||||
static char out[V4_CONSOLE_CAP + 1];
|
||||
|
||||
/* STORAGE (MESH.md section 8). The node asks its kernel for its blocks, and
|
||||
@@ -199,7 +200,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 + 8; /* in the first words of (IDLE), where it reads */
|
||||
return n.reading && !n.given && n.read_port == V4_PORT_ANY && n.p > w_idle && n.p <= w_idle_end; /* in (IDLE), where it reads */
|
||||
}
|
||||
|
||||
/* Run until the text ends, or until it has been inside KEY with nothing to
|
||||
@@ -278,7 +279,7 @@ 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 */
|
||||
v4_node_port_status(&n, 0, (1u | 1u << CONSOLE_PORT) * (1u + (1u << V4_PORTS))); /* 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;
|
||||
@@ -292,7 +293,7 @@ static void boot_with(unsigned depth)
|
||||
line_open = 0;
|
||||
to_node.count = 0; to_sent = 0; from_node.count = 0;
|
||||
shown_len = 0; shown_dropped = 0;
|
||||
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 */
|
||||
for (i = 0; i < 64 && !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. */
|
||||
@@ -869,6 +870,7 @@ int main(void)
|
||||
printf(" capsule: %ld words\n", (long)v4_text_here(&tx) - 16);
|
||||
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
|
||||
w_idle = v4_text_word(&tx, "(IDLE)");
|
||||
w_idle_end = v4_text_word(&tx, "(PASS-ON)"); /* the word after (IDLE) in quit.v4 */
|
||||
w_key = v4_text_word(&tx, "KEY");
|
||||
w_key_end = v4_text_word(&tx, "CR"); /* the word after KEY in core.v4 */
|
||||
w_fault = v4_text_word(&tx, "(FAULTS)");
|
||||
|
||||
@@ -140,7 +140,7 @@ static int host_born(void *self)
|
||||
v4_node_fault_attach(&pool[k].n, w_fault);
|
||||
v4_blocks_init(&node_blocks[k], BUF0_W);
|
||||
if (k != 0) { /* Hera's kernel is wired where she is made */
|
||||
node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k];
|
||||
node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k]; node_kernel[k].pending = 0;
|
||||
if (!v4_fabric_wire_device(&f, (unsigned)place, 0, &node_kernel[k])) return -1;
|
||||
}
|
||||
return place;
|
||||
@@ -194,7 +194,7 @@ static int kernel_take(void *self, v4_cell value) /* a request */
|
||||
if (value < FIRST_REQUEST || !v4_manage_serve(&manage, hera_place, (unsigned)(value - FIRST_REQUEST))) refused++;
|
||||
return 1;
|
||||
}
|
||||
static const v4_device kernel = { kernel_take, kernel_give, 0 };
|
||||
static const v4_device kernel = { kernel_take, kernel_give, 0, 0 };
|
||||
|
||||
/* ---- the console: a device on Hera's port 1 ---- */
|
||||
#define CONSOLE_ID 1
|
||||
@@ -237,7 +237,8 @@ static int console_take(void *self, v4_cell value)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
static const v4_device console = { console_take, console_give, 0 };
|
||||
static int console_pending(void *self) { (void)self; return going_at < going.count; }
|
||||
static const v4_device console = { console_take, console_give, 0, console_pending };
|
||||
|
||||
/* Is there a node that will execute at the next step? A step in which a
|
||||
* node only faulted counts nothing done (exec.h), and it goes on from its
|
||||
@@ -381,6 +382,7 @@ int main(void)
|
||||
/* ---- Hera is born empty, and takes in the nucleus ---- */
|
||||
v4_fabric_init(&f, places, PLACES);
|
||||
v4_fabric_gone_error(&f, V4_ERROR_NO_ONE);
|
||||
v4_fabric_interrupt_error(&f, V4_ERROR_INTERRUPTED);
|
||||
place = host_born(0);
|
||||
CHECK(place == 0, "a node is born");
|
||||
hera_place = (unsigned)place;
|
||||
@@ -546,14 +548,28 @@ int main(void)
|
||||
}
|
||||
step_limit = 30000000ul;
|
||||
|
||||
/* Hera blocked writing to a node that is stuck: a line from the console lets her go (MESH.md 7b.7) */
|
||||
step_limit = 3000000ul;
|
||||
CHECK(strcmp(tell(11, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 11 is stuck in a loop");
|
||||
CHECK(strcmp(tell(10, ": T11 S\" 1 DROP\" 11 SEND 67 EMIT ; T11"), "(still running)") == 0 && node_numbered(10)->asking,
|
||||
"Hera sends it text: its number is the higher, so she writes without looking, and is blocked");
|
||||
{
|
||||
const char *r = tell(10, "11 KILL 68 EMIT");
|
||||
CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "D") != NULL && strstr(r, "C") == NULL && strstr(r, "Interrupted\n") < strstr(r, "D"),
|
||||
"a line typed at the console ends the line that was blocked, with an error that says so, and is then done: \"%s\"", r);
|
||||
CHECK(node_numbered(11) == NULL && !node_numbered(10)->asking && waiting(10), "the line killed the stuck node, and Hera is back at her ports");
|
||||
}
|
||||
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0, "and she goes on");
|
||||
step_limit = 30000000ul;
|
||||
|
||||
/* what is not a node she has had born */
|
||||
{
|
||||
unsigned born = born_count;
|
||||
CHECK(strstr(tell(10, "99 KILL"), "KILL: no such node") != NULL && ended_how == V4_TEXT_ERROR, "KILL of a node there never was is an error that says so: \"%s\"", printed);
|
||||
CHECK(strstr(tell(10, "10 KILL"), "KILL: no such node") != NULL && ended_how == V4_TEXT_ERROR && node_numbered(10) != NULL, "so is KILL of Hera herself");
|
||||
CHECK(strstr(tell(10, "14 KILL"), "KILL: no such node") != NULL && ended_how == V4_TEXT_ERROR, "and of a node she has killed already");
|
||||
CHECK(born_count == born && node_numbered(11) != NULL && strcmp(tell(11, "11 100 * ."), "1100 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0,
|
||||
"nothing was removed, and the two that are left go on");
|
||||
CHECK(born_count == born && strcmp(tell(10, "1 2 + ."), "3 ") == 0,
|
||||
"nothing was removed, and Hera goes on");
|
||||
}
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
|
||||
Reference in New Issue
Block a user