Two zoom levels on a button, which the device already had

Forty columns and eighty are the same map, the same 8x8 cells and the
same engine; only how many of them fit differs. The map is 128 by 128
either way and the moon is exactly 128 columns of it. And the front end
scales whatever it is handed by the largest whole number that fits, so
320 by 200 at four times and 640 by 400 at twice fill the same glass.

So the two modes ARE two zoom levels and nothing in the video device had
to change to get them: forty columns shows under a third of the moon at
twice the size, eighty shows nearly two thirds. Out for the orbit, in for
the landing, and B says which.

What did have to change is every screen coordinate in Lander, because the
middle of the screen is 160 on one and 320 on the other. They now live in
one block that setView copies over from whichever of two tables matches
the mode, so a gauge reads a variable and never has to know which screen
it is on. Several coordinates became sixteen bit on the way, since 620
will not go in a byte. follow already read HalfScreen and showLander
already writes the world position straight through, so the lander itself
needed nothing but its resting column.

The moon is redrawn on a swap because it is filled as many rows deep as
the mode shows, and a moon drawn 25 deep on a screen showing 50 floats
over nothing.

The swap is edge triggered. A pad is LEVEL and not an event, so a view
that swapped while B was down would swap sixty times a second - which is
not a zoom, it is a strobe, and it redraws the whole moon each time. The
check for that holds the button for three hundred frames and requires the
lander to land where a six frame tap left it; with the edge dropped it
ends seventeen pixels adrift, which is the strobe costing it frames.

