Claim set · data/claims/s2disasm.yaml

Sonic the Hedgehog 2 (sonicretro/s2disasm)

Game: Sonic the Hedgehog 2.

Sonic_Jump takes off at $680 subpixels per frame (6.5 px/frame), $800 as Super Sonic, and $380 underwater, the same take-off as Sonic 1. s2disasm/jump-speed

About: Sonic the Hedgehog 2 Jump

Value: 1664 1/256 px/frame

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37400-L37407move.w #$680,d2 … move.w #$800,d2 ; set higher jump speed if super … move.w #$380,d2 ; set lower jump speed if underwaters2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37409-L37418subi.b #$40,d0 … jsr (CalcSine).l … add.w d0,y_vel(a0) ; make Sonic jump (in Y)s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L8son_jumpspeed: equ $680 ; Sonic's jump speedChecked by claude, 2026-10-21 — read Sonic_Jump at the pinned commit

As in Sonic 1 (s1disasm/jump-speed, s1disasm/jump-vector), the speed goes along the ground's normal.

Cited by: Jump, Sonic the Hedgehog, Sonic the Hedgehog 2

ObjectMoveAndFall adds $38 subpixels/frame to Sonic's downward speed each airborne frame, and underwater the air modes take $28 back, leaving $10. s2disasm/gravity

About: Sonic the Hedgehog 2 Gravity

Value: 56 1/256 px/frame²

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L30216-L30217move.w y_vel(a0),d0 ; load y speed … addi.w #$38,y_vel(a0) ; increase vertical speed (apply gravity)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36575-L36582jsr (ObjectMoveAndFall).l … subi.w #$28,y_vel(a0) ; reduce gravity by $28 ($38-$28=$10)Checked by claude, 2026-10-21 — read ObjectMoveAndFall and Obj01_MdJump at the pinned commit

Cited by: Gravity, Sonic the Hedgehog 2

With no jump button held, Sonic_JumpHeight cuts a rise faster than 4 px/frame to exactly -$400 (-$200 underwater), as in Sonic 1. s2disasm/release-cap

About: Sonic the Hedgehog 2 Variable jump height

Value: -1024 1/256 px/frame

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37465-L37475move.w #-$400,d1 … move.w #-$200,d1 … cmp.w y_vel(a0),d1 ; is Sonic going up faster than d1? … move.w d1,y_vel(a0) ; immediately reduce Sonic's upward speed to d1s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1276-L1288move.w #-$400,d1 ; set max jump height … move.w #-$200,d1 ; set underwater jump height … move.w d1,obVelY(a0) ; otherwise, cap Sonic's maximum jump heightChecked by claude, 2026-10-21 — read Sonic_JumpHeight at the pinned commit

Cited by: Sonic the Hedgehog, Sonic the Hedgehog 2, Variable jump height

Obj01_Init sets Sonic's top speed to $600, his acceleration to $C, and his deceleration to $80 subpixels, Sonic 1's values; speed shoes and Super Sonic swap in others. s2disasm/ground-speeds

About: Sonic the Hedgehog 2 Running

Value
{
 "top": 1536,
 "accel": 12,
 "decel": 128,
 "superTop": 2560,
 "superAccel": 48,
 "superDecel": 256
}
Codes2disasm: 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#L36364-L36366move.w #$A00,(Sonic_top_speed).w ; restore Super Sonic's top speeds1disasm: 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 decelerationChecked by claude, 2026-10-21 — read Obj01_Init and Obj01_ChkShoes at the pinned commit

Cited by: Running, Sonic the Hedgehog, Sonic the Hedgehog 2

Holding forward no longer pulls a faster Sonic down to the top speed on the ground; Sonic_MoveRight undoes the frame's acceleration and keeps the speed he had. s2disasm/forward-cap-kept

About: Sonic the Hedgehog 2 Running

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36998-L37004add.w d5,d0 ; add acceleration to the right … sub.w d5,d0 ; remove this frame's acceleration change … bge.s + ; if speed was already greater than the maximum, branch … move.w d6,d0 ; limit speed on ground going rights1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L716-L719 — Sonic 1 adds, compares, and caps, with no check of the speed he started the frame with (s1disasm/forward-cap)add.w d5,d0 ; add acceleration to current ground speed cmp.w d6,d0 ; is new speed above max speed? blt.s .nocap ; if not, branch move.w d6,d0 ; cap Sonic's ground speedChecked by claude, 2026-10-21 — read Sonic_MoveRight in both disassemblies at their pinned commits

Sonic_MoveLeft does the same the other way. In the air Sonic 2 still caps (s2disasm/air-accel).

Cited by: Running, Sonic the Hedgehog, Sonic the Hedgehog 2

In the air Sonic_ChgJumpDir adds twice the acceleration, $18, either way and still caps at the top speed even when Sonic was already faster, as Sonic 1 does. s2disasm/air-accel

About: Sonic the Hedgehog 2 Air control

Value: 24 1/256 px/frame²

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37204-L37228move.w (Sonic_acceleration).w,d5 … asl.w #1,d5 … move.w d1,d0 ; limit speed in air going left, even if Sonic was already going faster (speed limit/cap) … move.w d6,d0 ; limit speed in air going right, even if Sonic was already going faster (speed limit/cap)s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L940-L966asl.w #1,d5 … move.w d1,d0 ; cap leftward X-speed to maximum … move.w d6,d0 ; cap rightward X-speed to maximumChecked by claude, 2026-10-21 — read Sonic_ChgJumpDir at the pinned commit

Cited by: Air control, Sonic the Hedgehog 2

Sonic 2 keeps Sonic 1's air drag; Sonic_JumpPeakDecelerate is instruction for instruction Sonic 1's Sonic_AirDrag, taking X speed shifted right 5 bits while Y speed is from −$400 to −1. s2disasm/air-drag-same

About: Sonic the Hedgehog 2 Air control

Value
{
 "shift": 5,
 "vyFrom": -1024,
 "vyTo": -1
}
Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37241-L37264cmpi.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-L1013cmpi.w #-$400,obVelY(a0) ; is Sonic moving faster than -$400 upwards? … asr.w #5,d1 ; divide that copy by $20Checked by claude, 2026-10-21 — diffed the two routines line by line at their pinned commits; the opcodes, operands, and branches match and only the labels differ

The camera reset ahead of it (Obj01_Jump_ResetScr, S1 Sonic_RollJumpLock) matches too, and a roll jump still skips the d-pad but not the drag.

Cited by: Air control, Sonic the Hedgehog 2

Sonic 2 has no grace window after a ledge: only the grounded modes, Obj01_MdNormal and Obj01_MdRoll, call Sonic_Jump. s2disasm/no-coyote-time

About: Sonic the Hedgehog 2 Coyote time

