?TERMINAL reads CONSOLE-STATUS; KEY polls it until a character is pending and then takes it from CONSOLE-RX. Executed on the golden model at both cell widths, including a transcript of the v3 binary, and KEY shown still waiting after 5000 instruction words with no input. v3's KEY returned -1 at the end of its input; here KEY waits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
964 lines
78 KiB
Markdown
964 lines
78 KiB
Markdown
# 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! D2/ M* FILL ERASE MOVE COUNT CMOVE CMOVE> BLANK -TRAILING COMPARE SEARCH SCAN SKIP DECIMAL HEX OCTAL UM* * UM/MOD Q.FROM-INT Q.TO-INT Q.* Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS +LOOP J HOLD SIGN # #S . .R U. U.R D. D.R ? DUMP Q.PRINT TYPE SEND RECV`. Words that clobber `B`:
|
||
`COMPARE SEARCH Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS <# HOLD SIGN # #S #> . .R U. U.R D. D.R ? DUMP Q.PRINT EMIT CR SPACE SPACES TYPE KEY ?TERMINAL 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. |
|
||
| **D-13** | Pictured-output hold buffer: size and errors (ruled 2026-10-03). | **63 characters, as v3; on error set `NODE-ERROR` and drop the character.** The buffer holds 63 characters at either cell width. `HOLD` of a value outside 0–255, or into a full buffer, stores nothing and sets `NODE-ERROR` (§7), which is v3's behaviour (it set its error flag and dropped the character). A full double in base 2 therefore does not fit, as in v3. |
|
||
|
||
**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. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 7 data cells and 6 return entries. |
|
||
| `?DUP` | CAP | `if Z dup ; Z: ;` (`dup IF dup THEN` without the extra copy). Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `ROT` | CAP | §4. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 4 return entries; in line, with `SWAP` in line, it calls nothing. |
|
||
| `-ROT` | CAP | `SWAP push SWAP pop`, `SWAP` in line (`ROT ROT` in one pass). Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 5 return entries. |
|
||
| `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` — executed on the golden model (2026-10-03) with `R@` and `R>`, including a transcript of the v3 binary. |
|
||
| `R>` | IN | `pop` — executed on the golden model (2026-10-03), see `>R`. |
|
||
| `R@` | IN | `pop dup push` — executed on the golden model (2026-10-03), see `>R`. |
|
||
|
||
### 5.3 Memory
|
||
|
||
| Word | Fate | v4 definition / notes |
|
||
| --- | --- | --- |
|
||
| `@` | IN | `a! @` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `!` | IN | `a! !` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `+!` | CAP | `a! @ + !` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `-!` | CAP | `a! NEGATE @ + !`, `NEGATE` in line — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `2@` | CAP | `a! @+ @` (low cell at `addr`, high at `addr+1`, as in v3) — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `2!` | CAP | `a! SWAP !+ !`, `SWAP` in line — executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 7 data cells and 6 return entries. |
|
||
| `C@` | CAP | See below (D-1). Call-free. Executed on the golden model (2026-10-03). Leaves its caller 8 data cells and 7 return entries. |
|
||
| `C!` | CAP | See below (D-1). Call-free. Executed on the golden model (2026-10-03). |
|
||
| `FILL` | CAP | `( baddr u c -- )`: the low byte of `c`. See below. Nothing for `u = 0`; for `u < 0` nothing is written and `NODE-ERROR` is set (v3 read a negative count as a huge unsigned one). Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 5 data cells and 5 return entries. Clobbers `A` and, on error, `B`. |
|
||
| `MOVE` | CAP | `( src dst u -- )`, bytes: `push over over - -if UP drop pop jump CMOVE> UP: drop pop jump CMOVE` — `CMOVE>` when `src < dst`, else `CMOVE`, so overlapping bytes are read before they are overwritten, as v3's `memmove`. The test is the sign of `src - dst`, in line. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. |
|
||
| `ERASE` | CAP | `0 FILL`, as `0 jump FILL`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `CELLS` | IN | Empty (word-addressed, D-1); v3 answers 24 for `3 CELLS`. Executed on the golden model (2026-10-03). |
|
||
|
||
```forth
|
||
\ byte access on a word-addressed node, little-endian: four bytes to a cell at
|
||
\ either cell width (so compiled code is the same, D-9), byte address = 4 * word
|
||
\ address + byte index. `@` and `!` inside are the opcodes, addressing through A.
|
||
\ C@ is call-free, like C!: one case per byte position, the cell shifted down
|
||
\ with a 2/ loop and masked. It replaces a version built on LSHIFT, RSHIFT and
|
||
\ SWAP calls, which was correct but left its caller 4 return-stack entries.
|
||
: C@ ( baddr -- c )
|
||
dup 2/ 2/ a! 3 and \ k A: word address
|
||
if K0 -1 + if K1 -1 + if K2
|
||
drop @ 23 FOR 2/ UNEXT 255 and ; \ byte 3
|
||
K2: drop @ 15 FOR 2/ UNEXT 255 and ; \ byte 2
|
||
K1: drop @ 7 FOR 2/ UNEXT 255 and ; \ byte 1
|
||
K0: drop @ 255 and ; \ byte 0
|
||
|
||
\ C! is call-free: one case per byte position. The byte is shifted up with a
|
||
\ 2* loop, that byte of the cell cleared with a constant mask, and the two added.
|
||
\ It replaces a version built on LSHIFT, RSHIFT, SWAP and OR calls, which was
|
||
\ correct but left its caller 4 return-stack entries; this leaves 7.
|
||
: C! ( c baddr -- )
|
||
dup 2/ 2/ a! 3 and push 255 and pop \ c' k A: word address
|
||
if K0 -1 + if K1 -1 + if K2
|
||
drop 23 FOR 2* UNEXT @ 4278190080 inv and + ! ; \ byte 3
|
||
K2: drop 15 FOR 2* UNEXT @ -16711681 and + ! ; \ byte 2
|
||
K1: drop 7 FOR 2* UNEXT @ -65281 and + ! ; \ byte 1
|
||
K0: drop @ -256 and + ! ; \ byte 0
|
||
|
||
: FILL ( baddr u c -- )
|
||
-ROT \ c baddr u
|
||
-if OK drop drop drop NODE-ERROR b! -1 !b ; \ u < 0
|
||
OK: if DONE push over over C! 1 + pop -1 + jump OK
|
||
DONE: drop drop drop ;
|
||
```
|
||
|
||
### 5.4 Arithmetic
|
||
|
||
| Word | Fate | v4 definition / notes |
|
||
| --- | --- | --- |
|
||
| `+` | OP | `+` |
|
||
| `-` | CAP | `NEGATE +`, `NEGATE` in line — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `*` | CAP | `UM* drop` — executed on the golden model (2026-10-03) against C's wrapping product. Leaves its caller 6 data cells and 5 return entries. |
|
||
| `/` | CAP | `/MOD NIP`, as `push S>D pop SM/REM push drop pop` (`/MOD`'s body and `NIP` in line, so it runs no deeper than `/MOD`). Truncates toward zero, as v3. Executed on the golden model (2026-10-03) against C wherever the quotient fits a cell. Leaves its caller 5 data cells and 2 return entries. Division by zero is unspecified, as for `UM/MOD` (§4); v3 reported an error and stopped the line. |
|
||
| `MOD` `/MOD` `*/` `*/MOD` (§4 versions) | RET | Shadowed duplicates. Only the §6 versions survive. |
|
||
| `1+` `1-` `2+` `2-` | IN | `1 +`, `-1 +`, `2 +`, `-2 +` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `2*` | OP | `2*` |
|
||
| `2/` | OP | `2/` |
|
||
| `ABS` | CAP | `dup 0< IF NEGATE THEN` — executed on the golden model (2026-10-02). |
|
||
| `NEGATE` | CAP | §4. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `MIN` | CAP | `over over < if B drop drop ; B: drop push drop pop ;` — the smaller, signed. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 6 return entries. |
|
||
| `MAX` | CAP | `over over < if A drop push drop pop ; A: drop drop ;` — the larger, signed. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 6 return entries. |
|
||
|
||
### 5.5 Logic and comparison
|
||
|
||
| Word | Fate | v4 definition / notes |
|
||
| --- | --- | --- |
|
||
| `AND` | OP | `and` |
|
||
| `XOR` | OP | `xor` |
|
||
| `OR` | CAP | §4. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `INVERT` | OP | `inv` |
|
||
| `NOT` | CAP | `0=` (FORTH-79 logical not, as in v3), as `jump 0=`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `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. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `0<>` | CAP | `if Z drop -1 ; Z: ;` (`0= 0=` without calls). Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `0>` | CAP | `-if NN drop 0 ; NN: if Z drop -1 ; Z: ;` — two native sign tests in place of `dup 0< SWAP 0= OR 0=`; correct for the most negative number. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `=` | CAP | `xor 0=` — executed on the golden model (2026-10-02). |
|
||
| `<>` | CAP | `xor 0<>`, as `xor jump 0<>`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `<` | CAP | `2DUP xor 0< IF drop 0< ELSE - 0< THEN` (overflow-safe) — executed on the golden model (2026-10-02). |
|
||
| `>` | CAP | `SWAP <`, as `SWAP jump <` with `SWAP` in line. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 6 return entries. |
|
||
| `<=` | CAP | `> 0=`, as `SWAP < jump 0=`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 6 return entries. |
|
||
| `>=` | CAP | `< 0=`, as `< jump 0=`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 6 return entries. |
|
||
| `U<` | CAP | §4. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 7 return entries. |
|
||
| `U>` | CAP | `SWAP U<` — executed on the golden model (2026-10-02). |
|
||
| `WITHIN` | CAP | `( n low high -- flag )`: `push over pop < push < inv pop and` — `low <= n < high`, both comparisons signed, which is what v3 computes. It replaces `over - push - pop U<`, the circular form, which answers differently when `low > high` (`5 10 0 WITHIN` is false in v3 and here, true in that form). Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. |
|
||
| `TRUE` | IN | `-1` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
| `FALSE` | IN | `0` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||
|
||
### 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 | `( d n -- d )`: `S>D DNEGATE jump D+`, `S>D` by a sign test in line. `n` is widened before it is negated, so the most negative `n` is subtracted correctly; `NEGATE M+` would add it. Executed on the golden model (2026-10-03) against C and results recorded from the v3 binary (v3 took the double with its low cell on top; the standard order is kept here, as for `M+`). Leaves its caller 5 data cells and 5 return entries. |
|
||
| `M*` | CAP | `( n1 n2 -- d )`: `over over xor push -if A inv 1 + A: push -if B inv 1 + B: pop UM* pop -if P drop jump DNEGATE P: drop ;` — the unsigned product of the magnitudes, negated when the signs differ; the sign tests are native. Executed on the golden model (2026-10-03) against C and results recorded from the v3 binary. Leaves its caller 6 data cells and 4 return entries. Clobbers `A`. |
|
||
| `M/MOD` | CAP | `( d n -- rem quot )`: `jump SM/REM`. Truncating, the remainder with the dividend's sign, as v3. v3 took the double with its low cell on top (`dhigh dlow n`), the reverse of what its own `M*` leaves; the standard order is kept here, so `M* ... M/MOD` composes. Executed on the golden model (2026-10-03), including results recorded from the v3 binary with the double's cells exchanged. Leaves its caller 5 data cells and 3 return entries. |
|
||
| `MOD` | CAP | `/MOD drop`, with `/MOD`'s body in line: `push S>D pop SM/REM drop`. The remainder has the dividend's sign, as v3. Executed on the golden model (2026-10-03) against C and results recorded from the v3 binary. Leaves its caller 5 data cells and 2 return entries. Division by zero is unspecified, as for `/`. |
|
||
| `/MOD` | CAP | `push S>D pop SM/REM` — executed on the golden model (2026-10-02). |
|
||
| `*/` | CAP | `*/MOD NIP`, as `push M* pop SM/REM push drop pop`. Executed on the golden model (2026-10-03). Leaves its caller 5 data cells and 2 return entries. |
|
||
| `*/MOD` | CAP | `( n1 n2 n3 -- rem quot )`: `push M* pop jump SM/REM` — the product is a full double, so the answer is exact whenever the quotient fits a cell. v3 multiplied in one cell at 64-bit cells and so was right only while `n1 * n2` fitted one; the two agree there. Executed on the golden model (2026-10-03) against the identity `quot * n3 + rem = n1 * n2` on every combination of the edge values, and results recorded from the v3 binary. Leaves its caller 5 data cells and 2 return entries. |
|
||
|
||
### 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<`, as `push drop pop jump 0<`. Executed on the golden model (2026-10-03), including results recorded from the v3 binary. |
|
||
| `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 -if P drop 1 + jump J P: drop J: push 2* pop` — the low cell's top bit enters the high cell; a native sign test in place of `over 0< NEGATE OR`. Executed on the golden model (2026-10-03), including results recorded from the v3 binary. |
|
||
| `D2/` | CAP | `push a! 0 pop +* push drop a pop` — one `+*` with `S = 0` is an exact arithmetic right shift of `T:A`, as in `Q.FROM-INT`. It replaces `dup 1 and push 2/ SWAP 1 RSHIFT pop IF MSB OR THEN SWAP`. Executed on the golden model (2026-10-03), including results recorded from the v3 binary. Clobbers `A`. |
|
||
| `2DROP` | IN | `drop drop` — executed on the golden model (2026-10-03). |
|
||
| `2DUP` | IN | `over over` — executed on the golden model (2026-10-03). |
|
||
| `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 | `push push 2SWAP pop pop jump 2SWAP`. Executed on the golden model (2026-10-03), including a result recorded from the v3 binary. With six cells of its own it leaves its caller 3 data cells and 1 return entry. |
|
||
| `2>R` | IN | `SWAP push push` — executed on the golden model (2026-10-03) with `2R@` and `2R>`; the high cell is on top of the return stack, as in v3. |
|
||
| `2R>` | IN | `pop pop SWAP` — see `2>R`. |
|
||
| `2R@` | IN | `pop pop 2DUP push push SWAP` — see `2>R`. |
|
||
|
||
### 5.8 Number formatting and output
|
||
|
||
All output reaches the console through `EMIT` (DEV).
|
||
|
||
| Word | Fate | Notes |
|
||
| --- | --- | --- |
|
||
| `<#` `#` `#S` `HOLD` `SIGN` `#>` | CAP | Pictured output over `UM/MOD` and a 64-byte hold buffer, filled backwards from its end `HEND` through the pointer `HLD`. Standard stack effects (v3 took its double low cell on top, and its tolerant `#>` popped `ud` only if present; neither is kept). Otherwise v3's behaviour: digits `0`–`9` then `A`–`Z`; `BASE` outside 2–36 reads as 10; 63 characters; `HOLD` of a value outside 0–255 or into a full buffer stores nothing and sets `NODE-ERROR` (D-13). Definitions below. Executed on the golden model (2026-10-03) against a C reference in bases 2, 3, 8, 10, 16, 36 and four invalid ones. `<# #S #>` leaves its caller 4 data cells and 3 return entries; the signed picture `dup push ABS 0 <# #S pop SIGN #>` leaves 2 return entries. `#`, `#S`, `HOLD` and `SIGN` clobber `A` and `B`. |
|
||
| `HLD` | CAP | Variable: byte address of the first held character. `HLD ( -- addr )` is the variable's word address, a literal. Not a v3 word. Executed on the golden model (2026-10-03): after `<#` and each `HOLD` or `#`, `HLD @` is the address `#>` returns. |
|
||
| `.` `.R` `U.` `U.R` `D.` `D.R` | CAP | Built on pictured output and `TYPE`; definitions below. As v3: the number in the current base, then one space; the `.R` words right-justify it in `width` columns first, print a wider number whole, and pad nothing for `width <= 0`. (The space after the `.R` words is not FORTH-79; it is what v3 prints.) Where v4 parts from v3: a double is `( lo hi )`, where v3's `D.` took the low cell on top; `D.` prints the whole double, where v3 printed `DOUBLE-OVERFLOW` unless it fitted one signed cell (they agree whenever it does); printing honours the `BASE` variable, where v3 printed in a host copy that only `DECIMAL`, `HEX` and `OCTAL` set, so `n BASE !` changed v3's input base but not its output; and the number is built in the 63-character hold buffer, so a longer one — a 64-bit cell in base 2, a large double in a small base — loses its leading characters and sets `NODE-ERROR` (D-13), where v3 printed from a private 80-character buffer. Executed on the golden model (2026-10-03) against a C reference in the ten bases of the pictured-output test and eleven field widths, and against five transcripts of the v3 binary (a sixth, the extreme cells, at 64-bit cells). Each leaves its caller 4 data cells; `U.` and `U.R` leave 3 return entries, the signed words 2, their sign waiting on the return stack. All clobber `A` and `B`. |
|
||
| `(W)` | CAP | Variable: the field width of the `.R` words. Like `BASE` and `HLD`, it lives in node memory, so the width is on neither stack while the picture runs. |
|
||
| `.S` | RET | No visible stack pointer (D-2). |
|
||
| `?` | CAP | `( addr -- )`: `a! @ jump .` — the cell at word address `addr` (D-1), printed as `.` prints it. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 2 return entries. Clobbers `A` and `B`. |
|
||
| `DUMP` | CAP | `( baddr u -- )`: `u` bytes from byte address `baddr`, sixteen to a line, in v3's format: the address in hex, `": "`, each byte as two hex digits and a space (three spaces where the line runs out), `" \|"`, the bytes as characters with `.` for anything outside 32–126, `"\|"` and a new line. Always hex, whatever `BASE` is, and `BASE` is put back. The address is two hex digits per byte of a cell — 8 at 32-bit cells, 16 at 64, which is v3's width. Nothing for `u = 0`; for `u < 0` nothing is printed and `NODE-ERROR` is set, as `TYPE` does, where v3 raised its error flag. Addresses are not range-checked (open, `node.h`). Definition below. Executed on the golden model (2026-10-03) against a C reference at six alignments and every length 0–50, and at 64-bit cells against a transcript of the v3 binary, byte for byte. Leaves its caller 4 data cells and 4 return entries. Clobbers `A` and `B`. |
|
||
| `(DP)` | CAP | Variable, 4 cells: `DUMP`'s saved `BASE`, address, count and column. |
|
||
| `BASE` | CAP | Variable. `BASE ( -- addr )` is its word address (D-1), a literal. Executed on the golden model (2026-10-03). Storing to it changes what is printed, which it did not in v3 (see `.`). |
|
||
| `DECIMAL` `HEX` `OCTAL` | CAP | `10 BASE !`, `16 BASE !`, `8 BASE !`, with `!` in line as `a! !`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Each leaves its caller 8 data cells and 8 return entries. Clobber `A`. |
|
||
|
||
```forth
|
||
\ pictured output. HEND is the byte address just past the hold buffer.
|
||
: (BASE) ( -- b ) BASE b! @b dup -2 + -if L1 drop drop 10 ;
|
||
L1: drop dup -37 + -if L2 drop ; L2: drop drop 10 ;
|
||
: <# ( -- ) HEND HLD b! !b ;
|
||
: HOLD ( c -- ) dup -256 and if OKC drop jump ERR
|
||
OKC: drop HLD b! @b -(HEND-62) + -if ROOM drop
|
||
ERR: drop NODE-ERROR b! -1 !b ;
|
||
ROOM: drop HLD b! @b -1 + dup !b jump C!
|
||
: SIGN ( n -- ) -if L1 drop 45 jump HOLD L1: drop ;
|
||
: # ( ud1 -- ud2 )
|
||
0 (BASE) UM/MOD -ROT \ qhi lo rem1
|
||
(BASE) UM/MOD -ROT \ qlo qhi rem
|
||
dup -10 + -if L1 drop 48 + jump L2 L1: drop 55 + L2: jump HOLD
|
||
: #S ( ud -- 0 0 ) L: # over over OR if L1 drop jump L L1: drop ;
|
||
: #> ( ud -- baddr u ) drop drop HLD b! @b HEND over push inv pop + inv ;
|
||
```
|
||
|
||
`#` divides the double by the base in two `UM/MOD` steps, the high cell first and its remainder
|
||
leading the low cell; the last remainder is the digit. `HOLD`, `SIGN` and `#` end with a jump to the
|
||
next word rather than a call, so `C!` returns straight to their caller and two return-stack entries
|
||
are saved. `#` keeps the high quotient under the second division on the data stack, not on the
|
||
return stack. `-ROT` is in line (`SWAP push SWAP pop`).
|
||
|
||
```forth
|
||
\ number output. ABS is in line: -if A inv 1 + A:
|
||
: .R ( n width -- )
|
||
(W) b! !b dup push ABS 0 <# #S pop SIGN #> \ baddr u
|
||
TAIL: (W) b! @b -if POS drop jump OUT \ width < 0
|
||
POS: over - SPACES \ width - u spaces
|
||
OUT: TYPE jump SPACE
|
||
: . ( n -- ) 0 jump .R
|
||
: U.R ( u width -- ) (W) b! !b 0 <# #S #> jump TAIL
|
||
: U. ( u -- ) 0 jump U.R
|
||
: D.R ( d width -- ) (W) b! !b dup push -if A DNEGATE A: <# #S pop SIGN #> jump TAIL
|
||
: D. ( d -- ) 0 jump D.R
|
||
```
|
||
|
||
```forth
|
||
\ DUMP. DIGITS is two per byte of a cell.
|
||
: (DB) ( -- c ) (DP) 1 + b! @b (DP) 3 + b! @b + jump C@ \ the byte in this column
|
||
: DUMP ( baddr u -- )
|
||
-if OK drop drop NODE-ERROR b! -1 !b ; \ u < 0
|
||
OK: (DP) 2 + b! !b (DP) 1 + b! !b \ count, address
|
||
BASE b! @b (DP) b! !b 16 BASE b! !b \ hex
|
||
LINE: (DP) 2 + b! @b if DONE drop
|
||
(DP) 1 + b! @b 0 <# DIGITS (DP) 3 + b! !b \ the address
|
||
AD: # (DP) 3 + b! @b -1 + dup !b if ADX drop jump AD
|
||
ADX: drop #> TYPE 58 EMIT SPACE
|
||
0 (DP) 3 + b! !b \ the bytes in hex
|
||
HX: (DP) 2 + b! @b (DP) 3 + b! @b inv + -if HAVE \ count - column - 1
|
||
drop SPACE SPACE SPACE jump HN
|
||
HAVE: drop (DB) 0 <# # # #> TYPE SPACE
|
||
HN: (DP) 3 + b! @b 1 + dup !b -16 + -if HXX drop jump HX
|
||
HXX: drop SPACE 124 EMIT
|
||
0 (DP) 3 + b! !b \ the bytes as characters
|
||
CH: (DP) 2 + b! @b (DP) 3 + b! @b inv + -if HAVC jump CHX
|
||
HAVC: drop (DB)
|
||
dup -32 + -if GE drop drop 46 jump EM
|
||
GE: drop dup -127 + -if BIG drop jump EM
|
||
BIG: drop drop 46
|
||
EM: EMIT
|
||
(DP) 3 + b! @b 1 + dup !b -16 + -if CHX drop jump CH
|
||
CHX: drop 124 EMIT CR
|
||
(DP) 1 + b! @b 16 + !b \ next line
|
||
(DP) 2 + b! @b -16 + -if MORE jump DONE
|
||
MORE: !b jump LINE
|
||
DONE: drop (DP) b! @b BASE b! !b ;
|
||
```
|
||
|
||
Everything `DUMP` keeps between words — the address, the count, the column and the saved `BASE` — is
|
||
in the variable `(DP)`, so the stacks carry only what each picture and `TYPE` need, and no loop count
|
||
sits on the return stack across a call.
|
||
|
||
There is one picture for signed singles, one for unsigned and one for doubles; each plain word is
|
||
its `.R` word with a width of 0, entered by a jump, and all six share one tail, which ends in a jump
|
||
to `SPACE`. The width is tested for sign before `width - u`, which could wrap for a very negative
|
||
width and print a flood of spaces.
|
||
|
||
### 5.9 Strings, parsing, and input
|
||
|
||
| Word | Fate | Notes |
|
||
| --- | --- | --- |
|
||
| `COUNT` | CAP | `( baddr -- baddr+1 c )`: `dup C@ push 1 + pop`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 7 data cells and 6 return entries. Clobbers `A`. |
|
||
| `CMOVE` | CAP | `( src dst u -- )`, low byte first. See below. Nothing for `u = 0`; for `u < 0` nothing is written and `NODE-ERROR` is set, where v3 raised its error flag. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 5 data cells and 5 return entries. |
|
||
| `CMOVE>` | CAP | `( src dst u -- )`, high byte first. See below. Errors as `CMOVE`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. |
|
||
| `BLANK` | CAP | `32 FILL`, as `32 jump FILL`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. v3's counted-string auto-detection is not kept. |
|
||
| `-TRAILING` | CAP | `( baddr u -- baddr u' )`. See below. A negative count reads as 0, as v3. v3's counted-string auto-detection is not kept. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 6 return entries. |
|
||
| `COMPARE` | CAP | `( a1 u1 a2 u2 -- n )`: `-1`, `0` or `1` by the first differing byte, unsigned, else the shorter string is less. See below. Negative counts read as 0, as v3. v3's counted-string auto-detection is not kept. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. Clobbers `A` and `B`. |
|
||
| `SEARCH` | CAP | `( a1 u1 a2 u2 -- a3 u3 flag )`: the rest of string 1 from the first place string 2 occurs and `-1`, or string 1 and `0`; an empty string 2 is found at the start. See below; the inner comparison is in line, not a call to `COMPARE`. Same notes. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 3 data cells and 5 return entries. Clobbers `A` and `B`. |
|
||
| `SCAN` `SKIP` | CAP | `( baddr u c -- baddr' u' )`: `SCAN` stops at the first byte equal to the low byte of `c` (or at the end, with `u' = 0`); `SKIP` stops at the first byte not equal to it. See below. A negative count reads as 0, as v3. v3's counted-string auto-detection is not kept. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Each leaves its caller 5 data cells and 5 return entries. Clobber `A`. |
|
||
| `(S)` | CAP | Variable, 4 cells: `COMPARE`'s two lengths; `SEARCH`'s string 2 and the whole of string 1. |
|
||
| `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
|
||
\ SWAP and the subtractions ( - ) are in line.
|
||
: CMOVE ( src dst u -- )
|
||
-if OK drop drop drop NODE-ERROR b! -1 !b ; \ u < 0
|
||
OK: if DONE push \ src dst R: u
|
||
over C@ over C! 1 + push 1 + pop pop -1 + jump OK
|
||
DONE: drop drop drop ;
|
||
|
||
: CMOVE> ( src dst u -- )
|
||
-if OK drop drop drop NODE-ERROR b! -1 !b ; \ u < 0
|
||
OK: if DONE -1 + push \ src dst R: i = u - 1
|
||
over pop dup push + C@ \ src dst c
|
||
over pop dup push + C! \ src dst
|
||
pop jump OK
|
||
DONE: drop drop drop ;
|
||
|
||
: -TRAILING ( baddr u -- baddr u' )
|
||
-if L drop 0 ; \ u < 0
|
||
L: if DONE over over + -1 + C@ -32 + if SP drop ; \ not a space
|
||
SP: drop -1 + jump L
|
||
DONE: ;
|
||
|
||
: COMPARE ( a1 u1 a2 u2 -- n )
|
||
-if A drop 0 A: (S) 1 + b! !b \ a1 u1 a2
|
||
SWAP -if B drop 0 B: dup (S) b! !b \ a1 a2 u1
|
||
(S) 1 + b! @b \ a1 a2 u1 u2
|
||
over over - -if GE drop drop jump M \ the smaller
|
||
GE: drop push drop pop
|
||
M: \ a1 a2 m
|
||
L: if EQ push \ a1 a2 R: m
|
||
over C@ over C@ - if SAME \ c1 - c2
|
||
-if GT drop drop drop pop drop -1 ;
|
||
GT: drop drop drop pop drop 1 ;
|
||
SAME: drop 1 + push 1 + pop pop -1 + jump L
|
||
EQ: drop drop drop (S) b! @b (S) 1 + b! @b - \ u1 - u2
|
||
-if NN drop -1 ;
|
||
NN: if ZZ drop 1 ;
|
||
ZZ: ;
|
||
|
||
: SEARCH ( a1 u1 a2 u2 -- a3 u3 flag )
|
||
-if A drop 0 A: (S) 1 + b! !b (S) b! !b \ a1 u1 (S): a2 u2
|
||
-if B drop 0 B: dup (S) 3 + b! !b over (S) 2 + b! !b \ a u (S)+2: a1 u1
|
||
L: dup (S) 1 + b! @b - -if TRY \ u - u2
|
||
drop drop drop (S) 2 + b! @b (S) 3 + b! @b 0 ; \ not found
|
||
TRY: drop over (S) b! @b (S) 1 + b! @b \ a u p q k
|
||
I: if MATCH push \ a u p q R: k
|
||
over C@ over C@ xor if SAME
|
||
drop drop drop pop drop push 1 + pop -1 + jump L \ next place
|
||
SAME: drop 1 + push 1 + pop pop -1 + jump I
|
||
MATCH: drop drop drop -1 ;
|
||
|
||
: SCAN ( baddr u c -- baddr' u' )
|
||
255 and push -if L drop 0 \ baddr u R: c
|
||
L: if DONE over C@ pop dup push xor if FOUND
|
||
drop push 1 + pop -1 + jump L
|
||
FOUND: drop
|
||
DONE: pop drop ;
|
||
|
||
: SKIP ( baddr u c -- baddr' u' )
|
||
255 and push -if L drop 0 \ baddr u R: c
|
||
L: if DONE over C@ pop dup push xor if SAME drop jump DONE
|
||
SAME: drop push 1 + pop -1 + jump L
|
||
DONE: pop drop ;
|
||
```
|
||
|
||
### 5.10 Terminal I/O
|
||
|
||
| Word | Fate | Notes |
|
||
| --- | --- | --- |
|
||
| `EMIT` | DEV | Console service: one-character message. Until the mesh exists it is a store to the `CONSOLE-TX` register (§7): `CONSOLE-TX b! !b`. As in v3, the low byte of the cell is the character. Executed on the golden model (2026-10-03). Clobbers `B`. |
|
||
| `KEY` | DEV | Console service: blocking receive. Until the mesh exists it reads the `CONSOLE-STATUS` and `CONSOLE-RX` registers (§7): `L: CONSOLE-STATUS b! @b if WAIT drop CONSOLE-RX b! @b ; WAIT: drop jump L` — it asks whether a character is pending until one is, then takes it. The character is 0–255. v3's `KEY` returned −1 at the end of its input; here there is no end of input, and `KEY` waits. Executed on the golden model (2026-10-03), including a transcript of the v3 binary, and shown still waiting after 5000 instruction words with nothing pending. Leaves its caller 9 data cells and 8 return entries. Clobbers `B`. |
|
||
| `?TERMINAL` | DEV | Console service: non-blocking status. Until the mesh exists: `CONSOLE-STATUS b! @b` — −1 when a character is pending, 0 when not, as v3; it takes nothing. Executed on the golden model (2026-10-03). Clobbers `B`. |
|
||
| `TYPE` | CAP | `( baddr u -- )`. Loop of `C@ EMIT`, or one string message to the console node: `-if OK drop drop NODE-ERROR b! -1 !b ; OK: if DONE over C@ EMIT push 1 + pop -1 + jump OK DONE: drop drop ;` — nothing for `u = 0`; for `u < 0` nothing is printed and `NODE-ERROR` is set, where v3 printed nothing and raised its error flag. The address range is not checked (out-of-range addressing is still open, `node.h`). Executed on the golden model (2026-10-03). Leaves its caller 6 data cells and 6 return entries. Clobbers `A` and `B`. |
|
||
| `CR` | CAP | `10 EMIT`, as `10 jump EMIT` — executed on the golden model (2026-10-03). Character 10, as v3; the console turns it into a new line. |
|
||
| `SPACE` | CAP | `BL EMIT`, as `32 jump EMIT` — executed on the golden model (2026-10-03). |
|
||
| `SPACES` | CAP | `( n -- )`: `-if L drop ; L: if DONE SPACE -1 + jump L DONE: drop ;` — `n` spaces, none for `n <= 0`, as v3. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 7 data cells and 7 return entries. Clobbers `B`. |
|
||
| `."` `(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) — executed on the golden model (2026-10-03) as `IF 1 ELSE 2 THEN`, including results recorded from the v3 binary. |
|
||
| `(DO)` | IN | `over push push drop` (R: limit index) — `SWAP push push` without the `SWAP`. The body always runs once, as v3. Executed on the golden model (2026-10-03). |
|
||
| `(?DO)` | IN | `over over xor if SKIP drop (DO) … body and loop end … jump PAST SKIP: drop drop drop PAST:` — skips the loop when index = limit, as v3. Executed on the golden model (2026-10-03). |
|
||
| `(LOOP)` | IN | See below. Adds 1 and goes round again while index < limit, signed, as v3. Executed on the golden model (2026-10-03) against C on every pair of 14 loop ends, and sequences recorded from the v3 binary. |
|
||
| `(+LOOP)` | IN | See below. `( n -- )`: adds `n` and goes round again while index < limit for `n >= 0`, while index >= limit for `n < 0`, as v3. Executed on the golden model (2026-10-03) against C on every pair of 14 loop ends with 11 steps, and sequences recorded from the v3 binary. Clobbers `A`. |
|
||
| `(LEAVE)` | IN | `pop drop pop drop jump PAST` — discards the limit and index and leaves the loop at once. In a single loop this is what v3 does. v3 left the two on its return stack (index set to limit) and jumped past the loop, so a `LEAVE` in an inner loop made the outer `LOOP` count the inner loop's leftovers and stop: `: T 3 0 DO 10 0 DO I . I 1 = IF LEAVE THEN LOOP I . LOOP ;` prints `0 1 10` in v3 and `0 1 0 0 1 1 0 1 2` here. Executed on the golden model (2026-10-03) in `LOOP` and `+LOOP`, including sequences recorded from the v3 binary. |
|
||
| `I` | IN | `pop dup push` — executed on the golden model (2026-10-03). |
|
||
| `J` | IN | `pop pop pop dup push a! push push a` — the outer index waits in `A` while the inner pair goes back, so `J` needs no return entry beyond the two loops' own (with two in-line `SWAP`s it needed one more). Executed on the golden model (2026-10-03), including a sequence recorded from the v3 binary. Clobbers `A`. |
|
||
| `UNLOOP` | IN | `pop drop pop drop` — executed on the golden model (2026-10-03) as `UNLOOP EXIT`, including a sequence recorded from the v3 binary. |
|
||
|
||
```
|
||
<less>, used by both loop ends: ( i' lim -- i' lim s ), the top bit of s set
|
||
exactly when i' < lim, signed -- i' itself when the two differ in sign, else
|
||
i' - lim (the subtraction in line):
|
||
over over xor -if SAME
|
||
drop over jump T
|
||
SAME: drop over over -
|
||
T:
|
||
|
||
(LOOP) expansion:
|
||
pop 1 + pop \ index' limit
|
||
<less> \ index' limit s
|
||
-if Lexit \ not less: done
|
||
drop push push \ R: limit index'
|
||
jump Lbody
|
||
Lexit: drop drop drop
|
||
|
||
(+LOOP) expansion: ( n -- )
|
||
dup a! pop + pop \ index' limit A: n
|
||
<less> a xor \ top bit set: go round again
|
||
-if Lexit
|
||
drop push push
|
||
jump Lbody
|
||
Lexit: drop drop drop
|
||
```
|
||
|
||
`(LOOP)` goes round again while `index' < limit`, signed, which is v3's rule. So `0 5 DO … LOOP` runs
|
||
once, as in v3; a test for `index' = limit`, which this document had first, would run it round the whole
|
||
number circle. `(+LOOP)` goes round again exactly when `s` and `n` differ in sign: `index' < limit` for
|
||
`n >= 0` and the opposite for `n < 0`. A loop costs its word two return-stack entries while it runs.
|
||
|
||
**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 | `( q -- )`: the integer part, a point, the five digits `floor(frac * 100000 / 65536)`, then one space, as v3; always decimal, whatever `BASE` is, and `BASE` is put back. Signed (D-8): a negative value prints `-` and its magnitude (−1.5 prints `-1.50000`), where v3 printed it as a large unsigned number. `dup (QP) 3 + b! !b -if A DNEGATE A: (QP) 2 + b! !b dup (QP) 1 + b! !b BASE b! @b (QP) b! !b 10 BASE b! !b 65535 and 4 FOR dup 2* 2* + UNEXT 10 FOR 2/ UNEXT 0 <# # # # # # drop drop 46 HOLD (QP) 1 + b! @b (QP) 2 + b! @b dup push push a! 0 pop 15 FOR +* UNEXT drop drop a pop 15 FOR 2/ UNEXT HIMASK and #S (QP) 3 + b! @b SIGN #> (QP) b! @b BASE b! !b jump OUT` — `OUT` is the `TYPE jump SPACE` at the end of `.R`. The fraction is `frac * 3125 / 2048`, which stays inside a 32-bit cell where `frac * 100000` would not; times 3125 is times 5 five times. The integer part is `|q|` shifted right 16 as a double: its low cell by `Q.TO-INT`'s `+*` shift in line, its high cell by `2/` and `HIMASK`, which clears the top 16 bits. Executed on the golden model (2026-10-03) against a C reference, on every seventh fraction, and against a transcript of the v3 binary for non-negative values. Leaves its caller 4 data cells and 3 return entries. Clobbers `A` and `B`. |
|
||
| `(QP)` | CAP | Variable, 4 cells: `Q.PRINT`'s saved `BASE`, `|q|` and the sign. |
|
||
|
||
### 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), by `Q.SQRT` / `Q.LOG` outside their domain (D-12) and by `HOLD` on a bad character or a full buffer (D-13), cleared by writing 0. `VM-ERROR?` reads it. |
|
||
| `CONSOLE-TX` | W | Console transmit: a store sends the low 8 bits of the value as one character. This is what `EMIT` writes to. On the single-node golden model it is a capture register: the character is appended to a buffer on the node and memory is not written (`v4_node_console_attach`, `v4/include/v4/node.h`), so printing words can be run and their output compared with v3's. With the mesh (step 2) it becomes the port to the console node. |
|
||
| `CONSOLE-RX` | R | Console receive: a fetch gives the next pending character, 0–255, and takes it; with none pending it gives −1 and takes nothing. This is what `KEY` reads. On the single-node golden model the characters come from a queue the test feeds (`v4_node_console_input_attach`, `v4_node_console_feed`, `v4/include/v4/node.h`). Only the data fetches `@`, `@+` and `@b` see the register; instruction words and literals at the same address are read as memory. With the mesh (step 2) it becomes the port from the console node, and a read with nothing pending will block instead. |
|
||
| `CONSOLE-STATUS` | R | Console receive status: a fetch gives −1 when a character is pending and 0 when not, and changes nothing. `?TERMINAL` reads it, and `KEY` polls it. On the mesh it is the console port's bit of `PORT-STATUS`. |
|