Skip to content
On This Day:

Nintendo Entertainment System · Source Code

Metroid Source Code

By SnowBro

Metroid · All hacks & resources →

About this project

Disassembled and commented Metroid source code. It can even be reassembled with X816.

Original game requirements

See the release instructions.

Creator credits

SnowBro
Listed creator

RELEASE 1.0

Readme & instructions

[367]m1source.txt

; METROID source code ; MAIN PROGRAMMERS ; HAI YUKAMI ; ZARU SOBAJIMA ; GPZ SENGOKU ; N.SHIOTANI ; M.HOUDAI ; (C) 1986 NINTENDO ; Disassembled, reconstructed and commented ; by SnowBro [Kent Hansen] <kentmhan@online.no> ; A work in progress. ; Can be assembled with X816. .mem 8 .index 8 .base $C000 CodePtr EQU $0C JoyFirst EQU $12 JoyStatus EQU $14 JoyRetrig EQU $16 RetrigDelay EQU $18 NMIStatus EQU $1A PPUDataPending EQU $1B PalDataPending EQU $1C GameMode EQU $1D ; 0 = Game is playing ; 1 = At title / password screen MainRoutine EQU $1E TitleRoutine EQU $1F NextRoutine EQU $20 CurrentBank EQU $23 SwitchPending EQU $24 TimerDelay EQU $29…

Read moreRead less
; -------------------
; METROID source code
; -------------------
; MAIN PROGRAMMERS
;     HAI YUKAMI
;   ZARU SOBAJIMA
;    GPZ SENGOKU
;    N.SHIOTANI
;     M.HOUDAI
; (C) 1986 NINTENDO
;
; Disassembled, reconstructed and commented
; by SnowBro [Kent Hansen] <kentmhan@online.no>
; A work in progress.
; Can be assembled with X816.

.mem 8
.index 8
.base $C000

;-------------------------------[ Defines ]-----------------------------------

CodePtr                 EQU     $0C
JoyFirst                EQU     $12
JoyStatus               EQU     $14
JoyRetrig               EQU     $16
RetrigDelay             EQU     $18
NMIStatus               EQU     $1A
PPUDataPending          EQU     $1B
PalDataPending          EQU     $1C
GameMode                EQU     $1D     ; 0 = Game is playing
                                        ; 1 = At title / password screen
MainRoutine             EQU     $1E
TitleRoutine            EQU     $1F
NextRoutine             EQU     $20
CurrentBank             EQU     $23
SwitchPending           EQU     $24
TimerDelay              EQU     $29
Timer                   EQU     $2A
FrameCount              EQU     $2D     ; number of CPU frames executed
                                        ; (overflows every 256 frames)
GamePaused              EQU     $31
RoomPtr                 EQU     $33
StructPtr               EQU     $35

WRAMWorkPtr             EQU     $37
WRAMPtr                 EQU     $39
RoomPtrTable            EQU     $3B
StructPtrTable          EQU     $3D
MacroPtr                EQU     $3F
EnemyAnimPtr            EQU     $47
ScrollDir               EQU     $49     ; 0 = Up
                                        ; 1 = Down
                                        ; 2 = Left
                                        ; 3 = Right

PageIndex               EQU     $4B
SamusDir                EQU     $4D     ; 0 = Right
                                        ; 1 = Left
SamusDoorDir            EQU     $4E     ; direction Samus passed through door
MapPosY                 EQU     $4F
MapPosX                 EQU     $50
SamusScrX               EQU     $51
SamusScrY               EQU     $52
WalkSoundDelay          EQU     $53
IsSamus                 EQU     $55
DoorStatus              EQU     $56
DoorDelay               EQU     $59
RoomNumber              EQU     $5A
SpritePagePos           EQU     $5B
ObjectPal               EQU     $67
RoomPal                 EQU     $68
TempX                   EQU     $69
TempY                   EQU     $6A
SamusBlink              EQU     $70
PalToggle               EQU     $76
ScrollY                 EQU     $FC
ScrollX                 EQU     $FD
PPUCNT1ZP               EQU     $FE
PPUCNT0ZP               EQU     $FF
HealthLo                EQU     $0106   ; lower health digit in upper 4 bits
HealthHi                EQU     $0107   ; upper health digit in lower 4 bits
                                        ; # of full tanks in upper 4 bits
EndTimerLo              EQU     $010A
EndTimerHi              EQU     $010B
MissileToggle           EQU     $010E   ; 0 = fire bullets, 1 = fire missiles

SpriteRAM               EQU     $0200

ObjAction               EQU     $0300
ObjRadY                 EQU     $0301
ObjRadX                 EQU     $0302
AnimFrame               EQU     $0303
AnimDelay               EQU     $0304
AnimResetIndex          EQU     $0305
AnimIndex               EQU     $0306
SamusOnElevator         EQU     $0307   ; 1 = Samus is standing on elevator
ObjectHi                EQU     $030C
ObjectY                 EQU     $030D
ObjectX                 EQU     $030E

; Tile respawning

TileRoutine             EQU     $0500
TileAnimFrame           EQU     $0503
TileAnimDelay           EQU     $0504
TileAnimIndex           EQU     $0506
TileDelay               EQU     $0507
TileWRAMLo              EQU     $0508
TileWRAMHi              EQU     $0509
TileType                EQU     $050A

PPUStrIndex             EQU     $07A0
PPUDataString           EQU     $07A1

; bitmask defs used for SamusGear

gr_BOMBS                EQU     %00000001
gr_HIGHJUMP             EQU     %00000010
gr_LONGBEAM             EQU     %00000100
gr_SCREWATTACK          EQU     %00001000
gr_MARUMARI             EQU     %00010000
gr_VARIA                EQU     %00100000
gr_WAVEBEAM             EQU     %01000000
gr_ICEBEAM              EQU     %10000000

; Samus action handlers

sa_Stand                EQU     0
sa_Run                  EQU     1
sa_Jump                 EQU     2
sa_Roll                 EQU     3
sa_PntUp                EQU     4
sa_Door                 EQU     5
sa_PntJump              EQU     6
sa_Dead                 EQU     7
sa_Dead2                EQU     8
sa_Elevator             EQU     9

; Animations

an_SamusRun             EQU     $00
an_SamusFront           EQU     $04
an_SamusStand           EQU     $07
an_SamusJump            EQU     $0C
an_SamusSalto           EQU     $0E
an_SamusRunJump         EQU     $13
an_SamusRoll            EQU     $16
an_Bullet               EQU     $1B
an_SamusFireJump        EQU     $20
an_SamusFireRun         EQU     $22
an_SamusPntUp           EQU     $27
an_Explode              EQU     $32
an_SamusJumpPntUp       EQU     $35
an_SamusRunPntUp        EQU     $37
an_WaveBeam             EQU     $7D
an_BombTick             EQU     $7F
an_BombExplode          EQU     $82
an_MissileLeft          EQU     $8B
an_MissileRight         EQU     $8D
an_MissileExplode       EQU     $91

; Weapon action handlers

wa_RegularBeam          EQU     1
wa_WaveBeam             EQU     2
wa_IceBeam              EQU     3
wa_BulletExplode        EQU     4
wa_LayBomb              EQU     8
wa_BombCount            EQU     9
wa_BombExplode          EQU     10
wa_Missile              EQU     11

TankCount               EQU     $6877   ; number of energy tanks
SamusGear               EQU     $6878
MissileCount            EQU     $6879
MaxMissiles             EQU     $687A
SamusAge                EQU     $687D
JustInBailey            EQU     $69B3   ; 1 = Samus is without suit

PPUControl0             EQU     $2000
PPUControl1             EQU     $2001
PPUStatus               EQU     $2002
SPRAddress              EQU     $2003
SPRIOReg                EQU     $2004
PPUScroll               EQU     $2005
PPUAddress              EQU     $2006
PPUIOReg                EQU     $2007
SPRDMAReg               EQU     $4014
CPUJoyPad               EQU     $4016

MMC1Reg0                EQU     $8000
MMC1Reg1                EQU     $A000
MMC1Reg2                EQU     $C000
MMC1Reg3                EQU     $E000

FramePtrTable           EQU     $860B
PlacePtrTable           EQU     $86DF

; Joy pad defs

btn_RIGHT               EQU     %00000001
btn_LEFT                EQU     %00000010
btn_DOWN                EQU     %00000100
btn_UP                  EQU     %00001000
btn_START               EQU     %00010000
btn_SELECT              EQU     %00100000
btn_B                   EQU     %01000000       ; FIRE
btn_A                   EQU     %10000000       ; JUMP

modePlay                EQU     0
modeTitle               EQU     1

;----------------------------------[ Code ]-----------------------------------

; The following routine's purpose is unclear...

LC000:  txa
        pha
        ldx #$05
-       lda $2E
        clc
        adc #$05
        sta $2E
        lda $2F
        clc
        adc #$13
        sta $2F
        dex
        bne -
        pla
        tax
        lda $2E
        rts

;-----------------------------------------------------------------------------

        Startup:
        lda #$00
        sta MMC1Reg1    ; clear bit 0
        sta MMC1Reg1    ; clear bit 1
        sta MMC1Reg1    ; clear bit 2
        sta MMC1Reg1    ; clear bit 3
        sta MMC1Reg1    ; clear bit 4
        sta MMC1Reg2    ; clear bit 0
        sta MMC1Reg2    ; clear bit 1
        sta MMC1Reg2    ; clear bit 2
        sta MMC1Reg2    ; clear bit 3
        sta MMC1Reg2    ; clear bit 4
        jsr MMCWriteReg3   ; swap to PRG bank #0 at $8000
        dex             ; X = $FF
        txs             ; S points to end of stack page

; Clear RAM at $000-$7FF

        ldy #$07        ; high byte of start address
        sty $01
        ldy #$00        ; low byte of start address
        sty $00         ; $0000 = #$0700
        tya             ; A = 0
-       sta ($00),y     ; clear address
        iny
        bne -           ; repeat for entire page
        dec $01         ; decrement high byte of address
        bmi +           ; if $01 < 0, all pages are cleared
        ldx $01
        cpx #$01
        bne -

; Clear WRAM at $6000-$7FFF

+       ldy #$7F
        sty $01
        ldy #$00
        sty $00         ; $0000 points to $7F00
        tya             ; A = 0
-       sta ($00),y
        iny
        bne -
        dec $01
        ldx $01
        cpx #$60        ; is address < $6000?
        bcs -           ; if not, do another page

        lda #$0E
        sta $25
        lda #$00
        sta $28
        ldy #$00
        sty ScrollX     ; ScrollX = 0
        sty ScrollY     ; ScrollY = 0
        sty PPUScroll   ; clear hardware scroll x
        sty PPUScroll   ; clear hardware scroll y
        iny             ; Y = 1
        sty GameMode    ; title screen mode
        jsr ClearNameTables
        jsr EraseAllSprites

        lda #%10010000  ; NMI = enabled
                        ; Sprite size = 8x8
                        ; BG pattern table address = $1000
                        ; SPR pattern table address = $0000
                        ; PPU address increment = 1
                        ; Name table address = $2000
        sta PPUControl0
        sta PPUCNT0ZP

        lda #%00000010  ; Sprites visible = no
                        ; Background visible = no
                        ; Sprite clipping = yes
                        ; Background clipping = no
                        ; Display type = color
        sta PPUCNT1ZP

        lda #$47
        sta $FA
        jsr LC4B2
        lda #$00
        sta $4011       ; PCM volume = 0
        lda #$0F
        sta $4015       ; enable sound channel 0,1,2,3
        ldy #$00
        sty TitleRoutine
        sty MainRoutine
        lda #$11
        sta $2E
        lda #$FF
        sta $2F
        iny             ; Y = 1
        sty SwitchPending
        jsr CheckSwitch   ; switch ROM bank #0 into $8000-$BFFF
        bne WaitNMIEnd  ; branch always

;--------------------------------[ Main loop ]--------------------------------

        MainLoop:
        jsr CheckSwitch
        jsr UpdateTimer
        jsr GoMainRoutine
        inc FrameCount
        lda #$00
        sta NMIStatus
        WaitNMIEnd:
        tay
        lda NMIStatus
        bne +           ; if nonzero NMI has ended
        jmp WaitNMIEnd  ; else, keep waiting
+       jsr LC000
        jmp MainLoop

;-------------------------[ Non-Maskable Interrupt ]--------------------------

        NMI:
        php             ; not necessary! sloppy coding indeed ;-)
        pha             ; save A
        txa
        pha             ; save X
        tya
        pha             ; save Y
        lda #$00
        sta SPRAddress  ; Sprite RAM address = 0
        lda #$02
        sta SPRDMAReg   ; transfer page 2 ($200-$2FF) to Sprite RAM
        lda NMIStatus
        bne ++          ; skip if the frame couldn't finish in time
        lda GameMode
        beq +           ; branch if mode=Play
        jsr $9A07
+       jsr CheckPalWrite ; check if palette data pending
        jsr CheckPPUWrite ; check if any other PPU data pending
        jsr WritePPUCtrl ; update $2000 & $2001
        jsr WriteScroll ; update h/v scroll reg
        jsr ReadJoyPads ; read both joypads
++      jsr $B3B4       ; music player
        jsr UpdateAge   ; update Samus' age
        ldy #$01        ; NMI = finished
        sty NMIStatus
        pla
        tay             ; restore Y
        pla
        tax             ; restore X
        pla             ; restore A
        plp
        rti

;-----------------------------------------------------------------------------

; GoMainRoutine
; =============
; This is where the real code of each frame is executed.
; MainRoutine or TitleRoutine (Depending on the value of GameMode)
; is used as an index into a code pointer table, and this routine
; is executed.

        GoMainRoutine:
        lda GameMode
        beq +           ; branch if mode=Play
        jmp $8000       ; jump to $8000, where a routine similar to the one
                        ; below is executed, only using TitleRoutine instead
                        ; of MainRoutine as index into a jump table.
+       lda JoyFirst
        and #btn_START  ; has START been pressed?
        beq +++         ; if not, execute current routine as normal
        lda MainRoutine
        cmp #$03        ; game engine running?
        beq +           ; if yes, switch to routine #5 (pause game)
        cmp #$05        ; game paused?
        bne +++         ; if not routine #5 either, don't give a damn about START being pressed
        lda #$03        ; otherwise, switch to routine #3 (game engine)
        bne ++          ; branch always
+       lda #$05        ; switch to pause routine
++      sta MainRoutine
        lda GamePaused
        eor #$01
        sta GamePaused
        jsr PauseMusic
+++     lda MainRoutine
        jsr GoRoutine

; Pointer table to code

        .dw AreaInit    ; area init
        .dw LC81D       ; more area init
        .dw SamusInit   ; samus init
        .dw GameEngine  ; game engine
        .dw EndGame     ; game over / password display
        .dw PauseMode   ; pause
        .dw GoPassword  ; direct password display
        .dw LC155       ; just advances to next routine in table
        .dw SamusIntro  ; intro
        .dw WaitTimer   ; delay

LC155:  inc MainRoutine
        rts

; ClearNameTables
; ===============

        ClearNameTables:
        jsr +++
        lda GameMode
        beq +           ; branch if mode=Play
        lda TitleRoutine
        cmp #$1D
        beq ++
+       lda #$02
        bne +
++      lda #$03
        bne +
+++     lda #$01
+       sta $01         ; name table to fill
        lda #$FF
        sta $00         ; value to fill with
        ClearNameTable:
        ldx PPUStatus
        lda PPUCNT0ZP
        and #$FB        ; PPU increment = 1
        sta PPUCNT0ZP
        sta PPUControl0
        ldx $01
        dex
        lda HiPPUTable,x     ; get high PPU address
        sta PPUAddress
        lda #$00
        sta PPUAddress
        ldx #$04        ; prepare to fill 4 pages
        ldy #$00        ; reset page address
        lda $00         ; fill-value
-       sta PPUIOReg
        dey
        bne -
        dex
        bne -
        rts

HiPPUTable      .db $20,$24,$28,$2C

        EraseAllSprites:
        ldy #$02
        sty $01
        ldy #$00
        sty $00
        ldy #$00
        lda #$F0
-       sta ($00),y
        iny
        bne -
        lda GameMode
        beq Exit1       ; branch if mode=Play
        jmp $988A
Exit1:  rts

LC1BC:  ldy #$02
        sty $01
        ldy #$00
        sty $00         ; ($00) = $0200 (sprite page)
        ldy #$5F        ; prepare to clear RAM $0200-$025F
        lda #$F4
-       sta ($00),y
        dey
        bpl -
        lda GameMode
        beq Exit1       ; branch if mode=Play
        jmp $988A

; clear RAM $33-$DF

        ClearRAM_33_DF:
        ldx #$33
        lda #$00
-       sta $00,x
        inx
        cpx #$E0
        bcc -
        rts

        CheckPalWrite:
        lda GameMode
        beq +           ; branch if mode=Play
        lda TitleRoutine
        cmp #$1D
        bcc +
        jmp $9F54
+       ldy PalDataPending
        bne ++          ; if non-zero, need to update palette
        lda GameMode
        beq +           ; branch if mode=Play
        lda TitleRoutine
        cmp #$15
        bcs +
        jmp $8AC7
+       rts

; write palette data to PPU

++      dey             ; pal # = PalDataPending - 1
        tya
        asl a           ; * 2, each pal data ptr is 2 bytes (16-bit)
        tay
        ldx $9560,y     ; X = low byte of PPU data pointer
        lda $9561,y     ; A = high byte of PPU data pointer
        tay
        lda #$00
        sta PalDataPending
        stx $00
        sty $01         ; $0000 = pointer to PPU data string
        jmp ProcessPPUString   ; write data string to PPU

; Read joypads

        ReadJoyPads:
        ldx #$00
        stx $01
        jsr ReadOnePad
        inx
        inc $01

        ReadOnePad:
        ldy #$01
        sty CPUJoyPad   ; reset strobe
        dey
        sty CPUJoyPad   ; clear strobe
        ldy #$08        ; do 8 buttons
-       pha
        lda CPUJoyPad,x ; read button status
        sta $00
        lsr a
        ora $00
        lsr a
        pla
        rol a
        dey             ; done 8 buttons yet?
        bne -           ; if not, do another
        ldx $01         ; joypad # (0..1)
        ldy JoyStatus,x ; get joystat of previous refresh
        sty $00         ; store
        sta JoyStatus,x ; store new joystat
        eor $00
        beq +           ; branch if no buttons changed
        lda $00
        and #$BF
        sta $00
        eor JoyStatus,x
+       and JoyStatus,x
        sta JoyFirst,x
        sta JoyRetrig,x
        ldy #$20
        lda JoyStatus,x
        cmp $00
        bne +
        dec RetrigDelay,x
        bne ++
        sta JoyRetrig,x
        ldy #$08
+       sty RetrigDelay,x
++      rts

; UpdateTimer
; ===========
; This routine is used for timing - or for waiting around, rather.
; TimerDelay is decremented every frame. When it hits zero, $2A, $2B and $2C are
; decremented if they aren't already zero. The program can then check
; these variables (it usually just checks $2C) to determine when it's time
; to "move on". This is used for the various sequences of the intro screen,
; when the game is started, when Samus takes a special item, and when GAME
; OVER is displayed, to mention a few examples.

        UpdateTimer:
        ldx #$01
        dec TimerDelay
        bpl DecTimer
        lda #$09
        sta TimerDelay
        ldx #$02
        DecTimer:
        lda Timer,x
        beq +           ; don't decrease if it's already zero
        dec Timer,x
+       dex
        bpl DecTimer
        rts

; GoRoutine
; =========
; This is an indirect jump routine. A is used as an index into a code
; pointer table, and the routine at that position is executed. The programmers
; always put the pointer table itself directly after the JSR to GoRoutine,
; meaning that its address can be popped from the stack... Man, this is
; clever. :)

        GoRoutine:
        asl a           ; * 2, each ptr is 2 bytes (16-bit)
        sty TempY       ; temp storage
        stx TempX       ; temp storage
        tay
        iny
        pla             ; lo byte of ptr table address
        sta CodePtr
        pla             ; hi byte of ptr table address
        sta CodePtr+1
        lda (CodePtr),y     ; lo byte of code ptr
        tax
        iny
        lda (CodePtr),y     ; hi byte of code ptr
        sta CodePtr+1
        stx CodePtr
        ldx TempX       ; restore X
        ldy TempY       ; restore Y
        jmp (CodePtr)

; Write to scroll registers

        WriteScroll:
        lda PPUStatus   ; reset scroll register flip/flop
        lda ScrollX
        sta PPUScroll
        lda ScrollY
        sta PPUScroll
        rts

; Add Y to pointer at $0000

        AddYToPtr00:
        tya
        clc
        adc $00
        sta $00
        bcc +
        inc $01
+       rts

; Add Y to pointer at $0002

LC2B3:  tya
        clc
        adc $02
        sta $02
        bcc +
        inc $03
+       rts

Adiv32: lsr a
Adiv16: lsr a
Adiv8:  lsr a
        lsr a
        lsr a
        rts

Amul32: asl a
Amul16: asl a
Amul8:  asl a
        asl a
        asl a
        rts

; CheckPPUWrite
; =============
; Checks if any data is waiting to be written to the PPU.

        CheckPPUWrite:
        lda PPUDataPending
        beq +           ; if zero no PPU data to write, exit
        lda #<PPUDataString
        sta $00
        lda #>PPUDataString
        sta $01         ; $0000 = ptr to PPU data string ($07A1)
        jsr ProcessPPUString    ; write it to PPU!
        lda #$00
        sta PPUStrIndex
        sta PPUDataString
        sta PPUDataPending
+       rts

        PPUWrite:
        sta PPUAddress  ; set high PPU address
        iny
        lda ($00),y
        sta PPUAddress  ; set low PPU address
        iny
        lda ($00),y     ; get data byte containing rep length & RLE status
        asl a           ; CF = PPU address increment (0 = 1, 1 = 32)
        jsr SetPPUInc   ; update PPUCtrl0 according to CF
        asl a           ; CF = bit 6 of byte at ($00),y (1 = RLE)
        lda ($00),y     ; get data byte again
        and #$3F        ; keep lower 6 bits as loop counter
        tax
        bcc PPUWriteLoop ; if CF not set, the data is not RLE
        iny             ; data is RLE, advance to data byte
        PPUWriteLoop:
        bcs +
        iny             ; only inc Y if data is not RLE
+       lda ($00),y     ; get data byte
        sta PPUIOReg    ; write to PPU
        dex             ; decrease loop counter
        bne PPUWriteLoop ; keep going until X=0
        iny
        jsr AddYToPtr00 ; point to next data chunk

; ProcessPPUString
; ================
; write data string at ($00) to PPU

        ProcessPPUString:
        ldx PPUStatus   ; reset PPU addr flip/flop
        ldy #$00
        lda ($00),y
        bne PPUWrite    ; if A is non-zero, PPU data string follows
        jmp WriteScroll ; otherwise we're done

; In: CF = desired PPU address increment (0 = 1, 1 = 32)
; Out: PPU control #0 ($2000) updated accordingly

        SetPPUInc:
        pha             ; preserve A
        lda PPUCNT0ZP
        ora #$04        ; PPU increment = 32
        bcs +           ; only if CF set
        and #$FB        ; else PPU increment = 1
+       sta PPUControl0
        sta PPUCNT0ZP
        pla             ; restore A
        rts

; erase blasted tile on nametable

LC328:  ldy #$01
        sty PPUDataPending      ; data pending = YES
        dey
        lda ($02),y
        and #$0F
        sta $05         ; # of tiles horizontally
        lda ($02),y
        jsr Adiv16      ; / 16
        sta $04         ; # of tiles vertically
        ldx PPUStrIndex
--      lda $01
        jsr WritePPUByte        ; write PPU high address to $07A1
        lda $00
        jsr WritePPUByte        ; write PPU low address to $07A2
        lda $05         ; data length
        sta $06
        jsr WritePPUByte        ; write data length to $07A3
-       iny
        lda ($02),y     ; get data byte
        jsr WritePPUByte        ; write it to $07A1,x , inc x
        dec $06
        bne -
        stx PPUStrIndex
        sty $06
        ldy #$20
        jsr AddYToPtr00
        ldy $06
        dec $04
        bne --
        jsr EndPPUString

        WritePPUByte:
        sta PPUDataString,x
        NextPPUByte:
        inx
        cpx #$4F
        bcc +
        ldx PPUStrIndex
        EndPPUString:
        lda #$00
        sta PPUDataString,x
        pla
        pla
+       rts

LC37E:  ldy #$01
        sty PPUDataPending
        dey
        beq ++          ; branch always
-       sta $04
        lda $01
        jsr WritePPUByte
        lda $00
        jsr WritePPUByte
        lda $04
        jsr LC3C6
        bit $04
        bvc LC39B
        iny
LC39B:  bit $04
        bvs +
        iny
+       lda ($02),y
        jsr WritePPUByte
        sty $06
        ldy #$01
        bit $04
        bpl +
        ldy #$20
+       jsr AddYToPtr00
        ldy $06
        dec $05
        bne LC39B
        stx PPUStrIndex
        iny
++      ldx PPUStrIndex
        lda ($02),y
        bne -
        jsr EndPPUString

LC3C6:  sta $04
        and #$BF
        sta PPUDataString,x
        and #$3F
        sta $05
        jmp NextPPUByte

; GetAbsolute
; ===========

        GetAbsolute:
        eor #$FF
        clc
        adc #$01
        rts

LC3DA:  jsr LC41D
        adc $01
        cmp #$0A
        bcc +
        adc #$05
+       clc
        adc $02
        sta $02
        lda $03
        and #$F0
        adc $02
        bcc +
-       adc #$5F
        sec
        rts
+       cmp #$A0
        bcs -
        rts

