Commodore 64 · 1982 · Commodore and HAL Laboratory

Jupiter Lander

Five kilobytes of code, and a landing test that does not agree with the gauge it asks you to read.

01 · The screen

One frame, rebuilt from memory

Not a screenshot. Every pixel below is computed from three pieces of memory: the 1000 bytes of screen, the 1000 nibbles of colour, and the character set the game installs at $3800. The lander over it is sprite 1, drawn from its own 63 bytes.

rock, colour 2 gauge, colour 5 safe speed, colour 7 lander sprite, colour 3

320×200 · 40 columns by 25 rows of 8×8 characters. The terrain painter writes 38 of those columns. The last two belong to the speed gauge, and the bottom two rows to the score and the fuel bar.

02 · The cast

Sixteen sprite shapes, and one of them is the game

The character set is 2 KB, which is 256 glyphs. This game uses 128. The other half of the block holds the sixteen sprite shapes, addressed through the sprite pointers instead of through $D018, and the two never collide because no screen cell ever selects a code above $7F.

Click any shape to see it large. Slots $FC to $FF are empty; they are the tail of the 2 KB block and cost nothing.

Seven frames from one pointer

Hitting rock does not swap the ship for an explosion. It adds one to the sprite pointer, seven times, and cycles the sprite colour between frames.

$E46F lda #$07 $E471 sta boom_frame $E473 inc $07F9 ; the sprite pointer $E481 jsr flash_explosion_colour $E484 dec boom_frame $E486 bne $E473

Three sprites in the same place

The flames are not drawn into the hull. They are sprites 2 and 3, sitting at exactly the same coordinates as sprite 1 and switched on only while a thruster is held. Sprite 2 alternates between two shapes on every update, which is what makes the main engine flicker.

Sprite 1 is cyan, sprite 2 red, sprite 3 orange, and none of those colours is ever changed except during an explosion. Double size is the whole of the game's zoom: $D017 and $D01D both get $0E.

03 · Controls

Three keys, and A does not mean left

The instruction screen prints LEFT-A and D-RIGHT. Those labels name the thruster that fires, not the direction you travel. Firing the left thruster pushes you right.

A
F1
D
joystick 1: left  ·  fire  ·  right
Hold a key, or use the arrow keys and space.

The arrow shows the acceleration the held key applies to the ship this pass. Gravity is left out here; it is added separately and always.

One bit for two devices

F1 and the port 1 fire button are the same bit of the same register. Keyboard row 0 is selected by writing $FE to $DC00, and F1 then reads as bit 4 of $DC01 . That bit is also where the joystick's fire button lands. The game never has to choose between them.

$EB9E lda #$fe $EBA0 sta $dc00 ; select keyboard row 0 $EBA3 lda $dc01 ; read it back $EBA6 cmp $dc01 ; twice, until two reads agree $EBA9 bne $EBA3 $EBAB and #$10 ; bit 4: F1, and fire

The joystick is read first, with every keyboard row driven high so no key can show through. Only if the stick is completely idle does the keyboard get scanned, and then only for A, D and F1. D is tested last, so holding A and D together thrusts right.

04 · The world

Four hand-drawn screens

Fly near a landing site and the view snaps to a close-up. Nothing about the terrain is scaled. The game holds four separate pictures and flying between zones swaps which one is painted. The only thing that changes size is the lander, doubled by the video chip.

Every view is a run-length stream of count-and-character pairs, laid out 38 cells to a row and stopping on a count of zero. 924 bytes for 3496 cells.

The badge in the rock

All four streams carry the same four glyphs, $0C to $0F, arranged as a two-by-two block. It is a Commodore badge cut into the cliff, and it is terrain data rather than an overlay: the painter has no idea it is drawing a logo.

Where the pads are

A pad is one line in the world. The test for standing on one compares both bytes of the ship's Y position for equality, and the X range is an ordinary bounds check.

PadWorld YWorld XMultiplier
Left$016A$00B0 – $00E05
Middle plateau$009A$0160 – $01902
Right$017A$01E0 – $020010

Descend faster than one world unit per pass and the ship's Y can step over that line without ever equalling it. The game never sees a landing at all, and the ship dies on the rock a moment later.

