feat(v4.0.0): five nodes from nothing -- Hera asks for nodes, births and joins them
MESH.md step 5, in the fabric under test; the products are still one node each until steps 8 and 9. manage.c: what Hera asks of whoever holds the fabric, eleven requests by KERNEL-WORD -- NODE-ME -BORN -WIRE -UNWIRE -SLEEP -WAKE -KILL -PARITY and CAPSULE-OPEN -CELL -LINE. Nucleus: PORT!, SEND-ON, AWAIT, (SEAL). capsules/v4/hera.4th: BIRTH and UNIT, the unit rule in FORTH and nowhere else. The dictionary hash moves to the engine (v4_image_dict_hash). test_host_unit.c, 34 checks, 64-bit: Hera is born empty, takes the nucleus through her port, FORTH-79, POST and her capsule; 10 UNIT; four nodes are born, each takes the nucleus and FORTH-79 through its port from Hera and passes POST 538/538; four parities, one dictionary hash; they talk, and a message between two corners not wired goes by Hera. All v4 tests at both widths and under ASan+UBSan. hosted-check on three ISAs. Bare metal: logs/20261006-143934 (amd64), -144204 (aarch64), -144601 (riscv64). Open, recorded in MESH.md: not run at 32 bits; a node that never answers leaves Hera waiting; the capsules are read from files in the test, not from the baked directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f9c034cadd
commit
348eed7fa1
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-10-06T17:54:02Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-10-06T18:45:22Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
@@ -30,6 +30,7 @@
|
||||
| `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` | n/a |
|
||||
| `user-font-demo.4th` | 4200, 4201, 4202 | `0xce1fd7d1b581a56d` | n/a |
|
||||
| `v4:forth79.4th` | 6000, 6001, 6002 | `0x4055641ee17d176b` | n/a |
|
||||
| `v4:hera.4th` | 8000, 8001, 8002, 8003, 8004 | `0x93a3160c447d7641` | n/a |
|
||||
| `v4:post79.4th` | 7000, 7001, 7002, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 7011, 7012, 7013, 7014, 7015, 7016, 7017, 7018, 7019, 7020, 7021, 7022, 7023, 7024, 7025, 7026, 7027, 7028, 7029, 7030, 7031, 7032, 7033, 7034, 7035, 7036, 7037, 7038, 7039, 7040, 7041, 7042, 7043, 7044, 7045, 7046, 7047, 7048, 7049, 7050, 7051, 7052, 7053, 7054, 7055, 7056, 7057, 7058, 7059, 7060, 7061, 7062, 7063 | `0xb26d1b974884179d` | n/a |
|
||||
| `workload-0.4th` | 2200, 2201 | `0x93f86f60aeba8feb` | n/a |
|
||||
| `workload-1-lite.4th` | 5058, 5059 | `0x44a7a7e3176dcc8d` | n/a |
|
||||
@@ -435,10 +436,15 @@
|
||||
| 7061 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7062 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 7063 | `v4:post79.4th` | `0xb26d1b974884179d` | ok |
|
||||
| 8000 | `v4:hera.4th` | `0x93a3160c447d7641` | ok |
|
||||
| 8001 | `v4:hera.4th` | `0x93a3160c447d7641` | ok |
|
||||
| 8002 | `v4:hera.4th` | `0x93a3160c447d7641` | ok |
|
||||
| 8003 | `v4:hera.4th` | `0x93a3160c447d7641` | ok |
|
||||
| 8004 | `v4:hera.4th` | `0x93a3160c447d7641` | ok |
|
||||
|
||||
## Conflicts
|
||||
|
||||
None.
|
||||
|
||||
---
|
||||
*38 capsule(s) scanned. Re-run `mkcapsule --manifest <dir>` to refresh.*
|
||||
*39 capsule(s) scanned. Re-run `mkcapsule --manifest <dir>` to refresh.*
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
Block 8000
|
||||
( hera.4th -- Hera: the node that has the others born and )
|
||||
( manages them. docs/v4.0.0/MESH.md sections 9 and 10. )
|
||||
( She asks whoever holds the fabric for a node, wires it, )
|
||||
( and sends it its capsules through the port that joins )
|
||||
( them. The unit rule is here and nowhere else: four outer )
|
||||
( nodes round a centre, an outer node wired only inside its )
|
||||
( unit. )
|
||||
( Text for another node is built in PAD. )
|
||||
VARIABLE (T#)
|
||||
: (T0) ( -- ) 0 (T#) ! ;
|
||||
: (TC) ( c -- ) PAD (T#) @ + C! 1 (T#) +! ;
|
||||
: (TS) ( baddr u -- )
|
||||
DUP IF 0 DO DUP I + C@ (TC) LOOP DROP ELSE DROP DROP THEN ;
|
||||
: (TN) ( n -- ) 0 <# #S #> (TS) 32 (TC) ;
|
||||
: (T) ( -- baddr u ) PAD (T#) @ ;
|
||||
Block 8001
|
||||
( The node being born: its place in the fabric, the number )
|
||||
( it is to have, and the port of this node that leads to it. )
|
||||
VARIABLE (KID) VARIABLE (KID#) VARIABLE (KIDP)
|
||||
: (FAIL) ( -- )
|
||||
." BIRTH: node " (KID#) @ . ." did not do as it was told" CR
|
||||
-1 NODE-ERROR ! ;
|
||||
: (TOLD) ( how -- ) 1 - IF (FAIL) THEN ;
|
||||
: (TELL) ( baddr u -- ) (KID#) @ SEND (KID#) @ AWAIT (TOLD) ;
|
||||
( the nucleus, a capsule of F18 code, cell by cell to its port )
|
||||
: (NUCLEUS) ( -- )
|
||||
S" v4:nucleus-64.f18" CAPSULE-OPEN 0= IF (FAIL) THEN
|
||||
BEGIN CAPSULE-CELL WHILE (KIDP) @ PORT! REPEAT DROP ;
|
||||
( a capsule of FORTH blocks, a line at a time )
|
||||
: (LINES) ( baddr u -- ) CAPSULE-OPEN 0= IF (FAIL) THEN
|
||||
BEGIN PAD CAPSULE-LINE DUP 0< 0= WHILE PAD SWAP (TELL) REPEAT
|
||||
DROP ;
|
||||
Block 8002
|
||||
( who it is, where its printing goes, and that everything )
|
||||
( not told of goes by its port 2, where this node is )
|
||||
: (WHO) ( -- )
|
||||
(T0) (KID#) @ (TN) S" (ME) ! " (TS)
|
||||
(CONSOLE) @ (TN) S" (CONSOLE) ! 2 DEFAULT-ROUTE " (TS)
|
||||
(ME) @ (TN) S" 2 NEIGHBOUR" (TS)
|
||||
(T) 0 (KIDP) @ SEND-ON (KID#) @ AWAIT (TOLD) ;
|
||||
( number port -- place A NODE IS BORN on that port of )
|
||||
( this one: empty, then the nucleus, then FORTH-79 and POST. )
|
||||
: BIRTH ( number port -- place )
|
||||
(KIDP) ! (KID#) !
|
||||
NODE-BORN DUP 0< IF (FAIL) THEN (KID) !
|
||||
NODE-ME (KIDP) @ (KID) @ 2 NODE-WIRE 0= IF (FAIL) THEN
|
||||
(NUCLEUS)
|
||||
(KID#) @ (KIDP) @ ROUTE (KID#) @ (KIDP) @ NEIGHBOUR (WHO)
|
||||
S" v4:forth79.4th" (LINES) S" v4:post79.4th" (LINES)
|
||||
Block 8003
|
||||
S" (SEAL)" (TELL)
|
||||
(KID) @ (KID#) @ NODE-PARITY
|
||||
(KID) @ ;
|
||||
( Two outer nodes are joined: port 3 of X to port 4 of Y, )
|
||||
( and each is told the other is there. )
|
||||
VARIABLE (XP) VARIABLE (XN) VARIABLE (YP) VARIABLE (YN)
|
||||
: (X) ( place number -- ) (XN) ! (XP) ! ;
|
||||
: (Y) ( place number -- ) (YN) ! (YP) ! ;
|
||||
: (SAY) ( them port me -- ) (KID#) !
|
||||
(T0) OVER (TN) DUP (TN) S" ROUTE " (TS)
|
||||
SWAP (TN) (TN) S" NEIGHBOUR" (TS) (T) (TELL) ;
|
||||
: (JOIN) ( -- )
|
||||
(XP) @ 3 (YP) @ 4 NODE-WIRE 0= IF (FAIL) THEN
|
||||
(YN) @ 3 (XN) @ (SAY) (XN) @ 4 (YN) @ (SAY) ;
|
||||
Block 8004
|
||||
( n -- A UNIT: four outer nodes, n+1 to n+4, on ports 2 )
|
||||
( to 5 of this one, and joined in a square: 1-2, 2-4, 4-3, )
|
||||
( 3-1. The corners across from each other are not wired: )
|
||||
( what one sends the other goes by this node. )
|
||||
VARIABLE (U)
|
||||
VARIABLE (P1) VARIABLE (P2) VARIABLE (P3) VARIABLE (P4)
|
||||
: (N) ( k -- number ) (U) @ + ;
|
||||
: UNIT ( n -- ) (U) !
|
||||
1 (N) 2 BIRTH (P1) ! 2 (N) 3 BIRTH (P2) !
|
||||
3 (N) 4 BIRTH (P3) ! 4 (N) 5 BIRTH (P4) !
|
||||
(P1) @ 1 (N) (X) (P2) @ 2 (N) (Y) (JOIN)
|
||||
(P2) @ 2 (N) (X) (P4) @ 4 (N) (Y) (JOIN)
|
||||
(P4) @ 4 (N) (X) (P3) @ 3 (N) (Y) (JOIN)
|
||||
(P3) @ 3 (N) (X) (P1) @ 1 (N) (Y) (JOIN) ;
|
||||
Binary file not shown.
+61
-5
@@ -289,11 +289,11 @@ checked in this step.
|
||||
|
||||
## 9. Birth, and Hera
|
||||
|
||||
**Proposal.** Hera asks, through the port where her requests go
|
||||
(`ENGINE.md` 3.3), for a node to be added and wired; she then sends the
|
||||
newborn its capsules through the port that joins them. Putting a node to
|
||||
sleep, waking it, and removing it are requests of the same kind. Only
|
||||
Hera's requests are honoured.
|
||||
**Proposal, built as step 5 (section 10).** Hera asks, through the port
|
||||
where her requests go (`ENGINE.md` 3.3), for a node to be added and wired;
|
||||
she then sends the newborn its capsules through the port that joins them.
|
||||
Putting a node to sleep, waking it, and removing it are requests of the
|
||||
same kind. Only Hera's requests are honoured.
|
||||
|
||||
The unit rule (ruling 5) is Hera's, in capsule code: it is what she does
|
||||
with those requests. The engine and the host do not know it.
|
||||
@@ -420,6 +420,62 @@ Each is tested, committed and pushed before the next.
|
||||
`FILL` now have fewer cases; nothing stops any other text doing what
|
||||
those cases did.
|
||||
5. **Birth and Hera's requests (section 9); the unit of five.**
|
||||
**Done 2026-10-06, in the fabric under test; the products are still one
|
||||
node each until steps 8 and 9.** Section 9's proposal, as built:
|
||||
- *What Hera asks* (`v4/include/v4/manage.h`, `v4/src/manage.c`). Eleven
|
||||
requests, each a word on her made with `KERNEL-WORD`, written to the
|
||||
port her requests go to: `NODE-ME`, `NODE-BORN`, `NODE-WIRE`,
|
||||
`NODE-UNWIRE`, `NODE-SLEEP`, `NODE-WAKE`, `NODE-KILL`, `NODE-PARITY`;
|
||||
and `CAPSULE-OPEN`, `CAPSULE-CELL`, `CAPSULE-LINE`, by which she reads
|
||||
a capsule the host has found and checked. A node is named to the host
|
||||
by its place in the fabric; its number, which messages go by, is the
|
||||
nodes' own. Only the node the host has wired to this is answered.
|
||||
- *What a node needs to do it* (`v4/capsule/quit.v4`): `PORT! ( w port
|
||||
-- )`, a cell written to a port as it is; `SEND-ON ( baddr u node port
|
||||
-- )`, text by a port named, for a neighbour with no number yet;
|
||||
`AWAIT ( node -- how )`, blocked until that node's word of how text
|
||||
ended comes, keeping every other message for later; `(SEAL)`, what is
|
||||
in the dictionary now is the system.
|
||||
- *Hera's capsule* (`capsules/v4/hera.4th`, blocks 8000 to 8004), FORTH.
|
||||
`BIRTH ( number port -- place )`: a node is asked for and wired to
|
||||
that port; the nucleus is sent it cell by cell; it is told its number,
|
||||
its console and that Hera is on its port 2; FORTH-79 and POST are sent
|
||||
it a line at a time, each waited for; it seals; its parity is
|
||||
recorded. `UNIT ( n -- )`: four births, on ports 2 to 5, and the four
|
||||
joined in a square, each told of the two beside it. The unit rule is
|
||||
there and nowhere else.
|
||||
`v4/tests/test_host_unit.c`, 34 checks, 64-bit: Hera is born empty and
|
||||
takes the nucleus through her port, then FORTH-79, POST (538 of 538) and
|
||||
her capsule from the console; `10 UNIT`; four nodes are born, each
|
||||
passes POST, the host records four parities with one dictionary hash;
|
||||
all five wait and execute nothing; text for each outer node is done
|
||||
there and what it prints comes back; `COLD` on one comes back to the
|
||||
nucleus and FORTH-79; one outer node sends text to the one beside it,
|
||||
and to the one across from it by way of Hera. Acceptance 1 and 2, in
|
||||
the fabric. All v4 tests at both widths and under ASan and UBSan. The
|
||||
single-node products are unchanged but for the nucleus's new words:
|
||||
`hosted-check` on three ISAs; bare metal `logs/20261006-143934`
|
||||
(amd64), `-144204` (aarch64), `-144601` (riscv64).
|
||||
**What stands in, in the test only:** the capsules are read from the
|
||||
files the build bakes in, and the nucleus capsule is written from the
|
||||
nucleus as the test assembles it, not found in the baked directory with
|
||||
its hash and signature checked; that is the host's part and comes with
|
||||
step 8. Each node has a disk of its own attached at birth, as the boot's
|
||||
one node has; storage through the ports is step 6.
|
||||
**Not done, and open:**
|
||||
- The test is not run on 32-bit cells: the POST capsule's expected
|
||||
results are 64-bit v3's and the nucleus capsule is `nucleus-64`. POST
|
||||
has never been run at 32 bits.
|
||||
- A node that never answers leaves Hera waiting for ever in `AWAIT`;
|
||||
she cannot then kill it. What she does about a birth that does not
|
||||
finish is not decided.
|
||||
- What an outer node prints while Hera is giving birth waits in Hera's
|
||||
400 cells until she is idle, and is lost if there is more than they
|
||||
hold. POST prints one line.
|
||||
- An outer node has no port to the kernel, so no kernel words: no
|
||||
`BYE`, and nothing it asks is honoured, as ruled.
|
||||
- The suite now takes about four minutes, eight under the sanitizers:
|
||||
five POSTs.
|
||||
6. **Storage (section 8).**
|
||||
7. **A second unit; scaling while running; sleep, wake and kill by
|
||||
command.**
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
[2J[001;001H[=3h[2J[001;001H[2J[001;001H[8;042;160t[2J[001;001H[2J[001;001H[8;056;240t[2J[001;001HBdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
|
||||
BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
|
||||
[2J[001;001H[=3h[2J[001;001HStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
RAW SERIAL UP
|
||||
[CKPT 002] Serial (COM1) initialized
|
||||
Monolithic build - kernel linked directly
|
||||
Collecting boot information...
|
||||
CmdLine: parsed OK
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
GOP: linear framebuffer found
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
EBS...
|
||||
EBS OK
|
||||
Calling kernel_main (monolithic)...
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: amd64
|
||||
Build: Oct 6 2026 14:39:07
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 131
|
||||
Total memory: 1023 MB
|
||||
Usable memory: 968 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 249251
|
||||
Free pages : 247728
|
||||
Used pages : 1523
|
||||
Total MB : 973
|
||||
Free MB : 967
|
||||
Used MB : 5
|
||||
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
VMM self-test complete.
|
||||
|
||||
IDT installed.
|
||||
|
||||
APIC: init...
|
||||
APIC: IA32_APIC_BASE MSR=0x00000000fee00900
|
||||
APIC: stale-ISR drain: 0 EOI(s) issued
|
||||
APIC: initialized (xAPIC MMIO, TPR=0, SIVR=0x1FF, EOI-clear)
|
||||
APIC: init done
|
||||
|
||||
I/O APIC: init...
|
||||
I/O APIC: base=0xfec00000, gsi_base=0, overrides=5
|
||||
override: source=00 gsi=2 flags=0x0000
|
||||
override: source=05 gsi=5 flags=0x000d
|
||||
override: source=09 gsi=9 flags=0x000d
|
||||
override: source=0a gsi=10 flags=0x000d
|
||||
override: source=0b gsi=11 flags=0x000d
|
||||
i8042: keyboard ACKed enable-scanning
|
||||
i8042: IRQ1 enabled in controller config byte
|
||||
I/O APIC: keyboard IRQ1 routed (masked)
|
||||
|
||||
Timer: init...
|
||||
Timer: init start
|
||||
Timer: PM_TMR_BLK discovered from FADT at port 1544
|
||||
Timer: VM mode detected (hypervisor present).
|
||||
Timer: HPET calibration disabled (VM-exit MMIO would poison timing).
|
||||
Timer: WARNING: invariant TSC not present under hypervisor.
|
||||
Timer: continuing in RELATIVE mode (no determinism guarantees).
|
||||
Timer: RDTSCP not present; using RDTSC (less serialized).
|
||||
Timer: CPUID frequency unavailable; trying PM Timer...
|
||||
Timer: trust=1 (0=NONE,1=REL,2=ABS), TSC=2100263096 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=1007167846, tick_hz=100, initial_count=10071678
|
||||
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=0xdac6d69e81818b71 capsule_hash=0xdac6d69e81818b71 words=310
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x4abf568309282a76
|
||||
V4: capsule v4:post79.4th signature: missing (unsigned)
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0xd110b71c6c09dd8e
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
ok> COLD
|
||||
FORTH-79 Cold Start
|
||||
System initialized.
|
||||
ok
|
||||
ok> 3 4 U* . .
|
||||
0 12 ok
|
||||
ok> FORGET U*
|
||||
Protected word
|
||||
ERROR
|
||||
ok> 9 KERNEL-WORD ASK9 ASK9
|
||||
Argument out of range
|
||||
ERROR
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,104 @@
|
||||
UEFI firmware (version 2025.11-3ubuntu7.3 built at 15:40:26 on Sep 23 2026)
|
||||
[2J[001;001H[=3h[2J[001;001H[2J[001;001H[8;031;100t[2J[001;001HBdsDxe: failed to load Boot0002 "UEFI Misc Device" from PciRoot(0x0)/Pci(0x2,0x0): Not Found
|
||||
BdsDxe: failed to load Boot0003 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:04.0-1" from PciRoot(0x0)/Pci(0x4,0x0)/USB(0x0,0x0): Not Found
|
||||
BdsDxe: loading Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
|
||||
BdsDxe: starting Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
|
||||
[2J[001;001H[=3h[2J[001;001HStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
Collecting boot information...
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: aarch64
|
||||
Build: Oct 6 2026 14:41:37
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 108
|
||||
Total memory: 4093 MB
|
||||
Usable memory: 4056 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 1039866
|
||||
Free pages : 1038455
|
||||
Used pages : 1411
|
||||
Total MB : 4061
|
||||
Free MB : 4056
|
||||
Used MB : 5
|
||||
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
VMM self-test complete.
|
||||
|
||||
AArch64: running at EL1
|
||||
IDT installed.
|
||||
|
||||
APIC: init...
|
||||
PSCI: no DTB -- using HVC (QEMU virt-machine default)
|
||||
GICv2: no DTB GIC node -- using QEMU virt-machine defaults
|
||||
GICv2: distributor+CPU interface enabled, PPI 30
|
||||
APIC: init done
|
||||
|
||||
Timer: init...
|
||||
Timer: AArch64 generic timer initialised.
|
||||
Timer: init done
|
||||
|
||||
Kernel heap initialized.
|
||||
Heap statistics:
|
||||
Total bytes: 2147483608
|
||||
Free bytes: 2147483608
|
||||
Used bytes: 0
|
||||
Peak bytes: 0
|
||||
Heap base addr: 1207959552
|
||||
Heap end addr: 3355443200
|
||||
|
||||
Heartbeat: init...
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0xdac6d69e81818b71 capsule_hash=0xdac6d69e81818b71 words=310
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x4abf568309282a76
|
||||
V4: capsule v4:post79.4th signature: missing (unsigned)
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0xd110b71c6c09dd8e
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
ok> COLD
|
||||
FORTH-79 Cold Start
|
||||
System initialized.
|
||||
ok
|
||||
ok> 3 4 U* . .
|
||||
0 12 ok
|
||||
ok> FORGET U*
|
||||
Protected word
|
||||
ERROR
|
||||
ok> 9 KERNEL-WORD ASK9 ASK9
|
||||
Argument out of range
|
||||
ERROR
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,174 @@
|
||||
|
||||
OpenSBI v1.8
|
||||
____ _____ ____ _____
|
||||
/ __ \ / ____| _ \_ _|
|
||||
| | | |_ __ ___ _ __ | (___ | |_) || |
|
||||
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
|
||||
| |__| | |_) | __/ | | |____) | |_) || |_
|
||||
\____/| .__/ \___|_| |_|_____/|____/_____|
|
||||
| |
|
||||
|_|
|
||||
|
||||
Platform Name : riscv-virtio,qemu
|
||||
Platform Features : medeleg
|
||||
Platform HART Count : 1
|
||||
Platform HART Protection : pmp
|
||||
Platform IPI Device : aclint-mswi
|
||||
Platform Timer Device : aclint-mtimer @ 10000000Hz
|
||||
Platform Console Device : uart8250
|
||||
Platform HSM Device : ---
|
||||
Platform PMU Device : ---
|
||||
Platform Reboot Device : syscon-reboot
|
||||
Platform Shutdown Device : syscon-poweroff
|
||||
Platform Suspend Device : ---
|
||||
Platform CPPC Device : ---
|
||||
Firmware Base : 0x80000000
|
||||
Firmware Size : 321 KB
|
||||
Firmware RW Offset : 0x40000
|
||||
Firmware RW Size : 65 KB
|
||||
Firmware Heap Offset : 0x47000
|
||||
Firmware Heap Size : 37 KB (total), 0 KB (reserved), 12 KB (used), 23 KB (free)
|
||||
Firmware Scratch Size : 4096 B (total), 1464 B (used), 2632 B (free)
|
||||
Runtime SBI Version : 3.0
|
||||
Standard SBI Extensions : ipi,pmu,srst,sse,hsm,rfnc,fwft,time,base,legacy,dbcn,dbtr
|
||||
Experimental SBI Extensions : none
|
||||
|
||||
Domain0 Name : root
|
||||
Domain0 Boot HART : 0
|
||||
Domain0 HARTs : 0*
|
||||
Domain0 Region00 : 0x0000000080040000-0x000000008005ffff M: (F,R,W) S/U: ()
|
||||
Domain0 Region01 : 0x0000000080000000-0x000000008003ffff M: (F,R,X) S/U: ()
|
||||
Domain0 Region02 : 0x0000000000100000-0x0000000000100fff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region03 : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region04 : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
|
||||
Domain0 Region05 : 0x000000000c400000-0x000000000c5fffff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region06 : 0x000000000c000000-0x000000000c3fffff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region07 : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
|
||||
Domain0 Next Address : 0x0000000020000000
|
||||
Domain0 Next Arg1 : 0x00000000bfe00000
|
||||
Domain0 Next Mode : S-mode
|
||||
Domain0 SysReset : yes
|
||||
Domain0 SysSuspend : yes
|
||||
|
||||
Boot HART ID : 0
|
||||
Boot HART Domain : root
|
||||
Boot HART Priv Version : v1.12
|
||||
Boot HART Base ISA : rv64imafdch
|
||||
Boot HART ISA Extensions : sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu
|
||||
Boot HART PMP Count : 16
|
||||
Boot HART PMP Granularity : 2 bits
|
||||
Boot HART PMP Address Bits : 54
|
||||
Boot HART MHPM Info : 16 (0x0007fff8)
|
||||
Boot HART Debug Triggers : 2 triggers
|
||||
Boot HART MIDELEG : 0x0000000000001666
|
||||
Boot HART MEDELEG : 0x0000000000f4b509
|
||||
[2J[004D[=3h[2J[009D[2J[004D[8;031;100t[2J[016DRISC-V EDK2 firmware version 2025.11-3ubuntu7.3
|
||||
Press ESCAPE within 5 seconds for boot options ERROR: C40000002:V03051002 I0 6D33944A-EC75-4855-A54D-809C75241F6C 83FFF850
|
||||
BdsDxe: failed to load Boot0001 "UEFI Misc Device" fr
|
||||
om PciRoot(0x0)/Pci(0x1,0x0): Not Found
|
||||
[2J[001;001H[=3h[2J[009DStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
Monolithic build - kernel linked directly
|
||||
Collecting boot information...
|
||||
CmdLine: parsed OK
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
GOP: linear framebuffer found
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
Calling kernel_main (monolithic)...
|
||||
riscv64 item 4.3.5a: satp state at kernel entry (before switch)
|
||||
satp.MODE = 0x000000000000000a
|
||||
satp.PPN = 0x00000000000bf868
|
||||
__kernel_start = 0x00000000bdea38f8
|
||||
riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a)
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: riscv64
|
||||
Build: Oct 6 2026 14:45:22
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 100
|
||||
Total memory: 1020 MB
|
||||
Usable memory: 977 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 250780
|
||||
Free pages : 250128
|
||||
Used pages : 652
|
||||
Total MB : 979
|
||||
Free MB : 977
|
||||
Used MB : 2
|
||||
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
VMM self-test complete.
|
||||
|
||||
IDT installed.
|
||||
|
||||
APIC: init...
|
||||
PLIC: no DTB PLIC node -- using QEMU virt-machine default (base=0x0c000000)
|
||||
PLIC: init (S-mode context 1, threshold=0)
|
||||
APIC: init done
|
||||
|
||||
Timer: init...
|
||||
Timer: RISC-V time CSR @ 10000000 Hz (FALLBACK, no devicetree)
|
||||
Timer: init done
|
||||
|
||||
Kernel heap initialized.
|
||||
Heap statistics:
|
||||
Total bytes: 536870872
|
||||
Free bytes: 536870872
|
||||
Used bytes: 0
|
||||
Peak bytes: 0
|
||||
Heap base addr: 2214588416
|
||||
Heap end addr: 2751459328
|
||||
|
||||
Heartbeat: init...
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0xdac6d69e81818b71 capsule_hash=0xdac6d69e81818b71 words=310
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x4abf568309282a76
|
||||
V4: capsule v4:post79.4th signature: missing (unsigned)
|
||||
PARITY:V4_POST tests=538 pass=538 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb26d1b974884179d capsule_hash=0xb26d1b974884179d dict_hash=0xd110b71c6c09dd8e
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
ok> COLD
|
||||
FORTH-79 Cold Start
|
||||
System initialized.
|
||||
ok
|
||||
ok> 3 4 U* . .
|
||||
0 12 ok
|
||||
ok> FORGET U*
|
||||
Protected word
|
||||
ERROR
|
||||
ok> 9 KERNEL-WORD ASK9 ASK9
|
||||
Argument out of range
|
||||
ERROR
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
@@ -123,6 +123,15 @@ read -- or, of two neighbours, the one with the lower number may wait.
|
||||
`NEIGHBOUR ( node port -- )` tells a node who is on each port. A node with
|
||||
no room for one more message lets it go and counts it in `(LOST)`.
|
||||
|
||||
**Five nodes from nothing** (`MESH.md` step 5) is built and proven in the
|
||||
fabric under test: Hera asks whoever holds the fabric for a node
|
||||
(`v4/src/manage.c`: `NODE-BORN`, `NODE-WIRE` and the rest), sends it the
|
||||
nucleus through the port that joins them, then FORTH-79 and POST a line at
|
||||
a time, and joins four such nodes in a square round her
|
||||
(`capsules/v4/hera.4th`: `BIRTH`, `UNIT`). `v4/tests/test_host_unit.c`.
|
||||
The two products are not yet this: they become the five nodes at steps 8
|
||||
and 9.
|
||||
|
||||
**This is still the lone node.** On bare metal `kernel_main.c` starts it
|
||||
before the fleet tables, beside the kernel's own system and not in the VM's
|
||||
place. `ENGINE.md` sets out the steps from here; step 1 is done and step 2 is
|
||||
|
||||
+12
-4
@@ -181,13 +181,18 @@ header CMOVE
|
||||
SET: (MQ-HEAD) a! !
|
||||
(MQ#) a! @ -1 + ! ;
|
||||
|
||||
\ ( port to -- ) take in a message. B is at the port, by its address,
|
||||
\ and `to` is the message's first word, already read from it. A length
|
||||
\ ( 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.
|
||||
: (TAKE-HDR)
|
||||
(MQ-HDR) a! !+ 5 FOR @b !+ UNEXT ! ;
|
||||
|
||||
\ ( -- ) the message whose seven words are in (MQ-HDR) is kept with the
|
||||
\ messages waiting: its text is read from the port B is at. A length
|
||||
\ below zero, or above what a message carries, is kept as -1 with no text:
|
||||
\ a longer one is read to its end first, so that what follows it is not
|
||||
\ taken for a message.
|
||||
: (TAKE)
|
||||
(MQ-HDR) a! !+ 5 FOR @b !+ UNEXT ! \ its seven words, and the port
|
||||
: (TAKE-KEEP)
|
||||
(MQ-HDR)+6 a! @ -if SIZED
|
||||
drop -1 (MQ-HDR)+6 a! ! 0 jump COUNTED
|
||||
SIZED: dup -1025 + -if LONG drop 3 + 2/ 2/ jump COUNTED
|
||||
@@ -203,6 +208,9 @@ header CMOVE
|
||||
GONE: drop
|
||||
LOSE: (LOST) a! @ 1 + ! ;
|
||||
|
||||
\ ( port to -- ) take in a message and keep it
|
||||
: (TAKE) (TAKE-HDR) jump (TAKE-KEEP)
|
||||
|
||||
\ ( 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:
|
||||
|
||||
+56
-7
@@ -175,13 +175,11 @@ header DEFAULT-ROUTE
|
||||
header NO-ROUTES
|
||||
: NO-ROUTES 0 (ROUTE#) a! ! 0 (ROUTE-DEFAULT) a! ! ;
|
||||
|
||||
\ ( baddr u node -- ) send the node u characters of text to interpret. It
|
||||
\ is on its way when SEND returns; what it prints there goes to that node's
|
||||
\ console. With no way to the node it is error 12. The write waits for the
|
||||
\ neighbour to read, and is not begun until it may be (core.v4, (GATE)).
|
||||
header SEND
|
||||
: SEND
|
||||
dup (PORT-FOR) if NOWAY
|
||||
\ ( baddr u node port -- ) send the node u characters of text to
|
||||
\ interpret, by the port given, by its address. It is on its way when this
|
||||
\ returns. The write waits for the neighbour to read, and is not begun
|
||||
\ until it may be (core.v4, (GATE)).
|
||||
: (SEND-ON)
|
||||
(GATE) !b \ to
|
||||
1 (HDR)
|
||||
dup !b \ length
|
||||
@@ -189,8 +187,59 @@ header SEND
|
||||
FOR dup C@ over 1 + C@ 8* + over 2 + C@ 8* 8* + over 3 + C@ 8* 8* 8* + !b 4 + NEXT
|
||||
drop ;
|
||||
NONE: drop drop ;
|
||||
|
||||
\ ( baddr u node -- ) send the node u characters of text to interpret,
|
||||
\ by the way this node knows to it. What it prints there goes to that
|
||||
\ node's console. With no way to the node it is error 12.
|
||||
header SEND
|
||||
: SEND
|
||||
dup (PORT-FOR) if NOWAY jump (SEND-ON)
|
||||
NOWAY: drop drop drop drop NODE-ERROR b! 12 !b ;
|
||||
|
||||
\ ( baddr u node port -- ) the same by a port this node names, by its
|
||||
\ number, whatever ways it knows: for a neighbour that has no number of
|
||||
\ its own yet, to which text is sent as to node 0.
|
||||
header SEND-ON
|
||||
: SEND-ON
|
||||
dup -if POS jump BAD
|
||||
POS: -PORTS + -if BAD drop (PORT) + jump (SEND-ON)
|
||||
BAD: drop drop drop drop drop NODE-ERROR b! 12 !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
|
||||
\ comes meanwhile is kept with the messages waiting. It waits for ever
|
||||
\ for a node that never answers.
|
||||
header AWAIT
|
||||
: AWAIT
|
||||
(AWAIT-FROM) a! !
|
||||
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! @ -3 + if A2 drop jump KEEP
|
||||
A2: 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 @b ; \ its one word
|
||||
KEEP: (TAKE-KEEP) jump L
|
||||
|
||||
\ ( w port -- ) write a cell to the port, by its number. It waits until
|
||||
\ the neighbour has taken it. This is how a node is sent a capsule of F18
|
||||
\ code (docs/v4.0.0/MESH.md section 5): the cells go as they are, not as a
|
||||
\ message, to a node that is executing from its ports.
|
||||
header PORT!
|
||||
: PORT!
|
||||
dup -if POS jump BAD
|
||||
POS: -PORTS + -if BAD drop (PORT) + b! !b ;
|
||||
BAD: drop drop drop NODE-ERROR b! 12 !b ;
|
||||
|
||||
\ ( -- ) WHAT IS IN THE DICTIONARY NOW IS THE SYSTEM: COLD comes back to
|
||||
\ it and FORGET will not go below it. Whoever has loaded a node's capsules
|
||||
\ has it do this (system.v4, (BOOT) and FENCE).
|
||||
header (SEAL)
|
||||
: (SEAL)
|
||||
DP a! @ (BOOT) a! !
|
||||
(LATEST) a! @ (BOOT)+1 a! !
|
||||
DP a! @ 3 + 2/ 2/ FENCE a! ! ;
|
||||
|
||||
\ FORTH-79: clear the return stack, set execution mode, return control to
|
||||
\ the terminal; no message is given. The data stack is left as it is.
|
||||
\ NODE-ERROR, by name: how a definition written in FORTH raises an error.
|
||||
|
||||
@@ -65,6 +65,12 @@ extern const v4_image v4_capsule_image;
|
||||
* back to, and FORGET will not go below it. */
|
||||
void v4_image_seal(v4_node *n, const v4_image *im);
|
||||
|
||||
/* The dictionary hash of a node started from `im`: FNV-1a over its memory
|
||||
* below HERE, a cell at a time, low byte first, and then LATEST. It does
|
||||
* not depend on the machine: every build of one commit, with one cell
|
||||
* width, gives the same hash for the same dictionary. */
|
||||
uint64_t v4_image_dict_hash(const v4_node *n, const v4_image *im);
|
||||
|
||||
/* A node newly born (node.h), with the registers the nucleus expects of the
|
||||
* node it runs on -- and `disk`, `blocks` blocks of 1024 bytes, as its block
|
||||
* storage, or none if `disk` is 0. Its memory is empty and it is blocked
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/* manage.h -- what Hera asks of whoever holds the fabric.
|
||||
* docs/v4.0.0/MESH.md section 9.
|
||||
*
|
||||
* Hera decides which nodes exist and are awake (MESH.md ruling 7). She is
|
||||
* a node like any other and cannot make one: she asks, by a write to the
|
||||
* port where her requests go (ENGINE.md 3.3), and the write blocks her until
|
||||
* she is answered. Each request is a word on her, made with KERNEL-WORD
|
||||
* (v4/capsule/compile.v4); its arguments are what it finds on her data
|
||||
* stack and its results are what is left there.
|
||||
*
|
||||
* These are the requests, in order. The n-th is request `first + n`, where
|
||||
* `first` is whatever number the host gives the first of them:
|
||||
*
|
||||
* NODE-ME ( -- place ) the place of the node that asks
|
||||
* NODE-BORN ( -- place | -1 ) a node is born, empty, listening at
|
||||
* its ports; -1: there is no room
|
||||
* NODE-WIRE ( a pa b pb -- flag ) port pa of the node at a is wired
|
||||
* to port pb of the node at b
|
||||
* NODE-UNWIRE ( a pa -- ) what is on that port is cut
|
||||
* NODE-SLEEP ( place -- ) it executes nothing until woken
|
||||
* NODE-WAKE ( place -- )
|
||||
* NODE-KILL ( place -- ) it is removed, whatever it was doing
|
||||
* NODE-PARITY ( place number -- ) the host records the node's parity:
|
||||
* its number and its dictionary hash
|
||||
* CAPSULE-OPEN ( baddr u -- flag ) the capsule of that name is found
|
||||
* and checked, and is read from its
|
||||
* start; 0: there is none, or it is
|
||||
* not what it says it is
|
||||
* CAPSULE-CELL ( -- w flag ) the next cell of a capsule of F18
|
||||
* code; 0 0 when there are no more
|
||||
* CAPSULE-LINE ( baddr -- u | -1 ) the next line of a capsule of
|
||||
* FORTH blocks, put at baddr: at
|
||||
* most 64 characters; -1 at its end
|
||||
*
|
||||
* A place is where a node is in the fabric (fabric.h); it is the host's
|
||||
* name for the node. A node's number, which messages are addressed by, is
|
||||
* the nodes' own affair: Hera gives a newborn its number by telling it.
|
||||
*
|
||||
* Only the node the host has wired to this is answered, so only Hera's
|
||||
* requests are honoured. The unit rule (MESH.md ruling 5) is not here: it
|
||||
* is what Hera does with these, in her capsule.
|
||||
*
|
||||
* Nothing here uses the C library: the bare-metal kernel links this file.
|
||||
*/
|
||||
#ifndef V4_MANAGE_H
|
||||
#define V4_MANAGE_H
|
||||
|
||||
#include "v4/fabric.h"
|
||||
|
||||
#define V4_MANAGE_WORDS 11
|
||||
extern const char *const v4_manage_words[V4_MANAGE_WORDS];
|
||||
#define V4_MANAGE_LINE 64 /* the most characters in a line of a block */
|
||||
|
||||
typedef struct {
|
||||
v4_fabric *f;
|
||||
|
||||
/* A node is born: the host finds it room, adds it to the fabric and
|
||||
* gives it what a node's hardware gives it. Its place, or -1. */
|
||||
int (*born)(void *self);
|
||||
/* The node that was at `place` has been removed: its room is free. */
|
||||
void (*gone)(void *self, unsigned place, v4_fabric_node *was);
|
||||
/* The capsule called `name`, `len` characters: found, its hash and
|
||||
* signature checked, and to be read from its start. 1, or 0. */
|
||||
int (*open)(void *self, const char *name, unsigned len);
|
||||
/* The next cell of the open capsule: 1 with it in *w, 0 at its end. */
|
||||
int (*cell)(void *self, v4_cell *w);
|
||||
/* The next line of the open capsule that is part of a block, into
|
||||
* `text`, at most V4_MANAGE_LINE characters: its length, or -1. */
|
||||
int (*line)(void *self, char *text);
|
||||
/* The node at `place`, which Hera calls `number`: record its parity. */
|
||||
void (*parity)(void *self, unsigned place, v4_cell number, const v4_node *n);
|
||||
void *self;
|
||||
} v4_manage;
|
||||
|
||||
/* The node at `place` has written request `n` (0 is NODE-ME) to the port
|
||||
* its requests go to. Its arguments are taken from its data stack and its
|
||||
* results left there. Returns 1; 0 if `n` is not one of the requests, and
|
||||
* then nothing has been done. */
|
||||
int v4_manage_serve(const v4_manage *m, unsigned place, unsigned n);
|
||||
|
||||
#endif /* V4_MANAGE_H */
|
||||
+6
-1
@@ -180,8 +180,13 @@ unsigned v4_fabric_step(v4_fabric *f)
|
||||
{
|
||||
unsigned i, done = 0;
|
||||
|
||||
/* a node that is blocked fetches nothing, and is looked about for when
|
||||
* it is next to execute */
|
||||
for (i = 0; i < f->capacity; i++)
|
||||
if (awake(f, i)) look_about(f, i);
|
||||
if (awake(f, i)) {
|
||||
const v4_node *n = &f->place[i].node->n;
|
||||
if (!n->stopped && !n->asking && !(n->reading && !n->given)) look_about(f, i);
|
||||
}
|
||||
|
||||
for (i = 0; i < f->capacity; i++)
|
||||
if (awake(f, i)) done += v4_exec_step_word(&f->place[i].node->n, &f->place[i].node->es, &f->place[i].node->h);
|
||||
|
||||
@@ -35,3 +35,30 @@ void v4_image_seal(v4_node *n, const v4_image *im)
|
||||
n->mem[im->boot_cells + 1] = n->mem[im->latest];
|
||||
n->mem[im->fence] = (n->mem[im->dp] + 3) / 4;
|
||||
}
|
||||
|
||||
/* FNV-1a over the node's memory below HERE, a cell at a time, low byte
|
||||
* first, and then LATEST. */
|
||||
#define FNV_OFFSET 0xcbf29ce484222325ULL
|
||||
#define FNV_PRIME 0x00000100000001b3ULL
|
||||
|
||||
static uint64_t hash_cell(uint64_t h, v4_cell c)
|
||||
{
|
||||
v4_ucell u = (v4_ucell)c;
|
||||
unsigned i;
|
||||
for (i = 0; i < V4_CELL_BITS / 8; i++) {
|
||||
h ^= (uint64_t)((u >> (8 * i)) & 0xffu);
|
||||
h *= FNV_PRIME;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
uint64_t v4_image_dict_hash(const v4_node *n, const v4_image *im)
|
||||
{
|
||||
uint64_t h = FNV_OFFSET;
|
||||
v4_cell here = (n->mem[im->dp] + 3) / 4, k;
|
||||
|
||||
if (here < 0) here = 0;
|
||||
if (here > (v4_cell)V4_NODE_WORDS) here = (v4_cell)V4_NODE_WORDS;
|
||||
for (k = 0; k < here; k++) h = hash_cell(h, n->mem[k]);
|
||||
return hash_cell(h, n->mem[im->latest]);
|
||||
}
|
||||
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/* manage.c -- what Hera asks of whoever holds the fabric. See manage.h.
|
||||
*
|
||||
* Nothing here uses the C library: the bare-metal kernel links this file.
|
||||
*/
|
||||
#include "v4/manage.h"
|
||||
|
||||
const char *const v4_manage_words[V4_MANAGE_WORDS] = {
|
||||
"NODE-ME", "NODE-BORN", "NODE-WIRE", "NODE-UNWIRE", "NODE-SLEEP", "NODE-WAKE", "NODE-KILL", "NODE-PARITY",
|
||||
"CAPSULE-OPEN", "CAPSULE-CELL", "CAPSULE-LINE"
|
||||
};
|
||||
|
||||
/* A character of the node's memory: four to a cell, the first lowest, as
|
||||
* the nucleus keeps text (v4/capsule/core.v4). */
|
||||
static int in_memory(v4_cell baddr) { return baddr >= 0 && (baddr >> 2) < (v4_cell)V4_NODE_WORDS; }
|
||||
static char char_at(const v4_node *n, v4_cell baddr)
|
||||
{
|
||||
return (char)(((v4_ucell)n->mem[baddr >> 2] >> (8 * (unsigned)(baddr & 3))) & 0xFFu);
|
||||
}
|
||||
static void char_put(v4_node *n, v4_cell baddr, char c)
|
||||
{
|
||||
unsigned shift = 8 * (unsigned)(baddr & 3);
|
||||
v4_ucell cell = (v4_ucell)n->mem[baddr >> 2];
|
||||
cell = (cell & ~((v4_ucell)0xFFu << shift)) | ((v4_ucell)(unsigned char)c << shift);
|
||||
n->mem[baddr >> 2] = (v4_cell)cell;
|
||||
}
|
||||
|
||||
static unsigned place_of(v4_cell x) { return x < 0 ? ~0u : (unsigned)x; }
|
||||
|
||||
int v4_manage_serve(const v4_manage *m, unsigned place, unsigned request)
|
||||
{
|
||||
v4_fabric_node *asker = v4_fabric_node_at(m->f, place);
|
||||
v4_node *n;
|
||||
v4_cell a, b, c, d;
|
||||
|
||||
if (!asker || request >= V4_MANAGE_WORDS) return 0;
|
||||
n = &asker->n;
|
||||
|
||||
switch (request) {
|
||||
case 0: /* NODE-ME ( -- place ) */
|
||||
v4_dstack_push(&n->ds, (v4_cell)place);
|
||||
break;
|
||||
case 1: /* NODE-BORN ( -- place | -1 ) */
|
||||
v4_dstack_push(&n->ds, (v4_cell)(m->born ? m->born(m->self) : -1));
|
||||
break;
|
||||
case 2: /* NODE-WIRE ( a pa b pb -- flag ) */
|
||||
d = v4_dstack_pop(&n->ds); c = v4_dstack_pop(&n->ds); b = v4_dstack_pop(&n->ds); a = v4_dstack_pop(&n->ds);
|
||||
v4_dstack_push(&n->ds, v4_fabric_wire(m->f, place_of(a), place_of(b), place_of(c), place_of(d)) ? -1 : 0);
|
||||
break;
|
||||
case 3: /* NODE-UNWIRE ( a pa -- ) */
|
||||
b = v4_dstack_pop(&n->ds); a = v4_dstack_pop(&n->ds);
|
||||
(void)v4_fabric_unwire(m->f, place_of(a), place_of(b));
|
||||
break;
|
||||
case 4: /* NODE-SLEEP ( place -- ) */
|
||||
a = v4_dstack_pop(&n->ds);
|
||||
v4_fabric_sleep(m->f, place_of(a));
|
||||
break;
|
||||
case 5: /* NODE-WAKE ( place -- ) */
|
||||
a = v4_dstack_pop(&n->ds);
|
||||
v4_fabric_wake(m->f, place_of(a));
|
||||
break;
|
||||
case 6: { /* NODE-KILL ( place -- ) */
|
||||
v4_fabric_node *was;
|
||||
a = v4_dstack_pop(&n->ds);
|
||||
if (place_of(a) == place) break; /* not itself: there would be no one to answer */
|
||||
was = v4_fabric_remove(m->f, place_of(a));
|
||||
if (was && m->gone) m->gone(m->self, place_of(a), was);
|
||||
break;
|
||||
}
|
||||
case 7: { /* NODE-PARITY ( place number -- ) */
|
||||
v4_fabric_node *it;
|
||||
b = v4_dstack_pop(&n->ds); a = v4_dstack_pop(&n->ds);
|
||||
it = v4_fabric_node_at(m->f, place_of(a));
|
||||
if (it && m->parity) m->parity(m->self, place_of(a), b, &it->n);
|
||||
break;
|
||||
}
|
||||
case 8: { /* CAPSULE-OPEN ( baddr u -- flag ) */
|
||||
char name[64];
|
||||
unsigned i, len;
|
||||
b = v4_dstack_pop(&n->ds); a = v4_dstack_pop(&n->ds);
|
||||
len = b < 0 ? 0u : (unsigned)b;
|
||||
if (len == 0 || len > sizeof name || !in_memory(a) || !in_memory(a + (v4_cell)len - 1) || !m->open) {
|
||||
v4_dstack_push(&n->ds, 0);
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < len; i++) name[i] = char_at(n, a + (v4_cell)i);
|
||||
v4_dstack_push(&n->ds, m->open(m->self, name, len) ? -1 : 0);
|
||||
break;
|
||||
}
|
||||
case 9: /* CAPSULE-CELL ( -- w flag ) */
|
||||
a = 0;
|
||||
if (m->cell && m->cell(m->self, &a)) { v4_dstack_push(&n->ds, a); v4_dstack_push(&n->ds, -1); }
|
||||
else { v4_dstack_push(&n->ds, 0); v4_dstack_push(&n->ds, 0); }
|
||||
break;
|
||||
case 10: { /* CAPSULE-LINE ( baddr -- u | -1 ) */
|
||||
char text[V4_MANAGE_LINE];
|
||||
int len, i;
|
||||
a = v4_dstack_pop(&n->ds);
|
||||
len = m->line ? m->line(m->self, text) : -1;
|
||||
if (len > V4_MANAGE_LINE) len = V4_MANAGE_LINE;
|
||||
if (len > 0 && (!in_memory(a) || !in_memory(a + len - 1))) len = -1;
|
||||
for (i = 0; i < len; i++) char_put(n, a + i, text[i]);
|
||||
v4_dstack_push(&n->ds, (v4_cell)len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
+1
-21
@@ -56,29 +56,9 @@ static void say_hex(const v4_boot *b, uint64_t v)
|
||||
|
||||
/* ---- hashing: FNV-1a, 64 bits, as kernel/src/vm/parity.c ----------------- */
|
||||
|
||||
#define FNV_OFFSET 0xcbf29ce484222325ULL
|
||||
#define FNV_PRIME 0x00000100000001b3ULL
|
||||
|
||||
static uint64_t hash_cell(uint64_t h, v4_cell c)
|
||||
{
|
||||
v4_ucell u = (v4_ucell)c;
|
||||
unsigned i;
|
||||
for (i = 0; i < V4_CELL_BITS / 8; i++) {
|
||||
h ^= (uint64_t)((u >> (8 * i)) & 0xffu);
|
||||
h *= FNV_PRIME;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
uint64_t v4_boot_dict_hash(const v4_node *n, const v4_image *im)
|
||||
{
|
||||
uint64_t h = FNV_OFFSET;
|
||||
v4_cell here = (n->mem[im->dp] + 3) / 4, k;
|
||||
|
||||
if (here < 0) here = 0;
|
||||
if (here > (v4_cell)V4_NODE_WORDS) here = (v4_cell)V4_NODE_WORDS;
|
||||
for (k = 0; k < here; k++) h = hash_cell(h, n->mem[k]);
|
||||
return hash_cell(h, n->mem[im->latest]);
|
||||
return v4_image_dict_hash(n, im);
|
||||
}
|
||||
|
||||
/* how many words FORTH holds: the list from LATEST, each entry's link in the
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
#define MQ_HEAD (BUF0_W - 35) /* the messages waiting to be dealt with: where the oldest begins, */
|
||||
#define MQ_TAIL (BUF0_W - 36) /* where the next will go, */
|
||||
#define MQ_COUNT (BUF0_W - 37) /* and how many cells they take */
|
||||
#define AWAIT_FROM (BUF0_W - 39) /* the node whose word of how text ended is waited for */
|
||||
#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 */
|
||||
@@ -194,6 +195,7 @@ 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, "(AWAIT-FROM)", AWAIT_FROM);
|
||||
v4_text_constant(tx, "(MQ)", MQ_W);
|
||||
v4_text_constant(tx, "(MQ-END)", MQ_W + MQ_CELLS);
|
||||
v4_text_constant(tx, "-MQ-ROOM", -(v4_cell)(MQ_CELLS - 7)); /* cells taken + a text's words + this is below zero while the message fits */
|
||||
|
||||
@@ -0,0 +1,386 @@
|
||||
/* test_host_unit.c -- five nodes come up from nothing.
|
||||
* docs/v4.0.0/MESH.md section 9, step 5; acceptance 1 and 2.
|
||||
*
|
||||
* [11]---[12]
|
||||
* | \ / | the four outer nodes are each on a
|
||||
* console --1 [10] | port of Hera, 10, and joined in a
|
||||
* kernel --0 | / \ | square; the corners across from each
|
||||
* [13]---[14] other are not wired
|
||||
*
|
||||
* Hera is born empty and is given the nucleus through the port her requests
|
||||
* go to, as the boot gives it (v4/system/boot.c). She is told who she is,
|
||||
* given the words that ask for nodes (v4/include/v4/manage.h), FORTH-79,
|
||||
* POST, and her own capsule, capsules/v4/hera.4th. Then she is told
|
||||
* `10 UNIT`, and everything after that is hers: she asks for four nodes,
|
||||
* wires them, sends each the nucleus cell by cell through the port that
|
||||
* joins them and then FORTH-79 and POST a line at a time, and joins them.
|
||||
*
|
||||
* What stands in here for the host: the capsules are read from the files
|
||||
* the build bakes in, and the nucleus is assembled as mkimage assembles it.
|
||||
* Each node has a disk of its own, attached when it is born, as the boot's
|
||||
* one node has; storage through the ports is step 6.
|
||||
*/
|
||||
#include "v4/capsule.h"
|
||||
#include "v4/fabric.h"
|
||||
#include "v4/image.h"
|
||||
#include "v4/manage.h"
|
||||
#include "v4/message.h"
|
||||
#include "v4/text.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "host_map.h"
|
||||
|
||||
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)
|
||||
|
||||
#define PLACES 10u
|
||||
#define DISK_BLOCKS 64u
|
||||
static v4_text tx;
|
||||
static v4_node nucleus; /* the nucleus, assembled: what the capsule of F18 code is written from */
|
||||
static v4_cell w_idle, w_fault;
|
||||
static v4_fabric_node pool[PLACES];
|
||||
static int pool_used[PLACES];
|
||||
static unsigned char disks[PLACES][DISK_BLOCKS * V4_BLOCK_BYTES];
|
||||
static v4_place places[PLACES];
|
||||
static v4_fabric f;
|
||||
static v4_image im; /* only what the dictionary hash needs of it */
|
||||
|
||||
/* ---- the capsules ---- */
|
||||
static v4_cell f18[2u * V4_NODE_WORDS + 64u]; /* the nucleus as a capsule of F18 code */
|
||||
static unsigned f18_count;
|
||||
static void f18_put(void *self, v4_cell w) { (void)self; if (f18_count < sizeof f18 / sizeof f18[0]) f18[f18_count++] = w; }
|
||||
|
||||
typedef struct { char *text; long len, at; int in_block; unsigned cells_at; int is_f18; } reader;
|
||||
|
||||
static int read_file(reader *r, const char *name)
|
||||
{
|
||||
char path[600];
|
||||
FILE *in;
|
||||
snprintf(path, sizeof path, "%s/../../capsules/v4/%s", V4_CAPSULE_DIR, name);
|
||||
in = fopen(path, "rb");
|
||||
if (!in) return 0;
|
||||
free(r->text);
|
||||
fseek(in, 0, SEEK_END); r->len = ftell(in); fseek(in, 0, SEEK_SET);
|
||||
r->text = (char *)malloc((size_t)r->len + 1);
|
||||
if (!r->text || fread(r->text, 1, (size_t)r->len, in) != (size_t)r->len) { fclose(in); return 0; }
|
||||
fclose(in);
|
||||
r->at = 0; r->in_block = 0; r->is_f18 = 0;
|
||||
return 1;
|
||||
}
|
||||
/* the next line of a block that has anything on it: its length, or -1 */
|
||||
static int next_line(reader *r, char *text)
|
||||
{
|
||||
while (r->at < r->len) {
|
||||
const char *p = r->text + r->at;
|
||||
long n = 0;
|
||||
while (r->at + n < r->len && p[n] != '\n') n++;
|
||||
r->at += n + 1;
|
||||
if (n >= 7 && memcmp(p, "Block ", 6) == 0 && p[6] >= '0' && p[6] <= '9') { r->in_block = 1; continue; }
|
||||
if (n && p[n - 1] == '\r') n--;
|
||||
if (!r->in_block || n == 0) continue;
|
||||
if (n > V4_MANAGE_LINE) return -1;
|
||||
memcpy(text, p, (size_t)n);
|
||||
return (int)n;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ---- what Hera's requests are answered with ---- */
|
||||
static reader hera_reads; /* the capsule Hera has open */
|
||||
static unsigned born_count, parity_count;
|
||||
static v4_cell parity_number[8];
|
||||
static uint64_t parity_hash[8];
|
||||
|
||||
static int host_born(void *self)
|
||||
{
|
||||
unsigned k;
|
||||
int place;
|
||||
(void)self;
|
||||
for (k = 0; k < PLACES && pool_used[k]; k++) { }
|
||||
if (k == PLACES) return -1;
|
||||
place = v4_fabric_add(&f, &pool[k], PORT);
|
||||
if (place < 0) return -1;
|
||||
pool_used[k] = 1;
|
||||
born_count++;
|
||||
/* what a node's hardware gives it; there is nothing in its memory */
|
||||
v4_node_stack_regs_attach(&pool[k].n, DSTACK_REG, RSTACK_REG);
|
||||
v4_node_error_attach(&pool[k].n, NODE_ERROR);
|
||||
v4_node_fault_attach(&pool[k].n, w_fault);
|
||||
memset(disks[k], 0, sizeof disks[k]);
|
||||
v4_node_storage_attach(&pool[k].n, STORAGE_REG, disks[k], DISK_BLOCKS);
|
||||
return place;
|
||||
}
|
||||
static void host_gone(void *self, unsigned place, v4_fabric_node *was)
|
||||
{
|
||||
(void)self; (void)place;
|
||||
pool_used[was - pool] = 0;
|
||||
}
|
||||
static int host_open(void *self, const char *name, unsigned len)
|
||||
{
|
||||
(void)self;
|
||||
if (len == 17 && memcmp(name, "v4:nucleus-64.f18", 17) == 0) { hera_reads.is_f18 = 1; hera_reads.cells_at = 0; return 1; }
|
||||
if (len == 14 && memcmp(name, "v4:forth79.4th", 14) == 0) return read_file(&hera_reads, "forth79.4th");
|
||||
if (len == 13 && memcmp(name, "v4:post79.4th", 13) == 0) return read_file(&hera_reads, "post79.4th");
|
||||
return 0;
|
||||
}
|
||||
static int host_cell(void *self, v4_cell *w)
|
||||
{
|
||||
(void)self;
|
||||
if (!hera_reads.is_f18 || hera_reads.cells_at >= f18_count) return 0;
|
||||
*w = f18[hera_reads.cells_at++];
|
||||
return 1;
|
||||
}
|
||||
static int host_line(void *self, char *text)
|
||||
{
|
||||
(void)self;
|
||||
return hera_reads.is_f18 ? -1 : next_line(&hera_reads, text);
|
||||
}
|
||||
static void host_parity(void *self, unsigned place, v4_cell number, const v4_node *n)
|
||||
{
|
||||
(void)self; (void)place;
|
||||
if (parity_count < 8) { parity_number[parity_count] = number; parity_hash[parity_count] = v4_image_dict_hash(n, &im); }
|
||||
parity_count++;
|
||||
}
|
||||
static const v4_manage manage = { &f, host_born, host_gone, host_open, host_cell, host_line, host_parity, 0 };
|
||||
|
||||
/* ---- the kernel: a device on Hera's port 0 ---- */
|
||||
#define FIRST_REQUEST 1 /* the number of NODE-ME on her */
|
||||
static unsigned hera_place, given_cells, refused;
|
||||
static int kernel_give(void *self, v4_cell *value) /* the nucleus, to a node that has nothing in it */
|
||||
{
|
||||
(void)self;
|
||||
if (given_cells >= f18_count) return 0;
|
||||
*value = f18[given_cells++];
|
||||
return 1;
|
||||
}
|
||||
static int kernel_take(void *self, v4_cell value) /* a request */
|
||||
{
|
||||
(void)self;
|
||||
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 };
|
||||
|
||||
/* ---- the console: a device on Hera's port 1 ---- */
|
||||
#define CONSOLE_ID 1
|
||||
static v4_message going, coming;
|
||||
static unsigned going_at;
|
||||
static char printed[8192]; /* what has come back since the console last sent */
|
||||
static unsigned printed_len;
|
||||
static v4_cell printed_from;
|
||||
static int ended;
|
||||
static v4_cell ended_from, ended_how;
|
||||
static unsigned post_passed[32]; /* by node number: POST tallies with no failures seen from it */
|
||||
static unsigned post_other; /* tallies with failures */
|
||||
|
||||
static int console_give(void *self, v4_cell *value)
|
||||
{
|
||||
(void)self;
|
||||
if (going_at >= going.count) return 0;
|
||||
*value = going.word[going_at++];
|
||||
return 1;
|
||||
}
|
||||
static int console_take(void *self, v4_cell value)
|
||||
{
|
||||
(void)self;
|
||||
if (v4_message_word(&coming, value)) {
|
||||
if (v4_message_type(&coming) == V4_MSG_OUTPUT) {
|
||||
unsigned i, chars = v4_message_length(&coming), start = printed_len;
|
||||
for (i = 0; i < chars && printed_len + 1 < sizeof printed; i++) printed[printed_len++] = v4_message_char(&coming, i);
|
||||
printed[printed_len] = 0;
|
||||
printed_from = v4_message_from(&coming);
|
||||
if (strstr(printed + start, "PARITY:V4_POST")) {
|
||||
if (strstr(printed + start, " fail=0\n") && printed_from >= 0 && printed_from < 32) post_passed[printed_from]++;
|
||||
else post_other++;
|
||||
}
|
||||
} else if (v4_message_type(&coming) == V4_MSG_DONE) {
|
||||
ended++;
|
||||
ended_from = v4_message_from(&coming);
|
||||
ended_how = coming.word[V4_MSG_HEADER];
|
||||
}
|
||||
coming.count = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
static const v4_device console = { console_take, console_give, 0 };
|
||||
|
||||
/* 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
|
||||
* fault handler at the next. */
|
||||
static int any_running(void)
|
||||
{
|
||||
unsigned k;
|
||||
for (k = 0; k < PLACES; k++) {
|
||||
const v4_fabric_node *x = v4_fabric_node_at(&f, k);
|
||||
if (x && !places[k].asleep && !x->n.stopped && !x->n.asking && !(x->n.reading && !x->n.given)) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send text to a node and let everything that follows from it happen. */
|
||||
static const char *tell(v4_cell node, const char *text)
|
||||
{
|
||||
unsigned long steps = 0;
|
||||
printed_len = 0; printed[0] = 0; printed_from = -1;
|
||||
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 < 4000000000ul && (v4_fabric_step(&f) != 0 || any_running())) steps++;
|
||||
if (steps >= 4000000000ul) return "(still running)";
|
||||
return printed;
|
||||
}
|
||||
static int told(v4_cell node, const char *text) { return tell(node, text)[0] == 0 && ended == 1 && ended_how == V4_TEXT_COMPLETED; }
|
||||
|
||||
/* Every line of a capsule of FORTH blocks, told to a node from the console:
|
||||
* how many lines did not complete. */
|
||||
static unsigned load(v4_cell node, const char *file)
|
||||
{
|
||||
static reader r;
|
||||
char line[V4_MANAGE_LINE + 1];
|
||||
unsigned bad = 0;
|
||||
int n;
|
||||
if (!read_file(&r, file)) return 1;
|
||||
while ((n = next_line(&r, line)) >= 0) {
|
||||
line[n] = 0;
|
||||
(void)tell(node, line);
|
||||
if (ended != 1 || ended_how != V4_TEXT_COMPLETED) { if (!bad) { const v4_node *q = &pool[0].n; printf(" %s: \"%s\" -> \"%s\" ended=%d how=%ld; stopped=%d asking=%d port=%u reading=%d rport=%u p=%ld fault=%u@%ld\n", file, line, printed, ended, (long)ended_how, q->stopped, q->asking, q->ask_port, q->reading, q->read_port, (long)q->p, q->fault_kind, (long)q->fault_addr); } bad++; }
|
||||
}
|
||||
return bad;
|
||||
}
|
||||
|
||||
static v4_node *node_numbered(v4_cell number)
|
||||
{
|
||||
unsigned k;
|
||||
for (k = 0; k < PLACES; k++) if (v4_fabric_node_at(&f, k) && v4_fabric_node_at(&f, k)->n.mem[ME] == number) return &v4_fabric_node_at(&f, k)->n;
|
||||
return 0;
|
||||
}
|
||||
static v4_uheat_t clock_of(v4_cell number)
|
||||
{
|
||||
unsigned k;
|
||||
for (k = 0; k < PLACES; k++) if (v4_fabric_node_at(&f, k) && v4_fabric_node_at(&f, k)->n.mem[ME] == number) return v4_fabric_node_at(&f, k)->es.anticlock;
|
||||
return 0;
|
||||
}
|
||||
static int waiting(v4_cell number)
|
||||
{
|
||||
const v4_node *n = node_numbered(number);
|
||||
return n && n->reading && !n->given && n->read_port == V4_PORT_ANY;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unsigned i;
|
||||
int place;
|
||||
|
||||
printf("v4 unit tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS);
|
||||
if (V4_CELL_BITS != 64) {
|
||||
/* The capsules the unit is built from are the 64-bit products': the
|
||||
* expected results in post79.4th are v3's, on 64-bit cells, and the
|
||||
* nucleus capsule is nucleus-64. POST has never been run on 32-bit
|
||||
* cells (docs/v4.0.0/MESH.md, step 5). */
|
||||
printf(" not run at this width: the capsules are the 64-bit products'\n");
|
||||
return 0;
|
||||
}
|
||||
{
|
||||
static const char *const files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4", "compile.v4", "quit.v4", "forth.v4",
|
||||
"numout.v4", "words.v4", "system.v4", "qmath.v4", "blocks.v4", "log.v4", "acl.v4" };
|
||||
CHECK(host_load(&tx, &nucleus, files, 14), "the nucleus assembles");
|
||||
}
|
||||
CHECK(v4_text_finish(&tx), "everything is defined: %s", v4_text_error(&tx));
|
||||
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
|
||||
w_idle = v4_text_word(&tx, "(IDLE)");
|
||||
w_fault = v4_text_word(&tx, "(FAULTS)");
|
||||
nucleus.mem[DP] = DICT_W * 4; /* the variables, as at switch-on (tools/mkimage.c) */
|
||||
nucleus.mem[LATEST] = v4_text_latest(&tx);
|
||||
nucleus.mem[CFP] = CFS_W;
|
||||
nucleus.mem[BASE] = 10;
|
||||
nucleus.mem[FENCE] = DICT_W;
|
||||
nucleus.mem[BOOT_CELLS] = nucleus.mem[DP];
|
||||
nucleus.mem[BOOT_CELLS + 1] = nucleus.mem[LATEST];
|
||||
nucleus.mem[LOG_LEVEL] = 2;
|
||||
nucleus.mem[CONTEXT] = LATEST;
|
||||
nucleus.mem[CURRENT] = LATEST;
|
||||
nucleus.mem[SRC] = TIB;
|
||||
nucleus.mem[LINE_STATUS] = 1;
|
||||
HOST_MESSAGES_EMPTY(&nucleus);
|
||||
(void)v4_capsule_write(&nucleus, w_idle, f18_put, 0);
|
||||
CHECK(f18_count > 1000 && f18_count < sizeof f18 / sizeof f18[0], "the nucleus is a capsule of F18 code: %u cells", f18_count);
|
||||
memset(&im, 0, sizeof im);
|
||||
im.dp = DP; im.latest = LATEST;
|
||||
|
||||
/* ---- Hera is born empty, and takes in the nucleus ---- */
|
||||
v4_fabric_init(&f, places, PLACES);
|
||||
place = host_born(0);
|
||||
CHECK(place == 0, "a node is born");
|
||||
hera_place = (unsigned)place;
|
||||
for (i = 0; i < 4096 && pool[0].n.mem[i] == 0; i++) { }
|
||||
CHECK(i == 4096 && pool[0].n.p == PORT + (v4_cell)V4_PORT_ANY, "with nothing in it, listening at its ports");
|
||||
CHECK(v4_fabric_wire_device(&f, hera_place, 0, &kernel) && v4_fabric_wire_device(&f, hera_place, 1, &console), "the kernel is on its port 0 and the console on its port 1");
|
||||
going.count = 0; going_at = 0;
|
||||
for (i = 0; i < 400000 && v4_fabric_step(&f) != 0; i++) { }
|
||||
CHECK(given_cells == f18_count && pool[0].n.reading && pool[0].n.read_port == V4_PORT_ANY && !pool[0].n.stopped,
|
||||
"it takes in the nucleus through its port and waits for a message: %u of %u cells", given_cells, f18_count);
|
||||
|
||||
CHECK(told(0, "10 (ME) ! 1 (CONSOLE) ! 1 1 ROUTE") && ended_from == 10, "it is told it is 10, and where the console is");
|
||||
{
|
||||
char text[600];
|
||||
size_t at = 0;
|
||||
for (i = 0; i < V4_MANAGE_WORDS; i++) at += (size_t)snprintf(text + at, sizeof text - at, "%u KERNEL-WORD %s ", FIRST_REQUEST + i, v4_manage_words[i]);
|
||||
CHECK(told(10, text), "and given the words that ask for nodes and capsules: \"%s\"", printed);
|
||||
}
|
||||
CHECK(strcmp(tell(10, "NODE-ME ."), "0 ") == 0 && refused == 0, "it asks where it is, and is answered");
|
||||
CHECK(load(10, "forth79.4th") == 0, "it takes in FORTH-79");
|
||||
CHECK(load(10, "post79.4th") == 0 && post_passed[10] == 1 && post_other == 0, "and passes POST");
|
||||
CHECK(load(10, "hera.4th") == 0, "and its own capsule, which makes it Hera");
|
||||
CHECK(told(10, "(SEAL)"), "that is the system it comes back to");
|
||||
|
||||
/* ---- she births the unit ---- */
|
||||
born_count = 0;
|
||||
{
|
||||
const char *r = tell(10, "10 UNIT");
|
||||
CHECK(ended == 1 && ended_how == V4_TEXT_COMPLETED && ended_from == 10, "Hera births four nodes and joins them: \"%s\"", r);
|
||||
}
|
||||
CHECK(born_count == 4 && refused == 0, "four nodes were asked for: %u", born_count);
|
||||
CHECK(post_passed[11] == 1 && post_passed[12] == 1 && post_passed[13] == 1 && post_passed[14] == 1 && post_other == 0,
|
||||
"each took in the nucleus and FORTH-79 through its port from Hera, and passed POST: %u %u %u %u, %u failed",
|
||||
post_passed[11], post_passed[12], post_passed[13], post_passed[14], post_other);
|
||||
CHECK(parity_count == 4 && parity_number[0] == 11 && parity_number[1] == 12 && parity_number[2] == 13 && parity_number[3] == 14, "the parity of each was recorded: %u", parity_count);
|
||||
CHECK(parity_hash[0] == parity_hash[1] && parity_hash[1] == parity_hash[2] && parity_hash[2] == parity_hash[3],
|
||||
"and their dictionary hashes are the same: %016llx %016llx %016llx %016llx",
|
||||
(unsigned long long)parity_hash[0], (unsigned long long)parity_hash[1], (unsigned long long)parity_hash[2], (unsigned long long)parity_hash[3]);
|
||||
printf(" the four outer nodes: dict_hash=0x%016llx\n", (unsigned long long)parity_hash[0]);
|
||||
CHECK(parity_hash[0] != v4_image_dict_hash(node_numbered(10), &im), "Hera's is not: she has her own capsule");
|
||||
CHECK(waiting(10) && waiting(11) && waiting(12) && waiting(13) && waiting(14), "all five are waiting at their ports");
|
||||
{
|
||||
v4_uheat_t c[5];
|
||||
for (i = 0; i < 5; i++) c[i] = clock_of(10 + (v4_cell)i);
|
||||
for (i = 0; i < 200; i++) (void)v4_fabric_step(&f);
|
||||
for (i = 0; i < 5 && clock_of(10 + (v4_cell)i) == c[i]; i++) { }
|
||||
CHECK(i == 5, "and execute nothing while they wait");
|
||||
}
|
||||
|
||||
/* ---- they talk ---- */
|
||||
CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && printed_from == 10, "a line typed at the console is done by Hera");
|
||||
for (i = 11; i <= 14; i++) {
|
||||
char text[32], want[16];
|
||||
snprintf(text, sizeof text, "%u 100 * .", i);
|
||||
snprintf(want, sizeof want, "%u ", i * 100);
|
||||
CHECK(strcmp(tell((v4_cell)i, text), want) == 0 && printed_from == (v4_cell)i && ended_from == (v4_cell)i, "text for node %u is done there and what it prints comes back: \"%s\"", i, printed);
|
||||
}
|
||||
CHECK(strcmp(tell(13, "3 4 U* . ."), "0 12 ") == 0, "an outer node has FORTH-79: U* is a word of the capsule");
|
||||
CHECK(strcmp(tell(13, ": SQ DUP * ; 7 SQ ."), "49 ") == 0 && strstr(tell(12, "7 SQ ."), "UNKNOWN WORD") != NULL, "each has its own dictionary");
|
||||
CHECK(strstr(tell(13, "COLD"), "Cold Start") != NULL && strcmp(tell(13, "3 4 U* . ."), "0 12 ") == 0 && strstr(tell(13, "7 SQ ."), "UNKNOWN WORD") != NULL,
|
||||
"COLD on it comes back to the nucleus and FORTH-79");
|
||||
{
|
||||
v4_uheat_t hera = clock_of(10);
|
||||
CHECK(strcmp(tell(11, ": T S\" 65 EMIT\" 12 SEND ; T"), "A") == 0 && printed_from == 12, "an outer node sends text to the one beside it: \"%s\"", printed);
|
||||
hera = clock_of(10);
|
||||
CHECK(strcmp(tell(11, ": X S\" 66 EMIT\" 14 SEND ; X"), "B") == 0 && printed_from == 14, "and to the one across from it, which it is not wired to: \"%s\"", printed);
|
||||
CHECK(clock_of(10) > hera, "that went by a node in between");
|
||||
}
|
||||
CHECK(strcmp(tell(10, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(11, "(LOST) @ ."), "0 ") == 0 && strcmp(tell(14, "(LOST) @ ."), "0 ") == 0, "nothing was let go");
|
||||
|
||||
printf(" %d checks, %d failures\n", checks, failures);
|
||||
return failures != 0;
|
||||
}
|
||||
Reference in New Issue
Block a user