diff --git a/Programs/CosmOS/Apps/Mode.asm b/Programs/CosmOS/Apps/Mode.asm new file mode 100644 index 0000000..feb4a9e --- /dev/null +++ b/Programs/CosmOS/Apps/Mode.asm @@ -0,0 +1,50 @@ +; Forty columns or eighty, whichever the screen is not in. +; +; A machine wakes up in the forty column mode and CosmOS asks for eighty, because that is +; what its own output was written for. A game is the other way round: Snake on a forty column +; screen is the same board drawn twice the size, which is what a person sitting in front of +; it actually wants. +; +; ---- The smallest program this system can load ---- +; +; Ten instructions and NO DATA AT ALL, which is not a curiosity: it is the shape that found +; a bug in the loader. Every program written for CosmOS until this one had something in its +; Data Segment, so the loader had never been asked to move a segment of no bytes - and a +; length of zero asks the memory controller for the whole 64K, which does not fit, which it +; refused, which stopped the machine in the middle of loading. On a terminal that printed a +; fault. Behind a window it looked exactly like a hang. +; +; So this is kept dataless on purpose. The two digits it prints are put in a register one at +; a time rather than being a string, which is the only reason it can say anything at all. +; +; Written by Anachronaut + +#Include services.asm + +#Program + + #Base 0x4000 + +start: + INA 0x31 ; Which mode the screen is in now. + BRA modeWide ; Nought is the forty column one, so go the other way. + + ; Anything else becomes forty, and that deliberately includes bitmap mode. A program that + ; left the screen with no text on it left nowhere to print, so coming back to a mode that + ; has characters in it is more use than refusing. + RSTA + OUTA 0x31 + INIA 0x34 ; '4' + BRI modeSay +modeWide: + INIA 0x01 + OUTA 0x31 + INIA 0x38 ; '8' +modeSay: + OUTA 0x00 + INIA 0x30 ; '0' + OUTA 0x00 + INIA 0x0A + OUTA 0x00 + RSTA + SWI osExit diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index 399d274..b0bdc58 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -572,6 +572,8 @@ from every assembly file in it. Several are old programs written for the bare ma | Compare | Compares two files a block at a time, stopping at their real tails rather than comparing unused bytes in the final disk blocks. | | Wander | Goes to the directory it is given and reads a file there by a bare name. The only thing that moves the machine from inside a program, and so the only thing that can check the shell puts the working directory back afterwards. | | More | A forward-only pager. Space advances a screen, Return one line, and q stops. | +| Press | Says what the console handed it, in hexadecimal and by name. It reads a line and then keys, because the keys that are not characters are dropped in line mode and delivered in key mode, and both halves of that rule want showing. | +| Mode | Forty columns or eighty, whichever the screen is not in. Ten instructions and no data at all, which is the point of it: it is the smallest shape a loadable program can take, and the loader used to stop the machine dead on one. | ### The Monitor: diff --git a/Programs/CosmOS/Source/cosmos.asm b/Programs/CosmOS/Source/cosmos.asm index ceb19a0..9388e59 100644 --- a/Programs/CosmOS/Source/cosmos.asm +++ b/Programs/CosmOS/Source/cosmos.asm @@ -1548,11 +1548,29 @@ loadVersionKnown: LDA.0 OUTA 0xE6 INCD.0 - LDA.0 - OUTA 0xE7 + LDB.0 + OUTB 0xE7 + + ; ---- Nothing to move is not the same as everything to move ---- + ; + ; A LENGTH OF ZERO ASKS THE CONTROLLER FOR THE WHOLE 64K, which is the machine's rule and + ; a sensible one for a length somebody typed: two bytes cannot say 65536, and a transfer + ; of no bytes is not usually what anybody meant. It is exactly what is meant here. A + ; segment can genuinely be empty - a five instruction program that only writes to a port + ; has no data at all - and this asked to move 65536 bytes into a bank that has not got + ; them, so the controller refused and the machine stopped in the middle of loading. + ; + ; The header says how long the segment is, so the loader knows before it asks. It has + ; both bytes of the length in hand here, so testing them costs one instruction. + ; + ; NOTHING IS LOST BY SKIPPING IT. A blit leaves the controller's addresses past whatever + ; it touched, and a blit of nothing would have left them exactly where they are. + OR + BRQ loadNoCode INIA 0x01 OUTA 0xE8 ; Blit. +loadNoCode: ; Then the data. A blit leaves its addresses past whatever it touched, so the source is ; already sitting on the first byte of the data and only the destination changes. @@ -1571,11 +1589,16 @@ loadVersionKnown: LDA.0 OUTA 0xE6 INCD.0 - LDA.0 - OUTA 0xE7 + LDB.0 + OUTB 0xE7 + + ; And the same for the data, which is the segment that is actually empty in practice. + OR + BRQ loadNoData INIA 0x01 OUTA 0xE8 ; Blit. +loadNoData: ; ---- The vectors it brought ---- ; diff --git a/SplitBit Test Manual.md b/SplitBit Test Manual.md index 0ee7f1a..35693f2 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`. 189 tests, of which 127 run, 35 +everything it printed against a file in `Tests/expected`. 190 tests, of which 128 run, 35 only assemble, 16 are expected to fail to assemble, and 11 boot from ROM with no image given at all. @@ -443,7 +443,7 @@ than reading all of them: ## The Lint Baseline: `Tests/lint-baseline.txt` records how many warnings of each rule SplitLint finds in each -file across the whole corpus - 34 file-and-rule pairs. It is checked on every run, and it +file across the whole corpus - 33 file-and-rule pairs. It is checked on every run, and it is checked **in both directions.** A new warning appearing is a regression. A recorded warning *disappearing* is also diff --git a/Tests/expected/cosmosCrossDisk.out b/Tests/expected/cosmosCrossDisk.out index b999df1..28d1470 100644 --- a/Tests/expected/cosmosCrossDisk.out +++ b/Tests/expected/cosmosCrossDisk.out @@ -21,6 +21,7 @@ Say.sbx 156 Break.sbx 149 Grid.sbx 559 Press.sbx 872 +Mode.sbx 48 notes.txt 21 Apps hi.script 121 @@ -32,7 +33,7 @@ outer.script 376 inner.script 44 loop.script 35 crossed.txt 560 -19 files, 1 directory +20 files, 1 directory > exit halted Execution halted. diff --git a/Tests/expected/cosmosDrives.out b/Tests/expected/cosmosDrives.out index ce6aef6..7074d1e 100644 --- a/Tests/expected/cosmosDrives.out +++ b/Tests/expected/cosmosDrives.out @@ -11,6 +11,7 @@ Say.sbx 156 Break.sbx 149 Grid.sbx 559 Press.sbx 872 +Mode.sbx 48 notes.txt 21 Apps hi.script 121 @@ -21,7 +22,7 @@ nonl.script 38 outer.script 376 inner.script 44 loop.script 35 -18 files, 1 directory +19 files, 1 directory > drive 1 > dir other.txt 28 diff --git a/Tests/expected/cosmosGrid.out b/Tests/expected/cosmosGrid.out index 9ace0d8..c50b955 100644 --- a/Tests/expected/cosmosGrid.out +++ b/Tests/expected/cosmosGrid.out @@ -18,6 +18,7 @@ Say.sbx 156 Break.sbx 149 Grid.sbx 559 Press.sbx 872 +Mode.sbx 48 notes.txt 21 Apps hi.script 121 @@ -28,7 +29,7 @@ nonl.script 38 outer.script 376 inner.script 44 loop.script 35 -18 files, 1 directory +19 files, 1 directory > exit halted Execution halted. diff --git a/Tests/expected/cosmosNoData.out b/Tests/expected/cosmosNoData.out new file mode 100644 index 0000000..4653380 --- /dev/null +++ b/Tests/expected/cosmosNoData.out @@ -0,0 +1,15 @@ +CosmOS +> Mode +40 +finished +> echo a narrow screen +a narrow screen +> Mode +80 +finished +> echo and a wide one again +and a wide one again +> exit +halted +Execution halted. +[exit 0] diff --git a/Tests/expected/cosmosRun.out b/Tests/expected/cosmosRun.out index 43b9382..637c635 100644 --- a/Tests/expected/cosmosRun.out +++ b/Tests/expected/cosmosRun.out @@ -11,6 +11,7 @@ Say.sbx 156 Break.sbx 149 Grid.sbx 559 Press.sbx 872 +Mode.sbx 48 notes.txt 21 Apps hi.script 121 @@ -21,7 +22,7 @@ nonl.script 38 outer.script 376 inner.script 44 loop.script 35 -18 files, 1 directory +19 files, 1 directory > load load what? > load nosuch.sbx diff --git a/Tests/expected/cosmosSlowDisk.out b/Tests/expected/cosmosSlowDisk.out index e63e057..57cc512 100644 --- a/Tests/expected/cosmosSlowDisk.out +++ b/Tests/expected/cosmosSlowDisk.out @@ -9,6 +9,7 @@ Say.sbx 156 Break.sbx 149 Grid.sbx 559 Press.sbx 872 +Mode.sbx 48 notes.txt 21 Apps hi.script 121 @@ -19,7 +20,7 @@ nonl.script 38 outer.script 376 inner.script 44 loop.script 35 -18 files, 1 directory +19 files, 1 directory > load Say.sbx loaded, starting at 4000 > run the disk took its time diff --git a/Tests/input/cosmosNoData.in b/Tests/input/cosmosNoData.in new file mode 100644 index 0000000..a8d09dc --- /dev/null +++ b/Tests/input/cosmosNoData.in @@ -0,0 +1,5 @@ +Mode +echo a narrow screen +Mode +echo and a wide one again +exit diff --git a/Tests/lint-baseline.txt b/Tests/lint-baseline.txt index 1183ee7..4f5aed5 100644 --- a/Tests/lint-baseline.txt +++ b/Tests/lint-baseline.txt @@ -15,7 +15,6 @@ Programs/CosmOS/Assembler/token.asm redundant-assignment 4 Programs/CosmOS/Assembler/token.asm redundant-setd 2 Programs/CosmOS/Assembler/tokenTest.asm redundant-setd 1 Programs/CosmOS/Source/config.asm redundant-assignment 1 -Programs/CosmOS/Source/cosmos.asm redundant-assignment 1 Programs/CosmOS/Source/cosmos.asm redundant-setd 10 Programs/CosmOS/Source/sbfs.asm branch-to-next 1 Programs/CosmOS/Source/sbfs.asm redundant-assignment 4 diff --git a/Tests/makedisks.sh b/Tests/makedisks.sh index 0fa183a..7125ad4 100755 --- a/Tests/makedisks.sh +++ b/Tests/makedisks.sh @@ -118,6 +118,12 @@ for i in 1 2 3 4 5 6 7 8; do "$TOOL" put "$DISKS/sbfs.img" "filler$i.txt" >/dev/ "$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" \ "$ROOT/Programs/CosmOS/Apps/Press.asm" -o "$WORK/Press.sbx" >/dev/null "$TOOL" put "$DISKS/cosmos.img" "$WORK/Press.sbx" >/dev/null +# Mode.sbx has NO DATA SEGMENT, which is the whole reason it is here. Nothing else on this +# disk is that shape, and the loader used to stop the machine dead on it: a segment of no +# bytes asks the memory controller for the whole 64K, which does not fit, which it refuses. +"$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" \ + "$ROOT/Programs/CosmOS/Apps/Mode.asm" -o "$WORK/Mode.sbx" >/dev/null +"$TOOL" put "$DISKS/cosmos.img" "$WORK/Mode.sbx" >/dev/null printf 'this is not a program' > notes.txt "$TOOL" put "$DISKS/cosmos.img" notes.txt >/dev/null diff --git a/Tests/manifest b/Tests/manifest index 2a7a698..753175d 100644 --- a/Tests/manifest +++ b/Tests/manifest @@ -305,6 +305,17 @@ cosmosPress | CosmOS/Source/cosmos.asm | run | cosmosPre # end - the window's path through the gatherer - and it has its own copy of the line mode # rule, so it needs its own test. The two recordings should agree, which is the point. cosmosPressKeys | CosmOS/Source/cosmos.asm | run | - | - | disks/cosmos.img | cosmosPress.in +# A program with NO DATA SEGMENT, loaded and run. Every program written for this system had +# something in its data until somebody wrote one that did not, and the loader stopped the +# machine dead: a segment of no bytes asks the controller for the whole 64K, which does not +# fit, so it refused in the middle of loading. The most basic form a program can take, and +# nothing had ever tried it. +# +# It is run twice, because it is a toggle and the second run has to find the mode the first +# one left. The line between them is there so the recording shows the shell still working on +# a forty column screen, which is a width nothing else here ever runs at - and the shell +# reads that width for itself now, every line it edits. +cosmosNoData | CosmOS/Source/cosmos.asm | run | cosmosNoData.in | - | disks/cosmos.img # The shell editing the line it is being given, which is the whole reason the keys were # made to arrive. Every line here is typed wrong and then corrected with a different key, and # every one of them comes out as the same command - so the recording says both what the