Elite on the BBC Micro and NES

# Bank 1 (Part 3 of 3)

## [NES version]

Name: EDGES Type: Subroutine Category: Drawing lines Summary: Draw a horizontal line given a centre and a half-width
Context: See this subroutine on its own page References: This subroutine is called as follows: * SUN (Part 2 of 2) calls EDGES * SUN (Part 2 of 2) calls via EDGES-2

Set X1 and X2 to the x-coordinates of the ends of the horizontal line with centre x-coordinate YY(1 0), and length A in either direction from the centre (so a total line length of 2 * A). In other words, this line: X1 YY(1 0) X2 +-----------------+-----------------+ <- A -> <- A -> The resulting line gets clipped to the edges of the screen, if needed. If the calculation doesn't overflow, we return with the C flag clear, otherwise the C flag gets set to indicate failure.
Arguments: A The half-length of the line YY(1 0) The centre x-coordinate
Returns: C flag Clear if the line fits on-screen, set if it doesn't X1, X2 The x-coordinates of the clipped line Y Y is preserved
Other entry points: EDGES-2 Return the C flag set if argument A is 0
.ED3 BPL ED1 ; We jump here with the status flags set to the result ; of the high byte of this subtraction, and only if the ; high byte is non-zero: ; ; (A X1) = YY(1 0) - argument A ; ; If the result of the subtraction is positive and ; non-zero then the coordinate is not on-screen, so jump ; to ED1 to return the C flag set LDA #0 ; The result of the subtraction is negative, so we have STA X1 ; have gone past the left edge of the screen, so we clip ; the x-coordinate in X1 to 0 CLC ; Clear the C flag to indicate that the clipped line ; fits on-screen RTS ; Return from the subroutine .ED1 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 SEC ; Set the C flag to indicate that the line does not fit ; on-screen RTS ; Return from the subroutine BEQ ED1 ; If we call the routine at EDGES-2, this checks whether ; the argument in A is zero, and if it is, it jumps to ; ED1 to return the C flag set .EDGES STA T ; Set T to the line's half-length in argument A CLC ; We now calculate: ADC YY ; STA X2 ; (A X2) = YY(1 0) + A ; ; to set X2 to the x-coordinate of the right end of the ; line, starting with the low bytes LDA YY+1 ; And then adding the high bytes ADC #0 BMI ED1 ; If the addition is negative then the calculation has ; overflowed, so jump to ED1 to return a failure BEQ P%+6 ; If the high byte A from the result is 0, skip the ; next two instructions, as the result already fits on ; the screen LDA #253 ; The high byte is positive and non-zero, so we went STA X2 ; past the right edge of the screen, so clip X2 to the ; x-coordinate of the right edge of the screen LDA YY ; We now calculate: SEC ; SBC T ; (A X1) = YY(1 0) - argument A STA X1 ; ; to set X1 to the x-coordinate of the left end of the ; line, starting with the low bytes LDA YY+1 ; And then subtracting the high bytes SBC #0 BNE ED3 ; If the high byte of the subtraction is non-zero, then ; jump to ED3 to return a failure if the subtraction has ; taken us off the left edge of the screen LDA X1 ; Set the C flag if X1 >= X2, clear it if X1 < X2 CMP X2 ; ; So this sets the C flag if the line doesn't fit on ; the screen RTS ; Return from the subroutine
Name: DrawSunEdgeLeft Type: Subroutine Category: Drawing suns Summary: Draw a sun line in the tile on the left end of a sun row
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawSunEdgeRight calls via RTS7 * DrawSunEdgeRight calls via DrawSunEdge

Arguments: A The half-width of the sun line Y The number of the pixel row of the sun line within the tile row (0-7) P The pixel x-coordinate of the start of the middle section of the sun line (i.e. the x-coordinate just to the right of the leftmost tile) YY(1 0) The centre x-coordinate of the sun
Other entry points: RTS7 Contains an RTS DrawSunEdge Draw a sun line from (X1, Y) to (X2, Y)
.DrawSunEdgeLeft LDX P ; Set X2 to P, which contains the x-coordinate just to STX X2 ; the right of the leftmost tile ; ; We can use this as the x-coordinate of the right end ; of the line that we want to draw in the leftmost tile EOR #\$FF ; Use two's complement to set X1 = YY(1 0) - A SEC ; ADC YY ; So X1 contains the x-coordinate of the left end of the STA X1 ; sun line LDA YY+1 ADC #\$FF BEQ DrawSunEdge ; If the high byte of the result is zero, then the left ; end of the line is on-screen, so jump to DrawSunEdge ; to draw the sun line from (X1, Y) to (X2, Y) BMI sunl1 ; If the high byte of the result is negative, then the ; left end of the line is off the left edge of the ; screen, so jump to sunl1 to draw a clipped sun line ; from (0, Y) to (X2, Y) ; Otherwise the line is off-screen, so return from the ; subroutine without drawing anything .RTS7 RTS ; Return from the subroutine .DrawSunEdge LDA X1 ; If X1 >= X2 then the left end of the line is to the CMP X2 ; right of the right end of the line, so these are not BCS RTS7 ; valid line coordinates and we jump to RTS7 to return ; from the subroutine without drawing anything JMP HLOIN ; Otherwise draw the sun line from (X1, Y) to (X2, Y) ; and return from the subroutine using a tail call .sunl1 ; If we get here then we need to clip the left end of ; the line to fit on-screen LDA #0 ; Draw a clipped the sun line from (0, Y) to (X2, Y) STA X1 ; and return from the subroutine using a tail call JMP HLOIN
Name: DrawSunEdgeRight Type: Subroutine Category: Drawing suns Summary: Draw a sun line in the tile on the right end of a sun row
Context: See this subroutine on its own page References: This subroutine is called as follows:

Arguments: A The half-width of the sun line Y The number of the pixel row of the sun line within the tile row (0-7) X1 The pixel x-coordinate of the rightmost tile on the sun line YY(1 0) The centre x-coordinate of the sun
.DrawSunEdgeRight CLC ; Set X1 = YY(1 0) + A ADC YY ; STA X2 ; So X2 contains the x-coordinate of the right end of LDA YY+1 ; the sun line ADC #0 ; X1 is already set to the x-coordinate of the rightmost ; tile, so the line we need to draw is from (X1, Y) to ; (X2, Y) BEQ DrawSunEdge ; If the high byte of the result is zero, then the right ; end of the line is on-screen, so jump to DrawSunEdge ; to draw the sun line from (X1, Y) to (X2, Y) BMI RTS7 ; If the high byte of the result is negative, then the ; right end of the line is off the left edge of the ; screen, so the line is not on-screen and we jump to ; RTS7 to return from the subroutine (as RTS7 contains ; an RTS) ; If we get here then the right end of the line is past ; the right edge of the screen, so we need to clip the ; right end of the line to fit on-screen LDA #253 ; Set X2 = 253 so the line is clipped to the right edge STA X2 ; of the screen CMP X1 ; If X2 <= X1 then the right end of the line is to the BEQ RTS7 ; left of the left end of the line, so these are not BCC RTS7 ; valid line coordinates and we jump to RTS7 to return ; from the subroutine without drawing anything JMP HLOIN ; Otherwise draw the sun line from (X1, Y) to (X2, Y) ; and return from the subroutine using a tail call
Name: CHKON Type: Subroutine Category: Drawing circles Summary: Check whether any part of a circle appears on the extended screen
Context: See this subroutine on its own page References: This subroutine is called as follows: * CIRCLE calls CHKON * SUN (Part 1 of 2) calls CHKON

Arguments: K The circle's radius K3(1 0) Pixel x-coordinate of the centre of the circle K4(1 0) Pixel y-coordinate of the centre of the circle
Returns: C flag Clear if any part of the circle appears on-screen, set if none of the circle appears on-screen (A X) Minimum y-coordinate of the circle on-screen (i.e. the y-coordinate of the top edge of the circle) P(2 1) Maximum y-coordinate of the circle on-screen (i.e. the y-coordinate of the bottom edge of the circle)
.CHKON LDA K3 ; Set A = K3 + K CLC ADC K LDA K3+1 ; Set A = K3+1 + 0 + any carry from above, so this ADC #0 ; effectively sets A to the high byte of K3(1 0) + K: ; ; (A ?) = K3(1 0) + K ; ; so A is the high byte of the x-coordinate of the right ; edge of the circle BMI PL21 ; If A is negative then the right edge of the circle is ; to the left of the screen, so jump to PL21 to set the ; C flag and return from the subroutine, as the whole ; circle is off-screen to the left LDA K3 ; Set A = K3 - K SEC SBC K LDA K3+1 ; Set A = K3+1 - 0 - any carry from above, so this SBC #0 ; effectively sets A to the high byte of K3(1 0) - K: ; ; (A ?) = K3(1 0) - K ; ; so A is the high byte of the x-coordinate of the left ; edge of the circle BMI PL31 ; If A is negative then the left edge of the circle is ; to the left of the screen, and we already know the ; right edge is either on-screen or off-screen to the ; right, so skip to PL31 to move on to the y-coordinate ; checks, as at least part of the circle is on-screen in ; terms of the x-axis BNE PL21 ; If A is non-zero, then the left edge of the circle is ; to the right of the screen, so jump to PL21 to set the ; C flag and return from the subroutine, as the whole ; circle is off-screen to the right .PL31 LDA K4 ; Set P+1 = K4 + K CLC ADC K STA P+1 LDA K4+1 ; Set A = K4+1 + 0 + any carry from above, so this ADC #0 ; does the following: ; ; (A P+1) = K4(1 0) + K ; ; so A is the high byte of the y-coordinate of the ; bottom edge of the circle BMI PL21 ; If A is negative then the bottom edge of the circle is ; above the top of the screen, so jump to PL21 to set ; the C flag and return from the subroutine, as the ; whole circle is off-screen to the top STA P+2 ; Store the high byte in P+2, so now we have: ; ; P(2 1) = K4(1 0) + K ; ; i.e. the maximum y-coordinate of the circle on-screen ; (which we return) LDA K4 ; Set X = K4 - K SEC SBC K TAX LDA K4+1 ; Set A = K4+1 - 0 - any carry from above, so this SBC #0 ; does the following: ; ; (A X) = K4(1 0) - K ; ; so A is the high byte of the y-coordinate of the top ; edge of the circle BMI PL44 ; If A is negative then the top edge of the circle is ; above the top of the screen, and we already know the ; bottom edge is either on-screen or below the bottom ; of the screen, so skip to PL44 to clear the C flag and ; return from the subroutine using a tail call, as part ; of the circle definitely appears on-screen BNE PL21 ; If A is non-zero, then the top edge of the circle is ; below the bottom of the screen, so jump to PL21 to set ; the C flag and return from the subroutine, as the ; whole circle is off-screen to the bottom CPX Yx2M1 ; If we get here then A is zero, which means the top ; edge of the circle is within the screen boundary, so ; now we need to check whether it is in the space view ; (in which case it is on-screen) or the dashboard (in ; which case the top of the circle is hidden by the ; dashboard, so the circle isn't on-screen). We do this ; by checking the low byte of the result in X against ; Yx2M1, and returning the C flag from this comparison. ; The value in Yx2M1 is the y-coordinate of the bottom ; pixel row of the space view, so this does the ; following: ; ; * The C flag is set if coordinate (A X) is below the ; bottom row of the space view, i.e. the top edge of ; the circle is hidden by the dashboard ; ; * The C flag is clear if coordinate (A X) is above ; the bottom row of the space view, i.e. the top ; edge of the circle is on-screen RTS ; Return from the subroutine
Name: PL21 Type: Subroutine Category: Drawing planets Summary: Return from a planet/sun-drawing routine with a failure flag
Context: See this subroutine on its own page References: This subroutine is called as follows: * CHKON calls PL21

Set the C flag and return from the subroutine. This is used to return from a planet- or sun-drawing routine with the C flag indicating an overflow in the calculation.
.PL21 SEC ; Set the C flag to indicate an overflow RTS ; Return from the subroutine
Name: PL44 Type: Subroutine Category: Drawing planets Summary: Return from a planet/sun-drawing routine with a success flag
Context: See this subroutine on its own page References: This subroutine is called as follows: * CHKON calls PL44

