feat(v4.0.0): a node is sent text as a message and sends back what it prints
MESH.md step 3. The ports are the transport; the message is what is transported: to, from, type, heat and TTL, ACL tag, sequence, length, then text four characters to a word. - quit.v4: a node with nothing to do is blocked reading "any port"; text for it is interpreted; (FINISH) sends what it printed and then how the text ended, and it waits again - core.v4: EMIT keeps what is printed, (FLUSH-OUT) and (HDR) send it to the sender on the port the message came on. EMIT still needs one free data cell and no more; it works on the return stack and in A and B - message.h/.c: the same format for whatever is on a port and is not a node - boot.c: the boot is the node's console on port 1 and its kernel on port 0 - the prompt tests are a console that speaks messages - gone: v4_line_begin, v4_line_done, v4_line_status; writing a node's input buffer and setting its P from outside; any use of CONSOLE-TX Verified: make -C v4 test (test_host_quit.c 1283 checks, the full-stack figures unchanged) and make -C v4 sanitize pass; hosted-check passes on three ISAs with POST 550 of 550; clean qemu with STARFORTH_V4=1 passes POST and answers lines typed at each prompt on amd64, aarch64 and riscv64 (logs/20261006-110551, -111621, -111341). -110837 is an aarch64 run ended by the test wrapper's limit while still in UEFI firmware; it shows nothing about v4. Not done: KEY, EXPECT and QUERY still read the console's input registers; a message not for this node is let go (step 4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
42610e844f
commit
a041b401ea
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-10-06T14:30:09Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-10-06T15:15:54Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
Binary file not shown.
@@ -287,6 +287,29 @@ Each is tested, committed and pushed before the next.
|
||||
6).
|
||||
3. **Messages (section 6):** a StarForth node that reads messages when
|
||||
idle; the console as a device; a line typed is a message.
|
||||
**Done 2026-10-06, but for the keyboard.** A node with nothing to do is
|
||||
blocked reading "any port" (`(IDLE)`, `v4/capsule/quit.v4`). What
|
||||
arrives is a message; text for this node is interpreted; what it prints
|
||||
is kept and sent back as messages to the sender, on the port the
|
||||
message came on, and then a message saying how the text ended (`EMIT`,
|
||||
`(FLUSH-OUT)`, `(HDR)` in `core.v4`; `(FINISH)` in `quit.v4`).
|
||||
`v4/src/message.c` is the same format for whatever is on the other end
|
||||
of a port and is not a node. The boot is that, on the node's port 1, as
|
||||
the console (`v4_boot_line`); the prompt tests are too. Handing a node a
|
||||
line by writing its input buffer and setting its `P` is gone
|
||||
(`v4_line_begin` and the rest). Nothing reads the `CONSOLE-TX` register
|
||||
any more.
|
||||
`EMIT` still needs one free cell of the data stack and no more, as
|
||||
before; it keeps its working values on the return stack. The full-stack
|
||||
tests hold at the same figures as before.
|
||||
All v4 tests pass at both widths and under ASan and UBSan;
|
||||
`hosted-check` on three ISAs; bare metal `logs/20261006-110551` (amd64),
|
||||
`-111621` (aarch64), `-111341` (riscv64). `-110837` is an aarch64 run
|
||||
that was ended by the test wrapper's limit while still in UEFI firmware,
|
||||
before the kernel had started; it shows nothing about v4.
|
||||
**Not done:** `KEY`, `EXPECT` and `QUERY` still read the console's two
|
||||
input registers, not a message. A message not for this node, or not
|
||||
text, is let go: passing it on is step 4.
|
||||
4. **Finding the way (section 7).**
|
||||
5. **Birth and Hera's requests (section 9); the unit of five.**
|
||||
6. **Storage (section 8).**
|
||||
|
||||
+1
-1
@@ -613,7 +613,7 @@ V4_DEFS := -DSTARFORTH_V4=1 -Iv4/include \
|
||||
KERNEL_CFLAGS += $(V4_DEFS)
|
||||
LOADER_CFLAGS += $(V4_DEFS)
|
||||
|
||||
V4_ENGINE_SRCS := $(addprefix v4/src/,node.c exec.c stack.c iword.c heat.c guard.c image.c fabric.c capsule.c)
|
||||
V4_ENGINE_SRCS := $(addprefix v4/src/,node.c exec.c stack.c iword.c heat.c guard.c image.c fabric.c capsule.c message.c)
|
||||
V4_IMAGE_C := $(BUILD_DIR)/v4_image_64.c
|
||||
|
||||
LOADER_EXTRA_SRCS += $(KERNEL_SRC)/v4/sk_v4.c
|
||||
|
||||
@@ -100,8 +100,8 @@ void sk_v4_run(void)
|
||||
console_puts("ok> ");
|
||||
len = sk_v4_readline();
|
||||
how = v4_boot_line(&boot, sk_v4_text, len);
|
||||
if (how == V4_LINE_COMPLETED) console_puts(" ok\n");
|
||||
else if (how == V4_LINE_ERROR) console_puts(" ERROR\n");
|
||||
if (how == V4_TEXT_COMPLETED) console_puts(" ok\n");
|
||||
else if (how == V4_TEXT_ERROR) console_puts(" ERROR\n");
|
||||
else if (how == V4_BOOT_LINE_STOPPED) {
|
||||
console_println("StarForth v4: the node stopped on a fault");
|
||||
for (;;) { }
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
[2J[001;001H[=3h[2J[001;001H[2J[001;001H[8;042;160t[2J[001;001H[2J[001;001H[8;056;240t[2J[001;001HBdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
|
||||
BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
|
||||
[2J[001;001H[=3h[2J[001;001HStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
RAW SERIAL UP
|
||||
[CKPT 002] Serial (COM1) initialized
|
||||
Monolithic build - kernel linked directly
|
||||
Collecting boot information...
|
||||
CmdLine: parsed OK
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
GOP: linear framebuffer found
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
EBS...
|
||||
EBS OK
|
||||
Calling kernel_main (monolithic)...
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: amd64
|
||||
Build: Oct 6 2026 11:05:25
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 131
|
||||
Total memory: 1023 MB
|
||||
Usable memory: 968 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 249252
|
||||
Free pages : 247729
|
||||
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=2101540994 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=1006649045, tick_hz=100, initial_count=10066490
|
||||
APIC Timer: configured (masked, ready to start)
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x97a655081b2af559 capsule_hash=0x97a655081b2af559 words=298
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x594818fc69677603
|
||||
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=0x8768fb163c8529f4
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
ok> COLD
|
||||
FORTH-79 Cold Start
|
||||
System initialized.
|
||||
ok
|
||||
ok> 3 4 U* . .
|
||||
0 12 ok
|
||||
ok> FORGET U*
|
||||
Protected word
|
||||
ERROR
|
||||
ok> 9 KERNEL-WORD ASK9 ASK9
|
||||
Argument out of range
|
||||
ERROR
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,2 @@
|
||||
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;001H
|
||||
@@ -0,0 +1,174 @@
|
||||
|
||||
OpenSBI v1.8
|
||||
____ _____ ____ _____
|
||||
/ __ \ / ____| _ \_ _|
|
||||
| | | |_ __ ___ _ __ | (___ | |_) || |
|
||||
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
|
||||
| |__| | |_) | __/ | | |____) | |_) || |_
|
||||
\____/| .__/ \___|_| |_|_____/|____/_____|
|
||||
| |
|
||||
|_|
|
||||
|
||||
Platform Name : riscv-virtio,qemu
|
||||
Platform Features : medeleg
|
||||
Platform HART Count : 1
|
||||
Platform HART Protection : pmp
|
||||
Platform IPI Device : aclint-mswi
|
||||
Platform Timer Device : aclint-mtimer @ 10000000Hz
|
||||
Platform Console Device : uart8250
|
||||
Platform HSM Device : ---
|
||||
Platform PMU Device : ---
|
||||
Platform Reboot Device : syscon-reboot
|
||||
Platform Shutdown Device : syscon-poweroff
|
||||
Platform Suspend Device : ---
|
||||
Platform CPPC Device : ---
|
||||
Firmware Base : 0x80000000
|
||||
Firmware Size : 321 KB
|
||||
Firmware RW Offset : 0x40000
|
||||
Firmware RW Size : 65 KB
|
||||
Firmware Heap Offset : 0x47000
|
||||
Firmware Heap Size : 37 KB (total), 0 KB (reserved), 12 KB (used), 23 KB (free)
|
||||
Firmware Scratch Size : 4096 B (total), 1464 B (used), 2632 B (free)
|
||||
Runtime SBI Version : 3.0
|
||||
Standard SBI Extensions : ipi,pmu,srst,sse,hsm,rfnc,fwft,time,base,legacy,dbcn,dbtr
|
||||
Experimental SBI Extensions : none
|
||||
|
||||
Domain0 Name : root
|
||||
Domain0 Boot HART : 0
|
||||
Domain0 HARTs : 0*
|
||||
Domain0 Region00 : 0x0000000080040000-0x000000008005ffff M: (F,R,W) S/U: ()
|
||||
Domain0 Region01 : 0x0000000080000000-0x000000008003ffff M: (F,R,X) S/U: ()
|
||||
Domain0 Region02 : 0x0000000000100000-0x0000000000100fff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region03 : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region04 : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
|
||||
Domain0 Region05 : 0x000000000c400000-0x000000000c5fffff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region06 : 0x000000000c000000-0x000000000c3fffff M: (I,R,W) S/U: (R,W)
|
||||
Domain0 Region07 : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
|
||||
Domain0 Next Address : 0x0000000020000000
|
||||
Domain0 Next Arg1 : 0x00000000bfe00000
|
||||
Domain0 Next Mode : S-mode
|
||||
Domain0 SysReset : yes
|
||||
Domain0 SysSuspend : yes
|
||||
|
||||
Boot HART ID : 0
|
||||
Boot HART Domain : root
|
||||
Boot HART Priv Version : v1.12
|
||||
Boot HART Base ISA : rv64imafdch
|
||||
Boot HART ISA Extensions : sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu
|
||||
Boot HART PMP Count : 16
|
||||
Boot HART PMP Granularity : 2 bits
|
||||
Boot HART PMP Address Bits : 54
|
||||
Boot HART MHPM Info : 16 (0x0007fff8)
|
||||
Boot HART Debug Triggers : 2 triggers
|
||||
Boot HART MIDELEG : 0x0000000000001666
|
||||
Boot HART MEDELEG : 0x0000000000f4b509
|
||||
[2J[004D[=3h[2J[009D[2J[004D[8;031;100t[2J[016DRISC-V EDK2 firmware version 2025.11-3ubuntu7.3
|
||||
Press ESCAPE within 5 seconds for boot options ERROR: C40000002:V03051002 I0 6D33944A-EC75-4855-A54D-809C75241F6C 83FFF850
|
||||
BdsDxe: failed to load Boot0001 "UEFI Misc Device" fr
|
||||
om PciRoot(0x0)/Pci(0x1,0x0): Not Found
|
||||
[2J[001;001H[=3h[2J[009DStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
Monolithic build - kernel linked directly
|
||||
Collecting boot information...
|
||||
CmdLine: parsed OK
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
GOP: linear framebuffer found
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
Calling kernel_main (monolithic)...
|
||||
riscv64 item 4.3.5a: satp state at kernel entry (before switch)
|
||||
satp.MODE = 0x000000000000000a
|
||||
satp.PPN = 0x00000000000bf868
|
||||
__kernel_start = 0x00000000bdea78f8
|
||||
riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a)
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: riscv64
|
||||
Build: Oct 6 2026 11:13:04
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 100
|
||||
Total memory: 1020 MB
|
||||
Usable memory: 977 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 250782
|
||||
Free pages : 250130
|
||||
Used pages : 652
|
||||
Total MB : 979
|
||||
Free MB : 977
|
||||
Used MB : 2
|
||||
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
VMM self-test complete.
|
||||
|
||||
IDT installed.
|
||||
|
||||
APIC: init...
|
||||
PLIC: no DTB PLIC node -- using QEMU virt-machine default (base=0x0c000000)
|
||||
PLIC: init (S-mode context 1, threshold=0)
|
||||
APIC: init done
|
||||
|
||||
Timer: init...
|
||||
Timer: RISC-V time CSR @ 10000000 Hz (FALLBACK, no devicetree)
|
||||
Timer: init done
|
||||
|
||||
Kernel heap initialized.
|
||||
Heap statistics:
|
||||
Total bytes: 536870872
|
||||
Free bytes: 536870872
|
||||
Used bytes: 0
|
||||
Peak bytes: 0
|
||||
Heap base addr: 2214588416
|
||||
Heap end addr: 2751459328
|
||||
|
||||
Heartbeat: init...
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x97a655081b2af559 capsule_hash=0x97a655081b2af559 words=298
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x594818fc69677603
|
||||
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=0x8768fb163c8529f4
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
ok> COLD
|
||||
FORTH-79 Cold Start
|
||||
System initialized.
|
||||
ok
|
||||
ok> 3 4 U* . .
|
||||
0 12 ok
|
||||
ok> FORGET U*
|
||||
Protected word
|
||||
ERROR
|
||||
ok> 9 KERNEL-WORD ASK9 ASK9
|
||||
Argument out of range
|
||||
ERROR
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
@@ -0,0 +1,104 @@
|
||||
UEFI firmware (version 2025.11-3ubuntu7.3 built at 15:40:26 on Sep 23 2026)
|
||||
[2J[001;001H[=3h[2J[001;001H[2J[001;001H[8;031;100t[2J[001;001HBdsDxe: failed to load Boot0002 "UEFI Misc Device" from PciRoot(0x0)/Pci(0x2,0x0): Not Found
|
||||
BdsDxe: failed to load Boot0003 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:04.0-1" from PciRoot(0x0)/Pci(0x4,0x0)/USB(0x0,0x0): Not Found
|
||||
BdsDxe: loading Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
|
||||
BdsDxe: starting Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
|
||||
[2J[001;001H[=3h[2J[001;001HStarKernel UEFI Loader
|
||||
Loading kernel from ESP...
|
||||
[CKPT 001] Entered efi_main - ConOut live
|
||||
Collecting boot information...
|
||||
[CKPT 004] Command line parsed
|
||||
[CKPT 005] Kernel stack allocation decided
|
||||
[CKPT 006] Boot info collected (ACPI table located)
|
||||
[CKPT 007] GOP: linear framebuffer found
|
||||
[CKPT 008] About to enter ExitBootServices retry loop
|
||||
|
||||
|
||||
_____ _ _ __ _
|
||||
/ ____| | | |/ / | |
|
||||
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
|
||||
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
|
||||
____) | || (_| | | | . \ __/ | | | | | __/ |
|
||||
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
|
||||
|
||||
LithosAnanke v2.1.0
|
||||
Architecture: aarch64
|
||||
Build: Oct 6 2026 11:15:55
|
||||
|
||||
UEFI BootServices: EXITED
|
||||
|
||||
=== StarKernel Boot Information ===
|
||||
Memory map entries: 108
|
||||
Total memory: 4093 MB
|
||||
Usable memory: 4056 MB
|
||||
===================================
|
||||
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 1039866
|
||||
Free pages : 1038455
|
||||
Used pages : 1411
|
||||
Total MB : 4061
|
||||
Free MB : 4056
|
||||
Used MB : 5
|
||||
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
VMM self-test complete.
|
||||
|
||||
AArch64: running at EL1
|
||||
IDT installed.
|
||||
|
||||
APIC: init...
|
||||
PSCI: no DTB -- using HVC (QEMU virt-machine default)
|
||||
GICv2: no DTB GIC node -- using QEMU virt-machine defaults
|
||||
GICv2: distributor+CPU interface enabled, PPI 30
|
||||
APIC: init done
|
||||
|
||||
Timer: init...
|
||||
Timer: AArch64 generic timer initialised.
|
||||
Timer: init done
|
||||
|
||||
Kernel heap initialized.
|
||||
Heap statistics:
|
||||
Total bytes: 2147483608
|
||||
Free bytes: 2147483608
|
||||
Used bytes: 0
|
||||
Peak bytes: 0
|
||||
Heap base addr: 1207959552
|
||||
Heap end addr: 3355443200
|
||||
|
||||
Heartbeat: init...
|
||||
Heartbeat: init done
|
||||
Kernel initialization complete.
|
||||
Boot successful!
|
||||
|
||||
StarForth v4: one host node, the F18-derived engine
|
||||
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x97a655081b2af559 capsule_hash=0x97a655081b2af559 words=298
|
||||
V4: capsule v4:forth79.4th signature: missing (unsigned)
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x594818fc69677603
|
||||
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=0x8768fb163c8529f4
|
||||
PARITY:OK
|
||||
POST: PASSED
|
||||
ok> : SQ DUP * ;
|
||||
ok
|
||||
ok> 7 SQ . 3 4 U* . .
|
||||
49 0 12 ok
|
||||
ok> COLD
|
||||
FORTH-79 Cold Start
|
||||
System initialized.
|
||||
ok
|
||||
ok> 3 4 U* . .
|
||||
0 12 ok
|
||||
ok> FORGET U*
|
||||
Protected word
|
||||
ERROR
|
||||
ok> 9 KERNEL-WORD ASK9 ASK9
|
||||
Argument out of range
|
||||
ERROR
|
||||
ok> BYE
|
||||
BYE: cold restart
|
||||
+1
-1
@@ -69,7 +69,7 @@ ROOT := $(abspath $(HERE)/..)
|
||||
NUCLEUS_64 := $(ROOT)/capsules/v4/nucleus-64.f18
|
||||
NUCLEUS_32 := $(BINDIR)/nucleus-32.f18
|
||||
HOST_DEFS := -DV4_NODE_WORDS=$(HOST_WORDS) -DV4_DATA_RING=$(HOST_DATA_RING) -DV4_RET_RING=$(HOST_RET_RING)
|
||||
ENGINE_SRCS := $(addprefix $(HERE)/src/,node.c exec.c stack.c iword.c heat.c guard.c image.c fabric.c capsule.c)
|
||||
ENGINE_SRCS := $(addprefix $(HERE)/src/,node.c exec.c stack.c iword.c heat.c guard.c image.c fabric.c capsule.c message.c)
|
||||
|
||||
define IMAGE_RULE
|
||||
$(BINDIR)/mkimage-$(1): $(HERE)/tools/mkimage.c $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/tests/host_map.h $(HERE)/Makefile
|
||||
|
||||
+12
-12
@@ -59,7 +59,7 @@ 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 name=v4:nucleus-64.f18 capsule_id=... capsule_hash=0xbcc90cc5a638a299 words=298
|
||||
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=... capsule_hash=0x97a655081b2af559 words=298
|
||||
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=... dict_hash=...
|
||||
PARITY:V4_POST tests=550 pass=550 fail=0
|
||||
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=... dict_hash=...
|
||||
@@ -68,23 +68,23 @@ POST: PASSED
|
||||
ok>
|
||||
```
|
||||
|
||||
Bare-metal logs: `logs/20261006-102421/amd64/`, `logs/20261006-102706/aarch64/`,
|
||||
`logs/20261006-103048/riscv64/`. On each, seven lines were then typed at the
|
||||
Bare-metal logs: `logs/20261006-110551/amd64/`, `logs/20261006-111621/aarch64/`,
|
||||
`logs/20261006-111341/riscv64/`. On each, seven lines were then typed at the
|
||||
prompt through the serial port — a definition, its use with the capsule's
|
||||
`U*`, `COLD`, the capsule word again, a `FORGET` of it (refused), 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
|
||||
its own command line and prints no prompt. Its host puts the text in the
|
||||
node's input buffer and starts it at `(LINE)`; the node interprets the text
|
||||
and stops at `(IDLE)`, leaving in `(LINE-STATUS)` how it ended. The host says
|
||||
` ok` or ` ERROR` and prompts, as the kernel's REPL does for a v3 VM. A line
|
||||
may be 1024 characters, a block. `v4_line_begin`, `v4_line_done` and
|
||||
`v4_line_status` (`v4/include/v4/image.h`) are that interface;
|
||||
`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 is sent text as a message** (`docs/v4.0.0/MESH.md` section 6). It
|
||||
does not read its own command line, prints no prompt and has no console of
|
||||
its own. With nothing to do it is blocked reading its ports. A neighbour
|
||||
writes it a message; text for it is interpreted; what it prints goes back
|
||||
as messages to the sender, and then a message saying how the text ended.
|
||||
Its console says ` ok` or ` ERROR` and prompts, as the kernel's REPL does
|
||||
for a v3 VM. A message carries 1024 characters, a block. `v4/src/message.c`
|
||||
is the format for a host; `v4_boot_line` (`v4/system/boot.c`) is the
|
||||
console both products and the capsule loader use.
|
||||
|
||||
**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
|
||||
|
||||
+56
-8
@@ -5,7 +5,8 @@
|
||||
\ written here as text for the text assembler (v4/include/v4/text.h).
|
||||
\
|
||||
\ Constants the loader supplies: N-1 (cell bits - 1), NODE-ERROR, CONSOLE-TX,
|
||||
\ CONSOLE-RX, CONSOLE-STATUS, BASE, (EMIT-HOOK).
|
||||
\ CONSOLE-RX, CONSOLE-STATUS, BASE, (EMIT-HOOK), (MSG), (REPLY), (ME), (OUT),
|
||||
\ (OUT^).
|
||||
\
|
||||
\ ERRORS (D-18). A word that finds an error takes its arguments off the stack
|
||||
\ and stores a code in NODE-ERROR. On a node with a prompt that store is a
|
||||
@@ -91,16 +92,63 @@ header CMOVE
|
||||
DONE: drop drop drop ;
|
||||
|
||||
\ ---- section 5.10: the console -------------------------------------------
|
||||
\ EMIT sends the character to the console, unless (EMIT-HOOK) holds the xt
|
||||
\ of a word: then that word is given it instead ( c -- ), and returns to
|
||||
\ EMIT's caller. Everything the node prints goes through EMIT, so a hook
|
||||
\ sees all of it. The prompt takes the hook off at the end of every line
|
||||
\ (quit.v4), so that nothing can leave the console silent. POST uses it
|
||||
\ to compare what a case prints (docs/v4.0.0/NUCLEUS.md 6.3).
|
||||
\ ---- what a node prints (docs/v4.0.0/MESH.md section 6) ------------------------
|
||||
\ A node has no console of its own. It does what a message asks, and what
|
||||
\ it prints while doing it goes back, as a message, to whoever sent that one,
|
||||
\ on the port it came on. A message is seven words and then its text, four
|
||||
\ characters to a word:
|
||||
\ to, from, type, heat and TTL, ACL tag, sequence, length in characters
|
||||
\ The types: 1 text to be interpreted 2 what a node printed 3 how the
|
||||
\ text ended (one word of text: 0 QUIT, 1 completed, 2 an error).
|
||||
\ The header of the message being served is in the seven cells (MSG); (REPLY)
|
||||
\ is the address of the port it came on; (ME) is this node's number.
|
||||
\
|
||||
\ Characters are kept in (OUT), 256 of them, a character to a cell, and sent
|
||||
\ when it is full and when the text has been done with (quit.v4). (OUT^)
|
||||
\ is where the next goes.
|
||||
\
|
||||
\ THE STACK. What prints may be run with the data stack all but full: EMIT
|
||||
\ has always needed one free cell and no more, and still does -- it keeps
|
||||
\ the character and A on the return stack while it works. Sending needs
|
||||
\ two.
|
||||
|
||||
\ ( type -- ) the first six words of a message to the sender of the one
|
||||
\ being served; its length, and its text, are the caller's to send. Each
|
||||
\ write waits for the neighbour to take it. B is left at the port.
|
||||
: (HDR)
|
||||
(REPLY) a! @ b!
|
||||
(MSG)+1 a! @ !b \ to
|
||||
(ME) a! @ !b \ from
|
||||
!b \ type
|
||||
0 !b 0 !b 0 !b ; \ heat and TTL, ACL tag, sequence: carried, not used yet
|
||||
|
||||
\ ( -- ) send what has been printed: its length, then its characters, four
|
||||
\ to a word, the first lowest
|
||||
: (FLUSH-OUT)
|
||||
(OUT^) a! @ (OUT) xor if NONE drop
|
||||
2 (HDR)
|
||||
(OUT^) a! @ (OUT) - dup !b
|
||||
3 + 2/ 2/ -1 + (OUT) a!
|
||||
FOR @+ @+ 8* + @+ 8* 8* + @+ 8* 8* 8* + !b NEXT
|
||||
(OUT) (OUT^) a! ! ;
|
||||
NONE: drop ;
|
||||
|
||||
\ EMIT puts the character with what has been printed, unless (EMIT-HOOK)
|
||||
\ holds the xt of a word: then that word is given it instead ( c -- ), and
|
||||
\ returns to EMIT's caller. Everything the node prints goes through EMIT,
|
||||
\ so a hook sees all of it. The hook is taken off when the text being
|
||||
\ interpreted ends (quit.v4), so that nothing can leave a node silent.
|
||||
\ POST uses it to compare what a case prints (docs/v4.0.0/NUCLEUS.md 6.3).
|
||||
\ A is kept: the words that print have always been free to use it across
|
||||
\ an EMIT. As v3, only the low 8 bits of the character are printed.
|
||||
header EMIT
|
||||
: EMIT ( c -- )
|
||||
(EMIT-HOOK) b! @b if NONE push ;
|
||||
NONE: drop CONSOLE-TX b! !b ;
|
||||
NONE: drop
|
||||
255 and a push push \ A, and then the character, to the return stack
|
||||
(OUT^) b! @b a! pop !+ a !b \ the character goes where (OUT^) points, which moves on
|
||||
a (OUT)+256 xor if FULL drop pop a! ;
|
||||
FULL: drop (FLUSH-OUT) pop a! ;
|
||||
header KEY
|
||||
: KEY ( -- c )
|
||||
L: CONSOLE-STATUS b! @b if WAIT drop CONSOLE-RX b! @b ;
|
||||
|
||||
+66
-26
@@ -4,17 +4,17 @@
|
||||
\ DECOMPOSITION.md 5.15 and 5.10: QUIT ABORT ABORT" (ABORT") ." (."). Part of
|
||||
\ the compiler capsule. Rests on all the files before it.
|
||||
\
|
||||
\ A NODE IS HANDED A LINE (docs/v4.0.0/ENGINE.md 3.1). It does not read its
|
||||
\ own command line and prints no prompt: that is its host's, as it is the
|
||||
\ kernel's REPL's for a v3 VM. The host puts the text in TIB and starts
|
||||
\ (LINE); the node interprets it and stops at (IDLE), having left in the
|
||||
\ variable (LINE-STATUS) how the line ended:
|
||||
\ 1 it completed the host says " ok"
|
||||
\ A NODE IS SENT TEXT (docs/v4.0.0/MESH.md section 6). It does not read its
|
||||
\ own command line and prints no prompt. A node with nothing to do waits at
|
||||
\ its ports; a neighbour writes it a message; if that is text for this node
|
||||
\ it is interpreted; what it printed goes back as a message, and then a
|
||||
\ message saying how the text ended:
|
||||
\ 1 it completed a console says " ok"
|
||||
\ 2 it ended in an error the message is already printed; " ERROR"
|
||||
\ 0 QUIT nothing is said
|
||||
\ What the console then shows is v3's:
|
||||
\ ok> 65 EMIT the host's prompt, and the line
|
||||
\ A ok what the line printed, then the host's " ok"
|
||||
\ What a console then shows is v3's:
|
||||
\ ok> 65 EMIT the console's prompt, and the line
|
||||
\ A ok what the line printed, then " ok"
|
||||
\ ok> NOSUCH
|
||||
\ UNKNOWN WORD: 'NOSUCH'
|
||||
\ ERROR
|
||||
@@ -22,7 +22,8 @@
|
||||
\ Address out of range an address fault (D-14), from any depth
|
||||
\ ERROR
|
||||
\ KEY, EXPECT and QUERY are FORTH-79 words that read characters, as before;
|
||||
\ they are not how a line reaches the node.
|
||||
\ they are not how text reaches the node. They still read the console's
|
||||
\ registers, CONSOLE-RX and CONSOLE-STATUS, and not a message.
|
||||
\
|
||||
\ THE STACKS are guarded (D-16): each counts what it holds, and a push onto
|
||||
\ a full one or a pop from an empty one is a fault. So QUIT and ABORT, which
|
||||
@@ -30,7 +31,10 @@
|
||||
\ the data stack too. A store to RSTACK-DEPTH or DSTACK-DEPTH does that.
|
||||
\
|
||||
\ Constants the loader supplies:
|
||||
\ (LINE-STATUS) word address of the variable: how the last line ended
|
||||
\ (LINE-STATUS) word address of the variable: how the last text ended
|
||||
\ (MSG) (REPLY) (ME) (OUT) (OUT^) see core.v4, "what a node prints"
|
||||
\ (PORT) word address of the node's port 0; (PORT)+8 is "any port" and
|
||||
\ (PORT)+9 the port the last read from that came on
|
||||
\ (Q) word address of four cells of scratch, which system.v4 and
|
||||
\ blocks.v4 use too
|
||||
\ DSTACK-DEPTH RSTACK-DEPTH word addresses of the stack registers
|
||||
@@ -52,26 +56,62 @@ macro R-CLEAR RSTACK-DEPTH b! a !b endmacro
|
||||
\ (docs/v4.0.0/NUCLEUS.md 6.3). (EMIT-HOOK), core.v4, is set to 0 here at
|
||||
\ the end of every line, caught or not.
|
||||
\
|
||||
\ ( k -- ) the line is over. It is entered with how -- 0 QUIT, 1 completed,
|
||||
\ anything else an error -- and never returns: it leaves that in
|
||||
\ (LINE-STATUS) and stops at (IDLE), where the host finds the node. It is
|
||||
\ always jumped to, never called, by something that has emptied the return
|
||||
\ stack (or by a fault, which empties it).
|
||||
: (IDLE) L: jump L
|
||||
\ ( s -- ) the text is done with. What it printed is sent, then a message
|
||||
\ saying how it ended -- 0 QUIT, 1 completed, 2 an error -- and the node waits
|
||||
\ for the next. (DONE) is entered with the same number and decides whether
|
||||
\ an error was one to catch; it is always jumped to, never called, by
|
||||
\ something that has emptied the return stack (or by a fault, which empties
|
||||
\ it). (LINE-STATUS) keeps how the last text ended.
|
||||
: (FINISH) ( s -- )
|
||||
(LINE-STATUS) b! dup !b
|
||||
(FLUSH-OUT)
|
||||
3 (HDR) 4 !b !b \ a message of type 3, four characters long: how it ended
|
||||
jump (IDLE)
|
||||
|
||||
: (DONE)
|
||||
(EMIT-HOOK) b! 0 !b \ the console is the console again
|
||||
(EMIT-HOOK) b! 0 !b \ what is printed is printed again
|
||||
if GO -1 + if OK
|
||||
drop (RESET)
|
||||
(CATCH) b! @b if LOUD drop -1 !b 1 jump (DONE) \ caught: the line completed
|
||||
LOUD: drop 2 (LINE-STATUS) b! !b jump (IDLE)
|
||||
OK: drop 1 (LINE-STATUS) b! !b jump (IDLE)
|
||||
GO: drop 0 (LINE-STATUS) b! !b jump (IDLE)
|
||||
(CATCH) b! @b if LOUD drop -1 !b 1 jump (DONE) \ caught: the text completed
|
||||
LOUD: drop 2 jump (FINISH)
|
||||
OK: drop 1 jump (FINISH)
|
||||
GO: drop 0 jump (FINISH)
|
||||
|
||||
\ ( -- ) THE LINE ENTRY. The host has put a line of text in TIB, a zero
|
||||
\ after it, and its length in SPAN, has emptied the return stack and set P
|
||||
\ here. The line is interpreted with one return entry under it, this
|
||||
\ word's call of INTERPRET; whatever is on the data stack stays there.
|
||||
\ ( -- ) WAITING. A node with nothing to do is blocked reading its ports
|
||||
\ (docs/v4.0.0/MESH.md 4.1): it executes nothing until a neighbour writes.
|
||||
\ What arrives is a message (core.v4). Its first word is taken from any
|
||||
\ port and the rest from the port that came on. Its text goes into TIB,
|
||||
\ which holds 1024 characters, the most a message carries; a longer one is
|
||||
\ read to its end, so that what follows it is not taken for a message, and
|
||||
\ is error 12. A message of type 1 for this node is text to interpret: it
|
||||
\ is done as a line was, and (FINISH) reports how it ended. Any other is
|
||||
\ let go: passing a message on is MESH.md step 4.
|
||||
: (IDLE)
|
||||
(PORT)+8 b! @b (MSG) a! !+ \ to, from any port
|
||||
(PORT)+9 b! @b (PORT) + dup b! \ B: the port it came on
|
||||
5 FOR @b !+ UNEXT \ from, type, heat and TTL, ACL tag, sequence, length
|
||||
(REPLY) a! !
|
||||
(MSG)+6 a! @ -if SIZED jump BAD \ a length below zero: no telling what follows
|
||||
SIZED: dup -1025 + -if LONG drop jump FITS
|
||||
LONG: drop 3 + 2/ 2/ -1 + FOR @b drop UNEXT 0 \ too long: read it and let it go
|
||||
BAD: drop 0 (MSG)+6 a! ! 0 SPAN a! ! NODE-ERROR b! 12 !b
|
||||
FITS:
|
||||
3 + 2/ 2/ if EMPTY
|
||||
-1 + TIB 2/ 2/ a! FOR @b !+ UNEXT
|
||||
jump READ
|
||||
EMPTY: drop
|
||||
READ:
|
||||
0 (MSG)+6 a! @ TIB + C! \ a zero after the text
|
||||
(MSG)+6 a! @ SPAN a! !
|
||||
(MSG) a! @ (ME) a! @ xor if MINE drop jump (IDLE)
|
||||
MINE: drop
|
||||
(MSG)+2 a! @ -1 + if TEXT drop jump (IDLE)
|
||||
TEXT: drop jump (LINE)
|
||||
|
||||
\ ( -- ) THE TEXT, INTERPRETED. It is in TIB with a zero after it and its
|
||||
\ length in SPAN, and the return stack is empty. It is interpreted with one
|
||||
\ return entry under it, this word's call of INTERPRET; whatever is on the
|
||||
\ data stack stays there.
|
||||
: (LINE)
|
||||
0 BLK a! ! \ the terminal, whatever was being loaded
|
||||
TIB (SRC) a! ! 0 >IN a! !
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#define V4_BOOT_H
|
||||
|
||||
#include "v4/image.h"
|
||||
#include "v4/message.h"
|
||||
|
||||
typedef struct {
|
||||
v4_node *n;
|
||||
@@ -66,8 +67,9 @@ typedef struct {
|
||||
int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks);
|
||||
|
||||
/* Hand the node a line and run it to its end, sending what it prints to
|
||||
* `out`. Returns how it ended -- V4_LINE_QUIT, V4_LINE_COMPLETED or
|
||||
* V4_LINE_ERROR (image.h) -- or one of these: */
|
||||
* `out`. The line is sent to the node as a message and what it prints
|
||||
* comes back as messages (message.h). Returns how it ended -- V4_TEXT_QUIT,
|
||||
* V4_TEXT_COMPLETED or V4_TEXT_ERROR -- or one of these: */
|
||||
#define V4_BOOT_LINE_TOO_LONG (-1) /* more than the node's input buffer holds; nothing was run */
|
||||
#define V4_BOOT_LINE_STOPPED (-2) /* the node stopped on a fault, or never finished */
|
||||
#define V4_BOOT_LINE_NO_INPUT (-3) /* the line read the keyboard and there is no more input */
|
||||
|
||||
+8
-30
@@ -10,6 +10,10 @@
|
||||
* (capsules/v4/nucleus-<width>.f18). docs/v4.0.0/MESH.md section 5.
|
||||
* - this description of it, a C file: the addresses a host needs.
|
||||
*
|
||||
* A node that has taken the nucleus in is sent text as messages and sends
|
||||
* back what it prints (v4/include/v4/message.h); nothing here hands it a
|
||||
* line.
|
||||
*
|
||||
* A description is for one cell width, one node size and one pair of stack
|
||||
* sizes; v4_image_born refuses one the engine was not built for.
|
||||
*/
|
||||
@@ -22,16 +26,10 @@ typedef struct {
|
||||
/* what the image was built for */
|
||||
unsigned cell_bits, node_words, data_ring, ret_ring;
|
||||
|
||||
/* The node is handed a line (docs/v4.0.0/ENGINE.md 3.1). Its host puts
|
||||
* text in the input buffer and starts it at `line`; it stops at `idle`,
|
||||
* with how the line ended in the cell `line_status`. At switch-on it
|
||||
* is at `idle`. */
|
||||
v4_cell line; /* (LINE) */
|
||||
v4_cell idle; /* (IDLE) */
|
||||
v4_cell line_status; /* word address of the variable (LINE-STATUS) */
|
||||
v4_cell tib; /* byte address of the input buffer */
|
||||
unsigned tib_bytes; /* its size: a line may be one less */
|
||||
v4_cell span; /* word address of the variable SPAN */
|
||||
/* Where the nucleus waits for a message (v4/capsule/quit.v4, (IDLE)):
|
||||
* the node's P is there, blocked reading its ports, when it has nothing
|
||||
* to do. */
|
||||
v4_cell idle;
|
||||
v4_cell fault_table; /* (FAULTS): where a fault goes */
|
||||
v4_cell port; /* word address of the node's port (node.h) */
|
||||
|
||||
@@ -67,11 +65,6 @@ extern const v4_image v4_capsule_image;
|
||||
* back to, and FORGET will not go below it. */
|
||||
void v4_image_seal(v4_node *n, const v4_image *im);
|
||||
|
||||
/* How a line ended. */
|
||||
#define V4_LINE_QUIT 0 /* QUIT: nothing is to be said */
|
||||
#define V4_LINE_COMPLETED 1 /* the host says " ok" */
|
||||
#define V4_LINE_ERROR 2 /* the message is printed; the host says " ERROR" */
|
||||
|
||||
/* A node newly born (node.h), with the registers the nucleus expects of the
|
||||
* node it runs on -- and `disk`, `blocks` blocks of 1024 bytes, as its block
|
||||
* storage, or none if `disk` is 0. Its memory is empty and it is blocked
|
||||
@@ -81,21 +74,6 @@ void v4_image_seal(v4_node *n, const v4_image *im);
|
||||
int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im,
|
||||
unsigned char *disk, unsigned blocks);
|
||||
|
||||
/* Hand the node `len` characters of text to interpret. Returns 1, or 0 if
|
||||
* the text is longer than the node's input buffer holds (nothing is then
|
||||
* done). After it the host runs the node -- v4_exec_step_word again and
|
||||
* again, taking what the node prints from n->console and setting
|
||||
* n->console_len back to 0 -- until v4_line_done; if v4_image_waiting comes
|
||||
* first, the line is reading the keyboard, and the host gives it characters
|
||||
* with v4_node_console_feed. */
|
||||
int v4_line_begin(v4_node *n, const v4_image *im, const char *text, unsigned len);
|
||||
|
||||
/* 1 when the node has finished the line it was handed and is idle again. */
|
||||
int v4_line_done(const v4_node *n, const v4_image *im);
|
||||
|
||||
/* How the line just finished ended: V4_LINE_QUIT, _COMPLETED or _ERROR. */
|
||||
int v4_line_status(const v4_node *n, const v4_image *im);
|
||||
|
||||
/* 1 if the node is waiting for a character: it is inside KEY and none is
|
||||
* pending. */
|
||||
int v4_image_waiting(const v4_node *n, const v4_image *im);
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/* message.h -- a message, as it is written to a port and read from one.
|
||||
* docs/v4.0.0/MESH.md section 6.
|
||||
*
|
||||
* The ports are the transport; the message is what is transported. It is
|
||||
* v3's Hermes message with what it carries, a word at a time:
|
||||
*
|
||||
* word 0 to: the node it is for
|
||||
* word 1 from: the node that sent it
|
||||
* word 2 type
|
||||
* word 3 heat and TTL
|
||||
* word 4 ACL tag: the sender's identity fingerprint
|
||||
* word 5 sequence
|
||||
* word 6 how many characters of text follow, 0 to 1024
|
||||
* word 7.. the text, four characters to a word, the first lowest
|
||||
*
|
||||
* Words 3, 4 and 5 are carried and not yet used: checking them is the
|
||||
* router's, and awaits rulings (MESH.md section 3).
|
||||
*
|
||||
* This is for whatever is on the other end of a node's port and is not a
|
||||
* node -- a console, a host -- to speak what the nodes speak (v4/capsule/
|
||||
* core.v4 and quit.v4 are the same thing in the node). Nothing here uses
|
||||
* the C library.
|
||||
*/
|
||||
#ifndef V4_MESSAGE_H
|
||||
#define V4_MESSAGE_H
|
||||
|
||||
#include "v4/cell.h"
|
||||
|
||||
#define V4_MSG_HEADER 7u
|
||||
#define V4_MSG_MAX_CHARS 1024u /* a block */
|
||||
#define V4_MSG_MAX_WORDS (V4_MSG_HEADER + V4_MSG_MAX_CHARS / 4u)
|
||||
|
||||
#define V4_MSG_TEXT 1 /* text to be interpreted */
|
||||
#define V4_MSG_OUTPUT 2 /* what a node printed */
|
||||
#define V4_MSG_DONE 3 /* how the text ended: one word, V4_TEXT_* */
|
||||
|
||||
/* how text ended */
|
||||
#define V4_TEXT_QUIT 0 /* QUIT: nothing is to be said */
|
||||
#define V4_TEXT_COMPLETED 1 /* a console says " ok" */
|
||||
#define V4_TEXT_ERROR 2 /* the message is printed; a console says " ERROR" */
|
||||
|
||||
typedef struct {
|
||||
v4_cell word[V4_MSG_MAX_WORDS];
|
||||
unsigned count; /* words in it */
|
||||
} v4_message;
|
||||
|
||||
/* A message of `len` characters, at most V4_MSG_MAX_CHARS. Returns how many
|
||||
* words it is, or 0 if `len` is too many. */
|
||||
unsigned v4_message_text(v4_message *m, v4_cell to, v4_cell from, v4_cell type, const char *chars, unsigned len);
|
||||
|
||||
/* A message arriving a word at a time. Start with m->count = 0 and give
|
||||
* each word as it comes: returns 1 when the message is whole, 0 if there is
|
||||
* more to come. A length that is not 0 to V4_MSG_MAX_CHARS is taken as 0. */
|
||||
int v4_message_word(v4_message *m, v4_cell word);
|
||||
|
||||
/* Its parts. */
|
||||
#define v4_message_to(m) ((m)->word[0])
|
||||
#define v4_message_from(m) ((m)->word[1])
|
||||
#define v4_message_type(m) ((m)->word[2])
|
||||
unsigned v4_message_length(const v4_message *m); /* characters of text */
|
||||
char v4_message_char(const v4_message *m, unsigned i); /* the i-th of them */
|
||||
|
||||
#endif /* V4_MESSAGE_H */
|
||||
@@ -29,39 +29,6 @@ int v4_image_waiting(const v4_node *n, const v4_image *im)
|
||||
return n->input_pos == n->input_len && n->p >= im->key_start && n->p < im->key_end;
|
||||
}
|
||||
|
||||
/* One byte of the node's memory: four to a cell, the first lowest, at either
|
||||
* cell width -- as C! stores them (v4/capsule/core.v4). */
|
||||
static void put_byte(v4_node *n, v4_cell baddr, unsigned c)
|
||||
{
|
||||
v4_cell word = baddr / 4;
|
||||
unsigned shift = 8u * (unsigned)(baddr % 4);
|
||||
v4_ucell mask = (v4_ucell)0xFFu << shift;
|
||||
n->mem[word] = (v4_cell)(((v4_ucell)n->mem[word] & ~mask) | ((v4_ucell)(c & 0xFFu) << shift));
|
||||
}
|
||||
|
||||
int v4_line_begin(v4_node *n, const v4_image *im, const char *text, unsigned len)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (len + 1u > im->tib_bytes) return 0;
|
||||
for (i = 0; i < len; i++) put_byte(n, im->tib + (v4_cell)i, (unsigned char)text[i]);
|
||||
put_byte(n, im->tib + (v4_cell)len, 0);
|
||||
n->mem[im->span] = (v4_cell)len;
|
||||
v4_rstack_reset(&n->rs);
|
||||
n->p = im->line;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int v4_line_done(const v4_node *n, const v4_image *im)
|
||||
{
|
||||
return n->p == im->idle;
|
||||
}
|
||||
|
||||
int v4_line_status(const v4_node *n, const v4_image *im)
|
||||
{
|
||||
return (int)n->mem[im->line_status];
|
||||
}
|
||||
|
||||
void v4_image_seal(v4_node *n, const v4_image *im)
|
||||
{
|
||||
n->mem[im->boot_cells] = n->mem[im->dp];
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/* message.c -- a message, as it is written to a port and read from one.
|
||||
* See message.h. Nothing here uses the C library.
|
||||
*/
|
||||
#include "v4/message.h"
|
||||
|
||||
unsigned v4_message_text(v4_message *m, v4_cell to, v4_cell from, v4_cell type, const char *chars, unsigned len)
|
||||
{
|
||||
unsigned i, words;
|
||||
|
||||
if (len > V4_MSG_MAX_CHARS) return 0;
|
||||
words = (len + 3u) / 4u;
|
||||
m->word[0] = to;
|
||||
m->word[1] = from;
|
||||
m->word[2] = type;
|
||||
m->word[3] = 0;
|
||||
m->word[4] = 0;
|
||||
m->word[5] = 0;
|
||||
m->word[6] = (v4_cell)len;
|
||||
for (i = 0; i < words; i++) m->word[V4_MSG_HEADER + i] = 0;
|
||||
for (i = 0; i < len; i++)
|
||||
m->word[V4_MSG_HEADER + i / 4u] = (v4_cell)((v4_ucell)m->word[V4_MSG_HEADER + i / 4u]
|
||||
| ((v4_ucell)(unsigned char)chars[i] << (8u * (i % 4u))));
|
||||
m->count = V4_MSG_HEADER + words;
|
||||
return m->count;
|
||||
}
|
||||
|
||||
unsigned v4_message_length(const v4_message *m)
|
||||
{
|
||||
v4_cell len = m->word[6];
|
||||
return (len < 0 || len > (v4_cell)V4_MSG_MAX_CHARS) ? 0u : (unsigned)len;
|
||||
}
|
||||
|
||||
char v4_message_char(const v4_message *m, unsigned i)
|
||||
{
|
||||
return (char)(((v4_ucell)m->word[V4_MSG_HEADER + i / 4u] >> (8u * (i % 4u))) & 0xFFu);
|
||||
}
|
||||
|
||||
int v4_message_word(v4_message *m, v4_cell word)
|
||||
{
|
||||
if (m->count < V4_MSG_MAX_WORDS) m->word[m->count] = word;
|
||||
m->count++;
|
||||
if (m->count < V4_MSG_HEADER) return 0;
|
||||
return m->count >= V4_MSG_HEADER + (v4_message_length(m) + 3u) / 4u;
|
||||
}
|
||||
+56
-16
@@ -5,6 +5,7 @@
|
||||
* which only a whole system has.
|
||||
*/
|
||||
#include "v4/boot.h"
|
||||
#include "v4/message.h"
|
||||
#include "starkernel/capsule.h"
|
||||
#include "starkernel/capsule_generated.h"
|
||||
#include "starkernel/capsule_sig.h"
|
||||
@@ -126,22 +127,29 @@ static void post_watch(char c)
|
||||
post_len = 0;
|
||||
}
|
||||
|
||||
/* The boot is the node's neighbour on two of its ports. On port 0 it is
|
||||
* the kernel: the node writes the number of a request there (ENGINE.md
|
||||
* 3.3). On port 1 it is the console: it writes the node a message of text
|
||||
* and reads back messages of what the node printed and of how the text
|
||||
* ended (message.h). */
|
||||
#define KERNEL_PORT 0u
|
||||
#define CONSOLE_PORT 1u
|
||||
#define CONSOLE_ID 1 /* the console's number as a sender; the node's is 0 until it is given one */
|
||||
|
||||
int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
|
||||
{
|
||||
static v4_message out, in; /* one line at a time: the boot is not re-entered */
|
||||
v4_node *n = b->n;
|
||||
uint64_t steps = 0;
|
||||
unsigned i;
|
||||
unsigned sent = 0, i;
|
||||
|
||||
if (!v4_line_begin(n, b->im, text, len)) return V4_BOOT_LINE_TOO_LONG;
|
||||
if (!v4_message_text(&out, 0, CONSOLE_ID, V4_MSG_TEXT, text, len)) return V4_BOOT_LINE_TOO_LONG;
|
||||
in.count = 0;
|
||||
for (;;) {
|
||||
(void)v4_exec_step_word(n, b->es, b->h);
|
||||
if (n->console_len) {
|
||||
for (i = 0; i < n->console_len; i++) post_watch((char)n->console[i]);
|
||||
b->out((const char *)n->console, n->console_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 (n->asking && n->ask_port == KERNEL_PORT) { /* a request: 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);
|
||||
@@ -153,8 +161,34 @@ int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
|
||||
}
|
||||
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 */
|
||||
if (n->asking && n->ask_port == CONSOLE_PORT) { /* a word of a message from the node */
|
||||
v4_cell word = n->request;
|
||||
v4_node_port_served(n);
|
||||
if (v4_message_word(&in, word)) {
|
||||
unsigned chars = v4_message_length(&in);
|
||||
if (v4_message_type(&in) == V4_MSG_OUTPUT) {
|
||||
for (i = 0; i < chars; i++) {
|
||||
char c = v4_message_char(&in, i);
|
||||
post_watch(c);
|
||||
b->out(&c, 1);
|
||||
}
|
||||
} else if (v4_message_type(&in) == V4_MSG_DONE) {
|
||||
return (int)in.word[V4_MSG_HEADER];
|
||||
}
|
||||
in.count = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (n->asking) return V4_BOOT_LINE_STOPPED; /* a port with nothing on it */
|
||||
|
||||
if (n->reading && !n->given) {
|
||||
if (sent < out.count && (n->read_port == CONSOLE_PORT || n->read_port == V4_PORT_ANY)) {
|
||||
v4_node_port_give(n, CONSOLE_PORT, out.word[sent++]); /* a word of the message to the node */
|
||||
continue;
|
||||
}
|
||||
return V4_BOOT_LINE_STOPPED; /* waiting for what will not come */
|
||||
}
|
||||
if (v4_image_waiting(n, b->im)) { /* the text is reading the keyboard */
|
||||
int c = b->key ? b->key() : V4_BOOT_KEY_END;
|
||||
unsigned char ch = (unsigned char)c;
|
||||
if (c == V4_BOOT_KEY_END) return V4_BOOT_LINE_NO_INPUT;
|
||||
@@ -225,10 +259,10 @@ static int load_capsule(const v4_boot *b, const char *name)
|
||||
if (len) {
|
||||
for (i = 0; i < len; i++) text[i] = (char)p[i];
|
||||
int how = v4_boot_line(b, text, len);
|
||||
if (how != V4_LINE_COMPLETED) {
|
||||
if (how != V4_TEXT_COMPLETED) {
|
||||
say_where(b, name, block, line);
|
||||
say(b, how == V4_LINE_ERROR ? " ended in an error: "
|
||||
: how == V4_LINE_QUIT ? " ended with QUIT: "
|
||||
say(b, how == V4_TEXT_ERROR ? " ended in an error: "
|
||||
: how == V4_TEXT_QUIT ? " ended with QUIT: "
|
||||
: how == V4_BOOT_LINE_NO_INPUT ? " reads the keyboard: "
|
||||
: " stopped the node: ");
|
||||
b->out(text, len); say(b, "\n");
|
||||
@@ -289,7 +323,9 @@ static int load_nucleus(const v4_boot *b)
|
||||
if (!p || cap->length % CELL_BYTES != 0) { say(b, "V4: capsule " NUCLEUS_NAME " is not whole words\n"); return 0; }
|
||||
words = cap->length / CELL_BYTES;
|
||||
|
||||
while (at < words || !v4_line_done(n, b->im)) {
|
||||
/* until it has taken every word and is waiting for a message: reading
|
||||
* its ports again, but from its memory now and not from the port */
|
||||
while (at < words || !(n->reading && !n->given && n->p != b->im->port + (v4_cell)V4_PORT_ANY)) {
|
||||
(void)v4_exec_step_word(n, b->es, b->h);
|
||||
if (n->stopped || n->asking || ++steps > STEP_LIMIT) {
|
||||
say(b, "V4: the node did not take the nucleus in\n");
|
||||
@@ -298,7 +334,11 @@ static int load_nucleus(const v4_boot *b)
|
||||
if (n->reading && !n->given) {
|
||||
v4_ucell word = 0;
|
||||
unsigned i;
|
||||
if (at >= words) { say(b, "V4: the node wants more than the nucleus holds\n"); return 0; }
|
||||
if (at >= words) {
|
||||
if (n->p != b->im->port + (v4_cell)V4_PORT_ANY) break; /* it is waiting for a message */
|
||||
say(b, "V4: the node wants more than the nucleus holds\n");
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < CELL_BYTES; i++) word |= (v4_ucell)p[at * CELL_BYTES + i] << (8 * i);
|
||||
v4_node_port_give(n, 0, (v4_cell)word);
|
||||
at++;
|
||||
@@ -335,7 +375,7 @@ int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks)
|
||||
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) {
|
||||
if (b->words[i][k] || v4_boot_line(b, text, at) != V4_TEXT_COMPLETED) {
|
||||
say(b, "V4: kernel word "); say(b, b->words[i]); say(b, " could not be made\nPARITY:FAIL\nPOST: FAILED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,12 @@
|
||||
#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 - 32) /* the node's ports (node.h): V4_PORTS of them, then "any port" and "which port". Port 0 is where its requests go. */
|
||||
#define ME (BUF0_W - 13) /* this node's number: a message is for it when its first word is this */
|
||||
#define OUT_PTR (BUF0_W - 14) /* where the next character printed goes: a cell of the output buffer */
|
||||
#define REPLY (BUF0_W - 15) /* the address of the port the message being served came on */
|
||||
#define MSG (BUF0_W - 48) /* the header of the message being served, 7 cells */
|
||||
#define OUT_W (BUF0_W - 400) /* the output buffer: 256 cells, a character to a cell */
|
||||
#define OUT_CELLS 256
|
||||
#define WORD_DEFINED (BUF0_W - 11) /* the xt of the word that is told of each new entry, or 0 */
|
||||
#define WORD_FORGOTTEN (BUF0_W - 12) /* the xt of the word that is told when entries go, or 0 */
|
||||
#define LOG_LEVEL (BUF0_W - 5) /* the level at or below which a message is printed */
|
||||
@@ -108,6 +114,7 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
unsigned i;
|
||||
|
||||
v4_node_reset(n);
|
||||
n->mem[OUT_PTR] = OUT_W; /* nothing has been printed: what is, is kept from here (core.v4) */
|
||||
v4_text_begin(tx, n, 16);
|
||||
v4_text_constant(tx, "N-1", V4_CELL_BITS - 1);
|
||||
v4_text_constant(tx, "NODE-ERROR", NODE_ERROR);
|
||||
@@ -148,6 +155,11 @@ static int host_load(v4_text *tx, v4_node *n, const char *const *files, unsigned
|
||||
v4_text_constant(tx, "(CATCH)", CATCH);
|
||||
v4_text_constant(tx, "(LINE-STATUS)", LINE_STATUS);
|
||||
v4_text_constant(tx, "(PORT)", PORT);
|
||||
v4_text_constant(tx, "(ME)", ME);
|
||||
v4_text_constant(tx, "(OUT^)", OUT_PTR);
|
||||
v4_text_constant(tx, "(REPLY)", REPLY);
|
||||
v4_text_constant(tx, "(MSG)", MSG);
|
||||
v4_text_constant(tx, "(OUT)", OUT_W);
|
||||
v4_text_constant(tx, "(WORD-DEFINED)", WORD_DEFINED);
|
||||
v4_text_constant(tx, "(WORD-FORGOTTEN)", WORD_FORGOTTEN);
|
||||
v4_text_constant(tx, "BLOCK-NUMBER", STORAGE_REG);
|
||||
|
||||
@@ -263,7 +263,10 @@ int main(void)
|
||||
}
|
||||
n.mem[BASE] = 10;
|
||||
CHECK(v4_node_console_feed(&n, "Z", 1) == 1 && run("KEY", 0, 0, 0, 0) && pop() == 'Z' && clean(), "KEY");
|
||||
CHECK(run("EMIT", 1, 'q', 0, 0) && clean() && n.console_len == 1 && n.console[0] == 'q', "EMIT");
|
||||
/* a node has no console of its own: what it prints is kept to be sent (core.v4) */
|
||||
n.mem[OUT_PTR] = OUT_W;
|
||||
CHECK(run("EMIT", 1, 'q', 0, 0) && clean() && n.mem[OUT_PTR] == OUT_W + 1 && n.mem[OUT_W] == 'q', "EMIT");
|
||||
CHECK(run("EMIT", 1, 0x141, 0, 0) && clean() && n.mem[OUT_PTR] == OUT_W + 2 && n.mem[OUT_W + 1] == 'A', "EMIT prints the low 8 bits");
|
||||
}
|
||||
|
||||
/* ---- the in-line names ---- */
|
||||
|
||||
+75
-38
@@ -41,7 +41,7 @@
|
||||
* 3 ROLL is ROT. v3's count from zero.
|
||||
*/
|
||||
#include "v4/text.h"
|
||||
#include "v4/image.h"
|
||||
#include "v4/message.h"
|
||||
#include "v4/testcode.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -59,7 +59,7 @@ static v4_exec_state es;
|
||||
static v4_heat h;
|
||||
static v4_text tx;
|
||||
static v4_cell w_key, w_key_end, w_fault, capsule_latest;
|
||||
static v4_image img; /* where a line is handed to the node: v4/include/v4/image.h */
|
||||
static v4_cell w_idle; /* (IDLE): where the node waits for a message */
|
||||
static char out[V4_CONSOLE_CAP + 1];
|
||||
|
||||
/* The storage device's blocks: zeros at every switch-on. */
|
||||
@@ -73,16 +73,6 @@ static void put_block(unsigned num, const char *text)
|
||||
}
|
||||
static long last_steps;
|
||||
|
||||
/* THE HOST. A node is handed a line and prints no prompt (ENGINE.md 3.1);
|
||||
* these tests are its host, and do what tools/hosted.c does: take what is
|
||||
* typed a line at a time, hand each line over, and say " ok" or " ERROR" and
|
||||
* the next prompt. What is typed after a line is there for a word in it
|
||||
* that reads the keyboard, and what such a word does not take is the next
|
||||
* line. */
|
||||
static char typed[8192]; /* typed and not yet taken */
|
||||
static unsigned typed_len;
|
||||
static int line_open; /* a line has been handed over and has not ended */
|
||||
|
||||
/* THE KERNEL. A node asks its kernel by writing to its port (node.h), and
|
||||
* these tests are that too. Request 1 is "an entry has been made" ( xt -- )
|
||||
* and request 2 "the entries from here up have gone" ( w -- ): the kernel's
|
||||
@@ -149,7 +139,31 @@ static const char *node_name(v4_cell xt)
|
||||
return name;
|
||||
}
|
||||
|
||||
/* Run until the line ends, or until it has been inside KEY with nothing to
|
||||
/* THE CONSOLE. A node is sent text as a message and sends back what it
|
||||
* prints (MESH.md section 6); these tests are its console, on its port 1,
|
||||
* and do what tools/hosted.c does: take what is typed a line at a time,
|
||||
* send each line as a message, show what comes back, and say " ok" or
|
||||
* " ERROR" and the next prompt. What is typed after a line is there for a
|
||||
* word in it that reads the keyboard, and what such a word does not take is
|
||||
* the next line. */
|
||||
#define CONSOLE_PORT 1u
|
||||
static char typed[8192]; /* typed and not yet taken */
|
||||
static unsigned typed_len;
|
||||
static int line_open; /* a line has been sent and has not ended */
|
||||
static v4_message to_node, from_node; /* the message going in, and the one coming out */
|
||||
static unsigned to_sent; /* how many words of to_node the node has taken */
|
||||
static char shown[V4_CONSOLE_CAP]; /* what the node has printed since the console last looked */
|
||||
static unsigned shown_len;
|
||||
static int shown_dropped;
|
||||
static int text_ended; /* how the text ended, when it has */
|
||||
|
||||
/* 1 if the node is where it waits for a message, with nothing given it. */
|
||||
static int node_idle(void)
|
||||
{
|
||||
return n.reading && !n.given && n.read_port == V4_PORT_ANY && n.p > w_idle && n.p <= w_idle + 3;
|
||||
}
|
||||
|
||||
/* Run until the text ends, or until it has been inside KEY with nothing to
|
||||
* read for 64 instruction words; or until `max` words. 1: ended, 2: waiting
|
||||
* for a character, 0: neither. */
|
||||
static int run_line(long max)
|
||||
@@ -157,14 +171,36 @@ static int run_line(long max)
|
||||
long steps = 0;
|
||||
unsigned idle = 0;
|
||||
while (steps < max && idle < 64) {
|
||||
if (v4_line_done(&n, &img)) { last_steps = steps; return 1; }
|
||||
(void)v4_exec_step_word(&n, &es, &h);
|
||||
steps++;
|
||||
if (n.asking) { kernel_serve(); v4_node_port_served(&n); } /* blocked at its port: the kernel's turn */
|
||||
if (n.asking && n.ask_port == 0) { kernel_serve(); v4_node_port_served(&n); continue; } /* a request */
|
||||
if (n.asking && n.ask_port == CONSOLE_PORT) { /* a word of a message from the node */
|
||||
v4_cell word = n.request;
|
||||
v4_node_port_served(&n);
|
||||
if (v4_message_word(&from_node, word)) {
|
||||
unsigned i, chars = v4_message_length(&from_node);
|
||||
if (v4_message_type(&from_node) == V4_MSG_OUTPUT) {
|
||||
for (i = 0; i < chars; i++) {
|
||||
if (shown_len < sizeof shown) shown[shown_len++] = v4_message_char(&from_node, i);
|
||||
else shown_dropped = 1;
|
||||
}
|
||||
} else if (v4_message_type(&from_node) == V4_MSG_DONE) {
|
||||
text_ended = (int)from_node.word[V4_MSG_HEADER];
|
||||
from_node.count = 0;
|
||||
last_steps = steps;
|
||||
return 1;
|
||||
}
|
||||
from_node.count = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (n.reading && !n.given && to_sent < to_node.count && (n.read_port == CONSOLE_PORT || n.read_port == V4_PORT_ANY)) {
|
||||
v4_node_port_give(&n, CONSOLE_PORT, to_node.word[to_sent++]); /* a word of the message to the node */
|
||||
continue;
|
||||
}
|
||||
if (n.input_pos == n.input_len && n.p >= w_key && n.p < w_key_end) idle++; else idle = 0;
|
||||
}
|
||||
last_steps = steps;
|
||||
if (v4_line_done(&n, &img)) return 1;
|
||||
return idle >= 64 ? 2 : 0;
|
||||
}
|
||||
|
||||
@@ -193,7 +229,6 @@ static void boot_with(unsigned depth)
|
||||
for (v4_cell k = BVARS; k < BVARS + 14; k++) if (k != SRC) n.mem[k] = 0; /* empty buffers, SCR and BLK 0, no hook */
|
||||
memset(disk, 0, sizeof disk);
|
||||
v4_node_storage_attach(&n, STORAGE_REG, disk, DISK_BLOCKS);
|
||||
v4_node_console_attach(&n, CONSOLE_TX);
|
||||
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST);
|
||||
v4_node_fault_attach(&n, w_fault);
|
||||
v4_node_error_attach(&n, NODE_ERROR);
|
||||
@@ -208,10 +243,16 @@ static void boot_with(unsigned depth)
|
||||
n.mem[WORD_FORGOTTEN] = 0;
|
||||
known_n = 0;
|
||||
last_request = 0;
|
||||
n.mem[LINE_STATUS] = V4_LINE_COMPLETED;
|
||||
n.p = img.idle; /* idle, waiting to be handed a line */
|
||||
n.mem[LINE_STATUS] = 1;
|
||||
n.mem[ME] = 0; /* it has no number yet: a message to 0 is for it */
|
||||
n.mem[OUT_PTR] = OUT_W;
|
||||
n.mem[REPLY] = PORT + CONSOLE_PORT;
|
||||
n.p = w_idle; /* waiting for a message */
|
||||
typed_len = 0;
|
||||
line_open = 0;
|
||||
to_node.count = 0; to_sent = 0; from_node.count = 0;
|
||||
shown_len = 0; shown_dropped = 0;
|
||||
(void)v4_exec_step_word(&n, &es, &h); /* it reads its ports, and is blocked */
|
||||
}
|
||||
static void boot(void) { boot_with(0); }
|
||||
/* The same with nothing at all on the data stack. */
|
||||
@@ -252,27 +293,27 @@ static const char *say(const char *input)
|
||||
unsigned len;
|
||||
if (!nl) break;
|
||||
len = (unsigned)(nl - typed);
|
||||
if (!v4_line_begin(&n, &img, typed, len)) return "(line too long)";
|
||||
if (!v4_message_text(&to_node, 0, 1, V4_MSG_TEXT, typed, len)) return "(line too long)";
|
||||
to_sent = 0;
|
||||
memmove(typed, nl + 1, typed_len - len - 1);
|
||||
typed_len -= len + 1;
|
||||
line_open = 1;
|
||||
}
|
||||
v4_node_console_attach(&n, CONSOLE_TX); /* empties the capture */
|
||||
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST); /* and the keyboard */
|
||||
shown_len = 0; shown_dropped = 0;
|
||||
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST); /* empties the keyboard */
|
||||
if (v4_node_console_feed(&n, typed, typed_len) != typed_len) return "(input queue full)";
|
||||
ended = run_line(40000000);
|
||||
took = n.input_pos; /* what a word in the line read */
|
||||
memmove(typed, typed + took, typed_len - took);
|
||||
typed_len -= took;
|
||||
if (n.console_dropped || olen + n.console_len + 16 > sizeof out) return "(too much output)";
|
||||
memcpy(out + olen, n.console, n.console_len);
|
||||
olen += n.console_len;
|
||||
if (shown_dropped || olen + shown_len + 16 > sizeof out) return "(too much output)";
|
||||
memcpy(out + olen, shown, shown_len);
|
||||
olen += shown_len;
|
||||
out[olen] = 0;
|
||||
if (ended == 0) return "(still running)";
|
||||
if (ended == 2) break;
|
||||
line_open = 0;
|
||||
tail = v4_line_status(&n, &img) == V4_LINE_COMPLETED ? " ok\nok> "
|
||||
: v4_line_status(&n, &img) == V4_LINE_ERROR ? " ERROR\nok> " : "ok> ";
|
||||
tail = text_ended == V4_TEXT_COMPLETED ? " ok\nok> " : text_ended == V4_TEXT_ERROR ? " ERROR\nok> " : "ok> ";
|
||||
memcpy(out + olen, tail, strlen(tail) + 1);
|
||||
olen += (unsigned)strlen(tail);
|
||||
}
|
||||
@@ -739,12 +780,7 @@ int main(void)
|
||||
CHECK(v4_text_here(&tx) < DICT_W, "code stays below the dictionary space");
|
||||
printf(" capsule: %ld words\n", (long)v4_text_here(&tx) - 16);
|
||||
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
|
||||
img.line = v4_text_word(&tx, "(LINE)");
|
||||
img.idle = v4_text_word(&tx, "(IDLE)");
|
||||
img.line_status = LINE_STATUS;
|
||||
img.tib = TIB;
|
||||
img.tib_bytes = 1025u;
|
||||
img.span = SPAN;
|
||||
w_idle = v4_text_word(&tx, "(IDLE)");
|
||||
w_key = v4_text_word(&tx, "KEY");
|
||||
w_key_end = v4_text_word(&tx, "CR"); /* the word after KEY in core.v4 */
|
||||
w_fault = v4_text_word(&tx, "(FAULTS)");
|
||||
@@ -753,11 +789,12 @@ int main(void)
|
||||
|
||||
/* ---- switch-on ---- */
|
||||
boot();
|
||||
CHECK(v4_line_done(&n, &img) && n.console_len == 0, "the node is idle and has printed nothing: the prompt is its host's");
|
||||
CHECK(run_line(5000) == 1 && last_steps == 0, "and goes on waiting to be handed a line");
|
||||
CHECK(node_idle() && n.mem[OUT_PTR] == OUT_W, "the node is waiting at its ports and has printed nothing: the prompt is its console's");
|
||||
{ v4_uheat_t clock = es.anticlock;
|
||||
CHECK(run_line(5000) == 0 && es.anticlock == clock, "and goes on waiting, executing nothing"); }
|
||||
CHECK(canary_under_expect(), "with the data stack as it found it");
|
||||
(void)v4_exec_step_word(&n, &es, &h);
|
||||
CHECK(v4_line_done(&n, &img), "it stays idle however long it is run");
|
||||
CHECK(node_idle(), "it stays waiting however long it is run");
|
||||
|
||||
/* ---- the sessions ---- */
|
||||
for (i = 0; i < NSCRIPT; i++) {
|
||||
@@ -883,7 +920,7 @@ int main(void)
|
||||
memcpy(line, "65 EMIT", 7);
|
||||
line[1025] = '\n'; line[1026] = 0;
|
||||
CHECK(is(say(line), "(line too long)"), "one of 1025 is not taken: the node's input buffer holds a block");
|
||||
CHECK(v4_line_done(&n, &img) && n.console_len == 0, "and nothing of it was run");
|
||||
CHECK(node_idle() && n.mem[OUT_PTR] == OUT_W, "and nothing of it was sent");
|
||||
}
|
||||
|
||||
/* ---- ABORT and QUIT from deep in a programme, again and again ---- */
|
||||
@@ -1247,7 +1284,7 @@ int main(void)
|
||||
boot_bare();
|
||||
CHECK(load_source("tools.fth"), "tools.fth compiles, every line of it");
|
||||
CHECK(is(say(": SQ DUP * ; SEE SQ\n"), ": SQ\n dup call * \n ; \n ok\nok> "), "a definition: an in-line word, a call by name, the return");
|
||||
CHECK(is(say("SEE DUP\n"), ": DUP\n dup ; \n ok\nok> ") && is(say("SEE EMIT\n"), out) && strncmp(out, ": EMIT\n @p ", 12) == 0 && strstr(out, " b! !b ; \n"),
|
||||
CHECK(is(say("SEE DUP\n"), ": DUP\n dup ; \n ok\nok> ") && is(say("SEE EMIT\n"), out) && strncmp(out, ": EMIT\n @p ", 12) == 0 && strstr(out, " b! @b \n if ") && strstr(out, " pop a! ; \n"),
|
||||
"the capsule's own words; a literal's value follows its @p");
|
||||
CHECK(is(say("VARIABLE V 7 V ! SEE V\n"), ": V\n data: 7 \n ok\nok> ") && is(say("5 CONSTANT K SEE K\n"), ": K\n data: 5 \n ok\nok> "), "a variable and a constant show what they hold");
|
||||
CHECK(is(say(": IM 1 ; IMMEDIATE SEE IM\n"), ": IM\n @p 1 ; \nIMMEDIATE\n ok\nok> "), "an immediate word says so");
|
||||
|
||||
+2
-2
@@ -67,9 +67,9 @@ int main(void)
|
||||
len = strlen(line);
|
||||
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 (how == V4_TEXT_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_TEXT_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; }
|
||||
else if (how == V4_BOOT_LINE_STOPPED) { fflush(stdout); fprintf(stderr, "starforth4: the node stopped on a fault\n"); return 1; }
|
||||
|
||||
+4
-5
@@ -52,7 +52,7 @@ int main(int argc, char **argv)
|
||||
static const char *const files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4", "compile.v4", "quit.v4", "forth.v4",
|
||||
"numout.v4", "words.v4", "system.v4", "qmath.v4", "blocks.v4", "log.v4", "acl.v4" };
|
||||
FILE *out;
|
||||
v4_cell k, line, idle, faults, voc;
|
||||
v4_cell k, idle, faults, voc;
|
||||
unsigned count = 0;
|
||||
|
||||
if (argc != 3) die("usage: mkimage DESCRIPTION.c CAPSULE.f18", NULL);
|
||||
@@ -60,7 +60,6 @@ int main(int argc, char **argv)
|
||||
if (!host_load(&tx, &n, files, (unsigned)(sizeof files / sizeof files[0]))) die("the capsule does not assemble", NULL);
|
||||
if (!v4_text_finish(&tx)) die("not everything is defined", v4_text_error(&tx));
|
||||
if (v4_text_here(&tx) >= DICT_W) die("the capsule's code runs into the dictionary space", NULL);
|
||||
line = v4_text_word(&tx, "(LINE)");
|
||||
idle = v4_text_word(&tx, "(IDLE)");
|
||||
faults = v4_text_word(&tx, "(FAULTS)");
|
||||
w_key = v4_text_word(&tx, "KEY");
|
||||
@@ -99,7 +98,8 @@ int main(int argc, char **argv)
|
||||
for (voc = n.mem[VOC_LINK]; voc != 0; voc = n.mem[voc + 1]) clear_acl(n.mem[voc]);
|
||||
if (!v4_node_guards_intact(&n)) die("the node's guards are damaged", NULL);
|
||||
|
||||
n.mem[LINE_STATUS] = V4_LINE_COMPLETED; /* idle, with nothing to report */
|
||||
n.mem[LINE_STATUS] = 1; /* nothing has ended badly */
|
||||
n.mem[OUT_PTR] = OUT_W; /* nothing has been printed */
|
||||
|
||||
/* the nucleus as a capsule of F18 code: the words a neighbour writes to
|
||||
* an empty node's port, each in V4_CELL_BITS / 8 bytes, low byte first */
|
||||
@@ -113,8 +113,7 @@ int main(int argc, char **argv)
|
||||
fprintf(out, "/* Generated by v4/tools/mkimage.c from v4/capsule -- do not edit. */\n#include \"v4/image.h\"\n\n");
|
||||
fprintf(out, "const v4_image v4_capsule_image = {\n %uu, %uu, %uu, %uu,\n",
|
||||
(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, %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,\n %ld, %ld,\n %ld, %ld,\n", (long)idle, (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 %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,
|
||||
(long)BOOT_CELLS, (long)FENCE);
|
||||
|
||||
Reference in New Issue
Block a user