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:
co-authored by
Claude Opus 5.5
parent
5f1f60fc84
commit
8df6c16766
@@ -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
@@ -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,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 @@
|
||||
[2J[001;001H[=3h[2J[001;001H[2J[001;001H[8;042;160t[2J[001;001H[2J[001;001H[8;056;240t[2J[001;001HBdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
|
||||
BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
|
||||
[2J[001;001H[=3h[2J[001;001HStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
RAW SERIAL UP
|
||||
[CKPT 002] Serial (COM1) initialized
|
||||
Monolithic build - kernel linked directly
|
||||
Collecting boot information...
|
||||
CmdLine: parsed OK
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
GOP: linear framebuffer found
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
EBS...
|
||||
EBS OK
|
||||
Calling kernel_main (monolithic)...
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: amd64
|
||||
Build: Oct 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 @@
|
||||
[2J[001;001H[=3h[2J[001;001H[2J[001;001H[8;042;160t[2J[001;001H[2J[001;001H[8;056;240t[2J[001;001HBdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
|
||||
BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
|
||||
[2J[001;001H[=3h[2J[001;001HStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
RAW SERIAL UP
|
||||
[CKPT 002] Serial (COM1) initialized
|
||||
Monolithic build - kernel linked directly
|
||||
Collecting boot information...
|
||||
CmdLine: parsed OK
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
GOP: linear framebuffer found
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
EBS...
|
||||
EBS OK
|
||||
Calling kernel_main (monolithic)...
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: amd64
|
||||
Build: Oct 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)
|
||||
[2J[001;001H[=3h[2J[001;001H[2J[001;001H[8;031;100t[2J[001;001HBdsDxe: failed to load Boot0002 "UEFI Misc Device" from PciRoot(0x0)/Pci(0x2,0x0): Not Found
|
||||
BdsDxe: failed to load Boot0003 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:04.0-1" from PciRoot(0x0)/Pci(0x4,0x0)/USB(0x0,0x0): Not Found
|
||||
BdsDxe: loading Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
|
||||
BdsDxe: starting Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
|
||||
[2J[001;001H[=3h[2J[001;001HStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
Collecting boot information...
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: aarch64
|
||||
Build: Oct 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
|
||||
[2J[004D[=3h[2J[009D[2J[004D[8;031;100t[2J[016DRISC-V EDK2 firmware version 2025.11-3ubuntu7.3
|
||||
Press ESCAPE within 5 seconds for boot options ERROR: C40000002:V03051002 I0 6D33944A-EC75-4855-A54D-809C75241F6C 83FFF850
|
||||
BdsDxe: failed to load Boot0001 "UEFI Misc Device" fr
|
||||
om PciRoot(0x0)/Pci(0x1,0x0): Not Found
|
||||
[2J[001;001H[=3h[2J[009DStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
Monolithic build - kernel linked directly
|
||||
Collecting boot information...
|
||||
CmdLine: parsed OK
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
GOP: linear framebuffer found
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
Calling kernel_main (monolithic)...
|
||||
riscv64 item 4.3.5a: satp state at kernel entry (before switch)
|
||||
satp.MODE = 0x000000000000000a
|
||||
satp.PPN = 0x00000000000bf868
|
||||
__kernel_start = 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
@@ -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
|
||||
|
||||
@@ -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,)
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user