Orbit, and two bars that answer instead of reporting

Going sideways lifts the moon off you. Not because gravity weakened -
because at speed the surface falls away underneath as fast as the lander
falls towards it, which is what an orbit is.

A GRADIENT OUT OF INTEGER ARITHMETIC. Gravity is one sixteenth of a pixel
a tick and there is nothing between that and nothing, so it cannot be
scaled down. Instead four times the sideways speed goes into a byte every
tick and the tick's gravity is skipped whenever that byte carries: the
fraction cancelled is the speed over 64, smoothly, with no multiply and no
divide. At four pixels a frame it carries every time. That is the linear
approximation; the honest one is the square, and wants a table.

It did nothing at all for its first two versions. Once because the relief
was a 256th a tick, so orbit wanted a speed no lander would reach; and
once because A IS THE HIGH HALF of the shift register, so multiplying by
four left the answer in A while the code read B, which is nought. The same
trap as the scroll register and the pixel conversion before it.

And a bar for the vertical speed beside the one for drift, both GREEN
WHILE A LANDING WOULD SURVIVE AND RED WHILE IT WOULD NOT. That turns two
numbers into one question - can I put down - and answers it at a glance.

WHAT THIS COST: the flown delivery check. A recording is a list of buttons
and not a flight, so replaying it under different gravity flies somewhere
else; the delivery became a crash two columns short. The fixture is still
there and is still a faithful record of what somebody did, and is no
longer a record of what happens.

