1986 · Electric Dreams Software

Spindizzy

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/spindizzy with me to Gold.

A spinning top maps a world of 386 rooms against the clock. Every room is a short list of blocks and shared shapes, built into a grid of 8 by 8 by 8 cells and drawn in perspective one tile at a time, and the clock counts passes of the game's loop, not seconds.

01 · The game

A survey of a world of floating rooms

You steer GERALD, a surveying craft that looks like a spinning top, through a world of rooms seen from above at an angle. Each room is one screen: floors, ramps, stairs, bridges, water, ice, and trampolines, with lifts and switches that open the way on. Walking off an edge leads into the next room. The job is to visit every room and collect the jewels on the way.

The panel on the left counts down TIME, counts TO MAP (the rooms not yet visited, 385 at the start), and counts JEWELS. A new room adds 8 to the time and a jewel adds 10. Falling off the world or onto a deadly surface costs 5, and the game ends when the time runs out. M shows the map of the rooms visited, and F1, F3, F5 and F7 turn the view, since a wall can hide the way on.

The title screen with the start room drawn under the logo
The title, with the start room drawn inside the frame.
Gerald in the start room with the panel on the left
The start room: TIME 107, TO MAP 385, JEWELS 000.

02 · Sound

Sound: every effect on voice 3 buzzes

There is no music in the game. These are its sound effects, made by its own code.

The game's own code, run on this page on a model of the C64's processor over the listing's bytes and played through the site's model of the sound chip. Each effect starts the way the game starts it, and then the raster interrupt (raster_irq, $7066) runs once a frame, as it does in play. test_sound.js holds the first frames of each against the game's own instructions.

The C64's sound chip has three voices, and the game gives each its own job. Voice 1 plays the jingle for a jewel: a triangle wave (a soft, flute-like tone) whose pitch steps through eight values, one each frame, for 80 frames (jingle_pitches, $721B). Voice 2 beeps when Gerald turns a switch on, at the switch's own pitch from a table of 16 (switch_pitches, $8912), and hisses with noise while an inhabitant touches him.

Voice 3 makes everything else: a sawtooth (a brighter, buzzier tone) for a trampoline and for a lift passing a level, and a burst of noise for a hard landing or for Gerald bursting (noise_sound, $88BE). Every frame the interrupt flips voice 3's pitch between two values (hum_toggle, $70EE), whatever voice 3 is playing, so each of these sounds buzzes. A landing at a speed of 28 or more thuds (landing_sound, $89A5), and a harder one thuds longer, not louder: the speed sets only how slowly the sound fades, up to a speed of 60, which sounds the same as a burst. A gentler landing makes no sound at all, and walls make none either, which Discoveries explains.

03 · Drawing a room

One room, drawn by the game's own code

The start room as enter_room draws it. Turn the view as the F keys do.

The game's own routines, run on a model of the C64's processor over the bytes the Source tab lists, make the screen the C64 shows, byte for byte. The block in the bottom right corner is the compass, drawn for the view. Every room is on the Map tab.

A room is a box of 8 by 8 cells, 8 levels high. The game keeps it as a grid of 512 bytes ($B700), one for each cell, holding the type of block there or 0 for empty space. To draw it, build_picture ($9A98) walks the grid and works out where each cell lands on the screen. A step in X moves it one row down and one place right, a step in Y one row down and one place left, and a level up moves it one row up. That gives the diamond-shaped view, with no multiplication anywhere.

Each block type has a list of eight small tiles that make up its picture (block_tiles, $A900). Rather than draw a block over the one behind it, the game first queues every tile into a buffer of 25 rows of 16 places ($AA80), keeping up to four tiles a place with how near each is. Only then does draw_picture ($9A0D) build each place: each tile's mask ($B900) cuts a hole in what is behind it, and its picture ($BA00) fills the hole. A nearer tile hides a farther one without either being drawn twice.

Gerald, the jewels, the lifts, and the inhabitants are sprites, which the video chip draws over the picture. A sprite would float in front of a wall that should hide it, so before each one is drawn the game builds a mask from the tiles queued in front of it (build_sprite_mask, $9D18) and cuts its shape with it. That is how Gerald disappears behind a pillar.

Turning the view rebuilds the grid from the record with every block's position turned and every slope turned to face the new way (decode_block, $95D4; turn_block_type, $95AE), then draws it again. The room itself does not change.

04 · The world's data

A room is a list of blocks

The bytes of a room's record, coloured by what each one is. Pick a room.

length, map row, map column, flagsthe floor's block wordblock words, and the structure each type-2 word namesend of the blocks, end of the piecesmoving pieces, three bytes each

The whole world is 14,593 bytes, one file on the disk, which the game copies down to $0400 at start-up. Its 386 room records (room_directory, $0A00) are 9 to 86 bytes long, 33 on average. Each starts with its length, its place on the map, and a byte of flags whose low four bits pick one of 16 colour schemes. Then come block words, two bytes each: the first holds the block's type and how to fill the cells under it, the second its position in the room. The first word is the floor, which fill_floor ($966C) repeats across all 64 cells.

