diff --git a/Programs/CosmOS/Apps/Lander.asm b/Programs/CosmOS/Apps/Lander.asm index de9bbbb..9d1251b 100644 --- a/Programs/CosmOS/Apps/Lander.asm +++ b/Programs/CosmOS/Apps/Lander.asm @@ -70,6 +70,7 @@ start: CALL makeMoon CALL drawMoon CALL putLander + CALL putGauge INIA 0x01 OUTA 0x02 ; Key mode. @@ -94,6 +95,7 @@ everyFrame: CALL touchdown CALL showLander CALL showDrift + CALL showFuel SETD.0 Flying LDA.0 BNA everyFrame @@ -340,6 +342,97 @@ putLander: OUTA 0xE9 ; No flags, natural size, no depth. RET +; ---- The gauge, which lives where the moon cannot scroll it away ---- +; +; The window is a layer at a SCREEN position rather than a position in the world, so a bar +; drawn in it stays where it is put however far the moon turns underneath. One row, at the +; top, and the label written once because it never changes. +; +; The letters are ordinary tiles. The character generator starts at the space, so glyph n is +; character n plus thirty two - which makes F, U, E and L into 38, 53, 37 and 44. +putGauge: + INIA 0x01 + OUTA 0x3D ; One row tall. + RSTA + OUTA 0x3E ; At the top of the screen. + + INIA 0d5 + OUTA 0xE3 + INIA 0xC0 + OUTA 0xE4 + RSTA + OUTA 0xE5 ; The window begins at 0xC000 of the screen bank. + INIA 0d38 + OUTA 0xE9 + RSTA + OUTA 0xE9 ; F + INIA 0d53 + OUTA 0xE9 + RSTA + OUTA 0xE9 ; U + INIA 0d37 + OUTA 0xE9 + RSTA + OUTA 0xE9 ; E + INIA 0d44 + OUTA 0xE9 + RSTA + OUTA 0xE9 ; L + RET + +; ---- And how much of it is left ---- +; +; A byte of fuel makes a bar of up to 31 cells, which is the byte over eight: a shift, because +; there is no divide. Thirty five cells of room after the label, so a full tank does not quite +; fill the row and there is somewhere for it to be seen to stop. +; +; Redrawn whole every frame. Thirty five cells is seventy bytes out of one port with the +; address named once, which is cheaper than working out which of them changed. +showFuel: + RSTA + SETD.1 Fuel + LDB.1 + CALL eighth ; Q is the fuel over eight, in the low half where it belongs. + MVQA + SETD.1 GaugeLeft + STA.1 + + INIA 0d5 + OUTA 0xE3 + INIA 0xC0 + OUTA 0xE4 + INIA 0x0A + OUTA 0xE5 ; Cell five of the window row, just past the label. + RSTA + SETD.1 GaugeAt + STA.1 + +gaugeCell: + ; Solid while there is bar left to draw, blank after it. + SETD.1 GaugeLeft + LDA.1 + BRA gaugeEmpty + DECA + STA.1 + INIA 0xC8 + OUTA 0xE9 ; The solid block, which is the ground's tile. + INIA 0x02 + OUTA 0xE9 ; Scheme two, so the bar is not the colour of the moon. + BRI gaugeNext +gaugeEmpty: + OUTA 0xE9 + OUTA 0xE9 ; A is nought here: no tile and no colour. +gaugeNext: + SETD.1 GaugeAt + LDA.1 + INCA + STA.1 + INIB 0d35 + CCF + SUB + BNQ gaugeCell + RET + ; ---- What the pad is holding ---- ; ; One read, every button at once, and it does not go away when it is looked at. THIS IS THE @@ -474,6 +567,8 @@ fall: INIB 0x08 ; Up AND BRQ fallNotUp + CALL takeFuel + BRQ fallNotUp ; Nothing in the tank, so nothing out of the engine. SETD.0 SpeedDown SETD.1 PadUp CALL addWord @@ -483,6 +578,8 @@ fallNotUp: INIB 0x02 ; Left AND BRQ fallNotLeft + CALL takeFuel + BRQ fallNotLeft SETD.0 SpeedAcross SETD.1 PadLeft CALL addWord @@ -492,12 +589,39 @@ fallNotLeft: INIB 0x01 ; Right AND BRQ fallDone + CALL takeFuel + BRQ fallDone SETD.0 SpeedAcross SETD.1 PadRight CALL addWord fallDone: RET +; ---- One unit, if there is one ---- +; +; Q comes back nought when the tank is empty, and the thruster that asked does not fire. There +; is no message and no ending: a lander with no fuel is still flying, it just cannot do +; anything about where. What happens next is gravity, and gravity is patient. +; +; Charged PER THRUSTER PER TICK, so holding two at once costs two - which is the honest price +; and makes a drift you corrected expensive in a way a drift you avoided is not. +takeFuel: + SETD.1 Fuel + LDA.1 + BRA takeFuelNone + DECA + STA.1 + INIA 0x01 + RSTB + CCF + ADD ; Q is one, which the caller reads as yes. + RET +takeFuelNone: + RSTB + CCF + ADD ; A is nought here, so Q is too, which is no. + RET + ; ---- Where it is now ---- ; ; Across first, and the wrap is an AND rather than a comparison: the moon is 2^14 sixteenths @@ -1001,6 +1125,18 @@ DriftAt: 0x00 DriftSize: 0x00 + +; ---- The tank ---- +; +; One byte, and a byte is enough: over eight it is a bar of up to 31 cells, and at one unit a +; thruster a tick it is about forty seconds of holding the engine open. Sixteen bits would be +; more arithmetic for a number nobody reads to the unit. +Fuel: + 0xFF +GaugeLeft: + 0x00 +GaugeAt: + 0x00 SurfaceAt: 0x00 diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index 440fcad..b0ffbf0 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -727,7 +727,7 @@ from every assembly file in it. Several are old programs written for the bare ma | 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. | | Sprite | Moves a ball across the shell's own text, writing not one byte of the map to do it. It leaves the sprite in the table on the way out, because clearing them is the system's job - see below. | | Pad | Says what the controllers are doing, printing a line whenever one changes. It tells apart the three things that look identical from inside a game that is not responding: a pad nobody noticed, a pad mapped to nothing, and a mapping that is wrong. | -| Lander | Lunar Porter, rung one: a lander over a moon that wraps. Flown with a controller if there is one - a held thruster burns every tick it is held for - and with the arrow keys if there is not, where one press is one burn and that is the most the console can say. A bar at the bottom is the sideways drift, drawn as a sprite stretched to the speed - a moon has no air, so a drift never stops by itself and stopping one means cancelling it exactly, which is hard to do blind. It lands or crashes on arrival, and what decides is the speed at the moment it touches: gentler than three quarters of a pixel a frame downwards and half of one sideways, or it is a lander on its side. | +| Lander | Lunar Porter, rung one: a lander over a moon that wraps. Flown with a controller if there is one - a held thruster burns every tick it is held for - and with the arrow keys if there is not, where one press is one burn and that is the most the console can say. A bar at the bottom is the sideways drift, drawn as a sprite stretched to the speed - a moon has no air, so a drift never stops by itself and stopping one means cancelling it exactly, which is hard to do blind. It lands or crashes on arrival, and what decides is the speed at the moment it touches: gentler than three quarters of a pixel a frame downwards and half of one sideways, or it is a lander on its side. A fuel gauge sits in the window layer, where the moon turning underneath cannot scroll it away, and every thruster costs a unit of fuel every tick it fires. An empty tank is not an ending: it is a lander still flying that can no longer do anything about where. | | Depth | Four pillars at four distances and a ball walking past them, behind the near ones and in front of the far ones. The ball is sprite nought and every pillar is numbered after it, so table order puts it in front of all four - what actually decides is the depth buffer, asked a column 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 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. | diff --git a/SplitBit Test Manual.md b/SplitBit Test Manual.md index 5e483f6..9f6209a 100644 --- a/SplitBit Test Manual.md +++ b/SplitBit Test Manual.md @@ -101,7 +101,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`. 211 tests, of which 149 run, 35 +everything it printed against a file in `Tests/expected`. 212 tests, of which 150 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 9e5b68f..063b69b 100644 --- a/Tests/expected/cosmosCrossDisk.out +++ b/Tests/expected/cosmosCrossDisk.out @@ -25,7 +25,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1580 +Lander.sbx 1756 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosDrives.out b/Tests/expected/cosmosDrives.out index 6460598..d500564 100644 --- a/Tests/expected/cosmosDrives.out +++ b/Tests/expected/cosmosDrives.out @@ -15,7 +15,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1580 +Lander.sbx 1756 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFault.out b/Tests/expected/cosmosFault.out index 1581806..d51ccc5 100644 --- a/Tests/expected/cosmosFault.out +++ b/Tests/expected/cosmosFault.out @@ -32,7 +32,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1580 +Lander.sbx 1756 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFlip.out b/Tests/expected/cosmosFlip.out index fd10660..2542be5 100644 --- a/Tests/expected/cosmosFlip.out +++ b/Tests/expected/cosmosFlip.out @@ -22,7 +22,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1580 +Lander.sbx 1756 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosGrid.out b/Tests/expected/cosmosGrid.out index 884eb17..10093c2 100644 --- a/Tests/expected/cosmosGrid.out +++ b/Tests/expected/cosmosGrid.out @@ -22,7 +22,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1580 +Lander.sbx 1756 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosLanderDry.out b/Tests/expected/cosmosLanderDry.out new file mode 100644 index 0000000..343b429 --- /dev/null +++ b/Tests/expected/cosmosLanderDry.out @@ -0,0 +1,8 @@ +CosmOS +> Lander +Crashed. +finished +> exit +halted +Execution halted. +[exit 0] diff --git a/Tests/expected/cosmosMonitor.out b/Tests/expected/cosmosMonitor.out index e02a658..9a61cea 100644 --- a/Tests/expected/cosmosMonitor.out +++ b/Tests/expected/cosmosMonitor.out @@ -108,7 +108,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1580 +Lander.sbx 1756 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosMonitorRun.out b/Tests/expected/cosmosMonitorRun.out index a580a79..87e5446 100644 --- a/Tests/expected/cosmosMonitorRun.out +++ b/Tests/expected/cosmosMonitorRun.out @@ -27,7 +27,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1580 +Lander.sbx 1756 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosRun.out b/Tests/expected/cosmosRun.out index 636fbd9..56b16b2 100644 --- a/Tests/expected/cosmosRun.out +++ b/Tests/expected/cosmosRun.out @@ -15,7 +15,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1580 +Lander.sbx 1756 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSlowDisk.out b/Tests/expected/cosmosSlowDisk.out index 4332c7b..c61992d 100644 --- a/Tests/expected/cosmosSlowDisk.out +++ b/Tests/expected/cosmosSlowDisk.out @@ -13,7 +13,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1580 +Lander.sbx 1756 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSprite.out b/Tests/expected/cosmosSprite.out index 6ef7b63..0e3d038 100644 --- a/Tests/expected/cosmosSprite.out +++ b/Tests/expected/cosmosSprite.out @@ -22,7 +22,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1580 +Lander.sbx 1756 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/input/landerBurn.pad b/Tests/input/landerBurn.pad new file mode 100644 index 0000000..0034b31 --- /dev/null +++ b/Tests/input/landerBurn.pad @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Tests/manifest b/Tests/manifest index 433589d..6e5a147 100644 --- a/Tests/manifest +++ b/Tests/manifest @@ -1033,6 +1033,16 @@ cosmosLanderSoft | CosmOS/Source/cosmos.asm | run | - # nought; the front end hands out the numbers the host gave it, so reading only the first # works on the machine it was written on and silently does nothing on the next one. cosmosLanderPadOne | CosmOS/Source/cosmos.asm | run | - | 60000000 | disks/cosmos.img | lander.keys | landerIdle.pad,landerSoft.pad +# ---- A tank runs out, and gravity is patient ---- +# +# The thruster is held from the first frame to the last and it still crashes. Fuel is a byte +# and a thruster costs one a tick, so about forty seconds of holding the engine open empties +# it - and an empty tank is not an ending, it is a lander that is still flying and can no +# longer do anything about where. +# +# Which is what says the fuel is real. A lander that could hold Up for ever would land every +# time, and the whole economy this is the first half of would have nothing to buy. +cosmosLanderDry | CosmOS/Source/cosmos.asm | run | - | 40000000 | disks/cosmos.img | lander.keys | landerBurn.pad # ---- And the thing that says whether a controller is being seen at all ---- # # Three states look identical from inside a game that is not responding: a pad the front end diff --git a/Tests/video.sh b/Tests/video.sh index d8126a0..7cd1ec5 100755 --- a/Tests/video.sh +++ b/Tests/video.sh @@ -1707,6 +1707,38 @@ EOT && result ok "a held thruster keeps lifting" "row $EARLY early, row $LATE later" \ || result no "a held thruster keeps lifting" "row $EARLY early, row $LATE later" +# ---- The fuel gauge, in the window ---- +# +# A bar in the window layer, which is at a SCREEN position - so the moon turning underneath it +# does not move it, which is the whole reason the window exists and the reason this is not +# drawn in the map like the terrain is. +# +# Two captures with the thruster held throughout. The bar has to be SHORTER in the later one, +# because a thruster costs a unit of fuel every tick it fires: a gauge that did not shrink +# would be a tank that was not being spent. +python3 -c "open('$BUILD/burn.pad','wb').write(b'\x08' * 4000)" +for when in 1500000 6000000; do + timeout 30 "$EMU" --fast --cycles $when --keyboard "$BUILD/lander.keys" \ + --pad "$BUILD/burn.pad" --screen "$BUILD/burn$when.ppm" \ + --disk "$ROOT/Tests/build/disks/cosmos.img" --ram-disk 2048 \ + "$BUILD/cosmos.bin" > "$BUILD/burn.out" 2>&1 || true +done +read -r GAUGEEARLY GAUGELATE </dev/null || echo "-1 -1") +EOT +[ "$GAUGEEARLY" -gt 0 ] && [ "$GAUGELATE" -gt 0 ] && [ "$GAUGELATE" -lt "$GAUGEEARLY" ] \ + && result ok "a held thruster spends the tank" "the gauge ran to $GAUGEEARLY, then to $GAUGELATE" \ + || result no "a held thruster spends the tank" "$GAUGEEARLY early, $GAUGELATE later" + # ---- The drift bar, which is a scaled sprite doing a job ---- # # A moon has no air, so a sideways drift never stops by itself and stopping one means