Commodore 64 · 1986 · Firebird

The Sentinel

This minisite was contributed by air. It’s agent-generated and needs a human editor. Clone https://github.com/gamesexplained/gamesexplained and follow kit/START.md to curate games/c64/the-sentinel with me to Gold.

The Commodore 64 version is Geoff Crammond's BBC Micro program moved across, most of it to the same addresses. It still calls the BBC Micro's operating system, which a small layer of C64 code answers, and it grows all 10,000 landscapes, and their secret codes, from one 40-bit number.

01 · The landscape generator

All 10,000 landscapes, and their secret codes

No landscape is stored. The game grows each one from its four-digit number, and the same arithmetic gives the eight-digit code that unlocks it. This is that arithmetic, ported from the game's code: pick a number.

Drawn by this page from the port's output. The camera stands where the game puts it for its overview (object 16, 15 tiles across, 33 units up and 62 tiles in front, looking 31° down, set up at $13AB), but the page projects onto a flat screen, where the game works in angles through its arctangent tables, so the two pictures differ a little in shape. Flat tiles alternate the ground's first colour and blue by the parity of column and row, as $2A4B does; the page shades the slopes itself. The port matches the tiles, objects and variables of eight landscapes recorded in the emulator byte for byte, and all 10,000 codes of Simon Owen's published list. From above, hovering over a tile shows its byte in $0400-$07FF.

One sequence of numbers

Everything comes from a 40-bit shift register at $0C7B-$0C7F. When a landscape is chosen, $33ED puts its number, in binary-coded decimal, into the low two bytes, over a register the reset left as 00 00 01 00 00. Each call of $31CA shifts the register eight times, feeding in bit 3 of $0C7D exclusive-or bit 0 of $0C7F, and returns $0C7F. The game then uses the numbers in a fixed order, from the first hill to the last digit of the code, so one extra or missing call would change everything after it. The port keeps that order, and the game's 8-bit arithmetic, for the same reason.

From numbers to hills

The stage buttons under the picture step through the tile table as $2ACC builds it.

StageWhat happensWhere
1 Numbers81 numbers are drawn and set aside, then the steepness: 24 for landscape 0000, otherwise 14 plus a number from 0 to 22. Then every one of the 1,024 tile corners gets a number of its own.$2ACC, $3451
2 AveragedEach corner becomes the average of itself and the next three along its row, then along its column, and all of it twice. The rows wrap round, so the far edge is averaged with the near one.$2B83, $2BBC
3 ScaledEach value, less 128, is multiplied by the steepness and cut to an altitude from 1 to 11.$2B22
4 LevelledA corner higher than both neighbours drops to the higher of them; one lower than both rises to the lower. Rows, then columns, twice.$2BBC
5 Tiles and objectsEach tile gets one of 15 shapes from its four corners, and each byte ends as altitude × 16 + shape. Then the objects go on.$2C7C

The enemies, the player and the trees

Landscape 0000 has the Sentinel alone. Elsewhere the number of enemies starts from the thousands digit plus 2, moves up or down by a count of leading zero bits in a drawn number, and is drawn again until it lands between 1 and 8 ($3426); below landscape 0100 it is also held to the tens digit plus 1 ($33ED). The map is cut into 8 × 8 blocks of 4 × 4 tiles, and the enemies take the highest flat tiles, one per block, each chosen block ruling out its eight neighbours ($14FB). The first enemy is the Sentinel, on its tower.

The overview is drawn at this point ($8858). Only then does the player's robot go on a random empty flat tile, lower than every enemy and lower than altitude 6 ($1450), followed by the trees: 10 plus a number from 0 to 22, at most 48 minus three per enemy, all below the lowest enemy ($147D). That order is why the game's overview shows neither; the button above the picture adds them. Over all 10,000 landscapes, building one takes 1,212 to 1,880 numbers from the sequence and leaves 10 to 32 trees.

The ground's two colours count the enemies: green and white for one, cyan and red for four, yellow and red for eight ($14D4, $14F3).

02 · The secret entry code

The secret code is four more numbers from the same sequence

Once the landscape is built and populated, the game draws 43 more numbers and compares them with what was typed. Four of them are the code, which is why a code can be worked out for any landscape without playing it.

The 43 numbers of the check ($14AA) for the landscape chosen above. Each is a drawn byte with 6 taken off any nibble from 10 to 15, which turns it into two decimal digits ($339A). The four highlighted numbers meet the four bytes of the typed code; the result of each comparison is rotated into $0C65, and $14DC needs bits 1 to 4 set.

The traps around the check

