diff --git a/Programs/Boot/stage2.asm b/Programs/Boot/stage2.asm index 22fc068..c160811 100644 --- a/Programs/Boot/stage2.asm +++ b/Programs/Boot/stage2.asm @@ -154,11 +154,26 @@ stateSettled: useFallback: SETD.0 HaveFallback LDA.0 - BRA noSystem + BRA noFallbackTryAgain SETD.0 FallbackName RCAL tryImage 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 ---- ; ; DP0 names it. Returns only if it could not be started, having said why; everything that @@ -549,6 +564,9 @@ DidNotArriveText: StillBackText: "still on the fallback: settle it to try again " +NoFallbackText: +"no fallback, so trying it again +" NothingText: "nothing to start " diff --git a/Programs/CosmOS/Apps/Settle.asm b/Programs/CosmOS/Apps/Settle.asm new file mode 100644 index 0000000..4b28cc8 --- /dev/null +++ b/Programs/CosmOS/Apps/Settle.asm @@ -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 diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index 16bb204..89ee5f1 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -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. | | 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. | +| 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. | | 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. | @@ -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. | | 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. | +| 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 diff --git a/Programs/CosmOS/Source/cosmos.asm b/Programs/CosmOS/Source/cosmos.asm index 9560d56..4cd1e12 100644 --- a/Programs/CosmOS/Source/cosmos.asm +++ b/Programs/CosmOS/Source/cosmos.asm @@ -2276,6 +2276,55 @@ handlePrintNumber: ; ; 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. +; ---- 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: SETD.1 SystemStack LDD.0.1 @@ -3897,4 +3946,6 @@ CommandLine: osFileFetch handleFileFetch osPrintNumber handlePrintNumber osBreak handleBreak + osBootState handleBootState + osBootSettle handleBootSettle Device 0x20 diskDone diff --git a/Programs/CosmOS/Source/services.asm b/Programs/CosmOS/Source/services.asm index 0690bfc..5167219 100644 --- a/Programs/CosmOS/Source/services.asm +++ b/Programs/CosmOS/Source/services.asm @@ -135,3 +135,21 @@ ; restore. The price is that it is part of the program: a build with breakpoints in it has ; different addresses from one without. 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 diff --git a/Tests/expected/cosmosSettle.out b/Tests/expected/cosmosSettle.out new file mode 100644 index 0000000..fd75c38 --- /dev/null +++ b/Tests/expected/cosmosSettle.out @@ -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] diff --git a/Tests/expected/cosmosSettled.out b/Tests/expected/cosmosSettled.out new file mode 100644 index 0000000..7a23aaa --- /dev/null +++ b/Tests/expected/cosmosSettled.out @@ -0,0 +1,6 @@ +CosmOS +> already settled: the next start will use the configuration +finished +> halted +Execution halted. +[exit 0] diff --git a/Tests/expected/noFallback.out b/Tests/expected/noFallback.out new file mode 100644 index 0000000..976fd7e --- /dev/null +++ b/Tests/expected/noFallback.out @@ -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] diff --git a/Tests/expected/selfBootNoSystem.out b/Tests/expected/selfBootNoSystem.out index d745e55..8364cbb 100644 --- a/Tests/expected/selfBootNoSystem.out +++ b/Tests/expected/selfBootNoSystem.out @@ -1,6 +1,8 @@ stage two no /System/Boot/cosmos.bin trying the fallback +no fallback, so trying it again +no /System/Boot/cosmos.bin nothing to start Execution halted. [exit 0] diff --git a/Tests/input/settle.in b/Tests/input/settle.in new file mode 100644 index 0000000..ffccc9d --- /dev/null +++ b/Tests/input/settle.in @@ -0,0 +1,2 @@ +Settle +exit diff --git a/Tests/makedisks.sh b/Tests/makedisks.sh index 92f7cb0..bf45115 100755 --- a/Tests/makedisks.sh +++ b/Tests/makedisks.sh @@ -467,3 +467,27 @@ done # 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/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 diff --git a/Tests/manifest b/Tests/manifest index 76f1655..52a687f 100644 --- a/Tests/manifest +++ b/Tests/manifest @@ -433,6 +433,22 @@ wedgeFirst | Boot/stage1.asm | rom | - wedgeSecond | Boot/stage1.asm | rom | - | 90000000 | disks/wedgesecond.img wedgeThird | Boot/stage1.asm | rom | - | 90000000 | disks/wedgethird.img 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 # 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.