Give the screen back: osTakeScreen, and the RAM disk earns its keep

A program that takes the whole screen leaves the shell a blank one, and
whatever was on it is gone. There was nowhere to put 48K of video memory on
a machine with 64K of Data Memory that CosmOS already lives in.

A DRIVE MADE OF MEMORY IS SOMEWHERE. The screen goes to a file on the
scratch drive - the first volatile drive found at boot - like any other
file, and comes back from handleExit alongside the vectors and console mode
already put back there. The filesystem does the allocating, so this
invented nothing: it is 196 pages of tiles, map and palette, with a block
on the front holding the cursor, the four scroll registers and the mode.

NOT AUTOMATIC, and that is the whole design. Saving on every program start
would be cheap enough; restoring on every exit would be wrong, because dir
and Files and Say print and stop and their output is the reason you ran
them. A program says it took the screen, and one that says nothing behaves
exactly as every program did before this existed.

It deleted thirty lines of Grid, and they were all wrong anyway: four
scroll registers put back by hand, the map filled with spaces, the cursor
sent home, palette bank 0 written out - and the other fifteen banks kept
Grid's colours, because there was nowhere to have kept the real ones. Grid
is 64 bytes smaller and gives back what was actually there.

The check compares the screen before against the screen after, CELL BY
CELL, and allows only the rows around the cursor to differ - found from
where the text ends rather than guessed at, because the first version
assumed the cursor was near the bottom of the screen and let three real
differences through.

Two things cost time and neither was the feature:

  - An edit adding "SWI osTakeScreen" to Grid was in the same script as a
    failing s.index, so the file was never written - and the COMMENT
    describing the call did land, from a later edit. Grid documented a call
    it did not make, and read as though it should have worked.
  - docs.sh caught osTakeScreen having no row in the services table, which
    is the check the service layer added for exactly this and the second
    time it has earned itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-08-31 18:47:44 -04:00
co-authored by Claude Opus 5
parent 04f1ffabd4
commit ab72443b99
12 changed files with 551 additions and 71 deletions
+393
View File
@@ -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