Value: 0 frames

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36514-L36516Obj01_MdNormal: bsr.w Sonic_CheckSpindash ; check for spindash bsr.w Sonic_Jump ; check for jumpings2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36556-L36559Obj01_MdRoll: … bsr.w Sonic_Jump ; check for jumpings2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36539-L36543 — Obj01_MdAir runs Sonic_JumpHeight and Sonic_ChgJumpDir, never Sonic_JumpChecked by claude, 2026-10-21 — searched s2.asm for every call to Sonic_Jump (two; SSSonic_Jump is the special stage's own)

Cited by: Coyote time, Sonic the Hedgehog 2

The spin dash starts when a jump button is pressed while Sonic shows his ducking animation; Sonic_CheckSpindash runs before Sonic_Jump and skips the rest of the frame. s2disasm/spindash-start

About: Sonic the Hedgehog 2 Spin dash

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37600-L37613cmpi.b #AniIDSonAni_Duck,anim(a0) … andi.b #button_B_mask|button_C_mask|button_A_mask,d0 … addq.l #4,sp … move.b #1,spindash_flag(a0) move.w #0,spindash_counter(a0)Checked by claude, 2026-10-21 — read Sonic_CheckSpindash at the pinned commit

Cited by: Sonic the Hedgehog, Sonic the Hedgehog 2, Spin dash

Each jump press while charging adds $200 to the spin dash counter, capped at $800, and each frame the counter loses 1/32 of itself. s2disasm/spindash-charge

About: Sonic the Hedgehog 2 Spin dash

Value
{
 "rev": 512,
 "cap": 2048,
 "decayShift": 5
}
Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37711-L37729move.w spindash_counter(a0),d0 lsr.w #5,d0 sub.w d0,spindash_counter(a0) … addi.w #$200,spindash_counter(a0) cmpi.w #$800,spindash_counter(a0) … move.w #$800,spindash_counter(a0)Checked by claude, 2026-10-21 — read Sonic_ChargingSpindash at the pinned commit

Cited by: Sonic the Hedgehog 2, Spin dash

Releasing Down sets Sonic's ground speed from the counter's high byte, $800 plus $80 a step, from 8 px/frame uncharged to 12 at full charge (Super Sonic $B00 to $F00). s2disasm/spindash-speeds

About: Sonic the Hedgehog 2 Spin dash

Value
[
 2048,
 2176,
 2304,
 2432,
 2560,
 2688,
 2816,
 2944,
 3072
]
Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37633-L37647btst #button_down,d0 bne.w Sonic_ChargingSpindash … move.b spindash_counter(a0),d0 add.w d0,d0 move.w SpindashSpeeds(pc,d0.w),inertia(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37688-L37708SpindashSpeeds: dc.w $800 ; 0 … dc.w $C00 ; 8 … SpindashSpeedsSuper: dc.w $B00 ; 0Checked by claude, 2026-10-21 — read Sonic_UpdateSpindash and both tables at the pinned commit

move.b reads the counter word's high byte, so a counter of $800 picks entry 8. A release right after the first press gives $800. Each press adds $200, two table steps, but the counter leaks 1/32 of itself every frame, so presses spaced out in time stay below $800 and the top speed of $C00 needs fast pressing.

Cited by: Sonic the Hedgehog 2, Spin dash

On a slope Sonic's ground speed gains the sine of the angle times $20 a frame while he runs and $50 while he rolls downhill (a quarter of that uphill), with nothing while he stands; the code matches Sonic 1's. s2disasm/slope-factors

About: Sonic the Hedgehog 2 Slope physics

Value
{
 "walk": 32,
 "rollDown": 80,
 "rollUpShift": 2
}
Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37754-L37768move.b angle(a0),d0 jsr (CalcSine).l muls.w #$20,d0 asr.l #8,d0 tst.w inertia(a0) beq.s return_1ADCAs2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37787-L37803muls.w #$50,d0 asr.l #8,d0 tst.w inertia(a0) bmi.s loc_1ADFC tst.w d0 bpl.s loc_1ADF6 asr.l #2,d0s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1323-L1392muls.w #$20,d0 ; multiply by resist force while walking … beq.s .return ; if he's standing still, branch … muls.w #$50,d0 ; multiply by resist force while rolling (2.5x larger than walking)Checked by claude, 2026-10-21 — read Sonic_SlopeResist and Sonic_RollRepel in both disassemblies at their pinned commits

Both skip near-flat angles (angle + $60 ≥ $C0). The rolling quarter applies whenever the push opposes the roll.

Cited by: Slope physics, Sonic the Hedgehog 2

On a wall or ceiling below $280 (2.5 px/frame) ground speed, Sonic_SlopeRepel zeroes the speed, drops Sonic off, and locks the d-pad for 30 frames, as in Sonic 1. s2disasm/slope-repel

About: Sonic the Hedgehog 2 Slope physics

Value
{
 "minSpeed": 640,
 "lock": 30
}
Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37832-L37841addi.b #$20,d0 andi.b #$C0,d0 … cmpi.w #$280,d0 … clr.w inertia(a0) bset #status.player.in_air,status(a0) move.w #$1E,move_lock(a0)s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1412-L1427cmpi.w #$280,d0 ; is Sonic's ground speed high enough?Checked by claude, 2026-10-21 — read Sonic_SlopeRepel in both disassemblies at their pinned commits

Cited by: Sonic the Hedgehog 2

Rolling, Sonic slows by half his acceleration ($6) a frame, by $20 more against the d-pad, and his X speed is clamped to $1000 (16 px/frame). s2disasm/roll-speeds

About: Sonic the Hedgehog 2 Rolling

Value
{
 "friction": 6,
 "brake": 32,
 "xCap": 4096
}
Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37057-L37059move.w (Sonic_acceleration).w,d5 asr.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-L37143cmpi.w #$1000,d1 … move.w #$1000,d1 ; limit Sonic's speed rolling rights1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L770-L773asr.w #1,d5 … move.w (v_sonspeeddec).w,d4 … asr.w #2,d4s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L863-L865cmpi.w #$1000,d1 … move.w #$1000,d1Checked by claude, 2026-10-21 — read Sonic_RollSpeed and Sonic_SetRollSpeeds at the pinned commit

Sonic 1's roll uses the same numbers (s1disasm/roll-speed).

Cited by: Rolling, Sonic the Hedgehog 2

Sonic curls into a roll when Down is held without Left or Right and his ground speed is at least $80 either way, as in Sonic 1. s2disasm/roll-start

About: Sonic the Hedgehog 2 Rolling

Value: 128 1/256 px/frame

Codes2disasm: 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? … andi.b #button_left_mask|button_right_mask,d0 ; is left/right being pressed? … btst #button_down,(Ctrl_1_Held_Logical).w ; is down being pressed?s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1155-L1161cmpi.w #$80,d0 ; is Sonic moving at $80 speed or faster? … andi.b #btnL+btnR,d0 ; is left/right being held? … btst #bitDn,(v_jpadhold2).w ; is down being held?Checked by claude, 2026-10-21 — read Sonic_Roll at the pinned commit

Cited by: Sonic the Hedgehog 2

A hit throws Sonic up at -$400 and away from the object at $200 (half both underwater), and the hurt routine's gravity is $30. s2disasm/hurt-knockback

About: Sonic the Hedgehog 2 Knockback

Value
{
 "vy": -1024,
 "vx": 512,
 "gravity": 48,
 "waterVy": -512,
 "waterVx": 256
}
Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L85677-L85690move.w #-$400,y_vel(a0) ; make Sonic bounce away from the object move.w #-$200,x_vel(a0) … blo.s Hurt_ChkSpikes ; if Sonic is left of the object, branch neg.w x_vel(a0) ; if Sonic is right of the object, reverses2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38222-L38223jsr (ObjectMove).l addi.w #$30,y_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 horizontallys1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1869addi.w #gravity-8,obVelY(a0)Checked by claude, 2026-10-21 — read HurtCharacter and Obj01_Hurt_Normal at the pinned commit

Sonic 1 uses the same speeds and the same $30 (gravity-8) hurt gravity (s1disasm/hurt-knockback).

Cited by: Knockback, Sonic the Hedgehog 2

A hit sets invulnerable_time to $78 (120) and landing sets it to 120 again; it counts down only in the control routine, so Sonic has 120 frames of blinking invulnerability after he lands. s2disasm/hurt-invulnerable

About: Sonic the Hedgehog 2 Invincibility frames

Value: 120 frames

Codes2disasm: 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#L38262-L38271btst #status.player.in_air,status(a0) bne.s return_1B1C8 … move.w #$78,invulnerable_time(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36325-L36330Sonic_Display: move.w invulnerable_time(a0),d0 … subq.w #1,invulnerable_time(a0) ; decrease invulnerability frames lsr.w #3,d0 ; check every 3 or 4 framess2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L85633-L85636tst.w invulnerable_time(a0) bne.s Touch_NoHurtChecked by claude, 2026-10-21 — read HurtCharacter, Sonic_HurtStop, Sonic_Display, and Touch_Hurt at the pinned commit

Obj01_Hurt_Normal doesn't reach Sonic_Display, so the count holds at 120 through the knockback.

Cited by: Invincibility frames, Sonic the Hedgehog 2

Hit without a shield, Sonic drops his rings, at most 32 of them as bouncing rings, and the count goes to 0; with no rings he dies. s2disasm/hurt-rings

About: Sonic the Hedgehog 2 Rings as health

Value
{
 "scatterMax": 32
}
Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L85650-L85664move.w (Ring_count).w,d0 … btst #status_secondary.shield,status_secondary(a0) bne.s Hurt_Shield tst.w d0 beq.w KillCharacter … _move.b #ObjID_LostRings,id(a1) ; load objs2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L25178-L25181moveq #32,d0 cmp.w d0,d5 ; has player over 33 rings? blo.s + ; if not, branch move.w d0,d5 ; if yes, limit to 32 ringss2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L25240-L25243move.w #0,(Ring_count).wChecked by claude, 2026-10-21 — read HurtCharacter and Obj37 (lost rings) at the pinned commit

Cited by: Rings as health, Sonic the Hedgehog 2

Sonic 2's one shield takes a hit in place of the rings; HurtCharacter skips the ring drop, clears the shield, and knocks Sonic back as usual. s2disasm/shield

About: Sonic the Hedgehog 2 Shields

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L85658-L85678btst #status_secondary.shield,status_secondary(a0) bne.s Hurt_Shield … Hurt_Shield: bclr #status_secondary.shield,status_secondary(a0) ; remove shieldChecked by claude, 2026-10-21 — read HurtCharacter at the pinned commit

Cited by: Shields, Sonic the Hedgehog 2

Against objects Sonic is a 16 px wide box whose half-height is his height radius minus 3: 32 px tall standing, 22 px curled up. s2disasm/hitbox

About: Sonic the Hedgehog 2 Hitbox

Value
{
 "w": 16,
 "h": 32,
 "rollH": 22,
 "duckH": 20
}
Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L85229-L85250subi_.w #8,d2 moveq #0,d5 move.b y_radius(a0),d5 subq.b #3,d5 sub.w d5,d3 … move.w #$10,d4 add.w d5,d5s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36226-L36227move.b #$13,y_radius(a0)s1disasm: 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; rolling sets the radius to `$E` (L37368)

With fixBugs off the ducking box (20 px) applies only on one mapping frame, $4D, as the disassembly's comment says. Sonic 1's box has the same shape (s1disasm/hitbox).

Cited by: Hitbox

In the No-Intro Sonic The Hedgehog 2 (World) (Rev B) ROM, Sonic's object (Obj01) is the code s2disasm builds with gameRevision = 2, item for item, apart from absolute addresses, RAM variables at other addresses, branch displacements, and offset table entries, and less one jump table entry the build has after Sonic_HurtStop; the collision routines it calls (AnglePos up to Obj79), CalcSine and CalcAngle with their tables, ObjectMoveAndFall, ObjectMove, and ReadJoypads are that build's bytes. The build as a whole is not this ROM byte for byte. s2disasm/rom-matches-revision-2

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L20 — the default build is revision 1gameRevision = 1s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L40removeJmpTos = 0|(gameRevision>=2)|allOptimizationss2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38274-L38280 — the jump table entry the revision 2 build has and the ROM lacksreturn_1B1C8: rts if removeJmpTos JmpTo_KillCharacter ; JmpTo jmp (KillCharacter).l endifSonic The Hedgehog 2 (World) (Rev B) · 0x19EBE-0x1B817 — Obj01, 62 bytes before its place in the revision 2 build at its start and 68 at its endSonic The Hedgehog 2 (World) (Rev B) · 0x1E1C6-0x1F043 — AnglePos up to Obj79, 62 bytes before its place in the revision 2 buildChecked by claude, 2026-10-26 — read s2.asm's revision switches at the pinned commit (`gameRevision`, and `removeJmpTos`, on from revision 2). The probe builds s2disasm at the pin with gameRevision 2, and 1 for contrast, with the repository's own build.lua (listing on), and lines each build's code up with the ROM item by item from the listing. On 2026-10-26, against revision 2: Obj01's 1,683 items (build 0x19EFC-0x1B85B) have 68 that differ, 51 ROM addresses, 6 RAM addresses, 8 branch displacements and 3 offset table entries, and the build's `jmp (KillCharacter).l` after `return_1B1C8` isn't in the ROM, so the ROM's Obj01 sits 62 bytes before the build's up to there and 68 after; the collision routines (1,248 items), CalcSine and CalcAngle, ObjectMoveAndFall and ObjectMove, and ReadJoypads are byte for byte the build's, 62, 58, 62, and 58 bytes earlier. Against revision 1, 42 of Obj01's items differ in their code (the first an `addi.w #4,y_pos(a0)` in Obj01_Init_Continued), and its collision routines have a `nop` the ROM lacks. The whole revision 2 build differs from the ROM in 244,967 of its 1,048,576 bytes.Probe: probes/lab/sonic-2-revision.mjs

So the lines cited in this set, read with gameRevision = 2, are the code this ROM runs; the one entry the ROM lacks is a local copy of a jump (JmpTo_KillCharacter) that removeJmpTos puts in the build, and the ROM's Sonic_HurtStop jumps through Obj01's own jump table instead. Sonic's movement routines have no gameRevision conditional; the one in his object is in Sonic_CheckGoSuper (the HUD timer check), outside the movement, and the animation scripts' rev02even only pads them to an even address in revision 2. The disassembly so gives the same movement code for revisions 0 and 1, but no probe here ran those ROMs. The ROM's RAM variables from about $FFFFFF60 up sit elsewhere: Player_option at $FF7C (the build's $FF72), Player_mode at $FF7A ($FF70), Two_player_mode at $FFB8 ($FFD8). The probes use the ROM's addresses (probes/jump-physics/sonic-2-lib.lua).

