1989 · Firebird

Rick Dangerous

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

The play area is made of characters, four by four to a block and eight blocks to a map row; Rick and everything that moves are sprites, and every enemy's left-facing frames are made by the game at the start of its level.

01 · The video bank

Everything on screen comes from one 16 KB bank

The video chip can see 16 KB at a time, and Rick Dangerous gives it the top bank, $C000-$FFFF ($DD00, $0847). Everything the chip draws in play is in it: two screens, the tile set, the font of the score panel and every sprite frame. Three quarters of the bank is RAM the processor can only reach with the I/O chips or the KERNAL switched out, which suits graphics: the video chip always reads RAM there.

The bank as the game uses it in play. A sprite frame's number is its address in the bank divided by 64, so frames $40-$5F would be the tile set and $E0-$FF the font: the game never shows those numbers as sprites.

02 · A room, tile by tile

A map row of eight bytes is 128 tiles on screen

The screen is the C64's text screen, 40 by 25 characters, in multicolour mode: each character is 4 by 8 pixels, two pixels wide, in the black background, two colours shared by the whole screen and one of its own. Row 0 holds the score panel in the font; the play area below is 32 columns of tiles, columns 4 to 35, with character 0 to either side of it. A map row is eight block numbers ($9200-$A5BF), a block is four rows of four tile codes ($8200), and a tile code is a character in the room's tile set. Six map rows make the 24 tile rows of a view, of which the screen shows the last 23: the top one is kept off screen at $BFA0 for the scroll ($2DA0).

Drawn by this page from listing.json, the way $2D59 decodes a map row. Click a tile: the block it comes from (left, the tile outlined), the character enlarged with its four colours (right), and where each piece is in memory. A tile's own colour is its entry in the tile colour table ($8000, or $8100 with the second set): bit 3 set makes it multicolour, with the colour in the low three bits.

The level maps take 5,056 bytes for 632 map rows, 80,896 tiles. Written out as tiles they would fill 79 KB, more than the machine has. Blocks are what make the levels fit: 4 KB of them, shared by all four levels.

03 · Tile sets

Two sets of 256 tiles, swapped under the I/O chips

Levels 1 and 2 use the tile set at $D000, under the I/O chips, where the video chip reads it. Levels 3 and 4 use a second set that waits at $7000. When a room of the other set comes up, $1911 switches the I/O chips out and exchanges the two 2 KB sets byte for byte, with the two colour tables at $8000 and $8100. The order of the codes is also their behaviour: eight thresholds per set ($185B) divide them into background, ladders, deadly tiles and the rest (How it works, section 02).

Each set in its levels' colours, 32 codes to a row, code $00 top left. "Kinds" tints each code by its range; "Not in any map" marks the codes that no block any level's maps use holds.

04 · Blocks

253 of the 256 blocks are used

A block is 16 tile codes, so the same block can be drawn with either tile set: a block number means nothing until the room says which set is in. The first two levels' maps use 121 different blocks and the last two 132; three blocks appear in no map at all.

All 256 blocks ($8200-$91FF, through the address tables $B300 and $B400), drawn with the chosen level's tile set and colours, 16 to a row. Blocks the chosen level's maps do not use are dimmed; the three that no map uses are crossed.

The block address table shares its bytes with another table. Its high bytes run from $B400, and the last 48 of them, from $B4D0, are also the first 48 entries of the sprite frame colour table. No frame list names a frame below $30, so no object takes its colour from the shared part.

05 · Rick

Rick is two sprites: a black outline over a coloured body

Every frame of Rick is a pair of frames, shown on sprites 0 and 1 at the same place. The even frame is drawn in hires on sprite 0 in black; the odd one, in multicolour on sprite 1 in orange, sits under it and fills him in ($B5A4, $B5A5, $D01C). Sprite 0 has priority over sprite 1, so the outline is always on top. Each step the game copies both frames, masked by the scenery in front of him, into the image buffer the sprites show ($0F83).

Rick's frames $AC-$DF ($EB00-$F7FF), the right-facing ones first and the same moves facing left $18 frames later; the lists come from $4320, $4326 and $4330 and the frames the code names ($0D0C-$0D94). The speeds are the game's: a walking frame every four steps, a crawling or climbing one every two, at 25 steps a second.

Climbing has one pair of frames, $C2, and its left-facing twin $DA: the climbing list alternates the two, so Rick climbs by being mirrored every other step. The walking list has five pairs, but in play Rick uses only the first two ($0D65 keeps the place in the list at 0 or 1). The other three walk the figure across the story screens.

06 · Enemies

Each level has ten enemy frames, and the game mirrors them

The enemies are stored facing right only, ten frames a level from $6600. At the start of a level $16D3 copies that level's ten into frames $90-$A3, each followed by its mirror image, made by reversing every row's three bytes and the four two-bit pixels in each byte. The same ten frames give two kinds of enemy: one walks with frames $90-$99, another with the same lists ten frames on ($17A4), each with its own colour ($1780, $1784).

The twenty frames as enemyFrames, a port of $16D3, builds them; test_mirror.js holds it to the game's own routine for all four levels, every byte of every frame and its colour. Below them, the four lists ($430C-$431F) playing for both kinds: walking right, walking left, climbing, dying.

The climbing list is a frame and its own mirror image, $96 $97, the same trick as Rick's.

07 · Traps and treasures

Every object's frames, as its type plays them

Each object type has a frame list ($B780, $B800): its frame at rest, then the frames it animates through, ended by $FF. Many traps rest on $AB, a blank frame, so they cannot be seen until they go off.

The frame lists of every object type that the levels' object lists use, from $42FA-$4416, the traps (types 24 to 70) animating as they do once set off, a frame every fourth step. The treasures, boxes and bonus markers (types 16 to 23) show only the first frame of their list, which starts inside one shared run ($42FA); only a trap steps through its list. Colours from the frame colour table ($B4D0) and the two sprite colours the game sets for play ($D025 = 11, $D026 = 10).

08 · Fonts

The story screens bring their own font

The score panel, the title and the Hall of Fame use the font at $F800, at the very top of the bank. It stops eight bytes short of 2 KB: the last eight bytes of memory are the processor's vectors. Each story screen swaps in a second set from $7800 ($24DC), whose characters draw both the story's text and the picture of the next place, and swaps it back after. The picture fades in a bit of every byte at a time, from a copy of its characters ($254F).

The two sets, 32 characters to a row: the panel and text font ($F800-$FFF7), then the story set ($7800-$7FFF), drawn in one colour on black.

09 · Left over

Tiles that no map uses

Every sprite frame in the game is named by a frame list or by the code, but the tile sets hold graphics that no map places. Three of the 256 blocks appear in no map. The first tile set has seven codes no used block holds, two of them the bullet's, $FE and $FF, which $2ABA writes into the screen itself. The second set has 26 such codes, all with pixels in them.

The three unused blocks, in the first set's colours, then the tile codes of each set that no block in that set's maps holds, enlarged. Counted by this page from the maps; a code that the program writes into the screen itself, as the bullet's are, would not be counted.