All three numbers are placed to the pixel. Put the lander on a pad's line and run the same sprite-against-character test the video chip runs, and it is clear by a hair. Move it one world unit lower and the legs are inside the rock, on all three pads.

World Y relative to the padx5 padx2 padx10 pad
4 belowin rockin rockin rock
1 belowin rockin rockin rock
the pad's own lineclearclearclear
1 aboveclearclearclear

Run in this page against the decoded terrain and the sprite bitmap, not measured in the emulator. The pad line is the lowest position at which the lander does not touch.

05 · The flight model

Fly it

The physics is a handful of additions. What follows is the same arithmetic at the same rate with the same constants, over the terrain decoded from the game and against the same sprite-and-character collision test the video chip performs. Land inside a pad with a downward speed under 72 and it scores. Starting where the game starts you is as hard here as it is there, so three buttons will drop you in above a pad instead.

A and D for the side jets, space or F for the main engine. Or use the buttons.

Each pass: a side jet moves the X velocity by 6 and costs 30 fuel; the main engine moves the Y velocity by −12 and costs 111; then gravity is added whatever you did. Velocities are in 256ths of a world unit.

$E328 lda #$0c ; main thruster: 12 $E32A pha $E32B asl a ; x2 $E32C asl a ; x4 $E32D asl a ; x8 = 96 $E32E adc #$0f ; + 15 = 111, the fuel cost $E330 sta scratch $E332 lda fuel_lo ; tank -= 111 $E33F pla ; back to 12 $E342 lda ship_vy_lo ; velocity -= 12

The fuel cost and the thrust come out of the same constant. Twelve is shifted three places to make ninety-six, fifteen is added, and the original twelve is pulled back off the stack to use as the acceleration.

06 · The gauge

The needle cannot tell you whether you are about to land

The instructions say the vertical velocity has to be inside the yellow area of the scale. There is one yellow cell. It covers downward speeds 0 to 63. The landing test accepts everything up to 71, and accepts every upward speed there is.

Sixteen character rows, eight marker glyphs inside each, so 128 needle positions over the whole scale. The marker code is $18 plus the bottom three bits of the gauge value, and one step is eight units of velocity.

Set it to 63, then 64. The needle drops out of the yellow, and the landing still works: 64 through 71 all land. Then compare 71 with 72. They are one marker step apart, an eighth of a character, and one of them scores nine hundred points while the other prints SORRY NO BONUS and destroys the ship. Nothing on the scale marks that line.

07 · Scoring

Eighty minus your speed, and a zero that is always there

The bonus for a landing is a subtraction and at most two multiplications. Everything on screen is then shown ten times larger than the number the game is holding, because the status line has a 0 painted on the end of it.

The bonus line the game draws on screen row 2, rendered from the same character set.

Push the speed below zero. A ship that arrives climbing scores more than a ship that arrives stopped, because 80 − velocity is an ordinary eight-bit subtraction and a climbing ship has $F0 in the low byte. 960 points is the highest single landing the scoring can produce. A perfect stop gives 800.

Every point counted out also puts 70 units back in the tank, one point and 70 units at a time, at about 21 milliseconds each. A good landing on the x10 pad therefore takes roughly seventeen seconds to pay out, and you watch the fuel bar grow the whole time.

08 · Progression

Sixteen numbers and then it stops

Nothing else changes as the game goes on. The pads stay where they are, the fuel costs stay the same, the landing limit stays at 72. The only thing that gets harder is how fast you fall, and it is a sixteen-byte table.

Gravity at $E0DE, in 256ths of a world unit per pass per pass. The counter stops at sixteen, so the sixteenth landing is as hard as the game ever gets, and a pilot who survives fifty is flying the same game as one who survived sixteen.

The table does not climb steadily. It goes 4, 5, 4, 5, 6, 5, so the second landing is harder than the third, and the eleventh is harder than the twelfth. The difficulty steps back four times on its way up.

09 · Sound

Three tunes, stored as chip registers

There is no music driver. Each tune is a list of raw SID frequency words and a fixed gap between them, played by a loop that writes two registers and counts. The gate is opened once at the start and closed at the end, so a repeated note runs into the one before it instead of being struck again.