The BBC Micro version protects the check against anyone tracing it, and the C64 version keeps every trap.

The digits of all 10,000 codes, 80,000 in all, counted by the port. 0 to 3 turn up about 5,000 times each and 4 to 9 about 10,000: taking 6 off a nibble of 10 to 15 makes it 4 to 9, so those digits have two ways in.

The codes are the BBC Micro's: Simon Owen's generator gives one list for the BBC Micro and the Commodore 64, and it agrees with this port on all 10,000.

03 · Sound

The BBC Micro's tunes, played on the SID

The music is the BBC version's own bytes. The C64 plays it through its own copy of the BBC's sound call, which turns an 8-byte block into SID registers, and a tick once a frame that does the work of the BBC's envelopes. Every tune and sound is below.

The game's sound driver, ported to JavaScript and played through the site's model of the SID. The port was held to the game's own code, run in a 6502 simulator on the analysed image: 20 runs, 4,718 frames, every SID register after every frame and every write in order, with no difference. The game runs its sound routine $352C from nine places in its code, wherever it happens to be; the port runs it once a frame. That is what happens where the transfer and hyperspace tunes play, but not everywhere, so in the game the U-turn's second chord and the notes of the game-over tune can come a little later than here.

A sound is eight bytes

The BBC code plays sound n by handing the block at $AC00 + 8n to OSWORD 7, the BBC operating system's sound call ($3470). The C64 answers that call with code of its own ($8DB4, section 5 below), which reads the block like this:

ByteWhat it sets
0the voice, 0 to 2
1the SID's control byte, written first without its gate so the envelope starts again
2, 3attack and decay, sustain and release; the low nibble of byte 3 also picks a release time in frames ($8EC1)
4the pitch, an index into the frequency tables at $9033 and $9133
5the pulse width's high nibble; with bit 7 set, the frequency is halved (bits 4-6) + 1 times
6frames before the gate is released, or $80 and up to hold the note
7a pitch effect: the offset of a 7-byte record at $AC40, or $80 for none

The interrupt then does the rest once a frame: $8ED1 counts each note down, lets go of the gate, and silences the voice when its release time has passed; $8F0C steps the pitch effects, a change of pitch per frame in three sections. The three records are the pitch half of the BBC version's envelopes 2 to 4, byte for byte, for the music, the scanner and the ping.

The pitch is the BBC's too: 48 steps to the octave, quarter semitones. The C64's table holds 2100 × 2n/48 for pitch n, which puts pitch 89, the BBC's A above middle C, at 446 Hz on a PAL machine, about 1.3 % sharp.

The tunes

A tune ($34DE, bytes at $AB50) is a list of pitches. A byte of $C8 or more sets the wait after each following note to (byte − $C8) × 4 frames, and $FF ends the tune. Each note is played as sound 3 on the next voice in turn, so three notes with no wait between them make a chord. Five places in the code start the music, at five offsets:

OffsetWhenStarted atLength
0hyperspace$21815.5 s
25transfer$1B825.5 s
40U-turn: the last two chords of the transfer tune$1B3C4.1 s
50game over$87F66.5 s
66landscape won$36277.9 s

The game-over screen adds a falling noise ($352C): sound 6, one frame long, from pitch 230 down to 60, a step every one to four frames drawn from the game's random numbers. The BBC version falls from 250 to 80.

04 · The play screen

The view is drawn as a bitmap and shown as text

The drawing code writes a multicolour bitmap at $E000, and on the title screens that bitmap is what the screen shows. In play the screen shows multicolour text instead: the bitmap is copied into five character sets, and a raster interrupt switches between them every 40 lines.

One frame of the first view of landscape 0000, drawn by the site's renderer from what the emulator recorded: the video registers, the five $D018 writes and the memory the picture is made of. 0 of its 104,448 pixels differ from the emulator's own picture. The top row holds the energy (three robots and a tree: 10 units) and the frame of the scanner; the sights and the inside of the scanner are sprites.

The 3D code draws only into the bitmap at $E000-$FF3F, 320 bytes to a character row ($3D83, $3DB5). In play, $979D copies that bitmap into five character sets at $4000, $4800, $5000, $5800 and $6000, one for each band of 40 lines. The screen matrix at $7C00 holds the character codes 0 to 239 in order, over and over ($9AAC); only the top row differs, with the energy row's characters 240 to 249 ($9508). A band covers five character rows and part of a sixth, and 240 characters are six rows of 40, so each set holds exactly what its band shows. The raster interrupt at $95E9 runs five times a frame and points $D018 at the next set each time ($9589, $958E).

