; -------------------
; 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] 
; 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 $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 numberr
        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 poos
        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 ccarry 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 rroom?
        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 currennt 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 objject

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 objeect

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 bbits
        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 acttive?
        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