;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Boss Bass ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; symbolic names for RAM addresses RAM_SpriteDir = $157C RAM_NoJumpTimer = $1504 RAM_StateTimer = $1540 RAM_SpriteState = $C2 ExecutePtr = $0086DF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; INIT and MAIN routines ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DCB "INIT" LDA $D8,x ORA #$08 STA $D8,x STZ RAM_SpriteState,x JSR SubHorzPos TYA STA RAM_SpriteDir,x LDA TimeInState STA RAM_StateTimer,x RTL DCB "MAIN" PHB PHK PLB JSR DecrementTimers JSR BossBassMain PLB RTL DecrementTimers: LDA $14C8,x CMP #$08 BNE DoneDec LDA $9D BNE DoneDec LDA RAM_NoJumpTimer,x BEQ DoneDec DEC RAM_NoJumpTimer,x DoneDec: RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Sprite Main ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BossBassMain: JSR SubGfx ; Draw sprite LDA $1528,x ; If the eaten flag is set: BEQ NotEaten LDA #$03 ; Make Mario invisible STA $78 NotEaten: LDA $14C8,X ; \ Return if status != Normal CMP #$08 ; | BNE Return ; / LDA $9D ; \ Return if sprites are locked BNE Return ; / ;; JSR SubOffScreen ; Only process while on screen JSR SetUpMovement JSL $018022 ; Update x position, no gravity JSL $01801A ; Update y position, no gravity JSL $01A7DC ; Handle Mario/sprite contact BCC Return ; Return if no contact JSR HandleContact Return: RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Handle movement ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; TimeInState: dcb $11,$28 SwimSpeedX: DCB $31,$CF JumpProximity: DCB $25,$DC SetUpMovement: LDA RAM_SpriteState,x BNE Jumping Swimming: LDA RAM_SpriteDir,x LDY RAM_StateTimer,x BNE SkipFlipDireciton EOR #$01 STA RAM_SpriteDir,x LDA #$04 STA RAM_NoJumpTimer,x LDA #$1C STA RAM_StateTimer,x SkipFlipDireciton: LDY RAM_SpriteDir,x LDA SwimSpeedX,y STA $B6,x JSR SubHorzPos ;0 if traveling towards mario TYA EOR RAM_SpriteDir,x BNE SkipSetTimerMax LDA RAM_NoJumpTimer,x ; Don't jump if timer is set BNE NoIncState JSR SubVertPos CPY #$01 ; Doesn't jump if above Mario BNE NoIncState LDA $0F CMP #$B0 BCC NoIncState JSR SubHorzPos CPY #$00 BEQ Less20 GreatE0: LDA $0F CMP #$F0 BCS NoIncState CMP #$D0 BCC NoIncState BRA IncState Less20: LDA $0F CMP #$10 BCC NoIncState CMP #$30 BCS NoIncState IncState: INC RAM_SpriteState,x JSR SetStateTimer LDA #$D9 STA $AA,x RTS NoIncState: LDA #$20 STA RAM_StateTimer,x SkipSetTimerMax: STZ $AA,x RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; JumpAccelX: dcb $01,$FF Jumping: LDA RAM_StateTimer,x ; Go back to swimming state if time BEQ GotoSwim LDA $AA,x ; Adjust Y speed CLC ADC #$02 STA $AA,x LDA RAM_SpriteDir,x ; Get index for X acceleration LDY $00AA,x BPL NoAdjustIndex EOR #$01 NoAdjustIndex: TAY LDA $B6,x ; Adjust X speed CLC ADC JumpAccelX,Y STA $B6,x Return2: RTS GotoSwim: STZ RAM_SpriteState,x JSR SetStateTimer STZ $AA,x LDA #$10 STA RAM_NoJumpTimer,x RTS HandleContact: LDA RAM_SpriteState,x ; Only hurt Mario when jumping BEQ Return2 LDA.B #$09 ; Set death animation STA $71 STA $1DFB ; Set death music LDA.B #$30 ; Set time until we return to the OW STA $1496 STA $1528,x ; Set eaten flag Return2: RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SetStateTimer: LDY RAM_SpriteState,x LDA TimeInState,Y STA RAM_StateTimer,x RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Graphics Routine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SpriteTiles: dcb $46,$47,$2B,$49,$59,$FF,$FF,$FF dcb $46,$47,$2B,$4A,$5A,$FF,$FF,$FF dcb $46,$47,$2B,$4D,$7F,$FF,$FF,$FF dcb $46,$47,$04,$4D,$7F SpriteTileSize: dcb $02,$02,$02,$00,$00 ;; SpriteTileDispY: dcb $E8,$E8,$F8,$F8,$00 SpriteTileDispY: dcb $F0,$F0,$00,$00,$08 SpriteTileDispX: dcb $00,$08,$00,$10,$10 dcb $00,$F8,$00,$F8,$F8 SpriteGfxFlip: dcb $00,$40 SubGfx: JSR GetDrawInfo PHY LDA $15F6,X ; $02 = Palette info LDY RAM_SpriteDir,X BNE NoFlip ORA #$40 NoFlip: STA $02 LDA #$03 ; $03 = Tilemap offset LDY $C2,x BNE StoreFrame LDA $14 LSR AND #$02 StoreFrame: ASL ASL ASL STA $03 PLY PHX ; Push sprite index LDX #$04 ; Draw 5 tiles GfxLoopStart: PHX ; Push tile number PHX ; Push tile number LDA $02 AND #$40 BEQ NoAdjust TXA CLC ADC #$05 TAX NoAdjust: LDA $00 CLC ADC SpriteTileDispX,X STA $0300,Y PLX ; Pull tile number LDA $01 CLC ADC SpriteTileDispY,X STA $0301,Y PHX ; Push tile number TXA CLC ADC $03 TAX LDA SpriteTiles,X STA $0302,Y PLX ; Pull tile number LDA $02 ORA $64 STA $0303,Y LDA SpriteTileSize,X PHA TYA LSR LSR TAX PLA STA $0460,X PLX ; Pull tile number INY ; Adjust index into the OAM INY INY INY DEX BPL GfxLoopStart PLX ; Pull sprite index LDY #$FF ; We already wrote to $0460,Y LDA #$04 ; We wrote 5 tiles JSL $01B7B3 RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; GET_DRAW_INFO ; This is a helper for the graphics routine. It sets off screen flags, and sets up ; variables. It will return with the following: ; ; Y = index to sprite OAM ($300) ; $00 = sprite x position relative to screen boarder ; $01 = sprite y position relative to screen boarder ; ; It is adapted from the subroutine at $03B760 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATA_03B75C: dcb $0C,$1C DATA_03B75E: dcb $01,$02 GetDrawInfo: STZ $186C,X ; Reset sprite offscreen flag, vertical STZ $15A0,X ; Reset sprite offscreen flag, horizontal LDA $E4,X ; \ CMP $1A ; | Set horizontal offscreen if necessary LDA $14E0,X ; | SBC $1B ; | BEQ ADDR_03B774 ; | INC $15A0,X ; / ADDR_03B774: LDA $14E0,X ; \ XBA ; | Mark sprite invalid if far enough off screen LDA $E4,X ; | REP #$20 ; Accum (16 bit) SEC ; | SBC $1A ; | CLC ; | ADC.W #$0040 ; | CMP.W #$0180 ; | SEP #$20 ; Accum (8 bit) ROL ; | AND.B #$01 ; | STA $15C4,X ; | BNE ADDR_03B7CF ; / LDY.B #$00 ; \ set up loop: LDA $1662,X ; | AND.B #$20 ; | if not smushed (1662 & 0x20), go through loop twice BEQ ADDR_03B79A ; | else, go through loop once INY ; / ADDR_03B79A: LDA $D8,X ; \ CLC ; | set vertical offscree ADC DATA_03B75C,Y ; | PHP ; | CMP $1C ; | (vert screen boundry) ROL $00 ; | PLP ; | LDA $14D4,X ; | ADC.B #$00 ; | LSR $00 ; | SBC $1D ; | BEQ ADDR_03B7BA ; | LDA $186C,X ; | (vert offscreen) ORA DATA_03B75E,Y ; | STA $186C,X ; | ADDR_03B7BA: DEY ; | BPL ADDR_03B79A ; / LDY $15EA,X ; get offset to sprite OAM LDA $E4,X ; \ SEC ; | SBC $1A ; | STA $00 ; / $00 = sprite x position relative to screen boarder LDA $D8,X ; \ SEC ; | SBC $1C ; | STA $01 ; / $01 = sprite y position relative to screen boarder RTS ; Return ADDR_03B7CF: PLA ; \ Return from *main gfx routine* subroutine... PLA ; | ...(not just this subroutine) RTS ; / ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This routine determines which side of the sprite Mario is on. It sets the Y register ; to the direction such that the sprite would face Mario ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SubHorzPos: LDY #$00 LDA $94 SEC SBC $E4,x STA $0F LDA $95 SBC $14E0,x BPL HorzIncY INY HorzIncY: RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This routine determines if Mario is above or below the sprite. It sets the Y register ; to the direction such that the sprite would face Mario ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SubVertPos: LDY #$00 LDA $96 SEC SBC $D8,x STA $0F LDA $97 SBC $14D4,x BPL VertIncY INY VertIncY: RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SUB_OFF_SCREEN ; This subroutine deals with sprites that have moved off screen ; It is adapted from the subroutine at $01AC0D ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DATA_01AC0D: dcb $40,$B0 DATA_01AC0F: dcb $01,$FF DATA_01AC11: dcb $30,$C0 DATA_01AC19: dcb $01,$FF SubOffScreen: STZ $03 JSR IsSprOnScreen ; \ if sprite is not off screen, return BEQ Return01ACA4 ; / LDA $5B ; \ vertical level AND #$01 ; | BNE VerticalLevel ; / LDA $D8,X ; \ CLC ; | ADC #$50 ; | if the sprite has gone off the bottom of the level... LDA $14D4,X ; | (if adding 0x50 to the sprite y position would make the high byte >= 2) ADC #$00 ; | CMP #$02 ; | BPL OffScrEraseSprite ; / ...erase the sprite LDA $167A,X ; \ if "process offscreen" flag is set, return AND #$04 ; | BNE Return01ACA4 ; / LDA $13 AND #$01 STA $01 TAY LDA $1A CLC ADC DATA_01AC11,Y ROL $00 CMP $E4,X PHP LDA $1B LSR $00 ADC DATA_01AC19,Y PLP SBC $14E0,X STA $00 LSR $01 BCC ADDR_01AC7C EOR #$80 STA $00 ADDR_01AC7C: LDA $00 BPL Return01ACA4 OffScrEraseSprite: LDA $14C8,X ; \ If sprite status < 8, permanently erase sprite CMP #$08 ; | BCC OffScrKillSprite ; / LDY $161A,X CPY #$FF BEQ OffScrKillSprite LDA #$00 STA $1938,Y OffScrKillSprite: STZ $14C8,X ; Erase sprite Return01ACA4: RTS VerticalLevel: LDA $167A,X ; \ If "process offscreen" flag is set, return AND #$04 ; | BNE Return01ACA4 ; / LDA $13 ; \ LSR ; | BCS Return01ACA4 ; / LDA $E4,X ; \ CMP #$00 ; | If the sprite has gone off the side of the level... LDA $14E0,X ; | SBC #$00 ; | CMP #$02 ; | BCS OffScrEraseSprite ; / ...erase the sprite LDA $13 LSR AND #$01 STA $01 TAY LDA $1C CLC ADC DATA_01AC0D,Y ROL $00 CMP $D8,X PHP LDA $001D LSR $00 ADC DATA_01AC0F,Y PLP SBC $14D4,X STA $00 LDY $01 BEQ ADDR_01ACF3 EOR #$80 STA $00 ADDR_01ACF3: LDA $00 BPL Return01ACA4 BMI OffScrEraseSprite IsSprOnScreen: LDA $15A0,X ; \ A = Current sprite is offscreen ORA $186C,X ; / RTS ; Return