Asteroids
Atari's 1979 vector arcade shooter, read through Computer Archeology's annotated disassembly of the revision 2 ROMs.
On this page
The atlas reads Asteroids through the commented disassembly on Christopher Cantrell’s Computer Archeology, begun by Lonnie Howell and carried on by Mark McDougall, of Atari’s revision 2 program ROMs1. Labels and comments cited here are theirs. A frame here is one pass of the main loop, at most 62.5 a second1.
The ship turns 3 of its 256 directions a frame, and every other frame thrust adds up to 254/256 of a unit to its speed along the facing2, and each axis stops at 63 255/256 units per frame on each axis3. With the thrust off, drag takes about 1/128 of the speed each step, so the ship coasts4. Everything wraps at every edge, except that the saucer is removed at a side edge5.
Hyperspace puts the ship at a random place, and the same random draw decides whether it survives. The jump fails more often as the rocks thin out, up to 1 in 4 with 4 or fewer left6.
A shot rock leaves two of the next size down: large → 2 medium → 2 small; 20, 50, 100 points7. Each wave starts with two more large rocks than the last, from 4 up to 118.
The large saucer fires at random. The small one aims at the ship with a random error, which narrows at 35,000 points9. The first three saucers are large. After that, small saucers come more often as each player’s game goes on, and always from 30,000 points10.
Mechanics
| Mechanic | Kind | How |
|---|---|---|
| Inertia | Drag when coasting | thrust every other frame up to a per-axis cap; with it off, about 1/128 of the speed goes each step |
| Screen wrap | Both axes | rocks, ship, and shots wrap both ways; the saucer is removed at a side edge |
| Hyperspace | Risk by count | a random jump that fails on up to 8 of 32 values, more of them as rocks run out |
| Enemy splitting | Halving | large rocks leave two medium, medium two small, for 20, 50, and 100 points |
| Rank | Score | after the third saucer, small saucers come more often and always from 30,000 points; they aim tighter from 35,000 |
| Difficulty ramp | More enemies | each wave starts two more large rocks, up to 11 |
| Extra life | Repeating | a ship every 10,000 points |
| High score | Table | ten scores with three initials each |
| Random number generator | Shift register | a 16-bit shift register stepped on each call |
Every claim about this game, by concept (13)
References
- ^ abAsteroids paces its main loop by its 250 Hz NMI: every fourth NMI increments a counter, and the main loop waits on that counter before each pass, so the loop runs at most 62.5 times a second (computed: 250 / 4). The atlas calls one pass a frame. The listing reads Atari's revision 2 program ROMs.CodeComputer Archeology, Asteroids
How we know
7>>> binary 6800:roms/035145.02 + roms/035144.02 + roms/035143.02 ⋮ 23; 6800~035145.02+035144.02+035143.02 (Rev 2)
content/Arcade/Asteroids/Code.md lines 7–23 at 1adc2a7 · upstream ↗ 9The MMI interrupt is clocked at 250Hz.content/Arcade/Asteroids/Hardware.md line 9 at 1adc2a7 · upstream ↗ INC $5E ; {ram.$5E} LDA $5E ; {ram.$5E} AND #$03 BNE $7B83 ; {} INC $5B ; {ram.??timeout??}
content/Arcade/Asteroids/Code.md#L2556-L2563 · upstream ↗ LSR $5B ; {ram.??timeout??} Code check. This value is incremented by one each BCC $680C ; {}
content/Arcade/Asteroids/Code.md#L35-L36 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^While the ship is in play, a held rotate button turns it 3 of its 256 directions per frame. On every other frame (when the fast timer's low bit is clear), a held thrust button adds twice the cosine of the direction to the horizontal velocity and twice the sine to the vertical, read from a 65-entry sine table for a quarter turn, 0 to 127, which the lookup folds for the other quarters. Velocities carry 8 fraction bits, so one thrust step adds at most 254/256 of a position unit per frame to an axis's speed (computed).CodeComputer Archeology, Asteroids
How we know
LDA $2407 ; {hard.SWROTLEFT} Rotate Left Switch … LDA #$03 … LDA #$FD CLC ADC $61 ; {ram.direction} Current Ship Direction STA $61 ; {ram.direction} LDA $5C ; {ram.fastTimer} Fast Timer LSR A BCS $7085 ; {} LDA $2405 ; {hard.SWTHRUST} Thrust Switch …
content/Arcade/Asteroids/Code.md#L1054-L1099 · upstream ↗ ADC #$40 ; A holds current ship direction BPL $77DF ; {} A holds current ship direction … CMP #$41 … EOR #$7F … LDA $57B9,X ; Vector ROM
content/Arcade/Asteroids/Code.md#L1992-L2004 · upstream ↗ 1158# Sine lookup table ⋮ 1161; Used for vertical thrust (offset by 64 to get cosine for horizontal thrust)
content/Arcade/Asteroids/VectorROM.md lines 1158–1178 at 1adc2a7 · upstream ↗ LDY $59 ; {ram.hyperSpaceFlag} Hyperspace Flag … RTS
content/Arcade/Asteroids/Code.md#L1030-L1053 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^Thrust holds each axis of the ship's velocity to its own limit: at most 63 255/256 position units per frame right or up, and 63 255/256 left or down (computed from $3F.FF and $C0.01). Because the axes are clamped apart, a diagonal can reach about 1.41 times the top speed along one axis (computed).CodeComputer Archeology, Asteroids
How we know
BMI $7130 ; {} CMP #$40 BCC $7138 ; {} LDX #$FF LDA #$3F RTS CMP #$C0 BCS $7138 ; {} LDX #$01 LDA #$C0
content/Arcade/Asteroids/Code.md#L1135-L1145 · upstream ↗ ADC $023E ; {ram.horzVelShip} JSR $7125 ; {} STA $023E ; {ram.horzVelShip} STX $64 ; {ram.ship_thrust_dH}
content/Arcade/Asteroids/Code.md#L1076-L1084 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^On the same every-other frames, when thrust isn't held, each axis of the ship's velocity loses 2/256 of its whole-unit part (and 1/256 more when moving right or up), about 1/128 of itself per step, until it reaches zero. With the thrust off, the ship's speed so halves in about 88 steps, 176 frames, about 2.8 seconds at 62.5 frames a second (computed).CodeComputer Archeology, Asteroids
How we know
LDA #$00 STA $3C03 ; {hard.SNDTHRUST} Ship Thrust Sound LDA $023E ; {ram.horzVelShip} ORA $64 ; {ram.ship_thrust_dH} BEQ $7105 ; {} LDA $023E ; {ram.horzVelShip} ASL A LDX #$FF CLC EOR #$FF BMI $70FA ; {} INX …
content/Arcade/Asteroids/Code.md#L1100-L1134 · upstream ↗ 1050223 - 0245 Horizontal Velocity 0-255, (255-192) = Left, 1-63 = Rightcontent/Arcade/Asteroids/RAMUse.md line 105 at 1adc2a7 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^Each frame, Asteroids adds the velocity of every object that isn't exploding (rocks, ship, saucer, and shots) to its position, on a playfield of 32 by 24 units of 256 (8,192 by 6,144 position units, computed). An object that crosses the left or right edge comes back on the other side, except the saucer, which is removed instead; one that crosses the top or bottom comes back at the other edge, the saucer included.CodeComputer Archeology, Asteroids
How we know
JSR $6F57 ; {}
content/Arcade/Asteroids/Code.md#L67 · upstream ↗ LDX #$22 LDA $0200,X ; {ram.statusAsteroids} BNE $6F62 ; {} DEX BPL $6F59 ; {} RTS BPL $6FC7 ; {}
content/Arcade/Asteroids/Code.md#L915-L921 · upstream ↗ LDA $0223,X ; {ram.horzVelAsteroids} … ADC $0269,X ; {ram.hposhAsteroids} CMP #$20 BCC $6FEC ; {} AND #$1F CPX #$1C BNE $6FEC ; {} JSR $702D ; {} … ADC $028C,X ; {ram.vposhAsteroids} CMP #$18 …
content/Arcade/Asteroids/Code.md#L968-L1016 · upstream ↗ LDA #$00 STA $021C ; {ram.statusSaucer} Saucer Flag
content/Arcade/Asteroids/Code.md#L1015-L1021 · upstream ↗ 99| 0200:021A | statusAsteroids | Asteroids 27-1 | ⋮ 101| 021C | statusSaucer | Saucer, 0 = No Saucer, 1 = Small Saucer, 2 = Large Saucer |
content/Arcade/Asteroids/RAMUse.md lines 99–101 at 1adc2a7 · upstream ↗ 1290269 - 028B Horizontal Position High (0-31), 0 = Left, 31 = Right ⋮ 141028C - 02AE Vertical Position High (0-23), 0 = Bottom, 23 = Top
content/Arcade/Asteroids/RAMUse.md lines 129–141 at 1adc2a7 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^Hyperspace works while the ship is in play (not exploding and not waiting to return): it removes the ship, zeroes the whole parts of its velocity (the fractions in $64 and $65 stay), and sets a 48-frame return timer. The new horizontal position is a random whole unit from 3 to 28. A further random number r from 0 to 31 decides the jump: it fails when r is 24 or more and 2 × (r − 24) + 4 is at least the current rock count, so with 4 or fewer rocks 8 of the 32 values fail, and with 19 or more none do (computed). The vertical position is r below 24 and 2 × (r − 24) + 4 (4 to 18) from 24, held to 3 to 20. When the timer runs out the ship returns, or, if the jump failed, explodes and costs a ship.CodeComputer Archeology, Asteroidsrevised
How we know
LDA $021B ; {ram.statusShip} Player Flag BMI $6ED7 ; {} Branch If Currently Exploding LDA $02FA ; {ram.shipSpawnTimer} BNE $6ED7 ; {} LDA $2003 ; {hard.SWHYPER} Hyperspace Switch BPL $6ED7 ; {} Branch If NOT Pressed LDA #$00 STA $021B ; {ram.statusShip} STA $023E ; {ram.horzVelShip} STA $0261 ; {ram.vertVelShip} LDA #$30 STA $02FA ; {ram.shipSpawnTimer} …
content/Arcade/Asteroids/Code.md#L812-L857 · upstream ↗ LDA $02FA ; {ram.shipSpawnTimer} BEQ $7086 ; {} DEC $02FA ; {ram.shipSpawnTimer} BNE $7085 ; {} LDY $59 ; {ram.hyperSpaceFlag} Hyperspace Flag BMI $706F ; {} Gonna Die From Hyperspace, Branch BNE $7068 ; {} Successful Hyperspace, Branch … LDA #$01 ; Flag Ship OK … LDA #$A0 ; Switch To Explosion Timer … …
content/Arcade/Asteroids/Code.md#L1022-L1052 · upstream ↗ 177| 02F6 | curAsteroidCount | Current Amount Of Asteroids |content/Arcade/Asteroids/RAMUse.md line 177 at 1adc2a7 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^A rock that is hit, by the ship, the saucer, or either's shots, halves its size code (large 4, medium 2, small 1): a large rock leaves two medium rocks, a medium rock two small ones, and a small rock none. Each piece takes its parent's position and velocity, adds a random −16 to +15 to each axis, and holds each axis to a speed of 6 to 31. The first piece's horizontal position and the second's vertical one are changed by XOR with twice the low five bits of that piece's velocity. The hit rock becomes an explosion. A piece needs a free slot of the 27, so a full field gives fewer. During a game, a hit by the ship or its shots scores 20 for a large rock, 50 for a medium one, and 100 for a small one.CodeComputer Archeology, Asteroids
How we know
JSR $75EC ; {} … LDA #$A0 STA $0200,Y ; {ram.statusAsteroids}
content/Arcade/Asteroids/Code.md#L421-L435 · upstream ↗ LDA $0200,Y ; {ram.statusAsteroids} AND #$07 LSR A TAX BEQ $7605 ; {} ORA $0E ; {ram.$0E} STA $0200,Y ; {ram.statusAsteroids} LDA $1C ; {ram.numPlayers} Number Of Players In Current Game BEQ $761D ; {} None, Branch LDA $0D ; {ram.$0D} BEQ $7614 ; {} CMP #$04 …
content/Arcade/Asteroids/Code.md#L1740-L1788 · upstream ↗ JSR $77B5 ; {} AND #$8F BPL $720C ; {} ORA #$F0 CLC ADC $0223,Y ; {ram.horzVelAsteroids} JSR $7233 ; {} … CMP #$E1 … CMP #$FB … …
content/Arcade/Asteroids/Code.md#L1236-L1270 · upstream ↗ LDX #$1A LDA $0200,X ; {ram.statusAsteroids} BEQ $7464 ; {} DEX BPL $745C ; {}
content/Arcade/Asteroids/Code.md#L1531-L1536 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^A new wave starts once no rocks are left and a 127-frame wave timer, set when the last rock goes, has run out. Each wave has two more large rocks than the one before, up to 11; the count starts at 2 when a game starts, so the waves run 4, 6, 8, 10, 11, 11 (computed).CodeComputer Archeology, Asteroids
How we know
JSR $6EFA ; {} Reset Sound, Zero Out Sound Timers JSR $6ED8 ; {} Number Of Starting Ships To $56 And Zero JSR $7168 ; {} Start of main game loop???
content/Arcade/Asteroids/Code.md#L29-L31 · upstream ↗ JSR $6885 ; {} BCS $6803 ; {}
content/Arcade/Asteroids/Code.md#L54-L55 · upstream ↗ ASL A ; Zero A, And Set Carry Flag
content/Arcade/Asteroids/Code.md#L150-L152 · upstream ↗ LDA #$02 STA $02F5 ; {ram.asteroidsPerWave}
content/Arcade/Asteroids/Code.md#L858-L859 · upstream ↗ LDA $02FB ; {ram.astdWaveTimer} BEQ $687E ; {} DEC $02FB ; {ram.astdWaveTimer} ORA $02F6 ; {ram.curAsteroidCount} BNE $680C ; {} BEQ $6809 ; {}
content/Arcade/Asteroids/Code.md#L76-L81 · upstream ↗ DEC $02F6 ; {ram.curAsteroidCount} BNE $6F8C ; {} LDY #$7F STY $02FB ; {ram.astdWaveTimer}
content/Arcade/Asteroids/Code.md#L939-L942 · upstream ↗ LDA $02F5 ; {ram.asteroidsPerWave} CLC ADC #$02 CMP #$0B BCC $7193 ; {} Branch if smaller??? LDA #$0B STA $02F6 ; {ram.curAsteroidCount} STA $02F5 ; {ram.asteroidsPerWave} … AND #$18 ORA #$04 STA $0200,X ; {ram.statusAsteroids}
content/Arcade/Asteroids/Code.md#L1182-L1197 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^Between shots, a live saucer counts down 10 runs of its routine, which runs every fourth frame (40 frames, computed), and the count pauses while the player's ship is waiting to return. The large saucer fires in a random direction, any of 256. The small saucer aims: it takes the ship's position minus its own on each axis, subtracts half the saucer's velocity, turns the pair into a direction with an arctangent routine, and adds a random error of −16 to +15 directions while the player has under 35,000 points, and −7 to +8 from 35,000.CodeComputer Archeology, Asteroids
How we know
LDA $5C ; {ram.fastTimer} AND #$03 BEQ $6B9A ; {} RTS
content/Arcade/Asteroids/Code.md#L457-L460 · upstream ↗ LDA $1C ; {ram.numPlayers} Number Of Players In Current Game BEQ $6C4E ; {} None, Branch LDA $02FA ; {ram.shipSpawnTimer} BNE $6C53 ; {} DEC $02F7 ; {ram.saucerTimer} Time for saucer to shoot? BEQ $6C54 ; {} Yes, branch? RTS LDA #$0A ; Time Between Saucer Shots STA $02F7 ; {ram.saucerTimer} Reset for next shot LDA $021C ; {ram.statusSaucer} Saucer Flag LSR A ; Check Saucer Size BEQ $6C65 ; {} Branch If Small One …
content/Arcade/Asteroids/Code.md#L536-L604 · upstream ↗ TYA BPL $76FC ; {} … EOR #$80 EOR #$FF … LDA #$20 RTS JSR $776C ; {} LDA $772F,X ; {}
content/Arcade/Asteroids/Code.md#L1867-L1898 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^Each player's saucer reload value starts at 146 when a game starts and drops by 6 each time a saucer appears, as long as the result stays 32 or more. While it is 128 or more, every saucer is the large one. Below 128, a player with 30,000 points or more always gets the small saucer; under 30,000 a random byte decides, giving the small saucer when the byte is more than half the reload value: about 3 in 4 at the first chance (122) and about 15 in 16 at the floor of 32 (computed, for evenly spread bytes).CodeComputer Archeology, Asteroidsrevised
How we know
LDA #$92 STA $02F8 ; {ram.saucerTimeReload} … STA $02F7 ; {ram.saucerTimer} Countdown Timer For When Saucer Appears
content/Arcade/Asteroids/Code.md#L136-L140 · upstream ↗ LDA $02F8 ; {ram.saucerTimeReload} SEC SBC #$06 ; Shorten Time Between Saucer Appearence CMP #$20 ; At Lowest Value? BCC $6BDD ; {} Yes, Branch STA $02F8 ; {ram.saucerTimeReload} Make The Update
content/Arcade/Asteroids/Code.md#L483-L488 · upstream ↗ LDX #$02 ; Start With Large Saucer LDA $02F8 ; {ram.saucerTimeReload} Start Checking Score When @ #$7F And Below BMI $6C30 ; {} Not There Yet, Branch Around Score Check … CMP #$30 ; 30,000 Points Or More? BCS $6C2F ; {} Yes, Branch JSR $77B5 ; {} STA $08 ; {ram.$08} LDA $02F8 ; {ram.saucerTimeReload} LSR A CMP $08 ; {ram.$08} BCS $6C30 ; {} Going To Be A large Saucer, Branch …
content/Arcade/Asteroids/Code.md#L512-L527 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
Sources
Code
- Howell, Lonnie, Mark McDougall, and Christopher Cantrell. 2026. “Computer Archeology: Asteroids.” Commit 1adc2a7. topherCantrell/computerarcheology @ 1adc2a7 · licence, all uses