diff --git a/Programs/CosmOS/Apps/Lander.asm b/Programs/CosmOS/Apps/Lander.asm index cab9f5f..de23f25 100644 --- a/Programs/CosmOS/Apps/Lander.asm +++ b/Programs/CosmOS/Apps/Lander.asm @@ -73,6 +73,7 @@ start: CALL drawMoon CALL putLander CALL putGauge + CALL putOrbital INIA 0x01 OUTA 0x02 ; Key mode. @@ -99,6 +100,7 @@ everyFrame: CALL showDrift CALL showFall CALL showFuel + CALL showHeight SETD.0 Flying LDA.0 BNA everyFrame @@ -1505,6 +1507,223 @@ waitKeyDone: ; in the low half, and the answer needs both halves because a screen is taller than 255 ; sixteenths. restOnSurface: + CALL groundLevel + SETD.0 GroundLow + LDA.0 + SETD.1 DownLow + STA.1 + SETD.0 GroundHigh + LDA.0 + SETD.1 DownHigh + STA.1 + RET + +; ---- How far above the ground, as a bar up the left edge ---- +; +; The orbit put the lander OFF THE TOP OF THE SCREEN, and an instrument panel that only works +; while the ground is in sight is no use to a game whose best flying happens above it. Both +; other bars are rates: they say how fast, and neither of them says where. +; +; Height above the surface underneath rather than above some fixed line, so it reads NOUGHT +; the moment the lander is down - a bar that still showed a third of itself while sitting on a +; pad would be an altimeter nobody could trust. It moves as the moon does underneath, which is +; not noise: passing over a mountain really does leave less room below. +; +; Half a pixel of bar to a pixel of sky. The flyable band is about 256 pixels from the ceiling +; to the lowest ground and the screen is 200 tall, so a bar drawn pixel for pixel would run +; off the top of the very screen it is trying to describe. +showHeight: + CALL groundLevel + SETD.0 GroundLow + LDA.0 + SETD.1 DownLow + LDB.1 + CCF + SUB + SETD.0 HeightLow + STQ.0 + SETD.0 GroundHigh + LDA.0 + SETD.1 DownHigh + LDB.1 + SUB ; And the borrow the half below left, which is the other half. + SETD.0 HeightHigh + STQ.0 + + LDA.0 ; DP0 is still HeightHigh, from the store above. + INIB 0x80 + AND + BNQ showHeightNone ; Negative: the feet are under the surface, so there is no sky. + LDA.0 ; The AND left A alone, so it still holds the high half. + INIB 0d16 + CCF + SUB + BNC showHeightFull ; 4,096 sixteenths or more, which is the whole bar and then some. + + LDA.0 ; DP0 still names the high half, and the compare did not touch A. + SETD.0 HeightLow + LDB.0 + SHL + SHL + SHL ; ---- Over thirty two, which comes out in A ---- + ; + ; A is the HIGH half of the shift register, so three turns LEFT of + ; the whole sixteen bits leaves bits five to twelve in A - which is + ; the height over thirty two, in one instruction each and no + ; division anywhere. + BRI showHeightReady +showHeightFull: + INIA 0d127 + BRI showHeightReady +showHeightNone: + RSTA +showHeightReady: + SETD.1 HeightBar + STA.1 + + ; ---- Grown upwards, from a fixed foot ---- + ; + ; A sprite is placed by its TOP, so a bar that grows up is one whose top moves as its height + ; changes. Both come out of the same number and the foot stays put. + INIA 0d180 + SETD.0 HeightBar + LDB.0 + CCF + SUB + SETD.1 HeightAt + STQ.1 + + ; Nought is the off switch, and it is the SIZE that switches it: a target height of nought + ; is the natural height, which would be a tile sitting on the ground pretending to be sky. + LDA.0 ; DP0 still names the bar, from working the top out above. + BRA showHeightEmpty + INIA 0x11 + BRI showHeightSized +showHeightEmpty: + RSTA +showHeightSized: + SETD.1 HeightSize + STA.1 + + INIA 0d4 + OUTA 0xE3 + INIA 0xC0 + OUTA 0xE4 + INIA 0x30 + OUTA 0xE5 ; Sprite three begins forty eight bytes in. + INIA 0xC8 + OUTA 0xE9 ; The solid block. + INIA 0x05 + OUTA 0xE9 ; ---- Scheme five, magenta ---- + ; + ; Red and green are taken and they MEAN something here: how fast, + ; and whether it can be landed with. Neither is a thing an altitude + ; is, so magenta is the panel's other job - where the lander is + ; rather than how it is going - and the marks below share it. + ; + ; Cyan was the first choice and it is the colour of a landing pad. + ; The pads are checked as whole cells of it, so a three pixel bar + ; in the same colour would have made that check count the panel. + INIA 0d8 + OUTA 0xE9 + RSTA + OUTA 0xE9 ; X, hard against the left edge and clear of the moon's middle. + SETD.0 HeightAt + LDA.0 + OUTA 0xE9 + RSTA + OUTA 0xE9 ; Y. + SETD.0 HeightSize + LDA.0 + OUTA 0xE9 + RSTA + OUTA 0xE9 ; No flags. + INIA 0d3 + OUTA 0xE9 + RSTA + OUTA 0xE9 ; Three pixels wide. + SETD.0 HeightBar + LDA.0 + OUTA 0xE9 + RSTA + OUTA 0xE9 ; The height, which is the altitude. NOUGHT DRAWS NOTHING. + OUTA 0xE9 ; And no depth, A being nought already. + RET + +; ---- Where orbital speed is, marked on the drift bar ---- +; +; The bar says how fast sideways and the physics says 64 sixteenths is the speed at which the +; swing outwards cancels the pull. Without a mark on it that number is FOLKLORE: a pilot can +; feel that somewhere around here the falling stops, and has no way to see where. With one, +; the bar reaching the mark IS circular orbit, under it is falling and past it is climbing, +; and the whole mechanic becomes something read at a glance instead of guessed at. +; +; One each way, because a moon that wraps can be gone round in either direction. +; +; Placed so the BAR'S EDGE MEETS THE MARK at exactly orbital speed. The rightward bar starts +; at the middle and runs 64 pixels to 223, so its mark begins at 224; the leftward one ends at +; the middle and starts at 96, so its mark is the two pixels before that. Adjacent rather than +; overlapping, or a bar at orbital speed would hide the thing it is being measured against. +; +; Written once. They never move, and a sprite table is a memory rather than a display list. +putOrbital: + INIA 0d4 + OUTA 0xE3 + INIA 0xC0 + OUTA 0xE4 + INIA 0x40 + OUTA 0xE5 ; Sprite four begins sixty four bytes in. + INIA 0d94 + CALL putMark + INIA 0d4 + OUTA 0xE3 + INIA 0xC0 + OUTA 0xE4 + INIA 0x50 + OUTA 0xE5 ; And sprite five, sixteen bytes after it. + INIA 0d224 + CALL putMark + RET + +; One mark, at the screen column in A, straddling the drift bar so the bar meets it end on. +putMark: + SETD.1 MarkAt + STA.1 + INIA 0xC8 + OUTA 0xE9 ; The solid block, which is what every bar here is made of. + INIA 0x05 + OUTA 0xE9 ; Magenta, the same as the height bar: both say where, not how fast. + ; White was the first choice and white is what TEXT is, so the + ; ceiling check - which counts white to find its warning - started + ; counting these instead and read a warning that was never up. + LDA.1 ; DP1 still names it, from being handed it above. + OUTA 0xE9 + RSTA + OUTA 0xE9 ; X. + INIA 0d184 + OUTA 0xE9 + RSTA + OUTA 0xE9 ; Y, four pixels above the bar it brackets. + INIA 0x11 + OUTA 0xE9 ; One tile by one, however small it ends up drawn. + RSTA + OUTA 0xE9 ; No flags. + INIA 0d2 + OUTA 0xE9 + RSTA + OUTA 0xE9 ; Two pixels wide. + INIA 0d11 + OUTA 0xE9 + RSTA + OUTA 0xE9 ; And eleven tall, so it shows above and below a three pixel bar. + OUTA 0xE9 ; No depth, A being nought already. + RET + +; ---- Where a lander standing on the ground below would have its top ---- +; +; The same sum, wanted in two places: putting one down on the surface, and measuring how far +; above the surface one is. In sixteenths, because that is what a height is measured in. +groundLevel: SETD.0 SurfaceAt LDA.0 INIB 0d8 @@ -1516,13 +1735,13 @@ restOnSurface: SHL SHL SHL ; Four turns, which is times sixteen. - SETD.0 DownHigh + SETD.0 GroundHigh STA.0 RSTA CCF ADD MVQA - SETD.0 DownLow + SETD.0 GroundLow STA.0 RET @@ -2020,6 +2239,24 @@ OrbitAt: ; which is the only thing here that gravity did not already need. OrbitStep: 0x00 + +; Ground level under the lander, the height above it, and what the bar makes of that. +GroundLow: + 0x00 +GroundHigh: + 0x00 +HeightLow: + 0x00 +HeightHigh: + 0x00 +HeightBar: + 0x00 +HeightAt: + 0x00 +HeightSize: + 0x00 +MarkAt: + 0x00 Outward: 0x00 Lift: diff --git a/Tests/expected/cosmosCrossDisk.out b/Tests/expected/cosmosCrossDisk.out index 4681189..e5652a8 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 4651 +Lander.sbx 4968 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosDrives.out b/Tests/expected/cosmosDrives.out index 3373cd7..9996f44 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 4651 +Lander.sbx 4968 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFault.out b/Tests/expected/cosmosFault.out index 876807d..4934619 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 4651 +Lander.sbx 4968 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFlip.out b/Tests/expected/cosmosFlip.out index c84ae4d..7a2bee7 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 4651 +Lander.sbx 4968 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosGrid.out b/Tests/expected/cosmosGrid.out index 3c811ad..a96f531 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 4651 +Lander.sbx 4968 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosMonitor.out b/Tests/expected/cosmosMonitor.out index 758e7da..40b85be 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 4651 +Lander.sbx 4968 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosMonitorRun.out b/Tests/expected/cosmosMonitorRun.out index 1779c0f..1e6fbf7 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 4651 +Lander.sbx 4968 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosRun.out b/Tests/expected/cosmosRun.out index b467867..99ecc07 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 4651 +Lander.sbx 4968 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSlowDisk.out b/Tests/expected/cosmosSlowDisk.out index c5c9cde..78d19c2 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 4651 +Lander.sbx 4968 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSprite.out b/Tests/expected/cosmosSprite.out index 4b86208..e046646 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 4651 +Lander.sbx 4968 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/video.sh b/Tests/video.sh index a1187d0..82f501c 100755 --- a/Tests/video.sh +++ b/Tests/video.sh @@ -1966,6 +1966,72 @@ EOT && result ok "and no drift draws none" "a width of nought is a sprite that is not there" \ || result no "and no drift draws none" "$STILLWIDE pixels of bar with nothing moving" +# ---- The altitude bar, and the mark that says where orbital speed is ---- +# +# The orbit takes the lander OFF THE TOP OF THE SCREEN, which left an instrument panel that +# only worked while the ground was in sight. Two additions answer that: a bar up the left edge +# for how much sky is underneath, and a pair of marks on the drift bar showing where the +# sideways speed stops being a fall and starts being an orbit. +# +# BOTH HALVES OF THE BAR, because either alone is vacuous. A bar that was always full would +# pass "there is one while flying" and a bar that was never drawn would pass "there is none +# while landed" - it is the two together that say the thing is measuring something. +# +# Two flights, because no one flight holds both ends well. The climb reaches a bar of two +# hundred pixels and is nowhere near the edge of the claim; the descent lands and then STAYS +# landed, so its end of it cannot drift with the boot time the way the deleted orbit check +# did. One flight that did both spent four seconds airborne and read thirty pixels, which is a +# margin thin enough to be luck. +# +# The marks are checked BY POSITION rather than by counting, because position is the whole +# claim: 64 sixteenths is orbital speed, the bar is a pixel a sixteenth from the middle at +# 160, so the marks belong at 224 and at the two pixels before 96. A count would pass with +# them anywhere on the screen. Read off the landed frame, where nothing is drifting and the +# drift bar cannot be lying across them. +# ---- Its own keyboard file, and that is not fussiness ---- +# +# $BUILD/lander.keys is not just the command that starts the program: it holds a key down +# every forty eight bytes, because the test that made it is checking that a HELD KEY keeps +# lifting. Borrowing it here meant the lander was being flown from the keyboard and the +# controller at once, and the gentle descent below arrived as a crash - which reads exactly +# like a broken altimeter and is nothing of the sort. +python3 -c "open('$BUILD/fly.keys','wb').write(b'Lander\n' + b'\x00' * 8000)" +python3 -c "open('$BUILD/climb.pad','wb').write(b'\x00' * 20 + b'\x09' * 300 + b'\x01' * 200 + b'\x00' * 6000)" +python3 -c "open('$BUILD/settle.pad','wb').write(b'\x00' * 150 + b'\x08' * 90 + b'\x00' * 8000)" +timeout 60 "$EMU" --fast --cycles 6000000 --keyboard "$BUILD/fly.keys" \ + --pad "$BUILD/climb.pad" --screen "$BUILD/climb.ppm" \ + --disk "$ROOT/Tests/build/disks/cosmos.img" --ram-disk 2048 \ + "$BUILD/cosmos.bin" > "$BUILD/climb.out" 2>&1 || true +timeout 60 "$EMU" --fast --cycles 9000000 --keyboard "$BUILD/fly.keys" \ + --pad "$BUILD/settle.pad" --screen "$BUILD/settle.ppm" \ + --disk "$ROOT/Tests/build/disks/cosmos.img" --ram-disk 2048 \ + "$BUILD/cosmos.bin" > "$BUILD/settle.out" 2>&1 || true +read -r FLYING RESTING MARKS </dev/null || echo "0 0 none") +EOT +[ "$FLYING" -gt 100 ] \ + && result ok "a lander above the ground draws an altitude bar" "$FLYING pixels of it" \ + || result no "a lander above the ground draws an altitude bar" "$FLYING pixels of it" +# Sitting on a pad is nought sky underneath, and NOUGHT DRAWS NOTHING. An altimeter that still +# showed a third of itself on the ground is one nobody could read the rest of. +[ "$RESTING" = "0" ] \ + && result ok "and one sitting on the ground draws none" "a bar of nought is no bar at all" \ + || result no "and one sitting on the ground draws none" "$RESTING pixels still up" +[ "$MARKS" = "94,95,224,225" ] \ + && result ok "orbital speed is marked on the drift bar" "64 pixels either side of the middle" \ + || result no "orbital speed is marked on the drift bar" "marks at $MARKS" + # ---- 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