Skip to navigation

Drawing the landscape: GetTileVisibility

Name: GetTileVisibility [Show more] Type: Subroutine Category: Drawing the landscape Summary: For each tile in the landscape, calculate whether the player can see that tile, to speed up the process of drawing the landscape
Context: See this subroutine in context in the source code References: This subroutine is called as follows: * MainGameLoop calls GetTileVisibility

This routine sets a single bit in the tileVisibility table that records the visibility of that tile from the player's perspective (where 1 = visible, 0 = hidden). The position of the bit within the tileVisibility table is calculated in a rather complicated manner, presumably to make it harder to follow what's going on.
.GetTileVisibility LDA hyperspaceEndsGame \ If bit 7 of hyperspaceEndsGame is set then the game BMI tvis5 \ has ended because of a hyperspace, so jump to tvis5 \ to return from the subroutine \ \ This check appears to be unnecessary, as we only call \ GetTileVisibility from game2 in MainGameLoop, and we \ only reach game2 when bit 7 of hyperspaceEndsGame is \ clear, so this BMI will never be taken LDA #0 \ Set A = 0 to use when we zero the tileVisibility table STA drawingTableOffset \ Set drawingTableOffset = 0 so the call to first call \ to GetRowVisibility (for tile row 31) will populate \ the table at oddVisibility with the tile visibilities \ of the row being analysed LDX #127 \ We now zero the tileVisibility table, so set a counter \ in X for zeroing 128 bytes .tvis1 STA tileVisibility,X \ Zero the X-th byte of the tileVisibility table to \ indicate hidden tiles DEX \ Decrement the byte counter BPL tvis1 \ Loop back until we have zeroed both variables JSR GetTileAltitudes \ Calculate tile corner altitudes and maximum tile \ corner altitudes for each tile in the landscape \ \ This also sets the following: \ \ * (Q P) = &6000 \ \ * (S R) = &6020 \ \ So we have P = 0 and R = &20, ready to pass to the \ GetRowVisibility routine (which expects these values) LDA #31 \ Set zTileRow = 31 so the call to GetRowVisibility STA zTileRow \ analyses the back row of tiles JSR GetRowVisibility \ Calculate whether each tile corner in the rearmost row \ is obscured from the player by any intervening \ landscape, putting the results into 32 entries in the \ table at oddVisibility (as drawingTableOffset = 0) as \ follows: \ \ * %00000000 if the tile corner is not visible from \ the player's position \ \ * %11111111 if the tile corner is visible from the \ player's position DEC zTileRow \ Decrement zTileRow to move forward by one tile row .tvis2 LDA drawingTableOffset \ Flip drawingTableOffset between 0 and 32, so each EOR #32 \ call to GetRowVisibility alternates between storing STA drawingTableOffset \ the tile visibilities in oddVisibility and \ evenVisibility \ \ We EOR with 32 because: \ \ evenVisibility - oddVisibility = 32 \ \ So we can add the result in drawingTableOffset to \ oddVisibility to point to the correct table for \ storing the results JSR GetRowVisibility \ Calculate whether each tile corner in the row at \ zTileRow is obscured from the player by any \ intervening landscape, putting the results into 32 \ entries in the table at either oddVisibility or \ evenVisibility (depending on the parity of zTileRow) \ as follows: \ \ * %00000000 if the tile corner is not visible from \ the player's position \ \ * %11111111 if the tile corner is visible from the \ player's position \ \ So the current row is in either oddVisibility or \ evenVisibility and the previous row is in the other \ \ The call also sets (Q P) to the address of the table \ that contains the altitude and flatness data for tile \ corner row zTileRow, as follows: \ \ (Q P) = &6000 + (zTileRow 0) \ \ Each value in the table at (Q P) contains the flatness \ of the tile in bit 0 and the altitude of the tile \ corner in bits 1-4 LDX playerObject \ Set V to the high byte of the y-coordinate of the LDA yObjectHi,X \ player object STA V LDX #30 \ Set X = 30 to use as the column number, so we start \ iterating from the right, skipping the rightmost \ column as the tile corners in that column do not \ anchor any tiles (so X iterates from 30 to 0 in the \ following loop) .tvis3 TXA \ Set A to the X-th entry from (Q P), which contains the TAY \ altitude and flatness of the X-th tile in the row at LDA (P),Y \ zTileRow LDY #%11111111 \ Set Y to a bit mask containing all set bits, to use \ for flagging tiles as being visible in the calculation \ below LSR A \ Shift bit 0 into the C flag, so it contains the shape, \ and set A as the altitude of the tile BCS tvis4 \ If the tile is not flat then the C flag will be set, \ so jump to tvis4 with Y set to the bit mask for \ visible tiles CMP V \ If A <= V then the tile is at the same altitude or BCC tvis4 \ lower than the high byte of the player object's BEQ tvis4 \ altitude (which is the player's altitude rounded down \ to the nearest integer), so jump to tvis4 with Y set \ to the bit mask for visible tiles \ If we get here then the tile is flat and it is at a \ higher altitude than the player object, so INY \ Increment Y to zero to get a bit mask containing all \ clear bits, to use for flagging tiles as not being \ visible in the calculation below .tvis4 STY W \ Store the bit mask we just calculated in W \ We now store this tile's visibility as a single bit in \ in the tileVisibility table \ \ First we need to calculate the location of this tile's \ bit in the table, which we do by encoding the tile \ column number in X and row number in zTileRow into an \ offset into the table (in T) and a bit mask lookup \ (in Y) \ \ Presumably this calculation is complex to make it \ harder for people to work out what's going on (at \ least, I can't figure out why it's done this way) TXA \ X is the column number of the tile we are analysing, ASL A \ in the range 0 to 30 (%00000 to %11110), so shift that ASL A \ number into the top five bits of A and clear the last ASL A \ two bits, so that bits 5-7 contain bits 2-4 of the AND #%11100000 \ column number ORA zTileRow \ zTileRow is the row number of the tile we are LSR A \ analysing, again in the range 0 to 30 (%00000 to \ %11110), so put this into bits 0 to 4 of A and shift \ the whole thing to the right, so we lose bit 0 of the \ row number into the C flag (which we capture below) \ and bits 0 to 3 contain bits 1 to 4 of zTileRow STA T \ Set T to the value of A, so we now have the following \ in T: \ \ * Bit 7 is clear \ \ * Bits 4-6 contain bits 2-4 of the column number \ \ * Bits 0-3 contain bits 1-4 of the row number \ \ We have now encoded the tile number in T, which we can \ use as an index into the tileVisibility table when \ storing the tile's visibility TXA \ Set bits 0 to 2 of Y as follows: AND #%00000011 \ ROL A \ * Bit 0 contains bit 0 of the of the tile row (via TAY \ the C flag from above) \ \ * Bits 1-2 contain bits 0-1 of the column number LDA visibileBitMask,Y \ Set bitMask to a byte with all bits set except the EOR #%11111111 \ Y-th bit, counting from the left (so when Y is 0, bit STA bitMask \ 7 is clear, when Y is 1, bit 6 is clear and so on) LDA oddVisibility,X \ Set A to the combined visibility of the four tile ORA oddVisibility+1,X \ corners for the tile we are analysing, by OR'ing the ORA evenVisibility,X \ visibility bytes that we set for the four corners in ORA evenVisibility+1,X \ the call to GetRowVisibility: \ \ * oddVisibility+X contains the visibility for the \ tile's anchor (i.e. the X-th corner) \ \ * oddVisibility+X+1 contains the visibility for the \ corner to the right of the tile's anchor \ \ * evenVisibility+X contains the visibility for the \ corner behind the tile's anchor (i.e. the corner \ on the same x-coordinate but from the previous \ row's analysis) \ \ * evenVisibility+X+1 contains the visibility for the \ corner just to the right of the last one \ \ The GetRowVisibility routine sets visibility in the \ oddVisibility and evenVisibility tables as follows: \ \ * %00000000 if the tile corner is not visible \ \ * %11111111 if the tile corner is visible \ \ So OR'ing the four tile corners will give %11111111 if \ any of the tile corners are visible, and %00000000 if \ none of them are visible AND W \ Apply the W bit mask from above, which we set to \ %0000000 for tiles that are flat and above the level \ of the player, and %11111111 otherwise \ \ So this forces A to 0 (to indicate a hidden tile) for \ tiles that are flat and too high to be seen by the \ player, and leaves A alone for other tiles AND visibileBitMask,Y \ At this point A is either %00000000 for a hidden tile \ or %11111111 for a visible tile, so AND'ing it with \ the Y-th entry in visibileBitMask (which is a bit mask \ with all bits set except the Y-th bit, counting from \ the left) will clear all bits except the Y-th bit, \ which will be 0 for non-visible tiles and 1 for \ visible tiles STA U \ Store A in U, so U contains all clear bits apart from \ the Y-th bit, which contains the visibility of the \ tile we are processing LDY T \ Set A to the T-th byte in the tileVisibility table, LDA tileVisibility,Y \ which is where we want to store our visibility bit for \ the tile we are processing AND bitMask \ We set bitMask above to a byte with all bits set \ except the Y-th bit, counting from the left, which \ matches the bit that we used in U to store the tile's \ visibility bit, so this AND clears that bit in the \ byte we just fetched from the tileVisibility table ORA U \ U contains all clear bits apart from the Y-th bit, \ which contains the visibility of the tile we are \ processing, so this inserts that bit into the byte \ we just fetched from the tileVisibility table STA tileVisibility,Y \ Store the updated byte in the tileVisibility table so \ it contains the visibility bit for the tile in column \ X on row zTileRow DEX \ Decrement the column number to move left to the next \ tile in the row BPL tvis3 \ Loop back until we have processed all the tiles in the \ row DEC zTileRow \ Decrement zTileRow to move forward by one tile row BPL tvis2 \ Loop back until we have processed all the tile rows in \ the landscape .tvis5 RTS \ Return from the subroutine