Claim set · data/claims/mega-man-3.yaml

Mega Man 3 (plasticsmoke ca65 disassembly)

Game: Mega Man 3.

A fresh A press on the ground without Down sets Mega Man's upward speed to $04.$E5, 4.89 px/frame, and runs the airborne state at once. mega-man-3/jump-speed

About: Mega Man 3 Jump

Value: 1253 1/256 px/frame

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L59-L78player_ground_jump_or_slide: lda joy1_press ; check new button presses and #BTN_A ; A button pressed? beq player_ground_anim_dispatch ; no → animate walk/idle lda joy1_held ; check held buttons and #BTN_DOWN ; holding down? beq player_ground_normal_jump ; not holding down, jump jmp slide_initiate ; initiate slide … player_ground_normal_jump: lda joy2_held ; P2 held buttons and #$01 ; bit 0 = Right held? bne player_ground_super_jump ; yes → debug super jump lda #$E5 ; normal jump: Y vel = $04.E5 …Checked by claude, 2026-10-21 — read player_ground_jump_or_slide and player_ground_normal_jump at the pinned commit

1253/256 = 4.89453125 px/frame. Holding Right on the second controller jumps at $08.$00 instead (L80-L84), a debug jump left in the release.

Cited by: Jump, Mega Man 3

Mega Man 3 raises the take-off speed from Mega Man 2's $04.$DF (4.87 px/frame) to $04.$E5 (4.89) and keeps gravity at $40, but caps a fall at 7 px/frame ($F9) where Mega Man 2 allows 12 ($F4). mega-man-3/jump-line

About: Mega Man 3 Gravity

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L74-L77lda #$E5 ; normal jump: Y vel = $04.E5 sta ent_yvel_sub,x ; store Y velocity sub-pixel lda #$04 ; Y velocity whole = $04 sta ent_yvel,x ; store Y velocity wholeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/game_loop.asm#L648-L649prelude_setup_per_frame: lda #$40 ; gravity = $40 for player physics sta gravity ; (process_sprites sets $55 for entities)Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/movement.asm#L393-L402apply_gravity_player_check: lda ent_yvel_sub,x ; Y sub velocity -= gravity sec ; prepare for subtraction sbc gravity ; subtract gravity value sta ent_yvel_sub,x ; store new Y sub velocity lda ent_yvel,x ; Y velocity -= borrow sbc #$00 ; propagate borrow to whole byte sta ent_yvel,x ; store new Y velocity bpl apply_gravity_exit ; still positive (rising)? done cmp #$F9 ; check against terminal velocity $F9 bcs apply_gravity_exit ; within limit (-7 to -1)? donemm2: Mega Man 2 disassembly (lsmmega/mm2) · engine/check_object_horizontal_move.asm#L175-L185tsa_ycoord_speed_accelerate_fraction_table: .BYTE $40, $1E … tsa_regular_water_jump_speed_table: .BYTE $04, $05 … tsa_regular_water_jump_speed_fraction_table: .BYTE $DF, $80mm2: Mega Man 2 disassembly (lsmmega/mm2) · engine/check_object_vertical_move.asm#L81-L87BPL @skip CMP #$F4 BCS @skip LDA #$00 STA aobject_ycoord_speed_fraction LDA #$F4 STA aobject_ycoord_speedChecked by claude, 2026-10-21 — read Mega Man 3's jump start, the prelude that sets `gravity` before the player's state runs, and apply_gravity (the clamp below L402 stores `$F9`.`$00`), and Mega Man 2's tables and fall cap, at their pinned commits

The prelude writes $40 every frame; enemies processed later overwrite gravity for themselves, so the player always reads $40 out of water.

Cited by: Gravity, Mega Man 3

On every rising frame without A held, Mega Man 3 sets the vertical speed to $FF.$C0, 0.25 px/frame downward, whatever the speed was, before the frame's move. Mega Man 2 only lowers a rise of 2 px/frame or more to 1, after the move. mega-man-3/release-cut

About: Mega Man 3 Variable jump height

Value: -64 1/256 px/frame

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L260-L267player_airborne: lda ent_yvel ; check Y velocity sign bmi player_airborne_variable_jump ; negative (falling) → skip to clear flag lda force_full_jump ; check force-full-jump flag bne player_airborne_wall_check ; nonzero → skip variable jump lda joy1_held ; check if A button held and #BTN_A ; A button mask bne player_airborne_variable_jump ; held → keep rising (variable jump) jsr reset_gravity ; A released → clamp Y speed to 0 (variable jump)Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/movement.asm#L424-L428reset_gravity_player: lda #$C0 ; player rest vel sub = $C0 sta ent_yvel_sub,x ; store Y sub velocity lda #$FF ; player rest vel = $FF ($FFC0 = -0.25) sta ent_yvel,x ; store Y velocity whole byte rts ; player rest vel = $FFC0Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L272-L277player_airborne_wall_check: ldy #$06 ; Y=6: check at foot height jsr check_tile_horiz ; (Y=6: check at foot height) lda $10 ; A = tile collision result sta $0F ; save in $0F ldy #$00 ; Y=0 for vertical movement jsr move_vertical_gravity ; apply gravity and move verticallymm2: Mega Man 2 disassembly (lsmmega/mm2) · engine/run_megaman.asm#L208-L219LDA z:zjoy1_pressed AND #a_button BNE @skip LDA aobject_ycoord_speed BMI @skip CMP #$01 BCC @skip BEQ @skip LDA #$01 STA aobject_ycoord_speed LDA #$00 STA aobject_ycoord_speed_fractionChecked by claude, 2026-10-21 — read player_airborne, reset_gravity, and move_vertical_gravity (move first, then apply_gravity) in Mega Man 3, and _run_megaman_jumping in Mega Man 2 (mega-man-2/release-cap, mega-man-2/release-order), at their pinned commits. The comment at L267 says 0; the code stores `$FF`.`$C0`

The test reads only the sign of the whole byte, so it also cuts a rise slower than 1 px/frame. A Rush Coil bounce sets force_full_jump, which skips the cut until the fall (rush-coil).

Cited by: Mega Man 3, Variable jump height

Mega Man 3 drops Mega Man 2's slow step speed but keeps a pause. Before each frame's state runs, the prelude sets the speed to $01.$4C (1.30 px/frame) unless he is sliding. From standing, a held direction moves him by it at once and starts the turning animation ($0D); while that animation is on its first frame the ground state doesn't move him, and once it advances he walks at the same 1.30. In the air a held direction moves him at 1.30 on every frame. mega-man-3/walk-speed

About: Mega Man 3 Running

