diff --git a/Programs/CosmOS/Apps/Grid.asm b/Programs/CosmOS/Apps/Grid.asm index f04e0db..9066a3a 100644 --- a/Programs/CosmOS/Apps/Grid.asm +++ b/Programs/CosmOS/Apps/Grid.asm @@ -44,6 +44,14 @@ start: INIA 0x03 OUTA 0xE8 ; RegisterBank. + ; ---- Asking for the screen back afterwards ---- + ; + ; Everything below overwrites a tile, all sixteen colour schemes and every cell of the map, + ; and none of that is this program's to keep. The system puts it somewhere and gives it back + ; at exit - and if it says it cannot, this carries on anyway, because it did before there + ; was anywhere to put it. + SWI osTakeScreen + CALL putTile CALL putPalette CALL putMap @@ -119,74 +127,15 @@ finished: ; ---- Putting the screen back ---- ; - ; All four scroll registers, or the shell inherits a view that begins half way into a cell. - RSTA - OUTA 0x36 - OUTA 0x37 - OUTA 0x38 - - ; The row origin too, and then every cell of the map and not just the visible ones. A - ; console that scrolls would otherwise walk down into rows this program filled, and find a - ; grid underneath its own output. - OUTA 0x34 ; A is still nought, from the three above. - - INIA 0d4 - OUTA 0xE3 - INIA 0x40 - OUTA 0xE4 - RSTA - OUTA 0xE5 - OUTA 0xE2 ; The byte to write: tile 0 is the space, attribute 0 is plain. - INIA 0x80 - OUTA 0xE6 - RSTA - OUTA 0xE7 ; 0x8000 bytes, which is the whole map. - INIA 0x02 - OUTA 0xE8 ; Fill. - - ; ---- And the cursor back to the top ---- + ; ---- What used to be here ---- ; - ; The Fill above empties the map and says nothing to the console about it, so the shell - ; carried on writing from wherever the cursor had been left standing when this program - ; started - part way down a screen that no longer has anything on it. Clearing is what - ; puts a cursor home, and it costs one write. - INIA 0x01 - OUTA 0x05 - - ; ---- And the colours it woke up with ---- + ; Four scroll registers put back, the whole map filled with spaces, the cursor sent home, + ; and palette bank 0 written out by hand - and it was STILL wrong, because the other fifteen + ; banks kept this program's colours and there was nowhere to have put the real ones. ; - ; Only the first pair, and that is worth being honest about. The console's own scheme is - ; sixteen banks - colours on black in 0 to 7, the same colours inverted in 8 to 15 - and - ; this program wrote over all of them, because the attribute nibble lands on exactly the - ; entries the console uses and there is nowhere else for it to land. Putting all thirty two - ; back would mean copying the device's own table into an application, which is the kind of - ; duplication that goes stale the first time somebody picks a nicer green. - ; - ; So it restores bank 0, grey on black, which is what plain text has always been and what - ; the shell will be using when it gets the machine back. The other fifteen keep this - ; program's colours until something else writes them, which is visible only to a program - ; that sets the console's attribute port. - ; - ; THE REAL ANSWER IS A COMMAND TO THE SCREEN saying "give me back what you woke up with", - ; the same way the console has one for clearing. There is not one, and this program is the - ; first thing that ever wanted it. - INIA 0d4 - OUTA 0xE3 - INIA 0xFC - OUTA 0xE4 + ; All of it is osTakeScreen's now, and it gives back what was actually there rather than + ; what a clean machine looks like. RSTA - OUTA 0xE5 - OUTA 0xE9 ; Entry 0, the paper: black. - OUTA 0xE9 - OUTA 0xE9 - OUTA 0xE9 - INIA 0xD8 - OUTA 0xE9 ; Entry 1, the ink: grey. - OUTA 0xE9 - OUTA 0xE9 - RSTA - OUTA 0xE9 - OUTA 0x02 ; Line mode, the way it was found. RSTA ; splitlint[redundant-assignment]: an exit status, not a mode SWI osExit diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index c2e5b83..816720c 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -870,6 +870,36 @@ too, and that is no use on a disk which has not got one. The directory is sixtee names, chosen rather than worked out: a scratch disk runs out of names long before it runs out of room, and this machine cannot divide. +### Giving The Screen Back: + +A program that takes the whole screen leaves the shell a blank one, and whatever was on it - +the listing you were reading, the error you were about to act on - is gone. There is nowhere +to put 48K of video memory on a machine with 64K of Data Memory that CosmOS is already living +in. + +**A drive made of memory is somewhere.** `SWI osTakeScreen` says *"I am about to use the whole +screen, and would like what is on it now put back when I exit."* The system writes video +memory to a file on the scratch drive and restores it from `handleExit`, alongside the vectors +and console mode it already puts back. Q is zero if that was arranged; a machine with no +volatile drive says no, and **a program told no should carry on regardless**, because it was +going to before this existed. + +**It is not automatic, and that is the point.** Saving on every program start would be cheap +enough, but restoring on every exit would be wrong: `dir`, `Files` and `Say` print and stop, +and their output is the reason you ran them. A program that says nothing behaves exactly as +every program did before this existed. + +**Tiles, map and palette all go** - 196 pages, and a block on the front holding the cursor, +the four scroll registers and the mode. The map's off-screen rows are the console's +scrollback; the tiles are the font, which a program that redefines one has overwritten; and +the palette is where the console's own colours live. `Grid` used to give back the map and not +the colours, and handed the shell green text on a blue ground. + +It also replaced thirty lines of `Grid`: four scroll registers put back by hand, the map +filled with spaces, the cursor sent home, and palette bank 0 written out - all of which was +*still* wrong, because the other fifteen banks kept `Grid`'s colours and there was nowhere to +have kept the real ones. + ### Where A Program Is Looked For: Three places, tried in order: @@ -939,6 +969,7 @@ Those numbers are written down once, in `Programs/CosmOS/Source/services.asm`, w | 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. | +| osTakeScreen | Says this program is about to use the whole screen and would like what is on it put back when it exits. Q is zero if that was arranged; anything else means it was not, which is the ordinary answer on a machine with no volatile drive. See Giving The Screen Back. | | 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. | diff --git a/Programs/CosmOS/Source/cosmos.asm b/Programs/CosmOS/Source/cosmos.asm index 6124339..d4a74b0 100644 --- a/Programs/CosmOS/Source/cosmos.asm +++ b/Programs/CosmOS/Source/cosmos.asm @@ -1840,6 +1840,373 @@ scriptNoName: ; ; SET WHEREVER A PROGRAM STARTS, of which there are two: run, and typing a program's name. ; Restored in handleExit, which is the one place they both come back through. +; ---- Putting the screen somewhere and getting it back ---- +; +; A program that takes the whole screen leaves the shell a blank one, and everything that was +; on it - the listing you were reading, the error you were about to act on - is gone. There is +; nowhere to put 32K on a machine with 64K of Data Memory that CosmOS is already living in. +; +; A DRIVE MADE OF MEMORY IS SOMEWHERE. The map goes to a file on the scratch drive like any +; other file, and comes back the same way; the filesystem does the allocating, and this had to +; invent nothing at all to have somewhere to put it. +; +; EVERYTHING A PROGRAM CAN DISTURB, which is more than the part on screen. The map's off +; screen rows are the console's scrollback; the tiles are the font, which a program that +; redefines one has overwritten; and the palette is where the console's own colours live - +; Grid could give back the map and not the colours, and handed the shell green text on blue. +; +; 192 pages of tiles and map, which are next to each other, then the four of palette. 196 in +; all, and a register block on the front. +; +; ---- Saved on being asked, restored on the way out ---- +; +; Saving on every program start would be cheap enough. Restoring on every exit would be +; WRONG: dir, Files and Say print and stop, and their output is the reason you ran them. +; So a program says it is taking the screen, and one that says nothing behaves exactly as +; every program did before this existed. +screenTake: + SETD.0 SbfsScratch1 + LDA.0 + INIB 0xFF + CCF + SUB + BRQ screenNoScratch ; No volatile drive, so nowhere to put it. + + ; Where the caller was, and where the file is going. + INA 0x24 + SETD.0 ScreenWasDrive + STA.0 + SETD.0 SbfsScratch1 + LDA.0 + CALL sbfsUse + + CALL screenBank + + ; A hundred and twenty nine blocks: one of registers, then the map. + SETD.0 SbfsFileBlocks + RSTA + STA.0 + INCD.0 + INIA 0d197 + STA.0 + SETD.0 SbfsFileTail + RSTA + STA.0 + SETD.0 ScreenFileName + CALL sbfsStreamStart + BNQ screenTakeFailed + + ; Block nought is where the screen was, rather than what was on it: the cursor, the four + ; scroll registers and the mode. A picture put back under a different origin is not the + ; picture that was taken. + CALL screenClearBlock + SETD.0 ScreenBlock + INA 0x03 + STA.0 + INCD.0 + INA 0x04 + STA.0 + INCD.0 + INA 0x34 + STA.0 + INCD.0 + INA 0x36 + STA.0 + INCD.0 + INA 0x37 + STA.0 + INCD.0 + INA 0x38 + STA.0 + INCD.0 + INA 0x31 + STA.0 + + RSTA + RSTB + SETD.2 SbfsIndex + STA.2 + INCD.2 + STB.2 + SETD.1 ScreenBlock + CALL sbfsStreamWrite + BNQ screenTakeFailed + + ; And the map, a block at a time through the one buffer there is. + RSTA + SETD.0 ScreenAt + STA.0 +screenTakeBlock: + SETD.0 ScreenAt + LDA.0 + CALL screenPageFor + MVQA + CALL screenFromVideo + + SETD.0 ScreenAt + LDA.0 + INCA + SETD.2 SbfsIndex + RSTB + STB.2 + INCD.2 + STA.2 ; Block n of the map is block n+1 of the file. + SETD.1 ScreenBlock + CALL sbfsStreamWrite + BNQ screenTakeFailed + + SETD.0 ScreenAt + LDA.0 + INCA + STA.0 + INIB 0d196 + CCF + SUB + BNQ screenTakeBlock + + SETD.0 SbfsFileBlocks + RSTA + STA.0 + INCD.0 + INIA 0d197 + STA.0 + SETD.0 SbfsFileTail + RSTA + STA.0 + CALL sbfsStreamDone + BNQ screenTakeFailed + + INIA 0x01 + SETD.0 ScreenSaved + STA.0 + CALL screenGoBack + RSTA + RSTB + CCF + ADD + RET + +screenTakeFailed: + RSTA + SETD.0 ScreenSaved + STA.0 + CALL screenGoBack +screenNoScratch: + RSTA + INIB 0d1 + CCF + ADD + RET + +; Whatever screenTake put away, put back. Nothing at all if it never ran. +screenGive: + SETD.0 ScreenSaved + LDA.0 + BRA screenGiveNone + + RSTA + STA.0 ; Once only: the next program takes its own. + INA 0x24 + SETD.0 ScreenWasDrive + STA.0 + SETD.0 SbfsScratch1 + LDA.0 + CALL sbfsUse + + CALL screenBank + + SETD.0 ScreenFileName + CALL fileLookup + BNQ screenGiveDone + + ; The map first, then the registers, so that nothing is drawn under an origin that is about + ; to change. + RSTA + SETD.0 ScreenAt + STA.0 +screenGiveBlock: + SETD.0 ScreenAt + LDA.0 + INCA + SETD.2 SbfsIndex + RSTB + STB.2 + INCD.2 + STA.2 + SETD.1 ScreenBlock + CALL sbfsReadOne + BNQ screenGiveDone + + SETD.0 ScreenAt + LDA.0 + CALL screenPageFor + MVQA + CALL screenToVideo + + SETD.0 ScreenAt + LDA.0 + INCA + STA.0 + INIB 0d196 + CCF + SUB + BNQ screenGiveBlock + + ; And where it was. + RSTA + RSTB + SETD.2 SbfsIndex + STA.2 + INCD.2 + STB.2 + SETD.1 ScreenBlock + CALL sbfsReadOne + BNQ screenGiveDone + + SETD.0 ScreenBlock + LDA.0 + OUTA 0x03 + INCD.0 + LDA.0 + OUTA 0x04 + INCD.0 + LDA.0 + OUTA 0x34 + INCD.0 + LDA.0 + OUTA 0x36 + INCD.0 + LDA.0 + OUTA 0x37 + INCD.0 + LDA.0 + OUTA 0x38 + INCD.0 + LDA.0 + OUTA 0x31 + +screenGiveDone: + CALL screenGoBack +screenGiveNone: + RET + +; Video memory as bank 4, which is what makes it reachable at all. Bank 3 is the disk's; see +; the table in the CosmOS README, which exists because a program once took 3. +screenBank: + INIA 0d4 + OUTA 0xE3 + INIA 0x30 + OUTA 0xE2 + INIA 0x03 + OUTA 0xE8 + RET + +; Back to the drive whoever called was standing on. +screenGoBack: + SETD.0 ScreenWasDrive + LDA.0 + CALL sbfsUse + RET + +; ---- Which page of video memory a saved block is ---- +; +; Nought to 191 are the tiles and the map, which sit next to each other from 0x0000. After +; that comes 16K of nothing, so 192 to 195 jump to 0xFC and are the palette. One sum rather +; than two loops, because two loops is two places to get the file's block numbers wrong. +screenPageFor: + INIB 0d192 + CCF + SUB + BRC screenPageDirect ; Borrowed, so it is below 192 and the page is the index. + MVQA + INIB 0xFC + CCF + ADD + RET +screenPageDirect: + RSTB + CCF + ADD + RET + +; A holds a page of video memory. Its 256 bytes come into ScreenBlock. +screenFromVideo: + PSHA + INIA 0d4 + OUTA 0xE0 + POPA + OUTA 0xE1 + RSTA + OUTA 0xE2 + INIA 0x01 + OUTA 0xE3 ; Into Data Memory. + CALL screenBufferDest + CALL screenLength + INIA 0x01 + OUTA 0xE8 + RET + +; A holds a page of video memory. ScreenBlock goes back into it. +screenToVideo: + PSHA + INIA 0d4 + OUTA 0xE3 + POPA + OUTA 0xE4 + RSTA + OUTA 0xE5 + INIA 0x01 + OUTA 0xE0 ; Out of Data Memory. + CALL screenBufferSource + CALL screenLength + INIA 0x01 + OUTA 0xE8 + RET + +; Where ScreenBlock is, told to the controller. A Data Pointer's two bytes cannot be read out +; of it, so it goes to memory first and comes back a byte at a time. +screenBufferDest: + SETD.1 ScreenBlockAt + SETD.0 ScreenBlock + STD.0.1 + LDA.1 + OUTA 0xE4 + INCD.1 + LDA.1 + OUTA 0xE5 + RET + +screenBufferSource: + SETD.1 ScreenBlockAt + SETD.0 ScreenBlock + STD.0.1 + LDA.1 + OUTA 0xE1 + INCD.1 + LDA.1 + OUTA 0xE2 + RET + +screenLength: + INIA 0x01 + OUTA 0xE6 + RSTA + OUTA 0xE7 ; 0x0100, which is one block. + RET + +; The block, emptied. Everything the register block below does not fill has to be nought, or +; a screen restored would carry whatever the last file read left in here. +screenClearBlock: + SETD.0 ScreenBlock + RSTB +screenClearByte: + RSTA + STA.0 + INCD.0 + DECB + BNB screenClearByte + RET + ; ---- run ---- ; ; Hands the machine to whatever was loaded. Where the Stack is now is written down first, @@ -2683,6 +3050,16 @@ bootSettleNo: ADD ; A is the answer, so Q becomes it. SRET +; The program is about to draw over everything. Q says whether what is there now will come +; back, and a program that is told no carries on anyway. +handleTakeScreen: + CALL screenTake + MVQA + MVSD.2 + DPUP.2 0d02 + STA.2 + RETI + handleExit: ; ---- What the program made of it ---- ; @@ -2695,6 +3072,9 @@ handleExit: SETD.1 LastStatus STA.1 + ; What was on the screen before this program had it, if it asked for that. + CALL screenGive + ; The drive the person was on, whatever the program did with it. PSHA SETD.1 RunDrive @@ -3909,6 +4289,8 @@ Separator: ; Where a program is looked for when it is not where you are. One fixed place rather than a ; list somebody sets, because a list would need somewhere to live between one boot and the ; next, and there is no such place yet. +ScreenFileName: +"sbfs.screen" AppsPrefix: "/Apps/" SystemAppsPrefix: @@ -4194,6 +4576,16 @@ SearchDrive: 0x00 RunDrive: 0x00 +ScreenSaved: + 0x00 +ScreenWasDrive: + 0x00 +ScreenAt: + 0x00 +ScreenBlockAt: + 0x00 0x00 +ScreenBlock: + #Reserve 0d256 FileCacheValid: 0x00 FileCacheName: @@ -4391,6 +4783,7 @@ CommandLine: osPrintNumber handlePrintNumber osBreak handleBreak osLastStatus handleLastStatus + osTakeScreen handleTakeScreen osBootState handleBootState osBootSettle handleBootSettle Device 0x20 diskDone diff --git a/Programs/CosmOS/Source/sbfs.asm b/Programs/CosmOS/Source/sbfs.asm index b728492..f1b83b2 100644 --- a/Programs/CosmOS/Source/sbfs.asm +++ b/Programs/CosmOS/Source/sbfs.asm @@ -284,6 +284,9 @@ sbfsMountAll: SETD.1 SbfsDrive STA.1 OUTA 0x24 ; Drive 0, whatever the controller was left on. + INIA 0xFF + SETD.1 SbfsScratch1 + STA.1 ; No scratch drive until one is found. ; The live record belongs to nobody yet, so every slot starts empty and a drive that fails ; to mount keeps an empty one. @@ -347,6 +350,26 @@ sbfsMountEach: BNQ sbfsMountNext sbfsMountGot: + ; ---- And whether this is the one to keep scratch on ---- + ; + ; A volatile drive is somewhere the system may write without asking, because nothing on it + ; outlives the machine. The FIRST one found is the scratch drive; a machine with two has + ; made a decision nobody expressed, and taking the lower number is at least predictable. + INA 0x26 + INIB 0x01 + AND + BRQ sbfsMountNotScratch + SETD.1 SbfsScratch1 + LDA.1 + INIB 0xFF + CCF + SUB + BNQ sbfsMountNotScratch ; There is one already. + SETD.1 SbfsDriveAt + LDA.1 + SETD.1 SbfsScratch1 + STA.1 +sbfsMountNotScratch: ; Mounted: remember which, and put the live record where it belongs. SETD.1 SbfsDriveAt @@ -3661,6 +3684,10 @@ SbfsDriveCount: 0x00 SbfsDriveAt: 0x00 +; Which drive the system may write scratch to, or 0xFF for a machine with none. It is a drive +; the machine called volatile, so nothing written there was ever going to survive anyway. +SbfsScratch1: + 0xFF SbfsPathWanted: 0x00 diff --git a/Programs/CosmOS/Source/services.asm b/Programs/CosmOS/Source/services.asm index 425340f..38cce47 100644 --- a/Programs/CosmOS/Source/services.asm +++ b/Programs/CosmOS/Source/services.asm @@ -167,3 +167,14 @@ ; 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 + +; ---- Taking the screen, and giving it back ---- +; +; Says that this program is about to use the whole screen and would like what is on it now put +; back when it exits. Q is zero if that was arranged; anything else means it was not, which is +; the ordinary answer on a machine with no volatile drive to keep it on - and a program told +; no should carry on regardless, because it was going to before this existed. +; +; NOT AUTOMATIC, and that is the point: dir and Say print and stop, and their output is the +; reason you ran them. Only a program that says it took the screen gets the screen put back. + osTakeScreen 0d36 diff --git a/Tests/expected/cosmosCrossDisk.out b/Tests/expected/cosmosCrossDisk.out index 77d9c60..faf3578 100644 --- a/Tests/expected/cosmosCrossDisk.out +++ b/Tests/expected/cosmosCrossDisk.out @@ -12,7 +12,7 @@ Snake.sbx 2164 Keys.sbx 664 Say.sbx 156 Break.sbx 149 -Grid.sbx 543 +Grid.sbx 479 notes.txt 21 Apps