LC3FB:  jsr LC41D
        sbc $01
        sta $01
        bcs +
        adc #$0A
        sta $01
        lda $02
        adc #$0F
        sta $02
+       lda $03
        and #$F0
        sec
        sbc $02
        bcs +
        adc #$A0
        clc
+       ora $01
        rts

LC41D:  pha
        and #$0F
        sta $01
        pla
        and #$F0
        sta $02
        lda $03
        and #$0F
        rts

; WaitNMIPass
; ===========
; Wait for the NMI to end.

        WaitNMIPass:    ; $C42C
        jsr ClearNMIStat
-       lda NMIStatus
        beq -
        rts

        ClearNMIStat:
        lda #$00
        sta NMIStatus
        rts

; ScreenOff
; =========

        ScreenOff:
        lda PPUCNT1ZP
        and #$E7        ; BG & SPR visibility = off
--      sta PPUCNT1ZP
        WaitNMIPass_:
        jsr ClearNMIStat
-       lda NMIStatus
        beq -
        rts

; ScreenOn
; ========

        ScreenOn:
        lda PPUCNT1ZP
        ora #$1E        ; BG & SPR visibility = on
        bne --          ; branch always

; WritePPUCtrl
; ============
; Update the actual PPU control registers.

        WritePPUCtrl:
        lda PPUCNT0ZP
        sta PPUControl0
        lda PPUCNT1ZP
        sta PPUControl1
        lda $FA
        jsr LC4D9
        ExitSub:
        rts

; ScreenNmiOff
; ============
; Turn off both screen and NMI.

        ScreenNmiOff:
        lda PPUCNT1ZP
        and #$E7        ; BG & SPR visibility = off
        jsr --
        lda PPUCNT0ZP
        and #$7F        ; NMI = off
        sta PPUCNT0ZP
        sta PPUControl0
        rts

LC46E:  lda PPUCNT0ZP   ; Nmi&ScreenOn
        ora #$80
        sta PPUCNT0ZP
        sta PPUControl0
        lda PPUCNT1ZP
        ora #$1E
        bne --

LC47D:  lda PPUCNT0ZP
        and #$7B
-       sta PPUControl0
        sta PPUCNT0ZP
        rts

; NmiOn
; =====

        NmiOn:
        lda PPUStatus
        and #$80
        bne NmiOn
        lda PPUCNT0ZP
        ora #$80
        bne -           ; branch always

; WaitTimer
; =========

        WaitTimer:
        lda Timer+2
        bne +       ; exit if timer hasn't hit zero yet
        lda NextRoutine
        cmp #$04    ; EndGame
        beq SetMainRoutine
        cmp #$06    ; GoPassword
        beq SetMainRoutine
        jsr LD92C       ; start area music
        lda NextRoutine
        SetMainRoutine:
        sta MainRoutine
+       rts

; SetTimer
; ========

        SetTimer:
        sta Timer+2
        stx NextRoutine
        lda #$09        ; WaitTimer
        bne SetMainRoutine   ; branch always

LC4B2:  nop
        nop
        lda #$47
LC4B6:  lsr a
        lsr a
        lsr a
        and #$01
        sta $00
        lda $25
        and #$FE
        ora $00
        sta $25
        sta MMC1Reg0
        lsr a
        sta MMC1Reg0
        lsr a
        sta MMC1Reg0
        lsr a
        sta MMC1Reg0
        lsr a
        sta MMC1Reg0
        rts

LC4D9:  lda $FA
        jmp LC4B6

; CheckSwitch
; ===========
; This is how the bank switching works... Every frame, the routine below
; is executed. First, it checks the value of SwitchPending. If it is zero,
; the routine will simply exit. If it is non-zero, it means that a bank
; switch has been issued, and must be performed. SwitchPending then contains
; the bank to switch to, plus one.

        CheckSwitch:
        ldy SwitchPending
        beq +           ; exit if zero (no bank switch issued)
                        ; otherwise, Y contains bank# + 1
        jsr SwitchOK    ; perform bank switch
        jmp GoBankInit

        SwitchOK:
        lda #$00
        sta SwitchPending   ; reset (so that the bank switch won't be
                        ; performed every succeeding frame too)
        dey             ; Y now contains the bank to switch to
        sty CurrentBank

        ROMSwitch:
        tya
        sta $00
        lda $28
        and #$18
        ora $00
        sta $28

        MMCWriteReg3:
        sta MMC1Reg3       ; write bit 0 of ROM bank #
        lsr a
        sta MMC1Reg3       ; write bit 1 of ROM bank #
        lsr a
        sta MMC1Reg3       ; write bit 2 of ROM bank #
        lsr a
        sta MMC1Reg3       ; write bit 3 of ROM bank #
        lsr a
        sta MMC1Reg3       ; write bit 4 of ROM bank #
        lda $00
+       rts

; GoBankInit
; ==========
; Calls the proper routine according to the bank number in A.

        GoBankInit:
        asl a
        tay
        lda BankInitTable,y
        sta $0A
        lda BankInitTable+1,y
        sta $0B
        jmp ($000A)

        BankInitTable:
        .dw InitBank0
        .dw InitBank1
        .dw InitBank2
        .dw InitBank3
        .dw InitBank4
        .dw InitBank5
        .dw ExitSub       ; rts
        .dw ExitSub       ; rts
        .dw ExitSub       ; rts

; "Title screen" bank

        InitBank0:
        ldy #$00
        sty GamePaused
        iny
        sty GameMode    ; mode=Title
        jsr ScreenNmiOff
        jsr $A93E       ; copy game map from ROM to WRAM $7000-$73FF
        jsr ClearNameTables
        ldy #$A0
-       lda $98BF,y
        sta $6DFF,y
        dey
        bne -
        jsr InitTitleGFX
        jmp NmiOn

; Brinstar bank

        InitBank1:
        lda #modePlay
        sta GameMode
        jsr ScreenNmiOff
        lda MainRoutine
        cmp #$03
        beq +
        lda #$00
        sta MainRoutine
        sta $74
        sta GamePaused
        jsr ClearRAM_33_DF
        jsr ClearRAM_100_10F
+       ldy #$00
        jsr ROMSwitch
        jsr InitBrinstarGFX
        jmp NmiOn

; clear RAM $100-$10F

        ClearRAM_100_10F:
        ldy #$0F
        lda #$00
-       sta $0100,y
        dey
        bpl -
        rts

; Norfair bank

        InitBank2:
        lda #modePlay
        sta GameMode
        jsr ScreenNmiOff
        jsr InitNorfairGFX
        jmp NmiOn

; Tourian bank

        InitBank3:
        lda #modePlay
        sta GameMode
        jsr ScreenNmiOff
        ldy #$0D
-       lda Table00,y
        sta $77F0,y
        dey
        bpl -
        jsr InitTourianGFX
        jmp NmiOn

; Table used by above subroutine

Table00 .db $F8
        .db $08
        .db $30
        .db $D0
        .db $60
        .db $A0
        .db $02
        .db $04
        .db $00
        .db $00
        .db $00
        .db $00
        .db $00
        .db $00

; Kraid bank

        InitBank4:
        lda #modePlay
        sta GameMode
        jsr ScreenNmiOff
        jsr InitKraidGFX
        jmp NmiOn

; Ridley bank

        InitBank5:
        lda #modePlay
        sta GameMode
        jsr ScreenNmiOff
        jsr InitRidleyGFX
        jmp NmiOn

LC5D0:  lda #modeTitle
        sta GameMode
        jmp InitGFX6

        InitTitleGFX:
        ldy #$15
        jsr LoadGFX
        ldy #$00
        jsr LoadGFX
        lda JustInBailey
        beq +           ; branch if wearing suit
        ldy #$1B
        jsr LoadGFX     ; switch to girl gfx
+       ldy #$14
        jsr LoadGFX
        ldy #$17
        jsr LoadGFX
        ldy #$18
        jsr LoadGFX
        ldy #$19
        jsr LoadGFX
        ldy #$16
        jmp LoadGFX

        InitBrinstarGFX:
        ldy #$03
        jsr LoadGFX
        ldy #$04
        jsr LoadGFX
        ldy #$05
        jsr LoadGFX
        ldy #$06
        jsr LoadGFX
        ldy #$19
        jsr LoadGFX
        ldy #$16
        jmp LoadGFX

        InitNorfairGFX:
        ldy #$04
        jsr LoadGFX
        ldy #$05
        jsr LoadGFX
        ldy #$07
        jsr LoadGFX
        ldy #$08
        jsr LoadGFX
        ldy #$09
        jsr LoadGFX
        ldy #$19
        jsr LoadGFX
        ldy #$16
        jmp LoadGFX

        InitTourianGFX:
        ldy #$05
        jsr LoadGFX
        ldy #$0A
        jsr LoadGFX
        ldy #$0B
        jsr LoadGFX
        ldy #$0C
        jsr LoadGFX
        ldy #$0D
        jsr LoadGFX
        ldy #$0E
        jsr LoadGFX
        ldy #$1A
        jsr LoadGFX
        ldy #$1C
        jsr LoadGFX
        ldy #$19
        jsr LoadGFX
        ldy #$16
        jmp LoadGFX

        InitKraidGFX:
        ldy #$04
        jsr LoadGFX
        ldy #$05
        jsr LoadGFX
        ldy #$0A
        jsr LoadGFX
        ldy #$0F
        jsr LoadGFX
        ldy #$10
        jsr LoadGFX
        ldy #$11
        jsr LoadGFX
        ldy #$19
        jsr LoadGFX
        ldy #$16
        jmp LoadGFX

        InitRidleyGFX:
        ldy #$04
        jsr LoadGFX
        ldy #$05
        jsr LoadGFX
        ldy #$0A
        jsr LoadGFX
        ldy #$12
        jsr LoadGFX
        ldy #$13
        jsr LoadGFX
        ldy #$19
        jsr LoadGFX
        ldy #$16
        jmp LoadGFX

        InitGFX6:
        ldy #$01
        jsr LoadGFX
        ldy #$02
        jsr LoadGFX
        ldy #$19
        jsr LoadGFX
        ldy #$16
        jmp LoadGFX

        InitGFX7:
        ldy #$17
        jsr LoadGFX
        ldy #$16
        jmp LoadGFX

; The table below contains info for each tile data block in the ROM.
; Each entry is 7 bytes long. The format is as follows:

; byte 0: ROM bank where GFX data is located.
; byte 1-2: 16-bit ROM start address (src)
; byte 3-4: 16-bit PPU start address (dest)
; byte 5-6: data length (16-bit)

GFXInfo .db $06 : .dw $8000, $0000, $09A0       ; [SPR] Samus, items
        .db $04 : .dw $8D60, $0000, $0520       ; [SPR] Samus in ending
        .db $01 : .dw $8D60, $1000, $0400       ; [BGR] Partial font, "The End"
        .db $06 : .dw $9DA0, $1000, $0150
        .db $05 : .dw $8D60, $1200, $0450
        .db $06 : .dw $9EF0, $1800, $0800
        .db $01 : .dw $9160, $0C00, $0400       ; [SPR] Brinstar enemies
        .db $06 : .dw $A6F0, $1000, $0260
        .db $06 : .dw $A950, $1700, $0070
        .db $02 : .dw $8D60, $0C00, $0400       ; [SPR] Norfair enemies
        .db $06 : .dw $A9C0, $1000, $02E0
        .db $06 : .dw $ACA0, $1200, $0600
        .db $06 : .dw $B2A0, $1900, $0090
        .db $05 : .dw $91B0, $1D00, $0300
        .db $02 : .dw $9160, $0C00, $0400       ; [SPR] Tourian enemies
        .db $06 : .dw $B330, $1700, $00C0
        .db $04 : .dw $9360, $1E00, $0200
        .db $03 : .dw $8D60, $0C00, $0400       ; [SPR] Miniboss I enemies
        .db $06 : .dw $B3F0, $1700, $00C0
        .db $03 : .dw $9160, $0C00, $0400       ; [SPR] Miniboss II enemies
        .db $06 : .dw $89A0, $0C00, $0100
        .db $06 : .dw $8BE0, $1400, $0500       ; [BGR] Title
        .db $06 : .dw $9980, $1FC0, $0040
        .db $06 : .dw $B4C0, $1000, $0400       ; [BGR] Complete font
        .db $06 : .dw $B4C0, $0A00, $00A0
        .db $06 : .dw $9980, $0FC0, $0040
        .db $06 : .dw $B4C0, $1D00, $02A0
        .db $06 : .dw $90E0, $0000, $07B0       ; [SPR] Suitless Samus
        .db $06 : .dw $9890, $1F40, $0010

; LoadGFX
; =======
; Y contains the GFX header to fetch from the table above, GFXInfo.

        LoadGFX:
        lda #$FF
-       clc
        adc #$07        ; each header is 7 bytes
        dey             ; advanced to the (end of the) desired header?
        bpl -           ; if not, advance to the next
        tay             ; Y is now the index into GFXInfo table (see above)

; Copy header from ROM to $00-$06

        ldx #$06
-       lda GFXInfo,y
        sta $00,x
        dey
        dex
        bpl -

        ldy $00         ; ROM bank containing the GFX data
        jsr ROMSwitch   ; switch to that bank
        lda PPUCNT0ZP
        and #$FB        ; PPU increment = 1
        sta PPUCNT0ZP
        sta PPUControl0
        jsr CopyGFXBlock
        ldy CurrentBank
        jmp ROMSwitch   ; switch back to the "old" bank

; CopyGFXBlock
; =============
; Writes tile data from ROM to VRAM, according to the gfx header data
; contained in $00-$06.

        CopyGFXBlock:
        lda $05
        bne GFXCopyLoop
        dec $06
        GFXCopyLoop:
        lda $04
        sta PPUAddress
        lda $03
        sta PPUAddress
        ldy #$00
-       lda ($01),y
        sta PPUIOReg
        dec $05
        bne +
        lda $06
        beq ++
        dec $06
+       iny
        bne -
        inc $02
        inc $04
        jmp GFXCopyLoop
++      rts

; AreaInit
; ========

        AreaInit:
        lda #$00
        sta ScrollX     ; Clear ScrollX
        sta ScrollY     ; Clear ScrollY
        lda PPUCNT0ZP
        and #$FC        ; nametable address = $2000
        sta PPUCNT0ZP
        inc MainRoutine
        lda JoyStatus
        and #$C0
        sta $F0         ; ???
        jsr EraseAllSprites
        lda #$10
        jsr LCA18
LC81D:  ldy #$01
        sty PalDataPending
        ldx #$FF
        stx $75
        inx             ; X = 0
        stx $6883
        stx DoorStatus
        stx $58
        stx $71
        txa             ; A = 0
-       cpx #$65
        bcs +
        sta $7A,x
+       cpx #$FF
        bcs +
        sta ObjAction,x
+       inx
        bne -
        jsr ScreenOff
        jsr ClearNameTables
        jsr EraseAllSprites
        jsr DestroyEnemies
        stx $6C
        stx $6D
        inx
        stx $30
        inx
        stx ScrollDir
        lda $95D7       ; Get start x pos on map
        sta MapPosX
        lda $95D8       ; Get start y pos on map
        sta MapPosY
        lda $95DA       ; Get ??? Something to do with palette switch
        sta PalToggle
        lda #$FF
        sta RoomNumber  ; room number = $FF (invalid)
        jsr CopyPtrs    ; copy pointers from ROM to ZP
        jsr GetRoomNum  ; put room # at current map pos in $5A
-       jsr SetupRoom
        ldy RoomNumber  ; load room number
        iny
        bne -

        ldy WRAMPtr+1
        sty $01
        ldy WRAMPtr
        sty $00
        lda PPUCNT0ZP
        and #$FB        ; PPU increment = 1
        sta PPUCNT0ZP
        sta PPUControl0
        ldy PPUStatus   ; reset PPU addr flip/flop

; Copy WRAM Name Table #0 ($6000) to PPU Name Table #0 ($2000)

        ldy #$20
        sty PPUAddress
        ldy #$00
        sty PPUAddress
        ldx #$04        ; prepare to write 4 pages
-       lda ($00),y
        sta PPUIOReg
        iny
        bne -
        inc $01
        dex
        bne -

        stx $91
        inx             ; X = 1
        stx PalDataPending
        stx $30
        inc MainRoutine
        jmp ScreenOn

; CopyPtrs
; ========
; Copy 7 16-bit pointers at $959A to $3B

        CopyPtrs:
        ldx #$0D
-       lda $959A,x
        sta $3B,x
        dex
        bpl -
        rts

; DestroyEnemies
; ==============

        DestroyEnemies:
        lda #$00
        tax
-       cpx #$48
        bcs +
        sta $97,x
+       sta $6AF4,x
        pha
        pla
        inx
        bne -
        stx $92
        jmp $95AB

; SamusInit
; =========
; Code that sets up Samus, when the game is first started

        SamusInit:
        lda #$08
        sta MainRoutine ; intro will be executed next frame
        lda #$2C        ; Number of time units it takes to "beam down" Samus
        sta Timer+2
        jsr IntroMusic  ; start the intro music
        ldy #$14
        sty ObjAction
        ldx #$00
        stx SamusBlink
        dex             ; X = $FF
        stx $0728
        stx $0730
        stx $0732
        stx $0738
        stx EndTimerLo
        stx EndTimerHi
        stx $8B
        stx $8E
        ldy #$27
        lda $74
        and #$0F
        beq +           ; branch if Samus not in Brinstar
        lsr ScrollDir
        ldy #$2F
+       sty $FA
        sty $93
        sty $94
        lda $95D9       ; Samus' initial vertical position
        sta ObjectY
        lda #$80        ; Samus' initial horizontal position
        sta ObjectX
        lda PPUCNT0ZP
        and #$01
        sta ObjectHi
        lda #$00
        sta HealthLo
        lda #$03
        sta HealthHi
-       rts

; GameEngine
; ==========
; main game engine

        GameEngine:
        jsr ScrollDoor
        jsr ScrollDoor
        lda $69B2
        beq +
    ; think this must be some debugging mode they forgot to remove...
    ; gives you new health, missiles and every power-up every frame.
        lda #$03
        sta HealthHi
        lda #$FF
        sta SamusGear
        lda #$05
        sta MissileCount
+       jsr UpdateWorld
        lda $0108
        ora $0109
        beq +
        lda #$00
        sta $0108
        sta $0109
        lda #$18
        ldx #$03
        jsr SetTimer
+       lda ObjAction
        cmp #sa_Dead2   ; is Samus dead?
        bne -           ; exit if not
        lda AnimDelay
        bne -
    ; Samus is way dead...
        jsr SilenceMusic
        lda $98
        cmp #$0A
        beq +
        lda #$04        ; # of time units to delay
        ldx #$04        ; password display routine
        jmp SetTimer
+       inc MainRoutine
        rts

; UpdateAge
; =========
; This is the routine which keeps track of Samus' age. It is called in the
; NMI. Basically, this routine just increments a 24-bit variable every
; 256th frame. (Except it's not really 24-bit, because the lowest age byte
; overflows at $D0.)

        UpdateAge:
        lda GameMode
        bne +           ; exit if at title/password screen
        lda MainRoutine
        cmp #$03        ; is game engine running?
        bne +           ; if not, don't update age
        ldx FrameCount  ; only update age when FrameCount is zero
        bne +           ; (which is approx. every 4.266666666667 seconds)
        inc SamusAge,x  ; Minor Age = Minor Age + 1
        lda SamusAge
        cmp #$D0        ; has Minor Age reached $D0?
        bcc +           ; if not, we're done
        lda #$00        ; else...
        sta SamusAge    ; ... reset Minor Age
-       cpx #$03
        bcs +
        inx
        inc SamusAge,x
        beq -           ; branch if Middle Age overflowed, need to increment Major Age too
+       rts

; EndGame
; =======

        EndGame:
        lda #$1C
        sta TitleRoutine
        lda #$01
        sta SwitchPending
        jmp ScreenOff

; PauseMode
; =========

        PauseMode:
        lda JoyStatus+1 ; joypad #2
        and #$88
        eor #$88        ; both A & UP pressed? (zero result = yes)
        bne Exit14      ; exit if not
        ldy EndTimerHi
        iny
        bne Exit14      ; sorry, can't quit if this is during escape sequence
        sta GamePaused
        inc MainRoutine ; password display
        Exit14:
        rts

; GoPassword
; ==========

        GoPassword:
        lda #$19
        sta TitleRoutine
        lda #$01
        sta SwitchPending
        lda $0680
        ora #$01
        sta $0680
        jmp ScreenOff

; SamusIntro
; ==========
; Game intro - fades Samus onto the screen

        SamusIntro:
        jsr EraseAllSprites
        ldy ObjAction
        lda Timer+2
        bne +
        ; intro is over, time to start game
        sta $79
        lda #$FF
        sta ObjAction
        jsr LD92C       ; start main music
        jsr SelectSamusPal
        lda #$03
        sta MainRoutine ; game engine will be called next frame
+       cmp #$1F
        bcs Exit14
        cmp Table0B-20,y
        bne +
        inc ObjAction
        sty PalDataPending
+       lda FrameCount
        lsr a
        bcc Exit14       ; only display Samus on odd frames [the blink effect]
        lda #an_SamusFront
        jsr SetSamusAnim
        lda #$00
        sta SpritePagePos
        sta PageIndex
        jmp AnimDrawObject

Table0B .db $1E,$14,$0B,$04,$FF

LCA18:  ldy MainRoutine
        cpy #$07
        beq +
        cpy #$03
        beq ++
+       rts

; Switch to appropriate area bank

++      sta $74
        and #$0F
        tay
        lda BankTable,y
        sta SwitchPending
        jmp CheckSwitch

; Table used by above subroutine
; Each value is the area bank number plus one

BankTable       .db $02         ; Brinstar
                .db $03         ; Norfair
                .db $05         ; Kraid
                .db $04         ; Tourian
                .db $06         ; Ridley

LCA35:  pha
        pha
        jsr LCA96
        lda $6884
        bpl +
        and #$01
        sta $6884
        jsr LCAA1
        lda #$01
        sta $7800,y
+       lda MainRoutine
        cmp #$01
        beq +
        lda $74
        jsr LCAC6
        ldy #$3F
-       lda $6886,y
        sta ($00),y
        dey
        bpl -
        ldy $6875
        ldx #$00
-       lda $6876,x
        sta $77FE,y
        iny
        inx
        cpx #$10
        bne -
+       pla
        jsr LCAC6
        ldy #$3F
-       lda ($00),y
        sta $6886,y
        dey
        bpl -
        bmi +
        pha
+       ldy $6875
        ldx #$00
-       lda $77FE,y
        sta $6876,x
        iny
        inx
        cpx #$10
        bne -
        pla
        rts

LCA96:  lda $6885
        asl a
        asl a
        asl a
        asl a
        sta $6875
        rts

LCAA1:  lda #$00
        jsr LCAC6
        inc $03
        ldy #$00
        tya
-       sta ($00),y
        cpy #$40
        bcs +
        sta ($02),y
+       iny
        bne -
        ldy $6875
        ldx #$00
        txa
-       sta $77FE,y
        iny
        inx
        cpx #$0C
        bne -
        rts

LCAC6:  pha
        lda $6885
        asl a
        tax
        lda Table03,x
        sta $00
        sta $02
        lda Table03+1,x
        sta $01
        sta $03
        pla
        and #$0F
        tax
        beq ++
-       lda $00
        clc
        adc #$40
        sta $00
        bcc +
        inc $01
+       dex
        bne -
++      rts

; Table used by above subroutine

Table03 .dw $69B4
        .dw $69B4
        .dw $69B4

; determine what type of ending is to be shown, based on Samus' age

LCAF5:  ldy #$01
-       lda SamusAge+2
        bne +
        lda SamusAge+1
        cmp AgeTable-1,y
        bcs +
        iny
        cpy #$05
        bne -
+       sty $6872       ; store the ending # (1..5), 5 = best ending
        lda #$00
        cpy #$04        ; was the best or 2nd best ending achieved?
        bcc +           ; branch if not (suit stays on)
        lda #$01
+       sta JustInBailey ; suit OFF, baby!
        rts

; Table used by above subroutine

AgeTable .db $7A        ; worst ending... 30 hours+
        .db $16         ; max. 5.4 hours
        .db $0A         ; max. 2.5 hours
        .db $04         ; best ending... max. 1 hour

LCB1C:  jsr ScreenOff
        lda #$FF
        sta $00
        jsr ClearNameTable
        jmp EraseAllSprites

; ===== THE REAL GUTS OF THE GAME ENGINE! =====

        UpdateWorld:
        ldx #$00
        stx SpritePagePos
        jsr UpdateEnemies       ; display of enemies
        jsr UpdateProjectiles   ; display of bullets/missiles/bombs
        jsr UpdateSamus ; display / movement of Samus
        jsr $95C3       ; area-dependent
        jsr UpdateElevator
        jsr UpdateStatues       ; Ridley & Kraid statues
        jsr LFA9D       ; destruction of enemies
        jsr LFC65       ; update of Mellow/Memu enemies
        jsr LF93B
        jsr LFBDD       ; destruction of green spinners
        jsr $8B13       ; scrolling when Samus goes through door
        jsr $8B79       ; display of doors
        jsr UpdateTiles ; tile de/regeneration
        jsr LF034       ; Samus <--> enemies crash detection
        jsr DisplayBar  ; display of data bar
        jsr LFAF2
        jsr CheckMissileToggle
        jsr UpdateItems ; display of special items
        jsr LFDE3
; Clear remaining sprite RAM
        ldx SpritePagePos
        lda #$F4
-       sta SpriteRAM,x
        jsr Xplus4       ; X = X + 4
        bne -
        rts

