A base speaks in the window, not into the world

Two bugs with one cause. The console draws into the map, so a message
printed while flying was a message the lander then flew over - and
printing scrolls, so every one of them moved the whole world up a row. The
window is at a screen position and forty cells wide, and neither is true
of it.

So the window is two rows now: the gauge, and whatever there is to say.
The letters are ordinary tiles, because the character generator starts at
the space and glyph n is character n less thirty two. The rest of the row
is blanked after every message, or a short one would leave the tail of a
long one behind it.

Opening the throttle wipes the line, because a message that outlived the
moment would be read as describing this one.

The crash still goes to the console, deliberately: it is the last thing
the program says and it should survive the program.

A or Start continues from a message as readily as a key does. Somebody
flying on a controller should not have to reach for the keyboard to say
they have read something.

AND TWO TESTS WENT WITH IT, which is the interesting part. cosmosLanderSoft
and cosmosLanderPadOne asserted on lines in a transcript, and the lines
moved off the console - so both went on passing while checking nothing at
all. A test that asserts a side effect rather than the thing itself is
always one refactor from being decorative. What they were for is now
checked in the picture, where the message actually is.

The lander check moved earlier too. The window grew to two rows, so by 1.5
million cycles the lander had climbed behind the status bar - the window
doing exactly what it should, and leaving the check counting six pixels of
a forty pixel lander.

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 12:23:12 -04:00
co-authored by Claude Opus 5
parent bfb515e23b
commit caf5e1f99d
17 changed files with 146 additions and 47 deletions
+81 -17
View File
@@ -457,8 +457,8 @@ putLander:
; The letters are ordinary tiles. The character generator starts at the space, so glyph n is
; character n plus thirty two - which makes F, U, E and L into 38, 53, 37 and 44.
putGauge:
INIA 0x01
OUTA 0x3D ; One row tall.
INIA 0x02
OUTA 0x3D ; Two rows: the gauge, and whatever there is to say.
RSTA
OUTA 0x3E ; At the top of the screen.
@@ -680,9 +680,14 @@ fallResting:
BRQ fallNotUp
CALL takeFuel
BRQ fallNotUp ; Nothing in the tank, so nothing out of the engine.
RSTA
SETD.0 Landed
LDA.0
BRA fallAlreadyUp
RSTA
STA.0 ; Lifting off, which is the only way to stop being landed.
SETD.0 EmptyText
CALL sayInWindow ; And whatever the base said goes with it.
fallAlreadyUp:
SETD.0 SpeedDown
SETD.1 PadUp
CALL addWord
@@ -1103,13 +1108,24 @@ touchdownStop:
touchdownDone:
RET
; A key, asked for rather than waited on, so one already pressed counts.
; ---- A key, or a button ----
;
; Somebody flying on a controller should not have to reach for the keyboard to say "yes, I
; read that". A or Start does it, and so does any key - asked for rather than waited on, so
; one already pressed counts.
waitKey:
CALL readPad
SETD.0 Held
LDA.0
INIB 0x50 ; A, or Start.
AND
BNQ waitKeyDone
INA 0x01
INIB 0x01 ; READY
AND
BRQ waitKey
INA 0x00
waitKeyDone:
RET
; ---- Put down exactly on the ground ----
@@ -1140,6 +1156,53 @@ restOnSurface:
STA.0
RET
; ---- A line of text, into the window ----
;
; DP0 names a string. It goes into window row one and the rest of the row is BLANKED, so a
; short message never leaves the tail of a long one behind it.
;
; Into the window rather than out of the console, which is the whole of two bugs at once. The
; console draws into the map, so a message printed while flying was a message the lander then
; flew over - and printing scrolls, so every one of them moved the whole world up a row.
; The window is at a screen position and forty cells wide, and neither is true of it.
;
; The letters are ordinary tiles: the character generator starts at the space, so glyph n is
; character n less thirty two.
sayInWindow:
INIA 0d5
OUTA 0xE3
INIA 0xC1
OUTA 0xE4
RSTA
OUTA 0xE5 ; Window row one begins at 0xC100.
SETD.1 SayAt
STA.1
sayCell:
LDA.0
BRA sayBlank ; The string has ended, so the rest of the row is nothing.
INIB 0d32
CCF
SUB
OUTQ 0xE9
INIA 0x07
OUTA 0xE9 ; Scheme seven, white, which is nothing else on this screen.
INCD.0
BRI sayNext
sayBlank:
OUTA 0xE9
OUTA 0xE9 ; A is nought here, and DP0 stays put so it stays nought.
sayNext:
SETD.1 SayAt
LDA.1
INCA
STA.1
INIB 0d40
CCF
SUB
BNQ sayCell
RET
; ---- What a base does when a lander arrives ----
;
; Empty and at a base: it hands over cargo for the base ACROSS THE MOON, two along, so the
@@ -1172,8 +1235,8 @@ arrive:
STA.0 ; And DP0 is still Carrying, from reading it.
CALL payFuel
SETD.0 DeliveredText
SWI osPrintString
BRI arriveSaid
CALL sayInWindow
RET
arriveLoad:
; Two along, round a moon of four bases, which is the far side of it.
@@ -1190,21 +1253,17 @@ arriveLoad:
SETD.0 Carrying
STA.0
SETD.0 LoadedText
SWI osPrintString
BRI arriveSaid
CALL sayInWindow
RET
arriveWrong:
SETD.0 WrongText
SWI osPrintString
BRI arriveSaid
CALL sayInWindow
RET
arriveNowhere:
SETD.0 LandedText
SWI osPrintString
arriveSaid:
INIA 0x0A
OUTA 0x00
CALL sayInWindow
RET
; ---- Paid ----
@@ -1475,6 +1534,8 @@ LandColumn:
0x00
AtBase:
0x00
SayAt:
0x00
; ---- What is aboard, and whether the feet are down ----
;
@@ -1486,14 +1547,17 @@ Carrying:
Landed:
0x00
; ---- Forty cells is the row, so forty characters is the limit ----
LandedText:
"Down in the middle of nowhere. Nothing here."
"Nowhere in particular. Nothing here."
LoadedText:
"Loaded. Take it to the base across the moon."
"Loaded. Take it across the moon."
DeliveredText:
"Delivered. Eighty units of fuel."
WrongText:
"Not the base this cargo is for."
EmptyText:
0x00
CrashedText:
"Crashed."
+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. |
| 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. |
| 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. |