Value: 332 1/256 px/frame

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/game_loop.asm#L636-L645prelude_check_slide_speed: lda ent_xvel ; if X speed whole == $02 (slide speed): cmp #$02 ; slide speed? bne prelude_reset_walk_speed ; no → reset to walk speed lda player_state ; load current player state cmp #PSTATE_SLIDE ; check if sliding beq prelude_setup_per_frame ; sliding, keep slide speed prelude_reset_walk_speed: lda #$4C ; reset walk speed to $01.4C sta ent_xvel_sub ; store walk sub-pixel speed lda #$01 ; walk speed whole = $01 sta ent_xvel ; store walk whole speedMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L115-L128player_ground_turning_anim: lda #$0D ; OAM $0D = turning animation jsr reset_sprite_anim ; set turning animation lda shoot_anim_timer ; check if shooting beq player_ground_move_facing ; not shooting, move facing jsr shoot_oam_advance_variant ; update shoot animation variant player_ground_move_facing: lda player_facing ; move in facing direction jmp player_ground_move_horizontal ; move in facing direction … player_ground_walk_handler: lda joy1_held ; D-pad L/R held? and #$03 ; mask L/R bits beq player_ground_idle ; no → idle sta player_facing ; update facing directionMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L94-L102 — on the turning animation's first frame (state 0) the handler skips the movecmp #$0D ; $0D/$0E/$0F = turning/special anims beq player_ground_special_anim ; OAM $0D → check if anim complete … player_ground_special_anim: lda ent_anim_state ; check animation state bne player_ground_walk_handler ; → normal walk handler beq player_ground_slide_weapon ; anim done → slide/crouch checkMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/sprites.asm#L210-L217lda ent_anim_state,x ; ent_anim_state = current frame index and #$7F ; (bit 7 preserved) inc ent_anim_state,x ; advance to next frame … lda #$00 ; reached end: loop back to frame 0 sta ent_anim_state,x ; reset to frame 0Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L360-L364player_airborne_walk_shoot: lda joy1_held ; D-pad L/R held? and #$03 ; mask L/R bits beq player_airborne_shoot_check ; no → skip walk sta player_facing ; update facing direction jsr player_ground_move_horizontal ; move L/R with collisionMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/movement.asm#L259-L265move_sprite_right: lda ent_x_sub,x ; load X subpixel position clc ; X subpixel += X subpixel speed adc ent_xvel_sub,x ; add X subpixel velocity sta ent_x_sub,x ; store new X subpixel lda ent_x_px,x ; load X pixel position adc ent_xvel,x ; add X pixel velocity + carry sta ent_x_px,x ; store new X pixelmm2: Mega Man 2 disassembly (lsmmega/mm2) · engine/turn_megaman.asm#L222-L243megaman_status_xcoord_speed_table: … megaman_status_xcoord_speed_fraction_table: .BYTE $00 .BYTE $00 .BYTE $90 .BYTE $00 .BYTE $20 .BYTE $60 .BYTE $50 .BYTE $80Checked by claude, 2026-10-21 — read the prelude, the ground animation dispatch and walk handler (from idle, animation `$01` goes to player_ground_turning_anim, which sets `$0D` and moves), the animation step in sprites.asm, the airborne walk, and move_sprite_right in Mega Man 3, and Mega Man 2's state speed table (mega-man-2/state-speeds), at their pinned commits

332/256 = 1.296875 px/frame. The turning animation holds its first frame for 6 ticks (turn-animation), which makes a pause of 5 frames after the step (ground-measured). Mega Man 2 instead moves at 0.125 px/frame during its step.

Cited by: Air control, Mega Man 3, Running

Down and a fresh A press on the ground start a slide if no wall is ahead: 2 px lower, at 2.5 px/frame ($02.$80), with a timer of 20 ($14). The set-up frame moves once, and each slide frame moves before it tests the timer, so an unbroken slide moves on 21 frames. A turn, a wall, the timer's end, or (from the 9th frame) a fresh A press all lead to the standing test, where a solid tile at standing height keeps the slide going; otherwise it ends, with a jump at walking speed if A was pressed. mega-man-3/slide

About: Mega Man 3 Ground slide

Value
{
 "speed": 640,
 "frames": 21,
 "jumpFromFrame": 9
}
VerifiedMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L286-L299slide_initiate_confirmed: lda #PSTATE_SLIDE ; state $02 = player_slide sta player_state ; set player state to slide lda #$14 ; slide timer = 20 frames sta $33 ; store slide timer lda #$10 ; OAM anim $10 = slide sprite jsr reset_sprite_anim ; set slide sprite animation lda ent_y_px ; player Y += 2 (crouch posture) clc ; prepare for addition adc #$02 ; lower Y by 2 for slide crouch sta ent_y_px ; store adjusted Y position lda #$80 ; X speed = $02.80 = 2.5 px/frame sta ent_xvel_sub ; X velocity sub-pixel …Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L318 — the end of the set-up, which moves on the same framebeq slide_move_check_facing ; always → skip to slide movementMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L344-L366slide_collision_wall_solid: lda $10 ; hit a wall? and #$10 ; bit 4 = solid wall bne slide_end_ground_check ; yes → end slide lda $33 ; timer expired? beq slide_end_ground_check ; yes → end slide dec $33 ; decrement timer lda $33 ; check slide timer phase cmp #$0C ; >= $0C? (uncancellable phase) bcs slide_continue_moving ; yes → uncancellable, keep sliding lda joy1_press ; A button pressed? (cancellable phase) and #BTN_A ; check A button bne slide_end_ground_check ; yes → cancel into slide jump …Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L328-L334lda joy1_held ; read held D-pad and #$03 ; mask left/right bits beq slide_move_check_facing ; no direction → keep sliding cmp player_facing ; same as current facing? beq slide_move_check_facing ; yes → keep sliding sta player_facing ; changed direction → end slide bne slide_end_ground_check ; always taken → end slideMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L367-L390sta player_state ; state = on_ground lda joy1_press ; A button pressed? and #BTN_A ; check A button bne slide_jump_walk_speed_init ; yes → slide jump … slide_jump_walk_speed_init: lda #$4C ; walk speed X = $01.4C sta ent_xvel_sub ; X velocity sub = $4C lda #$01 ; X velocity whole = 1 sta ent_xvel ; X velocity = 1 jmp player_ground_normal_jump ; jump from slide (uses slide_jump velocity)Checked by claude, 2026-10-26 — ran movement.mjs (slide, slidejump8, slidejump9 from Top Man's stage start in Mesen) and re-read; earlier, read player_ground_jump_or_slide (Down + A), slide_initiate with its wall test, player_slide, and slide_end at the pinned commit. The set-up frame moves and takes the timer to 19; frames 2 to 20 take it to 0, and frame 21 moves, finds 0, and goes to the standing test. The timer reaches 11 on the 9th frame, the first that tests A. Any solid result from check_tile_horiz with Y=1 returns before the state changes (L361-L365), and the turn (L333-L334), wall (L344-L346), and A (L353-L355) exits all branch thereProbe: probes/jump-physics/movement.mjs

The comments call the Y=1 test "ground at standing height"; with the 2 px drop it reads as headroom, as Mega Man 4's labels name the same test (mega-man-4/slide-line). Measured in Mesen from Top Man's stage start (movement.mjs), a slide moves 2.5 px on 21 frames, and a fresh A on its 8th frame keeps it going while one on its 9th jumps; the lab model matches the cartridge on the conformance probe's slide scripts.

Cited by: Ground slide, Mega Man 2, Mega Man 3

A hit (state $06) sets Mega Man's vertical speed to the resting $FF.$C0 on its first frame, then each frame applies gravity and pushes him against his facing at 0.5 px/frame ($00.$80) until the hurt animation's state reaches 9; a hit during a slide doesn't push him. mega-man-3/knockback

About: Mega Man 3 Knockback

Value
{
 "back": 128
}
CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L750-L753player_damage_anim_set: jsr reset_sprite_anim ; set player damage animation lda #$00 ; clear walk/shoot flag sta shoot_anim_timer ; clear walk/shoot flag jsr reset_gravity ; reset vertical velocityMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L776-L790player_knockback_physics: lda $3E ; check saved OAM for special cases: cmp #$10 ; $10 = sliding (no knockback physics) beq player_knockback_landing ; sliding → skip knockback cmp #$79 ; >= $79 = special state (skip physics) bcs player_knockback_landing ; special OAM → skip knockback physics ldy #$00 ; Y = 0 (player slot) jsr move_vertical_gravity ; apply gravity, carry = landed lda #$80 ; knockback speed = $00.80 (half pixel/frame) sta ent_xvel_sub ; set knockback X speed sub lda #$00 ; knockback X speed whole = 0 sta ent_xvel ; x_speed whole = 0 lda ent_flags ; save facing before move …Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L806-L808player_knockback_landing: lda ent_anim_state ; ent_anim_state = animation/collision state cmp #$09 ; $09 = landed after knockback bne player_knockback_done ; not landed → continue knockbackThe atlas reads this differently: State 9 is a step of the hurt animation, not a landing: the code compares only the animation state, so the push lasts as long as the animation takes to reach it.Checked by claude, 2026-10-21 — read player_damage from its first-frame setup through player_knockback_done at the pinned commit; bit 6 set (facing right) branches to move_left_collide

