Skip to navigation

Elite on the BBC Micro and NES

Bank 1 (Part 2 of 3)

[NES version]

Name: LL61 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate (U R) = 256 * A / Q
Context: See this subroutine on its own page References: This subroutine is called as follows: * LL9 (Part 8 of 12) calls LL61

Calculate the following, where A >= Q: (U R) = 256 * A / Q This is a sister routine to LL28, which does the division when A < Q.
.LL61 LDX Q ; If Q = 0, jump down to LL84 to return a division BEQ LL84 ; error ; The LL28 routine returns A / Q, but only if A < Q. In ; our case A >= Q, but we still want to use the LL28 ; routine, so we halve A until it's less than Q, call ; the division routine, and then double A by the same ; number of times LDX #0 ; Set X = 0 to count the number of times we halve A .LL63 LSR A ; Halve A by shifting right INX ; Increment X CMP Q ; If A >= Q, loop back to LL63 to halve it again BCS LL63 STX S ; Otherwise store the number of times we halved A in S JSR LL28 ; Call LL28 to calculate: ; ; R = 256 * A / Q ; ; which we can do now as A < Q LDX S ; Otherwise restore the number of times we halved A ; above into X LDA R ; Set A = our division result .LL64 ASL A ; Double (U A) by shifting left ROL U BMI LL84 ; If bit 7 of U is set, the doubling has overflowed, so ; jump to LL84 to return a division error DEX ; Decrement X BNE LL64 ; If X is not yet zero then we haven't done as many ; doublings as we did halvings earlier, so loop back for ; another doubling STA R ; Store the low byte of the division result in R RTS ; Return from the subroutine .LL84 LDA #50 ; If we get here then either we tried to divide by 0, or STA R ; the result overflowed, so we set U and R to 50 STA U RTS ; Return from the subroutine
Name: LL62 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate 128 - (U R)
Context: See this subroutine on its own page References: This subroutine is called as follows: * LL9 (Part 8 of 12) calls LL62

