data/claims/mega-man-4.yamlMega Man 4 (plasticsmoke ca65 disassembly, and the Rev 1 ROM's bytes)
Game: Mega Man 4.
Mega Man 4 takes off at exactly 5 px/frame ($05.$00), up from Mega Man 3's $04.$E5, and a release cuts any rise to a speed of 0 where Mega Man 3 sets 0.25 px/frame downward. Both cut on every rising frame without A. mega-man-4/jump-line
About: Mega Man 4 Variable jump height
Value
{
"takeoff": 1280,
"afterRelease": 0
}src/bank3C.asm#L179-L190L_813E:
LDA ent_angle ; player $04B0 flag
BNE L_8149 ; nonzero: normal jump
LDA joy1_held ; held buttons
AND #$04 ; Down held?
BNE L_8156 ; Down+A = slide
L_8149:
LDA #$00 ; zero sub-velocity
STA ent_yvel_sub ; Y velocity sub = 0
LDA #$05 ; jump speed 5 px/frame
STA ent_yvel ; Y velocity = +5 (up)
JMP L_82C8 ; enter airborne physicsMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L420-L431L_8306:
LDA ent_yvel ; player Y velocity
BMI L_8338 ; moving down: keep falling
LDA ent_var6 ; knockback/no-jump-cut flag
BNE L_833D ; set: no jump cut
LDA joy1_held ; held buttons
AND #$80 ; A still held?
BNE L_8338 ; held: full jump
LDA #$00 ; zero
STA ent_yvel_sub ; cut jump: Y vel sub = 0
STA ent_yvel ; cut jump: Y vel = 0
BEQ L_8338 ; (always taken)Mega Man 4 (USA) (Rev 1) · 0x78159-0x78165 — bank $3C $8149, the take-off in the Rev 1 ROMMega Man 4 (USA) (Rev 1) · 0x78322-0x7832E — bank $3C $8312, the cut in the Rev 1 ROMMega 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/movement.asm#L424-L427reset_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 byteChecked by claude, 2026-10-21 — read the ground state's A handler and the shared air physics in Mega Man 4, and Mega Man 3's jump start and reset_gravity (mega-man-3/release-cut), at their pinned commits; the Rev 1 bytes at the two offsets are the same instructions (A9 00 8D D8 03 A9 05 8D F0 03 4C …)The check runs after the frame's move and gravity (JSR L_92C5 at L410), as in Mega Man 2. The ent_var6 flag skips the cut, as Mega Man 3's force_full_jump does.
Cited by: Jump, Mega Man 4, Variable jump height
Mega Man 4's slide keeps Mega Man 3's 2.5 px/frame but moves on 26 frames (a timer of $1A, counted after each move from the set-up frame on) where Mega Man 3's moves on 21 (a timer of 20). Its A test runs on every slide frame, the set-up's included: a fresh A press without Down jumps out whenever he has headroom, where Mega Man 3 waits for its 9th frame, and A with Down held keeps sliding. The set-up's own press comes with Down, and A held from it into the second frame is no fresh press, so the earliest jump out comes on the slide's 3rd frame. mega-man-4/slide-line
About: Mega Man 4 Ground slide
Value
{
"speed": 640,
"frames": 26,
"jumpFromFrame": 3
}src/bank3C.asm#L254 — the set-up runs the slide state's A test and move on the same frame, so the test runs from the slide's 1st frameJMP L_8368 ; post-slide-start processingMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L510-L516LDY #$04 ; speed selector 4 = slide
JSR entity_horiz_dispatch ; move player horizontally
BCS L_83C3 ; hit wall: try to stand
LDA $34 ; slide timer
BEQ L_83C3 ; expired: try to stand
DEC $34 ; count down slide
BNE L_83EF ; still sliding: doneMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L217-L226LDA #$1A ; slide duration 26 frames
STA $34 ; set slide timer
LDA #$02 ; state $02 = slide
STA player_state ; enter slide state
LDA #$80 ; slide speed 2.5 px/frame
STA ent_xvel_sub ; X velocity sub = $80
STA $37 ; mirror in $37
LDA #$02 ; 2 px
STA ent_xvel ; X velocity = 2
STA $38 ; mirror in $38Mega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L473-L496L_8368:
LDA joy1_press ; newly pressed
AND #$80 ; A button?
BEQ L_839D ; no jump: steering
LDA joy1_held ; held buttons
AND #$04 ; Down held?
BNE L_839D ; Down+A: stay sliding
LDY #$03 ; probe 3 = standing headroom
JSR tile_collision_detect ; check tile above head
LDA $10 ; collision result
BNE L_839D ; blocked: no jump
JSR L_83F2 ; platform overhead check
…Mega Man 4 (USA) (Rev 1) · 0x78194-0x781A7 — bank $3C $8184, the slide's set-up in the Rev 1 ROMMega Man 4 (USA) (Rev 1) · 0x78378-0x783AC — bank $3C $8368, the jump out of the slide in the Rev 1 ROMMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L288-L289lda #$14 ; slide timer = 20 frames
sta $33 ; store slide timerMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L344-L348 — Mega Man 3 tests for 0 before decrementing, so its last move comes on the frame the timer is already 0 (mega-man-3/slide)slide_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 slideMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L349-L355dec $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 jumpChecked by claude, 2026-10-26 — read Mega Man 4's slide set-up and the slide state (state $02) from its A test through the timer and the standing test (L497-L524), and Mega Man 3's slide (mega-man-3/slide), at their pinned commits. Mega Man 4 decrements and ends on reaching 0 in the same frame (26 → 0 over 26 moves); Mega Man 3 moves once more on the frame it finds 0. Measured in Mesen (MesenCE 2.2.1) from power-on, at the start of Skull Man's stage on flat floor: a slide moved 2.5 px on each of 26 frames and stood him up on the 26th; A held from the set-up into the 2nd frame didn't jump, a press on the 3rd frame did, and Down with A held for 60 frames never jumpedProbe: probes/jump-physics/movement.mjsAs in Mega Man 3, a turn ends the slide (L504-L508) and a ceiling over his head keeps it going past the timer (L518-L524). This claim's value used to say the jump out comes "from frame 2", the first frame after the set-up; mega-man-5/slide-kept's "frame 1" counts the set-up frame, where the test first runs. Both describe the same code, and neither frame can carry a fresh press without Down, so the earliest jump out in either count is the 3rd frame.
Cited by: Ground slide, Mega Man 4
A B press fires a normal shot and sets the charge counter to 1; each frame B stays held in a state below 4 the counter rises by 1, looping from 100 back to 92. On release, a counter under 32 ($20) fires nothing more, 32 or more fires a half-charged shot, and 92 ($5C) or more a full one. mega-man-4/charge-shot
About: Mega Man 4 Charge shot
Value
{
"half": 32,
"full": 92
}src/bank3C.asm#L73-L95L_8073:
LDA $35 ; charge counter
BEQ L_80CF ; not charging: done
LDA player_state ; player state
CMP #$04 ; states >= 4 (Rush riding etc.)
BCS L_80CF ; cannot charge: done
LDA joy1_held ; held buttons
AND #$40 ; B button still held?
BEQ L_80CF ; released: fire handled at L_8CCC
INC $35 ; advance charge
…
LDA $35 ; reload charge
…Mega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L1694-L1722LDA $35 ; charge counter
BEQ L_8D2F ; no charge: check B press
LDA current_weapon ; current weapon
BNE L_8D2F ; not buster: check B press
LDA joy1_held ; held buttons
AND #$40 ; B still held?
BNE L_8D59 ; still charging: no fire
LDA #$21 ; charged spawn offset $21
STA $11 ; set spawn-offset index
…
CMP #$20 ; level 1 threshold ($20)
BCC L_8D59 ; tap shot: normal spawn
…Mega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L1743-L1751L_8D2F:
LDA joy1_press ; newly pressed
AND #$40 ; B button?
BEQ L_8D5B ; no fire: done
L_8D35:
LDY #$00 ; slot 0 marker
STY temp_00 ; clear chosen-slot temp
INY ; Y = 1
STY $35 ; minimum charge (tap)Mega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L1769-L1772L_8D59:
LDA #$00 ; A = 0 (no shot fired)
L_8D5B:
RTS ; doneMega Man 4 (USA) (Rev 1) · 0x78083-0x780AE — bank $3C $8073, the counter in the Rev 1 ROMMega Man 4 (USA) (Rev 1) · 0x78D02-0x78D0F — bank $3C $8CF2, the thresholds in the Rev 1 ROMChecked by claude, 2026-10-21 — read the charge counter in the per-frame update and the Mega Buster's handler (L_8CCC) from the release test to the shot at the pinned commit. Both charged paths reach L_8D35 and fire with spawn offset `$21`, or `$20` for the full charge; under `$20` the release branches to L_8D59, which returns without a shot. The `$40` test only picks the flash colorsCharging runs only with the Mega Buster selected (L49-L51). The counter starts at 1, so the half charge comes 31 held frames after the press and the full one 91, if every frame counts; not yet measured.
Cited by: Charge shot, Mega Man 2, Mega Man 4
Mega Man 4 keeps Mega Man 3's knockback, 0.5 px/frame ($00.$80) against his facing under gravity until the hurt animation reaches 9, with no push when the hit lands during a slide. mega-man-4/knockback
About: Mega Man 4 Knockback
Value
{
"back": 128
}src/bank3C.asm#L801-L811LDY ent_var5 ; knockback probe selector
JSR entity_player_collide ; ride/collide with platforms
LDA #$80 ; knockback speed 0.5 px/frame
STA ent_xvel_sub ; X velocity sub
LDA #$00 ; zero
STA ent_xvel ; X velocity px
LDA ent_flags ; player flags
PHA ; save (facing)
LDA ent_hp ; hurt sub-state
CMP #$02 ; $02 = was sliding
BEQ L_8616 ; sliding: skip pushbackMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L830-L832LDA ent_anim_phase ; player anim counter
CMP #$09 ; knockback anim done (frame 9)?
BNE L_865B ; not yet: keep flying backMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L776-L786player_knockback_physics: lda $3E ; check saved OAM for special cases:
cmp #$10 ; $10 = sliding (no knockback physics)
beq player_knockback_landing ; sliding → skip knockback
…
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 = 0Checked by claude, 2026-10-21 — read the hurt state ($06) in Mega Man 4 and player_damage in Mega Man 3 (mega-man-3/knockback) at their pinned commitsBoth set a 60 ($3C) countdown when the hurt state ends (Mega Man 4 L833-L835); the atlas has checked in Mega Man 3 only that it gates damage.
Cited by: Knockback, Mega Man 4
One routine, entity_player_collide, moves Mega Man vertically in every state: by the sign of his speed's whole byte it moves him down or up by the speed (8.8 fixed point, positive up), takes gravity off the speed, and only then tests the floor points under a fall or the ceiling points over a rise. The ground, air, and slide states all run it before their horizontal move, so a frame's floor and ceiling tests use the X the frame started with and its wall test the new Y. mega-man-4/motion-order
About: Mega Man 4 Jump physics
CodeMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/fixed/fixed_entity_physics.asm#L321-L331L_F174:
LDA ent_yvel,X ; load vert velocity pixel
BMI L_F17C ; negative -> moving down (falling)
JMP L_F1FD ; positive/zero -> moving up (rising)
; ---------------------------------------------------------------------------
; player falling (vert velocity negative = downward in screen coords)
; ---------------------------------------------------------------------------
L_F17C:
JSR L_F2B8 ; apply downward vert velocityMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_entity_physics.asm#L365-L367L_F1B0:
JSR L_F2F6 ; apply gravity (accelerate downward)
JSR tile_collision_detect ; check tile collisionMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_entity_physics.asm#L410-L412L_F1FD:
INY ; increment screen offset
JSR L_F2D8 ; apply upward vert velocityMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_entity_physics.asm#L429-L431L_F219:
JSR L_F2F6 ; apply gravity (accelerate downward)
JSR tile_collision_detect ; check tile collisionMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L171-L172 — the ground state runs the air routine (L_82C8) before its own A test and walkJSR L_82C8 ; shared physics; carry = grounded
BCC L_80F9 ; airborne: done for this frameMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L387-L390L_82C8:
LDY #$00 ; probe selector 0
STY $A4 ; clear wind-tile flag
JSR entity_player_collide ; ride/collide with platformsMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L447-L453 — the air routine's horizontal move comes after entity_player_collideL_833D:
LDA joy1_held ; held buttons
AND #$03 ; L/R bits
STA ent_dir ; air control: face held dir
STA $31 ; store direction
LDY #$00 ; speed selector 0 = walk
JSR entity_horiz_dispatch ; move horizontally in airMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L464-L467LDY #$04 ; probe selector 4 (slide height)
JSR entity_player_collide ; ride/collide with platforms
JSR L_92C5 ; environment + ground check
BCS L_8368 ; still grounded: continue slideChecked by claude, 2026-10-26 — read entity_player_collide's dispatch, both branches, and the movers L_F2B8 and L_F2D8 at the pinned commit, and the three player states that call it (ground $8130, air $82C8, slide $8353)Mega Man 2 runs the horizontal move first in each state (mega-man-2/collision-order). A speed of exactly 0 counts as rising, so the frame after a release cut (jump-line) moves 0 and tests the ceiling.
Gravity takes $40/256 = 0.25 px/frame from Mega Man's upward speed every frame; the stage start writes $40 to gravity ($99). mega-man-4/gravity
About: Mega Man 4 Jump physics
Value: 64 1/256 px/frame²
VerifiedMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/fixed/fixed_game_loop.asm#L76-L77LDA #$40 ; normal gravity ($15 underwater)
STA gravity ; set gravity accelerationMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_entity_physics.asm#L622-L629L_F302:
LDA ent_yvel_sub,X ; load vert velocity sub-pixel
SEC ; prepare subtraction
SBC gravity ; subtract gravity acceleration
STA ent_yvel_sub,X ; store updated sub-pixel
LDA ent_yvel,X ; load vert velocity pixel
SBC #$00 ; subtract borrow
STA ent_yvel,X ; store updated pixelChecked by claude, 2026-10-26 — read the stage init (L_C541) and the gravity routine L_F2F6 at the pinned commit. Measured in Mesen (MesenCE 2.2.1) from power-on, standing where Mega Man lands at the start of Skull Man's stage, a held jump's speed fell by `$40` every frame from `$04`.`$C0` to the landingProbe: probes/jump-physics/movement.mjsWater doesn't change the value for the player: a water timer ($43, set to 3 on entering water) skips gravity for its first frames (L616-L621). The $15 in the stage start's comment is written only by a water enemy's own routine (bank3B L_A870), which puts $40 back after its move.
When gravity takes Mega Man's speed below -7 px/frame (its whole byte under $F9), the gravity routine sets it to exactly $F9.$00, so he falls at most 7 px a frame. mega-man-4/fall-cap
About: Mega Man 4 Jump physics
Value: -1792 1/256 px/frame
CodeMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/fixed/fixed_entity_physics.asm#L630-L636BPL L_F323 ; positive (rising) -> done
CMP #$F9 ; below terminal velocity ($F9)?
BCS L_F323 ; no -> done (already capped)
LDA #$F9 ; terminal velocity pixel
STA ent_yvel,X ; clamp to $F9
LDA #$00 ; zero sub-pixel
STA ent_yvel_sub,X ; clear sub-pixel velocityMega Man 4 (USA) (Rev 1) · 0x7F323-0x7F332 — fixed bank $F313, the clamp in the Rev 1 ROMChecked by claude, 2026-10-26 — read L_F2F6 at the pinned commit: after the subtraction a negative whole byte below `$F9` is replaced by `$F9` and the fraction zeroed; the Rev 1 bytes at the offset are the same instructionsMega Man 3 also caps at $F9 (mega-man-3/jump-line), and Mega Man 2 at 12 px/frame (mega-man-2/max-fall). Mega Man 6 stops only the whole byte, so its fall creeps past 7 (mega-man-6/fall-limits). Mega Man 4's fall reaches the cap 24 frames after a fall from rest begins at −1 px/frame (computed).
On the ground Mega Man's vertical speed rests at -1 px/frame ($FF.$00), which every landing and every floor contact sets through entity_stop_y. So each grounded frame moves him 1 px down, takes gravity, and tests his floor points one row lower; ground there puts him back on it at the same speed, and none leaves him in the air, falling at 1.25 px/frame from the next frame. mega-man-4/ground-check
About: Mega Man 4 Jump physics
Value: -256 1/256 px/frame
VerifiedMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/fixed/fixed_entity_physics.asm#L648-L652L_F324:
LDA #$00 ; zero
STA ent_yvel_sub,X ; clear vert velocity sub-pixel
LDA #$FF ; $FF = stopped / minimal
STA ent_yvel,X ; set vert velocity pixelMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_entity_physics.asm#L399-L404L_F1F5:
JSR L_D9E7 ; push entity Y up onto floor tile
JSR entity_stop_y ; stop vertical velocity
L_F1FB:
SEC ; set carry = collision
RTS ; returnMega Man 4 (USA) (Rev 1) · 0x7F334-0x7F33D — fixed bank $F324, entity_stop_y in the Rev 1 ROMChecked by claude, 2026-10-26 — read entity_stop_y and the falling branch of entity_player_collide at the pinned commit. Measured in Mesen (MesenCE 2.2.1) from power-on, standing and walking where Mega Man lands at the start of Skull Man's stage, the speed read `$FF`.`$00` on every grounded frame and his Y didn't changeProbe: probes/jump-physics/movement.mjsMega Man 3 rests at $FF.$C0 (-0.25 px/frame, mega-man-3/release-cut) and Mega Man 2 at -0.25 too (mega-man-2/ground-check). In Mega Man 4 the same $FF00 rests every entity (mega-man-code-lineage/mm3-mm4-plain-movers).
When a falling Mega Man's floor points find a solid tile, align_down subtracts the tested row's depth into its 16 px tile from his Y pixel, which puts the points on the tile's top row and his feet on its top, and keeps the Y fraction; the speed goes back to -1 px/frame. A fall of up to 7 px a frame always ends on the top of the tile it reaches. mega-man-4/floor-snap
About: Mega Man 4 Jump physics
CodeMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/fixed/fixed_tile_collision.asm#L1973-L1981L_D9E7:
LDA $11 ; load collision Y pixel position
PHA ; save original for restoration
AND #$0F ; mask to within-tile offset
STA $11 ; store offset temporarily
LDA ent_y_px,X ; load entity Y pixel position
SEC ; prepare subtraction
SBC $11 ; subtract within-tile offset
STA ent_y_px,X ; store adjusted positionMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_entity_physics.asm#L399-L401L_F1F5:
JSR L_D9E7 ; push entity Y up onto floor tile
JSR entity_stop_y ; stop vertical velocityChecked by claude, 2026-10-26 — read align_down ($D9E7) and its caller in entity_player_collide's falling branch at the pinned commitMega Man 2 zeroes the Y fraction on landing (mega-man-2/floor-snap); Mega Man 4 keeps it, so a jump's fraction carries into the next one. From flat ground every jump lands with fraction 0, since 5 px/frame and 0.25 px/frame² leave a whole number at each landing (computed).
Mega Man's position is his center. Standing, walking, and in the air his floor points are 12 px below it at x-7, x, and x+7; his ceiling points 9 px above it at x-7 and x+7; and his wall points 8 px to the side he moves toward, at 8 px above, level with, and 11 px below his center. mega-man-4/terrain-box
About: Mega Man 4 Jump physics
Value
{
"floor": {
"dy": 12,
"dx": [
-7,
0,
7
]
},
"ceiling": {
"dy": -9,
"dx": [
-7,
7
]
},
"wall": {
"dx": 8,
"dy": [
-8,
0,
11
]
}
}src/fixed/fixed_tile_collision.asm#L625-L631 — tile_collision_detect picks shape Y from the tables at $D69E and $D6DE; the floor test passes Y = 0, the ceiling test 1 (entity_player_collide's INY)L_D2FC:
JSR L_D53A ; save bank state, load stage bank
JSR L_D552 ; apply scroll offset to entity position
LDA $D69E,Y ; get collision shape pointer low byte
STA $5A ; store shape pointer low
LDA $D6DE,Y ; get collision shape pointer high byte
STA $5B ; store shape pointer highMega Man 4 (USA) (Rev 1) · 0x7D6AE-0x7D6B3 — $D69E-$D6A3, the low bytes of shapes 0-5: $1E $23 $27 $2A $2F $34Mega Man 4 (USA) (Rev 1) · 0x7D6EE-0x7D6F3 — $D6DE-$D6E3, their high bytes, all $D7Mega Man 4 (USA) (Rev 1) · 0x7D72E-0x7D748 — $D71E-$D738, shapes 0-5, each a point count less 1, the Y offset, the first X offset, and the X steps: shape 0 is 02 0C F9 07 07 (three points 12 px down, from x-7 in steps of 7), shape 1 is 01 F7 F9 0E (two points 9 px up, x-7 and x+7)Mega Man 4 (USA) (Rev 1) · 0x7D818-0x7D81F — $D808-$D80F, the low bytes of the wall shapes 0-7 ($D848-$D84F holds their high bytes, all $D8)Mega Man 4 (USA) (Rev 1) · 0x7D898-0x7D8B4 — $D888-$D8A4, wall shapes 0-7, each a point count less 1, the X offset, the first Y offset, and the Y steps: shape 0 (right) is 02 08 F8 08 0B, three points 8 px right at 8 up, level, and 11 down; shape 1 (left) is 02 F8 F8 08 0BChecked by claude, 2026-10-26 — read tile_collision_detect and scroll_collision_check at the pinned commit for the shape format, and the pointer tables and shapes as bytes in the Rev 1 ROM; the ground and air states pass Y = 0 (L_82C8, L388), entity_horiz_dispatch adds 1 for a move leftWith the floor points on the row under his feet, the wall points cover the 20 rows from his feet up and the ceiling points sit on the 21st; Mega Man 2 tests 24 rows (mega-man-2/terrain-box). Each test ORs the types of its points, and bit 4 of a type is solid.
When the horizontal move puts a wall point in a solid tile, the move routine pushes Mega Man back by the point's depth into that 16 px 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, his speed, and his pose stay, so he walks in place against it. mega-man-4/wall-stop
About: Mega Man 4 Jump physics
CodeMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/fixed/fixed_tile_collision.asm#L1905-L1916L_D99E:
.byte $05,$A5 ; shape data / overlap byte
.byte $12 ; (entry at $D99F is: LDA $12)
AND #$0F ; mask to within-tile offset
STA $12 ; store within-tile offset
LDA ent_x_px,X ; load entity X pixel
SEC ; prepare subtraction
SBC $12 ; subtract within-tile offset
STA ent_x_px,X ; store adjusted X pixel
LDA ent_x_screen,X ; load entity screen index
SBC #$00 ; subtract borrow from sub-pixel
STA ent_x_screen,X ; store adjusted screenMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_tile_collision.asm#L1928-L1937L_D9B7:
LDA $12 ; load collision X pixel position
AND #$0F ; mask to within-tile offset
EOR #$0F ; complement (distance to boundary)
SEC ; set carry for +1 in ADC
ADC ent_x_px,X ; add to entity X pixel
STA ent_x_px,X ; store adjusted X pixel
LDA ent_x_screen,X ; load entity screen index
ADC #$00 ; add carry (screen overflow)
STA ent_x_screen,X ; store adjusted screenMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_entity_physics.asm#L125-L131L_F079:
JSR scroll_collision_check ; check collision with scroll bounds
CLC ; default: no collision
LDA $10 ; load collision result
AND #$10 ; test solid-tile bit
BEQ L_F087 ; not solid -> return
JSR L_D9B7 ; push entity X right out of wallChecked by claude, 2026-10-26 — read entity_move_right and entity_move_left with their wall tests (L_F032, L_F079) and align_right/align_left at the pinned commitMega Man 2 also zeroes the X fraction at a wall (mega-man-2/wall-stop). A slide tests its own wall points (slide-box).
Because the ground state tests the floor before it moves him, Mega Man walks one frame past the edge before he falls: the frame whose move takes all three floor points off a ledge still counts as grounded, and on the next the floor test finds nothing after a 1 px drop, so he leaves the ground falling 1.25 px/frame and that frame moves him by the air's rules. His feet are then 1, 2, 3, 5, 7, and 9 px below the ledge on that frame and the five after (computed). mega-man-4/walk-off
About: Mega Man 4 Coyote time
Value
{
"heights": [
1,
2,
3,
5,
7,
9
],
"v": -320
}src/bank3C.asm#L171-L172JSR L_82C8 ; shared physics; carry = grounded
BCC L_80F9 ; airborne: done for this frameMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L410-L419JSR L_92C5 ; environment + ground check
BCS L_8320 ; on ground: landing handling
LDA #$07 ; sub-type $07 = falling
CMP ent_sub_type ; already falling sprite?
BEQ L_8306 ; yes: skip set
CLC ; prepare add
ADC $33 ; + shoot-pose offset
JSR entity_set_subtype ; set falling sprite
LDA #$01 ; state $01 = airborne
STA player_state ; now airborneMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_entity_physics.asm#L365-L367L_F1B0:
JSR L_F2F6 ; apply gravity (accelerate downward)
JSR tile_collision_detect ; check tile collisionChecked by claude, 2026-10-26 — read the ground state's entry and the air routine L_82C8 to L_8352 at the pinned commit; the heights come from the rest speed, gravity, and the move-then-gravity order (ground-check, gravity, motion-order), with the start's Y fraction of 0The heights are the model's (src/widgets/platformer/mega-man-4.model.ts); the atlas hasn't measured a walk-off in the cartridge.
Mega Man 4 gives no grace jump off a ledge. The ground state reads A only after the air routine reports him standing, and the air state never reads it, so a press on the frame the floor test first fails, or on any later frame of the fall, doesn't jump. mega-man-4/no-coyote-time
About: Mega Man 4 Coyote time
CodeMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/bank3C.asm#L171-L175JSR L_82C8 ; shared physics; carry = grounded
BCC L_80F9 ; airborne: done for this frame
LDA joy1_press ; newly pressed buttons
AND #$80 ; A button (jump)?
BNE L_813E ; pressed: jump/slide startMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L447-L458L_833D:
LDA joy1_held ; held buttons
AND #$03 ; L/R bits
STA ent_dir ; air control: face held dir
STA $31 ; store direction
LDY #$00 ; speed selector 0 = walk
JSR entity_horiz_dispatch ; move horizontally in air
JSR L_8CBD ; per-weapon handler dispatch
JSR L_922B ; Up held: grab ladder
CLC ; carry clear = airborne
L_8352:
RTS ; doneChecked by claude, 2026-10-26 — read the ground state's entry and the air state ($82C8) through its return at the pinned commit; neither path of the air state tests joy1_pressA press on the frame whose move carries him off the edge still jumps, because that frame's floor test ran before the move (walk-off).
When a rising Mega Man's move puts a ceiling point, x-7 or x+7 at 9 px above his center, in a solid tile, entity_align_to_tile adds 16 minus the point's depth into the tile to his Y pixel, which puts the point on the row under the tile, and entity_stop_y sets his speed to -1 px/frame; he stays in the air and falls from the next frame. mega-man-4/ceiling-hit
About: Mega Man 4 Jump physics
Value: -256 1/256 px/frame
CodeMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/fixed/fixed_entity_physics.asm#L429-L437L_F219:
JSR L_F2F6 ; apply gravity (accelerate downward)
JSR tile_collision_detect ; check tile collision
LDA $10 ; load collision result
AND #$10 ; test solid-tile bit
BEQ L_F22B ; not solid -> return no collision
JSR entity_align_to_tile ; push entity Y down out of ceiling
L_F228:
JSR entity_stop_y ; stop vertical velocityMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_tile_collision.asm#L1949-L1955L_D9CD:
LDA $11 ; load collision Y pixel position
AND #$0F ; mask to within-tile offset
EOR #$0F ; complement (distance to boundary)
SEC ; set carry for +1 in ADC
ADC ent_y_px,X ; add to entity Y pixel position
STA ent_y_px,X ; store adjusted positionChecked by claude, 2026-10-26 — read the rising branch of entity_player_collide (L_F1FD to L_F22B) and entity_align_to_tile ($D9CD) at the pinned commitThe rising branch returns carry clear even after a hit, so the air routine runs on: the release test sees a falling speed and skips the cut, and the frame's horizontal move follows. Mega Man 2 instead leaves the speed at -0.25 after a hit (mega-man-2/ceiling-hit).
Nothing tests the ceiling before a jump starts: a fresh A press on the ground sets the jump speed and runs the air routine, whose rise is the first test. On tile floor the lowest ceiling he can walk under is 32 px up, 11 px over his ceiling points, so a jump there rises 5 px and then 4.75 px on its first two frames and meets the block on the 3rd, which turns it into a fall. mega-man-4/jump-under-ceiling
About: Mega Man 4 Jump physics
CodeMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/bank3C.asm#L179-L190L_813E:
LDA ent_angle ; player $04B0 flag
BNE L_8149 ; nonzero: normal jump
LDA joy1_held ; held buttons
AND #$04 ; Down held?
BNE L_8156 ; Down+A = slide
L_8149:
LDA #$00 ; zero sub-velocity
STA ent_yvel_sub ; Y velocity sub = 0
LDA #$05 ; jump speed 5 px/frame
STA ent_yvel ; Y velocity = +5 (up)
JMP L_82C8 ; enter airborne physicsChecked by claude, 2026-10-26 — read the ground state's A handler at the pinned commit, and stepped the lab model under a ceiling 32 px up (src/widgets/platformer/mega-man-4.ceiling.test.ts)A block whose bottom is 16 px over the floor stops him with its wall points (terrain-box), so a ceiling 32 px up is the lowest he walks under; there the press frame lifts his ceiling points to 26 px and the second to 30.75, and the third frame's rise meets it. Only off the tile grid, on a platform entity, could the press frame itself meet a ceiling.
A jump moves Mega Man on its press frame. The ground state has already run the air routine and found the floor; the A press sets 5 px/frame and runs the air routine again, which lifts him 5 px and moves him by the air's rules, so the ground's own walk doesn't run that frame. Measured in Mesen from power-on, standing where he lands at the start of Skull Man's stage, a jump's first frame raised his Y by exactly 5 px. mega-man-4/takeoff
About: Mega Man 4 Variable jump height
Value
{
"rise": 5,
"peak": 52.5,
"peakFrame": 20,
"airFrames": 40
}src/bank3C.asm#L179-L190L_813E:
LDA ent_angle ; player $04B0 flag
BNE L_8149 ; nonzero: normal jump
LDA joy1_held ; held buttons
AND #$04 ; Down held?
BNE L_8156 ; Down+A = slide
L_8149:
LDA #$00 ; zero sub-velocity
STA ent_yvel_sub ; Y velocity sub = 0
LDA #$05 ; jump speed 5 px/frame
STA ent_yvel ; Y velocity = +5 (up)
JMP L_82C8 ; enter airborne physicsMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L171-L176JSR L_82C8 ; shared physics; carry = grounded
BCC L_80F9 ; airborne: done for this frame
LDA joy1_press ; newly pressed buttons
AND #$80 ; A button (jump)?
BNE L_813E ; pressed: jump/slide start
JMP L_81D6 ; no jump: ground movementChecked by claude, 2026-10-26 — read the ground state's entry and A handler at the pinned commit. Measured in Mesen (MesenCE 2.2.1) from power-on at the start of Skull Man's stage, a jump with A held rose 5 px on its first frame, 52.5 px by frame 20, and landed on frame 41 after 40 frames in the airProbe: probes/jump-physics/movement.mjsMega Man 2 sets its speed on the press frame but moves him from the next (mega-man-2/jump-start). The held jump's height is 21 × 5 − 0.25 × 210 = 52.5 px (computed from jump-line and gravity).
Mega Man walks and moves through the air at the speed held in his X speed (ent_xvel), 1.30 px/frame ($01.$4C) on ordinary floor; no walking or air code sets it. The slide writes 2.5 px/frame there, and its end and the jump out of it write $01.$4C back. Measured in Mesen from power-on, at the start of Skull Man's stage, the X speed read $01.$4C and each walking frame moved him 332/256 px. mega-man-4/walk-speed
About: Mega Man 4 Running
Value: 332 1/256 px/frame
VerifiedMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/bank3C.asm#L325-L331L_825D:
LDA $31 ; walk direction
STA ent_dir ; face walk direction
JSR L_93DB ; wind vs walk adjust (stage $08)
BCC L_826C ; wind overrides: skip move
LDY #$00 ; speed selector 0 = walk
JSR entity_horiz_dispatch ; move player horizontallyMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_entity_physics.asm#L793-L800L_F3ED:
CLC ; clear carry (default: no collision)
LDA ent_dir,X ; load direction byte
AND #$03 ; mask horiz direction bits (0-1)
BEQ L_F445 ; no direction -> return
AND #$01 ; test bit 0 (right?)
BEQ L_F3FC ; no -> move left
JMP entity_move_right ; move entity rightMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L525-L533L_83D3:
LDA #$00 ; state $00 = ground
STA player_state ; stand up
LDA #$4C ; walk speed 1.30 px/frame
STA ent_xvel_sub ; X velocity sub
STA $37 ; mirror in $37
LDA #$01 ; 1 px
STA ent_xvel ; X velocity
STA $38 ; mirror in $38Checked by claude, 2026-10-26 — read the walk (L_825D), the air move (L_833D), entity_horiz_dispatch, and every write to ent_xvel in bank $3C at the pinned commit; outside the slide, the hurt state, and the wind and quicksand code of stages $01, $03, and $08, only `$01`.`$4C` is written. Measured in Mesen (MesenCE 2.2.1) from power-on at the start of Skull Man's stageProbe: probes/jump-physics/movement.mjs332/256 = 1.296875 px/frame, Mega Man 3's walking speed too (mega-man-3/walk-speed).
On the ground Mega Man's sprite sub-type decides whether he moves. Standing ($01), a held direction sets the pause pose ($0D) and moves him one frame; in the pause pose he doesn't move until its animation advances, and then walks ($04) toward a held direction, or stands with none. Walking, the same direction keeps him moving; letting go puts him in the pause pose without a move, and the other direction does too, with one frame's move the new way. mega-man-4/ground-states
About: Mega Man 4 Running
CodeMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/bank3C.asm#L286-L324L_8214:
LDA ent_sub_type ; player sub-type
…
L_822B:
LDA ent_anim_phase ; player anim counter
BEQ L_82A6 ; anim step done: idle
…
L_8243:
LDA joy1_held ; held buttons
AND #$03 ; L/R held?
BEQ L_8292 ; none: stop walking
AND $31 ; same as current direction?
…Mega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_entity_physics.asm#L880-L884L_F446:
STA ent_sub_type,X ; set entity sub-type
LDA #$00 ; zero
STA ent_anim_phase,X ; clear animation counter
STA ent_var3,X ; clear variable 3Checked by claude, 2026-10-26 — read the ground state's walk (L_820C to L_82A6) and entity_set_subtype at the pinned commit; $31 holds the walk direction, and setting a sub-type zeroes its animation phaseSub-types $05, $0E, and $0F are the same poses with the shot's offset added ($33), and the code treats them alike. A press of A or Down+A on the ground is handled before any of this, in any pose.
The pause pose lasts 6 frames: its animation (sub-type $0D in bank $0D) holds its first frame for 6 animation ticks, one a frame. So from standing, or on a turn, a held direction moves Mega Man on the first frame, holds him still on the next 5, and walks from the 7th; letting go of a walk holds him for 6 frames, whatever the d-pad does, before he stands or walks on. Measured in Mesen from power-on, at the start of Skull Man's stage, Right moved him on frame 1, not on frames 2 to 6, and on every frame from 7 to 40; letting go on frame 41 kept him still in the pause pose on frames 41 to 46, and he stood from frame 47. mega-man-4/ground-measured
About: Mega Man 4 Running
Value
{
"pauseFrames": 6
}src/fixed/fixed_entity_core.asm#L295-L306LDA ent_var3,X ; load animation sub-frame counter
AND #$7F ; mask off bit 7 for counter value
INC ent_var3,X ; increment raw sub-frame counter
INY ; Y = 1 (index to frame duration in AI data)
CMP (temp_00),Y ; compare sub-frame to duration threshold
BNE L_DCE2 ; if not equal, sub-frame not done yet
LDA ent_var3,X ; reload sub-frame counter
AND #$80 ; preserve only bit 7 (phase flag)
STA ent_var3,X ; reset counter, keep phase flag
LDA ent_anim_phase,X ; load animation phase counter
AND #$7F ; mask off bit 7 for phase index
INC ent_anim_phase,X ; increment raw animation phase counterMega Man 4 (USA) (Rev 1) · 0x1A717-0x1A718 — bank $0D $A707-$A708, the start of sub-type $0D's animation: 1 frame (the loop count), and 5, the counter value that advances itChecked by claude, 2026-10-26 — read the animation tick in the fixed bank (the sub-frame counter ent_var3 advances the phase when it reaches the descriptor's second byte, so after 6 ticks) and the descriptor sub-type `$0D` reads through `$BE0D`/`$BF0D` in bank `$0D`, which `$DFFA` maps for the player. Measured in Mesen (MesenCE 2.2.1) from power-on at the start of Skull Man's stage, with the animation phase read each frameProbe: probes/jump-physics/movement.mjsMega Man 6 holds a 5-frame halt after the first step too (mega-man-6/walk-start), from a counter, not an animation.
Every frame in the air the d-pad alone sets where Mega Man moves: the air routine copies the held Left or Right into his direction and moves him by his X speed that way, 1.30 px/frame, and with neither held he doesn't move. He turns at once, and nothing carries over from the ground. mega-man-4/air-speed
About: Mega Man 4 Air control
CodeMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/bank3C.asm#L447-L453L_833D:
LDA joy1_held ; held buttons
AND #$03 ; L/R bits
STA ent_dir ; air control: face held dir
STA $31 ; store direction
LDY #$00 ; speed selector 0 = walk
JSR entity_horiz_dispatch ; move horizontally in airMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_entity_physics.asm#L793-L800L_F3ED:
CLC ; clear carry (default: no collision)
LDA ent_dir,X ; load direction byte
AND #$03 ; mask horiz direction bits (0-1)
BEQ L_F445 ; no direction -> return
AND #$01 ; test bit 0 (right?)
BEQ L_F3FC ; no -> move left
JMP entity_move_right ; move entity rightChecked by claude, 2026-10-26 — read L_833D and entity_horiz_dispatch at the pinned commitMega Man 2 sets a separate air speed, 1.3125 px/frame (mega-man-2/air-speed).
The frame a falling Mega Man's floor test finds ground puts him in the ground state with the walking pose ($04), and still moves him by the air's rules. On the next frame he walks on at once if he holds the direction he held on landing; none starts the pause pose, and the other direction starts it with one move that way, as a turn does. The air state reads no A press, so a press on the landing frame is lost, and the next frame's press jumps. mega-man-4/landing
About: Mega Man 4 Jump physics
VerifiedMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/bank3C.asm#L432-L453L_8320:
LDA player_state ; player state
BEQ L_8352 ; already grounded state: done
LDA #$23 ; sound $23 = landing thud
JSR queue_sound ; play it
LDA #$00 ; state $00 = ground
STA player_state ; land: back to ground state
LDA #$04 ; sub-type $04 = step
CLC ; prepare add
ADC $33 ; + shoot-pose offset
JSR entity_set_subtype ; set landing sprite
JSR L_946E ; capture walk vel for wind sys
…Checked by claude, 2026-10-26 — read L_8320 through L_833D at the pinned commit. Measured in Mesen (MesenCE 2.2.1) from power-on at the start of Skull Man's stage: a jump with Right held walked on from the landing without a pause; a standing jump landed into the pause pose; Left on the frame after a Right-held landing moved him 1.30 px back into the pause pose; an A press on the landing frame didn't jump, and a press on the next frame didProbe: probes/jump-physics/movement.mjsMega Man 2 instead lands running or into a landing stop (mega-man-2/landing).
Down and a fresh A press start a slide only if the slide's start point, 15 px ahead of his center and 4 px below it, isn't in a wall; with a wall there the press does nothing, neither slide nor jump. The slide moves his Y pixel 2 px down and tests a lower, wider box: floor points 10 px below the new center at x-14, x, and x+14, and wall points 15 px ahead, level with it and 9 px below. With no floor under those points, ground 16 px below the center keeps the slide going, and none stands him up. mega-man-4/slide-box
About: Mega Man 4 Ground slide
Value
{
"lower": 2,
"floor": {
"dy": 10,
"dx": [
-14,
0,
14
]
},
"ceiling": {
"dy": 0,
"dx": [
-14,
0,
14
]
},
"wall": {
"dx": 15,
"dy": [
0,
9
]
},
"start": {
"dx": 15,
"dy": 4
},
"headroom": {
"dy": -14,
"dx": [
-14,
0,
14
]
},
"below": {
"dy": 16,
"dx": [
-14,
0,
14
]
}
}src/bank3C.asm#L204-L213L_816D:
LDA temp_08 ; slide direction
LSR A ; /2 (1->0 right, 2->1 left)
CLC ; prepare add
ADC #$06 ; probe index 6/7 (slide height)
TAY ; Y = collision probe selector
JSR scroll_collision_check ; check wall at slide height
LDA $10 ; collision result
AND #$10 ; bit 4 = solid
BNE L_81D6 ; wall in the way: no slideMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L229-L232LDA ent_y_px ; player Y pixel
CLC ; prepare add
ADC #$02 ; lower hitbox by 2 px
STA ent_y_px ; store YMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L464-L472LDY #$04 ; probe selector 4 (slide height)
JSR entity_player_collide ; ride/collide with platforms
JSR L_92C5 ; environment + ground check
BCS L_8368 ; still grounded: continue slide
LDY #$39 ; probe $39 = above (slide box)
JSR tile_collision_detect ; check tile above
LDA $10 ; collision result
AND #$10 ; bit 4 = solid overhead
BEQ L_83D3 ; no ceiling: stand upMega Man 4 (USA) (Rev 1) · 0x7D72E-0x7D748 — $D71E-$D738: shape 3 (standing headroom) 02 F2 F2 0E 0E, 14 px up from x-14 in steps of 14; shape 4 (slide floor) 02 0A F2 0E 0E; shape 5 (slide ceiling) 02 00 F2 0E 0EMega Man 4 (USA) (Rev 1) · 0x7D6E7-0x7D6E7 — $D6D7, shape $39's low byte, $00 ($D717 holds $D8)Mega Man 4 (USA) (Rev 1) · 0x7D810-0x7D814 — $D800-$D804, shape $39: 02 10 F2 0E 0E, 16 px down from x-14 in steps of 14Mega Man 4 (USA) (Rev 1) · 0x7D898-0x7D8B4 — $D888-$D8A4: wall shapes 4 and 5 (slide) 01 0F 00 09 and 01 F1 00 09, two points 15 px ahead, level and 9 px down; shapes 6 and 7 (slide start) 00 0F 04 and 00 F1 04Checked by claude, 2026-10-26 — read the slide set-up (L_8156 to L_81D3) and the slide state's floor test at the pinned commit, and the shapes it names (vertical 3, 4, 5, $39; horizontal 4 to 7) as bytes in the Rev 1 ROMThe floor points stay on the same row, so on flat ground his feet don't move, and the slide box's wall points cover the 10 rows over his feet. Mega Man 3 lowers him 2 px too (mega-man-3/slide). His Y pixel stays 2 px low on the frame he stands up, until the next frame's floor test puts him back.
A slide ends in two ways. When the slide box finds no floor and no ground 16 px below his center either (slide-box), he stands up at once, with no headroom test, before that frame's A test and move. Otherwise it ends at the standing test, which a wall, the timer's end, or a turn sends it to. A turn (a held direction against the slide's) zeroes the timer and still moves him 2.5 px the new way on that frame. The test checks his standing headroom, three points 14 px above the slide's center (12 px above his standing one) at x-14, x, and x+14: a solid tile there keeps him sliding at 2.5 px/frame, testing again each frame, and otherwise he stands up in the walking pose, at walking speed, after that frame's move. A fresh A press without Down jumps out of it instead, at 5 px/frame and walking speed, if the same headroom is clear, from the 2 px lower slide center; the earliest such press comes on the slide's 3rd frame (slide-line). mega-man-4/slide-exit
About: Mega Man 4 Ground slide
VerifiedMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) ·src/bank3C.asm#L464-L472 — the slide state's entry; probe $39 is the ground 16 px below his center (slide-box)LDY #$04 ; probe selector 4 (slide height)
JSR entity_player_collide ; ride/collide with platforms
JSR L_92C5 ; environment + ground check
BCS L_8368 ; still grounded: continue slide
LDY #$39 ; probe $39 = above (slide box)
JSR tile_collision_detect ; check tile above
LDA $10 ; collision result
AND #$10 ; bit 4 = solid overhead
BEQ L_83D3 ; no ceiling: stand upMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L497-L524L_839D:
LDA joy1_held ; held buttons
AND #$03 ; L/R bits
BEQ L_83B4 ; none: keep sliding
STA temp_00 ; save held direction
AND $31 ; same as slide direction?
BNE L_83B4 ; yes: keep sliding
LDA temp_00 ; held direction
STA $31 ; turn: new slide direction
STA ent_dir ; face new direction
LDA #$00 ; zero
STA $34 ; end slide timer (turn ends it)
…Mega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L525-L537L_83D3:
LDA #$00 ; state $00 = ground
STA player_state ; stand up
LDA #$4C ; walk speed 1.30 px/frame
STA ent_xvel_sub ; X velocity sub
STA $37 ; mirror in $37
LDA #$01 ; 1 px
STA ent_xvel ; X velocity
STA $38 ; mirror in $38
LDX #$00 ; X = player slot
LDA #$04 ; sub-type $04 = step
JSR entity_set_subtype ; set standing sprite
…Mega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L473-L496L_8368:
LDA joy1_press ; newly pressed
AND #$80 ; A button?
BEQ L_839D ; no jump: steering
LDA joy1_held ; held buttons
AND #$04 ; Down held?
BNE L_839D ; Down+A: stay sliding
LDY #$03 ; probe 3 = standing headroom
JSR tile_collision_detect ; check tile above head
LDA $10 ; collision result
BNE L_839D ; blocked: no jump
JSR L_83F2 ; platform overhead check
…Checked by claude, 2026-10-26 — read the slide state ($8353) through L_83EF at the pinned commit. Measured in Mesen (MesenCE 2.2.1) from power-on at the start of Skull Man's stage on flat floor: a turn on a slide's 6th frame moved him 2.5 px back and stood him up, and a slide's end stood him up in the walking pose at 1.30 px/frame; an A press without Down on frame 3 jumped out at 5 px/frame and 1.30 px/frame, and A held from the set-up into frame 2 didn't. The lab's conformance check (probes/lab/conformance.mjs) matched the model frame for frame on these and on presses on frame 9, turns, and Down with A heldProbe: probes/jump-physics/movement.mjsThe stand-up leaves his walk direction ($31) as the slide's, so a direction held the same way walks on the next frame without the pause. A platform entity overhead blocks the jump and the stand-up too (L_83F2), which the lab doesn't model.
Mega Man 4's movement on ordinary floor differs from Mega Man 3's in five numbers. The first is the jump's take-off speed: 5 px/frame ($05.$00) against Mega Man 3's 4.89 ($04.$E5). The second is the speed on the ground and after a landing: -1 px/frame ($FF.$00) against -0.25 ($FF.$C0). Third, a release cuts a rise to 0, where Mega Man 3 sets the same -0.25. Fourth, the slide timer starts at 26 ($1A) against 20 ($14), so the slide moves on 26 frames against 21 (slide-line, mega-man-3/slide). Fifth, Mega Man 4 tests for the jump out of a slide on every slide frame, where Mega Man 3 skips the test while the timer is 12 ($0C) or more, so the earliest jump out comes on the 3rd frame against the 9th. mega-man-4/movement-differs-from-mega-man-3
About: Mega Man 4 Jump physics
Value
{
"takeoff": 1280,
"other": 1253
}src/bank3C.asm#L186-L189LDA #$00 ; zero sub-velocity
STA ent_yvel_sub ; Y velocity sub = 0
LDA #$05 ; jump speed 5 px/frame
STA ent_yvel ; Y velocity = +5 (up)Mega 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 4 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/fixed_entity_physics.asm#L648-L652 — entity_stop_y, which a landing and every grounded frame's floor contact callL_F324:
LDA #$00 ; zero
STA ent_yvel_sub,X ; clear vert velocity sub-pixel
LDA #$FF ; $FF = stopped / minimal
STA ent_yvel,X ; set vert velocity pixelMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/movement.asm#L203-L204gravity_snap_floor: jsr snap_y_to_floor ; snap to tile boundary (down)
gravity_reset_vel: jsr reset_gravity ; reset velocity to rest valueMega 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 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L425-L430LDA joy1_held ; held buttons
AND #$80 ; A still held?
BNE L_8338 ; held: full jump
LDA #$00 ; zero
STA ent_yvel_sub ; cut jump: Y vel sub = 0
STA ent_yvel ; cut jump: Y vel = 0Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_ground.asm#L264-L267 — reset_gravity sets the player's rest speed, `$FF`.`$C0` (movement.asm L424-L428), whatever this line's comment sayslda 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 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L217-L218LDA #$1A ; slide duration 26 frames
STA $34 ; set slide timerMega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L288-L289lda #$14 ; slide timer = 20 frames
sta $33 ; store slide timerMega Man 4 (U), annotated ca65 disassembly (plasticsmoke) · src/bank3C.asm#L473-L478L_8368:
LDA joy1_press ; newly pressed
AND #$80 ; A button?
BEQ L_839D ; no jump: steering
LDA joy1_held ; held buttons
AND #$04 ; Down held?Mega Man 3 (U), annotated ca65 disassembly (plasticsmoke) · src/fixed/player_states.asm#L349-L355dec $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 jumpChecked by claude, 2026-10-26 — read both games' jump press, floor contact and rest speed, release cut, slide timer, and slide A test at their pinned commits; the frame counts follow from mega-man-4/slide-line and mega-man-3/slide. Compared in code, not in the lab: Mega Man 3's model was being written at the same timeThe two games share the walk and air speed (1.30 px/frame), the slide's 2.5 px/frame, gravity of 0.25 px/frame², and the 7 px/frame fall cap; each set's own claims quote them (mega-man-4/walk-speed, slide-line, gravity, fall-cap; mega-man-3/walk-speed, slide, jump-line). The walk's pause and the box were not compared here.
Cited by: Mega Man (classic series)