; ------------------- ; 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] ; Continued by Dirty McDingus (nmikstas@yahoo.com) ; Can be reassembled using Ophis. ; A work in progress. ;Last updated: 3/9/2010 ;Game engine (memory page 7) .org $C000 .include "MetroidDefines.txt" ;-------------------------------------[ Forward declarations ]-------------------------------------- .alias ObjectAnimIndexTbl $8572 .alias FramePtrTable $860B .alias PlacePtrTable $86DF .alias StarPalSwitch $8AC7 .alias SamusEnterDoor $8B13 .alias AreaPointers $9598 .alias AreaRoutine $95C3 .alias EnemyHitPointTbl $962B .alias EnemyInitDelayTbl $96BB .alias DecSpriteYCoord $988A .alias NMIScreenWrite $9A07 .alias EndGamePalWrite $9F54 .alias SpecItmsTable $9598 .alias CopyMap $A93E .alias SoundEngine $B3B4 ;----------------------------------------[ Start of code ]------------------------------------------ ;This routine generates pseudo random numbers and updates those numbers ;every frame. The random numbers are used for several purposes including ;password scrambling and determinig what items, if any, an enemy leaves ;behind after it is killed. RandomNumbers: LC000: txa ; LC001: pha ; LC002: ldx #$05 ; LC004:* lda RandomNumber1 ; LC006: clc ; LC007: adc #$05 ; LC009: sta RandomNumber1 ;2E is increased by #$19 every frame and--> LC00B: lda RandomNumber2 ;2F is increased by #$5F every frame. LC00D: clc ; LC00E: adc #$13 ; LC010: sta RandomNumber2 ; LC012: dex ; LC013: bne - ; LC015: pla ; LC016: tax ; LC017: lda RandomNumber1 ; LC019: rts ; ;------------------------------------------[ Startup ]---------------------------------------------- Startup: LC01A: lda #$00 ; LC01C: sta MMC1Reg1 ;Clear bit 0. MMC1 is serial controlled LC01F: sta MMC1Reg1 ;Clear bit 1 LC022: sta MMC1Reg1 ;Clear bit 2 LC024: sta MMC1Reg1 ;Clear bit 3 LC027: sta MMC1Reg1 ;Clear bit 4 LC02B: sta MMC1Reg2 ;Clear bit 0 LC02E: sta MMC1Reg2 ;Clear bit 1 LC031: sta MMC1Reg2 ;Clear bit 2 LC034: sta MMC1Reg2 ;Clear bit 3 LC037: sta MMC1Reg2 ;Clear bit 4 LC03A: jsr MMCWriteReg3 ;($C4FA)Swap to PRG bank #0 at $8000 LC03D: dex ;X = $FF LC03E: txs ;S points to end of stack page ;Clear RAM at $000-$7FF. LC03F: ldy #$07 ;High byte of start address. LC041: sty $01 ; LC043: ldy #$00 ;Low byte of start address. LC045: sty $00 ;$0000 = #$0700 LC047: tya ;A = 0 LC048:* sta ($00),y ;clear address LC04A: iny ; LC04B: bne - ;Repeat for entire page. LC04D: dec $01 ;Decrement high byte of address. LC04F: bmi + ;If $01 < 0, all pages are cleared. LC051: ldx $01 ; LC053: cpx #$01 ;Keep looping until ram is cleared. LC055: bne - ; ;Clear cartridge RAM at $6000-$7FFF. LC057:* ldy #$7F ;High byte of start address. LC059: sty $01 ; LC05B: ldy #$00 ;Low byte of start address. LC05D: sty $00 ;$0000 points to $7F00 LC05F: tya ;A = 0 LC060:* sta ($00),y ; LC062: iny ;Clears 256 bytes of memory before decrementing to next--> LC063: bne - ;256 bytes. LC065: dec $01 ; LC067: ldx $01 ;Is address < $6000?--> LC069: cpx #$60 ;If not, do another page. LC06B: bcs - ; LC06D: lda #%00001110 ;Verticle mirroring. ;H/V mirroring (As opposed to one-screen mirroring). ;Switch low PRGROM area during a page switch. ;16KB PRGROM switching enabled. ;8KB CHRROM switching enabled. LC06F: sta MMCReg0Cntrl ; LC071: lda #$00 ;Clear bits 3 and 4 of MMC1 register 3. LC073: sta SwitchUpperBits ; LC075: ldy #$00 ; LC077: sty ScrollX ;ScrollX = 0 LC079: sty ScrollY ;ScrollY = 0 LC07B: sty PPUScroll ;Clear hardware scroll x LC07E: sty PPUScroll ;Clear hardware scroll y LC081: iny ;Y = #$01 LC082: sty GameMode ;Title screen mode LC084: jsr ClearNameTables ;($C158) LC087: jsr EraseAllSprites ;($C1A3) LC08A: 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 LC08C: sta PPUControl0 ; LC08F: sta PPUCNT0ZP ; LC091: lda #%00000010 ;Sprites visible = no ;Background visible = no ;Sprite clipping = yes ;Background clipping = no ;Display type = color LC093: sta PPUCNT1ZP ; LC095: lda #$47 ; LC097: sta MirrorCntrl ;Prepare to set PPU to vertical mirroring. LC099: jsr PrepVertMirror ;($C4B2) LC09C: lda #$00 ; LC09E: sta DMCCntrl1 ;PCM volume = 0 - disables DMC channel LC0A1: lda #$0F ; LC0A3: sta APUCommonCntrl0 ;Enable sound channel 0,1,2,3 LC0A6: ldy #$00 ; LC0A8: sty TitleRoutine ;Set title routine and and main routine function--> LC0AA: sty MainRoutine ;pointers equal to 0. LC0AC: lda #$11 ; LC0AE: sta RandomNumber1 ;Initialize RandomNumber1 to #$11 LC0B0: lda #$FF ; LC0B2: sta RandomNumber2 ;Initialize RandomNumber2 to #$FF LC0B4: iny ;Y = 1 LC0B5: sty SwitchPending ;Prepare to switch page 0 into lower PRGROM. LC0B7: jsr CheckSwitch ;($C4DE) LC0BA: bne WaitNMIEnd ;Branch always ;-----------------------------------------[ Main loop ]---------------------------------------------- ;The main loop runs all the routines that take place outside of the NMI. MainLoop: LC0BC: jsr CheckSwitch ;($C4DE)Check to see if memory page needs to be switched. LC0BF: jsr UpdateTimer ;($C266)Update Timers 1, 2 and 3. LC0C2: jsr GoMainRoutine ;($C114)Go to main routine for updating game. LC0C5: inc FrameCount ;Increment frame counter. LC0C7: lda #$00 ; LC0C9: sta NMIStatus ;Wait for next NMI to end. WaitNMIEnd: LC0CB: tay ; LC0CC: lda NMIStatus ; LC0CE: bne + ;If nonzero, NMI has ended. Else keep waiting. LC0D0: jmp WaitNMIEnd ; LC0D3:* jsr RandomNumbers ;($C000)Update pseudo random numbers. LC0D6: jmp MainLoop ;($C0BC)Jump to top of subroutine. ;-------------------------------------[ Non-Maskable Interrupt ]------------------------------------- ;The NMI is called 60 times a second by the VBlank signal from the PPU. When the ;NMI routine is called, the game should already be waiting for it in the main ;loop routine in the WaitNMIEnd loop. It is possible that the main loop routine ;will not be waiting as it is bogged down with excess calculations. This causes ;the game to slow down. NMI: LC0D9: php ;Save processor status, A, X and Y on stack. LC0DA: pha ;Save A. LC0DB: txa ; LC0DC: pha ;Save X. LC0DD: tya ; LC0DE: pha ;Save Y. LC0DF: lda #$00 ; LC0E1: sta SPRAddress ;Sprite RAM address = 0. LC0E4: lda #$02 ; LC0E6: sta SPRDMAReg ;Transfer page 2 ($200-$2FF) to Sprite RAM. LC0E9: lda NMIStatus ; LC0EB: bne ++ ;Skip if the frame couldn't finish in time. LC0ED: lda GameMode ; LC0EF: beq + ;Branch if mode=Play. LC0F1: jsr NMIScreenWrite ;($9A07)Write end message on screen(If appropriate). LC0F4:* jsr CheckPalWrite ;($C1E0)Check if palette data pending. LC0F7: jsr CheckPPUWrite ;($C2CA)check if data needs to be written to PPU. LC0FA: jsr WritePPUCtrl ;($C44D)Update $2000 & $2001. LC0FD: jsr WriteScroll ;($C29A)Update h/v scroll reg. LC100: jsr ReadJoyPads ;($C215)Read both joypads. LC103:* jsr SoundEngine ;($B3B4)Update music and SFX. LC106: jsr UpdateAge ;($C97E)Update Samus' age. LC109: ldy #$01 ; NMI = finished. LC10B: sty NMIStatus ; LC10D: pla ;Restore Y. LC10E: tay ; LC10F: pla ;Restore X. LC110: tax ; LC111: pla ;restore A. LC112: plp ;Restore processor status flags. LC113: rti ;Return from NMI. ;----------------------------------------[ 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: LC114: lda GameMode ;0 if game is running, 1 if at intro screen. LC116: beq + ;Branch if mode=Play. LC118: 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. LC11B:* lda Joy1Change ; LC11D: and #$10 ;Has START been pressed?--> LC11F: beq +++ ;if not, execute current routine as normal. LC121: lda MainRoutine ; LC123: cmp #$03 ;Is game engine running?--> LC125: beq + ;If yes, check for routine #5 (pause game). LC127: cmp #$05 ;Is game paused?--> LC129: bne +++ ;If not routine #5 either, don't care about START being pressed. LC12B: lda #$03 ;Otherwise, switch to routine #3 (game engine). LC12D: bne ++ ;Branch always. LC12F:* lda #$05 ;Switch to pause routine. LC131:* sta MainRoutine ;(MainRoutine = 5 if game paused, 3 if game engine running). LC133: lda GamePaused ; LC135: eor #$01 ;Toggle game paused. LC137: sta GamePaused ; LC139: jsr PauseMusic ;($CB92)Silences music while game paused. LC13c:* lda MainRoutine ; LC13E: jsr ChooseRoutine ;($C27C)Use MainRoutine as index into routine table below. ;Pointer table to code. LC141: .word AreaInit ;($C801)Area init. LC143: .word MoreInit ;($C81D)More area init. LC145: .word SamusInit ;($C8D1)Samus init. LC147: .word GameEngine ;($C92B)Game engine. LC149: .word GameOver ;($C9A6)Display GAME OVER. LC14B: .word PauseMode ;($C9B1)Pause game. LC14D: .word GoPassword ;($C9C4)Display password. LC14F: .word IncrementRoutine ;($C155)Just advances to next routine in table. LC151: .word SamusIntro ;($C9D7)Intro. LC153: .word WaitTimer ;($C494)Delay. IncrementRoutine: LC155: inc MainRoutine ;Increment to next routine in above table. LC157: rts ; ;-------------------------------------[ Clear name tables ]------------------------------------------ ClearNameTables: LC158: jsr ClearNameTable0 ;($C16D)Always clear name table 0 first. LC15B: lda GameMode ; LC15D: beq + ;Branch if mode = Play. LC15F: lda TitleRoutine ; LC161: cmp #$1D ;If running the end game routine, clear--> LC163: beq ++ ;name table 2, else clear name table 1. LC165:* lda #$02 ;Name table to clear + 1 (name table 1). LC167: bne +++ ;Branch always. LC169:* lda #$03 ;Name table to clear + 1 (name table 2). LC16B: bne ++ ;Branch always. ClearNameTable0: LC16D:* lda #$01 ;Name table to clear + 1 (name table 0). LC16F:* sta $01 ;Stores name table to clear. LC171: lda #$FF ; LC173: sta $00 ;Value to fill with. ClearNameTable: LC175: ldx PPUStatus ;Reset PPU address latch. LC178: lda PPUCNT0ZP ; LC17A: and #$FB ;PPU increment = 1. LC17C: sta PPUCNT0ZP ; LC17E: sta PPUControl0 ;Store control bits in PPU. LC181: ldx $01 ; LC183: dex ;Name table = X - 1. LC184: lda HiPPUTable,x ;get high PPU address. pointer table at $C19F. LC187: sta PPUAddress ; LC18A: lda #$00 ;Set PPU start address (High byte first). LC18C: sta PPUAddress ; LC18F: ldx #$04 ;Prepare to loop 4 times. LC191: ldy #$00 ;Inner loop value. LC193: lda $00 ;Fill-value. LC195:* sta PPUIOReg ; LC198: dey ; LC199: bne - ;Loops until the desired name table is cleared.--> LC19B: dex ;It also clears the associated attribute table. LC19C: bne - ; LC19E: rts ; ;The following table is used by the above routine for finding ;the high byte of the proper name table to clear. HiPPUTable: LC19F: .byte $20 ;Name table 0. LC1A0: .byte $24 ;Name table 1. LC1A1: .byte $28 ;Name table 2. LC1A2: .byte $2C ;Name table 3. ;-------------------------------------[ Erase all sprites ]------------------------------------------ EraseAllSprites: LC1A3: ldy #$02 ; LC1A5: sty $01 ;Loads locations $00 and $01 with --> LC1A7: ldy #$00 ;#$00 and #$02 respectively LC1A9: sty $00 ; LC1AB: ldy #$00 ; LC1AD: lda #$F0 ; LC1AF:* sta ($00),y ;Stores #$F0 in memory addresses $0200 thru $02FF. LC1B1: iny ; LC1B2: bne - ;Loop while more sprite RAM to clear. LC1B4: lda GameMode ; LC1B6: beq Exit101 ;Exit subroutine if GameMode=Play(#$00) LC1B8: jmp DecSpriteYCoord ;($988A)Find proper y coord of sprites. Exit101: LC1BB: rts ;Return used by subroutines above and below. ;---------------------------------------[ Remove intro sprites ]------------------------------------- ;The following routine is used in the Intro to remove the sparkle sprites and the crosshairs ;sprites every frame. It does this by loading the sprite values with #$F4 which moves the ;sprite to the bottom right of the screen and uses a blank graphic for the sprite. RemoveIntroSprites: LC1BC: ldy #$02 ;Start at address $200. LC1BE: sty $01 ; LC1C0: ldy #$00 ; LC1C2: sty $00 ;($00) = $0200 (sprite page) LC1C4: ldy #$5F ;Prepare to clear RAM $0200-$025F LC1C6: lda #$F4 ; LC1C8:* sta ($00),y ; LC1CA: dey ;Loop unitl $200 thru $25F is filled with #$F4. LC1CB: bpl - ; LC1CD: lda GameMode ; LC1CF: beq Exit101 ; branch if mode = Play. LC1D1: jmp DecSpriteYCoord ;($988A)Find proper y coord of sprites. ;-------------------------------------[Clear RAM $33 thru $DF]--------------------------------------- ;The routine below clears RAM associated with rooms and enemies. ClearRAM_33_DF: LC1D4: ldx #$33 ; LC1D6: lda #$00 ; LC1D8:* sta $00,x ;Clear RAM addresses $33 through $DF. LC1DA: inx ; LC1DB: cpx #$E0 ; LC1DD: bcc - ;Loop until all desired addresses are cleared. LC1DF: rts ; ;--------------------------------[ Check and prepare palette write ]--------------------------------- CheckPalWrite: LC1E0: lda GameMode ; LC1E2: beq + ;Is game being played? If so, branch to exit. LC1E4: lda TitleRoutine ; LC1E6: cmp #$1D ;Is Game at ending sequence? If not, branch LC1E8: bcc + ; LC1EA: jmp EndGamePalWrite ;($9F54)Write palette data for ending. LC1ED:* ldy PalDataPending ; LC1EF: bne ++ ;Is palette data pending? If so, branch. LC1F1: lda GameMode ; LC1F3: beq + ;Is game being played? If so, branch to exit. LC1F5: lda TitleRoutine ; LC1F7: cmp #$15 ;Is intro playing? If not, branch. LC1F9: bcs + ; LC1FB: jmp StarPalSwitch ;($8AC7)Cycles palettes for intro stars twinkle. LC1FE:* rts ;Exit when no palette data pending. ;Prepare to write palette data to PPU. LC1FF:* dey ;Palette # = PalDataPending - 1. LC200: tya ; LC201: asl ;* 2, each pal data ptr is 2 bytes (16-bit). LC202: tay ; LC203: ldx $9560,y ;X = low byte of PPU data pointer. LC206: lda $9561,y ; LC209: tay ;Y = high byte of PPU data pointer. LC20A: lda #$00 ;Clear A. LC20C: sta PalDataPending ;Reset palette data pending byte. PreparePPUProcess_: LC20E: stx $00 ;Lower byte of pointer to PPU string. LC210: sty $01 ;Upper byte of pointer to PPU string. LC212: jmp ProcessPPUString ;($C30C)Write data string to PPU. ;----------------------------------------[Read joy pad status ]-------------------------------------- ;The following routine reads the status of both joypads ReadJoyPads: LC215: ldx #$00 ;Load x with #$00. Used to read status of joypad 1. LC217: stx $01 ; LC219: jsr ReadOnePad ; LC21C: inx ;Load x with #$01. Used to read status of joypad 2. LC21D: inc $01 ; ReadOnePad: LC21F: ldy #$01 ;These lines strobe the --> LC221: sty CPUJoyPad1 ;joystick to enable the --> LC224: dey ;program to read the --> LC225: sty CPUJoyPad1 ;buttons pressed. LC228: ldy #$08 ;Do 8 buttons. LC22A:* pha ;Store A. LC22B: lda CPUJoyPad1,x ;Read button status. Joypad 1 or 2. LC22E: sta $00 ;Store button press at location $00. LC230: lsr ;Move button push to carry bit. LC231: ora $00 ;If joystick not connected, --> LC233: lsr ;fills Joy1Status with all 1s. LC234: pla ;Restore A. LC235: rol ;Add button press status to A. LC236: dey ;Loop 8 times to get --> LC237: bne - ;status of all 8 buttons. LC239: ldx $01 ;Joypad #(0 or 1). LC23B: ldy Joy1Status,x ;Get joypad status of previous refresh. LC23D: sty $00 ;Store at $00. LC23F: sta Joy1Status,x ;Store current joypad status. LC241: eor $00 ; LC243: beq + ;Branch if no buttons changed. LC245: lda $00 ; LC247: and #$BF ;Remove the previous status of the B button. LC249: sta $00 ; LC24B: eor Joy1Status,x ; LC24D:* and Joy1Status,x ;Save any button changes from the current frame--> LC24F: sta Joy1Change,x ;and the last frame to the joy change addresses. LC251: sta Joy1Retrig,x ;Store any changed buttons in JoyRetrig address. LC253: ldy #$20 ; LC255: lda Joy1Status,x ;Checks to see if same buttons are being--> LC257: cmp $00 ;pressed this frame as last frame.--> LC259: bne + ;If none, branch. LC25B: dec RetrigDelay1,x ;Decrement RetrigDelay if same buttons pressed. LC25D: bne ++ ; LC25F: sta Joy1Retrig,x ;Once RetrigDelay=#$00, store buttons to retrigger. LC261: ldy #$08 ; LC263:* sty RetrigDelay1,x ;Reset retrigger delay to #$20(32 frames)--> LC265:* rts ;or #$08(8 frames) if already retriggering. ;-------------------------------------------[ Update timer ]----------------------------------------- ;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: LC266: ldx #$01 ;First timer to decrement is Timer2. LC268: dec TimerDelay ; LC26A: bpl DecTimer ; LC26C: lda #$09 ;TimerDelay hits #$00 every 10th frame. LC26E: sta TimerDelay ;Reset TimerDelay after it hits #$00. LC270: ldx #$02 ;Decrement Timer3 every 10 frames. DecTimer: LC272: lda Timer1,x ; LC274: beq + ;Don't decrease if timer is already zero. LC276: dec Timer1,x ; LC278:* dex ;Timer1 and Timer2 decremented every frame. LC279: bpl DecTimer ; LC27B: rts ; ;-----------------------------------------[ Choose routine ]----------------------------------------- ;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 ChooseRoutine, ;meaning that its address can be popped from the stack. ChooseRoutine: LC27C: asl ;* 2, each ptr is 2 bytes (16-bit). LC27D: sty TempY ;Temp storage. LC27F: stx TempX ;Temp storage. LC281: tay ; LC282: iny ; LC283: pla ;Low byte of ptr table address. LC284: sta CodePtr ; LC286: pla ;High byte of ptr table address. LC287: sta CodePtr+1 ; LC289: lda (CodePtr),y ;Low byte of code ptr. LC28B: tax ; LC28C: iny ; LC28D: lda (CodePtr),y ;High byte of code ptr. LC28F: sta CodePtr+1 ; LC291: stx CodePtr ; LC293: ldx TempX ;Restore X. LC295: ldy TempY ;Restore Y. LC297: jmp (CodePtr) ; ;--------------------------------------[ Write to scroll registers ]--------------------------------- WriteScroll: LC29A: lda PPUStatus ;Reset scroll register flip/flop LC29D: lda ScrollX ; LC29F: sta PPUScroll ; LC2A2: lda ScrollY ;X and Y scroll offsets are loaded serially. LC2A4: sta PPUScroll ; LC2A7: rts ; ;----------------------------------[ Add y index to stored addresses ]------------------------------- ;Add Y to pointer at $0000. AddYToPtr00: LC2A8: tya ; LC2A9: clc ;Add value stored in Y to lower address--> LC2AA: adc $00 ;byte stored in $00. LC2AC: sta $00 ; LC2AE: bcc + ;Increment $01(upper address byte) if carry--> LC2B0: inc $01 ;has occurred. LC2B2:* rts ; ;Add Y to pointer at $0002 AddYToPtr02: LC2B3: tya ; LC2B4: clc ;Add value stored in Y to lower address--> LC2B5: adc $02 ;byte stored in $02. LC2B7: sta $02 ; LC2B9: bcc + ;Increment $01(upper address byte) if carry--> LC2BB: inc $03 ;has occurred. LC2BD:* rts ; ;--------------------------------[ Simple divide and multiply routines ]----------------------------- Adiv32: LC2BE: lsr ;Divide by 32. Adiv16: LC2BF: lsr ;Divide by 16. Adiv8: LC2C0: lsr ;Divide by 8. LC2C1: lsr ; LC2C2: lsr ;Divide by shifting A right. LC2C3: rts ; Amul32: LC2C4: asl ;Multiply by 32. Amul16: LC2C5: asl ;Multiply by 16. Amul8: LC2C6: asl ;Multiply by 8. LC2C7: asl ; LC2C8: asl ;Multiply by shifting A left. LC2C9: rts ; ;-------------------------------------[ PPU writing routines ]--------------------------------------- ;Checks if any data is waiting to be written to the PPU. ;RLE data is one tile that repeats several times in a row. RLE-Repeat Last Entry CheckPPUWrite: LC2CA: lda PPUDataPending ; LC2CC: beq + ;If zero no PPU data to write, branch to exit. LC2CE: lda #$A1 ; LC2D0: sta $00 ;Sets up PPU writer to start at address $07A1. LC2D2: lda #$07 ; LC2D4: sta $01 ;$0000 = ptr to PPU data string ($07A1). LC2D6: jsr ProcessPPUString ;($C30C)write it to PPU. LC2D9: lda #$00 ; LC2DB: sta PPUStrIndex ;PPU data string has been written so the data--> LC2DE: sta PPUDataString ;stored for the write is now erased. LC2E1: sta PPUDataPending ; LC2E3:* rts ; PPUWrite: LC2E4: sta PPUAddress ;Set high PPU address. LC2E7: iny ; LC2E8: lda ($00),y ; LC2EA: sta PPUAddress ;Set low PPU address. LC2ED: iny ; LC2EE: lda ($00),y ;Get data byte containing rep length & RLE status. LC2F0: asl ;Carry Flag = PPU address increment (0 = 1, 1 = 32). LC2F1: jsr SetPPUInc ;($C318)Update PPUCtrl0 according to Carry Flag. LC2F4: asl ;Carry Flag = bit 6 of byte at ($00),y (1 = RLE). LC2F5: lda ($00),y ;Get data byte again. LC2F7: and #$3F ;Keep lower 6 bits as loop counter. LC2F9: tax ; LC2FA: bcc PPUWriteLoop ;If Carry Flag not set, the data is not RLE. LC2FC: iny ;Data is RLE, advance to data byte. PPUWriteLoop: LC2FD: bcs + ; LC2FF: iny ;Only inc Y if data is not RLE. LC300:* lda ($00),y ;Get data byte. LC302: sta PPUIOReg ;Write to PPU. LC305: dex ;Decrease loop counter. LC306: bne PPUWriteLoop ;Keep going until X=0. LC308: iny ; LC309: jsr AddYToPtr00 ;($C2A8)Point to next data chunk. ;Write data string at ($00) to PPU. ProcessPPUString: LC30C: ldx PPUStatus ;Reset PPU address flip/flop. LC30F: ldy #$00 ; LC311: lda ($00),y ; LC313: bne PPUWrite ;If A is non-zero, PPU data string follows,--> LC315: jmp WriteScroll ;($C29A)Otherwise we're done. ;In: CF = desired PPU address increment (0 = 1, 1 = 32). ;Out: PPU control #0 ($2000) updated accordingly. SetPPUInc: LC318: pha ;Preserve A. LC319: lda PPUCNT0ZP ; LC31B: ora #$04 ; LC31D: bcs + ;PPU increment = 32 only if Carry Flag set,--> LC31F: and #$FB ;else PPU increment = 1. LC321:* sta PPUControl0 ; LC323: sta PPUCNT0ZP ; LC326: pla ;Restore A. LC327: rts ; ;Erase blasted tile on nametable. Each screen is 16 tiles across and 15 tiles down. EraseTile: LC328: ldy #$01 ; LC32A: sty PPUDataPending ;data pending = YES. LC32C: dey ; LC32D: lda ($02),y ; LC32F: and #$0F ; LC331: sta $05 ;# of tiles horizontally. LC333: lda ($02),y ; LC335: jsr Adiv16 ;($C2BF)/ 16. LC338: sta $04 ;# of tiles vertically. LC33A: ldx PPUStrIndex ; LC33D:* lda $01 ; LC33F: jsr WritePPUByte ;($C36B)write PPU high address to $07A1,PPUStrIndex. LC342: lda $00 ; LC344: jsr WritePPUByte ;($C36B)write PPU low address to $07A1,PPUStrIndex. LC347: lda $05 ;data length. LC349: sta $06 ; LC34B: jsr WritePPUByte ;($C36B)write PPU string length to $07A1,PPUStrIndex. LC34E:* iny ; LC34F: lda ($02),y ;Get new tile to replace old tile. LC351: jsr WritePPUByte ;($C36B)Write it to $07A1,PPUStrIndex, inc x. LC354: dec $06 ; LC356: bne - ;Branch if more horizontal tiles to replace. LC358: stx PPUStrIndex ; LC35B: sty $06 ; LC35D: ldy #$20 ; LC35F: jsr AddYToPtr00 ;($C2A8)Move to next name table line. LC362: ldy $06 ;Store index to find next tile info. LC364: dec $04 ; LC366: bne -- ;Branch if more lines need to be changed on name table. LC368: jsr EndPPUString ;($c376)Finish writing PPU string and exit. WritePPUByte: LC36B: sta PPUDataString,x ;Store data byte at end of PPUDataString. NextPPUByte: LC36E: inx ;PPUDataString has increased in size by 1 byte. LC36F: cpx #$4F ;PPU byte writer can only write a maximum of #$4F bytes LC371: bcc + ;If PPU string not full, branch to get more data. LC373: ldx PPUStrIndex ; EndPPUString: LC376: lda #$00 ;If PPU string is already full, or all PPU bytes loaded,--> LC378: sta PPUDataString,x ;add #$00 as last byte to the PPU byte string. LC37B: pla ; LC37C: pla ;Remove last return address from stack and jump out of--> LC37D:* rts ;PPU writing routines. ;The following routine is only used by the intro routine to load the sprite ;palette data for the twinkling stars. The following memory addresses are used: ;$00-$01 Destination address for PPU write, $02-$03 Source address for PPU data, ;$04 Temp storage for PPU data byte, $05 PPU data string counter byte, ;$06 Temp storage for index byte. PrepPPUPaletteString: LC37E: ldy #$01 ; LC380: sty PPUDataPending ;Indicate data waiting to be written to PPU. LC382: dey ; LC383: beq ++++ ;Branch always LC385:* sta $04 ;$04 now contains next data byte to be put into the PPU string. LC387: lda $01 ;High byte of staring address to write PPU data LC389: jsr WritePPUByte ;($C36B)Put data byte into PPUDataString. LC38c: lda $00 ;Low byte of starting address to write PPU data. LC38E: jsr WritePPUByte ;($C36B)Put data byte into PPUDataString. LC391: lda $04 ;A now contains next data byte to be put into the PPU string. LC393: jsr SeparateControlBits ;($C3C6)Break control byte into two bytes. LC396: bit $04 ;Check to see if RLE bit is set in control byte.--> LC398: bvc WritePaletteStringByte ;If not set, branch to load byte. Else increment index--> LC39A: iny ;to find repeating data byte. WritePaletteStringByte: LC39B: bit $04 ;Check if RLE bit is set (again). if set, load same--> LC39D: bvs + ;byte over and over again until counter = #$00. LC39F: iny ;Non-repeating data byte. Increment for next byte. LC3A0:* lda ($02),y ; LC3A2: jsr WritePPUByte ;($C36B)Put data byte into PPUDataString. LC3A5: sty $06 ;Temporarily store data index. LC3A7: ldy #$01 ;PPU address increment = 1. LC3A9: bit $04 ;If MSB set in control bit, it looks like this routine might--> LC3AB: bpl + ;have been used for a software control verticle mirror, but--> ;the starting address has already been written to the PPU--> ;string so this section has no effect whether the MSB is set--> ;or not. The PPU is always incremented by 1. LC3AD: ldy #$20 ;PPU address increment = 32. LC3AF:* jsr AddYToPtr00 ;($C2A8)Set next PPU write address.(Does nothing, already set). LC3B2: ldy $06 ;Restore data index to Y. LC3B4: dec $05 ;Decrement counter byte. LC3B6: bne WritePaletteStringByte ;If more bytes to write, branch to write another byte. LC3B8: stx PPUStrIndex ;Store total length, in bytes, of PPUDataString. LC3BB: iny ;Move to next data byte(should be #$00). LC3BC:* ldx PPUStrIndex ;X now contains current length of PPU data string. LC3BF: lda ($02),y ; LC3C1: bne ---- ;Is PPU string done loading (#$00)? If so exit,--> LC3C3: jsr EndPPUString ;($C376)else branch to process PPU byte. SeparateControlBits: LC3C6: sta $04 ;Store current byte LC3C8: and #$BF ; LC3CA: sta PPUDataString,x ;Remove RLE bit and save control bit in PPUDataString. LC3CD: and #$3F ; LC3CF: sta $05 ;Extract counter bits and save them for use above. LC3D1: jmp NextPPUByte ;($C36E) ;----------------------------------------[ Math routines ]------------------------------------------- TwosCompliment: LC3D4: eor #$FF ; LC3D6: clc ;Generate twos compliment of value stored in A. LC3D7: adc #$01 ; LC3D9: rts ; ;The following two routines add a Binary coded decimal (BCD) number to another BCD number. ;A base number is stored in $03 and the number in A is added/subtracted from $03. $01 and $02 ;contain the lower and upper digits of the value in A respectively. If an overflow happens after ;the addition/subtraction, the carry bit is set before the routine returns. Base10Add: LC3DA: jsr ExtractNibbles ;($C41D)Separate upper 4 bits and lower 4 bits. LC3DD: adc $01 ;Add lower nibble to number. LC3DF: cmp #$0A ; LC3E1: bcc + ;If result is greater than 9, add 5 to create--> LC3E3: adc #$05 ;valid result(skip #$0A thru #$0F). LC3E5:* clc ; LC3E6: adc $02 ;Add upper nibble to number. LC3E8: sta $02 ; LC3EA: lda $03 ; LC3EC: and #$F0 ;Keep upper 4 bits of HealthLo/HealthHi in A. LC3EE: adc $02 ; LC3F0: bcc ++ ; LC3F2:* adc #$5F ;If upper result caused a carry, add #$5F to create--> LC3F4: sec ;valid result. Set carry indicating carry to next digit. LC3F5: rts ; LC3F6:* cmp #$A0 ;If result of upper nibble add is greater than #$90,--> LC3F8: bcs -- ;Branch to add #$5F to create valid result. LC3FA: rts ; Base10Subtract: LC3FB: jsr ExtractNibbles ;($C41D)Separate upper 4 bits and lower 4 bits. LC3FE: sbc $01 ;Subtract lower nibble from number. LC400: sta $01 ; LC402: bcs + ;If result is less than zero, add 10 to create--> LC404: adc #$0A ;valid result. LC406: sta $01 ; LC408: lda $02 ; LC40A: adc #$0F ;Adjust $02 to account for borrowing. LC40C: sta $02 ; LC40E:* lda $03 ;Keep upper 4 bits of HealthLo/HealthHi in A. LC410: and #$F0 ; LC412: sec ; LC413: sbc $02 ;If result is greater than zero, branch to finish. LC415: bcs + ; LC417: adc #$A0 ;Add 10 to create valid result. LC419: clc ; LC41A:* ora $01 ;Combine A and $01 to create final value. LC41C: rts ; ExtractNibbles: LC41D: pha ; LC41E: and #$0F ;Lower 4 bits of value to change HealthLo/HealthHi by. LC420: sta $01 ; LC422: pla ; LC423: and #$F0 ;Upper 4 bits of value to change HealthLo/HealthHi by. LC425: sta $02 ; LC427: lda $03 ; LC429: and #$0F ;Keep lower 4 bits of HealthLo/HealthHi in A. LC42B: rts ; ;---------------------------[ NMI and PPU control routines ]-------------------------------- ; Wait for the NMI to end. WaitNMIPass: LC42C: jsr ClearNMIStat ;($C434)Indicate currently in NMI. LC42F:* lda NMIStatus ; LC431: beq - ;Wait for NMI to end. LC433: rts ; ClearNMIStat: LC434: lda #$00 ;Clear NMI byte to indicate the game is--> LC436: sta NMIStatus ;currently running NMI routines. LC438: rts ; ScreenOff: LC439: lda PPUCNT1ZP ; LC43B: and #$E7 ; BG & SPR visibility = off WriteAndWait: LC43D:* sta PPUCNT1ZP ;Update value to be loaded into PPU control register. WaitNMIPass_: LC43F: jsr ClearNMIStat ;($C434)Indicate currently in NMI. LC442:* lda NMIStatus ; LC444: beq - ;Wait for NMI to end before continuing. LC446: rts ; ScreenOn: LC447: lda PPUCNT1ZP ; LC449: ora #$1E ;BG & SPR visibility = on LC44B: bne -- ;Branch always ;Update the actual PPU control registers. WritePPUCtrl: LC44D: lda PPUCNT0ZP ; LC44F: sta PPUControl0 ; LC452: lda PPUCNT1ZP ;Update PPU control registers. LC454: sta PPUControl1 ; LC457: lda MirrorCntrl ; LC459: jsr PrepPPUMirror ;($C4D9)Setup vertical or horizontal mirroring. ExitSub: LC45C: rts ;Exit subroutines. ;Turn off both screen and NMI. ScreenNmiOff: LC45D: lda PPUCNT1ZP ; LC45F: and #$E7 ;BG & SPR visibility = off LC461: jsr WriteAndWait ;($C43D)Wait for end of NMI. LC464: lda PPUCNT0ZP ;Prepare to turn off NMI in PPU. LC466: and #$7F ;NMI = off LC468: sta PPUCNT0ZP ; LC46A: sta PPUControl0 ;Actually load PPU register with NMI off value. LC46D: rts ; ;The following routine does not appear to be used. LC46E: lda PPUCNT0ZP ;Enable VBlank. LC470: ora #$80 ; LC472: sta PPUCNT0ZP ;Write PPU control register 0 and PPU status byte. LC474: sta PPUControl0 ; LC477: lda PPUCNT1ZP ;Turn sprites and screen on. LC479: ora #$1E ; LC47B: bne -- ;Branch always. VBOffAndHorzWrite: LC47D: lda PPUCNT0ZP ; LC47F: and #$7B ;Horizontal write, disable VBlank. LC481:* sta PPUControl0 ;Save new values in the PPU control register--> LC484: sta PPUCNT0ZP ;and PPU status byte. LC486: rts ; NmiOn: LC487:* lda PPUStatus ; LC48A: and #$80 ;Wait for end of VBlank. LC48C: bne - ; LC48E: lda PPUCNT0ZP ; LC490: ora #$80 ;Enable VBlank interrupts. LC492: bne -- ;Branch always. ;--------------------------------------[ Timer routines ]-------------------------------------------- ;The following routines set the timer and decrement it. The timer is set after Samus dies and ;before the GAME OVER message is dispayed. The timer is also set while the item pickup music ;is playing. WaitTimer: LC494: lda Timer3 ;Exit if timer hasn't hit zero yet LC496: bne + ; LC498: lda NextRoutine ;Set GameOver as next routine. LC49A: cmp #$04 ; LC49C: beq SetMainRoutine ;Set GoPassword as main routine. LC49E: cmp #$06 ; LC4A0: beq SetMainRoutine ; LC4A2: jsr StartMusic ;($D92C)Assume power up was picked up and GameEngine--> LC4A5: lda NextRoutine ;is next routine. Start area music before exiting. SetMainRoutine: LC4A7: sta MainRoutine ;Set next routine to run. LC4A9:* rts ; SetTimer: LC4AA: sta Timer3 ;Set Timer3. Frames to wait is value stored in A*10. LC4AC: stx NextRoutine ;Save routine to jump to after Timer3 expires. LC4AE: lda #$09 ;Next routine to run is WaitTimer. LC4B0: bne SetMainRoutine ;Branch always. ;-----------------------------------[ PPU mirroring routines ]--------------------------------------- PrepVertMirror: LC4B2: nop ; LC4B3: nop ;Prepare to set PPU for vertical mirroring (again). LC4B4: lda #$47 ; SetPPUMirror: LC4B6: lsr ; LC4B7: lsr ;Move bit 3 to bit 0 position. LC4B8: lsr ; LC4B9: and #$01 ;Remove all other bits. LC4BB: sta $00 ;Store at address $00. LC4BD: lda MMCReg0Cntrl ; LC4BF: and #$FE ;Load MMCReg0Cntrl and remove bit 0. LC4C1: ora $00 ;Replace bit 0 with stored bit at $00. LC4C3: sta MMCReg0Cntrl ; LC4C5: sta MMC1Reg0 ; LC4C8: lsr ; LC4C9: sta MMC1Reg0 ; LC4Cc: lsr ; LC4CD: sta MMC1Reg0 ; LC4D0: lsr ;Load new configuration data serially--> LC4D1: sta MMC1Reg0 ;into MMC1Reg0. LC4D4: lsr ; LC4D5: sta MMC1Reg0 ; LC4D8: rts ; PrepPPUMirror: LC4D9: lda MirrorCntrl ;Load MirrorCntrl into A. LC4DB: jmp SetPPUMirror ;($C4B6)Set mirroring through MMC1 chip. ;-----------------------------[ Switch bank and init bank routines ]--------------------------------- ;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: LC4DE: ldy SwitchPending ; LC4E0: beq + ;Exit if zero(no bank switch issued). else Y contains bank#+1. LC4E2: jsr SwitchOK ;($C4E8)Perform bank switch. LC4E5: jmp GoBankInit ;($C510)Initialize bank switch data. SwitchOK: LC4E8: lda #$00 ;Reset(so that the bank switch won't be performed--> LC4EA: sta SwitchPending ;every succeeding frame too). LC4EC: dey ;Y now contains the bank to switch to. LC4ED: sty CurrentBank ; ROMSwitch: LC4EF: tya ; LC4F0: sta $00 ;Bank to switch to is stored at location $00. LC4F2: lda SwitchUpperBits ;Load upper two bits for Reg 3 (they should always be 0). LC4F4: and #$18 ;Extract bits 3 and 4 and add them to the current--> LC4F6: ora $00 ;bank to switch to. LC4F8: sta SwitchUpperBits ;Store any new bits set in 3 or 4(there should be none). ;Loads the lower memory page with the bank specified in A. MMCWriteReg3: LC4FA: sta MMC1Reg3 ;Write bit 0 of ROM bank #. LC4FD: lsr ; LC4FE: sta MMC1Reg3 ;Write bit 1 of ROM bank #. LC501: lsr ; LC502: sta MMC1Reg3 ;Write bit 2 of ROM bank #. LC505: lsr ; LC506: sta MMC1Reg3 ;Write bit 3 of ROM bank #. LC509: lsr ; LC50A: sta MMC1Reg3 ;Write bit 4 of ROM bank #. LC50D: lda $00 ;Restore A with current bank number before exiting. LC50F:* rts ; ;Calls the proper routine according to the bank number in A. GoBankInit: LC510: asl ;*2 For proper table offset below. LC511: tay ; LC512: lda BankInitTable,y ; LC515: sta $0A ;Load appropriate subroutine address into $0A and $0B. LC517: lda BankInitTable+1,y ; LC51A: sta $0B ; LC51C: jmp ($000A) ;Jump to appropriate initialization routine. BankInitTable: LC51F: .word InitBank0 ;($C531)Initialize bank 0. LC521: .word InitBank1 ;($C552)Initialize bank 1. LC523: .word InitBank2 ;($C583)Initialize bank 2. LC525: .word InitBank3 ;($C590)Initialize bank 3. LC527: .word InitBank4 ;($C5B6)Initialize bank 4. LC529: .word InitBank5 ;($C5C3)Initialize bank 5. LC52B: .word ExitSub ;($C45C)Rts LC52D: .word ExitSub ;($C45C)Rts LC52F: .word ExitSub ;($C45C)Rts ;Title screen memory page. InitBank0: LC531: ldy #$00 ; LC533: sty GamePaused ;Ensure game is not paused. LC535: iny ;Y=1. LC536: sty GameMode ;Game is at title routines. LC538: jsr ScreenNmiOff ;($C45D)Waits for NMI to end then turns it off. LC53B: jsr CopyMap ;($A93E)Copy game map from ROM to cartridge RAM $7000-$73FF LC53E: jsr ClearNameTables ;($C158)Erase name table data. LC541: ldy #$A0 ; LC543:* lda $98BF,y ; LC546: sta $6DFF,y ;Loads sprite info for stars into RAM $6E00 thru 6E9F. LC549: dey ; LC54A: bne - ; LC54C: jsr InitTitleGFX ;($C5D7)Load title GFX. LC54F: jmp NmiOn ;($C487)Turn on VBlank interrupts. ;Brinstar memory page. InitBank1: LC552: lda #$00 ; LC554: sta GameMode ;GameMode = play. LC556: jsr ScreenNmiOff ;($C45D)Disable screen and Vblank. LC559: lda MainRoutine ; LC55B: cmp #$03 ;Is game engine running? if so, branch.--> LC55D: beq + ;Else do some housekeeping first. LC55F: lda #$00 ; LC561: sta MainRoutine ;Run InitArea routine next. LC563: sta InArea ;Start in Brinstar. LC565: sta GamePaused ;Make sure game is not paused. LC567: jsr ClearRAM_33_DF ;($C1D4)Clear game engine memory addresses. LC56A: jsr ClearSamusStats ;($C578)Clear Samus' stats memory addresses. LC56D:* ldy #$00 ; LC56F: jsr ROMSwitch ;($C4EF)Load Brinstar memory page into lower 16Kb memory. LC572: jsr InitBrinstarGFX ;($C604)Load Brinstar GFX. LC575: jmp NmiOn ;($C487)Turn on VBlank interrupts. ClearSamusStats: LC578: ldy #$0F ; LC57A: lda #$00 ;Clears Samus stats(Health, full tanks, game timer, etc.). LC57C:* sta $0100,y ;Load $100 thru $10F with #$00. LC57F: dey ; LC580: bpl - ;Loop 16 times. LC582: rts ; ;Norfair memory page. InitBank2: LC583: lda #$00 ;GameMode = play. LC585: sta GameMode ; LC587: jsr ScreenNmiOff ;($C45D)Disable screen and Vblank. LC58A: jsr InitNorfairGFX ;($C622)Load Norfair GFX. LC58D: jmp NmiOn ;($C487)Turn on VBlank interrupts. ;Tourian memory page. InitBank3: LC590: lda #$00 ;GameMode = play. LC592: sta GameMode ; LC594: jsr ScreenNmiOff ;($C45D)Disable screen and Vblank. LC597: ldy #$0D ; LC599:* lda MetroidData,y ;Load info from table below into--> LC59C: sta $77F0,y ;$77F0 thru $77FD. LC59F: dey ; LC5A0: bpl - ; LC5A2: jsr InitTourianGFX ;($C645)Load Tourian GFX. LC5A5: jmp NmiOn ;($C487)Turn on VBlank interrupts. ;Table used by above subroutine and loads the initial data used to describe ;metroid's behavior in the Tourian section of the game. MetroidData: LC5A8: .byte $F8, $08, $30, $D0, $60, $A0, $02, $04, $00, $00, $00, $00, $00, $00 ;Kraid memory page. InitBank4: LC5B6: lda #$00 ;GameMode = play. LC5B8: sta GameMode ; LC5BA: jsr ScreenNmiOff ;($C45D)Disable screen and Vblank. LC5BD: jsr InitKraidGFX ;($C677)Load Kraid GFX. LC5C0: jmp NmiOn ;($C487)Turn on VBlank interrupts. ;Ridley memory page. InitBank5: LC5C3: lda #$00 ;GameMode = play. LC5C5: sta GameMode ; LC5C7: jsr ScreenNmiOff ;($C45D)Disable screen and Vblank. LC5CA: jsr InitRidleyGFX ;($C69F)Loag Ridley GFX. LC5CD: jmp NmiOn ;($C487)Turn on VBlank interrupts. InitEndGFX: LC5D0: lda #$01 ; LC5D2: sta GameMode ;Game is at title/end game. LC5D4: jmp InitGFX6 ;($C6C2)Load end game GFX. InitTitleGFX: LC5D7: ldy #$15 ;Entry 21 in GFXInfo table. LC5D9: jsr LoadGFX ;($C7AB)Load pattern table GFX. LoadSamusGFX: LC5DC: ldy #$00 ;Entry 0 in GFXInfo table. LC5DE: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC5E1: lda JustInBailey ; LC5E4: beq + ;Branch if wearing suit LC5E6: ldy #$1B ;Entry 27 in GFXInfo table. LC5E8: jsr LoadGFX ;($C7AB)Switch to girl gfx LC5EB:* ldy #$14 ;Entry 20 in GFXInfo table. LC5ED: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC5F0: ldy #$17 ;Entry 23 in GFXInfo table. LC5F2: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC5F5: ldy #$18 ;Entry 24 in GFXInfo table. LC5F7: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC5FA: ldy #$19 ;Entry 25 in GFXInfo table. LC5FC: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC5FF: ldy #$16 ;Entry 22 in GFXInfo table. LC601: jmp LoadGFX ;($C7AB)Load pattern table GFX. InitBrinstarGFX: LC604: ldy #$03 ;Entry 3 in GFXInfo table. LC606: jsr LoadGFX ;($C7AB)Load pattern table GFX. Lc609: ldy #$04 ;Entry 4 in GFXInfo table. LC60B: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC60E: ldy #$05 ;Entry 5 in GFXInfo table. LC610: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC613: ldy #$06 ;Entry 6 in GFXInfo table. LC615: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC618: ldy #$19 ;Entry 25 in GFXInfo table. LC61A: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC61D: ldy #$16 ;Entry 22 in GFXInfo table. LC61F: jmp LoadGFX ;($C7AB)Load pattern table GFX. InitNorfairGFX: LC622: ldy #$04 ;Entry 4 in GFXInfo table. LC624: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC627: ldy #$05 ;Entry 5 in GFXInfo table. LC629: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC62C: ldy #$07 ;Entry 7 in GFXInfo table. LC62E: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC631: ldy #$08 ;Entry 8 in GFXInfo table. LC633: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC636: ldy #$09 ;Entry 9 in GFXInfo table. LC638: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC63B: ldy #$19 ;Entry 25 in GFXInfo table. LC63D: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC640: ldy #$16 ;Entry 22 in GFXInfo table. LC642: jmp LoadGFX ;($C7AB)Load pattern table GFX. InitTourianGFX: LC645: ldy #$05 ;Entry 5 in GFXInfo table. LC647: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC64A: ldy #$0A ;Entry 10 in GFXInfo table. LC64C: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC64F: ldy #$0B ;Entry 11 in GFXInfo table. LC651: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC654: ldy #$0C ;Entry 12 in GFXInfo table. LC656: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC659: ldy #$0D ;Entry 13 in GFXInfo table. LC65B: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC65E: ldy #$0E ;Entry 14 in GFXInfo table. LC660: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC663: ldy #$1A ;Entry 26 in GFXInfo table. LC665: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC668: ldy #$1C ;Entry 28 in GFXInfo table. LC66A: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC66D: ldy #$19 ;Entry 25 in GFXInfo table. LC66F: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC672: ldy #$16 ;Entry 22 in GFXInfo table. LC674: jmp LoadGFX ;($C7AB)Load pattern table GFX. InitKraidGFX: LC677: ldy #$04 ;Entry 4 in GFXInfo table. LC679: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC67C: ldy #$05 ;Entry 5 in GFXInfo table. LC67E: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC681: ldy #$0A ;Entry 10 in GFXInfo table. LC683: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC686: ldy #$0F ;Entry 15 in GFXInfo table. LC688: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC68B: ldy #$10 ;Entry 16 in GFXInfo table. LC68D: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC690: ldy #$11 ;Entry 17 in GFXInfo table. LC692: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC695: ldy #$19 ;Entry 25 in GFXInfo table. LC697: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC69A: ldy #$16 ;Entry 22 in GFXInfo table. LC69C: jmp LoadGFX ;($C7AB)Load pattern table GFX. InitRidleyGFX: LC69F: ldy #$04 ;Entry 4 in GFXInfo table. LC6A1: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC6A4: ldy #$05 ;Entry 5 in GFXInfo table. LC6A6: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC6A9: ldy #$0A ;Entry 10 in GFXInfo table. LC6AB: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC6AE: ldy #$12 ;Entry 18 in GFXInfo table. LC6B0: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC6B3: ldy #$13 ;Entry 19 in GFXInfo table. LC6B5: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC6B8: ldy #$19 ;Entry 25 in GFXInfo table. LC6BA: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC6BD: ldy #$16 ;Entry 22 in GFXInfo table. LC6BF: jmp LoadGFX ;($C7AB)Load pattern table GFX. InitGFX6: LC6C2: ldy #$01 ;Entry 1 in GFXInfo table. LC6C4: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC6C7: ldy #$02 ;Entry 2 in GFXInfo table. LC6C9: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC6CC: ldy #$19 ;Entry 25 in GFXInfo table. LC6CE: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC6D1: ldy #$16 ;Entry 22 in GFXInfo table. LC6D3: jmp LoadGFX ;($C7AB)Load pattern table GFX. InitGFX7: LC6D6: ldy #$17 ;Entry 23 in GFXInfo table. LC6D8: jsr LoadGFX ;($C7AB)Load pattern table GFX. LC6DB: ldy #$16 ;Entry 22 in GFXInfo table. LC6DD: jmp LoadGFX ;($C7AB)Load pattern table GFX. ;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: LC6E0: .byte $06 ;[SPR]Samus, items. Entry 0. LC6E1: .word $8000, $0000, $09A0 LC6E7: .byte $04 ;[SPR]Samus in ending. Entry 1. LC6E8: .word $8D60, $0000, $0520 LC6EE: .byte $01 ;[BGR]Partial font, "The End". Entry 2. LC6EF: .word $8D60, $1000, $0400 LC6F5: .byte $06 ;[BGR]Brinstar rooms. Entry 3. LC6F6: .word $9DA0, $1000, $0150 LC6FC: .byte $05 ;[BGR]Misc. objects. Entry 4. LC6FD: .word $8D60, $1200, $0450 LC703: .byte $06 ;[BGR]More Brinstar rooms. Entry 5. LC704: .word $9EF0, $1800, $0800 LC70A: .byte $01 ;[SPR]Brinstar enemies. Entry 6. LC70B: .word $9160, $0C00, $0400 LC711: .byte $06 ;[BGR]Norfair rooms. Entry 7. LC712: .word $A6F0, $1000, $0260 LC718: .byte $06 ;[BGR]More Norfair rooms. Entry 8. LC719: .word $A950, $1700, $0070 LC71F: .byte $02 ;[SPR]Norfair enemies. Entry 9. LC720: .word $8D60, $0C00, $0400 LC726: .byte $06 ;[BGR]Tourian rooms. Entry 10. LC727: .word $A9C0, $1000, $02E0 LC72D: .byte $06 ;[BGR]More Tourian rooms. Entry 11. LC72E: .word $ACA0, $1200, $0600 LC734: .byte $06 ;[BGR]Mother Brain room. Entry 12. LC735: .word $B2A0, $1900, $0090 LC73B: .byte $05 ;[BGR]Misc. object. Entry 13. LC73C: .word $91B0, $1D00, $0300 LC742: .byte $02 ;[SPR]Tourian enemies. Entry 14. LC743: .word $9160, $0C00, $0400 LC749: .byte $06 ;[BGR]More Tourian rooms. Entry 15. LC74A: .word $B330, $1700, $00C0 LC750: .byte $04 ;[BGR]Misc. object and fonts. Entry 16. LC751: .word $9360, $1E00, $0200 LC757: .byte $03 ;[SPR]Miniboss I enemies. Entry 17. LC758: .word $8D60, $0C00, $0400 LC75E: .byte $06 ;[BGR]More Tourian Rooms. Entry 18. LC75F: .word $B3F0, $1700, $00C0 LC765: .byte $03 ;[SPR]Miniboss II enemies. Entry 19. LC766: .word $9160, $0C00, $0400 LC76C: .byte $06 ;[SPR]Inrto/End sprites. Entry 20. LC76D: .word $89A0, $0C00, $0100 LC773: .byte $06 ;[BGR]Title. Entry 21. LC774: .word $8BE0, $1400, $0500 LC77A: .byte $06 ;[BGR]Solid tiles. Entry 22. LC77B: .word $9980, $1FC0, $0040 LC781: .byte $06 ;[BGR]Complete font. Entry 23. LC782: .word $B4C0, $1000, $0400 LC788: .byte $06 ;[BGR]Complete font. Entry 24. LC789: .word $B4C0, $0A00, $00A0 LC78F: .byte $06 ;[BGR]Solid tiles. Entry 25. LC790: .word $9980, $0FC0, $0040 LC796: .byte $06 ;[BGR]Complete font. Entry 26. LC797: .word $B4C0, $1D00, $02A0 LC79D: .byte $06 ;[SPR]Suitless Samus. Entry 27. LC79E: .word $90E0, $0000, $07B0 LC7A4: .byte $06 ;[BGR]Exclaimation point. Entry 28. LC7A5: .word $9890, $1F40, $0010 ;--------------------------------[ Pattern table loading routines ]--------------------------------- ;Y contains the GFX header to fetch from the table above, GFXInfo. LoadGFX: LC7AB: lda #$FF ; LC7AD:* clc ;Every time y decrements, the entry into the table--> LC7AE: adc #$07 ;is increased by 7. When y is less than 0, A points--> LC7B0: dey ;to the last byte of the entry in the table. LC7B1: bpl - ; LC7B3: tay ;Transfer offset into table to Y. LC7B4: ldx #$06 ; LC7B6:* lda GFXInfo,y ; LC7B9: sta $00,x ;Copy entries from GFXInfo to $00-$06. LC7BB: dey ; LC7BC: dex ; LC7BD: bpl - ; LC7BF: ldy $00 ;ROM bank containing the GFX data. LC7C1: jsr ROMSwitch ;($C4EF)Switch to that bank. LC7C4: lda PPUCNT0ZP ; LC7C6: and #$FB ; LC7C8: sta PPUCNT0ZP ;Set the PPU to increment by 1. LC7CA: sta PPUControl0 ; LC7CD: jsr CopyGFXBlock ;($C7D5)Copy graphics into pattern tables. LC7D0: ldy CurrentBank ; LC7D2: jmp ROMSwitch ;($C4FE)Switch back to the "old" bank. ;Writes tile data from ROM to VRAM, according to the gfx header data ;contained in $00-$06. CopyGFXBlock: LC7D5: lda $05 ; LC7D7: bne GFXCopyLoop ;If $05 is #$00, decrement $06 before beginning. LC7D9: dec $06 ; GFXCopyLoop: LC7DB: lda $04 ; LC7DD: sta PPUAddress ;Set PPU to proper address for GFX block write. LC7E0: lda $03 ; LC7E2: sta PPUAddress ; LC7E5: ldy #$00 ;Set offset for GFX data to 0. LC7E7:* lda ($01),y ; LC7E9: sta PPUIOReg ;Copy GFX data byte from ROM to Pattern table. LC7EC: dec $05 ;Decrement low byte of data length. LC7EE: bne + ;Branch if high byte does not need decrementing. LC7F0: lda $06 ; LC7F2: beq ++ ;If copying complete, branch to exit. LC7F4: dec $06 ;Decrement when low byte has reached 0. LC7F6:* iny ;Increment to next byte to copy. LC7F7: bne -- ; LC7F9: inc $02 ;After 256 bytes loaded, increment upper bits of--> LC7FB: inc $04 ;Source and destination addresses. LC7FD: jmp GFXCopyLoop ;(&C7DB)Repeat copy routine. LC800:* rts ; ;-------------------------------------------[ AreaInit ]--------------------------------------------- AreaInit: LC801: lda #$00 ; LC803: sta ScrollX ;Clear ScrollX. LC805: sta ScrollY ;Clear ScrollY. LC807: lda PPUCNT0ZP ; LC809: and #$FC ;Sets nametable address = $2000. LC80B: sta PPUCNT0ZP ; LC80D: inc MainRoutine ;Increment MainRoutine to MoreInit. LC80F: lda Joy1Status ; LC811: and #$C0 ;Stores status of both the A and B buttons. LC813: sta ABStatus ;Appears to never be accessed. LC815: jsr EraseAllSprites ;($C1A3)Clear all sprite info. LC818: lda #$10 ;Prepare to load Brinstar memory page. LC81A: jsr IsEngineRunning ;($CA18)Check to see if ok to switch lower memory page. ;------------------------------------------[ MoreInit ]--------------------------------------------- MoreInit: LC81D: ldy #$01 ; LC81F: sty PalDataPending ;Palette data pending = yes. LC821: ldx #$FF ; LC823: stx SpareMem75 ;$75 Not referenced ever again in the game. LC825: inx ;X=0. LC826: stx AtEnding ;Not playing ending scenes. LC829: stx DoorStatus ;Samus not in door. LC82B: stx SamusDoorData ;Samus is not inside a door. LC82D: stx UpdatingProjectile ;No projectiles need to be updated. LC82F: txa ;A=0. LC830:* cpx #$65 ;Check to see if more RAM to clear in $7A thru $DE. LC832: bcs + ; LC834: sta $7A,x ;Clear RAM $7A thru $DE. LC836:* cpx #$FF ;Check to see if more RAM to clear in $300 thru $3FE. LC838: bcs + ; LC83A: sta ObjAction,x ;Clear RAM $300 thru $3FE. LC83D:* inx ; LC83E: bne --- ;Loop until all required RAM is cleared. LC840: jsr ScreenOff ;($C439)Turn off Background and visibility. LC843: jsr ClearNameTables ;($C158)Clear screen data. LC846: jsr EraseAllSprites ;($C1A3)Erase all sprites from sprite RAM. LC849: jsr DestroyEnemies ;($C8BB) stx DoorOnNameTable3 ;Clear data about doors on the name tables. stx DoorOnNameTable0 ; inx ;X=1. stx SpareMem30 ;Not accessed by game. inx ;X=2. LC854: stx ScrollDir ;Set initial scroll direction as left. lda $95D7 ;Get Samus start x pos on map. sta MapPosX ; lda $95D8 ;Get Samus start y pos on map. sta MapPosY ; LC860: lda $95DA ; Get ??? Something to do with palette switch sta PalToggle lda #$FF sta RoomNumber ;Room number = $FF(undefined room). LC869: jsr CopyPtrs ; copy pointers from ROM to RAM LC86C: jsr GetRoomNum ;($E720)Put room number at current map pos in $5A. * jsr SetupRoom ;($EA2B) ldy RoomNumber ; load room number iny bne - ldy CartRAMPtr+1 sty $01 ldy CartRAMPtr sty $00 lda PPUCNT0ZP and #$FB ; PPU increment = 1 sta PPUCNT0ZP sta PPUControl0 ldy PPUStatus ; reset PPU addr flip/flop ; Copy room RAM #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 SpareMem30 ;Not accessed by game. inc MainRoutine ;SamusInit is next routine to run. jmp ScreenOn ; CopyPtrs ; ======== ; Copy 7 16-bit pointers from $959A thru $95A7 to $3B thru $48. CopyPtrs: ldx #$0D * lda AreaPointers+2,x sta RoomPtrTable,x dex bpl - rts ; DestroyEnemies ; ============== DestroyEnemies: LC8BB: lda #$00 tax * cpx #$48 bcs + sta $97,x * sta EnStatus,x pha pla inx bne -- stx MetroidOnSamus ;Samus had no Metroid stuck to her. jmp $95AB ; SamusInit ; ========= ; Code that sets up Samus, when the game is first started. SamusInit: LC8D1: lda #$08 ; LC8D3: sta MainRoutine ;SamusIntro will be executed next frame. LC8D5: lda #$2C ;440 frames to fade in Samus(7.3 seconds). LC8D7: sta Timer3 ; LC8D9: jsr IntroMusic ;($CBFD)Start the intro music. LC8DC: ldy #sa_FadeIn0 ; sty ObjAction ;Set Samus status as fading onto screen. ldx #$00 stx SamusBlink dex ;X = $FF stx $0728 stx $0730 stx $0732 stx $0738 stx EndTimerLo ;Set end timer bytes to #$FF as--> stx EndTimerHi ;escape timer not currently active. stx $8B stx $8E ldy #$27 lda InArea and #$0F beq + ;Branch if Samus starting in Brinstar. lsr ScrollDir ;If not in Brinstar, change scroll direction from left--> ldy #$2F ;to down. and set PPU for horizontal mirroring. * sty MirrorCntrl ; sty MaxMissilePickup sty MaxEnergyPickup lda $95D9 ;Samus' initial vertical position sta ObjectY ; lda #$80 ;Samus' initial horizontal position sta ObjectX ; lda PPUCNT0ZP ; and #$01 ;Set Samus' name table position to current name table--> sta ObjectHi ;active in PPU. lda #$00 ; sta HealthLo ;Starting health is--> lda #$03 ;set to 30 units. sta HealthHi ; * rts ; ;------------------------------------[ Main game engine ]-------------------------------------------- GameEngine: LC92B: jsr ScrollDoor ;($E1F1)Scroll doors, if needed. 2 routine calls scrolls--> LC92E: jsr ScrollDoor ;($E1F1)twice as fast as 1 routine call. LC931: lda NARPASSWORD ; LC934: beq + ; LC936: lda #$03 ;The following code is only accessed if --> LC938: sta HealthHi ;NARPASSWORD has been entered at the --> LC93B: lda #$FF ;password screen. Gives you new health,--> LC93D: sta SamusGear ;missiles and every power-up every frame. LC940: lda #$05 ; LC942: sta MissileCount ; LC945:* jsr UpdateWorld ;($CB29)Update Samus, enemies and room tiles. LC948: lda MiniBossKillDelay ; LC94B: ora PowerUpDelay ;Check if mini boss was just killed or powerup aquired.--> LC94E: beq + ;If not, branch. LC950: lda #$00 ; LC952: sta MiniBossKillDelay ;Reset delay indicators. LC955: sta PowerUpDelay ; LC958: lda #$18 ;Set timer for 240 frames(4 seconds). LC95A: ldx #$03 ;GameEngine routine to run after delay expires LC95C: jsr SetTimer ;($C4AA)Set delay timer and game engine routine. LC95F:* lda ObjAction ;Check is Samus is dead. LC962: cmp #sa_Dead2 ;Is Samus dead?--> LC964: bne --- ;exit if not. LC966: lda AnimDelay ;Is Samus still exploding?--> LC969: bne --- ;Exit if still exploding. LC96B: jsr SilenceMusic ;Turn off music. LC96E: lda MotherBrainStatus ; LC970: cmp #$0A ;Is mother brain already dead? If so, branch. LC972: beq + ; LC974: lda #$04 ;Set timer for 40 frames (.667 seconds). LC976: ldx #$04 ;GameOver routine to run after delay expires. LC978: jmp SetTimer ;($C4AA)Set delay timer and run game over routine. LC97B:* inc MainRoutine ;Next routine to run is GameOver. LC97D: rts ; ;----------------------------------------[ Update age ]---------------------------------------------- ;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: LC97E: lda GameMode ; LC980: bne ++ ;Exit if at title/password screen. LC982: lda MainRoutine ; LC984: cmp #$03 ;Is game engine running? LC986: bne ++ ;If not, don't update age. LC988: ldx FrameCount ;Only update age when FrameCount is zero--> LC98A: bne ++ ;(which is approx. every 4.266666666667 seconds). LC98C: inc SamusAge,x ;Minor Age = Minor Age + 1. LC98F: lda SamusAge ; LC992: cmp #$D0 ;Has Minor Age reached $D0?--> LC994: bcc ++ ;If not, we're done.--> LC996: lda #$00 ;Else reset minor age. LC998: sta SamusAge ; LC99B:* cpx #$03 ; LC99D: bcs + ;Loop to update middle age and possibly major age. LC99F: inx ; LC9A0: inc SamusAge,x ; LC9A3: beq - ;Branch if middle age overflowed, need to increment--> LC9A5:* rts ;major age too. Else exit. ;-------------------------------------------[ Game over ]-------------------------------------------- GameOver: LC9A6: lda #$1C ;GameOver is the next routine to run. LC9A8: sta TitleRoutine ; LC9AA: lda #$01 ; LC9AC: sta SwitchPending ;Prepare to switch to title memory page. LC9AE: jmp ScreenOff ;($C439)Turn screen off. ;------------------------------------------[ Pause mode ]-------------------------------------------- PauseMode: LC9B1: lda Joy2Status ;Load buttons currently being pressed on joypad 2. LC9B3: and #$88 ; LC9B5: eor #$88 ;both A & UP pressed?--> LC9B7: bne Exit14 ;Exit if not. LC9B9: ldy EndTimerHi ; LC9BC: iny ;Is escape timer active?--> LC9BD: bne Exit14 ;Sorry, can't quit if this is during escape scence. LC9BF: sta GamePaused ;Clear pause game indicator. LC9C1: inc MainRoutine ;Display password is the next routine to run. Exit14: LC9C3: rts ;Exit for routines above and below. ;------------------------------------------[ GoPassword ]-------------------------------------------- GoPassword: LC9C4: lda #$19 ;DisplayPassword is next routine to run. LC9C6: sta TitleRoutine ; LC9C8: lda #$01 ; LC9CA: sta SwitchPending ;Prepare to switch to intro memory page. LC9CC: lda NoiseSFXFlag ; LC9CF: ora #$01 ;Silence music. LC9D1: sta NoiseSFXFlag ; LC9D4: jmp ScreenOff ;($C439)Turn off screen. ;-----------------------------------------[ Samus intro ]-------------------------------------------- SamusIntro: LC9D7: jsr EraseAllSprites ;($C1A3)Clear all sprites off screen. LC9DA: ldy ObjAction ;Load Samus' fade in status. LC9DD: lda Timer3 ; LC9E0: bne + ;Branch if Intro still playing. ;Fade in complete. LC9E2: sta ItemRoomMusicStatus ;Make sure item room music is not playing. LC9E4: lda #sa_Begin ;Samus facing forward and can't be hurt. LC9E6: sta ObjAction ; LC9E8: jsr StartMusic ;($D92C)Start main music. LC9EB: jsr SelectSamusPal ;($CB73)Select proper Samus palette. LC9EE: lda #$03 ; LC9F0: sta MainRoutine ;Game engine will be called next frame. ;Still fading in. LC9F2:* cmp #$1F ;When 310 frames left of intro, display Samus. LC9F4: bcs Exit14 ;Branch if not time to start drawing Samus. LC9F6: cmp SamusFadeInTimeTbl-20,y ;sa_FadeIn0 is beginning of table. LC9F9: bne + ;Every time Timer3 equals one of the entries in the table--> LC9FB: inc ObjAction ;below, change the palette used to color Samus. LC9FE: sty PalDataPending ; LCA00:* lda FrameCount ;Is game currently on an odd frame?--> LCA02: lsr ;If not, branch to exit. LCA03: bcc Exit14 ;Only display Samus on odd frames [the blink effect]. LCA05: lda #an_SamusFront ;Samus front animation is animation to display.--> LCA07: jsr SetSamusAnim ;($CF6B)while fading in. LCA0A: lda #$00 ; LCA0C: sta SpritePagePos ;Samus sprites start at Sprite00RAM. LCA0E: sta PageIndex ;Samus RAM is first set of RAM. LCA10: jmp AnimDrawObject ;($DE47)Draw Samus on screen. ;The following table marks the time remaining in Timer3 when a palette change should occur during ;the Samus fade-in sequence. This creates the fade-in effect. SamusFadeInTimeTbl: LCA13: .byte $1E,$14,$0B,$04,$FF ;---------------------------------[ Check if game engine running ]----------------------------------- IsEngineRunning: LCA18: ldy MainRoutine ;If Samus is fading in or the wait timer is--> LCA1A: cpy #$07 ;active, return from routine. LCA1C: beq + ; LCA1E: cpy #$03 ;Is game engine running? LCA20: beq ++ ;If yes, branch to SwitchBank. LCA22:* rts ;Exit if can't switch bank. ;-----------------------------------------[ Switch bank ]-------------------------------------------- ;Switch to appropriate area bank SwitchBank: LCA23:* sta InArea ;Save current area Samus is in. LCA25: and #$0F ; LCA27: tay ;Use 4 LSB to load switch pending offset from BankTable table. LCA28: lda BankTable,y ;Base is $CA30. LCA2B: sta SwitchPending ;Store switch data. LCA2D: jmp CheckSwitch ;($C4DE)Switch lower 16KB to appropriate memory page. ;Table used by above subroutine. ;Each value is the area bank number plus one. BankTable: LCA30: .byte $02 ;Brinstar. LCA31: .byte $03 ;Norfair. LCA32: .byte $05 ;Kraid hideout. LCA33: .byte $04 ;Tourian. LCA34: .byte $06 ;Ridley hideout. ;----------------------------------[ Saved game routines (not used) ]-------------------------------- AccessSavedGame: LCA35: pha ;Save two copies of A. Why? Who knows. This code is--> LCA36: pha ;Never implemented. A contains data slot to work on. LCA37: jsr GetGameDataIndex ;($CA96)Get index to this save game Samus data info. LCA3A: lda EraseGame ; LCA3D: bpl + ;Is MSB set? If so, erase saved game data. Else branch. LCA3F: and #$01 ; LCA41: sta EraseGame ;Clear MSB so saved game data is not erased again. LCA44: jsr EraseAllGameData ;($CAA1)Erase selected saved game data. LCA47: lda #$01 ;Indicate this saved game has been erased.--> LCA49: sta $7800,y ;Saved game 0=$780C, saved game 1=$781C, saved game 2=$782C. LCA4C:* lda MainRoutine ; LCA4E: cmp #$01 ;If initializing the area at the start of the game, branch--> LCA50: beq +++ ;to load Samus' saved game info. SaveGameData: LCA52: lda InArea ;Save game based on current area Samus is in. Don't know why. LCA54: jsr SavedDataBaseAddr ;($CAC6)Find index to unique item history for this saved game. LCA57: ldy #$3F ;Prepare to save unique item history which is 64 bytes--> LCA59:* lda NumberOfUniqueItems,y ;in length. LCA5C: sta ($00),y ;Save unique item history in appropriate saved game slot. LCA5E: dey ; LCA5F: bpl - ;Loop until unique item history transfer complete. LCA61: ldy SamusDataIndex ;Prepare to save Samus' data. LCA64: ldx #$00 ; LCA66:* lda SamusStat00,x ; LCA69: sta SamusData,y ;Save Samus' data in appropriate saved game slot. LCA6C: iny ; LCA6D: inx ; LCA6E: cpx #$10 ; LCA70: bne - ;Loop until Samus' data transfer complete. LoadGameData: LCA72:* pla ;Restore A to find appropriate saved game to load. LCA73: jsr SavedDataBaseAddr ;($CAC6)Find index to unique item history for this saved game. LCA76: ldy #$3F ;Prepare to load unique item history which is 64 bytes--> LCA78:* lda ($00),y ;in length. LCA7A: sta NumberOfUniqueItems,y ;Loop until unique item history is loaded. LCA7D: dey ; LCA7E: bpl - ; LCA80: bmi + ;Branch always. LCA82: pha ; LCA83:* ldy SamusDataIndex ;Prepare to load Samus' data. LCA86: ldx #$00 ; LCA88:* lda SamusData,y ; LCA8B: sta SamusStat00,x ;Load Samus' data from appropriate saved game slot. LCA8E: iny ; LCA8F: inx ; LCA90: cpx #$10 ; LCA92: bne - ;Loop until Samus' data transfer complete. LCA94: pla ; LCA95: rts ; GetGameDataIndex: LCA96: lda DataSlot ; LCA99: asl ;A contains the save game slot to work on (0 1 or 2).--> LCA9A: asl ;This number is transferred to the upper four bits to--> LCA9B: asl ;find the offset for Samus' data for this particular--> LCA9C: asl ;saved game (#$00, #$10 or #$20). LCA9D: sta SamusDataIndex ; LCAA0: rts ; EraseAllGameData: LCAA1: lda #$00 ;Always start at saved game 0. Erase all 3 saved games. LCAA3: jsr SavedDataBaseAddr ;($CAC6)Find index to unique item history for this saved game. LCAA6: inc $03 ;Prepare to erase saved game info at $6A00 and above. LCAA8: ldy #$00 ;Fill saved game data with #$00. LCAAA: tya ; LCAAB:* sta ($00),y ;Erase unique item histories from $69B4 to $69FF. LCAAD: cpy #$40 ; LCAAF: bcs + ;IF 64 bytes alrady erased, no need to erase any more--> LCAB1: sta ($02),y ;in the $6A00 and above range. LCAB3:* iny ; LCAB4: bne -- ;Lop until all saved game data is erased. LCAB6: ldy SamusDataIndex ;Load proper index to desired Samus data to erase. LCAB9: ldx #$00 ; LCABB: txa ; LCABC:* sta SamusData,y ;Erase Samus' data. LCABF: iny ; LCAC0: inx ; LCAC1: cpx #$0C ; LCAC3: bne - ;Loop until all data is erased. LCAC5: rts ; ;This routine finds the base address of the unique item history for the desired saved game (0, 1 or 2). ;The memory set aside for each unique item history is 64 bytes and occupies memory addresses $69B4 thru ;$6A73. SavedDataBaseAddr: LCAC6: pha ;Save contents of A. LCAC7: lda DataSlot ;Load saved game data slot to load. LCACA: asl ;*2. Table values below are two bytes. LCACB: tax ; LCACC: lda SavedDataTable,x ; LCACF: sta $00 ;Load $0000 and $0002 with base addresses from--> LCAD1: sta $02 ;table below($69B4). LCAD3: lda SavedDataTable+1,x ; LCAD6: sta $01 ; LCAD8: sta $03 ; LCADA: pla ;Restore A. LCADB: and #$0F ;Discard upper four bits in A. LCADD: tax ;X used for counting loop. LCADE: beq +++ ;Exit if at saved game 0. No further calculations required. LCAE0:* lda $00 ; LCAE2: clc ; LCAE3: adc #$40 ; LCAE5: sta $00 ;Loop to add #$40 to base address of $69B4 in order to find--> LCAE7: bcc + ;the proper base address for this saved game data. (save--> LCAE9: inc $01 ;slot 0 = $69B4, save slot 1 = $69F4, save slot 2 = $6A34). LCAEB:* dex ; LCAEC: bne -- ; LCAEE:* rts ; ;Table used by above subroutine to find base address to load saved game data from. The slot 0 ;starts at $69B4, slot 1 starts at $69F4 and slot 2 starts at $6A34. SavedDataTable: LCAEF: .word ItmeHistory ;($69B4)Base for save game slot 0. LCAF1: .word ItmeHistory ;($69B4)Base for save game slot 1. LCAF3: .word ItmeHistory ;($69B4)Base for save game slot 2. ;----------------------------------------[ Choose ending ]------------------------------------------- ;Determine what type of ending is to be shown, based on Samus' age. ChooseEnding: LCAF5: ldy #$01 ; LCAF7:* lda SamusAge+2 ;If SamusAge+2 anything but #$00, load worst--> LCAFA: bne + ;ending(more than 37 hours of gameplay). LCAFC: lda SamusAge+1 ; LCAFF: cmp AgeTable-1,y ;Loop four times to determine--> LCB02: bcs + ;ending type from table below. LCB04: iny ; LCB05: cpy #$05 ; LCB07: bne - ; LCB09:* sty EndingType ;Store the ending # (1..5), 5=best ending LCB0C: lda #$00 ; LCB0E: cpy #$04 ;Was the best or 2nd best ending achieved? LCB10: bcc + ;Branch if not (suit stays on) LCB12: lda #$01 ; LCB14:* sta JustInBailey ;Suit OFF, baby! LCB17: rts ; ;Table used by above subroutine to determine ending type. AgeTable: LCB18: .byte $7A ;Max. 37 hours LCB19: .byte $16 ;Max. 6.7 hours LCB1A: .byte $0A ;Max. 3.0 hours LCB1B: .byte $04 ;Best ending. Max. 1.2 hours ;--------------------------------[ Clear screen data (not used) ]------------------------------------ ClearScreenData: LCB1C: jsr ScreenOff ;($C439)Turn off screen. LCB1F: lda #$FF ; LCB21: sta $00 ;Prepare to fill nametable with #$FF. LCB23: jsr ClearNameTable ;($C175)Clear selected nametable. LCD26: jmp EraseAllSprites ;($C1A3)Clear sprite data. ;---------------------------------------------------------------------------------------------------- ; ===== THE REAL GUTS OF THE GAME ENGINE! ===== UpdateWorld: LCB29: ldx #$00 ;Set start of sprite RAM to $0200. LCB2B: stx SpritePagePos ; LCB2D: jsr UpdateEnemies ;($F345)Display of enemies. LCB30: jsr UpdateProjectiles ;($D4BF)Display of bullets/missiles/bombs. LCB33: jsr UpdateSamus ;($CC0D)Display/movement of Samus. LCB36: jsr AreaRoutine ;($95C3)Area specific routine. LCB39: jsr UpdateElevator ;($D7B3)Display of elevators. LCB3C: jsr UpdateStatues ;($D9D4)Display of Ridley & Kraid statues. LCB3F: jsr $FA9D ; destruction of enemies LCB42: jsr LFC65 ; update of Mellow/Memu enemies LCB45: jsr LF93B LCB48: jsr LFBDD ; destruction of green spinners LCB4B: jsr SamusEnterDoor ;($8B13)Check if Samus entered a door. LCB4E: jsr $8B79 ; display of doors LCB51: jsr UpdateTiles ; tile de/regeneration LCB54: jsr LF034 ; Samus <--> enemies crash detection LCB57: jsr DisplayBar ;($E0C1)Display of status bar. jsr LFAF2 jsr CheckMissileToggle jsr UpdateItems ;($DB37)Display of power-up items. jsr LFDE3 ;Clear remaining sprite RAM ldx SpritePagePos lda #$F4 * sta Sprite00RAM,x jsr Xplus4 ; X = X + 4 bne - rts ;------------------------------------[ Select Samus palette ]---------------------------------------- ; 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: LCB73: tya ; pha ;Temp storage of Y on the stack. lda SamusGear asl asl asl ;CF contains Varia status (1 = Samus has it) lda MissileToggle ;A = 1 if Samus is firing missiles, else 0 rol ;Bit 0 of A = 1 if Samus is wearing Varia adc #$02 ldy JustInBailey ;In suit?--> beq + ;If so, Branch. clc adc #$17 ;Add #$17 to the pal # to reach "no suit"-palettes. * sta PalDataPending ;Palette will be written next NMI. pla ; tay ;Restore the contents of y. rts ; ;----------------------------------[ Initiate SFX and music routines ]------------------------------- ;Initiate sound effects. SilenceMusic: ;The sound flags are stored in memory--> LCB8E: lda #$01 ;starting at $0680. The following is a--> LCB90: bne SFX_SetX0 ;list of sound effects played when the--> ;flags are set: PauseMusic: ; LCB92: lda #$02 ;$0680: These SFX use noise channel. LCB94: bne SFX_SetX0 ;Bit 7 - No sound. ;Bit 6 - ScrewAttack. SFX_SamusWalk: ;Bit 5 - MissileLaunch. LCB96: lda #$08 ;Bit 4 - BombExplode. LCB98: bne SFX_SetX0 ;Bit 3 - SamusWalk. ;Bit 2 - SpitFlame. SFX_BombExplode: ;Bit 1 - No sound. LCB9A: lda #$10 ;Bit 0 - No sound. LCB9C: bne SFX_SetX0 ; ;$0681: These SFX use sq1 channel. SFX_MissileLaunch: ;Bit 7 - MissilePickup. LCB9E: lda #$20 ;Bit 6 - EnergyPickup. ;Bit 5 - Metal. SFX_SetX0: ;Bit 4 - BulletFire. LCBA0: ldx #$00 ;Bit 3 - OutOfHole. LCBA2: beq SFX_SetSoundFlag ;Bit 2 - EnemyHit. ;Bit 1 - SamusJump. SFX_OutOfHole: ;Bit 0 - WaveFire. LCBA4: lda #$08 ; LCBA6: bne SFX_SetX1 ;$0682: Not used. ; SFX_BombLaunch: ;$0683: These SFX use tri channel. LCBA8: lda #$01 ;Bit 7 - SamusDie. LCBAA: bne SFX_SetX3 ;Bit 6 - DoorOpenClose. ;Bit 5 - MetroidHit. SFX_SamusJump: ;Bit 4 - StatueRaise. LCBAC: lda #$02 ;Bit 3 - Beep. LCBAE: bne SFX_SetX1 ;Bit 2 - BigEnemyHit. ;Bit 1 - SamusBall. SFX_EnemyHit: ;Bit 0 - BombLaunch. LCBB0: lda #$04 ; LCBB2: bne SFX_SetX1 ;$0684: These SFX use multi channels. ;Bit 7 - FadeInMusic (music). SFX_BulletFire: ;Bit 6 - PowerUpMusic (music). LCBB4: lda #$10 ;Bit 5 - EndMusic (Page 0 only)(music). LCBB6: bne SFX_SetX1 ;Bit 4 - IntroMusic(Page 0 only)(music). ;Bit 3 - not used (SFX). SFX_Metal: ;Bit 2 - SamusHit (SFX). LCBB8: lda #$20 ;Bit 1 - BossHit (SFX). LCBBA: bne SFX_SetX1 ;Bit 0 - IncorrectPassword (SFX). ; SFX_EnergyPickup: ;$0685: Music flags. The music flags start different--> LCBBC: lda #$40 ;music depending on what memory page is loaded. The--> LCBBD: bne SFX_SetX1 ;following lists what bits start what music for each--> ;memory page. SFX_MissilePickup: ; LCBC0: lda #$80 ;Page 0: Intro/ending. ;Bit 7 - Not used. SFX_SetX1: ;Bit 6 - TourianMusic. LCBC2: ldx #$01 ;Bit 5 - ItemRoomMusic. LCBC4: bne SFX_SetSoundFlag ;Bit 4 - Not used. ;Bit 3 - Not used. SFX_WaveFire: ;Bit 2 - Not used. LCBC6: lda #$01 ;Bit 1 - Not used. LCBC8: bne SFX_SetX1 ;Bit 0 - Not used. ; SFX_ScrewAttack: ;Page 1: Brinstar. LCBCA: lda #$40 ;Bit 7 - Not used. LCBCC: bne SFX_SetX0 ;Bit 6 - TourianMusic. ;Bit 5 - ItemRoomMusic. SFX_BigEnemyHit: ;Bit 4 - Not used. LCBCE: lda #$04 ;Bit 3 - Not used. LCBD0: bne SFX_SetX3 ;Bit 2 - Not used. ;Bit 1 - Not used. SFX_MetroidHit: ;Bit 0 - BrinstarMusic. LCBD2: lda #$20 ; LCBD4: bne SFX_SetX3 ;Page 2: Norfair. ;Bit 7 - Not used. SFX_BossHit: ;Bit 6 - TourianMusic. LCBD6: lda #$02 ;Bit 5 - ItemRoomMusic. LCBD8: bne SFX_SetX4 ;Bit 4 - Not used. ;Bit 3 - NorfairMusic. SFX_Door: ;Bit 2 - Not used. LCBDA: lda #$40 ;Bit 1 - Not used. LCBDC: bne SFX_SetX3 ;Bit 0 - Not used. ; SFX_SamusHit: ;Page 3: Tourian. LCBDE: lda #$04 ;Bit 7 - Not used. LCBE0: bne SFX_SetX4 ;Bit 6 - TourianMusic ;Bit 5 - ItemRoomMusic. SFX_SamusDie: ;Bit 4 - Not used. LCBE2: lda #$80 ;Bit 3 - Not used. LCBE4: bne SFX_SetX3 ;Bit 2 - EscapeMusic. ;Bit 1 - MotherBrainMusic SFX_SetX2: ;Bit 0 - Not used. LCBE6: ldx #$02 ; ;Page 4: Kraid. SFX_SetSoundFlag: ;Bit 7 - RidleyAreaMusic. LCBE8: ora $0680,x ;Bit 6 - TourianMusic. LCBEB: sta $0680,x ;Bit 5 - ItemRoomMusic. LCBEE: rts ;Bit 4 - KraidAreaMusic. ;Bit 3 - Not used. SFX_SamusBall: ;Bit 2 - Not used. LCBEF: lda #$02 ;Bit 1 - Not used. LCBF1: bne SFX_SetX3 ;Bit 0 - Not used. ; SFX_Beep: ;Page 5: Ridley. LCBF3: lda #$08 ;Bit 7 - RidleyAreaMusic. ;Bit 6 - TourianMusic. SFX_SetX3: ;Bit 5 - ItemRoomMusic. LCBF5: ldx #$03 ;Bit 4 - KraidAreaMusic. LCBF7: bne SFX_SetSoundFlag ;Bit 3 - Not used. ;Bit 2 - Not used. ;Initiate music ;Bit 1 - Not used. ;Bit 0 - Not used. PowerUpMusic: ; LCBF9: lda #$40 ; LCBFB: bne SFX_SetX4 ; ; IntroMusic: ; LCBFD: lda #$80 ; ; SFX_SetX4: ; LCBFF: ldx #$04 ; LCC01: bne SFX_SetSoundFlag ; ; MotherBrainMusic: ; LCC03: lda #$02 ; LCC05: bne SFX_SetX5 ; ; TourianMusic: ; LCC07: lda #$40 ; ; SFX_SetX5: ; LCC09: ldx #$05 ; LCC0B: bne SFX_SetSoundFlag ; ;--------------------------------------[ Update Samus ]---------------------------------------------- UpdateSamus: LCC0D: ldx #$00 ;Samus data is located at index #$00. LCC0F: stx PageIndex ; LCC11: inx ;x=1. LCC12: stx IsSamus ;Indicate Samus is the object being updated. LCC14: jsr GoSamusHandler ;($CC1A)Find proper Samus handler routine. LCC17: dec IsSamus ;Update of Samus complete. LCC19: rts ; GoSamusHandler: LCC1A: lda ObjAction ; LCC1D: bmi SamusStand ;Branch if Samus is standing. LCC1F: jsr ChooseRoutine ;($C27C)Goto proper Samus handler routine. ;Pointer table for Samus' action handlers. LCC22: .word SamusStand ;($CC36)Standing. LCC24: .word SamusRun ;($CCC2)Running. LCC26: .word SamusJump ;($D002)Jumping. LCC28: .word SamusRoll ;($D0E1)Rolling. LCC2A: .word SamusPntUp ;($D198)Pointing up. LCC2C: .word SamusDoor ;($D3A8)Inside door while screen scrolling. LCC2E: .word SamusJump ;($D002)Jumping while pointing up. LCC30: .word SamusDead ;($D41A)Dead. LCC32: .word SamusDead2 ;($D41F)More dead. LCC34: .word SamusElevator ;($D423)Samus on elevator. ;---------------------------------------[ Samus standing ]------------------------------------------- SamusStand: LCC36: lda Joy1Status ;Status of joypad 1. LCC38: and #$CF ;Remove SELECT & START status bits. LCC3A: beq + ;Branch if no buttons pressed. LCC3C: jsr ClearHorzMvmtAnimData ;($CF5D)Set no horiontal movement and single frame animation. LCC3F: lda Joy1Status ; LCC41:* and #$07 ;Keep status of DOWN/LEFT/RIGHT. LCC43: bne + ;Branch if any are pressed. LCC45: lda Joy1Change ; LCC47: and #$08 ;Check if UP was pressed last frame.--> LCC49: beq +++ ;If not, branch. LCC4B:* jsr BitScan ;($E1E1)Find which directional button is pressed. LCC4E: cmp #$02 ;Is down pressed?--> LCC50: bcs + ;If so, branch. LCC52: sta SamusDir ;1=left, 0=right. LCC54:* tax ; LCC55: lda ActionTable,x ;Load proper Samus status from table below. LCC58: sta ObjAction ;Save Samus status. LCC5B:* lda Joy1Change ; LCC5D: ora Joy1Retrig ;Check if fire was just pressed or needs to retrigger. LCC5F: asl ; LCC60: bpl + ;Branch if FIRE not pressed. LCC62: jsr FireWeapon ;($D1EE)Shoot left/right. LCC65:* bit Joy1Change ;Check if jump was just pressed. LCC67: bpl + ;Branch if JUMP not pressed. LCC69: lda #sa_Jump ; LCC6B: sta ObjAction ;Set Samus status as jumping. LCC6E:* lda #$04 ;Prepare to set animation delay to 4 frames. LCC70: jsr SetSamusData ;($CD6D)Set Samus control data and animation. LCC73: lda ObjAction ; LCC76: cmp #sa_Door ;Is Samus inside a door, dead or pointing up and jumping?--> LCC78: bcs + ;If so, branch to exit. LCC7A: jsr ChooseRoutine ;Select routine below. ;Pointer table to code. LCC7D: .word ExitSub ;($C45C)Rts. LCC7F: .word SetSamusRun ;($CC98)Samus is running. LCC81: .word SetSamusJump ;($CFC3)Samus is jumping. LCC83: .word SetSamusRoll ;($D0B5)Samus is in a ball. LCC85: .word SetSamusPntUp ;($CF77)Samus is pointing up. ;Table used by above subroutine. ActionTable: LCC87: .byte sa_Run ;Run right. LCC88: .byte sa_Run ;Run left. LCC89: .byte sa_Roll LCC8A: .byte sa_PntUp ;---------------------------------------------------------------------------------------------------- SetSamusExplode: LCC8B: lda #$50 sta SamusJumpDsplcmnt lda #an_Explode jsr SetSamusAnim sta ObjectCounter * rts SetSamusRun: LCC98: lda #$09 sta WalkSoundDelay ldx #$00 lda AnimResetIndex cmp #an_SamusStand beq + inx cmp #$27 beq + lda #$04 jsr SetSamusNextAnim * lda RunAnimationTbl,x sta AnimResetIndex ldx SamusDir LCCB7: lda RunAccelerationTbl,x sta SamusHorzAccel rts RunAnimationTbl: LCCBE: .byte an_SamusRun .byte an_SamusRunPntUp RunAccelerationTbl: LCCC0: .byte $30 ;Accelerate right. .byte $D0 ;Accelerate left. ; SamusRun ; ======== SamusRun: LCCC2: ldx SamusDir lda SamusGravity beq +++++++ ldy SamusJumpDsplcmnt bit ObjVertSpeed 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 Joy1Status and #$08 beq + lda #an_SamusJumpPntUp sta AnimResetIndex * bit Joy1Status bmi + jsr StopVertMovement ;($D147) * lda #an_SamusRun cmp AnimResetIndex bne + lda #an_SamusJump sta AnimResetIndex * lda SamusInLava beq + lda Joy1Change bmi LCD40 ; branch if JUMP pressed * jsr LCF88 jsr LD09C jsr LCF2E lda #$02 bne SetSamusData ; 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 Joy1Change bpl + ; branch if JUMP not pressed LCD40: jsr LCFC3 lda #$12 sta SamusHorzSpeedMax jmp LCD6B * ora Joy1Retrig asl bpl + ; branch if FIRE not pressed jsr LCDD7 * lda Joy1Status and #$03 bne + jsr LCF55 jmp LCD6B * jsr BitScan ;($E1E1) cmp SamusDir beq LCD6B sta SamusDir jsr LCC98 LCD6B: lda #$03 ;---------------------------------------[ Set Samus data ]------------------------------------------- ;The following function sets various animation and control data bytes for Samus. SetSamusData: LCD6D: jsr UpdateObjAnim ;($DC8F)Update animation if needed. LCD70: jsr IsScrewAttackActive ;($CD9C)Check if screw attack active to change palette. LCD73: bcs + ;If screw attack not active, branch to skip palette change. LCD75: lda FrameCount ; LCD77: lsr ; LCD78: and #$03 ;Every other frame, change Samus palette while screw--> LCD7A: ora #$A0 ;Attack is active. LCD7C: sta ObjectCntrl ; LCD7E:* jsr CheckHealthStatus ;($CDFA)Check if Samus hit, blinking or Health low. LCD81: jsr LavaAndMoveCheck ;($E269)Check if Samus is in lava or moving. LCD84: lda MetroidOnSamus ;Is a Metroid stuck to Samus?--> LCD86: beq + ;If not, branch. LCD88: lda #$A1 ;Metroid on Samus. Turn Samus blue. LCD8A: sta ObjectCntrl ; LCD8C:* jsr SetmirrorCntrlBit ;($CD92)Mirror Samus, if necessary. LCD8F: jmp DrawFrame ;($DE4A)Display Samus. ;---------------------------------[ Set mirror control bit ]----------------------------------------- SetmirrorCntrlBit: LCD92: lda SamusDir ;Facing left=#$01, facing right=#$00. LCD94: jsr Amul16 ;($C2C5)*16. Move bit 0 to bit 4 position. LCD97: ora ObjectCntrl ; LCD99: sta ObjectCntrl ;Use SamusDir bit to set mirror bit. LCD9B: rts ; ;------------------------------[ Check if screw attack is active ]----------------------------------- IsScrewAttackActive: LCD9C: sec ;Assume screw attack is not active. LCD9D: ldy ObjAction ; LCDA0: dey ;Is Samus running?--> LCDA1: bne ++ ;If not, branch to exit. LCDA3: lda SamusGear ; LCDA6: and #gr_SCREWATTACK ;Does Samus have screw attack?--> LCDA8: beq ++ ;If not, branch to exit. LCDAA: lda AnimResetIndex ; LCDAD: cmp #an_SamusSalto ;Is Samus summersaulting?--> LCDAF: beq + ;If so, branch to clear carry(screw attack active). LCDB1: cmp #an_SamusJump ; LCDB3: sec ;Is Samus jumping?--> LCDB4: bne ++ ;If not, branch to exit. LCDB6: bit ObjVertSpeed ;If Samus is jumping and still moving upwards, screw--> LCDB9: bpl ++ ;attack is active. LCDBB:* cmp AnimIndex ;Screw attack will still be active if not spinning, but--> LCDBE:* rts ;jumping while running and still moving upwards. ;---------------------------------------------------------------------------------------------------- LCDBF: lda Joy1Status and #$08 lsr lsr lsr tax lda LCCBE,x cmp AnimResetIndex beq - jsr SetSamusAnim pla pla jmp LCD6B LCDD7: jsr FireWeapon ;($D1EE)Shoot left/right. lda Joy1Status and #$08 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: .byte $3F .byte $3B .byte $3D .byte $3F CheckHealthStatus: LCDFA: lda SamusHit ; and #$20 ;Has Samus been hit?--> beq +++ ;If not, branch to check if still blinking from recent hit. lda #$32 ; sta SamusBlink ;Samus has been hit. Set blink for 32 frames. lda #$FF sta DamagePushDirection lda $73 sta $77 beq ++ bpl + jsr SFX_SamusHit * lda SamusHit and #$08 lsr lsr lsr sta DamagePushDirection * lda #$FD sta ObjVertSpeed lda #$38 ;Samus is hit. Store Samus hit gravity. sta SamusGravity ; jsr IsSamusDead bne + jmp CheckHealthBeep * lda SamusBlink beq CheckHealthBeep dec SamusBlink ldx DamagePushDirection inx beq +++ jsr Adiv16 ; / 16 cmp #$03 bcs + ldy SamusHorzAccel bne +++ jsr LCF4E * dex bne + jsr TwosCompliment ;($C3D4) * sta ObjHorzSpeed * 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 SamusHit LCE83: rts ;----------------------------------------[ Is Samus dead ]------------------------------------------- IsSamusDead: LCE84: lda ObjAction ; LCE87: cmp #sa_Dead ; LCE89: beq Exit3 ;Samus is dead. Zero flag is set. LCE8B: cmp #sa_Dead2 ; LCE8D: beq Exit3 ; LCE8F: cmp #$FF ;Samus not dead. Clear zero flag. Exit3: LCE91: rts ;Exit for routines above and below. ;----------------------------------------[ Subtract health ]----------------------------------------- SubtractHealth: LCE92: lda HealthLoChange ;Check to see if health needs to be changed.--> LCE94: ora HealthHiChange ;If not, branch to exit. LCE96: beq Exit3 ; LCE98: jsr IsSamusDead ;($CE84)Check if Samus is already dead. LCE9B: beq ClearDamage ;Samus is dead. Branch to clear damage values. LCE9D: ldy EndTimerHi ;If end escape timer is running, Samus cannot be hurt. LCEA0: iny ; LCEA1: beq + ;Branch if end escape timer not active. ClearDamage: LCEA3: jmp ClearHealthChange ;($F323)Clear health change values. LCEA6:* lda MotherBrainStatus ;If mother brain is in the process of dying, receive--> LCEA8: cmp #$03 ;no damage. LCEAA: bcs ClearDamage ; LCEAC: lda SamusGear ; LCEAF: and #gr_VARIA ;Check is Samus has Varia. LCEB1: beq + ; LCEB3: lsr HealthLoChange ;If Samus has Varia, divide damage by 2. LCEB5: lsr HealthHiChange ; LCEB7: bcc + ;If HealthHi moved a bit into the carry flag while--> LCEB9: lda #$4F ;dividing, add #$4F to HealthLo for proper--> LCEBB: adc HealthLoChange ;division results. LCEBD: sta HealthLoChange ; LCEBF:* lda HealthLo ;Prepare to subtract from HealthLo. LCEC2: sta $03 ; LCEC4: lda HealthLoChange ;Amount to subtract from HealthLo. LCEC6: sec ; LCEC7: jsr Base10Subtract ;($C3FB)Perform base 10 subtraction. LCECA: sta HealthLo ;Save results. LCECD: lda HealthHi ;Prepare to subtract from HealthHi. LCED0: sta $03 ; LCED2: lda HealthHiChange ;Amount to subtract from HealthHi. LCED4: jsr Base10Subtract ;($C3FB)Perform base 10 subtraction. LCED7: sta HealthHi ;Save Results. LCEDA: lda HealthLo ; LCEDD: and #$F0 ;Is Samus health at 0? If so, branch to--> LCEDF: ora HealthHi ;begin death routine. LCEE2: beq + ; LCEE4: bcs ++ ;Samus not dead. Branch to exit. LCEE6:* lda #$00 ;Samus is dead. LCEE8: sta HealthLo ; LCEEB: sta HealthHi ;Set health to #$00. LCEEE: lda #sa_Dead ; LCEF0: sta ObjAction ;Death handler. LCEF3: jsr SFX_SamusDie ;($CBE2)Start Samus die SFX. LCEF6: jmp SetSamusExplode ;($CC8B)Set Samus exlpode routine. ;----------------------------------------[ Add health ]---------------------------------------------- AddHealth: LCEF9: lda HealthLo ;Prepare to add to HealthLo. LCEFC: sta $03 ; LCEFE: lda HealthLoChange ;Amount to add to HealthLo. LCF00: clc ; LCF01: jsr Base10Add ;($C3DA)Perform base 10 addition. LCF04: sta HealthLo ;Save results. LCF07: lda HealthHi ;Prepare to add to HealthHi. LCF0A: sta $03 ; LCF0C: lda HealthHiChange ;Amount to add to HealthHi. LCF0E: jsr Base10Add ;($C3DA)Perform base 10 addition. LCF11: sta HealthHi ;Save results. LCF14: lda TankCount ; LCF17: jsr Amul16 ;($C2C5)*16. Move tank count to upper 4 bits. LCF1A: ora #$0F ;Set lower 4 bits. LCF1C: cmp HealthHi ; LCF1F: bcs + ;Is life less than max? if so, branch. LCF21: and #$F9 ;Life is more than max amount. LCF23: sta HealthHi ; LCF26: lda #$99 ;Set life to max amount. LCF28: sta HealthLo ; LCF2B:* jmp ClearHealthChange ;($F323) ;---------------------------------------------------------------------------------------------------- LCF2E: lda SamusHit LCF31: lsr and #$02 beq +++ bcs + lda SamusHorzAccel bmi +++ bpl ++ * lda SamusHorzAccel bmi + bne ++ * jsr TwosCompliment ;($C3D4) sta SamusHorzAccel ClearHorzMvmntData: LCF4C: ldy #$00 ; LCF4E: sty ObjHorzSpeed ;Set Samus Horizontal speed and horizontal--> sty HorzCntrLinear ;linear counter to #$00. * rts ; StopHorzMovement: LCF55: lda SamusHorzAccel ;Is Samus moving horizontally?--> bne ClearHorzMvmtAnimData ;If so, branch to stop movement. jsr SFX_SamusWalk ;($CB96)Play walk SFX. ClearHorzMvmtAnimData: LCF5D: jsr NoHorzMoveNoDelay ;($CF81)Clear horizontal movement and animation delay data. sty ObjAction ;Samus is standing. lda Joy1Status ; and #$08 ;Is The up button being pressed?--> bne + ;If so, branch. lda #an_SamusStand ;Set Samus animation for standing. SetSamusAnim: LCF6B: sta AnimResetIndex ;Set new animation reset index. SetSamusNextAnim: sta AnimIndex ;Set new animation data index. lda #$00 ; sta AnimDelay ;New animation to take effect immediately. rts ; SetSamusPntUp: LCF77:* lda #sa_PntUp ; sta ObjAction ;Samus is pointing up. lda #an_SamusPntUp ; jsr SetSamusAnim ;($CF6B)Set new animation values. NoHorzMoveNoDelay: LCF81: jsr ClearHorzData ;($CFB7)Clear all horizontal movement data. sty AnimDelay ;Clear animation delay data. rts ; LCF88: lda Joy1Status and #$03 beq + jsr BitScan ;($E1E1) tax jsr LCCB7 lda SamusGravity bmi ++ lda AnimResetIndex cmp #an_SamusSalto beq ++ stx SamusDir lda Table06+1,x jmp SetSamusAnim * lda SamusGravity bmi + beq + lda AnimResetIndex cmp #an_SamusJump bne + ClearHorzData: LCFB7: jsr ClearHorzMvmntData ;($CF4C)Clear horizontal speed and linear counter. sty SamusHorzAccel ;Clear horizontal acceleration data. * rts ; LCFBE: ldy #an_SamusJumpPntUp jmp + SetSamusJump: LCFC3: ldy #an_SamusJump * sty AnimResetIndex dey sty AnimIndex lda #$04 sta AnimDelay lda #$00 sta SamusJumpDsplcmnt lda #$FC sta ObjVertSpeed 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 LCFF3: 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 SamusGravity rts SamusJump: lda SamusJumpDsplcmnt bit ObjVertSpeed bpl + ; branch if falling down cmp #$20 bcc + ; branch if jumped less than 32 pixels upwards bit Joy1Status bmi + ; branch if JUMP button still pressed jsr StopVertMovement ;($D147)Stop jump (start falling). * jsr LD055 jsr LCF2E lda Joy1Status and #$08 ; UP pressed? beq + ; branch if not lda #an_SamusJumpPntUp sta AnimResetIndex lda #sa_PntJump ; "jumping & pointing up" handler sta ObjAction * jsr LD09C lda SamusInLava beq + lda Joy1Change bpl + ; branch if JUMP not pressed jsr LCFC3 jmp LCD6B * lda SamusGravity bne ++ lda ObjAction cmp #sa_PntJump bne + jsr LCF77 bne ++ * jsr LCF55 * lda #$03 jmp SetSamusData ;($CD6D)Set Samus control data and animation. LD055: ldx #$01 ldy #$00 lda Joy1Status lsr bcs + ; branch if RIGHT pressed dex lsr 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 ObjHorzSpeed * rts ; Table used by above subroutine Table06: .byte $0C .byte $0C .byte $0C Table04: .byte $35 .byte $35 .byte $35 LD09C: lda Joy1Change ora Joy1Retrig asl bpl - ; exit if FIRE not pressed lda AnimResetIndex cmp #an_SamusJumpPntUp bne + jmp LD275 * jsr LD210 lda #an_SamusFireJump jmp SetSamusAnim SetSamusRoll: LD0B5: lda SamusGear and #gr_MARUMARI beq + ; branch if Samus doesn't have Maru Mari lda SamusGravity bne + ;Turn Samus into ball ldx SamusDir lda #an_SamusRoll sta AnimResetIndex lda #an_SamusRunJump sta AnimIndex lda LCCC0,x sta SamusHorzAccel lda #$01 sta $0686 jmp SFX_SamusBall * lda #sa_Stand sta ObjAction rts ; SamusRoll ; ========= SamusRoll: lda Joy1Change and #$08 ; UP pressed? bne + ; branch if yes bit Joy1Change ; JUMP pressed? bpl ++ ; branch if no * lda Joy1Status and #$04 ; 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 StopVertMovement ;($D147) lda #$04 jmp LD144 * lda Joy1Change jsr BitScan ;($E1E1) cmp #$02 bcs + sta SamusDir lda #an_SamusRoll jsr SetSamusAnim * ldx SamusDir jsr LCCB7 jsr LCF2E jsr CheckBombLaunch lda Joy1Status and #$03 bne + jsr LCFB7 * lda #$02 LD144: jmp SetSamusData ;($CD6D)Set Samus control data and animation. StopVertMovement: LD147: ldy #$00 sty ObjVertSpeed sty VertCntrLinear 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 bcc ++ ; exit if Samus doesn't have Bombs lda Joy1Change ora Joy1Retrig asl ; bit 7 = status of FIRE button bpl ++ ; exit if FIRE not pressed lda ObjVertSpeed 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 Joy1Status and #$08 ; UP still pressed? bne + ; branch if yes lda #sa_Stand ; stand handler sta ObjAction * lda Joy1Status and #$07 ; DOWN, LEFT, RIGHT pressed? beq ++ ; branch if no jsr BitScan ;($E1E1) cmp #$02 bcs + sta SamusDir * tax lda Table07,x sta ObjAction * lda Joy1Change ora Joy1Retrig asl bpl + ; branch if FIRE not pressed jsr FireWeapon ;($D1EE)Shoot up. * bit Joy1Change bpl + ; branch if JUMP not pressed lda #sa_PntJump sta ObjAction * lda #$04 jsr SetSamusData ;($CD6D)Set Samus control data and animation. lda ObjAction jsr ChooseRoutine ; Pointer table to code .word $CF55 .word $CC98 .word ExitSub ;($C45C)rts .word $D0B5 .word ExitSub ;($C45C)rts .word ExitSub ;($C45C)rts .word $CFBE .word ExitSub ;($C45C)rts .word ExitSub ;($C45C)rts .word ExitSub ;($C45C)rts ; Table used by above subroutine Table07: .byte sa_Run .byte sa_Run .byte sa_Roll FireWeapon: LD1EE: lda Joy1Status and #$08 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 MetroidOnSamus bne + jsr LD1F7 bne + jsr LD2EB jsr LD359 jsr LD38E lda #$0C sta $030F,y ldx SamusDir lda Table99,x ; get bullet speed sta ObjHorzSpeed,y ; -4 or 4, depending on Samus' direction lda #$00 sta ObjVertSpeed,y lda #$01 sta ObjectOnScreen,y jsr CheckMissileLaunch lda ObjAction,y asl ora SamusDir and #$03 tax lda Table08,x sta $05 lda #$FA sta $04 jsr LD306 lda SamusGear and #gr_LONGBEAM lsr lsr lsr ror ora $061F sta $061F ldx ObjAction,y dex bne + jsr SFX_BulletFire * ldy #$09 LD26B: tya jmp SetSamusNextAnim Table08: .byte $0C .byte $F4 .byte $08 .byte $F8 Table99: .byte $04 .byte $FC LD275: lda MetroidOnSamus bne + jsr LD1F7 bne + jsr LD2EB jsr LD38A jsr LD38E lda #$0C sta $030F,y lda #$FC sta ObjVertSpeed,y lda #$00 sta ObjHorzSpeed,y lda #$01 sta ObjectOnScreen,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 lsr lsr ror ora $061F sta $061F lda ObjAction,y cmp #$01 bne + jsr SFX_BulletFire * ldx SamusDir ldy Table09,x lda SamusGravity beq + ldy Table09+2,x * lda ObjAction cmp #$01 beq + jmp LD26B ; Table used by above subroutine Table09: .byte $26 .byte $26 .byte $34 .byte $34 .byte $01 .byte $FF .byte $EC .byte $F0 LD2EB: tya tax inc ObjAction,x lda #$02 sta ObjRadY,y sta ObjRadX,y lda #an_Bullet SetProjectileAnim: LD2FA: 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: .byte an_MissileRight .byte 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 bcc + lsr sta DoorStatus bne +++++++ * jsr LD48C jsr LED65 jsr $95AB lda ItemRoomMusicStatus beq ++ pha jsr LD92C ; start music pla bpl ++ lda #$00 sta ItemRoomMusicStatus beq ++ * lda #$80 sta ItemRoomMusicStatus * lda KraidRidleyPresent beq + jsr LCC07 lda #$00 sta KraidRidleyPresent beq -- ; branch always * lda SamusDoorData and #$0F sta ObjAction lda #$00 sta SamusDoorData sta DoorStatus jsr StopVertMovement ;($D147) 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 CheckHealthStatus ;($CDFA)Check if Samus hit, blinking or Health low. jsr SetmirrorCntrlBit jmp DrawFrame ; display Samus SamusDead: D41A: lda #$01 jmp SetSamusData ;($CD6D)Set Samus control data and animation. SamusDead2: dec AnimDelay rts ; SamusElevator ; ============= SamusElevator: lda ElevatorStatus 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 ObjVertSpeed cmp #$05 beq + cmp #$07 beq + LD47E: lda FrameCount lsr bcc ++ * jsr SetmirrorCntrlBit ;($CD92)Mirror Samus, if necessary. lda #$01 jmp AnimDrawObject * rts LD48C: ldx #$60 sec * jsr LD4B4 txa sbc #$20 tax bpl - jsr GetNameTable ;($EB85) 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 LD4B7: and #$02 LD4B9: bne + LD4BB: sta EnStatus,x LD4BE:* rts ; UpdateProjectiles ; ================= UpdateProjectiles: ldx #$D0 jsr DoOneProjectile ldx #$E0 jsr DoOneProjectile ldx #$F0 DoOneProjectile: stx PageIndex lda ObjAction,x LD4D0: jsr ChooseRoutine LD4D3: .word ExitSub ;($C45C) rts LD4D5: .word UpdateBullet ; regular beam .word UpdateWaveBullet ; wave beam .word UpdateIceBullet ; ice beam .word BulletExplode ; bullet/missile explode .word $D65E ; lay bomb .word $D670 ; lay bomb .word $D691 ; lay bomb .word $D65E ; lay bomb .word $D670 ; bomb countdown .word $D691 ; bomb explode .word UpdateBullet ; missile UpdateBullet: lda #$01 sta UpdatingProjectile 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 UpdatingProjectile rts * inc $0500,x LD522: inc $0500,x lda #$00 sta $0501,x beq + ; branch always UpdateWaveBullet: lda #$01 sta UpdatingProjectile 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 ObjVertSpeed,x lda ($0A),y jsr $832F ldx PageIndex sta ObjHorzSpeed,x tay lda $0502,x lsr bcc + tya jsr TwosCompliment ;($C3D4) sta ObjHorzSpeed,x * jsr LD609 bcs + jsr LD624 * jmp CheckBulletStat Table0A: .word Table0C ; pointer to table #1 below .word Table0D ; pointer to table #2 below ; Table #1 (size: 25 bytes) Table0C: .byte $01 .byte $F3 .byte $01 .byte $D3 .byte $01 .byte $93 .byte $01 .byte $13 .byte $01 .byte $53 .byte $01 .byte $73 .byte $01 .byte $73 .byte $01 .byte $53 .byte $01 .byte $13 .byte $01 .byte $93 .byte $01 .byte $D3 .byte $01 .byte $F3 .byte $FF ; Table #2 (size: 25 bytes) Table0D: .byte $01 .byte $B7 .byte $01 .byte $B5 .byte $01 .byte $B1 .byte $01 .byte $B9 .byte $01 .byte $BD .byte $01 .byte $BF .byte $01 .byte $BF .byte $01 .byte $BD .byte $01 .byte $B9 .byte $01 .byte $B1 .byte $01 .byte $B5 .byte $01 .byte $B7 .byte $FF ; UpdateIceBullet ; =============== UpdateIceBullet: lda #$81 sta ObjectCntrl jmp UpdateBullet ; BulletExplode ; ============= ; bullet/missile explode BulletExplode: lda #$01 sta UpdatingProjectile 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 ObjectOnScreen,x lsr 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 ObjHorzSpeed,x sta $05 lda ObjVertSpeed,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 InArea 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 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 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 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 ;-------------------------------------[ Get object coordinates ]------------------------------------ GetObjCoords: LD79F: ldx PageIndex ;Load index into object RAM to find proper object. LD7A1: lda ObjectY,x ; LD7A4: sta $02 ;Load and save temp copy of object y coord. LD7A6: lda ObjectX,x ; LD7A9: sta $03 ;Load and save temp copy of object x coord. LD7AB: lda ObjectHi,x ; LD7AE: sta $0B ;Load and save temp copy of object nametable. LD7B0: jmp MakeCartRAMPtr ;($E96A)Find object position in room RAM. ;--------------------------------------------------------------------------------------------------- UpdateElevator: ldx #$20 stx PageIndex lda ObjAction,x jsr ChooseRoutine ; Pointer table to elevator handlers .word ExitSub ;($C45C) rts .word ElevatorIdle .word $D80E .word ElevatorMove .word ElevatorScroll .word $D8A3 .word $D8BF .word $D8A3 .word ElevatorMove .word ElevatorStop ElevatorIdle: lda SamusOnElevator beq ShowElevator lda #$04 bit $032F ; elevator direction in bit 7 (1 = up) bpl + asl ; btn_UP * and Joy1Status beq ShowElevator ; start elevator! jsr StopVertMovement ;($D147) sty AnimDelay sty SamusGravity tya sta ObjVertSpeed,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 bcc -- ; only display elevator at odd frames jmp DrawFrame ; display elevator LD80E: lda ScrollX bne + lda MirrorCntrl ora #$08 sta MirrorCntrl 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 Timer1 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 AtEnding 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 InArea cpy #$12 bcc + lda #$01 * sta PalDataPending jsr WaitNMIPass_ jsr SelectSamusPal jsr StartMusic ;($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 Timer1 rts StartMusic: LD92C: lda ElevatorStatus cmp #$06 bne + lda $032F bmi ++ * lda $95CD ;Load proper bit flag for area music. ldy ItemRoomMusicStatus bmi ++ beq ++ * lda #$81 sta ItemRoomMusicStatus lda #$20 ;Set flag to play item room music. * ora MusicInitFlag ; sta MusicInitFlag ;Store music flag info. 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 MirrorCntrl * jmp ShowElevator * jmp ElevScrollRoom SamusOnElevatorOrEnemy: LD976: lda #$00 ; sta SamusOnElevator ;Assume Samus is not on an elevator or on a frozen enemy. sta OnFrozenEnemy ; tay ldx #$50 jsr LF186 * lda EnStatus,x cmp #$04 bne + jsr LF152 jsr LF1BF jsr LF1FA bcs + jsr LD9BA bne + D99A: inc OnFrozenEnemy ;Samus is standing on a frozen enemy. bne ++ * jsr Xminus16 bpl -- * lda ElevatorStatus 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 SamusHit and #$38 ora $10 ora #$40 sta SamusHit 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 KraidStatueStatus 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 bcc ++ ; only display statue at odd frames * jmp DrawFrame ; display statue LDA39: .byte $88 .byte $68 LDA3B: .byte $65 .byte $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 asl 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: .byte $30 .byte $AC .byte $F0 .byte $6C Table1B: .byte $61 .byte $60 .byte $60 .byte $60 LDADA: lda $54 bmi Exit0 lda DoorStatus bne Exit0 lda KraidStatueStatus and $687C bpl Exit0 sta $54 ldx #$70 ldy #$08 * lda #$03 sta $0500,x tya asl sta $0507,x lda #$04 sta TileType,x lda $036C asl asl ora #$62 sta TileWRAMHi,x tya asl 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 Joy1Change ora Joy1Retrig and #$20 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: LDB2F: sec LDB30: lda #$00 LDB32:* rol LDB33: dey LDB34: bpl - LDB36:* rts ;------------------------------------------[ Update items ]----------------------------------------- UpdateItems: LDB37: lda #$40 ;PowerUp RAM starts at $0340. LDB39: sta PageIndex ; LDB3B: ldx #$00 ;Check first item slot. LDB3D: jsr CheckOneItem ;($DB42)Check current item slot. LDB40: ldx #$08 ;Check second item slot. CheckOneItem: LDB42: stx ItemIndex ;First or second item slot index(#$00 or #$08). LDB44: ldy PowerUpType,x ; LDB47: iny ;Is no item present in item slot(#$FF)?--> LDB48: beq - ;If so, branch to exit. LDB4A: lda PowerUpYCoord,x ; LDB4D: sta PowerUpY ; LDB50: lda PowerUpXCoord,x ;Store y, x and name table coordinates of power up item. LDB53: sta PowerUpX ; LDB56: lda PowerUpNameTable,x ; LDB59: sta PowerUpHi ; LDB5C: jsr GetObjCoords ;($D79F)Find object position in room RAM. LDB5F: ldx ItemIndex ;Index to proper power up item. LDB61: ldy #$00 ;Reset index. LDB63: lda ($04),y ;Load pointer into room RAM. LDB65: cmp #$A0 ;Is object being placed on top of a solid tile?--> LDB67: bcc - ;If so, branch to exit. LDB69: lda PowerUpType,x ; LDB6C: and #$0F ;Load power up type byte and keep only bits 0 thru 3. LDB6E: ora #$50 ;Set bits 4 and 6. LDB70: sta PowerUpAnimFrame ;Save index to find object animation. LDB73: lda FrameCount ; LDB75: lsr ;Color affected every other frame. LDB76: and #$03 ;the 2 LSBs of object control byte change palette of object. LDB78: ora #$80 ;Indicate ObjectCntrl contains valid data by setting MSB. LDB7A: sta ObjectCntrl ;Change color of item every other frame. LDB7C: lda SpritePagePos ;Load current index into sprite RAM. LDB7E: pha ;Temp save sprite RAM position. LDB7F: lda PowerUpAnimIndex,x ;Load entry into FramePtrTable for item animation. LDB82: jsr DrawFrame ;($DE4A)Display special item. LDB85: pla ;Restore sprite page position byte. LDB86: cmp SpritePagePos ;Was power up item successfully drawn?--> LDB88: beq Exit9 ;If not, branch to exit. LDB8A: tax ;Store sprite page position in x. LDB8B: ldy ItemIndex ;Load index to proper power up data slot. LDB8D: lda PowerUpType,y ;Reload power up type data. LDB90: ldy #$01 ;Set power up color for ice beam orb. LDB92: cmp #$07 ;Is power up item the ice beam?--> LDB94: beq + ;If so, branch. LDB96: dey ;Set power up color for long/wave beam orb. LDB97: cmp #$06 ;Is power up item the wave beam?--> LDB99: beq + ;If so, branch. LDB9B: cmp #$02 ;Is power up item the long beam?--> LDB9D: bne ++ ;If not, branch. LDB9F:* tya ;Transfer color data to A. LDBA0: sta Sprite01RAM+2,x ;Store power up color for beam weapon. LDBA3: lda #$FF ;Indicate power up obtained is a beam weapon. LDBA5:* pha ;Temporarily store power up type. LDBA6: ldx #$00 ;Index to object 0(Samus). LDBA8: ldy #$40 ;Index to object 1(power up). LDBAA: jsr AreObjectsTouching ;($DC7F)Determine if Samus is touching power up. LDBAD: pla ;Restore power up type byte. LDBAE: bcs Exit9 ;Carry clear=Samus touching power up. Carry set=not touching. LDBB0: tay ;Store power-up type byte in Y. LDBB1: jsr PowerUpMusic ;($CBF9)Power up obtained! Play power up music. LDBB4: ldx ItemIndex ;X=index to power up item slot. LDBB6: iny ;Is item obtained a beam weapon?--> LDBB7: beq + ;If so, branch. LDBB9: lda PowerUpNameTable,x ; LDBBC: sta $08 ;Temp storage of nametable and power-up type in $08--> LDBBE: lda PowerUpType,x ;and $09 respectively. LDBC1: sta $09 ; LDBC3: jsr GetItemXYPos ;($DC1C)Get proper X and Y coords of item, save in history. LDBC6:* lda PowerUpType,x ;Get power-up type byte again. LDBC9: tay ; LDBCA: cpy #$08 ;Is power-up item a missile or energy tank?--> LDBCC: bcs ++++ ;If so, branch. LDBCE: cpy #$06 ;Is item the wave beam or ice beam?--> LDBD0: bcc + ;If not, branch. LDBD2: lda SamusGear ;Clear status of wave beam and ice beam power ups. LDBD5: and #$3F ; LDBD7: sta SamusGear ;Remove beam weapon data from Samus gear byte. LDBDA:* jsr MakeBitMask ;($DB2F)Create a bit mask for beam weapon just obtained. LDBDD: ora SamusGear ; LDBE0: sta SamusGear ;Update Samus gear with new beam weapon. LDBE3:* lda #$FF ; LDBE5: sta PowerUpDelay ;Initiate delay while power up music plays. LDBE8: sta PowerUpType,x ;Clear out item data from RAM. LDBEB: ldy ItemRoomMusicStatus ;Is Samus not in an item room?--> LDBED: beq + ;If not, branch. LDBEF: ldy #$01 ;Restart item room music after special item music is done. LDBF1:* sty ItemRoomMusicStatus ; LDBF3: jmp SelectSamusPal ;($CB73)Set Samus new palette. Exit9: LDBF6: rts ;Exit for multiple routines above. MissileEnergyPickup: LDBF7:* beq + ;Branch if item is an energy tank. LDBF9: lda #$05 ; LDBFB: jsr AddToMaxMissiles ;($DD97)Increase missile capacity by 5. LDBFE: bne --- ;Branch always. LDC00:* lda TankCount ; LDC03: cmp #$06 ;Has Samus got 6 energy tanks?--> LDC05: beq + ;If so, she can't have any more.--> LDC07: inc TankCount ;Otherwise give her a new tank. LDC0A:* lda TankCount ; LDC0D: jsr Amul16 ;Get tank count and shift into upper nibble. LDC10: ora #$09 ; LDC12: sta HealthHi ;Set new tank count. Upper health digit set to 9. LDC15: lda #$99 ;Max out low health digit. LDC17: sta HealthLo ;Health is now FULL! LDC1A: bne ----- ;Branch always. ;It is possible for the current nametable in the PPU to not be the actual nametable the special item ;is on so this function checks for the proper location of the special item so the item ID can be ;properly calculated. GetItemXYPos: LDC1C: lda MapPosX ; LDC1E: sta $07 ;Temp storage of Samus map position x and y in $07--> LDC20: lda MapPosY ;and $06 respectively. LDC22: sta $06 ; LDC24: lda ScrollDir ;Load scroll direction and shift LSB into carry bit. LDC26: lsr ; LDC27: php ;Temp storage of processor status. LDC28: beq + ;Branch if scrolling up/down. LDC2A: bcc ++ ;Branch if scrolling right. ;Scrolling left. LDC2C: lda ScrollX ;Unless the scroll x offset is 0, the actual room x pos--> LDC2E: beq ++ ;needs to be decremented in order to be correct. LDC30: dec $07 ; LDC32: bcs ++ ;Branch always. LDC34:* bcc + ;Branch if scrolling up. ;Scrolling down. LDC36: lda ScrollY ;Unless the scroll y offset is 0, the actual room y pos--> LDC38: beq + ;needs to be decremented in order to be correct. LDC3A: dec $06 ; LDC3C:* lda PPUCNT0ZP ;If item is on the same nametable as current nametable,--> LDC3E: eor $08 ;then no further adjustment to item x and y position needed. LDC40: and #$01 ; LDC42: plp ;Restore the processor status and clear the carry bit. LDC43: clc ; LDC44: beq + ;If Scrolling up/down, branch to adjust item y position. LDC46: adc $07 ;Scrolling left/right. Make any necessary adjustments to--> LDC48: sta $07 ;item x position before writing to unique item history. LDC4A: jmp AddItemToHistory ;($DC51)Add unique item to unique item history. LDC4D:* adc $06 ;Scrolling up/down. Make any necessary adjustments to--> LDC4F: sta $06 ;item y position before writing to unique item history. AddItemToHistory: LDC51: jsr CreateItemID ;($DC67)Create an item ID to put into unique item history. LDC54: ldy NumberOfUniqueItems ;Store number of uniqie items in Y. LDC57: lda $06 ; LDC59: sta UniqueItemHistory,y ;Store item ID in inuque item history. LDC5C: lda $07 ; LDC5E: sta UniqueItemHistory+1,y ; LDC61: iny ;Add 2 to Y. 2 bytes ber unique item. LDC62: iny ; LDC63: sty NumberOfUniqueItems ;Store new number of unique items. LDC66: rts ; ;------------------------------------------[ Create item ID ]----------------------------------------- ;The following routine creates a unique two byte item ID number for that item. The description ;of the format of the item ID number is as follows: ; ;IIIIIIXX XXXYYYYY. I = item type, X = X coordinate on world map, Y = Y coordinate ;on world map. The items have the following values of IIIIII: ;High jump = 000001 ;Long beam = 000010 (Not considered a unique item). ;Screw attack = 000011 ;Maru Mari = 000100 ;Varia suit = 000101 ;Wave beam = 000110 (Not considered a unique item). ;Ice beam = 000111 (Not considered a unique item). ;Energy tank = 001000 ;Missiles = 001001 ;Missile door = 001010 ;Bombs = 001100 ;Mother brain = 001110 ;1st Zeebetite = 001111 ;2nd Zeebetite = 010000 ;3rd Zeebetite = 010001 ;4th Zeebetite = 010010 ;5th Zeebetite = 010011 ; ;The results are stored in $06(upper byte) and $07(lower byte). CreateItemID: LDC67: lda $07 ;Load x map position of item. LDC69: jsr Amul32 ;($C2C$)*32. Move lower 3 bytes to upper 3 bytes. LDC6C: ora $06 ;combine Y coordinates into data byte. LDC6E: sta $06 ;Lower data byte complete. Save in $06. LDC70: lsr $07 ; LDC72: lsr $07 ;Move upper two bits of X coordinate to LSBs. LDC74: lsr $07 ; LDC76: lda $09 ;Load item type bits. LDC78: asl ;Move the 6 bits of item type to upper 6 bits of byte. LDC79: asl ; LDC7A: ora $07 ;Add upper two bits of X coordinate to byte. LDC7C: sta $07 ;Upper data byte complete. Save in #$06. LDC7E: rts ; ;----------------------------------------------------------------------------------------------------- AreObjectsTouching: LDC7F: jsr LF186 LDC82: jsr LF172 LDC85: jsr LF1A7 LDC88: jmp LF1FA ;The following table is used to rotate the sprites of both Samus and enemies when they explode. ExplodeRotationTbl: LDC8B: .byte $00 ;No sprite flipping. LDC8C: .byte $80 ;Flip sprite vertically. LDC8D: .byte $C0 ;Flip sprite vertically and horizontally. LDC8E: .byte $40 ;Flip sprite horizontally. ; UpdateObjAnim ; ============= ; Advance to object's next frame of animation UpdateObjAnim: LDC8F: 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 ObjectAnimIndexTbl,y ;($8572)Load frame number. cmp #$FF ; has end of anim been reached? beq ++ sta AnimFrame,x ; store frame number iny ; inc anim index tya sta AnimIndex,x ; store anim index * rts * ldy AnimResetIndex,x ; reset anim frame index jmp --- ; do first frame of animation LDCB7: pha lda #$00 sta $06 pla bpl + dec $06 * clc rts ;--------------------------------[ Get sprite control byte ]----------------------------------------- ;The sprite control byte extracted from the frame data has the following format: AABBXXXX. ;Where AA are the two bits used to control the horizontal and verticle mirroring of the ;sprite and BB are the two bits used control the sprite colors. XXXX is the entry number ;in the PlacePtrTbl used to place the sprite on the screen. GetSpriteCntrlData: LDCC3: ldy #$00 ; LDCC5: sty $0F ;Clear index into placement data. LDCC7: lda ($00),y ;Load control byte from frame pointer data. LDCC9: sta $04 ;Store value in $04 for processing below. LDCCB: tax ;Keep a copy of the value in x as well. LDCCC: jsr Adiv16 ;($C2BF)Move upper 4 bits to lower 4 bits. LDCCF: and #$03 ; LDCD1: sta $05 ;The following lines take the upper 4 bits in the--> LDCD3: txa ;control byte and transfer bits 4 and 5 into $05 bits 0--> LDCD4: and #$C0 ;and 1(sprite color bits). Bits 6 and 7 are--> LDCD6: ora #$20 ;transferred into $05 bits 6 and 7(sprite flip bits).--> LDCD8: ora $05 ;bit 5 is then set(sprite always drawn behind background). LDCDA: sta $05 ; LDCDC: lda ObjectCntrl ;Extract bit from control byte that controls the LDCDE: and #$10 ;object mirroring. LDCE0: asl ; LDCE1: asl ; LDCE2: eor $04 ;Move it to the bit 6 position and use it to flip the--> LDCE4: sta $04 ;horizontal mirroring of the sprite if set. LDCE6: lda ObjectCntrl ; LDCE8: bpl + ;If MSB is set in ObjectCntrl, use its flip bits(6 and 7). LDCEA: asl ObjectCntrl ; LDCEC: jsr SpriteFlipBitsOveride ;($E038)Use object flip bits as priority over sprite flip bits. LDCEF:* txa ;Discard upper nibble so only entry number into--> LDCF0: and #$0F ;PlacePtrTbl remains. LDCF2: asl ;*2. pointers in PlacePntrTbl are 2 bytes in size. LDCF3: tax ;Transfer to X to use as an index to find proper--> LDCF4: rts ;placement data segment. ;----------------------------------------------------------------------------------------------------- LDCF5: jsr ClearObjectCntrl ;($DF2D)Clear object control byte. pla pla ldx PageIndex LDCFC: lda InArea cmp #$13 bne + lda EnDataIndex,x cmp #$04 beq +++++ cmp #$02 beq +++++ * lda $040C,x asl bmi LDD75 jsr LF74B sta $00 jsr $80B0 and #$20 sta EnDataIndex,x lda #$05 sta EnStatus,x lda #$60 sta $040D,x lda RandomNumber1 cmp #$10 bcc LDD5B * and #$07 tay lda ItemDropTbl,y sta EnAnimFrame,x cmp #$80 bne ++ ldy MaxMissilePickup cpy CurrentMissilePickups beq LDD5B lda MaxMissiles beq LDD5B inc CurrentMissilePickups * rts * ldy MaxEnergyPickup cpy CurrentEnergyPickups beq LDD5B inc CurrentEnergyPickups cmp #$89 bne -- lsr $00 bcs -- LDD5B: ldx PageIndex lda InArea cmp #$13 beq ++ * jmp KillObject ;($FA18)Free enemy data slot. * lda RandomNumber1 ldy #$00 sty CurrentEnergyPickups sty CurrentMissilePickups iny sty MaxMissilePickup sty MaxEnergyPickup bne ----- LDD75: jsr PowerUpMusic lda InArea and #$0F sta MiniBossKillDelay lsr tay sta MaxMissiles,y lda #75 jsr AddToMaxMissiles bne LDD5B LDD8B: ldx PageIndex lda EnAnimFrame,x cmp #$F7 bne +++ jmp ClearObjectCntrl ;($DF2D)Clear object control byte. ; AddToMaxMissiles ; ================ ; Adds A to both MissileCount & MaxMissiles, storing the new count ; (255 if it overflows) AddToMaxMissiles: pha ;Temp storage of # of missiles to add. clc adc MissileCount bcc + lda #$FF * sta MissileCount pla clc adc MaxMissiles bcc + lda #$FF * sta MaxMissiles rts * lda EnYRoomPos,x sta $0A ; Y coord lda EnXRoomPos,x sta $0B ; X coord lda EnNameTable,x sta $06 ; hi coord lda EnAnimFrame,x asl tay lda ($41),y bcc + lda ($43),y * sta $00 iny lda ($41),y bcc + lda ($43),y * sta $01 jsr GetSpriteCntrlData ;($DCC3)Get place pointer index and sprite control data. tay lda ($45),y sta $02 iny lda ($45),y sta $03 ldy #$00 cpx #$02 bne + ldx PageIndex inc EnCounter,x lda EnCounter,x pha and #$03 tax lda $05 and #$3F ora ExplodeRotationTbl,x sta $05 pla cmp #$19 bne + jmp LDCF5 * ldx PageIndex iny lda ($00),y sta EnRadY,x jsr ReduceYRadius ;($DE3D)Reduce temp y radius by #$10. iny lda ($00),y sta EnRadX,x sta $09 iny sty $11 jsr IsObjectVisible ;($DFDF)Determine if object is within screen boundaries. txa asl sta $08 ldx PageIndex lda $0405,x and #$FD ora $08 sta $0405,x lda $08 beq ++ jmp LDEDE ;----------------------------------------[ Item drop table ]----------------------------------------- ;The following table determines what, if any, items an enemy will drop when it is killed. ItemDropTbl: LDE35: .byte $80 ;Missile. LDE36: .byte $81 ;Energy. LDE37: .byte $89 ;No item. LDE38: .byte $80 ;Missile. LDE39: .byte $81 ;Energy. LDE3A: .byte $89 ;No item. LDE3B: .byte $81 ;Energy. LDE3C: .byte $89 ;No item. ;------------------------------------[ Object drawing routines ]------------------------------------- ;The following function effectively sets an object's temporary y radius to #$00 if the object ;is 4 tiles tall or less. If it is taller, #$10 is subtracted from the temporary y radius. ReduceYRadius: LDE3D: sec ; LDE3E: sbc #$10 ;Subtract #$10 from object y radius. LDE40: bcs + ;If number is still a positive number, branch to store value. LDE42: lda #$00 ;Number is negative. Set Y radius to #$00. LDE44:* sta $08 ;Store result and return. LDE46: rts ; AnimDrawObject: LDE47: jsr UpdateObjAnim ;($DC8F)Update animation if needed. DrawFrame: LDE4A: ldx PageIndex ;Get index to proper object to work with. LDE4C: lda AnimFrame,x ; LDE4F: cmp #$F7 ;Is the frame valid?--> LDE51: bne ++ ;Branch if yes. LDE53:* jmp ClearObjectCntrl ;($DF2D)Clear object control byte. LDE56:* cmp #$07 ;Is the animation of Samus facing forward?--> LDE58: bne + ;If not, branch. LDE5A: lda ObjectCntrl ;Ensure object mirroring bit is clear so Samus'--> LDE5C: and #$EF ;sprite appears properly when going up and down--> LDE5E: sta ObjectCntrl ;elevators. LDE60:* lda ObjectY,x ; LDE63: sta $0A ; LDE65: lda ObjectX,x ;Copy object y and x room position and name table--> LDE68: sta $0B ;data into $0A, $0B and $06 respectively. LDE6A: lda ObjectHi,x ; LDE6D: sta $06 ; LDE6F: lda AnimFrame,x ;Load A with index into FramePtrTable. LDE72: asl ;*2. Frame pointers are two bytes. LDE73: tax ;X is now the index into the FramePtrTable. LDE74: lda FramePtrTable,x ; LDE77: sta $00 ; LDE79: lda FramePtrTable+1,x ;Entry from FramePtrTable is stored in $0000. LDE7C: sta $01 ; LDE7E: jsr GetSpriteCntrlData ;($DCC3)Get place pointer index and sprite control data. LDE81: lda PlacePtrTable,x ; LDE84: sta $02 ; LDE86: lda PlacePtrTable+1,x ;Store pointer from PlacePtrTbl in $0002. LDE89: sta $03 ; LDE8B: lda IsSamus ;Is Samus the object being drawn?--> LDE8D: beq + ;If not, branch. ;Special case for Samus exploding. LDE8F: cpx #$0E ;Is Samus exploding?--> LDE91: bne + ;If not, branch to skip this section of code. LDE93: ldx PageIndex ;X=0. LDE95: inc ObjectCounter ;Incremented every frame during explode sequence.--> LDE97: lda ObjectCounter ;Bottom two bits used for index into ExplodeRotationTbl. LDE99: pha ;Save value of A. LDE9A: and #$03 ;Use 2 LSBs for index into ExplodeRotationTbl. LDE9C: tax ; LDE9D: lda $05 ;Drop mirror control bits from sprite control byte. LDE9F: and #$3F ; LDEA1: ora ExplodeRotationTbl,x ;Use mirror control bytes from table(Base is $DC8B). LDEA4: sta $05 ;Save modified sprite control byte. LDEA6: pla ;Restore A LDEA7: cmp #$19 ;After 25 frames, Move on to second part of death--> LDEA9: bne + ;handler, else branch to skip the rest of this code. LDEAB: ldx PageIndex ;X=0. LDEAD: lda #sa_Dead2 ; LDEAF: sta ObjAction,x ;Move to next part of the death handler. LDEB2: lda #$28 ; LDEB4: sta AnimDelay,x ;Set animation delay for 40 frames(.667 seconds). LDEB7: pla ;Pull last return address off of the stack. LDEB8: pla ; LDEB9: jmp ClearObjectCntrl ;($DF2D)Clear object control byte. LDEBC:* ldx PageIndex ; LDEBE: iny ;Increment to second frame data byte. LDEBF: lda ($00),y ; LDEC1: sta ObjRadY,x ;Get verticle radius in pixles of object. LDEC3: jsr ReduceYRadius ;($DE3D)Reduce temp y radius by #$10. LDEC6: iny ;Increment to third frame data byte. LDEC7: lda ($00),y ;Get horizontal radius in pixels of object. LDEC9: sta ObjRadX,x ; LDECB: sta $09 ;Temp storage for object x radius. LDECD: iny ;Set index to 4th byte of frame data. LDECE: sty $11 ;Store current index into frame data. LDED0: jsr IsObjectVisible ;($DFDF)Determine if object is within the screen boundaries. LDED3: txa ; LDED4: ldx PageIndex ;Get index to object. LDED6: sta ObjectOnScreen,x ;Store visibility status of object. LDEDB: tax ; LDEDC: beq + ;Branch if object is not within the screen boundaries. LDEDE: ldx SpritePagePos ;Load index into next unused sprite RAM segment. LDEE0: jmp DrawSpriteObject ;($DF19)Start drawing object. LDEE3:* jmp ClearObjectCntrl ;($DF2D)Clear object control byte then exit. WriteSpriteRAM: LDEE6:* ldy $0F ;Load index for placement data. LDEE8: jsr YDisplacement ;($DF6B)Get displacement for y direction. LDEEB: adc $10 ;Add initial Y position. LDEED: sta Sprite00RAM,x ;Store sprite Y coord. LDEF0: dec Sprite00RAM,x ;Because PPU uses Y + 1 as real Y coord. LDEF3: inc $0F ;Increment index to next byte of placement data. LDEF5: ldy $11 ;Get index to frame data. LDEF7: lda ($00),y ;Tile value. LDEF9: sta Sprite00RAM+1,x ;Store tile value in sprite RAM. LDEFC: lda ObjectCntrl ; LDEFE: asl ;Move horizontal mirror control byte to bit 6 and--> LDEFF: asl ;discard all other bits. LDF00: and #$40 ; LDF02: eor $05 ;Use it to override sprite horz mirror bit. LDF04: sta Sprite00RAM+2,x ;Store sprite control byte in sprite RAM. LDF07: inc $11 ;Increment to next byte of frame data. LDF09: ldy $0F ;Load index for placement data. LDF0B: jsr XDisplacement ;($DFA3)Get displacement for x direction. LDF0E: adc $0E ;Add initial X pos LDF10: sta Sprite00RAM+3,x ;Store sprite X coord LDF13: inc $0F ;Increment to next placement data byte. LDF15: inx ; LDF16: inx ; LDF17: inx ;Advance to next sprite. LDF18: inx ; DrawSpriteObject: LDF19: ldy $11 ;Get index into frame data. GetNextFrameByte: LDF1B: lda ($00),y ;Get next frame data byte. LDF1D: cmp #$FC ;If byte < #$FC, byte is tile data. If >= #$FC, byte is--> LDF1F: bcc WriteSpriteRAM ;frame data control info. Branch to draw sprite. LDF21: beq OffsetObjectPosition ;#$FC changes object's x and y position. LDF23: cmp #$FD ; LDF25: beq GetNewControlByte ;#$FD sets new control byte information for the next sprites. LDF27: cmp #$FE ;#$FE skips next sprite placement x and y bytes. LDF29: beq SkipPlacementData ; LDF2B: stx SpritePagePos ;Keep track of current position in sprite RAM. ClearObjectCntrl: LDF2D: lda #$00 ; LDF2F: sta ObjectCntrl ;Clear object control byte. LDF31: rts ; SkipPlacementData: LDF32:* inc $0F ;Skip next y and x placement data bytes. LDF34: inc $0F ; LDF36: inc $11 ;Increment to next data item in frame data. LDF38: jmp DrawSpriteObject ;($DF19)Draw next sprite. GetNewControlByte: LDF3B:* iny ;Increment index to next byte of frame data. LDF3C: asl ObjectCntrl ;If MSB of ObjectCntrl is not set, no overriding of--> LDF3E: bcc + ;flip bits needs to be performed. LDF40: jsr SpriteFlipBitsOveride ;($E038)Use object flip bits as priority over sprite flip bits. LDF43: bne ++ ;Branch always. LDF45:* lsr ObjectCntrl ;Restore MSB of ObjectCntrl. LDF47: lda ($00),y ; LDF49: sta $05 ;Save new sprite control byte. LDF4B:* iny ;Increment past sprite control byte. LDF4C: sty $11 ;Save index of frame data. LDF4E: jmp GetNextFrameByte ;($DF1B)Load next frame data byte. OffsetObjectPosition: LDF51:* iny ;Increment index to next byte of frame data. LDF52: lda ($00),y ;This data byte is used to offset the object from--> LDF54: clc ;its current y positon. LDF55: adc $10 ; LDF57: sta $10 ;Add offset amount to object y screen position. LDF59: inc $11 ; LDF5B: inc $11 ;Increment past control byte and y offset byte. LDF5D: ldy $11 ; LDF5F: lda ($00),y ;Load x offset data byte. LDF61: clc ; LDF62: adc $0E ;Add offset amount to object x screen position. LDF64: sta $0E ; LDF66: inc $11 ;Increment past x offset byte. LDF68: jmp DrawSpriteObject ;($DF19)Draw next sprite. ;----------------------------------[ Sprite placement routines ]------------------------------------- YDisplacement: LDF6B: lda ($02),y ;Load placement data byte. LDF6D: tay ; LDF6E: and #$F0 ;Check to see if this is placement data for the object--> LDF70: cmp #$80 ;exploding. If so, branch. LDF72: beq ++ ; LDF74: tya ;Restore placement data byte to A. LDF75:* bit $04 ; LDF77: bmi NegativeDisplacement ;Branch if MSB in $04 is set(Flips object). LDF79: clc ;Clear carry before returning. LDF7A: rts ; ExplodeYDisplace: LDF7B:* tya ;Transfer placement byte back into A. LDF7C: and #$0E ;Discard bits 7,6,5,4 and 0. LDF7E: lsr ;/2. LDF7F: tay ; LDF80: lda ExplodeIndexTbl,y ;Index into ExplodePlacementTbl. LDF83: ldy IsSamus ; LDF85: bne + ;Is Samus the object exploding? if so, branch. LDF87: ldy PageIndex ;Load index to proper enemy data. LDF89: adc EnCounter,y ;Increment every frame enemy is exploding. Initial=#$01. LDF8C: jmp ++ ;Jump to load explode placement data. ;Special case for Samus exploding. LDF8F:* adc ObjectCounter ;Increments every frame Samus is exploding. Initial=#$01. LDF91:* tay ; LDF92: lda ExplodeIndexTbl+2,y ;Get data from ExplodePlacementTbl. LDF95: pha ;Save data on stack. LDF96: lda $0F ;Load placement data index. LDF98: clc ; LDF99: adc #$0C ;Move index forward by 12 bytes. to find y--> LDF9B: tay ;placement data. LDF9C: pla ;Restore A with ExplodePlacementTbl data. LDF9D: clc ; LDF9E: adc ($02),y ;Add table displacements with sprite placement data. LDFA0: jmp ---- ;Branch to add y placement values to sprite coords. XDisplacement: LDFA3: lda ($02),y ;Load placement data byte. LDFA5: tay ; LDFA6: and #$F0 ;Check to see if this is placement data for the object--> LDFA8: cmp #$80 ;exploding. If so, branch. LDFAA: beq +++ ; LDFAC: tya ;Restore placement data byte to A. LDFAD:* bit $04 ; LDFAF: bvc + ;Branch if bit 6 cleared, else data is negative displacement. NegativeDisplacement: LDFB1: eor #$FF ; LDFB3: sec ;NOTE:Setting carry makes solution 1 higher than expected. LDFB4: adc #$F8 ;If flip bit is set in $04, this function flips the--> LDFB6:* clc ;object by using two compliment minus 8(Each sprite is--> LDFB7: rts ;8x8 pixels). ExplodeXDisplace: LDFB8:* ldy PageIndex ;Load index to proper enemy slot. LDFBA: lda EnCounter,y ;Load counter value. LDFBD: ldy IsSamus ;Is Samus the one exploding?--> LDFBF: beq + ;If not, branch. LDFC1: lda ObjectCounter ;Load object counter if it is Samus who is exploding. LDFC3:* asl ;*2. Move sprite in x direction 2 pixels every frame. LDFC4: pha ;Store value on stack. LDFC5: ldy $0F ; LDFC7: lda ($02),y ;Load placement data byte. LDFC9: lsr ; LDFCA: bcs + ;Check if LSB is set. If not, the byte stored on stack--> LDFCC: pla ;Will be twos complimented and used to move sprite in--> LDFCD: eor #$FF ;the negative x direction. LDFCF: adc #$01 ; LDFD1: pha ; LDFD2:* lda $0F ;Load placement data index. LDFD4: clc ; LDFD5: adc #$0C ;Move index forward by 12 bytes. to find x--> LDFD7: tay ;placement data. LDFD8: pla ;Restore A with x displacement data. LDFD9: clc ; LDFDA: adc ($02),y ;Add x displacement with sprite placement data. LDFDC: jmp ----- ;Branch to add x placement values to sprite coords. ;---------------------------------[ Check if object is on screen ]---------------------------------- ;The following set of functions determine if an object is visible on the screen. If the object ;is visible, X-1 when the function returns, X=0 if the object is not within the boundaries of the ;current screen. The function needs to know what nametable is currently in the PPU, what nametable ;the object is on and what the scroll offsets are. IsObjectVisible: LDFDF: ldx #$01 ;Assume object is visible on screen. LDFE1: lda $0A ;Object Y position in room. LDFE3: tay ; LDFE4: sec ;Subtract y scroll to find sprite's y position on screen. LDFE5: sbc ScrollY ; LDFE7: sta $10 ;Store result in $10. LDFE9: lda $0B ;Object X position in room. LDFEB: sec ; LDFEC: sbc ScrollX ;Subtract x scroll to find sprite's x position on screen. LDFEE: sta $0E ;Store result in $0E. LDFF0: lda ScrollDir ; LDFF2: and #$02 ;Is Samus scrolling left or right?--> LDFF4: bne HorzScrollCheck ;($E01C)If so, branch. VertScrollCheck: LDFF6: cpy ScrollY ;If object room pos is >= scrollY, set carry. LDFF8: lda $06 ;Check if object is on different name table as current--> LDFFA: eor PPUCNT0ZP ;name table active in PPU.--> LDFFC: and #$01 ;If not, branch. LDFFE: beq + ; LE000: bcs ++ ;If carry is still set, sprite is not in screen boundaries. LE002: lda $10 ; LE004: sbc #$0F ;Move sprite y position up 15 pixles. LE006: sta $10 ; LE008: lda $09 ; LE00A: clc ;If a portion of the object is outside the sceen--> LE00B: adc $10 ;boundaries, treat object as if the whole thing is--> LE00D: cmp #$F0 ;not visible. LE00F: bcc +++ ; LE011: clc ;Causes next statement to branch always. LE012:* bcc + ; LE014: lda $09 ;If object is on same name table as the current one in--> LE016: cmp $10 ;the PPU, check if part of object is out of screen--> LE018: bcc ++ ;boundaries. If so, branch. LE01A:* dex ;Sprite is not within screen boundaries. Decrement X. LE01B:* rts ; HorzScrollCheck: LE01C: lda $06 ; LE01E: eor PPUCNT0ZP ;Check if object is on different name table as current--> LE020: and #$01 ;name table active in PPU.--> LE022: beq + ;If not, branch. LE024: bcs ++ ;If carry is still set, sprite is not in screen boundaries. LE026: lda $09 ; LE028: clc ;If a portion of the object is outside the sceen--> LE029: adc $0E ;boundaries, treat object as if the whole thing is--> LE02B: bcc +++ ;not visible. LE02D: clc ;Causes next statement to branch always. LE02E:* bcc + ; LE030: lda $09 ;If object is on same name table as the current one in--> LE032: cmp $0E ;the PPU, check if part of object is out of screen--> LE034: bcc ++ ;boundaries. If so, branch. LE036:* dex ;Sprite is not within screen boundaries. Decrement X. LE037:* rts ; ;------------------------[ Override sprite flip bits with object flip bits ]------------------------- ;If the MSB is set in ObjectCntrl, its two upper bits that control sprite flipping take priority ;over the sprite control bits. This function modifies the sprite control byte with any flipping ;bits found in ObjectCntrl. SpriteFlipBitsOveride: LE038: lsr ObjectCntrl ;Restore MSB. LE03A: lda ($00),y ;Reload frame data control byte into A. LE03C: and #$C0 ;Extract the two sprite flip bytes from theoriginal--> LE03E: ora ObjectCntrl ;control byte and set any additional bits from ObjectCntrl. LE040: sta $05 ;Store modified byte to load in sprite control byte later. LE042: lda ObjectCntrl ; LE044: ora #$80 ; LE046: sta ObjectCntrl ;Ensure MSB of object control byte remains set. LE048: rts ; ;--------------------------------[ Explosion placement data ]--------------------------------------- ;The following table has the index values into the table after it for finding the placement data ;for an exploding object. ExplodeIndexTbl: LE049: .byte $00, $18, $30 ;The following table is used to produce the arcing motion of exploding objects. It is displacement ;data for the y directions only. The x displacement is constant. ExplodePlacementTbl: ;Bottom sprites. LE04C: .byte $FC, $F8, $F4, $F0, $EE, $EC, $EA, $E8, $E7, $E6, $E6, $E5, $E5, $E4, $E4, $E3 LE05C: .byte $E5, $E7, $E9, $EB, $EF, $F3, $F7, $FB ;Middle sprites. LE064: .byte $FE, $FC, $FA, $F8, $F6, $F4, $F2, $F0, $EE, $ED, $EB, $EA, $E9, $E8, $E7, $E6 LE074: .byte $E6, $E6, $E6, $E6, $E8, $EA, $EC, $EE ;Top sprites. LE07C: .byte $FE, $FC, $FA, $F8, $F7, $F6, $F5, $F4, $F3, $F2, $F1, $F1, $F0, $F0, $EF, $EF LE08C: .byte $EF, $EF, $EF, $EF, $F0, $F0, $F1, $F2 ;--------------------------------------[ Update enemy animation ]----------------------------------- ;Advance to next frame of enemy's animation. Basically the same as UpdateObjAnim, only for enemies. UpdateEnemyAnim: LE094: ldx PageIndex ;Load index to desired enemy. LE096: ldy EnStatus,x ; LE099: cpy #$05 ;Is enemy in the process of dying?--> LE09B: beq +++ ;If so, branch to exit. LE09D: ldy EnAnimDelay,x ; LE0A0: beq + ;Check if current anumation frame is ready to be updated. LE0A2: dec EnAnimDelay,x ;Not ready to update. decrement delay timer and--> LE0A5: bne +++ ;branch to exit. LE0A7:* sta EnAnimDelay,x ;Save new animation delay value. LE0AA: ldy EnAnimIndex,x ;Load enemy animation index. LE0AD:* lda (EnemyAnimPtr),y ;Get animation data. LE0AF: cmp #$FF ;End of animation? LE0B1: beq ++ ;If so, branch to reset animation. LE0B3: sta EnAnimFrame,x ;Store current animation frame data. LE0B6: iny ;Increment to next animation data index. LE0B7: tya ; LE0B8: sta EnAnimIndex,x ;Save new animation index. LE0BB:* rts ; LE0BC:* ldy EnResetAnimIndex,x ;reset animation index. LE0BF: bcs --- ;Branch always. ;---------------------------------------[ Display status bar ]--------------------------------------- ;Displays Samus' status bar components. DisplayBar: LE0C1: ldy #$00 ;Reset data index. LE0C3: lda SpritePagePos ;Load current sprite index. LE0C5: pha ;save sprite page pos. LE0C6: tax ; LE0C7:* lda DataDisplayTbl,y ; LE0CA: sta Sprite00RAM,x ;Stor contents of DataDisplayTbl in sprite RAM. LE0CD: inx ; LE0CE: iny ; LE0CF: cpy #$28 ;10*4. At end of DataDisplayTbl? If not, loop to--> LE0D1: bne - ;load next byte from table. ;Display 2-digit health count. LE0D3: stx SpritePagePos ;Save new location in sprite RAM. LE0D5: pla ;Restore initial sprite page pos. LE0D6: tax ; LE0D7: lda HealthHi ; LE0DA: and #$0F ;Extract upper health digit. LE0DC: jsr SPRWriteDigit ;($E173)Display digit on screen. LE0DF: lda HealthLo ; LE0E2: jsr Adiv16 ;($C2BF)Move lower health digit to 4 LSBs. LE0E5: jsr SPRWriteDigit ;($E173)Display digit on screen. LE0E8: ldy EndTimerHi ; LE0EB: iny ;Is Samus in escape sequence?--> LE0EC: bne ++ ;If so, branch. LE0EE: ldy MaxMissiles ; LE0F1: beq + ;Don't show missile count if Samus has no missile containers. ;Display 3-digit missile count. LE0F3: lda MissileCount ; LE0F6: jsr HexToDec ;($E198)Convert missile hex count to decimal cout. LE0F9: lda $02 ;Upper digit. LE0FB: jsr SPRWriteDigit ;($E173)Display digit on screen. LE0FE: lda $01 ;Middle digit. LE100: jsr SPRWriteDigit ;($E173)Display digit on screen. LE103: lda $00 ;Lower digit. LE105: jsr SPRWriteDigit ;($E173)Display digit on screen. LE108: bne +++ ;Branch always. ;Samus has no missiles, erase missile sprite. LE10A:* lda #$FF ;"Blank" tile. LE10C: cpx #$F4 ;If at last 3 sprites, branch to skip. LE10E: bcs ++ ; LE110: sta Sprite03RAM+1,x ;Erase left half of missile. LE113: cpx #$F0 ;If at last 4 sprites, branch to skip. LE115: bcs ++ ; LE117: sta Sprite04RAM+1,x ;Erase right half of missile. LE11A: bne ++ ;Branch always. ;Display 3-digit end sequence timer. LE11C:* lda EndTimerHi ; LE11F: jsr Adiv16 ;($C2BF)Upper timer digit. LE122: jsr SPRWriteDigit ;($E173)Display digit on screen. LE125: lda EndTimerHi ; LE128: and #$0F ;Middle timer digit. LE12A: jsr SPRWriteDigit ;($E173)Display digit on screen. LE12D: lda EndTimerLo ; LE130: jsr Adiv16 ;($C2BF)Lower timer digit. LE133: jsr SPRWriteDigit ;($E173)Display digit on screen. LE136: lda #$58 ;"TI" sprite(left half of "TIME"). LE138: sta Sprite00RAM+1,x ; LE13B: inc Sprite00RAM+2,x ;Change color of sprite. LE13E: cpx #$FC ;If at last sprite, branch to skip. LE140: bcs + ; LE142: lda #$59 ;"ME" sprite(right half of "TIME"). LE144: sta Sprite01RAM+1,x ; LE147: inc Sprite01RAM+2,x ;Change color of sprite. LE14A:* ldx SpritePagePos ;Restore initial sprite page pos. LE14C: lda TankCount ; LE14F: beq ++ ;Branch to exit if Samus has no energy tanks. ;Display full/empty energy tanks. LE151: sta $03 ;Temp store tank count. LE153: lda #$40 ;X coord of right-most energy tank. LE155: sta $00 ;Energy tanks are drawn from right to left. LE157: ldy #$6F ;"Full energy tank" tile. LE159: lda HealthHi ; LE15C: jsr Adiv16 ;($C2BF)/16. A contains # of full energy tanks. LE15F: sta $01 ;Storage of full tanks. LE161: bne AddTanks ;Branch if at least 1 tank is full. LE163: dey ;Else switch to "empty energy tank" tile. AddTanks: LE164: jsr AddOneTank ;($E17B)Add energy tank to display. LE167: dec $01 ;Any more full energy tanks left?--> LE169: bne + ;If so, then branch.--> LE16B: dey ;Otherwise, switch to "empty energy tank" tile. LE16C:* dec $03 ;done all tanks?--> LE16E: bne AddTanks ;if not, loop to do another. LE170: stx SpritePagePos ;Store new sprite page position. LE172:* rts ; ;----------------------------------------[Sprite write digit ]--------------------------------------- ;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: LE173: ora #$A0 ;#$A0 is index into pattern table for numbers. LE175: sta Sprite00RAM+1,x ;Store proper nametable pattern in sprite RAM. LE178: jmp Xplus4 ;Find next sprite pattern table byte. ;----------------------------------[ Add energy tank to display ]------------------------------------ ;Add energy tank to Samus' data display. AddOneTank: LE17B: lda #$17 ;Y coord-1. LE17D: sta Sprite00RAM,x ; LE180: tya ;Tile value. LE181: sta Sprite00RAM+1,x ; LE184: lda #$01 ;Palette #. LE186: sta Sprite00RAM+2,x ; LE189: lda $00 ;X coord. LE18B: sta Sprite00RAM+3,x ; LE18E: sec ; LE18F: sbc #$0A ;Find x coord of next energy tank. LE191: sta $00 ; ;-----------------------------------------[ Add 4 to x ]--------------------------------------------- Xplus4: LE193: inx ; LE194: inx ; LE195: inx ;Add 4 to value stored in X. LE196: inx ; LE197: rts ; ;------------------------------------[ Convert hex to decimal ]-------------------------------------- ;Convert 8-bit value in A to 3 decimal digits. Upper digit put in $02, middle in $01 and lower in $00. HexToDec: LE198: ldy #100 ;Find upper digit. LE19A: sty $0A ; LE19C: jsr GetDigit ;($E1AD)Extract hundreds digit. LE19F: sty $02 ;Store upper digit in $02. LE1A1: ldy #10 ;Find middle digit. LE1A3: sty $0A ; LE1A5: jsr GetDigit ;($E1AD)Extract tens digit. LE1A8: sty $01 ;Store middle digit in $01. LE1AA: sta $00 ;Store lower digit in $00 LE1AC: rts ; GetDigit: LE1AD: ldy #$00 ; LE1AF: sec ; LE1B0:* iny ; LE1B1: sbc $0A ;Loop and subtract value in $0A from A until carry flag--> LE1B3: bcs - ;is not set. The resulting number of loops is the decimal--> LE1B5: dey ;number extracted and A is the remainder. LE1B6: adc $0A ; LE1B8: rts ; ;-------------------------------------[ Status bar sprite data ]------------------------------------- ;Sprite data for Samus' data display DataDisplayTbl: LE1B9: .byte $21,$A0,$01,$30 ;Upper health digit. LE1BD: .byte $21,$A0,$01,$38 ;Lower health digit. LE1C1: .byte $2B,$FF,$01,$28 ;Upper missile digit. LE1C5: .byte $2B,$FF,$01,$30 ;Middle missile digit. LE1C9: .byte $2B,$FF,$01,$38 ;Lower missile digit. LE1CD: .byte $2B,$5E,$00,$18 ;Left half of missile. LE1D1: .byte $2B,$5F,$00,$20 ;Right half of missile. LE1D5: .byte $21,$76,$01,$18 ;E LE1D9: .byte $21,$7F,$01,$20 ;N LE1DD: .byte $21,$3A,$00,$28 ;.. ;-------------------------------------------[ Bit scan ]--------------------------------------------- ;This function takes the value stored in A and right shifts it until a set bit is encountered. ;Once a set bit is encountered, the function exits and returns the bit number of the set bit. ;The returned value is stored in A. BitScan: LE1E1: stx $0E ;Save X. LE1E3: ldx #$00 ;First bit is bit 0. LE1E5:* lsr ;Transfer bit to carry flag. LE1E6: bcs + ;If the shifted bit was 1, Branch out of loop. LE1E8: inx ;Increment X to keep of # of bits checked. LE1E9: cpx #$08 ;Have all 8 bit been tested?--> LE1EB: bne - ;If not, branch to check the next bit. LE1ED:* txa ;Return which bit number was set. LE1EE: ldx $0E ;Restore X. LE1F0:* rts ; ;------------------------------------------[ Scroll door ]------------------------------------------- ;Scrolls the screen if Samus is inside a door. ScrollDoor: LE1F1: ldx DoorStatus ; LE1F3: beq - ;Exit if Samus isn't in a door. LE1F5: dex ; LE1F6: bne + ;Not in right door. branch to check left door. LE1F8: jsr ScrollRight ;($E6D2)DoorStatus=1, scroll 1 pixel right. LE1FB: jmp ++ ;Jump to check if door scroll is finished. LE1FE:* dex ;Check if in left door. LE1FF: bne ++ ; LE201: jsr ScrollLeft ;($E6A7)DoorStatus=2, scroll 1 pixel left. LE204:* ldx ScrollX ;Has x scroll offset reached 0?--> LE206: bne Exit15 ;If not, branch to exit. ;Scrolled one full screen, time to exit door. LE208: ldx #$05 ;Samus is exiting the door. LE20A: bne DoOneDoorScroll ;Branch always. LE20C:* dex ; LE20D: bne + ;Check if need to scroll down to center door. LE20F: jsr ScrollDown ;($E519)DoorStatus=3, scroll 1 pixel down. LE212: jmp ++ ;Jump to check y scrolling value. LE215:* dex ; LE216: bne Exit15 ;Check if need to scroll up to center door. LE218: jsr ScrollUp ;($E4F1)DoorStatus=4, scroll 1 pixel up. VerticalRoomCentered: LE21B:* ldx ScrollY ;Has room been centered on screen?--> LE21D: bne Exit15 ;If not, branch to exit. LE21F: stx DoorOnNameTable3 ; LE221: stx DoorOnNameTable0 ;Erase door nametable data. LE223: inx ;X=1. LE224: lda ObjectX ;Did Samus enter in the right hand door?--> LE227: bmi ++ ;If so, branch. LE229: inx ;X=2. Samus is in left door. LE22A: bne ++ ;Branch always. ;This function is called once after door scrolling is complete. DoOneDoorScroll: LE22C: lda #$20 ;Set DoorDelay to 32 frames(comming out of door). LE22E: sta DoorDelay ; LE230: lda SamusDoorData ;Check if scrolling should be toggled. LE232: jsr Amul8 ;($C2C6)*8. Is door not to toggle scrolling(item room,--> LE235: bcs + ;bridge room, etc.)? If so, branch to NOT toggle scrolling. LE237: ldy DoorScrollStatus ;If comming from vertical shaft, skip ToggleScroll because--> LE239: cpy #$03 ;the scroll was already toggled after room was centered--> LE23B: bcc ++ ;by the routine just above. LE23D:* lda #$47 ;Set mirroring for vertical mirroring(horz scrolling). LE23F: bne ++ ;Branch always. LE241:* jsr ToggleScroll ;($E252)Toggle scrolling and mirroring. LE244:* sta MirrorCntrl ;Store new mirror control data. LE246: stx DoorStatus ;DoorStatus=5. Done with door scrolling. Exit15: LE248: rts ;Exit for several routines above. ;------------------------------------[ Toggle Samus nametable ]-------------------------------------- ToggleSamusHi: LE249: lda ObjectHi ; LE24C: eor #$01 ;Change Samus' current nametable from one to the other. LE24E: sta ObjectHi ; LE251: rts ; ;-------------------------------------------[ Toggle scroll ]---------------------------------------- ;Toggles both mirroring and scroll direction when Samus has moved from ;a horizontal shaft to a vertical shaft or vice versa. ToggleScroll: LE252: lda ScrollDir ; LE254: eor #$03 ;Toggle scroll direction. LE256: sta ScrollDir ; LE258: lda MirrorCntrl ;Toggle mirroring. LE25A: eor #$08 ; LE25C: rts ; ;----------------------------------------[ Is Samus in lava ]---------------------------------------- ;The following function checks to see if Samus is in lava. If she is, the carry bit is cleared, ;if she is not, the carry bit is set. Samus can only be in lava if in a horizontally scrolling ;room. If Samus is 24 pixels or less away from the bottom of the screen, she is considered to be ;in lava whether its actually there or not. IsSamusInLava: LE25D: lda #$01 ; LE25F: cmp ScrollDir ;Set carry bit(and exit) if scrolling up or down. LE261: bcs + ; LE263: lda #$D8 ;If Samus is Scrolling left or right and within 24 pixels--> LE265: cmp ObjectY ;of the bottom of the screen, she is in lava. Clear carry bit. LE268:* rts ; ;----------------------------------[ Check lava and movement routines ]------------------------------ LavaAndMoveCheck: LE269: lda ObjAction ; LE26C: cmp #sa_Elevator ;Is Samus on elevator?--> LE26E: beq + ;If so, branch. LE270: cmp #sa_Dead ;Is Samus Dead--> LE272: bcs - ;If so, branch to exit. LE274:* jsr IsSamusInLava ;($E25D)Clear carry flag if Samus is in lava. LE277: ldy #$FF ;Assume Samus not in lava. LE279: bcs ++++ ;Samus not in lava so branch. ;Samus is in lava. LE27B: sty DamagePushDirection ;Don't push Samus from lava damage. LE27D: jsr ClearHealthChange ;($F323)Clear any pending health changes to Samus. LE280: lda #$32 ; LE282: sta SamusBlink ;Make Samus blink. LE284: lda FrameCount ; LE286: and #$03 ;Start the jump SFX every 4th frame while in lava. LE288: bne + ; LE28A: jsr SFX_SamusJump ;($CBAC)Initiate jump SFX. LE28D:* lda FrameCount ; LE28F: lsr ;This portion of the code causes Samus to be damaged by--> LE290: and #$03 ;lava twice every 8 frames if she does not have the varia--> LE292: bne ++ ;but only once every 8 frames if she does. LE294: lda SamusGear ; LE297: and #gr_VARIA ;Does Samus have the Varia?--> LE299: beq + ;If not, branch. LE29B: bcc ++ ;Samus has varia. Carry set every other frame. Half damage. LE29D:* lda #$07 ; LE29F: sta HealthLoChange ;Samus takes lava damage. LE2A1: jsr SubtractHealth ;($CE92) LE2A4:* ldy #$00 ;Prepare to indicate Samus is in lava. LE2A6:* iny ;Set Samus lava status. LE2A7: sty SamusInLava ; SamusMoveVertically: LE2A9: jsr VertAccelerate ;($E37A)Calculate vertical acceleration. LE2AC: lda ObjectY ; LE2AF: sec ; LE2B0: sbc ScrollY ;Calculate Samus' screen y position. LE2B2: sta SamusScrY ; LE2B4: lda $00 ;Load temp copy of vertical speed. LE2B6: bpl ++++ ;If Samus is moving downwards, branch. LE2B8: jsr TwosCompliment ;($C3D4)Get twos compliment of vertical speed. LE2BB: ldy SamusInLava ;Is Samus in lava? LE2BD: beq + ;If not, branch,--> LE2BF: lsr ;else cut vertical speed in half. LE2C0: beq SamusMoveHorizontally ;($E31A)Branch if no vertical mvmnt to Check left/right mvmnt. ;Samus is moving upwards. LE2C2:* sta ObjectCounter ;Store number of pixels to move Samus this frame. LE2C4:* jsr MoveSamusUp ;($E457)Attempt to move Samus up 1 pixel. LE2C7: bcs + ;Branch if Samus successfully moved up 1 pixel. LE2C9: sec ;Samus blocked upwards. Divide her speed by 2 and set the LE2CA: ror ObjVertSpeed ;MSB to reverse her direction of travel. LE2CD: ror VertCntrLinear ; LE2D0: jmp SamusMoveHorizontally ;($E31A)Attempt to move Samus left/right. LE2D3:* dec ObjectCounter ;1 pixel movement is complete. LE2D5: bne -- ;Branch if Samus needs to be moved another pixel. ;Samus is moving downwards. LE2D7:* beq SamusMoveHorizontally ;($E31A)Branch if no vertical mvmnt to Check left/right mvmnt. LE2D9: ldy SamusInLava ;Is Samus in lava? LE2DB: beq + ;If not, branch,--> LE2DD: lsr ;Else reduce Samus speed by 75%(divide by 4). LE2DE: lsr ; LE2DF: beq SamusMoveHorizontally ;($E31A)Attempt to move Samus left/right. LE2E1:* sta ObjectCounter ;Store number of pixels to move Samus this frame. LE2E3:* jsr MoveSamusDown ;($E4A3)Attempt to move Samus 1 pixel down. LE2E6: bcs +++ ;Branch if Samus successfully moved down 1 pixel. ;Samus bounce after hitting the ground in ball form. LE2E8: lda ObjAction ; LE2EB: cmp #sa_Roll ;Is Samus rolled into a ball?--> LE2ED: bne + ;If not, branch. LE2EF: lsr ObjVertSpeed ;Divide verticle speed by 2. LE2F2: beq ++ ;Speed not fast enough to bounce. branch to skip. LE2F4: ror VertCntrLinear ;Move carry bit into MSB to reverse Linear counter. LE2F7: lda #$00 ; LE2F9: sec ; LE2FA: sbc VertCntrLinear ;Subtract linear counter from 0 and save the results.--> LE2FD: sta VertCntrLinear ;Carry will be cleared. LE300: lda #$00 ; LE302: sbc ObjVertSpeed ;Subtract vertical speed from 0. this will reverse the--> LE305: sta ObjVertSpeed ;vertical direction of travel(bounce up). LE308: jmp SamusMoveHorizontally ;($E31A)Attempt to move Samus left/right. ;Samus has hit the ground after moving downwards. LE30B:* jsr SFX_SamusWalk ;($CB96)Play walk SFX. LE30E:* jsr StopVertMovement ;($D147)Clear vertical movement data. LE311: sty SamusGravity ;Clear Samus gravity value. LE314: beq SamusMoveHorizontally ;($E31A)Attempt to move Samus left/right. LE316:* dec ObjectCounter ;1 pixel movement is complete. LE318: bne ---- ;Branch if Samus needs to be moved another pixel. SamusMoveHorizontally: LE31A: jsr HorzAccelerate ;($E3E5)Horizontally accelerate Samus. LE31D: lda ObjectX ; LE320: sec ;Calculate Samus' x position on screen. LE321: sbc ScrollX ; LE323: sta SamusScrX ;Save Samus' x position. LE325: lda $00 ;Load Samus' current horizontal speed. LE327: bpl +++ ;Branch if moving right. ;Samus is moving left. LE329: jsr TwosCompliment ;($C3D4)Get twos compliment of horizontal speed. LE32C: ldy SamusInLava ;Is Samus in lava?--> LE32E: beq + ;If not, branch,--> LE330: lsr ;else cut horizontal speed in half. LE331: beq Exit10 ;Branch to exit if Samus not going to move this frame. LE333:* sta ObjectCounter ;Store number of pixels to move Samus this frame. LE335:* jsr MoveSamusLeft ;($E626)Attempt to move Samus 1 pixel to the left. LE338: jsr CheckStopHorzMvmt ;($E365)Check if horizontal movement needs to be stopped. LE33B: dec ObjectCounter ;1 pixel movement is complete. LE33D: bne - ;Branch if Samus needs to be moved another pixel. LE33F: lda SamusDoorData ;Has Samus entered a door?--> LE341: beq Exit10 ;If not, branch to exit. LE343: lda #$01 ;Door leads to the left. LE345: bne ++++ ;Branch always. ;Samus is moving right. LE347:* beq Exit10 ;Branch to exit if Samus not moving horizontally. LE349: ldy SamusInLava ;Is Samus in lava?--> LE34B: beq + ;If not, branch,--> LE34D: lsr ;else cut horizontal speed in half. LE34E: beq Exit10 ;Branch to exit if Samus not going to move this frame. LE350:* sta ObjectCounter ;Store number of pixels to move Samus this frame. LE352:* jsr MoveSamusRight ;($E668)Attempt to move Samus 1 pixel to the right. LE355: jsr CheckStopHorzMvmt ;($E365)Check if horizontal movement needs to be stopped. LE358: dec ObjectCounter ;1 pixel movement is complete. LE35A: bne - ;Branch if Samus needs to be moved another pixel. LE35C: lda SamusDoorData ;Has Samus entered a door?--> LE35E: beq Exit10 ;If not, branch to exit. LE360: lda #$00 ; LE362:* sta SamusDoorDir ;Door leads to the right. Exit10: LE364: rts ;Exit for routines above and below. CheckStopHorzMvmt: LE365: bcs Exit10 ;Samus moved successfully. Branch to exit. LE367: lda #$01 ;Load counter with #$01 so this function will not be--> LE369: sta ObjectCounter ;called again. LE36C: lda SamusGravity ;Is Samus on the ground?--> LE36E: bne Exit10 ;If not, branch to exit. LE370: lda ObjAction ; LE373: cmp #sa_Roll ;Is Samus rolled into a ball?--> LE375: beq Exit10 ;If so, branch to exit. LE377: jmp StopHorzMovement ;($CF55)Stop horizontal movement or play walk SFX if stopped. ;-------------------------------------[ Samus vertical acceleration ]-------------------------------- ;The following code accelerates/decelerates Samus vertically. There are 4 possible values for ;gravity used in the acceleration calculation. The higher the number, the more intense the gravity. ;The possible values for gravity are as follows: ;#$38-When Samus has been hit by an enemy. ;#$1A-When Samus is falling. ;#$18-Jump without high jump boots. ;#$12-Jump with high jump boots. VertAccelerate: LE37A: lda SamusGravity ;Is Samus rising or falling?--> LE37D: bne ++ ;Branch if yes. LE37F: lda #$18 ; LE381: sta SamusHorzSpeedMax ;Set Samus maximum running speed. LE384: lda ObjectY ; LE387: clc ; LE388: adc ObjRadY ;Check is Samus is obstructed downwards on y room--> LE38B: and #$07 ;positions divisible by 8(every 8th pixel). LE38D: bne + ; LE38F: jsr CheckMoveDown ;($E7AD)Is Samus obstructed downwards?--> LE392: bcc ++ ;Branch if yes. LE394:* jsr SamusOnElevatorOrEnemy ;($D976)Calculate if Samus standing on elevator or enemy. LE397: lda SamusOnElevator ;Is Samus on an elevator?--> LE39A: bne + ;Branch if yes. LE39C: lda OnFrozenEnemy ;Is Samus standing on a frozen enemy?--> LE39E: bne + ;Branch if yes. LE3A0: lda #$1A ;Samus is falling. Store falling gravity value. LE3A2: sta SamusGravity ; LE3A5:* ldx #$05 ;Load X with maximum downward speed. LE3A7: lda VertCntrLinear ; LE3AA: clc ;The higher the gravity, the faster this addition overflows--> LE3AB: adc SamusGravity ;and the faster ObjVertSpeed is incremented. LE3AE: sta VertCntrLinear ; LE3B1: lda ObjVertSpeed ;Every time above addition sets carry bit, ObjVertSpeed is--> LE3B4: adc #$00 ;incremented. This has the effect of speeding up a fall--> LE3B6: sta ObjVertSpeed ;and slowing down a jump. LE3B9: bpl + ;Branch if Samus is moving downwards. ;Check if maximum upward speed has been exceeded. If so, prepare to set maximum speed. LE3BB: lda #$00 ; LE3BD: cmp VertCntrLinear ;Sets carry bit. LE3C0: sbc ObjVertSpeed ;Subtract ObjVertSpeed to see if maximum speed has--> LE3C3: cmp #$06 ;been exceeded. LE3C5: ldx #$FA ;Load X with maximum upward speed. LE3C7: bne ++ ;Branch always. ;Check if maximum downward speed has been reached. If so, prepare to set maximum speed. LE3C9:* cmp #$05 ;Has maximum downward speed been reached?--> LE3CB:* bcc + ;If not, branch. ;Max verticle speed reached or exceeded. Adjust Samus verticle speed to max. LE3CD: jsr StopVertMovement ;($D147)Clear verticle movement data. LE3D0: stx ObjVertSpeed ;Set Samus vertical speed to max. ;This portion of the function creates an exponential increase/decrease in verticle speed. This is the ;part of the function that does all the work to make Samus' jump seem natural. LE3D3:* lda VertCntrNonLinear ; LE3D6: clc ;This function adds itself plus the linear verticle counter--> LE3D7: adc VertCntrLinear ;onto itself every frame. This causes the non-linear--> LE3DA: sta VertCntrNonLinear ;counter to increase exponentially. This function will--> LE3DD: lda #$00 ;cause Samus to reach maximum speed first in most--> LE3DF: adc ObjVertSpeed ;situations before the linear counter. LE3E2: sta $00 ;$00 stores temp copy of current verticle speed. LE3E4: rts ; ;---------------------------------------------------------------------------------------------------- HorzAccelerate: LE3E5: lda SamusHorzSpeedMax jsr Amul16 ; * 16 sta $00 sta $02 lda SamusHorzSpeedMax jsr Adiv16 ; / 16 sta $01 sta $03 lda HorzCntrLinear clc adc SamusHorzAccel sta HorzCntrLinear tax lda #$00 bit SamusHorzAccel bpl + ;Branch if Samus accelerating to the right. lda #$FF * adc ObjHorzSpeed sta ObjHorzSpeed tay bpl + ;Branch if Samus accelerating to the right. lda #$00 sec sbc HorzCntrLinear tax lda #$00 sbc ObjHorzSpeed tay jsr LE449 * cpx $02 tya sbc $03 bcc + lda $00 sta HorzCntrLinear lda $01 sta ObjHorzSpeed * lda HorzCntrNonLinear clc adc HorzCntrLinear sta HorzCntrNonLinear lda #$00 adc ObjHorzSpeed sta $00 ;$00 stores temp copy of current horizontal speed. rts ; LE449: lda #$00 sec sbc $00 sta $00 lda #$00 sbc $01 sta $01 rts ;---------------------------------------------------------------------------------------------------- ;Attempt to move Samus one pixel up. MoveSamusUp: LE457: lda ObjectY ;Get Samus' y position in room. sec ; sbc ObjRadY ;Subtract Samus' vertical radius. LE45E: and #$07 ;Check if result is a multiple of 8. If so, branch to--> LE460: bne + ;Only call crash detection every 8th pixel. LE462: jsr CheckMoveUp ;($E7A2)Check if Samus obstructed UPWARDS.--> bcc +++++++ ;If so, branch to exit(can't move any further). * lda ObjAction ; cmp #sa_Elevator ;Is Samus riding elevator?--> beq + ;If so, branch. jsr SamusOnElevatorOrEnemy ;($D976)Calculate if Samus standing on elevator or enemy. lda SamusHit 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 SamusJumpDsplcmnt 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 OnFrozenEnemy 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 SamusJumpDsplcmnt 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: .byte $07 .byte $00 ;---------------------------------[ Get PPU and RoomRAM addresses ]---------------------------------- PPUAddrs: LE560: .byte $20 ;High byte of nametable #0(PPU). LE561: .byte $2C ;High byte of nametable #3(PPU) WRAMAddrs: LE562: .byte $60 ;High byte of RoomRAMA(cart RAM). LE563: .byte $64 ;High byte of RoomRAMB(cart RAM). GetNameAddrs: LE564: jsr GetNameTable ;($EB85)Get current name table number. LE567: and #$01 ;Update name table 0 or 3. LE569: tay ; LE56A: lda PPUAddrs,y ;Get high PPU addr of nametable(dest). LE56D: ldx WRAMAddrs,y ;Get high cart RAM addr of nametable(src). LE570: 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 TempScrollDir ;Still scrolling same direction when room was loaded?--> bne -- ;If not, branch to exit. lda ScrollY and #$F8 ; keep upper 5 bits sta $00 lda #$00 asl $00 rol asl $00 rol 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 = 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 ;($C36B)Put data byte into PPUDataString. lda $02 jsr WritePPUByte lda $04 jsr SeparateControlBits ;($C3C6) * 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 ;($C2A8) ldy $06 dec $05 bne -- stx PPUStrIndex jsr EndPPUString Table01: .byte $20 ;Horizontal write. PPU inc = 1, length = 32 tiles. .byte $9E ;Vertical write... PPU inc = 32, length = 30 tiles. ;---------------------------------[Write PPU attribute table data ]---------------------------------- WritePPUAttribTbl: LE5E2: ldx #$C0 ;Low byte of First row of attribute table. LE5E4: lda RoomNumber ; LE5E6: cmp #$F2 ;Is this the second pass through the routine?--> LE5E8: beq + ;If so, branch. LE5EA: ldx #$E0 ;Low byte of second row of attribute table. LE5EC:* stx $00 ;$0000=RoomRAM atrrib table starting address. LE5EE: stx $02 ;$0002=PPU attrib table starting address. LE5F0: jsr GetNameAddrs ;($E564)Get name table addr and corresponding RoomRAM addr. LE5F3: ora #$03 ;#$23 for attrib table 0, #$2F for attrib table 3. LE5F5: sta $03 ;Store results. LE5F7: txa ;move high byte of RoomRAM to A. LE5F8: ora #$03 ;#$63 for RoomRAMA, #$67 for RoomRAMB(Attrib tables). LE5FA: sta $01 ;Store results. LE5FC: lda #$01 ; LE5FE: sta PPUDataPending ;Data pending = YES. LE600: ldx PPUStrIndex ;Load current index into PPU strng to append data. LE603: lda $03 ;Store high byte of starting address(attrib table). LE605: jsr WritePPUByte ;($C36B)Put data byte into PPUDataString. LE608: lda $02 ;Store low byte of starting address(attrib table). LE60A: jsr WritePPUByte ;($C36B)Put data byte into PPUDataString. LE60D: lda #$20 ;Length of data to write(1 row of attrib data). LE60F: sta $04 ; LE611: jsr WritePPUByte ;($C36B)Write control byte. Horizontal write. LE614: ldy #$00 ;Reset index into data string. LE616:* lda ($00),y ;Get data byte. LE618: jsr WritePPUByte ;($C36B)Put data byte into PPUDataString. LE61B: iny ;Increment to next attrib data byte. LE61C: dec $04 ; LE61E: bne - ;Loop until all attrib data loaded into PPU. LE620: stx PPUStrIndex ;Store updated PPU string index. LE623: jsr EndPPUString ;($C376)Append end marker(#$00) and exit writing routines. ;---------------------------------------------------------------------------------------------------- ; attempt to move Samus one pixel left MoveSamusLeft: LE626: 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 SamusHit 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 SamusDoorData 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 SamusHit 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 SamusDoorData 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: .byte $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 TempScrollDir bne - lda ScrollX and #$F8 ; keep upper five bits jsr Adiv8 ; / 8 (make 'em lower five) sta $00 lda #$00 jmp LE590 ;---------------------------------------[ Get room number ]------------------------------------------- ;Gets room number at current map position. Sets carry flag if room # at map position is FF. ;If valid room number, the room number is stored in $5A. GetRoomNum: LE720: lda ScrollDir ; LE722: lsr ;Branch if scrolling vertical. LE723: beq + ; LE725: rol ;Restore value of a LE726: adc #$FF ;A=#$01 if scrolling left, A=#$02 if scrolling right. LE728: pha ;Save A. LE729: jsr OnNameTable0 ;($EC93)Y=1 if name table=0, Y=0 if name table=3. LE72C: pla ;Restore A. LE72D: and $006C,y ; LE730: sec ; LE731: bne +++++ ;Can't load room, a door is in the way. This has the--> ;effect of stopping the scrolling until Samus walks--> ;through the door(horizontal scrolling only). LE733:* lda MapPosY ;Map pos y. LE735: jsr Amul16 ;($C2C5)Multiply by 16. LE738: sta $00 ;Store multiplied value in $00. LE73A: lda #$00 ; LE73C: rol ;Save carry, if any. LE73D: rol $00 ;Multiply value in $00 by 2. LE73F: rol ;Save carry, if any. LE740: sta $01 ; LE742: lda $00 ; LE744: adc MapPosX ;Add map pos X to A. LE746: sta $00 ;Store result. LE748: lda $01 ; LE74A: adc #$70 ;Add #$7000 to result. LE74C: sta $01 ;$0000 = (MapY*32)+MapX+#$7000. LE74E: ldy #$00 ; LE750: lda ($00),y ;Load room number. LE752: cmp #$FF ;Is it unused?--> LE754: beq ++++ ;If so, branch to exit with carry flag set. LE756: sta RoomNumber ;Store room number. LE758:* cmp $95D0,y ;Is it a special room?--> LE75B: beq + ;If so, branch to set flag to play item room music. LE75D: iny ; LE75E: cpy #$07 ; LE760: bne - ;Loop until all special room numbers are checked. LE762: lda ItemRoomMusicStatus ;Load item room music status. LE764: beq ++ ;Branch if not in special room. LE766: lda #$80 ;Ptop playing item room music after next music start. LE768: bne ++ ;Branch always. LE76A:* lda #$01 ;Start item room music on next music start. LE76C:* sta ItemRoomMusicStatus ; LE76E: clc ;Clear carry flag. was able to get room number. LE76F:* rts ; ;----------------------------------------------------------------------------------------------------- LE770: ldx PageIndex lda EnRadY,x clc adc #$08 jmp LE783 LE77B: ldx PageIndex lda #$00 sec sbc EnRadY,x LE783: sta $02 lda #$08 sta $04 jsr LE792 lda EnRadX,x jmp LE7BD LE792: lda EnXRoomPos,x sta $09 ; X coord lda EnYRoomPos,x sta $08 ; Y coord lda EnNameTable,x sta $0B ; hi coord rts CheckMoveUp: LE7A2: 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 MakeCartRAMPtr ;($E96A)Find object position in room RAM. 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 SamusDoorData cmp #$A0 ; crash with tile #$A0? (scroll toggling door) beq + cmp #$A1 ; crash with tile #$A1? (horizontal scrolling door) bne ++ inc SamusDoorData * inc SamusDoorData * dex beq + jsr LE98E jmp LE7E6 * sec ; no crash Exit16: rts LE81E: ldx UpdatingProjectile 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 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 sec sbc $04 bcs + adc #$08 * tay lsr lsr lsr sta $04 tya and #$07 beq + inx * txa clc adc $04 rts LE8F1: ldx PageIndex lda EnRadX,x clc adc #$08 jmp LE904 LE8FC: ldx PageIndex lda #$00 sec sbc EnRadX,x LE904: sta $03 jsr LE792 ldy EnRadY,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 ;------------------------------------[ Object pointer into cart RAM ]------------------------------- ;Find object's equivalent position in room RAM based on object's coordinates. ;In: $02 = ObjectY, $03 = ObjectX, $0B = ObjectHi. Out: $04 = cart RAM pointer. MakeCartRAMPtr: LE96A: lda #$18 ;Set pointer to $6xxx(cart RAM). LE96C: sta $05 ; LE96E: lda $02 ;Object Y room position. LE970: and #$F8 ;Drop 3 LSBs. Only use multiples of 8. LE972: asl ; LE973: rol $05 ; LE975: asl ;Move upper 2 bits to lower 2 bits of $05 and move y bits--> LE976: rol $05 ;3, 4, 5 to upper 3 bits of $04. LR978: sta $04 ; LE97A: lda $03 ;Object X room position. LE97C: lsr ; LE97D: lsr ; LE97E: lsr ;A=ObjectX/8. LE97F: ora $04 ; LE981: sta $04 ;Put bits 0 thru 4 into $04. LE983: lda $0B ;Object nametable. LE985: asl ; LE986: asl ; A=ObjectHi*4. LE987: and #$04 ;Set bit 2 if object is on nametable 3. LE989: ora $05 ; LE98B: sta $05 ;Include nametable bit in $05. LE98D: rts ;Return pointer in $04 = 01100HYY YYYXXXXX. ;--------------------------------------------------------------------------------------------------- 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 UpdatingProjectile 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 InArea cmp #$11 bne + cpy #$76 bne + lda #$04 bne ++ * tya clc adc #$10 and #$3C lsr * lsr sta TileType,x * clc Exit18: rts ;------------------------------------------[ Select room RAM ]--------------------------------------- SelectRoomRAM: LEA05: jsr GetNameTable ;($EB85)Find name table to draw room on. LEA08: asl ; LEA09: asl ; LEA0A: ora #$60 ;A=#$64 for name table 3, A=#$60 for name table 0. LEA0C: sta CartRAMPtr+1 ; LEA0E: lda #$00 ; LEA10: sta CartRAMPtr ;Save two byte pointer to start of proper room RAM. LEA12: rts ; ;------------------------------------[ write attribute table data ]---------------------------------- AttribTableWrite: LEA13:* lda RoomNumber ; LEA15: and #$0F ;Determine what row of PPU attribute table data, if any,--> LEA17: inc RoomNumber ;to load from RoomRAM into PPU. LEA19: jsr ChooseRoutine ; ;The following table is used by the code above to determine when to write to the PPU attribute table. LEA1c: .word ExitSub ;($C45C)Rts. LEA1E: .word WritePPUAttribTbl ;($E5E2)Write first row of PPU attrib data. LEA20: .word ExitSub ;($C45C)Rts. LEA22: .word WritePPUAttribTbl ;($E5E2)Write second row of PPU attrib data. LEA24: .word RoomFinished ;($EA26)Finished writing attribute table data. ;-----------------------------------[ Finished writing room data ]----------------------------------- RoomFinished: LEA26: lda #$FF ;No more tasks to perform on current room.--> LEA28: sta RoomNumber ;Set RoomNumber to #$FF. LEA2A:* rts ; ;------------------------------------------[ Setup room ]-------------------------------------------- SetupRoom: LEA2B: lda RoomNumber ;Room number. LEA2D: cmp #$FF ; LEA2F: beq - ;Branch to exit if room is undefined. LEA31: cmp #$FE ; LEA33: beq + ;Branch if empty place holder byte found in room data. LEA35: cmp #$F0 ; LEA37: bcs -- ;Branch if time to write PPU attribute table data. LEA39: jsr UpdateRoomSpriteInfo ;($EC9B)Update which sprite belongs on which name table. LEA3C: jsr ScanForItems ;($ED98)Set up any special items. LEA3F: lda RoomNumber ;Room number to load. LEA41: asl ;*2(for loading address of room pointer). LEA42: tay ; LEA43: lda (RoomPtrTable),y ;Low byte of 16-bit room pointer.--> LEA45: sta RoomPtr ;Base copied from $959A to $3B. LEA47: iny ; LEA48: lda (RoomPtrTable),y ;High byte of 16-bit room pointer.--> LEA4A: sta RoomPtr+1 ;Base copied from $959B to $3C. LEA4C: ldy #$00 ; LEA4E: lda (RoomPtr),y ;First byte of room data. LEA50: sta RoomPal ;store initial palette # to fill attrib table with. LEA52: lda #$01 ; LEA54: jsr AddToRoomPtr ;($EAC0)Increment room data pointer. LEA57: jsr SelectRoomRAM ;($EA05)Determine where to draw room in RAM, $6000 or $6400. LEA5A: jsr InitTables ;($EFF8)clear Name Table & do initial Attrib table setup. LEA5D:* jmp DrawRoom ;($EAAA)Load room contents into room RAM. ;---------------------------------------[ Draw room object ]----------------------------------------- DrawObject: LEA60: sta $0E ;Store object position byte(%yyyyxxxx). LEA62: lda CartRAMPtr ; LEA64: sta CartRAMWorkPtr ;Set the working pointer equal to the room pointer--> LEA66: lda CartRAMPtr+1 ;(start at beginning of the room). LEA68: sta CartRAMWorkPtr+1 ; LEA6A: lda $0E ;Reload object position byte. LEA6C: jsr Adiv16 ;($C2BF)/16. Lower nibble contains object y position.--> LEA6F: tax ;Transfer it to X, prepare for loop. LEA70: beq +++ ;Skip y position calculation loop as y position=0 and--> ;does not need to be calculated. LEA72:* lda CartRAMWorkPtr ;LoW byte of pointer working in room RAM. LEA74: clc ; LEA75: adc #$40 ;Advance two rows in room RAM(one y unit). LEA77: sta CartRAMWorkPtr ; LEA79: bcc + ;If carry occurred, increment high byte of pointer--> LEA7B: inc CartRAMWorkPtr+1 ;in room RAM. LEA7D:* dex ; LEA7E: bne -- ;Repeat until at desired y position(X=0). LEA80:* lda $0E ;Reload object position byte. LEA82: and #$0F ;Remove y position upper nibble. LEA84: asl ;Each x unit is 2 tiles. LEA85: adc CartRAMWorkPtr ; LEA87: sta CartRAMWorkPtr ;Add x position to room RAM work pointer. LEA89: bcc + ;If carry occurred, increment high byte of room RAM work--> LEA8B: inc CartRAMWorkPtr+1 ;pointer, else branch to draw object. ;CartRAMWorkPtr now points to the object's starting location (upper left corner) ;on the room RAM which will eventually be loaded into a name table. LEA8D:* iny ;Move to the next byte of room data which is--> LEA8E: lda (RoomPtr),y ;the index into the structure pointer table. LEA90: tax ;Transfer structure pointer index into X. LEA91: iny ;Move to the next byte of room data which is--> LEA92: lda (RoomPtr),y ;the attrib table info for the structure. LEA94: sta ObjectPal ;Save attribute table info. LEA96: txa ;Restore structure pointer to A. LEA97: asl ;*2. Structure pointers are two bytes in size. LEA98: tay ; LEA99: lda (StructPtrTable),y ;Low byte of 16-bit structure ptr. LEA9B: sta StructPtr ; LEA9D: iny ; LEA9E: lda (StructPtrTable),y ;High byte of 16-bit structure ptr. LEAA0: sta StructPtr+1 ; LEAA2: jsr DrawStruct ;($EF8C)Draw one structure. LEAA5: lda #$03 ;Move to next set of structure data. LEAA7: jsr AddToRoomPtr ;($EAC0)Add A to room data pointer. ;-------------------------------------------[ Draw room ]-------------------------------------------- ;The following function draws a room in the room RAM which is eventually loaded into a name table. DrawRoom: LEAAA: ldy #$00 ;Zero index. LEAAC: lda (RoomPtr),y ;Load byte of room data.--> LEAAE: cmp #$FF ;Is it #$FF(end-of-room)?--> LEAB0: beq EndOfRoom ;If so, branch to exit. LEAB2: cmp #$FE ;Place holder for empty room objects(not used). LEAB4: beq + ; LEAB6: cmp #$FD ;is A=#$FD(end-of-objects)?--> LEAB8: bne DrawObject ;If not, branch to draw room object.--> LEABA: beq EndOfObjs ;Else branch to set up enemies/doors. LEABC:* sta RoomNumber ;Store #$FE if room object is empty. LEABE: lda #$01 ;Prepare to increment RoomPtr. ;-------------------------------------[ Add A to room pointer ]-------------------------------------- AddToRoomPtr: LEAC0: clc ;Prepare to add index in A to room pointer. LEAC1: adc RoomPtr ; LEAC3: sta RoomPtr ; LEAC5: bcc + ;Did carry occur? If not branch to exit. LEAC7: inc RoomPtr+1 ;Increment high byte of room pointer if carry occured. LEAC9:* rts ; ;---------------------------------------------------------------------------------------------------- EndOfObjs: LEACA: lda RoomPtr ; LEACC: sta $00 ;Store room pointer in $0000. LEACE: lda RoomPtr+1 ; LEAD0: sta $01 ; LEAD2: lda #$01 ;Prepare to increment to enemy/door data. EnemyLoop: LEAD4: jsr AddToPtr00 ;($EF09)Add A to pointer at $0000. LEAD7: ldy #$00 ; LEAD9: lda ($00),y ;Get first byte of enemy/door data. LEADB: cmp #$FF ;End of enemy/door data?--> LEADD: beq EndOfRoom ;If so, branch to finish room setup. LEADF: and #$0F ;Discard upper four bits of data. LEAE1: jsr ChooseRoutine ;Jump to proper enemy/door handling routine. ;Pointer table to code. LEAE4: .word ExitSub ;($C45C)Rts. LEAE6: .word LoadEnemy ;($EB06)Room enemies. LEAE8: .word LoadDoor ;($EB8C)Room doors. LEAEA: .word ExitSub ;($C45C)Rts. LEAEC: .word LoadElevator ;($EC04)Elevator. LEAEE: .word ExitSub ;($C45C)Rts. LEAF0: .word LoadStatues ;($EC2F)Kraid & Ridley statues. LEAF2: .word ZebHole ;($EC57)Regenerating enemies(such as Zeb). EndOfRoom: LEAF4: ldx #$F0 ;Prepare for PPU attribute table write. stx RoomNumber ; lda ScrollDir ; sta TempScrollDir ;Make temp copy of ScrollDir. and #$02 ;Check if scrolling left or right. bne + ; jmp LE57C * jmp LE70C LoadEnemy: LEB06: jsr GetEnemyData ;($EB0C)Get enemy data from room data. LEB09: jmp EnemyLoop ;($EAD4)Do next room object. GetEnemyData: LEB0C: lda ($00),y ;Get 1st byte again. and #$F0 ;Get object slot that enemy will occupy. tax ; jsr IsSlotTaken ;($EB7A)Check if object slot is already in use. bne ++ ;Exit if object slot taken. iny ; lda ($00),y ;Get enemy type. jsr GetEnemyType ;($EB28)Load data about enemy. ldy #$02 ; lda ($00),y ;Get enemy initial position(%yyyyxxxx). jsr LEB4D pha * pla * lda #$03 ;Number of bytes to add to ptr to find next room item. rts ; GetEnemyType: LEB28: pha ;Store enemy type. and #$C0 ;If MSB is set, the "tough" version of the enemy sta EnSpecialAttribs,x ;is to be loaded(more hit points, except rippers). asl ; bpl ++ ;If bit 6 is set, the enemy is either Kraid or Ridley. lda InArea ;Load current area Samus is in(to check if Kraid or--> and #$06 ;Ridley is alive or dead). lsr ;Use InArea to find status of Kraid/Ridley statue. tay ; lda MaxMissiles,y ;Load status of Kraid/Ridley statue. beq + ;Branch if Kraid or Ridley needs to be loaded. pla ; pla ;Mini boss is dead so pull enemy info and last address off--> jmp -- ;stack so next enemy/door item can be loaded. * lda #$01 ;Samus is in Kraid or Ridley's room and the--> sta KraidRidleyPresent ;mini boss is alive and needs to be loaded. * pla ;Restore enemy type data. and #$3F ;Keep 6 lower bits to use as index for enemy data tables. sta EnDataIndex,x ;Store index byte. rts ; LEB4D: tay ;Save enemy position data in Y. and #$F0 ;Extract Enemy y position. ora #$08 ;Add 8 pixels to y position so enemy is always on screen. sta EnYRoomPos,x ;Store enemy y position. tya ;Restore enemy position data. jsr Amul16 ;*16 to extract enemy x position. ora #$0C ;Add 12 pixels to x position so enemy is always on screen. sta EnXRoomPos,x ;Store enemy x position. lda #$01 ; sta EnStatus,x ;Indicate object slot is taken. lda #$00 sta $0404,x jsr GetNameTable ;($EB85)Get name table to place enemy on. sta EnNameTable,x ;Store name table. ldy EnDataIndex,x ;Load A with index to enemy data. asl $0405,x ;*2 jsr LFB7B jmp LF85A IsSlotTaken: LEB7A: lda EnStatus,x beq + lda $0405,x and #$02 * rts ;------------------------------------------[ Get name table ]---------------------------------------- ;The following routine is small but is called by several other routines so it is important and ;requires some explaining to understand its function. First of all, as Samus moves from one room ;to the next, she is also moving from one name table to the next. Samus does not move from one ;name table to the next as one might think. Samus moves diagonally through the name tables. To ;understand this concept, one must first know how the name tables are arranged. They are arranged ;like so: ; ; +-----+-----+ +-----+-----+ ; | | | The following is an incorrect example of how | | | ; | 2 | 3 | Samus goes from one name table to the next-----> | 2 | 3 | ; | | | | | | ; +-----+-----+ +-----+-----+ ; | | | | | | ; | 0 | 1 | INCORRECT!------> | 0<-|->1 | ; | | | | | | ; +-----+-----+ +-----+-----+ ; ;The following are examples of how the name tables are properly traversed while walking through rooms: ; ; +-----+-----+ +-----+-----+ ; | | | | | | ; | 2 | ->3 | | 2 | 3<-|-+ ; | |/ | | | | | ; +-----+-----+ <--------CORRECT!--------> +-----+-----+ | ; | /| | | | | | ; | 0<- | 1 | +-|->0 | 1 | | ; | | | | | | | | ; +-----+-----+ | +-----+-----+ | ; +---------------+ ; ;The same diagonal traversal of the name tables illustrated above applies to vetricle traversal as ;well. Since Samus can only travel between 2 name tables and not 4, the name table placement for ;objects is simplified. The following code determines which name table to use next: GetNameTable: LEB85: lda PPUCNT0ZP ; LEB87: eor ScrollDir ;Store #$01 if object should be loaded onto name table 3--> LEB89: and #$01 ;store #$00 if it should be loaded onto name table 0. LEB8B: rts ; ;---------------------------------------------------------------------------------------------------- ; LoadDoor ; ======== LoadDoor: jsr LEB92 * jmp EnemyLoop ; do next room object LEB92: iny lda ($00),y ; door info byte pha jsr Amul16 ; CF = door side (0=right, 1=left) php lda MapPosX clc adc MapPosY plp rol 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 GetNameTable ;($EB85) 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 GetNameTable ;($EB85) eor #$01 tax tya ora DoorOnNameTable3,x sta DoorOnNameTable3,x lda #$02 rts DoorXs: .byte $F0 ; X coord of RIGHT door .byte $10 ; X coord of LEFT door LEBFE: .byte $02 .byte $01 LEC00: .byte $80 .byte $B0 .byte $A0 .byte $90 ; LoadElevator ; ============ LoadElevator: jsr LEC09 bne ---- ; branch always LEC09: lda ElevatorStatus 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 GetNameTable ;($EB85) sta $032C ; high Y coord lda #$23 sta $0323 ; elevator frame inc ElevatorStatus ;1 * lda #$02 rts ; LoadStatues ; =========== LoadStatues: jsr GetNameTable ;($EB85) sta $036C lda #$40 ldx RidleyStatueStatus bpl + ; branch if Ridley statue not hit lda #$30 * sta $0370 lda #$60 ldx KraidStatueStatus bpl + ; branch if Kraid statue not hit lda #$50 * sta $036F sty $54 lda #$01 sta $0360 * jmp EnemyLoop ; do next room object ZebHole: 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 GetNameTable ;($EB85) sta $072C,x * lda #$03 bne --- OnNameTable0: LEC93: lda PPUCNT0ZP ; eor #$01 ;If currently on name table 0,--> and #$01 ;return #$01. Else return #$00. tay ; rts ; UpdateRoomSpriteInfo: LEC9B: ldx ScrollDir dex ldy #$00 jsr UpdateDoorData ;($ED51)Update name table 0 door data. iny jsr UpdateDoorData ;($ED51)Update name table 3 door data. ldx #$50 jsr GetNameTable ;($EB85) tay * tya eor EnNameTable,x lsr bcs + lda $0405,x and #$02 bne + sta EnStatus,x * jsr Xminus16 bpl -- ldx #$18 * tya eor $B3,x lsr bcs + lda #$00 sta $B0,x * txa sec sbc #$08 tax bpl -- jsr LED65 jsr LED5B jsr GetNameTable ;(EB85) asl asl tay ldx #$C0 * tya eor TileWRAMHi,x and #$04 bne + sta $0500,x * jsr Xminus16 cmp #$F0 bne -- tya lsr lsr tay ldx #$D0 jsr LED7A ldx #$E0 jsr LED7A ldx #$F0 jsr LED7A tya sec sbc $032C bne + sta ElevatorStatus * 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 UpdateDoorData: LED51: txa ; LED52: eor #$03 ; LED54: and $006C,y ;Moves door info from one name table to the next--> LED57:* sta $006C,y ;when the room is transferred across name tables. LED5A: rts ; LED5B: jsr GetNameTable ;($EB85) eor #$01 tay lda #$00 beq - LED65: ldx #$B0 * lda ObjAction,x beq + lda ObjectOnScreen,x bne + sta ObjAction,x * jsr Xminus16 bmi -- rts LED7A: lda ObjAction,x cmp #$05 bcc + tya eor ObjectHi,x lsr bcs + sta ObjAction,x * rts LED8C: tya cmp PowerUpNameTable,x bne Exit11 lda #$FF sta PowerUpType,x Exit11: rts ;---------------------------------------[ Setup special items ]-------------------------------------- ;The following routines look for special items on the game map and jump to ;the appropriate routine to handle those items. ScanForItems: LED98: lda SpecItmsTable ;Low byte of ptr to 1st item data. LED9B: sta $00 ; LED9D: lda SpecItmsTable+1 ;High byte of ptr to 1st item data. ScanOneItem: LEDA0: sta $01 ; LEDA2: ldy #$00 ;Index starts at #$00. LEDA4: lda ($00),y ;Load map Ypos of item.--> LEDA6: cmp MapPosY ;Does it equal Samus' Ypos on map?--> LEDA8: beq + ;If yes, check Xpos too. LEDAA: bcs Exit11 ;Exit if item Y pos > Samus Y Pos. LEDAC: iny ; LEDAD: lda ($00),y ;Low byte of ptr to next item data. LEDAF: tax ; LEDB0: iny ; LEDB1: and ($00),y ;AND with hi byte of item ptr. LEDB3: cmp #$FF ;if result is FFh, then this was the last item--> LEDB5: beq Exit11 ;(item ptr = FFFF). Branch to exit. LEDB7: lda ($00),y ;High byte of ptr to next item data. LEDB9: stx $00 ;Write low byte for next item. LEDBB: jmp ScanOneItem ;Process next item. LEDBE:* lda #$03 ;Get ready to look at byte containing X pos. LEDC0: jsr AddToPtr00 ;($EF09)Add 3 to pointer at $0000. ScanItemX: LEDC3: ldy #$00 ; LEDC5: lda ($00),y ;Load map Xpos of object.--> LEDC7: cmp MapPosX ;Does it equal Samus' Xpos on map?--> LEDC9: beq + ;If so, then load object. LEDCB: bcs Exit11 ;Exit if item pos X > Samus Pos X. LEDCD: iny ; LEDCE: jsr AnotherItem ;($EF00)Check for another item on same Y pos. LEDD1: jmp ScanItemX ;Try next X coord. LEDD4:* lda #$02 ;Move ahead two bytes to find item data. ChooseHandlerRoutine: LEDD6: jsr AddToPtr00 ;($EF09)Add A to pointer at $0000. LEDD9: ldy #$00 ; LEDDB: lda ($00),y ;Object type LEDDD: and #$0F ;Object handling routine index stored in 4 LSBs. LEDDF: jsr ChooseRoutine ;($C27C)Load proper handling routine from table below. ;Handler routines jumped to by above code. LEDE2: .word ExitSub ;($C45C)rts. LEDE4: .word SqueeptHandler ;($EDF8)Some squeepts. LEDE6: .word PowerUpHandler ;($EDFE)power-ups. LEDE8: .word SpecEnemyHandler ;($EE63)Special enemies(Mellows, Melias and Memus). LEDEA: .word ElevatorHandler ;($EEA1)Elevators. LEDEC: .word CannonHandler ;($EEA6)Mother brain room cannons. LEDEE: .word MotherBrainHandler ;($EEAE)Mother brain. LEDF0: .word ZeebetiteHandler ;($EECA)Zeebetites. LEDF2: .word RinkaHandler ;($EEEE)Rinkas. LEDF4: .word DoorHandler ;($EEF4)Some doors. LEDF6: .word PaletteHandler ;($EEFA)Background palette change. ;---------------------------------------[ Squeept handler ]------------------------------------------ SqueeptHandler: LEDF8: jsr GetEnemyData ;($EB0C)Load Squeept data. LEDFB:* jmp ChooseHandlerRoutine ;($EDD6)Exit handler routines. ;--------------------------------------[ Power-up Handler ]------------------------------------------ PowerUpHandler: LEDFE: iny ;Prepare to store item type. LEDFF: ldx #$00 ; LEE01: lda #$FF ; LEE03: cmp PowerUpType ;Is first power-up item slot available?--> LEE06: beq + ;if yes, branch to load item. LEE08: ldx #$08 ;Prepare to check second power-up item slot. LEE0A: cmp PowerUpBType ;Is second power-up item slot available?--> LEE0D: bne ++ ;If not, branch to exit. LEE0F:* lda ($00),y ;Power-up item type. LEE11: jsr PrepareItemID ;($EE3D)Get unique item ID. LEE14: jsr CheckForItem ;($EE4A)Check if Samus already has item. LEE17: bcs + ;Samus already has item. do not load it. LEE19: ldy #$02 ;Prepare to load item coordinates. LEE1B: lda $09 ; LEE1D: sta PowerUpType,x ;Store power-up type in available item slot. LEE20: lda ($00),y ;Load x and y screen positions of item. LEE22: tay ;Save position data for later processing. LEE23: and #$F0 ;Extract Y coordinate. LEE25: ora #$08 ;+ 8 to find Y coordinate center. LEE27: sta PowerUpYCoord,x ;Store center Y coord LEE2A: tya ;Reload position data. LEE2B: jsr Amul16 ;($C2C5)*16. Move lower 4 bits to upper 4 bits. LEE2E: ora #$08 ;+ 8 to find X coordinate center. LEE30: sta PowerUpXCoord,x ;Store center X coord LEE33: jsr GetNameTable ;($EB85)Get name table to place item on. LEE36: sta PowerUpNameTable,x ;Store name table Item is located on. LEE39:* lda #$03 ;Get next data byte(Always #$00). LEE3B: bne --- ;Branch always to exit handler routines. PrepareItemID: LEE3D: sta $09 ;Store item type. LEE3E: lda MapPosX ; LEE41: sta $07 ;Store item X coordinate. LEE42: lda MapPosY ; LEE45: sta $06 ;Store item Y coordinate. LEE47: jmp CreateItemID ;($DC67)Get unique item ID. CheckForItem: LEE4A: ldy NumberOfUniqueItems ; LEE4D: beq +++ ;Samus has no unique items. Load item and exit. LEE4F:* lda $07 ; LEE51: cmp NumberOfUniqueItems,y ;Look for lower byte of unique item. LEE54: bne + ; LEE56: lda $06 ;Look for upper byte of unique item. LEE58: cmp DataSlot,y ; LEE5B: beq +++ ;Samus already has item. Branch to exit. LEE5D:* dey ; LEE5E: dey ; LEE5F: bne -- ;Loop until all Samus' unique items are checked. LEE61:* clc ;Samus does not have the item. It will be placed on screen. LEE62:* rts ; ;----------------------------------------------------------------------------------------------------- SpecEnemyHandler: LEE63: ldx #$18 lda RandomNumber1 adc FrameCount sta $8A * jsr LEE86 txa sec sbc #$08 tax bpl - lda $95E4 sta $6BE9 sta $6BEA lda #$01 sta $6BE4 * jmp ChooseHandlerRoutine ;($EDD6)Exit handler routines. LEE86: lda $B0,x bne + txa adc $8A and #$7F sta $B1,x adc RandomNumber2 sta $B2,x jsr GetNameTable ;($EB85) sta $B3,x lda #$01 sta $B0,x rol $8A * rts ElevatorHandler: LEEA1: jsr LEC09 bne -- ;Branch always. CannonHandler: LEEA6: jsr $95B1 lda #$02 * jmp ChooseHandlerRoutine ;($EDD6)Exit handler routines. MotherBrainHandler: LEEAE: jsr $95B4 lda #$38 sta $07 lda #$00 sta $06 jsr LEE4A bcc LEEC6 lda #$08 sta MotherBrainStatus lda #$00 sta MotherBrainHits LEEC6: lda #$01 bne - ZeebetiteHandler: LEECA: jsr $95B7 txa lsr 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 RinkaHandler: LEEEE: jsr $95BA jmp LEEC6 DoorHandler: LEEF4: jsr LEB92 jmp ChooseHandlerRoutine ;($EDD6)Exit handler routines. PaletteHandler: LEEFA: lda ScrollDir sta $91 bne LEEC6 AnotherItem: LEF00: lda ($00),y ;Is there another item with same Y pos?--> cmp #$FF ;If so, A is amount to add to ptr. to find X pos. bne AddToPtr00 ;($EF09) pla ; pla ;No more items to check. Pull last subroutine--> rts ;off stack and exit. AddToPtr00: LEF09: clc ; adc $00 ; sta $00 ;A is added to the 16 bit address stored in $0000. bcc + ; inc $01 ; * rts ; ;----------------------------------[ Draw structure routines ]---------------------------------------- ;Draws one row of the structure. ;A = number of 2x2 tile macros to draw horizontally. DrawStructRow: LEF13: and #$0F ;Row length(in macros). Range #$00 thru #$0F. LEF15: bne + ; LEF17: lda #$10 ;#$00 in row length=16. LEF19:* sta $0E ;Store horizontal macro count. LEF1B: lda (StructPtr),y ;Get length byte again. LEF1D: jsr Adiv16 ;($C2BF)/16. Upper nibble contains x coord offset(if any). LEF20: asl ;*2, because a macro is 2 tiles wide. LEF21: adc CartRAMWorkPtr ;Add x coord offset to CartRAMWorkPtr and save in $00. LEF23: sta $00 ; LEF25: lda #$00 ; LEF27: adc CartRAMWorkPtr+1 ;Save high byte of work pointer in $01. LEF29: sta $01 ;$0000 = work pointer. DrawMacro: LEF2B: lda $01 ;High byte of current location in room RAM. LEF2D: cmp #$63 ;Check high byte of room RAM address for both room RAMs--> LEF2F: beq + ;to see if the attribute table data for the room RAM has--> LEF31: cmp #$67 ;been reached. If so, branch to check lower byte as well. LEF33: bcc ++ ;If not at end of room RAM, branch to draw macro. LEF35: beq + ; LEF37: rts ;Return if have gone past room RAM(should never happen). LEF38:* lda $00 ;Low byte of current nametable address. LEF3A: cmp #$A0 ;Reached attrib table?--> LEF3C: bcc + ;If not, branch to draw the macro. LEF3E: rts ;Can't draw any more of the structure, exit. LEF3F:* inc $10 ;Increase struct data index. LEF41: ldy $10 ;Load struct data index into Y. LEF43: lda (StructPtr),y ;Get macro number. LEF45: asl ; LEF46: asl ;A=macro number * 4. Each macro is 4 bytes long. LEF47: sta $11 ;Store macro index. LEF49: ldx #$03 ;Prepare to copy four tile numbers. LEF4B:* ldy $11 ;Macro index loaded into Y. LEF4D: lda (MacroPtr),y ;Get tile number. LEF4F: inc $11 ;Increase macro index LEF51: ldy TilePosTable,x ;get tile position in macro. LEF54: sta ($00),y ;Write tile number to room RAM. LEF56: dex ;Done four tiles yet?--> LEF57: bpl - ;If not, loop to do another. LEF59: jsr UpdateAttrib ;($EF9E)Update attribute table if necessary LEF5C: ldy #$02 ;Macro width(in tiles). LEF5E: jsr AddYToPtr00 ;($C2A8)Add 2 to pointer to move to next macro. LEF61: lda $00 ;Low byte of current room RAM work pointer. LEF63: and #$1F ;Still room left in current row?--> LEF65: bne + ;If yes, branch to do another macro. ;End structure row early to prevent it from wrapping on to the next row.. LEF67: lda $10 ;Struct index. LEF69: clc ; LEF6A: adc $0E ;Add number of macros remaining in current row. LEF6C: sec ; LEF6D: sbc #$01 ;-1 from macros remaining in current row. LEF6F: jmp AdvanceRow ;($EF78)Move to next row of structure. LEF72:* dec $0E ;Have all macros been drawn on this row?--> LEF74: bne DrawMacro ;If not, branch to draw another macro. LEF76: lda $10 ;Load struct index. AdvanceRow: LEF78: sec ;Since carry bit is set,--> LEF79: adc StructPtr ;addition will be one more than expected. LEF7B: sta StructPtr ;Update the struct pointer. LEF7D: bcc + ; LEF7F: inc StructPtr+1 ;Update high byte of struct pointer if carry occured. LEF81:* lda #$40 ; LEF83: clc ; LEF84: adc CartRAMWorkPtr ;Advance to next macro row in room RAM(two tile rows). LEF86: sta CartRAMWorkPtr ; LEF88: bcc DrawStruct ;Begin drawing next structure row. LEF8A: inc CartRAMWorkPtr+1 ;Increment high byte of pointer if necessary. DrawStruct: LEF8C: ldy #$00 ;Reset struct index. LEF8E: sty $10 ; LEF90: lda (StructPtr),y ;Load data byte. LEF92: cmp #$FF ;End-of-struct?--> LEF94: beq + ;If so, branch to exit. LEF96: jmp DrawStructRow ;($EF13)Draw a row of macros. LEF99:* rts ; ;The following table is used to draw macros in room RAM. Each macro is 2 x 2 tiles. ;The following table contains the offsets required to place the tiles in each macro. TilePosTable: LEF9A: .byte $21 ;Lower right tile. LEF9B: .byte $20 ;Lower left tile. LEF9C: .byte $01 ;Upper right tile. LEF9D: .byte $00 ;Upper left tile. ;---------------------------------[ Update attribute table bits ]------------------------------------ ;The following routine updates attribute bits for one 2x2 tile section on the screen. UpdateAttrib: LEF9E: lda ObjectPal ;Load attribute data of structure. LEFA0: cmp RoomPal ;Is it the same as the room's default attribute data?--> LEFA2: beq +++++ ;If so, no need to modify the attribute table, exit. ;Figure out cart RAM address of the byte containing the relevant bits. LEFA4: lda $00 ; LEFA6: sta $02 ; LEFA8: lda $01 ; LEFAA: lsr ; LEFAB: ror $02 ; LEFAD: lsr ; LEFAE: ror $02 ; LEFB0: lda $02 ;The following section of code calculates the--> LEFB2: and #$07 ;proper attribute byte that corresponds to the--> LEFB4: sta $03 ;macro that has just been placed in the room RAM. LEFB6: lda $02 ; LEFB8: lsr ; LEFB9: lsr ; LEFBA: and #$38 ; LEFBC: ora $03 ; LEFBE: ora #$C0 ; LEFC0: sta $02 ; LEFC2: lda #$63 ; LEFC4: sta $03 ;$0002 contains pointer to attribute byte. LEFC6: ldx #$00 ; LEFC8: bit $00 ; LEFCA: bvc + ; LEFCC: ldx #$02 ;The following section of code figures out which--> LEFCE:* lda $00 ;pair of bits to modify in the attribute table byte--> LEFD0: and #$02 ;for the macro that has just been placed in the--> LEFD2: beq + ;room RAM. LEFD4: inx ; ;X now contains which macro attribute table bits to modify: ;+---+---+ ;| 0 | 1 | ;+---+---+ ;| 2 | 3 | ;+---+---+ ;Where each box represents a macro(2x2 tiles). ;The following code clears the old attribute table bits and sets the new ones. LEFD5:* lda $01 ;Load high byte of work pointer in room RAM. LEFD7: and #$04 ; LEFD9: ora $03 ;Choose proper attribute table associated with the--> LEFDB: sta $03 ;current room RAM. LEFDD: lda AttribMaskTable,x ;Choose appropriate attribute table bit mask from table below. LEFE0: ldy #$00 ; LEFE2: and ($02),y ;clear the old attribute table bits. LEFE4: sta ($02),y ; LEFE6: lda ObjectPal ;Load new attribute table data(#$00 thru #$03). LEFE8:* dex ; LEFE9: bmi + ; LEFEB: asl ; LEFEC: asl ;Attribute table bits shifted one step left LEFED: bcc - ;Loop until attribute table bits are in the proper location. LEFEF:* ora ($02),y ; LEFF1: sta ($02),y ;Set attribute table bits. LEFF3:* rts ; AttribMaskTable: LEFF4: .byte %11111100 ;Upper left macro. LEFF5: .byte %11110011 ;Upper right macro. LEFF6: .byte %11001111 ;Lower left macro. LEFF7: .byte %00111111 ;Lower right macro. ;------------------------[ Initialize room RAM and associated attribute table ]----------------------- InitTables: LEFF8: lda CartRAMPtr+1 ;#$60 or #$64. LEFFA: tay ; LEFFB: tax ;Save value to create counter later. LEFFC: iny ; LEFFD: iny ;High byte of address to fill to ($63 or $67). LEFFE: iny ; LEFFF: lda #$FF ;Value to fill room RAM with. LF001: jsr FillRoomRAM ;($F01C)Fill entire RAM for designated room with #$FF. LF004: ldx $01 ;#$5F or #$63 depening on which room RAM was initialized. LF006: jsr Xplus4 ;($E193)X = X + 4. LF009: stx $01 ;Set high byte for attribute table write(#$63 or #$67). LF00B: ldx RoomPal ;Index into table below (Lowest 2 bits). LF00D: lda ATDataTable,x ;Load attribute table data from table below. LF010: ldy #$C0 ;Low byte of start of all attribute tables. LF012:* sta ($00),y ;Fill attribute table. LF014: iny ; LF015: bne - ;Loop until entire attribute table is filled. LF017: rts ; ATDataTable: LF018: .byte %00000000 ; LF019: .byte %01010101 ;Data to fill attribute tables with. LF01A: .byte %10101010 ; LF01B: .byte %11111111 ; FillRoomRAM: LF01C: pha ;Temporarily store A. LF01D: txa ; LF01E: sty $01 ;Calculate value to store in X to use as upper byte--> LF020: clc ;counter for initilaizing room RAM(X=#$FC).--> LF021: sbc $01 ;Since carry bit is cleared, result is one less than expected. LF023: tax ; LF024: pla ;Restore value to fill room RAM with(#$FF). LF025: ldy #$00 ;Lower address byte to start at. LF027: sty $00 ; LF029:* sta ($00),y ; LF02B: dey ; LF02C: bne - ; LF02E: dec $01 ;Loop until all the room RAM is filled with #$FF(black). LF030: inx ; LF031: bne - ; LF033: 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 AreObjectsTouching ;($DC7F) jsr LF277 * jsr Xminus16 bmi -- ; enemy <--> bullet/missile/bomb detection * ldx #$50 ; start with enemy slot #5 LF09F: lda EnStatus,x ; slot active? beq + ; branch if not cmp #$03 * beq NextEnemy ; next slot jsr LF152 lda EnStatus,x cmp #$05 beq ++++ ldy #$D0 ; first projectile slot * lda ObjAction,y ; is it active? beq ++ ; branch if not cmp #wa_BulletExplode bcc + cmp #$07 beq + cmp #wa_BombExplode beq + cmp #wa_Missile bne ++ ; check if enemy is actually hit * jsr LF140 jsr LF2CA * jsr Yplus16 ; next projectile slot bne --- * ldy #$00 lda SamusBlink bne NextEnemy jsr IsSamusDead beq NextEnemy jsr LF140 jsr LF282 NextEnemy: jsr Xminus16 bmi + jmp LF09F * ldx #$00 jsr LF172 ldy #$60 * lda EnStatus,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 SubtractHealth ;($CE92) LF140: jsr LF1BF jsr LF186 jmp LF1FA LF149: jsr LF186 jsr LF1D2 jmp LF1FA LF152: lda EnYRoomPos,x sta $07 ; Y coord lda EnXRoomPos,x sta $09 ; X coord lda EnNameTable,x ; hi coord jmp LF17F LF162: lda EnYRoomPos,y ; Y coord sta $06 lda EnXRoomPos,y ; X coord sta $08 lda EnNameTable,y ; hi coord jmp LF193 GetObject0CoordData: LF172: lda ObjectY,x sta $07 lda ObjectX,x sta $09 lda ObjectHi,x LF17F: eor PPUCNT0ZP and #$01 sta $0B rts GetObject1CoordData: 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 DistFromObj0ToObj1: LF1A7: lda ObjRadY,x jsr LF1E0 lda ObjRadX,x jmp LF1D9 DistFromObj0ToEn1: LF1B3: lda ObjRadY,x jsr LF1E7 lda ObjRadX,x jmp LF1CB DistFromEn0ToObj1: LF1BF: lda EnRadY,x jsr LF1E0 lda EnRadX,x jmp LF1D9 AddEnemy1XRadius: LF1CB: clc adc EnRadX,y sta $05 rts LF1D2: lda #$04 jsr LF1E0 lda #$08 AddObject1XRadius: LF1D9: clc adc ObjRadX,y sta $05 rts AddObject1YRadius: LF1E0: clc adc ObjRadY,y sta $04 rts LF1E7: clc adc EnRadY,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 ;Load object 0 y coord. sec ; sbc $06 ;Subtract object 1 y coord. sta $00 ;Store difference in $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 IsScrewAttackActive ;($CD9C)Check if screw attack active. ldy #$00 bcc +++ lda EnStatus,x cmp #$04 bcs Exit17 lda EnDataIndex,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 IsScrewAttackActive ;($CD9C)Check if screw attack active. 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 IsScrewAttackActive ;($CD9C)Check if screw attack active. pla tay bcc + lda #$80 sta $010F jsr LF332 jsr LF270 LF306: lda $95CE sta HealthLoChange lda $95CF sta HealthHiChange * rts LF311: bcs Exit22 lda #$E0 sta $010F jsr LF338 lda $0F beq + lda #$01 * sta $73 ClearHealthChange: LF323: lda #$00 LF325: sta HealthLoChange LF327: sta HealthHiChange Exit22: LF329: rts ;Return for routine above and below. LF32A: bcs Exit22 jsr LF279 jmp LF2BF LF332: jsr LF340 jmp Amul8 ; * 8 LF338: lda $10 asl asl asl jmp LF27B LF340: lda $10 eor #$03 rts ; UpdateEnemies ; ============= UpdateEnemies: LF345: ldx #$50 ;Load x with #$50 * jsr DoOneEnemy ;($F351) ldx PageIndex jsr Xminus16 bne - DoOneEnemy: LF351: stx PageIndex ;PageIndex starts at $50 and is subtracted by #$0F each--> ;iteration. There is a max of 6 enemies at a time. ldy EnStatus,x beq + cpy #$03 bcs + jsr LF37F * jsr LF3AA lda EnStatus,x sta $81 cmp #$07 bcs + jsr ChooseRoutine ; Pointer table to code .word ExitSub ;($C45C) rts .word $F3BE .word $F3E6 .word $F40D .word $F43E .word $F483 .word $F4EE * jmp KillObject ;($FA18)Free enemy data slot. LF37F: lda $0405,x and #$02 bne + lda EnYRoomPos,x ; Y coord sta $0A lda EnXRoomPos,x ; X coord sta $0B lda EnNameTable,x ; hi coord sta $06 lda EnRadY,x sta $08 lda EnRadX,x sta $09 jsr IsObjectVisible ;($DFDF)Determine if object is within the screen boundaries. txa bne + pla pla * ldx PageIndex rts LF3AA: lda $0405,x asl rol tay txa jsr Adiv16 ;($C2BF)/16. eor FrameCount lsr tya ror ror sta $0405,x rts LF3BE: lda $0405,x asl bmi + lda #$00 sta $6B01,x sta EnCounter,x sta $040A,x jsr LF6B9 jsr LF75B jsr LF682 jsr LF676 lda EnDelay,x beq + jsr LF7BA * jmp ++ LF3E6: lda $0405,x asl bmi ++ lda $0405,x and #$20 beq + ldy EnDataIndex,x lda EnemyInitDelayTbl,y ;($96BB) sta EnDelay,x dec EnStatus,x bne ++ * jsr LF6B9 jsr LF75B jsr LF51E LF40A:* jsr LF536 jmp $95E5 LF410: jsr UpdateEnemyAnim jsr $8058 LF416: ldx PageIndex lda EnSpecialAttribs,x bpl + lda ObjectCntrl bmi + lda #$A3 LF423: sta ObjectCntrl * lda EnStatus,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 EnStatus,x cmp #$03 beq LF410 bit ObjectCntrl bmi + lda #$A1 sta ObjectCntrl * lda FrameCount and #$07 bne + dec $040D,x bne + lda EnStatus,x cmp #$03 beq + lda $040C,x sta EnStatus,x ldy EnDataIndex,x lda $969B,y sta $040D,x * lda $040D,x cmp #$0B bcs + lda FrameCount and #$02 beq + asl ObjectCntrl * jmp LF416 LF483: lda $0404,x and #$24 beq ++++++ jsr KillObject ;($FA18)Free enemy data slot. ldy EnAnimFrame,x cpy #$80 beq PickupMissile tya pha lda EnDataIndex,x pha ldy #$00 ldx #$03 pla bne ++ dex pla cmp #$81 bne + ldx #$00 ;Increase HealthHi by 0. ldy #$50 ;Increase HealthLo by 5. * pha * pla sty HealthLoChange stx HealthHiChange jsr AddHealth ;($CEF9)Add health to Samus. jmp SFX_EnergyPickup PickupMissile: lda #$02 ldy EnDataIndex,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 ;($FA18)Free enemy data slot. * lda FrameCount and #$02 lsr ora #$A0 sta ObjectCntrl jmp LF416 LF4EE: dec EnSpecialAttribs,x bne ++ lda $040C,x tay and #$C0 sta EnSpecialAttribs,x tya and #$3F sta EnStatus,x pha jsr $80B0 and #$20 beq + pla jsr LF515 pha * pla * lda #$A0 jmp LF423 LF515: sta $040C,x LF518: lda #$04 sta EnStatus,x rts LF51E: lda ScrollDir ldx PageIndex cmp #$02 bcc ++ lda EnYRoomPos,x ; Y coord cmp #$EC bcc ++ jmp KillObject ;($FA18)Free enemy data slot. * jsr SFX_MetroidHit jmp GetPageIndex LF536: lda EnSpecialAttribs,x sta $0A lda $0404,x and #$20 beq + lda $040E,x cmp #$03 bne +++ bit $0A bvs +++ lda EnStatus,x cmp #$04 beq +++ jsr LF515 lda #$40 sta $040D,x jsr $80B0 and #$20 beq + lda #$05 sta EnHitPoints,x jmp $95A8 * rts * jsr $80B0 and #$20 bne --- jsr SFX_Metal jmp LF42D * lda EnHitPoints,x cmp #$FF beq -- bit $0A bvc + jsr SFX_BossHit 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 SFX_BigEnemyHit ;($CBCE) * ldx PageIndex jsr $80B0 and #$20 beq + lda $040E,x cmp #$0B bne ---- * lda EnStatus,x cmp #$04 bne + lda $040C,x * ora $0A sta $040C,x asl bmi + jsr $80B0 and #$20 bne + ldy $040E,x cpy #$0B beq +++++ cpy #$81 beq +++++ * lda #$06 sta EnStatus,x lda #$0A bit $0A bvc + lda #$03 * sta EnSpecialAttribs,x cpy #$02 beq + bit $0A bvc ++ ldy $040E,x cpy #$0B bne ++ dec EnHitPoints,x beq +++ dec EnHitPoints,x beq +++ * dec EnHitPoints,x beq ++ * dec EnHitPoints,x bne GetPageIndex * lda #$03 sta EnStatus,x bit $0A bvs + lda $040E,x cmp #$02 bcs + lda #$00 jsr LDCFC ldx PageIndex * jsr LF844 lda $960B,y jsr LF68D sta EnCounter,x ldx #$C0 * lda EnStatus,x beq + txa clc adc #$08 tax cmp #$E0 bne - beq GetPageIndex * lda $95DD jsr LF68D lda #$0A sta EnCounter,x inc EnStatus,x lda #$00 bit $0A bvc + lda #$03 * sta $0407,x ldy PageIndex lda EnYRoomPos,y sta EnYRoomPos,x lda EnXRoomPos,y sta EnXRoomPos,x lda EnNameTable,y sta EnNameTable,x GetPageIndex: ldx PageIndex rts LF676: jsr $80B0 asl asl asl and #$C0 sta $6B03,x rts LF682: jsr LF844 lda $963B,y cmp EnResetAnimIndex,x beq + LF68D: sta EnResetAnimIndex,x LF690: sta EnAnimIndex,x LF693: lda #$00 sta EnAnimDelay,x * rts LF699: jsr LF844 lda $965B,y cmp EnResetAnimIndex,x beq Exit12 jsr LF68D ldy EnDataIndex,x lda $967B,y and #$7F beq Exit12 tay * dec EnAnimIndex,x dey bne - Exit12: rts LF6B9: lda #$00 sta $82 jsr LF74B tay lda EnStatus,x cmp #$02 bne + tya and #$02 beq Exit12 * tya dec $040D,x bne Exit12 pha ldy EnDataIndex,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 EnXRoomPos,x cmp ObjectX bne + inc $82 * rol * and #$01 jsr LF744 lsr ror eor $0403,x bpl + jsr $81DA * lda #$FB jsr LF7B3 lda ScrollDir cmp #$02 bcs + jsr LF752 bcc + tya eor PPUCNT0ZP bcs +++ * lda EnYRoomPos,x cmp ObjectY bne + inc $82 inc $82 * rol * and #$01 asl asl jsr LF744 lsr lsr lsr ror eor $0402,x bpl + jmp $820F LF744: ora $0405,x sta $0405,x * rts LF74B: ldy EnDataIndex,x lda $968B,y rts LF752: lda EnNameTable,x tay eor ObjectHi lsr rts LF75B: lda #$E7 sta $06 lda #$18 jsr LF744 ldy EnDataIndex,x lda $96AB,y beq +++++ tay lda $0405,x and #$02 beq ++++ tya ldy #$F7 asl bcs + ldy #$EF * lsr sta $02 sty $06 lda ObjectY sta $00 ldy EnYRoomPos,x lda $0405,x bmi + ldy ObjectX sty $00 ldy EnXRoomPos,x * lda ObjectHi lsr ror $00 lda EnNameTable,x lsr tya ror sec sbc $00 bpl + jsr TwosCompliment ;($C3D4) * lsr lsr lsr cmp $02 bcc ++ * lda $06 LF7B3: and $0405,x sta $0405,x * rts LF7BA: dec EnDelay,x bne + lda $0405,x and #$08 bne ++ inc EnDelay,x * rts * lda EnDataIndex,x cmp #$07 bne + jsr SFX_OutOfHole ldx PageIndex * inc EnStatus,x jsr LF699 ldy EnDataIndex,x lda $96CB,y clc adc #$D1 sta $00 lda #$00 adc #$97 sta $01 lda FrameCount eor RandomNumber1 ldy #$00 and ($00),y tay iny lda ($00),y sta $0408,x jsr $80B0 bpl ++ lda #$00 sta EnCounter,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 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 lsr * lsr lda EnDataIndex,x rol tay rts LF852: txa lsr lsr lsr adc FrameCount lsr rts LF85A: ldy EnDataIndex,x lda $969B,y sta $040D,x lda EnemyHitPointTbl,y ;($962B) ldy EnSpecialAttribs,x bpl + asl * sta EnHitPoints,x * rts LF870: lda $0405,x and #$10 beq - lda $87 and EnStatus,x beq - lda $87 bpl + ldy $6B01,x bne - * jsr LF8E8 bcs ++ sta $0404,y jsr LF92C lda $0405,x lsr lda $85 pha rol tax lda $978B,x pha tya tax pla jsr LF68D ldx PageIndex lda #$01 sta EnStatus,y and $0405,x tax lda Table15,x sta $0403,y lda #$00 sta $0402,y ldx PageIndex jsr LF8F8 lda $0405,x lsr pla tax lda $97A3,x sta $04 txa rol 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 EnStatus,y beq + jsr Yplus16 cmp #$C0 bne - * rts LF8F8: lda $85 cmp #$02 bcc + ldx PageIndex lda $0405,x lsr lda $88 rol and #$07 sta $040A,y lda #$02 sta EnStatus,y lda #$00 sta EnDelay,y sta EnAnimDelay,y sta $0408,y * rts LF91D: ldx PageIndex jsr LE792 tya tax jsr LFD8F jmp LFA49 ; Table used by above subroutine Table15: .byte $02 .byte $FE LF92C: lda #$02 sta EnRadY,y sta EnRadX,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 ;($FA18)Free enemy data slot. * lda EnStatus,x beq Exit19 jsr ChooseRoutine ; Pointer table to code .word ExitSub ;($C45C) rts .word $F96A .word LF991 ; spit dragon's fireball .word ExitSub ;($C45C) rts .word $FA6B .word $FA91 Exit19: rts LF96A: jsr LFA5B jsr LFA1E ldx PageIndex bcs LF97C lda EnStatus,x beq Exit19 jsr LFA60 LF97C: lda #$01 LF97E: jsr UpdateEnemyAnim jmp LDD8B * inc $0408,x LF987: inc $0408,x lda #$00 sta EnDelay,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 EnDelay,x beq --- inc EnDelay,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 php bcc + tya jsr TwosCompliment ;($C3D4) sta $0403,x * plp bne + lda $0402,x beq + bmi + ldy $040A,x lda $95E0,y sta EnResetAnimIndex,x * jsr LFA1E ldx PageIndex bcs ++ lda EnStatus,x beq Exit20 ldy #$00 lda $040A,x lsr beq + iny * lda $95E2,y jsr LF68D jsr LF518 lda #$0A sta EnDelay,x * jmp LF97C KillObject: LFA18: lda #$00 ; LFA1A: sta EnStatus,x ;Store #$00 as enemy status(enemy slot is open). LFA1D: rts ; ; enemy<-->background crash detection LFA1E: lda InArea cmp #$11 bne + lda EnStatus,x lsr 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 ;($FA18)Free enemy data slot. LFA49: lda $08 sta EnYRoomPos,x lda $09 sta EnXRoomPos,x lda $0B and #$01 sta EnNameTable,x * rts LFA5B: lda $0404,x beq Exit20 LFA60: lda #$00 sta $0404,x lda #$05 sta EnStatus,x Exit20: rts LFA6B: lda EnAnimFrame,x cmp #$F7 beq + dec EnDelay,x bne ++ * jsr KillObject ;($FA18)Free enemy data slot. * jmp LF97C LFA7D: ldx PageIndex lda EnYRoomPos,x sta $02 lda EnXRoomPos,x sta $03 lda EnNameTable,x sta $0B jmp MakeCartRAMPtr ;($E96A)Find enemy position in room RAM. LFA91: jsr KillObject ;($FA18)Free enemy data slot. lda $95DC jsr LF68D jmp LF97C LFA9D: ldx #$C0 * stx PageIndex lda EnStatus,x beq + jsr LFAB4 * lda PageIndex clc adc #$08 tax cmp #$E0 bne -- * rts LFAB4: dec EnCounter,x bne ++ lda #$0C sta EnCounter,x dec $0407,x bmi + bne ++ * jsr KillObject ;($FA18)Free enemy data slot. * lda EnCounter,x cmp #$09 bne + lda $0407,x asl tay lda Table16,y sta $04 lda Table16+1,y sta $05 jsr LFA41 * lda #$80 sta ObjectCntrl lda #$03 jmp LF97E ; Table used by above subroutine Table16: .byte $00 .byte $00 .byte $0C .byte $1C .byte $10 .byte $F0 .byte $F0 .byte $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 EnStatus,x beq + lda $0405,x and #$02 bne Exit13 * sta $0404,x lda #$FF cmp EnDataIndex,x bne + dec EnDelay,x bne Exit13 lda $0728,y jsr LEB28 ldy PageIndex lda $072A,y sta EnYRoomPos,x lda $072B,y sta EnXRoomPos,x lda $072C,y sta EnNameTable,x lda #$18 sta EnRadX,x lda #$0C sta EnRadY,x ldy #$00 jsr LF186 jsr LF152 jsr LF1BF jsr LF1FA bcc Exit13 lda #$01 sta EnDelay,x sta EnStatus,x and ScrollDir asl sta $0405,x ldy EnDataIndex,x jsr LFB7B jmp LF85A * sta EnDataIndex,x lda #$01 sta EnDelay,x jmp KillObject ;($FA18)Free enemy data slot. LFB7B: jsr $80B0 ror $0405,x lda EnemyInitDelayTbl,y ;($96BB)Load initial delay for enemy movement. sta EnDelay,x ; Exit13: rts ;Exit from multiple routines. LFB88: ldx PageIndex jsr LF844 lda $6B01,x inc $6B03,x dec $6B03,x bne + pha pla * bpl + jsr TwosCompliment ;($C3D4) * cmp #$08 bcc + cmp #$10 bcs Exit13 tya and #$01 tay lda $0085,y cmp EnResetAnimIndex,x beq Exit13 sta EnAnimIndex,x dec EnAnimIndex,x sta EnResetAnimIndex,x jmp LF693 * lda $963B,y cmp EnResetAnimIndex,x beq Exit13 jmp LF68D LFBCA: ldx PageIndex jsr LF844 lda $965B,y cmp EnResetAnimIndex,x beq Exit13 sta EnResetAnimIndex,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 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 PowerUpAnimFrame ;Save index to find object animation. txa pha jsr DrawFrame lda SamusBlink bne + ldy #$00 ldx #$40 jsr AreObjectsTouching ;($DC7F) bcs + jsr IsScrewAttackActive ;($CD9C)Check if screw attack active. ldy #$00 bcc + clc jsr LF311 lda #$50 sta HealthLoChange jsr SubtractHealth ;($CE92) * pla tax * rts * lda #$00 sta $A0,x rts ; Table used by above subroutine Table17: .byte $00 .byte $FB .byte $FB .byte $FE .byte $FB .byte $02 .byte $00 .byte $05 LFC65: lda $6BE4 beq ++ ldx #$F0 stx PageIndex lda $6BE9 cmp $95E4 bne +++ lda #$03 jsr UpdateEnemyAnim lda RandomNumber1 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 ;($FA18)Free enemy data slot. LFC98: lda $B0,x jsr ChooseRoutine ; Pointer table to code .word ExitSub ;($C45C) rts .word $FCA5 .word $FCB1 .word $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 bcc + ldy #$FF * sty $05 ldy #$04 lsr 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 TwosCompliment ;($C3D4) * cmp #$10 bcs + tya sta $B4,x lda #$02 sta $B0,x * rts LFD25: txa lsr lsr lsr 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 TwosCompliment ;($C3D4) cpy #$80 bcc ++ * sta $04 * jsr LFD8F jmp LFD6C ; Table used by above subroutine Table18: .byte $02 .byte $FE .byte $01 .byte $FF .byte $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 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 ChooseRoutine ; Pointer table to code .word ExitSub ;($C45C) rts .word $FE3D .word $FE54 .word $FE59 .word $FE54 .word $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: .byte $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 lsr 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 HealthLoChange jmp SubtractHealth ;($CE92) GetTileFramePtr: lda TileAnimFrame,x asl 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 ror $02 lsr 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: .byte $06,$07,$00,$FE .byte $07,$06,$01,$FE .byte $07,$06,$02,$FE .byte $07,$06,$03,$FE .byte $07,$06,$04,$FE .byte $07,$06,$05,$FE .byte $07,$06,$09,$FE .byte $07,$06,$0A,$FE .byte $07,$06,$0B,$FE .byte $07,$06,$08,$FE .byte $00 .byte $00 ;-----------------------------------------------[ RESET ]-------------------------------------------- RESET: LFFB0: SEI ;Disables interrupt LFFB1: CLD ;Sets processor to binary mode LFFB2: LDX #$00 ; LFFB4: STX PPUControl0 ;Clear PPU control registers LFFB7: STX PPUControl1 ; LFFBA:* LDA PPUStatus ; LFFBD: BPL - ;Wait for VBlank LFFBF:* LDA PPUStatus ; LFFC2: BPL - ; LFFC4: ORA #$FF ; LFFC6: STA MMC1Reg0 ;Reset MMC1 chip LFFC9: STA MMC1Reg1 ;(MSB is set) LFFCC: STA MMC1Reg2 ; LFFCF: STA MMC1Reg3 ; LFFD2: JMP Startup ;($C01A)Do preliminary housekeeping. ;Not used. LFFD5: .byte $FF, $FF, $FF, $4C, $E4, $B3, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF LFFE5: .byte $FF, $FF, $FF, $FF, $4D, $45, $54, $52, $4F, $49, $44, $E4, $8D, $00, $00, $38 LFFF5: .byte $04, $01, $06, $01, $BC ;-----------------------------------------[ Interrupt vectors ]-------------------------------------- LBFFA: .word NMI ;($C0D9)NMI vector. LBFFC: .word RESET ;($FFB0)Reset vector. LBFFE: .word RESET ;($FFB0)IRQ vector.