A frame-by-frame model of Obj01_MdNormal_Checks, Obj01_MdNormal, Obj01_MdRoll, and the air modes, with Sonic_Animate's timing for the standing and blinking animations, matches Sonic the Hedgehog 2 itself, run in MAME with Sonic alone on a floor built of Emerald Hill Act 1's blocks, on every frame of the movement lab's flat-ground presets, eleven scripts of Sonic 2's own moves, and 100 random input scripts. s2disasm/movement-rule

About: Sonic the Hedgehog 2 Running

Value
{
 "ground": {
  "accel": 12,
  "friction": 12,
  "decel": 128,
  "top": 1536,
  "flip": 128
 },
 "roll": {
  "start": 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
 }
}
Verifieds2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36280-L36305 — Obj01_Control, the mode by the in-air and rolling bits, the angle buffers copied after it, then Sonic_Animates2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36488-L36582 — Obj01_MdNormal_Checks, Obj01_MdNormal, Obj01_MdAir, Obj01_MdRoll, Obj01_MdJumps2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36515-L36519bsr.w Sonic_CheckSpindash ; check for spindash bsr.w Sonic_Jump ; check for jumping bsr.w Sonic_SlopeResist ; handle resistance from running up slopes bsr.w Sonic_Move ; handle Sonic's left/right movement bsr.w Sonic_Roll ; check for rollings2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36596-L37046 — Sonic_Move, the balance check, friction, traction, the ground wall check, Sonic_MoveLeft and Sonic_MoveRights2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37054-L37270 — Sonic_RollSpeed, Sonic_RollLeft and Sonic_RollRight, Sonic_ChgJumpDir with the drag at the top of a jumps2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37342-L37490 — Sonic_Roll, Sonic_Jump, Sonic_JumpHeights2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37600-L37743 — Sonic_CheckSpindash, Sonic_UpdateSpindashs2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37934-L38200 — Sonic_DoLevelCollision, Sonic_ResetOnFloors2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L1381-L1386 — Joypad_Read, a press per buttonnot.b d0 move.b (a0),d1 ; Get button press data eor.b d0,d1 ; Toggle off held buttons move.b d0,(a0)+ ; Store raw controller input for held button data and.b d0,d1 move.b d1,(a0)+ ; Store pressed controller inputChecked by claude, 2026-10-26 — read the mode routines and every subroutine they call for flat ground, Sonic_Animate, and Joypad_Read at the pinned commit, with gameRevision 2 and fixBugs off. The probe runs Sonic The Hedgehog 2 (World) (Rev B) in MAME 0.289 through the movement lab's ROM check (probes/lab/conformance.mjs, sonic-2-stage.mjs, sonic-2-movement.lua): Emerald Hill Act 1 started with Player_option set to Sonic alone before the title screen's Start (Player_mode read back as 1, and every frame the Lua stops if a Tails object is loaded; none was), its objects cleared every frame, the case's floor built in its foreground rows 6 and 7 from the act's full flat block (block 3, collision index `$FF`) with the first path's top and side bits (`$3000`, the path Sonic starts on), Sonic placed standing, input written into the joypad bytes each game frame, and the model run on the same input. On 2026-10-26 it compared 143 scripts (32 lab presets, 11 of Sonic 2's own: spin dashes charged and not, both ways, run into and jumped from; standing 180 and 1,040 frames, then the d-pad; a slow roll jump; a spin dash's roll jump landed above the top speed with Right held; and 100 random ones of 300 frames), 35,497 frames, on X and Y in 16.16, X, Y, and ground speed, airborne, curled, and facing, and all matched on the model's first run.Probe: probes/lab/sonic-2-conformance.mjs

Sonic 1's frame (s1disasm/movement-rule) with these changes, in code order. A standing frame first runs Obj01_MdNormal_Checks (idle-blink), which can skip the whole frame. Then Sonic_CheckSpindash: while charging, or ducking with a jump press, it ends the frame after the floor snap with no move (spindash-start, spindash-charge, spindash-release). Moving forward above the top speed keeps the speed (forward-cap-kept). A standing Sonic with ground speed 0 checks balance (balance), and otherwise looks up or ducks. Rolling, the air, and landing are Sonic 1's. The ground modes end in AnglePos (floor-sensors, walk-off), whose reach down depends on his X speed. Obj01_Control copies the angle buffers to next_tilt and tilt after the mode, and then runs Sonic_Animate. A, B, or C going down is a press, with another held or not.

Cited by: Sonic the Hedgehog, Sonic the Hedgehog 2

On stages built in Emerald Hill Act 1 from the act's full flat 16 × 16 block, solid on the collision path Sonic starts on, the movement lab's Sonic 2 model matches Sonic the Hedgehog 2 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. s2disasm/stage-rule

About: Sonic the Hedgehog 2 Hitbox

Value
{
 "stages": 100,
 "frames": 300
}
Verifieds2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43051-L43186 — AnglePos, Sonic_Angles2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43459-L43530 — FindFloors2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43476-L43477loc_1E7F0: ; block has some solidity movea.l (Collision_addr).w,a2 ; pointer to collision data, i.e. blockID -> collisionID arrays2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43990-L44235 — CalcRoomInFront, CalcRoomOverHead, Sonic_CheckFloor, ChkFloorEdges2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L44371-L44575 — CheckRightWallDist, Sonic_CheckCeiling, CheckLeftWallDists2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37934-L38200 — Sonic_DoLevelCollision, Sonic_ResetOnFloorChecked by claude, 2026-10-26 — read the routines above at the pinned commit, with gameRevision 2 and fixBugs off. The probe runs Sonic The Hedgehog 2 (World) (Rev B) in MAME 0.289 through the movement lab's ROM check (probes/lab/conformance.mjs, sonic-2-stage.mjs, sonic-2-movement.lua): Emerald Hill Act 1 started with Player_option set to Sonic alone before the title screen's Start (Player_mode read back as 1, and every frame the Lua stops if a Tails object is loaded; none was), its objects cleared every frame, the case's floor built in its foreground rows 6 and 7 from the act's full flat block (block 3, collision index `$FF`) with the first path's top and side bits (`$3000`, the path Sonic starts on), Sonic placed standing, input written into the joypad bytes each game frame, and the model run on the same input, each stage built over the case's span. The random stages are probes/jump-physics/sonic-movement.mjs's (randomStages, seed 7), the ones Sonic 1's check runs. On 2026-10-26 it compared 115 cases, 100 random stages of 300 frames and 15 stage-preset panes, 28,569 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 where he came within 48 px of the built floor's ends, x 0 and 4096.Probe: probes/lab/sonic-2-conformance.mjs

The stages are Sonic 1's (s1disasm/stage-rule), from the same generator and seed: every edge on one 16 px grid, tops from 64 px down to 64 px up, pits, and in three in four two to eight floating blocks. The game runs them in its layout's rows 6 and 7 (y 768 to 1023), Sonic's starting floor at row 928, with rows 4, 5, and 8 emptied over the built span.

Sonic dies once his Y position is more than 224 px below the camera's bottom boundary, checked by Sonic_LevelBound before each move, as in Sonic 1, but with fixBugs off Sonic 2 checks the boundary where it is (Camera_Max_Y_pos) and Sonic 1 the target it's moving to. In Emerald Hill Act 1, run in MAME, each of 18 falls into a pit killed him on the first frame after his center passed row 1024. s2disasm/bottom-boundary

About: Sonic the Hedgehog 2 Fall damage

Value: 224 px

