1989 · Image Works

Speedball

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

Four bugs in the rules of the C64 Speedball, each shown here by running the game's own code: a knockout cup that hands you the wrong opponents, pairs of players who walk through each other, a skill figure that works backwards, and leagues that run eleven weeks long.

01 · The knockout

Bug: the knockout's opponents play with another team's figures

The KNOCKOUT ROUND screen names your opponent and shows a card of figures. The figures belong to a different team in eight or ten rounds of ten, and they get weaker as the cup goes on.

The game's own knockout_init ($3F05) and the knockout's part of match_restart ($31DF-$31E6, up to the prematch screen) run on the site's 6502, a model of the C64's processor, on the bytes in the Source tab, from a new random seed at each press. Names in code type are the routines and variables the Source tab names, and numbers that start with $ are their addresses in memory, in hexadecimal. The figures are the computer side's stamina, power and skill as opponent_load leaves them; the template is the one that holds them.

Each team in a cup or league has a record in memory, which holds its figures and results. A new knockout fills the eleven team records from the templates (knockout_init, $3F05). Record 0 is yours. Templates 1 to 10 go into the other records in a shuffled order: template 1 into a random record, template 2 three records on, and so on round the ten. Each record notes its template in team_template ($1CD1), and the KNOCKOUT ROUND screen draws round n's opponent from record n, its face and name from that template.

Before each leg, match_restart ($31C2) loads the computer's side. It reads record n's template number and then loads the record with that number:

ldx round · inx · ldy team_template,x · jsr opponent_load

In words: take the round number, add one, read that record's template number into the register Y, and call opponent_load ($3F81). That routine takes Y as a record number, so the template number picks a record a second time. The figures loaded are those of whatever template sits in that other record. Applied twice, the shuffle steps backwards through the templates one at a time, so from a random start the opponents' figures fall a template a round, and wrap once from the weakest team, Mira, to the strongest, Persus. The manual promises opponents that get harder as the rounds go on. The card under the named team's face shows the figures the computer really plays with, since attributes_text_set ($ACE6) reads them from the side, not the record. A league uses the records in template order, so its opponents are who they say they are.

02 · Tackles

Bug: red's players run straight through green's keeper

Speedball is a game of tackles: when two opponents touch, the game decides which of them goes down. Yet green's keeper can never touch a red player, and four more pairs of opponents pass through each other as if the other were not there. Green is your side in a one player game.

Each square is a pair of players, its row the first and its column the second: 0 to 4 are green (0 the keeper), 5 to 9 red (5 the keeper). For each square the page sets that pair touching and the rest apart, then runs the game's own pair_distances ($B038) and tackles_check ($8DD6) on the site's 6502. Green squares are seen to touch, amber ones are seen and set off other pairs as well, and red ones are never seen. Press a square to see what the game does.

With ten players on the pitch there are 45 ways to choose two of them, and the game numbers them as pairs 0 to 44. Every pass of play, every second frame, one routine measures how far apart the two players of each pair are (pair_distances, $B038) and writes each answer into a square of its own in a grid of 10 by 10 squares at $1E49. A second routine (tackles_check, $8DD6) then reads the answers back. Two opponents close enough to touch go to the tackle routine, and two team-mates are pushed apart.

Both routines find a pair's square in a list of square numbers, and both start reading that list thirteen bytes too early, at $F290. The right list starts at $F29D, and nothing reads it. For most pairs this does no harm: the answer is written into the wrong square and read back from the same wrong square, and no other pair uses that square. But the first thirteen bytes the routines read are not square numbers. They are the end of the table that names each pair's second player, the numbers 6 to 9, and they send pairs 0 to 12 to just four squares, which pairs 18 to 21 use as well. Several pairs write into each of those squares, so each holds only the last answer written. The game measures the pairs from 44 down to 0, so the last is always one of pairs 0 to 3: green's keeper and one of his own team.

