; -------------------
; 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