The comment reads ent_anim_state 9 as a landing; the code only compares the animation state, so the push lasts as long as the hurt animation takes to reach it. Mega Man 2 lifts him at 1.25 px/frame and pushes at 0.5625 (mm2-line/knockback).

Cited by: Knockback, Mega Man 2, Mega Man 3

Mega Man 3 starts the invincibility after the knockback: when the hurt state ends it sets invincibility_timer to 60 ($3C), which the prelude counts down every frame. Mega Man 2 sets 111 at the moment of the hit. mega-man-3/i-frames

About: Mega Man 3 Invincibility frames

Value: 60 frames

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L828-L830player_knockback_set_invincible: sta player_state ; set return player state lda #$3C ; $39 = $3C (60 frames invincibility) sta invincibility_timer ; 60 frames of invincibilityMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/game_loop.asm#L657-L659prelude_invincibility_timer: lda invincibility_timer ; load invincibility timer beq prelude_charge_shot_check ; zero → skip dec invincibility_timer ; decrement timerMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/collision.asm#L141-L142lda invincibility_timer ; skip if invincibility active bne metatile_cleanup_return ; immune → skip hazard checkmm2: Mega Man 2 disassembly (lsmmega/mm2) · home/megaman_hit.asm#L11-L12LDA #$6F STA z:zmegaman_invincibility_timeChecked by claude, 2026-10-21 — read player_knockback_set_invincible, the prelude's countdown, and the hazard test in Mega Man 3, and _megaman_hit in Mega Man 2, at their pinned commits

The atlas hasn't traced how Mega Man 3 ignores enemy contact during the hurt state itself, nor measured its length; collision.asm L317 tests the same timer before entity contact.

Cited by: Invincibility frames, Mega Man 3

Mega Man 3 moves weapon costs into two tables indexed by weapon: weapon_cost, the units taken, and weapon_framerate, how many uses (or frames, for Rush Marine and Rush Jet) each take costs, counted on one shared counter. Needle Cannon costs 1 unit per 4 shots and Gemini Laser 2 per shot. Mega Man 2 writes the costs into the weapons' own routines, as its Air Shooter, Crash Bomber, and Metal Blade code shows. mega-man-3/weapon-cost

About: Mega Man 3 Weapon energy

Value
{
 "cost": [
  0,
  2,
  1,
  2,
  2,
  0,
  1,
  3,
  1,
  1,
  1,
  1
 ],
 "uses": [
  0,
  1,
  4,
  1,
  1,
  0,
  2,
  1,
  1,
  30,
  2,
  30
 ]
}
CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_warp.asm#L254-L268decrease_ammo_tick: ldy current_weapon ; Y = current weapon index inc $B5 ; increment usage frame counter lda $B5 ; load current count cmp weapon_framerate,y ; compare to drain rate for this weapon bne decrease_ammo_return ; not at threshold → return lda #$00 ; reset frame counter to 0 sta $B5 ; store reset counter lda player_hp,y ; load weapon ammo and #$1F ; mask to ammo value (low 5 bits) sec ; subtract ammo cost sbc weapon_cost,y ; subtract ammo cost bcs ammo_store_clamped ; no underflow → store result …Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_warp.asm#L289-L296weapon_cost: .byte $00,$02,$01,$02,$02,$00,$01,$03 .byte $01,$01,$01,$01 … weapon_framerate: .byte $00,$01,$04,$01,$01,$00,$02 .byte $01,$01,$1E,$02,$1E ; Spark $01, R.Marine $1E, Shadow $02, R.Jet $1EMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · include/constants.inc#L64-L75WPN_BUSTER = $00 ; Mega Buster (arm cannon) WPN_GEMINI = $01 ; Gemini Laser WPN_NEEDLE = $02 ; Needle Cannon … WPN_RUSH_JET = $0B ; Rush Jetmm2: Mega Man 2 disassembly (lsmmega/mm2) · home/weapons.asm#L95-L97 — Air ShooterLDA z:zweapon_energy + $01 SBC #$02 STA z:zweapon_energy + $01mm2: Mega Man 2 disassembly (lsmmega/mm2) · home/weapons.asm#L212-L215 — Crash BomberLDA z:zweapon_energy + $07 SBC #$04 BCC @skip_8 STA z:zweapon_energy + $07mm2: Mega Man 2 disassembly (lsmmega/mm2) · home/weapons.asm#L244-L250INC z:zweapon_consume_fraction LDA z:zweapon_consume_fraction CMP #$04 BNE @not_max_3 LDA #$00 STA z:zweapon_consume_fraction DEC z:zweapon_energy + $06Checked by claude, 2026-10-21 — read decrease_ammo, decrease_ammo_tick, both tables, and the weapon ids in Mega Man 3, and Mega Man 2's Metal Blade cost (mm2-line/weapon-energy), at their pinned commits. The comment above weapon_framerate says frames; for shots the routine runs once per shot (weapon_fire, L387-L388 of player_ground.asm)

Top Spin's row is 0 here; it costs energy elsewhere, on contact, which the atlas hasn't traced.

Cited by: Mega Man 3, Weapon energy

Mega Man 3 indexes each weapon's 256-byte damage table by the target's main routine id, one table for regular enemies and bosses alike, where Mega Man 2 gives each weapon a separate 14-entry boss table. In the Spark Shock table the entry for routine id $C1 is 7, and $58 marks targets the shock freezes instead of damaging. That $C1 is Magnet Man is the disassembly's labeling, not checked here. mega-man-3/boss-weakness

About: Mega Man 3 Boss weakness

Value
{
 "sparkShockVsMagnetMan": 7
}
CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/bank1C_1D_entity_ai.asm#L290-L299ldy current_weapon ; weapon ID as table index lda weapon_damage_ptr_lo,y ; grab damage table pointer for sta temp_00 ; currently equipped weapon lda weapon_damage_ptr_hi,y ; high byte of damage ptr sta $01 ; store to pointer at $00-$01 ldy ent_routine,x ; if damage for main routine index lda (temp_00),y ; is nonzero, do stuff bne check_weapon_hit_spark_check ; nonzero damage → check weapon type jsr check_weapon_hit_tink_sound ; else tink shot jmp check_weapon_hit_restore_bank ; zero damage → tink and exitMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/bank1C_1D_entity_ai.asm#L562-L565weapon_damage_ptr_lo: .byte $00,$00,$00,$00,$00 ; Buster, Gemini, Needle, Hard, Magnet weapon_damage_ptr_lo_special: .byte $00,$00,$00,$00,$00,$00,$00 ; TopSpin, Snake, RushCoil, Spark, RushMarine, Shadow, RushJet weapon_damage_ptr_hi: .byte $A1,$A4,$A2,$A5,$A3 ; Buster, Gemini, Needle, Hard, Magnet weapon_damage_ptr_hi_special: .byte $A6,$A7,$A1,$A8,$A1,$A9,$A1 ; TopSpin, Snake, RushCoil, Spark, RushMarine, Shadow, RushJetMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/bank0A_damage_tables.asm#L424-L425spark_damage_table: .byte $00,$00,$58,$58,$58,$58,$00,$58 ; $00-$07: $58=freezeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/bank0A_damage_tables.asm#L451.byte $00,$07,$01,$01,$00,$00,$00,$00 ; $C0: Needle=0, Magnet=7 (weakness!), Top=1, Shadow=1Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/bank1C_1D_entity_ai.asm#L301-L307check_weapon_hit_spark_check: lda current_weapon ; if weapon is cmp #WPN_SPARK ; anything but spark bne check_weapon_hit_ack_check ; apply normal damage lda (temp_00),y ; if damage value is zero, beq check_weapon_hit_spark_freeze_done ; don't do anything cmp #$58 ; if damage isn't magic number $58, bne check_weapon_hit_ack_check ; apply normal damagemm2: Mega Man 2 disassembly (lsmmega/mm2) · engine/weapons_bosses_damage.asm#L388-L392LDA aobject_flag + 1 AND #objects_can_recoiled BNE @immune_8 LDY z:zcurrent_boss LDA @metal_blade_damage_table, YChecked by claude, 2026-10-21 — read check_weapon_hit's lookup, the pointer tables (Spark Shock, weapon 8, is the fourth special entry, `$A8`), and the Spark Shock table's first and `$C0` rows in Mega Man 3, and Mega Man 2's Metal Blade lookup (mm2-line/boss-weakness), at their pinned commits

