data/claims/s3k.yamlSonic 3 & Knuckles (sonicretro/skdisasm)
Game: Sonic 3 & Knuckles.
Sonic and Tails take off at $680 subpixels per frame (6.5 px/frame) and Knuckles at $600 (6 px/frame); underwater the speeds are $380 and $300. s3k/jump-speed
About: Sonic 3 & Knuckles Jump
Value
{
"sonic": 1664,
"knuckles": 1536,
"sonicWater": 896,
"knucklesWater": 768
}sonic3k.asm#L23342-L23350move.w #$680,d2 … move.w #$800,d2 ; set higher jump speed if super … move.w #$380,d2 ; set lower jump speed if underskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L28578-L28581move.w #$680,d2 btst #Status_Underwater,status(a0) … move.w #$380,d2skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L32494-L32497move.w #$600,d2 btst #Status_Underwater,status(a0) beq.s loc_1775C move.w #$300,d2Checked by claude, 2026-10-21 — read Sonic_Jump, Tails_Jump, and Knux_Jump at the pinned commitAs in the earlier games the speed goes along the ground's normal (L23352-L23362).
Cited by: Jump, Sonic 3 & Knuckles
MoveSprite adds $38 subpixels/frame to a falling player's speed, Sonic 1's and Sonic 2's gravity, and underwater the air modes take $28 back. s3k/gravity
About: Sonic 3 & Knuckles Gravity
Value: 56 1/256 px/frame²
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L36077-L36078move.w y_vel(a0),d0 ; load y speed addi.w #$38,y_vel(a0) ; increase vertical speed (apply gravity)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22442-L22449jsr (MoveSprite_TestGravity).l … subi.w #$28,y_vel(a0) ; reduce gravity by $28 ($38-$28=$10)s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/sub ObjectFall & SpeedToPos.asm#L5gravity: equ $38s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L30217addi.w #$38,y_vel(a0) ; increase vertical speed (apply gravity)Checked by claude, 2026-10-21 — read MoveSprite, MoveSprite_TestGravity, and Sonic_MdJump at the pinned commitCited by: Gravity, Sonic 3 & Knuckles
With no jump button held, Sonic_JumpHeight cuts a rise faster than 4 px/frame to exactly -$400 (-$200 underwater); a press once the rise is no faster than that starts a shield move instead. s3k/release-cap
About: Sonic 3 & Knuckles Variable jump height
Value: -1024 1/256 px/frame
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L23404-L23415move.w #-$400,d1 … move.w #-$200,d1 ; Underwater-specific … cmp.w y_vel(a0),d1 ; is y speed greater than 4? (2 if underwater) ble.w Sonic_ShieldMoves ; if not, branch … move.w d1,y_vel(a0) ; cap jump heightChecked by claude, 2026-10-21 — read Sonic_JumpHeight at the pinned commitCited by: Sonic 3 & Knuckles, Variable jump height
Sonic_Init sets the top speed to $600, the acceleration to $C, and the deceleration to $80 subpixels, the values of Sonic 1 and Sonic 2. s3k/ground-speeds
About: Sonic 3 & Knuckles Running
Value
{
"top": 1536,
"accel": 12,
"decel": 128
}sonic3k.asm#L21950-L21952move.w #$600,Max_speed-Max_speed(a4) move.w #$C,Acceleration-Max_speed(a4) move.w #$80,Deceleration-Max_speed(a4)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22902-L22908add.w d5,d0 cmp.w d6,d0 blt.s loc_114AA sub.w d5,d0 cmp.w d6,d0 bge.s loc_114AA move.w d6,d0s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L5-L7son_maxspeed: equ $600 ; Sonic's max speed son_acceleration: equ $C ; Sonic's acceleration son_deceleration: equ $80 ; Sonic's decelerations2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36232-L36234move.w #$600,(Sonic_top_speed).w ; set Sonic's top speed move.w #$C,(Sonic_acceleration).w ; set Sonic's acceleration move.w #$80,(Sonic_deceleration).w ; set Sonic's decelerations2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36998-L37004sub.w d5,d0 ; remove this frame's acceleration changeChecked by claude, 2026-10-21 — read Sonic_Init and the ground move-right routine at the pinned commitAs in Sonic 2, holding forward keeps a speed already above the top (s2disasm/forward-cap-kept).
Cited by: Running, Sonic 3 & Knuckles
In the air Sonic_ChgJumpDir still adds $18 a frame, but a Sonic already faster than the top speed keeps his speed; Sonic 1 and Sonic 2 pulled him down to it. s3k/air-keep-speed
About: Sonic 3 & Knuckles Air control
Value: 24 1/256 px/frame²
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L23129-L23155sub.w d5,d0 ; add acceleration to the left … add.w d5,d0 ; remove this frame's acceleration change cmp.w d1,d0 ; compare speed with top speed ble.s loc_11682 ; if speed was already greater than the maximum, branch move.w d1,d0skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · s3.asm#L20845-L20866 — Sonic 3 alone has the same checkadd.w d5,d0 ; remove this frame's acceleration change cmp.w d1,d0 ; compare speed with top speed ble.s loc_126F2 ; if speed was already greater than the maximum, branchs2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37214-L37219move.w d1,d0 ; limit speed in air going left, even if Sonic was already going faster (speed limit/cap)s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L951-L956sub.w d5,d0 ; increase leftward movement speed … cmp.w d1,d0 ; is new speed exceeding maximum? bgt.s .notleft ; if not, branch move.w d1,d0 ; cap leftward X-speed to maximumChecked by claude, 2026-10-21 — read Sonic_ChgJumpDir in sonic3k.asm and s3.asm and compared it with Sonic 2's at their pinned commitsCited by: Air control, Sonic 3 & Knuckles, Sonic the Hedgehog 2
Sonic 3 & Knuckles keeps Sonic 1's air drag, X speed less X speed shifted right 5 bits while Y speed is from −$400 to −1. s3k/air-drag-same
About: Sonic 3 & Knuckles Air control
Value
{
"shift": 5,
"vyFrom": -1024,
"vyTo": -1
}sonic3k.asm#L23169-L23193cmpi.w #-$400,y_vel(a0) ; is Sonic moving faster than -$400 upwards? … asr.w #5,d1 ; d1 = x_velocity / 32 … sub.w d1,d0 ; reduce x velocity by d1s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L986-L995cmpi.w #-$400,obVelY(a0) ; is Sonic moving faster than -$400 upwards? … asr.w #5,d1 ; divide that copy by $20 … sub.w d1,d0 ; slightly reduce speed (air drag)Checked by claude, 2026-10-21 — read Sonic_JumpPeakDecelerate at the pinned commit and compared it with s2disasm/air-drag-sameSonic 3 & Knuckles has no grace window after a ledge: Sonic_Jump is called only from the grounded modes. s3k/no-coyote-time
About: Sonic 3 & Knuckles Coyote time
Value: 0 frames
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L22328-L22330Sonic_MdNormal: bsr.w SonicKnux_Spindash bsr.w Sonic_Jumpskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22402-L22405Sonic_MdRoll: tst.b spin_dash_flag(a0) bne.s loc_10FEA bsr.w Sonic_Jumpskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22385-L22389 — Sonic_MdAir runs Sonic_JumpHeight and Sonic_ChgJumpDir, never Sonic_JumpChecked by claude, 2026-10-21 — searched sonic3k.asm for every call to Sonic_Jump; the other two (L21658, L21691) are the same two ground modes of the Competition-mode SonicTails's and Knuckles's jump routines were not traced for this claim.
Cited by: Coyote time, Sonic 3 & Knuckles
The spin dash is Sonic 2's, with the same $200 a press to $800, the same 1/32 decay, and the same release speeds from $800 to $C00. s3k/spindash
About: Sonic 3 & Knuckles Spin dash
Value
[ 2048, 2176, 2304, 2432, 2560, 2688, 2816, 2944, 3072 ]
sonic3k.asm#L23740-L23743move.b spin_dash_counter(a0),d0 add.w d0,d0 move.w word_11CF2(pc,d0.w),ground_vel(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23773-L23782word_11CF2: dc.w $800 dc.w $880skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23798-L23814lsr.w #5,d0 sub.w d0,spin_dash_counter(a0) … addi.w #$200,spin_dash_counter(a0) cmpi.w #$800,spin_dash_counter(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37688-L37697SpindashSpeeds: dc.w $800 ; 0 … dc.w $C00 ; 8s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37714-L37729lsr.w #5,d0 sub.w d0,spindash_counter(a0) … addi.w #$200,spindash_counter(a0) cmpi.w #$800,spindash_counter(a0)Checked by claude, 2026-10-21 — read SonicKnux_Spindash at the pinned commit and compared it with s2disasm/spindash-charge and spindash-speedsCited by: Sonic 3 & Knuckles, Sonic the Hedgehog 2, Spin dash
Unlike Sonic 1 and Sonic 2, a standing player slides on a slope whose push, sine × $20, is at least $D; the walking and rolling factors stay $20 and $50. s3k/slope-standing
About: Sonic 3 & Knuckles Slope physics
Value
{
"walk": 32,
"rollDown": 80,
"standMin": 13
}sonic3k.asm#L23866-L23896muls.w #$20,d0 asr.l #8,d0 tst.w ground_vel(a0) beq.s loc_11DDC … loc_11DDC: move.w d0,d1 bpl.s loc_11DE2 neg.w d1 loc_11DE2: cmpi.w #$D,d1 blo.s locret_11DDA add.w d0,ground_vel(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23914muls.w #$50,d0s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37763-L37764tst.w inertia(a0) beq.s return_1ADCAs1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1334-L1335tst.w obInertia(a0) ; check Sonic's ground speed beq.s .return ; if he's standing still, branchChecked by claude, 2026-10-21 — read Player_SlopeResist and Player_RollRepel at the pinned commit and compared them with Sonic 2'ss3.asm has the same branch (L21460-L21468), so Sonic 3 alone slides too. With a sine of 1.0 as 256, $D needs a sine of at least 104/256, an angle of roughly 24° or more.
Cited by: Slope physics, Sonic 3 & Knuckles, Sonic the Hedgehog, Sonic the Hedgehog 2
Below $280 ground speed on a steep slope the player now slips, a $80 push along the slope and a 30-frame d-pad lock, and falls off only near walls and ceilings; Sonic 1 and 2 dropped him from any slope past 45°. s3k/slope-slip
About: Sonic 3 & Knuckles Slope physics
Value
{
"minSpeed": 640,
"lock": 30,
"nudge": 128
}sonic3k.asm#L23953-L23983addi.b #$18,d0 cmpi.b #$30,d0 blo.s locret_11E6E … cmpi.w #$280,d0 bhs.s locret_11E6E move.w #30,move_lock(a0) move.b angle(a0),d0 addi.b #$30,d0 cmpi.b #$60,d0 blo.s loc_11E70 bset #Status_InAir,status(a0) … addi.w #$80,ground_vel(a0) … subi.w #$80,ground_vel(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37832-L37841addi.b #$20,d0 andi.b #$C0,d0 … clr.w inertia(a0) bset #status.player.in_air,status(a0)s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1412-L1427addi.b #$20,d0 ; rotate it by 45 degrees andi.b #$C0,d0 ; snap to nearest multiple of 90 degrees … clr.w obInertia(a0) ; clear ground speed bset #status_in_air_bit,obStatus(a0) ; set in-air flag to detach Sonic from wallChecked by claude, 2026-10-21 — read Player_SlopeRepel at the pinned commit and compared it with Sonic 2's Sonic_SlopeRepelAngles are bytes, $40 a quarter turn. Sonic 3 & Knuckles checks angles past $18 (about 34°) and drops the player only past $30 (about 68°); Sonic 2 drops him past $20 (45°) and zeroes his speed.
Cited by: Slope physics, Sonic 3 & Knuckles
Rolling keeps Sonic 2's numbers, friction of half the acceleration ($6), $20 more against the d-pad, and X speed clamped to $1000. s3k/roll-speeds
About: Sonic 3 & Knuckles Rolling
Value
{
"friction": 6,
"brake": 32,
"xCap": 4096
}sonic3k.asm#L22962-L22969move.w Acceleration-Max_speed(a4),d5 asr.w #1,d5 … move.w #$20,d4skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23055-L23057cmpi.w #$1000,d1 ble.s loc_115F6 move.w #$1000,d1s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37057-L37059asr.w #1,d5 ; natural roll deceleration = 1/2 normal acceleration move.w #$20,d4s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37137-L37139cmpi.w #$1000,d1 ble.s + move.w #$1000,d1 ; limit Sonic's speed rolling rightChecked by claude, 2026-10-21 — read Sonic_RollSpeed at the pinned commitCited by: Rolling
Against objects the player is still a 16 px wide box whose half-height is his height radius minus 3, and ducking no longer shrinks it. s3k/hitbox
About: Sonic 3 & Knuckles Hitbox
Value
{
"w": 16,
"h": 32,
"rollH": 22
}sonic3k.asm#L20679-L20689subi.w #8,d2 ; Subtract Obj_Sonic's x_radius-1 from x_pos (becomes player's left collision boundary) moveq #0,d5 move.b y_radius(a0),d5 subq.b #3,d5 ; Now player's collision height … ; Height is no longer reduced by ducking move.w #$10,d4 ; Player's collision widths2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L85229-L85250subi_.w #8,d2 … subq.b #3,d5 … move.w #$10,d4s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/Sonic ReactToItem.asm#L17-L41subq.w #sonic_react_width,d2 … subq.b #3,d5 … move.w #sonic_react_width*2,d4Checked by claude, 2026-10-21 — read TouchResponse at the pinned commit; Sonic_Init sets the height radius to `$13` (L21940)Cited by: Hitbox
A hit throws the player up at -$400 and away from the object at $200 (half both underwater), the speeds of Sonic 1 and Sonic 2. s3k/hurt-knockback
About: Sonic 3 & Knuckles Knockback
Value
{
"vy": -1024,
"vx": 512,
"waterVy": -512,
"waterVx": 256
}sonic3k.asm#L21130-L21141move.w #-$400,y_vel(a0) move.w #-$200,x_vel(a0) … move.w #-$200,y_vel(a0) move.w #-$100,x_vel(a0) … neg.w x_vel(a0)s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/Sonic ReactToItem.asm#L394-L395move.w #-$400,obVelY(a0) ; bounce Sonic vertically move.w #-$200,obVelX(a0) ; bounce Sonic horizontallys2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L85678-L85679move.w #-$400,y_vel(a0) ; make Sonic bounce away from the object move.w #-$200,x_vel(a0)Checked by claude, 2026-10-21 — read HurtCharacter at the pinned commitCited by: Knockback
The invulnerability count is now a byte but still 120, set by the hit and again on landing, and still blinks the sprite. s3k/hurt-invulnerable
About: Sonic 3 & Knuckles Invincibility frames
Value: 120 frames
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L21146move.b #120,invulnerability_timer(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24545move.b #2*60,invulnerability_timer(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22073-L22078move.b invulnerability_timer(a0),d0 beq.s loc_10CA6 subq.b #1,invulnerability_timer(a0) lsr.b #3,d0s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/Sonic ReactToItem.asm#L410move.w #2*60,flashtime(a0) ; set temporary invulnerability time to 2 secondss2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L85696move.w #$78,invulnerable_time(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38271move.w #$78,invulnerable_time(a0)Checked by claude, 2026-10-21 — read HurtCharacter, the landing code after a hit, and Sonic_Display at the pinned commitCited by: Invincibility frames
Hit without a shield, the player drops his rings, at most 32 as bouncing rings, and the count goes to 0; with no rings he dies. s3k/hurt-rings
About: Sonic 3 & Knuckles Rings as health
Value
{
"scatterMax": 32
}sonic3k.asm#L21102-L21118move.w (Ring_count).w,d0 … btst #0,status_secondary(a0) bne.s loc_102DA … tst.w d0 beq.w loc_10350 … move.l #Obj_Bouncing_Ring,(a1)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L35603-L35606moveq #$20,d0 cmp.w d0,d5 blo.s loc_1A6A6 move.w d0,d5skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L35658move.w #0,(Ring_count).wChecked by claude, 2026-10-21 — read HurtCharacter and the bouncing-ring spawner at the pinned commit; bit 0 of status_secondary is Status_ShieldCited by: Rings as health, Sonic 3 & Knuckles
A hit with any shield costs the shield, not the rings; HurtCharacter clears the shield bit and all three element bits together (`andi.b s3k/shield-loss
About: Sonic 3 & Knuckles Shields
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L21110-L21124btst #0,status_secondary(a0) bne.s loc_102DA … loc_102DA: andi.b #$8E,status_secondary(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.constants.asm#L184-L190Status_Shield = 0 Status_Invincible = 1 Status_SpeedShoes = 2Checked by claude, 2026-10-21 — read HurtCharacter and the status bit equates at the pinned commitCited by: Sonic 3 & Knuckles
An elemental shield makes the player immune to each object whose shield_reaction byte names that element, and any shield deflects objects flagged for it at 8 px/frame. s3k/shield-immunity
About: Sonic 3 & Knuckles Shields
Value
{
"deflectSpeed": 2048
}sonic3k.asm#L21039-L21083and.b shield_reaction(a1),d0 ; Does one of the player's shields grant immunity to this object?? … andi.b #1<<3,d0 ; Should the object be bounced away by a shield? … muls.w #-$800,d1Checked by claude, 2026-10-21 — read Touch_ChkHurt at the pinned commitWhich hazards carry which bits (fire, electric, water, projectile) is set object by object and was not surveyed.
Cited by: Shields, Sonic 3 & Knuckles
A second press during a jump, once the rise is no faster than 4 px/frame (2 underwater), uses the shield: fire dashes at 8 px/frame and stops the fall, lightning jumps again at -$580, and bubble stops dead and drops at 8 px/frame, then bounces at 7.5 px/frame on landing. s3k/shield-moves
About: Sonic 3 & Knuckles Shields
Value
{
"fireX": 2048,
"lightningY": -1408,
"bubbleY": 2048,
"bubbleBounce": 1920,
"bubbleBounceWater": 1024
}sonic3k.asm#L23400-L23411Sonic_JumpHeight: tst.b jumping(a0) ; is Sonic jumping? beq.s Sonic_UpVelCap ; if not, branch … ble.w Sonic_ShieldMoves ; if not, branchskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23446-L23488move.w #$800,d0 … move.w #0,y_vel(a0) ; kill y-velocity … move.w #-$580,y_vel(a0) ; bounce Sonic up, creating the double jump effect … move.w #0,x_vel(a0) ; halt horizontal speed … move.w #$800,y_vel(a0) ; force Sonic downskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24436-L24441BubbleShield_Bounce: movem.l d1-d2,-(sp) move.w #$780,d2 btst #Status_Underwater,status(a0) beq.s loc_12246 move.w #$400,d2Checked by claude, 2026-10-21 — read Sonic_ShieldMoves through Sonic_BubbleShield and BubbleShield_Bounce at the pinned commitOnly Sonic gets the moves; Sonic_FireShield is skipped while he is invincible. The bounce goes along the ground's normal.
Cited by: Shields, Sonic 3 & Knuckles
With no shield, the press gives Sonic the insta-shield, which while active makes him invincible and widens his hit box to 48 × 48 px. s3k/insta-shield
About: Sonic 3 & Knuckles Shields
Value
{
"w": 48,
"h": 48
}sonic3k.asm#L23508-L23514btst #Status_Shield,status_secondary(a0) ; does Sonic have an S2 shield? (the elementals were already filtered out at this point) bne.s locret_11A14 ; if yes, branch move.b #1,(Shield+anim).w move.b #1,double_jump_flag(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L20656-L20666cmpi.b #1,double_jump_flag(a0) ; Is the Insta-Shield currently in its 'attacking' mode? … bset #Status_Invincible,status_secondary(a0) ; Make the player invincible … move.w #$30,d4 ; Player's width move.w #$30,d5 ; Player's heightChecked by claude, 2026-10-21 — read Sonic_InstaShield and TouchResponse at the pinned commitHow many frames the attacking mode lasts is set by the shield object's animation and was not traced.
Cited by: Shields, Sonic 3 & Knuckles
Sonic 3 alone (s3.asm) has the same shield moves and insta-shield; its transformation check asks only for the seven Chaos Emeralds and 50 rings. s3k/sonic-3-alone
About: Sonic 3 & Knuckles Shields
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·s3.asm#L21122-L21184move.w #$800,d0 … move.w #-$580,y_vel(a0) … move.w #$800,y_vel(a0) … Sonic_CheckTransform: cmpi.w #7,(Chaos_emerald_count).wChecked by claude, 2026-10-21 — read Sonic_ShieldMoves in s3.asm at the pinned commit and compared it with sonic3k.asm L23432-L23518Cited by: Sonic 3 & Knuckles
Tails flies for 480 frames, a count of 240 that drops every other frame; flying adds $8 gravity a frame, and while time remains each press lifts him $20 a frame for up to 30 frames, until he rises faster than 1 px/frame. s3k/tails-flight
About: Sonic 3 & Knuckles Flight
Value
{
"frames": 480,
"gravity": 8,
"lift": 32,
"liftFrames": 30,
"liftUntil": -256
}sonic3k.asm#L28703-L28705move.b #1,double_jump_flag(a0) move.b #(8*60)/2,double_jump_property(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L27632-L27674andi.b #1,d0 beq.s loc_14836 tst.b double_jump_property(a0) beq.s loc_14836 subq.b #1,double_jump_property(a0) … cmpi.w #-$100,y_vel(a0) blt.s loc_14858 subi.w #$20,y_vel(a0) addq.b #1,double_jump_flag(a0) cmpi.b #$20,double_jump_flag(a0) … move.b #2,double_jump_flag(a0) loc_1488C: addi.w #8,y_vel(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L27610-L27614jsr (MoveSprite_TestGravity2).lChecked by claude, 2026-10-21 — read Tails_Test_For_Flight, Tails_FlyingSwimming, and Tails_Move_FlySwim at the pinned commit; MoveSprite_TestGravity2 moves without gravity, so the 8 is the only gravity in flightA press sets the flag to 2; each lifting frame adds 1, and at $20 the lift stops (30 frames). Lifting frames skip the +8. With the count at 0 a press does nothing. Underwater, carrying Sonic, a press doesn't lift.
Cited by: Flight, Sonic 3 & Knuckles
Knuckles's glide starts at 4 px/frame and gains $4 a frame to a cap of 24 px/frame, and its fall settles near 0.5 px/frame, $20 added below $80 and taken above. s3k/knuckles-glide
About: Sonic 3 & Knuckles Flight
Value
{
"start": 1024,
"accel": 4,
"cap": 6144,
"fallTarget": 128,
"fallStep": 32
}sonic3k.asm#L32600-L32613addi.w #$200,y_vel(a0) bpl.s loc_17898 move.w #0,y_vel(a0) loc_17898: moveq #0,d1 move.w #$400,d0 move.w d0,ground_vel(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L31642-L31663cmpi.w #$400,d0 bhs.s .mediumSpeed … addq.w #8,d0 … cmpi.w #$1800,d0 bhs.s .applySpeed … addq.w #4,d0skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L31723-L31731cmpi.w #$80,y_vel(a0) blt.s .fallingSlow subi.w #$20,y_vel(a0) bra.s .fallingFast … addi.w #$20,y_vel(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L30748-L30764andi.b #button_A_mask|button_B_mask|button_C_mask,d0 bne.s .continueGliding … asr.w x_vel(a0) asr.w x_vel(a0)Checked by claude, 2026-10-21 — read Knux_Test_For_Glide, Knuckles_Move_Glide, and Knuckles_Glide at the pinned commit; Knux_Glide_Freespace moves with MoveSprite_TestGravity2, which adds no gravityThe start adds $200 to Y speed and stops any rise. Gliding needs the button held; letting go quarters the X speed and drops him. Turning holds the speed and swings the direction through a sine.
Cited by: Flight, Sonic 3 & Knuckles
With neither Left nor Right held, Sonic_Move takes the current acceleration off a standing or running Sonic's ground speed each frame, stopping at 0, as Sonic 2 does; that is $C out of water and without speed shoes (ground-speeds), and $C for Super Sonic. With the infinite-inertia flag some objects set (bit 7 of status_secondary), it skips the d-pad and the friction both. s3k/ground-friction
About: Sonic 3 & Knuckles Running
Value: 12 1/256 px/frame²
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L22713-L22735loc_11306:
…
andi.b #button_left_mask|button_right_mask,d0
bne.s loc_11332
move.w ground_vel(a0),d0
…
sub.w d5,d0
bcc.s loc_11320
move.w #0,d0
…
add.w d5,d0
bcc.s loc_1132E
…s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36826-L36846move.w inertia(a0),d0 ; get Sonic's current ground speed
…
sub.w d5,d0 ; reduce current rightward speed by acceleration
…
move.w #0,d0 ; reset speed to zero on sign change
…
add.w d5,d0 ; reduce current leftward speed by acceleration
…
move.w #0,d0 ; reset speed to zero on sign changeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22467-L22468tst.b status_secondary(a0) ; is bit 7 set? (Infinite inertia) bmi.w loc_11332 ; if so, branchskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22709-L22711tst.b (Super_Sonic_Knux_flag).w beq.s loc_11306 move.w #$C,d5Checked by claude, 2026-10-26 — read loc_11306 in Sonic_Move at the pinned commit; d5 holds the acceleration from the Max_speed block (Sonic_Move's first lines, L22463-L22465), which water and speed shoes change, or `$C` for Super SonicSonic 3 & Knuckles keeps Sonic 2's skid bug: on flat ground, not mid-flip (flip_type not negative), a reversal skids (stopping animation, facing his motion) when the slowed speed is at least $400 moving right but only $301 moving left, because the angle read overwrites the speed's low byte. s3k/skid-threshold
About: Sonic 3 & Knuckles Running
Value
{
"right": 1152,
"left": 897
}sonic3k.asm#L22850-L22879loc_11430:
sub.w d4,d0
…
move.w #-$80,d0
…
; These three instructions partially overwrite the inertia value in
…
move.b angle(a0),d0
addi.b #$20,d0
andi.b #$C0,d0
…
cmpi.w #$400,d0
…skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22916-L22943loc_114B6:
add.w d4,d0
…
move.w #$80,d0
…
move.b angle(a0),d0
addi.b #$20,d0
andi.b #$C0,d0
…
cmpi.w #-$400,d0
bgt.s locret_11506
tst.b flip_type(a0)
…skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L38 — the build's FixBugs, 0, keeps the bugFixBugs = 0s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36966-L36974move.b angle(a0),d0 ; get Sonic's current angle (and partially overwrite d0...)
…
cmpi.w #$400,d0 ; has Sonic changed direction while being really fast?
blt.s return_1A744 ; if not, don't play skidding animation/sound
…
bclr #status.player.x_flip,status(a0) ; clear X-flip flag (Sonic is now facing right)Checked by claude, 2026-10-26 — read sub_113F6 and sub_11482 at the pinned commit: d0's low byte becomes the angle + `$20` masked to `$C0`, 0 on flat ground, before `cmpi.w #$400,d0` (right) and `cmpi.w #-$400,d0` (left)Moving left, the slowed speed with its low byte zeroed is at most −$400 once it is −$301 or less; before the frame, that is −$381 (897). Moving right it needs $400 after the $80 brake, $480 (1152) before.
On the ground the d-pad turns Sonic only when his ground speed is 0 or already that way, and a skid turns him to face his motion; rolling, the same, with no skid; in the air, Left or Right turns him at once unless he jumped from a roll. While the spin dash charges, the d-pad doesn't turn him. On the ground and rolling, the d-pad is skipped altogether while an object holds his controls: move_lock, the infinite-inertia flag (bit 7 of status_secondary), or, rolling, a negative spin_dash_flag (pinball mode). s3k/facing
About: Sonic 3 & Knuckles Running
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L22822-L22828sub_113F6:
move.w ground_vel(a0),d0
beq.s loc_113FE
bpl.s loc_11430
loc_113FE:
bset #Status_Facing,status(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22893-L22897sub_11482:
move.w ground_vel(a0),d0
bmi.s loc_114B6
bclr #Status_Facing,status(a0)
beq.s loc_1149Cskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23073-L23080sub_11608:
move.w ground_vel(a0),d0
beq.s loc_11610
bpl.s loc_1161E
loc_11610:
bset #Status_Facing,status(a0)
move.b #2,anim(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23127-L23133btst #Status_RollJump,status(a0) ; did Sonic jump from rolling?
bne.s Sonic_Jump_ResetScr ; if yes, branch to skip midair control
move.w x_vel(a0),d0
btst #button_left,(Ctrl_1_held_logical).w
beq.s loc_11682 ; if not holding left, branch
bset #Status_Facing,status(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23762-L23764btst #Status_Facing,status(a0)
beq.s loc_11CDC
neg.w ground_vel(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22467-L22470tst.b status_secondary(a0) ; is bit 7 set? (Infinite inertia) bmi.w loc_11332 ; if so, branch tst.w move_lock(a0) bne.w loc_112EAskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22970-L22975tst.b spin_dash_flag(a0) bmi.w loc_115C6 tst.b status_secondary(a0) bmi.w loc_115C6 tst.w move_lock(a0) bne.s loc_1154EChecked by claude, 2026-10-26 — read Sonic_Move's and Sonic_RollSpeed's first lines, sub_113F6, sub_11482, sub_11608, sub_1162C, Sonic_ChgJumpDir, and the spin dash's release at the pinned commit; Status_Facing set is facing left (`bset` on Left, `bclr` on Right)A balance turns him too (balance).
Standing, Sonic's floor and ceiling sensors sit 19 px above and below his center and 9 px to each side, a 19 × 39 px box; curled up, 14 and 7, 15 × 29 px; his wall sensors sit 10 px to either side. These are Sonic 2's sizes. s3k/collision-box
About: Sonic 3 & Knuckles Hitbox
Value
{
"w": 19,
"h": 39,
"rollW": 15,
"rollH": 29,
"radius": {
"w": 9,
"h": 19,
"rollW": 7,
"rollH": 14,
"push": 10
}
}sonic3k.asm#L21940-L21943move.b #$13,y_radius(a0) ; this sets Sonic's collision height (2*pixels)
move.b #9,x_radius(a0)
move.b #$13,default_y_radius(a0)
move.b #9,default_x_radius(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23296-L23299move.b #$E,y_radius(a0)
move.b #7,x_radius(a0)
move.b #2,anim(a0) ; enter roll animation
addq.w #5,y_pos(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L20231addi.w #$A,d3skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L20548subi.w #$A,d3s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37368-L37371move.b #$E,y_radius(a0)
move.b #7,x_radius(a0)
move.b #AniIDSonAni_Roll,anim(a0) ; use "rolling" animation
addq.w #5,y_pos(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36226-L36227move.b #$13,y_radius(a0) ; this sets Sonic's collision height (2*pixels) move.b #9,x_radius(a0) ; this sets Sonic's Collision width (2*pixels)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L44376 — Sonic 2's CheckRightWallDistaddi.w #$A,d3s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L44567 — Sonic 2's CheckLeftWallDistsubi.w #$A,d3Checked by claude, 2026-10-26 — read Sonic_Init, Player_DoRoll, CheckRightWallDist, and CheckLeftWallDist at the pinned commit, and Sonic 2's Obj01_Init, Sonic_Roll, CheckRightWallDist, and CheckLeftWallDistA sensor at center + radius tests the radius-th pixel out, so the box spans 2 × radius + 1 pixels.
With Down held, neither Left nor Right, and no infinite-inertia flag (bit 7 of status_secondary), Sonic curls into a roll at a ground speed of $100 or more either way, twice Sonic 2's $80; slower than that, unless he stands on an object, he crouches, where Sonic 2 and Sonic 3 alone crouch only from Sonic_Move's standstill branch. s3k/roll-start
About: Sonic 3 & Knuckles Rolling
Value: 256 1/256 px/frame
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L23258-L23278SonicKnux_Roll:
…
andi.b #button_left_mask|button_right_mask,d0 ; is left/right being pressed?
bne.s locret_1177E ; if yes, branch
btst #button_down,(Ctrl_1_logical).w ; is down being pressed?
…
cmpi.w #$100,d0 ; is Sonic moving at $100 speed or faster?
bhs.s Player_ChkRoll ; if so, branch
btst #Status_OnObj,status(a0)
…
move.b #8,anim(a0) ; enter ducking animations2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37345-L37352mvabs.w inertia(a0),d0
cmpi.w #$80,d0 ; is Sonic moving at $80 speed or faster?
blo.s Obj01_NoRoll ; if not, branchskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · s3.asm#L20970-L20976 — Sonic 3 alone keeps Sonic 2's checkcmpi.w #$80,d0 ; is Sonic moving at $80 speed or faster?
blo.s locret_127D0 ; if so, branchs2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36788-L36791 — Sonic 2's crouch, reached from Sonic_Move's standstill branchSonic_Duck:
btst #button_down,(Ctrl_1_Held_Logical).w ; is down being pressed?
beq.s Obj01_ResetScr ; if not, branch
move.b #AniIDSonAni_Duck,anim(a0) ; use "ducking" animationskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · s3.asm#L20420-L20423 — Sonic 3 alone's crouch, in Sonic_Move's standstill branchloc_12362: btst #button_down,(Ctrl_1_held_logical).w beq.s loc_1238C move.b #8,anim(a0)Checked by claude, 2026-10-26 — read SonicKnux_Roll in sonic3k.asm, Sonic_Roll and Sonic_Move's standstill branch in s3.asm, and Sonic 2's Sonic_Roll and Sonic_Duck at their pinned commitsThe crouch is animation 8, which a jump press turns into a spin dash (spindash-controls); with Down released, Player_ChkWalk sets animation 0 again. Holding Left or Right skips the routine, so a crouch already begun stays through a brake.
Cited by: Sonic 3 & Knuckles
Outside pinball mode (spin_dash_flag set on a roll) and without the infinite-inertia flag, a roll ends once friction and the d-pad leave the ground speed under $80 either way, and the speed left is kept; Sonic 2 and Sonic 3 alone unroll only at 0. Standing up moves his center 5 px up with normal gravity. In pinball mode a positive flag sets the speed to $400 in his facing direction instead, and a negative one skips the d-pad, friction, and this check. s3k/unroll
About: Sonic 3 & Knuckles Rolling
Value: 128 1/256 px/frame
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L23007-L23028move.w ground_vel(a0),d0
…
cmpi.w #$80,d0
bhs.s loc_115C6
…
bclr #Status_Roll,status(a0)
move.b y_radius(a0),d0
move.b default_y_radius(a0),y_radius(a0)
…
sub.b default_y_radius(a0),d0
…
add.w d0,y_pos(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37097-L37106Sonic_CheckRollStop:
tst.w inertia(a0)
bne.s Obj01_Roll_ResetScr
…
bclr #status.player.rolling,status(a0)
…
subq.w #5,y_pos(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · s3.asm#L20728-L20739tst.w ground_vel(a0)
bne.s loc_12636
…
bclr #Status_Roll,status(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22970-L22973tst.b spin_dash_flag(a0) bmi.w loc_115C6 tst.b status_secondary(a0) bmi.w loc_115C6skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23032-L23036loc_115B4: move.w #$400,ground_vel(a0) btst #Status_Facing,status(a0) beq.s loc_115C6 neg.w ground_vel(a0)Checked by claude, 2026-10-26 — read Sonic_RollSpeed in sonic3k.asm and s3.asm and Sonic 2's Sonic_CheckRollStop at their pinned commitsThe check runs after the roll's friction, every rolling frame. spin_dash_flag stays 0 on a roll in the lab.
Cited by: Sonic 3 & Knuckles
Crouching (animation 8), a press of A, B, or C starts the spin dash's charge at 0; while Down stays held, each frame first takes 1/32 of the counter (rounded down) and a press adds $200, to $800; letting go of Down releases it, curling him (5 px down) and setting the ground speed from the counter's high byte (spindash; Super Sonic's table is faster), negated facing left. On the start, charge, and release frames he doesn't move, and the floor sensors still run. The charge's decay, step, and cap are Sonic 2's. s3k/spindash-controls
About: Sonic 3 & Knuckles Spin dash
Value
{
"charge": 512,
"max": 2048,
"decayShift": 5
}sonic3k.asm#L23679-L23696SonicKnux_Spindash:
tst.b spin_dash_flag(a0)
bne.s loc_11C5E
cmpi.b #8,anim(a0)
…
move.b (Ctrl_1_pressed_logical).w,d0
andi.b #button_A_mask|button_B_mask|button_C_mask,d0
…
addq.l #4,sp
move.b #1,spin_dash_flag(a0)
move.w #0,spin_dash_counter(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23737-L23746move.b #0,spin_dash_flag(a0)
…
move.b spin_dash_counter(a0),d0
…
move.w word_11CF2(pc,d0.w),ground_vel(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23770-L23771jsr (Play_SFX).l
bra.s loc_11D5Eskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23796-L23822move.w spin_dash_counter(a0),d0
lsr.w #5,d0
sub.w d0,spin_dash_counter(a0)
…
addi.w #$200,spin_dash_counter(a0)
cmpi.w #$800,spin_dash_counter(a0)
…
loc_11D5E:
addq.l #4,sps2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37714-L37729lsr.w #5,d0
sub.w d0,spindash_counter(a0)
…
addi.w #$200,spindash_counter(a0)
cmpi.w #$800,spindash_counter(a0)Checked by claude, 2026-10-26 — read SonicKnux_Spindash at the pinned commit and compared it with s2disasm/spindash-start and spindash-charge; `addq.l #4,sp` drops Sonic_MdNormal's return, so the rest of the mode (the move included) doesn't run, and loc_11D5E calls Player_LevelBound and Player_AnglePos itselfThe crouch can begin while he still moves slower than $100 (roll-start), so a spin dash can start with ground speed left over; it stays until the release sets the speed.
Cited by: Sonic 3 & Knuckles
With room overhead (jump-headroom), a jump press adds the jump speed along the ground's normal, -$680 straight up on flat ground with normal gravity (jump-speed gives the underwater and Super speeds), keeps the X speed, and ends the frame: the jump frame moves nothing. Standing, the jump curls Sonic and moves his center 5 px down (up with reverse gravity); from a roll he keeps the standing size (roll-jump-size). s3k/takeoff
About: Sonic 3 & Knuckles Jump
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L23353-L23366add.w d1,x_vel(a0) ; make Sonic jump (in X... this adds nothing on level ground)
…
add.w d0,y_vel(a0) ; make Sonic jump (in Y)
bset #Status_InAir,status(a0)
…
addq.l #4,spskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23370-L23386move.b default_y_radius(a0),y_radius(a0)
…
btst #Status_Roll,status(a0)
bne.s Sonic_RollJump
move.b #$E,y_radius(a0)
…
bset #Status_Roll,status(a0)
…
sub.w d0,y_pos(a0)s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1224-L1236add.w d1,obVelX(a0) ; apply to X speed
…
add.w d0,obVelY(a0) ; apply to Y speed
…
addq.l #4,sp ; run in-air subroutines when we returnThe atlas reads this differently: Dropping the return address returns from Sonic_MdNormal itself, skipping the rest of the grounded routine, SpeedToPos included. The in-air routines run from the next frame. (s1disasm/jump-frame-no-move)Checked by claude, 2026-10-26 — read Sonic_Jump at the pinned commit; `addq.l #4,sp` drops the caller's return, so Sonic_Move and the move don't run on the press frameA jump from a roll sets the roll-jump flag, and while it is set Sonic_ChgJumpDir skips the d-pad: no steering and no turning, though the air drag still runs. Landing clears it, as Sonic 2's Sonic_ResetOnFloor does; unlike Sonic 2, whose Sonic_JumpHeight only caps the rise, a jump press once his rise is no faster than 4 px/frame (2 underwater) clears it too (insta-shield-motion), as in Sonic 3 alone. s3k/roll-jump-lock
About: Sonic 3 & Knuckles Air control
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L23392-L23393Sonic_RollJump:
bset #Status_RollJump,status(a0) ; set the rolling+jumping flagskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23127-L23128btst #Status_RollJump,status(a0) ; did Sonic jump from rolling?
bne.s Sonic_Jump_ResetScr ; if yes, branch to skip midair controls2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37207-L37208btst #status.player.rolljumping,status(a0) ; did Sonic jump from rolling?
bne.s Obj01_Jump_ResetScr ; if yes, branch to skip midair controlskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24408 — Player_TouchFloorbclr #Status_RollJump,status(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38188-L38191 — Sonic 2's Sonic_ResetOnFloorbclr #status.player.in_air,status(a0) bclr #status.player.pushing,status(a0) bclr #status.player.rolljumping,status(a0) move.b #0,jumping(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37461-L37479 — Sonic 2's routine caps the rise and checks for Super Sonic, and touches no roll-jump flagSonic_JumpHeight:
tst.b jumping(a0) ; is Sonic jumping?
…
move.w d1,y_vel(a0) ; immediately reduce Sonic's upward speed to d1
+
tst.b y_vel(a0) ; is Sonic exactly at the height of his jump?
beq.s Sonic_CheckGoSuper ; if yes, test for turning into Super Sonic
rtsskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · s3.asm#L21115 — Sonic 3 alone's Sonic_ShieldMovesbclr #Status_RollJump,status(a0)Checked by claude, 2026-10-26 — read Sonic_Jump, Sonic_ChgJumpDir, Sonic_JumpHeight, and Player_TouchFloor at the pinned commit, and Sonic 2's Sonic_ChgJumpDir, Sonic_JumpHeight, and Sonic_ResetOnFloor; Sonic_Jump_ResetScr falls into Sonic_JumpPeakDecelerateA jump from a roll resets Sonic's sensors to the standing size, 19 px below his center, while his ball stays drawn, so he lands 5 px higher than a standing jump would, and landing moves him by the difference, 0. Sonic 2 has the same order of instructions. s3k/roll-jump-size
About: Sonic 3 & Knuckles Jump
Value: 5 px
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L23370-L23373move.b default_y_radius(a0),y_radius(a0)
move.b default_x_radius(a0),x_radius(a0)
btst #Status_Roll,status(a0)
bne.s Sonic_RollJumpskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24381-L24388move.b y_radius(a0),d0
move.b default_y_radius(a0),y_radius(a0)
…
btst #Status_Roll,status(a0)
…
sub.b default_y_radius(a0),d0s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37431-L37435move.b #$13,y_radius(a0)
move.b #9,x_radius(a0)
endif
btst #status.player.rolling,status(a0)
bne.s Sonic_RollJumpChecked by claude, 2026-10-26 — read Sonic_Jump and Player_TouchFloor at the pinned commit and Sonic 2's Sonic_JumpA jump press during a jump, once the rise is no faster than 4 px/frame (2 underwater) and before any shield move or insta-shield this jump, clears the roll-jump flag before the shield checks, so a roll jump gets its steering back. With no shield, not invincible, not Super, and short of turning Super (50 rings, Update_HUD_timer nonzero, and seven Super Emeralds, or seven Chaos Emeralds with Emeralds_converted_flag clear), the insta-shield follows and doesn't change his position or speed; with an elemental shield its move follows (shield-moves). Later presses do nothing until he lands. s3k/insta-shield-motion
About: Sonic 3 & Knuckles Shields
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L23432-L23438Sonic_ShieldMoves:
tst.b double_jump_flag(a0) ; is Sonic currently performing a double jump?
bne.w locret_11A14 ; if yes, branch
move.b (Ctrl_1_pressed_logical).w,d0
…
bclr #Status_RollJump,status(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23446-L23481 — Sonic_FireShield, Sonic_LightningShield, and Sonic_BubbleShield, each passed without its shield; invincibility returns at onceskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23494-L23506Sonic_CheckTransform:
cmpi.b #7,(Super_emerald_count).w ; does Sonic have all 7 Super Emeralds?
bhs.s loc_119E8 ; if yes, branch
cmpi.b #7,(Chaos_emerald_count).w ; does Sonic have all 7 Chaos Emeralds?
blo.s Sonic_InstaShield ; if not, branch
tst.b (Emeralds_converted_flag).w
bne.s Sonic_InstaShield
…
cmpi.w #50,(Ring_count).w ; does Sonic have at least 50 rings?
blo.s Sonic_InstaShield ; if not, perform Insta-Shield
tst.b (Update_HUD_timer).w
bne.s Sonic_Transformskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23508-L23514Sonic_InstaShield:
…
move.b #1,(Shield+anim).w
move.b #1,double_jump_flag(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L34647-L34651cmpi.b #7,mapping_frame(a0) ; Has it reached then end of its animation?
…
move.b #2,double_jump_flag(a2) ; Mark attack as overskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24415-L24426tst.b double_jump_flag(a0)
…
loc_1222A:
move.b #0,double_jump_flag(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23400-L23411Sonic_JumpHeight: tst.b jumping(a0) ; is Sonic jumping? beq.s Sonic_UpVelCap ; if not, branch move.w #-$400,d1 btst #Status_Underwater,status(a0) ; is Sonic underwater? beq.s loc_118D2 ; if not, branch move.w #-$200,d1 ; Underwater-specific loc_118D2: cmp.w y_vel(a0),d1 ; is y speed greater than 4? (2 if underwater) ble.w Sonic_ShieldMoves ; if not, branchskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · s3.asm#L21115 — Sonic 3 alone's Sonic_ShieldMovesbclr #Status_RollJump,status(a0)Checked by claude, 2026-10-26 — read Sonic_JumpHeight, Sonic_ShieldMoves through Sonic_InstaShield, Obj_InstaShield_Main, and Player_TouchFloor at the pinned commit, and s3.asm's Sonic_ShieldMoves; double_jump_flag goes 1 on the press, 2 when the shield's animation ends, and 0 only on landingSonic 3 alone has the same bclr. The elemental shields' moves do change his motion (shield-moves) but aren't in the lab.
Landing on flat ground keeps Sonic's horizontal speed as his ground speed, sets his Y speed to 0, and, if he was curled and spin_dash_flag is clear (not pinball mode), uncurls him; with normal gravity a ball from a standing jump or a roll moves 5 px up, one from a roll jump doesn't move. It clears the jump, roll-jump, and insta-shield flags; with the bubble shield used, it bounces him instead (shield-moves). s3k/landing
About: Sonic 3 & Knuckles Jump
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L24139-L24142loc_11F9C:
move.w #0,y_vel(a0)
move.w x_vel(a0),ground_vel(a0)
bsr.w Player_TouchFloor_Check_Spindashskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24365-L24368 — with the flag set it jumps past the uncurlPlayer_TouchFloor_Check_Spindash: tst.b spin_dash_flag(a0) bne.s loc_121D8 move.b #0,anim(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24375-L24409Player_TouchFloor:
…
move.b y_radius(a0),d0
move.b default_y_radius(a0),y_radius(a0)
…
btst #Status_Roll,status(a0)
…
bclr #Status_Roll,status(a0)
…
sub.b default_y_radius(a0),d0
…
add.w d0,y_pos(a0)
…skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24415-L24426tst.b double_jump_flag(a0)
…
loc_1222A:
move.b #0,double_jump_flag(a0)Checked by claude, 2026-10-26 — read SonicKnux_DoLevelCollision's floor branch and Player_TouchFloor at the pinned commitThe lab's stages are built of Angel Island Act 1's block 1, a full 16 × 16 px block solid from every side; Sonic 3 & Knuckles's level collision is 16 × 16 px blocks, and a sensor's search looks in its block and at most one more. s3k/collision-blocks
About: Sonic 3 & Knuckles Hitbox
Value
{
"word": 61441,
"index": 255
}sonic3k.asm#L19250-L19264sub_F264:
…
move.w (a1),d0
…
andi.w #$3FF,d0
…
btst d5,d4
bne.s loc_F282
loc_F274:
add.w a3,d2
bsr.w sub_F30C
…
…skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L19301-L19325cmpi.b #$10,d0
beq.s loc_F2FE
…
move.w #$F,d1
sub.w d0,d1
…
loc_F2FE:
sub.w a3,d2
bsr.w sub_F30C
…
subi.w #$10,d1Checked by claude, 2026-10-26 — read FindFloor's sub_F264 at the pinned commit; read block 1's entries in Levels/AIZ/Collision/1.bin (primary and secondary index `$FF`) and height map `$FF` (16 in every column) in the disassembly's dataA chunk word's low 10 bits are the block and bits 12 to 15 its solidity for the two collision paths; $F001 is block 1 solid from every side on both. The lab's ROM check reads block 1's collision index in RAM at the start of each run.
On a flat floor, not standing on an object, after each ground move, Player_AnglePos reads two floor sensors, at his center's X ± his width radius and his center's Y + his height radius; the nearer floor counts, and from 1 to 14 px into it he moves up to its surface. s3k/floor-sensors
About: Sonic 3 & Knuckles Hitbox
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L18812-L18847move.w y_pos(a0),d2
move.w x_pos(a0),d3
…
move.b y_radius(a0),d0
…
add.w d0,d2
move.b x_radius(a0),d0
…
add.w d0,d3
…
bsr.w FindFloor
…
…skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L18886-L18893Player_Angle:
move.w d0,d3
move.b (Secondary_Angle).w,d2
cmp.w d0,d1
ble.s loc_ED5E
move.b (Primary_Angle).w,d2
move.w d1,d3
move.w d0,d1Checked by claude, 2026-10-26 — read Player_AnglePos and Player_Angle at the pinned commitAbove the floor, walk-off decides between a snap down and a fall.
On flat ground, not on an object, when both floor sensors find the floor more than |X speed's high byte| + 4 px below, at most 14, Sonic leaves the ground with his speeds as they are, unless stick_to_convex is set; closer than that, he moves down to it. Sonic 2 has the same rule; Sonic 1 used a flat 14 px. s3k/walk-off
About: Sonic 3 & Knuckles Coyote time
Value
{
"add": 4,
"max": 14
}sonic3k.asm#L18853-L18879loc_ED14:
…
move.b x_vel(a0),d0
…
neg.b d0
…
addq.b #4,d0
cmpi.b #$E,d0
…
move.b #$E,d0
…
cmp.b d0,d1
…s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43135-L43143loc_1E31E:
mvabs.b x_vel(a0),d0
addq.b #4,d0
cmpi.b #$E,d0
blo.s +
move.b #$E,d0
+
cmp.b d0,d1
bgt.s loc_1E33Cs1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/Sonic AnglePos.asm#L100-L101 — Sonic 1's AnglePoscmpi.w #$E,d1 bgt.s .in_air ; branch if Sonic is > 14px above floorskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L18854tst.b stick_to_convex(a0)Checked by claude, 2026-10-26 — read Player_AnglePos at the pinned commit, Sonic 2's AnglePos, and Sonic 1's; `move.b x_vel(a0),d0` reads the speed's high byte, its whole pixelsstick_to_convex, which skips the test, is 0 in the lab.
Cited by: Sonic 3 & Knuckles
Outside Competition mode, on flat ground with a ground speed, Sonic's wall sensor sits 10 px ahead of where the frame's speeds would put his center and 8 px below it; in the air his side sensors sit 10 px to either side of his center at its height. s3k/wall-sensors
About: Sonic 3 & Knuckles Hitbox
Value: 8 px below his center, on flat ground
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L19725-L19773move.l x_pos(a0),d3
move.l y_pos(a0),d2
move.w x_vel(a0),d1
…
add.l d1,d3
move.w y_vel(a0),d1
…
add.l d1,d2
…
andi.b #$38,d1
…
addq.w #8,d2skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L20224-L20231CheckRightWallDist:
move.w y_pos(a0),d2
move.w x_pos(a0),d3
tst.w (Competition_mode).w
bne.s loc_FAA4
loc_FA88:
addi.w #$A,d3skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L20541-L20548CheckLeftWallDist:
move.w y_pos(a0),d2
move.w x_pos(a0),d3
tst.w (Competition_mode).w
bne.s loc_FDC8
loc_FDA8:
subi.w #$A,d3Checked by claude, 2026-10-26 — read sub_F61C, CheckRightWallDist, and CheckLeftWallDist at the pinned commitOn flat ground, with a ground speed and bit 6 of object_control clear (no object carrying him), a wall at the sensor ahead cuts the frame's X speed by the sensor's depth in whole pixels, so the move stops him beside it, and sets the ground speed to 0. s3k/ground-wall
About: Sonic 3 & Knuckles Hitbox
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L22747-L22809loc_11350:
btst #6,object_control(a0)
bne.w locret_113F4
…
tst.w ground_vel(a0)
…
bsr.w sub_F61C
…
tst.w d1
bpl.s locret_113F4
asl.w #8,d1
…
…Checked by claude, 2026-10-26 — read loc_11350 in Sonic_Move at the pinned commit; Sonic_RollSpeed branches to it too (L23066)In the air SonicKnux_DoLevelCollision picks one of four checks by the direction of his speeds after gravity, from GetArcTan less $20: mostly down, mostly up, mostly left, or mostly right. The arctangent table puts the split where the smaller speed is 250/256 of the larger. s3k/air-quadrant
About: Sonic 3 & Knuckles Hitbox
Value: 250 /256, the ratio of the smaller speed to the larger at which the table reaches `$20`
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L24083-L24093move.w x_vel(a0),d1
move.w y_vel(a0),d2
jsr (GetArcTan).l
subi.b #$20,d0
andi.b #$C0,d0
cmpi.b #$40,d0
beq.w Player_HitLeftWall
cmpi.b #$80,d0
beq.w Player_HitCeilingAndWalls
cmpi.b #$C0,d0
beq.w loc_12102skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L3094-L3118cmp.w d3,d4
…
move.b ArcTanTable(pc,d4.w),d0
…
moveq #$40,d0
sub.b ArcTanTable(pc,d3.w),d0 ; arctan(y/x) = 90 - arctan(x/y)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L3132-L3133ArcTanTable: binclude "Levels/Misc/arctan.bin"Checked by claude, 2026-10-26 — read SonicKnux_DoLevelCollision and GetArcTan at the pinned commit; `od -An -tx1 -j 240 -N 17 Levels/Misc/arctan.bin` at the pin gives `$1F` for entries 240 to 249 and `$20` from entry 250 to 256With both speeds 0, GetArcTan returns $40, the down check.
Moving mostly down or up, both side sensors push Sonic out of a wall and stop his X speed; moving mostly left or right, only that side's does, and it also sets his ground speed to his Y speed. Unlike Sonic 2, a push moving left or right doesn't end the checks: the ceiling and then the floor are still tested. s3k/air-wall
About: Sonic 3 & Knuckles Hitbox
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L24094-L24105bsr.w CheckLeftWallDist
tst.w d1
bpl.s loc_11F44
sub.w d1,x_pos(a0)
move.w #0,x_vel(a0) ; stop Sonic since he hit a wall
loc_11F44:
bsr.w CheckRightWallDist
tst.w d1
bpl.s loc_11F56
add.w d1,x_pos(a0)
move.w #0,x_vel(a0) ; stop Sonic since he hit a wallskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24203-L24212Player_HitLeftWall:
bsr.w CheckLeftWallDist
tst.w d1
bpl.s Player_HitCeiling ; branch if distance is positive (not inside wall)
sub.w d1,x_pos(a0)
move.w #0,x_vel(a0) ; stop Sonic since he hit a wall
move.w y_vel(a0),ground_vel(a0)
Player_HitCeiling:
bsr.w sub_11FEEskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24312-L24321loc_12102:
bsr.w CheckRightWallDist
tst.w d1
bpl.s loc_1211A
add.w d1,x_pos(a0)
move.w #0,x_vel(a0)
move.w y_vel(a0),ground_vel(a0)
loc_1211A:
bsr.w sub_11FEEs2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38012-L38019Sonic_HitLeftWall:
bsr.w CheckLeftWallDist
tst.w d1
bpl.s Sonic_HitCeiling ; branch if distance is positive (not inside wall)
sub.w d1,x_pos(a0)
move.w #0,x_vel(a0) ; stop Sonic since he hit a wall
move.w y_vel(a0),inertia(a0)
rtss2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38106-L38113Sonic_HitRightWall:
bsr.w CheckRightWallDist
tst.w d1
bpl.s Sonic_HitCeiling2
add.w d1,x_pos(a0)
move.w #0,x_vel(a0) ; stop Sonic since he hit a wall
move.w y_vel(a0),inertia(a0)
rtsskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · s3.asm#L21730-L21743 — Sonic 3 alone's Player_HitLeftWall, falling through to the ceiling checkbsr.w CheckLeftWallDist
…
move.w y_vel(a0),ground_vel(a0)
Player_HitCeiling:Checked by claude, 2026-10-26 — read SonicKnux_DoLevelCollision, Player_HitLeftWall, and loc_12102 at the pinned commit, s3.asm's Player_HitLeftWall, and Sonic 2's Sonic_HitLeftWall and Sonic_HitRightWalls3.asm's Player_HitLeftWall is the same, so Sonic 3 alone changed this already.
Sonic's head sensors are read in the air only while he moves mostly up, left, or right. Inside a ceiling he moves down by the depth; moving up under a flat ceiling his Y speed becomes 0, moving sideways only if he was rising, and the floor isn't tested that frame. Moving mostly left, a head 20 px or more inside a ceiling is instead pushed off a wall on his right, a check Sonic 2 lacks. s3k/air-ceiling
About: Sonic 3 & Knuckles Hitbox
Value
{
"tall": 20
}sonic3k.asm#L24211-L24240Player_HitCeiling:
bsr.w sub_11FEE
…
neg.w d1
cmpi.w #$14,d1
bhs.s loc_12054
…
add.w d1,y_pos(a0)
tst.w y_vel(a0)
…
move.w #0,y_vel(a0) ; stop Sonic in y since he hit a ceiling
…
…skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24283-L24297bsr.w sub_11FEE
…
sub.w d1,y_pos(a0)
…
andi.b #$40,d0
bne.s loc_120EA
move.w #0,y_vel(a0) ; stop Sonic in y since he hit a ceilingskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24321-L24335bsr.w sub_11FEE
…
sub.w d1,y_pos(a0)
tst.w y_vel(a0)
…
move.w #0,y_vel(a0)
…
rtss2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38022-L38029Sonic_HitCeiling:
bsr.w Sonic_CheckCeiling
…
sub.w d1,y_pos(a0)
…
move.w #0,y_vel(a0) ; stop Sonic in y since he hit a ceilingChecked by claude, 2026-10-26 — read Player_HitCeiling, Player_HitCeilingAndWalls, and loc_12102 at the pinned commit and Sonic 2's Sonic_HitCeilingA ceiling block whose angle is steep lands him on it instead (loc_120EA); the lab's full block has the flat angle.
Falling mostly down, Sonic lands when a floor sensor is inside the ground unless both are deeper than his Y speed's whole pixels plus 8, and moves up by the deeper one's depth; moving mostly left or right and not rising, any depth lands him; rising, he doesn't land, except in a wind tunnel. s3k/landing-depth
About: Sonic 3 & Knuckles Jump
Value: 8 px
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L24108-L24126bsr.w sub_11FD6
…
bpl.s locret_11FD4
move.b y_vel(a0),d2
addq.b #8,d2
neg.b d2
cmp.b d2,d1
bge.s loc_11F6E
cmp.b d2,d0
blt.s locret_11FD4
…
add.w d1,y_pos(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24243-L24261loc_12068:
…
tst.w y_vel(a0)
bmi.s locret_1209C
…
bsr.w sub_11FD6
…
bpl.s locret_1209C
…
add.w d1,y_pos(a0)
…
move.w #0,y_vel(a0)
…Checked by claude, 2026-10-26 — read SonicKnux_DoLevelCollision and loc_12068 at the pinned commit; sub_11FD6 returns the nearer sensor's distance in d1 and the other's in d0The test compares bytes.
Standing on flat ground, not on an object, not Super or Hyper, with no move_lock or infinite-inertia flag, with ground speed 0 after the left and right steps, and the floor under his center 12 px or more below his feet, Sonic turns to face right if last frame's right floor sensor met no block, or else left if the left one met none, and balances; otherwise Down crouches. Sonic 2 turned him only if the floor 6 px back the other way was also that far down. s3k/balance
About: Sonic 3 & Knuckles Hitbox
Value: 12 px
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L22480-L22490bne.w loc_112EA ; if yes, branch
tst.w ground_vel(a0) ; is Sonic moving?
…
btst #Status_OnObj,status(a0)
beq.w Sonic_Balanceskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22566-L22576Sonic_Balance:
move.w x_pos(a0),d3
bsr.w ChooseChkFloorEdge
cmpi.w #$C,d1
blt.w loc_11276
tst.b (Super_Sonic_Knux_flag).w ; is Sonic Super/Hyper?
bne.w loc_11250 ; if so, branch
cmpi.b #3,next_tilt(a0)
bne.s loc_111F6
btst #Status_Facing,status(a0)
bne.s loc_111CEskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22587-L22591loc_111CE:
…
bclr #Status_Facing,status(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22602-L22618loc_111F6:
cmpi.b #3,tilt(a0)
bne.s loc_11276
btst #Status_Facing,status(a0)
beq.s loc_11228
…
loc_11228:
bset #Status_Facing,status(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22035-L22036move.b (Primary_Angle).w,next_tilt(a0)
move.b (Secondary_Angle).w,tilt(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36717-L36724+ move.b #AniIDSonAni_Balance3,anim(a0) ; set Sonic's animation to "Balance3"
…
subq.w #6,d3
jsr (ChkFloorEdge_Part2).l
cmpi.w #$C,d1
blt.w Obj01_ResetScr
…
bclr #status.player.x_flip,status(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22467-L22470tst.b status_secondary(a0) ; is bit 7 set? (Infinite inertia) bmi.w loc_11332 ; if so, branch tst.w move_lock(a0) bne.w loc_112EAskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · s3.asm#L20343-L20347 — Sonic 3 alone's Sonic_Balance turns him before its 6 px check tooloc_1228A:
…
bclr #Status_Facing,status(a0)Checked by claude, 2026-10-26 — read Sonic_Move's first lines and standstill branch and Sonic_Balance at the pinned commit (facing right on a right edge he keeps facing right; facing left, `bclr` turns him), s3.asm's Sonic_Balance, and Sonic 2's Sonic_BalancePlayer_AnglePos writes 3 to both angle buffers and a sensor that meets a block overwrites its own; Sonic_Control copies them to next_tilt and tilt. The edge check here runs on the frame's start position.
Cited by: Sonic 3 & Knuckles
Standing or rolling on flat ground with normal gravity, a jump press goes through only when the head sensors find 6 or more clear pixel rows above them; refused, the rest of the ground frame runs. On slopes and walls, and with reverse gravity, the sensors used turn with the angle. s3k/jump-headroom
About: Sonic 3 & Knuckles Jump
Value: 6 px
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L23329-L23341tst.b (Reverse_gravity_flag).w
…
bsr.w CalcRoomOverHead
…
cmpi.w #6,d1 ; does Sonic have enough room to jump?
blt.w locret_118B2 ; if not, branchskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L19855-L19862move.b d0,(Primary_Angle).w
move.b d0,(Secondary_Angle).w
…
cmpi.b #$80,d0
beq.w Sonic_CheckCeilingChecked by claude, 2026-10-26 — read Sonic_Jump and CalcRoomOverHead at the pinned commitCalcRoomOverHead writes $80 to both angle buffers, which the balance check reads the next frame.
On the ground, while Background_collision_flag is clear, nothing but a jump press looks above Sonic: walking or rolling under a block that overlaps him, he stays on the ground. s3k/ground-no-ceiling
About: Sonic 3 & Knuckles Hitbox
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L22328-L22337Sonic_MdNormal:
bsr.w SonicKnux_Spindash
bsr.w Sonic_Jump
bsr.w Player_SlopeResist
bsr.w Sonic_Move
bsr.w SonicKnux_Roll
bsr.w Player_LevelBound
jsr (MoveSprite_TestGravity2).l
bsr.w Call_Player_AnglePos
bsr.w Player_SlopeRepelskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22402-L22411Sonic_MdRoll:
tst.b spin_dash_flag(a0)
bne.s loc_10FEA
bsr.w Sonic_Jump
loc_10FEA:
bsr.w Player_RollRepel
bsr.w Sonic_RollSpeed
bsr.w Player_LevelBound
jsr (MoveSprite_TestGravity2).lChecked by claude, 2026-10-26 — read Sonic_MdNormal and Sonic_MdRoll and the routines they call at the pinned commit; only Sonic_Jump calls CalcRoomOverHead, and the wall checks after Player_SlopeRepel run only with Background_collision_flag set, which Angel Island Act 1 leaves clear in the labSonic dies once his Y position is more than 224 px below the level's bottom boundary, unless the level has set Disable_death_plane; with reverse gravity it is the top boundary instead. In Angel Island Act 1, run in MAME, every one of 18 falls into a pit killed him on the first frame after his center passed the boundary plus 224 px; the other branches are read in the code only. s3k/bottom-boundary
About: Sonic 3 & Knuckles Fall damage
Value: 224 px
Verifiedskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L23207-L23243Player_LevelBound:
…
move.w (Camera_min_X_pos).w,d0
addi.w #$10,d0
…
move.w (Camera_max_X_pos).w,d0
addi.w #$128,d0
…
Player_Boundary_CheckBottom:
tst.b (Disable_death_plane).w
bne.s locret_11720
…
…Checked by claude, 2026-10-26 — read Player_LevelBound at the pinned commit (`cmp.w y_pos(a0),d0` then `blt.s Player_Boundary_Bottom`: a death once y_pos exceeds Camera_max_Y_pos plus 224). The probe runs Sonic & Knuckles + Sonic The Hedgehog 3 (USA) in MAME 0.289 on the stages of stage-rule, built in Angel Island Act 1 with the bottom boundary held at `$300`; on 2026-10-26 18 of its cases ended in a fall into a pit, and each death came on the first frame after his center passed row 992 (`$300` + 224), the row the probe computes from the boundary it reads in RAM and this claim's 224Probe: probes/lab/s3k-conformance.mjsIt runs in every mode before the move, so the death comes the frame after his center passes the line. The same routine stops him at the camera's left limit plus 16 px and its right limit plus 296.
A frame-by-frame model of Sonic_MdNormal, Sonic_MdRoll, Sonic_MdJump, and SonicKnux_Spindash on flat ground, with the insta-shield, matches Sonic 3 & Knuckles itself, run in MAME with Sonic alone on a floor built of Angel Island Act 1's blocks, on every frame of the movement lab's flat-ground presets, stress scripts, and spin-dash and roll scripts and of 100 random input scripts. s3k/movement-rule
About: Sonic 3 & Knuckles Running
Value
{
"ground": {
"accel": 12,
"friction": 12,
"decel": 128,
"top": 1536,
"flip": 128
},
"roll": {
"start": 256,
"stop": 128,
"friction": 6,
"decel": 32,
"flip": 128,
"cap": 4096
},
"air": {
"accel": 24,
"top": 1536,
"dragShift": 5,
"dragFrom": -1024
},
"jump": {
"speed": -1664,
"releaseCap": -1024,
"gravity": 56,
"rollShift": 5
},
"spindash": {
"charge": 512,
"max": 2048,
"decayShift": 5
}
}sonic3k.asm#L22328-L22337 — the standing mode and its orderSonic_MdNormal:
bsr.w SonicKnux_Spindash
bsr.w Sonic_Jump
bsr.w Player_SlopeResist
bsr.w Sonic_Move
bsr.w SonicKnux_Roll
bsr.w Player_LevelBound
jsr (MoveSprite_TestGravity2).l
bsr.w Call_Player_AnglePos
bsr.w Player_SlopeRepelskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22385-L22451 — Sonic_MdAir, Sonic_MdRoll, Sonic_MdJumpskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22463-L22951 — Sonic_Move with friction, the speeds from the ground speed, the wall ahead, and sub_113F6 and sub_11482skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22959-L23196 — Sonic_RollSpeed and its left and right steps, Sonic_ChgJumpDir with the air dragskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23258-L23438 — SonicKnux_Roll, Sonic_Jump, Sonic_JumpHeight, the start of Sonic_ShieldMovesskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23679-L23822 — SonicKnux_Spindashskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24075-L24160 — SonicKnux_DoLevelCollision's down branchskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24375-L24426 — Player_TouchFloorskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L36108-L36110 — MoveSprite_TestGravity is MoveSprite with normal gravityMoveSprite_TestGravity:
tst.b (Reverse_gravity_flag).w
beq.s MoveSpriteskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L1324-L1328 — Poll_Controller, a press per buttonmove.b (a0),d1 ; Get press button data
eor.b d0,d1 ; Toggle off buttons that are being held
move.b d0,(a0)+ ; Put raw controller input (for held buttons) in F604/F606
and.b d0,d1
move.b d1,(a0)+ ; Put pressed controller input in F605/F607skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22006move.w (Ctrl_1).w,(Ctrl_1_logical).w ; copy new held buttons, to enable joypad controlChecked by claude, 2026-10-26 — read the four mode routines, the spin dash, and every subroutine they call for flat ground, and Poll_Controller, at the pinned commit. The probe runs Sonic & Knuckles + Sonic The Hedgehog 3 (USA) in MAME 0.289 through the movement lab's ROM check (probes/lab/conformance.mjs): Angel Island Act 1 after its intro, Sonic alone (the save select's no-save slot with Sonic picked, no Tails object), the level's objects cleared every frame, Sonic placed standing on a 4096 px floor of the act's block 1, input written into the joypad bytes each game frame, and the model run on the same input. On 2026-10-26 it compared 140 scripts (23 presets, 9 stress scripts, 8 of the entry's own with spin dashes, a slow crouch, an unroll, and an insta-shield after a roll jump, and 100 random ones of 300 frames), 33,965 frames, on X and Y in 16.16, X, Y, and ground speed, airborne, curled, and facing, and all matched, on the first run of the model written from the codeProbe: probes/lab/s3k-conformance.mjsOne frame, out of water, g the ground speed and vx, vy the X and Y speeds in 1/256 px/frame, positions 16.16. Down is +y. A, B, or C going down this frame is a press, even with another held. Standing: crouching (animation 8), a press starts the spin dash, and while it charges each frame takes 1/32 of the counter and a press adds $200 (to $800); letting go of Down releases it (curl, Y + 5, g from spindash, negated facing left). These frames, and the start, don't move him. Otherwise a press sets vy −= $680, curls him (Y + 5) unless he is rolling (then the roll-jump flag), and ends the frame. Then Left, then Right: Left with g ≤ 0 faces left and takes $C, a speed past −$600 kept; Left with g > 0 takes $80 (to −$80 if it crosses), skidding (facing right) if the result's high byte is ≥ 4. Right mirrors it, skidding if the result with its low byte cleared is ≤ −$400. With g 0 after them: balance or, with Down, crouch. With neither held, friction $C toward 0. vx = g; with Down and neither Left nor Right, |g| ≥ $100 curls him into a roll (Y + 5), slower crouches. Rolling: Left or Right brake by $20 against the motion (to ∓$80 if it crosses), friction 6, then under $80 he stands up (Y − 5) with g kept; vx is g clamped to ±$1000. Then X += vx and the floor sensors snap him, or he walks off. Air: a jump rising faster than $400 with no button held is cut to −$400; once no faster, the first press clears the roll-jump flag (the insta-shield). Unless roll-jumping, Left and Right add $18 and face that way, a speed past $600 kept. If −$400 ≤ vy < 0, vx −= vx >> 5. X += vx, Y += vy, vy += $38; then the level checks, and landing makes g = vx, vy = 0, and uncurls him (Y − 5 unless roll-jumping). The values: ground-speeds, ground-friction, skid-threshold, roll-start, unroll, roll-speeds, air-keep-speed, air-drag-same, jump-speed, release-cap, gravity, takeoff, spindash, spindash-controls. Slopes, water, objects, and the camera are outside the model.
Cited by: Sonic the Hedgehog
On stages built in Angel Island Act 1 from the act's full 16 × 16 block, the movement lab's Sonic 3 & Knuckles model matches the game itself, run in MAME, on every frame of 100 random stages with random input scripts, three in four with floating blocks, and of the lab's stage presets, until a fall into a pit kills him at the bottom boundary or he nears the end of the built floor. s3k/stage-rule
About: Sonic 3 & Knuckles Hitbox
Value
{
"stages": 100,
"frames": 300
}sonic3k.asm#L18764-L18900 — Player_AnglePos, Player_Angleskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L19223-L19340 — FindFloorFindFloor:
lea (Find_Tile_FG).l,a5
tst.b (Background_collision_flag).w
beq.s sub_F264skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L19509-L19700 — FindWallskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L19715-L19900 — sub_F61C, CalcRoomOverHead, Sonic_CheckFloorskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L20016-L20030 — ChkFloorEdge_Part2skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L20224-L20300 — CheckRightWallDist, Sonic_CheckCeilingskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L20541-L20555 — CheckLeftWallDistskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22566-L22650 — Sonic_Balanceskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24075-L24360 — SonicKnux_DoLevelCollision's four directionsskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L3075-L3133 — GetArcTan and its tableChecked by claude, 2026-10-26 — read the routines above at the pinned commit. The probe runs Sonic & Knuckles + Sonic The Hedgehog 3 (USA) in MAME 0.289 through the movement lab's ROM check (probes/lab/conformance.mjs, s3k-stage.mjs): each stage built in Angel Island Act 1's foreground rows 6 and 7 (y 768 to 1023, every other row emptied) from the act's block 1, after checking in RAM that block 1's collision index is s3k/collision-blocks' on both paths, the level's objects cleared every frame, Sonic placed standing at the stage's start, input written into the joypad bytes each game frame, and the model run on the same stage and input. The random stages are sonic-movement.mjs's (randomStages, seed 7), the ones Sonic the Hedgehog's check runs. On 2026-10-26 it compared 115 cases, 100 random stages of 300 frames and 15 stage-preset panes, 28,505 frames, on X and Y in 16.16, X, Y, and ground speed, airborne, curled, and facing, and all matched. 18 ended when the bottom boundary killed him (bottom-boundary); 3 stopped where he came within 48 px of the built floor's ends, x 0 and 4096Probe: probes/lab/s3k-conformance.mjsThe stages are Sonic the Hedgehog's layouts (s1disasm/stage-rule), so the two games' runs meet the same walls, ledges, and ceilings, and the camera's limits the probe writes each frame (left 0, right $4000, bottom $300) keep the level's own from moving.
On flat ground, Sonic 3 & Knuckles changes when Sonic rolls and stands: Down curls him only from $100, not Sonic 2's $80, and, off objects, crouches him while he is still moving slower, where Sonic 2 crouched only from a standstill; a roll stands up under $80 with that speed kept, where Sonic 2's rolls on to 0 (both outside pinball mode). In the air he keeps a speed above the top while steering, where Sonic 2 pulled him down to it; a second press after a roll jump gives back steering, which Sonic 2's jump never did; a wall push moving sideways no longer ends the frame's checks; and a head 20 px into a ceiling moving left is pushed off a right wall instead, a check Sonic 2 lacks. Standing over a drop, not Super, he turns toward it at once, where Sonic 2 turned only if the floor 6 px the other way was also low. s3k/movement-differs-from-sonic-the-hedgehog-2
About: Sonic 3 & Knuckles Running
Codeskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) ·sonic3k.asm#L23271-L23275 — roll-startcmpi.w #$100,d0 ; is Sonic moving at $100 speed or faster?
bhs.s Player_ChkRoll ; if so, branch
btst #Status_OnObj,status(a0)
bne.s locret_1177E
move.b #8,anim(a0) ; enter ducking animations2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37345-L37347mvabs.w inertia(a0),d0
cmpi.w #$80,d0 ; is Sonic moving at $80 speed or faster?
blo.s Obj01_NoRoll ; if not, branchs2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36788-L36791 — Sonic 2 crouches only from Sonic_Move's standstill branchSonic_Duck:
btst #button_down,(Ctrl_1_Held_Logical).w ; is down being pressed?
beq.s Obj01_ResetScr ; if not, branch
move.b #AniIDSonAni_Duck,anim(a0) ; use "ducking" animationskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23012-L23016 — unrollcmpi.w #$80,d0
bhs.s loc_115C6
tst.b spin_dash_flag(a0)
bne.s loc_115B4
bclr #Status_Roll,status(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37097-L37102Sonic_CheckRollStop:
tst.w inertia(a0)
bne.s Obj01_Roll_ResetScr
…
bclr #status.player.rolling,status(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23139-L23142 — air-keep-speedadd.w d5,d0 ; remove this frame's acceleration change
cmp.w d1,d0 ; compare speed with top speed
ble.s loc_11682 ; if speed was already greater than the maximum, branch
move.w d1,d0s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37214-L37219sub.w d5,d0 ; add acceleration to the left
move.w d6,d1
neg.w d1
cmp.w d1,d0 ; compare new speed with top speed
bgt.s + ; if new speed is less than the maximum, branch
move.w d1,d0 ; limit speed in air going left, even if Sonic was already going faster (speed limit/cap)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L23438 — insta-shield-motionbclr #Status_RollJump,status(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37461-L37479 — Sonic 2's routine for the rise touches no roll-jump flag; landing clears it (roll-jump-lock)Sonic_JumpHeight:
tst.b jumping(a0) ; is Sonic jumping?
…
move.w d1,y_vel(a0) ; immediately reduce Sonic's upward speed to d1
+
tst.b y_vel(a0) ; is Sonic exactly at the height of his jump?
beq.s Sonic_CheckGoSuper ; if yes, test for turning into Super Sonic
rtsskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24207-L24212 — air-wall, falling through to the ceiling checksub.w d1,x_pos(a0)
move.w #0,x_vel(a0) ; stop Sonic since he hit a wall
move.w y_vel(a0),ground_vel(a0)
Player_HitCeiling:
bsr.w sub_11FEEs2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38016-L38019sub.w d1,x_pos(a0)
move.w #0,x_vel(a0) ; stop Sonic since he hit a wall
move.w y_vel(a0),inertia(a0)
rtsskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L24215-L24217 — air-ceilingneg.w d1
cmpi.w #$14,d1
bhs.s loc_12054s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38022-L38029 — Sonic 2's ceiling check after a left wall, with no depth testSonic_HitCeiling:
bsr.w Sonic_CheckCeiling
…
sub.w d1,y_pos(a0)
…
move.w #0,y_vel(a0) ; stop Sonic in y since he hit a ceilingskdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · sonic3k.asm#L22587-L22591 — balanceloc_111CE:
…
bclr #Status_Facing,status(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36717-L36724+ move.b #AniIDSonAni_Balance3,anim(a0) ; set Sonic's animation to "Balance3"
…
subq.w #6,d3
…
cmpi.w #$C,d1
blt.w Obj01_ResetScr
…
bclr #status.player.x_flip,status(a0)skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · s3.asm#L21730-L21743 — Sonic 3 alone's air wall falls through to the same ceiling check, with the 20 px rulebsr.w CheckLeftWallDist
…
move.w y_vel(a0),ground_vel(a0)
Player_HitCeiling:
…
cmpi.w #$14,d1skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · s3.asm#L21115 — and its shield press clears the roll-jump flagbclr #Status_RollJump,status(a0)Checked by claude, 2026-10-26 — compared Sonic 3 & Knuckles's Sonic_MdNormal, Sonic_MdRoll, Sonic_MdJump, and the routines they call for flat ground with Sonic 2's at both pinned commits (s2disasm at main's pin); the claims named in each note quote both sides in fullRules that stayed, each in a claim that quotes Sonic 2's lines beside these: ground-speeds, ground-friction, skid-threshold, roll-speeds, spindash-controls, collision-box, walk-off, and roll-jump-size. Off flat ground there is more: a standing player slides on slopes (slope-standing) and slips rather than falls off steep ones (slope-slip). Sonic 3 alone (s3.asm) already has the air-wall and ceiling change (air-wall, and the s3.asm lines here), the insta-shield's unlock (roll-jump-lock), the turn on a ledge (balance), and the air steering (air-keep-speed), but keeps Sonic 2's roll start at $80, its unroll at 0, and its standstill-only crouch (roll-start, unroll).
Cited by: Sonic the Hedgehog, Sonic the Hedgehog 2
Built at the pinned commit with its own build scripts, as they stand (FixBugs = 0, Sonic 3 Complete off), skdisasm reproduces the No-Intro Sonic & Knuckles + Sonic The Hedgehog 3 (USA) ROM byte for byte: buildSK.lua (sonic3k.asm) gives its first 2 MB, the code the locked-on game runs, and buildS3.lua (s3.asm) its last 2 MB. s3k/rom-matches-build
Value
{
"sk": "88d6499d874dcb5721ff58d76fe1b9af811192e3",
"s3": "75e9c4705259d84112b3e697a6c00a0813d47d71"
}sonic3k.asm#L38FixBugs = 0skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · s3.asm#L25FixBugs = 0skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · buildSK.lua#L28common.build_rom_and_handle_failure("sonic3k", "skbuilt", "-D Sonic3_Complete=0",skdisasm: Sonic 3 & Knuckles disassembly (sonicretro/skdisasm) · buildS3.lua#L10common.build_rom_and_handle_failure("s3", "s3built", "",Sonic & Knuckles + Sonic The Hedgehog 3 (USA) · 0x0-0x3FFFFF — the whole file; the probe compares each build with its halfChecked by claude, 2026-10-26 — ran buildSK.lua and buildS3.lua on a git archive of the pinned commit (lua 5.4) and compared each build with its half of the ROM with cmp; read the four lines quotedProbe: probes/lab/s3k-build.mjsSo every s3k claim's code is what the cartridge holds, not a revision near it (contrast Sonic 2, s2disasm/rom-matches-revision-2). The MD5s the repository's chkbitperfect.lua expects of the two builds are these halves' (sonic-3-and-knuckles-genesis-rom).