That is the standing cost of a flown fixture, and it is worse than the
transcript tests dropped earlier: those broke when an output moved, and
this breaks whenever a NUMBER moves. Making the delivery reachable without
flying - a way to start already carrying, or at a chosen base - is what
would fix it properly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-09-03 16:25:56 -04:00
co-authored by Claude Opus 5
parent 4b1c3d8e3f
commit a069ee7a00
13 changed files with 270 additions and 40 deletions
+200 -2
View File
@@ -96,6 +96,7 @@ everyFrame:
CALL touchdown
CALL showLander
CALL showDrift
CALL showFall
CALL showFuel
SETD.0 Flying
LDA.0
@@ -660,6 +661,62 @@ fall:
SETD.0 Landed
LDA.0
BNA fallResting
; ---- Orbit ----
;
; The faster it is going sideways, the less of the moon it feels. Not because gravity got
; weaker - because at speed the surface is falling away underneath as fast as the lander is
; falling towards it, which is what an orbit IS.
;
; A GRADIENT OUT OF INTEGER ARITHMETIC. Gravity is one sixteenth of a pixel a tick and there
; is nothing between that and nothing, so it cannot simply be scaled down. Instead the
; 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
; multiply this machine has not got; a table would give the curve if the feel ever asks for
; it. What is here is one add and one branch.
SETD.0 SpeedAcross
CALL magnitude
SETD.0 DriftHigh
LDA.0
BNA fallResting ; A whole high byte of it is far past orbital speed.
; ---- 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
LDA.0
INIB 0d64
CCF
SUB
BNC fallOrbiting ; No borrow, so it is at orbital speed and gravity is gone.
LDA.0 ; DP0 is still DriftLow, from the comparison above.
RSTB
SHL
SHL ; ---- Times four, and it comes out in A ----
;
; A is the HIGH half of the shift register, so a value in A with
; nothing in B is already that value times 256 - and rotating left
; twice makes it times 1024, whose top byte is the value times
; four. Written the other way first, reading B out of it, which is
; nought every time: the relief was always none and the whole
; mechanic did nothing at all.
SETD.1 OrbitAt
LDB.1
CCF
ADD
STQ.1
BRC fallResting ; Carried, so this tick's pull is what the speed cancelled.
BRI fallPulled
fallOrbiting:
BRI fallResting
fallPulled:
SETD.0 SpeedDown
SETD.1 Gravity
CALL addWord
@@ -809,6 +866,33 @@ showLander:
OUTA 0xE9
RET
; ---- Green if it would survive, red if it would not ----
;
; The magnitude is in DriftLow and DriftHigh and DP1 names the limit. Q comes back the
; attribute to draw with, which turns a bar from a number into an ANSWER: a person aiming at a
; pad does not want to know their speed, they want to know whether they can put it down.
barColour:
SETD.0 DriftHigh
LDA.0
BNA barColourFast ; A whole high byte of speed is past any limit worth having.
SETD.0 DriftLow
LDA.0
LDB.1
CCF
SUB
BNC barColourFast ; No borrow, so it is at or over the limit.
INIA 0x02
RSTB
CCF
ADD ; Scheme two, green, which is within tolerance.
RET
barColourFast:
INIA 0x01
RSTB
CCF
ADD ; Scheme one, red, which is not.
RET
; ---- How fast sideways, as a bar ----
;
; A moon has no air, so a drift never stops by itself and stopping one means cancelling the
@@ -862,6 +946,10 @@ magnitudeDone:
showDrift:
SETD.0 SpeedAcross
CALL magnitude
SETD.1 GentleAcross
CALL barColour
SETD.1 DriftColour
STQ.1
; ---- 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
@@ -921,8 +1009,9 @@ driftAt:
OUTA 0xE5 ; Sprite one begins sixteen bytes in.
INIA 0xC8
OUTA 0xE9 ; The ground block, which is a solid tile.
INIA 0x01
OUTA 0xE9 ; Attribute one, so the bar is not the colour of the moon.
SETD.0 DriftColour
LDA.0
OUTA 0xE9 ; Green while this drift could be landed with, red while not.
SETD.0 DriftAt
LDA.0
OUTA 0xE9
@@ -1285,6 +1374,102 @@ payFuelFull:
STA.0
RET
; ---- How fast downwards, as a bar down the side ----
;
; The same idea stood on its end. Sprite two, three pixels wide, growing DOWN from the middle
; of the screen while falling and UP while climbing - so which way is as plain as how fast,
; and a lander holding its height has no bar at all.
;
; Two bars and two limits is the whole instrument panel: when both are green the lander can be
; put down, and that is a question a person can answer at a glance instead of by counting.
showFall:
SETD.0 SpeedDown
CALL magnitude
SETD.1 GentleDown
CALL barColour
SETD.1 FallColour
STQ.1
; Clamped, and to less than the drift bar because there is half a screen either way.
SETD.0 DriftHigh
LDA.0
BNA fallBarWide
SETD.0 DriftLow
LDA.0
INIB 0d90
CCF
SUB
BRC fallBarReady
fallBarWide:
INIA 0d90
SETD.0 DriftLow
STA.0
fallBarReady:
; No tiles at all when there is nothing to show, which is the off switch: a target size of
; nought is the NATURAL size, not an empty sprite.
SETD.0 DriftLow
LDA.0
BRA fallBarEmpty
INIA 0x11
BRI fallBarSized
fallBarEmpty:
RSTA
fallBarSized:
SETD.1 FallSize
STA.1
; Where it starts: the middle going down, and that much back going up.
INIA 0d100
SETD.0 DriftLeftward
LDB.0
BRB fallBarAt ; Nought is downward, so it starts at the middle.
SETD.0 DriftLow
LDB.0
CCF
SUB
MVQA
fallBarAt:
SETD.1 FallAt
STA.1
INIA 0d4
OUTA 0xE3
INIA 0xC0
OUTA 0xE4
INIA 0x20
OUTA 0xE5 ; Sprite two begins thirty two bytes in.
INIA 0xC8
OUTA 0xE9 ; The solid block.
SETD.0 FallColour
LDA.0
OUTA 0xE9
INIA 0x2C
OUTA 0xE9
INIA 0x01
OUTA 0xE9 ; X is 300, which is two bytes: the screen is wider than one.
SETD.0 FallAt
LDA.0
OUTA 0xE9
RSTA
OUTA 0xE9 ; Y.
SETD.0 FallSize
LDA.0
OUTA 0xE9
RSTA
OUTA 0xE9 ; No flags.
INIA 0x03
OUTA 0xE9
RSTA
OUTA 0xE9 ; Three pixels wide.
SETD.0 DriftLow
LDA.0
OUTA 0xE9
RSTA
OUTA 0xE9 ; And as tall as the lander is fast.
OUTA 0xE9 ; No depth.
RET
; ---- Sums ----
;
; DP0 names the two byte value being changed, low half first, and DP1 the one being added to
@@ -1455,6 +1640,14 @@ DriftAt:
0x00
DriftSize:
0x00
DriftColour:
0x00
FallColour:
0x00
FallSize:
0x00
FallAt:
0x00
; ---- The tank ----
;
@@ -1534,6 +1727,11 @@ LandColumn:
0x00
AtBase:
0x00
; Where the orbit sum has got to. The sideways speed goes in here every tick and the carry out
; of it is a tick of gravity that never happened.
OrbitAt:
0x00
SayAt:
0x00
+1 -1
View File
@@ -727,7 +727,7 @@ from every assembly file in it. Several are old programs written for the bare ma
| Grid | The first program to use the screen as a screen. Redefines a tile above the font, fills all 128 map rows, and scrolls it diagonally a pixel at a time. |
| Sprite | Moves a ball across the shell's own text, writing not one byte of the map to do it. It leaves the sprite in the table on the way out, because clearing them is the system's job - see below. |
| Pad | Says what the controllers are doing, printing a line whenever one changes. It tells apart the three things that look identical from inside a game that is not responding: a pad nobody noticed, a pad mapped to nothing, and a mapping that is wrong. |
| Lander | Lunar Porter, rung one: a lander over a moon that wraps. Flown with a controller if there is one - a held thruster burns every tick it is held for - and with the arrow keys if there is not, where one press is one burn and that is the most the console can say. A bar at the bottom is the sideways drift, drawn as a sprite stretched to the speed - a moon has no air, so a drift never stops by itself and stopping one means cancelling it exactly, which is hard to do blind. It lands or crashes on arrival, and what decides is the speed at the moment it touches: gentler than three quarters of a pixel a frame downwards and half of one sideways, or it is a lander on its side. A fuel gauge sits in the window layer, where the moon turning underneath cannot scroll it away, and every thruster costs a unit of fuel every tick it fires. An empty tank is not an ending: it is a lander still flying that can no longer do anything about where. Four landing pads are carved into the moon after it is generated, levelled to whatever height their first column happened to have, and marked in cyan by an attribute rather than a tile of their own. The lander starts above one, because that is where a porter's day begins. Each pad is a base, told apart by the colour it is drawn in, and landing at one either loads cargo for the base across the moon or delivers what is aboard and pays eighty units of fuel. A landing is not an ending: the lander rests where it is until the throttle opens again, and A or Start says "read it" as readily as a key does. What a base says goes in the window rather than out of the console: the console draws into the map, so a message printed while flying is one the lander then flies over, and printing scrolls the whole world up a row. Opening the throttle wipes the line. |
| Lander | Lunar Porter, rung one: a lander over a moon that wraps. Flown with a controller if there is one - a held thruster burns every tick it is held for - and with the arrow keys if there is not, where one press is one burn and that is the most the console can say. A bar at the bottom is the sideways drift, drawn as a sprite stretched to the speed - a moon has no air, so a drift never stops by itself and stopping one means cancelling it exactly, which is hard to do blind. It lands or crashes on arrival, and what decides is the speed at the moment it touches: gentler than three quarters of a pixel a frame downwards and half of one sideways, or it is a lander on its side. A fuel gauge sits in the window layer, where the moon turning underneath cannot scroll it away, and every thruster costs a unit of fuel every tick it fires. An empty tank is not an ending: it is a lander still flying that can no longer do anything about where. Four landing pads are carved into the moon after it is generated, levelled to whatever height their first column happened to have, and marked in cyan by an attribute rather than a tile of their own. The lander starts above one, because that is where a porter's day begins. Each pad is a base, told apart by the colour it is drawn in, and landing at one either loads cargo for the base across the moon or delivers what is aboard and pays eighty units of fuel. A landing is not an ending: the lander rests where it is until the throttle opens again, and A or Start says "read it" as readily as a key does. What a base says goes in the window rather than out of the console: the console draws into the map, so a message printed while flying is one the lander then flies over, and printing scrolls the whole world up a row. Opening the throttle wipes the line. Two bars read the speeds and are green while a landing would survive and red while it would not, so "can I put down" is a glance rather than a sum. And going sideways lifts the moon off you: at four pixels a frame the surface falls away as fast as the lander falls towards it, and gravity stops arriving. |
| Depth | Four pillars at four distances and a ball walking past them, behind the near ones and in front of the far ones. The ball is sprite nought and every pillar is numbered after it, so table order puts it in front of all four - what actually decides is the depth buffer, asked a column at a time. |
| Flip | Draws a whole screen into the bank that is not being shown, waits, and then shows it in one byte out of one port. It writes nothing else at all - not a tile, not a colour - so it does not ask for the screen to be saved, and the line it printed is still there when it comes back. It deliberately does not put the displayed screen back either, because that is the system's to restore: a program that faulted while flipped could not have. |
| Edit | A line editor. |