Lunar Porter moves into a directory of its own

The first application with things of its own, and the first customer for
all three rungs before it at once.

  /Apps/Lander                      a launcher
  /Packages/app.Lander/Lander.sbx   the program
  /Packages/app.Lander/splash.tune  its tune

Typing "Lander" starts the launcher, which needs no new rule: the shell
runs what it reads, so a launcher beginning "#!" and Say.sbx beginning
"SBEX" go down the same road and neither the shell nor the person has to
know which kind of thing they started. It passes on what it was told with
$args, and says #quiet, because a launcher is machinery and not
narration. The game then asks where it came from and joins its tune's
name to that, so nothing anywhere names /splash.tune.

Every recorded Lander test still types just "Lander" and knows nothing
about any of this, which is exactly the claim.

---- Why the directory is not in /Apps ----

A launcher and a directory of the same name cannot both be there; SBFS
refuses the second. And /Apps is the directory the shell walks for every
word it does not know and Tab walks for every first word, so doubling
what is in it is a cost on the path that runs most.

The "app." goes in FRONT rather than behind because Tab matches the start
of a name: a prefix is a namespace and a suffix is a collision. Copy.app
beside Copy.sbx makes "Copy" and Tab complete to the shared "Copy." and
hand you a broken word.

The launcher names drive 0, so a game started from a disk of your own is
looked for where the game is rather than where you are - its lines run on
your disk, which is what makes everything else in a script work.

/lander.state stays at the root. A saved position belongs to whoever
saved it and is found where they are standing; a tune belongs to the
program and is found beside it. That pair is the whole distinction.

---- And what it cost ----

