data/claims/metroid-samus.yamlMetroid, Samus's movement (nmikstas's disassembly of the US NES release)
Game: Metroid.
Every jump takes off with the whole part of Samus's vertical speed at $FC, 4 px/frame up. The High Jump Boots change the gravity, not the speed: SetSamusJump sets SamusGravity to $18, or $12 with the boots. metroid-samus/jump-speed
Value: -4 px/frame
Codemetroid-disassembly: Metroid software disassembly ·Source_Files/Bank07.asm#L2672-L2698SetSamusJump:
…
LDA #$FC
STA ObjVertSpeed
…
LCFF3: LDY #$18 ; gravity (high value -> low jump)
LDA SamusGear
AND #gr_HIGHJUMP
BEQ + ; branch if Samus doesn't have High Jump
LDY #$12 ; lower gravity value -> high jump!
* STY SamusGravityChecked by claude, 2026-10-21 — read SetSamusJump, which both the standing and the running jump call (LD002's lava branch and SamusRun's LCD40)Each frame VertAccelerate adds SamusGravity to the fraction byte of Samus's vertical speed and carries into its whole part, so gravity is SamusGravity/256 px/frame²: $18 (0.09375) in a jump, $12 with the High Jump Boots, $1A after walking off a ledge, and $38 after a hit (computed: 0.0703125, 0.1015625, 0.21875). A fall is capped at 5 px/frame and a rise at 6. metroid-samus/gravity
Value
{
"jump": 0.09375,
"highJump": 0.0703125,
"fall": 0.1015625,
"hit": 0.21875,
"maxFall": 5,
"maxRise": 6
}Source_Files/Bank07.asm#L5582-L5587;gravity used in the acceleration calculation. The higher the number, the more intense the gravity.
;The possible values for gravity are as follows:
;#$38-When Samus has been hit by an enemy.
;#$1A-When Samus is falling.
;#$18-Jump without high jump boots.
;#$12-Jump with high jump boots.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2693-L2698LCFF3: LDY #$18 ; gravity (high value -> low jump)
LDA SamusGear
AND #gr_HIGHJUMP
BEQ + ; branch if Samus doesn't have High Jump
LDY #$12 ; lower gravity value -> high jump!
* STY SamusGravitymetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2440-L2443* lda #$FD
sta ObjVertSpeed
lda #$38 ;Samus is hit. Store Samus hit gravity.
sta SamusGravity ;metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5606-L5616LE3A0: lda #$1A ;Samus is falling. Store falling gravity value.
LE3A2: sta SamusGravity ;
…
LE3A5:* ldx #$05 ;Load X with maximum downward speed.
LE3A7: lda VertCntrLinear ;
LE3AA: clc ;The higher the gravity, the faster this addition overflows
LE3AB: adc SamusGravity ;and the faster ObjVertSpeed is incremented.
LE3AE: sta VertCntrLinear ;
LE3B1: lda ObjVertSpeed ;Every time above addition sets carry bit, ObjVertSpeed is
LE3B4: adc #$00 ;incremented. This has the effect of speeding up a fallmetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5620-L5633LE3BB: lda #$00 ;
LE3BD: cmp VertCntrLinear ;Sets carry bit.
LE3C0: sbc ObjVertSpeed ;Subtract ObjVertSpeed to see if maximum speed has
LE3C3: cmp #$06 ;been exceeded.
LE3C5: ldx #$FA ;Load X with maximum upward speed.
…
LE3C9:* cmp #$05 ;Has maximum downward speed been reached?
LE3CB:* bcc + ;If not, branch.
…
LE3CD: jsr StopVertMovement ;($D147)Clear verticle movement data.
LE3D0: stx ObjVertSpeed ;Set Samus vertical speed to max.Checked by claude, 2026-10-25 — read the jump's gravity store at LCFF3 ($18, or $12 with gr_HIGHJUMP) and the hit's ($38 after ObjVertSpeed $FD), then VertAccelerate to its end. Rising (L5617-L5633), 0 − speed ≥ 6 loads $FA and stores it as the whole speed with the fraction cleared, so a rise is capped at 6 px/frame; falling, the cap is 5. The $1A store runs only when Samus is grounded and nothing is below her. Neither SamusJump nor SamusRun writes SamusGravity while she is airborne, so the jump button doesn't change itCited by: Metroid
Once a jump has carried Samus 32 px up (SamusJmpDsplcmnt at $20), letting go of jump while she still rises calls StopVertMovement, which sets her vertical speed to 0. The running-jump handler does the same past 32 px. metroid-samus/release-cap
About: Metroid Variable jump height
Value: 32 px risen before a release counts
Codemetroid-disassembly: Metroid software disassembly ·Source_Files/Bank07.asm#L2701-L2709SamusJump:
LDA SamusJmpDsplcmnt
BIT ObjVertSpeed
BPL + ; branch if falling down
CMP #$20
BCC + ; branch if jumped less than 32 pixels upwards
BIT Joy1Status
BMI + ; branch if JUMP button still pressed
JSR StopVertMovement ;($D147)Stop jump (start falling).metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2271-L2280* cpy #$20
bcc ++
…
* bit Joy1Status
bmi +
jsr StopVertMovement ;($D147)metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2867-L2870StopVertMovement:
LD147: ldy #$00
sty ObjVertSpeed
sty VertCntrLinearChecked by claude, 2026-10-21 — read SamusJump and SamusRun's airborne branch, which reaches the cpy #$20 test only while Samus rises (bit ObjVertSpeed / bmi)Cited by: Metroid, Variable jump height
On the ground Samus accelerates at $30/256 (0.1875) px/frame² toward SamusHorzSpdMax/16 px/frame, which VertAccelerate sets to $18 (1.5 px/frame) while she is grounded. With no direction held, StopHorzMovement clears her horizontal speed at once. metroid-samus/run
Value
{
"accel": 0.1875,
"max": 1.5
}Source_Files/Bank07.asm#L2245-L2247RunAccelerationTbl:
LCCC0: .byte $30 ;Accelerate right.
.byte $D0 ;Accelerate left.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5590-L5593LE37A: lda SamusGravity ;Is Samus rising or falling?
LE37D: bne ++ ;Branch if yes.
LE37F: lda #$18 ;
LE381: sta SamusHorzSpdMax ;Set Samus maximum running speed.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5648-L5690HorzAccelerate:
LE3E5: lda SamusHorzSpdMax
jsr Amul16 ; * 16
…
jsr Adiv16 ; / 16
…
lda HorzCntrLinear
clc
adc SamusHorzAccel
…
lda $00
sta HorzCntrLinear
…metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2316-L2319* lda Joy1Status
and #$03
bne +
jsr LCF55metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2600-L2666ClearHorzMvmntData:
LCF4C: LDY #$00 ;
STY ObjHorzSpeed ;Set Samus Horizontal speed and horizontal
STY HorzCntrLinear ;linear counter to #$00.
…
StopHorzMovement:
…
LCF5D: JSR NoHorzMoveNoDelay ;($CF81)Clear horizontal movement and animation delay data.
…
LCF81: JSR ClearHorzData ;($CFB7)Clear all horizontal movement data.
…
ClearHorzData:
…Checked by claude, 2026-10-21 — read SetSamusRun, SamusRun's grounded branch, and HorzAccelerate, which clamps the speed to $01.$80 (whole.fraction) for a maximum of $18: 1.5 px/frameThe accelerations store whole.fraction in two bytes, ObjHorzSpeed and HorzCntrLinear, as the vertical speed does.
Cited by: Metroid
A running jump sets the top speed to $12 (1.125 px/frame). In the air a held direction sets the acceleration toward it; with neither held, a jump still in the plain jump animation loses its horizontal speed, and a somersault keeps its speed and acceleration. metroid-samus/air-run
About: Metroid Air control
Value: 1.125 px/frame
Codemetroid-disassembly: Metroid software disassembly ·Source_Files/Bank07.asm#L2305-L2309lda Joy1Change
bpl + ; branch if JUMP not pressed
LCD40: jsr LCFC3
lda #$12
sta SamusHorzSpdMaxmetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2286-L2290* lda SamusInLava
…
* jsr LCF88metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2642-L2667LCF88: LDA Joy1Status
AND #$03
BEQ +
JSR BitScan ;($E1E1)
TAX
JSR LCCB7
…
* LDA SamusGravity
BMI +
BEQ +
LDA AnimResetIndex
CMP #an_SamusJump
…Checked by claude, 2026-10-21 — read SamusRun's airborne branch, LCF88, and LCCB7, which stores RunAccelerationTbl's entry in SamusHorzAccelCited by: Metroid
In a jump from a standstill, LD055 sets the whole part of Samus's horizontal speed outright every frame, 1 px/frame toward a held direction and 0 with neither held. metroid-samus/air-stand
About: Metroid Air control
Value: 1 px/frame
Codemetroid-disassembly: Metroid software disassembly ·Source_Files/Bank07.asm#L2738-L2767LD055: ldx #$01
ldy #$00
lda Joy1Status
lsr
bcs + ; branch if RIGHT pressed
dex
lsr
bcc ++++ ; branch if LEFT not pressed
dex
…
* stx ObjHorzSpeedmetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2710-L2711* JSR LD055
JSR LCF2EChecked by claude, 2026-10-21 — read SamusJump and LD055; the fourth anonymous label after `bcc ++++` is the `stx ObjHorzSpeed`, with X decremented to 0X is $FF, 1 px/frame left, with left held.
Cited by: Metroid
Once Samus is airborne, the run and jump handlers start a jump only while she is in lava. Walking off a ledge makes her airborne at once: VertAccelerate sets the falling gravity on the first frame with nothing below her. metroid-samus/no-air-jump
About: Metroid Coyote time
Codemetroid-disassembly: Metroid software disassembly ·Source_Files/Bank07.asm#L2252-L2255SamusRun:
LCCC2: ldx SamusDir
lda SamusGravity
beq +++++++metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2286-L2289* lda SamusInLava
beq +
lda Joy1Change
bmi LCD40 ; branch if JUMP pressedmetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2719-L2724LDA SamusInLava
BEQ +
LDA Joy1Change
BPL + ; branch if JUMP not pressed
JSR LCFC3metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5590-L5607LE37A: lda SamusGravity ;Is Samus rising or falling?
…
LE38F: jsr CheckMoveDown ;($E7AD)Is Samus obstructed downwards?
…
LE3A0: lda #$1A ;Samus is falling. Store falling gravity value.
LE3A2: sta SamusGravity ;Checked by claude, 2026-10-21 — read SamusRun's airborne branch (SamusGravity nonzero) to its end at `bne SetSamusData`, and SamusJump; the grounded jump read (`lda Joy1Change / bpl +` before LCD40) is reached only through the `beq` taken when SamusGravity is 0The atlas hasn't read the pointing-up and morph-ball handlers for jump reads in the air; the ball's jump press unrolls it (metroid-samus/morph-ball).
Cited by: Coyote time, Metroid, Metroid: Zero Mission, Wall jump
Samus is a box around her center: each animation frame's data sets ObjRadY and ObjRadX, the distances from her center to her top or bottom and to her sides. The level checks add these radii to her position, and so does the distance check between her and an enemy. metroid-samus/hitbox
Source_Files/Metroid_Defines.asm#L287-L288.alias ObjRadY $0301 ;Distance in pixels from object center to top or bottom.
.alias ObjRadX $0302 ;Distance in pixels from object center to left or right side.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L4810-L4816LDEC1: sta ObjRadY,x ;Get verticle radius in pixles of object.
…
LDEC7: lda ($00),y ;Get horizontal radius in pixels of object.
LDEC9: sta ObjRadX,x ;metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5594-L5597LE384: lda ObjectY ;
LE387: clc ;
LE388: adc ObjRadY ;Check is Samus is obstructed downwards on y roommetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5981-L5987MoveSamusLeft:
LE626: lda ObjectX
sec
sbc ObjRadX
and #$07
bne + ; only call crash detection every 8th pixel
jsr CheckMoveLeft ; check if player is obstructed to the LEFTmetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L7802-L7806DistFromObj0ToEn1:
LF1B3: lda ObjRadY,x
jsr LF1E7
lda ObjRadX,xChecked by claude, 2026-10-21 — read the frame-data reader, VertAccelerate's ground test, MoveSamusLeft, and the distance helpers, which add the enemy's EnRadY and EnRadX to Samus's radiiCited by: Hitbox
Samus rolls into the ball only with the Maru Mari (gr_MARUMARI) and only on the ground. In the ball, Up or a jump press unrolls her unless Down is held, and only if she has room to stand. metroid-samus/morph-ball
About: Metroid Morph ball
Codemetroid-disassembly: Metroid software disassembly ·Source_Files/Bank07.asm#L2794-L2799SetSamusRoll:
LD0B5: lda SamusGear
and #gr_MARUMARI
beq + ; branch if Samus doesn't have Maru Mari
lda SamusGravity
bne +metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2820-L2834SamusRoll:
lda Joy1Change
and #$08 ; UP pressed?
bne + ; branch if yes
bit Joy1Change ; JUMP pressed?
bpl ++ ; branch if no
* lda Joy1Status
and #$04 ; DOWN pressed?
bne + ; branch if yes
;break out of "ball mode"
…
jsr CheckMoveUpChecked by claude, 2026-10-21 — read SetSamusRoll and SamusRoll; CheckMoveUp's failure branch keeps the ballCited by: Morph ball
IsScrewAttackActive counts the Screw Attack as on only in the running state with the item: in a somersault, or in the plain jump animation while Samus still rises. The Samus-enemy crash code at LF282 calls it. metroid-samus/screw-attack
About: Metroid Screw Attack
Codemetroid-disassembly: Metroid software disassembly ·Source_Files/Bank07.asm#L2362-L2379IsScrewAttackActive:
LCD9C: SEC ;Assume screw attack is not active.
LCD9D: LDY ObjAction ;
LCDA0: DEY ;Is Samus running?
LCDA1: BNE ++ ;If not, branch to exit.
LCDA3: LDA SamusGear ;
LCDA6: AND #gr_SCREWATTACK ;Does Samus have screw attack?
LCDA8: BEQ ++ ;If not, branch to exit.
LCDAA: LDA AnimResetIndex ;
LCDAD: CMP #an_SamusSalto ;Is Samus summersaulting?
LCDAF: BEQ + ;If so, branch to clear carry(screw attack active).
LCDB1: CMP #an_SamusJump ;
…metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L7935-L7937LF282: bcs Exit17
jsr LF2E8
jsr IsScrewAttackActive ;($CD9C)Check if screw attack active.metroid-disassembly: Metroid software disassembly · Source_Files/Metroid_Defines.asm#L865-L866.alias sa_Run 1 ;Also run and jump.
.alias sa_Jump 2Checked by claude, 2026-10-21 — read IsScrewAttackActive; a running jump keeps ObjAction at sa_Run (1), and a standing jump uses sa_Jump (2)What LF282 does with the result is uncommented in the disassembly, and the atlas hasn't traced it.
Cited by: Screw Attack
Samus's gear byte, SamusGear, has one bit each for the bombs, High Jump, Long Beam, Screw Attack, Maru Mari, Varia, Wave Beam, and Ice Beam: all eight bits, none for a Space Jump. metroid-samus/gear-bits
About: Metroid Space Jump
Codemetroid-disassembly: Metroid software disassembly ·Source_Files/Metroid_Defines.asm#L853-L861;Bitmask defs used for SamusGear.
.alias gr_BOMBS $01
.alias gr_HIGHJUMP $02
.alias gr_LONGBEAM $04
.alias gr_SCREWATTACK $08
.alias gr_MARUMARI $10
.alias gr_VARIA $20
.alias gr_WAVEBEAM $40
.alias gr_ICEBEAM $80metroid-disassembly: Metroid software disassembly · Source_Files/Metroid_Defines.asm#L584.alias SamusGear $6878 ;Stores power-up items Samus has.Checked by claude, 2026-10-21 — read the gear bitmask definitions; missiles and energy tanks are counts kept elsewhere (MaxMissiles, TankCount)Cited by: Space Jump
Each frame, the handler for Samus's state (standing, running, jumping) reads the pad first and then calls SetSamusData, which reaches LavaAndMoveCheck. That routine accelerates and moves her vertically (VertAccelerate, then one pixel at a time), and then horizontally (HorzAccelerate, then one pixel at a time). metroid-samus/frame-order
Source_Files/Bank07.asm#L2142-L2151GoSamusHandler:
LCC1A: LDA ObjAction ;
LCC1D: BMI SamusStand ;Branch if Samus is standing.
LCC1F: JSR ChooseRoutine ;($C27C)Goto proper Samus handler routine.
…
LCC22: .word SamusStand ;($CC36)Standing.
LCC24: .word SamusRun ;($CCC2)Running.
LCC26: .word SamusJump ;($D002)Jumping.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2333-L2343SetSamusData:
LCD6D: JSR UpdateObjAnim ;($DC8F)Update animation if needed.
…
LCD81: JSR LavaAndMoveCheck ;($E269)Check if Samus is in lava or moving.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5452-L5456LE2A6:* iny ;Set Samus lava status.
LE2A7: sty SamusInLava ;
SamusMoveVertically:
LE2A9: jsr VertAccelerate ;($E37A)Calculate vertical acceleration.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5471-L5472LE2C2:* sta ObjectCounter ;Store number of pixels to move Samus this frame.
LE2C4:* jsr MoveSamusUp ;($E457)Attempt to move Samus up 1 pixel.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5518-L5521LE318: bne ---- ;Branch if Samus needs to be moved another pixel.
SamusMoveHorizontally:
LE31A: jsr HorzAccelerate ;($E3E5)Horizontally accelerate Samus.Checked by claude, 2026-10-25 — read GoSamusHandler, the ends of SamusStand, SamusRun, and SamusJump (each jumps to SetSamusData), SetSamusData, and LavaAndMoveCheck, which falls through into SamusMoveVertically and then SamusMoveHorizontallyCited by: Metroid
Samus's speeds are 8.8 fixed point: a whole byte, ObjVertSpeed or ObjHorzSpeed, and a fraction, VertCntrLinear or HorzCntrLinear. Her position's fraction is one more byte per axis, VertCntrNonLinr or HorzCntrNonLinr. Each frame the speed's fraction is added to it, and she moves the speed's whole part plus the carry, in pixels. The comments call that byte a counter that grows exponentially; in the code it is the fraction of her position. metroid-samus/subpixels
Value: 256 steps per px
Codemetroid-disassembly: Metroid software disassembly ·Source_Files/Bank07.asm#L5637-L5643LE3D3:* lda VertCntrNonLinr ;
LE3D6: clc ;This function adds itself plus the linear verticle counter
LE3D7: adc VertCntrLinear ;onto itself every frame. This causes the non-linear
LE3DA: sta VertCntrNonLinr ;counter to increase exponentially. This function will
LE3DD: lda #$00 ;cause Samus to reach maximum speed first in most
LE3DF: adc ObjVertSpeed ;situations before the linear counter.
LE3E2: sta $00 ;$00 stores temp copy of current verticle speed.The atlas reads this differently: The byte is the fraction of Samus's vertical position: each frame the speed's fraction is added to it, and the carry adds a pixel to the move. Nothing here grows exponentially.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5691-L5697* lda HorzCntrNonLinr
clc
adc HorzCntrLinear
sta HorzCntrNonLinr
lda #$00
adc ObjHorzSpeed
sta $00 ;$00 stores temp copy of current horizontal speed.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5471-L5472LE2C2:* sta ObjectCounter ;Store number of pixels to move Samus this frame.
LE2C4:* jsr MoveSamusUp ;($E457)Attempt to move Samus up 1 pixel.Checked by claude, 2026-10-25 — read the ends of VertAccelerate and HorzAccelerate and the move loops, which take $00 as the pixel count (its two's complement when negative); VertCntrNonLinr and HorzCntrNonLinr are written nowhere else in the disassemblyCited by: Metroid
From a standstill, the first frame with Left or Right held doesn't move Samus. SamusStand picks the running state, calls SetSamusData, which moves her at her speed of 0, and only then calls SetSamusRun, which sets her acceleration. Her speed rises from the next frame. metroid-samus/run-start
Source_Files/Bank07.asm#L2162-L2199SamusStand:
LCC36: LDA Joy1Status ;Status of joypad 1.
LCC38: AND #$CF ;Remove SELECT & START status bits.
LCC3A: BEQ + ;Branch if no buttons pressed.
LCC3C: JSR ClearHrztAnimData ;($CF5D)Set no horiontal movement and single frame animation.
…
LCC4B:* JSR BitScan ;($E1E1)Find which directional button is pressed.
…
LCC52: STA SamusDir ;1=left, 0=right.
LCC54:* TAX ;
LCC55: LDA ActionTable,x ;Load proper Samus status from table below.
LCC58: STA ObjAction ;Save Samus status.
…metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2236-L2238ldx SamusDir
LCCB7: lda RunAccelerationTbl,x
sta SamusHorzAccelChecked by claude, 2026-10-25 — read SamusStand and SetSamusRun; ClearHrztAnimData, run for any button held, zeroes the speed and the acceleration firstCited by: Metroid
Running on the ground, Left or Right against Samus's facing turns her and sets the acceleration toward the new side on that frame, through SetSamusRun, but leaves her speed as it is. She slows by 0.1875 px/frame each frame, passes 0, and speeds up the other way: from 1.5 px/frame, 16 frames to 1.5 px/frame the other way (computed). metroid-samus/turn
Value: 16 frames
Codemetroid-disassembly: Metroid software disassembly ·Source_Files/Bank07.asm#L2316-L2326* lda Joy1Status
and #$03
bne +
jsr LCF55
jmp LCD6B
* jsr BitScan ;($E1E1)
cmp SamusDir
beq LCD6B
sta SamusDir
jsr LCC98metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2236-L2247ldx SamusDir
LCCB7: lda RunAccelerationTbl,x
sta SamusHorzAccel
…
LCCC0: .byte $30 ;Accelerate right.
.byte $D0 ;Accelerate left.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5658-L5661lda HorzCntrLinear
clc
adc SamusHorzAccel
sta HorzCntrLinearChecked by claude, 2026-10-25 — read SamusRun's grounded branch and SetSamusRun (LCC98), which writes the acceleration and the animation but not ObjHorzSpeed or HorzCntrLinear; HorzAccelerate adds the signed acceleration to the speedCited by: Metroid
On the ground, the running handler checks Samus's animation before it reads A or the d-pad (LCDBF). Unless it is the running animation (or, with Up held, running and aiming up), it sets that animation and skips the rest of the handler for the frame. A jump press or a released d-pad on that frame is ignored, and she accelerates toward her facing. That happens on her first frame on the ground after a running jump or a fall, and on the frame after a turn: SetSamusRun gives Samus the running animation only if her animation was standing (an_SamusStand), and otherwise the running-and-aiming-up animation (an_SamusRunPntUp). metroid-samus/run-pose
Source_Files/Bank07.asm#L2295-L2298* lda SamusOnElevator
bne +
jsr LCCB7
* jsr LCDBFmetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2383-L2395LCDBF: lda Joy1Status
and #$08
lsr
lsr
lsr
tax
lda LCCBE,x
cmp AnimResetIndex
beq -
jsr SetSamusAnim
pla
pla
…metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2225-L2243ldx #$00
lda AnimResetIndex
cmp #an_SamusStand
beq +
inx
cmp #$27
beq +
lda #$04
jsr SetSamusNextAnim
* lda RunAnimationTbl,x
sta AnimResetIndex
…
…Checked by claude, 2026-10-25 — read SamusRun's grounded branch, LCDBF (its `beq -` reaches the `rts` that ends IsScrewAttackActive, and its two `pla` drop the return to SamusRun), and SetSamusRun, where only an_SamusStand keeps X at 0 (an_SamusRun); $27, an_SamusPntUp in Metroid_Defines.asm, branches after the `inx`, so it gets X = 1 (an_SamusRunPntUp) like every other animation. The jump press, the fire press, and the d-pad are read after the callThe conformance probe (probes/lab/conformance.mjs) plays turns and landings in the ROM and matches the model, which skips those frames' reads.
Cited by: Metroid
A jump from a standstill starts after the press frame's move: SamusStand calls SetSamusData, which moves her, and then SetSamusJump, so she first rises on the next frame. A running jump's handler calls SetSamusJump (LCD40) before SetSamusData, so she rises on the press frame, and there it sets her top speed for the jump, SamusHorzSpdMax, to $12. metroid-samus/jump-start
Source_Files/Bank07.asm#L2185-L2200LCC65:* BIT Joy1Change ;Check if jump was just pressed.
LCC67: BPL + ;Branch if JUMP not pressed.
LCC69: LDA #sa_Jump ;
LCC6B: STA ObjAction ;Set Samus status as jumping.
LCC6E:* LDA #$04 ;Prepare to set animation delay to 4 frames.
LCC70: JSR SetSamusData ;($CD6D)Set Samus control data and animation.
…
LCC7A: JSR ChooseRoutine ;Select routine below.
…
LCC81: .word SetSamusJump ;($CFC3)Samus is jumping.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2304-L2310* jsr LCF2E
lda Joy1Change
bpl + ; branch if JUMP not pressed
LCD40: jsr LCFC3
lda #$12
sta SamusHorzSpdMax
jmp LCD6BChecked by claude, 2026-10-25 — read SamusStand and SamusRun's grounded branch; LCD6B loads the animation delay and falls into SetSamusDataCited by: Metroid
In a running jump, the air branch of the running handler poses Samus by SamusJmpDsplcmnt, which a jump sets to 0 and each pixel up adds 1 to and each pixel down takes 1 from. Rising at 24 or more (cpy #$18), she somersaults (an_SamusSalto) unless she is firing; falling at less than 24, she takes the plain jump pose (an_SamusJump), as she does from the running pose. The pose decides whether letting go of the d-pad stops her (metroid-samus/air-run). metroid-samus/somersault
About: Metroid Air control
Value: 24 px
Codemetroid-disassembly: Metroid software disassembly ·Source_Files/Bank07.asm#L2253-L2270LCCC2: ldx SamusDir
lda SamusGravity
beq +++++++
ldy SamusJmpDsplcmnt
bit ObjVertSpeed
bmi +
cpy #$18
bcs ++++
lda #an_SamusJump
sta AnimResetIndex
bcc ++++ ; branch always
* cpy #$18
…metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2281-L2285* lda #an_SamusRun
cmp AnimResetIndex
bne +
lda #an_SamusJump
sta AnimResetIndexmetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5744-L5745* dec ObjectY
inc SamusJmpDsplcmntmetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5783-L5784* inc ObjectY
dec SamusJmpDsplcmntChecked by claude, 2026-10-25 — read SamusRun's airborne branch with its anonymous labels counted, SetSamusJump (which stores 0 in SamusJmpDsplcmnt), and the ends of MoveSamusUp and MoveSamusDown; the compares are unsignedCited by: Metroid
Moving down, and not in the ball, Samus stops when MoveSamusDown finds a solid tile under her: StopVertMovement clears her vertical speed, SamusGravity is set to 0, and the frame goes on to her horizontal move. A jump from a standstill ends on the next frame: SamusJump finds SamusGravity at 0 and calls StopHorzMovement, which stops her and makes her stand, so she doesn't move that frame, and a jump press then does nothing, since the jumping handler starts a jump only in lava. In the ball (sa_Roll), a landing halves her downward speed and turns it upward, a bounce, until the halved whole speed is 0 and she stops. metroid-samus/landing
Source_Files/Bank07.asm#L5491-L5493LE2E1:* sta ObjectCounter ;Store number of pixels to move Samus this frame.
LE2E3:* jsr MoveSamusDown ;($E4A3)Attempt to move Samus 1 pixel down.
LE2E6: bcs +++ ;Branch if Samus successfully moved down 1 pixel.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5511-L5515;Samus has hit the ground after moving downwards.
LE30B:* jsr SFXSamusWalk ;($CB96)Play walk SFX.
LE30E:* jsr StopVertMovement ;($D147)Clear vertical movement data.
LE311: sty SamusGravity ;Clear Samus gravity value.
LE314: beq SamusMoveHorizontally ;($E31A)Attempt to move Samus left/right.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5496-L5500LE2E8: lda ObjAction ;
LE2EB: cmp #sa_Roll ;Is Samus rolled into a ball?
LE2ED: bne + ;If not, branch.
LE2EF: lsr ObjVertSpeed ;Divide verticle speed by 2.
LE2F2: beq ++ ;Speed not fast enough to bounce. branch to skip.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2719-L2736LDA SamusInLava
BEQ +
LDA Joy1Change
BPL + ; branch if JUMP not pressed
JSR LCFC3
JMP LCD6B
* LDA SamusGravity
BNE ++
…
* JSR LCF55
* LDA #$03
JMP SetSamusData ;($CD6D)Set Samus control data and animation.Checked by claude, 2026-10-25 — read the downward move loop, its ball branch (L5496-L5509: a halved speed of 0 skips to StopVertMovement, any other is negated, a bounce), and SamusJump's end; LCF55 falls into ClearHrztAnimData, which zeroes the horizontal speed and acceleration and stores 0 (sa_Stand) in ObjAction before SetSamusData moves herIn Brinstar's bank, the frames of Samus's running, standing, jumping, and somersault animations all give the radii $0F and $04: 15 px from her center to her top or bottom and 4 px to either side, so her box is 30 px tall and 8 wide (computed). metroid-samus/terrain-box
Value
{
"x": 4,
"y": 15
}Source_Files/Bank01.asm#L679-L701;Samus run animation.
L8572: .byte $03, $04, $05, $FF
…
;Samus Stand animation.
L8579: .byte $08, $FF
…
;Samus Jump animation.
L857E: .byte $10, $FF
…
;Samus summersault animation.
L8580: .byte $17, $18, $19, $1A, $FFmetroid-disassembly: Metroid software disassembly · Source_Files/Bank01.asm#L789-L793FramePtrTable:
L860B: .word $87CB, $87CB, $87CB, $87CB, $87DD, $87F0, $8802, $8802
L861B: .word $8818, $882C, $882C, $882C, $882C, $883E, $8851, $8863
L862B: .word $8863, $8874, $8874, $8885, $8885, $8885, $8885, $8885
L863B: .word $888F, $8899, $88A3, $88AD, $88B8, $88C3, $88CE, $88D9metroid-disassembly: Metroid software disassembly · Source_Files/Bank01.asm#L1157-L1161;current frame. The second byte is saved as the object's y radius and the third byte is saved
;as the object's x radius.
…
;Samus run.
L87CB: .byte $40, $0F, $04, $00, $01, $FD, $20, $FE, $41, $40, $FD, $60, $20, $21, $FE, $FEmetroid-disassembly: Metroid software disassembly · Source_Files/Bank01.asm#L1176-L1177;Samus stand.
L8818: .byte $40, $0F, $04, $FD, $20, $0E, $0D, $FE, $1E, $1D, $2E, $2D, $FE, $FD, $60, $3Bmetroid-disassembly: Metroid software disassembly · Source_Files/Bank01.asm#L1192-L1210;Samus stand and jump.
L8863: .byte $40, $0F, $04, $00, $01, $FD, $20, $FE, $41, $40, $FD, $60, $22, $07, $08, $32
…
;Samus summersault.
L8885: .byte $41, $0F, $04, $52, $53, $62, $63, $72, $73, $FF
…
;Samus summersault.
L888F: .byte $42, $0F, $04, $54, $55, $56, $64, $65, $66, $FF
…
;Samus summersault.
L8899: .byte $81, $0F, $04, $52, $53, $62, $63, $72, $73, $FF
…
…Checked by claude, 2026-10-25 — followed an_SamusRun ($00), an_SamusStand ($07), an_SamusJump ($0C), and an_SamusSalto ($0E) into ObjectAnimIdxTbl, their frames ($03-$05, $08, $10, $17-$1A) into FramePtrTable, and read each frame's second and third bytes; the frame reader at LDEBC stores them in ObjRadY and ObjRadX (metroid-samus/hitbox)The other area banks carry their own copies of these tables; the atlas has read only Brinstar's.
Cited by: Metroid
Samus moves one pixel at a time, and a step tests the room's 8 px tiles only when the edge she moves toward lies on a tile boundary: MoveSamusUp, MoveSamusDown, MoveSamusLeft, and MoveSamusRight call the crash check only when her center less or plus her radius is a multiple of 8. The check tests the row of tiles above her top or below her bottom, or the column beside her side, across every tile her box spans. On the ground, VertAccelerate makes the same test under her each frame. metroid-samus/tile-checks
Value: 8 px per tile
Codemetroid-disassembly: Metroid software disassembly ·Source_Files/Bank07.asm#L5713-L5720MoveSamusUp:
LE457: lda ObjectY ;Get Samus' y position in room.
sec ;
sbc ObjRadY ;Subtract Samus' vertical radius.
LE45E: and #$07 ;Check if result is a multiple of 8. If so, branch to
LE460: bne + ;Only call crash detection every 8th pixel.
LE462: jsr CheckMoveUp ;($E7A2)Check if Samus obstructed UPWARDS.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5751-L5757MoveSamusDown:
lda ObjectY
clc
adc ObjRadY
and #$07
bne + ; only call crash detection every 8th pixel
jsr CheckMoveDown ; check if Samus obstructed DOWNWARDSmetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L6215-L6229CheckMoveUp:
LE7A2: ldx PageIndex
lda ObjRadY,x
clc
adc #$08
jmp +
CheckMoveDown:
ldx PageIndex
lda #$00
sec
sbc ObjRadY,x
* sta $02
…metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L6234-L6245* sta $03
tay
ldx #$00
lda $09
sec
sbc $03
and #$07
beq +
inx
* jsr LE8CE
sta $04
jsr LE90Fmetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L6280-L6283* dex
beq +
jsr LE98E
jmp LE7E6metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L6341-L6352CheckMoveLeft:
ldx PageIndex
lda ObjRadX,x
clc
adc #$08
jmp +
CheckMoveRight:
ldx PageIndex
lda #$00
sec
sbc ObjRadX,xmetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5594-L5600LE384: lda ObjectY ;
LE387: clc ;
LE388: adc ObjRadY ;Check is Samus is obstructed downwards on y room
LE38B: and #$07 ;positions divisible by 8(every 8th pixel).
LE38D: bne + ;
LE38F: jsr CheckMoveDown ;($E7AD)Is Samus obstructed downwards?
LE392: bcc ++ ;Branch if yes.Checked by claude, 2026-10-25 — read the four one-pixel moves, the four checks, LE8CE (the count of 8 px tiles the box spans from its first edge, one more when that edge is mid-tile), LE90F (the row or column: center less the offset), and the loop at LE7E6, which steps 8 px along the row or column per tile; MakeCartRAMPtr drops each coordinate's low 3 bitsUp tests the row 8 px above her top edge (the offset ObjRadY+8), down the row at her center plus ObjRadY, left the column 8 px past ObjRadX, right the column at her center plus ObjRadX; with an 8 px tile, each is the tile next to her edge when that edge is on a boundary.
Cited by: Metroid
On the ground, VertAccelerate sets SamusHorzSpdMax to $18 and tests the tiles under Samus before she moves. With none solid, and no elevator or frozen enemy under her, it sets the falling gravity, $1A, and adds it at once, so she is airborne from that frame. The running handler's air branch then turns her running pose into the plain jump pose, so letting go of the d-pad stops her (metroid-samus/air-run), and her top speed stays 1.5 px/frame. metroid-samus/walk-off
About: Metroid Coyote time
Codemetroid-disassembly: Metroid software disassembly ·Source_Files/Bank07.asm#L5589-L5609VertAccelerate:
LE37A: lda SamusGravity ;Is Samus rising or falling?
LE37D: bne ++ ;Branch if yes.
LE37F: lda #$18 ;
LE381: sta SamusHorzSpdMax ;Set Samus maximum running speed.
…
LE38F: jsr CheckMoveDown ;($E7AD)Is Samus obstructed downwards?
LE392: bcc ++ ;Branch if yes.
LE394:* jsr SamusOnElevatorOrEnemy ;($D976)Calculate if Samus standing on elevator or enemy.
…
LE3A0: lda #$1A ;Samus is falling. Store falling gravity value.
LE3A2: sta SamusGravity ;
…metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L2281-L2285* lda #an_SamusRun
cmp AnimResetIndex
bne +
lda #an_SamusJump
sta AnimResetIndexChecked by claude, 2026-10-25 — read VertAccelerate's grounded path into the gravity add at LE3A5, and SamusRun's airborne branch; SamusHorzSpdMax is written only here and at LCD40, so it stays $18 through a fallCited by: Metroid
A solid tile beside Samus stops her sideways move for the rest of the frame (CheckStopHorzMvmt). On the ground (SamusGravity 0), and not in the ball, it also calls StopHorzMovement, which clears her speed and acceleration and makes her stand. In the air her speed is kept. metroid-samus/wall-stop
Source_Files/Bank07.asm#L5536-L5540LE333:* sta ObjectCounter ;Store number of pixels to move Samus this frame.
LE335:* jsr MoveSamusLeft ;($E626)Attempt to move Samus 1 pixel to the left.
LE338: jsr CheckStopHorzMvmt ;($E365)Check if horizontal movement needs to be stopped.
LE33B: dec ObjectCounter ;1 pixel movement is complete.
LE33D: bne - ;Branch if Samus needs to be moved another pixel.metroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5568-L5577CheckStopHorzMvmt:
LE365: bcs Exit10 ;Samus moved successfully. Branch to exit.
LE367: lda #$01 ;Load counter with #$01 so this function will not be
LE369: sta ObjectCounter ;called again.
LE36C: lda SamusGravity ;Is Samus on the ground?
LE36E: bne Exit10 ;If not, branch to exit.
LE370: lda ObjAction ;
LE373: cmp #sa_Roll ;Is Samus rolled into a ball?
LE375: beq Exit10 ;If so, branch to exit.Checked by claude, 2026-10-25 — read the left and right move loops and CheckStopHorzMvmt, which jumps to StopHorzMovement (LCF55, into ClearHrztAnimData)Cited by: Metroid
Blocked moving up, Samus's vertical speed, whole byte and fraction, is shifted right one bit with the sign bit set (sec, ror ObjVertSpeed, ror VertCntrLinear). An upward speed is halved and stays upward, though the comment says the shift reverses her direction. The rest of that frame's rise is dropped. Each later frame that would still move her up is blocked and halved again, until gravity turns her speed downward. metroid-samus/ceiling-hit
Source_Files/Bank07.asm#L5471-L5478LE2C2:* sta ObjectCounter ;Store number of pixels to move Samus this frame.
LE2C4:* jsr MoveSamusUp ;($E457)Attempt to move Samus up 1 pixel.
LE2C7: bcs + ;Branch if Samus successfully moved up 1 pixel.
LE2C9: sec ;Samus blocked upwards. Divide her speed by 2 and set the
LE2CA: ror ObjVertSpeed ;MSB to reverse her direction of travel.
LE2CD: ror VertCntrLinear ;
LE2D0: jmp SamusMoveHorizontally ;($E31A)Attempt to move Samus left/right.The atlas reads this differently: An upward speed already has its sign bit set, so sec and the two rors are a signed shift: the speed halves and stays upward. Gravity, not this shift, turns her around.Checked by claude, 2026-10-25 — read the upward move loop; an upward speed already has bit 7 of ObjVertSpeed set, so the sec/ror pair is an arithmetic shift right of the 16-bit speed. A blocked Samus stays with her top on the tile boundary, where MoveSamusUp tests againNot measured in the ROM; the conformance probe's start area has no ceiling within a jump.
Cited by: Metroid
Measured in the game, Samus's jump from a standstill peaks at 32 px on frame 10 when A is tapped or held up to 10 frames, and at 84 px on frame 41 when A is held to the top, landing on frames 36 and 86. A running jump, which rises on its press frame, peaks and lands a frame sooner at the same heights. metroid-samus/apex-by-hold
About: Metroid Variable jump height
Value
{
"tap": {
"apex": 32,
"apexAt": 10,
"airtime": 36
},
"full": {
"apex": 84,
"apexAt": 41,
"airtime": 86
},
"byHold": {
"1": {
"apex": 32,
"apexAt": 10,
"airtime": 36
},
"2": {
"apex": 32,
"apexAt": 10,
"airtime": 36
},
"4": {
"apex": 32,
"apexAt": 10,
"airtime": 36
},
"6": {
"apex": 32,
"apexAt": 10,
"airtime": 36
},
"8": {
"apex": 32,
"apexAt": 10,
"airtime": 36
},
"10": {
"apex": 32,
"apexAt": 10,
"airtime": 36
},
"12": {
"apex": 38,
"apexAt": 12,
"airtime": 41
},
"16": {
"apex": 49,
"apexAt": 16,
"airtime": 49
},
"20": {
"apex": 59,
"apexAt": 20,
"airtime": 56
},
"30": {
"apex": 76,
"apexAt": 30,
"airtime": 70
},
"50": {
"apex": 84,
"apexAt": 41,
"airtime": 86
}
},
"running": {
"tap": {
"apex": 32,
"apexAt": 9,
"airtime": 35
},
"full": {
"apex": 84,
"apexAt": 40,
"airtime": 85
}
}
}Source_Files/Bank07.asm#L2701-L2709 — the release test in SamusJumpLDA SamusJmpDsplcmnt
BIT ObjVertSpeed
BPL +
CMP #$20
BCC +
BIT Joy1Status
BMI +
JSR StopVertMovementmetroid-disassembly: Metroid software disassembly · Source_Files/Bank07.asm#L5589-L5643 — VertAccelerate, gravity and the move's pixel countlda VertCntrLinear
clc
adc SamusGravity
sta VertCntrLinear
lda ObjVertSpeed
adc #$00
sta ObjVertSpeedThe atlas reads this differently: The byte is the fraction of Samus's vertical position: each frame the speed's fraction is added to it, and the carry adds a pixel to the move. Nothing here grows exponentially. (metroid-samus/subpixels)Checked by claude-agent, 2026-10-25 — Read SamusJump's release test (Bank07.asm L2701-2709: letting go stops the rise only once she has risen $20 px) and VertAccelerate (L5589-5643: SamusGravity added to a counter whose carry steps ObjVertSpeed) at the pinned commit; the heights and frames are the probe's run in the US ROM.Probe: probes/lab/metroid-jumps.mjsThe start state is set by RAM writes in probes/lab/metroid-movement.lua, so the enemies never touch her and her position fractions start at 0; neither changes the jump's arc. A tap and a hold of up to 10 frames give the same jump, because the release counts only once she has risen 32 px (metroid-samus/release-cap), which takes 10 frames.
Cited by: Metroid