Verifieds2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37299-L37315Sonic_Boundary_CheckBottom: move.w (Camera_Max_Y_pos).w,d0 if fixBugs ; The original code does not consider that the camera boundary ; may be in the middle of lowering itself, which is why going ; down the S-tunnel in Green Hill Zone Act 1 fast enough can ; kill Sonic. move.w (Camera_Max_Y_pos_target).w,d1 cmp.w d0,d1 blo.s .skip move.w d1,d0 .skip: …s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37317-L37323Sonic_Boundary_Bottom: ;; if fixBugs ; a2 needs to be set here, otherwise KillCharacter ; will access a dangling pointer! movea.l a0,a2 endif jmpto JmpTo_KillCharacters1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1083-L1097 — Sonic 1 loads the target, `v_limitbtm2`.chkbottom: move.w (v_limitbtm2).w,d0 ; load current target bottom level boundary if FixBugs ; The original code does not consider that the camera boundary ; may be in the middle of lowering itself, which is why going ; down the S-tunnel in GHZ act 1 fast enough can kill Sonic. move.w (v_limitbtm1).w,d1 ; load current actual bottom level boundary cmp.w d0,d1 ; is target bottom level boundary higher than current real one? blo.s .skipboundaryoverride ; if not, branch move.w d1,d0 ; use target level boundary while it's moving down to prevent unfair deaths .skipboundaryoverride: endif …Checked by claude, 2026-10-26 — read Sonic_LevelBound at the pinned commit (`cmp.w y_pos(a0),d0` then `blt.s Sonic_Boundary_Bottom`). The probe runs Sonic The Hedgehog 2 (World) (Rev B) in MAME 0.289 on the stages of stage-rule, in Emerald Hill Act 1 (bottom boundary 800, read from RAM); 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 1024, the boundary plus this claim's 224.Probe: probes/lab/sonic-2-conformance.mjs

screen_height is 224. Sonic 1 checks the boundary's target instead (s1disasm/bottom-boundary), which fixBugs restores here. Emerald Hill Act 1's bottom boundary is 800 ($320), so the line is row 1024.

With neither Left nor Right held and the sliding status clear, a standing Sonic's ground speed moves 12 ($C, his acceleration) toward 0 each frame and stops at 0, as in Sonic 1; while sliding, Sonic_Move goes straight to Obj01_Traction, with no d-pad and no friction. s2disasm/ground-friction

About: Sonic the Hedgehog 2 Running

Value: 12 1/256 px/frame²

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36826-L36834move.w inertia(a0),d0 ; get Sonic's current ground speed beq.s Obj01_Traction ; is he standing still? if yes, branch bmi.s Obj01_SettleLeft ; is he moving to the left? if yes, branch ; slow down when facing right and not pressing a direction ; Obj01_SettleRight: sub.w d5,d0 ; reduce current rightward speed by acceleration bcc.s + ; if result is still to the right, branch move.w #0,d0 ; reset speed to zero on sign changes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36600-L36603_btst #status_secondary.sliding,status_secondary(a0) ; is Sonic sliding? _bne.w Obj01_Traction ; if yes, branch tst.w move_lock(a0) ; is D-Pad input locked? bne.w Obj01_ResetScr ; if yes, branchs1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L499-L508Sonic_CheckDpadLetGo: move.b (v_jpadhold2).w,d0 ; get held buttons andi.b #btnL+btnR,d0 ; is left or right held? bne.s Sonic_AngleSpeed ; if yes, branch (don't decrease speed) move.w obInertia(a0),d0 ; get Sonic's current ground speed beq.s Sonic_AngleSpeed ; is he standing still? if yes, branch bmi.s .movingleftward ; is he moving to the left? if yes, branch sub.w d5,d0 ; reduce current rightward speed by acceleration bcc.s .stillright ; if result is still to the right, branch move.w #0,d0 ; reset speed to zero on sign changeChecked by claude, 2026-10-26 — read Sonic_Move and Obj01_UpdateSpeedOnGround at the pinned commit, and Sonic 1's Sonic_CheckDpadLetGo at its pin

d5 holds Sonic_acceleration (ground-speeds); Super Sonic uses $C here too, by the override above it.

Turning against his motion on flat ground, Sonic skids (the stopping animation, still facing his old direction of motion) when his ground speed before the turning step is at least $480 going right or $381 going left, as in Sonic 1: the angle check overwrites the low byte of the speed it then compares. s2disasm/skid-threshold

About: Sonic the Hedgehog 2 Running

Value
{
 "right": 1152,
 "left": 897
}
Codes2disasm: 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...) addi.b #$20,d0 ; rotate by 45 degrees clockwise andi.b #$C0,d0 ; snap to nearest multiple of 90 degrees endif bne.s return_1A744 ; if Sonic is on a wall or ceiling, prevent stopping animation cmpi.w #$400,d0 ; has Sonic changed direction while being really fast? blt.s return_1A744 ; if not, don't play skidding animation/sound move.b #AniIDSonAni_Stop,anim(a0) ; use "stopping" animation bclr #status.player.x_flip,status(a0) ; clear X-flip flag (Sonic is now facing right)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37031-L37035bne.s return_1A7C4 ; if Sonic is on a wall or ceiling, prevent stopping animation cmpi.w #-$400,d0 ; has Sonic changed direction while being really fast? bgt.s return_1A7C4 ; if not, don't play skidding animation/sound move.b #AniIDSonAni_Stop,anim(a0) ; use "stopping" animation bset #status.player.x_flip,status(a0) ; set X-flip flag (Sonic is now facing left)s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L684-L692move.b obAngle(a0),d0 ; get Sonic's current angle (and partially overwrite d0...) addi.b #$20,d0 ; rotate by 45 degrees clockwise andi.b #$C0,d0 ; snap to nearest multiple of 90 degrees endif bne.s .nostopping ; if Sonic is on a wall or ceiling, prevent stopping animation cmpi.w #$400,d0 ; has Sonic changed direction while being really fast? blt.s .nostopping ; if not, don't play skidding animation/sound move.b #id_Stop,obAnim(a0) ; use "stopping" animation bclr #status_xflip_bit,obStatus(a0) ; clear X-flip flag (Sonic is now facing right)Checked by claude, 2026-10-26 — read Sonic_TurnLeft and Sonic_TurnRight at the pinned commit, fixBugs off, and Sonic 1's at its pin

After $80 comes off, cmpi.w #$400 sees the speed with its low byte zeroed (the angle, 0 on flat ground): ≥ $400 going right needs $480 before the step; ≤ −$400 going left needs −$301 after it, −$381 before. fixBugs uses d1 and makes both $400 (s1disasm/skid-threshold).

Sonic's level collision radii are 9 × 19 px standing and 7 × 14 curled, set by Obj01_Init, the jump, the roll, and the spin dash, and his wall sensors sit 10 px from his center: Sonic 1's sizes. s2disasm/collision-box

About: Sonic the Hedgehog 2 Hitbox

Value
{
 "w": 19,
 "h": 39,
 "rollW": 15,
 "rollH": 29,
 "radius": {
  "w": 9,
  "h": 19,
  "rollW": 7,
  "rollH": 14,
  "push": 10
 }
}
Codes2disasm: 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#L37436-L37440move.b #$E,y_radius(a0) move.b #7,x_radius(a0) move.b #AniIDSonAni_Roll,anim(a0) ; use "jumping" animation bset #status.player.rolling,status(a0) addq.w #5,y_pos(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L44375-L44376CheckRightWallDist_Part2: addi.w #$A,d3s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L44566-L44567CheckLeftWallDist_Part2: subi.w #$A,d3s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _Constants.asm#L311-L318sonic_width: equ 18/2 ; Sonic's width sonic_height: equ 38/2 ; Sonic's height sonic_roll_width: equ 14/2 ; Sonic's width (rolling) sonic_roll_height: equ 28/2 ; Sonic's height (rolling) sonic_solid_width: equ 22/2 ; Sonic's width (solid object collision) sonic_react_width: equ 16/2 ; Sonic's width (object collision) sonic_duck_height: equ 20/2 ; Sonic's height (object collision, ducking) sonic_quick_size: equ 20/2 ; Sonic's size (quick terrain find)Checked by claude, 2026-10-26 — read Obj01_Init, Sonic_Jump, Obj01_DoRoll, Sonic_UpdateSpindash and the wall searches at the pinned commit, and Sonic 1's sizes at its pin

w and h are the spans the floor sensors cover standing (2 × radius + 1), rollW and rollH curled. Curling moves his center 5 px down so his feet stay put; uncurling moves it back. Same as s1disasm/collision-box.

Sonic 2's level collision is 16 × 16 px blocks whose layout words carry solidity bits for two collision paths; Sonic starts on the first, testing bit $C for floors and $D for walls and ceilings. As in Sonic 1, each search looks in the sensor's block and at most one more. s2disasm/collision-blocks

About: Sonic the Hedgehog 2 Hitbox

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36239-L36240move.b #$C,top_solid_bit(a0) move.b #$D,lrb_solid_bit(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43459-L43472FindFloor: bsr.w Find_Tile move.w (a1),d0 move.w d0,d4 andi.w #$3FF,d0 beq.s loc_1E7E2 btst d5,d4 bne.s loc_1E7F0 loc_1E7E2: add.w a3,d2 bsr.w FindFloor2 sub.w a3,d2 …s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43474-L43479; =========================================================================== loc_1E7F0: ; block has some solidity movea.l (Collision_addr).w,a2 ; pointer to collision data, i.e. blockID -> collisionID array move.b (a2,d0.w),d0 ; get collisionID andi.w #$FF,d0s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37934-L37940Sonic_DoLevelCollision: move.l #Primary_Collision,(Collision_addr).w cmpi.b #$C,top_solid_bit(a0) beq.s + move.l #Secondary_Collision,(Collision_addr).w + move.b lrb_solid_bit(a0),d5s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/sub FindNearestTile & FindFloor & FindWall.asm#L111-L125FindFloor: bsr.s FindNearestTile ; a1 = address within 256x256 mappings of 16x16 block being stood on move.w (a1),d0 ; get value for solidness, orientation and 16x16 block number move.w d0,d4 andi.w #$7FF,d0 ; ignore solid/orientation bits beq.s .isblank ; branch if block is blank btst d5,d4 ; is the block solid? bne.s .issolid ; if yes, branch .isblank: add.w a3,d2 bsr.w FindFloor2 ; try block below the nearest sub.w a3,d2 …Checked by claude, 2026-10-26 — read Obj01_Init, FindFloor and the collision routines' path choice at the pinned commit, and Sonic 1's FindFloor at its pin

