Running
How a character's speed on the ground follows the d-pad: how it starts, how fast it gets, and how it stops and turns.
On this page
Running is how a character’s speed on the ground follows the d-pad. Three games span the range, from a speed that switches on and off to one that builds and fades. The lab runs each game’s rules frame by frame, checked against its cartridge.
- InstantCastlevania (1986). Simon walks at one speed from the first frame and stops dead.1
- SteppedMega Man (1987). A short first step, then full speed, with no acceleration.2Mega Man 2 (1988). A short first step, then full speed, with no acceleration.3,4Mega Man 3 (1990). One walking speed, with a brief halt after the first step.5
- Ramp upMetroid (1986). 0.1875 px/frame² to 1.5 px/frame after a still first frame, a stop the moment the d-pad is released, and 16 frames to turn at full speed.6,7,8
- MomentumSonic the Hedgehog (1991). Speed builds and fades slowly, to a single top speed.9,10Sonic the Hedgehog 2 (1992). Speed builds and fades slowly to a top speed, and on the ground holding forward lets him keep a speed above it.11,12Sonic 3 & Knuckles (1994). The top speed, acceleration, and braking of the first two games.13
- AnalogSuper Mario 64 (1996). The stick's tilt sets a target speed of up to 32 units/frame, approached by 1.1 − speed/43 a frame.14
- Run buttonSuper Mario Bros. (1985). Holding B raises his top speed from 1.5 to 2.5 px a frame.15,16
- P-meterSuper Mario World (1990). A full P-meter raises the top speed from 2.25 to 3 px/frame.17,18Sonic Advance 2 (2002). Holding forward at the 6 px/frame top speed fills a hidden counter, faster with more rings, and boost mode then raises the top speed to 12.19,20,21,22
#Later games
Wolfenstein 3D keeps no velocity for the player, so a step starts and stops at once23, as Simon’s walk does1. Quake III Arena accelerates by Quake’s rule against a friction of 624, and a run settles at the full wish speed, 32025,25. Sonic Advance 2 keeps Sonic’s top speed20,26 but raises his acceleration with the ring count19, where Sonic’s is fixed27. Its boost mode doubles that top speed22. Banjo-Kazooie maps the stick’s tilt to a target of up to 500 units/s28 in five bands28, where Super Mario 64 squares the tilt14.
Mega Man 3 drops Mega Man 2’s slow step speed: from standing, a held direction moves Mega Man at 1.30 px/frame ($01.$4C)5 at once, then holds him still while the turning animation stays on its first frame5. The atlas hasn’t read the sequence data that sets the length of that pause5. Sonic the Hedgehog 2 keeps Sonic the Hedgehog’s top speed, acceleration, and deceleration11,26,27,29. On the ground, holding forward no longer pulls a faster Sonic down to the top speed12. Sonic 3 & Knuckles keeps the same three values13.
#Quirks
- Super Mario World tests only the speed’s high byte against the cap and adds the acceleration unclamped, so Mario’s top speed cycles around the cap instead of resting on it30.
- Super Mario Bros. doubles its braking whenever Mario faces away from his motion, so a skid brakes at twice the rate of a release31. Any held button but A stops a slow slide dead32. The level’s first step is doubled too, because Mario’s moving direction starts at 031.
- Mega Man lands running if a direction was held the frame before he lands, with no first step33.
- Simon can’t move while he crouches, but he can turn34,35.
- Sonic the Hedgehog takes 128 frames each way9 to reach top speed or coast to a stop9, and his turn from top speed isn’t symmetric: 140 frames right to left, 130 left to right10,10,36.
- Celeste uses one rate to speed up, turn, and stop, so Madeline reaches top speed in 6 updates on the ground, 9 in the air37,37,38.
Games
| Game | Year | How | Note |
|---|---|---|---|
| Felix the Cat | 1992 | the x speed builds by a step from RAM to a cap of 533/256 px a frame | |
| Castlevania | 1986 | Instant | Simon walks at one speed from the first frame and stops dead |
| Metroid II: Return of Samus | 1991 | Instant | 1.5 px/frame from 1- and 2-pixel steps, 2 px/frame with the Varia Suit |
| Wolfenstein 3D | 1992 | Instant | no velocity: each frame moves the player by the keys' value, 5,250 units a tic walking and 10,500 running, and stops at once on release |
| Mega Man | 1987 | Stepped | a short first step, then full speed, with no acceleration |
| Mega Man 2 | 1988 | Stepped | a short first step, then full speed, with no acceleration |
| Mega Man 3 | 1990 | Stepped | one walking speed, with a brief halt after the first step |
| Mega Man 6 | 1993 | Stepped | a step, a short halt, then the walking speed |
| Metroid | 1986 | Ramp up | 0.1875 px/frame² to 1.5 px/frame after a still first frame, a stop the moment the d-pad is released, and 16 frames to turn at full speed |
| Sonic the Hedgehog | 1991 | Momentum | speed builds and fades slowly, to a single top speed |
| Sonic the Hedgehog 2 | 1992 | Momentum | speed builds and fades slowly to a top speed, and on the ground holding forward lets him keep a speed above it |
| Doom | 1993 | Momentum | a thrust each tic on the floor against 0.90625 friction, settling near 16.7 units/tic at a run |
| Sonic 3 & Knuckles | 1994 | Momentum | the top speed, acceleration, and braking of the first two games |
| Duke Nukem 3D | 1996 | Momentum | a thrust each tic, in the air too, against 0.812 friction from the CON's RUNNINGSPEED |
| Quake | 1996 | Momentum | acceleration 10 against friction 4 settles a run at exactly the 320 cap, reached in about a seventh of a second |
| Quake II | 1997 | Momentum | acceleration 10 against a friction of 6 settles a run at exactly the 300 cap, the numbers Quake III kept |
| Quake III Arena | 1999 | Momentum | acceleration 10 against a friction of 6, up from Quake's 4, so a run still settles at exactly 320 but stops sooner |
| Celeste | 2018 | Momentum | one rate to speed up, turn, and stop |
| Super Mario 64 | 1996 | Analog | the stick's tilt sets a target speed of up to 32 units/frame, approached by 1.1 − speed/43 a frame |
| Banjo-Kazooie | 1998 | Analog | five bands of stick tilt map linearly to a target of up to 500 units/s, closed by a fraction of the gap each frame |
| Super Mario Bros. | 1985 | Run button | holding B raises his top speed from 1.5 to 2.5 px a frame |
| Super Mario Bros.: The Lost Levels | 1986 | Run button | Super Mario Bros.'s top speeds; Luigi takes longer to speed up, stop, and turn |
| Super Mario Bros. 2 | 1988 | Run button | B raises the top speed by half, from 1.5 to 2.25 px/frame; carrying an enemy or a block changes the cap |
| Super Metroid | 1994 | Run button | 2.75 px/frame, and holding dash builds up to 2 px/frame more, 7 with the Speed Booster |
| Super Mario Bros. 3 | 1988 | P-meter | B raises the top speed to 2.5 px/frame, and a full P-meter to 3.5 |
| Super Mario World | 1990 | P-meter | a full P-meter raises the top speed from 2.25 to 3 px/frame |
| Sonic Advance 2 | 2002 | P-meter | holding forward at the 6 px/frame top speed fills a hidden counter, faster with more rings, and boost mode then raises the top speed to 12 |
Every claim about this mechanic, by game (135)
References
- ^ abSimon has no acceleration; each frame on the ground his direction bits are set from the d-pad's held Left and Right, and the move routine shifts him by his speed bytes, 1 px/frame, in that direction or not at all.Verified“Castlevania (USA) (Rev 1)”
How we know
In the cartridge (“Castlevania (USA) (Rev 1)”): bank 6 $93D9-$93E3, the standing state: copy the held buttons ($F7) to $0584, then call the direction routine $9809 and the move routine $989A; bank 6 $9815-$982E: direction bits $3C = bit 2 kept | ($0584 AND 3), the held Right (1) and Left (2); bank 6 $989A-$990A: no direction bits, no move; else add or subtract $3E.$3D to his screen X or to the camera; bank 7 $C9A5-$C9AC, stage start sets $3D = 0 and $3E = 1. We don't reproduce the bytes.
Measured by a script in MesenCE 2.2.1 (build 4b8a196), on Castlevania (USA) (Rev 1) (SHA-1
4e3ef47de86a…). It fails if the result disagrees with the claim.The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^Off ice, Mega Man never accelerates. Each frame his animation picks a speed from
special_object_xcoord_speed_table, 0 standing, 0.125 px/frame on the first step, 1.375 running, 0.5 sliding to a stop, 0 landing, and 0.5625 when hit; any other animation, such as the jump, moves at 1.3125 with a direction held and 0 without.Codelsmmega/mm1revisedHow we know
1_ice_tsa_move: 2 LDY #$00 3 LDA aobject_pointer 4 LDX #$08 ⋮ 6@loop: 7 CMP @special_object_id_table, X 8 BEQ @yes 9 DEX 10 BPL @loop 11 LDA z:zjoy1_pressed 12 AND #left_button | right_button 13 BEQ @regular ⋮
engine/ice_tsa.asm lines 1–22 at b606a84 · upstream ↗ 146@special_object_id_table: 147 .BYTE $00 148 .BYTE $03 149 .BYTE $06 150 .BYTE $0C 151 .BYTE $0F 152 .BYTE $12 153 .BYTE $13 154 .BYTE $14 155 .BYTE $6E ⋮ 157@special_object_id_index_xcoord_speed_table: 158 .BYTE $00 ⋮
engine/ice_tsa.asm lines 146–166 at b606a84 · upstream ↗ 1special_object_xcoord_speed_table: 2;fraction, integer 3 .BYTE $00 | $00 4 .BYTE $00 | $20 5 .BYTE $01 | $20 6 .BYTE $00 | $80 7 .BYTE $01 | $00 8 .BYTE $04 | $00 9 .BYTE $05 | $10 10 .BYTE $01 | $50 11 .BYTE $01 | $60 12 .BYTE $00 | $90
data/special_xcoord_speed_table.asm lines 1–12 at b606a84 · upstream ↗ 22_write_special_object_xcoord_speed_continue: 23 LDA special_object_xcoord_speed_table, Y 24 PHA 25 AND #%11110000 26 STA aobject_xcoord_speed_fraction, X 27 PLA 28 AND #%00001111 29 STA aobject_xcoord_speed, X
home/write_special_object_xcoord_speed.asm lines 22–29 at b606a84 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-26.
- ^Mega Man never accelerates; each frame his state's row of
megaman_status_xcoord_speed_tablesets the speed outright, 0 standing, 0.125 px/frame on the first step, 1.375 running, 1.3125 jumping, and 0.5 sliding to a stop.Codelsmmega/mm2How we know
72_megaman_status_parameter: 73 LDX z:zmegaman_status 74 LDA megaman_status_xcoord_speed_table, X 75 STA aobject_xcoord_speed 76 LDA megaman_status_xcoord_speed_fraction_table, X 77 STA aobject_xcoord_speed_fraction
engine/turn_megaman.asm lines 72–77 at 94f0bf3 · upstream ↗ 222megaman_status_xcoord_speed_table: 223 .BYTE $00 224 .BYTE $00 225 .BYTE $00 226 .BYTE $00 227 .BYTE $00 228 .BYTE $01 229 .BYTE $01 230 .BYTE $00 231 .BYTE $00 232 .BYTE $00 233 .BYTE $00 ⋮
engine/turn_megaman.asm lines 222–243 at 94f0bf3 · upstream ↗ 401run_megaman_lo_jumptable: 402 .LOBYTES _run_megaman_teleport_out 403 .LOBYTES _run_megaman_nothing 404 .LOBYTES _run_megaman_megaman_hit 405 .LOBYTES _run_megaman_standing 406 .LOBYTES _run_megaman_walk_slow 407 .LOBYTES _run_megaman_running 408 .LOBYTES _run_megaman_jumping 409 .LOBYTES _run_megaman_landing 410 .LOBYTES _run_megaman_landing
engine/run_megaman.asm lines 401–412 at 94f0bf3 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^Measured in the game, Mega Man first steps 0.125 px/frame for 7 frames, then runs at 1.375 px/frame.Verifiedlsmmega/mm2
How we know
222megaman_status_xcoord_speed_table: 223 .BYTE $00 224 .BYTE $00 225 .BYTE $00 226 .BYTE $00 227 .BYTE $00 228 .BYTE $01 229 .BYTE $01 230 .BYTE $00 231 .BYTE $00 232 .BYTE $00 233 .BYTE $00 ⋮
engine/turn_megaman.asm lines 222–246 at 94f0bf3 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Checked by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-25.
- ^ abcdMega Man 3 drops Mega Man 2's slow step speed but keeps a pause. Before each frame's state runs, the prelude sets the speed to
$01.$4C(1.30 px/frame) unless he is sliding. From standing, a held direction moves him by it at once and starts the turning animation ($0D); while that animation is on its first frame the ground state doesn't move him, and once it advances he walks at the same 1.30. In the air a held direction moves him at 1.30 on every frame.Codeplasticsmoke/megaman3-disassembly-ca65 · lsmmega/mm2revisedHow we know
636prelude_check_slide_speed: lda ent_xvel ; if X speed whole == $02 (slide speed): 637 cmp #$02 ; slide speed? 638 bne prelude_reset_walk_speed ; no → reset to walk speed 639 lda player_state ; load current player state 640 cmp #PSTATE_SLIDE ; check if sliding 641 beq prelude_setup_per_frame ; sliding, keep slide speed 642prelude_reset_walk_speed: lda #$4C ; reset walk speed to $01.4C 643 sta ent_xvel_sub ; store walk sub-pixel speed 644 lda #$01 ; walk speed whole = $01 645 sta ent_xvel ; store walk whole speed
src/fixed/game_loop.asm lines 636–645 at 58ca1c3 · upstream ↗ 115player_ground_turning_anim: lda #$0D ; OAM $0D = turning animation 116 jsr reset_sprite_anim ; set turning animation 117 lda shoot_anim_timer ; check if shooting 118 beq player_ground_move_facing ; not shooting, move facing 119 jsr shoot_oam_advance_variant ; update shoot animation variant 120player_ground_move_facing: lda player_facing ; move in facing direction 121 jmp player_ground_move_horizontal ; move in facing direction ⋮ 125player_ground_walk_handler: lda joy1_held ; D-pad L/R held? 126 and #$03 ; mask L/R bits 127 beq player_ground_idle ; no → idle 128 sta player_facing ; update facing direction
src/fixed/player_ground.asm lines 115–128 at 58ca1c3 · upstream ↗ 94 cmp #$0D ; $0D/$0E/$0F = turning/special anims 95 beq player_ground_special_anim ; OAM $0D → check if anim complete ⋮ 100player_ground_special_anim: lda ent_anim_state ; check animation state 101 bne player_ground_walk_handler ; → normal walk handler 102 beq player_ground_slide_weapon ; anim done → slide/crouch check
src/fixed/player_ground.asm lines 94–102 at 58ca1c3 · upstream ↗ 210 lda ent_anim_state,x ; ent_anim_state = current frame index 211 and #$7F ; (bit 7 preserved) 212 inc ent_anim_state,x ; advance to next frame ⋮ 216 lda #$00 ; reached end: loop back to frame 0 217 sta ent_anim_state,x ; reset to frame 0
src/fixed/sprites.asm lines 210–217 at 58ca1c3 · upstream ↗ 360player_airborne_walk_shoot: lda joy1_held ; D-pad L/R held? 361 and #$03 ; mask L/R bits 362 beq player_airborne_shoot_check ; no → skip walk 363 sta player_facing ; update facing direction 364 jsr player_ground_move_horizontal ; move L/R with collision
src/fixed/player_ground.asm lines 360–364 at 58ca1c3 · upstream ↗ 259move_sprite_right: lda ent_x_sub,x ; load X subpixel position 260 clc ; X subpixel += X subpixel speed 261 adc ent_xvel_sub,x ; add X subpixel velocity 262 sta ent_x_sub,x ; store new X subpixel 263 lda ent_x_px,x ; load X pixel position 264 adc ent_xvel,x ; add X pixel velocity + carry 265 sta ent_x_px,x ; store new X pixel
src/fixed/movement.asm lines 259–265 at 58ca1c3 · upstream ↗ 222megaman_status_xcoord_speed_table: 223 .BYTE $00 224 .BYTE $00 225 .BYTE $00 226 .BYTE $00 227 .BYTE $00 ⋮ 230 .BYTE $00 231 .BYTE $00 232 .BYTE $00 233 .BYTE $00 ⋮ 235megaman_status_xcoord_speed_fraction_table: 236 .BYTE $00 ⋮
engine/turn_megaman.asm lines 222–243 at 94f0bf3 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-26.
- ^On the ground Samus accelerates at
$30/256 (0.1875) px/frame² towardSamusHorzSpdMax/16 px/frame, whichVertAcceleratesets to$18(1.5 px/frame) while she is grounded. With no direction held,StopHorzMovementclears her horizontal speed at once.Codenmikstas/metroid-disassemblyHow we know
2245RunAccelerationTbl: 2246LCCC0: .byte $30 ;Accelerate right. 2247 .byte $D0 ;Accelerate left.
Source_Files/Bank07.asm lines 2245–2247 at 4270d57 · upstream ↗ 5590LE37A: lda SamusGravity ;Is Samus rising or falling? 5591LE37D: bne ++ ;Branch if yes. 5592LE37F: lda #$18 ; 5593LE381: sta SamusHorzSpdMax ;Set Samus maximum running speed.
Source_Files/Bank07.asm lines 5590–5593 at 4270d57 · upstream ↗ 5648HorzAccelerate: 5649LE3E5: lda SamusHorzSpdMax 5650 jsr Amul16 ; * 16 ⋮ 5653 lda SamusHorzSpdMax 5654 jsr Adiv16 ; / 16 ⋮ 5658 lda HorzCntrLinear 5659 clc 5660 adc SamusHorzAccel 5661 sta HorzCntrLinear ⋮ 5670 sta ObjHorzSpeed ⋮ 5687 lda $00 5688 sta HorzCntrLinear ⋮
Source_Files/Bank07.asm lines 5648–5690 at 4270d57 · upstream ↗ 2316* lda Joy1Status 2317 and #$03 2318 bne + 2319 jsr LCF55
Source_Files/Bank07.asm lines 2316–2319 at 4270d57 · upstream ↗ 2603ClearHorzMvmntData: 2604LCF4C: LDY #$00 ; ⋮ 2606 STY HorzCntrLinear ;linear counter to #$00. ⋮ 2609StopHorzMovement: ⋮ 2615LCF5D: JSR NoHorzMoveNoDelay ;($CF81)Clear horizontal movement and animation delay data. ⋮ 2638LCF81: JSR ClearHorzData ;($CFB7)Clear all horizontal movement data. ⋮ 2664ClearHorzData: 2665LCFB7: JSR ClearHorzMvmntData ;($CF4C)Clear horizontal speed and linear counter.
Source_Files/Bank07.asm lines 2600–2666 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^From a standstill, the first frame with Left or Right held doesn't move Samus.
SamusStandpicks the running state, callsSetSamusData, which moves her at her speed of 0, and only then callsSetSamusRun, which sets her acceleration. Her speed rises from the next frame.Codenmikstas/metroid-disassemblyHow we know
2162SamusStand: 2163LCC36: LDA Joy1Status ;Status of joypad 1. 2164LCC38: AND #$CF ;Remove SELECT & START status bits. 2165LCC3A: BEQ + ;Branch if no buttons pressed. 2166LCC3C: JSR ClearHrztAnimData ;($CF5D)Set no horiontal movement and single frame animation. ⋮ 2173LCC4B:* JSR BitScan ;($E1E1)Find which directional button is pressed. ⋮ 2176LCC52: STA SamusDir ;1=left, 0=right. 2177LCC54:* TAX ; 2178LCC55: LDA ActionTable,x ;Load proper Samus status from table below. 2179LCC58: STA ObjAction ;Save Samus status. ⋮ 2190LCC70: JSR SetSamusData ;($CD6D)Set Samus control data and animation. ⋮ 2194LCC7A: JSR ChooseRoutine ;Select routine below. ⋮
Source_Files/Bank07.asm lines 2162–2199 at 4270d57 · upstream ↗ 2236 ldx SamusDir 2237LCCB7: lda RunAccelerationTbl,x 2238 sta SamusHorzAccel
Source_Files/Bank07.asm lines 2236–2238 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^Running on the ground, Left or Right against Samus's facing turns her and sets the acceleration toward the new side on that frame, through
SetSamusRun, but leaves her speed as it is. She slows by 0.1875 px/frame each frame, passes 0, and speeds up the other way: from 1.5 px/frame, 16 frames to 1.5 px/frame the other way (computed).Codenmikstas/metroid-disassemblyHow we know
2316* lda Joy1Status 2317 and #$03 2318 bne + 2319 jsr LCF55 2320 jmp LCD6B ⋮ 2322* jsr BitScan ;($E1E1) 2323 cmp SamusDir 2324 beq LCD6B 2325 sta SamusDir 2326 jsr LCC98
Source_Files/Bank07.asm lines 2316–2326 at 4270d57 · upstream ↗ 2236 ldx SamusDir 2237LCCB7: lda RunAccelerationTbl,x 2238 sta SamusHorzAccel ⋮ 2246LCCC0: .byte $30 ;Accelerate right. 2247 .byte $D0 ;Accelerate left.
Source_Files/Bank07.asm lines 2236–2247 at 4270d57 · upstream ↗ 5658 lda HorzCntrLinear 5659 clc 5660 adc SamusHorzAccel 5661 sta HorzCntrLinear
Source_Files/Bank07.asm lines 5658–5661 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^ abcIn the running game, on flat ground, Sonic reaches top speed from a standstill in 128 frames, and from top speed he coasts to a stop in 128 frames with the d-pad released.Verifiedsonicretro/s1disasmrevised
How we know
5son_maxspeed: equ $600 ; Sonic's max speed 6son_acceleration: equ $C ; Sonic's acceleration
_incObj/01 Sonic.asm lines 5–6 at 15dfa3a · upstream ↗ Measured by a script. It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Revised after a check by a second AI model, 2026-10-25.
- ^ abcIn the running game, on flat ground, turning around from top speed takes 140 frames to reach top speed the other way when Sonic runs right, and 130 when he runs left.Verifiedsonicretro/s1disasmrevised
How we know
665.changeddirection: 666 sub.w d4,d0 ; apply deceleration to current speed 667 bcc.s .stilldecel ; if still decelerating, branch 668 move.w #-$80,d0 ; set minimum speed on sign change
_incObj/01 Sonic.asm lines 636–672 at 15dfa3a · upstream ↗ 715.alreadyright: 716 add.w d5,d0 ; add acceleration to current ground speed 717 cmp.w d6,d0 ; is new speed above max speed? 718 blt.s .nocap ; if not, branch 719 move.w d6,d0 ; cap Sonic's ground speed
_incObj/01 Sonic.asm lines 706–736 at 15dfa3a · upstream ↗ Measured by a script. It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Revised after a check by a second AI model, 2026-10-25.
- ^ ab
Obj01_Initsets Sonic's top speed to$600, his acceleration to$C, and his deceleration to$80subpixels, Sonic 1's values; speed shoes and Super Sonic swap in others.Codesonicretro/s2disasm · sonicretro/s1disasmrevisedHow we know
36232 move.w #$600,(Sonic_top_speed).w ; set Sonic's top speed 36233 move.w #$C,(Sonic_acceleration).w ; set Sonic's acceleration 36234 move.w #$80,(Sonic_deceleration).w ; set Sonic's deceleration
s2.asm lines 36232–36234 at 4ef457c · upstream ↗ 36364 move.w #$A00,(Sonic_top_speed).w ; restore Super Sonic's top speeds2.asm lines 36364–36366 at 4ef457c · upstream ↗ 5son_maxspeed: equ $600 ; Sonic's max speed 6son_acceleration: equ $C ; Sonic's acceleration 7son_deceleration: equ $80 ; Sonic's deceleration
_incObj/01 Sonic.asm lines 5–7 at 15dfa3a · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-21.
- ^ abHolding forward no longer pulls a faster Sonic down to the top speed on the ground;
Sonic_MoveRightundoes the frame's acceleration and keeps the speed he had.Codesonicretro/s2disasm · sonicretro/s1disasmrevisedHow we know
36998 add.w d5,d0 ; add acceleration to the right ⋮ 37001 sub.w d5,d0 ; remove this frame's acceleration change ⋮ 37003 bge.s + ; if speed was already greater than the maximum, branch 37004 move.w d6,d0 ; limit speed on ground going right
s2.asm lines 36998–37004 at 4ef457c · upstream ↗ 716 add.w d5,d0 ; add acceleration to current ground speed 717 cmp.w d6,d0 ; is new speed above max speed? 718 blt.s .nocap ; if not, branch 719 move.w d6,d0 ; cap Sonic's ground speed
_incObj/01 Sonic.asm lines 716–719 at 15dfa3a · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-21.
- ^ ab
Sonic_Initsets the top speed to$600, the acceleration to$C, and the deceleration to$80subpixels, the values of Sonic 1 and Sonic 2.Codesonicretro/skdisasm · sonicretro/s1disasm · sonicretro/s2disasmrevisedHow we know
21950 move.w #$600,Max_speed-Max_speed(a4) 21951 move.w #$C,Acceleration-Max_speed(a4) 21952 move.w #$80,Deceleration-Max_speed(a4)
sonic3k.asm lines 21950–21952 at 73de97c · upstream ↗ 22902 add.w d5,d0 22903 cmp.w d6,d0 22904 blt.s loc_114AA 22905 sub.w d5,d0 22906 cmp.w d6,d0 22907 bge.s loc_114AA 22908 move.w d6,d0
sonic3k.asm lines 22902–22908 at 73de97c · upstream ↗ 5son_maxspeed: equ $600 ; Sonic's max speed 6son_acceleration: equ $C ; Sonic's acceleration 7son_deceleration: equ $80 ; Sonic's deceleration
_incObj/01 Sonic.asm lines 5–7 at 15dfa3a · upstream ↗ 36232 move.w #$600,(Sonic_top_speed).w ; set Sonic's top speed 36233 move.w #$C,(Sonic_acceleration).w ; set Sonic's acceleration 36234 move.w #$80,(Sonic_deceleration).w ; set Sonic's deceleration
s2.asm lines 36232–36234 at 4ef457c · upstream ↗ 37001 sub.w d5,d0 ; remove this frame's acceleration changes2.asm lines 36998–37004 at 4ef457c · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-21.
- ^ abOn the ground the stick's tilt sets a target speed:
intendedMagis 32 times the square of the tilt's fraction of full. Below the target Mario gains 1.1 − speed/43 units/frame each frame (1.1 from a standstill); above it, on level ground, he loses 1. The target is at most 32, or 24 on slow floors, and the speed is capped at 48.Coden64decomp/sm64How we know
1295 f32 mag = ((controller->stickMag / 64.0f) * (controller->stickMag / 64.0f)) * 64.0f; ⋮ 1298 m->intendedMag = mag / 2.0f;
src/game/mario.c lines 1295–1298 at 9921382 · upstream ↗ 455 if (controller->stickMag > 64) { ⋮ 458 controller->stickMag = 64;
src/game/game_init.c lines 455–458 at 9921382 · upstream ↗ 438 if (m->floor != NULL && m->floor->type == SURFACE_SLOW) { 439 maxTargetSpeed = 24.0f; 440 } else { 441 maxTargetSpeed = 32.0f; ⋮ 444 targetSpeed = m->intendedMag < maxTargetSpeed ? m->intendedMag : maxTargetSpeed; ⋮ 450 if (m->forwardVel <= 0.0f) { 451 m->forwardVel += 1.1f; 452 } else if (m->forwardVel <= targetSpeed) { 453 m->forwardVel += 1.1f - m->forwardVel / 43.0f; 454 } else if (m->floor->normal.y >= 0.95f) { 455 m->forwardVel -= 1.0f; ⋮ 458 if (m->forwardVel > 48.0f) { ⋮
src/game/mario_actions_moving.c lines 438–460 at 9921382 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^Horizontal speeds are 4.4 fixed point, whole pixels and sixteenths per frame;
MoveObjectHorizontallyshifts the sixteenths into the subpixel byte.CodeXkeeper0/smb1How we know
6495 ASL ; and move low nybble to high 6496 ASL 6497 ASL ⋮ 6500 LSR ; move high nybble to low 6501 LSR 6502 LSR 6503 LSR
src/prg.asm lines 6492–6503 at 1cc3c92 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-27.
- ^Mario's top ground speed is
$18walking and$28running (B held).CodeXkeeper0/smb1 · Jdaster64 n.d.How we know
4873MaxRightXSpdData: 4874 .db $28, $18, $10
src/prg.asm lines 4873–4874 at 1cc3c92 · upstream ↗ 5009 AND #B_Button 5010 BNE SetRTmr ; if pressed, skip ahead to set timer ⋮ 5014 INY ; if running timer not set or level type is water,
src/prg.asm lines 5000–5014 at 1cc3c92 · upstream ↗ Maximum walk speed (normal, castle cutscene) 01900 | Maximum running speed 02900
SMB PHYSICS 101 / GROUND PHYSICS, Mario table · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-27.
- ^
DATA_00D535caps Mario's ground speed at$14walking and$24holding Y, and at$30while the P-meter is full and charging.CodeSMWDisXHow we know
9764DATA_00D535: ;! 9765 db $EC,$14,$DC,$24,$DC,$24,$D0,$30 ;!
bank_00.asm lines 9764–9765 at 30643c7 · upstream ↗ 10083 LDA.B PlayerXSpeed+1 ⋮ 10087 + CMP.B #con($23,$23,$23,$2C,$2C) 10088 BMI CODE_00D737 10089 LDA.B PlayerInAir 10090 BNE CODE_00D732 10091 LDA.B #$10 10092 STA.W RunTakeoffTimer 10093 BRA CODE_00D736 ⋮ 10098CODE_00D736: 10099 INY 10100CODE_00D737: 10101 JSR CODE_00D96A ⋮
bank_00.asm lines 10083–10106 at 30643c7 · upstream ↗ 10504CODE_00D96A: 10505 LDA.W PlayerPMeter 10506 CLC 10507 ADC.W DATA_00D5EB,Y 10508 BPL + 10509 LDA.B #$00 10510 + CMP.B #con($70,$70,$70,$40,$68) 10511 BCC + 10512 INY 10513 LDA.B #con($70,$70,$70,$40,$68) 10514 + STA.W PlayerPMeter
bank_00.asm lines 10504–10514 at 30643c7 · upstream ↗ 9908DATA_00D5EB: ;! 9909 db $FF,$FF,$02 ;!
bank_00.asm lines 9908–9909 at 30643c7 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^Measured on flat ground, Mario's top speed is 20/16 px/frame walking, 36/16 holding Y, and 47/16 to 49/16 once the P-meter is full, near
DATA_00D535's$14,$24, and$30.VerifiedSMWDisXHow we know
9764DATA_00D535: ;! 9765 db $EC,$14,$DC,$24,$DC,$24,$D0,$30 ;!
bank_00.asm lines 9764–9765 at 30643c7 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abIn Sonic Advance 2 the ground acceleration rises with the ring count. At the start of each frame
Player_HandleBoostThresholdpicks a bracket from the rings andPlayer_ApplyBoostPhysicssets the acceleration fromplayerBoostPhysicsTable: 8 subpixels (1/256 px/frame) a frame with 10 rings or fewer, 12 with 11 to 49, 14 with 50 to 99, 16 with 100 to 149, and 18 with 150 or more. The deceleration, taken when the d-pad is held against the motion, is 64 in every bracket, and the speed-up item doubles both. With neither Left nor Right held, or whileunk2Acounts down (a 30-frame timer set when the player slips off a steep slope, in the atlas's reading),Player_HandleGroundMovementtakes a fixed 8 a frame off the speed toward 0. Sonic 1's rates are$C(12) to accelerate, the same$Cfor the friction, and$80(128) against the motion.CodeSAT-R/sa2 · sonicretro/s1disasmHow we know
4899#if (GAME == GAME_SA2) 4900 Player_HandleBoostThreshold(p); 4901 Player_HandleBoostState(p); 4902 Player_ApplyBoostPhysics(p);
src/game/shared/stage/player.c lines 4899–4902 at 393357f · upstream ↗ 9107void Player_HandleBoostThreshold(Player *p) ⋮ 9109 if (gRingCount >= 150) { 9110 p->boostThreshold = 4; 9111 } else if (gRingCount >= 100) { 9112 p->boostThreshold = 3; 9113 } else if (gRingCount >= 50) { 9114 p->boostThreshold = 2; 9115 } else if (gRingCount > 10) { 9116 p->boostThreshold = 1; 9117 } else { 9118 p->boostThreshold = 0;
src/game/shared/stage/player.c lines 9107–9120 at 393357f · upstream ↗ 552static const s16 playerBoostPhysicsTable[5][2] = { 553 { 8, 64 }, { 12, 64 }, { 14, 64 }, { 16, 64 }, { 18, 64 },
src/game/shared/stage/player.c lines 552–553 at 393357f · upstream ↗ 9926 p->acceleration = playerBoostPhysicsTable[boostThreshold][0]; 9927 p->deceleration = playerBoostPhysicsTable[boostThreshold][1];
src/game/shared/stage/player.c lines 9926–9927 at 393357f · upstream ↗ 4907 Player_HandleInputs(p); ⋮ 4910 Player_HandleWater(p);
src/game/shared/stage/player.c lines 4907–4910 at 393357f · upstream ↗ 4429 if (p->itemEffect & PLAYER_ITEM_EFFECT__SPEED_UP) { ⋮ 4432 p->acceleration = p->acceleration * 2; 4433 p->deceleration = p->deceleration * 2; 4434#else 4435 p->acceleration = p->acceleration * 2; 4436 p->deceleration = p->deceleration * 2;
src/game/shared/stage/player.c lines 4429–4437 at 393357f · upstream ↗ 6652 if (!Player_TryTaunt(p) && !Player_TryCrouchOrSpinAttack(p) && !Player_TryJump(p) ⋮ 6658 Player_HandleGroundMovement(p);
src/game/shared/stage/player.c lines 6652–6658 at 393357f · upstream ↗ 8965void Player_HandleGroundMovement(Player *p) ⋮ 8967 s8 charState = CHARSTATE_INVALID; 8968 s32 acc = p->acceleration; 8969 s32 dec = p->deceleration; ⋮ 8971 if ((p->SA2_LABEL(unk2A) == 0) && p->heldInput & (DPAD_LEFT | DPAD_RIGHT)) { 8972 if (p->qSpeedGround > 0) { 8973 if (p->heldInput & DPAD_RIGHT) { 8974 if (p->qSpeedGround < p->topSpeed) { 8975 p->qSpeedGround += acc;
src/game/shared/stage/player.c lines 8965–8975 at 393357f · upstream ↗ 8984 } else if (p->qSpeedGround >= Q(2.0)) { ⋮ 8994 p->qSpeedGround -= dec;
src/game/shared/stage/player.c lines 8984–8994 at 393357f · upstream ↗ 9063 } else { 9064 s32 grndSpeed = p->qSpeedGround; 9065 if (grndSpeed > 0) { 9066 s16 val = grndSpeed - Q(8.0 / 256.0); 9067 if (val <= 0) { 9068 val = 0; ⋮ 9070 } else {
src/game/shared/stage/player.c lines 9063–9074 at 393357f · upstream ↗ 118#define PLAYERFN_UPDATE_UNK2A(player) \ 119 { \ 120 if (player->SA2_LABEL(unk2A) != 0) { \ 121 player->SA2_LABEL(unk2A) -= 1; \ 122 } else if ((player->rotation + Q(0.125)) & 0xC0) { \ 123 if (ABS(player->qSpeedGround) < Q(1.875)) { \ ⋮ 127 player->SA2_LABEL(unk2A) = GBA_FRAMES_PER_SECOND / 2; \
src/game/shared/stage/player.c lines 118–127 at 393357f · upstream ↗ 5son_maxspeed: equ $600 ; Sonic's max speed 6son_acceleration: equ $C ; Sonic's acceleration 7son_deceleration: equ $80 ; Sonic's deceleration
_incObj/01 Sonic.asm lines 5–7 at 15dfa3a · upstream ↗ 499Sonic_CheckDpadLetGo: ⋮ 501 andi.b #btnL+btnR,d0 ; is left or right held? ⋮ 506 sub.w d5,d0 ; reduce current rightward speed by acceleration
_incObj/01 Sonic.asm lines 499–506 at 15dfa3a · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^ abOutside boost mode
Player_ApplyBoostPhysicssetstopSpeed, the speed holding forward builds to inPlayer_HandleGroundMovement, to 6 px/frame (Q(6.0)), Sonic 1'sson_maxspeed($600). A faster speed, from a slope, is cut only atmaxSpeed, bysub_8023260, which the ground state (Player_Idle) calls after adding the slope term: 9 px/frame, or 15 whileMOVESTATE_SPIN_ATTACKis set, as a spin dash sets it.CodeSAT-R/sa2 · sonicretro/s1disasmHow we know
9912void Player_ApplyBoostPhysics(Player *p) ⋮ 9914 u32 boostThreshold = p->boostThreshold; 9915 if (p->isBoosting) { 9916 p->topSpeed = Q(12.0); 9917 p->maxSpeed = Q(15.0); 9918 } else if (p->moveState & MOVESTATE_SPIN_ATTACK) { 9919 p->topSpeed = Q(6.0); 9920 p->maxSpeed = Q(15.0); 9921 } else { 9922 p->topSpeed = Q(6.0); 9923 p->maxSpeed = Q(9.0);
src/game/shared/stage/player.c lines 9912–9924 at 393357f · upstream ↗ 8974 if (p->qSpeedGround < p->topSpeed) { 8975 p->qSpeedGround += acc; ⋮ 8977 if (p->qSpeedGround > p->topSpeed) { 8978 p->qSpeedGround = p->topSpeed;
src/game/shared/stage/player.c lines 8974–8979 at 393357f · upstream ↗ 6658 Player_HandleGroundMovement(p); ⋮ 6660 if (((p->rotation + Q(0.375)) & 0xFF) < 0xC0) { 6661 s32 acceleration = GET_ROTATED_ACCEL(p->rotation); ⋮ 6664 p->qSpeedGround += acceleration; ⋮ 6669 SA2_LABEL(sub_8023260)(p);
src/game/shared/stage/player.c lines 6658–6669 at 393357f · upstream ↗ 3383void SA2_LABEL(sub_8023260)(Player *p) ⋮ 3385 s32 maxSpeed = p->maxSpeed; ⋮ 3387 if (p->qSpeedGround > (s16)maxSpeed) { 3388 p->qSpeedGround = +maxSpeed;
src/game/shared/stage/player.c lines 3383–3388 at 393357f · upstream ↗ 7190 p->moveState |= (MOVESTATE_SPINDASH | MOVESTATE_SPIN_ATTACK);src/game/shared/stage/player.c line 7190 at 393357f · upstream ↗ 5son_maxspeed: equ $600 ; Sonic's max speed_incObj/01 Sonic.asm line 5 at 15dfa3a · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^Boost mode starts from a run held at the top speed. Holding forward on the ground at 6 px/frame or faster,
Player_HandleGroundMovementleaves the speed as it is and adds the frame's acceleration toboostSpeed, in the branch where Sonic 2'sSonic_MoveRightkeeps a speed above the top and Sonic 1's cuts it back to the top. At the start of each framePlayer_HandleBoostStateturns boost mode on onceboostSpeedreaches the ring bracket's entry inplayerBoostThresholdTable:Q(8)(2048) with 10 rings or fewer, 2040 with 11 to 49, 1680 with 50 to 99, 1440 with 100 to 149, and 1080 with 150 or more. In the air or below the top speed it setsboostSpeedback to 0. With each bracket's acceleration that is 256, 170, 120, 90, and 60 frames of holding forward at the top speed, and half as many with the speed-up item (computed).CodeSAT-R/sa2 · sonicretro/s2disasm · sonicretro/s1disasmHow we know
8972 if (p->qSpeedGround > 0) { 8973 if (p->heldInput & DPAD_RIGHT) { 8974 if (p->qSpeedGround < p->topSpeed) { ⋮ 8980 } else { 8981 p->boostSpeed += acc;
src/game/shared/stage/player.c lines 8972–8982 at 393357f · upstream ↗ 9009 } else if (p->qSpeedGround < 0) { 9010 if (p->heldInput & DPAD_LEFT) { 9011 if (p->qSpeedGround > -p->topSpeed) { ⋮ 9017 } else { 9018 p->boostSpeed += acc;
src/game/shared/stage/player.c lines 9009–9019 at 393357f · upstream ↗ 556static const s16 playerBoostThresholdTable[5] = { Q(8), Q(7.96875), Q(6.5625), Q(5.625), Q(4.21875) };src/game/shared/stage/player.c line 556 at 393357f · upstream ↗ 4899#if (GAME == GAME_SA2) 4900 Player_HandleBoostThreshold(p); 4901 Player_HandleBoostState(p); 4902 Player_ApplyBoostPhysics(p);
src/game/shared/stage/player.c lines 4899–4902 at 393357f · upstream ↗ 9122void Player_HandleBoostState(Player *p) ⋮ 9124 bool32 isBoostActive = p->isBoosting; 9125 if (isBoostActive) { ⋮ 9134 } else { 9135 if ((!(p->moveState & MOVESTATE_IN_AIR)) && ((ABS(p->qSpeedGround) >= p->topSpeed))) { 9136 if (p->boostSpeed >= playerBoostThresholdTable[p->boostThreshold]) { 9137 p->isBoosting = TRUE; ⋮ 9144 } else { 9145#ifndef NON_MATCHING 9146 p->boostSpeed = isBoostActive;
src/game/shared/stage/player.c lines 9122–9150 at 393357f · upstream ↗ 552static const s16 playerBoostPhysicsTable[5][2] = { 553 { 8, 64 }, { 12, 64 }, { 14, 64 }, { 16, 64 }, { 18, 64 },
src/game/shared/stage/player.c lines 552–553 at 393357f · upstream ↗ 4429 if (p->itemEffect & PLAYER_ITEM_EFFECT__SPEED_UP) { ⋮ 4432 p->acceleration = p->acceleration * 2; ⋮ 4435 p->acceleration = p->acceleration * 2;
src/game/shared/stage/player.c lines 4429–4437 at 393357f · upstream ↗ 36998 add.w d5,d0 ; add acceleration to the right 36999 cmp.w d6,d0 ; compare new speed with top speed 37000 blt.s + ; if new speed is less than the maximum, branch 37001 sub.w d5,d0 ; remove this frame's acceleration change 37002 cmp.w d6,d0 ; compare speed with top speed 37003 bge.s + ; if speed was already greater than the maximum, branch 37004 move.w d6,d0 ; limit speed on ground going right
s2.asm lines 36998–37004 at 4ef457c · upstream ↗ 716 add.w d5,d0 ; add acceleration to current ground speed 717 cmp.w d6,d0 ; is new speed above max speed? 718 blt.s .nocap ; if not, branch 719 move.w d6,d0 ; cap Sonic's ground speed
_incObj/01 Sonic.asm lines 716–719 at 15dfa3a · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^ abIn boost mode
Player_ApplyBoostPhysicsraisestopSpeed, the speed holding forward builds to, from 6 to 12 px/frame, twice Sonic 1's single top speed (son_maxspeed,$600; computed), andmaxSpeed, the ground cap, to 15; the acceleration still follows the ring count.Player_HandleBoostStatelooks for the end of boost mode only on the ground, where a ground speed below 4.5 px/frame ends it. Among the other ends are a hit (Player_InitHurt), the start of Tails's or Cream's flight, a multiplayer finish, the special stage transition, andInitializePlayer.CodeSAT-R/sa2 · sonicretro/s1disasmrevisedHow we know
9912void Player_ApplyBoostPhysics(Player *p) ⋮ 9914 u32 boostThreshold = p->boostThreshold; 9915 if (p->isBoosting) { 9916 p->topSpeed = Q(12.0); 9917 p->maxSpeed = Q(15.0); ⋮ 9920 p->maxSpeed = Q(15.0); ⋮ 9926 p->acceleration = playerBoostPhysicsTable[boostThreshold][0];
src/game/shared/stage/player.c lines 9912–9927 at 393357f · upstream ↗ 9122void Player_HandleBoostState(Player *p) ⋮ 9124 bool32 isBoostActive = p->isBoosting; 9125 if (isBoostActive) { 9126 if (!(p->moveState & MOVESTATE_IN_AIR)) { 9127 p->boostSpeed = playerBoostThresholdTable[p->boostThreshold]; ⋮ 9129 if (ABS(p->qSpeedGround) < Q(4.5)) { 9130 p->isBoosting = FALSE; 9131 p->boostSpeed = 0;
src/game/shared/stage/player.c lines 9122–9133 at 393357f · upstream ↗ 7856void Player_InitHurt(Player *p) ⋮ 7858 p->timerInvulnerability = 0x78; 7859 p->isBoosting = FALSE;
src/game/shared/stage/player.c lines 7856–7859 at 393357f · upstream ↗ 893void Player_Tails_InitFlying(Player *p) ⋮ 905 p->isBoosting = FALSE;
src/game/sa2/stage/player_callbacks.c lines 893–905 at 393357f · upstream ↗ 632void Player_Cream_InitFlying(Player *p) ⋮ 644 p->isBoosting = FALSE;
src/game/sa2/stage/player_callbacks.c lines 632–644 at 393357f · upstream ↗ 8193void Player_HandleMultiplayerFinish(Player *p) ⋮ 8230 p->isBoosting = FALSE;
src/game/shared/stage/player.c lines 8193–8230 at 393357f · upstream ↗ 10050void Player_InitSpecialStageTransition(Player *p) ⋮ 10057 p->isBoosting = FALSE;
src/game/shared/stage/player.c lines 10050–10057 at 393357f · upstream ↗ 976void InitializePlayer(Player *p) ⋮ 987 if ( ⋮ 999#if (GAME == GAME_SA2) ⋮ 1059#if (GAME == GAME_SA2) 1060 p->transition = 0; 1061 p->unk6E = 0; 1062 p->prevTransition = 0; 1063 p->isBoosting = FALSE;
src/game/shared/stage/player.c lines 976–1063 at 393357f · upstream ↗ 5son_maxspeed: equ $600 ; Sonic's max speed_incObj/01 Sonic.asm line 5 at 15dfa3a · 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.
- ^Wolfenstein 3D keeps no velocity for the player: each frame
ControlMovementzeroesthrustspeed, andThrustmoves the player straight throughClipMoveby the frame's control value timesMOVESCALE(150), orBACKMOVESCALE(100) backward. The keyboard sets that value to 35 a tic walking (BASEMOVE) or 70 running (RUNMOVE), times the frame's tics, so a walk moves 5,250 and a run 10,500 units a tic, with 65,536 units to a tile (TILEGLOBAL);Thrustclamps each move toMINDIST*2-1(45,055), so those rates hold on frames of at most 8 tics walking or 4 running, and longer frames move less a tic. At the 70 tics a second ofTickBase's comment a run is about 11.2 tiles a second (computed).Codeid-Software/wolf3drevisedHow we know
18#define MOVESCALE 150l 19#define BACKMOVESCALE 100l
WOLFSRC/WL_AGENT.C lines 18–19 at 0516778 · upstream ↗ 156 thrustspeed = 0;WOLFSRC/WL_AGENT.C line 156 at 0516778 · upstream ↗ 205 if (controly < 0) ⋮ 207 Thrust (ob->angle,-controly*MOVESCALE); // move forwards ⋮ 209 else if (controly > 0) ⋮ 211 angle = ob->angle + ANGLES/2; 212 if (angle >= ANGLES) 213 angle -= ANGLES; 214 Thrust (angle,controly*BACKMOVESCALE); // move backwards
WOLFSRC/WL_AGENT.C lines 205–214 at 0516778 · upstream ↗ 943 thrustspeed += speed; ⋮ 945// moving bounds speed ⋮ 947 if (speed >= MINDIST*2) 948 speed = MINDIST*2-1; ⋮ 950 xmove = FixedByFrac(speed,costable[angle]); 951 ymove = -FixedByFrac(speed,sintable[angle]); ⋮ 953 ClipMove(player,xmove,ymove);
WOLFSRC/WL_AGENT.C lines 943–953 at 0516778 · upstream ↗ 115#define MINDIST (0x5800l)WOLFSRC/WL_DEF.H line 115 at 0516778 · upstream ↗ 246#define BASEMOVE 35 247#define RUNMOVE 70
WOLFSRC/WL_PLAY.C lines 246–247 at 0516778 · upstream ↗ 343 if (buttonstate[bt_run]) ⋮ 345 if (Keyboard[dirscan[di_north]]) 346 controly -= RUNMOVE*tics; 347 if (Keyboard[dirscan[di_south]]) 348 controly += RUNMOVE*tics; 349 if (Keyboard[dirscan[di_west]]) 350 controlx -= RUNMOVE*tics; 351 if (Keyboard[dirscan[di_east]]) 352 controlx += RUNMOVE*tics; ⋮ 354 else ⋮ 356 if (Keyboard[dirscan[di_north]]) 357 controly -= BASEMOVE*tics;
WOLFSRC/WL_PLAY.C lines 343–357 at 0516778 · upstream ↗ 97#define GLOBAL1 (1l<<16) 98#define TILEGLOBAL GLOBAL1
WOLFSRC/WL_DEF.H lines 97–98 at 0516778 · upstream ↗ 17#define TickBase 70 // 70Hz per tick - used as a base for timer 0WOLFSRC/ID_SD.H line 17 at 0516778 · 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.
- ^On the ground, Quake III's
PM_Frictiontakes the horizontal speed and lowers it by step time ×pm_friction× the larger of the speed andpm_stopspeed, scaling all three velocity axes by the same factor, withpm_friction6 andpm_stopspeed100 set in the code; it skips ground friction in water deeper than level 1, on a slick surface, and while the knockback timer runs, and it has no edge friction. Quake's ground friction is the same rule with friction 4 (sv_friction) and the same stop speed, doubled over a ledge (quake/ground-friction).Codeid-Software/Quake-III-Arena · id-Software/QuakerevisedHow we know
34float pm_stopspeed = 100.0f;code/game/bg_pmove.c line 34 at dbe4ddb · upstream ↗ 44float pm_friction = 6.0f;code/game/bg_pmove.c line 44 at dbe4ddb · upstream ↗ 180 VectorCopy( vel, vec ); 181 if ( pml.walking ) { 182 vec[2] = 0; // ignore slope movement ⋮ 185 speed = VectorLength(vec); ⋮ 196 if ( pm->waterlevel <= 1 ) { 197 if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) { 198 // if getting knocked back, no friction 199 if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) { 200 control = speed < pm_stopspeed ? pm_stopspeed : speed; 201 drop += control*pm_friction*pml.frametime; ⋮ 221 newspeed = speed - drop; 222 if (newspeed < 0) { ⋮
code/game/bg_pmove.c lines 180–229 at dbe4ddb · upstream ↗ 42cvar_t sv_friction = {"sv_friction","4",false,true}; 43cvar_t sv_stopspeed = {"sv_stopspeed","100"};
WinQuake/sv_phys.c lines 42–43 at bf4ac42 · upstream ↗ 27cvar_t sv_edgefriction = {"edgefriction", "2"};WinQuake/sv_user.c line 27 at bf4ac42 · upstream ↗ 144 if (trace.fraction == 1.0) 145 friction = sv_friction.value*sv_edgefriction.value; 146 else 147 friction = sv_friction.value; ⋮ 150 control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed; 151 newspeed = speed - host_frametime*control*friction;
WinQuake/sv_user.c lines 144–151 at bf4ac42 · 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.
- ^ abQuake III's
PM_WalkMoveruns friction and thenPM_Acceleratewithpm_accelerate10, which is Quake's rule (Quake III'sPM_Acceleratecomment says "q2 style"): if the speed along the wish direction is under the wish speed, it adds 10 × step time × wish speed along that direction, no more than the shortfall. The full wish speed is the player'sspeed, set before each move fromg_speed(320, × 1.3 with Haste). Computed, not measured: since 10 outpaces the friction of 6, a run settles at exactly 320, as Quake's does at 10 against 4; at 8 ms steps it reaches 320 from rest in 19 steps (0.15 s) and stops from 320 in 45 (0.36 s). These figures leave out the roundingtrap_SnapVectorapplies to the velocity after each step, engine code the atlas hasn't read.Codeid-Software/Quake-III-Arena · id-Software/QuakerevisedHow we know
39float pm_accelerate = 10.0f;code/game/bg_pmove.c line 39 at dbe4ddb · upstream ↗ 240static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) { 241#if 1 242 // q2 style ⋮ 246 currentspeed = DotProduct (pm->ps->velocity, wishdir); 247 addspeed = wishspeed - currentspeed; 248 if (addspeed <= 0) { 249 return; ⋮ 251 accelspeed = accel*pml.frametime*wishspeed; 252 if (accelspeed > addspeed) { 253 accelspeed = addspeed; ⋮ 257 pm->ps->velocity[i] += accelspeed*wishdir[i];
code/game/bg_pmove.c lines 240–258 at dbe4ddb · upstream ↗ 720 PM_Friction ();code/game/bg_pmove.c line 720 at dbe4ddb · upstream ↗ 772 if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) { 773 accelerate = pm_airaccelerate; 774 } else { 775 accelerate = pm_accelerate; ⋮ 778 PM_Accelerate (wishdir, wishspeed, accelerate);
code/game/bg_pmove.c lines 772–778 at dbe4ddb · upstream ↗ 142 { &g_speed, "g_speed", "320", 0, 0, qtrue },code/game/g_main.c line 142 at dbe4ddb · upstream ↗ 2015 trap_SnapVector( pm->ps->velocity );code/game/bg_pmove.c line 2015 at dbe4ddb · upstream ↗ 843 client->ps.speed = g_speed.value; ⋮ 851 if ( client->ps.powerups[PW_HASTE] ) { 852 client->ps.speed *= 1.3;
code/game/g_active.c lines 843–853 at dbe4ddb · upstream ↗ 195 currentspeed = DotProduct (velocity, wishdir); 196 addspeed = wishspeed - currentspeed; ⋮ 199 accelspeed = sv_accelerate.value*host_frametime*wishspeed; 200 if (accelspeed > addspeed) 201 accelspeed = addspeed;
WinQuake/sv_user.c lines 195–201 at bf4ac42 · upstream ↗ 167cvar_t sv_maxspeed = {"sv_maxspeed", "320", false, true}; 168cvar_t sv_accelerate = {"sv_accelerate", "10"};
WinQuake/sv_user.c lines 167–168 at bf4ac42 · 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.
- ^ abIn the running game, on flat ground, Sonic's top running speed,
son_maxspeed, is$600(6 px/frame): holding the d-pad his ground speed stops there, and in the air the d-pad caps his X speed there too.Verifiedsonicretro/s1disasm · Sonic Retro, “Sonic Physics Guide: Running”How we know
5son_maxspeed: equ $600 ; Sonic's max speed_incObj/01 Sonic.asm line 5 at 15dfa3a · upstream ↗ 45 move.w #son_maxspeed,(v_sonspeedmax).w ; set Sonic's top speed_incObj/01 Sonic.asm line 45 at 15dfa3a · upstream ↗ 940 move.w (v_sonspeedmax).w,d6 ; get Sonic's current top speed_incObj/01 Sonic.asm line 940 at 15dfa3a · upstream ↗ top_speed 6
Constants · archived copy ↗ Measured by a script. It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Checked by a second AI model, 2026-10-25.
- ^ abIn the running game, on flat ground, holding the d-pad the way Sonic runs, or from a standstill, adds
son_acceleration,$C, to his ground speed each frame, up to the top speed.Verifiedsonicretro/s1disasm · Sonic Retro, “Sonic Physics Guide: Running”How we know
6son_acceleration: equ $C ; Sonic's acceleration_incObj/01 Sonic.asm line 6 at 15dfa3a · upstream ↗ 706Sonic_MoveRight: 707 move.w obInertia(a0),d0 ; get Sonic's current ground speed 708 bmi.s .changedirection ; has Sonic changed direction? if yes, branch ⋮ 716 add.w d5,d0 ; add acceleration to current ground speed 717 cmp.w d6,d0 ; is new speed above max speed? 718 blt.s .nocap ; if not, branch 719 move.w d6,d0 ; cap Sonic's ground speed
_incObj/01 Sonic.asm lines 706–723 at 15dfa3a · upstream ↗ acceleration_speed 0.046875 (12 subpixels)
Constants · archived copy ↗ Measured by a script. It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Checked by a second AI model, 2026-10-25.
- ^ abOn the ground the stick's tilt sets Banjo's target speed in five bands of the stick's distance from center, each axis clamped to a raw 7 to 59 (x) or 7 to 61 (y) and scaled to 0 to 1, and the distance capped at 1. Up to 0.12 the target is 0; above it the target rises linearly within each band: 30 to 80 units/s up to 0.2, 80 to 150 up to 0.5, 150 to 225 up to 0.75, and 225 to 500 at full tilt. The decompilers name the bands creep, slow walk, walk, and fast walk. On ground flagged
BA_FLAG_13_TOUCHING_DANGEROUS_GROUND, the target is instead 30 to 150 by the distance itself, and 0 inside the 0.12 dead zone. Each frame the horizontal velocity closes the gap to the target by a fraction from a five-entry table (0, 0.07, 0.29, 0.15, 0.05) indexed by the unnamedD_8037C284(the atlas reads the index as a movement state), unlessfunc_8029CF48has set an override for that index, whichfunc_8029CED0then returns instead, times the frame's time over 1/30 s; when an unnamed floor check (func_80294548) passes, the target is first scaled by the slope. Super Mario 64's target is 32 times the square of the tilt, and Mario gains a fixed 1.1 − speed/43 units/frame below it (sm64/walking-speed).Codebanjo.decomp/banjo-kazooie · n64decomp/sm64revisedHow we know
18f32 bsWalkMinCreepVelocity = 30.0f;//creep_min 19f32 bsWalkCreepSlowWalkVelocityThreshold = 80.0f;//creep_max/slow_walk_min 20f32 bsWalkSlowWalkWalkVelocityThreshold = 150.0f;//slow_walk_max/walk_min 21f32 bsWalkWalkFastWalkVelocityThreshold = 225.0f;//walk_max/walk_fast_min 22f32 bsWalkMaxFastWalkVelocity = 500.0f;//walk_fast_max 23f32 bsWalkMinMudVelocity = 30.0f; //mud_min 24f32 bsWalkMaxMudVelocity = 150.0f; //mud_max
src/core2/bs/walk.c lines 18–24 at e8d31fa · upstream ↗ 41void func_802B6D00(void){ ⋮ 45 sp1C = bastick_getZonePosition(); 46 sp18 = bastick_getZone(); 47 if(player_isOnDangerousGround()){ 48 if(sp18 == 0){ 49 baphysics_set_target_horizontal_velocity(0.0f); 50 }else{//L802B6D48 51 baphysics_set_target_horizontal_velocity(ml_interpolate_f(bastick_distance(), bsWalkMinMudVelocity, bsWalkMaxMudVelocity)); ⋮ 55 switch(sp18){ 56 case 0://802B6D98 57 baphysics_set_target_horizontal_velocity(0.0f); ⋮ 60 baphysics_set_target_horizontal_velocity(ml_interpolate_f(sp1C, bsWalkMinCreepVelocity, bsWalkCreepSlowWalkVelocityThreshold)); ⋮
src/core2/bs/walk.c lines 41–72 at e8d31fa · upstream ↗ 28f32 bastick_calculateZonePosition(f32 arg0, f32 arg1, f32 arg2){ 29 return (arg0 - arg1)/(arg2 - arg1); ⋮ 35 if (bastick.distance <= bastick.zone_markers[0]) { 36 bastick.zone = 0; 37 bastick.zone_position = 0.0f; ⋮ 63void bastick_resetZones(void) { 64 bastick_setZoneMax(0, 0.12f); 65 bastick_setZoneMax(1, 0.2f); 66 bastick_setZoneMax(2, 0.5f); 67 bastick_setZoneMax(3, 0.75f); 68 bastick_setZoneMax(4, 1.0f);
src/core2/bastick.c lines 28–69 at e8d31fa · upstream ↗ 85 bastick.distance = sqrtf(bastick.value[0]*bastick.value[0] + bastick.value[1]*bastick.value[1]); ⋮ 100 if (bastick.distance > 1.0) { 101 bastick.distance = 1.0f;
src/core2/bastick.c lines 85–102 at e8d31fa · upstream ↗ 51f32 controller_clampAndNormaliseJoyAxis(s32 input, s32 min, s32 max) ⋮ 53 f32 unit = 1 / 80.f; ⋮ 61 if (input > 0) ⋮ 63 input = input > max ? max : input < min ? min : input; 64 input = ((input - min) * 80) / (max - min); ⋮ 72 return unit *= input;
src/core1/joy.c lines 51–72 at e8d31fa · upstream ↗ 155 dst[0] = controller_clampAndNormaliseJoyAxis(sInputs[controllerIdx].stick_x, 7, 59); 156 dst[1] = controller_clampAndNormaliseJoyAxis(sInputs[controllerIdx].stick_y, 7, 61);
src/core1/joy.c lines 155–156 at e8d31fa · upstream ↗ 705f32 ml_interpolate_f(f32 arg0, f32 arg1, f32 arg2) ⋮ 707 return arg0 * (arg2 - arg1) + arg1;
src/core1/ml.c lines 705–707 at e8d31fa · upstream ↗ 188bool player_isOnDangerousGround(void){ 189 return baflag_isTrue(BA_FLAG_13_TOUCHING_DANGEROUS_GROUND);
src/core2/playerutils.c lines 188–189 at e8d31fa · upstream ↗ 49 func_80256D0C(0.0f, baphysics_target_yaw, 0.0f, 0.0f, baphysics_target_horizontal_velocity, &baphysics_target_velocity[0], &baphysics_target_velocity[1], &baphysics_target_velocity[2]); ⋮ 57 if(func_80294548()){ ⋮ 71 ml_vec3f_scale(sp60, 1.0 + sp40); ⋮ 75 ml_vec3f_scale(sp60, sp40 + 1.0); ⋮ 80 ml_vec3f_scale_copy(sp84, sp60, func_8029CED0()); 81 ml_vec3f_scale_copy(sp78, sp6C, func_8029CED0()); 82 ml_vec3f_diff(sp84, sp78); 83 ml_vec3f_scale(sp84, time_getDelta()/0.0333333); 84 s_player_velocity[0] += sp84[0];\
src/core2/ba/physics.c lines 49–86 at e8d31fa · upstream ↗ 16f32 func_8029CED0(void) { ⋮ 19 temp_v0 = func_8029CEB0(); 20 if (D_8037D1F0[temp_v0] != 0) { 21 return D_8037D1F8[temp_v0]; ⋮ 23 return func_8033DE30(temp_v0);
src/core2/code_15F20.c lines 16–23 at e8d31fa · upstream ↗ 30void func_8029CF48(s32 arg0, s32 arg1, f32 arg2) { 31 D_8037D1F0[arg0] = arg1; 32 D_8037D1F8[arg0] = arg2;
src/core2/code_15F20.c lines 30–33 at e8d31fa · upstream ↗ 440s32 func_802944F4(void){ 441 return D_8037C284;
src/core2/code_C4B0.c lines 440–441 at e8d31fa · upstream ↗ 77Struct_Core2_B6CE0_1 D_80371E30[] ={ 78 { 0, 0, 0.0f }, 79 { 1, 0, 0.07f }, 80 { 2, 1, 0.29f }, 81 { 3, 2, 0.15f }, 82 { 4, 1, 0.05f }
src/core2/commonParticle.c lines 77–83 at e8d31fa · upstream ↗ 97f32 func_8033DE30(s32 arg0){ 98 return D_80371E30[arg0].unk4;
src/core2/commonParticle.c lines 97–98 at e8d31fa · upstream ↗ 1295 f32 mag = ((controller->stickMag / 64.0f) * (controller->stickMag / 64.0f)) * 64.0f; ⋮ 1298 m->intendedMag = mag / 2.0f;
src/game/mario.c lines 1295–1298 at 9921382 · upstream ↗ 450 if (m->forwardVel <= 0.0f) { 451 m->forwardVel += 1.1f; 452 } else if (m->forwardVel <= targetSpeed) { 453 m->forwardVel += 1.1f - m->forwardVel / 43.0f;
src/game/mario_actions_moving.c lines 450–453 at 9921382 · 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.
- ^In the running game, on flat ground, holding the d-pad against Sonic's motion subtracts
son_deceleration,$80, a frame, and a step that would cross zero sets the speed to$80the other way: from$40written in by the probe, Left gives −$80, and from −$40, Right gives$80.Verifiedsonicretro/s1disasm · Sonic Retro, “Sonic Physics Guide: Running”How we know
7son_deceleration: equ $80 ; Sonic's deceleration_incObj/01 Sonic.asm line 7 at 15dfa3a · upstream ↗ 665.changeddirection: 666 sub.w d4,d0 ; apply deceleration to current speed 667 bcc.s .stilldecel ; if still decelerating, branch 668 move.w #-$80,d0 ; set minimum speed on sign change
_incObj/01 Sonic.asm lines 665–668 at 15dfa3a · upstream ↗ 729.changedirection: 730 add.w d4,d0 ; apply deceleration to current speed 731 bcc.s .stilldecel ; if still decelerating, branch 732 move.w #$80,d0 ; set minimum speed on sign change
_incObj/01 Sonic.asm lines 729–732 at 15dfa3a · upstream ↗ At any time an addition or subtraction of deceleration_speed results in Ground Speed changing sign, Ground Speed is set to 0.5 (128 subpixels) in the opposite direction.
Deceleration · archived copy ↗ Measured by a script. It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Checked by a second AI model, 2026-10-25.
- ^
CODE_00D742compares only the speed's high byte with the cap; below it the frame adds the full acceleration, unclamped, and at or past it the frame applies friction instead, so Mario's top speed cycles around the cap.CodeSMWDisXHow we know
10107CODE_00D742: 10108 LDA.B PlayerXSpeed+1 10109 SEC 10110 SBC.W DATA_00D535,Y 10111 BEQ CODE_00D76B 10112 EOR.W DATA_00D535,Y 10113 BPL CODE_00D76B
bank_00.asm lines 10107–10113 at 30643c7 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^ ab
X_Physicsdoubles the acceleration whenever Mario faces away from the direction he moves, so a turn brakes at$130,$1A0, or$1C8a frame.CodeXkeeper0/smb1How we know
5042 LDA PlayerFacingDir 5043 CMP Player_MovingDir ; check facing direction against moving direction 5044 BEQ ExitPhy ; if the same, branch to leave 5045 ASL FrictionAdderLow ; otherwise shift d7 of friction adder low into carry 5046 ROL FrictionAdderHigh ; then rotate carry onto d0 of friction adder high 5047ExitPhy:
src/prg.asm lines 5042–5047 at 1cc3c92 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^On the ground,
GetPlayerAnimSpeedzeroes Mario's speed and turns his moving direction to his facing one when a button other than A is held, the d-pad's Left/Right bits differ from his moving direction, and his speed is below$0B.CodeXkeeper0/smb1How we know
5064ChkSkid: 5065 LDA SavedJoypadBits ; get controller bits 5066 AND #%01111111 ; mask out A button 5067 BEQ SetAnimSpd ; if no other buttons pressed, branch ahead of all this 5068 AND #$03 ; mask out all others except left and right 5069 CMP Player_MovingDir ; check against moving direction 5070 BNE ProcSkid ; if left/right controller bits <> moving direction, branch ⋮ 5075ProcSkid: 5076 LDA Player_XSpeedAbsolute ; check player's walking/running speed 5077 CMP #$0b ; against one last amount 5078 BCS SetAnimSpd ; if greater than this amount, branch 5079 LDA PlayerFacingDir ⋮
src/prg.asm lines 5064–5083 at 1cc3c92 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^Mega Man lands into the running state if Left or Right was held on the frame before landing, at 1.375 px/frame from the next frame with no first step, and into the landing-stop state otherwise.Verifiedlsmmega/mm2
How we know
225@jump_down: 226 JSR _check_object_vertical_move 227 LDA z:z00 228 BEQ @not_landing 229 track_queue track_landing 230 LDX #$05 231 LDA z:zjoy1_pressed_frame 232 AND #left_button | right_button 233 BNE @horizontal_jump_down 234 LDX #$08 ⋮ 236@horizontal_jump_down: 237 STX z:zmegaman_status ⋮
engine/run_megaman.asm lines 225–238 at 94f0bf3 · upstream ↗ 1_nmi_wait_0e: 2 LDA z:zjoy1_pressed 3 STA z:zjoy1_pressed_frame
home/nmi_wait.asm lines 1–3 at 94f0bf3 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^Down on the ground puts Simon in the crouching state 4 px lower, where the d-pad no longer moves him; letting go stands him back up, and that frame he doesn't move either.Verified“Castlevania (USA) (Rev 1)”
How we know
In the cartridge (“Castlevania (USA) (Rev 1)”): bank 6 $944A-$9457: Down held, state 3, then Y += 4 ($9A69); bank 6 $9539-$9584: the crouching state writes $04 (Down alone) to $0584 before the move, so no direction bits; Down let go: Y −= 4 and state 0 ($9578). We don't reproduce the bytes.
Measured by a script in MesenCE 2.2.1 (build 4b8a196), on Castlevania (USA) (Rev 1) (SHA-1
4e3ef47de86a…). It fails if the result disagrees with the claim.The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^While Left or Right is held, the crouching state, and the standing state on a frame that doesn't start a fall, a stair climb, a whip, a jump, or a crouch, set Simon's facing (
$0450) from it, so he turns on the first frame, and crouching he can turn without moving. A frame that starts any of those keeps the facing he had.Verified“Castlevania (USA) (Rev 1)”revisedHow we know
In the cartridge (“Castlevania (USA) (Rev 1)”): bank 6 $9458-$9462: held Left/Right shifted right one bit into $0450 (0 right, 1 left); bank 6 $9539-$9584, the crouching state, which does the same at $9557-$955E; bank 6 $9401-$943B, the jump's take-off, which returns before $9458; bank 6 $944A-$9457, the crouch's start, which returns through $9A69 before $9458; bank 6 $93ED-$9400: Up or Down at a stair ($9C10) starts the climb and returns at $9400; bank 6 $943C-$9449: a whip started ($9A2A, carry set) moves the state on and returns; bank 6 $93C5-$93D8, the fall entry, which returns before the move. We don't reproduce the bytes.
Measured by a script in MesenCE 2.2.1 (build 4b8a196), on Castlevania (USA) (Rev 1) (SHA-1
4e3ef47de86a…). It fails if the result disagrees with the claim.The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-21.
Checked by a second AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-22.
- ^In the running game, on flat ground, reversing from top speed, Sonic running right reaches exactly 0 on the 12th frame and −
$Con the 13th, but running left he jumps from −$80to +$80on the 12th.Verifiedsonicretro/s1disasmrevisedHow we know
668 move.w #-$80,d0 ; set minimum speed on sign change_incObj/01 Sonic.asm lines 665–668 at 15dfa3a · upstream ↗ 732 move.w #$80,d0 ; set minimum speed on sign change_incObj/01 Sonic.asm lines 729–732 at 15dfa3a · upstream ↗ Measured by a script. It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Revised after a check by a second AI model, 2026-10-25.
- ^ abSimulating the code, Madeline reaches 90 px/s from a standstill in 6 updates on the ground and 9 in the air, and needs 11 and 17 to turn from 90 to −90 px/s.CodeNoelFB/Celeste · JamesMcMahon/monocle-engine
How we know
2884 float mult = onGround ? 1 : AirMult;Source/Player/Player.cs lines 2879–2895 at 1b0ce45 · upstream ↗ 854 return val > target ? Math.Max(val - maxMove, target) : Math.Min(val + maxMove, target);Monocle/Util/Calc.cs lines 852–855 at 759c15a · upstream ↗ Measured by a script. It fails if the result disagrees with the claim.
Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-25.
- ^Each update moves Madeline's horizontal speed toward 90 px/s times the d-pad direction by
RunAccel, 1000 px/s² on the ground, so the same rate speeds her up, turns her, and stops her.CodeNoelFB/CelesteHow we know
31 public const float RunAccel = 1000f;Source/Player/Player.cs line 31 at 1b0ce45 · upstream ↗ 2884 float mult = onGround ? 1 : AirMult;Source/Player/Player.cs line 2884 at 1b0ce45 · upstream ↗ 2893 else 2894 Speed.X = Calc.Approach(Speed.X, max * moveX, RunAccel * mult * Engine.DeltaTime); //Approach the max speed
Source/Player/Player.cs lines 2893–2894 at 1b0ce45 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^On the ground
X_Physicsuses the running row (cap$28, acceleration$E4) while B and the direction Mario moves in are held and for the 9 frames after B goes up; otherwise the walking row (cap$18,$98, or$D0whileRunningSpeedis set or his speed is$21or more).CodeXkeeper0/smb1revisedHow we know
4991X_Physics: ⋮ 4994 LDA Player_State ; if mario is on the ground, branch 4995 BEQ ProcPRun ⋮ 5000ProcPRun: 5001 INY ; if mario on the ground, increment Y 5002 LDA AreaType ; check area type 5003 BEQ ChkRFast ; if water type, branch 5004 DEY ; decrement Y by default for non-water type area 5005 LDA Left_Right_Buttons ; get left/right controller bits 5006 CMP Player_MovingDir ; check against moving direction 5007 BNE ChkRFast ; if controller bits <> moving direction, skip this part 5008 LDA A_B_Buttons ; check for b button pressed ⋮
src/prg.asm lines 4991–5026 at 1cc3c92 · upstream ↗ 4870MaxLeftXSpdData: 4871 .db $d8, $e8, $f0 ⋮ 4873MaxRightXSpdData: 4874 .db $28, $18, $10
src/prg.asm lines 4870–4874 at 1cc3c92 · upstream ↗ 5055GetPlayerAnimSpeed: ⋮ 5058 CMP #$1c ; against preset amount 5059 BCS SetRunSpd ; if greater than a certain amount, branch ahead ⋮ 5068 AND #$03 ; mask out all others except left and right 5069 CMP Player_MovingDir ; check against moving direction 5070 BNE ProcSkid ; if left/right controller bits <> moving direction, branch 5071 LDA #$00 ; otherwise set zero value here 5072SetRunSpd: 5073 STA RunningSpeed ; store zero or running speed here
src/prg.asm lines 5055–5074 at 1cc3c92 · upstream ↗ 126DecTimers: 127 LDX #$14 ; load end offset for end of frame timers ⋮ 136 DEC Timers,x ; otherwise decrement the current timer
src/prg.asm lines 126–136 at 1cc3c92 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
Checked against the evidence by its author, an AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-21.
Sources
Code
- Software, id. 2012. “Wolfenstein 3D: the source release (id-Software/wolf3d).” Commit 0516778. id-Software/wolf3d @ 0516778 · licence, all uses
- 1987. “Castlevania (USA) (Rev 1).” · licence, all uses
- Software, id. 2012. “Quake III Arena: the GPL source release (id-Software/Quake-III-Arena, 1.32b).” Commit dbe4ddb. id-Software/Quake-III-Arena @ dbe4ddb · licence, all uses
- Software, id. 2012. “Quake: the engine source release (id-Software/Quake: WinQuake and QuakeWorld).” Commit bf4ac42. id-Software/Quake @ bf4ac42 · licence, all uses
- SAT-R contributors. 2026. “sa2: a decompilation of Sonic Advance 1 and 2 (SAT-R/sa2).” Commit 393357f. SAT-R/sa2 @ 393357f · licence, all uses
- Sonic Retro contributors. 2026. “s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm).” Commit 15dfa3a. sonicretro/s1disasm @ 15dfa3a · licence, all uses
- banjo.decomp. 2026. “banjo-kazooie: a decompilation of Banjo-Kazooie (banjo.decomp/banjo-kazooie).” Commit e8d31fa. banjo.decomp/banjo-kazooie @ e8d31fa · licence, all uses
- n64decomp. 2023. “sm64: a decompilation of Super Mario 64 (n64decomp/sm64).” Commit 9921382. n64decomp/sm64 @ 9921382 · licence, all uses
- plasticsmoke. 2026. “Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke).” Commit 58ca1c3. plasticsmoke/megaman3-disassembly-ca65 @ 58ca1c3 · licence, all uses
- lsmmega. 2026. “mm2: Mega Man 2 disassembly (lsmmega/mm2).” Commit 94f0bf3. lsmmega/mm2 @ 94f0bf3 · licence, all uses
- Sonic Retro contributors. 2026. “s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm).” Commit 4ef457c. sonicretro/s2disasm @ 4ef457c · licence, all uses
- Sonic Retro contributors. 2026. “skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm).” Commit 73de97c. sonicretro/skdisasm @ 73de97c · licence, all uses
- IsoFrieze, kaizoman666, mikeyk, and loveemu. 2026. “SMWDisX: Super Mario World disassembly (IsoFrieze/SMWDisX).” Commit 30643c7. IsoFrieze/SMWDisX @ 30643c7 · licence, all uses
- doppelganger and Xkeeper. 2022. “smb1: Super Mario Bros. disassembly for asm6f (Xkeeper0/smb1).” Commit 1cc3c92. Xkeeper0/smb1 @ 1cc3c92 · licence, all uses
- Thorson, Maddy and Noel Berry. 2021. “Celeste: released class files (NoelFB/Celeste).” Commit 1b0ce45. NoelFB/Celeste @ 1b0ce45 · licence, all uses
- Thorson, Maddy. 2018. “Monocle Engine (public source, GitHub mirror JamesMcMahon/monocle-engine).” Commit 759c15a. JamesMcMahon/monocle-engine @ 759c15a · licence, all uses
Secondary
- Sonic Retro contributors. n.d. “Sonic Physics Guide: Running.” Sonic Retro. original · archived · licence, all uses