On this page
  1. Cartridge
  2. Related
  3. Mechanics
  4. References
  5. Sources

The atlas reads Contra’s US NES release through vermiceli’s annotated ca65 disassembly, which grew from Trax’s earlier one. Built with the ROM’s assets, it matches the ROM’s PRG byte for byte. Labels and comments quoted here are theirs. Nothing has been measured in the ROM yet.

Each falcon is a weapon pickup. M, F, S, and L replace the gun, R adds rapid fire, B makes the player invincible for a while, and the falcon destroys the enemies on screen; a death returns the default gun1. The Konami code, entered while the title scrolls in, raises the starting lives from 3 to 30 lives (29 in reserve), against 32,2. A game has 3 continues3, and an extra life comes at 20,000 points and every 30,000 after4.

Contra keeps a rank. Random soldiers come faster with each loop of the game cleared, counting up to 3, and with the strongest gun either player holds5. The screen moves only when a player pushes it; on the boss’s screen it scrolls on its own to reveal the boss6.

A jump rises at 4.06 px/frame ($FB.$F0)7 and gravity takes 0.137 px/frame² off on every frame, whatever the buttons do7.

Cartridge

Interactive: rom-parts (needs JavaScript)

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

Mechanics

MechanicKindHow
Weapon pickupReplacefalcons carry M, F, S, L, R, B, or a screen-clearing falcon; a new gun drops rapid fire unless it's the one held
Cheat codeOn the titleUp Up Down Down Left Right Left Right B A on the title screen: 30 lives
ContinueLimited3 lives and 3 continues a game
Extra lifeRepeatingat 20,000 points and every 30,000 after, up to 99 lives
RankLoops and gunsrandom soldiers come faster with each loop cleared, up to 3, and with the strongest gun held
Auto-scrollPlayer-pushedplayers push the screen past X 128; it scrolls on its own only to reveal a boss
JumpOne speed4.06 px/frame outdoors, 3.44 in the base
GravityOne gravity0.137 px/frame² on every airborne frame
Variable jump heightNonethe jump routine never reads A
Every claim about this game, by concept (8)
generalContra'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
Auto-scrollOn the side-scrolling levels of the US NES Contra the screen moves only while a player passes X 128 (176 when two are playing), by that player's speed, and not while the other player is left of X 33; on the boss's screen it takes over and scrolls on its own to reveal the boss.Code
Cheat codeWhile the title screen scrolls in, the US NES Contra compares each new d-pad, A, or B press with a ten-entry table, Up Up Down Down Left Right Left Right B A. A wrong press sets the count to $FF, which stops the check until the title starts over. The full sequence sets a flag, and a new game then gives each player 29 lives in reserve instead of 2: 30 lives instead of 3.Code
ContinueA new game of the US NES Contra gives each player 2 lives in reserve (3 in all) and the game 3 continues. A death with none in reserve puts that player in game over, and each game over spends a continue; with none left, the CONTINUE/END choice is skipped.Code
Extra lifeThe US NES Contra awards its first extra life at 20,000 points and each later one 30,000 points past the last threshold, and caps lives at 99.Code
JumpA jump in the US NES Contra sets the 16-bit vertical speed to $FB.$F0, a rise of 4.06 px/frame ($FC.$90, 3.44 px/frame, in the indoor base levels), and the jump routine adds $23 to it every airborne frame, 0.137 px/frame², without reading a button: the jump's height doesn't depend on holding A.Code
RankEach completed loop of the US NES Contra and the strongest gun either player holds both shorten the timer between random soldiers: from the level's start value (144 on level 1), 40 comes off per loop, counting up to 3, and 5 per point of weapon strength (S 3, M and L 2, F 1, the default 0).Code
Weapon pickupTouching a weapon item in the US NES Contra adds 1,000 points and dispatches on the item's type: R sets the rapid-fire bit, M, F, S, and L replace the gun (keeping rapid fire only when the new gun is the one held), B starts an invincibility timer at $80 ($90 on level 7), and the falcon destroys the enemies on screen. A death resets the gun to the default.Code