Calculate the following for a positive sign-magnitude number (U R): 128 - (U R) and then store the result, low byte then high byte, on the end of the heap at XX3, where X points to the first free byte on the heap. Return by jumping down to LL66.
Returns: X X is incremented by 1
.LL62 LDA #128 ; Calculate 128 - (U R), starting with the low bytes SEC SBC R STA XX3,X ; Store the low byte of the result in the X-th byte of ; the heap at XX3 INX ; Increment the heap pointer in X to point to the next ; byte LDA #0 ; And then subtract the high bytes SBC U STA XX3,X ; Store the low byte of the result in the X-th byte of ; the heap at XX3 JMP LL66 ; Jump down to LL66
Name: LL9 (Part 7 of 12) [Show more] Type: Subroutine Category: Drawing ships Summary: Draw ship: Calculate the visibility of each of the ship's vertices Deep dive: Drawing ships Calculating vertex coordinates
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This section continues the coordinate adding from part 6 by finishing off the calculation that we started above: [ sidev_x roofv_x nosev_x ] [ x ] [ x ] vector to vertex = [ sidev_y roofv_y nosev_y ] . [ y ] + [ y ] [ sidev_z roofv_z nosev_z ] [ z ] [ z ] The gets stored as follows, in sign-magnitude values with the magnitudes fitting into the low bytes: XX15(2 0) [ x y z ] . [ sidev_x roofv_x nosev_x ] + [ x y z ] XX15(5 3) [ x y z ] . [ sidev_y roofv_y nosev_y ] + [ x y z ] (U T) [ x y z ] . [ sidev_z roofv_z nosev_z ] + [ x y z ] Finally, because this vector is from our ship to the vertex, and we are at the origin, this vector is the same as the coordinates of the vertex. In other words, we have just worked out: XX15(2 0) x-coordinate of the current vertex XX15(5 3) y-coordinate of the current vertex (U T) z-coordinate of the current vertex
.LL56 LDA XX1+6 ; Set (U T) = XX1(7 6) - XX12(5 4) SEC ; = (z_hi z_lo) - vertv_z SBC XX12+4 ; STA T ; Starting with the low bytes LDA XX1+7 ; And then doing the high bytes (we can subtract 0 here SBC #0 ; as we know the sign byte of vertv_z is 0) STA U BCC LL140 ; If the subtraction just underflowed, skip to LL140 to ; set (U T) to the minimum value of 4 BNE LL57 ; If U is non-zero, jump down to LL57 LDA T ; If T >= 4, jump down to LL57 CMP #4 BCS LL57 .LL140 LDA #0 ; If we get here then either (U T) < 4 or the STA U ; subtraction underflowed, so set (U T) = 4 LDA #4 STA T .LL57 ; By this point we have our results, so now to scale ; the 16-bit results down into 8-bit values LDA U ; If the high bytes of the result are all zero, we are ORA XX15+1 ; done, so jump down to LL60 for the next stage ORA XX15+4 BEQ LL60 LSR XX15+1 ; Shift XX15(1 0) to the right ROR XX15 LSR XX15+4 ; Shift XX15(4 3) to the right ROR XX15+3 LSR U ; Shift (U T) to the right ROR T JMP LL57 ; Jump back to LL57 to see if we can shift the result ; any more
Name: LL9 (Part 8 of 12) [Show more] Type: Subroutine Category: Drawing ships Summary: Draw ship: Calculate the screen coordinates of visible vertices Deep dive: Drawing ships
Context: See this subroutine on its own page References: This subroutine is called as follows: * LL62 calls via LL66

This section projects the coordinate of the vertex into screen coordinates and stores them on the XX3 heap. By the end of this part, the XX3 heap contains four bytes containing the 16-bit screen coordinates of the current vertex, in the order: x_lo, x_hi, y_lo, y_hi. When we reach here, we are looping through the vertices, and we've just worked out the coordinates of the vertex in our normal coordinate system, as follows XX15(2 0) (x_sign x_lo) = x-coordinate of the current vertex XX15(5 3) (y_sign y_lo) = y-coordinate of the current vertex (U T) (z_sign z_lo) = z-coordinate of the current vertex Note that U is always zero when we get to this point, as the vertex is always in front of us (so it has a positive z-coordinate, into the screen).
Other entry points: LL70+1 Contains an RTS (as the first byte of an LDA instruction) LL66 A re-entry point into the ship-drawing routine, used by the LL62 routine to store 128 - (U R) on the XX3 heap
.LL60 LDA T ; Set Q = z_lo STA Q LDA XX15 ; Set A = x_lo CMP Q ; If x_lo < z_lo jump to LL69 BCC LL69 JSR LL61 ; Call LL61 to calculate: ; ; (U R) = 256 * A / Q ; = 256 * x / z ; ; which we can do as x >= z JMP LL69+3 ; Jump over the next instruction to skip the division ; for x_lo < z_lo .LL69 JSR LL28 ; Call LL28 to calculate: ; ; R = 256 * A / Q ; = 256 * x / z ; ; Because x < z, the result fits into one byte, and we ; also know that U = 0, so (U R) also contains the ; result ; At this point we have: ; ; (U R) = x / z ; ; so (U R) contains the vertex's x-coordinate projected ; on screen ; ; The next task is to convert (U R) to a pixel screen ; coordinate and stick it on the XX3 heap. ; ; We start with the x-coordinate. To convert the ; x-coordinate to a screen pixel we add 128, the ; x-coordinate of the centre of the screen, because the ; projected value is relative to an origin at the centre ; of the screen, but the origin of the screen pixels is ; at the top-left of the screen LDX CNT ; Fetch the pointer to the end of the XX3 heap from CNT ; into X SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA XX15+2 ; If x_sign is negative, jump up to LL62, which will BMI LL62 ; store 128 - (U R) on the XX3 heap and return by ; jumping down to LL66 below LDA R ; Calculate 128 + (U R), starting with the low bytes CLC ADC #128 STA XX3,X ; Store the low byte of the result in the X-th byte of ; the heap at XX3 INX ; Increment the heap pointer in X to point to the next ; byte LDA U ; And then add the high bytes ADC #0 STA XX3,X ; Store the high byte of the result in the X-th byte of ; the heap at XX3 .LL66 ; We've just stored the screen x-coordinate of the ; vertex on the XX3 heap, so now for the y-coordinate TXA ; Store the heap pointer in X on the stack (at this PHA ; it points to the last entry on the heap, not the first ; free byte) LDA #0 ; Set U = 0 STA U LDA T ; Set Q = z_lo STA Q LDA XX15+3 ; Set A = y_lo CMP Q ; If y_lo < z_lo jump to LL67 BCC LL67 JSR LL61 ; Call LL61 to calculate: ; ; (U R) = 256 * A / Q ; = 256 * y / z ; ; which we can do as y >= z JMP LL68 ; Jump to LL68 to skip the division for y_lo < z_lo .LL70 ; This gets called from below when y_sign is negative LDA halfScreenHeight ; Calculate halfScreenHeight + (U R), starting with the CLC ; low bytes ADC R STA XX3,X ; Store the low byte of the result in the X-th byte of ; the heap at XX3 INX ; Increment the heap pointer in X to point to the next ; byte LDA #0 ; And then add the high bytes ADC U STA XX3,X ; Store the high byte of the result in the X-th byte of ; the heap at XX3 JMP LL50 ; Jump to LL50 to move on to the next vertex .LL67 JSR LL28 ; Call LL28 to calculate: ; ; R = 256 * A / Q ; = 256 * y / z ; ; Because y < z, the result fits into one byte, and we ; also know that U = 0, so (U R) also contains the ; result .LL68 ; At this point we have: ; ; (U R) = y / z ; ; so (U R) contains the vertex's y-coordinate projected ; on screen ; ; We now want to convert this to a screen y-coordinate ; and stick it on the XX3 heap, much like we did with ; the x-coordinate above. Again, we convert the ; coordinate by adding or subtracting the y-coordinate ; of the centre of the screen, which is in the variable ; halfScreenHeight, but this time we do the opposite, as ; a positive projected y-coordinate, i.e. up the space ; y-axis and up the screen, converts to a low ; y-coordinate, which is the opposite way round to the ; x-coordinates PLA ; Restore the heap pointer from the stack into X TAX INX ; When we stored the heap pointer, it pointed to the ; last entry on the heap, not the first free byte, so we ; increment it so it does point to the next free byte LDA XX15+5 ; If y_sign is negative, jump up to LL70, which will BMI LL70 ; store halfScreenHeight + (U R) on the XX3 heap and ; return by jumping down to LL50 below LDA halfScreenHeight ; Calculate halfScreenHeight - (U R), starting with the SEC ; low bytes SBC R STA XX3,X ; Store the low byte of the result in the X-th byte of ; the heap at XX3 INX ; Increment the heap pointer in X to point to the next ; byte LDA #0 ; And then subtract the high bytes SBC U STA XX3,X ; Store the high byte of the result in the X-th byte of ; the heap at XX3 .LL50 ; By the time we get here, the XX3 heap contains four ; bytes containing the screen coordinates of the current ; vertex, in the order: x_lo, x_hi, y_lo, y_hi CLC ; Set CNT = CNT + 4, so the heap pointer points to the LDA CNT ; next free byte on the heap ADC #4 STA CNT LDA XX17 ; Set A to the offset of the current vertex's data, ; which we set in part 6 ADC #6 ; Set Y = A + 6, so Y now points to the data for the TAY ; next vertex BCS LL72 ; If the addition just overflowed, meaning we just tried ; to access vertex #43, jump to LL72, as the maximum ; number of vertices allowed is 42 CMP XX20 ; If Y >= number of vertices * 6 (which we stored in BCS LL72 ; XX20 in part 6), jump to LL72, as we have processed ; all the vertices for this ship JMP LL48 ; Loop back to LL48 in part 6 to calculate visibility ; and screen coordinates for the next vertex
Name: LL9 (Part 9 of 12) [Show more] Type: Subroutine Category: Drawing ships Summary: Draw ship: Draw laser beams if the ship is firing its laser at us Deep dive: Drawing ships
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This part sets things up so we can loop through the edges in the next part. It also draws a laser line if the ship is firing at us. When we get here, the heap at XX3 contains all the visible vertex screen coordinates.
.LL72 LDA XX1+31 ; If bit 5 of the ship's byte #31 is clear, then the AND #%00100000 ; ship is not currently exploding, so jump down to EE31 BEQ EE31 LDA XX1+31 ; The ship is exploding, so set bit 3 of the ship's byte ORA #%00001000 ; #31 to denote that we are drawing something on-screen STA XX1+31 ; for this ship JMP DOEXP ; Jump to DOEXP to display the explosion cloud, ; returning from the subroutine using a tail call .EE31 LDA #%00001000 ; If bit 3 of the ship's byte #31 is clear, then there BIT XX1+31 ; is nothing already being shown for this ship, so skip BEQ LL74 ; to LL74 as we don't need to erase anything from the ; screen LDA #%00001000 ; Set bit 3 of A so the next instruction sets bit 3 of ; the ship's byte #31 to denote that we are drawing ; something on-screen for this ship .LL74 ORA XX1+31 ; Apply bit 3 of A to the ship's byte #31, so if there STA XX1+31 ; was no ship already on screen, the bit is clear, ; otherwise it is set LDY #9 ; Fetch byte #9 of the ship's blueprint, which is the LDA (XX0),Y ; number of edges, and store it in XX20 STA XX20 LDY #0 ; We are about to step through all the edges, using Y ; as a counter STY U ; Set U = 0 (though we increment it to 1 below) STY XX17 ; Set XX17 = 0, which we are going to use as a counter ; for stepping through the ship's edges INC U ; We are going to start calculating the lines we need to ; draw for this ship, and will store them in the ship ; line heap, using U to point to the end of the heap, so ; we start by setting U = 1 BIT XX1+31 ; If bit 6 of the ship's byte #31 is clear, then the BVC LL170 ; ship is not firing its lasers, so jump to LL170 to ; skip the drawing of laser lines ; The ship is firing its laser at us, so we need to draw ; the laser lines LDA XX1+31 ; Clear bit 6 of the ship's byte #31 so the ship doesn't AND #%10111111 ; keep firing endlessly STA XX1+31 LDY #6 ; Fetch byte #6 of the ship's blueprint, which is the LDA (XX0),Y ; number * 4 of the vertex where the ship has its lasers TAY ; Put the vertex number into Y, where it can act as an ; index into list of vertex screen coordinates we added ; to the XX3 heap LDX XX3,Y ; Fetch the x_lo coordinate of the laser vertex from the STX XX15 ; XX3 heap into XX15 INX ; If X = 255 then the laser vertex is not visible, as BEQ LL170 ; the value we stored in part 2 wasn't overwritten by ; the vertex calculation in part 6 and 7, so jump to ; LL170 to skip drawing the laser lines ; We now build a laser beam from the ship's laser vertex ; towards our ship, as follows: ; ; XX15(1 0) = laser vertex x-coordinate ; ; XX15(3 2) = laser vertex y-coordinate ; ; XX15(5 4) = x-coordinate of the end of the beam ; ; XX12(1 0) = y-coordinate of the end of the beam ; ; The end of the laser beam will be positioned to look ; good, rather than being directly aimed at us, as ; otherwise we would only see a flashing point of light ; as they unleashed their attack LDX XX3+1,Y ; Fetch the x_hi coordinate of the laser vertex from the STX XX15+1 ; XX3 heap into XX15+1 INX ; If X = 255 then the laser vertex is not visible, as BEQ LL170 ; the value we stored in part 2 wasn't overwritten by ; a vertex calculation in part 6 and 7, so jump to LL170 ; to skip drawing the laser beam LDX XX3+2,Y ; Fetch the y_lo coordinate of the laser vertex from the STX XX15+2 ; XX3 heap into XX15+2 LDX XX3+3,Y ; Fetch the y_hi coordinate of the laser vertex from the STX XX15+3 ; XX3 heap into XX15+3 LDA #0 ; Set XX15(5 4) = 0, so their laser beam fires to the STA XX15+4 ; left edge of the screen STA XX15+5 STA XX12+1 ; Set XX12(1 0) = the ship's z_lo coordinate, which will LDA XX1+6 ; effectively make the vertical position of the end of STA XX12 ; the laser beam move around as the ship moves in space LDA XX1+2 ; If the ship's x_sign is positive, skip the next BPL P%+4 ; instruction DEC XX15+4 ; The ship's x_sign is negative (i.e. it's on the left ; side of the screen), so switch the laser beam so it ; goes to the right edge of the screen by decrementing ; XX15(5 4) to 255 JSR CLIP ; Call CLIP to see if the laser beam needs to be ; clipped to fit on-screen, returning the clipped line's ; end-points in (X1, Y1) and (X2, Y2) BCS LL170 ; If the C flag is set then the line is not visible on ; screen, so jump to LL170 so we don't draw this line LDY U ; This instruction is left over from the other versions ; of Elite and has no effect ; ; It would fetch the ship line heap pointer from U, but ; the NES version does not have a ship line heap as the ; screen is redrawn for every frame JSR LOIN ; Draw the laser line
Name: LL9 (Part 10 of 12) [Show more] Type: Subroutine Category: Drawing ships Summary: Draw ship: Calculate the visibility of each of the ship's edges Deep dive: Drawing ships
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This part calculates which edges are visible - in other words, which lines we should draw - and clips them to fit on the screen. When we get here, the heap at XX3 contains all the visible vertex screen coordinates.
.LL170 LDY #3 ; Fetch byte #3 of the ship's blueprint, which contains CLC ; the low byte of the offset to the edges data LDA (XX0),Y ADC XX0 ; Set V = low byte edges offset + XX0 STA V LDY #16 ; Fetch byte #16 of the ship's blueprint, which contains LDA (XX0),Y ; the high byte of the offset to the edges data ADC XX0+1 ; Set V+1 = high byte edges offset + XX0+1 STA V+1 ; ; So V(1 0) now points to the start of the edges data ; for this ship LDY #5 ; Fetch byte #5 of the ship's blueprint, which contains LDA (XX0),Y ; the maximum heap size for plotting the ship (which is STA T1 ; 1 + 4 * the maximum number of visible edges) and store ; it in T1 LDY XX17 ; Set Y to the edge counter in XX17 .LL75 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA (V),Y ; Fetch byte #0 for this edge, which contains the ; visibility distance for this edge, beyond which the ; edge is not shown CMP XX4 ; If XX4 > the visibility distance, where XX4 contains BCC LL79-3 ; the ship's z-distance reduced to 0-31 (which we set in ; part 2), then this edge is too far away to be visible, ; so jump down to LL78 (via LL79-3) to move on to the ; next edge INY ; Increment Y to point to byte #1 LDA (V),Y ; Fetch byte #1 for this edge into A, so: ; ; A = %ffff ffff, where: ; ; * Bits 0-3 = the number of face 1 ; ; * Bits 4-7 = the number of face 2 INY ; Increment Y to point to byte #2 STA P ; Store byte #1 into P AND #%00001111 ; Extract the number of face 1 into X TAX LDA XX2,X ; If XX2+X is non-zero then we decided in part 5 that BNE LL79 ; face 1 is visible, so jump to LL79 LDA P ; Fetch byte #1 for this edge into A LSR A ; Shift right four times to extract the number of face 2 LSR A ; from bits 4-7 into X LSR A LSR A TAX LDA XX2,X ; If XX2+X is non-zero then we decided in part 5 that BNE LL79 ; face 2 is visible, so skip the following instruction JMP LL78 ; Face 2 is hidden, so jump to LL78 .LL79 ; We now build the screen line for this edge, as ; follows: ; ; XX15(1 0) = start x-coordinate ; ; XX15(3 2) = start y-coordinate ; ; XX15(5 4) = end x-coordinate ; ; XX12(1 0) = end y-coordinate ; ; We can then pass this to the line clipping routine ; before storing the resulting line in the ship line ; heap LDA (V),Y ; Fetch byte #2 for this edge into X, which contains TAX ; the number of the vertex at the start of the edge INY ; Increment Y to point to byte #3 LDA (V),Y ; Fetch byte #3 for this edge into Q, which contains STA Q ; the number of the vertex at the end of the edge LDA XX3+1,X ; Fetch the x_hi coordinate of the edge's start vertex STA XX15+1 ; from the XX3 heap into XX15+1 LDA XX3,X ; Fetch the x_lo coordinate of the edge's start vertex STA XX15 ; from the XX3 heap into XX15 LDA XX3+2,X ; Fetch the y_lo coordinate of the edge's start vertex STA XX15+2 ; from the XX3 heap into XX15+2 LDA XX3+3,X ; Fetch the y_hi coordinate of the edge's start vertex STA XX15+3 ; from the XX3 heap into XX15+3 LDX Q ; Set X to the number of the vertex at the end of the ; edge, which we stored in Q LDA XX3,X ; Fetch the x_lo coordinate of the edge's end vertex STA XX15+4 ; from the XX3 heap into XX15+4 LDA XX3+3,X ; Fetch the y_hi coordinate of the edge's end vertex STA XX12+1 ; from the XX3 heap into XX11+1 LDA XX3+2,X ; Fetch the y_lo coordinate of the edge's end vertex STA XX12 ; from the XX3 heap into XX12 LDA XX3+1,X ; Fetch the x_hi coordinate of the edge's end vertex STA XX15+5 ; from the XX3 heap into XX15+5 JSR CLIP2 ; Call CLIP2 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 LL79-3 ; If the C flag is set then the line is not visible on ; screen, so jump to LL78 (via LL79-3) so we don't draw ; this line JSR LOIN ; Draw this edge JMP LL78 ; Jump down to part 11 to skip to the next edge
Name: LL145 (Part 1 of 4) [Show more] Type: Subroutine Category: Drawing lines Summary: Clip line: Work out which end-points are on-screen, if any Deep dive: Line-clipping Extended screen coordinates
Context: See this subroutine on its own page References: This subroutine is called as follows: * BLINE calls via CLIP * CLIP_b1 calls via CLIP * LL9 (Part 9 of 12) calls via CLIP * LL9 (Part 10 of 12) calls via CLIP2

This routine clips the line from (x1, y1) to (x2, y2) so it fits on-screen, or returns an error if it can't be clipped to fit. The arguments are 16-bit coordinates, and the clipped line is returned using 8-bit screen coordinates. This part sets XX13 to reflect which of the two points are on-screen and off-screen.
Arguments: XX15(1 0) x1 as a 16-bit coordinate (x1_hi x1_lo) XX15(3 2) y1 as a 16-bit coordinate (y1_hi y1_lo) XX15(5 4) x2 as a 16-bit coordinate (x2_hi x2_lo) XX12(1 0) y2 as a 16-bit coordinate (y2_hi y2_lo)
Returns: (X1, Y1) Screen coordinate of the start of the clipped line (X2, Y2) Screen coordinate of the end of the clipped line C flag Clear if the clipped line fits on-screen, set if it doesn't XX13 The state of the original coordinates on-screen: * 0 = (x2, y2) on-screen * 127 = (x1, y1) on-screen, (x2, y2) off-screen * 255 = (x1, y1) off-screen, (x2, y2) off-screen So XX13 is non-zero if the end of the line was clipped, meaning the next line sent to BLINE can't join onto the end but has to start a new segment SWAP The swap status of the returned coordinates: * $FF if we swapped the values of (x1, y1) and (x2, y2) as part of the clipping process * 0 if the coordinates are still in the same order Y Y is preserved
Other entry points: CLIP Another name for LL145 CLIP2 Don't initialise the values in SWAP or A
.LL145 .CLIP LDA #0 ; Set SWAP = 0 STA SWAP LDA XX15+5 ; Set A = x2_hi .CLIP2 LDX #255 ; Set X = 255, the highest y-coordinate possible, beyond ; the bottom of the screen ORA XX12+1 ; If one or both of x2_hi and y2_hi are non-zero, jump BNE LL107 ; to LL107 to skip the following, leaving X at 255 LDA Yx2M1 ; If y2_lo > the y-coordinate of the bottom of screen CMP XX12 ; (which is in the variable Yx2M1), then (x2, y2) is off BCC LL107 ; the bottom of the screen, so skip the following ; instruction, leaving X at 255 LDX #0 ; Set X = 0 .LL107 STX XX13 ; Set XX13 = X, so we have: ; ; * XX13 = 0 if x2_hi = y2_hi = 0, y2_lo is on-screen ; ; * XX13 = 255 if x2_hi or y2_hi are non-zero or y2_lo ; is off the bottom of the screen ; ; In other words, XX13 is 255 if (x2, y2) is off-screen, ; otherwise it is 0 LDA XX15+1 ; If one or both of x1_hi and y1_hi are non-zero, jump ORA XX15+3 ; to LL83 BNE LL83 LDA Yx2M1 ; If y1_lo > the y-coordinate of the bottom of screen CMP XX15+2 ; (which is in the variable Yx2M1), then (x1, y1) is BCC LL83 ; off the bottom of the screen, so jump to LL83 ; If we get here, (x1, y1) is on-screen LDA XX13 ; If XX13 is non-zero, i.e. (x2, y2) is off-screen, jump BNE LL108 ; to LL108 to halve it before continuing at LL83 ; If we get here, the high bytes are all zero, which ; means the x-coordinates are < 256 and therefore fit on ; screen, and neither coordinate is off the bottom of ; the screen. That means both coordinates are already on ; screen, so we don't need to do any clipping, all we ; need to do is move the low bytes into (X1, Y1) and ; X2, Y2) and return .LL146 ; If we get here then we have clipped our line to the ; (if we had to clip it at all), so we move the low ; bytes from (x1, y1) and (x2, y2) into (X1, Y1) and ; (X2, Y2), remembering that they share locations with ; XX15: ; ; X1 = XX15 ; Y1 = XX15+1 ; X2 = XX15+2 ; Y2 = XX15+3 ; ; X1 already contains x1_lo, so now we do the rest LDA XX15+2 ; Set Y1 (aka XX15+1) = y1_lo STA XX15+1 LDA XX15+4 ; Set X2 (aka XX15+2) = x2_lo STA XX15+2 LDA XX12 ; Set Y2 (aka XX15+3) = y2_lo STA XX15+3 CLC ; Clear the C flag as the clipped line fits on-screen RTS ; Return from the subroutine .LL109 SEC ; Set the C flag to indicate the clipped line does not ; fit on-screen RTS ; Return from the subroutine .LL108 LSR XX13 ; If we get here then (x2, y2) is off-screen and XX13 is ; 255, so shift XX13 right to halve it to 127
Name: LL145 (Part 2 of 4) [Show more] Type: Subroutine Category: Drawing lines Summary: Clip line: Work out if any part of the line is on-screen Deep dive: Line-clipping Extended screen coordinates
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This part does a number of tests to see if the line is on or off the screen. If we get here then at least one of (x1, y1) and (x2, y2) is off-screen, with XX13 set as follows: * 0 = (x1, y1) off-screen, (x2, y2) on-screen * 127 = (x1, y1) on-screen, (x2, y2) off-screen * 255 = (x1, y1) off-screen, (x2, y2) off-screen where "off-screen" is defined as having a non-zero high byte in one of the coordinates, or in the case of y-coordinates, having a low byte > Yx2M1, the y-coordinate of the bottom of the space view.
.LL83 LDA XX13 ; If XX13 < 128 then only one of the points is on-screen BPL LL115 ; so jump down to LL115 to skip the checks of whether ; both points are in the strips to the right or bottom ; of the screen ; If we get here, both points are off-screen LDA XX15+1 ; If both x1_hi and x2_hi have bit 7 set, jump to LL109 AND XX15+5 ; to return from the subroutine with the C flag set, as BMI LL109 ; the entire line is above the top of the screen LDA XX15+3 ; If both y1_hi and y2_hi have bit 7 set, jump to LL109 AND XX12+1 ; to return from the subroutine with the C flag set, as BMI LL109 ; the entire line is to the left of the screen LDX XX15+1 ; Set A = X = x1_hi - 1 DEX TXA LDX XX15+5 ; Set XX12+2 = x2_hi - 1 DEX STX XX12+2 ORA XX12+2 ; If neither (x1_hi - 1) or (x2_hi - 1) have bit 7 set, BPL LL109 ; jump to LL109 to return from the subroutine with the C ; flag set, as the line doesn't fit on-screen LDA XX15+2 ; If y1_lo < y-coordinate of screen bottom (which is in CMP screenHeight ; the variable screenHeight), clear the C flag, ; otherwise set it LDA XX15+3 ; Set XX12+2 = y1_hi - (1 - C), so: SBC #0 ; STA XX12+2 ; * Set XX12+2 = y1_hi - 1 if y1_lo is on-screen ; * Set XX12+2 = y1_hi otherwise ; ; We do this subtraction because we are only interested ; in trying to move the points up by a screen if that ; might move the point into the space view portion of ; the screen, i.e. if y1_lo is on-screen LDA XX12 ; If y2_lo < y-coordinate of screen bottom (which is in CMP screenHeight ; the variable screenHeight), clear the C flag, ; otherwise set it LDA XX12+1 ; Set XX12+2 = y2_hi - (1 - C), so: SBC #0 ; ; * Set XX12+1 = y2_hi - 1 if y2_lo is on-screen ; * Set XX12+1 = y2_hi otherwise ; ; We do this subtraction because we are only interested ; in trying to move the points up by a screen if that ; might move the point into the space view portion of ; the screen, i.e. if y1_lo is on-screen ORA XX12+2 ; If neither XX12+1 or XX12+2 have bit 7 set, jump to BPL LL109 ; LL109 to return from the subroutine with the C flag ; set, as the line doesn't fit on-screen
Name: LL145 (Part 3 of 4) [Show more] Type: Subroutine Category: Drawing lines Summary: Clip line: Calculate the line's gradient Deep dive: Line-clipping Extended screen coordinates
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.LL115 TYA ; Store Y on the stack so we can preserve it through the PHA ; call to this subroutine LDA XX15+4 ; Set XX12+2 = x2_lo - x1_lo SEC SBC XX15 STA XX12+2 LDA XX15+5 ; Set XX12+3 = x2_hi - x1_hi SBC XX15+1 STA XX12+3 LDA XX12 ; Set XX12+4 = y2_lo - y1_lo SEC SBC XX15+2 STA XX12+4 LDA XX12+1 ; Set XX12+5 = y2_hi - y1_hi SBC XX15+3 STA XX12+5 ; So we now have: ; ; delta_x in XX12(3 2) ; delta_y in XX12(5 4) ; ; where the delta is (x1, y1) - (x2, y2)) EOR XX12+3 ; Set S = the sign of delta_x * the sign of delta_y, so STA S ; if bit 7 of S is set, the deltas have different signs LDA XX12+5 ; If delta_y_hi is positive, jump down to LL110 to skip BPL LL110 ; the following LDA #0 ; Otherwise flip the sign of delta_y to make it SEC ; positive, starting with the low bytes SBC XX12+4 STA XX12+4 LDA #0 ; And then doing the high bytes, so now: SBC XX12+5 ; STA XX12+5 ; XX12(5 4) = |delta_y| .LL110 LDA XX12+3 ; If delta_x_hi is positive, jump down to LL111 to skip BPL LL111 ; the following SEC ; Otherwise flip the sign of delta_x to make it LDA #0 ; positive, starting with the low bytes SBC XX12+2 STA XX12+2 LDA #0 ; And then doing the high bytes, so now: SBC XX12+3 ; ; (A XX12+2) = |delta_x| .LL111 ; We now keep halving |delta_x| and |delta_y| until ; both of them have zero in their high bytes TAX ; If |delta_x_hi| is non-zero, skip the following BNE LL112 LDX XX12+5 ; If |delta_y_hi| = 0, jump down to LL113 (as both BEQ LL113 ; |delta_x_hi| and |delta_y_hi| are 0) .LL112 LSR A ; Halve the value of delta_x in (A XX12+2) ROR XX12+2 LSR XX12+5 ; Halve the value of delta_y XX12(5 4) ROR XX12+4 JMP LL111 ; Loop back to LL111 .LL113 ; By now, the high bytes of both |delta_x| and |delta_y| ; are zero STX T ; We know that X = 0 as that's what we tested with a BEQ ; above, so this sets T = 0 LDA XX12+2 ; If delta_x_lo < delta_y_lo, so our line is more CMP XX12+4 ; vertical than horizontal, jump to LL114 BCC LL114 ; If we get here then our line is more horizontal than ; vertical, so it is a shallow slope STA Q ; Set Q = delta_x_lo LDA XX12+4 ; Set A = delta_y_lo JSR LL28 ; Call LL28 to calculate: ; ; R = 256 * A / Q ; = 256 * delta_y_lo / delta_x_lo JMP LL116 ; Jump to LL116, as we now have the line's gradient in R .LL114 ; If we get here then our line is more vertical than ; horizontal, so it is a steep slope LDA XX12+4 ; Set Q = delta_y_lo STA Q LDA XX12+2 ; Set A = delta_x_lo JSR LL28 ; Call LL28 to calculate: ; ; R = 256 * A / Q ; = 256 * delta_x_lo / delta_y_lo DEC T ; T was set to 0 above, so this sets T = $FF when our ; line is steep
Name: LL145 (Part 4 of 4) [Show more] Type: Subroutine Category: Drawing lines Summary: Clip line: Call the routine in LL188 to do the actual clipping Deep dive: Line-clipping Extended screen coordinates
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This part sets things up to call the routine in LL188, which does the actual clipping. If we get here, then R has been set to the gradient of the line (x1, y1) to (x2, y2), with T indicating the gradient of slope: * 0 = shallow slope (more horizontal than vertical) * $FF = steep slope (more vertical than horizontal) and XX13 has been set as follows: * 0 = (x1, y1) off-screen, (x2, y2) on-screen * 127 = (x1, y1) on-screen, (x2, y2) off-screen * 255 = (x1, y1) off-screen, (x2, y2) off-screen
.LL116 STA XX12+2 ; Store the gradient in XX12+2 (as the call to LL28 in ; part 3 returns the gradient in both A and 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 S ; Store the type of slope in XX12+3, bit 7 clear means STA XX12+3 ; top left to bottom right, bit 7 set means top right to ; bottom left LDA XX13 ; If XX13 = 0, skip the following instruction BEQ LL138 BPL LLX117 ; If XX13 is positive, it must be 127. This means ; (x1, y1) is on-screen but (x2, y2) isn't, so we jump ; to LLX117 to swap the (x1, y1) and (x2, y2) ; coordinates around before doing the actual clipping, ; because we need to clip (x2, y2) but the clipping ; routine at LL118 only clips (x1, y1) .LL138 ; If we get here, XX13 = 0 or 255, so (x1, y1) is ; off-screen and needs clipping JSR LL118 ; Call LL118 to move (x1, y1) along the line onto the ; screen, i.e. clip the line at the (x1, y1) end LDA XX13 ; If XX13 = 255, i.e. (x2, y2) is off-screen, jump down BMI LL117 ; down to LL117 to skip the following PLA ; Restore Y from the stack so it gets preserved through TAY ; the call to this subroutine JMP LL146 ; Jump up to LL146 to move the low bytes of (x1, y1) and ; (x2, y2) into (X1, Y1) and (X2, Y2), and return from ; the subroutine with a successfully clipped line .LL117 ; If we get here, XX13 = 255 (both coordinates are ; off-screen) LDA XX15+1 ; If either of x1_hi or y1_hi are non-zero, jump to ORA XX15+3 ; LL137 to return from the subroutine with the C flag BNE LL137 ; set, as the line doesn't fit on-screen LDA XX15+2 ; If y1_lo > y-coordinate of the bottom of the screen CMP screenHeight ; (which is in the variable screenHeight), jump to LL137 BCS LL137 ; to return from the subroutine with the C flag set, as ; the line doesn't fit on-screen .LLX117 ; If we get here, XX13 = 127 or 255, and in both cases ; (x2, y2) is off-screen, so we now need to swap the ; (x1, y1) and (x2, y2) coordinates around before doing ; the actual clipping, because we need to clip (x2, y2) ; but the clipping routine at LL118 only clips (x1, y1) LDX XX15 ; Swap x1_lo = x2_lo LDA XX15+4 STA XX15 STX XX15+4 LDA XX15+5 ; Swap x2_lo = x1_lo LDX XX15+1 STX XX15+5 STA XX15+1 LDX XX15+2 ; Swap y1_lo = y2_lo LDA XX12 STA XX15+2 STX XX12 LDA XX12+1 ; Swap y2_lo = y1_lo LDX XX15+3 STX XX12+1 STA XX15+3 JSR LL118 ; Call LL118 to move (x1, y1) along the line onto the ; screen, i.e. clip the line at the (x1, y1) end LDA XX15+1 ; If either of x1_hi or y1_hi are non-zero, jump to ORA XX15+3 ; LL137 to return from the subroutine with the C flag BNE LL137 ; set, as the line doesn't fit on-screen DEC SWAP ; Set SWAP = $FF to indicate that we just clipped the ; line at the (x2, y2) end by swapping the coordinates ; (the DEC does this as we set SWAP to 0 at the start of ; this subroutine) .LL124 PLA ; Restore Y from the stack so it gets preserved through TAY ; the call to this subroutine ; If we get here then we have clipped our line to the ; (if we had to clip it at all), so we move the low ; bytes from (x1, y1) and (x2, y2) into (X1, Y1) and ; (X2, Y2), remembering that they share locations with ; XX15: ; ; X1 = XX15 ; Y1 = XX15+1 ; X2 = XX15+2 ; Y2 = XX15+3 ; ; X1 already contains x1_lo, so now we do the rest LDA XX15+2 ; Set A = y1_lo CMP screenHeight ; If A >= screenHeight then jump down to clip2 to clip BCS clip2 ; the coordinate to the screen before jumping back to ; clip1 .clip1 STA XX15+1 ; Set Y1 (aka XX15+1) = y1_lo LDA XX15+4 ; Set X2 (aka XX15+2) = x2_lo STA XX15+2 LDA XX12 ; Set Y2 (aka XX15+3) = y2_lo STA XX15+3 CLC ; Clear the C flag as the clipped line fits on-screen RTS ; Return from the subroutine .clip2 LDA Yx2M1 ; Set A = Yx2M1, which contains the height in pixels of ; the space view BNE clip1 ; Jump to clip1 to continue setting the clipped line's ; coordinates (this BNE is effectively a JMP as A is ; never zero) .LL137 PLA ; Restore Y from the stack so it gets preserved through TAY ; the call to this subroutine SEC ; Set the C flag to indicate the clipped line does not ; fit on-screen RTS ; Return from the subroutine
Name: LL9 (Part 11 of 12) [Show more] Type: Subroutine Category: Drawing ships Summary: Draw ship: Loop back for the next edge Deep dive: Drawing ships
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.LL78 INC XX17 ; Increment the edge counter to point to the next edge LDY XX17 ; If Y >= XX20, which contains the number of edges in CPY XX20 ; the blueprint, jump to LL81 as we have processed all BCS LL81 ; the edges and don't need to loop back for the next one LDY #0 ; Set Y to point to byte #0 again, ready for the next ; edge LDA V ; Increment V by 4 so V(1 0) points to the data for the ADC #4 ; next edge STA V BCC ll81 ; If the above addition didn't overflow, jump to ll81 to ; skip the following instruction INC V+1 ; Otherwise increment the high byte of V(1 0), as we ; just moved the V(1 0) pointer past a page boundary .ll81 JMP LL75 ; Loop back to LL75 to process the next edge .LL81 LDA U ; This instruction is left over from the other versions ; of Elite and has no effect ; ; It would fetch the ship line heap pointer from U, but ; the NES version does not have a ship line heap as the ; screen is redrawn for every frame
Name: LL9 (Part 12 of 12) [Show more] Type: Subroutine Category: Drawing ships Summary: Does nothing in the NES version Deep dive: Drawing ships
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The NES version does not have a ship line heap as the screen is redrawn for every frame, so this part of LL9 does nothing (in the other versions it draws all the visible edges from the ship line heap).
RTS ; Return from the subroutine
Name: LL118 [Show more] Type: Subroutine Category: Drawing lines Summary: Move a point along a line until it is on-screen Deep dive: Line-clipping
Context: See this subroutine on its own page References: This subroutine is called as follows: * LL145 (Part 4 of 4) calls LL118

Given a point (x1, y1), a gradient and a direction of slope, move the point along the line until it is on-screen, so this effectively clips the (x1, y1) end of a line to be on the screen. See the deep dive on "Line-clipping" for more details.
Arguments: XX15(1 0) x1 as a 16-bit coordinate (x1_hi x1_lo) XX15(3 2) y1 as a 16-bit coordinate (y1_hi y1_lo) XX12+2 The line's gradient * 256 (so 1.0 = 256) XX12+3 The direction of slope: * Positive (bit 7 clear) = top left to bottom right * Negative (bit 7 set) = top right to bottom left T The gradient of slope: * 0 if it's a shallow slope * $FF if it's a steep slope
Returns: XX15 x1 as an 8-bit coordinate XX15+2 y1 as an 8-bit coordinate
Other entry points: LL118-1 Contains an RTS
.LL118 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA XX15+1 ; Set S = x1_hi STA S BPL LL119 ; If x1_hi is positive, jump down to LL119 to skip the ; following JSR LL120 ; Call LL120 to calculate: ; ; (Y X) = (S x1_lo) * XX12+2 if T = 0 ; = x1 * gradient ; ; (Y X) = (S x1_lo) / XX12+2 if T <> 0 ; = x1 / gradient ; ; with the sign of (Y X) set to the opposite of the ; line's direction of slope TXA ; Set y1 = y1 + (Y X) CLC ; ADC XX15+2 ; starting with the low bytes STA XX15+2 TYA ; And then adding the high bytes ADC XX15+3 STA XX15+3 LDA #0 ; Set x1 = 0 STA XX15 STA XX15+1 TAX ; Set X = 0 so the next instruction becomes a JMP BEQ LL134S ; If x1_hi = 0 then jump down to LL134S to skip the ; following, as the x-coordinate is already on-screen ; (as 0 <= (x_hi x_lo) <= 255) .LL119 BEQ LL134 ; If x1_hi = 0 then jump down to LL134 to skip the ; following, as the x-coordinate is already on-screen ; (as 0 <= (x_hi x_lo) <= 255) DEC S ; Otherwise x1_hi is positive, i.e. x1 >= 256 and off ; the right side of the screen, so set: ; ; S = S - 1 ; = x1_hi - 1 JSR LL120 ; Call LL120 to calculate: ; ; (Y X) = (S x1_lo) * XX12+2 if T = 0 ; = (x1 - 256) * gradient ; ; (Y X) = (S x1_lo) / XX12+2 if T <> 0 ; = (x1 - 256) / gradient ; ; with the sign of (Y X) set to the opposite of the ; line's direction of slope TXA ; Set y1 = y1 + (Y X) CLC ; ADC XX15+2 ; starting with the low bytes STA XX15+2 TYA ; And then adding the high bytes ADC XX15+3 STA XX15+3 LDX #255 ; Set x1 = 255 STX XX15 INX STX XX15+1 .LL134S 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 .LL134 ; We have moved the point so the x-coordinate is on ; screen (i.e. in the range 0-255), so now for the ; y-coordinate LDA XX15+3 ; If y1_hi is positive, jump down to LL119 to skip BPL LL135 ; the following STA S ; Otherwise y1_hi is negative, i.e. off the top of the ; screen, so set S = y1_hi LDA XX15+2 ; Set R = y1_lo STA R JSR LL123 ; Call LL123 to calculate: ; ; (Y X) = (S R) / XX12+2 if T = 0 ; = y1 / gradient ; ; (Y X) = (S R) * XX12+2 if T <> 0 ; = y1 * gradient ; ; with the sign of (Y X) set to the opposite of the ; line's direction of slope TXA ; Set x1 = x1 + (Y X) CLC ; ADC XX15 ; starting with the low bytes STA XX15 TYA ; And then adding the high bytes ADC XX15+1 STA XX15+1 LDA #0 ; Set y1 = 0 STA XX15+2 STA XX15+3 .LL135 LDA XX15+2 ; Set (S R) = (y1_hi y1_lo) - screen height SEC ; SBC screenHeight ; starting with the low bytes STA R LDA XX15+3 ; And then subtracting the high bytes SBC #0 STA S BCC LL136 ; If the subtraction underflowed, i.e. if y1 < screen ; height, then y1 is already on-screen, so jump to LL136 ; to return from the subroutine, as we are done .LL139 ; If we get here then y1 >= screen height, i.e. off the ; bottom of the screen JSR LL123 ; Call LL123 to calculate: ; ; (Y X) = (S R) / XX12+2 if T = 0 ; = (y1 - screen height) / gradient ; ; (Y X) = (S R) * XX12+2 if T <> 0 ; = (y1 - screen height) * gradient ; ; with the sign of (Y X) set to the opposite of the ; line's direction of slope TXA ; Set x1 = x1 + (Y X) CLC ; ADC XX15 ; starting with the low bytes STA XX15 TYA ; And then adding the high bytes ADC XX15+1 STA XX15+1 LDA Yx2M1 ; Set y1 = 2 * Yx2M1. The variable Yx2M1 is the STA XX15+2 ; y-coordinate of the mid-point of the space view, so LDA #0 ; this sets Y2 to y-coordinate of the bottom pixel STA XX15+3 ; row of the space view .LL136 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 RTS ; Return from the subroutine
Name: LL120 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate (Y X) = (S x1_lo) * XX12+2 or (S x1_lo) / XX12+2
Context: See this subroutine on its own page References: This subroutine is called as follows: * LL118 calls LL120 * LL123 calls via LL122

Calculate the following: * If T = 0, this is a shallow slope, so calculate (Y X) = (S x1_lo) * XX12+2 * If T <> 0, this is a steep slope, so calculate (Y X) = (S x1_lo) / XX12+2 giving (Y X) the opposite sign to the slope direction in XX12+3.
Arguments: T The gradient of slope: * 0 if it's a shallow slope * $FF if it's a steep slope
Other entry points: LL122 Calculate (Y X) = (S R) * Q and set the sign to the opposite of the top byte on the stack
.LL120 LDA XX15 ; Set R = x1_lo STA R JSR LL129 ; Call LL129 to do the following: ; ; Q = XX12+2 ; = line gradient ; ; A = S EOR XX12+3 ; = S EOR slope direction ; ; (S R) = |S R| ; ; So A contains the sign of S * slope direction PHA ; Store A on the stack so we can use it later LDX T ; If T is non-zero, then it's a steep slope, so jump BNE LL121 ; down to LL121 to calculate this instead: ; ; (Y X) = (S R) / Q .LL122 ; The following calculates: ; ; (Y X) = (S R) * Q ; ; using the same shift-and-add algorithm that's ; documented in MULT1 LDA #0 ; Set A = 0 TAX ; Set (Y X) = 0 so we can start building the answer here TAY LSR S ; Shift (S R) to the right, so we extract bit 0 of (S R) ROR R ; into the C flag ASL Q ; Shift Q to the left, catching bit 7 in the C flag BCC LL126 ; If C (i.e. the next bit from Q) is clear, do not do ; the addition for this bit of Q, and instead skip to ; LL126 to just do the shifts .LL125 TXA ; Set (Y X) = (Y X) + (S R) CLC ; ADC R ; starting with the low bytes TAX TYA ; And then doing the high bytes ADC S TAY .LL126 LSR S ; Shift (S R) to the right ROR R ASL Q ; Shift Q to the left, catching bit 7 in the C flag BCS LL125 ; If C (i.e. the next bit from Q) is set, loop back to ; LL125 to do the addition for this bit of Q BNE LL126 ; If Q has not yet run out of set bits, loop back to ; LL126 to do the "shift" part of shift-and-add until ; we have done additions for all the set bits in Q, to ; give us our multiplication result PLA ; Restore A, which we calculated above, from the stack BPL LL133 ; If A is positive jump to LL133 to negate (Y X) and ; return from the subroutine using a tail call RTS ; Return from the subroutine
Name: LL123 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate (Y X) = (S R) / XX12+2 or (S R) * XX12+2
Context: See this subroutine on its own page References: This subroutine is called as follows: * LL118 calls LL123 * LL120 calls via LL121 * LL120 calls via LL133

Calculate the following: * If T = 0, this is a shallow slope, so calculate (Y X) = (S R) / XX12+2 * If T <> 0, this is a steep slope, so calculate (Y X) = (S R) * XX12+2 giving (Y X) the opposite sign to the slope direction in XX12+3.
Arguments: XX12+2 The line's gradient * 256 (so 1.0 = 256) XX12+3 The direction of slope: * Bit 7 clear means top left to bottom right * Bit 7 set means top right to bottom left T The gradient of slope: * 0 if it's a shallow slope * $FF if it's a steep slope
Other entry points: LL121 Calculate (Y X) = (S R) / Q and set the sign to the opposite of the top byte on the stack LL133 Negate (Y X) and return from the subroutine LL128 Contains an RTS
.LL123 JSR LL129 ; Call LL129 to do the following: ; ; Q = XX12+2 ; = line gradient ; ; A = S EOR XX12+3 ; = S EOR slope direction ; ; (S R) = |S R| ; ; So A contains the sign of S * slope direction PHA ; Store A on the stack so we can use it later LDX T ; If T is non-zero, then it's a steep slope, so jump up BNE LL122 ; to LL122 to calculate this instead: ; ; (Y X) = (S R) * Q .LL121 ; The following calculates: ; ; (Y X) = (S R) / Q ; ; using the same shift-and-subtract algorithm that's ; documented in TIS2 LDA #%11111111 ; Set Y = %11111111 TAY ASL A ; Set X = %11111110 TAX ; This sets (Y X) = %1111111111111110, so we can rotate ; through 15 loop iterations, getting a 1 each time, and ; then getting a 0 on the 16th iteration... and we can ; also use it to catch our result bits into bit 0 each ; time .LL130 ASL R ; Shift (S R) to the left ROL S LDA S ; Set A = S BCS LL131 ; If bit 7 of S was set, then jump straight to the ; subtraction CMP Q ; If A < Q (i.e. S < Q), skip the following subtractions BCC LL132 .LL131 SBC Q ; A >= Q (i.e. S >= Q) so set: STA S ; ; S = (A R) - Q ; = (S R) - Q ; ; starting with the low bytes (we know the C flag is ; set so the subtraction will be correct) LDA R ; And then doing the high bytes SBC #0 STA R SEC ; Set the C flag to rotate into the result in (Y X) .LL132 TXA ; Rotate the counter in (Y X) to the left, and catch the ROL A ; result bit into bit 0 (which will be a 0 if we didn't TAX ; do the subtraction, or 1 if we did) TYA ROL A TAY BCS LL130 ; If we still have set bits in (Y X), loop back to LL130 ; to do the next iteration of 15, until we have done the ; whole division PLA ; Restore A, which we calculated above, from the stack BMI LL128 ; If A is negative jump to LL128 to return from the ; subroutine with (Y X) as is .LL133 TXA ; Otherwise negate (Y X) using two's complement by first EOR #%11111111 ; setting the low byte to ~X + 1 ADC #1 ; TAX ; The addition works as we know the C flag is clear from ; when we passed through the BCS above TYA ; Then set the high byte to ~Y + C EOR #%11111111 ADC #0 TAY .LL128 RTS ; Return from the subroutine
Name: LL129 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate Q = XX12+2, A = S EOR XX12+3 and (S R) = |S R|
Context: See this subroutine on its own page References: This subroutine is called as follows: * LL120 calls LL129 * LL123 calls LL129

Do the following, in this order: Q = XX12+2 A = S EOR XX12+3 (S R) = |S R| This sets up the variables required above to calculate (S R) / XX12+2 and give the result the opposite sign to XX13+3.
.LL129 LDX XX12+2 ; Set Q = XX12+2 STX Q LDA S ; If S is positive, jump to LL127 BPL LL127 LDA #0 ; Otherwise set R = -R SEC SBC R STA R LDA S ; Push S onto the stack PHA EOR #%11111111 ; Set S = ~S + 1 + C ADC #0 STA S PLA ; Pull the original, negative S from the stack into A .LL127 EOR XX12+3 ; Set A = original argument S EOR'd with XX12+3 RTS ; Return from the subroutine
Name: DOEXP [Show more] Type: Subroutine Category: Drawing ships Summary: Draw an exploding ship Deep dive: Drawing explosion clouds Generating random numbers
Context: See this subroutine on its own page References: This subroutine is called as follows: * LL9 (Part 1 of 12) calls DOEXP * LL9 (Part 9 of 12) calls DOEXP * DrawExplosionBurst calls via EXS1

Other entry points: EXS1 Set (A X) = (A R) +/- random * cloud size
.EX2 LDA INWK+31 ; Set bits 5 and 7 of the ship's byte #31 to denote that ORA #%10100000 ; the ship is exploding and has been killed STA INWK+31 .dexp1 JMP HideExplosionBurst ; Hide the four sprites that make up the explosion burst ; and return from the subroutine using a tail call EQUB $00, $02 ; These bytes appear to be unused .DOEXP 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+6 ; Set T = z_lo STA T LDA INWK+7 ; Set A = z_hi, so (A T) = z CMP #32 ; If z_hi < 32, skip the next two instructions BCC P%+6 LDA #$FE ; Set A = 254 and jump to yy (this BNE is effectively a BNE yy ; JMP, as A is never zero) ASL T ; Shift (A T) left twice ROL A ASL T ROL A SEC ; And then shift A left once more, inserting a 1 into ROL A ; bit 0 ; Overall, the above multiplies A by 8 and makes sure it ; is at least 1, to leave a one-byte distance in A. We ; can use this as the distance for our cloud, to ensure ; that the explosion cloud is visible even for ships ; that blow up a long way away .yy STA Q ; Store the distance to the explosion in Q LDA INWK+34 ; Set A to the cloud counter from byte #34 of the ship's ; data block ADC #4 ; Add 4 to the cloud counter, so it ticks onwards every ; we redraw it BCS EX2 ; If the addition overflowed, jump up to EX2 to update ; the explosion flags and return from the subroutine STA INWK+34 ; Store the updated cloud counter in byte #34 of the ; ship data block JSR DVID4 ; Calculate the following: ; ; (P R) = 256 * A / Q ; = 256 * cloud counter / distance ; ; We are going to use this as our cloud size, so the ; further away the cloud, the smaller it is, and as the ; cloud counter ticks onward, the cloud expands 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 ; Set A = P, so we now have: ; ; (A R) = 256 * cloud counter / distance CMP #$1C ; If A < 28, skip the next two instructions BCC P%+6 LDA #$FE ; Set A = 254 and skip the following (this BNE is BNE LABEL_1 ; effectively a JMP as A is never zero) ASL R ; Shift (A R) left three times to multiply by 8 ROL A ASL R ROL A ASL R ROL A ; Overall, the above multiplies (A R) by 8 to leave a ; one-byte cloud size in A, given by the following: ; ; A = 8 * cloud counter / distance .LABEL_1 STA cloudSize ; Store the cloud size in cloudSize so we can access it ; later LDA INWK+31 ; Clear bit 6 of the ship's byte #31 to denote that the AND #%10111111 ; explosion has not yet been drawn STA INWK+31 AND #%00001000 ; If bit 3 of the ship's byte #31 is clear, then nothing BEQ dexp1 ; is being drawn on-screen for this ship anyway, so ; return from the subroutine LDA INWK+7 ; If z_hi = 0 then jump to PTCLS to draw the explosion BEQ PTCLS ; cloud (but not the explosion burst, as the ship is too ; close for the burst sprites to look good) LDY INWK+34 ; Fetch byte #34 of the ship data block, which contains ; the cloud counter CPY #24 ; If Y >= 24 then jump to PTCLS to draw the explosion BCS PTCLS ; cloud (but not the explosion burst as the explosion is ; already past that point) ; If we get here then the exploding ship is not too ; close and we haven't yet counted past the initial part ; of the explosion, so we can show the explosion burst ; using the explosion sprites JMP DrawExplosionBurst ; Draw the exploding ship along with an explosion burst, ; returning from the subroutine using a tail call .PTCLS ; This part of the routine actually draws the explosion ; cloud JSR HideExplosionBurst ; Hide the four sprites that make up the explosion burst LDA cloudSize ; Fetch the cloud size that we stored above, and store STA Q ; it in Q LDA INWK+34 ; Fetch byte #34 of the ship data block, which contains ; the cloud counter BPL P%+4 ; If the cloud counter < 128, then we are in the first ; half of the cloud's existence, so skip the next ; instruction EOR #$FF ; Flip the value of A so that in the second half of the ; cloud's existence, A counts down instead of up LSR A ; Divide A by 16 so that is has a maximum value of 7 LSR A LSR A LSR A ORA #1 ; Make sure A is at least 1 and store it in U, to STA U ; give us the number of particles in the explosion for ; each vertex LDY #7 ; Fetch byte #7 of the ship blueprint, which contains LDA (XX0),Y ; the explosion count for this ship (i.e. the number of STA TGT ; vertices used as origins for explosion clouds) and ; store it in TGT LDA RAND+1 ; Fetch the current random number seed in RAND+1 and PHA ; store it on the stack, so we can re-randomise the ; seeds when we are done LDY #6 ; Set Y = 6 to point to the byte before the first vertex ; coordinate we stored on the XX3 heap above (we ; increment it below so it points to the first vertex) .EXL5 LDX #3 ; We are about to fetch a pair of coordinates from the ; XX3 heap, so set a counter in X for 4 bytes .dexp2 INY ; Increment the index in Y so it points to the next byte ; from the coordinate we are copying LDA XX3-7,Y ; Copy byte Y-7 from the XX3 heap to the X-th byte of K3 STA K3,X DEX ; Decrement the loop counter BPL dexp2 ; Keep copying vertex coordinates into K3 until we have ; copied all six coordinates ; The above loop copies the vertex coordinates from the ; XX3 heap to K3, reversing them as we go, so it sets ; the following: ; ; K3+3 = x_lo ; K3+2 = x_hi ; K3+1 = y_lo ; K3+0 = y_hi STY CNT ; Set CNT to the index that points to the next vertex on ; the XX3 heap ; This next part copies bytes #37 to #40 from the ship ; data block into the four random number seeds in RAND ; to RAND+3, EOR'ing them with the vertex index so they ; are different for every vertex. This enables us to ; generate random numbers for drawing each vertex that ; are random but repeatable, which we need when we ; redraw the cloud to remove it ; ; We set the values of bytes #37 to #40 randomly in the ; LL9 routine before calling DOEXP, so the explosion ; cloud is random but repeatable LDY #37 ; Set Y to act as an index into the ship data block for ; byte #37 LDA (INF),Y ; Set the seed at RAND to byte #37, EOR'd with the EOR CNT ; vertex index, so the seeds are different for each STA RAND ; vertex INY ; Increment Y to point to byte #38 LDA (INF),Y ; Set the seed at RAND+1 to byte #38, EOR'd with the EOR CNT ; vertex index, so the seeds are different for each STA RAND+1 ; vertex INY ; Increment Y to point to byte #39 LDA (INF),Y ; Set the seed at RAND+2 to byte #39, EOR'd with the EOR CNT ; vertex index, so the seeds are different for each STA RAND+2 ; vertex INY ; Increment Y to point to byte #40 LDA (INF),Y ; Set the seed at RAND+3 to byte #49, EOR'd with the EOR CNT ; vertex index, so the seeds are different for each STA RAND+3 ; vertex LDY U ; Set Y to the number of particles in the explosion for ; each vertex, which we stored in U above. We will now ; use this as a loop counter to iterate through all the ; particles in the explosion .EXL4 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 CLC ; This contains the code from the DORND2 routine, so LDA RAND ; this section is exactly equivalent to a JSR DORND2 ROL A ; call, but is slightly faster as it's been inlined TAX ; (so it sets A and X to random values, making sure ADC RAND+2 ; the C flag doesn't affect the outcome) STA RAND STX RAND+2 LDA RAND+1 TAX ADC RAND+3 STA RAND+1 STX RAND+3 STA ZZ ; Set ZZ to a random number LDA K3+1 ; Set (A R) = (y_hi y_lo) STA R ; = y LDA K3 JSR EXS1 ; Set (A X) = (A R) +/- random * cloud size ; = y +/- random * cloud size BNE EX11 ; If A is non-zero, the particle is off-screen as the ; coordinate is bigger than 255), so jump to EX11 to do ; the next particle CPX Yx2M1 ; If X > the y-coordinate of the bottom of the screen BCS EX11 ; (which is in Yx2M1) then the particle is off the ; bottom of the screen, so jump to EX11 to do the next ; particle ; Otherwise X contains a random y-coordinate within the ; cloud STX Y1 ; Set Y1 = our random y-coordinate within the cloud LDA K3+3 ; Set (A R) = (x_hi x_lo) STA R LDA K3+2 JSR EXS1 ; Set (A X) = (A R) +/- random * cloud size ; = x +/- random * cloud size BNE EX4 ; If A is non-zero, the particle is off-screen as the ; coordinate is bigger than 255), so jump to EX11 to do ; the next particle ; Otherwise X contains a random x-coordinate within the ; cloud LDA Y1 ; Set A = our random y-coordinate within the cloud JSR PIXEL ; Draw a point at screen coordinate (X, A) with the ; point size determined by the distance in ZZ .EX4 DEY ; Decrement the loop counter for the next particle BPL EXL4 ; Loop back to EXL4 until we have done all the particles ; in the cloud LDY CNT ; Set Y to the index that points to the next vertex on ; the XX3 heap CPY TGT ; If Y < TGT, which we set to the explosion count for BCC EXL5 ; this ship (i.e. the number of vertices used as origins ; for explosion clouds), loop back to EXL5 to do a cloud ; for the next vertex PLA ; Restore the current random number seed to RAND+1 that STA RAND+1 ; we stored at the start of the routine LDA K%+6 ; Store the z_lo coordinate for the planet (which will STA RAND+3 ; be pretty random) in the RAND+3 seed RTS ; Return from the subroutine .EX11 CLC ; This contains the code from the DORND2 routine, so LDA RAND ; this section is exactly equivalent to a JSR DORND2 ROL A ; call, but is slightly faster as it's been inlined TAX ; (so it sets A and X to random values, making sure ADC RAND+2 ; the C flag doesn't affect the outcome) STA RAND STX RAND+2 LDA RAND+1 TAX ADC RAND+3 STA RAND+1 STX RAND+3 JMP EX4 ; We just skipped a particle, so jump up to EX4 to do ; the next one .EXS1 ; This routine calculates the following: ; ; (A X) = (A R) +/- random * cloud size ; ; returning with the flags set for the high byte in A STA S ; Store A in S so we can use it later CLC ; This contains the code from the DORND2 routine, so LDA RAND ; this section is exactly equivalent to a JSR DORND2 ROL A ; call, but is slightly faster as it's been inlined TAX ; (so it sets A and X to random values, making sure ADC RAND+2 ; the C flag doesn't affect the outcome) STA RAND STX RAND+2 LDA RAND+1 TAX ADC RAND+3 STA RAND+1 STX RAND+3 ROL A ; Set A = A * 2 BCS EX5 ; If bit 7 of A was set (50% chance), jump to EX5 JSR FMLTU ; Set A = A * Q / 256 ; = random << 1 * projected cloud size / 256 ADC R ; Set (A X) = (S R) + A TAX ; = (S R) + random * projected cloud size ; ; where S contains the argument A, starting with the low ; bytes LDA S ; And then the high bytes ADC #0 RTS ; Return from the subroutine .EX5 JSR FMLTU ; Set T = A * Q / 256 STA T ; = random << 1 * projected cloud size / 256 LDA R ; Set (A X) = (S R) - T SBC T ; TAX ; where S contains the argument A, starting with the low ; bytes LDA S ; And then the high bytes SBC #0 RTS ; Return from the subroutine
Name: PLANET [Show more] Type: Subroutine Category: Drawing planets Summary: Draw the planet or sun
Context: See this subroutine on its own page References: This subroutine is called as follows: * LL9 (Part 1 of 12) calls PLANET

Arguments: INWK The planet or sun's ship data block
.PL2 RTS ; Return from the subroutine .PLANET LDA INWK+8 ; Set A = z_sign (the highest byte in the planet/sun's ; coordinates) CMP #48 ; If A >= 48 then the planet/sun is too far away to be BCS PL2 ; seen, so jump to PL2 to remove it from the screen, ; returning from the subroutine using a tail call ORA INWK+7 ; Set A to 0 if both z_sign and z_hi are 0 BEQ PL2 ; If both z_sign and z_hi are 0, then the planet/sun is ; too close to be shown, so jump to PL2 to remove it ; from the screen, returning from the subroutine using a ; tail call JSR PROJ ; Project the planet/sun onto the screen, returning the ; centre's coordinates in K3(1 0) and K4(1 0) BCS PL2 ; If the C flag is set by PROJ then the planet/sun is ; not visible on-screen, so jump to PL2 to remove it ; from the screen, returning from the subroutine using ; a tail call LDA #96 ; Set (A P+1 P) = (0 96 0) = 24576 STA P+1 ; LDA #0 ; This represents the planet/sun's radius at a distance STA P ; of z = 1 JSR DVID3B2 ; Call DVID3B2 to calculate: ; ; K(3 2 1 0) = (A P+1 P) / (z_sign z_hi z_lo) ; = (0 96 0) / z ; = 24576 / z ; ; so K now contains the planet/sun's radius, reduced by ; the actual distance to the planet/sun. We know that ; K+3 and K+2 will be 0, as the number we are dividing, ; (0 96 0), fits into the two bottom bytes, so the ; result is actually in K(1 0) LDA K+1 ; If the high byte of the reduced radius is zero, jump BEQ PL82 ; to PL82, as K contains the radius on its own LDA #248 ; Otherwise set K = 248, to round up the radius in STA K ; K(1 0) to the nearest integer (if we consider the low ; byte to be the fractional part) .PL82 LDA TYPE ; If the planet/sun's type has bit 0 clear, then it's LSR A ; either 128 or 130, which is a planet (the sun has type BCC PL9 ; 129, which has bit 0 set). So jump to PL9 to draw the ; planet with radius K, returning from the subroutine ; using a tail call JMP SUN ; Otherwise jump to SUN to draw the sun with radius K, ; returning from the subroutine using a tail call
Name: PL9 (Part 1 of 3) [Show more] Type: Subroutine Category: Drawing planets Summary: Draw the planet, with either an equator and meridian, or a crater
Context: See this subroutine on its own page References: This subroutine is called as follows: * PLANET calls PL9

Draw the planet with radius K at pixel coordinate (K3, K4), and with either an equator and meridian, or a crater.
Arguments: K(1 0) The planet's radius K3(1 0) Pixel x-coordinate of the centre of the planet K4(1 0) Pixel y-coordinate of the centre of the planet INWK The planet's ship data block
.PL9 JSR CIRCLE ; Call CIRCLE to draw the planet's new circle BCS PL20 ; If the call to CIRCLE returned with the C flag set, ; then the circle does not fit on-screen, so jump to ; PL20 to return from the subroutine LDA K+1 ; If K+1 is zero, jump to PL25 as K(1 0) < 256, so the BEQ PL25 ; planet fits on the screen and we can draw meridians or ; craters .PL20 RTS ; The planet doesn't fit on-screen, so return from the ; subroutine .PL25 LDA TYPE ; If the planet type is 128 then it has an equator and CMP #128 ; a meridian, so this jumps to PL26 if this is not a BNE PL26 ; planet with an equator - in other words, if it is a ; planet with a crater ; Otherwise this is a planet with an equator and ; meridian, so fall through into the following to draw ; them
Name: PL9 (Part 2 of 3) [Show more] Type: Subroutine Category: Drawing planets Summary: Draw the planet's equator and meridian Deep dive: Drawing meridians and equators
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

Draw the planet's equator and meridian.
Arguments: K(1 0) The planet's radius K3(1 0) Pixel x-coordinate of the centre of the planet K4(1 0) Pixel y-coordinate of the centre of the planet INWK The planet's ship data block
LDA K ; If the planet's radius is less than 6, the planet is CMP #6 ; too small to show a meridian, so jump to PL20 to BCC PL20 ; return from the subroutine LDA INWK+14 ; Set P = -nosev_z_hi EOR #%10000000 STA P LDA INWK+20 ; Set A = roofv_z_hi JSR PLS4 ; Call PLS4 to calculate the following: ; ; CNT2 = arctan(P / A) / 4 ; = arctan(-nosev_z_hi / roofv_z_hi) / 4 ; ; and do the following if nosev_z_hi >= 0: ; ; CNT2 = CNT2 + PI LDX #9 ; Set X to 9 so the call to PLS1 divides nosev_x JSR PLS1 ; Call PLS1 to calculate the following: STA K2 ; STY XX16 ; (XX16 K2) = nosev_x / z ; ; and increment X to point to nosev_y for the next call JSR PLS1 ; Call PLS1 to calculate the following: STA K2+1 ; STY XX16+1 ; (XX16+1 K2+1) = nosev_y / z LDX #15 ; Set X to 15 so the call to PLS5 divides roofv_x JSR PLS5 ; Call PLS5 to calculate the following: ; ; (XX16+2 K2+2) = roofv_x / z ; ; (XX16+3 K2+3) = roofv_y / z JSR PLS2 ; Call PLS2 to draw the first meridian LDA INWK+14 ; Set P = -nosev_z_hi EOR #%10000000 STA P LDA INWK+26 ; Set A = sidev_z_hi, so the second meridian will be at ; 90 degrees to the first JSR PLS4 ; Call PLS4 to calculate the following: ; ; CNT2 = arctan(P / A) / 4 ; = arctan(-nosev_z_hi / sidev_z_hi) / 4 ; ; and do the following if nosev_z_hi >= 0: ; ; CNT2 = CNT2 + PI LDX #21 ; Set X to 21 so the call to PLS5 divides sidev_x JSR PLS5 ; Call PLS5 to calculate the following: ; ; (XX16+2 K2+2) = sidev_x / z ; ; (XX16+3 K2+3) = sidev_y / z JMP PLS2 ; Jump to PLS2 to draw the second meridian, returning ; from the subroutine using a tail call
Name: PL9 (Part 3 of 3) [Show more] Type: Subroutine Category: Drawing planets Summary: Draw the planet's crater Deep dive: Drawing craters
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

Draw the planet's crater.
Arguments: K(1 0) The planet's radius K3(1 0) Pixel x-coordinate of the centre of the planet K4(1 0) Pixel y-coordinate of the centre of the planet INWK The planet's ship data block
.PL26 LDA INWK+20 ; Set A = roofv_z_hi BMI PL20 ; If A is negative, the crater is on the far side of the ; planet, so return from the subroutine (as PL2 ; contains an RTS) LDX #15 ; Set X = 15, so the following call to PLS3 operates on ; roofv JSR PLS3 ; Call PLS3 to calculate: ; ; (Y A P) = 222 * roofv_x / z ; ; to give the x-coordinate of the crater offset and ; increment X to point to roofv_y for the next call CLC ; Calculate: ADC K3 ; STA K3 ; K3(1 0) = (Y A) + K3(1 0) ; = 222 * roofv_x / z + x-coordinate of planet ; centre ; ; starting with the high bytes TYA ; And then doing the low bytes, so now K3(1 0) contains ADC K3+1 ; the x-coordinate of the crater offset plus the planet STA K3+1 ; centre to give the x-coordinate of the crater's centre JSR PLS3 ; Call PLS3 to calculate: ; ; (Y A P) = 222 * roofv_y / z ; ; to give the y-coordinate of the crater offset STA P ; Calculate: LDA K4 ; SEC ; K4(1 0) = K4(1 0) - (Y A) SBC P ; = 222 * roofv_y / z - y-coordinate of planet STA K4 ; centre ; ; starting with the low bytes STY P ; And then doing the low bytes, so now K4(1 0) contains LDA K4+1 ; the y-coordinate of the crater offset plus the planet SBC P ; centre to give the y-coordinate of the crater's centre STA K4+1 LDX #9 ; Set X = 9, so the following call to PLS1 operates on ; nosev JSR PLS1 ; Call PLS1 to calculate the following: ; ; (Y A) = nosev_x / z ; ; and increment X to point to nosev_y for the next call LSR A ; Set (XX16 K2) = (Y A) / 2 STA K2 STY XX16 JSR PLS1 ; Call PLS1 to calculate the following: ; ; (Y A) = nosev_y / z ; ; and increment X to point to nosev_z for the next call LSR A ; Set (XX16+1 K2+1) = (Y A) / 2 STA K2+1 STY XX16+1 LDX #21 ; Set X = 21, so the following call to PLS1 operates on ; sidev JSR PLS1 ; Call PLS1 to calculate the following: ; ; (Y A) = sidev_x / z ; ; and increment X to point to sidev_y for the next call LSR A ; Set (XX16+2 K2+2) = (Y A) / 2 STA K2+2 STY XX16+2 JSR PLS1 ; Call PLS1 to calculate the following: ; ; (Y A) = sidev_y / z ; ; and increment X to point to sidev_z for the next call LSR A ; Set (XX16+3 K2+3) = (Y A) / 2 STA K2+3 STY XX16+3 LDA #64 ; Set TGT = 64, so we draw a full ellipse in the call to STA TGT ; PLS22 below LDA #0 ; Set CNT2 = 0 as we are drawing a full ellipse, so we STA CNT2 ; don't need to apply an offset JMP PLS22 ; Jump to PLS22 to draw the crater, returning from the ; subroutine using a tail call
Name: PLS1 [Show more] Type: Subroutine Category: Drawing planets Summary: Calculate (Y A) = nosev_x / z
Context: See this subroutine on its own page References: This subroutine is called as follows: * PL9 (Part 2 of 3) calls PLS1 * PL9 (Part 3 of 3) calls PLS1 * PLS3 calls PLS1 * PLS5 calls PLS1

Calculate the following division of a specified value from one of the orientation vectors (in this example, nosev_x): (Y A) = nosev_x / z where z is the z-coordinate of the planet from INWK. The result is an 8-bit magnitude in A, with maximum value 254, and just a sign bit (bit 7) in Y.
Arguments: X Determines which of the INWK orientation vectors to divide: * X = 9, 11, 13: divides nosev_x, nosev_y, nosev_z * X = 15, 17, 19: divides roofv_x, roofv_y, roofv_z * X = 21, 23, 25: divides sidev_x, sidev_y, sidev_z INWK The planet's ship data block
Returns: A The result as an 8-bit magnitude with maximum value 254 Y The sign of the result in bit 7 K+3 Also the sign of the result in bit 7 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)
.PLS1 LDA INWK,X ; Set P = nosev_x_lo STA P LDA INWK+1,X ; Set P+1 = |nosev_x_hi| AND #%01111111 STA P+1 LDA INWK+1,X ; Set A = sign bit of nosev_x_lo AND #%10000000 JSR DVID3B2 ; Call DVID3B2 to calculate: ; ; K(3 2 1 0) = (A P+1 P) / (z_sign z_hi z_lo) LDA K ; Fetch the lowest byte of the result into A LDY K+1 ; Fetch the second byte of the result into Y BEQ P%+4 ; If the second byte is 0, skip the next instruction LDA #254 ; The second byte is non-zero, so the result won't fit ; into one byte, so set A = 254 as our maximum one-byte ; value to return LDY K+3 ; Fetch the sign of the result from K+3 into Y INX ; Add 2 to X so the index points to the next coordinate INX ; in this orientation vector (so consecutive calls to ; the routine will start with x, then move onto y and z) RTS ; Return from the subroutine
Name: PLS2 [Show more] Type: Subroutine Category: Drawing planets Summary: Draw a half-ellipse Deep dive: Drawing ellipses Drawing meridians and equators
Context: See this subroutine on its own page References: This subroutine is called as follows: * PL9 (Part 2 of 3) calls PLS2

Draw a half-ellipse, used for the planet's equator and meridian.
.PLS2 LDA #31 ; Set TGT = 31, so we only draw half an ellipse STA TGT ; Fall through into PLS22 to draw the half-ellipse
Name: PLS22 [Show more] Type: Subroutine Category: Drawing planets Summary: Draw an ellipse or half-ellipse Deep dive: Drawing ellipses Drawing meridians and equators Drawing craters
Context: See this subroutine on its own page References: This subroutine is called as follows: * PL9 (Part 3 of 3) calls PLS22 * SUN (Part 1 of 2) calls via PL40

Draw an ellipse or half-ellipse, to be used for the planet's equator and meridian (in which case we draw half an ellipse), or crater (in which case we draw a full ellipse). The ellipse is defined by a centre point, plus two conjugate radius vectors, u and v, where: u = [ u_x ] v = [ v_x ] [ u_y ] [ v_y ] The individual components of these 2D vectors (i.e. u_x, u_y etc.) are 16-bit sign-magnitude numbers, where the high bytes contain only the sign bit (in bit 7), with bits 0 to 6 being clear. This means that as we store u_x as (XX16 K2), for example, we know that |u_x| = K2. This routine calls BLINE to draw each line segment in the ellipse, passing the coordinates as follows: K6(1 0) = K3(1 0) + u_x * cos(CNT2) + v_x * sin(CNT2) K6(3 2) = K4(1 0) - u_y * cos(CNT2) - v_y * sin(CNT2) The y-coordinates are negated because BLINE expects pixel coordinates but the u and v vectors are extracted from the orientation vector. The y-axis runs in the opposite direction in 3D space to that on the screen, so we need to negate the 3D space coordinates before we can combine them with the ellipse's centre coordinates.
Arguments: K(1 0) The planet's radius K3(1 0) The pixel x-coordinate of the centre of the ellipse K4(1 0) The pixel y-coordinate of the centre of the ellipse (XX16 K2) The x-component of u (i.e. u_x), where XX16 contains just the sign of the sign-magnitude number (XX16+1 K2+1) The y-component of u (i.e. u_y), where XX16+1 contains just the sign of the sign-magnitude number (XX16+2 K2+2) The x-component of v (i.e. v_x), where XX16+2 contains just the sign of the sign-magnitude number (XX16+3 K2+3) The y-component of v (i.e. v_y), where XX16+3 contains just the sign of the sign-magnitude number TGT The number of segments to draw: * 32 for a half ellipse (a meridian) * 64 for a full ellipse (a crater) CNT2 The starting segment for drawing the half-ellipse
Other entry points: PL40 Contains an RTS
.PLS22 LDX #0 ; Set CNT = 0 STX CNT DEX ; Set FLAG = $FF to reset the ball line heap in the call STX FLAG ; to the BLINE routine below .PLL4 LDA CNT2 ; Set X = CNT2 mod 32 AND #31 ; TAX ; So X is the starting segment, reduced to the range 0 ; to 32, so as there are 64 segments in the circle, this ; reduces the starting angle to 0 to 180 degrees, so we ; can use X as an index into the sine table (which only ; contains values for segments 0 to 31) ; ; Also, because CNT2 mod 32 is in the range 0 to 180 ; degrees, we know that sin(CNT2 mod 32) is always ; positive, or to put it another way: ; ; sin(CNT2 mod 32) = |sin(CNT2)| LDA SNE,X ; Set Q = sin(X) STA Q ; = sin(CNT2 mod 32) ; = |sin(CNT2)| LDA K2+2 ; Set A = K2+2 ; = |v_x| JSR FMLTU ; Set R = A * Q / 256 STA R ; = |v_x| * |sin(CNT2)| LDA K2+3 ; Set A = K2+3 ; = |v_y| JSR FMLTU ; Set K = A * Q / 256 STA K ; = |v_y| * |sin(CNT2)| LDX CNT2 ; If CNT2 >= 33 then this sets the C flag, otherwise CPX #33 ; it's clear, so this means that: ; ; * C is clear if the segment starts in the first half ; of the circle, 0 to 180 degrees ; ; * C is set if the segment starts in the second half ; of the circle, 180 to 360 degrees ; ; In other words, the C flag contains the sign bit for ; sin(CNT2), which is positive for 0 to 180 degrees ; and negative for 180 to 360 degrees LDA #0 ; Shift the C flag into the sign bit of XX16+5, so ROR A ; XX16+5 has the correct sign for sin(CNT2) STA XX16+5 ; ; Because we set the following above: ; ; K = |v_y| * |sin(CNT2)| ; R = |v_x| * |sin(CNT2)| ; ; we can add XX16+5 as the high byte to give us the ; following: ; ; (XX16+5 K) = |v_y| * sin(CNT2) ; (XX16+5 R) = |v_x| * sin(CNT2) LDA CNT2 ; Set X = (CNT2 + 16) mod 32 CLC ; ADC #16 ; So we can use X as a lookup index into the SNE table AND #31 ; to get the cosine (as there are 16 segments in a TAX ; quarter-circle) ; ; Also, because the sine table only contains positive ; values, we know that sin((CNT2 + 16) mod 32) will ; always be positive, or to put it another way: ; ; sin((CNT2 + 16) mod 32) = |cos(CNT2)| LDA SNE,X ; Set Q = sin(X) STA Q ; = sin((CNT2 + 16) mod 32) ; = |cos(CNT2)| LDA K2+1 ; Set A = K2+1 ; = |u_y| JSR FMLTU ; Set K+2 = A * Q / 256 STA K+2 ; = |u_y| * |cos(CNT2)| LDA K2 ; Set A = K2 ; = |u_x| JSR FMLTU ; Set P = A * Q / 256 STA P ; = |u_x| * |cos(CNT2)| ; ; The call to FMLTU also sets the C flag, so in the ; following, ADC #15 adds 16 rather than 15 LDA CNT2 ; If (CNT2 + 16) mod 64 >= 33 then this sets the C flag, ADC #15 ; otherwise it's clear, so this means that: AND #63 ; CMP #33 ; * C is clear if the segment starts in the first or ; last quarter of the circle, 0 to 90 degrees or 270 ; to 360 degrees ; ; * C is set if the segment starts in the second or ; third quarter of the circle, 90 to 270 degrees ; ; In other words, the C flag contains the sign bit for ; cos(CNT2), which is positive for 0 to 90 degrees or ; 270 to 360 degrees, and negative for 90 to 270 degrees LDA #0 ; Shift the C flag into the sign bit of XX16+4, so: ROR A ; XX16+4 has the correct sign for cos(CNT2) STA XX16+4 ; ; Because we set the following above: ; ; K+2 = |u_y| * |cos(CNT2)| ; P = |u_x| * |cos(CNT2)| ; ; we can add XX16+4 as the high byte to give us the ; following: ; ; (XX16+4 K+2) = |u_y| * cos(CNT2) ; (XX16+4 P) = |u_x| * cos(CNT2) LDA XX16+5 ; Set S = the sign of XX16+2 * XX16+5 EOR XX16+2 ; = the sign of v_x * XX16+5 STA S ; ; So because we set this above: ; ; (XX16+5 R) = |v_x| * sin(CNT2) ; ; we now have this: ; ; (S R) = v_x * sin(CNT2) LDA XX16+4 ; Set A = the sign of XX16 * XX16+4 EOR XX16 ; = the sign of u_x * XX16+4 ; ; So because we set this above: ; ; (XX16+4 P) = |u_x| * cos(CNT2) ; ; we now have this: ; ; (A P) = u_x * cos(CNT2) JSR ADD ; Set (A X) = (A P) + (S R) ; = u_x * cos(CNT2) + v_x * sin(CNT2) STA T ; Store the high byte in T, so the result is now: ; ; (T X) = u_x * cos(CNT2) + v_x * sin(CNT2) BPL PL42 ; If the result is positive, jump down to PL42 TXA ; The result is negative, so we need to negate the EOR #%11111111 ; magnitude using two's complement, first doing the low CLC ; byte in X ADC #1 TAX LDA T ; And then the high byte in T, making sure to leave the EOR #%01111111 ; sign bit alone ADC #0 STA T .PL42 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(1 0) = K3(1 0) + (T X) ADC K3 ; STA K6 ; starting with the low bytes LDA T ; And then doing the high bytes, so we now get: ADC K3+1 ; STA K6+1 ; K6(1 0) = K3(1 0) + (T X) ; = K3(1 0) + u_x * cos(CNT2) ; + v_x * sin(CNT2) ; ; K3(1 0) is the x-coordinate of the centre of the ; ellipse, so we now have the correct x-coordinate for ; our ellipse segment that we can pass to BLINE below LDA K ; Set R = K = |v_y| * sin(CNT2) STA R LDA XX16+5 ; Set S = the sign of XX16+3 * XX16+5 EOR XX16+3 ; = the sign of v_y * XX16+5 STA S ; ; So because we set this above: ; ; (XX16+5 K) = |v_y| * sin(CNT2) ; ; and we just set R = K, we now have this: ; ; (S R) = v_y * sin(CNT2) LDA K+2 ; Set P = K+2 = |u_y| * cos(CNT2) STA P LDA XX16+4 ; Set A = the sign of XX16+1 * XX16+4 EOR XX16+1 ; = the sign of u_y * XX16+4 ; ; So because we set this above: ; ; (XX16+4 K+2) = |u_y| * cos(CNT2) ; ; and we just set P = K+2, we now have this: ; ; (A P) = u_y * cos(CNT2) JSR ADD ; Set (A X) = (A P) + (S R) ; = u_y * cos(CNT2) + v_y * sin(CNT2) EOR #%10000000 ; Store the negated high byte in T, so the result is STA T ; now: ; ; (T X) = - u_y * cos(CNT2) - v_y * sin(CNT2) ; ; This negation is necessary because BLINE expects us ; to pass pixel coordinates, where y-coordinates get ; larger as we go down the screen; u_y and v_y, on the ; other hand, are extracted from the orientation ; vectors, where y-coordinates get larger as we go up ; in space, so to rectify this we need to negate the ; result in (T X) before we can add it to the ; y-coordinate of the ellipse's centre in BLINE BPL PL43 ; If the result is positive, jump down to PL43 TXA ; The result is negative, so we need to negate the EOR #%11111111 ; magnitude using two's complement, first doing the low CLC ; byte in X ADC #1 TAX LDA T ; And then the high byte in T, making sure to leave the EOR #%01111111 ; sign bit alone ADC #0 STA T .PL43 ; We now call BLINE to draw the ellipse line segment ; ; The first few instructions of BLINE do the following: ; ; K6(3 2) = K4(1 0) + (T X) ; ; which gives: ; ; K6(3 2) = K4(1 0) - u_y * cos(CNT2) ; - v_y * sin(CNT2) ; ; K4(1 0) is the pixel y-coordinate of the centre of the ; ellipse, so this gives us the correct y-coordinate for ; our ellipse segment (we already calculated the ; x-coordinate in K3(1 0) above) JSR BLINE ; Call BLINE to draw this segment, which also returns ; the updated value of CNT in A CMP TGT ; If CNT > TGT then jump to PL40 to stop drawing the BEQ P%+4 ; ellipse (which is how we draw half-ellipses) BCS PL40 LDA CNT2 ; Set CNT2 = (CNT2 + STP) mod 64 CLC ADC STP AND #63 STA CNT2 JMP PLL4 ; Jump back to PLL4 to draw the next segment .PL40 RTS ; Return from the subroutine
Name: SUN (Part 1 of 2) [Show more] Type: Subroutine Category: Drawing suns Summary: Draw the sun: Set up all the variables needed to draw the sun Deep dive: Drawing the sun
Context: See this subroutine on its own page References: This subroutine is called as follows: * PLANET calls SUN * SUN_b1 calls SUN

Draw a new sun with radius K at pixel coordinate (K3, K4), removing the old sun if there is one. This routine is used to draw the sun, as well as the star systems on the Short-range Chart. The first part sets up all the variables needed to draw the new sun.
Arguments: K The new sun's radius K3(1 0) Pixel x-coordinate of the centre of the new sun K4(1 0) Pixel y-coordinate of the centre of the new sun SUNX(1 0) The x-coordinate of the vertical centre axis of the old sun (the one currently on-screen)
.PLF3 ; This is called from below to negate X and set A to ; $FF, for when the new sun's centre is off the bottom ; of the screen (so we don't need to draw its bottom ; half) ; ; This happens when the y-coordinate of the centre of ; the sun is bigger than the y-coordinate of the bottom ; of the space view TXA ; Negate X using two's complement, so A = ~X + 1 EOR #%11111111 CLC ADC #1 CMP K ; If A >= K then the centre of the sun is further BCS PL40 ; off-screen than the radius of the sun in K, which ; means the sun is too far away from the screen to be ; visible and there is nothing to draw, to jump to PL40 ; to return from the subroutine TAX ; Set X to the negated value in A, so X = ~X + 1 .PLF17 ; This is called from below to set A to $FF, for when ; the new sun's centre is right on the bottom of the ; screen (so we don't need to draw its bottom half) LDA #$FF ; Set A = $FF JMP PLF5 ; Jump to PLF5 .SUN LDA nmiCounter ; Set the random number seed to a fairly random state STA RAND ; that's based on the NMI counter (which increments ; every VBlank, so will be pretty random) JSR CHKON ; Call CHKON to check whether any part of the new sun's ; circle appears on-screen, and if it does, set P(2 1) ; to the maximum y-coordinate of the new sun on-screen BCS PL40 ; If CHKON set the C flag then the new sun's circle does ; not appear on-screen, which means there is nothing to ; draw, so jump to PL40 to return from the subroutine LDA #0 ; Set A = 0 LDX K ; Set X = K = radius of the new sun BEQ PL40 ; If the radius of the new sun is zero then there is ; nothing to draw, so jump to PL40 to return from the ; subroutine CPX #96 ; If X >= 96, set the C flag and rotate it into bit 0 ROL A ; of A, otherwise rotate a 0 into bit 0 CPX #40 ; If X >= 40, set the C flag and rotate it into bit 0 ROL A ; of A, otherwise rotate a 0 into bit 0 CPX #16 ; If X >= 16, set the C flag and rotate it into bit 0 ROL A ; of A, otherwise rotate a 0 into bit 0 ; By now, A contains the following: ; ; * If radius is 96-255 then A = %111 = 7 ; ; * If radius is 40-95 then A = %11 = 3 ; ; * If radius is 16-39 then A = %1 = 1 ; ; * If radius is 0-15 then A = %0 = 0 ; ; The value of A determines the size of the new sun's ; ragged fringes - the bigger the sun, the bigger the ; fringes .PLF18 STA CNT ; Store the fringe size in CNT ; We now calculate the highest pixel y-coordinate of the ; new sun, given that P(2 1) contains the 16-bit maximum ; y-coordinate of the new sun on-screen LDA Yx2M1 ; Set Y to the y-coordinate of the bottom of the space ; view LDX P+2 ; If P+2 is non-zero, the maximum y-coordinate is off BNE PLF2 ; the bottom of the screen, so skip to PLF2 with A set ; to the y-coordinate of the bottom of the space view CMP P+1 ; If A < P+1, the maximum y-coordinate is underneath the BCC PLF2 ; dashboard, so skip to PLF2 with A set to the ; y-coordinate of the bottom of the space view LDA P+1 ; Set A = P+1, the low byte of the maximum y-coordinate ; of the sun on-screen BNE PLF2 ; If A is non-zero, skip to PLF2 as it contains the ; value we are after LDA #1 ; Otherwise set A = 1, the top line of the screen .PLF2 STA TGT ; Set TGT to A, the maximum y-coordinate of the sun on ; screen ; We now calculate the number of lines we need to draw ; and the direction in which we need to draw them, both ; from the centre of the new sun LDA Yx2M1 ; Set (A X) = y-coordinate of bottom of screen - K4(1 0) SEC ; SBC K4 ; Starting with the low bytes TAX LDA #0 ; And then doing the high bytes, so (A X) now contains SBC K4+1 ; the number of lines between the centre of the sun and ; the bottom of the screen. If it is positive then the ; centre of the sun is above the bottom of the screen, ; if it is negative then the centre of the sun is below ; the bottom of the screen BMI PLF3 ; If A < 0, then this means the new sun's centre is off ; the bottom of the screen, so jump up to PLF3 to negate ; the height in X (so it becomes positive), set A to $FF ; and jump down to PLF5 BNE PLF4 ; If A > 0, then the new sun's centre is at least a full ; screen above the bottom of the space view, so jump ; down to PLF4 to set X = radius and A = 0 INX ; Set the flags depending on the value of X DEX BEQ PLF17 ; If X = 0 (we already know A = 0 by this point) then ; jump up to PLF17 to set A to $FF before jumping down ; to PLF5 CPX K ; If X < the radius in K, jump down to PLF5, so if BCC PLF5 ; X >= the radius in K, we set X = radius and A = 0 .PLF4 LDX K ; Set X to the radius LDA #0 ; Set A = 0 .PLF5 STX V ; Store the height in V STA V+1 ; Store the direction in V+1 LDA K ; Set (A P) = K * K JSR SQUA2 STA K2+1 ; Set K2(1 0) = (A P) = K * K LDA P STA K2 ; By the time we get here, the variables should be set ; up as shown in the header for the PLFL subroutine
Name: SUN (Part 2 of 2) [Show more] Type: Subroutine Category: Drawing suns Summary: Draw the sun: Starting from the bottom of the sun, draw the new sun line by line Deep dive: Drawing the sun
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This part erases the old sun, starting at the bottom of the screen and working upwards until we reach the bottom of the new sun.
LDA K3 ; Set YY(1 0) to the pixel x-coordinate of the centre STA YY ; of the new sun, from K3(1 0) LDA K3+1 STA YY+1 LDY TGT ; Set Y to the maximum y-coordinate of the sun on the ; screen (i.e. the bottom of the sun), which we set up ; in part 1 LDA #0 ; Set the sub width variables to zero, so we can use STA sunWidth1 ; them below to store the widths of the sun on each STA sunWidth2 ; pixel row within each tile row STA sunWidth3 STA sunWidth4 STA sunWidth5 STA sunWidth6 STA sunWidth7 TYA ; Set A to the maximum y-coordinate of the sun, so we ; can apply the first AND below TAX ; Set X to the maximum y-coordinate of the sun, so we ; can apply the second AND below AND #%11111000 ; Each tile row contains 8 pixel rows, so to get the TAY ; y-coordinate of the first row of pixels in the tile ; row, we clear bits 0-2, so Y now contains the pixel ; y-coordinate of the top pixel row in the tile row ; containing the bottom of the sun LDA V+1 ; If V+1 is non-zero then we are doing the top half of BNE dsun11 ; the new sun, so jump down to dsun11 to work our way ; upwards from the centre towards the top of the sun ; If we get here then we are drawing the bottom half of ; of the sun, so we work our way up from the bottom by ; decrementing V for each pixel line, as V contains the ; vertical distance between the line we're drawing and ; the centre of the new sun, and it starts out pointing ; to the bottom of the sun TXA ; Set A = X mod 8, which is the pixel row within the AND #7 ; tile row of the bottom of the sun BEQ dsun8 ; If A = 0 then the bottom of the sun is only in the top ; pixel row of the tile row, so jump to dsun8 to ; calculate the sun's width on one pixel row CMP #2 ; If A = 1, jump to dsun7 to calculate the sun's width BCC dsun7 ; on two pixel rows BEQ dsun6 ; If A = 2, jump to dsun6 to calculate the sun's width ; on three pixel rows CMP #4 ; If A = 3, jump to dsun5 to calculate the sun's width BCC dsun5 ; on four pixel rows BEQ dsun4 ; If A = 4, jump to dsun4 to calculate the sun's width ; on five pixel rows CMP #6 ; If A = 5, jump to dsun3 to calculate the sun's width BCC dsun3 ; on six pixel rows BEQ dsun2 ; If A = 6, jump to dsun2 to calculate the sun's width ; on seven pixel rows ; If we get here then A = 7, so keep going to calculate ; the sun's width on all eight pixel rows, starting from ; row 7 at the bottom of the tile row, all the way up to ; pixel row 0 at the top of the tile row .dsun1 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth7 ; Store the half-width of pixel row 7 in sunWidth7 DEC V ; Decrement V, the height of the sun that we use to work ; out the width, so this makes the line get wider, as we ; move up towards the sun's centre BEQ dsun12 ; If V is zero then we have reached the centre, so jump ; to dsun12 to start working our way up from the centre, ; incrementing V instead .dsun2 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth6 ; Store the half-width of pixel row 6 in sunWidth6 DEC V ; Decrement V, the height of the sun that we use to work ; out the width, so this makes the line get wider, as we ; move up towards the sun's centre BEQ dsun13 ; If V is zero then we have reached the centre, so jump ; to dsun13 to start working our way up from the centre, ; incrementing V for the rest of this tile row .dsun3 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth5 ; Store the half-width of pixel row 5 in sunWidth5 DEC V ; Decrement V, the height of the sun that we use to work ; out the width, so this makes the line get wider, as we ; move up towards the sun's centre BEQ dsun14 ; If V is zero then we have reached the centre, so jump ; to dsun14 to start working our way up from the centre, ; incrementing V for the rest of this tile row .dsun4 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth4 ; Store the half-width of pixel row 4 in sunWidth4 DEC V ; Decrement V, the height of the sun that we use to work ; out the width, so this makes the line get wider, as we ; move up towards the sun's centre BEQ dsun15 ; If V is zero then we have reached the centre, so jump ; to dsun15 to start working our way up from the centre, ; incrementing V for the rest of this tile row .dsun5 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth3 ; Store the half-width of pixel row 3 in sunWidth3 DEC V ; Decrement V, the height of the sun that we use to work ; out the width, so this makes the line get wider, as we ; move up towards the sun's centre BEQ dsun16 ; If V is zero then we have reached the centre, so jump ; to dsun16 to start working our way up from the centre, ; incrementing V for the rest of this tile row .dsun6 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth2 ; Store the half-width of pixel row 2 in sunWidth2 DEC V ; Decrement V, the height of the sun that we use to work ; out the width, so this makes the line get wider, as we ; move up towards the sun's centre BEQ dsun17 ; If V is zero then we have reached the centre, so jump ; to dsun17 to start working our way up from the centre, ; incrementing V for the rest of this tile row .dsun7 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth1 ; Store the half-width of pixel row 1 in sunWidth1 DEC V ; Decrement V, the height of the sun that we use to work ; out the width, so this makes the line get wider, as we ; move up towards the sun's centre BEQ dsun10 ; If V is zero then we have reached the centre, so jump ; to dsun18 via dsun10 to start working our way up from ; the centre, incrementing V for the rest of this tile ; row .dsun8 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth0 ; Store the half-width of pixel row 0 in sunWidth0 DEC V ; Decrement V, the height of the sun that we use to work ; out the width, so this makes the line get wider, as we ; move up towards the sun's centre BEQ dsun9 ; If V is zero then we have reached the centre, so jump ; to dsun19 via dsun9 to start working our way up from ; the centre, incrementing V for the rest of this tile ; row JSR dsun28 ; Call dsun28 to draw all eight lines for this tile row TYA ; Set Y = Y - 8 to move up a tile row SEC SBC #8 TAY BCS dsun1 ; If the subtraction didn't underflow, then Y is still ; positive and is therefore still on-screen, so loop ; back to dsun1 to keep drawing pixel rows RTS ; Otherwise we have reached the top of the screen, so ; return from the subroutine as we are done drawing .dsun9 BEQ dsun19 ; Jump down to dsun19 (this is only used to enable us to ; use a BEQ dsun9 above) .dsun10 BEQ dsun18 ; Jump down to dsun18 (this is only used to enable us to ; use a BEQ dsun10 above) .dsun11 ; If we get here then we are drawing the top half of the ; sun, so we increment V for each pixel line as we move ; up the screen JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth7 ; Store the half-width of pixel row 7 in sunWidth7 LDX V ; Increment V, the height of the sun that we use to work INX ; out the width, so this makes the line get less wide, STX V ; as we move up and away from the sun's centre CPX K ; If V >= K then we have reached the top of the sun (as BCS dsun21 ; K is the sun's radius, so there are K pixel lines in ; each half of the sun), so jump to dsun21 to draw the ; lines that we have calculated so far for this tile row .dsun12 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth6 ; Store the half-width of pixel row 6 in sunWidth6 LDX V ; Increment V, the height of the sun that we use to work INX ; out the width, so this makes the line get less wide, STX V ; as we move up and away from the sun's centre CPX K ; If V >= K then we have reached the top of the sun (as BCS dsun22 ; K is the sun's radius, so there are K pixel lines in ; each half of the sun), so jump to dsun22 to draw the ; lines that we have calculated so far for this tile row .dsun13 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth5 ; Store the half-width of pixel row 5 in sunWidth5 LDX V ; Increment V, the height of the sun that we use to work INX ; out the width, so this makes the line get less wide, STX V ; as we move up and away from the sun's centre CPX K ; If V >= K then we have reached the top of the sun (as BCS dsun23 ; K is the sun's radius, so there are K pixel lines in ; each half of the sun), so jump to dsun23 to draw the ; lines that we have calculated so far for this tile row .dsun14 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth4 ; Store the half-width of pixel row 4 in sunWidth4 LDX V ; Increment V, the height of the sun that we use to work INX ; out the width, so this makes the line get less wide, STX V ; as we move up and away from the sun's centre CPX K ; If V >= K then we have reached the top of the sun (as BCS dsun24 ; K is the sun's radius, so there are K pixel lines in ; each half of the sun), so jump to dsun24 to draw the ; lines that we have calculated so far for this tile row .dsun15 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth3 ; Store the half-width of pixel row 3 in sunWidth3 LDX V ; Increment V, the height of the sun that we use to work INX ; out the width, so this makes the line get less wide, STX V ; as we move up and away from the sun's centre CPX K ; If V >= K then we have reached the top of the sun (as BCS dsun25 ; K is the sun's radius, so there are K pixel lines in ; each half of the sun), so jump to dsun25 to draw the ; lines that we have calculated so far for this tile row .dsun16 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth2 ; Store the half-width of pixel row 2 in sunWidth2 LDX V ; Increment V, the height of the sun that we use to work INX ; out the width, so this makes the line get less wide, STX V ; as we move up and away from the sun's centre CPX K ; If V >= K then we have reached the top of the sun (as BCS dsun26 ; K is the sun's radius, so there are K pixel lines in ; each half of the sun), so jump to dsun26 to draw the ; lines that we have calculated so far for this tile row .dsun17 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth1 ; Store the half-width of pixel row 1 in sunWidth1 LDX V ; Increment V, the height of the sun that we use to work INX ; out the width, so this makes the line get less wide, STX V ; as we move up and away from the sun's centre CPX K ; If V >= K then we have reached the top of the sun (as BCS dsun27 ; K is the sun's radius, so there are K pixel lines in ; each half of the sun), so jump to dsun27 to draw the ; lines that we have calculated so far for this tile row .dsun18 JSR PLFL ; Call PLFL to set A to the half-width of the new sun on ; the sun line given in V STA sunWidth0 ; Store the half-width of pixel row 0 in sunWidth0 LDX V ; Increment V, the height of the sun that we use to work INX ; out the width, so this makes the line get less wide, STX V ; as we move up and away from the sun's centre CPX K ; If V >= K then we have reached the top of the sun (as BCS dsun28 ; K is the sun's radius, so there are K pixel lines in ; each half of the sun), so jump to dsun28 to draw the ; lines that we have calculated so far for this tile row .dsun19 JSR dsun28 ; Call dsun28 to draw all eight lines for this tile row TYA ; Set Y = Y - 8 to move up a tile row SEC SBC #8 TAY BCC dsun20 ; If the subtraction underflowed, then Y is negative ; and is therefore off the top of the screen, so jump to ; dsun20 to return from the subroutine JMP dsun11 ; Otherwise we still have work to do, so jump up to ; dsun11 to keep working our way up the top half of the ; sun .dsun20 RTS ; Return from the subroutine .dsun21 ; If we jump here then we have reached the top of the ; sun and only need to draw pixel row 7 in the current ; tile row, so we zero sunWidth0 through sunWidth6 LDA #0 ; Zero sunWidth6 STA sunWidth6 .dsun22 ; If we jump here then we have reached the top of the ; sun and need to draw pixel rows 6 and 7 in the current ; tile row, so we zero sunWidth0 through sunWidth5 LDA #0 ; Zero sunWidth5 STA sunWidth5 .dsun23 ; If we jump here then we have reached the top of the ; sun and need to draw pixel rows 5 to 7 in the current ; tile row, so we zero sunWidth0 through sunWidth4 LDA #0 ; Zero sunWidth4 STA sunWidth4 .dsun24 ; If we jump here then we have reached the top of the ; sun and need to draw pixel rows 4 to 7 in the current ; tile row, so we zero sunWidth0 through sunWidth3 LDA #0 ; Zero sunWidth3 STA sunWidth3 .dsun25 ; If we jump here then we have reached the top of the ; sun and need to draw pixel rows 3 to 7 in the current ; tile row, so we zero sunWidth0 through sunWidth2 LDA #0 ; Zero sunWidth2 STA sunWidth2 .dsun26 ; If we jump here then we have reached the top of the ; sun and need to draw pixel rows 2 to 7 in the current ; tile row, so we zero sunWidth0 through sunWidth1 LDA #0 ; Zero sunWidth1 STA sunWidth1 .dsun27 ; If we jump here then we have reached the top of the ; sun and need to draw pixel rows 1 to 7 in the current ; tile row, so we zero sunWidth0 LDA #0 ; Zero sunWidth0 STA sunWidth0 ; So by this point sunWidth0 through sunWidth7 are set ; up with the correct widths that we need to draw on ; each pixel row of the current tile row, with some of ; them possibly set to zero ; We now fall through into dsun28 to draw these eight ; pixel rows and return from the subroutine .dsun28 ; If we jump here with a branch instruction or fall ; through from above, then we have reached the top of ; the sun and need to draw pixel rows 0 to 7 in the ; current tile row, and then we are done drawing ; ; If we call this code as a subroutine using JSR dsun28 ; then we need to draw pixel rows 0 to 7 in the current ; tile row, and when we return from the call we keep ; drawing rows ; ; In either case, we now need to draw all eight rows ; before returning from the subroutine ; ; We start by finding the smallest width out of ; sunWidth0 through sunWidth7 LDA sunWidth0 ; Set A to sunWidth0 as our starting point CMP sunWidth1 ; If A >= sunWidth1 then set A = sunWidth1, so this sets BCC dsun29 ; A = min(A, sunWidth1) LDA sunWidth1 .dsun29 CMP sunWidth2 ; If A >= sunWidth2 then set A = sunWidth2, so this sets BCC dsun30 ; A = min(A, sunWidth2) LDA sunWidth2 .dsun30 CMP sunWidth3 ; If A >= sunWidth3 then set A = sunWidth3, so this sets BCC dsun31 ; A = min(A, sunWidth3) LDA sunWidth2 .dsun31 CMP sunWidth4 ; If A >= sunWidth4 then set A = sunWidth4, so this sets BCC dsun32 ; A = min(A, sunWidth4) LDA sunWidth4 .dsun32 CMP sunWidth5 ; If A >= sunWidth5 then set A = sunWidth5, so this sets BCC dsun33 ; A = min(A, sunWidth5) LDA sunWidth5 .dsun33 CMP sunWidth6 ; If A >= sunWidth6 then set A = sunWidth6, so this sets BCC dsun34 ; A = min(A, sunWidth6) LDA sunWidth6 .dsun34 CMP sunWidth7 ; If A >= sunWidth7 then set A = sunWidth7, so this sets BCC dsun35 ; A = min(A, sunWidth7) LDA sunWidth7 ; So by this point A = min(sunWidth0 to sunWidth7), and ; we can now check to see if we can save time by drawing ; a portion of this tile row out of filled blocks BEQ dsun37 ; If A = 0 then at least one of the pixel rows needs to ; be left blank, so we can't draw the row using filled ; blocks, so jump to dsun37 to draw the tile row one ; pixel row at a time .dsun35 JSR EDGES ; Call EDGES to calculate X1 and X2 for the horizontal ; line centred on YY(1 0) and with half-width A, clipped ; to fit on-screen if necessary, so this gives us the ; coordinates of the smallest pixel row in the tile row ; that we want to draw BCS dsun37 ; If the C flag is set, then the smallest pixel row ; is off-screen, so jump to dsun37 to draw the tile row ; one pixel row at a time, as there is at least one ; pixel row in the tile row that doesn't need drawing ; If we get here then every pixel row in the tile row ; fits on-screen and contains some sun pixels, so we ; can now work out how to draw this row using filled ; tiles where possible ; ; We do this by breaking the line up into a tile at the ; left end of the row, a tile at the right end of the ; row, and a set of filled tiles in the middle ; ; We set P and P+1 to the pixel coordinates of the block ; of filled tiles in the middle LDA X2 ; Set P+1 to the x-coordinate of the right end of the AND #%11111000 ; smallest sun line by clearing bits 0-2 of X2, giving STA P+1 ; P+1 = (X2 div 8) * 8 ; ; This gives us what we want as each tile is 8 pixels ; wide LDA X1 ; Now to calculate the x-coordinate of the left end of ADC #7 ; the filled tiles, so set A = X1 + 7 (we know the C ; flag is clear for the addition as we just passed ; through a BCS) BCS dsun37 ; If the addition overflowed, then this addition pushes ; us past the right edge of the screen, so jump to ; dsun37 to draw the tile row one pixel row at a time as ; there isn't any room for filled tiles AND #%11111000 ; Clear bits 0-2 of A to give us the x-coordinate of the ; left end of the set of filled tiles CMP P+1 ; If A >= P+1 then there is no room for any filled as BCS dsun37 ; the entire line fits into one tile, so jump to dsun37 ; to draw the tile row one pixel row at a time STA P ; Otherwise we now have valid values for the ; x-coordinate range of the filled blocks in the ; middle of the row, so store A in P so the coordinate ; range is from P to P+1 CMP #248 ; If A >= 248 then we only have room for one block on BCS dsun36 ; this row, and it's at the right edge of the screen, ; so jump to dsun36 to skip the right and middle tiles ; and just draw the tile at the left end of the row JSR dsun47 ; Call dsun47 to draw the tile at the right end of this ; tile row JSR DrawSunRowOfBlocks ; Draw the tiles containing the horizontal line (P, Y) ; to (P+1, Y) with filled blocks, silhouetting any ; existing content against the sun .dsun36 JMP dsun46 ; Jump to dsun46 to draw the tile at the left end of ; this tile row, returning from the subroutine using a ; tail call as we have now drawn the middle of the row, ; plus both ends .dsun37 ; If we get here then we draw the current tile row one ; pixel row at a time 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 TYA ; Set Y = Y + 7 CLC ; ADC #7 ; We draw the lines from row 7 up the screen to row 0, TAY ; so this sets Y to the pixel y-coordinate of row 7 LDA sunWidth7 ; Call EDGES-2 to calculate X1 and X2 for the horizontal JSR EDGES-2 ; line centred on YY(1 0) and with half-width sunWidth7, ; which is the pixel line for row 7 in the tile row ; ; Calling EDGES-2 will set the C flag if A = 0, which ; isn't the case for a straight call to EDGES BCS dsun38 ; If the C flag is set then either A = 0 (in which case ; there is no sun line on this pixel row), or the line ; does not fit on-screen, so in either case skip the ; following instruction and move on to the next pixel ; row JSR HLOIN ; Draw a horizontal line from (X1, Y) to (X2, Y) to draw ; pixel row 7 of the sun on this tile row, using EOR ; logic so anything already on-screen appears as a ; silhouette in front of the sun .dsun38 DEY ; Decrement the pixel y-coordinate in Y to row 6 in the ; tile row LDA sunWidth6 ; Call EDGES-2 to calculate X1 and X2 for the horizontal JSR EDGES-2 ; line centred on YY(1 0) and with half-width sunWidth6, ; which is the pixel line for row 6 in the tile row ; ; Calling EDGES-2 will set the C flag if A = 0, which ; isn't the case for a straight call to EDGES BCS dsun39 ; If the C flag is set then either A = 0 (in which case ; there is no sun line on this pixel row), or the line ; does not fit on-screen, so in either case skip the ; following instruction and move on to the next pixel ; row JSR HLOIN ; Draw a horizontal line from (X1, Y) to (X2, Y) to draw ; pixel row 6 of the sun on this tile row, using EOR ; logic so anything already on-screen appears as a ; silhouette in front of the sun .dsun39 DEY ; Decrement the pixel y-coordinate in Y to row 5 in the ; tile row LDA sunWidth5 ; Call EDGES-2 to calculate X1 and X2 for the horizontal JSR EDGES-2 ; line centred on YY(1 0) and with half-width sunWidth5, ; which is the pixel line for row 5 in the tile row ; ; Calling EDGES-2 will set the C flag if A = 0, which ; isn't the case for a straight call to EDGES BCS dsun40 ; If the C flag is set then either A = 0 (in which case ; there is no sun line on this pixel row), or the line ; does not fit on-screen, so in either case skip the ; following instruction and move on to the next pixel ; row JSR HLOIN ; Draw a horizontal line from (X1, Y) to (X2, Y) to draw ; pixel row 5 of the sun on this tile row, using EOR ; logic so anything already on-screen appears as a ; silhouette in front of the sun .dsun40 DEY ; Decrement the pixel y-coordinate in Y to row 4 in the ; tile row LDA sunWidth4 ; Call EDGES-2 to calculate X1 and X2 for the horizontal JSR EDGES-2 ; line centred on YY(1 0) and with half-width sunWidth4, ; which is the pixel line for row 4 in the tile row ; ; Calling EDGES-2 will set the C flag if A = 0, which ; isn't the case for a straight call to EDGES BCS dsun41 ; If the C flag is set then either A = 0 (in which case ; there is no sun line on this pixel row), or the line ; does not fit on-screen, so in either case skip the ; following instruction and move on to the next pixel ; row JSR HLOIN ; Draw a horizontal line from (X1, Y) to (X2, Y) to draw ; pixel row 4 of the sun on this tile row, using EOR ; logic so anything already on-screen appears as a ; silhouette in front of the sun .dsun41 DEY ; Decrement the pixel y-coordinate in Y to row 3 in the ; tile row LDA sunWidth3 ; Call EDGES-2 to calculate X1 and X2 for the horizontal JSR EDGES-2 ; line centred on YY(1 0) and with half-width sunWidth3, ; which is the pixel line for row 3 in the tile row ; ; Calling EDGES-2 will set the C flag if A = 0, which ; isn't the case for a straight call to EDGES BCS dsun42 ; If the C flag is set then either A = 0 (in which case ; there is no sun line on this pixel row), or the line ; does not fit on-screen, so in either case skip the ; following instruction and move on to the next pixel ; row JSR HLOIN ; Draw a horizontal line from (X1, Y) to (X2, Y) to draw ; pixel row 3 of the sun on this tile row, using EOR ; logic so anything already on-screen appears as a ; silhouette in front of the sun .dsun42 DEY ; Decrement the pixel y-coordinate in Y to row 2 in the ; tile row LDA sunWidth2 ; Call EDGES-2 to calculate X1 and X2 for the horizontal JSR EDGES-2 ; line centred on YY(1 0) and with half-width sunWidth2, ; which is the pixel line for row 2 in the tile row ; ; Calling EDGES-2 will set the C flag if A = 0, which ; isn't the case for a straight call to EDGES BCS dsun43 ; If the C flag is set then either A = 0 (in which case ; there is no sun line on this pixel row), or the line ; does not fit on-screen, so in either case skip the ; following instruction and move on to the next pixel ; row JSR HLOIN ; Draw a horizontal line from (X1, Y) to (X2, Y) to draw ; pixel row 2 of the sun on this tile row, using EOR ; logic so anything already on-screen appears as a ; silhouette in front of the sun .dsun43 DEY ; Decrement the pixel y-coordinate in Y to row 1 in the ; tile row LDA sunWidth1 ; Call EDGES-2 to calculate X1 and X2 for the horizontal JSR EDGES-2 ; line centred on YY(1 0) and with half-width sunWidth1, ; which is the pixel line for row 1 in the tile row ; ; Calling EDGES-2 will set the C flag if A = 0, which ; isn't the case for a straight call to EDGES BCS dsun44 ; If the C flag is set then either A = 0 (in which case ; there is no sun line on this pixel row), or the line ; does not fit on-screen, so in either case skip the ; following instruction and move on to the next pixel ; row JSR HLOIN ; Draw a horizontal line from (X1, Y) to (X2, Y) to draw ; pixel row 1 of the sun on this tile row, using EOR ; logic so anything already on-screen appears as a ; silhouette in front of the sun .dsun44 DEY ; Decrement the pixel y-coordinate in Y to row 0 in the ; tile row LDA sunWidth0 ; Call EDGES-2 to calculate X1 and X2 for the horizontal JSR EDGES-2 ; line centred on YY(1 0) and with half-width sunWidth0, ; which is the pixel line for row 0 in the tile row ; ; Calling EDGES-2 will set the C flag if A = 0, which ; isn't the case for a straight call to EDGES BCS dsun45 ; If the C flag is set then either A = 0 (in which case ; there is no sun line on this pixel row), or the line ; does not fit on-screen, so in either case skip the ; following instruction and return from the subroutine ; as we are done JMP HLOIN ; Draw a horizontal line from (X1, Y) to (X2, Y) to draw ; pixel row 0 of the sun on this tile row, using EOR ; logic so anything already on-screen appears as a ; silhouette in front of the sun, and return from the ; subroutine using a tail call as we have now drawn all ; the lines in this row .dsun45 RTS ; Return from the subroutine .dsun46 ; If we get here then we need to draw the tile at the ; left end of the current tile row 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 P ; Set X to P, the x-coordinate of the left end of the ; middle part of the sun row (which is the same as the ; x-coordinate just to the right of the leftmost tile) BEQ dsun45 ; If X = 0 then the leftmost tile is off the left of the ; screen, so jump to dsun45 to return from the ; subroutine TYA ; Set Y = Y + 7 CLC ; ADC #7 ; We draw the lines from row 7 up the screen to row 0, TAY ; so this sets Y to the pixel y-coordinate of row 7 LDA sunWidth7 ; Draw a pixel byte for the left edge of the sun at the JSR DrawSunEdgeLeft ; left end of pixel row 7 DEY ; Decrement the pixel y-coordinate in Y to row 6 in the ; tile row LDA sunWidth6 ; Draw a pixel byte for the left edge of the sun at the JSR DrawSunEdgeLeft ; left end of pixel row 6 DEY ; Decrement the pixel y-coordinate in Y to row 5 in the ; tile row LDA sunWidth5 ; Draw a pixel byte for the left edge of the sun at the JSR DrawSunEdgeLeft ; left end of pixel row 5 DEY ; Decrement the pixel y-coordinate in Y to row 4 in the ; tile row LDA sunWidth4 ; Draw a pixel byte for the left edge of the sun at the JSR DrawSunEdgeLeft ; left end of pixel row 4 DEY ; Decrement the pixel y-coordinate in Y to row 3 in the ; tile row LDA sunWidth3 ; Draw a pixel byte for the left edge of the sun at the JSR DrawSunEdgeLeft ; left end of pixel row 3 DEY ; Decrement the pixel y-coordinate in Y to row 2 in the ; tile row LDA sunWidth2 ; Draw a pixel byte for the left edge of the sun at the JSR DrawSunEdgeLeft ; left end of pixel row 2 DEY ; Decrement the pixel y-coordinate in Y to row 1 in the ; tile row LDA sunWidth1 ; Draw a pixel byte for the left edge of the sun at the JSR DrawSunEdgeLeft ; left end of pixel row 1 DEY ; Decrement the pixel y-coordinate in Y to row 0 in the ; tile row LDA sunWidth0 ; Draw a pixel byte for the left edge of the sun at the JMP DrawSunEdgeLeft ; left end of pixel row 0 and return from the subroutine ; using a tail call .dsun47 ; If we get here then we need to draw the tile at the ; right end of the current tile row 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 P+1 ; Set X1 to P+1, the x-coordinate of the right end of STX X1 ; the middle part of the sun row (which is the same as ; x-coordinate of the left end of the rightmost tile) TYA ; Set Y = Y + 7 CLC ; ADC #7 ; We draw the lines from row 7 up the screen to row 0, TAY ; so this sets Y to the pixel y-coordinate of row 7 LDA sunWidth7 ; Draw a pixel byte for the right edge of the sun at the JSR DrawSunEdgeRight ; right end of pixel row 7 DEY ; Decrement the pixel y-coordinate in Y to row 6 in the ; tile row LDA sunWidth6 ; Draw a pixel byte for the right edge of the sun at the JSR DrawSunEdgeRight ; right end of pixel row 6 DEY ; Decrement the pixel y-coordinate in Y to row 5 in the ; tile row LDA sunWidth5 ; Draw a pixel byte for the right edge of the sun at the JSR DrawSunEdgeRight ; right end of pixel row 5 DEY ; Decrement the pixel y-coordinate in Y to row 4 in the ; tile row LDA sunWidth4 ; Draw a pixel byte for the right edge of the sun at the JSR DrawSunEdgeRight ; right end of pixel row 4 DEY ; Decrement the pixel y-coordinate in Y to row 3 in the ; tile row LDA sunWidth3 ; Draw a pixel byte for the right edge of the sun at the JSR DrawSunEdgeRight ; right end of pixel row 3 DEY ; Decrement the pixel y-coordinate in Y to row 2 in the ; tile row LDA sunWidth1 ; Draw a pixel byte for the right edge of the sun at the JSR DrawSunEdgeRight ; right end of pixel row 2 ; ; This appears to be a bug (though one you would be ; hard-pressed to detect from looking at the screen), as ; we should probably be loading sunWidth2 here, not ; sunWidth1 ; ; As it stands, on each tile row of the sun, the right ; edge always has matching lines on pixel rows 1 and 2 DEY ; Decrement the pixel y-coordinate in Y to row 1 in the ; tile row LDA sunWidth1 ; Draw a pixel byte for the right edge of the sun at the JSR DrawSunEdgeRight ; right end of pixel row 1 DEY ; Decrement the pixel y-coordinate in Y to row 0 in the ; tile row LDA sunWidth0 ; Draw a pixel byte for the right edge of the sun at the JMP DrawSunEdgeRight ; right end of pixel row 0 and return from the ; subroutine using a tail call
Name: PLFL [Show more] Type: Subroutine Category: Drawing suns Summary: Calculate the sun's width on a given pixel row Deep dive: Drawing the sun
Context: See this subroutine on its own page References: This subroutine is called as follows: * SUN (Part 2 of 2) calls PLFL * CIRCLE calls via RTS2

This part calculate the sun's width on a given pixel row.
Arguments: V As we draw lines for the new sun, V contains the vertical distance between the line we're drawing and the centre of the new sun. As we draw lines and move up the screen, we either decrement (bottom half) or increment (top half) this value. See the deep dive on "Drawing the sun" to see a diagram that shows V in action V+1 This determines which half of the new sun we are drawing as we work our way up the screen, line by line: * 0 means we are drawing the bottom half, so the lines get wider as we work our way up towards the centre, at which point we will move into the top half, and V+1 will switch to $FF * $FF means we are drawing the top half, so the lines get smaller as we work our way up, away from the centre TGT The maximum y-coordinate of the new sun on-screen (i.e. the screen y-coordinate of the bottom row of the new sun) CNT The fringe size of the new sun K2(1 0) The new sun's radius squared, i.e. K^2 Y The y-coordinate of the bottom row of the new sun
Returns: A The half-width of the sun on the line specified in V
Other entry points: RTS2 Contains an RTS
.PLFL 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 STY Y1 ; Store Y in Y1, so we can restore it after the call to ; LL5 LDA V ; Set (T P) = V * V JSR SQUA2 ; = V^2 STA T LDA K2 ; Set (R Q) = K^2 - V^2 SEC ; SBC P ; First calculating the low bytes STA Q LDA K2+1 ; And then doing the high bytes SBC T STA R JSR LL5 ; Set Q = SQRT(R Q) ; = SQRT(K^2 - V^2) ; ; So Q contains the half-width of the new sun's line at ; height V from the sun's centre - in other words, it ; contains the half-width of the sun's line on the ; current pixel row Y LDY Y1 ; Restore Y from Y1 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 DORND ; Set A and X to random numbers AND CNT ; Reduce A to a random number in the range 0 to CNT, ; where CNT is the fringe size of the new sun LDY Y1 ; Restore Y from Y1 CLC ; Set A = A + Q ADC Q ; ; So A now contains the half-width of the sun on row ; V, plus a random variation based on the fringe size BCC RTS2 ; If the above addition did not overflow then LDA #255 ; The above overflowed, so set the value of A to 255 ; So A contains the half-width of the new sun on pixel ; line Y, changed by a random amount within the size of ; the sun's fringe .RTS2 RTS ; Return from the subroutine
Name: CIRCLE [Show more] Type: Subroutine Category: Drawing circles Summary: Draw a circle for the planet Deep dive: Drawing circles
Context: See this subroutine on its own page References: This subroutine is called as follows: * PL9 (Part 1 of 3) calls CIRCLE

Draw a circle with the centre at (K3, K4) and radius K. Used to draw the planet's main outline.
Arguments: K The planet's radius K3(1 0) Pixel x-coordinate of the centre of the planet K4(1 0) Pixel y-coordinate of the centre of the planet
.CIRCLE 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 CHKON ; Call CHKON to check whether the circle fits on-screen BCS RTS2 ; If CHKON set the C flag then the circle does not fit ; on-screen, so return from the subroutine (as RTS2 ; contains an RTS) LDX K ; Set X = K = radius LDA #8 ; Set A = 8 CPX #8 ; If the radius < 8, skip to PL89 BCC PL89 LSR A ; Halve A so A = 4 CPX #60 ; If the radius < 60, skip to PL89 BCC PL89 LSR A ; Halve A so A = 2 .PL89 STA STP ; Set STP = A. STP is the step size for the circle, so ; the above sets a smaller step size for bigger circles ; Fall through into CIRCLE2 to draw the circle with the ; correct step size
Name: CIRCLE2 [Show more] Type: Subroutine Category: Drawing circles Summary: Draw a circle (for the planet or chart) Deep dive: Drawing circles
Context: See this subroutine on its own page References: This subroutine is called as follows: * CIRCLE2_b1 calls CIRCLE2

Draw a circle with the centre at (K3, K4) and radius K. Used to draw the planet and the chart circles.
Arguments: STP The step size for the circle 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 The C flag is cleared
.CIRCLE2 LDX #$FF ; Set FLAG = $FF to reset the ball line heap in the call STX FLAG ; to the BLINE routine below INX ; Set CNT = 0, our counter that goes up to 64, counting STX CNT ; segments in our circle .PLL3 LDA CNT ; Set A = CNT JSR FMLTU2 ; Call FMLTU2 to calculate: ; ; A = K * sin(A) ; = K * sin(CNT) LDX #0 ; Set T = 0, so we have the following: STX T ; ; (T A) = K * sin(CNT) ; ; which is the x-coordinate of the circle for this count LDX CNT ; If CNT < 33 then jump to PL37, as this is the right CPX #33 ; half of the circle and the sign of the x-coordinate is BCC PL37 ; correct EOR #%11111111 ; This is the left half of the circle, so we want to ADC #0 ; flip the sign of the x-coordinate in (T A) using two's TAX ; complement, so we start with the low byte and store it ; in X (the ADC adds 1 as we know the C flag is set) LDA #$FF ; And then we flip the high byte in T ADC #0 STA T TXA ; Finally, we restore the low byte from X, so we have ; now negated the x-coordinate in (T A) CLC ; Clear the C flag so we can do some more addition below .PL37 ADC K3 ; We now calculate the following: STA K6 ; ; K6(1 0) = (T A) + K3(1 0) ; ; to add the coordinates of the centre to our circle ; point, starting with the low bytes LDA K3+1 ; And then doing the high bytes, so we now have: ADC T ; STA K6+1 ; K6(1 0) = K * sin(CNT) + K3(1 0) ; ; which is the result we want for the x-coordinate LDA CNT ; Set A = CNT + 16 CLC ADC #16 JSR FMLTU2 ; Call FMLTU2 to calculate: ; ; A = K * sin(A) ; = K * sin(CNT + 16) ; = K * cos(CNT) TAX ; Set X = A ; = K * cos(CNT) LDA #0 ; Set T = 0, so we have the following: STA T ; ; (T X) = K * cos(CNT) ; ; which is the y-coordinate of the circle for this count LDA CNT ; Set A = (CNT + 15) mod 64 CLC ADC #15 AND #63 CMP #33 ; If A < 33 (i.e. CNT is 0-16 or 48-64) then jump to BCC PL38 ; PL38, as this is the bottom half of the circle and the ; sign of the y-coordinate is correct TXA ; This is the top half of the circle, so we want to EOR #%11111111 ; flip the sign of the y-coordinate in (T X) using two's ADC #0 ; complement, so we start with the low byte in X (the TAX ; ADC adds 1 as we know the C flag is set) LDA #$FF ; And then we flip the high byte in T, so we have ADC #0 ; now negated the y-coordinate in (T X) STA T CLC ; Clear the C flag so we can do some more addition below .PL38 JSR BLINE ; Call BLINE to draw this segment, which also increases ; CNT by STP, the step size CMP #65 ; If CNT >= 65 then skip the next instruction BCS P%+5 JMP PLL3 ; Jump back for the next segment CLC ; Clear the C flag to indicate success RTS ; Return from the subroutine