Files
LithosAnanake/docs/v4.0.0/DECOMPOSITION.md
T
rajamesandClaude Opus 5.5 f030edcd08 test(v4.0.0): execute LSHIFT and RSHIFT on the golden model
Both run exactly as written in DECOMPOSITION.md 5.5 and need no change.
Checked against C on the edge vectors for every count 0 .. N (a count
of N gives 0), at 32- and 64-bit cells, optimised and ASan+UBSan
(`make test`, `make sanitize`). Removing RSHIFT's sign-bit mask fails.

Headroom (data under args / return): 7/5 each.

They are dependencies of C@ and C!, which the pictured-output hold
buffer needs.

Test results on the amd64 host only. This is a development check, not
acceptance (JUSTIFICATION.md section 16).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 11:10:03 -04:00

783 lines
48 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# StarForth v4.0.0 — Primitive Decomposition
This document assigns every C primitive in StarForth v3 (`admin/LithosAnanake` at `6302dcb`) a fate in
StarForth v4.0.0. v4 is built on a 32-instruction core derived from Chuck Moore's F18 (the GA144 node).
Everything that is not one of those 32 instructions becomes capsule code, a service message to another
node, a memory-mapped register, or is retired.
The rationale is in `JUSTIFICATION.md`. This document is the specification.
**Status.** Every colon definition below is written against the ISA in §1 and has been traced by hand,
but none has been executed. Each one becomes a POST test target: it is correct when the hosted v4 golden
model produces the same results as the v3 C primitive it replaces.
---
## Contents
0. [Fates](#0-fates)
1. [The v4 core ISA](#1-the-v4-core-isa)
2. [Compiler conventions](#2-compiler-conventions)
3. [Decisions](#3-decisions)
4. [Foundation layer (new words)](#4-foundation-layer-new-words)
5. [Fate of every v3 primitive](#5-fate-of-every-v3-primitive)
6. [Messaging between nodes](#6-messaging-between-nodes)
7. [Memory-mapped registers](#7-memory-mapped-registers)
---
## 0. Fates
| Code | Fate | Meaning |
| --- | --- | --- |
| **OP** | Instruction | One of the 32 core opcodes. |
| **IN** | Inline macro | A short opcode sequence the compiler places in-line. Never called. Required for anything that touches the return stack, since a call would bury the return address. |
| **CAP** | Core capsule | A colon definition in the core capsule, built only from OP, IN, and earlier CAP words. |
| **CC** | Compiler capsule | Part of the compiler/interpreter capsule, which runs on the host node only. Mesh nodes receive compiled code. |
| **DEV** | Device service | A message to a device node that owns real hardware (console, storage, display, keyboard, log). |
| **HERA** | Hera service | A message to the Hera node, which owns lifecycle, identity, capsules, ACLs, and Stadium admission. |
| **MM** | Memory-mapped | An `@` or `!` against a hardware register (heat counters, anti-clock, governor, stack pointer). |
| **RET** | Retired | A diagnostic of v3's software machine that has no equivalent in v4. |
---
## 1. The v4 core ISA
### 1.1 Machine model
| Item | v4 node |
| --- | --- |
| Cell | 32 bits (mesh node). The host node's width is a build parameter; see D-5. |
| Addressing | Word-addressed (D-1). |
| Registers | `T` (top of data stack), `S` (second), `R` (top of return stack), `P` (program counter), `A` and `B` (address registers). |
| Stacks | F18 circular hardware stacks, not visible to code (D-2): data stack `T`, `S` + 8 circular (10 deep), return stack `R` + 8 circular (9 deep). No overflow or underflow; pushing past the bottom silently overwrites the oldest entry. |
| Instruction word | Six 5-bit slots, plus 2 spare bits. |
### 1.2 Instruction word layout
```
31 27 26 22 21 17 16 12 11 7 6 2 1 0
+--------+--------+--------+--------+--------+--------+----+
| slot 0 | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 | xx |
+--------+--------+--------+--------+--------+--------+----+
```
Slots execute left to right. A branch (`jump`, `call`, `next`, `if`, `-if`) takes its target from all
bits to the right of its slot, and that target replaces the same low bits of `P` (page-relative, as in
the F18). Branches are therefore legal in slots 0–3 only:
| Branch in slot | Address bits | Reach |
| --- | --- | --- |
| 0 | 27 | whole address space |
| 1 | 22 | 4M words |
| 2 | 17 | 128K words |
| 3 | 12 | 4K words (node-local) |
### 1.3 The 32 opcodes
Opcode numbering follows the F18. Two names differ from Moore's: F18 `-` is renamed `inv` and F18 `or`
(which is exclusive-or) is renamed `xor`, so instruction names never collide with FORTH-79 word names.
| Op | Name | Effect |
| --- | --- | --- |
| 00 | `;` | Return: `P ← R`, pop `R`. |
| 01 | `ex` | Swap `P` and `R` (co-routine / execute). |
| 02 | `jump a` | `P ← a`. |
| 03 | `call a` | Push `P` to `R`, `P ← a`. |
| 04 | `unext` | If `R ≠ 0`: decrement `R`, restart the current instruction word at slot 0. Else pop `R`. |
| 05 | `next a` | If `R ≠ 0`: decrement `R`, `P ← a`. Else pop `R`. |
| 06 | `if a` | If `T = 0`: `P ← a`. **Does not pop `T`.** |
| 07 | `-if a` | If `T ≥ 0` (sign bit clear): `P ← a`. **Does not pop `T`.** |
| 08 | `@p` | Push the word at `P`, `P ← P+1` (literal). |
| 09 | `@+` | Push the word at `A`, `A ← A+1`. |
| 0A | `@b` | Push the word at `B`. |
| 0B | `@` | Push the word at `A`. |
| 0C | `!p` | Store `T` at `P`, pop, `P ← P+1`. |
| 0D | `!+` | Store `T` at `A`, pop, `A ← A+1`. |
| 0E | `!b` | Store `T` at `B`, pop. |
| 0F | `!` | Store `T` at `A`, pop. |
| 10 | `+*` | Multiply step: if bit 0 of `A` is set, `T ← T+S`; then shift `T:A` right one bit. See D-3. |
| 11 | `2*` | `T ← T << 1`. |
| 12 | `2/` | `T ← T >> 1`, arithmetic. |
| 13 | `inv` | `T ← ~T`. |
| 14 | `+` | `T ← S + T`, pop. |
| 15 | `and` | `T ← S & T`, pop. |
| 16 | `xor` | `T ← S ^ T`, pop. |
| 17 | `drop` | Pop `T`. |
| 18 | `dup` | Push a copy of `T`. |
| 19 | `pop` | Pop `R` onto the data stack. |
| 1A | `over` | Push a copy of `S`. |
| 1B | `a` | Push `A`. |
| 1C | `nop` | Nothing. |
| 1D | `push` | Pop `T` onto the return stack. |
| 1E | `b!` | `B ← T`, pop. |
| 1F | `a!` | `A ← T`, pop. |
Note that `@` and `!` address through `A`, not through `T`. The FORTH words `@` and `!` therefore become
`a! @` and `a! !`.
### 1.4 Side effects that are not instructions
Execution itself drives the physics. Every retired instruction increments that opcode's heat counter and
advances the anti-clock; every `call` increments the heat counter of its target. None of this costs an
instruction. Capsule code reads it through the memory-mapped registers in §7.
---
## 2. Compiler conventions
**Literals.** A number in source compiles to `@p` followed by the value in the next word.
**Capsule `IF`.** Native `if` and `-if` leave the flag on the stack. The capsule-level `IF` consumes it,
so the compiler drops it on both paths:
```
IF body THEN → if L1 drop body jump L2
L1: drop
L2:
IF body1 ELSE body2 THEN → if L1 drop body1 jump L2
L1: drop body2
L2:
```
`BEGIN ... WHILE ... REPEAT` and `BEGIN ... UNTIL` expand the same way.
**`FOR ... NEXT`.** `n FOR ... NEXT` compiles `push` then the body then `next`. The body runs `n+1`
times, as on the F18. `FOR ... UNEXT` is the same but the body must fit in one instruction word.
**Register conventions.** `A` and `B` are caller-saved. A word that uses them says so. Words in this
document that clobber `A`: `@ ! +! -! 2@ 2! C@ C! UM* * UM/MOD Q.FROM-INT Q.TO-INT Q.* Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS SEND RECV`. Words that clobber `B`:
`Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS SEND RECV`.
**Return-stack words** (`>R R> R@ 2>R 2R> 2R@ I J UNLOOP` and the loop runtimes) are always IN.
---
## 3. Decisions
Ruled by Captain Bob, 2026-10-01, except D-4 and D-6, which are deferred to development step 2 (the
hosted mesh, `JUSTIFICATION.md` §10) because only the mesh and the RTL depend on them. Where a
definition below depends on one, it says so.
| ID | Decision | Ruling |
| --- | --- | --- |
| **D-1** | Word addressing (pure Moore) or byte addressing. | **Word addressing.** `C@`/`C!` are CAP; `CELLS` is a no-op. |
| **D-2** | Stack depth, and whether the stacks are visible. | **F18 circular stacks, hidden.** Data stack 10 deep (`T`, `S` + 8 circular), return stack 9 deep (`R` + 8 circular), exactly as the F18. No stack pointer is visible to code, so `DEPTH`, `PICK`, `ROLL`, `.S`, `SP@` and `SP!` are **retired everywhere**, host node included. |
| **D-3** | Exact `+*` semantics at 32 bits: whether the add carries out of `T` into the shift. | **Plain F18 semantics.** The carry out of `T` is not kept. `UM*` in §4 is written for this and is exact over the full range (revised and proven on the golden model, 2026-10-02). |
| **D-4** | Node memory map, including port and register addresses. | **Deferred** to step 2. Symbolic names only for now (§6, §7). |
| **D-5** | Host node cell width. | **Match the host CPU:** 32 on a Zynq-7000 (Cortex-A9), 64 on an aarch64 host. The compiler capsule is written width-independent. |
| **D-6** | Which heat structures exist in hardware: per-opcode counters only, or also per-call-target and word-to-word transition counters. | **Deferred** to step 2. The golden model implements per-opcode and per-call-target heat in the meantime. |
| **D-7** | `ROLL` semantics. | **Moot.** `ROLL` is retired under D-2. |
| **D-8** | Q48.16 signedness. | **Signed.** v3's unsigned comparisons and `Q.FROM-INT` clamping are retired. |
| **D-9** | Instruction word width on a 64-bit-cell host (ruled 2026-10-02). | **32 bits at every cell width.** Six 5-bit slots plus 2 spare bits, as in §1.2. On a 64-bit host the instruction word is the low 32 bits of the cell and the high half is ignored, so compiled code is identical at both widths. Only data and `@p` literals are a full cell wide. |
| **D-10** | Q48.16 width on a 64-bit-cell node (ruled 2026-10-02). | **Two cells at every cell width.** A Q value is a signed double on 32- and 64-bit nodes alike, so every Q word is the same double word at both widths. At 32-bit cells this is bit-for-bit v3's 64-bit Q. At 64-bit cells the low cell is v3's value, and where v3 wraps on Q48.16 overflow (a sum past Q max, `ABS` or `NEG` of Q min) the high cell carries the true result instead. |
| **D-11** | `Q./` semantics: division by zero, rounding, overflow (ruled 2026-10-02). | **Saturate and flag; round toward zero; saturate on overflow.** The quotient of `a * 2^16 / b` is rounded toward zero, like `SM/REM`. When it does not fit a Q value it is clamped to Q max or Q min by its sign. Division by zero returns Q max or Q min by the sign of the dividend (`0 / 0` gives 0) and sets the node's `NODE-ERROR` register (§7), which `VM-ERROR?` reads. v3 returned 0 on division by zero and saturated whenever the dividend was 2^48 or more, even when the quotient would have fit. |
| **D-12** | Q approximations outside their domain (ruled 2026-10-02). | **Return 0 and set `NODE-ERROR`.** `Q.SQRT` of a negative value and `Q.LOG` of zero or a negative value return 0 and set `NODE-ERROR` (§7), as `Q./` does on division by zero (D-11). v3 returned 0 for `ln(0)` without a flag and read negative arguments as large unsigned values. |
**Consequences of D-2 that every definition must respect.** The data stack holds 10 items and the
return stack 9, and every `call`, `FOR`, `DO` loop frame and `push` uses return-stack slots. Nesting
is therefore shallow, and exceeding either depth corrupts silently rather than faulting. Every CAP
definition in §4 and §5 must be re-checked against these limits before it is accepted as a POST
target; the hand traces so far assumed unbounded stacks.
---
## 4. Foundation layer (new words)
These words do not exist as primitives in v3 but everything else is built on them. They are listed in
dependency order.
```forth
\ ---- helpers ------------------------------------------------------------
: NIP ( a b -- b ) push drop pop ;
: SWAP ( a b -- b a ) over push push drop pop pop ;
: OR ( a b -- a|b ) over inv and xor ;
: NEGATE ( n -- -n ) inv 1 + ;
: ROT ( a b c -- b c a ) push SWAP pop SWAP ;
\ ---- sign and zero tests (raw branches, labels shown) ------------------
: 0< ( n -- flag ) -if L1 drop -1 ; L1: drop 0 ;
: 0= ( n -- flag ) if L1 drop 0 ; L1: drop -1 ;
\ ---- unsigned compare ---------------------------------------------------
: U< ( u1 u2 -- flag ) 2DUP xor 0< IF NIP 0< ELSE - 0< THEN ;
\ ---- multiply (D-3) -----------------------------------------------------
\ Plain F18 +* loses the carry out of T, and its shift keeps T's sign bit, so
\ the loop is exact only while the multiplicand in S and the running T both
\ lie in [-2^(n-2), 2^(n-2)). UM* therefore multiplies by s = u1 2/, which
\ always does, starting T at t0 = u2 2/ when u1 is odd, and gets
\ hi:lo = t0 + s*u2. Then u1*u2 = 2*(hi:lo) + c_lo + c_hi*2^n, where
\ c_lo = u1 & u2 & 1
\ c_hi = (u1<0 ? u2 : 0) + (u1 odd and u2<0 ? 1 : 0)
\ restore the bits the two halvings dropped and the unsigned reading of
\ both top bits. Exact over the full range; proven on the golden model.
: UM* ( u1 u2 -- ulo uhi )
over 1 and inv 1 + over 2/ and \ u1 u2 t0 t0 = u2 2/ if u1 odd
over a! 31 push \ A: u2 R: loop count (FOR's push, early)
push over 2/ pop \ u1 u2 s t0 s = u1 2/
L: +* unext \ u1 u2 s hi A: lo
push drop pop \ u1 u2 hi
2* a -if L0 drop 1 + jump L1 L0: drop L1: push \ R: 2hi + top bit of lo
over -if L2 drop dup jump L3 L2: drop 0 L3: push \ R: + (u1<0 ? u2 : 0)
dup -if L4 drop over 1 and jump L5 L4: drop 0 L5: \ u2<0 ? u1&1 : 0
pop + pop + push \ u1 u2 R: hi'
and 1 and a 2* + pop ; \ lo' hi'
\ u1 and u2 wait on the data stack under the loop (+* touches only T, S and
\ A), so the corrections are made after it, with -if in place of 0< calls,
\ and the return stack holds at most two temporaries. The loop count is
\ pushed before s is made, so the data stack never holds more than four.
\ Leaves its caller 6 data cells under its arguments and 6 return entries
\ (revised 2026-10-02; the first full-range version parked its three
\ corrections on the return stack and left 4).
\ ---- divide: 32-step restoring division, divisor held in A --------------
\ Each step shifts hi:lo left one bit and, when the shifted-out bit of hi was
\ set or hi' >= d, subtracts d from hi' and sets the new low bit of lo. The
\ unsigned compare is done by sign tests in line (as U< does), and x - d is
\ `inv a + inv`, so the loop body makes no calls: it needs one return-stack
\ entry beyond its own count.
: UM/MOD ( ulo uhi ud -- urem uquot )
a! 31 FOR
-if L0 \ hi's top bit set:
2* over -if L1 drop 1 + jump L2 \ hi' = 2hi + top bit of lo
L1: drop L2: push 2* pop \ lo' = 2lo
jump SUB \ true hi' >= 2^n > d
L0:
2* over -if L3 drop 1 + jump L4
L3: drop L4: push 2* pop
dup a xor -if L5 \ top bits of hi' and d differ:
drop -if NOSUB jump SUB \ hi' >= d iff hi' has it
L5: drop dup inv a + inv -if L6 \ same: hi' >= d iff hi'-d >= 0
drop jump NOSUB
L6: push drop pop jump SETBIT
SUB: inv a + inv \ hi' - d
SETBIT: push 1 + pop \ lo' is even: + 1 sets bit 0
NOSUB:
NEXT
over push push drop pop pop ; \ SWAP in line
\ Executed on the golden model (2026-10-02): exact, q*d + r = uhi:ulo with
\ r < d, for every uhi < ud at 32- and 64-bit cells. Outside that range
\ (uhi >= ud, including ud = 0) the result is unspecified, as in FORTH-79; it
\ always terminates after 32 steps. Stack use (D-2): up to 6 data cells may
\ lie under the three arguments and up to 6 return-stack entries under its
\ return address. For UM* the same limits are 6 and 6. This replaces a
\ first version built on U<, 0<, SWAP and OR calls, which was also exact but
\ left only 3 and 3 (too few for SM/REM inside /MOD) and stepped about five
\ times as many instruction words.
\ ---- signed division, truncating toward zero (v3 semantics) -------------
\ The quotient takes the sign of d xor n, the remainder the sign of d. Sign
\ tests are native -if (as in 0<) and NEGATE is in line, so the only calls
\ are DNEGATE and UM/MOD, both call-free inside.
: SM/REM ( d n -- rem quot )
over over xor push \ R: quotient sign (top bit)
over push \ R: + remainder sign (top bit of d)
-if L0 inv 1 + L0: push \ R: + |n|
-if L1 DNEGATE L1: \ |d|
pop UM/MOD \ urem uquot
pop -if L2 drop push inv 1 + pop jump L3 L2: drop L3:
pop -if L4 drop inv 1 + ; L4: drop ;
\ Executed on the golden model (2026-10-02): exact whenever the truncated
\ quotient fits a signed cell, at 32- and 64-bit cells. Stack use (D-2): up
\ to 5 data cells under the three arguments and 3 return-stack entries under
\ its return address; /MOD, one call further out, leaves 2. The first
\ version, built on ABS, DABS, 0< and NEGATE calls, overflowed the return
\ stack inside DABS -> DNEGATE -> D+ -> U> and never returned correctly for
\ a negative dividend.
```
`2DUP`, `-`, and `DNEGATE` are defined in §5; the compiler resolves forward references within the
core capsule.
---
## 5. Fate of every v3 primitive
Section numbers match the v3 primitive reference.
### 5.1 Stack
| Word | Fate | v4 definition / notes |
| --- | --- | --- |
| `DROP` | OP | `drop` |
| `DUP` | OP | `dup` |
| `OVER` | OP | `over` |
| `SWAP` | CAP | §4 |
| `?DUP` | CAP | `dup IF dup THEN` |
| `ROT` | CAP | §4 |
| `-ROT` | CAP | `ROT ROT` |
| `DEPTH` | RET | No visible stack pointer (D-2). |
| `PICK` | RET | No visible stack pointer (D-2). |
| `ROLL` | RET | No visible stack pointer (D-2); D-7 is moot. |
### 5.2 Return stack
| Word | Fate | v4 definition |
| --- | --- | --- |
| `>R` | IN | `push` |
| `R>` | IN | `pop` |
| `R@` | IN | `pop dup push` |
### 5.3 Memory
| Word | Fate | v4 definition / notes |
| --- | --- | --- |
| `@` | IN | `a! @` |
| `!` | IN | `a! !` |
| `+!` | CAP | `a! @ + !` |
| `-!` | CAP | `a! NEGATE @ + !` |
| `2@` | CAP | `a! @+ @` (low cell at `addr`, high at `addr+1`, as in v3) |
| `2!` | CAP | `a! SWAP !+ !` |
| `C@` | CAP | See below (D-1). |
| `C!` | CAP | See below (D-1). |
| `FILL` | CAP | See below. |
| `MOVE` | CAP | `push 2DUP U< IF pop CMOVE> ELSE pop CMOVE THEN` |
| `ERASE` | CAP | `0 FILL` |
| `CELLS` | IN | Empty (word-addressed). Becomes `2* 2*` if D-1 chooses bytes. |
```forth
\ byte access on a word-addressed node, little-endian
: C@ ( baddr -- c )
dup 3 and 3 LSHIFT SWAP 2 RSHIFT a! @ SWAP RSHIFT 255 and ;
: C! ( c baddr -- )
dup 2 RSHIFT a! \ A = word address
3 and 3 LSHIFT \ c bits
SWAP 255 and over LSHIFT \ bits c'
SWAP 255 SWAP LSHIFT inv \ c' ~mask
@ and OR ! ;
: FILL ( baddr u c -- )
SWAP BEGIN dup WHILE 1- push 2DUP SWAP C! SWAP 1+ SWAP pop REPEAT 2DROP drop ;
```
### 5.4 Arithmetic
| Word | Fate | v4 definition / notes |
| --- | --- | --- |
| `+` | OP | `+` |
| `-` | CAP | `NEGATE +` |
| `*` | CAP | `UM* drop` |
| `/` | CAP | `/MOD NIP` |
| `MOD` `/MOD` `*/` `*/MOD` (§4 versions) | RET | Shadowed duplicates. Only the §6 versions survive. |
| `1+` `1-` `2+` `2-` | IN | `1 +`, `-1 +`, `2 +`, `-2 +` |
| `2*` | OP | `2*` |
| `2/` | OP | `2/` |
| `ABS` | CAP | `dup 0< IF NEGATE THEN` — executed on the golden model (2026-10-02). |
| `NEGATE` | CAP | §4 |
| `MIN` | CAP | `2DUP > IF SWAP THEN drop` |
| `MAX` | CAP | `2DUP < IF SWAP THEN drop` |
### 5.5 Logic and comparison
| Word | Fate | v4 definition / notes |
| --- | --- | --- |
| `AND` | OP | `and` |
| `XOR` | OP | `xor` |
| `OR` | CAP | §4 |
| `INVERT` | OP | `inv` |
| `NOT` | CAP | `0=` (FORTH-79 logical not, as in v3) |
| `LSHIFT` | CAP | `BEGIN dup WHILE 1- SWAP 2* SWAP REPEAT drop` — executed on the golden model (2026-10-03). |
| `RSHIFT` | CAP | `BEGIN dup WHILE 1- SWAP 2/ MSB inv and SWAP REPEAT drop` (clears the sign bit each step; `MSB` is the cell-width top-bit constant) — executed on the golden model (2026-10-03). |
| `0=` `0<` | CAP | §4 |
| `0<>` | CAP | `0= 0=` |
| `0>` | CAP | `dup 0< SWAP 0= OR 0=` (correct for the most negative number) |
| `=` | CAP | `xor 0=` — executed on the golden model (2026-10-02). |
| `<>` | CAP | `xor 0<>` |
| `<` | CAP | `2DUP xor 0< IF drop 0< ELSE - 0< THEN` (overflow-safe) — executed on the golden model (2026-10-02). |
| `>` | CAP | `SWAP <` |
| `<=` | CAP | `> 0=` |
| `>=` | CAP | `< 0=` |
| `U<` | CAP | §4 |
| `U>` | CAP | `SWAP U<` — executed on the golden model (2026-10-02). |
| `WITHIN` | CAP | `over - push - pop U<` |
| `TRUE` | IN | `-1` |
| `FALSE` | IN | `0` |
### 5.6 Mixed-precision arithmetic
A double is two 32-bit cells on a mesh node.
| Word | Fate | v4 definition |
| --- | --- | --- |
| `M+` | CAP | `S>D D+` — executed on the golden model (2026-10-02). |
| `M-` | CAP | `NEGATE M+` |
| `M*` | CAP | `2DUP xor push ABS SWAP ABS UM* pop 0< IF DNEGATE THEN` |
| `M/MOD` | CAP | `SM/REM` |
| `MOD` | CAP | `/MOD drop` |
| `/MOD` | CAP | `push S>D pop SM/REM` — executed on the golden model (2026-10-02). |
| `*/` | CAP | `*/MOD NIP` |
| `*/MOD` | CAP | `push M* pop SM/REM` |
### 5.7 Double-cell numbers
| Word | Fate | v4 definition |
| --- | --- | --- |
| `S>D` | CAP | `dup 0<` — executed on the golden model (2026-10-02). |
| `D+` | CAP | `push over push push drop pop over over xor -if L1 drop + -if C1 jump C0 L1: drop over -if L2 drop + jump C1 L2: drop + C0: pop pop + ; C1: pop pop + 1 + ;` — call-free. The carry out of the low cells is found by sign tests: if their top bits differ, there is a carry exactly when the sum's top bit is clear; if they match, exactly when both are set. Executed on the golden model (2026-10-02); leaves its caller 6 data cells under its arguments and 5 return entries. Replaces `push SWAP push over + 2DUP U> ROT drop NEGATE pop pop + +`, which was exact but left 1 return entry. |
| `DNEGATE` | CAP | `inv over if L1 drop push inv 1 + pop ; L1: drop 1 + ;` — call-free: `~d + 1`, carrying into the high cell exactly when the low cell is 0. Executed on the golden model (2026-10-02). Replaces `inv SWAP inv SWAP 1 0 D+`, which left a caller no return-stack room, so `DABS` could not run at all. |
| `D-` | CAP | `DNEGATE D+` — executed on the golden model (2026-10-02). |
| `DABS` | CAP | `dup 0< IF DNEGATE THEN` — executed on the golden model (2026-10-02). |
| `D0=` | CAP | `OR 0=` — executed on the golden model (2026-10-02). |
| `D0<` | CAP | `NIP 0<` |
| `D=` | CAP | `D- D0=` — executed on the golden model (2026-10-02). |
| `D<` | CAP | `ROT 2DUP = IF 2DROP U< ELSE SWAP < NIP NIP THEN` — executed on the golden model (2026-10-02). |
| `(D<)` | CAP | `( d1 d2 -- d1 d2 flag )`, call-free; internal to `DMAX` and `DMIN`. Copies of the high cells go on top; if they differ the flag comes from them, else from the low cells unsigned. The value tested always has its top bit set exactly when `d1 < d2`: `ah` (high signs differ), `ah - bh` (agree), `bl` (low top bits differ), `al - bl` (agree); `x - y` with `y` on top is `push inv pop + inv`. `dup push push over pop over over xor if TIE drop over over xor -if HS drop drop jump S1 HS: drop push inv pop + inv S1: -if N1 drop -1 jump D1 N1: drop 0 D1: pop SWAP ; TIE: drop drop drop dup push push over pop over over xor -if LS drop NIP jump S2 LS: drop push inv pop + inv S2: -if N2 drop -1 jump D2 N2: drop 0 D2: pop pop ROT ;` with `SWAP`, `NIP` and `ROT` in line. Executed on the golden model (2026-10-02). |
| `DMAX` | CAP | `(D<) if L drop push push drop drop pop pop ; L: drop drop drop ;` Executed on the golden model (2026-10-02). Replaces `2OVER 2OVER D< IF 2SWAP THEN 2DROP`, which needs 8 data cells plus `D<`'s 2: the whole 10-deep data stack, so it failed whenever the caller held anything at all (D-2). |
| `DMIN` | CAP | `(D<) if L drop drop drop ; L: drop push push drop drop pop pop ;` Executed on the golden model (2026-10-02); replaces `2OVER 2OVER D< 0= IF 2SWAP THEN 2DROP` for the same reason as `DMAX`. |
| `D2*` | CAP | `2* over 0< NEGATE OR SWAP 2* SWAP` |
| `D2/` | CAP | `dup 1 and push 2/ SWAP 1 RSHIFT pop IF MSB OR THEN SWAP` |
| `2DROP` | IN | `drop drop` |
| `2DUP` | IN | `over over` |
| `2SWAP` | CAP | `ROT push ROT pop`, with `ROT` and `SWAP` in line so it makes no calls. Executed on the golden model (2026-10-02). Called, `ROT` and `SWAP` left its caller 2 return entries; in line, 4. |
| `2OVER` | CAP | `push push 2DUP pop pop 2SWAP` — executed on the golden model (2026-10-02). |
| `2ROT` | CAP | `2>R 2SWAP 2R> 2SWAP` |
| `2>R` | IN | `SWAP push push` |
| `2R>` | IN | `pop pop SWAP` |
| `2R@` | IN | `pop pop 2DUP push push SWAP` |
### 5.8 Number formatting and output
All output reaches the console through `EMIT` (DEV).
| Word | Fate | Notes |
| --- | --- | --- |
| `<#` `#` `#S` `HOLD` `SIGN` `#>` | CAP | Standard pictured-output definitions over `UM/MOD` and a hold buffer. v3's tolerant `#>` (pops `ud` only if present) is not kept; v4 follows the standard stack effect. |
| `.` `.R` `U.` `U.R` `D.` `D.R` | CAP | Built on pictured output and `TYPE`. |
| `.S` | RET | No visible stack pointer (D-2). |
| `?` | CAP | `@ .` |
| `DUMP` | CAP | Loop over `@`/`C@` with pictured output. |
| `BASE` | CAP | Variable. |
| `DECIMAL` `HEX` `OCTAL` | CAP | `10 BASE !` and so on. |
### 5.9 Strings, parsing, and input
| Word | Fate | Notes |
| --- | --- | --- |
| `COUNT` | CAP | `dup 1+ SWAP C@` |
| `CMOVE` | CAP | See below. |
| `CMOVE>` | CAP | See below. |
| `BLANK` | CAP | `32 FILL` |
| `-TRAILING` | CAP | Loop from the end while the character is a space. |
| `COMPARE` | CAP | Byte loop returning `-1`, `0`, or `1`. v3's counted-string auto-detection is not kept. |
| `SEARCH` | CAP | Nested loop over `COMPARE`. Same note. |
| `SCAN` `SKIP` | CAP | Byte loops. |
| `BL` | IN | `32` |
| `EXPECT` `QUERY` | CC | Built on `KEY` (DEV). |
| `SPAN` `TIB` `>IN` `SOURCE` | CC | Interpreter state on the host node. |
| `WORD` `ENCLOSE` | CC | Parser. |
| `NUMBER` `CONVERT` | CC | Rewritten to honour `BASE` (v3's `NUMBER` is base 10 only). |
| `S"` `(s")` `[']` | CC | Compiler words. |
| `LITERAL` `[LITERAL]` (placeholders) | RET | The working `LITERAL` is in §5.17. |
```forth
: CMOVE ( src dst u -- )
BEGIN dup WHILE 1- push over C@ over C! 1+ SWAP 1+ SWAP pop REPEAT drop 2DROP ;
: CMOVE> ( src dst u -- )
BEGIN dup WHILE 1- push over R@ + C@ over R@ + C! pop REPEAT drop 2DROP ;
```
### 5.10 Terminal I/O
| Word | Fate | Notes |
| --- | --- | --- |
| `EMIT` | DEV | Console service: one-character message. |
| `KEY` | DEV | Console service: blocking receive. |
| `?TERMINAL` | DEV | Console service: non-blocking status. |
| `TYPE` | CAP | Loop of `C@ EMIT`, or one string message to the console node. |
| `CR` | CAP | `10 EMIT` |
| `SPACE` | CAP | `BL EMIT` |
| `SPACES` | CAP | `BEGIN dup 0> WHILE SPACE 1- REPEAT drop` |
| `."` `(do-string)` | CC | Compiler words. |
### 5.11 Blocks and mass storage
The storage service belongs to the Artemis role, now a device node.
| Word | Fate | Notes |
| --- | --- | --- |
| `BLOCK` `BUFFER` `UPDATE` `SAVE-BUFFERS` `EMPTY-BUFFERS` `FLUSH` | DEV | Block-service messages. Buffers live in the requesting node's RAM or in DDR. |
| `LOAD` `THRU` `-->` | CC | The interpreter reads blocks through the service. |
| `LIST` | CAP | `BLOCK` plus `TYPE`. |
| `SCR` | CAP | Variable. |
| `BLK-CONFIRM-FORMAT` `RELOCATE-BLOCK` | DEV | Owner-only storage messages. |
| `BLK-ACL-ALLOW@` `BLK-ACL-ALLOW!` `BLK-ACL-TTL@` `BLK-ACL-TTL!` | HERA | ACL state is held by Hera. |
| `BLK-OWNER@` | HERA | Read-only, as in v3. |
| `BLK-ATTACH` | RET | Took a raw host pointer. Replaced by an attach message from the device node. |
### 5.12 Dictionary space
| Word | Fate | Notes |
| --- | --- | --- |
| `HERE` `ALIGN` `ALLOT` `,` `C,` `2,` `PAD` `LATEST` | CC | The dictionary lives on the host node. |
| `SP@` `SP!` | RET | No visible stack pointer (D-2). |
### 5.13 Dictionary manipulation
| Word | Fate |
| --- | --- |
| `'` `FIND` `SMUDGE` `HIDDEN` `>BODY` `>NAME` `NAME>` `>LINK` `LINK>` `CFA` `LFA` `NFA` `PFA` `TRAVERSE` `INTERPRET` | CC |
### 5.14 Vocabularies
| Word | Fate |
| --- | --- |
| `VOCABULARY` `DEFINITIONS` `CONTEXT` `CURRENT` `FORTH` `ORDER` `(FIND)` | CC |
### 5.15 System
| Word | Fate | Notes |
| --- | --- | --- |
| `(` `\` | CC | |
| `EXECUTE` | CAP | `push ;` (tail-jumps to the xt; the xt returns to `EXECUTE`'s caller) |
| `NOP` | OP | `nop` |
| `QUIT` `ABORT` `ABORT"` `(ABORT")` `COLD` `WARM` | CC | |
| `BYE` `REBOOT` | HERA | |
| `SAVE-SYSTEM` | HERA | Snapshot becomes a capsule-image request. |
| `WORDS` `VLIST` `SEE` | CC | |
| `PAGE` | DEV | |
| `79-STANDARD` | CC | |
### 5.16 Line editor
| Word | Fate |
| --- | --- |
| `L` `S` `SHOW` `EDIT` | CC |
### 5.17 Defining words and the compiler
| Word | Fate | Notes |
| --- | --- | --- |
| `:` `;` `CREATE` `VARIABLE` `CONSTANT` `DOES>` `IMMEDIATE` `STATE` `[` `]` `LITERAL` `COMPILE` `[COMPILE]` `FORGET` `FENCE` | CC | |
| `LIT` | OP | `@p` |
| `does_rt` | RET | Internal helper; `DOES>` is implemented by the compiler capsule. |
### 5.18 Control flow
| Word | Fate | v4 definition / notes |
| --- | --- | --- |
| `IF` `ELSE` `THEN` `BEGIN` `UNTIL` `AGAIN` `WHILE` `REPEAT` `DO` `?DO` `LOOP` `+LOOP` `LEAVE` `CASE` `OF` `ENDOF` `ENDCASE` | CC | Compile-time structure words. Expansions per §2 and below. |
| `EXIT` | OP | `;` |
| `(BRANCH)` | OP | `jump` |
| `(0BRANCH)` | IN | `if L … drop` (§2) |
| `(DO)` | IN | `SWAP push push` (R: limit index) |
| `(?DO)` | IN | `2DUP = IF 2DROP jump past-loop THEN SWAP push push` |
| `(LOOP)` | IN | See below. |
| `(+LOOP)` | IN | Sign-aware boundary-crossing test; specified in the compiler capsule. |
| `(LEAVE)` | IN | `pop drop pop dup push push jump loop-test` (sets index to limit) |
| `I` | IN | `pop dup push` |
| `J` | IN | `pop pop pop dup push SWAP push SWAP push` |
| `UNLOOP` | IN | `pop pop drop drop` |
```
(LOOP) expansion:
pop 1 + pop \ index' limit
2DUP xor \ index' limit flag (0 when equal)
if Lexit
drop push push \ R: limit index'
jump Lbody
Lexit: drop drop drop
```
`(LOOP)` terminates when the index reaches the limit, which matches v3 for every loop where
`start < limit`.
**New in v4:** `FOR`, `NEXT`, and `UNEXT` are native (§2). Counted loops that don't need an ascending
index should use them; they cost one instruction per iteration.
### 5.19 StarForth extensions
| Word | Fate | Notes |
| --- | --- | --- |
| `ENTROPY@` `ENTROPY!` | MM | Per-call-target heat table (D-6). |
| `WORD-ENTROPY` `RESET-ENTROPY` `TOP-WORDS` | CC | Reports over the heat registers. |
| `(-` `INIT` | CC | |
| `VERSION` | CC | |
| `SEED` `RANDOM` | CAP | Deterministic PRNG (xorshift) in capsule code, reproducible from the seed. |
| `WAIT` | CAP | Loop until the `ANTICLOCK` register has advanced `n`. |
| `HEARTBEAT-TICKS@` | MM | `HEARTBEAT` register. |
| `ZUSE-AUTHENTICATE` `ZUSE-SESSION?` `ZUSE-PUBKEY@` `ZUSE-CERT-INSTALLED?` | HERA | |
### 5.20 Word-level ACL
| Word | Fate | Notes |
| --- | --- | --- |
| `ACL-MODE@` `ACL-MODE!` `ACL-TTL@` `ACL-TTL!` `ACL-ALLOW@` `ACL-ALLOW!` `ACL-PINNED?` `ACL-PIN` `ACL-INHERIT` `ACL-INIT-PRIMITIVES` | HERA | Hera holds the ACL table. In the fabric, per-message ACL checks happen in the router (§6). |
| `ACL-HEAT@` | MM | |
| `ACL-WORD-ID` | CC | |
### 5.21 Physics: benchmark and diagnostics
| Word | Fate | Notes |
| --- | --- | --- |
| `BENCH-DICT-LOOKUP` `PHYSICS-CACHE-STATS` `PHYSICS-TOGGLE-CACHE` `PHYSICS-RESET-STATS` `PHYSICS-BUILD-INFO` | RET | Measure v3's software dictionary and hot-words cache, which do not exist on a node. |
| `PHYSICS-BAYESIAN-REPORT` | CAP | Host node only; kept as an analysis tool. |
| `PHYSICS-WORD-METRICS` `PHYSICS-CALC-KNOBS` `PHYSICS-BURN` `PHYSICS-SHOW-FEEDBACK` | RET | Never registered in v3. |
### 5.22 Physics: pipelining diagnostics
| Word | Fate | Notes |
| --- | --- | --- |
| `PIPELINING-*` (all six) | RET | Return as MM reports if D-6 adds transition counters. |
### 5.23 Physics: freeze, heat, and decay
| Word | Fate | Notes |
| --- | --- | --- |
| `FREEZE-WORD` `UNFREEZE-WORD` `FROZEN?` | MM | Freeze bit in the heat table. |
| `HEAT@` `HEAT!` | MM | Test-only write retained. |
| `SHOW-HEAT` `ALL-HEATS` | CC | |
| `DECAY-RATE@` | MM | Governor parameter register. |
| `FREEZE-CRITICAL` | CAP | Freezes the core set; the list is rewritten for v4 names. |
### 5.24 Dictionary heat optimisation
| Word | Fate | Notes |
| --- | --- | --- |
| `HEAT-PERCENTILES` `LOOKUP-STRATEGY@` `LOOKUP-STRATEGY!` `REORG-BUCKETS` `SHOW-HEAT-OPTIMIZATION` `COMPARE-LOOKUPS` | RET | Lookup strategy is a software-dictionary concern. The host compiler may keep a heat-ordered dictionary internally, but these words are not carried forward. |
### 5.25 Logging
| Word | Fate | Notes |
| --- | --- | --- |
| `LOG-ERROR` … `LOG-DEBUG` (levels) | IN | Constants. |
| `LOG-LEVEL!` `LOG-LEVEL@` | CAP | Variable. |
| `LOG-ERROR"` … `LOG-DEBUG"` | CC | |
| `LOG-*-STR` | DEV | Log-ring service on the recorder (the ARM). |
| `(do-log-*)` | CC | |
| `(LOG-APPEND-RAW)` | DEV | |
### 5.26 Q48.16 fixed-point math
A Q48.16 value is 64 bits, so on a 32-bit node it occupies **two cells** and every Q word is a double
word. Per D-10 it occupies two cells on a 64-bit node too. Per D-8, v4 Q values are signed.
| Word | Fate | Notes |
| --- | --- | --- |
| `Q.+` `Q.-` | CAP | `D+`, `D-` — executed on the golden model (2026-10-02) against v3's `q48_add`/`q48_sub` (`uint64_t` wrapping): bit-for-bit at 32-bit cells; at 64-bit cells the low cell is v3's, per D-10. |
| `Q.*` | CAP | `SWAP push over over UM* drop push push over pop -if L1 SWAP jump L2 L1: SWAP drop 0 L2: pop SWAP - push push over pop UM* pop + ROT pop dup push over -if L3 drop dup jump L4 L3: drop 0 L4: push UM* pop - D+ ROT pop UM* SWAP push 0 D+ pop push over pop SWAP Q.TO-INT push Q.TO-INT pop SWAP` (`SWAP` and `ROT` in line) — `floor(a*b / 2^16)`, signed (D-8), cut to two cells. Cells 0–2 of the product come from the unsigned cell products `a1*b1` (low cell), `a0*b1`, `a1*b0`, `a0*b0`, in that order, each input dropped after its last use and `b0` waiting on the return stack. Reading `a1` and `b1` as signed takes `(b1<0 ? a0 : 0)` and `(a1<0 ? b0 : 0)` off cell 2; each is folded in as soon as its operands are adjacent. The result is cells 0–2 shifted right 16 with `Q.TO-INT` twice. Executed on the golden model (2026-10-02) against an independent limb-by-limb reference, and against v3's `q48_mul` for non-negative operands. Leaves its caller 3 data cells under its arguments and 3 return entries. Clobbers `A`. |
| `Q./` | CAP | `over over OR if ZERO drop push over pop SWAP over xor (Q/) 4 + b! !b -if L1 DNEGATE L1: push push -if L2 DNEGATE L2: pop pop dup if CHK drop jump DIV CHK: drop over -131072 and if CHK2 drop jump DIV CHK2: drop push push dup pop dup push N-18 FOR 2* UNEXT over over xor -if SAME drop drop -if NOOV jump OV SAME: drop push inv pop + inv -if OV NOOV: drop pop pop jump DIV OV: drop drop drop pop pop drop drop (Q/) 4 + b! @b -if OVP drop 0 MSB ; OVP: drop -1 MAXHI ; DIV: (UQ/) (Q/) 4 + b! @b -if L3 drop DNEGATE ; L3: drop ; ZERO: drop drop drop NODE-ERROR b! -1 !b over over OR if Z0 drop -if ZP drop drop 0 MSB ; ZP: drop drop -1 MAXHI ; Z0: drop ;` — D-11: `a * 2^16 / b`, rounded toward zero; saturated to Q max / Q min on overflow; on division by zero, Q max / Q min by the dividend's sign (0 for `0 / 0`) and `NODE-ERROR` set. The sign of the result waits in `(Q/)`. Overflow needs no division: the quotient reaches `2^(2N-1)` exactly when `|a| * 2^16 >= |b| * 2^(2N-1)`, possible only for `|b| < 2^17`, where it is `a1 >= b0 << (N-17)` unsigned. `MSB` and `MAXHI` are the high cells of Q min and Q max. Executed on the golden model (2026-10-02) against an independent limb-by-limb reference (including `NODE-ERROR`), on width-native edge values, and against v3's `q48_div` for non-negative `a < 2^48`. Leaves its caller 3 data cells under its arguments and 3 return entries. Clobbers `A` and `B`. |
| `(UQ/)` | CAP | Internal to `Q./`: unsigned `floor(a * 2^16 / b)` for `b != 0` and no overflow. Restoring division, `2N+16` steps, on one shifting register — quotient (starting as `a`) above remainder — with the quotient and divisor in `(Q/)` so the stacks carry only the remainder, the quotient bit and the loop count. Each step's quotient bit enters at the next step's shift; with no overflow the bits leaving the quotient in the last 16 steps are zeros. Trial subtraction by in-line sign tests (`x - y` with `y` on top is `push inv pop + inv`), including the full double subtraction with its borrow, so the loop calls nothing but `D2*C`. Executed on the golden model (2026-10-02). Clobbers `A` and `B`. |
| `D2*C` | CAP | `( lo hi cin -- lo' hi' cout )`: the double shifted left one bit, `cin` entering at the bottom and `cout` the bit leaving the top (both 0 or 1). `a! dup -if L1 drop 1 jump L2 L1: drop 0 L2: push over -if L3 drop 1 jump L4 L3: drop 0 L4: over + + push 2* a + pop pop` — `cin` waits in `A`, and `2hi + m` is `over + +`, so it needs one return entry. Executed on the golden model (2026-10-02). Clobbers `A`. |
| `(Q/)` | CAP | Variable, 5 cells: `Q./`'s quotient register, divisor and result sign. Like `BASE` and the hold buffer, it lives in node memory. |
| `Q.ABS` `Q.NEG` | CAP | `DABS`, `DNEGATE` — executed on the golden model (2026-10-02) against v3's `q48_abs` and `0 - q`: bit-for-bit at 32-bit cells, including v3's wrap of Q min to itself; at 64-bit cells the low cell is v3's and the high cell is the true sign, per D-10. |
| `Q.COS` | CAP | v3's `q48_cos_approx`: on `xu = |(Q.REDUCE) x|`, `1 - xu^2/2! + xu^4/4! - …` to `n = 10`, run by `Q.SIN`'s loop: `(Q.REDUCE) -if A1 inv 1 + A1: 0 (QT) b! !b 0 over 0 Q.* drop (QT) 1 + b! !b 65536 (QT) 2 + b! !b 65536 (QT) 3 + b! !b 2 (QT) 4 + b! !b -1 (QT) 5 + b! !b jump TRIG` — term and sum start at 1.0, `n` at 2, and the sign cell is 0 so nothing is negated at the end. It jumps into the loop rather than calling it, so it costs no extra return entry. Executed on the golden model (2026-10-03): bit for bit v3's result on angles of every size and sign. Leaves its caller 3 data cells under its argument and 2 return entries. Clobbers `A` and `B`. |
| `Q.SIN` | CAP | v3's `q48_sin_approx`: on `r = (Q.REDUCE) x` and `xu = |r|`, `xu - xu^3/3! + xu^5/5! - …` to `n = 11`, each term the last times `xu^2` over `n(n-1)`, stopping once a term is below 10 ulp; negated if `r < 0`. After the reduction every value fits one cell, so the variable `(QT)` holds single cells and the only calls are `(Q.REDUCE)`, `Q.*`, `Q./` and `UM*`. The Taylor loop is written so that `Q.COS` can jump into it. Executed on the golden model (2026-10-03): bit for bit v3's result on angles of every size and sign. Leaves its caller 3 data cells under its argument and 2 return entries. Clobbers `A` and `B`. |
| `(Q.REDUCE)` | CAP | `( x -- r )`, v3's `q48_reduce_angle`: the angle reduced to one cell, `-pi <= r <= pi` with `pi = 205887`, `2pi = 411774` — the remainder of `x / 2pi` with the sign of `x`, then one step of `2pi` back into range. `x / 2pi` does not fit a cell, but only the remainder is wanted: `|x| mod 2pi` is two `UM/MOD` steps, the high cell first and its remainder then leading the low cell. `dup (QT) b! !b -if L1 DNEGATE L1: 0 411774 UM/MOD drop 411774 UM/MOD drop (QT) b! @b -if P1 drop inv 1 + jump J1 P1: drop J1: dup -205888 + -if BIG drop dup 205887 + -if OK drop 411774 + ; OK: drop ; BIG: drop -411774 + ;` Executed on the golden model (2026-10-03), bit for bit v3's. Clobbers `A` and `B`. |
| `(QT)` | CAP | Variable, 6 cells: `Q.SIN` / `Q.COS`'s sign, `x^2`, term, sum, `n` and subtract flag. |
| `Q.LOG` | CAP | v3's `q48_log_approx`: `x = 2^k * m` with `1.0 <= m < 2.0`; `ln m` by up to 6 Newton rounds on `e^y = m` from `y = m - 1.0`, stopping once `|m - e^y| < 100` ulp; result `y + k * ln 2` with `ln 2 = 45426`. `e^y` is `Q.EXP`'s Taylor loop **written in line** (flattened, ruled 2026-10-02), so `Q.LOG` calls only `Q.*`, `Q./` and `UM*` and the call chain is one level shorter than through `Q.EXP`. After the reduction `m`, `y`, the Taylor term and its sum each fit one cell at either width, so the variable `(QL)` holds single cells. `x <= 0` returns 0 and sets `NODE-ERROR` (D-12). Executed on the golden model (2026-10-03): bit for bit v3's result for every `x > 0` tested, including a sweep of the reduced `m`. v3's clamp `y = max(y - corr, 0)` is kept for fidelity but cannot fire: over all 65536 values of `m` it is never reached, and the iteration never needs more than 4 rounds. Leaves its caller 3 data cells under its argument and 2 return entries. Clobbers `A` and `B`. |
| `(QL)` | CAP | Variable, 7 cells: `Q.LOG`'s `m`, `y`, `k`, rounds left, Taylor term (then `delta`), sum, and `n * 1.0`. |
| `Q.SQRT` | CAP | v3's `q48_sqrt_approx`: Newton from `x0 = q/2 + 0.25`, up to 8 rounds of `x' = (x + q/x) / 2`, returning `x` as soon as `|x' - x| < 10` ulp; `sqrt(0) = 0`, `sqrt(1.0) = 1.0`. `q < 0` returns 0 and sets `NODE-ERROR` (D-12). `q`, `x` and the rounds left live in the variable `(QR)`. The halving is a logical double shift, `push a! 0 pop +* MAXHI and push drop a pop`. Executed on the golden model (2026-10-02): bit for bit v3's result for `0 <= q < 2^48`; above that v3's `q48_div` saturates and v4 divides correctly, so they part. Leaves its caller 3 data cells under its argument and 2 return entries. Clobbers `A` and `B`. |
| `(QR)` | CAP | Variable, 5 cells: `Q.SQRT`'s `q`, `x` and rounds left. |
| `Q.EXP` | CAP | v3's `q48_exp_approx`: `1 + x + x^2/2! + …` to 10 terms on `x = |q|`, stopping once a term is below 50 ulp; `1.0 Q./ e^|q|` for `q < 0`; `e^0 = 1.0`; `|q| >= 16.0` gives 0 (`q < 0`) or **Q max** (v3 returned all ones, which reads as −ulp under D-8). `x`, the term, the sum, the sign and the term index live in the variable `(QE)`, so only `Q.*` and `Q./` arguments sit on the stacks. Executed on the golden model (2026-10-02): bit for bit v3's result for `|q| < 16.0` (at 64-bit cells as the same value, high cell 0). Leaves its caller 3 data cells under its argument and 2 return entries. Clobbers `A` and `B`. |
| `(QE)` | CAP | Variable, 8 cells: `Q.EXP`'s `x`, term, sum, sign and term index. |
| `Q.FROM-INT` | CAP | `push 0 a! 0 pop 15 FOR +* UNEXT push drop a pop` — `n * 2^16` as a signed double. `+*` with `S = 0` never adds, so each step is an exact arithmetic right shift of `T:A`; starting from `T:A = n:0` (that is, `n * 2^N`) and shifting `N-16` bits leaves `n * 2^16`. The count is `N-17`: 15 at 32-bit cells, 47 at 64. Negative values are no longer clamped to 0. Executed on the golden model (2026-10-02): matches v3's `q48_from_u64` for `n >= 0` (at 64-bit cells in the low cell, where v3 wraps for `n >= 2^47`, per D-10). Clobbers `A`. |
| `Q.TO-INT` | CAP | `push a! 0 pop 15 FOR +* UNEXT drop drop a` — the same `+*` shift, 16 bits at every cell width, leaving the low cell in `A`. Rounds toward minus infinity, as v3's arithmetic shift does (−1.5 gives −2). Executed on the golden model (2026-10-02): v3's `q48_to_u64` exactly at 64-bit cells, its low cell at 32. Clobbers `A`. |
| `Q.1` `Q.0` `Q.SCALE` | IN | Double-cell constants, placed in line as two literals, low cell first: `Q.1` and `Q.SCALE` are `65536 0` (1.0), `Q.0` is `0 0`. v3's are 65536, 0 and 65536. Executed on the golden model (2026-10-03): the values match v3's, `Q.1 Q.TO-INT` is 1, `1 Q.FROM-INT` is `Q.1`, and `q Q.1 Q.*` and `q Q.0 Q.+` return `q`. |
| `Q.=` `Q.<` `Q.>` `Q.0=` `Q.MAX` `Q.MIN` | CAP | `D=`, `D<`, `2SWAP D<` (for `Q.>`), `D0=`, `DMAX`, `DMIN`. Signed (D-8); v3 compared unsigned, so v3 agrees only when both values have the same sign. Executed on the golden model (2026-10-02). `Q.>` was given here as `SWAP D<`, which swaps single cells, not Q values, and gave a wrong answer in 11702 of 20169 test cases. |
| `Q.PRINT` | CAP | Pictured output, five fractional digits. |
### 5.27 Inference engine
The runtime governor moves into hardware as the multi-level Rolling Window of Truth (see
`JUSTIFICATION.md`). These words survive on the host node as analysis tools.
| Word | Fate | Notes |
| --- | --- | --- |
| `INFER-RUN` `INFER-WINDOW@` `INFER-DECAY@` `INFER-VARIANCE@` `INFER-FIT@` `INFER-EARLY-EXIT@` | CAP | Host node only. Ported from `inference_words.c`. |
| `Q.VARIANCE` `INFER-DECAY-SLOPE` `INFER-WINDOW-WIDTH` `WINDOW-DIVERSITY` | CAP | Host node only. |
| `L8-MODE` `L8-UPDATE` `L8-APPLY` `L8-TABLE-FORCE` | MM | Jacquard selector state becomes governor registers. `L8-TABLE-FORCE` remains the DoE override. |
| `BAYES-*` (all six) | RET | Model the v3 hot-words cache and bucket search. |
### 5.28 DEFER and IS
| Word | Fate | Notes |
| --- | --- | --- |
| `DEFER` `IS` `DEFER@` | CC | A deferred word's runtime is `@p push ;` followed by the stored xt. |
### 5.29 – 5.32 Framebuffer, keyboard, TrueType, scrollback
| Word | Fate |
| --- | --- |
| `PLOT` `FB-WIDTH` `FB-HEIGHT` | DEV |
| `KBD-SCAN` `KBD-DEBUG` `VKBD-EVENT` `VKBD-DEBUG` `KEY-EVENT` `ALT+TAB` | DEV |
| `TTF-TEXT` | DEV |
| `SCROLL-BACK` `SCROLL-FWD` | DEV |
### 5.33 Kernel REPL and DoE hooks
| Word | Fate | Notes |
| --- | --- | --- |
| `HB-ON` `HB-OFF` | MM | Recorder-enable register. The fabric pushes DoE rows into a FIFO; the ARM drains it to storage. |
| `BLK-ATTACH-ACK` `KH-BLK-ATTACH-SEND` `KH-ELEVATE-SEND` | RET | Kernel-Hermes calls. Hermes is now the fabric; these become ordinary packets (§6). |
### 5.34 Hera and child-VM words
| Word | Fate | Notes |
| --- | --- | --- |
| `BIRTH` `KILL` `START` `STOP` `USE` `EXEC` `EJECT` `CONNECT-HERMES` `CONNECT-ARTEMIS` `BYE` | HERA | A VM becomes a node (or a group of nodes). `BIRTH` loads a capsule into a node and releases it. |
| `VM-EXEC` `VM-CALL` `VM-STEP` | CAP | Built on `SEND` and `RECV` (§6). |
| `VM-HEAT` `VM-ERROR?` `VM-COUNT` `VM-CONSERVED?` `VM-PHYSICS-STATUS` | MM + HERA | Per-node heat is a register; fleet totals are Hera's. |
| `SWITCH-MARK-WORK` | RET | Nodes run concurrently; there is no context switch to mark. |
| `MAMA-VM-ID` `NAME>XT` | HERA, CC | |
| `CAPSULE-*` `WORKER-BIRTH` `UNATTENDED-BIRTH` `CONSOLE-ATTACH` | HERA | |
| `MINT` `MINT-SCRATCH` `MINT-SCRATCH-EMIT` `ZUSE-ELIGIBILITY-ADD` `ZUSE-ELIGIBLE?` `ELEVATE-PUBKEY-UNPACK` | HERA | |
| `RUNCAP-TEST` `PAIR-TEST` | HERA | Diagnostics, kept. |
| `STADIUM-ADMIT` `STADIUM-EVICT` `STADIUM-RES-PULL` `STADIUM-RES-PUSH` | HERA | Admission and reservoir are Hera's. |
| `STADIUM-HEAT@` `STADIUM-HEAT!` `STADIUM-RES@` `STADIUM-WORD-HEAT` | MM | |
### 5.35 Hosted lifecycle stubs
| Word | Fate |
| --- | --- |
| `BIRTH` `KILL` `PAUSE` `RESUME` `USE` (hosted) | RET — the hosted v4 golden model implements the real HERA messages. |
---
## 6. Messaging between nodes
Each node has four neighbour ports (`PORT-UP`, `PORT-DOWN`, `PORT-LEFT`, `PORT-RIGHT`) mapped into its
address space. A read from a port blocks until the neighbour writes; a write blocks until the neighbour
reads. This is the GA144 model. No instruction is added: a port is an address.
### 6.1 Packet format (proposal)
| Word | Contents |
| --- | --- |
| 0 | Header: destination node (8 bits), type (8), TTL/heat (8), payload length in words (8). |
| 1 | ACL tag: sender identity fingerprint, checked by the router on every packet. |
| 2 … | Payload. |
The router in each node forwards packets not addressed to it, decrements TTL, and drops a packet whose
TTL reaches zero or whose ACL tag fails. This is v3 Hermes's per-message ACL check and unconditional TTL
expiry, moved into logic. A packet type `SOS` is reserved for any node to emit.
### 6.2 Send and receive
```forth
: SEND ( addr u port -- ) b! SWAP a! BEGIN dup WHILE 1- @+ !b REPEAT drop ;
: RECV ( addr u port -- ) b! SWAP a! BEGIN dup WHILE 1- @b !+ REPEAT drop ;
```
`A` walks the buffer and `B` holds the port, so each word moved costs one fetch and one store.
---
## 7. Memory-mapped registers
Addresses are assigned in the node memory map (D-4). Names only here.
| Register | Access | Contents |
| --- | --- | --- |
| `HEAT-OP[0..31]` | R | Per-opcode retirement heat. |
| `HEAT-CALL[...]` | R/W | Per-call-target heat, with freeze bit (D-6). |
| `ANTICLOCK` | R | Virtual tick: a pure function of the execution stream. |
| `HEARTBEAT` | R | Adaptive heartbeat count. |
| `GOV-*` | R/W | Governor parameters and Jacquard selector state (`L8-*`, decay rate). |
| `REC-ENABLE` | R/W | DoE recorder on/off (`HB-ON` / `HB-OFF`). |
| `PORT-STATUS` | R | Per-port ready flags, for non-blocking polls. |
| `NODE-ERROR` | R/W | Arithmetic error flag: set to −1 by `Q./` on division by zero (D-11) and by `Q.SQRT` / `Q.LOG` outside their domain (D-12), cleared by writing 0. `VM-ERROR?` reads it. |