From 87d819847e6b3396df85baf6f569b990175d209a Mon Sep 17 00:00:00 2001 From: Anachronaut Date: Thu, 27 Aug 2026 19:16:21 -0400 Subject: [PATCH] A program can say how it went SWI osExit takes a status in A, and the shell keeps it. Fifty eight exits across twenty three programs now say deliberately whether they worked: 25 did what they were asked, 24 did not, 9 were asked wrongly. Compare is the exception and says so - one there means the files differ, which is a result rather than a failure, the way diff has always had it. IN A RATHER THAN Q, which is not a departure from the rule that a service answers in Q. This one takes an ARGUMENT, the way osPrintNumber takes A and B, and it never returns to answer anything. A is free precisely because a return would have put it back - and Q is the ALU's output, so a small number costs four instructions there against one in A. The shell does not print it. A program that failed has already said so in words and a number beside that is noise, so osLastStatus hands it back and Status is the program that shows it. That indirection is the point: the number exists for the thing that cannot read words. MARKING THE EXITS FOUND A DEFECT ON THE FIRST RUN. Type and More printed why they had failed and then fell through into the success exit, reporting that all was well. Nobody had noticed, because while the only reader was a person, the person could see both the complaint and the claim. Two smaller things. Snake sets the console to line mode and then exits with zero, and the linter flagged the second RSTA as redundant - an exit status and a console mode, equal by accident, which is the class that must never be collapsed. And the README still taught answering by writing into the frame, three months of habit that SRET replaced yesterday; that section is gone and the one describing SRET stands in its place. --- Programs/CosmOS/Apps/Break.asm | 1 + Programs/CosmOS/Apps/Claim.asm | 5 ++ Programs/CosmOS/Apps/Compare.asm | 7 +++ Programs/CosmOS/Apps/Copy.asm | 7 +++ Programs/CosmOS/Apps/Edit.asm | 2 + Programs/CosmOS/Apps/Fib-16.asm | 1 + Programs/CosmOS/Apps/Fib-32.asm | 1 + Programs/CosmOS/Apps/Fib-8.asm | 1 + Programs/CosmOS/Apps/Files.asm | 6 ++ Programs/CosmOS/Apps/Keys.asm | 1 + Programs/CosmOS/Apps/Life.asm | 1 + Programs/CosmOS/Apps/More.asm | 8 +++ Programs/CosmOS/Apps/Pour.asm | 4 ++ Programs/CosmOS/Apps/Say.asm | 1 + Programs/CosmOS/Apps/Settle.asm | 3 + Programs/CosmOS/Apps/Sieve-16.asm | 1 + Programs/CosmOS/Apps/Sieve-8.asm | 1 + Programs/CosmOS/Apps/Snake.asm | 1 + Programs/CosmOS/Apps/Status.asm | 87 +++++++++++++++++++++++++++++ Programs/CosmOS/Apps/Stream.asm | 4 ++ Programs/CosmOS/Apps/Type.asm | 8 +++ Programs/CosmOS/Apps/Wander.asm | 4 ++ Programs/CosmOS/Apps/greet.asm | 1 + Programs/CosmOS/Apps/hello.asm | 1 + Programs/CosmOS/README.md | 53 +++++++++--------- Programs/CosmOS/Source/cosmos.asm | 28 ++++++++++ Programs/CosmOS/Source/services.asm | 14 +++++ Tests/expected/cosmosBreak.out | 2 +- Tests/expected/cosmosClaim.out | 2 +- Tests/expected/cosmosCwd.out | 2 +- Tests/expected/cosmosInvoke.out | 4 +- Tests/expected/cosmosMonitor.out | 2 +- Tests/expected/cosmosRun.out | 14 ++--- Tests/expected/cosmosSlowDisk.out | 14 ++--- Tests/expected/cosmosStatus.out | 16 ++++++ Tests/expected/cosmosTreeWrite.out | 4 +- Tests/input/status.in | 7 +++ Tests/lint-baseline.txt | 1 + Tests/makedisks.sh | 14 +++++ Tests/manifest | 10 ++++ 40 files changed, 297 insertions(+), 47 deletions(-) create mode 100644 Programs/CosmOS/Apps/Status.asm create mode 100644 Tests/expected/cosmosStatus.out create mode 100644 Tests/input/status.in diff --git a/Programs/CosmOS/Apps/Break.asm b/Programs/CosmOS/Apps/Break.asm index 50efcb6..f6231fa 100644 --- a/Programs/CosmOS/Apps/Break.asm +++ b/Programs/CosmOS/Apps/Break.asm @@ -58,6 +58,7 @@ start: SETD.0 DoneText SWI osPrintString + RSTA SWI osExit deeper: diff --git a/Programs/CosmOS/Apps/Claim.asm b/Programs/CosmOS/Apps/Claim.asm index bf35f5f..eb6a55a 100644 --- a/Programs/CosmOS/Apps/Claim.asm +++ b/Programs/CosmOS/Apps/Claim.asm @@ -47,6 +47,7 @@ start: BNQ refused SETD.0 Allowed SWI osPrintString + RSTA SWI osExit refused: @@ -60,19 +61,23 @@ refused: BNQ noHonest SETD.0 Honest SWI osPrintString + RSTA SWI osExit noHonest: SETD.0 NoHonest SWI osPrintString + INIA 0d1 SWI osExit noStart: SETD.0 NoStart SWI osPrintString + INIA 0d1 SWI osExit noWrite: SETD.0 NoWrite SWI osPrintString + INIA 0d1 SWI osExit #Data diff --git a/Programs/CosmOS/Apps/Compare.asm b/Programs/CosmOS/Apps/Compare.asm index d4eb647..afaeac7 100644 --- a/Programs/CosmOS/Apps/Compare.asm +++ b/Programs/CosmOS/Apps/Compare.asm @@ -202,30 +202,37 @@ byteBorrow: alike: SETD.0 SameText SWI osPrintString + RSTA SWI osExit different: SETD.0 DifferentText SWI osPrintString + INIA 0d1 SWI osExit usage: SETD.0 Usage SWI osPrintString + INIA 0d2 SWI osExit firstFailed: SETD.0 FirstError SWI osPrintString + INIA 0d2 SWI osExit secondFailed: SETD.0 SecondError SWI osPrintString + INIA 0d2 SWI osExit firstReadFailed: SETD.0 FirstReadError SWI osPrintString + INIA 0d2 SWI osExit secondReadFailed: SETD.0 SecondReadError SWI osPrintString + INIA 0d2 SWI osExit #Data diff --git a/Programs/CosmOS/Apps/Copy.asm b/Programs/CosmOS/Apps/Copy.asm index fe82fad..7c09799 100644 --- a/Programs/CosmOS/Apps/Copy.asm +++ b/Programs/CosmOS/Apps/Copy.asm @@ -154,6 +154,7 @@ copyDone: BNQ doneFailed SETD.0 Copied SWI osPrintString + RSTA SWI osExit ; DP0 names the source word and DP1 the destination word. @@ -202,26 +203,32 @@ takeBorrow: usage: SETD.0 Usage SWI osPrintString + INIA 0d2 SWI osExit sourceFailed: SETD.0 SourceError SWI osPrintString + INIA 0d1 SWI osExit readFailed: SETD.0 ReadError SWI osPrintString + INIA 0d1 SWI osExit startFailed: SETD.0 StartError SWI osPrintString + INIA 0d1 SWI osExit writeFailed: SETD.0 WriteError SWI osPrintString + INIA 0d1 SWI osExit doneFailed: SETD.0 DoneError SWI osPrintString + INIA 0d1 SWI osExit #Data diff --git a/Programs/CosmOS/Apps/Edit.asm b/Programs/CosmOS/Apps/Edit.asm index ddc3cde..5d7764b 100644 --- a/Programs/CosmOS/Apps/Edit.asm +++ b/Programs/CosmOS/Apps/Edit.asm @@ -161,12 +161,14 @@ commandArgument: BRI commandLoop quit: + RSTA SWI osExit noName: SETD.0 NoNameText SWI osPrintString CALL newLine + INIA 0d2 SWI osExit diff --git a/Programs/CosmOS/Apps/Fib-16.asm b/Programs/CosmOS/Apps/Fib-16.asm index 7a4966f..ad34d22 100644 --- a/Programs/CosmOS/Apps/Fib-16.asm +++ b/Programs/CosmOS/Apps/Fib-16.asm @@ -67,6 +67,7 @@ start: BRI start end: CALL lineFeed + RSTA SWI osExit #Data diff --git a/Programs/CosmOS/Apps/Fib-32.asm b/Programs/CosmOS/Apps/Fib-32.asm index a654989..b3d7b6f 100644 --- a/Programs/CosmOS/Apps/Fib-32.asm +++ b/Programs/CosmOS/Apps/Fib-32.asm @@ -123,6 +123,7 @@ start: end: CALL lineFeed ;HALT + RSTA SWI osExit #Data diff --git a/Programs/CosmOS/Apps/Fib-8.asm b/Programs/CosmOS/Apps/Fib-8.asm index 76afb10..6d6314d 100644 --- a/Programs/CosmOS/Apps/Fib-8.asm +++ b/Programs/CosmOS/Apps/Fib-8.asm @@ -33,6 +33,7 @@ start: end: CALL lineFeed ;HALT + RSTA SWI osExit ; Return to CosmOS. #Data diff --git a/Programs/CosmOS/Apps/Files.asm b/Programs/CosmOS/Apps/Files.asm index 179ec06..067688e 100644 --- a/Programs/CosmOS/Apps/Files.asm +++ b/Programs/CosmOS/Apps/Files.asm @@ -101,28 +101,34 @@ atEnd: BRQ stillThere SETD.0 GoneText SWI osPrintString + RSTA SWI osExit stillThere: SETD.0 StillText SWI osPrintString + RSTA SWI osExit noSave: SETD.0 NoSaveText SWI osPrintString + INIA 0d1 SWI osExit noRead: SETD.0 NoReadText SWI osPrintString + INIA 0d1 SWI osExit noRename: SETD.0 NoRenameText SWI osPrintString + INIA 0d1 SWI osExit noDelete: SETD.0 NoDeleteText SWI osPrintString + INIA 0d1 SWI osExit #Data diff --git a/Programs/CosmOS/Apps/Keys.asm b/Programs/CosmOS/Apps/Keys.asm index 6a886b8..a8b1ad3 100644 --- a/Programs/CosmOS/Apps/Keys.asm +++ b/Programs/CosmOS/Apps/Keys.asm @@ -56,6 +56,7 @@ spin: SETD.0 DoneText CALL printString CALL newLine + RSTA SWI osExit ; Entered because the console had something to say. Never called. diff --git a/Programs/CosmOS/Apps/Life.asm b/Programs/CosmOS/Apps/Life.asm index 5c0487d..124d98f 100644 --- a/Programs/CosmOS/Apps/Life.asm +++ b/Programs/CosmOS/Apps/Life.asm @@ -105,6 +105,7 @@ lifeEnd: CALL lineFeed CALL printString ; DP0 still holds the words: CALL puts DP0 back. CALL lineFeed + RSTA SWI osExit seedGlider: diff --git a/Programs/CosmOS/Apps/More.asm b/Programs/CosmOS/Apps/More.asm index f0c081d..6c73935 100644 --- a/Programs/CosmOS/Apps/More.asm +++ b/Programs/CosmOS/Apps/More.asm @@ -109,6 +109,7 @@ fullPage: noName: SETD.0 Usage SWI osPrintString + INIA 0d2 SWI osExit openFailed: SETD.0 OpenError @@ -123,7 +124,14 @@ printError: SWI osPrintNumber SETD.0 NewLine SWI osPrintString + ; ITS OWN EXIT, and it did not have one. This fell through into finished and reported + ; that everything was fine, having just printed the reason it was not - which nobody + ; noticed while the only reader was a person, who could see both. + INIA 0d1 + SWI osExit + finished: + RSTA SWI osExit #Data diff --git a/Programs/CosmOS/Apps/Pour.asm b/Programs/CosmOS/Apps/Pour.asm index 5ec5afb..16baa6f 100644 --- a/Programs/CosmOS/Apps/Pour.asm +++ b/Programs/CosmOS/Apps/Pour.asm @@ -118,6 +118,7 @@ theTail: SWI osPrintNumber SETD.0 AndTail SWI osPrintString + RSTA SWI osExit ; The block becomes 256 copies of the block number plus a fixed byte, so that a block @@ -198,14 +199,17 @@ tenLoop: startFailed: SETD.0 NoStart SWI osPrintString + INIA 0d1 SWI osExit writeFailed: SETD.0 NoWrite SWI osPrintString + INIA 0d1 SWI osExit doneFailed: SETD.0 NoDone SWI osPrintString + INIA 0d1 SWI osExit #Data diff --git a/Programs/CosmOS/Apps/Say.asm b/Programs/CosmOS/Apps/Say.asm index f8ae9a3..ffa4856 100644 --- a/Programs/CosmOS/Apps/Say.asm +++ b/Programs/CosmOS/Apps/Say.asm @@ -40,6 +40,7 @@ sayNothing: sayEnd: SETD.0 NewLine SWI osPrintString + RSTA SWI osExit #Data diff --git a/Programs/CosmOS/Apps/Settle.asm b/Programs/CosmOS/Apps/Settle.asm index 4b28cc8..2f571df 100644 --- a/Programs/CosmOS/Apps/Settle.asm +++ b/Programs/CosmOS/Apps/Settle.asm @@ -54,16 +54,19 @@ doSettle: BNA settleFailed SETD.0 Settled SWI osPrintString + RSTA SWI osExit settleFailed: SETD.0 NoDisk SWI osPrintString + INIA 0d1 SWI osExit alreadySettled: SETD.0 Already SWI osPrintString + RSTA SWI osExit #Data diff --git a/Programs/CosmOS/Apps/Sieve-16.asm b/Programs/CosmOS/Apps/Sieve-16.asm index 7bba19a..24e3fef 100644 --- a/Programs/CosmOS/Apps/Sieve-16.asm +++ b/Programs/CosmOS/Apps/Sieve-16.asm @@ -74,6 +74,7 @@ advancePage: finished: CALL lineFeed + RSTA SWI osExit ; Return to CosmOS. ; DP0 points at a PrimeStates entry. CALL restores it on return. diff --git a/Programs/CosmOS/Apps/Sieve-8.asm b/Programs/CosmOS/Apps/Sieve-8.asm index ae0b212..b0e6f92 100644 --- a/Programs/CosmOS/Apps/Sieve-8.asm +++ b/Programs/CosmOS/Apps/Sieve-8.asm @@ -38,6 +38,7 @@ start: BRI markMultiples ; Otherwise, loop again to mark the next multiple as nonprime. end: CALL lineFeed ; Print a linefeed to make it look nice. + RSTA SWI osExit ; The program is done, we found all the primes! diff --git a/Programs/CosmOS/Apps/Snake.asm b/Programs/CosmOS/Apps/Snake.asm index 4f5621b..0e1af2d 100644 --- a/Programs/CosmOS/Apps/Snake.asm +++ b/Programs/CosmOS/Apps/Snake.asm @@ -105,6 +105,7 @@ gameOverSay: CALL newLine RSTA OUTA 0x02 ; Line mode, the way it was found. + RSTA ; splitlint[redundant-assignment]: an exit status, not a console mode SWI osExit ; ---- Reaching a square ---- diff --git a/Programs/CosmOS/Apps/Status.asm b/Programs/CosmOS/Apps/Status.asm new file mode 100644 index 0000000..c8d9939 --- /dev/null +++ b/Programs/CosmOS/Apps/Status.asm @@ -0,0 +1,87 @@ +; Status.asm +; Says what the last program made of what it was asked to do. +; +; The shell keeps the number and does not print it, because a program that failed has +; already said so in words and a number beside that would be noise. But a number nobody can +; see is a number nobody can trust, so this is how a person looks. +; +; 0 it did what it was asked +; 1 it did not +; 2 it was asked wrongly +; +; A program may give its own meanings if it says so, and Compare does: one there means the +; files differ, which is a result rather than a failure. +; +; Written by Anachronaut + +#Include services.asm + +#Program + + #Base 0x4000 + +start: + SWI osLastStatus + MVQA + SETD.0 Was + STA.0 + + SETD.0 Prefix + SWI osPrintString + + ; A and B together, most significant first - so the status is the LOW half. Put in A the + ; first time this was written, which printed a status of two as five hundred and twelve. + RSTA + SETD.0 Was + LDB.0 + SWI osPrintNumber + + ; And in words, for the three the system itself uses. + SETD.0 Was + LDA.0 + BRA sayWorked + INIB 0d1 + XOR + BRQ sayFailed + SETD.0 Was + LDA.0 + INIB 0d2 + XOR + BRQ sayAsked + BRI done + +sayWorked: + SETD.0 WorkedText + SWI osPrintString + BRI done +sayFailed: + SETD.0 FailedText + SWI osPrintString + BRI done +sayAsked: + SETD.0 AskedText + SWI osPrintString + +done: + SETD.0 NewLine + SWI osPrintString + RSTA + SWI osExit + +#Data + + #Base 0x2000 + +Prefix: +"the last program left " +WorkedText: +", which is: it did what it was asked" +FailedText: +", which is: it did not" +AskedText: +", which is: it was asked wrongly" +NewLine: +" +" +Was: + 0x00 diff --git a/Programs/CosmOS/Apps/Stream.asm b/Programs/CosmOS/Apps/Stream.asm index 3ed0285..05bdcf1 100644 --- a/Programs/CosmOS/Apps/Stream.asm +++ b/Programs/CosmOS/Apps/Stream.asm @@ -216,6 +216,7 @@ missing: SWI osPrintString CALL printWhy + INIA 0d1 SWI osExit noBig: @@ -223,14 +224,17 @@ noBig: SETD.0 NoBigText SWI osPrintString CALL printWhy + INIA 0d1 SWI osExit noSmall: SETD.0 NoSmallText SWI osPrintString + INIA 0d1 SWI osExit noRename: SETD.0 NoRenameText SWI osPrintString + INIA 0d1 SWI osExit ; ---- Routines ---- diff --git a/Programs/CosmOS/Apps/Type.asm b/Programs/CosmOS/Apps/Type.asm index e62b6d6..911935d 100644 --- a/Programs/CosmOS/Apps/Type.asm +++ b/Programs/CosmOS/Apps/Type.asm @@ -44,6 +44,7 @@ printLoop: noName: SETD.0 Usage SWI osPrintString + INIA 0d2 SWI osExit openFailed: SETD.0 OpenError @@ -58,7 +59,14 @@ printError: SWI osPrintNumber SETD.0 NewLine SWI osPrintString + ; ITS OWN EXIT, and it did not have one. This fell through into finished and reported + ; that everything was fine, having just printed the reason it was not - which nobody + ; noticed while the only reader was a person, who could see both. + INIA 0d1 + SWI osExit + finished: + RSTA SWI osExit #Data diff --git a/Programs/CosmOS/Apps/Wander.asm b/Programs/CosmOS/Apps/Wander.asm index 5d51715..6864483 100644 --- a/Programs/CosmOS/Apps/Wander.asm +++ b/Programs/CosmOS/Apps/Wander.asm @@ -65,19 +65,23 @@ wanderByte: BRI wanderBlock wanderDone: + RSTA SWI osExit noWhere: SETD.0 NoWhereText SWI osPrintString + INIA 0d1 SWI osExit noSuchPlace: SETD.0 NoPlaceText SWI osPrintString + INIA 0d1 SWI osExit noFile: SETD.0 NoFileText SWI osPrintString + INIA 0d1 SWI osExit #Data diff --git a/Programs/CosmOS/Apps/greet.asm b/Programs/CosmOS/Apps/greet.asm index 547cd09..d783b6a 100644 --- a/Programs/CosmOS/Apps/greet.asm +++ b/Programs/CosmOS/Apps/greet.asm @@ -39,6 +39,7 @@ greet: ; Give the machine back. The system takes its Stack back at this point, so everything ; this program pushed goes with it. + RSTA SWI osExit #Data diff --git a/Programs/CosmOS/Apps/hello.asm b/Programs/CosmOS/Apps/hello.asm index 2a2422f..7deacf7 100644 --- a/Programs/CosmOS/Apps/hello.asm +++ b/Programs/CosmOS/Apps/hello.asm @@ -21,6 +21,7 @@ End: OUTA 0x00 ; Output it to the text console. ;HALT ; Terminate the program. ; Instead, let's call osExit to return the system nicely. Fourth change. + RSTA SWI osExit #Data diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index 6adc125..8ecdbeb 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -361,8 +361,9 @@ 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. | +| Status | Says what the last program made of what it was asked to do, in 222 bytes. The shell keeps the number and does not print it; this is how a person looks. | +| Settle | Says how the last start went and tells the machine to stop falling back, in 353 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 675 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. | | Stream | Reads an 84,000 byte file through a buffer of 256, which is what says a file bigger than Data Memory can be read at all. | @@ -463,7 +464,7 @@ Typed in as bytes, checked by disassembling it back, and run. It ends with `SWI `Edit` is the first program on this machine that makes a file a person typed - every byte on every disk before it was put there by the host tool. It is line oriented in the manner of `ed`: `l` lists, `a` adds at the end, `i` and `c` and `d` take a line number, `w` writes and `q` stops. -It includes nothing but `services.asm` and `text.asm`: the filesystem and the console are the system's, asked for rather than carried. That is what brought `Edit` down from 4,941 bytes to 1,983 bytes without a line of its own logic changing - and the way that was checked is worth knowing, because the recorded output of the `cosmosEdit` test did not move by a single byte across the rewrite. +It includes nothing but `services.asm` and `text.asm`: the filesystem and the console are the system's, asked for rather than carried. That is what brought `Edit` down from 4,941 bytes to 1,986 bytes without a line of its own logic changing - and the way that was checked is worth knowing, because the recorded output of the `cosmosEdit` test did not move by a single byte across the rewrite. It keeps the document as a **linked list of lines** rather than one buffer with newlines in it. Each line says where the next one is, how long it is, and then its bytes. Inserting is two pointers changed and nothing moved; with a flat buffer it would mean shifting every byte after the edit, on a machine whose only block move is a device asked politely. The price is that deleted lines are not reused, so a heavy session uses more room than the document needs and writing it out is what tidies up. @@ -591,6 +592,7 @@ 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. | +| osLastStatus | Q answers what the last program exited with: 0 it did what it was asked, 1 it did not, 2 it was asked wrongly. A program may give its own meanings if it says so. | | 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. | @@ -675,33 +677,12 @@ Running off the end is how a reader finds out it has finished, so it gets an ans `Programs/CosmOS/Apps/Stream.asm` reads an 84,000 byte file through a 256 byte buffer, then reads a small file both ways - whole with `osFileRead` and streamed - and checks that the two agree. -`Programs/CosmOS/Apps/Files.asm` does the whole round trip - write, read, report, rename, delete - in 665 bytes, and includes nothing but the service names. +`Programs/CosmOS/Apps/Files.asm` does the whole round trip - write, read, report, rename, delete - in 675 bytes, and includes nothing but the service names. `osArgument` is how a program is told what it is for. Everything written before it did the same thing however it was started, which is fine for a program that greets you and no use to one that edits a named document. What arrives is the whole rest of the line, spaces and all, rather than a list of words: what counts as an argument is the program's business, and handing over what was typed is the system's. A handler is entered with the caller's registers exactly as they were, because an interrupt frame is pushed rather than cleared. That is why a service can be given a pointer in DP0 and a count in B without any of it being copied anywhere first. -### How A Service Answers: - -The same thing that makes an interrupt safe makes a service mute. RETI restores every register from the frame, so whatever a handler worked out is thrown away on the way out - which is exactly right for a device interrupting at a moment nobody chose, and useless for a service that was asked a question. - -A service answers by **writing into its own frame**, over the saved register, and letting RETI put it back. MVSD copies the Stack Pointer into a Data Pointer and the frame sits just above it, so returning a byte in Q is three instructions: - -``` -answer: - INIA 0d42 - MVSD.1 - DPUP.1 0d02 ; The saved Q. See the frame table under Interrupts. - STA.1 - RETI -``` - -**Which registers a service may answer in is the convention CALL already has: Q and DP3.** A subroutine cannot hand back A, B or Data Pointers 0 to 2 because RET puts them back; a service *could* write over any of them and should not, for exactly the reason that list exists. A caller is entitled to find what it kept still there. - -**Only the handler itself can do this.** The offsets are from wherever the Stack Pointer is, and a CALL moves it by ten - so a routine called by a handler that tried the same thing would be writing into its own return address. The poke belongs inline, next to the RETI. - -A service that has nothing to say does nothing, and the caller's registers arrive back untouched. That is worth knowing from the other side too: a service cannot corrupt a register by accident, only by deciding to. - ## The Filesystem Library: The disk knows blocks and nothing else, so a filesystem is software. Programs/CosmOS/Source/sbfs.asm is one. @@ -884,6 +865,28 @@ A file's length is its block count times 256 plus its tail, which is the same as The other implementation of this format is SplitDisk, on the host. Nothing is shared between the two but the specification, so a change to either has to be a change to both. +### What A Program Made Of It: + +`SWI osExit` takes a status **in A**: zero if the program did what it was asked, one if it +did not, two if it was asked wrongly. A program may give its own meanings if it says so, and +`Compare` does - one there means the files differ, which is a result rather than a failure. + +**In A rather than Q**, which is not a departure from the rule that a service answers in Q. +This one *takes an argument*, the way `osPrintNumber` takes A and B, and it never returns to +answer anything. A is free precisely because a return would have put it back - and Q is the +ALU's output, so setting it to a small number costs four instructions where A costs one. + +**The shell keeps the number and does not print it.** A program that failed has already said +so in words, and a number beside that would be noise. `osLastStatus` hands it back and +`Status` is the program that shows it. The indirection is the point: this number is for the +thing that cannot read words - whatever comes to run programs in sequence and has to decide +whether to run the next one. + +Marking all fifty eight exits found a defect on its first run. `Type` and `More` printed why +they had failed and then **fell through into the success exit**, reporting that all was +well. Nobody had noticed, because while the only reader was a person, the person could see +both. + ### How A Service Answers: A handler arrives with the caller's registers pushed rather than cleared, and **`RETI` diff --git a/Programs/CosmOS/Source/cosmos.asm b/Programs/CosmOS/Source/cosmos.asm index 9f8ccef..e3cc51f 100644 --- a/Programs/CosmOS/Source/cosmos.asm +++ b/Programs/CosmOS/Source/cosmos.asm @@ -2242,6 +2242,18 @@ 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. +; What the last program made of what it was asked to do. Kept rather than shown: a program +; that failed has already said so in words, and a number beside that would be noise. It is +; here for the thing that cannot read words - whatever comes to run programs in sequence and +; has to decide whether to run the next one. +handleLastStatus: + SETD.2 LastStatus + LDA.2 + RSTB + CCF + ADD ; A is the answer, so Q becomes it. + SRET + ; ---- 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 @@ -2292,6 +2304,17 @@ bootSettleNo: SRET handleExit: + ; ---- What the program made of it ---- + ; + ; A, before anything below disturbs it. IN A RATHER THAN Q, and that is not a departure + ; from the rule that a service answers in Q: this one takes an argument, the way + ; osPrintNumber takes A and B, and it never returns to answer anything. The register a + ; subroutine cannot hand anything back in is exactly the one that is free here - and Q is + ; the ALU's output, so setting it to a small number costs four instructions where A costs + ; one. + SETD.1 LastStatus + STA.1 + SETD.1 SystemStack LDD.0.1 MVDS.0 @@ -3417,6 +3440,10 @@ Banner: "CosmOS" PromptText: "> " +; Nothing has run yet, so nothing has failed yet. +LastStatus: + 0x00 + OnFallback: "this is the fallback: what boot.cfg asks for did not start " @@ -3913,6 +3940,7 @@ CommandLine: osFileFetch handleFileFetch osPrintNumber handlePrintNumber osBreak handleBreak + osLastStatus handleLastStatus osBootState handleBootState osBootSettle handleBootSettle Device 0x20 diskDone diff --git a/Programs/CosmOS/Source/services.asm b/Programs/CosmOS/Source/services.asm index 5167219..425340f 100644 --- a/Programs/CosmOS/Source/services.asm +++ b/Programs/CosmOS/Source/services.asm @@ -153,3 +153,17 @@ ; about something the loader has no way to check. osBootState 0d33 osBootSettle 0d34 + +; ---- What a program made of it ---- +; +; osExit takes a status in A: zero if the program did what it was asked, and a number of +; its own choosing if it did not. IN A RATHER THAN Q, which is not a departure from the +; rule that a service answers in Q - this one takes an argument, the way osPrintNumber +; does, and never returns to answer anything. A is free precisely because a return would +; have put it back, and Q is the ALU's output, so a small number costs four instructions +; there and one in A. +; +; osLastStatus answers in Q with what the last program exited with. The shell does not +; print it: a program that failed has already said so in words, and a number beside that +; would be noise. This is for the thing that cannot read words. + osLastStatus 0d35 diff --git a/Tests/expected/cosmosBreak.out b/Tests/expected/cosmosBreak.out index b017de8..3ba8dd2 100644 --- a/Tests/expected/cosmosBreak.out +++ b/Tests/expected/cosmosBreak.out @@ -5,7 +5,7 @@ break at 4016 A 11 B 22 Q 00 status 00 DP0 2030 DP1 2000 DP2 2037 DP3 4000 SP FFFF press a key -break at 4033 +break at 4034 A 44 B 55 Q 00 status 00 DP0 2000 DP1 2037 DP2 2030 DP3 4000 SP FFF5 press a key diff --git a/Tests/expected/cosmosClaim.out b/Tests/expected/cosmosClaim.out index 86f2f7a..fc599df 100644 --- a/Tests/expected/cosmosClaim.out +++ b/Tests/expected/cosmosClaim.out @@ -2,7 +2,7 @@ CosmOS > claiming more than was reserved was refused and the size it really came to was taken finished -> Claim.sbx 572 +> Claim.sbx 580 claim.dat 266 2 files > halted diff --git a/Tests/expected/cosmosCwd.out b/Tests/expected/cosmosCwd.out index 6616087..c30eda7 100644 --- a/Tests/expected/cosmosCwd.out +++ b/Tests/expected/cosmosCwd.out @@ -3,7 +3,7 @@ CosmOS A B 0 files, 3 directories -> /A> Say.sbx 52 +> /A> Say.sbx 53 notes.txt 25 2 files /A> these are the notes in A diff --git a/Tests/expected/cosmosInvoke.out b/Tests/expected/cosmosInvoke.out index cfa9dff..b10c9ef 100644 --- a/Tests/expected/cosmosInvoke.out +++ b/Tests/expected/cosmosInvoke.out @@ -5,8 +5,8 @@ finished finished > it says: once more finished -> Say.sbx 155 -dir.sbx 155 +> Say.sbx 156 +dir.sbx 156 notes.txt 21 notes.sbx 21 4 files diff --git a/Tests/expected/cosmosMonitor.out b/Tests/expected/cosmosMonitor.out index d00ceae..37e4c7c 100644 --- a/Tests/expected/cosmosMonitor.out +++ b/Tests/expected/cosmosMonitor.out @@ -14,7 +14,7 @@ CosmOS 4014 47 00 20 5D SETD.0 205D * 4000 47 00 20 00 72 10 47 00 20 44 72 10 47 00 20 7A G. .r.G. Dr.G. z 4010 27 1F 72 11 47 00 20 5D 72 10 47 00 20 7A 72 10 '.r.G. ]r.G. zr. -4020 47 00 20 65 72 10 72 12 00 00 00 00 00 00 00 00 G. er.r......... +4020 47 00 20 65 72 10 20 72 12 00 00 00 00 00 00 00 G. er. r........ 4030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * bank 01 * 2000 61 20 70 72 6F 67 72 61 6D 2C 20 6C 6F 61 64 65 a program, loade diff --git a/Tests/expected/cosmosRun.out b/Tests/expected/cosmosRun.out index d835bb6..7088d8e 100644 --- a/Tests/expected/cosmosRun.out +++ b/Tests/expected/cosmosRun.out @@ -1,12 +1,12 @@ CosmOS > nothing is loaded -> greet.sbx 210 -hello.sbx 52 -Life.sbx 1409 -Snake.sbx 2167 -Keys.sbx 663 -Say.sbx 155 -Break.sbx 148 +> greet.sbx 211 +hello.sbx 53 +Life.sbx 1410 +Snake.sbx 2168 +Keys.sbx 664 +Say.sbx 156 +Break.sbx 149 notes.txt 21 8 files > load what? diff --git a/Tests/expected/cosmosSlowDisk.out b/Tests/expected/cosmosSlowDisk.out index c998466..9845771 100644 --- a/Tests/expected/cosmosSlowDisk.out +++ b/Tests/expected/cosmosSlowDisk.out @@ -1,11 +1,11 @@ CosmOS -> greet.sbx 210 -hello.sbx 52 -Life.sbx 1409 -Snake.sbx 2167 -Keys.sbx 663 -Say.sbx 155 -Break.sbx 148 +> greet.sbx 211 +hello.sbx 53 +Life.sbx 1410 +Snake.sbx 2168 +Keys.sbx 664 +Say.sbx 156 +Break.sbx 149 notes.txt 21 8 files > loaded, starting at 4000 diff --git a/Tests/expected/cosmosStatus.out b/Tests/expected/cosmosStatus.out new file mode 100644 index 0000000..32c19f4 --- /dev/null +++ b/Tests/expected/cosmosStatus.out @@ -0,0 +1,16 @@ +CosmOS +> it says: it worked +finished +> the last program left 0, which is: it did what it was asked +finished +> type: give me a file name +finished +> the last program left 2, which is: it was asked wrongly +finished +> type: cannot find the file, error 2 +finished +> the last program left 1, which is: it did not +finished +> halted +Execution halted. +[exit 0] diff --git a/Tests/expected/cosmosTreeWrite.out b/Tests/expected/cosmosTreeWrite.out index 6192d57..98230ff 100644 --- a/Tests/expected/cosmosTreeWrite.out +++ b/Tests/expected/cosmosTreeWrite.out @@ -1,13 +1,13 @@ CosmOS > Folder -Edit.sbx 1983 +Edit.sbx 1986 1 file, 1 directory > loaded, starting at 4000 > note.txt, 0 lines > : : : > written, 67 bytes > finished > Folder -Edit.sbx 1983 +Edit.sbx 1986 note.txt 67 2 files, 1 directory > halted diff --git a/Tests/input/status.in b/Tests/input/status.in new file mode 100644 index 0000000..350dd98 --- /dev/null +++ b/Tests/input/status.in @@ -0,0 +1,7 @@ +Say it worked +Status +Type +Status +Type /nothing/here.txt +Status +exit diff --git a/Tests/lint-baseline.txt b/Tests/lint-baseline.txt index 4d42437..601d4eb 100644 --- a/Tests/lint-baseline.txt +++ b/Tests/lint-baseline.txt @@ -3,6 +3,7 @@ Programs/Boot/stage1.asm redundant-setd 2 Programs/Boot/stage2.asm redundant-setd 1 Programs/CosmOS/Apps/Copy.asm redundant-setd 2 Programs/CosmOS/Apps/Edit.asm redundant-setd 3 +Programs/CosmOS/Apps/Status.asm redundant-setd 1 Programs/CosmOS/Apps/Wander.asm redundant-setd 1 Programs/CosmOS/Assembler/Asm.asm redundant-assignment 2 Programs/CosmOS/Assembler/Asm.asm redundant-setd 8 diff --git a/Tests/makedisks.sh b/Tests/makedisks.sh index bf45115..6cc07f9 100755 --- a/Tests/makedisks.sh +++ b/Tests/makedisks.sh @@ -491,3 +491,17 @@ cp "$DISKS/selfboot.img" "$DISKS/nofallback.img" 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 + +# ---- What a program made of it ---- +# +# A status is only worth having if it survives the program that set it, so this runs three +# in a row and asks after each: one that works, one that is asked wrongly, and one that +# fails outright. Status is a PROGRAM and reaches the number through a service, which is +# what will let something other than a person read it. +"$TOOL" format "$DISKS/status.img" 512 4 >/dev/null +"$TOOL" mkdir "$DISKS/status.img" /Apps >/dev/null +for app in Status Say Type Settle; do + "$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" \ + "$ROOT/Programs/CosmOS/Apps/$app.asm" -o "$WORK/$app.sbx" >/dev/null + "$TOOL" put "$DISKS/status.img" "$WORK/$app.sbx" /Apps/$app.sbx >/dev/null +done diff --git a/Tests/manifest b/Tests/manifest index 1718a1c..cd431ef 100644 --- a/Tests/manifest +++ b/Tests/manifest @@ -445,6 +445,16 @@ cosmosSettle | CosmOS/Source/cosmos.asm | run | settle.in cosmosSettled | CosmOS/Source/cosmos.asm | run | settle.in | 90000000 | disks/settled.img settleApp | CosmOS/Apps/Settle.asm | assemble | - | - +# ---- What a program made of it ---- +# +# Three programs in a row, each asked after. A status that did not survive the program that +# set it would be no use to the thing this exists for - whatever comes to run programs in +# sequence and has to decide whether to run the next one. +# +# 0 it did what it was asked, 1 it did not, 2 it was asked wrongly. +cosmosStatus | CosmOS/Source/cosmos.asm | run | status.in | 90000000 | disks/status.img +statusApp | CosmOS/Apps/Status.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.