Which routine id belongs to which Robot Master comes from the disassembly's annotations, which the atlas hasn't checked against the spawn data.

Cited by: Boss weakness, Mega Man 3

Landing on Rush Coil launches Mega Man at $06.$EE, 6.93 px/frame, once per bounce, and sets force_full_jump, so releasing A doesn't cut that rise. mega-man-3/rush-coil

About: Mega Man 3 Jump

Value: 1774 1/256 px/frame

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L32-L38lda $05A1 ; if Rush already bounced: skip bne player_ground_normal_walk ; already bounced, normal walk inc force_full_jump ; force full jump (no variable-jump cutoff) lda #$EE ; Y speed = $06.EE (Rush Coil bounce) sta ent_yvel_sub ; strong upward bounce lda #$06 ; Y velocity whole = $06 (upward) sta ent_yvel ; store Y velocity wholeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L260-L263player_airborne: lda ent_yvel ; check Y velocity sign bmi player_airborne_variable_jump ; negative (falling) → skip to clear flag lda force_full_jump ; check force-full-jump flag bne player_airborne_wall_check ; nonzero → skip variable jumpChecked by claude, 2026-10-21 — read the Rush Coil handler reached from player_ground's Rush dispatch and player_airborne at the pinned commit; the flag clears at L268-L269 only once the speed turns negative

1774/256 = 6.9296875 px/frame. The bounce also takes Rush Coil's energy through decrease_ammo_tick (L45).

Cited by: Jump, Mega Man 3

Mega Man 3 moves Mega Man vertically before horizontally. Every frame on the ground or in the air first runs the airborne routine: a rise without A held is cut (release-cut), then move_vertical_gravity moves him by his vertical speed, takes gravity from it, and tests the floor under a fall or the ceiling over a rise. Only then does the frame's horizontal move run: the air move if he is still airborne, the ground state's move if he stood on a floor. mega-man-3/motion-order

About: Mega Man 3 Gravity

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L4-L7 — the ground state runs the airborne routine before anything elseplayer_on_ground: jsr player_airborne ; run $99/collision first bcc prelude_exit ; carry clear = still airborne → RTSMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L260-L278player_airborne: lda ent_yvel ; check Y velocity sign bmi player_airborne_variable_jump ; negative (falling) → skip to clear flag lda force_full_jump ; check force-full-jump flag bne player_airborne_wall_check ; nonzero → skip variable jump lda joy1_held ; check if A button held and #BTN_A ; A button mask bne player_airborne_variable_jump ; held → keep rising (variable jump) jsr reset_gravity ; A released → clamp Y speed to 0 (variable jump) … player_airborne_wall_check: ldy #$06 ; Y=6: check at foot height jsr check_tile_horiz ; (Y=6: check at foot height) lda $10 ; A = tile collision result …Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L343-L364 — still airborne: the air move comes after the vertical oneplayer_airborne_state_dispatch: lda player_state ; check current player state cmp #PSTATE_LADDER ; state $03 = on ladder beq player_airborne_exit ; on ladder → just return cmp #PSTATE_AIRBORNE ; state $01 = already airborne beq player_airborne_walk_shoot ; already airborne → walk+shoot … player_airborne_walk_shoot: lda joy1_held ; D-pad L/R held? and #$03 ; mask L/R bits beq player_airborne_shoot_check ; no → skip walk sta player_facing ; update facing direction jsr player_ground_move_horizontal ; move L/R with collisionMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/movement.asm#L176-L223gravity_save_y: lda ent_yvel,x ; Y velocity whole byte bpl gravity_rising ; positive = rising jsr apply_y_velocity_fall ; pos -= vel (moves down) … gravity_apply_physics: jsr apply_gravity ; increase downward velocity jsr check_tile_horiz ; vertical tile collision check … gravity_rising: iny ; Y=1: upward collision check offset jsr apply_y_velocity_rise ; pos -= vel (moves up) … gravity_apply_rise: jsr apply_gravity ; apply gravity (slows rise) jsr check_tile_horiz ; vertical tile collision checkChecked by claude, 2026-10-26 — read player_on_ground, player_airborne through the state dispatch and the air move, and move_vertical_gravity's falling and rising branches at the pinned commit; the falling branch moves (apply_y_velocity_fall), applies gravity, then tests with check_tile_horiz, and the rising branch does the same after INY selects the ceiling points

Mega Man 2 runs the other way round: the horizontal move with its wall test first, then the vertical move (mega-man-2/collision-order). A landing ends the airborne routine before the air move, so the landing frame moves him only vertically (landing).

Gravity takes $40/256 = 0.25 px/frame from Mega Man's upward speed every frame out of water, after the frame's vertical move. mega-man-3/gravity

About: Mega Man 3 Gravity

Value: 64 1/256 px/frame²

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/game_loop.asm#L648-L649prelude_setup_per_frame: lda #$40 ; gravity = $40 for player physics sta gravity ; (process_sprites sets $55 for entities)Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/movement.asm#L393-L399apply_gravity_player_check: lda ent_yvel_sub,x ; Y sub velocity -= gravity sec ; prepare for subtraction sbc gravity ; subtract gravity value sta ent_yvel_sub,x ; store new Y sub velocity lda ent_yvel,x ; Y velocity -= borrow sbc #$00 ; propagate borrow to whole byte sta ent_yvel,x ; store new Y velocityChecked by claude, 2026-10-26 — read the prelude, which sets `gravity` before the player's state runs, and apply_gravity at the pinned commit

The same as Mega Man 2 (mega-man-2/gravity). A water landing's cooldown in $B9 skips gravity for a few frames (apply_gravity L387-L392); the atlas models ordinary floor only.

A fall is capped at 7 px/frame: when gravity takes the speed's whole byte below $F9, apply_gravity sets it to $F9.$00. mega-man-3/max-fall

About: Mega Man 3 Gravity

Value: -7 px/frame

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/movement.asm#L400-L409bpl apply_gravity_exit ; still positive (rising)? done cmp #$F9 ; check against terminal velocity $F9 bcs apply_gravity_exit ; within limit (-7 to -1)? done lda ent_anim_id,x ; check entity animation ID cmp #$13 ; $13 = exempt from terminal vel clamp beq apply_gravity_exit ; special entity → no clamp lda #$F9 ; clamp to terminal velocity sta ent_yvel,x ; clamp Y velocity to $F9.00 lda #$00 ; clear sub velocity sta ent_yvel_sub,x ; clear Y sub velocityChecked by claude, 2026-10-26 — read the clamp at the end of apply_gravity at the pinned commit; the exemption is for animation `$13`, which the player's normal states don't use

Mega Man 2 caps a fall at 12 px/frame (mega-man-2/max-fall). The cap isn't reached on the flat floor of the start area, where a full jump lands at about 4.9 px/frame.

Mega Man's position is his center. A fall tests the floor at x − 7, x, and x + 7, 12 px below it, and a rise tests the ceiling at x − 7 and x + 7, 12 px above it; a horizontal move tests x + 8 or x − 8 at 11 px above, level with, and 11 px below the center. Each point reads the 16 px tile holding it. mega-man-3/terrain-box

About: Mega Man 3 Hitbox

