diff --git a/capsules/BLOCK_MAP.md b/capsules/BLOCK_MAP.md index f5a3aad0..1992f014 100644 --- a/capsules/BLOCK_MAP.md +++ b/capsules/BLOCK_MAP.md @@ -1,5 +1,5 @@ # Capsule Block Manifest — Auto-generated - + diff --git a/docs/v4.0.0/ENGINE.md b/docs/v4.0.0/ENGINE.md index b755736c..ce7cd517 100644 --- a/docs/v4.0.0/ENGINE.md +++ b/docs/v4.0.0/ENGINE.md @@ -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 | diff --git a/kernel/src/v4/sk_v4.c b/kernel/src/v4/sk_v4.c index 372f5891..980e8e0a 100644 --- a/kernel/src/v4/sk_v4.c +++ b/kernel/src/v4/sk_v4.c @@ -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 (;;) { } diff --git a/logs/20261005-185234/amd64/qemu-amd64-20261005-185234.log b/logs/20261005-185234/amd64/qemu-amd64-20261005-185234.log new file mode 100644 index 00000000..22f255ef --- /dev/null +++ b/logs/20261005-185234/amd64/qemu-amd64-20261005-185234.log @@ -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> \ No newline at end of file diff --git a/logs/20261005-185506/amd64/qemu-amd64-20261005-185506.log b/logs/20261005-185506/amd64/qemu-amd64-20261005-185506.log new file mode 100644 index 00000000..acd6e056 --- /dev/null +++ b/logs/20261005-185506/amd64/qemu-amd64-20261005-185506.log @@ -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 diff --git a/logs/20261005-185734/aarch64/qemu-aarch64-20261005-185734.log b/logs/20261005-185734/aarch64/qemu-aarch64-20261005-185734.log new file mode 100644 index 00000000..307b0548 --- /dev/null +++ b/logs/20261005-185734/aarch64/qemu-aarch64-20261005-185734.log @@ -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 diff --git a/logs/20261005-190101/riscv64/qemu-riscv64-20261005-190101.log b/logs/20261005-190101/riscv64/qemu-riscv64-20261005-190101.log new file mode 100644 index 00000000..caa31588 --- /dev/null +++ b/logs/20261005-190101/riscv64/qemu-riscv64-20261005-190101.log @@ -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 diff --git a/v4/README.md b/v4/README.md index 4db5a21f..d449320e 100644 --- a/v4/README.md +++ b/v4/README.md @@ -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 diff --git a/v4/capsule/compile.v4 b/v4/capsule/compile.v4 index a00e6255..7c8fd0c9 100644 --- a/v4/capsule/compile.v4 +++ b/v4/capsule/compile.v4 @@ -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,) diff --git a/v4/include/v4/boot.h b/v4/include/v4/boot.h index 85be9f14..48d60597 100644 --- a/v4/include/v4/boot.h +++ b/v4/include/v4/boot.h @@ -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. */ diff --git a/v4/include/v4/exec.h b/v4/include/v4/exec.h index 5bfa09d9..ca066b72 100644 --- a/v4/include/v4/exec.h +++ b/v4/include/v4/exec.h @@ -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); diff --git a/v4/include/v4/image.h b/v4/include/v4/image.h index 6d039c66..f017ac97 100644 --- a/v4/include/v4/image.h +++ b/v4/include/v4/image.h @@ -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 */ diff --git a/v4/include/v4/node.h b/v4/include/v4/node.h index 0c4d558b..2810ff77 100644 --- a/v4/include/v4/node.h +++ b/v4/include/v4/node.h @@ -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. * diff --git a/v4/src/exec.c b/v4/src/exec.c index 407c5605..8a2bfbc8 100644 --- a/v4/src/exec.c +++ b/v4/src/exec.c @@ -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 diff --git a/v4/src/image.c b/v4/src/image.c index e33cd1fc..7517eff6 100644 --- a/v4/src/image.c +++ b/v4/src/image.c @@ -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; diff --git a/v4/src/node.c b/v4/src/node.c index 55beba22..a1746239 100644 --- a/v4/src/node.c +++ b/v4/src/node.c @@ -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; +} diff --git a/v4/system/boot.c b/v4/system/boot.c index fca98d1b..d9ee20be 100644 --- a/v4/system/boot.c +++ b/v4/system/boot.c @@ -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 diff --git a/v4/tests/host_map.h b/v4/tests/host_map.h index 585c29b2..37470be3 100644 --- a/v4/tests/host_map.h +++ b/v4/tests/host_map.h @@ -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); diff --git a/v4/tests/test_port.c b/v4/tests/test_port.c new file mode 100644 index 00000000..5c9e1fe6 --- /dev/null +++ b/v4/tests/test_port.c @@ -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 +#include + +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; +} diff --git a/v4/tools/hosted.c b/v4/tools/hosted.c index a9627e0d..ab3cc20e 100644 --- a/v4/tools/hosted.c +++ b/v4/tools/hosted.c @@ -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; } diff --git a/v4/tools/mkimage.c b/v4/tools/mkimage.c index a77e714b..983817af 100644 --- a/v4/tools/mkimage.c +++ b/v4/tools/mkimage.c @@ -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]);