Compare commits
106
Commits
+1
-1
@@ -14,7 +14,7 @@ Checks: >
|
||||
|
||||
WarningsAsErrors: [ ]
|
||||
|
||||
HeaderFilterRegex: '^src/.*|^include/.*'
|
||||
HeaderFilterRegex: '^v3/(src|include)/.*|^kernel/(src|include)/.*'
|
||||
|
||||
AnalyzeTemporaryDtors: false
|
||||
FormatStyle: none
|
||||
|
||||
+28
-28
@@ -6,8 +6,8 @@
|
||||
# clang-tidy will ALWAYS false-positive them. Always.
|
||||
# ---------------------------------------------------------
|
||||
|
||||
src/platform/**
|
||||
src/platform/*
|
||||
v3/src/platform/**
|
||||
v3/src/platform/*
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Skip test infrastructure
|
||||
@@ -15,8 +15,8 @@ src/platform/*
|
||||
# and stress edge conditions clang-tidy will never grok.
|
||||
# ---------------------------------------------------------
|
||||
|
||||
src/test_runner/**
|
||||
src/test_runner/*
|
||||
v3/src/test_runner/**
|
||||
v3/src/test_runner/*
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Skip word definitions generated or "mechanically patterned"
|
||||
@@ -24,30 +24,30 @@ src/test_runner/*
|
||||
# clang-tidy gives ZERO useful feedback on them.
|
||||
# ---------------------------------------------------------
|
||||
|
||||
src/word_source/string_words.c
|
||||
src/word_source/block_words.c
|
||||
src/word_source/arithmetic_words.c
|
||||
src/word_source/mixed_arithmetic_words.c
|
||||
src/word_source/return_stack_words.c
|
||||
src/word_source/defining_words.c
|
||||
src/word_source/stack_words.c
|
||||
src/word_source/logical_words.c
|
||||
src/word_source/memory_words.c
|
||||
src/word_source/io_words.c
|
||||
src/word_source/dictionary_words.c
|
||||
src/word_source/dictionary_manipulation_words.c
|
||||
src/word_source/vocabulary_words.c
|
||||
src/word_source/system_words.c
|
||||
src/word_source/starforth_words.c
|
||||
src/word_source/editor_words.c
|
||||
src/word_source/format_words.c
|
||||
v3/src/word_source/string_words.c
|
||||
v3/src/word_source/block_words.c
|
||||
v3/src/word_source/arithmetic_words.c
|
||||
v3/src/word_source/mixed_arithmetic_words.c
|
||||
v3/src/word_source/return_stack_words.c
|
||||
v3/src/word_source/defining_words.c
|
||||
v3/src/word_source/stack_words.c
|
||||
v3/src/word_source/logical_words.c
|
||||
v3/src/word_source/memory_words.c
|
||||
v3/src/word_source/io_words.c
|
||||
v3/src/word_source/dictionary_words.c
|
||||
v3/src/word_source/dictionary_manipulation_words.c
|
||||
v3/src/word_source/vocabulary_words.c
|
||||
v3/src/word_source/system_words.c
|
||||
v3/src/word_source/starforth_words.c
|
||||
v3/src/word_source/editor_words.c
|
||||
v3/src/word_source/format_words.c
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Skip headers belonging to the word system
|
||||
# Same reason: no signal, infinite false positives.
|
||||
# ---------------------------------------------------------
|
||||
|
||||
src/word_source/include/*
|
||||
v3/src/word_source/include/*
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Skip anything that implements performance knobs,
|
||||
@@ -55,15 +55,15 @@ src/word_source/include/*
|
||||
# These are meant to be weird, and tidy can't reason about them.
|
||||
# ---------------------------------------------------------
|
||||
|
||||
src/stack_management.c
|
||||
src/profiler.c
|
||||
src/vm_debug.c
|
||||
src/physics_runtime.c
|
||||
v3/src/stack_management.c
|
||||
v3/src/profiler.c
|
||||
v3/src/vm_debug.c
|
||||
v3/src/physics_runtime.c
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Skip integration + stress torture-tests.
|
||||
# These *intentionally* do UB-like bullshit.
|
||||
# ---------------------------------------------------------
|
||||
|
||||
src/test_runner/modules/*stress*
|
||||
src/test_runner/modules/*integration*
|
||||
v3/src/test_runner/modules/*stress*
|
||||
v3/src/test_runner/modules/*integration*
|
||||
|
||||
+54
-38
@@ -11,20 +11,31 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
> **Superseded subsystem docs (Captain Bob, 2026-08-15):** `.claude/TRIPOD.md`,
|
||||
> `.claude/HERMES.md`, `.claude/ARTEMIS.md`, and `.claude/CONSOLE.md` are all superseded —
|
||||
> `FABRIC-0.md`, `FABRIC-1.md`, and `FABRIC-2.md` (all design history/archival as of
|
||||
> 2026-09-04) and `FABRIC-3.md` (current/living, topic: bare metal boot, read this one first)
|
||||
> are the sole authoritative source for Tripod/Hermes/Artemis/Console work now. The four
|
||||
> subsystem docs remain in the repo as historical record only; each carries its own
|
||||
> superseded-header pointing here. Do not treat them as current, do not read them for design
|
||||
> authority, and do not cite them in place of `FABRIC-0.md`/`FABRIC-1.md`/`FABRIC-2.md`/
|
||||
> `FABRIC-3.md`.
|
||||
> 2026-09-04), `FABRIC-3.md` (current/living, topic: bare metal boot), and `FABRIC-3.5.md`/
|
||||
> `FABRIC-3.6.md` (Tripod/kernel reshuffle, design + execution) are the sole authoritative
|
||||
> source for Tripod/Hermes/Artemis/Console work now. The four subsystem docs remain in the
|
||||
> repo as pure historical record — they describe the pre-reshuffle Hera/Hermes/Artemis fleet,
|
||||
> and `.claude/HERMES.md` in particular now describes a VM that no longer exists in this
|
||||
> codebase at all, not merely a superseded design. Each carries its own superseded-header
|
||||
> pointing here. Do not treat them as current, do not read them for design authority, and do
|
||||
> not cite them in place of `FABRIC-0.md`/`FABRIC-1.md`/`FABRIC-2.md`/`FABRIC-3.md`/
|
||||
> `FABRIC-3.5.md`/`FABRIC-3.6.md`. **Decision (Captain Bob, 2026-09-22, FABRIC-3.6.md task
|
||||
> 5.2): archive as-is, do not rewrite.** These four docs stay frozen at their 2026-08-15
|
||||
> superseded state; they are not updated to reflect Hermes's removal, since they are already
|
||||
> explicitly out of scope for current design authority and rewriting historical record to
|
||||
> track a codebase it no longer describes would defeat its purpose as a record.
|
||||
|
||||
> **WORK IN PROGRESS — Tripod/kernel reshuffle (2026-09-19).** Hermes moves into the kernel;
|
||||
> the Tripod becomes **Hera / Artemis / Hestia**. Design is complete and ruled in
|
||||
> **`FABRIC-3.5.md`** (authoritative); execution is tracked in **`FABRIC-3.6.md`** — **start
|
||||
> there, at its `START HERE` section.** `FABRIC-3.md` remains open and authoritative for its
|
||||
> own topic (bare metal boot); nothing in the reshuffle supersedes it. **No reshuffle code has
|
||||
> been written yet.** The descriptions of the Tripod elsewhere in this file describe the
|
||||
> *current* fleet (Hera/Hermes/Artemis) and stay correct until the reshuffle lands.
|
||||
> **Tripod/kernel reshuffle — Phases 0–4 COMPLETE (2026-09-22); Phase 5 close-out in
|
||||
> progress.** Hermes moved into the kernel as kernel-Hermes (`src/starkernel/vm/kernel_
|
||||
> hermes.c`); the Tripod is now **Hera / Artemis / Hestia** — **the Hermes VM, `capsules/
|
||||
> hermes/init.4th`, and `capsules/common/messaging.4th` no longer exist in this codebase.**
|
||||
> Design is complete and ruled in **`FABRIC-3.5.md`** (authoritative); execution is tracked in
|
||||
> **`FABRIC-3.6.md`**. `FABRIC-3.md` remains open and authoritative for its own topic (bare
|
||||
> metal boot); nothing in the reshuffle supersedes it. **The Tripod/fleet descriptions
|
||||
> elsewhere in this file have been updated to match** (Hera/Artemis/Hestia, no Hermes) — this
|
||||
> is no longer a "stays correct until it lands" caveat, it already landed. Remaining:
|
||||
> Isabelle pass ✅, doc sweep (this note) ✅, SBOM/version bump/master-merge still open — see
|
||||
> `FABRIC-3.6.md` Phase 5 for status.
|
||||
|
||||
> **Scope:** This repo is LithosAnanke — the bare-metal UEFI kernel that boots StarForth
|
||||
> directly on hardware. StarForth (the hosted FORTH-79 VM) has its own separate repository
|
||||
@@ -181,8 +192,11 @@ userspace runtime. No libc, no traditional OS underneath. It is the bare-metal t
|
||||
removed as an active target.)
|
||||
|
||||
**Current status: M7.1** — capsule birth protocol, Mama FORTH vocabulary, Tripod multi-VM
|
||||
fleet (Hera/Hermes/Artemis), and word-level ACL (Phases 1–7) are live; POST at boot verifies
|
||||
parity hash across amd64/aarch64/riscv64 with a 453-word Mama capsule dictionary.
|
||||
fleet (Hera/Artemis/Hestia — Hermes moved into the kernel as kernel-Hermes, FABRIC-3.6.md
|
||||
Phase 4, 2026-09-22), and word-level ACL (Phases 1–7) are live; POST at boot verifies parity
|
||||
hash across amd64/aarch64/riscv64 with a 530-word Mama capsule dictionary (word count as of
|
||||
the Phase 4 strip; verify against a live `PARITY:M7.1a word_count=` line before citing, it
|
||||
changes with any new C word registration).
|
||||
|
||||
**Key distinguishing features:**
|
||||
|
||||
@@ -191,9 +205,11 @@ parity hash across amd64/aarch64/riscv64 with a 453-word Mama capsule dictionary
|
||||
- Content-addressed, immutable **capsules** as the primary organizational unit — no dynamic
|
||||
allocator in the traditional sense; identity is a content hash (XXHash64), mutation
|
||||
produces a new capsule
|
||||
- **Tripod** — a named multi-VM fleet (Hera the Mama VM, two Hermes instances, Artemis)
|
||||
that births, runs, and re-births independently; verified booting live pre-REPL on all
|
||||
three architectures. See `.claude/TRIPOD.md`, `.claude/HERMES.md`, `.claude/ARTEMIS.md`.
|
||||
- **Tripod** — a named multi-VM fleet (Hera the Mama VM, Artemis, Hestia) that births, runs,
|
||||
and re-births independently; verified booting live pre-REPL on all three architectures.
|
||||
Hermes moved into the kernel as kernel-Hermes (`src/starkernel/vm/kernel_hermes.c`) and is
|
||||
no longer a Tripod VM — see `FABRIC-3.5.md`/`FABRIC-3.6.md`, not the archived
|
||||
`.claude/TRIPOD.md`/`.claude/HERMES.md`/`.claude/ARTEMIS.md`.
|
||||
- Word-level ACL security system with kernel parity (see above) — measured overhead three
|
||||
orders of magnitude below the measurement floor
|
||||
- Kconfig-based build configuration (~40 discoverable symbols spanning physics/heartbeat/
|
||||
@@ -208,43 +224,43 @@ parity hash across amd64/aarch64/riscv64 with a 453-word Mama capsule dictionary
|
||||
|
||||
```bash
|
||||
# Build kernel for a given architecture (amd64 default)
|
||||
make -f Makefile.starkernel ARCH=amd64
|
||||
make -f Makefile.starkernel ARCH=aarch64
|
||||
make -f Makefile.starkernel ARCH=riscv64
|
||||
make -f kernel/Makefile ARCH=amd64
|
||||
make -f kernel/Makefile ARCH=aarch64
|
||||
make -f kernel/Makefile ARCH=riscv64
|
||||
|
||||
# Run in QEMU with OVMF
|
||||
make -f Makefile.starkernel qemu
|
||||
make -f Makefile.starkernel ARCH=aarch64 qemu
|
||||
make -f Makefile.starkernel ARCH=riscv64 qemu
|
||||
make -f kernel/Makefile qemu
|
||||
make -f kernel/Makefile ARCH=aarch64 qemu
|
||||
make -f kernel/Makefile ARCH=riscv64 qemu
|
||||
|
||||
# Clean
|
||||
make -f Makefile.starkernel clean
|
||||
make -f kernel/Makefile clean
|
||||
```
|
||||
|
||||
Output: `build/<arch>/kernel/starkernel_loader.efi` + `build/<arch>/kernel/starkernel_kernel.elf`.
|
||||
|
||||
Two independently tracked version strings flow into the generated `include/version.h`:
|
||||
`VERSION` (`Makefile.starkernel` — the embedded StarForth engine version, currently `3.1.0`;
|
||||
`VERSION` (`kernel/Makefile` — the embedded StarForth engine version, currently `3.1.0`;
|
||||
note this does **not** auto-sync with the standalone StarForth repo's own version) and
|
||||
`LITHOS_VERSION` (`Makefile.starkernel` — the kernel version, currently **`2.0.0`**;
|
||||
`LITHOS_VERSION` (`kernel/Makefile` — the kernel version, currently **`2.0.0`**;
|
||||
corrected 2026-09-19 — this file said `2.0.1`, but `FABRIC-3.md` §I.2 rolled it back to
|
||||
`2.0.0` on 2026-09-04 because `2.0.1` names the SER5 hardware-track line and claims hardware
|
||||
progress not yet verified. The versioning policy is **semantic, not sequential** — see the
|
||||
roadmap table in `Makefile.starkernel` before choosing any version).
|
||||
roadmap table in `kernel/Makefile` before choosing any version).
|
||||
|
||||
### Build configuration (Kconfig — real, wired, not vestigial)
|
||||
|
||||
Every kernel-only knob (`STARFORTH_ENABLE_VM`, `PARITY_MODE`, the shared physics/heartbeat
|
||||
family, etc.) is an optional Kconfig symbol defined across `Kconfig`, `Kconfig.arch`,
|
||||
`Kconfig.heartbeat`, `Kconfig.kernel`, `Kconfig.physics`, `Kconfig.variant` (~40 symbols
|
||||
total). `Makefile.starkernel` pulls its defaults from this system via a `kconfig_bool(...)`
|
||||
total). `kernel/Makefile` pulls its defaults from this system via a `kconfig_bool(...)`
|
||||
mechanism — e.g. `STARFORTH_ENABLE_VM` defaults to **1** (confirmed at
|
||||
`Makefile.starkernel:61`), meaning a plain `make -f Makefile.starkernel` already builds with
|
||||
`kernel/Makefile:61`), meaning a plain `make -f kernel/Makefile` already builds with
|
||||
VM + capsule-birth + ACL active. A bare invocation uses the defaults it always has:
|
||||
|
||||
```bash
|
||||
make -f Makefile.starkernel ARCH=amd64 menuconfig
|
||||
make -f Makefile.starkernel ARCH=amd64 kernel_amd64_defconfig
|
||||
make -f kernel/Makefile ARCH=amd64 menuconfig
|
||||
make -f kernel/Makefile ARCH=amd64 kernel_amd64_defconfig
|
||||
```
|
||||
|
||||
### Hosted VM (vendored, for local sanity only)
|
||||
@@ -261,7 +277,7 @@ StarForth repo) were removed 2026-08-15 — they referenced
|
||||
in the actual generated `include/version.h` (which only has `STARFORTH_VERSION`,
|
||||
`STARFORTH_ARCH`, `STARFORTH_TARGET`, `STARFORTH_TIMESTAMP`, `STARFORTH_VERSION_FULL`,
|
||||
`LITHOS_VERSION`, `LITHOS_VERSION_STR`), so they could never have worked. Bump versions by hand-editing the `VERSION`/
|
||||
`LITHOS_VERSION` variables in `Makefile.starkernel` instead. Report the broken targets if
|
||||
`LITHOS_VERSION` variables in `kernel/Makefile` instead. Report the broken targets if
|
||||
asked, don't silently fix them.
|
||||
|
||||
### Important: Linker Configuration
|
||||
@@ -282,9 +298,9 @@ The vendored hosted `make` build (above) is NEVER used to validate kernel change
|
||||
|
||||
```bash
|
||||
# Run in this exact order for every kernel change:
|
||||
make -f Makefile.starkernel ARCH=amd64 clean qemu
|
||||
make -f Makefile.starkernel ARCH=aarch64 clean qemu
|
||||
make -f Makefile.starkernel ARCH=riscv64 clean qemu
|
||||
make -f kernel/Makefile ARCH=amd64 clean qemu
|
||||
make -f kernel/Makefile ARCH=aarch64 clean qemu
|
||||
make -f kernel/Makefile ARCH=riscv64 clean qemu
|
||||
```
|
||||
|
||||
**QEMU rule — non-negotiable:** Only ONE QEMU instance may run at a time, always in the
|
||||
@@ -459,7 +475,7 @@ listed in `proof/ROOT` (corrected 2026-09-19; this file said 23, and `proof/COVE
|
||||
said 52 all along). **Read `proof/COVERAGE.md` first**: its stated goal is not a green build but
|
||||
"identify precisely what cannot be proven and why — the boundary between 'formally verified'
|
||||
and 'not, for this specific reason'." Run `isabelle build -D proof/`
|
||||
directly; neither `Makefile` nor `Makefile.starkernel` in this repo defines an
|
||||
directly; neither `Makefile` nor `kernel/Makefile` in this repo defines an
|
||||
`isabelle-build`/`isabelle-check` target (unlike the standalone StarForth repo, which has a
|
||||
broken one — this repo simply doesn't have the target at all, so there's nothing to
|
||||
mistakenly invoke).
|
||||
@@ -479,7 +495,7 @@ status for kernel work.
|
||||
|
||||
- **Strict ANSI C99** — No GNU extensions, no C++ features
|
||||
- **Zero warnings target, with four explicit exceptions — corrected 2026-08-18, previous
|
||||
claim was wrong.** Build with `-Wall -Wextra -Werror`, but `Makefile.starkernel` carries
|
||||
claim was wrong.** Build with `-Wall -Wextra -Werror`, but `kernel/Makefile` carries
|
||||
`-Wno-error=unused-parameter -Wno-error=shift-negative-value -Wno-error=sign-compare
|
||||
-Wno-error=missing-field-initializers` — those four classes are enabled (still visible as
|
||||
warnings) but deliberately downgraded from fatal, everything else is. The previous version
|
||||
|
||||
@@ -933,10 +933,12 @@ WARN_LOGFILE = docs/api/doxygen_warnings.log
|
||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = include \
|
||||
src \
|
||||
src/word_source \
|
||||
src/test_runner \
|
||||
INPUT = v3/include \
|
||||
v3/src \
|
||||
v3/src/word_source \
|
||||
v3/src/test_runner \
|
||||
kernel/include \
|
||||
kernel/src \
|
||||
README.md
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
@@ -2366,9 +2368,10 @@ SEARCH_INCLUDES = YES
|
||||
# RECURSIVE has no effect here.
|
||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
||||
|
||||
INCLUDE_PATH = include \
|
||||
src/word_source/include \
|
||||
src/test_runner/include
|
||||
INCLUDE_PATH = v3/include \
|
||||
kernel/include \
|
||||
v3/src/word_source/include \
|
||||
v3/src/test_runner/include
|
||||
|
||||
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
|
||||
# patterns (like *.h and *.hpp) to filter out the header-files in the
|
||||
|
||||
-282
@@ -1,282 +0,0 @@
|
||||
# FABRIC-3.6.md — the Tripod/kernel reshuffle: execution log
|
||||
|
||||
> ## START HERE — session handoff
|
||||
>
|
||||
> **If you have just been told "go build it", read this section, then `FABRIC-3.5.md`'s §XLI,
|
||||
> then start at task 0.0 below. Do not re-derive the design — it is settled.**
|
||||
>
|
||||
> **Where the code is.** This repo is **LithosAnanke, on the Gitea instance at
|
||||
> `gitea.strshipos.com`, repo `admin/LithosAnanake`** — Captain Bob has the credentials.
|
||||
> **Confirm you are in the right repo before anything else.** `git remote -v` must show
|
||||
> `gitea.strshipos.com/admin/LithosAnanake`. **Nothing outside Gitea is this project.** (The
|
||||
> handoff was authored in a container whose default working directory was a *different*, empty
|
||||
> repo on an unrelated remote — if you ever see that, you are in the wrong place.) `master` is
|
||||
> the sole production line. Work on branch **`claude/starshipos-tripod-kernel-reshuffle-itbjns`**
|
||||
> (a clean fast-forward of `master` at `e56974e`; **documentation only — no reshuffle code has
|
||||
> been written**. The branch head is the source of truth; do not trust a commit id quoted here).
|
||||
>
|
||||
> **The two documents.** `FABRIC-3.5.md` is the **design record and is authoritative** — every
|
||||
> ruling, with its reasoning and evidence, §I–§XLIII. **This** document is the execution log.
|
||||
> Rulings are cited here, never restated. **If a task proves a ruling wrong, record the finding
|
||||
> here and amend `FABRIC-3.5.md` — never silently diverge.**
|
||||
>
|
||||
> **First action: task 0.0**, the three-ISA baseline. It is a gate, not a formality — see its
|
||||
> own rationale below. **Nothing else starts until it is recorded.**
|
||||
>
|
||||
> **Five traps this project's own history proves are real. A fresh session will walk into these:**
|
||||
>
|
||||
> 1. **Silent failure is the dominant mode here.** `FABRIC-3.5.md` §XXXV.0 lists four
|
||||
> independent instances. Most relevantly: *"a switch storm and a healthy idle REPL produce an
|
||||
> identical serial log."* **A green boot is weak evidence.** Name the specific observation
|
||||
> that proves a task worked, before running it.
|
||||
> 2. **`grep` cannot establish capsule reachability.** Capsules are birthed by name from runtime
|
||||
> strings. A reference count over `capsules/` and `src/` put the six live `init-l8-*`
|
||||
> capsules at **zero references**; including `experiments/` found 18–19 each. §XXII.2. **Use
|
||||
> the three routes, never grep alone.**
|
||||
> 3. **`fleet_conserved` cannot see Stadium heat.** The two heat accountings are entirely
|
||||
> decoupled (§XXXIX). A leaking message allocator leaves `fleet_conserved` reporting a serene
|
||||
> `1`. **Stage B's evidence is the ledger plus `stadium_conserved()`, never `fleet_conserved`.**
|
||||
> 4. **`dict_hash` *changing* is expected; `dict_hash` *diverging* across architectures is the
|
||||
> stop condition.** §XXXIV.6. Do not "fix" a changed hash.
|
||||
> 5. **Documentation in this repo drifts from the code — trust the code.** `.claude/CLAUDE.md`
|
||||
> carried four stale claims; **all four were corrected 2026-09-19 (§XLIV) and it is now
|
||||
> reliable.** Others are not: `capsules/MANIFEST.md` still describes block 4055 as a live
|
||||
> "immutable ABI" that `FABRIC-2.md:2773` declared stale, and still misdescribes block 2049.
|
||||
> `FABRIC-0.md` §25.7 lists resolved defects as open. **Where a document and the code
|
||||
> disagree, the code wins — and record the drift rather than working around it.**
|
||||
>
|
||||
> **Blocked, and not to be worked around:** Phase 3 needs three decisions from Captain Bob —
|
||||
> **B1** channels (one membership or negotiation), **B2** the `SK_SWITCH_MAX_SLOTS` ceiling of
|
||||
> 16, **B4** the payload bound against `INPUT_BUFFER_SIZE` 1025. **All design is complete; these
|
||||
> are rulings, not investigations.**
|
||||
>
|
||||
> **Do not:** create branches, stash, fix defects found in passing, bundle tasks into one
|
||||
> commit, or start any task without explicit authorization. Captain Bob's Law,
|
||||
> `.claude/CLAUDE.md`.
|
||||
|
||||
**Status: LIVING WORKING DOCUMENT, opened 2026-09-19.** This is the **execution** record for
|
||||
the reshuffle designed in `FABRIC-3.5.md`. Work is tracked, annotated and closed here.
|
||||
|
||||
**Why this is a separate document, per the series' own rule.** `FABRIC-1.md` closed at **4,420
|
||||
lines** for a stated reason: *"continuing to append here made the still-open work hard to find."*
|
||||
`FABRIC-3.5.md` stands at **4,452 lines** with 40+ open punch items scattered among hundreds of
|
||||
settled rulings — **it has crossed the same threshold, for the same reason.** Annotating a task
|
||||
list inside it, commit by commit, would bury the design record it exists to be.
|
||||
|
||||
**The split of responsibility is strict, and stated because `FABRIC-3.5.md` §XXXI found that
|
||||
documents in this series lose track of each other:**
|
||||
|
||||
| | `FABRIC-3.5.md` | `FABRIC-3.6.md` (this) |
|
||||
|---|---|---|
|
||||
| **Holds** | The design record — every ruling, its reasoning, its evidence | The work — tasks, results, dates, commits |
|
||||
| **State** | Design phase closed; archival close at `v2.1.0` (§XXVI.5) | Living until the work is done |
|
||||
| **On a conflict** | **Authoritative** | Defers, and records the discrepancy |
|
||||
|
||||
**Design rulings are never restated here, only cited** (`§XXXIV.2`, `§XL.4`, …). If a task needs
|
||||
a rule explained, read `FABRIC-3.5.md`. **If executing a task proves a ruling wrong, that is a
|
||||
finding: record it here and amend `FABRIC-3.5.md` there** — never silently diverge.
|
||||
|
||||
**The checkbox convention is deliberate.** `FABRIC-3.5.md` §XXXI.2 found the series'
|
||||
carry-forward discipline was mechanically auditable (`grep -c '\- \[ \]'`) up to `FABRIC-2.md`,
|
||||
and **broke at `FABRIC-3.md`, which uses no checkboxes at all** — after which "is anything still
|
||||
open?" stopped being a `grep` and became a reading exercise. **This document restores the
|
||||
convention**, so that question stays answerable by machine.
|
||||
|
||||
---
|
||||
|
||||
## Standing rules
|
||||
|
||||
Apply to **every** task, from `.claude/CLAUDE.md` and `FABRIC-3.5.md` §XXXIV:
|
||||
|
||||
- **One task, one commit.** No bundling.
|
||||
- **Acceptance is the three-architecture QEMU boot** — `clean qemu`, amd64/aarch64/riscv64, one
|
||||
at a time, in the foreground. Logs committed. There is no other acceptance test.
|
||||
- **`dict_hash` changing is expected. `dict_hash` *diverging* between architectures is a stop
|
||||
condition** (§XXXIV.6).
|
||||
- **`mkcapsule --lint capsules/` clean** after any capsule change.
|
||||
- **No task starts without explicit authorization.** Captain Bob's Law.
|
||||
- **Report, don't fix.** A defect found while doing a task is recorded here, not repaired in
|
||||
passing.
|
||||
|
||||
## Annotation convention
|
||||
|
||||
```
|
||||
- [x] 0.2 — Strip common/msg.4th
|
||||
2026-09-DD · commit abc1234 · 3-arch boot clean, lint 0 violations
|
||||
note: <anything surprising; a finding gets its own entry below>
|
||||
```
|
||||
|
||||
Mark `[~]` for started-not-finished, with what is outstanding. **Never mark `[x]` on a task
|
||||
whose check did not actually run** — `FABRIC-3.5.md` §XXXV.6 records that declaring done too
|
||||
early is this project's most repeated failure.
|
||||
|
||||
---
|
||||
|
||||
## Pre-flight — establish the baseline before anything changes
|
||||
|
||||
**Branch state at open (2026-09-19):** `claude/starshipos-tripod-kernel-reshuffle-itbjns`, head
|
||||
`3a4e5cf`, **working tree clean, pushed, 33 commits ahead of `master`** — all documentation, no
|
||||
code. `master` is unmoved at `e56974e`, so the branch remains a clean fast-forward. **Execution
|
||||
starts from this commit.**
|
||||
|
||||
- [ ] **0.0** — **Three-ISA baseline smoke test on the unmodified branch.**
|
||||
`make -f Makefile.starkernel ARCH=<arch> clean qemu` for amd64, aarch64, riscv64 — one at
|
||||
a time, in the foreground, per `.claude/CLAUDE.md`.
|
||||
*Check:* all three reach `zuse)ok>`; zero `UNKNOWN WORD`; logs committed under
|
||||
`logs/<timestamp>/<arch>/`; **record each `dict_hash` and confirm the three are
|
||||
identical.**
|
||||
|
||||
**Why this is a gate and not a formality.** Every task in this document takes the
|
||||
three-architecture boot as its acceptance (§XXXIV). **Without a known-good baseline captured
|
||||
first, the first red boot is ambiguous** — pre-existing fault or something task 0.2 just did?
|
||||
This project has been bitten by exactly that ambiguity before (`FABRIC-3.md` §XXVI: "the
|
||||
lockdown was never broken — wrong VM tested"). The baseline is what makes every later
|
||||
acceptance run interpretable, and the recorded `dict_hash` triple is the reference every
|
||||
subsequent §XXXIV.6 divergence check compares against.
|
||||
|
||||
**Run it yourself — you are expected to.** Captain Bob confirmed (2026-09-19) that execution
|
||||
happens on his PC with the full toolchain installed, so **the session reading this can and
|
||||
should run task 0.0 directly**, once authorized. Verify first that `qemu-system-x86_64`,
|
||||
`qemu-system-aarch64`, `qemu-system-riscv64`, the aarch64/riscv64 cross-compilers and
|
||||
OVMF/AAVMF are all present; **if any are missing you are not on the intended machine — stop and
|
||||
say so rather than improvising a partial test.**
|
||||
|
||||
*(Historical note: this document was authored in a container with none of that toolchain, which
|
||||
is why task 0.0 was written but never run. Do not take its unchecked state as a prior failure —
|
||||
it has simply not been attempted.)*
|
||||
|
||||
---
|
||||
|
||||
## Phase 0 — Preparation (no behaviour change)
|
||||
|
||||
**Gate:** all three architectures boot to `zuse)ok>`, `stadium_conserved()` true, no
|
||||
`UNKNOWN WORD`.
|
||||
|
||||
- [ ] **0.1** — Establish Category A reachability by §XXII.2's three routes (boot path, tooling,
|
||||
baked capsule directory). **Never by grep alone.**
|
||||
*Check:* a written list naming the route that proves each entry dead.
|
||||
- [ ] **0.2** — Strip `capsules/common/msg.4th`. *Check:* 3-arch boot; lint clean.
|
||||
- [ ] **0.3** — Strip `capsules/process.4th` (takes `EVENT-EMIT`/`-WAIT`/`-DRAIN` with it,
|
||||
§XXXIII.3). *Check:* 3-arch boot; lint clean.
|
||||
- [ ] **0.4** — Strip `SPAWN-EVENT`. *Check:* 3-arch boot; lint clean.
|
||||
- [ ] **0.5** — Correct `capsules/MANIFEST.md` blocks 4055 and 2049 **as their files are
|
||||
stripped** (§XXII.5). *Check:* manifest describes no file that no longer exists.
|
||||
- [ ] **0.6** — Return freed block ranges to `capsule-reserved.txt`. *Check:* lint clean.
|
||||
- [ ] **0.7** — Add `stadium_conserved()`: `Σ patron + reservoir + consumed == Q48_ONE`,
|
||||
**epsilon zero** (§XL.4, item 41). *Check:* true on a clean boot, all three arches.
|
||||
- [ ] **0.8** — Audit that `PLOT`/`FB-WIDTH`/`FB-HEIGHT` are registered **nowhere** but the
|
||||
table Hestia will own (item 33). *Check:* read-only; a second site is a defect to report.
|
||||
|
||||
## Phase 1 — Hestia (messaging untouched)
|
||||
|
||||
**Gate:** Tripod is Hera/Artemis/Hestia **plus** Hermes; drawing works from Hestia and only
|
||||
Hestia; headless policy intact.
|
||||
|
||||
- [ ] **1.1** — Allocate Hestia's block range vs. `capsule-reserved.txt`, **avoiding 4997**.
|
||||
*Check:* lint clean.
|
||||
- [ ] **1.2** — Create `capsules/hestia/init.4th`: messaging load, `MSG-CD-INIT`, banner. **No
|
||||
fabric yet.** *Check:* boots; Hestia not yet birthed.
|
||||
- [ ] **1.3** — Add Hestia to `is_fleet_foundation` (`capsule_birth.c:793-796`) — **four names,
|
||||
Hermes retained** (§XXXIV.4). *Check:* 3-arch boot; Hermes still live.
|
||||
- [ ] **1.4** — Birth Hestia in `kernel_main.c`, **alongside** Hermes's existing birth.
|
||||
*Check:* registry shows both; `dict_hash` identical across arches.
|
||||
- [ ] **1.5** — Register Hestia for switch signals (the `:1007` pattern). *Check:* boot clean;
|
||||
**no switch storm** (§XXVIII.2's shape — and note it is invisible by default, §XXXV.0).
|
||||
- [ ] **1.6** — Move `fabric.4th` from `init.4th` to `hestia/init.4th`. *Check:* Hera's dict
|
||||
shrinks, Hestia's grows; cross-arch identity holds.
|
||||
- [ ] **1.7** — Move `font.4th` likewise. *Check:* same.
|
||||
- [ ] **1.8** — Move `PLOT`/`FB-WIDTH`/`FB-HEIGHT` registration to Hestia's table **only**.
|
||||
*Check:* **positively verify** a non-Hestia VM calling `PLOT` gets `UNKNOWN WORD`.
|
||||
- [ ] **1.9** — Assert §XVIII.6's headless invariant: Hestia's birth sets no
|
||||
`g_wirebind_attached_username` and mints no proxy. *Check:* boot headless, no thumbdrive,
|
||||
**no prompt appears**.
|
||||
|
||||
## Phase 2 — Allocator and audit (inert) — **the real go/no-go**
|
||||
|
||||
**Gate:** task 2.7. **If it fails, stop and re-plan. Do not proceed to Phase 3.**
|
||||
|
||||
- [ ] **2.1** — Kernel-Hermes message/membership structures, **wired to nothing, drawing no
|
||||
heat**. *Check:* boot byte-identical; `dict_hash` unmoved.
|
||||
- [ ] **2.2** — Allocate: pull `Q.SLOT` from the caller's reservoir; roll back on refusal.
|
||||
*Check:* N allocs against a known reservoir; refusal at the right count.
|
||||
- [ ] **2.3** — Release: return remaining heat via the eviction path. *Check:* reservoir
|
||||
restored exactly for an undecayed message.
|
||||
- [ ] **2.4** — The four counters: `held`, `pulled`, `returned`, `consumed` (§XL.4). *Check:*
|
||||
each increments at exactly one site.
|
||||
- [ ] **2.5** — Decay: apply, and **record the delta into `consumed`** (§XXXVII.3). *Check:*
|
||||
`consumed` grows by exactly `heat_before − heat_after`.
|
||||
- [ ] **2.6** — Self-audit: `held == pulled − returned − consumed`, **epsilon zero**. *Check:*
|
||||
holds across the cycle; **deliberately corrupt a counter → audit fires on the first unit**.
|
||||
- [ ] **2.7** — **Stage B proof** (§XXXIV.3 as corrected by §XXXIX.4): alloc/free cycle
|
||||
verifying **(a)** the ledger and **(b)** `stadium_conserved()` before and after.
|
||||
*Check:* both true, all three arches. **`fleet_conserved` is not evidence here** — it
|
||||
cannot see Stadium heat (§XXXIX.1).
|
||||
- [ ] **2.8** — Scan-based cross-check of the counters, diagnostics only, off the hot path
|
||||
(§XXXVII.4). *Check:* scan agrees with counters.
|
||||
|
||||
## Phase 3 — Cutover — **BLOCKED**
|
||||
|
||||
**Not buildable until all three clear.** Shape once unblocked (§XXXIV.3): cut over
|
||||
`BLK-ATTACH-EVENT` alone, then remaining types one at a time, under §XXXIV.2's partition rule —
|
||||
one message type owned by exactly one layer, no message shared.
|
||||
|
||||
- [ ] **B1** — **Item 27**: channels — negotiation, or one broadcast membership? (§XXXIII.5
|
||||
recommends the latter; unruled.)
|
||||
- [ ] **B2** — **Item 32**: `SK_SWITCH_MAX_SLOTS` is 16 and §XXXII made the switcher the sole
|
||||
mover of control. Constant bump or table redesign?
|
||||
- [x] **B3** — **CLEARED 2026-09-19 by `FABRIC-3.5.md` §XLIII**: the target drains its own
|
||||
queue at its own outermost interpret checkpoint; kernel-Hermes publishes and never
|
||||
dispatches. Reuses `sk_vm_at_outermost_interpret()` and the Stage 3 checkpoint.
|
||||
- [ ] **B4** — **Item 44**: payload bound or chunking against `INPUT_BUFFER_SIZE` 1025
|
||||
(§XLIII.6.1). Decide before Phase 3.
|
||||
|
||||
## Phase 4 — Category B strip
|
||||
|
||||
Only after every live type is cut over. **Hermes leaves `is_fleet_foundation` and
|
||||
`kernel_main.c` here, not earlier** (§XXXIV.4).
|
||||
|
||||
- [ ] **4.1** — Strip `capsules/hermes/init.4th`.
|
||||
- [ ] **4.2** — Strip the FORTH routing table and slot-3 pairing convention.
|
||||
- [ ] **4.3** — Strip `capsules/common/messaging.4th`.
|
||||
- [ ] **4.4** — Remove Hermes from `is_fleet_foundation` and its birth from `kernel_main.c`.
|
||||
|
||||
## Phase 5 — Close-out
|
||||
|
||||
- [ ] **5.1** — Isabelle/HOL pass. **Deliverable is the restated boundary**, explicitly
|
||||
including §XXV.4's coverage loss — *not* a green build (§XXV.3).
|
||||
- [~] **5.2** — Documentation sweep, grepping **by exclusion** (§XXVIII.3).
|
||||
**CLAUDE.md's four errors: DONE 2026-09-19 (§XLIV), pointer to this document added.**
|
||||
Outstanding: `MANIFEST.md` (rides the strip, §XXII.5), the `TRIPOD.md`/0.1 contradiction,
|
||||
item 42's K-qualification, the superseded subsystem docs' update-or-archive call, and
|
||||
item 45 (`experiments/bare_metal/README.md` block framing).
|
||||
- [ ] **5.3** — `make sbom`; check `Created:` and `DocumentName` (§XXVI.2).
|
||||
- [ ] **5.4** — `LITHOS_VERSION = 2.1.0`; engine `VERSION` per §XXX.6's rule; roadmap table
|
||||
gains its `2.1.0` line in two live places (§XXVIII.2).
|
||||
- [ ] **5.5** — Resolve the stray `refs/heads/v2.0.1` and PR #1 (§XXVI.4). **Investigate, do
|
||||
not delete.**
|
||||
- [ ] **5.6** — Merge to `master`; tag `v2.1.0`.
|
||||
- [ ] **5.7** — Archival close of `FABRIC-3.5.md` (§XXVI.5) and of this document.
|
||||
|
||||
---
|
||||
|
||||
## Findings log
|
||||
|
||||
*Defects and surprises found while executing. Reported, not fixed (unless the task was to fix
|
||||
them). A finding that contradicts a `FABRIC-3.5.md` ruling must also be amended there.*
|
||||
|
||||
*(none yet — work not started)*
|
||||
|
||||
---
|
||||
|
||||
## Out of scope, carried for visibility
|
||||
|
||||
Recorded in `FABRIC-3.5.md`, deliberately **not** part of this reshuffle. Listed so they are not
|
||||
absorbed by accident:
|
||||
|
||||
- **Item 43** — should a Stadium reservoir ever replenish? (§XL.6) A VM's send capacity declines
|
||||
monotonically today.
|
||||
- **Items 23–26** — the `FABRIC-3.5.md` §XXXI gap-analysis follow-ups: re-home `FABRIC-2` §17.4,
|
||||
consolidate the three reported-not-scheduled registries, decide `FABRIC-2`'s five orphaned
|
||||
design items, reconcile `FABRIC-0`'s seven opens.
|
||||
- **`src/*.c.bak`** — tracked-but-stale, reported in three places and actioned in none
|
||||
(§XXII.5).
|
||||
+1
-1
@@ -5,7 +5,7 @@ choice
|
||||
default ARCH_AMD64
|
||||
help
|
||||
Selects the value passed as ARCH= to whichever Makefile
|
||||
(Makefile or Makefile.starkernel) is driven from this config.
|
||||
(Makefile or kernel/Makefile) is driven from this config.
|
||||
Both Makefiles already accept "amd64"/"aarch64"/"riscv64" as
|
||||
canonical spellings (each has its own alias-normalizing logic
|
||||
for other spellings like x86_64/arm64/riscv), so this choice
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ config HEARTBEAT_THREAD_ENABLED
|
||||
builds without needing a special case. This is UI/model
|
||||
correctness (menuconfig can't offer a choice LithosAnanke has no
|
||||
way to honor -- there are no pthreads in a freestanding kernel),
|
||||
not the sole enforcement mechanism: Makefile.starkernel additionally
|
||||
not the sole enforcement mechanism: kernel/Makefile additionally
|
||||
keeps its own unconditional `VM_FEATURE_OVERRIDES +=
|
||||
-DHEARTBEAT_THREAD_ENABLED=0` post-override exactly as it was
|
||||
before this migration. Deliberately redundant with the `depends
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ config STARFORTH_ENABLE_VM
|
||||
kernel that only reaches the M0-M6 hardware milestones (console,
|
||||
PMM, VMM, interrupts, timers, kmalloc) with no FORTH interpreter,
|
||||
no capsules, no "ok" REPL. Gates a large source-file selection
|
||||
block in Makefile.starkernel, not just a handful of -D flags.
|
||||
block in kernel/Makefile, not just a handful of -D flags.
|
||||
|
||||
config PARITY_MODE
|
||||
bool "Deterministic parity harness mode (PARITY_MODE)"
|
||||
|
||||
@@ -6,6 +6,25 @@
|
||||
|
||||
---
|
||||
|
||||
## Products
|
||||
|
||||
StarForth v4 moves the engine onto a 32-instruction core derived from Chuck Moore's F18
|
||||
(`docs/v4.0.0/`). With it, this repository carries three products, not one:
|
||||
|
||||
| Product | What it is |
|
||||
|---|---|
|
||||
| **Hosted StarForth F18** | The v4 engine as a native Linux build, for amd64, arm64 and riscv64, on real hardware. |
|
||||
| **FPGA StarForth F18** | A 32-bit build of the same engine loaded into the FPGA. It is the gateway to the rest: the foundation everything else is built up from. |
|
||||
| **StarshipOS** | The bare-metal product: LithosAnanke running the v4 F18 engine. |
|
||||
|
||||
FORTH-79 is recomposed on the F18 engine and stored as a capsule; the StarshipOS-specific
|
||||
vocabulary gets the same treatment. The source tree will be reorganised around this split.
|
||||
|
||||
v3 remains the reference system until v4 meets its acceptance criteria
|
||||
(`docs/v4.0.0/JUSTIFICATION.md` §16). Everything below describes v3 as it stands today.
|
||||
|
||||
---
|
||||
|
||||
## Status — M7.1 (Capsule System · Multi-VM Fleet)
|
||||
|
||||
| Milestone | | Status |
|
||||
@@ -48,19 +67,39 @@ POST at boot: **parity hash verified across amd64/aarch64/riscv64 · Mama capsul
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# One boot image per physical target (output: build/boards/<board>/)
|
||||
make boot_image TARGET=SER5 # Beelink SER5 -- GPT/ESP, BOOTX64.EFI
|
||||
make boot_image TARGET=RASPI # Raspberry Pi 5 -- FAT, config.txt + kernel_2712.img + DTB
|
||||
make boot_image TARGET=MILKV # Milk-V Mars -- GPT/ESP, BOOTRISCV64.EFI (U-Boot bootefi)
|
||||
make boot_image TARGET=ZYNQ7020 # Zynq-7020 -- ARMv7 port in progress
|
||||
make boards # list boards
|
||||
|
||||
# The same TARGET drives all, docs and clean (make help)
|
||||
make # every board with a kernel port + hosted v3 + the book
|
||||
make TARGET=RASPI # one board's image + build/docs/LithosAnanke-raspi.pdf
|
||||
make docs # build/docs/LithosAnanke.pdf (LaTeX master: docs/book/main.tex)
|
||||
make clean TARGET=RASPI # that board's outputs and build/aarch64/kernel
|
||||
make clean # all of build/ except build/cache/
|
||||
|
||||
# Build kernel (requires cross-compilation toolchain, or native gcc)
|
||||
make -f Makefile.starkernel ARCH=amd64
|
||||
make -f kernel/Makefile ARCH=amd64
|
||||
|
||||
# Run in QEMU with OVMF
|
||||
make -f Makefile.starkernel qemu
|
||||
make -f kernel/Makefile qemu
|
||||
|
||||
# Other architectures
|
||||
make -f Makefile.starkernel ARCH=aarch64 qemu
|
||||
make -f Makefile.starkernel ARCH=riscv64 qemu
|
||||
make -f kernel/Makefile ARCH=aarch64 qemu
|
||||
make -f kernel/Makefile ARCH=riscv64 qemu
|
||||
```
|
||||
|
||||
Artifacts: `build/amd64/kernel/starkernel_loader.efi` · `build/amd64/kernel/starkernel_kernel.elf`
|
||||
|
||||
Tree: `kernel/` (LithosAnanke: `Makefile`, `src/`, `include/starkernel/`, `linker/`),
|
||||
`v3/` (StarForth v3 engine and hosted build: `Makefile`, `src/`, `include/`),
|
||||
`v4/` (StarForth v4, see `docs/v4.0.0/`), `boards/<board>/` (`board.mk` + boot files).
|
||||
Every makefile runs from the repo root; the root `Makefile` forwards any other goal to
|
||||
`v3/Makefile` (hosted build).
|
||||
|
||||
For the hosted VM by itself (Linux, no cross-compiler needed, no bare-metal tooling): see
|
||||
the separate **StarForth** repository — LithosAnanke used to be a branch inside that repo,
|
||||
now it's its own project with its own `master`.
|
||||
@@ -69,12 +108,12 @@ now it's its own project with its own `master`.
|
||||
|
||||
Every kernel-only knob (`STARFORTH_ENABLE_VM`, `PARITY_MODE`, the shared
|
||||
physics/heartbeat family, etc.) is an optional Kconfig symbol — a plain
|
||||
`make -f Makefile.starkernel` uses the same defaults it always has unless
|
||||
`make -f kernel/Makefile` uses the same defaults it always has unless
|
||||
you opt in:
|
||||
|
||||
```bash
|
||||
make -f Makefile.starkernel ARCH=amd64 menuconfig
|
||||
make -f Makefile.starkernel ARCH=amd64 kernel_amd64_defconfig
|
||||
make -f kernel/Makefile ARCH=amd64 menuconfig
|
||||
make -f kernel/Makefile ARCH=amd64 kernel_amd64_defconfig
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# boards/
|
||||
|
||||
One directory per physical target. `make boot_image TARGET=<NAME>` (repo root) maps the
|
||||
name to a directory here, reads its `board.mk`, and builds the single file that goes on
|
||||
that board's boot medium into `build/boards/<board>/`.
|
||||
|
||||
| TARGET | Directory | ISA | Boot path | Image |
|
||||
|---|---|---|---|---|
|
||||
| `SER5` | `ser5/` | amd64 | UEFI firmware, removable-media path | GPT + FAT32 ESP: `EFI/BOOT/BOOTX64.EFI` |
|
||||
| `RASPI` | `raspi/` | aarch64 | Pi 5 EEPROM firmware, native | MBR + FAT32: `config.txt`, `kernel_2712.img`, `bcm2712-rpi-5-b.dtb` |
|
||||
| `MILKV` | `milkv/` | riscv64 | SPI-flash U-Boot + OpenSBI, `bootefi` | GPT + FAT32 ESP: `EFI/BOOT/BOOTRISCV64.EFI` |
|
||||
| `ZYNQ7020` | `zynq7020/` | armv7 | BootROM, `BOOT.BIN`, U-Boot | not yet: needs `kernel/src/arch/armv7` |
|
||||
|
||||
`board.mk` sets:
|
||||
|
||||
- `BOARD_DESC`: one-line description (`make boards`)
|
||||
- `BOARD_ARCH`: kernel ISA (`kernel/src/arch/<arch>`)
|
||||
- `BOARD_BOOT`: image recipe in `kernel/Makefile` (`uefi-esp`, `rpi-native`, ...)
|
||||
- `BOARD_IMAGE`: output file name
|
||||
|
||||
Disk images are assembled by `scripts/mkdiskimage.sh`, which needs no root access and no loop
|
||||
devices. `KERNEL_ARGS="..."` is written as `starforth.cfg` (UEFI boards) or `cmdline.txt`
|
||||
(Pi 5, where the firmware copies it into `/chosen/bootargs`). `BOOT_IMAGE_SIZE_MIB` sets the
|
||||
image size; the default is 128.
|
||||
|
||||
Hardware status: none of these images has been booted on real silicon from this build. The
|
||||
SER5 image has been booted in QEMU (q35 + OVMF, from its own disk) through POST. The Pi 5
|
||||
native path (`raspi/README.md`) and the JH7110 peripherals have still only been checked by
|
||||
compiling.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Milk-V Mars boot media
|
||||
|
||||
`make boot_image TARGET=MILKV` builds `build/boards/milkv/lithos-milkv.img`: a GPT disk with one
|
||||
FAT32 EFI System Partition. Write it to an SD card with `dd`. The partition holds:
|
||||
|
||||
- `EFI/BOOT/BOOTRISCV64.EFI`: the riscv64 monolithic loader, the same one QEMU boots.
|
||||
- `startup.nsh`: for a UEFI shell, if one is used.
|
||||
- `starforth.cfg`: only present when `KERNEL_ARGS="..."` is given.
|
||||
|
||||
The board's own SPI-flash U-Boot, with OpenSBI underneath, finds the loader through its standard
|
||||
`bootefi` scan of the removable-media path. U-Boot passes its devicetree to the loader through the
|
||||
EFI configuration table.
|
||||
|
||||
Status: booted in QEMU through the same chain the board uses: OpenSBI, then U-Boot
|
||||
`qemu-riscv64_smode`, then `bootefi` from this image's ESP, then the loader and the kernel. POST
|
||||
passed, Stadium conservation was exact (resident 43691 + reservoir 21845 = 65536 = `Q48_ONE`), and
|
||||
Hestia and Artemis came up. Not yet booted on the Mars itself. The JH7110 peripherals
|
||||
(framebuffer, RNG) have only been checked by compiling.
|
||||
|
||||
To repeat the QEMU boot (`qemu-system-riscv64` is in Ubuntu 26.04's `qemu-system-riscv` package;
|
||||
OpenSBI and U-Boot come from `opensbi` and `u-boot-qemu`). Use a copy, because the kernel writes
|
||||
to its disk:
|
||||
|
||||
```
|
||||
cp build/boards/milkv/lithos-milkv.img /tmp/mv.img
|
||||
qemu-system-riscv64 -machine virt -cpu rv64 -m 2048 -nographic \
|
||||
-bios /usr/lib/riscv64-linux-gnu/opensbi/generic/fw_dynamic.bin \
|
||||
-kernel /usr/lib/u-boot/qemu-riscv64_smode/u-boot.bin \
|
||||
-drive if=none,format=raw,file=/tmp/mv.img,id=d0 -device virtio-blk-device,drive=d0
|
||||
```
|
||||
@@ -0,0 +1,11 @@
|
||||
# boards/milkv/board.mk -- Milk-V Mars (StarFive JH7110, SiFive U74, RV64GC).
|
||||
#
|
||||
# The board's own SPI-flash U-Boot (OpenSBI underneath) runs the EFI
|
||||
# application through its standard distro/bootefi scan: the SD card is a GPT
|
||||
# disk with one FAT32 EFI System Partition holding EFI/BOOT/BOOTRISCV64.EFI,
|
||||
# the same riscv64 monolithic loader QEMU boots. U-Boot passes its own
|
||||
# devicetree through the EFI configuration table.
|
||||
BOARD_DESC := Milk-V Mars (StarFive JH7110, RV64GC, U-Boot bootefi)
|
||||
BOARD_ARCH := riscv64
|
||||
BOARD_BOOT := uefi-esp
|
||||
BOARD_IMAGE := lithos-milkv.img
|
||||
@@ -0,0 +1,20 @@
|
||||
# Raspberry Pi 5 native boot media
|
||||
|
||||
`make boot_image TARGET=RASPI` builds `build/boards/raspi/lithos-raspi.img`: an MBR disk
|
||||
with one FAT32 (LBA) partition. Write it to an SD card with `dd`. The partition holds:
|
||||
|
||||
- `config.txt`: checked in here (FABRIC-3.md §IV.3 item 6). It pins `kernel_address=0x80000`
|
||||
because Pi 5 firmware loads `kernel_2712.img` at 0x200000 by default.
|
||||
- `kernel_2712.img`: the aarch64 kernel objects relinked at 0x80000 by
|
||||
`kernel/linker/starkernel-native-rpi5.ld`, entered at `rpi5_native_start`
|
||||
(`kernel/src/arch/aarch64/native_rpi5_entry.S`, which zeroes `.bss` first), and flattened
|
||||
with `objcopy -O binary`.
|
||||
- `bcm2712-rpi-5-b.dtb`: the Raspberry Pi firmware's own stock DTB. It is not built by this
|
||||
project; it is fetched once from the firmware release pinned in `board.mk`
|
||||
(`RPI_FIRMWARE_TAG`) into `build/cache/`. To use a local copy, pass `RPI5_DTB=/path/to/dtb`.
|
||||
- `cmdline.txt`: only present when `KERNEL_ARGS="..."` is given. The firmware copies it into
|
||||
`/chosen/bootargs`, which `rpi5_native_boot()` parses.
|
||||
|
||||
Not yet verified on hardware. The native path still assumes everything `kernel_main()`
|
||||
needs after a UEFI handoff. The firmware enters at EL2 with the MMU off, which is not what
|
||||
UEFI hands over, and that difference has not been exercised.
|
||||
@@ -0,0 +1,18 @@
|
||||
# boards/raspi/board.mk -- Raspberry Pi 5 (BCM2712, Cortex-A76, aarch64).
|
||||
#
|
||||
# Native firmware boot, no UEFI (FABRIC-3.md §IV): the Pi 5 bootloader
|
||||
# lives in EEPROM and reads config.txt, kernel_2712.img and the board DTB
|
||||
# from the first FAT partition of the SD card. kernel_2712.img is the
|
||||
# aarch64 kernel relinked at 0x80000 (kernel/linker/starkernel-native-rpi5.ld)
|
||||
# and flattened to a raw binary.
|
||||
#
|
||||
# The DTB is the Raspberry Pi firmware's own stock file, not built here. It
|
||||
# is fetched once from the pinned firmware release below into build/cache/
|
||||
# unless RPI5_DTB points at a local copy.
|
||||
BOARD_DESC := Raspberry Pi 5 (BCM2712, Cortex-A76, native firmware boot)
|
||||
BOARD_ARCH := aarch64
|
||||
BOARD_BOOT := rpi-native
|
||||
BOARD_IMAGE := lithos-raspi.img
|
||||
RPI_FIRMWARE_TAG ?= 1.20260915
|
||||
RPI5_DTB_URL ?= https://raw.githubusercontent.com/raspberrypi/firmware/$(RPI_FIRMWARE_TAG)/boot/bcm2712-rpi-5-b.dtb
|
||||
RPI5_DTB ?= build/cache/raspi-$(RPI_FIRMWARE_TAG)/bcm2712-rpi-5-b.dtb
|
||||
@@ -12,6 +12,13 @@
|
||||
# firmware picks up.
|
||||
kernel=kernel_2712.img
|
||||
|
||||
# Pin the load address to the one the image is linked at
|
||||
# (kernel/linker/starkernel-native-rpi5.ld). Pi 5 firmware's own default
|
||||
# for kernel_2712.img is 0x200000, not the classic 0x80000 -- leaving it
|
||||
# implicit would load a 0x80000-linked flat image 1.5 MiB away from where
|
||||
# every absolute address in it points.
|
||||
kernel_address=0x80000
|
||||
|
||||
# Disable the firmware's Linux-compatible-image sanity check. Without
|
||||
# this, official docs describe it as checking for "a compatible Device
|
||||
# Tree file before attempting to boot" and warn that "older non-compatible
|
||||
@@ -0,0 +1,14 @@
|
||||
# Beelink SER5 boot media
|
||||
|
||||
`make boot_image TARGET=SER5` builds `build/boards/ser5/lithos-ser5.img`: a GPT disk with one
|
||||
FAT32 EFI System Partition. Write it to a USB stick with `dd`. The partition holds:
|
||||
|
||||
- `EFI/BOOT/BOOTX64.EFI`: the amd64 monolithic loader, with the whole kernel embedded. Firmware
|
||||
finds it through the UEFI removable-media path, so no boot entry has to be created.
|
||||
- `startup.nsh`: runs the loader if the firmware drops into the UEFI shell.
|
||||
- `starforth.cfg`: only present when `KERNEL_ARGS="..."` is given.
|
||||
|
||||
Nothing in the image is SER5-specific. It follows the generic UEFI/ACPI path in FABRIC-3.md §III.
|
||||
|
||||
Status: booted in QEMU (q35 + OVMF, from this image as its own disk) through POST, with Stadium
|
||||
conservation exact (sum 65536 = `Q48_ONE`). Not yet booted on the SER5 itself.
|
||||
@@ -0,0 +1,10 @@
|
||||
# boards/ser5/board.mk -- Beelink SER5 (AMD Ryzen, x86-64, UEFI/ACPI).
|
||||
#
|
||||
# Generic standards-compliant UEFI path (FABRIC-3.md §III): nothing in the
|
||||
# image is SER5-specific. The monolithic loader embeds the whole kernel, so
|
||||
# the image is a GPT disk with one FAT32 EFI System Partition holding
|
||||
# EFI/BOOT/BOOTX64.EFI. Write it to a USB stick with dd.
|
||||
BOARD_DESC := Beelink SER5 (AMD Ryzen, x86-64, UEFI)
|
||||
BOARD_ARCH := amd64
|
||||
BOARD_BOOT := uefi-esp
|
||||
BOARD_IMAGE := lithos-ser5.img
|
||||
@@ -0,0 +1,17 @@
|
||||
# Xilinx Zynq-7020 boot media
|
||||
|
||||
The Zynq-7020's processing system is a dual Cortex-A9: ARMv7-A, 32-bit. It is the host for the
|
||||
StarForth v4 mesh (`docs/v4.0.0/JUSTIFICATION.md` §5, §8).
|
||||
|
||||
Boot chain: BootROM, then `BOOT.BIN` (U-Boot SPL with the board's `ps7_init` DDR and clock setup),
|
||||
then U-Boot, then LithosAnanke. Everything is read from the SD card's FAT partition.
|
||||
|
||||
Status: no image yet. `make boot_image TARGET=ZYNQ7020` stops with an error, because there is no
|
||||
ARMv7 kernel port (`kernel/src/arch/armv7`: entry code, MMU, GIC, private timer). A trial
|
||||
compile for Cortex-A9 built 147 of the 151 kernel and engine sources. The four that failed use
|
||||
`__int128` or have no ARMv7 branch: `kernel/src/crypto/fe25519.c`,
|
||||
`kernel/src/crypto/scalar25519.c`, `kernel/src/hal/hal.c` and
|
||||
`v3/src/word_source/mixed_arithmetic_words.c`.
|
||||
|
||||
`BOOT.BIN` depends on the exact board (Zybo Z7-20, PYNQ-Z2, Arty Z7-20, ZedBoard, ...), because
|
||||
`ps7_init` holds that board's DDR timing. The board has not been chosen yet.
|
||||
@@ -0,0 +1,11 @@
|
||||
# boards/zynq7020/board.mk -- Xilinx Zynq-7020 (dual Cortex-A9, ARMv7-A, 32-bit).
|
||||
#
|
||||
# Boot chain: BootROM -> BOOT.BIN (U-Boot SPL with the board's ps7_init DDR/
|
||||
# clock setup) -> U-Boot -> LithosAnanke, all from the SD card's FAT
|
||||
# partition. Needs the ARMv7 kernel port (kernel/src/arch/armv7), which is
|
||||
# in progress: until it exists, boot_image stops with an explicit error
|
||||
# rather than producing an image that cannot run.
|
||||
BOARD_DESC := Xilinx Zynq-7020 (Cortex-A9, ARMv7-A, BOOT.BIN) -- ARMv7 port in progress
|
||||
BOARD_ARCH := armv7
|
||||
BOARD_BOOT := zynq-bootbin
|
||||
BOARD_IMAGE := lithos-zynq7020.img
|
||||
@@ -1,12 +0,0 @@
|
||||
# Raspberry Pi 5 native boot media
|
||||
|
||||
What goes on the SD card for the native (non-UEFI) boot path (FABRIC-3.md §IV):
|
||||
|
||||
- `config.txt` — checked in here, done (§IV.3 item 6).
|
||||
- `bcm2712-rpi-5-b.dtb` — Raspberry Pi firmware's own stock DTB; not built by this
|
||||
project, copied from the firmware release the card is otherwise built from.
|
||||
- `kernel_2712.img` — **does not exist yet.** `config.txt` names it, but no build target
|
||||
in `Makefile.starkernel` currently emits a raw image by this name at load address
|
||||
0x80000 — item 1 (entry stub) explicitly scoped a separate-image build target as future
|
||||
work, not part of that item. Building this file is the remaining prerequisite before
|
||||
item 7 (assembling the card) is possible.
|
||||
@@ -98,6 +98,16 @@ Block 4007
|
||||
( rewired: physics-style regression belongs to the kernel )
|
||||
( exclusively, and this file is deliberately host-portable. )
|
||||
|
||||
Block 4008
|
||||
( HERMES-CHANNEL-OPEN? -- FABRIC-3.6.md task 3.7 hook. )
|
||||
( Stack: req-hi req-lo -- allow? Kernel-Hermes asks )
|
||||
( this word, never decides in C, never gates on )
|
||||
( zuse_session (CLAUDE.md). Default: approve every )
|
||||
( request. Policy authors edit THIS word's body only -- )
|
||||
( kernel_hermes.c's own query function never changes. )
|
||||
: HERMES-CHANNEL-OPEN? ( req-hi req-lo -- allow? )
|
||||
2DROP 1 ;
|
||||
|
||||
Block 4015
|
||||
( Self-activation - runs after all ACL words are defined )
|
||||
ACL-BOOT
|
||||
|
||||
+120
-163
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-09-16T19:45:24Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-10-01T19:21:21Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
@@ -8,53 +8,52 @@
|
||||
|
||||
| Capsule | Blocks claimed | xxHash64 | Signed |
|
||||
|---------|----------------|----------|--------|
|
||||
| `ACL.4th` | 4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4015 | `0xd781d22148ff171d` | yes |
|
||||
| `acl-std79.4th` | 4023, 4024, 4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 4034, 4035, 4036, 4037, 4038, 4039, 4040, 4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048 | `0x773bf9209df191d1` | yes |
|
||||
| `artemis:init.4th` | 4110, 4111, 4112, 4113, 4122, 4123, 4124, 4125, 4126, 4127, 4128, 4129, 4130, 4131, 4132, 4133, 4134, 4135, 4136, 4137, 4138, 4139, 4140, 4141, 4160, 4161, 4162, 4163, 4164, 4165, 4166, 4167, 4168, 4169, 4170, 4171, 4172, 4173, 4174, 4177, 4178, 4179, 4180, 4181, 4182, 4851, 4852, 4853, 4854, 4856, 4857, 4858, 4860 | `0xf55edc4e76a8c294` | yes |
|
||||
| `block-acl.4th` | 4019, 4020 | `0xf6cc2a59e3a6734e` | yes |
|
||||
| `common:messaging.4th` | 5003, 5004, 5005, 5006, 5007, 5008, 5009, 5010, 5011, 5012, 5013, 5014, 5015, 5016, 5017, 5018, 5019, 5020, 5021, 5022, 5023, 5024, 5025, 5026, 5027, 5028, 5029, 5030, 5031, 5032, 5033, 5034, 5035, 5036, 5037, 5038, 5039, 5040, 5041, 5042 | `0x201cfd6d39fcb22d` | yes |
|
||||
| `common:msg.4th` | 4055 | `0x850a0382344ea6c4` | yes |
|
||||
| `doe-campaign.4th` | 4060, 4061, 4062, 4063, 4064, 4065 | `0x3d4549142d91ec20` | yes |
|
||||
| `doe.4th` | 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107 | `0xf154616d248e861f` | yes |
|
||||
| `fabric.4th` | 4900, 4901, 4902, 4903, 4904, 4905, 4906, 4907, 4908, 4909, 4910, 4911, 4912, 4913, 4914, 4915, 4916, 4917, 4918, 4919, 4920, 4921, 4922, 4923, 4924, 5000, 5001, 5002 | `0x9d9489cbeca4099b` | yes |
|
||||
| `font.4th` | 4925, 4926, 4927, 4928, 4929, 4930, 4931, 4932, 4933, 4934, 4935, 4936, 4937, 4938, 4939, 4940, 4941, 4942, 4943, 4944, 4945, 4946, 4947, 4948, 4949, 4950, 4951, 4952, 4953, 4954, 4955, 4956, 4957, 4958, 4959, 4960, 4961, 4962, 4963, 4964, 4965, 4966, 4967, 4968, 4969, 4970, 4971, 4972, 4973, 4974, 4975, 4976, 4977, 4978, 4979, 4980, 4981, 4982, 4983, 4984, 4985 | `0x3f305911500c78f6` | yes |
|
||||
| `hermes:init.4th` | 4153, 4855, 5116 | `0x2df61924448a6812` | yes |
|
||||
| `init-l8-diverse.4th` | 4820, 4821, 4822 | `0xaa293201a6c91838` | yes |
|
||||
| `init-l8-omni.4th` | 2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079 | `0x5979e314d6452045` | yes |
|
||||
| `init-l8-stable.4th` | 4806 | `0xdc3830f189063a9a` | yes |
|
||||
| `init-l8-temporal.4th` | 4830, 4831 | `0x51abd4c138246651` | yes |
|
||||
| `init-l8-transition.4th` | 4840, 4841, 4842 | `0xbcc1a81976f0a4c9` | yes |
|
||||
| `init-l8-volatile.4th` | 4810, 4811, 4812, 4813 | `0x98caabbbd92abac4` | yes |
|
||||
| `init.4th` | 2049, 2050, 2057 | `0x1ef4939ed32ec1e6` | yes |
|
||||
| `lib.4th` | 4050 | `0x4b216635c359ef73` | yes |
|
||||
| `multiuser-doe.4th` | 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055 | `0x22140588ee7c39e6` | yes |
|
||||
| `process.4th` | 4300, 4301 | `0x781afc1dbd0294f7` | yes |
|
||||
| `sdk.4th` | 5109, 5110, 5111, 5112, 5113, 5114, 5115 | `0x008fdbbb62c94a3a` | yes |
|
||||
| `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` | yes |
|
||||
| `user-font-demo.4th` | 4200, 4201, 4202 | `0xce1fd7d1b581a56d` | yes |
|
||||
| `workload-0.4th` | 2200, 2201 | `0x93f86f60aeba8feb` | yes |
|
||||
| `workload-1-lite.4th` | 5058, 5059 | `0x44a7a7e3176dcc8d` | yes |
|
||||
| `workload-1.4th` | 4406, 4415, 4425, 4435 | `0x63e251adb0a03613` | yes |
|
||||
| `workload-2.4th` | 4506, 4515, 4525, 4535, 4545 | `0xf113b3d0bcccae47` | yes |
|
||||
| `workload-3.4th` | 4606, 4615, 4625, 4635, 4645, 4655, 4665 | `0x62b7a71576ad1041` | yes |
|
||||
| `workload-4.4th` | 2130, 2131, 2132 | `0x099619264e650b1c` | yes |
|
||||
| `workload-5-lite.4th` | 5056, 5057 | `0xea0303f38824f254` | yes |
|
||||
| `workload-5.4th` | *(none — raw code capsule)* | `0x526964c7c9506a11` | yes |
|
||||
| `workload-6.4th` | 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095 | `0x06fc0ce1e369ef5a` | yes |
|
||||
| `workload-7.4th` | 2150 | `0x02862291387eb7ac` | yes |
|
||||
| `workload-8.4th` | 2160 | `0x56b7f2f0efa000df` | yes |
|
||||
| `workload-9.4th` | 4706, 4715, 4725, 4735, 4745 | `0x3f2bec73142aa424` | yes |
|
||||
| `workload-calib1.4th` | 5043 | `0x3b9f2d17b554fabc` | yes |
|
||||
| `zuse-eligibility.4th` | 4021, 4022 | `0x8b49c1bc1e01dc58` | yes |
|
||||
| `zuse.4th` | 4016, 4017, 4018 | `0x490ded9be257a90b` | yes |
|
||||
| `ACL.4th` | 4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 4015 | `0xf8890c05c0d8f921` | n/a |
|
||||
| `acl-std79.4th` | 4023, 4024, 4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 4034, 4035, 4036, 4037, 4038, 4039, 4040, 4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048 | `0x773bf9209df191d1` | n/a |
|
||||
| `artemis:init.4th` | 4110, 4111, 4112, 4113, 4122, 4123, 4124, 4125, 4126, 4127, 4128, 4129, 4130, 4131, 4132, 4133, 4134, 4135, 4136, 4137, 4138, 4139, 4140, 4141, 4160, 4161, 4162, 4163, 4164, 4165, 4166, 4167, 4168, 4169, 4170, 4171, 4172, 4173, 4174, 4177, 4178, 4179, 4180, 4181, 4182, 4851, 4852, 4853, 4854, 4856, 4857, 4858, 4860 | `0xe6fba9ae56e1c916` | n/a |
|
||||
| `block-acl.4th` | 4019, 4020 | `0xf6cc2a59e3a6734e` | n/a |
|
||||
| `doe-campaign.4th` | 4060, 4061, 4062, 4063, 4064, 4065 | `0x26fb485e5c9dc6ff` | n/a |
|
||||
| `doe.4th` | 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107 | `0xf154616d248e861f` | n/a |
|
||||
| `fabric.4th` | 4900, 4901, 4902, 4903, 4904, 4905, 4906, 4907, 4908, 4909, 4910, 4911, 4912, 4913, 4914, 4915, 4916, 4917, 4918, 4919, 4920, 4921, 4922, 4923, 4924, 5000, 5001, 5002 | `0x9d9489cbeca4099b` | n/a |
|
||||
| `font.4th` | 4925, 4926, 4927, 4928, 4929, 4930, 4931, 4932, 4933, 4934, 4935, 4936, 4937, 4938, 4939, 4940, 4941, 4942, 4943, 4944, 4945, 4946, 4947, 4948, 4949, 4950, 4951, 4952, 4953, 4954, 4955, 4956, 4957, 4958, 4959, 4960, 4961, 4962, 4963, 4964, 4965, 4966, 4967, 4968, 4969, 4970, 4971, 4972, 4973, 4974, 4975, 4976, 4977, 4978, 4979, 4980, 4981, 4982, 4983, 4984, 4985 | `0x3f305911500c78f6` | n/a |
|
||||
| `hestia:init.4th` | 4986, 4987, 4988 | `0x64e111990fbc45ff` | n/a |
|
||||
| `init-l8-diverse.4th` | 4820, 4821, 4822 | `0xaa293201a6c91838` | n/a |
|
||||
| `init-l8-omni.4th` | 2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079 | `0x5979e314d6452045` | n/a |
|
||||
| `init-l8-stable.4th` | 4806 | `0xdc3830f189063a9a` | n/a |
|
||||
| `init-l8-temporal.4th` | 4830, 4831 | `0x51abd4c138246651` | n/a |
|
||||
| `init-l8-transition.4th` | 4840, 4841, 4842 | `0xbcc1a81976f0a4c9` | n/a |
|
||||
| `init-l8-volatile.4th` | 4810, 4811, 4812, 4813 | `0x98caabbbd92abac4` | n/a |
|
||||
| `init.4th` | 2049, 2050, 2057 | `0xea038ba684c53443` | n/a |
|
||||
| `lib.4th` | 4050 | `0x4b216635c359ef73` | n/a |
|
||||
| `multiuser-doe.4th` | 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055 | `0x22140588ee7c39e6` | n/a |
|
||||
| `sdk.4th` | 5109, 5110, 5111, 5112, 5113, 5114, 5115 | `0x008fdbbb62c94a3a` | n/a |
|
||||
| `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` | n/a |
|
||||
| `user-font-demo.4th` | 4200, 4201, 4202 | `0xce1fd7d1b581a56d` | n/a |
|
||||
| `workload-0.4th` | 2200, 2201 | `0x93f86f60aeba8feb` | n/a |
|
||||
| `workload-1-lite.4th` | 5058, 5059 | `0x44a7a7e3176dcc8d` | n/a |
|
||||
| `workload-1.4th` | 4406, 4415, 4425, 4435 | `0x63e251adb0a03613` | n/a |
|
||||
| `workload-2.4th` | 4506, 4515, 4525, 4535, 4545 | `0xf113b3d0bcccae47` | n/a |
|
||||
| `workload-3.4th` | 4606, 4615, 4625, 4635, 4645, 4655, 4665 | `0x62b7a71576ad1041` | n/a |
|
||||
| `workload-4.4th` | 2130, 2131, 2132 | `0x099619264e650b1c` | n/a |
|
||||
| `workload-5-lite.4th` | 5056, 5057 | `0xea0303f38824f254` | n/a |
|
||||
| `workload-5.4th` | *(none — raw code capsule)* | `0x526964c7c9506a11` | n/a |
|
||||
| `workload-6.4th` | 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095 | `0x06fc0ce1e369ef5a` | n/a |
|
||||
| `workload-7.4th` | 2150 | `0x02862291387eb7ac` | n/a |
|
||||
| `workload-8.4th` | 2160 | `0x56b7f2f0efa000df` | n/a |
|
||||
| `workload-9.4th` | 4706, 4715, 4725, 4735, 4745 | `0x3f2bec73142aa424` | n/a |
|
||||
| `workload-calib1.4th` | 5043 | `0x3b9f2d17b554fabc` | n/a |
|
||||
| `zuse-eligibility.4th` | 4021, 4022 | `0x7b28f4776a32e0b7` | n/a |
|
||||
| `zuse.4th` | 4016, 4017, 4018 | `0xa14d5a7a88791cf6` | n/a |
|
||||
|
||||
*Signed column is `n/a`: this manifest run had no `--sign-key`. Re-run with `--sign-key <path>` to check signing status (does not modify or require rebuilding capsule_generated.c).*
|
||||
|
||||
## Block Map (sorted by LBN)
|
||||
|
||||
| LBN | Capsule | xxHash64 | Status |
|
||||
|-----|---------|----------|--------|
|
||||
| 2049 | `init.4th` | `0x1ef4939ed32ec1e6` | ok |
|
||||
| 2050 | `init.4th` | `0x1ef4939ed32ec1e6` | ok |
|
||||
| 2057 | `init.4th` | `0x1ef4939ed32ec1e6` | ok |
|
||||
| 2049 | `init.4th` | `0xea038ba684c53443` | ok |
|
||||
| 2050 | `init.4th` | `0xea038ba684c53443` | ok |
|
||||
| 2057 | `init.4th` | `0xea038ba684c53443` | ok |
|
||||
| 2064 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
|
||||
| 2065 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
|
||||
| 2066 | `init-l8-omni.4th` | `0x5979e314d6452045` | ok |
|
||||
@@ -102,22 +101,23 @@
|
||||
| 2160 | `workload-8.4th` | `0x56b7f2f0efa000df` | ok |
|
||||
| 2200 | `workload-0.4th` | `0x93f86f60aeba8feb` | ok |
|
||||
| 2201 | `workload-0.4th` | `0x93f86f60aeba8feb` | ok |
|
||||
| 4000 | `ACL.4th` | `0xd781d22148ff171d` | ok |
|
||||
| 4001 | `ACL.4th` | `0xd781d22148ff171d` | ok |
|
||||
| 4002 | `ACL.4th` | `0xd781d22148ff171d` | ok |
|
||||
| 4003 | `ACL.4th` | `0xd781d22148ff171d` | ok |
|
||||
| 4004 | `ACL.4th` | `0xd781d22148ff171d` | ok |
|
||||
| 4005 | `ACL.4th` | `0xd781d22148ff171d` | ok |
|
||||
| 4006 | `ACL.4th` | `0xd781d22148ff171d` | ok |
|
||||
| 4007 | `ACL.4th` | `0xd781d22148ff171d` | ok |
|
||||
| 4015 | `ACL.4th` | `0xd781d22148ff171d` | ok |
|
||||
| 4016 | `zuse.4th` | `0x490ded9be257a90b` | ok |
|
||||
| 4017 | `zuse.4th` | `0x490ded9be257a90b` | ok |
|
||||
| 4018 | `zuse.4th` | `0x490ded9be257a90b` | ok |
|
||||
| 4000 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||
| 4001 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||
| 4002 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||
| 4003 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||
| 4004 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||
| 4005 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||
| 4006 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||
| 4007 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||
| 4008 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||
| 4015 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||
| 4016 | `zuse.4th` | `0xa14d5a7a88791cf6` | ok |
|
||||
| 4017 | `zuse.4th` | `0xa14d5a7a88791cf6` | ok |
|
||||
| 4018 | `zuse.4th` | `0xa14d5a7a88791cf6` | ok |
|
||||
| 4019 | `block-acl.4th` | `0xf6cc2a59e3a6734e` | ok |
|
||||
| 4020 | `block-acl.4th` | `0xf6cc2a59e3a6734e` | ok |
|
||||
| 4021 | `zuse-eligibility.4th` | `0x8b49c1bc1e01dc58` | ok |
|
||||
| 4022 | `zuse-eligibility.4th` | `0x8b49c1bc1e01dc58` | ok |
|
||||
| 4021 | `zuse-eligibility.4th` | `0x7b28f4776a32e0b7` | ok |
|
||||
| 4022 | `zuse-eligibility.4th` | `0x7b28f4776a32e0b7` | ok |
|
||||
| 4023 | `acl-std79.4th` | `0x773bf9209df191d1` | ok |
|
||||
| 4024 | `acl-std79.4th` | `0x773bf9209df191d1` | ok |
|
||||
| 4025 | `acl-std79.4th` | `0x773bf9209df191d1` | ok |
|
||||
@@ -145,64 +145,60 @@
|
||||
| 4047 | `acl-std79.4th` | `0x773bf9209df191d1` | ok |
|
||||
| 4048 | `acl-std79.4th` | `0x773bf9209df191d1` | ok |
|
||||
| 4050 | `lib.4th` | `0x4b216635c359ef73` | ok |
|
||||
| 4055 | `common:msg.4th` | `0x850a0382344ea6c4` | ok |
|
||||
| 4060 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
|
||||
| 4061 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
|
||||
| 4062 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
|
||||
| 4063 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
|
||||
| 4064 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
|
||||
| 4065 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
|
||||
| 4110 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4111 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4112 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4113 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4122 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4123 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4124 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4125 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4126 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4127 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4128 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4129 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4130 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4131 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4132 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4133 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4134 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4135 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4136 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4137 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4138 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4139 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4140 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4141 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4153 | `hermes:init.4th` | `0x2df61924448a6812` | ok |
|
||||
| 4160 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4161 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4162 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4163 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4164 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4165 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4166 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4167 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4168 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4169 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4170 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4171 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4172 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4173 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4174 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4177 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4178 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4179 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4180 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4181 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4182 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4060 | `doe-campaign.4th` | `0x26fb485e5c9dc6ff` | ok |
|
||||
| 4061 | `doe-campaign.4th` | `0x26fb485e5c9dc6ff` | ok |
|
||||
| 4062 | `doe-campaign.4th` | `0x26fb485e5c9dc6ff` | ok |
|
||||
| 4063 | `doe-campaign.4th` | `0x26fb485e5c9dc6ff` | ok |
|
||||
| 4064 | `doe-campaign.4th` | `0x26fb485e5c9dc6ff` | ok |
|
||||
| 4065 | `doe-campaign.4th` | `0x26fb485e5c9dc6ff` | ok |
|
||||
| 4110 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4111 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4112 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4113 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4122 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4123 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4124 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4125 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4126 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4127 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4128 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4129 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4130 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4131 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4132 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4133 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4134 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4135 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4136 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4137 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4138 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4139 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4140 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4141 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4160 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4161 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4162 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4163 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4164 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4165 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4166 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4167 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4168 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4169 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4170 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4171 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4172 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4173 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4174 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4177 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4178 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4179 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4180 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4181 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4182 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4200 | `user-font-demo.4th` | `0xce1fd7d1b581a56d` | ok |
|
||||
| 4201 | `user-font-demo.4th` | `0xce1fd7d1b581a56d` | ok |
|
||||
| 4202 | `user-font-demo.4th` | `0xce1fd7d1b581a56d` | ok |
|
||||
| 4300 | `process.4th` | `0x781afc1dbd0294f7` | ok |
|
||||
| 4301 | `process.4th` | `0x781afc1dbd0294f7` | ok |
|
||||
| 4406 | `workload-1.4th` | `0x63e251adb0a03613` | ok |
|
||||
| 4415 | `workload-1.4th` | `0x63e251adb0a03613` | ok |
|
||||
| 4425 | `workload-1.4th` | `0x63e251adb0a03613` | ok |
|
||||
@@ -237,15 +233,14 @@
|
||||
| 4840 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
|
||||
| 4841 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
|
||||
| 4842 | `init-l8-transition.4th` | `0xbcc1a81976f0a4c9` | ok |
|
||||
| 4851 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4852 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4853 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4854 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4855 | `hermes:init.4th` | `0x2df61924448a6812` | ok |
|
||||
| 4856 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4857 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4858 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4860 | `artemis:init.4th` | `0xf55edc4e76a8c294` | ok |
|
||||
| 4851 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4852 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4853 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4854 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4856 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4857 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4858 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4860 | `artemis:init.4th` | `0xe6fba9ae56e1c916` | ok |
|
||||
| 4900 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
|
||||
| 4901 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
|
||||
| 4902 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
|
||||
@@ -332,49 +327,12 @@
|
||||
| 4983 | `font.4th` | `0x3f305911500c78f6` | ok |
|
||||
| 4984 | `font.4th` | `0x3f305911500c78f6` | ok |
|
||||
| 4985 | `font.4th` | `0x3f305911500c78f6` | ok |
|
||||
| 4986 | `hestia:init.4th` | `0x64e111990fbc45ff` | ok |
|
||||
| 4987 | `hestia:init.4th` | `0x64e111990fbc45ff` | ok |
|
||||
| 4988 | `hestia:init.4th` | `0x64e111990fbc45ff` | ok |
|
||||
| 5000 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
|
||||
| 5001 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
|
||||
| 5002 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
|
||||
| 5003 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5004 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5005 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5006 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5007 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5008 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5009 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5010 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5011 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5012 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5013 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5014 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5015 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5016 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5017 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5018 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5019 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5020 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5021 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5022 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5023 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5024 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5025 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5026 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5027 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5028 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5029 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5030 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5031 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5032 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5033 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5034 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5035 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5036 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5037 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5038 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5039 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5040 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5041 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5042 | `common:messaging.4th` | `0x201cfd6d39fcb22d` | ok |
|
||||
| 5043 | `workload-calib1.4th` | `0x3b9f2d17b554fabc` | ok |
|
||||
| 5044 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
||||
| 5045 | `multiuser-doe.4th` | `0x22140588ee7c39e6` | ok |
|
||||
@@ -408,11 +366,10 @@
|
||||
| 5113 | `sdk.4th` | `0x008fdbbb62c94a3a` | ok |
|
||||
| 5114 | `sdk.4th` | `0x008fdbbb62c94a3a` | ok |
|
||||
| 5115 | `sdk.4th` | `0x008fdbbb62c94a3a` | ok |
|
||||
| 5116 | `hermes:init.4th` | `0x2df61924448a6812` | ok |
|
||||
|
||||
## Conflicts
|
||||
|
||||
None.
|
||||
|
||||
---
|
||||
*39 capsule(s) scanned. Re-run `mkcapsule --manifest <dir>` to refresh.*
|
||||
*36 capsule(s) scanned. Re-run `mkcapsule --manifest <dir>` to refresh.*
|
||||
|
||||
+87
-74
@@ -49,7 +49,7 @@ Blocks: **2049–2052, 2057**
|
||||
|
||||
| Block | Immutable | Justification |
|
||||
|-------|-----------|---------------|
|
||||
| 2049 | No | Hera VM init: loads compudynamics, VM-INIT, lib, common:msg, fleet-k, process; BIRTHs Artemis + Hermes. VM-TREE + VM-CHILDREN implemented (`b52281b9`), unrolled for v1 Tripod |
|
||||
| 2049 | No | Hera VM init: `VM-TREE`/`VM-PARENT`/`VM-CHILDREN`; loads `ACL.4th`, `block-acl.4th`, `zuse-eligibility.4th`, `lib.4th`. **Corrected 2026-09-19 (three times)** — first pass removed a stale listing of `compudynamics`/`common:msg`/`fleet-k`/`process` (none loaded here; two already-deleted, two stripped in the same pass, FABRIC-3.6.md tasks 0.2/0.3). Second pass (same day): `fabric.4th`/`font.4th` **removed** from this list — relocated to `hestia/init.4th` (FABRIC-3.6.md tasks 1.6/1.7), Hera no longer loads either. Third pass (2026-09-22, FABRIC-3.6.md Phase 4 Stage E): `common:messaging.4th` + `MSG-CD-INIT` **removed** — the file itself is deleted (see "Deleted capsules" below); every FORTH-owned message type was cut over to kernel-Hermes first (Phase 3) |
|
||||
| 2050 | No | BOOT-BANNER call — separated so the banner block can be swapped without touching init logic |
|
||||
| 2051 | No | TRIPOD-TEST — 6 acceptance gates per TRIPOD.md. Dead EVENT-WAIT step replaced with HERMES-TICK liveness check (`3436d564`) |
|
||||
| 2052 | No | TRIPOD-TEST invocation block |
|
||||
@@ -308,14 +308,6 @@ Blocks: **4050**
|
||||
|-------|-----------|---------------|
|
||||
| 4050 | No | N. COMMA CRLF CSV-COL CSV-LAST Q.SHOW USE RUN. Not immutable: these are utilities; adding words is safe. Removing or renaming existing words requires audit of all callers |
|
||||
|
||||
### `common/msg.4th` — Hermes participant interface
|
||||
|
||||
Blocks: **4055**
|
||||
|
||||
| Block | Immutable | Justification |
|
||||
|-------|-----------|---------------|
|
||||
| 4055 | Yes | HERMES-ACK / HERMES-NACK. Immutable: this is the cross-VM ACK/NACK ABI. Every messaging VM (Hera, Hermes, Artemis) loads this at birth. Changing the block or the word names breaks the Hermes delivery protocol |
|
||||
|
||||
### `doe-campaign.4th` — DoE campaign harness
|
||||
|
||||
Blocks: **4060–4065**
|
||||
@@ -325,74 +317,51 @@ Blocks: **4060–4065**
|
||||
matched the file at any point on record. Rewritten to match the actual
|
||||
content, migrated this pass off the deleted `compudynamics.4th`/
|
||||
`fleet-k.4th` primitives onto the `VM-PHYSICS-STATUS`/`VM-CONSERVED?`
|
||||
mechanism — see `capsules/hermes/init.4th` block 4153 and
|
||||
mechanism — see (historical) `hermes/init.4th` block 4153 and
|
||||
`capsules/artemis/init.4th` block 4852 for the companion `LOAD-DOE` fix
|
||||
this migration also required.
|
||||
|
||||
**Note (2026-09-22, FABRIC-3.6.md Phase 4 Stage E):** `SETUP-HERMES` and every
|
||||
Hermes touch in `PHASE1-DOE`/`CD-TICK`/`SMOKE-CAMPAIGN` removed — Hermes no
|
||||
longer exists. Every block below is now Artemis-only.
|
||||
|
||||
| Block | Immutable | Justification |
|
||||
|-------|-----------|---------------|
|
||||
| 4060 | No | `SETUP-HERMES`/`SETUP-ARTEMIS`/`SETUP-VMS`: `BIRTH` + remote `LOAD-DOE` VM-EXEC per child VM. Not immutable: setup sequence may grow |
|
||||
| 4061 | No | `PHASE1-DOE`: real per-VM `DOE-WORK` baseline touches. Historical note: dead `CD-WORK` (index-dispatch, deleted VM-HERA/HERMES/ARTEMIS constants) removed here 2026-07-08 |
|
||||
| 4062 | No | `CD-TICK`/`CD-DOE`: one real fleet-touch pass (`VM-EXEC "DOE-WORK"` on Hermes+Artemis), looped N times. `CAMPAIGN-STATUS`: `VM-PHYSICS-STATUS` + `VM-CONSERVED?` check. Not immutable: workload/status content may evolve |
|
||||
| 4060 | No | `SETUP-ARTEMIS`/`SETUP-VMS`: `BIRTH` + remote `LOAD-DOE` VM-EXEC on Artemis. Not immutable: setup sequence may grow |
|
||||
| 4061 | No | `PHASE1-DOE`: real per-VM `DOE-WORK` baseline touch, Artemis only. Historical note: dead `CD-WORK` (index-dispatch, deleted VM-HERA/HERMES/ARTEMIS constants) removed here 2026-07-08 |
|
||||
| 4062 | No | `CD-TICK`/`CD-DOE`: one real fleet-touch pass (`VM-EXEC "DOE-WORK"` on Artemis), looped N times. `CAMPAIGN-STATUS`: `VM-PHYSICS-STATUS` + `VM-CONSERVED?` check. Not immutable: workload/status content may evolve |
|
||||
| 4063 | No | `CAMPAIGN`: full campaign — baselines then 30 real fleet touches |
|
||||
| 4064 | No | `SMOKE-CAMPAIGN`: 1-rep seed-1959 smoke test + 16 real fleet touches |
|
||||
| 4065 | No | `THREE-VM-CAMPAIGN`: loads `doe.4th` locally on Hera, then 48 real fleet touches, all three VMs as Compudynamics peers |
|
||||
|
||||
### `hermes/init.4th` — Hermes VM (messenger)
|
||||
### `hermes/init.4th` — DELETED 2026-09-22 (formerly Hermes VM, the messenger)
|
||||
|
||||
Blocks: **4100–4109, 4114–4121, 4142–4153**
|
||||
**Deleted, FABRIC-3.6.md Phase 4 Stage E (task 4.1).** Formerly blocks
|
||||
**4100–4109, 4114–4121, 4142–4153** (gaps 4110–4113 and 4122–4141 were always
|
||||
Artemis's, hard-locked, never Hermes's — see the `artemis/init.4th` entry below,
|
||||
unaffected by this deletion). Every FORTH-owned message type this file's
|
||||
`messaging.4th` companion implemented (`BLK-ATTACH-EVENT`, `CONSOLE-CMD-EVENT`,
|
||||
`ELEVATE-REQUEST`) was cut over to kernel-Hermes first (Phase 3, tasks 3.8–3.10),
|
||||
verified live on all three architectures at each step; only then was the Hermes
|
||||
VM itself, this capsule, and `capsules/common/messaging.4th` removed. Full
|
||||
three-architecture acceptance for the deletion: `logs/20260922-181141/amd64/`,
|
||||
`logs/20260922-181753/aarch64/`, `logs/20260922-182136/riscv64/` — zero
|
||||
`UNKNOWN WORD`, identical `dict_hash` across all three. See FABRIC-3.6.md's own
|
||||
Phase 4 entry for the full account, including one found-not-fixed collateral
|
||||
defect (`SEND-ELEVATE-REQUEST`, this file's own former caller into
|
||||
`zuse-eligibility.4th`'s `ELEVATE-GRANT`, is now unreachable — Captain Bob's
|
||||
2026-09-22 decision: leave it, Phase 8 PKI will build its own entrypoint).
|
||||
Former blocks 4100–4153 (ex-Artemis-gaps) are now UNASSIGNED — see Unassigned
|
||||
Ranges below. The per-block justification table this section used to carry
|
||||
(arena allocator ABI, message/channel field accessors, the VM-name routing
|
||||
table, channel negotiation, broadcast) is not reproduced here; it is historical
|
||||
detail about deleted code, recoverable from git history (`git show
|
||||
<pre-strip-commit>:capsules/hermes/init.4th`) if ever needed again.
|
||||
|
||||
Gap 4110–4113 = Artemis. **HARD LOCKED. Never touch from Hermes side.**
|
||||
Gap 4122–4141 = Artemis (extended — see below). Never touch from Hermes side.
|
||||
|
||||
| Block | Immutable | Justification |
|
||||
|-------|-----------|---------------|
|
||||
| 4100 | Yes | Hermes constants: event codes, channel states, node sizes, arena sizes, MSG-DELIVERED. Immutable: changing node size constants corrupts all arena math |
|
||||
| 4101 | Yes | Arena CREATE: MSG-ARENA CH-ARENA MBR-ARENA; free-list roots; MSG-SEQ CH-ACTIVE. Immutable: CREATE allocates memory at compile time; re-running changes VM memory layout |
|
||||
| 4102 | Yes | MSG-INIT-FREE + CH-INIT-FREE. Immutable: free-list init is called once from CD-INIT; structure change breaks allocator |
|
||||
| 4103 | Yes | MBR-INIT-FREE + MSG-ALLOC + MSG-FREE-NODE. Immutable: allocator ABI; MSG-ALLOC is called by every MSG-SEND |
|
||||
| 4104 | Yes | CH-ALLOC + CH-FREE-NODE + MBR-ALLOC + MBR-FREE-NODE. Immutable: same allocator ABI constraint |
|
||||
| 4105 | Yes | Message field accessors (MSG-TYPE@/! through MSG-SEQ@/!). Immutable: field offsets are load-bearing; changing breaks every accessor caller |
|
||||
| 4106 | Yes | Channel + member accessors (CH-ID@/! through MBR-VM@). Immutable: same field offset constraint |
|
||||
| 4107 | No | VARIABLE MSG-LAST-MSG + IDX>NAME + MSG-DELIVER. Not fully immutable: MSG-DELIVER may evolve for async model. IDX>NAME now a table lookup over VM-NAME-ADDRS/LENS (see 4142) |
|
||||
| 4108 | No | MSG-SEND. Not immutable: send protocol may evolve |
|
||||
| 4109 | No | MSG-REAP. Ordering bug fixed (`8ca0cda2`) — type cleared before free-node prepend |
|
||||
| — | — | 4110–4113: ARTEMIS. NEVER TOUCH. |
|
||||
| 4114 | No | VARIABLE CH-SCAN + CH-COOL-ALL + CH-TOTAL-HEAT. Not immutable: channel cooling may evolve |
|
||||
| 4115 | No | CH-REAP-SAFE. Not immutable: channel reaping logic may evolve |
|
||||
| 4116 | No | VARIABLE COMMON-CH + COMMON-INIT + HERMES-TICK. Not immutable: HERMES-TICK drives the event loop; may grow |
|
||||
| 4117 | No | EVENT-EMIT + EVENT-WAIT + EVENT-DRAIN (backward compat). Not immutable: EVENT-WAIT is a diagnostic peek only per HERMES.md |
|
||||
| — | — | 4118: reserved, currently empty (former HERA-NOTIFY-SPAWN/KILL, removed `9323f776` — notification is automatic via `vm_physics_init`/`retire`) |
|
||||
| 4119 | No | CH-MINT-ID + CH-REQUEST. Not immutable: channel negotiation protocol may expand (multi-party invite deferred) |
|
||||
| 4120 | No | CD-INIT. Not immutable: init sequence may grow |
|
||||
| 4121 | No | MSG-ACK-LAST + MSG-NACK-LAST. Not immutable: NACK-requeue (reduced heat, retry) deferred; block may need extension |
|
||||
| — | — | 4122–4141: ARTEMIS (extended). NEVER TOUCH. |
|
||||
| 4142 | No | VM name routing table: VM-NAME-ADDRS/LENS + VM-NAME-REG + VM-NAMES-INIT. Not immutable: table-driven, grows via VM-MAX |
|
||||
| 4143 | Yes | MSG-CH@/! + MSG-ORIG-TYPE@/! + MSG-NACKED. Immutable: field offset constraint, same as 4105 |
|
||||
| 4144 | No | MSG-SEND. Not immutable: send protocol may evolve |
|
||||
| 4145 | No | MSG-TOTAL-HEAT + MSG-REDELIVER-NACKED. Not immutable: cooling/redelivery logic may evolve |
|
||||
| 4146 | No | MSG-DELIVER-ALL. Not immutable: delivery logic may evolve |
|
||||
| 4147 | No | HERMES-K + WELCOME. Not immutable: may grow |
|
||||
| 4148 | No | CH-ACCEPT + CH-CONFIRM + CH-CLOSE. Not immutable: channel negotiation protocol may expand |
|
||||
| 4149 | No | HERMES-STATUS (MSG-USED + CH-USED). Not immutable: status may expand |
|
||||
| 4150 | No | Member management: MBR-NEXT!/VM! + CH-ADD-MBR + HERMES-MSG-TEST. Not immutable: may grow |
|
||||
| 4151 | No | Phase 2 real multi-member broadcast: MSG-BROADCAST + BC-* variables. Not immutable: broadcast protocol may expand |
|
||||
| 4152 | No | Phase 2 COMMON-CH membership + test: BCAST-GOT/BCAST-RECV/REGISTER-COMMON-MEMBERS/SEND-BROADCAST-TEST. Not immutable: test harness may grow |
|
||||
| 4153 | No | `LOAD-DOE` ( -- ): `S" doe.4th" EXEC`. Added 2026-07-08 — `doe-campaign.4th`'s `SETUP-HERMES` calls this remotely so Hermes gets `DOE-WORK`; was missing entirely before this pass, so `doe-campaign.4th` could never have worked even pre-dating the VM-fleet-physics redesign. Not immutable: trivial wrapper |
|
||||
|
||||
**2026-07-05 collision fix:** blocks 4142–4150 were previously numbered
|
||||
4122–4131 (interleaved with the blocks below them in this list), directly
|
||||
colliding with Artemis's real block range. The collision was introduced
|
||||
`fca7b09a` (Jul 1) when the VM-name routing table was added without
|
||||
checking MANIFEST.md, and went undetected for 4 days because `mkcapsule`'s
|
||||
build path doesn't run conflict detection (only `--manifest` does). Found
|
||||
via `mkcapsule --manifest` while syncing this document; renumbered into
|
||||
genuinely free space and reverified with a clean amd64 TRIPOD-TEST boot
|
||||
(`PASS: fleet K`, `PASS: Hermes liveness`, `PASS: Artemis ready`, `K soak`,
|
||||
`PASS: E2E msg flow` — Hermes's kill/rebirth soak test reproduced an
|
||||
identical `dict_hash` before and after, confirming the reload path is now
|
||||
clean). amd64-only per the rev-f iteration rule; three-arch not yet run for
|
||||
this fix. See Conflict Register entry C5.
|
||||
**2026-07-05 collision fix (historical, still relevant to Conflict Register
|
||||
C5 below):** blocks 4142–4150 were previously numbered 4122–4131, directly
|
||||
colliding with Artemis's real block range, introduced `fca7b09a` (Jul 1),
|
||||
found and resolved via `mkcapsule --manifest`. See Conflict Register entry C5.
|
||||
|
||||
### `artemis/init.4th` — Artemis VM (flat pool disk manager v2)
|
||||
|
||||
@@ -439,14 +408,19 @@ Hermes (see above). The table below reflects the actual current footprint.
|
||||
| 4851 | No | ART-PING ( -- ): cheap O(1) touch target for Phase 3 fleet DoE workload, unlike ART-TICK/ART-STATUS which scan all ART-DATA-BLKS. Not immutable: may extend |
|
||||
| 4852 | No | `LOAD-DOE` ( -- ): `S" doe.4th" EXEC`. Added 2026-07-08 — same missing-word fix as `hermes/init.4th` 4153; `doe-campaign.4th`'s `SETUP-ARTEMIS` calls this remotely. Not immutable: trivial wrapper |
|
||||
|
||||
### `process.4th` — VM process management
|
||||
### `hestia/init.4th` — Hestia VM (third Tripod leg, bind point)
|
||||
|
||||
Blocks: **4300–4301**
|
||||
Blocks: **4986–4988** (allocated 4986–4996, FABRIC-3.6.md task 1.1; 3 of the 11 used so far)
|
||||
|
||||
Added 2026-09-19 (FABRIC-3.6.md tasks 1.2/1.4/1.6/1.7, Tripod/kernel reshuffle — see
|
||||
FABRIC-3.5.md §II/§IV). Reconstitutes the Tripod as Hera/Artemis/Hestia. Birthed (task 1.4)
|
||||
and owns the drawing fabric (`fabric.4th`/`font.4th`, tasks 1.6/1.7 — see block 4988).
|
||||
|
||||
| Block | Immutable | Justification |
|
||||
|-------|-----------|---------------|
|
||||
| 4300 | No | Event code constants + SPAWN + PAUSE lifecycle operators. Not immutable: process model may evolve |
|
||||
| 4301 | No | RESUME + KILL-VM. Not immutable: process model may evolve |
|
||||
| 4986 | No | `WELCOME` banner word + call. Not immutable: message text may change |
|
||||
| 4988 | No | `S" fabric.4th" EXEC` + `S" font.4th" EXEC` — moved here from `init.4th` (Hera), tasks 1.6/1.7, **merged into one commit rather than done separately as the punchlist originally split them**: `font.4th` calls `G-LINE`/`G-ELLIPSE`, which are `fabric.4th`'s own words, so moving one without the other strands the dependency in whichever VM keeps only half — confirmed live, moving `fabric.4th` alone floods Hera's boot with `UNKNOWN WORD: 'G-LINE'`. Order preserved (`fabric.4th` before `font.4th`), matching `init.4th`'s own prior load order. Not immutable: may grow if more of the drawing/font surface relocates here |
|
||||
| 4987 | No | **Rewritten 2026-09-22, FABRIC-3.6.md Phase 4 Stage E.** Originally joined `COMMON-CH` on slot 11 (Hera/Hermes/Artemis held 0/1/2, identities held 3–10, `messaging.4th:78-85`) — that whole `COMMON-CH` concept no longer exists, `messaging.4th` is deleted. Now just `S" lib.4th" EXEC` + `LOG-INFO" Hestia: ready"` + `STARTUP-BANNER`. Not immutable: startup sequence may grow |
|
||||
|
||||
### Deleted capsules (historical)
|
||||
|
||||
@@ -458,6 +432,31 @@ generalized VM fleet physics mechanism (`capsule_vm_physics.c`/`.h`,
|
||||
and `docs/working/archive/session-logs/2026-07-05-worklog.md`. Their
|
||||
former ranges are now UNASSIGNED (see Namespace Map).
|
||||
|
||||
`common/msg.4th` (formerly 4055) and `process.4th` (formerly 4300–4301)
|
||||
were deleted 2026-09-19 (FABRIC-3.6.md tasks 0.2/0.3, part of the
|
||||
Tripod/kernel reshuffle's Category A surgical strip — see FABRIC-3.5.md
|
||||
§XXII). Both were confirmed dead by all three of §XXII.2's reachability
|
||||
routes: zero `EXEC` sites in any boot-loaded capsule, and zero callers
|
||||
anywhere of their exported words (`HERMES-ACK`/`HERMES-NACK` for the
|
||||
former; `SPAWN`/`PAUSE`/`RESUME`/`KILL-VM` for the latter).
|
||||
`common/msg.4th`'s block-4055 entry had claimed it was an immutable ABI
|
||||
"every messaging VM loads at birth" — `FABRIC-2.md:2773` had already
|
||||
flagged that claim as stale before this correction. `process.4th`'s
|
||||
removal also took `messaging.4th`'s `EVENT-EMIT`/`EVENT-WAIT`/
|
||||
`EVENT-DRAIN` (former block 5030) with it, its only live caller. Their
|
||||
former ranges are now UNASSIGNED (see Namespace Map).
|
||||
|
||||
`hermes/init.4th` (formerly 4100–4109, 4114–4121, 4142–4153) and
|
||||
`common/messaging.4th` were deleted 2026-09-22 (FABRIC-3.6.md Phase 4 Stage E,
|
||||
tasks 4.1/4.3), the final step of the Tripod/kernel reshuffle — the Hermes VM
|
||||
itself and its FORTH messaging layer, superseded by kernel-Hermes
|
||||
(`src/starkernel/vm/kernel_hermes.c`) after every live message type was cut
|
||||
over in Phase 3 (see the `hermes/init.4th` entry above for the full account).
|
||||
Their former ranges are now UNASSIGNED (see Namespace Map). This is the same
|
||||
"cut over first, delete after, one commit per Category B item, verified on all
|
||||
three architectures" discipline `compudynamics.4th`/`fleet-k.4th` and
|
||||
`common/msg.4th`/`process.4th` were deleted under above.
|
||||
|
||||
---
|
||||
|
||||
## Unassigned Ranges
|
||||
@@ -476,14 +475,16 @@ former ranges are now UNASSIGNED (see Namespace Map).
|
||||
| 4008–4009 | UNASSIGNED | ACL extension space |
|
||||
| 4019–4049 | UNASSIGNED | ACL / zuse extension space |
|
||||
| 4051–4054 | UNASSIGNED | lib.4th extension space |
|
||||
| 4056–4059 | UNASSIGNED | common:msg extension space |
|
||||
| 4055–4059 | UNASSIGNED | Former `common/msg.4th` range (deleted 2026-09-19, FABRIC-3.6.md task 0.2) |
|
||||
| 4066–4099 | UNASSIGNED | doe-campaign extension space |
|
||||
| 4154–4199 | UNASSIGNED | Hermes extension space (post-4153) |
|
||||
| 4100–4153 | UNASSIGNED | Former `hermes/init.4th` range (deleted 2026-09-22, FABRIC-3.6.md Phase 4 Stage E task 4.1). Excludes 4110–4113/4122–4141, which were always Artemis's, hard-locked, unaffected |
|
||||
| 4154–4199 | UNASSIGNED | Hermes extension space (post-4153), now moot along with 4100–4153 above |
|
||||
| 4200–4299 | UNASSIGNED | Former compudynamics.4th range (deleted `9323f776`) |
|
||||
| 4302–4399 | UNASSIGNED | process extension space |
|
||||
| 4300–4399 | UNASSIGNED | Former `process.4th` range (deleted 2026-09-19, FABRIC-3.6.md task 0.3) |
|
||||
| 4400–4405 | UNASSIGNED | Former fleet-k.4th range (deleted `9323f776`) |
|
||||
| 4410–4414, 4420–4424, 4430–4434 | UNASSIGNED | workload-1.4th's private zone, room for growth |
|
||||
| 4853+ | OPEN | Future capsules — claim here first |
|
||||
| 4986–4996 | CLAIMED | `hestia/init.4th`, allocated 2026-09-19 (FABRIC-3.6.md task 1.1, Tripod/kernel reshuffle) — file not yet created (task 1.2). Sits between `font.4th`'s last block (4985) and the console proxy's hardcoded `Block 4997` (`capsule_console.c:27-29`) — chosen specifically to avoid 4997, per §XVIII.4 |
|
||||
| 4853–4985, 4998+ | OPEN | Future capsules — claim here first (excludes 4900–4985, already `fabric.4th`/`font.4th`, and 4986–4996, now claimed above) |
|
||||
|
||||
---
|
||||
|
||||
@@ -536,3 +537,15 @@ before this doc pass. Not re-verified this pass: whether every other
|
||||
still-unflagged row elsewhere in this document matches source exactly —
|
||||
this was a targeted correction of the rows touched by recent work plus
|
||||
`doe.4th`/`ACL.4th`, not a full line-by-line re-audit of all ~250 blocks.*
|
||||
|
||||
*Doc-debt pass, 2026-09-22 — MANIFEST.md updated to ride the Tripod/kernel
|
||||
reshuffle's Category B strip (FABRIC-3.6.md Phase 4 Stage E, task 5.2 close-out
|
||||
sweep, §XXII.5). `hermes/init.4th`'s block-by-block table replaced with a
|
||||
deletion note (git history retains the detail); `common/messaging.4th`'s
|
||||
deletion recorded in "Deleted capsules"; `init.4th` block 2049,
|
||||
`doe-campaign.4th` blocks 4060–4062, and `hestia/init.4th` block 4987 updated
|
||||
to match the post-strip live files (verified by direct read, not by this
|
||||
document's own prior claims, same discipline as every other pass here).
|
||||
Former Hermes range (4100–4153, excluding Artemis's hard-locked 4110–4113/
|
||||
4122–4141) marked UNASSIGNED. Not re-verified this pass: the ~230 other rows
|
||||
this strip didn't touch.*
|
||||
|
||||
@@ -492,13 +492,9 @@ Block 4853
|
||||
CD-INIT
|
||||
S" lib.4th" EXEC
|
||||
Block 4854
|
||||
( Phase C 2026-08-28: own messaging vocab + subscribe into )
|
||||
( Hermes's canonical COMMON-CH (idx 2 = Artemis, VM-NAMES- )
|
||||
( INIT). Hermes always exists by Artemis's birth. )
|
||||
S" common:messaging.4th" EXEC
|
||||
MSG-CD-INIT
|
||||
2 MY-CH-ID !
|
||||
S" 2 COMMON-CH @ CH-ADD-MBR" S" Hermes" VM-EXEC
|
||||
( FABRIC-3.6.md Phase 4, Stage E: common:messaging.4th/ )
|
||||
( MSG-CD-INIT/MY-CH-ID/COMMON-CH subscription all removed )
|
||||
( -- Hermes, the channel, and the words are all gone. )
|
||||
STARTUP-BANNER
|
||||
|
||||
Block 4856
|
||||
@@ -525,8 +521,9 @@ Block 4857
|
||||
Block 4858
|
||||
( HERA-BLK-ATTACH-REQ ( dev-addr -- ): VM-EXEC'd by MSG- )
|
||||
( DELIVER when Hera's request arrives. Runs BLK-ATTACH on )
|
||||
( Artemis's own dictionary (real ACL gating, same as any )
|
||||
( other message payload), then replies to Hera (idx 0). )
|
||||
( Artemis's own dictionary, then replies to Hera via kernel- )
|
||||
( Hermes's KH-BLK-ATTACH-SEND (repl.c) -- FABRIC-3.6.md task )
|
||||
( 3.8 Stage C, FORTH Hermes no longer sees this message type. )
|
||||
: HERA-BLK-ATTACH-REQ ( dev-addr -- )
|
||||
0 ATTACH-ACK-LEN !
|
||||
DUP ACK-APPEND-NUM
|
||||
@@ -535,8 +532,7 @@ Block 4858
|
||||
DROP
|
||||
BL ACK-APPEND-CHAR
|
||||
S" BLK-ATTACH-ACK" ACK-APPEND
|
||||
BLK-ATTACH-EVENT 2 0 ATTACH-ACK-BUF
|
||||
ATTACH-ACK-LEN @ 0 MSG-SEND ;
|
||||
ATTACH-ACK-BUF ATTACH-ACK-LEN @ KH-BLK-ATTACH-SEND DROP ;
|
||||
|
||||
Block 4860
|
||||
( LOG-APPEND ( level ts msg-addr msg-u -- ) )
|
||||
|
||||
@@ -1,504 +0,0 @@
|
||||
Block 5003
|
||||
( common:messaging.4th -- generic per-VM messaging vocab. )
|
||||
1 CONSTANT SPAWN-EVENT
|
||||
2 CONSTANT PAUSE-EVENT
|
||||
3 CONSTANT RESUME-EVENT
|
||||
4 CONSTANT KILL-EVENT
|
||||
0 CONSTANT CH-NEGOTIATING
|
||||
1 CONSTANT CH-OPEN
|
||||
2 CONSTANT CH-CLOSING
|
||||
9 CONSTANT MSG-CELLS
|
||||
6 CONSTANT CH-CELLS
|
||||
2 CONSTANT MBR-CELLS
|
||||
32 CONSTANT MSG-MAX
|
||||
16 CONSTANT CH-MAX
|
||||
64 CONSTANT MBR-MAX
|
||||
65208 CONSTANT Q-DECAY
|
||||
255 CONSTANT MSG-DELIVERED
|
||||
Block 5038
|
||||
( CONSOLE-CMD-EVENT: console-VM -> paired user-VM command )
|
||||
( relay message type, async MSG-SEND/MSG-DELIVER. )
|
||||
7 CONSTANT CONSOLE-CMD-EVENT
|
||||
( ELEVATE-REQUEST: H.5/H.8 -- word-ACL elevation ask, )
|
||||
( carried to Zuse via CH-REQUEST. See SEND-ELEVATE- )
|
||||
( REQUEST/ELEVATE-GRANT below and in zuse-eligibility.4th. )
|
||||
8 CONSTANT ELEVATE-REQUEST
|
||||
Block 5041
|
||||
( BLK-ATTACH-EVENT: Hera -> Artemis storage-attach request, )
|
||||
( and Artemis -> Hera ack. Payload is FORTH text, executed )
|
||||
( by the receiver, same as every other message here -- see )
|
||||
( HERA-BLK-ATTACH-REQ (artemis:init.4th) and BLK-ATTACH-ACK )
|
||||
( (repl.c). Messaging-migration decision, Bob, 2026-09-07: )
|
||||
( Hera keeps polling/sig-checking; the storage-registration )
|
||||
( step (blk_subsys_attach_device) moves to a real message )
|
||||
( instead of a direct C call, so identity birth only )
|
||||
( proceeds once Artemis confirms storage succeeded. )
|
||||
9 CONSTANT BLK-ATTACH-EVENT
|
||||
Block 5004
|
||||
( StadiumBehaviour tags, match stadium.h's enum )
|
||||
0 CONSTANT SB-MIGRATE
|
||||
1 CONSTANT SB-DELIVER
|
||||
2 CONSTANT SB-EXPIRE
|
||||
3 CONSTANT SB-COOL
|
||||
-1 CONSTANT STADIUM-NONE
|
||||
( per-item admission heat for MSG-SEND/CH-ACCEPT. )
|
||||
( Remaining reservoir after COMMON-CH's Q.1/3 floor, split )
|
||||
( evenly across MSG-MAX messages + non-COMMON CH-MAX-1 slots. )
|
||||
Q.1 Q.1 3 / - MSG-MAX CH-MAX 1- + / CONSTANT Q.SLOT
|
||||
Block 5005
|
||||
( arenas. heat/capacity via Stadium; MBR keeps its own )
|
||||
( free list (not part of heat economy). )
|
||||
CREATE MSG-ARENA MSG-MAX MSG-CELLS * CELLS ALLOT
|
||||
CREATE CH-ARENA CH-MAX CH-CELLS * CELLS ALLOT
|
||||
CREATE MBR-ARENA MBR-MAX MBR-CELLS * CELLS ALLOT
|
||||
VARIABLE MSG-ALLOC-SLOT
|
||||
VARIABLE CH-ALLOC-SLOT
|
||||
VARIABLE MBR-FREE-HEAD
|
||||
VARIABLE MSG-SEQ
|
||||
VARIABLE CH-ACTIVE
|
||||
VARIABLE COMMON-CH
|
||||
VARIABLE MY-CH-ID
|
||||
Block 5006
|
||||
( VM routing table. SXIX: 16 slots, not 8 -- see FABRIC-3.md. )
|
||||
( Hera/Hermes/Artemis stay 0/1/2 (artemis:init.4th depends on )
|
||||
( it); identities get NEW slots 3-10, never renumbered. )
|
||||
16 CONSTANT VM-MAX
|
||||
CREATE VM-NAME-ADDRS VM-MAX CELLS ALLOT
|
||||
CREATE VM-NAME-LENS VM-MAX CELLS ALLOT
|
||||
: VM-NAME-REG ( addr u idx -- )
|
||||
DUP VM-MAX >= IF DROP 2DROP EXIT THEN
|
||||
>R R@ CELLS VM-NAME-LENS + !
|
||||
R> CELLS VM-NAME-ADDRS + ! ;
|
||||
Block 5042
|
||||
( doe-idx (std79-doe.fth ID-NAME) -> msg-idx: 1..8 -> 3..10. )
|
||||
: DOE-IDX>MSG-IDX ( doe-idx -- msg-idx ) 2 + ;
|
||||
: VM-NAMES-INIT ( -- )
|
||||
S" Hera" 0 VM-NAME-REG
|
||||
S" Hermes" 1 VM-NAME-REG
|
||||
S" Artemis" 2 VM-NAME-REG
|
||||
S" rajames" 1 DOE-IDX>MSG-IDX VM-NAME-REG
|
||||
S" 00" 2 DOE-IDX>MSG-IDX VM-NAME-REG
|
||||
S" 01" 3 DOE-IDX>MSG-IDX VM-NAME-REG
|
||||
S" 02" 4 DOE-IDX>MSG-IDX VM-NAME-REG
|
||||
S" 03" 5 DOE-IDX>MSG-IDX VM-NAME-REG
|
||||
S" 04" 6 DOE-IDX>MSG-IDX VM-NAME-REG
|
||||
S" 05" 7 DOE-IDX>MSG-IDX VM-NAME-REG
|
||||
S" 06" 8 DOE-IDX>MSG-IDX VM-NAME-REG ;
|
||||
Block 5007
|
||||
( INIT-FREE. stamps STADIUM-NONE into each slot's stadium- )
|
||||
( cell field (msg off 5, ch off 3). Raw offsets: accessors )
|
||||
( aren't defined yet in file order. )
|
||||
: MSG-INIT-FREE ( -- )
|
||||
MSG-MAX 0 DO
|
||||
STADIUM-NONE I MSG-CELLS * CELLS MSG-ARENA + 5 CELLS + !
|
||||
LOOP ;
|
||||
: CH-INIT-FREE ( -- )
|
||||
CH-MAX 0 DO
|
||||
STADIUM-NONE I CH-CELLS * CELLS CH-ARENA + 3 CELLS + !
|
||||
LOOP ;
|
||||
Block 5008
|
||||
( MBR-INIT-FREE (member free list, untouched) )
|
||||
: MBR-INIT-FREE ( -- )
|
||||
MBR-MAX 1- 0 DO
|
||||
I MBR-CELLS * CELLS MBR-ARENA +
|
||||
I 1+ MBR-CELLS * CELLS MBR-ARENA + SWAP !
|
||||
LOOP
|
||||
0 MBR-MAX 1- MBR-CELLS * CELLS MBR-ARENA + !
|
||||
MBR-ARENA MBR-FREE-HEAD ! ;
|
||||
Block 5009
|
||||
( MSG-ALLOC: finds a free slot (TYPE=0) first, admits into )
|
||||
( Stadium only once confirmed free (no leak). )
|
||||
: MSG-FIND-FREE-SLOT ( -- addr|0 )
|
||||
MSG-ARENA MSG-MAX 0 DO
|
||||
DUP @ 0= IF UNLOOP EXIT THEN
|
||||
MSG-CELLS CELLS +
|
||||
LOOP DROP 0 ;
|
||||
Block 5010
|
||||
( MSG-ALLOC: pulls heat from reservoir first, admits )
|
||||
( (identity=idx, heat=pulled), rolls back on refusal. )
|
||||
: MSG-ALLOC ( heat -- addr|0 )
|
||||
MSG-FIND-FREE-SLOT DUP 0= IF SWAP DROP EXIT THEN
|
||||
MSG-ALLOC-SLOT !
|
||||
STADIUM-RES-PULL
|
||||
MSG-ALLOC-SLOT @ MSG-ARENA - MSG-CELLS CELLS /
|
||||
SWAP DUP >R
|
||||
SB-DELIVER STADIUM-ADMIT
|
||||
DUP STADIUM-NONE = IF
|
||||
DROP R> STADIUM-RES-PUSH 0 EXIT
|
||||
THEN
|
||||
R> DROP
|
||||
MSG-ALLOC-SLOT @ MSG-CELLS CELLS 0 FILL
|
||||
MSG-ALLOC-SLOT @ 5 CELLS + !
|
||||
MSG-ALLOC-SLOT @ ;
|
||||
Block 5011
|
||||
( MSG-FREE-NODE: evict from Stadium, clear field )
|
||||
: MSG-FREE-NODE ( addr -- )
|
||||
DUP 5 CELLS + @ STADIUM-EVICT DROP
|
||||
DUP 5 CELLS + STADIUM-NONE SWAP !
|
||||
DROP ;
|
||||
Block 5012
|
||||
( MBR alloc/free (unchanged; not heat economy) )
|
||||
: MBR-ALLOC ( -- addr|0 )
|
||||
MBR-FREE-HEAD @ DUP 0= IF EXIT THEN
|
||||
DUP @ MBR-FREE-HEAD !
|
||||
DUP MBR-CELLS CELLS 0 FILL ;
|
||||
: MBR-FREE-NODE ( addr -- )
|
||||
MBR-FREE-HEAD @ OVER ! MBR-FREE-HEAD ! ;
|
||||
Block 5013
|
||||
( CH-FIND-FREE-SLOT. No TYPE field, so freeness is )
|
||||
( stadium-cell = STADIUM-NONE. CH-ALLOC -> block 4176. )
|
||||
: CH-FIND-FREE-SLOT ( -- addr|0 )
|
||||
CH-ARENA CH-MAX 0 DO
|
||||
DUP 3 CELLS + @ STADIUM-NONE = IF UNLOOP EXIT THEN
|
||||
CH-CELLS CELLS +
|
||||
LOOP DROP 0 ;
|
||||
Block 5014
|
||||
( CH-ALLOC: pulls heat from reservoir first, admits )
|
||||
( (identity=idx, heat=pulled), rolls back on refusal. )
|
||||
: CH-ALLOC ( heat -- addr|0 )
|
||||
CH-FIND-FREE-SLOT DUP 0= IF SWAP DROP EXIT THEN
|
||||
CH-ALLOC-SLOT !
|
||||
STADIUM-RES-PULL
|
||||
CH-ALLOC-SLOT @ CH-ARENA - CH-CELLS CELLS /
|
||||
SWAP DUP >R
|
||||
SB-COOL STADIUM-ADMIT
|
||||
DUP STADIUM-NONE = IF
|
||||
DROP R> STADIUM-RES-PUSH 0 EXIT
|
||||
THEN
|
||||
R> DROP
|
||||
CH-ALLOC-SLOT @ CH-CELLS CELLS 0 FILL
|
||||
CH-ALLOC-SLOT @ 3 CELLS + !
|
||||
CH-ALLOC-SLOT @ ;
|
||||
Block 5015
|
||||
( CH-FREE-NODE: evict from Stadium, clear field )
|
||||
: CH-FREE-NODE ( addr -- )
|
||||
DUP 3 CELLS + @ STADIUM-EVICT DROP
|
||||
DUP 3 CELLS + STADIUM-NONE SWAP !
|
||||
DROP ;
|
||||
Block 5016
|
||||
( message field accessors )
|
||||
: MSG-TYPE@ ( m -- n ) @ ;
|
||||
: MSG-TYPE! ( n m -- ) ! ;
|
||||
: MSG-FROM@ ( m -- n ) 1 CELLS + @ ;
|
||||
: MSG-FROM! ( n m -- ) 1 CELLS + ! ;
|
||||
: MSG-TO@ ( m -- n ) 2 CELLS + @ ;
|
||||
: MSG-TO! ( n m -- ) 2 CELLS + ! ;
|
||||
: MSG-PADDR@ ( m -- a ) 3 CELLS + @ ;
|
||||
: MSG-PADDR! ( a m -- ) 3 CELLS + ! ;
|
||||
: MSG-PLEN@ ( m -- u ) 4 CELLS + @ ;
|
||||
: MSG-PLEN! ( u m -- ) 4 CELLS + ! ;
|
||||
( offset 5 = Stadium cell idx. MSG-HEAT@/! -> 4154. )
|
||||
: MSG-STADIUM-CELL@ ( m -- cell ) 5 CELLS + @ ;
|
||||
: MSG-STADIUM-CELL! ( cell m -- ) 5 CELLS + ! ;
|
||||
: MSG-SEQ@ ( m -- n ) 6 CELLS + @ ;
|
||||
: MSG-SEQ! ( n m -- ) 6 CELLS + ! ;
|
||||
Block 5017
|
||||
( message accessors: CH@/CH! )
|
||||
: MSG-CH@ ( m -- c ) 7 CELLS + @ ;
|
||||
: MSG-CH! ( c m -- ) 7 CELLS + ! ;
|
||||
: MSG-ORIG-TYPE@ ( m -- n ) 8 CELLS + @ ;
|
||||
: MSG-ORIG-TYPE! ( n m -- ) 8 CELLS + ! ;
|
||||
253 CONSTANT MSG-NACKED
|
||||
Block 5018
|
||||
( channel + member accessors )
|
||||
: CH-ID@ ( c -- n ) @ ;
|
||||
: CH-ID! ( n c -- ) ! ;
|
||||
: CH-OWNER@ ( c -- n ) 1 CELLS + @ ;
|
||||
: CH-OWNER! ( n c -- ) 1 CELLS + ! ;
|
||||
: CH-STATE@ ( c -- n ) 2 CELLS + @ ;
|
||||
: CH-STATE! ( n c -- ) 2 CELLS + ! ;
|
||||
( offset 3 = Stadium cell index. CH-HEAT@/! -> 4154. )
|
||||
: CH-STADIUM-CELL@ ( c -- cell ) 3 CELLS + @ ;
|
||||
: CH-STADIUM-CELL! ( cell c -- ) 3 CELLS + ! ;
|
||||
: CH-MBRS@ ( c -- a ) 4 CELLS + @ ;
|
||||
: CH-MBRS! ( a c -- ) 4 CELLS + ! ;
|
||||
: CH-NEXT@ ( c -- a ) 5 CELLS + @ ;
|
||||
: CH-NEXT! ( a c -- ) 5 CELLS + ! ;
|
||||
: MBR-NEXT@ ( m -- a ) @ ;
|
||||
: MBR-VM@ ( m -- n ) 1 CELLS + @ ;
|
||||
Block 5019
|
||||
( composed MSG/CH-HEAT@/!, same names/stacks, bodies )
|
||||
( routed via Stadium. Callers need no changes. )
|
||||
: MSG-HEAT@ ( m -- q ) MSG-STADIUM-CELL@ STADIUM-HEAT@ ;
|
||||
: MSG-HEAT! ( q m -- ) MSG-STADIUM-CELL@ STADIUM-HEAT! ;
|
||||
: CH-HEAT@ ( c -- q ) CH-STADIUM-CELL@ STADIUM-HEAT@ ;
|
||||
: CH-HEAT! ( q c -- ) CH-STADIUM-CELL@ STADIUM-HEAT! ;
|
||||
Block 5020
|
||||
( deliver )
|
||||
VARIABLE MSG-LAST-MSG
|
||||
VARIABLE MSG-SEND-TO-IDX
|
||||
: IDX>NAME ( idx -- addr u )
|
||||
DUP VM-MAX >= IF DROP 0 0 EXIT THEN
|
||||
DUP CELLS VM-NAME-ADDRS + @
|
||||
SWAP CELLS VM-NAME-LENS + @ ;
|
||||
: MSG-DELIVER ( m -- )
|
||||
DUP MSG-LAST-MSG !
|
||||
DUP MSG-PADDR@ OVER MSG-PLEN@
|
||||
ROT MSG-TO@ IDX>NAME VM-EXEC ;
|
||||
Block 5021
|
||||
( MSG-SEND. heat -> MSG-ALLOC's admission directly )
|
||||
( (zero-heat would lose eviction-fallback density )
|
||||
( comparisons), not set afterward as before. )
|
||||
: MSG-SEND ( type from to paddr plen ch -- )
|
||||
3 PICK MSG-SEND-TO-IDX !
|
||||
Q.SLOT MSG-ALLOC DUP 0= IF 2DROP 2DROP 2DROP DROP EXIT THEN
|
||||
>R
|
||||
MSG-SEQ @ 1+ DUP MSG-SEQ ! R@ MSG-SEQ!
|
||||
R@ MSG-CH!
|
||||
R@ MSG-PLEN! R@ MSG-PADDR!
|
||||
R@ MSG-TO! R@ MSG-FROM! DUP R@ MSG-TYPE! R@ MSG-ORIG-TYPE!
|
||||
R> DROP
|
||||
MSG-SEND-TO-IDX @ IDX>NAME SWITCH-MARK-WORK ;
|
||||
Block 5022
|
||||
( MSG-COOL-ONE/ALL: linear decay per tick )
|
||||
VARIABLE MSG-SCAN
|
||||
: MSG-COOL-ONE ( m -- )
|
||||
DUP MSG-HEAT@ Q-DECAY Q.* SWAP MSG-HEAT! ;
|
||||
: MSG-COOL-ALL ( -- )
|
||||
MSG-ARENA MSG-SCAN !
|
||||
MSG-MAX 0 DO
|
||||
MSG-SCAN @ MSG-HEAT@ 0 > IF
|
||||
MSG-SCAN @ MSG-COOL-ONE
|
||||
THEN
|
||||
MSG-SCAN @ MSG-CELLS CELLS + MSG-SCAN !
|
||||
LOOP ;
|
||||
Block 5023
|
||||
( MSG-TOTAL-HEAT )
|
||||
: MSG-TOTAL-HEAT ( -- q48 )
|
||||
0 MSG-ARENA MSG-SCAN !
|
||||
MSG-MAX 0 DO
|
||||
MSG-SCAN @ MSG-TYPE@ 0 <> IF
|
||||
MSG-SCAN @ MSG-HEAT@ +
|
||||
THEN
|
||||
MSG-SCAN @ MSG-CELLS CELLS + MSG-SCAN !
|
||||
LOOP ;
|
||||
: MSG-REDELIVER-NACKED ( -- )
|
||||
MSG-ARENA MSG-SCAN !
|
||||
MSG-MAX 0 DO
|
||||
MSG-SCAN @ MSG-TYPE@ MSG-NACKED = IF
|
||||
MSG-SCAN @ MSG-ORIG-TYPE@ MSG-SCAN @ MSG-TYPE! THEN
|
||||
MSG-SCAN @ MSG-CELLS CELLS + MSG-SCAN !
|
||||
LOOP ;
|
||||
Block 5024
|
||||
( MSG-DELIVER-ALL )
|
||||
: MSG-DELIVER-ALL ( -- )
|
||||
MSG-ARENA MSG-SCAN !
|
||||
MSG-MAX 0 DO
|
||||
MSG-SCAN @ MSG-TYPE@ 0 <>
|
||||
MSG-SCAN @ MSG-TYPE@ MSG-DELIVERED <> AND
|
||||
MSG-SCAN @ MSG-TYPE@ MSG-NACKED <> AND IF
|
||||
MSG-SCAN @ MSG-DELIVER
|
||||
MSG-SCAN @ MSG-TYPE@ 0 <> IF
|
||||
MSG-DELIVERED MSG-SCAN @ MSG-TYPE!
|
||||
THEN
|
||||
THEN
|
||||
MSG-SCAN @ MSG-CELLS CELLS + MSG-SCAN !
|
||||
LOOP ;
|
||||
Block 5025
|
||||
( message reaping )
|
||||
( K reap only fires at heat=0: freed K contribution is 0. )
|
||||
( Force-reap not yet implemented. If added: explicit )
|
||||
( K redistribution will be required here. )
|
||||
: MSG-REAP ( -- )
|
||||
MSG-ARENA MSG-SCAN !
|
||||
MSG-MAX 0 DO
|
||||
MSG-SCAN @ MSG-HEAT@ 0 = IF
|
||||
MSG-SCAN @ MSG-TYPE@ 0 <> IF
|
||||
0 MSG-SCAN @ MSG-TYPE!
|
||||
MSG-SCAN @ MSG-FREE-NODE
|
||||
THEN
|
||||
THEN
|
||||
MSG-SCAN @ MSG-CELLS CELLS + MSG-SCAN !
|
||||
LOOP ;
|
||||
Block 5026
|
||||
( channel cooling + heat aggregate )
|
||||
VARIABLE CH-SCAN
|
||||
: CH-COOL-ALL ( -- )
|
||||
CH-ACTIVE @ CH-SCAN !
|
||||
BEGIN CH-SCAN @ 0 <> WHILE
|
||||
CH-SCAN @ CH-HEAT@ Q-DECAY Q.* CH-SCAN @ CH-HEAT!
|
||||
CH-SCAN @ CH-NEXT@ CH-SCAN !
|
||||
REPEAT ;
|
||||
: CH-TOTAL-HEAT ( -- q48 )
|
||||
0 CH-ACTIVE @ CH-SCAN !
|
||||
BEGIN CH-SCAN @ 0 <> WHILE
|
||||
CH-SCAN @ CH-HEAT@ +
|
||||
CH-SCAN @ CH-NEXT@ CH-SCAN !
|
||||
REPEAT ;
|
||||
Block 5027
|
||||
( channel reaping )
|
||||
: CH-REAP-SAFE ( -- )
|
||||
CH-ACTIVE @ CH-SCAN !
|
||||
0 CH-ACTIVE !
|
||||
BEGIN CH-SCAN @ 0 <> WHILE
|
||||
CH-SCAN @ CH-NEXT@
|
||||
CH-SCAN @ CH-HEAT@ 0 =
|
||||
CH-SCAN @ COMMON-CH @ <> AND IF
|
||||
CH-SCAN @ CH-FREE-NODE
|
||||
ELSE
|
||||
CH-ACTIVE @ CH-SCAN @ CH-NEXT!
|
||||
CH-SCAN @ CH-ACTIVE !
|
||||
THEN
|
||||
CH-SCAN !
|
||||
REPEAT ;
|
||||
Block 5028
|
||||
( COMMON + MSG-TICK (was HERMES-TICK -- generic now). )
|
||||
( floor=Q.1/3. COMMON-CH VARIABLE is in block 4101. )
|
||||
: COMMON-INIT ( -- )
|
||||
Q.1 3 / CH-ALLOC DUP COMMON-CH !
|
||||
0 OVER CH-ID! 0 OVER CH-OWNER!
|
||||
CH-OPEN OVER CH-STATE!
|
||||
0 OVER CH-MBRS!
|
||||
CH-ACTIVE @ OVER CH-NEXT!
|
||||
CH-ACTIVE ! ;
|
||||
( floor-refresh is reservoir-constrained, may no-op under )
|
||||
( pressure. )
|
||||
: MSG-TICK ( -- )
|
||||
MSG-DELIVER-ALL MSG-REDELIVER-NACKED
|
||||
MSG-COOL-ALL MSG-REAP
|
||||
CH-COOL-ALL CH-REAP-SAFE
|
||||
Q.1 3 / COMMON-CH @ CH-HEAT! ;
|
||||
Block 5029
|
||||
( MSG-K (was HERMES-K -- generic now). WELCOME stays )
|
||||
( behind in each VM's own init.4th. )
|
||||
: MSG-K ( -- q48 )
|
||||
MSG-TOTAL-HEAT CH-TOTAL-HEAT + STADIUM-RES@ +
|
||||
STADIUM-WORD-HEAT + ;
|
||||
Block 5030
|
||||
( event compat interface. SPAWN/KILL notify-Hera is )
|
||||
( automatic: see vm_physics_init/retire in mama_word_birth. )
|
||||
: EVENT-EMIT ( type -- ) DROP ;
|
||||
: EVENT-WAIT ( -- type )
|
||||
MSG-ARENA MSG-SCAN !
|
||||
MSG-MAX 0 DO
|
||||
MSG-SCAN @ MSG-TYPE@ 0 <> IF
|
||||
MSG-SCAN @ MSG-TYPE@ UNLOOP EXIT THEN
|
||||
MSG-SCAN @ MSG-CELLS CELLS + MSG-SCAN !
|
||||
LOOP 0 ;
|
||||
: EVENT-DRAIN ( -- )
|
||||
( no-op: MSG-REAP owns cleanup; drain breaks async ) ;
|
||||
Block 5031
|
||||
( channel negotiation: mint/request )
|
||||
: CH-MINT-ID ( owner -- id )
|
||||
MSG-SEQ @ 1+ DUP MSG-SEQ !
|
||||
SWAP 32 LSHIFT OR ;
|
||||
( H.8: initiator-only gate. Refuses if `from` doesn't )
|
||||
( match MY-CH-ID -- no posting while claiming another VM. )
|
||||
: CH-REQUEST ( type from to paddr plen -- )
|
||||
3 PICK MY-CH-ID @ <> IF 2DROP 2DROP DROP EXIT THEN
|
||||
COMMON-CH @ CH-STATE@ CH-OPEN = IF
|
||||
COMMON-CH @ MSG-SEND
|
||||
ELSE 2DROP 2DROP DROP THEN ;
|
||||
Block 5032
|
||||
( channel ops: accept confirm close )
|
||||
: CH-ACCEPT ( -- ch|0 )
|
||||
Q.SLOT CH-ALLOC DUP 0= IF EXIT THEN
|
||||
1 CH-MINT-ID OVER CH-ID!
|
||||
1 OVER CH-OWNER!
|
||||
CH-NEGOTIATING OVER CH-STATE!
|
||||
0 OVER CH-MBRS!
|
||||
CH-ACTIVE @ OVER CH-NEXT!
|
||||
DUP CH-ACTIVE ! ;
|
||||
: CH-CONFIRM ( ch -- )
|
||||
DUP CH-STATE@ CH-NEGOTIATING = IF CH-OPEN SWAP CH-STATE!
|
||||
ELSE DROP THEN ;
|
||||
: CH-CLOSE ( ch -- )
|
||||
DUP COMMON-CH @ = IF DROP EXIT THEN
|
||||
CH-CLOSING SWAP CH-STATE! ;
|
||||
Block 5033
|
||||
( MSG-CD-INIT (was CD-INIT -- renamed to avoid colliding )
|
||||
( with Artemis's own unrelated block-subsystem CD-INIT). )
|
||||
( common:msg.4th's HERMES-ACK/NACK indirection is retired: )
|
||||
( every VM now has its own local MSG-ACK-LAST/NACK-LAST. )
|
||||
: MSG-CD-INIT ( -- )
|
||||
MSG-ARENA MSG-MAX MSG-CELLS * CELLS 0 FILL
|
||||
CH-ARENA CH-MAX CH-CELLS * CELLS 0 FILL
|
||||
MBR-ARENA MBR-MAX MBR-CELLS * CELLS 0 FILL
|
||||
MSG-INIT-FREE
|
||||
CH-INIT-FREE
|
||||
MBR-INIT-FREE
|
||||
0 MSG-SEQ !
|
||||
0 MSG-LAST-MSG !
|
||||
0 CH-ACTIVE ! -1 MY-CH-ID !
|
||||
COMMON-INIT
|
||||
VM-NAMES-INIT ;
|
||||
Block 5034
|
||||
( ACK/NACK -- now purely local, no VM-EXEC indirection. )
|
||||
: MSG-ACK-LAST ( -- )
|
||||
MSG-LAST-MSG @ DUP 0= IF DROP EXIT THEN
|
||||
0 OVER MSG-TYPE! MSG-FREE-NODE ;
|
||||
: MSG-NACK-LAST ( -- )
|
||||
MSG-LAST-MSG @ DUP 0= IF DROP EXIT THEN
|
||||
DUP MSG-HEAT@ 2 / OVER MSG-HEAT!
|
||||
MSG-NACKED SWAP MSG-TYPE! ;
|
||||
Block 5035
|
||||
( MSG-STATUS (was HERMES-STATUS -- generic now) )
|
||||
: MSG-USED ( -- n )
|
||||
0 MSG-ARENA MSG-SCAN !
|
||||
MSG-MAX 0 DO
|
||||
MSG-SCAN @ MSG-TYPE@ 0 <> IF 1+ THEN
|
||||
MSG-SCAN @ MSG-CELLS CELLS + MSG-SCAN !
|
||||
LOOP ;
|
||||
: CH-USED ( -- n )
|
||||
0 CH-ACTIVE @ CH-SCAN !
|
||||
BEGIN CH-SCAN @ 0 <> WHILE
|
||||
1+
|
||||
CH-SCAN @ CH-NEXT@ CH-SCAN !
|
||||
REPEAT ;
|
||||
: MSG-STATUS ( -- )
|
||||
MSG-USED . ." msgs " CH-USED . ." channels" CR ;
|
||||
Block 5036
|
||||
( member management )
|
||||
: MBR-NEXT! ( a m -- ) ! ;
|
||||
: MBR-VM! ( n m -- ) 1 CELLS + ! ;
|
||||
: CH-ADD-MBR ( vm ch -- )
|
||||
MBR-ALLOC DUP 0= IF DROP 2DROP EXIT THEN
|
||||
ROT OVER MBR-VM!
|
||||
OVER CH-MBRS@ OVER MBR-NEXT!
|
||||
SWAP CH-MBRS! ;
|
||||
Block 5037
|
||||
( Phase 2: real multi-member broadcast )
|
||||
VARIABLE BC-TYPE VARIABLE BC-FROM
|
||||
VARIABLE BC-PADDR VARIABLE BC-PLEN
|
||||
VARIABLE BC-CH VARIABLE BC-SCAN
|
||||
: MSG-BROADCAST ( type from paddr plen ch -- )
|
||||
BC-CH ! BC-PLEN ! BC-PADDR ! BC-FROM ! BC-TYPE !
|
||||
BC-CH @ CH-MBRS@ BC-SCAN !
|
||||
BEGIN BC-SCAN @ 0<> WHILE
|
||||
BC-TYPE @ BC-FROM @ BC-SCAN @ MBR-VM@
|
||||
BC-PADDR @ BC-PLEN @ BC-CH @ MSG-SEND
|
||||
BC-SCAN @ MBR-NEXT@ BC-SCAN !
|
||||
REPEAT ;
|
||||
Block 5039
|
||||
( H.8: SEND-ELEVATE-REQUEST payload builder -- )
|
||||
( scratch text buffer + append primitives. )
|
||||
CREATE ELEVATE-REQ-BUF 256 ALLOT
|
||||
VARIABLE ELEVATE-REQ-LEN
|
||||
: ELEVATE-REQ-RESET ( -- ) 0 ELEVATE-REQ-LEN ! ;
|
||||
: ELEVATE-REQ-PUTC ( c -- )
|
||||
ELEVATE-REQ-BUF ELEVATE-REQ-LEN @ + C!
|
||||
1 ELEVATE-REQ-LEN +! ;
|
||||
: ELEVATE-REQ-APPEND ( addr u -- )
|
||||
>R
|
||||
ELEVATE-REQ-BUF ELEVATE-REQ-LEN @ +
|
||||
R@ CMOVE
|
||||
ELEVATE-REQ-LEN @ R> + ELEVATE-REQ-LEN ! ;
|
||||
: ELEVATE-REQ-NUM ( n -- )
|
||||
<# 0 SWAP #S #> ELEVATE-REQ-APPEND ;
|
||||
Block 5040
|
||||
( SEND-ELEVATE-REQUEST: ask Zuse for word-ACL )
|
||||
( elevation. pk3..pk0 order so top-down emits )
|
||||
( pk0 first; waddr/wu = target word name text. )
|
||||
: SEND-ELEVATE-REQUEST ( pk3 pk2 pk1 pk0 waddr wu -- )
|
||||
ELEVATE-REQ-RESET
|
||||
83 ELEVATE-REQ-PUTC 34 ELEVATE-REQ-PUTC
|
||||
32 ELEVATE-REQ-PUTC
|
||||
ELEVATE-REQ-APPEND
|
||||
34 ELEVATE-REQ-PUTC 32 ELEVATE-REQ-PUTC
|
||||
ELEVATE-REQ-NUM 32 ELEVATE-REQ-PUTC
|
||||
ELEVATE-REQ-NUM 32 ELEVATE-REQ-PUTC
|
||||
ELEVATE-REQ-NUM 32 ELEVATE-REQ-PUTC
|
||||
ELEVATE-REQ-NUM 32 ELEVATE-REQ-PUTC
|
||||
S" ELEVATE-GRANT" ELEVATE-REQ-APPEND
|
||||
ELEVATE-REQUEST MY-CH-ID @ 0 ELEVATE-REQ-BUF
|
||||
ELEVATE-REQ-LEN @ CH-REQUEST ;
|
||||
@@ -1,10 +0,0 @@
|
||||
Block 4055
|
||||
( common:msg.4th — Hermes participant interface )
|
||||
( Load into every messaging VM at birth: )
|
||||
( S" common:msg.4th" EXEC )
|
||||
: HERMES-ACK ( -- )
|
||||
LOG-DEBUG" msg: ACK sent"
|
||||
S" MSG-ACK-LAST" S" Hermes" VM-EXEC ;
|
||||
: HERMES-NACK ( -- )
|
||||
LOG-DEBUG" msg: NACK sent"
|
||||
S" MSG-NACK-LAST" S" Hermes" VM-EXEC ;
|
||||
@@ -1,20 +1,17 @@
|
||||
Block 4060
|
||||
( doe-campaign.4th: Compudynamics DoE campaign orchestrator )
|
||||
( Requires: doe.4th (word-level) + real VM fleet touches. )
|
||||
( Child VMs must define LOAD-DOE. )
|
||||
: SETUP-HERMES ( -- )
|
||||
S" Hermes" BIRTH
|
||||
S" LOAD-DOE" S" Hermes" VM-EXEC ;
|
||||
( Child VMs must define LOAD-DOE. FABRIC-3.6.md Phase 4, )
|
||||
( Stage E: SETUP-HERMES removed -- Hermes no longer exists. )
|
||||
: SETUP-ARTEMIS ( -- )
|
||||
S" Artemis" BIRTH
|
||||
S" LOAD-DOE" S" Artemis" VM-EXEC ;
|
||||
: SETUP-VMS ( -- )
|
||||
SETUP-HERMES SETUP-ARTEMIS ;
|
||||
SETUP-ARTEMIS ;
|
||||
Block 4061
|
||||
( Phase 1: each VM runs DOE-WORK once for baseline. )
|
||||
( FABRIC-3.6.md Phase 4, Stage E: Hermes's own line removed. )
|
||||
: PHASE1-DOE ( -- )
|
||||
." Phase 1: Hermes" CR
|
||||
S" DOE-WORK" S" Hermes" VM-EXEC
|
||||
." Phase 1: Artemis" CR
|
||||
S" DOE-WORK" S" Artemis" VM-EXEC ;
|
||||
( HISTORICAL: CD-WORK removed 2026-07-08 -- dead code, )
|
||||
@@ -24,8 +21,9 @@ Block 4062
|
||||
( CD-TICK: one real fleet touch pass -- not a fake pump. )
|
||||
( Heat only moves via real VM-EXEC/VM-CALL/VM-STEP now; )
|
||||
( there is nothing left to artificially bump per VM. )
|
||||
( FABRIC-3.6.md Phase 4, Stage E: Hermes's own line )
|
||||
( removed -- one touch per tick now, not two. )
|
||||
: CD-TICK ( -- )
|
||||
S" DOE-WORK" S" Hermes" VM-EXEC
|
||||
S" DOE-WORK" S" Artemis" VM-EXEC ;
|
||||
: CD-DOE ( n -- ) 0 DO CD-TICK LOOP ;
|
||||
: CAMPAIGN-STATUS ( -- )
|
||||
@@ -47,11 +45,10 @@ Block 4064
|
||||
( Smoke test: 1 rep seed 1959 + 16 real fleet touches )
|
||||
( HISTORICAL: VM-HEAT! artificial seeding removed -- new )
|
||||
( VMs start at zero heat by construction, no seed needed. )
|
||||
( FABRIC-3.6.md Phase 4, Stage E: Hermes's own rep removed. )
|
||||
: SMOKE-CAMPAIGN ( -- )
|
||||
." === Smoke: 1 rep seed 1959 ===" CR
|
||||
SETUP-VMS
|
||||
." Hermes 1 rep..." CR
|
||||
S" 1959 1 EXEC-DOE" S" Hermes" VM-EXEC
|
||||
." Artemis 1 rep..." CR
|
||||
S" 1959 1 EXEC-DOE" S" Artemis" VM-EXEC
|
||||
." 16 real fleet touches..." CR
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
Block 4855
|
||||
( Hermes v1 -- WELCOME. Generic messaging vocab moved to )
|
||||
( common:messaging.4th, FABRIC-2.md Phase C 2026-08-28. )
|
||||
: WELCOME ( -- ) LOG-INFO" Hermes: loaded" ;
|
||||
WELCOME
|
||||
Block 4153
|
||||
( LOAD-DOE: pulls in doe.4th's word-level DOE-WORK )
|
||||
( workload, for doe-campaign.4th's remote VM-EXEC. )
|
||||
: LOAD-DOE ( -- ) S" doe.4th" EXEC ;
|
||||
Block 5116
|
||||
( Hermes owns the one real, canonical COMMON-CH. Every )
|
||||
( other VM subscribes into it via VM-EXEC (see Artemis's )
|
||||
( and Hera's own init.4th); Hera always exists first, so )
|
||||
( Hermes adds her here rather than Hera subscribing to a )
|
||||
( VM that doesn't exist yet at Hera's own birth. )
|
||||
S" common:messaging.4th" EXEC
|
||||
MSG-CD-INIT
|
||||
1 MY-CH-ID !
|
||||
S" lib.4th" EXEC
|
||||
1 COMMON-CH @ CH-ADD-MBR
|
||||
0 COMMON-CH @ CH-ADD-MBR
|
||||
LOG-INFO" Hermes: ready"
|
||||
STARTUP-BANNER
|
||||
@@ -0,0 +1,22 @@
|
||||
Block 4986
|
||||
( Hestia -- WELCOME. Third Tripod leg (Hera/Artemis/Hestia, )
|
||||
( FABRIC-3.5.md SII/SIV), bind point + drawing fabric owner. )
|
||||
: WELCOME ( -- ) LOG-INFO" Hestia: loaded" ;
|
||||
WELCOME
|
||||
Block 4988
|
||||
( fabric.4th/font.4th moved here from init.4th (Hera) -- )
|
||||
( FABRIC-3.6.md tasks 1.6/1.7, merged into one commit: they )
|
||||
( are not independent -- font.4th's G-LINE/G-ELLIPSE calls )
|
||||
( are fabric.4th's own words, so moving one without the )
|
||||
( other breaks whichever VM keeps only half the pair. Order )
|
||||
( preserved: fabric.4th before font.4th, same as init.4th's )
|
||||
( own load order before this move. )
|
||||
S" fabric.4th" EXEC
|
||||
S" font.4th" EXEC
|
||||
Block 4987
|
||||
( FABRIC-3.6.md Phase 4, Stage E: common:messaging.4th/ )
|
||||
( MSG-CD-INIT/MY-CH-ID/COMMON-CH subscription all removed )
|
||||
( -- the whole COMMON-CH concept is gone with the file. )
|
||||
S" lib.4th" EXEC
|
||||
LOG-INFO" Hestia: ready"
|
||||
STARTUP-BANNER
|
||||
+2
-5
@@ -17,11 +17,8 @@ S" ACL.4th" EXEC
|
||||
S" block-acl.4th" EXEC
|
||||
S" zuse-eligibility.4th" EXEC
|
||||
S" lib.4th" EXEC
|
||||
S" fabric.4th" EXEC
|
||||
S" font.4th" EXEC
|
||||
( SXX: same pattern Hermes/Artemis/console/mint already use. )
|
||||
S" common:messaging.4th" EXEC
|
||||
MSG-CD-INIT
|
||||
( FABRIC-3.6.md Phase 4, Stage E: common:messaging.4th/ )
|
||||
( MSG-CD-INIT removed -- no longer exists, no longer needed. )
|
||||
Block 2050
|
||||
( Hera boot — banner )
|
||||
BOOT-BANNER
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
Block 4300
|
||||
( process.4th — lifecycle phase operators )
|
||||
( Event codes must match hermes/init.4th )
|
||||
1 CONSTANT SPAWN-EVENT
|
||||
2 CONSTANT PAUSE-EVENT
|
||||
3 CONSTANT RESUME-EVENT
|
||||
4 CONSTANT KILL-EVENT
|
||||
( SPAWN: birth VM, notify Hermes )
|
||||
: SPAWN ( c-addr u -- )
|
||||
BIRTH
|
||||
LOG-INFO" process: spawned"
|
||||
S" 1 EVENT-EMIT" S" Hermes" VM-EXEC ;
|
||||
( PAUSE: notify Hermes, send STOP to the named VM )
|
||||
: PAUSE ( c-addr u -- )
|
||||
S" 2 EVENT-EMIT" S" Hermes" VM-EXEC
|
||||
S" STOP" 2SWAP VM-EXEC ;
|
||||
Block 4301
|
||||
( Lifecycle operators: resume, kill, phase )
|
||||
( RESUME: notify Hermes, START the named VM )
|
||||
: RESUME ( c-addr u -- )
|
||||
S" 3 EVENT-EMIT" S" Hermes" VM-EXEC
|
||||
LOG-INFO" process: resumed"
|
||||
START ;
|
||||
( KILL-VM: notify Hermes, then kill VM )
|
||||
: KILL-VM ( c-addr u -- )
|
||||
S" 4 EVENT-EMIT" S" Hermes" VM-EXEC
|
||||
LOG-INFO" process: killed"
|
||||
KILL ;
|
||||
@@ -3,9 +3,9 @@ Block 4021
|
||||
( C prims: ZUSE-ELIGIBILITY-ADD ZUSE-ELIGIBLE? )
|
||||
( NAME>XT ELEVATE-PUBKEY-UNPACK )
|
||||
( Policy word this file: ELEVATE-GRANT. Runs in )
|
||||
( Hera's own dict -- delivered here by messaging.4th's )
|
||||
( MSG-DELIVER via VM-EXEC when a session calls the )
|
||||
( common:messaging.4th SEND-ELEVATE-REQUEST entrypoint. )
|
||||
( Hera's own dict -- delivered here by kernel-Hermes's own )
|
||||
( drain checkpoint (FABRIC-3.6.md task 3.10), not FORTH )
|
||||
( MSG-DELIVER (messaging.4th, removed Phase 4 Stage E). )
|
||||
( FABRIC-2.md H.12 step 21, 2026-09-03. )
|
||||
CREATE ELEVATE-PK-BUF 32 ALLOT
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ Block 4016
|
||||
( >BODY-then-store, so a pinned CONSTANT isn't tamper-proof. )
|
||||
( Read with ZUSE-PUBKEY@ / ZUSE-CERT-INSTALLED? -- both C )
|
||||
( primitives, read-only; the seed has no FORTH access at all. )
|
||||
( ZUSE-ELIGIBILITY-ADD denied by default -- see block 4017. )
|
||||
0 ['] ZUSE-ELIGIBILITY-ADD ACL-ALLOW!
|
||||
|
||||
Block 4017
|
||||
( ACL-ZUSE-BOOT ( -- ) re-invokable: capsule_zuse_boot.c )
|
||||
@@ -23,6 +25,8 @@ Block 4017
|
||||
: ACL-ZUSE-BOOT ( -- )
|
||||
ZUSE-CERT-INSTALLED? IF
|
||||
ZUSE-AUTHENTICATE
|
||||
1 ['] ZUSE-ELIGIBILITY-ADD ACL-ALLOW!
|
||||
['] ZUSE-ELIGIBILITY-ADD ACL-PIN
|
||||
LOG-INFO" zuse: activated"
|
||||
ELSE
|
||||
LOG-INFO" zuse: NOT activated -- no cert installed"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
# configs/
|
||||
|
||||
Example Kconfig `defconfig` files — starting points for `make -f
|
||||
Makefile.starkernel ARCH=<arch> defconfig` (kernel) or the equivalent
|
||||
kernel/Makefile ARCH=<arch> defconfig` (kernel) or the equivalent
|
||||
hosted target, matching the current committed default build behavior for
|
||||
each profile. Loaded via `tools/kconfig/conf`.
|
||||
|
||||
|
||||
@@ -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,112 @@
|
||||
# docs/book/Makefile -- the single LithosAnanke book, built from LaTeX.
|
||||
#
|
||||
# Run from the repo root (the root Makefile does this):
|
||||
# make docs -> build/docs/LithosAnanke.pdf
|
||||
# make docs TARGET=<board> -> build/docs/LithosAnanke-<board>.pdf
|
||||
# (same book + that board's appendix)
|
||||
#
|
||||
# docs/book/main.tex is the master. Markdown sources are converted by pandoc
|
||||
# into LaTeX fragments under build/docs/gen/ and \input from main.tex; the
|
||||
# Markdown stays the source of truth until a chapter is rewritten in LaTeX.
|
||||
# Figures and tables that present data read the CSV directly at LaTeX time
|
||||
# (pgfplots / pgfplotstable) and cite it with \datasource{path}; see
|
||||
# docs/book/README.md.
|
||||
|
||||
BOOK_DIR := docs/book
|
||||
OUT := build/docs
|
||||
GEN := $(OUT)/gen
|
||||
|
||||
BOARD ?=
|
||||
BOARD_NAME ?=
|
||||
VARIANT := $(if $(BOARD),$(BOARD),book)
|
||||
VAR_DIR := $(OUT)/$(VARIANT)
|
||||
PDF := $(OUT)/LithosAnanke$(if $(BOARD),-$(BOARD)).pdf
|
||||
|
||||
PANDOC ?= pandoc
|
||||
LATEXMK ?= latexmk
|
||||
PANDOC_FILTERS := $(BOOK_DIR)/pandoc/table-widths.lua $(BOOK_DIR)/pandoc/code-breaks.lua
|
||||
PANDOC_FLAGS := -f gfm -t latex --top-level-division=chapter --wrap=preserve \
|
||||
$(foreach f,$(PANDOC_FILTERS),--lua-filter=$(f))
|
||||
|
||||
# fragment name : Markdown source. The order of \input lines is main.tex's.
|
||||
CHAPTERS := \
|
||||
ontology:docs/ONTOLOGY.md \
|
||||
roadmap:docs/ROADMAP.md \
|
||||
boards:boards/README.md \
|
||||
justification:docs/v4.0.0/JUSTIFICATION.md \
|
||||
decomposition:docs/v4.0.0/DECOMPOSITION.md \
|
||||
fabric-0:docs/fabric/FABRIC-0.md \
|
||||
fabric-1:docs/fabric/FABRIC-1.md \
|
||||
fabric-2:docs/fabric/FABRIC-2.md \
|
||||
fabric-3:docs/fabric/FABRIC-3.md \
|
||||
fabric-3-5:docs/fabric/FABRIC-3.5.md \
|
||||
fabric-3-6:docs/fabric/FABRIC-3.6.md \
|
||||
fabric-3-7:docs/fabric/FABRIC-3.7.md \
|
||||
fabric-4:docs/fabric/FABRIC-4.md
|
||||
|
||||
ifneq ($(BOARD),)
|
||||
ifeq ($(wildcard boards/$(BOARD)/README.md),)
|
||||
$(error boards/$(BOARD)/README.md is missing: it is the board's appendix)
|
||||
endif
|
||||
CHAPTERS += board-$(BOARD):boards/$(BOARD)/README.md
|
||||
endif
|
||||
|
||||
chapter_name = $(word 1,$(subst :, ,$(1)))
|
||||
chapter_src = $(word 2,$(subst :, ,$(1)))
|
||||
FRAGMENTS := $(foreach c,$(CHAPTERS),$(GEN)/$(call chapter_name,$(c)).tex)
|
||||
|
||||
.PHONY: all tools FORCE
|
||||
all: $(PDF)
|
||||
|
||||
tools:
|
||||
@missing=""; \
|
||||
for t in $(PANDOC) $(LATEXMK) xelatex; do command -v $$t >/dev/null 2>&1 || missing="$$missing $$t"; done; \
|
||||
if [ -n "$$missing" ]; then \
|
||||
echo "Error: make docs needs:$$missing"; \
|
||||
echo " sudo apt-get install -y pandoc latexmk texlive-xetex texlive-latex-extra texlive-pictures texlive-fonts-recommended fonts-dejavu fonts-dejavu-extra"; \
|
||||
echo " (or a user-local TinyTeX + pandoc in ~/.local; see docs/book/README.md)"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
# --id-prefix keeps heading labels unique across documents that reuse
|
||||
# section names ("Summary", "Open questions", ...).
|
||||
define chapter_rule
|
||||
$(GEN)/$(call chapter_name,$(1)).tex: $(call chapter_src,$(1)) $(PANDOC_FILTERS) | tools
|
||||
@mkdir -p $(GEN)
|
||||
@echo " PANDOC $$< -> $$@"
|
||||
@$(PANDOC) $(PANDOC_FLAGS) --id-prefix=$(call chapter_name,$(1))- $$< -o $$@
|
||||
endef
|
||||
$(foreach c,$(CHAPTERS),$(eval $(call chapter_rule,$(c))))
|
||||
|
||||
# Pandoc's syntax-highlighting macros, taken from the installed pandoc so the
|
||||
# fragments and their macros always come from the same version.
|
||||
$(GEN)/pandoc-highlighting.tex: $(BOOK_DIR)/pandoc/highlighting.latex | tools
|
||||
@mkdir -p $(GEN)
|
||||
@printf '```c\nx\n```\n' | $(PANDOC) -f gfm -t latex -s --template=$< -o $@
|
||||
|
||||
# Per-build facts the book prints: the commit every \datasource refers to,
|
||||
# and which board appendix (if any) is included.
|
||||
$(VAR_DIR)/meta.tex: FORCE
|
||||
@mkdir -p $(VAR_DIR)
|
||||
@{ \
|
||||
c=$$(git rev-parse --short=12 HEAD 2>/dev/null || echo unknown); \
|
||||
git diff --quiet HEAD -- 2>/dev/null || c="$$c (+ uncommitted changes)"; \
|
||||
printf '\\newcommand{\\bookcommit}{%s}\n' "$$c"; \
|
||||
printf '\\newcommand{\\bookdate}{%s}\n' "$$(date -u +%Y-%m-%d)"; \
|
||||
printf '\\def\\bookboard{%s}\n' "$(BOARD_NAME)"; \
|
||||
$(if $(BOARD),printf '\\newcommand{\\bookboardappendix}{board-%s}\n' "$(BOARD)";) \
|
||||
} > $@.tmp
|
||||
@cmp -s $@.tmp $@ && rm -f $@.tmp || mv $@.tmp $@
|
||||
|
||||
FORCE:
|
||||
|
||||
TEXINPUTS_BOOK := $(abspath $(VAR_DIR)):$(abspath $(GEN)):$(abspath $(BOOK_DIR)):
|
||||
|
||||
$(PDF): $(BOOK_DIR)/main.tex $(FRAGMENTS) $(GEN)/pandoc-highlighting.tex $(VAR_DIR)/meta.tex | tools
|
||||
@echo " LATEXMK $(BOOK_DIR)/main.tex -> $@"
|
||||
@TEXINPUTS=$(TEXINPUTS_BOOK) $(LATEXMK) -xelatex -interaction=nonstopmode -halt-on-error \
|
||||
-file-line-error -outdir=$(VAR_DIR) $(BOOK_DIR)/main.tex > $(VAR_DIR)/latexmk.log 2>&1 || { \
|
||||
grep -A4 -E '^(.*:[0-9]+:|!)' $(VAR_DIR)/main.log | head -40; \
|
||||
echo "Error: LaTeX failed; full log: $(VAR_DIR)/main.log"; exit 1; }
|
||||
@cp $(VAR_DIR)/main.pdf $@
|
||||
@echo " PDF $@"
|
||||
@@ -0,0 +1,66 @@
|
||||
# docs/book/
|
||||
|
||||
`make docs` builds one PDF, `build/docs/LithosAnanke.pdf`, from `main.tex` with xelatex.
|
||||
`make docs TARGET=<board>` builds `build/docs/LithosAnanke-<board>.pdf`: the same book with
|
||||
`boards/<board>/README.md` added as an appendix.
|
||||
|
||||
Tools: `sudo apt-get install -y pandoc latexmk texlive-xetex texlive-latex-extra texlive-pictures texlive-fonts-recommended fonts-dejavu fonts-dejavu-extra`
|
||||
|
||||
Without root, a user-local TeX Live works too (this is how the book was first built):
|
||||
TinyTeX (`curl -sL https://yihui.org/tinytex/install-bin-unix.sh | sh`) plus
|
||||
`tlmgr install latexmk xetex fontspec pgf pgfplots booktabs multirow fancyvrb fvextra lineno upquote ulem enumitem newunicodechar bookmark hyperref geometry xcolor graphics tools etoolbox fancyhdr truncate amsfonts`,
|
||||
and the pandoc release tarball unpacked into `~/.local`.
|
||||
|
||||
The installed DejaVu decides italics: `fonts-dejavu-core` alone has no serif italic, so
|
||||
`main.tex` falls back to a slanted upright face; `fonts-dejavu-extra` gives real italics.
|
||||
|
||||
## How it fits together
|
||||
|
||||
- `main.tex` is the master file. It sets the parts and the chapter order.
|
||||
- Chapters that are still Markdown are listed in `Makefile` (`CHAPTERS`, as `name:source.md`).
|
||||
At build time pandoc converts each one into `build/docs/gen/<name>.tex`, and `main.tex`
|
||||
includes it with `\input{<name>}`. The Markdown stays the source until the chapter is
|
||||
rewritten in LaTeX. At that point the `.tex` moves into this directory and the `CHAPTERS`
|
||||
entry is removed.
|
||||
- `pandoc/highlighting.latex` extracts the code-highlighting macros from the installed pandoc,
|
||||
so the macros and the fragments always come from the same pandoc version.
|
||||
- Two Lua filters run on every chapter. `pandoc/table-widths.lua` gives wide tables
|
||||
proportional wrapping columns, because pandoc's gfm reader leaves column widths unset.
|
||||
`pandoc/code-breaks.lua` lets long inline identifiers and paths break after `_ / . - :`.
|
||||
Code blocks wrap through fvextra (`breaklines`).
|
||||
- `build/docs/<book|board>/meta.tex` is written on every build. It records the commit (and
|
||||
whether there were uncommitted changes), the date, and the board.
|
||||
|
||||
## Rule for data
|
||||
|
||||
Any figure or table that shows measured numbers is generated from the CSV when the book is
|
||||
built. Numbers are never typed in by hand:
|
||||
|
||||
```latex
|
||||
\begin{tikzpicture}
|
||||
\begin{axis}[xlabel=run, ylabel=K]
|
||||
\addplot table[col sep=comma, x=run, y=K]{experiments/<campaign>/results.csv};
|
||||
\end{axis}
|
||||
\end{tikzpicture}
|
||||
\datasource{experiments/<campaign>/results.csv}
|
||||
```
|
||||
|
||||
Use `\pgfplotstabletypeset[col sep=comma]{...}` for tables. `\datasource` prints the CSV path
|
||||
and the commit, so a reader can find the exact file the figure was drawn from. CSV paths are
|
||||
repo-relative, because xelatex runs from the repo root.
|
||||
|
||||
## Older pipelines to fold in or retire
|
||||
|
||||
These still exist and still have their own targets. Each one needs a decision during the
|
||||
curation pass: move it into this book, or retire it.
|
||||
|
||||
| Source | Current target | Produces |
|
||||
|---|---|---|
|
||||
| `docs/formal/vol1-vm-physics`, `vol2-kernel`, `vol3-research` | `make -C docs/formal vols` | three volume PDFs |
|
||||
| `docs/formal/dev-guide`, `user-guide`, `cookbook` | `make -C docs/formal books` | three practitioner PDFs |
|
||||
| `docs/formal/experiments`, `proofs`, `ssrn`, `patent` | `make -C docs/formal standalone` | four standalone PDFs |
|
||||
| Doxygen (`Doxyfile`) | `make -C docs/formal doxygen` | API reference PDF |
|
||||
| `scripts/generate-doxygen-appendix.sh` | `make -f v3/Makefile api-docs` | AsciiDoc API appendix |
|
||||
| `docs/src/internal/formal/*.thy` | `make -f v3/Makefile docs-isabelle` | Isabelle report |
|
||||
| `scripts/asciidoc-to-latex.sh` | `make -f v3/Makefile docs-latex` | `docs/latex/` |
|
||||
| `docs/SSRN_companion/Math_Companion_SSRN.tex` | `make -f v3/Makefile math-companion` | SSRN math companion |
|
||||
@@ -0,0 +1,161 @@
|
||||
% docs/book/main.tex -- master file of the single LithosAnanke book.
|
||||
%
|
||||
% Built by `make docs [TARGET=<board>]` (docs/book/Makefile) with xelatex.
|
||||
% Fragments named below (ontology, fabric-0, ...) are generated by pandoc
|
||||
% from the Markdown listed in docs/book/Makefile's CHAPTERS into
|
||||
% build/docs/gen/; meta.tex is generated per build. Neither is committed.
|
||||
\documentclass[11pt,openany]{book}
|
||||
|
||||
\usepackage{amsmath,amssymb}
|
||||
\usepackage{fontspec}
|
||||
% fonts-dejavu-core has no serif/sans italics (fonts-dejavu-extra does); when a
|
||||
% shape is missing, slant the upright one rather than silently drop emphasis.
|
||||
\setmainfont{DejaVu Serif}[AutoFakeSlant=0.2]
|
||||
\setsansfont{DejaVu Sans}[AutoFakeSlant=0.2]
|
||||
\setmonofont{DejaVu Sans Mono}[Scale=0.85]
|
||||
\newfontfamily\symbolfont{DejaVu Sans}
|
||||
|
||||
\usepackage[letterpaper,margin=1in]{geometry}
|
||||
\usepackage{xcolor}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{longtable,booktabs,array,calc,multirow}
|
||||
\usepackage{fancyvrb}
|
||||
\usepackage[normalem]{ulem}
|
||||
\usepackage{enumitem}
|
||||
\usepackage{newunicodechar}
|
||||
\usepackage{pgfplots}
|
||||
\usepackage{pgfplotstable}
|
||||
\pgfplotsset{compat=1.18}
|
||||
\usepackage[hidelinks]{hyperref}
|
||||
\usepackage{bookmark}
|
||||
|
||||
% --- what pandoc's LaTeX fragments expect (normally from its own template)
|
||||
\providecommand{\tightlist}{\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
|
||||
\providecommand{\pandocbounded}[1]{#1}
|
||||
\providecommand{\st}[1]{\sout{#1}}
|
||||
\newcounter{none} % pandoc: {\def\LTcaptype{none} ...} marks unnumbered tables
|
||||
\usepackage{etoolbox}
|
||||
\makeatletter
|
||||
\def\fnum@table{\tablename~\thetable}
|
||||
\patchcmd\longtable{\par}{\if@noskipsec\mbox{}\fi\par}{}{}
|
||||
\makeatother
|
||||
\usepackage{fvextra}
|
||||
\input{pandoc-highlighting}
|
||||
% Code and diagrams are wider than the page in places: wrap, never overflow.
|
||||
\RecustomVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\},breaklines,breakanywhere,fontsize=\small}
|
||||
\RecustomVerbatimEnvironment{verbatim}{Verbatim}{breaklines,breakanywhere,fontsize=\small}
|
||||
\setlistdepth{9}
|
||||
\renewlist{itemize}{itemize}{9}
|
||||
\setlist[itemize]{label=\textbullet}
|
||||
\renewlist{enumerate}{enumerate}{9}
|
||||
\setlist[enumerate]{label=\arabic*.}
|
||||
% Running heads: chapter on the left page, section on the right, both cut to
|
||||
% the page width (several design-record titles are a full sentence long).
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage[fit]{truncate}
|
||||
\pagestyle{fancy}
|
||||
\fancyhf{}
|
||||
\renewcommand{\chaptermark}[1]{\markboth{#1}{}}
|
||||
\renewcommand{\sectionmark}[1]{\markright{#1}}
|
||||
\fancyhead[LE]{\small\truncate{\dimexpr\headwidth-3em}{\leftmark}}
|
||||
\fancyhead[RO]{\small\truncate{\dimexpr\headwidth-3em}{\rightmark}}
|
||||
\fancyhead[RE,LO]{\small\thepage}
|
||||
\renewcommand{\headrulewidth}{0.4pt}
|
||||
\setlength{\headheight}{14pt}
|
||||
\fancypagestyle{plain}{\fancyhf{}\fancyfoot[C]{\small\thepage}\renewcommand{\headrulewidth}{0pt}}
|
||||
\setlength{\emergencystretch}{3em}
|
||||
\setlength{\parindent}{0pt}
|
||||
\setlength{\parskip}{0.5em}
|
||||
|
||||
% --- glyphs DejaVu Serif lacks: the status marks used across the docs
|
||||
\newunicodechar{✅}{{\symbolfont ✔}}
|
||||
\newunicodechar{✓}{{\symbolfont ✓}}
|
||||
\newunicodechar{❌}{{\symbolfont ✘}}
|
||||
\newunicodechar{✗}{{\symbolfont ✗}}
|
||||
\newunicodechar{⬜}{{\symbolfont ☐}}
|
||||
\newunicodechar{⭐}{{\symbolfont ★}}
|
||||
\newunicodechar{🔶}{{\symbolfont ◆}}
|
||||
\newunicodechar{⚠}{{\symbolfont ⚠}}
|
||||
\newunicodechar{📋}{}
|
||||
\newunicodechar{🐛}{[bug]}
|
||||
\newunicodechar{🎯}{[goal]}
|
||||
\newunicodechar{🟡}{{\symbolfont ●}}
|
||||
\newunicodechar{📍}{{\symbolfont ▸}}
|
||||
\newunicodechar{🔓}{[unlocked]}
|
||||
\newunicodechar{❓}{?}
|
||||
\newunicodechar{⟺}{\ensuremath{\Longleftrightarrow}}
|
||||
\newunicodechar{⋯}{\ensuremath{\cdots}}
|
||||
\newunicodechar{^^^^fe0f}{}
|
||||
|
||||
% --- the documents number their own sections (§III.1, D-3, ...)
|
||||
\setcounter{secnumdepth}{0}
|
||||
\setcounter{tocdepth}{1}
|
||||
|
||||
\input{meta}
|
||||
|
||||
% \datasource{path/to/data.csv}: cite the data behind a figure or table.
|
||||
% Every figure or table of measured numbers reads its CSV at build time
|
||||
% (\addplot table / \pgfplotstabletypeset) and is followed by this line, so
|
||||
% the reader can find the exact file at the exact commit.
|
||||
\newcommand{\datasource}[1]{%
|
||||
\par{\small Data: \texttt{\detokenize{#1}} at commit \texttt{\bookcommit}.}\par}
|
||||
|
||||
\title{LithosAnanke and StarForth}
|
||||
\author{}
|
||||
\date{Built \bookdate{} from commit \texttt{\bookcommit}%
|
||||
\ifx\bookboard\empty\else\\Board appendix: \bookboard\fi}
|
||||
|
||||
\begin{document}
|
||||
\frontmatter
|
||||
\maketitle
|
||||
\tableofcontents
|
||||
|
||||
\chapter{About this book}
|
||||
This book is generated from the repository by \texttt{make docs}. Each
|
||||
chapter below is still maintained as the Markdown file named in the table and
|
||||
converted at build time; a chapter moves into LaTeX source when it is
|
||||
rewritten. Part~III is the design record: those documents are archival and
|
||||
are reproduced as written, including their struck-through corrections.
|
||||
|
||||
\begin{longtable}{@{}ll@{}}
|
||||
\toprule
|
||||
Chapter & Source \\
|
||||
\midrule
|
||||
\endhead
|
||||
Ontology & \texttt{docs/ONTOLOGY.md} \\
|
||||
Roadmap & \texttt{docs/ROADMAP.md} \\
|
||||
Boot targets & \texttt{boards/README.md} \\
|
||||
v4 justification & \texttt{docs/v4.0.0/JUSTIFICATION.md} \\
|
||||
v4 decomposition & \texttt{docs/v4.0.0/DECOMPOSITION.md} \\
|
||||
FABRIC-0 \dots{} FABRIC-4 & \texttt{docs/fabric/FABRIC-*.md} \\
|
||||
Board appendix (\texttt{TARGET=}) & \texttt{boards/<board>/README.md} \\
|
||||
\bottomrule
|
||||
\end{longtable}
|
||||
|
||||
\mainmatter
|
||||
|
||||
\part{The system}
|
||||
\input{ontology}
|
||||
\input{roadmap}
|
||||
\input{boards}
|
||||
|
||||
\part{StarForth v4}
|
||||
\input{justification}
|
||||
\input{decomposition}
|
||||
|
||||
\part{Design record (FABRIC series)}
|
||||
\input{fabric-0}
|
||||
\input{fabric-1}
|
||||
\input{fabric-2}
|
||||
\input{fabric-3}
|
||||
\input{fabric-3-5}
|
||||
\input{fabric-3-6}
|
||||
\input{fabric-3-7}
|
||||
\input{fabric-4}
|
||||
|
||||
\ifdefined\bookboardappendix
|
||||
\appendix
|
||||
\input{\bookboardappendix}
|
||||
\fi
|
||||
|
||||
\end{document}
|
||||
@@ -0,0 +1,29 @@
|
||||
-- docs/book/pandoc/code-breaks.lua
|
||||
--
|
||||
-- Inline code in the design record is full of long identifiers and paths
|
||||
-- (g_wirebind_attached_username, kernel/src/arch/riscv64/...), which LaTeX
|
||||
-- cannot break, so they run into the margin. Emit inline code as \texttt
|
||||
-- ourselves, escaped the same way pandoc does, with a break opportunity
|
||||
-- after each separator character.
|
||||
|
||||
local BREAK_AFTER = { ["_"] = true, ["/"] = true, ["."] = true, ["-"] = true,
|
||||
[":"] = true, [","] = true, ["("] = true, ["="] = true }
|
||||
|
||||
local ESCAPE = {
|
||||
["\\"] = "\\textbackslash{}", ["{"] = "\\{", ["}"] = "\\}",
|
||||
["$"] = "\\$", ["&"] = "\\&", ["#"] = "\\#", ["%"] = "\\%", ["_"] = "\\_",
|
||||
["^"] = "\\textasciicircum{}", ["~"] = "\\textasciitilde{}",
|
||||
["'"] = "\\textquotesingle{}", ["`"] = "\\textasciigrave{}",
|
||||
[" "] = "\\ ",
|
||||
}
|
||||
|
||||
function Code(el)
|
||||
if not FORMAT:match("latex") then return nil end
|
||||
local out = {}
|
||||
for _, cp in utf8.codes(el.text) do
|
||||
local ch = utf8.char(cp)
|
||||
out[#out + 1] = ESCAPE[ch] or ch
|
||||
if BREAK_AFTER[ch] then out[#out + 1] = "\\allowbreak{}" end
|
||||
end
|
||||
return pandoc.RawInline("latex", "\\texttt{" .. table.concat(out) .. "}")
|
||||
end
|
||||
@@ -0,0 +1,3 @@
|
||||
$if(highlighting-macros)$
|
||||
$highlighting-macros$
|
||||
$endif$
|
||||
@@ -0,0 +1,55 @@
|
||||
-- docs/book/pandoc/table-widths.lua
|
||||
--
|
||||
-- pandoc's gfm reader leaves every column width unset, so the LaTeX writer
|
||||
-- emits `l` columns and a table with long cells runs off the page. For any
|
||||
-- table wider than WIDE characters, give each column a width proportional
|
||||
-- to its longest cell (clamped to [MIN_COL, MAX_COL] characters); the
|
||||
-- writer then emits wrapping p{} columns that together fill \linewidth.
|
||||
|
||||
local WIDE = 72
|
||||
local MIN_COL = 6
|
||||
local MAX_COL = 60
|
||||
|
||||
local stringify = pandoc.utils.stringify
|
||||
|
||||
local function measure(rows, widths)
|
||||
for _, row in ipairs(rows) do
|
||||
local col = 1
|
||||
for _, cell in ipairs(row.cells) do
|
||||
local span = cell.col_span or 1
|
||||
local len = utf8.len(stringify(cell.contents)) or #stringify(cell.contents)
|
||||
local per = len / span
|
||||
for c = col, col + span - 1 do
|
||||
if per > (widths[c] or 0) then widths[c] = per end
|
||||
end
|
||||
col = col + span
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Table(tbl)
|
||||
local n = #tbl.colspecs
|
||||
local widths = {}
|
||||
for i = 1, n do widths[i] = 0 end
|
||||
|
||||
measure(tbl.head.rows, widths)
|
||||
for _, body in ipairs(tbl.bodies) do
|
||||
measure(body.head, widths)
|
||||
measure(body.body, widths)
|
||||
end
|
||||
measure(tbl.foot.rows, widths)
|
||||
|
||||
local total = 0
|
||||
for i = 1, n do total = total + widths[i] end
|
||||
if total <= WIDE then return nil end
|
||||
|
||||
local sum = 0
|
||||
for i = 1, n do
|
||||
widths[i] = math.max(MIN_COL, math.min(MAX_COL, widths[i]))
|
||||
sum = sum + widths[i]
|
||||
end
|
||||
for i = 1, n do
|
||||
tbl.colspecs[i] = { tbl.colspecs[i][1], widths[i] / sum }
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
@@ -1,37 +1,26 @@
|
||||
# FABRIC-3.5.md — the Tripod/kernel reshuffle
|
||||
|
||||
**Status: REOPENED 2026-09-19, by direct instruction ("reopen 3.5 and write it up as a gap
|
||||
analysis section"), to add §XXXI — a gap-analysis sweep of the whole FABRIC set. The design
|
||||
phase remains closed; §XXXI adds findings, not new design.**
|
||||
**Status: CLOSED/ARCHIVAL as of 2026-09-22, at tag `v2.1.0`.** Per §XXVI.5's own close
|
||||
condition ("this document closes when the tag exists"). Produced the full design ruling for
|
||||
the Tripod/kernel reshuffle: Hermes moves from a FORTH VM into the kernel as kernel-Hermes; the
|
||||
Tripod becomes Hera/Artemis/Hestia; every design question this document opened is ruled (§XXX.7
|
||||
/ §XLI's punch list). Execution against that design is recorded in `FABRIC-3.6.md`, not here —
|
||||
all five phases (0–5) closed there, ending in the `v2.1.0` tag this header names. **Nothing is
|
||||
carried forward and no successor is created** — this document is not part of the
|
||||
`FABRIC-0 → -1 → -2 → -3` chain (§XXVI.5), so closing it hands nothing to a successor.
|
||||
**`FABRIC-3.md` remains open, living and authoritative for its own topic** (bare metal boot);
|
||||
nothing here ever superseded it.
|
||||
|
||||
> **Prior status, kept rather than overwritten: DESIGN PHASE CLOSED as of 2026-09-19, by
|
||||
> direct instruction ("close the document for now"). Not yet archival.** That close stood for
|
||||
> the duration of the sweep and its substance is unchanged — every design question was and
|
||||
> remains ruled. The reopen is recorded rather than the close deleted, per this series' own
|
||||
> rule against silently rewriting a prior state.
|
||||
|
||||
**What is closed, and what is deliberately not.** Every design question this document opened is
|
||||
ruled — see §XXX.7. **No code has been written and no code is authorized.** The execution
|
||||
sequence (§XXVI.6, as amended by §XXX.7) is untouched: the surgical strip, the build, the
|
||||
Isabelle/HOL pass, the documentation sweep, the SBOM, the merge to `master`, and the `v2.1.0`
|
||||
tag all remain ahead.
|
||||
|
||||
**This is therefore a narrower close than §XXVI.5 specified**, and the difference is stated
|
||||
rather than glossed. §XXVI.5 ruled that this document closes *when the tag exists*, in
|
||||
`FABRIC-2.md`'s CLOSED/ARCHIVAL form, naming the tag it closed at. **The tag does not exist**,
|
||||
so claiming that close would be a label running ahead of the real state — the exact error
|
||||
`FABRIC-3.md` §I.2 corrected when it rolled `LITHOS_VERSION` back, and the same standard §XXIX
|
||||
applied to the LTS question. **The archival close specified by §XXVI.5 still stands and still
|
||||
happens at `v2.1.0`.** This header is the provisional one the instruction's own "for now" asks
|
||||
for.
|
||||
|
||||
**Nothing is carried forward and no successor is created.** As §XXVI.5 establishes, this
|
||||
document is not part of the `FABRIC-0 → -1 → -2 → -3` chain, so closing it hands nothing to a
|
||||
successor. **`FABRIC-3.md` remains open, living and authoritative for its own topic** (bare
|
||||
metal boot); nothing here supersedes it. The live artifact from this document is its punch list
|
||||
(§XXVI.6 / §XXX.7) — that, not this header, is what the build works from.
|
||||
|
||||
**Open by design, not by omission:** items 10–17 of §XXVI.6 are execution, not decisions.
|
||||
> **Prior status headers, kept rather than overwritten, per this series' own rule against
|
||||
> silently rewriting a prior state:**
|
||||
>
|
||||
> **REOPENED 2026-09-19**, by direct instruction ("reopen 3.5 and write it up as a gap analysis
|
||||
> section"), to add §XXXI — a gap-analysis sweep of the whole FABRIC set.
|
||||
>
|
||||
> **DESIGN PHASE CLOSED as of 2026-09-19**, by direct instruction ("close the document for
|
||||
> now"). Not yet archival at that point — the execution sequence (surgical strip, build,
|
||||
> Isabelle pass, doc sweep, SBOM, merge, tag) was still entirely ahead. That gap is now closed:
|
||||
> `FABRIC-3.6.md`'s Phases 0–5 are the execution this header once described as pending.
|
||||
Three things recorded here are explicitly *outside* this reshuffle and still need their own
|
||||
authorization — the `.claude/CLAUDE.md`/`MANIFEST.md` documentation reconciliation (§XXVI.1),
|
||||
the `src/*.c.bak` hygiene question (§XXII.5), and the stray `refs/heads/v2.0.1` branch and
|
||||
@@ -4593,6 +4582,34 @@ any checkpoint reached *during* the drain sees `depth > 1` and will not drain ag
|
||||
counter that defines the safe boundary also makes the drain non-reentrant**, with no flag, no
|
||||
lock and no new state.
|
||||
|
||||
**Amendment, found while building task 3.4 (2026-09-22): this claim is true and covers a
|
||||
different hazard than the one that actually mattered.** Non-reentrant drain (the same message
|
||||
cannot drain twice) is real, but `g_vm_interpret_depth` was built for *cross-VM* nesting — the
|
||||
checkpoint's own comment frames it as "nested inside a `VM-EXEC`/`VM-CALL` dispatch **from
|
||||
another VM's own `vm_interpret()` call**." A different VM means a different `VM` struct, so
|
||||
`input_buffer`/`input_length`/`input_pos` never collide in that case. **Same-VM reentrancy —
|
||||
draining calls `vm_interpret()` on the very `vm` whose own `vm_interpret()` call is still
|
||||
paused on the C stack, mid-word, at the checkpoint that triggered the drain — is a genuinely
|
||||
separate hazard §XLIII.5 never named.** `FABRIC-3.md` §XX had already flagged this exact class
|
||||
for Hera specifically: the old messaging pump skipped her because "self-targeting `VM-EXEC`
|
||||
would hit the same reentrancy class… (`input_buffer`/`input_pos` not being saved by
|
||||
`vm_state_push`/`pop`)." Read directly: `VMCallState` (`vm_state_push()`/`vm_state_pop()`,
|
||||
`mama_forth_words.c`) saves only `rsp`/`exit_colon`/`ecw_nesting` — never `input_buffer`,
|
||||
`input_length`, or `input_pos`. Left unaddressed, the enclosing `vm_interpret()` call's own
|
||||
while loop would silently lose the rest of its input line or block the moment a drain fires
|
||||
mid-line — the same failure shape as the `INPUT_BUFFER_SIZE` 256 defect `.claude/CLAUDE.md`
|
||||
calls non-negotiable, and trap #1 in `FABRIC-3.6.md`'s own START HERE ("a green boot is weak
|
||||
evidence").
|
||||
|
||||
**Not a divergence from this section's ruling — the mechanism is exactly as ruled.** Cursor
|
||||
preservation is the implementer's own obligation *inside* the ruled mechanism, the same way
|
||||
§XLIV.1 found `mkcapsule`'s "1024-byte limit" was arithmetically right and operationally
|
||||
incomplete: fixing one thing while leaving a real gap unnamed. `FABRIC-3.6.md` task 3.4's own
|
||||
implementation snapshots `vm->input_buffer`/`input_length`/`input_pos`, plus `vm->mode` (a
|
||||
checkpoint reached mid-colon-definition must not let a drained payload's words compile into
|
||||
the enclosing definition) and `vm->error`/`abort_requested` (a bad message must not abort the
|
||||
enclosing execution), around the `vm_interpret()` call, restoring all six afterward.
|
||||
|
||||
### XLIII.6 — Three constraints, named rather than discovered later
|
||||
|
||||
1. **`INPUT_BUFFER_SIZE` is 1025** — 1024 content bytes plus NUL, and `.claude/CLAUDE.md` calls
|
||||
@@ -4665,3 +4682,80 @@ not edited from here. `.claude/CLAUDE.md` now carries the full 64×16 rule.
|
||||
`MANIFEST.md`'s ride the strip per §XXII.5.
|
||||
- ⬜ **Item 45, NEW** — carry §XLIV.1's 64-char line limit into the `experiments/bare_metal/README.md`
|
||||
capsule guidance if it repeats the byte framing. **Not checked; flagged.**
|
||||
|
||||
---
|
||||
|
||||
## XLV. B1, B2, B4 SETTLED by Captain Bob (2026-09-21) — channels negotiate, the switch table is dynamic, payloads are one block
|
||||
|
||||
Three of Phase 3's blockers (`FABRIC-3.6.md` B1/B2/B4) ruled in one session. **These are rulings,
|
||||
recorded as given; the reasoning below is the design consequence, not new design.**
|
||||
|
||||
### XLV.1 — B1 (item 27): negotiated channels, pub/sub, ACK/NACK
|
||||
|
||||
> **Messaging is publish/subscribe. There is one common channel every VM listens to. A private
|
||||
> channel is created by request → grant/deny over the common channel, and the granted channel
|
||||
> is a new topic with its own membership. ACK/NACK negotiation runs throughout.**
|
||||
|
||||
This **overrules §XXXIII.5's recommendation** (one flat broadcast membership) in favour of the
|
||||
negotiation that `messaging.4th`'s CH-REQUEST/ACCEPT/CONFIRM/CLOSE already sketched. Consequences:
|
||||
|
||||
- **`SkHermesMembership` (task 2.1) is per-channel.** The flat list is right *per topic*; there
|
||||
is now one per channel, held in a **dynamic** table with no fixed channel maximum (same
|
||||
reasoning as XLV.2).
|
||||
- **The common channel is a permanent topic**; every VM is subscribed from birth.
|
||||
- **A deny is a NACK.** ACK/NACK are ordinary message types on the existing heat-coupled
|
||||
allocator (tasks 2.2–2.7), so they draw and decay heat like any message.
|
||||
- **Advised, not ruled — open for Captain Bob:** ACK the private-channel open and delivery, not
|
||||
every common-channel message, since ACK-everything roughly doubles message heat draw against
|
||||
the consumption economy (§XL.4).
|
||||
- **Advised, not ruled — open:** who may open a channel with whom is policy and belongs in
|
||||
`ACL.4th` (CLAUDE.md: policy in FORTH, never gated on `zuse_session`); kernel-Hermes should
|
||||
ask, not decide. Where that hook lives must be settled before the grant task is written.
|
||||
|
||||
### XLV.2 — B2 (item 32): the switch table is dynamic, not a constant
|
||||
|
||||
> **`SK_SWITCH_MAX_SLOTS` (16) is not to be fixed and static; it becomes a dynamic component.**
|
||||
|
||||
Precedent already in the tree: Stadium sizes its VM quota table from RAM at boot
|
||||
(`stadium_max_vm_count_val`, kmalloc'd, 2026-08-15). The switch table follows the same pattern.
|
||||
**The concrete sizing rule is not yet specified** and is a task-writing prerequisite.
|
||||
|
||||
### XLV.3 — B4 (item 44): payload bound is one block; larger payloads chunk
|
||||
|
||||
> **Follow the block convention the `*.4th` files use.**
|
||||
|
||||
Read as (confirmed by Captain Bob 2026-09-21): a message payload is bounded to **one block =
|
||||
1024 bytes** (64×16, §XLIV.1), which matches §XLIII.6.1's `INPUT_BUFFER_SIZE − 1` drain limit;
|
||||
anything larger is **chunked across messages**. Chunk framing (sequence/last-chunk marking) is
|
||||
a Phase 3 task-writing prerequisite. Note the 64-char line rule (§XLIV.1) governs *capsule
|
||||
source blocks*; whether it also constrains message payload content is **not** ruled here.
|
||||
|
||||
### XLV.4 — Punch list
|
||||
|
||||
- ✅ **Items 27, 32, 44 — SETTLED** (§XLV.1–.3).
|
||||
- ✅ **NEW** — ACK cadence (XLV.1); ACL hook for channel-open policy (XLV.1); dynamic switch-table
|
||||
sizing rule (XLV.2); chunk framing (XLV.3). **SETTLED §XLVI.**
|
||||
- ✅ **Phase 3 task breakdown** — drafted `FABRIC-3.6.md` tasks 3.0–3.11, 2026-09-21.
|
||||
|
||||
## XLVI. §XLV.4 sub-items SETTLED by Captain Bob (2026-09-21) — task 3.0 closed
|
||||
|
||||
All five `FABRIC-3.6.md` task 3.0 sub-items, plus task 3.3's heat-cost design point, ruled in one
|
||||
session (all recommended defaults accepted):
|
||||
|
||||
- **3.0(a) ACK cadence:** channel-open + delivery only, not every common-channel message —
|
||||
confirms §XLV.1's own advised default.
|
||||
- **3.0(b) Channel-open ACL hook location:** a new word in `ACL.4th` itself (e.g.
|
||||
`HERMES-CHANNEL-OPEN?`), extending its scope from per-word ACL to also cover channel-open
|
||||
policy, rather than a separate policy file. Keeps "all policy in `ACL.4th`" as one rule with
|
||||
no exceptions.
|
||||
- **3.0(c) Dynamic switch-table sizing:** mirrors Stadium's `stadium_max_vm_count_val` pattern —
|
||||
RAM-derived at boot, no separate sizing formula.
|
||||
- **3.0(d) Chunk framing:** each chunk carries `(msg_id, seq, is_last)` ahead of up to 1024
|
||||
bytes of payload; receiver reassembles by `msg_id`+`seq` order.
|
||||
- **3.0(e) Drain cadence:** one message per outermost-interpret checkpoint (§XLIII.6.3's own
|
||||
recommendation) — a backlog drains across several checkpoint hits, not in one burst.
|
||||
- **Task 3.3 heat cost per publish:** one message per subscriber (separate heat draw each),
|
||||
matching the existing heat-coupled allocator 1:1 — no refcount machinery to add to the
|
||||
allocator/ledger.
|
||||
|
||||
`FABRIC-3.6.md` task 3.0 is closed by this ruling; tasks 3.1–3.7 may now be written precisely.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,205 @@
|
||||
# FABRIC-3.7.md — Phase 8 PKI: the elevation entrypoint
|
||||
|
||||
**Status: OPEN — design only, no code written or authorized.**
|
||||
|
||||
**CORRECTION (2026-09-22, before any code was written against this document): §2's central
|
||||
claim — that the old `SEND-ELEVATE-REQUEST` passed a raw cross-VM address into `ELEVATE-GRANT`'s
|
||||
`waddr` — is wrong.** Found while starting Part A's implementation: reading the actual deleted
|
||||
source (`git show 3e201c8^:capsules/common/messaging.4th`, block 5040) shows
|
||||
`SEND-ELEVATE-REQUEST` copied the target word's **name as literal character bytes** (via
|
||||
`ELEVATE-REQ-APPEND`'s `CMOVE`) into a scratch buffer, building the text `S" <name-text>" <pk0>
|
||||
<pk1> <pk2> <pk3> ELEVATE-GRANT`, and sent *that whole string* to Hera. When Hera's own
|
||||
interpreter runs `S" <name-text>"`, it allocates a fresh string **in Hera's own memory** and
|
||||
pushes Hera's own valid address — no numeric cross-VM address ever appears anywhere in this
|
||||
flow. §2 was written from `ELEVATE-GRANT`'s signature alone, assuming the caller forwarded a raw
|
||||
address, without first reading how the caller actually built its message. It didn't.
|
||||
|
||||
**What is still real, much narrower than originally claimed:** if a future caller ever spliced
|
||||
*attacker-influenced* text into the name field without checking for an embedded `"` character,
|
||||
that could break out of the `S" ... "` literal early and inject arbitrary FORTH source, executed
|
||||
with Hera's privilege. That's an input-validation discipline question for whoever writes the new
|
||||
caller (validate: no embedded `"`, or just always use a compile-time-fixed literal name, never a
|
||||
runtime-supplied one) — not an architectural cross-VM-memory defect requiring the buffer/message
|
||||
redesign §3 originally called for. **§3's proposed mechanism (Hera-side fixed receive buffer,
|
||||
kernel-constructed integer-literal-only command) is not needed** — the original text-copy
|
||||
design was already safe against the bug as actually diagnosed. Kept below, struck through, for
|
||||
traceability, per this series' own rule against silently rewriting a prior state.
|
||||
|
||||
Successor to
|
||||
`FABRIC-3.5.md`/`FABRIC-3.6.md` (both CLOSED/ARCHIVAL at `v2.1.0`) for exactly one topic: the
|
||||
Ed25519-challenge-response elevation entrypoint that `.claude/CLAUDE.md`'s ACL section names as
|
||||
Phase 8, the last open item in the word-level ACL system. This is a **new document**, not a
|
||||
reopening of `FABRIC-3.6.md` — that document's own close header says future fleet work gets its
|
||||
own document, and this is that.
|
||||
|
||||
**Provenance.** Written 2026-09-22, immediately after `FABRIC-3.6.md`'s close, from a design
|
||||
conversation with Captain Bob about a security concern he raised directly: how to rebuild the
|
||||
elevation entrypoint that Phase 4's Category B strip left dangling, without reopening a hole.
|
||||
The design below was proposed, and Captain Bob asked for it in writing here rather than left
|
||||
only in session memory.
|
||||
|
||||
---
|
||||
|
||||
## 1. What's dangling, and why
|
||||
|
||||
`FABRIC-3.6.md` Phase 4 (Category B strip, 2026-09-22) deleted `capsules/common/messaging.4th`
|
||||
after every FORTH-owned message type had been cut over to kernel-Hermes. One casualty was
|
||||
collateral, not intended: `SEND-ELEVATE-REQUEST` (`messaging.4th` block 5040) was the only
|
||||
caller of both `KH-ELEVATE-SEND` (`src/starkernel/repl.c`) and, transitively, `ELEVATE-GRANT`
|
||||
(`capsules/zuse-eligibility.4th`, blocks 4021–4022). All three still exist in the tree.
|
||||
`ELEVATE-GRANT` is still loaded at boot (`capsules/init.4th:18`). Nothing can call it any more.
|
||||
|
||||
Captain Bob's decision at the time (`FABRIC-3.6.md`'s own Phase 4 entry): leave it unreachable,
|
||||
don't patch a caller back in as part of that strip. Phase 8 builds its own entrypoint instead of
|
||||
resuming this one. **This document is that entrypoint's design.**
|
||||
|
||||
<details>
|
||||
<summary>Original §2/§3 (WRONG — see the correction at the top of this document; kept for
|
||||
traceability, not current design)</summary>
|
||||
|
||||
### 2. The security hole in the old mechanism — found before any code was written
|
||||
|
||||
`ELEVATE-GRANT`'s signature, unchanged since it was written:
|
||||
|
||||
```
|
||||
ELEVATE-GRANT ( waddr wu pk0 pk1 pk2 pk3 -- )
|
||||
```
|
||||
|
||||
`waddr`/`wu` are an address/length pair meant to point at the string naming the word to elevate.
|
||||
`pk0`–`pk3` are the caller's Ed25519 pubkey, packed 8 bytes per cell (`ELEVATE-PUBKEY-UNPACK`,
|
||||
`mama_forth_words.c`).
|
||||
|
||||
**The old `SEND-ELEVATE-REQUEST` computed `waddr` in the *sending* VM's own address space, but
|
||||
`ELEVATE-GRANT` always executes on Hera** (`ELEVATE-GRANT always runs on Hera` — `repl.c`'s own
|
||||
comment on `KH-ELEVATE-SEND`, still there). A word's name string lives in the sending VM's
|
||||
memory. `ELEVATE-GRANT` dereferences `waddr` in Hera's memory. Those are not the same address
|
||||
space by construction — `vaddr_t` is per-VM.
|
||||
|
||||
**Consequence:** whoever controls `waddr` controls what bytes `NAME>XT` reads and resolves as a
|
||||
word name, in Hera's dictionary, not the caller's. This is not "the string might be malformed" —
|
||||
it is a primitive for making Hera's own `ELEVATE-GRANT` grant `ACL-ALLOW!`/`ACL-TTL!` on
|
||||
*whatever dictionary entry the attacker's chosen `waddr` happens to land on*, regardless of what
|
||||
word name the caller claims to be requesting elevation for. A caller who can influence `waddr`
|
||||
at all — not forge a signature, not defeat `zuse_eligibility_is_member()`, just choose a number
|
||||
— has a privilege-escalation primitive against the fleet governor.
|
||||
|
||||
This was never exploited (the entrypoint has had zero live callers since the file that called it
|
||||
was deleted), and is reported here as a design defect found by inspection, not a live incident.
|
||||
|
||||
### 3. The fix: never cross an address, only ever cross bytes
|
||||
|
||||
This project already solved the general version of this problem once, this same session
|
||||
(`FABRIC-3.6.md` tasks 3.8/3.9, the payload-aliasing fix): a kernel-Hermes message's payload
|
||||
must be **copied into the message's own storage**, never a pointer into the sender's memory that
|
||||
might be reused or freed before the receiver drains it. `SkHermesMessage.payload_buf`
|
||||
(`include/starkernel/vm/kernel_hermes.h:160`, `SK_HERMES_CHUNK_MAX_PAYLOAD` = 1024 bytes) is
|
||||
exactly that fix, already built, already proven on all three architectures.
|
||||
|
||||
**The elevation entrypoint's hole is the same defect one level up: an address crossing a
|
||||
boundary it isn't valid on the other side of.** The fix generalizes directly:
|
||||
|
||||
1. **Never send `waddr`/`wu` across the kernel-Hermes boundary.** Send the pubkey (32 bytes,
|
||||
already the right shape for `payload_buf`) and the target word's **name, as literal bytes**,
|
||||
copied inline into the message payload — not an address, the actual characters. This is
|
||||
already how `CONSOLE-CMD-EVENT`'s payload works (a command string's bytes, not a pointer to
|
||||
one), so this isn't a new pattern, it's applying the existing one to the one caller that
|
||||
still passed a raw address.
|
||||
|
||||
2. **On receipt, kernel-Hermes's C drain-checkpoint copies those name bytes into a small,
|
||||
fixed, kernel-owned buffer that already lives in Hera's own VM memory** — a receive-side
|
||||
mirror of the existing send-side pattern (`g_kh_elevate_buf`, `repl.c:445`, is the
|
||||
already-built precedent for "a static buffer this mechanism owns"; this needs its Hera-side
|
||||
counterpart). The buffer's address is a compile-time constant, known to the kernel, never
|
||||
computed from anything the caller supplied.
|
||||
|
||||
3. **The FORTH command handed to `vm_interpret()` on Hera references only that fixed buffer's
|
||||
address and length as plain integer literals.** Both are always kernel-controlled. Neither is
|
||||
ever derived from caller input. `ELEVATE-GRANT` itself does not change — same signature, same
|
||||
`zuse_eligibility_is_member()` check, same `ACL-ALLOW!`/`ACL-TTL!` grant. Policy logic stays
|
||||
in FORTH, per `ACL.4th`'s own rule (no new C primitive for policy) — this fix is entirely
|
||||
about how bytes get from one VM to another, not about who is allowed to grant what.
|
||||
|
||||
**Why this closes the hole structurally, not by validation:** there is no string to sanitize and
|
||||
no address to bounds-check, because the interpreted command never contains anything an attacker
|
||||
touched except opaque data bytes that get copied, never dereferenced as a pointer, by the
|
||||
receiving side. The class of bug (cross-address-space pointer confusion) becomes impossible by
|
||||
construction, the same way `payload_buf` made use-after-free impossible by construction rather
|
||||
than by careful lifetime tracking.
|
||||
|
||||
</details>
|
||||
|
||||
## 2 (corrected). What the old mechanism actually did, and the one real gap in it
|
||||
|
||||
Re-read from the actual deleted source (`git show 3e201c8^:capsules/common/messaging.4th`,
|
||||
blocks 5039–5040): `SEND-ELEVATE-REQUEST ( pk3 pk2 pk1 pk0 waddr wu -- )` used `waddr`/`wu` only
|
||||
to `CMOVE` the target word's **name bytes**, as text, into a scratch buffer
|
||||
(`ELEVATE-REQ-BUF`/`ELEVATE-REQ-APPEND`) it owned — building the literal string `S"
|
||||
<name-text>" <pk0> <pk1> <pk2> <pk3> ELEVATE-GRANT` entirely in the *sending* VM's own memory.
|
||||
Only that finished string — not `waddr` itself — went to `KH-ELEVATE-SEND` and across to Hera.
|
||||
When Hera's interpreter runs `S" <name-text>"`, Hera's own `S"` allocates a fresh string **in
|
||||
Hera's own memory** and pushes Hera's own valid address. `waddr`/`wu` never cross the VM
|
||||
boundary as numbers at any point — only as copied character content. There is no cross-VM
|
||||
pointer dereference anywhere in this flow.
|
||||
|
||||
**The one real, much narrower gap:** the name text is spliced into `S" ... "` with no check for
|
||||
an embedded `"` character. If a future caller ever passed attacker-influenced text as the name
|
||||
(none ever did — the word had zero live callers), a `"` in the name would close the string
|
||||
literal early and let the rest of the name execute as raw FORTH source, with Hera's privilege.
|
||||
This is a caller-discipline / input-validation question, not an architectural defect: either
|
||||
always use a compile-time-fixed name literal at the call site (no runtime input, no risk at
|
||||
all), or validate for an embedded `"` before building the command if a name ever does need to
|
||||
come from something less trusted than the call site's own source code.
|
||||
|
||||
**Net effect on Phase 8 v1's scope:** Part A, as originally conceived in §3 above, is not
|
||||
needed. If a `SEND-ELEVATE-REQUEST` replacement is ever built, it can follow the original
|
||||
text-copy design as-is, with the one-line `"`-check added if and only if the name is ever
|
||||
runtime-supplied rather than a fixed literal. No kernel-Hermes/`repl.c` changes required. Part B
|
||||
(`capsules/zuse.4th`, gating `ZUSE-ELIGIBILITY-ADD`) stands on its own, independently verified,
|
||||
unaffected by this correction.
|
||||
|
||||
## 4. What Phase 8 actually needs to build
|
||||
|
||||
Corrected per §2's re-read above. Concretely, when Phase 8 next picks this up:
|
||||
|
||||
- If a caller into `ELEVATE-GRANT` is ever needed again, rebuild it close to the original
|
||||
`SEND-ELEVATE-REQUEST` shape (`ELEVATE-REQ-BUF`/`ELEVATE-REQ-APPEND`/text-copy into `S" ...
|
||||
"`) — it was already safe. Add the one-line embedded-`"` check only if the name is ever
|
||||
runtime-supplied rather than a call-site literal. No `kernel_hermes.c`/`kernel_hermes.h`/
|
||||
`repl.c` changes needed for this.
|
||||
- `ELEVATE-GRANT` unchanged either way.
|
||||
- **Part B is done** (`capsules/zuse.4th`, committed and three-arch verified this session,
|
||||
2026-09-22) — `ZUSE-ELIGIBILITY-ADD` denied by default, granted only inside `ACL-ZUSE-BOOT`'s
|
||||
authenticated branch. This closes the actual "grant yourself eligibility with no real drive at
|
||||
all" path — a real, independently-confirmed gap, unaffected by this correction.
|
||||
- **Defending against a cloned drive — settled, 2026-09-23: not going to happen, by design.**
|
||||
Today, WIREBIND/MINT trust whatever identity is stored on an attached thumbdrive with no
|
||||
challenge at all (confirmed by grep: no `ed25519_sign`/`ed25519_verify` call anywhere in
|
||||
`capsule_wirebind.c` or `capsule_mint.c`), and the private key seed itself is stored in
|
||||
plaintext on the drive, read in the same devblock as the pubkey/cert. **This is accepted, not
|
||||
a gap.** Captain Bob, directly: *"nothing like a pin or a password or secret code or any
|
||||
bullshit... Everybody has secrets. There's only the drive."* Physical possession of the drive
|
||||
is the entire, deliberate credential model — a byte-for-byte clone being equivalent to the
|
||||
real drive is the accepted design, not a defect to close. A PIN/passphrase second factor was
|
||||
built, live-tested on all three architectures, and fully reverted before commit
|
||||
(`/home/rajames/.claude/plans/jiggly-cuddling-stallman.md`, now marked rejected; memory
|
||||
`feedback_no_knowledge_factor_identity`) — **do not revisit a knowledge-factor approach here.**
|
||||
Any future work in this space needs a fundamentally different mechanism (not something typed
|
||||
and known) or stays an accepted limitation.
|
||||
- **Phase 8 v3, 2026-09-23 — done, a distinct and narrower concern from the item above.** The
|
||||
"accepted limitation" above is about a drive image copied *outside* StarshipOS entirely (e.g.
|
||||
imaged on an external computer) — that's still accepted, unchanged by this item. Captain Bob
|
||||
separately asked to close a narrower, different threat: **cloning a device's block content
|
||||
from *within* StarshipOS's own console**, using its own stock, unpinned words
|
||||
(`<src> BLOCK <dst> BUFFER 1024 MOVE`/`RELOCATE-BLOCK`). That's now closed — `MOVE`/`CMOVE`/
|
||||
`CMOVE>`/`RELOCATE-BLOCK` all refuse a same-VM, cross-device copy, verified live on all three
|
||||
architectures. See `/home/rajames/.claude/plans/jiggly-cuddling-stallman.md`'s "Phase 8 v3"
|
||||
section for the full design and verification record. The identity record itself
|
||||
(seed/pubkey/cert) was already unreachable from FORTH before this — this closes the one real
|
||||
gap the research found: ordinary block content, not the identity record.
|
||||
|
||||
## 5. What this document is not
|
||||
|
||||
Not a reopening of `FABRIC-3.6.md`, not a change to anything currently built, not an
|
||||
authorization to write code. Per this series' own convention: design here, execution gets its
|
||||
own document when the work actually starts, the same relationship `FABRIC-3.5.md` had to
|
||||
`FABRIC-3.6.md`.
|
||||
@@ -7,34 +7,70 @@ longer exists here — see `.claude/CLAUDE.md`'s "On the branch topology" note)
|
||||
|
||||
---
|
||||
|
||||
## Release Versioning Policy (decided 2026-08-28)
|
||||
## 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.
|
||||
|
||||
- **`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. Nothing real-hardware-only is required to cut
|
||||
an `X.0.0`.
|
||||
- **`X.5.0` — Hardware bare-metal release.** A `X.5` release is the real-hardware release:
|
||||
the point where the same story transfers to bare metal on real boards (SER5, RasPi 6,
|
||||
Milk-V, Zynq FPGA). Real-hardware-only work — the per-arch RNG drivers behind the unified
|
||||
entropy entry point, and real-board boot validation — gates `X.5.0`, not `X.0.0`.
|
||||
- **Even major numbers are LTS.** A release whose major number (`X`) is even is a
|
||||
Long-Term-Support release — maintained and supported long-term (bug fixes, security,
|
||||
backports) rather than a point-in-time cut. Odd major numbers are non-LTS development
|
||||
lines.
|
||||
**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.
|
||||
|
||||
Applied to the two planned releases: the **v2.0.0** cut (even major, so LTS) is the QEMU
|
||||
release; **v2.5.0** is the hardware bare-metal release that transfers v2.0.0's QEMU story to
|
||||
real boards. See `FABRIC-2.md` §G for the release-gate punch lists.
|
||||
- **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).** 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 even-minor cut on the same line (each `X.Y.0` here is an LTS point-in-time cut,
|
||||
not a separate dev line):
|
||||
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
|
||||
|
||||
@@ -0,0 +1,963 @@
|
||||
# StarForth v4.0.0 — Primitive Decomposition
|
||||
|
||||
This document assigns every C primitive in StarForth v3 (`admin/LithosAnanake` at `6302dcb`) a fate in
|
||||
StarForth v4.0.0. v4 is built on a 32-instruction core derived from Chuck Moore's F18 (the GA144 node).
|
||||
Everything that is not one of those 32 instructions becomes capsule code, a service message to another
|
||||
node, a memory-mapped register, or is retired.
|
||||
|
||||
The rationale is in `JUSTIFICATION.md`. This document is the specification.
|
||||
|
||||
**Status.** Every colon definition below is written against the ISA in §1 and has been traced by hand,
|
||||
but none has been executed. Each one becomes a POST test target: it is correct when the hosted v4 golden
|
||||
model produces the same results as the v3 C primitive it replaces.
|
||||
|
||||
---
|
||||
|
||||
## Contents
|
||||
|
||||
0. [Fates](#0-fates)
|
||||
1. [The v4 core ISA](#1-the-v4-core-isa)
|
||||
2. [Compiler conventions](#2-compiler-conventions)
|
||||
3. [Decisions](#3-decisions)
|
||||
4. [Foundation layer (new words)](#4-foundation-layer-new-words)
|
||||
5. [Fate of every v3 primitive](#5-fate-of-every-v3-primitive)
|
||||
6. [Messaging between nodes](#6-messaging-between-nodes)
|
||||
7. [Memory-mapped registers](#7-memory-mapped-registers)
|
||||
|
||||
---
|
||||
|
||||
## 0. Fates
|
||||
|
||||
| Code | Fate | Meaning |
|
||||
| --- | --- | --- |
|
||||
| **OP** | Instruction | One of the 32 core opcodes. |
|
||||
| **IN** | Inline macro | A short opcode sequence the compiler places in-line. Never called. Required for anything that touches the return stack, since a call would bury the return address. |
|
||||
| **CAP** | Core capsule | A colon definition in the core capsule, built only from OP, IN, and earlier CAP words. |
|
||||
| **CC** | Compiler capsule | Part of the compiler/interpreter capsule, which runs on the host node only. Mesh nodes receive compiled code. |
|
||||
| **DEV** | Device service | A message to a device node that owns real hardware (console, storage, display, keyboard, log). |
|
||||
| **HERA** | Hera service | A message to the Hera node, which owns lifecycle, identity, capsules, ACLs, and Stadium admission. |
|
||||
| **MM** | Memory-mapped | An `@` or `!` against a hardware register (heat counters, anti-clock, governor, stack pointer). |
|
||||
| **RET** | Retired | A diagnostic of v3's software machine that has no equivalent in v4. |
|
||||
|
||||
---
|
||||
|
||||
## 1. The v4 core ISA
|
||||
|
||||
### 1.1 Machine model
|
||||
|
||||
| Item | v4 node |
|
||||
| --- | --- |
|
||||
| Cell | 32 bits (mesh node). The host node's width is a build parameter; see D-5. |
|
||||
| Addressing | Word-addressed (D-1). |
|
||||
| Registers | `T` (top of data stack), `S` (second), `R` (top of return stack), `P` (program counter), `A` and `B` (address registers). |
|
||||
| Stacks | F18 circular hardware stacks, not visible to code (D-2): data stack `T`, `S` + 8 circular (10 deep), return stack `R` + 8 circular (9 deep). No overflow or underflow; pushing past the bottom silently overwrites the oldest entry. |
|
||||
| Instruction word | Six 5-bit slots, plus 2 spare bits. |
|
||||
|
||||
### 1.2 Instruction word layout
|
||||
|
||||
```
|
||||
31 27 26 22 21 17 16 12 11 7 6 2 1 0
|
||||
+--------+--------+--------+--------+--------+--------+----+
|
||||
| slot 0 | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 | xx |
|
||||
+--------+--------+--------+--------+--------+--------+----+
|
||||
```
|
||||
|
||||
Slots execute left to right. A branch (`jump`, `call`, `next`, `if`, `-if`) takes its target from all
|
||||
bits to the right of its slot, and that target replaces the same low bits of `P` (page-relative, as in
|
||||
the F18). Branches are therefore legal in slots 0–3 only:
|
||||
|
||||
| Branch in slot | Address bits | Reach |
|
||||
| --- | --- | --- |
|
||||
| 0 | 27 | whole address space |
|
||||
| 1 | 22 | 4M words |
|
||||
| 2 | 17 | 128K words |
|
||||
| 3 | 12 | 4K words (node-local) |
|
||||
|
||||
### 1.3 The 32 opcodes
|
||||
|
||||
Opcode numbering follows the F18. Two names differ from Moore's: F18 `-` is renamed `inv` and F18 `or`
|
||||
(which is exclusive-or) is renamed `xor`, so instruction names never collide with FORTH-79 word names.
|
||||
|
||||
| Op | Name | Effect |
|
||||
| --- | --- | --- |
|
||||
| 00 | `;` | Return: `P ← R`, pop `R`. |
|
||||
| 01 | `ex` | Swap `P` and `R` (co-routine / execute). |
|
||||
| 02 | `jump a` | `P ← a`. |
|
||||
| 03 | `call a` | Push `P` to `R`, `P ← a`. |
|
||||
| 04 | `unext` | If `R ≠ 0`: decrement `R`, restart the current instruction word at slot 0. Else pop `R`. |
|
||||
| 05 | `next a` | If `R ≠ 0`: decrement `R`, `P ← a`. Else pop `R`. |
|
||||
| 06 | `if a` | If `T = 0`: `P ← a`. **Does not pop `T`.** |
|
||||
| 07 | `-if a` | If `T ≥ 0` (sign bit clear): `P ← a`. **Does not pop `T`.** |
|
||||
| 08 | `@p` | Push the word at `P`, `P ← P+1` (literal). |
|
||||
| 09 | `@+` | Push the word at `A`, `A ← A+1`. |
|
||||
| 0A | `@b` | Push the word at `B`. |
|
||||
| 0B | `@` | Push the word at `A`. |
|
||||
| 0C | `!p` | Store `T` at `P`, pop, `P ← P+1`. |
|
||||
| 0D | `!+` | Store `T` at `A`, pop, `A ← A+1`. |
|
||||
| 0E | `!b` | Store `T` at `B`, pop. |
|
||||
| 0F | `!` | Store `T` at `A`, pop. |
|
||||
| 10 | `+*` | Multiply step: if bit 0 of `A` is set, `T ← T+S`; then shift `T:A` right one bit. See D-3. |
|
||||
| 11 | `2*` | `T ← T << 1`. |
|
||||
| 12 | `2/` | `T ← T >> 1`, arithmetic. |
|
||||
| 13 | `inv` | `T ← ~T`. |
|
||||
| 14 | `+` | `T ← S + T`, pop. |
|
||||
| 15 | `and` | `T ← S & T`, pop. |
|
||||
| 16 | `xor` | `T ← S ^ T`, pop. |
|
||||
| 17 | `drop` | Pop `T`. |
|
||||
| 18 | `dup` | Push a copy of `T`. |
|
||||
| 19 | `pop` | Pop `R` onto the data stack. |
|
||||
| 1A | `over` | Push a copy of `S`. |
|
||||
| 1B | `a` | Push `A`. |
|
||||
| 1C | `nop` | Nothing. |
|
||||
| 1D | `push` | Pop `T` onto the return stack. |
|
||||
| 1E | `b!` | `B ← T`, pop. |
|
||||
| 1F | `a!` | `A ← T`, pop. |
|
||||
|
||||
Note that `@` and `!` address through `A`, not through `T`. The FORTH words `@` and `!` therefore become
|
||||
`a! @` and `a! !`.
|
||||
|
||||
### 1.4 Side effects that are not instructions
|
||||
|
||||
Execution itself drives the physics. Every retired instruction increments that opcode's heat counter and
|
||||
advances the anti-clock; every `call` increments the heat counter of its target. None of this costs an
|
||||
instruction. Capsule code reads it through the memory-mapped registers in §7.
|
||||
|
||||
---
|
||||
|
||||
## 2. Compiler conventions
|
||||
|
||||
**Literals.** A number in source compiles to `@p` followed by the value in the next word.
|
||||
|
||||
**Capsule `IF`.** Native `if` and `-if` leave the flag on the stack. The capsule-level `IF` consumes it,
|
||||
so the compiler drops it on both paths:
|
||||
|
||||
```
|
||||
IF body THEN → if L1 drop body jump L2
|
||||
L1: drop
|
||||
L2:
|
||||
|
||||
IF body1 ELSE body2 THEN → if L1 drop body1 jump L2
|
||||
L1: drop body2
|
||||
L2:
|
||||
```
|
||||
|
||||
`BEGIN ... WHILE ... REPEAT` and `BEGIN ... UNTIL` expand the same way.
|
||||
|
||||
**`FOR ... NEXT`.** `n FOR ... NEXT` compiles `push` then the body then `next`. The body runs `n+1`
|
||||
times, as on the F18. `FOR ... UNEXT` is the same but the body must fit in one instruction word.
|
||||
|
||||
**Register conventions.** `A` and `B` are caller-saved. A word that uses them says so. Words in this
|
||||
document that clobber `A`: `@ ! +! -! 2@ 2! C@ C! D2/ M* FILL ERASE MOVE COUNT CMOVE CMOVE> BLANK -TRAILING COMPARE SEARCH SCAN SKIP DECIMAL HEX OCTAL UM* * UM/MOD Q.FROM-INT Q.TO-INT Q.* Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS +LOOP J HOLD SIGN # #S . .R U. U.R D. D.R ? DUMP Q.PRINT TYPE SEND RECV`. Words that clobber `B`:
|
||||
`COMPARE SEARCH Q./ Q.EXP Q.SQRT Q.LOG Q.SIN Q.COS <# HOLD SIGN # #S #> . .R U. U.R D. D.R ? DUMP Q.PRINT EMIT CR SPACE SPACES TYPE KEY ?TERMINAL SEND RECV`.
|
||||
|
||||
**Return-stack words** (`>R R> R@ 2>R 2R> 2R@ I J UNLOOP` and the loop runtimes) are always IN.
|
||||
|
||||
---
|
||||
|
||||
## 3. Decisions
|
||||
|
||||
Ruled by Captain Bob, 2026-10-01, except D-4 and D-6, which are deferred to development step 2 (the
|
||||
hosted mesh, `JUSTIFICATION.md` §10) because only the mesh and the RTL depend on them. Where a
|
||||
definition below depends on one, it says so.
|
||||
|
||||
| ID | Decision | Ruling |
|
||||
| --- | --- | --- |
|
||||
| **D-1** | Word addressing (pure Moore) or byte addressing. | **Word addressing.** `C@`/`C!` are CAP; `CELLS` is a no-op. |
|
||||
| **D-2** | Stack depth, and whether the stacks are visible. | **F18 circular stacks, hidden.** Data stack 10 deep (`T`, `S` + 8 circular), return stack 9 deep (`R` + 8 circular), exactly as the F18. No stack pointer is visible to code, so `DEPTH`, `PICK`, `ROLL`, `.S`, `SP@` and `SP!` are **retired everywhere**, host node included. |
|
||||
| **D-3** | Exact `+*` semantics at 32 bits: whether the add carries out of `T` into the shift. | **Plain F18 semantics.** The carry out of `T` is not kept. `UM*` in §4 is written for this and is exact over the full range (revised and proven on the golden model, 2026-10-02). |
|
||||
| **D-4** | Node memory map, including port and register addresses. | **Deferred** to step 2. Symbolic names only for now (§6, §7). |
|
||||
| **D-5** | Host node cell width. | **Match the host CPU:** 32 on a Zynq-7000 (Cortex-A9), 64 on an aarch64 host. The compiler capsule is written width-independent. |
|
||||
| **D-6** | Which heat structures exist in hardware: per-opcode counters only, or also per-call-target and word-to-word transition counters. | **Deferred** to step 2. The golden model implements per-opcode and per-call-target heat in the meantime. |
|
||||
| **D-7** | `ROLL` semantics. | **Moot.** `ROLL` is retired under D-2. |
|
||||
| **D-8** | Q48.16 signedness. | **Signed.** v3's unsigned comparisons and `Q.FROM-INT` clamping are retired. |
|
||||
| **D-9** | Instruction word width on a 64-bit-cell host (ruled 2026-10-02). | **32 bits at every cell width.** Six 5-bit slots plus 2 spare bits, as in §1.2. On a 64-bit host the instruction word is the low 32 bits of the cell and the high half is ignored, so compiled code is identical at both widths. Only data and `@p` literals are a full cell wide. |
|
||||
| **D-10** | Q48.16 width on a 64-bit-cell node (ruled 2026-10-02). | **Two cells at every cell width.** A Q value is a signed double on 32- and 64-bit nodes alike, so every Q word is the same double word at both widths. At 32-bit cells this is bit-for-bit v3's 64-bit Q. At 64-bit cells the low cell is v3's value, and where v3 wraps on Q48.16 overflow (a sum past Q max, `ABS` or `NEG` of Q min) the high cell carries the true result instead. |
|
||||
| **D-11** | `Q./` semantics: division by zero, rounding, overflow (ruled 2026-10-02). | **Saturate and flag; round toward zero; saturate on overflow.** The quotient of `a * 2^16 / b` is rounded toward zero, like `SM/REM`. When it does not fit a Q value it is clamped to Q max or Q min by its sign. Division by zero returns Q max or Q min by the sign of the dividend (`0 / 0` gives 0) and sets the node's `NODE-ERROR` register (§7), which `VM-ERROR?` reads. v3 returned 0 on division by zero and saturated whenever the dividend was 2^48 or more, even when the quotient would have fit. |
|
||||
| **D-12** | Q approximations outside their domain (ruled 2026-10-02). | **Return 0 and set `NODE-ERROR`.** `Q.SQRT` of a negative value and `Q.LOG` of zero or a negative value return 0 and set `NODE-ERROR` (§7), as `Q./` does on division by zero (D-11). v3 returned 0 for `ln(0)` without a flag and read negative arguments as large unsigned values. |
|
||||
| **D-13** | Pictured-output hold buffer: size and errors (ruled 2026-10-03). | **63 characters, as v3; on error set `NODE-ERROR` and drop the character.** The buffer holds 63 characters at either cell width. `HOLD` of a value outside 0–255, or into a full buffer, stores nothing and sets `NODE-ERROR` (§7), which is v3's behaviour (it set its error flag and dropped the character). A full double in base 2 therefore does not fit, as in v3. |
|
||||
|
||||
**Consequences of D-2 that every definition must respect.** The data stack holds 10 items and the
|
||||
return stack 9, and every `call`, `FOR`, `DO` loop frame and `push` uses return-stack slots. Nesting
|
||||
is therefore shallow, and exceeding either depth corrupts silently rather than faulting. Every CAP
|
||||
definition in §4 and §5 must be re-checked against these limits before it is accepted as a POST
|
||||
target; the hand traces so far assumed unbounded stacks.
|
||||
|
||||
---
|
||||
|
||||
## 4. Foundation layer (new words)
|
||||
|
||||
These words do not exist as primitives in v3 but everything else is built on them. They are listed in
|
||||
dependency order.
|
||||
|
||||
```forth
|
||||
\ ---- helpers ------------------------------------------------------------
|
||||
: NIP ( a b -- b ) push drop pop ;
|
||||
: SWAP ( a b -- b a ) over push push drop pop pop ;
|
||||
: OR ( a b -- a|b ) over inv and xor ;
|
||||
: NEGATE ( n -- -n ) inv 1 + ;
|
||||
: ROT ( a b c -- b c a ) push SWAP pop SWAP ;
|
||||
|
||||
\ ---- sign and zero tests (raw branches, labels shown) ------------------
|
||||
: 0< ( n -- flag ) -if L1 drop -1 ; L1: drop 0 ;
|
||||
: 0= ( n -- flag ) if L1 drop 0 ; L1: drop -1 ;
|
||||
|
||||
\ ---- unsigned compare ---------------------------------------------------
|
||||
: U< ( u1 u2 -- flag ) 2DUP xor 0< IF NIP 0< ELSE - 0< THEN ;
|
||||
|
||||
\ ---- multiply (D-3) -----------------------------------------------------
|
||||
\ Plain F18 +* loses the carry out of T, and its shift keeps T's sign bit, so
|
||||
\ the loop is exact only while the multiplicand in S and the running T both
|
||||
\ lie in [-2^(n-2), 2^(n-2)). UM* therefore multiplies by s = u1 2/, which
|
||||
\ always does, starting T at t0 = u2 2/ when u1 is odd, and gets
|
||||
\ hi:lo = t0 + s*u2. Then u1*u2 = 2*(hi:lo) + c_lo + c_hi*2^n, where
|
||||
\ c_lo = u1 & u2 & 1
|
||||
\ c_hi = (u1<0 ? u2 : 0) + (u1 odd and u2<0 ? 1 : 0)
|
||||
\ restore the bits the two halvings dropped and the unsigned reading of
|
||||
\ both top bits. Exact over the full range; proven on the golden model.
|
||||
: UM* ( u1 u2 -- ulo uhi )
|
||||
over 1 and inv 1 + over 2/ and \ u1 u2 t0 t0 = u2 2/ if u1 odd
|
||||
over a! 31 push \ A: u2 R: loop count (FOR's push, early)
|
||||
push over 2/ pop \ u1 u2 s t0 s = u1 2/
|
||||
L: +* unext \ u1 u2 s hi A: lo
|
||||
push drop pop \ u1 u2 hi
|
||||
2* a -if L0 drop 1 + jump L1 L0: drop L1: push \ R: 2hi + top bit of lo
|
||||
over -if L2 drop dup jump L3 L2: drop 0 L3: push \ R: + (u1<0 ? u2 : 0)
|
||||
dup -if L4 drop over 1 and jump L5 L4: drop 0 L5: \ u2<0 ? u1&1 : 0
|
||||
pop + pop + push \ u1 u2 R: hi'
|
||||
and 1 and a 2* + pop ; \ lo' hi'
|
||||
\ u1 and u2 wait on the data stack under the loop (+* touches only T, S and
|
||||
\ A), so the corrections are made after it, with -if in place of 0< calls,
|
||||
\ and the return stack holds at most two temporaries. The loop count is
|
||||
\ pushed before s is made, so the data stack never holds more than four.
|
||||
\ Leaves its caller 6 data cells under its arguments and 6 return entries
|
||||
\ (revised 2026-10-02; the first full-range version parked its three
|
||||
\ corrections on the return stack and left 4).
|
||||
|
||||
\ ---- divide: 32-step restoring division, divisor held in A --------------
|
||||
\ Each step shifts hi:lo left one bit and, when the shifted-out bit of hi was
|
||||
\ set or hi' >= d, subtracts d from hi' and sets the new low bit of lo. The
|
||||
\ unsigned compare is done by sign tests in line (as U< does), and x - d is
|
||||
\ `inv a + inv`, so the loop body makes no calls: it needs one return-stack
|
||||
\ entry beyond its own count.
|
||||
: UM/MOD ( ulo uhi ud -- urem uquot )
|
||||
a! 31 FOR
|
||||
-if L0 \ hi's top bit set:
|
||||
2* over -if L1 drop 1 + jump L2 \ hi' = 2hi + top bit of lo
|
||||
L1: drop L2: push 2* pop \ lo' = 2lo
|
||||
jump SUB \ true hi' >= 2^n > d
|
||||
L0:
|
||||
2* over -if L3 drop 1 + jump L4
|
||||
L3: drop L4: push 2* pop
|
||||
dup a xor -if L5 \ top bits of hi' and d differ:
|
||||
drop -if NOSUB jump SUB \ hi' >= d iff hi' has it
|
||||
L5: drop dup inv a + inv -if L6 \ same: hi' >= d iff hi'-d >= 0
|
||||
drop jump NOSUB
|
||||
L6: push drop pop jump SETBIT
|
||||
SUB: inv a + inv \ hi' - d
|
||||
SETBIT: push 1 + pop \ lo' is even: + 1 sets bit 0
|
||||
NOSUB:
|
||||
NEXT
|
||||
over push push drop pop pop ; \ SWAP in line
|
||||
\ Executed on the golden model (2026-10-02): exact, q*d + r = uhi:ulo with
|
||||
\ r < d, for every uhi < ud at 32- and 64-bit cells. Outside that range
|
||||
\ (uhi >= ud, including ud = 0) the result is unspecified, as in FORTH-79; it
|
||||
\ always terminates after 32 steps. Stack use (D-2): up to 6 data cells may
|
||||
\ lie under the three arguments and up to 6 return-stack entries under its
|
||||
\ return address. For UM* the same limits are 6 and 6. This replaces a
|
||||
\ first version built on U<, 0<, SWAP and OR calls, which was also exact but
|
||||
\ left only 3 and 3 (too few for SM/REM inside /MOD) and stepped about five
|
||||
\ times as many instruction words.
|
||||
|
||||
\ ---- signed division, truncating toward zero (v3 semantics) -------------
|
||||
\ The quotient takes the sign of d xor n, the remainder the sign of d. Sign
|
||||
\ tests are native -if (as in 0<) and NEGATE is in line, so the only calls
|
||||
\ are DNEGATE and UM/MOD, both call-free inside.
|
||||
: SM/REM ( d n -- rem quot )
|
||||
over over xor push \ R: quotient sign (top bit)
|
||||
over push \ R: + remainder sign (top bit of d)
|
||||
-if L0 inv 1 + L0: push \ R: + |n|
|
||||
-if L1 DNEGATE L1: \ |d|
|
||||
pop UM/MOD \ urem uquot
|
||||
pop -if L2 drop push inv 1 + pop jump L3 L2: drop L3:
|
||||
pop -if L4 drop inv 1 + ; L4: drop ;
|
||||
\ Executed on the golden model (2026-10-02): exact whenever the truncated
|
||||
\ quotient fits a signed cell, at 32- and 64-bit cells. Stack use (D-2): up
|
||||
\ to 5 data cells under the three arguments and 3 return-stack entries under
|
||||
\ its return address; /MOD, one call further out, leaves 2. The first
|
||||
\ version, built on ABS, DABS, 0< and NEGATE calls, overflowed the return
|
||||
\ stack inside DABS -> DNEGATE -> D+ -> U> and never returned correctly for
|
||||
\ a negative dividend.
|
||||
```
|
||||
|
||||
`2DUP`, `-`, and `DNEGATE` are defined in §5; the compiler resolves forward references within the
|
||||
core capsule.
|
||||
|
||||
---
|
||||
|
||||
## 5. Fate of every v3 primitive
|
||||
|
||||
Section numbers match the v3 primitive reference.
|
||||
|
||||
### 5.1 Stack
|
||||
|
||||
| Word | Fate | v4 definition / notes |
|
||||
| --- | --- | --- |
|
||||
| `DROP` | OP | `drop` |
|
||||
| `DUP` | OP | `dup` |
|
||||
| `OVER` | OP | `over` |
|
||||
| `SWAP` | CAP | §4. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 7 data cells and 6 return entries. |
|
||||
| `?DUP` | CAP | `if Z dup ; Z: ;` (`dup IF dup THEN` without the extra copy). Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `ROT` | CAP | §4. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 4 return entries; in line, with `SWAP` in line, it calls nothing. |
|
||||
| `-ROT` | CAP | `SWAP push SWAP pop`, `SWAP` in line (`ROT ROT` in one pass). Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 5 return entries. |
|
||||
| `DEPTH` | RET | No visible stack pointer (D-2). |
|
||||
| `PICK` | RET | No visible stack pointer (D-2). |
|
||||
| `ROLL` | RET | No visible stack pointer (D-2); D-7 is moot. |
|
||||
|
||||
### 5.2 Return stack
|
||||
|
||||
| Word | Fate | v4 definition |
|
||||
| --- | --- | --- |
|
||||
| `>R` | IN | `push` — executed on the golden model (2026-10-03) with `R@` and `R>`, including a transcript of the v3 binary. |
|
||||
| `R>` | IN | `pop` — executed on the golden model (2026-10-03), see `>R`. |
|
||||
| `R@` | IN | `pop dup push` — executed on the golden model (2026-10-03), see `>R`. |
|
||||
|
||||
### 5.3 Memory
|
||||
|
||||
| Word | Fate | v4 definition / notes |
|
||||
| --- | --- | --- |
|
||||
| `@` | IN | `a! @` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `!` | IN | `a! !` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `+!` | CAP | `a! @ + !` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `-!` | CAP | `a! NEGATE @ + !`, `NEGATE` in line — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `2@` | CAP | `a! @+ @` (low cell at `addr`, high at `addr+1`, as in v3) — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `2!` | CAP | `a! SWAP !+ !`, `SWAP` in line — executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 7 data cells and 6 return entries. |
|
||||
| `C@` | CAP | See below (D-1). Call-free. Executed on the golden model (2026-10-03). Leaves its caller 8 data cells and 7 return entries. |
|
||||
| `C!` | CAP | See below (D-1). Call-free. Executed on the golden model (2026-10-03). |
|
||||
| `FILL` | CAP | `( baddr u c -- )`: the low byte of `c`. See below. Nothing for `u = 0`; for `u < 0` nothing is written and `NODE-ERROR` is set (v3 read a negative count as a huge unsigned one). Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 5 data cells and 5 return entries. Clobbers `A` and, on error, `B`. |
|
||||
| `MOVE` | CAP | `( src dst u -- )`, bytes: `push over over - -if UP drop pop jump CMOVE> UP: drop pop jump CMOVE` — `CMOVE>` when `src < dst`, else `CMOVE`, so overlapping bytes are read before they are overwritten, as v3's `memmove`. The test is the sign of `src - dst`, in line. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. |
|
||||
| `ERASE` | CAP | `0 FILL`, as `0 jump FILL`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `CELLS` | IN | Empty (word-addressed, D-1); v3 answers 24 for `3 CELLS`. Executed on the golden model (2026-10-03). |
|
||||
|
||||
```forth
|
||||
\ byte access on a word-addressed node, little-endian: four bytes to a cell at
|
||||
\ either cell width (so compiled code is the same, D-9), byte address = 4 * word
|
||||
\ address + byte index. `@` and `!` inside are the opcodes, addressing through A.
|
||||
\ C@ is call-free, like C!: one case per byte position, the cell shifted down
|
||||
\ with a 2/ loop and masked. It replaces a version built on LSHIFT, RSHIFT and
|
||||
\ SWAP calls, which was correct but left its caller 4 return-stack entries.
|
||||
: C@ ( baddr -- c )
|
||||
dup 2/ 2/ a! 3 and \ k A: word address
|
||||
if K0 -1 + if K1 -1 + if K2
|
||||
drop @ 23 FOR 2/ UNEXT 255 and ; \ byte 3
|
||||
K2: drop @ 15 FOR 2/ UNEXT 255 and ; \ byte 2
|
||||
K1: drop @ 7 FOR 2/ UNEXT 255 and ; \ byte 1
|
||||
K0: drop @ 255 and ; \ byte 0
|
||||
|
||||
\ C! is call-free: one case per byte position. The byte is shifted up with a
|
||||
\ 2* loop, that byte of the cell cleared with a constant mask, and the two added.
|
||||
\ It replaces a version built on LSHIFT, RSHIFT, SWAP and OR calls, which was
|
||||
\ correct but left its caller 4 return-stack entries; this leaves 7.
|
||||
: C! ( c baddr -- )
|
||||
dup 2/ 2/ a! 3 and push 255 and pop \ c' k A: word address
|
||||
if K0 -1 + if K1 -1 + if K2
|
||||
drop 23 FOR 2* UNEXT @ 4278190080 inv and + ! ; \ byte 3
|
||||
K2: drop 15 FOR 2* UNEXT @ -16711681 and + ! ; \ byte 2
|
||||
K1: drop 7 FOR 2* UNEXT @ -65281 and + ! ; \ byte 1
|
||||
K0: drop @ -256 and + ! ; \ byte 0
|
||||
|
||||
: FILL ( baddr u c -- )
|
||||
-ROT \ c baddr u
|
||||
-if OK drop drop drop NODE-ERROR b! -1 !b ; \ u < 0
|
||||
OK: if DONE push over over C! 1 + pop -1 + jump OK
|
||||
DONE: drop drop drop ;
|
||||
```
|
||||
|
||||
### 5.4 Arithmetic
|
||||
|
||||
| Word | Fate | v4 definition / notes |
|
||||
| --- | --- | --- |
|
||||
| `+` | OP | `+` |
|
||||
| `-` | CAP | `NEGATE +`, `NEGATE` in line — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `*` | CAP | `UM* drop` — executed on the golden model (2026-10-03) against C's wrapping product. Leaves its caller 6 data cells and 5 return entries. |
|
||||
| `/` | CAP | `/MOD NIP`, as `push S>D pop SM/REM push drop pop` (`/MOD`'s body and `NIP` in line, so it runs no deeper than `/MOD`). Truncates toward zero, as v3. Executed on the golden model (2026-10-03) against C wherever the quotient fits a cell. Leaves its caller 5 data cells and 2 return entries. Division by zero is unspecified, as for `UM/MOD` (§4); v3 reported an error and stopped the line. |
|
||||
| `MOD` `/MOD` `*/` `*/MOD` (§4 versions) | RET | Shadowed duplicates. Only the §6 versions survive. |
|
||||
| `1+` `1-` `2+` `2-` | IN | `1 +`, `-1 +`, `2 +`, `-2 +` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `2*` | OP | `2*` |
|
||||
| `2/` | OP | `2/` |
|
||||
| `ABS` | CAP | `dup 0< IF NEGATE THEN` — executed on the golden model (2026-10-02). |
|
||||
| `NEGATE` | CAP | §4. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `MIN` | CAP | `over over < if B drop drop ; B: drop push drop pop ;` — the smaller, signed. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 6 return entries. |
|
||||
| `MAX` | CAP | `over over < if A drop push drop pop ; A: drop drop ;` — the larger, signed. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 6 return entries. |
|
||||
|
||||
### 5.5 Logic and comparison
|
||||
|
||||
| Word | Fate | v4 definition / notes |
|
||||
| --- | --- | --- |
|
||||
| `AND` | OP | `and` |
|
||||
| `XOR` | OP | `xor` |
|
||||
| `OR` | CAP | §4. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `INVERT` | OP | `inv` |
|
||||
| `NOT` | CAP | `0=` (FORTH-79 logical not, as in v3), as `jump 0=`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `LSHIFT` | CAP | `BEGIN dup WHILE 1- SWAP 2* SWAP REPEAT drop` — executed on the golden model (2026-10-03). |
|
||||
| `RSHIFT` | CAP | `BEGIN dup WHILE 1- SWAP 2/ MSB inv and SWAP REPEAT drop` (clears the sign bit each step; `MSB` is the cell-width top-bit constant) — executed on the golden model (2026-10-03). |
|
||||
| `0=` `0<` | CAP | §4. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `0<>` | CAP | `if Z drop -1 ; Z: ;` (`0= 0=` without calls). Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `0>` | CAP | `-if NN drop 0 ; NN: if Z drop -1 ; Z: ;` — two native sign tests in place of `dup 0< SWAP 0= OR 0=`; correct for the most negative number. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `=` | CAP | `xor 0=` — executed on the golden model (2026-10-02). |
|
||||
| `<>` | CAP | `xor 0<>`, as `xor jump 0<>`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `<` | CAP | `2DUP xor 0< IF drop 0< ELSE - 0< THEN` (overflow-safe) — executed on the golden model (2026-10-02). |
|
||||
| `>` | CAP | `SWAP <`, as `SWAP jump <` with `SWAP` in line. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 6 return entries. |
|
||||
| `<=` | CAP | `> 0=`, as `SWAP < jump 0=`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 6 return entries. |
|
||||
| `>=` | CAP | `< 0=`, as `< jump 0=`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 6 return entries. |
|
||||
| `U<` | CAP | §4. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 7 return entries. |
|
||||
| `U>` | CAP | `SWAP U<` — executed on the golden model (2026-10-02). |
|
||||
| `WITHIN` | CAP | `( n low high -- flag )`: `push over pop < push < inv pop and` — `low <= n < high`, both comparisons signed, which is what v3 computes. It replaces `over - push - pop U<`, the circular form, which answers differently when `low > high` (`5 10 0 WITHIN` is false in v3 and here, true in that form). Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. |
|
||||
| `TRUE` | IN | `-1` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
| `FALSE` | IN | `0` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||
|
||||
### 5.6 Mixed-precision arithmetic
|
||||
|
||||
A double is two 32-bit cells on a mesh node.
|
||||
|
||||
| Word | Fate | v4 definition |
|
||||
| --- | --- | --- |
|
||||
| `M+` | CAP | `S>D D+` — executed on the golden model (2026-10-02). |
|
||||
| `M-` | CAP | `( d n -- d )`: `S>D DNEGATE jump D+`, `S>D` by a sign test in line. `n` is widened before it is negated, so the most negative `n` is subtracted correctly; `NEGATE M+` would add it. Executed on the golden model (2026-10-03) against C and results recorded from the v3 binary (v3 took the double with its low cell on top; the standard order is kept here, as for `M+`). Leaves its caller 5 data cells and 5 return entries. |
|
||||
| `M*` | CAP | `( n1 n2 -- d )`: `over over xor push -if A inv 1 + A: push -if B inv 1 + B: pop UM* pop -if P drop jump DNEGATE P: drop ;` — the unsigned product of the magnitudes, negated when the signs differ; the sign tests are native. Executed on the golden model (2026-10-03) against C and results recorded from the v3 binary. Leaves its caller 6 data cells and 4 return entries. Clobbers `A`. |
|
||||
| `M/MOD` | CAP | `( d n -- rem quot )`: `jump SM/REM`. Truncating, the remainder with the dividend's sign, as v3. v3 took the double with its low cell on top (`dhigh dlow n`), the reverse of what its own `M*` leaves; the standard order is kept here, so `M* ... M/MOD` composes. Executed on the golden model (2026-10-03), including results recorded from the v3 binary with the double's cells exchanged. Leaves its caller 5 data cells and 3 return entries. |
|
||||
| `MOD` | CAP | `/MOD drop`, with `/MOD`'s body in line: `push S>D pop SM/REM drop`. The remainder has the dividend's sign, as v3. Executed on the golden model (2026-10-03) against C and results recorded from the v3 binary. Leaves its caller 5 data cells and 2 return entries. Division by zero is unspecified, as for `/`. |
|
||||
| `/MOD` | CAP | `push S>D pop SM/REM` — executed on the golden model (2026-10-02). |
|
||||
| `*/` | CAP | `*/MOD NIP`, as `push M* pop SM/REM push drop pop`. Executed on the golden model (2026-10-03). Leaves its caller 5 data cells and 2 return entries. |
|
||||
| `*/MOD` | CAP | `( n1 n2 n3 -- rem quot )`: `push M* pop jump SM/REM` — the product is a full double, so the answer is exact whenever the quotient fits a cell. v3 multiplied in one cell at 64-bit cells and so was right only while `n1 * n2` fitted one; the two agree there. Executed on the golden model (2026-10-03) against the identity `quot * n3 + rem = n1 * n2` on every combination of the edge values, and results recorded from the v3 binary. Leaves its caller 5 data cells and 2 return entries. |
|
||||
|
||||
### 5.7 Double-cell numbers
|
||||
|
||||
| Word | Fate | v4 definition |
|
||||
| --- | --- | --- |
|
||||
| `S>D` | CAP | `dup 0<` — executed on the golden model (2026-10-02). |
|
||||
| `D+` | CAP | `push over push push drop pop over over xor -if L1 drop + -if C1 jump C0 L1: drop over -if L2 drop + jump C1 L2: drop + C0: pop pop + ; C1: pop pop + 1 + ;` — call-free. The carry out of the low cells is found by sign tests: if their top bits differ, there is a carry exactly when the sum's top bit is clear; if they match, exactly when both are set. Executed on the golden model (2026-10-02); leaves its caller 6 data cells under its arguments and 5 return entries. Replaces `push SWAP push over + 2DUP U> ROT drop NEGATE pop pop + +`, which was exact but left 1 return entry. |
|
||||
| `DNEGATE` | CAP | `inv over if L1 drop push inv 1 + pop ; L1: drop 1 + ;` — call-free: `~d + 1`, carrying into the high cell exactly when the low cell is 0. Executed on the golden model (2026-10-02). Replaces `inv SWAP inv SWAP 1 0 D+`, which left a caller no return-stack room, so `DABS` could not run at all. |
|
||||
| `D-` | CAP | `DNEGATE D+` — executed on the golden model (2026-10-02). |
|
||||
| `DABS` | CAP | `dup 0< IF DNEGATE THEN` — executed on the golden model (2026-10-02). |
|
||||
| `D0=` | CAP | `OR 0=` — executed on the golden model (2026-10-02). |
|
||||
| `D0<` | CAP | `NIP 0<`, as `push drop pop jump 0<`. Executed on the golden model (2026-10-03), including results recorded from the v3 binary. |
|
||||
| `D=` | CAP | `D- D0=` — executed on the golden model (2026-10-02). |
|
||||
| `D<` | CAP | `ROT 2DUP = IF 2DROP U< ELSE SWAP < NIP NIP THEN` — executed on the golden model (2026-10-02). |
|
||||
| `(D<)` | CAP | `( d1 d2 -- d1 d2 flag )`, call-free; internal to `DMAX` and `DMIN`. Copies of the high cells go on top; if they differ the flag comes from them, else from the low cells unsigned. The value tested always has its top bit set exactly when `d1 < d2`: `ah` (high signs differ), `ah - bh` (agree), `bl` (low top bits differ), `al - bl` (agree); `x - y` with `y` on top is `push inv pop + inv`. `dup push push over pop over over xor if TIE drop over over xor -if HS drop drop jump S1 HS: drop push inv pop + inv S1: -if N1 drop -1 jump D1 N1: drop 0 D1: pop SWAP ; TIE: drop drop drop dup push push over pop over over xor -if LS drop NIP jump S2 LS: drop push inv pop + inv S2: -if N2 drop -1 jump D2 N2: drop 0 D2: pop pop ROT ;` with `SWAP`, `NIP` and `ROT` in line. Executed on the golden model (2026-10-02). |
|
||||
| `DMAX` | CAP | `(D<) if L drop push push drop drop pop pop ; L: drop drop drop ;` Executed on the golden model (2026-10-02). Replaces `2OVER 2OVER D< IF 2SWAP THEN 2DROP`, which needs 8 data cells plus `D<`'s 2: the whole 10-deep data stack, so it failed whenever the caller held anything at all (D-2). |
|
||||
| `DMIN` | CAP | `(D<) if L drop drop drop ; L: drop push push drop drop pop pop ;` Executed on the golden model (2026-10-02); replaces `2OVER 2OVER D< 0= IF 2SWAP THEN 2DROP` for the same reason as `DMAX`. |
|
||||
| `D2*` | CAP | `2* over -if P drop 1 + jump J P: drop J: push 2* pop` — the low cell's top bit enters the high cell; a native sign test in place of `over 0< NEGATE OR`. Executed on the golden model (2026-10-03), including results recorded from the v3 binary. |
|
||||
| `D2/` | CAP | `push a! 0 pop +* push drop a pop` — one `+*` with `S = 0` is an exact arithmetic right shift of `T:A`, as in `Q.FROM-INT`. It replaces `dup 1 and push 2/ SWAP 1 RSHIFT pop IF MSB OR THEN SWAP`. Executed on the golden model (2026-10-03), including results recorded from the v3 binary. Clobbers `A`. |
|
||||
| `2DROP` | IN | `drop drop` — executed on the golden model (2026-10-03). |
|
||||
| `2DUP` | IN | `over over` — executed on the golden model (2026-10-03). |
|
||||
| `2SWAP` | CAP | `ROT push ROT pop`, with `ROT` and `SWAP` in line so it makes no calls. Executed on the golden model (2026-10-02). Called, `ROT` and `SWAP` left its caller 2 return entries; in line, 4. |
|
||||
| `2OVER` | CAP | `push push 2DUP pop pop 2SWAP` — executed on the golden model (2026-10-02). |
|
||||
| `2ROT` | CAP | `push push 2SWAP pop pop jump 2SWAP`. Executed on the golden model (2026-10-03), including a result recorded from the v3 binary. With six cells of its own it leaves its caller 3 data cells and 1 return entry. |
|
||||
| `2>R` | IN | `SWAP push push` — executed on the golden model (2026-10-03) with `2R@` and `2R>`; the high cell is on top of the return stack, as in v3. |
|
||||
| `2R>` | IN | `pop pop SWAP` — see `2>R`. |
|
||||
| `2R@` | IN | `pop pop 2DUP push push SWAP` — see `2>R`. |
|
||||
|
||||
### 5.8 Number formatting and output
|
||||
|
||||
All output reaches the console through `EMIT` (DEV).
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `<#` `#` `#S` `HOLD` `SIGN` `#>` | CAP | Pictured output over `UM/MOD` and a 64-byte hold buffer, filled backwards from its end `HEND` through the pointer `HLD`. Standard stack effects (v3 took its double low cell on top, and its tolerant `#>` popped `ud` only if present; neither is kept). Otherwise v3's behaviour: digits `0`–`9` then `A`–`Z`; `BASE` outside 2–36 reads as 10; 63 characters; `HOLD` of a value outside 0–255 or into a full buffer stores nothing and sets `NODE-ERROR` (D-13). Definitions below. Executed on the golden model (2026-10-03) against a C reference in bases 2, 3, 8, 10, 16, 36 and four invalid ones. `<# #S #>` leaves its caller 4 data cells and 3 return entries; the signed picture `dup push ABS 0 <# #S pop SIGN #>` leaves 2 return entries. `#`, `#S`, `HOLD` and `SIGN` clobber `A` and `B`. |
|
||||
| `HLD` | CAP | Variable: byte address of the first held character. `HLD ( -- addr )` is the variable's word address, a literal. Not a v3 word. Executed on the golden model (2026-10-03): after `<#` and each `HOLD` or `#`, `HLD @` is the address `#>` returns. |
|
||||
| `.` `.R` `U.` `U.R` `D.` `D.R` | CAP | Built on pictured output and `TYPE`; definitions below. As v3: the number in the current base, then one space; the `.R` words right-justify it in `width` columns first, print a wider number whole, and pad nothing for `width <= 0`. (The space after the `.R` words is not FORTH-79; it is what v3 prints.) Where v4 parts from v3: a double is `( lo hi )`, where v3's `D.` took the low cell on top; `D.` prints the whole double, where v3 printed `DOUBLE-OVERFLOW` unless it fitted one signed cell (they agree whenever it does); printing honours the `BASE` variable, where v3 printed in a host copy that only `DECIMAL`, `HEX` and `OCTAL` set, so `n BASE !` changed v3's input base but not its output; and the number is built in the 63-character hold buffer, so a longer one — a 64-bit cell in base 2, a large double in a small base — loses its leading characters and sets `NODE-ERROR` (D-13), where v3 printed from a private 80-character buffer. Executed on the golden model (2026-10-03) against a C reference in the ten bases of the pictured-output test and eleven field widths, and against five transcripts of the v3 binary (a sixth, the extreme cells, at 64-bit cells). Each leaves its caller 4 data cells; `U.` and `U.R` leave 3 return entries, the signed words 2, their sign waiting on the return stack. All clobber `A` and `B`. |
|
||||
| `(W)` | CAP | Variable: the field width of the `.R` words. Like `BASE` and `HLD`, it lives in node memory, so the width is on neither stack while the picture runs. |
|
||||
| `.S` | RET | No visible stack pointer (D-2). |
|
||||
| `?` | CAP | `( addr -- )`: `a! @ jump .` — the cell at word address `addr` (D-1), printed as `.` prints it. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 2 return entries. Clobbers `A` and `B`. |
|
||||
| `DUMP` | CAP | `( baddr u -- )`: `u` bytes from byte address `baddr`, sixteen to a line, in v3's format: the address in hex, `": "`, each byte as two hex digits and a space (three spaces where the line runs out), `" \|"`, the bytes as characters with `.` for anything outside 32–126, `"\|"` and a new line. Always hex, whatever `BASE` is, and `BASE` is put back. The address is two hex digits per byte of a cell — 8 at 32-bit cells, 16 at 64, which is v3's width. Nothing for `u = 0`; for `u < 0` nothing is printed and `NODE-ERROR` is set, as `TYPE` does, where v3 raised its error flag. Addresses are not range-checked (open, `node.h`). Definition below. Executed on the golden model (2026-10-03) against a C reference at six alignments and every length 0–50, and at 64-bit cells against a transcript of the v3 binary, byte for byte. Leaves its caller 4 data cells and 4 return entries. Clobbers `A` and `B`. |
|
||||
| `(DP)` | CAP | Variable, 4 cells: `DUMP`'s saved `BASE`, address, count and column. |
|
||||
| `BASE` | CAP | Variable. `BASE ( -- addr )` is its word address (D-1), a literal. Executed on the golden model (2026-10-03). Storing to it changes what is printed, which it did not in v3 (see `.`). |
|
||||
| `DECIMAL` `HEX` `OCTAL` | CAP | `10 BASE !`, `16 BASE !`, `8 BASE !`, with `!` in line as `a! !`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Each leaves its caller 8 data cells and 8 return entries. Clobber `A`. |
|
||||
|
||||
```forth
|
||||
\ pictured output. HEND is the byte address just past the hold buffer.
|
||||
: (BASE) ( -- b ) BASE b! @b dup -2 + -if L1 drop drop 10 ;
|
||||
L1: drop dup -37 + -if L2 drop ; L2: drop drop 10 ;
|
||||
: <# ( -- ) HEND HLD b! !b ;
|
||||
: HOLD ( c -- ) dup -256 and if OKC drop jump ERR
|
||||
OKC: drop HLD b! @b -(HEND-62) + -if ROOM drop
|
||||
ERR: drop NODE-ERROR b! -1 !b ;
|
||||
ROOM: drop HLD b! @b -1 + dup !b jump C!
|
||||
: SIGN ( n -- ) -if L1 drop 45 jump HOLD L1: drop ;
|
||||
: # ( ud1 -- ud2 )
|
||||
0 (BASE) UM/MOD -ROT \ qhi lo rem1
|
||||
(BASE) UM/MOD -ROT \ qlo qhi rem
|
||||
dup -10 + -if L1 drop 48 + jump L2 L1: drop 55 + L2: jump HOLD
|
||||
: #S ( ud -- 0 0 ) L: # over over OR if L1 drop jump L L1: drop ;
|
||||
: #> ( ud -- baddr u ) drop drop HLD b! @b HEND over push inv pop + inv ;
|
||||
```
|
||||
|
||||
`#` divides the double by the base in two `UM/MOD` steps, the high cell first and its remainder
|
||||
leading the low cell; the last remainder is the digit. `HOLD`, `SIGN` and `#` end with a jump to the
|
||||
next word rather than a call, so `C!` returns straight to their caller and two return-stack entries
|
||||
are saved. `#` keeps the high quotient under the second division on the data stack, not on the
|
||||
return stack. `-ROT` is in line (`SWAP push SWAP pop`).
|
||||
|
||||
```forth
|
||||
\ number output. ABS is in line: -if A inv 1 + A:
|
||||
: .R ( n width -- )
|
||||
(W) b! !b dup push ABS 0 <# #S pop SIGN #> \ baddr u
|
||||
TAIL: (W) b! @b -if POS drop jump OUT \ width < 0
|
||||
POS: over - SPACES \ width - u spaces
|
||||
OUT: TYPE jump SPACE
|
||||
: . ( n -- ) 0 jump .R
|
||||
: U.R ( u width -- ) (W) b! !b 0 <# #S #> jump TAIL
|
||||
: U. ( u -- ) 0 jump U.R
|
||||
: D.R ( d width -- ) (W) b! !b dup push -if A DNEGATE A: <# #S pop SIGN #> jump TAIL
|
||||
: D. ( d -- ) 0 jump D.R
|
||||
```
|
||||
|
||||
```forth
|
||||
\ DUMP. DIGITS is two per byte of a cell.
|
||||
: (DB) ( -- c ) (DP) 1 + b! @b (DP) 3 + b! @b + jump C@ \ the byte in this column
|
||||
: DUMP ( baddr u -- )
|
||||
-if OK drop drop NODE-ERROR b! -1 !b ; \ u < 0
|
||||
OK: (DP) 2 + b! !b (DP) 1 + b! !b \ count, address
|
||||
BASE b! @b (DP) b! !b 16 BASE b! !b \ hex
|
||||
LINE: (DP) 2 + b! @b if DONE drop
|
||||
(DP) 1 + b! @b 0 <# DIGITS (DP) 3 + b! !b \ the address
|
||||
AD: # (DP) 3 + b! @b -1 + dup !b if ADX drop jump AD
|
||||
ADX: drop #> TYPE 58 EMIT SPACE
|
||||
0 (DP) 3 + b! !b \ the bytes in hex
|
||||
HX: (DP) 2 + b! @b (DP) 3 + b! @b inv + -if HAVE \ count - column - 1
|
||||
drop SPACE SPACE SPACE jump HN
|
||||
HAVE: drop (DB) 0 <# # # #> TYPE SPACE
|
||||
HN: (DP) 3 + b! @b 1 + dup !b -16 + -if HXX drop jump HX
|
||||
HXX: drop SPACE 124 EMIT
|
||||
0 (DP) 3 + b! !b \ the bytes as characters
|
||||
CH: (DP) 2 + b! @b (DP) 3 + b! @b inv + -if HAVC jump CHX
|
||||
HAVC: drop (DB)
|
||||
dup -32 + -if GE drop drop 46 jump EM
|
||||
GE: drop dup -127 + -if BIG drop jump EM
|
||||
BIG: drop drop 46
|
||||
EM: EMIT
|
||||
(DP) 3 + b! @b 1 + dup !b -16 + -if CHX drop jump CH
|
||||
CHX: drop 124 EMIT CR
|
||||
(DP) 1 + b! @b 16 + !b \ next line
|
||||
(DP) 2 + b! @b -16 + -if MORE jump DONE
|
||||
MORE: !b jump LINE
|
||||
DONE: drop (DP) b! @b BASE b! !b ;
|
||||
```
|
||||
|
||||
Everything `DUMP` keeps between words — the address, the count, the column and the saved `BASE` — is
|
||||
in the variable `(DP)`, so the stacks carry only what each picture and `TYPE` need, and no loop count
|
||||
sits on the return stack across a call.
|
||||
|
||||
There is one picture for signed singles, one for unsigned and one for doubles; each plain word is
|
||||
its `.R` word with a width of 0, entered by a jump, and all six share one tail, which ends in a jump
|
||||
to `SPACE`. The width is tested for sign before `width - u`, which could wrap for a very negative
|
||||
width and print a flood of spaces.
|
||||
|
||||
### 5.9 Strings, parsing, and input
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `COUNT` | CAP | `( baddr -- baddr+1 c )`: `dup C@ push 1 + pop`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 7 data cells and 6 return entries. Clobbers `A`. |
|
||||
| `CMOVE` | CAP | `( src dst u -- )`, low byte first. See below. Nothing for `u = 0`; for `u < 0` nothing is written and `NODE-ERROR` is set, where v3 raised its error flag. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 5 data cells and 5 return entries. |
|
||||
| `CMOVE>` | CAP | `( src dst u -- )`, high byte first. See below. Errors as `CMOVE`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. |
|
||||
| `BLANK` | CAP | `32 FILL`, as `32 jump FILL`. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. v3's counted-string auto-detection is not kept. |
|
||||
| `-TRAILING` | CAP | `( baddr u -- baddr u' )`. See below. A negative count reads as 0, as v3. v3's counted-string auto-detection is not kept. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 6 data cells and 6 return entries. |
|
||||
| `COMPARE` | CAP | `( a1 u1 a2 u2 -- n )`: `-1`, `0` or `1` by the first differing byte, unsigned, else the shorter string is less. See below. Negative counts read as 0, as v3. v3's counted-string auto-detection is not kept. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 4 data cells and 5 return entries. Clobbers `A` and `B`. |
|
||||
| `SEARCH` | CAP | `( a1 u1 a2 u2 -- a3 u3 flag )`: the rest of string 1 from the first place string 2 occurs and `-1`, or string 1 and `0`; an empty string 2 is found at the start. See below; the inner comparison is in line, not a call to `COMPARE`. Same notes. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 3 data cells and 5 return entries. Clobbers `A` and `B`. |
|
||||
| `SCAN` `SKIP` | CAP | `( baddr u c -- baddr' u' )`: `SCAN` stops at the first byte equal to the low byte of `c` (or at the end, with `u' = 0`); `SKIP` stops at the first byte not equal to it. See below. A negative count reads as 0, as v3. v3's counted-string auto-detection is not kept. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Each leaves its caller 5 data cells and 5 return entries. Clobber `A`. |
|
||||
| `(S)` | CAP | Variable, 4 cells: `COMPARE`'s two lengths; `SEARCH`'s string 2 and the whole of string 1. |
|
||||
| `BL` | IN | `32` — executed on the golden model (2026-10-04). |
|
||||
| `EXPECT` `QUERY` | CC | Built on `KEY` (DEV); source in `v4/capsule/input.v4`. `EXPECT ( baddr u -- )` as v3's `fgets`: at most `u-1` characters, stopping after a new-line (taken, not stored), then a zero; `SPAN` is the count; a line that does not fit leaves its tail to be read next; `u < 0` does nothing and sets `NODE-ERROR`. `QUERY` is `TIB #TIB EXPECT 0 >IN !` with `#TIB` 1025, as v3. Executed on the golden model's host node (2026-10-04), including results recorded from the v3 binary. Both part from FORTH-79, which takes up to `u` characters in `EXPECT` and 80 in `QUERY`: reported 2026-10-04, awaiting a ruling. `EXPECT` leaves its caller 6 data cells and 4 return entries. |
|
||||
| `SPAN` `TIB` `>IN` `SOURCE` | CC | Interpreter state on the host node. `TIB` is the buffer's byte address and `>IN` and `SPAN` their variables' word addresses, all in line; `SOURCE ( -- baddr u )` is `TIB` and `SPAN @`, a negative `SPAN` read as 0. Executed on the golden model's host node (2026-10-04). |
|
||||
| `WORD` `ENCLOSE` | CC | Parser; source in `v4/capsule/input.v4`. `WORD ( c -- baddr )`, as v3: the next word of `TIB` delimited by `c`, as a counted string in a 64-byte buffer with a zero after it; delimiters before it are skipped and so are all those after it; a longer word is cut to 62 characters; at the end of the line the count is 0. `ENCLOSE ( baddr c -- baddr n1 n2 n3 )` on a zero-terminated string. Executed on the golden model's host node (2026-10-04) against C and the counts and `>IN` values recorded from the v3 binary (the text in v3's own buffer was corrupt in those runs: `hello` came back as `hlllo`). `WORD` parts from FORTH-79 in skipping every trailing delimiter, storing a zero where the standard stores the delimiter, and cutting at 62: reported 2026-10-04, awaiting a ruling. `WORD` leaves its caller 4 data cells and 3 return entries. |
|
||||
| `NUMBER` `CONVERT` | CC | FORTH-79, not v3 (ruled 2026-10-04); source in `v4/capsule/input.v4`. `CONVERT ( d1 baddr1 -- d2 baddr2 )` takes the characters from `baddr1 + 1` on as digits in the current `BASE`, in either case, accumulating each into the double after multiplying it by `BASE`, and stops at the first that is not one; the double is `( lo hi )`. v3's was base 10 only, started at `baddr1`, and took the double low cell on top. `NUMBER ( baddr -- d )` is the counted string as a signed double in the current `BASE`, with an optional leading minus; anything else gives 0 and sets `NODE-ERROR`. v3's returned `( n flag )` in base 10. Executed on the golden model's host node (2026-10-04) against C in eight bases, including a digit that carries out of the low cell. `NUMBER` leaves its caller 3 data cells and 3 return entries. |
|
||||
| `S"` `(s")` `[']` | CC | Compiler words. |
|
||||
| `LITERAL` `[LITERAL]` (placeholders) | RET | The working `LITERAL` is in §5.17. |
|
||||
|
||||
```forth
|
||||
\ SWAP and the subtractions ( - ) are in line.
|
||||
: CMOVE ( src dst u -- )
|
||||
-if OK drop drop drop NODE-ERROR b! -1 !b ; \ u < 0
|
||||
OK: if DONE push \ src dst R: u
|
||||
over C@ over C! 1 + push 1 + pop pop -1 + jump OK
|
||||
DONE: drop drop drop ;
|
||||
|
||||
: CMOVE> ( src dst u -- )
|
||||
-if OK drop drop drop NODE-ERROR b! -1 !b ; \ u < 0
|
||||
OK: if DONE -1 + push \ src dst R: i = u - 1
|
||||
over pop dup push + C@ \ src dst c
|
||||
over pop dup push + C! \ src dst
|
||||
pop jump OK
|
||||
DONE: drop drop drop ;
|
||||
|
||||
: -TRAILING ( baddr u -- baddr u' )
|
||||
-if L drop 0 ; \ u < 0
|
||||
L: if DONE over over + -1 + C@ -32 + if SP drop ; \ not a space
|
||||
SP: drop -1 + jump L
|
||||
DONE: ;
|
||||
|
||||
: COMPARE ( a1 u1 a2 u2 -- n )
|
||||
-if A drop 0 A: (S) 1 + b! !b \ a1 u1 a2
|
||||
SWAP -if B drop 0 B: dup (S) b! !b \ a1 a2 u1
|
||||
(S) 1 + b! @b \ a1 a2 u1 u2
|
||||
over over - -if GE drop drop jump M \ the smaller
|
||||
GE: drop push drop pop
|
||||
M: \ a1 a2 m
|
||||
L: if EQ push \ a1 a2 R: m
|
||||
over C@ over C@ - if SAME \ c1 - c2
|
||||
-if GT drop drop drop pop drop -1 ;
|
||||
GT: drop drop drop pop drop 1 ;
|
||||
SAME: drop 1 + push 1 + pop pop -1 + jump L
|
||||
EQ: drop drop drop (S) b! @b (S) 1 + b! @b - \ u1 - u2
|
||||
-if NN drop -1 ;
|
||||
NN: if ZZ drop 1 ;
|
||||
ZZ: ;
|
||||
|
||||
: SEARCH ( a1 u1 a2 u2 -- a3 u3 flag )
|
||||
-if A drop 0 A: (S) 1 + b! !b (S) b! !b \ a1 u1 (S): a2 u2
|
||||
-if B drop 0 B: dup (S) 3 + b! !b over (S) 2 + b! !b \ a u (S)+2: a1 u1
|
||||
L: dup (S) 1 + b! @b - -if TRY \ u - u2
|
||||
drop drop drop (S) 2 + b! @b (S) 3 + b! @b 0 ; \ not found
|
||||
TRY: drop over (S) b! @b (S) 1 + b! @b \ a u p q k
|
||||
I: if MATCH push \ a u p q R: k
|
||||
over C@ over C@ xor if SAME
|
||||
drop drop drop pop drop push 1 + pop -1 + jump L \ next place
|
||||
SAME: drop 1 + push 1 + pop pop -1 + jump I
|
||||
MATCH: drop drop drop -1 ;
|
||||
|
||||
: SCAN ( baddr u c -- baddr' u' )
|
||||
255 and push -if L drop 0 \ baddr u R: c
|
||||
L: if DONE over C@ pop dup push xor if FOUND
|
||||
drop push 1 + pop -1 + jump L
|
||||
FOUND: drop
|
||||
DONE: pop drop ;
|
||||
|
||||
: SKIP ( baddr u c -- baddr' u' )
|
||||
255 and push -if L drop 0 \ baddr u R: c
|
||||
L: if DONE over C@ pop dup push xor if SAME drop jump DONE
|
||||
SAME: drop push 1 + pop -1 + jump L
|
||||
DONE: pop drop ;
|
||||
```
|
||||
|
||||
### 5.10 Terminal I/O
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `EMIT` | DEV | Console service: one-character message. Until the mesh exists it is a store to the `CONSOLE-TX` register (§7): `CONSOLE-TX b! !b`. As in v3, the low byte of the cell is the character. Executed on the golden model (2026-10-03). Clobbers `B`. |
|
||||
| `KEY` | DEV | Console service: blocking receive. Until the mesh exists it reads the `CONSOLE-STATUS` and `CONSOLE-RX` registers (§7): `L: CONSOLE-STATUS b! @b if WAIT drop CONSOLE-RX b! @b ; WAIT: drop jump L` — it asks whether a character is pending until one is, then takes it. The character is 0–255. v3's `KEY` returned −1 at the end of its input; here there is no end of input, and `KEY` waits. Executed on the golden model (2026-10-03), including a transcript of the v3 binary, and shown still waiting after 5000 instruction words with nothing pending. Leaves its caller 9 data cells and 8 return entries. Clobbers `B`. |
|
||||
| `?TERMINAL` | DEV | Console service: non-blocking status. Until the mesh exists: `CONSOLE-STATUS b! @b` — −1 when a character is pending, 0 when not, as v3; it takes nothing. Executed on the golden model (2026-10-03). Clobbers `B`. |
|
||||
| `TYPE` | CAP | `( baddr u -- )`. Loop of `C@ EMIT`, or one string message to the console node: `-if OK drop drop NODE-ERROR b! -1 !b ; OK: if DONE over C@ EMIT push 1 + pop -1 + jump OK DONE: drop drop ;` — nothing for `u = 0`; for `u < 0` nothing is printed and `NODE-ERROR` is set, where v3 printed nothing and raised its error flag. The address range is not checked (out-of-range addressing is still open, `node.h`). Executed on the golden model (2026-10-03). Leaves its caller 6 data cells and 6 return entries. Clobbers `A` and `B`. |
|
||||
| `CR` | CAP | `10 EMIT`, as `10 jump EMIT` — executed on the golden model (2026-10-03). Character 10, as v3; the console turns it into a new line. |
|
||||
| `SPACE` | CAP | `BL EMIT`, as `32 jump EMIT` — executed on the golden model (2026-10-03). |
|
||||
| `SPACES` | CAP | `( n -- )`: `-if L drop ; L: if DONE SPACE -1 + jump L DONE: drop ;` — `n` spaces, none for `n <= 0`, as v3. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. Leaves its caller 7 data cells and 7 return entries. Clobbers `B`. |
|
||||
| `."` `(do-string)` | CC | Compiler words. |
|
||||
|
||||
### 5.11 Blocks and mass storage
|
||||
|
||||
The storage service belongs to the Artemis role, now a device node.
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `BLOCK` `BUFFER` `UPDATE` `SAVE-BUFFERS` `EMPTY-BUFFERS` `FLUSH` | DEV | Block-service messages. Buffers live in the requesting node's RAM or in DDR. |
|
||||
| `LOAD` `THRU` `-->` | CC | The interpreter reads blocks through the service. |
|
||||
| `LIST` | CAP | `BLOCK` plus `TYPE`. |
|
||||
| `SCR` | CAP | Variable. |
|
||||
| `BLK-CONFIRM-FORMAT` `RELOCATE-BLOCK` | DEV | Owner-only storage messages. |
|
||||
| `BLK-ACL-ALLOW@` `BLK-ACL-ALLOW!` `BLK-ACL-TTL@` `BLK-ACL-TTL!` | HERA | ACL state is held by Hera. |
|
||||
| `BLK-OWNER@` | HERA | Read-only, as in v3. |
|
||||
| `BLK-ATTACH` | RET | Took a raw host pointer. Replaced by an attach message from the device node. |
|
||||
|
||||
### 5.12 Dictionary space
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `HERE` `ALIGN` `ALLOT` `,` `C,` `2,` `PAD` `LATEST` | CC | The dictionary lives on the host node. |
|
||||
| `SP@` `SP!` | RET | No visible stack pointer (D-2). |
|
||||
|
||||
### 5.13 Dictionary manipulation
|
||||
|
||||
| Word | Fate |
|
||||
| --- | --- |
|
||||
| `'` `FIND` `SMUDGE` `HIDDEN` `>BODY` `>NAME` `NAME>` `>LINK` `LINK>` `CFA` `LFA` `NFA` `PFA` `TRAVERSE` `INTERPRET` | CC |
|
||||
|
||||
### 5.14 Vocabularies
|
||||
|
||||
| Word | Fate |
|
||||
| --- | --- |
|
||||
| `VOCABULARY` `DEFINITIONS` `CONTEXT` `CURRENT` `FORTH` `ORDER` `(FIND)` | CC |
|
||||
|
||||
### 5.15 System
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `(` `\` | CC | `(` is `41 WORD drop`; `\` is `SPAN @ >IN !`. In `v4/capsule/input.v4` under the names `PAREN` and `BACKSLASH`. Executed on the golden model's host node (2026-10-04). |
|
||||
| `EXECUTE` | CAP | `push ;` (tail-jumps to the xt; the xt returns to `EXECUTE`'s caller) |
|
||||
| `NOP` | OP | `nop` |
|
||||
| `QUIT` `ABORT` `ABORT"` `(ABORT")` `COLD` `WARM` | CC | |
|
||||
| `BYE` `REBOOT` | HERA | |
|
||||
| `SAVE-SYSTEM` | HERA | Snapshot becomes a capsule-image request. |
|
||||
| `WORDS` `VLIST` `SEE` | CC | |
|
||||
| `PAGE` | DEV | |
|
||||
| `79-STANDARD` | CC | |
|
||||
|
||||
### 5.16 Line editor
|
||||
|
||||
| Word | Fate |
|
||||
| --- | --- |
|
||||
| `L` `S` `SHOW` `EDIT` | CC |
|
||||
|
||||
### 5.17 Defining words and the compiler
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `:` `;` `CREATE` `VARIABLE` `CONSTANT` `DOES>` `IMMEDIATE` `STATE` `[` `]` `LITERAL` `COMPILE` `[COMPILE]` `FORGET` `FENCE` | CC | |
|
||||
| `LIT` | OP | `@p` |
|
||||
| `does_rt` | RET | Internal helper; `DOES>` is implemented by the compiler capsule. |
|
||||
|
||||
### 5.18 Control flow
|
||||
|
||||
| Word | Fate | v4 definition / notes |
|
||||
| --- | --- | --- |
|
||||
| `IF` `ELSE` `THEN` `BEGIN` `UNTIL` `AGAIN` `WHILE` `REPEAT` `DO` `?DO` `LOOP` `+LOOP` `LEAVE` `CASE` `OF` `ENDOF` `ENDCASE` | CC | Compile-time structure words. Expansions per §2 and below. |
|
||||
| `EXIT` | OP | `;` |
|
||||
| `(BRANCH)` | OP | `jump` |
|
||||
| `(0BRANCH)` | IN | `if L … drop` (§2) — executed on the golden model (2026-10-03) as `IF 1 ELSE 2 THEN`, including results recorded from the v3 binary. |
|
||||
| `(DO)` | IN | `over push push drop` (R: limit index) — `SWAP push push` without the `SWAP`. The body always runs once, as v3. Executed on the golden model (2026-10-03). |
|
||||
| `(?DO)` | IN | `over over xor if SKIP drop (DO) … body and loop end … jump PAST SKIP: drop drop drop PAST:` — skips the loop when index = limit, as v3. Executed on the golden model (2026-10-03). |
|
||||
| `(LOOP)` | IN | See below. Adds 1 and goes round again while index < limit, signed, as v3. Executed on the golden model (2026-10-03) against C on every pair of 14 loop ends, and sequences recorded from the v3 binary. |
|
||||
| `(+LOOP)` | IN | See below. `( n -- )`: adds `n` and goes round again while index < limit for `n >= 0`, while index >= limit for `n < 0`, as v3. Executed on the golden model (2026-10-03) against C on every pair of 14 loop ends with 11 steps, and sequences recorded from the v3 binary. Clobbers `A`. |
|
||||
| `(LEAVE)` | IN | `pop drop pop drop jump PAST` — discards the limit and index and leaves the loop at once. In a single loop this is what v3 does. v3 left the two on its return stack (index set to limit) and jumped past the loop, so a `LEAVE` in an inner loop made the outer `LOOP` count the inner loop's leftovers and stop: `: T 3 0 DO 10 0 DO I . I 1 = IF LEAVE THEN LOOP I . LOOP ;` prints `0 1 10` in v3 and `0 1 0 0 1 1 0 1 2` here. Executed on the golden model (2026-10-03) in `LOOP` and `+LOOP`, including sequences recorded from the v3 binary. |
|
||||
| `I` | IN | `pop dup push` — executed on the golden model (2026-10-03). |
|
||||
| `J` | IN | `pop pop pop dup push a! push push a` — the outer index waits in `A` while the inner pair goes back, so `J` needs no return entry beyond the two loops' own (with two in-line `SWAP`s it needed one more). Executed on the golden model (2026-10-03), including a sequence recorded from the v3 binary. Clobbers `A`. |
|
||||
| `UNLOOP` | IN | `pop drop pop drop` — executed on the golden model (2026-10-03) as `UNLOOP EXIT`, including a sequence recorded from the v3 binary. |
|
||||
|
||||
```
|
||||
<less>, used by both loop ends: ( i' lim -- i' lim s ), the top bit of s set
|
||||
exactly when i' < lim, signed -- i' itself when the two differ in sign, else
|
||||
i' - lim (the subtraction in line):
|
||||
over over xor -if SAME
|
||||
drop over jump T
|
||||
SAME: drop over over -
|
||||
T:
|
||||
|
||||
(LOOP) expansion:
|
||||
pop 1 + pop \ index' limit
|
||||
<less> \ index' limit s
|
||||
-if Lexit \ not less: done
|
||||
drop push push \ R: limit index'
|
||||
jump Lbody
|
||||
Lexit: drop drop drop
|
||||
|
||||
(+LOOP) expansion: ( n -- )
|
||||
dup a! pop + pop \ index' limit A: n
|
||||
<less> a xor \ top bit set: go round again
|
||||
-if Lexit
|
||||
drop push push
|
||||
jump Lbody
|
||||
Lexit: drop drop drop
|
||||
```
|
||||
|
||||
`(LOOP)` goes round again while `index' < limit`, signed, which is v3's rule. So `0 5 DO … LOOP` runs
|
||||
once, as in v3; a test for `index' = limit`, which this document had first, would run it round the whole
|
||||
number circle. `(+LOOP)` goes round again exactly when `s` and `n` differ in sign: `index' < limit` for
|
||||
`n >= 0` and the opposite for `n < 0`. A loop costs its word two return-stack entries while it runs.
|
||||
|
||||
**New in v4:** `FOR`, `NEXT`, and `UNEXT` are native (§2). Counted loops that don't need an ascending
|
||||
index should use them; they cost one instruction per iteration.
|
||||
|
||||
### 5.19 StarForth extensions
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `ENTROPY@` `ENTROPY!` | MM | Per-call-target heat table (D-6). |
|
||||
| `WORD-ENTROPY` `RESET-ENTROPY` `TOP-WORDS` | CC | Reports over the heat registers. |
|
||||
| `(-` `INIT` | CC | |
|
||||
| `VERSION` | CC | |
|
||||
| `SEED` `RANDOM` | CAP | Deterministic PRNG (xorshift) in capsule code, reproducible from the seed. |
|
||||
| `WAIT` | CAP | Loop until the `ANTICLOCK` register has advanced `n`. |
|
||||
| `HEARTBEAT-TICKS@` | MM | `HEARTBEAT` register. |
|
||||
| `ZUSE-AUTHENTICATE` `ZUSE-SESSION?` `ZUSE-PUBKEY@` `ZUSE-CERT-INSTALLED?` | HERA | |
|
||||
|
||||
### 5.20 Word-level ACL
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `ACL-MODE@` `ACL-MODE!` `ACL-TTL@` `ACL-TTL!` `ACL-ALLOW@` `ACL-ALLOW!` `ACL-PINNED?` `ACL-PIN` `ACL-INHERIT` `ACL-INIT-PRIMITIVES` | HERA | Hera holds the ACL table. In the fabric, per-message ACL checks happen in the router (§6). |
|
||||
| `ACL-HEAT@` | MM | |
|
||||
| `ACL-WORD-ID` | CC | |
|
||||
|
||||
### 5.21 Physics: benchmark and diagnostics
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `BENCH-DICT-LOOKUP` `PHYSICS-CACHE-STATS` `PHYSICS-TOGGLE-CACHE` `PHYSICS-RESET-STATS` `PHYSICS-BUILD-INFO` | RET | Measure v3's software dictionary and hot-words cache, which do not exist on a node. |
|
||||
| `PHYSICS-BAYESIAN-REPORT` | CAP | Host node only; kept as an analysis tool. |
|
||||
| `PHYSICS-WORD-METRICS` `PHYSICS-CALC-KNOBS` `PHYSICS-BURN` `PHYSICS-SHOW-FEEDBACK` | RET | Never registered in v3. |
|
||||
|
||||
### 5.22 Physics: pipelining diagnostics
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `PIPELINING-*` (all six) | RET | Return as MM reports if D-6 adds transition counters. |
|
||||
|
||||
### 5.23 Physics: freeze, heat, and decay
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `FREEZE-WORD` `UNFREEZE-WORD` `FROZEN?` | MM | Freeze bit in the heat table. |
|
||||
| `HEAT@` `HEAT!` | MM | Test-only write retained. |
|
||||
| `SHOW-HEAT` `ALL-HEATS` | CC | |
|
||||
| `DECAY-RATE@` | MM | Governor parameter register. |
|
||||
| `FREEZE-CRITICAL` | CAP | Freezes the core set; the list is rewritten for v4 names. |
|
||||
|
||||
### 5.24 Dictionary heat optimisation
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `HEAT-PERCENTILES` `LOOKUP-STRATEGY@` `LOOKUP-STRATEGY!` `REORG-BUCKETS` `SHOW-HEAT-OPTIMIZATION` `COMPARE-LOOKUPS` | RET | Lookup strategy is a software-dictionary concern. The host compiler may keep a heat-ordered dictionary internally, but these words are not carried forward. |
|
||||
|
||||
### 5.25 Logging
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `LOG-ERROR` … `LOG-DEBUG` (levels) | IN | Constants. |
|
||||
| `LOG-LEVEL!` `LOG-LEVEL@` | CAP | Variable. |
|
||||
| `LOG-ERROR"` … `LOG-DEBUG"` | CC | |
|
||||
| `LOG-*-STR` | DEV | Log-ring service on the recorder (the ARM). |
|
||||
| `(do-log-*)` | CC | |
|
||||
| `(LOG-APPEND-RAW)` | DEV | |
|
||||
|
||||
### 5.26 Q48.16 fixed-point math
|
||||
|
||||
A Q48.16 value is 64 bits, so on a 32-bit node it occupies **two cells** and every Q word is a double
|
||||
word. Per D-10 it occupies two cells on a 64-bit node too. Per D-8, v4 Q values are signed.
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `Q.+` `Q.-` | CAP | `D+`, `D-` — executed on the golden model (2026-10-02) against v3's `q48_add`/`q48_sub` (`uint64_t` wrapping): bit-for-bit at 32-bit cells; at 64-bit cells the low cell is v3's, per D-10. |
|
||||
| `Q.*` | CAP | `SWAP push over over UM* drop push push over pop -if L1 SWAP jump L2 L1: SWAP drop 0 L2: pop SWAP - push push over pop UM* pop + ROT pop dup push over -if L3 drop dup jump L4 L3: drop 0 L4: push UM* pop - D+ ROT pop UM* SWAP push 0 D+ pop push over pop SWAP Q.TO-INT push Q.TO-INT pop SWAP` (`SWAP` and `ROT` in line) — `floor(a*b / 2^16)`, signed (D-8), cut to two cells. Cells 0–2 of the product come from the unsigned cell products `a1*b1` (low cell), `a0*b1`, `a1*b0`, `a0*b0`, in that order, each input dropped after its last use and `b0` waiting on the return stack. Reading `a1` and `b1` as signed takes `(b1<0 ? a0 : 0)` and `(a1<0 ? b0 : 0)` off cell 2; each is folded in as soon as its operands are adjacent. The result is cells 0–2 shifted right 16 with `Q.TO-INT` twice. Executed on the golden model (2026-10-02) against an independent limb-by-limb reference, and against v3's `q48_mul` for non-negative operands. Leaves its caller 3 data cells under its arguments and 3 return entries. Clobbers `A`. |
|
||||
| `Q./` | CAP | `over over OR if ZERO drop push over pop SWAP over xor (Q/) 4 + b! !b -if L1 DNEGATE L1: push push -if L2 DNEGATE L2: pop pop dup if CHK drop jump DIV CHK: drop over -131072 and if CHK2 drop jump DIV CHK2: drop push push dup pop dup push N-18 FOR 2* UNEXT over over xor -if SAME drop drop -if NOOV jump OV SAME: drop push inv pop + inv -if OV NOOV: drop pop pop jump DIV OV: drop drop drop pop pop drop drop (Q/) 4 + b! @b -if OVP drop 0 MSB ; OVP: drop -1 MAXHI ; DIV: (UQ/) (Q/) 4 + b! @b -if L3 drop DNEGATE ; L3: drop ; ZERO: drop drop drop NODE-ERROR b! -1 !b over over OR if Z0 drop -if ZP drop drop 0 MSB ; ZP: drop drop -1 MAXHI ; Z0: drop ;` — D-11: `a * 2^16 / b`, rounded toward zero; saturated to Q max / Q min on overflow; on division by zero, Q max / Q min by the dividend's sign (0 for `0 / 0`) and `NODE-ERROR` set. The sign of the result waits in `(Q/)`. Overflow needs no division: the quotient reaches `2^(2N-1)` exactly when `|a| * 2^16 >= |b| * 2^(2N-1)`, possible only for `|b| < 2^17`, where it is `a1 >= b0 << (N-17)` unsigned. `MSB` and `MAXHI` are the high cells of Q min and Q max. Executed on the golden model (2026-10-02) against an independent limb-by-limb reference (including `NODE-ERROR`), on width-native edge values, and against v3's `q48_div` for non-negative `a < 2^48`. Leaves its caller 3 data cells under its arguments and 3 return entries. Clobbers `A` and `B`. |
|
||||
| `(UQ/)` | CAP | Internal to `Q./`: unsigned `floor(a * 2^16 / b)` for `b != 0` and no overflow. Restoring division, `2N+16` steps, on one shifting register — quotient (starting as `a`) above remainder — with the quotient and divisor in `(Q/)` so the stacks carry only the remainder, the quotient bit and the loop count. Each step's quotient bit enters at the next step's shift; with no overflow the bits leaving the quotient in the last 16 steps are zeros. Trial subtraction by in-line sign tests (`x - y` with `y` on top is `push inv pop + inv`), including the full double subtraction with its borrow, so the loop calls nothing but `D2*C`. Executed on the golden model (2026-10-02). Clobbers `A` and `B`. |
|
||||
| `D2*C` | CAP | `( lo hi cin -- lo' hi' cout )`: the double shifted left one bit, `cin` entering at the bottom and `cout` the bit leaving the top (both 0 or 1). `a! dup -if L1 drop 1 jump L2 L1: drop 0 L2: push over -if L3 drop 1 jump L4 L3: drop 0 L4: over + + push 2* a + pop pop` — `cin` waits in `A`, and `2hi + m` is `over + +`, so it needs one return entry. Executed on the golden model (2026-10-02). Clobbers `A`. |
|
||||
| `(Q/)` | CAP | Variable, 5 cells: `Q./`'s quotient register, divisor and result sign. Like `BASE` and the hold buffer, it lives in node memory. |
|
||||
| `Q.ABS` `Q.NEG` | CAP | `DABS`, `DNEGATE` — executed on the golden model (2026-10-02) against v3's `q48_abs` and `0 - q`: bit-for-bit at 32-bit cells, including v3's wrap of Q min to itself; at 64-bit cells the low cell is v3's and the high cell is the true sign, per D-10. |
|
||||
| `Q.COS` | CAP | v3's `q48_cos_approx`: on `xu = |(Q.REDUCE) x|`, `1 - xu^2/2! + xu^4/4! - …` to `n = 10`, run by `Q.SIN`'s loop: `(Q.REDUCE) -if A1 inv 1 + A1: 0 (QT) b! !b 0 over 0 Q.* drop (QT) 1 + b! !b 65536 (QT) 2 + b! !b 65536 (QT) 3 + b! !b 2 (QT) 4 + b! !b -1 (QT) 5 + b! !b jump TRIG` — term and sum start at 1.0, `n` at 2, and the sign cell is 0 so nothing is negated at the end. It jumps into the loop rather than calling it, so it costs no extra return entry. Executed on the golden model (2026-10-03): bit for bit v3's result on angles of every size and sign. Leaves its caller 3 data cells under its argument and 2 return entries. Clobbers `A` and `B`. |
|
||||
| `Q.SIN` | CAP | v3's `q48_sin_approx`: on `r = (Q.REDUCE) x` and `xu = |r|`, `xu - xu^3/3! + xu^5/5! - …` to `n = 11`, each term the last times `xu^2` over `n(n-1)`, stopping once a term is below 10 ulp; negated if `r < 0`. After the reduction every value fits one cell, so the variable `(QT)` holds single cells and the only calls are `(Q.REDUCE)`, `Q.*`, `Q./` and `UM*`. The Taylor loop is written so that `Q.COS` can jump into it. Executed on the golden model (2026-10-03): bit for bit v3's result on angles of every size and sign. Leaves its caller 3 data cells under its argument and 2 return entries. Clobbers `A` and `B`. |
|
||||
| `(Q.REDUCE)` | CAP | `( x -- r )`, v3's `q48_reduce_angle`: the angle reduced to one cell, `-pi <= r <= pi` with `pi = 205887`, `2pi = 411774` — the remainder of `x / 2pi` with the sign of `x`, then one step of `2pi` back into range. `x / 2pi` does not fit a cell, but only the remainder is wanted: `|x| mod 2pi` is two `UM/MOD` steps, the high cell first and its remainder then leading the low cell. `dup (QT) b! !b -if L1 DNEGATE L1: 0 411774 UM/MOD drop 411774 UM/MOD drop (QT) b! @b -if P1 drop inv 1 + jump J1 P1: drop J1: dup -205888 + -if BIG drop dup 205887 + -if OK drop 411774 + ; OK: drop ; BIG: drop -411774 + ;` Executed on the golden model (2026-10-03), bit for bit v3's. Clobbers `A` and `B`. |
|
||||
| `(QT)` | CAP | Variable, 6 cells: `Q.SIN` / `Q.COS`'s sign, `x^2`, term, sum, `n` and subtract flag. |
|
||||
| `Q.LOG` | CAP | v3's `q48_log_approx`: `x = 2^k * m` with `1.0 <= m < 2.0`; `ln m` by up to 6 Newton rounds on `e^y = m` from `y = m - 1.0`, stopping once `|m - e^y| < 100` ulp; result `y + k * ln 2` with `ln 2 = 45426`. `e^y` is `Q.EXP`'s Taylor loop **written in line** (flattened, ruled 2026-10-02), so `Q.LOG` calls only `Q.*`, `Q./` and `UM*` and the call chain is one level shorter than through `Q.EXP`. After the reduction `m`, `y`, the Taylor term and its sum each fit one cell at either width, so the variable `(QL)` holds single cells. `x <= 0` returns 0 and sets `NODE-ERROR` (D-12). Executed on the golden model (2026-10-03): bit for bit v3's result for every `x > 0` tested, including a sweep of the reduced `m`. v3's clamp `y = max(y - corr, 0)` is kept for fidelity but cannot fire: over all 65536 values of `m` it is never reached, and the iteration never needs more than 4 rounds. Leaves its caller 3 data cells under its argument and 2 return entries. Clobbers `A` and `B`. |
|
||||
| `(QL)` | CAP | Variable, 7 cells: `Q.LOG`'s `m`, `y`, `k`, rounds left, Taylor term (then `delta`), sum, and `n * 1.0`. |
|
||||
| `Q.SQRT` | CAP | v3's `q48_sqrt_approx`: Newton from `x0 = q/2 + 0.25`, up to 8 rounds of `x' = (x + q/x) / 2`, returning `x` as soon as `|x' - x| < 10` ulp; `sqrt(0) = 0`, `sqrt(1.0) = 1.0`. `q < 0` returns 0 and sets `NODE-ERROR` (D-12). `q`, `x` and the rounds left live in the variable `(QR)`. The halving is a logical double shift, `push a! 0 pop +* MAXHI and push drop a pop`. Executed on the golden model (2026-10-02): bit for bit v3's result for `0 <= q < 2^48`; above that v3's `q48_div` saturates and v4 divides correctly, so they part. Leaves its caller 3 data cells under its argument and 2 return entries. Clobbers `A` and `B`. |
|
||||
| `(QR)` | CAP | Variable, 5 cells: `Q.SQRT`'s `q`, `x` and rounds left. |
|
||||
| `Q.EXP` | CAP | v3's `q48_exp_approx`: `1 + x + x^2/2! + …` to 10 terms on `x = |q|`, stopping once a term is below 50 ulp; `1.0 Q./ e^|q|` for `q < 0`; `e^0 = 1.0`; `|q| >= 16.0` gives 0 (`q < 0`) or **Q max** (v3 returned all ones, which reads as −ulp under D-8). `x`, the term, the sum, the sign and the term index live in the variable `(QE)`, so only `Q.*` and `Q./` arguments sit on the stacks. Executed on the golden model (2026-10-02): bit for bit v3's result for `|q| < 16.0` (at 64-bit cells as the same value, high cell 0). Leaves its caller 3 data cells under its argument and 2 return entries. Clobbers `A` and `B`. |
|
||||
| `(QE)` | CAP | Variable, 8 cells: `Q.EXP`'s `x`, term, sum, sign and term index. |
|
||||
| `Q.FROM-INT` | CAP | `push 0 a! 0 pop 15 FOR +* UNEXT push drop a pop` — `n * 2^16` as a signed double. `+*` with `S = 0` never adds, so each step is an exact arithmetic right shift of `T:A`; starting from `T:A = n:0` (that is, `n * 2^N`) and shifting `N-16` bits leaves `n * 2^16`. The count is `N-17`: 15 at 32-bit cells, 47 at 64. Negative values are no longer clamped to 0. Executed on the golden model (2026-10-02): matches v3's `q48_from_u64` for `n >= 0` (at 64-bit cells in the low cell, where v3 wraps for `n >= 2^47`, per D-10). Clobbers `A`. |
|
||||
| `Q.TO-INT` | CAP | `push a! 0 pop 15 FOR +* UNEXT drop drop a` — the same `+*` shift, 16 bits at every cell width, leaving the low cell in `A`. Rounds toward minus infinity, as v3's arithmetic shift does (−1.5 gives −2). Executed on the golden model (2026-10-02): v3's `q48_to_u64` exactly at 64-bit cells, its low cell at 32. Clobbers `A`. |
|
||||
| `Q.1` `Q.0` `Q.SCALE` | IN | Double-cell constants, placed in line as two literals, low cell first: `Q.1` and `Q.SCALE` are `65536 0` (1.0), `Q.0` is `0 0`. v3's are 65536, 0 and 65536. Executed on the golden model (2026-10-03): the values match v3's, `Q.1 Q.TO-INT` is 1, `1 Q.FROM-INT` is `Q.1`, and `q Q.1 Q.*` and `q Q.0 Q.+` return `q`. |
|
||||
| `Q.=` `Q.<` `Q.>` `Q.0=` `Q.MAX` `Q.MIN` | CAP | `D=`, `D<`, `2SWAP D<` (for `Q.>`), `D0=`, `DMAX`, `DMIN`. Signed (D-8); v3 compared unsigned, so v3 agrees only when both values have the same sign. Executed on the golden model (2026-10-02). `Q.>` was given here as `SWAP D<`, which swaps single cells, not Q values, and gave a wrong answer in 11702 of 20169 test cases. |
|
||||
| `Q.PRINT` | CAP | `( q -- )`: the integer part, a point, the five digits `floor(frac * 100000 / 65536)`, then one space, as v3; always decimal, whatever `BASE` is, and `BASE` is put back. Signed (D-8): a negative value prints `-` and its magnitude (−1.5 prints `-1.50000`), where v3 printed it as a large unsigned number. `dup (QP) 3 + b! !b -if A DNEGATE A: (QP) 2 + b! !b dup (QP) 1 + b! !b BASE b! @b (QP) b! !b 10 BASE b! !b 65535 and 4 FOR dup 2* 2* + UNEXT 10 FOR 2/ UNEXT 0 <# # # # # # drop drop 46 HOLD (QP) 1 + b! @b (QP) 2 + b! @b dup push push a! 0 pop 15 FOR +* UNEXT drop drop a pop 15 FOR 2/ UNEXT HIMASK and #S (QP) 3 + b! @b SIGN #> (QP) b! @b BASE b! !b jump OUT` — `OUT` is the `TYPE jump SPACE` at the end of `.R`. The fraction is `frac * 3125 / 2048`, which stays inside a 32-bit cell where `frac * 100000` would not; times 3125 is times 5 five times. The integer part is `|q|` shifted right 16 as a double: its low cell by `Q.TO-INT`'s `+*` shift in line, its high cell by `2/` and `HIMASK`, which clears the top 16 bits. Executed on the golden model (2026-10-03) against a C reference, on every seventh fraction, and against a transcript of the v3 binary for non-negative values. Leaves its caller 4 data cells and 3 return entries. Clobbers `A` and `B`. |
|
||||
| `(QP)` | CAP | Variable, 4 cells: `Q.PRINT`'s saved `BASE`, `|q|` and the sign. |
|
||||
|
||||
### 5.27 Inference engine
|
||||
|
||||
The runtime governor moves into hardware as the multi-level Rolling Window of Truth (see
|
||||
`JUSTIFICATION.md`). These words survive on the host node as analysis tools.
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `INFER-RUN` `INFER-WINDOW@` `INFER-DECAY@` `INFER-VARIANCE@` `INFER-FIT@` `INFER-EARLY-EXIT@` | CAP | Host node only. Ported from `inference_words.c`. |
|
||||
| `Q.VARIANCE` `INFER-DECAY-SLOPE` `INFER-WINDOW-WIDTH` `WINDOW-DIVERSITY` | CAP | Host node only. |
|
||||
| `L8-MODE` `L8-UPDATE` `L8-APPLY` `L8-TABLE-FORCE` | MM | Jacquard selector state becomes governor registers. `L8-TABLE-FORCE` remains the DoE override. |
|
||||
| `BAYES-*` (all six) | RET | Model the v3 hot-words cache and bucket search. |
|
||||
|
||||
### 5.28 DEFER and IS
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `DEFER` `IS` `DEFER@` | CC | A deferred word's runtime is `@p push ;` followed by the stored xt. |
|
||||
|
||||
### 5.29 – 5.32 Framebuffer, keyboard, TrueType, scrollback
|
||||
|
||||
| Word | Fate |
|
||||
| --- | --- |
|
||||
| `PLOT` `FB-WIDTH` `FB-HEIGHT` | DEV |
|
||||
| `KBD-SCAN` `KBD-DEBUG` `VKBD-EVENT` `VKBD-DEBUG` `KEY-EVENT` `ALT+TAB` | DEV |
|
||||
| `TTF-TEXT` | DEV |
|
||||
| `SCROLL-BACK` `SCROLL-FWD` | DEV |
|
||||
|
||||
### 5.33 Kernel REPL and DoE hooks
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `HB-ON` `HB-OFF` | MM | Recorder-enable register. The fabric pushes DoE rows into a FIFO; the ARM drains it to storage. |
|
||||
| `BLK-ATTACH-ACK` `KH-BLK-ATTACH-SEND` `KH-ELEVATE-SEND` | RET | Kernel-Hermes calls. Hermes is now the fabric; these become ordinary packets (§6). |
|
||||
|
||||
### 5.34 Hera and child-VM words
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `BIRTH` `KILL` `START` `STOP` `USE` `EXEC` `EJECT` `CONNECT-HERMES` `CONNECT-ARTEMIS` `BYE` | HERA | A VM becomes a node (or a group of nodes). `BIRTH` loads a capsule into a node and releases it. |
|
||||
| `VM-EXEC` `VM-CALL` `VM-STEP` | CAP | Built on `SEND` and `RECV` (§6). |
|
||||
| `VM-HEAT` `VM-ERROR?` `VM-COUNT` `VM-CONSERVED?` `VM-PHYSICS-STATUS` | MM + HERA | Per-node heat is a register; fleet totals are Hera's. |
|
||||
| `SWITCH-MARK-WORK` | RET | Nodes run concurrently; there is no context switch to mark. |
|
||||
| `MAMA-VM-ID` `NAME>XT` | HERA, CC | |
|
||||
| `CAPSULE-*` `WORKER-BIRTH` `UNATTENDED-BIRTH` `CONSOLE-ATTACH` | HERA | |
|
||||
| `MINT` `MINT-SCRATCH` `MINT-SCRATCH-EMIT` `ZUSE-ELIGIBILITY-ADD` `ZUSE-ELIGIBLE?` `ELEVATE-PUBKEY-UNPACK` | HERA | |
|
||||
| `RUNCAP-TEST` `PAIR-TEST` | HERA | Diagnostics, kept. |
|
||||
| `STADIUM-ADMIT` `STADIUM-EVICT` `STADIUM-RES-PULL` `STADIUM-RES-PUSH` | HERA | Admission and reservoir are Hera's. |
|
||||
| `STADIUM-HEAT@` `STADIUM-HEAT!` `STADIUM-RES@` `STADIUM-WORD-HEAT` | MM | |
|
||||
|
||||
### 5.35 Hosted lifecycle stubs
|
||||
|
||||
| Word | Fate |
|
||||
| --- | --- |
|
||||
| `BIRTH` `KILL` `PAUSE` `RESUME` `USE` (hosted) | RET — the hosted v4 golden model implements the real HERA messages. |
|
||||
|
||||
---
|
||||
|
||||
## 6. Messaging between nodes
|
||||
|
||||
Each node has four neighbour ports (`PORT-UP`, `PORT-DOWN`, `PORT-LEFT`, `PORT-RIGHT`) mapped into its
|
||||
address space. A read from a port blocks until the neighbour writes; a write blocks until the neighbour
|
||||
reads. This is the GA144 model. No instruction is added: a port is an address.
|
||||
|
||||
### 6.1 Packet format (proposal)
|
||||
|
||||
| Word | Contents |
|
||||
| --- | --- |
|
||||
| 0 | Header: destination node (8 bits), type (8), TTL/heat (8), payload length in words (8). |
|
||||
| 1 | ACL tag: sender identity fingerprint, checked by the router on every packet. |
|
||||
| 2 … | Payload. |
|
||||
|
||||
The router in each node forwards packets not addressed to it, decrements TTL, and drops a packet whose
|
||||
TTL reaches zero or whose ACL tag fails. This is v3 Hermes's per-message ACL check and unconditional TTL
|
||||
expiry, moved into logic. A packet type `SOS` is reserved for any node to emit.
|
||||
|
||||
### 6.2 Send and receive
|
||||
|
||||
```forth
|
||||
: SEND ( addr u port -- ) b! SWAP a! BEGIN dup WHILE 1- @+ !b REPEAT drop ;
|
||||
: RECV ( addr u port -- ) b! SWAP a! BEGIN dup WHILE 1- @b !+ REPEAT drop ;
|
||||
```
|
||||
|
||||
`A` walks the buffer and `B` holds the port, so each word moved costs one fetch and one store.
|
||||
|
||||
---
|
||||
|
||||
## 7. Memory-mapped registers
|
||||
|
||||
Addresses are assigned in the node memory map (D-4). Names only here.
|
||||
|
||||
| Register | Access | Contents |
|
||||
| --- | --- | --- |
|
||||
| `HEAT-OP[0..31]` | R | Per-opcode retirement heat. |
|
||||
| `HEAT-CALL[...]` | R/W | Per-call-target heat, with freeze bit (D-6). |
|
||||
| `ANTICLOCK` | R | Virtual tick: a pure function of the execution stream. |
|
||||
| `HEARTBEAT` | R | Adaptive heartbeat count. |
|
||||
| `GOV-*` | R/W | Governor parameters and Jacquard selector state (`L8-*`, decay rate). |
|
||||
| `REC-ENABLE` | R/W | DoE recorder on/off (`HB-ON` / `HB-OFF`). |
|
||||
| `PORT-STATUS` | R | Per-port ready flags, for non-blocking polls. |
|
||||
| `NODE-ERROR` | R/W | Arithmetic error flag: set to −1 by `Q./` on division by zero (D-11), by `Q.SQRT` / `Q.LOG` outside their domain (D-12) and by `HOLD` on a bad character or a full buffer (D-13), cleared by writing 0. `VM-ERROR?` reads it. |
|
||||
| `CONSOLE-TX` | W | Console transmit: a store sends the low 8 bits of the value as one character. This is what `EMIT` writes to. On the single-node golden model it is a capture register: the character is appended to a buffer on the node and memory is not written (`v4_node_console_attach`, `v4/include/v4/node.h`), so printing words can be run and their output compared with v3's. With the mesh (step 2) it becomes the port to the console node. |
|
||||
| `CONSOLE-RX` | R | Console receive: a fetch gives the next pending character, 0–255, and takes it; with none pending it gives −1 and takes nothing. This is what `KEY` reads. On the single-node golden model the characters come from a queue the test feeds (`v4_node_console_input_attach`, `v4_node_console_feed`, `v4/include/v4/node.h`). Only the data fetches `@`, `@+` and `@b` see the register; instruction words and literals at the same address are read as memory. With the mesh (step 2) it becomes the port from the console node, and a read with nothing pending will block instead. |
|
||||
| `CONSOLE-STATUS` | R | Console receive status: a fetch gives −1 when a character is pending and 0 when not, and changes nothing. `?TERMINAL` reads it, and `KEY` polls it. On the mesh it is the console port's bit of `PORT-STATUS`. |
|
||||
@@ -0,0 +1,257 @@
|
||||
# StarForth v4.0.0 — Justification
|
||||
|
||||
This document records why StarForth v4.0.0 exists, what it changes, and why each major decision was
|
||||
made. The specification is `DECOMPOSITION.md`. Per project practice, this document is written before
|
||||
any v4 code.
|
||||
|
||||
---
|
||||
|
||||
## 1. The problem with v3
|
||||
|
||||
StarForth v3 is a successful software machine. It boots on amd64, aarch64, and riscv64, runs the
|
||||
Tripod fleet on LithosAnanke, and has held K≡1.0 across 38,400+ experimental runs. But it was designed
|
||||
for a large host, and it shows:
|
||||
|
||||
- **More than 300 C primitives.** Most are not primitive in any hardware sense. Double-cell arithmetic,
|
||||
string handling, comparisons, pictured output, and Q48.16 transcendentals are all expressible in a
|
||||
handful of machine operations.
|
||||
- **64-bit cells and 5 MB of linear memory per VM.** Reasonable on a PC; far too large for a node in a
|
||||
fabric.
|
||||
- **Diagnostics of its own implementation.** A significant block of words (hot-words cache statistics,
|
||||
lookup strategies, pipelining metrics, Bayesian cache models) measures v3's software dictionary, not
|
||||
the computation the dictionary performs.
|
||||
- **Hermes in software.** Message routing, per-message ACL checks, and TTL expiry are C code executed by
|
||||
a CPU that is also doing everything else.
|
||||
|
||||
None of this is wrong for a hosted or bare-metal OS. It is wrong for silicon. The project's direction
|
||||
is now an FPGA embodiment, and eventually an ASIC, and v3 cannot be carried there by porting.
|
||||
|
||||
## 2. What v4 is
|
||||
|
||||
StarForth v4 is a Forth machine designed to be the same thing in software and in hardware:
|
||||
|
||||
1. **A 32-instruction core ISA** derived from Chuck Moore's F18, the node of the GA144. Every core word
|
||||
is a mnemonic; every mnemonic is one 5-bit opcode.
|
||||
2. **Everything else is capsule code**, compiled from those 32 instructions, or a message to a node that
|
||||
owns a service, or a memory-mapped register, or retired.
|
||||
3. **A mesh of small nodes** that talk to their four neighbours through blocking ports. Hermes becomes
|
||||
the network itself: routing, ACL checks, and TTL expiry move into logic in every node's router.
|
||||
4. **Compudynamics as a side effect of execution.** Heat counters, the anti-clock, and the heartbeat are
|
||||
driven by instruction retirement in hardware. They cost no instructions.
|
||||
5. **A power-aware governor** built from a multi-level Rolling Window of Truth, controlling timing only.
|
||||
|
||||
v4 is a new implementation, not a refactor. v3 remains the reference system for LithosAnanke until v4
|
||||
reaches parity.
|
||||
|
||||
## 3. Why Moore's F18 instruction set
|
||||
|
||||
**It is proven minimal.** Moore spent decades removing instructions from his stack machines. The F18's
|
||||
32 opcodes are the result: enough to build a complete Forth, nothing that can be composed from the
|
||||
rest. There is no multiply, no divide, no compare, and no `SWAP`; each is a short sequence (for example
|
||||
`SWAP` is `over push push drop pop pop`).
|
||||
|
||||
**It fits a 32-bit word exactly.** 32 opcodes need 5 bits. Six slots fill 30 bits of a 32-bit
|
||||
instruction word, with 2 spare. One fetch feeds six instructions.
|
||||
|
||||
**It matches the project's formal-verification plan.** Proving 32 instruction semantics in Isabelle/HOL
|
||||
is a bounded task. Proving 300 C primitives is not. Every higher word then inherits correctness from its
|
||||
definition, which is itself a checkable object.
|
||||
|
||||
**It matches the dictionary-shrink plan that was already underway.** The existing POST suite, which
|
||||
exercises every dictionary word, was to be used as a regression gate while C primitives were replaced by
|
||||
colon definitions. v4 carries that plan to its end point: the surviving primitives are the ISA.
|
||||
|
||||
**It comes with a mesh precedent.** The GA144 places 144 F18 nodes on one die, each talking to its
|
||||
neighbours through blocking ports. v4 adopts that topology directly.
|
||||
|
||||
## 4. Why a mesh, and why Hermes goes into the fabric
|
||||
|
||||
v3's Tripod is several VMs sharing one CPU, with Hermes arbitrating messages between them in software.
|
||||
The mesh replaces time-sharing with space: each VM role runs on its own node or group of nodes,
|
||||
concurrently.
|
||||
|
||||
Moving Hermes into the fabric has three consequences:
|
||||
|
||||
- **The message semantics become hardware.** Per-message ACL checks and unconditional TTL expiry, which
|
||||
v3 already treats as rules rather than options, become router logic that cannot be bypassed.
|
||||
- **Contention disappears as a scheduling problem.** A blocking port is flow control. There is no
|
||||
scheduler to write, which honours the existing design goal of avoiding one.
|
||||
- **The SOS mechanism generalises.** Any node can emit an `SOS` packet. A node whose router fails can
|
||||
only stop forwarding, which its neighbours detect as blocked ports; this is the hardware form of v3's
|
||||
"Hermes raises a semaphore while sinking" rule.
|
||||
|
||||
## 5. Why cell width becomes a parameter
|
||||
|
||||
The Zynq-7000's processing system is a Cortex-A9, a 32-bit ARMv7-A core. Rather than maintain a separate
|
||||
32-bit fork, v4 makes cell width a build parameter of one VM (32 or 64). This has three benefits:
|
||||
|
||||
1. **The ARM becomes a real StarForth host**, not just a bootloader, at 32 bits.
|
||||
2. **Mesh nodes use 32-bit cells**, roughly halving stack and ALU cost in the fabric.
|
||||
3. **It opens a second invariance axis.** K≡1.0 has been shown invariant across amd64, aarch64, and
|
||||
riscv64. If it also holds across cell widths, the conservation law is shown not to depend on word
|
||||
size either. That is a stronger claim than ISA invariance alone.
|
||||
|
||||
The physics does not shrink with the cell. Heat and K arithmetic remain 64-bit (`int64_t` in C99 on
|
||||
every host; double cells on a 32-bit node). Changing only the payload width keeps the experiment clean:
|
||||
any difference in K can be attributed to cell width and not to lost precision.
|
||||
|
||||
## 6. Why the physics splits into "what" and "when"
|
||||
|
||||
The fabric can measure real power: the Zynq's XADC reads on-die temperature and supply voltages, and a
|
||||
current sensor on the core rail gives true power draw. This makes "heat" a physical quantity rather than
|
||||
a metaphor.
|
||||
|
||||
Physical measurements are noisy and never reproducible run to run. If they controlled which code
|
||||
executes, parity hashes would break and formal proofs of behaviour would become impossible. v4
|
||||
therefore splits the physics:
|
||||
|
||||
| Layer | Driven by | Controls | Property |
|
||||
| --- | --- | --- | --- |
|
||||
| **Virtual heat** | Instruction retirement and call counts | What executes (selection, promotion, eviction) | Deterministic and provable |
|
||||
| **Physical power** | XADC and rail current | When things happen (clock gating, node sleep, message pacing) | Adaptive, never affects results |
|
||||
|
||||
This settles a question left open in the original FPGA concept: whether compudynamic feedback into the
|
||||
control unit should affect only timing or also the execution path. The answer is both, through separate
|
||||
channels: logic chooses *what*, physics chooses *when*.
|
||||
|
||||
## 7. Why the governor is a multi-level Rolling Window of Truth
|
||||
|
||||
The timing governor uses the project's own Rolling Window of Truth mechanism at three timescales:
|
||||
|
||||
| Window | Timescale | Governs |
|
||||
| --- | --- | --- |
|
||||
| Short | microseconds | Clock gating on one node |
|
||||
| Medium | milliseconds | Node sleep and wake |
|
||||
| Long | seconds | Thermal trend and mesh-wide message pacing |
|
||||
|
||||
Positive feedback (rising message load) wakes neighbouring nodes and raises the clock. Negative feedback
|
||||
(rising temperature or power) throttles pacing and puts cool nodes to sleep. Each level reacts much more
|
||||
slowly than the one below it, so the loops do not fight; hysteresis at each level prevents flapping at
|
||||
thresholds. Hard limits (thermal ceiling, minimum clock) sit outside the adaptive layer as fixed logic.
|
||||
|
||||
A small neural network is a later candidate. Because the governor only controls timing, a poor governor
|
||||
costs power or speed and never correctness, so it is a safe place to experiment. The DoE recorder
|
||||
(below) produces exactly the training data such a network would need, so the two approaches can be
|
||||
compared on identical workloads.
|
||||
|
||||
## 8. Division of labour on the Zynq
|
||||
|
||||
| Component | Runs on | Role |
|
||||
| --- | --- | --- |
|
||||
| Mesh nodes | Fabric | All StarForth execution, the anti-clock, heat counters, routers |
|
||||
| Governor | Fabric | Multi-level RWT, single clock domain, cycle-exact |
|
||||
| Host node | ARM (32-bit) | Boot and bitstream load, compiler capsule, console bridge, DoE recorder |
|
||||
|
||||
The anti-clock stays in the fabric because it is defined as a pure function of the execution stream and
|
||||
must live where execution happens. The heartbeat's adaptive loop stays in the fabric because a loop
|
||||
crossing the PS–PL boundary would inherit ARM-side jitter (caches, interrupts, bus latency).
|
||||
|
||||
The ARM's recorder role keeps measurement separate from the thing being measured: the fabric pushes
|
||||
DoE rows into a FIFO, the ARM drains them to storage, and if the ARM falls behind rows are dropped
|
||||
rather than execution stalled. This is the fabric form of the planned `HB-ON`/`HB-OFF` disk recording.
|
||||
|
||||
## 9. Why the compiler lives on the host node
|
||||
|
||||
GA144 nodes have 64 words of RAM and 64 of ROM, and arrayForth compiles on a host. v4 follows the same
|
||||
split. The outer interpreter, dictionary, vocabularies, and defining words form the compiler capsule,
|
||||
which runs on the host node. Mesh nodes receive compiled code. Large capsules stay in DDR and are
|
||||
streamed to nodes as needed, so capsule size is not limited by node memory.
|
||||
|
||||
This is also why so many v3 words become CC rather than CAP in `DECOMPOSITION.md`: they are compiler
|
||||
machinery, not computation.
|
||||
|
||||
## 10. Development path
|
||||
|
||||
Each stage is checked against the one before it. Nothing proceeds on trust.
|
||||
|
||||
1. **Hosted golden model.** A C99 implementation of the v4 ISA and node model, with cell width, node
|
||||
count, and node memory as parameters. The POST suite, rewritten against v4 capsules, must pass at
|
||||
both 32 and 64 bits, and K≡1.0 must hold.
|
||||
2. **Hosted mesh.** Several golden-model nodes wired through simulated ports, running the Tripod roles
|
||||
as nodes. The 144-node configuration is exercised here, since the host is not limited by fabric size.
|
||||
3. **Co-simulation.** The node RTL is compiled with Verilator and run in lockstep with the golden model.
|
||||
After every instruction, stacks, registers, and heat counters are compared. The first mismatch
|
||||
identifies the faulty mnemonic exactly.
|
||||
4. **FPGA.** A 2×2 mesh on the PZ7020, then the largest grid that fits. The bitstream only has to match
|
||||
the co-simulation.
|
||||
5. **ASIC.** A single v4 node, not the mesh, as a proof of silicon through an open-source shuttle
|
||||
(currently Tiny Tapeout on IHP's SG13G2 130 nm open PDK). The same RTL is reused; block RAM is
|
||||
replaced by the process's SRAM macros.
|
||||
|
||||
## 11. Scaling beyond the PZ7020
|
||||
|
||||
Node count, node memory, and cell width are parameters, and the mesh is generated by a loop over rows
|
||||
and columns, so a larger board changes numbers, not design.
|
||||
|
||||
| Part | Approximate resources | Estimated nodes |
|
||||
| --- | --- | --- |
|
||||
| Zynq-7020 | ~53K LUTs, 140 BRAM36 | ~8–16 |
|
||||
| Zynq-7045 | ~218K LUTs, 545 BRAM36 | ~50–70 |
|
||||
| Zynq UltraScale+ (e.g. Kria K26) | ~117K LUTs, 144 BRAM36, 64 UltraRAM | ~30–40, with much larger node memory |
|
||||
| Larger UltraScale+ / Versal | Several hundred K LUTs and up | A full 144 |
|
||||
|
||||
Node counts are estimates. The first hardware measurement to take is the LUT cost of one node plus its
|
||||
router on the 7020; every other board's capacity follows from that number.
|
||||
|
||||
UltraScale+ parts also change the host: their Cortex-A53 cores are aarch64, so the host node can run
|
||||
64-bit StarForth while the mesh runs 32-bit cells, which the cell-width parameter already supports.
|
||||
Larger meshes will need registered router-to-router links to close timing, and the free edition of
|
||||
Vivado supports only smaller devices, so tool licensing must be checked before choosing a board.
|
||||
|
||||
## 12. Risks
|
||||
|
||||
| Risk | Mitigation |
|
||||
| --- | --- |
|
||||
| Capsule-level arithmetic is much slower than v3's C primitives on a hosted build. | Accepted. v4's measure of performance is the fabric, where each instruction is one cycle. The hosted build is a correctness oracle. |
|
||||
| Word addressing makes byte and string operations expensive. | D-1 in `DECOMPOSITION.md` keeps the choice open; colorForth's packed, pre-parsed source is a proven alternative for text. |
|
||||
| K≡1.0 may behave differently at 32-bit cell width. | That is an experimental result either way, and the hosted golden model finds it before any hardware exists. |
|
||||
| Hand-traced definitions contain errors. | Every CAP definition is a POST target against the v3 C primitive it replaces. |
|
||||
| The mesh does not fit the 7020 at a useful size. | Measure one node first; the design scales to larger parts unchanged. |
|
||||
|
||||
## 13. Relationship to intellectual property
|
||||
|
||||
v4 strengthens rather than replaces the existing claims. The Jacquard Selector, the Rolling Window of
|
||||
Truth, and the Steady State Machine all survive, now as hardware structures. The new elements a filing
|
||||
could draw on are: compudynamic heat as a zero-cost side effect of instruction retirement; the split of
|
||||
deterministic virtual heat (selection) from physical power (timing); per-packet ACL and TTL enforcement
|
||||
in a mesh router; and conservation invariance across cell width. Whether any of these belong in the
|
||||
LithosAnanke filing is a question for counsel.
|
||||
|
||||
## 14. Definition of done for v4.0.0
|
||||
|
||||
- The 32-instruction ISA is specified, with every open decision in `DECOMPOSITION.md` §3 settled.
|
||||
- The hosted golden model passes the rewritten POST suite at 32-bit and 64-bit cell widths.
|
||||
- K≡1.0 holds on the golden model at both widths, on all three host ISAs.
|
||||
- A hosted mesh runs the Tripod roles as nodes, with Hermes as the network.
|
||||
- Verilator co-simulation of one node matches the golden model instruction for instruction.
|
||||
|
||||
## 15. Products
|
||||
|
||||
Ruled by Captain Bob, 2026-10-03. v4 turns one project into three products built on the same engine:
|
||||
|
||||
1. **Hosted StarForth F18.** The v4 engine as a native Linux build for amd64, arm64 and riscv64, on
|
||||
real hardware.
|
||||
2. **FPGA StarForth F18.** A 32-bit build of the engine loaded into the FPGA. This is the gateway into
|
||||
building the whole system, and its foundation.
|
||||
3. **StarshipOS.** The bare-metal product: LithosAnanke running the v4 F18 engine.
|
||||
|
||||
The FORTH-79 vocabulary, recomposed on the F18 engine (`DECOMPOSITION.md`), is stored as a capsule. The
|
||||
StarshipOS-specific portions are recomposed and stored as capsules in the same way. The source tree
|
||||
will be reorganised around this split.
|
||||
|
||||
## 16. Acceptance criteria
|
||||
|
||||
Ruled by Captain Bob, 2026-10-03. Until then v4 had none: this document and `v4/README.md` named a
|
||||
POST suite and K≡1.0 on three host ISAs, neither of which exists yet, so nothing could be called
|
||||
accepted.
|
||||
|
||||
- **v4 is equivalent to v3 at any point in time.** It must provide the same vocabularies and behave
|
||||
the same way. The only difference is the machine underneath: the F18-derived engine instead of the
|
||||
original StarForth VM.
|
||||
- **Every ISA, hosted and bare metal, must still reach its `ok` prompt.** That is amd64, aarch64 and
|
||||
riscv64, as a hosted build and as a bare-metal boot.
|
||||
|
||||
The existing acceptance tests keep their force and their rules: the three-architecture QEMU boot for
|
||||
anything bare metal (`.claude/CLAUDE.md`), and the hosted three-architecture test
|
||||
(`docs/lithosananke/hosted-acceptance-test/README.md`). Passing `make -C v4 test` is a development
|
||||
check on the golden model, not acceptance.
|
||||
@@ -137,16 +137,22 @@ before building) rather than wiring it into `doe.4th`.
|
||||
## `.4th` File Structure
|
||||
|
||||
Every `.4th` file must follow StarForth's block format. The block system
|
||||
maps source text to 1024-byte logical blocks; the `Block NNNN` header tells
|
||||
the loader which block slot to fill.
|
||||
maps source text to logical blocks; the `Block NNNN` header tells the loader
|
||||
which block slot to fill.
|
||||
|
||||
**Mandatory rules:**
|
||||
|
||||
1. The first line of each logical block must be `Block NNNN` (capital B,
|
||||
single space, decimal integer).
|
||||
2. Block numbers must be unique within a single capsule file.
|
||||
2. Block numbers must be unique within a single capsule file, and must fall
|
||||
in `[2048, 5120)`.
|
||||
3. Blocks are loaded in file order and executed top-to-bottom.
|
||||
4. Each block can hold up to 1024 bytes of source text.
|
||||
4. Each block can hold at most 16 content lines, each at most 64 characters
|
||||
long (`validate_forth_blocks` in `tools/mkcapsule.c`, corrected 2026-09-19
|
||||
against the tool itself — **not** a 1024-byte budget: 8 lines of 128
|
||||
characters is 1024 bytes and still fails, since the limit is enforced
|
||||
per-line and per-line-count, not as a total byte count). Verify with
|
||||
`mkcapsule --lint capsules/`, not `wc -c`.
|
||||
5. Comments use `( ... )` — parentheses with spaces inside.
|
||||
6. Word definitions use `: NAME ... ;` — standard FORTH-79.
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user