An orbit that comes back round, out of gravity minus the swing

The relief version could never make one. It only ever SUBTRACTED from
gravity, so a lander a little too slow sank for ever and one a little
too fast rose for ever - nothing in it could turn a fall around, because
nothing in it ever pushed up. There was no periapse to have.

Gravity minus the swing outwards has a sign change in it, and that is
the whole mechanic. Below orbital speed the pull wins and the lander
falls; above it the swing wins and the lander climbs; at 64 sixteenths
they cancel and it circles. Falling buys sideways speed and climbing
spends it, so a fall carries the lander past orbital and turns into a
climb, and the climb pays it back and turns into a fall.

The trade is the quarter square multiply, because the rate has to be
the PRODUCT of the two speeds. Set by the vertical speed alone it drained
a climb to nothing, and any minimum to stop that became a trap the climb
spent its way into - measured freezing at 23 with a gate of 24 and at 3
with a gate of 4. With the product in it there is no gate: as the
sideways speed goes to nothing the trade stops by itself.

Half the product rather than a quarter or the high half. The high half
alone is nought below a product of 256, which is a dead patch exactly
where the turn begins, and a quarter still ran the lander into the roof
before it came round - the whole sky is about 145 pixels.

Measured, placed at 80 sideways and left alone: apoapse at -1024 with 59
sideways, periapse at -124 with 71, and round again at 165, 241, 299 and
369 ticks with no sign of decay. A period of about 22 seconds.

The ceiling also spends one sideways when it wipes a climb. Without that
it was a trap with no way out: the pin wipes the climb, so the trade sees
neither fall nor climb and never touches the speed that is pushing the
lander up. Measured pinned at the top with 117 sideways, unmoving, for
the whole of a four minute flight.