Frequency in hertz against time, from the stored words. A SID frequency of n is n × 985248 / 16777216 hertz on a PAL machine. Zero is a rest.

The thrusters and the explosion do not use this path at all. They gate voices 2 and 3 to noise directly, and the only difference between the main engine and a crash is the frequency: $0900 for the engine, $0700 for the bang.

10 · Quirks

Things a pilot would not know

Nothing is synchronised to the screen

No raster interrupt is enabled, and the timer interrupt is masked for the whole of a game. The game times itself by counting down a register ten times two hundred, which is about 10.3 milliseconds, and waits three of those per update. On an NTSC machine, with a faster clock, the whole game runs about four per cent quicker.

The interrupt exists to flash one line of text

It runs only during the attract sequence. Every twentieth tick it steps the colour of eighteen cells at $DBCA, which is exactly the eighteen characters of PUSH F1 TO START, and on every tick it checks whether F1 is down. That is all it does.

The last 255 units of fuel are unspendable

The tank is called empty when its high byte reaches zero, and that same test clears both thrust flags. Up to 255 units are still sitting in the low byte, and no thruster will ever touch them. It also means the counter can never underflow, which is presumably the point.

Running dry does not end the game

OUT OF FUEL appears, the engines stop, and the ship keeps falling. The game ends at the next impact, when the crash handler finds the tank at zero and there is nothing to charge the crash to.

A crash costs fuel, and the bill is capped

The impact speed shifted right by four comes off the fuel high byte. Above a speed of $01FF the calculation is clamped, so every very fast crash costs the same 7936 units. Hitting the ground at speed 512 and at speed 768 cost exactly the same.

What the counter actually holds

Four packed BCD digits, printed followed by a 0 that is part of the static template. The counter tops out at 9999, which reads as 99990, and a single point is worth ten on screen.

Landing while climbing pays best

The bonus is 80 − velocity done in eight bits. A ship arriving at minus 16 has $F0 in the velocity's low byte, and $50 − $F0 is 96. On the x10 pad that is 9600 on screen, against 8000 for a perfect stop.

The whole game is 49 bytes of state

Everything the game knows about itself lives in $02 to $32: two positions, two velocities, a tank, a score, a high score, a view number and a handful of flags. Nothing else in memory changes while you fly.

11 · The screens

Everything it can put in front of you

The thruster flames in two of these were forced on by patching two bytes. They are otherwise almost impossible to catch, because they are only lit while a key is down.

Title screen

The title, unpacked from a bitmap of two screen cells per byte. The wordmark under it is 21 one-off glyphs rather than letters.

Instruction screen

The instruction screen. All 382 characters of it sit in one block at $E874.

Wide view

The pulled-back view, with all three pads and the badge in the rock.

Close-up of the middle pad

The middle plateau at full size, with both flames lit and the sprite doubled.

Landing bonus

A perfect landing on the x10 pad. 800 points, shown as 8000.

Out of fuel

The tank empty. The engines are already dead; the flight ends at the next impact.

Attract control demo

The attract sequence lighting each thruster in turn beside its label.

Game over

Game over, with a score of 3600 poked in as four BCD digits and a painted zero.

12 · Where it lives

Five kilobytes under the KERNAL

Reset switches the ROMs out through the processor port and never switches them back, so the game's own code occupies the addresses the KERNAL used to. All three hardware vectors point into it.

RangeBytesWhat
$0002 – $003249every variable the game has
$0400 – $07E71000screen, sprite pointers at $07F8
$3800 – $3BFF1024128 character glyphs
$3C00 – $3FFF102416 sprite shapes
$E000 – $E03655the video and sound chips' opening state
$E037 – $E8732109flight, scoring, explosion, attract
$E874 – $E9F1382the instruction screen, as text
$E9F2 – $EEB71222attract demo, interrupt, input, the panel, OUT OF FUEL
$EEB8 – $F253924four views, run-length encoded
$F254 – $F450509noise, and the title screen
$FFFA – $FFFF6NMI $E037, RESET $E037, IRQ $EB39

Addresses are from the Remember release of the cartridge, with both trainer options declined. The symbol map, the verified facts and the feature checklist sit beside this page.