The switches fall in the middle of character rows 0, 5, 10, 15 and 20, the dashed rows above, so the game copies those rows into two sets ($3A40). The five sets also form a circular screen: $94 and $95 hold the column and row where it starts, and a step of a pan moves them ($96A6).

The character sets double as scratch memory. Before a full redraw the screen goes blue ($358D), and the visibility pass builds its tables of corner heights in the character sets themselves, $4000-$5F3E ($25C4), before the new view is copied over them ($98B2).

05 · A BBC Micro program

Inside, it is still a BBC Micro program

Long stretches of the C64 code are byte for byte the BBC Micro original, most of them at the same addresses. What the BBC code expects of its machine, the C64 version supplies itself.

Where each part of the BBC Micro program ended up; hover over a band. The BBC side comes from Mark Moxon's reconstruction of the BBC version. A ten-byte window of its code found exactly once in the C64 image places 10,751 of its 23,534 code bytes (most of the others differ only in addresses that moved), and 556 of its 885 code labels land on the same instruction. Grey blocks are the C64's own.

The operating system, rebuilt

A BBC Micro program prints, reads keys and makes sounds by calling the operating system at fixed addresses at the top of memory. The C64 version's start-up code ($8900) writes six JMPs at those same addresses, and the BBC code calls them unchanged.

AddressOn the BBC MicroOn the C64
$FFEEOSWRCH, print a character$8A6B: draws the BBC Micro's own 8 × 8 font, two cells wide, into the bitmap, and follows the BBC's control codes for colour and position; the bell is ignored, so the number entry's "buffer full" beep is silent
$FFE0OSRDCH, read a key$8D2C: waits for the last key to be let go, then for a key, and returns its ASCII from $8FF3 ($8FB3 with SHIFT)
$FFF4OSBYTE$8F78: two of the BBC's calls, test a key ($81) and silence a voice ($15); every other call returns at once
$FFF1OSWORD$8D81: two calls, play a sound (7) and read a character's shape ($0A, which the title uses to build its 3D letters)
$FFC2, $FFC5GSINIT and GSREAD, for reading strings$8ED1 and $8F0C: the sound's two once-a-frame ticks

Zero page kept its BBC addresses too, with one exception. The BBC version kept the enemy being processed and the current object at $00 and $01, which on the C64 are the processor's own port, so they moved to $90 and $91: BBC LDX $00 is C64 $16B9 LDX $90.

The keys

The table of the game's 15 keys at $138D is in the BBC's order, with C64 key numbers, and the table beside it is the BBC's byte for byte. The BBC's COPY and DELETE, pause and continue, became the two cursor keys. The key test ($8CF9) numbers a key by the bit read on $DC01 times 8 plus the line driven on $DC00, the transpose of the C64's own numbering; it is the only code that reads the keyboard, and nothing reads a joystick.

KeysAction
S, D / L, ,pan left, right / up, down; with the sights on, move the sights
SPACEsights on and off
A, Q, H, Uabsorb, transfer, hyperspace, U-turn
T, B, Rcreate a tree, a boulder, a robot
CRSR ←→, CRSR ↑↓pause, continue
7, 8volume down, up, 16 steps
F1abandon the game

06 · The enemies' clock

The Sentinel turns every 15 seconds, not 10

The enemies run on the BBC version's timers, but the C64 feeds them through a gate that lets 205 frames in 256 through. The Sentinel turns a fifth slower than on the BBC Micro, and slower than the ten seconds that guides to the game give.

A model of $130C and $1317. Each frame adds $CD to $1335 and calls the timer routine on a carry; the routine counts the timers down on one call in three; the Sentinel's turn timer is set to 200 at $1813, and it turns by 20/256 of a circle, 28.1°, when the timer runs out. In the emulator, 1,000 frames gave 801 calls, and the Sentinel's heading went 112, 92, 72, 52 in steps of about 752 frames, 15.0 seconds.

Nothing turns until the player does something. Bit 7 of $0CE5 holds the timers and the scanner still, and $12E1 clears it on any action key, a U-turn or a hyperspace included, even when the action then fails. Panning does not count. In the emulator the Sentinel held its heading for 20 seconds while the player did nothing, and the timer routine was not called once in 500 frames.

Being seen costs energy on the same clock. An enemy that can see the player waits 120 ticks ($1835), then takes a unit every cycle of its tactics, whose timer is set to 30 ticks ($1848), 2.2 seconds. The manual says a unit about every five seconds. In the emulator the last two units went 2.6 seconds apart; what the other 0.4 seconds is has not been traced.

07 · Energy

Energy, and a sentry worth 3