References

  1. ^Touching a weapon item in the US NES Contra adds 1,000 points and dispatches on the item's type: R sets the rapid-fire bit, M, F, S, and L replace the gun (keeping rapid fire only when the new gun is the one held), B starts an invincibility timer at $80 ($90 on level 7), and the falcon destroys the enemies on screen. A death resets the gun to the default.Codevermiceli/nes-contra-us
    How we know
    6884pick_up_weapon_item:
    ⋮
    6886    lda #$0a                     ; a = #$0a
    6887    sta $00                      ; set score to add to player as #$0a (1,000 points)
    ⋮
    6895    lda ENEMY_ATTRIBUTES,y       ; get weapon item attributes
    6896    and #$07                     ; keep bits 0-2 (attributes)
    6897    beq @set_rapid_flag          ; set rapid flag for weapon if attribute is #$00
    6898    cmp #$05                     ; check for b weapon (barrier). Gives invincibility
    6899    bcc @compare_and_set_weapon  ; branch if less than #$05 (MFSL)
    6900    beq @set_invincibility_timer ; branch for barrier weapon
    6901    jsr destroy_all_enemies      ; falcon weapon effect - destroy all enemies
    ⋮
    6912@set_invincibility_timer:
    6913    lda #$80          ; set duration of the b weapon effect
    ⋮
    
    src/bank7.asm lines 6884–6955 at 687d651 · upstream ↗
    5150init_player_dec_num_lives:
    5151    jsr init_player_and_weapon ; initialize player variables to 0
    
    src/bank7.asm lines 5150–5151 at 687d651 · upstream ↗
    5338init_player_and_weapon:
    5339    lda #$00                ; a = #$00
    5340    sta P1_CURRENT_WEAPON,x ; reset current player's weapon
    
    src/bank7.asm lines 5338–5340 at 687d651 · 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

  2. ^ abWhile the title screen scrolls in, the US NES Contra compares each new d-pad, A, or B press with a ten-entry table, Up Up Down Down Left Right Left Right B A. A wrong press sets the count to $FF, which stops the check until the title starts over. The full sequence sets a flag, and a new game then gives each player 29 lives in reserve instead of 2: 30 lives instead of 3.Codevermiceli/nes-contra-us
    How we know
    750game_routine_01:
    751    jsr konami_input_check                   ; check if current input is part of Konami code (30-lives code)
    
    src/bank7.asm lines 749–752 at 687d651 · upstream ↗
    930konami_input_check:
    931    ldy KONAMI_CODE_NUM_CORRECT    ; load the number of successful inputs of Konami code
    932    bmi konami_code_exit           ; if #$ff (invalid Konami input), exit
    933    lda CONTROLLER_STATE_DIFF      ; buttons pressed (only care on input change so held button doesn't affect code)
    934    and #$cf                       ; only care about input from d-pad and A/B buttons (not select nor start)
    935    beq konami_code_exit           ; if no input detected, exit
    936    cmp konami_code_lookup_table,y ; compare with Konami code sequence at index y
    937    beq konami_input_index_correct ; on success, goto konami_input_index_correct
    938    lda #$ff                       ; incorrect sequence for Konami code
    939    sta KONAMI_CODE_NUM_CORRECT    ; since incorrect set KONAMI_CODE_NUM_CORRECT (number of successful inputs) to $ff
    ⋮
    945    cpy #$0a                    ; Konami code is 10 inputs, compare against how many successfully entered
    946    bcc konami_code_exit        ; Konami code not yet fully entered, exit
    ⋮
    
    src/bank7.asm lines 930–955 at 687d651 · upstream ↗
    716game_routine_00:
    ⋮
    720    sty KONAMI_CODE_NUM_CORRECT     ; initialize konami check to #$00 (see konami_input_check)
    
    src/bank7.asm lines 716–720 at 687d651 · upstream ↗
    1165init_player_lives:
    1166    lda #$02                  ; start off with #$02 lives
    1167    ldy KONAMI_CODE_STATUS    ; 30-lives code switch ($01 = code activated)
    1168    beq init_player_num_lives ; if KONAMI_CODE_STATUS is not set, then just set 2 lives
    1169    lda #$1d                  ; KONAMI_CODE_STATUS active so set lives to #$1d (29 decimal)
    ⋮
    1174    sta P1_NUM_LIVES,x          ; if X is 1, then set P2_NUM_LIVES to accumulator (a is either #$02 or #$1d)
    
    src/bank7.asm lines 1165–1174 at 687d651 · 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

  3. ^A new game of the US NES Contra gives each player 2 lives in reserve (3 in all) and the game 3 continues. A death with none in reserve puts that player in game over, and each game over spends a continue; with none left, the CONTINUE/END choice is skipped.Codevermiceli/nes-contra-us
    How we know
    1143init_score_player_lives:
    ⋮
    1146    lda #$03           ; a = #$03
    1147    sta NUM_CONTINUES  ; continues remaining
    
    src/bank7.asm lines 1143–1147 at 687d651 · upstream ↗
    1165init_player_lives:
    1166    lda #$02                  ; start off with #$02 lives
    
    src/bank7.asm lines 1165–1166 at 687d651 · upstream ↗
    5150init_player_dec_num_lives:
    ⋮
    5153    lda P1_NUM_LIVES,x         ; load player number of lives
    5154    beq @set_game_over         ; branch if no more lives
    5155    dec P1_NUM_LIVES,x         ; decrement player number of lives
    ⋮
    5158@set_game_over:
    5159    lda #$01                  ; a = #$01
    5160    sta P1_GAME_OVER_STATUS,x ; game over state of player (1 = game over)
    
    src/bank7.asm lines 5150–5161 at 687d651 · upstream ↗
    3289show_game_over_screen:
    ⋮
    3301    dec NUM_CONTINUES                         ; subtract from number of continues remaining
    3302    bmi @no_continues_remaining               ; if run out of continues, jump
    3303    lda #$0e                                  ; a = #$0e (continue  end)
    
    src/bank7.asm lines 3289–3307 at 687d651 · 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

  4. ^The US NES Contra awards its first extra life at 20,000 points and each later one 30,000 points past the last threshold, and caps lives at 99.Codevermiceli/nes-contra-us
    How we know
    1177    lda #$c8                    ; set default high score. #$c8 is 200 decimal
    1178    sta EXTRA_LIFE_SCORE_LOW    ; starting score for extra life (20,000)
    
    src/bank7.asm lines 1177–1178 at 687d651 · upstream ↗
    1283; every time an extra life is obtained, #$12c is added to points required to get next extra life (300 decimal, 30,000 in game score)
    ⋮
    1291    lda #$2c                    ; a = #$2c (12c = 300 decimal) (high byte will be set to #$01 a few lines down)
    ⋮
    1294    adc EXTRA_LIFE_SCORE_LOW,y  ; add 300 decimal to low byte of score required to get extra life
    
    src/bank7.asm lines 1283–1294 at 687d651 · upstream ↗
    1310    inc P1_NUM_LIVES,x          ; if X is $01, then set P2 number of lives
    1311    lda P1_NUM_LIVES,x          ; load incremented number into memory
    1312    cmp #$63                    ; compare to #$63 (99 decimal)
    1313    bcc @set_num_lives          ; can't have more than 99 lives
    1314    lda #$63                    ; a = #$63 (63 = 99 decimal)
    
    src/bank7.asm lines 1310–1314 at 687d651 · 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

  5. ^Each completed loop of the US NES Contra and the strongest gun either player holds both shorten the timer between random soldiers: from the level's start value (144 on level 1), 40 comes off per loop, counting up to 3, and 5 per point of weapon strength (S 3, M and L 2, F 1, the default 0).Codevermiceli/nes-contra-us
    How we know
    1736adjust_generation_timer:
    1737    jsr get_soldier_gen_timer_for_lvl
    1738    sta SOLDIER_GENERATION_TIMER      ; soldier generation timer
    1739    ldy GAME_COMPLETION_COUNT         ; load the number of times the game has been completed
    1740    beq @adjust_timer_for_weapon      ; branch if haven't yet beat the game any times
    1741    tya
    1742    cmp #$04                          ; compare GAME_COMPLETION_COUNT to #$04
    1743    bcc @continue                     ; branch if GAME_COMPLETION_COUNT < #$04
    1744    ldy #$03                          ; maximum multiplier of increase chance for generating soldier is #$03
    ⋮
    1747    lda #$28          ; lower soldier generation timer
    ⋮
    1752    ldy PLAYER_WEAPON_STRENGTH ; load player weapon strength (damage strength)
    1753    beq @exit                  ; don't generate if player's weapon strength is #$00
    ⋮
    
    src/bank2.asm lines 1736–1779 at 687d651 · upstream ↗
    3270    inc GAME_COMPLETION_COUNT       ; increment game completion count, used mainly to increase enemy difficulty every play-through
    
    src/bank7.asm line 3270 at 687d651 · upstream ↗
    3920set_frame_scroll_weapon_strength:
    ⋮
    3926    sta PLAYER_WEAPON_STRENGTH ; clear player weapon strength
    
    src/bank7.asm lines 3920–3926 at 687d651 · upstream ↗
    4096set_player_weapon_strength:
    ⋮
    4102    lda weapon_strength,y         ; load how strong the weapon is
    4103    cmp PLAYER_WEAPON_STRENGTH    ; compare against current weapon strength
    4104    bcc @exit                     ; exit and do not lower player's current weapon strength
    4105    sta PLAYER_WEAPON_STRENGTH    ; store current weapon strength code (#$00-#$03)
    ⋮
    4111weapon_strength:
    4112    .byte $00 ; Regular = Weak
    4113    .byte $02 ; M = Strong
    4114    .byte $01 ; F = Medium
    4115    .byte $03 ; S = Very Strong
    4116    .byte $02 ; L = Strong
    
    src/bank7.asm lines 4096–4116 at 687d651 · 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. ^On the side-scrolling levels of the US NES Contra the screen moves only while a player passes X 128 (176 when two are playing), by that player's speed, and not while the other player is left of X 33; on the boss's screen it takes over and scrolls on its own to reveal the boss.Codevermiceli/nes-contra-us
    How we know
    5164set_frame_scroll_if_appropriate:
    ⋮
    5178    lda SPRITE_X_POS,x                     ; load player X position
    5179    cmp horizontal_scroll_point_tbl,y      ; load horizontal scroll point position
    5180    bcc @exit                              ; if not yet reached point to initiate horizontal scroll, exit
    5181    cpy #$02                               ; see if both players are active
    5182    bne @set_horizontal_level_frame_scroll ; if both players aren't active, begin setting scroll
    ⋮
    5186    lda SPRITE_X_POS,y                     ; load other player's X position
    5187    cmp #$21                               ; compare other player to left edge of screen
    5188    bcc @stop_player_x_velocity            ; don't scroll if other player is preventing it by being too far to the left
    ⋮
    5196    jsr set_boss_auto_scroll            ; starts the auto scroll to reveal boss if at right screen, otherwise do nothing
    ⋮
    5202    lda PLAYER_X_VELOCITY,x             ; load player X velocity
    5203    adc #$00                            ; incorporate any velocity overflow from INDOOR_TRANSITION_X_ACCUM
    ⋮
    
    src/bank7.asm lines 5164–5208 at 687d651 · upstream ↗
    5228horizontal_scroll_point_tbl:
    5229    .byte $80,$80,$b0
    
    src/bank7.asm lines 5228–5229 at 687d651 · upstream ↗
    6552set_boss_auto_scroll:
    ⋮
    6557    cmp LEVEL_SCREEN_NUMBER        ; screen number
    6558    bne @exit                      ; exit if not on the appropriate screen to start auto scroll
    ⋮
    6563    lda auto_scroll_timer_tbl,y    ; load the appropriate auto scroll timer for the level
    6564    sta AUTO_SCROLL_TIMER_00       ; start auto scroll to reveal the boss
    
    src/bank7.asm lines 6552–6566 at 687d651 · 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

  7. ^ abA jump in the US NES Contra sets the 16-bit vertical speed to $FB.$F0, a rise of 4.06 px/frame ($FC.$90, 3.44 px/frame, in the indoor base levels), and the jump routine adds $23 to it every airborne frame, 0.137 px/frame², without reading a button: the jump's height doesn't depend on holding A.Codevermiceli/nes-contra-us
    How we know
    4533set_jump_status_and_y_velocity:
    ⋮
    4545    lda LEVEL_LOCATION_TYPE            ; 0 = outdoor; 1 = indoor
    4546    lsr
    4547    lda #$fb                           ; a = #$fb (-5)
    4548    ldy #$f0                           ; y = #$f0 (.94)
    4549    bcc @set_y_velocity                ; branch if outdoor level (use Y velocity -5.94)
    4550    lda #$fc                           ; indoor level, (Y velocity -4.56), set fast velocity to #$fc (-4)
    4551    ldy #$90                           ; set fractional Y velocity to #$90 (.56)
    ⋮
    4554    sta PLAYER_Y_FAST_VELOCITY,x  ; set Y fast velocity to a
    4555    tya                           ; transfer fractional velocity to a
    4556    sta PLAYER_Y_FRACT_VELOCITY,x ; set Y fractional velocity to a
    
    src/bank7.asm lines 4533–4557 at 687d651 · upstream ↗
    4708handle_jump:
    4709    lda PLAYER_Y_FAST_VELOCITY,x ; load player Y fast velocity
    4710    bmi @check_collision_above   ; branch if player is still ascending
    ⋮
    4747@apply_gravity:
    4748    jsr apply_gravity                   ; increments Y fractional velocity by #$23 (applying gravity)
    ⋮
    4756@set_y_pos:
    4757    jsr player_jumping_set_y_pos ; set player Y position based on PLAYER_JUMP_COEFFICIENT and velocity
    
    src/bank7.asm lines 4708–4757 at 687d651 · upstream ↗
    5114player_jumping_set_y_pos:
    ⋮
    5123    lda PLAYER_JUMP_COEFFICIENT,x ; load player's jump modifier (alters height of jump)
    5124    clc                           ; clear carry in preparation for addition
    5125    adc PLAYER_Y_FRACT_VELOCITY,x ; add to player's fractional Y velocity
    5126    sta PLAYER_JUMP_COEFFICIENT,x ; update player's jump modifier (alters height of jump)
    5127    lda SPRITE_Y_POS,x            ; load player's Y position
    5128    adc PLAYER_Y_FAST_VELOCITY,x  ; add (subtract) the fast Y velocity (with jump coefficient overflow)
    
    src/bank7.asm lines 5113–5130 at 687d651 · upstream ↗
    5139apply_gravity:
    5140    clc
    5141    lda PLAYER_Y_FRACT_VELOCITY,x ; load player's Y fractional velocity
    5142    adc #$23                      ; add #$23 to Y fractional velocity (.1367 decimal)
    
    src/bank7.asm lines 5139–5147 at 687d651 · 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

Sources

Code