Clear the C flag and return from the subroutine. This is used to return from a planet- or sun-drawing routine with the C flag indicating an overflow in the calculation.
.PL44 CLC ; Clear the C flag to indicate success RTS ; Return from the subroutine
Name: PLS3 Type: Subroutine Category: Drawing planets Summary: Calculate (Y A P) = 222 * roofv_x / z
Context: See this subroutine on its own page References: This subroutine is called as follows: * PL9 (Part 3 of 3) calls PLS3

Calculate the following, with X determining the vector to use: (Y A P) = 222 * roofv_x / z though in reality only (Y A) is used. Although the code below supports a range of values of X, in practice the routine is only called with X = 15, and then again after X has been incremented to 17. So the values calculated by PLS1 use roofv_x first, then roofv_y. The comments below refer to roofv_x, for the first call.
Arguments: X Determines which of the INWK orientation vectors to divide: * X = 15: divides roofv_x * X = 17: divides roofv_y
Returns: X X gets incremented by 2 so it points to the next coordinate in this orientation vector (so consecutive calls to the routine will start with x, then move onto y and then z)
.PLS3 JSR PLS1 ; Call PLS1 to calculate the following: STA P ; ; P = |roofv_x / z| ; K+3 = sign of roofv_x / z ; ; and increment X to point to roofv_y for the next call LDA #222 ; Set Q = 222, the offset to the crater STA Q STX U ; Store the vector index X in U for retrieval after the ; call to MULTU JSR MULTU ; Call MULTU to calculate ; ; (A P) = P * Q ; = 222 * |roofv_x / z| LDX U ; Restore the vector index from U into X LDY K+3 ; If the sign of the result in K+3 is positive, skip to BPL PL12 ; PL12 to return with Y = 0 EOR #\$FF ; Otherwise the result should be negative, so negate the CLC ; high byte of the result using two's complement with ADC #1 ; A = ~A + 1 BEQ PL12 ; If A = 0, jump to PL12 to return with (Y A) = 0 LDY #\$FF ; Set Y = \$FF to be a negative high byte RTS ; Return from the subroutine .PL12 LDY #0 ; Set Y = 0 to be a positive high byte RTS ; Return from the subroutine
Name: PLS4 Type: Subroutine Category: Drawing planets Summary: Calculate CNT2 = arctan(P / A) / 4
Context: See this subroutine on its own page References: This subroutine is called as follows: * PL9 (Part 2 of 3) calls PLS4

Calculate the following: CNT2 = arctan(P / A) / 4 and do the following if nosev_z_hi >= 0: CNT2 = CNT2 + 32 which is the equivalent of adding 180 degrees to the result (or PI radians), as there are 64 segments in a full circle. This routine is called with the following arguments when calculating the equator and meridian for planets: * A = roofv_z_hi, P = -nosev_z_hi * A = sidev_z_hi, P = -nosev_z_hi So it calculates the angle between the planet's orientation vectors, in the z-axis.
.PLS4 STA Q ; Set Q = A JSR ARCTAN ; Call ARCTAN to calculate: ; ; A = arctan(P / Q) ; arctan(P / A) ; ; The result in A will be in the range 0 to 128, which ; represents an angle of 0 to 180 degrees (or 0 to PI ; radians) LDX INWK+14 ; If nosev_z_hi is negative, skip the following BMI P%+4 ; instruction to leave the angle in A as a positive ; integer in the range 0 to 128 (so when we calculate ; CNT2 below, it will be in the right half of the ; anti-clockwise arc that we describe when drawing ; circles, i.e. from 6 o'clock, through 3 o'clock and ; on to 12 o'clock) EOR #%10000000 ; If we get here then nosev_z_hi is positive, so flip ; bit 7 of the angle in A, which is the same as adding ; 128 to give a result in the range 129 to 256 (i.e. 129 ; to 0), or 180 to 360 degrees (so when we calculate ; CNT2 below, it will be in the left half of the ; anti-clockwise arc that we describe when drawing ; circles, i.e. from 12 o'clock, through 9 o'clock and ; on to 6 o'clock) LSR A ; Set CNT2 = A / 4 LSR A STA CNT2 RTS ; Return from the subroutine
Name: PLS5 Type: Subroutine Category: Drawing planets Summary: Calculate roofv_x / z and roofv_y / z
Context: See this subroutine on its own page References: This subroutine is called as follows: * PL9 (Part 2 of 3) calls PLS5

Calculate the following divisions of a specified value from one of the orientation vectors (in this example, roofv): (XX16+2 K2+2) = roofv_x / z (XX16+3 K2+3) = roofv_y / z
Arguments: X Determines which of the INWK orientation vectors to divide: * X = 15: divides roofv_x and roofv_y * X = 21: divides sidev_x and sidev_y INWK The planet's ship data block
.PLS5 JSR PLS1 ; Call PLS1 to calculate the following: STA K2+2 ; STY XX16+2 ; K+2 = |roofv_x / z| ; XX16+2 = sign of roofv_x / z ; ; i.e. (XX16+2 K2+2) = roofv_x / z ; ; and increment X to point to roofv_y for the next call JSR PLS1 ; Call PLS1 to calculate the following: STA K2+3 ; STY XX16+3 ; K+3 = |roofv_y / z| ; XX16+3 = sign of roofv_y / z ; ; i.e. (XX16+3 K2+3) = roofv_y / z ; ; and increment X to point to roofv_z for the next call RTS ; Return from the subroutine
Name: ARCTAN Type: Subroutine Category: Maths (Geometry) Summary: Calculate A = arctan(P / Q) Deep dive: The sine, cosine and arctan tables
Context: See this subroutine on its own page References: This subroutine is called as follows: * PLS4 calls ARCTAN