top_solid_bit $C picks Primary_Collision (a block's collision index on the first path) and $E the second; a block counts only if its word has the routine's bit set, a nonzero block ID, and a nonzero collision index. The lab builds its stages with both first-path bits set ($3000), so every block is solid from every side, like Sonic 1's. Path-switching objects and the second path's $E/$F aren't on the lab's stages. A search's distances are Sonic 1's (s1disasm/collision-blocks); a block meets a sensor that writes its angle ($FF for the full block) to the sensor's buffer.

On flat ground and not on an object, after each ground move AnglePos sets both angle buffers to 3, then reads two floor sensors at his feet, X ± his width radius, each writing its buffer when it meets a block; the nearer distance counts, and a floor up to 14 px above his feet lifts him onto it. Standing on an object, it sets both buffers to 0 and reads no sensors; on a wall or a ceiling it turns the sensors to match (Sonic_WalkVertL, Sonic_WalkCeiling, Sonic_WalkVertR). s2disasm/floor-sensors

About: Sonic the Hedgehog 2 Hitbox

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43058-L43067btst #status.player.on_object,status(a0) beq.s + ; branch if Sonic isn't on a platform moveq #0,d0 move.b d0,(Primary_Angle).w ; clear angle hotspots move.b d0,(Secondary_Angle).w rts ; --------------------------------------------------------------------------- + moveq #3,d0 move.b d0,(Primary_Angle).w move.b d0,(Secondary_Angle).ws2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43087-L43093andi.b #$C0,d0 ; read only bits 6-7 of angle cmpi.b #$40,d0 beq.w Sonic_WalkVertL ; branch if on left vertical cmpi.b #$80,d0 beq.w Sonic_WalkCeiling ; branch if on ceiling cmpi.b #$C0,d0 beq.w Sonic_WalkVertR ; branch if on right verticals2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43094-L43106move.w y_pos(a0),d2 move.w x_pos(a0),d3 moveq #0,d0 move.b y_radius(a0),d0 ext.w d0 add.w d0,d2 ; d2 = y pos of bottom edge of Sonic move.b x_radius(a0),d0 ext.w d0 add.w d0,d3 ; d3 = x pos of right edge of Sonic lea (Primary_Angle).w,a4 ; write angle here movea.w #$10,a3 ; tile height move.w #0,d6 …s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43122-L43129move.w (sp)+,d0 ; d0 = distance to floor right side bsr.w Sonic_Angle ; update angle tst.w d1 beq.s return_1E31C ; branch if Sonic is 0px from floor bpl.s loc_1E31E ; branch if Sonic is above floor cmpi.w #-$E,d1 blt.s return_1E31C ; branch if Sonic is > 14px below floor add.w d1,y_pos(a0) ; align to floors2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43166-L43172Sonic_Angle: move.b (Secondary_Angle).w,d2 ; use left side angle cmp.w d0,d1 ble.s + ; branch if floor is nearer on left side move.b (Primary_Angle).w,d2 ; use right side angle move.w d0,d1 ; use distance of right side +Checked by claude, 2026-10-26 — read AnglePos, its quadrant choice, and Sonic_Angle at the pinned commit

The right sensor writes Primary_Angle, the left Secondary_Angle. A distance of 0 does nothing, and one deeper than 14 px leaves him where he is. As in Sonic 1 (s1disasm/floor-sensors), the stage's floor is flat, so the angle stays 0 ($FF, odd, snaps it to the nearest quarter).

When both floor sensors are above the floor after a ground move, AnglePos moves Sonic down onto it if it's within his X speed's whole pixels plus 4, at most 14; any farther and he's in the air. Sonic 1 reaches down 14 px at every speed. s2disasm/walk-off

About: Sonic the Hedgehog 2 Coyote time

Value
{
 "plus": 4,
 "max": 14
}
Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43135-L43156loc_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_1E33C loc_1E336: add.w d1,y_pos(a0) ; align to floor rts …s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/Sonic AnglePos.asm#L99-L101.above_floor: cmpi.w #$E,d1 bgt.s .in_air ; branch if Sonic is > 14px above floorChecked by claude, 2026-10-26 — read AnglePos in both disassemblies at their pinned commits

mvabs.b x_vel(a0) takes the high byte of the X speed, its whole pixels rounded down, then its absolute value: below 1 px/frame (or −1 to 0 going left, whose high byte is −1, so 1) the reach is 4 or 5 px. So a slow landing that leaves a gap (a roll jump keeps his standing radius, roll-jump-size) lifts him off again. Leaving the ground sets the in-air flag and prev_anim, and leaves the speeds as they were.

On the ground, the wall check looks from where this frame's X speed will put him, 10 px ahead of his center and 8 px below it on flat ground, as in Sonic 1 (whose 10 px is sonic_quick_size). s2disasm/wall-sensors

About: Sonic the Hedgehog 2 Hitbox

Value: 8 px

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L43997-L44012move.l x_pos(a0),d3 move.l y_pos(a0),d2 move.w x_vel(a0),d1 ext.l d1 asl.l #8,d1 add.l d1,d3 move.w y_vel(a0),d1 ext.l d1 asl.l #8,d1 add.l d1,d2 swap d2 swap d3 …s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L44031-L44036beq.w CheckFloorDist_Part2 ; Player is going mostly down cmpi.b #$80,d0 beq.w CheckCeilingDist_Part2 ; Player is going mostly up andi.b #$38,d1 bne.s + addq.w #8,d2s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/Sonic Collision.asm#L13-L23Sonic_CalcRoomAhead: move.l obX(a0),d3 move.l obY(a0),d2 move.w obVelX(a0),d1 ext.l d1 asl.l #8,d1 add.l d1,d3 ; d3 = predicted x pos. at next frame move.w obVelY(a0),d1 ext.l d1 asl.l #8,d1 add.l d1,d2 ; d2 = predicted y pos. at next frames1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/Sonic Collision.asm#L55-L59cmpi.b #$80,d0 beq.w Sonic_FindCeiling_Quick andi.b #$38,d1 bne.s .find_wall_lr addq.w #8,d2s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _Constants.asm#L318sonic_quick_size: equ 20/2 ; Sonic's size (quick terrain find)s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/Sonic Collision.asm#L298-L299Sonic_FindWallRight_Quick: addi.w #sonic_quick_size,d3Checked by claude, 2026-10-26 — read CalcRoomInFront at the pinned commit, and Sonic 1's Sonic_CalcRoomAhead at its pin

CalcRoomInFront writes the direction it looks ($40 or $C0 on flat ground) to both angle buffers before searching; AnglePos sets them again later in the frame.

On the ground with nonzero ground speed, a wall found ahead (the sensor of wall-sensors inside a block) cuts this frame's X speed so he stops beside it, and sets his ground speed to 0, as in Sonic 1. s2disasm/ground-wall

About: Sonic the Hedgehog 2 Hitbox

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36886-L36890bsr.w CalcRoomInFront ; calculate distance to the wall in front of Sonic (if any) move.w (sp)+,d0 ; restore d0 tst.w d1 ; has Sonic touched a wall? bpl.s return_1A6BE ; if not, branch asl.w #8,d1 ; convert diff to wall into 8.8 fixed point for adjusting velocitys2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36899-L36901add.w d1,x_vel(a0) ; adjust X-velocity to prevent Sonic from walking into the wall bset #status.player.pushing,status(a0) ; set pushing flag move.w #0,inertia(a0) ; clear ground speeds1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L567-L579tst.w d1 ; has Sonic touched a wall? bpl.s .return ; if not, branch asl.w #8,d1 ; convert diff to wall into 8.8 fixed point for adjusting velocity addi.b #$20,d0 ; rotate angle by 45 degrees clockwise andi.b #$C0,d0 ; snap angle to nearest multiple of 90 degrees beq.s .hitdown ; if Sonic is facing down, branch cmpi.b #$40,d0 ; is Sonic facing left? beq.s .hitleft ; if yes, branch cmpi.b #$80,d0 ; is Sonic facing up? beq.s .hitup ; if yes, branch .hitright: ; d0 is $C0, Sonic is facing right add.w d1,obVelX(a0) ; adjust X-velocity to prevent Sonic from walking into the wallChecked by claude, 2026-10-26 — read Obj01_CheckWallsOnGround at the pinned commit, fixBugs off, and Sonic 1's wall check at its pin

Rolling reaches the same check from Sonic_SetRollSpeeds. The check is skipped upside down; with fixBugs S3K's flat-angle exception would apply, which doesn't change flat ground.

In the air Sonic_DoLevelCollision picks one of four checks by the direction of his speed: CalcAngle of X and Y speed less $20, to a quarter, as in Sonic 1, with the same angle table. s2disasm/air-quadrant

About: Sonic the Hedgehog 2 Hitbox

Value: 250 256 × |the minor speed| / |the major one| at the `$20` boundary

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37941-L37951move.w x_vel(a0),d1 move.w y_vel(a0),d2 jsr (CalcAngle).l subi.b #$20,d0 andi.b #$C0,d0 cmpi.b #$40,d0 beq.w Sonic_HitLeftWall cmpi.b #$80,d0 beq.w Sonic_HitCeilingAndWalls cmpi.b #$C0,d0 beq.w Sonic_HitRightWalls2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L4049-L4060cmp.w d3,d4 bhs.w + ; if abs(y) >= abs(x) lsl.l #8,d4 divu.w d3,d4 moveq #0,d0 move.b Angle_Data(pc,d4.w),d0 bra.s ++ + lsl.l #8,d3 divu.w d4,d3 moveq #$40,d0s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1485-L1504move.w obVelX(a0),d1 ; get current horizontal speed move.w obVelY(a0),d2 ; get current vertical speed jsr (CalcAngle).l ; calculate arctan based on Sonic's current fall direction if UnusedOptimization subi.b #$20,d0 ; rotate 45 degrees counterclockwise andi.b #$C0,d0 ; snap to nearest multiple of 90 degrees else move.b d0,(v_unused3).w ; (unused) store basic angle subi.b #$20,d0 ; rotate 45 degrees counterclockwise move.b d0,(v_unused4).w ; (unused) store -45 degrees angle andi.b #$C0,d0 ; snap to nearest multiple of 90 degrees move.b d0,(v_unused5).w ; (unused) store snapped angle …Checked by claude, 2026-10-26 — read Sonic_DoLevelCollision and CalcAngle at the pinned commit, and Sonic 1's Sonic_Floor at its pin; Angle_Data in the ROM is byte for byte Sonic 1's

Mostly down: both walls, then the floor. Mostly left or right: that wall; if none, the ceiling; if none and he's falling, the floor. Mostly up: both walls, then the ceiling. The ratio at which the table reaches $20 is s1disasm/air-quadrant's 250.

In the air, a wall sensor 10 px from his center inside a block pushes Sonic out by whole pixels and sets his X speed to 0; moving mostly sideways, that ends the frame's checks, as in Sonic 1. s2disasm/air-wall

About: Sonic the Hedgehog 2 Hitbox

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37952-L37962bsr.w CheckLeftWallDist tst.w d1 bpl.s + sub.w d1,x_pos(a0) move.w #0,x_vel(a0) ; stop Sonic since he hit a wall + bsr.w CheckRightWallDist tst.w d1 bpl.s + add.w d1,x_pos(a0) move.w #0,x_vel(a0) ; stop Sonic since he hit a walls2disasm: 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) rtss1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1511-L1524Sonic_FloorDown: bsr.w Sonic_FindWallLeft_Quick_UsePos ; check Sonic's distance to nearest left wall tst.w d1 ; is Sonic grazing a wall to the left while falling? bpl.s .noleftgraze ; if not, branch sub.w d1,obX(a0) ; align Sonic with the wall move.w #0,obVelX(a0) ; clear horizontal speed ; loc_135F0: .noleftgraze: bsr.w Sonic_FindWallRight_Quick_UsePos ; check Sonic's distance to nearest right wall tst.w d1 ; is Sonic grazing a wall to the right while falling? bpl.s .norightgraze ; if not, branch add.w d1,obX(a0) ; align Sonic with the wall …s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1625-L1632Sonic_FloorLeft: bsr.w Sonic_FindWallLeft_Quick_UsePos ; check Sonic's distance to nearest left wall tst.w d1 ; is Sonic grazing a wall to the left while falling? bpl.s .noleftgraze ; if not, branch sub.w d1,obX(a0) ; align Sonic to the wall move.w #0,obVelX(a0) ; clear horizontal speed move.w obVelY(a0),obInertia(a0) ; convert in-air vertical speed to ground speed when landing rts ; returnChecked by claude, 2026-10-26 — read Sonic_DoLevelCollision, Sonic_HitLeftWall and Sonic_HitRightWall at the pinned commit, and Sonic 1's Sonic_Floor at its pin

Both wall searches write Primary_Angle. Moving sideways into a wall copies his Y speed to his ground speed.

Falling mostly down, Sonic lands only if one floor sensor is no deeper in the floor than his Y speed's whole pixels plus 8, as in Sonic 1; moving mostly sideways he lands at any depth while not rising, with fixBugs off. s2disasm/landing-depth

About: Sonic the Hedgehog 2 Hitbox

Value: 8 px

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37964-L37977bsr.w Sonic_CheckFloor tst.w d1 bpl.s return_1AF8A move.b y_vel(a0),d2 addq.b #8,d2 neg.b d2 cmp.b d2,d1 bge.s + cmp.b d2,d0 blt.s return_1AF8A + add.w d1,y_pos(a0) …s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38035-L38040 — moving sideways, the floor check, skipped while rising; the depth check below it sits inside `if fixBugs`Sonic_HitFloor: tst.w y_vel(a0) bmi.s return_1AFE6 bsr.w Sonic_CheckFloor tst.w d1 bpl.s return_1AFE6s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38058-L38061endif add.w d1,y_pos(a0) move.b d3,angle(a0) bsr.w Sonic_ResetOnFloors1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1549-L1555move.b obVelY(a0),d2 ; get Sonic's fall speed at the time of impact (upper byte only, pixel delta) addq.b #8,d2 ; increase it by one tile neg.b d2 ; mirror it cmp.b d2,d1 ; is result bigger than distance to floor? bge.s .landed ; if yes, branch cmp.b d2,d0 ; is result bigger than distance to floor? (sloped variant) blt.s .return ; if not, branchs1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1651-L1656.noceiling: tst.w obVelY(a0) ; is Sonic going up? bmi.s .return ; if yes, branch bsr.w Sonic_FindFloor ; find Sonic's distance to floor tst.w d1 ; has Sonic touched the floor? bpl.s .return ; if not, branchChecked by claude, 2026-10-26 — read Sonic_DoLevelCollision and Sonic_HitFloor at the pinned commit, fixBugs off, and Sonic 1's Sonic_FloorDown at its pin

The comparison is on bytes, against the Y speed after this frame's gravity.

Moving mostly up, a head sensor inside a block moves Sonic down out of it and sets his Y speed to 0 on a flat ceiling; moving mostly sideways it moves him down and stops a rise, and skips the floor check that frame, as in Sonic 1. s2disasm/air-ceiling

About: Sonic the Hedgehog 2 Hitbox

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38023-L38033bsr.w Sonic_CheckCeiling tst.w d1 bpl.s Sonic_HitFloor ; branch if distance is positive (not inside ceiling) sub.w d1,y_pos(a0) tst.w y_vel(a0) bpl.s return_1AFBE move.w #0,y_vel(a0) ; stop Sonic in y since he hit a ceiling return_1AFBE: rts ; ===========================================================================s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38081-L38091+ bsr.w Sonic_CheckCeiling tst.w d1 bpl.s return_1B042 sub.w d1,y_pos(a0) move.b d3,d0 addi.b #$20,d0 andi.b #$40,d0 bne.s loc_1B02C move.w #0,y_vel(a0) ; stop Sonic in y since he hit a ceiling rtss1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1714-L1722bsr.w Sonic_FindCeiling ; find Sonic's distance to the ceiling tst.w d1 ; is Sonic touching the ceiling? bpl.s .return ; if not, branch sub.w d1,obY(a0) ; align Sonic with the ceiling move.b d3,d0 ; get Sonic's landing angle addi.b #$20,d0 ; rotate it by 45 degrees andi.b #$40,d0 ; are we bumping against an angled ceiling? bne.s .angledceiling ; if yes, branch move.w #0,obVelY(a0) ; clear vertical speed when bumping against a flat ceilings1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1636-L1647.noleftgraze: bsr.w Sonic_FindCeiling ; check if Sonic's distance to the ceiling tst.w d1 ; is Sonic touching the ceiling? bpl.s .noceiling ; if not, branch sub.w d1,obY(a0) ; align Sonic with the ceiling tst.w obVelY(a0) ; is vertical speed positive? (going down) bpl.s .noyspeedreset ; if yes, branch move.w #0,obVelY(a0) ; if going up, reset it to zero ; locret_136B2: .noyspeedreset: rts ; returnChecked by claude, 2026-10-26 — read Sonic_HitCeiling and Sonic_HitCeilingAndWalls at the pinned commit, and Sonic 1's Sonic_FloorUp at its pin

A steep ceiling (angle bit $40 after $20 added) would land him on it; the lab's full block reads flat.

On the ground nothing checks above Sonic except a jump press; standing or rolling into a block's underside leaves him inside it, as in Sonic 1. s2disasm/ground-no-ceiling

About: Sonic the Hedgehog 2 Hitbox

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36515-L36527bsr.w Sonic_CheckSpindash ; check for spindash bsr.w Sonic_Jump ; check for jumping bsr.w Sonic_SlopeResist ; handle resistance from running up slopes bsr.w Sonic_Move ; handle Sonic's left/right movement bsr.w Sonic_Roll ; check for rolling if fixBugs Obj01_MdNormal_Skip: endif bsr.w Sonic_LevelBound ; keep Sonic within level bounds and handle bottomless pits jsr (ObjectMove).l ; update Sonic's position based on his current velocities bsr.w AnglePos ; update Sonic's current angle as he walks along the floor bsr.w Sonic_SlopeRepel ; handle Sonic detaching from walls if not fast enoughs2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36557-L36565tst.b pinball_mode(a0) ; is Sonic in pinball mode? bne.s + ; if yes, branch bsr.w Sonic_Jump ; check for jumping + bsr.w Sonic_RollRepel ; handle resistance from rolling up slopes bsr.w Sonic_RollSpeed ; update speed as Sonic rolls bsr.w Sonic_LevelBound ; keep Sonic within level bounds and handle bottomless pits jsr (ObjectMove).l ; update Sonic's position based on his current velocities bsr.w AnglePos ; update Sonic's current angle as he walks along the floors1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L317-L325Sonic_MdNormal: ; While Sonic is on the ground and not rolling bsr.w Sonic_Jump ; check if we need to jump bsr.w Sonic_SlopeResistWalk ; handle resistance from running up slopes bsr.w Sonic_Move ; handle Sonic's left/right movement bsr.w Sonic_Roll ; check if we need to roll bsr.w Sonic_LevelBound ; make sure Sonic stays within level bounds and handle bottomless pits jsr (SpeedToPos).l ; update Sonic's position based on his current velocities bsr.w Sonic_AnglePos ; update Sonic's current angle as he walks along the floor bsr.w Sonic_SlopeRepel ; handle Sonic detaching from walls if not fast enoughChecked by claude, 2026-10-26 — read Obj01_MdNormal and Obj01_MdRoll and the routines they call at the pinned commit, and Sonic 1's Sonic_MdNormal at its pin

The wall check ahead looks 8 px below his center (wall-sensors), so a block whose underside is above that isn't a wall to him.

A jump press jumps only if both head sensors find 6 px or more of room above his head, as in Sonic 1; the check writes $80 to both angle buffers first. s2disasm/jump-headroom

About: Sonic the Hedgehog 2 Jump

Value: 6 px

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37394-L37399moveq #0,d0 move.b angle(a0),d0 addi.b #$80,d0 bsr.w CalcRoomOverHead cmpi.w #6,d1 ; does Sonic have enough room to jump? blt.w return_1AAE6 ; if not, branchs2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L44061-L44066move.b d0,(Primary_Angle).w move.b d0,(Secondary_Angle).w addi.b #$20,d0 andi.b #$C0,d0 cmpi.b #$40,d0 beq.w CheckLeftCeilingDists1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1211-L1216moveq #0,d0 ; clear d0 move.b obAngle(a0),d0 ; get Sonic's current angle addi.b #$80,d0 ; rotate 180 degrees for ceiling collision check bsr.w Sonic_CalcHeadroom ; calculate pixels above Sonic's head cmpi.w #6,d1 ; are there less than 6px between Sonic and the ceiling? blt.w .return ; if yes, prevent jumpingChecked by claude, 2026-10-26 — read Sonic_Jump, CalcRoomOverHead and Sonic_CheckCeiling at the pinned commit, and Sonic 1's Sonic_Jump at its pin

The head sensors sit at X ± his width radius, his height radius above his center. The nearer counts. A failed check leaves the frame to run on, and its AnglePos sets the buffers again.

Standing on flat ground, not on an object, not sliding, and with d-pad control (move_lock 0), with ground speed 0 after the left and right steps and the floor under his center 12 px or more below his feet, Sonic balances on the side whose floor sensor met no block last frame (the right first). He turns to face that side only when he faces away from it and the floor 6 px back from his center is also 12 px or more below; Sonic 1 turns him at any such edge. s2disasm/balance

About: Sonic the Hedgehog 2 Hitbox

Value
{
 "far": 12,
 "back": 6
}
Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36600-L36603_btst #status_secondary.sliding,status_secondary(a0) ; is Sonic sliding? _bne.w Obj01_Traction ; if yes, branch tst.w move_lock(a0) ; is D-Pad input locked? bne.w Obj01_ResetScr ; if yes, branchs2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36614-L36623move.b angle(a0),d0 ; load Sonic's current angle addi.b #$20,d0 ; rotate it 45 degrees clockwise andi.b #$C0,d0 ; snap to nearest multiple of 90 degrees bne.w Obj01_ResetScr ; if Sonic is on a slope, then branch tst.w inertia(a0) ; is Sonic moving? bne.w Obj01_ResetScr ; if yes, branch bclr #status.player.pushing,status(a0) ; clear pushing flag move.b #AniIDSonAni_Wait,anim(a0) ; use "standing" animation btst #status.player.on_object,status(a0) ; is Sonic standing on a platform object? beq.w Sonic_Balance ; if not, branchs2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36698-L36724Sonic_Balance: jsr (ChkFloorEdge).l ; get distance of Sonic to nearest floor cmpi.w #$C,d1 ; is he within $C (12px) to floor? blt.w Sonic_Lookup ; if yes, don't do balance animation (would look awkward) tst.b (Super_Sonic_flag).w ; is Sonic in his Super form? bne.w SuperSonic_Balance2 ; if yes, branch cmpi.b #3,next_tilt(a0) bne.s Sonic_BalanceLeft btst #status.player.x_flip,status(a0) ; is Sonic facing right? bne.s + ; if not, branch move.b #AniIDSonAni_Balance,anim(a0) ; set Sonic's animation to "Balance" move.w x_pos(a0),d3 …s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36727-L36748Sonic_BalanceLeft: cmpi.b #3,tilt(a0) bne.s Sonic_Lookup btst #status.player.x_flip,status(a0) ; is Sonic facing left? beq.s + ; if not, branch move.b #AniIDSonAni_Balance,anim(a0) ; set Sonic's animation to "Balance" move.w x_pos(a0),d3 addq.w #6,d3 jsr (ChkFloorEdge_Part2).l cmpi.w #$C,d1 blt.w Obj01_ResetScr move.b #AniIDSonAni_Balance2,anim(a0) ; set Sonic's animation to "Balance2" …s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36297-L36298move.b (Primary_Angle).w,next_tilt(a0) ; update front collision hot spot move.b (Secondary_Angle).w,tilt(a0) ; update rear collision hot spots1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L437-L457 — Sonic 1 turns him to face the edge whichever way he faced.chkbalance: jsr (ObjFloorDist).l ; get distance of Sonic to nearest floor cmpi.w #12,d1 ; is he within 12px to floor? blt.s Sonic_LookUp ; if yes, don't do balance animation (would look awkward) cmpi.b #3,angleright(a0) ; is Sonic at the right edge of the floor? bne.s .chkleftedge ; if not, check for the left edge ; loc_12F5A: .rightbalance: bclr #status_xflip_bit,obStatus(a0) ; clear X-flip flag (make Sonic face right) bra.s .balance ; do balance animation ; =========================================================================== ; loc_12F62: …Checked by claude, 2026-10-26 — read Sonic_Move's balance check, ChkFloorEdge, AnglePos and Obj01_Control at the pinned commit, and Sonic 1's Sonic_Move at its pin

next_tilt and tilt are the angle buffers as last frame left them (floor-sensors). Facing the edge he plays Balance, or Balance2 when the floor 6 px back is also gone; facing away, Balance3, or Balance4 and a turn. Balancing skips looking up and ducking, so Down at an edge doesn't duck and can't start a spin dash. As Super Sonic he faces the edge whichever way he faced (SuperSonic_Balance2).

A jump adds nothing to Sonic's X speed on flat ground, and the rest of that frame's mode (the move and the floor snap) doesn't run, as in Sonic 1. s2disasm/takeoff-x

About: Sonic the Hedgehog 2 Jump

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37409-L37421moveq #0,d0 move.b angle(a0),d0 subi.b #$40,d0 jsr (CalcSine).l muls.w d2,d1 asr.l #8,d1 add.w d1,x_vel(a0) ; make Sonic jump (in X... this adds nothing on level ground) muls.w d2,d0 asr.l #8,d0 add.w d0,y_vel(a0) ; make Sonic jump (in Y) bset #status.player.in_air,status(a0) bclr #status.player.pushing,status(a0) …s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1224-L1236moveq #0,d0 ; clear d0 move.b obAngle(a0),d0 ; get Sonic's current angle subi.b #$40,d0 ; rotate it perpendicularly for jump trajectory jsr (CalcSine).l ; find the direction Sonic should jump muls.w d2,d1 ; apply jump force to the cosine angle asr.l #8,d1 ; shift it to upper word add.w d1,obVelX(a0) ; apply to X speed muls.w d2,d0 ; apply jump force to the sine angle asr.l #8,d0 ; shift it to upper word add.w d0,obVelY(a0) ; apply to Y speed bset #status_in_air_bit,obStatus(a0) ; set in-air flag bclr #status_pushing_bit,obStatus(a0) ; clear pushing flag …The 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, and Sonic 1's at its pin

addq.l #4,sp drops the return into the mode, so Obj01_MdNormal or Obj01_MdRoll ends there.

A jump from a roll sets the roll-jump flag, and while it's set Sonic_ChgJumpDir skips the d-pad, as in Sonic 1; the drag near the top of the jump still applies. s2disasm/roll-jump-lock

About: Sonic the Hedgehog 2 Air control

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37434-L37435btst #status.player.rolling,status(a0) bne.s Sonic_RollJumps2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37446-L37447Sonic_RollJump: bset #status.player.rolljumping,status(a0) ; set the rolling+jumping flags2disasm: 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 controls1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L944-L945btst #status_rolljumping_bit,obStatus(a0) ; is Roll-Jump flag set? bne.s Sonic_RollJumpLock ; if yes, prevent midair direction changeChecked by claude, 2026-10-26 — read Sonic_Jump and Sonic_ChgJumpDir at the pinned commit, and Sonic 1's Sonic_JumpDirection at its pin

With fixBugs off, a jump sets the standing collision radii before checking for a roll, so a roll jump keeps them while curled, and lands 5 px early; landing then lifts his center 5 px more, as in Sonic 1. s2disasm/roll-jump-size

About: Sonic the Hedgehog 2 Jump

Value: 5 px

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37426-L37440if ~~fixBugs ; These lines are a remnant of the victory animation that would play ; when jumping at the end of a stage in the Sonic 1 prototype. Not only ; are these two lines now useless, they also cause Sonic to incorrectly ; reset to his standing collision when roll-jumping. move.b #$13,y_radius(a0) move.b #9,x_radius(a0) endif btst #status.player.rolling,status(a0) bne.s Sonic_RollJump move.b #$E,y_radius(a0) move.b #7,x_radius(a0) …s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38179-L38185btst #status.player.rolling,status(a0) beq.s Sonic_ResetOnFloor_Part3 bclr #status.player.rolling,status(a0) move.b #$13,y_radius(a0) ; this increases Sonic's collision height to standing move.b #9,x_radius(a0) move.b #AniIDSonAni_Walk,anim(a0) ; use running/walking/standing animation subq.w #5,y_pos(a0) ; move Sonic up 5 pixels so the increased height doesn't push him into the grounds1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1241-L1249if (FixBugs=0)&(UnusedOptimization=0) ; This sets Sonic's hitbox to standing size when roll-jumping. ; A leftover from the victory animation in prototypes. move.b #sonic_height,obHeight(a0) ; set height to standing size move.b #sonic_width,obWidth(a0) ; set width to standing size endif btst #status_rolling_bit,obStatus(a0) ; is Sonic already in a ball state? bne.s .rolljump ; if so, branchs1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1830-L1838btst #status_rolling_bit,obStatus(a0) ; check if Sonic is in a ball state beq.s .notball ; if not, skip bclr #status_rolling_bit,obStatus(a0) ; clear ball flag move.b #sonic_height,obHeight(a0) ; set Sonic's hitbox height to standing move.b #sonic_width,obWidth(a0) ; set Sonic's hitbox width to standing if FixBugs=0 move.b #id_Walk,obAnim(a0) ; use running/walking animation endif subq.w #sonic_height-sonic_roll_height,obY(a0) ; raise Sonic up 5 pixels so he's not inside the groundChecked by claude, 2026-10-26 — read Sonic_Jump and Sonic_ResetOnFloor at the pinned commit, and Sonic 1's at its pin

Landed with a slow X speed, the gap he lands with can be more than AnglePos reaches down (walk-off), and the next frame lifts him off again.

Landing on flat ground sets his ground speed to his X speed and his Y speed to 0, and clears the jump, roll-jump, and in-air flags, as in Sonic 1; out of pinball mode it also uncurls him (the standing radii, his center 5 px up), as Sonic 1 always does. s2disasm/landing

About: Sonic the Hedgehog 2 Jump

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37975-L37977add.w d1,y_pos(a0) move.b d3,angle(a0) bsr.w Sonic_ResetOnFloors2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37990-L37993loc_1AF5A: move.w #0,y_vel(a0) move.w x_vel(a0),inertia(a0) rtss2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38168-L38171Sonic_ResetOnFloor: tst.b pinball_mode(a0) bne.s Sonic_ResetOnFloor_Part3 move.b #AniIDSonAni_Walk,anim(a0)s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38179-L38185btst #status.player.rolling,status(a0) beq.s Sonic_ResetOnFloor_Part3 bclr #status.player.rolling,status(a0) move.b #$13,y_radius(a0) ; this increases Sonic's collision height to standing move.b #9,x_radius(a0) move.b #AniIDSonAni_Walk,anim(a0) ; use running/walking/standing animation subq.w #5,y_pos(a0) ; move Sonic up 5 pixels so the increased height doesn't push him into the grounds2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38187-L38191Sonic_ResetOnFloor_Part3: bclr #status.player.in_air,status(a0) bclr #status.player.pushing,status(a0) bclr #status.player.rolljumping,status(a0) move.b #0,jumping(a0)s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1594-L1596.flatsurface: move.w #0,obVelY(a0) ; completely clear Sonic's vertical speed when landing on a flat surface move.w obVelX(a0),obInertia(a0) ; convert in-air horizontal speed to ground speed when landings1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L1822-L1842bclr #status_pushing_bit,obStatus(a0) ; clear push flag bclr #status_in_air_bit,obStatus(a0) ; clear in-air flag bclr #status_rolljumping_bit,obStatus(a0) ; clear roll-jump flag if FixBugs ; This line was placed too late into the routine, ; occasionally causing Sonic "sliding" on the floor move.b #id_Walk,obAnim(a0) ; use running/walking animation endif btst #status_rolling_bit,obStatus(a0) ; check if Sonic is in a ball state beq.s .notball ; if not, skip bclr #status_rolling_bit,obStatus(a0) ; clear ball flag move.b #sonic_height,obHeight(a0) ; set Sonic's hitbox height to standing …Checked by claude, 2026-10-26 — read Sonic_DoLevelCollision and Sonic_ResetOnFloor at the pinned commit, and Sonic 1's at its pin

Letting go of Down while charging curls Sonic (Y + 5), sets his ground speed from the counter's high byte (spindash-speeds, or SpindashSpeedsSuper as Super Sonic), negated facing left, and ends the frame after the floor snap with his X speed unchanged, so he starts moving the next frame. s2disasm/spindash-release

About: Sonic the Hedgehog 2 Spin dash

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37633-L37647Sonic_UpdateSpindash: move.b (Ctrl_1_Held_Logical).w,d0 btst #button_down,d0 bne.w Sonic_ChargingSpindash ; unleash the charged spindash and start rolling quickly: move.b #$E,y_radius(a0) move.b #7,x_radius(a0) move.b #AniIDSonAni_Roll,anim(a0) addq.w #5,y_pos(a0) ; add the difference between Sonic's rolling and standing heights move.b #0,spindash_flag(a0) moveq #0,d0 move.b spindash_counter(a0),d0 …s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37648-L37651tst.b (Super_Sonic_flag).w beq.s + move.w SpindashSpeedsSuper(pc,d0.w),inertia(a0) +s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37677-L37685btst #status.player.x_flip,status(a0) beq.s + neg.w inertia(a0) + bset #status.player.rolling,status(a0) move.b #0,(Sonic_Dust+anim).w move.w #SndID_SpindashRelease,d0 ; spindash zoom sound jsr (PlaySound).l bra.s Obj01_Spindash_ResetScrs2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L37732-L37743Obj01_Spindash_ResetScr: addq.l #4,sp cmpi.w #(screen_height/2)-16,(Camera_Y_pos_bias).w beq.s loc_1AD8C bhs.s + addq.w #4,(Camera_Y_pos_bias).w + subq.w #2,(Camera_Y_pos_bias).w loc_1AD8C: bsr.w Sonic_LevelBound bsr.w AnglePos rtsChecked by claude, 2026-10-26 — read Sonic_UpdateSpindash at the pinned commit

Every spin dash frame (the start, each charging frame, the release) runs Sonic_LevelBound and AnglePos and nothing else of the mode.

Cited by: Sonic the Hedgehog 2

Sonic 2's movement on flat ground is Sonic 1's with five changes: the spin dash, a ground speed above the top kept while forward is held, a blink after standing that skips up to five frames of input, balancing that turns him only when he's far over the edge, and a floor snap that reaches down his X speed's whole pixels plus 4 rather than 14 px. s2disasm/movement-differs-from-sonic-the-hedgehog

About: Sonic the Hedgehog 2 Running

Codes2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36515-L36519bsr.w Sonic_CheckSpindash ; check for spindash bsr.w Sonic_Jump ; check for jumping bsr.w Sonic_SlopeResist ; handle resistance from running up slopes bsr.w Sonic_Move ; handle Sonic's left/right movement bsr.w Sonic_Roll ; check for rollings1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L317-L325Sonic_MdNormal: ; While Sonic is on the ground and not rolling bsr.w Sonic_Jump ; check if we need to jump bsr.w Sonic_SlopeResistWalk ; handle resistance from running up slopes bsr.w Sonic_Move ; handle Sonic's left/right movement bsr.w Sonic_Roll ; check if we need to roll bsr.w Sonic_LevelBound ; make sure Sonic stays within level bounds and handle bottomless pits jsr (SpeedToPos).l ; update Sonic's position based on his current velocities bsr.w Sonic_AnglePos ; update Sonic's current angle as he walks along the floor bsr.w Sonic_SlopeRepel ; handle Sonic detaching from walls if not fast enoughs2disasm: 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/01 Sonic.asm#L437-L457 — Sonic 1 turns him to face any edge he balances at.chkbalance: jsr (ObjFloorDist).l ; get distance of Sonic to nearest floor cmpi.w #12,d1 ; is he within 12px to floor? blt.s Sonic_LookUp ; if yes, don't do balance animation (would look awkward) cmpi.b #3,angleright(a0) ; is Sonic at the right edge of the floor? bne.s .chkleftedge ; if not, check for the left edge ; loc_12F5A: .rightbalance: bclr #status_xflip_bit,obStatus(a0) ; clear X-flip flag (make Sonic face right) bra.s .balance ; do balance animation ; =========================================================================== ; loc_12F62: …s1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/01 Sonic.asm#L716-L719 — Sonic 1 clamps a forward speed above the top to the topadd.w d5,d0 ; add acceleration to current ground speed cmp.w d6,d0 ; is new speed above max speed? blt.s .nocap ; if not, branch move.w d6,d0 ; cap Sonic's ground speeds1disasm: Sonic the Hedgehog (16-bit) disassembly (sonicretro/s1disasm) · _incObj/Sonic AnglePos.asm#L99-L101 — Sonic 1 reaches down 14 px at any speed.above_floor: cmpi.w #$E,d1 bgt.s .in_air ; branch if Sonic is > 14px above floorChecked by claude, 2026-10-26 — read both games' mode routines and every subroutine they call for flat ground at their pinned commits

The changes, with the claims that hold them: spindash-start, spindash-charge, spindash-speeds, spindash-release; forward-cap-kept; idle-blink; balance; walk-off. Unchanged (each claim says "as in Sonic 1"): speeds, friction, the skid, rolling, the jump and its release cap, gravity, air control and drag, the four air checks, landing depth, the roll jump's lock and size, the headroom check, and the wall check ahead.

Cited by: Sonic the Hedgehog, Sonic the Hedgehog 2