Start CosmOS with a script, and let a script hold its tongue

Three things scripts wanted, and they are one thing: a machine that can
have a face.

/System/Boot/startup.sh runs before anybody can type. Every way of reaching
the prompt for the first time goes through it, including the one where
there is no disk - in which case there is nothing to find and nothing is
said. A MISSING one is ordinary and silent, because a clean install has
none and a machine that complained every boot about a file nobody wrote
would be teaching its owner to ignore it. One that is THERE and does not
begin with #! is the other case entirely: somebody meant that to run.

#quiet stops each line being echoed, #loud puts it back. The prompt and the
echo go together, because together they are what makes a script look like
typing, so a quiet script gets neither and what it prints is all that
appears. A nested script inherits quiet - a build that asked for it meant
its helpers too - and gets its own setting back when the helper returns.
Anything else beginning with # is handed to the shell, which does not know
it and stops the script, because a script that asked for something this
shell cannot do should not carry on as though it had been given it.

clear empties the screen, which the console has been able to do since
before there was a screen to do it on.

THE PROMPT IS NOW SAID BY WHOEVER SUPPLIES THE LINE. It used to be said at
the top of the loop, which is a decision made before the line is read and
an answer not known until after - and it was wrong at both ends. #quiet is
itself a line, so its prompt went out before anything knew to stay silent;
and the line after a quiet script's last one comes from the console, having
already been denied one. Off by exactly one line in opposite directions. A
first attempt at this remembered whether the prompt had been skipped, which
worked and was a flag standing in for a structure. The monitor's assembler
prints a prompt of its own, so it reads through shellReadRaw, which is the
same source without one.

One admission. Handing the console its prompt back when a quiet script
ended was a real fix when I wrote it and stopped being one an hour later,
because the restructure above means the console's own path prompts whatever
the flag holds. The comment claimed it fixed something. Breaking it on
purpose changed nothing, which is how that was found, and it is now a
comment saying so instead of a line pretending to work.

The startup fixture ends QUIET on purpose: nothing puts the flag back when
the outermost script finishes, so a script ending #loud would have tested
the easy half.

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-30 16:59:23 -04:00
co-authored by Claude Opus 5
parent c28826df77
commit 553882d28d
13 changed files with 297 additions and 40 deletions
+1
View File
@@ -8,6 +8,7 @@ mkdir <path> make a directory
rmdir <path> remove an empty one
do <file> run the lines in a file, which must start with #!
echo [words] say them
clear empty the screen
delete <file> take it off the disk
rename <file> <to> call it something else
monitor look at memory, change it, and jump into it
+1
View File
@@ -8,6 +8,7 @@ mkdir <path> make a directory
rmdir <path> remove an empty one
do <file> run the lines in a file, which must start with #!
echo [words] say them
clear empty the screen
delete <file> take it off the disk
rename <file> <to> call it something else
monitor look at memory, change it, and jump into it
+1
View File
@@ -25,6 +25,7 @@ mkdir <path> make a directory
rmdir <path> remove an empty one
do <file> run the lines in a file, which must start with #!
echo [words] say them
clear empty the screen
delete <file> take it off the disk
rename <file> <to> call it something else
monitor look at memory, change it, and jump into it
+10
View File
@@ -0,0 +1,10 @@
CosmOS
Segan Voyager
CosmOS ready.
> echo and loud again
and loud again
and quiet again at the end
> > typed by hand
> halted
Execution halted.
[exit 0]
+6
View File
@@ -0,0 +1,6 @@
CosmOS
startup.sh is there but does not begin with #!, so it was not run
> > typed by hand
> halted
Execution halted.
[exit 0]
+1
View File
@@ -8,6 +8,7 @@ mkdir <path> make a directory
rmdir <path> remove an empty one
do <file> run the lines in a file, which must start with #!
echo [words] say them
clear empty the screen
delete <file> take it off the disk
rename <file> <to> call it something else
monitor look at memory, change it, and jump into it
+3
View File
@@ -0,0 +1,3 @@
clear
echo typed by hand
exit
+22
View File
@@ -156,6 +156,28 @@ printf '#! script\necho inner one\necho inner two\n' > inner.script
printf '#! script\necho self\ndo loop.script\n' > loop.script
"$TOOL" put "$DISKS/cosmos.img" loop.script >/dev/null
# ---- A disk that starts itself ----
#
# Its own image, and that is the point rather than an inconvenience: a startup script runs on
# every boot, so putting one on the disk the other twenty tests use would prepend it to all of
# them. This is that disk with /System/Boot/startup.sh added.
cp "$DISKS/cosmos.img" "$DISKS/startup.img"
"$TOOL" mkdir "$DISKS/startup.img" /System >/dev/null
"$TOOL" mkdir "$DISKS/startup.img" /System/Boot >/dev/null
# It ENDS QUIET on purpose. Nothing puts the flag back when the outermost script finishes -
# there is no caller to restore it from - so a script ending quiet is the case that catches a
# shell left with no prompt for whoever types next. Ending #loud tests nothing.
printf '#! script\n#quiet\necho Segan Voyager\necho CosmOS ready.\n#loud\necho and loud again\n#quiet\necho and quiet again at the end\n' > startup.sh
"$TOOL" put "$DISKS/startup.img" startup.sh /System/Boot/startup.sh >/dev/null
# The same disk with something at that name that is not a script, which must say so rather
# than run it or ignore it.
cp "$DISKS/cosmos.img" "$DISKS/startupbad.img"
"$TOOL" mkdir "$DISKS/startupbad.img" /System >/dev/null
"$TOOL" mkdir "$DISKS/startupbad.img" /System/Boot >/dev/null
printf 'echo I have no shebang\n' > startupbad.sh
"$TOOL" put "$DISKS/startupbad.img" startupbad.sh /System/Boot/startup.sh >/dev/null
# A disk of its own for the writing test, with one file already on it so that what it
# writes has to be placed somewhere that does not tread on what is there.
"$TOOL" format "$DISKS/write.img" 32 1 >/dev/null
+10
View File
@@ -781,6 +781,16 @@ cosmosScriptEdges | CosmOS/Source/cosmos.asm | run | cosmosScr
# the byte it left. Then a script that runs itself, which stops at four deep and takes every
# level with it, because a build whose helper failed should not carry on in its caller.
cosmosScriptNest | CosmOS/Source/cosmos.asm | run | cosmosScriptNest.in | 200000000 | disks/cosmos.img
# ---- Starting itself ----
#
# /System/Boot/startup.sh runs before anybody can type. This one is also the check on #quiet
# and #loud: the welcome has no prompts interleaved with it, the line after #loud does, and
# the console gets its prompt back when the script ends. And on clear, whose mark on a
# terminal is the escape the console has always sent.
cosmosStartup | CosmOS/Source/cosmos.asm | run | cosmosStartup.in | 200000000 | disks/startup.img
# Present, and not a script. A missing one is ordinary and says nothing; one somebody meant
# to run and got wrong is worth a word.
cosmosStartupBad | CosmOS/Source/cosmos.asm | run | cosmosStartup.in | 200000000 | disks/startupbad.img
printDecimalTest | testPrograms/printDecimalTest.asm | xfail | - | -
printDigitTest | testPrograms/printDigitTest.asm | xfail | - | -
printHexTest | testPrograms/printHexTest.asm | xfail | - | -