.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 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 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 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 faceName: 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 cullingContext: See this subroutine in context in the source code References: No direct references to this subroutine in this source file
[X]
Subroutine FMLTU (category: Maths (Arithmetic))
Calculate A = A * Q / 256
[X]
Subroutine LL38 (category: Maths (Arithmetic))
Calculate (S A) = (S R) + (A Q)
[X]
Label LL41 in subroutine LL9 (Part 4 of 12)
[X]
Label LL42 in subroutine LL9 (Part 6 of 12)
[X]
Subroutine LL51 (category: Maths (Geometry))
Calculate the dot product of XX15 and XX16
[X]
Label LL86 is local to this routine
[X]
Label LL87 is local to this routine
[X]
Label LL88 is local to this routine
[X]
Label LL89 is local to this routine
[X]
Label LL90 is local to this routine
[X]
[X]
Label LL91 is local to this routine
[X]
Label LL92 is local to this routine
[X]
Label LL93 is local to this routine
[X]
[X]
Label LL94 is local to this routine
[X]
Temporary storage, used to store the address of a ship blueprint. For example, it is used when we add a new ship to the local bubble in routine NWSHP, and it contains the address of the current ship's blueprint as we loop through all the nearby ships in the main flight loop
[X]
Label ovflw is local to this routine