; SelectSamusPal
; ==============
; Select the proper palette for Samus
; Based on:
; - Is Samus wearing Varia (protective suit)?
; - Is Samus firing missiles or regular bullets?
; - Is Samus with or without suit?

        SelectSamusPal:
        tya
        pha
        lda SamusGear
        asl a
        asl a
        asl a           ; CF contains Varia status (1 = Samus has it)
        lda MissileToggle   ; A = 1 if Samus is firing missiles, else 0
        rol a           ; bit 0 of A = 1 if Samus is wearing Varia
        adc #$02
        ldy JustInBailey ; in suit?
        beq +           ; branch if yes
        clc
        adc #$17        ; add #$17 to the pal # to reach "no suit"-palettes
+       sta PalDataPending      ; palette will be written next NMI
        pla
        tay
        rts

; initiate sound effects

        SilenceMusic:
        lda #$01        ; ??? (silence)
        bne SFX_SetX0
        PauseMusic:
        lda #$02        ; ??? (silence)
        bne SFX_SetX0
        SFX_SamusWalk:
        lda #$08
        bne SFX_SetX0
        SFX_BombExplode:
        lda #$10
        bne SFX_SetX0
        SFX_MissileLaunch:
        lda #$20
        SFX_SetX0:
        ldx #$00
        beq SFX_SetSoundFlag
        SFX_Zeb:
        lda #$08
        bne SFX_SetX1
        SFX_BombLaunch:
        lda #$01
        bne SFX_SetX3
        SFX_SamusJump:
        lda #$02
        bne SFX_SetX1
        SFX_EnemyHit:
        lda #$04
        bne SFX_SetX1
        SFX_BulletFire:
        lda #$10
        bne SFX_SetX1
        SFX_Metal:
        lda #$20
        bne SFX_SetX1
        SFX_EnergyPickup:
        lda #$40
        bne SFX_SetX1
        SFX_MissilePickup:
        lda #$80
        SFX_SetX1:
        ldx #$01
        bne SFX_SetSoundFlag
        SFX_WaveFire:
        lda #$01
        bne SFX_SetX1
        SFX_ScrewAttack:
        lda #$40
        bne SFX_SetX0
LCBCE:  lda #$04        ; ??? (silence)
        bne SFX_SetX3
        SFX_MetroidHit:
        lda #$20
        bne SFX_SetX3
        SFX_MBrainHit:
        lda #$02
        bne SFX_SetX4
LCBDA:  lda #$40        ; Door open/close
        bne SFX_SetX3
        SFX_SamusHit:
        lda #$04
        bne SFX_SetX4
        SFX_SamusDie:
        lda #$80
        bne SFX_SetX3
        ldx #$02
        SFX_SetSoundFlag:
        ora $0680,x
        sta $0680,x
        rts

        SFX_SamusBall:
        lda #$02        ; Samus --> ball
        bne SFX_SetX3
        SFX_Beep:
        lda #$08
        SFX_SetX3:
        ldx #$03
        bne SFX_SetSoundFlag

; initiate music

        PowerUpMusic:
        lda #$40        ; Take power-up
        bne SFX_SetX4
        IntroMusic:
        lda #$80        ; Game begin
        SFX_SetX4:
        ldx #$04
        bne SFX_SetSoundFlag
LCC01:  lda #$02        ; ??? (silence)
        bne SFX_SetX5
LCC07:  lda #$40        ; Tourian music
        SFX_SetX5:
        ldx #$05
        bne SFX_SetSoundFlag

; UpdateSamus
; ===========

        UpdateSamus:
        ldx #$00
        stx PageIndex
        inx
        stx IsSamus     ; signal that Samus is the obj being updated
        jsr GoSamusHandler
        dec IsSamus
        rts

        GoSamusHandler:
        lda ObjAction
        bmi SamusStand
        jsr GoRoutine

; Pointer table for Samus' action handlers... One of these is executed
; EVERY frame

        .dw SamusStand  ; standing
        .dw SamusRun    ; running
        .dw SamusJump   ; jumping
        .dw SamusRoll   ; rolling
        .dw SamusPntUp  ; pointing up
        .dw SamusDoor   ; inside door while screen scrolling
        .dw SamusJump   ; jumping while pointing up
        .dw SamusDead   ; dead
        .dw SamusDead2  ; more dead
        .dw SamusElevator

; SamusStand
; ==========

        SamusStand:
        lda JoyStatus   ; status of joypad #0
        and #%11001111  ; ditch SEL & START status bits
        beq +           ; branch if no buttons pressed
        jsr LCF5D
        lda JoyStatus
+       and #%00000111  ; keep status of DOWN/LEFT/RIGHT
        bne +           ; branch if pressed
        lda JoyFirst
        and #btn_UP     ; keep status of UP
        beq ++          ; branch if not pressed
+       jsr BitScan
        cmp #$02
        bcs +
        sta SamusDir    ; 0 = right, 1 = left
+       tax
        lda ActionTable,x
        sta ObjAction
++      lda JoyFirst
        ora JoyRetrig
        asl a
        bpl +           ; branch if FIRE not pressed
        jsr LD1EE       ; shoot left/right
+       bit JoyFirst
        bpl +           ; branch if JUMP not pressed
        lda #sa_Jump    ; jump handler
        sta ObjAction
+       lda #$04
        jsr LCD6D
        lda ObjAction
        cmp #sa_Door
        bcs +           ; rts
        jsr GoRoutine

; Pointer table to code

        .dw ExitSub       ; rts
        .dw $CC98
        .dw $CFC3
        .dw $D0B5
        .dw LCF77

; Table used by above subroutine

ActionTable     .db sa_Run
                .db sa_Run
                .db sa_Roll
                .db sa_PntUp

LCC8B:  lda #$50
        sta $030F
        lda #an_Explode
        jsr SetSamusAnim
        sta $65
+       rts

LCC98:  lda #$09
        sta WalkSoundDelay
        ldx #$00
        lda AnimResetIndex
        cmp #an_SamusStand
        beq +
        inx
        cmp #$27
        beq +
        lda #$04
        jsr SetSamusNextAnim
+       lda LCCBE,x
        sta AnimResetIndex
        ldx SamusDir
LCCB7:  lda LCCC0,x
        sta $0315
        rts

LCCBE:  .db an_SamusRun
        .db an_SamusRunPntUp
LCCC0:  .db $30
        .db $D0

; SamusRun
; ========

        SamusRun:
        ldx SamusDir
        lda $0314
        beq +++
        ldy $030F
        bit $0308
        bmi +
        cpy #$18
        bcs ++
        lda #an_SamusJump
        sta AnimResetIndex
        bcc ++          ; branch always
+       cpy #$18
        bcc ++
        lda AnimResetIndex
        cmp #an_SamusFireJump
        beq +
        lda #an_SamusSalto
        sta AnimResetIndex
+       cpy #$20
        bcc ++
        lda JoyStatus
        and #btn_UP
        beq +
        lda #an_SamusJumpPntUp
        sta AnimResetIndex
+       bit JoyStatus
        bmi ++
        jsr LD147
++      lda #an_SamusRun
        cmp AnimResetIndex
        bne +
        lda #an_SamusJump
        sta AnimResetIndex
+       lda $64
        beq +
        lda JoyFirst
        bmi LCD40       ; branch if JUMP pressed
+       jsr LCF88
        jsr LD09C
        jsr LCF2E
        lda #$02
        bne LCD6D       ; branch always
+++     lda SamusOnElevator
        bne +
        jsr LCCB7
+       jsr LCDBF
        dec WalkSoundDelay  ; time to play walk sound?
        bne +               ; branch if not
        lda #$09
        sta WalkSoundDelay  ; # of frames till next walk sound trigger
        jsr SFX_SamusWalk
+       jsr LCF2E
        lda JoyFirst
        bpl +           ; branch if JUMP not pressed
LCD40:  jsr LCFC3
        lda #$12
        sta $0316
        jmp LCD6B

+       ora JoyRetrig
        asl a
        bpl +           ; branch if FIRE not pressed
        jsr LCDD7
+       lda JoyStatus
        and #$03
        bne +
        jsr LCF55
        jmp LCD6B

+       jsr BitScan
        cmp SamusDir
        beq LCD6B
        sta SamusDir
        jsr LCC98
LCD6B:  lda #$03
LCD6D:  jsr UpdateObjAnim
        jsr LCD9C
        bcs +
        lda FrameCount
        lsr a
        and #$03
        ora #$A0
        sta $6B
+       jsr LCDFA
        jsr LE269
        lda $92
        beq +
        lda #$A1
        sta $6B
+       jsr LCD92
        jmp DrawFrame   ; display Samus!

LCD92:  lda SamusDir
        jsr Amul16       ; * 16
        ora $6B
        sta $6B
        rts

LCD9C:  sec
        ldy ObjAction
        dey
        bne Exit2       ; exit if Samus isn't running
        lda SamusGear
        and #gr_SCREWATTACK
        beq Exit2       ; exit if Samus doesn't have Screw Attack
        lda AnimResetIndex
        cmp #an_SamusSalto
        beq +
        cmp #an_SamusJump
        sec
        bne Exit2
        bit $0308
        bpl Exit2
+       cmp AnimIndex
Exit2:  rts

LCDBF:  lda JoyStatus
        and #btn_UP
        lsr a
        lsr a
        lsr a
        tax
        lda LCCBE,x
        cmp AnimResetIndex
        beq Exit2
        jsr SetSamusAnim
        pla
        pla
        jmp LCD6B

LCDD7:  jsr LD1EE
        lda JoyStatus
        and #btn_UP
        bne +
        lda #an_SamusFireRun
        sta AnimIndex
        rts

+       lda AnimIndex
        sec
        sbc AnimResetIndex
        and #$03
        tax
        lda Table05,x
        jmp SetSamusNextAnim

; Table used by above subroutine

Table05 .db $3F
        .db $3B
        .db $3D
        .db $3F

LCDFA:  lda $030A
        and #$20
        beq +++
        lda #$32
        sta SamusBlink
        lda #$FF
        sta $72
        lda $73
        sta $77
        beq ++
        bpl +
        jsr SFX_SamusHit
+       lda $030A
        and #$08
        lsr a
        lsr a
        lsr a
        sta $72
++      lda #$FD
        sta $0308
        lda #$38
        sta $0314
        jsr IsSamusDead
        bne +++
        jmp CheckHealthBeep

+++     lda SamusBlink
        beq CheckHealthBeep
        dec SamusBlink
        ldx $72
        inx
        beq ++
        jsr Adiv16       ; / 16
        cmp #$03
        bcs +
        ldy $0315
        bne ++
        jsr LCF4E
+       dex
        bne +
        jsr GetAbsolute
+       sta $0309
++      lda $77
        bpl CheckHealthBeep
        lda FrameCount
        and #$01
        bne CheckHealthBeep
        tay
        sty AnimDelay
        ldy #$F7
        sty AnimFrame
        CheckHealthBeep:
        ldy HealthHi
        dey
        bmi +
        bne ++
        lda HealthLo
        cmp #$70
        bcs ++
; health < 17
+       lda FrameCount
        and #$0F
        bne ++          ; only beep every 16th frame
        jsr SFX_Beep
++      lda #$00
        sta $030A
        rts

        IsSamusDead:
        lda ObjAction
        cmp #sa_Dead
        beq Exit3
        cmp #sa_Dead2
        beq Exit3
        cmp #$FF        ; make sure zero flag is NOT set on exit
Exit3:  rts

LCE92:  lda $6E
        ora $6F
        beq Exit3
        jsr IsSamusDead
        beq LCEA3
        ldy EndTimerHi
        iny
        beq +
LCEA3:  jmp LF323

+       lda $98
        cmp #$03
        bcs LCEA3
        lda SamusGear
        and #gr_VARIA
        beq +           ; branch if Samus doesn't have Varia
        lsr $6E
        lsr $6F
        bcc +
        lda #$4F
        adc $6E
        sta $6E
+       lda HealthLo
        sta $03
        lda $6E
        sec
        jsr LC3FB
        sta HealthLo
        lda HealthHi
        sta $03
        lda $6F
        jsr LC3FB
        sta HealthHi
        lda HealthLo
        and #$F0
        ora HealthHi
        beq +
        bcs ++

; Samus is dead!

+       lda #$00
        sta HealthLo
        sta HealthHi
        lda #sa_Dead        ; death handler
        sta ObjAction
        jsr SFX_SamusDie
        jmp LCC8B

LCEF9:  lda HealthLo
        sta $03
        lda $6E
        clc
        jsr LC3DA
        sta HealthLo
        lda HealthHi
        sta $03
        lda $6F
        jsr LC3DA
        sta HealthHi
        lda TankCount
        jsr Amul16       ; * 16
        ora #$0F
        cmp HealthHi
        bcs ++
        and #$F9
        sta HealthHi
        lda #$99
        sta HealthLo
++      jmp LF323

LCF2E:  lda $030A
        lsr a
        and #$02
        beq +++
        bcs +
        lda $0315
        bmi +++
        bpl ++
+       lda $0315
        bmi ++
        bne +++
++      jsr GetAbsolute
        sta $0315
LCF4C:  ldy #$00
LCF4E:  sty $0309
        sty $0313
+++     rts

LCF55:  lda $0315
        bne LCF5D
        jsr SFX_SamusWalk
LCF5D:  jsr LCF81
        sty ObjAction
        lda JoyStatus
        and #btn_UP
        bne LCF77
        lda #an_SamusStand
        SetSamusAnim:
        sta AnimResetIndex
        SetSamusNextAnim:
        sta AnimIndex
        lda #$00
        sta AnimDelay
        rts

LCF77:  lda #sa_PntUp
        sta ObjAction
        lda #an_SamusPntUp
        jsr SetSamusAnim
LCF81:  jsr LCFB7
        sty AnimDelay
        rts

LCF88:  lda JoyStatus
        and #$03
        beq +
        jsr BitScan
        tax
        jsr LCCB7
        lda $0314
        bmi ++
        lda AnimResetIndex
        cmp #an_SamusSalto
        beq ++
        stx SamusDir
        lda Table06+1,x
        jmp SetSamusAnim

+       lda $0314
        bmi ++
        beq ++
        lda AnimResetIndex
        cmp #an_SamusJump
        bne ++
LCFB7:  jsr LCF4C
        sty $0315
++      rts

LCFBE:  ldy #an_SamusJumpPntUp
        jmp +

LCFC3:  ldy #an_SamusJump
+       sty AnimResetIndex
        dey
        sty AnimIndex
        lda #$04
        sta AnimDelay
        lda #$00
        sta $030F
        lda #$FC
        sta $0308
        ldx ObjAction
        dex
        bne +           ; branch if Samus is standing still
        lda SamusGear
        and #gr_SCREWATTACK
        beq +           ; branch if Samus doesn't have Screw Attack
        lda #$00
        sta $0686
        jsr SFX_ScrewAttack
+       jsr SFX_SamusJump
        ldy #$18        ; gravity (high value -> low jump)
        lda SamusGear
        and #gr_HIGHJUMP
        beq +           ; branch if Samus doesn't have High Jump
        ldy #$12        ; lower gravity value -> high jump!
+       sty $0314
        rts

        SamusJump:
        lda $030F
        bit $0308
        bpl +           ; branch if falling down
        cmp #$20
        bcc +           ; branch if jumped less than 32 pixels upwards
        bit JoyStatus
        bmi +           ; branch if JUMP button still pressed
        jsr LD147       ; stop jump (start falling)
+       jsr LD055
        jsr LCF2E
        lda JoyStatus
        and #btn_UP     ; UP pressed?
        beq +           ; branch if not
        lda #an_SamusJumpPntUp
        sta AnimResetIndex
        lda #sa_PntJump      ; "jumping & pointing up" handler
        sta ObjAction
+       jsr LD09C
        lda $64
        beq +
        lda JoyFirst
        bpl +           ; branch if JUMP not pressed
        jsr LCFC3
        jmp LCD6B

+       lda $0314
        bne ++
        lda ObjAction
        cmp #sa_PntJump
        bne +
        jsr LCF77
        bne ++
+       jsr LCF55
++      lda #$03
        jmp LCD6D

LD055:  ldx #$01
        ldy #$00
        lda JoyStatus
        lsr a
        bcs +           ; branch if RIGHT pressed
        dex
        lsr a
        bcc +++       ; branch if LEFT not pressed
        dex
        iny
+       cpy SamusDir
        beq +++
        lda ObjAction
        cmp #sa_PntJump
        bne +
        lda AnimResetIndex
        cmp Table04,y
        bne ++
        lda Table04+1,y
        jmp ++

+       lda AnimResetIndex
        cmp Table06,y
        bne ++
        lda Table06+1,y
++      jsr SetSamusAnim
        lda #$08
        sta AnimDelay
        sty SamusDir
+++     stx $0309
-       rts

; Table used by above subroutine

Table06 .db $0C
        .db $0C
        .db $0C
Table04 .db $35
        .db $35
        .db $35

LD09C:  lda JoyFirst
        ora JoyRetrig
        asl a
        bpl -           ; exit if FIRE not pressed
        lda AnimResetIndex
        cmp #an_SamusJumpPntUp
        bne +
        jmp LD275

+       jsr LD210
        lda #an_SamusFireJump
        jmp SetSamusAnim

LD0B5:  lda SamusGear
        and #gr_MARUMARI
        beq +           ; branch if Samus doesn't have Maru Mari
        lda $0314
        bne +
    ; turn Samus into ball
        ldx SamusDir
        lda #an_SamusRoll
        sta AnimResetIndex
        lda #an_SamusRunJump
        sta AnimIndex
        lda LCCC0,x
        sta $0315
        lda #$01
        sta $0686
        jmp SFX_SamusBall

+       lda #sa_Stand
        sta ObjAction
        rts

; SamusRoll
; =========

        SamusRoll:
        lda JoyFirst
        and #btn_UP     ; UP pressed?
        bne +           ; branch if yes
        bit JoyFirst    ; JUMP pressed?
        bpl ++          ; branch if no
+       lda JoyStatus
        and #btn_DOWN   ; DOWN pressed?
        bne ++          ; branch if yes
    ; break out of "ball mode"
        lda ObjRadY
        clc
        adc #$08
        sta ObjRadY
        jsr CheckMoveUp
        bcc ++          ; branch if not possible to stand up
        ldx #$00
        jsr LE8BE
        stx $05
        lda #$F5
        sta $04
        jsr LFD8F
        jsr LD638
        jsr LCF55
        dec AnimIndex
        jsr LD147
        lda #$04
        jmp LD144

++      lda JoyFirst
        jsr BitScan
        cmp #$02
        bcs +
        sta SamusDir
        lda #an_SamusRoll
        jsr SetSamusAnim
+       ldx SamusDir
        jsr LCCB7
        jsr LCF2E
        jsr CheckBombLaunch
        lda JoyStatus
        and #$03
        bne +
        jsr LCFB7
+       lda #$02
LD144:  jmp LCD6D

LD147:  ldy #$00
        sty $0308
        sty $0312
        rts

; CheckBombLaunch
; ===============
; This routine is called only when Samus is rolled into a ball.
; It does the following:
; - Checks if Samus has bombs
; - If so, checks if the FIRE button has been pressed
; - If so, checks if there are any object "slots" available
;   (only 3 bullets/bombs can be active at the same time)
; - If so, a bomb is launched.

        CheckBombLaunch:
        lda SamusGear
        lsr a
        bcc ++          ; exit if Samus doesn't have Bombs
        lda JoyFirst
        ora JoyRetrig
        asl a           ; bit 7 = status of FIRE button
        bpl ++          ; exit if FIRE not pressed
        lda $0308
        ora SamusOnElevator
        bne ++
        ldx #$D0        ; try object slot D
        lda ObjAction,x
        beq +           ; launch bomb if slot available
        ldx #$E0        ; try object slot E
        lda ObjAction,x
        beq +           ; launch bomb if slot available
        ldx #$F0        ; try object slot F
        lda ObjAction,x
        bne ++          ; no bomb slots available, exit
; launch bomb... give it same coords as Samus
+       lda ObjectHi
        sta ObjectHi,x
        lda ObjectX
        sta ObjectX,x
        lda ObjectY
        clc
        adc #$04        ; 4 pixels further down than Samus' center
        sta ObjectY,x
        lda #wa_LayBomb
        sta ObjAction,x
        jsr SFX_BombLaunch
++      rts

        SamusPntUp:
        lda JoyStatus
        and #btn_UP     ; UP still pressed?
        bne +           ; branch if yes
        lda #sa_Stand   ; stand handler
        sta ObjAction
+       lda JoyStatus
        and #$07        ; DOWN, LEFT, RIGHT pressed?
        beq ++          ; branch if no
        jsr BitScan
        cmp #$02
        bcs +
        sta SamusDir
+       tax
        lda Table07,x
        sta ObjAction
++      lda JoyFirst
        ora JoyRetrig
        asl a
        bpl +           ; branch if FIRE not pressed
        jsr LD1EE
+       bit JoyFirst
        bpl +           ; branch if JUMP not pressed
        lda #sa_PntJump
        sta ObjAction
+       lda #$04
        jsr LCD6D
        lda ObjAction
        jsr GoRoutine

; Pointer table to code

        .dw $CF55
        .dw $CC98
        .dw ExitSub       ; rts
        .dw $D0B5
        .dw ExitSub       ; rts
        .dw ExitSub       ; rts
        .dw $CFBE
        .dw ExitSub       ; rts
        .dw ExitSub       ; rts
        .dw ExitSub       ; rts

; Table used by above subroutine

Table07 .db sa_Run
        .db sa_Run
        .db sa_Roll

LD1EE:  lda JoyStatus
        and #btn_UP
        beq LD210
        jmp LD275

LD1F7:  ldy #$D0
-       lda ObjAction,y
        beq +
        jsr Yplus16
        bne -
        iny
        rts

+       sta $030A,y
        lda MissileToggle
        beq +
        cpy #$D0
+       rts

LD210:  lda $92
        bne +
        jsr LD1F7
        bne +
        jsr LD2EB
        jsr LD359
        jsr LD38E
        lda #$0C
        sta $030F,y
        ldx SamusDir
        lda Table99,x   ; get bullet speed
        sta $0309,y     ; -4 or 4, depending on Samus' direction
        lda #$00
        sta $0308,y
        lda #$01
        sta $030B,y
        jsr CheckMissileLaunch
        lda ObjAction,y
        asl a
        ora SamusDir
        and #$03
        tax
        lda Table08,x
        sta $05
        lda #$FA
        sta $04
        jsr LD306
        lda SamusGear
        and #gr_LONGBEAM
        lsr a
        lsr a
        lsr a
        ror a
        ora $061F
        sta $061F
        ldx ObjAction,y
        dex
        bne +
        jsr SFX_BulletFire
+       ldy #$09
LD26B:  tya
        jmp SetSamusNextAnim

Table08 .db $0C
        .db $F4
        .db $08
        .db $F8

Table99 .db $04
        .db $FC

LD275:  lda $92
        bne +
        jsr LD1F7
        bne +
        jsr LD2EB
        jsr LD38A
        jsr LD38E
        lda #$0C
        sta $030F,y
        lda #$FC
        sta $0308,y
        lda #$00
        sta $0309,y
        lda #$01
        sta $030B,y
        jsr LD340
        ldx SamusDir
        lda Table09+4,x
        sta $05
        lda ObjAction,y
        and #$01
        tax
        lda Table09+6,x
        sta $04
        jsr LD306
        lda SamusGear
        and #gr_LONGBEAM
        lsr a
        lsr a
        lsr a
        ror a
        ora $061F
        sta $061F
        lda ObjAction,y
        cmp #$01
        bne +
        jsr SFX_BulletFire
+       ldx SamusDir
        ldy Table09,x
        lda $0314
        beq +
        ldy Table09+2,x
+       lda ObjAction
        cmp #$01
        beq +
        jmp LD26B

; Table used by above subroutine

Table09 .db $26
        .db $26
        .db $34
        .db $34
        .db $01
        .db $FF
        .db $EC
        .db $F0

LD2EB:  tya
        tax
        inc ObjAction,x
        lda #$02
        sta ObjRadY,y
        sta ObjRadX,y
        lda #an_Bullet
        SetProjectileAnim:
        sta AnimResetIndex,x
        sta AnimIndex,x
        lda #$00
        sta AnimDelay,x
+       rts

LD306:  ldx #$00
        jsr LE8BE
        tya
        tax
        jsr LFD8F
        txa
        tay
        jmp LD638

        CheckMissileLaunch:
        lda MissileToggle
        beq Exit4       ; exit if Samus not in "missile fire" mode
        cpy #$D0
        bne Exit4
        ldx SamusDir
        lda MissileAnims,x
-       jsr SetBulletAnim
        jsr SFX_MissileLaunch
        lda #wa_Missile        ; missile handler
        sta ObjAction,y
        lda #$FF
        sta $030F,y     ; # of frames projectile should last
        dec MissileCount
        bne Exit4       ; exit if not the last missile
; Samus has no more missiles left
        dec MissileToggle       ; put Samus in "regular fire" mode
        jmp SelectSamusPal      ; update Samus' palette to reflect this

        MissileAnims:
        .db an_MissileRight
        .db an_MissileLeft

LD340:  lda MissileToggle
        beq Exit4
        cpy #$D0
        bne Exit4
        lda #$8F
        bne -

        SetBulletAnim:
        sta AnimIndex,y
        sta AnimResetIndex,y
        lda #$00
        sta AnimDelay,y
Exit4:  rts

LD359:  lda SamusDir
-       sta $0502,y
        bit SamusGear
        bvc Exit4       ; branch if Samus doesn't have Wave Beam
        lda MissileToggle
        bne Exit4
        lda #$00
        sta $0501,y
        sta $0304,y
        tya
        jsr Adiv32      ; / 32
        lda #$00
        bcs +
        lda #$0C
+       sta $0500,y
        lda #wa_WaveBeam
        sta ObjAction,y
        lda #an_WaveBeam
        jsr SetBulletAnim
        jmp SFX_WaveFire

