Skip to navigation

Elite on the BBC Micro and NES

Drawing ships: LL9 (Part 5 of 12)

[NES version, Bank 1]

Name: LL9 (Part 5 of 12) [Show more] Type: Subroutine Category: Drawing ships Summary: Draw ship: Calculate the visibility of each of the ship's faces Deep dive: Drawing ships Back-face culling
Context: See this subroutine in context in the source code References: No direct references to this subroutine in this source file
.EE29 LDA (XX0),Y ; We set Y to 12 above before jumping down to EE29, so ; this fetches byte #12 of the ship's blueprint, which ; contains the number of faces * 4 BEQ LL41 ; If there are no faces in this ship, jump to LL42 (via ; LL41) to skip the face visibility calculations STA XX20 ; Set A = the number of faces * 4 LDY #18 ; Fetch byte #18 of the ship's blueprint, which contains LDA (XX0),Y ; the factor by which we scale the face normals, into X TAX LDA XX18+7 ; Set A = z_hi .LL90 TAY ; Set Y = z_hi BEQ LL91 ; If z_hi = 0 then jump to LL91 ; The following is a loop that jumps back to LL90+3, ; i.e. here. LL90 is only used for this loop, so it's a ; bit of a strange use of the label here INX ; Increment the scale factor in X LSR XX18+4 ; Divide (y_hi y_lo) by 2 ROR XX18+3 LSR XX18+1 ; Divide (x_hi x_lo) by 2 ROR XX18 LSR A ; Divide (z_hi z_lo) by 2 (as A contains z_hi) ROR XX18+6 TAY ; Set Y = z_hi BNE LL90+3 ; If Y is non-zero, loop back to LL90+3 to divide the ; three coordinates until z_hi is 0 .LL91 ; By this point z_hi is 0 and X contains the number of ; right shifts we had to do, plus the scale factor from ; the blueprint STX XX17 ; Store the updated scale factor in XX17 LDA XX18+8 ; Set XX15+5 = z_sign STA XX15+5 LDA XX18 ; Set XX15(1 0) = (x_sign x_lo) STA XX15 LDA XX18+2 STA XX15+1 LDA XX18+3 ; Set XX15(3 2) = (y_sign y_lo) STA XX15+2 LDA XX18+5 STA XX15+3 LDA XX18+6 ; Set XX15+4 = z_lo, so now XX15(5 4) = (z_sign z_lo) STA XX15+4 JSR LL51 ; Call LL51 to set XX12 to the dot products of XX15 and ; XX16, which we'll call dot_sidev, dot_roofv and ; dot_nosev: ; ; XX12(1 0) = [x y z] . sidev ; = (dot_sidev_sign dot_sidev_lo) ; = dot_sidev ; ; XX12(3 2) = [x y z] . roofv ; = (dot_roofv_sign dot_roofv_lo) ; = dot_roofv ; ; XX12(5 4) = [x y z] . nosev ; = (dot_nosev_sign dot_nosev_lo) ; = dot_nosev LDA XX12 ; Set XX18(2 0) = dot_sidev STA XX18 LDA XX12+1 STA XX18+2 LDA XX12+2 ; Set XX18(5 3) = dot_roofv STA XX18+3 LDA XX12+3 STA XX18+5 LDA XX12+4 ; Set XX18(8 6) = dot_nosev STA XX18+6 LDA XX12+5 STA XX18+8 LDY #4 ; Fetch byte #4 of the ship's blueprint, which contains LDA (XX0),Y ; the low byte of the offset to the faces data CLC ; Set V = low byte faces offset + XX0 ADC XX0 STA V LDY #17 ; Fetch byte #17 of the ship's blueprint, which contains LDA (XX0),Y ; the high byte of the offset to the faces data ADC XX0+1 ; Set V+1 = high byte faces offset + XX0+1 STA V+1 ; ; So V(1 0) now points to the start of the faces data ; for this ship LDY #0 ; We're now going to loop through all the faces for this ; ship, so set a counter in Y, starting from 0, which we ; will increment by 4 each loop to step through the ; four bytes of data for each face .LL86 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA (V),Y ; Fetch byte #0 for this face into A, so: ; ; A = %xyz vvvvv, where: ; ; * Bits 0-4 = visibility distance, beyond which the ; face is always shown ; ; * Bits 7-5 = the sign bits of normal_x, normal_y ; and normal_z STA XX12+1 ; Store byte #0 in XX12+1, so XX12+1 now has the sign of ; normal_x AND #%00011111 ; Extract bits 0-4 to give the visibility distance CMP XX4 ; If XX4 <= the visibility distance, where XX4 contains BCS LL87 ; the ship's z-distance reduced to 0-31 (which we set in ; part 2), skip to LL87 as this face is close enough ; that we have to test its visibility using the face ; normals ; Otherwise this face is within range and is therefore ; always shown TYA ; Set X = Y / 4 LSR A ; = the number of this face * 4 /4 LSR A ; = the number of this face TAX LDA #255 ; Set the X-th byte of XX2 to 255 to denote that this STA XX2,X ; face is visible TYA ; Set Y = Y + 4 to point to the next face ADC #4 TAY JMP LL88 ; Jump down to LL88 to skip the following, as we don't ; need to test the face normals .LL87 LDA XX12+1 ; Fetch byte #0 for this face into A ASL A ; Shift A left and store it, so XX12+3 now has the sign STA XX12+3 ; of normal_y ASL A ; Shift A left and store it, so XX12+5 now has the sign STA XX12+5 ; of normal_z INY ; Increment Y to point to byte #1 LDA (V),Y ; Fetch byte #1 for this face and store in XX12, so STA XX12 ; XX12 = normal_x INY ; Increment Y to point to byte #2 LDA (V),Y ; Fetch byte #2 for this face and store in XX12+2, so STA XX12+2 ; XX12+2 = normal_y INY ; Increment Y to point to byte #3 LDA (V),Y ; Fetch byte #3 for this face and store in XX12+4, so STA XX12+4 ; XX12+4 = normal_z ; So we now have: ; ; XX12(1 0) = (normal_x_sign normal_x) ; ; XX12(3 2) = (normal_y_sign normal_y) ; ; XX12(5 4) = (normal_z_sign normal_z) LDX XX17 ; If XX17 < 4 then jump to LL92, otherwise we stored a CPX #4 ; larger scale factor above BCC LL92 .LL143 LDA XX18 ; Set XX15(1 0) = XX18(2 0) STA XX15 ; = dot_sidev LDA XX18+2 STA XX15+1 LDA XX18+3 ; Set XX15(3 2) = XX18(5 3) STA XX15+2 ; = dot_roofv LDA XX18+5 STA XX15+3 LDA XX18+6 ; Set XX15(5 4) = XX18(8 6) STA XX15+4 ; = dot_nosev LDA XX18+8 STA XX15+5 JMP LL89 ; Jump down to LL89 .ovflw ; If we get here then the addition below overflowed, so ; we halve the dot products and normal vector SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LSR XX18 ; Divide dot_sidev_lo by 2, so dot_sidev = dot_sidev / 2 LSR XX18+6 ; Divide dot_nosev_lo by 2, so dot_nosev = dot_nosev / 2 LSR XX18+3 ; Divide dot_roofv_lo by 2, so dot_roofv = dot_roofv / 2 LDX #1 ; Set X = 1 so when we fall through into LL92, we divide ; the normal vector by 2 as well .LL92 ; We jump here from above with the scale factor in X, ; and now we apply it by scaling the normal vector down ; by a factor of 2^X (i.e. divide by 2^X) LDA XX12 ; Set XX15 = normal_x STA XX15 LDA XX12+2 ; Set XX15+2 = normal_y STA XX15+2 LDA XX12+4 ; Set A = normal_z .LL93 DEX ; Decrement the scale factor in X BMI LL94 ; If X was 0 before the decrement, there is no scaling ; to do, so jump to LL94 to exit the loop LSR XX15 ; Set XX15 = XX15 / 2 ; = normal_x / 2 LSR XX15+2 ; Set XX15+2 = XX15+2 / 2 ; = normal_y / 2 LSR A ; Set A = A / 2 ; = normal_z / 2 DEX ; Decrement the scale factor in X BPL LL93+3 ; If we have more scaling to do, loop back up to the ; first LSR above until the normal vector is scaled down .LL94 STA R ; Set R = normal_z LDA XX12+5 ; Set S = normal_z_sign STA S LDA XX18+6 ; Set Q = dot_nosev_lo STA Q LDA XX18+8 ; Set A = dot_nosev_sign JSR LL38 ; Set (S A) = (S R) + (A Q) ; = normal_z + dot_nosev ; ; setting the sign of the result in S BCS ovflw ; If the addition overflowed, jump up to ovflw to divide ; both the normal vector and dot products by 2 and try ; again STA XX15+4 ; Set XX15(5 4) = (S A) LDA S ; = normal_z + dot_nosev STA XX15+5 LDA XX15 ; Set R = normal_x STA R LDA XX12+1 ; Set S = normal_x_sign STA S LDA XX18 ; Set Q = dot_sidev_lo STA Q LDA XX18+2 ; Set A = dot_sidev_sign JSR LL38 ; Set (S A) = (S R) + (A Q) ; = normal_x + dot_sidev ; ; setting the sign of the result in S BCS ovflw ; If the addition overflowed, jump up to ovflw to divide ; both the normal vector and dot products by 2 and try ; again STA XX15 ; Set XX15(1 0) = (S A) LDA S ; = normal_x + dot_sidev STA XX15+1 LDA XX15+2 ; Set R = normal_y STA R LDA XX12+3 ; Set S = normal_y_sign STA S LDA XX18+3 ; Set Q = dot_roofv_lo STA Q LDA XX18+5 ; Set A = dot_roofv_sign JSR LL38 ; Set (S A) = (S R) + (A Q) ; = normal_y + dot_roofv BCS ovflw ; If the addition overflowed, jump up to ovflw to divide ; both the normal vector and dot products by 2 and try ; again STA XX15+2 ; Set XX15(3 2) = (S A) LDA S ; = normal_y + dot_roofv STA XX15+3 .LL89 ; When we get here, we have set up the following: ; ; XX15(1 0) = normal_x + dot_sidev ; = normal_x + [x y z] . sidev ; ; XX15(3 2) = normal_y + dot_roofv ; = normal_y + [x y z] . roofv ; ; XX15(5 4) = normal_z + dot_nosev ; = normal_z + [x y z] . nosev ; ; and: ; ; XX12(1 0) = (normal_x_sign normal_x) ; ; XX12(3 2) = (normal_y_sign normal_y) ; ; XX12(5 4) = (normal_z_sign normal_z) ; ; We now calculate the dot product XX12 . XX15 to tell ; us whether or not this face is visible LDA XX12 ; Set Q = XX12 STA Q SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA XX15 ; Set A = XX15 JSR FMLTU ; Set T = A * Q / 256 STA T ; = XX15 * XX12 / 256 LDA XX12+1 ; Set S = sign of XX15(1 0) * XX12(1 0), so: EOR XX15+1 ; STA S ; (S T) = XX15(1 0) * XX12(1 0) / 256 LDA XX12+2 ; Set Q = XX12+2 STA Q LDA XX15+2 ; Set A = XX15+2 JSR FMLTU ; Set Q = A * Q STA Q ; = XX15+2 * XX12+2 / 256 LDA T ; Set T = R, so now: STA R ; ; (S R) = XX15(1 0) * XX12(1 0) / 256 LDA XX12+3 ; Set A = sign of XX15+3 * XX12+3, so: EOR XX15+3 ; ; (A Q) = XX15(3 2) * XX12(3 2) / 256 JSR LL38 ; Set (S T) = (S R) + (A Q) STA T ; = XX15(1 0) * XX12(1 0) / 256 ; + XX15(3 2) * XX12(3 2) / 256 LDA XX12+4 ; Set Q = XX12+4 STA Q LDA XX15+4 ; Set A = XX15+4 JSR FMLTU ; Set Q = A * Q STA Q ; = XX15+4 * XX12+4 / 256 LDA T ; Set T = R, so now: STA R ; ; (S R) = XX15(1 0) * XX12(1 0) / 256 ; + XX15(3 2) * XX12(3 2) / 256 LDA XX15+5 ; Set A = sign of XX15+5 * XX12+5, so: EOR XX12+5 ; ; (A Q) = XX15(5 4) * XX12(5 4) / 256 JSR LL38 ; Set (S A) = (S R) + (A Q) ; = XX15(1 0) * XX12(1 0) / 256 ; + XX15(3 2) * XX12(3 2) / 256 ; + XX15(5 4) * XX12(5 4) / 256 PHA ; Push the result A onto the stack, so the stack now ; contains the dot product XX12 . XX15 TYA ; Set X = Y / 4 LSR A ; = the number of this face * 4 /4 LSR A ; = the number of this face TAX PLA ; Pull the dot product off the stack into A BIT S ; If bit 7 of S is set, i.e. the dot product is BMI P%+4 ; negative, then this face is visible as its normal is ; pointing towards us, so skip the following instruction LDA #0 ; Otherwise the face is not visible, so set A = 0 so we ; can store this to mean "not visible" STA XX2,X ; Store the face's visibility in the X-th byte of XX2 INY ; Above we incremented Y to point to byte #3, so this ; increments Y to point to byte #4, i.e. byte #0 of the ; next face .LL88 CPY XX20 ; If Y >= XX20, the number of faces * 4, jump down to BCS LL42 ; LL42 to move on to the JMP LL86 ; Otherwise loop back to LL86 to work out the visibility ; of the next face