Mirror the source tree onto the system disk

A list of files in a makefile goes stale the moment somebody adds a program and forgets to
name it, and what they forgot is invisible until they go looking for it on the machine. So
SplitDisk gained a mirror command and the disk rule is one line: putting a file where the
others live is now the whole of putting it on the disk.

EVERY FILE GOES THROUGH put AND EVERY DIRECTORY THROUGH mkdir. That is the point of it -
mirror adds a walk and no filesystem code at all, so anything the format refuses here it
refuses everywhere, in the same words. What is new is the walk, and the walk is what the
six checks in Tests/disk.sh are about: that it goes all the way down, that it leaves dotfiles
and named directories behind, and that a name too long stops it.

REFUSED RATHER THAN SKIPPED, because a disk quietly missing a file is the exact failure a
mirror exists to prevent. Which meant four sources had to be renamed - a directory entry
holds 22 characters and they were 23, 23, 24 and 29:

  16bitSegmentedSieve.asm        -> 16bitSieve.asm
  16bitSegmentedSieveModern.asm  -> 16bitSieveModern.asm
  consoleInterruptTest.asm       -> consoleInterrupt.asm
  controllerWriteTest.asm        -> controllerWrite.asm

The test names in the manifest are unchanged, since those are identifiers and every recorded
result is filed under them. Only where the source lives has moved.

The entries are sorted before anything is written. readdir hands them back in whatever order
the host filesystem feels like, and a disk image that comes out different from one run to the
next is an image no test could compare against another.

The disk grew from one megabyte to four and from 192 directory entries to 1,024. The sources
are 2,850 blocks and the mirror filled the old directory on its first run, which is a thing
that should not need thinking about again.

