Settle is a program, and a machine with no fallback still starts

The boot state opened a loop that could not be closed from inside: the
machine said "settle it to try again" and gave you no way to do so. Settle
closes it, in 349 bytes.

A PROGRAM RATHER THAN A SHELL WORD. The shell is for the things that cannot
be done without it, and this is not one - it reaches the system through SWI
like anything else, which means it can be replaced, left off a disk, or
called by whatever comes to call programs in turn. That last one is the
point: a shell word is not callable by anything.

Two services for it. osBootState answers in Q, and a machine with no disk
answers settled, because there is nothing there to be unsettled about.
osBootSettle puts it back. SETTLING IS THE ONLY WRITE A PROGRAM GETS -
marking a start as trying or fallen back is the loader's business, and a
service that let a program claim either would let it lie about something
the loader has no way to check.

And a hole the tests walked into, which was mine rather than theirs. With
no fallback configured, a failed start left the machine unable to start at
all: the mark said do not use the system, and there was nothing else to
use. That turns "the last start failed" into "no start is permitted", which
is worse than the problem the mark was added to solve. With nothing to fall
back to it now tries the configuration again and says so - a failure that
was passing recovers, and one that is not leaves the machine exactly where
it would have been without any of this, which is the most that can be
promised when there is only one thing to start.

