Ruled 2026-10-08. The engine, the nucleus and their tests are as they
were at dfabfa46, before step 6d; the code of the wait and its tests are
in history at 075385ab..2cf37aab. The limit of MESH.md 7b.7 stands: a
node that is stuck can hold up its neighbours until Hera kills it.
MESH.md 7c is kept as the record of the design as approved, and 7c.6 says
what was built, what the two reviews found, the cause the findings share
-- a node's message machinery runs on the stacks its text is using, and a
fault abandons whatever was in progress -- and what a second attempt
must settle before anything is built.
Kept from it, in hosted-check: a line that leaves 29 values on the stack,
and WORDS with 26 values on it. Both hung the products at some commit of
the wait.
make -C v4 test and sanitize at both widths, hosted-check, lint; three
bare-metal boots typing both. POST 538 of 538, word_count=317,
dict_hash=0xc0769523a47b7dc3 on all six: the hash of step 6c's code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
206 lines
11 KiB
Markdown
206 lines
11 KiB
Markdown
# v4/
|
|
|
|
StarForth v4: the 32-instruction F18-derived core. The design lives in
|
|
`docs/v4.0.0/JUSTIFICATION.md` (why) and `docs/v4.0.0/DECOMPOSITION.md`
|
|
(every v3 word mapped to a v4 fate).
|
|
|
|
The first deliverable is the hosted C99 golden model (`JUSTIFICATION.md` §10,
|
|
step 1). It must pass POST and hold K≡1.0 with 32- and 64-bit cells on all
|
|
three host ISAs.
|
|
|
|
Acceptance (`JUSTIFICATION.md` §16): v4 must be equivalent to v3 at any point
|
|
in time, with the same vocabularies and behaviour, on the F18-derived engine;
|
|
and every ISA, hosted and bare metal, must still reach its `ok` prompt.
|
|
`make -C v4 test` passing is a development check, not acceptance.
|
|
|
|
What exists so far is the single node: registers, circular stacks, memory, all
|
|
32 opcodes, per-opcode and per-call-target heat, and three console
|
|
registers (`CONSOLE-TX`, which captures output, and `CONSOLE-RX` and `CONSOLE-STATUS`, which hand out
|
|
input a test feeds) standing in for the console node until the mesh exists. The tests load definitions
|
|
onto a node either opcode by opcode (`v4/include/v4/asm.h`) or as text in the notation `DECOMPOSITION.md`
|
|
uses (`v4/include/v4/text.h`). `make -C v4 test` builds
|
|
and runs the tests at both cell widths; `make -C v4 sanitize` repeats them
|
|
under ASan and UBSan. There is no POST and no K measurement yet.
|
|
|
|
## The system: nucleus, capsules, prompt
|
|
|
|
`docs/v4.0.0/NUCLEUS.md` is the design. A v4 system is four things:
|
|
|
|
| Part | Where | What it is |
|
|
|---|---|---|
|
|
| Engine | `v4/src` | The golden model of the 32-opcode node |
|
|
| Nucleus | `v4/capsule/*.v4`, built by `v4/tools/mkimage.c` into `capsules/v4/nucleus-64.f18` | The assembled words, as a capsule of F18 code: sent through its port to a node born empty |
|
|
| Capsules | `capsules/v4/*.4th`, baked by `tools/mkcapsule.c` | FORTH source, loaded when the system comes up |
|
|
| Boot | `v4/system/boot.c` | Starts the nucleus, checks and loads each capsule, prints the parity lines, gives the prompt |
|
|
|
|
Two products link the same four and differ only in the console:
|
|
|
|
- **Hosted Linux**, `v4/tools/hosted.c`: `make -C v4 hosted` builds
|
|
`v4/build/starforth4-amd64`, `-aarch64` and `-riscv64`, static, 64-bit cells.
|
|
- **Bare metal**, `kernel/src/v4/sk_v4.c`: `make -f kernel/Makefile ARCH=<arch> STARFORTH_V4=1`.
|
|
|
|
A boot prints:
|
|
|
|
```
|
|
PARITY:V4_NUCLEUS words=292 image_hash=0x...
|
|
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x... capsule_hash=0x... dict_hash=0x...
|
|
PARITY:OK
|
|
ok>
|
|
```
|
|
|
|
Every build of one commit prints the same hashes. `make -C v4 hosted-check`
|
|
boots the three hosted binaries (the two foreign ones under user-mode QEMU)
|
|
and fails unless their output is identical and ends in `PARITY:OK`.
|
|
|
|
A capsule line the node does not answer ` ok` to ends the boot, naming the
|
|
capsule, block and line, with `PARITY:FAIL` and `POST: FAILED`.
|
|
|
|
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=0x97a655081b2af559 words=298
|
|
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=... dict_hash=...
|
|
PARITY:V4_POST tests=538 pass=538 fail=0
|
|
PARITY:V4_SYSTEM word_count=314 dict_hash=...
|
|
PARITY:OK
|
|
POST: PASSED
|
|
ok>
|
|
```
|
|
|
|
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 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
|
|
dictionary entry made by `n KERNEL-WORD name`, whose body writes `n` to the
|
|
port; its arguments and results are on the data stack. `BYE` is the one
|
|
kernel word so far. A node can be stopped between any two instruction
|
|
words and is blocked while it waits at its port, which is what a system of
|
|
many users and tasks, preemptive and cooperative, needs of it
|
|
(`ENGINE.md` 3a).
|
|
|
|
**The two products have parted** (`ENGINE.md` 3c). They share the engine,
|
|
the FORTH-79 capsule and its POST; their nuclei may differ. The hosted
|
|
product is its own and may become a hosted SDK. The bare-metal product is
|
|
to be LithosAnanke with the node in the StarForth VM's place, and
|
|
everything about word records, the fleet and identity is its alone.
|
|
|
|
**Nodes that talk** (`docs/v4.0.0/MESH.md`) are the next step, ahead of
|
|
making v4 equal v3 on bare metal. Its first part is built and tested in
|
|
the engine: a node has `V4_PORTS` ports; a read from one blocks until the
|
|
neighbour writes and a write until the neighbour reads; a node is born
|
|
empty with `P` at its ports and executes what a neighbour sends it, which
|
|
is how each product's node now gets its nucleus, as a capsule; and
|
|
the fabric (`v4/src/fabric.c`) is the nodes there are and the table of how
|
|
their ports are wired, both of which change while they run. The products
|
|
below are still one node each; they become the five nodes at `MESH.md`
|
|
steps 8 and 9.
|
|
|
|
**Finding the way** (`MESH.md` step 4) is built: a node has a table of
|
|
which port leads toward which node (`ROUTE`, `DEFAULT-ROUTE`, `NO-ROUTES`),
|
|
passes on a message that is not for it, and `SEND`s text to another node;
|
|
`v4/tests/test_host_mesh.c` runs three StarForth nodes in a row. A node
|
|
looks before it begins a message (`MESH.md` section 7a): it takes in what
|
|
its neighbours are waiting to write to it, keeps those messages until it
|
|
has nothing else to do, and writes only to a neighbour that is waiting to
|
|
read -- or, of two neighbours, the one with the lower number may wait.
|
|
`NEIGHBOUR ( node port -- )` tells a node who is on each port. A node with
|
|
no room for one more message lets it go and counts it in `(LOST)`.
|
|
|
|
**Five nodes from nothing** (`MESH.md` step 5) is built and proven in the
|
|
fabric under test: Hera asks whoever holds the fabric for a node
|
|
(`v4/src/manage.c`: `NODE-BORN`, `NODE-WIRE` and the rest), sends it the
|
|
nucleus through the port that joins them, then FORTH-79 a line at a
|
|
time, and joins four such nodes in a square round her
|
|
(`capsules/v4/hera.4th`: `BIRTH`, `UNIT`). `v4/tests/test_host_unit.c`.
|
|
The two products are not yet this: they become the five nodes at steps 8
|
|
and 9.
|
|
A node she births is not POSTed: POST is the kernel's, once, on Hera.
|
|
|
|
**Refusals and waits** (`MESH.md` 7b, step 6c). A node is never left
|
|
waiting on a node that is gone, and a wait on a node that is alive and
|
|
stuck is ended by Hera's killing it, or from the console. A node with no
|
|
room for a message, or no way to pass it on, or that would be the
|
|
sixteenth to pass it on, sends a NACK to whoever waits on it; `(REFUSED)`
|
|
counts them. `AWAIT` ends in an error if the answer is not going to come:
|
|
"Message refused", "Node gone", or "Interrupted" when a line is typed at
|
|
the console. `n KILL` on Hera removes node n and tells every other node it
|
|
is gone; a node believes that only from its centre. A stuck node can hold
|
|
up its neighbours until it is killed: `MESH.md` 7b.7 says how.
|
|
|
|
**Blocks** (`MESH.md` step 6). The block words are the kernel's, as they
|
|
are for a v3 VM: `BLOCK`, `BUFFER`, `UPDATE`, `SAVE-BUFFERS` and
|
|
`EMPTY-BUFFERS` each write one request to port 0, by block number only
|
|
(`v4/include/v4/blocks.h`). The node has a window of four slots in its
|
|
memory; the kernel copies blocks into it, keeps the record of what is in
|
|
it, and decides when a block is written (`v4/system/blocks.c`). The blocks
|
|
are the kernel's block subsystem's, which is v3's
|
|
(`v3/src/block_subsystem.c`):
|
|
one chain, the same blocks at the same numbers for every node. A hosted
|
|
program has the chain's fast RAM, blocks 1 to 2047. On bare metal the chain
|
|
is fast RAM, the ramdrive and the virtio disk; the disk is read and not yet
|
|
written, because nothing in v4 gives its owner's word that it may be
|
|
formatted. Who may have which block is not checked yet.
|
|
|
|
**This is still the lone node.** On bare metal `kernel_main.c` starts it
|
|
before the fleet tables, beside the kernel's own system and not in the VM's
|
|
place. `ENGINE.md` sets out the steps from here; step 1 is done and step 2 is
|
|
part done. `V3-PARITY.md` records the rulings it is built from.
|
|
|
|
**What this does not yet show.** `forth79.4th` holds two definitions, `U*`
|
|
and `U/MOD`. Every other word is still in the assembled nucleus, so POST is
|
|
so far a test of the assembled words. Moving them to the capsule, a group at
|
|
a time with POST after each, is the next step (`NUCLEUS.md` section 8).
|
|
|
|
## POST
|
|
|
|
POST is the kernel's, as in v3 (`MESH.md` step 6b, `NUCLEUS.md` 6.3). The
|
|
kernel holds the cases, `v4/system/post_cases.c`: 538 of them, covering 126
|
|
of the 130 words of the FORTH-79 Required Word Set. 431 are v3's cases with
|
|
what the hosted v3 binary did as the expected result; every other case,
|
|
and the v3 cases left out, are listed with reasons in
|
|
`docs/v4.0.0/POST79.md`.
|
|
|
|
- `make -C v4 post` boots the amd64 system: the quick check after a change.
|
|
- `make -C v4 hosted-check` boots all three hosted ISAs and compares them.
|
|
- `make -C v4 post79` writes the cases again (`tools/mkpost.py`,
|
|
`tools/post79_rules.py`); it needs the hosted v3 binary.
|
|
|
|
The runner, `v4/system/post.c`, feeds each case to the node a line at a
|
|
time, keeps what the node prints, reads its data stack from outside, and
|
|
judges: an error exactly if one is expected, and otherwise the stack and
|
|
every character printed. Nothing of POST is in the node's dictionary, and
|
|
POST leaves nothing: the system is sealed before it, and afterwards the
|
|
kernel puts the node back to the system as sealed, as v3 puts the
|
|
dictionary back after each of its suites. The boot then prints
|
|
`PARITY:V4_SYSTEM word_count=N dict_hash=0x...`, and passes only if no
|
|
case failed (`v4/system/boot.c`). A case the node does not come back from
|
|
ends POST there, named. `make -C v4 hosted-check` also boots a program
|
|
whose POST has failing cases and requires that the boot fails.
|
|
|
|
Not tested: `KEY`, `EXPECT`, `QUERY` (the keyboard) and `QUIT` (it returns
|
|
without ` ok`).
|
|
|
|
Open: `PAD 42 OVER !` faults on v4, because `PAD` is a byte address and `!`
|
|
takes a cell address (D-1). FORTH-79 expects it to work. The v3 case for it
|
|
is left out until that is ruled.
|
|
|
|
The Required Word Set list in `tools/mkpost.py` was written from memory of
|
|
the standard and has not been checked against the document.
|