The Tests fixture disk is deliberately NOT mirrored. It is a controlled fixture with known
contents, and the shipped disk is the one meant to be useful; they want different things.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-08-29 08:45:46 -04:00
co-authored by Claude Opus 5
parent ff4b025058
commit 0852666e73
13 changed files with 247 additions and 37 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
; The 16-bit segmented sieve rewritten for SplitBit's four-Data-Pointer ISA.
;
; This deliberately implements the same algorithm and emits the same text as
; 16bitSegmentedSieve.asm, making the two versions useful as a direct comparison.
; 16bitSieve.asm, making the two versions useful as a direct comparison.
; DP0 walks PrimeStates, DP1 holds Page, DP2 walks Segment, and volatile DP3
; marks multiples. CALL preserves the first three pointers automatically.
+25
View File
@@ -93,6 +93,31 @@ The generated files are kept under `Programs/build/`:
- `CosmOS/Apps/*.sbx` are loadable application images.
- `cosmos.img` is the SBFS disk containing those applications.
**The disk carries every source in `Programs/`, mirrored.** Not a list kept in the makefile -
a list goes stale the moment somebody adds a program and forgets to name it, and what they
forgot is invisible until they go looking for it on the machine. Putting a file where the
others live is the whole of putting it on the disk.
That matters most for the things nobody thought worth shipping a binary of. A demo that is
not interesting enough to build by default is still worth having the source of, because the
machine can build it:
```
> cd /Source/Examples
/Source/Examples> Asm colours.asm
wrote colours.bin: program 114, data 86, labels 8
```
Two things are left behind. `build`, because what a project builds is not what it wrote. And
anything whose name is longer than a directory entry holds, which is **refused rather than
skipped**: a disk quietly missing a file is exactly the failure a mirror exists to prevent,
so the build stops and says which name to shorten.
`/Lib` still holds the library sources separately, because that is where an `#Include` looks
after looking beside the file that asked. The same files therefore appear twice - once as
what a program includes, once as part of the source tree - and that is the difference between
an installed library and a copy of the source.
The disk is rebuilt from scratch when its applications change, so its contents describe
the current source tree rather than accumulating files left by older builds.
@@ -1,7 +1,7 @@
; The 16-bit segmented sieve rewritten for SplitBit's four-Data-Pointer ISA.
;
; This deliberately implements the same algorithm and emits the same text as
; 16bitSegmentedSieve.asm, making the two versions useful as a direct comparison.
; 16bitSieve.asm, making the two versions useful as a direct comparison.
; DP0 walks PrimeStates, DP1 holds Page, DP2 walks Segment, and volatile DP3
; marks multiples. CALL preserves the first three pointers automatically.
+23 -28
View File
@@ -138,7 +138,15 @@ $(COSMOS_DISK): $(APPS) $(NATIVE_ASM) $(COSMOS) $(STAGE2) testPrograms/stringKey
@# one it has to be handed. Forty blocks a slot and two slots: stage two is about
@# eight thousand bytes, and the second slot is what makes replacing it survivable,
@# since raw blocks have no name and so nothing to rename.
$(DISKTOOL) format $@ 4096 24 40
@# ---- Room for the whole source tree ----
@#
@# Sixteen thousand blocks is four megabytes, which is absurd for a machine with 128K
@# of memory and exactly right for a disk: the sources alone are 2,850 blocks and the
@# point of mirroring them is that nobody has to think about it again when they add
@# one. A hundred and twenty-eight directory blocks is 1,024 entries against the 149
@# the tree has now, for the same reason - it was 24, which is 192, and the mirror
@# filled it on its first run.
$(DISKTOOL) format $@ 16384 128 40
$(DISKTOOL) boot $@ $(STAGE2) 0
@# THREE DIRECTORIES, WHICH IS WHAT A CLEAN INSTALL LOOKS LIKE: what you run, what you
@# assemble, and what those include. It was thirty nine files in one list with
@@ -164,37 +172,24 @@ $(COSMOS_DISK): $(APPS) $(NATIVE_ASM) $(COSMOS) $(STAGE2) testPrograms/stringKey
@for app in $(APPS); do \
$(DISKTOOL) put $@ $$app /Apps/`basename $$app` >/dev/null || exit 1; done
$(DISKTOOL) put $@ $(NATIVE_ASM) /Apps/Asm.sbx
@# What you assemble. All of it, because an assembler with nothing to assemble is a
@# demonstration of nothing:
@# ---- What you assemble: all of it ----
@#
@# > cd /Source
@# /Source> Asm cosmos.asm the system it is running on
@# /Source> Asm Asm.asm and the thing that built it
@# MIRRORED RATHER THAN LISTED. A list in a makefile goes stale the moment somebody
@# adds a program and forgets to name it here, and what they forgot is invisible until
@# they go looking for it on the machine. Now putting a file where the others live is
@# the whole of putting it on the disk.
@#
@# Both come out byte for byte what the host tool makes from the same source.
@# That matters most for the things nobody thought worth building an .sbx of. A demo
@# that is not interesting enough to ship as a binary is still worth having the source
@# of, because somebody curious can assemble it on the machine itself:
@#
@# Keys.asm brings a vector of its own, so assembling it exercises the version two
@# header and the Vector Segment: the loader installs its handler, the console
@# interrupts into it, and the shell takes the vector back at exit.
@# > cd /Source/Examples
@# /Source/Examples> Asm colours.asm
@#
@# strings.asm is the odd one out on purpose. It has no #Include and no #Base, so it
@# comes out as a boot image rather than a loadable program, and the difference between
@# the two is visible on one disk.
$(DISKTOOL) put $@ CosmOS/Source/cosmos.asm /Source/cosmos.asm
$(DISKTOOL) put $@ CosmOS/Assembler/Asm.asm /Source/Asm.asm
$(DISKTOOL) put $@ CosmOS/Assembler/readTest.asm /Source/readTest.asm
$(DISKTOOL) put $@ CosmOS/Assembler/tokenTest.asm /Source/tokenTest.asm
$(DISKTOOL) put $@ CosmOS/Apps/hello.asm /Source/hello.asm
$(DISKTOOL) put $@ CosmOS/Apps/Say.asm /Source/Say.asm
$(DISKTOOL) put $@ CosmOS/Apps/Keys.asm /Source/Keys.asm
$(DISKTOOL) put $@ testPrograms/stringKeyword.asm /Source/strings.asm
@# And the loader, so the machine can rebuild what starts it. Assembling stage2.asm on
@# the machine and writing the result into the other boot slot is the whole of a
@# self-hosted boot chain, and everything it includes is already in /Lib.
$(DISKTOOL) put $@ Boot/stage1.asm /Source/stage1.asm
$(DISKTOOL) put $@ Boot/stage2.asm /Source/stage2.asm
@# And what those include. Everything here is named by an #Include somewhere and by
@# nothing else, which is exactly what makes it a library rather than a source.
@# build is left behind, because what a project builds is not what it wrote. Anything
@# with a name longer than a directory entry holds is refused rather than skipped: a
@# disk quietly missing a file is the failure a mirror exists to prevent.
$(DISKTOOL) mirror $@ . /Source build
@for f in CosmOS/Source/console.asm CosmOS/Source/fileStream.asm \
CosmOS/Source/sbfs.asm CosmOS/Source/services.asm CosmOS/Source/text.asm \
CosmOS/Source/config.asm \
+1 -1
View File
@@ -12,7 +12,7 @@
;
; There is a fourth bit, for whether the console interrupts on input, and nothing here
; sets it. Polling and interrupting are the two ways to get a byte and this is the one
; about polling; consoleInterruptTest.asm is the other.
; about polling; consoleInterrupt.asm is the other.
;
; This runs with input from a file rather than a terminal, so key mode has no terminal to
; put into another state and the mode bit is the only thing that changes. That is on