Metroid
Nintendo R&D1's 1986 side-view action-adventure, read through nmikstas's disassembly and measured in the US ROM.
On this page
Metroid is Nintendo Research & Development 1’s action-adventure, released for the Famicom Disk System in Japan in August 19861. nmikstas’s disassembly reassembles to the US ROM byte for byte2. It began from Kent Hansen’s disassembly of the game engine, and much of the collision code still carries his raw labels2.
#How Samus moves
Samus’s speeds and positions carry a fraction byte, so they move in 1/256 px steps3. Each frame the game reads the pad first, then moves her up or down, then sideways4.
On the ground she speeds up at 0.1875 px/frame² to 1.5 px/frame5,5. The first frame with a direction held doesn’t move her6, and letting go of the d-pad stops her at once5. Pressing the other way turns her on that frame but keeps her speed, so she slides on and takes 16 frames to run the other way at full speed7. On the frame after a turn, or after landing from a running jump, the game sets her running pose and ignores the pad8.
Every jump leaves the ground at 4 px/frame9 and slows under 0.09375 px/frame² in a jump10,10. Letting go of A stops the rise only once she has climbed 32 px11, so the shortest jump is a tall one. Measured in the game, a standing jump peaks at 84 px held, 32 px tapped12,12. A running jump takes off on the press frame, a standing jump on the frame after13.
A running jump caps her speed at 1.125 px/frame, and the d-pad steers it by acceleration14. At 24 px of rise she somersaults15. With neither direction held, a jump in the plain jump pose stops dead sideways, while a somersault carries on14. A standing jump moves 1 px/frame while a direction is held16.
Walking off a ledge makes her airborne at once17. Because the pad is read before the floor test, a press on that frame still jumps4,17; a frame later it doesn’t18.
Against the level she is an 8 × 30 px19 box19, tested against every 8 px tile it spans20. A wall stops her move; on the ground she also stops and stands, while in the air her speed is kept21. The code’s comment says a ceiling divides her speed by 2 and reverses it. The shift keeps the sign, though, so her upward speed is only halved, and she presses against the ceiling until gravity turns her22.
#Energy and damage
Samus’s health is binary-coded decimal in tenths of a unit. The full energy tanks sit in the high nibble of HealthHi, above the tens, ones, and tenths digits23. She starts with 30 units24 and no tanks24.
An enemy’s contact damage doesn’t depend on the enemy. The game charges the base damage of the current area, two bytes in that area’s bank: 8 units in Brinstar25, 10 in Norfair, 20 in Kraid’s hideout, 24 in Ridley’s hideout, and 30 in Tourian25. The Varia suit halves every amount taken26. In the probe, a crawler in Brinstar’s first room takes 8 units with each hit27.
#The blink
A hit sets the blink counter, SamusBlink, to $32, which is 5028. The comment beside it says “Set blink for 32 frames”, reading the hex as decimal28. The counter drops by 1 on every frame of game logic29. While it is above 0, the collision code skips the check between Samus and the enemies30.
In the probe, a crawler held against Samus hurts her every 50 frames27 of game logic27. One of the two gaps also held 2 lag frames, so it took 52 frames of video27.
Lava sets the counter to 50 on every frame. Enemies can’t touch Samus there, but the lava takes 0.7 units twice every 8 frames, or 0.3 units once with the Varia suit31.
#The push
A hit gives Samus an upward speed and the hit gravity, and takes a push direction away from the enemy28. On each frame of the blink, the game sets her horizontal speed to the counter divided by 16, so the push fades as the blink runs down. It ends when the counter falls below 1632. Once the counter is below 48, the push yields to the player: any frame with horizontal acceleration skips it32.
on a hit: blink = 50; vertical speed = up; gravity = hit gravity; push = away from the enemy
every frame: if blink > 0:
d = blink / 16 (rounded down); blink -= 1
if d < 3 and Samus is accelerating sideways: skip
horizontal speed = d toward push # 0 once blink < 16
In the probe, with no input, three pushes carried Samus 37–45 px33 from the enemy33.
#Item drops
RandomNumber1 gains 25 once per frame, so it steps through all 256 values every 256 frames, and a drop depends on the frame the enemy dies34. A value below 16 drops nothing. Otherwise its low 3 bits pick from an 8-entry table: a missile twice, an energy ball three times, and item $89 three times35.
The table’s comment calls $89 “No item”, but the code drops it for any enemy whose byte in the area’s $968B table has bit 0 set. Samus collects it as 20 units36. A small energy ball gives 5 units and a missile 2 missiles37. A missile drops only after Samus has some missile capacity38.
Each door re-rolls two caps for the next room, 0 to 15 missile drops and 64 to 94 energy drops, and each drop that passes its cap adds to its count, even a $89 the enemy then can’t drop38. The comments give “16 max” and “128 max”, and they call the counts power-ups “collected by Samus”38. Before the first door, both caps are 39, because the start routine stores a mirroring value into them39.
Tourian handles drops its own way. Two of its enemy data indexes drop nothing40. For any other kill there, a roll that would drop nothing resets both caps to 1 and rolls again40. Tourian’s data indexes 0 and 1 carry a flag that makes their drops worth 30 units or 30 missiles37.
The probe calls the drop code for all 256 values of RandomNumber1 with Brinstar’s bank mapped41. For an enemy of data index 0, the 256 values give 60 missiles, 90 energy balls, 90 $89 balls, and 16 nothing41,41. For index 3, whose bit is clear, the 90 $89 rolls drop nothing41.
#Health growth
An energy tank adds a tank, up to 6, and fills Samus’s health42. Health can’t exceed 43 units for tanks43. A missile tank adds 5 to her missile capacity44.
Cartridge
Mechanics
| Mechanic | Kind | How |
|---|---|---|
| Invincibility frames | Frame timer | a 50-frame blink |
| Knockback | Fading push | a push that fades with the blink |
| Item drop table | One roll | an 8-entry table read by a frame-stepped byte, capped per room |
| Damage formula | Fixed table | one contact damage per area |
| Random number generator | Stepped counter | a byte that steps by 25 every frame, through all 256 values |
| Health upgrade | Whole containers | energy tanks, each filling Samus's health to the new maximum |
| Jump | One speed | 4 px/frame up for every jump; the High Jump Boots lower the gravity instead |
| Gravity | One gravity | 0.09375 px/frame² in a jump, 0.0703 with the High Jump Boots, held or not |
| Variable jump height | Release cap | past 32 px of rise, letting go sets the vertical speed to 0, so a tap still rises 32 px |
| Running | Ramp up | 0.1875 px/frame² to 1.5 px/frame after a still first frame, a stop the moment the d-pad is released, and 16 frames to turn at full speed |
| Air control | Momentum | a running jump steers by acceleration and a somersault keeps its speed; a standing jump moves 1 px/frame only while held |
| Hitbox | Box | radii from each animation frame, 8 × 30 px against the level, tested at every 8 px tile the box spans |
| Ammunition | One pool | missiles only, 5 more capacity per missile tank |
| Morph ball | No jump | the Maru Mari, from the ground only; a jump press unrolls it |
| Screw Attack | Running jump | a running jump with the item, while it somersaults or still rises |
| Password and battery save | Password with a check value | 24 characters of 6 bits holding 18 bytes, with an 8-bit sum and a random rotation |
| Coyote time | None | a press on the frame Samus walks off still jumps; after that, a jump press works only in lava |
| Space Jump | None | the gear byte has no Space Jump bit |
| Wall jump | None | in the air a jump press works only in lava |
Every claim about this game, by concept (57)
References
- ^Wikidata lists Nintendo Research & Development 1 as the developer of Metroid and Nintendo as its publisher, and dates its release on the Famicom Disk System in Japan 6 August 1986.WikidataWikidata
How we know
+1986-08-06T00:00:00Z … +1987-08-15T00:00:00Z … +1987-08-00T00:00:00Z
Q2530723, P178 developer, P123 publisher, and P577 publication date qualified by P291 place and P400 platform · archived copy ↗ The quote matches our archived copy.
Checked by a second AI model, 2026-10-21.
- ^ abnmikstas's disassembly of Metroid, assembled with Ophis, equals the PRG ROM of Metroid (USA) byte for byte.Code“Metroid (USA)” · nmikstas/metroid-disassembly
How we know
In the cartridge (“Metroid (USA)”): bank 7 $C000, RandomNumbers, the first routine of the engine bank. We don't reproduce the bytes.
32out00=($(md5sum Output_Files/Bank00.bin)) 33org00="8890cbea191c4218a754aa926ca61c9a"
build_script lines 32–33 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^Samus's speeds are 8.8 fixed point: a whole byte,
ObjVertSpeedorObjHorzSpeed, and a fraction,VertCntrLinearorHorzCntrLinear. Her position's fraction is one more byte per axis,VertCntrNonLinrorHorzCntrNonLinr. 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.Codenmikstas/metroid-disassemblyHow we know
5637LE3D3:* lda VertCntrNonLinr ; 5638LE3D6: clc ;This function adds itself plus the linear verticle counter 5639LE3D7: adc VertCntrLinear ;onto itself every frame. This causes the non-linear 5640LE3DA: sta VertCntrNonLinr ;counter to increase exponentially. This function will 5641LE3DD: lda #$00 ;cause Samus to reach maximum speed first in most 5642LE3DF: adc ObjVertSpeed ;situations before the linear counter. 5643LE3E2: sta $00 ;$00 stores temp copy of current verticle speed.
Source_Files/Bank07.asm lines 5637–5643 at 4270d57 · upstream ↗ 5691* lda HorzCntrNonLinr 5692 clc 5693 adc HorzCntrLinear 5694 sta HorzCntrNonLinr 5695 lda #$00 5696 adc ObjHorzSpeed 5697 sta $00 ;$00 stores temp copy of current horizontal speed.
Source_Files/Bank07.asm lines 5691–5697 at 4270d57 · upstream ↗ 5471LE2C2:* sta ObjectCounter ;Store number of pixels to move Samus this frame. 5472LE2C4:* jsr MoveSamusUp ;($E457)Attempt to move Samus up 1 pixel.
Source_Files/Bank07.asm lines 5471–5472 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^ abEach frame, the handler for Samus's state (standing, running, jumping) reads the pad first and then calls
SetSamusData, which reachesLavaAndMoveCheck. That routine accelerates and moves her vertically (VertAccelerate, then one pixel at a time), and then horizontally (HorzAccelerate, then one pixel at a time).Codenmikstas/metroid-disassemblyHow we know
2142GoSamusHandler: 2143LCC1A: LDA ObjAction ; 2144LCC1D: BMI SamusStand ;Branch if Samus is standing. 2145LCC1F: JSR ChooseRoutine ;($C27C)Goto proper Samus handler routine. ⋮ 2149LCC22: .word SamusStand ;($CC36)Standing. 2150LCC24: .word SamusRun ;($CCC2)Running. 2151LCC26: .word SamusJump ;($D002)Jumping.
Source_Files/Bank07.asm lines 2142–2151 at 4270d57 · upstream ↗ 2333SetSamusData: 2334LCD6D: JSR UpdateObjAnim ;($DC8F)Update animation if needed. ⋮ 2343LCD81: JSR LavaAndMoveCheck ;($E269)Check if Samus is in lava or moving.
Source_Files/Bank07.asm lines 2333–2343 at 4270d57 · upstream ↗ 5452LE2A6:* iny ;Set Samus lava status. 5453LE2A7: sty SamusInLava ; ⋮ 5455SamusMoveVertically: 5456LE2A9: jsr VertAccelerate ;($E37A)Calculate vertical acceleration.
Source_Files/Bank07.asm lines 5452–5456 at 4270d57 · upstream ↗ 5471LE2C2:* sta ObjectCounter ;Store number of pixels to move Samus this frame. 5472LE2C4:* jsr MoveSamusUp ;($E457)Attempt to move Samus up 1 pixel.
Source_Files/Bank07.asm lines 5471–5472 at 4270d57 · upstream ↗ 5518LE318: bne ---- ;Branch if Samus needs to be moved another pixel. ⋮ 5520SamusMoveHorizontally: 5521LE31A: jsr HorzAccelerate ;($E3E5)Horizontally accelerate Samus.
Source_Files/Bank07.asm lines 5518–5521 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^ abcOn the ground Samus accelerates at
$30/256 (0.1875) px/frame² towardSamusHorzSpdMax/16 px/frame, whichVertAcceleratesets to$18(1.5 px/frame) while she is grounded. With no direction held,StopHorzMovementclears her horizontal speed at once.Codenmikstas/metroid-disassemblyHow we know
2245RunAccelerationTbl: 2246LCCC0: .byte $30 ;Accelerate right. 2247 .byte $D0 ;Accelerate left.
Source_Files/Bank07.asm lines 2245–2247 at 4270d57 · upstream ↗ 5590LE37A: lda SamusGravity ;Is Samus rising or falling? 5591LE37D: bne ++ ;Branch if yes. 5592LE37F: lda #$18 ; 5593LE381: sta SamusHorzSpdMax ;Set Samus maximum running speed.
Source_Files/Bank07.asm lines 5590–5593 at 4270d57 · upstream ↗ 5648HorzAccelerate: 5649LE3E5: lda SamusHorzSpdMax 5650 jsr Amul16 ; * 16 ⋮ 5653 lda SamusHorzSpdMax 5654 jsr Adiv16 ; / 16 ⋮ 5658 lda HorzCntrLinear 5659 clc 5660 adc SamusHorzAccel 5661 sta HorzCntrLinear ⋮ 5670 sta ObjHorzSpeed ⋮ 5687 lda $00 5688 sta HorzCntrLinear ⋮
Source_Files/Bank07.asm lines 5648–5690 at 4270d57 · upstream ↗ 2316* lda Joy1Status 2317 and #$03 2318 bne + 2319 jsr LCF55
Source_Files/Bank07.asm lines 2316–2319 at 4270d57 · upstream ↗ 2603ClearHorzMvmntData: 2604LCF4C: LDY #$00 ; ⋮ 2606 STY HorzCntrLinear ;linear counter to #$00. ⋮ 2609StopHorzMovement: ⋮ 2615LCF5D: JSR NoHorzMoveNoDelay ;($CF81)Clear horizontal movement and animation delay data. ⋮ 2638LCF81: JSR ClearHorzData ;($CFB7)Clear all horizontal movement data. ⋮ 2664ClearHorzData: 2665LCFB7: JSR ClearHorzMvmntData ;($CF4C)Clear horizontal speed and linear counter.
Source_Files/Bank07.asm lines 2600–2666 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^From a standstill, the first frame with Left or Right held doesn't move Samus.
SamusStandpicks the running state, callsSetSamusData, which moves her at her speed of 0, and only then callsSetSamusRun, which sets her acceleration. Her speed rises from the next frame.Codenmikstas/metroid-disassemblyHow we know
2162SamusStand: 2163LCC36: LDA Joy1Status ;Status of joypad 1. 2164LCC38: AND #$CF ;Remove SELECT & START status bits. 2165LCC3A: BEQ + ;Branch if no buttons pressed. 2166LCC3C: JSR ClearHrztAnimData ;($CF5D)Set no horiontal movement and single frame animation. ⋮ 2173LCC4B:* JSR BitScan ;($E1E1)Find which directional button is pressed. ⋮ 2176LCC52: STA SamusDir ;1=left, 0=right. 2177LCC54:* TAX ; 2178LCC55: LDA ActionTable,x ;Load proper Samus status from table below. 2179LCC58: STA ObjAction ;Save Samus status. ⋮ 2190LCC70: JSR SetSamusData ;($CD6D)Set Samus control data and animation. ⋮ 2194LCC7A: JSR ChooseRoutine ;Select routine below. ⋮
Source_Files/Bank07.asm lines 2162–2199 at 4270d57 · upstream ↗ 2236 ldx SamusDir 2237LCCB7: lda RunAccelerationTbl,x 2238 sta SamusHorzAccel
Source_Files/Bank07.asm lines 2236–2238 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^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).Codenmikstas/metroid-disassemblyHow we know
2316* lda Joy1Status 2317 and #$03 2318 bne + 2319 jsr LCF55 2320 jmp LCD6B ⋮ 2322* jsr BitScan ;($E1E1) 2323 cmp SamusDir 2324 beq LCD6B 2325 sta SamusDir 2326 jsr LCC98
Source_Files/Bank07.asm lines 2316–2326 at 4270d57 · upstream ↗ 2236 ldx SamusDir 2237LCCB7: lda RunAccelerationTbl,x 2238 sta SamusHorzAccel ⋮ 2246LCCC0: .byte $30 ;Accelerate right. 2247 .byte $D0 ;Accelerate left.
Source_Files/Bank07.asm lines 2236–2247 at 4270d57 · upstream ↗ 5658 lda HorzCntrLinear 5659 clc 5660 adc SamusHorzAccel 5661 sta HorzCntrLinear
Source_Files/Bank07.asm lines 5658–5661 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^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:SetSamusRungives Samus the running animation only if her animation was standing (an_SamusStand), and otherwise the running-and-aiming-up animation (an_SamusRunPntUp).Codenmikstas/metroid-disassemblyHow we know
2295* lda SamusOnElevator 2296 bne + 2297 jsr LCCB7 2298* jsr LCDBF
Source_Files/Bank07.asm lines 2295–2298 at 4270d57 · upstream ↗ 2383LCDBF: lda Joy1Status 2384 and #$08 2385 lsr 2386 lsr 2387 lsr 2388 tax 2389 lda LCCBE,x 2390 cmp AnimResetIndex 2391 beq - 2392 jsr SetSamusAnim 2393 pla 2394 pla ⋮
Source_Files/Bank07.asm lines 2383–2395 at 4270d57 · upstream ↗ 2225 ldx #$00 2226 lda AnimResetIndex 2227 cmp #an_SamusStand 2228 beq + 2229 inx 2230 cmp #$27 2231 beq + 2232 lda #$04 2233 jsr SetSamusNextAnim 2234* lda RunAnimationTbl,x 2235 sta AnimResetIndex ⋮ 2241RunAnimationTbl: ⋮
Source_Files/Bank07.asm lines 2225–2243 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^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:SetSamusJumpsetsSamusGravityto$18, or$12with the boots.Codenmikstas/metroid-disassemblyHow we know
2672SetSamusJump: ⋮ 2681 LDA #$FC 2682 STA ObjVertSpeed ⋮ 2686 LDA SamusGear ⋮ 2693LCFF3: LDY #$18 ; gravity (high value -> low jump) 2694 LDA SamusGear 2695 AND #gr_HIGHJUMP 2696 BEQ + ; branch if Samus doesn't have High Jump 2697 LDY #$12 ; lower gravity value -> high jump! 2698* STY SamusGravity
Source_Files/Bank07.asm lines 2672–2698 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^ abEach frame
VertAccelerateaddsSamusGravityto the fraction byte of Samus's vertical speed and carries into its whole part, so gravity isSamusGravity/256 px/frame²:$18(0.09375) in a jump,$12with the High Jump Boots,$1Aafter walking off a ledge, and$38after a hit (computed: 0.0703125, 0.1015625, 0.21875). A fall is capped at 5 px/frame and a rise at 6.Codenmikstas/metroid-disassemblyHow we know
5582;gravity used in the acceleration calculation. The higher the number, the more intense the gravity. 5583;The possible values for gravity are as follows: 5584;#$38-When Samus has been hit by an enemy. 5585;#$1A-When Samus is falling. 5586;#$18-Jump without high jump boots. 5587;#$12-Jump with high jump boots.
Source_Files/Bank07.asm lines 5582–5587 at 4270d57 · upstream ↗ 2693LCFF3: LDY #$18 ; gravity (high value -> low jump) 2694 LDA SamusGear 2695 AND #gr_HIGHJUMP 2696 BEQ + ; branch if Samus doesn't have High Jump 2697 LDY #$12 ; lower gravity value -> high jump! 2698* STY SamusGravity
Source_Files/Bank07.asm lines 2693–2698 at 4270d57 · upstream ↗ 2440* lda #$FD 2441 sta ObjVertSpeed 2442 lda #$38 ;Samus is hit. Store Samus hit gravity. 2443 sta SamusGravity ;
Source_Files/Bank07.asm lines 2440–2443 at 4270d57 · upstream ↗ 5606LE3A0: lda #$1A ;Samus is falling. Store falling gravity value. 5607LE3A2: sta SamusGravity ; ⋮ 5609LE3A5:* ldx #$05 ;Load X with maximum downward speed. 5610LE3A7: lda VertCntrLinear ; 5611LE3AA: clc ;The higher the gravity, the faster this addition overflows 5612LE3AB: adc SamusGravity ;and the faster ObjVertSpeed is incremented. 5613LE3AE: sta VertCntrLinear ; 5614LE3B1: lda ObjVertSpeed ;Every time above addition sets carry bit, ObjVertSpeed is 5615LE3B4: adc #$00 ;incremented. This has the effect of speeding up a fall
Source_Files/Bank07.asm lines 5606–5616 at 4270d57 · upstream ↗ 5620LE3BB: lda #$00 ; 5621LE3BD: cmp VertCntrLinear ;Sets carry bit. 5622LE3C0: sbc ObjVertSpeed ;Subtract ObjVertSpeed to see if maximum speed has 5623LE3C3: cmp #$06 ;been exceeded. 5624LE3C5: ldx #$FA ;Load X with maximum upward speed. ⋮ 5628LE3C9:* cmp #$05 ;Has maximum downward speed been reached? 5629LE3CB:* bcc + ;If not, branch. ⋮ 5632LE3CD: jsr StopVertMovement ;($D147)Clear verticle movement data. 5633LE3D0: stx ObjVertSpeed ;Set Samus vertical speed to max.
Source_Files/Bank07.asm lines 5620–5633 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-25.
- ^Once a jump has carried Samus 32 px up (
SamusJmpDsplcmntat$20), letting go of jump while she still rises callsStopVertMovement, which sets her vertical speed to 0. The running-jump handler does the same past 32 px.Codenmikstas/metroid-disassemblyHow we know
2701SamusJump: 2702 LDA SamusJmpDsplcmnt 2703 BIT ObjVertSpeed 2704 BPL + ; branch if falling down 2705 CMP #$20 2706 BCC + ; branch if jumped less than 32 pixels upwards 2707 BIT Joy1Status 2708 BMI + ; branch if JUMP button still pressed 2709 JSR StopVertMovement ;($D147)Stop jump (start falling).
Source_Files/Bank07.asm lines 2701–2709 at 4270d57 · upstream ↗ 2271* cpy #$20 2272 bcc ++ ⋮ 2278* bit Joy1Status 2279 bmi + 2280 jsr StopVertMovement ;($D147)
Source_Files/Bank07.asm lines 2271–2280 at 4270d57 · upstream ↗ 2867StopVertMovement: 2868LD147: ldy #$00 2869 sty ObjVertSpeed 2870 sty VertCntrLinear
Source_Files/Bank07.asm lines 2867–2870 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^ abMeasured 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.Verifiednmikstas/metroid-disassembly
How we know
2702 LDA SamusJmpDsplcmnt 2703 BIT ObjVertSpeed ⋮ 2705 CMP #$20 ⋮ 2707 BIT Joy1Status
Source_Files/Bank07.asm lines 2701–2709 at 4270d57 · upstream ↗ lda VertCntrLinear clc adc SamusGravity sta VertCntrLinear lda ObjVertSpeed adc #$00 sta ObjVertSpeed
Source_Files/Bank07.asm#L5589-L5643 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Checked by a second AI model, 2026-10-25.
- ^A jump from a standstill starts after the press frame's move:
SamusStandcallsSetSamusData, which moves her, and thenSetSamusJump, so she first rises on the next frame. A running jump's handler callsSetSamusJump(LCD40) beforeSetSamusData, so she rises on the press frame, and there it sets her top speed for the jump,SamusHorzSpdMax, to$12.Codenmikstas/metroid-disassemblyHow we know
2185LCC65:* BIT Joy1Change ;Check if jump was just pressed. 2186LCC67: BPL + ;Branch if JUMP not pressed. 2187LCC69: LDA #sa_Jump ; 2188LCC6B: STA ObjAction ;Set Samus status as jumping. 2189LCC6E:* LDA #$04 ;Prepare to set animation delay to 4 frames. 2190LCC70: JSR SetSamusData ;($CD6D)Set Samus control data and animation. ⋮ 2194LCC7A: JSR ChooseRoutine ;Select routine below. ⋮ 2200LCC81: .word SetSamusJump ;($CFC3)Samus is jumping.
Source_Files/Bank07.asm lines 2185–2200 at 4270d57 · upstream ↗ 2304* jsr LCF2E 2305 lda Joy1Change 2306 bpl + ; branch if JUMP not pressed 2307LCD40: jsr LCFC3 2308 lda #$12 2309 sta SamusHorzSpdMax 2310 jmp LCD6B
Source_Files/Bank07.asm lines 2304–2310 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^ abA 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.Codenmikstas/metroid-disassemblyHow we know
2305 lda Joy1Change 2306 bpl + ; branch if JUMP not pressed 2307LCD40: jsr LCFC3 2308 lda #$12 2309 sta SamusHorzSpdMax
Source_Files/Bank07.asm lines 2305–2309 at 4270d57 · upstream ↗ 2286* lda SamusInLava ⋮ 2290* jsr LCF88
Source_Files/Bank07.asm lines 2286–2290 at 4270d57 · upstream ↗ 2642LCF88: LDA Joy1Status 2643 AND #$03 2644 BEQ + 2645 JSR BitScan ;($E1E1) 2646 TAX 2647 JSR LCCB7 2648 LDA SamusGravity ⋮ 2650 LDA AnimResetIndex ⋮ 2657 * LDA SamusGravity 2658 BMI + 2659 BEQ + 2660 LDA AnimResetIndex ⋮
Source_Files/Bank07.asm lines 2642–2667 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^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).Codenmikstas/metroid-disassemblyHow we know
2253LCCC2: ldx SamusDir 2254 lda SamusGravity 2255 beq +++++++ 2256 ldy SamusJmpDsplcmnt 2257 bit ObjVertSpeed 2258 bmi + 2259 cpy #$18 2260 bcs ++++ 2261 lda #an_SamusJump 2262 sta AnimResetIndex 2263 bcc ++++ ; branch always 2264* cpy #$18 ⋮
Source_Files/Bank07.asm lines 2253–2270 at 4270d57 · upstream ↗ 2281* lda #an_SamusRun 2282 cmp AnimResetIndex 2283 bne + 2284 lda #an_SamusJump 2285 sta AnimResetIndex
Source_Files/Bank07.asm lines 2281–2285 at 4270d57 · upstream ↗ 5744* dec ObjectY 5745 inc SamusJmpDsplcmnt
Source_Files/Bank07.asm lines 5744–5745 at 4270d57 · upstream ↗ 5783* inc ObjectY 5784 dec SamusJmpDsplcmnt
Source_Files/Bank07.asm lines 5783–5784 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^In a jump from a standstill,
LD055sets the whole part of Samus's horizontal speed outright every frame, 1 px/frame toward a held direction and 0 with neither held.Codenmikstas/metroid-disassemblyHow we know
2738LD055: ldx #$01 2739 ldy #$00 2740 lda Joy1Status 2741 lsr 2742 bcs + ; branch if RIGHT pressed 2743 dex 2744 lsr 2745 bcc ++++ ; branch if LEFT not pressed 2746 dex ⋮ 2767* stx ObjHorzSpeed
Source_Files/Bank07.asm lines 2738–2767 at 4270d57 · upstream ↗ 2710* JSR LD055 2711 JSR LCF2E
Source_Files/Bank07.asm lines 2710–2711 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^ abOn the ground,
VertAcceleratesetsSamusHorzSpdMaxto$18and 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.Codenmikstas/metroid-disassemblyHow we know
5589VertAccelerate: 5590LE37A: lda SamusGravity ;Is Samus rising or falling? 5591LE37D: bne ++ ;Branch if yes. 5592LE37F: lda #$18 ; 5593LE381: sta SamusHorzSpdMax ;Set Samus maximum running speed. ⋮ 5599LE38F: jsr CheckMoveDown ;($E7AD)Is Samus obstructed downwards? 5600LE392: bcc ++ ;Branch if yes. 5601LE394:* jsr SamusOnElevatorOrEnemy ;($D976)Calculate if Samus standing on elevator or enemy. ⋮ 5606LE3A0: lda #$1A ;Samus is falling. Store falling gravity value. 5607LE3A2: sta SamusGravity ; ⋮ 5609LE3A5:* ldx #$05 ;Load X with maximum downward speed.
Source_Files/Bank07.asm lines 5589–5609 at 4270d57 · upstream ↗ 2281* lda #an_SamusRun 2282 cmp AnimResetIndex 2283 bne + 2284 lda #an_SamusJump 2285 sta AnimResetIndex
Source_Files/Bank07.asm lines 2281–2285 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^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:
VertAcceleratesets the falling gravity on the first frame with nothing below her.Codenmikstas/metroid-disassemblyHow we know
2252SamusRun: 2253LCCC2: ldx SamusDir 2254 lda SamusGravity 2255 beq +++++++
Source_Files/Bank07.asm lines 2252–2255 at 4270d57 · upstream ↗ 2286* lda SamusInLava 2287 beq + 2288 lda Joy1Change 2289 bmi LCD40 ; branch if JUMP pressed
Source_Files/Bank07.asm lines 2286–2289 at 4270d57 · upstream ↗ 2720 LDA SamusInLava 2721 BEQ + 2722 LDA Joy1Change 2723 BPL + ; branch if JUMP not pressed 2724 JSR LCFC3
Source_Files/Bank07.asm lines 2719–2724 at 4270d57 · upstream ↗ 5590LE37A: lda SamusGravity ;Is Samus rising or falling? ⋮ 5599LE38F: jsr CheckMoveDown ;($E7AD)Is Samus obstructed downwards? ⋮ 5606LE3A0: lda #$1A ;Samus is falling. Store falling gravity value. 5607LE3A2: sta SamusGravity ;
Source_Files/Bank07.asm lines 5590–5607 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Checked by a second AI model, 2026-10-21.
- ^ abIn Brinstar's bank, the frames of Samus's running, standing, jumping, and somersault animations all give the radii
$0Fand$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).Codenmikstas/metroid-disassemblyHow we know
679;Samus run animation. 680L8572: .byte $03, $04, $05, $FF ⋮ 688;Samus Stand animation. 689L8579: .byte $08, $FF ⋮ 697;Samus Jump animation. 698L857E: .byte $10, $FF ⋮ 700;Samus summersault animation. 701L8580: .byte $17, $18, $19, $1A, $FF
Source_Files/Bank01.asm lines 679–701 at 4270d57 · upstream ↗ 789FramePtrTable: 790L860B: .word $87CB, $87CB, $87CB, $87CB, $87DD, $87F0, $8802, $8802 791L861B: .word $8818, $882C, $882C, $882C, $882C, $883E, $8851, $8863 792L862B: .word $8863, $8874, $8874, $8885, $8885, $8885, $8885, $8885 793L863B: .word $888F, $8899, $88A3, $88AD, $88B8, $88C3, $88CE, $88D9
Source_Files/Bank01.asm lines 789–793 at 4270d57 · upstream ↗ 1157;current frame. The second byte is saved as the object's y radius and the third byte is saved 1158;as the object's x radius. ⋮ 1160;Samus run. 1161L87CB: .byte $40, $0F, $04, $00, $01, $FD, $20, $FE, $41, $40, $FD, $60, $20, $21, $FE, $FE
Source_Files/Bank01.asm lines 1157–1161 at 4270d57 · upstream ↗ 1176;Samus stand. 1177L8818: .byte $40, $0F, $04, $FD, $20, $0E, $0D, $FE, $1E, $1D, $2E, $2D, $FE, $FD, $60, $3B
Source_Files/Bank01.asm lines 1176–1177 at 4270d57 · upstream ↗ 1192;Samus stand and jump. 1193L8863: .byte $40, $0F, $04, $00, $01, $FD, $20, $FE, $41, $40, $FD, $60, $22, $07, $08, $32 ⋮ 1200;Samus summersault. 1201L8885: .byte $41, $0F, $04, $52, $53, $62, $63, $72, $73, $FF ⋮ 1203;Samus summersault. 1204L888F: .byte $42, $0F, $04, $54, $55, $56, $64, $65, $66, $FF ⋮ 1206;Samus summersault. 1207L8899: .byte $81, $0F, $04, $52, $53, $62, $63, $72, $73, $FF ⋮ 1209;Samus summersault. 1210L88A3: .byte $82, $0F, $04, $54, $55, $56, $64, $65, $66, $FF
Source_Files/Bank01.asm lines 1192–1210 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^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, andMoveSamusRightcall 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,VertAcceleratemakes the same test under her each frame.Codenmikstas/metroid-disassemblyHow we know
5713MoveSamusUp: 5714LE457: lda ObjectY ;Get Samus' y position in room. 5715 sec ; 5716 sbc ObjRadY ;Subtract Samus' vertical radius. 5717LE45E: and #$07 ;Check if result is a multiple of 8. If so, branch to 5718LE460: bne + ;Only call crash detection every 8th pixel. 5719LE462: jsr CheckMoveUp ;($E7A2)Check if Samus obstructed UPWARDS.
Source_Files/Bank07.asm lines 5713–5720 at 4270d57 · upstream ↗ 5751MoveSamusDown: 5752 lda ObjectY 5753 clc 5754 adc ObjRadY 5755 and #$07 5756 bne + ; only call crash detection every 8th pixel 5757 jsr CheckMoveDown ; check if Samus obstructed DOWNWARDS
Source_Files/Bank07.asm lines 5751–5757 at 4270d57 · upstream ↗ 6215CheckMoveUp: 6216LE7A2: ldx PageIndex 6217 lda ObjRadY,x 6218 clc 6219 adc #$08 6220 jmp + ⋮ 6222CheckMoveDown: 6223 ldx PageIndex 6224 lda #$00 6225 sec 6226 sbc ObjRadY,x 6227* sta $02 ⋮
Source_Files/Bank07.asm lines 6215–6229 at 4270d57 · upstream ↗ 6234* sta $03 6235 tay 6236 ldx #$00 6237 lda $09 6238 sec 6239 sbc $03 6240 and #$07 6241 beq + 6242 inx 6243* jsr LE8CE 6244 sta $04 6245 jsr LE90F
Source_Files/Bank07.asm lines 6234–6245 at 4270d57 · upstream ↗ 6280* dex 6281 beq + 6282 jsr LE98E 6283 jmp LE7E6
Source_Files/Bank07.asm lines 6280–6283 at 4270d57 · upstream ↗ 6341CheckMoveLeft: 6342 ldx PageIndex 6343 lda ObjRadX,x 6344 clc 6345 adc #$08 6346 jmp + ⋮ 6348CheckMoveRight: 6349 ldx PageIndex 6350 lda #$00 6351 sec 6352 sbc ObjRadX,x
Source_Files/Bank07.asm lines 6341–6352 at 4270d57 · upstream ↗ 5594LE384: lda ObjectY ; 5595LE387: clc ; 5596LE388: adc ObjRadY ;Check is Samus is obstructed downwards on y room 5597LE38B: and #$07 ;positions divisible by 8(every 8th pixel). 5598LE38D: bne + ; 5599LE38F: jsr CheckMoveDown ;($E7AD)Is Samus obstructed downwards? 5600LE392: bcc ++ ;Branch if yes.
Source_Files/Bank07.asm lines 5594–5600 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^A solid tile beside Samus stops her sideways move for the rest of the frame (
CheckStopHorzMvmt). On the ground (SamusGravity0), and not in the ball, it also callsStopHorzMovement, which clears her speed and acceleration and makes her stand. In the air her speed is kept.Codenmikstas/metroid-disassemblyHow we know
5536LE333:* sta ObjectCounter ;Store number of pixels to move Samus this frame. 5537LE335:* jsr MoveSamusLeft ;($E626)Attempt to move Samus 1 pixel to the left. 5538LE338: jsr CheckStopHorzMvmt ;($E365)Check if horizontal movement needs to be stopped. 5539LE33B: dec ObjectCounter ;1 pixel movement is complete. 5540LE33D: bne - ;Branch if Samus needs to be moved another pixel.
Source_Files/Bank07.asm lines 5536–5540 at 4270d57 · upstream ↗ 5568CheckStopHorzMvmt: 5569LE365: bcs Exit10 ;Samus moved successfully. Branch to exit. 5570LE367: lda #$01 ;Load counter with #$01 so this function will not be 5571LE369: sta ObjectCounter ;called again. 5572LE36C: lda SamusGravity ;Is Samus on the ground? 5573LE36E: bne Exit10 ;If not, branch to exit. 5574LE370: lda ObjAction ; 5575LE373: cmp #sa_Roll ;Is Samus rolled into a ball? 5576LE375: beq Exit10 ;If so, branch to exit.
Source_Files/Bank07.asm lines 5568–5577 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^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.Codenmikstas/metroid-disassemblyHow we know
5471LE2C2:* sta ObjectCounter ;Store number of pixels to move Samus this frame. 5472LE2C4:* jsr MoveSamusUp ;($E457)Attempt to move Samus up 1 pixel. 5473LE2C7: bcs + ;Branch if Samus successfully moved up 1 pixel. ⋮ 5475LE2C9: sec ;Samus blocked upwards. Divide her speed by 2 and set the 5476LE2CA: ror ObjVertSpeed ;MSB to reverse her direction of travel. 5477LE2CD: ror VertCntrLinear ; 5478LE2D0: jmp SamusMoveHorizontally ;($E31A)Attempt to move Samus left/right.
Source_Files/Bank07.asm lines 5471–5478 at 4270d57 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-25.
Checked by a second AI model, 2026-10-27.
- ^Samus's health is binary-coded decimal in tenths of a unit;
HealthHiholds the full energy tanks in its high nibble and the tens digit in its low nibble, andHealthLoholds the ones digit in its high nibble.Codenmikstas/metroid-disassembly · “Metroid (USA)”How we know
270.alias HealthLo $0106 ;Lower health digit in upper 4 bits. 271.alias HealthHi $0107 ;Upper health digit in lower 4 bits 272 ;# of full tanks in upper 4 bits.
Source_Files/Metroid_Defines.asm lines 270–272 at 4270d57 · upstream ↗ 8211 ldx #$00 ;Increase HealthHi by 0. 8212 ldy #$50 ;Increase HealthLo by 5.
Source_Files/Bank07.asm lines 8211–8212 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 $CE92, SubtractHealth, which subtracts in base 10. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abSamus starts with 30 units of energy and no energy tanks.Codenmikstas/metroid-disassembly · “Metroid (USA)”
How we know
1580 LDA #$00 ; 1581 STA HealthLo ;Starting health is 1582 LDA #$03 ;set to 30 units. 1583 STA HealthHi ;
Source_Files/Bank07.asm lines 1580–1583 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 SamusInit, $C8D1. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abWhen an enemy touches Samus, the game charges her the base damage of the current area, two bytes in that area's bank at
$95CE, whatever the enemy; Brinstar's is 8 units, Norfair's 10, Kraid's hideout's 20, Ridley's hideout's 24, and Tourian's 30.Codenmikstas/metroid-disassembly · “Metroid (USA)”How we know
7997LF306: lda $95CE 7998 sta HealthLoChange 7999 lda $95CF 8000 sta HealthHiChange
Source_Files/Bank07.asm lines 7997–8000 at 4270d57 · upstream ↗ 1829L95CE: .byte $80 ;Base damage caused by area enemies to lower health byte. 1830L95CF: .byte $00 ;Base damage caused by area enemies to upper health byte.
Source_Files/Bank01.asm lines 1829–1830 at 4270d57 · upstream ↗ 1844L95CE: .byte $00 ;Base damage caused by area enemies to lower health byte. 1845L95CF: .byte $01 ;Base damage caused by area enemies to upper health byte.
Source_Files/Bank02.asm lines 1844–1845 at 4270d57 · upstream ↗ 1845L95CE: .byte $00 ;Base damage caused by area enemies to lower health byte. 1846L95CF: .byte $02 ;Base damage caused by area enemies to upper health byte.
Source_Files/Bank04.asm lines 1845–1846 at 4270d57 · upstream ↗ 1849L95CE: .byte $40 ;Base damage caused by area enemies to lower health byte. 1850L95CF: .byte $02 ;Base damage caused by area enemies to upper health byte.
Source_Files/Bank05.asm lines 1849–1850 at 4270d57 · upstream ↗ 1852L95CE: .byte $00 ;Base damage caused by area enemies to lower health byte. 1853L95CF: .byte $03 ;Base damage caused by area enemies to upper health byte.
Source_Files/Bank03.asm lines 1852–1853 at 4270d57 · upstream ↗ 18* Bank01 - Brinstar 19* Bank02 - Norfair 20* Bank03 - Tourian 21* Bank04 - Kraid Hideout 22* Bank05 - Ridley Hideout
README.md lines 18–22 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 $F306, the load; bank 1 $95CE, Brinstar's bytes; banks 2-5 hold the others at the same address. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^With the Varia suit,
SubtractHealthhalves every amount it takes from Samus.Codenmikstas/metroid-disassembly · “Metroid (USA)”How we know
2523LCEAC: lda SamusGear ; 2524LCEAF: and #gr_VARIA ;Check is Samus has Varia. 2525LCEB1: beq + ; 2526LCEB3: lsr HealthLoChange ;If Samus has Varia, divide damage by 2. 2527LCEB5: lsr HealthHiChange ;
Source_Files/Bank07.asm lines 2523–2531 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 SubtractHealth, $CE92. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abcdMeasured in the ROM, a crawler held against Samus in Brinstar's first room takes 8 units with each hit, and the next hit lands 50 game frames later; one of the two gaps also held 2 lag frames, for 52 frames of video.Verified“Metroid (USA)” · nmikstas/metroid-disassembly
How we know
In the cartridge (“Metroid (USA)”): bank 7 CheckHealthStatus, which the probe's hits run. We don't reproduce the bytes.
2449 beq CheckHealthBeep 2450 dec SamusBlink
Source_Files/Bank07.asm lines 2421–2504 at 4270d57 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on Metroid (USA) (SHA-1
2080059bea45…). It fails if the result disagrees with the claim.The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Checked by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-25.
- ^ abcA hit sets
SamusBlinkto$32, which is 50, gives Samus an upward speed of$FDwith the hit gravity$38, and takes the push direction from the hit; the comment says "Set blink for 32 frames".Codenmikstas/metroid-disassembly · “Metroid (USA)”How we know
2421CheckHealthStatus: 2422LCDFA: lda SamusHit ; 2423 and #$20 ;Has Samus been hit? 2424 beq +++ ;If not, branch to check if still blinking from recent hit. 2425 lda #$32 ; 2426 sta SamusBlink ;Samus has been hit. Set blink for 32 frames. ⋮ 2428 sta DmgPushDir ⋮ 2431 beq ++ ⋮ 2434* lda SamusHit 2435 and #$08 2436 lsr 2437 lsr ⋮
Source_Files/Bank07.asm lines 2421–2443 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 CheckHealthStatus, $CDFA. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^
CheckHealthStatus, which Samus's routines call once per frame, lowersSamusBlinkby 1 each time, so the blink lasts 50 frames of game logic.Codenmikstas/metroid-disassembly · “Metroid (USA)”How we know
2448* lda SamusBlink 2449 beq CheckHealthBeep 2450 dec SamusBlink
Source_Files/Bank07.asm lines 2448–2450 at 4270d57 · upstream ↗ 2342LCD7E:* JSR CheckHealthStatus ;($CDFA)Check if Samus hit, blinking or Health low.Source_Files/Bank07.asm line 2342 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 CheckHealthStatus. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^Each frame, the crash detection skips the contact check between Samus and the enemies while
SamusBlinkis above 0.Codenmikstas/metroid-disassembly · “Metroid (USA)”How we know
7696* ldy #$00 7697 lda SamusBlink 7698 bne NextEnemy 7699 jsr IsSamusDead 7700 beq NextEnemy 7701 jsr LF140 7702 jsr LF282
Source_Files/Bank07.asm lines 7696–7702 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 crash detection, $F034, which ends by calling SubtractHealth. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^In lava, Samus's blink is set to 50 on every frame, so enemies can't touch her, while the lava takes 0.7 units twice every 8 frames without a push; the Varia suit cuts that to once, and
SubtractHealthhalves it to 0.3 units.Codenmikstas/metroid-disassembly · “Metroid (USA)”revisedHow we know
5432LE27B: sty DmgPushDir ;Don't push Samus from lava damage. 5433LE27D: jsr ClearHealthChange ;($F323)Clear any pending health changes to Samus. 5434LE280: lda #$32 ; 5435LE282: sta SamusBlink ;Make Samus blink. ⋮ 5441LE28F: lsr ;This portion of the code causes Samus to be damaged by 5442LE290: and #$03 ;lava twice every 8 frames if she does not have the varia 5443LE292: bne ++ ;but only once every 8 frames if she does. ⋮ 5448LE29D:* lda #$07 ; 5449LE29F: sta HealthLoChange ;Samus takes lava damage. 5450LE2A1: jsr SubtractHealth ;($CE92)
Source_Files/Bank07.asm lines 5432–5450 at 4270d57 · upstream ↗ 2523LCEAC: lda SamusGear ; 2524LCEAF: and #gr_VARIA ;Check is Samus has Varia. ⋮ 2526LCEB3: lsr HealthLoChange ;If Samus has Varia, divide damage by 2.
Source_Files/Bank07.asm lines 2523–2527 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 LavaAndMoveCheck, $E269. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Revised after a check by a second AI model, 2026-10-21: The Varia suit halves lava damage, to 0.3 units.
- ^ abcOn each frame of the blink,
CheckHealthStatussets Samus's horizontal speed in the push direction to the blink counter divided by 16, so the push ends when the counter falls below 16; below 48, it skips the push on any frame Samus has horizontal acceleration.Codenmikstas/metroid-disassembly · “Metroid (USA)”How we know
2451 ldx DmgPushDir 2452 inx 2453 beq +++ 2454 jsr Adiv16 ; / 16 2455 cmp #$03 2456 bcs + 2457 ldy SamusHorzAccel 2458 bne +++ 2459 jsr LCF4E 2460* dex 2461 bne + 2462 jsr TwosCompliment ;($C3D4) ⋮
Source_Files/Bank07.asm lines 2451–2463 at 4270d57 · upstream ↗ 165.alias DmgPushDir $72 ;#$00=Push Samus left when hit, #$01=Push right, #$FF=No push.Source_Files/Metroid_Defines.asm line 165 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 CheckHealthStatus. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abcMeasured the same way, with the crawler moved off Samus during each blink, the three pushes carried her 37, 45, and 44 px away from it; two lifted her 19 px and one 2 px.Verified“Metroid (USA)” · nmikstas/metroid-disassembly
How we know
In the cartridge (“Metroid (USA)”): bank 7 CheckHealthStatus. We don't reproduce the bytes.
2428 sta DmgPushDir ⋮ 2435 and #$08 2436 lsr 2437 lsr 2438 lsr 2439 sta DmgPushDir 2440* lda #$FD 2441 sta ObjVertSpeed
Source_Files/Bank07.asm lines 2421–2463 at 4270d57 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on Metroid (USA) (SHA-1
2080059bea45…). It fails if the result disagrees with the claim.The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Checked by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-25.
- ^The main loop calls
RandomNumbersonce per frame, after the frame's work; it adds 5 toRandomNumber1five times, 25 in all, so the byte steps through all 256 values every 256 frames.Codenmikstas/metroid-disassembly · “Metroid (USA)”How we know
34RandomNumbers: ⋮ 37LC002: LDX #$05 ; 38LC004:* LDA RandomNumber1 ; 39LC006: CLC ; 40LC007: ADC #$05 ; 41LC009: STA RandomNumber1 ;2E is increased by #$19 every frame and ⋮ 46LC012: DEX ; 47LC013: BNE - ;
Source_Files/Bank07.asm lines 34–47 at 4270d57 · upstream ↗ 170WaitNMIEnd: ⋮ 176LC0D3:* JSR RandomNumbers ;($C000)Update pseudo random numbers. 177LC0D6: JMP MainLoop ;($C0BC)Jump to top of subroutine.
Source_Files/Bank07.asm lines 170–177 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 $C000. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^When an enemy dies, a
RandomNumber1below 16 drops nothing; otherwise its low 3 bits pick fromItemDropTbl, which holds a missile at 0 and 3, energy at 1, 4, and 6, and$89at 2, 5, and 7.Codenmikstas/metroid-disassembly · “Metroid (USA)”How we know
4575 LDA RandomNumber1 4576 CMP #$10 4577 BCC LDD5B 4578* AND #$07 4579 TAY 4580 LDA ItemDropTbl,y 4581 STA EnAnimFrame,x
Source_Files/Bank07.asm lines 4575–4581 at 4270d57 · upstream ↗ 4722;The following table determines what, if any, items an enemy will drop when it is killed. ⋮ 4724ItemDropTbl: 4725LDE35: .byte $80 ;Missile. 4726LDE36: .byte $81 ;Energy. 4727LDE37: .byte $89 ;No item. 4728LDE38: .byte $80 ;Missile. 4729LDE39: .byte $81 ;Energy. 4730LDE3A: .byte $89 ;No item. 4731LDE3B: .byte $81 ;Energy. 4732LDE3C: .byte $89 ;No item.
Source_Files/Bank07.asm lines 4722–4732 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 $DCFC, the drop code; bank 7 $DE35, ItemDropTbl. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^Item
$89drops only for an enemy whose byte in the area's table at$968Bhas bit 0 set, and Samus collects it as 20 units of energy; the table's comment calls it "No item".Codenmikstas/metroid-disassembly · “Metroid (USA)”How we know
4592* LDY MaxEnergyPickup 4593 CPY CrntEnrgyPickups 4594 BEQ LDD5B 4595 INC CrntEnrgyPickups 4596 CMP #$89 4597 BNE -- 4598 LSR $00 4599 BCS --
Source_Files/Bank07.asm lines 4592–4599 at 4270d57 · upstream ↗ 8534LF74B: ldy EnDataIndex,x 8535 lda $968B,y
Source_Files/Bank07.asm lines 8534–8535 at 4270d57 · upstream ↗ 8203 ldy #$00 8204 ldx #$03 8205 pla 8206 bne ++ 8207 dex 8208 pla 8209 cmp #$81 8210 bne + 8211 ldx #$00 ;Increase HealthHi by 0. 8212 ldy #$50 ;Increase HealthLo by 5. ⋮ 8215 sty HealthLoChange 8216 stx HealthHiChange
Source_Files/Bank07.asm lines 8203–8216 at 4270d57 · upstream ↗ 1878L968B: .byte $01, $01, $01, $00, $86, $04, $89, $80, $81, $00, $00, $00, $82, $00, $00, $00Source_Files/Bank01.asm line 1878 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 $F483, the pickup code; bank 1 $968B, Brinstar's table. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abA small energy ball gives Samus 5 units and a
$89ball 20; a missile gives 2 missiles; an enemy whose data index carries the 30-unit flag gives 30 units or 30 missiles instead.Codenmikstas/metroid-disassembly · “Metroid (USA)”How we know
8203 ldy #$00 8204 ldx #$03 8205 pla 8206 bne ++ 8207 dex 8208 pla ⋮ 8210 bne + ⋮ 8220PickupMissile: 8221 lda #$02 8222 ldy EnDataIndex,x 8223 beq + 8224 lda #$1E
Source_Files/Bank07.asm lines 8203–8224 at 4270d57 · upstream ↗ 4566 JSR LF74B 4567 STA $00 4568 JSR $80B0 4569 AND #$20 4570 STA EnDataIndex,x
Source_Files/Bank07.asm lines 4566–4570 at 4270d57 · upstream ↗ 89L80B0: LDY EnDataIndex,X 90L80B3: LDA $977B,Y 91L80B6: ASL ;*2
Source_Files/Bank01.asm lines 89–91 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 $F483, the pickup code. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abcEach door re-rolls the room's caps from
RandomNumber1, 0 to 15 missile drops and 64 to 94 energy drops, and zeroes the counts. A drop that would pass its cap is lost; one that passes the test adds to its count, even a$89roll that the enemy then can't drop.Codenmikstas/metroid-disassembly · “Metroid (USA)”revisedHow we know
1381L8B1B: STA CrntMslePickups ; 1382L8B1D: STA CrntEnrgyPickups ;Reset current missile and energy power-up counters. 1383L8B1F: LDA RandomNumber1 ; 1384L8B21: AND #$0F ;Randomly recalculate max missile pickups(16 max, 0 min). 1385L8B23: STA MaxMissilePickup ; 1386L8B25: ASL ; 1387L8B26: ORA #$40 ;*2 for energy pickups and set bit 6(128 max, 64 min). 1388L8B28: STA MaxEnergyPickup ;
Source_Files/Bank01.asm lines 1381–1388 at 4270d57 · upstream ↗ 4584 LDY MaxMissilePickup 4585 CPY CrntMslePickups 4586 BEQ LDD5B 4587 LDA MaxMissiles 4588 BEQ LDD5B 4589 INC CrntMslePickups ⋮ 4594 BEQ LDD5B 4595 INC CrntEnrgyPickups 4596 CMP #$89 4597 BNE -- 4598 LSR $00 4599 BCS --
Source_Files/Bank07.asm lines 4584–4599 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 1 $8B1B-$8B29, the reset in SamusEnterDoor; each area bank has its own copy; bank 7, the drop code. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Revised after a check by a second AI model, 2026-10-21: A withheld $89 drop still counts toward the cap.
- ^Until the first door, the caps on missile and energy drops are 39 when the game starts in Brinstar, because
SamusInitstores the mirroring value$27into them, or 47 ($2F) elsewhere.Codenmikstas/metroid-disassembly · “Metroid (USA)”How we know
1564 LDY #$27 1565 LDA InArea 1566 AND #$0F 1567 BEQ + ;Branch if Samus starting in Brinstar. 1568 LSR ScrollDir ;If not in Brinstar, change scroll direction from left 1569 LDY #$2F ;to down. and set PPU for horizontal mirroring. 1570 * STY MirrorCntrl ; 1571 STY MaxMissilePickup 1572 STY MaxEnergyPickup
Source_Files/Bank07.asm lines 1564–1572 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 SamusInit, $C8D1. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abIn Tourian, enemies with data index 2 or 4 drop nothing, and any other kill that would drop nothing resets both caps to 1 and both counts to 0 and rolls the same
RandomNumber1again, skipping the test for 16.Codenmikstas/metroid-disassembly · “Metroid (USA)”How we know
4555LDCFC: LDA InArea 4556 CMP #$13 4557 BNE + 4558 LDA EnDataIndex,x 4559 CMP #$04 4560 BEQ +++++ 4561 CMP #$02 4562 BEQ +++++
Source_Files/Bank07.asm lines 4555–4562 at 4270d57 · upstream ↗ 4601LDD5B: ldx PageIndex 4602 lda InArea 4603 cmp #$13 4604 beq ++ 4605* jmp KillObject ;($FA18)Free enemy data slot. ⋮ 4607* lda RandomNumber1 4608 ldy #$00 4609 sty CrntEnrgyPickups 4610 sty CrntMslePickups 4611 iny 4612 sty MaxMissilePickup 4613 sty MaxEnergyPickup ⋮
Source_Files/Bank07.asm lines 4601–4614 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7, the drop code. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abcdCalled in the ROM for all 256 values of
RandomNumber1, the drop code gives a Brinstar enemy of data index 0 60 missiles, 90 small energy balls, 90$89balls, and 16 nothing; index 3, whose byte in the tableLF74Breads ($968B+ data index, kept in$00) has bit 0 clear, gives 106 nothing and no$89.Verified“Metroid (USA)” · nmikstas/metroid-disassemblyrevisedHow we know
In the cartridge (“Metroid (USA)”): bank 7 $DCFC, the code the probe calls with Brinstar's bank mapped. We don't reproduce the bytes.
4566 JSR LF74B 4567 STA $00 ⋮ 4575 LDA RandomNumber1 4576 CMP #$10 4577 BCC LDD5B 4578* AND #$07 4579 TAY 4580 LDA ItemDropTbl,y ⋮ 4596 CMP #$89 4597 BNE -- 4598 LSR $00 4599 BCS --
Source_Files/Bank07.asm lines 4555–4614 at 4270d57 · upstream ↗ 8535 lda $968B,y 8536 rts
Source_Files/Bank07.asm lines 8534–8536 at 4270d57 · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on Metroid (USA) (SHA-1
2080059bea45…). It fails if the result disagrees with the claim.The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Checked by a second AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-25.
- ^An energy tank adds a tank, up to 6, and fills Samus's health to her new maximum.Codenmikstas/metroid-disassembly · “Metroid (USA)”
How we know
4354LDC00:* LDA TankCount ; 4355LDC03: CMP #$06 ;Has Samus got 6 energy tanks? 4356LDC05: BEQ + ;If so, she can't have any more. 4357LDC07: INC TankCount ;Otherwise give her a new tank. 4358LDC0A:* LDA TankCount ; 4359LDC0D: JSR Amul16 ;Get tank count and shift into upper nibble. 4360LDC10: ORA #$09 ; 4361LDC12: STA HealthHi ;Set new tank count. Upper health digit set to 9. 4362LDC15: LDA #$99 ;Max out low health digit. 4363LDC17: STA HealthLo ;Health is now FULL!
Source_Files/Bank07.asm lines 4354–4363 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 $DBF7, the missile and energy tank pickup. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abSamus's health can't exceed 99.9 units above her tanks, units for tanks, which is 699.9 with all six.Codenmikstas/metroid-disassembly · “Metroid (USA)”
How we know
2576LCF14: LDA TankCount ; 2577LCF17: JSR Amul16 ;($C2C5)*16. Move tank count to upper 4 bits. 2578LCF1A: ORA #$0F ;Set lower 4 bits. 2579LCF1C: CMP HealthHi ; 2580LCF1F: BCS + ;Is life less than max? if so, branch. 2581LCF21: AND #$F9 ;Life is more than max amount. 2582LCF23: STA HealthHi ; 2583LCF26: LDA #$99 ;Set life to max amount. 2584LCF28: STA HealthLo ;
Source_Files/Bank07.asm lines 2576–2584 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 AddHealth, $CEF9. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^A missile tank raises Samus's missile capacity by 5.Codenmikstas/metroid-disassembly · “Metroid (USA)”
How we know
4349LDBF7:* BEQ + ;Branch if item is an energy tank. 4350LDBF9: LDA #$05 ; 4351LDBFB: JSR AddToMaxMissiles ;($DD97)Increase missile capacity by 5.
Source_Files/Bank07.asm lines 4349–4351 at 4270d57 · upstream ↗ In the cartridge (“Metroid (USA)”): bank 7 $DBF7. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
Sources
Code
- n.d. “Metroid (USA).” · licence, all uses
- nmikstas and Kent Hansen. 2025. “metroid-disassembly: Metroid software disassembly.” Commit 4270d57. nmikstas/metroid-disassembly @ 4270d57 · licence, all uses
Secondary
- Wikidata contributors. 2026. “Wikidata.” Wikimedia Foundation. original · licence, all uses