B4: the disk remembers whether the last start arrived

The loader marks the superblock before it hands over and the system clears
the mark when it reaches its prompt, so a system that crashes on the way
there leaves it set. The loader finding it still set next time is how a
machine that will not start says so to the only thing in a position to do
anything about it. Without that, pointing boot.cfg at something that dies
before the shell is a machine that can never be told anything again - the
shell is the only way to change the file, and the file is what stops the
shell from starting.

Three states rather than two, and the third is the one worth having:

  0 settled    the last start arrived; use the configuration
  1 trying     handed over, and nothing came back to say it got there
  2 fell back  a try failed and the fallback was used, until settled

With only 0 and 1 the machine alternates for ever: fall back, reach a
prompt, clear the mark, retry the broken system, crash, fall back. State 2
stops that. A system known not to start is not tried again until somebody
says the situation has changed.

REACHING THE PROMPT IS A DELIBERATE THRESHOLD. It is not a claim that the
system works - a shell can be reached by something broken in every other
way. It is the point where a person can type, which is exactly what the
fallback exists to give back: anything wrong past there is fixable from the
prompt and nothing wrong before it is fixable at all.

The routines live in sbfs.asm because both the loader and the system read
and write this byte, and two pieces of code with their own idea of where a
byte lives is what this format has two implementations and a byte for byte
comparison to avoid.

And the trap this system documents in its own manual caught me anyway: the
first version handed the state back in A, which CALL restores, so every
read got whatever the caller happened to be holding. It comes back in
memory now, and the comment says why.

Three disks differing only in the state on them, so the tests read as three
consecutive starts of one machine while none depends on another running.
This commit is contained in:
Anachronaut
2026-08-27 16:51:39 -04:00
parent 546f336823
commit dc74149321
14 changed files with 368 additions and 3 deletions
+35
View File
@@ -63,6 +63,38 @@ boot:
BNQ bootNoDisk
INIA 0x01
STA.0
; ---- Saying that this start arrived ----
;
; The loader marks the disk before handing over, and nothing clears it but this. A system
; that crashes on the way here leaves the mark, and the loader finding it still set next
; time is how a machine that will not start says so.
;
; HERE RATHER THAN LATER, and the threshold is the whole of what the mark means. It is
; not a claim that anything works - a prompt can be reached by something broken in every
; other way. It is the point where somebody can type, which is what the fallback exists
; to give back: anything wrong past here can be fixed from the prompt, and nothing wrong
; before it can be fixed at all.
CALL sbfsBootState
BNQ prompt
SETD.0 SbfsStateWas
LDA.0
INIB 0d1
XOR
BRQ bootArrived
INIB 0d2
XOR
BNQ prompt
; Started by the fallback, so the system somebody asked for is not the one running. Said
; once, here, because there is nowhere else it would be noticed.
SETD.0 OnFallback
CALL printString
BRI prompt
bootArrived:
RSTA
CALL sbfsSetBootState
BRI prompt
bootNoDisk:
RSTA
@@ -3370,6 +3402,9 @@ Banner:
"CosmOS"
PromptText:
"> "
OnFallback:
"this is the fallback: what boot.cfg asks for did not start
"
NoDisk:
"no filesystem on the disk"
Unknown:
+78
View File
@@ -2984,8 +2984,86 @@ sbfsSaveFailed:
ADD
RET
; ---- How the last start went ----
;
; One byte of the superblock, written by the loader before it hands over and cleared by the
; system once it is running. A system that never gets that far leaves the mark set, and the
; loader seeing it still set next time is how a machine that cannot start says so to the
; only thing in a position to do anything about it.
;
; Kept here rather than in either of them because BOTH read and write it, and two pieces of
; code with their own idea of where a byte lives is the thing this filesystem has two
; implementations and a byte for byte comparison to avoid.
;
; sbfsBootState leaves the state in SbfsStateWas, and Q zero if the disk answered.
;
; NOT IN A, and the first version of this tried to. CALL restores A, so a routine cannot
; hand anything back in it: the value was set, the RET put the caller's own A back over it,
; and every state read as whatever the caller happened to be holding. It is the trap this
; system documents in its own manual and it still catches people.
sbfsBootState:
RSTA
SETD.0 SbfsBlock
STA.0
INCD.0
STA.0
CALL sbfsReadBlock
BNQ sbfsBootStateNo
SETD.1 SbfsBuffer
CALL sbfsBufferOut
SETD.0 SbfsBuffer
DPUP.0 0d17
LDA.0
SETD.1 SbfsStateWas
STA.1
RSTA
RSTB
CCF
ADD ; Q is zero: SbfsStateWas is what the disk says.
RET
sbfsBootStateNo:
RSTA
SETD.0 SbfsStateWas
STA.0
INIB 0d1
CCF
ADD ; Q is not zero, and the state reads as settled.
RET
; A is what to write. Q is zero if it went down.
;
; The superblock is read back before it is changed rather than kept from the mount, because
; everything between then and now has been reading other blocks over the buffer it was in.
sbfsSetBootState:
SETD.0 SbfsStateWants
STA.0
RSTA
SETD.0 SbfsBlock
STA.0
INCD.0
STA.0
CALL sbfsReadBlock
BNQ sbfsBootStateNo
SETD.1 SbfsBuffer
CALL sbfsBufferOut
SETD.0 SbfsStateWants
LDA.0
SETD.1 SbfsBuffer
DPUP.1 0d17
STA.1
SETD.1 SbfsBuffer
CALL sbfsBufferIn
CALL sbfsWriteBlock
RET
#Data
SbfsStateWas:
0x00
SbfsStateWants:
0x00
SbfsMagic:
"SBFS"