feat(v4.0.0): a node asks its kernel by a blocking write to its port

ENGINE.md step 2, the carrier.  Ruled 2026-10-05 (V3-PARITY.md 1i), on
DECOMPOSITION.md section 6: a write to a port blocks until the neighbour
reads.

- node: v4_node_port_attach, v4_node_port_served; a store to the port keeps
  the value as the request and blocks the node
- exec: a blocked node executes nothing; served, it goes on from the opcode
  after the store, in the same instruction word; a fault meanwhile abandons
  the rest of the word
- compile.v4: n KERNEL-WORD name makes a word whose body writes n to the
  port; its arguments and results are on the data stack
- boot: the kernel's words are made by handing the node text, and requests
  are served between the node's opcodes; one no one serves is error 12
- BYE, the first kernel word: hosted it leaves the program, as hosted v3;
  on the lone node it is v3's cold restart
- ENGINE.md 3a: multiuser, multitasking, preemptive and cooperative, and
  what that asks of the engine

Verified: make -C v4 test passes at both widths, with tests/test_port.c;
hosted-check passes on three ISAs; clean qemu with STARFORTH_V4=1 on amd64,
aarch64 and riscv64 passes POST with the same hashes as hosted, and a
kernel word no one serves and BYE typed at each prompt are answered
(logs/20261005-185506, -185734, -190101; -185234 is an amd64 run in which
those two lines were not typed).

