On this page
  1. How Samus moves
  2. Energy and damage
  3. The blink
  4. The push
  5. Item drops
  6. Health growth
  7. Cartridge
  8. Related
  9. Mechanics
  10. References
  11. Sources

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.

Interactive: movement-lab (needs JavaScript)

Samus’s jump: a tap, a held jump, and a late release

#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.

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 100t+99.943 units for t tanks43. A missile tank adds 5 to her missile capacity44.

Cartridge

Interactive: rom-parts (needs JavaScript)

128 KB, a cell per 512 bytes, from 373 mapped regions

Mechanics

MechanicKindHow
Invincibility framesFrame timera 50-frame blink
KnockbackFading pusha push that fades with the blink
Item drop tableOne rollan 8-entry table read by a frame-stepped byte, capped per room
Damage formulaFixed tableone contact damage per area
Random number generatorStepped countera byte that steps by 25 every frame, through all 256 values
Health upgradeWhole containersenergy tanks, each filling Samus's health to the new maximum
JumpOne speed4 px/frame up for every jump; the High Jump Boots lower the gravity instead
GravityOne gravity0.09375 px/frame² in a jump, 0.0703 with the High Jump Boots, held or not
Variable jump heightRelease cappast 32 px of rise, letting go sets the vertical speed to 0, so a tap still rises 32 px
RunningRamp up0.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 controlMomentuma running jump steers by acceleration and a somersault keeps its speed; a standing jump moves 1 px/frame only while held
HitboxBoxradii from each animation frame, 8 × 30 px against the level, tested at every 8 px tile the box spans
AmmunitionOne poolmissiles only, 5 more capacity per missile tank
Morph ballNo jumpthe Maru Mari, from the ground only; a jump press unrolls it
Screw AttackRunning jumpa running jump with the item, while it somersaults or still rises
Password and battery savePassword with a check value24 characters of 6 bits holding 18 bytes, with an 8-bit sum and a random rotation
Coyote timeNonea press on the frame Samus walks off still jumps; after that, a jump press works only in lava
Space JumpNonethe gear byte has no Space Jump bit
Wall jumpNonein the air a jump press works only in lava
Every claim about this game, by concept (57)
generalMetroid's cartridge ROM, counted by part of the game: each stretch of code or data goes to the part the disassembly's labels and comments say it serves, and the value sums the bytes of each part.Code notes
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).Code
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.Code
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.Code
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.Code
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.Wikidata
nmikstas's disassembly of Metroid, assembled with Ophis, equals the PRG ROM of Metroid (USA) byte for byte.Code
A missile tank raises Samus's missile capacity by 5.Code
Air controlA 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.Code
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.Code
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).Code
Coyote timeOnce 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.Code
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.Code
Damage formulaWhen 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.Code
With the Varia suit, SubtractHealth halves every amount it takes from Samus.Code
GravityEach 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.Code
Health upgradeSamus's health is binary-coded decimal in tenths of a unit; HealthHi holds the full energy tanks in its high nibble and the tens digit in its low nibble, and HealthLo holds the ones digit in its high nibble.Code
Samus starts with 30 units of energy and no energy tanks.Code
An energy tank adds a tank, up to 6, and fills Samus's health to her new maximum.Code
Samus's health can't exceed 99.9 units above her tanks, 100t+99.9 units for t tanks, which is 699.9 with all six.Code
HitboxSamus 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.Code
In 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).Code
Invincibility framesA hit sets SamusBlink to $32, which is 50, gives Samus an upward speed of $FD with the hit gravity $38, and takes the push direction from the hit; the comment says "Set blink for 32 frames".Code
Each frame, the crash detection skips the contact check between Samus and the enemies while SamusBlink is above 0.Code
CheckHealthStatus, which Samus's routines call once per frame, lowers SamusBlink by 1 each time, so the blink lasts 50 frames of game logic.Code
Measured 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
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 SubtractHealth halves it to 0.3 units.Code
Item drop tableWhen an enemy dies, a RandomNumber1 below 16 drops nothing; otherwise its low 3 bits pick from ItemDropTbl, which holds a missile at 0 and 3, energy at 1, 4, and 6, and $89 at 2, 5, and 7.Code
Item $89 drops only for an enemy whose byte in the area's table at $968B has bit 0 set, and Samus collects it as 20 units of energy; the table's comment calls it "No item".Code
Each 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 $89 roll that the enemy then can't drop.Code
Until the first door, the caps on missile and energy drops are 39 when the game starts in Brinstar, because SamusInit stores the mirroring value $27 into them, or 47 ($2F) elsewhere.Code
A small energy ball gives Samus 5 units and a $89 ball 20; a missile gives 2 missiles; an enemy whose data index carries the 30-unit flag gives 30 units or 30 missiles instead.Code
In 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 RandomNumber1 again, skipping the test for 16.Code
Called 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 $89 balls, and 16 nothing; index 3, whose byte in the table LF74B reads ($968B + data index, kept in $00) has bit 0 clear, gives 106 nothing and no $89.Verified
JumpEvery 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.Code
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.Code
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.Code
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.Code
KnockbackOn each frame of the blink, CheckHealthStatus sets 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.Code
Measured 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
Morph ballSamus 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.Code
Password and battery saveMetroid's password holds 18 bytes; bytes 0-7 carry one bit for each of 59 items, doors, and Tourian objects collected, byte 8 the starting area (bits 0-5) and whether Samus wears her suit (bit 7), byte 9 her equipment, byte 10 her missile count, bytes 11-13 her age, byte 15 the Kraid and Ridley statue and defeat bits, byte 14 a copy of the RAM byte after the age, which nmikstas labels unused, byte 16 a scramble count, and byte 17 a check byte.Code
Metroid writes the 18 bytes as 24 characters of 6 bits each, three bytes to every four characters, from a 64-symbol set of 0-9, A-Z, a-z, ?, and -; the entry grid's 65th symbol, a space, enters as $FF, all 8 bits set.Code
To make a password, Metroid sums bytes 0-16 into byte 17, then rotates bytes 0-15 right as one 128-bit number, one bit at a time, as many times as byte 16 says; byte 16 is the low 4 bits of a random byte, redrawn until it is not 0.Code
When the player presses Start, Metroid rebuilds the 18 bytes from the 24 characters, rotates bytes 0-15 back left by byte 16's count, and compares their sum with byte 17; on a mismatch it plays the wrong-password sound and shows ERROR TRY AGAIN for 120 frames, and on a match it starts the game; nothing else in the password is checked.Code
Metroid compares the first 16 characters entered with NARPASSWORD00000 before anything else and, on a match, sets a flag and ignores the rest; while that flag is set, the game engine writes $03 to Samus's health high byte, $FF to her equipment byte, and 5 to her missile count, every frame.Code
If Mother Brain has been destroyed when Metroid makes a password, it clears her bit and every Tourian bit in bytes 6 and 7, so the password brings back Mother Brain, the zebetites, and Tourian's missile doors.Code
Computed from the decoder's lines, the password JUSTIN BAILEY ------ ------ passes; after 255 rotations, its bytes 0-16 sum to $FF mod 256, which equals its check byte, byte 17, and byte 8's bit 7 is set, which the game loads as Samus without her suit.Code
Random number generatorThe main loop calls RandomNumbers once per frame, after the frame's work; it adds 5 to RandomNumber1 five times, 25 in all, so the byte steps through all 256 values every 256 frames.Code
RunningOn 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.Code
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.Code
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).Code
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).Code
Screw AttackIsScrewAttackActive 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.Code
Space JumpSamus'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.Code
Variable jump heightOnce 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.Code
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.Verified