Value
{
 "tile": 16,
 "floor": {
  "x": [
   -7,
   0,
   7
  ],
  "y": 12
 },
 "ceiling": {
  "x": [
   -7,
   7
  ],
  "y": -12
 },
 "wall": {
  "x": 8,
  "y": [
   -11,
   0,
   11
  ]
 }
}
CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/collision.asm#L491-L499 — hitbox_h_* rows 0 and 1: from start index 0, 3 points ($02) at Y + $0C, X −7 then +7 and +7 again; from index 5, 2 points ($01) at Y − $0C, X −7 then +14hitbox_h_start_idx: .byte $00,$05,$09,$0E,$12,$16,$1A,$1F … hitbox_h_count: .byte $02 hitbox_h_y_offset: .byte $0C hitbox_h_x_offsets: .byte $F9,$07,$07,$01,$F4,$F9,$0E,$02Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/collision.asm#L525-L532 — hitbox_v_* rows 0 and 1: 3 points at X + 8, Y −11 then +11 and +11 again; from index 5, the same at X − 8hitbox_v_start_idx: .byte $00,$05,$0A,$0E,$12,$17,$1C,$21 … hitbox_v_count: .byte $02 hitbox_v_x_offset: .byte $08 hitbox_v_y_offsets: .byte $F5,$0B,$0B,$02,$F8,$F5,$0B,$0BMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/collision.asm#L64-L77 — each point's X offset adds to the last one's; the tile column is X / 16lda hitbox_h_x_offsets,y ; first X offset (signed) bpl metatile_compute_x_pos ; positive → skip sign extension dec $04 ; negative: $04 = $FF metatile_compute_x_pos: clc ; $12/$13 = entity_X + X_offset adc ent_x_px,x ; $12 = X pixel sta $12 ; store X pixel position … lda $12 ; X >> 4 = tile column lsr a ; divide X by 16 lsr a ; to get tile column index lsr a ; (16 pixels per tile) lsr a ; A = X / 16Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L153-L159 — a walk or air move to the right uses wall row 0, to the left row 1player_ground_move_horizontal: and #$01 ; bit 0: right? beq player_ground_move_left ; left direction, branch ldy #$00 ; move right with collision jmp move_right_collide ; jump to right move handler player_ground_move_left: ldy #$01 ; move left with collision jmp move_left_collide ; jump to left move handlerMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/movement.asm#L208 — a rise moves the floor row 0 to the ceiling row 1gravity_rising: iny ; Y=1: upward collision check offsetChecked by claude, 2026-10-26 — read check_tile_horiz and check_tile_collision, which add each offset to the running point, the two offset tables, player_ground_move_horizontal's row choice, and move_vertical_gravity's INY at the pinned commit; the offsets are signed bytes

Mega Man 2 tests the same floor and ceiling corners without the middle floor point, and its wall points at 12 and 4 px above and 11 px below the center (mega-man-2/terrain-box). With 16 px tiles a middle point 7 px from both corners never finds a tile they miss.

When a fall's floor points find a solid tile, snap_y_to_floor subtracts (Y + 12) mod 16 from Mega Man's Y pixel, which puts his feet on the tile's top, and leaves the Y fraction as it was; his vertical speed becomes the resting $FF.$C0, 0.25 px/frame down. Every grounded frame repeats the fall, so standing still his Y pixel holds while the fraction steps by $40 each frame: in Top Man's stage start in Mesen it cycles through $00, $40, $80, and $C0. mega-man-3/floor-snap

About: Mega Man 3 Jump

Value: -64 1/256 px/frame

VerifiedMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/collision.asm#L626-L634 — only ent_y_px changes; ent_y_sub is never writtensnap_y_to_floor: lda $11 ; save original $11 pha ; save original $11 and #$0F ; offset = $11 & $0F sta $11 ; (sub-tile penetration depth) lda ent_y_px,x ; Y pixel -= offset sec ; (push entity up) sbc $11 ; subtract penetration depth sta ent_y_px,x ; store corrected Y pixel bcs metatile_restore_result ; no underflow? doneMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/movement.asm#L203-L206gravity_snap_floor: jsr snap_y_to_floor ; snap to tile boundary (down) gravity_reset_vel: jsr reset_gravity ; reset velocity to rest value sec ; C=1: landed rts ; return C=1 (on ground)Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/movement.asm#L424-L428reset_gravity_player: lda #$C0 ; player rest vel sub = $C0 sta ent_yvel_sub,x ; store Y sub velocity lda #$FF ; player rest vel = $FF ($FFC0 = -0.25) sta ent_yvel,x ; store Y velocity whole byte rts ; player rest vel = $FFC0Checked by claude, 2026-10-26 — read snap_y_to_floor, the landing branch of move_vertical_gravity, and reset_gravity at the pinned commit, and ran movement.mjs, whose stand case records the Y fraction over 12 frames from Top Man's stage start in MesenProbe: probes/jump-physics/movement.mjs

Mega Man 2 zeroes the Y fraction on every snap (mega-man-2/floor-snap), so its jumps all start from a whole pixel; in Mega Man 3 a jump starts from whichever fraction that frame's cycle reached, and a landing keeps the fraction the fall left, so his height can differ by under a pixel between two jumps with the same input.

A grounded frame tests the floor before it moves Mega Man: if no floor point under his last position is solid, he falls 0.25 px, takes the jumping animation and the airborne state, and that frame's horizontal move is the air move, 1.30 px/frame toward a held direction or none. No grace period follows: only the ground state reads a jump press, and it runs only on frames that found a floor. mega-man-3/walk-off

About: Mega Man 3 Coyote time

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L4-L7player_on_ground: jsr player_airborne ; run $99/collision first bcc prelude_exit ; carry clear = still airborne → RTSMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L343-L364player_airborne_state_dispatch: lda player_state ; check current player state cmp #PSTATE_LADDER ; state $03 = on ladder beq player_airborne_exit ; on ladder → just return cmp #PSTATE_AIRBORNE ; state $01 = already airborne beq player_airborne_walk_shoot ; already airborne → walk+shoot ; --- first airborne frame: set jump animation --- lda #$07 ; OAM $07 = jump/fall animation jsr reset_sprite_anim ; set jump/fall animation lda #PSTATE_AIRBORNE ; set state to airborne ($01) sta player_state ; store player state … player_airborne_walk_shoot: lda joy1_held ; D-pad L/R held? …Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L59-L61 — the jump press is read only past the bcc at L7, on a frame that found a floorplayer_ground_jump_or_slide: lda joy1_press ; check new button presses and #BTN_A ; A button pressed? beq player_ground_anim_dispatch ; no → animate walk/idleChecked by claude, 2026-10-26 — read player_on_ground, which returns at L7 when the airborne routine finds no floor, and the airborne routine's first-frame branch at the pinned commit

So he leaves a ledge one frame later than Mega Man 2, which tests the floor at the frame's new X (mega-man-2/walk-off), and the walk-off frame gives him the air move even where the walk would have paused.

When a horizontal move puts a wall point in a solid tile, the move routine pushes Mega Man back by the point's depth into the tile, so x + 8 ends on the wall's first column moving right and x − 8 on the first column past it moving left. The X fraction stays as it was, and his speed and state are untouched, so he walks in place against the wall. mega-man-3/wall-stop

About: Mega Man 3 Hitbox

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/movement.asm#L45-L53mr_tile_check: jsr check_tile_collision ; check tiles at entity position jsr update_collision_flags ; update ladder/collision flags clc ; clear carry (no collision yet) lda $10 ; tile collision result and #$10 ; isolate solid tile bit beq move_right_collision_exit ; not solid → return clear jsr snap_x_to_wall_right ; snap to tile boundary (right) sec ; set carry = hit solid move_right_collision_exit: rts ; return collision result in CMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/collision.asm#L565-L571 — pushing left: X − (point mod 16); ent_x_sub is never writtensnap_x_to_wall_right: lda $12 ; offset = $12 & $0F and #$0F ; (sub-tile penetration depth) sta $12 ; store sub-tile penetration lda ent_x_px,x ; X pixel -= offset sec ; (push entity left) sbc $12 ; subtract penetration depth sta ent_x_px,x ; store corrected X pixelMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/collision.asm#L585-L590 — pushing right: X + 16 − (point mod 16)snap_x_to_wall_left: lda $12 ; offset = ($12 & $0F) ^ $0F and #$0F ; = 15 - sub-tile position eor #$0F ; invert to get complement sec ; X pixel += (16 - sub_tile_pos) adc ent_x_px,x ; SEC + ADC = add offset + 1 sta ent_x_px,x ; (push entity right)Checked by claude, 2026-10-26 — read mr_tile_check and snap_x_to_wall_right and snap_x_to_wall_left at the pinned commit; neither touches ent_x_sub

