docs: reorganise -- current reference gathered, finished work archived, superseded and dead ideas marked as such
docs/CLAUDE.md Phase 2, 2026-10-14. All moves by git mv; every archived file carries a header saying what it is, why, and where to look instead. - docs/reference/ (new): roadmap and versioning policy, M7.1, word-ACL design, kernel args, hosted acceptance test, dictionary, primitives, ontology, L8 table -- previously spread over docs/, docs/lithosananke/ and docs/03-architecture/. - docs/working/archive/historical/: finished work (session plan, test-gap audit, birthing plan, fix write-ups, the January architecture draft). - docs/working/archive/superseded/: the Hermes-VM Tripod docs and July Hermes designs (-> FABRIC-3.5/3.6), the StarForth roadmap, every working doc already flagged superseded or obsolete. - docs/working/archive/dead/: the hal_* target contract (never adopted), L4Re, metrics-and-knobs. - Papers and proposals into docs/working/papers/. - References rewritten across the repo (not logs/, not docs/patent/); CLAUDE.md's documentation section rewritten. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1a4b163861
commit
7aed588f78
@@ -0,0 +1,726 @@
|
||||
# LithosAnanke / StarForth — Word Dictionary Reference
|
||||
|
||||
**Status:** Living reference document, generated 2026-08-12. Keep it up to date as words
|
||||
are added, removed, or re-registered — see "Keeping this current" at the bottom.
|
||||
|
||||
**Scope: core C primitives only.** This covers every word registered via `register_word()`
|
||||
(and the one file that uses `vm_create_word()` directly, `physics_pipelining_diagnostic_words.c`)
|
||||
across `src/word_source/*.c` and `src/starkernel/capsule/mama_forth_words.c` — roughly 470
|
||||
words. It does **not** cover the ~330 FORTH-defined words living inside `.4th` capsules
|
||||
(`ACL.4th`, `zuse.4th`, `doe.4th`, workload capsules, etc.) — those are experiment/workload/
|
||||
policy definitions layered on top of this vocabulary, not the language itself, and change
|
||||
per-capsule rather than per-build. If a capsule-word reference is wanted later, it belongs in
|
||||
a separate document — the two have very different lifecycles.
|
||||
|
||||
**Source of truth.** Every entry below was extracted directly from source: the word name as
|
||||
passed to `register_word()`, the stack effect and description from the doc comment
|
||||
immediately above the implementing function (or inferred from the function body where no
|
||||
comment exists, marked `(inferred)`). Registration order within each file is preserved
|
||||
(rather than alphabetized) because related words are grouped together at the call site.
|
||||
|
||||
**Two name collisions exist in the live dictionary.** FORTH dictionary lookup walks the
|
||||
definition chain newest-first (`vm->latest` backward via `->link`), so when two files
|
||||
register the same name, **the file registered later in `register_forth79_words()`
|
||||
(`src/word_registry.c`) wins** — the earlier registration becomes permanently unreachable by
|
||||
name (though still present in the chain). Both cases are noted at point of use below:
|
||||
- `[`, `]`, `STATE` — registered by `dictionary_manipulation_words.c` (Module 13), then again
|
||||
by `defining_words.c` (Module 17). **`defining_words.c`'s versions are live.**
|
||||
- `MOD`, `/MOD`, `*/`, `*/MOD` — registered by `arithmetic_words.c` (Module 4), then again by
|
||||
`mixed_arithmetic_words.c` (Module 6). **`mixed_arithmetic_words.c`'s versions are live.**
|
||||
|
||||
---
|
||||
|
||||
## Stack (`stack_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `DROP` | `( n -- )` | Removes the top stack item. |
|
||||
| `DUP` | `( n -- n n )` | Duplicates the top stack item. |
|
||||
| `?DUP` | `( n -- n n \| n -- 0 )` | Duplicates the top item only if it is non-zero. |
|
||||
| `SWAP` | `( n1 n2 -- n2 n1 )` | Exchanges the top two stack items. |
|
||||
| `OVER` | `( n1 n2 -- n1 n2 n1 )` | Copies the second stack item to the top. |
|
||||
| `ROT` | `( n1 n2 n3 -- n2 n3 n1 )` | Rotates the top three stack items, moving the third to the top. |
|
||||
| `-ROT` | `( n1 n2 n3 -- n3 n1 n2 )` | Reverse-rotates the top three stack items. |
|
||||
| `DEPTH` | `( -- n )` | Pushes the current number of items on the data stack. |
|
||||
| `PICK` | `( n -- stack[n] )` | Copies the nth stack item (0-indexed from top, after popping `n`) to the top. |
|
||||
| `ROLL` | `( n -- )` | Moves the nth stack item to the top, shifting intermediate items down. |
|
||||
|
||||
## Return Stack (`return_stack_words.c`)
|
||||
|
||||
FORTH-79 compliance, stated in this file's own header: only `>R`/`R>`/`R@` are provided — no
|
||||
direct return-stack addressing (`RP!`/`RP@`), which FORTH-79 forbids.
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `>R` | `( x -- )` | Moves x from the data stack to the return stack. |
|
||||
| `R>` | `( -- x )` | Moves x from the return stack back to the data stack. |
|
||||
| `R@` | `( -- x )` | Copies the top of the return stack to the data stack without removing it. |
|
||||
|
||||
## Memory (`memory_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `@` | `( addr -- n )` | Fetches a cell from VM memory at `addr`. |
|
||||
| `!` | `( n addr -- )` | Stores cell `n` at VM memory address `addr`. |
|
||||
| `C@` | `( addr -- c )` | Fetches a byte from VM memory at `addr`. |
|
||||
| `C!` | `( c addr -- )` | Stores byte `c` at VM memory address `addr`. |
|
||||
| `+!` | `( n addr -- )` | Adds `n` to the cell currently at `addr`. |
|
||||
| `-!` | `( n addr -- )` | Subtracts `n` from the cell currently at `addr`. |
|
||||
| `2@` | `( addr -- x_low x_high )` | Fetches two consecutive cells starting at `addr`. |
|
||||
| `2!` | `( x_low x_high addr -- )` | Stores two consecutive cells starting at `addr`. |
|
||||
| `FILL` | `( addr len c -- )` | Fills `len` bytes starting at `addr` with byte value `c`. |
|
||||
| `MOVE` | `( addr1 addr2 len -- )` | Copies `len` bytes from `addr1` to `addr2`, handling overlap correctly. |
|
||||
| `ERASE` | `( addr len -- )` | Zeroes `len` bytes starting at `addr`. |
|
||||
| `CELLS` | `( n -- n' )` | Multiplies `n` by the cell size in bytes. |
|
||||
|
||||
## Arithmetic (`arithmetic_words.c`)
|
||||
|
||||
`MOD`, `/MOD`, `*/`, `*/MOD` below are **shadowed** — see the collision note at the top of
|
||||
this document; `mixed_arithmetic_words.c`'s versions are the ones actually live.
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `+` | `( n1 n2 -- n3 )` | Adds `n1` and `n2`. |
|
||||
| `-` | `( n1 n2 -- n3 )` | Subtracts `n2` from `n1`. |
|
||||
| `*` | `( n1 n2 -- n3 )` | Multiplies `n1` by `n2`. |
|
||||
| `/` | `( n1 n2 -- n3 )` | Divides `n1` by `n2`; errors on division by zero. |
|
||||
| `MOD` *(shadowed)* | `( n1 n2 -- n3 )` | `n1` modulo `n2`; errors on division by zero. |
|
||||
| `/MOD` *(shadowed)* | `( n1 n2 -- n3 n4 )` | Pushes `n1 MOD n2` (remainder) then `n1 / n2` (quotient). |
|
||||
| `*/` *(shadowed)* | `( n1 n2 n3 -- n4 )` | Computes `n1 * n2 / n3` using a 64-bit intermediate to avoid overflow. |
|
||||
| `*/MOD` *(shadowed)* | `( n1 n2 n3 -- n4 n5 )` | Computes `n1 * n2 / n3`, pushing remainder then quotient, via a 64-bit intermediate. |
|
||||
| `1+` | `( n -- n+1 )` | Adds 1. |
|
||||
| `1-` | `( n -- n-1 )` | Subtracts 1. |
|
||||
| `2+` | `( n -- n+2 )` | Adds 2. |
|
||||
| `2-` | `( n -- n-2 )` | Subtracts 2. |
|
||||
| `2*` | `( n -- n*2 )` | Multiplies by 2 (left shift). |
|
||||
| `2/` | `( n -- n/2 )` | Divides by 2 (right shift). |
|
||||
| `ABS` | `( n -- \|n\| )` | Absolute value. |
|
||||
| `NEGATE` | `( n -- -n )` | Two's-complement negation. |
|
||||
| `MIN` | `( n1 n2 -- n3 )` | Pushes the smaller of `n1` and `n2`. |
|
||||
| `MAX` | `( n1 n2 -- n3 )` | Pushes the larger of `n1` and `n2`. |
|
||||
|
||||
## Logical & Comparison (`logical_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `AND` | `( n1 n2 -- n3 )` | Bitwise AND. |
|
||||
| `OR` | `( n1 n2 -- n3 )` | Bitwise OR. |
|
||||
| `XOR` | `( n1 n2 -- n3 )` | Bitwise XOR. |
|
||||
| `NOT` | `( flag -- flag )` | FORTH-79 logical NOT: 0 → true (-1), non-zero → false (0). |
|
||||
| `INVERT` | `( n1 -- n2 )` | Bitwise complement (FORTH-83 extension). |
|
||||
| `LSHIFT` | `( x1 u -- x2 )` | Logical left shift by `u` bits. |
|
||||
| `RSHIFT` | `( x1 u -- x2 )` | Logical right shift by `u` bits. |
|
||||
| `0=` | `( n -- flag )` | True if `n` is zero. |
|
||||
| `0<` | `( n -- flag )` | True if `n` is negative. |
|
||||
| `0>` | `( n -- flag )` | True if `n` is positive. |
|
||||
| `0<>` | `( n -- flag )` | True if `n` is non-zero. |
|
||||
| `=` | `( n1 n2 -- flag )` | True if equal. |
|
||||
| `<>` | `( n1 n2 -- flag )` | True if not equal. |
|
||||
| `<` | `( n1 n2 -- flag )` | True if `n1 < n2` (signed). |
|
||||
| `>` | `( n1 n2 -- flag )` | True if `n1 > n2` (signed). |
|
||||
| `>=` | `( n1 n2 -- flag )` | True if `n1 >= n2` (signed). |
|
||||
| `<=` | `( n1 n2 -- flag )` | True if `n1 <= n2` (signed). |
|
||||
| `U<` | `( u1 u2 -- flag )` | True if `u1 < u2` (unsigned). |
|
||||
| `U>` | `( u1 u2 -- flag )` | True if `u1 > u2` (unsigned). |
|
||||
| `WITHIN` | `( n low high -- flag )` | True if `low <= n < high`. |
|
||||
| `TRUE` | `( -- flag )` | Pushes -1. |
|
||||
| `FALSE` | `( -- flag )` | Pushes 0. |
|
||||
|
||||
## Mixed Arithmetic (`mixed_arithmetic_words.c`)
|
||||
|
||||
`MOD`, `/MOD`, `*/`, `*/MOD` here are the **live** versions — see the collision note at the
|
||||
top of this document.
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `M+` | `( d_high d_low n -- d_high' d_low' )` | Adds single-cell signed `n` to double-cell `d`, with carry propagation. |
|
||||
| `M-` | `( d_high d_low n -- d_high' d_low' )` | Subtracts single-cell signed `n` from double-cell `d`, with carry propagation. |
|
||||
| `M*` | `( n1 n2 -- d_low d_high )` | Multiplies two single-cell signed integers into a double-cell result (TOS = high cell). |
|
||||
| `M/MOD` | `( d_high d_low n -- remainder quotient )` | Divides double-cell dividend `d` by single-cell divisor `n`; TOS = quotient. |
|
||||
| `MOD` | `( n1 n2 -- remainder )` | Computes `n1 % n2`; errors on division by zero. |
|
||||
| `/MOD` | `( n1 n2 -- remainder quotient )` | Divides `n1` by `n2`, leaving both remainder and quotient (TOS = quotient). |
|
||||
| `*/` | `( n1 n2 n3 -- quotient )` | Computes `(n1 * n2) / n3` using a wider intermediate to avoid overflow. |
|
||||
| `*/MOD` | `( n1 n2 n3 -- remainder quotient )` | Computes `(n1 * n2) / n3`, leaving both remainder and quotient. |
|
||||
|
||||
## Double-Precision (`double_words.c`)
|
||||
|
||||
A double is represented on the stack as `dlow dhigh` (high cell on top).
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `S>D` | `( n -- d )` | Converts a single-precision number to double, sign-extending into the high cell. |
|
||||
| `D+` | `( d1 d2 -- d3 )` | Double-precision addition with carry propagation. |
|
||||
| `D-` | `( d1 d2 -- d3 )` | Double-precision subtraction with borrow propagation. |
|
||||
| `DNEGATE` | `( d1 -- d2 )` | Double-precision two's-complement negation. |
|
||||
| `DABS` | `( d1 -- d2 )` | Double-precision absolute value. |
|
||||
| `DMAX` | `( d1 d2 -- d3 )` | Maximum of two double-precision numbers. |
|
||||
| `DMIN` | `( d1 d2 -- d3 )` | Minimum of two double-precision numbers. |
|
||||
| `D<` | `( d1 d2 -- flag )` | True if `d1 < d2` (double-precision signed compare). |
|
||||
| `D=` | `( d1 d2 -- flag )` | True if `d1 = d2`. |
|
||||
| `2DROP` | `( d -- )` | Drops a double (two cells). |
|
||||
| `2DUP` | `( d -- d d )` | Duplicates a double. |
|
||||
| `2SWAP` | `( d1 d2 -- d2 d1 )` | Swaps two doubles. |
|
||||
| `2OVER` | `( d1 d2 -- d1 d2 d1 )` | Copies the second double to the top. |
|
||||
| `2ROT` | `( d1 d2 d3 -- d2 d3 d1 )` | Rotates three doubles. |
|
||||
| `2>R` | `( d -- ) ( R: -- d )` | Moves a double to the return stack (nesting-aware: preserves the resume IP above it when inside a colon-word executor). |
|
||||
| `2R>` | `( -- d ) ( R: d -- )` | Moves a double from the return stack back to the data stack. |
|
||||
| `2R@` | `( -- d ) ( R: d -- d )` | Copies a double from the return stack without removing it. |
|
||||
| `D0=` | `( d -- flag )` | True if the double is zero. |
|
||||
| `D0<` | `( d -- flag )` | True if the double is negative. |
|
||||
| `D2*` | `( d1 -- d2 )` | Double-precision left shift by 1 (multiply by 2), carrying across cells. |
|
||||
| `D2/` | `( d1 -- d2 )` | Double-precision arithmetic right shift by 1 (divide by 2), preserving sign, carrying across cells. |
|
||||
|
||||
## Format & Numeric Output (`format_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `.` | `( n -- )` | Prints `n` in the current base, followed by a space. |
|
||||
| `.R` | `( n width -- )` | Prints `n` right-justified in a field of `width` characters. |
|
||||
| `U.` | `( u -- )` | Prints `u` as unsigned, followed by a space. |
|
||||
| `U.R` | `( u width -- )` | Prints `u` as unsigned, right-justified in a field of `width`. |
|
||||
| `D.` | `( d -- )` | Prints double-cell `d` as signed decimal (or `DOUBLE-OVERFLOW` if it doesn't fit a single cell). |
|
||||
| `D.R` | `( d width -- )` | Prints double-cell `d` right-justified in a field of `width`. |
|
||||
| `.S` | `( -- )` | Prints the entire data stack contents non-destructively, prefixed with the depth. |
|
||||
| `?` | `( addr -- )` | Fetches and prints the cell at `addr`. |
|
||||
| `DUMP` | `( addr u -- )` | Prints a hex/ASCII memory dump of `u` bytes starting at `addr`. |
|
||||
| `<#` | `( -- )` | Begins pictured numeric output conversion; resets the hold buffer. |
|
||||
| `#` | `( ud \| n -- ud2 )` | Converts one digit of the number (in the current base) into the hold buffer. |
|
||||
| `#S` | `( ud \| n -- 0 0 )` | Converts all remaining digits of the number into the hold buffer. |
|
||||
| `#>` | `( [ud] -- addr u )` | Ends pictured numeric conversion; pushes the address and length of the formatted string. |
|
||||
| `HOLD` | `( c -- )` | Prepends character `c` to the pictured-numeric hold buffer. |
|
||||
| `SIGN` | `( n -- )` | If `n` is negative, holds a `-` character for pictured output. |
|
||||
| `BASE` | `( -- addr )` | Pushes the VM address of the `BASE` variable (current numeric conversion radix). |
|
||||
| `DECIMAL` | `( -- )` | Sets `BASE` to 10. |
|
||||
| `HEX` | `( -- )` | Sets `BASE` to 16. |
|
||||
| `OCTAL` | `( -- )` | Sets `BASE` to 8. |
|
||||
|
||||
## String & Text Processing (`string_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `COUNT` | `( addr1 -- addr2 u )` | Converts a counted string (length-prefixed) at `addr1` into an address/length pair. |
|
||||
| `EXPECT` | `( addr u -- )` | Reads a line from stdin into the buffer at `addr` (max `u` bytes) and updates `SPAN` with the actual length read. |
|
||||
| `SPAN` | `( -- addr )` | Pushes the address of the `SPAN` variable, holding the character count from the last `EXPECT`/`QUERY`. |
|
||||
| `QUERY` | `( -- )` | Reads a line from stdin into the terminal input buffer (TIB), resetting `>IN` to 0 and updating `SPAN`. |
|
||||
| `TIB` | `( -- addr )` | Pushes the address of the terminal input buffer. |
|
||||
| `WORD` | `( c -- addr )` | Parses the next word from the input stream delimited by character `c`, returning the address of a counted-string scratch buffer. |
|
||||
| `(s")` | `( -- c-addr u )` | Runtime for compiled `S"` — reads an inline length-prefixed string from the threaded-code stream and pushes its address/length, advancing past it. |
|
||||
| `S"` | `( "ccc<quote>" -- c-addr u )` | Parses a quoted string literal. Interpret mode: stores it at `HERE` and pushes address/length. Compile mode: compiles `(s")` plus the inline string data. Immediate. |
|
||||
| `>IN` | `( -- addr )` | Pushes the address of the `>IN` input-stream-position variable. |
|
||||
| `SOURCE` | `( -- addr u )` | Pushes the address and length of the current input source (the TIB). |
|
||||
| `BL` | `( -- c )` | Pushes the ASCII code for space (32). |
|
||||
| `[']` | `( -- xt )` | Parses the next word from the input stream and pushes (or compiles as a literal) its execution token. Immediate. |
|
||||
| `LITERAL` | `( -- )` | Placeholder/no-op in this implementation. |
|
||||
| `[LITERAL]` | `( -- )` | Placeholder/no-op in this implementation (delegates to `LITERAL`). |
|
||||
| `CONVERT` | `( d1 addr1 -- d2 addr2 )` | Converts digit characters starting at `addr1` into a running double-precision accumulator `d1`→`d2` (simplified, base 10 only), returning the address just past the converted digits. |
|
||||
| `NUMBER` | `( addr -- n flag )` | Converts a counted string at `addr` to a number (base 10 only); flag is 1 on success, 0 on failure. |
|
||||
| `ENCLOSE` | `( addr c -- addr1 n1 n2 n3 )` | Parses a delimited field: `n1` = leading-delimiter count, `n2` = end offset of the field, `n3` = offset past trailing delimiters. |
|
||||
| `-TRAILING` | `( addr u -- addr u' )` | Trims trailing ASCII spaces from a string (direct or counted-string form auto-detected), returning the trimmed length. |
|
||||
| `CMOVE` | `( addr1 addr2 u -- )` | Copies `u` bytes from `addr1` to `addr2`, ascending address order (correct for overlap when `addr2 >= addr1`). |
|
||||
| `CMOVE>` | `( addr1 addr2 u -- )` | Copies `u` bytes from `addr1` to `addr2`, descending address order (correct for overlap when `addr2 < addr1`). |
|
||||
| `COMPARE` | `( addr1 u1 addr2 u2 -- n )` | Lexicographically compares two strings (direct or counted-string form), returns -1/0/+1. |
|
||||
| `SEARCH` | `( addr1 u1 addr2 u2 -- addr3 u3 flag )` | Finds the first occurrence of string 2 within string 1; returns the matching tail and a found flag. |
|
||||
| `SCAN` | `( addr u char -- addr' u' )` | Finds the first occurrence of `char` in the string, returning the tail starting there (or end-of-string if not found). |
|
||||
| `SKIP` | `( addr u char -- addr' u' )` | Skips leading occurrences of `char` in the string, returning the remaining tail. |
|
||||
| `BLANK` | `( addr u -- )` | Fills `u` bytes at `addr` with ASCII space (auto-detects counted-string form). |
|
||||
|
||||
## I/O & Terminal (`io_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `EMIT` | `( c -- )` | Outputs the character `c` to the terminal. |
|
||||
| `CR` | `( -- )` | Outputs a newline character. |
|
||||
| `KEY` | `( -- c )` | Reads one character from the terminal and pushes it. |
|
||||
| `?TERMINAL` | `( -- flag )` | Pushes whether input is available (stub implementation, always pushes false). |
|
||||
| `TYPE` | `( addr u -- )` | Outputs `u` characters from VM memory starting at `addr`. |
|
||||
| `SPACE` | `( -- )` | Outputs a single space character. |
|
||||
| `SPACES` | `( n -- )` | Outputs `n` space characters. |
|
||||
| `(do-string)` | `( -- )` | Runtime helper compiled by `."` — reads an inline length-prefixed string from the threaded code and prints it, advancing IP past it. Not meant to be called directly. |
|
||||
| `."` | `( "ccc<quote>" -- )` | Immediate. Interpretation: parses and prints a string up to the closing `"` directly. Compilation: compiles `(do-string)` plus the inline string data. |
|
||||
|
||||
## Block & Mass Storage (`block_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `BLOCK` | `( u -- addr )` | Returns the VM address of block `u`'s content, loading it if needed (does not mark dirty). |
|
||||
| `BUFFER` | `( u -- addr )` | Returns the VM address of block `u`'s buffer without reading its content; marks it dirty. |
|
||||
| `UPDATE` | `( -- )` | Marks the currently-loaded block (per `SCR`) dirty and syncs it to the underlying block-subsystem buffer. |
|
||||
| `BLK-CONFIRM-FORMAT` | `( lbn -- )` | Commits the low-level disk container format for the slot owning block `lbn`; until called, writes to that disk slot are refused. Must be called only by the disk's owner after classifying its content as safe. |
|
||||
| `SAVE-BUFFERS` | `( -- )` | Writes back every dirty block window slot to the block subsystem. |
|
||||
| `EMPTY-BUFFERS` | `( -- )` | Invalidates all block window slots and zeroes all user blocks, without writing back. |
|
||||
| `FLUSH` | `( -- )` | Same as `SAVE-BUFFERS` — syncs and invalidates all buffers. |
|
||||
| `LOAD` | `( u -- )` | Sets `SCR` to `u` and interprets block `u`'s content as FORTH source. |
|
||||
| `LIST` | `( u -- )` | Sets `SCR` to `u` and prints the block's content formatted as 16 lines of 64 characters with line numbers. |
|
||||
| `THRU` | `( u1 u2 -- )` | Executes `LOAD` on every block from `u1` to `u2` inclusive. |
|
||||
| `SCR` | `( -- addr )` | Pushes the VM address of the `SCR` (current block) variable. |
|
||||
| `-->` | `( -- )` | Continues interpretation on the next sequential block after the current `SCR`. |
|
||||
|
||||
## Dictionary & Compilation (`dictionary_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `HERE` | `( -- addr )` | Returns the dictionary pointer. |
|
||||
| `ALIGN` | `( -- )` | Aligns the dictionary pointer to a cell boundary. |
|
||||
| `ALLOT` | `( n -- )` | Allocates `n` bytes in the dictionary; `n` may be negative to deallocate. |
|
||||
| `,` | `( n -- )` | Compiles a single cell value into the dictionary at `HERE`. |
|
||||
| `C,` | `( c -- )` | Compiles a single byte value into the dictionary at `HERE`. |
|
||||
| `2,` | `( d -- )` | Compiles a double-cell value into the dictionary at `HERE`, low cell first. |
|
||||
| `PAD` | `( -- addr )` | Returns the VM address of the 512-byte scratch text buffer at top of memory. |
|
||||
| `SP!` | `( sp -- )` | Sets the data stack pointer; can only shrink the stack, never grow it. |
|
||||
| `SP@` | `( -- sp )` | Returns the current data stack-pointer index (top is 0). |
|
||||
| `LATEST` | `( -- addr )` | Returns the VM address near the most recent compiled definition (end of dictionary). |
|
||||
|
||||
## Dictionary Manipulation (`dictionary_manipulation_words.c`)
|
||||
|
||||
`[`, `]`, `STATE` below are **shadowed** — see the collision note at the top of this
|
||||
document; `defining_words.c`'s versions are the ones actually live.
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `[` *(shadowed)* | `( -- )` | Enters interpretation mode. |
|
||||
| `]` *(shadowed)* | `( -- )` | Enters compilation mode. |
|
||||
| `STATE` *(shadowed)* | `( -- addr )` | Pushes the address of this file's own `state_variable` (a separate static, not the VM's `vm->state_addr` used by `defining_words.c`'s `STATE`). |
|
||||
| `SMUDGE` | `( -- )` | Compile-only: intended to toggle the hidden/smudge bit of the latest word (function body is a stub/placeholder in the current source). |
|
||||
| `HIDDEN` | `( -- )` | Compile-only: sets the hidden flag on the most recently defined word. |
|
||||
| `>BODY` | `( xt -- addr )` | Converts an execution token to its data-field (body) address. |
|
||||
| `>NAME` | `( xt -- addr )` | Converts an execution token to the address of its name field. |
|
||||
| `NAME>` | `( addr -- xt )` | Converts a name-field address back to its execution token, by linear search of the dictionary. |
|
||||
| `>LINK` | `( addr -- addr )` | Pushes the address of a dictionary entry's link field. |
|
||||
| `LINK>` | `( addr -- addr )` | Dereferences a link-field address to get the next dictionary entry. |
|
||||
| `CFA` | `( addr -- xt )` | Returns the code-field address of an entry — in this implementation, the entry pointer itself. |
|
||||
| `LFA` | `( addr -- addr )` | Alias for `>LINK` — returns the link-field address. |
|
||||
| `NFA` | `( addr -- addr )` | Returns the name-field address of a dictionary entry. |
|
||||
| `PFA` | `( addr -- addr )` | Returns the parameter-field (body) address of a dictionary entry. |
|
||||
| `TRAVERSE` | `( addr n -- addr )` | Moves forward or backward across a name field by `n`. |
|
||||
| `INTERPRET` | `( -- )` | Sets interpret mode; actual interpretation is driven by the VM's own `vm_interpret()`, not this word's body. |
|
||||
| `FIND` | `( "name" -- xt \| 0 )` | Parses the next word and pushes its dictionary entry pointer, or 0 if not found (a miss is not an error). |
|
||||
| `'` | `( "name" -- xt )` | FORTH-79 tick: parses the next word and pushes its execution token; errors if not found. Non-immediate — the compiler compiles a call to it in compile mode. |
|
||||
|
||||
## Vocabulary System (`vocabulary_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `VOCABULARY` | `( -- )` | Creates a new vocabulary; executing the created word selects itself as CONTEXT. |
|
||||
| `DEFINITIONS` | `( -- )` | Sets CURRENT to CONTEXT, so new words are defined into the CONTEXT vocabulary. |
|
||||
| `CONTEXT` | `( -- addr )` | Returns the VM address of the cell holding the CONTEXT vocabulary pointer. |
|
||||
| `CURRENT` | `( -- addr )` | Returns the VM address of the cell holding the CURRENT vocabulary pointer. |
|
||||
| `FORTH` | `( -- )` | Makes FORTH the CONTEXT vocabulary. |
|
||||
| `ORDER` | `( -- )` | Prints the current search order (CONTEXT then FORTH) and CURRENT. |
|
||||
| `(FIND)` | `( addr -- addr flag )` | Primitive finder: searches CONTEXT then FORTH vocabularies for the counted string at `addr`. |
|
||||
|
||||
## System & Environment (`system_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `(` | `( -- )` | Begins a comment; skips input up to the matching `)`, honoring nested parens. Immediate. |
|
||||
| `\` | `( -- )` | Line comment; skips input to the end of the current line. Immediate. |
|
||||
| `COLD` | `( -- )` | Performs a cold start: resets VM state and rewinds `HERE` toward the base dictionary. |
|
||||
| `WARM` | `( -- )` | Performs a warm restart: resets stacks/mode/error without rewinding `HERE`. |
|
||||
| `BYE` | `( -- )` | Halts the VM, signalling the REPL to stop. (Overridden for Hera — see Kernel: Capsule Birth & Tripod Fleet below.) |
|
||||
| `REBOOT` | `( addr len -- )` | Sets boot arguments from the given string and performs a cold reset (kernel target) or prints a hosted-stub message and halts. Interpret-only. |
|
||||
| `SAVE-SYSTEM` | `( -- )` | Writes a trivial snapshot of VM memory (up to `HERE`) to `forth_system.img`. |
|
||||
| `WORDS` | `( -- )` | Lists every word name currently in the dictionary, 8 per line, with a total count. |
|
||||
| `VLIST` | `( -- )` | Lists every dictionary entry with its name, address, and flags byte. |
|
||||
| `SEE` | `( "name" -- )` | Decompiles and prints the named word's definition (threaded code, or `<primitive>` for C words). |
|
||||
| `PAGE` | `( -- )` | Clears the terminal screen via an ANSI escape sequence. |
|
||||
| `EXECUTE` | `( xt -- )` | Executes the word whose execution token is on the stack. |
|
||||
| `NOP` | `( -- )` | Does nothing. |
|
||||
| `79-STANDARD` | `( -- flag )` | Prints and pushes whether FORTH-79 standard-compliance mode is active (-1) or not (0). |
|
||||
| `QUIT` | `( -- )` | Clears the return stack and returns to the interpreter, without treating it as an error. Immediate. |
|
||||
| `ABORT` | `( -- )` | Clears both stacks and returns to the interpreter; not treated as an error condition. |
|
||||
| `(ABORT")` | `( flag addr len -- )` | Runtime helper for `ABORT"`: if `flag` is nonzero, prints the message at `addr len` and performs `ABORT` semantics. |
|
||||
| `ABORT"` | `( flag -- )` | Parses a message up to the next `"`; if `flag` is nonzero (interpret mode) or at run time (compiled), prints it and aborts. Immediate. |
|
||||
|
||||
## Line Editor (`editor_words.c`)
|
||||
|
||||
Non-standard (not FORTH-79).
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `L` | `( u -- )` | Prints line `u` (0-15) of the current SCR block. |
|
||||
| `S` | `( c-addr len u -- )` | Sets line `u` of the current SCR block from a buffer, padding with spaces to 64 chars. |
|
||||
| `SHOW` | `( -- )` | Prints the whole 16x64 current screen with line numbers. |
|
||||
| `EDIT` | `( u -- )` | Enters a tiny interactive line-editor shell (stdin/stdout) on block `u`. |
|
||||
|
||||
## Defining Words (`defining_words.c`)
|
||||
|
||||
`[`, `]`, `STATE` here are the **live** versions — see the collision note at the top of this
|
||||
document.
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `:` | `( "name" -- )` | Begins a colon definition; parses the name and enters compile mode. Immediate. |
|
||||
| `;` | `( -- )` | Ends a colon definition, compiling an implicit `EXIT` and returning to interpret mode. Immediate. |
|
||||
| `CREATE` | `( "name" -- )` | Defines a new word whose runtime pushes its data-field address (FORTH-79: allocates no data itself). |
|
||||
| `VARIABLE` | `( "name" -- )` | Defines a new word that allocates one cell (initialized to 0) and whose runtime pushes that cell's address. |
|
||||
| `CONSTANT` | `( n "name" -- )` | Defines a new word whose runtime pushes the fixed value `n`. |
|
||||
| `IMMEDIATE` | `( -- )` | Marks the most recently defined word as IMMEDIATE (executes during compilation instead of being compiled in). |
|
||||
| `STATE` | `( -- addr )` | Pushes the VM address of the `STATE` variable (0 = interpreting, nonzero = compiling). |
|
||||
| `[` | `( -- )` | Enters interpret state (sets `STATE` to 0). Immediate. |
|
||||
| `]` | `( -- )` | Enters compile state (sets `STATE` to -1). |
|
||||
| `FORGET` | `( "name" -- )` | Removes the named word and every word defined after it from the dictionary, rewinding `HERE`. |
|
||||
| `COMPILE` | `( "word" -- )` | Legacy immediate word: parses the next word and compiles a call to it. Immediate. |
|
||||
| `[COMPILE]` | `( "word" -- )` | Compiles the next word even if it is itself IMMEDIATE. Immediate. |
|
||||
| `LIT` | `( -- n )` | Runtime primitive: fetches the next cell from threaded code (via the return-stack IP) and pushes it as a literal. |
|
||||
| `LITERAL` | `( n -- )` | Compiles `n` as a literal into the current definition (via `LIT`). Immediate. |
|
||||
| `does_rt` | `( -- )` *(inferred)* | Internal helper: patches the just-created child word to use the `DOES>` runtime and records the `DOES>` body address in its parameter field. Not meant to be called directly from FORTH. |
|
||||
| `DOES>` | `( -- )` | Finalizes a defining word's CREATE-part and compiles the following code as the DOES>-body run by every word the defining word subsequently creates. Immediate, compile-only. |
|
||||
| `DEFER` | `( "name" -- )` | Creates a deferred word with an empty execution-token slot; errors if executed before `IS` sets it. |
|
||||
| `IS` | `( xt "name" -- )` | Stores execution token `xt` into a word previously created with `DEFER`. |
|
||||
| `DEFER@` | `( "name" -- xt )` | Fetches the execution token currently stored in a deferred word. |
|
||||
|
||||
## Control Flow (`control_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `(BRANCH)` | `( -- )` | Runtime: unconditional branch — reads a relative byte offset from the inline thread and adjusts the return-stack IP by it. |
|
||||
| `(0BRANCH)` | `( f -- )` | Runtime: conditional branch — pops a flag; if zero, branches by the inline relative offset, otherwise skips it. |
|
||||
| `(?DO)` | `( limit index -- )` | Runtime for `?DO` — if index equals limit the loop body is skipped entirely (branches past it); otherwise pushes (limit, index) onto the return stack as the loop frame. |
|
||||
| `(DO)` | `( limit index -- )` | Runtime for `DO` — unconditionally pushes (limit, index) onto the return stack as the loop frame. |
|
||||
| `(LOOP)` | `( -- )` | Runtime for `LOOP` — increments the loop index; if still less than the limit, branches back to the loop body, else drops the loop frame and falls through. |
|
||||
| `(+LOOP)` | `( n -- )` | Runtime for `+LOOP` — adds `n` to the loop index; continues looping while the (signed-direction-aware) index hasn't crossed the limit, else exits. |
|
||||
| `(LEAVE)` | `( -- )` | Runtime for `LEAVE` — forces the current loop to exit at the next `LOOP`/`+LOOP` by setting index equal to limit. |
|
||||
| `IF` | `( f -- )` *(compile-time)* | Compiles a conditional branch (`0BRANCH`) with a placeholder target, pushed for later patching by `ELSE`/`THEN`. Immediate. |
|
||||
| `ELSE` | `( -- )` *(compile-time)* | Patches the matching `IF`'s branch to land here, compiles an unconditional branch past the else-clause, pushed for `THEN` to patch. Immediate. |
|
||||
| `THEN` | `( -- )` *(compile-time)* | Patches the matching `IF` or `ELSE` branch to land here, closing the conditional. Immediate. |
|
||||
| `BEGIN` | `( -- )` *(compile-time)* | Marks the start of a loop (target for `UNTIL`/`AGAIN`/`REPEAT`). Immediate. |
|
||||
| `WHILE` | `( f -- )` *(compile-time)* | Compiles a conditional exit branch inside a `BEGIN...REPEAT` loop, patched by `REPEAT`. Immediate. |
|
||||
| `REPEAT` | `( -- )` *(compile-time)* | Compiles an unconditional branch back to `BEGIN` and patches `WHILE`'s exit branch to land after it. Immediate. |
|
||||
| `AGAIN` | `( -- )` *(compile-time)* | Compiles an unconditional branch back to the matching `BEGIN` (infinite loop unless exited via `LEAVE`/`EXIT`). Immediate. |
|
||||
| `UNTIL` | `( f -- )` *(compile-time)* | Compiles a conditional branch back to the matching `BEGIN`; loop repeats while the flag is false. Immediate. |
|
||||
| `?DO` | `( limit index -- )` *(compile-time)* | Compiles the `(?DO)` runtime call plus loop-frame bookkeeping for `LOOP`/`LEAVE`. Immediate. |
|
||||
| `DO` | `( limit index -- )` *(compile-time)* | Compiles the `(DO)` runtime call plus loop-frame bookkeeping for `LOOP`/`LEAVE`. Immediate. |
|
||||
| `LOOP` | `( -- )` *(compile-time)* | Compiles the `(LOOP)` runtime call, patches the back-edge to `DO`/`?DO` and any pending `LEAVE` sites. Immediate. |
|
||||
| `+LOOP` | `( n -- )` *(compile-time)* | Compiles the `(+LOOP)` runtime call, patches the back-edge to `DO`/`?DO` and any pending `LEAVE` sites. Immediate. |
|
||||
| `LEAVE` | `( -- )` *(compile-time)* | Compiles the runtime leave-flag plus a forward branch out of the enclosing loop, patched at the next `LOOP`/`+LOOP`. Immediate. |
|
||||
| `I` | `( -- i )` | Pushes the index of the innermost active `DO`/`?DO` loop. |
|
||||
| `J` | `( -- j )` | Pushes the index of the next-outer `DO`/`?DO` loop (requires nested loops). |
|
||||
| `UNLOOP` | `( -- )` | Discards the current loop's (limit, index) frame from the return stack without branching — used before an early `EXIT` from inside a loop. |
|
||||
| `EXIT` | `( -- )` | Marks the current colon definition for a one-shot early return. |
|
||||
| `CASE` | `( n -- n )` *(compile-time)* | Marks the start of a `CASE...OF...ENDOF...ENDCASE` selector statement. Immediate. |
|
||||
| `OF` | `( n1 n2 -- \| n1 )` *(compile-time)* | Compiles a compare-and-branch (`OVER = 0BRANCH DROP`) for one `CASE` clause. Immediate. |
|
||||
| `ENDOF` | `( -- )` *(compile-time)* | Ends one `OF` clause: patches its branch, compiles a jump to `ENDCASE`. Immediate. |
|
||||
| `ENDCASE` | `( n -- )` *(compile-time)* | Ends the `CASE` statement: compiles a `DROP` for the selector and patches all `ENDOF` branches to land here. Immediate. |
|
||||
|
||||
## StarForth Extensions (`starforth_words.c`)
|
||||
|
||||
Each word is registered twice — once into the root vocabulary, once again into the
|
||||
`STARFORTH` vocabulary context (`ENTROPY@`/`ENTROPY!` only registered the second time).
|
||||
Listed once below.
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `ENTROPY@` | `( addr -- n )` | Fetches the execution-heat counter for the `DictEntry` at `addr` (validated against the live dictionary chain). |
|
||||
| `ENTROPY!` | `( n addr -- )` | Sets the execution-heat counter for the `DictEntry` at `addr`. |
|
||||
| `WORD-ENTROPY` | `( -- )` | Prints execution-heat statistics (per word, total, average) for every word in the dictionary. |
|
||||
| `RESET-ENTROPY` | `( -- )` | Resets every word's execution-heat counter and compudynamics metadata (temperature, latency, last-active) to zero. |
|
||||
| `TOP-WORDS` | `( n -- )` | Prints the `n` most frequently executed words, sorted descending by execution heat. |
|
||||
| `(-` | `( -- )` | Comment word: consumes input up to a balanced closing `)` — used for `init.4th` metadata markers. |
|
||||
| `INIT` | `( -- )` | Reads `./capsules/core/init.4th`, remaps and copies its numbered blocks sequentially starting at block 1, executes them via `LOAD`, then zeroes the init blocks for userspace reuse. |
|
||||
| `VERSION` | `( -- )` | Prints the StarForth version string (`STARFORTH_VERSION_FULL`). |
|
||||
| `SEED` | `( n -- )` | Seeds the internal PRNG (LCG) with `n` (forces non-zero state). |
|
||||
| `RANDOM` | `( lo hi -- n )` | Returns a pseudo-random integer in the inclusive range `[lo, hi]`. |
|
||||
| `WAIT` | `( n -- )` | Waits `n` heartbeat ticks (calls `vm_tick()` n times) — architecture-independent, no wall-clock dependency. |
|
||||
| `ZUSE-AUTHENTICATE` | `( -- )` | Sets `vm->zuse_session = 1` — C-only superuser bypass, no FORTH-level authentication logic. |
|
||||
|
||||
## Word-Level ACL (`acl_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `ACL-MODE@` | `( xt -- mode )` | Fetches the ACL enforcement mode of the word `xt`: 0 = STRICT (permanent), 1 = TTL (expires after countdown). |
|
||||
| `ACL-PINNED?` | `( xt -- flag )` | Pushes -1 if the word `xt` is pinned (its ACL fields are immutable), 0 otherwise. |
|
||||
| `ACL-TTL@` | `( xt -- n )` | Fetches the current TTL countdown of the word's ACL entry. |
|
||||
| `ACL-ALLOW@` | `( xt -- flag )` | Fetches the cached allow/deny decision for the word: -1 = allowed, 0 = denied. |
|
||||
| `ACL-HEAT@` | `( xt -- heat )` | Fetches the execution-heat counter for the word (used to calibrate TTL). |
|
||||
| `ACL-WORD-ID` | `( xt -- n )` | Fetches the word's stable `word_id`, assigned at registration and usable as a persistent table index. |
|
||||
| `ACL-MODE!` | `( mode xt -- )` | Sets the ACL enforcement mode of word `xt`. No-op if the word is pinned. |
|
||||
| `ACL-TTL!` | `( n xt -- )` | Sets the TTL countdown on the word's ACL entry (clamped to `[0, UINT32_MAX]`). No-op if pinned. |
|
||||
| `ACL-ALLOW!` | `( flag xt -- )` | Sets the cached allow/deny decision for word `xt`. No-op if pinned. |
|
||||
| `ACL-PIN` | `( xt -- )` | Sets `acl_pinned` on word `xt`. One-way ratchet — can never be cleared once set. |
|
||||
| `ACL-INHERIT` | `( src dst -- )` | Copies `acl_mode` from `src` to `dst`, then clears `dst`'s pin, resets its TTL to 0, and sets `acl_allow` to 1 (optimistic default). |
|
||||
| `ACL-INIT-PRIMITIVES` | `( -- )` | Walks the entire dictionary and resets ACL fields (TTL=0, allow=1, mode=TTL) on every unpinned entry. |
|
||||
|
||||
## Compudynamics: Benchmark (`physics_benchmark_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `BENCH-DICT-LOOKUP` | `( iterations -- )` | Benchmarks dictionary lookup performance over a set of common words for the given iteration count; warns if iterations < 10,000 for statistical validity. |
|
||||
| `PHYSICS-CACHE-STATS` | `( -- )` | Displays detailed hot-words cache statistics and current cache contents. |
|
||||
| `PHYSICS-TOGGLE-CACHE` | `( -- )` | Enables/disables the hot-words cache at runtime for A/B testing. |
|
||||
| `PHYSICS-RESET-STATS` | `( -- )` | Resets cache and pipeline-metrics statistics for a clean before/after comparison. |
|
||||
| `PHYSICS-BUILD-INFO` | `( -- )` | Prints the current hot-words cache build configuration and runtime state. |
|
||||
| `PHYSICS-BAYESIAN-REPORT` | `( addr_baseline -- )` | Generates a Bayesian inference report comparing current cache stats against a baseline. |
|
||||
|
||||
## Compudynamics: Pipelining Diagnostics (`physics_pipelining_diagnostic_words.c`)
|
||||
|
||||
Registers via `vm_create_word()` directly rather than `register_word()` — the only file in
|
||||
the tree that does this (verified by a `vm_create_word(vm, "` sweep of the whole
|
||||
`word_source/` directory).
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `PIPELINING-SHOW-STATS` | `( c-addr u -- )` | Displays word-to-word transition metrics (prefetch attempts/hits/misses, latency saved) for the named word. |
|
||||
| `PIPELINING-SHOW-TOP-TRANSITIONS` | `( c-addr u n -- )` | Displays the top `n` words that typically follow the named word, ranked by transition count. |
|
||||
| `PIPELINING-RESET-ALL` | `( -- )` | Clears transition metrics from every word in the dictionary. |
|
||||
| `PIPELINING-ENABLE` | `( -- )` | Reports whether pipelining metrics collection is enabled (compile-time flag; this word does not toggle it). |
|
||||
| `PIPELINING-STATS` | `( -- )` | Displays aggregate pipelining statistics (transitions, prefetch hit rate) across the whole dictionary. |
|
||||
| `PIPELINING-ANALYZE-WORD` | `( c-addr u -- )` | Comprehensive analysis of one word's transition predictability (entropy/distribution spread) plus prefetch net benefit. |
|
||||
|
||||
## Compudynamics: Freeze/Decay Control (`physics_freeze_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `FREEZE-WORD` | `( caddr u -- )` | Sets `WORD_FROZEN` on the named word so its execution heat stops decaying; silently succeeds if not found. |
|
||||
| `UNFREEZE-WORD` | `( caddr u -- )` | Clears `WORD_FROZEN` on the named word, letting heat decay resume normally. |
|
||||
| `FROZEN?` | `( caddr u -- flag )` | Pushes true (-1) if the named word is frozen, false (0) otherwise or if not found. |
|
||||
| `HEAT!` | `( heat caddr u -- )` | Directly writes `execution_heat` for the named word (diagnostics; bypasses normal accumulation/decay). |
|
||||
| `HEAT@` | `( caddr u -- heat )` | Pushes the current `execution_heat` of the named word (0 if not found). |
|
||||
| `SHOW-HEAT` | `( caddr u -- )` | Prints the named word's heat plus frozen/pinned status to stdout. |
|
||||
| `ALL-HEATS` | `( -- )` | Prints a table of every dictionary word sorted by descending execution heat. |
|
||||
| `DECAY-RATE@` | `( -- rate )` | Pushes the compile-time base heat decay rate (Q48.16) per microsecond used by Loop #3. |
|
||||
| `FREEZE-CRITICAL` | `( -- )` | Freezes a hard-coded list of 21 system-critical words (`DUP`, `DROP`, `SWAP`, `IF`, `EXECUTE`, etc.) so they never decay out of the hot-words cache. |
|
||||
|
||||
## Compudynamics: General Diagnostics (`physics_diagnostic_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `PHYSICS-WORD-METRICS` | `( -- )` | Displays temperature, execution heat, latency, mass, and derived thermal pressure for the most recently executed word. |
|
||||
| `PHYSICS-CALC-KNOBS` | `( -- )` | Calculates and displays recommended priority/sampling/stack-limit/affinity adjustments based on the most recent word's thermal pressure. |
|
||||
| `PHYSICS-BURN` | `( n -- )` | Executes the most recently executed word `n` times, printing thermal feedback progress and a final summary. |
|
||||
| `PHYSICS-SHOW-FEEDBACK` | `( -- )` | Displays the full feedback-loop demonstration (metrics → math → knobs → effect) for the most recent word. |
|
||||
|
||||
## Dictionary Heat Optimization (`dictionary_heat_diagnostic_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `HEAT-PERCENTILES` | `( -- 25th 50th 75th )` | Pushes the three heat percentile thresholds used by heat-aware dictionary lookup. |
|
||||
| `LOOKUP-STRATEGY@` | `( -- strategy )` | Pushes the current lookup strategy: 0 = naive (newest-first), 1 = heat-aware. |
|
||||
| `LOOKUP-STRATEGY!` | `( strategy -- )` | Sets the lookup strategy; only 0 or 1 accepted, other values silently discarded. |
|
||||
| `REORG-BUCKETS` | `( -- )` | Forces an immediate re-sort of dictionary lookup buckets by heat and refreshes percentile thresholds. |
|
||||
| `SHOW-HEAT-OPTIMIZATION` | `( -- )` | Prints a summary of lookup strategy, percentile thresholds, and resulting hot/warm/cool zones. |
|
||||
| `COMPARE-LOOKUPS` | `( iterations -- )` | Micro-benchmarks naive vs. heat-aware dictionary lookup over six common words and prints the speedup/slowdown. |
|
||||
|
||||
## Log Level Control (`log_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `LOG-ERROR` | `( -- n )` | Pushes the `LOG_ERROR` level constant. |
|
||||
| `LOG-WARN` | `( -- n )` | Pushes the `LOG_WARN` level constant. |
|
||||
| `LOG-INFO` | `( -- n )` | Pushes the `LOG_INFO` level constant. |
|
||||
| `LOG-TEST` | `( -- n )` | Pushes the `LOG_TEST` level constant. |
|
||||
| `LOG-DEBUG` | `( -- n )` | Pushes the `LOG_DEBUG` level constant. |
|
||||
| `LOG-LEVEL!` | `( n -- )` | Sets the active log filter level (clamped to `[LOG_ERROR, LOG_DEBUG]`). |
|
||||
| `LOG-LEVEL@` | `( -- n )` | Pushes the current log filter level. |
|
||||
| `(do-log-error)` | `( -- )` | Runtime for compiled `LOG-ERROR"` — emits the inline string at ERROR level. |
|
||||
| `(do-log-warn)` | `( -- )` | Runtime for compiled `LOG-WARN"` — emits the inline string at WARN level. |
|
||||
| `(do-log-info)` | `( -- )` | Runtime for compiled `LOG-INFO"` — emits the inline string at INFO level. |
|
||||
| `(do-log-test)` | `( -- )` | Runtime for compiled `LOG-TEST"` — emits the inline string at TEST level. |
|
||||
| `(do-log-debug)` | `( -- )` | Runtime for compiled `LOG-DEBUG"` — emits the inline string at DEBUG level. |
|
||||
| `LOG-ERROR"` | `( "ccc<quote>" -- )` | Logs a quoted string literal at ERROR level (interpret: immediate; compile: compiles the runtime + inline data). Immediate. |
|
||||
| `LOG-WARN"` | `( "ccc<quote>" -- )` | Logs a quoted string literal at WARN level. Immediate. |
|
||||
| `LOG-INFO"` | `( "ccc<quote>" -- )` | Logs a quoted string literal at INFO level. Immediate. |
|
||||
| `LOG-TEST"` | `( "ccc<quote>" -- )` | Logs a quoted string literal at TEST level. Immediate. |
|
||||
| `LOG-DEBUG"` | `( "ccc<quote>" -- )` | Logs a quoted string literal at DEBUG level. Immediate. |
|
||||
| `LOG-ERROR-STR` | `( c-addr u -- )` | Logs a stack-supplied string (e.g. from `S"`) at ERROR level. |
|
||||
| `LOG-WARN-STR` | `( c-addr u -- )` | Logs a stack-supplied string at WARN level. |
|
||||
| `LOG-INFO-STR` | `( c-addr u -- )` | Logs a stack-supplied string at INFO level. |
|
||||
| `LOG-TEST-STR` | `( c-addr u -- )` | Logs a stack-supplied string at TEST level. |
|
||||
| `LOG-DEBUG-STR` | `( c-addr u -- )` | Logs a stack-supplied string at DEBUG level. |
|
||||
|
||||
## Q48.16 Fixed-Point Math (`q48_words.c`)
|
||||
|
||||
`cell_t` reinterpreted as `uint64_t`; 1.0 = 65536, resolution ≈ 0.0000153.
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `Q.+` | `( q1 q2 -- q_sum )` | Q48.16 addition. |
|
||||
| `Q.-` | `( q1 q2 -- q_diff )` | Q48.16 subtraction. |
|
||||
| `Q.*` | `( q1 q2 -- q_prod )` | Q48.16 multiplication. |
|
||||
| `Q./` | `( q1 q2 -- q_quot )` | Q48.16 division; returns `q=0` on division by zero. |
|
||||
| `Q.ABS` | `( q -- \|q\| )` | Absolute value. |
|
||||
| `Q.NEG` | `( q -- -q )` | Negation. |
|
||||
| `Q.LOG` | `( q -- ln_q )` | Natural log via Newton-Raphson approximation. |
|
||||
| `Q.EXP` | `( q -- e^q )` | Exponential via Taylor series approximation. |
|
||||
| `Q.SQRT` | `( q -- sqrt_q )` | Square root via Newton-Raphson approximation. |
|
||||
| `Q.SIN` | `( q -- sin_q )` | Sine (radians, any magnitude) via Taylor series. |
|
||||
| `Q.COS` | `( q -- cos_q )` | Cosine (radians, any magnitude) via Taylor series. |
|
||||
| `Q.FROM-INT` | `( n -- q )` | Converts integer to Q48.16 (`n << 16`). |
|
||||
| `Q.TO-INT` | `( q -- n )` | Converts Q48.16 to integer, truncating (`q >> 16`). |
|
||||
| `Q.1` | `( -- 65536 )` | Pushes 1.0 in Q48.16. |
|
||||
| `Q.0` | `( -- 0 )` | Pushes 0.0 in Q48.16. |
|
||||
| `Q.SCALE` | `( -- 65536 )` | Pushes the scale factor (alias of `Q.1`). |
|
||||
| `Q.=` | `( q1 q2 -- flag )` | -1 if equal, else 0. |
|
||||
| `Q.<` | `( q1 q2 -- flag )` | -1 if `q1 < q2`, else 0. |
|
||||
| `Q.>` | `( q1 q2 -- flag )` | -1 if `q1 > q2`, else 0. |
|
||||
| `Q.0=` | `( q -- flag )` | -1 if `q = 0`, else 0. |
|
||||
| `Q.MAX` | `( q1 q2 -- q_max )` | Maximum of two Q48.16 values. |
|
||||
| `Q.MIN` | `( q1 q2 -- q_min )` | Minimum of two Q48.16 values. |
|
||||
| `Q.PRINT` | `( q -- )` | Prints as `"integer.frac"` to the console. |
|
||||
|
||||
Note: `q48_16_words.c` (despite the similar filename) registers no FORTH words at all — it's
|
||||
a pure C support library (multiply/divide with wide intermediates) consumed by other files
|
||||
like `ttf_words.c`, not a word-registration file.
|
||||
|
||||
## SSM Inference + Jacquard (`inference_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `Q.VARIANCE` | `( addr u -- q )` | Computes the variance of `u` uint64 cells starting at `addr`, result as Q48.16. |
|
||||
| `INFER-DECAY-SLOPE` | `( addr u -- q )` | Computes the exponential decay slope of `u` cells at `addr` via linear regression, as Q48.16. |
|
||||
| `INFER-WINDOW-WIDTH` | `( addr u -- n )` | Computes the optimal window width from the variance inflection point of `u` cells at `addr`. |
|
||||
| `WINDOW-DIVERSITY` | `( -- u )` | Pushes the rolling window's diversity (entropy) metric. |
|
||||
| `INFER-RUN` | `( -- )` | Runs the full inference engine against this VM's rolling window and dictionary heat stats, updating `vm->last_inference_outputs`. |
|
||||
| `INFER-WINDOW@` | `( -- u )` | Fetches the last computed adaptive window width. |
|
||||
| `INFER-DECAY@` | `( -- q )` | Fetches the last computed adaptive decay slope (Q48.16). |
|
||||
| `INFER-VARIANCE@` | `( -- q )` | Fetches the last computed window variance (Q48.16). |
|
||||
| `INFER-FIT@` | `( -- q )` | Fetches the last computed slope fit quality (Q48.16). |
|
||||
| `INFER-EARLY-EXIT@` | `( -- flag )` | Pushes 1 if the last `INFER-RUN` used the ANOVA early-exit path. |
|
||||
| `L8-MODE` | `( -- n )` | Pushes the current L8 Jacquard mode (0-15). |
|
||||
| `L8-UPDATE` | `( entropy_q cv_q temporal_q stability_q -- )` | Feeds four Q48.16 metrics into the L8 Jacquard mode selector's update function. |
|
||||
| `L8-APPLY` | `( -- )` | Applies the current L8 mode to `vm->ssm_config` (legacy 16-mode path). |
|
||||
| `L8-TABLE-FORCE` | `( config_idx -- )` | Forces the adaptive 128-config table onto `config_idx`, as if the bandit's own selection had picked it, and applies it immediately. |
|
||||
| `BAYES-CACHE-MEAN` | `( -- q )` | Pushes the Bayesian mean latency estimate for hot-word cache hits (Q48.16). |
|
||||
| `BAYES-CACHE-LOWER` | `( -- q )` | Pushes the 95% credible lower bound for cache-hit latency. |
|
||||
| `BAYES-CACHE-UPPER` | `( -- q )` | Pushes the 95% credible upper bound for cache-hit latency. |
|
||||
| `BAYES-BUCKET-MEAN` | `( -- q )` | Pushes the Bayesian mean latency estimate for dictionary bucket searches. |
|
||||
| `BAYES-BUCKET-LOWER` | `( -- q )` | Pushes the 95% credible lower bound for bucket-search latency. |
|
||||
| `BAYES-BUCKET-UPPER` | `( -- q )` | Pushes the 95% credible upper bound for bucket-search latency. |
|
||||
|
||||
## DEFER / Late Binding (`defer_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `DEFER` | `( -- )` | Parses a name and creates a deferred word with an unset (0) data field; calling it before `IS` sets `vm->error`. |
|
||||
| `IS` | `( xt -- )` | Parses a name (which must have been created by `DEFER`) and stores `xt` as its target action, enabling late binding. |
|
||||
| `DEFER@` | `( -- xt )` | Parses a name (must be `DEFER`-created) and pushes its current target execution token. |
|
||||
|
||||
## Console Fabric — Framebuffer (`framebuffer_words.c`)
|
||||
|
||||
Kernel-only (`__STARKERNEL__`); no-ops on hosted builds. Raw hardware-boundary access —
|
||||
FABRIC-0.md item 4.3.3.
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `PLOT` | `( x y color -- )` | Raw raster pixel write at (x,y), top-left origin, Y-down. No Cartesian awareness. |
|
||||
| `FB-WIDTH` | `( -- n )` | Pushes the framebuffer width in pixels (0 on hosted builds). |
|
||||
| `FB-HEIGHT` | `( -- n )` | Pushes the framebuffer height in pixels (0 on hosted builds). |
|
||||
|
||||
## Console Fabric — Keyboard (`keyboard_words.c`)
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `KBD-SCAN` | `( -- c -1 \| 0 )` | Pops one raw scancode off the amd64 i8042 ring buffer if available; pushes 0 if empty or on other architectures. |
|
||||
| `KBD-DEBUG` | `( -- isr_count spurious_count )` | Pushes the i8042 interrupt and spurious-interrupt counters (amd64 diagnostic). |
|
||||
| `VKBD-EVENT` | `( -- code value -1 \| 0 )` | Pops one decoded EV_KEY event off the virtio-input ring buffer (riscv64/aarch64); pushes 0 if empty. |
|
||||
| `VKBD-DEBUG` | `( -- isr_count )` | Pushes the virtio-input interrupt counter (riscv64/aarch64 diagnostic). |
|
||||
| `KEY-EVENT` | `( -- keycode pressed -1 \| 0 )` | Cross-architecture converged key event: pops one keycode/pressed pair in the Linux input-keycode namespace, translated from whichever backend (i8042 or virtio-input) is live. |
|
||||
| `ALT+TAB` | `( -- )` | Programmatic equivalent of the physical Alt+Tab interception — toggles the framebuffer graphics/text overlay state. |
|
||||
|
||||
## Console Fabric — TrueType Text (`ttf_words.c`)
|
||||
|
||||
Kernel-only (`__STARKERNEL__`); no-op on hosted builds. FABRIC-0.md item 4.3.7e.
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `TTF-TEXT` | `( c-addr u x y size color -- )` | Renders a UTF-8 string via the TrueType rasterizer at Cartesian position (x,y) (origin bottom-left of the framebuffer) with the given point size and color. |
|
||||
|
||||
## Console Fabric — Scrollback (`scroll_words.c`)
|
||||
|
||||
Kernel-only (`__STARKERNEL__`); no-op on hosted builds. FABRIC-0.md item 4.4q (boot-mode
|
||||
scrollback added by 4.4ac).
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `SCROLL-BACK` | `( n -- )` | Moves the REPL framebuffer scrollback view back `n` lines. |
|
||||
| `SCROLL-FWD` | `( n -- )` | Moves the REPL framebuffer scrollback view forward `n` lines, toward live. |
|
||||
|
||||
## Kernel: Capsule Birth & Tripod Fleet (`mama_forth_words.c`)
|
||||
|
||||
Kernel-only (`__STARKERNEL__`). Implements the MAMA vocabulary — capsule birth protocol and
|
||||
Tripod fleet control (`src/starkernel/capsule/mama_forth_words.c`). Two registration
|
||||
functions exist: `register_mama_forth_words()` gives Hera (Mama) her full word set,
|
||||
registered identically into both the FORTH and MAMA vocabularies; `register_child_vm_words()`
|
||||
gives child VMs (Hermes/Artemis/etc.) a smaller subset, since they aren't bootstrapped
|
||||
through the same path.
|
||||
|
||||
**Hera's word set:**
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `BYE` | `( -- )` | Hera-only override: reaps all child VMs, then cold-restarts the machine. (Children instead get the standard `system_word_bye` from `system_words.c`, which just halts back to the parent's REPL.) |
|
||||
| `CONNECT-HERMES` | `( -- )` | Enters Hermes's REPL, birthing her first if not already live/stopped (idempotent). |
|
||||
| `CONNECT-ARTEMIS` | `( -- )` | Enters Artemis's REPL, birthing her first if not already live/stopped (idempotent). |
|
||||
| `BIRTH` | `( c-addr u -- )` | Births a named child VM from its capsule (name → `name:init.4th`). Idempotent if already live; refuses to re-birth Hera. |
|
||||
| `KILL` | `( c-addr u -- )` | Destroys a named VM unconditionally, fanning its heat out to survivors first. Hera cannot be killed. Idempotent. |
|
||||
| `START` | `( c-addr u -- )` | Enters a named VM's REPL loop synchronously; caller blocks until the target halts via STOP/BYE. Cannot start a LIVE/DEAD/STILLBORN VM. |
|
||||
| `STOP` | `( -- )` | Self-stop: sets `vm->halted` so the current VM's REPL loop exits on its next iteration. Registered in every VM including children. |
|
||||
| `USE` | `( c-addr u -- )` | Redirects system-wide REPL input to a named VM without touching the C call stack; `USE Hera` resets to default dispatch. |
|
||||
| `EXEC` | `( c-addr u -- )` | Executes a named capsule in the current VM — same path as Mama's own init auto-run. |
|
||||
| `CAPSULE-COUNT` | `( -- n )` | Pushes the number of capsules in the capsule directory. |
|
||||
| `CAPSULE@` | `( idx -- desc )` | Pushes the capsule descriptor address at index `idx` (0 if out of bounds). |
|
||||
| `CAPSULE-HASH@` | `( desc -- hash )` | Pushes the content hash field from a capsule descriptor. |
|
||||
| `CAPSULE-FLAGS@` | `( desc -- flags )` | Pushes the flags field from a capsule descriptor. |
|
||||
| `CAPSULE-LEN@` | `( desc -- len )` | Pushes the payload length field from a capsule descriptor. |
|
||||
| `CAPSULE-BIRTH` | `( capsule-id -- vm-id-hi vm-id-lo )` | Births a baby VM from a production (p) capsule by index; returns the new VM's 128-bit ID as a double, or all-ones on failure. |
|
||||
| `CAPSULE-RUN` | `( capsule-id -- )` | Runs an experiment (e) capsule on Mama by index. |
|
||||
| `MAMA-VM-ID` | `( -- 0 0 )` | Pushes Mama's (Hera's) own VM ID as a double — always zero. |
|
||||
| `VM-COUNT` | `( -- n )` | Pushes the number of registered VMs. |
|
||||
| `VM-CONSERVED?` | `( -- flag )` | FORTH boolean: true if the fleet heat sum is within epsilon of `Q.1` (conservation check). |
|
||||
| `VM-PHYSICS-STATUS` | `( -- )` | Prints the fleet compudynamics diagnostic report. |
|
||||
| `VM-STEP` | `( c-addr u -- )` | Gives one REPL turn (prompt, read one line, execute, return) to a named VM — the Compudynamics context-switch primitive. |
|
||||
| `VM-EXEC` | `( cmd-caddr cmd-u vm-name-caddr vm-name-u -- )` | Injects and immediately executes a command string in a named VM, no readline/blocking. |
|
||||
| `VM-CALL` | `( cmd-caddr cmd-u vm-name-caddr vm-name-u -- n )` | Like `VM-EXEC` but pops one cell from the target's stack afterward and pushes it onto the caller's stack (cross-VM query primitive). |
|
||||
| `CAPSULE-TEST` | `( -- )` | Prints a diagnostic message confirming the capsule system is active. |
|
||||
|
||||
**Additional words in `register_child_vm_words()` only (not in Hera's own dictionary — this
|
||||
keeps Hera's `dict_hash` off item 4.1's baseline):**
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `STADIUM-ADMIT` | `( identity heat behaviour -- cell \| -1 )` | Admits a mass-1 patron into the calling VM's own Stadium quota; -1 if behaviour tag is invalid or admission is refused. |
|
||||
| `STADIUM-EVICT` | `( cell -- flag )` | Reaps the patron header at `cell`; true (-1) on success, false (0) if refused (out of range, not resident, pinned, or contains-gated). |
|
||||
| `STADIUM-RES@` | `( -- heat )` | Read-only peek at the calling VM's own reservoir balance (Q48.16). |
|
||||
| `STADIUM-RES-PULL` | `( qty -- heat )` | Pulls up to `qty` (Q48.16) from the calling VM's own reservoir; returns the amount actually pulled (never more, never negative). |
|
||||
| `STADIUM-RES-PUSH` | `( heat -- )` | Credits heat (Q48.16) back into the calling VM's own reservoir. |
|
||||
| `STADIUM-HEAT@` | `( cell -- heat )` | Reads a resident cell's own heat; 0 if not resident or not owned by the calling VM. |
|
||||
| `STADIUM-HEAT!` | `( new-heat cell -- )` | Writes a resident cell's own heat, atomically reconciling the reservoir delta; silently refused if not owned by the calling VM or reservoir can't cover an increase. |
|
||||
| `STADIUM-WORD-HEAT` | `( -- heat )` | Sum of heat held by the calling VM's own word-execution residents (item 4.1's cells) — closes the term application-level conservation checks (e.g. Hermes's `HERMES-K`) need. |
|
||||
|
||||
## Hosted-Build-Only Lifecycle Stubs (`lifecycle_words_hosted.c`)
|
||||
|
||||
Compiled only when `__STARKERNEL__` is **not** defined. In the hosted build there is no
|
||||
capsule blob or kernel VM registry, so these five just consume their arguments and log
|
||||
intent — real behavior lives in `mama_forth_words.c` (`BIRTH`/`USE`) plus separate
|
||||
kernel-side `KILL`/`PAUSE`/`RESUME` words for the kernel build.
|
||||
|
||||
| Word | Stack Effect | Description |
|
||||
|---|---|---|
|
||||
| `BIRTH` | `( c-addr u -- )` | Hosted stub: logs "BIRTH name (hosted)"; no real VM lifecycle in the hosted build. |
|
||||
| `KILL` | `( c-addr u -- )` | Hosted stub: logs "KILL name (hosted)". |
|
||||
| `PAUSE` | `( c-addr u -- )` | Hosted stub: logs "PAUSE name (hosted)". |
|
||||
| `RESUME` | `( c-addr u -- )` | Hosted stub: logs "RESUME name (hosted)". |
|
||||
| `USE` | `( c-addr u -- )` | Hosted stub: logs "USE name (hosted)". |
|
||||
|
||||
---
|
||||
|
||||
## Keeping this current
|
||||
|
||||
This document was built by reading every `register_word()` (and the one `vm_create_word()`)
|
||||
call site directly, plus the doc comment above each implementing function. When words are
|
||||
added, removed, or re-registered:
|
||||
|
||||
1. Update the relevant category table above — keep registration order within a file, not
|
||||
alphabetical.
|
||||
2. If a new file registers a name that collides with an existing one, add it to the collision
|
||||
note at the top of this document and mark both entries (`*(shadowed)*` / live) at point of
|
||||
use, the same way `[`/`]`/`STATE` and `MOD`/`/MOD`/`*/`/`*/MOD` are handled above —
|
||||
determine the winner from `register_forth79_words()`'s call order in `src/word_registry.c`
|
||||
(later registration wins; search is newest-first via `vm->latest`/`->link`).
|
||||
2. If a new `word_source/*.c` file is added, give it its own `##` section, placed near
|
||||
related categories.
|
||||
3. Word counts aren't tracked as a running total in this document on purpose — they drift
|
||||
too easily and the categories are the useful unit, not a single number. If a total is
|
||||
needed, `grep -rc 'register_word(' src/word_source/*.c src/starkernel/capsule/mama_forth_words.c`
|
||||
plus a check for `vm_create_word(vm, "` (the one alternate registration path found so
|
||||
far) gets close, but always verify against this document rather than trusting either
|
||||
grep alone — see the `physics_pipelining_diagnostic_words.c` case above for why.
|
||||
@@ -0,0 +1,745 @@
|
||||
# L8 Jacquard: Adaptive 128-Config Heat-Ranked Selector
|
||||
|
||||
**Status:** Design proposal — awaiting review and approval before implementation
|
||||
**Author:** Claude Code session, June 2026
|
||||
**Affects:** `include/ssm_jacquard.h`, `src/ssm_jacquard.c`, `src/inference_engine.c`, `include/vm.h`
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive Summary
|
||||
|
||||
The current L8 Jacquard selector is a **static 4-bit threshold classifier**: three metric
|
||||
thresholds produce a 4-bit pattern that maps to one of 16 fixed modes controlling L2/L3/L5/L6.
|
||||
It has no memory, no learning, and no ability to discover that the DoE-derived thresholds may
|
||||
not be optimal for a specific deployed workload.
|
||||
|
||||
This document proposes replacing it with a **heat-ranked 8×128 adaptive config table** —
|
||||
the same structure used by the hotword cache applied to configuration selection. The 128
|
||||
entries cover all 2^7 DoE configurations (L1–L7). Benefit scores are updated via negative
|
||||
feedback from the coupled (window, slope) convergence signal that the inference engine already
|
||||
computes. DoE priors seed the initial scores so there is no cold-start penalty.
|
||||
|
||||
**Estimated additional overhead:** ~3–4 ns per heartbeat tick (0.03–0.04% of the 10 µs tick
|
||||
period). Memory: ~10 KB, fully L1-cached.
|
||||
|
||||
**Verdict:** Worthwhile, conditional on the reward signal design described in Section 5.
|
||||
See Section 9 for the full benefit/cost analysis.
|
||||
|
||||
---
|
||||
|
||||
## 2. What Exists Today
|
||||
|
||||
### 2.1 The Seven Loops
|
||||
|
||||
| Loop | Name | Current State | Reason |
|
||||
|------|------|--------------|--------|
|
||||
| L1 | Execution Heat Tracking | Permanently OFF | Harmful in 86% of top DoE configs |
|
||||
| L2 | Rolling Window History | L8-controlled | ON if entropy > 0.75 |
|
||||
| L3 | Linear Decay | L8-controlled | ON if temporal_decay > 0.5 |
|
||||
| L4 | Pipelining Metrics | Permanently OFF | Harmful in 100% of top DoE configs |
|
||||
| L5 | Window Width Inference | L8-controlled | ON if CV > 0.15 |
|
||||
| L6 | Decay Slope Inference | L8-controlled | ON if CV > 0.15 AND temporal_decay > 0.3 |
|
||||
| L7 | Adaptive Heartrate | Permanently ON | Beneficial in 71% of top DoE configs |
|
||||
| L8 | Jacquard Policy Engine | — | Drives L2/L3/L5/L6 |
|
||||
|
||||
### 2.2 Current L8 Logic (`ssm_l8_update`, 156 lines total)
|
||||
|
||||
```c
|
||||
int L2_bit = (entropy >= SSM_ENTROPY_HIGH_THRESHOLD); // 0.75
|
||||
int L3_bit = (temporal >= SSM_TEMPORAL_DECAY_THRESHOLD); // 0.5
|
||||
int L5_bit = (cv >= SSM_CV_HIGH_THRESHOLD); // 0.15
|
||||
int L6_bit = (cv >= 0.15 && temporal >= SSM_TEMPORAL_DECAY_LOW_THRESHOLD);
|
||||
|
||||
target_mode = (L2<<3) | (L3<<2) | (L5<<1) | L6;
|
||||
// hysteresis: commit only after SSM_HYSTERESIS_TICKS=5 consecutive votes
|
||||
```
|
||||
|
||||
Four thresholds hardcoded from the DoE. No state beyond the hysteresis counter and current mode.
|
||||
|
||||
### 2.3 Top-5% DoE Validated Modes
|
||||
|
||||
| Mode | Bits (L2 L3 L5 L6) | Description |
|
||||
|------|-------------------|-------------|
|
||||
| C4 | 0 1 0 0 | Temporal locality only |
|
||||
| C7 | 0 1 1 1 | Decay + both inferences |
|
||||
| C9 | 1 0 0 1 | Rolling window + decay-slope inference |
|
||||
| C11 | 1 0 1 1 | Rolling window + both inferences |
|
||||
| C12 | 1 1 0 0 | Rolling window + decay, no inference |
|
||||
|
||||
These modes will seed the table with high initial benefit scores (see Section 6).
|
||||
|
||||
---
|
||||
|
||||
## 3. Signal Chain and Coupled Feedback Topology
|
||||
|
||||
Understanding the signal chain is prerequisite to understanding the reward signal.
|
||||
The three variables — decay slope, window width, and heat trajectory — form a coupled
|
||||
feedback system, not three independent loops.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ COUPLED FEEDBACK SYSTEM │
|
||||
│ │
|
||||
│ decay_slope_q48 │
|
||||
│ │ │
|
||||
│ ▼ (applied each heartbeat tick) │
|
||||
│ DictEntry.execution_heat ◄── decays at slope rate │
|
||||
│ │ │
|
||||
│ ▼ (word IDs recorded in execution order) │
|
||||
│ RollingWindowOfTruth ◄── width = effective_window_size │
|
||||
│ │ │
|
||||
│ ▼ Phase 2B: linearize → heat trajectory[] │
|
||||
│ Inference Engine │
|
||||
│ │ │
|
||||
│ ├─► Phase 2A: variance stable? ──► early-exit (most ticks) │
|
||||
│ │ │
|
||||
│ ├─► Phase 2C (L5): Levene's test │
|
||||
│ │ └──► new effective_window_size ──────────────┐ │
|
||||
│ │ │ │
|
||||
│ └─► Phase 2D (L6): log regression │ │
|
||||
│ └──► new decay_slope_q48 ────────────────────┼──┐ │
|
||||
│ │ │ │
|
||||
│ ┌────────────────────────────────────────────────────────┘ │ │
|
||||
│ │ window feeds back into how much trajectory │ │
|
||||
│ │ Phase 2B extracts next time │ │
|
||||
│ │ │ │
|
||||
│ └──► effective_window_size │ │
|
||||
│ │ │
|
||||
│ slope feeds back into heat distribution │ │
|
||||
│ rolling window captures │ │
|
||||
│ └──── decay_slope_q48 ◄──────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Parallel: adaptive pass adjusts window based on diversity │
|
||||
│ (growth_rate_q48 vs threshold → shrink or grow) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.1 Why the Coupling Matters
|
||||
|
||||
- A steeper decay slope → words cool faster → rolling window sees a more homogeneous
|
||||
heat distribution → trajectory is less noisy → Levene's inflection shifts left
|
||||
(narrower window sufficient) → Phase 2C infers a narrower window.
|
||||
|
||||
- A narrower window → Phase 2B extracts a shorter trajectory → log regression in
|
||||
Phase 2D has fewer samples → slope estimate is noisier → less stable decay_slope_q48.
|
||||
|
||||
- They iterate toward a **joint attractor**: the window is exactly wide enough to give
|
||||
the slope estimator reliable data, and the slope is exactly steep enough that the window
|
||||
captures a stationary heat distribution. Neither is pushing the other any more.
|
||||
|
||||
This coupling is what makes the reward signal design non-trivial. See Section 5.
|
||||
|
||||
---
|
||||
|
||||
## 4. Proposed Architecture
|
||||
|
||||
### 4.1 Core Concept
|
||||
|
||||
Replace the 4-bit threshold classifier with a stratified heat-ranked table:
|
||||
|
||||
```
|
||||
regime_scores[8][128] — benefit score per (regime, config)
|
||||
regime_trials[8][128] — trial count per (regime, config)
|
||||
```
|
||||
|
||||
The **8 regime bins** are the same 3-bit metric partition the current L8 already computes:
|
||||
|
||||
```c
|
||||
regime = (entropy_high << 2) | (cv_high << 1) | temporal_high
|
||||
```
|
||||
|
||||
Eight bins, one per combination of the three binary threshold tests. This is the same
|
||||
partition as current L8 — we are not introducing new thresholds.
|
||||
|
||||
The **128 config entries** cover the full 2^7 DoE space: all combinations of L1–L7 as
|
||||
independent binary variables. L1 and L4 are included with very low initial scores —
|
||||
not disabled by fiat, but deprioritized by evidence.
|
||||
|
||||
### 4.2 Data Structures
|
||||
|
||||
```c
|
||||
/* One entry in the config table */
|
||||
typedef struct {
|
||||
uint8_t config_bits; /* 7-bit mask: b6=L1, b5=L2, b4=L3, b3=L4,
|
||||
b2=L5, b1=L6, b0=L7 */
|
||||
uint32_t benefit_score; /* Heat analog — rises when stable, falls when not */
|
||||
uint32_t trial_count; /* Total completed trials under this config */
|
||||
uint64_t last_active_ns; /* Monotonic timestamp — supports score decay */
|
||||
uint32_t prev_window; /* effective_window_size at end of last trial */
|
||||
uint64_t prev_slope; /* decay_slope_q48 at end of last trial */
|
||||
} SsmConfigEntry; /* ~32 bytes per entry */
|
||||
|
||||
/* Full stratified table */
|
||||
typedef struct {
|
||||
SsmConfigEntry entries[128]; /* Config metadata, static */
|
||||
uint32_t regime_scores[8][128]; /* Benefit score per regime per config */
|
||||
uint32_t regime_trials[8][128]; /* Trial count per regime per config */
|
||||
uint8_t current_config; /* Index of active config (0–127) */
|
||||
uint8_t current_regime; /* Regime bin at start of current trial */
|
||||
uint32_t trial_tick_count; /* Ticks elapsed in current trial (relative time) */
|
||||
uint32_t anova_exits_this_trial; /* Phase 2A early-exits accumulated */
|
||||
uint32_t total_regime_trials[8]; /* Total trials per regime (for UCB ln term) */
|
||||
} SsmConfigTable;
|
||||
```
|
||||
|
||||
**Memory footprint:**
|
||||
|
||||
| Structure | Size |
|
||||
|---|---|
|
||||
| `SsmConfigEntry[128]` | 128 × 32 = 4,096 bytes |
|
||||
| `regime_scores[8][128]` (uint32_t) | 4,096 bytes |
|
||||
| `regime_trials[8][128]` (uint32_t) | 4,096 bytes |
|
||||
| Trial state + totals | ~64 bytes |
|
||||
| **Total** | **~12 KB** |
|
||||
|
||||
The entire table fits in L1 cache (32–64 KB typical). The access pattern is regime-local:
|
||||
each trial-end touches 128 contiguous uint32_t values for the current regime, then stops.
|
||||
This is the same cache-friendly pattern the hotword cache exploits.
|
||||
|
||||
### 4.3 `ssm_l8_state_t` Changes
|
||||
|
||||
Current `ssm_l8_state_t` has: `current_mode`, `hysteresis_counter`, `pending_mode`.
|
||||
|
||||
New `ssm_l8_state_t` adds a pointer to the config table (keeping the existing fields
|
||||
for backward compatibility during transition, removing them once validated):
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
ssm_l8_mode_t current_mode; /* Keep for compatibility */
|
||||
uint32_t hysteresis_counter; /* Keep for compatibility */
|
||||
ssm_l8_mode_t pending_mode; /* Keep for compatibility */
|
||||
SsmConfigTable *table; /* NULL = use legacy threshold mode */
|
||||
} ssm_l8_state_t;
|
||||
```
|
||||
|
||||
Gated behind `table != NULL` so the old path remains functional during development
|
||||
and testing.
|
||||
|
||||
---
|
||||
|
||||
## 5. Reward Signal Design
|
||||
|
||||
### 5.1 The Circularity Problem
|
||||
|
||||
A naive reward signal based on correction magnitude within a trial will produce wrong results:
|
||||
|
||||
- A config with **L5+L6 enabled** always shows non-zero `Δwindow` and `Δslope` — because
|
||||
the inference engine is running and making corrections. Large corrections on the first trial
|
||||
after a config switch may reflect genuine convergence toward the attractor, not config failure.
|
||||
|
||||
- A config with **L5+L6 disabled** always shows zero `Δwindow` and `Δslope` — because
|
||||
nothing is running to update them. This would be incorrectly rewarded as "stable."
|
||||
|
||||
**The fix:** measure **joint convergence across consecutive trials**, not correction magnitude
|
||||
within a trial. The system is converging when the corrections are getting *smaller* from one
|
||||
trial to the next — not when they are zero in one trial.
|
||||
|
||||
### 5.2 Joint Error Signal
|
||||
|
||||
At the end of each trial, record the current (window, slope) pair as `(prev_window, prev_slope)`
|
||||
in the `SsmConfigEntry`. At the end of the *next* trial under the same config and same regime,
|
||||
compute joint error:
|
||||
|
||||
```
|
||||
Δw = |window_trial_N+1 - window_trial_N| / window_trial_N
|
||||
Δs = |slope_trial_N+1 - slope_trial_N| / slope_trial_N
|
||||
|
||||
joint_error = sqrt(Δw² + Δs²) / sqrt(2) ← normalized RMS in [0, 1]
|
||||
|
||||
stability = 1.0 - clamp(joint_error, 0.0, 1.0)
|
||||
```
|
||||
|
||||
When the window and slope have stopped moving relative to each other, `joint_error → 0`
|
||||
and `stability → 1.0`. When they are still adjusting, `joint_error > 0` and
|
||||
`stability < 1.0`.
|
||||
|
||||
### 5.3 ANOVA Rate as Secondary Signal
|
||||
|
||||
The ANOVA early-exit rate (Phase 2A) is a fast, cheap proxy for system stability:
|
||||
it fires when variance hasn't changed enough to justify full inference. It is a necessary
|
||||
but not sufficient condition for joint attractor convergence:
|
||||
|
||||
```
|
||||
anova_stability = anova_exits_this_trial / trial_ticks ∈ [0, 1]
|
||||
```
|
||||
|
||||
### 5.4 Combined Stability Score
|
||||
|
||||
```
|
||||
combined_stability = w_joint × stability + w_anova × anova_stability
|
||||
```
|
||||
|
||||
#### What each signal actually measures
|
||||
|
||||
`stability` (from `joint_error`) is **output convergence**: did the inference engine's
|
||||
corrections reach a fixed point? It requires at least one of L5 or L6 to be active —
|
||||
if neither is running, `Δwindow = Δslope = 0` always and the signal collapses (Section 5.7).
|
||||
|
||||
`anova_stability` is **input stationarity**: is the execution trajectory quiet enough that
|
||||
the inference engine decided not to bother computing? It fires regardless of L5/L6 state,
|
||||
making it the only signal available when both are disabled.
|
||||
|
||||
These are not redundant. A config can show:
|
||||
- High `anova_stability`, low `stability`: input is quiet (ANOVA skips often) but corrections
|
||||
keep churning — window and slope are wandering on a flat input landscape. Indicates
|
||||
misconfigured L5/L6 or an under-constrained attractor.
|
||||
- Low `anova_stability`, high `stability`: input is noisy/dynamic but corrections have
|
||||
settled — the config found a stable operating point despite volatile input. Desirable.
|
||||
- Both high: ideal. Both low: still converging, not yet settled.
|
||||
|
||||
#### The partial-inference bias problem
|
||||
|
||||
When only one of L5 or L6 is active, `joint_error` is only half-informed:
|
||||
- L5-on, L6-off: `Δslope = 0` always (slope frozen) → `joint_error = |Δw|/sqrt(2)`,
|
||||
biased toward 1.0 because the frozen component contributes zero.
|
||||
- L5-off, L6-on: symmetric bias in the other dimension.
|
||||
|
||||
A fixed weight like 0.75 treats both the one-active and both-active cases identically,
|
||||
overstating the signal quality in the one-active case.
|
||||
|
||||
#### Option A: Fixed weights (simpler, conservative)
|
||||
|
||||
```
|
||||
w_joint = 0.75, w_anova = 0.25
|
||||
```
|
||||
|
||||
This is the design-conservative choice. The joint signal dominates when it is available;
|
||||
ANOVA fills in when it cannot run (full fallback in Section 5.7). The 3:1 ratio reflects
|
||||
the ordering: output convergence is what we ultimately care about; input stationarity
|
||||
is a proxy that is easier to measure. These weights need DoE validation — they are an
|
||||
informed initial guess, not a derived result.
|
||||
|
||||
#### Option B: Activity-weighted (principled, slightly more complex)
|
||||
|
||||
```c
|
||||
active_outputs = L5_enabled + L6_enabled; /* ∈ {0, 1, 2} */
|
||||
w_joint = active_outputs / 2; /* 0.0, 0.5, or 1.0 */
|
||||
w_anova = 1 - w_joint;
|
||||
```
|
||||
|
||||
When both L5 and L6 are active: 100% joint (both components of `joint_error` valid).
|
||||
When one is active: 50/50 (half the joint signal is meaningful, ANOVA fills the rest).
|
||||
When neither is active: 100% ANOVA (collapses cleanly to the Section 5.7 special case,
|
||||
eliminating the need for a separate branch).
|
||||
|
||||
This is strictly more correct than Option A for the partial-inference cases. It costs
|
||||
three integer operations per trial end. The tradeoff: it makes the effective weight
|
||||
a function of the current config, which complicates reasoning about the reward surface
|
||||
when comparing configs with different L5/L6 settings.
|
||||
|
||||
#### Decision required
|
||||
|
||||
This is the most consequential tunable in the design. **Recommend starting with Option A
|
||||
at the conservative 0.75/0.25 split and adding instrumented logging of both components**
|
||||
so the effective ratio can be observed over real workload runs before any adjustment.
|
||||
Option B should be considered if the logs show systematic bias in the partial-inference
|
||||
configs (e.g., L5-on/L6-off configs consistently overscoring their L5-on/L6-on neighbors).
|
||||
|
||||
### 5.5 Negative Feedback Reward Update
|
||||
|
||||
```
|
||||
benefit_delta = GAIN × (combined_stability - 0.5) × 2.0
|
||||
|
||||
new_score = old_score × DECAY_FACTOR + benefit_delta
|
||||
new_score = clamp(new_score, SCORE_MIN, SCORE_MAX)
|
||||
```
|
||||
|
||||
- `combined_stability > 0.5`: the config is driving the system toward its attractor → positive
|
||||
delta → benefit_score rises.
|
||||
- `combined_stability < 0.5`: the config is preventing convergence → negative delta →
|
||||
benefit_score falls.
|
||||
- `combined_stability = 0.5`: neutral → no update.
|
||||
|
||||
`DECAY_FACTOR < 1.0` ensures scores decay over time, preventing a config from holding
|
||||
the top position indefinitely based on past performance.
|
||||
|
||||
#### DECAY_FACTOR stability analysis
|
||||
|
||||
The half-life of a benefit score is expressed in **trials** — the natural relative-time unit
|
||||
for this system. Wall-clock conversion is L7's concern and is not the right framing here.
|
||||
|
||||
```
|
||||
half_life_trials = ln(0.5) / ln(DECAY_FACTOR)
|
||||
```
|
||||
|
||||
At `DECAY_FACTOR = 0.99`:
|
||||
|
||||
```
|
||||
half_life_trials ≈ 69 trials
|
||||
```
|
||||
|
||||
The key assumption: **regime dwell time, measured in trials**. A config earns its score
|
||||
over many trials in a given regime. If the system typically dwells in a regime for ~200 trials
|
||||
before the metric signature shifts, a 69-trial half-life means scores from the previous dwell
|
||||
carry moderate weight into the next — which is appropriate. If regimes shift every ~20 trials,
|
||||
the half-life is too long and the table remembers the wrong regime. If regimes dwell for
|
||||
~2,000 trials, the half-life is fine and the exploration bonus from UCB prevents stagnation.
|
||||
|
||||
**Effective-zero crossover:**
|
||||
|
||||
```
|
||||
0.99^n = 0.01 → n ≈ 458 trials
|
||||
```
|
||||
|
||||
This is the memory horizon — how many trials back the table looks. Flag `SSM_SCORE_DECAY_FACTOR`
|
||||
as a first-class tunable. Calibrate it against observed regime dwell time in trials, not
|
||||
against wall-clock expectations.
|
||||
|
||||
### 5.6 Minimum Trial Duration
|
||||
|
||||
Each trial must contain at least two inference cycles — one to establish `(prev_window,
|
||||
prev_slope)` and one to measure how much they moved. Since inference runs every
|
||||
`HEARTBEAT_INFERENCE_FREQUENCY = 1,000` ticks:
|
||||
|
||||
```
|
||||
SSM_MIN_TRIAL_TICKS = 2 × HEARTBEAT_INFERENCE_FREQUENCY = 2,000 ticks
|
||||
```
|
||||
|
||||
**This is a relative-time measure, not a wall-clock measure — intentionally so.**
|
||||
|
||||
The tick is the fundamental unit of relative time in this system. L7 (adaptive heartrate)
|
||||
varies the *wall-clock duration* of a tick based on observed workload. That variation is the
|
||||
system's own response to workload; it is not noise. A trial lasting 2,000 ticks at a slower
|
||||
L7 heartrate is a trial over a longer relative-time span, which corresponds to more actual
|
||||
system activity — it is a larger, not smaller, sample. Anchoring to wall clock here would
|
||||
import an absolute time dependency into a system built deliberately around relative time with
|
||||
CV promises.
|
||||
|
||||
The correct framing: `SSM_MIN_TRIAL_TICKS` is a promise — "this trial will not end until
|
||||
the system has processed at least 2,000 of its own ticks." What that means in nanoseconds
|
||||
is L7's concern, not the adaptive selector's.
|
||||
|
||||
`SSM_MIN_TRIAL_TICKS` is a first-class tunable in `starforth_config.h`. Larger values give
|
||||
more stable reward estimates; smaller values allow faster adaptation to regime changes.
|
||||
|
||||
### 5.7 Special Case: L5 and L6 Both Disabled
|
||||
|
||||
When both L5 and L6 are disabled in the current config, `Δwindow` and `Δslope` from the
|
||||
inference engine are always zero (the values are frozen). The joint error signal is
|
||||
meaningless — both numerator and denominator are zero.
|
||||
|
||||
In this case, fall back to ANOVA rate alone:
|
||||
|
||||
```c
|
||||
if (!L5_enabled && !L6_enabled) {
|
||||
combined_stability = anova_stability;
|
||||
}
|
||||
```
|
||||
|
||||
This correctly evaluates "stability without active inference" — does the system at least
|
||||
not need correcting when left alone?
|
||||
|
||||
---
|
||||
|
||||
## 6. DoE Prior Seeding
|
||||
|
||||
The 38,400-run DoE provides empirical priors. These seed `regime_scores` at initialization
|
||||
so the selector immediately gravitates toward known-good configs on first deployment.
|
||||
|
||||
### 6.1 Seeding Strategy
|
||||
|
||||
| Config category | Initial score | Reasoning |
|
||||
|---|---|---|
|
||||
| Top-5% configs (C4, C7, C9, C11, C12) in their 4-bit form | `SCORE_MAX × 0.80` | DoE-validated |
|
||||
| Other configs with L1=0, L4=0 | `SCORE_MAX × 0.40` | Not validated but not harmful |
|
||||
| Configs with L1=1 OR L4=1 | `SCORE_MAX × 0.10` | DoE found harmful on average |
|
||||
| Configs with L1=1 AND L4=1 | `SCORE_MAX × 0.05` | Doubly harmful |
|
||||
|
||||
Note: the top-5% configs from the DoE are defined in 4-bit (L2/L3/L5/L6) space. For the
|
||||
7-bit table, each top-5% 4-bit config maps to multiple 7-bit entries (L1=0, L4=0, L7=1 held
|
||||
fixed). All such mappings receive the same high initial score.
|
||||
|
||||
### 6.2 Regime-Specific Seeding
|
||||
|
||||
The DoE ran across all regimes combined. If regime-specific DoE data is available in the
|
||||
future, `regime_scores[r]` can be seeded independently per regime. For now, all 8 regimes
|
||||
receive the same initial scores — the table will differentiate them over time through runtime
|
||||
learning.
|
||||
|
||||
---
|
||||
|
||||
## 7. Config Selection: UCB Algorithm
|
||||
|
||||
### 7.1 Selection Score
|
||||
|
||||
At each trial end, the selector computes a UCB (Upper Confidence Bound) score for all 128
|
||||
configs in the current regime and picks the highest:
|
||||
|
||||
```
|
||||
ucb_score[r][c] = regime_scores[r][c]
|
||||
+ UCB_K × sqrt(ln(total_regime_trials[r]) / regime_trials[r][c])
|
||||
```
|
||||
|
||||
- `regime_scores[r][c]`: exploitation term — how well config C has performed in regime R
|
||||
- `UCB_K × sqrt(...)`: exploration term — bonus for under-tried configs; shrinks as
|
||||
`regime_trials[r][c]` grows toward `total_regime_trials[r]`
|
||||
|
||||
**UCB_K** controls exploration aggressiveness. Suggest starting at `UCB_K = 0.1 × SCORE_MAX`.
|
||||
Too large: wastes trials on known-bad configs. Too small: never explores.
|
||||
|
||||
### 7.2 Implementation Cost
|
||||
|
||||
- `ln(total_regime_trials[r])`: computed **once** per trial end, reused for all 128 entries
|
||||
- Per config: 1 divide + 1 integer sqrt (~10 cycles) + 1 multiply + 1 add
|
||||
- 128 configs × ~4 ops × 2 ns = **~1–2 µs per trial end**
|
||||
- Amortized over 2,000-tick trial: **~0.5–1 ns per tick**
|
||||
|
||||
Integer sqrt via Newton's method (4–5 iterations, convergence guaranteed for uint32_t):
|
||||
no floating point, no libm dependency. Compatible with freestanding kernel builds.
|
||||
|
||||
### 7.3 Bootstrap: First Two Trials
|
||||
|
||||
The first trial under any (regime, config) pair has `regime_trials[r][c] = 0`,
|
||||
making the UCB exploration term undefined (`ln/0`). Bootstrap rule:
|
||||
|
||||
```
|
||||
if regime_trials[r][c] == 0:
|
||||
ucb_score[r][c] = SCORE_MAX // treat unexplored as maximally interesting
|
||||
```
|
||||
|
||||
This ensures every config gets at least one trial in each regime before the UCB formula
|
||||
takes over — equivalent to the "try everything once" initialization of UCB1.
|
||||
|
||||
---
|
||||
|
||||
## 8. Re-Ranking and Selection Path
|
||||
|
||||
Mirrors `dict_adaptive_optimization_pass` / `dict_reorganize_buckets_by_heat` exactly.
|
||||
|
||||
### 8.1 Per-Trial-End Sequence
|
||||
|
||||
```
|
||||
1. Compute regime bin for current metrics (same 3 comparisons as today)
|
||||
2. Compute joint_error and anova_stability for completed trial
|
||||
3. Update regime_scores[current_regime][current_config] with benefit_delta
|
||||
4. Increment regime_trials[current_regime][current_config]
|
||||
5. Apply score decay: regime_scores[r][c] *= DECAY_FACTOR
|
||||
6. Compute ucb_score for all 128 configs in current_regime
|
||||
7. Select next_config = argmax(ucb_score)
|
||||
8. Apply next_config's 7-bit L1–L7 mask to ssm_config
|
||||
9. Reset trial state: trial_tick_count=0, anova_exits_this_trial=0
|
||||
10. Record current (window, slope) into entries[current_config].prev_*
|
||||
```
|
||||
|
||||
### 8.2 Per-Tick Sequence (replaces current ssm_l8_update)
|
||||
|
||||
```
|
||||
1. Increment trial_tick_count
|
||||
2. If inference ran this tick: increment anova_exits_this_trial (if early-exited)
|
||||
3. If trial_tick_count >= SSM_MIN_TRIAL_TICKS:
|
||||
run per-trial-end sequence (steps 1–10 above)
|
||||
```
|
||||
|
||||
Trial duration is a relative-time promise in ticks. L7 adaptive heartrate affects wall-clock
|
||||
duration but not relative-time semantics. See Section 5.6 for full rationale.
|
||||
|
||||
---
|
||||
|
||||
## 9. Overhead Estimates
|
||||
|
||||
All figures assume 3 GHz CPU, base heartrate tick, 1,000-tick inference frequency,
|
||||
2,000-tick minimum trial period. Trial duration in wall-clock time varies with L7.
|
||||
|
||||
### 9.1 Per-Tick Additions
|
||||
|
||||
| Operation | Cost | Notes |
|
||||
|---|---|---|
|
||||
| Regime bin computation | 0 ns | Identical to today's 3 comparisons |
|
||||
| `trial_tick_count++` | ~0.3 ns | 1 integer add |
|
||||
| ANOVA exit accumulation | ~0.3 ns | 1 conditional add |
|
||||
| Trial-end check | ~0.3 ns | 1 compare |
|
||||
| **Per-tick total** | **~1 ns** | 0.01% of 10 µs tick |
|
||||
|
||||
### 9.2 Per-Trial-End Additions (every 2,000 ticks = 20 ms)
|
||||
|
||||
| Operation | Cost | Amortized/tick |
|
||||
|---|---|---|
|
||||
| Joint error computation (Δw, Δs, RMS) | ~50 ns | 0.025 ns |
|
||||
| ANOVA rate computation | ~10 ns | 0.005 ns |
|
||||
| Combined stability + benefit_delta | ~30 ns | 0.015 ns |
|
||||
| Score update + decay (1 array write) | ~15 ns | 0.0075 ns |
|
||||
| UCB over 128 entries (int sqrt ×128) | ~1,500 ns | 0.75 ns |
|
||||
| Argmax selection | ~50 ns | 0.025 ns |
|
||||
| **Per-trial-end total** | **~1,655 ns** | **~0.83 ns/tick at base heartrate** |
|
||||
|
||||
### 9.3 Memory Access
|
||||
|
||||
| | Size | Cache behavior |
|
||||
|---|---|---|
|
||||
| `regime_scores[r][*]` (128 uint32_t) | 512 bytes per regime | ~8 cache lines, stays hot |
|
||||
| `SsmConfigEntry[128]` | 4,096 bytes | Fits in L1; accessed sequentially |
|
||||
| Full table | ~12 KB | Fits in L1 (32–64 KB typical) |
|
||||
|
||||
### 9.4 Summary Against Existing Costs
|
||||
|
||||
| Component | ns/tick amortized | % of 10 µs tick |
|
||||
|---|---|---|
|
||||
| Existing `ssm_l8_update` (today) | ~4 ns | 0.04% |
|
||||
| Existing inference engine (20–50 µs every 1,000 ticks) | 20–50 ns | 0.20–0.50% |
|
||||
| **New per-tick accumulation** | **~1 ns** | **0.01%** |
|
||||
| **New per-trial-end (amortized)** | **~0.83 ns** | **0.008%** |
|
||||
| **New L8 total addition** | **~2 ns** | **0.02%** |
|
||||
| **New L8 total (replacing old L8)** | **~2 ns** | **0.02%** |
|
||||
|
||||
The new L8 is marginally *cheaper* than the current one in per-tick terms
|
||||
(today's `ssm_l8_update` runs every tick; the new one accumulates cheaply per tick and
|
||||
runs the heavy logic only at trial end, amortized over 2,000 ticks).
|
||||
|
||||
The existing Levene's test `malloc` inside Phase 2C costs more per call than the entire
|
||||
new L8 reward + UCB selection combined.
|
||||
|
||||
---
|
||||
|
||||
## 10. Files Changed
|
||||
|
||||
| File | Change | Lines added (est.) |
|
||||
|---|---|---|
|
||||
| `include/ssm_jacquard.h` | Add `SsmConfigEntry`, `SsmConfigTable`; extend `ssm_l8_state_t` | +80 |
|
||||
| `src/ssm_jacquard.c` | Add `ssm_l8_init_table()`, `ssm_l8_seed_from_doe()`, `ssm_l8_update_trial()`, `ssm_l8_select_next_config()`, `ssm_rerank_ucb()`; keep old `ssm_l8_update` as legacy path | +250 |
|
||||
| `src/inference_engine.c` | Add `window_delta_q48` and `slope_delta_q48` to `InferenceOutputs`; populate them (already computed internally, just not exposed) | +10 |
|
||||
| `include/vm.h` (via `inference_engine.h`) | Two new fields on `InferenceOutputs` | +5 |
|
||||
| `src/ssm_jacquard.c` | DoE prior seeding table (static const array of 128 seed scores) | +40 |
|
||||
|
||||
**No changes to:** `rolling_window_of_truth.c`, `heartbeat_export.c`, `physics_runtime.c`,
|
||||
`framebuffer.c`, `vt100.c`, any kernel or boot code.
|
||||
|
||||
Total estimated addition: **~385 lines**, isolated entirely within the L8 and inference
|
||||
engine files.
|
||||
|
||||
---
|
||||
|
||||
## 11. What Doesn't Change
|
||||
|
||||
- The five inference engine phases (2A–2D) are architecturally unchanged. Two small
|
||||
outputs are added (`window_delta_q48`, `slope_delta_q48`) — values already computed
|
||||
internally that are simply not currently surfaced.
|
||||
- The `ssm_config_t` L1–L7 bit flags are unchanged. Everything downstream reads them
|
||||
the same way.
|
||||
- The rolling window, heartbeat, heat tracking, hotword cache, dictionary optimization
|
||||
— all unchanged.
|
||||
- The kernel build (freestanding) is unaffected. `SsmConfigTable` uses only `stdint.h`
|
||||
types, no libc allocation (table is statically sized), no floating point
|
||||
(all arithmetic in Q48.16 or integer).
|
||||
|
||||
---
|
||||
|
||||
## 12. Open Questions for Captain Bob
|
||||
|
||||
These require decisions before coding begins:
|
||||
|
||||
**Q1: Include L1 and L4 in the 7-bit space?**
|
||||
Current plan: yes, seeded at 5–10% of `SCORE_MAX`. The DoE found them harmful on average
|
||||
but the table can discover exceptions if they exist. Risk: UCB exploration may occasionally
|
||||
select them and hurt performance during that trial. Mitigation: low seed + score decay means
|
||||
they stay near the bottom. Alternative: exclude them (use 5-bit space, 32 configs) and keep
|
||||
L7 hardwired to ON (as today).
|
||||
|
||||
**Q2: Tunable constants — bake in or expose via `starforth_config.h`?**
|
||||
Proposed new constants:
|
||||
- `SSM_MIN_TRIAL_TICKS` (suggest 2000 — relative-time floor, guarantees ≥ 2 inference
|
||||
cycles; wall-clock meaning is L7's concern, not ours; see Section 5.6)
|
||||
- `SSM_SCORE_MAX` (suggest 65535 to match Q48.16 range intuition)
|
||||
- `SSM_SCORE_DECAY_FACTOR` (suggest 0.99 ≈ Q48.16 64881; half-life ~69 trials —
|
||||
calibrate against observed regime dwell time in trials; see Section 5.5)
|
||||
- `SSM_UCB_K` (suggest `SSM_SCORE_MAX / 10`)
|
||||
- `SSM_REWARD_GAIN` (suggest `SSM_SCORE_MAX / 4`)
|
||||
- `SSM_JOINT_WEIGHT` (suggest 0.75 for Option A; see Section 5.4)
|
||||
- `SSM_ANOVA_WEIGHT` (suggest 0.25 for Option A; or use activity-weighted Option B)
|
||||
|
||||
All of these map naturally alongside the existing `ROLLING_WINDOW_SIZE`, `SSM_HYSTERESIS_TICKS`
|
||||
etc. in `include/starforth_config.h`.
|
||||
|
||||
**Q3: Regime-specific seeding now or later?**
|
||||
Current plan: all 8 regimes seeded identically from DoE priors. Regime-specific DoE
|
||||
analysis (if the raw run data is available broken out by metric regime) would improve
|
||||
cold-start behavior. Can be deferred — the table learns regime-specific scores at runtime.
|
||||
|
||||
**Q4: Legacy threshold mode as compile-time flag or runtime gate?**
|
||||
Suggested: runtime gate (`table != NULL` in `ssm_l8_state_t`). Allows A/B testing the
|
||||
old vs new L8 without recompilation. Alternative: compile-time `#ifdef SSM_L8_ADAPTIVE`
|
||||
to keep the kernel build lean.
|
||||
|
||||
**Q5: How to validate correctness before running the full DoE?**
|
||||
Suggested validation sequence (hosted VM only):
|
||||
1. `make -f Makefile.starkernel fbtest` — confirms no regression in framebuffer
|
||||
2. `make test` — confirms all 936+ FORTH words still pass
|
||||
3. Run `--doe` mode for 300 reps each with and without `SSM_L8_ADAPTIVE` defined;
|
||||
compare variance and mean execution time. The adaptive selector should produce equal
|
||||
or better variance at equal or lower mean time after a warm-up period of ~1000 trials.
|
||||
|
||||
---
|
||||
|
||||
## 13. Benefit / Cost Assessment
|
||||
|
||||
### 13.1 Concrete Expected Benefits
|
||||
|
||||
**1. Regime-specific optimality.**
|
||||
The DoE established which configs win *on average*. This table learns which config wins
|
||||
*for this specific workload in this specific regime*. For FORTH programs with unusual heat
|
||||
distributions (e.g., heavily recursive, deeply compositional, or with very stable vs very
|
||||
bursty execution patterns), the optimal config per regime may differ from the DoE average.
|
||||
The table discovers this without re-running the DoE.
|
||||
|
||||
**2. No manual tuning for new workloads.**
|
||||
Current L8: if the DoE thresholds are miscalibrated for a specific FORTH application, the
|
||||
classifier is stuck in the wrong mode permanently. The adaptive table self-corrects within
|
||||
a few hundred trial periods (a few seconds of runtime at 20ms trials).
|
||||
|
||||
**3. Joint convergence signal closes the feedback loop.**
|
||||
The inference engine currently produces (window, slope) outputs with no feedback about
|
||||
whether those outputs are causing the system to converge. The reward signal adds this
|
||||
feedback, making the overall system a genuine closed-loop controller rather than
|
||||
open-loop inference.
|
||||
|
||||
**4. Architecturally consistent.**
|
||||
The hotword cache, bucket reorganization, and adaptive window already use heat-ranked
|
||||
structures. L8 becoming heat-ranked completes the pattern — everything in the SSM is
|
||||
adaptive and data-driven, not hardcoded.
|
||||
|
||||
### 13.2 Honest Caveats
|
||||
|
||||
**1. The 0% algorithmic variance result.**
|
||||
The DoE showed the system converges to the same attractor states from any of the 128
|
||||
starting configurations. If the attractor is already well-characterized and robust, the
|
||||
adaptive table is solving a problem that doesn't significantly affect final-state behavior
|
||||
for most workloads. The benefit is primarily in *how quickly* the system reaches its
|
||||
attractor, not *where* the attractor is.
|
||||
|
||||
**2. L1 and L4 almost certainly stay at the bottom.**
|
||||
The DoE evidence against them is overwhelming (86% and 100% harmful respectively). The
|
||||
exploration of L1 and L4 slots in the 128-entry space is methodologically correct but
|
||||
practically likely to produce no new findings. The useful learning is within the 16-config
|
||||
subspace of {L2, L3, L5, L6} combinations, stratified by regime — which the current L8
|
||||
already approximates with fixed thresholds.
|
||||
|
||||
**3. Reward signal parameter sensitivity.**
|
||||
The weights (`w_joint`, `w_anova`), gain, decay factor, UCB_K, and trial duration interact.
|
||||
The DoE that validated the current system was designed to avoid exactly this kind of
|
||||
parameter sensitivity. A second DoE over the adaptive L8's parameters may be needed to
|
||||
find the optimal operating point — or the parameters can be set conservatively and left
|
||||
fixed, accepting slightly suboptimal but stable behavior.
|
||||
|
||||
**4. Complexity increase is real.**
|
||||
`ssm_jacquard.c` is currently 156 clean lines. The addition is ~385 lines of nontrivial
|
||||
logic in the heartbeat thread. Bugs here affect the adaptation rate, not correctness
|
||||
(worst case: the selector sticks on a suboptimal config, not on an incorrect config),
|
||||
but the surface area grows meaningfully.
|
||||
|
||||
### 13.3 Verdict
|
||||
|
||||
**Worth doing.** The overhead is negligible by any measure. The architectural coherence
|
||||
with the existing hotword cache is a genuine advantage. The joint convergence reward signal
|
||||
is a principled design that closes a feedback loop the current system leaves open.
|
||||
|
||||
The primary condition: **get the reward signal right before writing any other code**.
|
||||
Specifically, resolve Q1 (L1/L4 inclusion), confirm the `window_delta` / `slope_delta`
|
||||
output additions to `InferenceOutputs`, and agree on the `min_trial_ticks` value.
|
||||
Everything else is mechanical once those are fixed.
|
||||
|
||||
The primary risk is not overhead or correctness — it is parameter sensitivity in the
|
||||
reward signal. Suggest conservative initial values (low GAIN, high DECAY, moderate UCB_K)
|
||||
and validate against the existing DoE baseline before changing any parameters.
|
||||
|
||||
---
|
||||
|
||||
*End of design document. Awaiting review and approval.*
|
||||
@@ -0,0 +1,571 @@
|
||||
# M7.1: Init Capsule Architecture
|
||||
|
||||
**Status:** Shipped and live, not just designed — this document predates the actual
|
||||
capsule/Tripod/Stadium work; see `FABRIC-0.md`/`FABRIC-1.md`/`FABRIC-2.md` for real status
|
||||
(per `.claude/CLAUDE.md`'s standing redirect)
|
||||
**Branch:** `master` (post-split; the old `lithosananke` branch no longer exists in this repo)
|
||||
**Prerequisite:** M7 (VM Parity Validation)
|
||||
|
||||
## The Immutable Law
|
||||
|
||||
> **Exactly ONE production INIT `(p)` defines a baby VM.**
|
||||
>
|
||||
> No INIT is shared implicitly.
|
||||
> No base INIT exists.
|
||||
> Any similarity between VMs is the result of deliberate reuse by Mama.
|
||||
|
||||
This rule is non-negotiable. Everything in this document exists to enforce it.
|
||||
|
||||
---
|
||||
|
||||
## 1. DOMAIN vs PERSONALITY
|
||||
|
||||
### 1.1 DOMAIN (Mama-only Concept)
|
||||
|
||||
**DOMAIN** is a *construction and selection space* that exists **only in Mama FORTH**.
|
||||
|
||||
DOMAIN is used to:
|
||||
- Classify *kinds* of VMs (e.g., HTTP, storage, control-plane)
|
||||
- Compose and curate block sets
|
||||
- Run experiments `(e)`
|
||||
- Decide which block set is promoted to production `(p)`
|
||||
|
||||
DOMAIN:
|
||||
- Is **never** executed by babies
|
||||
- Is **never** visible to babies
|
||||
- Is **not** part of VM truth
|
||||
- Leaves **no** runtime trace in the baby
|
||||
|
||||
DOMAIN answers: *"What kind of thing is Mama trying to create?"*
|
||||
|
||||
### 1.2 PERSONALITY (Baby-only Reality)
|
||||
|
||||
**PERSONALITY** is the *result* of Mama's work.
|
||||
|
||||
A baby VM receives:
|
||||
- **Exactly one `(p)` INIT**
|
||||
- Executed exactly once at birth
|
||||
- Which defines its entire worldview
|
||||
|
||||
PERSONALITY:
|
||||
- Defines role, capabilities, invariants, vocabulary
|
||||
- Is immutable for the lifetime of the VM
|
||||
- Is the only truth the VM knows
|
||||
|
||||
PERSONALITY answers: *"What am I?"*
|
||||
|
||||
**The baby has no knowledge of DOMAIN.**
|
||||
|
||||
---
|
||||
|
||||
## 2. What is an Init Capsule?
|
||||
|
||||
An INIT capsule is **not**:
|
||||
- A shared base
|
||||
- A layered inheritance mechanism
|
||||
- A runtime-selectable script
|
||||
- A file or "boot code"
|
||||
|
||||
An INIT capsule **is**:
|
||||
- An **ordered, immutable block sequence**
|
||||
- A **truth-bearing unit**
|
||||
- Executed **exactly once** at VM birth
|
||||
- The **sole source of identity** for that VM
|
||||
- **Referentially addressable** — by content hash, not name
|
||||
- **Observationally enumerable** — Mama can list all capsules
|
||||
|
||||
Think: ROM image + provenance, not "config."
|
||||
|
||||
---
|
||||
|
||||
## 3. Architecture Decision
|
||||
|
||||
### Option 1: One Big Memory Blob (Indexed)
|
||||
Single contiguous arena with descriptor table + payload region.
|
||||
|
||||
### Option 2: Individual Named Blobs
|
||||
Separate addressable entities with symbolic names.
|
||||
|
||||
### Decision: Hybrid
|
||||
|
||||
**Physically:** Option 1 — single arena, indexed by ID + offset
|
||||
**Logically:** Option 2 — capsules declared symbolically at build time
|
||||
|
||||
At runtime: **symbols do not exist.** Only facts (hashes, offsets, lengths).
|
||||
|
||||
> Names lie. Hashes don't.
|
||||
|
||||
---
|
||||
|
||||
## 4. Data Structures
|
||||
|
||||
### 3.1 CapsuleDesc (64 bytes, cache-line aligned)
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint64_t magic; /* 0x00: 'CAPS' | ver | hashAlg | reserved */
|
||||
uint64_t capsule_id; /* 0x08: == content_hash (content-addressed) */
|
||||
uint64_t content_hash; /* 0x10: hash of payload bytes */
|
||||
uint64_t offset; /* 0x18: byte offset into payload arena */
|
||||
uint64_t length; /* 0x20: payload length in bytes */
|
||||
uint32_t flags; /* 0x28: state + policy bits */
|
||||
uint32_t owner_vm; /* 0x2C: 0 = mama, else child VM ID */
|
||||
uint64_t birth_count; /* 0x30: how many VMs born from this */
|
||||
uint64_t created_ns; /* 0x38: monotonic timestamp at registration */
|
||||
} CapsuleDesc; /* 0x40 = 64 bytes */
|
||||
```
|
||||
|
||||
### 3.2 Magic Field Packing
|
||||
|
||||
```
|
||||
bits 0..31 : 'CAPS' (0x53504143 little-endian)
|
||||
bits 32..39 : version (0 for v0)
|
||||
bits 40..47 : hashAlg (enum CapsuleHashAlg)
|
||||
bits 48..63 : reserved (zero)
|
||||
```
|
||||
|
||||
### 3.3 CapsuleDirHeader
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint64_t magic; /* 'CAPD' | ver | reserved */
|
||||
uint64_t arena_base; /* phys or virt base of payload arena */
|
||||
uint64_t arena_size; /* bytes */
|
||||
uint32_t desc_count; /* current number of descriptors */
|
||||
uint32_t desc_capacity; /* max (fixed at compile time for Phase A) */
|
||||
uint64_t dir_hash; /* hash of descriptor table (for parity) */
|
||||
} CapsuleDirHeader;
|
||||
```
|
||||
|
||||
### 3.4 CapsuleRunRecord (for DoE logging)
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint64_t run_id; /* sequential run identifier */
|
||||
uint32_t vm_id; /* which VM executed this */
|
||||
uint32_t reserved;
|
||||
uint64_t capsule_id; /* which capsule was run */
|
||||
uint64_t capsule_hash; /* hash at time of execution */
|
||||
uint64_t pre_dict_hash; /* dictionary state before run */
|
||||
uint64_t post_dict_hash; /* dictionary state after run */
|
||||
uint64_t started_ns; /* monotonic start time */
|
||||
uint64_t ended_ns; /* monotonic end time */
|
||||
uint32_t result_code; /* execution result */
|
||||
uint32_t flags; /* run flags (mode, etc.) */
|
||||
} CapsuleRunRecord;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Content Addressing
|
||||
|
||||
**Rule:** `capsule_id` MUST be derived from `content_hash`.
|
||||
|
||||
For v0: `capsule_id == content_hash` (identity)
|
||||
|
||||
This ensures:
|
||||
- No sequential ID collisions
|
||||
- No "what's the salt" questions
|
||||
- Facts only, no oracles
|
||||
|
||||
---
|
||||
|
||||
## 6. Hash Algorithm
|
||||
|
||||
**v0 Choice:** xxHash64
|
||||
- Deterministic across hosted + kernel
|
||||
- Easy to implement freestanding
|
||||
- Fast
|
||||
- Good enough collision resistance for practice
|
||||
|
||||
**Future-proofing:** The `hashAlg` field in magic allows upgrade to SHA-256 or BLAKE3 without breaking history.
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
CAPSULE_HASH_XXHASH64 = 0,
|
||||
CAPSULE_HASH_SHA256 = 1,
|
||||
CAPSULE_HASH_BLAKE3 = 2,
|
||||
} CapsuleHashAlg;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Flags
|
||||
|
||||
### 6.1 State Flags
|
||||
|
||||
```c
|
||||
#define CAPSULE_FLAG_ACTIVE 0x00000001 /* eligible for use */
|
||||
#define CAPSULE_FLAG_REVOKED 0x00000002 /* birth-blocked forever */
|
||||
#define CAPSULE_FLAG_DEPRECATED 0x00000004 /* eligible but discouraged */
|
||||
#define CAPSULE_FLAG_PINNED 0x00000008 /* immune to GC */
|
||||
```
|
||||
|
||||
### 6.2 Mode Flags (Production vs Experiment)
|
||||
|
||||
```c
|
||||
#define CAPSULE_FLAG_PRODUCTION 0x00000010 /* (p) truth-bearing */
|
||||
#define CAPSULE_FLAG_EXPERIMENT 0x00000020 /* (e) workload only */
|
||||
```
|
||||
|
||||
**Invariant:** Exactly one of (p) or (e) must be set.
|
||||
|
||||
```c
|
||||
#define CAPSULE_MODE_VALID(f) \
|
||||
((((f) & CAPSULE_FLAG_PRODUCTION) != 0) ^ \
|
||||
(((f) & CAPSULE_FLAG_EXPERIMENT) != 0))
|
||||
```
|
||||
|
||||
If both or neither are set: **invalid capsule.**
|
||||
|
||||
### 6.3 Revocation Semantics
|
||||
|
||||
A revoked capsule:
|
||||
- Remains enumerable (for audit)
|
||||
- Remains hashable (for provenance)
|
||||
- Is birth-blocked (mama refuses to bind it)
|
||||
|
||||
**REVOKED always overrides ACTIVE.**
|
||||
|
||||
---
|
||||
|
||||
## 8. Eligibility Rules
|
||||
|
||||
### 7.1 Birth Eligibility
|
||||
|
||||
A VM may be born **only** from a (p) capsule:
|
||||
|
||||
```c
|
||||
#define BIRTH_ELIGIBLE(flags) \
|
||||
(((flags) & CAPSULE_FLAG_PRODUCTION) && \
|
||||
((flags) & CAPSULE_FLAG_ACTIVE) && \
|
||||
!((flags) & CAPSULE_FLAG_REVOKED))
|
||||
```
|
||||
|
||||
If mama attempts to bind an (e) capsule at birth: **hard refusal.**
|
||||
No warnings. No fallback. No exceptions.
|
||||
|
||||
### 7.2 DoE/Runtime Eligibility
|
||||
|
||||
**(e) capsules exist ONLY in Mama's domain.** Baby VMs never see them, never execute them, never know they exist.
|
||||
|
||||
Mama may execute:
|
||||
- **(e) capsules** freely on herself (subject to policy)
|
||||
- **(p) capsules** only as birth capsules for babies (never as workloads)
|
||||
|
||||
This prevents the deadly mistake:
|
||||
> "Let's just rerun a production init as an experiment."
|
||||
|
||||
No. That turns truth into a variable.
|
||||
|
||||
### 7.3 Twins, Triplets, and Variants
|
||||
|
||||
**Twins/Triplets/Variants are not special constructs.** They are simply:
|
||||
- Multiple VMs born from the **same (p) capsule** (twins)
|
||||
- VMs born from **similar (p) capsules** (variants)
|
||||
|
||||
Any similarity between VMs is the result of:
|
||||
1. Mama deliberately choosing the same (p) capsule for birth
|
||||
2. Mama creating similar (p) capsules from shared block sets
|
||||
|
||||
There is no implicit inheritance. There is no base class. If two VMs share behavior, it's because Mama *made* them share it — not because of any hidden mechanism.
|
||||
|
||||
---
|
||||
|
||||
## 9. Parity Integration
|
||||
|
||||
Birth becomes a parity checkpoint:
|
||||
|
||||
```
|
||||
PARITY:BIRTH vm_id=42 capsule_id=0xA13F mode=p capsule_hash=0x... dict_hash=0x...
|
||||
```
|
||||
|
||||
DoE runs are logged separately:
|
||||
|
||||
```
|
||||
PARITY:RUN vm_id=42 run_id=17 capsule_id=0xBEEF mode=e pre_dict=0x... post_dict=0x...
|
||||
```
|
||||
|
||||
### 8.1 Truth Chain
|
||||
|
||||
For any VM, you can prove:
|
||||
1. Which capsule created it (birth_capsule_id)
|
||||
2. What the capsule contained (content_hash)
|
||||
3. What dictionary state resulted (dict_hash)
|
||||
4. What workloads were run after birth (CapsuleRunRecord log)
|
||||
|
||||
This is **provenance**. This is **science**. This is **patent-grade**.
|
||||
|
||||
---
|
||||
|
||||
## 10. Arena Placement
|
||||
|
||||
### Phase A: Static (M7.1 - M8)
|
||||
|
||||
- Descriptor table + payload arena live in `.rodata` or dedicated section
|
||||
- Built by tooling (or compiled-in)
|
||||
- Zero allocator dependency
|
||||
- Deterministic
|
||||
|
||||
```c
|
||||
/* In kernel image */
|
||||
extern const CapsuleDirHeader capsule_directory;
|
||||
extern const CapsuleDesc capsule_descriptors[];
|
||||
extern const uint8_t capsule_arena[];
|
||||
```
|
||||
|
||||
### Phase B: Dynamic (Future)
|
||||
|
||||
- Mama manages RAM arena
|
||||
- Runtime capsule registration
|
||||
- Optional persistence via memory-mapped block store
|
||||
|
||||
**Key:** `CapsuleDesc` doesn't care where bytes come from. It only cares about `offset`/`length` within "the arena."
|
||||
|
||||
---
|
||||
|
||||
## 11. Capacity
|
||||
|
||||
### Phase A: Fixed Array
|
||||
|
||||
```c
|
||||
#define MAX_CAPSULES 256
|
||||
```
|
||||
|
||||
`CapsuleDirHeader` includes `desc_count` and `desc_capacity`.
|
||||
|
||||
Growable arrays can come later without interface changes.
|
||||
|
||||
---
|
||||
|
||||
## 12. Who Writes Descriptors?
|
||||
|
||||
### Phase A (Static)
|
||||
- **Build tooling** generates descriptors
|
||||
- Kernel boots with sealed capsule directory
|
||||
- Mama reads and binds, never writes
|
||||
|
||||
### Phase B (Dynamic)
|
||||
- **Mama** appends new capsules to RAM arena
|
||||
- Only mama writes descriptors at runtime
|
||||
- **Children never write descriptors, ever**
|
||||
|
||||
---
|
||||
|
||||
## 13. VM Birth Protocol
|
||||
|
||||
1. Mama selects **one production INIT `(p)`** (by content hash)
|
||||
2. Mama validates eligibility:
|
||||
- `CAPSULE_FLAG_PRODUCTION` set
|
||||
- `CAPSULE_FLAG_ACTIVE` set
|
||||
- `CAPSULE_FLAG_REVOKED` not set
|
||||
3. Mama allocates a new VM
|
||||
4. INIT blocks are **copied** into the VM execution window (RAM blocks 0–2047)
|
||||
5. INIT blocks are **executed sequentially**
|
||||
6. Execution window is **cleared**
|
||||
7. VM begins life with its **PERSONALITY fully imprinted**
|
||||
8. Mama logs parity checkpoint:
|
||||
```
|
||||
PARITY:BIRTH vm_id=N capsule_id=X mode=p capsule_hash=H dict_hash=D
|
||||
```
|
||||
9. Mama increments `capsule.birth_count`
|
||||
|
||||
**There is:**
|
||||
- No shared INIT
|
||||
- No implicit base
|
||||
- No post-birth INIT execution
|
||||
- No runtime selection
|
||||
|
||||
**The baby VM cannot:**
|
||||
- Enumerate sibling capsules
|
||||
- Mutate the arena
|
||||
- Know its own capsule ID (optional: can query mama)
|
||||
- See or know about DOMAIN
|
||||
|
||||
---
|
||||
|
||||
## 14. DoE Execution Protocol
|
||||
|
||||
1. Mama selects workload capsule(s)
|
||||
2. Mama validates eligibility:
|
||||
- `CAPSULE_FLAG_EXPERIMENT` set
|
||||
- `CAPSULE_FLAG_ACTIVE` set
|
||||
- `CAPSULE_FLAG_REVOKED` not set
|
||||
3. For each workload:
|
||||
a. Record `pre_dict_hash`
|
||||
b. Execute capsule on target VM
|
||||
c. Record `post_dict_hash`
|
||||
d. Log run record:
|
||||
```
|
||||
PARITY:RUN vm_id=N run_id=R capsule_id=X mode=e pre_dict=P post_dict=Q
|
||||
```
|
||||
|
||||
**DoE runs do not change the VM's birth truth.** They are experiments, not identity.
|
||||
|
||||
---
|
||||
|
||||
## 15. Validation Function
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
CAPSULE_VALID = 0,
|
||||
CAPSULE_ERR_BAD_MAGIC,
|
||||
CAPSULE_ERR_BAD_VERSION,
|
||||
CAPSULE_ERR_BAD_HASH_ALG,
|
||||
CAPSULE_ERR_BOUNDS,
|
||||
CAPSULE_ERR_MODE_INVALID,
|
||||
CAPSULE_ERR_REVOKED_ACTIVE,
|
||||
CAPSULE_ERR_HASH_MISMATCH,
|
||||
} CapsuleValidateResult;
|
||||
|
||||
CapsuleValidateResult capsule_validate(
|
||||
const CapsuleDesc *desc,
|
||||
const uint8_t *arena_base,
|
||||
uint64_t arena_size,
|
||||
int verify_hash /* if true, recompute and compare */
|
||||
);
|
||||
```
|
||||
|
||||
Checks:
|
||||
1. Magic signature and version
|
||||
2. Hash algorithm is known
|
||||
3. `offset + length <= arena_size`
|
||||
4. Exactly one of (p)/(e) is set
|
||||
5. REVOKED and ACTIVE not both set (REVOKED wins, but warn)
|
||||
6. Optional: recompute hash and compare to `content_hash`
|
||||
|
||||
---
|
||||
|
||||
## 16. File Deliverables
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `include/starkernel/capsule.h` | CapsuleDesc, CapsuleDirHeader, flags, enums |
|
||||
| `include/starkernel/capsule_run.h` | CapsuleRunRecord for DoE logging |
|
||||
| `src/starkernel/capsule/capsule_validate.c` | Validation function |
|
||||
| `src/starkernel/capsule/capsule_birth.c` | Birth protocol implementation |
|
||||
| `src/starkernel/capsule/capsule_run.c` | DoE run logging |
|
||||
|
||||
---
|
||||
|
||||
## 17. Summary
|
||||
|
||||
| Principle | Enforcement |
|
||||
|-----------|-------------|
|
||||
| One truth per VM | Birth only accepts (p) capsules |
|
||||
| DOMAIN is Mama-only | Construction concept, never visible to babies |
|
||||
| PERSONALITY is baby-only | Executed INIT result, immutable identity |
|
||||
| (e) capsules are Mama-only | Never executed by or visible to baby VMs |
|
||||
| Content-addressed | `capsule_id == content_hash` |
|
||||
| Names don't exist at runtime | Symbolic resolution at build time only |
|
||||
| Revocation is permanent | REVOKED overrides ACTIVE, capsule stays enumerable |
|
||||
| DoE doesn't change truth | Workloads are (e), logged separately |
|
||||
| Mama sees all | Children see one capsule, read-only |
|
||||
| No implicit sharing | Twins exist because Mama chose same capsule |
|
||||
| Provenance is mandatory | PARITY:BIRTH and PARITY:RUN logged |
|
||||
|
||||
---
|
||||
|
||||
## 18. Next Steps
|
||||
|
||||
1. **Implement `capsule.h`** — structs, flags, macros
|
||||
2. **Implement `capsule_validate()`** — freestanding, no libc
|
||||
3. **Build tooling** — generate static capsule directory from `.4th` files
|
||||
4. **Integrate with birth** — extend `sk_vm_bootstrap` to accept capsule pointer
|
||||
5. **Parity logging** — extend parity subsystem for BIRTH/RUN records
|
||||
|
||||
---
|
||||
|
||||
## 19. Gap Analysis (Resolved)
|
||||
|
||||
### 18.1 Payload Encoding
|
||||
|
||||
**Decision:** Raw UTF-8 FORTH source for Phase A.
|
||||
|
||||
- Simple, debuggable, portable
|
||||
- Hash computed over raw bytes
|
||||
- Tokenization/compilation is premature optimization
|
||||
- Future phases may add `CAPSULE_FLAG_TOKENIZED` if needed
|
||||
|
||||
### 18.2 VM ID Allocation
|
||||
|
||||
Mama maintains a VM registry:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint32_t vm_id; /* assigned at birth, immutable */
|
||||
uint64_t birth_capsule_id;
|
||||
uint64_t birth_timestamp_ns;
|
||||
uint32_t state; /* EMBRYO, LIVE, DEAD */
|
||||
uint32_t flags;
|
||||
} VMRegistryEntry;
|
||||
```
|
||||
|
||||
- VM ID 0 is reserved (mama)
|
||||
- IDs are sequential (1, 2, 3, ...)
|
||||
- ID is never reused within a session
|
||||
|
||||
### 18.3 Birth Failure Handling
|
||||
|
||||
If init execution fails:
|
||||
|
||||
1. VM never transitions to LIVE state
|
||||
2. VM is marked STILLBORN
|
||||
3. Parity log records failure:
|
||||
```
|
||||
PARITY:BIRTH_FAILED vm_id=N capsule_id=X error=E partial_dict_hash=H
|
||||
```
|
||||
4. Partial state is **not** observable by other VMs
|
||||
5. Mama may retry with same or different capsule
|
||||
|
||||
### 18.4 Run Log Storage
|
||||
|
||||
`CapsuleRunRecord` entries stored in:
|
||||
|
||||
**Phase A:** Fixed ring buffer in kernel BSS
|
||||
```c
|
||||
#define MAX_RUN_RECORDS 1024
|
||||
CapsuleRunRecord run_log[MAX_RUN_RECORDS];
|
||||
uint32_t run_log_head;
|
||||
```
|
||||
|
||||
**Phase B:** Append-only log, optionally persisted to block device
|
||||
|
||||
### 18.5 Atomicity of birth_count
|
||||
|
||||
**Rule:** Only mama increments `birth_count`.
|
||||
|
||||
Since mama is single-threaded (or serialized), no atomic operations needed in Phase A. Phase B (SMP mama) would require `__atomic_fetch_add`.
|
||||
|
||||
### 18.6 xxHash64 Implementation
|
||||
|
||||
Freestanding xxHash64 required:
|
||||
|
||||
```
|
||||
include/starkernel/xxhash64.h
|
||||
src/starkernel/hash/xxhash64.c
|
||||
```
|
||||
|
||||
Public domain implementation (~200 lines). Must be:
|
||||
- No libc dependency
|
||||
- Deterministic across platforms
|
||||
- Seedable (seed = 0 for capsule hashing)
|
||||
|
||||
---
|
||||
|
||||
## 20. Implementation Checklist
|
||||
|
||||
- [ ] `include/starkernel/capsule.h` — structs, flags, macros
|
||||
- [ ] `include/starkernel/capsule_run.h` — CapsuleRunRecord
|
||||
- [ ] `include/starkernel/xxhash64.h` — hash function
|
||||
- [ ] `src/starkernel/hash/xxhash64.c` — freestanding xxHash64
|
||||
- [ ] `src/starkernel/capsule/capsule_validate.c` — validation
|
||||
- [ ] `src/starkernel/capsule/capsule_birth.c` — birth protocol
|
||||
- [ ] `src/starkernel/capsule/capsule_run.c` — DoE run logging
|
||||
- [ ] `tools/mkcapsule.c` — build tool to generate capsule directory
|
||||
- [ ] Extend parity subsystem for BIRTH/BIRTH_FAILED/RUN records
|
||||
- [ ] VM registry in mama
|
||||
|
||||
---
|
||||
|
||||
*This design will still make sense 20 years from now.*
|
||||
@@ -0,0 +1,700 @@
|
||||
# StarForth Ontology, Taxonomy, and Lexicon
|
||||
|
||||
**Version**: 1.1
|
||||
**Date**: 2026-08-13
|
||||
**Purpose**: Formal conceptual framework for precise academic discourse
|
||||
|
||||
---
|
||||
|
||||
## I. ONTOLOGY (Conceptual Framework)
|
||||
|
||||
### 1.1 Core Concepts
|
||||
|
||||
```
|
||||
StarForth Adaptive Runtime System
|
||||
├── Execution Metrics
|
||||
│ ├── Execution Frequency (primary measurable)
|
||||
│ ├── Temporal Decay (derived quantity)
|
||||
│ └── Transition Probability (derived quantity)
|
||||
├── Adaptive Mechanisms
|
||||
│ ├── Frequency-Based Caching
|
||||
│ ├── Window-Based Inference
|
||||
│ └── Decay-Based Pruning
|
||||
├── Convergence Properties
|
||||
│ ├── Deterministic Behavior
|
||||
│ ├── Steady-State Equilibrium
|
||||
│ └── Variance Reduction
|
||||
└── Analysis Frameworks
|
||||
├── Dynamical Systems View
|
||||
├── Statistical Inference View
|
||||
└── Control Theory View
|
||||
```
|
||||
|
||||
### 1.2 Conceptual Relationships
|
||||
|
||||
```
|
||||
METAPHORICAL MAPPING (Thermodynamics → Execution):
|
||||
Thermal Energy ≈ Execution Frequency (measurable count)
|
||||
Heat Dissipation ≈ Exponential Decay (time-based reduction)
|
||||
Thermal Equilibrium ≈ Steady-State Convergence (stable metrics)
|
||||
Temperature ≈ Normalized Frequency Rank
|
||||
Cooling Rate ≈ Decay Coefficient (λ)
|
||||
|
||||
LITERAL IMPLEMENTATIONS (No Metaphor):
|
||||
Frequency Counter → Integer increment on execution
|
||||
Decay Function → f(t) = f₀ * e^(-λt)
|
||||
Hot-Words Cache → Top-K frequency-sorted entries
|
||||
Rolling Window → Circular buffer of execution records
|
||||
ANOVA Test → Statistical variance analysis (Levene's test)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## II. TAXONOMY (Hierarchical Classification)
|
||||
|
||||
### 2.1 Adaptive Runtime Components
|
||||
|
||||
```
|
||||
1. Measurement Layer
|
||||
1.1 Execution Frequency Tracking
|
||||
└── Dictionary Entry Counter (per-word increment)
|
||||
1.2 Temporal Recording
|
||||
└── Rolling Window of Truth (circular buffer)
|
||||
1.3 Transition Tracking
|
||||
└── Word-to-Word Transition Matrix
|
||||
|
||||
2. Transformation Layer
|
||||
2.1 Decay Models
|
||||
2.1.1 Linear Decay (Loop #3)
|
||||
└── Δf = -k * Δt
|
||||
2.1.2 Exponential Decay (Loop #6 Inference)
|
||||
└── f(t) = f₀ * e^(-λt)
|
||||
2.2 Normalization
|
||||
2.2.1 Frequency Ranking
|
||||
2.2.2 Probability Calculation
|
||||
|
||||
3. Inference Layer
|
||||
3.1 Window Width Inference (Loop #5)
|
||||
3.1.1 Variance Analysis (Levene's Test)
|
||||
3.1.2 Binary Search (Variance Inflection Point)
|
||||
3.2 Decay Slope Inference (Loop #6)
|
||||
3.2.1 Exponential Regression
|
||||
3.2.2 Least Squares Fitting
|
||||
|
||||
4. Actuation Layer
|
||||
4.1 Hot-Words Cache (Loop #1)
|
||||
4.1.1 Frequency-Based Sorting
|
||||
4.1.2 Top-K Selection
|
||||
4.1.3 Fast-Path Dictionary Lookup
|
||||
4.2 Speculative Execution (Loop #4)
|
||||
4.2.1 Transition Probability Calculation
|
||||
4.2.2 Prefetch Decision
|
||||
|
||||
5. Coordination Layer
|
||||
5.1 Heartbeat System (Loop #7)
|
||||
5.1.1 Time-Driven Tick Generation
|
||||
5.1.2 Loop Orchestration
|
||||
5.1.3 Adaptive Tick Rate
|
||||
```
|
||||
|
||||
### 2.2 Feedback Loop Taxonomy
|
||||
|
||||
```
|
||||
Feedback Loops (7 Total)
|
||||
├── Positive Loops (Amplifying)
|
||||
│ ├── Loop #1: Execution Heat Tracking
|
||||
│ │ └── More executions → Higher rank → More cache hits → More executions
|
||||
│ └── Loop #4: Pipelining Metrics
|
||||
│ └── More transitions → Better prediction → More prefetch hits
|
||||
├── Negative Loops (Stabilizing)
|
||||
│ ├── Loop #3: Linear Decay
|
||||
│ │ └── High frequency → Faster decay → Lower frequency → Slower decay
|
||||
│ ├── Loop #5: Window Width Inference
|
||||
│ │ └── High variance → Smaller window → Lower variance
|
||||
│ └── Loop #6: Decay Slope Inference
|
||||
│ └── Unstable metrics → Steeper decay → Faster stabilization
|
||||
├── Neutral Loops (Monitoring)
|
||||
│ └── Loop #2: Rolling Window History
|
||||
│ └── Execution occurs → Record in window → Historical data available
|
||||
└── Meta-Loop (Adaptive Coordination)
|
||||
└── Loop #7: Adaptive Heartbeat
|
||||
└── Stable system → Slower ticks → Less overhead
|
||||
```
|
||||
|
||||
### 2.3 Convergence Taxonomy
|
||||
|
||||
```
|
||||
Convergence Properties
|
||||
├── Determinism
|
||||
│ ├── Algorithmic Determinism (same inputs → same outputs)
|
||||
│ ├── Temporal Determinism (time-invariant steady state)
|
||||
│ └── Statistical Determinism (0% variance)
|
||||
├── Steady-State Characteristics
|
||||
│ ├── Fixed Point (attractors in phase space)
|
||||
│ ├── Periodic Behavior (limit cycles)
|
||||
│ └── Chaotic Behavior (sensitive dependence - avoided)
|
||||
└── Variance Metrics
|
||||
├── Inter-Run Variance (across multiple executions)
|
||||
├── Intra-Run Variance (within single execution)
|
||||
└── Temporal Variance (across time windows)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## III. LEXICON (Precise Definitions)
|
||||
|
||||
### 3.1 Core Terms (Alphabetical)
|
||||
|
||||
**Adaptive Heartbeat**
|
||||
*Definition*: Time-driven coordination mechanism that orchestrates feedback loop execution at dynamically-adjusted intervals.
|
||||
*Formal*: Thread T executing `vm_tick()` at frequency f_tick, where f_tick ∈ [f_min, f_max] adapts based on system stability.
|
||||
*Measurement*: Tick period in nanoseconds (configurable: HEARTBEAT_TICK_NS).
|
||||
*Category*: Coordination mechanism.
|
||||
|
||||
**Attractor**
|
||||
*Definition*: Stable equilibrium point or region in phase space toward which execution trajectories converge.
|
||||
*Formal*: Fixed point x* where F(x*) = x* for dynamical system x_{t+1} = F(x_t).
|
||||
*Measurement*: Coordinates in (window_size, decay_slope, variance) space.
|
||||
*Category*: Dynamical systems concept.
|
||||
|
||||
**Decay Coefficient (λ)**
|
||||
*Definition*: Rate parameter controlling exponential reduction in execution frequency over time.
|
||||
*Formal*: λ in f(t) = f₀ * e^(-λt), units of [1/time].
|
||||
*Measurement*: Derived via exponential regression; stored as Q48.16 fixed-point.
|
||||
*Category*: Transformation parameter.
|
||||
|
||||
**Deterministic Convergence**
|
||||
*Definition*: Property whereby repeated executions of identical workloads produce statistically indistinguishable steady-state metrics.
|
||||
*Formal*: ∀ executions i,j: |metric_i - metric_j| / σ < ε, where ε → 0 as t → ∞.
|
||||
*Measurement*: Coefficient of variation CV = σ/μ → 0%.
|
||||
*Category*: Convergence property.
|
||||
|
||||
**Execution Frequency**
|
||||
*Definition*: Count of times a dictionary entry has been executed since VM initialization, optionally adjusted by decay.
|
||||
*Formal*: f = Σ executions - ∫ decay(t) dt.
|
||||
*Measurement*: Unsigned 64-bit integer (`uint64_t execution_heat`).
|
||||
*Category*: Primary measurable quantity.
|
||||
*Note*: "Heat" is metaphorical naming; actual quantity is frequency.
|
||||
|
||||
**Exponential Decay**
|
||||
*Definition*: Mathematical function modeling reduction in execution frequency proportional to current value.
|
||||
*Formal*: f(t) = f₀ * e^(-λt), where f₀ is initial frequency.
|
||||
*Measurement*: Applied periodically by heartbeat system.
|
||||
*Category*: Transformation function.
|
||||
*Metaphor*: Analogous to radioactive decay or thermal dissipation.
|
||||
|
||||
**Hot-Words Cache**
|
||||
*Definition*: Fixed-size array storing pointers to the K most frequently executed dictionary entries for O(1) lookup acceleration.
|
||||
*Formal*: Cache C = {e_1, e_2, ..., e_K} where f(e_i) ≥ f(e_{i+1}) ∀ i.
|
||||
*Measurement*: Cache size K (configurable: HOTWORDS_CACHE_SIZE).
|
||||
*Category*: Frequency-based optimization.
|
||||
|
||||
**Levene's Test**
|
||||
*Definition*: Non-parametric statistical test for homogeneity of variance across groups.
|
||||
*Formal*: H₀: σ₁² = σ₂² = ... = σ_k² (variances equal).
|
||||
*Measurement*: F-statistic with p-value threshold (typically 0.05).
|
||||
*Category*: Statistical inference method.
|
||||
*Application*: Used to detect variance changes when adjusting window size.
|
||||
|
||||
**Phase Space**
|
||||
*Definition*: Multi-dimensional coordinate system where each axis represents a system state variable.
|
||||
*Formal*: Space S = {(w, λ, σ²) | w ∈ ℕ, λ ∈ ℝ⁺, σ² ∈ ℝ⁺}.
|
||||
*Measurement*: Coordinates: (window_size, decay_slope_Q48, variance_Q48).
|
||||
*Category*: Dynamical systems representation.
|
||||
|
||||
**Rolling Window of Truth**
|
||||
*Definition*: Circular buffer recording recent execution history for deterministic metric seeding.
|
||||
*Formal*: Buffer B[i] = word_id at execution event i mod |B|.
|
||||
*Measurement*: Buffer size (configurable: ROLLING_WINDOW_SIZE = 4096).
|
||||
*Category*: Temporal recording mechanism.
|
||||
*Purpose*: Ensures identical initial conditions for reproducibility.
|
||||
|
||||
**Steady-State Equilibrium**
|
||||
*Definition*: Condition where adaptive system metrics stabilize within bounded oscillation.
|
||||
*Formal*: ∃ t_0: ∀ t > t_0, |x(t) - x*| < δ for small δ.
|
||||
*Measurement*: Variance CV < 0.1% over 1000-tick window.
|
||||
*Category*: Convergence property.
|
||||
*Metaphor*: Analogous to thermodynamic equilibrium.
|
||||
|
||||
**Thermodynamic Metaphor**
|
||||
*Definition*: Conceptual mapping between thermodynamic quantities and execution metrics.
|
||||
*Mapping*:
|
||||
- Heat ↔ Execution Frequency
|
||||
- Temperature ↔ Normalized Rank
|
||||
- Cooling ↔ Exponential Decay
|
||||
- Equilibrium ↔ Steady State
|
||||
*Category*: Conceptual framework (not literal physics).
|
||||
*Warning*: Must qualify as metaphor in academic writing.
|
||||
|
||||
**Transition Probability**
|
||||
*Definition*: Conditional probability that word B is executed immediately after word A.
|
||||
*Formal*: P(B|A) = count(A→B) / count(A).
|
||||
*Measurement*: Stored as Q48.16 fixed-point in transition matrix.
|
||||
*Category*: Derived metric for speculative execution.
|
||||
|
||||
**Variance Inflection Point**
|
||||
*Definition*: Window size w* where variance begins to increase when window shrinks below w*.
|
||||
*Formal*: w* = arg min_w { Var(w) | w < w_current }.
|
||||
*Measurement*: Found via binary search with Levene's test.
|
||||
*Category*: Inference target.
|
||||
*Purpose*: Optimal window size for stable metrics.
|
||||
|
||||
**Word Transition**
|
||||
*Definition*: Sequential execution of word B immediately following word A in threaded code.
|
||||
*Formal*: Event (A, B, t) where A executes at time t and B at time t+ε.
|
||||
*Measurement*: Recorded in transition_metrics.history[] circular buffer.
|
||||
*Category*: Execution event.
|
||||
|
||||
---
|
||||
|
||||
### 3.2 Avoid/Deprecated Terms
|
||||
|
||||
| ❌ Avoid | ✅ Use Instead | Reason |
|
||||
|---------|---------------|--------|
|
||||
| "Physics-based" | "Thermodynamically-inspired metaphor" | Not literal physics |
|
||||
| "Execution heat" (in formal writing) | "Execution frequency with decay" | "Heat" is metaphorical |
|
||||
| "Temperature" | "Normalized frequency rank" | No actual thermal quantity |
|
||||
| "Quantum-inspired" | N/A | No quantum mechanics involved |
|
||||
| "AI-driven" | "Statistically-inferred" | No neural networks or ML |
|
||||
| "Learning" | "Adaptive inference" | Not machine learning |
|
||||
| "Training" | "Convergence to steady state" | Not supervised learning |
|
||||
|
||||
---
|
||||
|
||||
## IV. MATHEMATICAL FORMALISM
|
||||
|
||||
### 4.1 Execution Frequency Evolution
|
||||
|
||||
**Discrete-time update**:
|
||||
```
|
||||
f[t+1] = f[t] + Δexec[t] - Δdecay[t]
|
||||
|
||||
where:
|
||||
Δexec[t] = 1 if word executed at tick t, else 0
|
||||
Δdecay[t] = λ * f[t] * Δt (exponential decay approximation)
|
||||
```
|
||||
|
||||
**Continuous-time model**:
|
||||
```
|
||||
df/dt = r(t) - λf(t)
|
||||
|
||||
where:
|
||||
r(t) = execution rate [executions/second]
|
||||
λ = decay coefficient [1/second]
|
||||
|
||||
Solution:
|
||||
f(t) = e^(-λt) * [f₀ + ∫₀ᵗ r(τ)e^(λτ) dτ]
|
||||
```
|
||||
|
||||
### 4.2 Hot-Words Cache Selection
|
||||
|
||||
**Cache membership criterion**:
|
||||
```
|
||||
e ∈ Cache ⟺ rank(e) ≤ K
|
||||
|
||||
where:
|
||||
rank(e) = |{e' ∈ Dictionary : f(e') > f(e)}| + 1
|
||||
K = cache size (constant)
|
||||
```
|
||||
|
||||
### 4.3 Window Width Inference
|
||||
|
||||
**Objective function**:
|
||||
```
|
||||
w* = arg min { Var(w) : w ∈ [w_min, w_current] }
|
||||
|
||||
Subject to:
|
||||
Levene(w, w_current) → F-statistic
|
||||
p-value(F) < α (typically α = 0.05)
|
||||
```
|
||||
|
||||
### 4.4 Decay Slope Inference
|
||||
|
||||
**Exponential regression**:
|
||||
```
|
||||
Given: {(t_i, f_i)}_{i=1}^N from rolling window
|
||||
|
||||
Model: f(t) = f₀ * e^(-λt)
|
||||
|
||||
Log-transform: ln(f) = ln(f₀) - λt
|
||||
|
||||
Least squares:
|
||||
λ* = arg min Σ [ln(f_i) - (ln(f₀) - λt_i)]²
|
||||
```
|
||||
|
||||
### 4.5 Transition Probability
|
||||
|
||||
**Maximum likelihood estimate**:
|
||||
```
|
||||
P(B|A) = count(A→B) / count(A)
|
||||
|
||||
where:
|
||||
count(A→B) = # times B executed immediately after A
|
||||
count(A) = # times A executed
|
||||
```
|
||||
|
||||
### 4.6 Convergence Metric
|
||||
|
||||
**Coefficient of Variation**:
|
||||
```
|
||||
CV = σ / μ
|
||||
|
||||
where:
|
||||
σ = √(Var[metric]) = standard deviation
|
||||
μ = E[metric] = mean
|
||||
|
||||
Convergence achieved when: CV → 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## V. RELATIONSHIP DIAGRAM
|
||||
|
||||
### 5.1 Component Dependencies
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Execution Event │
|
||||
│ (word executed) │
|
||||
└──────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
┌───────────┴───────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌────────────────┐ ┌──────────────────┐
|
||||
│ Frequency │ │ Rolling Window │
|
||||
│ Increment │ │ Recording │
|
||||
│ (Loop #1) │ │ (Loop #2) │
|
||||
└────────┬───────┘ └─────────┬────────┘
|
||||
│ │
|
||||
│ │
|
||||
▼ ▼
|
||||
┌────────────────┐ ┌──────────────────┐
|
||||
│ Decay │ │ Inference │
|
||||
│ Application │ │ Engine │
|
||||
│ (Loop #3,#6) │ │ (Loop #5,#6) │
|
||||
└────────┬───────┘ └─────────┬────────┘
|
||||
│ │
|
||||
└───────────┬────────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────┐
|
||||
│ Hot-Words Cache │
|
||||
│ Reorganization │
|
||||
└───────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────────────────┐
|
||||
│ Optimized Lookup │
|
||||
│ (O(1) cache hit) │
|
||||
└───────────────────────┘
|
||||
```
|
||||
|
||||
### 5.2 Feedback Loop Interactions
|
||||
|
||||
```
|
||||
Execution → Frequency ↑ → Cache Rank ↑ → Lookup Speed ↑ → More Execution
|
||||
▲ │
|
||||
│ │
|
||||
└─────────────── (Positive Feedback) ────────────┘
|
||||
|
||||
Frequency ↑ → Decay ↑ → Frequency ↓ → Decay ↓ → Frequency Stabilizes
|
||||
▲ │
|
||||
│ │
|
||||
└──── (Negative Feedback) ────┘
|
||||
|
||||
Variance ↑ → Window Shrink → Variance ↓ → Window Stable
|
||||
▲ │
|
||||
│ │
|
||||
└──── (Negative Feedback) ──┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## VI. ONTOLOGICAL COMMITMENTS
|
||||
|
||||
### 6.1 Foundational Assumptions
|
||||
|
||||
1. **Frequency as Proxy for Importance**
|
||||
- Assumption: Frequently executed code is more important to optimize
|
||||
- Justification: Empirical validation (Zipf's law in execution patterns)
|
||||
|
||||
2. **Decay Models Temporal Relevance**
|
||||
- Assumption: Recent executions are more relevant than distant past
|
||||
- Justification: Locality of reference (temporal locality principle)
|
||||
|
||||
3. **Determinism Through Convergence**
|
||||
- Assumption: Adaptive systems can converge to deterministic steady states
|
||||
- Justification: Fixed-point theorems for contractive mappings
|
||||
|
||||
4. **Statistical Inference Validity**
|
||||
- Assumption: Execution patterns are statistically analyzable
|
||||
- Justification: Central Limit Theorem for large sample sizes
|
||||
|
||||
### 6.2 Scope Limitations
|
||||
|
||||
**What This Ontology DOES Cover**:
|
||||
- ✓ Execution frequency measurement and decay
|
||||
- ✓ Adaptive caching and inference mechanisms
|
||||
- ✓ Dynamical systems characterization
|
||||
- ✓ Statistical convergence properties
|
||||
|
||||
**What This Ontology DOES NOT Cover**:
|
||||
- ✗ Actual thermodynamic processes (metaphor only)
|
||||
- ✗ Machine learning or neural networks (no training)
|
||||
- ✗ Quantum computing (no quantum effects)
|
||||
- ✗ Biological neural systems (no biomimicry)
|
||||
|
||||
---
|
||||
|
||||
## VII. USAGE GUIDELINES
|
||||
|
||||
### 7.1 Academic Writing
|
||||
|
||||
**In Abstracts/Titles**: Use precise, non-metaphorical terms
|
||||
```
|
||||
✅ "Thermodynamically-Inspired Adaptive Runtime"
|
||||
❌ "Physics-Based Virtual Machine"
|
||||
```
|
||||
|
||||
**In Technical Sections**: Qualify metaphors explicitly
|
||||
```
|
||||
✅ "We employ a thermodynamic metaphor where execution frequency
|
||||
is treated as 'heat' that dissipates over time..."
|
||||
❌ "The physics model applies heat decay..."
|
||||
```
|
||||
|
||||
**In Formalism**: Use mathematical definitions, not analogies
|
||||
```
|
||||
✅ "Frequency evolves as f(t) = f₀ * e^(-λt)"
|
||||
❌ "Heat cools exponentially like in Newton's law"
|
||||
```
|
||||
|
||||
### 7.2 Code Comments
|
||||
|
||||
**In Source Code**: Use concrete terms from lexicon
|
||||
```c
|
||||
// ✅ GOOD: Increment execution frequency counter
|
||||
entry->execution_heat++;
|
||||
|
||||
// ❌ BAD: Increase temperature of word
|
||||
entry->execution_heat++; // heat up!
|
||||
```
|
||||
|
||||
### 7.3 Presentation/Talks
|
||||
|
||||
**Slides**: Use metaphor for intuition, then formalize
|
||||
```
|
||||
Slide 1: "Think of execution frequency like heat..."
|
||||
Slide 2: "Formally: f(t) = f₀ * e^(-λt)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## VIII. REFERENCES
|
||||
|
||||
### 8.1 Foundational Concepts
|
||||
|
||||
- **Dynamical Systems**: Strogatz, S. (2015). *Nonlinear Dynamics and Chaos*
|
||||
- **Control Theory**: Åström, K. & Murray, R. (2008). *Feedback Systems*
|
||||
- **Statistical Inference**: Casella, G. & Berger, R. (2002). *Statistical Inference*
|
||||
- **Exponential Decay**: Standard mathematical function (e^(-λt))
|
||||
|
||||
### 8.2 Related Work
|
||||
|
||||
- **Trace-based JIT**: Bolz et al. (2009). "Tracing the Meta-Level: PyPy's Tracing JIT Compiler"
|
||||
- **Adaptive Systems**: Garlan et al. (2004). "Rainbow: Architecture-Based Self-Adaptation"
|
||||
- **Forth Optimization**: Ertl, M.A. (1996). "Stack Caching for Interpreters"
|
||||
|
||||
---
|
||||
|
||||
## IX. STADIUM / FABRIC VOCABULARY (v1.1 extension)
|
||||
|
||||
**Added 2026-08-13**, closing a real, acknowledged documentation-debt item: the physics
|
||||
vocabulary built on top of the original heat/decay/inference model above (patron, mass,
|
||||
density, Stadium, and the Kconfig knob names built on them) accumulated across
|
||||
`FABRIC-0.md`/`FABRIC-1.md` without an explicit statement, in one place, that these are named
|
||||
analogies — the same discipline Section III already applies to "heat," extended to the
|
||||
newer terms. Definitions below are drawn directly from `FABRIC-0.md`'s own DECIDED sections
|
||||
(cited inline), not invented here.
|
||||
|
||||
### 9.1 The metaphor, restated for this layer
|
||||
|
||||
```
|
||||
METAPHORICAL MAPPING (Stadium → Runtime state):
|
||||
Stadium ≈ Bounded live-state region (a venue, not a building)
|
||||
Patron ≈ A live thing occupying floor space in that region
|
||||
Mass ≈ How much floor space a patron occupies
|
||||
Density ≈ How much heat a patron holds per unit of floor space
|
||||
Warehouse ≈ What is not currently live (Artemis, the dictionary at rest)
|
||||
Utility ≈ Infrastructure beside the Stadium, never inside it (framebuffer)
|
||||
|
||||
LITERAL IMPLEMENTATIONS (No Metaphor):
|
||||
Stadium → A fixed-size array of cells, index-linked, allocated at boot
|
||||
Patron → A cell (or linked run of cells) with a nine-field header
|
||||
Mass → Integer count of cells a patron occupies (≥ 1)
|
||||
Density → heat ÷ mass, a derived ratio, not stored
|
||||
K → Σ execution_heat_q48 across live entries, conserved at 1.0
|
||||
```
|
||||
|
||||
The Stadium metaphor is a **venue**, not a building: §2 (`FABRIC-0.md`) is explicit that a
|
||||
patron *arrives and departs* — the Stadium holds what is live, not everything that exists.
|
||||
That is the load-bearing distinction between "Stadium" and "warehouse" below, and it is why
|
||||
the dictionary-at-rest is warehouse, not Stadium, even though every dictionary entry is
|
||||
*eligible* to become a patron.
|
||||
|
||||
### 9.2 Lexicon additions (alphabetical, same format as Section III)
|
||||
|
||||
**Cell**
|
||||
*Definition*: The Stadium's fixed-size unit of storage — a patron header, or a continuation
|
||||
cell owned by exactly one patron.
|
||||
*Formal*: A closed, two-constructor sum type: `Cell = Header | Continuation(owner)`, fixed
|
||||
at build time (`FABRIC-0.md` §3).
|
||||
*Measurement*: Proposed size 64 bytes (`FABRIC-0.md` §23.3, **not yet built** — see
|
||||
`FABRIC-1.md`'s §23.4 #2 closure: the one real patron kind actually shipped, Hermes
|
||||
messages, did not adopt this cell shape).
|
||||
*Category*: Storage unit.
|
||||
*Warning*: Not a pointer target — links between cells are Stadium-array indices, never
|
||||
addresses (`FABRIC-0.md` §3, §13).
|
||||
|
||||
**Code field**
|
||||
*Definition*: A closed-enumeration behaviour tag stored in a patron header, dispatched by
|
||||
the engine rather than branched on by kind.
|
||||
*Formal*: `behaviour ∈ {MIGRATE, DELIVER, EXPIRE, COOL, ...}` — a fixed datatype, not an
|
||||
open extension point (`FABRIC-0.md` §3, §13, §18.3).
|
||||
*Category*: Dispatch mechanism.
|
||||
*Note*: This is deliberately **the entire type system** — the engine never asks what kind
|
||||
of patron it holds, only what its code field says to do (`FABRIC-0.md` §3).
|
||||
|
||||
**Density**
|
||||
*Definition*: A patron's heat share divided by its mass — heat per unit of occupied
|
||||
Stadium floor space.
|
||||
*Formal*: `density(p) = heat(p) / mass(p)`, derived, never stored (`FABRIC-0.md` §19.2).
|
||||
*Measurement*: Used for ranking and admission (denser evicts sparser), not computed by a
|
||||
separate scheduler — it is read, not decided (`FABRIC-0.md` §19.3).
|
||||
*Category*: Derived quantity.
|
||||
*Warning*: This is the one place the thermodynamic metaphor is literal in form (quantity
|
||||
per unit volume) even though heat itself remains metaphorical.
|
||||
|
||||
**K (conservation constant)**
|
||||
*Definition*: The sum of every live entry's normalised heat share.
|
||||
*Formal*: `K = Σ execution_heat_q48`, held at `Q48_ONE` (1.0) by construction — verified by
|
||||
`vm_physics_conserved()` (`capsule_vm_physics.c`).
|
||||
*Measurement*: A correctness check, not a diagnostic — it is 1.0 by construction and can
|
||||
never itself signal system state (`FABRIC-0.md` §14, §19.1). Entropy over the heat
|
||||
*distribution* is the actual instrument; K is not.
|
||||
*Category*: Invariant.
|
||||
*Warning*: Not an occupancy ratio (`Σmass / capacity`) — that definition was considered and
|
||||
explicitly rejected because it contradicts the already-implemented, tested mechanism
|
||||
(`FABRIC-0.md` §19.1).
|
||||
|
||||
**Mass**
|
||||
*Definition*: The number of Stadium cells a patron occupies — its footprint.
|
||||
*Formal*: Integer ≥ 1, an entry field independent of heat; never enters K
|
||||
(`FABRIC-0.md` §3, §19.2).
|
||||
*Category*: Structural quantity, not conserved.
|
||||
*Note*: A large patron is not "referenced" from the Stadium — it simply occupies more
|
||||
cells. By-reference storage is reserved for things outside the Stadium entirely
|
||||
(`FABRIC-0.md` §19.3, §23.1).
|
||||
|
||||
**Mass conservation vs. heat conservation**
|
||||
*Definition*: Two independent invariants that must not be confused. Heat (K) is conserved
|
||||
globally and moved by traffic. Mass is conserved *per patron* during a residency —
|
||||
identity and mass never change as a side effect of use — but is not a fleet-wide summed
|
||||
invariant the way heat is (`FABRIC-0.md` §24.2).
|
||||
|
||||
**Patron**
|
||||
*Definition*: A live thing occupying a Stadium cell or linked run of cells — the unit the
|
||||
Stadium actually holds.
|
||||
*Formal*: One structure, no variants, no type field — identified by its code field's
|
||||
behaviour, not by a kind tag (`FABRIC-0.md` §3, §13).
|
||||
*Measurement*: Nine header wires: identity, heat, TTL, pin, link, code field, mass,
|
||||
payload, contains (`FABRIC-0.md` §3, with amendments cited in place).
|
||||
*Category*: The Stadium's sole occupant type.
|
||||
*Known kinds (as of this writing)*: words (dictionary — fully migrated and live via
|
||||
`stadium_word_dispatch()`, `FABRIC-0.md` §16.3/§17.7), blocks (Artemis), messages (Hermes — shipped with a
|
||||
**different** shape than this header, see `FABRIC-1.md`'s §23.4 #2 closure), VMs (Tripod,
|
||||
`FABRIC-0.md` §20), dirty events (console, `FABRIC-0.md` §17.5). Five kinds, not four — an
|
||||
earlier draft undercounted (`FABRIC-0.md` §1).
|
||||
|
||||
**Reap**
|
||||
*Definition*: The event that ends a patron's residency — not destruction, a return of its
|
||||
cells and remaining heat to the pool.
|
||||
*Formal*: Consumed on delivery (messages), on redraw (dirty events), on cooling below floor
|
||||
(VMs), or on eviction by a denser newcomer (`FABRIC-0.md` §17.2, §17.5, §19.3).
|
||||
*Category*: Lifecycle event.
|
||||
|
||||
**Stadium**
|
||||
*Definition*: A single, bounded region of memory outside any VM, holding everything
|
||||
currently live.
|
||||
*Formal*: A fixed-size array of cells, index-linked (not pointer-linked), allocated at
|
||||
boot before any VM exists, not part of the heap (`FABRIC-0.md` §2, §17.6).
|
||||
*Measurement*: Per-VM quota over a single global cell pool, elastic under pressure,
|
||||
arbitrated by Hera (`FABRIC-0.md` §22) — not per-VM physical regions.
|
||||
*Category*: The bounded live-state substrate this whole vocabulary describes.
|
||||
*Warning*: "Live" is the operative word — the Stadium holds what is live, not everything
|
||||
that exists. A dictionary word sitting cold and unexecuted is warehouse, not Stadium, even
|
||||
once the dictionary itself migrates into this representation (`FABRIC-0.md` §2, §16.3).
|
||||
|
||||
**Warehouse**
|
||||
*Definition*: What lies beneath the Stadium — storage for things not currently live.
|
||||
*Formal*: Artemis (block storage) and the resting dictionary are warehouse; the Stadium is
|
||||
the floor above it (`FABRIC-0.md` §17.5's three-category taxonomy: warehouse / Stadium /
|
||||
utility).
|
||||
*Category*: Storage layer, outside the engine's live-state model.
|
||||
|
||||
**Utility**
|
||||
*Definition*: External infrastructure the Stadium consumes but never contains — beside the
|
||||
building, not inside or beneath it.
|
||||
*Formal*: The framebuffer and devices generally; the engine never learns about a utility's
|
||||
existence, which is what makes it a boundary rather than an exception (`FABRIC-0.md` §17.5).
|
||||
*Category*: Third taxonomy category, coordinate with warehouse and Stadium, not a
|
||||
sub-case of either.
|
||||
|
||||
### 9.3 Kconfig knob names built on this vocabulary
|
||||
|
||||
Per Bob's own flag: code, Kconfig, and documentation terminology must not drift apart.
|
||||
The table below is the current, verified wiring status of every `STADIUM_*` Kconfig
|
||||
symbol — confirmed by direct source grep during the `FABRIC-1.md` closure work this
|
||||
vocabulary accompanies, not assumed from the symbol's name or Kconfig help text alone.
|
||||
|
||||
| Kconfig symbol | Vocabulary term | Wiring status (2026-08-13) |
|
||||
|---|---|---|
|
||||
| `STADIUM_MAX_VM_COUNT` | Patron (VM kind) population bound | **Live** — read in `stadium.c`, `stadium_words.c`, `capsule_birth.c` |
|
||||
| `STADIUM_MEMORY_PERCENT` | Stadium capacity sizing | **Live** — read in `stadium.c` |
|
||||
| `STADIUM_WORD_HEAT_QUANTUM` | Heat (word-patron admission) | **Live** — read in `stadium_words.c` |
|
||||
| `STADIUM_WORD_COOL_RATE_Q48` | Heat decay rate (word patrons) | **Live** — read in `stadium_words.c` |
|
||||
| `STADIUM_CONTAINS_DEPTH_MAX` | `contains` wire depth cap (§3, §8) | **Declared, not yet enforced** — reap-gating enforcement is item 3.5's scope, not implemented (`FABRIC-1.md` §A item 5.3 area; not a bug, a known sequencing gap) |
|
||||
| `STADIUM_CAPACITY_TICK` | Capacity-loop cadence, distinct from heat's (§22.4) | **Dead** — declared with the correct intended default (1000, matching §22.4's precedent) but never read by any code path; only appearance beyond its own default is a compile-time `> 0` static assertion in `stadium.h`. Found and reported during this session's §12 Q5 closure (`FABRIC-1.md`) — `vm_physics_heartbeat_tick()` still literally shares `HEARTBEAT_INFERENCE_FREQUENCY` instead |
|
||||
|
||||
This table is a snapshot, not a promise — it will drift the moment any of these symbols is
|
||||
wired or unwired. Re-verify against source before relying on it for anything past a casual
|
||||
read; do not assume "Live" here still means live without checking.
|
||||
|
||||
---
|
||||
|
||||
## X. VERSION HISTORY
|
||||
|
||||
**v1.1** (2026-08-13):
|
||||
- Added Section IX: Stadium/Fabric vocabulary (patron, mass, density, Stadium, warehouse,
|
||||
utility, K, cell, code field, reap) as named analogies, matching Section III's existing
|
||||
discipline for the original heat/decay vocabulary
|
||||
- Added a Kconfig knob-name-to-concept mapping table with verified (not assumed) wiring
|
||||
status, per Captain Bob's 2026-08-04 flag that code/Kconfig/documentation terminology
|
||||
must not drift apart
|
||||
|
||||
**v1.0** (2025-12-13):
|
||||
- Initial ontology, taxonomy, and lexicon
|
||||
- Formal mathematical definitions
|
||||
- Relationship diagrams
|
||||
- Usage guidelines
|
||||
|
||||
---
|
||||
|
||||
## XI. ACKNOWLEDGMENTS
|
||||
|
||||
This ontology provides the formal conceptual framework for the StarForth project.
|
||||
It aims to eliminate ambiguity and enable precise academic discourse while
|
||||
acknowledging the metaphorical nature of certain conceptual mappings.
|
||||
|
||||
**License**: See ./LICENSE
|
||||
@@ -0,0 +1,587 @@
|
||||
# LithosAnanke Roadmap
|
||||
|
||||
**Branch:** `master` (post-split: this repo's `master` is the sole LithosAnanke production
|
||||
line; the old `lithosananke` branch belonged to the pre-split combined monorepo and no
|
||||
longer exists here — see `.claude/CLAUDE.md`'s "On the branch topology" note)
|
||||
**Current:** M7.1 in progress — see `FABRIC-0.md`/`FABRIC-1.md`/`FABRIC-2.md` for real status
|
||||
|
||||
---
|
||||
|
||||
## Release Versioning Policy (ratified 2026-09-19, FABRIC-3.5.md §XXX — supersedes the
|
||||
2026-08-28 policy below)
|
||||
|
||||
This is the standing release-versioning policy for LithosAnanke. It is authoritative; the
|
||||
FABRIC documents track implementation against it, and any permanent change to the policy
|
||||
belongs here.
|
||||
|
||||
**Superseded 2026-09-19.** The original 2026-08-28 policy (`X.0.0` = QEMU release, `X.5.0` =
|
||||
hardware bare-metal release, **even** major = LTS) used the minor field to carry a milestone
|
||||
meaning and had the LTS parity backwards relative to the ratified policy below. It is kept
|
||||
struck through immediately after this note for historical traceability, not as current rule.
|
||||
|
||||
- **Major: ODD = LTS line** (non-breaking fixes only — backports, never redesigns). **EVEN =
|
||||
working line** (breaking changes land here). The line you are on tells you whether breakage
|
||||
is possible at all — not the major number's parity signaling "supported vs. not" the way the
|
||||
retired policy read it.
|
||||
- **Minor** — release within that line. No longer carries a milestone meaning (`X.5.0` no
|
||||
longer means anything special) — milestones are roadmap entries now, not arithmetic.
|
||||
- **Patch** — working builds within a release.
|
||||
- **`LITHOS_VERSION` (kernel) and `VERSION` (embedded StarForth engine) are independent and do
|
||||
not auto-sync** — do not infer a relationship if they ever coincide.
|
||||
- **Engine `VERSION` bumps at tag time by what actually changed:** major = any
|
||||
FORTH-79-visible word semantics change; minor = words added/removed/relocated; patch =
|
||||
build-only, no dictionary-visible change.
|
||||
|
||||
```
|
||||
v1.0.x — serial-only production (released, historical encoding)
|
||||
v1.5.x — framebuffer VT100 console milestone (released, historical encoding)
|
||||
v2.0.0 — QEMU release: three-arch QEMU story complete (released)
|
||||
v2.0.1 — SER5 hardware-track line (historical; superseded by this policy)
|
||||
v2.1.0 — Tripod/kernel reshuffle: Hermes into the kernel, Tripod = Hera/Artemis/Hestia
|
||||
v2.2.0 — amd64 bare-metal bring-up (Beelink SER5)
|
||||
v2.x — further working releases; riscv64 / aarch64 board bring-up
|
||||
v3.0.0 — FIRST LTS, when and only when all 5 criteria below are met
|
||||
```
|
||||
|
||||
**LTS criteria (a tag may take an odd major only when all five hold):** real-hardware boot
|
||||
demonstrated on at least the amd64 reference board, logs committed as audit artifacts; no
|
||||
known-reproducible silent-failure defect open; ACL Phase 8 closed (or explicitly scoped out in
|
||||
writing); the `proof/` boundary restated and not contracted relative to the prior LTS; a soak —
|
||||
architecture unchanged for at least one full release cycle. The first tag meeting all five
|
||||
takes the odd major — no judgement call.
|
||||
|
||||
See `FABRIC-3.5.md` §XXX for the full ratified policy text and reasoning, and `FABRIC-2.md` §G
|
||||
for the release-gate punch lists this ladder replaced the arithmetic reading of.
|
||||
|
||||
<details>
|
||||
<summary>Historical: the retired 2026-08-28 policy (kept for traceability only)</summary>
|
||||
|
||||
- ~~`X.0.0` — QEMU release. A whole-number minor in the `X.0` position is the QEMU-only
|
||||
release: the point where the three-architecture (amd64/aarch64/riscv64) story under QEMU
|
||||
is complete and defensible on its own terms.~~
|
||||
- ~~`X.5.0` — Hardware bare-metal release. Real-hardware-only work gates `X.5.0`, not
|
||||
`X.0.0`.~~
|
||||
- ~~Even major numbers are LTS.~~ **Wrong — the ratified policy is odd = LTS.**
|
||||
|
||||
</details>
|
||||
|
||||
**Board-by-board hardware rollout, decided 2026-08-29 (extends the above as boards come
|
||||
online; corrected 2026-09-22 for the ratified §XXX policy — these are working-line minor
|
||||
releases, not LTS cuts, since v2.x is the even/working line).** Real silicon is arriving
|
||||
incrementally (Beelink SER5 in hand now; RasPi 5 + Milk-V orderable around Mon 2026-08-31), so
|
||||
the hardware release is split per real board in hand, each its own minor cut on the v2.x
|
||||
working line (v2.1.0, the Tripod/kernel reshuffle, landed first — see the ladder above):
|
||||
|
||||
- **v2.2.0 — amd64 bare metal.** Beelink SER5 (in hand). Gate: the generic GPT/FAT32
|
||||
thumbdrive image (`make -f Makefile.starkernel ARCH=amd64 thumbdrive`) flashes to and boots
|
||||
on the real SER5 via its real UEFI; POST + `ok>`; the amd64 **RDRAND** backend
|
||||
(`rng: backend = rdrand`) serves live entropy; and a real Zuse identity is minted on a
|
||||
second thumbdrive in real hardware and re-attaches. Two 16 GB sticks available: one to boot
|
||||
the image, one to mint the Zuse user.
|
||||
- **v2.4.0 — aarch64 bare metal.** Raspberry Pi 5 (orderable ~Mon 2026-08-31). Gate: boots
|
||||
on the real board, aarch64 peripheral-RNG backend live, Zuse mint/attach on real media.
|
||||
- **v2.5.0 — all three bare metal.** Adds Milk-V (riscv64) to the above; the Zkr (RNDR)
|
||||
backend live. This is the full "same story on all three arches on real silicon" cut.
|
||||
|
||||
**Beyond v2.5.0 — Zynq FPGA is the next big milestone, after a hardening phase.** Once the
|
||||
three general-purpose boards close, the next major transfer is to a **Zynq (AMD Xilinx)
|
||||
FPGA** SoC — the step where the battle-tested amd64/aarch64/riscv64 story rides on
|
||||
configurable silicon. That is a genuinely bigger milestone than any single prior board: an
|
||||
FPGA demands its platform be carried forward rather than ported with trivial
|
||||
architecture-delta work, and it reshapes the hardware story (soft/hard CPU cores, PL fabric,
|
||||
non-standard memory map, custom peripherals). Expect a new even-major line for it once the
|
||||
coloring-in period below lands.
|
||||
|
||||
**The FPGA creates a three-product split, decided 2026-08-29.** The Zynq is not just a fourth
|
||||
platform to port to — it is the pivot that forces the project to separate into three
|
||||
distinct products, each with its own host, delivery, and proof character. This is the neat
|
||||
partition the FPGA's configurable-silicon nature makes possible and demands:
|
||||
|
||||
1. **Hosted StarForth** — the hosted/interpreted StarForth product that already exists
|
||||
(3-arch acceptance-tested; runs StarForth hosted on an OS). Its delivery is the Forth
|
||||
+ VM + capsule semantics as a portable, embeddable interpreted runtime.
|
||||
2. **A full StarshipOS** — the standalone operating system built on LithosAnanke
|
||||
(LithosAnanke → StarshipOS). Its delivery is a self-booting OS on general-purpose
|
||||
silicon (the SER5/RasPi/Milk-V line already covers this).
|
||||
3. **Hardware steady-state machinery with sealed executions, HOL-proven** — the
|
||||
FPGA-native product: hardware-enforced sealed executions and steady-state machinery
|
||||
whose guarantees are machine-checked in a proof assistant (HOL). This is the product only
|
||||
configurable silicon can honor — secret/hardware-boundary enforcement and formally
|
||||
verified behavior carried in silicon rather than software. Its delivery is the bitstream +
|
||||
the HOL proof artifacts (not just a port of the OS).
|
||||
|
||||
The coupling is the point: the FPGA is where product 3 is born, and product 3's existence is
|
||||
what cleanly separates 1 from 2 from 3. Product 1 ships hosted on an OS; product 2 ships as
|
||||
an OS on general silicon; product 3 ships as proven hardware. Anything that straddles those
|
||||
boundaries after this decision is a conscious product-line choice, not an accident of history.
|
||||
Scoping of each product's concrete gates, and the new even-major line that carries products 1
|
||||
through 3 as separate tracks, is defined at v2.5.0 close / during the coloring-in phase.
|
||||
(This complements, and does not retract, the existing "hardware bare-metal release" policy
|
||||
above.)
|
||||
|
||||
**IP framing of the three products — 3 patent applications + 3 marks, decided 2026-08-29.**
|
||||
The three products are treated as **three patent applications**, each matched to a trademark,
|
||||
with one caveat about the pre-existing provisional:
|
||||
|
||||
- **Patent 1 + mark StarForth.** The hosted/interpreted StarForth runtime product.
|
||||
- **Patent 2 + marks StarshipOS + LithosAnanke.** The full standalone OS
|
||||
(LithosAnanke → StarshipOS). (Noted as "StarshopOS" in the 2026-08-29 decision words;
|
||||
canonical repo spelling and mark is **StarshipOS** — confirmed as the literal mark
|
||||
to register.)
|
||||
- **Patent 3 + mark Compudynamics.** The Zynq hardware steady-state machinery with sealed
|
||||
executions, HOL-proven — anchored by the physics-adaptive runtime.
|
||||
|
||||
**Driving deadlines (why "no hurry" is wrong for #1 and #2), clarified 2026-08-29.** Two
|
||||
clocks bind the near term, independent of product #3 and the FPGA:
|
||||
|
||||
- **#1 — the provisional will expire before conversion.** Its priority claim is time-boxed:
|
||||
if the non-provisional isn't filed claiming benefit before the provisional's window lapses
|
||||
(provisional filed ~Dec 2025 → window ~Dec 2026), the provisional's priority is lost and
|
||||
the same subject matter cannot be re-staked by refiling thereafter. So #1 is on a hard
|
||||
clock regardless of the December scope.
|
||||
- **#2 — full StarshipOS is the December deliverable.** This is what "keeps the flagship
|
||||
covered": the standalone-OS product (and, pending confirmation below, possibly the vehicle
|
||||
that converts #1's provisional) must deliver by December.
|
||||
|
||||
- **Open point to resolve with counsel:** is **#2's** application (or the December delivery)
|
||||
the **conversion vehicle** for #1's provisional — i.e., does it claim benefit from the
|
||||
#1 provisional and thereby secure what would otherwise lapse — or is #1 converted by a
|
||||
filing separate from #2? Captured as a decision record; no filing made.
|
||||
|
||||
**Relationship to the existing provisional (must be reconciled, not assumed).** The repo
|
||||
already carries a **"Patent pending"** USPTO **provisional filed December 2025** for the
|
||||
physics-grounded self-adaptive runtime (the Compudynamics adaptive runtime), sourced in
|
||||
`docs/patent/` and referenced in `README.md`. Compudynamics is therefore already staked as
|
||||
the brand of that foundation. Open points to resolve with counsel before any filing, so
|
||||
nothing is invented or double-filed: is patent 3 the continuation/refinement of that
|
||||
provisional (its HOL hardware realization) or a fresh application? Are the three product
|
||||
applications additive to, or folding in, the Dec-2025 provisional? This record states intent;
|
||||
it does not file, claim, or draft legal text.
|
||||
|
||||
**Before the FPGA — "coloring in", decided 2026-08-29.** The period between the all-three
|
||||
bare-metal cut (v2.5.0) and starting the Zynq is finishing/hardening work that thickens the
|
||||
shape of what already exists rather than adding new silicon. This is not idle time; it is
|
||||
the point where v2.5.0's real-hardware story is made production-honest before the FPGA asks
|
||||
to carry it further. Concrete items to define during it (draft scope, to be firmed at v2.5.0
|
||||
close): Zynq-preflight robustness of the USB BOT/xHCI and block paths; live-entropy and
|
||||
Zuse-cert hardening on the three real boards; SMP/multi-core bring-up and IRQ routing on real
|
||||
ASICs (parked in the HAL notes); driver set expansion beyond the three boards before
|
||||
committing an FPGA port; and whatever v2.5.0's real-board validation surfaces. As boards
|
||||
land and the v2.5.0 gates close, this list is edited down to the concrete coloring-in punch
|
||||
list, and the Zynq becomes the even-major target after it.
|
||||
|
||||
QEMU remains the zero-degradation reference on every line; each `X.Y.0` must reproduce the
|
||||
QEMU acceptance story (`POST 1012/0/0` + `ok>`, block-fence Zuse true, backend entropy
|
||||
non-deterministic) on its board before it closes.
|
||||
|
||||
---
|
||||
|
||||
## Milestone Overview
|
||||
|
||||
```
|
||||
M0 UEFI Boot ████████████████████ COMPLETE
|
||||
M1 PMM ████████████████████ COMPLETE
|
||||
M2 VMM ████████████████████ COMPLETE
|
||||
M3 IDT ████████████████████ COMPLETE
|
||||
M4 APIC ████████████████████ COMPLETE
|
||||
M5 Timer ████████████████████ COMPLETE
|
||||
M6 Heap ████████████████████ COMPLETE
|
||||
M7 VM Parity ████████████████████ COMPLETE
|
||||
M7.1 Capsules ████████████████░░░░ LIVE — see FABRIC-0.md/FABRIC-1.md/FABRIC-2.md
|
||||
M8 REPL ░░░░░░░░░░░░░░░░░░░░ OBSOLETE — see FABRIC-0.md §25.5 item 4.4
|
||||
M9 Block I/O ░░░░░░░░░░░░░░░░░░░░ PLANNED
|
||||
M10 Networking ░░░░░░░░░░░░░░░░░░░░ FUTURE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Boot Foundation (M0-M6)
|
||||
|
||||
### M0: UEFI Boot + Serial Output
|
||||
|
||||
**Goal:** Kernel prints "LithosAnanke booting..." to serial
|
||||
|
||||
**Deliverables:**
|
||||
- [x] UEFI loader (`uefi_loader.c`)
|
||||
- [x] BootInfo handoff (memory map, ACPI, framebuffer)
|
||||
- [x] ExitBootServices
|
||||
- [x] Serial console output
|
||||
|
||||
**Validation:**
|
||||
```
|
||||
StarKernel UEFI Loader
|
||||
Loading...
|
||||
RAW SERIAL UP
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### M1: Physical Memory Manager
|
||||
|
||||
**Goal:** Track and allocate physical page frames
|
||||
|
||||
**Deliverables:**
|
||||
- [x] Bitmap allocator
|
||||
- [x] Parse UEFI memory map
|
||||
- [x] `pmm_alloc_pages()` / `pmm_free_pages()`
|
||||
- [x] Statistics reporting
|
||||
|
||||
**Validation:**
|
||||
```
|
||||
PMM initialized.
|
||||
PMM statistics:
|
||||
Total pages: 249450
|
||||
Free pages : 247921
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### M2: Virtual Memory Manager
|
||||
|
||||
**Goal:** 4-level paging, identity + higher-half mapping
|
||||
|
||||
**Deliverables:**
|
||||
- [x] PML4 → PDPT → PD → PT setup
|
||||
- [x] CR3 switch
|
||||
- [x] `vmm_map()` / `vmm_unmap()`
|
||||
- [x] Self-test
|
||||
|
||||
**Validation:**
|
||||
```
|
||||
VMM initialized (mapped RAM, CR3 switched)
|
||||
VMM self-test: mapped OK at 0xffff800000000000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### M3: Interrupt Descriptor Table
|
||||
|
||||
**Goal:** CPU exceptions and IRQ handling
|
||||
|
||||
**Deliverables:**
|
||||
- [x] 256-entry IDT
|
||||
- [x] ISR stubs (asm)
|
||||
- [x] Exception handlers (div-by-zero, page fault, etc.)
|
||||
- [x] IRQ routing framework
|
||||
|
||||
**Validation:**
|
||||
```
|
||||
IDT installed.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### M4: APIC Timer
|
||||
|
||||
**Goal:** Local APIC initialization and timer IRQs
|
||||
|
||||
**Deliverables:**
|
||||
- [x] Local APIC enable
|
||||
- [x] APIC timer configuration
|
||||
- [x] Spurious interrupt vector
|
||||
|
||||
**Validation:**
|
||||
```
|
||||
APIC: init...
|
||||
APIC enabled (SIVR=0xFF).
|
||||
APIC: init done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### M5: Timer Calibration
|
||||
|
||||
**Goal:** Accurate time measurement
|
||||
|
||||
**Deliverables:**
|
||||
- [x] TSC frequency detection
|
||||
- [x] HPET calibration (when available)
|
||||
- [x] PM Timer fallback
|
||||
- [x] Relative vs. absolute trust levels
|
||||
|
||||
**Validation:**
|
||||
```
|
||||
Timer: init...
|
||||
Timer: trust=1 (0=NONE,1=REL,2=ABS), TSC=0 Hz
|
||||
Timer: init done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### M6: Kernel Heap
|
||||
|
||||
**Goal:** `kmalloc()` / `kfree()` working
|
||||
|
||||
**Deliverables:**
|
||||
- [x] Heap initialization
|
||||
- [x] Allocation tracking
|
||||
- [x] Statistics
|
||||
|
||||
**Validation:**
|
||||
```
|
||||
Kernel heap initialized.
|
||||
Heap statistics:
|
||||
Total bytes: 16777176
|
||||
Free bytes: 16777176
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: VM Integration (M7)
|
||||
|
||||
### M7: VM Parity Validation
|
||||
|
||||
**Goal:** StarForth VM boots with reproducible dictionary hash
|
||||
|
||||
**Deliverables:**
|
||||
- [x] VMHostServices abstraction
|
||||
- [x] VM arena allocation (5 MB)
|
||||
- [x] FORTH-79 word registration (295 words)
|
||||
- [x] Parity checkpoint logging
|
||||
- [x] Heartbeat thread start
|
||||
|
||||
**Validation:**
|
||||
```
|
||||
VM: bootstrap parity...
|
||||
[HAL][host] VMHostServices table registered
|
||||
VM arena allocated: 0xffff900000000000 (5 MB)
|
||||
Registering FORTH-79 Standard word set...
|
||||
PARITY:M7.1a word_count=295 here=0x30 latest_id=294 hash=0x684bbf2fa1d96d55
|
||||
PARITY:OK
|
||||
VM: parity bootstrap complete
|
||||
Starting heartbeat...
|
||||
APIC Timer: started
|
||||
```
|
||||
|
||||
**Commit:** `6f350bc` — M7: StarForth VM integration with parity validation
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Capsule Architecture (M7.1)
|
||||
|
||||
### M7.1: Init Capsule System
|
||||
|
||||
**Goal:** Content-addressed, immutable init capsules for VM birth
|
||||
|
||||
**Status:** Design Complete (see [M7.1.md](M7.1.md)) — this section predates the actual
|
||||
capsule/Tripod/Stadium work and is not current. **See `FABRIC-0.md`/`FABRIC-1.md` for real
|
||||
status** (per `.claude/CLAUDE.md`'s standing redirect) — Tripod (Hera/Hermes/Artemis),
|
||||
word-level ACL Phases 1–7, and the Stadium physics-economy migration are all live, not just
|
||||
designed. Most recently: item 4.6 (Artemis's block-heat arena migrated to Stadium
|
||||
admission-on-allocate) verified passing its 30-rep stress campaign on all three
|
||||
architectures 2026-08-18, after fixing a quota-grant ordering bug (`capsule_birth.c`) that
|
||||
had made every admission fail; see `FABRIC-1.md` Section H. The same pass also fixed a
|
||||
long-standing aarch64-only crash on `BYE` (wrong PSCI conduit — SMC has nothing to answer it
|
||||
under AAVMF/no-EL3 boot; switched to HVC), see `FABRIC-1.md` Section I.
|
||||
|
||||
**Core Concepts:**
|
||||
|
||||
| Concept | Description |
|
||||
|---------|-------------|
|
||||
| **DOMAIN** | Mama-only construction space — never visible to babies |
|
||||
| **PERSONALITY** | Baby-only identity — result of executing (p) INIT |
|
||||
| **(p) Production** | Truth-bearing capsules that birth VMs |
|
||||
| **(e) Experiment** | Mama-only workloads for DoE |
|
||||
|
||||
**Birth Protocol:**
|
||||
1. Mama selects one production `(p)` capsule by content hash
|
||||
2. Mama validates eligibility (ACTIVE, PRODUCTION, not REVOKED)
|
||||
3. Mama allocates new VM
|
||||
4. INIT blocks copied to execution window (RAM blocks 0–2047)
|
||||
5. INIT blocks executed sequentially
|
||||
6. Execution window cleared
|
||||
7. VM begins life with PERSONALITY imprinted
|
||||
8. Mama logs `PARITY:BIRTH vm_id=N capsule_id=X mode=p ...`
|
||||
9. Mama increments `capsule.birth_count`
|
||||
|
||||
**Deliverables:**
|
||||
- [ ] `CapsuleDesc` struct (64 bytes, cache-aligned)
|
||||
- [ ] `CapsuleDirHeader` struct
|
||||
- [ ] xxHash64 implementation (freestanding)
|
||||
- [ ] `capsule_validate()` function
|
||||
- [ ] Birth protocol (`PARITY:BIRTH` logging)
|
||||
- [ ] DoE run logging (`CapsuleRunRecord`)
|
||||
- [ ] `mkcapsule` build tool
|
||||
|
||||
**Key Design Decisions:**
|
||||
- Content-addressed: `capsule_id == content_hash`
|
||||
- (p) Production vs (e) Experiment modes
|
||||
- One truth per VM — no shared/implicit base INITs
|
||||
- Mama holds all truths — (e) capsules never touch babies
|
||||
- Twins/variants are just VMs with same/similar capsules
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Interactive Forth (M8)
|
||||
|
||||
> **⚠ OBSOLETE (2026-08-11).** This entire M8 section is superseded by
|
||||
> `FABRIC-0.md` §25.5 item 4.4 (REPL). The console/REPL design has moved past
|
||||
> what's scoped here — do not use this section as a source for REPL or
|
||||
> CANVAS design decisions. Kept in place for history only.
|
||||
|
||||
### M8: REPL + Interactive Forth
|
||||
|
||||
**Status:** Scoped 2026-08-11 (this section). Substrate (interrupt-driven keyboard
|
||||
input, glyph rendering) already complete via Stadium's Console fabric work
|
||||
(FABRIC-0.md §25.5 items 4.3.5–4.3.7f) — see "What's already done" below. This
|
||||
scoping pass covers what's left: wiring the existing keyboard-event stream into
|
||||
the REPL's character input path.
|
||||
|
||||
**Goal:** Type Forth at the kernel *keyboard* (not just serial), get `ok` prompt.
|
||||
The REPL already works interactively over serial today (`sk_repl_run()`,
|
||||
`src/starkernel/repl.c`) — that is not what this milestone adds. What's missing
|
||||
is a second input source feeding the same loop.
|
||||
|
||||
**What's already done, read from the code before scoping this, not assumed:**
|
||||
- Interrupt-driven keyboard input on all three architectures (amd64 i8042,
|
||||
aarch64/riscv64 virtio-keyboard-pci + GIC/PLIC), converged behind one shared
|
||||
interface: `KEY-EVENT ( -- keycode pressed -1 | 0 )` in
|
||||
`src/word_source/keyboard_words.c`, `keycode` in the Linux input keycode
|
||||
namespace. FABRIC-0.md items 4.3.5–4.3.5f, verified live on all three
|
||||
architectures with real keypresses.
|
||||
- Glyph rendering, both paths: the stroke-drawn font (`TEXT`, capsule-based,
|
||||
113-glyph v1 repertoire, FABRIC-0.md 4.3.6–4.3.6g) and TrueType (`TTF-TEXT`,
|
||||
proportional spacing via real `hmtx` advance widths, FABRIC-0.md 4.3.7–4.3.7f).
|
||||
Neither is required for the REPL text itself, though — see the VT100 finding
|
||||
below.
|
||||
- **The REPL's on-screen text already renders independently of both of the
|
||||
above.** `console_fb_init()` (`src/starkernel/hal/console.c`) calls
|
||||
`vt100_init()`, which mirrors every `console_putc()`/`console_puts()` call to
|
||||
the framebuffer via the baked-in `font_8x16.c` raster font. This is *already
|
||||
live* — the REPL prompt and its output appear on screen today, serial-driven,
|
||||
no CANVAS/glyph-capsule dependency. This confirms §27.6's mockup: a fixed
|
||||
VT100 REPL strip (already working) plus a separate large CANVAS region above
|
||||
it (still unsized — see the last deliverable below).
|
||||
|
||||
**What's actually missing, confirmed by reading the code, not assumed:**
|
||||
`src/starkernel/repl.c`'s `sk_readline()` reads characters via
|
||||
`console_getc()` (`src/starkernel/hal/console.c`), which is serial-UART-only —
|
||||
it never looks at `KEY-EVENT`. Separately, the FORTH-79 words `KEY`/`?TERMINAL`
|
||||
(`src/word_source/io_words.c:84-108`, shared/vendored source) are dead in the
|
||||
kernel build: `KEY` calls libc `getchar()`, which the kernel shim
|
||||
(`src/starkernel/vm/host/shim.c:1206`) hardcodes to return `-1` (EOF);
|
||||
`?TERMINAL` is a literal stub that always returns false. Neither path has ever
|
||||
been wired to a real keyboard.
|
||||
|
||||
**Deliverables:**
|
||||
- [ ] Minimal scancode→ASCII layout: a small lookup table (US QWERTY,
|
||||
unshifted/shifted rows only — no dead keys, no locale switching) driven
|
||||
by `KEY-EVENT`'s Linux-namespace keycodes, plus shift-state tracking
|
||||
(`KEY_LEFTSHIFT`/`KEY_RIGHTSHIFT` press/release) since `KEY-EVENT` itself
|
||||
carries no modifier state today (confirmed by reading
|
||||
`keyboard_words.c` — deferred at 4.3.5f, not implemented anywhere).
|
||||
- [ ] Merge keyboard into `console_getc()`'s poll: extend it (or add a second
|
||||
function `sk_readline()` polls alongside it) to also drain `KEY-EVENT`
|
||||
and translate presses to ASCII via the layout table above. **Serial stays
|
||||
a co-equal input source, not replaced** — the entire acceptance/DoE
|
||||
harness injects FORTH commands over the serial socket
|
||||
(`socat ... UNIX-CONNECT:$SERIAL_SOCK`) and greps the serial log for
|
||||
`ok>`; breaking that path breaks `make qemu`, `DOE_INJECT`, and every
|
||||
screendump verification technique used throughout 4.3.x. Both sources
|
||||
feed the same byte stream into the existing `sk_readline()` — no new
|
||||
line-editing code needed, backspace/echo already work
|
||||
(`sk_readline()`, `repl.c:83-141`).
|
||||
- [ ] Wire `KEY`/`?TERMINAL` (`io_words.c`) to the same merged source under
|
||||
`#ifdef __STARKERNEL__`, for FORTH-79 compliance — any capsule calling
|
||||
these directly (not just the REPL) should get real input too, not a
|
||||
permanently-EOF stub. Small addition once the merge above exists.
|
||||
- [ ] CANVAS viewport sizing (§27.6's explicit deferral to this milestone): the
|
||||
large graphics region above the VT100 REPL strip needs an actual
|
||||
scrollable framebuffer viewport size decided (e.g. 640×480, per the
|
||||
original mockup) — layout/presentation, not input plumbing; do this
|
||||
last, after keyboard input works, since it doesn't block interactivity.
|
||||
|
||||
**Explicitly out of scope for this milestone (minimal bar, matches "Line
|
||||
editing (backspace, minimal)"):** extended (`0xE0`-prefixed) i8042 scancodes,
|
||||
virtio-input autorepeat (`value=2`) as actual character repeat, arrow-key
|
||||
cursor movement/history, non-US keyboard layouts. Deferred to a later item if
|
||||
ever needed — not blocking "type Forth at the kernel, get `ok`."
|
||||
|
||||
**Validation:**
|
||||
```
|
||||
LithosAnanke v0.3.0
|
||||
ok 1 2 + .
|
||||
3 ok
|
||||
```
|
||||
(via a real keypress on all three architectures, same live-keypress
|
||||
verification standard FABRIC-0.md 4.3.5f already established — not a
|
||||
serial-injected synthetic test, though serial injection must still work
|
||||
identically afterward.)
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Persistence (M9)
|
||||
|
||||
### M9: Block Storage
|
||||
|
||||
**Goal:** Read/write blocks to disk
|
||||
|
||||
**Deliverables:**
|
||||
- [x] Block device driver — implemented via `virtio_blk.c` (virtio block device), not the
|
||||
AHCI/SATA driver originally scoped here; see `.claude/CLAUDE.md`'s M9 note.
|
||||
- [x] Block device abstraction — `block_subsystem.c`'s unified LBN address space, backend-agnostic
|
||||
across RAM/RAMDRIVE/DISK/USB via the `blkio_dev` vtable.
|
||||
- [x] `BLOCK` / `BUFFER` / `UPDATE` / `FLUSH` words — live, `src/word_source/block_words.c`.
|
||||
- [ ] Persistent dictionary — no evidence found of word definitions surviving reboot; not
|
||||
independently verified as done, left unchecked rather than assumed from the rest of this
|
||||
list being live.
|
||||
|
||||
---
|
||||
|
||||
## Future Milestones
|
||||
|
||||
### M10: Networking
|
||||
- VirtIO-net driver
|
||||
- TCP/IP stack (minimal)
|
||||
- DHCP client
|
||||
|
||||
### M11: Process Model
|
||||
- Forth tasks
|
||||
- Scheduling
|
||||
- IPC
|
||||
|
||||
### M12: Self-Hosting
|
||||
- Compile Forth on LithosAnanke
|
||||
- Edit/assemble/link cycle
|
||||
|
||||
---
|
||||
|
||||
## Validation Commands
|
||||
|
||||
```bash
|
||||
# Build
|
||||
make -f kernel/Makefile ARCH=amd64 STARFORTH_ENABLE_VM=1
|
||||
|
||||
# Run QEMU
|
||||
make -f kernel/Makefile ARCH=amd64 STARFORTH_ENABLE_VM=1 qemu
|
||||
|
||||
# Clean
|
||||
make -f kernel/Makefile ARCH=amd64 clean-kernel
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Branch Relationship
|
||||
|
||||
```
|
||||
master (post-split: sole LithosAnanke production line — the old
|
||||
lithosananke/master two-branch topology no longer exists in this repo)
|
||||
└── M0–M7 complete, M7.1 live (capsules / Tripod / Stadium / ACL)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
LithosAnanke is successful when:
|
||||
|
||||
1. **M7 Parity** — VM dictionary hash reproducible across boots
|
||||
2. **M7.1 Capsules** — Birth protocol enforced, provenance logged
|
||||
3. ~~**M8 REPL** — Interactive Forth at bare metal~~ — superseded, this criterion is now met
|
||||
via Stadium's Console fabric work (`FABRIC-0.md` §25.5 item 4.4), not a standalone M8; see
|
||||
the OBSOLETE banner above Phase 4
|
||||
4. **M9 Persistence** — State survives reboot
|
||||
|
||||
---
|
||||
|
||||
*The foundation is laid. The necessity is clear.*
|
||||
@@ -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,178 @@
|
||||
# Hosted StarForth — 3-Architecture Acceptance Test
|
||||
|
||||
**Date**: 2026-07-24 (original), corrected 2026-08-02
|
||||
**Branch**: `master`
|
||||
**Commits**: `edced063` (original block-subsystem fix), `4485c38` (doc +
|
||||
3 clang-surfaced bug fixes), `e287334` (asm register-reuse fix), this pass
|
||||
(`riscv64-clang` Makefile target)
|
||||
|
||||
---
|
||||
|
||||
## What this verifies
|
||||
|
||||
The **hosted** StarForth VM (`make`, not `Makefile.starkernel`) is a static
|
||||
binary. This test confirms it starts cleanly — reaches the `ok>` REPL prompt
|
||||
with no errors — on all three architectures the project targets:
|
||||
|
||||
- **amd64** — native execution on the build host
|
||||
- **aarch64** — cross-compiled, run under QEMU user-mode emulation
|
||||
- **riscv64** — cross-compiled, run under QEMU user-mode emulation
|
||||
|
||||
This is distinct from the kernel (`Makefile.starkernel`) 3-arch acceptance
|
||||
test documented in
|
||||
[`../amd64-isr-fix/README.md`](../amd64-isr-fix/README.md), which boots a
|
||||
full UEFI bare-metal image under system-mode QEMU. This test only exercises
|
||||
the hosted Linux binary, using QEMU **user-mode** emulation to run a
|
||||
foreign-architecture static binary directly on the x86_64 host — no kernel,
|
||||
no UEFI firmware, no system emulation.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Cross toolchains, QEMU user-mode emulation, and clang (needed for the
|
||||
riscv64 leg — see below):
|
||||
|
||||
```bash
|
||||
sudo apt-get install -y \
|
||||
gcc-aarch64-linux-gnu \
|
||||
gcc-riscv64-linux-gnu \
|
||||
clang-18 lld-18 \
|
||||
qemu-user
|
||||
```
|
||||
|
||||
`qemu-user` alone is enough — the binaries built below are statically
|
||||
linked (`-static`), so the non-static `qemu-<arch>` emulators run them
|
||||
directly with no sysroot. (The `qemu-user-static` package, if you see it
|
||||
referenced elsewhere, provides statically-linked *emulators*; it's not
|
||||
required just because the *guest* binary is static.)
|
||||
|
||||
---
|
||||
|
||||
## Invocation note
|
||||
|
||||
None of the three binaries accept a `-c "<script>"` flag — earlier
|
||||
revisions of this doc showed one, but `src/cli.c`/`src/main.c` have never
|
||||
had one in this repo's history. Pipe the script via stdin with `-s`
|
||||
(script/silent mode) instead. Startup always runs the full internal POST
|
||||
test suite before the REPL takes stdin, so expect substantial test-suite
|
||||
output before your script's result appears at the very end.
|
||||
|
||||
---
|
||||
|
||||
## amd64 — native
|
||||
|
||||
```bash
|
||||
make clean && make
|
||||
echo "1 2 + . BYE" | ./build/amd64/standard/starforth -s
|
||||
```
|
||||
|
||||
Expect `3 Goodbye!` at the end of the output, then a clean exit. No
|
||||
emulation needed — this is the build host's native architecture.
|
||||
|
||||
---
|
||||
|
||||
## aarch64 — cross-compile + QEMU user-mode
|
||||
|
||||
```bash
|
||||
make rpi4-cross
|
||||
echo "1 2 + . BYE" | qemu-aarch64 ./build/raspi/fastest/starforth -s
|
||||
```
|
||||
|
||||
`make rpi4-cross` cross-compiles with `aarch64-linux-gnu-gcc`
|
||||
(`ARCH=raspi`, `TARGET=fastest`, `-static`); see `Makefile:547-554`. The
|
||||
output binary is statically linked, so `qemu-aarch64` can execute it
|
||||
directly with no sysroot.
|
||||
|
||||
---
|
||||
|
||||
## riscv64 — cross-compile + QEMU user-mode
|
||||
|
||||
```bash
|
||||
make riscv64-clang
|
||||
echo "1 2 + . BYE" | qemu-riscv64 ./build/riscv64/fastest/starforth -s
|
||||
```
|
||||
|
||||
`make riscv64-clang` cross-compiles with `clang-18` (`ARCH=riscv64`,
|
||||
`TARGET=fastest`, `-static`); see `Makefile:566-579`. **This leg must use
|
||||
clang, not GCC**: `riscv64-linux-gnu-gcc` fails to build this tree
|
||||
(`nanosleep` visibility failure under `-std=c99`, unrelated to any project
|
||||
change — root cause not yet chased down, see
|
||||
`docs/working/archive/session-logs/2026-07-24-punch-list.md` item #3). The
|
||||
target's `CFLAGS` deliberately does not reuse the Makefile's
|
||||
`$(BASE_CFLAGS)` (which hardcodes `-std=c99`) — clang needs `-std=c11
|
||||
-pthread` here instead, plus `-fuse-ld=lld` in `LDFLAGS` (folding it into
|
||||
`CC` makes clang reject it as an unused argument during `-c`/compile-only
|
||||
invocations under `-Werror`).
|
||||
|
||||
---
|
||||
|
||||
## Pass criteria
|
||||
|
||||
For each architecture:
|
||||
|
||||
1. Build completes with no errors.
|
||||
2. `blk_subsys_init` succeeds — no `"Failed to initialize block subsystem"`
|
||||
error. (This was broken on every architecture, including amd64, prior to
|
||||
`edced063` — see below.)
|
||||
3. The internal POST suite runs (substantial log output — expected, not a
|
||||
failure by itself) and the piped `1 2 + . BYE` script prints `3 Goodbye!`
|
||||
at the end.
|
||||
|
||||
---
|
||||
|
||||
## Background: the block-subsystem bug this depends on
|
||||
|
||||
Before `edced063`, the hosted VM could not start on **any** architecture on
|
||||
this branch. `src/main.c` allocated a hardcoded `blk_ram[1024 * 1024]`
|
||||
buffer, stale relative to `BLK_RAM_BLOCKS=2080` (`include/block_subsystem.h`)
|
||||
— a deliberate redesign that moved the RAM/ramdrive boundary to physical
|
||||
block 2080 (user-visible LBN 2048), per `BLK_FORTH_SYS_RESERVED=32`. The
|
||||
kernel-side buffer in `src/starkernel/vm/bootstrap/sk_vm_bootstrap.c` already
|
||||
derived its size correctly from the shared constant; `main.c` was the one
|
||||
remaining hardcoded holdout. The fix:
|
||||
|
||||
```c
|
||||
#include "block_subsystem.h"
|
||||
...
|
||||
static uint8_t blk_ram[BLK_RAM_BLOCKS * BLK_FORTH_SIZE]; /* was: blk_ram[1024 * 1024] */
|
||||
```
|
||||
|
||||
This is why the acceptance test above is meaningful on this branch
|
||||
specifically: it did not pass before this fix, on any of the three
|
||||
architectures.
|
||||
|
||||
---
|
||||
|
||||
## Background: the riscv64 clang build (2026-08-02 correction)
|
||||
|
||||
The riscv64 command above was broken in this doc from 2026-07-24 until this
|
||||
correction — `docs/working/archive/session-logs/2026-07-24-punch-list.md`
|
||||
(item #2) recorded that the working clang invocation had been found and
|
||||
fixed in the old pre-split monorepo's `master` branch's `.claude/CLAUDE.md`,
|
||||
but the correction was never copied into this doc. Verifying it end-to-end
|
||||
for this correction surfaced that the clang build also needs three small
|
||||
source fixes from that same punch list (item #1), previously ported to the
|
||||
old monorepo's `master` but not to this line:
|
||||
|
||||
- `src/math_portable.c` — `-100LL << 16` is UB (shifting a negative value)
|
||||
under `-Wshift-negative-value`; changed to `-(100LL << 16)`.
|
||||
- `src/physics_pipelining_metrics.c` — removed dead `q48_mul_q48()`
|
||||
(`-Wunused-function` under clang; GCC doesn't catch this by default).
|
||||
- `src/word_source/editor_words.c` — removed dead `set_scr()` (same reason).
|
||||
|
||||
The punch list's highest-priority item — a genuine SIGSEGV-causing
|
||||
register-reuse hazard in `include/vm_asm_opt_riscv64.h`'s `vm_pop_asm`/
|
||||
`vm_rpop_asm` (clang's register allocator can pick the same register for a
|
||||
memory operand referenced both before and after a write-back, corrupting
|
||||
the result; GCC happens not to hit it) was *not* required just to get this
|
||||
acceptance test's `-O3` build to compile and pass, but has since been
|
||||
fixed too (commit `e287334`) — and turned out not to be merely a latent
|
||||
risk: it was silently corrupting 10 `CASE.*` control-flow tests under this
|
||||
exact build (`955`/`10 failed` → `965`/`0 failed`, `ALL IMPLEMENTED TESTS
|
||||
PASSED!`, with nothing else changed).
|
||||
|
||||
Item #3 of the same punch list — no `make` target for this leg, only the
|
||||
long manual invocation — is also resolved as of this pass: `make
|
||||
riscv64-clang` wires in the same recipe verified above directly into the
|
||||
Makefile.
|
||||
@@ -0,0 +1,323 @@
|
||||
# Kernel Command-Line Arguments (KernelArgs)
|
||||
|
||||
**Branch:** `lithosananke`
|
||||
**Status:** Design complete — awaiting implementation
|
||||
|
||||
## Motivation
|
||||
|
||||
Memory sizing and runtime behavior are currently compile-time constants:
|
||||
|
||||
```c
|
||||
#define KERNEL_HEAP_SIZE (2ULL * 1024 * 1024 * 1024) /* kernel_main.c */
|
||||
.space 0x200000 /* kernel_entry.S */
|
||||
```
|
||||
|
||||
This means tuning stack depth or heap size for a given machine requires a
|
||||
full rebuild. The JVM model (`-Xmx`, `-Xss`) is the right analogy: bake in
|
||||
safe defaults, allow runtime overrides via boot-time command line. The same
|
||||
mechanism also lets the kernel self-start the DoE and control log verbosity
|
||||
without relying on external socat injection from the Makefile scripts.
|
||||
|
||||
## UEFI Mechanism
|
||||
|
||||
`EFI_LOADED_IMAGE_PROTOCOL->LoadOptions` is a UCS-2 string passed to the
|
||||
EFI application by the firmware or boot manager. It is available to the
|
||||
loader before `ExitBootServices()` at no extra cost — the loader already
|
||||
holds `loaded_image` (see `uefi_loader.c:160`). We convert it to ASCII
|
||||
(simple 8-bit truncation; all valid switches are ASCII) and parse it into
|
||||
a `KernelArgs` struct that travels to the kernel via `BootInfo`.
|
||||
|
||||
## New Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `include/starkernel/kernel_args.h` | `KernelArgs` struct + default macros |
|
||||
| `src/starkernel/boot/cmdline.c` | UCS-2→ASCII conversion + tokeniser + parser |
|
||||
| `include/starkernel/cmdline.h` | Parser API (`cmdline_parse`) |
|
||||
|
||||
## Changed Files
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `include/starkernel/uefi.h` | Add `KernelArgs args` + `void *kernel_stack_base` + `uint64_t kernel_stack_size` to `BootInfo` |
|
||||
| `src/starkernel/boot/uefi_loader.c` | Call `cmdline_parse(loaded_image->LoadOptions, ...)`, allocate dynamic stack, fill `BootInfo` |
|
||||
| `src/starkernel/arch/amd64/kernel_entry.S` | Read `boot_info->kernel_stack_base`; fall back to BSS stack if zero |
|
||||
| `src/starkernel/kernel_main.c` | Use `boot_info->args.heap_size`; honour `run_doe` and `log_level` |
|
||||
| `Makefile.starkernel` | Pass `--doe` via OVMF boot entry args; remove socat EXEC-DOE injection |
|
||||
|
||||
## `KernelArgs` Struct
|
||||
|
||||
```c
|
||||
/* include/starkernel/kernel_args.h */
|
||||
|
||||
#define KARGS_DEFAULT_STACK_SIZE (2ULL * 1024 * 1024) /* 2 MB */
|
||||
#define KARGS_DEFAULT_HEAP_SIZE (2ULL * 1024 * 1024 * 1024) /* 2 GB */
|
||||
#define KARGS_DEFAULT_LOG_LEVEL 1 /* info */
|
||||
#define KARGS_DEFAULT_RUN_DOE 0
|
||||
|
||||
typedef struct {
|
||||
/* Memory */
|
||||
uint64_t stack_size; /* --stack=<size> e.g. 2M 4M 8M */
|
||||
uint64_t heap_size; /* --heap=<size> e.g. 2G 4G */
|
||||
|
||||
/* Runtime behaviour */
|
||||
int run_doe; /* --doe flag, default off */
|
||||
int log_level; /* --log-level=<level> debug=0 info=1 warn=2 error=3 */
|
||||
|
||||
/* Extension: raw unparsed cmdline preserved verbatim (null-terminated) */
|
||||
char raw[512];
|
||||
} KernelArgs;
|
||||
```
|
||||
|
||||
All fields have explicit defaults. Unknown switches are ignored and
|
||||
appended to `raw` so future kernels can act on them while old kernels boot
|
||||
safely.
|
||||
|
||||
## `BootInfo` Changes
|
||||
|
||||
Two fields are added **directly** to `BootInfo` (not nested inside
|
||||
`KernelArgs`) so `kernel_entry.S` can read them at fixed, known offsets
|
||||
without any C header dependency:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
EFI_MEMORY_DESCRIPTOR *memory_map;
|
||||
UINTN memory_map_size;
|
||||
UINTN memory_map_descriptor_size;
|
||||
EFI_RUNTIME_SERVICES *runtime_services;
|
||||
void *acpi_table;
|
||||
FramebufferInfo framebuffer;
|
||||
UINT8 uefi_boot_services_exited;
|
||||
|
||||
/* NEW — kernel stack (loader-allocated; zero = use BSS fallback) */
|
||||
void *kernel_stack_base; /* low address of allocation */
|
||||
uint64_t kernel_stack_size;
|
||||
|
||||
/* NEW — all other parsed arguments */
|
||||
KernelArgs args;
|
||||
} BootInfo;
|
||||
```
|
||||
|
||||
Keeping `kernel_stack_base` / `kernel_stack_size` at the top-level avoids
|
||||
having to compute a nested struct offset inside assembly.
|
||||
|
||||
## Assembly Trampoline (`kernel_entry.S`)
|
||||
|
||||
```asm
|
||||
kernel_main:
|
||||
/* rdi = BootInfo* */
|
||||
mov rax, [rdi + BOOT_INFO_STACK_BASE_OFFSET]
|
||||
test rax, rax
|
||||
jnz .dynamic_stack
|
||||
lea rax, [rip + g_kernel_stack_top] /* BSS fallback */
|
||||
jmp .stack_ready
|
||||
.dynamic_stack:
|
||||
mov rcx, [rdi + BOOT_INFO_STACK_SIZE_OFFSET]
|
||||
add rax, rcx /* top = base + size */
|
||||
.stack_ready:
|
||||
and rax, -16
|
||||
sub rax, 8
|
||||
mov rsp, rax
|
||||
xor rbp, rbp
|
||||
jmp kernel_main_impl
|
||||
```
|
||||
|
||||
`BOOT_INFO_STACK_BASE_OFFSET` and `BOOT_INFO_STACK_SIZE_OFFSET` are defined
|
||||
as integer constants in a small `boot_info_offsets.h` that is included by
|
||||
both C (to `_Static_assert` correctness) and the assembly file.
|
||||
|
||||
## Parser (`cmdline.c`)
|
||||
|
||||
```c
|
||||
void cmdline_parse(const CHAR16 *load_options, BootInfo *boot_info);
|
||||
```
|
||||
|
||||
Steps:
|
||||
1. Convert UCS-2 → ASCII into a local 512-byte buffer (truncate at 511).
|
||||
2. Copy verbatim into `boot_info->args.raw`.
|
||||
3. Fill defaults into `boot_info->args`.
|
||||
4. Tokenise on spaces; for each token:
|
||||
- `--stack=<N>[KMG]` → `boot_info->kernel_stack_size` + `args.stack_size`
|
||||
- `--heap=<N>[KMG]` → `args.heap_size`
|
||||
- `--doe` → `args.run_doe = 1`
|
||||
- `--log-level=<s>` → `args.log_level` (debug/info/warn/error → 0-3)
|
||||
- anything else → silently ignored (forward-compat)
|
||||
5. Allocate stack via `BS->AllocatePages(AllocateAnyPages, EfiLoaderData, pages)`.
|
||||
Store result in `boot_info->kernel_stack_base`.
|
||||
|
||||
Size suffix parser handles `K`/`M`/`G` (case-insensitive); bare numbers are
|
||||
treated as bytes.
|
||||
|
||||
## Loader Integration
|
||||
|
||||
In `uefi_loader.c`, after the existing `HandleProtocol` call for
|
||||
`loaded_image` and before `ExitBootServices`:
|
||||
|
||||
```c
|
||||
cmdline_parse(loaded_image->LoadOptions, &boot_info);
|
||||
```
|
||||
|
||||
`boot_info` is already on the loader stack and passed to the kernel entry;
|
||||
no structural change needed.
|
||||
|
||||
## `kernel_main.c` Integration
|
||||
|
||||
```c
|
||||
/* Heap — use runtime size if provided */
|
||||
uint64_t heap_sz = boot_info->args.heap_size
|
||||
? boot_info->args.heap_size
|
||||
: KARGS_DEFAULT_HEAP_SIZE;
|
||||
kmalloc_init(heap_sz);
|
||||
|
||||
/* DoE — self-start if requested */
|
||||
#ifdef STARFORTH_ENABLE_VM
|
||||
if (boot_info->args.run_doe) {
|
||||
vm_interpret(mama, "EXEC-DOE");
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
Log level feeds into the existing `LOG-DEBUG` / `LOG-INFO` etc. word
|
||||
infrastructure via a new `sk_set_log_level(int)` call.
|
||||
|
||||
## Makefile Integration
|
||||
|
||||
The `qemu` target currently injects `EXEC-DOE` via socat after detecting
|
||||
`ok>`. With `--doe` supported natively:
|
||||
|
||||
```makefile
|
||||
# Pass --doe via OVMF boot entry OptionalData
|
||||
QEMU_KERNEL_ARGS ?= --doe --log-level=info --stack=2M --heap=2G
|
||||
```
|
||||
|
||||
The ISO build embeds these in the UEFI boot entry so the kernel receives
|
||||
them from firmware. The socat injection dance is removed. The `SK_CMD`
|
||||
compile-time flag remains available for one-off testing.
|
||||
|
||||
## Passing Args in QEMU
|
||||
|
||||
OVMF reads boot entry `OptionalData` from the UEFI variable store. The
|
||||
cleanest approach for the Makefile: use a small Python/shell script to
|
||||
write the boot entry with the desired args into `OVMF_VARS.fd` before
|
||||
launching QEMU. Alternatively, `efibootmgr` syntax baked into the ISO's
|
||||
`startup.nsh` script works with the OVMF shell.
|
||||
|
||||
## `REBOOT` FORTH Word
|
||||
|
||||
### Purpose
|
||||
|
||||
Allows a running kernel to set new boot flags and immediately cold-reset.
|
||||
The canonical use case:
|
||||
|
||||
```forth
|
||||
S" --doe --log-level=debug" REBOOT
|
||||
```
|
||||
|
||||
On the next boot the loader finds the one-shot NVRAM variable, uses it
|
||||
instead of `LoadOptions`, and clears it so subsequent boots revert to
|
||||
normal.
|
||||
|
||||
### Stack effect
|
||||
|
||||
```
|
||||
REBOOT ( addr len -- )
|
||||
```
|
||||
|
||||
Takes a counted string (same convention as `S"`) containing the new
|
||||
argument line.
|
||||
|
||||
### Interpret-only guard
|
||||
|
||||
`REBOOT` is a normal (non-immediate) word that checks compilation state at
|
||||
the top of its body. Attempting to include it in a `:` definition produces
|
||||
a `-14` throw (`interpretation semantics only`) **at definition time**, not
|
||||
at run time — which is the correct FORTH-standard behaviour.
|
||||
|
||||
```c
|
||||
static void word_reboot(VM *vm) {
|
||||
if (vm->compiling) {
|
||||
vm_throw(vm, -14); /* interpretation semantics only */
|
||||
return;
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### NVRAM one-shot mechanism
|
||||
|
||||
When `REBOOT` executes it:
|
||||
|
||||
1. Pops `addr` / `len` from the data stack.
|
||||
2. Validates length (`0 < len < KERNEL_ARGS_CMDLINE_MAX`); throws `-11` on
|
||||
out-of-range.
|
||||
3. Converts the string to UCS-2.
|
||||
4. Reads `StarForthRebootTries` from NVRAM (default 0 if absent).
|
||||
5. If `tries >= REBOOT_MAX_TRIES` (default **3**):
|
||||
- Clears both `StarForthBootArgs` and `StarForthRebootTries` variables.
|
||||
- Prints warning to serial: `REBOOT: max tries reached — dropping to REPL`.
|
||||
- Returns normally (does **not** reset); execution continues at the REPL.
|
||||
6. Otherwise: writes `StarForthBootArgs` and increments `StarForthRebootTries`,
|
||||
then calls `RuntimeServices->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL)`.
|
||||
This call does not return.
|
||||
|
||||
On the next boot, the loader checks for `StarForthBootArgs`:
|
||||
- If found: copies value, **clears `StarForthBootArgs`** (one-shot), uses it
|
||||
for parsing. `StarForthRebootTries` is left untouched by the loader — it
|
||||
is only cleared by a successful REPL session (see below) or by the
|
||||
max-tries guard above.
|
||||
|
||||
On reaching the REPL successfully, `kernel_main` clears `StarForthRebootTries`
|
||||
to zero, resetting the counter for the next `REBOOT` invocation. This means
|
||||
a clean interactive boot always resets the escape-hatch state.
|
||||
|
||||
### `SetVariable` failure path
|
||||
|
||||
If any `SetVariable` call returns a non-`EFI_SUCCESS` status:
|
||||
|
||||
- Print warning: `REBOOT: NVRAM write failed (<status>) — dropping to REPL`.
|
||||
- Do **not** attempt `ResetSystem`.
|
||||
- Return normally; execution continues at the REPL.
|
||||
|
||||
No infinite retry loop. One attempt, one warning, one escape to REPL.
|
||||
|
||||
### Hosted-VM stub
|
||||
|
||||
On the Linux-hosted build `RuntimeServices` is unavailable. The stub logs
|
||||
the would-be args and sets `vm->halted = 1`:
|
||||
|
||||
```c
|
||||
#ifndef STARFORTH_KERNEL
|
||||
hal_console_puts("[REBOOT] hosted stub — would reboot with: ");
|
||||
hal_console_write((const char *)VM_ADDR(vm, addr), (size_t)len);
|
||||
hal_console_puts("\n");
|
||||
vm->halted = 1;
|
||||
#endif
|
||||
```
|
||||
|
||||
### Registration
|
||||
|
||||
Registered alongside `BYE` / `WARM` / `COLD` in `system_words.c`:
|
||||
|
||||
```c
|
||||
REGISTER_WORD("REBOOT", word_reboot, WORD_NORMAL);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Extension Points
|
||||
|
||||
Adding a new switch requires:
|
||||
1. A field in `KernelArgs` with a default macro.
|
||||
2. One `else if` branch in `cmdline_parse()`.
|
||||
3. One read site in the kernel (usually `kernel_main.c`).
|
||||
|
||||
No changes to `BootInfo`, `kernel_entry.S`, or the loader calling code.
|
||||
|
||||
## Invariants
|
||||
|
||||
- If `LoadOptions` is NULL or empty, all defaults apply — identical to
|
||||
current behaviour. Existing QEMU launch commands continue to work.
|
||||
- The BSS stack (`g_kernel_stack`) remains in the binary as a fallback.
|
||||
It can be removed in a future cleanup once dynamic allocation is proven.
|
||||
- `KernelArgs.raw` is always null-terminated; max 511 chars.
|
||||
- Unknown arguments never cause a boot failure.
|
||||
@@ -0,0 +1,18 @@
|
||||
# docs/reference/kernel-args/
|
||||
|
||||
Design doc for kernel command-line arguments (`KernelArgs`).
|
||||
|
||||
- **[DESIGN.md](DESIGN.md)** — Design for runtime-configurable memory sizing
|
||||
and behavior (heap size, stack depth, DoE self-start, log verbosity)
|
||||
currently baked in as compile-time constants. Covers the UEFI
|
||||
`EFI_LOADED_IMAGE_PROTOCOL->LoadOptions` mechanism, the `KernelArgs`
|
||||
struct, `BootInfo` changes, the assembly trampoline
|
||||
(`kernel_entry.S`), the `cmdline.c` parser, loader/`kernel_main.c`/
|
||||
Makefile integration, passing args under QEMU, and the `REBOOT` FORTH
|
||||
word (including its NVRAM one-shot mechanism and hosted-VM stub). Status:
|
||||
design complete, awaiting implementation.
|
||||
|
||||
## See also
|
||||
|
||||
- [`docs/working/archive/historical/2026-10-lithosananke-orientation.md`](../README.md) — kernel branch overview.
|
||||
- [Project root](../../../README.md)
|
||||
@@ -0,0 +1,404 @@
|
||||
# Word-Level ACL System
|
||||
|
||||
**Status:** Implemented through Phase 7 (LithosAnanke kernel parity) — independently verified
|
||||
present in current `master` (`.claude/CLAUDE.md`'s Word-Level ACL System section). Phase 8
|
||||
(PKI/thumbdrive minting) is the current open item.
|
||||
**Target branch:** `master` — post-split, this repo's sole production line; there is no
|
||||
separate `lithosananke` branch to reach parity with (see `.claude/CLAUDE.md`'s "On the branch
|
||||
topology" note)
|
||||
**Implementation files:** `capsules/ACL.4th`, `capsules/zuse.4th`, `src/word_source/acl_words.c`, `src/test_runner/modules/acl_words_test.c`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
### Overview
|
||||
|
||||
A word-level access control system for StarForth VMs, implemented as a
|
||||
combination of a thin C infrastructure layer and a pure-FORTH policy capsule
|
||||
(`ACL.4th`). The C layer provides four `DictEntry` fields for the hot path
|
||||
(TTL counter + allow bit + mode + pin). All policy, all adaptive logic, all
|
||||
inheritance rules live as colon definitions in `ACL.4th`. A bootstrap
|
||||
superuser (`zuse.4th`) provides the sole authenticated escalation path.
|
||||
|
||||
---
|
||||
|
||||
### ACL Disposition — Three States
|
||||
|
||||
Each word in the dictionary has one of three enforcement dispositions:
|
||||
|
||||
| State | Behavior |
|
||||
|-------|---------|
|
||||
| `STRICT` | ACL re-checked on every execution |
|
||||
| `TTL` | ACL cached; re-checked only when TTL counter reaches zero |
|
||||
| `PINNED` | Current mode and decision frozen permanently; one-way ratchet |
|
||||
|
||||
---
|
||||
|
||||
### TTL — Statistically Adaptive
|
||||
|
||||
The TTL is not a fixed value. It is derived and continuously updated from
|
||||
the existing SSM execution physics:
|
||||
|
||||
- **Heat** (`execution_heat`): hotter words earn longer TTLs; check cost
|
||||
amortized over more executions
|
||||
- **Rolling window**: sudden shift in caller pattern or access rate causes
|
||||
TTL to shrink aggressively
|
||||
- **Decay**: quiescent words pull their TTL back down so the next burst
|
||||
re-validates early
|
||||
- **Inference engine** (L5/L6): detects oscillating TTL and stabilizes it —
|
||||
same CV threshold logic already used for window-width inference
|
||||
|
||||
`PINNED` is the asymptote: the adaptive process converges, you thumbtack
|
||||
it, and the accumulator freezes permanently.
|
||||
|
||||
A FORTH security word selects enforcement mode per word:
|
||||
|
||||
```forth
|
||||
' MY-WORD ACL-STRICT \ check every execution
|
||||
' MY-WORD ACL-TTL-MODE \ use adaptive TTL
|
||||
' MY-WORD ACL-PIN \ freeze — mode and decision immutable forever
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### The Thumbtack / Pin Flag
|
||||
|
||||
`ACL-PIN ( xt -- )` is a one-way ratchet:
|
||||
|
||||
- Transitions: `STRICT → PINNED` or `TTL → PINNED` — never back
|
||||
- Once pinned, `acl_mode` and `acl_ttl` are immutable for that VM context
|
||||
- Any attempt to change a pinned word's ACL is silently ignored (or errors,
|
||||
depending on policy)
|
||||
- Kernel primitive words (`BIRTH`, `EXEC`, `BYE`, etc.) are pinned by Mama
|
||||
at boot before the first `BIRTH` fires
|
||||
|
||||
---
|
||||
|
||||
### Inheritance at Birth
|
||||
|
||||
When a child VM is born:
|
||||
|
||||
```
|
||||
acl_mode = parent->acl_mode \ policy propagates (STRICT or TTL)
|
||||
acl_pinned = 0 \ always clear — child owns its own lock
|
||||
acl_ttl = default \ reset; child has no execution history
|
||||
acl_decision = ALLOW \ re-evaluated on first access
|
||||
```
|
||||
|
||||
Key properties:
|
||||
|
||||
- **Pin is contextual, not viral.** A parent's pinned words do not force
|
||||
the child to be pinned. The child inherits the mode as a starting point
|
||||
and can tighten, relax, or re-pin freely.
|
||||
- **Security lattice flows downward at birth only.** After birth, each VM's
|
||||
ACL state is independent. A compromised child cannot bootstrap its way
|
||||
back to Mama's pinned ACLs.
|
||||
|
||||
---
|
||||
|
||||
### Interpreter Hook — Two-Level Check
|
||||
|
||||
The C interpreter reads only two fields per `DictEntry`:
|
||||
|
||||
```c
|
||||
if (vm->emergency_console) goto execute; /* 100% bypass — physical access */
|
||||
if (entry->acl_ttl-- > 0) goto execute; /* TTL good — single decrement */
|
||||
acl_recheck(vm, entry); /* TTL=0: call FORTH ACL-RECHECK */
|
||||
if (!entry->acl_allow) goto reject;
|
||||
```
|
||||
|
||||
**Hot path cost:** one decrement and a branch — essentially free.
|
||||
**Cold path:** calls `ACL-RECHECK` in `ACL.4th`, which recomputes the
|
||||
adaptive TTL, updates `acl_allow`, and resets the counter. The C side
|
||||
never reasons about policy; it only reads the result.
|
||||
|
||||
---
|
||||
|
||||
### Two-Console Architecture
|
||||
|
||||
Two permanent, independent console layers exist at all times:
|
||||
|
||||
#### 1. Emergency Console (always present)
|
||||
|
||||
- `emergency_console` (`uint8_t`) flag in `VM` struct — C-only write; no
|
||||
FORTH word sets it
|
||||
- Checked first in the interpreter hot path — 100% ACL bypass
|
||||
- `acl_recheck()` uses save/restore around it for re-entrancy protection
|
||||
- `EMERGENCY_CONSOLE_ENABLED` Makefile build flag (default=1): when set to
|
||||
0, strips interactive fallthrough; `vm_fault_handler()` weak symbol is
|
||||
called instead (override for hardware reset, watchdog, debug probe)
|
||||
- `BYE` returns to the emergency console
|
||||
- Only `panic` kills the VM
|
||||
- Prompt: `ok>`
|
||||
|
||||
#### 2. Zuse Console (omnipresent, awaiting authentication)
|
||||
|
||||
- `zuse_session` (`uint8_t`) flag in `VM` struct — C-only write; no FORTH
|
||||
word sets it
|
||||
- Prompt: `zuse)ok>`
|
||||
- Full ACL bypass when a Zuse session is active
|
||||
- Completely independent of `emergency_console` — setting one does not
|
||||
affect the other
|
||||
|
||||
---
|
||||
|
||||
### Superuser: Zuse
|
||||
|
||||
Named for Konrad Zuse, pioneer of programmable computers. Zuse is the
|
||||
bootstrap superuser: the sole entity that can own the zuse console and mint
|
||||
user credentials.
|
||||
|
||||
- Defined in `capsules/zuse.4th`, loaded by `ACL.4th` at capsule boot
|
||||
- Software-only for now (no thumbdrive); `zuse.4th` is a capsule citizen
|
||||
from day one
|
||||
- Zuse's words are pinned by `ACL-ZUSE-BOOT` at load time
|
||||
- `ACL-BOOT` is called first, then `S" zuse.4th" EXEC` — both from within
|
||||
`ACL.4th` itself (Block 2067)
|
||||
- Future: replace with thumbdrive PKI (Ed25519 challenge-response);
|
||||
`zuse.4th` becomes the bootstrapper that validates the physical drive
|
||||
|
||||
---
|
||||
|
||||
### CA Root
|
||||
|
||||
- Embedded in `ACL.4th` (Block 2066) as constants `ACL-CA-KEY-LO` /
|
||||
`ACL-CA-KEY-HI`
|
||||
- Capsule hash = root-of-trust fingerprint: any CA key change changes the
|
||||
capsule hash and the birth protocol detects tampering
|
||||
- Placeholder (zeros) until `tools/mkcapsule` embeds the real Ed25519 key
|
||||
at build time
|
||||
|
||||
---
|
||||
|
||||
### Security Model / No-Security Condition
|
||||
|
||||
Security is opt-in via `init.4th`. The toggle is one commented-out line:
|
||||
|
||||
```forth
|
||||
\ S" ACL.4th" EXEC
|
||||
```
|
||||
|
||||
| `ACL.4th` | `zuse.4th` | Result |
|
||||
|-----------|------------|--------|
|
||||
| absent | absent | No security — open dev mode |
|
||||
| present | absent | ACL enforced; emergency REPL locked until Zuse provisioned |
|
||||
| present | present | Full lockdown; Zuse owns the zuse console |
|
||||
|
||||
Uncomment the line in `init.4th` to enable full lockdown. Leave it
|
||||
commented out for development builds.
|
||||
|
||||
---
|
||||
|
||||
### Self-Activating Capsule
|
||||
|
||||
`ACL.4th` is self-activating — `init.4th` only needs one line:
|
||||
|
||||
```forth
|
||||
S" ACL.4th" EXEC
|
||||
```
|
||||
|
||||
Internal structure of `ACL.4th`:
|
||||
- **Block 2066**: CA root placeholder constants (`ACL-CA-KEY-LO` /
|
||||
`ACL-CA-KEY-HI`)
|
||||
- **Block 2067**: Calls `ACL-BOOT` then `S" zuse.4th" EXEC`
|
||||
|
||||
`ACL-BOOT` stamps default ACL entries on all existing dictionary words.
|
||||
After it runs, every subsequent `:` definition gets an ACL entry via the
|
||||
defining-word hook. No word can exist without an ACL entry.
|
||||
|
||||
---
|
||||
|
||||
### Emergency Console — 100% Bypass
|
||||
|
||||
`vm->emergency_console` is set by the C layer when the physical `ok>` REPL
|
||||
is active. It is checked first, before any ACL table lookup. Physical
|
||||
presence always wins. The emergency console is never subject to ACL
|
||||
constraints regardless of what `ACL.4th` defines.
|
||||
|
||||
The `EMERGENCY_CONSOLE_ENABLED=0` build flag strips the interactive
|
||||
fallthrough for production or embedded builds, routing faults to
|
||||
`vm_fault_handler()` instead.
|
||||
|
||||
---
|
||||
|
||||
### ACL.4th — Pure FORTH Implementation
|
||||
|
||||
The ACL table is a `CREATE`d FORTH array indexed by execution token (XT).
|
||||
`'` (tick) gives the XT of any word; XT is just a cell value usable as a
|
||||
table key.
|
||||
|
||||
Core words:
|
||||
|
||||
| Word | Stack | Description |
|
||||
|------|-------|-------------|
|
||||
| `ACL-ENTRY` | `( xt -- addr )` | O(1) table lookup by XT |
|
||||
| `ACL-MODE@` | `( xt -- mode )` | read enforcement mode |
|
||||
| `ACL-MODE!` | `( mode xt -- )` | set mode (no-op if pinned) |
|
||||
| `ACL-PINNED?` | `( xt -- flag )` | test pin bit |
|
||||
| `ACL-PIN` | `( xt -- )` | set pin — one-way, irreversible |
|
||||
| `ACL-STRICT` | `( xt -- )` | set STRICT mode (no-op if pinned) |
|
||||
| `ACL-TTL-MODE` | `( xt -- )` | set TTL mode (no-op if pinned) |
|
||||
| `ACL-TTL@` | `( xt -- n )` | read current TTL counter |
|
||||
| `ACL-TTL!` | `( n xt -- )` | write TTL counter |
|
||||
| `ACL-ALLOW@` | `( xt -- flag )` | read cached allow/deny decision |
|
||||
| `ACL-ALLOW!` | `( flag xt -- )` | write allow/deny decision |
|
||||
| `ACL-INHERIT` | `( src dst -- )` | birth inheritance: copy mode, clear pin, reset ttl+decision |
|
||||
| `ACL-RECHECK` | `( xt -- )` | adaptive TTL recomputation; updates allow + new TTL |
|
||||
| `ACL-INIT-PRIMITIVES` | `( -- )` | bulk-initialize ACL entries for all existing dictionary words |
|
||||
|
||||
Example pin at boot in `init.4th`:
|
||||
|
||||
```forth
|
||||
' BIRTH ACL-STRICT ' BIRTH ACL-PIN
|
||||
' EXEC ACL-STRICT ' EXEC ACL-PIN
|
||||
' BYE ACL-STRICT ' BYE ACL-PIN
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Bootstrap Sequence
|
||||
|
||||
```forth
|
||||
\ In init.4th (one line, opt-in toggle — uncomment to enable):
|
||||
S" ACL.4th" EXEC \ self-activating: calls ACL-BOOT then loads zuse.4th
|
||||
|
||||
\ ACL.4th Block 2067 does internally:
|
||||
ACL-BOOT \ stamp default ACLs on all existing words
|
||||
S" zuse.4th" EXEC \ load and pin Zuse's words
|
||||
|
||||
\ Subsequent capsule loads (e.g. doe.4th) get ACL entries via : hook
|
||||
S" doe.4th" EXEC
|
||||
```
|
||||
|
||||
Every `:` definition after `ACL-BOOT` runs creates its own ACL entry at
|
||||
definition time via a hook in the defining word. No word is ever born
|
||||
without an ACL entry.
|
||||
|
||||
---
|
||||
|
||||
### Capsule Namespace
|
||||
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `init.4th` | Mama VM personality (default); contains opt-in ACL toggle |
|
||||
| `init-*.4th` | Alternate personalities |
|
||||
| `doe.4th` | DoE experiment harness |
|
||||
| `ACL.4th` | Word-level ACL subsystem — self-activating; loads `zuse.4th` |
|
||||
| `zuse.4th` | Bootstrap superuser personality; words pinned by `ACL-ZUSE-BOOT` |
|
||||
| `std-blob.4th` | Standard library layer (future) |
|
||||
|
||||
---
|
||||
|
||||
## Implementation Punch List
|
||||
|
||||
### Phase 1 — C Infrastructure (`master`) ✅ COMPLETE
|
||||
|
||||
- [x] Add `acl_ttl` (`uint32_t`) to `DictEntry` in `include/vm.h`
|
||||
- [x] Add `acl_allow` (`uint8_t`) to `DictEntry`
|
||||
- [x] Add `acl_mode` (`uint8_t`: STRICT=0 / TTL=1) to `DictEntry`
|
||||
- [x] Add `acl_pinned` (`uint8_t`) to `DictEntry`
|
||||
- [x] Default-initialize all four fields in `word_register()`:
|
||||
`ttl=ACL_OPEN, allow=1, mode=TTL, pinned=0`
|
||||
- [x] Add `emergency_console` (`uint8_t`) flag to `VM` struct
|
||||
- [x] Add `zuse_session` (`uint8_t`) flag to `VM` struct
|
||||
- [x] Insert two-level ACL check into interpreter loop in `vm.c`
|
||||
- [x] Implement `acl_recheck()` in `vm.c` — calls FORTH `ACL-RECHECK` when
|
||||
TTL=0; save/restore `emergency_console` for re-entrancy protection
|
||||
- [x] Add `:` definition hook to create ACL entry for each newly defined word
|
||||
|
||||
### Phase 2 — C Primitive Words + `ACL.4th` Capsule (`master`) ✅ COMPLETE
|
||||
|
||||
- [x] 12 C primitive words in `src/word_source/acl_words.c`
|
||||
- [x] `CREATE ACL-TABLE` sized to max dictionary entries
|
||||
- [x] `ACL-ENTRY ( xt -- addr )` — XT-indexed O(1) lookup
|
||||
- [x] Field accessors: `ACL-MODE@`, `ACL-MODE!`, `ACL-PINNED?`,
|
||||
`ACL-TTL@`, `ACL-TTL!`, `ACL-ALLOW@`, `ACL-ALLOW!`
|
||||
- [x] `ACL-PIN ( xt -- )` — one-way ratchet; no-op if already pinned
|
||||
- [x] `ACL-STRICT ( xt -- )` — set STRICT mode; no-op if pinned
|
||||
- [x] `ACL-TTL-MODE ( xt -- )` — set TTL mode; no-op if pinned
|
||||
- [x] `ACL-INHERIT ( src-xt dst-xt -- )` — copy mode, clear pin,
|
||||
reset ttl+decision
|
||||
- [x] `ACL-RECHECK ( xt -- )` — recompute adaptive TTL from heat and rolling
|
||||
window; update `acl_allow` and reset counter
|
||||
- [x] `ACL-INIT-PRIMITIVES` — walk dictionary, create default entry per word
|
||||
- [x] Pin privileged words: `BIRTH`, `EXEC`, `BYE`, and other Mama-only words
|
||||
|
||||
### Phase 3 — `capsules/ACL.4th` Self-Activating Capsule (`master`) ✅ COMPLETE
|
||||
|
||||
- [x] `ACL.4th` is self-activating: Block 2067 calls `ACL-BOOT` then
|
||||
`S" zuse.4th" EXEC`
|
||||
- [x] Block 2066: CA root placeholder constants (`ACL-CA-KEY-LO` /
|
||||
`ACL-CA-KEY-HI`)
|
||||
- [x] `ACL-BOOT` stamps default ACL entries on all existing dictionary words
|
||||
- [x] `capsules/zuse.4th` loaded at end of `ACL.4th` capsule
|
||||
- [x] Zuse's words pinned by `ACL-ZUSE-BOOT` at load time
|
||||
|
||||
### Phase 4 — `init.4th` Opt-In Toggle (`master`) ✅ COMPLETE
|
||||
|
||||
- [x] `init.4th` has one commented-out line as the opt-in toggle:
|
||||
`\ S" ACL.4th" EXEC`
|
||||
- [x] Uncomment to enable full lockdown; leave commented for dev/open mode
|
||||
- [x] No other changes to `init.4th` required — `ACL.4th` is self-contained
|
||||
|
||||
### Phase 5 — POST Tests (`master`) ✅ COMPLETE
|
||||
|
||||
POST tests implemented in `src/test_runner/modules/acl_words_test.c`,
|
||||
registered in `src/test_runner/test_runner.c`. 800/800 passing.
|
||||
|
||||
- [x] POST test: `ACL-PIN` is one-way — mode cannot change after pin set
|
||||
- [x] POST test: inheritance — child entry has mode copied, pin cleared,
|
||||
ttl and decision reset
|
||||
- [x] POST test: emergency console bypass — `emergency_console=1` skips
|
||||
ACL check entirely
|
||||
- [x] POST test: STRICT mode — ACL re-evaluated on every execution
|
||||
- [x] POST test: TTL hot path — TTL > 0 bypasses re-evaluation
|
||||
- [x] POST test: adaptive accumulator — heat increase produces TTL increase
|
||||
- [x] POST test: privileged words pinned at boot remain pinned after
|
||||
`ACL-INIT-PRIMITIVES`
|
||||
|
||||
### Phase 6 — Isabelle/HOL Formal Verification (`master`) ✅ COMPLETE
|
||||
|
||||
Five `.thy` files in `proof/` alongside existing VM proofs:
|
||||
|
||||
- [x] `ACL_Pin_Monotone.thy` — pin bit is set-only; no operation clears it
|
||||
once set
|
||||
- [x] `ACL_Inherit_Clears_Pin.thy` — `ACL-INHERIT` always produces an entry
|
||||
with `acl_pinned = 0` regardless of source entry state
|
||||
- [x] `ACL_TTL_Bounded.thy` — TTL counter is bounded above by
|
||||
`ACL-TTL-COMPUTE` output; cannot grow unboundedly
|
||||
- [x] `ACL_Emergency_Bypass.thy` — when `emergency_console = 1` the
|
||||
allow/deny decision is never consulted
|
||||
- [x] `ACL_No_Escalation.thy` — a child VM cannot produce a pinned entry
|
||||
with higher privilege than its inherited mode
|
||||
|
||||
### Phase 7 — LithosAnanke Parity (COMPLETE — independently verified)
|
||||
|
||||
There is no separate `lithosananke` branch to merge/port to (post-split, this repo's `master`
|
||||
is the sole production line) — that framing is stale. Verified present directly in current
|
||||
`master`: `acl_recheck()`/`zuse_session`/`emergency_console` wiring confirmed in
|
||||
`src/starkernel/vm/vm_core.c`; the per-iteration `emergency_console = zuse_session ? 0 : 1`
|
||||
assignment confirmed in `src/starkernel/repl.c`; the old `!vm->zuse_session` ACL-check bypass
|
||||
confirmed absent from `src/vm.c`. See `.claude/CLAUDE.md`'s Word-Level ACL System section for
|
||||
the full verification writeup.
|
||||
|
||||
- [x] Verify `ACL.4th` loads cleanly in kernel context (freestanding)
|
||||
- [x] `ACL-BOOT` runs at kernel boot before first `BIRTH`
|
||||
- [x] `vm->emergency_console` wired to kernel REPL active flag
|
||||
- [x] `vm->zuse_session` wired to kernel Zuse console authentication path
|
||||
- [x] Three-arch acceptance: amd64, aarch64, riscv64 boot to `ok>` with ACL
|
||||
active and no regressions
|
||||
- [x] Acceptance logs — this repo's standing convention commits every acceptance boot's serial
|
||||
log under `logs/`, not a one-time Phase 7 action
|
||||
|
||||
### Phase 8 — PKI / Thumbdrive Authentication (FUTURE)
|
||||
|
||||
- [ ] Ed25519 challenge-response authentication for Zuse thumbdrive
|
||||
- [ ] `tools/mkcapsule` embeds real Ed25519 CA key into `ACL.4th` Block 2066
|
||||
at build time (replacing zero placeholder)
|
||||
- [ ] User minting: admin creates thumbdrive with CA-signed certificate + home
|
||||
block image
|
||||
- [ ] Lose the drive → admin mints a new one; no software recovery path by
|
||||
design
|
||||
- [ ] `zuse.4th` becomes the bootstrapper that validates the physical drive
|
||||
before granting Zuse session access
|
||||
@@ -0,0 +1,24 @@
|
||||
# docs/reference/word-acl/
|
||||
|
||||
Design doc for the word-level ACL (access control) system.
|
||||
|
||||
- **[DESIGN.md](DESIGN.md)** — Full design: three ACL dispositions per word
|
||||
(`STRICT`, `TTL`, `PINNED`), the four `DictEntry` fields (`acl_ttl`,
|
||||
`acl_allow`, `acl_mode`, `acl_pinned`), the two VM flags
|
||||
(`emergency_console`, `zuse_session`), and the implementation split between
|
||||
C infrastructure and the pure-FORTH policy capsule `capsules/ACL.4th`.
|
||||
Status: implemented through Phase 6 on `master`; Phase 7 (LithosAnanke
|
||||
parity) is next.
|
||||
|
||||
Implementation files referenced by this design: `capsules/ACL.4th`,
|
||||
`capsules/zuse.4th`, `src/word_source/acl_words.c`,
|
||||
`src/test_runner/modules/acl_words_test.c`, and the ACL proof set in
|
||||
`proof/` (`ACL_Pin_Monotone.thy`, `ACL_Inherit_Clears_Pin.thy`,
|
||||
`ACL_TTL_Bounded.thy`, `ACL_Emergency_Bypass.thy`, `ACL_No_Escalation.thy`).
|
||||
|
||||
## See also
|
||||
|
||||
- [`proof/README.md`](../../../proof/README.md) — the ACL proof theory files.
|
||||
- [`capsules/README.md`](../../../capsules/README.md) — capsule files
|
||||
including `ACL.4th` and `zuse.4th`.
|
||||
- [Project root](../../../README.md)
|
||||
Reference in New Issue
Block a user