References

  1. ^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.

    In the claim ledger

  2. ^ 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.

    In the claim ledger

  3. ^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.Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  4. ^ abEach 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).Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  5. ^ abcOn 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.Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  6. ^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.Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  7. ^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-disassembly
    How 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.

    In the claim ledger

  8. ^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).Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  9. ^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.Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  10. ^ abEach 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.Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  11. ^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.Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  12. ^ 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.

    In the claim ledger

  13. ^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.Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  14. ^ 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-disassembly
    How 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 the claim ledger

  15. ^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-disassembly
    How 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 the claim ledger

  16. ^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.Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  17. ^ abOn 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.Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  18. ^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.Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  19. ^ abIn 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).Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  20. ^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.Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  21. ^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.Codenmikstas/metroid-disassembly
    How 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.

    In the claim ledger

  22. ^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-disassembly
    How 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.

    In the claim ledger

  23. ^Samus's health is binary-coded decimal in tenths of a unit; HealthHi holds the full energy tanks in its high nibble and the tens digit in its low nibble, and HealthLo holds 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.

    In the claim ledger

  24. ^ 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.

    In the claim ledger

  25. ^ 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.

    In the claim ledger

  26. ^With the Varia suit, SubtractHealth halves 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.

    In the claim ledger

  27. ^ 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.

    In the claim ledger

  28. ^ abcA hit sets SamusBlink to $32, which is 50, gives Samus an upward speed of $FD with 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.

    In the claim ledger

  29. ^CheckHealthStatus, which Samus's routines call once per frame, lowers SamusBlink by 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.

    In the claim ledger

  30. ^Each frame, the crash detection skips the contact check between Samus and the enemies while SamusBlink is 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 the claim ledger

  31. ^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 SubtractHealth halves it to 0.3 units.Codenmikstas/metroid-disassembly · “Metroid (USA)”revised
    How 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.

    In the claim ledger

  32. ^ abcOn each frame of the blink, CheckHealthStatus sets 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.

    In the claim ledger

  33. ^ 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.

    In the claim ledger

  34. ^The main loop calls RandomNumbers once per frame, after the frame's work; it adds 5 to RandomNumber1 five 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.

    In the claim ledger

  35. ^When an enemy dies, a RandomNumber1 below 16 drops nothing; otherwise its low 3 bits pick from ItemDropTbl, which holds a missile at 0 and 3, energy at 1, 4, and 6, and $89 at 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.

    In the claim ledger

  36. ^Item $89 drops only for an enemy whose byte in the area's table at $968B has 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, $00 
    
    Source_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.

    In the claim ledger

  37. ^ abA small energy ball gives Samus 5 units and a $89 ball 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.

    In the claim ledger

  38. ^ 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 $89 roll that the enemy then can't drop.Codenmikstas/metroid-disassembly · “Metroid (USA)”revised
    How 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.

    In the claim ledger

  39. ^Until the first door, the caps on missile and energy drops are 39 when the game starts in Brinstar, because SamusInit stores the mirroring value $27 into 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.

    In the claim ledger

  40. ^ 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 RandomNumber1 again, 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.

    In the claim ledger

  41. ^ 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 $89 balls, and 16 nothing; index 3, whose byte in the table LF74B reads ($968B + data index, kept in $00) has bit 0 clear, gives 106 nothing and no $89.Verified“Metroid (USA)” · nmikstas/metroid-disassemblyrevised
    How 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.

    In the claim ledger

  42. ^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.

    In the claim ledger

  43. ^ abSamus's health can't exceed 99.9 units above her tanks, 100t+99.9 units for t 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.

    In the claim ledger

  44. ^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.

    In the claim ledger

Sources

Code

Secondary