boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs

commit 49dcd6e7b024c3b4921442ba79db41bb510e5cc3
parent 8bee01c9d52db4681e63f4f36b3b5ff8517cf023
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 21 Apr 2026 07:55:31 -0700

reorg: docs/ arch/ src/ tests/

Root was flat with ~20 files. Group by role:
  docs/  — PLAN, SEED, P1, C1, LISP
  arch/  — ELF-*.hex2, p1_gen.py, generated p1_<arch>.M1
  src/   — lisp.M1, kaem-minimal.M1 (real programs)
  tests/ — hello.M1, demo.M1, kaem.run (smoke; lisp/ fixtures unchanged)

Makefile resolves PROG.M1 against src/ then tests/; hard-errors if
neither has it. RUN_ARGS_lisp now points at tests/lisp/00-identity.scm
(hello.scm was a byte-identical duplicate, removed). Added
RUN_ARGS_kaem-minimal := tests/kaem.run.

p1_gen.py already writes relative to its own dir, so moving it into
arch/ needed no code change.

Diffstat:
M.gitignore | 6+++---
MMakefile | 45++++++++++++++++++++++++++++-----------------
MREADME.md | 26++++++++++++++++----------
RELF-aarch64.hex2 -> arch/ELF-aarch64.hex2 | 0
RELF-amd64.hex2 -> arch/ELF-amd64.hex2 | 0
RELF-riscv64.hex2 -> arch/ELF-riscv64.hex2 | 0
Rp1_gen.py -> arch/p1_gen.py | 0
RC1.md -> docs/C1.md | 0
RLISP.md -> docs/LISP.md | 0
RP1.md -> docs/P1.md | 0
RPLAN.md -> docs/PLAN.md | 0
RSEED.md -> docs/SEED.md | 0
Dhello.scm | 2--
Rkaem-minimal.M1 -> src/kaem-minimal.M1 | 0
Rlisp.M1 -> src/lisp.M1 | 0
Rdemo.M1 -> tests/demo.M1 | 0
Rhello.M1 -> tests/hello.M1 | 0
Rkaem.run -> tests/kaem.run | 0
18 files changed, 47 insertions(+), 32 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,4 +1,4 @@ build/ -p1_aarch64.M1 -p1_amd64.M1 -p1_riscv64.M1 +arch/p1_aarch64.M1 +arch/p1_amd64.M1 +arch/p1_riscv64.M1 diff --git a/Makefile b/Makefile @@ -23,10 +23,20 @@ PROG ?= hello ARCH ?= aarch64 +# Programs live in src/ (real: lisp, kaem-minimal) or tests/ (smoke: +# hello, demo). Resolve PROG.M1 against both so the user doesn't have +# to remember which bucket a program is in. +PROG_SRC := $(firstword $(wildcard src/$(PROG).M1 tests/$(PROG).M1)) +ifeq ($(PROG_SRC),) + $(error PROG '$(PROG)' not found — no src/$(PROG).M1 or tests/$(PROG).M1) +endif + # Per-program runtime arguments. The lisp interpreter reads its script -# from argv[1] (LISP.md step 8); supply hello.scm by default so -# `make run-all` stays a smoke test. -RUN_ARGS_lisp ?= hello.scm +# from argv[1] (docs/LISP.md step 8); kaem-minimal reads its build +# script. Default each to its smoke fixture so `make run-all` stays a +# smoke test. +RUN_ARGS_lisp ?= tests/lisp/00-identity.scm +RUN_ARGS_kaem-minimal ?= tests/kaem.run RUN_ARGS := $(RUN_ARGS_$(PROG)) # Map P1 ARCH -> Linux-platform tag for the container. @@ -95,10 +105,10 @@ $(TOOLS_DIR)/M0 $(TOOLS_DIR)/hex2-0 $(TOOLS_DIR)/catm $(TOOLS_DIR)/hex0 $(TOOLS_ # M0 takes a single positional input (no -f flag), so we catm the two # sources together first. The intermediate .combined.M1 is kept in OUT_DIR # so it gets cleaned along with everything else. -$(OUT_DIR)/$(PROG).hex2: $(PROG).M1 p1_$(ARCH).M1 lint.sh $(TOOLS_DIR)/M0 $(TOOLS_DIR)/catm | $(OUT_DIR) - ./lint.sh p1_$(ARCH).M1 $(PROG).M1 +$(OUT_DIR)/$(PROG).hex2: $(PROG_SRC) arch/p1_$(ARCH).M1 lint.sh $(TOOLS_DIR)/M0 $(TOOLS_DIR)/catm | $(OUT_DIR) + ./lint.sh arch/p1_$(ARCH).M1 $(PROG_SRC) $(PODMAN) sh -ec ' \ - $(TOOLS_DIR)/catm $(OUT_DIR)/$(PROG).combined.M1 p1_$(ARCH).M1 $(PROG).M1 ; \ + $(TOOLS_DIR)/catm $(OUT_DIR)/$(PROG).combined.M1 arch/p1_$(ARCH).M1 $(PROG_SRC) ; \ $(TOOLS_DIR)/M0 $(OUT_DIR)/$(PROG).combined.M1 $(OUT_DIR)/$(PROG).hex2' # Link: prepend the ELF header and feed to hex2-0. @@ -107,9 +117,9 @@ $(OUT_DIR)/$(PROG).hex2: $(PROG).M1 p1_$(ARCH).M1 lint.sh $(TOOLS_DIR)/M0 $(TOOL # base address 0x00600000 (no --base-address flag), which is why the ELF # header references `&ELF_base` symbolically rather than baking in a # concrete VA — the header travels to whatever base the linker chose. -$(OUT_DIR)/$(PROG): $(OUT_DIR)/$(PROG).hex2 ELF-$(ARCH).hex2 $(TOOLS_DIR)/hex2-0 $(TOOLS_DIR)/catm +$(OUT_DIR)/$(PROG): $(OUT_DIR)/$(PROG).hex2 arch/ELF-$(ARCH).hex2 $(TOOLS_DIR)/hex2-0 $(TOOLS_DIR)/catm $(PODMAN) sh -ec ' \ - $(TOOLS_DIR)/catm $(OUT_DIR)/$(PROG).linked.hex2 ELF-$(ARCH).hex2 $(OUT_DIR)/$(PROG).hex2 ; \ + $(TOOLS_DIR)/catm $(OUT_DIR)/$(PROG).linked.hex2 arch/ELF-$(ARCH).hex2 $(OUT_DIR)/$(PROG).hex2 ; \ $(TOOLS_DIR)/hex2-0 $(OUT_DIR)/$(PROG).linked.hex2 $(OUT_DIR)/$(PROG)' run: $(OUT_DIR)/$(PROG) @@ -122,7 +132,7 @@ run-all: -$(MAKE) --no-print-directory PROG=$(PROG) ARCH=amd64 run -$(MAKE) --no-print-directory PROG=$(PROG) ARCH=riscv64 run -# Test harness (LISP.md step 9). Each fixtures pair in tests/lisp/ is a +# Test harness (docs/LISP.md step 9). Each fixtures pair in tests/lisp/ is a # <name>.scm program plus a <name>.expected file holding its exact # stdout. test-lisp runs every fixture on $(ARCH); test-lisp-all runs # the suite on all three arches — cross-arch consistency falls out by @@ -158,11 +168,12 @@ test-lisp-all: $(MAKE) --no-print-directory ARCH=riscv64 test-lisp clean: - rm -rf build/ p1_aarch64.M1 p1_amd64.M1 p1_riscv64.M1 - -# Generate all three per-arch DEFINE tables from p1_gen.py in a single -# shot. Grouped target (&:) because p1_gen.py writes all three files -# unconditionally. These are build artifacts — gitignored; the build -# regenerates them on any p1_gen.py edit so there's no staleness risk. -p1_aarch64.M1 p1_amd64.M1 p1_riscv64.M1 &: p1_gen.py - python3 p1_gen.py + rm -rf build/ arch/p1_aarch64.M1 arch/p1_amd64.M1 arch/p1_riscv64.M1 + +# Generate all three per-arch DEFINE tables from arch/p1_gen.py in a +# single shot. Grouped target (&:) because p1_gen.py writes all three +# files unconditionally (next to itself, i.e. into arch/). These are +# build artifacts — gitignored; the build regenerates them on any +# p1_gen.py edit so there's no staleness risk. +arch/p1_aarch64.M1 arch/p1_amd64.M1 arch/p1_riscv64.M1 &: arch/p1_gen.py + python3 arch/p1_gen.py diff --git a/README.md b/README.md @@ -3,27 +3,33 @@ Experimental alternative bootstrap path: a small Lisp written directly in M1 assembly + a C compiler written in that Lisp, replacing the `M2-Planet → mes → MesCC → nyacc` stack between M1 asm and tcc-boot. -Goal is a 4–6× shrink in auditable LOC. See [PLAN.md](PLAN.md). +Goal is a 4–6× shrink in auditable LOC. See [docs/PLAN.md](docs/PLAN.md). ## Status -Stage 0: hello-world in the P1 portable pseudo-ISA (see [P1.md](P1.md)), +Stage 0: hello-world in the P1 portable pseudo-ISA (see [docs/P1.md](docs/P1.md)), assembled and run inside a pristine alpine container on all three target -arches (aarch64, amd64, riscv64). The same `hello.M1` source assembles -for every arch; only the backing `p1_<arch>.M1` defs file varies. +arches (aarch64, amd64, riscv64). The same `tests/hello.M1` source assembles +for every arch; only the backing `arch/p1_<arch>.M1` defs file varies. Toolchain (M1, hex2) builds statically from the upstream mescc-tools C source. ## Layout ``` -PLAN.md design doc (Lisp-in-M1 → C compiler path) -hello.M1 portable P1 hello, unchanged across arches -p1_<arch>.M1 per-arch P1 defs (aarch64, amd64, riscv64) -ELF-<arch>.hex2 per-arch ELF header template -Containerfile builder image: alpine + gcc + musl-dev +docs/ design docs (PLAN, SEED, P1, C1, LISP) +src/ real programs (lisp.M1, kaem-minimal.M1) +tests/ smoke programs (hello.M1, demo.M1) + fixtures + lisp/ lisp test fixtures (*.scm + *.expected) + kaem.run smoke input for kaem-minimal +arch/ per-arch defs + ELF headers + p1_gen.py generator for p1_<arch>.M1 + p1_<arch>.M1 per-arch P1 defs (gitignored, generated) + ELF-<arch>.hex2 per-arch ELF header template +bootstrap.sh hex0-seed → M0/hex2-0/catm toolchain build +lint.sh M1 undefined-token guard Makefile podman-driven build, ARCH-parameterized -build/<arch>/ per-arch outputs (hello.hex2, hello) +build/<arch>/ per-arch outputs + toolchain ``` ## Build & run diff --git a/ELF-aarch64.hex2 b/arch/ELF-aarch64.hex2 diff --git a/ELF-amd64.hex2 b/arch/ELF-amd64.hex2 diff --git a/ELF-riscv64.hex2 b/arch/ELF-riscv64.hex2 diff --git a/p1_gen.py b/arch/p1_gen.py diff --git a/C1.md b/docs/C1.md diff --git a/LISP.md b/docs/LISP.md diff --git a/P1.md b/docs/P1.md diff --git a/PLAN.md b/docs/PLAN.md diff --git a/SEED.md b/docs/SEED.md diff --git a/hello.scm b/hello.scm @@ -1,2 +0,0 @@ -(define id (lambda (x) x)) -(id 42) diff --git a/kaem-minimal.M1 b/src/kaem-minimal.M1 diff --git a/lisp.M1 b/src/lisp.M1 diff --git a/demo.M1 b/tests/demo.M1 diff --git a/hello.M1 b/tests/hello.M1 diff --git a/kaem.run b/tests/kaem.run