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
+1 -1
View File
@@ -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 <dir>
hi.script 121
+1 -1
View File
@@ -7,7 +7,7 @@ Snake.sbx 2164
Keys.sbx 664
Say.sbx 156
Break.sbx 149
Grid.sbx 543
Grid.sbx 479
notes.txt 21
Apps <dir>
hi.script 121
+2 -2
View File
@@ -1,7 +1,7 @@
CosmOS
> it says: before Grid
finished
> finished
> finished
> > it says: after Grid
finished
> greet.sbx 211
@@ -11,7 +11,7 @@ Snake.sbx 2164
Keys.sbx 664
Say.sbx 156
Break.sbx 149
Grid.sbx 543
Grid.sbx 479
notes.txt 21
Apps <dir>
hi.script 121
+1 -1
View File
@@ -7,7 +7,7 @@ Snake.sbx 2164
Keys.sbx 664
Say.sbx 156
Break.sbx 149
Grid.sbx 543
Grid.sbx 479
notes.txt 21
Apps <dir>
hi.script 121
+1 -1
View File
@@ -6,7 +6,7 @@ Snake.sbx 2164
Keys.sbx 664
Say.sbx 156
Break.sbx 149
Grid.sbx 543
Grid.sbx 479
notes.txt 21
Apps <dir>
hi.script 121
+47
View File
@@ -0,0 +1,47 @@
# Two screens, compared a cell at a time: is the second the first, restored?
#
# Cells rather than pixels, and "ink or no ink" rather than colour, because what is being
# checked is that the TEXT came back - a restore that got the map right and the palette wrong
# would still be a restore that lost the colours, and that is checked by the colours being
# the console's own elsewhere.
#
# THE ROWS AROUND THE CURSOR ARE ALLOWED TO DIFFER, and they are found rather than guessed at.
# The screen is given back and THEN the shell carries on writing to it - a "finished" and a
# prompt, two or three lines - so the bottom of what was there is where they land. The cursor
# is not near the bottom of the SCREEN, so "the last few rows of the screen" is the wrong
# exemption and let a real difference through the first time this ran.
#
# Everything above that is what the restore is judged on, which here is 25 rows of 28.
import sys
def cells(path):
data = open(path, "rb").read()
fields = data.split(b"\n", 3)
width, height = (int(n) for n in fields[1].split())
body = fields[3]
out = []
for cy in range(height // 8):
row = ""
for cx in range(width // 8):
inked = any(
body[((cy * 8 + j) * width + cx * 8 + i) * 3:
((cy * 8 + j) * width + cx * 8 + i) * 3 + 3] != b"\x00\x00\x00"
for j in range(8) for i in range(8))
row += "#" if inked else "."
out.append(row)
return out
before, after = cells(sys.argv[1]), cells(sys.argv[2])
if len(before) != len(after):
print("the screens are different sizes")
sys.exit(0)
differ = [n for n, (b, a) in enumerate(zip(before, after)) if b != a]
inked = [n for n, row in enumerate(before) if "#" in row]
settled = (max(inked) - 3) if inked else 0
early = [n for n in differ if n < settled]
if early:
print("row %d is not what it was" % early[0])
else:
print("yes")
+22
View File
@@ -838,6 +838,28 @@ else
result no "a program drew a grid of its own tile" "no picture came out"
fi
# ---- A program gives the screen back ----
#
# Grid takes the whole screen: it redefines a tile, writes all sixteen colour schemes over the
# console's own, and fills every cell of the map. Then it asks the system for what was there
# before, and the system has somewhere to put it because the machine has a drive made of
# memory.
#
# WHAT IS COMPARED IS THE SCREEN BEFORE AGAINST THE SCREEN AFTER, cell by cell. Checking that
# it merely looks like text would pass on a restore that put back somebody else's text, and
# checking a few pixels would pass on one that got the palette right and the map wrong.
python3 -c "open('$BUILD/before.keys','wb').write(b'dir\n' + b'Say a line to come back to\n' + b'\x00'*200)"
python3 -c "open('$BUILD/after.keys','wb').write(b'dir\n' + b'Say a line to come back to\n' + b'Grid\n' + b'\x00'*600 + b'q' + b'\x00'*200)"
for phase in before after; do
timeout 30 "$EMU" --fast --cycles 200000000 --keyboard "$BUILD/$phase.keys" \
--screen "$BUILD/$phase.ppm" --disk "$ROOT/Tests/build/disks/cosmos.img" \
--ram-disk 2048 "$BUILD/cosmos.bin" > "$BUILD/$phase.out" 2>&1 || true
done
SAME="$(python3 "$ROOT/Tests/samescreen.py" "$BUILD/before.ppm" "$BUILD/after.ppm")"
[ "$SAME" = "yes" ] \
&& result ok "a program gives the screen back" "every row it did not write on is as it was" \
|| result no "a program gives the screen back" "$SAME"
# ---- Clearing puts the cursor back at the top ----
#
# A screen with nothing on it and a cursor half way down it is not a cleared screen. This