Calculate the following: A = arctan(P / Q) In other words, this finds the angle in the right-angled triangle where the opposite side to angle A is length P and the adjacent side to angle A has length Q, so: tan(A) = P / Q The result in A is an integer representing the angle in radians. The routine returns values in the range 0 to 128, which covers 0 to 180 degrees (or 0 to PI radians).
.ARCTAN LDA P ; Set T1 = P EOR Q, which will have the sign of P * Q EOR Q STA T1 LDA Q ; If Q = 0, jump to AR2 to return a right angle BEQ AR2 ASL A ; Set Q = |Q| * 2 (this is a quick way of clearing the STA Q ; sign bit, and we don't need to shift right again as we ; only ever use this value in the division with |P| * 2, ; which we set next) LDA P ; Set A = |P| * 2 ASL A CMP Q ; If A >= Q, i.e. |P| > |Q|, jump to AR1 to swap P BCS AR1 ; and Q around, so we can still use the lookup table JSR ARS1 ; Call ARS1 to set the following from the lookup table: ; ; A = arctan(A / Q) ; = arctan(|P / Q|) SEC ; Set the C flag so the SBC instruction in AR3 will be ; correct, should we jump there .AR4 LDX T1 ; If T1 is negative, i.e. P and Q have different signs, BMI AR3 ; jump down to AR3 to return arctan(-|P / Q|) RTS ; Otherwise P and Q have the same sign, so our result is ; correct and we can return from the subroutine .AR1 ; We want to calculate arctan(t) where |t| > 1, so we ; can use the calculation described in the documentation ; for the ACT table, i.e. 64 - arctan(1 / t) LDX Q ; Swap the values in Q and P, using the fact that we STA Q ; called AR1 with A = P STX P ; TXA ; This also sets A = P (which now contains the original ; argument |Q|) JSR ARS1 ; Call ARS1 to set the following from the lookup table: ; ; A = arctan(A / Q) ; = arctan(|Q / P|) ; = arctan(1 / |P / Q|) STA T ; Set T = 64 - T LDA #64 SBC T BCS AR4 ; Jump to AR4 to continue the calculation (this BCS is ; effectively a JMP as the subtraction will never ; underflow, as ARS1 returns values in the range 0-31) .AR2 ; If we get here then Q = 0, so tan(A) = infinity and ; A is a right angle, or 0.25 of a circle. We allocate ; 255 to a full circle, so we should return 63 for a ; right angle LDA #63 ; Set A to 63, to represent a right angle RTS ; Return from the subroutine .AR3 ; A contains arctan(|P / Q|) but P and Q have different ; signs, so we need to return arctan(-|P / Q|), using ; the calculation described in the documentation for the ; ACT table, i.e. 128 - A STA T ; Set A = 128 - A LDA #128 ; SBC T ; The subtraction will work because we did a SEC before ; calling AR3 RTS ; Return from the subroutine .ARS1 ; This routine fetches arctan(A / Q) from the ACT table, ; so A will be set to an integer in the range 0 to 31 ; that represents an angle from 0 to 45 degrees (or 0 to ; PI / 4 radians) JSR LL28 ; Call LL28 to calculate: ; ; R = 256 * A / Q LDA R ; Set X = R / 8 LSR A ; = 32 * A / Q LSR A ; LSR A ; so X has the value t * 32 where t = A / Q, which is TAX ; what we need to look up values in the ACT table LDA ACT,X ; Fetch ACT+X from the ACT table into A, so now: ; ; A = value in ACT + X ; = value in ACT + (32 * A / Q) ; = arctan(A / Q) RTS ; Return from the subroutine
Name: BLINE Type: Subroutine Category: Drawing circles Summary: Draw a circle segment and add it to the ball line heap Deep dive: The ball line heap Drawing circles
Context: See this subroutine on its own page References: This subroutine is called as follows: * CIRCLE2 calls BLINE * PLS22 calls BLINE

Draw a single segment of a circle, adding the point to the ball line heap.
Arguments: CNT The number of this segment STP The step size for the circle K6(1 0) The x-coordinate of the new point on the circle, as a screen coordinate (T X) The y-coordinate of the new point on the circle, as an offset from the centre of the circle FLAG Set to \$FF for the first call, so it sets up the first point in the heap but waits until the second call before drawing anything (as we need two points, i.e. two calls, before we can draw a line) K The circle's radius K3(1 0) Pixel x-coordinate of the centre of the circle K4(1 0) Pixel y-coordinate of the centre of the circle K5(1 0) Screen x-coordinate of the previous point added to the ball line heap (if this is not the first point) K5(3 2) Screen y-coordinate of the previous point added to the ball line heap (if this is not the first point) SWAP If non-zero, we swap (X1, Y1) and (X2, Y2)
Returns: CNT CNT is updated to CNT + STP A The new value of CNT K5(1 0) Screen x-coordinate of the point that we just added to the ball line heap K5(3 2) Screen y-coordinate of the point that we just added to the ball line heap FLAG Set to 0
.BLINE 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 TXA ; Set K6(3 2) = (T X) + K4(1 0) ADC K4 ; = y-coord of centre + y-coord of new point STA K6+2 ; LDA K4+1 ; so K6(3 2) now contains the y-coordinate of the new ADC T ; point on the circle but as a screen coordinate, to go STA K6+3 ; along with the screen y-coordinate in K6(1 0) LDA FLAG ; If FLAG = 0, jump down to BL1 BEQ BL1 INC FLAG ; Flag is \$FF so this is the first call to BLINE, so ; increment FLAG to set it to 0, as then the next time ; we call BLINE it can draw the first line, from this ; point to the next JMP BL5 ; This is the first call to BLINE, so we don't need to ; copy the previous point to XX15 as there isn't one, ; so we jump to BL5 to tidy up and return from the ; subroutine .BL1 LDA K5 ; Set XX15 = K5 = x_lo of previous point STA XX15 LDA K5+1 ; Set XX15+1 = K5+1 = x_hi of previous point STA XX15+1 LDA K5+2 ; Set XX15+2 = K5+2 = y_lo of previous point STA XX15+2 LDA K5+3 ; Set XX15+3 = K5+3 = y_hi of previous point STA XX15+3 LDA K6 ; Set XX15+4 = x_lo of new point STA XX15+4 LDA K6+1 ; Set XX15+5 = x_hi of new point STA XX15+5 LDA K6+2 ; Set XX12 = y_lo of new point STA XX12 LDA K6+3 ; Set XX12+1 = y_hi of new point STA XX12+1 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 JSR CLIP ; Call CLIP to see if the new line segment needs to be ; clipped to fit on-screen, returning the clipped line's ; end-points in (X1, Y1) and (X2, Y2) BCS BL5 ; If the C flag is set then the line is not visible on ; screen anyway, so jump to BL5, to avoid drawing and ; storing this line LDA SWAP ; If SWAP = 0, then we didn't have to swap the line BEQ BL9 ; coordinates around during the clipping process, so ; jump to BL9 to skip the following swap LDA X1 ; Otherwise the coordinates were swapped by the call to LDY X2 ; LL145 above, so we swap (X1, Y1) and (X2, Y2) back STA X2 ; again STY X1 LDA Y1 LDY Y2 STA Y2 STY Y1 .BL9 JSR LOIN ; Draw a line from (X1, Y1) to (X2, Y2) .BL5 LDA K6 ; Copy the data for this step point from K6(3 2 1 0) STA K5 ; into K5(3 2 1 0), for use in the next call to BLINE: LDA K6+1 ; STA K5+1 ; * K5(1 0) = screen x-coordinate of this point LDA K6+2 ; STA K5+2 ; * K5(3 2) = screen y-coordinate of this point LDA K6+3 ; STA K5+3 ; They now become the "previous point" in the next call LDA CNT ; Set CNT = CNT + STP CLC ADC STP STA CNT RTS ; Return from the subroutine
Name: STARS Type: Subroutine Category: Stardust Summary: The main routine for processing the stardust Deep dive: Sprite usage in NES Elite
Context: See this subroutine on its own page References: This subroutine is called as follows: * STARS_b1 calls STARS

Called at the very end of the main flight loop.
.STARS LDX VIEW ; Load the current view into X: ; ; 0 = front ; 1 = rear ; 2 = left ; 3 = right BEQ STARS1 ; If this 0, jump to STARS1 to process the stardust for ; the front view DEX ; If this is view 2 or 3, jump to STARS2 (via ST11) to BNE ST11 ; process the stardust for the left or right views JMP STARS6 ; Otherwise this is the rear view, so jump to STARS6 to ; process the stardust for the rear view .ST11 JMP STARS2 ; Jump to STARS2 for the left or right views, as it's ; too far for the branch instruction above
Name: STARS1 Type: Subroutine Category: Stardust Summary: Process the stardust for the front view Deep dive: Stardust in the front view Deep dive: Sprite usage in NES Elite
Context: See this subroutine on its own page References: This subroutine is called as follows: * STARS calls STARS1

This moves the stardust towards us according to our speed (so the dust rushes past us), and applies our current pitch and roll to each particle of dust, so the stardust moves correctly when we steer our ship. When a stardust particle rushes past us and falls off the side of the screen, its memory is recycled as a new particle that's positioned randomly on-screen. These are the calculations referred to in the commentary: 1. q = 64 * speed / z_hi 2. z = z - speed * 64 3. y = y + |y_hi| * q 4. x = x + |x_hi| * q 5. y = y + alpha * x / 256 6. x = x - alpha * y / 256 7. x = x + 2 * (beta * y / 256) ^ 2 8. y = y - beta * 256 For more information see the deep dive on "Stardust in the front view".
.STARS1 LDY NOSTM ; Set Y to the current number of stardust particles, so ; we can use it as a counter through all the stardust ; In the following, we're going to refer to the 16-bit ; space coordinates of the current particle of stardust ; (i.e. the Y-th particle) like this: ; ; x = (x_hi x_lo) ; y = (y_hi y_lo) ; z = (z_hi z_lo) ; ; These values are stored in (SX+Y SXL+Y), (SY+Y SYL+Y) ; and (SZ+Y SZL+Y) respectively .STL1 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 JSR DV42 ; Call DV42 to set the following: ; ; (P R) = 256 * DELTA / z_hi ; = 256 * speed / z_hi ; ; The maximum value returned is P = 2 and R = 128 (see ; DV42 for an explanation) LDA R ; Set A = R, so now: ; ; (P A) = 256 * speed / z_hi LSR P ; Rotate (P A) right by 2 places, which sets P = 0 (as P ROR A ; has a maximum value of 2) and leaves: LSR P ; ROR A ; A = 64 * speed / z_hi ORA #1 ; Make sure A is at least 1, and store it in Q, so we STA Q ; now have result 1 above: ; ; Q = 64 * speed / z_hi LDA SZL,Y ; We now calculate the following: SBC DELT4 ; STA SZL,Y ; (z_hi z_lo) = (z_hi z_lo) - DELT4(1 0) ; ; starting with the low bytes LDA SZ,Y ; And then we do the high bytes STA ZZ ; SBC DELT4+1 ; We also set ZZ to the original value of z_hi, which we STA SZ,Y ; use below to remove the existing particle ; ; So now we have result 2 above: ; ; z = z - DELT4(1 0) ; = z - speed * 64 JSR MLU1 ; Call MLU1 to set: ; ; Y1 = y_hi ; ; (A P) = |y_hi| * Q ; ; So Y1 contains the original value of y_hi, which we ; use below to remove the existing particle ; We now calculate: ; ; (S R) = YY(1 0) = (A P) + y STA YY+1 ; First we do the low bytes with: LDA P ; ADC SYL,Y ; YY+1 = A STA YY ; R = YY = P + y_lo STA R ; ; so we get this: ; ; (? R) = YY(1 0) = (A P) + y_lo LDA Y1 ; And then we do the high bytes with: ADC YY+1 ; STA YY+1 ; S = YY+1 = y_hi + YY+1 STA S ; ; so we get our result: ; ; (S R) = YY(1 0) = (A P) + (y_hi y_lo) ; = |y_hi| * Q + y ; ; which is result 3 above, and (S R) is set to the new ; value of y LDA SX,Y ; Set X1 = A = x_hi STA X1 ; ; So X1 contains the original value of x_hi, which we ; use below to remove the existing particle JSR MLU2 ; Set (A P) = |x_hi| * Q ; We now calculate: ; ; XX(1 0) = (A P) + x STA XX+1 ; First we store the high byte A in XX+1 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 P ; Then we do the low bytes: ADC SXL,Y ; STA XX ; XX(1 0) = (A P) + x_lo LDA X1 ; And then we do the high bytes: ADC XX+1 ; STA XX+1 ; XX(1 0) = XX(1 0) + (x_hi 0) ; ; so we get our result: ; ; XX(1 0) = (A P) + x ; = |x_hi| * Q + x ; ; which is result 4 above, and we also have: ; ; A = XX+1 = (|x_hi| * Q + x) / 256 ; ; i.e. A is the new value of x, divided by 256 EOR ALP2+1 ; EOR with the flipped sign of the roll angle alpha, so ; A has the opposite sign to the flipped roll angle ; alpha, i.e. it gets the same sign as alpha JSR MLS1 ; Call MLS1 to calculate: ; ; (A P) = A * ALP1 ; = (x / 256) * alpha JSR ADD ; Call ADD to calculate: ; ; (A X) = (A P) + (S R) ; = (x / 256) * alpha + y ; = y + alpha * x / 256 STA YY+1 ; Set YY(1 0) = (A X) to give: STX YY ; ; YY(1 0) = y + alpha * x / 256 ; ; which is result 5 above, and we also have: ; ; A = YY+1 = y + alpha * x / 256 ; ; i.e. A is the new value of y, divided by 256 EOR ALP2 ; EOR A with the correct sign of the roll angle alpha, ; so A has the opposite sign to the roll angle alpha JSR MLS2 ; Call MLS2 to calculate: ; ; (S R) = XX(1 0) ; = x ; ; (A P) = A * ALP1 ; = -y / 256 * alpha JSR ADD ; Call ADD to calculate: ; ; (A X) = (A P) + (S R) ; = -y / 256 * alpha + x STA XX+1 ; Set XX(1 0) = (A X), which gives us result 6 above: STX XX ; ; x = x - alpha * y / 256 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 LDX BET1 ; Fetch the pitch magnitude into X LDA YY+1 ; Set A to y_hi and set it to the flipped sign of beta EOR BET2+1 JSR MULTS-2 ; Call MULTS-2 to calculate: ; ; (A P) = X * A ; = -beta * y_hi STA Q ; Store the high byte of the result in Q, so: ; ; Q = -beta * y_hi / 256 JSR MUT2 ; Call MUT2 to calculate: ; ; (S R) = XX(1 0) = x ; ; (A P) = Q * A ; = (-beta * y_hi / 256) * (-beta * y_hi / 256) ; = (beta * y / 256) ^ 2 ASL P ; Double (A P), store the top byte in A and set the C ROL A ; flag to bit 7 of the original A, so this does: STA T ; ; (T P) = (A P) << 1 ; = 2 * (beta * y / 256) ^ 2 LDA #0 ; Set bit 7 in A to the sign bit from the A in the ROR A ; calculation above and apply it to T, so we now have: ORA T ; ; (A P) = (A P) * 2 ; = 2 * (beta * y / 256) ^ 2 ; ; with the doubling retaining the sign of (A P) JSR ADD ; Call ADD to calculate: ; ; (A X) = (A P) + (S R) ; = 2 * (beta * y / 256) ^ 2 + x STA XX+1 ; Store the high byte A in XX+1 TXA ; Store the low byte X in x_lo STA SXL,Y ; So (XX+1 x_lo) now contains: ; ; x = x + 2 * (beta * y / 256) ^ 2 ; ; which is result 7 above LDA YY ; Set (S R) = YY(1 0) = y STA R LDA YY+1 STA S LDA #0 ; Set P = 0 STA P 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 BETA ; Set A = -beta, so: EOR #%10000000 ; ; (A P) = (-beta 0) ; = -beta * 256 ; Calculate the following: ; ; (YY+1 y_lo) = (A P) + (S R) ; = -beta * 256 + y ; ; i.e. y = y - beta * 256, which is result 8 above JSR ADD ; Set (A X) = (A P) + (S R) STA YY+1 ; Set YY+1 to A, the high byte of the result TXA ; Set SYL+Y to X, the low byte of the result STA SYL,Y ; We now have our newly moved stardust particle at ; x-coordinate (XX+1 x_lo) and y-coordinate (YY+1 y_lo) ; and distance z_hi, so we draw it if it's still on ; screen, otherwise we recycle it as a new bit of ; stardust and draw that LDA XX+1 ; Set X1 and x_hi to the high byte of XX in XX+1, so STA X1 ; the new x-coordinate is in (x_hi x_lo) and the high STA SX,Y ; byte is in X1 AND #%01111111 ; If |x_hi| >= 120 then jump to KILL1 to recycle this CMP #120 ; particle, as it's gone off the side of the screen, BCS KILL1 ; and rejoin at STC1 with the new particle LDA YY+1 ; Set Y1 and y_hi to the high byte of YY in YY+1, so STA SY,Y ; the new x-coordinate is in (y_hi y_lo) and the high STA Y1 ; byte is in Y1 AND #%01111111 ; If |y_hi| >= 120 then jump to KILL1 to recycle this CMP #120 ; particle, as it's gone off the top or bottom of the BCS KILL1 ; screen, and rejoin at STC1 with the new particle LDA SZ,Y ; If z_hi < 16 then jump to KILL1 to recycle this CMP #16 ; particle, as it's so close that it's effectively gone BCC KILL1 ; past us, and rejoin at STC1 with the new particle STA ZZ ; Set ZZ to the z-coordinate in z_hi .STC1 JSR PIXEL2 ; Draw a stardust particle at (X1,Y1) with distance ZZ, ; i.e. draw the newly moved particle at (x_hi, y_hi) ; with distance z_hi DEY ; Decrement the loop counter to point to the next ; stardust particle BEQ P%+5 ; If we have just done the last particle, skip the next ; instruction to return from the subroutine JMP STL1 ; We have more stardust to process, so jump back up to ; STL1 for the next particle RTS ; Return from the subroutine .KILL1 ; Our particle of stardust just flew past us, so let's ; recycle that particle, starting it at a random ; position that isn't too close to the centre point JSR DORND ; Set A and X to random numbers ORA #4 ; Make sure A is at least 4 and store it in Y1 and y_hi, STA Y1 ; so the new particle starts at least 4 pixels above or STA SY,Y ; below the centre of the screen JSR DORND ; Set A and X to random numbers ORA #16 ; Make sure A is at least 16 and is a multiple of 16 and AND #%11110000 ; store it in X1 and x_hi, so the new particle starts at STA X1 ; least 16 pixels either side of the centre of the STA SX,Y ; screen and on a spaced-out grid that's 16 pixels wide JSR DORND ; Set A and X to random numbers ORA #144 ; Make sure A is at least 144 and store it in ZZ and STA SZ,Y ; z_hi so the new particle starts in the far distance STA ZZ 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 Y1 ; Set A to the new value of y_hi. This has no effect as ; STC1 starts with a jump to PIXEL2, which starts with a ; LDA instruction JMP STC1 ; Jump up to STC1 to draw this new particle
Name: STARS6 Type: Subroutine Category: Stardust Summary: Process the stardust for the rear view Deep dive: Sprite usage in NES Elite
Context: See this subroutine on its own page References: This subroutine is called as follows: * STARS calls STARS6

This routine is very similar to STARS1, which processes stardust for the front view. The main difference is that the direction of travel is reversed, so the signs in the calculations are different, as well as the order of the first batch of calculations. When a stardust particle falls away into the far distance, it is removed from the screen and its memory is recycled as a new particle, positioned randomly along one of the four edges of the screen. These are the calculations referred to in the commentary: 1. q = 64 * speed / z_hi 2. z = z - speed * 64 3. y = y + |y_hi| * q 4. x = x + |x_hi| * q 5. y = y + alpha * x / 256 6. x = x - alpha * y / 256 7. x = x + 2 * (beta * y / 256) ^ 2 8. y = y - beta * 256 For more information see the deep dive on "Stardust in the front view".
.STARS6 LDY NOSTM ; Set Y to the current number of stardust particles, so ; we can use it as a counter through all the stardust .STL6 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 JSR DV42 ; Call DV42 to set the following: ; ; (P R) = 256 * DELTA / z_hi ; = 256 * speed / z_hi ; ; The maximum value returned is P = 2 and R = 128 (see ; DV42 for an explanation) LDA R ; Set A = R, so now: ; ; (P A) = 256 * speed / z_hi LSR P ; Rotate (P A) right by 2 places, which sets P = 0 (as P ROR A ; has a maximum value of 2) and leaves: LSR P ; ROR A ; A = 64 * speed / z_hi ORA #1 ; Make sure A is at least 1, and store it in Q, so we STA Q ; now have result 1 above: ; ; Q = 64 * speed / z_hi LDA SX,Y ; Set X1 = A = x_hi STA X1 ; ; So X1 contains the original value of x_hi, which we ; use below to remove the existing particle JSR MLU2 ; Set (A P) = |x_hi| * Q ; We now calculate: ; ; XX(1 0) = x - (A P) STA XX+1 ; First we do the low bytes: LDA SXL,Y ; SBC P ; XX(1 0) = x_lo - (A P) STA XX LDA X1 ; And then we do the high bytes: SBC XX+1 ; STA XX+1 ; XX(1 0) = (x_hi 0) - XX(1 0) ; ; so we get our result: ; ; XX(1 0) = x - (A P) ; = x - |x_hi| * Q ; ; which is result 2 above, and we also have: JSR MLU1 ; Call MLU1 to set: ; ; Y1 = y_hi ; ; (A P) = |y_hi| * Q ; ; So Y1 contains the original value of y_hi, which we ; use below to remove the existing particle ; We now calculate: ; ; (S R) = YY(1 0) = y - (A P) STA YY+1 ; First we store the high byte A in YY+1 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 SYL,Y ; Then we do the low bytes with: SBC P ; STA YY ; YY+1 = A STA R ; R = YY = y_lo - P ; ; so we get this: ; ; (? R) = YY(1 0) = y_lo - (A P) LDA Y1 ; And then we do the high bytes with: SBC YY+1 ; STA YY+1 ; S = YY+1 = y_hi - YY+1 STA S ; ; so we get our result: ; ; (S R) = YY(1 0) = (y_hi y_lo) - (A P) ; = y - |y_hi| * Q ; ; which is result 3 above, and (S R) is set to the new ; value of y LDA SZL,Y ; We now calculate the following: ADC DELT4 ; STA SZL,Y ; (z_hi z_lo) = (z_hi z_lo) + DELT4(1 0) ; ; starting with the low bytes LDA SZ,Y ; And then we do the high bytes STA ZZ ; ADC DELT4+1 ; We also set ZZ to the original value of z_hi, which we STA SZ,Y ; use below to remove the existing particle ; ; So now we have result 4 above: ; ; z = z + DELT4(1 0) ; = z + speed * 64 LDA XX+1 ; EOR x with the correct sign of the roll angle alpha, EOR ALP2 ; so A has the opposite sign to the roll angle alpha JSR MLS1 ; Call MLS1 to calculate: ; ; (A P) = A * ALP1 ; = (-x / 256) * alpha JSR ADD ; Call ADD to calculate: ; ; (A X) = (A P) + (S R) ; = (-x / 256) * alpha + y ; = y - alpha * x / 256 STA YY+1 ; Set YY(1 0) = (A X) to give: STX YY ; ; YY(1 0) = y - alpha * x / 256 ; ; which is result 5 above, and we also have: ; ; A = YY+1 = y - alpha * x / 256 ; ; i.e. A is the new value of y, divided by 256 EOR ALP2+1 ; EOR with the flipped sign of the roll angle alpha, so ; A has the opposite sign to the flipped roll angle ; alpha, i.e. it gets the same sign as alpha JSR MLS2 ; Call MLS2 to calculate: ; ; (S R) = XX(1 0) ; = x ; ; (A P) = A * ALP1 ; = y / 256 * alpha JSR ADD ; Call ADD to calculate: ; ; (A X) = (A P) + (S R) ; = y / 256 * alpha + x STA XX+1 ; Set XX(1 0) = (A X), which gives us result 6 above: STX XX ; ; x = x + alpha * y / 256 LDA YY+1 ; Set A to y_hi and set it to the flipped sign of beta EOR BET2+1 LDX BET1 ; Fetch the pitch magnitude into X JSR MULTS-2 ; Call MULTS-2 to calculate: ; ; (A P) = X * A ; = beta * y_hi STA Q ; Store the high byte of the result in Q, so: ; ; Q = beta * y_hi / 256 LDA XX+1 ; Set S = x_hi STA S EOR #%10000000 ; Flip the sign of A, so A now contains -x JSR MUT1 ; Call MUT1 to calculate: ; ; R = XX = x_lo ; ; (A P) = Q * A ; = (beta * y_hi / 256) * (-beta * y_hi / 256) ; = (-beta * y / 256) ^ 2 ASL P ; Double (A P), store the top byte in A and set the C ROL A ; flag to bit 7 of the original A, so this does: STA T ; ; (T P) = (A P) << 1 ; = 2 * (-beta * y / 256) ^ 2 LDA #0 ; Set bit 7 in A to the sign bit from the A in the ROR A ; calculation above and apply it to T, so we now have: ORA T ; ; (A P) = -2 * (beta * y / 256) ^ 2 ; ; with the doubling retaining the sign of (A P) JSR ADD ; Call ADD to calculate: ; ; (A X) = (A P) + (S R) ; = -2 * (beta * y / 256) ^ 2 + x STA XX+1 ; Store the high byte A in XX+1 TXA ; Store the low byte X in x_lo STA SXL,Y ; So (XX+1 x_lo) now contains: ; ; x = x - 2 * (beta * y / 256) ^ 2 ; ; which is result 7 above LDA YY ; Set (S R) = YY(1 0) = y (low byte) STA R 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 YY+1 ; Set (S R) = YY(1 0) = y (high byte) STA S LDA #0 ; Set P = 0 STA P LDA BETA ; Set A = beta, so (A P) = (beta 0) = beta * 256 ; Calculate the following: ; ; (YY+1 y_lo) = (A P) + (S R) ; = -beta * 256 + y ; ; i.e. y = y - beta * 256, which is result 8 above JSR ADD ; Set (A X) = (A P) + (S R) STA YY+1 ; Set YY+1 to A, the high byte of the result TXA ; Set SYL+Y to X, the low byte of the result STA SYL,Y ; We now have our newly moved stardust particle at ; x-coordinate (XX+1 x_lo) and y-coordinate (YY+1 y_lo) ; and distance z_hi, so we draw it if it's still on ; screen, otherwise we recycle it as a new bit of ; stardust and draw that LDA XX+1 ; Set X1 and x_hi to the high byte of XX in XX+1, so STA X1 ; the new x-coordinate is in (x_hi x_lo) and the high STA SX,Y ; byte is in X1 LDA YY+1 ; Set Y1 and y_hi to the high byte of YY in YY+1, so STA SY,Y ; the new x-coordinate is in (y_hi y_lo) and the high STA Y1 ; byte is in Y1 AND #%01111111 ; If |y_hi| >= 110 then jump to KILL6 to recycle this CMP #110 ; particle, as it's gone off the top or bottom of the BCS KILL6 ; screen, and rejoin at STC6 with the new particle LDA SZ,Y ; If z_hi >= 160 then jump to star1 to recycle this CMP #160 ; particle, as it's so far away that it's too far to BCS star1 ; see, and rejoin at STC1 with the new particle STA ZZ ; Set ZZ to the z-coordinate in z_hi .STC6 JSR PIXEL2 ; Draw a stardust particle at (X1,Y1) with distance ZZ, ; i.e. draw the newly moved particle at (x_hi, y_hi) ; with distance z_hi DEY ; Decrement the loop counter to point to the next ; stardust particle BEQ ST3 ; If we have just done the last particle, skip the next ; instruction to return from the subroutine JMP STL6 ; We have more stardust to process, so jump back up to ; STL6 for the next particle .ST3 RTS ; Return from the subroutine .KILL6 JSR DORND ; Set A and X to random numbers AND #31 ; Clear the sign bit of A and set it to a random number ; in the range 0 to 31 ADC #10 ; Make sure A is at least 10 and store it in z_hi and STA SZ,Y ; ZZ, so the new particle starts close to us STA ZZ LSR A ; Divide A by 2 and randomly set the C flag BCS ST4 ; Jump to ST4 half the time LSR A ; Randomly set the C flag again LDA #224 ; Set A to either +112 or -112 (224 >> 1) depending on ROR A ; the C flag, as this is a sign-magnitude number with ; the C flag rotated into its sign bit STA X1 ; Set x_hi and X1 to A, so this particle starts on STA SX,Y ; either the left or right edge of the screen JSR DORND ; Set A and X to random numbers AND #%10111111 ; Clear bit 6 of A so A is in the range -63 to +63 STA Y1 ; Set y_hi and Y1 to random numbers, so the particle STA SY,Y ; starts anywhere along either the left or right edge JMP STC6 ; Jump up to STC6 to draw this new particle .star1 JSR DORND ; Set A and X to random numbers AND #%01111111 ; Clear the sign bit of A to get |A| ADC #10 ; Make sure A is at least 10 and store it in z_hi and STA SZ,Y ; ZZ, so the new particle starts close to us STA ZZ .ST4 JSR DORND ; Set A and X to random numbers AND #%11111001 ; Clear bits 1 and 2 of A so A is a random multiple of 8 ; in the range -120 to +120, randomly minus or plus 1 STA X1 ; Set x_hi and X1 to random numbers, so the particle STA SX,Y ; starts anywhere along the x-axis LSR A ; Randomly set the C flag LDA #216 ; Set A to either +108 or -108 (216 >> 1) depending on ROR A ; the C flag, as this is a sign-magnitude number with ; the C flag rotated into its sign bit STA Y1 ; Set y_hi and Y1 to A, so the particle starts anywhere STA SY,Y ; along either the top or bottom edge of the screen BNE STC6 ; Jump up to STC6 to draw this new particle (this BNE is ; effectively a JMP as A will never be zero)
Name: STARS2 Type: Subroutine Category: Stardust Summary: Process the stardust for the left or right view Deep dive: Stardust in the side views Sprite usage in NES Elite
Context: See this subroutine on its own page References: This subroutine is called as follows: * STARS calls STARS2

This moves the stardust sideways according to our speed and which side we are looking out of, and applies our current pitch and roll to each particle of dust, so the stardust moves correctly when we steer our ship. These are the calculations referred to in the commentary: 1. delta_x = 8 * 256 * speed / z_hi 2. x = x + delta_x 3. x = x + beta * y 4. y = y - beta * x 5. x = x - alpha * x * y 6. y = y + alpha * y * y + alpha For more information see the deep dive on "Stardust in the side views".
Arguments: X The view to process: * X = 1 for left view * X = 2 for right view
Name: yHangarFloor Type: Variable Category: Ship hangar Summary: Pixel y-coordinates for the four horizontal lines that make up the floor of the ship hangar
Context: See this variable on its own page References: This variable is used as follows: * HANGER uses yHangarFloor
.yHangarFloor EQUB 80 EQUB 88 EQUB 98 EQUB 120
Name: HANGER Type: Subroutine Category: Ship hangar Summary: Display the ship hangar
Context: See this subroutine on its own page References: This subroutine is called as follows: * HALL calls HANGER

This routine is called after the ships in the hangar have been drawn, so all it has to do is draw the hangar's background. The hangar background is made up of two parts: * The hangar floor consists of four screen-wide horizontal lines at the y-coordinates given in the yHangarFloor table, which are close together at the horizon and further apart as the eye moves down and towards us, giving the hangar a simple sense of perspective * The back wall of the hangar consists of equally spaced vertical lines that join the horizon to the top of the screen The ships in the hangar have already been drawn by this point, so the lines are drawn so they don't overlap anything that's already there, which makes them look like they are behind and below the ships. This is achieved by drawing the lines in from the screen edges until they bump into something already on-screen. For the horizontal lines, when there are multiple ships in the hangar, this also means drawing lines between the ships, as well as in from each side. This routine does a similar job to the routine of the same name in the BBC Master version of Elite, but the code is significantly different.
.HANGER ; We start by drawing the floor LDX #0 ; We are going to work our way through the four lines in ; the hangar floor, so .hang1 STX TGT ; Store the line number in TGT so we can retrieve it ; later LDA yHangarFloor,X ; Set Y to the pixel y-coordinate of the line, from the TAY ; yHangarFloor table LDA #8 ; Set A = 8 so the call to HAL3 draws a horizontal line ; that starts at pixel x-coordinate 8 (i.e. just inside ; the left box edge surrounding the view) LDX #28 ; Set X = 28 so the call to HAL3 draws a horizontal line ; of up to 28 blocks (i.e. almost the full screen width) JSR HAL3 ; Call HAL3 to draw a line from the left edge of the ; screen, going right until we bump into something ; already on-screen, at which point it stops drawing LDA #240 ; Set A = 240 so the call to HAS3 draws a horizontal ; line that starts at pixel x-coordinate 240 (i.e. just ; inside the right box edge surrounding the view) LDX #28 ; Set X = 28 so the call to HAS3 draws a horizontal line ; of up to 28 blocks (i.e. almost the full screen width) JSR HAS3 ; Draw a horizontal line from the right edge of the ; screen, going left until we bump into something ; already on-screen, at which point stop drawing LDA HANGFLAG ; Fetch the value of HANGFLAG, which gets set to 0 in ; the HALL routine above if there is only one ship BEQ hang2 ; If HANGFLAG is zero, jump to hang2 to skip the ; following as there is only one ship in the hangar ; If we get here then there are multiple ships in the ; hangar, so we also need to draw the horizontal line in ; the gap between the ships LDA #128 ; Set A = 128 so the call to HAL3 draws a horizontal ; line that starts at pixel x-coordinate 128 (i.e. ; from halfway across the screen) LDX #12 ; Set X = 12 so the call to HAL3 draws a horizontal line ; of up to 12 blocks, which will be enough to draw ; between the ships JSR HAL3 ; Call HAL3 to draw a line from the halfway point across ; the right half of the screen, going right until we ; bump into something already on-screen, at which point ; it stops drawing LDA #127 ; Set A = 127 so the call to HAS3 draws a horizontal ; line that starts at pixel x-coordinate 127 (i.e. ; just before the halfway point) LDX #12 ; Set X = 12 so the call to HAL3 draws a horizontal line ; of up to 12 blocks, which will be enough to draw ; between the ships JSR HAS3 ; Draw a horizontal line from the right edge of the ; screen, going left until we bump into something ; already on-screen, at which point stop drawing .hang2 ; We have finished threading our horizontal line behind ; the ships already on-screen, so now for the next line LDX TGT ; Set X to the number of the floor line we are drawing INX ; Increment X to move on to the next floor line CPX #4 ; Loop back to hang1 to draw the next floor line until BNE hang1 ; we have drawn all four ; The floor is done, so now we move on to the back wall JSR DORND ; Set A to a random number between 0 and 7, with bit 2 AND #7 ; set, to give a random number in the range 4 to 7, ORA #4 ; which we use as the x-coordinate of the first vertical ; line in the hangar wall LDY #0 ; Set Y = 0 so the call to DrawHangarWallLine starts ; drawing the wall lines in the first tile of the screen ; row, at the left edge of the screen .hang3 JSR DrawHangarWallLine ; Draw a vertical wall line at x-coordinate A CLC ; Add 10 to A ADC #10 BCS hang4 ; If adding 10 made the addition overflow then we have ; fallen off the right edge of the screen, so jump to ; hang4 to return from the subroutine CMP #248 ; Loop back until we have drawn lines all the way to the BCC hang3 ; right edge of the screen, not going further than an ; x-coordinate of 247 .hang4 RTS ; Return from the subroutine
Name: DrawHangarWallLine Type: Subroutine Category: Ship hangar Summary: Draw a vertical hangar wall line from top to bottom, stopping when it bumps into existing on-screen content
Context: See this subroutine on its own page References: This subroutine is called as follows: * HANGER calls DrawHangarWallLine
.DrawHangarWallLine STA S ; Store A in S so we can retrieve it when returning ; from the subroutine STY YSAV ; Store Y in YSAV so we can retrieve it when returning ; from the subroutine LSR A ; Set SC2(1 0) = (nameBufferHi 0) + yLookup(Y) + A / 8 LSR A ; LSR A ; where yLookup(Y) uses the (yLookupHi yLookupLo) table CLC ; to convert the pixel y-coordinate in Y into the number ADC yLookupLo,Y ; of the first tile on the row containing the pixel STA SC2 ; LDA nameBufferHi ; Adding nameBufferHi and A / 8 therefore sets SC2(1 0) ADC yLookupHi,Y ; to the address of the entry in the nametable buffer STA SC2+1 ; that contains the tile number for the tile containing ; the pixel at (A, Y), i.e. the start of the line we are ; drawing LDA S ; Set T = S mod 8, which is the pixel column within the AND #7 ; character block at which we want to draw the start of STA T ; our line (as each character block has 8 columns) ; ; As we are drawing a vertical line, we do not need to ; vary the value of T, as we will always want to draw on ; the same pixel column within each character block .hanw1 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 LDX #0 ; If the nametable buffer entry is zero for the tile LDA (SC2,X) ; containing the pixels that we want to draw, then a BEQ hanw3 ; pattern has not yet been allocated to this entry, so ; jump to hanw3 to allocate a new pattern LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC LDY #0 ; We want to start drawing the line from the top pixel ; line in the next character row, so set Y = 0 to use as ; the pixel row number LDX T ; Set X to the pixel column within the character block ; at which we want to draw our line, which we stored in ; T above .hanw2 LDA (SC),Y ; We now work out whether the pixel in column X would AND TWOS,X ; overlap with the top edge of the on-screen ship, which ; we do by AND'ing the pixel pattern with the on-screen ; pixel pattern in SC+Y, so if there are any pixels in ; both the pixel pattern and on-screen, they will be set ; in the result BNE hanw5 ; If the result is non-zero then our pixel in column X ; does indeed overlap with the on-screen ship, so we ; need to stop drawing our well line, so jump to hanw5 ; to return from the subroutine ; If we get here then our pixel in column X does not ; overlap with the on-screen ship, so we can draw it LDA (SC),Y ; Draw a pixel at x-coordinate X into the Y-th byte ORA TWOS,X ; of SC(1 0) STA (SC),Y INY ; Increment the y-coordinate in Y so we move down the ; line by one pixel CPY #8 ; If Y <> 8, loop back to hanw2 draw the next pixel as BNE hanw2 ; we haven't yet reached the bottom of the character ; block containing the line's top end JMP hanw4 ; Otherwise we have finished drawing the vertical line ; in this character row, so jump to hanw4 to move down ; to the next row .hanw3 LDA T ; Set A to the pixel column within the character block ; at which we want to draw our line, which we stored in ; T above CLC ; Patterns 52 to 59 contain pre-rendered patterns as ADC #52 ; follows: ; ; * Pattern 52 has a vertical line in pixel column 0 ; * Pattern 53 has a vertical line in pixel column 1 ; ... ; * Pattern 58 has a vertical line in pixel column 6 ; * Pattern 59 has a vertical line in pixel column 7 ; ; So A contains the pre-rendered pattern number that ; contains an 8-pixel line in pixel column T, and as T ; contains the offset of the pixel column for the line ; we are drawing, this means A contains the correct ; pattern number for this part of the line STA (SC2,X) ; Display the pre-rendered pattern on-screen by setting ; the nametable entry to A .hanw4 ; Next, we update SC2(1 0) to the address of the next ; row down in the nametable buffer, which we can do by ; adding 32 as there are 32 tiles in each row LDA SC2 ; Set SC2(1 0) = SC2(1 0) + 32 CLC ; ADC #32 ; Starting with the low bytes STA SC2 BCC hanw1 ; And then the high bytes, jumping to hanw1 when we are INC SC2+1 ; done to draw the vertical line on the next row JMP hanw1 .hanw5 LDA S ; Retrieve the value of A we stored above, so A is ; preserved LDY YSAV ; Retrieve the value of Y we stored above, so Y is ; preserved RTS ; Return from the subroutine
Name: HAL3 Type: Subroutine Category: Ship hangar Summary: Draw a hangar background line from left to right, stopping when it bumps into existing on-screen content
Context: See this subroutine on its own page References: This subroutine is called as follows: * HANGER calls HAL3

This routine does a similar job to the routine of the same name in the BBC Master version of Elite, but the code is significantly different.
.HAL3 STX R ; Set R to the line length in X STY YSAV ; Store Y in YSAV so we can retrieve it below LSR A ; Set SC2(1 0) = (nameBufferHi 0) + yLookup(Y) + A / 8 LSR A ; LSR A ; where yLookup(Y) uses the (yLookupHi yLookupLo) table CLC ; to convert the pixel y-coordinate in Y into the number ADC yLookupLo,Y ; of the first tile on the row containing the pixel STA SC2 ; LDA nameBufferHi ; Adding nameBufferHi and A / 8 therefore sets SC2(1 0) ADC yLookupHi,Y ; to the address of the entry in the nametable buffer STA SC2+1 ; that contains the tile number for the tile containing ; the pixel at (A, Y), i.e. the start of the line we are ; drawing TYA ; Set Y = Y mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw the start of TAY ; our line (as each character block has 8 rows) ; ; As we are drawing a horizontal line, we do not need to ; vary the value of Y, as we will always want to draw on ; the same pixel row within each character block .hanl1 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 LDX #0 ; If the nametable buffer entry is zero for the tile LDA (SC2,X) ; containing the pixels that we want to draw, then a BEQ hanl7 ; pattern has not yet been allocated to this entry, so ; jump to hanl7 to allocate a new pattern LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC LDA (SC),Y ; If the pattern data where we want to draw the line is BEQ hanl4 ; zero, then there is nothing currently on-screen at ; this point, so jump to hanl4 to draw a full 8-pixel ; line into the pattern data for this tile ; There is something on-screen where we want to draw our ; line, so we now draw the line until it bumps into ; what's already on-screen, so the floor line goes right ; up to the edge of the ship in the hangar LDA #%10000000 ; Set A to a pixel byte containing one set pixel at the ; left end of the 8-pixel row, which we can extend to ; the right by one pixel each time until it meets the ; edge of the on-screen ship .hanl2 STA T ; Store the current pixel pattern in T AND (SC),Y ; We now work out whether the pixel pattern in A would ; overlap with the edge of the on-screen ship, which we ; do by AND'ing the pixel pattern with the on-screen ; pixel pattern in SC+Y, so if there are any pixels in ; both the pixel pattern and on-screen, they will be set ; in the result BNE hanl3 ; If the result is non-zero then our pixel pattern in A ; does indeed overlap with the on-screen ship, so this ; is the pattern we want, so jump to hanl3 to draw it ; If we get here then our pixel pattern in A does not ; overlap with the on-screen ship, so we need to extend ; our pattern to the right by one pixel and try again LDA T ; Shift the whole pixel pattern to the right by one SEC ; pixel, shifting a set pixel into the left end (bit 7) ROR A JMP hanl2 ; Jump back to hanl2 to check whether our extended pixel ; pattern has reached the edge of the ship yet .hanl3 LDA T ; Draw our pixel pattern into the pattern buffer, using ORA (SC),Y ; OR logic so it overwrites what's already there and STA (SC),Y ; merges into the existing ship edge LDY YSAV ; Retrieve the value of Y we stored above, so Y is ; preserved RTS ; Return from the subroutine .hanl4 ; If we get here then we can draw a full 8-pixel wide ; horizontal line into the pattern data for the current ; tile, as there is nothing there already LDA #%11111111 ; Set A to a pixel byte containing eight pixels in a row STA (SC),Y ; Store the 8-pixel line in the Y-th entry in the ; pattern buffer .hanl5 DEC R ; Decrement the line length in R BEQ hanl6 ; If we have drawn all R blocks, jump to hanl6 to return ; from the subroutine INC SC2 ; Increment SC2(1 0) to point to the next nametable BNE hanl1 ; entry to the right, starting with the low byte, and ; if the increment didn't wrap the low byte round to ; zero, jump back to hanl1 to draw the next block of the ; horizontal line INC SC2+1 ; The low byte of SC2(1 0) incremented round to zero, so JMP hanl1 ; we also need to increment the high byte before jumping ; back to hanl1 to draw the next block of the horizontal ; line .hanl6 LDY YSAV ; Retrieve the value of Y we stored above, so Y is ; preserved RTS ; Return from the subroutine .hanl7 ; If we get here then there is no pattern allocated to ; the part of the line we want to draw, so we can use ; one of the pre-rendered patterns that contains an ; 8-pixel horizontal line on the correct pixel row ; ; We jump here with X = 0 TYA ; Set A = Y + 37 CLC ; ADC #37 ; Patterns 37 to 44 contain pre-rendered patterns as ; follows: ; ; * Pattern 37 has a horizontal line on pixel row 0 ; * Pattern 38 has a horizontal line on pixel row 1 ; ... ; * Pattern 43 has a horizontal line on pixel row 6 ; * Pattern 44 has a horizontal line on pixel row 7 ; ; So A contains the pre-rendered pattern number that ; contains an 8-pixel line on pixel row Y, and as Y ; contains the offset of the pixel row for the line we ; are drawing, this means A contains the correct pattern ; number for this part of the line STA (SC2,X) ; Display the pre-rendered pattern on-screen by setting ; the nametable entry to A JMP hanl5 ; Jump up to hanl5 to move on to the next character ; block to the right
Name: HAS3 Type: Subroutine Category: Ship hangar Summary: Draw a hangar background line from right to left
Context: See this subroutine on its own page References: This subroutine is called as follows: * HANGER calls HAS3

This routine does a similar job to the routine of the same name in the BBC Master version of Elite, but the code is significantly different.
.HAS3 STX R ; Set R to the line length in X STY YSAV ; Store Y in YSAV so we can retrieve it below LSR A ; Set SC2(1 0) = (nameBufferHi 0) + yLookup(Y) + A / 8 LSR A ; LSR A ; where yLookup(Y) uses the (yLookupHi yLookupLo) table CLC ; to convert the pixel y-coordinate in Y into the number ADC yLookupLo,Y ; of the first tile on the row containing the pixel STA SC2 ; LDA nameBufferHi ; Adding nameBufferHi and A / 8 therefore sets SC2(1 0) ADC yLookupHi,Y ; to the address of the entry in the nametable buffer STA SC2+1 ; that contains the tile number for the tile containing ; the pixel at (A, Y), i.e. the start of the line we are ; drawing TYA ; Set Y = Y mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw the start of TAY ; our line (as each character block has 8 rows) ; ; As we are drawing a horizontal line, we do not need to ; vary the value of Y, as we will always want to draw on ; the same pixel row within each character block .hanr1 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 LDX #0 ; If the nametable buffer entry is zero for the tile LDA (SC2,X) ; containing the pixels that we want to draw, then a BEQ hanr8 ; pattern has not yet been allocated to this entry, so ; jump to hanr8 to allocate a new pattern LDX pattBufferHiDiv8 ; Set SC(1 0) = (pattBufferHiDiv8 A) * 8 STX SC+1 ; = (pattBufferHi 0) + A * 8 ASL A ; ROL SC+1 ; So SC(1 0) is the address in the pattern buffer for ASL A ; pattern number A (as each pattern contains 8 bytes of ROL SC+1 ; pattern data), which means SC(1 0) points to the ASL A ; pattern data for the tile containing the line we are ROL SC+1 ; drawing STA SC LDA (SC),Y ; If the pattern data where we want to draw the line is BEQ hanr5 ; zero, then there is nothing currently on-screen at ; this point, so jump to hanr5 to draw a full 8-pixel ; line into the pattern data for this tile ; There is something on-screen where we want to draw our ; line, so we now draw the line until it bumps into ; what's already on-screen, so the floor line goes right ; up to the edge of the ship in the hangar LDA #%00000001 ; Set A to a pixel byte containing one set pixel at the ; right end of the 8-pixel row, which we can extend to ; the left by one pixel each time until it meets the ; edge of the on-screen ship .hanr2 STA T ; Store the current pixel pattern in T AND (SC),Y ; We now work out whether the pixel pattern in A would ; overlap with the edge of the on-screen ship, which we ; do by AND'ing the pixel pattern with the on-screen ; pixel pattern in SC+Y, so if there are any pixels in ; both the pixel pattern and on-screen, they will be set ; in the result BNE hanr3 ; If the result is non-zero then our pixel pattern in A ; does indeed overlap with the on-screen ship, so this ; is the pattern we want, so jump to hanr3 to draw it ; If we get here then our pixel pattern in A does not ; overlap with the on-screen ship, so we need to extend ; our pattern to the left by one pixel and try again LDA T ; Shift the whole pixel pattern to the left by one SEC ; pixel, shifting a set pixel into the right end (bit 0) ROL A JMP hanr2 ; Jump back to hanr2 to check whether our extended pixel ; pattern has reached the edge of the ship yet .hanr3 LDA T ; Draw our pixel pattern into the pattern buffer, using ORA (SC),Y ; OR logic so it overwrites what's already there and STA (SC),Y ; merges into the existing ship edge .hanr4 LDY YSAV ; Retrieve the value of Y we stored above, so Y is ; preserved RTS ; Return from the subroutine .hanr5 ; If we get here then we can draw a full 8-pixel wide ; horizontal line into the pattern data for the current ; tile, as there is nothing there already LDA #%11111111 ; Set A to a pixel byte containing eight pixels in a row STA (SC),Y ; Store the 8-pixel line in the Y-th entry in the ; pattern buffer .hanr6 DEC R ; Decrement the line length in R BEQ hanr4 ; If we have drawn all R blocks, jump to hanr4 to return ; from the subroutine LDA SC2 ; We now decrement SC2(1 0) to point to the next BNE hanr7 ; nametable entry to the left, so check whether the low ; byte of SC2(1 0) is non-zero, and if so jump to hanr7 ; to decrement it DEC SC2+1 ; Otherwise we also need to decrement the high byte ; before decrementing the low byte round to \$FF .hanr7 DEC SC2 ; Decrement the low byte of SC2(1 0) JMP hanr1 ; Jump back to hanr1 to draw the next block of the ; horizontal line .hanr8 ; If we get here then there is no pattern allocated to ; the part of the line we want to draw, so we can use ; one of the pre-rendered patterns that contains an ; 8-pixel horizontal line on the correct pixel row ; ; We jump here with X = 0 TYA ; Set A = Y + 37 CLC ; ADC #37 ; Patterns 37 to 44 contain pre-rendered patterns as ; follows: ; ; * Pattern 37 has a horizontal line on pixel row 0 ; * Pattern 38 has a horizontal line on pixel row 1 ; ... ; * Pattern 43 has a horizontal line on pixel row 6 ; * Pattern 44 has a horizontal line on pixel row 7 ; ; So A contains the pre-rendered pattern number that ; contains an 8-pixel line on pixel row Y, and as Y ; contains the offset of the pixel row for the line we ; are drawing, this means A contains the correct pattern ; number for this part of the line STA (SC2,X) ; Display the pre-rendered pattern on-screen by setting ; the nametable entry to A JMP hanr6 ; Jump up to hanr6 to move on to the next character ; block to the left
Name: HATB Type: Variable Category: Ship hangar Summary: Ship hangar group table
Context: See this variable on its own page References: This variable is used as follows: * HALL uses HATB

This table contains groups of ships to show in the ship hangar. A group of ships is shown half the time (the other half shows a solo ship), and each of the four groups is equally likely. The bytes for each ship in the group contain the following information: Byte #0 Non-zero = Ship type to draw 0 = don't draw anything Byte #1 Bits 0-7 = Ship's x_hi Bit 0 = Ship's z_hi (1 if clear, or 2 if set) Byte #2 Bits 0-7 = Ship's z_lo Bit 0 = Ship's x_sign The ship's y-coordinate is calculated in the has1 routine from the size of its targetable area. Ships of type 0 are not shown.
.HATB ; Hangar group for X = 0 ; ; Shuttle (left) and Transporter (right) EQUB 9 ; Ship type = 9 = Shuttle EQUB %01010100 ; x_hi = %01010100 = 84, z_hi = 1 -> x = -84 EQUB %00111011 ; z_lo = %00111011 = 59, x_sign = 1 z = +315 EQUB 10 ; Ship type = 10 = Transporter EQUB %10000010 ; x_hi = %10000010 = 130, z_hi = 1 -> x = +130 EQUB %10110000 ; z_lo = %10110000 = 176, x_sign = 0 z = +432 EQUB 0 ; No third ship EQUB 0 EQUB 0 ; Hangar group for X = 9 ; ; Three cargo canisters (left, far right and forward, ; right) EQUB OIL ; Ship type = OIL = Cargo canister EQUB %01010000 ; x_hi = %01010000 = 80, z_hi = 1 -> x = -80 EQUB %00010001 ; z_lo = %00010001 = 17, x_sign = 1 z = +273 EQUB OIL ; Ship type = OIL = Cargo canister EQUB %11010001 ; x_hi = %11010001 = 209, z_hi = 2 -> x = +209 EQUB %00101000 ; z_lo = %00101000 = 40, x_sign = 0 z = +552 EQUB OIL ; Ship type = OIL = Cargo canister EQUB %01000000 ; x_hi = %01000000 = 64, z_hi = 1 -> x = +64 EQUB %00000110 ; z_lo = %00000110 = 6, x_sign = 0 z = +262 ; Hangar group for X = 18 ; ; Viper (right) and Krait (left) EQUB COPS ; Ship type = COPS = Viper EQUB %01100000 ; x_hi = %01100000 = 96, z_hi = 1 -> x = +96 EQUB %10010000 ; z_lo = %10010000 = 144, x_sign = 0 z = +400 EQUB KRA ; Ship type = KRA = Krait EQUB %00010000 ; x_hi = %00010000 = 16, z_hi = 1 -> x = -16 EQUB %11010001 ; z_lo = %11010001 = 209, x_sign = 1 z = +465 EQUB 0 ; No third ship EQUB 0 EQUB 0 ; Hangar group for X = 27 ; ; Viper (right and forward) and Krait (left) EQUB 16 ; Ship type = 16 = Viper EQUB %01010001 ; x_hi = %01010001 = 81, z_hi = 2 -> x = +81 EQUB %11111000 ; z_lo = %11111000 = 248, x_sign = 0 z = +760 EQUB 19 ; Ship type = 19 = Krait EQUB %01100000 ; x_hi = %01100000 = 96, z_hi = 1 -> x = -96 EQUB %01110101 ; z_lo = %01110101 = 117, x_sign = 1 z = +373 EQUB 0 ; No third ship EQUB 0 EQUB 0
Name: HALL Type: Subroutine Category: Ship hangar Summary: Draw the ships in the ship hangar, then draw the hangar
Context: See this subroutine on its own page References: This subroutine is called as follows: * HALL_b1 calls HALL

Half the time this will draw one of the four pre-defined ship hangar groups in HATB, and half the time this will draw a solitary Sidewinder, Mamba, Krait or Adder on a random position. In all cases, the ships will be randomly spun around on the ground so they can face in any direction, and larger ships are drawn higher up off the ground than smaller ships.
.HALL LDA #\$00 ; Clear the screen and set the view type in QQ11 to \$00 JSR TT66_b0 ; (Space view with no fonts loaded) LDA nmiCounter ; Set the random number seeds to a fairly random state STA RAND+1 ; that's based on the NMI counter (which increments LDA #\$86 ; every VBlank, so will be pretty random), the current STA RAND+3 ; system's galactic x-coordinate (QQ0), the high byte LDA QQ0 ; of our combat rank (TALLY+1), and a fixed number \$86 STA RAND LDA TALLY+1 STA RAND+2 JSR DORND ; Set A and X to random numbers BPL HA7 ; Jump to HA7 if A is positive (50% chance) AND #3 ; Reduce A to a random number in the range 0-3 STA T ; Set X = A * 8 + A ASL A ; = 9 * A ASL A ; ASL A ; so X is a random number, either 0, 9, 18 or 27 ADC T TAX ; The following double loop calls the HAS1 routine three ; times to display three ships on screen. For each call, ; the values passed to HAS1 in XX15+2 to XX15 are taken ; from the HATB table, depending on the value in X, as ; follows: ; ; * If X = 0, pass bytes #0 to #2 of HATB to HAS1 ; then bytes #3 to #5 ; then bytes #6 to #8 ; ; * If X = 9, pass bytes #9 to #11 of HATB to HAS1 ; then bytes #12 to #14 ; then bytes #15 to #17 ; ; * If X = 18, pass bytes #18 to #20 of HATB to HAS1 ; then bytes #21 to #23 ; then bytes #24 to #26 ; ; * If X = 27, pass bytes #27 to #29 of HATB to HAS1 ; then bytes #30 to #32 ; then bytes #33 to #35 ; ; Note that the values are passed in reverse, so for the ; first call, for example, where we pass bytes #0 to #2 ; of HATB to HAS1, we call HAS1 with: ; ; XX15 = HATB+2 ; XX15+1 = HATB+1 ; XX15+2 = HATB LDY #3 ; Set CNT2 = 3 to act as an outer loop counter going STY CNT2 ; from 3 to 1, so the HAL8 loop is run 3 times .HAL8 LDY #2 ; Set Y = 2 to act as an inner loop counter going from ; 2 to 0 .HAL9 LDA HATB,X ; Copy the X-th byte of HATB to the Y-th byte of XX15, STA XX15,Y ; as described above INX ; Increment X to point to the next byte in HATB DEY ; Decrement Y to point to the previous byte in XX15 BPL HAL9 ; Loop back to copy the next byte until we have copied ; three of them (i.e. Y was 3 before the DEY) TXA ; Store X on the stack so we can retrieve it after the PHA ; call to HAS1 (as it contains the index of the next ; byte in HATB JSR HAS1 ; Call HAS1 to draw this ship in the hangar PLA ; Restore the value of X, so X points to the next byte TAX ; in HATB after the three bytes we copied into XX15 DEC CNT2 ; Decrement the outer loop counter in CNT2 BNE HAL8 ; Loop back to HAL8 to do it 3 times, once for each ship ; in the HATB table LDY #128 ; Set Y = 128 to send as byte #2 of the parameter block ; to the OSWORD 248 command below, to tell the I/O ; processor that there are multiple ships in the hangar BNE HA9 ; Jump to HA9 to display the ship hangar (this BNE is ; effectively a JMP as Y is never zero) .HA7 ; If we get here, A is a positive random number in the ; range 0-127 LSR A ; Set XX15+1 = A / 2 (random number 0-63) STA XX15+1 JSR DORND ; Set XX15 = random number 0-255 STA XX15 JSR DORND ; Set XX15+2 = #SH3 + random number 0-3 AND #3 ; ADC #SH3 ; which is the ship type of a Sidewinder, Mamba, Krait STA XX15+2 ; or Adder JSR HAS1 ; Call HAS1 to draw this ship in the hangar, with the ; following properties: ; ; * Random x-coordinate from -63 to +63 ; ; * Randomly chosen Sidewinder, Mamba, Krait or Adder ; ; * Random z-coordinate from +256 to +639 LDY #0 ; Set Y = 0 to use in the following instruction, to tell ; the hangar-drawing routine that there is just one ship ; in the hangar, so it knows not to draw between the ; ships .HA9 STY HANGFLAG ; Store Y in HANGFLAG to specify whether there are ; multiple ships in the hangar JSR HANGER ; Call HANGER to draw the hangar background LDA #0 ; Tell the NMI handler to send pattern entries from STA firstPattern ; pattern 0 in the buffer LDA #80 ; Tell the NMI handler to only clear nametable entries STA maxNameTileToClear ; up to tile 80 * 8 = 640 (i.e. up to the end of tile ; row 19) JMP UpdateHangarView ; Update the hangar view on-screen by sending the data ; to the PPU, returning from the subroutine using a tail ; call
Name: ZINF_b1 Type: Subroutine Category: Universe Summary: Reset the INWK workspace and orientation vectors Deep dive: Orientation vectors
Context: See this subroutine on its own page References: This subroutine is called as follows: * HAS1 calls ZINF_b1

Zero-fill the INWK ship workspace and reset the orientation vectors, with nosev pointing out of the screen, towards us.
Returns: Y Y is set to \$FF
.ZINF_b1 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 LDY #NI%-1 ; There are NI% bytes in the INWK workspace, so set a ; counter in Y so we can loop through them LDA #0 ; Set A to 0 so we can zero-fill the workspace .ZI1_b1 STA INWK,Y ; Zero the Y-th byte of the INWK workspace DEY ; Decrement the loop counter BPL ZI1_b1 ; Loop back for the next byte, ending when we have ; zero-filled the last byte at INWK, which leaves Y ; with a value of \$FF ; Finally, we reset the orientation vectors as follows: ; ; sidev = (1, 0, 0) ; roofv = (0, 1, 0) ; nosev = (0, 0, -1) ; ; 96 * 256 (\$6000) represents 1 in the orientation ; vectors, while -96 * 256 (\$E000) represents -1. We ; already set the vectors to zero above, so we just ; need to set up the high bytes of the diagonal values ; and we're done. The negative nosev makes the ship ; point towards us, as the z-axis points into the screen 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 #96 ; Set A to represent a 1 (in vector terms) STA INWK+18 ; Set byte #18 = roofv_y_hi = 96 = 1 STA INWK+22 ; Set byte #22 = sidev_x_hi = 96 = 1 ORA #128 ; Flip the sign of A to represent a -1 STA INWK+14 ; Set byte #14 = nosev_z_hi = -96 = -1 RTS ; Return from the subroutine
Name: HAS1 Type: Subroutine Category: Ship hangar Summary: Draw a ship in the ship hangar
Context: See this subroutine on its own page References: This subroutine is called as follows: * HALL calls HAS1

The ship's position within the hangar is determined by the arguments and the size of the ship's targetable area, as follows: * The x-coordinate is (x_sign x_hi 0) from the arguments, so the ship can be left of centre or right of centre * The y-coordinate is negative and is lower down the screen for smaller ships, so smaller ships are drawn closer to the ground (because they are) * The z-coordinate is positive, with both z_hi (which is 1 or 2) and z_lo coming from the arguments
Arguments: XX15 Bits 0-7 = Ship's z_lo Bit 0 = Ship's x_sign XX15+1 Bits 0-7 = Ship's x_hi Bit 0 = Ship's z_hi (1 if clear, or 2 if set) XX15+2 Non-zero = Ship type to draw 0 = Don't draw anything
.HAS1 JSR ZINF_b1 ; Call ZINF to reset the INWK ship workspace and reset ; the orientation vectors, with nosev pointing out of ; the screen, so this puts the ship flat on the ; horizontal deck (the y = 0 plane) with its nose ; pointing towards us LDA XX15 ; Set z_lo = XX15 STA INWK+6 LSR A ; Set the sign bit of x_sign to bit 0 of A ROR INWK+2 LDA XX15+1 ; Set x_hi = XX15+1 STA INWK LSR A ; Set z_hi = 1 + bit 0 of XX15+1 LDA #1 ADC #0 STA INWK+7 LDA #%10000000 ; Set bit 7 of y_sign, so y is negative STA INWK+5 STA RAT2 ; Set RAT2 = %10000000, so the yaw calls in HAL5 below ; are negative LDA #\$B ; This instruction is left over from the other versions STA INWK+34 ; of Elite, which store the ship line heap pointer in ; INWK(34 33), but the NES version doesn't have a ship ; line heap, so this instruction has no effect (INWK+34 ; is reused in NES Elite for the ship's explosion cloud ; counter, but that is ignored by the hangar code) JSR DORND ; We now perform a random number of small angle (3.6 STA XSAV ; degree) rotations to spin the ship on the deck while ; keeping it flat on the deck (a bit like spinning a ; bottle), so we set XSAV to a random number between 0 ; and 255 for the number of small yaw rotations to ; perform, so the ship could be pointing in any ; direction by the time we're done .HAL5 LDX #21 ; Rotate (sidev_x, nosev_x) by a small angle (yaw) LDY #9 JSR MVS5_b0 LDX #23 ; Rotate (sidev_y, nosev_y) by a small angle (yaw) LDY #11 JSR MVS5_b0 LDX #25 ; Rotate (sidev_z, nosev_z) by a small angle (yaw) LDY #13 JSR MVS5_b0 DEC XSAV ; Decrement the yaw counter in XSAV BNE HAL5 ; Loop back to yaw a little more until we have yawed ; by the number of times in XSAV LDY XX15+2 ; Set Y = XX15+2, the ship type of the ship we need to ; draw BEQ HA1 ; If Y = 0, return from the subroutine (as HA1 contains ; an RTS) TYA ; Set X = 2 * Y ASL A TAX LDA XX21-2,X ; Set XX0(1 0) to the X-th address in the ship blueprint STA XX0 ; address lookup table at XX21, so XX0(1 0) now points LDA XX21-1,X ; to the blueprint for the ship we need to draw STA XX0+1 BEQ HA1 ; If the high byte of the blueprint address is 0, then ; this is not a valid blueprint address, so return from ; the subroutine (as HA1 contains an RTS) LDY #1 ; Set Q = ship byte #1 LDA (XX0),Y STA Q INY ; Set R = ship byte #2 LDA (XX0),Y ; STA R ; so (R Q) contains the ship's targetable area, which is ; a square number JSR LL5 ; Set Q = SQRT(R Q) LDA #100 ; Set y_lo = (100 - Q) / 2 SBC Q ; LSR A ; so the bigger the ship's targetable area, the smaller STA INWK+3 ; the magnitude of the y-coordinate, so because we set ; y_sign to be negative above, this means smaller ships ; are drawn lower down, i.e. closer to the ground, while ; larger ships are drawn higher up, as you would expect JSR TIDY ; Call TIDY to tidy up the orientation vectors, to ; prevent the ship from getting elongated and out of ; shape due to the imprecise nature of trigonometry ; in assembly language JMP LL9 ; Jump to LL9 to display the ship and return from the ; subroutine using a tail call .HA1 RTS ; Return from the subroutine
Name: TIDY Type: Subroutine Category: Maths (Geometry) Summary: Orthonormalise the orientation vectors for a ship Deep dive: Tidying orthonormal vectors Orientation vectors
Context: See this subroutine on its own page References: This subroutine is called as follows: * HAS1 calls TIDY * TIDY_b1 calls TIDY

This routine orthonormalises the orientation vectors for a ship. This means making the three orientation vectors orthogonal (perpendicular to each other), and normal (so each of the vectors has length 1). We do this because we use the small angle approximation to rotate these vectors in space. It is not completely accurate, so the three vectors tend to get stretched over time, so periodically we tidy the vectors with this routine to ensure they remain as orthonormal as possible.
.TI2 ; Called from below with A = 0, X = 0, Y = 4 when ; nosev_x and nosev_y are small, so we assume that ; nosev_z is big TYA ; A = Y = 4 LDY #2 JSR TIS3 ; Call TIS3 with X = 0, Y = 2, A = 4, to set roofv_z = STA INWK+20 ; -(nosev_x * roofv_x + nosev_y * roofv_y) / nosev_z JMP TI3 ; Jump to TI3 to keep tidying .TI1 ; Called from below with A = 0, Y = 4 when nosev_x is ; small TAX ; Set X = A = 0 LDA XX15+1 ; Set A = nosev_y, and if the top two magnitude bits AND #%01100000 ; are both clear, jump to TI2 with A = 0, X = 0, Y = 4 BEQ TI2 LDA #2 ; Otherwise nosev_y is big, so set up the index values ; to pass to TIS3 JSR TIS3 ; Call TIS3 with X = 0, Y = 4, A = 2, to set roofv_y = STA INWK+18 ; -(nosev_x * roofv_x + nosev_z * roofv_z) / nosev_y JMP TI3 ; Jump to TI3 to keep tidying .TIDY 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 INWK+10 ; Set (XX15, XX15+1, XX15+2) = nosev STA XX15 LDA INWK+12 STA XX15+1 LDA INWK+14 STA XX15+2 JSR NORM ; Call NORM to normalise the vector in XX15, i.e. nosev LDA XX15 ; Set nosev = (XX15, XX15+1, XX15+2) STA INWK+10 LDA XX15+1 STA INWK+12 LDA XX15+2 STA INWK+14 LDY #4 ; Set Y = 4 LDA XX15 ; Set A = nosev_x, and if the top two magnitude bits AND #%01100000 ; are both clear, jump to TI1 with A = 0, Y = 4 BEQ TI1 LDX #2 ; Otherwise nosev_x is big, so set up the index values LDA #0 ; to pass to TIS3 JSR TIS3 ; Call TIS3 with X = 2, Y = 4, A = 0, to set roofv_x = STA INWK+16 ; -(nosev_y * roofv_y + nosev_z * roofv_z) / nosev_x .TI3 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 INWK+16 ; Set (XX15, XX15+1, XX15+2) = roofv STA XX15 LDA INWK+18 STA XX15+1 LDA INWK+20 STA XX15+2 JSR NORM ; Call NORM to normalise the vector in XX15, i.e. roofv LDA XX15 ; Set roofv = (XX15, XX15+1, XX15+2) STA INWK+16 LDA XX15+1 STA INWK+18 LDA XX15+2 STA INWK+20 LDA INWK+12 ; Set Q = nosev_y STA Q LDA INWK+20 ; Set A = roofv_z JSR MULT12 ; Set (S R) = Q * A = nosev_y * roofv_z LDX INWK+14 ; Set X = nosev_z LDA INWK+18 ; Set A = roofv_y JSR TIS1 ; Set (A ?) = (-X * A + (S R)) / 96 ; = (-nosev_z * roofv_y + nosev_y * roofv_z) / 96 ; ; This also sets Q = nosev_z EOR #%10000000 ; Set sidev_x = -A STA INWK+22 ; = (nosev_z * roofv_y - nosev_y * roofv_z) / 96 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 INWK+16 ; Set A = roofv_x JSR MULT12 ; Set (S R) = Q * A = nosev_z * roofv_x LDX INWK+10 ; Set X = nosev_x LDA INWK+20 ; Set A = roofv_z JSR TIS1 ; Set (A ?) = (-X * A + (S R)) / 96 ; = (-nosev_x * roofv_z + nosev_z * roofv_x) / 96 ; ; This also sets Q = nosev_x EOR #%10000000 ; Set sidev_y = -A STA INWK+24 ; = (nosev_x * roofv_z - nosev_z * roofv_x) / 96 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 INWK+18 ; Set A = roofv_y JSR MULT12 ; Set (S R) = Q * A = nosev_x * roofv_y LDX INWK+12 ; Set X = nosev_y LDA INWK+16 ; Set A = roofv_x JSR TIS1 ; Set (A ?) = (-X * A + (S R)) / 96 ; = (-nosev_y * roofv_x + nosev_x * roofv_y) / 96 EOR #%10000000 ; Set sidev_z = -A STA INWK+26 ; = (nosev_y * roofv_x - nosev_x * roofv_y) / 96 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 #0 ; Set A = 0 so we can clear the low bytes of the ; orientation vectors LDX #14 ; We want to clear the low bytes, so start from sidev_y ; at byte #9+14 (we clear all except sidev_z_lo, though ; I suspect this is in error and that X should be 16) .TIL1 STA INWK+9,X ; Set the low byte in byte #9+X to zero DEX ; Set X = X - 2 to jump down to the next low byte DEX BPL TIL1 ; Loop back until we have zeroed all the low bytes RTS ; Return from the subroutine
Name: TIS3 Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate -(nosev_1 * roofv_1 + nosev_2 * roofv_2) / nosev_3
Context: See this subroutine on its own page References: This subroutine is called as follows: * TIDY calls TIS3

Calculate the following expression: A = -(nosev_1 * roofv_1 + nosev_2 * roofv_2) / nosev_3 where 1, 2 and 3 are x, y, or z, depending on the values of X, Y and A. This routine is called with the following values: X = 0, Y = 2, A = 4 -> A = -(nosev_x * roofv_x + nosev_y * roofv_y) / nosev_z X = 0, Y = 4, A = 2 -> A = -(nosev_x * roofv_x + nosev_z * roofv_z) / nosev_y X = 2, Y = 4, A = 0 -> A = -(nosev_y * roofv_y + nosev_z * roofv_z) / nosev_x
Arguments: X Index 1 (0 = x, 2 = y, 4 = z) Y Index 2 (0 = x, 2 = y, 4 = z) A Index 3 (0 = x, 2 = y, 4 = z)
.TIS3 STA P+2 ; Store P+2 in A for later LDA INWK+10,X ; Set Q = nosev_x_hi (plus X) STA Q LDA INWK+16,X ; Set A = roofv_x_hi (plus X) JSR MULT12 ; Set (S R) = Q * A ; = nosev_x_hi * roofv_x_hi LDX INWK+10,Y ; Set Q = nosev_x_hi (plus Y) STX Q LDA INWK+16,Y ; Set A = roofv_x_hi (plus Y) JSR MAD ; Set (A X) = Q * A + (S R) ; = (nosev_x,X * roofv_x,X) + ; (nosev_x,Y * roofv_x,Y) STX P ; Store low byte of result in P, so result is now in ; (A P) LDY P+2 ; Set Q = roofv_x_hi (plus argument A) LDX INWK+10,Y STX Q EOR #%10000000 ; Flip the sign of A ; Fall through into DIVDT to do: ; ; (P+1 A) = (A P) / Q ; ; = -((nosev_x,X * roofv_x,X) + ; (nosev_x,Y * roofv_x,Y)) ; / nosev_x,A
Name: DVIDT Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate (P+1 A) = (A P) / Q
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

Calculate the following integer division between sign-magnitude numbers: (P+1 A) = (A P) / Q This uses the same shift-and-subtract algorithm as TIS2.
.DVIDT STA P+1 ; Set P+1 = A, so P(1 0) = (A P) EOR Q ; Set T = the sign bit of A EOR Q, so it's 1 if A and Q AND #%10000000 ; have different signs, i.e. it's the sign of the result STA T ; of A / Q LDA #0 ; Set A = 0 for us to build a result LDX #16 ; Set a counter in X to count the 16 bits in P(1 0) ASL P ; Shift P(1 0) left ROL P+1 ASL Q ; Clear the sign bit of Q the C flag at the same time LSR Q .DVL2 ROL A ; Shift A to the left CMP Q ; If A < Q skip the following subtraction BCC P%+4 SBC Q ; Set A = A - Q ; ; Going into this subtraction we know the C flag is ; set as we passed through the BCC above, and we also ; know that A >= Q, so the C flag will still be set once ; we are done ROL P ; Rotate P(1 0) to the left, and catch the result bit ROL P+1 ; into the C flag (which will be a 0 if we didn't ; do the subtraction, or 1 if we did) DEX ; Decrement the loop counter BNE DVL2 ; Loop back for the next bit until we have done all 16 ; bits of P(1 0) LDA P ; Set A = P so the low byte is in the result in A ORA T ; Set A to the correct sign bit that we set in T above RTS ; Return from the subroutine
Name: SCAN Type: Subroutine Category: Dashboard Summary: Display the current ship on the scanner Deep dive: Sprite usage in NES Elite
Context: See this subroutine on its own page References: This subroutine is called as follows: * SCAN_b1 calls SCAN

This routine does a similar job to the routine of the same name in the BBC Master version of Elite, but the code is significantly different.
Arguments: INWK The ship's data block
Name: HideShip Type: Subroutine Category: Dashboard Summary: Update the current ship so it is no longer shown on the scanner
Context: See this subroutine on its own page References: This subroutine is called as follows:
.HideShip LDA #0 ; Zero byte #33 in the current ship's data block at K%, LDY #33 ; so it is not shown on the scanner (a non-zero byte #33 STA (INF),Y ; represents the ship's number on the scanner, with a ; ship number of zero indicating that the ship is not ; shown on the scanner) ; Fall through into HideFromScanner to hide the scanner ; sprites for this ship and reset byte #33 in the INWK ; workspace
Name: HideFromScanner Type: Subroutine Category: Dashboard Summary: Hide the current ship from the scanner
Context: See this subroutine on its own page References: This subroutine is called as follows:
.HideFromScanner 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 LDX INWK+33 ; Set X to the number of the current ship on the ; scanner, which is in ship data byte #33 BEQ hide2 ; If byte #33 for the current ship is zero, then the ; ship doesn't appear on the scanner, so jump to hide2 ; to return from the subroutine as there is nothing to ; hide LDA #0 ; Otherwise we need to hide this ship, so we start by STA scannerNumber,X ; zeroing the scannerNumber entry for ship number X, so ; it no longer has an allocated scanner number ; We now hide the three sprites used to show this ship ; on the scanner ; ; There are four data bytes for each sprite in the ; sprite buffer, and there are three sprites used to ; display each ship on the scanner, so we start by ; calculating the offset of the sprite data for this ; ship's scanner sprites TXA ; Set X = (X * 2 + X) * 4 ASL A ; = (3 * X) * 4 ADC INWK+33 ; ASL A ; So X is the index of the sprite buffer data for the ASL A ; three sprites for ship number X on the scanner TAX LDA QQ11 ; If this is not the space view, jump to hide1 as the BNE hide1 ; dashboard is only shown in the space view LDA #240 ; Set A to the y-coordinate that's just below the bottom ; of the screen, so we can hide the required sprites by ; moving them off-screen STA ySprite11,X ; Hide the three scanner sprites for ship number X, so STA ySprite12,X ; the current ship is no longer shown on the scanner STA ySprite13,X ; (the first ship on the scanner, ship number 1, uses ; the three sprites at 14, 15 and 16 in the buffer, and ; each sprite has four bytes in the buffer, so we can ; get the sprite numbers by adding X, which contains the ; offset within the sprite buffer, to the addresses of ; sprites 11, 12 and 13) .hide1 LDA #0 ; Zero the current ship's byte #33 in INWK, so that it STA INWK+33 ; no longer has a ship number on the scanner (a non-zero ; byte #33 represents the ship's number on the scanner, ; but a ship number of zero indicates that the ship is ; not shown on the scanner) .hide2 RTS ; Return from the subroutine
Name: DrawExplosionBurst Type: Subroutine Category: Drawing ships Summary: Draw an exploding ship along with an explosion burst made up of colourful sprites
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOEXP calls DrawExplosionBurst