So thirteen pairs of players are never seen to touch, however close they come. Nine are opponents: green's keeper with each of the five red players, green 1 with red's keeper, and green 2 with red's keeper and red 6 and 7. The other four are green team-mates who walk through each other. Red's own pairs all work. The bug also works the other way. When green's keeper touches green 1, 2 or 3, every pair that shares that square seems to touch too, wherever its players are on the pitch. The keeper touching green 1 also sends green's keeper against red 5 and red 8, and green 2 against red 5, to the tackle routine.

The right list is in memory, complete, all along. Four instructions read $F290, one in pair_distances and three in tackles_check. Pointed at $F29D instead, a change of one byte in each, they make every one of the 45 pairs touch when it should and act on nothing else, on the site's 6502.

03 · Skill

Bug: skill makes your players easier to bring down

The SKILL on a team's card is 255 less a tackle figure. In play the figure decides only whether a slide into one of the team's players works, and a higher skill makes that slide more likely to work.

The chance that a slide brings down a player of each team, from the test in tackle_resolve ($8EC2, and $9057 the other way): the slide works when a random byte is at least the victim's tackle figure. Run on the site's simulator with every value of the random byte, a slide into a side with skill 224 worked for 225 of the 256 (88 %), and into one with skill 48 for 49 (19 %), whatever the slider's own skill.

The shop sells extra skill, which takes 10 off your own tackle figure (shop_extra_skill, $AE59), and reduce skill, which adds 26 to the computer's for the next match (shop_reduce_skill, $AE6F). Both work against the buyer: extra skill makes your players easier to slide down, and reduce skill makes the computer's harder. The figure's one other use in a match is the computer's aiming, which tests only whether it is above 127 (ai_side1_target, $A898). Between other teams in a league the figure works the way the card suggests: the random results of the week's other matches favour the team with the lower figure (league_week_end, $8519).

04 · The league

Bug: a ten-week league lasts twenty-two weeks

The menu offers leagues of TEN WEEKS to ONE HUNDRED WEEKS. Each lasts eleven weeks more than it says, and the shortest more than twice as long.

The end of the game's own match_over ($3339-$336D) run once a week on the site's 6502 from the listing, from the state league_init ($3EEF) leaves, until it reaches the league's last screen (league_over_screen, $825A). The week's other matches (league_week_end, $8519) stand in by their one effect on the count: after week 10 they play week 11 at once.

A league's weeks run from 1 to 11. Your team plays in weeks 1 to 10 and rests in week 11, so each run of eleven weeks gives you ten matches. The length you choose goes into $1EB0 as a number of runs, 1 for TEN WEEKS up to 10 for ONE HUNDRED. match_over counts down $1EDB at the end of each run and moves the round counter on when it reaches 0, and the league ends when the round counter reaches the length. league_init sets $1EDB to 2, not 1, so the first round takes two runs: a ten-week league is 22 weeks and 20 matches, and a hundred-week one 121 weeks. The two player league counts its weeks separately and ends on time (twoplayer_week_end, $81E4).

05 · Tiles

The manual's electron tiles never appear

The manual lists ten power-up tiles. The C64 game chooses from eight of them.

The tile table at $BB21 holds a token, the types F, R, P, S, D and G once each, W three times, and J. It never holds 7 or 9, the electron bolt E and the eight-way burst M. Their entries in the table of effects (tile_effect_lo, $BBBF) point at the same routine as W, the ball that tackles whoever it touches (tile_ball_tackles, $9A9F), and the game has no sprite for a bolt. The How it works page rolls the game's own tile_choose a thousand times at a press.

06 · The disk

Two saved games ship on the original disk

The disk's directory lists two files the loader never reads, called 0 and 1. They are saved games.

SAVE GAME writes the game's state, the 1,024 bytes from $1C00 to $1FFF, through the save routine of the KERNAL, the C64's built-in system code, as file 0 for a league, 1 for a knockout and 2 for a two player game (game_save, $BD50; the digit from $2293). The disk's 0 and 1 are 1,024 bytes each and load at $1C00. In 0 the game type $1EAF is $FF, a league, its length 10 runs (ONE HUNDRED WEEKS), at week 1 with nothing played; its team records are in a knockout's shuffled order. In 1 the game type is 1, a knockout, at round 0. LOAD LEAGUE and LOAD KNOCKOUT therefore work on the original disk with no game saved, and start these two.