STANDINGLASTFRAME = $1FD6 MOVEMODE = $1504 HEADFRAME = $1528 FLYFRAME = $1602 CHGTMR = $163E WEIGHTTMR = $1540 TEMPRAM = $00 SPRXTMP = $04 SPRYTMP = $06 SPRXTMP2 = $08 SPRYTMP2 = $0A LASTXLO = $1534 LASTXHI = $1570 LASTYLO = $1594 LASTYHI = $1626 WEIGHT_TIME = $18 GLIDE_TIME = $63 FLY_TIME = $FF FLYYSPEEDS dcb $F8,$00,$00,$00,$00,$F8,$F0,$F0,$F8,$10 STAGETIMERS dcb GLIDE_TIME,FLY_TIME ; glide down, fly up ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; INIT and MAIN JSL targets ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; dcb "INIT" JSR SUB_HORZ_POS ;face mario initially TYA STA $157C,x LDA #GLIDE_TIME STA CHGTMR,x RTL dcb "MAIN" PHB PHK PLB JSR SPRITE_ROUTINE PLB RTL RETURN1 RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SPRITE_ROUTINE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SPRITE_ROUTINE JSR SUB_GFX LDA $14C8,x ; \ return if sprite CMP #$08 ; / status is not 8 BNE RETURN1 ; \ return if LDA $9D ; | sprites BNE RETURN1 ; / are locked LDA $15D0,x ; \ return if BNE RETURN1 ; / being eaten JSR SUB_OFF_SCREEN_X0 ; only process sprite while on screen LDA FLYFRAME,x ; \ LSR A ; | set Y speed LSR A ; | according to TAY ; | flying frame LDA FLYYSPEEDS,y ; | STA $AA,x ; / LDA #$F4 ; \ LDY $157C,x ; | set X speed BNE NOFLIPXSPEED ; | and flip if EOR #%11111111 ; | going the INC A ; | other way NOFLIPXSPEED STA $B6,x ; / LDA FLYFRAME,x ; \ CMP #FLYFRAMES-1 ; | go to next in BEQ RESETFLY ; | indexes to INC FLYFRAME,x ; | frame data BRA NORESETFLY ; | RESETFLY STZ FLYFRAME,x ; / NORESETFLY LDA HEADFRAME,x ; \ CMP #HEADFRAMES-1 ; | go to next in BEQ RESETHEAD ; | indexes to INC HEADFRAME,x ; | frame data BRA NORESETHEAD ; | RESETHEAD STZ HEADFRAME,x ; / NORESETHEAD LDA STANDINGLASTFRAME,x ; \ store standing status prior STA TEMPRAM ; / to this frame into scratch RAM JSR POSOFFSETSTART ; \ do everything to JSR MAKE_PLATFORM ; | generate a JSR POSOFFSETEND ; / "platform" LDA STANDINGLASTFRAME,x ; \ don't execute following BEQ NOT_STANDING ; / code if not standing LDA TEMPRAM ; \ BNE WASSTANDINGLAST ; | if first frame of standing, LDA #WEIGHT_TIME ; | then start the weight timer STA WEIGHTTMR,x ; / WASSTANDINGLAST LDA FLYFRAME,x ; \ CMP #FLYFRAMES-1 ; | go to next in BEQ RESETFLY2 ; | indexes to INC FLYFRAME,x ; | frame data BRA NORESETFLY2 ; | (again) RESETFLY2 STZ FLYFRAME,x ; / NORESETFLY2 LDA HEADFRAME,x ; \ CMP #HEADFRAMES-1 ; | go to next in BEQ RESETHEAD2 ; | indexes to INC HEADFRAME,x ; | frame data BRA NORESETHEAD2 ; | (again) RESETHEAD2 STZ HEADFRAME,x ; / NORESETHEAD2 LDA $AA,x ; \ BPL YISPLUS ; | get absolute EOR #%11111111 ; | value of Y INC A ; / YISPLUS LSR A ; \ LDY $AA,x ; | divide by BPL YISPLUS2 ; | 2 and then EOR #%11111111 ; | make negative INC A ; | if necessary YISPLUS2 STA $AA,x ; / LDA $AA,x ; \ CLC ; | generate weight effect ADC WEIGHTTMR,x ; | when mario stands on it STA $AA,x ; / LDA $B6,x ; \ BPL XISPLUS ; | get absolute EOR #%11111111 ; | value of X INC A ; / XISPLUS LSR A ; \ LDY $B6,x ; | divide by BPL XISPLUS2 ; | 2 and then EOR #%11111111 ; | make negative INC A ; | if necessary XISPLUS2 STA $B6,x ; / LDA #$01 ; \ set mode to STA MOVEMODE,x ; / flying up LDA #FLY_TIME ; \ hold timer STA CHGTMR,x ; / at start LDA FLYFRAME,x ; \ CMP #FLYFRAMES ; | if frame is gliding frame BCC FLYFRAMEISOK ; | then set frame to zero STZ FLYFRAME,x ; / FLYFRAMEISOK LDA $77 ; \ AND #%00001000 ; | don't move upwards if BEQ NO_CEIL ; | mario is hitting ceiling STZ $AA,x ; / NO_CEIL NOT_STANDING LDA MOVEMODE,x ; \ BNE NO_GLIDE ; | set frame to glide LDA #FLYFRAMES ; | frame if gliding STA FLYFRAME,x ; / NO_GLIDE LDA CHGTMR,x ; \ BNE NO_CHG_MODE ; | switch modes LDA MOVEMODE,x ; | and set timer EOR #%00000001 ; | properly if STA MOVEMODE,x ; | it's time LDY MOVEMODE,x ; | LDA STAGETIMERS,y ; | STA CHGTMR,x ; | STZ FLYFRAME,x ; / NO_CHG_MODE LDA $D8,x ; \ PHA ; | interact LDA $14D4,x ; | with mario PHA ; | and other LDA $D8,x ; | sprites four CLC ; | pixels lower ADC #$04 ; | than normal STA $D8,x ; | LDA $14D4,x ; | ADC #$00 ; | STA $14D4,x ; | JSL $01A7DC ; | JSL $018032 ; | PLA ; | STA $14D4,x ; | PLA ; | STA $D8,x ; / JSL $018022 ; Update X position without gravity JSL $01801A ; Update Y position without gravity LDA $D8,x ; \ STA SPRYTMP ; | store actual (non-shifted) LDA $14D4,x ; | sprite Y to scratch RAM STA SPRYTMP+1 ; / LDY #$00 ; \ PHP ; | check if REP #%00100000 ; | sprite is LDA SPRYTMP ; | too high CMP.w #$FFD0 ; | BPL POS_IS_OK ; | if not, then LDY #$01 ; | don't execute POS_IS_OK PLP ; | following code CPY #$00 ; | BEQ POS_IS_OK_2 ; / LDA #$00 ; \ kill sprite, LDY $161A,x ; | but allow STA $1938,y ; | sprite to STZ $14C8,x ; / reload again POS_IS_OK_2 RETURN RTS STAND_YPOS = $FFE4 ; Y position of platform relative to sprite STAND_XMIN = $FFF3 ; Left X boundary STAND_XMAX = $000C ; Right X boundary MAKE_PLATFORM LDA $187A ; \ don't shift BEQ NOYOSHI ; / if not on Yoshi LDA $96 ; \ CLC ; | offset Y ADC #$10 ; | by #$10 STA $96 ; | again to LDA $97 ; | compensate ADC #$00 ; | for yoshi STA $97 ; / NOYOSHI LDA LASTXLO,x ; \ STA SPRXTMP2 ; | store sprite's old LDA LASTXHI,x ; | X and Y positions STA SPRXTMP2+1 ; | into scratch LDA LASTYLO,x ; | RAM for use STA SPRYTMP2 ; | in some of the LDA LASTYHI,x ; | following code STA SPRYTMP2+1 ; / LDA $E4,x ; \ STA SPRXTMP ; | store sprite X LDA $14E0,x ; | and Y position STA SPRXTMP+1 ; | into scratch LDA $D8,x ; | RAM for use STA SPRYTMP ; | in some of the LDA $14D4,x ; | following code STA SPRYTMP+1 ; / LDA $E4,x ; \ STA LASTXLO,x ; | store current position LDA $14E0,x ; | to sprite tables for STA LASTXHI,x ; | use next time sprite LDA $D8,x ; | routine is called. STA LASTYLO,x ; | It's used for moving mario LDA $14D4,x ; | while he's standing on it STA LASTYHI,x ; / LDA STANDINGLASTFRAME,x ; \ check if mario was BEQ NOT_STANDING_LAST_FRAME ; / standing last frame LDA $77 ; \ don't move mario if AND #%00000011 ; | he is hitting the side BNE NO_MOVE_MARIO ; / of an object PHP ; \ REP #%00100000 ; | move mario LDA SPRXTMP ; | with the SEC ; | sprite SBC SPRXTMP2 ; | CLC ; | ADC $94 ; | STA $94 ; | ... and also REP #%00100000 ; | move mario LDA SPRYTMP ; | with the bird SEC ; | on the Y SBC SPRYTMP2 ; | axis CLC ; | ADC $96 ; | STA $96 ; | PLP ; / NO_MOVE_MARIO STZ STANDINGLASTFRAME,x ; zero this in case it won't be set this frame NOT_STANDING_LAST_FRAME LDA $7D ; \ don't stand on if BMI NO_STAND ; / mario not moving down PHP ; back up processor bits REP #%00100000 ; set 16 bit A/math LDY #$00 ; Y register = 0 LDA SPRYTMP ; get sprite's Y position CLC ; \ offset to get minimum ADC.w #STAND_YPOS-1 ; / Y area for standing CMP $96 ; compare with mario's Y position BCS NO_STAND_1 ; don't execute next command if area is under mario LDY #$01 ; set Y register = 1 NO_STAND_1 PLP ; load backed up processor bits CPY #$00 ; \ if Y is not set BEQ NO_STAND ; / then don't stand PHP ; back up processor bits REP #%00100000 ; set 16 bit A/math LDY #$00 ; Y register = 0 LDA SPRYTMP ; get sprite's Y position CLC ; \ offset to get maximum ADC.w #STAND_YPOS+5 ; / Y area for standing CMP $96 ; compare with mario's Y position BCC NO_STAND_2 ; don't execute next command if area is over mario LDY #$01 ; set Y register = 1 NO_STAND_2 PLP ; load backed up processor bits CPY #$00 ; \ if Y is not set BEQ NO_STAND ; / then don't stand PHP ; back up processor bits REP #%00100000 ; 16 bit A/math LDY #$00 ; Y register = 0 LDA SPRXTMP ; get sprite's X position CLC ; \ offset to get minimum ADC.w #STAND_XMIN ; / X area for standing BPL CMP1 ; \ if area goes backward past LDA.w #$0000 ; / level start then assume zero CMP1 CMP $94 ; compare with mario's X position BCS NO_STAND_3 ; don't execute next command if area is after mario LDY #$01 ; set Y register = 1 NO_STAND_3 PLP ; load backed up processor bits CPY #$00 ; \ if Y is not set BEQ NO_STAND ; / then don't stand PHP ; back up processor bits REP #%00100000 ; set 16 bit A/math LDY #$00 ; Y register = 0 LDA SPRXTMP ; get sprite's X position CLC ; \ offset to get maximum ADC.w #STAND_XMAX ; / X area for standing BPL CMP2 ; \ if X area goes backward past LDA.w #$0000 ; / level start then assume zero CMP2 CMP $94 ; compare with mario's X position BCC NO_STAND_4 ; don't execute next command if area is before mario LDY #$01 ; set Y register = 1 NO_STAND_4 PLP ; load backed up processor bits CPY #$00 ; \ if Y is not set BEQ NO_STAND ; / then don't stand PHP ; \ REP #%00100000 ; | offset mario's LDA SPRYTMP ; | Y position so CLC ; | that he is ADC.w #STAND_YPOS ; | standing at STA $96 ; | specified offset PLP ; / LDA #$01 ; \ set standing STA $1471 ; / mode LDA #$01 ; \ for the next frame, indicate mario STA STANDINGLASTFRAME,x ; / was standing during this frame NO_STAND LDA $187A ; \ don't shift BEQ NOYOSHI2 ; / if not on Yoshi LDA $96 ; \ reverse SEC ; | offset Y SBC #$10 ; | by #$10 STA $96 ; | again to LDA $97 ; | compensate SBC #$00 ; | for yoshi STA $97 ; / NOYOSHI2 RTS ;; This temporarily offsets mario's and the sprite's ;; Y positions so the lift doesn't have a glitch ;; when it's at the top of the level PERCEPTIONOFFSET = $FF POSOFFSETSTART LDA $96 ; \ CLC ; | add specified ADC #PERCEPTIONOFFSET ; | offset to STA $96 ; | mario LDA $97 ; | ADC #$00 ; | STA $97 ; / LDA $D8,x ; \ CLC ; | add specified ADC #PERCEPTIONOFFSET ; | offset to STA $D8,x ; | sprite LDA $14D4,x ; | ADC #$00 ; | STA $14D4,x ; / RTS POSOFFSETEND LDA $96 ; \ SEC ; | subtract SBC #PERCEPTIONOFFSET ; | specified STA $96 ; | offset from LDA $97 ; | mario SBC #$00 ; | STA $97 ; / LDA $D8,x ; \ SEC ; | subtract SBC #PERCEPTIONOFFSET ; | specified STA $D8,x ; | offset from LDA $14D4,x ; | sprite SBC #$00 ; | STA $14D4,x ; / RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; GRAPHICS ROUTINE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; FLYFRAMES = $24 ; amount in the "wingindexes" table (not including glide frame) HEADFRAMES = $0C ; amount in the "headindexes" table TOTAL_WING_OFFSET = $03 ADDRTEMP = $04 FRAMETMP = $06 FLIP_TMP = $07 FLIP_TMP_2 = $08 TILESDRAWN = $09 PROP_TMP = $0A COUNT_TMP = $0B DIRWINGOFFSETS dcb $FD,$03 WINGINDEXES dcb $00,$00,$00,$00 dcb $02,$02,$02,$02 dcb $04,$04,$04,$04 dcb $06,$06,$06,$06 dcb $08,$08,$08,$08 dcb $0A,$0A,$0A,$0A dcb $0C,$0C,$0C,$0C dcb $0E,$0E,$0E,$0E dcb $10,$10,$10,$10 dcb $12 WCOUNT dcb $02,$02,$02,$01,$01,$02,$02,$01,$01,$02 WTILES dcw WTILES1&$FFFF,WTILES2&$FFFF,WTILES3&$FFFF dcw WTILES4&$FFFF,WTILES5&$FFFF,WTILES6&$FFFF dcw WTILES7&$FFFF,WTILES8&$FFFF,WTILES9&$FFFF dcw WTILES10&$FFFF WXDISP dcw WXDISP1&$FFFF,WXDISP2&$FFFF,WXDISP3&$FFFF dcw WXDISP4&$FFFF,WXDISP5&$FFFF,WXDISP6&$FFFF dcw WXDISP7&$FFFF,WXDISP8&$FFFF,WXDISP9&$FFFF dcw WXDISP10&$FFFF WYDISP dcw WYDISP1&$FFFF,WYDISP2&$FFFF,WYDISP3&$FFFF dcw WYDISP4&$FFFF,WYDISP5&$FFFF,WYDISP6&$FFFF dcw WYDISP7&$FFFF,WYDISP8&$FFFF,WYDISP9&$FFFF dcw WYDISP10&$FFFF WSIZES dcw WSIZES1&$FFFF,WSIZES2&$FFFF,WSIZES3&$FFFF dcw WSIZES4&$FFFF,WSIZES5&$FFFF,WSIZES6&$FFFF dcw WSIZES7&$FFFF,WSIZES8&$FFFF,WSIZES9&$FFFF dcw WSIZES10&$FFFF WPROPS dcw WPROPS1&$FFFF,WPROPS2&$FFFF,WPROPS3&$FFFF dcw WPROPS4&$FFFF,WPROPS5&$FFFF,WPROPS6&$FFFF dcw WPROPS7&$FFFF,WPROPS8&$FFFF,WPROPS9&$FFFF dcw WPROPS10&$FFFF WTILES1 dcb $16,$26 WXDISP1 dcb $09,$09 WYDISP1 dcb $00,$F0 WSIZES1 dcb $00,$02 WPROPS1 dcb $80,$80 WTILES2 dcb $22,$20 WXDISP2 dcb $08,$08 WYDISP2 dcb $F7,$E7 WSIZES2 dcb $02,$02 WPROPS2 dcb $00,$00 WTILES3 dcb $24,$6B WXDISP3 dcb $09,$0E WYDISP3 dcb $F6,$F1 WSIZES3 dcb $02,$00 WPROPS3 dcb $00,$00 WTILES4 dcb $68 WXDISP4 dcb $0B WYDISP4 dcb $00 WSIZES4 dcb $02 WPROPS4 dcb $00 WTILES5 dcb $6A WXDISP5 dcb $0A WYDISP5 dcb $08 WSIZES5 dcb $02 WPROPS5 dcb $00 WTILES6 dcb $16,$26 WXDISP6 dcb $09,$09 WYDISP6 dcb $08,$10 WSIZES6 dcb $00,$02 WPROPS6 dcb $00,$00 WTILES7 dcb $24,$6B WXDISP7 dcb $09,$0E WYDISP7 dcb $0A,$17 WSIZES7 dcb $02,$00 WPROPS7 dcb $80,$80 WTILES8 dcb $68 WXDISP8 dcb $0B WYDISP8 dcb $00 WSIZES8 dcb $02 WPROPS8 dcb $80 WTILES9 dcb $6A WXDISP9 dcb $0A WYDISP9 dcb $F8 WSIZES9 dcb $02 WPROPS9 dcb $80 WTILES10 dcb $64,$66 WXDISP10 dcb $0C,$1C WYDISP10 dcb $00,$00 WSIZES10 dcb $02,$02 WPROPS10 dcb $00,$00 HEADINDEXES dcb $00,$00,$00,$00 dcb $02,$02,$02,$02 dcb $04,$04,$04,$04 HTILES dcw HTILES1&$FFFF,HTILES2&$FFFF,HTILES3&$FFFF HXDISP dcw HXDISP1&$FFFF,HXDISP2&$FFFF,HXDISP3&$FFFF HYDISP dcw HYDISP1&$FFFF,HYDISP2&$FFFF,HYDISP3&$FFFF HTILES1 dcb $44,$4A HXDISP1 dcb $F8,$08 HYDISP1 dcb $00,$00 HTILES2 dcb $44,$46 HXDISP2 dcb $F8,$08 HYDISP2 dcb $00,$00 HTILES3 dcb $44,$48 HXDISP3 dcb $F8,$08 HYDISP3 dcb $00,$00 SUB_GFX JSR GET_DRAW_INFO ; get info to draw tiles STZ TILESDRAWN ; zero tiles drawn STZ FLIP_TMP ; next wing not flipped JSR DRAW_WING ; draw wing JSR DRAW_HEAD ; draw head/body INC FLIP_TMP ; next wing is flipped JSR DRAW_WING ; draw wing JSR SETTILES ; set tiles / don't draw offscreen ENDSUB RTS DRAW_WING LDA $157C,x ; \ actual flip of this EOR FLIP_TMP ; | wing = sprite's flip STA FLIP_TMP_2 ; / flipped with wing's flip PHY ; \ LDY FLYFRAME,x ; | store index for LDA WINGINDEXES,y ; | frame data to STA FRAMETMP ; | scratch RAM LSR A ; | TAY ; | ... then set LDA WCOUNT,y ; | number of tiles STA COUNT_TMP ; | for this wing PLY ; / PHX ; back up X LDX #$00 ; load X with zero TILELP CPX COUNT_TMP ; end of loop? BNE NORETFRML ; \ if so, JMP RETFRML ; / then end NORETFRML PHY ; \ LDY FRAMETMP ; | get X LDA WXDISP,y ; | offset STA ADDRTEMP ; | for this LDA WXDISP+1,y ; | tile and STA ADDRTEMP+1 ; | flip it TXY ; | if the LDA (ADDRTEMP),y ; | wing is PHX ; | flipped LDX $15E9 ; | LDY FLIP_TMP_2 ; | BNE NO_FLIP_X ; | EOR #%11111111 ; | INC A ; | NO_FLIP_X LDY $157C,x ; | CLC ; | ADC DIRWINGOFFSETS,y ; | PLX ; | PLY ; / CLC ; \ add it to sprite's ADC $00 ; / screen X offset STA $0300,y ; set tile's X position PHY ; \ LDY FRAMETMP ; | get Y LDA WYDISP,y ; | offset STA ADDRTEMP ; | for this LDA WYDISP+1,y ; | tile STA ADDRTEMP+1 ; | TXY ; | LDA (ADDRTEMP),y ; | PLY ; / CLC ; \ add it to sprite's ADC $01 ; / screen Y offset STA $0301,y ; set tile's Y position PHY ; \ LDY FRAMETMP ; | get LDA WTILES,y ; | tile STA ADDRTEMP ; | number LDA WTILES+1,y ; | STA ADDRTEMP+1 ; | TXY ; | LDA (ADDRTEMP),y ; | PLY ; / STA $0302,y ; set tile # PHY ; \ LDY FRAMETMP ; | get LDA WSIZES,y ; | tile STA ADDRTEMP ; | size LDA WSIZES+1,y ; | STA ADDRTEMP+1 ; | TXY ; | LDA (ADDRTEMP),y ; | PLY ; / PHX ; back up X PHA ; \ TYA ; | get index to LSR A ; | extra tile LSR A ; | properties TAX ; | PLA ; / STA $0460,x ; set tile size PLX ; load backed up X CMP #$02 ; \ BEQ NO_ADD_8 ; | if flipped LDA FLIP_TMP_2 ; | and it is BNE NO_ADD_8 ; | 8x8, add LDA $0300,y ; | 8 to its CLC ; | X position ADC #$08 ; | STA $0300,y ; / NO_ADD_8 PHY ; \ LDY FRAMETMP ; | get Y LDA WPROPS,y ; | offset STA ADDRTEMP ; | for this LDA WPROPS+1,y ; | tile STA ADDRTEMP+1 ; | TXY ; | LDA (ADDRTEMP),y ; | PLY ; / STA PROP_TMP PHX ; back up X (index to tile data) LDX $15E9 ; load X with index to sprite LDA $15F6,x ; load palette info PHY ; \ LDY FLIP_TMP_2 ; | flip the tile BNE NO_FLIP_X_2 ; | if the sprite ORA #%01000000 ; | is flipped NO_FLIP_X_2 PLY ; / PLX ; load backed up X ORA PROP_TMP ORA $64 ; add in priority bits STA $0303,y ; set extra info INY ; \ INY ; | index to next slot INY ; | INY ; / INX ; next tile to draw INC TILESDRAWN ; another tile was drawn JMP TILELP ; loop RETFRML PLX ; load backed up X RTS DRAW_HEAD LDA $157C,x ; \ flip for the head/body is STA FLIP_TMP_2 ; / the sprite's flip setting PHY ; \ LDY HEADFRAME,x ; | store index for LDA HEADINDEXES,y ; | frame data to STA FRAMETMP ; | scratch RAM PLY ; / PHX ; back up X LDX #$00 ; load X with zero TILELP2 CPX #$02 ; end of loop? BNE NORETFRML2 ; if so, then end JMP RETFRML2 NORETFRML2 PHY ; \ LDY FRAMETMP ; | get X LDA HXDISP,y ; | offset STA ADDRTEMP ; | for this LDA HXDISP+1,y ; | tile and STA ADDRTEMP+1 ; | flip it TXY ; | if the LDA (ADDRTEMP),y ; | wing is PHX ; | flipped LDX $15E9 ; | LDY FLIP_TMP_2 ; | BNE NO_FLIP_X_3 ; | EOR #%11111111 ; | INC A ; | NO_FLIP_X_3 PLX ; | PLY ; / CLC ; \ add it to sprite's ADC $00 ; / screen X offset STA $0300,y ; set tile's X position PHY ; \ LDY FRAMETMP ; | get Y LDA HYDISP,y ; | offset STA ADDRTEMP ; | for this LDA HYDISP+1,y ; | tile STA ADDRTEMP+1 ; | TXY ; | LDA (ADDRTEMP),y ; | PLY ; / CLC ; \ add it to sprite's ADC $01 ; / screen Y offset STA $0301,y ; set tile's Y position PHY ; \ LDY FRAMETMP ; | get LDA HTILES,y ; | tile STA ADDRTEMP ; | number LDA HTILES+1,y ; | STA ADDRTEMP+1 ; | TXY ; | LDA (ADDRTEMP),y ; | PLY ; / STA $0302,y ; set tile # PHX ; back up X TYA ; \ get index to LSR A ; | extra tile LSR A ; | properties TAX ; / LDA #$02 ; #$02 means size = 16x16 STA $0460,x ; set tile size LDX $15E9 ; load X with index to sprite LDA $15F6,x ; load palette info PHY ; \ LDY FLIP_TMP_2 ; | flip the tile BNE NO_FLIP_X_4 ; | if the sprite ORA #%01000000 ; | is flipped NO_FLIP_X_4 PLY ; / PLX ; load backed up X ORA $64 ; add in priority bits STA $0303,y ; set extra info INY ; \ INY ; | index to next slot INY ; | INY ; / INX ; next tile to draw INC TILESDRAWN ; another tile was drawn JMP TILELP2 ; loop RETFRML2 PLX ; load backed up X RTS SETTILES LDA TILESDRAWN ; \ don't do it BEQ NODRAW ; / if no tiles LDY #$FF ; #$FF means don't define sizes here DEC A ; A = # tiles - 1 JSL $01B7B3 ; don't draw if offscreen NODRAW RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ROUTINES FROM THE LIBRARY ARE PASTED BELOW ; You should never have to modify this code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 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 ;A:8A00 X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdiZcHC:0756 VC:176 00 FL:205 AND #$01 ;A:8A01 X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdizcHC:0780 VC:176 00 FL:205 ORA $03 ;A:8A01 X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdizcHC:0796 VC:176 00 FL:205 STA $01 ;A:8A01 X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdizcHC:0820 VC:176 00 FL:205 TAY ;A:8A01 X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdizcHC:0844 VC:176 00 FL:205 LDA $1A ;A:8A01 X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdizcHC:0858 VC:176 00 FL:205 CLC ;A:8A00 X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdiZcHC:0882 VC:176 00 FL:205 ADC SPR_T14,y ;A:8A00 X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdiZcHC:0896 VC:176 00 FL:205 ROL $00 ;A:8AC0 X:0009 Y:0001 D:0000 DB:01 S:01F1 P:eNvMXdizcHC:0928 VC:176 00 FL:205 CMP $E4,x ;A:8AC0 X:0009 Y:0001 D:0000 DB:01 S:01F1 P:eNvMXdizCHC:0966 VC:176 00 FL:205 PHP ;A:8AC0 X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdizCHC:0996 VC:176 00 FL:205 LDA $1B ;A:8AC0 X:0009 Y:0001 D:0000 DB:01 S:01F0 P:envMXdizCHC:1018 VC:176 00 FL:205 LSR $00 ;A:8A00 X:0009 Y:0001 D:0000 DB:01 S:01F0 P:envMXdiZCHC:1042 VC:176 00 FL:205 ADC SPR_T15,y ;A:8A00 X:0009 Y:0001 D:0000 DB:01 S:01F0 P:envMXdizcHC:1080 VC:176 00 FL:205 PLP ;A:8AFF X:0009 Y:0001 D:0000 DB:01 S:01F0 P:eNvMXdizcHC:1112 VC:176 00 FL:205 SBC $14E0,x ;A:8AFF X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdizCHC:1140 VC:176 00 FL:205 STA $00 ;A:8AFF X:0009 Y:0001 D:0000 DB:01 S:01F1 P:eNvMXdizCHC:1172 VC:176 00 FL:205 LSR $01 ;A:8AFF X:0009 Y:0001 D:0000 DB:01 S:01F1 P:eNvMXdizCHC:1196 VC:176 00 FL:205 BCC SPR_L31 ;A:8AFF X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdiZCHC:1234 VC:176 00 FL:205 EOR #$80 ;A:8AFF X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdiZCHC:1250 VC:176 00 FL:205 STA $00 ;A:8A7F X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdizCHC:1266 VC:176 00 FL:205 SPR_L31 LDA $00 ;A:8A7F X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdizCHC:1290 VC:176 00 FL:205 BPL RETURN_35 ;A:8A7F X:0009 Y:0001 D:0000 DB:01 S:01F1 P:envMXdizCHC:1314 VC:176 00 FL:205 ERASE_SPRITE LDA $14C8,x ; \ if sprite status < 8, permanently erase sprite CMP #$08 ; | BCC KILL_SPRITE ; / LDY $161A,x ;A:FF08 X:0007 Y:0001 D:0000 DB:01 S:01F3 P:envMXdiZCHC:1108 VC:059 00 FL:2878 CPY #$FF ;A:FF08 X:0007 Y:0000 D:0000 DB:01 S:01F3 P:envMXdiZCHC:1140 VC:059 00 FL:2878 BEQ KILL_SPRITE ;A:FF08 X:0007 Y:0000 D:0000 DB:01 S:01F3 P:envMXdizcHC:1156 VC:059 00 FL:2878 LDA #$00 ;A:FF08 X:0007 Y:0000 D:0000 DB:01 S:01F3 P:envMXdizcHC:1172 VC:059 00 FL:2878 STA $1938,y ;A:FF00 X:0007 Y:0000 D:0000 DB:01 S:01F3 P:envMXdiZcHC:1188 VC:059 00 FL:2878 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 ;A:0000 X:0009 Y:00E4 D:0000 DB:01 S:01F3 P:eNvMXdizcHC:1218 VC:250 00 FL:5379 LSR A ;A:0016 X:0009 Y:00E4 D:0000 DB:01 S:01F3 P:envMXdizcHC:1242 VC:250 00 FL:5379 AND #$01 ;A:000B X:0009 Y:00E4 D:0000 DB:01 S:01F3 P:envMXdizcHC:1256 VC:250 00 FL:5379 STA $01 ;A:0001 X:0009 Y:00E4 D:0000 DB:01 S:01F3 P:envMXdizcHC:1272 VC:250 00 FL:5379 TAY ;A:0001 X:0009 Y:00E4 D:0000 DB:01 S:01F3 P:envMXdizcHC:1296 VC:250 00 FL:5379 LDA $1C ;A:001A X:0009 Y:0001 D:0000 DB:01 S:01F3 P:eNvMXdizcHC:0052 VC:251 00 FL:5379 CLC ;A:00BD X:0009 Y:0001 D:0000 DB:01 S:01F3 P:eNvMXdizcHC:0076 VC:251 00 FL:5379 ADC SPR_T12,y ;A:00BD X:0009 Y:0001 D:0000 DB:01 S:01F3 P:eNvMXdizcHC:0090 VC:251 00 FL:5379 ROL $00 ;A:006D X:0009 Y:0001 D:0000 DB:01 S:01F3 P:enVMXdizCHC:0122 VC:251 00 FL:5379 CMP $D8,x ;A:006D X:0009 Y:0001 D:0000 DB:01 S:01F3 P:eNVMXdizcHC:0160 VC:251 00 FL:5379 PHP ;A:006D X:0009 Y:0001 D:0000 DB:01 S:01F3 P:eNVMXdizcHC:0190 VC:251 00 FL:5379 LDA.W $001D ;A:006D X:0009 Y:0001 D:0000 DB:01 S:01F2 P:eNVMXdizcHC:0212 VC:251 00 FL:5379 LSR $00 ;A:0000 X:0009 Y:0001 D:0000 DB:01 S:01F2 P:enVMXdiZcHC:0244 VC:251 00 FL:5379 ADC SPR_T13,y ;A:0000 X:0009 Y:0001 D:0000 DB:01 S:01F2 P:enVMXdizCHC:0282 VC:251 00 FL:5379 PLP ;A:0000 X:0009 Y:0001 D:0000 DB:01 S:01F2 P:envMXdiZCHC:0314 VC:251 00 FL:5379 SBC $14D4,x ;A:0000 X:0009 Y:0001 D:0000 DB:01 S:01F3 P:eNVMXdizcHC:0342 VC:251 00 FL:5379 STA $00 ;A:00FF X:0009 Y:0001 D:0000 DB:01 S:01F3 P:eNvMXdizcHC:0374 VC:251 00 FL:5379 LDY $01 ;A:00FF X:0009 Y:0001 D:0000 DB:01 S:01F3 P:eNvMXdizcHC:0398 VC:251 00 FL:5379 BEQ SPR_L38 ;A:00FF X:0009 Y:0001 D:0000 DB:01 S:01F3 P:envMXdizcHC:0422 VC:251 00 FL:5379 EOR #$80 ;A:00FF X:0009 Y:0001 D:0000 DB:01 S:01F3 P:envMXdizcHC:0438 VC:251 00 FL:5379 STA $00 ;A:007F X:0009 Y:0001 D:0000 DB:01 S:01F3 P:envMXdizcHC:0454 VC:251 00 FL:5379 SPR_L38 LDA $00 ;A:007F X:0009 Y:0001 D:0000 DB:01 S:01F3 P:envMXdizcHC:0478 VC:251 00 FL:5379 BPL RETURN_35 ;A:007F X:0009 Y:0001 D:0000 DB:01 S:01F3 P:envMXdizcHC:0502 VC:251 00 FL:5379 BMI ERASE_SPRITE ;A:8AFF X:0002 Y:0000 D:0000 DB:01 S:01F3 P:eNvMXdizcHC:0704 VC:184 00 FL:5490 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 ;A:25D0 X:0006 Y:0001 D:0000 DB:03 S:01ED P:eNvMXdizCHC:1020 VC:097 00 FL:31642 LDA $94 ;A:25D0 X:0006 Y:0000 D:0000 DB:03 S:01ED P:envMXdiZCHC:1036 VC:097 00 FL:31642 SEC ;A:25F0 X:0006 Y:0000 D:0000 DB:03 S:01ED P:eNvMXdizCHC:1060 VC:097 00 FL:31642 SBC $E4,x ;A:25F0 X:0006 Y:0000 D:0000 DB:03 S:01ED P:eNvMXdizCHC:1074 VC:097 00 FL:31642 STA $0F ;A:25F4 X:0006 Y:0000 D:0000 DB:03 S:01ED P:eNvMXdizcHC:1104 VC:097 00 FL:31642 LDA $95 ;A:25F4 X:0006 Y:0000 D:0000 DB:03 S:01ED P:eNvMXdizcHC:1128 VC:097 00 FL:31642 SBC $14E0,x ;A:2500 X:0006 Y:0000 D:0000 DB:03 S:01ED P:envMXdiZcHC:1152 VC:097 00 FL:31642 BPL SPR_L16 ;A:25FF X:0006 Y:0000 D:0000 DB:03 S:01ED P:eNvMXdizcHC:1184 VC:097 00 FL:31642 INY ;A:25FF X:0006 Y:0000 D:0000 DB:03 S:01ED P:eNvMXdizcHC:1200 VC:097 00 FL:31642 SPR_L16 RTS ;A:25FF X:0006 Y:0001 D:0000 DB:03 S:01ED P:envMXdizcHC:1214 VC:097 00 FL:31642