Mega Man 2 zeroes the X fraction at a wall (mega-man-2/wall-stop).

When a rise puts a ceiling point, x − 7 or x + 7 at 12 px above his center, in a solid tile, snap_y_to_ceil adds 16 − ((Y − 12) mod 16) to Mega Man's Y pixel, which puts that point on the row under the tile, keeps the Y fraction, and sets the resting speed, 0.25 px/frame down; he stays airborne. Since a jump runs the airborne routine on its press frame, a ceiling within the first frame's 4.89 px is met on that frame. mega-man-3/ceiling-hit

About: Mega Man 3 Hitbox

Value: -64 1/256 px/frame

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/movement.asm#L222-L231gravity_apply_rise: jsr apply_gravity ; apply gravity (slows rise) jsr check_tile_horiz ; vertical tile collision check jsr update_collision_flags ; update ladder/collision flags lda $10 ; bit 4 = solid tile? and #$10 ; isolate solid tile bit beq gravity_airborne_exit ; no solid → continue rising jsr snap_y_to_ceil ; snap to tile boundary (up) gravity_snap_ceil: jsr reset_gravity ; reset velocity gravity_airborne_exit: clc ; C=0: airborne rts ; return C=0 (in air)Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/collision.asm#L604-L609snap_y_to_ceil: lda $11 ; offset = ($11 & $0F) ^ $0F and #$0F ; isolate sub-tile position eor #$0F ; invert to get complement sec ; Y pixel += (16 - sub_tile_pos) adc ent_y_px,x ; (push entity down) sta ent_y_px,x ; store corrected Y pixelMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L71-L78 — the jump press runs the airborne routine at onceplayer_ground_normal_jump: lda joy2_held ; P2 held buttons and #$01 ; bit 0 = Right held? bne player_ground_super_jump ; yes → debug super jump lda #$E5 ; normal jump: Y vel = $04.E5 sta ent_yvel_sub,x ; store Y velocity sub-pixel lda #$04 ; Y velocity whole = $04 sta ent_yvel,x ; store Y velocity whole jmp player_airborne ; enter airborne stateChecked by claude, 2026-10-26 — read the rising branch of move_vertical_gravity, snap_y_to_ceil, and player_ground_normal_jump at the pinned commit

Mega Man 2 meets a ceiling only from the frame after the press (mega-man-2/jump-under-ceiling) and zeroes the Y fraction at the hit (mega-man-2/ceiling-hit).

The turning animation, $0D, has 2 frames of 6 ticks each: its sequence's count byte is 1 and its tick byte 5, and the sprite engine advances a frame when the tick that started equal to the tick byte ends. While it shows its first frame the ground state doesn't move Mega Man. mega-man-3/turn-animation

About: Mega Man 3 Running

Value
{
 "frames": 2,
 "ticks": 6
}
CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/bank1A_1B_oam_sequences.asm#L199 — the record at $8332, animation $0D's (pointer bytes $32 and $83 in the tables at $8000 and $8080): count $01, ticks $05, sprites $0C and $0C.byte $08,$19,$01,$05,$0C,$0C,$01,$05Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/sprites.asm#L201-L217lda ent_anim_frame,x ; ent_anim_frame = frame tick counter and #$7F ; (bit 7 = damage flash flag) inc ent_anim_frame,x ; increment tick ldy #$01 ; Y=1 → offset to duration byte cmp (temp_00),y ; compare tick vs duration at seq[1] bne sprite_drawn_flag_check ; not reached? keep waiting lda ent_anim_frame,x ; tick reached duration: and #$80 ; reset tick to 0, preserve bit 7 sta ent_anim_frame,x ; (damage flash flag) lda ent_anim_state,x ; ent_anim_state = current frame index and #$7F ; (bit 7 preserved) inc ent_anim_state,x ; advance to next frame …Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L94-L102 — frame 0 of $0D goes to the fire check, which doesn't move himcmp #$0D ; $0D/$0E/$0F = turning/special anims beq player_ground_special_anim ; OAM $0D → check if anim complete cmp #$0E ; OAM $0E = special anim variant beq player_ground_special_anim ; OAM $0E → check if anim complete cmp #$0F ; OAM $0F = special anim variant bne player_ground_walk_handler ; other → normal walk handler player_ground_special_anim: lda ent_anim_state ; check animation state bne player_ground_walk_handler ; → normal walk handler beq player_ground_slide_weapon ; anim done → slide/crouch checkChecked by claude, 2026-10-26 — read animation $0D's pointer and record in bank $1A, the tick step in setup_sprite_render, and the ground state's dispatch at the pinned commit; the ROM's bytes at the record match the listing

reset_sprite_anim (movement.asm L435-L441) clears both counters when the animation starts.

On the ground Mega Man 3 picks the move from his animation. Standing, a held direction faces him that way, starts the turning animation, and moves him 1.30 px; on the turning animation's first frame he doesn't move; once it reaches its second, a held direction starts the walking animation and moves him, and nothing held stands him. Walking, a direction matching his facing moves him; anything else, letting go or the other direction, starts the turning animation and moves him once more toward his facing. Down alone does nothing on open ground. mega-man-3/ground-states

About: Mega Man 3 Running

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L89-L102player_ground_anim_dispatch: lda ent_anim_id ; A = current animation ID cmp #$04 ; $04/$05 = shoot-walk anims beq player_ground_shoot_walk ; OAM $04 → shoot-walk handler cmp #$05 ; OAM $05 = shoot-walk variant beq player_ground_shoot_walk ; shoot-walk variant match cmp #$0D ; $0D/$0E/$0F = turning/special anims beq player_ground_special_anim ; OAM $0D → check if anim complete cmp #$0E ; OAM $0E = special anim variant beq player_ground_special_anim ; OAM $0E → check if anim complete cmp #$0F ; OAM $0F = special anim variant bne player_ground_walk_handler ; other → normal walk handler player_ground_special_anim: lda ent_anim_state ; check animation state …Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L105-L121player_ground_shoot_walk: lda joy1_held ; D-pad matches facing direction? and player_facing ; mask held direction with facing beq player_ground_turning_anim ; no → turning animation pha ; save direction on stack jsr player_ground_slide_weapon ; slide/crouch check pla ; restore direction from stack jmp player_ground_move_horizontal ; move L/R based on direction ; --- turning: set turn animation, then move --- player_ground_turning_anim: lda #$0D ; OAM $0D = turning animation jsr reset_sprite_anim ; set turning animation lda shoot_anim_timer ; check if shooting beq player_ground_move_facing ; not shooting, move facing …Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L125-L141player_ground_walk_handler: lda joy1_held ; D-pad L/R held? and #$03 ; mask L/R bits beq player_ground_idle ; no → idle sta player_facing ; update facing direction jsr player_ground_slide_weapon ; slide/crouch check lda ent_anim_id ; check current animation ID cmp #$0D ; OAM $0D = turning anim? beq player_ground_shoot_walk_anim ; yes → use shoot-walk anim cmp #$0E ; OAM $0E = special anim? beq player_ground_shoot_walk_anim ; special anim variant match cmp #$0F ; check OAM $0F bne player_ground_turning_anim ; else → turning animation …Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L163-L168player_ground_idle: lda shoot_anim_timer ; if shooting: skip idle anim reset bne player_ground_slide_weapon ; shooting, skip idle check lda #$01 ; OAM $01 = idle standing cmp ent_anim_id ; compare with current anim beq player_ground_slide_weapon ; already idle → skip reset jsr reset_sprite_anim ; set OAM to $01 (idle standing)Checked by claude, 2026-10-26 — read player_ground_anim_dispatch through player_ground_idle at the pinned commit; Down is read on the ground only with a fresh A (slide) and by the ladder test (player_states.asm L394-L420), which needs a ladder tile

