data/claims/s2disasm.yamlSonic 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 commitAs 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 commitCited 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 commitCited 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
}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 commitCited 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 commitsSonic_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 commitCited 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
}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 differThe 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 commitCited 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
}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 commitCited 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 ]
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 commitmove.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
}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 commitsBoth 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
}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 commitsCited 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
}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 commitSonic 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 commitCited 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
}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 commitSonic 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 commitObj01_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
}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 commitCited 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 commitCited 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
}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
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.mjsSo 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
}
}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.mjsSonic 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
}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.mjsThe 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.mjsscreen_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 pind5 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
}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 pinAfter $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
}
}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 pinw 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 pintop_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 commitThe 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
}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 commitsmvabs.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 pinCalcRoomInFront 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 pinRolling 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'sMostly 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 pinBoth 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 pinThe 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 pinA 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 pinThe 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 pinThe 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
}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 pinnext_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 pinaddq.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 pinWith 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 pinLanded 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 pinLetting 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 commitEvery 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
After Sonic has stood in his waiting animation to its 30th frame (about 175 frames), each frame with nothing held skips his movement, and the first frame with the d-pad or a jump button held but no jump press only starts a blink, which skips two more frames; from its 172nd frame he gets up instead, which skips five frames in all. s2disasm/idle-blink
About: Sonic the Hedgehog 2 Input reading
Value
{
"from": 30,
"getUpFrom": 172,
"wait": {
"dur": 5,
"n": 179,
"back": 6
},
"blink": {
"dur": 1,
"n": 1,
"to": 0
},
"getUp": {
"dur": 3,
"n": 1,
"to": 0
}
}s2.asm#L36493-L36511move.b (Ctrl_1_Press_Logical).w,d0 ; load currently pressed buttons
andi.b #button_B_mask|button_C_mask|button_A_mask,d0 ; is A, B, or C pressed?
bne.s Obj01_MdNormal ; if yes, branch
cmpi.b #AniIDSonAni_Blink,anim(a0) ; is Sonic's animation "Blink"?
beq.s Obj01_MdNormal_Skip ; if yes, branch
cmpi.b #AniIDSonAni_GetUp,anim(a0) ; is Sonic's animation "GetUp"?
beq.s Obj01_MdNormal_Skip ; if yes, branch
cmpi.b #AniIDSonAni_Wait,anim(a0) ; is Sonic's animation "Wait"?
bne.s Obj01_MdNormal ; if not, branch
cmpi.b #$1E,anim_frame(a0) ; is the animation frame $1E or higher?
blo.s Obj01_MdNormal ; if not, branch
move.b (Ctrl_1_Held_Logical).w,d0 ; load currently held buttons
…s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L36531-L36533Obj01_MdNormal_Skip:
endif
rtss2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38769-L38771SonAni_Blink: dc.b 1, 2,$FD, 0
rev02even
SonAni_GetUp: dc.b 3, $A,$FD, 0s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38747-L38759 — SonAni_Wait, 179 entries of 6 frames, then `$FE,6`s2disasm: Sonic the Hedgehog 2 (16-bit) disassembly (sonicretro/s2disasm) · s2.asm#L38432-L38461cmp.b prev_anim(a0),d0 ; has animation changed?
beq.s SAnim_Do ; if not, branch
move.b d0,prev_anim(a0) ; set previous animation
move.b #0,anim_frame(a0) ; reset animation frame
move.b #0,anim_frame_duration(a0) ; reset frame duration
bclr #status.player.pushing,status(a0)
; loc_1B384:
SAnim_Do:
add.w d0,d0
adda.w (a1,d0.w),a1 ; calculate address of appropriate animation script
move.b (a1),d0
bmi.s SAnim_WalkRun ; if animation is walk/run/roll/jump, branch
…Checked by claude, 2026-10-26 — read Obj01_MdNormal_Checks, Sonic_Animate and the animation scripts at the pinned commit, fixBugs offObj01_MdNormal_Skip is the mode's rts with fixBugs off, so a skipped frame has no move and no floor snap. Sonic_Move sets the waiting animation whenever he stands with ground speed 0 and doesn't balance, look up, or duck; it restarts when the animation changes. The blink's script is one sprite entry shown for 2 frames (duration 1), from the frame that sets it, then $FD,0, which on the third frame switches to walking; getting up shows its entry for 4 frames (duration 3), so it runs five. A frame with nothing held changes nothing on flat ground but the animation. A jump press skips the check. rev02even pads each script to an even address in revision 2 and changes none of its bytes.
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 commitsThe 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