LD38A:  lda #$02
        bne -
LD38E:  lda MissileToggle
        bne Exit4
        lda SamusGear
        bpl Exit4       ; branch if Samus doesn't have Ice Beam
        lda #wa_IceBeam
        sta ObjAction,y
        lda $061F
        ora #$01
        sta $061F
        jmp SFX_BulletFire

; SamusDoor
; =========

        SamusDoor:
        lda DoorStatus
        cmp #$05
        bcc +++
    ; move Samus out of door, how far depends on initial value of DoorDelay
        dec DoorDelay
        bne MoveOutDoor
    ; done moving
        asl a
        bcc +
        lsr a
        sta DoorStatus
        bne +++
+       jsr LD48C
        jsr LED65
        jsr $95AB
        lda $79
        beq +
        pha
        jsr LD92C       ; start music
        pla
        bpl +
        lda #$00
        sta $79
        beq +
-       lda #$80
        sta $79
+       lda $6987
        beq +
        jsr LCC07
        lda #$00
        sta $6987
        beq -           ; branch always
+       lda $58
        and #$0F
        sta ObjAction
        lda #$00
        sta $58
        sta DoorStatus
        jsr LD147
        MoveOutDoor:
        lda SamusDoorDir
        beq ++          ; branch if door leads to the right
        ldy ObjectX
        bne +
        jsr ToggleSamusHi       ; toggle 9th bit of Samus' X coord
+       dec ObjectX
        jmp +++

++      inc ObjectX
        bne +++
        jsr ToggleSamusHi       ; toggle 9th bit of Samus' X coord
+++     jsr LCDFA
        jsr LCD92
        jmp DrawFrame       ; display Samus

        SamusDead:
        lda #$01
        jmp LCD6D

        SamusDead2:
        dec $0304
        rts

; SamusElevator
; =============

        SamusElevator:
        lda $0320
        cmp #$03
        beq +
        cmp #$08
        bne +++
+       lda $032F
        bmi ++
        lda ObjectY
        sec
        sbc ScrollY     ; A = Samus' Y position on the visual screen
        cmp #$84
        bcc +           ; if ScreenY < $84, don't scroll
        jsr ScrollDown  ; otherwise, attempt to scroll
+       ldy ObjectY
        cpy #239        ; wrap-around required?
        bne +
        jsr ToggleSamusHi       ; toggle 9th bit of Samus' Y coord
        ldy #$FF        ; ObjectY will now be 0
+       iny
        sty ObjectY
        jmp LD47E

++      lda ObjectY
        sec
        sbc ScrollY     ; A = Samus' Y position on the visual screen
        cmp #$64
        bcs +           ; if ScreenY >= $64, don't scroll
        jsr ScrollUp    ; otherwise, attempt to scroll
+       ldy ObjectY
        bne +           ; wraparound required? (branch if not)
        jsr ToggleSamusHi       ; toggle 9th bit of Samus' Y coord
        ldy #240        ; ObjectY will now be 239
+       dey
        sty ObjectY
        jmp LD47E

+++     ldy #$00
        sty $0308
        cmp #$05
        beq +
        cmp #$07
        beq +
LD47E:  lda FrameCount
        lsr a
        bcc ++
+       jsr LCD92
        lda #$01
        jmp AnimDrawObject
++      rts

LD48C:  ldx #$60
        sec
-       jsr LD4B4
        txa
        sbc #$20
        tax
        bpl -
        jsr LEB85
        tay
        ldx #$18
-       jsr LD4A8
        txa
        sec
        sbc #$08
        tax
        bne -
LD4A8:  tya
        cmp $072C,x
        bne +
        lda #$FF
        sta $0728,x
+       rts

LD4B4:  lda $0405,x
        and #$02
        bne +
        sta $6AF4,x
+       rts

; UpdateProjectiles
; =================

        UpdateProjectiles:
        ldx #$D0
        jsr DoOneProjectile
        ldx #$E0
        jsr DoOneProjectile
        ldx #$F0
        DoOneProjectile:
        stx PageIndex
        lda ObjAction,x
        jsr GoRoutine

        .dw ExitSub     ; rts
        .dw UpdateBullet ; regular beam
        .dw UpdateWaveBullet      ; wave beam
        .dw UpdateIceBullet       ; ice beam
        .dw BulletExplode    ; bullet/missile explode
        .dw $D65E       ; lay bomb
        .dw $D670       ; lay bomb
        .dw $D691       ; lay bomb
        .dw $D65E       ; lay bomb
        .dw $D670       ; bomb countdown
        .dw $D691       ; bomb explode
        .dw UpdateBullet  ; missile

        UpdateBullet:
        lda #$01
        sta $71
        jsr LD5FC
        jsr LD5DA
        jsr LD609
        CheckBulletStat:
        ldx PageIndex
        bcc +
        lda SamusGear
        and #gr_LONGBEAM
        bne DrawBullet  ; branch if Samus has Long Beam
        dec $030F,x     ; decrement bullet timer
        bne DrawBullet
        lda #$00        ; timer hit 0, kill bullet
        sta ObjAction,x
        beq DrawBullet  ; branch always
+       lda ObjAction,x
        beq +
        jsr LD5E4
        DrawBullet:
        lda #$01
        jsr AnimDrawObject
+       dec $71
        rts

-       inc $0500,x
LD522:  inc $0500,x
        lda #$00
        sta $0501,x
        beq +           ; branch always

        UpdateWaveBullet:
        lda #$01
        sta $71
        jsr LD5FC
        jsr LD5DA
        lda $0502,x
        and #$FE
        tay
        lda Table0A,y
        sta $0A
        lda Table0A+1,y
        sta $0B
+       ldy $0500,x
        lda ($0A),y
        cmp #$FF
        bne +
        sta $0500,x
        jmp LD522

+       cmp $0501,x
        beq -
        inc $0501,x
        iny
        lda ($0A),y
        jsr $8296
        ldx PageIndex
        sta $0308,x
        lda ($0A),y
        jsr $832F
        ldx PageIndex
        sta $0309,x
        tay
        lda $0502,x
        lsr a
        bcc +
        tya
        jsr GetAbsolute
        sta $0309,x
+       jsr LD609
        bcs +
        jsr LD624
+       jmp CheckBulletStat

Table0A .dw Table0C     ; pointer to table #1 below
        .dw Table0D     ; pointer to table #2 below

; Table #1 (size: 25 bytes)

Table0C .db $01
        .db $F3
        .db $01
        .db $D3
        .db $01
        .db $93
        .db $01
        .db $13
        .db $01
        .db $53
        .db $01
        .db $73
        .db $01
        .db $73
        .db $01
        .db $53
        .db $01
        .db $13
        .db $01
        .db $93
        .db $01
        .db $D3
        .db $01
        .db $F3
        .db $FF

; Table #2 (size: 25 bytes)

Table0D .db $01
        .db $B7
        .db $01
        .db $B5
        .db $01
        .db $B1
        .db $01
        .db $B9
        .db $01
        .db $BD
        .db $01
        .db $BF
        .db $01
        .db $BF
        .db $01
        .db $BD
        .db $01
        .db $B9
        .db $01
        .db $B1
        .db $01
        .db $B5
        .db $01
        .db $B7
        .db $FF

; UpdateIceBullet
; ===============

        UpdateIceBullet:
        lda #$81
        sta $6B
        jmp UpdateBullet

; BulletExplode
; =============
; bullet/missile explode

        BulletExplode:
        lda #$01
        sta $71
        lda $0303,x
        sec
        sbc #$F7
        bne +
        sta ObjAction,x         ; kill bullet
+       jmp DrawBullet

LD5DA:  lda $030A,x
        beq Exit5
        lda #$00
        sta $030A,x
LD5E4:  lda #$1D
        ldy ObjAction,x
        cpy #wa_BulletExplode
        beq Exit5
        cpy #wa_Missile
        bne +
        lda #an_MissileExplode
+       jsr SetProjectileAnim
        lda #wa_BulletExplode
-       sta ObjAction,x
Exit5:  rts

LD5FC:  lda $030B,x
        lsr a
        bcs Exit5
--      lda #$00
        beq -         ; branch always
-       jmp LE81E

; bullet <--> background crash detection

LD609:  jsr GetObjCoords
        ldy #$00
        lda ($04),y     ; get tile # that bullet touches
        cmp #$A0
        bcs LD624
        jsr $95C0
        cmp #$4E
        beq -
        jsr LD651
        bcc ++
        clc
        jmp IsBlastTile

LD624:  ldx PageIndex
        lda $0309,x
        sta $05
        lda $0308,x
        sta $04
        jsr LE8BE
        jsr LFD8F
        bcc --
LD638:  lda $08
        sta ObjectY,x
        lda $09
        sta ObjectX,x
        lda $0B
        and #$01
        bpl +           ; branch always
        ToggleObjectHi:
        lda ObjectHi,x
        eor #$01
+       sta ObjectHi,x
++      rts

LD651:  ldy $74
        cpy #$10
        beq +
        cmp #$70
        bcs ++
+       cmp #$80
++      rts

LD65E:  lda #an_BombTick
        jsr SetProjectileAnim
        lda #$18        ; fuse length :-)
        sta $030F,x
        inc ObjAction,x       ; bomb update handler
        DrawBomb:
        lda #$03
        jmp AnimDrawObject

LD670:  lda FrameCount
        lsr a
        bcc ++          ; only update counter on odd frames
        dec $030F,x
        bne ++
        lda #$37
        ldy ObjAction,x
        cpy #$09
        bne +
        lda #an_BombExplode
+       jsr SetProjectileAnim
        inc ObjAction,x
        jsr SFX_BombExplode
++      jmp DrawBomb

LD691:  inc $030F,x
        jsr LD6A7
        ldx PageIndex
        lda $0303,x
        sec
        sbc #$F7
        bne +
        sta ObjAction,x     ; kill bomb
+       jmp DrawBomb

LD6A7:  jsr GetObjCoords
        lda $04
        sta $0A
        lda $05
        sta $0B
        ldx PageIndex
        ldy $030F,x
        dey
        beq ++
        dey
        bne +++
        lda #$40
        jsr LD78B
        txa
        bne +
        lda $04
        and #$20
        beq Exit6
+       lda $05
        and #$03
        cmp #$03
        bne ++
        lda $04
        cmp #$C0
        bcc ++
        lda ScrollDir
        and #$02
        bne Exit6
        lda #$80
        jsr LD78B
++      jsr LD76A
Exit6:  rts

+++     dey
        bne ++
        lda #$40
        jsr LD77F
        txa
        bne +
        lda $04
        and #$20
        bne Exit6
+       lda $05
        and #$03
        cmp #$03
        bne +
        lda $04
        cmp #$C0
        bcc +
        lda ScrollDir
        and #$02
        bne Exit6
        lda #$80
        jsr LD77F
+       jmp LD76A

++      dey
        bne ++
        lda #$02
        jsr LD78B
        txa
        bne +
        lda $04
        lsr a
        bcc Exit7
+       lda $04
        and #$1F
        cmp #$1E
        bcc +
        lda ScrollDir
        and #$02
        beq Exit7
        lda #$1E
        jsr LD77F
        lda $05
        eor #$04
        sta $05
+       jmp LD76A

++      dey
        bne Exit7
        lda #$02
        jsr LD77F
        txa
        bne +
        lda $04
        lsr a
        bcs Exit7
+       lda $04
        and #$1F
        cmp #$02
        bcs LD76A
        lda ScrollDir
        and #$02
        beq Exit7
        lda #$1E
        jsr LD78B
        lda $05
        eor #$04
        sta $05
LD76A:  txa
        pha
        ldy #$00
        lda ($04),y
        jsr LD651
        bcc +
        cmp #$A0
        bcs +
        jsr LE9C2
+       pla
        tax
Exit7:  rts

LD77F:  clc
        adc $0A
        sta $04
        lda $0B
        adc #$00
        jmp LD798

LD78B:  sta $00
        lda $0A
        sec
        sbc $00
        sta $04
        lda $0B
        sbc #$00
LD798:  and #$07
        ora #$60
        sta $05
-       rts

        GetObjCoords:
        ldx PageIndex
        lda ObjectY,x
        sta $02
        lda ObjectX,x
        sta $03
        lda ObjectHi,x
        sta $0B
        jmp MakeWRAMPtr

        UpdateElevator:
        ldx #$20
        stx PageIndex
        lda ObjAction,x
        jsr GoRoutine

; Pointer table to elevator handlers

        .dw ExitSub       ; rts
        .dw ElevatorIdle
        .dw $D80E
        .dw ElevatorMove
        .dw ElevatorScroll
        .dw $D8A3
        .dw $D8BF
        .dw $D8A3
        .dw ElevatorMove
        .dw ElevatorStop

        ElevatorIdle:
        lda SamusOnElevator
        beq ShowElevator
        lda #btn_DOWN
        bit $032F       ; elevator direction in bit 7 (1 = up)
        bpl +
        asl a           ; btn_UP
+       and JoyStatus
        beq ShowElevator
    ; start elevator!
        jsr LD147
        sty AnimDelay
        sty $0314
        tya
        sta $0308,x
        inc ObjAction,x
        lda #sa_Elevator
        sta ObjAction
        lda #an_SamusFront
        jsr SetSamusAnim
        lda #128
        sta ObjectX     ; center
        lda #112
        sta ObjectY     ; center
        ShowElevator:
        lda FrameCount
        lsr a
        bcc -          ; only display elevator at odd frames
        jmp DrawFrame       ; display elevator

LD80E:  lda ScrollX
        bne +
        lda $FA
        ora #$08
        sta $FA
        lda ScrollDir
        and #$01
        sta ScrollDir
        inc ObjAction,x
        jmp ShowElevator

+       lda #$80
        sta ObjectX
        lda ObjectX,x
        sec
        sbc ScrollX
        bmi +
        jsr ScrollLeft
        jmp ShowElevator

+       jsr ScrollRight
        jmp ShowElevator

        ElevatorMove:
        lda $030F,x
        bpl ++          ; branch if elevator going down
    ; move elevator up one pixel
        ldy ObjectY,x
        bne +
        jsr ToggleObjectHi
        ldy #240
+       dey
        tya
        sta ObjectY,x
        jmp +++

    ; move elevator down one pixel
++      inc ObjectY,x
        lda ObjectY,x
        cmp #240
        bne +++
        jsr ToggleObjectHi
        lda #$00
        sta ObjectY,x
+++     cmp #$83
        bne +           ; move until Y coord = $83
        inc ObjAction,x
+       jmp ShowElevator

        ElevatorScroll:
        lda ScrollY
        bne ElevScrollRoom  ; scroll until ScrollY = 0
        lda #$4E
        sta AnimResetIndex
        lda #$41
        sta AnimIndex
        lda #$5D
        sta AnimResetIndex,x
        lda #$50
        sta AnimIndex,x
        inc ObjAction,x
        lda #$40
        sta Timer
        jmp ShowElevator

        ElevScrollRoom:
        lda $030F,x
        bpl +           ; branch if elevator going down
        jsr ScrollUp
        jmp ShowElevator

+       jsr ScrollDown
        jmp ShowElevator

LD8A3:  inc ObjAction,x
        lda ObjAction,x
        cmp #$08        ; ElevatorMove
        bne +
        lda #$23
        sta $0303,x
        lda #an_SamusFront
        jsr SetSamusAnim
        jmp ShowElevator

+       lda #$01
        jmp AnimDrawObject

LD8BF:  lda $030F,x
        tay
        cmp #$8F        ; Leads-To-Ending elevator?
        bne +
    ; Samus made it! YAY!
        lda #$07
        sta MainRoutine
        inc $6883
        ldy #$00
        sty $33
        iny
        sty SwitchPending   ; switch to bank 0
        lda #$1D        ; ending
        sta TitleRoutine
        rts

+       tya
        bpl ++
        ldy #$00
        cmp #$84
        bne +
        iny
+       tya
++      ora #$10
        jsr LCA18
        lda PalToggle
        eor #$07
        sta PalToggle
        ldy $74
        cpy #$12
        bcc +
        lda #$01
+       sta PalDataPending
        jsr WaitNMIPass_
        jsr SelectSamusPal
        jsr LD92C       ; start music
        jsr ScreenOn
        jsr CopyPtrs
        jsr DestroyEnemies
        ldx #$20
        stx PageIndex
        lda #$6B
        sta AnimResetIndex
        lda #$5F
        sta AnimIndex
        lda #$7A
        sta AnimResetIndex,x
        lda #$6E
        sta AnimIndex,x
        inc ObjAction,x
        lda #$40
        sta Timer
        rts

; start music

LD92C:  lda $0320
        cmp #$06
        bne +
        lda $032F
        bmi ++
+       lda $95CD
        ldy $79
        bmi +++
        beq +++
++      lda #$81
        sta $79
        lda #$20
+++     ora $0685
        sta $0685
        rts

        ElevatorStop:
        lda ScrollY
        bne ++          ; scroll until ScrollY = 0
        lda #sa_Stand
        sta ObjAction
        jsr LCF55
        ldx PageIndex   ; #$20
        lda #$01        ; ElevatorIdle
        sta ObjAction,x
        lda $030F,x
        eor #$80        ; switch elevator direction
        sta $030F,x
        bmi +
        jsr ToggleScroll
        sta $FA
+       jmp ShowElevator
++      jmp ElevScrollRoom

LD976:  lda #$00
        sta SamusOnElevator
        sta $7D
        tay
        ldx #$50
        jsr LF186
-       lda $6AF4,x
        cmp #$04
        bne +
        jsr LF152
        jsr LF1BF
        jsr LF1FA
        bcs +
        jsr LD9BA
        bne +
        inc $7D
        bne ++
+       jsr Xminus16
        bpl -
++      lda $0320
        beq +
        ldy #$00
        ldx #$20
        jsr LDC82
        bcs +
        jsr LD9BA
        bne +
        inc SamusOnElevator     ; Samus is standing on elevator
+       rts

LD9BA:  lda $10
        and #$02
        bne +
        ldy $11
        iny
        cpy $04
        beq Exit8
+       lda $030A
        and #$38
        ora $10
        ora #$40
        sta $030A
Exit8:  rts

; UpdateStatues
; =============

        UpdateStatues:
        lda #$60
        sta PageIndex
        ldy $0360
        beq Exit8           ; exit if no statue present
        dey
        bne +
        jsr LDAB0
        ldy #$01
        jsr LDAB0
        bcs +
        inc $0360
+       ldy $0360
        cpy #$02
        bne ++
        lda $687B
        bpl +
        ldy #$02
        jsr LDAB0
+       lda $687C
        bpl +
        ldy #$03
        jsr LDAB0
+       bcs ++
        inc $0360
++      ldx #$60
        jsr LDA1A
        ldx #$61
        jsr LDA1A
        jmp LDADA

LDA1A:  jsr LDA3D
        jsr LDA7C
        txa
        and #$01
        tay
        lda LDA3B,y
        sta $0363
        lda $681B,x
        beq +
        bmi +
        lda FrameCount
        lsr a
        bcc ++          ; only display statue at odd frames
+       jmp DrawFrame       ; display statue

LDA39:  .db $88
        .db $68
LDA3B:  .db $65
        .db $66

LDA3D:  lda $0304,x
        bmi ++
        lda #$01
        sta $0304,x
        lda $030F,x
        and #$0F
        beq ++
        inc $0304,x
        dec $030F,x
        lda $030F,x
        and #$0F
        bne ++
        lda $0304,x
        ora #$80
        sta $0304,x
        sta $681B,x
        inc $0304,x
        txa
        pha
        and #$01
        pha
        tay
        jsr LDAB0
        pla
        tay
        iny
        iny
        jsr LDAB0
        pla
        tax
++      rts

LDA7C:  lda $030F,x
        sta $036D
        txa
        and #$01
        tay
        lda LDA39,y
        sta $036E
        lda $681B,x
        beq +
        bmi +
        lda $0304,x
        cmp #$01
        bne +
        lda $0306,x
        beq +
        dec $030F,x
        lda $0683
        ora #$10
        sta $0683
+       lda #$00
        sta $0306,x
        rts

LDAB0:  lda Table0E,y
        sta $05C8
        lda $036C
        asl a
        asl a
        ora Table1B,y
        sta $05C9
        lda #$09
        sta $05C3
        lda #$C0
        sta PageIndex
        jsr DrawTileBlast
        lda #$60
        sta PageIndex
        rts

; Table used by above subroutine

Table0E .db $30
        .db $AC
        .db $F0
        .db $6C
Table1B .db $61
        .db $60
        .db $60
        .db $60

LDADA:  lda $54
        bmi Exit0
        lda DoorStatus
        bne Exit0
        lda $687B
        and $687C
        bpl Exit0
        sta $54
        ldx #$70
        ldy #$08
-       lda #$03
        sta $0500,x
        tya
        asl a
        sta $0507,x
        lda #$04
        sta TileType,x
        lda $036C
        asl a
        asl a
        ora #$62
        sta TileWRAMHi,x
        tya
        asl a
        adc #$08
        sta TileWRAMLo,x
        jsr Xminus16
        dey
        bne -
Exit0:  rts

; CheckMissileToggle
; ==================
; Toggles between bullets/missiles (if Samus has any missiles).

        CheckMissileToggle:
        lda MissileCount
        beq Exit0       ; exit if Samus has no missiles
        lda JoyFirst
        ora JoyRetrig
        and #btn_SELECT
        beq Exit0       ; exit if SELECT not pressed
        lda MissileToggle
        eor #$01        ; 0 = fire bullets, 1 = fire missiles
        sta MissileToggle
        jmp SelectSamusPal

; MakeBitMask
; ===========
; in: Y = bit index
; out: A = bit Y set, other 7 bits zero

        MakeBitMask:
        sec
        lda #$00
-       rol a
        dey
        bpl -
-       rts

; UpdateItems
; ===========

        UpdateItems:
        lda #$40
        sta PageIndex
        ldx #$00
        jsr LDB42
        ldx #$08
LDB42:  stx $4C
        ldy $0748,x
        iny
        beq -
        lda $0749,x
        sta $034D
        lda $074A,x
        sta $034E
        lda $074B,x
        sta $034C
        jsr GetObjCoords
        ldx $4C
        ldy #$00
        lda ($04),y
        cmp #$A0
        bcc -
        lda $0748,x
        and #$0F
        ora #$50
        sta $0343
        lda FrameCount
        lsr a
        and #$03
        ora #$80
        sta $6B
        lda SpritePagePos
        pha
        lda $074F,x
        jsr DrawFrame       ; display special item
        pla
        cmp SpritePagePos
        beq Exit9
        tax
        ldy $4C
        lda $0748,y
        ldy #$01
        cmp #$07
        beq +
        dey
        cmp #$06
        beq +
        cmp #$02
        bne ++
+       tya
        sta $0206,x
        lda #$FF
++      pha
        ldx #$00
        ldy #$40
        jsr LDC7F
        pla
        bcs Exit9
        tay
        jsr PowerUpMusic
        ldx $4C
        iny
        beq +
        lda $074B,x
        sta $08
        lda $0748,x
        sta $09
        jsr LDC1C
+       lda $0748,x
        tay
        cpy #$08
        bcs ++
        cpy #$06
        bcc +
        lda SamusGear
        and #$3F
        sta SamusGear
+       jsr MakeBitMask
        ora SamusGear
        sta SamusGear
-       lda #$FF
        sta $0109
        sta $0748,x
        ldy $79
        beq +
        ldy #$01
+       sty $79
        jmp SelectSamusPal
Exit9:  rts

++      beq +
        lda #5
        jsr AddToMaxMissiles
        bne -           ; branch always
+       lda TankCount
        cmp #$06        ; has Samus got 6 energy tanks?
        beq +           ; then she can't have any more
        inc TankCount   ; otherwise give her a new tank
+       lda TankCount
        jsr Amul16      ; shift into upper nibble
        ora #$09
        sta HealthHi
        lda #$99
        sta HealthLo    ; health is now FULL!
        bne -           ; branch always

LDC1C:  lda MapPosX
        sta $07
        lda MapPosY
        sta $06
        lda ScrollDir
        lsr a
        php
        beq ++
        bcc +
        lda ScrollX
        beq +
        dec $07
        bcs +
++      bcc +
        lda ScrollY
        beq +
        dec $06
+       lda PPUCNT0ZP
        eor $08
        and #$01
        plp
        clc
        beq +
        adc $07
        sta $07
        jmp LDC51

+       adc $06
        sta $06
LDC51:  jsr LDC67
LDC54:  ldy $6886
        lda $06
        sta $6887,y
        lda $07
        sta $6888,y
        iny
        iny
        sty $6886
        rts

LDC67:  lda $07
        jsr Amul32       ; * 32
        ora $06
        sta $06
        lsr $07
        lsr $07
        lsr $07
        lda $09
        asl a
        asl a
        ora $07
        sta $07
        rts

LDC7F:  jsr LF186
LDC82:  jsr LF172
        jsr LF1A7
        jmp LF1FA

; Table

Table0F .db $00
        .db $80
        .db $C0
        .db $40

