Inertia
A craft keeps moving after its thrust stops, its speed built by thrust and worn down by drag.
On this page
A craft with inertia keeps its velocity until thrust or drag changes it. Asteroids adds thrust along the ship’s facing every other frame and takes about 1/128 of the speed away only while the thrust is off1,2. Defender damps its ship’s horizontal speed by 1/64 every frame, thrusting or not, so thrust alone levels off below the speed cap3. In Star Control II, the Earthling Cruiser keeps its velocity when thrust stops, where Asteroids’ coasting ship loses speed to drag4,2. Its cap holds the speed itself, so a diagonal is no faster, where Asteroids clamps each axis apart5,6. The Arilou Skiff has no inertia: it moves at full speed only while thrust is held and stops when it is released7.
- NoneStar Control II (1992), Arilou Skiff. Full speed while thrust is held, and a dead stop when it is let go.7
- No dragStar Control II (1992), Earthling Cruiser. Thrust every 5 frames up to a cap on the speed itself; with thrust off, nothing slows it.8,9,5,4
- Drag when coastingAsteroids (1979). Thrust every other frame up to a per-axis cap; with it off, about 1/128 of the speed goes each step.10,1,2,6
- Drag alwaysDefender (1981). Horizontal speed damped by 1/64 every frame, so thrust levels off at 6 pixels a frame; up and down stop at once.3,11
Games
| Game | Year | How | Note |
|---|---|---|---|
| Star Control II | 1992 | No drag | Earthling Cruiser · thrust every 5 frames up to a cap on the speed itself; with thrust off, nothing slows it |
| Asteroids | 1979 | 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 |
| Defender | 1981 | Drag always | horizontal speed damped by 1/64 every frame, so thrust levels off at 6 pixels a frame; up and down stop at once |
| Descent 3 | 1999 | Drag always | thrust on three axes against a drag proportional to speed, solved exactly each frame, so the ship settles at thrust over drag |
Every claim about this mechanic, by game (13)
References
- ^ abWhile 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.
- ^ abcOn 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.
- ^ abDefender moves its ship once per video frame: the interrupt calls the player routine once each time the beam passes line 128. Each call damps the horizontal velocity by 1/64 of itself, then a held thrust button adds 3/256 of a velocity unit in the facing direction (reverse negates it), and the velocity is held to ±1 unit. A unit is 256 world units, 8 pixels, a frame (the world has 32 units to a pixel). Since the damping runs every frame, thrust alone levels off at 3 × 64 = 192/256 of a unit, 6 pixels a frame, below the cap (computed). The source is the Red Label release.Codemwenge/defender
How we know
75The game source code for Defender in [src](src) was originally retrieved from 76https://github.com/historicalsource/defender. It is the Motorola 6809 assembly language 77source code for the 'Red Label' version of the game.
README.md lines 75–77 at e740063 · upstream ↗ 1937 LDA VERTCT ;PAST 128 1938 CMPA #128 1939 BLO I0 ;NO 1940 LDA IFLG ;ALREADY DONE ? 1941 BNE IRQX ;NO 1942 INC IFLG ⋮ 1944 JSR PLAYER
src/defa7.src lines 1931–1944 at e740063 · upstream ↗ 1960I0 LDB IFLG 1961 BEQ IRQX 1962 CLR IFLG
src/defa7.src lines 1960–1962 at e740063 · upstream ↗ 2336*PLAYER PROCESS 2337*CALLED FORM IRQ ⋮ 2342*X DAMPING 2343 CLR ITEMP 2344 LDD PLAXV 2345 COMA 2346 COMB 2347 ADDD #1 ;NEGD 2348 BPL PL00 2349 COM ITEMP ;SIGN EXTEND 2350PL00 ASLB ; SHIFT 2351 ROLA ⋮
src/defa7.src lines 2336–2371 at e740063 · upstream ↗ 2421 LDD PLAXV 2422 CMPD #$0100 ;MAX VEL 2423 BLT PV11 2424 LDD #$0100 2425PV11 CMPD #-$0100 2426 BGT PV12 2427 LDD #-$0100 2428PV12 STD PLAXV 2429 ADDD BGL 2430 SUBD BGDELT 2431 STD BGL
src/defa7.src lines 2420–2431 at e740063 · upstream ↗ 1249 LDD #$0300 1250 STD PLADIR ;THRUST+DIRECTION
src/defa7.src lines 1249–1250 at e740063 · upstream ↗ 3155*REVERSE ⋮ 3160 LDD PLADIR 3161 COMB 3162 COMA 3163 ADDD #1 3164 STD NPLAD
src/defa7.src lines 3155–3164 at e740063 · upstream ↗ 3333ISCAN LDD BGL 3334 SUBD #100*32 ;100 PIXEL LEFT BUFFER
src/defa7.src lines 3333–3334 at e740063 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^ abA ship that stops thrusting keeps its velocity: the frame's move step (process.c) adds the velocity to the position without changing it, and
ship_preprocesschanges it only throughinertial_thrust, called while thrust is held. The Earthling Cruiser's own code (human.c) sets a velocity only for its missile. Other code can still change a ship's velocity, such as a planet's pull (sc2/gravity-well) and the Arilou Skiff's own routine (sc2/arilou).CodeThe Ur-Quan Masters (2002 import)How we know
141 if (!(state_flags & IGNORE_VELOCITY)) ⋮ 145 GetNextVelocityComponents (&ElementPtr->velocity, 146 &delta_x, &delta_y, 1); ⋮ 150 ElementPtr->next.location.x += delta_x;
sc2/src/sc2code/process.c lines 141–151 at 2f5b2c3 · upstream ↗ 241 if (ElementPtr->thrust_wait) 242 --ElementPtr->thrust_wait; 243 else if (cur_status_flags & THRUST) ⋮ 247 thrust_status = inertial_thrust ((ELEMENTPTR)ElementPtr);
sc2/src/sc2code/ship.c lines 241–254 at 2f5b2c3 · upstream ↗ 111nuke_preprocess (PELEMENT ElementPtr) ⋮ 139 SetVelocityVector (&ElementPtr->velocity,
sc2/src/sc2code/ships/human/human.c lines 111–140 at 2f5b2c3 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^ ab
inertial_thrustcaps the speed itself, not each axis: after adding a thrust step it compares the sum of the squared components withMAX_THRUSTsquared, so a ship is no faster on a diagonal. Asteroids clamps each axis of its ship's velocity apart, so its diagonal is faster (asteroids/top-speed). A step that would pass the cap still applies if the result is slower than the current speed. At the cap, a step along the line of travel sets the velocity to exactlyMAX_THRUSTalong the facing (if the ship isn't above the cap); a step off the line instead adds half a thrust step along the facing and takes a whole step off along the line of travel, kept if that ends within the cap or slower than before, so the course swings toward the facing. While the ship is flagged in a gravity well, a thrust step may take it past the cap up toMAX_ALLOWED_SPEED, 18 display units per frame.CodeThe Ur-Quan Masters (2002 import) · Computer Archeology, AsteroidsHow we know
75 desired_speed = (DWORD)((long)delta_x * delta_x) 76 + (DWORD)((long)delta_y * delta_y); 77 max_speed = (DWORD)WORLD_TO_VELOCITY (max_thrust) 78 * WORLD_TO_VELOCITY (max_thrust); 79 if (desired_speed <= max_speed) 80 SetVelocityComponents (VelocityPtr, delta_x, delta_y); 81 else ⋮ 83#define MAX_ALLOWED_SPEED WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (18)) ⋮ 86 if (((StarShipPtr->cur_status_flags & SHIP_IN_GRAVITY_WELL) 87 && desired_speed <= 88 (DWORD)MAX_ALLOWED_SPEED * (DWORD)MAX_ALLOWED_SPEED) 89 || (current_speed = ⋮
sc2/src/sc2code/ship.c lines 75–128 at 2f5b2c3 · upstream ↗ 56 else if ((TravelAngle = 57 GetVelocityTravelAngle (VelocityPtr)) == CurrentAngle 58 && (StarShipPtr->cur_status_flags 59 & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED)) 60 && !(StarShipPtr->cur_status_flags & SHIP_IN_GRAVITY_WELL))
sc2/src/sc2code/ship.c lines 56–62 at 2f5b2c3 · upstream ↗ 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 ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^ abThrust 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.
- ^ abThe Arilou Skiff's stats set
THRUST_INCREMENTequal toMAX_THRUST, 40 world units (10 display units) per frame, withTURN_WAITandTHRUST_WAIT0. For such a shipinertial_thrustsets the velocity straight toMAX_THRUSTalong the facing. The Skiff's own preprocess routine, whichship_preprocessruns before turning and thrust, zeroes its velocity whenever its thrust wait is 0, outside its jump (while it isn'tNONSOLID). So the Skiff moves at full speed only on frames thrust is held and stops when it is released, with no inertia.CodeThe Ur-Quan Masters (2002 import)How we know
29#define MAX_THRUST /* DISPLAY_TO_WORLD (10) */ 40 30#define THRUST_INCREMENT MAX_THRUST 31#define TURN_WAIT 0 32#define THRUST_WAIT 0
sc2/src/sc2code/ships/arilou/arilou.c lines 29–32 at 2f5b2c3 · upstream ↗ 50 if (thrust_increment == max_thrust) ⋮ 52 SetVelocityVector (VelocityPtr, 53 max_thrust, StarShipPtr->ShipFacing); 54 return (SHIP_AT_MAX_SPEED);
sc2/src/sc2code/ship.c lines 50–55 at 2f5b2c3 · upstream ↗ 185arilou_preprocess (PELEMENT ElementPtr) ⋮ 190 if (!(ElementPtr->state_flags & NONSOLID)) ⋮ 192 if (ElementPtr->thrust_wait == 0) ⋮ 194 ZeroVelocityComponents (&ElementPtr->velocity);
sc2/src/sc2code/ships/arilou/arilou.c lines 185–196 at 2f5b2c3 · upstream ↗ 217 if (RDPtr->preprocess_func) ⋮ 219 (*RDPtr->preprocess_func) (ElementPtr); ⋮ 223 if (ElementPtr->turn_wait) ⋮ 247 thrust_status = inertial_thrust ((ELEMENTPTR)ElementPtr);
sc2/src/sc2code/ship.c lines 217–247 at 2f5b2c3 · upstream ↗ 280 arilou_desc.preprocess_func = arilou_preprocess;sc2/src/sc2code/ships/arilou/arilou.c line 280 at 2f5b2c3 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^In the 3DO source as The Ur-Quan Masters first imported it (2002), each pass of the battle loop sleeps until 5 ticks (
BATTLE_FRAME_RATE) after the previous pass woke, on a clock of 120 ticks a second (ONE_SECOND), so battle runs at most 24 frames a second (computed: 120 / 5). The sleep is the port'sSleepTask, in its SDL wrapper. Whennth_frameis set, which encount.c does only while the cyborg option flies the player's ships in an encounter, the loop only yields (TaskSwitch) and does not wait. The atlas calls one pass a frame.CodeThe Ur-Quan Masters (2002 import)How we know
22#define BATTLE_FRAME_RATE 5sc2/src/sc2code/element.h line 22 at 2f5b2c3 · upstream ↗ 24#define ONE_SECOND 120sc2/src/sc2code/libs/timlib.h line 24 at 2f5b2c3 · upstream ↗ 229 if (nth_frame) 230 TaskSwitch (); 231 else 232 NextTime = SleepTask (NextTime + BATTLE_FRAME_RATE);
sc2/src/sc2code/battle.c lines 229–232 at 2f5b2c3 · upstream ↗ 1315SleepTask (DWORD wake_time) ⋮ 1320 if (wake_time <= t) 1321 SDL_Delay (0); 1322 else 1323 SDL_Delay ((wake_time - t) * 1000 / ONE_SECOND); ⋮ 1325 return (GetTimeCounter ());
sc2/src/SDL_wrapper.c lines 1315–1325 at 2f5b2c3 · upstream ↗ 745 if (GLOBAL (glob_flags) & CYBORG_ENABLED) ⋮ 757 nth_frame = MAKE_WORD (1, cur_speed); 758 PlayerControl[0] = CYBORG_CONTROL | AWESOME_RATING;
sc2/src/sc2code/encount.c lines 745–758 at 2f5b2c3 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^The Earthling Cruiser's stats set
MAX_THRUST24,THRUST_INCREMENT3,TURN_WAIT1, andTHRUST_WAIT4. Speeds are in world units per frame;DISPLAY_TO_WORLDmakes one display unit 4 world units. Each frame,ship_preprocesscounts a ship's turn wait down, or, when it is 0 and a turn is held, steps the ship one of its 16 facings and sets the wait toTURN_WAIT. Thrust works the same way with its own wait: when the wait is 0 and thrust is held,ship_preprocesscallsinertial_thrustonce and sets the wait toTHRUST_WAIT. Each such call is one thrust step, which addsTHRUST_INCREMENTto the velocity along the facing, up toMAX_THRUST(sc2/speed-cap). So the Cruiser turns a facing every 2 frames and takes a thrust step every 5 frames. Each step raises its speed by 3 world units per frame (3/4 of a display unit per frame), to a top speed of 24 world units (6 display units) per frame, which it reaches from rest after 8 steps along an axis (computed). Comments beside the two thrust values readDISPLAY_TO_WORLD (6), which is 24 and agrees with the code, andDISPLAY_TO_WORLD (2), which is 8 and disagrees with the code's 3.CodeThe Ur-Quan Masters (2002 import)revisedHow we know
29#define MAX_THRUST /* DISPLAY_TO_WORLD (6) */ 24 30#define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 3 31#define TURN_WAIT 1 32#define THRUST_WAIT 4
sc2/src/sc2code/ships/human/human.c lines 29–32 at 2f5b2c3 · upstream ↗ 57 MAX_THRUST, 58 THRUST_INCREMENT, ⋮ 63 TURN_WAIT, 64 THRUST_WAIT,
sc2/src/sc2code/ships/human/human.c lines 56–67 at 2f5b2c3 · upstream ↗ 74 COUNT max_thrust, 75 thrust_increment; ⋮ 80 turn_wait, 81 thrust_wait,
sc2/src/sc2code/races.h lines 74–84 at 2f5b2c3 · upstream ↗ 30#define ONE_SHIFT 2 ⋮ 33#define DISPLAY_TO_WORLD(x) ((x)<<ONE_SHIFT)
sc2/src/sc2code/units.h lines 30–33 at 2f5b2c3 · upstream ↗ 105#define FACING_SHIFT 4 ⋮ 112#define NORMALIZE_FACING(f) ((COUNT)(f)&((1 << FACING_SHIFT)-1))
sc2/src/sc2code/units.h lines 105–112 at 2f5b2c3 · upstream ↗ 223 if (ElementPtr->turn_wait) 224 --ElementPtr->turn_wait; 225 else if (cur_status_flags & (LEFT | RIGHT)) ⋮ 229 NORMALIZE_FACING (StarShipPtr->ShipFacing - 1); 230 else ⋮ 238 ElementPtr->turn_wait = RDPtr->characteristics.turn_wait; ⋮ 241 if (ElementPtr->thrust_wait) 242 --ElementPtr->thrust_wait; 243 else if (cur_status_flags & THRUST) ⋮ 247 thrust_status = inertial_thrust ((ELEMENTPTR)ElementPtr); ⋮ 254 ElementPtr->thrust_wait = RDPtr->characteristics.thrust_wait;
sc2/src/sc2code/ship.c lines 223–254 at 2f5b2c3 · upstream ↗ 69 thrust_increment = WORLD_TO_VELOCITY (thrust_increment); 70 GetCurrentVelocityComponents (VelocityPtr, &cur_delta_x, &cur_delta_y); 71 delta_x = cur_delta_x 72 + COSINE (CurrentAngle, thrust_increment); 73 delta_y = cur_delta_y 74 + SINE (CurrentAngle, thrust_increment); ⋮ 79 if (desired_speed <= max_speed) 80 SetVelocityComponents (VelocityPtr, delta_x, delta_y);
sc2/src/sc2code/ship.c lines 69–80 at 2f5b2c3 · upstream ↗ 49#define VELOCITY_SHIFT 5 ⋮ 53#define WORLD_TO_VELOCITY(l) ((l)<<VELOCITY_SHIFT)
sc2/src/sc2code/velocity.h lines 49–53 at 2f5b2c3 · upstream ↗ 141 if (!(state_flags & IGNORE_VELOCITY)) ⋮ 145 GetNextVelocityComponents (&ElementPtr->velocity, 146 &delta_x, &delta_y, 1); ⋮ 150 ElementPtr->next.location.x += delta_x; 151 ElementPtr->next.location.y += delta_y;
sc2/src/sc2code/process.c lines 141–151 at 2f5b2c3 · 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.
- ^Asteroids 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.
- ^Up and down have no inertia in Defender: a held direction moves the ship 1 pixel a frame at once, rising by 8/256 of a pixel each frame to 2 pixels a frame (after 32 frames, computed), and letting go stops it. The ship stops climbing once at row 43 or higher, and descending at row 238 or lower.Codemwenge/defender
How we know
2441*VERTICAL MOTION 2442 LDB PLAY16 ;GET ADDR FOR LIMIT CHK 2443 LDA PIA31 ;UP? 2444 LSRA 2445 BCS PLAUP ;YES 2446 LDA PIA21 2447 BMI PLADN ;DOWN 2448 LDD #0 2449 BRA PYV1 2450PLAUP CMPB #YMIN+1 2451 BLS PLAYX 2452 LDD PLAYV ⋮
src/defa7.src lines 2441–2476 at e740063 · upstream ↗ 20YMAX EQU 240 21YMIN EQU 42 ;MIN Y COORD
src/phr6.src lines 20–21 at e740063 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked 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
- Jarvis, Eugene, Sam Dicker, and Robert Hargreaves. 2023. “Defender (1981) by Eugene Jarvis and Sam Dicker: the 6809 source.” Commit e740063. mwenge/defender @ e740063 · licence, all uses
- Reiche, Paul, Fred Ford, and The Ur-Quan Masters project. 2002. “The Ur-Quan Masters: Star Control II's 3DO source, the project's initial import.” Commit 2f5b2c3. sc2/uqm @ 2f5b2c3 · licence, all uses