docs.sh caught both new services having no row in the services table before
anything else did.
This commit is contained in:
Anachronaut
2026-08-27 17:02:59 -04:00
parent dc74149321
commit ce2a2cd7e6
12 changed files with 248 additions and 1 deletions
+19 -1
View File
@@ -154,11 +154,26 @@ stateSettled:
useFallback: useFallback:
SETD.0 HaveFallback SETD.0 HaveFallback
LDA.0 LDA.0
BRA noSystem BRA noFallbackTryAgain
SETD.0 FallbackName SETD.0 FallbackName
RCAL tryImage RCAL tryImage
BRI noSystem BRI noSystem
noFallbackTryAgain:
; NOTHING TO FALL BACK TO, so try what was asked for anyway rather than stopping. With no
; second name the mark is the only thing standing between the machine and its own
; configuration, and refusing on the strength of it would turn "the last start failed"
; into "no start is permitted", which is worse than the problem it was added to solve.
;
; A failure that was passing recovers here. One that is not leaves the machine exactly
; where it would have been without any of this, which is the most that can be promised
; when there is only one thing to start.
SETD.0 NoFallbackText
RCAL say
SETD.0 BootName
RCAL tryImage
BRI noSystem
; ---- Starting one particular image ---- ; ---- Starting one particular image ----
; ;
; DP0 names it. Returns only if it could not be started, having said why; everything that ; DP0 names it. Returns only if it could not be started, having said why; everything that
@@ -549,6 +564,9 @@ DidNotArriveText:
StillBackText: StillBackText:
"still on the fallback: settle it to try again "still on the fallback: settle it to try again
" "
NoFallbackText:
"no fallback, so trying it again
"
NothingText: NothingText:
"nothing to start "nothing to start
" "
+90
View File
@@ -0,0 +1,90 @@
; Settle.asm
; Says how the last start went, and tells the machine to stop falling back.
;
; A program rather than a shell command, because the shell is for the things you cannot do
; without it and this is not one of them. It reaches the system through SWI like anything
; else, which means it can be replaced, left off a disk, or called by whatever comes to
; call programs in sequence - and none of that is true of a word built into the shell.
;
; ---- What settling means ----
;
; The loader marks the disk before handing over and the system clears the mark on reaching
; its prompt, so a mark still set is a start that never arrived. After that the loader uses
; the fallback and KEEPS USING IT, because a system known not to start should not be tried
; every other boot for ever.
;
; Settling is how it is told that has changed. It does not fix anything and it does not
; check anything: it says "the situation is different now, try again". Which is why it is a
; deliberate act by somebody who has just changed something, rather than anything automatic.
;
; Written by Anachronaut
#Include services.asm
#Program
#Base 0x4000
start:
SWI osBootState
MVQA
SETD.0 State
STA.0
BRA alreadySettled
INIB 0d2
XOR
BRQ fellBack
; Trying. Nothing has gone wrong yet - this is what the disk looks like while a start is
; still in progress, which from in here means the system that is running has not reached
; its prompt, which it plainly has. So the mark is stale.
SETD.0 WasTrying
SWI osPrintString
BRI doSettle
fellBack:
SETD.0 WasFallen
SWI osPrintString
doSettle:
SWI osBootSettle
MVQA
BNA settleFailed
SETD.0 Settled
SWI osPrintString
SWI osExit
settleFailed:
SETD.0 NoDisk
SWI osPrintString
SWI osExit
alreadySettled:
SETD.0 Already
SWI osPrintString
SWI osExit
#Data
#Base 0x2000
WasTrying:
"the disk says a start is still in progress
"
WasFallen:
"the disk says the last start did not arrive, so this is the fallback
"
Settled:
"settled: the next start will use the configuration again
"
Already:
"already settled: the next start will use the configuration
"
NoDisk:
"nothing to settle: no disk answered
"
State:
0x00
+3
View File
@@ -361,6 +361,7 @@ from every assembly file in it. Several are old programs written for the bare ma
| Snake | A game. Draws a whole screen with cursor addressing and steers with single keys, asking the console once a frame and never waiting. | | Snake | A game. Draws a whole screen with cursor addressing and steers with single keys, asking the console once a frame and never waiting. |
| Keys | The console interrupting rather than being asked. The only one that brings a vector of its own, which is what the version two format exists for. | | Keys | The console interrupting rather than being asked. The only one that brings a vector of its own, which is what the version two format exists for. |
| Say | Prints whatever it was told, which is the shortest thing that shows osArgument working. | | Say | Prints whatever it was told, which is the shortest thing that shows osArgument working. |
| Settle | Says how the last start went and tells the machine to stop falling back, in 349 bytes. A program rather than a shell word, because the shell is for what cannot be done without it. |
| Files | Writes a file, reads it back, renames it and deletes it, in 665 bytes, including nothing but the service names. It is what says a program does not need a filesystem inside it. | | Files | Writes a file, reads it back, renames it and deletes it, in 665 bytes, including nothing but the service names. It is what says a program does not need a filesystem inside it. |
| Break | Stops itself twice with SWI osBreak, so that the registers can be seen changing between one stop and the next. | | Break | Stops itself twice with SWI osBreak, so that the registers can be seen changing between one stop and the next. |
| Edit | A line editor. | | Edit | A line editor. |
@@ -564,6 +565,8 @@ Those numbers are written down once, in `Programs/CosmOS/Source/services.asm`, w
| osFileFetch | DP1 is where a block should go, A and B together are which block. Reads back a block of the file being written. | | osFileFetch | DP1 is where a block should go, A and B together are which block. Reads back a block of the file being written. |
| osPrintNumber | A and B together are a number. Prints it in decimal, without leading zeroes. | | osPrintNumber | A and B together are a number. Prints it in decimal, without leading zeroes. |
| osBreak | Stops the program, shows every register as it had them, waits for a key, and carries on. | | osBreak | Stops the program, shows every register as it had them, waits for a key, and carries on. |
| osBootState | Q answers how the last start went: 0 settled, 1 trying, 2 fell back. A machine with no disk answers settled, because there is nothing there to be unsettled about. |
| osBootSettle | Puts it back to settled, which is how a machine that fell back is told the situation has changed. Q is zero if the disk took it. **Settling is the only write a program gets** - marking a start as trying or fallen back is the loader's business, and a service that let a program claim either would let it lie about something the loader cannot check. |
``` ```
#Include services.asm #Include services.asm
+51
View File
@@ -2276,6 +2276,55 @@ handlePrintNumber:
; ;
; Which is exactly why this cannot RETI. Its return address is on the Stack it just walked ; Which is exactly why this cannot RETI. Its return address is on the Stack it just walked
; away from, so it branches to the prompt instead. ; away from, so it branches to the prompt instead.
; ---- How the last start went, and settling it ----
;
; The answer goes in Q by writing into this handler's own frame, which is how every service
; here hands anything back: a handler arrives with the caller's registers pushed, not
; cleared, and RETI puts them back - so the way to return a value is to change the copy the
; return is going to restore.
handleBootState:
SETD.2 DiskReady
LDA.2
BRA bootStateNone
CALL sbfsBootState
BNQ bootStateNone
SETD.2 SbfsStateWas
LDA.2
MVSD.2
DPUP.2 0d02
STA.2
RETI
bootStateNone:
; No disk, or one that would not answer. Nothing there to be unsettled about.
MVSD.2
DPUP.2 0d02
RSTA
STA.2
RETI
handleBootSettle:
SETD.2 DiskReady
LDA.2
BRA bootSettleNo
RSTA
CALL sbfsSetBootState
BNQ bootSettleNo
MVSD.2
DPUP.2 0d02
RSTA
STA.2
RETI
bootSettleNo:
MVSD.2
DPUP.2 0d02
INIA 0d1
STA.2
RETI
handleExit: handleExit:
SETD.1 SystemStack SETD.1 SystemStack
LDD.0.1 LDD.0.1
@@ -3897,4 +3946,6 @@ CommandLine:
osFileFetch handleFileFetch osFileFetch handleFileFetch
osPrintNumber handlePrintNumber osPrintNumber handlePrintNumber
osBreak handleBreak osBreak handleBreak
osBootState handleBootState
osBootSettle handleBootSettle
Device 0x20 diskDone Device 0x20 diskDone
+18
View File
@@ -135,3 +135,21 @@
; restore. The price is that it is part of the program: a build with breakpoints in it has ; restore. The price is that it is part of the program: a build with breakpoints in it has
; different addresses from one without. ; different addresses from one without.
osBreak 0d25 osBreak 0d25
; ---- How the last start went ----
;
; The loader marks the disk before it hands over and the system clears the mark on reaching
; its prompt, so a mark still set is a start that never arrived. See the boot state in
; sbfs.h for what the numbers mean.
;
; osBootState answers in Q: 0 settled, 1 trying, 2 fell back. A machine with no disk answers
; settled, because there is nothing there to be unsettled about.
;
; osBootSettle puts it back to settled, which is how a machine that fell back is told the
; situation has changed. Q is zero if the disk took it.
;
; THE ONLY WRITE A PROGRAM GETS IS SETTLING. Marking a start as trying or fallen back is
; the loader's business, and a service that let a program claim either would let it lie
; about something the loader has no way to check.
osBootState 0d33
osBootSettle 0d34
+8
View File
@@ -0,0 +1,8 @@
CosmOS
this is the fallback: what boot.cfg asks for did not start
> the disk says the last start did not arrive, so this is the fallback
settled: the next start will use the configuration again
finished
> halted
Execution halted.
[exit 0]
+6
View File
@@ -0,0 +1,6 @@
CosmOS
> already settled: the next start will use the configuration
finished
> halted
Execution halted.
[exit 0]
+9
View File
@@ -0,0 +1,9 @@
stage two
the last start did not arrive
no fallback, so trying it again
CosmOS
this is the fallback: what boot.cfg asks for did not start
>
halted
Execution halted.
[exit 0]
+2
View File
@@ -1,6 +1,8 @@
stage two stage two
no /System/Boot/cosmos.bin no /System/Boot/cosmos.bin
trying the fallback trying the fallback
no fallback, so trying it again
no /System/Boot/cosmos.bin
nothing to start nothing to start
Execution halted. Execution halted.
[exit 0] [exit 0]
+2
View File
@@ -0,0 +1,2 @@
Settle
exit
+24
View File
@@ -467,3 +467,27 @@ done
# consecutive starts of one machine without any of them depending on the others running. # consecutive starts of one machine without any of them depending on the others running.
"$TOOL" bootstate "$DISKS/wedgesecond.img" 1 >/dev/null "$TOOL" bootstate "$DISKS/wedgesecond.img" 1 >/dev/null
"$TOOL" bootstate "$DISKS/wedgethird.img" 2 >/dev/null "$TOOL" bootstate "$DISKS/wedgethird.img" 2 >/dev/null
# ---- Settling from the machine itself ----
#
# The loop the boot state opens has to be closeable from inside: a machine that says
# "settle it to try again" and gives you no way to do so has told you about a problem it
# will not let you fix. Settle is a PROGRAM rather than a shell word, which is the shape
# this system is growing into - one job each, reached through SWI, replaceable.
"$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" \
"$ROOT/Programs/CosmOS/Apps/Settle.asm" -o "$WORK/Settle.sbx" >/dev/null
cp "$DISKS/wedgethird.img" "$DISKS/settle.img"
"$TOOL" put "$DISKS/settle.img" "$WORK/Settle.sbx" /Apps/Settle.sbx >/dev/null
# And one already settled, so that saying so is checked as well as doing it.
cp "$DISKS/selfboot.img" "$DISKS/settled.img"
"$TOOL" put "$DISKS/settled.img" "$WORK/Settle.sbx" /Apps/Settle.sbx >/dev/null
# A configuration naming something broken with NOTHING to fall back to. The mark would
# otherwise be the only thing between the machine and its own configuration, and refusing
# on the strength of it turns "the last start failed" into "no start is permitted".
cp "$DISKS/selfboot.img" "$DISKS/nofallback.img"
"$TOOL" put "$DISKS/nofallback.img" "$WORK/wedged.bin" /System/Boot/wedged.bin >/dev/null
printf 'system /System/Boot/cosmos.bin\n' > "$WORK/lonely.cfg"
"$TOOL" put "$DISKS/nofallback.img" "$WORK/lonely.cfg" /System/Boot/boot.cfg >/dev/null
"$TOOL" bootstate "$DISKS/nofallback.img" 1 >/dev/null
+16
View File
@@ -433,6 +433,22 @@ wedgeFirst | Boot/stage1.asm | rom | -
wedgeSecond | Boot/stage1.asm | rom | - | 90000000 | disks/wedgesecond.img wedgeSecond | Boot/stage1.asm | rom | - | 90000000 | disks/wedgesecond.img
wedgeThird | Boot/stage1.asm | rom | - | 90000000 | disks/wedgethird.img wedgeThird | Boot/stage1.asm | rom | - | 90000000 | disks/wedgethird.img
wedgedImage | Boot/wedged.asm | assemble | - | - wedgedImage | Boot/wedged.asm | assemble | - | -
# ---- Closing the loop from inside the machine ----
#
# A machine that says "settle it to try again" and gives you no way to do so has told you
# about a problem it will not let you fix. Settle is a program rather than a shell word:
# one job, reached through SWI, replaceable, and callable by anything that comes to call
# programs in sequence.
cosmosSettle | CosmOS/Source/cosmos.asm | run | settle.in | 90000000 | disks/settle.img
# Saying so when there is nothing to settle is as much a part of it as doing it.
cosmosSettled | CosmOS/Source/cosmos.asm | run | settle.in | 90000000 | disks/settled.img
settleApp | CosmOS/Apps/Settle.asm | assemble | - | -
# A failed start with NOTHING to fall back to. The mark must not become a reason to refuse
# to start at all - a failure that was passing recovers here, and one that is not leaves
# the machine exactly where it would have been without any of this.
noFallback | Boot/stage1.asm | rom | - | 90000000 | disks/nofallback.img
# Reading a disk that has directories on it. The machine can walk a path at this point but # Reading a disk that has directories on it. The machine can walk a path at this point but
# cannot make a directory, so the disk is built by the host tool and read here - which is # cannot make a directory, so the disk is built by the host tool and read here - which is
# the two implementations checking each other rather than either checking itself. # the two implementations checking each other rather than either checking itself.