

collision_routines.asm
CheckMoveLeft
This subroutine checks to see if a tile is to the left of the Player. There are two return statuses here:Return Status
If Return = 1 Then a tile is to the left and that way will be blocked.
If Return = 0 Then a tile is not to the left and the Player can move that way.
CheckMoveLeft
;===========================================================================================
;
;
; X = sprite we want to check for
;
; Returns A = 0 we can move or A = 1 we are blocked
;
; Modifies A, Y, PARAM1, PARAM2
; X register is left intact
;------------------------------------------------------------------------------------------
lda SPRITE_CHAR_POS_X,x ; PARAM1 will hold the adjusted X position sta PARAM1 lda SPRITE_CHAR_POS_Y,x ; PARAM2 will hold the adjusted Y position sta PARAM2 lda SPRITE_POS_X_DELTA,x ; Load X pos delta sec sbc SCROLL_COUNT_X ; adjust the delta for scrolling left/right bpl @noAdjX ; check to see if delta wraps to -1 ($FF) dec PARAM1 ; if the delta wraps, adjust the X char position
@noAdjX
and #%0111 ; mask the delta value back to a 0-7 value cmp #0 ; Check X delta to see if the sprite is 'flush' beq @checkLeft ; if it is, continue lda #0 ; else return with a 'clear' code rts
@checkLeft
lda SPRITE_POS_Y_DELTA,x ; load the sprite delta Y pos
sec ; subtract the current Y scroll
sbc SCROLL_COUNT_Y
bpl @noAdjY ; if it wraps, decrement the Y char position
dec PARAM2
@noAdjY
and #%0111 ; mask back to a 0-7 value
beq @checkLeft2 ; if adjusted delta Y is 0, we only need to check 2
; characters
; If not we need to check 3 characters
ldy PARAM2 ; load adjusted Y char position (screen line)
iny
jsr FetchPlayfieldLineAddress ; Use screen_routines helper to fetch the address
ldy PARAM1 ; fetch sprites X position
dey ; sub 1 character (left)
lda (ZEROPAGE_POINTER_1),y ; fetch the character from screen memory
jsr TestBlocking ; test for a blocking character
bne @blockedLeft
tya
clc
adc #40
tay
lda (ZEROPAGE_POINTER_1),y
jsr TestBlocking
bne @blockedLeft
lda #0
rts
@blockedLeft
lda #1
rts
CheckMoveRight
This subroutine checks to see if a tile is to the right of the Player. There are two return statuses here:Return Status
If Return = 1 Then a tile is to the right and that way will be blocked.
If Return = 0 Then a tile is not to the right and the Player can move that way.
CheckMoveRight
; =============================================================================
; X = Sprite to check against
;
; Modifies Y, PARAM1, PARAM2
;
; Returns A = blocking code
; X register is left intact
;------------------------------------------------------------------------------
lda SPRITE_CHAR_POS_X,x ; PARAM1 will hold the adjusted X position
sta PARAM1
lda SPRITE_CHAR_POS_Y,x ; PARAM2 will hold the adjusted Y position
sta PARAM2
lda SPRITE_POS_X_DELTA,x ; Fetch X Delta
sec
sbc SCROLL_COUNT_X ; Subtract the scroll count
bpl @noAdjX
dec PARAM1 ; increment PARAM1
@noAdjX and #%0111 ; mask back to a range of 0-7 ; No adjustment is needed cmp #2 ; is new delta = 0? beq @checkRight ; if so, we can check right lda #0 ; otherwise give a clear code and carry on rts
@checkRight
lda SPRITE_POS_Y_DELTA,x ; Load the Y delta
sec
sbc SCROLL_COUNT_Y ; adjust by the current Y scroll value
bpl @noAdjY ; if the delta goes below 0 adjust Y position
dec PARAM2
@noAdjY
and #%0111 ; mask adjusted delta value back to 0 - 7
; if Y delta is 0, we are flush on the Y axis, so only
beq @rightCheck2 ; check 2 characters
; Otherwise we are overlapping, so we need to check 3
ldy PARAM2 ; Fetch the sprites Y character position
iny ; add 1 (one character line down)
jsr FetchPlayfieldLineAddress ; fetch the address for the start of that screen line
ldy PARAM1 ; fetch the sprites X position and store it in Y
iny ; add 1 character (right)
lda (ZEROPAGE_POINTER_1),y ; fetch the screen character
jsr TestBlocking ; see if it's a blocking character
bne @blockedRight ; return if it is
@rightCheck2 ; check the 2 characters to the right of the sprite ; get the sprite Y character coord ldy PARAM2 dey ; subtract one (one character down) jsr FetchPlayfieldLineAddress ; fetch the address for the start of that line ldy PARAM1 ; Load the adjusted X character position in Y iny ; add one character to the right lda (ZEROPAGE_POINTER_1),y ; fetch the character jsr TestBlocking ; test the character for blocking bne @blockedRight ; exit and return the code if blocking tya ; Add #40 to the current X position clc ; this is the same as going down one block adc #40 tay lda (ZEROPAGE_POINTER_1),y ; load the character to CheckMoveDown jsr TestBlocking ; test it bne @blockedRight ; return the code if blocking lda #0 ; else return with a 'clear' code rts
@blockedRight
lda #1 rts
CheckMoveDown
This subroutine checks to see if a tile is below the Player. There are two return statuses here:Return Status
If Return = 1 Then a tile is underneath the Player and that way will be blocked.
If Return = 0 Then a tile is not underneath and the Player can move that way.
CheckMoveDown
; ============================================================================
; X = sprite we want to check for
;
; Returns : A = 0 we move or A = 1 we are blocked
;
; Modifies : Y,PARAM1, PARAM2
; X is left intact
;-----------------------------------------------------------------------------
lda SPRITE_CHAR_POS_X,x ; Fetch the sprites X character coord sta PARAM1 ; PARAM1 will hold the adjusted Y coord lda SPRITE_CHAR_POS_Y,x ; Fetch the sprites Y character coord sta PARAM2 ; PARAM2 will hold the adjusted Y coord ; Adjust the Y Delta and Pos Y values lda SPRITE_POS_Y_DELTA,x ; load the delta Y sec sbc SCROLL_COUNT_Y ; subtract the Y scroll count bpl @noAdjustY ; if it's less than 0, adjust Y pos dec PARAM2
@noAdjustY
and #%0111 ; mask back to 0-7 value
; if adjusted delta Y is 0, we are flush
beq @downCheck ; and do a character check
lda #0 ; else return a clear code
rts
@downCheck
lda SPRITE_POS_X_DELTA,x ; check the X delta, if it's flush (0) we only
sec
sbc SCROLL_COUNT_X
bpl @noAdjX
dec PARAM1
@noAdjX
and #%0111 ; (mask at 0-7 pixels)
beq @downCheck2 ; need to check 2 characters
ldy PARAM2 ; load the adjusted Y character line
iny ; increment to one line down
iny
iny
jsr FetchPlayfieldLineAddress ; load sprite X char position
ldy PARAM1
iny
; inc X pos (left character)
lda (ZEROPAGE_POINTER_1),y ; fetch the character
jsr TestBlocking
bne @downBlocked
@downCheck2
ldy PARAM2 ; load sprite Y char coord
iny ; increment down one line
iny
iny
jsr FetchPlayfieldLineAddress ;saves data in ZEROPPAGE_POINTER_1
ldy PARAM1
lda (ZEROPAGE_POINTER_1),y
jsr TestBlocking
bne @downBlocked
lda #0
rts @downBlocked
lda #1
rts
CheckMoveUp
This subroutine checks to see if a tile is above the Player. There are two return statuses here:Return Status
If Return = 1 Then a tile is above and the Player can’t move that way.
If Return = 0 Then a tile is not above and the Player can move that way.
CheckMoveUp
; Checks ahead to see if this sprite can move up, or if it's going to be stopped by a blocking
; character
;
; X = sprite we want to check for
;
; returns A = 0 we can move or A = 1 we are blocked
;
; Modifies Y,PARAM1,PARAM2
;---------------------------------------------------------------------------------------------------
lda SPRITE_CHAR_POS_X,x ; load sprites X character pos sta PARAM1 ; adjusted X char pos will be in PARAM1 lda SPRITE_CHAR_POS_Y,x ; adjusted Y character pos sta PARAM2 ; load sprites y character pos lda SPRITE_POS_Y_DELTA,x ; load sprites Y delta sec ; subtract Y scroll value sbc SCROLL_COUNT_Y bpl @noAdjY ; if it wraps past 0 - adjust Y char pos dec PARAM2
@noAdjY
and #%0111 ; mask back to a value of 0–7
beq @checkUp ; if it’s 0 – we are flush and do a check
lda #0 ; otherwise return with a ‘clear’ code
rts
@checkUp lda SPRITE_POS_X_DELTA,x ; load the sprite X delta value sec sbc SCROLL_COUNT_X ; subtract current scroll X value bpl @noAdjX ; if it wraps past 0 - adjust the X char pos dec PARAM1
@noAdjX
and #%0111 ; mask back to a 0-7 value beq @checkUp2 ; if we are not flush we need to check 2 characters ldy PARAM2 ; fetch the adjusted sprite Y char coord dey ; subtract 2 lines (up) dey dey jsr FetchPlayfieldLineAddress ldy PARAM1 ; load adjusted X character pos iny ; inc X by 1 (one char right) lda (ZEROPAGE_POINTER_1),y jsr TestBlocking bne @upBlocked
@checkUp2
ldy PARAM2 ; load the adjusted sprite char Y position
dey ; decrement (go up) by 2 lines
dey
jsr FetchPlayfieldLineAddress
ldy PARAM1 ; load adjusted X character position
lda (ZEROPAGE_POINTER_1),y
jsr TestBlocking
bne @upBlocked
lda #0
rts
@upBlocked
lda #1
rts
CheckBlockUnder
This subroutine checks to see if a tile is underneath the Player. Useful when falling or object checkingReturn Status
If Return = 1 Then a tile is underneath the Player and that way will be blocked.
If Return = 0 Then a tile is not underneath and the Player can move that way.
CheckBlockUnder
;===========================================================================================
; CHECK BLOCK UNDER
;-------------------------------------------------------------------------------------------
; Check the block under the sprite for collision (usefull for things like ropes and chests)
;-------------------------------------------------------------------------------------------
lda SPRITE_CHAR_POS_X,x ; PARAM1 will hold the adjusted X position sta PARAM1 lda SPRITE_CHAR_POS_Y,x ; SPRITE_STACK_Y sta PARAM2 ; PARAM2 will hold the adjusted Y position lda SPRITE_POS_X_DELTA ; Fetch X delta sec sbc SCROLL_COUNT_X ; subtract the scroll count clc adc #3 bpl @noAdjX ; if it's 0 or greater, don't adjust X position dec PARAM1 ; otherwise subtract 1 @noAdjX and #%0111 ; Mask delta back to a value between 0-7 beq @adjY ; we are flush on the X axis - do check on Y ; Otherwise ?? I could do another set of lookups ; based on 'non flush'
@adjY
lda SPRITE_POS_Y_DELTA
; Fetch the Y delta
sec
sbc SCROLL_COUNT_Y
bpl @noAdjY
dec PARAM2
@noAdjY
and #%0111 ; if we are flush on Y we only need to check 2 characters
beq @underCheck2 ; otherwise we need to check 3
@underCheck
; PARAM2 - points to where the SCREEN MEMORY will be grabbing the Y register at.
; So for example: If PARAM2 = 2 then the subroutine FetchPlayfieldLineAddress
; will be then pointing to SCREEN2_LINE_OFFSET_TABLE_LO,y which uses the table exactly at
; SCREEN2_LINE_OFFSET_TABLE_LO which reads as <byte SCREEN2_MEM + X, y
; Therefore it translate to point to byte 40 (since this is 2nd on the list.)
; Then 40 would be saved in ZERO_PAGE_POINTER_1 & ZERO_PAGE_POINTER_1 + 1
ldy PARAM2 ; fetch the adjusted Y position
iny ; add one line (down) - SPRITE_STACK_Y, y
jsr FetchPlayfieldLineAddress ; Fetch the address for the start of the screen line
; returned in ZEROPAGE_POINTER_1
ldy PARAM1 ; fetch the sprites adjusted X character position
lda (ZEROPAGE_POINTER_1),y ; fetch the screen character
jsr TestBlocking
bne @blockingUnder ; exit if its a blocking char (shouldn't be)
; TO-DO exit if it's a special character to keep
; our 'last test' intact
lda COLLIDER_ATTR
bne @special_under ; A special block is under us - exit
@undercheck2 ldy PARAM2 ; fetch the adjusted Y position dey ; one line up jsr FetchPlayfieldLineAddress ; Fetch the address for the start of the screen line ; returned in ZEROPAGE_POINTER_1 ldy PARAM1 ; fetch the sprites adjusted X character position lda (ZEROPAGE_POINTER_1),y ; screen data at SCREEN_LINE_OFFSET_TABLE_LO (screen memory) jsr TestBlocking bne @blockingUnder lda COLLIDER_ATTR bne @special_under tya clc adc #40 tay lda (ZEROPAGE_POINTER_1),y bne @blockingUnder lda COLLIDER_ATTR bne @special_under lda #0 rts
@blockingUnder
lda #1 rts
@special_under
; return the 'special - not 1 or 2' rts
;===================================================================================================
; TEST CHARACTER FOR BLOCKING
;===================================================================================================
; Originally we had a simple check for blocking characters > 128. Using Charpad
; we can test for attributes we encode in the upper half byte of the color info
; Note : it seems you can't read this back directly from color ram, but we can
; look it up easily enough given the character number.
;
;
;
; A = Character number we're checking against
; Returns: A = 0 or 1 (clear - blocked) and stores the collison attribute so we can test against it
;
; Modifies A
; Restores X, Y
;---------------------------------------------------------------------------------------------------
; COLLIDER CODES
;---------------------------------------------------------------------------------------------------
COLL_CLEAR = $
00 COLL_FLOOR = $10 COLL_STAIR = $20 COLL_ROPE = $30 COLL_WATER = $40 COLL_OBJ = $50
TestBlocking
This subroutine checks to see what tile was blocking the Player or it returns an empty valueReturn Status
If Return = 1 Then the tile character is returned that the Player bumped into.
If Return = 0 Then no tile was found near the Player.
TestBlocking
sta COLLIDER_ATTR ; save the info passed to us, we need to use A txa ; store X and Y on the stack pha ; the routines we go back to need these intact tya pha ; NEW - 2/1/20 lda #0 ldx COLLIDER_ATTR ; load the character number in X lda ATTRIBUTE_MEM,x ; fetch the attribute and #%11110000 ; mask the color info - leaving the attribute sta COLLIDER_ATTR ; store it so the rest of the program can use it beq @returnClear ; 0 is always clear - so return cmp #COLL_ROPE beq @returnClear ; Ropes ($30) don't block cmp #COLL_WATER beq @returnClear bne @blocking ; Blocking character (COMMENT OUT TO PREVENT BLOCKING)
@returnClear pla ; restore x and y off the stack tay pla tax lda #0 ; return 0 - A clear code sta TESTBLOCK rts
@blocking
; ATM only basic collide info - if not 0, we're blocking pla ; restore X and Y from the stack sta TESTBLOCK tay pla tax lda #1 ; set to blocking and return rts
COLLIDER_ATTR
; A place to store our attribute, and read it later if needed byte 0 TESTBLOCK byte 0 ; TestBlocking test TILE_DATA byte 0 SPRITE_Y_ADJUST byte 0
[…] SetupLevel SetupCollision RoutinesGeneral RoutinesPlayer SetupRaster RoutinesButton TextButton TextButton […]