The player's energy is one byte, $0C0A. The row of icons at the top of the screen is built from it by subtraction: 15 at a time, then 3, then what is left.

10

The top row as $9508 writes it for the energy on the slider, drawn from the ten characters at $ABB0 in landscape 0000's colours: a gold robot for each 15 units, a robot for each 3, then a tree for 1 or a boulder for 2, and the scanner's frame from column 29. In the emulator, 10 units showed three robots and a tree, 9 three robots, 8 two robots and a boulder, 5 a robot and a boulder.

ObjectUnits ($214F)The wiki's figure
Robot33
Sentry34
Tree11
Boulder22
Meanie11
The Sentinel44
Its tower0

Absorbing adds the table's value ($1B9E). Creating takes it first and gives it back if the object cannot be placed ($1BBA, $1BD5); in the emulator T, A, B and R took the energy from 10 to 9, 10, 8 and 5. A hyperspace costs 3 units, and without them the game ends ($2156). The BBC version's table gives a sentry 3 as well.

The byte is kept to six bits (AND #$3F at $2148), so 64 units would read as none. It cannot happen: by the placement rules no landscape holds more than 62 units in all, counting the player's 10 and the robot they start in. The most is landscape 0340, with the Sentinel, seven sentries and 24 trees.

08 · Secrets, quirks and leftovers

Eight things found in the code

Each of these is in the code at the address given; the ones marked live were also seen in the emulator.

The overview leaves out the player and the trees

The landscape is shown from above before the player's robot and the trees are placed ($8858, then $1450 and $147D), so the overview never shows them. Live: the overview of landscape 0000 has the Sentinel and no trees; the first view from the robot has trees in it.

Overview of landscape 0000
The overview
First view of landscape 0000
The first view: two trees

Past landscape 9999, the count starts again

The next landscape is the current number plus the energy left, added in binary-coded decimal ($1A87-$1A95). The carry out of the top digit is dropped, so 9990 with 15 units left leads to 0005.

Landscape + energy
next landscape

Zero is printed as the letter O

The digit printer swaps 0 for O ($31F6), in the number entry too. Live: every prompt and every code the game prints. The code of the landscape chosen above, as the game prints it (white) and with the font's own zero (yellow):

A U-turn plays the end of the transfer tune

The transfer tune starts at offset 25 of the music ($1B82); the U-turn starts the same tune at offset 40 ($1B3C), its last two chords. Live: a stop on the tune starter, $888F, caught the U-turn with 40 in A. Both are in the sound player above.

Any action wakes the enemies, even one that fails

The enemies wait until the first action key: absorb, create, transfer, hyperspace or U-turn ($12E1). The key counts before the action is tried, so a refused creation wakes them too. Panning never does. Live, with a U-turn.

BBC BASIC, inside a C64 game

The gaps between the C64's tables hold pieces of BBC BASIC's own code, at the addresses BASIC occupies in a BBC Micro: SQR at $A7B4, whose BMI reaches the error "-ve root" at $A7A9; the range reduction for SIN and COS at $A9D3, reaching "Accuracy lost" at $AA38; log10 e and ln 2 in BASIC's five-byte form at $A869 and $A86E. Nothing in the game reads them. At BBC addresses the gaps are where the BBC version keeps screen-buffer rows and the routines the C64 moved elsewhere.

Four font characters nobody sees

At start-up $89C5 patches four characters of the BBC font, ], _, { and |, into an arrow, a centred dash and two blocks. The game never prints any of them.

The BBC's beep is gone

The number entry sends the BBC's bell code when the field is full ($3319). The C64's copy of the print routine ignores that code, so the C64 version stays silent.

09 · The copy analysed

This copy is missing the 1.5 KB that scrolls the view

The copy analysed here is a freezer-cartridge backup, a snapshot of the running game saved to disk, not the original release. Two parts of memory did not survive. The top 192 bytes, $FF40-$FFFF, the interrupt vectors and the six JMPs above, the game writes itself when it starts, so the backup plays when it is restarted at the game's own entry, $3F00.

The other, $B000-$B5FF, is code. The game calls $B006 once for each step of a pan ($367C), after moving the screen's circular origin ($96A6). Nothing else in memory rewrites the screen matrix for a moved origin, or recomputes the lines of the five bands, which $9595 does only when the whole view is redrawn, so that must be at least part of what the missing code does. With an RTS there, as a test, the game plays on and a pan leaves the picture as it was until the next full redraw. A track-by-track image of the original disk was tried as well; its track 25, where the loader starts reading, is damaged. The About tab has the details.

What is still unknown