Two hundred thousand cycles, about twelve frames, between the machine
starting and the game drawing: a script opened, a deeper path walked. Six
video captures moved out by that much. The drift bar needed something
else - its pad is counted from the MACHINE starting rather than the game,
so the burn is now twelve frames shorter from the game's point of view,
and the recording holds the button longer instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-09-06 15:55:19 -04:00
co-authored by Claude Opus 5
parent 2defbb49e2
commit 94bdf71356
16 changed files with 210 additions and 65 deletions
+47 -3
View File
@@ -186,6 +186,21 @@ PROG_DEPS = $(BINARIES:.bin=.d)
COSMOS = $(PROG_BUILD)/CosmOS/Source/cosmos.bin
APPS = $(patsubst $(PROG_DIR)/CosmOS/Apps/%.asm,$(PROG_BUILD)/CosmOS/Apps/%.sbx,\
$(wildcard $(PROG_DIR)/CosmOS/Apps/*.asm))
# ---- Applications that have things of their own ----
#
# A simple program is one file and lives in /Apps. One with ASSETS gets a directory of its
# own - /Packages/app.<name> - and a launcher in /Apps under its bare name, which is what
# somebody actually types. The launcher is indirection: where the thing lives stops being
# part of its name, so an installer can put it elsewhere and change one line.
#
# Named here rather than found, because being packaged is a decision somebody makes and not
# something a directory listing can say. Both halves of the placing below are driven from
# this one list, so a name added here cannot end up half moved.
PACKAGED = Lander
# And what each of them owns beyond its own program. A tune named here is put in its
# package instead of at the root, where the tunes that belong to nobody in particular go.
LANDER_TUNES = splash
COSMOS_DISK = $(PROG_BUILD)/cosmos.img
# ---- A disk that is yours ----
@@ -585,7 +600,7 @@ $(COSMOS_DISK): makefile
#
# Found rather than wildcarded, because the tree the mirror walks is a tree and $(wildcard)
# does not recurse. build is left out for the reason the mirror leaves it out.
MIRRORED := $(shell find $(PROG_DIR) \( -name '*.asm' -o -name '*.score' \) -not -path '$(PROG_BUILD)/*' 2>/dev/null)
MIRRORED := $(shell find $(PROG_DIR) \( -name '*.asm' -o -name '*.score' -o -path '*/Launchers/*' \) -not -path '$(PROG_BUILD)/*' 2>/dev/null)
$(COSMOS_DISK): $(MIRRORED)
$(COSMOS_DISK): $(APPS) $(NATIVE_ASM) $(COSMOS) $(STAGE2) \
$(PROG_DIR)/testPrograms/stringKeyword.asm \
@@ -624,6 +639,22 @@ $(COSMOS_DISK): $(APPS) $(NATIVE_ASM) $(COSMOS) $(STAGE2) \
$(DISKTOOL) mkdir $@ /Lib
$(DISKTOOL) mkdir $@ /System
$(DISKTOOL) mkdir $@ /System/Boot
@# ---- And what applications own ----
@#
@# One directory per packaged application, so its files can be found together and
@# removed together - which is the question install and remove are both made of. NOT in
@# /Apps, for two reasons: a launcher and a directory of the same name cannot both be
@# there, and /Apps is the directory the shell walks for every word it does not know and
@# Tab walks for every first word, so doubling what is in it is a cost on the path that
@# runs most.
@#
@# The "app." in front rather than behind is what keeps them out of the way. Tab matches
@# the START of a name, so a prefix is a namespace and a suffix is a collision: with
@# Copy.app beside Copy.sbx, typing Copy and pressing Tab completes to the shared "Copy."
@# and hands you a broken word.
$(DISKTOOL) mkdir $@ /Packages
@for app in $(PACKAGED); do \
$(DISKTOOL) mkdir $@ /Packages/app.$$app >/dev/null || exit 1; done
@# The system itself, as a file, which is the whole of what boot.cfg chooses between.
@# No boot.cfg is written: stage two falls back to this name when there is none, and a
@# clean install having nothing to configure is the right default.
@@ -631,8 +662,17 @@ $(COSMOS_DISK): $(APPS) $(NATIVE_ASM) $(COSMOS) $(STAGE2) \
@# What you run. /Apps is the second place the shell looks when a word it does not know
@# turns out to be a program, so anything in here starts by name from anywhere.
@for app in $(APPS); do \
$(DISKTOOL) put $@ $$app /Apps/`basename $$app` >/dev/null || exit 1; done
name=`basename $$app .sbx`; \
case " $(PACKAGED) " in \
*" $$name "*) where=/Packages/app.$$name/$$name.sbx ;; \
*) where=/Apps/$$name.sbx ;; \
esac; \
$(DISKTOOL) put $@ $$app $$where >/dev/null || exit 1; done
$(DISKTOOL) put $@ $(NATIVE_ASM) /Apps/Asm.sbx
@# The launcher goes where the program used to be, under the bare name that is typed.
@for app in $(PACKAGED); do \
$(DISKTOOL) put $@ $(PROG_DIR)/CosmOS/Launchers/$$app /Apps/$$app >/dev/null \
|| exit 1; done
@# ---- And the music, compiled ----
@#
@# A .score is written and a .tune is what the machine reads, the same way a .asm is
@@ -656,7 +696,11 @@ $(COSMOS_DISK): $(APPS) $(NATIVE_ASM) $(COSMOS) $(STAGE2) \
name=`basename $$score .score`; \
$(TUNE_RUN) -I $(PROG_BUILD)/tunes $$score $(PROG_BUILD)/tunes/$$name.tune \
>/dev/null || exit 1; \
$(DISKTOOL) put $@ $(PROG_BUILD)/tunes/$$name.tune $$name.tune >/dev/null \
case " $(LANDER_TUNES) " in \
*" $$name "*) where=/Packages/app.Lander/$$name.tune ;; \
*) where=$$name.tune ;; \
esac; \
$(DISKTOOL) put $@ $(PROG_BUILD)/tunes/$$name.tune $$where >/dev/null \
|| exit 1; done
@# ---- What you assemble: all of it ----
@#