From 2fcc468ecb519b26f92e51e28987108334198473 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 05:19:09 +0000 Subject: [PATCH] docs: add StarForth primitive word reference Lists every C-registered StarForth primitive with stack notation and usage notes, grouped by module, plus a section on implementation quirks. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BY9HMwK5Cetz3caBgHGyds --- docs/STARFORTH_PRIMITIVES.md | 787 +++++++++++++++++++++++++++++++++++ 1 file changed, 787 insertions(+) create mode 100644 docs/STARFORTH_PRIMITIVES.md diff --git a/docs/STARFORTH_PRIMITIVES.md b/docs/STARFORTH_PRIMITIVES.md new file mode 100644 index 00000000..34488266 --- /dev/null +++ b/docs/STARFORTH_PRIMITIVES.md @@ -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 (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" -- )` | 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 `. | +| `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 `~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 `~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 +`" (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.