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:
rajames
2026-10-06 11:19:46 -04:00
co-authored by Claude Opus 5.5
parent 42610e844f
commit a041b401ea
24 changed files with 844 additions and 178 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated # 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. --> <!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live --> <!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. --> <!-- in MANIFEST.md alongside this auto-generated index. -->
Binary file not shown.
+23
View File
@@ -287,6 +287,29 @@ Each is tested, committed and pushed before the next.
6). 6).
3. **Messages (section 6):** a StarForth node that reads messages when 3. **Messages (section 6):** a StarForth node that reads messages when
idle; the console as a device; a line typed is a message. 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).** 4. **Finding the way (section 7).**
5. **Birth and Hera's requests (section 9); the unit of five.** 5. **Birth and Hera's requests (section 9); the unit of five.**
6. **Storage (section 8).** 6. **Storage (section 8).**
+1 -1
View File
@@ -613,7 +613,7 @@ V4_DEFS := -DSTARFORTH_V4=1 -Iv4/include \
KERNEL_CFLAGS += $(V4_DEFS) KERNEL_CFLAGS += $(V4_DEFS)
LOADER_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 V4_IMAGE_C := $(BUILD_DIR)/v4_image_64.c
LOADER_EXTRA_SRCS += $(KERNEL_SRC)/v4/sk_v4.c LOADER_EXTRA_SRCS += $(KERNEL_SRC)/v4/sk_v4.c
+2 -2
View File
@@ -100,8 +100,8 @@ void sk_v4_run(void)
console_puts("ok> "); console_puts("ok> ");
len = sk_v4_readline(); len = sk_v4_readline();
how = v4_boot_line(&boot, sk_v4_text, len); how = v4_boot_line(&boot, sk_v4_text, len);
if (how == V4_LINE_COMPLETED) console_puts(" ok\n"); if (how == V4_TEXT_COMPLETED) console_puts(" ok\n");
else if (how == V4_LINE_ERROR) console_puts(" ERROR\n"); else if (how == V4_TEXT_ERROR) console_puts(" ERROR\n");
else if (how == V4_BOOT_LINE_STOPPED) { else if (how == V4_BOOT_LINE_STOPPED) {
console_println("StarForth v4: the node stopped on a fault"); console_println("StarForth v4: the node stopped on a fault");
for (;;) { } for (;;) { }
@@ -0,0 +1,130 @@
[=3hBdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
[=3hStarKernel UEFI Loader
Loading kernel from ESP...
[CKPT 001] Entered efi_main - ConOut live
RAW SERIAL UP
[CKPT 002] Serial (COM1) initialized
Monolithic build - kernel linked directly
Collecting boot information...
CmdLine: parsed OK
[CKPT 004] Command line parsed
[CKPT 005] Kernel stack allocation decided
[CKPT 006] Boot info collected (ACPI table located)
GOP: linear framebuffer found
[CKPT 007] GOP: linear framebuffer found
[CKPT 008] About to enter ExitBootServices retry loop
EBS...
EBS OK
Calling kernel_main (monolithic)...
_____ _ _ __ _
/ ____| | | |/ / | |
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
____) | || (_| | | | . \ __/ | | | | | __/ |
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
LithosAnanke v2.1.0
Architecture: amd64
Build: Oct 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)
[=3h
@@ -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
[=3hRISC-V EDK2 firmware version 2025.11-3ubuntu7.3
Press ESCAPE within 5 seconds for boot options ERROR: C40000002:V03051002 I0 6D33944A-EC75-4855-A54D-809C75241F6C 83FFF850
BdsDxe: failed to load Boot0001 "UEFI Misc Device" fr
om PciRoot(0x0)/Pci(0x1,0x0): Not Found
[=3hStarKernel UEFI Loader
Loading kernel from ESP...
[CKPT 001] Entered efi_main - ConOut live
Monolithic build - kernel linked directly
Collecting boot information...
CmdLine: parsed OK
[CKPT 004] Command line parsed
[CKPT 005] Kernel stack allocation decided
[CKPT 006] Boot info collected (ACPI table located)
GOP: linear framebuffer found
[CKPT 007] GOP: linear framebuffer found
[CKPT 008] About to enter ExitBootServices retry loop
Calling kernel_main (monolithic)...
riscv64 item 4.3.5a: satp state at kernel entry (before switch)
satp.MODE = 0x000000000000000a
satp.PPN = 0x00000000000bf868
__kernel_start = 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)
[=3hBdsDxe: failed to load Boot0002 "UEFI Misc Device" from PciRoot(0x0)/Pci(0x2,0x0): Not Found
BdsDxe: failed to load Boot0003 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:04.0-1" from PciRoot(0x0)/Pci(0x4,0x0)/USB(0x0,0x0): Not Found
BdsDxe: loading Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
BdsDxe: starting Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
[=3hStarKernel UEFI Loader
Loading kernel from ESP...
[CKPT 001] Entered efi_main - ConOut live
Collecting boot information...
[CKPT 004] Command line parsed
[CKPT 005] Kernel stack allocation decided
[CKPT 006] Boot info collected (ACPI table located)
[CKPT 007] GOP: linear framebuffer found
[CKPT 008] About to enter ExitBootServices retry loop
_____ _ _ __ _
/ ____| | | |/ / | |
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
____) | || (_| | | | . \ __/ | | | | | __/ |
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
LithosAnanke v2.1.0
Architecture: aarch64
Build: Oct 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
View File
@@ -69,7 +69,7 @@ ROOT := $(abspath $(HERE)/..)
NUCLEUS_64 := $(ROOT)/capsules/v4/nucleus-64.f18 NUCLEUS_64 := $(ROOT)/capsules/v4/nucleus-64.f18
NUCLEUS_32 := $(BINDIR)/nucleus-32.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) 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 define IMAGE_RULE
$(BINDIR)/mkimage-$(1): $(HERE)/tools/mkimage.c $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/tests/host_map.h $(HERE)/Makefile $(BINDIR)/mkimage-$(1): $(HERE)/tools/mkimage.c $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/tests/host_map.h $(HERE)/Makefile
+12 -12
View File
@@ -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: 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_CAPSULE name=v4:forth79.4th capsule_id=... dict_hash=...
PARITY:V4_POST tests=550 pass=550 fail=0 PARITY:V4_POST tests=550 pass=550 fail=0
PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=... dict_hash=... PARITY:V4_CAPSULE name=v4:post79.4th capsule_id=... dict_hash=...
@@ -68,23 +68,23 @@ POST: PASSED
ok> ok>
``` ```
Bare-metal logs: `logs/20261006-102421/amd64/`, `logs/20261006-102706/aarch64/`, Bare-metal logs: `logs/20261006-110551/amd64/`, `logs/20261006-111621/aarch64/`,
`logs/20261006-103048/riscv64/`. On each, seven lines were then typed at the `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 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 `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 word no one serves, and `BYE` — and each was answered as the hosted binary
answers it. The answers it. The
capsules were unsigned (no signing key on this machine). 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 **A node is sent text as a message** (`docs/v4.0.0/MESH.md` section 6). It
its own command line and prints no prompt. Its host puts the text in the does not read its own command line, prints no prompt and has no console of
node's input buffer and starts it at `(LINE)`; the node interprets the text its own. With nothing to do it is blocked reading its ports. A neighbour
and stops at `(IDLE)`, leaving in `(LINE-STATUS)` how it ended. The host says writes it a message; text for it is interpreted; what it prints goes back
` ok` or ` ERROR` and prompts, as the kernel's REPL does for a v3 VM. A line as messages to the sender, and then a message saying how the text ended.
may be 1024 characters, a block. `v4_line_begin`, `v4_line_done` and Its console says ` ok` or ` ERROR` and prompts, as the kernel's REPL does
`v4_line_status` (`v4/include/v4/image.h`) are that interface; for a v3 VM. A message carries 1024 characters, a block. `v4/src/message.c`
`v4_boot_line` (`v4/system/boot.c`) is the one loop the hosted binary, the is the format for a host; `v4_boot_line` (`v4/system/boot.c`) is the
kernel and the capsule loader all run it with. console both products and the capsule loader use.
**A node asks its kernel by writing to its port** (`ENGINE.md` 3.3). The **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 write blocks the node until it has been served. A kernel word is a
+56 -8
View File
@@ -5,7 +5,8 @@
\ written here as text for the text assembler (v4/include/v4/text.h). \ 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, \ 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 \ 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 \ 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 ; DONE: drop drop drop ;
\ ---- section 5.10: the console ------------------------------------------- \ ---- section 5.10: the console -------------------------------------------
\ EMIT sends the character to the console, unless (EMIT-HOOK) holds the xt \ ---- what a node prints (docs/v4.0.0/MESH.md section 6) ------------------------
\ of a word: then that word is given it instead ( c -- ), and returns to \ A node has no console of its own. It does what a message asks, and what
\ EMIT's caller. Everything the node prints goes through EMIT, so a hook \ it prints while doing it goes back, as a message, to whoever sent that one,
\ sees all of it. The prompt takes the hook off at the end of every line \ on the port it came on. A message is seven words and then its text, four
\ (quit.v4), so that nothing can leave the console silent. POST uses it \ characters to a word:
\ to compare what a case prints (docs/v4.0.0/NUCLEUS.md 6.3). \ 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 header EMIT
: EMIT ( c -- ) : EMIT ( c -- )
(EMIT-HOOK) b! @b if NONE push ; (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 header KEY
: KEY ( -- c ) : KEY ( -- c )
L: CONSOLE-STATUS b! @b if WAIT drop CONSOLE-RX b! @b ; L: CONSOLE-STATUS b! @b if WAIT drop CONSOLE-RX b! @b ;
+66 -26
View File
@@ -4,17 +4,17 @@
\ DECOMPOSITION.md 5.15 and 5.10: QUIT ABORT ABORT" (ABORT") ." (."). Part of \ DECOMPOSITION.md 5.15 and 5.10: QUIT ABORT ABORT" (ABORT") ." (."). Part of
\ the compiler capsule. Rests on all the files before it. \ 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 \ 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: that is its host's, as it is the \ own command line and prints no prompt. A node with nothing to do waits at
\ kernel's REPL's for a v3 VM. The host puts the text in TIB and starts \ its ports; a neighbour writes it a message; if that is text for this node
\ (LINE); the node interprets it and stops at (IDLE), having left in the \ it is interpreted; what it printed goes back as a message, and then a
\ variable (LINE-STATUS) how the line ended: \ message saying how the text ended:
\ 1 it completed the host says " ok" \ 1 it completed a console says " ok"
\ 2 it ended in an error the message is already printed; " ERROR" \ 2 it ended in an error the message is already printed; " ERROR"
\ 0 QUIT nothing is said \ 0 QUIT nothing is said
\ What the console then shows is v3's: \ What a console then shows is v3's:
\ ok> 65 EMIT the host's prompt, and the line \ ok> 65 EMIT the console's prompt, and the line
\ A ok what the line printed, then the host's " ok" \ A ok what the line printed, then " ok"
\ ok> NOSUCH \ ok> NOSUCH
\ UNKNOWN WORD: 'NOSUCH' \ UNKNOWN WORD: 'NOSUCH'
\ ERROR \ ERROR
@@ -22,7 +22,8 @@
\ Address out of range an address fault (D-14), from any depth \ Address out of range an address fault (D-14), from any depth
\ ERROR \ ERROR
\ KEY, EXPECT and QUERY are FORTH-79 words that read characters, as before; \ 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 \ 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 \ 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. \ the data stack too. A store to RSTACK-DEPTH or DSTACK-DEPTH does that.
\ \
\ Constants the loader supplies: \ 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 \ (Q) word address of four cells of scratch, which system.v4 and
\ blocks.v4 use too \ blocks.v4 use too
\ DSTACK-DEPTH RSTACK-DEPTH word addresses of the stack registers \ 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 \ (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. \ the end of every line, caught or not.
\ \
\ ( k -- ) the line is over. It is entered with how -- 0 QUIT, 1 completed, \ ( s -- ) the text is done with. What it printed is sent, then a message
\ anything else an error -- and never returns: it leaves that in \ saying how it ended -- 0 QUIT, 1 completed, 2 an error -- and the node waits
\ (LINE-STATUS) and stops at (IDLE), where the host finds the node. It is \ for the next. (DONE) is entered with the same number and decides whether
\ always jumped to, never called, by something that has emptied the return \ an error was one to catch; it is always jumped to, never called, by
\ stack (or by a fault, which empties it). \ something that has emptied the return stack (or by a fault, which empties
: (IDLE) L: jump L \ 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) : (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 if GO -1 + if OK
drop (RESET) drop (RESET)
(CATCH) b! @b if LOUD drop -1 !b 1 jump (DONE) \ caught: the line completed (CATCH) b! @b if LOUD drop -1 !b 1 jump (DONE) \ caught: the text completed
LOUD: drop 2 (LINE-STATUS) b! !b jump (IDLE) LOUD: drop 2 jump (FINISH)
OK: drop 1 (LINE-STATUS) b! !b jump (IDLE) OK: drop 1 jump (FINISH)
GO: drop 0 (LINE-STATUS) b! !b jump (IDLE) GO: drop 0 jump (FINISH)
\ ( -- ) THE LINE ENTRY. The host has put a line of text in TIB, a zero \ ( -- ) WAITING. A node with nothing to do is blocked reading its ports
\ after it, and its length in SPAN, has emptied the return stack and set P \ (docs/v4.0.0/MESH.md 4.1): it executes nothing until a neighbour writes.
\ here. The line is interpreted with one return entry under it, this \ What arrives is a message (core.v4). Its first word is taken from any
\ word's call of INTERPRET; whatever is on the data stack stays there. \ 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) : (LINE)
0 BLK a! ! \ the terminal, whatever was being loaded 0 BLK a! ! \ the terminal, whatever was being loaded
TIB (SRC) a! ! 0 >IN a! ! TIB (SRC) a! ! 0 >IN a! !
+4 -2
View File
@@ -33,6 +33,7 @@
#define V4_BOOT_H #define V4_BOOT_H
#include "v4/image.h" #include "v4/image.h"
#include "v4/message.h"
typedef struct { typedef struct {
v4_node *n; v4_node *n;
@@ -66,8 +67,9 @@ typedef struct {
int v4_boot_run(const v4_boot *b, unsigned char *disk, unsigned blocks); 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 /* 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 * `out`. The line is sent to the node as a message and what it prints
* V4_LINE_ERROR (image.h) -- or one of these: */ * 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_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_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 */ #define V4_BOOT_LINE_NO_INPUT (-3) /* the line read the keyboard and there is no more input */
+8 -30
View File
@@ -10,6 +10,10 @@
* (capsules/v4/nucleus-<width>.f18). docs/v4.0.0/MESH.md section 5. * (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. * - 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 * 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. * sizes; v4_image_born refuses one the engine was not built for.
*/ */
@@ -22,16 +26,10 @@ typedef struct {
/* what the image was built for */ /* what the image was built for */
unsigned cell_bits, node_words, data_ring, ret_ring; 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 /* Where the nucleus waits for a message (v4/capsule/quit.v4, (IDLE)):
* text in the input buffer and starts it at `line`; it stops at `idle`, * the node's P is there, blocked reading its ports, when it has nothing
* with how the line ended in the cell `line_status`. At switch-on it * to do. */
* is at `idle`. */ v4_cell 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 */
v4_cell fault_table; /* (FAULTS): where a fault goes */ v4_cell fault_table; /* (FAULTS): where a fault goes */
v4_cell port; /* word address of the node's port (node.h) */ 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. */ * back to, and FORGET will not go below it. */
void v4_image_seal(v4_node *n, const v4_image *im); 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 /* 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 * 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 * 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, int v4_image_born(v4_node *n, v4_exec_state *es, v4_heat *h, const v4_image *im,
unsigned char *disk, unsigned blocks); 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 /* 1 if the node is waiting for a character: it is inside KEY and none is
* pending. */ * pending. */
int v4_image_waiting(const v4_node *n, const v4_image *im); int v4_image_waiting(const v4_node *n, const v4_image *im);
+63
View File
@@ -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 */
-33
View File
@@ -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; 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) void v4_image_seal(v4_node *n, const v4_image *im)
{ {
n->mem[im->boot_cells] = n->mem[im->dp]; n->mem[im->boot_cells] = n->mem[im->dp];
+44
View File
@@ -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
View File
@@ -5,6 +5,7 @@
* which only a whole system has. * which only a whole system has.
*/ */
#include "v4/boot.h" #include "v4/boot.h"
#include "v4/message.h"
#include "starkernel/capsule.h" #include "starkernel/capsule.h"
#include "starkernel/capsule_generated.h" #include "starkernel/capsule_generated.h"
#include "starkernel/capsule_sig.h" #include "starkernel/capsule_sig.h"
@@ -126,22 +127,29 @@ static void post_watch(char c)
post_len = 0; 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) 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; v4_node *n = b->n;
uint64_t steps = 0; 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 (;;) { for (;;) {
(void)v4_exec_step_word(n, b->es, b->h); (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->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) { if (b->serve && n->request >= 1 && n->request <= (v4_cell)b->word_count) {
b->serve(n, (unsigned)n->request); b->serve(n, (unsigned)n->request);
v4_node_port_served(n); v4_node_port_served(n);
@@ -153,8 +161,34 @@ int v4_boot_line(const v4_boot *b, const char *text, unsigned len)
} }
continue; continue;
} }
if (v4_line_done(n, b->im)) return v4_line_status(n, b->im); if (n->asking && n->ask_port == CONSOLE_PORT) { /* a word of a message from the node */
if (v4_image_waiting(n, b->im)) { /* the line is reading the keyboard */ 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; int c = b->key ? b->key() : V4_BOOT_KEY_END;
unsigned char ch = (unsigned char)c; unsigned char ch = (unsigned char)c;
if (c == V4_BOOT_KEY_END) return V4_BOOT_LINE_NO_INPUT; 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) { if (len) {
for (i = 0; i < len; i++) text[i] = (char)p[i]; for (i = 0; i < len; i++) text[i] = (char)p[i];
int how = v4_boot_line(b, text, len); 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_where(b, name, block, line);
say(b, how == V4_LINE_ERROR ? " ended in an error: " say(b, how == V4_TEXT_ERROR ? " ended in an error: "
: how == V4_LINE_QUIT ? " ended with QUIT: " : how == V4_TEXT_QUIT ? " ended with QUIT: "
: how == V4_BOOT_LINE_NO_INPUT ? " reads the keyboard: " : how == V4_BOOT_LINE_NO_INPUT ? " reads the keyboard: "
: " stopped the node: "); : " stopped the node: ");
b->out(text, len); say(b, "\n"); 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; } 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; 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); (void)v4_exec_step_word(n, b->es, b->h);
if (n->stopped || n->asking || ++steps > STEP_LIMIT) { if (n->stopped || n->asking || ++steps > STEP_LIMIT) {
say(b, "V4: the node did not take the nucleus in\n"); 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) { if (n->reading && !n->given) {
v4_ucell word = 0; v4_ucell word = 0;
unsigned i; 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); 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); v4_node_port_give(n, 0, (v4_cell)word);
at++; 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 (; 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; " 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]; 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"); say(b, "V4: kernel word "); say(b, b->words[i]); say(b, " could not be made\nPARITY:FAIL\nPOST: FAILED\n");
return 0; return 0;
} }
+12
View File
@@ -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 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 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 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_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 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 */ #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; unsigned i;
v4_node_reset(n); 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_begin(tx, n, 16);
v4_text_constant(tx, "N-1", V4_CELL_BITS - 1); v4_text_constant(tx, "N-1", V4_CELL_BITS - 1);
v4_text_constant(tx, "NODE-ERROR", NODE_ERROR); 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, "(CATCH)", CATCH);
v4_text_constant(tx, "(LINE-STATUS)", LINE_STATUS); v4_text_constant(tx, "(LINE-STATUS)", LINE_STATUS);
v4_text_constant(tx, "(PORT)", PORT); 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-DEFINED)", WORD_DEFINED);
v4_text_constant(tx, "(WORD-FORGOTTEN)", WORD_FORGOTTEN); v4_text_constant(tx, "(WORD-FORGOTTEN)", WORD_FORGOTTEN);
v4_text_constant(tx, "BLOCK-NUMBER", STORAGE_REG); v4_text_constant(tx, "BLOCK-NUMBER", STORAGE_REG);
+4 -1
View File
@@ -263,7 +263,10 @@ int main(void)
} }
n.mem[BASE] = 10; n.mem[BASE] = 10;
CHECK(v4_node_console_feed(&n, "Z", 1) == 1 && run("KEY", 0, 0, 0, 0) && pop() == 'Z' && clean(), "KEY"); 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 ---- */ /* ---- the in-line names ---- */
+75 -38
View File
@@ -41,7 +41,7 @@
* 3 ROLL is ROT. v3's count from zero. * 3 ROLL is ROT. v3's count from zero.
*/ */
#include "v4/text.h" #include "v4/text.h"
#include "v4/image.h" #include "v4/message.h"
#include "v4/testcode.h" #include "v4/testcode.h"
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
@@ -59,7 +59,7 @@ static v4_exec_state es;
static v4_heat h; static v4_heat h;
static v4_text tx; static v4_text tx;
static v4_cell w_key, w_key_end, w_fault, capsule_latest; 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]; static char out[V4_CONSOLE_CAP + 1];
/* The storage device's blocks: zeros at every switch-on. */ /* 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; 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 /* 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 -- ) * 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 * 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; 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 * read for 64 instruction words; or until `max` words. 1: ended, 2: waiting
* for a character, 0: neither. */ * for a character, 0: neither. */
static int run_line(long max) static int run_line(long max)
@@ -157,14 +171,36 @@ static int run_line(long max)
long steps = 0; long steps = 0;
unsigned idle = 0; unsigned idle = 0;
while (steps < max && idle < 64) { while (steps < max && idle < 64) {
if (v4_line_done(&n, &img)) { last_steps = steps; return 1; }
(void)v4_exec_step_word(&n, &es, &h); (void)v4_exec_step_word(&n, &es, &h);
steps++; 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; if (n.input_pos == n.input_len && n.p >= w_key && n.p < w_key_end) idle++; else idle = 0;
} }
last_steps = steps; last_steps = steps;
if (v4_line_done(&n, &img)) return 1;
return idle >= 64 ? 2 : 0; 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 */ 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); memset(disk, 0, sizeof disk);
v4_node_storage_attach(&n, STORAGE_REG, disk, DISK_BLOCKS); 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_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST);
v4_node_fault_attach(&n, w_fault); v4_node_fault_attach(&n, w_fault);
v4_node_error_attach(&n, NODE_ERROR); v4_node_error_attach(&n, NODE_ERROR);
@@ -208,10 +243,16 @@ static void boot_with(unsigned depth)
n.mem[WORD_FORGOTTEN] = 0; n.mem[WORD_FORGOTTEN] = 0;
known_n = 0; known_n = 0;
last_request = 0; last_request = 0;
n.mem[LINE_STATUS] = V4_LINE_COMPLETED; n.mem[LINE_STATUS] = 1;
n.p = img.idle; /* idle, waiting to be handed a line */ 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; typed_len = 0;
line_open = 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); } static void boot(void) { boot_with(0); }
/* The same with nothing at all on the data stack. */ /* The same with nothing at all on the data stack. */
@@ -252,27 +293,27 @@ static const char *say(const char *input)
unsigned len; unsigned len;
if (!nl) break; if (!nl) break;
len = (unsigned)(nl - typed); 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); memmove(typed, nl + 1, typed_len - len - 1);
typed_len -= len + 1; typed_len -= len + 1;
line_open = 1; line_open = 1;
} }
v4_node_console_attach(&n, CONSOLE_TX); /* empties the capture */ shown_len = 0; shown_dropped = 0;
v4_node_console_input_attach(&n, CONSOLE_RX, CONSOLE_ST); /* and the keyboard */ 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)"; if (v4_node_console_feed(&n, typed, typed_len) != typed_len) return "(input queue full)";
ended = run_line(40000000); ended = run_line(40000000);
took = n.input_pos; /* what a word in the line read */ took = n.input_pos; /* what a word in the line read */
memmove(typed, typed + took, typed_len - took); memmove(typed, typed + took, typed_len - took);
typed_len -= took; typed_len -= took;
if (n.console_dropped || olen + n.console_len + 16 > sizeof out) return "(too much output)"; if (shown_dropped || olen + shown_len + 16 > sizeof out) return "(too much output)";
memcpy(out + olen, n.console, n.console_len); memcpy(out + olen, shown, shown_len);
olen += n.console_len; olen += shown_len;
out[olen] = 0; out[olen] = 0;
if (ended == 0) return "(still running)"; if (ended == 0) return "(still running)";
if (ended == 2) break; if (ended == 2) break;
line_open = 0; line_open = 0;
tail = v4_line_status(&n, &img) == V4_LINE_COMPLETED ? " ok\nok> " tail = text_ended == V4_TEXT_COMPLETED ? " ok\nok> " : text_ended == V4_TEXT_ERROR ? " ERROR\nok> " : "ok> ";
: v4_line_status(&n, &img) == V4_LINE_ERROR ? " ERROR\nok> " : "ok> ";
memcpy(out + olen, tail, strlen(tail) + 1); memcpy(out + olen, tail, strlen(tail) + 1);
olen += (unsigned)strlen(tail); olen += (unsigned)strlen(tail);
} }
@@ -739,12 +780,7 @@ int main(void)
CHECK(v4_text_here(&tx) < DICT_W, "code stays below the dictionary space"); CHECK(v4_text_here(&tx) < DICT_W, "code stays below the dictionary space");
printf(" capsule: %ld words\n", (long)v4_text_here(&tx) - 16); printf(" capsule: %ld words\n", (long)v4_text_here(&tx) - 16);
if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; } if (failures) { printf(" %d checks, %d failures\n", checks, failures); return 1; }
img.line = v4_text_word(&tx, "(LINE)"); w_idle = v4_text_word(&tx, "(IDLE)");
img.idle = v4_text_word(&tx, "(IDLE)");
img.line_status = LINE_STATUS;
img.tib = TIB;
img.tib_bytes = 1025u;
img.span = SPAN;
w_key = v4_text_word(&tx, "KEY"); w_key = v4_text_word(&tx, "KEY");
w_key_end = v4_text_word(&tx, "CR"); /* the word after KEY in core.v4 */ w_key_end = v4_text_word(&tx, "CR"); /* the word after KEY in core.v4 */
w_fault = v4_text_word(&tx, "(FAULTS)"); w_fault = v4_text_word(&tx, "(FAULTS)");
@@ -753,11 +789,12 @@ int main(void)
/* ---- switch-on ---- */ /* ---- switch-on ---- */
boot(); 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(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");
CHECK(run_line(5000) == 1 && last_steps == 0, "and goes on waiting to be handed a line"); { 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"); CHECK(canary_under_expect(), "with the data stack as it found it");
(void)v4_exec_step_word(&n, &es, &h); (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 ---- */ /* ---- the sessions ---- */
for (i = 0; i < NSCRIPT; i++) { for (i = 0; i < NSCRIPT; i++) {
@@ -883,7 +920,7 @@ int main(void)
memcpy(line, "65 EMIT", 7); memcpy(line, "65 EMIT", 7);
line[1025] = '\n'; line[1026] = 0; 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(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 ---- */ /* ---- ABORT and QUIT from deep in a programme, again and again ---- */
@@ -1247,7 +1284,7 @@ int main(void)
boot_bare(); boot_bare();
CHECK(load_source("tools.fth"), "tools.fth compiles, every line of it"); 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(": 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"); "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("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"); CHECK(is(say(": IM 1 ; IMMEDIATE SEE IM\n"), ": IM\n @p 1 ; \nIMMEDIATE\n ok\nok> "), "an immediate word says so");
+2 -2
View File
@@ -67,9 +67,9 @@ int main(void)
len = strlen(line); len = strlen(line);
if (len && line[len - 1] == '\n') len--; if (len && line[len - 1] == '\n') len--;
how = v4_boot_line(&boot, line, (unsigned)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; } 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_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_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; } else if (how == V4_BOOT_LINE_STOPPED) { fflush(stdout); fprintf(stderr, "starforth4: the node stopped on a fault\n"); return 1; }
+4 -5
View File
@@ -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", 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" }; "numout.v4", "words.v4", "system.v4", "qmath.v4", "blocks.v4", "log.v4", "acl.v4" };
FILE *out; FILE *out;
v4_cell k, line, idle, faults, voc; v4_cell k, idle, faults, voc;
unsigned count = 0; unsigned count = 0;
if (argc != 3) die("usage: mkimage DESCRIPTION.c CAPSULE.f18", NULL); 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 (!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_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); 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)"); idle = v4_text_word(&tx, "(IDLE)");
faults = v4_text_word(&tx, "(FAULTS)"); faults = v4_text_word(&tx, "(FAULTS)");
w_key = v4_text_word(&tx, "KEY"); 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]); 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); 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 /* 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 */ * 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, "/* 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", 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); (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, fprintf(out, " %ld,\n %ld, %ld,\n %ld, %ld,\n", (long)idle, (long)faults, (long)PORT, (long)w_key, (long)w_key_end);
(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 %ld, %ld\n};\n", (long)CONSOLE_TX, (long)CONSOLE_RX, (long)CONSOLE_ST, 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)DSTACK_REG, (long)RSTACK_REG, (long)NODE_ERROR, (long)STORAGE_REG, (long)DP, (long)LATEST,
(long)BOOT_CELLS, (long)FENCE); (long)BOOT_CELLS, (long)FENCE);