Most rooms are built from shared shapes. A block word of type 2 is followed by the number of a structure (structure_list, $0400): 127 records of block words of their own, placed with the calling word's position as an offset and turned by two of its bits. A structure may name other structures, as 76 of them do, so a flight of steps can be built once and used in many places. The start room is 13 block words, all of them structures.

After the blocks come the moving pieces, three bytes each: a type (a jewel, a lift, an inhabitant, or a block that a switch puts in place), a packed position, and a third byte that is the jewel's number, the lift's levels and pause, the inhabitant's top speed, or the switched block's structure. The world holds 220 jewels in 198 rooms, 64 lifts, 14 inhabitants, and 70 switched blocks. 141 rooms have no moving pieces at all.

05 · Movement

Gerald slides, and the surface decides how

The joystick never moves Gerald directly. While he touches a surface, each direction held adds 1 to his speed along that axis on every pass of the main loop (main_loop, $8DA1), or 2 with fire held, and friction takes a 64th of the speed away again (apply_friction, $898D). In the air he cannot steer, and gravity takes 8 from his upward speed each pass (apply_gravity, $8951). What happens when he comes down depends on the cell under him (surface_response, $8F46):

SurfaceIn the gridWhat it does
Floor$01Stops the fall. Friction slows him and the joystick steers him.
Deadly$02Bursts him (burst_animation, $7360), then puts him back where he last stood, for 5 of the time.
Trampoline$03Reverses the fall, so he bounces back up, with a buzz on voice 3.
Slopes$04-$1FSeven shapes in four directions each: gentle and steep slopes, outer and inner corners, steep corners, ridges, and valleys. Each has its own height across the cell, and turns his speed along it into speed up or down, so he rolls down and slows climbing.
Ice$63Holds him up, but neither friction nor the joystick works on it: he slides on in a straight line. If he ever stops dead on it, he is restarted, since he could never move again (on_ice, $9116).
Water$20Counts as nothing under him (find_floor_at, $9B57), so he falls through it and, 16 levels further down, is put back where he last stood.
A switch pad$D0-$DBA floor that also presses the switch (press_switch, $916A).

Landing too hard bursts him as surely as a deadly block: an upward speed below -108 when he lands (check_hard_landing, $89CF). The main loop saves his position on every pass he stands on something solid, and that is where every restart puts him.

06 · The clock

The clock counts passes of the main loop

The time is three decimal digits, kept in BCD (each half of a byte holds one decimal digit, $5E/$5F). A new game sets it to 100, and the start room adds 8 as the first room visited. All losses go through one routine (add_to_time, $9533), always as an addition of a negative number written as a decimal complement (9999 for -1, 9995 for -5). All gains go through another (add_time_capped, $9C8E), which never lets the time pass 150.

EventTimeWhere
A room visited for the first time+8mark_room_visited, $8BB8
A jewel+10jewel_piece, $A12C
A tick of the clock, every 64 passes, or 256 in the beginners' rooms-1main_loop, $8DA1
A restart after a fall or a burst-5restart_at_safe_point, $89E7
Space, the brake, each pass it is held while he stands-1brake, $8F0A
An inhabitant touching him, every other pass-2inhabitant_piece, $A622

The clock is not timed by the video chip's 50 frames a second. It ticks once for every 64 passes of the main loop. Each pass begins by waiting for a timer on the second CIA chip (one of the C64's two timer and input chips) that the game restarts every pass: 16,000 processor cycles at the slow speed, 1 cycle at the fast. At the slow speed the loop makes about 1.22 passes a frame, so the clock ticks about once a second. The fast speed only lets the loop run as fast as its work allows, and the clock speeds up with it: Discoveries has the measurements.

The start room and seven rooms east of it have bit 4 of their flags set, which makes the counter step 1 a pass instead of 4 (room_header, $8B57): the clock there runs at a quarter of its speed, the beginners' area the manual mentions. When every room is visited and all 220 jewels are taken, the game shows the score and offers to carry on exploring with the time set to 150 (survey_complete, $85DB).

The score screen gives three percentages (score_screen, $83B0): the rooms visited out of 386, the jewels out of 220, and the game, the average of the two.

07 · Switches, lifts and inhabitants

Two switches can be on at once

Switch pads carry a number from 0 to 11 in the world data, and the game remembers two switches as on ($36/$37; press_switch, $916A). Pad 0 turns both off. Pads 1 to 7 turn every other switch off and leave only themselves on. Pads 8 to 11 take one of the two places in turn, so one of them can be on together with another. The box at the bottom of the panel shows the symbols of the two that are on (draw_switch_box, $91FA).

Each of the 64 lifts names a switch, or none for the 20 that always move. A lift that is on moves between two levels, may pause at each end, and carries Gerald while he is in its cell (lift_piece, $A37F). A switched block is a structure placed into the room while its switch is on, and the room is rebuilt whenever the switch changes (switched_block_piece, $A0FF).

The 14 inhabitants each have a kind and a top speed in their record. Kind 0 keeps to the floor of its level and kind 1 crosses gaps. Each one steers towards Gerald, changing its speed along each axis by 4 at a time (steer_speed, $A818), and while it shares his cell and level it takes 2 from the time every other pass, which is as often as the pieces in such a room move. They are drawn with the two shapes Gerald is not using, so pressing I changes their shape along with his. Their top speed holds in only two of the four directions, which Discoveries explains.