Compare commits
3
Commits
6302dcb50e
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c709c115b | ||
|
|
357cb5b4ac | ||
|
|
2fcc468ecb |
@@ -0,0 +1,787 @@
|
|||||||
|
# StarForth Primitive Word Reference
|
||||||
|
|
||||||
|
This reference covers every **C-implemented primitive** word that StarForth registers. It was built from
|
||||||
|
`admin/LithosAnanake` at commit `6302dcb` (2026-09-23). It lists only words registered in C
|
||||||
|
through `register_word()` or `vm_create_word()`. Words defined in FORTH inside capsules (`*.4th`) are out of scope.
|
||||||
|
|
||||||
|
Sources:
|
||||||
|
|
||||||
|
- `src/word_registry.c`: `register_forth79_words()` registers the core set in every VM.
|
||||||
|
- `src/word_source/*.c`: one file per module.
|
||||||
|
- `src/starkernel/capsule/mama_forth_words.c`: kernel-only Hera (Mama) and child-VM words.
|
||||||
|
- `src/starkernel/repl.c` and `src/starkernel/doe_log.c`: kernel-only REPL and DoE words.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
| Item | Meaning |
|
||||||
|
|---|---|
|
||||||
|
| Cell | `cell_t` is `int64_t`, so every cell is 64 bits and signed. |
|
||||||
|
| Flag | TRUE is `-1` (all bits set) and FALSE is `0`. Words that take a flag treat any non-zero value as true. |
|
||||||
|
| `addr` | A **VM address**: a byte offset into the VM's 5 MB linear memory (`VM_MEMORY_SIZE`), not a host pointer. |
|
||||||
|
| `c-addr u` | A string given as its address and length. |
|
||||||
|
| `d`, `ud` | A double-cell number made of two cells, with the **high cell on top**. |
|
||||||
|
| `xt` | An execution token. In StarForth this is the `DictEntry*` of the word. |
|
||||||
|
| `q` | A Q48.16 fixed-point value in one cell (`1.0` = `65536`). |
|
||||||
|
| `"name"` | The word parses a name from the input stream after it. |
|
||||||
|
| `( R: ... )` | The effect on the return stack. |
|
||||||
|
| **IMM** | The word is IMMEDIATE, so it runs even while compiling. |
|
||||||
|
| **CO** | The word is compile-only and sets `vm->error` if used outside a definition. |
|
||||||
|
| **K** | The word is registered only in the kernel build (`__STARKERNEL__`). |
|
||||||
|
| **H** | The word is registered only in the hosted build (the Linux or macOS binary). |
|
||||||
|
|
||||||
|
Errors: a primitive does not throw. On stack underflow or overflow, a bad address, or division by zero it sets
|
||||||
|
`vm->error = 1` and logs a message.
|
||||||
|
|
||||||
|
Stack limits: the data stack and return stack hold 1024 cells each (`STACK_SIZE`). A word name can be at most 31
|
||||||
|
characters (`WORD_NAME_MAX`).
|
||||||
|
|
||||||
|
Shadowing: when a later module registers a name again, the newer entry wins lookups. For example, `MOD`, `/MOD`,
|
||||||
|
`*/` and `*/MOD` are registered by the arithmetic module and again by the mixed-arithmetic module, so the
|
||||||
|
mixed-arithmetic versions are the active ones.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
1. [Stack](#1-stack)
|
||||||
|
2. [Return stack](#2-return-stack)
|
||||||
|
3. [Memory](#3-memory)
|
||||||
|
4. [Arithmetic](#4-arithmetic)
|
||||||
|
5. [Logic and comparison](#5-logic-and-comparison)
|
||||||
|
6. [Mixed-precision arithmetic](#6-mixed-precision-arithmetic)
|
||||||
|
7. [Double-cell numbers](#7-double-cell-numbers)
|
||||||
|
8. [Number formatting and output](#8-number-formatting-and-output)
|
||||||
|
9. [Strings, parsing, and input](#9-strings-parsing-and-input)
|
||||||
|
10. [Terminal I/O](#10-terminal-io)
|
||||||
|
11. [Blocks and mass storage](#11-blocks-and-mass-storage)
|
||||||
|
12. [Dictionary space](#12-dictionary-space)
|
||||||
|
13. [Dictionary manipulation](#13-dictionary-manipulation)
|
||||||
|
14. [Vocabularies](#14-vocabularies)
|
||||||
|
15. [System](#15-system)
|
||||||
|
16. [Line editor](#16-line-editor)
|
||||||
|
17. [Defining words and the compiler](#17-defining-words-and-the-compiler)
|
||||||
|
18. [Control flow](#18-control-flow)
|
||||||
|
19. [StarForth extensions](#19-starforth-extensions)
|
||||||
|
20. [Word-level ACL](#20-word-level-acl)
|
||||||
|
21. [Physics: benchmark and diagnostics](#21-physics-benchmark-and-diagnostics)
|
||||||
|
22. [Physics: pipelining diagnostics](#22-physics-pipelining-diagnostics)
|
||||||
|
23. [Physics: freeze, heat, and decay](#23-physics-freeze-heat-and-decay)
|
||||||
|
24. [Dictionary heat optimisation](#24-dictionary-heat-optimisation)
|
||||||
|
25. [Logging](#25-logging)
|
||||||
|
26. [Q48.16 fixed-point math](#26-q4816-fixed-point-math)
|
||||||
|
27. [Inference engine (SSM, L8, and Bayes)](#27-inference-engine-ssm-l8-and-bayes)
|
||||||
|
28. [DEFER and IS](#28-defer-and-is)
|
||||||
|
29. [Framebuffer (Hestia only)](#29-framebuffer-hestia-only)
|
||||||
|
30. [Keyboard](#30-keyboard)
|
||||||
|
31. [TrueType text](#31-truetype-text)
|
||||||
|
32. [REPL scrollback](#32-repl-scrollback)
|
||||||
|
33. [Kernel REPL and DoE hooks](#33-kernel-repl-and-doe-hooks)
|
||||||
|
34. [Hera (Mama) and child-VM words](#34-hera-mama-and-child-vm-words)
|
||||||
|
35. [Hosted lifecycle stubs](#35-hosted-lifecycle-stubs)
|
||||||
|
36. [Implementation quirks to know](#36-implementation-quirks-to-know)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Stack
|
||||||
|
`src/word_source/stack_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `DROP` | `( x -- )` | Discards the top cell. |
|
||||||
|
| `DUP` | `( x -- x x )` | Copies the top cell. |
|
||||||
|
| `?DUP` | `( x -- x x \| 0 -- 0 )` | Copies the top cell only when it is non-zero. The usual idiom is `?DUP IF ... THEN`. |
|
||||||
|
| `SWAP` | `( x1 x2 -- x2 x1 )` | Swaps the top two cells. |
|
||||||
|
| `OVER` | `( x1 x2 -- x1 x2 x1 )` | Copies the second cell to the top. |
|
||||||
|
| `ROT` | `( x1 x2 x3 -- x2 x3 x1 )` | Moves the third cell to the top. |
|
||||||
|
| `-ROT` | `( x1 x2 x3 -- x3 x1 x2 )` | Moves the top cell down to third place. This is the reverse of `ROT`. |
|
||||||
|
| `DEPTH` | `( -- n )` | Pushes the number of cells that were on the data stack before `DEPTH` ran. |
|
||||||
|
| `PICK` | `( xn … x0 n -- xn … x0 xn )` | Copies the n-th cell to the top. **0-based:** `0 PICK` is `DUP` and `1 PICK` is `OVER`. An error occurs if `n < 0` or `n ≥ depth`. |
|
||||||
|
| `ROLL` | `( … n -- … )` | Moves a cell to the top and closes the gap. **Non-standard:** `n` counts from the *bottom* of the stack (1-based), so `1 ROLL` moves the deepest cell to the top. `0 ROLL` does nothing. See [§36](#36-implementation-quirks-to-know). |
|
||||||
|
|
||||||
|
## 2. Return stack
|
||||||
|
`src/word_source/return_stack_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `>R` | `( x -- ) ( R: -- x )` | Moves a cell from the data stack to the return stack. Inside a definition, balance it with `R>` before `;` or `EXIT`. |
|
||||||
|
| `R>` | `( -- x ) ( R: x -- )` | Moves a cell from the return stack back to the data stack. |
|
||||||
|
| `R@` | `( -- x ) ( R: x -- x )` | Copies the top of the return stack without removing it. |
|
||||||
|
|
||||||
|
## 3. Memory
|
||||||
|
`src/word_source/memory_words.c`. Every address is a VM byte offset and is checked against the VM's memory bounds.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `@` | `( addr -- x )` | Fetches the cell at `addr`. |
|
||||||
|
| `!` | `( x addr -- )` | Stores `x` at `addr`. |
|
||||||
|
| `C@` | `( addr -- c )` | Fetches the byte at `addr`, zero-extended. |
|
||||||
|
| `C!` | `( c addr -- )` | Stores the low 8 bits of `c` at `addr`. |
|
||||||
|
| `+!` | `( n addr -- )` | Adds `n` to the cell at `addr`. |
|
||||||
|
| `-!` | `( n addr -- )` | Subtracts `n` from the cell at `addr`. |
|
||||||
|
| `2@` | `( addr -- x-lo x-hi )` | Fetches two cells: the low cell from `addr` and the high cell from `addr+8`, leaving the high cell on top. |
|
||||||
|
| `2!` | `( x-lo x-hi addr -- )` | Stores two cells: the low cell at `addr` and the high cell at `addr+8`. |
|
||||||
|
| `FILL` | `( addr u c -- )` | Fills `u` bytes starting at `addr` with the byte `c`. |
|
||||||
|
| `MOVE` | `( src dst u -- )` | Copies `u` bytes from `src` to `dst`. It is safe when the ranges overlap because it uses memmove semantics. |
|
||||||
|
| `ERASE` | `( addr u -- )` | Sets `u` bytes to zero. |
|
||||||
|
| `CELLS` | `( n -- n*8 )` | Scales a cell count to a byte count. |
|
||||||
|
|
||||||
|
## 4. Arithmetic
|
||||||
|
`src/word_source/arithmetic_words.c`. All arithmetic is signed 64-bit, and division truncates toward zero as in C.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `+` | `( n1 n2 -- n1+n2 )` | Adds the two cells. |
|
||||||
|
| `-` | `( n1 n2 -- n1-n2 )` | Subtracts `n2` from `n1`. |
|
||||||
|
| `*` | `( n1 n2 -- n1*n2 )` | Multiplies the two cells. The result wraps modulo 2⁶⁴. |
|
||||||
|
| `/` | `( n1 n2 -- n1/n2 )` | Divides, truncating toward zero. Division by zero sets an error. |
|
||||||
|
| `MOD` | `( n1 n2 -- rem )` | Pushes the remainder of `n1 / n2`, which has the sign of `n1`. This name is shadowed by §6. |
|
||||||
|
| `/MOD` | `( n1 n2 -- rem quot )` | Pushes the remainder and the quotient, with the quotient on top. This name is shadowed by §6. |
|
||||||
|
| `*/` | `( n1 n2 n3 -- n1*n2/n3 )` | Multiplies and then divides using a wide intermediate. This name is shadowed by §6. |
|
||||||
|
| `*/MOD` | `( n1 n2 n3 -- rem quot )` | Like `*/`, but also leaves the remainder. This name is shadowed by §6. |
|
||||||
|
| `1+` `1-` | `( n -- n±1 )` | Increments or decrements by 1. |
|
||||||
|
| `2+` `2-` | `( n -- n±2 )` | Adds or subtracts 2. |
|
||||||
|
| `2*` | `( n -- n*2 )` | Shifts left by one bit. |
|
||||||
|
| `2/` | `( n -- n/2 )` | Shifts right by one bit, keeping the sign (arithmetic shift). |
|
||||||
|
| `ABS` | `( n -- \|n\| )` | Pushes the absolute value. |
|
||||||
|
| `NEGATE` | `( n -- -n )` | Pushes the two's-complement negation. |
|
||||||
|
| `MIN` `MAX` | `( n1 n2 -- n3 )` | Pushes the smaller or the larger value, compared as signed numbers. |
|
||||||
|
|
||||||
|
## 5. Logic and comparison
|
||||||
|
`src/word_source/logical_words.c`. Comparison words return a proper flag of `-1` or `0`.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `AND` `OR` `XOR` | `( x1 x2 -- x3 )` | Bitwise AND, OR, and XOR. |
|
||||||
|
| `NOT` | `( x -- flag )` | **FORTH-79 logical NOT:** `0` gives `TRUE` and any other value gives `FALSE`. This is *not* a bitwise complement; use `INVERT` for that. |
|
||||||
|
| `INVERT` | `( x -- ~x )` | Bitwise complement (from FORTH-83). |
|
||||||
|
| `LSHIFT` | `( x u -- x<<u )` | Logical shift left by `u` bits. |
|
||||||
|
| `RSHIFT` | `( x u -- x>>u )` | Logical (unsigned) shift right by `u` bits. |
|
||||||
|
| `0=` | `( n -- flag )` | True if `n` is 0. |
|
||||||
|
| `0<` | `( n -- flag )` | True if `n` is negative. |
|
||||||
|
| `0>` | `( n -- flag )` | True if `n` is positive. |
|
||||||
|
| `0<>` | `( n -- flag )` | True if `n` is not 0. |
|
||||||
|
| `=` `<>` | `( n1 n2 -- flag )` | Tests for equality or inequality. |
|
||||||
|
| `<` `>` `<=` `>=` | `( n1 n2 -- flag )` | Signed comparisons of `n1` against `n2`. |
|
||||||
|
| `U<` `U>` | `( u1 u2 -- flag )` | Unsigned comparisons. |
|
||||||
|
| `WITHIN` | `( n lo hi -- flag )` | True if `lo ≤ n < hi`, using the standard half-open range. |
|
||||||
|
| `TRUE` | `( -- -1 )` | Pushes the canonical true flag. |
|
||||||
|
| `FALSE` | `( -- 0 )` | Pushes the canonical false flag. |
|
||||||
|
|
||||||
|
## 6. Mixed-precision arithmetic
|
||||||
|
`src/word_source/mixed_arithmetic_words.c`. A double here means two full 64-bit cells (128 bits).
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `M+` | `( d n -- d' )` | Adds a signed single to a double and propagates the carry into the high cell. |
|
||||||
|
| `M-` | `( d n -- d' )` | Subtracts a signed single from a double and propagates the borrow. |
|
||||||
|
| `M*` | `( n1 n2 -- d )` | Multiplies 64×64 into a full 128-bit signed product, using `__int128` internally. The result can be printed with `D.`. |
|
||||||
|
| `M/MOD` | `( d n -- rem quot )` | Divides a 128-bit double by a single, leaving the quotient on top. It uses bit-serial long division, so it works in the freestanding kernel without libgcc. |
|
||||||
|
| `MOD` | `( n1 n2 -- rem )` | **Active version.** Computes `n1 % n2`. Division by zero sets an error. |
|
||||||
|
| `/MOD` | `( n1 n2 -- rem quot )` | **Active version.** Leaves the remainder under the quotient. Division by zero sets an error. |
|
||||||
|
| `*/` | `( n1 n2 n3 -- n4 )` | **Active version.** Computes `(n1*n2)/n3` with a wide intermediate, so `n1*n2` does not overflow. Division by zero sets an error. Typical use is scaling, for example `x 355 113 */`. |
|
||||||
|
| `*/MOD` | `( n1 n2 n3 -- rem quot )` | **Active version.** Like `*/`, but also leaves the remainder under the quotient. |
|
||||||
|
|
||||||
|
## 7. Double-cell numbers
|
||||||
|
`src/word_source/double_words.c`. In every stack picture, `d` stands for the pair `( lo hi )` with the high cell on top.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `S>D` | `( n -- d )` | Sign-extends a single to a double. |
|
||||||
|
| `D+` `D-` | `( d1 d2 -- d3 )` | Double add and subtract, with carry or borrow. |
|
||||||
|
| `DNEGATE` | `( d -- -d )` | Negates a double. |
|
||||||
|
| `DABS` | `( d -- \|d\| )` | Pushes the absolute value of a double. |
|
||||||
|
| `DMAX` `DMIN` | `( d1 d2 -- d3 )` | Pushes the larger or smaller double, compared as signed values. |
|
||||||
|
| `D<` | `( d1 d2 -- flag )` | Signed less-than on doubles. |
|
||||||
|
| `D=` | `( d1 d2 -- flag )` | Equality on doubles. |
|
||||||
|
| `D0=` | `( d -- flag )` | True if the double is zero. |
|
||||||
|
| `D0<` | `( d -- flag )` | True if the double is negative. |
|
||||||
|
| `D2*` | `( d -- d*2 )` | Shifts a double left by one bit across both cells. |
|
||||||
|
| `D2/` | `( d -- d/2 )` | Shifts a double right by one bit (arithmetic shift) across both cells. |
|
||||||
|
| `2DROP` | `( x1 x2 -- )` | Drops a cell pair. |
|
||||||
|
| `2DUP` | `( x1 x2 -- x1 x2 x1 x2 )` | Duplicates a cell pair. |
|
||||||
|
| `2SWAP` | `( p1 p2 -- p2 p1 )` | Swaps two cell pairs. |
|
||||||
|
| `2OVER` | `( p1 p2 -- p1 p2 p1 )` | Copies the second pair to the top. |
|
||||||
|
| `2ROT` | `( p1 p2 p3 -- p2 p3 p1 )` | Rotates three cell pairs. |
|
||||||
|
| `2>R` | `( x1 x2 -- ) ( R: -- x1 x2 )` | Moves a pair to the return stack. |
|
||||||
|
| `2R>` | `( -- x1 x2 ) ( R: x1 x2 -- )` | Moves a pair back from the return stack. |
|
||||||
|
| `2R@` | `( -- x1 x2 ) ( R: x1 x2 -- x1 x2 )` | Copies a pair from the return stack. |
|
||||||
|
|
||||||
|
## 8. Number formatting and output
|
||||||
|
`src/word_source/format_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `.` | `( n -- )` | Prints a signed number in the current `BASE`, followed by a space. |
|
||||||
|
| `.R` | `( n width -- )` | Prints a signed number right-aligned in a field `width` characters wide. |
|
||||||
|
| `U.` | `( u -- )` | Prints an unsigned number followed by a space. |
|
||||||
|
| `U.R` | `( u width -- )` | Prints an unsigned number right-aligned. |
|
||||||
|
| `D.` | `( d -- )` | Prints a signed double. |
|
||||||
|
| `D.R` | `( d width -- )` | Prints a signed double right-aligned. |
|
||||||
|
| `.S` | `( -- )` | Prints the data stack without changing it. This is the main debugging aid. |
|
||||||
|
| `?` | `( addr -- )` | Prints the cell at `addr`; it is the same as `@ .`. |
|
||||||
|
| `DUMP` | `( addr u -- )` | Prints a hex and ASCII dump of `u` bytes starting at `addr`. |
|
||||||
|
| `<#` | `( -- )` | Starts pictured numeric output by resetting the conversion buffer. |
|
||||||
|
| `#` | `( ud -- ud' )` | Converts one digit (`ud mod BASE`) into the buffer. It also accepts a single signed cell and converts its magnitude. |
|
||||||
|
| `#S` | `( ud -- 0 0 )` | Converts digits until the value is zero, always producing at least one digit. It accepts a single cell the same way `#` does. |
|
||||||
|
| `HOLD` | `( c -- )` | Inserts the character `c` into the pictured output buffer. |
|
||||||
|
| `SIGN` | `( n -- )` | Inserts `-` if `n` is negative. |
|
||||||
|
| `#>` | `( ud -- c-addr u )` | Ends conversion and leaves the string. It is tolerant: it pops `ud` only if one is present. |
|
||||||
|
| `BASE` | `( -- addr )` | Pushes the address of the number-conversion radix variable. |
|
||||||
|
| `DECIMAL` `HEX` `OCTAL` | `( -- )` | Sets `BASE` to 10, 16, or 8. |
|
||||||
|
|
||||||
|
Example: `: .$ ( n -- ) <# # # 46 HOLD #S #> TYPE ;` prints `1234` as `12.34`.
|
||||||
|
|
||||||
|
## 9. Strings, parsing, and input
|
||||||
|
`src/word_source/string_words.c`. The comparison and search words below also accept a counted string in place of an
|
||||||
|
`addr u` pair; they detect it when the first byte at `addr` equals `u`.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `COUNT` | `( c-addr1 -- c-addr2 u )` | Converts a counted string (length byte followed by characters) into an address and length. |
|
||||||
|
| `EXPECT` | `( addr u -- )` | Reads up to `u` characters from the terminal into `addr` and stores the count read in `SPAN`. |
|
||||||
|
| `SPAN` | `( -- addr )` | Pushes the address of the variable that holds the count from the last `EXPECT`. |
|
||||||
|
| `QUERY` | `( -- )` | Reads a line into `TIB` and resets `>IN`. |
|
||||||
|
| `TIB` | `( -- addr )` | Pushes the address of the terminal input buffer. |
|
||||||
|
| `>IN` | `( -- addr )` | Pushes the address of the offset into the current input source. |
|
||||||
|
| `SOURCE` | `( -- addr u )` | Pushes the current input buffer and its length. |
|
||||||
|
| `WORD` | `( c -- c-addr )` | Skips leading `c` characters, parses up to the next `c`, and returns a counted string. The usual form is `BL WORD`. |
|
||||||
|
| `BL` | `( -- 32 )` | Pushes the ASCII space character. |
|
||||||
|
| `S"` **IMM** | `( "ccc<">" -- c-addr u )` | In interpret mode, stores the string at `HERE` and pushes it. When compiling, it compiles `(s")` followed by the inline text. |
|
||||||
|
| `(s")` | `( -- c-addr u )` | Runtime for a compiled `S"`: reads the inline `[len][chars][pad]` block and skips the IP past it. The compiler inserts it; you do not call it directly. |
|
||||||
|
| `[']` **IMM** | `( "name" -- xt )` | While compiling, compiles the xt of `name` as a literal. In interpret mode it behaves like `'`. |
|
||||||
|
| `LITERAL` `[LITERAL]` | – | Placeholders that do nothing. `LITERAL` is re-registered in §17 (the working version); `[LITERAL]` has no replacement and still does nothing. |
|
||||||
|
| `CONVERT` | `( d1 addr1 -- d2 addr2 )` | Accumulates the digits at `addr1+1…` into `d1` and stops at the first non-digit. This is a simplified version. |
|
||||||
|
| `NUMBER` | `( c-addr -- n flag )` | Converts a counted string to a number. Only base 10 is supported, and `flag` shows whether it succeeded. |
|
||||||
|
| `ENCLOSE` | `( addr c -- addr n1 n2 n3 )` | The classic FIG parser: gives the offsets of the start of the token, the delimiter after it, and the next character. |
|
||||||
|
| `-TRAILING` | `( addr u -- addr u' )` | Removes trailing spaces from the length. |
|
||||||
|
| `CMOVE` | `( src dst u -- )` | Copies bytes upward from low to high addresses. It is safe for overlapping ranges when `dst ≤ src`. |
|
||||||
|
| `CMOVE>` | `( src dst u -- )` | Copies bytes downward from high to low addresses. It is safe for overlapping ranges when `dst > src`. |
|
||||||
|
| `COMPARE` | `( a1 u1 a2 u2 -- n )` | Compares two strings case-sensitively and returns `-1`, `0`, or `1`. |
|
||||||
|
| `SEARCH` | `( a1 u1 a2 u2 -- a3 u3 flag )` | Finds string 2 inside string 1. If found, it returns the tail starting at the match and `-1`. If not, it returns string 1 unchanged and `0`. |
|
||||||
|
| `SCAN` | `( addr u c -- addr' u' )` | Advances to the first occurrence of `c`. If there is none, it returns the end of the string and `0`. |
|
||||||
|
| `SKIP` | `( addr u c -- addr' u' )` | Skips leading occurrences of `c`. |
|
||||||
|
| `BLANK` | `( addr u -- )` | Fills `u` bytes with spaces. |
|
||||||
|
|
||||||
|
## 10. Terminal I/O
|
||||||
|
`src/word_source/io_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `EMIT` | `( c -- )` | Prints one character. |
|
||||||
|
| `CR` | `( -- )` | Prints a newline. |
|
||||||
|
| `KEY` | `( -- c )` | Waits for a character and pushes it. |
|
||||||
|
| `?TERMINAL` | `( -- flag )` | True if a key is waiting. It does not block. |
|
||||||
|
| `TYPE` | `( c-addr u -- )` | Prints `u` characters. |
|
||||||
|
| `SPACE` | `( -- )` | Prints one space. |
|
||||||
|
| `SPACES` | `( n -- )` | Prints `n` spaces. |
|
||||||
|
| `."` **IMM** | `( "ccc<">" -- )` | In interpret mode, prints the string immediately. When compiling, it compiles `(do-string)` and the inline text. |
|
||||||
|
| `(do-string)` | `( -- )` | Runtime for a compiled `."`: prints the inline string and skips the IP past it. The compiler inserts it; you do not call it directly. |
|
||||||
|
|
||||||
|
## 11. Blocks and mass storage
|
||||||
|
`src/word_source/block_words.c`. A block is 1024 bytes, viewed as 16 lines of 64 characters. Block numbers are
|
||||||
|
LBNs (logical block numbers) in one address space that spans every attached device.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `BLOCK` | `( u -- addr )` | Returns the VM address of the buffer holding block `u`, reading it from disk if needed. It does not mark the buffer dirty. |
|
||||||
|
| `BUFFER` | `( u -- addr )` | Assigns a buffer to block `u` *without* reading from disk and marks it dirty. Use it when you will overwrite the whole block. |
|
||||||
|
| `UPDATE` | `( -- )` | Marks the current (`SCR`) block dirty and syncs it to the C block layer. |
|
||||||
|
| `SAVE-BUFFERS` | `( -- )` | Writes every dirty buffer to disk. |
|
||||||
|
| `EMPTY-BUFFERS` | `( -- )` | Discards every buffer **without** writing it and zeroes the user block window. |
|
||||||
|
| `FLUSH` | `( -- )` | Runs `SAVE-BUFFERS` and then invalidates all buffers. |
|
||||||
|
| `LOAD` | `( u -- )` | Sets `SCR` to `u` and interprets the 1024 bytes of block `u` as FORTH source. Block 0 cannot be loaded. |
|
||||||
|
| `THRU` | `( u1 u2 -- )` | Loads blocks `u1` through `u2`, including both ends. |
|
||||||
|
| `-->` | `( -- )` | Inside a block being loaded, continues interpreting at the next block. |
|
||||||
|
| `LIST` | `( u -- )` | Sets `SCR` to `u` and prints the block. |
|
||||||
|
| `SCR` | `( -- addr )` | Pushes the address of the variable holding the block number last listed or loaded. |
|
||||||
|
| `BLK-CONFIRM-FORMAT` | `( lbn -- )` | Commits the container format of the device that owns `lbn`. Until this has run, the block layer **refuses every write** to that device. Only the owner (for example Artemis) should call it, and only after checking the disk contents are safe to touch. |
|
||||||
|
| `RELOCATE-BLOCK` | `( home target -- )` | Moves the contents of `home` to `target` and redirects all later access to `home` through `target`. It is a mechanical primitive: it does not check whether `target` is free or owned by the caller. |
|
||||||
|
| `BLK-ACL-ALLOW@` | `( blk -- allow )` | Reads the cached allow/deny flag of a block's ACL. |
|
||||||
|
| `BLK-ACL-ALLOW!` | `( allow blk -- )` | Sets a block's cached allow/deny flag. |
|
||||||
|
| `BLK-ACL-TTL@` | `( blk -- ttl )` | Reads a block's ACL TTL countdown. |
|
||||||
|
| `BLK-ACL-TTL!` | `( ttl blk -- )` | Sets a block's ACL TTL countdown. |
|
||||||
|
| `BLK-OWNER@` | `( blk -- fp )` | Pushes the 8-byte owner fingerprint as the raw bits of one cell. There is no `BLK-OWNER!`: ownership is set only in C, during MINT or birth. |
|
||||||
|
| `BLK-ATTACH` | `( dev-ptr -- ok? )` | Registers an already-open `blkio_dev_t*`, passed as a raw pointer cell, in the unified LBN space. The pointer is trusted without checks. Artemis's USB-attach handler uses it. |
|
||||||
|
|
||||||
|
## 12. Dictionary space
|
||||||
|
`src/word_source/dictionary_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `HERE` | `( -- addr )` | Pushes the next free byte in the dictionary. |
|
||||||
|
| `ALIGN` | `( -- )` | Rounds `HERE` up to the next 8-byte cell boundary. |
|
||||||
|
| `ALLOT` | `( n -- )` | Reserves `n` bytes at `HERE`. A negative `n` gives space back. |
|
||||||
|
| `,` | `( x -- )` | Compiles one cell at `HERE` and advances `HERE`. |
|
||||||
|
| `C,` | `( c -- )` | Compiles one byte. |
|
||||||
|
| `2,` | `( x-lo x-hi -- )` | Compiles two cells, low cell first. |
|
||||||
|
| `PAD` | `( -- addr )` | Pushes the address of a 512-byte scratch buffer near the top of memory. It is safe for temporary strings. |
|
||||||
|
| `SP@` | `( -- n )` | Pushes the data stack pointer *index* (`dsp`). An empty stack gives `-1` and one item gives `0`. |
|
||||||
|
| `SP!` | `( n -- )` | Restores the stack pointer index. It can only shrink the stack, never grow it. |
|
||||||
|
| `LATEST` | `( -- addr )` | Pushes the address of the most recent definition. |
|
||||||
|
|
||||||
|
## 13. Dictionary manipulation
|
||||||
|
`src/word_source/dictionary_manipulation_words.c`. Header-field words work on raw header addresses. They exist for
|
||||||
|
FIG and FORTH-79 compatibility; take care with them.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `'` | `( "name" -- xt )` | Parses `name` and pushes its xt. It is not IMMEDIATE; inside a definition, use `[']`. |
|
||||||
|
| `FIND` | `( "name" -- xt \| 0 )` | **Parses from the input stream**, not from a counted string on the stack. It pushes the entry, or `0` if the word is not found (a miss is not an error). For a counted string already in memory, use `(FIND)` from §14. |
|
||||||
|
| `SMUDGE` | `( -- )` | **CO.** Toggles the smudge (hidden) bit on the latest word. |
|
||||||
|
| `HIDDEN` | `( -- )` | **CO.** Sets the latest word's hidden bit (unlike `SMUDGE`, it does not toggle). |
|
||||||
|
| `>BODY` | `( xt -- addr )` | Pushes the address of the parameter (data) field. |
|
||||||
|
| `>NAME` | `( xt -- nfa )` | Pushes the name field. |
|
||||||
|
| `NAME>` | `( nfa -- xt )` | Goes from the name field to the xt. |
|
||||||
|
| `>LINK` | `( xt -- lfa )` | Pushes the link field. |
|
||||||
|
| `LINK>` | `( lfa -- xt )` | Follows the link to the next (older) word. |
|
||||||
|
| `CFA` `LFA` `NFA` `PFA` | `( addr -- addr' )` | FIG-style field-address conversions (code, link, name, and parameter fields). |
|
||||||
|
| `TRAVERSE` | `( addr n -- addr' )` | Moves across a name field forward (`n=1`) or backward (`n=-1`). |
|
||||||
|
| `INTERPRET` | `( -- )` | Runs the text interpreter on the rest of the current input. |
|
||||||
|
|
||||||
|
## 14. Vocabularies
|
||||||
|
`src/word_source/vocabulary_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `VOCABULARY` | `( "name" -- )` | Creates a vocabulary. Running `name` later makes it the `CONTEXT` (search) vocabulary. |
|
||||||
|
| `DEFINITIONS` | `( -- )` | Sets `CURRENT` to `CONTEXT`, so new definitions go into the vocabulary being searched. |
|
||||||
|
| `CONTEXT` | `( -- addr )` | Pushes the address of the search-vocabulary pointer. |
|
||||||
|
| `CURRENT` | `( -- addr )` | Pushes the address of the definition-vocabulary pointer. |
|
||||||
|
| `FORTH` | `( -- )` | Makes the root `FORTH` vocabulary the context. |
|
||||||
|
| `ORDER` | `( -- )` | Prints the search order (`CONTEXT`, then `FORTH`) and `CURRENT`. |
|
||||||
|
| `(FIND)` | `( c-addr -- c-addr 0 \| xt 1 \| xt -1 )` | Looks up a counted string in `CONTEXT` and then in `FORTH`. It returns `1` for an IMMEDIATE word, `-1` for a normal word, and `0` if not found. |
|
||||||
|
|
||||||
|
Example: `VOCABULARY GRAPHICS GRAPHICS DEFINITIONS : BOX ... ; FORTH DEFINITIONS`
|
||||||
|
|
||||||
|
## 15. System
|
||||||
|
`src/word_source/system_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `(` **IMM** | `( "ccc<)>" -- )` | Starts a comment that runs to the closing `)`. |
|
||||||
|
| `\` **IMM** | `( "ccc<eol>" -- )` | Starts a comment that runs to the end of the line. |
|
||||||
|
| `EXECUTE` | `( xt -- )` | Runs the word identified by `xt`. |
|
||||||
|
| `NOP` | `( -- )` | Does nothing. |
|
||||||
|
| `QUIT` **IMM** | `( -- ) ( R: … -- )` | Clears the return stack and the error flag and returns to the outer interpreter. The data stack is kept. It is refused (sets an error) inside a definition. |
|
||||||
|
| `ABORT` | `( … -- )` | Clears both stacks and returns to the interpreter. It is **not** reported as an error. |
|
||||||
|
| `ABORT"` **IMM** | `( flag "ccc<">" -- )` | If `flag` is non-zero, prints the message and runs `ABORT`. It works in both interpret and compile mode. |
|
||||||
|
| `(ABORT")` | `( flag addr u -- )` | Runtime for a compiled `ABORT"`. The compiler inserts it; you do not call it directly. |
|
||||||
|
| `COLD` | `( -- )` | Clears both stacks and the error flag, returns to interpret mode, and moves `HERE` back to 1024 if it is higher. Dictionary headers are **not** removed; this is a minimal cold start. |
|
||||||
|
| `WARM` | `( -- )` | Clears both stacks and the error flag and returns to interpret mode. `HERE` and the dictionary are kept. |
|
||||||
|
| `BYE` | `( -- )` | Leaves this VM. In a child VM it halts the VM and returns to the parent's REPL. On Hera, the kernel's `BYE` from §34 takes precedence. |
|
||||||
|
| `REBOOT` | `( c-addr u -- )` | Sets the boot arguments and does a cold reset. Interpret mode only. |
|
||||||
|
| `SAVE-SYSTEM` | `( -- )` | Takes a simple snapshot of the start of VM memory. |
|
||||||
|
| `WORDS` | `( -- )` | Lists the words in the current vocabulary. |
|
||||||
|
| `VLIST` | `( -- )` | Gives a detailed word listing. |
|
||||||
|
| `SEE` | `( "name" -- )` | Decompiles and shows a definition. |
|
||||||
|
| `PAGE` | `( -- )` | Clears the screen. |
|
||||||
|
| `79-STANDARD` | `( -- flag )` | Pushes `-1` when FORTH-79 compliance mode is on. |
|
||||||
|
|
||||||
|
## 16. Line editor
|
||||||
|
`src/word_source/editor_words.c`. The editor works on block `SCR` as 16 lines of 64 characters.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `L` | `( u -- )` | Prints line `u` (0–15) of the current screen. |
|
||||||
|
| `S` | `( c-addr len u -- )` | Replaces line `u` with the string, padding with spaces or truncating to 64 characters. |
|
||||||
|
| `SHOW` | `( -- )` | Prints the whole screen with line numbers. |
|
||||||
|
| `EDIT` | `( u -- )` | Opens a minimal stdin/stdout line-editor shell on block `u`. |
|
||||||
|
|
||||||
|
## 17. Defining words and the compiler
|
||||||
|
`src/word_source/defining_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `:` **IMM** | `( "name" -- )` | Starts a colon definition and switches to compile mode. The new word stays hidden until `;`. |
|
||||||
|
| `;` **IMM** | `( -- )` | Compiles `EXIT`, ends the definition, reveals the word, and returns to interpret mode. |
|
||||||
|
| `CREATE` | `( "name" -- )` | Makes a header whose runtime pushes its data-field address (`HERE` aligned to a cell). It allocates **no** space, so follow it with `ALLOT` or `,`. |
|
||||||
|
| `VARIABLE` | `( "name" -- )` | Makes a word that pushes the address of one newly allocated cell. |
|
||||||
|
| `CONSTANT` | `( x "name" -- )` | Makes a word that pushes `x`. |
|
||||||
|
| `DOES>` **IMM** | `( -- )` | Inside a defining word, ends the create part. Words later made by that defining word run the code after `DOES>` with their body address on the stack. |
|
||||||
|
| `IMMEDIATE` **IMM** | `( -- )` | Marks the latest definition IMMEDIATE. |
|
||||||
|
| `STATE` | `( -- addr )` | Pushes the address of the compile-state cell (0 means interpreting). |
|
||||||
|
| `[` **IMM** | `( -- )` | Switches to interpret mode inside a definition. |
|
||||||
|
| `]` **IMM** | `( -- )` | Switches to compile mode. |
|
||||||
|
| `LITERAL` **IMM** | `( x -- )` | Compiles `x` so that it is pushed at runtime. Typical use: `[ 6 7 * ] LITERAL`. |
|
||||||
|
| `LIT` | `( -- x )` | Runtime for literals: pushes the next inline cell. The compiler inserts it; you do not call it directly. |
|
||||||
|
| `COMPILE` **IMM** | `( "name" -- )` | Legacy form: compiles a call to `name`. |
|
||||||
|
| `[COMPILE]` **IMM** | `( "name" -- )` | Compiles `name` even when it is IMMEDIATE. |
|
||||||
|
| `FORGET` | `( "name" -- )` | Removes `name` and every newer word and moves `HERE` back. Words below `FENCE` cannot be forgotten. |
|
||||||
|
| `FENCE` | `( -- )` | Moves the `FORGET` boundary up to the current top of the dictionary. A capsule calls it after loading to protect its own words. |
|
||||||
|
| `does_rt` | – | Internal `DOES>` helper that switches the new child word to DODOES. It is registered only so the threaded code can refer to it; do not call it. |
|
||||||
|
|
||||||
|
Example: `: ARRAY ( n "name" -- ) CREATE CELLS ALLOT DOES> ( i -- addr ) SWAP CELLS + ;`
|
||||||
|
|
||||||
|
## 18. Control flow
|
||||||
|
`src/word_source/control_words.c`. Every structure word is **IMM** and **CO**. Branch offsets are in bytes. Up to 64
|
||||||
|
structures can be nested at compile time (`CF_STACK_MAX`).
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `IF` | `( flag -- )` | Runs the following code only if `flag` is non-zero. It compiles `(0BRANCH)`. |
|
||||||
|
| `ELSE` | `( -- )` | Starts the code that runs when the `IF` flag was zero. |
|
||||||
|
| `THEN` | `( -- )` | Ends an `IF` or `IF … ELSE` structure. |
|
||||||
|
| `BEGIN` | `( -- )` | Marks the start of a loop. |
|
||||||
|
| `UNTIL` | `( flag -- )` | Loops back to `BEGIN` while `flag` is zero. |
|
||||||
|
| `AGAIN` | `( -- )` | Loops back to `BEGIN` unconditionally. Leave with `EXIT` or `ABORT`. |
|
||||||
|
| `WHILE` | `( flag -- )` | In `BEGIN … WHILE … REPEAT`, leaves the loop when `flag` is zero. |
|
||||||
|
| `REPEAT` | `( -- )` | Jumps back to `BEGIN` and resolves the exit of `WHILE`. |
|
||||||
|
| `DO` | `( limit start -- ) ( R: -- limit index )` | Starts a counted loop that always runs at least once. |
|
||||||
|
| `?DO` | `( limit start -- )` | Like `DO`, but skips the loop body when `start = limit`. |
|
||||||
|
| `LOOP` | `( -- )` | Adds 1 to the index and loops while `index < limit`. |
|
||||||
|
| `+LOOP` | `( n -- )` | Adds `n` to the index. For `n ≥ 0` it continues while `index < limit`; for `n < 0` it continues while `index ≥ limit`. |
|
||||||
|
| `LEAVE` | `( -- )` | Exits the innermost `DO` loop immediately: it sets the index to the limit and jumps past `LOOP`. |
|
||||||
|
| `I` | `( -- index )` | Pushes the index of the innermost loop. It is not IMMEDIATE. |
|
||||||
|
| `J` | `( -- index )` | Pushes the index of the next outer loop. |
|
||||||
|
| `UNLOOP` | `( -- ) ( R: limit index -- )` | Drops the loop parameters. Use it before `EXIT` inside a `DO` loop. |
|
||||||
|
| `EXIT` | `( -- )` | Returns from the current colon definition. Using it in interpret mode is an error. |
|
||||||
|
| `CASE` | `( x -- x )` | Starts a case structure. |
|
||||||
|
| `OF` | `( x v -- \| x )` | If `x = v`, drops both and runs the clause; otherwise keeps `x` and skips to the next `OF`. |
|
||||||
|
| `ENDOF` | `( -- )` | Ends an `OF` clause and jumps to `ENDCASE`. |
|
||||||
|
| `ENDCASE` | `( x -- )` | Drops the selector and resolves every `ENDOF` jump. |
|
||||||
|
| `(BRANCH)` | `( -- )` | Runtime: unconditional relative branch. The compiler inserts it; you do not call it directly. |
|
||||||
|
| `(0BRANCH)` | `( flag -- )` | Runtime: branches when `flag` is 0. The compiler inserts it; you do not call it directly. |
|
||||||
|
| `(DO)` `(?DO)` | `( limit start -- )` | Runtime for loop entry. The compiler inserts them; you do not call them directly. |
|
||||||
|
| `(LOOP)` `(+LOOP)` | `( -- )` / `( n -- )` | Runtime for loop increment and test. The compiler inserts them; you do not call them directly. |
|
||||||
|
| `(LEAVE)` | `( -- )` | Runtime for `LEAVE`: sets index to limit. The compiler inserts it; you do not call it directly. |
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
```forth
|
||||||
|
: COUNTDOWN ( n -- ) BEGIN DUP . 1- DUP 0= UNTIL DROP ;
|
||||||
|
: TABLE ( -- ) 5 0 DO 5 0 DO I J * 4 .R LOOP CR LOOP ;
|
||||||
|
: COLOR ( n -- ) CASE 0 OF ." red" ENDOF 1 OF ." green" ENDOF ." ?" ENDCASE ;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 19. StarForth extensions
|
||||||
|
`src/word_source/starforth_words.c`. These words are registered in both `FORTH` and the `STARFORTH` vocabulary.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `ENTROPY@` | `( xt -- n )` | Pushes the `execution_heat` counter of a word. The name says "entropy", but the value is execution heat. Registered only in the `STARFORTH` vocabulary. |
|
||||||
|
| `ENTROPY!` | `( n xt -- )` | Sets a word's `execution_heat` counter. Registered only in the `STARFORTH` vocabulary. |
|
||||||
|
| `WORD-ENTROPY` | `( -- )` | Prints the execution heat of every word. |
|
||||||
|
| `RESET-ENTROPY` | `( -- )` | Sets every heat counter to zero. |
|
||||||
|
| `TOP-WORDS` | `( n -- )` | Prints the `n` hottest words. |
|
||||||
|
| `(-` | `( "ccc<)>" -- )` | A comment that marks metadata blocks to extract into `init.4th`. It consumes input up to `)`. |
|
||||||
|
| `INIT` | `( -- )` | Reads `./capsules/core/init.4th`, copies its blocks from block 1 onward, and runs them. |
|
||||||
|
| `VERSION` | `( -- )` | Prints `StarForth v<ver> <arch> <variant> <timestamp>`. |
|
||||||
|
| `SEED` | `( n -- )` | Seeds the PRNG so random sequences can be reproduced. |
|
||||||
|
| `RANDOM` | `( lo hi -- n )` | Pushes a pseudo-random number in `[lo, hi]`, including both ends. |
|
||||||
|
| `WAIT` | `( n -- )` | Waits `n` heartbeat ticks by calling `vm_tick()` `n` times. It counts heartbeats, not wall-clock time, so it behaves the same on amd64, aarch64, and riscv64. |
|
||||||
|
| `HEARTBEAT-TICKS@` | `( -- n )` | Pushes the canonical heartbeat tick count (Loop #7). The project uses this as its clock. It is read-only. |
|
||||||
|
| `ZUSE-AUTHENTICATE` | `( -- )` | Sets `zuse_session = 1`. The write happens only in C. |
|
||||||
|
| `ZUSE-SESSION?` | `( -- flag )` | True if `ZUSE-AUTHENTICATE` has run during this boot. It is read-only. |
|
||||||
|
| `ZUSE-PUBKEY@` | `( i -- u )` | Pushes 8-byte little-endian chunk `i` (0–3) of Zuse's Ed25519 **public** key. An out-of-range `i` pushes 0 and sets an error. The private seed is never exposed. |
|
||||||
|
| `ZUSE-CERT-INSTALLED?` | `( -- flag )` | True once the one-time certificate fuse has been blown. |
|
||||||
|
|
||||||
|
## 20. Word-level ACL
|
||||||
|
`src/word_source/acl_words.c`. Every word takes an `xt`, obtained with `'` or `[']`. Writes are silently ignored for
|
||||||
|
a **pinned** word.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `ACL-MODE@` | `( xt -- mode )` | Pushes the enforcement mode: 0 is STRICT (the decision is permanent) and 1 is TTL (the decision is rechecked when the countdown expires). |
|
||||||
|
| `ACL-MODE!` | `( mode xt -- )` | Sets the enforcement mode. |
|
||||||
|
| `ACL-TTL@` | `( xt -- n )` | Pushes the TTL countdown. At 0 in TTL mode, the interpreter calls `acl_recheck()`. |
|
||||||
|
| `ACL-TTL!` | `( n xt -- )` | Sets the TTL, clamped to `[0, UINT32_MAX]`. |
|
||||||
|
| `ACL-ALLOW@` | `( xt -- flag )` | Pushes the cached decision: `-1` means allowed and `0` means denied. |
|
||||||
|
| `ACL-ALLOW!` | `( flag xt -- )` | Sets the cached decision; any non-zero value means allowed. |
|
||||||
|
| `ACL-PINNED?` | `( xt -- flag )` | True if the ACL fields are pinned and therefore immutable. |
|
||||||
|
| `ACL-PIN` | `( xt -- )` | Pins the word. **This is one-way**: no FORTH word can unpin it. |
|
||||||
|
| `ACL-HEAT@` | `( xt -- heat )` | Pushes the execution heat. `ACL.4th` uses it to calibrate TTLs. |
|
||||||
|
| `ACL-WORD-ID` | `( xt -- id )` | Pushes the word's stable `word_id`. It never changes, so it is safe to use as a table index. |
|
||||||
|
| `ACL-INHERIT` | `( src-xt dst-xt -- )` | Copies the mode from `src` to `dst` and resets `dst`: unpinned, TTL 0, allowed. It is written in C because only C may clear a pin. |
|
||||||
|
| `ACL-INIT-PRIMITIVES` | `( -- )` | For every unpinned word, sets TTL to 0, allow to 1, and mode to TTL. `ACL-BOOT` calls it. |
|
||||||
|
|
||||||
|
## 21. Physics: benchmark and diagnostics
|
||||||
|
`src/word_source/physics_benchmark_words.c`. These are interactive diagnostics that print to the console.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `BENCH-DICT-LOOKUP` | `( iterations -- )` | Benchmarks dictionary lookup and records Q48.16 latencies. Use at least 10,000 iterations; 100,000 is the standard run and 1,000,000 is a stress test. |
|
||||||
|
| `PHYSICS-CACHE-STATS` | `( -- )` | Prints hot-words cache statistics. |
|
||||||
|
| `PHYSICS-TOGGLE-CACHE` | `( -- )` | Turns the hot-words cache on or off, for A/B testing. |
|
||||||
|
| `PHYSICS-RESET-STATS` | `( -- )` | Resets the cache statistics. |
|
||||||
|
| `PHYSICS-BUILD-INFO` | `( -- )` | Prints the variant's build configuration. |
|
||||||
|
| `PHYSICS-BAYESIAN-REPORT` | `( addr -- )` | Prints a Bayesian comparison of the current cache statistics against the baseline stored at `addr`. |
|
||||||
|
|
||||||
|
> `physics_diagnostic_words.c` also defines `PHYSICS-WORD-METRICS`, `PHYSICS-CALC-KNOBS`, `PHYSICS-BURN ( n -- )`
|
||||||
|
> and `PHYSICS-SHOW-FEEDBACK`, but nothing calls `register_physics_diagnostic_words()`, so **none of them are in
|
||||||
|
> the dictionary** at this commit.
|
||||||
|
|
||||||
|
## 22. Physics: pipelining diagnostics
|
||||||
|
`src/word_source/physics_pipelining_diagnostic_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `PIPELINING-SHOW-STATS` | `( "name" -- )` | Prints the word-to-word transition metrics of `name`. |
|
||||||
|
| `PIPELINING-SHOW-TOP-TRANSITIONS` | `( "name" n -- )` | Prints the `n` words that most often follow `name`. |
|
||||||
|
| `PIPELINING-ANALYZE-WORD` | `( "name" -- )` | Prints a full analysis of one word's transitions, with hints for reading them. |
|
||||||
|
| `PIPELINING-STATS` | `( -- )` | Prints pipelining statistics aggregated across the whole dictionary. |
|
||||||
|
| `PIPELINING-RESET-ALL` | `( -- )` | Clears all transition metrics. |
|
||||||
|
| `PIPELINING-ENABLE` | `( -- )` | Placeholder. Pipelining is switched on or off at compile time. |
|
||||||
|
|
||||||
|
## 23. Physics: freeze, heat, and decay
|
||||||
|
`src/word_source/physics_freeze_words.c`. Words are named by `c-addr u` strings, for example `S" DUP" HEAT@`. An
|
||||||
|
unknown name is not an error.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `FREEZE-WORD` | `( c-addr u -- )` | Sets `WORD_FROZEN` on the word, so Loop #3 decay stops lowering its heat. |
|
||||||
|
| `UNFREEZE-WORD` | `( c-addr u -- )` | Clears `WORD_FROZEN` and leaves `WORD_PINNED` alone. |
|
||||||
|
| `FROZEN?` | `( c-addr u -- flag )` | True if the word is frozen. An unknown word gives `0`. |
|
||||||
|
| `HEAT!` | `( heat c-addr u -- )` | Writes `execution_heat` directly, bypassing Loops #1 and #3. For testing only. |
|
||||||
|
| `HEAT@` | `( c-addr u -- heat )` | Reads `execution_heat`. An unknown word gives `0`. |
|
||||||
|
| `SHOW-HEAT` | `( c-addr u -- )` | Prints `NAME: HEAT (frozen) (pinned)`. |
|
||||||
|
| `ALL-HEATS` | `( -- )` | Prints up to 1024 words sorted by heat, hottest first. |
|
||||||
|
| `DECAY-RATE@` | `( -- q )` | Pushes the base decay rate per µs (`DECAY_RATE_PER_US_Q16`) in Q48.16. |
|
||||||
|
| `FREEZE-CRITICAL` | `( -- )` | Freezes 21 core words: `DUP DROP SWAP OVER ROT @ ! C@ C! EXECUTE IF THEN ELSE DO LOOP BEGIN UNTIL REPEAT . EMIT CR`. |
|
||||||
|
|
||||||
|
## 24. Dictionary heat optimisation
|
||||||
|
`src/word_source/dictionary_heat_diagnostic_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `HEAT-PERCENTILES` | `( -- p25 p50 p75 )` | Pushes the current heat percentile thresholds, with the 75th on top. |
|
||||||
|
| `LOOKUP-STRATEGY@` | `( -- n )` | Pushes the lookup strategy: 0 is naive (newest-first linear scan) and 1 is heat-aware (hot bucket first). |
|
||||||
|
| `LOOKUP-STRATEGY!` | `( n -- )` | Sets the strategy. Only 0 or 1 is accepted; other values are silently ignored. |
|
||||||
|
| `REORG-BUCKETS` | `( -- )` | Re-sorts the lookup buckets by heat and refreshes the percentiles immediately, without waiting for the heartbeat. |
|
||||||
|
| `SHOW-HEAT-OPTIMIZATION` | `( -- )` | Prints the strategy, the percentiles, and the hot, warm, and cool zones. |
|
||||||
|
| `COMPARE-LOOKUPS` | `( iterations -- )` | Benchmarks naive against heat-aware lookup and prints the speedup. It restores the original strategy afterwards. |
|
||||||
|
|
||||||
|
## 25. Logging
|
||||||
|
`src/word_source/log_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `LOG-ERROR` `LOG-WARN` `LOG-INFO` `LOG-TEST` `LOG-DEBUG` | `( -- level )` | Push the log level constants. |
|
||||||
|
| `LOG-LEVEL!` | `( level -- )` | Sets the active log filter, clamped to `[LOG-ERROR, LOG-DEBUG]`. |
|
||||||
|
| `LOG-LEVEL@` | `( -- level )` | Pushes the current log level. |
|
||||||
|
| `LOG-ERROR"` `LOG-WARN"` `LOG-INFO"` `LOG-TEST"` `LOG-DEBUG"` **IMM** | `( "ccc<">" -- )` | Log a literal string at that level. In interpret mode the string is logged immediately; when compiling, a runtime word and the inline string are compiled. |
|
||||||
|
| `LOG-ERROR-STR` `LOG-WARN-STR` `LOG-INFO-STR` `LOG-TEST-STR` `LOG-DEBUG-STR` | `( c-addr u -- )` | Log a string taken from the stack at that level. |
|
||||||
|
| `(do-log-error)` `(do-log-warn)` `(do-log-info)` `(do-log-test)` `(do-log-debug)` | `( -- )` | Runtimes for the compiled `LOG-*"` words. The compiler inserts them; you do not call them directly. |
|
||||||
|
| `(LOG-APPEND-RAW)` **K** | `( level timestamp c-addr u -- )` | Appends a raw entry to the kernel log ring, attributed to the calling VM (or `HADES`). It reports errors on the console, not through `log_message`, to avoid recursion. |
|
||||||
|
|
||||||
|
Example: `: CHECK ( n -- ) 0< IF LOG-WARN" negative input" THEN ;`
|
||||||
|
|
||||||
|
## 26. Q48.16 fixed-point math
|
||||||
|
`src/word_source/q48_words.c`. A value is `n × 65536`. The underlying type is **unsigned** `uint64_t`; see
|
||||||
|
[§36](#36-implementation-quirks-to-know) for what that means for negative values.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `Q.+` `Q.-` | `( q1 q2 -- q3 )` | Add and subtract. |
|
||||||
|
| `Q.*` | `( q1 q2 -- q3 )` | Multiplies, computing `(a*b) >> 16`. |
|
||||||
|
| `Q./` | `( q1 q2 -- q3 )` | Divides, computing `(a << 16) / b`. **Division by zero returns 0** and sets no error. |
|
||||||
|
| `Q.ABS` | `( q -- \|q\| )` | Absolute value, treating the top bit as a sign bit. |
|
||||||
|
| `Q.NEG` | `( q -- -q )` | Two's-complement negation. |
|
||||||
|
| `Q.LOG` | `( q -- ln q )` | Natural logarithm by Newton-Raphson. Requires `q > 0`. |
|
||||||
|
| `Q.EXP` | `( q -- e^q )` | Exponential by Taylor series. |
|
||||||
|
| `Q.SQRT` | `( q -- √q )` | Square root by Newton-Raphson. |
|
||||||
|
| `Q.SIN` `Q.COS` | `( q -- q' )` | Sine and cosine of an angle in radians. The argument is reduced to [-π, π] and then a Taylor series is applied. |
|
||||||
|
| `Q.FROM-INT` | `( n -- q )` | Converts an integer to Q48.16 as `n << 16`. **A negative `n` becomes 0.** |
|
||||||
|
| `Q.TO-INT` | `( q -- n )` | Converts to an integer as `q >> 16`, truncating. |
|
||||||
|
| `Q.1` | `( -- 65536 )` | Pushes 1.0. |
|
||||||
|
| `Q.0` | `( -- 0 )` | Pushes 0.0. |
|
||||||
|
| `Q.SCALE` | `( -- 65536 )` | Pushes the scale factor; the same value as `Q.1`. |
|
||||||
|
| `Q.=` | `( q1 q2 -- flag )` | Equality. |
|
||||||
|
| `Q.<` `Q.>` | `( q1 q2 -- flag )` | Comparison, done **unsigned**. |
|
||||||
|
| `Q.0=` | `( q -- flag )` | True if the value is zero. |
|
||||||
|
| `Q.MAX` `Q.MIN` | `( q1 q2 -- q3 )` | Maximum and minimum, compared **unsigned**. |
|
||||||
|
| `Q.PRINT` | `( q -- )` | Prints the value as `int.fffff ` with five fractional digits. |
|
||||||
|
|
||||||
|
Example: `3 Q.FROM-INT Q.SQRT Q.PRINT` prints approximately `1.732`.
|
||||||
|
|
||||||
|
## 27. Inference engine (SSM, L8, and Bayes)
|
||||||
|
`src/word_source/inference_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `INFER-RUN` | `( -- )` | Runs the full inference engine on this VM's rolling window and dictionary heat, and caches the results. |
|
||||||
|
| `INFER-WINDOW@` | `( -- u )` | Pushes the last inferred optimal window width. |
|
||||||
|
| `INFER-DECAY@` | `( -- q )` | Pushes the last inferred decay slope. |
|
||||||
|
| `INFER-VARIANCE@` | `( -- q )` | Pushes the last inferred variance. |
|
||||||
|
| `INFER-FIT@` | `( -- q )` | Pushes the last fit quality. |
|
||||||
|
| `INFER-EARLY-EXIT@` | `( -- flag )` | True if the last run exited early. |
|
||||||
|
| `Q.VARIANCE` | `( addr u -- q )` | Pushes the variance of `u` uint64 cells at `addr`. |
|
||||||
|
| `INFER-DECAY-SLOPE` | `( addr u -- q )` | Fits a decay slope to the array by linear regression. |
|
||||||
|
| `INFER-WINDOW-WIDTH` | `( addr u -- n )` | Computes the optimal window width for the array. |
|
||||||
|
| `WINDOW-DIVERSITY` | `( -- u )` | Pushes the number of distinct words in the rolling window. |
|
||||||
|
| `L8-MODE` | `( -- n )` | Pushes the current L8 (legacy 16-mode) Jacquard selection. |
|
||||||
|
| `L8-UPDATE` | `( entropy cv temporal stability -- )` | Feeds four Q48.16 metrics to `ssm_l8_update()`. `stability` is on top of the stack. |
|
||||||
|
| `L8-APPLY` | `( -- )` | Applies the currently selected L8 mode. |
|
||||||
|
| `L8-TABLE-FORCE` | `( idx -- )` | Forces the adaptive 128-config table to `idx & 127` as though the UCB bandit had picked it, and applies it. Unlike `L8-UPDATE` and `L8-APPLY`, this choice is not overwritten at the next heartbeat trial. Use it for DoE campaigns. |
|
||||||
|
| `BAYES-CACHE-MEAN` `BAYES-CACHE-LOWER` `BAYES-CACHE-UPPER` | `( -- q )` | Push the posterior mean latency and the 95 % credible bounds for hot-words cache hits. |
|
||||||
|
| `BAYES-BUCKET-MEAN` `BAYES-BUCKET-LOWER` `BAYES-BUCKET-UPPER` | `( -- q )` | Push the same three values for bucket searches. |
|
||||||
|
|
||||||
|
## 28. DEFER and IS
|
||||||
|
`src/word_source/defer_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `DEFER` | `( "name" -- )` | Creates a vectored word. Running it before an action has been set with `IS` sets `vm->error`. |
|
||||||
|
| `IS` | `( xt "name" -- )` | Sets the action of `name`. It is refused unless `name` was created by `DEFER`. |
|
||||||
|
| `DEFER@` | `( "name" -- xt )` | Pushes the current action of a deferred word. |
|
||||||
|
|
||||||
|
Example: `DEFER GREET : HI ." hi" ; ' HI IS GREET GREET`
|
||||||
|
|
||||||
|
## 29. Framebuffer (Hestia only)
|
||||||
|
`src/word_source/framebuffer_words.c`. These words are registered only in the Hestia VM, by `capsule_birth_baby()`,
|
||||||
|
and not by `register_forth79_words()`.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `PLOT` | `( x y color -- )` | Writes one raw pixel. The origin is top-left and Y increases downward. |
|
||||||
|
| `FB-WIDTH` | `( -- n )` | Pushes the framebuffer width in pixels. |
|
||||||
|
| `FB-HEIGHT` | `( -- n )` | Pushes the framebuffer height in pixels. |
|
||||||
|
|
||||||
|
## 30. Keyboard
|
||||||
|
`src/word_source/keyboard_words.c`. These are diagnostics for the console fabric. On a platform without the device,
|
||||||
|
each word pushes `0`.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `KBD-SCAN` | `( -- sc -1 \| 0 )` | amd64 i8042: pops one raw XT scancode from the queue, if there is one. |
|
||||||
|
| `KBD-DEBUG` | `( -- isr spurious )` | amd64: pushes the i8042 interrupt count and the spurious-interrupt count. |
|
||||||
|
| `VKBD-EVENT` | `( -- code value -1 \| 0 )` | riscv64 and aarch64 virtio-input: pops one Linux-style `EV_KEY` code and value. |
|
||||||
|
| `VKBD-DEBUG` | `( -- isr )` | riscv64 and aarch64: pushes the virtio-input interrupt count. |
|
||||||
|
| `KEY-EVENT` | `( -- keycode pressed -1 \| 0 )` | The unified event on every architecture: pops one keycode with its pressed (1) or released (0) state. |
|
||||||
|
| `ALT+TAB` | `( -- )` | Switches the console between text and graphics, the same as the physical Alt+Tab. |
|
||||||
|
|
||||||
|
## 31. TrueType text
|
||||||
|
`src/word_source/ttf_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `TTF-TEXT` **K** | `( c-addr u x y size color -- )` | Draws the string with the TrueType renderer at pixel `(x, y)` in the given size and color. |
|
||||||
|
|
||||||
|
## 32. REPL scrollback
|
||||||
|
`src/word_source/scroll_words.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `SCROLL-BACK` **K** | `( n -- )` | Scrolls the REPL view back `n` lines. |
|
||||||
|
| `SCROLL-FWD` **K** | `( n -- )` | Scrolls the REPL view forward `n` lines, toward the live output. |
|
||||||
|
|
||||||
|
## 33. Kernel REPL and DoE hooks
|
||||||
|
`src/starkernel/repl.c` and `src/starkernel/doe_log.c`
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `HB-ON` **K** | `( -- )` | Turns on per-tick DoE instrumentation. |
|
||||||
|
| `HB-OFF` **K** | `( -- )` | Turns off per-tick DoE instrumentation. |
|
||||||
|
| `BLK-ATTACH-ACK` **K** | `( dev-ptr ok? -- )` | The acknowledgement Artemis sends to Hera after `BLK-ATTACH`, delivered with `VM-EXEC`. On success, Hera runs the deferred Zuse or WIREBIND attach. |
|
||||||
|
| `KH-BLK-ATTACH-SEND` **K** | `( c-addr u -- ok? )` | Sends a payload to Hera through kernel-Hermes. The sender and receiver are worked out in C, so the caller cannot spoof them. A refused send is logged. |
|
||||||
|
| `KH-ELEVATE-SEND` **K** | `( c-addr u -- ok? )` | The same mechanism for elevation requests. Nothing calls it at present. |
|
||||||
|
|
||||||
|
## 34. Hera (Mama) and child-VM words
|
||||||
|
`src/starkernel/capsule/mama_forth_words.c`. These are **K** only. Hera receives every word in this section, in
|
||||||
|
both `FORTH` and the `MAMA` vocabulary. Child VMs receive only `STOP EXEC USE BIRTH CAPSULE-BIRTH VM-EXEC VM-CALL
|
||||||
|
VM-HEAT VM-ERROR? SWITCH-MARK-WORK` and the `STADIUM-*` words. VM names are matched without regard to case.
|
||||||
|
|
||||||
|
### VM lifecycle
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `BIRTH` | `( c-addr u -- )` | Births a VM from its capsule (`S" Artemis"` loads `artemis:init.4th`). If the VM is already live, nothing happens. `Hera` is rejected. |
|
||||||
|
| `KILL` | `( c-addr u -- )` | Destroys a VM. Hera cannot be killed, and killing a dead VM does nothing. |
|
||||||
|
| `START` | `( c-addr u -- )` | Enters the VM's REPL and blocks until it runs `STOP` or `BYE`. It refuses a VM that is LIVE, DEAD, or STILLBORN. |
|
||||||
|
| `STOP` | `( -- )` | Halts the current VM, so its REPL loop returns. |
|
||||||
|
| `USE` | `( c-addr u -- )` | Redirects console input to the named VM without nesting the C stack, and changes the prompt to `[Name]`. `S" Hera" USE` switches back to Hera. |
|
||||||
|
| `EXEC` | `( c-addr u -- )` | Runs a named capsule inside the current VM. |
|
||||||
|
| `EJECT` | `( -- )` | Cleanly detaches the identity attached through USB home blocks: it flushes the user VM and kills it, or logs Zuse out. |
|
||||||
|
| `CONNECT-HERMES` | `( -- )` | Enters Hermes's REPL, birthing Hermes first if needed. |
|
||||||
|
| `CONNECT-ARTEMIS` | `( -- )` | Enters Artemis's REPL, birthing Artemis first if needed. |
|
||||||
|
| `BYE` | `( -- )` | **On Hera:** reaps every child and then cold-resets the machine. |
|
||||||
|
|
||||||
|
### Cross-VM execution (compudynamics)
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `VM-EXEC` | `( cmd-a cmd-u name-a name-u -- )` | Injects a command into the named VM and runs it immediately without blocking. Example: `S" DOE-WORK" S" Hermes" VM-EXEC`. |
|
||||||
|
| `VM-CALL` | `( cmd-a cmd-u name-a name-u -- n )` | Like `VM-EXEC`, then pops the target's top of stack onto the caller's stack. If the target left nothing, it pushes 0 and sets an error. |
|
||||||
|
| `VM-STEP` | `( c-addr u -- )` | Gives the named VM one REPL turn: one prompt, one line, then it returns. |
|
||||||
|
| `VM-HEAT` | `( c-addr u -- q )` | Pushes the VM's `execution_heat_q48`. An unknown VM gives 0 and prints nothing. |
|
||||||
|
| `VM-ERROR?` | `( c-addr u -- flag )` | True if the VM has `vm->error` set. An unknown VM gives `0`. |
|
||||||
|
| `VM-COUNT` | `( -- n )` | Pushes the number of registered VMs. |
|
||||||
|
| `VM-CONSERVED?` | `( -- flag )` | True if the total fleet heat is within ε of `Q.1`. |
|
||||||
|
| `VM-PHYSICS-STATUS` | `( -- )` | Prints the fleet physics report. |
|
||||||
|
| `SWITCH-MARK-WORK` | `( c-addr u -- )` | Marks a VM as having work, which makes it eligible for a context switch. The message path calls it, and it ignores bad names silently. |
|
||||||
|
| `MAMA-VM-ID` | `( -- 0 0 )` | Pushes Hera's 128-bit VM ID, which is all zeros. |
|
||||||
|
| `NAME>XT` | `( c-addr u -- xt \| 0 )` | Looks up a name held in a data buffer. A miss gives `0`. |
|
||||||
|
|
||||||
|
### Capsules
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `CAPSULE-COUNT` | `( -- n )` | Pushes the number of entries in the capsule directory. |
|
||||||
|
| `CAPSULE@` | `( idx -- desc \| 0 )` | Pushes the descriptor for the capsule at `idx`. |
|
||||||
|
| `CAPSULE-HASH@` | `( desc -- hash )` | Pushes the capsule's content hash. |
|
||||||
|
| `CAPSULE-FLAGS@` | `( desc -- flags )` | Pushes the capsule's flags. |
|
||||||
|
| `CAPSULE-LEN@` | `( desc -- len )` | Pushes the capsule's payload length. |
|
||||||
|
| `CAPSULE-BIRTH` | `( id -- vmid-lo vmid-hi )` | Births an unnamed VM from a production (p) capsule and pushes its 128-bit ID. On failure both cells are all ones. |
|
||||||
|
| `CAPSULE-RUN` | `( id -- )` | Runs an experiment (e) capsule on Hera. |
|
||||||
|
| `CAPSULE-TEST` | `( -- )` | Prints a message confirming the capsule system is running. |
|
||||||
|
| `WORKER-BIRTH` | `( cap-a cap-u name-a name-u -- ok? )` | Births a named, `VM-EXEC`-addressable worker from any p-capsule, with no identity attached. |
|
||||||
|
| `UNATTENDED-BIRTH` | `( cap-a cap-u name-a name-u -- ok? )` | Births a VM and installs the verified identity whose `UNATTENDED-ID-UUID` and `UNATTENDED-ID-CERT` the capsule defined. |
|
||||||
|
| `CONSOLE-ATTACH` | `( name-a name-u -- ok? )` | Pairs a new console VM with the live VM registered as `<name>~user`. |
|
||||||
|
|
||||||
|
### Identity, Zuse, and diagnostics
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `MINT` | `( fn-a fn-u un-a un-u em-a em-u ph-a ph-u restrict? -- ok? )` | Mints an identity onto the attached USB drive. The full name and username are required; pass an empty string for email or phone to leave them out. A non-zero `restrict?` selects the locked-down personality that allows only FORTH-79 and FORTH-83 words. |
|
||||||
|
| `MINT-SCRATCH` | same as `MINT` | Mints onto the scratch device instead, prints the UUID, and pushes 1 or 0. |
|
||||||
|
| `MINT-SCRATCH-EMIT` | `( -- ok? )` | Prints the last scratch mint as FORTH source (`CREATE UNATTENDED-ID-UUID` / `-CERT` byte lists) for copying by hand. It refuses (pushes 0) if no mint has succeeded. |
|
||||||
|
| `ZUSE-ELIGIBILITY-ADD` | `( c-addr -- ok? )` | Adds the 32-byte Ed25519 public key at `c-addr` to Zuse's elevation list. |
|
||||||
|
| `ZUSE-ELIGIBLE?` | `( c-addr -- flag )` | Checks whether a key is on the list. It fails closed. |
|
||||||
|
| `ELEVATE-PUBKEY-UNPACK` | `( pk0 pk1 pk2 pk3 buf -- )` | Rebuilds a 32-byte public key from four cells. It is the inverse of `ZUSE-PUBKEY@`. |
|
||||||
|
| `RUNCAP-TEST` | `( c-addr u -- ok? rc )` | Diagnostic: runs `capsule_runcap_birth()` against the current home-blocks drive. |
|
||||||
|
| `PAIR-TEST` | `( c-addr u -- ok? )` | Diagnostic: births a console VM and a `<name>~user` VM as a pair. |
|
||||||
|
|
||||||
|
### Stadium (heat accounting)
|
||||||
|
Heat values are Q48.16. Each word works only on the calling VM's own quota.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `STADIUM-ADMIT` | `( identity heat behaviour -- cell \| -1 )` | Admits a patron. `behaviour` must be 0–3. |
|
||||||
|
| `STADIUM-EVICT` | `( cell -- flag )` | Reaps the patron in `cell`. It is refused if the cell is out of range, not resident, pinned, or blocked by what it contains. |
|
||||||
|
| `STADIUM-HEAT@` | `( cell -- heat )` | Reads a resident cell's heat. A cell that is not the caller's gives 0. |
|
||||||
|
| `STADIUM-HEAT!` | `( heat cell -- )` | Writes a cell's heat, pulling the difference from the reservoir or pushing it back. An increase the reservoir cannot cover is silently refused. |
|
||||||
|
| `STADIUM-RES@` | `( -- heat )` | Pushes the VM's reservoir balance. |
|
||||||
|
| `STADIUM-RES-PULL` | `( qty -- got )` | Pulls up to `qty` from the reservoir and pushes the amount actually taken. |
|
||||||
|
| `STADIUM-RES-PUSH` | `( heat -- )` | Credits heat back to the reservoir. |
|
||||||
|
| `STADIUM-WORD-HEAT` | `( -- heat )` | Pushes the total heat held by this VM's word-execution residents. |
|
||||||
|
|
||||||
|
## 35. Hosted lifecycle stubs
|
||||||
|
`src/word_source/lifecycle_words_hosted.c`. These are **H** only; `main.c` registers them. Each word only logs
|
||||||
|
`"<WORD> <name> (hosted)"`. They exist so that capsule scripts also parse in hosted builds.
|
||||||
|
|
||||||
|
| Word | Stack | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `BIRTH` `KILL` `PAUSE` `RESUME` `USE` | `( c-addr u -- )` | No-op stubs that only write a log line. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 36. Implementation quirks to know
|
||||||
|
|
||||||
|
These behaviours differ from what a FORTH-79 or ANS programmer would expect. Each was checked against the C source.
|
||||||
|
|
||||||
|
1. **`ROLL` counts from the bottom of the stack.** With `1 2 3` on the stack, `1 ROLL` gives `2 3 1`; ANS gives
|
||||||
|
`1 3 2`. The test suite (`stack_words_test.c`) asserts the current behaviour, so it looks intended. Portable code
|
||||||
|
should use `SWAP` and `ROT`.
|
||||||
|
2. **`PICK` is 0-based,** as in ANS. FORTH-79's `PICK` was 1-based.
|
||||||
|
3. **`FIND` parses the input stream.** It does not take a counted string. Use `(FIND)` for a counted string or
|
||||||
|
`NAME>XT` (kernel only) for a name in a buffer.
|
||||||
|
4. **The Q48.16 type is unsigned.** `Q.<`, `Q.>`, `Q.MIN`, `Q.MAX` and `Q.PRINT` treat a negative Q value as a huge
|
||||||
|
positive one, and `Q.FROM-INT` turns a negative integer into 0. `Q.ABS` and `Q.NEG` do treat the top bit as a sign.
|
||||||
|
5. **`Q./` by zero returns 0 without setting an error,** while the integer `/`, `MOD`, and `*/` all set `vm->error`.
|
||||||
|
6. **`[LITERAL]` does nothing,** and `LITERAL` works only because §17 registers it again after the placeholder.
|
||||||
|
7. **`MOD`, `/MOD`, `*/`, and `*/MOD` are registered twice.** The mixed-arithmetic versions (§6) are the ones used.
|
||||||
|
8. **Four `PHYSICS-*` diagnostic words are not registered.** `PHYSICS-WORD-METRICS`, `PHYSICS-CALC-KNOBS`,
|
||||||
|
`PHYSICS-BURN` and `PHYSICS-SHOW-FEEDBACK` are defined in C but never added to the dictionary.
|
||||||
|
9. **`does_rt` is a visible dictionary entry.** It is an internal helper; do not call it.
|
||||||
|
10. **The `STARFORTH` vocabulary registers its words twice** (once in `FORTH`, once in `STARFORTH`). Hera does the
|
||||||
|
same with `MAMA`. As a result, those names appear twice in `WORDS` output.
|
||||||
@@ -0,0 +1,698 @@
|
|||||||
|
# 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. [Open decisions](#3-open-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 (pending D-1). |
|
||||||
|
| Registers | `T` (top of data stack), `S` (second), `R` (top of return stack), `P` (program counter), `A` and `B` (address registers). |
|
||||||
|
| Stacks | Hardware stacks below `S` and `R`. Depth and visibility are D-2. |
|
||||||
|
| 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 SEND RECV`. Words that clobber `B`:
|
||||||
|
`SEND RECV`.
|
||||||
|
|
||||||
|
**Return-stack words** (`>R R> R@ 2>R 2R> 2R@ I J UNLOOP` and the loop runtimes) are always IN.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Open decisions
|
||||||
|
|
||||||
|
These must be settled before the golden model is built. Where a definition below depends on one, it
|
||||||
|
says so.
|
||||||
|
|
||||||
|
| ID | Decision | Default in this document |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **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 (needed by `DEPTH`, `PICK`, `ROLL`, `SP@`, `SP!`). | Stacks backed by node RAM, with the data stack pointer exposed as MM register `DSP`. |
|
||||||
|
| **D-3** | Exact `+*` semantics at 32 bits: whether the add carries out of `T` into the shift. | Carry is kept (extended multiply step), so `UM*` returns a full 64-bit product. |
|
||||||
|
| **D-4** | Node memory map, including port and register addresses. | Symbolic names only (§6, §7). |
|
||||||
|
| **D-5** | Host node cell width. | 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. | Per-opcode and per-call-target. Transition counters deferred. |
|
||||||
|
| **D-7** | `ROLL` semantics. | Fix to ANS (count from the top). v3's bottom-counting `ROLL` is retired. |
|
||||||
|
| **D-8** | Q48.16 signedness. | Signed. v3's unsigned comparisons and `Q.FROM-INT` clamping are retired. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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) -----------------------------------------------------
|
||||||
|
: UM* ( u1 u2 -- ulo uhi ) a! 0 31 FOR +* UNEXT push drop a pop ;
|
||||||
|
|
||||||
|
\ ---- divide: 32-step restoring division, divisor held in A --------------
|
||||||
|
: UM/MOD ( ulo uhi ud -- urem uquot )
|
||||||
|
a!
|
||||||
|
31 FOR
|
||||||
|
over 0< NEGATE push \ R: lo's top bit (1/0)
|
||||||
|
dup 0< push \ R: hi's top bit (-1/0)
|
||||||
|
2* pop pop SWAP push OR pop \ lo hi' t hi shifted, lo bit brought in
|
||||||
|
push SWAP 2* SWAP pop \ lo' hi' t
|
||||||
|
over a U< 0= OR \ lo' hi' flag t OR hi' >= d
|
||||||
|
IF a - SWAP 1 OR SWAP THEN
|
||||||
|
NEXT
|
||||||
|
SWAP ;
|
||||||
|
|
||||||
|
\ ---- signed division, truncating toward zero (v3 semantics) -------------
|
||||||
|
: SM/REM ( d n -- rem quot )
|
||||||
|
2DUP xor push \ R: quotient sign
|
||||||
|
over push \ R: remainder sign (sign of dividend)
|
||||||
|
ABS push DABS pop UM/MOD
|
||||||
|
pop 0< IF push NEGATE pop THEN
|
||||||
|
pop 0< IF NEGATE THEN ;
|
||||||
|
```
|
||||||
|
|
||||||
|
`2DUP`, `-`, `ABS`, and `DABS` 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` | MM | Read `DSP` (D-2). |
|
||||||
|
| `PICK` | MM + CAP | Read stack RAM at `DSP − n` (D-2). 0-based, as in v3. |
|
||||||
|
| `ROLL` | CAP | Rewritten with ANS semantics (D-7), using `PICK` and a copy loop. |
|
||||||
|
|
||||||
|
### 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` |
|
||||||
|
| `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` |
|
||||||
|
| `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) |
|
||||||
|
| `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=` |
|
||||||
|
| `<>` | CAP | `xor 0<>` |
|
||||||
|
| `<` | CAP | `2DUP xor 0< IF drop 0< ELSE - 0< THEN` (overflow-safe) |
|
||||||
|
| `>` | CAP | `SWAP <` |
|
||||||
|
| `<=` | CAP | `> 0=` |
|
||||||
|
| `>=` | CAP | `< 0=` |
|
||||||
|
| `U<` | CAP | §4 |
|
||||||
|
| `U>` | CAP | `SWAP U<` |
|
||||||
|
| `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+` |
|
||||||
|
| `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` |
|
||||||
|
| `*/` | CAP | `*/MOD NIP` |
|
||||||
|
| `*/MOD` | CAP | `push M* pop SM/REM` |
|
||||||
|
|
||||||
|
### 5.7 Double-cell numbers
|
||||||
|
|
||||||
|
| Word | Fate | v4 definition |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `S>D` | CAP | `dup 0<` |
|
||||||
|
| `D+` | CAP | `push SWAP push over + 2DUP U> ROT drop NEGATE pop pop + +` |
|
||||||
|
| `DNEGATE` | CAP | `inv SWAP inv SWAP 1 0 D+` |
|
||||||
|
| `D-` | CAP | `DNEGATE D+` |
|
||||||
|
| `DABS` | CAP | `dup 0< IF DNEGATE THEN` |
|
||||||
|
| `D0=` | CAP | `OR 0=` |
|
||||||
|
| `D0<` | CAP | `NIP 0<` |
|
||||||
|
| `D=` | CAP | `D- D0=` |
|
||||||
|
| `D<` | CAP | `ROT 2DUP = IF 2DROP U< ELSE SWAP < NIP NIP THEN` |
|
||||||
|
| `DMAX` | CAP | `2OVER 2OVER D< IF 2SWAP THEN 2DROP` |
|
||||||
|
| `DMIN` | CAP | `2OVER 2OVER D< 0= IF 2SWAP THEN 2DROP` |
|
||||||
|
| `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` |
|
||||||
|
| `2OVER` | CAP | `push push 2DUP pop pop 2SWAP` |
|
||||||
|
| `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` | CAP | Walks the stack via `DSP` (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!` | MM | `DSP` register (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-8, v4 Q values are signed.
|
||||||
|
|
||||||
|
| Word | Fate | Notes |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `Q.+` `Q.-` | CAP | `D+`, `D-` |
|
||||||
|
| `Q.*` | CAP | 64×64 product from four `UM*` partial products, shifted right 16. |
|
||||||
|
| `Q./` | CAP | Shifted long division. **Division by zero sets an error** (v3 returned 0 silently). |
|
||||||
|
| `Q.ABS` `Q.NEG` | CAP | `DABS`, `DNEGATE` |
|
||||||
|
| `Q.LOG` `Q.EXP` `Q.SQRT` `Q.SIN` `Q.COS` | CAP | Algorithms ported from `q48_words.c`; the hosted C versions are the golden model. |
|
||||||
|
| `Q.FROM-INT` | CAP | `S>D` shifted left 16. Negative values are no longer clamped to 0. |
|
||||||
|
| `Q.TO-INT` | CAP | Shift right 16, take the low cell. |
|
||||||
|
| `Q.1` `Q.0` `Q.SCALE` | IN | Double-cell constants. |
|
||||||
|
| `Q.=` `Q.<` `Q.>` `Q.0=` `Q.MAX` `Q.MIN` | CAP | `D=`, `D<`, `SWAP D<` (for `Q.>`), `D0=`, `DMAX`, `DMIN`. Signed. |
|
||||||
|
| `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 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `DSP` | R/W | Data stack pointer (D-2). |
|
||||||
|
| `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. |
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
# StarForth v4.0.0 — Justification
|
||||||
|
|
||||||
|
This document records why StarForth v4.0.0 exists, what it changes, and why each major decision was
|
||||||
|
made. The specification is `DECOMPOSITION.md`. Per project practice, this document is written before
|
||||||
|
any v4 code.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. The problem with v3
|
||||||
|
|
||||||
|
StarForth v3 is a successful software machine. It boots on amd64, aarch64, and riscv64, runs the
|
||||||
|
Tripod fleet on LithosAnanke, and has held K≡1.0 across 38,400+ experimental runs. But it was designed
|
||||||
|
for a large host, and it shows:
|
||||||
|
|
||||||
|
- **More than 300 C primitives.** Most are not primitive in any hardware sense. Double-cell arithmetic,
|
||||||
|
string handling, comparisons, pictured output, and Q48.16 transcendentals are all expressible in a
|
||||||
|
handful of machine operations.
|
||||||
|
- **64-bit cells and 5 MB of linear memory per VM.** Reasonable on a PC; far too large for a node in a
|
||||||
|
fabric.
|
||||||
|
- **Diagnostics of its own implementation.** A significant block of words (hot-words cache statistics,
|
||||||
|
lookup strategies, pipelining metrics, Bayesian cache models) measures v3's software dictionary, not
|
||||||
|
the computation the dictionary performs.
|
||||||
|
- **Hermes in software.** Message routing, per-message ACL checks, and TTL expiry are C code executed by
|
||||||
|
a CPU that is also doing everything else.
|
||||||
|
|
||||||
|
None of this is wrong for a hosted or bare-metal OS. It is wrong for silicon. The project's direction
|
||||||
|
is now an FPGA embodiment, and eventually an ASIC, and v3 cannot be carried there by porting.
|
||||||
|
|
||||||
|
## 2. What v4 is
|
||||||
|
|
||||||
|
StarForth v4 is a Forth machine designed to be the same thing in software and in hardware:
|
||||||
|
|
||||||
|
1. **A 32-instruction core ISA** derived from Chuck Moore's F18, the node of the GA144. Every core word
|
||||||
|
is a mnemonic; every mnemonic is one 5-bit opcode.
|
||||||
|
2. **Everything else is capsule code**, compiled from those 32 instructions, or a message to a node that
|
||||||
|
owns a service, or a memory-mapped register, or retired.
|
||||||
|
3. **A mesh of small nodes** that talk to their four neighbours through blocking ports. Hermes becomes
|
||||||
|
the network itself: routing, ACL checks, and TTL expiry move into logic in every node's router.
|
||||||
|
4. **Compudynamics as a side effect of execution.** Heat counters, the anti-clock, and the heartbeat are
|
||||||
|
driven by instruction retirement in hardware. They cost no instructions.
|
||||||
|
5. **A power-aware governor** built from a multi-level Rolling Window of Truth, controlling timing only.
|
||||||
|
|
||||||
|
v4 is a new implementation, not a refactor. v3 remains the reference system for LithosAnanke until v4
|
||||||
|
reaches parity.
|
||||||
|
|
||||||
|
## 3. Why Moore's F18 instruction set
|
||||||
|
|
||||||
|
**It is proven minimal.** Moore spent decades removing instructions from his stack machines. The F18's
|
||||||
|
32 opcodes are the result: enough to build a complete Forth, nothing that can be composed from the
|
||||||
|
rest. There is no multiply, no divide, no compare, and no `SWAP`; each is a short sequence (for example
|
||||||
|
`SWAP` is `over push push drop pop pop`).
|
||||||
|
|
||||||
|
**It fits a 32-bit word exactly.** 32 opcodes need 5 bits. Six slots fill 30 bits of a 32-bit
|
||||||
|
instruction word, with 2 spare. One fetch feeds six instructions.
|
||||||
|
|
||||||
|
**It matches the project's formal-verification plan.** Proving 32 instruction semantics in Isabelle/HOL
|
||||||
|
is a bounded task. Proving 300 C primitives is not. Every higher word then inherits correctness from its
|
||||||
|
definition, which is itself a checkable object.
|
||||||
|
|
||||||
|
**It matches the dictionary-shrink plan that was already underway.** The existing POST suite, which
|
||||||
|
exercises every dictionary word, was to be used as a regression gate while C primitives were replaced by
|
||||||
|
colon definitions. v4 carries that plan to its end point: the surviving primitives are the ISA.
|
||||||
|
|
||||||
|
**It comes with a mesh precedent.** The GA144 places 144 F18 nodes on one die, each talking to its
|
||||||
|
neighbours through blocking ports. v4 adopts that topology directly.
|
||||||
|
|
||||||
|
## 4. Why a mesh, and why Hermes goes into the fabric
|
||||||
|
|
||||||
|
v3's Tripod is several VMs sharing one CPU, with Hermes arbitrating messages between them in software.
|
||||||
|
The mesh replaces time-sharing with space: each VM role runs on its own node or group of nodes,
|
||||||
|
concurrently.
|
||||||
|
|
||||||
|
Moving Hermes into the fabric has three consequences:
|
||||||
|
|
||||||
|
- **The message semantics become hardware.** Per-message ACL checks and unconditional TTL expiry, which
|
||||||
|
v3 already treats as rules rather than options, become router logic that cannot be bypassed.
|
||||||
|
- **Contention disappears as a scheduling problem.** A blocking port is flow control. There is no
|
||||||
|
scheduler to write, which honours the existing design goal of avoiding one.
|
||||||
|
- **The SOS mechanism generalises.** Any node can emit an `SOS` packet. A node whose router fails can
|
||||||
|
only stop forwarding, which its neighbours detect as blocked ports; this is the hardware form of v3's
|
||||||
|
"Hermes raises a semaphore while sinking" rule.
|
||||||
|
|
||||||
|
## 5. Why cell width becomes a parameter
|
||||||
|
|
||||||
|
The Zynq-7000's processing system is a Cortex-A9, a 32-bit ARMv7-A core. Rather than maintain a separate
|
||||||
|
32-bit fork, v4 makes cell width a build parameter of one VM (32 or 64). This has three benefits:
|
||||||
|
|
||||||
|
1. **The ARM becomes a real StarForth host**, not just a bootloader, at 32 bits.
|
||||||
|
2. **Mesh nodes use 32-bit cells**, roughly halving stack and ALU cost in the fabric.
|
||||||
|
3. **It opens a second invariance axis.** K≡1.0 has been shown invariant across amd64, aarch64, and
|
||||||
|
riscv64. If it also holds across cell widths, the conservation law is shown not to depend on word
|
||||||
|
size either. That is a stronger claim than ISA invariance alone.
|
||||||
|
|
||||||
|
The physics does not shrink with the cell. Heat and K arithmetic remain 64-bit (`int64_t` in C99 on
|
||||||
|
every host; double cells on a 32-bit node). Changing only the payload width keeps the experiment clean:
|
||||||
|
any difference in K can be attributed to cell width and not to lost precision.
|
||||||
|
|
||||||
|
## 6. Why the physics splits into "what" and "when"
|
||||||
|
|
||||||
|
The fabric can measure real power: the Zynq's XADC reads on-die temperature and supply voltages, and a
|
||||||
|
current sensor on the core rail gives true power draw. This makes "heat" a physical quantity rather than
|
||||||
|
a metaphor.
|
||||||
|
|
||||||
|
Physical measurements are noisy and never reproducible run to run. If they controlled which code
|
||||||
|
executes, parity hashes would break and formal proofs of behaviour would become impossible. v4
|
||||||
|
therefore splits the physics:
|
||||||
|
|
||||||
|
| Layer | Driven by | Controls | Property |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| **Virtual heat** | Instruction retirement and call counts | What executes (selection, promotion, eviction) | Deterministic and provable |
|
||||||
|
| **Physical power** | XADC and rail current | When things happen (clock gating, node sleep, message pacing) | Adaptive, never affects results |
|
||||||
|
|
||||||
|
This settles a question left open in the original FPGA concept: whether compudynamic feedback into the
|
||||||
|
control unit should affect only timing or also the execution path. The answer is both, through separate
|
||||||
|
channels: logic chooses *what*, physics chooses *when*.
|
||||||
|
|
||||||
|
## 7. Why the governor is a multi-level Rolling Window of Truth
|
||||||
|
|
||||||
|
The timing governor uses the project's own Rolling Window of Truth mechanism at three timescales:
|
||||||
|
|
||||||
|
| Window | Timescale | Governs |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Short | microseconds | Clock gating on one node |
|
||||||
|
| Medium | milliseconds | Node sleep and wake |
|
||||||
|
| Long | seconds | Thermal trend and mesh-wide message pacing |
|
||||||
|
|
||||||
|
Positive feedback (rising message load) wakes neighbouring nodes and raises the clock. Negative feedback
|
||||||
|
(rising temperature or power) throttles pacing and puts cool nodes to sleep. Each level reacts much more
|
||||||
|
slowly than the one below it, so the loops do not fight; hysteresis at each level prevents flapping at
|
||||||
|
thresholds. Hard limits (thermal ceiling, minimum clock) sit outside the adaptive layer as fixed logic.
|
||||||
|
|
||||||
|
A small neural network is a later candidate. Because the governor only controls timing, a poor governor
|
||||||
|
costs power or speed and never correctness, so it is a safe place to experiment. The DoE recorder
|
||||||
|
(below) produces exactly the training data such a network would need, so the two approaches can be
|
||||||
|
compared on identical workloads.
|
||||||
|
|
||||||
|
## 8. Division of labour on the Zynq
|
||||||
|
|
||||||
|
| Component | Runs on | Role |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Mesh nodes | Fabric | All StarForth execution, the anti-clock, heat counters, routers |
|
||||||
|
| Governor | Fabric | Multi-level RWT, single clock domain, cycle-exact |
|
||||||
|
| Host node | ARM (32-bit) | Boot and bitstream load, compiler capsule, console bridge, DoE recorder |
|
||||||
|
|
||||||
|
The anti-clock stays in the fabric because it is defined as a pure function of the execution stream and
|
||||||
|
must live where execution happens. The heartbeat's adaptive loop stays in the fabric because a loop
|
||||||
|
crossing the PS–PL boundary would inherit ARM-side jitter (caches, interrupts, bus latency).
|
||||||
|
|
||||||
|
The ARM's recorder role keeps measurement separate from the thing being measured: the fabric pushes
|
||||||
|
DoE rows into a FIFO, the ARM drains them to storage, and if the ARM falls behind rows are dropped
|
||||||
|
rather than execution stalled. This is the fabric form of the planned `HB-ON`/`HB-OFF` disk recording.
|
||||||
|
|
||||||
|
## 9. Why the compiler lives on the host node
|
||||||
|
|
||||||
|
GA144 nodes have 64 words of RAM and 64 of ROM, and arrayForth compiles on a host. v4 follows the same
|
||||||
|
split. The outer interpreter, dictionary, vocabularies, and defining words form the compiler capsule,
|
||||||
|
which runs on the host node. Mesh nodes receive compiled code. Large capsules stay in DDR and are
|
||||||
|
streamed to nodes as needed, so capsule size is not limited by node memory.
|
||||||
|
|
||||||
|
This is also why so many v3 words become CC rather than CAP in `DECOMPOSITION.md`: they are compiler
|
||||||
|
machinery, not computation.
|
||||||
|
|
||||||
|
## 10. Development path
|
||||||
|
|
||||||
|
Each stage is checked against the one before it. Nothing proceeds on trust.
|
||||||
|
|
||||||
|
1. **Hosted golden model.** A C99 implementation of the v4 ISA and node model, with cell width, node
|
||||||
|
count, and node memory as parameters. The POST suite, rewritten against v4 capsules, must pass at
|
||||||
|
both 32 and 64 bits, and K≡1.0 must hold.
|
||||||
|
2. **Hosted mesh.** Several golden-model nodes wired through simulated ports, running the Tripod roles
|
||||||
|
as nodes. The 144-node configuration is exercised here, since the host is not limited by fabric size.
|
||||||
|
3. **Co-simulation.** The node RTL is compiled with Verilator and run in lockstep with the golden model.
|
||||||
|
After every instruction, stacks, registers, and heat counters are compared. The first mismatch
|
||||||
|
identifies the faulty mnemonic exactly.
|
||||||
|
4. **FPGA.** A 2×2 mesh on the PZ7020, then the largest grid that fits. The bitstream only has to match
|
||||||
|
the co-simulation.
|
||||||
|
5. **ASIC.** A single v4 node, not the mesh, as a proof of silicon through an open-source shuttle
|
||||||
|
(currently Tiny Tapeout on IHP's SG13G2 130 nm open PDK). The same RTL is reused; block RAM is
|
||||||
|
replaced by the process's SRAM macros.
|
||||||
|
|
||||||
|
## 11. Scaling beyond the PZ7020
|
||||||
|
|
||||||
|
Node count, node memory, and cell width are parameters, and the mesh is generated by a loop over rows
|
||||||
|
and columns, so a larger board changes numbers, not design.
|
||||||
|
|
||||||
|
| Part | Approximate resources | Estimated nodes |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Zynq-7020 | ~53K LUTs, 140 BRAM36 | ~8–16 |
|
||||||
|
| Zynq-7045 | ~218K LUTs, 545 BRAM36 | ~50–70 |
|
||||||
|
| Zynq UltraScale+ (e.g. Kria K26) | ~117K LUTs, 144 BRAM36, 64 UltraRAM | ~30–40, with much larger node memory |
|
||||||
|
| Larger UltraScale+ / Versal | Several hundred K LUTs and up | A full 144 |
|
||||||
|
|
||||||
|
Node counts are estimates. The first hardware measurement to take is the LUT cost of one node plus its
|
||||||
|
router on the 7020; every other board's capacity follows from that number.
|
||||||
|
|
||||||
|
UltraScale+ parts also change the host: their Cortex-A53 cores are aarch64, so the host node can run
|
||||||
|
64-bit StarForth while the mesh runs 32-bit cells, which the cell-width parameter already supports.
|
||||||
|
Larger meshes will need registered router-to-router links to close timing, and the free edition of
|
||||||
|
Vivado supports only smaller devices, so tool licensing must be checked before choosing a board.
|
||||||
|
|
||||||
|
## 12. Risks
|
||||||
|
|
||||||
|
| Risk | Mitigation |
|
||||||
|
| --- | --- |
|
||||||
|
| Capsule-level arithmetic is much slower than v3's C primitives on a hosted build. | Accepted. v4's measure of performance is the fabric, where each instruction is one cycle. The hosted build is a correctness oracle. |
|
||||||
|
| Word addressing makes byte and string operations expensive. | D-1 in `DECOMPOSITION.md` keeps the choice open; colorForth's packed, pre-parsed source is a proven alternative for text. |
|
||||||
|
| K≡1.0 may behave differently at 32-bit cell width. | That is an experimental result either way, and the hosted golden model finds it before any hardware exists. |
|
||||||
|
| Hand-traced definitions contain errors. | Every CAP definition is a POST target against the v3 C primitive it replaces. |
|
||||||
|
| The mesh does not fit the 7020 at a useful size. | Measure one node first; the design scales to larger parts unchanged. |
|
||||||
|
|
||||||
|
## 13. Relationship to intellectual property
|
||||||
|
|
||||||
|
v4 strengthens rather than replaces the existing claims. The Jacquard Selector, the Rolling Window of
|
||||||
|
Truth, and the Steady State Machine all survive, now as hardware structures. The new elements a filing
|
||||||
|
could draw on are: compudynamic heat as a zero-cost side effect of instruction retirement; the split of
|
||||||
|
deterministic virtual heat (selection) from physical power (timing); per-packet ACL and TTL enforcement
|
||||||
|
in a mesh router; and conservation invariance across cell width. Whether any of these belong in the
|
||||||
|
LithosAnanke filing is a question for counsel.
|
||||||
|
|
||||||
|
## 14. Definition of done for v4.0.0
|
||||||
|
|
||||||
|
- The 32-instruction ISA is specified, with every open decision in `DECOMPOSITION.md` §3 settled.
|
||||||
|
- The hosted golden model passes the rewritten POST suite at 32-bit and 64-bit cell widths.
|
||||||
|
- K≡1.0 holds on the golden model at both widths, on all three host ISAs.
|
||||||
|
- A hosted mesh runs the Tripod roles as nodes, with Hermes as the network.
|
||||||
|
- Verilator co-simulation of one node matches the golden model instruction for instruction.
|
||||||
Reference in New Issue
Block a user