So a reversal from a walk moves him one frame the old way, pauses, then walks the new way, where Mega Man 2 turns at once at full speed (mega-man-2/turn-instant).

Measured in Mesen from a standstill in Top Man's stage start, Right held moves Mega Man 1.30 px on its first frame, not at all on the next 5, and 1.30 px on every frame after; let go after a walk, he moves 1.30 px on one more frame and then stands. mega-man-3/ground-measured

About: Mega Man 3 Running

Value
{
 "stepFrames": 1,
 "pauseFrames": 5,
 "stopFrames": 1
}
VerifiedMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L115-L121 — the step: the turning animation, then a moveplayer_ground_turning_anim: lda #$0D ; OAM $0D = turning animation jsr reset_sprite_anim ; set turning animation lda shoot_anim_timer ; check if shooting beq player_ground_move_facing ; not shooting, move facing jsr shoot_oam_advance_variant ; update shoot animation variant player_ground_move_facing: lda player_facing ; move in facing direction jmp player_ground_move_horizontal ; move in facing directionMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/bank1A_1B_oam_sequences.asm#L199 — animation $0D: 2 frames of 6 ticks (turn-animation).byte $08,$19,$01,$05,$0C,$0C,$01,$05Checked by claude, 2026-10-26 — ran movement.mjs, whose walk case holds Right 30 frames and lets go 20 from Top Man's stage start in Mesen, and checked the per-frame X steps against the values; the 5 still frames are the 6 ticks of the turning animation's first frame less the one counted on the step frameProbe: probes/jump-physics/movement.mjs

probes/lab/conformance.mjs runs every lab preset and stress script, the slide scripts among them, through the model and the cartridge from the same start and finds them equal frame for frame.

A jump lifts Mega Man on its press frame: the ground state sets the jump speed and runs the airborne routine at once, which moves him 4.89 px up from where that frame's floor test left him (the test takes him 0.25 px down, or, when that crosses into the next pixel and the snap puts his feet back on the floor, 0.75 px up), takes gravity, and gives the frame the air move, 1.30 px/frame toward a held direction or none, in place of the ground move. A press in a walking pause still jumps, since the press is read before the walk. mega-man-3/takeoff

About: Mega Man 3 Jump

VerifiedMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L59-L65player_ground_jump_or_slide: lda joy1_press ; check new button presses and #BTN_A ; A button pressed? beq player_ground_anim_dispatch ; no → animate walk/idle lda joy1_held ; check held buttons and #BTN_DOWN ; holding down? beq player_ground_normal_jump ; not holding down, jump jmp slide_initiate ; initiate slideMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L71-L78player_ground_normal_jump: lda joy2_held ; P2 held buttons and #$01 ; bit 0 = Right held? bne player_ground_super_jump ; yes → debug super jump lda #$E5 ; normal jump: Y vel = $04.E5 sta ent_yvel_sub,x ; store Y velocity sub-pixel lda #$04 ; Y velocity whole = $04 sta ent_yvel,x ; store Y velocity whole jmp player_airborne ; enter airborne stateChecked by claude, 2026-10-26 — read player_ground_jump_or_slide and player_ground_normal_jump at the pinned commit, and ran movement.mjs, whose jump case from Top Man's stage start in Mesen shows the first frame's rise equal to the jump speed less the 0.25 px dropProbe: probes/jump-physics/movement.mjs

Mega Man 2's press frame moves him at the ground speed and doesn't lift him (mega-man-2/takeoff-x, jump-start).

A landing frame moves Mega Man only vertically: the airborne routine snaps him onto the floor, sets the ground state and the turning animation already on its second frame, and returns before the air move. From the next frame he walks at once if a direction is held, with no pause. An A press on the landing frame is lost, since the airborne state reads no presses. mega-man-3/landing

About: Mega Man 3 Jump

VerifiedMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L322-L339player_airborne_landing_check: plp ; restore carry (landing status) jsr ladder_check_entry_ground ; check if player should enter ladder bcc player_airborne_state_dispatch ; carry clear = no ladder → continue airborne ; --- player landed on ground → transition to on_ground --- lda player_state ; check current player state beq player_airborne_landing_anim ; already on_ground → skip transition lda #SFX_LAND ; play landing sound jsr submit_sound_ID ; play landing sound effect lda #$00 ; state = on_ground ($00) sta player_state ; set to on_ground state lda #$0D ; OAM $0D = landing animation jsr reset_sprite_anim ; set landing animation …Checked by claude, 2026-10-26 — read player_airborne_landing_check through player_airborne_landing_anim at the pinned commit, and ran movement.mjs: in Top Man's stage start in Mesen, the jumprun case's landing frame has no X step and the next a walking one, and the mash case, A pressed every other frame, lands on press frames and stays grounded the frame afterProbe: probes/jump-physics/movement.mjs

Mega Man 2 jumps on a press on the landing frame (mega-man-2/landing).

Starting a slide, Mega Man 3 first tests for a wall 8 and 22 px ahead of his center at its height, and a wall there cancels the press. A slide lowers him 2 px, tests the floor at x − 15, x, and x + 15, 10 px below his lowered center, and moves with a wall point 16 px ahead, at 5 px above and 7 px below that center. A turn, a wall, the timer's end, or a jump press leads to the standing test, the ceiling points 12 px over his lowered center. mega-man-3/slide-box

About: Mega Man 3 Ground slide

Value
{
 "drop": 2,
 "floor": {
  "x": [
   -15,
   0,
   15
  ],
  "y": 10
 },
 "wall": {
  "x": 16,
  "y": [
   -5,
   7
  ]
 },
 "start": {
  "x": [
   8,
   22
  ],
  "y": 0
 }
}
CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L269-L282slide_initiate: lda joy1_held ; read D-pad state and #$03 ; left/right bits beq slide_check_direction ; no direction → keep current sta player_facing ; update facing from D-pad slide_check_direction: ldy #$04 ; Y=4 (check right) or Y=5 (check left) lda player_facing ; based on facing direction and #$01 ; bit 0 = right bne slide_wall_collision ; facing right → skip INY iny ; facing left → Y=5 slide_wall_collision: jsr check_tile_horiz ; check for wall ahead at slide height lda $10 ; collision result and #$10 ; bit 4 = solid wall …Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/collision.asm#L491-L501 — hitbox_h_* rows 2, 4, and 5: from index 9, 3 points at Y + $0A, X −15 then +15 and +15; from $12 and $16, 2 points at Y + 0, X +8 then +14, and X −22 then +14hitbox_h_start_idx: .byte $00,$05,$09,$0E,$12,$16,$1A,$1F … hitbox_h_x_offsets: .byte $F9,$07,$07,$01,$F4,$F9,$0E,$02 .byte $0A,$F1,$0F,$0F,$01,$00,$F9,$0E .byte $01,$00,$08,$0E,$01,$00,$EA,$0EMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/collision.asm#L525-L533 — hitbox_v_* rows 2 and 3: from index $0A, 2 points at X + $10, Y −5 then +12; from $0E, the same at X − $10hitbox_v_start_idx: .byte $00,$05,$0A,$0E,$12,$17,$1C,$21 … hitbox_v_y_offsets: .byte $F5,$0B,$0B,$02,$F8,$F5,$0B,$0B .byte $01,$10,$FB,$0C,$01,$F0,$FB,$0CMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L324-L343 — the slide's floor test (row 2) and moves (rows 2 and 3)player_slide: ldy #$02 ; apply $99 (Y=2 = slide hitbox) jsr move_vertical_gravity ; apply gravity with slide hitbox bcc slide_end_clear_state ; C=0 → no ground, fell off ledge lda joy1_held ; read held D-pad and #$03 ; mask left/right bits beq slide_move_check_facing ; no direction → keep sliding cmp player_facing ; same as current facing? beq slide_move_check_facing ; yes → keep sliding sta player_facing ; changed direction → end slide bne slide_end_ground_check ; always taken → end slide slide_move_check_facing: lda player_facing ; facing direction: bit 0 = right …Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L361-L365 — the standing test, row 1, the ceiling pointsslide_end_ground_check: ldy #$01 ; check ground at standing height jsr check_tile_horiz ; check tile at standing height lda $10 ; solid ground under feet? and #$10 ; bit 4 = solid bne slide_end_on_ground_return ; on ground → return to standingChecked by claude, 2026-10-26 — read slide_initiate, player_slide, and slide_end_ground_check with the offset rows they pick at the pinned commit; the 2 px drop is slide_initiate_confirmed's (slide)

