The coarse scroll register was being sent the wrong register
CALL eighth OUTA 0x36 RET puts A back the way it found it, so the column origin was written the high byte of the position that had been passed in, and the answer the subroutine had worked out went nowhere. The fine register was computed inline with OUTQ and was correct, which is exactly what it looked like from the outside: smooth scrolling within a cell that never advanced one. Q is the only register that crosses a RET. Every other answer in this program already came back in it; this one had been written as if A would do, and A very nearly does, which is what makes it worth a comment rather than a fix. Gravity was Jupiter's. A sixteenth of a pixel per frame per frame is the smallest step this arithmetic can take and it crossed the screen in a second, so it is applied one frame in six instead - which divides the pull by six and costs a byte and a compare. The alternative was a finer unit for velocity than for position, and that means a shift every time one is added to the other, twice a frame, for ever. And the check that catches all this now looks 1.5 million cycles in rather than twelve. The first number came from assuming a program that saves a whole screen takes a long time to start; it does not, and by twelve million the lander had flown seven hundred frames and left the picture. A capture near the beginning is worth more than a tuned one - there is less between it and the start that can move. 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
d6c81fa32c
commit
88ecb208f4
@@ -371,11 +371,30 @@ burnRight:
|
||||
CALL addWord
|
||||
RET
|
||||
|
||||
; Gravity, which is the same small number every frame and the reason this is a game.
|
||||
; ---- Gravity, which is the reason this is a game ----
|
||||
;
|
||||
; NOT EVERY FRAME. One sixteenth of a pixel per frame per frame is the smallest step this
|
||||
; arithmetic can take and it is still far too much - it crossed the screen in a second and
|
||||
; flew like a gas giant. So it is applied one frame in FallEvery, which divides the pull by
|
||||
; that much and costs a byte and a compare.
|
||||
;
|
||||
; The alternative was a finer unit for velocity than for position, which means a shift every
|
||||
; time one is added to the other, twice a frame, for ever. A counter is cheaper and it is one
|
||||
; byte to change while the feel is being found.
|
||||
fall:
|
||||
SETD.1 FallTick
|
||||
LDA.1
|
||||
DECA
|
||||
STA.1
|
||||
BNA fallDone
|
||||
SETD.1 FallEvery
|
||||
LDA.1
|
||||
SETD.1 FallTick
|
||||
STA.1
|
||||
SETD.0 SpeedDown
|
||||
SETD.1 Gravity
|
||||
CALL addWord
|
||||
fallDone:
|
||||
RET
|
||||
|
||||
; ---- Where it is now ----
|
||||
@@ -425,7 +444,7 @@ follow:
|
||||
SETD.0 PixelLow
|
||||
LDB.0
|
||||
CALL eighth
|
||||
OUTA 0x36 ; The column origin.
|
||||
OUTQ 0x36 ; The column origin. IN Q, because RET puts A back.
|
||||
RET
|
||||
|
||||
; ---- The lander, put where it now is ----
|
||||
@@ -514,7 +533,16 @@ toPixels:
|
||||
; Three turns right is a divide by eight, and a moon 1024 pixels round is 128 columns, so the
|
||||
; whole answer is in the low half and the high half is the bits that rotated out of it.
|
||||
;
|
||||
; B cannot be read. Adding nothing to it puts it in Q, which can be moved.
|
||||
; ---- And the answer comes back in Q ----
|
||||
;
|
||||
; It came back in A first, which is a lie a subroutine cannot tell: RET puts A back the way it
|
||||
; found it, so the caller wrote out the high byte of the position it had passed in. The fine
|
||||
; register was computed inline and was right, the coarse register was not, and the picture
|
||||
; scrolled smoothly within a cell and never advanced one. Q is the only register that crosses
|
||||
; a RET, which is why every answer in this program comes back in it.
|
||||
;
|
||||
; B cannot be read at all. Adding nothing to it puts it in Q, which is where this wanted to
|
||||
; be anyway.
|
||||
eighth:
|
||||
SHR
|
||||
SHR
|
||||
@@ -522,7 +550,6 @@ eighth:
|
||||
RSTA
|
||||
CCF
|
||||
ADD
|
||||
MVQA
|
||||
RET
|
||||
|
||||
waitFrame:
|
||||
@@ -550,14 +577,21 @@ SpeedAcross:
|
||||
SpeedDown:
|
||||
0x00 0x00
|
||||
|
||||
; ---- The four numbers the feel lives in ----
|
||||
;
|
||||
; Adjacent on purpose, because tuning them is what the first rung is for.
|
||||
Gravity:
|
||||
0x01 0x00
|
||||
FallEvery:
|
||||
0d6 ; Gravity one frame in six. Every frame was Jupiter.
|
||||
FallTick:
|
||||
0d1
|
||||
ThrustUp:
|
||||
0xEC 0xFF ; Twenty sixteenths upwards, which is minus twenty.
|
||||
0xF8 0xFF ; Eight sixteenths upwards, which is minus eight.
|
||||
ThrustLeft:
|
||||
0xF8 0xFF ; Eight sixteenths to the left.
|
||||
0xFC 0xFF ; Four to the left.
|
||||
ThrustRight:
|
||||
0x08 0x00
|
||||
0x04 0x00
|
||||
HalfScreen:
|
||||
0xA0 0x00 ; 160 pixels, which is half of a forty column screen.
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 984
|
||||
Lander.sbx 1009
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
blocks.script 343
|
||||
|
||||
@@ -15,7 +15,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 984
|
||||
Lander.sbx 1009
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
blocks.script 343
|
||||
|
||||
@@ -32,7 +32,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 984
|
||||
Lander.sbx 1009
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
blocks.script 343
|
||||
|
||||
@@ -22,7 +22,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 984
|
||||
Lander.sbx 1009
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
blocks.script 343
|
||||
|
||||
@@ -22,7 +22,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 984
|
||||
Lander.sbx 1009
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
blocks.script 343
|
||||
|
||||
@@ -108,7 +108,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 984
|
||||
Lander.sbx 1009
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
blocks.script 343
|
||||
|
||||
@@ -27,7 +27,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 984
|
||||
Lander.sbx 1009
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
blocks.script 343
|
||||
|
||||
@@ -15,7 +15,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 984
|
||||
Lander.sbx 1009
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
blocks.script 343
|
||||
|
||||
@@ -13,7 +13,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 984
|
||||
Lander.sbx 1009
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
blocks.script 343
|
||||
|
||||
@@ -22,7 +22,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 984
|
||||
Lander.sbx 1009
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
blocks.script 343
|
||||
|
||||
+13
-7
@@ -1525,17 +1525,23 @@ EOT
|
||||
# takes the screen, changes the mode, redefines tiles, fills all 3,200 cells of the map from a
|
||||
# terrain it generated, and moves a sprite over it every frame.
|
||||
#
|
||||
# The keys are a burn of the lifting thruster every sixteenth frame, which is more than the
|
||||
# gravity and so keeps the lander up long enough to be looked at. The cycle count is tuned to
|
||||
# catch it before it climbs off the top; if CosmOS's size shifts the boot far enough, the
|
||||
# lander check fails saying it is not on screen, which is a request to re-tune.
|
||||
# A burn of the lifting thruster every forty eighth frame, which is exactly the gravity: one
|
||||
# sixteenth of a pixel every sixth frame against eight every forty eighth. Exactly cancelling
|
||||
# the ACCELERATION still leaves an average velocity, so it drifts upward slowly and this is
|
||||
# caught early rather than left to settle.
|
||||
#
|
||||
# EARLY, and that is the point of 1.5 million cycles. This program starts almost at once - the
|
||||
# first guess at where to look was ten million cycles in, on the assumption that taking a
|
||||
# screen was expensive, and by then the lander had flown 700 frames and left the picture. A
|
||||
# capture near the start is worth far more than a tuned one: there is much less between it and
|
||||
# the beginning that can move.
|
||||
python3 -c "
|
||||
keys = b'Lander\n'
|
||||
for i in range(400):
|
||||
keys += b'\x80' + b'\x00' * 15
|
||||
open('$BUILD/lander.keys','wb').write(keys + b'\x00' * 4000)
|
||||
keys += b'\x80' + b'\x00' * 47
|
||||
open('$BUILD/lander.keys','wb').write(keys + b'\x00' * 8000)
|
||||
"
|
||||
timeout 30 "$EMU" --fast --cycles 12000000 --keyboard "$BUILD/lander.keys" \
|
||||
timeout 30 "$EMU" --fast --cycles 1500000 --keyboard "$BUILD/lander.keys" \
|
||||
--screen "$BUILD/lander.ppm" --disk "$ROOT/Tests/build/disks/cosmos.img" \
|
||||
--ram-disk 2048 "$BUILD/cosmos.bin" > "$BUILD/lander.out" 2>&1 || true
|
||||
read -r MOON SKY SHIP SHIPLEFT SHIPRIGHT <<EOT
|
||||
|
||||
Reference in New Issue
Block a user