revert(v4.0.0): step 6d, the wait, is backed out -- two reviews found it unsound

Ruled 2026-10-08.  The engine, the nucleus and their tests are as they
were at dfabfa46, before step 6d; the code of the wait and its tests are
in history at 075385ab..2cf37aab.  The limit of MESH.md 7b.7 stands: a
node that is stuck can hold up its neighbours until Hera kills it.

MESH.md 7c is kept as the record of the design as approved, and 7c.6 says
what was built, what the two reviews found, the cause the findings share
-- a node's message machinery runs on the stacks its text is using, and a
fault abandons whatever was in progress -- and what a second attempt
must settle before anything is built.

Kept from it, in hosted-check: a line that leaves 29 values on the stack,
and WORDS with 26 values on it.  Both hung the products at some commit of
the wait.

make -C v4 test and sanitize at both widths, hosted-check, lint; three
bare-metal boots typing both.  POST 538 of 538, word_count=317,
dict_hash=0xc0769523a47b7dc3 on all six: the hash of step 6c's code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-08 07:31:40 -04:00
co-authored by Claude Opus 5.5
parent 10f5ffd00b
commit 6015c87157
20 changed files with 1108 additions and 1203 deletions
Binary file not shown.
+132 -181
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,204 @@
[=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
Architecture: amd64
Build: Oct 8 2026 07:20:37
UEFI BootServices: EXITED
=== StarKernel Boot Information ===
Memory map entries: 135
Total memory: 1023 MB
Usable memory: 966 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 248743
Free pages : 247220
Used pages : 1523
Total MB : 971
Free MB : 965
Used MB : 5
VMM initialized (mapped RAM, CR3 switched)
VMM initialized (mapped RAM, CR3 switched)
VMM self-test: mapped OK at 0xffff800000000000
VMM self-test complete.
IDT installed.
APIC: init...
APIC: IA32_APIC_BASE MSR=0x00000000fee00900
APIC: stale-ISR drain: 0 EOI(s) issued
APIC: initialized (xAPIC MMIO, TPR=0, SIVR=0x1FF, EOI-clear)
APIC: init done
I/O APIC: init...
I/O APIC: base=0xfec00000, gsi_base=0, overrides=5
override: source=00 gsi=2 flags=0x0000
override: source=05 gsi=5 flags=0x000d
override: source=09 gsi=9 flags=0x000d
override: source=0a gsi=10 flags=0x000d
override: source=0b gsi=11 flags=0x000d
i8042: keyboard ACKed enable-scanning
i8042: IRQ1 enabled in controller config byte
I/O APIC: keyboard IRQ1 routed (masked)
Timer: init...
Timer: init start
Timer: PM_TMR_BLK discovered from FADT at port 1544
Timer: VM mode detected (hypervisor present).
Timer: HPET calibration disabled (VM-exit MMIO would poison timing).
Timer: WARNING: invariant TSC not present under hypervisor.
Timer: continuing in RELATIVE mode (no determinism guarantees).
Timer: RDTSCP not present; using RDTSC (less serialized).
Timer: CPUID frequency unavailable; trying PM Timer...
Timer: trust=1 (0=NONE,1=REL,2=ABS), TSC=2096654913 Hz
Timer: init done
Kernel heap initialized.
Heap statistics:
Total bytes: 536870872
Free bytes: 536870872
Used bytes: 0
Peak bytes: 0
Heap base addr: 24641536
Heap end addr: 561512448
Heartbeat: init...
APIC Timer: calibrating...
APIC Timer: apic_hz=1007656006, tick_hz=100, initial_count=10076560
APIC Timer: configured (masked, ready to start)
Heartbeat: init done
Kernel initialization complete.
Boot successful!
StarForth v4: one host node, the F18-derived engine
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x5ff3672509f6c4e5 capsule_hash=0x5ff3672509f6c4e5 words=311
V4: capsule v4:forth79.4th signature: missing (unsigned)
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xc07eb35ca84c4aa8
PARITY:V4_POST tests=538 pass=538 fail=0
PARITY:V4_SYSTEM word_count=317 dict_hash=0xc0769523a47b7dc3
PARITY:OK
POST: PASSED
[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: 79653714] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
Artemis: virtio-blk attached
ok> : SQ DUP * ;
ok
ok> 7 SQ . 3 4 U* . .
49 0 12 ok
ok> T{
UNKNOWN WORD: 'T{'
ERROR
ok> RS1
UNKNOWN WORD: 'RS1'
ERROR
ok> COLD
FORTH-79 Cold Start
System initialized.
ok
ok> 3 4 U* . .
0 12 ok
ok> FORGET U*
Protected word
ERROR
ok> 9 KERNEL-WORD ASK9 ASK9
Argument out of range
ERROR
ok> 1 BLOCK C@ . 2047 BLOCK C@ .
0 0 ok
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
65 ok
ok> 3072 BLOCK C@ .
0 ok
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
Storage refused
ERROR
ok> 9999999 BLOCK
Block out of range
ERROR
ok> 5 7 PORT!
No one on that port
ERROR
ok> 1 2 + .
3 ok
ok> 5 AWAIT
7 8 * .
Interrupted
ERROR
ok> 56 ok
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Stack overflow
ERROR
ok> 3 4 + .
7 ok
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
ok
ok> WORDS
ASK9 U/MOD U* (UD) (NB) (BITS) BYE ACL-WORD-ID ACL-HEAT@ ACL-INIT-PRIMITIVES
ACL-INHERIT ACL-ALLOW! ACL-TTL! ACL-MODE! ACL-PIN ACL-PINNED? ACL-ALLOW@
ACL-TTL@ ACL-MODE@ ACL-HOOK LOG-DEBUG" LOG-TEST" LOG-INFO" LOG-WARN"
LOG-ERROR" (LOG") LOG-DEBUG-STR LOG-TEST-STR LOG-INFFO-STR LOG-WARN-STR
LOG-ERROR-STR LOG-LEVEL! LOG-LEVEL@ LOG-DEBUG LOG-TEST LOG-INFO
LOG-WARN LOG-ERROR THRU --> LOAD LIST EMPTY-BUFFERS FLUSH SAVE-BUFFERS
UPDATE BLOCK BUFFER BLK SCR Q.PRINT Q.COS Q.SIN Q.LOG Q.SQRT Q.EXP
Q./ Q.* Q.MIN Q.MAX Q.0= Q.> Q.< Q.= Q.NEG Q.ABS QQ.- Q.+ Q.SCALE
Q.0 Q.1 Q.TO-INT Q.FROM-INT DEFER@ IS DEFER COLD WARM PAGE VERSION
79-STANDARD FORGET VLIST WORDS ORDER VOCABULARY DEFINITIONS FORTH
CURRENT CONTEXT FENCE SKIP SCAN SEARCH COMPARE -TRAILING BLANK ERASE
FILL MOVE CMOVE> ?TERMINAL DMIN DMAX D< M- M+ D2/ D2* D== D0< D0=
DABS D- RSHIFT LSHIFT WITHIN MIN MAX ABS U> U< >= <= <> 0> 0<> -!
2! 2@ INVERT FALSE TRUE NOP 2R@ 2R> 2>R 2ROT 2OVER 2SWAP DUMP .S
? D. D.R U. U.R . .R OCTAL HEX DECIMAL SPACES #> #S # SIGN HOLD
<# STATE PAD BASE SPAN >IN TIB BL > < = NOT 0< 0= OR XOR AND M/MODD
*/ */MOD MOD / /MOD M* S>D 2/ 2* 2- 2+ 1- 1+ NEGATE * - + CELLS
+! ! @ UNLOOP LEAVE J I R@ R> >R ROLL PICK DEPTH 2DROP 2DUP ?DUP
-ROT ROT SWAP OVER DROP DUP S" (S") ABORT" (ABORT") ." (.") ABORT
QUIT (WORD-FORGOTTEN) (WORD-DEFINED) (REFUSED) (LOST) (CONSOLE)
(ME) NODE--ERROR (SEAL) PORT! AWAIT SEND-ON SEND NO-ROUTES GONE NO-ROUTE
DEFAULT-ROUTE NEIGHBOUR ROUTE +LOOP LOOP ?DO DO ENDCASE ENDOF OF
CASE REPEAT WHILE AGAIN UNTIL BEGIN THEN ELSE IF DOES> CONSTANT
VARIABLE CREATE [COMPILE] COMPILE IMMEDIATE EXIT KERNEL-WORD ; :
[LITERAL] ['] '' LITERAL ] [ INTERPRET EXECUTE FIND HIDDEN SMUDGE
TRAVERSE >BODY PFA CFA NAME> NFA >NAME LINK> LFA >LINK LATEST 2,
C, , ALLOT ALIGN HERE \ ( NUMBER CONVERT ENCLOSE WORD QUERY EXPECT
SOURCE TYPE COUNT SPACE CR KEY EMIT CMOVE C! C@ DNEGATE D+ UM*
ok
ok> DEPTH .
26 ok
ok> HERE .
8300 ok
ok> BYE
BYE: cold restart
@@ -0,0 +1,177 @@
UEFI firmware (version 2025.11-3ubuntu7.3 built at 15:40:26 on Sep 23 2026)
[=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
Architecture: aarch64
Build: Oct 8 2026 07:23:27
UEFI BootServices: EXITED
=== StarKernel Boot Information ===
Memory map entries: 108
Total memory: 4093 MB
Usable memory: 4054 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 1039354
Free pages : 1037943
Used pages : 1411
Total MB : 4059
Free MB : 4054
Used MB : 5
VMM initialized (mapped RAM, CR3 switched)
VMM initialized (mapped RAM, CR3 switched)
VMM self-test: mapped OK at 0xffff800000000000
VMM self-test complete.
AArch64: running at EL1
IDT installed.
APIC: init...
PSCI: no DTB -- using HVC (QEMU virt-machine default)
GICv2: no DTB GIC node -- using QEMU virt-machine defaults
GICv2: distributor+CPU interface enabled, PPI 30
APIC: init done
Timer: init...
Timer: AArch64 generic timer initialised.
Timer: init done
Kernel heap initialized.
Heap statistics:
Total bytes: 2147483608
Free bytes: 2147483608
Used bytes: 0
Peak bytes: 0
Heap base addr: 1207959552
Heap end addr: 3355443200
Heartbeat: init...
Heartbeat: init done
Kernel initialization complete.
Boot successful!
StarForth v4: one host node, the F18-derived engine
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x5ff3672509f6c4e5 capsule_hash=0x5ff3672509f6c4e5 words=311
V4: capsule v4:forth79.4th signature: missing (unsigned)
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xc07eb35ca84c4aa8
PARITY:V4_POST tests=538 pass=538 fail=0
PARITY:V4_SYSTEM word_count=317 dict_hash=0xc0769523a47b7dc3
PARITY:OK
POST: PASSED
[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
PCI: init...
virtio-blk: found device
[HADES][INFO ] [KRELTSC: 1411419] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
Artemis: virtio-blk attached
ok> : SQ DUP * ;
ok
ok> 7 SQ . 3 4 U* . .
49 0 12 ok
ok> T{
UNKNOWN WORD: 'T{'
ERROR
ok> RS1
UNKNOWN WORD: 'RS1'
ERROR
ok> COLD
FORTH-79 Cold Start
System initialized.
ok
ok> 3 4 U* . .
0 12 ok
ok> FORGET U*
Protected word
ERROR
ok> 9 KERNEL-WORD ASK9 ASK9
Argument out of range
ERROR
ok> 1 BLOCK C@ . 2047 BLOCK C@ .
0 0 ok
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
65 ok
ok> 3072 BLOCK C@ .
0 ok
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
Storage refused
ERROR
ok> 9999999 BLOCK
Block out of range
ERROR
ok> 5 7 PORT!
No one on that port
ERROR
ok> 1 2 + .
3 ok
ok> 5 AWAIT
7 8 * .
Interrupted
ERROR
ok> 56 ok
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Stack overflow
ERROR
ok> 3 4 + .
7 ok
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
ok
ok> WORDS
ASK9 U/MOD U* (UD) (NB) (BITS) BYE ACL-WORD-ID ACL-HEAT@ ACL-INIT-PRIMITIVES
ACL-INHERIT ACL-ALLOW! ACL-TTL! ACL-MODE! ACL-PIN ACL-PINNED? ACL-ALLOW@
ACL-TTL@ ACL-MODE@ ACL-HOOK LOG-DEBUG" LOG-TEST" LOG-INFO" LOG-WARN"
LOG-ERROR" (LOG") LOG-DEBUG-STR LOG-TEST-STR LOG-INFO-STR LOG-WARN-STR
LOG-ERROR-STR LOG-LEVEL! LOG-LEVEL@ LOG-DEBUG LOG-TEST LOG-INFO
LOG-WARN LOG-ERROR THRU --> LOAD LIST EMPTY-BUFFERS FLUSH SAVE-BUFFERS
UPDATE BLOCK BUFFER BLK SCR Q.PRINT Q.COS Q.SIN Q.LOG Q.SQRT Q.EXP
Q./ Q.* Q.MIN Q.MAX Q.0= Q.> Q.< Q.= Q.NEG Q.ABS Q.- Q.+ Q.SCALE
Q.0 Q.1 Q.TO-INT Q.FROM-INT DEFER@ IS DEFER COLD WARM PAGE VERSION
79-STANDARD FORGET VLIST WORDS ORDER VOCABULARY DEFINITIONS FORTH
CURRENT CONTEXT FENCE SKIP SCAN SEARCH COMPARE -TRAILING BLANK ERASE
FILL MOVE CMOVE> ?TERMINAL DMIN DMAX D< M- M+ D2/ D2* D= D0< D0=
DABS D- RSHIFT LSHIFT WITHIN MIN MAX ABS U> U< >= <= <> 0> 0<> -!
2! 2@ INVERT FALSE TRUE NOP 2R@ 2R> 2>R 2ROT 2OVER 2SWAP DUMP .S
? D. D.R U. U.R . .R OCTAL HEX DECIMAL SPACES #> #S # SIGN HOLD
<# STATE PAD BASE SPAN >IN TIB BL > < = NOT 0< 0= OR XOR AND M/MOD
*/ */MOD MOD / /MOD M* S>D 2/ 2* 2- 2+ 1- 1+ NEGATE * - + CELLS
+! ! @ UNLOOP LEAVE J I R@ R> >R ROLL PICK DEPTH 2DROP 2DUP ?DUP
-ROT ROT SWAP OVER DROP DUP S" (S") ABORT" (ABORT") ." (.") ABORT
QUIT (WORD-FORGOTTEN) (WORD-DEFINED) (REFUSED) (LOST) (CONSOLE)
(ME) NODE-ERROR (SEAL) PORT! AWAIT SEND-ON SEND NO-ROUTES GONE NO-ROUTE
DEFAULT-ROUTE NEIGHBOUR ROUTE +LOOP LOOP ?DO DO ENDCASE ENDOF OF
CASE REPEAT WHILE AGAIN UNTIL BEGIN THEN ELSE IF DOES> CONSTANT
VARIABLE CREATE [COMPILE] COMPILE IMMEDIATE EXIT KERNEL-WORD ; :
[LITERAL] ['] ' LITERAL ] [ INTERPRET EXECUTE FIND HIDDEN SMUDGE
TRAVERSE >BODY PFA CFA NAME> NFA >NAME LINK> LFA >LINK LATEST 2,
C, , ALLOT ALIGN HERE \ ( NUMBER CONVERT ENCLOSE WORD QUERY EXPECT
SOURCE TYPE COUNT SPACE CR KEY EMIT CMOVE C! C@ DNEGATE D+ UM*
ok
ok> DEPTH .
26 ok
ok> HERE .
8300 ok
ok> BYE
BYE: cold restart
@@ -0,0 +1,247 @@
OpenSBI v1.8
____ _____ ____ _____
/ __ \ / ____| _ \_ _|
| | | |_ __ ___ _ __ | (___ | |_) || |
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
| |__| | |_) | __/ | | |____) | |_) || |_
\____/| .__/ \___|_| |_|_____/|____/_____|
| |
|_|
Platform Name : riscv-virtio,qemu
Platform Features : medeleg
Platform HART Count : 1
Platform HART Protection : pmp
Platform IPI Device : aclint-mswi
Platform Timer Device : aclint-mtimer @ 10000000Hz
Platform Console Device : uart8250
Platform HSM Device : ---
Platform PMU Device : ---
Platform Reboot Device : syscon-reboot
Platform Shutdown Device : syscon-poweroff
Platform Suspend Device : ---
Platform CPPC Device : ---
Firmware Base : 0x80000000
Firmware Size : 321 KB
Firmware RW Offset : 0x40000
Firmware RW Size : 65 KB
Firmware Heap Offset : 0x47000
Firmware Heap Size : 37 KB (total), 0 KB (reserved), 12 KB (used), 23 KB (free)
Firmware Scratch Size : 4096 B (total), 1464 B (used), 2632 B (free)
Runtime SBI Version : 3.0
Standard SBI Extensions : ipi,pmu,srst,sse,hsm,rfnc,fwft,time,base,legacy,dbcn,dbtr
Experimental SBI Extensions : none
Domain0 Name : root
Domain0 Boot HART : 0
Domain0 HARTs : 0*
Domain0 Region00 : 0x0000000080040000-0x000000008005ffff M: (F,R,W) S/U: ()
Domain0 Region01 : 0x0000000080000000-0x000000008003ffff M: (F,R,X) S/U: ()
Domain0 Region02 : 0x0000000000100000-0x0000000000100fff M: (I,R,W) S/U: (R,W)
Domain0 Region03 : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
Domain0 Region04 : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
Domain0 Region05 : 0x000000000c400000-0x000000000c5fffff M: (I,R,W) S/U: (R,W)
Domain0 Region06 : 0x000000000c000000-0x000000000c3fffff M: (I,R,W) S/U: (R,W)
Domain0 Region07 : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
Domain0 Next Address : 0x0000000020000000
Domain0 Next Arg1 : 0x00000000bfe00000
Domain0 Next Mode : S-mode
Domain0 SysReset : yes
Domain0 SysSuspend : yes
Boot HART ID : 0
Boot HART Domain : root
Boot HART Priv Version : v1.12
Boot HART Base ISA : rv64imafdch
Boot HART ISA Extensions : sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu
Boot HART PMP Count : 16
Boot HART PMP Granularity : 2 bits
Boot HART PMP Address Bits : 54
Boot HART MHPM Info : 16 (0x0007fff8)
Boot HART Debug Triggers : 2 triggers
Boot HART MIDELEG : 0x0000000000001666
Boot HART MEDELEG : 0x0000000000f4b509
[=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 = 0x00000000bdc9caaa
riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a)
_____ _ _ __ _
/ ____| | | |/ / | |
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
____) | || (_| | | | . \ __/ | | | | | __/ |
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
LithosAnanke v2.1.0
Architecture: riscv64
Build: Oct 8 2026 07:27:03
UEFI BootServices: EXITED
=== StarKernel Boot Information ===
Memory map entries: 100
Total memory: 1020 MB
Usable memory: 975 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 250269
Free pages : 249617
Used pages : 652
Total MB : 977
Free MB : 975
Used MB : 2
VMM initialized (mapped RAM, CR3 switched)
VMM initialized (mapped RAM, CR3 switched)
VMM self-test: mapped OK at 0xffff800000000000
VMM self-test complete.
IDT installed.
APIC: init...
PLIC: no DTB PLIC node -- using QEMU virt-machine default (base=0x0c000000)
PLIC: init (S-mode context 1, threshold=0)
APIC: init done
Timer: init...
Timer: RISC-V time CSR @ 10000000 Hz (FALLBACK, no devicetree)
Timer: init done
Kernel heap initialized.
Heap statistics:
Total bytes: 536870872
Free bytes: 536870872
Used bytes: 0
Peak bytes: 0
Heap base addr: 2214588416
Heap end addr: 2751459328
Heartbeat: init...
Heartbeat: init done
Kernel initialization complete.
Boot successful!
StarForth v4: one host node, the F18-derived engine
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x5ff3672509f6c4e5 capsule_hash=0x5ff3672509f6c4e5 words=311
V4: capsule v4:forth79.4th signature: missing (unsigned)
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0xc07eb35ca84c4aa8
PARITY:V4_POST tests=538 pass=538 fail=0
PARITY:V4_SYSTEM word_count=317 dict_hash=0xc0769523a47b7dc3
PARITY:OK
POST: PASSED
[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
PCI: init...
virtio-blk: found device
[HADES][INFO ] [KRELTSC: 17911425] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
Artemis: virtio-blk attached
ok> : SQ DUP * ;
ok
ok> 7 SQ . 3 4 U* . .
49 0 12 ok
ok> T{
UNKNOWN WORD: 'T{'
ERROR
ok> RS1
UNKNOWN WORD: 'RS1'
ERROR
ok> COLD
FORTH-79 Cold Start
System initialized.
ok
ok> 3 4 U* . .
0 12 ok
ok> FORGET U*
Protected word
ERROR
ok> 9 KERNEL-WORD ASK9 ASK9
Argument out of range
ERROR
ok> 1 BLOCK C@ . 2047 BLOCK C@ .
0 0 ok
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
65 ok
ok> 3072 BLOCK C@ .
0 ok
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
Storage refused
ERROR
ok> 9999999 BLOCK
Block out of range
ERROR
ok> 5 7 PORT!
No one on that port
ERROR
ok> 1 2 + .
3 ok
ok> 5 AWAIT
7 8 * .
Interrupted
ERROR
ok> 56 ok
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Stack overflow
ERROR
ok> 3 4 + .
7 ok
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
ok
ok> WORDS
ASK9 U/MOD U* (UD) (NB) (BITS) BYE ACL-WORD-ID ACL-HEAT@ ACL-INIT-PRIMITIVES
ACL-INHERIT ACL-ALLOW! ACL-TTL! ACL-MODE! ACL-PIN ACL-PINNED? ACL-ALLOW@
ACL-TTL@ ACL-MODE@ ACL-HOOK LOG-DEBUG" LOG-TEST" LOG-INFO" LOG-WARN"
LOG-ERROR" (LOG") LOG-DEBUG-STR LOG-TEST-STR LOG-INFFO-STR LOG-WARN-STR
LOG-ERROR-STR LOG-LEVEL! LOG-LEVEL@ LOG-DEBUG LOG-TEST LOG-INFO
LOG-WARN LOG-ERROR THRU --> LOAD LIST EMPTY-BUFFERS FLUSH SAVE-BUFFERS
UPDATE BLOCK BUFFER BLK SCR Q.PRINT Q.COS Q.SIN Q.LOG Q.SQRT Q.EXP
Q./ Q.* Q.MIN Q.MAX Q.0= Q.> Q.< Q.= Q.NEG Q.ABS QQ.- Q.+ Q.SCALE
Q.0 Q.1 Q.TO-INT Q.FROM-INT DEFER@ IS DEFER COLD WARM PAGE VERSION
79-STANDARD FORGET VLIST WORDS ORDER VOCABULARY DEFINITIONS FORTH
CURRENT CONTEXT FENCE SKIP SCAN SEARCH COMPARE -TRAILING BLANK ERASE
FILL MOVE CMOVE> ?TERMINAL DMIN DMAX D< M- M+ D2/ D2* DD= D0< D0=
DABS D- RSHIFT LSHIFT WITHIN MIN MAX ABS U> U< >= <= <> 0> 0<> -!
2! 2@ INVERT FALSE TRUE NOP 2R@ 2R> 2>R 2ROT 2OVER 2SWAP DUMP .S
? D. D.R U. U.R . .R OCTAL HEX DECIMAL SPACES #> #S # SIGN HOLD
<# STATE PAD BASE SPAN >IN TIB BL > < = NOT 0< 0= OR XOR AND M/MOD
*/ */MOD MOD / /MOD M* S>D 2/ 2* 2- 2+ 1- 1+ NEGATE * - + CELLS
+! ! @ UNLOOP LEAVE J I R@ R> >R ROLL PICK DEPTH 2DROP 2DUP ?DUP
-ROT ROT SWAP OVER DROP DUP S" (S") ABORT" (ABORT") ." (.") ABORT
QUIT (WORD-FORGOTTEN) (WORD-DEFINED) (REFUSED) (LOST) (CONSOLE)
(ME) NODE-EERROR (SEAL) PORT! AWAIT SEND-ON SEND NO-ROUTES GONE NO-ROUTE
DEFAULT-ROUTE NEIGHBOUR ROUTE +LOOP LOOP ?DO DO ENDCASE ENDOF OF
CASE REPEAT WHILE AGAIN UNTIL BEGIN THEN ELSE IF DOES> CONSTANT
VARIABLE CREATE [COMPILE] COMPILE IMMEDIATE EXIT KERNEL-WORD ; :
[LITERAL] ['] ' LLITERAL ] [ INTERPRET EXECUTE FIND HIDDEN SMUDGE
TRAVERSE >BODY PFA CFA NAME> NFA >NAME LINK> LFA >LINK LATEST 2,
C, , ALLOT ALIGN HERE \ ( NUMBER CONVERT ENCLOSE WORD QUERY EXPECT
SOURCE TYPE COUNT SPACE CR KEY EMIT CMOVE C! C@ DNEGATE D+ UM*
ok
ok> DEPTH .
26 ok
ok> HERE .
8300 ok
ok> BYE
BYE: cold restart
+2
View File
@@ -204,6 +204,8 @@ hosted-check: $(foreach i,$(HOSTED_ISAS),$(BINDIR)/boot-$(i).txt) $(BINDIR)/star
@grep -q '^ok> Interrupted$$' $(BINDIR)/await-amd64.txt && grep -q '^ok> 3 ok$$' $(BINDIR)/await-amd64.txt && grep -q '^ok> 56 ok$$' $(BINDIR)/await-amd64.txt || { echo "hosted-check: a line typed while the node waited did not break the wait and then run"; tail -6 $(BINDIR)/await-amd64.txt; exit 1; }
@printf '1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29\n1 2 + .\n' | $(BINDIR)/starforth4-amd64 > $(BINDIR)/deep-amd64.txt 2>&1; true
@grep -q '^ok> Stack overflow$$' $(BINDIR)/deep-amd64.txt && grep -q '^ok> 3 ok$$' $(BINDIR)/deep-amd64.txt || { echo "hosted-check: a line that left 29 values on the stack was not an error the node came back from"; tail -4 $(BINDIR)/deep-amd64.txt; exit 1; }
@printf '1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26\nWORDS\nDEPTH .\n' | $(BINDIR)/starforth4-amd64 > $(BINDIR)/deepwords-amd64.txt 2>&1; true
@grep -q 'DUP' $(BINDIR)/deepwords-amd64.txt && grep -q '^ok> 26 ok$$' $(BINDIR)/deepwords-amd64.txt || { echo "hosted-check: WORDS with 26 values on the stack did not print and come back"; tail -4 $(BINDIR)/deepwords-amd64.txt | cut -c1-200; exit 1; }
@$(BINDIR)/starforth4-postfail < /dev/null > $(BINDIR)/postfail.txt; test $$? -ne 0 || { echo "hosted-check: a boot with a failing case did not fail"; exit 1; }
@grep -q '^POST FAIL: fail.wrong out<5 5 $$' $(BINDIR)/postfail.txt || { echo "hosted-check: the failing case was not named"; cat $(BINDIR)/postfail.txt; exit 1; }
@grep -q '^POST FAIL: fail.many out<QQQQ' $(BINDIR)/postfail.txt || { echo "hosted-check: the case that prints too much was not named"; exit 1; }
+2 -11
View File
@@ -141,17 +141,8 @@ sixteenth to pass it on, sends a NACK to whoever waits on it; `(REFUSED)`
counts them. `AWAIT` ends in an error if the answer is not going to come:
"Message refused", "Node gone", or "Interrupted" when a line is typed at
the console. `n KILL` on Hera removes node n and tells every other node it
is gone; a node believes that only from its centre.
**The wait** (`MESH.md` 7c, step 6d). A node does not write the first word
of a message: it offers it, and sleeps until it is taken or a word comes
for it. What a node has to pass on, the answers it has to give and the
refusals it owes all wait with it, offered, and go when they can. So a
node that is stuck holds up no other, within the limits `MESH.md` 7c.4
states: text that must itself send to the stuck node waits, and a node
begins no more text from a sender it cannot yet answer. A review found
this unsound as first built; what it found is mended and listed under
step 6d in `MESH.md` section 10, and has not been reviewed again.
is gone; a node believes that only from its centre. A stuck node can hold
up its neighbours until it is killed: `MESH.md` 7b.7 says how.
**Blocks** (`MESH.md` step 6). The block words are the kernel's, as they
are for a v3 VM: `BLOCK`, `BUFFER`, `UPDATE`, `SAVE-BUFFERS` and
+78 -143
View File
@@ -131,22 +131,25 @@ header CMOVE
NONE: drop drop (ROUTE-DEFAULT) a! @ ;
HIT: drop drop @+ pop drop ;
\ BEGINNING A MESSAGE: THE WAIT (MESH.md section 7c). A write waits until
\ the neighbour reads, and a node that is waiting to write reads nothing:
\ two neighbours that each began to write to the other would wait for ever,
\ and a node that wrote to one that was stuck would be stuck with it. So
\ a node does not write the first word of a message. It offers it, (GATE):
\ the word is stored at the port's offer address, and the node then reads
\ the wait, where it sleeps until the offer is taken or a word comes for
\ it, whichever is first. If a word comes, that message is taken in and
\ kept with the messages waiting, and the node offers again. When the
\ offer is taken the neighbour has the first word and reads to the end:
\ the rest is written, each word waiting for it.
\ (OFFER) is the offer address of port 0, and (WAIT) the wait. (READERS)
\ is the node's look at its neighbours, a bit for each port that has one
\ waiting to read, and above those a bit for each port that has anything
\ on it at all. (NEAR) is the number of the node on each port, told by
\ whoever wires it (NEIGHBOUR, quit.v4): it is how a node knows its centre.
\ LOOKING BEFORE WRITING (MESH.md section 7a). A write waits until the
\ neighbour reads, and a node that is waiting to write reads nothing: two
\ neighbours that each began to write to the other would wait for ever. So
\ a node does not begin a message until it has looked, (GATE):
\ - while any neighbour is waiting to write to it, it takes that message
\ in and keeps it with the messages waiting, to deal with when it has
\ nothing else to do;
\ - then, if the neighbour it means to write to is waiting to read, it
\ writes;
\ - and if that neighbour is not, it writes all the same when the
\ neighbour's number is the higher of the two, and otherwise looks
\ again. Of two neighbours one only may wait to write to the other, and
\ it is the lower: so those that wait are waiting on ever higher
\ numbers, and the highest of them is not waiting to write; it is
\ looking, and takes in what is being written to it.
\ (WRITERS) and (READERS) are the node's two looks at its neighbours, a bit
\ for each port. (NEAR) is the number of the node on each port, told by
\ whoever wires it (NEIGHBOUR, quit.v4); a port not told of -- a device's
\ -- is written to only when what is there is waiting to read.
\
\ THE MESSAGES WAITING are kept in (MQ) .. (MQ-END), one after another,
\ going round: for each its seven words, the port it came on, and its text.
@@ -159,6 +162,12 @@ header CMOVE
if Z -1 + FOR 2/ NEXT 1 and ;
Z: drop 1 and ;
\ ( mask -- index ) the lowest port in it; the mask is not zero
: (LOW)
0 push
L: dup 1 and if UP drop drop pop ;
UP: drop 2/ pop 1 + push jump L
\ ( w -- ) one more cell of the messages waiting. B is kept.
: (MQ!)
(MQ-TAIL) a! @ a! !+
@@ -182,7 +191,7 @@ header CMOVE
\ messages in so as to be free to write, where it cannot begin a message
\ of its own: so it notes what it owes, (OWE), in the eight pairs at
\ (OWED) -- whom it is owed to, and the node the message was for -- and
\ sends it when it next has nothing else to do, (PAY-SET). With eight owed
\ sends it when it next has nothing else to do, (PAY). With eight owed
\ already, one more is let go and counted. Nothing is owed for a NACK or
\ for a GONE, type 5: there is no NACK for a NACK. The last 36 cells of
\ the messages waiting are kept for those two types, so that ordinary
@@ -239,76 +248,37 @@ header CMOVE
KEPT-FOR: drop -MQ-ROOM +
ROOM: -if FULL
drop push
(MQ-TAIL) a! @ (T-TAIL) a! ! (MQ#) a! @ (T-COUNT) a! ! 1 (TAKING) a! ! \ until all of it is here: (MQ-MEND)
(MQ-HDR) a! @ (MQ!) (MQ-HDR)+1 a! @ (MQ!) (MQ-HDR)+2 a! @ (MQ!) (MQ-HDR)+3 a! @ (MQ!)
(MQ-HDR)+4 a! @ (MQ!) (MQ-HDR)+5 a! @ (MQ!) (MQ-HDR)+6 a! @ (MQ!) (MQ-HDR)+7 a! @ (MQ!)
pop if NOTEXT -1 + FOR @b (MQ!) NEXT 0 (TAKING) a! ! ;
NOTEXT: drop 0 (TAKING) a! ! ;
pop if NOTEXT -1 + FOR @b (MQ!) NEXT ;
NOTEXT: drop ;
FULL: drop if READ -1 + FOR @b drop UNEXT jump LOSE
READ: drop
LOSE: (LOST) a! @ 1 + !
(MQ-HDR)+2 a! @ push 0 (MQ-HDR)+2 a! ! \ its type is taken out of (MQ-HDR): it is not here
(MQ-HDR)+1 a! @ (MQ-HDR) a! @ pop jump (TELL-OF)
\ ( -- ) A MESSAGE HALF TAKEN IN IS LET GO. Whoever was writing it was
\ removed, and the error that told this node so ended the taking in: what
\ had come is with the messages waiting, and would be taken for a whole
\ message. They are put back as they were before it, and it is counted.
: (MQ-MEND)
(TAKING) a! @ if WHOLE
drop (T-TAIL) a! @ (MQ-TAIL) a! ! (T-COUNT) a! @ (MQ#) a! ! 0 (TAKING) a! !
(LOST) a! @ 1 + ! ;
WHOLE: drop ;
(MQ-HDR)+1 a! @ (MQ-HDR) a! @ (MQ-HDR)+2 a! @ jump (TELL-OF)
\ ( port to -- ) take in a message and keep it
: (TAKE) (TAKE-HDR) jump (TAKE-KEEP)
\ ( -- how ) THE WAIT, once. Whatever has been offered stands until
\ this returns. 0: a word came for this node instead; that whole message
\ has been taken in and kept, (TAKE), and its seven words are still in
\ (MQ-HDR). -1: an offer was taken; B is at the port it was taken on, and
\ (GATE-PORT) holds that port's address. 1: there is nothing on a port
\ that had an offer: no one will ever take it.
: (OFFERS)
(WAIT) b! @b push (PORT)+9 b! @b \ ( which ) R: the word
dup -PORTS + -if TAKEN
drop (PORT) + dup b! pop (TAKE) 0 ; \ a word, from that port: the message's first
TAKEN: dup -PORTS + -if NOONE \ ( which k x ) k: the port, by its number
drop (PORT) + (GATE-PORT) a! ! drop pop drop
(GATE-PORT) a! @ b! -1 ;
NOONE: drop drop drop pop drop 1 ;
\ ( w port -- ) BEGIN A MESSAGE: the word, its first, is offered on the
\ port, by its address, until it is taken; whatever is written to this
\ node meanwhile is taken in. B is left at the port. The two are kept in
\ (GATE-WORD) and (GATE-PORT), not on the stack: (GATE1), which is this
\ with the word already in (GATE-WORD), is run with the stack as full as
\ EMIT may be.
\ TEXT FROM THE CONSOLE for this node, coming while text it is doing waits
\ here, ends that text in error 21, Interrupted (MESH.md 7c.3); it is with
\ the messages waiting and is done next. It is how a node whose text must
\ begin a message to a node that is stuck is typed to. Not while (QUIET)
\ is set: what waits then is no text's doing.
\ With nothing on the port it is error 18.
: (GATE1) ( port -- )
\ ( port -- ) wait until a message may be begun on the port, by its
\ address, taking in whatever is being written to this node meanwhile; and
\ leave B at the port. The port is kept in (GATE-PORT), not on the stack:
\ this is run with the stack as full as EMIT may be.
: (GATE)
(GATE-PORT) a! !
L: 0 (WAIT) a! ! \ nothing else is offered
(GATE-WORD) a! @ (GATE-PORT) a! @ -PORTS - 4 + a! ! \ the offer
(OFFERS) if CAME -if NOONE drop ;
NOONE: drop NODE-ERROR b! 18 !b ; \ no one on that port
CAME: drop
(QUIET) a! @ if TEXTS drop jump L
TEXTS: drop
(MQ-HDR) a! @ (ME) a! @ xor if MINE drop jump L
MINE: drop
(MQ-HDR)+2 a! @ -1 + if TEXT drop jump L
TEXT: drop
(CONSOLE) a! @ if C0 jump C1
C0: drop (MSG)+1 a! @ \ no console told of: whoever sent the text being done
C1: (MQ-HDR)+1 a! @ xor if BREAK drop jump L
BREAK: drop NODE-ERROR b! 21 !b ;
: (GATE) ( w port -- ) push (GATE-WORD) a! ! pop jump (GATE1)
L: (WRITERS) b! @b if QUIET
(LOW) (PORT) + dup b! @b (TAKE) jump L
QUIET: drop
(GATE-PORT) a! @ (PORT) - (NEAR) + a! @ if ASK
(ME) a! @ - -if GO jump ASK \ its number less this node's
GO: drop (GATE-PORT) a! @ b! ; \ the neighbour's is the higher
ASK: drop
(READERS) b! @b (GATE-PORT) a! @ (PORT) - (BIT) if NOTYET
drop (GATE-PORT) a! @ b! ; \ it is waiting to read
NOTYET: drop
(READERS) b! @b (GATE-PORT) a! @ (PORT) - -PORTS - (BIT) if NOONE \ is there anything on the port at all?
drop jump L
NOONE: drop NODE-ERROR b! 18 !b ; \ no: what waits here would wait for ever
\ ( type -- ) words 1 to 5 of a message this node sends: from, type, and
\ the three that are carried and not used yet. Its caller has put B at the
@@ -319,30 +289,14 @@ header CMOVE
!b \ type
16 !b 0 !b 0 !b ; \ how many nodes may pass it on (the TTL: MESH.md 7b); ACL tag; sequence
\ PAYING WHAT IS OWED (MESH.md 7c.3). Every refusal owed is offered at
\ once, each on the port it goes by, one to a port, with whatever the node
\ has to pass on (quit.v4, (IDLE)), and the node sleeps in the wait: so
\ one owed to a node that is stuck does not keep back one owed to a node
\ that waits for it, and the node takes in whatever is written to it
\ meanwhile. One there is no way for, or whose way is a
\ port with nothing on it, is let go and counted first. (PAY-K) has a
\ cell for each port: which refusal is offered there, counted from 1; 0,
\ none; -1, a message being passed on.
\ ( w -- ) WRITE A WORD OF A MESSAGE AFTER ITS FIRST, on the port
\ (GATE-PORT) holds, from where no error must come: the passes over the
\ messages waiting (quit.v4). It is offered, and the node waits for that
\ offer only: the neighbour has the first word and is reading the rest.
\ If there turns out to be nothing on the port -- the neighbour was removed
\ -- (W-GONE) is set, and this word and every one after it is let go
\ without waiting. Whoever begins the writing clears (W-GONE) first.
: (!W)
(W-GONE) a! @ if THERE drop drop ;
THERE: drop
(GATE-PORT) a! @ -PORTS - 4 + a! !
(WAIT1) b! @b drop (PORT)+9 b! @b
-PORTS + -PORTS + -if NOONE drop ;
NOONE: drop 1 (W-GONE) a! ! ;
\ PAYING WHAT IS OWED does not hold this node up. A refusal is sent if the
\ neighbour it goes by is reading at this moment. If it is not, and its
\ number is the higher, the refusal is written all the same, as any message
\ would be (MESH.md 7a): that neighbour takes it in when it next looks. If
\ its number is the lower, or it has not been told of, the refusal stays
\ owed, and this node goes on with what it has to do and tries again; it
\ does not sleep while it owes one (quit.v4, (IDLE)). One there is no way
\ for, or whose way is a port with nothing on it, is let go and counted.
\ ( k -- ) the k-th refusal owed is done with: the last takes its place
: (PAID)
@@ -350,55 +304,36 @@ header CMOVE
2* (OWED) + a! @+ @
push push 2* (OWED) + a! pop !+ pop ! ;
\ ( k -- ) the k-th refusal owed is let go if it cannot be sent at all
: (PAY-DROP)
dup 2* (OWED) + a! @ \ k to
(PORT-FOR) if NOWAY \ k port
(THERE) if GONE drop drop ;
GONE:
NOWAY: drop (LOST) a! @ 1 + ! jump (PAID)
\ ( k -- ) send the k-th refusal owed, if it can go now
: (PAY1)
dup 2* (OWED) + a! @+ @ \ k to about
over (PORT-FOR) if NOWAY \ k to about port
dup (THERE) if GONE
drop (READERS) b! @b over (PORT) - (BIT) if BUSY
drop
WRITE: b! \ k to about B: the port
SWAP !b 4 (HDR) 4 !b !b \ k to; from, type 4; four characters; about
jump (PAID)
BUSY: drop \ it is not reading:
dup (PORT) - (NEAR) + a! @ if LEAVE \ k to about port near
(ME) a! @ - -if BLIND jump LEAVE \ its number less this node's
BLIND: drop jump WRITE \ the higher: it will take it in
LEAVE: drop drop drop drop drop ;
GONE: drop
NOWAY: drop drop drop (LOST) a! @ 1 + ! jump (PAID)
\ ( k -- ) offer the k-th refusal owed on its port, if nothing is there yet
: (PAY-OFFER)
dup push 2* (OWED) + a! @ \ to R: k (the stack is kept shallow: this runs between lines)
dup (PORT-FOR) (PORT) - \ to n the port, by its number
dup (PAY-K) + a! @ if FREE
drop drop drop pop drop ;
FREE: drop
dup push (OFFER) + a! ! \ the message's first word, whom it is to
pop pop 1 + SWAP (PAY-K) + a! ! ;
\ ( -- ) let go every refusal owed that cannot be sent; no port has an
\ offer; then offer each that is left on its port
: (PAY-SET)
0 (WAIT) a! ! \ what was offered before is not: (IDLE) may have gone round without waiting
(OWED#) a! @ if NONE
-1 + FOR pop dup push (PAY-DROP) NEXT
jump CLEAR
NONE: drop
CLEAR: (PAY-K) a! PORTS-1 FOR 0 !+ NEXT
(OWED#) a! @ if NONE2
-1 + FOR pop dup push (PAY-OFFER) NEXT ;
NONE2: drop ;
\ ( k -- ) the offer of the k-th refusal owed was taken, and B is at the
\ port: its other words are written, and it is done with
: (PAY-GO)
dup (A-WORD) a! ! \ k is kept off the stack: nothing is left there if the port goes
2* (OWED) + 1 + a! @ push \ R: about
0 (W-GONE) a! !
(ME) a! @ (!W) 4 (!W) 16 (!W) 0 (!W) 0 (!W) \ from, type 4, and the three carried
4 (!W) pop (!W) \ four characters; about
(W-GONE) a! @ if SENT drop (LOST) a! @ 1 + ! jump DONE
SENT: drop
DONE: (A-WORD) a! @ jump (PAID)
\ ( -- ) send every refusal this node owes that can go now
: (PAY)
(OWED#) a! @ if NONE -1 +
FOR pop dup push (PAY1) NEXT ;
NONE: drop ;
\ ( -- ) send what has been printed, to where (PRINT-TO) says, by the port
\ (PRINT-PORT): its length, then its characters, four to a word, the first
\ lowest. quit.v4 sets those two when text for this node arrives.
: (FLUSH-OUT)
(OUT^) a! @ (OUT) xor if NONE drop
(PRINT-TO) a! @ (GATE-WORD) a! ! (PRINT-PORT) a! @ (GATE1)
(PRINT-PORT) a! @ (GATE) (PRINT-TO) a! @ !b
2 (HDR)
(OUT^) a! @ (OUT) - dup !b
3 + 2/ 2/ -1 + (OUT) a!
+93 -256
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -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);
+5
View File
@@ -65,6 +65,11 @@ typedef struct {
* port". */
#define V4_ERROR_NO_ONE 18
/* The nucleus's error for a node let go of a message it was blocked on the
* first word of, because its console has a line for it (fabric.h,
* v4_fabric_interrupt_error): "Interrupted". */
#define V4_ERROR_INTERRUPTED 21
/* The description of the nucleus built from v4/capsule (the generated file
* defines it). */
extern const v4_image v4_capsule_image;
+8 -50
View File
@@ -43,9 +43,6 @@
#define V4_PORTS 8u
#endif
#define V4_PORT_ANY V4_PORTS /* not a port: "whichever port has a neighbour writing" */
#define V4_PORT_OFFER (V4_PORTS + 4u) /* not a port: the offer on port 0; the offer on port k is k further (the wait, below) */
#define V4_PORT_WAIT (2u * V4_PORTS + 4u) /* not a port: the wait */
#define V4_PORT_WAIT1 (2u * V4_PORTS + 5u) /* not a port: the wait for an offer only */
#ifndef V4_NODE_WORDS
#define V4_NODE_WORDS 1024u
@@ -114,10 +111,7 @@ typedef struct {
unsigned last_from; /* the port the last read from "any" took its word from */
unsigned writers; /* bit k: the neighbour on port k is blocked writing to this node */
unsigned readers; /* bit k: the neighbour on port k is blocked reading from this node */
unsigned offers; /* bit k: a word is offered on port k (the wait: docs/v4.0.0/MESH.md 7c) */
v4_cell offer[V4_PORTS]; /* the words offered */
int waiting; /* non-zero: the read it is blocked in is the wait */
int offer_asked; /* non-zero: `asking` stands for the offer on `ask_port` (v4_node_offer_as_write) */
unsigned since_look; /* words taken from this node since it last fetched either of those */
/* DSTACK-DEPTH and RSTACK-DEPTH (D-16). See v4_node_stack_regs_attach. */
v4_cell dstack_reg; /* its word address, or -1 */
@@ -225,6 +219,13 @@ void v4_node_port_attach(v4_node *n, v4_cell base);
/* The neighbour has taken what the node wrote. */
void v4_node_port_served(v4_node *n);
/* How many words the node has had taken since it last looked at its
* neighbours (a fetch of either of the two status addresses). A node that
* looks before it begins a message (MESH.md 7a) and is blocked writing with
* this at 0 is blocked on the first word of the message: nothing of it has
* gone. */
unsigned v4_node_words_since_look(const v4_node *n);
/* THERE IS NO ONE ON THE PORT the node is blocked at -- writing to it, or
* reading from that one port -- and there will not be. A node that can
* take an error (it has an error register and a fault table: node.h,
@@ -235,49 +236,6 @@ void v4_node_port_served(v4_node *n);
* that is not blocked at a port, or is reading "any port". */
int v4_node_port_gone(v4_node *n, v4_cell code);
/* THE WAIT (docs/v4.0.0/MESH.md 7c). After the addresses above come
* base + V4_PORT_OFFER + k the offer on port k: a store here is the word
* the node offers to write to that port. It is
* remembered and waits for nothing.
* base + V4_PORT_WAIT the wait: a fetch here blocks the node until a
* word comes for it on any port, or one of its
* offers is taken, whichever is first.
* base + V4_PORT_WAIT1 the wait for an offer only: as the wait, but
* a word that comes for the node does not end
* it, and a writer to the node is not served.
* It is how the words of a message after the
* first are written by a node that must not be
* faulted if its reader is removed.
* A store to the wait withdraws every offer and waits for nothing.
* A word that came is fetched as from "any port", and "which port" says
* where from. When an offer was taken the fetch gives 0 and "which port"
* gives V4_PORTS + k: the offer on port k. When there turns out to be
* nothing on a port it has an offer on, the fetch gives 0 and "which
* port" gives 2 * V4_PORTS + k. Whichever, every offer the node had is
* withdrawn. Whoever connects the ports does the handing over
* (fabric.h); a node in the wait is blocked reading "any port" as far as a
* writer to it can tell.
*
* 1 if the node is blocked in the wait with nothing given yet. */
int v4_node_in_wait(const v4_node *n);
/* The offer on port k has been taken: the node is unblocked and will fetch
* 0, from V4_PORTS + k. 0 if it is not in the wait or has no offer there. */
int v4_node_offer_taken(v4_node *n, unsigned k);
/* There is nothing on port k, where the node has an offer: a node in the
* wait that can take an error (v4_node_port_gone says which can) is
* unblocked and will fetch 0, from 2 * V4_PORTS + k. No error is raised:
* what the node does about it is its own to say. 0 if it was not done. */
int v4_node_offer_gone(v4_node *n, unsigned k);
/* For a host with one node, whose devices always take what is written: if
* the node is in the wait with an offer, it becomes a blocked write of the
* word offered on the lowest such port -- asking, request, ask_port -- and
* v4_node_port_served then completes the wait as v4_node_offer_taken would.
* 1 if it did. */
int v4_node_offer_as_write(v4_node *n);
/* The neighbour on `port` writes `value`: the node, blocked reading that
* port or "any", is unblocked and will fetch it. */
void v4_node_port_give(v4_node *n, unsigned port, v4_cell value);
+21 -37
View File
@@ -18,6 +18,7 @@ void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity)
f->place = places;
f->capacity = capacity;
f->gone_error = 0;
f->interrupt_error = 0;
for (i = 0; i < capacity; i++) clear_place(&places[i]);
}
@@ -26,6 +27,10 @@ void v4_fabric_gone_error(v4_fabric *f, v4_cell code)
f->gone_error = code;
}
void v4_fabric_interrupt_error(v4_fabric *f, v4_cell code)
{
f->interrupt_error = code;
}
void v4_fabric_grow(v4_fabric *f, v4_place *places, unsigned capacity)
{
@@ -147,15 +152,11 @@ static unsigned hand_over_write(v4_fabric *f, unsigned a)
static unsigned hand_over_read(v4_fabric *f, unsigned a)
{
v4_node *n = &f->place[a].node->n;
unsigned first;
unsigned last;
unsigned first = n->read_port == V4_PORT_ANY ? 0 : n->read_port;
unsigned last = n->read_port == V4_PORT_ANY ? V4_PORTS - 1 : n->read_port;
unsigned k;
v4_cell value;
if (n->read_port > V4_PORT_ANY) return 0; /* the wait for an offer only: it reads no port */
first = n->read_port == V4_PORT_ANY ? 0 : n->read_port;
last = n->read_port == V4_PORT_ANY ? V4_PORTS - 1 : n->read_port;
for (k = first; k <= last; k++) {
const v4_wire *w = &f->place[a].wire[k];
if (w->kind == V4_WIRE_DEVICE && w->device->give && w->device->give(w->device->self, &value)) {
@@ -208,35 +209,23 @@ unsigned v4_fabric_step(v4_fabric *f)
for (i = 0; i < f->capacity; i++)
if (awake(f, i) && f->place[i].node->n.reading && !f->place[i].node->n.given) done += hand_over_read(f, i);
/* THE WAIT (docs/v4.0.0/MESH.md 7c). A node in the wait that was given a
* word above is no longer in it: a word for a node comes before its own
* offer. Of the rest, each offer is taken by whoever is ready for it: a
* device that takes, or a node blocked reading that port or any -- which
* a node in the wait is, so of two that offer to each other the one at
* the lower place writes and the other's offers are withdrawn. */
for (i = 0; i < f->capacity; i++) {
v4_node *n;
unsigned k;
if (!awake(f, i)) continue;
n = &f->place[i].node->n;
if (!v4_node_in_wait(n)) continue;
for (k = 0; k < V4_PORTS; k++) {
const v4_wire *w = &f->place[i].wire[k];
if (!(n->offers & (1u << k))) continue;
if (w->kind == V4_WIRE_DEVICE) {
if (w->device->take && w->device->take(w->device->self, n->offer[k])) { (void)v4_node_offer_taken(n, k); done++; break; }
} else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) {
v4_node *r = &f->place[w->node].node->n;
if (r->reading && !r->given && (r->read_port == w->port || r->read_port == V4_PORT_ANY)) {
v4_cell word = n->offer[k];
(void)v4_node_offer_taken(n, k);
v4_node_port_give(r, w->port, word);
done++;
/* a node blocked on the first word of a message to a node that is not
* reading, while a device of its own has something for it */
if (f->interrupt_error != 0)
for (i = 0; i < f->capacity; i++) {
v4_node *n;
unsigned k;
if (!awake(f, i)) continue;
n = &f->place[i].node->n;
if (!n->asking || f->place[i].wire[n->ask_port].kind != V4_WIRE_NODE || v4_node_words_since_look(n) != 0) continue;
for (k = 0; k < V4_PORTS; k++) {
const v4_wire *w = &f->place[i].wire[k];
if (w->kind == V4_WIRE_DEVICE && w->device->pending && w->device->pending(w->device->self)) {
done += (unsigned)v4_node_port_gone(n, f->interrupt_error);
break;
}
}
}
}
/* a node blocked at one port that nothing is wired to */
if (f->gone_error != 0)
@@ -245,13 +234,8 @@ unsigned v4_fabric_step(v4_fabric *f)
if (!awake(f, i)) continue;
n = &f->place[i].node->n;
if ((n->asking && f->place[i].wire[n->ask_port].kind == V4_WIRE_NONE)
|| (n->reading && !n->given && n->read_port < V4_PORTS && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE))
|| (n->reading && !n->given && n->read_port != V4_PORT_ANY && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE))
done += (unsigned)v4_node_port_gone(n, f->gone_error);
else if (v4_node_in_wait(n)) { /* an offer on a port with nothing on it */
unsigned k;
for (k = 0; k < V4_PORTS; k++)
if ((n->offers & (1u << k)) && f->place[i].wire[k].kind == V4_WIRE_NONE) { done += (unsigned)v4_node_offer_gone(n, k); break; }
}
}
return done;
+14 -70
View File
@@ -93,14 +93,7 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value)
n->asking = 1;
return;
}
if (port >= (int)V4_PORT_OFFER && port < (int)V4_PORT_WAIT) { /* an offer: remembered until the wait */
unsigned k = (unsigned)port - V4_PORT_OFFER;
n->offer[k] = value;
n->offers |= 1u << k;
return;
}
if (port == (int)V4_PORT_WAIT) { n->offers = 0; return; } /* a store to the wait: every offer is withdrawn */
if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any", "which port" and the rest are not written to */
if (port >= 0) { v4_node_fault(n, V4_FAULT_ADDRESS, addr); return; } /* "any" and "which port" are not written to */
}
/* The stack registers: a store empties the stack. -1 when not attached. */
if (addr == n->dstack_reg) { v4_dstack_clear(&n->ds); return; }
@@ -127,16 +120,8 @@ v4_cell v4_node_fetch(v4_node *n, v4_cell addr)
{
int port = v4_node_port_index(n, addr);
if (port == (int)V4_PORTS + 1) return (v4_cell)n->last_from;
if (port == (int)V4_PORTS + 2) return (v4_cell)n->writers;
if (port == (int)V4_PORTS + 3) return (v4_cell)n->readers;
if (port == (int)V4_PORT_WAIT || port == (int)V4_PORT_WAIT1) {
/* the wait: the word that came, or 0 for an offer taken */
if (!n->given) return 0;
n->given = 0;
n->last_from = n->given_port;
return n->given_value;
}
if (port >= (int)V4_PORT_OFFER) return 0; /* an offer is not read back */
if (port == (int)V4_PORTS + 2) { n->since_look = 0; return (v4_cell)n->writers; }
if (port == (int)V4_PORTS + 3) { n->since_look = 0; return (v4_cell)n->readers; }
if (port >= 0) {
/* the word its neighbour wrote; v4_node_read_ready has said it is there */
if (!n->given) return 0;
@@ -194,9 +179,7 @@ void v4_node_port_attach(v4_node *n, v4_cell base)
n->last_from = 0;
n->writers = 0;
n->readers = 0;
n->offers = 0;
n->waiting = 0;
n->offer_asked = 0;
n->since_look = 0;
}
void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
@@ -207,19 +190,17 @@ void v4_node_port_status(v4_node *n, unsigned writers, unsigned readers)
int v4_node_port_index(const v4_node *n, v4_cell addr)
{
if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORT_WAIT1) return -1;
if (n->port < 0 || addr < n->port || addr > n->port + (v4_cell)V4_PORTS + 3) return -1;
return (int)(addr - n->port);
}
int v4_node_read_ready(v4_node *n, v4_cell addr)
{
int port = v4_node_port_index(n, addr);
if (port < 0 || (port > (int)V4_PORT_ANY && port != (int)V4_PORT_WAIT && port != (int)V4_PORT_WAIT1)) return 1;
if (n->given && (port >= (int)V4_PORT_ANY || (unsigned)port == n->given_port)) return 1;
if (port < 0 || port > (int)V4_PORT_ANY) return 1;
if (n->given && (port == (int)V4_PORT_ANY || (unsigned)port == n->given_port)) return 1;
n->reading = 1;
n->waiting = port >= (int)V4_PORT_WAIT; /* the wait is a read of any port, with offers standing; */
n->read_port = port == (int)V4_PORT_WAIT ? V4_PORT_ANY /* the wait for an offer only reads no port at all */
: (unsigned)port;
n->read_port = (unsigned)port;
return 0;
}
@@ -229,39 +210,6 @@ void v4_node_port_give(v4_node *n, unsigned port, v4_cell value)
n->given_value = value;
n->given_port = port;
n->reading = 0;
if (n->waiting) { n->waiting = 0; n->offers = 0; } /* the wait is over: every offer is withdrawn */
}
int v4_node_in_wait(const v4_node *n)
{
return n->waiting && n->reading && !n->given;
}
int v4_node_offer_taken(v4_node *n, unsigned k)
{
if (!v4_node_in_wait(n) || k >= V4_PORTS || !(n->offers & (1u << k))) return 0;
v4_node_port_give(n, V4_PORTS + k, 0);
return 1;
}
int v4_node_offer_gone(v4_node *n, unsigned k)
{
if (n->error_reg < 0 || n->fault_vector < 0 || n->stopped) return 0;
if (!v4_node_in_wait(n) || k >= V4_PORTS || !(n->offers & (1u << k))) return 0;
v4_node_port_give(n, 2u * V4_PORTS + k, 0);
return 1;
}
int v4_node_offer_as_write(v4_node *n)
{
unsigned k;
if (!v4_node_in_wait(n) || n->offers == 0 || n->asking) return 0;
for (k = 0; !(n->offers & (1u << k)); k++) { }
n->request = n->offer[k];
n->ask_port = k;
n->asking = 1;
n->offer_asked = 1;
return 1;
}
void v4_node_born(v4_node *n, v4_cell base)
@@ -274,23 +222,19 @@ void v4_node_born(v4_node *n, v4_cell base)
void v4_node_port_served(v4_node *n)
{
n->asking = 0;
if (n->offer_asked) { /* what was served was an offer: the wait is over */
n->offer_asked = 0;
v4_node_port_give(n, V4_PORTS + n->ask_port, 0);
}
n->since_look++;
}
unsigned v4_node_words_since_look(const v4_node *n)
{
return n->since_look;
}
int v4_node_port_gone(v4_node *n, v4_cell code)
{
if (code == 0 || n->error_reg < 0 || n->fault_vector < 0 || n->stopped) return 0;
if (n->asking && n->offer_asked) { /* what it wrote was an offer: it is told, not faulted */
n->asking = 0; n->offer_asked = 0;
v4_node_port_give(n, 2u * V4_PORTS + n->ask_port, 0);
return 1;
}
if (n->asking) n->asking = 0;
else if (n->reading && !n->given && n->read_port < V4_PORTS) n->reading = 0;
else if (n->reading && !n->given && n->read_port != V4_PORT_ANY) n->reading = 0;
else return 0;
v4_node_store(n, n->error_reg, code); /* raises it: P is the node's handler now */
return 1;
-1
View File
@@ -107,7 +107,6 @@ static int run_line(const v4_boot *b, const char *text, unsigned len, char *keep
for (;;) {
(void)v4_exec_step_word(n, b->es, b->h);
if (n->stopped) return V4_BOOT_LINE_STOPPED;
(void)v4_node_offer_as_write(n); /* in the wait (MESH.md 7c): what it offers is a write, here where whatever is on a port takes at once */
if (n->asking && n->ask_port == KERNEL_PORT) { /* a request: the kernel's turn */
if (v4_blocks_serve(&boot_blocks, n, n->request)) { /* a block, as for any node */
+1 -19
View File
@@ -73,7 +73,7 @@
#define OWED (BUF0_W - 112) /* those: for each, the node it is owed to and the node the refused message was for */
#define ACL_HOOK (BUF0_W - 6) /* the xt of the access control recheck word, or 0 */
#define LINE_STATUS (BUF0_W - 9) /* how the last line ended: 0 QUIT, 1 completed, 2 an error */
#define PORT (BUF0_W - 144) /* the node's ports (node.h): V4_PORTS of them, then "any port", "which port", who writes, who reads, an offer for each port, and the two waits: 2 * V4_PORTS + 6 cells, between the output buffer and A_FOUND. Port 0 is where its requests go. */
#define PORT (BUF0_W - 32) /* the node's ports (node.h): V4_PORTS of them, then "any port" and "which port". Port 0 is where its requests go. */
#define CONSOLE (BUF0_W - 16) /* the node what this one prints is sent to; 0: whoever sent the text being served */
#define ROUTE_COUNT (BUF0_W - 17) /* how many entries the table of ways holds */
#define ROUTE_DEFAULT (BUF0_W - 18) /* the port address for a node not in the table, or 0: there is none */
@@ -87,12 +87,6 @@
#define MQ_TAIL (BUF0_W - 36) /* where the next will go, */
#define MQ_COUNT (BUF0_W - 37) /* and how many cells they take */
#define AWAIT_FROM (BUF0_W - 39) /* the node whose word of how text ended is waited for */
#define W_GONE (BUF0_W - 122) /* not 0: the port a message was being written to has nothing on it; its other words are let go */
#define TAKING (BUF0_W - 121) /* not 0: a message is being taken in, and is only partly with the messages waiting; */
#define T_TAIL (BUF0_W - 120) /* where they ended before it, */
#define T_COUNT (BUF0_W - 119) /* and how many cells they took */
#define GATE_WORD (BUF0_W - 32) /* the first word of a message that is being offered */
#define PAY_K (BUF0_W - 31) /* for each port, which refusal owed is offered there, counted from 1; 0: none. V4_PORTS cells */
#define GATE_PORT (BUF0_W - 38) /* the port address a message is about to be begun on */
#define NEAR (BUF0_W - 64) /* for each port, the number of the node on the other end; 0: not told */
#define MQ_HDR (BUF0_W - 56) /* a message being taken in: its seven words and the port it came on */
@@ -130,8 +124,6 @@ typedef char host_map_ports_fit[(V4_PORTS <= 8u) ? 1 : -1];
#endif
/* the messages waiting are above the dictionary */
typedef char host_map_queue_fits[(MQ_W >= DICT_END_W) ? 1 : -1];
/* the ports, with the offers and the wait after them, end below the cells that follow */
typedef char host_map_port_block_fits[(PORT + 2 * (v4_cell)V4_PORTS + 6 <= W_GONE && PAY_K + (v4_cell)V4_PORTS <= PRINT_TO) ? 1 : -1];
/* How many slots, from slot 0, a branch may sit in on a node this size: those
* whose address field reaches every word of it. */
@@ -204,16 +196,6 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
v4_text_constant(tx, "-PORTS", -(v4_cell)V4_PORTS);
v4_text_constant(tx, "(NEAR)", NEAR);
v4_text_constant(tx, "(GATE-PORT)", GATE_PORT);
v4_text_constant(tx, "(GATE-WORD)", GATE_WORD);
v4_text_constant(tx, "(PAY-K)", PAY_K);
v4_text_constant(tx, "PORTS-1", (v4_cell)V4_PORTS - 1);
v4_text_constant(tx, "(OFFER)", PORT + (v4_cell)V4_PORT_OFFER);
v4_text_constant(tx, "(WAIT)", PORT + (v4_cell)V4_PORT_WAIT);
v4_text_constant(tx, "(WAIT1)", PORT + (v4_cell)V4_PORT_WAIT1);
v4_text_constant(tx, "(W-GONE)", W_GONE);
v4_text_constant(tx, "(TAKING)", TAKING);
v4_text_constant(tx, "(T-TAIL)", T_TAIL);
v4_text_constant(tx, "(T-COUNT)", T_COUNT);
v4_text_constant(tx, "(AWAIT-FROM)", AWAIT_FROM);
v4_text_constant(tx, "(MQ)", MQ_W);
v4_text_constant(tx, "(MQ-END)", MQ_W + MQ_CELLS);
+52 -174
View File
@@ -15,7 +15,7 @@ static int failures = 0, checks = 0;
#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0)
/* The memory map is open (D-4); the test chooses where the ports are. */
#define PB ((v4_cell)(V4_NODE_WORDS - 32u)) /* port 0: the ports and what follows them end below the top of memory */
#define PB ((v4_cell)(V4_NODE_WORDS - 16u)) /* port 0 */
#define ANY (PB + (v4_cell)V4_PORT_ANY)
#define ORG ((v4_cell)16) /* where a test's programme starts */
#define OUT ((v4_cell)900) /* where it leaves what it found */
@@ -45,36 +45,9 @@ static unsigned loaded(unsigned k)
/* the programme ends by waiting at a port nothing is wired to */
static void wait_for_ever(void) { LIT(PB + 7); O(BANG_B); O(FETCH_B); (void)v4_asm_label(&as); } /* and the last word is written out */
/* ---- the wait (MESH.md 7c) ---- */
static void fresh(void)
{
unsigned k;
v4_fabric_init(&f, places, 4);
for (k = 0; k < 4; k++) v4_node_reset(&pool[k].n);
}
/* offer `word` on port k */
static void offer(unsigned k, v4_cell word) { LIT(word); LIT(PB + (v4_cell)V4_PORT_OFFER + (v4_cell)k); O(BANG_A); O(STORE_A); }
/* the wait: what it gives goes to `at`, and which port to `at + 1` */
static void wait_into(v4_cell at)
{
LIT(PB + (v4_cell)V4_PORT_WAIT); O(BANG_B); O(FETCH_B); LIT(at); O(BANG_A); O(STORE_A);
LIT(PB + (v4_cell)V4_PORTS + 1); O(BANG_B); O(FETCH_B); LIT(at + 1); O(BANG_A); O(STORE_A);
}
/* the wait for an offer only */
static void wait1_into(v4_cell at)
{
LIT(PB + (v4_cell)V4_PORT_WAIT1); O(BANG_B); O(FETCH_B); LIT(at); O(BANG_A); O(STORE_A);
LIT(PB + (v4_cell)V4_PORTS + 1); O(BANG_B); O(FETCH_B); LIT(at + 1); O(BANG_A); O(STORE_A);
}
static void done_mark(void) { LIT(1); LIT(OUT + 2); O(BANG_A); O(STORE_A); }
static void count_for_ever(void)
{
v4_cell again;
LIT(OUT); O(BANG_A);
again = v4_asm_label(&as);
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
v4_asm_branch(&as, V4_OP_JUMP, again);
}
/* a device that says whether it has something pending */
static int *dev_has;
static int device_has(void *self) { (void)self; return *dev_has; }
/* Let time pass until nothing more can happen, or `max` steps. */
static unsigned settle(unsigned max)
@@ -394,155 +367,60 @@ int main(void)
CHECK(ND(a)->n.asking && ND(a)->n.request == 5 && ND(c)->n.reading && ND(c)->n.mem[OUT] == 0, "looking takes nothing and gives nothing: both wait as they did");
CHECK(!ND(b)->n.stopped && ND(b)->n.reading, "and the one that looked did not wait to look");
/* ---- the wait (MESH.md 7c): a node offers a word and sleeps ---- */
/* ---- a node blocked on the first word of a message, while a device of its own has something for it ---- */
{
static int has; /* what the device says it has pending */
static stream none;
static v4_device sink;
v4_uheat_t clock;
sink.take = stream_take; sink.give = 0; sink.self = &none;
/* taken by a reader */
fresh();
a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever();
b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to one that reads");
(void)settle(300);
CHECK(ND(b)->n.mem[OUT] == 7, "the reader has the word offered: %ld", (long)ND(b)->n.mem[OUT]);
CHECK(ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0,
"the one that offered is told its offer on port 0 was taken, and has none left: %ld from %ld", (long)ND(a)->n.mem[OUT], (long)ND(a)->n.mem[OUT + 1]);
/* taken by a device */
fresh(); none.took_n = 0;
a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever();
CHECK(v4_fabric_wire_device(&f, a, 0, &sink), "the same, wired to a device that takes what is written");
(void)settle(300);
CHECK(none.took_n == 1 && none.took[0] == 7 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1, "the device has the word, and the node is told");
/* a neighbour that never reads */
fresh();
a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever();
b = loaded(1); count_for_ever();
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to one that never reads");
for (i = 0; i < 200; i++) (void)v4_fabric_step(&f);
clock = ND(a)->es.anticlock;
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->es.anticlock == clock && ND(a)->n.mem[OUT + 2] == 0, "it is in the wait, and executes nothing there");
CHECK((ND(b)->n.readers & (1u << 3)) != 0, "and shows to its neighbour as waiting to read");
/* withdrawn by a word that comes for it */
c = loaded(2); LIT(PB + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever();
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, c, 0, a, 2), "a third node writes to it");
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.mem[OUT] == 9 && ND(a)->n.mem[OUT + 1] == 2 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0,
"it gets that word, from that port, and its offer is withdrawn: %ld from %ld", (long)ND(a)->n.mem[OUT], (long)ND(a)->n.mem[OUT + 1]);
CHECK(!ND(b)->n.given && !ND(c)->n.asking, "the node offered to was given nothing, and the writer was served");
/* an offer that could be taken in the step a word comes: the word comes first */
fresh();
a = loaded(0); offer(0, 7); wait_into(OUT); offer(0, 7); wait_into(OUT + 3); done_mark(); wait_for_ever();
b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
c = loaded(2); LIT(PB + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever();
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, c, 0, a, 2), "one offers to a reader while a third writes to it");
v4_fabric_sleep(&f, a);
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
CHECK(ND(b)->n.reading && ND(c)->n.asking, "the reader and the writer are both blocked");
v4_fabric_sleep(&f, b); v4_fabric_sleep(&f, c); v4_fabric_wake(&f, a);
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
CHECK(v4_node_in_wait(&ND(a)->n), "and then the one that offers is in the wait");
v4_fabric_wake(&f, b); v4_fabric_wake(&f, c);
static v4_device dev;
v4_fabric_init(&f, places, 4);
for (i = 0; i < 3; i++) v4_node_reset(&pool[i].n);
a = loaded(0); /* looks about, then writes two words to its port 0 */
LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); O(DROP);
LIT(PB + 0); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); wait_for_ever();
b = loaded(1); /* never reads */
LIT(OUT); O(BANG_A);
{
v4_cell again = v4_asm_label(&as);
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
v4_asm_branch(&as, V4_OP_JUMP, again);
}
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a writer that looks first, wired to a node that never reads");
dev.take = stream_take; dev.give = 0; dev.self = &none; dev.pending = device_has; has = 1; dev_has = &has;
CHECK(v4_fabric_wire_device(&f, a, 5, &dev), "and to a device, which has something for it");
for (i = 0; i < 40; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.asking && ND(a)->n.request == 1 && v4_node_words_since_look(&ND(a)->n) == 0, "the writer is blocked on its first word, and waits: the fabric has no error for it");
v4_fabric_interrupt_error(&f, 21);
for (i = 0; i < 40; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "nor is a bare node let go when it has: it cannot take an error");
v4_node_error_attach(&ND(a)->n, OUT + 20);
v4_node_fault_attach(&ND(a)->n, 900);
has = 0;
for (i = 0; i < 40; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.asking && ND(a)->n.faults == 0, "a node that can take one waits all the same while the device has nothing for it");
has = 1;
(void)v4_fabric_step(&f);
CHECK(!v4_node_in_wait(&ND(a)->n) && !ND(c)->n.asking && ND(b)->n.reading && !ND(b)->n.given, "in one step: the word for it comes first, and the reader is given nothing");
(void)settle(300);
CHECK(ND(a)->n.mem[OUT] == 9 && ND(a)->n.mem[OUT + 1] == 2 && ND(a)->n.mem[OUT + 3] == 0 && ND(a)->n.mem[OUT + 4] == (v4_cell)V4_PORTS + 0 && ND(b)->n.mem[OUT] == 7,
"it offers again, and the reader gets the word once");
CHECK(!ND(a)->n.asking && ND(a)->n.fault_kind == V4_FAULT_RAISED && ND(a)->n.fault_addr == 21 && ND(a)->n.mem[OUT + 20] == 21,
"when it has, the writer is let go and the error raised on it");
/* two nodes each offering to the other */
fresh();
a = loaded(0); offer(0, 5); wait_into(OUT); done_mark(); wait_for_ever();
b = loaded(1); offer(3, 6); wait_into(OUT); done_mark(); wait_for_ever();
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "two nodes each offer to the other");
(void)settle(300);
CHECK(ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0, "the one at the lower place has its offer taken");
CHECK(ND(b)->n.mem[OUT] == 5 && ND(b)->n.mem[OUT + 1] == 3, "the other gets the word, from the port it came by");
CHECK(ND(a)->n.offers == 0 && ND(b)->n.offers == 0 && ND(a)->n.mem[OUT + 2] == 1 && ND(b)->n.mem[OUT + 2] == 1, "and neither has an offer left");
/* three in a ring, each offering to the next */
fresh();
a = loaded(0); offer(0, 11); wait_into(OUT); done_mark(); wait_for_ever();
b = loaded(1); offer(0, 12); wait_into(OUT); done_mark(); wait_for_ever();
c = loaded(2); offer(0, 13); wait_into(OUT); done_mark(); wait_for_ever();
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 1) && v4_fabric_wire(&f, b, 0, c, 1) && v4_fabric_wire(&f, c, 0, a, 1), "three nodes in a ring, each offering to the next");
CHECK(settle(1000) < 1000, "they come to rest");
CHECK(ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(b)->n.mem[OUT] == 11 && ND(b)->n.mem[OUT + 1] == 1, "the first's offer was taken by the second");
CHECK(v4_node_in_wait(&ND(c)->n) && ND(c)->n.offers == 1u && ND(c)->n.mem[OUT + 2] == 0 && !ND(a)->n.given, "the third still offers to the first, which is not reading: nothing was handed over twice");
/* a neighbour that is asleep */
fresh();
a = loaded(0); offer(0, 7); wait_into(OUT); done_mark(); wait_for_ever();
b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "a node that offers, wired to a reader");
v4_fabric_sleep(&f, a);
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
v4_fabric_sleep(&f, b); v4_fabric_wake(&f, a);
for (i = 0; i < 100; i++) (void)v4_fabric_step(&f);
CHECK(v4_node_in_wait(&ND(a)->n) && ND(b)->n.reading && !ND(b)->n.given, "while the reader is asleep the offer stands");
v4_fabric_wake(&f, b);
(void)settle(300);
CHECK(ND(b)->n.mem[OUT] == 7 && ND(a)->n.mem[OUT + 2] == 1, "woken, it takes it");
/* a port with nothing on it */
fresh();
a = loaded(0); offer(5, 7); wait_into(OUT); done_mark(); wait_for_ever();
CHECK(v4_asm_ok(&as) && v4_fabric_wire_device(&f, a, 7, &sink), "a node that offers on a port nothing is wired to (the port it ends by reading has a device that gives nothing)");
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
v4_fabric_gone_error(&f, 18);
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.faults == 0, "a bare node waits, though the fabric has an error for it");
/* once the first word has gone, it is not let go: the message must be finished */
v4_fabric_init(&f, places, 4);
for (i = 0; i < 3; i++) v4_node_reset(&pool[i].n);
a = loaded(0);
LIT(PB + (v4_cell)V4_PORTS + 3); O(BANG_B); O(FETCH_B); O(DROP);
LIT(PB + 0); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); wait_for_ever();
b = loaded(1); /* reads one word, and no more */
LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A);
{
v4_cell again = v4_asm_label(&as);
O(FETCH_A); LIT(1); O(ADD); O(STORE_A);
v4_asm_branch(&as, V4_OP_JUMP, again);
}
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire_device(&f, a, 5, &dev), "the same, to a node that reads one word and no more");
v4_node_error_attach(&ND(a)->n, OUT + 20);
v4_node_fault_attach(&ND(a)->n, 900);
for (i = 0; i < 100 && ND(a)->n.mem[OUT + 2] == 0; i++) (void)v4_fabric_step(&f); /* until it has noted what it was told */
CHECK(!v4_node_in_wait(&ND(a)->n) && ND(a)->n.faults == 0 && ND(a)->n.offers == 0 && ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == 2 * (v4_cell)V4_PORTS + 5 && ND(a)->n.mem[OUT + 2] == 1,
"one that can take an error is woken and told there is nothing on port 5: no error is raised, and its offer is withdrawn: %ld", (long)ND(a)->n.mem[OUT + 1]);
/* offers are withdrawn by a store to the wait (found by the review of step 6d) */
fresh();
a = loaded(0); offer(0, 7); LIT(0); LIT(PB + (v4_cell)V4_PORT_WAIT); O(BANG_A); O(STORE_A); done_mark(); wait_for_ever();
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
CHECK(v4_asm_ok(&as) && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0 && ND(a)->n.faults == 0, "a node that offers and then stores to the wait has no offer left");
/* the wait for an offer only: a word that comes for the node does not end it */
fresh();
a = loaded(0); offer(0, 7); wait1_into(OUT); done_mark(); wait_for_ever();
b = loaded(1); count_for_ever();
c = loaded(2); LIT(PB + 0); O(BANG_B); LIT(9); O(STORE_B); wait_for_ever();
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3) && v4_fabric_wire(&f, c, 0, a, 2), "a node waits for its offer only, to a node that never reads, while a third writes to it");
for (i = 0; i < 200; i++) (void)v4_fabric_step(&f);
CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.mem[OUT + 2] == 0 && ND(c)->n.asking && (ND(c)->n.readers & 1u) == 0, "it goes on waiting; the writer is not served, and does not see it as reading");
CHECK(v4_fabric_remove(&f, b) != 0, "the node offered to is removed");
v4_fabric_gone_error(&f, 18);
v4_node_error_attach(&ND(a)->n, OUT + 20);
v4_node_fault_attach(&ND(a)->n, 900);
CHECK(v4_fabric_wire_device(&f, a, 7, &sink), "(the port it ends by reading has a device that gives nothing)");
for (i = 0; i < 100 && ND(a)->n.mem[OUT + 2] == 0; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.mem[OUT + 1] == 2 * (v4_cell)V4_PORTS + 0 && ND(a)->n.faults == 0 && ND(a)->n.offers == 0 && ND(c)->n.asking, "it is told there is nothing on that port; no error is raised; the writer still waits");
fresh();
a = loaded(0); offer(0, 7); wait1_into(OUT); done_mark(); wait_for_ever();
b = loaded(1); LIT(PB + 3); O(BANG_B); O(FETCH_B); LIT(OUT); O(BANG_A); O(STORE_A); wait_for_ever();
CHECK(v4_asm_ok(&as) && v4_fabric_wire(&f, a, 0, b, 3), "the same, to a node that reads");
(void)settle(300);
CHECK(ND(b)->n.mem[OUT] == 7 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 0 && ND(a)->n.mem[OUT + 2] == 1, "its offer is taken, and it is told");
/* a host with one node, whose devices always take */
fresh();
a = loaded(0); offer(1, 21); offer(3, 23); wait_into(OUT); done_mark(); wait_for_ever();
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
CHECK(v4_node_in_wait(&ND(a)->n) && ND(a)->n.offers == ((1u << 1) | (1u << 3)), "a node offers on two ports and is in the wait");
CHECK(v4_node_offer_as_write(&ND(a)->n) && ND(a)->n.asking && ND(a)->n.ask_port == 1 && ND(a)->n.request == 21, "its host turns that into a write of the lower port's word");
v4_node_port_served(&ND(a)->n);
for (i = 0; i < 50; i++) (void)v4_fabric_step(&f);
CHECK(!ND(a)->n.asking && ND(a)->n.mem[OUT] == 0 && ND(a)->n.mem[OUT + 1] == (v4_cell)V4_PORTS + 1 && ND(a)->n.mem[OUT + 2] == 1 && ND(a)->n.offers == 0,
"and when it is served the node is told that offer was taken");
CHECK(!v4_node_offer_as_write(&ND(a)->n), "a node that is not in the wait is left alone");
v4_fabric_interrupt_error(&f, 21);
for (i = 0; i < 60; i++) (void)v4_fabric_step(&f);
CHECK(ND(a)->n.asking && ND(a)->n.request == 2 && ND(a)->n.faults == 0, "blocked on its second word with the device still pending, the writer waits");
}
printf(" %d checks, %d failures\n", checks, failures);
+33 -206
View File
File diff suppressed because it is too large Load Diff
+2 -18
View File
@@ -63,7 +63,7 @@ static v4_heat h;
static v4_text tx;
static v4_cell w_key, w_key_end, w_fault, capsule_latest;
static v4_cell w_idle; /* (IDLE): where the node waits for a message */
static v4_cell w_idle_end; /* (LINE): the word after it */
static v4_cell w_idle_end; /* (PASS-ON): the word after it */
static char out[V4_CONSOLE_CAP + 1];
/* STORAGE (MESH.md section 8). The node asks its kernel for its blocks, and
@@ -213,7 +213,6 @@ static int run_line(long max)
while (steps < max && idle < 64) {
(void)v4_exec_step_word(&n, &es, &h);
steps++;
(void)v4_node_offer_as_write(&n); /* in the wait: what it offers is a write, the kernel and the console taking at once */
if (n.asking && n.ask_port == 0) { kernel_serve(); v4_node_port_served(&n); continue; } /* a request */
if (n.asking && n.ask_port == CONSOLE_PORT) { /* a word of a message from the node */
v4_cell word = n.request;
@@ -871,7 +870,7 @@ int main(void)
printf(" capsule: %ld words\n", (long)v4_text_here(&tx) - 16);
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
w_idle = v4_text_word(&tx, "(IDLE)");
w_idle_end = v4_text_word(&tx, "(LINE)"); /* the word after (IDLE) in quit.v4 */
w_idle_end = v4_text_word(&tx, "(PASS-ON)"); /* the word after (IDLE) in quit.v4 */
w_key = v4_text_word(&tx, "KEY");
w_key_end = v4_text_word(&tx, "CR"); /* the word after KEY in core.v4 */
w_fault = v4_text_word(&tx, "(FAULTS)");
@@ -1134,21 +1133,6 @@ int main(void)
}
printf(" %u values may wait on the stack while the next line is typed and interpreted\n", most);
CHECK(most + 4u >= V4_DATA_DEPTH, "the prompt and the interpreter take no more than four cells of the stack");
/* one more than may wait: the line ends in an error that says so, and the node goes on (found by the review of step 6d) */
{
char line[4 * V4_DATA_DEPTH + 16];
size_t at = 0;
unsigned m;
const char *r;
boot_bare();
for (m = 0; m < most + 1; m++) at += (size_t)snprintf(line + at, sizeof line - at, "1 ");
snprintf(line + at, sizeof line - at, "\n");
r = say(line);
CHECK(strstr(r, "Stack overflow") != NULL && strstr(r, " ERROR\nok> ") != NULL, "a line that leaves one value more than may wait ends in an error that says so: \"%s\"", r);
CHECK(strcmp(say("1 2 + .\n"), "3 ok\nok> ") == 0, "and the next line is done as usual");
for (m = 0; m < 2000 && !node_idle(); m++) (void)v4_exec_step_word(&n, &es, &h); /* it finishes what it was doing when its answer went */
CHECK(n.mem[MQ_COUNT] == 0 && node_idle(), "with nothing left among the messages waiting, and the node at its ports");
}
}
/* ---- how much of the data stack a line has ---- */
+22 -37
View File
@@ -140,7 +140,7 @@ static int host_born(void *self)
v4_node_fault_attach(&pool[k].n, w_fault);
v4_blocks_init(&node_blocks[k], BUF0_W);
if (k != 0) { /* Hera's kernel is wired where she is made */
node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k];
node_kernel[k].take = node_kernel_take; node_kernel[k].give = 0; node_kernel[k].self = &pool[k]; node_kernel[k].pending = 0;
if (!v4_fabric_wire_device(&f, (unsigned)place, 0, &node_kernel[k])) return -1;
}
return place;
@@ -194,7 +194,7 @@ static int kernel_take(void *self, v4_cell value) /* a request */
if (value < FIRST_REQUEST || !v4_manage_serve(&manage, hera_place, (unsigned)(value - FIRST_REQUEST))) refused++;
return 1;
}
static const v4_device kernel = { kernel_take, kernel_give, 0 };
static const v4_device kernel = { kernel_take, kernel_give, 0, 0 };
/* ---- the console: a device on Hera's port 1 ---- */
#define CONSOLE_ID 1
@@ -237,7 +237,8 @@ static int console_take(void *self, v4_cell value)
}
return 1;
}
static const v4_device console = { console_take, console_give, 0 };
static int console_pending(void *self) { (void)self; return going_at < going.count; }
static const v4_device console = { console_take, console_give, 0, console_pending };
/* Is there a node that will execute at the next step? A step in which a
* node only faulted counts nothing done (exec.h), and it goes on from its
@@ -275,8 +276,6 @@ static const char *tell(v4_cell node, const char *text)
if (steps >= step_limit) { stuck(); return "(still running)"; }
return printed;
}
/* The same, answering with what has been printed: for when a node is spinning and the fabric never rests. */
static const char *said(v4_cell node, const char *text) { (void)tell(node, text); return printed; }
static int told(v4_cell node, const char *text) { return tell(node, text)[0] == 0 && ended == 1 && ended_how == V4_TEXT_COMPLETED; }
/* POST's host, for the node numbered 10 (v4/include/v4/post.h): a line is
@@ -383,6 +382,7 @@ int main(void)
/* ---- Hera is born empty, and takes in the nucleus ---- */
v4_fabric_init(&f, places, PLACES);
v4_fabric_gone_error(&f, V4_ERROR_NO_ONE);
v4_fabric_interrupt_error(&f, V4_ERROR_INTERRUPTED);
place = host_born(0);
CHECK(place == 0, "a node is born");
hera_place = (unsigned)place;
@@ -506,8 +506,8 @@ int main(void)
/* a node is killed while its neighbour is writing to it */
step_limit = 3000000ul;
CHECK(strcmp(tell(14, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 14 is stuck in a loop that never ends");
CHECK(strcmp(tell(12, ": TO14 S\" 65 EMIT\" 14 SEND ; TO14"), "(still running)") == 0 && v4_node_in_wait(node_numbered(12)) && !node_numbered(12)->asking,
"node 12, beside it, sends it text: 14 never reads, and 12 waits to begin the message, in the wait");
CHECK(strcmp(tell(12, ": TO14 S\" 65 EMIT\" 14 SEND ; TO14"), "(still running)") == 0 && node_numbered(12)->asking,
"node 12, beside it, sends it text: 14 never reads, and 12 is blocked writing to it");
{
unsigned born = born_count;
const char *r = tell(10, "14 KILL 65 EMIT");
@@ -525,34 +525,21 @@ int main(void)
CHECK(strstr(tell(10, ": H14 S\" 1\" 14 SEND ; H14"), "Argument out of range") != NULL, "Hera herself has no way to it");
}
/* two nodes each send a third text and are then stuck: the third has an answer for each that it
* cannot give, and goes on doing what others send it (MESH.md 7c). While a node spins the fabric
* never rests: said() answers with what was printed. */
/* a node waits on one that is stuck and is not its neighbour; Hera kills the stuck one */
step_limit = 3000000ul;
CHECK(strcmp(tell(12, ": S11 S\" 1 DROP\" 11 SEND BEGIN 0 UNTIL ; S11"), "(still running)") == 0 &&
strcmp(tell(13, ": S11 S\" 1 DROP\" 11 SEND BEGIN 0 UNTIL ; S11"), "(still running)") == 0, "nodes 12 and 13, either side of node 11, each send it text and are then stuck in a loop");
CHECK(node_numbered(11)->mem[MQ_COUNT] == 18 && v4_node_in_wait(node_numbered(11)), "node 11 keeps its answer to each, offered, and sleeps: %ld cells", (long)node_numbered(11)->mem[MQ_COUNT]);
CHECK(strcmp(said(11, "11 100 * ."), "1100 ") == 0 && ended == 1 && node_numbered(11)->mem[MQ_COUNT] == 18, "text typed for node 11 is done all the same: \"%s\"", printed);
/* a node waits on one that is stuck; text for the stuck one waits with Hera, who goes on; Hera kills it */
CHECK(said(11, ": W13 13 AWAIT . ; W13")[0] == 0 && ended == 0 && node_numbered(11)->mem[AWAIT_FROM] == 13 && waiting(11), "node 11 waits for node 13's answer");
{
v4_cell kept = node_numbered(10)->mem[MQ_COUNT];
(void)tell(13, "1 DROP");
CHECK(ended == 0 && v4_node_in_wait(node_numbered(10)) && !node_numbered(10)->asking && node_numbered(10)->mem[MQ_COUNT] > kept,
"text typed for the stuck node waits with Hera, offered: she sleeps in the wait, not blocked writing");
}
CHECK(strcmp(said(10, "1 2 + ."), "3 ") == 0 && ended == 1, "and text for Hera herself is done meanwhile: \"%s\"", printed);
(void)tell(10, "13 KILL 65 EMIT");
CHECK(node_numbered(13) == NULL && strstr(printed, "Node gone") != NULL && node_numbered(11)->mem[AWAIT_FROM] == 0,
"Hera kills node 13: node 11's wait ends, with an error that says the node is gone: \"%s\"", printed);
CHECK(strstr(printed, "A") != NULL && ended == 2 && node_numbered(10)->mem[MQ_COUNT] == 9 && v4_node_in_wait(node_numbered(10)),
"her line is not held by node 12, which is stuck and has to be told too: that message waits with her, offered, and what she kept for node 13 is let go: %ld cells",
(long)node_numbered(10)->mem[MQ_COUNT]);
CHECK(strcmp(said(11, "11 100 * ."), "1100 ") == 0 && strcmp(said(10, "1 2 + ."), "3 ") == 0, "and the others go on");
CHECK(strcmp(tell(13, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 13 is stuck in a loop");
CHECK(strcmp(tell(12, ": W13 13 AWAIT . ; W13"), "(still running)") == 0 && node_numbered(12)->mem[AWAIT_FROM] == 13 && waiting(12),
"node 12, which is not wired to it, waits for its answer");
(void)tell(10, "13 KILL");
CHECK(node_numbered(13) == NULL && strstr(printed, "Node gone") != NULL && node_numbered(12)->mem[AWAIT_FROM] == 0 && waiting(12),
"Hera kills node 13: node 12's wait ends, with an error that says the node is gone: \"%s\"", printed);
step_limit = 30000000ul;
CHECK(strcmp(tell(12, "12 100 * ."), "1200 ") == 0 && strcmp(tell(11, "11 100 * ."), "1100 ") == 0, "and the others go on");
/* Hera's own wait, on a node that is stuck: a line from the console ends it */
CHECK(said(10, "12 AWAIT .")[0] == 0 && ended == 0 && node_numbered(10)->mem[AWAIT_FROM] == 12 && waiting(10), "Hera waits for node 12's answer, which will not come");
step_limit = 3000000ul;
CHECK(strcmp(tell(12, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 12 is stuck in a loop");
CHECK(strcmp(tell(10, "12 AWAIT ."), "(still running)") == 0 && node_numbered(10)->mem[AWAIT_FROM] == 12 && waiting(10), "Hera waits for its answer, which will not come");
{
const char *r = tell(10, "12 KILL 66 EMIT");
CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "B") != NULL && strstr(r, "Interrupted\n") < strstr(r, "B") && ended == 2,
@@ -560,14 +547,12 @@ int main(void)
CHECK(node_numbered(12) == NULL && node_numbered(10)->mem[AWAIT_FROM] == 0, "the line killed the stuck node");
}
step_limit = 30000000ul;
CHECK(strcmp(tell(11, "11 100 * ."), "1100 ") == 0 && strcmp(tell(10, "1 2 + ."), "3 ") == 0 && node_numbered(11)->mem[MQ_COUNT] == 0 && node_numbered(10)->mem[MQ_COUNT] == 0 && waiting(10) && waiting(11),
"the two that are left go on, and keep nothing for the nodes that are gone");
/* Hera's line must begin a message to a node that is stuck: it waits, and a line from the console ends it (MESH.md 7c.3) */
/* Hera blocked writing to a node that is stuck: a line from the console lets her go (MESH.md 7b.7) */
step_limit = 3000000ul;
CHECK(strcmp(tell(11, ": SPIN BEGIN 0 UNTIL ; SPIN"), "(still running)") == 0, "node 11 is stuck in a loop");
CHECK(strcmp(tell(10, ": T11 S\" 1 DROP\" 11 SEND 67 EMIT ; T11"), "(still running)") == 0 && v4_node_in_wait(node_numbered(10)) && !node_numbered(10)->asking,
"Hera sends it text: it never reads, and she waits to begin the message, in the wait");
CHECK(strcmp(tell(10, ": T11 S\" 1 DROP\" 11 SEND 67 EMIT ; T11"), "(still running)") == 0 && node_numbered(10)->asking,
"Hera sends it text: its number is the higher, so she writes without looking, and is blocked");
{
const char *r = tell(10, "11 KILL 68 EMIT");
CHECK(strstr(r, "Interrupted\n") != NULL && strstr(r, "D") != NULL && strstr(r, "C") == NULL && strstr(r, "Interrupted\n") < strstr(r, "D"),