Compare commits
58
Commits
master
..
dac7ffac92
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dac7ffac92 | ||
|
|
bbd1b4047f | ||
|
|
44ffd261bb | ||
|
|
26a6a16537 | ||
|
|
880560fc00 | ||
|
|
c10d3a9cca | ||
|
|
01b5bbb23f | ||
|
|
b137678691 | ||
|
|
e178a76c36 | ||
|
|
7b42a9b6c1 | ||
|
|
443439c3d2 | ||
|
|
31fcdcd0a3 | ||
|
|
7b710aa322 | ||
|
|
861f800b7f | ||
|
|
2c16183788 | ||
|
|
03d8d991ef | ||
|
|
481d484e93 | ||
|
|
84c7711763 | ||
|
|
cb66db1107 | ||
|
|
7c5be22799 | ||
|
|
e50cc1f73a | ||
|
|
030ca63639 | ||
|
|
13335dcc9c | ||
|
|
40178098e9 | ||
|
|
7e3932a4ad | ||
|
|
7a21f07ad5 | ||
|
|
638aeb6636 | ||
|
|
6a019b8968 | ||
|
|
2e4d1330ea | ||
|
|
f030edcd08 | ||
|
|
13ec4b1b6c | ||
|
|
8235078b3b | ||
|
|
1ce705783c | ||
|
|
e117333a87 | ||
|
|
a2a4a85d38 | ||
|
|
f4bfe7de02 | ||
|
|
a2d21d5cda | ||
|
|
29cf2688d2 | ||
|
|
9e03bdab19 | ||
|
|
8f37e15f8a | ||
|
|
54e1cdafb1 | ||
|
|
ab3b708540 | ||
|
|
d0accdbb88 | ||
|
|
167fdc3973 | ||
|
|
981f4180ce | ||
|
|
d948e0c1a3 | ||
|
|
4f493fa317 | ||
|
|
5ac52f19d6 | ||
|
|
6a371b850e | ||
|
|
092036c97a | ||
|
|
93d09840da | ||
|
|
3b9d1e4337 | ||
|
|
41f59afeb3 | ||
|
|
9a391eb0ad | ||
|
|
067f317c47 | ||
|
|
1f98034311 | ||
|
|
a8b70e88d3 | ||
|
|
f238f02afa |
+1
-1
@@ -14,7 +14,7 @@ Checks: >
|
|||||||
|
|
||||||
WarningsAsErrors: [ ]
|
WarningsAsErrors: [ ]
|
||||||
|
|
||||||
HeaderFilterRegex: '^src/.*|^include/.*'
|
HeaderFilterRegex: '^v3/(src|include)/.*|^kernel/(src|include)/.*'
|
||||||
|
|
||||||
AnalyzeTemporaryDtors: false
|
AnalyzeTemporaryDtors: false
|
||||||
FormatStyle: none
|
FormatStyle: none
|
||||||
|
|||||||
+28
-28
@@ -6,8 +6,8 @@
|
|||||||
# clang-tidy will ALWAYS false-positive them. Always.
|
# clang-tidy will ALWAYS false-positive them. Always.
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
src/platform/**
|
v3/src/platform/**
|
||||||
src/platform/*
|
v3/src/platform/*
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# Skip test infrastructure
|
# Skip test infrastructure
|
||||||
@@ -15,8 +15,8 @@ src/platform/*
|
|||||||
# and stress edge conditions clang-tidy will never grok.
|
# and stress edge conditions clang-tidy will never grok.
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
src/test_runner/**
|
v3/src/test_runner/**
|
||||||
src/test_runner/*
|
v3/src/test_runner/*
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# Skip word definitions generated or "mechanically patterned"
|
# Skip word definitions generated or "mechanically patterned"
|
||||||
@@ -24,30 +24,30 @@ src/test_runner/*
|
|||||||
# clang-tidy gives ZERO useful feedback on them.
|
# clang-tidy gives ZERO useful feedback on them.
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
src/word_source/string_words.c
|
v3/src/word_source/string_words.c
|
||||||
src/word_source/block_words.c
|
v3/src/word_source/block_words.c
|
||||||
src/word_source/arithmetic_words.c
|
v3/src/word_source/arithmetic_words.c
|
||||||
src/word_source/mixed_arithmetic_words.c
|
v3/src/word_source/mixed_arithmetic_words.c
|
||||||
src/word_source/return_stack_words.c
|
v3/src/word_source/return_stack_words.c
|
||||||
src/word_source/defining_words.c
|
v3/src/word_source/defining_words.c
|
||||||
src/word_source/stack_words.c
|
v3/src/word_source/stack_words.c
|
||||||
src/word_source/logical_words.c
|
v3/src/word_source/logical_words.c
|
||||||
src/word_source/memory_words.c
|
v3/src/word_source/memory_words.c
|
||||||
src/word_source/io_words.c
|
v3/src/word_source/io_words.c
|
||||||
src/word_source/dictionary_words.c
|
v3/src/word_source/dictionary_words.c
|
||||||
src/word_source/dictionary_manipulation_words.c
|
v3/src/word_source/dictionary_manipulation_words.c
|
||||||
src/word_source/vocabulary_words.c
|
v3/src/word_source/vocabulary_words.c
|
||||||
src/word_source/system_words.c
|
v3/src/word_source/system_words.c
|
||||||
src/word_source/starforth_words.c
|
v3/src/word_source/starforth_words.c
|
||||||
src/word_source/editor_words.c
|
v3/src/word_source/editor_words.c
|
||||||
src/word_source/format_words.c
|
v3/src/word_source/format_words.c
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# Skip headers belonging to the word system
|
# Skip headers belonging to the word system
|
||||||
# Same reason: no signal, infinite false positives.
|
# Same reason: no signal, infinite false positives.
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
src/word_source/include/*
|
v3/src/word_source/include/*
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# Skip anything that implements performance knobs,
|
# 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.
|
# These are meant to be weird, and tidy can't reason about them.
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
src/stack_management.c
|
v3/src/stack_management.c
|
||||||
src/profiler.c
|
v3/src/profiler.c
|
||||||
src/vm_debug.c
|
v3/src/vm_debug.c
|
||||||
src/physics_runtime.c
|
v3/src/physics_runtime.c
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# Skip integration + stress torture-tests.
|
# Skip integration + stress torture-tests.
|
||||||
# These *intentionally* do UB-like bullshit.
|
# These *intentionally* do UB-like bullshit.
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
src/test_runner/modules/*stress*
|
v3/src/test_runner/modules/*stress*
|
||||||
src/test_runner/modules/*integration*
|
v3/src/test_runner/modules/*integration*
|
||||||
|
|||||||
+20
-20
@@ -224,43 +224,43 @@ changes with any new C word registration).
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build kernel for a given architecture (amd64 default)
|
# Build kernel for a given architecture (amd64 default)
|
||||||
make -f Makefile.starkernel ARCH=amd64
|
make -f kernel/Makefile ARCH=amd64
|
||||||
make -f Makefile.starkernel ARCH=aarch64
|
make -f kernel/Makefile ARCH=aarch64
|
||||||
make -f Makefile.starkernel ARCH=riscv64
|
make -f kernel/Makefile ARCH=riscv64
|
||||||
|
|
||||||
# Run in QEMU with OVMF
|
# Run in QEMU with OVMF
|
||||||
make -f Makefile.starkernel qemu
|
make -f kernel/Makefile qemu
|
||||||
make -f Makefile.starkernel ARCH=aarch64 qemu
|
make -f kernel/Makefile ARCH=aarch64 qemu
|
||||||
make -f Makefile.starkernel ARCH=riscv64 qemu
|
make -f kernel/Makefile ARCH=riscv64 qemu
|
||||||
|
|
||||||
# Clean
|
# Clean
|
||||||
make -f Makefile.starkernel clean
|
make -f kernel/Makefile clean
|
||||||
```
|
```
|
||||||
|
|
||||||
Output: `build/<arch>/kernel/starkernel_loader.efi` + `build/<arch>/kernel/starkernel_kernel.elf`.
|
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`:
|
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
|
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
|
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
|
`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
|
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)
|
### Build configuration (Kconfig — real, wired, not vestigial)
|
||||||
|
|
||||||
Every kernel-only knob (`STARFORTH_ENABLE_VM`, `PARITY_MODE`, the shared physics/heartbeat
|
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`,
|
family, etc.) is an optional Kconfig symbol defined across `Kconfig`, `Kconfig.arch`,
|
||||||
`Kconfig.heartbeat`, `Kconfig.kernel`, `Kconfig.physics`, `Kconfig.variant` (~40 symbols
|
`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
|
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:
|
VM + capsule-birth + ACL active. A bare invocation uses the defaults it always has:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make -f Makefile.starkernel ARCH=amd64 menuconfig
|
make -f kernel/Makefile ARCH=amd64 menuconfig
|
||||||
make -f Makefile.starkernel ARCH=amd64 kernel_amd64_defconfig
|
make -f kernel/Makefile ARCH=amd64 kernel_amd64_defconfig
|
||||||
```
|
```
|
||||||
|
|
||||||
### Hosted VM (vendored, for local sanity only)
|
### Hosted VM (vendored, for local sanity only)
|
||||||
@@ -277,7 +277,7 @@ StarForth repo) were removed 2026-08-15 — they referenced
|
|||||||
in the actual generated `include/version.h` (which only has `STARFORTH_VERSION`,
|
in the actual generated `include/version.h` (which only has `STARFORTH_VERSION`,
|
||||||
`STARFORTH_ARCH`, `STARFORTH_TARGET`, `STARFORTH_TIMESTAMP`, `STARFORTH_VERSION_FULL`,
|
`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`, `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.
|
asked, don't silently fix them.
|
||||||
|
|
||||||
### Important: Linker Configuration
|
### Important: Linker Configuration
|
||||||
@@ -298,9 +298,9 @@ The vendored hosted `make` build (above) is NEVER used to validate kernel change
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run in this exact order for every kernel change:
|
# Run in this exact order for every kernel change:
|
||||||
make -f Makefile.starkernel ARCH=amd64 clean qemu
|
make -f kernel/Makefile ARCH=amd64 clean qemu
|
||||||
make -f Makefile.starkernel ARCH=aarch64 clean qemu
|
make -f kernel/Makefile ARCH=aarch64 clean qemu
|
||||||
make -f Makefile.starkernel ARCH=riscv64 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
|
**QEMU rule — non-negotiable:** Only ONE QEMU instance may run at a time, always in the
|
||||||
@@ -475,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
|
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'
|
"identify precisely what cannot be proven and why — the boundary between 'formally verified'
|
||||||
and 'not, for this specific reason'." Run `isabelle build -D proof/`
|
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
|
`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
|
broken one — this repo simply doesn't have the target at all, so there's nothing to
|
||||||
mistakenly invoke).
|
mistakenly invoke).
|
||||||
@@ -495,7 +495,7 @@ status for kernel work.
|
|||||||
|
|
||||||
- **Strict ANSI C99** — No GNU extensions, no C++ features
|
- **Strict ANSI C99** — No GNU extensions, no C++ features
|
||||||
- **Zero warnings target, with four explicit exceptions — corrected 2026-08-18, previous
|
- **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=unused-parameter -Wno-error=shift-negative-value -Wno-error=sign-compare
|
||||||
-Wno-error=missing-field-initializers` — those four classes are enabled (still visible as
|
-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
|
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
|
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||||
# Note: If this tag is empty the current directory is searched.
|
# Note: If this tag is empty the current directory is searched.
|
||||||
|
|
||||||
INPUT = include \
|
INPUT = v3/include \
|
||||||
src \
|
v3/src \
|
||||||
src/word_source \
|
v3/src/word_source \
|
||||||
src/test_runner \
|
v3/src/test_runner \
|
||||||
|
kernel/include \
|
||||||
|
kernel/src \
|
||||||
README.md
|
README.md
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# 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.
|
# RECURSIVE has no effect here.
|
||||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
||||||
|
|
||||||
INCLUDE_PATH = include \
|
INCLUDE_PATH = v3/include \
|
||||||
src/word_source/include \
|
kernel/include \
|
||||||
src/test_runner/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
|
# 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
|
# patterns (like *.h and *.hpp) to filter out the header-files in the
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ choice
|
|||||||
default ARCH_AMD64
|
default ARCH_AMD64
|
||||||
help
|
help
|
||||||
Selects the value passed as ARCH= to whichever Makefile
|
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
|
Both Makefiles already accept "amd64"/"aarch64"/"riscv64" as
|
||||||
canonical spellings (each has its own alias-normalizing logic
|
canonical spellings (each has its own alias-normalizing logic
|
||||||
for other spellings like x86_64/arm64/riscv), so this choice
|
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
|
builds without needing a special case. This is UI/model
|
||||||
correctness (menuconfig can't offer a choice LithosAnanke has no
|
correctness (menuconfig can't offer a choice LithosAnanke has no
|
||||||
way to honor -- there are no pthreads in a freestanding kernel),
|
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 +=
|
keeps its own unconditional `VM_FEATURE_OVERRIDES +=
|
||||||
-DHEARTBEAT_THREAD_ENABLED=0` post-override exactly as it was
|
-DHEARTBEAT_THREAD_ENABLED=0` post-override exactly as it was
|
||||||
before this migration. Deliberately redundant with the `depends
|
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,
|
kernel that only reaches the M0-M6 hardware milestones (console,
|
||||||
PMM, VMM, interrupts, timers, kmalloc) with no FORTH interpreter,
|
PMM, VMM, interrupts, timers, kmalloc) with no FORTH interpreter,
|
||||||
no capsules, no "ok" REPL. Gates a large source-file selection
|
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
|
config PARITY_MODE
|
||||||
bool "Deterministic parity harness mode (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)
|
## Status — M7.1 (Capsule System · Multi-VM Fleet)
|
||||||
|
|
||||||
| Milestone | | Status |
|
| Milestone | | Status |
|
||||||
@@ -48,19 +67,39 @@ POST at boot: **parity hash verified across amd64/aarch64/riscv64 · Mama capsul
|
|||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```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)
|
# 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
|
# Run in QEMU with OVMF
|
||||||
make -f Makefile.starkernel qemu
|
make -f kernel/Makefile qemu
|
||||||
|
|
||||||
# Other architectures
|
# Other architectures
|
||||||
make -f Makefile.starkernel ARCH=aarch64 qemu
|
make -f kernel/Makefile ARCH=aarch64 qemu
|
||||||
make -f Makefile.starkernel ARCH=riscv64 qemu
|
make -f kernel/Makefile ARCH=riscv64 qemu
|
||||||
```
|
```
|
||||||
|
|
||||||
Artifacts: `build/amd64/kernel/starkernel_loader.efi` · `build/amd64/kernel/starkernel_kernel.elf`
|
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
|
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,
|
the separate **StarForth** repository — LithosAnanke used to be a branch inside that repo,
|
||||||
now it's its own project with its own `master`.
|
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
|
Every kernel-only knob (`STARFORTH_ENABLE_VM`, `PARITY_MODE`, the shared
|
||||||
physics/heartbeat family, etc.) is an optional Kconfig symbol — a plain
|
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:
|
you opt in:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make -f Makefile.starkernel ARCH=amd64 menuconfig
|
make -f kernel/Makefile ARCH=amd64 menuconfig
|
||||||
make -f Makefile.starkernel ARCH=amd64 kernel_amd64_defconfig
|
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.
|
# firmware picks up.
|
||||||
kernel=kernel_2712.img
|
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
|
# Disable the firmware's Linux-compatible-image sanity check. Without
|
||||||
# this, official docs describe it as checking for "a compatible Device
|
# this, official docs describe it as checking for "a compatible Device
|
||||||
# Tree file before attempting to boot" and warn that "older non-compatible
|
# 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.
|
|
||||||
+42
-40
@@ -1,5 +1,5 @@
|
|||||||
# Capsule Block Manifest — Auto-generated
|
# Capsule Block Manifest — Auto-generated
|
||||||
<!-- Generated by mkcapsule --manifest 2026-09-22T22:29:26Z -->
|
<!-- Generated by mkcapsule --manifest 2026-10-01T19:21:21Z -->
|
||||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||||
<!-- Hand-written justifications and immutability notes live -->
|
<!-- Hand-written justifications and immutability notes live -->
|
||||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||||
@@ -8,42 +8,44 @@
|
|||||||
|
|
||||||
| Capsule | Blocks claimed | xxHash64 | Signed |
|
| Capsule | Blocks claimed | xxHash64 | Signed |
|
||||||
|---------|----------------|----------|--------|
|
|---------|----------------|----------|--------|
|
||||||
| `ACL.4th` | 4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 4015 | `0xf8890c05c0d8f921` | 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` | 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` | 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` | 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 | `0xe6fba9ae56e1c916` | n/a |
|
||||||
| `block-acl.4th` | 4019, 4020 | `0xf6cc2a59e3a6734e` | yes |
|
| `block-acl.4th` | 4019, 4020 | `0xf6cc2a59e3a6734e` | n/a |
|
||||||
| `doe-campaign.4th` | 4060, 4061, 4062, 4063, 4064, 4065 | `0x26fb485e5c9dc6ff` | yes |
|
| `doe-campaign.4th` | 4060, 4061, 4062, 4063, 4064, 4065 | `0x26fb485e5c9dc6ff` | n/a |
|
||||||
| `doe.4th` | 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107 | `0xf154616d248e861f` | yes |
|
| `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` | 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` | 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` | 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` | n/a |
|
||||||
| `hestia:init.4th` | 4986, 4987, 4988 | `0x64e111990fbc45ff` | yes |
|
| `hestia:init.4th` | 4986, 4987, 4988 | `0x64e111990fbc45ff` | n/a |
|
||||||
| `init-l8-diverse.4th` | 4820, 4821, 4822 | `0xaa293201a6c91838` | yes |
|
| `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` | yes |
|
| `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` | yes |
|
| `init-l8-stable.4th` | 4806 | `0xdc3830f189063a9a` | n/a |
|
||||||
| `init-l8-temporal.4th` | 4830, 4831 | `0x51abd4c138246651` | yes |
|
| `init-l8-temporal.4th` | 4830, 4831 | `0x51abd4c138246651` | n/a |
|
||||||
| `init-l8-transition.4th` | 4840, 4841, 4842 | `0xbcc1a81976f0a4c9` | yes |
|
| `init-l8-transition.4th` | 4840, 4841, 4842 | `0xbcc1a81976f0a4c9` | n/a |
|
||||||
| `init-l8-volatile.4th` | 4810, 4811, 4812, 4813 | `0x98caabbbd92abac4` | yes |
|
| `init-l8-volatile.4th` | 4810, 4811, 4812, 4813 | `0x98caabbbd92abac4` | n/a |
|
||||||
| `init.4th` | 2049, 2050, 2057 | `0xea038ba684c53443` | yes |
|
| `init.4th` | 2049, 2050, 2057 | `0xea038ba684c53443` | n/a |
|
||||||
| `lib.4th` | 4050 | `0x4b216635c359ef73` | yes |
|
| `lib.4th` | 4050 | `0x4b216635c359ef73` | n/a |
|
||||||
| `multiuser-doe.4th` | 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055 | `0x22140588ee7c39e6` | yes |
|
| `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` | yes |
|
| `sdk.4th` | 5109, 5110, 5111, 5112, 5113, 5114, 5115 | `0x008fdbbb62c94a3a` | n/a |
|
||||||
| `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` | yes |
|
| `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` | n/a |
|
||||||
| `user-font-demo.4th` | 4200, 4201, 4202 | `0xce1fd7d1b581a56d` | yes |
|
| `user-font-demo.4th` | 4200, 4201, 4202 | `0xce1fd7d1b581a56d` | n/a |
|
||||||
| `workload-0.4th` | 2200, 2201 | `0x93f86f60aeba8feb` | yes |
|
| `workload-0.4th` | 2200, 2201 | `0x93f86f60aeba8feb` | n/a |
|
||||||
| `workload-1-lite.4th` | 5058, 5059 | `0x44a7a7e3176dcc8d` | yes |
|
| `workload-1-lite.4th` | 5058, 5059 | `0x44a7a7e3176dcc8d` | n/a |
|
||||||
| `workload-1.4th` | 4406, 4415, 4425, 4435 | `0x63e251adb0a03613` | yes |
|
| `workload-1.4th` | 4406, 4415, 4425, 4435 | `0x63e251adb0a03613` | n/a |
|
||||||
| `workload-2.4th` | 4506, 4515, 4525, 4535, 4545 | `0xf113b3d0bcccae47` | yes |
|
| `workload-2.4th` | 4506, 4515, 4525, 4535, 4545 | `0xf113b3d0bcccae47` | n/a |
|
||||||
| `workload-3.4th` | 4606, 4615, 4625, 4635, 4645, 4655, 4665 | `0x62b7a71576ad1041` | yes |
|
| `workload-3.4th` | 4606, 4615, 4625, 4635, 4645, 4655, 4665 | `0x62b7a71576ad1041` | n/a |
|
||||||
| `workload-4.4th` | 2130, 2131, 2132 | `0x099619264e650b1c` | yes |
|
| `workload-4.4th` | 2130, 2131, 2132 | `0x099619264e650b1c` | n/a |
|
||||||
| `workload-5-lite.4th` | 5056, 5057 | `0xea0303f38824f254` | yes |
|
| `workload-5-lite.4th` | 5056, 5057 | `0xea0303f38824f254` | n/a |
|
||||||
| `workload-5.4th` | *(none — raw code capsule)* | `0x526964c7c9506a11` | yes |
|
| `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` | yes |
|
| `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` | yes |
|
| `workload-7.4th` | 2150 | `0x02862291387eb7ac` | n/a |
|
||||||
| `workload-8.4th` | 2160 | `0x56b7f2f0efa000df` | yes |
|
| `workload-8.4th` | 2160 | `0x56b7f2f0efa000df` | n/a |
|
||||||
| `workload-9.4th` | 4706, 4715, 4725, 4735, 4745 | `0x3f2bec73142aa424` | yes |
|
| `workload-9.4th` | 4706, 4715, 4725, 4735, 4745 | `0x3f2bec73142aa424` | n/a |
|
||||||
| `workload-calib1.4th` | 5043 | `0x3b9f2d17b554fabc` | yes |
|
| `workload-calib1.4th` | 5043 | `0x3b9f2d17b554fabc` | n/a |
|
||||||
| `zuse-eligibility.4th` | 4021, 4022 | `0x7b28f4776a32e0b7` | yes |
|
| `zuse-eligibility.4th` | 4021, 4022 | `0x7b28f4776a32e0b7` | n/a |
|
||||||
| `zuse.4th` | 4016, 4017, 4018 | `0x490ded9be257a90b` | yes |
|
| `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)
|
## Block Map (sorted by LBN)
|
||||||
|
|
||||||
@@ -109,9 +111,9 @@
|
|||||||
| 4007 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
| 4007 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||||
| 4008 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
| 4008 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||||
| 4015 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
| 4015 | `ACL.4th` | `0xf8890c05c0d8f921` | ok |
|
||||||
| 4016 | `zuse.4th` | `0x490ded9be257a90b` | ok |
|
| 4016 | `zuse.4th` | `0xa14d5a7a88791cf6` | ok |
|
||||||
| 4017 | `zuse.4th` | `0x490ded9be257a90b` | ok |
|
| 4017 | `zuse.4th` | `0xa14d5a7a88791cf6` | ok |
|
||||||
| 4018 | `zuse.4th` | `0x490ded9be257a90b` | ok |
|
| 4018 | `zuse.4th` | `0xa14d5a7a88791cf6` | ok |
|
||||||
| 4019 | `block-acl.4th` | `0xf6cc2a59e3a6734e` | ok |
|
| 4019 | `block-acl.4th` | `0xf6cc2a59e3a6734e` | ok |
|
||||||
| 4020 | `block-acl.4th` | `0xf6cc2a59e3a6734e` | ok |
|
| 4020 | `block-acl.4th` | `0xf6cc2a59e3a6734e` | ok |
|
||||||
| 4021 | `zuse-eligibility.4th` | `0x7b28f4776a32e0b7` | ok |
|
| 4021 | `zuse-eligibility.4th` | `0x7b28f4776a32e0b7` | ok |
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
# configs/
|
# configs/
|
||||||
|
|
||||||
Example Kconfig `defconfig` files — starting points for `make -f
|
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
|
hosted target, matching the current committed default build behavior for
|
||||||
each profile. Loaded via `tools/kconfig/conf`.
|
each profile. Loaded via `tools/kconfig/conf`.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
+514
-179
@@ -18,7 +18,7 @@ model produces the same results as the v3 C primitive it replaces.
|
|||||||
0. [Fates](#0-fates)
|
0. [Fates](#0-fates)
|
||||||
1. [The v4 core ISA](#1-the-v4-core-isa)
|
1. [The v4 core ISA](#1-the-v4-core-isa)
|
||||||
2. [Compiler conventions](#2-compiler-conventions)
|
2. [Compiler conventions](#2-compiler-conventions)
|
||||||
3. [Open decisions](#3-open-decisions)
|
3. [Decisions](#3-decisions)
|
||||||
4. [Foundation layer (new words)](#4-foundation-layer-new-words)
|
4. [Foundation layer (new words)](#4-foundation-layer-new-words)
|
||||||
5. [Fate of every v3 primitive](#5-fate-of-every-v3-primitive)
|
5. [Fate of every v3 primitive](#5-fate-of-every-v3-primitive)
|
||||||
6. [Messaging between nodes](#6-messaging-between-nodes)
|
6. [Messaging between nodes](#6-messaging-between-nodes)
|
||||||
@@ -48,9 +48,9 @@ model produces the same results as the v3 C primitive it replaces.
|
|||||||
| Item | v4 node |
|
| Item | v4 node |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Cell | 32 bits (mesh node). The host node's width is a build parameter; see D-5. |
|
| Cell | 32 bits (mesh node). The host node's width is a build parameter; see D-5. |
|
||||||
| Addressing | Word-addressed (pending D-1). |
|
| 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). |
|
| Registers | `T` (top of data stack), `S` (second), `R` (top of return stack), `P` (program counter), `A` and `B` (address registers). |
|
||||||
| Stacks | Hardware stacks below `S` and `R`. Depth and visibility are D-2. |
|
| 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. |
|
| Instruction word | Six 5-bit slots, plus 2 spare bits. |
|
||||||
|
|
||||||
### 1.2 Instruction word layout
|
### 1.2 Instruction word layout
|
||||||
@@ -147,28 +147,40 @@ IF body1 ELSE body2 THEN → if L1 drop body1 jump L2
|
|||||||
times, as on the F18. `FOR ... UNEXT` is the same but the body must fit in one instruction word.
|
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
|
**Register conventions.** `A` and `B` are caller-saved. A word that uses them says so. Words in this
|
||||||
document that clobber `A`: `@ ! +! -! 2@ 2! C@ C! UM* * UM/MOD SEND RECV`. Words that clobber `B`:
|
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`:
|
||||||
`SEND RECV`.
|
`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.
|
**Return-stack words** (`>R R> R@ 2>R 2R> 2R@ I J UNLOOP` and the loop runtimes) are always IN.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Open decisions
|
## 3. Decisions
|
||||||
|
|
||||||
These must be settled before the golden model is built. Where a definition below depends on one, it
|
Ruled by Captain Bob, 2026-10-01, except D-4 and D-6, which are deferred to development step 2 (the
|
||||||
says so.
|
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 | Default in this document |
|
| ID | Decision | Ruling |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| **D-1** | Word addressing (pure Moore) or byte addressing. | Word addressing. `C@`/`C!` are CAP; `CELLS` is a no-op. |
|
| **D-1** | Word addressing (pure Moore) or byte addressing. | **Word addressing.** `C@`/`C!` are CAP; `CELLS` is a no-op. |
|
||||||
| **D-2** | Stack depth, and whether the stacks are visible (needed by `DEPTH`, `PICK`, `ROLL`, `SP@`, `SP!`). | Stacks backed by node RAM, with the data stack pointer exposed as MM register `DSP`. |
|
| **D-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. | Carry is kept (extended multiply step), so `UM*` returns a full 64-bit product. |
|
| **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. | Symbolic names only (§6, §7). |
|
| **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. | 32 on a Zynq-7000 (Cortex-A9); 64 on an aarch64 host. The compiler capsule is written width-independent. |
|
| **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. | Per-opcode and per-call-target. Transition counters deferred. |
|
| **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. | Fix to ANS (count from the top). v3's bottom-counting `ROLL` is retired. |
|
| **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-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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -193,31 +205,91 @@ dependency order.
|
|||||||
: U< ( u1 u2 -- flag ) 2DUP xor 0< IF NIP 0< ELSE - 0< THEN ;
|
: U< ( u1 u2 -- flag ) 2DUP xor 0< IF NIP 0< ELSE - 0< THEN ;
|
||||||
|
|
||||||
\ ---- multiply (D-3) -----------------------------------------------------
|
\ ---- multiply (D-3) -----------------------------------------------------
|
||||||
: UM* ( u1 u2 -- ulo uhi ) a! 0 31 FOR +* UNEXT push drop a pop ;
|
\ 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 --------------
|
\ ---- 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 )
|
: UM/MOD ( ulo uhi ud -- urem uquot )
|
||||||
a!
|
a! 31 FOR
|
||||||
31 FOR
|
-if L0 \ hi's top bit set:
|
||||||
over 0< NEGATE push \ R: lo's top bit (1/0)
|
2* over -if L1 drop 1 + jump L2 \ hi' = 2hi + top bit of lo
|
||||||
dup 0< push \ R: hi's top bit (-1/0)
|
L1: drop L2: push 2* pop \ lo' = 2lo
|
||||||
2* pop pop SWAP push OR pop \ lo hi' t hi shifted, lo bit brought in
|
jump SUB \ true hi' >= 2^n > d
|
||||||
push SWAP 2* SWAP pop \ lo' hi' t
|
L0:
|
||||||
over a U< 0= OR \ lo' hi' flag t OR hi' >= d
|
2* over -if L3 drop 1 + jump L4
|
||||||
IF a - SWAP 1 OR SWAP THEN
|
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
|
NEXT
|
||||||
SWAP ;
|
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) -------------
|
\ ---- 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 )
|
: SM/REM ( d n -- rem quot )
|
||||||
2DUP xor push \ R: quotient sign
|
over over xor push \ R: quotient sign (top bit)
|
||||||
over push \ R: remainder sign (sign of dividend)
|
over push \ R: + remainder sign (top bit of d)
|
||||||
ABS push DABS pop UM/MOD
|
-if L0 inv 1 + L0: push \ R: + |n|
|
||||||
pop 0< IF push NEGATE pop THEN
|
-if L1 DNEGATE L1: \ |d|
|
||||||
pop 0< IF NEGATE THEN ;
|
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`, `-`, `ABS`, and `DABS` are defined in §5; the compiler resolves forward references within the
|
`2DUP`, `-`, and `DNEGATE` are defined in §5; the compiler resolves forward references within the
|
||||||
core capsule.
|
core capsule.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -233,53 +305,71 @@ Section numbers match the v3 primitive reference.
|
|||||||
| `DROP` | OP | `drop` |
|
| `DROP` | OP | `drop` |
|
||||||
| `DUP` | OP | `dup` |
|
| `DUP` | OP | `dup` |
|
||||||
| `OVER` | OP | `over` |
|
| `OVER` | OP | `over` |
|
||||||
| `SWAP` | CAP | §4 |
|
| `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 | `dup IF dup THEN` |
|
| `?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 |
|
| `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 | `ROT ROT` |
|
| `-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` | MM | Read `DSP` (D-2). |
|
| `DEPTH` | RET | No visible stack pointer (D-2). |
|
||||||
| `PICK` | MM + CAP | Read stack RAM at `DSP − n` (D-2). 0-based, as in v3. |
|
| `PICK` | RET | No visible stack pointer (D-2). |
|
||||||
| `ROLL` | CAP | Rewritten with ANS semantics (D-7), using `PICK` and a copy loop. |
|
| `ROLL` | RET | No visible stack pointer (D-2); D-7 is moot. |
|
||||||
|
|
||||||
### 5.2 Return stack
|
### 5.2 Return stack
|
||||||
|
|
||||||
| Word | Fate | v4 definition |
|
| Word | Fate | v4 definition |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `>R` | IN | `push` |
|
| `>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` |
|
| `R>` | IN | `pop` — executed on the golden model (2026-10-03), see `>R`. |
|
||||||
| `R@` | IN | `pop dup push` |
|
| `R@` | IN | `pop dup push` — executed on the golden model (2026-10-03), see `>R`. |
|
||||||
|
|
||||||
### 5.3 Memory
|
### 5.3 Memory
|
||||||
|
|
||||||
| Word | Fate | v4 definition / notes |
|
| Word | Fate | v4 definition / notes |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `@` | IN | `a! @` |
|
| `@` | IN | `a! @` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||||
| `!` | IN | `a! !` |
|
| `!` | IN | `a! !` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||||
| `+!` | CAP | `a! @ + !` |
|
| `+!` | CAP | `a! @ + !` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||||
| `-!` | CAP | `a! NEGATE @ + !` |
|
| `-!` | 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) |
|
| `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 !+ !` |
|
| `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). |
|
| `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). |
|
| `C!` | CAP | See below (D-1). Call-free. Executed on the golden model (2026-10-03). |
|
||||||
| `FILL` | CAP | See below. |
|
| `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 | `push 2DUP U< IF pop CMOVE> ELSE pop CMOVE THEN` |
|
| `MOVE` | CAP | `( addr1 addr2 n -- )` — FORTH-79 (ruled 2026-10-04): `n` cells from `addr1` to `addr2`, the cell at `addr1` first; nothing for `n <= 0`. The addresses are word addresses (D-1). `-if OK drop drop drop ; OK: if DONE push over a! @ over a! ! 1 + push 1 + pop pop -1 + jump OK DONE: drop drop drop ;` v3's `MOVE` moved bytes, in whichever direction did not overwrite what it had yet to read; that is `CMOVE` and `CMOVE>`. Executed on the golden model (2026-10-04) against C, overlapping both ways. Leaves its caller 6 data cells and 6 return entries. Clobbers `A`. |
|
||||||
| `ERASE` | CAP | `0 FILL` |
|
| `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). Becomes `2* 2*` if D-1 chooses bytes. |
|
| `CELLS` | IN | Empty (word-addressed, D-1); v3 answers 24 for `3 CELLS`. Executed on the golden model (2026-10-03). |
|
||||||
|
|
||||||
```forth
|
```forth
|
||||||
\ byte access on a word-addressed node, little-endian
|
\ 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 )
|
: C@ ( baddr -- c )
|
||||||
dup 3 and 3 LSHIFT SWAP 2 RSHIFT a! @ SWAP RSHIFT 255 and ;
|
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 -- )
|
: C! ( c baddr -- )
|
||||||
dup 2 RSHIFT a! \ A = word address
|
dup 2/ 2/ a! 3 and push 255 and pop \ c' k A: word address
|
||||||
3 and 3 LSHIFT \ c bits
|
if K0 -1 + if K1 -1 + if K2
|
||||||
SWAP 255 and over LSHIFT \ bits c'
|
drop 23 FOR 2* UNEXT @ 4278190080 inv and + ! ; \ byte 3
|
||||||
SWAP 255 SWAP LSHIFT inv \ c' ~mask
|
K2: drop 15 FOR 2* UNEXT @ -16711681 and + ! ; \ byte 2
|
||||||
@ and OR ! ;
|
K1: drop 7 FOR 2* UNEXT @ -65281 and + ! ; \ byte 1
|
||||||
|
K0: drop @ -256 and + ! ; \ byte 0
|
||||||
|
|
||||||
: FILL ( baddr u c -- )
|
: FILL ( baddr u c -- )
|
||||||
SWAP BEGIN dup WHILE 1- push 2DUP SWAP C! SWAP 1+ SWAP pop REPEAT 2DROP drop ;
|
-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
|
### 5.4 Arithmetic
|
||||||
@@ -287,17 +377,17 @@ Section numbers match the v3 primitive reference.
|
|||||||
| Word | Fate | v4 definition / notes |
|
| Word | Fate | v4 definition / notes |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `+` | OP | `+` |
|
| `+` | OP | `+` |
|
||||||
| `-` | CAP | `NEGATE +` |
|
| `-` | CAP | `NEGATE +`, `NEGATE` in line — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||||
| `*` | CAP | `UM* drop` |
|
| `*` | 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` |
|
| `/` | 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. |
|
| `MOD` `/MOD` `*/` `*/MOD` (§4 versions) | RET | Shadowed duplicates. Only the §6 versions survive. |
|
||||||
| `1+` `1-` `2+` `2-` | IN | `1 +`, `-1 +`, `2 +`, `-2 +` |
|
| `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*` |
|
||||||
| `2/` | OP | `2/` |
|
| `2/` | OP | `2/` |
|
||||||
| `ABS` | CAP | `dup 0< IF NEGATE THEN` |
|
| `ABS` | CAP | `dup 0< IF NEGATE THEN` — executed on the golden model (2026-10-02). |
|
||||||
| `NEGATE` | CAP | §4 |
|
| `NEGATE` | CAP | §4. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||||
| `MIN` | CAP | `2DUP > IF SWAP THEN drop` |
|
| `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 | `2DUP < IF SWAP THEN drop` |
|
| `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
|
### 5.5 Logic and comparison
|
||||||
|
|
||||||
@@ -305,25 +395,25 @@ Section numbers match the v3 primitive reference.
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `AND` | OP | `and` |
|
| `AND` | OP | `and` |
|
||||||
| `XOR` | OP | `xor` |
|
| `XOR` | OP | `xor` |
|
||||||
| `OR` | CAP | §4 |
|
| `OR` | CAP | §4. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||||
| `INVERT` | OP | `inv` |
|
| `INVERT` | OP | `inv` |
|
||||||
| `NOT` | CAP | `0=` (FORTH-79 logical not, as in v3) |
|
| `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` |
|
| `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) |
|
| `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 |
|
| `0=` `0<` | CAP | §4. Executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||||
| `0<>` | CAP | `0= 0=` |
|
| `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 | `dup 0< SWAP 0= OR 0=` (correct for the most negative number) |
|
| `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=` |
|
| `=` | CAP | `xor 0=` — executed on the golden model (2026-10-02). |
|
||||||
| `<>` | CAP | `xor 0<>` |
|
| `<>` | 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) |
|
| `<` | CAP | `2DUP xor 0< IF drop 0< ELSE - 0< THEN` (overflow-safe) — executed on the golden model (2026-10-02). |
|
||||||
| `>` | CAP | `SWAP <` |
|
| `>` | 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=` |
|
| `<=` | 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=` |
|
| `>=` | 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 |
|
| `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<` |
|
| `U>` | CAP | `SWAP U<` — executed on the golden model (2026-10-02). |
|
||||||
| `WITHIN` | CAP | `over - push - pop U<` |
|
| `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` |
|
| `TRUE` | IN | `-1` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||||
| `FALSE` | IN | `0` |
|
| `FALSE` | IN | `0` — executed on the golden model (2026-10-03), including a transcript of the v3 binary. |
|
||||||
|
|
||||||
### 5.6 Mixed-precision arithmetic
|
### 5.6 Mixed-precision arithmetic
|
||||||
|
|
||||||
@@ -331,40 +421,41 @@ A double is two 32-bit cells on a mesh node.
|
|||||||
|
|
||||||
| Word | Fate | v4 definition |
|
| Word | Fate | v4 definition |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `M+` | CAP | `S>D D+` |
|
| `M+` | CAP | `S>D D+` — executed on the golden model (2026-10-02). |
|
||||||
| `M-` | CAP | `NEGATE M+` |
|
| `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 | `2DUP xor push ABS SWAP ABS UM* pop 0< IF DNEGATE THEN` |
|
| `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 | `SM/REM` |
|
| `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` |
|
| `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` |
|
| `/MOD` | CAP | `push S>D pop SM/REM` — executed on the golden model (2026-10-02). |
|
||||||
| `*/` | CAP | `*/MOD NIP` |
|
| `*/` | 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 | `push M* pop SM/REM` |
|
| `*/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
|
### 5.7 Double-cell numbers
|
||||||
|
|
||||||
| Word | Fate | v4 definition |
|
| Word | Fate | v4 definition |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `S>D` | CAP | `dup 0<` |
|
| `S>D` | CAP | `dup 0<` — executed on the golden model (2026-10-02). |
|
||||||
| `D+` | CAP | `push SWAP push over + 2DUP U> ROT drop NEGATE pop pop + +` |
|
| `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 SWAP inv SWAP 1 0 D+` |
|
| `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+` |
|
| `D-` | CAP | `DNEGATE D+` — executed on the golden model (2026-10-02). |
|
||||||
| `DABS` | CAP | `dup 0< IF DNEGATE THEN` |
|
| `DABS` | CAP | `dup 0< IF DNEGATE THEN` — executed on the golden model (2026-10-02). |
|
||||||
| `D0=` | CAP | `OR 0=` |
|
| `D0=` | CAP | `OR 0=` — executed on the golden model (2026-10-02). |
|
||||||
| `D0<` | CAP | `NIP 0<` |
|
| `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=` |
|
| `D=` | CAP | `D- D0=` — executed on the golden model (2026-10-02). |
|
||||||
| `D<` | CAP | `ROT 2DUP = IF 2DROP U< ELSE SWAP < NIP NIP THEN` |
|
| `D<` | CAP | `ROT 2DUP = IF 2DROP U< ELSE SWAP < NIP NIP THEN` — executed on the golden model (2026-10-02). |
|
||||||
| `DMAX` | CAP | `2OVER 2OVER D< IF 2SWAP THEN 2DROP` |
|
| `(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). |
|
||||||
| `DMIN` | CAP | `2OVER 2OVER D< 0= IF 2SWAP THEN 2DROP` |
|
| `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). |
|
||||||
| `D2*` | CAP | `2* over 0< NEGATE OR SWAP 2* SWAP` |
|
| `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 | `dup 1 and push 2/ SWAP 1 RSHIFT pop IF MSB OR THEN SWAP` |
|
| `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. |
|
||||||
| `2DROP` | IN | `drop drop` |
|
| `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`. |
|
||||||
| `2DUP` | IN | `over over` |
|
| `2DROP` | IN | `drop drop` — executed on the golden model (2026-10-03). |
|
||||||
| `2SWAP` | CAP | `ROT push ROT pop` |
|
| `2DUP` | IN | `over over` — executed on the golden model (2026-10-03). |
|
||||||
| `2OVER` | CAP | `push push 2DUP pop pop 2SWAP` |
|
| `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. |
|
||||||
| `2ROT` | CAP | `2>R 2SWAP 2R> 2SWAP` |
|
| `2OVER` | CAP | `push push 2DUP pop pop 2SWAP` — executed on the golden model (2026-10-02). |
|
||||||
| `2>R` | IN | `SWAP push push` |
|
| `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. |
|
||||||
| `2R>` | IN | `pop pop SWAP` |
|
| `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 2DUP push push SWAP` |
|
| `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
|
### 5.8 Number formatting and output
|
||||||
|
|
||||||
@@ -372,53 +463,196 @@ All output reaches the console through `EMIT` (DEV).
|
|||||||
|
|
||||||
| Word | Fate | Notes |
|
| Word | Fate | Notes |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `<#` `#` `#S` `HOLD` `SIGN` `#>` | CAP | Standard pictured-output definitions over `UM/MOD` and a hold buffer. v3's tolerant `#>` (pops `ud` only if present) is not kept; v4 follows the standard stack effect. |
|
| `<#` `#` `#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`. |
|
||||||
| `.` `.R` `U.` `U.R` `D.` `D.R` | CAP | Built on pictured output and `TYPE`. |
|
| `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. |
|
||||||
| `.S` | CAP | Walks the stack via `DSP` (D-2). |
|
| `.` `.R` `U.` `U.R` `D.` `D.R` | CAP | Built on pictured output and `TYPE`; definitions below. `.`, `U.` and `D.` print the number in the current base, then one space. The `.R` words right-justify it in `width` columns, print a wider number whole, pad nothing for `width <= 0`, and print no space after it: that is FORTH-79's reference word (ruled 2026-10-04); v3 printed a space after these too. 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`. |
|
||||||
| `?` | CAP | `@ .` |
|
| `(W)` | CAP | Variable, 2 cells: the field width of the `.R` words, and whether a blank follows the number. Like `BASE` and `HLD`, it lives in node memory, so the width is on neither stack while the picture runs. |
|
||||||
| `DUMP` | CAP | Loop over `@`/`C@` with pictured output. |
|
| `.S` | RET | No visible stack pointer (D-2). |
|
||||||
| `BASE` | CAP | Variable. |
|
| `?` | 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`. |
|
||||||
| `DECIMAL` `HEX` `OCTAL` | CAP | `10 BASE !` and so on. |
|
| `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 -- )
|
||||||
|
0 (W) 1 + b! !b \ no blank after it
|
||||||
|
BODY: (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 (W) 1 + b! @b if DONE drop jump SPACE
|
||||||
|
DONE: drop ;
|
||||||
|
: . ( n -- ) -1 (W) 1 + b! !b 0 jump BODY \ a blank after it
|
||||||
|
: U.R ( u width -- ) 0 (W) 1 + b! !b UBODY: (W) b! !b 0 <# #S #> jump TAIL
|
||||||
|
: U. ( u -- ) -1 (W) 1 + b! !b 0 jump UBODY
|
||||||
|
: D.R ( d width -- ) 0 (W) 1 + b! !b
|
||||||
|
DBODY: (W) b! !b dup push -if A DNEGATE A: <# #S pop SIGN #> jump TAIL
|
||||||
|
: D. ( d -- ) -1 (W) 1 + b! !b 0 jump DBODY
|
||||||
|
```
|
||||||
|
|
||||||
|
```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 and the blank flag set, entered by a jump past where `.R` clears
|
||||||
|
that flag, and all six share one tail, which prints the blank only when the flag is set. 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
|
### 5.9 Strings, parsing, and input
|
||||||
|
|
||||||
| Word | Fate | Notes |
|
| Word | Fate | Notes |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `COUNT` | CAP | `dup 1+ SWAP C@` |
|
| `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 | See below. |
|
| `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 | See below. |
|
| `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` |
|
| `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 | Loop from the end while the character is a space. |
|
| `-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 | Byte loop returning `-1`, `0`, or `1`. v3's counted-string auto-detection is not kept. |
|
| `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 | Nested loop over `COMPARE`. Same note. |
|
| `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 | Byte loops. |
|
| `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`. |
|
||||||
| `BL` | IN | `32` |
|
| `(S)` | CAP | Variable, 4 cells: `COMPARE`'s two lengths; `SEARCH`'s string 2 and the whole of string 1. |
|
||||||
| `EXPECT` `QUERY` | CC | Built on `KEY` (DEV). |
|
| `BL` | IN | `32` — executed on the golden model (2026-10-04). |
|
||||||
| `SPAN` `TIB` `>IN` `SOURCE` | CC | Interpreter state on the host node. |
|
| `EXPECT` `QUERY` | CC | Built on `KEY` (DEV); source in `v4/capsule/input.v4`. FORTH-79 (ruled 2026-10-04). `EXPECT ( baddr n -- )` stores characters from `baddr` upward until a new-line (taken, not stored) or until `n` have been received, then a zero; no action for `n <= 0`; a line longer than `n` leaves the rest to be read next. It also sets `SPAN`, which is not FORTH-79 but is v3's. `QUERY` is `TIB 80 EXPECT 0 >IN !`. v3's `EXPECT` was C's `fgets` and took `n-1` characters, and its `QUERY` took 1024. Executed on the golden model's host node (2026-10-04) against C for every size and line length. `EXPECT` leaves its caller 6 data cells and 4 return entries. |
|
||||||
| `WORD` `ENCLOSE` | CC | Parser. |
|
| `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). |
|
||||||
| `NUMBER` `CONVERT` | CC | Rewritten to honour `BASE` (v3's `NUMBER` is base 10 only). |
|
| `WORD` `ENCLOSE` | CC | Parser; source in `v4/capsule/input.v4`. `WORD ( c -- baddr )` is FORTH-79 (ruled 2026-10-04): characters are taken from `TIB` until the delimiter `c` or the end of the text, leading delimiters ignored, and stored as a counted string; the delimiter met (`c`, or a zero if the text ran out) is stored after them, uncounted; `>IN` is left just past it; with nothing left the count is 0. The count is a byte, so a word over 255 characters is cut to 255; the buffer is 257 bytes. v3's `WORD` skipped every delimiter after the word, always stored a zero, and cut at 62. `ENCLOSE ( baddr c -- baddr n1 n2 n3 )`, not a FORTH-79 word, works on a zero-terminated string as v3's. Executed on the golden model's host node (2026-10-04) against C, and `ENCLOSE` against values recorded from the v3 binary. `WORD` keeps what it works on in memory and has at most three cells of its own on the data stack: it leaves its caller 7 data cells and 6 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 2 return entries; the interpreter does not use it (it has a single-cell conversion of its own that needs far less of the stack). |
|
||||||
| `S"` `(s")` `[']` | CC | Compiler words. |
|
| `S"` `(s")` `[']` | CC | Compiler words. |
|
||||||
| `LITERAL` `[LITERAL]` (placeholders) | RET | The working `LITERAL` is in §5.17. |
|
| `LITERAL` `[LITERAL]` (placeholders) | RET | The working `LITERAL` is in §5.17. |
|
||||||
|
|
||||||
```forth
|
```forth
|
||||||
|
\ SWAP and the subtractions ( - ) are in line.
|
||||||
: CMOVE ( src dst u -- )
|
: CMOVE ( src dst u -- )
|
||||||
BEGIN dup WHILE 1- push over C@ over C! 1+ SWAP 1+ SWAP pop REPEAT drop 2DROP ;
|
-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 -- )
|
: CMOVE> ( src dst u -- )
|
||||||
BEGIN dup WHILE 1- push over R@ + C@ over R@ + C! pop REPEAT drop 2DROP ;
|
-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
|
### 5.10 Terminal I/O
|
||||||
|
|
||||||
| Word | Fate | Notes |
|
| Word | Fate | Notes |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `EMIT` | DEV | Console service: one-character message. |
|
| `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. |
|
| `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. |
|
| `?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 | Loop of `C@ EMIT`, or one string message to the console node. |
|
| `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` |
|
| `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` |
|
| `SPACE` | CAP | `BL EMIT`, as `32 jump EMIT` — executed on the golden model (2026-10-03). |
|
||||||
| `SPACES` | CAP | `BEGIN dup 0> WHILE SPACE 1- REPEAT drop` |
|
| `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. |
|
| `."` `(do-string)` | CC | Compiler words. |
|
||||||
|
|
||||||
### 5.11 Blocks and mass storage
|
### 5.11 Blocks and mass storage
|
||||||
@@ -440,14 +674,33 @@ The storage service belongs to the Artemis role, now a device node.
|
|||||||
|
|
||||||
| Word | Fate | Notes |
|
| Word | Fate | Notes |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `HERE` `ALIGN` `ALLOT` `,` `C,` `2,` `PAD` `LATEST` | CC | The dictionary lives on the host node. |
|
| `HERE` `ALIGN` `ALLOT` `,` `C,` `2,` `PAD` `LATEST` | CC | The dictionary lives on the host node; source in `v4/capsule/dict.v4`. The dictionary pointer `DP` is a byte address, so `C,` packs four characters to a cell; `HERE` is the next whole cell (a word address); `,` and `ALLOT` align first. An address unit is a cell (D-1), so `ALLOT` counts cells where v3's counted bytes; a negative count gives space back. `2,` stores the low cell first, as v3. Going outside the dictionary space stores nothing, moves nothing and sets `NODE-ERROR`. `PAD` is a fixed scratch area, as in v3, given as a byte address. `LATEST` is the xt of the newest entry, 0 if there is none (v3's pushes `HERE`, which looks like a bug; reported, not changed). Executed on the golden model's host node (2026-10-04). `,` calls nothing and leaves its caller 7 data cells and 8 return entries; `ALLOT` leaves 7 and 7. |
|
||||||
| `SP@` `SP!` | MM | `DSP` register (D-2). |
|
| `SP@` `SP!` | RET | No visible stack pointer (D-2). |
|
||||||
|
|
||||||
### 5.13 Dictionary manipulation
|
### 5.13 Dictionary manipulation
|
||||||
|
|
||||||
| Word | Fate |
|
| Word | Fate |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `'` `FIND` `SMUDGE` `HIDDEN` `>BODY` `>NAME` `NAME>` `>LINK` `LINK>` `CFA` `LFA` `NFA` `PFA` `TRAVERSE` `INTERPRET` | CC |
|
| `FIND` | CC | `( -- xt \| 0 )`, FORTH-79 and v3 alike: the compilation address of the next word in the input stream, or 0 if it is not in the dictionary. `32 WORD` then a search from the newest entry back, skipping hidden ones; names are case sensitive, as v3, and 31 characters are significant (FORTH-79). Executed on the golden model's host node (2026-10-04) against a list kept in C, 300 entries. Leaves its caller 7 data cells and 5 return entries. |
|
||||||
|
| `'` | CC | `( -- addr )`, FORTH-79: the parameter field address of the next word in the input stream; not found is an error (0, and `NODE-ERROR` set). v3's `'` returned what `FIND` returns. For a word that is code the two are the same address; for a data word the parameter field is the cell after its code, which is what FORTH-79's `n ' NAME !` needs. It is immediate: inside a definition the address is compiled as a literal (FORTH-79), where v3's `'` parsed when the definition ran. Executed on the golden model's host node (2026-10-04). |
|
||||||
|
| `>LINK` `LFA` `LINK>` `>NAME` `NFA` `NAME>` `CFA` `PFA` `>BODY` `TRAVERSE` | CC | The fields of an entry, all reached from its xt, as in v3 where the xt was the entry: `>LINK`/`LFA` the link's address, `LINK>` the older entry's xt, `>NAME`/`NFA` the byte address of the counted name, `NAME>` back to the xt, `CFA` the xt itself, `PFA`/`>BODY` the parameter field, `TRAVERSE` from a name's count byte to past its last character (for `n > 0`; otherwise unchanged, as v3). Executed on the golden model's host node (2026-10-04). |
|
||||||
|
| `SMUDGE` `HIDDEN` | CC | `SMUDGE` toggles the hidden flag of the newest entry; `HIDDEN` sets it. v3 refuses both outside compilation; that check comes with the compiler words. Executed on the golden model's host node (2026-10-04). |
|
||||||
|
| `INTERPRET` | CC | FORTH-79; source in `v4/capsule/compile.v4`. Takes words from the input stream until it is exhausted: a word in the dictionary is executed, or, when compiling and not immediate, compiled; anything else must be a number in the current `BASE` (a single cell, with an optional leading minus), left on the stack or compiled as a literal. What is neither, or a compile-only word met while interpreting, abandons the line: compiling stops, the definition under way stays hidden, the rest of the input is skipped and `NODE-ERROR` is set (the message and the stack reset come with `QUIT`). Executed on the golden model's host node (2026-10-04). |
|
||||||
|
|
||||||
|
An entry, as `v4/capsule/dict.v4` builds it. A word's execution address (xt) is the address of its
|
||||||
|
code, and everything else is found from it:
|
||||||
|
|
||||||
|
```
|
||||||
|
name the count byte, then the characters, four bytes to a cell, zero-padded to a whole cell
|
||||||
|
xt - 3 flags: 1 immediate, 2 hidden, 4 data
|
||||||
|
xt - 2 the word address of the name
|
||||||
|
xt - 1 the link: the xt of the entry before this one, 0 for the first
|
||||||
|
xt the code
|
||||||
|
```
|
||||||
|
|
||||||
|
A name holds at most 31 characters. A *data* word (one made by `CREATE`, `VARIABLE` or `CONSTANT`) is
|
||||||
|
one whose code is a single call to its run-time routine, with its parameter field in the cell after
|
||||||
|
it, `xt + 1`; for any other word the parameter field is the code itself.
|
||||||
|
|
||||||
### 5.14 Vocabularies
|
### 5.14 Vocabularies
|
||||||
|
|
||||||
@@ -459,8 +712,8 @@ The storage service belongs to the Artemis role, now a device node.
|
|||||||
|
|
||||||
| Word | Fate | Notes |
|
| Word | Fate | Notes |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `(` `\` | CC | |
|
| `(` `\` | 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) |
|
| `EXECUTE` | CAP | `push ;` (tail-jumps to the xt; the xt returns to `EXECUTE`'s caller). Executed on the golden model's host node (2026-10-04). |
|
||||||
| `NOP` | OP | `nop` |
|
| `NOP` | OP | `nop` |
|
||||||
| `QUIT` `ABORT` `ABORT"` `(ABORT")` `COLD` `WARM` | CC | |
|
| `QUIT` `ABORT` `ABORT"` `(ABORT")` `COLD` `WARM` | CC | |
|
||||||
| `BYE` `REBOOT` | HERA | |
|
| `BYE` `REBOOT` | HERA | |
|
||||||
@@ -479,39 +732,105 @@ The storage service belongs to the Artemis role, now a device node.
|
|||||||
|
|
||||||
| Word | Fate | Notes |
|
| Word | Fate | Notes |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `:` `;` `CREATE` `VARIABLE` `CONSTANT` `DOES>` `IMMEDIATE` `STATE` `[` `]` `LITERAL` `COMPILE` `[COMPILE]` `FORGET` `FENCE` | CC | |
|
| `:` `;` `EXIT` `IMMEDIATE` `STATE` `[` `]` `LITERAL` `COMPILE` `[COMPILE]` | CC | Source in `v4/capsule/compile.v4`, all to FORTH-79. `:` makes an entry and starts compiling; the word cannot be found until `;`, so a redefinition can use the old word. `;` lays the return, reveals the word and stops compiling. `LITERAL` compiles the number on the stack when compiling. `COMPILE xxx` lays code that compiles `xxx` when the word it is in runs; `[COMPILE] xxx` compiles `xxx` though it is immediate. Executed on the golden model's host node (2026-10-04) against results recorded from the v3 binary; v3's `COMPILE` does not work (`: C1 COMPILE DUP ; IMMEDIATE : U3 C1 + ;` gives `DUP: Stack underflow`), reported, not fixed. |
|
||||||
|
| `CREATE` `VARIABLE` `CONSTANT` `DOES>` | CC | FORTH-79. A data word's code is one instruction word, a call to its run-time routine, and its parameter field is the cell after it: `(DOVAR)` is `pop ;` (the call's return address is the parameter field's address) and `(DOCON)` is `pop a! @ ;`. `VARIABLE` allots one cell, set to 0. `DOES>` lays a call to `(DOES)` and then `pop`: `(DOES)`, run by the defining word, rewrites the newest entry's code into a call to the code after it and returns from the defining word; that code's `pop` fetches the parameter field address. Executed on the golden model's host node (2026-10-04) against results recorded from the v3 binary. |
|
||||||
|
| `FORGET` `FENCE` | CC | |
|
||||||
| `LIT` | OP | `@p` |
|
| `LIT` | OP | `@p` |
|
||||||
| `does_rt` | RET | Internal helper; `DOES>` is implemented by the compiler capsule. |
|
| `does_rt` | RET | Internal helper; `DOES>` is implemented by the compiler capsule. |
|
||||||
|
|
||||||
|
**How a word is compiled.** v4 code is native: a colon definition is instruction words, and
|
||||||
|
compiling a word into one lays down a call to it. Two flags on an entry change that. An **in-line**
|
||||||
|
word's code is a straight run of opcodes and literals ending in `;`, and that run is copied in place
|
||||||
|
of a call; this is fate IN (§0), and everything that touches the return stack must be in line, since a
|
||||||
|
call would bury what it works on. A **compile-only** word may not be executed by the interpreter.
|
||||||
|
`v4/capsule/forth.v4` holds the first words of the vocabulary flagged this way (`DUP`, `+`, `>R`, `I`,
|
||||||
|
`LEAVE`, `@`, the in-line variables and so on).
|
||||||
|
|
||||||
|
**The short stacks (D-2) and the compiler.** The interpreter and compiler run on the same ten-cell and
|
||||||
|
nine-entry stacks as the user's words, with the user's values beneath them. Two consequences are
|
||||||
|
built into the capsule:
|
||||||
|
|
||||||
|
- Every word of the capsule keeps what it works on in memory and has at most three cells of its own on
|
||||||
|
the data stack. Measured on the golden model: a line may have **6 values on the stack** while the
|
||||||
|
interpreter reads its next word, and defining and running a word leaves 4 cells under it untouched.
|
||||||
|
- Words called from the interpreter may nest **8 deep**; a `DO` loop takes two return entries, so two
|
||||||
|
nested loops leave four. `*` is therefore not `UM* drop` on the host node but N steps of `+*` with
|
||||||
|
only the count on the return stack; it gives the same low cell (what `+*` loses at the top of `T`
|
||||||
|
takes N shifts to reach the bit that moves into `A`, and the loop ends first).
|
||||||
|
|
||||||
|
A programme that v3 runs with more than that on its stacks does not run here; that is D-2, not the
|
||||||
|
compiler.
|
||||||
|
|
||||||
|
**The code generator** (`v4/capsule/codegen.v4`) is what these words are built on. It packs opcodes,
|
||||||
|
literals and branches into instruction words at `HERE`, by the rules of §1.2 and §2: slots filled left
|
||||||
|
to right, unused slots `nop`, a literal's value in the cell after its word, `;` `ex` and any branch
|
||||||
|
closing the word. A branch goes only in a slot whose address field reaches all of the node's memory
|
||||||
|
(slots 0–2 on a 16,384-word host node, 0–3 on a 1,024-word one); otherwise it starts the next word, so
|
||||||
|
no reach check is ever needed.
|
||||||
|
|
||||||
|
| Word | Does |
|
||||||
|
| --- | --- |
|
||||||
|
| `(OP,)` | `( op -- )` any opcode that is neither a branch nor `@p` |
|
||||||
|
| `(LIT,)` | `( x -- )` `@p` and its value |
|
||||||
|
| `(LABEL)` | `( -- addr )` closes the word being built; the address of the next: a branch target |
|
||||||
|
| `(BRANCH,)` | `( target op -- )` a branch to a known address; `(JUMP,)` and `(CALL,)` `( addr -- )` are the two commonest |
|
||||||
|
| `(BRANCH>)` `(RESOLVE)` | `( op -- ref )` a branch whose address is not known yet, and `( target ref -- )` filling it in |
|
||||||
|
| `(FLUSH)` `(CG-RESET)` | append the word being built; forget it |
|
||||||
|
|
||||||
|
Executed on the golden model's host node (2026-10-04) by laying the same programmes down with the text
|
||||||
|
assembler on one node and with the code generator, running, on another, and comparing memory word for
|
||||||
|
word: every opcode, literals and branches in every slot position, and 600 random programmes. What it
|
||||||
|
lays down was then run.
|
||||||
|
|
||||||
### 5.18 Control flow
|
### 5.18 Control flow
|
||||||
|
|
||||||
| Word | Fate | v4 definition / notes |
|
| 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. |
|
| `IF` `ELSE` `THEN` `BEGIN` `UNTIL` `AGAIN` `WHILE` `REPEAT` `DO` `?DO` `LOOP` `+LOOP` | CC | Compile-time structure words, immediate and compile-only; source in `v4/capsule/compile.v4`. Expansions per §2 and below. What an opening word leaves for the word that closes it goes on a **control-flow stack in memory** (32 cells), not on the data stack, with a number saying which word left it; a closing word that finds the wrong number, or nothing, abandons the line. A loop that tests at its end (`UNTIL`) must drop the flag on both ways out, so `BEGIN` lays `jump L0 Ld: drop L0:` and `UNTIL` lays `if Ld drop`. Executed on the golden model's host node (2026-10-04): the code laid down for each is word for word what the text assembler makes of the expansion given here, and the programmes run give what the v3 binary gave. |
|
||||||
|
| `LEAVE` `I` `J` `UNLOOP` | CC | In-line words (below), compile-only. |
|
||||||
|
| `CASE` `OF` `ENDOF` `ENDCASE` | CC | |
|
||||||
| `EXIT` | OP | `;` |
|
| `EXIT` | OP | `;` |
|
||||||
| `(BRANCH)` | OP | `jump` |
|
| `(BRANCH)` | OP | `jump` |
|
||||||
| `(0BRANCH)` | IN | `if L … drop` (§2) |
|
| `(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 | `SWAP push push` (R: limit index) |
|
| `(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 | `2DUP = IF 2DROP jump past-loop THEN SWAP push push` |
|
| `(?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. |
|
| `(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 | Sign-aware boundary-crossing test; specified in the compiler capsule. |
|
| `(+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 dup push push jump loop-test` (sets index to limit) |
|
| `(LEAVE)` | IN | `pop pop drop dup push push` — FORTH-79 (ruled 2026-10-04): the limit is set equal to the index, so the loop ends at the next `LOOP` or `+LOOP`; the index is unchanged and the rest of the body still runs. v3's `LEAVE` left the loop at once (FORTH-83's behaviour) and, having set the index to the limit, left both on its return stack, 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-04) in `LOOP` and `+LOOP`, up, down and with a step of 0. |
|
||||||
| `I` | IN | `pop dup push` |
|
| `I` | IN | `pop dup push` — executed on the golden model (2026-10-03). |
|
||||||
| `J` | IN | `pop pop pop dup push SWAP push SWAP push` |
|
| `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 pop drop drop` |
|
| `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:
|
(LOOP) expansion:
|
||||||
pop 1 + pop \ index' limit
|
pop 1 + pop \ index' limit
|
||||||
2DUP xor \ index' limit flag (0 when equal)
|
<less> \ index' limit s
|
||||||
if Lexit
|
-if Lexit \ not less: done
|
||||||
drop push push \ R: limit index'
|
drop push push \ R: limit index'
|
||||||
jump Lbody
|
jump Lbody
|
||||||
Lexit: drop drop drop
|
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)` terminates when the index reaches the limit, which matches v3 for every loop where
|
`(LOOP)` goes round again while `index' < limit`, signed, which is v3's rule. So `0 5 DO … LOOP` runs
|
||||||
`start < limit`.
|
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
|
**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.
|
index should use them; they cost one instruction per iteration.
|
||||||
@@ -581,20 +900,33 @@ index should use them; they cost one instruction per iteration.
|
|||||||
### 5.26 Q48.16 fixed-point math
|
### 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
|
A Q48.16 value is 64 bits, so on a 32-bit node it occupies **two cells** and every Q word is a double
|
||||||
word. Per D-8, v4 Q values are signed.
|
word. Per D-10 it occupies two cells on a 64-bit node too. Per D-8, v4 Q values are signed.
|
||||||
|
|
||||||
| Word | Fate | Notes |
|
| Word | Fate | Notes |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `Q.+` `Q.-` | CAP | `D+`, `D-` |
|
| `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 | 64×64 product from four `UM*` partial products, shifted right 16. |
|
| `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 | Shifted long division. **Division by zero sets an error** (v3 returned 0 silently). |
|
| `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`. |
|
||||||
| `Q.ABS` `Q.NEG` | CAP | `DABS`, `DNEGATE` |
|
| `(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`. |
|
||||||
| `Q.LOG` `Q.EXP` `Q.SQRT` `Q.SIN` `Q.COS` | CAP | Algorithms ported from `q48_words.c`; the hosted C versions are the golden model. |
|
| `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.FROM-INT` | CAP | `S>D` shifted left 16. Negative values are no longer clamped to 0. |
|
| `(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.TO-INT` | CAP | Shift right 16, take the low cell. |
|
| `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.1` `Q.0` `Q.SCALE` | IN | Double-cell constants. |
|
| `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.=` `Q.<` `Q.>` `Q.0=` `Q.MAX` `Q.MIN` | CAP | `D=`, `D<`, `SWAP D<` (for `Q.>`), `D0=`, `DMAX`, `DMIN`. Signed. |
|
| `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.PRINT` | CAP | Pictured output, five fractional digits. |
|
| `(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
|
### 5.27 Inference engine
|
||||||
|
|
||||||
@@ -688,7 +1020,6 @@ Addresses are assigned in the node memory map (D-4). Names only here.
|
|||||||
|
|
||||||
| Register | Access | Contents |
|
| Register | Access | Contents |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `DSP` | R/W | Data stack pointer (D-2). |
|
|
||||||
| `HEAT-OP[0..31]` | R | Per-opcode retirement heat. |
|
| `HEAT-OP[0..31]` | R | Per-opcode retirement heat. |
|
||||||
| `HEAT-CALL[...]` | R/W | Per-call-target heat, with freeze bit (D-6). |
|
| `HEAT-CALL[...]` | R/W | Per-call-target heat, with freeze bit (D-6). |
|
||||||
| `ANTICLOCK` | R | Virtual tick: a pure function of the execution stream. |
|
| `ANTICLOCK` | R | Virtual tick: a pure function of the execution stream. |
|
||||||
@@ -696,3 +1027,7 @@ Addresses are assigned in the node memory map (D-4). Names only here.
|
|||||||
| `GOV-*` | R/W | Governor parameters and Jacquard selector state (`L8-*`, decay rate). |
|
| `GOV-*` | R/W | Governor parameters and Jacquard selector state (`L8-*`, decay rate). |
|
||||||
| `REC-ENABLE` | R/W | DoE recorder on/off (`HB-ON` / `HB-OFF`). |
|
| `REC-ENABLE` | R/W | DoE recorder on/off (`HB-ON` / `HB-OFF`). |
|
||||||
| `PORT-STATUS` | R | Per-port ready flags, for non-blocking polls. |
|
| `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`. |
|
||||||
|
|||||||
@@ -224,3 +224,34 @@ LithosAnanke filing is a question for counsel.
|
|||||||
- K≡1.0 holds on the golden model at both widths, on all three host ISAs.
|
- 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.
|
- 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.
|
- 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.
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# Makefile.starkernel — StarKernel UEFI Build System
|
# kernel/Makefile — StarKernel UEFI Build System
|
||||||
# Multi-architecture: amd64 (x86_64), aarch64 (ARM64), riscv64 (RISC-V 64)
|
# Multi-architecture: amd64 (x86_64), aarch64 (ARM64), riscv64 (RISC-V 64)
|
||||||
#
|
#
|
||||||
# Quick start:
|
# Quick start:
|
||||||
# make -f Makefile.starkernel — build for host arch
|
# make -f kernel/Makefile — build for host arch
|
||||||
# make -f Makefile.starkernel ARCH=amd64 qemu — build + boot (x86_64)
|
# make -f kernel/Makefile ARCH=amd64 qemu — build + boot (x86_64)
|
||||||
# make -f Makefile.starkernel ARCH=aarch64 qemu — build + boot (aarch64)
|
# make -f kernel/Makefile ARCH=aarch64 qemu — build + boot (aarch64)
|
||||||
# make -f Makefile.starkernel ARCH=riscv64 qemu — build + boot (riscv64)
|
# make -f kernel/Makefile ARCH=riscv64 qemu — build + boot (riscv64)
|
||||||
# make -f Makefile.starkernel help — show all targets
|
# make -f kernel/Makefile help — show all targets
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@@ -64,7 +64,7 @@ $(eval $(call kconfig_bool,STARFORTH_ENABLE_VM,1))
|
|||||||
MONOLITHIC ?= 1
|
MONOLITHIC ?= 1
|
||||||
|
|
||||||
# Parallel build — use all available cores when not cleaning
|
# Parallel build — use all available cores when not cleaning
|
||||||
# (adding -j when 'clean' is also a goal causes a race on include/version.h)
|
# (adding -j when 'clean' is also a goal causes a race on v3/include/version.h)
|
||||||
NPROC := $(shell nproc 2>/dev/null || echo 4)
|
NPROC := $(shell nproc 2>/dev/null || echo 4)
|
||||||
ifeq ($(filter clean,$(MAKECMDGOALS)),)
|
ifeq ($(filter clean,$(MAKECMDGOALS)),)
|
||||||
MAKEFLAGS += -j$(NPROC)
|
MAKEFLAGS += -j$(NPROC)
|
||||||
@@ -106,9 +106,9 @@ KERNEL_OBJ_DIR := $(OBJ_DIR)/kernel
|
|||||||
LOADER_EFI := $(BUILD_DIR)/starkernel_loader.efi
|
LOADER_EFI := $(BUILD_DIR)/starkernel_loader.efi
|
||||||
KERNEL_ELF := $(BUILD_DIR)/starkernel_kernel.elf
|
KERNEL_ELF := $(BUILD_DIR)/starkernel_kernel.elf
|
||||||
|
|
||||||
KERNEL_SRC := src/starkernel
|
KERNEL_SRC := kernel/src
|
||||||
KERNEL_INC := include/starkernel
|
KERNEL_INC := kernel/include/starkernel
|
||||||
STARFORTH_CONFIG_HEADER := include/starforth_config.h
|
STARFORTH_CONFIG_HEADER := v3/include/starforth_config.h
|
||||||
|
|
||||||
QEMU_LOG_DIR := logs
|
QEMU_LOG_DIR := logs
|
||||||
QEMU_ISO := $(BUILD_DIR)/starkernel.iso
|
QEMU_ISO := $(BUILD_DIR)/starkernel.iso
|
||||||
@@ -129,17 +129,17 @@ ARTDISK ?= disk/artemis.img
|
|||||||
# headroom above the current 9-device identity roster (Zuse + 8 minted identities)
|
# headroom above the current 9-device identity roster (Zuse + 8 minted identities)
|
||||||
# rather than matching it exactly, per Bob's standing ruling against hardcoding a bound
|
# rather than matching it exactly, per Bob's standing ruling against hardcoding a bound
|
||||||
# to today's scale (FABRIC-3.md §VII.4). Override if a future test needs more, e.g.
|
# to today's scale (FABRIC-3.md §VII.4). Override if a future test needs more, e.g.
|
||||||
# `make -f Makefile.starkernel qemu XHCI_PORTS=32`.
|
# `make -f kernel/Makefile qemu XHCI_PORTS=32`.
|
||||||
XHCI_PORTS ?= 16
|
XHCI_PORTS ?= 16
|
||||||
# ZUSEDISK -- Zuse's own minted USB thumbdrive, attached by default so a
|
# ZUSEDISK -- Zuse's own minted USB thumbdrive, attached by default so a
|
||||||
# plain `make qemu` lands already authenticated into the Zuse identity
|
# plain `make qemu` lands already authenticated into the Zuse identity
|
||||||
# (Captain Bob, 2026-08-28). Attached at QEMU launch time via -drive/
|
# (Captain Bob, 2026-08-28). Attached at QEMU launch time via -drive/
|
||||||
# -device on the qemu-xhci controller each arch's qemu target already
|
# -device on the qemu-xhci controller each arch's qemu target already
|
||||||
# creates; xhci_bringup()'s initial port scan (src/starkernel/usb/xhci.c)
|
# creates; xhci_bringup()'s initial port scan (kernel/src/usb/xhci.c)
|
||||||
# is what makes an already-connected-at-launch device visible at all --
|
# is what makes an already-connected-at-launch device visible at all --
|
||||||
# xhci_poll_events() alone is purely hotplug-event-driven and would never
|
# xhci_poll_events() alone is purely hotplug-event-driven and would never
|
||||||
# see a device present before controller reset. Override to "" (empty) to
|
# see a device present before controller reset. Override to "" (empty) to
|
||||||
# boot without Zuse attached, e.g. `make -f Makefile.starkernel qemu ZUSEDISK=`.
|
# boot without Zuse attached, e.g. `make -f kernel/Makefile qemu ZUSEDISK=`.
|
||||||
ZUSEDISK ?= disk/thumbdrives/zuse-thumb-ident.img
|
ZUSEDISK ?= disk/thumbdrives/zuse-thumb-ident.img
|
||||||
# Precomputed (not inlined as $(if ...,...)) because the drive/device specs
|
# Precomputed (not inlined as $(if ...,...)) because the drive/device specs
|
||||||
# below are comma-heavy and GNU make's $(if) function splits its own
|
# below are comma-heavy and GNU make's $(if) function splits its own
|
||||||
@@ -153,10 +153,10 @@ else
|
|||||||
ZUSEDISK_QEMU_ARGS :=
|
ZUSEDISK_QEMU_ARGS :=
|
||||||
endif
|
endif
|
||||||
MKCAPSULE_SRC = tools/mkcapsule.c tools/pkcs8_ed25519.c \
|
MKCAPSULE_SRC = tools/mkcapsule.c tools/pkcs8_ed25519.c \
|
||||||
src/starkernel/crypto/ed25519.c \
|
kernel/src/crypto/ed25519.c \
|
||||||
src/starkernel/crypto/fe25519.c \
|
kernel/src/crypto/fe25519.c \
|
||||||
src/starkernel/crypto/scalar25519.c \
|
kernel/src/crypto/scalar25519.c \
|
||||||
src/starkernel/crypto/sha512.c
|
kernel/src/crypto/sha512.c
|
||||||
MKCAPSULE_BIN = $(BUILD_DIR)/tools/mkcapsule
|
MKCAPSULE_BIN = $(BUILD_DIR)/tools/mkcapsule
|
||||||
# Milestone 6 (Phase 8): the snakeoil intermediate's private key, generated
|
# Milestone 6 (Phase 8): the snakeoil intermediate's private key, generated
|
||||||
# offline outside this repo entirely (see FABRIC-2.md's Phase 8 §Milestone 6
|
# offline outside this repo entirely (see FABRIC-2.md's Phase 8 §Milestone 6
|
||||||
@@ -169,6 +169,28 @@ CAPSULE_GENERATED = $(BUILD_DIR)/capsule_generated.c
|
|||||||
CAPSULE_GENERATED_OBJ = $(BUILD_DIR)/capsule_generated.o
|
CAPSULE_GENERATED_OBJ = $(BUILD_DIR)/capsule_generated.o
|
||||||
CAPSULE_GENERATED_KOBJ = $(KERNEL_OBJ_DIR)/capsule_generated.o
|
CAPSULE_GENERATED_KOBJ = $(KERNEL_OBJ_DIR)/capsule_generated.o
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# BOARD (boot_image)
|
||||||
|
# ==============================================================================
|
||||||
|
# BOARD names a directory under boards/ (ser5, raspi, milkv, ...). Its
|
||||||
|
# board.mk sets BOARD_ARCH, BOARD_BOOT (how the firmware finds us) and
|
||||||
|
# BOARD_IMAGE (the single file written to the boot medium). The root
|
||||||
|
# Makefile's `make boot_image TARGET=<NAME>` maps NAME to BOARD and passes
|
||||||
|
# ARCH=$(BOARD_ARCH); the kernel objects themselves are the ordinary
|
||||||
|
# build/$(ARCH)/kernel ones, shared with `make qemu`.
|
||||||
|
BOARD ?=
|
||||||
|
ifneq ($(strip $(BOARD)),)
|
||||||
|
ifeq ($(wildcard boards/$(BOARD)/board.mk),)
|
||||||
|
$(error Unknown BOARD='$(BOARD)': no boards/$(BOARD)/board.mk)
|
||||||
|
endif
|
||||||
|
include boards/$(BOARD)/board.mk
|
||||||
|
ifneq ($(BOARD_ARCH),$(ARCH))
|
||||||
|
$(error BOARD=$(BOARD) is ARCH=$(BOARD_ARCH), but ARCH=$(ARCH) was requested)
|
||||||
|
endif
|
||||||
|
BOARD_OUT := build/boards/$(BOARD)
|
||||||
|
endif
|
||||||
|
BOOT_IMAGE_SIZE_MIB ?= 128
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# TOOLCHAIN
|
# TOOLCHAIN
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@@ -195,8 +217,8 @@ ifeq ($(ARCH),amd64)
|
|||||||
# correctly. These flags come after COMMON_CFLAGS on the command line so
|
# correctly. These flags come after COMMON_CFLAGS on the command line so
|
||||||
# they win for amd64 only; riscv64/aarch64 keep -fPIC.
|
# they win for amd64 only; riscv64/aarch64 keep -fPIC.
|
||||||
ARCH_CFLAGS := -m64 -march=x86-64 -mno-red-zone -DARCH_AMD64 -fno-pic -fno-pie
|
ARCH_CFLAGS := -m64 -march=x86-64 -mno-red-zone -DARCH_AMD64 -fno-pic -fno-pie
|
||||||
LOADER_LINKER_SCRIPT := linker/starkernel-loader-amd64.ld
|
LOADER_LINKER_SCRIPT := kernel/linker/starkernel-loader-amd64.ld
|
||||||
KERNEL_LINKER_SCRIPT := linker/starkernel-kernel-amd64.ld
|
KERNEL_LINKER_SCRIPT := kernel/linker/starkernel-kernel-amd64.ld
|
||||||
|
|
||||||
else ifeq ($(ARCH),aarch64)
|
else ifeq ($(ARCH),aarch64)
|
||||||
ifneq ($(shell which aarch64-linux-gnu-gcc 2>/dev/null),)
|
ifneq ($(shell which aarch64-linux-gnu-gcc 2>/dev/null),)
|
||||||
@@ -211,14 +233,16 @@ else ifeq ($(ARCH),aarch64)
|
|||||||
$(error No ARM64 toolchain found. Install aarch64-linux-gnu-gcc or aarch64-none-elf-gcc)
|
$(error No ARM64 toolchain found. Install aarch64-linux-gnu-gcc or aarch64-none-elf-gcc)
|
||||||
endif
|
endif
|
||||||
# aarch64 loader uses clang + lld-link (COFF target — GCC cannot produce aarch64 PE)
|
# aarch64 loader uses clang + lld-link (COFF target — GCC cannot produce aarch64 PE)
|
||||||
LOADER_CC := clang-18
|
# Any clang with the aarch64 COFF backend works; the version is not
|
||||||
# Debian/Ubuntu package only installs the versioned lld-link-18 on PATH by
|
# pinned (Ubuntu 26.04 ships clang-21, older hosts clang-18).
|
||||||
# default (unversioned lld-link lives under /usr/lib/llvm-18/bin, which
|
LOADER_CC := $(shell which clang 2>/dev/null || ls /usr/bin/clang-[0-9]* 2>/dev/null | sort -V | tail -1 || echo clang)
|
||||||
|
# Debian/Ubuntu package only installs the versioned lld-link-NN on PATH by
|
||||||
|
# default (unversioned lld-link lives under /usr/lib/llvm-NN/bin, which
|
||||||
# isn't on PATH unless explicitly prepended) — detect whichever resolves.
|
# isn't on PATH unless explicitly prepended) — detect whichever resolves.
|
||||||
LOADER_LD := $(shell which lld-link 2>/dev/null || which lld-link-18 2>/dev/null || echo lld-link)
|
LOADER_LD := $(shell which lld-link 2>/dev/null || ls /usr/bin/lld-link-[0-9]* /usr/lib/llvm-*/bin/lld-link 2>/dev/null | sort -V | tail -1 || echo lld-link)
|
||||||
ARCH_CFLAGS := -march=armv8-a -mcpu=cortex-a72 -DARCH_AARCH64 -mno-outline-atomics
|
ARCH_CFLAGS := -march=armv8-a -mcpu=cortex-a72 -DARCH_AARCH64 -mno-outline-atomics
|
||||||
LOADER_LINKER_SCRIPT := linker/starkernel-loader-aarch64.ld
|
LOADER_LINKER_SCRIPT := kernel/linker/starkernel-loader-aarch64.ld
|
||||||
KERNEL_LINKER_SCRIPT := linker/starkernel-kernel-aarch64.ld
|
KERNEL_LINKER_SCRIPT := kernel/linker/starkernel-kernel-aarch64.ld
|
||||||
|
|
||||||
else ifeq ($(ARCH),riscv64)
|
else ifeq ($(ARCH),riscv64)
|
||||||
ifneq ($(shell which riscv64-unknown-elf-gcc 2>/dev/null),)
|
ifneq ($(shell which riscv64-unknown-elf-gcc 2>/dev/null),)
|
||||||
@@ -233,8 +257,8 @@ else ifeq ($(ARCH),riscv64)
|
|||||||
$(error No RISC-V toolchain found. Install riscv64-unknown-elf-gcc or riscv64-linux-gnu-gcc)
|
$(error No RISC-V toolchain found. Install riscv64-unknown-elf-gcc or riscv64-linux-gnu-gcc)
|
||||||
endif
|
endif
|
||||||
ARCH_CFLAGS := -march=rv64gc -mabi=lp64d -DARCH_RISCV64
|
ARCH_CFLAGS := -march=rv64gc -mabi=lp64d -DARCH_RISCV64
|
||||||
LOADER_LINKER_SCRIPT := linker/starkernel-loader-riscv64.ld
|
LOADER_LINKER_SCRIPT := kernel/linker/starkernel-loader-riscv64.ld
|
||||||
KERNEL_LINKER_SCRIPT := linker/starkernel-kernel-riscv64.ld
|
KERNEL_LINKER_SCRIPT := kernel/linker/starkernel-kernel-riscv64.ld
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Default loader toolchain = kernel toolchain (overridden for aarch64 above)
|
# Default loader toolchain = kernel toolchain (overridden for aarch64 above)
|
||||||
@@ -260,13 +284,13 @@ COMMON_CFLAGS := \
|
|||||||
-ffreestanding -nostdlib -fno-builtin \
|
-ffreestanding -nostdlib -fno-builtin \
|
||||||
-fPIC -fvisibility=hidden -fno-stack-protector -fshort-wchar \
|
-fPIC -fvisibility=hidden -fno-stack-protector -fshort-wchar \
|
||||||
$(ARCH_CFLAGS) \
|
$(ARCH_CFLAGS) \
|
||||||
-I$(KERNEL_INC) -Iinclude -Isrc \
|
-I$(KERNEL_INC) -Iv3/include -Ikernel/include -Iv3/src \
|
||||||
-include $(STARFORTH_CONFIG_HEADER) \
|
-include $(STARFORTH_CONFIG_HEADER) \
|
||||||
-DPARITY_MODE=$(PARITY_MODE)
|
-DPARITY_MODE=$(PARITY_MODE)
|
||||||
|
|
||||||
VMCORE_CFLAGS_COMMON := \
|
VMCORE_CFLAGS_COMMON := \
|
||||||
$(filter-out -I$(KERNEL_INC),$(COMMON_CFLAGS)) \
|
$(filter-out -I$(KERNEL_INC),$(COMMON_CFLAGS)) \
|
||||||
-Iinclude -I. -Isrc/word_source -Isrc/test_runner/include \
|
-Iv3/include -Ikernel/include -I. -Iv3/src/word_source -Iv3/src/test_runner/include \
|
||||||
-Wno-error=unused-parameter -Wno-error=shift-negative-value \
|
-Wno-error=unused-parameter -Wno-error=shift-negative-value \
|
||||||
-Wno-error=sign-compare -Wno-error=missing-field-initializers
|
-Wno-error=sign-compare -Wno-error=missing-field-initializers
|
||||||
|
|
||||||
@@ -281,8 +305,8 @@ LOADER_BASE_CFLAGS := \
|
|||||||
-mno-stack-arg-probe \
|
-mno-stack-arg-probe \
|
||||||
-march=armv8-a \
|
-march=armv8-a \
|
||||||
-DARCH_AARCH64 \
|
-DARCH_AARCH64 \
|
||||||
-I$(KERNEL_INC) -Iinclude -Isrc \
|
-I$(KERNEL_INC) -Iv3/include -Ikernel/include -Iv3/src \
|
||||||
-Iinclude/starkernel/freestanding \
|
-Ikernel/include/starkernel/freestanding \
|
||||||
-include $(STARFORTH_CONFIG_HEADER) \
|
-include $(STARFORTH_CONFIG_HEADER) \
|
||||||
-DPARITY_MODE=$(PARITY_MODE) \
|
-DPARITY_MODE=$(PARITY_MODE) \
|
||||||
-DPLATFORM_TIME_NO_INLINE
|
-DPLATFORM_TIME_NO_INLINE
|
||||||
@@ -308,7 +332,7 @@ ifeq ($(ARCH),aarch64)
|
|||||||
else ifeq ($(ARCH),riscv64)
|
else ifeq ($(ARCH),riscv64)
|
||||||
LOADER_LDFLAGS_PE := # ELF→objcopy pipeline in link rule
|
LOADER_LDFLAGS_PE := # ELF→objcopy pipeline in link rule
|
||||||
else
|
else
|
||||||
LOADER_LINKER_SCRIPT_PE := linker/starkernel-loader-amd64-pe.ld
|
LOADER_LINKER_SCRIPT_PE := kernel/linker/starkernel-loader-amd64-pe.ld
|
||||||
LOADER_LDFLAGS_PE := \
|
LOADER_LDFLAGS_PE := \
|
||||||
-T $(LOADER_LINKER_SCRIPT_PE) -m i386pep -nostdlib \
|
-T $(LOADER_LINKER_SCRIPT_PE) -m i386pep -nostdlib \
|
||||||
--enable-reloc-section --image-base 0 --subsystem 10 -e efi_main
|
--enable-reloc-section --image-base 0 --subsystem 10 -e efi_main
|
||||||
@@ -429,7 +453,7 @@ LOADER_CFLAGS += $(VM_FEATURE_OVERRIDES)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
# SK_CMD — optional startup FORTH script injected before the interactive REPL.
|
# SK_CMD — optional startup FORTH script injected before the interactive REPL.
|
||||||
# Usage: make -f Makefile.starkernel qemu SK_CMD="TIME-TICKS . BYE"
|
# Usage: make -f kernel/Makefile qemu SK_CMD="TIME-TICKS . BYE"
|
||||||
ifdef SK_CMD
|
ifdef SK_CMD
|
||||||
KERNEL_CFLAGS += -DSK_STARTUP_FORTH='"$(SK_CMD)"'
|
KERNEL_CFLAGS += -DSK_STARTUP_FORTH='"$(SK_CMD)"'
|
||||||
LOADER_CFLAGS += -DSK_STARTUP_FORTH='"$(SK_CMD)"'
|
LOADER_CFLAGS += -DSK_STARTUP_FORTH='"$(SK_CMD)"'
|
||||||
@@ -439,7 +463,7 @@ endif
|
|||||||
# The loader reads this file from the EFI partition and parses it at boot.
|
# The loader reads this file from the EFI partition and parses it at boot.
|
||||||
# Supported flags: --doe --log-level=<debug|info|warn|error>
|
# Supported flags: --doe --log-level=<debug|info|warn|error>
|
||||||
# --stack=<N>[MG] --heap=<N>[MG]
|
# --stack=<N>[MG] --heap=<N>[MG]
|
||||||
# Usage: make -f Makefile.starkernel qemu KERNEL_ARGS="--doe --log-level=info"
|
# Usage: make -f kernel/Makefile qemu KERNEL_ARGS="--doe --log-level=info"
|
||||||
KERNEL_ARGS ?=
|
KERNEL_ARGS ?=
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@@ -534,19 +558,19 @@ KERNEL_ASM := $(wildcard $(KERNEL_SRC)/arch/$(ARCH)/*.S)
|
|||||||
|
|
||||||
ifeq ($(STARFORTH_ENABLE_VM),1)
|
ifeq ($(STARFORTH_ENABLE_VM),1)
|
||||||
VM_ALL_SRCS := \
|
VM_ALL_SRCS := \
|
||||||
$(wildcard src/*.c) \
|
$(wildcard v3/src/*.c) \
|
||||||
$(wildcard src/word_source/*.c) \
|
$(wildcard v3/src/word_source/*.c) \
|
||||||
$(wildcard src/test_runner/*.c) \
|
$(wildcard v3/src/test_runner/*.c) \
|
||||||
$(wildcard src/test_runner/modules/*.c)
|
$(wildcard v3/src/test_runner/modules/*.c)
|
||||||
|
|
||||||
VM_EXCLUDE := \
|
VM_EXCLUDE := \
|
||||||
src/main.c src/cli.c src/repl.c \
|
v3/src/main.c v3/src/cli.c v3/src/repl.c \
|
||||||
src/platform/% \
|
v3/src/platform/% \
|
||||||
src/blkio_factory.c src/blkio_file.c \
|
v3/src/blkio_factory.c v3/src/blkio_file.c \
|
||||||
src/log.c src/doe_metrics.c \
|
v3/src/log.c v3/src/doe_metrics.c \
|
||||||
src/vm.c src/vm_core.c src/vm_bootstrap.c src/vm_runtime.c src/vm_time.c \
|
v3/src/vm.c v3/src/vm_core.c v3/src/vm_bootstrap.c v3/src/vm_runtime.c v3/src/vm_time.c \
|
||||||
src/word_source/q48_16_words.c \
|
v3/src/word_source/q48_16_words.c \
|
||||||
src/test_runner/modules/break_me_tests.c
|
v3/src/test_runner/modules/break_me_tests.c
|
||||||
|
|
||||||
VM_CORE_SRCS := $(filter-out $(VM_EXCLUDE),$(VM_ALL_SRCS))
|
VM_CORE_SRCS := $(filter-out $(VM_EXCLUDE),$(VM_ALL_SRCS))
|
||||||
|
|
||||||
@@ -569,8 +593,8 @@ LOADER_EXTRA_SRCS := \
|
|||||||
|
|
||||||
KERNEL_EXTRA_SRCS := $(LOADER_EXTRA_SRCS)
|
KERNEL_EXTRA_SRCS := $(LOADER_EXTRA_SRCS)
|
||||||
|
|
||||||
LOADER_VM_OBJS := $(patsubst src/%.c,$(LOADER_OBJ_DIR)/vmcore/%.o,$(VM_CORE_SRCS))
|
LOADER_VM_OBJS := $(patsubst v3/src/%.c,$(LOADER_OBJ_DIR)/vmcore/%.o,$(VM_CORE_SRCS))
|
||||||
KERNEL_VM_OBJS := $(patsubst src/%.c,$(KERNEL_OBJ_DIR)/vmcore/%.o,$(VM_CORE_SRCS))
|
KERNEL_VM_OBJS := $(patsubst v3/src/%.c,$(KERNEL_OBJ_DIR)/vmcore/%.o,$(VM_CORE_SRCS))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LOADER_SRCS := $(LOADER_SRCS_BASE) $(LOADER_EXTRA_SRCS)
|
LOADER_SRCS := $(LOADER_SRCS_BASE) $(LOADER_EXTRA_SRCS)
|
||||||
@@ -603,7 +627,7 @@ KERNEL_OBJS := \
|
|||||||
# MAIN TARGETS
|
# MAIN TARGETS
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
|
||||||
all: include/version.h $(CAPSULE_GENERATED_OBJ) $(CAPSULE_GENERATED_KOBJ) $(LOADER_EFI) $(KERNEL_ELF)
|
all: v3/include/version.h $(CAPSULE_GENERATED_OBJ) $(CAPSULE_GENERATED_KOBJ) $(LOADER_EFI) $(KERNEL_ELF)
|
||||||
@echo "StarKernel built successfully for $(ARCH)"
|
@echo "StarKernel built successfully for $(ARCH)"
|
||||||
@echo " Loader: $(LOADER_EFI)"
|
@echo " Loader: $(LOADER_EFI)"
|
||||||
@echo " Kernel: $(KERNEL_ELF)"
|
@echo " Kernel: $(KERNEL_ELF)"
|
||||||
@@ -611,13 +635,13 @@ all: include/version.h $(CAPSULE_GENERATED_OBJ) $(CAPSULE_GENERATED_KOBJ) $(LOAD
|
|||||||
kernel: all
|
kernel: all
|
||||||
|
|
||||||
kernel-all:
|
kernel-all:
|
||||||
@$(MAKE) -f Makefile.starkernel ARCH=amd64 TARGET=$(TARGET)
|
@$(MAKE) -f kernel/Makefile ARCH=amd64 TARGET=$(TARGET)
|
||||||
@$(MAKE) -f Makefile.starkernel ARCH=aarch64 TARGET=$(TARGET)
|
@$(MAKE) -f kernel/Makefile ARCH=aarch64 TARGET=$(TARGET)
|
||||||
@$(MAKE) -f Makefile.starkernel ARCH=riscv64 TARGET=$(TARGET)
|
@$(MAKE) -f kernel/Makefile ARCH=riscv64 TARGET=$(TARGET)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf build/$(ARCH)/$(TARGET)
|
rm -rf build/$(ARCH)/$(TARGET)
|
||||||
rm -f include/version.h
|
rm -f v3/include/version.h
|
||||||
|
|
||||||
clean-kernel:
|
clean-kernel:
|
||||||
@echo "Cleaning all StarKernel build artifacts..."
|
@echo "Cleaning all StarKernel build artifacts..."
|
||||||
@@ -636,8 +660,8 @@ FORCE:
|
|||||||
# here after a hosted `make` run silently reuses that stale/wrong-flavored file
|
# here after a hosted `make` run silently reuses that stale/wrong-flavored file
|
||||||
# and fails with "LITHOS_VERSION_STR undeclared" deep in kernel_main.c, instead
|
# and fails with "LITHOS_VERSION_STR undeclared" deep in kernel_main.c, instead
|
||||||
# of regenerating its own correct version.
|
# of regenerating its own correct version.
|
||||||
include/version.h: FORCE
|
v3/include/version.h: FORCE
|
||||||
@mkdir -p include
|
@mkdir -p v3/include
|
||||||
@BUILD_TS=$$(date -Iseconds 2>/dev/null || echo "unknown"); \
|
@BUILD_TS=$$(date -Iseconds 2>/dev/null || echo "unknown"); \
|
||||||
printf '#ifndef STARFORTH_VERSION_H\n#define STARFORTH_VERSION_H\n\n' > $@; \
|
printf '#ifndef STARFORTH_VERSION_H\n#define STARFORTH_VERSION_H\n\n' > $@; \
|
||||||
printf '#define STARFORTH_VERSION "%s"\n' "$(VERSION)" >> $@; \
|
printf '#define STARFORTH_VERSION "%s"\n' "$(VERSION)" >> $@; \
|
||||||
@@ -653,7 +677,7 @@ include/version.h: FORCE
|
|||||||
$(MKCAPSULE_BIN): $(MKCAPSULE_SRC)
|
$(MKCAPSULE_BIN): $(MKCAPSULE_SRC)
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
@echo "HOSTCC $(MKCAPSULE_SRC)"
|
@echo "HOSTCC $(MKCAPSULE_SRC)"
|
||||||
@cc -std=c99 -Wall -Wextra -O2 -Iinclude -Itools -o $@ $(MKCAPSULE_SRC)
|
@cc -std=c99 -Wall -Wextra -O2 -Iv3/include -Ikernel/include -Itools -o $@ $(MKCAPSULE_SRC)
|
||||||
|
|
||||||
# Generate capsule_generated.c from capsules/
|
# Generate capsule_generated.c from capsules/
|
||||||
CAPSULE_SRCS := $(shell find $(CAPSULES_DIR) -type f ! -name '.*' 2>/dev/null)
|
CAPSULE_SRCS := $(shell find $(CAPSULES_DIR) -type f ! -name '.*' 2>/dev/null)
|
||||||
@@ -695,12 +719,12 @@ $(LOADER_OBJ_DIR)/%.o: $(KERNEL_SRC)/%.c | $(LOADER_OBJ_DIR)
|
|||||||
@$(LOADER_CC) $(LOADER_CFLAGS) -c $< -o $@
|
@$(LOADER_CC) $(LOADER_CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
# Compile loader VM core sources
|
# Compile loader VM core sources
|
||||||
$(LOADER_OBJ_DIR)/vmcore/%.o: src/%.c include/version.h | $(LOADER_OBJ_DIR)
|
$(LOADER_OBJ_DIR)/vmcore/%.o: v3/src/%.c v3/include/version.h | $(LOADER_OBJ_DIR)
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
@echo "CC (loader) $<"
|
@echo "CC (loader) $<"
|
||||||
ifeq ($(ARCH),aarch64)
|
ifeq ($(ARCH),aarch64)
|
||||||
@$(LOADER_CC) $(LOADER_CFLAGS) \
|
@$(LOADER_CC) $(LOADER_CFLAGS) \
|
||||||
-Iinclude -I. -Isrc/test_runner/include \
|
-Iv3/include -Ikernel/include -I. -Iv3/src/test_runner/include \
|
||||||
-Wno-error=unused-parameter -Wno-error=shift-negative-value \
|
-Wno-error=unused-parameter -Wno-error=shift-negative-value \
|
||||||
-Wno-error=sign-compare -Wno-error=missing-field-initializers \
|
-Wno-error=sign-compare -Wno-error=missing-field-initializers \
|
||||||
-Wno-unused-function \
|
-Wno-unused-function \
|
||||||
@@ -722,10 +746,10 @@ $(KERNEL_OBJ_DIR)/%.o: $(KERNEL_SRC)/%.c | $(KERNEL_OBJ_DIR)
|
|||||||
@$(CC) $(KERNEL_CFLAGS) -c $< -o $@
|
@$(CC) $(KERNEL_CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
# Compile kernel VM core sources
|
# Compile kernel VM core sources
|
||||||
$(KERNEL_OBJ_DIR)/vmcore/%.o: src/%.c include/version.h | $(KERNEL_OBJ_DIR)
|
$(KERNEL_OBJ_DIR)/vmcore/%.o: v3/src/%.c v3/include/version.h | $(KERNEL_OBJ_DIR)
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
@echo "CC (kernel) $<"
|
@echo "CC (kernel) $<"
|
||||||
@$(CC) $(VMCORE_CFLAGS_COMMON) $(filter-out -I$(KERNEL_INC),$(KERNEL_CFLAGS)) -Isrc/starkernel/vm -c $< -o $@
|
@$(CC) $(VMCORE_CFLAGS_COMMON) $(filter-out -I$(KERNEL_INC),$(KERNEL_CFLAGS)) -Ikernel/src/vm -c $< -o $@
|
||||||
|
|
||||||
# Compile kernel assembly sources
|
# Compile kernel assembly sources
|
||||||
$(KERNEL_OBJ_DIR)/%.o: $(KERNEL_SRC)/%.S | $(KERNEL_OBJ_DIR)
|
$(KERNEL_OBJ_DIR)/%.o: $(KERNEL_SRC)/%.S | $(KERNEL_OBJ_DIR)
|
||||||
@@ -802,7 +826,7 @@ OVMF_CODE := /usr/share/OVMF/OVMF_CODE_4M.fd
|
|||||||
OVMF_VARS_RO := /usr/share/OVMF/OVMF_VARS_4M.fd
|
OVMF_VARS_RO := /usr/share/OVMF/OVMF_VARS_4M.fd
|
||||||
|
|
||||||
# QEMU_DISPLAY — window backend for the framebuffer console (gtk/sdl/none/...).
|
# QEMU_DISPLAY — window backend for the framebuffer console (gtk/sdl/none/...).
|
||||||
# Usage: make -f Makefile.starkernel qemu QEMU_DISPLAY=none # headless/CI
|
# Usage: make -f kernel/Makefile qemu QEMU_DISPLAY=none # headless/CI
|
||||||
QEMU_DISPLAY ?= gtk
|
QEMU_DISPLAY ?= gtk
|
||||||
|
|
||||||
# QEMU_EXTRA — extra raw qemu-system-* args appended verbatim to every arch's
|
# QEMU_EXTRA — extra raw qemu-system-* args appended verbatim to every arch's
|
||||||
@@ -1072,38 +1096,98 @@ else ifeq ($(ARCH),riscv64)
|
|||||||
# fallback boot path EFI/BOOT/BOOT<ARCH>.EFI. Storage on real hardware is the
|
# fallback boot path EFI/BOOT/BOOT<ARCH>.EFI. Storage on real hardware is the
|
||||||
# USB BOT/xHCI path (already live), not virtio.
|
# USB BOT/xHCI path (already live), not virtio.
|
||||||
thumbdrive: all
|
thumbdrive: all
|
||||||
@mkdir -p $(BUILD_DIR)
|
@printf 'FS0:\\EFI\\BOOT\\%s\r\n' "$(EFI_BOOTNAME)" > $(BUILD_DIR)/thumbdrive-startup.nsh
|
||||||
@if ! which sgdisk >/dev/null 2>&1; then echo "Error: sgdisk not found. Install gdisk (apt-get install gdisk)."; exit 1; fi
|
@bash scripts/mkdiskimage.sh --out $(BUILD_DIR)/starkernel-thumbdrive.img --table gpt \
|
||||||
@if ! which mkfs.fat >/dev/null 2>&1; then echo "Error: mkfs.fat not found. Install dosfstools (apt-get install dosfstools)."; exit 1; fi
|
--size-mib 128 --label STARKERNEL \
|
||||||
@case "$(ARCH)" in \
|
$(LOADER_EFI):/EFI/BOOT/$(EFI_BOOTNAME) \
|
||||||
amd64) BOOTNAME="BOOTX64.EFI" ;; \
|
$(BUILD_DIR)/thumbdrive-startup.nsh:/startup.nsh
|
||||||
aarch64) BOOTNAME="BOOTAA64.EFI" ;; \
|
@rm -f $(BUILD_DIR)/thumbdrive-startup.nsh
|
||||||
riscv64) BOOTNAME="BOOTRISCV64.EFI" ;; \
|
@echo "Write to a USB stick with: dd if=$(BUILD_DIR)/starkernel-thumbdrive.img of=/dev/sdX bs=4M status=progress"
|
||||||
*) echo "Error: no thumbdrive EFI boot name for ARCH=$(ARCH)"; exit 1 ;; \
|
|
||||||
esac; \
|
|
||||||
# Geometry: 128 MiB disk (262144 sectors), GPT partition 1 spans sectors \
|
|
||||||
# 2048..262110 (the GPT last-usable sector for this disk size), i.e. \
|
|
||||||
# 260063 sectors. The FAT32 ESP image MUST match the partition size \
|
|
||||||
# exactly; a larger ESP overruns the disk (GPT grows corrupt) or \
|
|
||||||
# spills past the partition end, both of which made earlier builds \
|
|
||||||
# unbootable on hardware. \
|
|
||||||
DISK=$(BUILD_DIR)/starkernel-thumbdrive.img; \
|
|
||||||
ESP_SECTORS=260063; \
|
|
||||||
rm -f $$DISK $(BUILD_DIR)/thumbdrive-esp.img; \
|
|
||||||
dd if=/dev/zero of=$(BUILD_DIR)/thumbdrive-esp.img bs=512 count=$$ESP_SECTORS 2>/dev/null; \
|
|
||||||
mkfs.fat -F 32 -n "STARKERNEL" $(BUILD_DIR)/thumbdrive-esp.img >/dev/null 2>&1; \
|
|
||||||
mmd -i $(BUILD_DIR)/thumbdrive-esp.img ::/EFI; \
|
|
||||||
mmd -i $(BUILD_DIR)/thumbdrive-esp.img ::/EFI/BOOT; \
|
|
||||||
mcopy -i $(BUILD_DIR)/thumbdrive-esp.img $(LOADER_EFI) ::/EFI/BOOT/$$BOOTNAME; \
|
|
||||||
printf 'FS0:\\EFI\\BOOT\\%s\r\n' "$$BOOTNAME" > $(BUILD_DIR)/thumbdrive-startup.nsh; \
|
|
||||||
mcopy -i $(BUILD_DIR)/thumbdrive-esp.img $(BUILD_DIR)/thumbdrive-startup.nsh ::/startup.nsh; \
|
|
||||||
dd if=/dev/zero of=$$DISK bs=1M count=128 2>/dev/null; \
|
|
||||||
sgdisk -n 1:2048:0 -t 1:ef00 -c 1:"EFI System" $$DISK >/dev/null 2>&1; \
|
|
||||||
dd if=$(BUILD_DIR)/thumbdrive-esp.img of=$$DISK bs=512 seek=2048 conv=notrunc 2>/dev/null; \
|
|
||||||
rm -f $(BUILD_DIR)/thumbdrive-esp.img $(BUILD_DIR)/thumbdrive-startup.nsh; \
|
|
||||||
echo "Thumbdrive image: $$DISK"; \
|
|
||||||
echo "Write to a USB stick with: dd if=$$DISK of=/dev/sdX bs=4M status=progress"
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# BOOT IMAGES (one per board)
|
||||||
|
# ==============================================================================
|
||||||
|
# boot_image -- the single file written to a board's boot medium. Driven by
|
||||||
|
# BOARD (boards/<board>/board.mk); normally invoked from the repo root as
|
||||||
|
# make boot_image TARGET=SER5|RASPI|MILKV|ZYNQ7020
|
||||||
|
# Output: build/boards/<board>/$(BOARD_IMAGE).
|
||||||
|
#
|
||||||
|
# BOARD_BOOT selects the recipe:
|
||||||
|
# uefi-esp -- GPT disk, one FAT32 EFI System Partition holding the
|
||||||
|
# monolithic loader as EFI/BOOT/BOOT<ARCH>.EFI (+ startup.nsh
|
||||||
|
# for the UEFI shell, + starforth.cfg when KERNEL_ARGS is set).
|
||||||
|
# Real UEFI firmware (SER5) and U-Boot's bootefi (Milk-V) both
|
||||||
|
# boot it through the removable-media fallback path.
|
||||||
|
# rpi-native -- MBR disk, one FAT32 (LBA) partition holding config.txt,
|
||||||
|
# kernel_2712.img (kernel relinked at 0x80000 and flattened)
|
||||||
|
# and the stock BCM2712 DTB (+ cmdline.txt, which firmware
|
||||||
|
# copies into /chosen/bootargs, when KERNEL_ARGS is set).
|
||||||
|
ifeq ($(ARCH),amd64)
|
||||||
|
EFI_BOOTNAME := BOOTX64.EFI
|
||||||
|
else ifeq ($(ARCH),aarch64)
|
||||||
|
EFI_BOOTNAME := BOOTAA64.EFI
|
||||||
|
else ifeq ($(ARCH),riscv64)
|
||||||
|
EFI_BOOTNAME := BOOTRISCV64.EFI
|
||||||
|
endif
|
||||||
|
|
||||||
|
RPI5_NATIVE_LINKER_SCRIPT := kernel/linker/starkernel-native-rpi5.ld
|
||||||
|
RPI5_NATIVE_ELF := $(BUILD_DIR)/starkernel_rpi5.elf
|
||||||
|
RPI5_NATIVE_IMG := $(BUILD_DIR)/kernel_2712.img
|
||||||
|
|
||||||
|
$(RPI5_NATIVE_ELF): $(KERNEL_OBJS) $(RPI5_NATIVE_LINKER_SCRIPT) | $(BUILD_DIR)
|
||||||
|
@echo "LD (rpi5 native) $@"
|
||||||
|
@$(LD) -T $(RPI5_NATIVE_LINKER_SCRIPT) -nostdlib --build-id=none \
|
||||||
|
-e rpi5_native_start $(KERNEL_OBJS) -o $@
|
||||||
|
|
||||||
|
$(RPI5_NATIVE_IMG): $(RPI5_NATIVE_ELF)
|
||||||
|
@echo "OBJCOPY (raw) $@"
|
||||||
|
@$(OBJCOPY) -O binary $< $@
|
||||||
|
|
||||||
|
.PHONY: boot_image
|
||||||
|
ifeq ($(strip $(BOARD)),)
|
||||||
|
boot_image:
|
||||||
|
@echo "boot_image needs a board. From the repo root:"
|
||||||
|
@echo " make boot_image TARGET=SER5|RASPI|MILKV|ZYNQ7020"
|
||||||
|
@exit 1
|
||||||
|
else ifeq ($(BOARD_BOOT),uefi-esp)
|
||||||
|
boot_image: all
|
||||||
|
@mkdir -p $(BOARD_OUT)
|
||||||
|
@printf 'FS0:\\EFI\\BOOT\\%s\r\n' "$(EFI_BOOTNAME)" > $(BOARD_OUT)/startup.nsh
|
||||||
|
@rm -f $(BOARD_OUT)/starforth.cfg
|
||||||
|
$(if $(KERNEL_ARGS),@printf '%s\n' '$(KERNEL_ARGS)' > $(BOARD_OUT)/starforth.cfg)
|
||||||
|
@bash scripts/mkdiskimage.sh --out $(BOARD_OUT)/$(BOARD_IMAGE) --table gpt \
|
||||||
|
--size-mib $(BOOT_IMAGE_SIZE_MIB) --label LITHOS \
|
||||||
|
$(LOADER_EFI):/EFI/BOOT/$(EFI_BOOTNAME) \
|
||||||
|
$(BOARD_OUT)/startup.nsh:/startup.nsh \
|
||||||
|
$(if $(KERNEL_ARGS),$(BOARD_OUT)/starforth.cfg:/starforth.cfg)
|
||||||
|
@echo "$(BOARD_DESC)"
|
||||||
|
@echo "Write with: dd if=$(BOARD_OUT)/$(BOARD_IMAGE) of=/dev/sdX bs=4M conv=fsync status=progress"
|
||||||
|
else ifeq ($(BOARD_BOOT),rpi-native)
|
||||||
|
$(RPI5_DTB):
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
@echo " FETCH $(RPI5_DTB_URL)"
|
||||||
|
@curl -fsSL -o $@.tmp $(RPI5_DTB_URL) || { rm -f $@.tmp; \
|
||||||
|
echo "Error: could not fetch the Pi 5 DTB. Copy bcm2712-rpi-5-b.dtb from a Raspberry Pi"; \
|
||||||
|
echo "firmware release and pass RPI5_DTB=/path/to/bcm2712-rpi-5-b.dtb"; exit 1; }
|
||||||
|
@mv $@.tmp $@
|
||||||
|
|
||||||
|
boot_image: v3/include/version.h $(RPI5_NATIVE_IMG) $(RPI5_DTB)
|
||||||
|
@mkdir -p $(BOARD_OUT)
|
||||||
|
@rm -f $(BOARD_OUT)/cmdline.txt
|
||||||
|
$(if $(KERNEL_ARGS),@printf '%s\n' '$(KERNEL_ARGS)' > $(BOARD_OUT)/cmdline.txt)
|
||||||
|
@bash scripts/mkdiskimage.sh --out $(BOARD_OUT)/$(BOARD_IMAGE) --table mbr \
|
||||||
|
--size-mib $(BOOT_IMAGE_SIZE_MIB) --label LITHOS \
|
||||||
|
boards/$(BOARD)/config.txt:/config.txt \
|
||||||
|
$(RPI5_NATIVE_IMG):/kernel_2712.img \
|
||||||
|
$(RPI5_DTB):/bcm2712-rpi-5-b.dtb \
|
||||||
|
$(if $(KERNEL_ARGS),$(BOARD_OUT)/cmdline.txt:/cmdline.txt)
|
||||||
|
@echo "$(BOARD_DESC)"
|
||||||
|
@echo "Write with: dd if=$(BOARD_OUT)/$(BOARD_IMAGE) of=/dev/mmcblkX bs=4M conv=fsync status=progress"
|
||||||
|
else
|
||||||
|
boot_image:
|
||||||
|
@echo "Error: BOARD=$(BOARD) has BOARD_BOOT='$(BOARD_BOOT)', which has no boot_image recipe"
|
||||||
|
@exit 1
|
||||||
|
endif
|
||||||
|
|
||||||
# iso-usb — build a PURE UEFI isohybrid ISO for the Iso Image Writer workflow
|
# iso-usb — build a PURE UEFI isohybrid ISO for the Iso Image Writer workflow
|
||||||
# (GNOME Disks "Restore Disk Image...", or dd). Writes a single .iso to a USB
|
# (GNOME Disks "Restore Disk Image...", or dd). Writes a single .iso to a USB
|
||||||
@@ -1150,7 +1234,7 @@ iso-usb: all
|
|||||||
# yourself with Ctrl-A X or by closing the window).
|
# yourself with Ctrl-A X or by closing the window).
|
||||||
# amd64/aarch64: serial → stdio (bidirectional, you can type into the REPL);
|
# amd64/aarch64: serial → stdio (bidirectional, you can type into the REPL);
|
||||||
# riscv64: uses proper GPT (esp dir not supported)
|
# riscv64: uses proper GPT (esp dir not supported)
|
||||||
qemu-esp: include/version.h $(LOADER_EFI) $(KERNEL_ELF)
|
qemu-esp: v3/include/version.h $(LOADER_EFI) $(KERNEL_ELF)
|
||||||
ifeq ($(ARCH),amd64)
|
ifeq ($(ARCH),amd64)
|
||||||
@rm -rf $(BUILD_DIR)/esp
|
@rm -rf $(BUILD_DIR)/esp
|
||||||
@mkdir -p $(BUILD_DIR)/esp/EFI/BOOT
|
@mkdir -p $(BUILD_DIR)/esp/EFI/BOOT
|
||||||
@@ -1206,7 +1290,7 @@ else ifeq ($(ARCH),riscv64)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
# qemu-gdb — launch QEMU with GDB stub on port 1234
|
# qemu-gdb — launch QEMU with GDB stub on port 1234
|
||||||
qemu-gdb: include/version.h $(LOADER_EFI) $(KERNEL_ELF)
|
qemu-gdb: v3/include/version.h $(LOADER_EFI) $(KERNEL_ELF)
|
||||||
ifeq ($(ARCH),amd64)
|
ifeq ($(ARCH),amd64)
|
||||||
@mkdir -p $(BUILD_DIR)/esp/EFI/BOOT
|
@mkdir -p $(BUILD_DIR)/esp/EFI/BOOT
|
||||||
@cp $(LOADER_EFI) $(BUILD_DIR)/esp/EFI/BOOT/BOOTX64.EFI
|
@cp $(LOADER_EFI) $(BUILD_DIR)/esp/EFI/BOOT/BOOTX64.EFI
|
||||||
@@ -1259,7 +1343,7 @@ help:
|
|||||||
@echo "StarKernel Build System"
|
@echo "StarKernel Build System"
|
||||||
@echo "======================="
|
@echo "======================="
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Usage: make -f Makefile.starkernel [ARCH=<arch>] [TARGET=<target>] <goal>"
|
@echo "Usage: make -f kernel/Makefile [ARCH=<arch>] [TARGET=<target>] <goal>"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Architectures (aliases accepted):"
|
@echo "Architectures (aliases accepted):"
|
||||||
@echo " ARCH=amd64 / x86_64 — x86-64 (default on x86 host)"
|
@echo " ARCH=amd64 / x86_64 — x86-64 (default on x86 host)"
|
||||||
@@ -1289,8 +1373,8 @@ help:
|
|||||||
@echo " help — show this message"
|
@echo " help — show this message"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Examples:"
|
@echo "Examples:"
|
||||||
@echo " make -f Makefile.starkernel ARCH=amd64 qemu"
|
@echo " make -f kernel/Makefile ARCH=amd64 qemu"
|
||||||
@echo " make -f Makefile.starkernel ARCH=aarch64 clean qemu"
|
@echo " make -f kernel/Makefile ARCH=aarch64 clean qemu"
|
||||||
@echo " make -f Makefile.starkernel ARCH=riscv64 clean qemu"
|
@echo " make -f kernel/Makefile ARCH=riscv64 clean qemu"
|
||||||
@echo " make -f Makefile.starkernel ARCH=x86_64 qemu # alias works"
|
@echo " make -f kernel/Makefile ARCH=x86_64 qemu # alias works"
|
||||||
@echo " make -f Makefile.starkernel kernel-all"
|
@echo " make -f kernel/Makefile kernel-all"
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user