; UpdateObjAnim
; =============
; Advance to object's next frame of animation

        UpdateObjAnim:
        ldx PageIndex
        ldy AnimDelay,x
        beq +           ; is it time to advance to the next anim frame?
        dec AnimDelay,x     ; nope
        bne ++          ; exit if still not zero (don't update animation)
+       sta AnimDelay,x     ; set initial anim countdown value
        ldy AnimIndex,x
-       lda $8572,y     ; load frame number
        cmp #$FF        ; has end of anim been reached?
        beq +
        sta AnimFrame,x     ; store frame number
        iny             ; inc anim index
        tya
        sta AnimIndex,x     ; store anim index
++      rts

+       ldy AnimResetIndex,x     ; reset anim frame index
        jmp -           ; do first frame of animation

LDCB7:  pha
        lda #$00
        sta $06
        pla
        bpl +
        dec $06
+       clc
        rts

LDCC3:  ldy #$00
        sty $0F
        lda ($00),y
        sta $04
        tax
        jsr Adiv16       ; / 16
        and #$03
        sta $05
        txa
        and #$C0
        ora #$20
        ora $05
        sta $05
        lda $6B
        and #$10
        asl a
        asl a
        eor $04
        sta $04
        lda $6B
        bpl +
        asl $6B
        jsr LE038
+       txa
        and #$0F
        asl a
        tax
        rts

LDCF5:  jsr LDF2D
        pla
        pla
        ldx PageIndex
LDCFC:  lda $74
        cmp #$13
        bne +
        lda $6B02,x
        cmp #$04
        beq ++
        cmp #$02
        beq ++
+       lda $040C,x
        asl a
        bmi LDD75
        jsr LF74B
        sta $00
        jsr $80B0
        and #$20
        sta $6B02,x
        lda #$05
        sta $6AF4,x
        lda #$60
        sta $040D,x
        lda $2E
        cmp #$10
        bcc LDD5B
--      and #$07
        tay
        lda Table88,y
        sta $6AF7,x
        cmp #$80
        bne +
        ldy $93
        cpy $95
        beq LDD5B
        lda MaxMissiles
        beq LDD5B
        inc $95
-       rts

+       ldy $94
        cpy $96
        beq LDD5B
        inc $96
        cmp #$89
        bne -
        lsr $00
        bcs -
LDD5B:  ldx PageIndex
        lda $74
        cmp #$13
        beq +
++      jmp KillObject

+       lda $2E
        ldy #$00
        sty $96
        sty $95
        iny
        sty $93
        sty $94
        bne --

LDD75:  jsr PowerUpMusic
        lda $74
        and #$0F
        sta $0108
        lsr a
        tay
        sta MaxMissiles,y
        lda #75
        jsr AddToMaxMissiles
        bne LDD5B
LDD8B:  ldx PageIndex
        lda $6AF7,x
        cmp #$F7
        bne ++
        jmp LDF2D

; AddToMaxMissiles
; ================
; Adds A to both MissileCount & MaxMissiles, storing the new count
; (255 if it overflows)

        AddToMaxMissiles:
        pha
        clc
        adc MissileCount
        bcc +
        lda #$FF
+       sta MissileCount
        pla
        clc
        adc MaxMissiles
        bcc +
        lda #$FF
+       sta MaxMissiles
        rts

++      lda $0400,x
        sta $0A         ; Y coord
        lda $0401,x
        sta $0B         ; X coord
        lda $6AFB,x
        sta $06         ; hi coord
        lda $6AF7,x
        asl a
        tay
        lda ($41),y
        bcc +
        lda ($43),y
+       sta $00
        iny
        lda ($41),y
        bcc +
        lda ($43),y
+       sta $01
        jsr LDCC3
        tay
        lda ($45),y
        sta $02
        iny
        lda ($45),y
        sta $03
        ldy #$00
        cpx #$02
        bne +
        ldx PageIndex
        inc $0406,x
        lda $0406,x
        pha
        and #$03
        tax
        lda $05
        and #$3F
        ora Table0F,x
        sta $05
        pla
        cmp #$19
        bne +
        jmp LDCF5

+       ldx PageIndex
        iny
        lda ($00),y
        sta $6AF5,x
        jsr LDE3D
        iny
        lda ($00),y
        sta $6AF6,x
        sta $09
        iny
        sty $11
        jsr LDFDF
        txa
        asl a
        sta $08
        ldx PageIndex
        lda $0405,x
        and #$FD
        ora $08
        sta $0405,x
        lda $08
        beq ++
        jmp LDEDE

; Table

Table88 .db $80
        .db $81
        .db $89
        .db $80
        .db $81
        .db $89
        .db $81
        .db $89

LDE3D:  sec
        sbc #$10
        bcs +
        lda #$00
+       sta $08
        rts

        AnimDrawObject:
        jsr UpdateObjAnim
        DrawFrame:
        ldx PageIndex
        lda AnimFrame,x
        cmp #$F7            ; is the frame valid?
        bne +               ; branch if yes
++      jmp LDF2D
+       cmp #$07
        bne +
        lda $6B
        and #$EF
        sta $6B
+       lda ObjectY,x
        sta $0A
        lda ObjectX,x
        sta $0B
        lda ObjectHi,x
        sta $06
        lda AnimFrame,x
        asl a
        tax
        lda FramePtrTable,x
        sta $00
        lda FramePtrTable+1,x
        sta $01
        jsr LDCC3
        lda PlacePtrTable,x
        sta $02
        lda PlacePtrTable+1,x
        sta $03
        lda IsSamus
        beq +
    ; special case for Samus
        cpx #$0E
        bne +
        ldx PageIndex
        inc $65
        lda $65
        pha
        and #$03
        tax
        lda $05
        and #$3F
        ora Table0F,x
        sta $05
        pla
        cmp #$19
        bne +
        ldx PageIndex
        lda #$08
        sta ObjAction,x
        lda #$28
        sta AnimDelay,x
        pla
        pla
        jmp LDF2D

+       ldx PageIndex
        iny
        lda ($00),y
        sta ObjRadY,x
        jsr LDE3D
        iny
        lda ($00),y
        sta ObjRadX,x
        sta $09
        iny
        sty $11
        jsr LDFDF
        txa
        ldx PageIndex
        sta $030B,x
        tax
        beq +
LDEDE:  ldx SpritePagePos
        jmp LDF19       ; start drawing object

+       jmp LDF2D
-       ldy $0F         ; placement data pos
        jsr LDF6B
        adc $10         ; add initial Y pos
        sta SpriteRAM,x     ; store sprite Y coord
        dec SpriteRAM,x     ; because PPU uses Y + 1 as real Y coord
        inc $0F         ; advance to X placement
        ldy $11         ; frame data pos
        lda ($00),y     ; tile value
        sta SpriteRAM+1,x
        lda $6B
        asl a
        asl a
        and #$40
        eor $05
        sta SpriteRAM+2,x
        inc $11
        ldy $0F
        jsr LDFA3
        adc $0E         ; add initial X pos
        sta SpriteRAM+3,x     ; store sprite X coord
        inc $0F         ; advance to next placement
        inx             ; advance
        inx             ;  to
        inx             ;  next
        inx             ;  sprite
LDF19:  ldy $11         ; get frame data pos
LDF1B:  lda ($00),y
        cmp #$FC
        bcc -           ; if < FC, do another tile
        beq +++
        cmp #$FD
        beq ++
        cmp #$FE
        beq +
        stx SpritePagePos
LDF2D:  lda #$00
        sta $6B
        rts

+       inc $0F
        inc $0F
        inc $11
        jmp LDF19

++      iny
        asl $6B
        bcc +
        jsr LE038
        bne ++
+       lsr $6B
        lda ($00),y
        sta $05
++      iny
        sty $11
        jmp LDF1B

+++     iny
        lda ($00),y
        clc
        adc $10
        sta $10
        inc $11
        inc $11
        ldy $11
        lda ($00),y
        clc
        adc $0E
        sta $0E
        inc $11
        jmp LDF19

LDF6B:  lda ($02),y
        tay
        and #$F0
        cmp #$80
        beq +
        tya
-       bit $04
        bmi LDFB1
        clc
        rts

+       tya
        and #$0E
        lsr a
        tay
        lda Table10,y
        ldy IsSamus
        bne +
        ldy PageIndex
        adc $0406,y
        jmp ++
    ; special case for Samus
+       adc $65
++      tay
        lda Table10+2,y
        pha
        lda $0F
        clc
        adc #$0C
        tay
        pla
        clc
        adc ($02),y
        jmp -

LDFA3:  lda ($02),y
        tay
        and #$F0
        cmp #$80
        beq ++
        tya
-       bit $04
        bvc +
LDFB1:  eor #$FF
        sec
        adc #$F8
+       clc
        rts

++      ldy PageIndex
        lda $0406,y
        ldy IsSamus
        beq +
        lda $65
+       asl a
        pha
        ldy $0F
        lda ($02),y
        lsr a
        bcs +
        pla
        eor #$FF
        adc #$01
        pha
+       lda $0F
        clc
        adc #$0C
        tay
        pla
        clc
        adc ($02),y
        jmp -

LDFDF:  ldx #$01
        lda $0A         ; ObjectY
        tay
        sec
        sbc ScrollY
        sta $10         ; ScreenY
        lda $0B         ; ObjectX
        sec
        sbc ScrollX
        sta $0E         ; ScreenX
        lda ScrollDir
        and #$02
        bne LE01C
        cpy ScrollY
        lda $06
        eor PPUCNT0ZP
        and #$01
        beq +
        bcs ++
        lda $10
        sbc #$0F
        sta $10
        lda $09
        clc
        adc $10
        cmp #$F0
        bcc +++
        clc
+       bcc ++
        lda $09
        cmp $10
        bcc +++
++      dex
+++     rts

LE01C:  lda $06
        eor PPUCNT0ZP
        and #$01
        beq +
        bcs ++
        lda $09
        clc
        adc $0E
        bcc +++
        clc
+       bcc ++
        lda $09
        cmp $0E
        bcc +++
++      dex
+++     rts

LE038:  lsr $6B
        lda ($00),y
        and #$C0
        ora $6B
        sta $05
        lda $6B
        ora #$80
        sta $6B
        rts

; Table

Table10 .db $00
        .db $18
        .db $30
        .db $FC
        .db $F8
        .db $F4
        .db $F0
        .db $EE
        .db $EC
        .db $EA
        .db $E8
        .db $E7
        .db $E6
        .db $E6
        .db $E5
        .db $E5
        .db $E4
        .db $E4
        .db $E3
        .db $E5
        .db $E7
        .db $E9
        .db $EB
        .db $EF
        .db $F3
        .db $F7
        .db $FB
        .db $FE
        .db $FC
        .db $FA
        .db $F8
        .db $F6
        .db $F4
        .db $F2
        .db $F0
        .db $EE
        .db $ED
        .db $EB
        .db $EA
        .db $E9
        .db $E8
        .db $E7
        .db $E6
        .db $E6
        .db $E6
        .db $E6
        .db $E6
        .db $E8
        .db $EA
        .db $EC
        .db $EE
        .db $FE
        .db $FC
        .db $FA
        .db $F8
        .db $F7
        .db $F6
        .db $F5
        .db $F4
        .db $F3
        .db $F2
        .db $F1
        .db $F1
        .db $F0
        .db $F0
        .db $EF
        .db $EF
        .db $EF
        .db $EF
        .db $EF
        .db $EF
        .db $F0
        .db $F0
        .db $F1
        .db $F2

; UpdateEnemyAnim
; ===============
; Advance to next frame of enemy's animation.
; Basically the same as UpdateSamusAnim, only for enemies.

        UpdateEnemyAnim:
        ldx PageIndex
        ldy $6AF4,x
        cpy #$05
        beq ++
        ldy $6AF8,x     ; anim delay
        beq +
        dec $6AF8,x
        bne ++
+       sta $6AF8,x
        ldy $6AFA,x     ; anim index
-       lda (EnemyAnimPtr),y
        cmp #$FF        ; end of anim?
        beq +++
        sta $6AF7,x     ; frame
        iny
        tya
        sta $6AFA,x     ; anim index
++      rts

+++     ldy $6AF9,x     ; reset anim index
        bcs -

; DisplayBar
; ==========
; Displays Samus' status bar components.

        DisplayBar:
        ldy #$00
        lda SpritePagePos
        pha             ; save sprite page pos
        tax
-       lda DataDisplay,y
        sta SpriteRAM,x
        inx
        iny
        cpy #(10*4)
        bne -
        stx SpritePagePos
        pla             ; restore initial sprite page pos
        tax
        lda HealthHi
        and #$0F        ; upper health digit
        jsr SPRWriteDigit
        lda HealthLo     
        jsr Adiv16      ; lower health digit
        jsr SPRWriteDigit
        ldy EndTimerHi
        iny
        bne ++          ; branch if Samus is in escape sequence
        ldy MaxMissiles
        beq +           ; don't show missile count if Samus has no containers
; display 3-digit missile count
        lda MissileCount
        jsr HexToDec
        lda $02         ; upper digit
        jsr SPRWriteDigit
        lda $01         ; middle digit
        jsr SPRWriteDigit
        lda $00         ; lower digit
        jsr SPRWriteDigit
        bne +++         ; branch always
; Samus has no missiles, erase missile sprite
+       lda #$FF        ; "blank" tile
        cpx #$F4
        bcs +++
        sta $020D,x     ; erase left half of missile
        cpx #$F0
        bcs +++
        sta $0211,x     ; erase right half of missile
        bne +++         ; branch always
; display 3-digit end sequence timer
++      lda EndTimerHi
        jsr Adiv16      ; upper timer digit
        jsr SPRWriteDigit
        lda EndTimerHi
        and #$0F        ; middle timer digit
        jsr SPRWriteDigit
        lda EndTimerLo
        jsr Adiv16      ; lower timer digit
        jsr SPRWriteDigit
        lda #$58        ; "TI"  sprite (left half of "TIME")
        sta SpriteRAM+1,x
        inc SpriteRAM+2,x
        cpx #$FC
        bcs +++
        lda #$59        ; "ME" sprite (right half of "TIME")
        sta SpriteRAM+5,x
        inc SpriteRAM+6,x
+++     ldx SpritePagePos
        lda TankCount
        beq ++          ; exit if Samus has no tanks
; display full/empty energy tanks
        sta $03         ; temp store tank count
        lda #$40        ; X coord of right-most energy tank
        sta $00         ; (they are drawn from right to left)
        ldy #$6F        ; "full energy tank" tile
        lda HealthHi
        jsr Adiv16      ; / 16. A contains # of full energy tanks
        sta $01
        bne AddTanks    ; branch if at least 1 tank is full
        dey             ; else switch to "empty energy tank" tile

        AddTanks:
        jsr AddOneTank
        dec $01         ; any more full energy tanks left?
        bne +           ; branch if yes
        dey             ; otherwise, switch to "empty energy tank" tile
+       dec $03         ; done all tanks?
        bne AddTanks    ; if not, do another

        stx SpritePagePos
++      rts

; SPRWriteDigit
; =============
; In: A = value in range 0..9.
; #$A0 is added to A (the number sprites begin at $A0),
; and the result is stored as the tile # for the sprite indexed by X.

        SPRWriteDigit:
        ora #$A0
        sta SpriteRAM+1,x
        jmp Xplus4

; Add missile container to Samus' data display

        AddOneTank:
        lda #$17        ; Y coord - 1
        sta SpriteRAM,x
        tya             ; tile value
        sta SpriteRAM+1,x
        lda #$01        ; palette #
        sta SpriteRAM+2,x
        lda $00         ; X coord
        sta SpriteRAM+3,x
        sec
        sbc #$0A
        sta $00         ; X coord of next energy tank
        Xplus4:
        inx
        inx
        inx
        inx
        rts

; HexToDec
; ========
; Convert 8-bit value in A to 3 decimal digits.
; Upper digit stored in $02, middle in $01 and lower in $00.

        HexToDec:
        ldy #100
        sty $0A
        jsr GetDigit
        sty $02
        ldy #10
        sty $0A
        jsr GetDigit
        sty $01
        sta $00
        rts

        GetDigit:
        ldy #$00
        sec
-       iny
        sbc $0A
        bcs -
        dey
        adc $0A
        rts

; Sprite data for Samus' data display

        DataDisplay:
        .db $21,$A0,$01,$30     ; Upper health digit
        .db $21,$A0,$01,$38     ; Lower health digit
        .db $2B,$FF,$01,$28     ; Upper missile digit
        .db $2B,$FF,$01,$30     ; Middle missile digit
        .db $2B,$FF,$01,$38     ; Lower missile digit
        .db $2B,$5E,$00,$18     ; Left half of missile
        .db $2B,$5F,$00,$20     ; Right half of missile
        .db $21,$76,$01,$18     ; E
        .db $21,$7F,$01,$20     ; N
        .db $21,$3A,$00,$28     ; ..

; BitScan
; =======
; Input: A = any value.
; Output: A = index of first bit of A that's set (0..7, 8 if none set).

        BitScan:
        stx $0E         ; preserve X
        ldx #$00        ; first bit is bit 0
-       lsr a           ; transfer bit to carry flag
        bcs +           ; if the shifted bit was 1, we're done
        inx
        cpx #$08        ; tested all 8 bits?
        bne -
+       txa
        ldx $0E         ; restore X
-       rts

; ScrollDoor
; ==========
; Scrolls the screen if Samus is inside a door.

        ScrollDoor:
        ldx DoorStatus
        beq -           ; exit if Samus isn't in a door
        dex
        bne +
        jsr ScrollRight ; DoorStatus = 1 --> scroll 1 pixel right
        jmp ++

+       dex
        bne +
        jsr ScrollLeft  ; DoorStatus = 2 --> scroll 1 pixel left
++      ldx ScrollX
        bne Exit15
    ; scrolled one full screen, time to exit door
        ldx #$05
        bne DoneDoorScroll
+       dex
        bne +
        jsr ScrollDown
        jmp ++
+       dex
        bne Exit15
        jsr ScrollUp
++      ldx ScrollY
        bne Exit15
        stx $6C
        stx $6D
        inx
        lda ObjectX
        bmi ++
        inx
        bne ++
        DoneDoorScroll:
        lda #$20
        sta DoorDelay
        lda $58
        jsr Amul8       ; * 8
        bcs +
        ldy $57
        cpy #$03
        bcc ++
+       lda #$47
        bne +++
++      jsr ToggleScroll
+++     sta $FA
        stx $56         ; = 5
        Exit15:
        rts

        ToggleSamusHi:
        lda ObjectHi
        eor #$01
        sta ObjectHi
        rts

; ToggleScroll
; ============
; Toggles both mirroring and scroll direction when Samus has moved from
; a horizontal shaft to a vertical shaft or vice versa.

        ToggleScroll:
        lda ScrollDir
        eor #$03
        sta ScrollDir
        lda $FA
        eor #$08
        rts

LE25D:  lda #$01
        cmp ScrollDir
        bcs Exit21
        lda #$D8
        cmp ObjectY
Exit21: rts

LE269:  lda ObjAction
        cmp #sa_Elevator    ; is Samus in elevator?
        beq +
        cmp #sa_Dead
        bcs Exit21
+       jsr LE25D
        ldy #$FF
        bcs +++
        sty $72
        jsr LF323
        lda #$32
        sta SamusBlink
        lda FrameCount
        and #$03
        bne +
        jsr SFX_SamusJump
+       lda FrameCount
        lsr a
        and #$03
        bne ++
        lda SamusGear
        and #gr_VARIA
        beq +       ; branch if Samus doesn't have Varia
        bcc ++
+       lda #$07
        sta $6E
        jsr LCE92
++      ldy #$00
+++     iny
        sty $64
        jsr LE37A
        lda ObjectY
        sec
        sbc ScrollY
        sta SamusScrY
        lda $00
        bpl ++
        jsr GetAbsolute
        ldy $64
        beq +
        lsr a
        beq LE31A
+       sta $65
-       jsr MoveSamusUp
        bcs +
        sec
        ror $0308
        ror $0312
        jmp LE31A
+       dec $65
        bne -
++      beq LE31A
        ldy $64
        beq +
        lsr a
        lsr a
        beq LE31A
+       sta $65
-       jsr MoveSamusDown
        bcs +++
        lda ObjAction
        cmp #sa_Roll
        bne +
        lsr $0308
        beq ++
        ror $0312
        lda #$00
        sec
        sbc $0312
        sta $0312
        lda #$00
        sbc $0308
        sta $0308
        jmp LE31A

+       jsr SFX_SamusWalk       ; Samus has hit ground, make sum noise
++      jsr LD147
        sty $0314
        beq LE31A
+++     dec $65
        bne -
LE31A:  jsr LE3E5
        lda ObjectX
        sec
        sbc ScrollX
        sta SamusScrX
        lda $00
        bpl ++
        jsr GetAbsolute
        ldy $64
        beq +
        lsr a
        beq Exit10
+       sta $65
-       jsr MoveSamusLeft
        jsr LE365
        dec $65
        bne -
        lda $58
        beq Exit10
        lda #$01        ; door leads to the left
        bne +++         ; branch always
++      beq Exit10
        ldy $64
        beq +
        lsr a
        beq Exit10
+       sta $65
-       jsr MoveSamusRight
        jsr LE365
        dec $65
        bne -
        lda $58
        beq Exit10
        lda #$00        ; door leads to the right
+++     sta SamusDoorDir
Exit10: rts

LE365:  bcs Exit10
        lda #$01
        sta $65
        lda $0314
        bne Exit10
        lda ObjAction
        cmp #sa_Roll
        beq Exit10
        jmp LCF55

LE37A:  lda $0314
        bne ++
        lda #$18
        sta $0316
        lda ObjectY
        clc
        adc ObjRadY
        and #$07
        bne +
        jsr CheckMoveDown       ; check if Samus obstructed DOWNWARDS
        bcc ++          ; branch if yes
+       jsr LD976
        lda SamusOnElevator
        bne ++
        lda $7D
        bne ++
        lda #$1A
        sta $0314
++      ldx #$05
        lda $0312
        clc
        adc $0314
        sta $0312
        lda $0308
        adc #$00
        sta $0308
        bpl +
        lda #$00
        cmp $0312
        sbc $0308
        cmp #$06
        ldx #$FA
        bne ++
+       cmp #$05
++      bcc +
        jsr LD147
        stx $0308
+       lda $0310
        clc
        adc $0312
        sta $0310
        lda #$00
        adc $0308
        sta $00
        rts

LE3E5:  lda $0316
        jsr Amul16       ; * 16
        sta $00
        sta $02
        lda $0316
        jsr Adiv16       ; / 16
        sta $01
        sta $03
        lda $0313
        clc
        adc $0315
        sta $0313
        tax
        lda #$00
        bit $0315
        bpl +
        lda #$FF
+       adc $0309
        sta $0309
        tay
        bpl +
        lda #$00
        sec
        sbc $0313
        tax
        lda #$00
        sbc $0309
        tay
        jsr LE449
+       cpx $02
        tya
        sbc $03
        bcc +
        lda $00
        sta $0313
        lda $01
        sta $0309
+       lda $0311
        clc
        adc $0313
        sta $0311
        lda #$00
        adc $0309
        sta $00
        rts

LE449:  lda #$00
        sec
        sbc $00
        sta $00
        lda #$00
        sbc $01
        sta $01
        rts

; attempt to move Samus one pixel up

        MoveSamusUp:
        lda ObjectY
        sec
        sbc ObjRadY
        and #$07
        bne +           ; only call crash detection every 8th pixel
        jsr CheckMoveUp ; check if Samus obstructed UPWARDS
        bcc +++         ; exit if yes (can't move any further)
+       lda ObjAction
        cmp #sa_Elevator    ; is Samus in elevator?
        beq +
        jsr LD976
        lda $030A
        and #$42
        cmp #$42
        clc
        beq +++
+       lda SamusScrY
        cmp #$66        ; reached up scroll limit?
        bcs +           ; branch if not
        jsr ScrollUp
        bcc ++
+       dec SamusScrY
++      lda ObjectY
        bne ++
        lda ScrollDir
        and #$02
        bne +
        jsr ToggleSamusHi       ; toggle 9th bit of Samus' Y coord
+       lda #240
        sta ObjectY
++      dec ObjectY
        inc $030F
        sec
+++     rts

; attempt to move Samus one pixel down

        MoveSamusDown:
        lda ObjectY
        clc
        adc ObjRadY
        and #$07
        bne +                   ; only call crash detection every 8th pixel
        jsr CheckMoveDown       ; check if Samus obstructed DOWNWARDS
        bcc +++         ; exit if yes
+       lda ObjAction
        cmp #sa_Elevator        ; is Samus in elevator?
        beq +
        jsr LD976
        lda SamusOnElevator
        clc
        bne +++
        lda $7D
        bne +++
+       lda SamusScrY
        cmp #$84        ; reached down scroll limit?
        bcc +           ; branch if not
        jsr ScrollDown
        bcc ++
+       inc SamusScrY
++      lda ObjectY
        cmp #239
        bne ++
        lda ScrollDir
        and #$02
        bne +
        jsr ToggleSamusHi       ; toggle 9th bit of Samus' Y coord
+       lda #$FF
        sta ObjectY
++      inc ObjectY
        dec $030F
        sec
+++     rts

; Attempt to scroll UP

        ScrollUp:
        lda ScrollDir
        beq +
        cmp #$01
        bne +++
        dec ScrollDir
        lda ScrollY
        beq +
        dec MapPosY
+       ldx ScrollY
        bne +
        dec MapPosY     ; decrement MapY
        jsr GetRoomNum  ; put room # at current map pos in $5A
        bcs ++   ; if function returns CF = 1, moving up is not possible
        jsr LE9B7       ; switch to the opposite Name Table
        ldx #240        ; new Y coord
+       dex
        jmp LE53F

++      inc MapPosY
+++     sec
        rts

; Attempt to scroll DOWN

        ScrollDown:
        ldx ScrollDir
        dex
        beq +
        bpl +++
        inc ScrollDir
        lda ScrollY
        beq +
        inc MapPosY
+       lda ScrollY
        bne +
        inc MapPosY     ; increment MapY
        jsr GetRoomNum  ; put room # at current map pos in $5A
        bcs ++   ; if function returns CF = 1, moving down is not possible
+       ldx ScrollY
        cpx #239
        bne +
        jsr LE9B7       ; switch to the opposite Name Table
        ldx #$FF
+       inx
LE53F:  stx ScrollY
        jsr LE54A       ; check if it's time to update Name Table
        clc
        rts

++      dec MapPosY
+++     sec
-       rts

LE54A:  jsr SetupRoom
        ldx RoomNumber
        inx
        bne -
        lda ScrollDir
        and #$02
        bne +
        jmp LE571
+       jmp LE701

; Table

Table11 .db $07
        .db $00

PPUAddrs .db $20         ; hi byte of nametable #0 (PPU)
        .db $2C         ; hi byte of nametable #1 (PPU)
WRAMAddrs .db $60         ; hi byte of nametable #0 (WRAM)
        .db $64         ; hi byte of nametable #1 (WRAM)

        GetNameAddrs:
        jsr LEB85
        and #$01        ; A = nametable to update
        tay
        lda PPUAddrs,y  ; get high PPU addr of nametable (dest)
        ldx WRAMAddrs,y ; get high WRAM addr of nametable (src)
        rts

; check if it's time to update nametable (when scrolling is VERTICAL)

LE571:  ldx ScrollDir
        lda ScrollY
        and #$07        ; compare value = 0 if ScrollDir = down, else 7
        cmp Table11,x
        bne -           ; exit if not equal (no nametable update)
LE57C:  ldx ScrollDir
        cpx $4A
        bne -
        lda ScrollY
        and #$F8        ; keep upper 5 bits
        sta $00
        lda #$00
        asl $00
        rol a
        asl $00
        rol a
LE590:  sta $01         ; $0001 = (ScrollY & 0xF8) << 2 = row offset
        jsr GetNameAddrs
        ora $01
        sta $03
        txa
        ora $01
        sta $01
        lda $00
        sta $02
        lda ScrollDir
        lsr a           ; A = 0 if vertical scrolling, 1 if horizontal
        tax
        lda Table01,x
        sta $04
        ldy #$01
        sty PPUDataPending      ; data pending = YES
        dey
        ldx PPUStrIndex
        lda $03
        jsr WritePPUByte
        lda $02
        jsr WritePPUByte
        lda $04
        jsr LC3C6
-       lda ($00),y
        jsr WritePPUByte
        sty $06
        ldy #$01        ; WRAM pointer increment = 1...
        bit $04         ; ... if bit 7 (PPU inc) of $04 clear
        bpl +
        ldy #$20        ; else ptr inc = 32
+       jsr AddYToPtr00
        ldy $06
        dec $05
        bne -
        stx PPUStrIndex
        jsr EndPPUString

Table01 .db $20         ; horizontal write... PPU inc = 1, length = 32 tiles
        .db $9E         ; vertical write... PPU inc = 32, length = 30 tiles

LE5E2:  ldx #$C0
        lda $5A
        cmp #$F2
        beq +
        ldx #$E0
+       stx $00
        stx $02
        jsr GetNameAddrs
        ora #$03
        sta $03
        txa
        ora #$03
        sta $01
        lda #$01
        sta PPUDataPending      ; data pending = YES
        ldx PPUStrIndex
        lda $03
        jsr WritePPUByte
        lda $02
        jsr WritePPUByte
        lda #$20
        sta $04
        jsr WritePPUByte
        ldy #$00
-       lda ($00),y
        jsr WritePPUByte
        iny
        dec $04
        bne -
        stx PPUStrIndex
        jsr EndPPUString

; attempt to move Samus one pixel left

        MoveSamusLeft:
        lda ObjectX
        sec
        sbc ObjRadX
        and #$07
        bne +                   ; only call crash detection every 8th pixel
        jsr CheckMoveLeft       ; check if player is obstructed to the LEFT
        bcc +++         ; branch if yes! (CF = 0)
+       jsr LD976
        lda $030A
        and #$41
        cmp #$41
        clc
        beq +++
        lda SamusScrX
        cmp #$71        ; reached left scroll limit?
        bcs +           ; branch if not
        jsr ScrollLeft
        bcc ++
+       dec SamusScrX
++      lda ObjectX
        bne +
        lda ScrollDir
        and #$02
        beq +
        jsr ToggleSamusHi       ; toggle 9th bit of Samus' X coord
+       dec ObjectX
        sec
        rts

; crash with object on the left

+++     lda #$00
        sta $58
        rts

; attempt to move Samus one pixel right

        MoveSamusRight:
        lda ObjectX
        clc
        adc ObjRadX
        and #$07
        bne +                   ; only call crash detection every 8th pixel
        jsr CheckMoveRight      ; check if Samus is obstructed to the RIGHT
        bcc +++       ; branch if yes! (CF = 0)
+       jsr LD976
        lda $030A
        and #$41
        cmp #$40
        clc
        beq +++
        lda SamusScrX
        cmp #$8F        ; reached right scroll limit?
        bcc +           ; branch if not
        jsr ScrollRight
        bcc ++
+       inc SamusScrX
++      inc ObjectX      ; go right, Samus!
        bne +
        lda ScrollDir
        and #$02
        beq +
        jsr ToggleSamusHi       ; toggle 9th bit of Samus' X coord
+       sec
        rts

; crash with object on the right

+++     lda #$00
        sta $58
        rts

; Attempt to scroll LEFT

        ScrollLeft:
        lda ScrollDir
        cmp #$02
        beq +
        cmp #$03
        bne +++
        dec ScrollDir
        lda ScrollX
        beq +
        dec MapPosX
+       lda ScrollX
        bne +
        dec MapPosX     ; decrement MapX
        jsr GetRoomNum  ; put room # at current map pos in $5A
        bcs ++  ; if function returns CF=1, scrolling left is not possible
        jsr LE9B7       ; switch to the opposite Name Table
+       dec ScrollX
        jsr LE54A       ; check if it's time to update Name Table
        clc
        rts

++      inc MapPosX
+++     sec
        rts

; Attempt to scroll RIGHT

        ScrollRight:
        lda ScrollDir
        cmp #$03
        beq +
        cmp #$02
        bne +++
        inc ScrollDir
        lda ScrollX
        beq +
        inc MapPosX
+       lda ScrollX
        bne +
        inc MapPosX
        jsr GetRoomNum  ; put room # at current map pos in $5A
        bcs ++   ; if function returns CF=1, scrolling right is not possible
+       inc ScrollX
        bne +
        jsr LE9B7       ; switch to the opposite Name Table
+       jsr LE54A       ; check if it's time to update Name Table
        clc
        rts

++      dec MapPosX
+++     sec
-       rts

Table02 .db $07,$00

; check if it's time to update nametable (when scrolling is HORIZONTAL)

LE701:  ldx ScrollDir
        lda ScrollX
        and #$07        ; keep lower 3 bits
        cmp Table02-2,x ; compare value = 0 if ScrollDir = right, else 7
        bne -           ; exit if not equal (no nametable update)
LE70C:  ldx ScrollDir
        cpx $4A
        bne -
        lda ScrollX
        and #$F8        ; keep upper five bits
        jsr Adiv8       ; / 8 (make 'em lower five)
        sta $00
        lda #$00
        jmp LE590

; GetRoomNum
; ==========
; Description: Gets room number at current map position
; Returns: CF = 1 if room # at map position is FF
;          Else room # in $5A (RAM)

        GetRoomNum:
        lda ScrollDir
        lsr a
        beq +
        rol a
        adc #$FF
        pha
        jsr LEC93
        pla
        and $006C,y
        sec
        bne +++
+       lda MapPosY         ; map pos y
        jsr Amul16       ; * 16
        sta $00
        lda #$00
        rol a
        rol $00
        rol a
        sta $01
        lda $00
        adc MapPosX
        sta $00
        lda $01
        adc #$70
        sta $01         ; $0000 = (MapY*32)+MapX + 7000h
        ldy #$00
        lda ($00),y     ; load map entry!
        cmp #$FF        ; is it unused?
        beq +++       ; if so, exit with CF = 1
        sta RoomNumber  ; store room number
-       cmp $95D0,y     ; is it a special room?
        beq +           ; if so, branch
        iny             ; advance to next special room
        cpy #$07        ; tried 7 rooms yet?
        bne -           ; if not, try another
        lda $79
        beq ++
        lda #$80
        bne ++
+       lda #$01        ; creepy music = yes
++      sta $79
        clc             ; return CF = 0, function succeeded
+++     rts

LE770:  ldx PageIndex
        lda $6AF5,x
        clc
        adc #$08
        jmp LE783

LE77B:  ldx PageIndex
        lda #$00
        sec
        sbc $6AF5,x
LE783:  sta $02
        lda #$08
        sta $04
        jsr LE792
        lda $6AF6,x
        jmp LE7BD

LE792:  lda $0401,x
        sta $09     ; X coord
        lda $0400,x
        sta $08     ; Y coord
        lda $6AFB,x
        sta $0B     ; hi coord
        rts

        CheckMoveUp:
        ldx PageIndex
        lda ObjRadY,x
        clc
        adc #$08
        jmp +

        CheckMoveDown:
        ldx PageIndex
        lda #$00
        sec
        sbc ObjRadY,x
+       sta $02
        jsr LE8BE
        lda ObjRadX,x
LE7BD:  bne +
        sec
        rts

+       sta $03
        tay
        ldx #$00
        lda $09
        sec
        sbc $03
        and #$07
        beq +
        inx
+       jsr LE8CE
        sta $04
        jsr LE90F
        ldx #$00
        ldy #$08
        lda $00
LE7DE:  bne +++
        stx $06
        sty $07
        ldx $04

; object<-->background crash detection

LE7E6:  jsr MakeWRAMPtr ; set up ptr in $0004
        ldy #$00
        lda ($04),y     ; get tile value
        cmp #$4E
        beq LE81E
        jsr $95C0
        jsr LD651
        bcc Exit16      ; CF = 0 if tile # < $80 (solid tile)... CRASH!!!
        cmp #$A0        ; is tile >= A0h? (walkable tile)
        bcs IsWalkableTile
        jmp IsBlastTile  ; tile is $80-$9F (blastable tiles)

        IsWalkableTile:
        ldy IsSamus
        beq ++
    ; special case for Samus
        dey             ; = 0
        sty $58
        cmp #$A0        ; crash with tile A0h? (door)
        beq +
        cmp #$A1        ; crash with tile A1h? (door)
        bne ++
        inc $58
+       inc $58
++      dex
        beq +++
        jsr LE98E
        jmp LE7E6

+++     sec             ; no crash
        Exit16:
        rts

LE81E:  ldx $71
        beq ClcExit
        ldx #$06
-       lda $05
        eor $5D,x
        and #$04
        bne +++
        lda $04
        eor $5C,x
        and #$1F
        bne +++
        txa
        jsr Amul8       ; * 8
        ora #$80
        tay
        lda ObjAction,y
        beq +++
        lda $0307,y
        lsr a
        bcs ++
        ldx PageIndex
        lda ObjAction,x
        eor #$0B
        beq +
        lda ObjAction,x
        eor #$04
        bne PlaySnd4
        lda AnimResetIndex,x
        eor #$91
        bne PlaySnd4
+       lda $0683
        ora #$02
        sta $0683
++      lda #$04
        sta $030A,y
        bne ClcExit
+++     dex
        dex
        bpl -
        lda $04
        jsr Adiv8       ; / 8
        and #$01
        tax
        inc $0366,x
        ClcExit:
        clc
        rts

        PlaySnd4:
        jmp SFX_Metal

        CheckMoveLeft:
        ldx PageIndex
        lda ObjRadX,x
        clc
        adc #$08
        jmp +

        CheckMoveRight:
        ldx PageIndex
        lda #$00
        sec
        sbc ObjRadX,x
+       sta $03
        jsr LE8BE
        ldy ObjRadY,x
LE89B:  bne +
        sec
        rts

+       sty $02
        ldx #$00
        lda $08
        sec
        sbc $02
        and #$07
        beq +
        inx
+       jsr LE8CE
        sta $04
        jsr LE90F
        ldx #$08
        ldy #$00
        lda $01
        jmp LE7DE

LE8BE:  lda ObjectHi,x
        sta $0B
        lda ObjectY,x
        sta $08
        lda ObjectX,x
        sta $09
        rts

LE8CE:  eor #$FF
        clc
        adc #$01
        and #$07
        sta $04
        tya
        asl a
        sec
        sbc $04
        bcs +
        adc #$08
+       tay
        lsr a
        lsr a
        lsr a
        sta $04
        tya
        and #$07
        beq +
        inx
+       txa
        clc
        adc $04
        rts

LE8F1:  ldx PageIndex
        lda $6AF6,x
        clc
        adc #$08
        jmp LE904

LE8FC:  ldx PageIndex
        lda #$00
        sec
        sbc $6AF6,x
LE904:  sta $03
        jsr LE792
        ldy $6AF5,x
        jmp LE89B

LE90F:  lda $02
        bpl ++
        jsr LE95F
        bcs +
        cpx #$F0
        bcc +++
+       txa
        adc #$0F
        jmp LE934

++      jsr LE95F
        lda $08
        sec
        sbc $02
        tax
        and #$07
        sta $00
        bcs +++
        txa
        sbc #$0F
LE934:  tax
        lda ScrollDir
        and #$02
        bne +++
        inc $0B
+++     stx $02
        ldx #$00
        lda $03
        bmi +
        dex
+       lda $09
        sec
        sbc $03
        sta $03
        and #$07
        sta $01
        txa
        adc #$00
        beq +
        lda ScrollDir
        and #$02
        beq +
        inc $0B
+       rts

LE95F:  lda $08
        sec
        sbc $02
        tax
        and #$07
        sta $00
        rts

; MakeWRAMPtr
; ===========
; Makes pointer to WRAM nametable based on object's coordinates.
; In: $02 = ObjectY, $03 = ObjectX, $0B = ObjectHi
; Out: $04 = WRAM pointer

        MakeWRAMPtr:
        lda #$18
        sta $05
        lda $02         ; ObjectY
        and #$F8        ; keep upper 5 bits
        asl a
        rol $05
        asl a
        rol $05
        sta $04
        lda $03         ; ObjectX
        lsr a
        lsr a
        lsr a           ; A = ObjectX / 8
        ora $04
        sta $04
        lda $0B         ; ObjectYHi
        asl a
        asl a           ; A = ObjectYHi * 4
        and #$04
        ora $05
        sta $05
        rts

LE98E:  lda $02
        clc
        adc $06
        sta $02
        cmp #$F0
        bcc +
        adc #$0F
        sta $02
        lda ScrollDir
        and #$02
        bne +
        inc $0B
+       lda $03
        clc
        adc $07
        sta $03
        bcc +
        lda ScrollDir
        and #$02
        beq +
        inc $0B
+       rts

LE9B7:  lda PPUCNT0ZP
        eor #$03
        sta PPUCNT0ZP
        rts

        IsBlastTile:
        ldy $71
        beq Exit18
LE9C2:  tay
        jsr $95BD
        cpy #$98
        bcs +++
    ; attempt to find a vacant tile slot
        ldx #$C0
-       lda TileRoutine,x
        beq +           ; 0 = free slot
        jsr Xminus16
        bne -
        lda TileRoutine,x
        bne +++         ; no more slots, can't blast tile
+       inc TileRoutine,x
        lda $04
        and #$DE
        sta TileWRAMLo,x
        lda $05
        sta TileWRAMHi,x
        lda $74
        cmp #$11
        bne +
        cpy #$76
        bne +
        lda #$04
        bne ++
+       tya
        clc
        adc #$10
        and #$3C
        lsr a
++      lsr a
        sta TileType,x
+++     clc
Exit18: rts

LEA05:  jsr LEB85
        asl a
        asl a
        ora #$60
        sta WRAMPtr+1
        lda #$00
        sta WRAMPtr
        rts

--      lda RoomNumber
        and #$0F
        inc RoomNumber
        jsr GoRoutine

; Pointer table to code

        .dw ExitSub       ; rts
        .dw $E5E2
        .dw ExitSub       ; rts
        .dw $E5E2
        .dw $EA26

LEA26:  lda #$FF
        sta RoomNumber
-       rts

; SetupRoom
; =========

        SetupRoom:
        lda RoomNumber  ; room number
        cmp #$FF        ; is it FF?
        beq -           ; if so, exit
        cmp #$FE
        beq +
        cmp #$F0
        bcs --
        jsr LEC9B
        jsr ScanForItems ; set up special items
        lda RoomNumber  ; room number
        asl a           ; * 2
        tay             ; transfer to index Y register
        lda (RoomPtrTable),y     ; low byte of 16-bit room pointer
        sta RoomPtr
        iny
        lda (RoomPtrTable),y     ; high byte of 16-bit room pointer
        sta RoomPtr+1
        ldy #$00
        lda (RoomPtr),y ; first byte of room data
        sta RoomPal     ; store initial palette # to fill attrib table with
        lda #$01
        jsr AddToRoomPtr        ; add A to room data pointer
        jsr LEA05       ; set up destination WRAM address of decompressed room data
        jsr InitTables  ; clear Name Table & do initial Attrib Table setup
+       jmp DrawRoom

; DrawObject
; ==========

        DrawObject:
        sta $0E         ; store object pos (%yyyyxxxx)
        lda WRAMPtr
        sta WRAMWorkPtr
        lda WRAMPtr+1
        sta WRAMWorkPtr+1
        lda $0E         ; get object pos
        jsr Adiv16      ; / 16. Acc contains object ypos
        tax             ; transfer it to X, prepare for loop
        beq ++          ; skip the next piece of code if ypos is zero
-       lda WRAMWorkPtr ; lo byte of current nametable address
        clc
        adc #(2*32)     ; advance two rows on nametable (one y unit)
        sta WRAMWorkPtr
        bcc +
        inc WRAMWorkPtr+1
+       dex
        bne -           ; repeat until X is zero
++      lda $0E         ; get object pos
        and #$0F        ; A contains object xpos
        asl a           ; each x unit is 2 tiles
        adc WRAMWorkPtr
        sta WRAMWorkPtr ; update nametable pointer
        bcc +
        inc WRAMWorkPtr+1
+
; WRAMWorkPtr now points to the object's starting location (upper left
; corner) on the WRAM nametable
        iny
        lda (RoomPtr),y     ; load structure number
        tax             ; transfer to X reg
        iny
        lda (RoomPtr),y     ; load pal # of structure
        sta ObjectPal
        txa             ; restore struct number to A
        asl a           ; * 2
        tay
        lda (StructPtrTable),y  ; low byte of 16-bit structure ptr
        sta StructPtr
        iny
        lda (StructPtrTable),y  ; high byte of 16-bit structure ptr
        sta StructPtr+1
        jsr DrawStruct  ; draw one structure
        lda #$03
        jsr AddToRoomPtr        ; add A to room data pointer

; DrawRoom
; ========
; Draw room on WRAM nametable.

        DrawRoom:
        ldy #$00
        lda (RoomPtr),y     ; load next byte of room data
        cmp #$FF        ; is it FF (end-of-room)?
        beq EndOfRoom
        cmp #$FE        ; ???
        beq +
        cmp #$FD        ; is it FD (end-of-objects)?
        bne DrawObject  ; no, so it's another room object
        beq EndOfObjs   ; yes, set up enemies/doors
+       sta RoomNumber
        lda #$01

; adds A to 16-bit pointer

        AddToRoomPtr:
        clc
        adc RoomPtr
        sta RoomPtr
        bcc +
        inc RoomPtr+1
+       rts

; enemy/door handler

        EndOfObjs:
        lda RoomPtr
        sta $00
        lda RoomPtr+1
        sta $01
        lda #$01

        EnemyLoop:
        jsr AddToPtr00  ; add A to pointer at $00
        ldy #$00
        lda ($00),y
        cmp #$FF        ; end of enemy/door data?
        beq EndOfRoom   ; if so, branch
        and #$0F
        jsr GoRoutine

; Pointer table to code

        .dw ExitSub     ; rts
        .dw LoadEnemy
        .dw LoadDoor
        .dw ExitSub     ; rts
        .dw LoadElevator    ; Elevator
        .dw ExitSub     ; rts
        .dw LoadStatues     ; Kraid & Ridley statues
        .dw $EC57

        EndOfRoom:
        ldx #$F0
        stx RoomNumber
        lda ScrollDir
        sta $4A
        and #$02
        bne +
        jmp LE57C
+       jmp LE70C

; LoadEnemy
; =========

        LoadEnemy:
        jsr LEB0C
        jmp EnemyLoop   ; do next room object

LEB0C:  lda ($00),y     ; get 1st byte again
        and #$F0        ; keep ID tag
        tax
        jsr LEB7A
        bne +       ; exit if object slot taken
        iny
        lda ($00),y     ; get enemy #
        jsr LEB28
        ldy #$02
        lda ($00),y     ; get position (%yyyyxxxx)
        jsr LEB4D
        pha
-       pla
+       lda #$03        ; # of bytes to add to ptr at $0000
        rts

LEB28:  pha
        and #$C0
        sta $040F,x
        asl a
        bpl ++
        lda $74
        and #$06
        lsr a
        tay
        lda MaxMissiles,y
        beq +
        pla
        pla
        jmp -

+       lda #$01
        sta $6987
++      pla
        and #$3F
        sta $6B02,x
        rts

LEB4D:  tay
        and #$F0
        ora #$08
        sta $0400,x
        tya
        jsr Amul16       ; * 16
        ora #$0C
        sta $0401,x
        lda #$01
        sta $6AF4,x
        lda #$00
        sta $0404,x
        jsr LEB85
        sta $6AFB,x
        ldy $6B02,x
        asl $0405,x
        jsr LFB7B
        jmp LF85A

LEB7A:  lda $6AF4,x
        beq +
        lda $0405,x
        and #$02
+       rts

LEB85:  lda PPUCNT0ZP
        eor ScrollDir
        and #$01
        rts

; LoadDoor
; ========

        LoadDoor:
        jsr LEB92
-       jmp EnemyLoop    ; do next room object

LEB92:  iny
        lda ($00),y     ; door info byte
        pha
        jsr Amul16      ; CF = door side (0=right, 1=left)
        php
        lda MapPosX
        clc
        adc MapPosY
        plp
        rol a
        and #$03
        tay
        ldx $EC00,y
        pla             ; retrieve door info
        and #$03
        sta $0307,x     ; door palette
        tya
        pha
        lda $0307,x
        cmp #$01
        beq ++
        cmp #$03
        beq ++
        lda #$0A
        sta $09
        ldy MapPosX
        txa
        jsr Amul16       ; * 16
        bcc +
        dey
+       tya
        jsr LEE41
        jsr LEE4A
        bcs +
++      lda #$01
        sta ObjAction,x
+       pla
        and #$01        ; A = door side (0=right, 1=left)
        tay
        jsr LEB85
        sta ObjectHi,x
        lda DoorXs,y    ; get door's X coordinate
        sta ObjectX,x
        lda #$68        ; door Y coord is always #$68
        sta ObjectY,x
        lda LEBFE,y
        tay
        jsr LEB85
        eor #$01
        tax
        tya
        ora $6C,x
        sta $6C,x
        lda #$02
        rts

DoorXs  .db $F0         ; X coord of RIGHT door
        .db $10         ; X coord of LEFT door
LEBFE:  .db $02
        .db $01
LEC00:  .db $80
        .db $B0
        .db $A0
        .db $90

; LoadElevator
; ============

        LoadElevator:
        jsr LEC09
        bne -           ; branch always

LEC09:  lda $0320
        bne +           ; exit if elevator already present
        iny
        lda ($00),y
        sta $032F
        ldy #$83
        sty $032D       ; elevator Y coord
        lda #$80
        sta $032E       ; elevator X coord
        jsr LEB85
        sta $032C       ; high Y coord
        lda #$23
        sta $0323       ; elevator frame
        inc $0320       ; 1
+       lda #$02
        rts

; LoadStatues
; ===========

        LoadStatues:
        jsr LEB85
        sta $036C
        lda #$40
        ldx $687C
        bpl +           ; branch if Kraid statue not hit
        lda #$30
+       sta $0370
        lda #$60
        ldx $687B
        bpl +           ; branch if Ridley statue not hit
        lda #$50
+       sta $036F
        sty $54
        lda #$01
        sta $0360
--      jmp EnemyLoop   ; do next room object

LEC57:  ldx #$20
-       txa
        sec
        sbc #$08
        bmi +
        tax
        ldy $0728,x
        iny
        bne -
        ldy #$00
        lda ($00),y
        and #$F0
        sta $0729,x
        iny
        lda ($00),y
        sta $0728,x
        iny
        lda ($00),y
        tay
        and #$F0
        ora #$08
        sta $072A,x
        tya
        jsr Amul16       ; * 16
        ora #$00
        sta $072B,x
        jsr LEB85
        sta $072C,x
+       lda #$03
        bne --

LEC93:  lda PPUCNT0ZP
        eor #$01
        and #$01
        tay
        rts

LEC9B:  ldx ScrollDir
        dex
        ldy #$00
        jsr LED51
        iny
        jsr LED51
        ldx #$50
        jsr LEB85
        tay
-       tya
        eor $6AFB,x
        lsr a
        bcs +
        lda $0405,x
        and #$02
        bne +
        sta $6AF4,x
+       jsr Xminus16
        bpl -
        ldx #$18
-       tya
        eor $B3,x
        lsr a
        bcs +
        lda #$00
        sta $B0,x
+       txa
        sec
        sbc #$08
        tax
        bpl -
        jsr LED65
        jsr LED5B
        jsr LEB85
        asl a
        asl a
        tay
        ldx #$C0
-       tya
        eor TileWRAMHi,x
        and #$04
        bne +
        sta $0500,x
+       jsr Xminus16
        cmp #$F0
        bne -
        tya
        lsr a
        lsr a
        tay
        ldx #$D0
        jsr LED7A
        ldx #$E0
        jsr LED7A
        ldx #$F0
        jsr LED7A
        tya
        sec
        sbc $032C
        bne +
        sta $0320
+       ldx #$1E
-       lda $0704,x
        bne +
        lda #$FF
        sta $0700,x
+       txa
        sec
        sbc #$06
        tax
        bpl -
        cpy $036C
        bne +
        lda #$00
        sta $0360
+       ldx #$18
-       tya
        cmp $072C,x
        bne +
        lda #$FF
        sta $0728,x
+       txa
        sec
        sbc #$08
        tax
        bpl -
        ldx #$00
        jsr LED8C
        ldx #$08
        jsr LED8C
        jmp $95AE

LED51:  txa
        eor #$03
        and $006C,y
-       sta $006C,y
        rts

LED5B:  jsr LEB85
        eor #$01
        tay
        lda #$00
        beq -
LED65:  ldx #$B0
-       lda ObjAction,x
        beq +
        lda $030B,x
        bne +
        sta ObjAction,x
+       jsr Xminus16
        bmi -
        rts

LED7A:  lda ObjAction,x
        cmp #$05
        bcc +
        tya
        eor ObjectHi,x
        lsr a
        bcs +
        sta ObjAction,x
+       rts

LED8C:  tya
        cmp $074B,x
        bne Exit11
        lda #$FF
        sta $0748,x
Exit11: rts

; Special item handler

        ScanForItems:
        lda $9598       ; lo byte of ptr to 1st item data
        sta $00
        lda $9599       ; hi byte of ptr to 1st item data

        ScanOneItem:
        sta $01
        ldy #$00
        lda ($00),y     ; load map Ypos of item
        cmp MapPosY     ; does it equal Samus' Ypos on map?
        beq +           ; if yes, check Xpos too
        bcs Exit11      ; exit if Ypos > MapPosY
        iny
        lda ($00),y     ; lo byte of ptr to next item data
        tax             ; safe-keep in X
        iny
        and ($00),y     ; AND with hi byte of item ptr
        cmp #$FF        ; if result is FFh, then this was the last item (item ptr = FFFF)
        beq Exit11      ; exit
        lda ($00),y     ; hi byte of ptr to next item data
        stx $00         ; write lo byte
        jmp ScanOneItem ; process next item

+       lda #$03
        jsr AddToPtr00  ; add A to pointer at $0000

        ScanItemX:
        ldy #$00
        lda ($00),y     ; load map Xpos of object
        cmp MapPosX     ; does it equal Samus' Xpos on map?
        beq +           ; if so, then load object
        bcs Exit11      ; exit if A > MapPosX
        iny
        jsr GetItemByte
        jmp ScanItemX   ; try next X coord

+       lda #$02
LEDD6:  jsr AddToPtr00  ; add A to pointer at $0000
        ldy #$00
        lda ($00),y     ; object type
        and #$0F
        jsr GoRoutine       ; GO!

; Code pointer table (used by above routine)

        .dw ExitSub       ; rts
        .dw $EDF8
        .dw $EDFE       ; power-up
        .dw $EE63
        .dw $EEA1
        .dw $EEA6
        .dw $EEAE
        .dw $EECA
        .dw $EEEE
        .dw $EEF4
        .dw $EEFA

LEDF8:  jsr LEB0C
-       jmp LEDD6

; power-up item handler

LEDFE:  iny
        ldx #$00
        lda #$FF
        cmp $0748
        beq +
        ldx #$08
        cmp $0750
        bne ++
+       lda ($00),y     ; power-up #
        jsr LEE3D
        jsr LEE4A
        bcs ++      ; exit if CF set
        ldy #$02
        lda $09
        sta $0748,x     ; store power-up #
        lda ($00),y     ; %yyyyxxxx
        tay             ; safe-keep
        and #$F0        ; keep Y coord
        ora #$08        ; + 8
        sta $0749,x     ; store center Y coord
        tya             ; restore %yyyyxxxx
        jsr Amul16       ; * 16
        ora #$08        ; + 8
        sta $074A,x     ; store center X coord
        jsr LEB85
        sta $074B,x
++      lda #$03
        bne -           ; branch always

                        ; NOTE: If you trace the code from "bne -",
                        ; you will see that this is an indirect exit. What
                        ; it does is this: #$03 is added to the item data
                        ; pointer, and the byte at the new address is used
                        ; as an index into the code pointer table above.
                        ; The byte always seems to be 0, so the code will
                        ; jump to an RTS ($C45C).

LEE3D:  sta $09
        lda MapPosX
LEE41:  sta $07
        lda MapPosY
        sta $06
        jmp LDC67

LEE4A:  ldy $6886
        beq ++
-       lda $07
        cmp $6886,y
        bne +
        lda $06
        cmp $6885,y
        beq +++
+       dey
        dey
        bne -
++      clc
+++     rts

LEE63:  ldx #$18
        lda $2E
        adc FrameCount
        sta $8A
-       jsr LEE86
        txa
        sec
        sbc #$08
        tax
        bpl -
        lda $95E4
        sta $6BE9
        sta $6BEA
        lda #$01
        sta $6BE4
-       jmp LEDD6

LEE86:  lda $B0,x
        bne +
        txa
        adc $8A
        and #$7F
        sta $B1,x
        adc $2F
        sta $B2,x
        jsr LEB85
        sta $B3,x
        lda #$01
        sta $B0,x
        rol $8A
+       rts

LEEA1:  jsr LEC09
        bne -           ; branch always

LEEA6:  jsr $95B1
        lda #$02
-       jmp LEDD6

LEEAE:  jsr $95B4
        lda #$38
        sta $07
        lda #$00
        sta $06
        jsr LEE4A
        bcc LEEC6
        lda #$08
        sta $98
        lda #$00
        sta $99
LEEC6:  lda #$01
        bne -

LEECA:  jsr $95B7
        txa
        lsr a
        adc #$3C
        sta $07
        lda #$00
        sta $06
        jsr LEE4A
        bcc +
        lda #$81
        sta $0758,x
        lda #$01
        sta $075D,x
        lda #$07
        sta $075B,x
+       jmp LEEC6

LEEEE:  jsr $95BA
        jmp LEEC6

LEEF4:  jsr LEB92
        jmp LEDD6

LEEFA:  lda ScrollDir
        sta $91
        bne LEEC6

        GetItemByte:
        lda ($00),y
        cmp #$FF        ; end of data reached?
        bne AddToPtr00  ; if not, A is amount to add to ptr
        pla
        pla
        rts

; add A to pointer at $0000

        AddToPtr00:
        clc
        adc $00
        sta $00
        bcc +
        inc $01
+       rts

; DrawStructRow
; =============
; draws one row of the structure
; A = # of 2x2 tile macros to draw horizontally

        DrawStructRow:
        and #$0F        ; row length (in macros), range 00..0F
        bne +
        lda #$10
+       sta $0E         ; store horizontal macro count
        lda (StructPtr),y      ; get length byte again
        jsr Adiv16       ; / 16. Acc contains relative x coord
        asl a           ; * 2, because a macro is 2 tiles wide
        adc WRAMWorkPtr
        sta $00
        lda #$00
        adc WRAMWorkPtr+1
        sta $01         ; $0000 = workptr

        DrawMacro:
        lda $01         ; high byte of current nametable address
        cmp #$63
        beq +           ; check if end of nametable #0 reached
        cmp #$67
        bcc ++          ; draw macro
        beq +           ; check if end of nametable #1 reached
        rts

+       lda $00         ; low byte of current nametable address
        cmp #$A0        ; reached attrib table?
        bcc ++          ; if not, draw the macro
        rts             ; can't draw any more of the structure, exit

++      inc $10         ; increase struct data index
        ldy $10         ; struct data index in Y
        lda (StructPtr),y     ; get macro #
        asl a
        asl a           ; A = macro # * 4. Each macro is 4 bytes long
        sta $11         ; store macro index
        ldx #$03        ; prepare to copy four tile #'s
-       ldy $11         ; macro index in Y
        lda (MacroPtr),y     ; get tile #
        inc $11         ; increase macro index
        ldy TilePosTable,x   ; get buffer index
        sta ($00),y     ; write tile #
        dex             ; done four tiles yet?
        bpl -           ; if not, do another
        jsr UpdateAttrib ; update attribute table if necessary
        ldy #$02        ; macrowidth (in tiles)
        jsr AddYToPtr00
        lda $00         ; low byte of current nametable address
        and #$1F        ; still inside nametable?
        bne +           ; branch = yes, do another macro
; need to "clip" structure
        lda $10         ; struct index
        clc
        adc $0E         ; + number of macros remaining
        sec
        sbc #$01        ; - 1
        jmp AdvanceRow
+       dec $0E         ; drawn all macros on this row?
        bne DrawMacro   ; if not, draw another

        lda $10         ; struct index
        AdvanceRow:
        sec
        adc StructPtr
        sta StructPtr   ; update the struct pointer
        bcc +
        inc StructPtr+1
+       lda #(2*32)
        clc
        adc WRAMWorkPtr ; advance two rows in nametable
        sta WRAMWorkPtr
        bcc DrawStruct
        inc WRAMWorkPtr+1

; DrawStruct
; ==========
; Draws one structure on the WRAM nametable.

        DrawStruct:
        ldy #$00
        sty $10         ; reset struct index
        lda (StructPtr),y     ; load data byte
        cmp #$FF        ; end-of-struct?
        beq +           ; if so, exit
        jmp DrawStructRow       ; draw a row of macros
+       rts

; Tile placement table

TilePosTable    .db $21
                .db $20
                .db $01
                .db $00

; UpdateAttrib
; ============
; Updates attribute bits for one 2x2 tile section on the screen.
; All this code just to modify *TWO* bits -- whew.

        UpdateAttrib:
        lda ObjectPal   ; load pal # of structure
        cmp RoomPal     ; is it the same as the room's default pal?
        beq ++          ; then no need to modify the attribute table, exit

; figure out WRAM address of the byte containing the relevant bits

        lda $00
        sta $02
        lda $01
        lsr a
        ror $02
        lsr a
        ror $02
        lda $02
        and #$07
        sta $03
        lda $02
        lsr a
        lsr a
        and #$38
        ora $03
        ora #$C0
        sta $02
        lda #$63
        sta $03         ; $0002 contains ptr to attribute byte

; Figure out palette selector (0..3)

        ldx #$00
        bit $00
        bvc +
        ldx #$02
+       lda $00
        and #$02
        beq +
        inx

; X now contains which 2x2 tile section's palette to modify:
; +---+---+
; | 0 | 1 |
; +---+---+
; | 2 | 3 |
; +---+---+
; Where each box represents 2x2 tiles, and the value inside the
; corresponding X value

+       lda $01
        and #$04
        ora $03
        sta $03
        lda PalMaskTable,x
        ldy #$00
        and ($02),y     ; clear the old palette bits
        sta ($02),y
        lda ObjectPal   ; palette # (0..3)
-       dex
        bmi +
        asl a
        asl a           ; palette bits shifted one step left
        bcc -           ; branch always
+       ora ($02),y     ; set palette bits
        sta ($02),y
++      rts

PalMaskTable    .db %11111100
                .db %11110011
                .db %11001111
                .db %00111111

; InitTables
; ==========

        InitTables:
        lda WRAMPtr+1   ; $60 or $64
        tay
        tax
        iny
        iny
        iny
        lda #$FF        ; value to fill WRAM nametable with
        jsr FillWRAMTable
        ldx $01
        jsr Xplus4       ; X = X + 4
        stx $01

; fill attribute table with initial pal #

        ldx RoomPal     ; pal # (2-bit)
        lda PalLookup,x
        ldy #$C0        ; prepare to fill entire attribute table
-       sta ($00),y     ; write attribute bits
        iny
        bne -
        rts

PalLookup       .db %00000000
                .db %01010101
                .db %10101010
                .db %11111111

        FillWRAMTable:
        pha
        txa
        sty $01
        clc
        sbc $01
        tax
        pla
        ldy #$00
        sty $00
-       sta ($00),y
        dey
        bne -
        dec $01
        inx
        bne -
        rts

; Crash detection
; ===============

LF034:  lda #$FF
        sta $73
        sta $010F
    ; check for crash with Memus
        ldx #$18
--      lda $B0,x
        beq +++             ; branch if no Memu in slot
        cmp #$03
        beq +++
        jsr LF19A
        jsr IsSamusDead
        beq +
        lda SamusBlink
        bne +
        ldy #$00
        jsr LF149
        jsr LF2B4
    ; check for crash with bullets
+       ldy #$D0
-       lda ObjAction,y       ; projectile active?
        beq ++                  ; try next one if not
        cmp #wa_BulletExplode
        bcc +
        cmp #$07
        beq +
        cmp #wa_BombExplode
        beq +
        cmp #wa_Missile
        bne ++
+       jsr LF149
        jsr LF32A
++      jsr Yplus16
        bne -
+++     txa
        sec
        sbc #$08                ; each Memu occupies 8 bytes
        tax
        bpl --

        ldx #$B0
-       lda ObjAction,x
        cmp #$02
        bne +
        ldy #$00
        jsr IsSamusDead
        beq ++
        jsr LDC7F
        jsr LF277
+       jsr Xminus16
        bmi -
; enemy <--> bullet/missile/bomb detection
++      ldx #$50                ; start with enemy slot #5
LF09F:  lda $6AF4,x             ; slot active?
        beq +                   ; branch if not
        cmp #$03
+       beq NextEnemy           ; next slot
        jsr LF152
        lda $6AF4,x
        cmp #$05
        beq +++
        ldy #$D0                ; first projectile slot
-       lda ObjAction,y         ; is it active?
        beq ++                  ; branch if not
        cmp #wa_BulletExplode
        bcc +
        cmp #$07
        beq +
        cmp #wa_BombExplode
        beq +
        cmp #wa_Missile
        bne ++
; check if enemy is actually hit
+       jsr LF140
        jsr LF2CA
++      jsr Yplus16             ; next projectile slot
        bne -
+++     ldy #$00
        lda SamusBlink
        bne NextEnemy
        jsr IsSamusDead
        beq NextEnemy
        jsr LF140
        jsr LF282
        NextEnemy:
        jsr Xminus16
        bmi +
        jmp LF09F

+       ldx #$00
        jsr LF172
        ldy #$60
-       lda $6AF4,y
        beq +
        cmp #$05
        beq +
        lda SamusBlink
        bne +
        jsr IsSamusDead
        beq +
        jsr LF1B3
        jsr LF162
        jsr LF1FA
        jsr LF2ED
+       jsr Yplus16
        cmp #$C0
        bne -
        ldy #$00
        jsr IsSamusDead
        beq +++
        jsr LF186
        ldx #$F0
-       lda ObjAction,x
        cmp #$07
        beq +
        cmp #$0A
        bne ++
+       jsr LDC82
        jsr LF311
++      jsr Xminus16
        cmp #$C0
        bne -
+++     jmp LCE92

LF140:  jsr LF1BF
        jsr LF186
        jmp LF1FA

LF149:  jsr LF186
        jsr LF1D2
        jmp LF1FA

LF152:  lda $0400,x
        sta $07         ; Y coord
        lda $0401,x
        sta $09         ; X coord
        lda $6AFB,x     ; hi coord
        jmp LF17F

LF162:  lda $0400,y     ; Y coord
        sta $06
        lda $0401,y     ; X coord
        sta $08
        lda $6AFB,y     ; hi coord
        jmp LF193

LF172:  lda ObjectY,x
        sta $07
        lda ObjectX,x
        sta $09
        lda ObjectHi,x
LF17F:  eor PPUCNT0ZP
        and #$01
        sta $0B
        rts

LF186:  lda ObjectY,y
        sta $06
        lda ObjectX,y
        sta $08
        lda ObjectHi,y
LF193:  eor PPUCNT0ZP
        and #$01
        sta $0A
        rts

LF19A:  lda $B1,x
        sta $07
        lda $B2,x
        sta $09
        lda $B3,x
        jmp LF17F

LF1A7:  lda ObjRadY,x
        jsr LF1E0
        lda ObjRadX,x
        jmp LF1D9

LF1B3:  lda ObjRadY,x
        jsr LF1E7
        lda ObjRadX,x
        jmp LF1CB

LF1BF:  lda $6AF5,x
        jsr LF1E0
        lda $6AF6,x
        jmp LF1D9

LF1CB:  clc
        adc $6AF6,y
        sta $05
        rts

LF1D2:  lda #$04
        jsr LF1E0
        lda #$08
LF1D9:  clc
        adc ObjRadX,y
        sta $05
        rts

LF1E0:  clc
        adc ObjRadY,y
        sta $04
        rts

LF1E7:  clc
        adc $6AF5,y
        sta $04
        rts

; Y = Y + 16

        Yplus16:
        tya
        clc
        adc #$10
        tay
        rts

; X = X - 16

        Xminus16:
        txa
        sec
        sbc #$10
        tax
        rts

LF1FA:  lda #$02
        sta $10
        and ScrollDir
        sta $03
        lda $07
        sec
        sbc $06     ; Y
        sta $00
        lda $03
        bne ++
        lda $0B
        eor $0A
        beq ++
        jsr LF262
        lda $00
        sec
        sbc #$10
        sta $00
        bcs +
        dec $01
+       jmp LF22B

++      lda #$00
        sbc #$00
        jsr LF266
LF22B:  sec
        lda $01
        bne ++
        lda $00
        sta $11
        cmp $04
        bcs ++
        asl $10
        lda $09
        sec
        sbc $08
        sta $00
        lda $03
        beq +
        lda $0B
        eor $0A
        beq +
        jsr LF262
        jmp LF256

+       sbc #$00
        jsr LF266
LF256:  sec
        lda $01
        bne ++
        lda $00
        sta $0F
        cmp $05
++      rts

LF262:  lda $0B
        sbc $0A
LF266:  sta $01
        bpl +
        jsr LE449
        inc $10
+       rts

LF270:  ora $030A,x
        sta $030A,x
        rts

LF277:  bcs Exit17
LF279:  lda $10
LF27B:  ora $030A,y
        sta $030A,y
        Exit17:
        rts

LF282:  bcs Exit17
        jsr LF2E8
        jsr LCD9C
        ldy #$00
        bcc ++
        lda $6AF4,x
        cmp #$04
        bcs Exit17
        lda $6B02,x
-       sta $010F
        tay
        bmi +
        lda $968B,y
        and #$10
        bne Exit17
+       ldy #$00
        jsr LF338
        jmp LF306

++      lda #$81
        sta $040E,x
        bne ++
LF2B4:  bcs +
        jsr LCD9C
        ldy #$00
        lda #$C0
        bcs -
LF2BF:  lda $B6,x
        and #$F8
        ora $10
        eor #$03
        sta $B6,x
+       rts

LF2CA:  bcs +
        lda ObjAction,y
        sta $040E,x
        jsr LF279
++      jsr LF332
-       ora $0404,x
        sta $0404,x
+       rts

LF2DF:  lda $10
        ora $0404,y
        sta $0404,y
        rts

LF2E8:  jsr LF340
        bne -
LF2ED:  bcs +
        jsr LF2DF
        tya
        pha
        jsr LCD9C
        pla
        tay
        bcc +
        lda #$80
        sta $010F
        jsr LF332
        jsr LF270
LF306:  lda $95CE
        sta $6E
        lda $95CF
        sta $6F
+       rts

LF311:  bcs Exit22
        lda #$E0
        sta $010F
        jsr LF338
        lda $0F
        beq +
        lda #$01
+       sta $73
LF323:  lda #$00
        sta $6E
        sta $6F
Exit22: rts

LF32A:  bcs Exit22
        jsr LF279
        jmp LF2BF

LF332:  jsr LF340
        jmp Amul8       ; * 8

LF338:  lda $10
        asl a
        asl a
        asl a
        jmp LF27B

LF340:  lda $10
        eor #$03
        rts

; UpdateEnemies
; =============

        UpdateEnemies:
        ldx #$50
-       jsr DoOneEnemy
        ldx PageIndex
        jsr Xminus16
        bne -
        DoOneEnemy:
        stx PageIndex
        ldy $6AF4,x
        beq +
        cpy #$03
        bcs +
        jsr LF37F
+       jsr LF3AA
        lda $6AF4,x
        sta $81
        cmp #$07
        bcs +
        jsr GoRoutine

; Pointer table to code

        .dw ExitSub       ; rts
        .dw $F3BE
        .dw $F3E6
        .dw $F40D
        .dw $F43E
        .dw $F483
        .dw $F4EE

+       jmp KillObject

LF37F:  lda $0405,x
        and #$02
        bne +
        lda $0400,x     ; Y coord
        sta $0A
        lda $0401,x     ; X coord
        sta $0B
        lda $6AFB,x     ; hi coord
        sta $06
        lda $6AF5,x
        sta $08
        lda $6AF6,x
        sta $09
        jsr LDFDF
        txa
        bne +
        pla
        pla
+       ldx PageIndex
        rts

LF3AA:  lda $0405,x
        asl a
        rol a
        tay
        txa
        jsr Adiv16       ; / 16
        eor FrameCount
        lsr a
        tya
        ror a
        ror a
        sta $0405,x
        rts

LF3BE:  lda $0405,x
        asl a
        bmi +
        lda #$00
        sta $6B01,x
        sta $0406,x
        sta $040A,x
        jsr LF6B9
        jsr LF75B
        jsr LF682
        jsr LF676
        lda $0409,x
        beq +
        jsr LF7BA
+       jmp ++

LF3E6:  lda $0405,x
        asl a
        bmi ++
        lda $0405,x
        and #$20
        beq +
        ldy $6B02,x
        lda $96BB,y
        sta $0409,x
        dec $6AF4,x
        bne ++
+       jsr LF6B9
        jsr LF75B
        jsr LF51E
++      jsr LF536
        jmp $95E5

LF410:  jsr UpdateEnemyAnim
        jsr $8058
LF416:  ldx PageIndex
        lda $040F,x
        bpl +
        lda $6B
        bmi +
        lda #$A3
LF423:  sta $6B
+       lda $6AF4,x
        beq LF42D
        jsr LDD8B
LF42D:  ldx PageIndex
        lda #$00
        sta $0404,x
        sta $040E,x
        rts

LF438:  jsr UpdateEnemyAnim
LF43B:  jmp LF416

LF43E:  jsr LF536
        lda $6AF4,x
        cmp #$03
        beq LF410
        bit $6B
        bmi +
        lda #$A1
        sta $6B
+       lda FrameCount
        and #$07
        bne +
        dec $040D,x
        bne +
        lda $6AF4,x
        cmp #$03
        beq +
        lda $040C,x
        sta $6AF4,x
        ldy $6B02,x
        lda $969B,y
        sta $040D,x
+       lda $040D,x
        cmp #$0B
        bcs +
        lda FrameCount
        and #$02
        beq +
        asl $6B
+       jmp LF416

LF483:  lda $0404,x
        and #$24
        beq +++
        jsr KillObject
        ldy $6AF7,x
        cpy #$80
        beq PickupMissile
        tya
        pha
        lda $6B02,x
        pha
        ldy #$00
        ldx #$03
        pla
        bne ++
        dex
        pla
        cmp #$81
        bne +
        ldx #$00
        ldy #$50
+       pha
++      pla
        sty $6E
        stx $6F
        jsr LCEF9
        jmp SFX_EnergyPickup

        PickupMissile:
        lda #$02
        ldy $6B02,x
        beq +
        lda #$1E
+       clc
        adc MissileCount
        bcs +                   ; can't have more than 255 missiles
        cmp MaxMissiles         ; can Samus hold this many missiles?
        bcc ++                  ; branch if yes
+       lda MaxMissiles         ; set to max. # of missiles allowed
++      sta MissileCount
        jmp SFX_MissilePickup

+++     lda FrameCount
        and #$03
        bne +
        dec $040D,x
        bne +
        jsr KillObject
+       lda FrameCount
        and #$02
        lsr a
        ora #$A0
        sta $6B
        jmp LF416

LF4EE:  dec $040F,x
        bne ++
        lda $040C,x
        tay
        and #$C0
        sta $040F,x
        tya
        and #$3F
        sta $6AF4,x
        pha
        jsr $80B0
        and #$20
        beq +
        pla
        jsr LF515
        pha
+       pla
++      lda #$A0
        jmp LF423

LF515:  sta $040C,x
LF518:  lda #$04
        sta $6AF4,x
        rts

LF51E:  lda ScrollDir
        ldx PageIndex
        cmp #$02
        bcc +
        lda $0400,x     ; Y coord
        cmp #$EC
        bcc +
        jmp KillObject

-       jsr SFX_MetroidHit
        jmp GetPageIndex

LF536:  lda $040F,x
        sta $0A
        lda $0404,x
        and #$20
        beq +
        lda $040E,x
        cmp #$03
        bne ++
        bit $0A
        bvs ++
        lda $6AF4,x
        cmp #$04
        beq ++
        jsr LF515
        lda #$40
        sta $040D,x
        jsr $80B0
        and #$20
        beq +
        lda #$05
        sta $040B,x
        jmp $95A8
+       rts

--      jsr $80B0
        and #$20
        bne -
        jsr SFX_Metal
        jmp LF42D

++      lda $040B,x
        cmp #$FF
        beq --
        bit $0A
        bvc ++
        jsr SFX_MBrainHit
        bne +
++      jsr LF74B
        and #$0C
        beq PlaySnd1
        cmp #$04
        beq PlaySnd2
        cmp #$08
        beq PlaySnd3
        jsr SFX_MetroidHit
        bne +       ; branch always
        PlaySnd1:
        jsr SFX_EnemyHit
        bne +       ; branch always
        PlaySnd2:
        jsr SFX_EnemyHit
        bne +       ; branch always
        PlaySnd3:
        jsr LCBCE
+       ldx PageIndex
        jsr $80B0
        and #$20
        beq +
        lda $040E,x
        cmp #$0B
        bne --
+       lda $6AF4,x
        cmp #$04
        bne +
        lda $040C,x
+       ora $0A
        sta $040C,x
        asl a
        bmi +
        jsr $80B0
        and #$20
        bne +
        ldy $040E,x
        cpy #$0B
        beq +++
        cpy #$81
        beq +++
+       lda #$06
        sta $6AF4,x
        lda #$0A
        bit $0A
        bvc +
        lda #$03
+       sta $040F,x
        cpy #$02
        beq +
        bit $0A
        bvc ++
        ldy $040E,x
        cpy #$0B
        bne ++
        dec $040B,x
        beq +++
        dec $040B,x
        beq +++
+       dec $040B,x
        beq +++
++      dec $040B,x
        bne GetPageIndex
+++     lda #$03
        sta $6AF4,x
        bit $0A
        bvs +
        lda $040E,x
        cmp #$02
        bcs +
        lda #$00
        jsr LDCFC
        ldx PageIndex
+       jsr LF844
        lda $960B,y
        jsr LF68D
        sta $0406,x
        ldx #$C0
-       lda $6AF4,x
        beq +
        txa
        clc
        adc #$08
        tax
        cmp #$E0
        bne -
        beq GetPageIndex
+       lda $95DD
        jsr LF68D
        lda #$0A
        sta $0406,x
        inc $6AF4,x
        lda #$00
        bit $0A
        bvc +
        lda #$03
+       sta $0407,x
        ldy PageIndex
        lda $0400,y
        sta $0400,x
        lda $0401,y
        sta $0401,x
        lda $6AFB,y
        sta $6AFB,x
        GetPageIndex:
        ldx PageIndex
        rts

LF676:  jsr $80B0
        asl a
        asl a
        asl a
        and #$C0
        sta $6B03,x
        rts

LF682:  jsr LF844
        lda $963B,y
        cmp $6AF9,x
        beq +
LF68D:  sta $6AF9,x
LF690:  sta $6AFA,x
LF693:  lda #$00
        sta $6AF8,x
+       rts

LF699:  jsr LF844
        lda $965B,y
        cmp $6AF9,x
        beq Exit12
        jsr LF68D
        ldy $6B02,x
        lda $967B,y
        and #$7F
        beq Exit12
        tay
-       dec $6AFA,x
        dey
        bne -
Exit12: rts

LF6B9:  lda #$00
        sta $82
        jsr LF74B
        tay
        lda $6AF4,x
        cmp #$02
        bne +
        tya
        and #$02
        beq Exit12
+       tya
        dec $040D,x
        bne Exit12
        pha
        ldy $6B02,x
        lda $969B,y
        sta $040D,x
        pla
        bpl +++
        lda #$FE
        jsr LF7B3
        lda ScrollDir
        cmp #$02
        bcc +
        jsr LF752
        bcc +
        tya
        eor PPUCNT0ZP
        bcs ++
+       lda $0401,x
        cmp ObjectX
        bne +
        inc $82
+       rol a
++      and #$01
        jsr LF744
        lsr a
        ror a
        eor $0403,x
        bpl +++
        jsr $81DA
+++     lda #$FB
        jsr LF7B3
        lda ScrollDir
        cmp #$02
        bcs +
        jsr LF752
        bcc +
        tya
        eor PPUCNT0ZP
        bcs ++
+       lda $0400,x
        cmp ObjectY
        bne +
        inc $82
        inc $82
+       rol a
++      and #$01
        asl a
        asl a
        jsr LF744
        lsr a
        lsr a
        lsr a
        ror a
        eor $0402,x
        bpl +
        jmp $820F

LF744:  ora $0405,x
        sta $0405,x
+       rts

LF74B:  ldy $6B02,x
        lda $968B,y
        rts

LF752:  lda $6AFB,x
        tay
        eor ObjectHi
        lsr a
        rts

LF75B:  lda #$E7
        sta $06
        lda #$18
        jsr LF744
        ldy $6B02,x
        lda $96AB,y
        beq +++
        tay
        lda $0405,x
        and #$02
        beq ++
        tya
        ldy #$F7
        asl a
        bcs +
        ldy #$EF
+       lsr a
        sta $02
        sty $06
        lda ObjectY
        sta $00
        ldy $0400,x
        lda $0405,x
        bmi +
        ldy ObjectX
        sty $00
        ldy $0401,x
+       lda ObjectHi
        lsr a
        ror $00
        lda $6AFB,x
        lsr a
        tya
        ror a
        sec
        sbc $00
        bpl +
        jsr GetAbsolute
+       lsr a
        lsr a
        lsr a
        cmp $02
        bcc +++
++      lda $06
LF7B3:  and $0405,x
        sta $0405,x
+++     rts

LF7BA:  dec $0409,x
        bne +
        lda $0405,x
        and #$08
        bne ++
        inc $0409,x
+       rts

++      lda $6B02,x
        cmp #$07
        bne +
        jsr SFX_Zeb
        ldx PageIndex
+       inc $6AF4,x
        jsr LF699
        ldy $6B02,x
        lda $96CB,y
        clc
        adc #$D1
        sta $00
        lda #$00
        adc #$97
        sta $01
        lda FrameCount
        eor $2E
        ldy #$00
        and ($00),y
        tay
        iny
        lda ($00),y
        sta $0408,x
        jsr $80B0
        bpl ++
        lda #$00
        sta $0406,x
        sta $0407,x
        ldy $0408,x
        lda $972B,y
        sta $6AFE,x
        lda $973F,y
        sta $6AFF,x
        lda $9753,y
        sta $0402,x
        lda $9767,y
        sta $0403,x
        lda $0405,x
        bmi +
        lsr a
        bcc ++
        jsr $81D1
        jmp ++

+       and #$04
        beq ++
        jsr $8206
++      lda #$DF
        jmp LF7B3

LF83E:  lda $0405,x
LF841:  jmp +

LF844:  lda $0405,x
        bpl +
        lsr a
        lsr a
+       lsr a
        lda $6B02,x
        rol a
        tay
        rts

LF852:  txa
        lsr a
        lsr a
        lsr a
        adc FrameCount
        lsr a
        rts

LF85A:  ldy $6B02,x
        lda $969B,y
        sta $040D,x
        lda $962B,y
        ldy $040F,x
        bpl +
        asl a
+       sta $040B,x
-       rts

LF870:  lda $0405,x
        and #$10
        beq -
        lda $87
        and $6AF4,x
        beq -
        lda $87
        bpl +
        ldy $6B01,x
        bne -
+       jsr LF8E8
        bcs +
        sta $0404,y
        jsr LF92C
        lda $0405,x
        lsr a
        lda $85
        pha
        rol a
        tax
        lda $978B,x
        pha
        tya
        tax
        pla
        jsr LF68D
        ldx PageIndex
        lda #$01
        sta $6AF4,y
        and $0405,x
        tax
        lda Table15,x
        sta $0403,y
        lda #$00
        sta $0402,y
        ldx PageIndex
        jsr LF8F8
        lda $0405,x
        lsr a
        pla
        tax
        lda $97A3,x
        sta $04
        txa
        rol a
        tax
        lda $979B,x
        sta $05
        jsr LF91D
        ldx PageIndex
        bit $87
        bvc +
        lda $0405,x
        and #$01
        tay
        lda $0083,y
        jmp LF690

LF8E8:  ldy #$60
        clc
-       lda $6AF4,y
        beq +
        jsr Yplus16
        cmp #$C0
        bne -
+       rts

LF8F8:  lda $85
        cmp #$02
        bcc +
        ldx PageIndex
        lda $0405,x
        lsr a
        lda $88
        rol a
        and #$07
        sta $040A,y
        lda #$02
        sta $6AF4,y
        lda #$00
        sta $0409,y
        sta $6AF8,y
        sta $0408,y
+       rts

LF91D:  ldx PageIndex
        jsr LE792
        tya
        tax
        jsr LFD8F
        jmp LFA49

; Table used by above subroutine

Table15 .db $02
        .db $FE

LF92C:  lda #$02
        sta $6AF5,y
        sta $6AF6,y
        ora $0405,y
        sta $0405,y
        rts

LF93B:  ldx #$B0
-       jsr LF949
        ldx PageIndex
        jsr Xminus16
        cmp #$60
        bne -
LF949:  stx PageIndex
        lda $0405,x
        and #$02
        bne +
        jsr KillObject
+       lda $6AF4,x
        beq Exit19
        jsr GoRoutine

; Pointer table to code

        .dw ExitSub     ; rts
        .dw $F96A
        .dw LF991       ; spit dragon's fireball
        .dw ExitSub     ; rts
        .dw $FA6B
        .dw $FA91

Exit19: rts

LF96A:  jsr LFA5B
        jsr LFA1E
        ldx PageIndex
        bcs LF97C
        lda $6AF4,x
        beq Exit19
        jsr LFA60
LF97C:  lda #$01
LF97E:  jsr UpdateEnemyAnim
        jmp LDD8B

-       inc $0408,x
LF987:  inc $0408,x
        lda #$00
        sta $0409,x
        beq +
LF991:  jsr LFA5B
        lda $040A,x
        and #$FE
        tay
        lda $97A7,y
        sta $0A
        lda $97A8,y
        sta $0B
+       ldy $0408,x
        lda ($0A),y
        cmp #$FF
        bne +
        sta $0408,x
        jmp LF987

+       cmp $0409,x
        beq -
        inc $0409,x
        iny
        lda ($0A),y
        jsr $8296
        ldx PageIndex
        sta $0402,x
        lda ($0A),y
        jsr $832F
        ldx PageIndex
        sta $0403,x
        tay
        lda $040A,x
        lsr a
        php
        bcc +
        tya
        jsr GetAbsolute
        sta $0403,x
+       plp
        bne +
        lda $0402,x
        beq +
        bmi +
        ldy $040A,x
        lda $95E0,y
        sta $6AF9,x
+       jsr LFA1E
        ldx PageIndex
        bcs ++
        lda $6AF4,x
        beq Exit20
        ldy #$00
        lda $040A,x
        lsr a
        beq +
        iny
+       lda $95E2,y
        jsr LF68D
        jsr LF518
        lda #$0A
        sta $0409,x
++      jmp LF97C

        KillObject:
        lda #$00
        sta $6AF4,x
        rts

; enemy<-->background crash detection

LFA1E:  lda $74
        cmp #$11
        bne +
        lda $6AF4,x
        lsr a
        bcc ++
+       jsr LFA7D
        ldy #$00
        lda ($04),y
        cmp #$A0
        bcc +
        ldx PageIndex
++      lda $0403,x
        sta $05
        lda $0402,x
        sta $04
LFA41:  jsr LE792
        jsr LFD8F
        bcc KillObject
LFA49:  lda $08
        sta $0400,x
        lda $09
        sta $0401,x
        lda $0B
        and #$01
        sta $6AFB,x
+       rts

LFA5B:  lda $0404,x
        beq Exit20
LFA60:  lda #$00
        sta $0404,x
        lda #$05
        sta $6AF4,x
Exit20: rts

LFA6B:  lda $6AF7,x
        cmp #$F7
        beq +
        dec $0409,x
        bne ++
+       jsr KillObject
++      jmp LF97C

LFA7D:  ldx PageIndex
        lda $0400,x
        sta $02
        lda $0401,x
        sta $03
        lda $6AFB,x
        sta $0B
        jmp MakeWRAMPtr

LFA91:  jsr KillObject
        lda $95DC
        jsr LF68D
        jmp LF97C

LFA9D:  ldx #$C0
-       stx PageIndex
        lda $6AF4,x
        beq +
        jsr LFAB4
+       lda PageIndex
        clc
        adc #$08
        tax
        cmp #$E0
        bne -
--      rts

LFAB4:  dec $0406,x
        bne ++
        lda #$0C
        sta $0406,x
        dec $0407,x
        bmi +
        bne ++
+       jsr KillObject
++      lda $0406,x
        cmp #$09
        bne +
        lda $0407,x
        asl a
        tay
        lda Table16,y
        sta $04
        lda Table16+1,y
        sta $05
        jsr LFA41
+       lda #$80
        sta $6B
        lda #$03
        jmp LF97E

; Table used by above subroutine

Table16 .db $00
        .db $00
        .db $0C
        .db $1C
        .db $10
        .db $F0
        .db $F0
        .db $08

LFAF2:  ldy #$18
-       jsr LFAFF
        lda PageIndex
        sec
        sbc #$08
        tay
        bne -

LFAFF:  sty PageIndex
        ldx $0728,y
        inx
        beq --
        ldx $0729,y
        lda $6AF4,x
        beq +
        lda $0405,x
        and #$02
        bne Exit13
+       sta $0404,x
        lda #$FF
        cmp $6B02,x
        bne +
        dec $0409,x
        bne Exit13
        lda $0728,y
        jsr LEB28
        ldy PageIndex
        lda $072A,y
        sta $0400,x
        lda $072B,y
        sta $0401,x
        lda $072C,y
        sta $6AFB,x
        lda #$18
        sta $6AF6,x
        lda #$0C
        sta $6AF5,x
        ldy #$00
        jsr LF186
        jsr LF152
        jsr LF1BF
        jsr LF1FA
        bcc Exit13
        lda #$01
        sta $0409,x
        sta $6AF4,x
        and ScrollDir
        asl a
        sta $0405,x
        ldy $6B02,x
        jsr LFB7B
        jmp LF85A

+       sta $6B02,x
        lda #$01
        sta $0409,x
        jmp KillObject

LFB7B:  jsr $80B0
        ror $0405,x
        lda $96BB,y
        sta $0409,x
Exit13: rts

LFB88:  ldx PageIndex
        jsr LF844
        lda $6B01,x
        inc $6B03,x
        dec $6B03,x
        bne +
        pha
        pla
+       bpl +
        jsr GetAbsolute
+       cmp #$08
        bcc +
        cmp #$10
        bcs Exit13
        tya
        and #$01
        tay
        lda $0085,y
        cmp $6AF9,x
        beq Exit13
        sta $6AFA,x
        dec $6AFA,x
        sta $6AF9,x
        jmp LF693

+       lda $963B,y
        cmp $6AF9,x
        beq Exit13
        jmp LF68D

LFBCA:  ldx PageIndex
        jsr LF844
        lda $965B,y
        cmp $6AF9,x
        beq Exit13
        sta $6AF9,x
        jmp LF690

LFBDD:  lda #$40
        sta PageIndex
        ldx #$0C
-       jsr LFBEC
        dex
        dex
        dex
        dex
        bne -
LFBEC:  lda $A0,x
        beq ++
        dec $A0,x
        txa
        lsr a
        tay
        lda Table17,y
        sta $04
        lda Table17+1,y
        sta $05
        lda $A1,x
        sta $08
        lda $A2,x
        sta $09
        lda $A3,x
        sta $0B
        jsr LFD8F
        bcc +++
        lda $08
        sta $A1,x
        sta $034D
        lda $09
        sta $A2,x
        sta $034E
        lda $0B
        and #$01
        sta $A3,x
        sta $034C
        lda $A3,x
        sta $034C
        lda #$5A
        sta $0343
        txa
        pha
        jsr DrawFrame
        lda SamusBlink
        bne +
        ldy #$00
        ldx #$40
        jsr LDC7F
        bcs +
        jsr LCD9C
        ldy #$00
        bcc +
        clc
        jsr LF311
        lda #$50
        sta $6E
        jsr LCE92
+       pla
        tax
++      rts

+++     lda #$00
        sta $A0,x
        rts

; Table used by above subroutine

Table17 .db $00
        .db $FB
        .db $FB
        .db $FE
        .db $FB
        .db $02
        .db $00
        .db $05

LFC65:  lda $6BE4
        beq +
        ldx #$F0
        stx PageIndex
        lda $6BE9
        cmp $95E4
        bne ++
        lda #$03
        jsr UpdateEnemyAnim
        lda $2E
        sta $8A
        lda #$18
-       pha
        tax
        jsr LFC98
        pla
        tax
        lda $B6,x
        and #$F8
        sta $B6,x
        txa
        sec
        sbc #$08
        bpl -
+       rts

++      jmp KillObject

LFC98:  lda $B0,x
        jsr GoRoutine

; Pointer table to code

        .dw ExitSub       ; rts
        .dw $FCA5
        .dw $FCB1
        .dw $FCBA

LFCA5:  jsr LFD84
        jsr LFD08
        jsr LFD25
        jmp LDD8B

LFCB1:  jsr LFD84
        jsr LFCC1
        jmp LDD8B

LFCBA:  lda #$00
        sta $B0,x
        jmp SFX_EnemyHit

LFCC1:  jsr LFD5F
        lda $B4,x
        cmp #$02
        bcs +
        ldy $08
        cpy ObjectY
        bcc +
        ora #$02
        sta $B4,x
+       ldy #$01
        lda $B4,x
        lsr a
        bcc +
        ldy #$FF
+       sty $05
        ldy #$04
        lsr a
        lda $B5,x
        bcc +
        ldy #$FD
+       sty $04
        inc $B5,x
        jsr LFD8F
        bcs +
        lda $B4,x
        ora #$02
        sta $B4,x
+       bcc +
        jsr LFD6C
+       lda $B5,x
        cmp #$50
        bcc +
        lda #$01
        sta $B0,x
+       rts

LFD08:  lda #$00
        sta $B5,x
        tay
        lda ObjectX
        sec
        sbc $B2,x
        bpl +
        iny
        jsr GetAbsolute
+       cmp #$10
        bcs +
        tya
        sta $B4,x
        lda #$02
        sta $B0,x
+       rts

LFD25:  txa
        lsr a
        lsr a
        lsr a
        adc $8A
        sta $8A
        lsr $8A
        and #$03
        tay
        lda Table18,y
        sta $04
        lda Table18+1,y
        sta $05
        jsr LFD5F
        lda $08
        sec
        sbc ScrollY
        tay
        lda #$02
        cpy #$20
        bcc +
        jsr GetAbsolute
        cpy #$80
        bcc ++
+       sta $04
++      jsr LFD8F
        jmp LFD6C

; Table used by above subroutine

Table18 .db $02
        .db $FE
        .db $01
        .db $FF
        .db $02

LFD5F:  lda $B3,x
        sta $0B
        lda $B1,x
        sta $08
        lda $B2,x
        sta $09
        rts

LFD6C:  lda $08
        sta $B1,x
        sta $04F0
        lda $09
        sta $B2,x
        sta $04F1
        lda $0B
        and #$01
        sta $B3,x
        sta $6BEB
        rts

LFD84:  lda $B6,x
        and #$04
        beq +
        lda #$03
        sta $B0,x
+       rts

LFD8F:  lda ScrollDir
        and #$02
        sta $02
        lda $04
        clc
        bmi +++
        beq LFDBF
        adc $08
        bcs +
        cmp #$F0
        bcc ++
+       adc #$0F
        ldy $02
        bne ClcExit2
        inc $0B
++      sta $08
        jmp LFDBF

+++     adc $08
        bcs +
        sbc #$0F
        ldy $02
        bne ClcExit2
        inc $0B
+       sta $08
LFDBF:  lda $05
        clc
        bmi ++
        beq SecExit
        adc $09
        bcc +
        ldy $02
        beq ClcExit2
        inc $0B
+       jmp +++

++      adc $09
        bcs +++
        ldy $02
        beq ClcExit2
        inc $0B
+++     sta $09
        SecExit:
        sec
        rts

        ClcExit2:
        clc
--      rts

LFDE3:  lda EndTimerHi
        cmp #$99
        bne +
        clc
        sbc EndTimerLo  ; A = zero if timer just started
        bne +           ; branch if not
        sta $06
        lda #$38
        sta $07
        jsr LDC54
+       ldx #$20
-       jsr LFE05
        txa
        sec
        sbc #$08
        tax
        bne -

LFE05:  lda $0758,x
        sec
        sbc #$02
        bne --
        sta $06
        inc $0758,x
        txa
        lsr a
        adc #$3C
        sta $07
        jmp LDC54

; Tile degenerate/regenerate

        UpdateTiles:
        ldx #$C0
-       jsr DoOneTile
        ldx PageIndex
        jsr Xminus16
        bne -
        DoOneTile:
        stx PageIndex
        lda TileRoutine,x
        beq +               ; exit if tile not active
        jsr GoRoutine

; Pointer table to code

        .dw ExitSub       ; rts
        .dw $FE3D
        .dw $FE54
        .dw $FE59
        .dw $FE54
        .dw $FE83

LFE3D:  inc TileRoutine,x
        lda #$00
        jsr SetTileAnim
        lda #$50
        sta TileDelay,x
        lda TileWRAMLo,x     ; low WRAM addr of blasted tile
        sta $00
        lda TileWRAMHi,x     ; high WRAM addr
        sta $01

LFE54:  lda #$02
        jmp UpdateTileAnim

LFE59:  lda FrameCount
        and #$03
        bne +       ; only update tile timer every 4th frame
        dec TileDelay,x
        bne +       ; exit if timer not reached zero
        inc TileRoutine,x
        ldy TileType,x
        lda Table19,y
        SetTileAnim:
        sta TileAnimIndex,x
        sta $0505,x
        lda #$00
        sta TileAnimDelay,x
+       rts

; Table used for indexing the animations in TileBlastAnim (see below)

Table19 .db $18,$1C,$20,$00,$04,$08,$0C,$10,$24,$14

LFE83:  lda #$00
        sta TileRoutine,x       ; tile = respawned
        lda TileWRAMLo,x
        clc
        adc #$21
        sta $00
        lda TileWRAMHi,x
        sta $01
        jsr LFF3C
        lda $02
        sta $07
        lda $03
        sta $09
        lda $01
        lsr a
        lsr a
        and #$01
        sta $0B
        ldy #$00
        jsr LF186
        lda #$04
        clc
        adc ObjRadY
        sta $04
        lda #$04
        clc
        adc ObjRadX
        sta $05
        jsr LF1FA
        bcs Exit23
        jsr LF311
        lda #$50
        sta $6E
        jmp LCE92

        GetTileFramePtr:
        lda TileAnimFrame,x
        asl a
        tay
        lda $97AF,y
        sta $02
        lda $97B0,y
        sta $03
Exit23: rts

        DrawTileBlast:
        lda PPUStrIndex
        cmp #$1F
        bcs Exit23
        ldx PageIndex
        lda TileWRAMLo,x
        sta $00
        lda TileWRAMHi,x
        sta $01
        jsr GetTileFramePtr
        ldy #$00
        sty $11
        lda ($02),y
        tax
        jsr Adiv16       ; / 16
        sta $04
        txa
        and #$0F
        sta $05
        iny
        sty $10
--      ldx $05
-       ldy $10
        lda ($02),y
        inc $10
        ldy $11
        sta ($00),y
        inc $11
        dex
        bne -
        lda $11
        clc
        adc #$20
        sec
        sbc $05
        sta $11
        dec $04
        bne --
        lda $01
        and #$04
        beq +
        lda $01
        ora #$0C
        sta $01
+       lda $01
        and #$2F
        sta $01
        jsr LC328
        clc
        rts

LFF3C:  lda $00
        tay
        and #$E0
        sta $02
        lda $01
        lsr a
        ror $02
        lsr a
        ror $02
        tya
        and #$1F
        jsr Amul8       ; * 8
        sta $03
        rts

        UpdateTileAnim:
        ldx PageIndex
        ldy TileAnimDelay,x
        beq +
        dec TileAnimDelay,x
        bne ++
+       sta TileAnimDelay,x
        ldy TileAnimIndex,x
        lda TileBlastAnim,y
        cmp #$FE            ; end of "tile-blast" animation?
        beq +
        sta TileAnimFrame,x
        iny
        tya
        sta TileAnimIndex,x
        jsr DrawTileBlast
        bcc ++
        ldx PageIndex
        dec TileAnimIndex,x
++      rts

+       inc TileRoutine,x
        pla
        pla
        rts

; Frame data for tile blasts

        TileBlastAnim:
        .db $06,$07,$00,$FE
        .db $07,$06,$01,$FE
        .db $07,$06,$02,$FE
        .db $07,$06,$03,$FE
        .db $07,$06,$04,$FE
        .db $07,$06,$05,$FE
        .db $07,$06,$09,$FE
        .db $07,$06,$0A,$FE
        .db $07,$06,$0B,$FE
        .db $07,$06,$08,$FE

        .db $00
        .db $00

;--------------------[ Reset address (code entrypoint) ]----------------------

        RESET:
        sei
        cld
        ldx #$00
        stx PPUControl0       ; clear PPUCNT0
        stx PPUControl1       ; clear PPUCNT1
-       lda PPUStatus
        bpl -           ; wait for vblank
-       lda PPUStatus
        bpl -           ; wait for vblank
        ora #$FF
        sta MMC1Reg0       ; reset MMCREG0
        sta MMC1Reg1       ; reset MMCREG1
        sta MMC1Reg2       ; reset MMCREG2
        sta MMC1Reg3       ; reset MMCREG3
        jmp Startup

        .db $FF
        .db $FF
        .db $FF
        jmp $B3E4
        .db $FF
        .db $FF
        .db $FF
        .db $FF
        .db $FF
        .db $FF
        .db $FF
        .db $FF
        .db $FF
        .db $FF
        .db $FF
        .db $FF
        .db $FF
        .db $FF
        .db $4D ; M
        .db $45 ; E
        .db $54 ; T
        .db $52 ; R
        .db $4F ; O
        .db $49 ; I
        .db $44 ; D
        .db $E4
        .db $8D
        .db $00
        .db $00
        .db $38
        .db $04
        .db $01
        .db $06
        .db $01
        .db $BC

; Vectors

.dw     NMI
.dw     RESET
.dw     RESET

.end

Download [367]m1source.txt ↓

FROM THE COMMUNITY

Player reviews

0 reviews
No ratings yet

Played this project? Tell other players how it holds up.

Your take on Metroid Source Code

Spam has been overwhelming. To help us approve your review, sign up and introduce yourself in the Introductions forum so we know you’re not a bot.

Sign in with your Retro Replay account to rate this project and share your experience.

Sign in to review