diff --git a/Programs/CosmOS/Apps/Flip.asm b/Programs/CosmOS/Apps/Flip.asm index 18553a4..d2f1f81 100644 --- a/Programs/CosmOS/Apps/Flip.asm +++ b/Programs/CosmOS/Apps/Flip.asm @@ -28,18 +28,11 @@ #Base 0x5000 start: - ; ---- The two banks this touches ---- + ; ---- The one bank this touches ---- ; - ; Four is the atlas, where the tiles are. Six is the other screen: three is the disk's and - ; four and five are the ones the system registers to save a screen with, so six is the - ; first number free. Nothing hands these out - see the table in the CosmOS README. - INIA 0d4 - OUTA 0xE3 - INIA 0x30 - OUTA 0xE2 - INIA 0x03 - OUTA 0xE8 - + ; Six: three is the disk's and four and five are the ones the system registers to save a + ; screen with, so six is the first number free. Nothing hands these out - see the table in + ; the CosmOS README. INIA 0d6 OUTA 0xE3 INIA 0x3B @@ -47,50 +40,57 @@ start: INIA 0x03 OUTA 0xE8 - ; The screen back afterwards, because the tile overwritten below is one the shell spells - ; with. Refused is survivable and is not checked: there is nothing this could do about it, - ; and the system puts the font back at exit whether or not it saved anything. - SWI osTakeScreen + ; ---- And it does NOT take the screen ---- + ; + ; osTakeScreen saves what is on the screen and puts it back at exit, and this program + ; called it at first. That was wrong in a way worth keeping written down: it saves the + ; screen AS IT WAS BEFORE, so the line printed below - the line whose whole job is to still + ; be there afterwards - was wiped out by the restore that was meant to be tidy. + ; + ; It was not needed either. NOTHING THIS TOUCHES IS THE SHELL'S: the only memory it writes + ; is the map of the screen nobody is looking at, and the only register it leaves changed is + ; which screen that is - which the system puts back itself. + ; + ; A program that wants a screen saved should ask. A program that damages nothing should + ; not, and asking anyway is not free - it costs the screen it was standing on. SETD.0 Message SWI osPrintString + INIA 0x0A + OUTA 0x00 ; The assembler has no escapes; a newline is a byte. ; Key mode, so a key arrives when it is pressed rather than when Return is. INIA 0x01 OUTA 0x02 - ; ---- Tile one, made solid ---- + ; ---- Every cell of the other screen, in one command ---- ; - ; Sixty four pixels of index one, FILLED rather than blitted, which is why this program - ; carries no picture of its own. - INIA 0d4 - OUTA 0xE3 - RSTA - OUTA 0xE4 - INIA 0x40 - OUTA 0xE5 ; Tile one begins at sixty four. - INIA 0x01 - OUTA 0xE2 ; Fill takes the byte it writes from SourceLow. - RSTA - OUTA 0xE6 - INIA 0x40 - OUTA 0xE7 ; Sixty four bytes of it. - INIA 0x02 - OUTA 0xE8 - - ; ---- And every cell of the other screen ---- + ; TEN, and the reason is worth the paragraph. Fill writes one byte, and a cell is two, so + ; whatever is filled with is BOTH the tile and the attribute - there is no filling a map + ; with a tile and a colour that are different numbers. ; - ; One byte fills both halves of a cell, so this is tile one and attribute one everywhere: - ; the tile is index one at every pixel and the attribute adds sixteen to all of them, which - ; comes out as a screen solidly in scheme one's ink. + ; Ten as a tile is an asterisk, because the font begins at the space and glyph n is + ; character n plus thirty two. Ten as an attribute is one of the eight REVERSED schemes, + ; whose paper is a colour and whose ink is black. So the screen comes out green, covered in + ; black asterisks, without a single tile being redefined. + ; + ; The high nibble stays at nought, which the manual asks for: it is reserved. That is the + ; real constraint on which byte this can be - it has to be under sixteen to leave the + ; nibble alone, and eight or over to land on a reversed scheme whose PAPER is the colour. + ; + ; Which means THIS PROGRAM WRITES NOTHING BUT THE SCREEN NOBODY IS LOOKING AT. It was + ; drawn with a tile of its own first, and that tile was one the system copies the font back + ; over at every exit - so the screen it had filled turned blank the moment it left, and a + ; check that it had been put back could not tell a restored screen from an abandoned one. + ; A picture that depends on the atlas does not survive leaving. INIA 0d6 OUTA 0xE3 INIA 0x40 OUTA 0xE4 RSTA OUTA 0xE5 - INIA 0x01 - OUTA 0xE2 + INIA 0x0A + OUTA 0xE2 ; Fill takes the byte it writes from SourceLow. INIA 0x80 OUTA 0xE6 RSTA @@ -141,5 +141,8 @@ waitKey: #Base 0x3000 +; One line and no newline in it. A string literal is terminated where it ends, so a second +; one after a 0x0A byte would never be reached - printing stops at the first terminator. The +; newline is written to the console directly instead. Message: -"A whole screen is about to be drawn where you cannot see it - this line will still\nbe here when it is done. Press a key to show it, and another to come back.\n" +"A screen is drawn where you cannot see it. A key shows it, another comes back." diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index 39d1211..4288fad 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -725,7 +725,7 @@ from every assembly file in it. Several are old programs written for the bare ma | 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. | | Grid | The first program to use the screen as a screen. Redefines a tile above the font, fills all 128 map rows, and scrolls it diagonally a pixel at a time. | -| Flip | Draws a whole screen into the bank that is not being shown, waits, and then shows it in one byte out of one port. It deliberately does not put the displayed screen back, because that is the system's to restore - a program that faulted while flipped could not have. | +| Flip | Draws a whole screen into the bank that is not being shown, waits, and then shows it in one byte out of one port. It writes nothing else at all - not a tile, not a colour - so it does not ask for the screen to be saved, and the line it printed is still there when it comes back. It deliberately does not put the displayed screen back either, because that is the system's to restore: a program that faulted while flipped could not have. | | 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. | | Type | Prints a named text file a block at a time, including one too large to fit in Data Memory. | diff --git a/SplitBit Test Manual.md b/SplitBit Test Manual.md index 5070174..552ce1e 100644 --- a/SplitBit Test Manual.md +++ b/SplitBit Test Manual.md @@ -79,7 +79,7 @@ from `make`, not from here. ### 1. Recorded output `Tests/run.sh` assembles each program named in `Tests/manifest`, runs it, and compares -everything it printed against a file in `Tests/expected`. 203 tests, of which 141 run, 35 +everything it printed against a file in `Tests/expected`. 204 tests, of which 142 run, 35 only assemble, 16 are expected to fail to assemble, and 11 boot from ROM with no image given at all. diff --git a/Tests/expected/cosmosCrossDisk.out b/Tests/expected/cosmosCrossDisk.out index bf72de1..fb61fe9 100644 --- a/Tests/expected/cosmosCrossDisk.out +++ b/Tests/expected/cosmosCrossDisk.out @@ -22,7 +22,7 @@ Break.sbx 149 Grid.sbx 571 Press.sbx 872 Mode.sbx 48 -Flip.sbx 291 +Flip.sbx 173 Crash.sbx 632 vars.script 50 blocks.script 343 diff --git a/Tests/expected/cosmosDrives.out b/Tests/expected/cosmosDrives.out index 5d22b1d..3227acc 100644 --- a/Tests/expected/cosmosDrives.out +++ b/Tests/expected/cosmosDrives.out @@ -12,7 +12,7 @@ Break.sbx 149 Grid.sbx 571 Press.sbx 872 Mode.sbx 48 -Flip.sbx 291 +Flip.sbx 173 Crash.sbx 632 vars.script 50 blocks.script 343 diff --git a/Tests/expected/cosmosFault.out b/Tests/expected/cosmosFault.out index 4040d16..64868e0 100644 --- a/Tests/expected/cosmosFault.out +++ b/Tests/expected/cosmosFault.out @@ -29,7 +29,7 @@ Break.sbx 149 Grid.sbx 571 Press.sbx 872 Mode.sbx 48 -Flip.sbx 291 +Flip.sbx 173 Crash.sbx 632 vars.script 50 blocks.script 343 diff --git a/Tests/expected/cosmosFlip.out b/Tests/expected/cosmosFlip.out new file mode 100644 index 0000000..1162360 --- /dev/null +++ b/Tests/expected/cosmosFlip.out @@ -0,0 +1,42 @@ +CosmOS +> Say before Flip +it says: before Flip +finished +> Flip +A screen is drawn where you cannot see it. A key shows it, another comes back. +finished +> Say after Flip +it says: after Flip +finished +> dir +greet.sbx 211 +hello.sbx 53 +Life.sbx 1396 +Snake.sbx 2164 +Keys.sbx 664 +Say.sbx 156 +Break.sbx 149 +Grid.sbx 571 +Press.sbx 872 +Mode.sbx 48 +Flip.sbx 173 +Crash.sbx 632 +vars.script 50 +blocks.script 343 +loops.script 272 +tune.sbx 306 +notes.txt 21 +Apps +hi.script 121 +bad.script 45 +plain.script 24 +cross.script 280 +nonl.script 38 +outer.script 376 +inner.script 44 +loop.script 35 +25 files, 1 directory +> exit +halted +Execution halted. +[exit 0] diff --git a/Tests/expected/cosmosGrid.out b/Tests/expected/cosmosGrid.out index ca906bc..014eb78 100644 --- a/Tests/expected/cosmosGrid.out +++ b/Tests/expected/cosmosGrid.out @@ -19,7 +19,7 @@ Break.sbx 149 Grid.sbx 571 Press.sbx 872 Mode.sbx 48 -Flip.sbx 291 +Flip.sbx 173 Crash.sbx 632 vars.script 50 blocks.script 343 diff --git a/Tests/expected/cosmosMonitor.out b/Tests/expected/cosmosMonitor.out index 7d27726..7e82e35 100644 --- a/Tests/expected/cosmosMonitor.out +++ b/Tests/expected/cosmosMonitor.out @@ -105,7 +105,7 @@ Break.sbx 149 Grid.sbx 571 Press.sbx 872 Mode.sbx 48 -Flip.sbx 291 +Flip.sbx 173 Crash.sbx 632 vars.script 50 blocks.script 343 diff --git a/Tests/expected/cosmosMonitorRun.out b/Tests/expected/cosmosMonitorRun.out index 01734a2..a04e0b9 100644 --- a/Tests/expected/cosmosMonitorRun.out +++ b/Tests/expected/cosmosMonitorRun.out @@ -24,7 +24,7 @@ Break.sbx 149 Grid.sbx 571 Press.sbx 872 Mode.sbx 48 -Flip.sbx 291 +Flip.sbx 173 Crash.sbx 632 vars.script 50 blocks.script 343 diff --git a/Tests/expected/cosmosRun.out b/Tests/expected/cosmosRun.out index 1d524c0..0adb628 100644 --- a/Tests/expected/cosmosRun.out +++ b/Tests/expected/cosmosRun.out @@ -12,7 +12,7 @@ Break.sbx 149 Grid.sbx 571 Press.sbx 872 Mode.sbx 48 -Flip.sbx 291 +Flip.sbx 173 Crash.sbx 632 vars.script 50 blocks.script 343 diff --git a/Tests/expected/cosmosSlowDisk.out b/Tests/expected/cosmosSlowDisk.out index a821e5e..c459036 100644 --- a/Tests/expected/cosmosSlowDisk.out +++ b/Tests/expected/cosmosSlowDisk.out @@ -10,7 +10,7 @@ Break.sbx 149 Grid.sbx 571 Press.sbx 872 Mode.sbx 48 -Flip.sbx 291 +Flip.sbx 173 Crash.sbx 632 vars.script 50 blocks.script 343 diff --git a/Tests/input/cosmosFlip.in b/Tests/input/cosmosFlip.in new file mode 100644 index 0000000..16cc028 --- /dev/null +++ b/Tests/input/cosmosFlip.in @@ -0,0 +1,5 @@ +Say before Flip +Flip +abSay after Flip +dir +exit diff --git a/Tests/manifest b/Tests/manifest index 5f605f7..c2f68d0 100644 --- a/Tests/manifest +++ b/Tests/manifest @@ -968,6 +968,15 @@ cosmosStartupBad | CosmOS/Source/cosmos.asm | run | cosmosSta # So this runs a program by name, then Grid, then the same program again. The second one is # the check. dir at the end says the disk is still there to be read. cosmosGrid | CosmOS/Source/cosmos.asm | run | cosmosGrid.in | 60000000 | disks/cosmos.img +# ---- The screen nobody is looking at ---- +# +# Flip draws a whole screen into the bank that is not shown. What is checked here is not the +# picture - video.sh has that - but the LINE IT PRINTS, which is the thing the program claims +# will still be there when it comes back. It was not, twice over: the assembler has no string +# escapes, so a "\n" written in a literal printed as two characters; and the program called +# osTakeScreen, which restores the screen as it was BEFORE the line was printed and so wiped +# out the one thing it was pointing at. A program that damages nothing should not ask. +cosmosFlip | CosmOS/Source/cosmos.asm | run | cosmosFlip.in | 60000000 | disks/cosmos.img # Which disk the registers mean. Several disks are one controller with a drive register # rather than several devices, because a port is an immediate byte inside the instruction # that names it and a program cannot compute one. Run with a single disk, so drive 1 is a diff --git a/Tests/video.sh b/Tests/video.sh index 4934f61..65968b6 100755 --- a/Tests/video.sh +++ b/Tests/video.sh @@ -85,6 +85,21 @@ ASM # bitmap pixel 5 are both address 0x0005. So every write below names the memory it is for, # and there is deliberately no bare poke that picks by address - a helper that guessed would # be right for the tiles and wrong for a picture, silently. +# The colour most of a picture is made of. Asked this way rather than by naming a pixel, +# because a filled screen is a PATTERN - the font has one blank glyph and it is the space, +# whose attribute nibble is nought - and which pixel lands on paper depends on the shape of +# whichever character was filled with. +commonest() { + # commonest + python3 -c " +import collections, sys +d = open(sys.argv[1], 'rb').read() +px = d[d.index(b'255\n') + 4:] +counts = collections.Counter(px[o:o + 3] for o in range(0, len(px), 3)) +print(counts.most_common(1)[0][0].hex()) +" "$1" 2>/dev/null || echo none +} + pokeTo() { # pokeTo
printf ' INIA 0d%d\n OUTA 0xE3\n INIA 0x%02X\n OUTA 0xE4\n INIA 0x%02X\n OUTA 0xE5\n INIA 0x%02X\n OUTA 0xE9\n' \ @@ -1014,33 +1029,30 @@ python3 -c "open('$BUILD/flip.keys','wb').write(b'Flip\n' + b'\x00'*3000 + b' ' timeout 30 "$EMU" --fast --cycles 200000000 --keyboard "$BUILD/flip.keys" \ --screen "$BUILD/flip.ppm" --disk "$ROOT/Tests/build/disks/cosmos.img" \ --ram-disk 2048 "$BUILD/cosmos.bin" > "$BUILD/flip.out" 2>&1 || true -FLIPPED="$(python3 -c " -d = open('$BUILD/flip.ppm', 'rb').read() -px = d[d.index(b'255\n') + 4:] -print(len({px[o:o + 3] for o in range(0, len(px), 3)})) -" 2>/dev/null || echo 0)" -[ "$FLIPPED" = "1" ] \ - && result ok "a program shows the other screen" "the whole picture is the one it filled" \ - || result no "a program shows the other screen" "$FLIPPED colours, not a filled screen" +FLIPPED="$(commonest "$BUILD/flip.ppm")" +[ "$FLIPPED" = "50c050" ] \ + && result ok "a program shows the other screen" "green, which is the paper it filled with" \ + || result no "a program shows the other screen" "commonest colour $FLIPPED, not the fill" # ---- And the system takes it back ---- # # The shell's scrollback, its prompt and every line the person typed are in screen NOUGHT. # A program that exited while showing screen one would hand back a shell drawing perfectly -# onto a screen nobody had ever written to - blank, and looking for all the world like the -# machine had lost everything. Compared cell by cell against the same session without Flip -# in it, which is the same way Grid's restore is checked. -python3 -c "open('$BUILD/flipbefore.keys','wb').write(b'dir\n' + b'Say a line to come back to\n' + b'\x00'*200)" -python3 -c "open('$BUILD/flipafter.keys','wb').write(b'dir\n' + b'Say a line to come back to\n' + b'Flip\n' + b'\x00'*3000 + b' ' + b'\x00'*3000 + b' ' + b'\x00'*3000)" -for phase in flipbefore flipafter; 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 -BACK="$(python3 "$ROOT/Tests/samescreen.py" "$BUILD/flipbefore.ppm" "$BUILD/flipafter.ppm")" -[ "$BACK" = "yes" ] \ - && result ok "and the system puts the screen back" "screen nought, with what was on it" \ - || result no "and the system puts the screen back" "$BACK" +# onto a screen nobody had ever written to, and Flip does exit while flipped - deliberately, +# because a program that FAULTED while flipped could not put it back either. +# +# What says so is a corner of the screen with nothing on it. The map Flip filled is one tile +# and one attribute in every cell, so a screen still showing it is that colour EVERYWHERE; a +# screen nought that came back is black where nobody has printed. Checking a corner rather +# than comparing whole pictures, because Flip's own line is meant to survive on this one and +# an equality check would call that a difference. +python3 -c "open('$BUILD/flipafter.keys','wb').write(b'Say a line to come back to\n' + b'Flip\n' + b'\x00'*3000 + b' ' + b'\x00'*3000 + b' ' + b'\x00'*3000)" +timeout 30 "$EMU" --fast --cycles 200000000 --keyboard "$BUILD/flipafter.keys" \ + --screen "$BUILD/flipafter.ppm" --disk "$ROOT/Tests/build/disks/cosmos.img" \ + --ram-disk 2048 "$BUILD/cosmos.bin" > "$BUILD/flipafter.out" 2>&1 || true +[ "$(commonest "$BUILD/flipafter.ppm")" = "000000" ] \ + && result ok "and the system puts the screen back" "black again, and not the filled screen" \ + || result no "and the system puts the screen back" "commonest colour $(commonest "$BUILD/flipafter.ppm")" # ---- Clearing puts the cursor back at the top ---- #