Tests/makedisks.sh needed the library path, since Lander now includes
math.asm, and the app went from 2,924 bytes to 4,651 - mostly the 1,022
byte table - which is what the listing expectations move for.
This commit is contained in:
Anachronaut
2026-09-03 19:15:50 -04:00
parent f5642f52b5
commit c514d5328e
13 changed files with 295 additions and 60 deletions
+259 -36
View File
@@ -66,6 +66,7 @@ start:
OUTA 0x34 OUTA 0x34
OUTA 0x38 ; No fraction of a cell downwards either. OUTA 0x38 ; No fraction of a cell downwards either.
CALL mulReady ; The quarter square table, once. The orbit below needs a product.
CALL putTiles CALL putTiles
CALL makeMoon CALL makeMoon
CALL carvePads CALL carvePads
@@ -662,64 +663,112 @@ fall:
LDA.0 LDA.0
BNA fallResting BNA fallResting
; ---- Orbit ---- CALL exchange
; ---- Orbit, which is gravity MINUS the swing outwards ----
; ;
; The faster it is going sideways, the less of the moon it feels. Not because gravity got ; The first version of this only ever SUBTRACTED from gravity, and that is why it never
; weaker - because at speed the surface is falling away underneath as fast as the lander is ; behaved like an orbit. Cancelling some of the pull can stop a fall from getting worse; it
; falling towards it, which is what an orbit IS. ; can never turn one round, because nothing in it ever pushes up. A lander a little too slow
; sank for ever and a lander a little too fast rose for ever, and both of them were right.
; ;
; A GRADIENT OUT OF INTEGER ARITHMETIC. Gravity is one sixteenth of a pixel a tick and there ; What actually happens is a difference between two things. Gravity pulls in and going round
; is nothing between that and nothing, so it cannot simply be scaled down. Instead the ; throws out, and which of them wins depends on the sideways speed:
; sideways speed is added into a byte every tick and the tick's gravity is skipped whenever
; that byte carries - so the fraction of ticks cancelled is the speed over 256, smoothly,
; with no multiply and no divide. At 256 sixteenths a frame it carries every time and the
; lander is in orbit.
; ;
; That is the linear approximation. The honest one is the square of the speed, which wants a ; below orbital speed the pull wins and the lander falls
; multiply this machine has not got; a table would give the curve if the feel ever asks for ; at orbital speed they cancel and the lander circles
; it. What is here is one add and one branch. ; above orbital speed the swing wins and the lander climbs
;
; That sign change is the whole mechanic. Falling buys sideways speed (see the exchange
; above), so a fall carries the lander past orbital speed and the swing turns it into a
; climb: a periapse. Climbing spends that speed back, drops it below orbital, and the pull
; turns the climb into a fall: an apoapse. Round and round, which is what an orbit is.
;
; ---- A gradient out of integer arithmetic ----
;
; The step is one sixteenth of a pixel a tick and there is nothing between that and nothing,
; so the strength cannot be scaled down directly. Instead the distance from orbital speed is
; added into a byte every tick and the step is taken only when that byte carries - so the
; fraction of ticks that act is the distance over 256, smoothly, with no divide.
;
; Times four, because the speed alone put orbit at a lateral speed no lander would ever
; reach. At four it lands on 64 sixteenths - four pixels a frame, which crosses the moon in
; about three seconds and takes ten seconds of holding a thruster to build. Something worked
; up to rather than stumbled into.
SETD.0 SpeedAcross SETD.0 SpeedAcross
CALL magnitude CALL magnitude
SETD.0 DriftHigh SETD.0 DriftHigh
LDA.0 LDA.0
BNA fallResting ; A whole high byte of it is far past orbital speed. BNA fallOutFull ; A whole high byte of it: far past orbital, and thrown outwards.
; ---- Four times the speed, and orbit at sixty four ----
;
; The speed alone made the relief a 256th a tick, so orbit wanted a lateral speed no lander
; would ever reach and the whole thing was invisible. Times four puts it at 64 sixteenths -
; four pixels a frame, which crosses the moon in about three seconds and takes ten seconds
; of holding a thruster to build. Something worked up to rather than stumbled into.
SETD.0 DriftLow SETD.0 DriftLow
LDA.0 LDA.0
INIB 0d64 INIB 0d64
CCF CCF
SUB SUB
BNC fallOrbiting ; No borrow, so it is at orbital speed and gravity is gone. BRC fallInward ; Borrowed, so it is short of orbital speed and the pull wins.
LDA.0 ; DP0 is still DriftLow, from the comparison above.
; ---- Above orbital speed, so the swing wins ----
MVQA ; Q is how far past, from the comparison just above.
CCF
SUB ; B is still 64, from that same comparison.
BNC fallOutFull
RSTB RSTB
SHL SHL
SHL ; ---- Times four, and it comes out in A ---- SHL ; Times four; A is the high half, so the answer comes out in A.
; BRI fallOutStep
; A is the HIGH half of the shift register, so a value in A with fallOutFull:
; nothing in B is already that value times 256 - and rotating left INIA 0xFF
; twice makes it times 1024, whose top byte is the value times fallOutStep:
; four. Written the other way first, reading B out of it, which is SETD.1 OrbitStep
; nought every time: the relief was always none and the whole STA.1
; mechanic did nothing at all. INIA 0x01
SETD.1 Outward
STA.1
BRI fallSwing
; ---- Below orbital speed, so the pull wins ----
fallInward:
MVQA ; Q is the shortfall, less its sign.
NOTA
MVQA
INCA
INIB 0d64
CCF
SUB
BNC fallInFull
RSTB
SHL
SHL
BRI fallInStep
fallInFull:
INIA 0xFF ; Standing still, which is the whole of gravity and no swing.
fallInStep:
SETD.1 OrbitStep
STA.1
RSTA
SETD.1 Outward
STA.1
fallSwing:
SETD.0 OrbitStep
LDA.0
SETD.1 OrbitAt SETD.1 OrbitAt
LDB.1 LDB.1
CCF CCF
ADD ADD
STQ.1 STQ.1
BRC fallResting ; Carried, so this tick's pull is what the speed cancelled. BNC fallResting ; No carry, so this tick is one of the ones that does nothing.
BRI fallPulled SETD.0 Outward
fallOrbiting: LDA.0
BRI fallResting BNA fallPushed
fallPulled:
SETD.0 SpeedDown SETD.0 SpeedDown
SETD.1 Gravity SETD.1 Gravity
CALL addWord CALL addWord
BRI fallResting
fallPushed:
SETD.0 SpeedDown
SETD.1 Lift
CALL addWord
fallResting: fallResting:
; ---- And whatever is being leaned on, on the same tick ---- ; ---- And whatever is being leaned on, on the same tick ----
@@ -773,6 +822,142 @@ fallNotLeft:
fallDone: fallDone:
RET RET
; ---- Falling buys sideways speed; climbing spends it ----
;
; What the first orbit was missing. Cancelling gravity by speed lets a fast lander stop
; falling and gives it no way back: a little too slow and it sank for ever, faster and faster,
; because ALTITUDE DID NOTHING. Nothing turned height into speed or speed into height.
;
; A real orbit is that exchange. Falling towards periapse trades height for speed, which buys
; more relief, which turns the fall into a climb; climbing towards apoapse pays it back and
; the climb becomes a fall. That cycle is the whole character of an orbit.
;
; ---- And the rate is the PRODUCT, which is why this needed a multiply ----
;
; The first attempt traded at a rate set by the vertical speed alone, and it drained a climb
; all the way to nought: a lander that climbed long enough had no sideways speed left to buy
; relief with, and sank. Gating it above a minimum made it worse - the climb spent its way
; down to the gate and locked itself out, frozen there for ever.
;
; The honest rate is proportional to BOTH speeds, and that is what fixes it: as the sideways
; speed falls towards nought the trade stops by itself. The product is its own gate, and no
; threshold is needed or wanted.
exchange:
SETD.0 SpeedAcross
CALL magnitude
SETD.0 DriftHigh
LDA.0
BNA exchangeQuick ; A whole high byte of sideways speed: take the largest step.
SETD.0 DriftLow
LDA.0
SETD.1 MulA
STA.1
SETD.0 DriftLeftward
LDA.0
SETD.1 AcrossWasLeft
STA.1 ; Which way it is going, before the next magnitude overwrites it.
SETD.0 SpeedDown
CALL magnitude
SETD.0 DriftHigh
LDA.0
BNA exchangeQuick
SETD.0 DriftLow
LDA.0
SETD.1 MulB
STA.1
CALL mul8
; ---- Half the product ----
;
; The product reaches 65,025 and the accumulator is a byte, so what is wanted is a window
; into the middle of it. The high half alone was tried first and it was both too weak and
; too coarse: a lander at 80 sideways falling at 2 has a product of 160, whose high half is
; nought, so the trade did not happen at all until the fall was already fast. A dead patch
; right where the turn is supposed to begin.
;
; Half keeps the low bits, so the trade starts as soon as there is any fall at all, and it
; is eight times stronger besides. That is what closes the loop inside the height of the
; screen rather than somewhere off the top of it: the whole sky here is about 145 pixels
; between the ground and the ceiling, and a quarter - tried in between - still ran the
; lander into the roof before the turn came round.
SETD.0 MulHigh
LDA.0
INIB 0d02
CCF
SUB
BNC exchangeQuick ; 512 or more, which saturates the step whatever the low half.
RSTB
SHL
SHL
SHL
SHL
SHL
SHL
SHL ; Times a hundred and twenty eight, which comes out in A.
SETD.1 ExchangeStep
STA.1
SETD.0 MulLow
LDA.0
RSTB
SHR ; And the low half halved, which is the rest of the window.
LDB.1 ; DP1 still names the step, from the store just above.
CCF
ADD ; At most 128 plus 127, so this cannot carry.
MVQA
BRI exchangeAdd
exchangeQuick:
INIA 0xFF
exchangeAdd:
SETD.1 ExchangeAt
LDB.1
CCF
ADD
STQ.1
BNC exchangeDone ; No carry, so there is nothing to trade this tick.
; ---- Which way the step goes ----
;
; Falling grows the sideways speed and climbing shrinks it, which is a step AWAY from nought
; or TOWARDS it depending on which way the lander is already going. Both signs are the top
; bit a magnitude left behind, so the exclusive or of them is the whole decision: the same
; means grow, different means shrink.
SETD.0 DriftLeftward
LDA.0
SETD.1 AcrossWasLeft
LDB.1
XOR
BNQ exchangeSlower
SETD.0 SpeedAcross
SETD.1 StepUp
CALL addWord
RET
exchangeSlower:
SETD.0 SpeedAcross
SETD.1 StepDown
CALL addWord
exchangeDone:
RET
; One sideways, towards standing still, whichever way it is going.
spendAcross:
SETD.0 SpeedAcross
CALL magnitude
SETD.0 DriftLeftward
LDA.0
BNA spendAcrossLeft
SETD.0 SpeedAcross
SETD.1 StepDown
CALL addWord
RET
spendAcrossLeft:
SETD.0 SpeedAcross
SETD.1 StepUp
CALL addWord
RET
; ---- One unit, if there is one ---- ; ---- One unit, if there is one ----
; ;
; Q comes back nought when the tank is empty, and the thruster that asked does not fire. There ; Q comes back nought when the tank is empty, and the thruster that asked does not fire. There
@@ -888,6 +1073,20 @@ moveAdrift:
SETD.0 SpeedDownHigh SETD.0 SpeedDownHigh
STA.0 STA.0
; ---- And the sideways speed that bought the climb ----
;
; Without this the ceiling was a trap with no way out. A lander held here has its climb
; wiped every time, so the exchange below sees no fall and no climb, trades nothing, and the
; sideways speed that is pushing it up NEVER CHANGES: above orbital speed the swing pushes
; up, the pin wipes it, and round again for ever. Measured: pinned at the top with 117
; sideways, unmoving, for the whole of a four minute flight.
;
; So the pin spends one sideways as well, which is only reached on the ticks the lander is
; actually trying to climb - about one frame in ten. Six seconds of scraping along the roof
; brings it back under orbital speed and it falls away on its own. Pushing against the top
; of the sky costs the speed that got you there.
CALL spendAcross
moveAdriftSaid: moveAdriftSaid:
; Said once rather than sixty times a second. ; Said once rather than sixty times a second.
SETD.0 Adrift SETD.0 Adrift
@@ -1816,6 +2015,28 @@ AtBase:
; of it is a tick of gravity that never happened. ; of it is a tick of gravity that never happened.
OrbitAt: OrbitAt:
0x00 0x00
; How hard the swing is pulling or pushing this tick, and which way. And one step upwards,
; which is the only thing here that gravity did not already need.
OrbitStep:
0x00
Outward:
0x00
Lift:
0xFF 0xFF
; Where the height-for-speed trade has got to, and which way the lander was going when the
; product was worked out. One step of sideways speed, either way.
ExchangeAt:
0x00
ExchangeStep:
0x00
AcrossWasLeft:
0x00
StepUp:
0x01 0x00
StepDown:
0xFF 0xFF
SayAt: SayAt:
0x00 0x00
@@ -1862,3 +2083,5 @@ LanderArt:
LanderArtAt: LanderArtAt:
#Reserve 0d2 #Reserve 0d2
#Include math.asm
+1 -1
View File
@@ -25,7 +25,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2924 Lander.sbx 4651
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -15,7 +15,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2924 Lander.sbx 4651
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -32,7 +32,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2924 Lander.sbx 4651
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -22,7 +22,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2924 Lander.sbx 4651
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -22,7 +22,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2924 Lander.sbx 4651
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -108,7 +108,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2924 Lander.sbx 4651
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -27,7 +27,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2924 Lander.sbx 4651
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -15,7 +15,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2924 Lander.sbx 4651
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -13,7 +13,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2924 Lander.sbx 4651
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -22,7 +22,7 @@ Mode.sbx 48
Flip.sbx 173 Flip.sbx 173
Sprite.sbx 442 Sprite.sbx 442
Depth.sbx 672 Depth.sbx 672
Lander.sbx 2924 Lander.sbx 4651
Pad.sbx 264 Pad.sbx 264
Crash.sbx 632 Crash.sbx 632
vars.script 50 vars.script 50
+1 -1
View File
@@ -146,7 +146,7 @@ for i in 1 2 3 4 5 6 7 8; do "$TOOL" put "$DISKS/sbfs.img" "filler$i.txt" >/dev/
"$TOOL" put "$DISKS/cosmos.img" "$WORK/Depth.sbx" >/dev/null "$TOOL" put "$DISKS/cosmos.img" "$WORK/Depth.sbx" >/dev/null
# Lander.sbx is Lunar Porter, and the biggest program on the disk. It takes the screen, the # Lander.sbx is Lunar Porter, and the biggest program on the disk. It takes the screen, the
# mode and the map, so it is also the one that says a program can have all three back. # mode and the map, so it is also the one that says a program can have all three back.
"$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" \ "$ROOT/Assembler" -I "$ROOT/Programs/Libraries" -I "$ROOT/Programs/CosmOS/Source" \
"$ROOT/Programs/CosmOS/Apps/Lander.asm" -o "$WORK/Lander.sbx" >/dev/null "$ROOT/Programs/CosmOS/Apps/Lander.asm" -o "$WORK/Lander.sbx" >/dev/null
"$TOOL" put "$DISKS/cosmos.img" "$WORK/Lander.sbx" >/dev/null "$TOOL" put "$DISKS/cosmos.img" "$WORK/Lander.sbx" >/dev/null
# Pad.sbx says what the controllers are doing, which is the only way to tell a pad nobody # Pad.sbx says what the controllers are doing, which is the only way to tell a pad nobody
+25 -13
View File
@@ -1790,21 +1790,33 @@ HOME="$(countColour "$BUILD/ceiling22000000.ppm" f0f0f0)"
# ---- Orbit, which is measured and not checked here ---- # ---- Orbit, which is measured and not checked here ----
# #
# Going sideways lifts the moon off you: four times the lateral speed goes into a byte every # Gravity minus the swing outwards. Below orbital speed the pull wins and the lander falls;
# tick and the tick's gravity is skipped when it carries. It works, and it was measured # above it the swing wins and the lander climbs; and falling buys sideways speed while
# working - climbing at 13, 26, 39 sixteenths without lateral speed and 14, 31, 50 with it, # climbing spends it. That is what makes a closed orbit rather than a one way trip, and it
# the gap widening as the speed builds. # was measured doing it. Placed at 80 sideways and left alone, turning points at:
# #
# THERE IS NO CHECK FOR IT, and that is deliberate rather than forgotten. Every window in # tick 44 height -1024 sideways 59 apoapse, and it falls away
# which the difference is visible turned out to be a few frames wide: hold the thruster and # tick 123 height -124 sideways 71 periapse, and it climbs again
# both landers are pinned against the ceiling with their climbs spent; ease off and both land # tick 165 height -1024 sideways 59
# and freeze; and either way the comparison becomes two nothings. A version of this check did # tick 241 height -124 sideways 70
# exist and passed against one disk and failed against another, which is a check that is # tick 299 height -1024 sideways 59
# measuring the boot time rather than the physics. # tick 369 height -114 sideways 70
# #
# What would make it checkable is a way to put the lander somewhere with a velocity, without # Four hundred and eighty ticks with no sign of the amplitude decaying, a period of about a
# flying it there - the same thing the delivery below wants. Until then this is verified by # hundred and thirty ticks, which is twenty two seconds of play.
# somebody measuring it, and said so here rather than left looking tested. #
# THERE IS STILL NO CHECK FOR IT, and it is still deliberate. The measurement above was taken
# with a build that PLACED the lander at eighty sideways, because reaching orbital speed
# through the controls takes a sustained burn while holding height, and the phase of that burn
# against the gravity tick - one frame in ten - decides whether the thruster is seen at all.
# Two pad files a frame apart fly differently. That is a fine thing for a game and a poor one
# for a check.
#
# What would make it checkable is still the same thing: a way to put the lander somewhere with
# a velocity, without flying it there, which is now wanted by four separate untested paths.
# The other candidate is a hand flown recording of an orbit, the way the delivery below is a
# hand flown delivery. Until one of those exists this is verified by somebody measuring it,
# and said so here rather than left looking tested.
# ---- A delivery, flown by hand, and what it cost ---- # ---- A delivery, flown by hand, and what it cost ----
# #