A slide that ends on open ground leaves Mega Man in the ground state with the walking animation, still 2 px low; the next frame's floor test pushes him back up. That walking animation moves him on the next frame toward his facing, so with no direction held he steps once more at 1.30 px/frame and then pauses, and with one held he walks on at once. A slide whose floor test finds nothing ends without moving that frame, and he falls from the next. mega-man-3/slide-exit

About: Mega Man 3 Ground slide

VerifiedMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L366-L384sta $33 ; clear timer and state sta player_state ; state = on_ground lda joy1_press ; A button pressed? and #BTN_A ; check A button bne slide_jump_walk_speed_init ; yes → slide jump beq slide_end_fall_anim ; no → fall from slide slide_end_clear_state: lda #$00 ; clear timer sta $33 ; clear slide timer lda #$01 ; OAM anim $01 = airborne bne slide_end_anim_set ; always taken → set anim slide_end_fall_anim: lda #$04 ; OAM anim $04 = jump/fall slide_end_anim_set: jsr reset_sprite_anim ; set fall/airborne animation …Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L324-L327player_slide: ldy #$02 ; apply $99 (Y=2 = slide hitbox) jsr move_vertical_gravity ; apply gravity with slide hitbox bcc slide_end_clear_state ; C=0 → no ground, fell off ledgeChecked by claude, 2026-10-26 — read slide_end_ground_check through slide_end_on_ground_return at the pinned commit (no Y adjustment on any exit), and ran movement.mjs: in Top Man's stage start in Mesen the slide case is 2 px low through its last frame, level the frame after, and moves 1.30 px on that frameProbe: probes/jump-physics/movement.mjs

The slide's floor points reach 15 px out, so a slide carries him further over a ledge than walking would before it ends.

Mega Man 3's ordinary-floor movement differs from Mega Man 2's from the first frame of a walk: Right held from a standstill moves him 1.30 px, then nothing for 5 frames, then 1.30 px a frame, where Mega Man 2 moves 0.125 px a frame for 7 frames, then 1.375. A jump differs from its press frame, which lifts him 4.89 px where Mega Man 2 lifts him 0. Letting go of A mid-rise starts the fall at once, where Mega Man 2 cuts a rise of 2 px/frame or more to 1, and falls cap at 7 px/frame instead of 12. He keeps Mega Man 2's gravity, floor and ceiling corners, and 16 px tiles, and adds the slide. mega-man-3/movement-differs-from-mega-man-2

About: Mega Man 3 Running

CodeMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L115-L121 — Mega Man 3's stepplayer_ground_turning_anim: lda #$0D ; OAM $0D = turning animation jsr reset_sprite_anim ; set turning animation lda shoot_anim_timer ; check if shooting beq player_ground_move_facing ; not shooting, move facing jsr shoot_oam_advance_variant ; update shoot animation variant player_ground_move_facing: lda player_facing ; move in facing direction jmp player_ground_move_horizontal ; move in facing directionMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/game_loop.asm#L642-L645 — one speed for walking and the airprelude_reset_walk_speed: lda #$4C ; reset walk speed to $01.4C sta ent_xvel_sub ; store walk sub-pixel speed lda #$01 ; walk speed whole = $01 sta ent_xvel ; store walk whole speedmm2: Mega Man 2 disassembly (lsmmega/mm2) · engine/run_megaman.asm#L208-L219 — Mega Man 2's release capLDA z:zjoy1_pressed AND #a_button BNE @skip LDA aobject_ycoord_speed BMI @skip CMP #$01 BCC @skip BEQ @skip LDA #$01 STA aobject_ycoord_speed LDA #$00 STA aobject_ycoord_speed_fractionMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L71-L78 — Mega Man 3's press frame runs the airborne state at onceplayer_ground_normal_jump: lda joy2_held ; P2 held buttons and #$01 ; bit 0 = Right held? bne player_ground_super_jump ; yes → debug super jump lda #$E5 ; normal jump: Y vel = $04.E5 sta ent_yvel_sub,x ; store Y velocity sub-pixel lda #$04 ; Y velocity whole = $04 sta ent_yvel,x ; store Y velocity whole jmp player_airborne ; enter airborne stateMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L260-L267 — Mega Man 3's release cutplayer_airborne: lda ent_yvel ; check Y velocity sign bmi player_airborne_variable_jump ; negative (falling) → skip to clear flag lda force_full_jump ; check force-full-jump flag bne player_airborne_wall_check ; nonzero → skip variable jump lda joy1_held ; check if A button held and #BTN_A ; A button mask bne player_airborne_variable_jump ; held → keep rising (variable jump) jsr reset_gravity ; A released → clamp Y speed to 0 (variable jump)Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/movement.asm#L393-L402 — Mega Man 3's fall capapply_gravity_player_check: lda ent_yvel_sub,x ; Y sub velocity -= gravity sec ; prepare for subtraction sbc gravity ; subtract gravity value sta ent_yvel_sub,x ; store new Y sub velocity lda ent_yvel,x ; Y velocity -= borrow sbc #$00 ; propagate borrow to whole byte sta ent_yvel,x ; store new Y velocity bpl apply_gravity_exit ; still positive (rising)? done cmp #$F9 ; check against terminal velocity $F9 bcs apply_gravity_exit ; within limit (-7 to -1)? donemm2: Mega Man 2 disassembly (lsmmega/mm2) · engine/run_megaman.asm#L84-L88 — Mega Man 2 moves on the ground first, then checks for a jump, so the press frame doesn't lift himJSR _megaman_status_parameter JSR _check_object_horizontal_move JSR _check_object_vertical_move LDA z:z00 BNE _run_megaman_landing_common_1mm2: Mega Man 2 disassembly (lsmmega/mm2) · engine/check_object_vertical_move.asm#L81-L87 — Mega Man 2's fall capBPL @skip CMP #$F4 BCS @skip LDA #$00 STA aobject_ycoord_speed_fraction LDA #$F4 STA aobject_ycoord_speedChecked by claude, 2026-10-26 — compared the two games'' claims (mega-man-3/ground-measured, takeoff, release-cut, max-fall, gravity, terrain-box; mega-man-2/state-speeds, ground-measured, jump-start, release-cap, max-fall), each checked against its cartridge by probes/lab/conformance.mjs, and re-read the lines quoted at their pinned commits

Mega Man 2's 7-frame step and 1.375 px/frame run are measured in mega-man-2/ground-measured and mega-man-2/state-speeds. Their code differs too: Mega Man 2 sets each state's speed from a table, while Mega Man 3 runs one speed and gates the move by animation. Mega Man 3's lab model is its own (mega-man-3.model.ts); the two models first differ on frame 1 of a walk or a jump.

Cited by: Mega Man (classic series)