Four checks, each seen to fail on its own break. The README's Lander
entry also still described the relief orbit that the previous commit
replaced, and now describes the one that is there.
This commit is contained in:
Anachronaut
2026-09-03 21:48:32 -04:00
parent 85029d3b85
commit a02d701efe
13 changed files with 329 additions and 56 deletions
+253 -45
View File
@@ -48,8 +48,11 @@ start:
; floor the system gives every program does not.
SWI osTakeScreen
CALL setView ; The block every gauge below reads its screen numbers out of.
SETD.0 Wide
LDA.0
OUTA 0x31 ; Forty columns to start in. B swaps to eighty and back.
RSTA
OUTA 0x31 ; Forty columns. A moon 320 pixels across reads better than 640.
; ---- And the view put back to the top of the map ----
;
@@ -92,6 +95,7 @@ everyFrame:
CALL waitFrame
CALL readPad
CALL readControls
CALL zoomButton
CALL fall
CALL move
CALL follow
@@ -418,7 +422,8 @@ drawNextCell:
LDA.1
INCA
STA.1
INIB 0d25
SETD.2 MoonRows
LDB.2
CCF
SUB
BNQ drawRow
@@ -440,10 +445,12 @@ putLander:
OUTA 0xE9 ; Tile 201.
INIA 0x03
OUTA 0xE9 ; Attribute three, so it is yellow against a grey moon.
INIA 0d156
SETD.0 ShipX
LDA.0
OUTA 0xE9
RSTA
OUTA 0xE9 ; X: the middle of a 320 pixel screen, less half a tile.
INCD.0
LDA.0
OUTA 0xE9 ; X: the middle of the screen, less half a tile. Never moves.
OUTA 0xE9
OUTA 0xE9 ; Y, which every frame overwrites.
INIA 0x11
@@ -1269,20 +1276,35 @@ driftSized2:
SETD.1 DriftSize
STA.1
; Where it starts: the middle for a rightward drift, and that much back for a leftward one.
INIA 0d160
SETD.0 DriftLeftward
LDB.0
BRB driftAt ; Nought is rightward, so the bar starts at the middle.
SETD.0 DriftLow
LDB.0
CCF
SUB
MVQA
driftAt:
; ---- Where it starts, in sixteen bits ----
;
; The middle for a rightward drift and that much back for a leftward one, and it is a WORD
; now because the middle of an eighty column screen is 320 and will not go in a byte.
SETD.0 MiddleX
LDA.0
SETD.1 DriftAt
STA.1
INCD.0
LDA.0
INCD.1
STA.1
SETD.0 DriftLeftward
LDA.0
BRA driftAt ; Nought is rightward, so the bar starts at the middle.
SETD.0 DriftAt
LDA.0
SETD.1 DriftLow
LDB.1
CCF
SUB
SETD.1 DriftAt
STQ.1
INCD.0
LDA.0
RSTB
SUB ; And the borrow, which is the other half of the step back.
STQ.0
driftAt:
INIA 0d4
OUTA 0xE3
@@ -1298,11 +1320,14 @@ driftAt:
SETD.0 DriftAt
LDA.0
OUTA 0xE9
RSTA
INCD.0
LDA.0
OUTA 0xE9 ; X.
INIA 0d188
SETD.0 BarY
LDA.0
OUTA 0xE9
RSTA
INCD.0
LDA.0
OUTA 0xE9 ; Y, near the bottom and clear of the ground most places.
SETD.0 DriftSize
LDA.0
@@ -1585,17 +1610,24 @@ showHeightReady:
;
; 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
SETD.0 HeightFoot
LDA.0
LDB.1
CCF
SUB
SETD.1 HeightAt
STQ.1
INCD.0
LDA.0
RSTB
SUB ; And the borrow, which is the other half of the foot.
INCD.1
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.
SETD.0 HeightBar
LDA.0
BRA showHeightEmpty
INIA 0x11
BRI showHeightSized
@@ -1631,7 +1663,8 @@ showHeightSized:
SETD.0 HeightAt
LDA.0
OUTA 0xE9
RSTA
INCD.0
LDA.0
OUTA 0xE9 ; Y.
SETD.0 HeightSize
LDA.0
@@ -1650,6 +1683,85 @@ showHeightSized:
OUTA 0xE9 ; And no depth, A being nought already.
RET
; ---- Two zoom levels, which the device turns out to have already had ----
;
; Forty columns and eighty are the same map, the same 8x8 cells and the same engine; only how
; many of them fit on the screen differs. The map is 128 by 128 either way and the moon is
; exactly 128 columns of it, so forty shows under a third of the moon and eighty shows nearly
; two thirds - and the front end scales whatever it is handed up to the same window, so 320 by
; 200 at four times and 640 by 400 at twice fill the same glass.
;
; THAT IS A ZOOM, and nothing in the video device had to change to get it. What has to change
; is every screen coordinate in this program, because the middle of the screen is 160 on one
; and 320 on the other. They all live in one block.
setView:
SETD.0 ViewNarrow
SETD.1 Wide
LDA.1
BRA setViewFrom
SETD.0 ViewWide
setViewFrom:
SETD.1 View
INIA 0d15
SETD.2 ViewLeft
STA.2
setViewByte:
LDA.0
STA.1
INCD.0
INCD.1
SETD.2 ViewLeft
LDA.2
DECA
STA.2
BNA setViewByte
RET
; ---- Out for the orbit, in for the landing ----
;
; The moon is redrawn because it is filled as many rows deep as the mode shows, and a moon
; drawn 25 deep on a screen showing 50 is a moon floating over nothing. The fixed sprites go
; again because their places moved with the middle.
toggleView:
SETD.0 Wide
LDA.0
INIB 0x01
XOR
STQ.0
CALL setView
SETD.0 Wide
LDA.0
OUTA 0x31
CALL drawMoon
CALL putLander
CALL putGauge
CALL putOrbital
RET
; ---- One press, one swap ----
;
; A pad is LEVEL and not an event: it says what is held NOW, sixty times a second. A view that
; swapped whenever B was down would swap sixty times a second, which is not a zoom, it is a
; strobe. So the swap happens on the frame the button goes down, and the frame it comes up is
; remembered for the comparison rather than acted on.
zoomButton:
SETD.0 Held
LDA.0
INIB 0x20 ; B.
AND
MVQA
SETD.1 ZoomWas
LDB.1
STA.1 ; Now becomes what "last frame" means next time round.
CCF
SUB
BRQ zoomDone ; The same as last frame, so nothing has been pressed or let go.
LDA.1 ; DP1 still names it, and it now holds this frame's answer.
BRA zoomDone ; It changed to NOT held, which is the release and not the press.
CALL toggleView
zoomDone:
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
@@ -1667,28 +1779,54 @@ showHeightSized:
;
; Written once. They never move, and a sprite table is a memory rather than a display list.
putOrbital:
; The leftward mark: sixty four back from the middle, and two more for its own width, so the
; bar's leading edge meets it rather than the bar covering it up.
SETD.0 MiddleX
LDA.0
INIB 0d66
CCF
SUB
SETD.1 MarkAt
STQ.1
INCD.0
LDA.0
RSTB
SUB ; And the borrow, because the middle can be 320.
INCD.1
STQ.1
INIA 0d4
OUTA 0xE3
INIA 0xC0
OUTA 0xE4
INIA 0x40
OUTA 0xE5 ; Sprite four begins sixty four bytes in.
INIA 0d94
CALL putMark
; And the rightward one, sixty four on from it.
SETD.0 MiddleX
LDA.0
INIB 0d64
CCF
ADD
SETD.1 MarkAt
STQ.1
INCD.0
LDA.0
RSTB
ADD
INCD.1
STQ.1
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.
; One mark, at the column in MarkAt, 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
@@ -1696,14 +1834,23 @@ putMark:
; 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.
SETD.1 MarkAt
LDA.1
OUTA 0xE9
RSTA
INCD.1
LDA.1
OUTA 0xE9 ; X.
INIA 0d184
OUTA 0xE9
RSTA
OUTA 0xE9 ; Y, four pixels above the bar it brackets.
SETD.0 BarY
LDA.0
INIB 0d4
CCF
SUB
OUTQ 0xE9
INCD.0
LDA.0
RSTB
SUB
OUTQ 0xE9 ; Y, four pixels above the bar it brackets.
INIA 0x11
OUTA 0xE9 ; One tile by one, however small it ends up drawn.
RSTA
@@ -1786,7 +1933,8 @@ sayNext:
LDA.1
INCA
STA.1
INIB 0d40
SETD.2 RowCells
LDB.2
CCF
SUB
BNQ sayCell
@@ -1920,7 +2068,8 @@ fallBarSized:
STA.1
; Where it starts: the middle going down, and that much back going up.
INIA 0d100
SETD.0 MiddleY
LDA.0
SETD.0 DriftLeftward
LDB.0
BRB fallBarAt ; Nought is downward, so it starts at the middle.
@@ -1944,10 +2093,12 @@ fallBarAt:
SETD.0 FallColour
LDA.0
OUTA 0xE9
INIA 0x2C
SETD.0 FallX
LDA.0
OUTA 0xE9
INIA 0x01
OUTA 0xE9 ; X is 300, which is two bytes: the screen is wider than one.
INCD.0
LDA.0
OUTA 0xE9 ; X, in two bytes, because the screen is wider than one.
SETD.0 FallAt
LDA.0
OUTA 0xE9
@@ -2139,7 +2290,7 @@ DriftHigh:
DriftLeftward:
0x00
DriftAt:
0x00
0x00 0x00
DriftSize:
0x00
DriftColour:
@@ -2175,8 +2326,65 @@ GentleDown:
0d12
GentleAcross:
0d8
; ---- The numbers that change when the screen does ----
;
; Copied over from whichever table below matches the mode, so a gauge reads a variable and
; never has to know which screen it is drawing on. The order here and in both tables is the
; same order and has to stay that way: setView copies the block straight across.
View:
HalfScreen:
0xA0 0x00 ; 160 pixels, which is half of a forty column screen.
0xA0 0x00
ShipX:
0x9C 0x00
MiddleX:
0xA0 0x00
BarY:
0xBC 0x00
FallX:
0x2C 0x01
MiddleY:
0d100
HeightFoot:
0xB4 0x00
RowCells:
0d40
MoonRows:
0d25
ViewLeft:
0x00 ; How much of the block is still to copy.
; Forty columns: 320 by 200, and the moon 25 rows deep because that is all there is to see.
ViewNarrow:
0xA0 0x00 ; Half a screen, which is where the lander is held.
0x9C 0x00 ; And the lander itself, half a tile back from it.
0xA0 0x00 ; The middle, where both bars start.
0xBC 0x00 ; The drift bar's row, twelve pixels off the bottom.
0x2C 0x01 ; The fall bar's column, twenty pixels in from the right.
0d100 ; Half the height, which is where the fall bar starts.
0xB4 0x00 ; The height bar's foot.
0d40 ; Cells to a window row.
0d25 ; Rows of moon to draw.
; Eighty columns: 640 by 400, the same cells at half the size, so twice as much moon either
; way. Every screen number doubles except the ones measured from an edge, which are the edge
; less the same margin - a panel should sit the same distance in whichever screen it is on.
ViewWide:
0x40 0x01 ; 320.
0x3C 0x01 ; 316.
0x40 0x01 ; 320.
0x84 0x01 ; 388, which is twelve off the bottom of four hundred.
0x6C 0x02 ; 620, twenty in from the right of six hundred and forty.
0d200
0x7C 0x01 ; 380.
0d80
0d50
; Which of the two is up. Nought is forty columns, which is what the program starts in.
Wide:
0x00
; What B was doing last frame, because a pad says what is held and not what was pressed.
ZoomWas:
0x00
PixelLow:
0x00
@@ -2252,11 +2460,11 @@ HeightHigh:
HeightBar:
0x00
HeightAt:
0x00
0x00 0x00
HeightSize:
0x00
MarkAt:
0x00
0x00 0x00
Outward:
0x00
Lift: