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

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

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

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

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

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.
;
; 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

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

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

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

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

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
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
; 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

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

; 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

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

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

.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

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

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

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
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

.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

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

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
STA V

; 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

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

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

* 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
;
;   (Y X) = (S x1_lo) / XX12+2      if T <> 0
;
; 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
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
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
;
;   (Y X) = (S R) * XX12+2      if T <> 0
;
; 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
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
; 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
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

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:

* 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
;
;   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
; 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
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

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

* 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
;
;   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
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
TAY

.LL128

RTS                    ; Return from the subroutine

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

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
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

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

; 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
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
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
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

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

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
; 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

; returning from the subroutine using a tail call

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:

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,
; 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

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:

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

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:

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:
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

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

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

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:

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

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
TAX

LDA T                  ; And then the high byte in T, making sure to leave the
EOR #%01111111         ; sign bit alone
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)
STA K6                 ; starting with the low bytes

LDA T                  ; And then doing the high bytes, so we now get:
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
TAX

LDA T                  ; And then the high byte in T, making sure to leave the
EOR #%01111111         ; sign bit alone
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
AND #63
STA CNT2

JMP PLL4               ; Jump back to PLL4 to draw the next segment

.PL40

RTS                    ; Return from the subroutine

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:

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

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

.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

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

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,

.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)

; 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
; 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
; 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

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
; 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

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:

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

BCC PL89

LSR A                  ; Halve A so A = 4

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

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

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
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:
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

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
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
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

```