# 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. |