;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Dragonfly, by imamelia ;; ;; This is a 32x16 sprite that flies back and forth and can be jumped on. ;; The extra property 1 determines its flight path. ;; ;; Uses first extra bit: YES ;; ;; If the first extra bit is clear, the dragonfly will fly until it hits an object, ;; and then it will turn around. If the first extra bit is set, the sprite will ;; fly in a limited range. ;; ;; Extra Property Byte 1: ;; ;; This determines what flight path the dragonfly will take. ;; 00=stationary, 01=horizontal, 02=vertical, 03=diagonally upper left/lower right, 04=diagonally upper ;; right/lower left. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; TimerValue = $7F TimerTable = $C2 XSpeed: dcb $00,$10,$00,$10,$F0 YSpeed: dcb $00,$00,$10,$10,$10 Tile1 = $82 Tile2 = $84 Tile3 = $86 Tile4 = $88 dcb "INIT" JSR SUB_HORZ_POS TYA STA $157C STZ $AA,x STZ $B6,x ; set initial X and Y speeds to 0 RTL dcb "MAIN" PHB PHK PLB LDA $14C8 CMP #$08 BEQ Eraser JSR SpriteRoutine PLB RTL Eraser STZ $14C8 RTS Label1: JMP Label2 SpriteRoutine JSR SubGFX LDA $14C8 BNE Return LDA $9D ; return if sprites locked BNE Return LDA $7FAB28,x ; load extra property byte 1... TAY ; (you can't LDY a 24-bit address) LDA XSpeed,y ; set X speed based on extra property byte 1 PHY ; save value for later LDY $157C,x CPY #$00 ; BEQ NoFlipXSpeed ; if horizontal direction=0 (right), don't flip X speed value EOR #$FF ; if going left, flip speed value INC A NoFlipXSpeed: STA $B6,x ; store X speed value PLY LDA YSpeed,y ; load second table with same Y value PHY LDY $151C,x CPY #$00 BEQ NoFlipYSpeed ; if vertical direction=0 (down), don't flip Y speed value EOR #$FF INC A NoFlipYSpeed: STA $AA,x ; store Y speed value PLY ; get previous Y value back LDA $7FAB10,x AND #$04 ; if first extra bit is not set... BEQ DontUseTimer ; don't worry about the timer LDA TimerTable,x BEQ Reset ; if timer=0, reset timer DEC TimerTable,x ; if not, decrement timer BRA Label2 ; skip timer reset Reset LDA #TimerValue STA TimerTable,x BRA Flip DontUseTimer LDA $1588,x AND #$0F ; if sprite is in contact with any surfaces... BEQ Label2 ; flip direction Flip LDA $157C,x EOR #$01 ; flip horizontal direction STA $157C,x ; when resetting timer (or touching object side) LDA $151C,x EOR #$01 ; as well as STA $151C,x ; vertical direction Label2 JSL $018022 ; Update X position without gravity JSL $01801A ; Update Y position without gravity LDA $14c8,x CMP #$08 BCC Return JSL $01A7DC ; interact with Mario JSL $019138 ; interact with objects (normally called within $01802A) Return RTS SubGFX JSR GET_DRAW_INFO LDA $14 AND #$10 ; BEQ SecondFrame ; use different graphics if sprite is on its second frame LDA $157C,x ; store sprite's direction STA $02 ; to $02 for later LDA $00 ; PHX LDX $02 CPX #$00 BNE NoAdd1 ; change offset if the sprite is facing right CLC ADC #$10 NoAdd1: PLX STA $0300,y ; write X position of first tile LDA $01 ; STA $0301,y ; write Y position of first tile LDA #Tile1 ; STA $0302,y ; store first tile to $0302,y LDA $15F6,x ; load palette and GFX page info PHX LDX $02 CPX #$00 BNE NoFlip1 ; Y-flip tile if the sprite is facing right ORA #$40 NoFlip1 PLX ORA $64 ; add priority information STA $0303,y INY INY INY INY ; prepare Y for the next tile LDA $00 ; PHX LDX $02 CPX #$00 BEQ NoAdd2 ; change offset if the sprite is facing right CLC ADC #$10 NoAdd2: PLX STA $0300,y ; write X position of second tile LDA $01 ; STA $0301,y ; write Y position of second tile LDA #Tile2 ; STA $0302,y ; store second tile to $0302,y LDA $15F6,x ; load palette and GFX page info PHX LDX $02 CPX #$00 BNE NoFlip2 ; Y-flip tile if the sprite is facing right ORA #$40 NoFlip2 PLX ORA $64 ; add priority information STA $0303,y INY INY INY INY ; prepare Y for the next tile BRA EndGFXRoutine SecondFrame LDA $157C,x ; store sprite's direction STA $02 ; to $02 for later LDA $00 ; PHX LDX $02 CPX #$00 BNE NoAdd3 ; change offset if the sprite is facing right CLC ADC #$10 NoAdd3: PLX STA $0300,y ; write X position of first tile LDA $01 ; STA $0301,y ; write Y position of first tile LDA #Tile3 ; STA $0302,y ; store first tile to $0302,y LDA $15F6,x ; load palette and GFX page info PHX LDX $02 CPX #$00 BNE NoFlip3 ; Y-flip tile if the sprite is facing right ORA #$40 NoFlip3 PLX ORA $64 ; add priority information STA $0303,y INY INY INY INY LDA $00 ; PHX LDX $02 CPX #$00 BEQ NoAdd4 ; change offset if the sprite is facing right CLC ADC #$10 NoAdd4: PLX STA $0300,y ; write X position of second tile LDA $01 ; STA $0301,y ; write Y position of second tile LDA #Tile4 ; STA $0302,y ; store second tile to $0302,y LDA $15F6,x ; load palette and GFX page info PHX LDX $02 CPX #$00 BNE NoFlip4 ; Y-flip tile if the sprite is facing right ORA #$40 NoFlip4 PLX ORA $64 ; add priority information STA $0303,y INY INY INY INY ; prepare Y for the next tile EndGFXRoutine: LDY #$02 ; 16x16 tiles LDA #$01 ; 2 tiles were drawn JSL $01B7B3 ; prevent tiles from being overwritten 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SPR_T1 dcb $0C,$1C SPR_T2 dcb $01,$02 GET_DRAW_INFO 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 ON_SCREEN_X ; | INC $15A0,x ; / ON_SCREEN_X LDA $14E0,x ; \ XBA ; | LDA $E4,x ; | REP #$20 ; | SEC ; | SBC $1A ; | mark sprite invalid if far enough off screen CLC ; | ADC.W #$0040 ; | CMP.W #$0180 ; | SEP #$20 ; | ROL A ; | AND #$01 ; | STA $15C4,x ; / BNE INVALID ; LDY #$00 ; \ set up loop LDA $1662,x ; | AND #$20 ; | if not smushed (1662 & 0x20), go through loop twice BEQ ON_SCREEN_LOOP ; | else, go through loop once INY ; / ON_SCREEN_LOOP LDA $D8,x ; \ CLC ; | set vertical offscreen if necessary ADC SPR_T1,y ; | PHP ; | CMP $1C ; | (vert screen boundry) ROL $00 ; | PLP ; | LDA $14D4,x ; | ADC #$00 ; | LSR $00 ; | SBC $1D ; | BEQ ON_SCREEN_Y ; | LDA $186C,x ; | (vert offscreen) ORA SPR_T2,y ; | STA $186C,x ; | ON_SCREEN_Y DEY ; | BPL ON_SCREEN_LOOP ; / LDY $15EA,x ; get offset to sprite OAM LDA $E4,x ; \ SEC ; | SBC $1A ; | $00 = sprite x position relative to screen boarder STA $00 ; / LDA $D8,x ; \ SEC ; | SBC $1C ; | $01 = sprite y position relative to screen boarder STA $01 ; / RTS ; return INVALID PLA ; \ return from *main gfx routine* subroutine... PLA ; | ...(not just this subroutine) RTS ; / ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SUB_OFF_SCREEN ; This subroutine deals with sprites that have moved off screen ; It is adapted from the subroutine at $01AC0D ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SPR_T12 dcb $40,$B0 SPR_T13 dcb $01,$FF SPR_T14 dcb $30,$C0,$A0,$C0,$A0,$F0,$60,$90 ;bank 1 sizes dcb $30,$C0,$A0,$80,$A0,$40,$60,$B0 ;bank 3 sizes SPR_T15 dcb $01,$FF,$01,$FF,$01,$FF,$01,$FF ;bank 1 sizes dcb $01,$FF,$01,$FF,$01,$00,$01,$FF ;bank 3 sizes SUB_OFF_SCREEN_X1 LDA #$02 ; \ entry point of routine determines value of $03 BRA STORE_03 ; | (table entry to use on horizontal levels) SUB_OFF_SCREEN_X2 LDA #$04 ; | BRA STORE_03 ; | SUB_OFF_SCREEN_X3 LDA #$06 ; | BRA STORE_03 ; | SUB_OFF_SCREEN_X4 LDA #$08 ; | BRA STORE_03 ; | SUB_OFF_SCREEN_X5 LDA #$0A ; | BRA STORE_03 ; | SUB_OFF_SCREEN_X6 LDA #$0C ; | BRA STORE_03 ; | SUB_OFF_SCREEN_X7 LDA #$0E ; | STORE_03 STA $03 ; | BRA START_SUB ; | SUB_OFF_SCREEN_X0 STZ $03 ; / START_SUB JSR SUB_IS_OFF_SCREEN ; \ if sprite is not off screen, return BEQ RETURN_35 ; / LDA $5B ; \ goto VERTICAL_LEVEL if vertical level AND #$01 ; | BNE VERTICAL_LEVEL ; / 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 ERASE_SPRITE ; / ...erase the sprite LDA $167A,x ; \ if "process offscreen" flag is set, return AND #$04 ; | BNE RETURN_35 ; / LDA $13 ;A8A00 X0009 Y0001 D0000 dcb01 S01F1 PenvMXdiZcHC0756 VC176 00 FL205 AND #$01 ;A8A01 X0009 Y0001 D0000 dcb01 S01F1 PenvMXdizcHC0780 VC176 00 FL205 ORA $03 ;A8A01 X0009 Y0001 D0000 dcb01 S01F1 PenvMXdizcHC0796 VC176 00 FL205 STA $01 ;A8A01 X0009 Y0001 D0000 dcb01 S01F1 PenvMXdizcHC0820 VC176 00 FL205 TAY ;A8A01 X0009 Y0001 D0000 dcb01 S01F1 PenvMXdizcHC0844 VC176 00 FL205 LDA $1A ;A8A01 X0009 Y0001 D0000 dcb01 S01F1 PenvMXdizcHC0858 VC176 00 FL205 CLC ;A8A00 X0009 Y0001 D0000 dcb01 S01F1 PenvMXdiZcHC0882 VC176 00 FL205 ADC SPR_T14,y ;A8A00 X0009 Y0001 D0000 dcb01 S01F1 PenvMXdiZcHC0896 VC176 00 FL205 ROL $00 ;A8AC0 X0009 Y0001 D0000 dcb01 S01F1 PeNvMXdizcHC0928 VC176 00 FL205 CMP $E4,x ;A8AC0 X0009 Y0001 D0000 dcb01 S01F1 PeNvMXdizCHC0966 VC176 00 FL205 PHP ;A8AC0 X0009 Y0001 D0000 dcb01 S01F1 PenvMXdizCHC0996 VC176 00 FL205 LDA $1B ;A8AC0 X0009 Y0001 D0000 dcb01 S01F0 PenvMXdizCHC1018 VC176 00 FL205 LSR $00 ;A8A00 X0009 Y0001 D0000 dcb01 S01F0 PenvMXdiZCHC1042 VC176 00 FL205 ADC SPR_T15,y ;A8A00 X0009 Y0001 D0000 dcb01 S01F0 PenvMXdizcHC1080 VC176 00 FL205 PLP ;A8AFF X0009 Y0001 D0000 dcb01 S01F0 PeNvMXdizcHC1112 VC176 00 FL205 SBC $14E0,x ;A8AFF X0009 Y0001 D0000 dcb01 S01F1 PenvMXdizCHC1140 VC176 00 FL205 STA $00 ;A8AFF X0009 Y0001 D0000 dcb01 S01F1 PeNvMXdizCHC1172 VC176 00 FL205 LSR $01 ;A8AFF X0009 Y0001 D0000 dcb01 S01F1 PeNvMXdizCHC1196 VC176 00 FL205 BCC SPR_L31 ;A8AFF X0009 Y0001 D0000 dcb01 S01F1 PenvMXdiZCHC1234 VC176 00 FL205 EOR #$80 ;A8AFF X0009 Y0001 D0000 dcb01 S01F1 PenvMXdiZCHC1250 VC176 00 FL205 STA $00 ;A8A7F X0009 Y0001 D0000 dcb01 S01F1 PenvMXdizCHC1266 VC176 00 FL205 SPR_L31 LDA $00 ;A8A7F X0009 Y0001 D0000 dcb01 S01F1 PenvMXdizCHC1290 VC176 00 FL205 BPL RETURN_35 ;A8A7F X0009 Y0001 D0000 dcb01 S01F1 PenvMXdizCHC1314 VC176 00 FL205 ERASE_SPRITE LDA $14C8,x ; \ if sprite status < 8, permanently erase sprite CMP #$08 ; | BCC KILL_SPRITE ; / LDY $161A,x ;AFF08 X0007 Y0001 D0000 dcb01 S01F3 PenvMXdiZCHC1108 VC059 00 FL2878 CPY #$FF ;AFF08 X0007 Y0000 D0000 dcb01 S01F3 PenvMXdiZCHC1140 VC059 00 FL2878 BEQ KILL_SPRITE ;AFF08 X0007 Y0000 D0000 dcb01 S01F3 PenvMXdizcHC1156 VC059 00 FL2878 LDA #$00 ;AFF08 X0007 Y0000 D0000 dcb01 S01F3 PenvMXdizcHC1172 VC059 00 FL2878 STA $1938,y ;AFF00 X0007 Y0000 D0000 dcb01 S01F3 PenvMXdiZcHC1188 VC059 00 FL2878 KILL_SPRITE STZ $14C8,x ; erase sprite RETURN_35 RTS ; return VERTICAL_LEVEL LDA $167A,x ; \ if "process offscreen" flag is set, return AND #$04 ; | BNE RETURN_35 ; / LDA $13 ; \ LSR A ; | BCS RETURN_35 ; / LDA $E4,x ; \ CMP #$00 ; | if the sprite has gone off the side of the level... LDA $14E0,x ; | SBC #$00 ; | CMP #$02 ; | BCS ERASE_SPRITE ; / ...erase the sprite LDA $13 ;A0000 X0009 Y00E4 D0000 dcb01 S01F3 PeNvMXdizcHC1218 VC250 00 FL5379 LSR A ;A0016 X0009 Y00E4 D0000 dcb01 S01F3 PenvMXdizcHC1242 VC250 00 FL5379 AND #$01 ;A000B X0009 Y00E4 D0000 dcb01 S01F3 PenvMXdizcHC1256 VC250 00 FL5379 STA $01 ;A0001 X0009 Y00E4 D0000 dcb01 S01F3 PenvMXdizcHC1272 VC250 00 FL5379 TAY ;A0001 X0009 Y00E4 D0000 dcb01 S01F3 PenvMXdizcHC1296 VC250 00 FL5379 LDA $1C ;A001A X0009 Y0001 D0000 dcb01 S01F3 PeNvMXdizcHC0052 VC251 00 FL5379 CLC ;A00BD X0009 Y0001 D0000 dcb01 S01F3 PeNvMXdizcHC0076 VC251 00 FL5379 ADC SPR_T12,y ;A00BD X0009 Y0001 D0000 dcb01 S01F3 PeNvMXdizcHC0090 VC251 00 FL5379 ROL $00 ;A006D X0009 Y0001 D0000 dcb01 S01F3 PenVMXdizCHC0122 VC251 00 FL5379 CMP $D8,x ;A006D X0009 Y0001 D0000 dcb01 S01F3 PeNVMXdizcHC0160 VC251 00 FL5379 PHP ;A006D X0009 Y0001 D0000 dcb01 S01F3 PeNVMXdizcHC0190 VC251 00 FL5379 LDA.W $001D ;A006D X0009 Y0001 D0000 dcb01 S01F2 PeNVMXdizcHC0212 VC251 00 FL5379 LSR $00 ;A0000 X0009 Y0001 D0000 dcb01 S01F2 PenVMXdiZcHC0244 VC251 00 FL5379 ADC SPR_T13,y ;A0000 X0009 Y0001 D0000 dcb01 S01F2 PenVMXdizCHC0282 VC251 00 FL5379 PLP ;A0000 X0009 Y0001 D0000 dcb01 S01F2 PenvMXdiZCHC0314 VC251 00 FL5379 SBC $14D4,x ;A0000 X0009 Y0001 D0000 dcb01 S01F3 PeNVMXdizcHC0342 VC251 00 FL5379 STA $00 ;A00FF X0009 Y0001 D0000 dcb01 S01F3 PeNvMXdizcHC0374 VC251 00 FL5379 LDY $01 ;A00FF X0009 Y0001 D0000 dcb01 S01F3 PeNvMXdizcHC0398 VC251 00 FL5379 BEQ SPR_L38 ;A00FF X0009 Y0001 D0000 dcb01 S01F3 PenvMXdizcHC0422 VC251 00 FL5379 EOR #$80 ;A00FF X0009 Y0001 D0000 dcb01 S01F3 PenvMXdizcHC0438 VC251 00 FL5379 STA $00 ;A007F X0009 Y0001 D0000 dcb01 S01F3 PenvMXdizcHC0454 VC251 00 FL5379 SPR_L38 LDA $00 ;A007F X0009 Y0001 D0000 dcb01 S01F3 PenvMXdizcHC0478 VC251 00 FL5379 BPL RETURN_35 ;A007F X0009 Y0001 D0000 dcb01 S01F3 PenvMXdizcHC0502 VC251 00 FL5379 BMI ERASE_SPRITE ;A8AFF X0002 Y0000 D0000 dcb01 S01F3 PeNvMXdizcHC0704 VC184 00 FL5490 SUB_IS_OFF_SCREEN LDA $15A0,x ; \ if sprite is on screen, accumulator = 0 ORA $186C,x ; | RTS ; / return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SUB_HORZ_POS ; 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 ; It is ripped from $03B817 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SUB_HORZ_POS LDY #$00 ;A25D0 X0006 Y0001 D0000 dcb03 S01ED PeNvMXdizCHC1020 VC097 00 FL31642 LDA $94 ;A25D0 X0006 Y0000 D0000 dcb03 S01ED PenvMXdiZCHC1036 VC097 00 FL31642 SEC ;A25F0 X0006 Y0000 D0000 dcb03 S01ED PeNvMXdizCHC1060 VC097 00 FL31642 SBC $E4,x ;A25F0 X0006 Y0000 D0000 dcb03 S01ED PeNvMXdizCHC1074 VC097 00 FL31642 STA $0F ;A25F4 X0006 Y0000 D0000 dcb03 S01ED PeNvMXdizcHC1104 VC097 00 FL31642 LDA $95 ;A25F4 X0006 Y0000 D0000 dcb03 S01ED PeNvMXdizcHC1128 VC097 00 FL31642 SBC $14E0,x ;A2500 X0006 Y0000 D0000 dcb03 S01ED PenvMXdiZcHC1152 VC097 00 FL31642 BPL SPR_L16 ;A25FF X0006 Y0000 D0000 dcb03 S01ED PeNvMXdizcHC1184 VC097 00 FL31642 INY ;A25FF X0006 Y0000 D0000 dcb03 S01ED PeNvMXdizcHC1200 VC097 00 FL31642 SPR_L16 RTS ;A25FF X0006 Y0001 D0000 dcb03 S01ED PenvMXdizcHC1214 VC097 00 FL31642