Not done: v3's own C functions serving a node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-05 19:03:03 -04:00
co-authored by Claude Opus 5.5
parent 5f1f60fc84
commit 8df6c16766
21 changed files with 908 additions and 21 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-10-05T22:15:04Z -->
<!-- Generated by mkcapsule --manifest 2026-10-05T23:00:23Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
+60 -5
View File
@@ -85,10 +85,14 @@ framebuffer words. It takes its arguments from the VM's data stack.
*Ruled:* a node asks for a block by number and the kernel decides the
rest; a node sends a message by asking.
*v4:* a kernel word is a dictionary entry on the node whose code hands a
request to the kernel, with its arguments on the node's data stack. The
C functions are v3's. **How the request is carried is not designed yet**
and is the first thing step 2 settles.
*v4, ruled 2026-10-05:* a kernel word is an ordinary dictionary entry on
the node whose body writes its request number to the node's port. The
write blocks the node until the kernel, its neighbour on that port, has
served it (`DECOMPOSITION.md` section 6: "a write blocks until the
neighbour reads"). The kernel serves between the node's opcodes, so the
node is always stopped when C touches it; the function takes its arguments
from the node's data stack and leaves its results there. Kernel words are
made by handing the node text at boot. The C functions are v3's.
### 3.4 The stacks and the dictionary, from the kernel's side
@@ -129,6 +133,41 @@ engine's.
*v3:* `vm_dict_hash_fn`, a hook the birth protocol calls. *v4:* the same
hook, answered from the node's dictionary.
## 3a. Many VMs at once (Captain Bob, 2026-10-05)
> Do not forget that this is multiuser, multitasking, and a hybrid of
> preemptive and cooperative.
And, the same day: "Hera will be the process manager via compudynamics per
node."
What that asks of the engine, and what it has:
- **A node can be stopped between any two instruction words and gone on
with later.** Everything a node is doing is in the node and its
execution state; `v4_exec_step_word` runs one instruction word and
returns. So whoever runs the nodes can take the processor from one at any
word and give it to another: that is the preemptive half, and the engine
already allows it. Nothing may be built that needs a node to run a line,
or a request, to its end without interruption.
- **A node gives way by itself when it writes to its port.** It is blocked
until served (3.3), and while it is blocked another can run: that is the
cooperative half.
- **Each node has its own execution state.** The place a blocked node goes
on from is kept per node (`v4_exec_state`), never in one shared place.
- **Each user is a VM** (`FABRIC-2.md` D.2: "a session IS a VM"), so each is
a node, with its own dictionary, stacks and ACL cards.
What is not designed: who decides which node runs next, and when. In v3
that is the switcher, reading what kernel-Hermes publishes, at the
checkpoint in the inner loop; the ruling makes it Hera's, by compudynamics.
It is step 6's, and section 6 lists it as open.
**The loop that runs one node until its line ends** (`v4_boot_line`,
`v4/system/boot.c`) is the lone node's and the hosted program's, where
there is one node and nothing to share the processor with. It is not how
the kernel will run a fleet, and goes with the lone node at step 4.
## 4. What was built on the detour
| Built 2026-10-05 | What becomes of it |
@@ -156,6 +195,22 @@ its logs.
2. **Asking the kernel, and the stacks from the kernel's side (3.3,
3.4).** Settle how a request is carried. Kernel words callable from a
node.
**The carrier is done, 2026-10-05; the rest is not.** Ruled: a node asks
by a blocking write to a port. Built: the port (`v4/src/node.c`,
`v4_node_port_attach`, `v4_node_port_served`); a blocked node goes on
from the opcode after the store, in the same instruction word
(`v4/src/exec.c`); `KERNEL-WORD` (`v4/capsule/compile.v4`), which makes a
word whose body writes its request number to the port; the boot makes
the kernel's words by handing the node text, and serves requests
(`v4/system/boot.c`). A request no one serves is error 12 on the node.
The one kernel word so far is `BYE`, on both products: hosted it leaves
the program, as hosted v3; on the lone node it is v3's cold restart.
`v4/tests/test_port.c`; six builds agree; `BYE` and an unserved request
typed at each bare-metal prompt. `logs/20261005-185506`, `-185734`,
`-190101`.
**Not done:** v3's own C functions serving a node. They take a `VM *`
and use `vm_push`, `vm_pop` and, in places, the stack's fields directly;
a node has to stand behind that `VM` record first.
3. **A word is being executed (3.5).** The hook at `call`; the ACL's
countdown and recheck; the word's count; `stadium_word_dispatch`.
In-line words become calls. The per-call-target count goes.
@@ -175,10 +230,10 @@ these, a group at a time, with POST after each.
| Open | Before |
|---|---|
| How a node's request to the kernel is carried | Step 2 |
| How the node tells a dictionary entry from a bare address at `call` | Step 3 |
| `EXECUTE`, which enters a word by a return | Step 3 |
| `>R R> R@ I J LEAVE`, which cannot be called | Step 3 |
| Who decides which node runs next, and when: Hera, by compudynamics; preemptive and cooperative | Step 6 |
| The node's safe moment for message delivery and switching | Step 6 |
| `PAD 42 OVER !`, a byte address given to `!` (D-1) | Step 5 |
| Which word patrons' accounts the hosted product keeps, having no Stadium | Step 3 |
+18
View File
@@ -18,6 +18,7 @@
*/
#include "starkernel/v4/sk_v4.h"
#include "starkernel/console.h"
#include "starkernel/arch.h"
#include "v4/boot.h"
#define SK_V4_BLOCKS 64u
@@ -43,6 +44,22 @@ static int sk_v4_key(void)
return c < 0 ? V4_BOOT_KEY_NONE : c;
}
/* THE KERNEL'S WORDS for the lone node. BYE is v3's (mama_word_bye): a cold
* restart. v3's reaps Hera's children first; the lone node is started
* before there is a fleet, so there are none. The rest of the kernel's
* words come when the node is brought up in the VM's place (ENGINE.md
* step 4), where v3's own functions serve them. */
static const char *const sk_v4_words[] = { "BYE" };
static void sk_v4_serve(v4_node *node, unsigned request)
{
(void)node;
if (request == 1) {
console_println("BYE: cold restart");
arch_cold_reset();
}
}
/* Read a line from the keyboard: characters are echoed, backspace rubs one
* out, Enter ends it. Returns its length. */
static unsigned sk_v4_readline(void)
@@ -68,6 +85,7 @@ void sk_v4_run(void)
console_println("StarForth v4: one host node, the F18-derived engine");
boot.n = &sk_v4_node; boot.es = &sk_v4_es; boot.h = &sk_v4_heat; boot.im = &v4_capsule_image;
boot.out = sk_v4_out; boot.key = sk_v4_key;
boot.words = sk_v4_words; boot.word_count = sizeof sk_v4_words / sizeof sk_v4_words[0]; boot.serve = sk_v4_serve;
if (!v4_boot_run(&boot, sk_v4_disk, SK_V4_BLOCKS)) {
console_println("StarForth v4: not started");
for (;;) { }
@@ -0,0 +1,119 @@
[=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 5 2026 18:52:09
UEFI BootServices: EXITED
=== StarKernel Boot Information ===
Memory map entries: 131
Total memory: 1023 MB
Usable memory: 968 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 249244
Free pages : 247721
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=2097857641 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=1004827019, tick_hz=100, initial_count=10048270
APIC Timer: configured (masked, ready to start)
Heartbeat: init done
Kernel initialization complete.
Boot successful!
StarForth v4: one host node, the F18-derived engine
PARITY:V4_NUCLEUS words=296 image_hash=0x736921433a2e0e97
V4: capsule v4:forth79.4th signature: missing (unsigned)
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x01a3a48159f28a3e
V4: capsule v4:post79.4th signature: missing (unsigned)
PARITY:V4_POST tests=550 pass=550 fail=0
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb987dbb4388990bd capsule_hash=0xb987dbb4388990bd dict_hash=0x212b7b70848080e7
PARITY:OK
POST: PASSED
ok> : SQ DUP * ;
ok
ok> 7 SQ . 3 4 U* . .
49 0 12 ok
ok> NOSUCH
UNKNOWN WORD: 'NOSUCH'
ERROR
ok>
@@ -0,0 +1,125 @@
[=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 5 2026 18:54:40
UEFI BootServices: EXITED
=== StarKernel Boot Information ===
Memory map entries: 135
Total memory: 1023 MB
Usable memory: 968 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 249247
Free pages : 247724
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=2100864460 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=1006925528, tick_hz=100, initial_count=10069255
APIC Timer: configured (masked, ready to start)
Heartbeat: init done
Kernel initialization complete.
Boot successful!
StarForth v4: one host node, the F18-derived engine
PARITY:V4_NUCLEUS words=296 image_hash=0x736921433a2e0e97
V4: capsule v4:forth79.4th signature: missing (unsigned)
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x01a3a48159f28a3e
V4: capsule v4:post79.4th signature: missing (unsigned)
PARITY:V4_POST tests=550 pass=550 fail=0
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb987dbb4388990bd capsule_hash=0xb987dbb4388990bd dict_hash=0x212b7b70848080e7
PARITY:OK
POST: PASSED
ok> : SQ DUP * ;
ok
ok> 7 SQ . 3 4 U* . .
49 0 12 ok
ok> NOSUCH
UNKNOWN WORD: 'NOSUCH'
ERROR
ok> 9 KERNEL-WORD ASK9
ok
ok> 1 2 ASK9
Argument out of range
ERROR
ok> BYE
BYE: cold restart
@@ -0,0 +1,99 @@
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 5 2026 18:57:09
UEFI BootServices: EXITED
=== StarKernel Boot Information ===
Memory map entries: 108
Total memory: 4093 MB
Usable memory: 4056 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 1039860
Free pages : 1038449
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
PARITY:V4_NUCLEUS words=296 image_hash=0x736921433a2e0e97
V4: capsule v4:forth79.4th signature: missing (unsigned)
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x01a3a48159f28a3e
V4: capsule v4:post79.4th signature: missing (unsigned)
PARITY:V4_POST tests=550 pass=550 fail=0
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb987dbb4388990bd capsule_hash=0xb987dbb4388990bd dict_hash=0x212b7b70848080e7
PARITY:OK
POST: PASSED
ok> : SQ DUP * ;
ok
ok> 7 SQ . 3 4 U* . .
49 0 12 ok
ok> NOSUCH
UNKNOWN WORD: 'NOSUCH'
ERROR
ok> 9 KERNEL-WORD ASK9
ok
ok> 1 2 ASK9
Argument out of range
ERROR
ok> BYE
BYE: cold restart
@@ -0,0 +1,169 @@
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 = 0x00000000bde968f8
riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a)
_____ _ _ __ _
/ ____| | | |/ / | |
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
____) | || (_| | | | . \ __/ | | | | | __/ |
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
LithosAnanke v2.1.0
Architecture: riscv64
Build: Oct 5 2026 19:00:24
UEFI BootServices: EXITED
=== StarKernel Boot Information ===
Memory map entries: 100
Total memory: 1020 MB
Usable memory: 977 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 250773
Free pages : 250121
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
PARITY:V4_NUCLEUS words=296 image_hash=0x736921433a2e0e97
V4: capsule v4:forth79.4th signature: missing (unsigned)
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x01a3a48159f28a3e
V4: capsule v4:post79.4th signature: missing (unsigned)
PARITY:V4_POST tests=550 pass=550 fail=0
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=0xb987dbb4388990bd capsule_hash=0xb987dbb4388990bd dict_hash=0x212b7b70848080e7
PARITY:OK
POST: PASSED
ok> : SQ DUP * ;
ok
ok> 7 SQ . 3 4 U* . .
49 0 12 ok
ok> NOSUCH
UNKNOWN WORD: 'NOSUCH'
ERROR
ok> 9 KERNEL-WORD ASK9
ok
ok> 1 2 ASK9
Argument out of range
ERROR
ok> BYE
BYE: cold restart
+18 -8
View File
@@ -59,19 +59,20 @@ State, 2026-10-05, after `ENGINE.md` step 1: all six builds start the nucleus,
load `v4:forth79.4th`, pass POST and reach `ok>`, with the same lines:
```
PARITY:V4_NUCLEUS words=295 image_hash=0x3b5a9a7a2f7c5361
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=... dict_hash=0xc45c4db43ea0b1f1
PARITY:V4_NUCLEUS words=296 image_hash=0x736921433a2e0e97
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=... dict_hash=0x01a3a48159f28a3e
PARITY:V4_POST tests=550 pass=550 fail=0
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=... dict_hash=0x88df0873060da73e
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=... dict_hash=0x212b7b70848080e7
PARITY:OK
POST: PASSED
ok>
```
Bare-metal logs: `logs/20261005-180922/amd64/`, `logs/20261005-181152/aarch64/`,
`logs/20261005-181541/riscv64/`. On each, three lines were then typed at the
Bare-metal logs: `logs/20261005-185506/amd64/`, `logs/20261005-185734/aarch64/`,
`logs/20261005-190101/riscv64/`. On each, six lines were then typed at the
prompt through the serial port — a definition, its use with the capsule's
`U*`, and an unknown word — and each answered as the hosted binary does. The
`U*`, an unknown word, a kernel word no one serves, and `BYE` — and each
was answered as the hosted binary answers it. The
capsules were unsigned (no signing key on this machine).
**A node is handed a line** (`docs/v4.0.0/ENGINE.md` 3.1). It does not read
@@ -84,10 +85,19 @@ may be 1024 characters, a block. `v4_line_begin`, `v4_line_done` and
`v4_boot_line` (`v4/system/boot.c`) is the one loop the hosted binary, the
kernel and the capsule loader all run it with.
**A node asks its kernel by writing to its port** (`ENGINE.md` 3.3). The
write blocks the node until it has been served. A kernel word is a
dictionary entry made by `n KERNEL-WORD name`, whose body writes `n` to the
port; its arguments and results are on the data stack. `BYE` is the one
kernel word so far. A node can be stopped between any two instruction
words and is blocked while it waits at its port, which is what a system of
many users and tasks, preemptive and cooperative, needs of it
(`ENGINE.md` 3a).
**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 the only one
done. `V3-PARITY.md` records the rulings it is built from.
place. `ENGINE.md` sets out the steps from here; step 1 is done and step 2 is
part done. `V3-PARITY.md` records the rulings it is built from.
**What this does not yet show.** `forth79.4th` holds two definitions, `U*`
and `U/MOD`. Every other word is still in the assembled nucleus, so POST is
+14
View File
@@ -246,6 +246,20 @@ header ; immediate compile-only
LATEST (FLAGS) a! @ -3 and !
jump LEFT-BRACKET
\ ( n -- ) KERNEL-WORD xxx A WORD THAT ASKS THE KERNEL (docs/v4.0.0/
\ ENGINE.md 3.3). xxx is made a word whose whole body is "write n to the
\ port": n is the number of a request, the port is the node's way to its
\ kernel, and the write blocks the node until the kernel has served it
\ (DECOMPOSITION.md section 6). xxx's arguments are whatever it finds on
\ the data stack and its results are what the kernel leaves there. This is
\ what a word that is a C function in v3 -- BIRTH, BLOCK, BYE -- is on a
\ node. Not FORTH-79 and not in v3, where such a word is registered from C.
\ The constant (PORT) is the port's word address.
header KERNEL-WORD
: KERNEL-WORD
COLON (LIT,) (PORT) (LIT,) 30 (OP,) 14 (OP,) \ n port b! !b
jump SEMICOLON
\ FORTH-79: return from the definition at this point
header EXIT immediate compile-only
: EXIT 0 jump (OP,)
+12
View File
@@ -41,6 +41,18 @@ typedef struct {
const v4_image *im; /* the nucleus */
void (*out)(const char *text, unsigned len); /* the console */
int (*key)(void); /* a character for a line that reads the keyboard, or 0: there is none */
/* THE KERNEL'S WORDS (docs/v4.0.0/ENGINE.md 3.3). `words` names them,
* `word_count` of them; the boot makes each a word on the node (KERNEL-
* WORD, v4/capsule/compile.v4) whose request number is its place in the
* list, counting from 1. When the node writes a request to its port it
* is blocked, and `serve` is called with the number: it takes the
* word's arguments from the node's data stack and leaves its results
* there, and when it returns the node goes on. A request that is not
* in the list is an error on the node: 12, Argument out of range. */
const char *const *words;
unsigned word_count;
void (*serve)(v4_node *n, unsigned request);
} v4_boot;
/* What `key` returns when it has no character. */
+8
View File
@@ -36,6 +36,14 @@
typedef struct {
v4_uheat_t anticlock;
/* A node blocked at its port in the middle of an instruction word
* (node.h) goes on, when it has been served, from the slot after the
* store: the word and the slot are kept here meanwhile. */
int resume;
v4_iword resume_iw;
unsigned resume_slot;
unsigned resume_faults; /* n->faults when it blocked: a fault since then means P is a handler and the rest of the word is abandoned */
} v4_exec_state;
void v4_exec_reset(v4_exec_state *es);
+1
View File
@@ -40,6 +40,7 @@ typedef struct {
unsigned tib_bytes; /* its size: a line may be one less */
v4_cell span; /* word address of the variable SPAN */
v4_cell fault_table; /* (FAULTS): where a fault goes */
v4_cell port; /* word address of the node's port (node.h) */
/* KEY's code: a node whose P is in key_start .. key_end - 1 with no
* character pending is waiting for one */
+29
View File
@@ -88,6 +88,13 @@ typedef struct {
unsigned faults; /* how many there have been */
int stopped; /* non-zero: faulted with no handler */
/* The port (DECOMPOSITION.md section 6): a write to it blocks the node
* until its neighbour has taken what was written. See
* v4_node_port_attach below. */
v4_cell port; /* its word address, or -1: no port */
int asking; /* non-zero: blocked, having written `request` */
v4_cell request; /* what it wrote */
/* DSTACK-DEPTH and RSTACK-DEPTH (D-16). See v4_node_stack_regs_attach. */
v4_cell dstack_reg; /* its word address, or -1 */
v4_cell rstack_reg; /* its word address, or -1 */
@@ -149,6 +156,28 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value);
* Passing -1 detaches. The address is the caller's choice. */
void v4_node_console_attach(v4_node *n, v4_cell addr);
/* THE PORT. A node reaches its neighbour through a port, and a port is an
* address (DECOMPOSITION.md section 6): "a write blocks until the neighbour
* reads. This is the GA144 model. No instruction is added."
*
* After v4_node_port_attach(n, addr), a store to word address `addr` does
* not write memory: the node keeps the value in n->request, sets n->asking
* and is blocked. A blocked node executes nothing -- v4_exec_step_word
* returns 0 and changes nothing -- until its neighbour calls
* v4_node_port_served(n). It then goes on from the opcode after the store,
* in the same instruction word if there are any left.
*
* This is how a node asks its kernel for something (docs/v4.0.0/ENGINE.md
* 3.3): the neighbour on the port is the kernel, what is written is the
* number of the request, and the arguments and results are on the node's
* data stack, which the kernel may use while the node is blocked. Only the
* write is here. Nothing reads from a port yet, and a fetch from the
* address is a fetch from memory.
*
* A node has no port until one is attached: v4_node_reset detaches it. */
void v4_node_port_attach(v4_node *n, v4_cell addr);
void v4_node_port_served(v4_node *n);
/* CONSOLE-RX and CONSOLE-STATUS, the console's receive side, on the
* single-node model.
*
+30 -5
View File
@@ -87,6 +87,10 @@ static int stack_faulted(v4_node *n, unsigned op)
void v4_exec_reset(v4_exec_state *es)
{
es->anticlock = 0;
es->resume = 0;
es->resume_iw = 0;
es->resume_slot = 0;
es->resume_faults = 0;
}
void v4_exec_op(v4_node *n, v4_exec_state *es, v4_heat *h,
@@ -244,11 +248,21 @@ unsigned v4_exec_step_word(v4_node *n, v4_exec_state *es, v4_heat *h)
unsigned executed = 0;
unsigned slot = 0;
/* D-14: a node that faulted with no handler has stopped; and P itself
* may be what is outside memory. */
if (n->stopped || faulted(n, n->p)) return 0;
iw = fetch_iword(n, n->p);
n->p = add_wrap(n->p, 1);
/* D-14: a node that faulted with no handler has stopped. A node that
* has written to its port is blocked until it has been served. */
if (n->stopped || n->asking) return 0;
if (es->resume && es->resume_faults != n->faults) es->resume = 0; /* it faulted while blocked: P is the handler */
if (es->resume) {
/* served: the rest of the word the store was in */
es->resume = 0;
iw = es->resume_iw;
slot = es->resume_slot;
} else {
/* P itself may be what is outside memory */
if (faulted(n, n->p)) return 0;
iw = fetch_iword(n, n->p);
n->p = add_wrap(n->p, 1);
}
while (slot < V4_SLOT_COUNT) {
unsigned op = v4_iword_op(iw, slot);
@@ -264,6 +278,17 @@ unsigned v4_exec_step_word(v4_node *n, v4_exec_state *es, v4_heat *h)
}
executed++;
if (n->asking) {
/* The store was to the port. The node is blocked here; what is
* left of the word waits until it has been served. */
if (slot + 1 < V4_SLOT_COUNT) {
es->resume = 1;
es->resume_iw = iw;
es->resume_slot = slot + 1;
es->resume_faults = n->faults;
}
break;
}
if (again) {
/* Same word, slot 0, no refetch and P untouched: an `@p` or `!p`
* inside a unext loop therefore walks the words that follow, as
+1
View File
@@ -24,6 +24,7 @@ int v4_image_boot(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im,
v4_node_stack_regs_attach(n, im->dstack_reg, im->rstack_reg);
v4_node_error_attach(n, im->node_error);
v4_node_fault_attach(n, im->fault_table);
v4_node_port_attach(n, im->port);
if (disk && blocks) v4_node_storage_attach(n, im->storage_reg, disk, blocks);
n->mem[im->line_status] = V4_LINE_COMPLETED;
+16
View File
@@ -17,6 +17,7 @@ void v4_node_reset(v4_node *n)
v4_node_fault_attach(n, -1);
v4_node_stack_regs_attach(n, -1, -1);
v4_node_error_attach(n, -1);
v4_node_port_attach(n, -1);
v4_node_storage_attach(n, -1, 0, 0);
}
@@ -119,6 +120,9 @@ void v4_node_store(v4_node *n, v4_cell addr, v4_cell value)
n->console_dropped++;
return;
}
/* The port: the node keeps what it wrote and is blocked until its
* neighbour has taken it. -1 when no port is attached. */
if (addr == n->port) { n->request = value; n->asking = 1; return; }
/* The stack registers: a store empties the stack. -1 when not attached. */
if (addr == n->dstack_reg) { v4_dstack_clear(&n->ds); return; }
if (addr == n->rstack_reg) { v4_rstack_clear(&n->rs); return; }
@@ -177,3 +181,15 @@ void v4_node_console_attach(v4_node *n, v4_cell addr)
n->console_len = 0;
n->console_dropped = 0;
}
void v4_node_port_attach(v4_node *n, v4_cell addr)
{
n->port = addr;
n->asking = 0;
n->request = 0;
}
void v4_node_port_served(v4_node *n)
{
n->asking = 0;
}
+26
View File
@@ -152,6 +152,18 @@ int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
n->console_len = 0;
}
if (n->stopped) return V4_BOOT_LINE_STOPPED;
if (n->asking) { /* blocked at its port: the kernel's turn */
if (b->serve && n->request >= 1 && n->request <= (v4_cell)b->word_count) {
b->serve(n, (unsigned)n->request);
v4_node_port_served(n);
} else {
/* a request no one serves is an error, as any other (D-18):
* 12, Argument out of range */
v4_node_port_served(n);
v4_node_store(n, n->error_reg, 12); /* a store to NODE-ERROR raises it */
}
continue;
}
if (v4_line_done(n, b->im)) return v4_line_status(n, b->im);
if (v4_image_waiting(n, b->im)) { /* the line is reading the keyboard */
int c = b->key ? b->key() : V4_BOOT_KEY_END;
@@ -261,6 +273,20 @@ int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks)
say(b, " image_hash="); say_hex(b, image_hash(b->im));
say(b, "\n");
/* the kernel's words, before any capsule that might use one */
for (i = 0; i < b->word_count; i++) {
char text[64];
unsigned at = 0, k, num = i + 1, div = 1;
while (num / div >= 10) div *= 10;
for (; div; div /= 10) text[at++] = (char)('0' + num / div % 10);
for (k = 0; " KERNEL-WORD "[k]; k++) text[at++] = " KERNEL-WORD "[k];
for (k = 0; b->words[i][k] && at < sizeof text; k++) text[at++] = b->words[i][k];
if (b->words[i][k] || v4_boot_line(b, text, at) != V4_LINE_COMPLETED) {
say(b, "V4: kernel word "); say(b, b->words[i]); say(b, " could not be made\nPARITY:FAIL\nPOST: FAILED\n");
return 0;
}
}
for (i = 0; i < sizeof boot_capsules / sizeof boot_capsules[0]; i++)
if (!load_capsule(b, boot_capsules[i])) { say(b, "PARITY:FAIL\nPOST: FAILED\n"); return 0; }
#if V4_POST_AT_BOOT
+2
View File
@@ -68,6 +68,7 @@
#define EMIT_HOOK (BUF0_W - 7) /* the xt of a word EMIT gives its character to, or 0: the console */
#define CATCH (BUF0_W - 8) /* non-zero: a line's error is recorded here, -1, and the line ends " ok" */
#define LINE_STATUS (BUF0_W - 9) /* how the last line ended: 0 QUIT, 1 completed, 2 an error */
#define PORT (BUF0_W - 10) /* the node's port: a write to it blocks until its neighbour, the kernel, has served it */
#define LOG_LEVEL (BUF0_W - 5) /* the level at or below which a message is printed */
#define BOOT_CELLS (BVARS + 14) /* (BOOT): DP and LATEST as the loader left them, 2 cells */
#define BUF0_W (BVARS - 2 * 256) /* the two block buffers, 256 cells each */
@@ -144,6 +145,7 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
v4_text_constant(tx, "(EMIT-HOOK)", EMIT_HOOK);
v4_text_constant(tx, "(CATCH)", CATCH);
v4_text_constant(tx, "(LINE-STATUS)", LINE_STATUS);
v4_text_constant(tx, "(PORT)", PORT);
v4_text_constant(tx, "BLOCK-NUMBER", STORAGE_REG);
v4_text_constant(tx, "BLOCK-ADDRESS", STORAGE_REG + 1);
v4_text_constant(tx, "BLOCK-COMMAND", STORAGE_REG + 2);
+142
View File
@@ -0,0 +1,142 @@
/* test_port.c -- the node's port: a write blocks the node until its
* neighbour has served it (node.h; DECOMPOSITION.md section 6).
*
* At the level of opcodes: no word of the vocabulary is defined here.
*/
#include "v4/asm.h"
#include "v4/testcode.h"
#include <stdio.h>
#include <string.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)
/* The memory map is open (D-4); the test chooses the address. */
#define PORT ((v4_cell)(V4_NODE_WORDS - 9u))
#define MARK ((v4_cell)0x1234)
#define HOME ((v4_cell)8) /* where a word returns to when the test called it */
static v4_node n;
static v4_exec_state es;
static v4_heat h;
static v4_asm as;
#define O(name) v4_asm_op(&as, V4_OP_##name)
#define LIT(v) v4_asm_lit(&as, (v4_cell)(v))
static void start(v4_cell word)
{
v4_dstack_reset(&n.ds);
v4_rstack_reset(&n.rs);
v4_exec_reset(&es);
v4_heat_reset(&h);
v4_rstack_push(&n.rs, HOME);
n.p = word;
}
/* Run until the node is blocked, is back at HOME, or `max` words. */
static unsigned run(unsigned max)
{
unsigned words = 0;
while (words < max && !n.asking && n.p != HOME) { (void)v4_exec_step_word(&n, &es, &h); words++; }
return words;
}
int main(void)
{
v4_cell w_ask, w_mid, w_two;
v4_uheat_t clock;
printf("v4 port tests: V4_CELL_BITS=%d\n", V4_CELL_BITS);
v4_asm_begin(&as, &n, 16);
/* what KERNEL-WORD lays down: the request, the port, b! !b ; */
w_ask = v4_asm_label(&as);
LIT(7); LIT(PORT); O(BANG_B); O(STORE_B); O(SEMI);
/* a write with more of the instruction word after it: dup + */
w_mid = v4_asm_label(&as);
LIT(5); LIT(PORT); O(BANG_B); O(STORE_B); O(DUP); O(ADD); O(SEMI);
/* two writes one after the other */
w_two = v4_asm_label(&as);
LIT(PORT); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); O(SEMI);
CHECK(v4_asm_ok(&as), "the test words assemble");
/* ---- no port: the address is memory ---- */
v4_node_reset(&n);
v4_asm_begin(&as, &n, 16);
w_ask = v4_asm_label(&as);
LIT(7); LIT(PORT); O(BANG_B); O(STORE_B); O(SEMI);
w_mid = v4_asm_label(&as);
LIT(5); LIT(PORT); O(BANG_B); O(STORE_B); O(DUP); O(ADD); O(SEMI);
w_two = v4_asm_label(&as);
LIT(PORT); O(BANG_B); LIT(1); O(STORE_B); LIT(2); O(STORE_B); O(SEMI);
CHECK(n.port == -1 && !n.asking, "a node has no port until one is attached");
n.mem[PORT] = MARK;
start(w_ask);
(void)run(100);
CHECK(n.p == HOME && !n.asking && n.mem[PORT] == 7, "with none, a store to the address writes memory and the word returns");
/* ---- the write blocks ---- */
v4_node_port_attach(&n, PORT);
n.mem[PORT] = MARK;
start(w_ask);
v4_dstack_push(&n.ds, 11);
v4_dstack_push(&n.ds, 22);
(void)run(100);
CHECK(n.asking && n.request == 7, "a store to the port blocks the node, with what it wrote: %ld", (long)n.request);
CHECK(n.mem[PORT] == MARK, "and does not write memory");
CHECK(n.p != HOME, "the word has not returned");
CHECK(n.ds.depth == 2, "what was on the data stack is there for its neighbour");
clock = es.anticlock;
CHECK(v4_exec_step_word(&n, &es, &h) == 0 && v4_exec_step_word(&n, &es, &h) == 0, "a blocked node executes nothing");
CHECK(es.anticlock == clock && n.asking, "however often it is stepped: the anti-clock does not move");
/* the neighbour takes two values and leaves one, then says it is served */
{
v4_cell b = v4_dstack_pop(&n.ds), a = v4_dstack_pop(&n.ds);
v4_dstack_push(&n.ds, a + b);
}
v4_node_port_served(&n);
(void)run(100);
CHECK(n.p == HOME && !n.asking, "served, the word goes on and returns");
CHECK(n.ds.depth == 1 && v4_dstack_pop(&n.ds) == 33, "with what its neighbour left on the stack");
/* ---- the rest of the instruction word waits ---- */
start(w_mid);
v4_dstack_push(&n.ds, 100); /* to be replaced while the node is blocked */
(void)run(100);
CHECK(n.asking && n.request == 5, "blocked in the middle of an instruction word");
(void)v4_dstack_pop(&n.ds);
v4_dstack_push(&n.ds, 21); /* the neighbour's result */
v4_node_port_served(&n);
(void)run(100);
CHECK(n.p == HOME && n.ds.depth == 1 && v4_dstack_pop(&n.ds) == 42,
"the opcodes after the store run when it is served, on what the neighbour left: dup + of 21");
/* ---- one write after another ---- */
start(w_two);
(void)run(100);
CHECK(n.asking && n.request == 1, "the first write blocks");
v4_node_port_served(&n);
(void)run(100);
CHECK(n.asking && n.request == 2, "served, the node runs to the second and blocks again");
v4_node_port_served(&n);
(void)run(100);
CHECK(n.p == HOME && !n.asking, "and returns when that is served");
/* ---- a fault while blocked abandons the rest of the word ---- */
start(w_mid);
v4_dstack_push(&n.ds, 1);
(void)run(100);
CHECK(n.asking, "blocked again");
v4_node_port_served(&n);
v4_node_fault(&n, V4_FAULT_RAISED, 12); /* no handler attached: the node stops */
CHECK(v4_exec_step_word(&n, &es, &h) == 0 && n.stopped, "a node its neighbour faults does not go on with the word");
/* ---- detaching ---- */
v4_node_reset(&n);
CHECK(n.port == -1 && !n.asking, "reset detaches the port");
printf(" %d checks, %d failures\n", checks, failures);
return failures != 0;
}
+16
View File
@@ -31,6 +31,20 @@ static int console_key(void)
return c == EOF ? V4_BOOT_KEY_END : c;
}
/* THE KERNEL'S WORDS, for a node that is a Linux program: the words that
* are C in hosted v3 because only the host can do them. */
static const char *const kernel_words[] = { "BYE" };
static int leaving;
static void serve(v4_node *node, unsigned request)
{
(void)node;
if (request == 1) { /* BYE ( -- ): as hosted v3, say so and leave */
fputs("Goodbye!\n", stdout);
leaving = 1;
}
}
/* The prompt is the host's, as it is the kernel's REPL's for a v3 VM: read a
* line, hand it to the node, say how it ended. */
int main(void)
@@ -40,6 +54,7 @@ int main(void)
boot.n = &n; boot.es = &es; boot.h = &h; boot.im = &v4_capsule_image;
boot.out = console_out; boot.key = console_key;
boot.words = kernel_words; boot.word_count = sizeof kernel_words / sizeof kernel_words[0]; boot.serve = serve;
if (!v4_boot_run(&boot, disk, BLOCKS)) { fflush(stdout); return 1; }
for (;;) {
@@ -53,6 +68,7 @@ int main(void)
if (len && line[len - 1] == '\n') len--;
how = v4_boot_line(&boot, line, (unsigned)len);
if (how == V4_LINE_COMPLETED) fputs(" ok\n", stdout);
if (leaving) { fflush(stdout); return 0; }
else if (how == V4_LINE_ERROR) fputs(" ERROR\n", stdout);
else if (how == V4_BOOT_LINE_TOO_LONG) fputs("Line too long\n ERROR\n", stdout);
else if (how == V4_BOOT_LINE_NO_INPUT) { putchar('\n'); return 0; }
+2 -2
View File
@@ -100,8 +100,8 @@ int main(int argc, char **argv)
}
fprintf(out, "};\n\nconst v4_image v4_capsule_image = {\n cells, %uu,\n %uu, %uu, %uu, %uu,\n", count,
(unsigned)V4_CELL_BITS, (unsigned)V4_NODE_WORDS, (unsigned)V4_DATA_RING, (unsigned)V4_RET_RING);
fprintf(out, " %ld, %ld, %ld,\n %ld, %uu, %ld,\n %ld,\n %ld, %ld,\n", (long)line, (long)idle, (long)LINE_STATUS,
(long)TIB, 1025u, (long)SPAN, (long)faults, (long)w_key, (long)w_key_end);
fprintf(out, " %ld, %ld, %ld,\n %ld, %uu, %ld,\n %ld, %ld,\n %ld, %ld,\n", (long)line, (long)idle, (long)LINE_STATUS,
(long)TIB, 1025u, (long)SPAN, (long)faults, (long)PORT, (long)w_key, (long)w_key_end);
fprintf(out, " %ld, %ld, %ld,\n %ld, %ld,\n %ld,\n %ld,\n %ld, %ld\n};\n", (long)CONSOLE_TX, (long)CONSOLE_RX, (long)CONSOLE_ST,
(long)DSTACK_REG, (long)RSTACK_REG, (long)NODE_ERROR, (long)STORAGE_REG, (long)DP, (long)LATEST);
if (fclose(out) != 0) die("cannot write", argv[1]);