;; This tutorial teaches you, through code, how to get the ;; tile collision type index based on any arbitrary x- and ;; y-position on screen. ;; ;; First, we'll write a subroutine that converts the x- and ;; y-value of any position you want, to the collision type ;; of the tile that's in that position. Note that this ;; subroutine will trash the value of the y-register, so if ;; you need that value after this routine, you'll have to ;; temporarily store it elsewhere, for example by pusing it ;; on the stack and pulling it back after. When this routine ;; is done, the accumulator holds the value of the collision ;; type index. ;; sub_getCollisionType: ;; Check if the currently active screen is odd or even. ;; We do this by loading the current screen number, then ;; unset all bits but the last one. The resulting value is ;; 0 if even, or 1 if odd. We store this value in the temp ;; variable because we'll possibly need to modify this ;; value later. LDA currentNametable AND #%00000001 STA temp ;; Add the horizontal camera offset to the x-position ;; you'd like to know the collision type of. For non- ;; scrolling modules, the camera offset is always 0, ;; so this will still work if you're using a non-scrolling ;; module, although you can remove lines 28-54 if you ;; want to shave some bytes and cycles off your game. LDA tempx CLC ADC camX STA tempx ;; If this sets the carry, we're actually on the other ;; screen, so we need to adjust the current nametable ;; accordingly. BCC + ;; Modifying this value is done with the EOR (or the ;; bitwise exclusive-or) operation. EOR compares bits of ;; two bytes and sets the resulting bits to 1 only if ;; EXACTLY one of the compared bits are 1. If both bits ;; have the same value (either 0 or 1), the resulting ;; bit wil be reset to 0. More info on EOR: ;; https://en.wikipedia.org/wiki/Exclusive_or LDA temp EOR #$01 STA temp + ;; We don't have to correct the y-position since vertical ;; scrolling is not a thing in the default module. If you ;; have vertical, or even 4-way scrolling in your game, ;; you probably won't need this tutorial anyway. ;; Convert the x- and y-position to the correct tile offset ;; in RAM. This offset can be defined as follows: ;; ;; %YYYY XXXX ;; |||| ++++-- the x-position (in metatiles, 0-15) ;; ++++------- the y-position (in metatiles, 0-15) ;; ;; To get this offset, we'll shift the value of the x-value ;; to the right four times (essentially dividing by 16), ;; then superimpose that value on the high nybble of the ;; y-value (which we can get by setting the lower nybble, ;; or rightmost four bits, of the y-value to zero). ;; ;; NOTE: this is what the GetTileAtPosition subroutine does ;; as well, although that assumes the x- and y-values ;; to be stored in tileX and tileY respectively. ;; Round the y-value down to 16's by setting the lower nybble ;; to zero. LDA tempy ; %YYYYyyyy AND #%11110000 ; %11110000 STA tempy ; %YYYY0000 ;; Divide the x-value by 16, by shifting its value right ;; four times. LDA tempx ; %XXXXxxxx LSR ; %0XXXXxxx LSR ; %00XXXXxx LSR ; %000XXXXx LSR ; %0000XXXX ;; Add these together to get the %YYYYXXXX value of the ;; position you want to check. We use the ORA operation, ;; which does this: ;; ;; %0000XXXX - the value of the accumulator ;; ORA %YYYY0000 - the value of tempy ;; ------------ ;; %YYYYXXXX - the result ORA tempy ;; Transfer the result to the y-register, so that we can ;; use it as an offset to retrieve the correct value from ;; the collision table. TAY ;; Based on if this tile is on an odd or even screen, we'll ;; need to read from collision table 1 or 2. LDA temp BEQ +evenScreen ;; We're on an odd screen, so we need to return the ;; collision type value that is in table 2. Since we're ;; done after this step, we can return to where we came ;; from. LDA collisionTable2,y RTS +evenScreen: ;; We're on an even screen, so we need to return the collision ;; type value that is in table 1. LDA collisionTable,y ;; The accumulator now holds the value we're looking for, so ;; we can return to from where we called this routine. RTS ;; Here's an additional macro that can help with calling the ;; subroutine without trashing any registers (other than the ;; accumulator, but that's not really supposed to hold anything ;; important long-term anyway). ;; ;; Example usage: ;; ;; GetCollisionTypeAt #$80, #$80 ;; ; gets the collision type of the tile at position (128,128) ;; ; on screen. ;; MACRO GetCollisionTypeAt arg_x, arg_y ;; Because the subroutine trashes the value of the y-register, ;; we push that onto the stack, so we can retrieve it later. TYA PHA ;; We now put the argument into temp variables for use with ;; the subroutine. LDA arg_x STA tempx LDA arg_y STA tempy ;; We run the subroutine and store the result in the tempx ;; variable -- this is an arbitrary choice and can be any ;; other variable if you so desire. JSR sub_getCollisionType STA tempx ;; Finally, we retrieve the original value of the y-register ;; so we can still use that in case we need it. PLA TAY ENDM