Lunar Porter, rung two: it lands, or it does not
The terrain is an array in Data Memory rather than something read back out of the map, and that is the whole reason this is cheap: the ground under the lander is one index into 128 bytes, where asking the screen would be a transfer through the controller every frame. The column is the world position over eight, masked to the moon's 128. The surface is that column's row times eight - three turns left of the shift register, since a row is at most 24 and 192 fits in the low half. The feet are the lander's top plus its eight pixels. WHAT DECIDES IS THE SPEED AT THE MOMENT IT ARRIVES. Both of them, and both have to be gentle: three quarters of a pixel a frame downwards and half of one sideways. Sideways is the tighter on purpose, because a landing that was soft downwards and sliding is a lander on its side - which is the interesting half of the difficulty, and the half the drift bar was blind about until it existed. Two fixtures say it works, and they differ only in what was held: one holds nothing and falls the whole way, the other pulses the thruster six frames in sixteen and survives. Same terrain, same seed, same keys. Also: the gamepad did nothing, and the reason is that the four direction buttons are the D-PAD. A lot of controllers made this century have one nobody uses - the thumb goes on the stick, which reports as an axis rather than a button - so a pad that was plugged in and working correctly did nothing at all. The stick counts as held past halfway now. Untested here, because there is no controller in this environment and the suite runs headless; Voyager also says at startup which controllers it can see, so a pad that still does nothing can be told apart from one nothing noticed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
db0c26e13f
commit
8eb4e4d67e
@@ -77,6 +77,7 @@ everyFrame:
|
||||
CALL fall
|
||||
CALL move
|
||||
CALL follow
|
||||
CALL touchdown
|
||||
CALL showLander
|
||||
CALL showDrift
|
||||
SETD.0 Flying
|
||||
@@ -542,10 +543,15 @@ showLander:
|
||||
;
|
||||
; The whole of it is the sprite's target width, which the device stretches one tile into. The
|
||||
; program does no drawing at all: it works out one number a frame and writes it.
|
||||
showDrift:
|
||||
; The magnitude, and which way. A two's complement pair is inverted and stepped, and the
|
||||
; step's carry is what runs into the high half.
|
||||
SETD.0 SpeedAcross
|
||||
; ---- How big a signed pair is, without its sign ----
|
||||
;
|
||||
; DP0 names a two byte value, low half first. Leaves the size in DriftLow and DriftHigh and
|
||||
; the sign in DriftLeftward, which is nought for a positive one.
|
||||
;
|
||||
; A two's complement pair is inverted and stepped, and the step's carry is what runs into the
|
||||
; high half - the same carry that makes addWord work, used one instruction at a time because
|
||||
; there is nothing to add.
|
||||
magnitude:
|
||||
LDA.0
|
||||
SETD.1 DriftLow
|
||||
STA.1
|
||||
@@ -557,7 +563,7 @@ showDrift:
|
||||
AND
|
||||
SETD.1 DriftLeftward
|
||||
STQ.1 ; The top bit of the high half, which is the sign.
|
||||
BRQ driftSized
|
||||
BRQ magnitudeDone
|
||||
|
||||
SETD.0 DriftLow
|
||||
LDA.0
|
||||
@@ -572,8 +578,12 @@ showDrift:
|
||||
RSTB
|
||||
ADD ; Plus the carry the step above left, which is the other half.
|
||||
STQ.0
|
||||
magnitudeDone:
|
||||
RET
|
||||
|
||||
driftSized:
|
||||
showDrift:
|
||||
SETD.0 SpeedAcross
|
||||
CALL magnitude
|
||||
; ---- Clamped, because a bar wider than the screen says nothing a full one does not ----
|
||||
;
|
||||
; And because a target width is sixteen bits: an unclamped one would be asking the device to
|
||||
@@ -661,6 +671,110 @@ driftAt:
|
||||
OUTA 0xE9 ; And no depth.
|
||||
RET
|
||||
|
||||
; ---- Has it arrived, and how hard ----
|
||||
;
|
||||
; The terrain is an array in Data Memory and not something read back out of the map, which is
|
||||
; the whole reason this is cheap: the ground under the lander is one index into 128 bytes,
|
||||
; where asking the screen would mean a transfer through the controller every frame.
|
||||
;
|
||||
; The column is the world position divided by eight, masked to the moon's 128. The surface is
|
||||
; that column's row times eight, and the lander's feet are its top plus its eight pixels.
|
||||
touchdown:
|
||||
SETD.0 AcrossLow
|
||||
CALL toPixels
|
||||
SETD.0 PixelHigh
|
||||
LDA.0
|
||||
SETD.0 PixelLow
|
||||
LDB.0
|
||||
CALL eighth
|
||||
MVQA
|
||||
INIB 0x7F
|
||||
AND ; The moon is 128 columns, so seven bits of it.
|
||||
MVQA
|
||||
SETD.1 Terrain
|
||||
DPUA.1 ; Terrain plus the column, which is the row its surface is on.
|
||||
LDA.1
|
||||
|
||||
; Times eight, into pixels. A is the high half of the shift register and B the low, so the
|
||||
; row goes in B and three turns LEFT multiply it - and a row is at most 24, so 192 fits in
|
||||
; the low half and the high one stays empty.
|
||||
RSTB
|
||||
CCF
|
||||
ADD
|
||||
MVQB ; The row, in the low half.
|
||||
RSTA
|
||||
SHL
|
||||
SHL
|
||||
SHL
|
||||
RSTA
|
||||
CCF
|
||||
ADD
|
||||
SETD.1 SurfaceAt
|
||||
STQ.1
|
||||
|
||||
; The feet: where the lander's top is, plus the eight pixels of it.
|
||||
SETD.0 DownLow
|
||||
CALL toPixels
|
||||
SETD.0 PixelHigh
|
||||
LDA.0
|
||||
BNA touchdownDone ; Above the screen entirely, so nowhere near the ground.
|
||||
SETD.0 PixelLow
|
||||
LDA.0
|
||||
INIB 0d8
|
||||
CCF
|
||||
ADD
|
||||
MVQA
|
||||
SETD.1 SurfaceAt
|
||||
LDB.1
|
||||
CCF
|
||||
SUB
|
||||
BRC touchdownDone ; Borrowed: the feet are still above the surface.
|
||||
|
||||
; ---- Arrived. Now, how ----
|
||||
;
|
||||
; Both speeds, and both have to be gentle. A landing that was soft downwards and sliding
|
||||
; sideways is a lander on its side, which is the interesting half of the difficulty: the
|
||||
; drift bar is the thing that was blind about it until this rung.
|
||||
SETD.0 SpeedDown
|
||||
CALL magnitude
|
||||
SETD.0 DriftHigh
|
||||
LDA.0
|
||||
BNA touchdownCrash ; Anything in the high half is far too fast to argue about.
|
||||
SETD.0 DriftLow
|
||||
LDA.0
|
||||
SETD.1 GentleDown
|
||||
LDB.1
|
||||
CCF
|
||||
SUB
|
||||
BNC touchdownCrash ; No borrow, so it is at or past the limit.
|
||||
|
||||
SETD.0 SpeedAcross
|
||||
CALL magnitude
|
||||
SETD.0 DriftHigh
|
||||
LDA.0
|
||||
BNA touchdownCrash
|
||||
SETD.0 DriftLow
|
||||
LDA.0
|
||||
SETD.1 GentleAcross
|
||||
LDB.1
|
||||
CCF
|
||||
SUB
|
||||
BNC touchdownCrash
|
||||
|
||||
SETD.0 LandedText
|
||||
BRI touchdownStop
|
||||
touchdownCrash:
|
||||
SETD.0 CrashedText
|
||||
touchdownStop:
|
||||
SWI osPrintString
|
||||
INIA 0x0A
|
||||
OUTA 0x00
|
||||
RSTA
|
||||
SETD.1 Flying
|
||||
STA.1 ; Which ends the loop, and the program tidies up as it always did.
|
||||
touchdownDone:
|
||||
RET
|
||||
|
||||
; ---- Sums ----
|
||||
;
|
||||
; DP0 names the two byte value being changed, low half first, and DP1 the one being added to
|
||||
@@ -821,6 +935,19 @@ DriftAt:
|
||||
0x00
|
||||
DriftSize:
|
||||
0x00
|
||||
SurfaceAt:
|
||||
0x00
|
||||
|
||||
; ---- What counts as gentle ----
|
||||
;
|
||||
; In sixteenths of a pixel a frame, so twelve is three quarters of a pixel a frame and eight
|
||||
; is half of one. Sideways is the tighter of the two on purpose: a lander that arrives
|
||||
; straight down at three quarters of a pixel is a landing, and one sliding at the same speed
|
||||
; is a lander on its side.
|
||||
GentleDown:
|
||||
0d12
|
||||
GentleAcross:
|
||||
0d8
|
||||
HalfScreen:
|
||||
0xA0 0x00 ; 160 pixels, which is half of a forty column screen.
|
||||
|
||||
@@ -844,6 +971,11 @@ Flying:
|
||||
Terrain:
|
||||
#Reserve 0d128
|
||||
|
||||
LandedText:
|
||||
"Down safely."
|
||||
CrashedText:
|
||||
"Crashed."
|
||||
|
||||
LanderArt:
|
||||
0x00 0x00 0x01 0x01 0x01 0x01 0x00 0x00
|
||||
0x00 0x01 0x01 0x01 0x01 0x01 0x01 0x00
|
||||
|
||||
Reference in New Issue
Block a user