Elite on the BBC Micro

# Elite L parasite source [Elite-A]

``` ELITE L FILE

CODE_L% = P%
LOAD_L% = LOAD% + P% - CODE%

Type: Subroutine
Category: Market
Summary: Work out if we have space for one tonne of cargo
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 8 of 16) calls tnpr1

Given a market item, work out whether there is room in the cargo hold for one
tonne of this item.

For standard tonne canisters, the limit is given by size of the cargo hold of
our current ship.

For items measured in kg (gold, platinum), g (gem-stones) and alien items,
there is no limit.

Arguments:

X                    The type of market item (see QQ23 for a list of market
item numbers)

Returns:

A                    The new number of items of type X in the hold

C flag               Returns the result:

* Set if there is no room for this item

* Clear if there is room for this item

.tnpr1

CPX #16                \ If we are checking whether to add alien items (item
BEQ n_aliens           \ type 16), jump to n_aliens to skip the following two
\ instructions

CPX #13                \ If X >= 13, then X = 13, 14 or 15 (gold, platinum or
BCS l_2b04             \ gem-stones), for which there is no storage limit, so
\ jump to l_2b04 to signal that there is room for this
\ item

.n_aliens

LDY #12                \ Here we count the tonne canisters we have in the hold
\ and add 1 to see if we have enough room for one more
\ tonne of cargo, using Y as the loop counter, starting
\ with Y = 12

SEC                    \ Set the C flag, so the addition below has one extra,
\ representing the extra tonne we want to try to add

LDA QQ20+16            \ Set A to the number of alien items we already have in
\ the hold

.l_2af9

ADC QQ20,Y             \ Set A = A + the number of tonnes we have in the hold
\ of market item number Y

BCS n_cargo            \ If the addition overflowed, jump to n_cargo to return
\ from the subroutine with the C flag set, as the hold
\ is already full

DEY                    \ Decrement the loop counter

BPL l_2af9             \ Loop back to add in the next market item in the hold,
\ until we have added up all market items from 12
\ (minerals) down to 0 (food)

CMP new_hold           \ If A < new_hold then the C flag will be clear (we have
\ room in the hold)
\
\ If A >= new_hold then the C flag will be set (we do
\ not have room in the hold)

.n_cargo

RTS                    \ Return from the subroutine

.l_2b04

\ If we get here then the item is gold, platinum or
\ gem-stones, for which there is no storage limit, and
\ the C flag is set

LDA QQ20,X             \ Set A to the number of units of this item that we
\ already have in the hold

ADC #0                 \ The C flag is set, so this adds one for the item we
\ just scooped

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Flight
Summary: Start the hyperspace process
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* TT102 calls hyp

Called when "H" or CTRL-H is pressed during flight. Checks the following:

* We are in space

* We are not already in a hyperspace countdown

If CTRL is being held down, we jump to Ghy to engage the galactic hyperdrive,
otherwise we check that:

* The selected system is not the current system

* We have enough fuel to make the jump

and if all the pre-jump checks are passed, we print the destination on-screen
and start the countdown.

Other entry points:

TTX111               Used to rejoin this routine from the call to TTX110

.hyp

LDA QQ22+1             \ Fetch QQ22+1, which contains the number that's shown
\ on-screen during hyperspace countdown

ORA QQ12               \ If we are docked (QQ12 = &FF) or there is already a
BNE zZ+1               \ countdown in progress, then return from the subroutine
\ using a tail call (as zZ+1 contains an RTS), as we
\ can't hyperspace when docked, or there is already a
\ countdown in progress

JSR CTRL               \ Scan the keyboard to see if CTRL is currently pressed

BMI Ghy                \ If it is, then the galactic hyperdrive has been
\ activated, so jump to Ghy to process it

LDA QQ11               \ If the current view is not 0 (i.e. not the space view)
BNE P%+8               \ then skip the following two instructions

JSR TT111              \ This is the space view, so select the system closest
\ to galactic coordinates (QQ9, QQ10)

JMP TTX111             \ Skip the following instruction, as we don't want to
\ draw the crosshairs in the space view

JSR hm                 \ This is a chart view, so call hm to redraw the chart
\ crosshairs

.TTX111

\ If we get here then the current view is either the
\ space view or a chart

LDA QQ8                \ If both bytes of the distance to the selected system
ORA QQ8+1              \ in QQ8 are zero, return from the subroutine (as zZ+1
BEQ zZ+1               \ contains an RTS), as the selected system is the
\ current system

LDA #7                 \ Move the text cursor to column 7, row 23 (in the
STA XC                 \ middle of the bottom text row)
LDA #23
STA YC

LDA #0                 \ Set QQ17 = 0 to switch to ALL CAPS
STA QQ17

LDA #189               \ Print recursive token 29 ("HYPERSPACE ")
JSR TT27

LDA QQ8+1              \ If the high byte of the distance to the selected
BNE TT147              \ system in QQ8 is > 0, then it is definitely too far to
\ jump (as our maximum range is 7.0 light years, or a
\ value of 70 in QQ8(1 0)), so jump to TT147 to print
\ "RANGE?" and return from the subroutine using a tail
\ call

LDA QQ14               \ Fetch our current fuel level from Q114 into A

CMP QQ8                \ If our fuel reserves are less than the distance to the
BCC TT147              \ selected system, then we don't have enough fuel for
\ this jump, so jump to TT147 to print "RANGE?" and
\ return from the subroutine using a tail call

LDA #'-'               \ Print a hyphen
JSR TT27

JSR cpl                \ Call cpl to print the name of the selected system

\ Fall through into wW to start the hyperspace countdown

Type: Subroutine
Category: Flight
Summary: Start a hyperspace countdown
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Ghy calls wW

Start the hyperspace countdown (for both inter-system hyperspace and the
galactic hyperdrive).

.wW

LDA #15                \ The hyperspace countdown starts from 15, so set A to
\ to 15 so we can set the two hyperspace counters

STA QQ22+1             \ Set the number in QQ22+1 to 15, which is the number
\ that's shown on-screen during the hyperspace countdown

STA QQ22               \ Set the number in QQ22 to 15, which is the internal
\ counter that counts down by 1 each iteration of the
\ main game loop, and each time it reaches zero, the
\ on-screen counter gets decremented, and QQ22 gets set
\ to 5, so setting QQ22 to 15 here makes the first tick
\ of the hyperspace counter longer than subsequent ticks

TAX                    \ Print the 8-bit number in X (i.e. 15) at text location
BNE ee3                \ (0, 1), padded to 5 digits, so it appears in the top
\ left corner of the screen, and return from the
\ subroutine using a tail call (the BNE is effectively a
\ JMP as A is never zero)

Type: Subroutine
Category: Flight
Summary: Perform a galactic hyperspace jump
Deep dive: Twisting the system seeds
Galaxy and system seeds
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* hyp calls Ghy
* hyp calls entry point zZ+1

Engage the galactic hyperdrive. Called from the hyp routine above if CTRL-H is
being pressed.

This routine also updates the galaxy seeds to point to the next galaxy. Using
a galactic hyperdrive rotates each seed byte to the left, rolling each byte
left within itself like this:

01234567 -> 12345670

to get the seeds for the next galaxy. So after 8 galactic jumps, the seeds
roll round to those of the first galaxy again.

We always arrive in a new galaxy at galactic coordinates (96, 96), and then
find the nearest system and set that as our location.

Other entry points:

zZ+1                 Contains an RTS

.Ghy

LDX GHYP               \ Fetch GHYP, which tells us whether we own a galactic
BEQ zZ+1               \ hyperdrive, and if it is zero, which means we don't,
\ return from the subroutine (as zZ+1 contains an RTS)

INC new_hold           \ Free up one tonne of space in the hold, as we have
\ just used up the galactic hyperdrive

INX                    \ We own a galactic hyperdrive, so X is &FF, so this
\ instruction sets X = 0

STX GHYP               \ The galactic hyperdrive is a one-use item, so set GHYP
\ to 0 so we no longer have one fitted

STX FIST               \ Changing galaxy also clears our criminal record, so
\ set our legal status in FIST to 0 ("clean")

STX cmdr_cour          \ Reset the special cargo delivery mission timer in
STX cmdr_cour+1        \ cmdr_cour(1 0)

JSR wW                 \ Call wW to start the hyperspace countdown

LDX #5                 \ To move galaxy, we rotate the galaxy's seeds left, so
\ set a counter in X for the 6 seed bytes

INC GCNT               \ Increment the current galaxy number in GCNT

LDA GCNT               \ Set GCNT = GCNT mod 8, so we jump from galaxy 7 back
AND #7                 \ to galaxy 0 (shown in-game as going from galaxy 8 back
STA GCNT               \ to the starting point in galaxy 1)

.G1

LDA QQ21,X             \ Load the X-th seed byte into A

ASL A                  \ Set the C flag to bit 7 of the seed

ROL QQ21,X             \ Rotate the seed in memory, which will add bit 7 back
\ in as bit 0, so this rolls the seed around on itself

DEX                    \ Decrement the counter

BPL G1                 \ Loop back for the next seed byte, until we have
\ rotated them all

.zZ

LDA #96                \ Set (QQ9, QQ10) to (96, 96), which is where we always
STA QQ9                \ arrive in a new galaxy (the selected system will be
STA QQ10               \ set to the nearest actual system later on)

JSR TT110              \ Call TT110 to show the front space view

JSR TT111              \ Call TT111 to set the current system to the nearest
\ system to (QQ9, QQ10), and put the seeds of the
\ nearest system into QQ15 to QQ15+5
\
\ This call fixes a bug in the cassette version, where
\ the galactic hyperdrive will take us to coordinates
\ (96, 96) in the new galaxy, even if there isn't
\ actually a system there, so if we jump when we are
\ low on fuel, it is possible to get stuck in the
\ middle of nowhere when changing galaxy
\
\ This call sets the current system correctly, so we
\ always arrive at the nearest system to (96, 96)

LDX #0                 \ Set the distance to the selected system in QQ8(1 0)
STX QQ8                \ to 0
STX QQ8+1

LDA #116               \ Print recursive token 116 (GALACTIC HYPERSPACE ")
JSR MESS               \ as an in-flight message

\ Fall through into jmp to set the system to the
\ current system and return from the subroutine there

JMP jmp                \ Set the current system to the selected system and
\ return from the subroutine using a tail call

Type: Subroutine
Category: Text
Summary: Print the hyperspace countdown in the top-left of the screen
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* TT102 calls ee3
* TTX66 calls ee3
* wW calls ee3

Print the 8-bit number in X at text location (1, 1). Print the number to
5 digits, left-padding with spaces for numbers with fewer than 3 digits (so
numbers < 10000 are right-aligned), with no decimal point.

Arguments:

X                    The number to print

.ee3

LDY #1                 \ Move the text cursor to column 1
STY XC

STY YC                 \ Move the text cursor to row 1

DEY                    \ Decrement Y to 0 for the high byte in pr6

JMP pr6                \ Jump to pr6 to print X to 5 digits, as the high byte
\ in Y is 0, and return from the subroutine using a tail
\ call

Type: Subroutine
Category: Text
Summary: Print an error when a system is out of hyperspace range
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* hyp calls TT147

Print "RANGE?" for when the hyperspace distance is too far

.TT147

LDA #202               \ Print token 42 ("RANGE") followed by a question mark
JMP prq                \ and return from the subroutine using a tail call

Type: Subroutine
Category: Universe
Summary: Process a jump to the system closest to (QQ9, QQ10) (flight
version)
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* TT18 calls hyp1_FLIGHT

.hyp1_FLIGHT

JSR jmp                \ Set the current system to the selected system

LDX #5                 \ We now want to copy the seeds for the selected system
\ in QQ15 into QQ2, where we store the seeds for the
\ current system, so set up a counter in X for copying
\ 6 bytes (for three 16-bit seeds)

.TT112_FLIGHT

LDA QQ15,X             \ Copy the X-th byte in QQ15 to the X-th byte in QQ2, to
STA QQ2,X              \ update the selected system to the new one. Note that
\ this approach has a minor bug associated with it: if
\ your hyperspace counter hits 0 just as you're docking,
\ then you will magically appear in the station in your
\ hyperspace destination, without having to go to the
\ effort of actually flying there. This bug was fixed in
\ later versions by saving the destination seeds in a
\ separate location called safehouse, and using those
\ instead... but that isn't the case in this version

DEX                    \ Decrement the counter

BPL TT112_FLIGHT       \ Loop back to TT112_FLIGHT if we still have more bytes
\ to copy

INX                    \ Set X = 0 (as we ended the above loop with X = &FF)

STX EV                 \ Set EV, the extra vessels spawning counter, to 0, as
\ we are entering a new system with no extra vessels
\ spawned

LDA QQ3                \ Set the current system's economy in QQ28 to the
STA QQ28               \ selected system's economy from QQ3

LDA QQ5                \ Set the current system's tech level in tek to the
STA tek                \ selected system's economy from QQ5

LDA QQ4                \ Set the current system's government in gov to the
STA gov                \ selected system's government from QQ4

JSR DORND              \ Set A and X to random numbers

STA QQ26               \ Set QQ26 to the random byte that's used in the market
\ calculations

JMP GVL                \ Jump to GVL to calculate the availability of market
\ items, returning from the subroutine using a tail call

Type: Subroutine
Category: Universe
Summary: Spawn a Thargoid ship and a Thargon companion
Deep dive: Fixing ship positions
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* MJP calls GTHG
* Main game loop for flight (Part 4 of 6) calls GTHG

.GTHG

JSR Ze                 \ Call Ze to initialise INWK
\
\ Note that because Ze uses the value of X returned by
\ DORND, and X contains the value of A returned by the
\ previous call to DORND, this does not set the new ship
\ to a totally random location. See the deep dive on
\ "Fixing ship positions" for details

LDA #%11111111         \ Set the AI flag in byte #32 so that the ship has AI,
STA INWK+32            \ is extremely and aggressively hostile, and has E.C.M.

LDA #THG               \ Call NWSHP to add a new Thargoid ship to our local
JSR NWSHP              \ bubble of universe

LDA #TGL               \ Call NWSHP to add a new Thargon ship to our local
JMP NWSHP              \ bubble of universe, and return from the subroutine
\ using a tail call

Type: Subroutine
Category: Flight
Summary: Process a mis-jump into witchspace
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* TT18 calls MJP
* TT18 calls entry point RTS111

Process a mis-jump into witchspace (which happens very rarely). Witchspace has
a strange, almost dust-free aspect to it, and it is populated by hostile
Thargoids. Using our escape pod will be fatal, and our position on the
galactic chart is in-between systems. It is a scary place...

There is a 1% chance that this routine is called from TT18 instead of doing
a normal hyperspace, or we can manually trigger a mis-jump by holding down
CTRL after first enabling the "author display" configuration option ("X") when
paused.

Other entry points:

RTS111               Contains an RTS

.MJP

LDA #3                 \ Call SHIPinA populate the ship blueprints table but
JSR SHIPinA            \ without setting the compass to show the planet (the
\ LDA here has no effect and is left over from the disc
\ version)

LDA #3                 \ Clear the top part of the screen, draw a white border,
JSR TT66               \ and set the current view type in QQ11 to 3

JSR LL164              \ Call LL164 to show the hyperspace tunnel and make the
\ hyperspace sound for a second time (as we already
\ called LL164 in TT18)

JSR RES2               \ Reset a number of flight variables and workspaces, as
\ well as setting Y to &FF

STY MJ                 \ Set the mis-jump flag in MJ to &FF, to indicate that
\ we are now in witchspace

.MJP1

JSR GTHG               \ Call GTHG to spawn a Thargoid ship

LDA #3                 \ Fetch the number of Thargoid ships from MANY+THG, and
CMP MANY+THG           \ if it is less than or equal to 3, loop back to MJP1 to
BCS MJP1               \ spawn another one, until we have four Thargoids

STA NOSTM              \ Set NOSTM (the maximum number of stardust particles)
\ to 3, so there are fewer bits of stardust in
\ witchspace (normal space has a maximum of 18)

LDX #0                 \ Initialise the front space view
JSR LOOK1

LDA QQ1                \ Fetch the current system's galactic y-coordinate in
EOR #%00011111         \ QQ1 and flip bits 0-5, so we end up somewhere in the
STA QQ1                \ vicinity of our original destination, but above or
\ below it in the galactic chart

.RTS111

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Flight
Summary: Try to initiate a jump into hyperspace
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* TT102 calls TT18
* Main flight loop (Part 3 of 16) calls entry point hyper_snap

Try to go through hyperspace. Called from TT102 in the main loop when the
hyperspace countdown has finished.

Other entry points:

hyper_snap           Perform a hyperspace, but without using up any fuel

.TT18

LDA QQ14               \ Subtract the distance to the selected system (in QQ8)
SEC                    \ from the amount of fuel in our tank (in QQ14) into A
SBC QQ8

STA QQ14               \ Store the updated fuel amount in QQ14

.hyper_snap

LDA QQ11               \ If the current view is not a space view, jump to ee5
BNE ee5                \ to skip the following

JSR TT66               \ Clear the top part of the screen, draw a white border,
\ and set the current view type in QQ11 to 0 (space
\ view)

JSR LL164              \ Call LL164 to show the hyperspace tunnel and make the
\ hyperspace sound

.ee5

JSR DORND              \ Set A and X to random numbers

CMP #253               \ If A >= 253 (1% chance) then jump to MJP to trigger a
BCS MJP                \ mis-jump into witchspace

JSR hyp1_FLIGHT        \ Jump straight to the system at (QQ9, QQ10)

JSR RES2               \ Reset a number of flight variables and workspaces

JSR SOLAR              \ Halve our legal status, update the missile indicators,
\ and set up data blocks and slots for the planet and
\ sun

JSR LSHIPS             \ Call LSHIPS to populate the ship blueprints table
\ with a random selection of ships

LDA QQ11               \ If the current view in QQ11 is not a space view (0) or
AND #%00111111         \ one of the charts (64 or 128), return from the
BNE RTS111             \ subroutine (as RTS111 contains an RTS)

JSR TTX66              \ Otherwise clear the screen and draw a white border

LDA QQ11               \ If the current view is one of the charts, jump to
BNE TT114              \ TT114 (from which we jump to the correct routine to
\ display the chart)

INC QQ11               \ This is a space view, so increment QQ11 to 1

\ Fall through into TT110 to show the front space view

Type: Subroutine
Category: Flight
Summary: Launch from a station or show the front space view
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Ghy calls TT110
* TT102 calls TT110

Launch the ship (if we are docked), or show the front space view (if we are

Called when red key f0 is pressed while docked (launch), after we arrive in a
new galaxy, or after a hyperspace if the current view is a space view.

.TT110

LDX QQ12               \ If we are not docked (QQ12 = 0) then jump to NLUNCH
BEQ NLUNCH             \ to skip the launch tunnel and setup process

JSR LAUN               \ Show the space station launch tunnel

JSR RES2               \ Reset a number of flight variables and workspaces

JSR TT111              \ Select the system closest to galactic coordinates
\ (QQ9, QQ10)

INC INWK+8             \ Increment z_sign ready for the call to SOS, so the
\ planet appears at a z_sign of 1 in front of us when
\ we launch

JSR SOS1               \ Call SOS1 to set up the planet's data block and add it
\ to FRIN, where it will get put in the first slot as
\ it's the first one to be added to our local bubble of
\ universe following the call to RES2 above

LDA #128               \ For the space station, set z_sign to &80, so it's
STA INWK+8             \ behind us (&80 is negative)

INC INWK+7             \ And increment z_hi, so it's only just behind us

JSR NWSPS              \ Add a new space station to our local bubble of
\ universe

LDA #12                \ Set our launch speed in DELTA to 12
STA DELTA

JSR BAD                \ Call BAD to work out how much illegal contraband we
\ are carrying in our hold (A is up to 40 for a
\ standard hold crammed with contraband, up to 70 for
\ an extended cargo hold full of narcotics and slaves)

ORA FIST               \ OR the value in A with our legal status in FIST to
\ get a new value that is at least as high as both
\ values, to reflect the fact that launching with a
\ hold full of contraband can only make matters worse

STA FIST               \ Update our legal status with the new value

LDA #255               \ Set the view number in QQ11 to 255
STA QQ11

JSR HFS1               \ Call HFS1 to draw 8 concentric rings

.NLUNCH

LDX #0                 \ Set QQ12 to 0 to indicate we are not docked
STX QQ12

JMP LOOK1              \ Jump to LOOK1 to switch to the front view (X = 0),
\ returning from the subroutine using a tail call

Type: Subroutine
Category: Charts
Summary: Display either the Long-range or Short-range Chart
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* TT18 calls TT114

Display either the Long-range or Short-range Chart, depending on the current
view setting. Called from TT18 once we know the current view is one of the
charts.

Arguments:

A                    The current view, loaded from QQ11

.TT114

BMI TT115              \ If bit 7 of the current view is set (i.e. the view is
\ the Short-range Chart, 128), skip to TT115 below to
\ jump to TT23 to display the chart

JMP TT22               \ Otherwise the current view is the Long-range Chart, so
\ jump to TT22 to display it

.TT115

JMP TT23               \ Jump to TT23 to display the Short-range Chart

Type: Subroutine
Category: Tube
Summary: Update the value of LASCT by sending a write_0346 command to the
I/O processor
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* DEATH calls write_0346
* Main flight loop (Part 3 of 16) calls write_0346
* TTX66 calls write_0346

Arguments:

A                    The new value of LASCT

.write_0346

PHA                    \ Store the new value of LASCT on the stack

LDA #&97               \ Send command &97 to the I/O processor:
JSR tube_write         \
\   write_0346(value)
\
\ which will set the I/O processor's copy of LASCT to
\ the given value

PLA                    \ Send the parameter to the I/O processor:
JMP tube_write         \
\   * value = the new value of LASCT that we stored on
\             the stack
\
\ and return from the subroutine using a tail call

Type: Subroutine
Category: Tube
Summary: Get the value of LASCT by sending a read_0346 command to the I/O
processor
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* DEATH calls read_0346
* Main flight loop (Part 16 of 16) calls read_0346
* Main flight loop (Part 3 of 16) calls read_0346

LDA #&98               \ Send command &98 to the I/O processor:
JSR tube_write         \
\
\ which will ask the I/O processor to send the value of
\ its copy of LASCT

JSR tube_read          \ Set A to the response from the I/O processor, which
\ will be the value of LASCT that's stored in the I/O
\ processor

STA LASCT              \ Update LASCT to the value received from the I/O
\ processor

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

.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

RTS                    \ Return from the subroutine

.DOEXP

LDA INWK+31            \ If bit 6 of the ship's byte #31 is clear, then the
AND #%01000000         \ ship is not already exploding so there is no existing
BEQ P%+5               \ explosion cloud to remove, so skip the following
\ instruction

JSR PTCLS              \ Call PTCLS to remove the existing cloud by drawing it
\ again

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

LDY #1                 \ Fetch byte #1 of the ship line heap, which contains
LDA (XX19),Y           \ the cloud counter

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 (XX19),Y           \ Store the updated cloud counter in byte #1 of the ship
\ line heap

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

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

DEY                    \ Decrement Y to 0

STA (XX19),Y           \ Store the cloud size in byte #0 of the ship line heap

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 DOEXP-1            \ is being drawn on-screen for this ship anyway, so
\ return from the subroutine (as DOEXP-1 contains an
\ RTS)

LDY #2                 \ Otherwise it's time to draw an explosion cloud, so
LDA (XX19),Y           \ fetch byte #2 of the ship line heap into Y, which we
TAY                    \ set to the explosion count for this ship (i.e. the
\ number of vertices used as origins for explosion
\ clouds)
\
\ The explosion count is stored as 4 * n + 6, where n is
\ the number of vertices, so the following loop copies
\ the coordinates of the first n vertices from the heap
\ at XX3, which is where we stored all the visible
\ vertex coordinates in part 8 of the LL9 routine, and
\ sticks them in the ship line heap pointed to by XX19,
\ starting at byte #7 (so it leaves the first 6 bytes of
\ the ship line heap alone)

.EXL1

LDA XX3-7,Y            \ Copy byte Y-7 from the XX3 heap, into the Y-th byte of
STA (XX19),Y           \ the ship line heap

DEY                    \ Decrement the loop counter

CPY #6                 \ Keep copying vertex coordinates into the ship line
BNE EXL1               \ heap until Y = 6 (which will copy n vertices, where n
\ is the number of vertices we should be exploding)

LDA INWK+31            \ Set bit 6 of the ship's byte #31 to denote that the
ORA #%01000000         \ explosion has been drawn (as it's about to be)
STA INWK+31

.PTCLS

\ This part of the routine actually draws the explosion
\ cloud

LDY #0                 \ Fetch byte #0 of the ship line heap, which contains
LDA (XX19),Y           \ the cloud size we stored above, and store it in Q
STA Q

INY                    \ Increment the index in Y to point to byte #1

LDA (XX19),Y           \ Fetch byte #1 of the ship line heap, which contains
\ the cloud counter. We are now going to process this
\ into the number of particles in each vertex's cloud

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 8 so that is has a maximum value of 15
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

INY                    \ Increment the index in Y to point to byte #2

LDA (XX19),Y           \ Fetch byte #2 of the ship line heap, which contains
STA TGT                \ the explosion count for this ship (i.e. the number of
\ 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 ship line 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
\ ship line heap, so set a counter in X for 4 bytes

.EXL3

INY                    \ Increment the index in Y so it points to the next byte
\ from the coordinate we are copying

LDA (XX19),Y           \ Copy the Y-th byte from the ship line heap to the X-th
STA K3,X               \ byte of K3

DEX                    \ Decrement the X index

BPL EXL3               \ Loop back to EXL3 until we have copied all four bytes

\ The above loop copies the vertex coordinates from the
\ ship line 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 ship line heap

LDY #2                 \ Set Y = 2, which we will use to point to bytes #3 to
\ #6, after incrementing it

\ This next loop copies bytes #3 to #6 from the ship
\ line heap 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
\
\ Note that we haven't actually set the values of bytes
\ #3 to #6 in the ship line heap, so we have no idea
\ what they are, we just use what's already there. But
\ the fact that those bytes are stored for this ship
\ means we can repeat the random generation of the
\ cloud, which is the important bit

.EXL2

INY                    \ Increment the index in Y so it points to the next
\ random number seed to copy

LDA (XX19),Y           \ Fetch the Y-th byte from the ship line heap

EOR CNT                \ EOR with the vertex index, so the seeds are different
\ for each vertex

STA &FFFD,Y            \ Y is going from 3 to 6, so this stores the four bytes
\ in memory locations &00, &01, &02 and &03, which are
\ the memory locations of RAND through RAND+3

CPY #6                 \ Loop back to EXL2 until Y = 6, which means we have
BNE EXL2               \ copied four bytes

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

JSR DORND2             \ Set ZZ to a random number, making sure the C flag
STA ZZ                 \ doesn't affect the outcome

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 #2*Y-1             \ If X > the y-coordinate of the bottom of the screen,
BCS EX11               \ 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 ship line 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

JSR DORND2             \ Set A and X to random numbers, making sure the C flag
\ doesn't affect the outcome

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

JSR DORND2             \ Set A and X to random numbers, making sure the C flag
\ doesn't affect the outcome

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: Universe
Summary: Update the missile indicators, set up the planet data block
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* SOLAR calls SOS1
* TT110 calls SOS1

Update the missile indicators, and set up a data block for the planet, but
only setting the pitch and roll counters to 127 (no damping).

.SOS1

JSR msblob             \ Reset the dashboard's missile indicators so none of
\ them are targeted

LDA #127               \ Set the pitch and roll counters to 127 (no damping
STA INWK+29            \ so the planet's rotation doesn't slow down)
STA INWK+30

LDA tek                \ Set A = 128 or 130 depending on bit 1 of the system's
AND #%00000010         \ tech level in tek
ORA #%10000000

JMP NWSHP              \ Add a new planet to our local bubble of universe,
\ with the planet type defined by A (128 is a planet
\ with an equator and meridian, 130 is a planet with
\ a crater)

Type: Subroutine
Category: Universe
Summary: Set up various aspects of arriving in a new system
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* TT18 calls SOLAR

Halve our legal status, update the missile indicators, and set up data blocks
and slots for the planet and sun.

.SOLAR

\ We now want to extract bits 3-10 of QQ8(1 0) into A,
\ so we can subtract this value from our legal status in
\ FIST (so the further we travel, the quicker our legal
\ status drops back down to clean, as we put more
\ distance between us and our crimes - specifically, we
\ drop 1.2 FIST points for each light year, as we
\ subtract (QQ8 / 8) from FIST, and QQ8 contains the
\ distance in light years * 10)

LDA QQ8                \ Set A to the low byte of the distance to the selected
\ system in QQ8(1 0), so (QQ8+1 A) contains the distance

LDY #3                 \ We're going to extract bits 3-10 by shifting QQ8(1 0)
\ right by 3 places, so we start by setting a loop
\ counter in Y

.legal_div

LSR QQ8+1              \ Shift (QQ8+1 A) to the right by one place
ROR A

DEY                    \ Decrement the loop counter

BNE legal_div          \ Loop back until we have shifted right by 3 places, by
\ which point A will contain bits 3-10 of QQ8(1 0)

\ We now subtract A from FIST, and subtract 1 more,
\ making sure we don't reduce FIST beyond 0, which we do
\ by doing the subtraction in reverse and then negating
\ the result with one's complement

SEC                    \ Set A = A - FIST (which we will negate later)
SBC FIST

BCC legal_over         \ If the subtraction underflowed, i.e. A < FIST, skip
\ the following instruction

LDA #&FF               \ A > FIST, so we set A = &FF so the EOR flips this to
\ 0, so FIST gets set to 0 when we travel far enough to
\ clear our name

.legal_over

EOR #&FF               \ Flip all the bits in A to negate the result, so if the
\ subtraction underflowed, i.e. A < FIST, we now have
\ A = FIST - A - 1

STA FIST               \ Update the value of FIST to the new value in A

JSR ZINF               \ Call ZINF to reset the INWK ship workspace, which
\ doesn't affect the C flag

LDA QQ15+1             \ Fetch s0_hi

AND #%00000011         \ Extract bits 0-1 (which also help to determine the
\ economy), which will be between 0 and 3

ADC #3                 \ Add 3 + C, to get a result between 3 and 7, clearing
\ the C flag in the process

STA INWK+8             \ Store the result in z_sign in byte #6

ROR A                  \ Halve A, rotating in the C flag (which is clear) and
STA INWK+2             \ store in both x_sign and y_sign, moving the planet to
STA INWK+5             \ the upper right

JSR SOS1               \ Call SOS1 to set up the planet's data block and add it
\ to FRIN, where it will get put in the first slot as
\ it's the first one to be added to our local bubble of
\ this new system's universe

LDA QQ15+3             \ Fetch s1_hi, extract bits 0-2, set bits 0 and 7 and
AND #%00000111         \ store in z_sign, so the sun is behind us at a distance
ORA #%10000001         \ of 1 to 7
STA INWK+8

LDA QQ15+5             \ Fetch s2_hi, extract bits 0-1 and store in x_sign and
AND #%00000011         \ y_sign, so the sun is either dead in our rear laser
STA INWK+2             \ crosshairs, or off to the top left by a distance of 1
STA INWK+1             \ or 2 when we look out the back

LDA #0                 \ Set the pitch and roll counters to 0 (no rotation)
STA INWK+29
STA INWK+30

LDA #129               \ Set A = 129, the ship type for the sun

JSR NWSHP              \ Call NWSHP to set up the sun's data block and add it
\ to FRIN, where it will get put in the second slot as
\ it's the second one to be added to our local bubble
\ of this new system's universe

Type: Subroutine
Category: Stardust
Summary: Initialise the stardust field
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* LOOK1 calls NWSTARS

This routine is called when the space view is initialised in routine LOOK1.

.NWSTARS

LDA QQ11               \ If this is not a space view, jump to WPSHPS via
BNE WPSHPSS            \ WPSHPSS to skip the initialisation of the SX, SY and
\ SZ tables

Type: Subroutine
Category: Stardust
Summary: Create a random cloud of stardust
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* DEATH calls nWq

Create a random cloud of stardust containing the correct number of dust
particles, i.e. NOSTM of them, which is 3 in witchspace and 18 (#NOST) in
normal space. Also clears the scanner and initialises the LSO block.

This is called by the DEATH routine when it displays our untimely demise.

.nWq

LDY NOSTM              \ Set Y to the current number of stardust particles, so
\ we can use it as a counter through all the stardust

.SAL4

JSR DORND              \ Set A and X to random numbers

ORA #8                 \ Set A so that it's at least 8

STA SZ,Y               \ Store A in the Y-th particle's z_hi coordinate at
\ SZ+Y, so the particle appears in front of us

STA ZZ                 \ Set ZZ to the particle's z_hi coordinate

JSR DORND              \ Set A and X to random numbers

STA SX,Y               \ Store A in the Y-th particle's x_hi coordinate at
\ SX+Y, so the particle appears in front of us

STA X1                 \ Set X1 to the particle's x_hi coordinate

JSR DORND              \ Set A and X to random numbers

STA SY,Y               \ Store A in the Y-th particle's y_hi coordinate at
\ SY+Y, so the particle appears in front of us

STA Y1                 \ Set Y1 to the particle's y_hi coordinate

JSR PIXEL2             \ Draw a stardust particle at (X1,Y1) with distance ZZ

DEY                    \ Decrement the counter to point to the next particle of
\ stardust

BNE SAL4               \ Loop back to SAL4 until we have randomised all the
\ stardust particles

\ Fall through into WPSHPS to clear the scanner and
\ reset the LSO block

Type: Subroutine
Category: Dashboard
Summary: Clear the scanner, reset the ball line and sun line heaps
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* LOOK1 calls WPSHPSS
* NWSTARS calls WPSHPSS

This routine is a duplicate of WPSHPS that is close enough to the NWSTARS
routine for it to be called by a branch instruction.

.WPSHPSS

JMP WPSHPS             \ Jump to WPSHPS to clear the scanner and reset the ball
\ line and sun line heaps

Type: Subroutine
Category: Screen mode
Summary: Show or hide the dashboard (for when we die) by sending a
write_crtc command to the I/O processor
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* DEATH calls DET1

Arguments:

X                    The number of text rows to display on the screen (24
will hide the dashboard, 31 will make it reappear)

.DET1

LDA #&95               \ Send command &95 to the I/O processor:
JSR tube_write         \
\   write_crtc(rows)
\
\ which will update the screen to show the specified
\ number of text rows

TXA                    \ Send the parameter to the I/O processor:
JMP tube_write         \
\   * rows = X
\
\ and return from the subroutine using a tail call

Type: Subroutine
Category: Flight
Summary: Charge a shield and drain some energy from the energy banks
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 13 of 16) calls SHD

Charge up a shield, and if it needs charging, drain some energy from the
energy banks.

Arguments:

X                    The value of the shield to recharge

DEX                    \ Increment the shield value so that it doesn't go past
\ a maximum of 255

RTS                    \ Return from the subroutine

.SHD

INX                    \ Increment the shield value

BEQ SHD-2              \ If the shield value is 0 then this means it was 255
\ before, which is the maximum value, so jump to SHD-2
\ to bring it back down to 258 and return

\ Otherwise fall through into DENGY to drain our energy
\ to pay for all this shield charging

Type: Subroutine
Category: Flight
Summary: Drain some energy from the energy banks
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* LASLI calls DENGY
* Main flight loop (Part 16 of 16) calls DENGY

Returns:

Z flag               Set if we have no energy left, clear otherwise

.DENGY

DEC ENERGY             \ Decrement the energy banks in ENERGY

PHP                    \ Save the flags on the stack

BNE P%+5               \ If the energy levels are not yet zero, skip the
\ following instruction

INC ENERGY             \ The minimum allowed energy level is 1, and we just
\ reached 0, so increment ENERGY back to 1

PLP                    \ Restore the flags from the stack, so we return with
\ the Z flag from the DEC instruction above

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Maths (Arithmetic)
Summary: Calculate (Y X) = A / 10
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* SP2 calls SPS2

Calculate the following, where A is a signed 8-bit integer and the result is a
signed 16-bit integer:

(Y X) = A / 10

Returns:

C flag               The C flag is cleared

.SPS2

ASL A                  \ Set X = |A| * 2, and set the C flag to the sign bit of
TAX                    \ A

LDA #0                 \ Set Y to have the sign bit from A in bit 7, with the
ROR A                  \ rest of its bits zeroed, so Y now contains the sign of
TAY                    \ the original argument

LDA #20                \ Set Q = 20
STA Q

TXA                    \ Copy X into A, so A now contains the argument A * 2

JSR DVID4              \ Calculate the following:
\
\   P = A / Q
\     = |argument A| * 2 / 20
\     = |argument A| / 10

LDX P                  \ Set X to the result

TYA                    \ If the sign of the original argument A is negative,
BMI LL163              \ jump to LL163 to flip the sign of the result

LDY #0                 \ Set the high byte of the result to 0, as the result is
\ positive

RTS                    \ Return from the subroutine

.LL163

LDY #&FF               \ The result is negative, so set the high byte to &FF

TXA                    \ Flip the low byte and add 1 to get the negated low
EOR #&FF               \ byte, using two's complement
TAX
INX

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Dashboard
Summary: Update the compass
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main game loop for flight (Part 5 of 6) calls COMPAS

.COMPAS

JSR DOT                \ Call DOT to redraw (i.e. remove) the current compass
\ dot

LDY #NI%               \ Set Y = NI%, so SPS1 will calculate the vector to the
\ second slot in the local bubble, i.e. the space
\ station or the sun

LDA SSPR               \ If we are inside the space station safe zone, jump to
BNE l_station          \ l_station to skip the following instruction and ensure
\ we draw the space station on the compass

LDY finder             \ We are not inside the space station safe zone, so
\ set the value of Y to finder, which determines whether
\ the compass is configured to show the sun or the
\ planet

.l_station

JSR SPS1               \ We now draw the planet or sun/station on the compass,
\ so first call SPS1 to calculate the vector to the
\ planet/sun/station and store it in XX15

\ Fall through into SP2 to draw XX15 on the compass

Type: Subroutine
Category: Dashboard
Summary: Draw a dot on the compass, given the planet/station vector
Context: See this subroutine on its own page
References: No direct references to this subroutine in this source file

Draw a dot on the compass to represent the planet or station, whose normalised
vector is in XX15.

XX15 to XX15+2      The normalised vector to the planet or space station,
stored as x in XX15, y in XX15+1 and z in XX15+2

.SP2

LDA XX15               \ Set A to the x-coordinate of the planet or station to
\ show on the compass, which will be in the range -96 to
\ +96 as the vector has been normalised

JSR SPS2               \ Set (Y X) = A / 10, so X will be from -9 to +9, which
\ is the x-offset from the centre of the compass of the
\ dot we want to draw. Returns with the C flag clear

TXA                    \ Set COMX = 195 + X, as 186 is the pixel x-coordinate
ADC #195               \ of the leftmost dot possible on the compass, and X can
STA COMX               \ be -9, which would be 195 - 9 = 186. This also means
\ that the highest value for COMX is 195 + 9 = 204,
\ which is the pixel x-coordinate of the rightmost dot
\ in the compass... but the compass dot is actually two
\ pixels wide, so the compass dot can overlap the right
\ edge of the compass, but not the left edge

LDA XX15+1             \ Set A to the y-coordinate of the planet or station to
\ show on the compass, which will be in the range -96 to
\ +96 as the vector has been normalised

JSR SPS2               \ Set (Y X) = A / 10, so X will be from -9 to +9, which
\ is the y-offset from the centre of the compass of the
\ dot we want to draw. Returns with the C flag clear

STX T                  \ Set COMY = 204 - X, as 203 is the pixel y-coordinate
LDA #204               \ of the centre of the compass, the C flag is clear,
SBC T                  \ and the y-axis needs to be flipped around (because
STA COMY               \ when the planet or station is above us, and the
\ vector is therefore positive, we want to show the dot
\ higher up on the compass, which has a smaller pixel
\ y-coordinate). So this calculation does this:
\
\   COMY = 204 - X - (1 - 0) = 203 - X

LDA #&F0               \ Set A to a 4-pixel mode 5 byte row in colour 2
\ (yellow/white), the colour for when the planet or
\ station in the compass is in front of us

LDX XX15+2             \ If the z-coordinate of the XX15 vector is positive,
BPL P%+4               \ skip the following instruction

LDA #&FF               \ The z-coordinate of XX15 is negative, so the planet or
\ station is behind us and the compass dot should be in
\ green/cyan, so set A to a 4-pixel mode 5 byte row in
\ colour 3

STA COMC               \ Store the compass colour in COMC

\ Fall through into DOT to draw the dot on the compass

Type: Subroutine
Category: Dashboard
Summary: Draw a dash on the compass
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* COMPAS calls DOT

Arguments:

COMX                 The screen pixel x-coordinate of the dash

COMY                 The screen pixel y-coordinate of the dash

COMC                 The colour and thickness of the dash:

* &F0 = a double-height dash in yellow/white, for when
the object in the compass is in front of us

* &FF = a single-height dash in green/cyan, for when
the object in the compass is behind us

.DOT

LDA COMY               \ Set Y1 = COMY, the y-coordinate of the dash
STA Y1

LDA COMX               \ Set X1 = COMX, the x-coordinate of the dash
STA X1

LDA COMC               \ Set COL = COMC, the mode 5 colour byte for the dash
STA COL

CMP #&F0               \ If COL is &F0 then the planet/station is in front of
BNE CPIX2              \ us and we want to draw a double-height dash, so if it
\ isn't &F0 jump to CPIX2 to draw a single-height dash

\ Otherwise fall through into CPIX4 to draw a double-
\ height dash

Type: Subroutine
Category: Drawing pixels
Summary: Draw a double-height dot on the dashboard
Context: See this subroutine on its own page
References: No direct references to this subroutine in this source file

Draw a double-height mode 5 dot (2 pixels high, 2 pixels wide).

Arguments:

X1                   The screen pixel x-coordinate of the bottom-left corner
of the dot

Y1                   The screen pixel y-coordinate of the bottom-left corner
of the dot

COL                  The colour of the dot as a mode 5 character row byte

.CPIX4

JSR CPIX2              \ Call CPIX2 to draw a single-height dash at (X1, Y1)

DEC Y1                 \ Decrement Y1

\ Fall through into CPIX2 to draw a second single-height
\ dash on the pixel row above the first one, to create a
\ double-height dot

Type: Subroutine
Category: Drawing pixels
Summary: Draw a single-height dash on the dashboard by sending a draw_blob
command to the I/O processor
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* CPIX4 calls CPIX2
* DOT calls CPIX2

Arguments:

X1                   The screen pixel x-coordinate of the dash

Y1                   The screen pixel y-coordinate of the dash

COL                  The colour of the dash as a mode 5 character row byte

.CPIX2

LDA #&90               \ Send command &90 to the I/O processor:
JSR tube_write         \
\   draw_blob(x, y, colour)
\
\ which will draw a dash of the specified colour and
\ position on the dashboard

LDA X1                 \ Send the first parameter to the I/O processor:
JSR tube_write         \
\   * x = X1

LDA Y1                 \ Send the second parameter to the I/O processor:
JSR tube_write         \
\   * y = Y1

LDA COL                \ Send the third parameter to the I/O processor:
JMP tube_write         \
\   * colour = COL
\
\ and return from the subroutine using a tail call

Type: Subroutine
Category: Flight
Summary: Take some damage, taking our ship's shields into consideration
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* TACTICS (Part 1 of 7) calls n_oops
* TACTICS (Part 6 of 7) calls n_oops

We just took some damage, so calculate whether the amount of damage will be
sucked up by the shields, and if not, apply that damage to our ship.

Arguments:

A                    The amount of damage to take

.n_oops

SEC                    \ Reduce the amount of damage in A by the level of our
SBC new_shields        \ shields in new_shields

BCC n_shok             \ If the amount of damage is less than the level of our
\ shields, then return from the subroutine without
\ taking any damage (as b_shok contains an RTS)

\ The amount of damage is greater than our shield level,
\ so we need to take some damage. The amount of damage
\ has already been reduced by our shield level (as they
\ absorb the amount of damage in new_shields), so fall
\ into OOPS to take the remaining amount of damage

Type: Subroutine
Category: Flight
Summary: Take some damage
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 10 of 16) calls OOPS
* n_oops calls entry point n_shok

We just took some damage, so reduce the shields if we have any, or reduce the
energy levels and potentially take some damage to the cargo if we don't.

Arguments:

A                    The amount of damage to take

INF                  The address of the ship block for the ship that attacked
us, or the ship that we just ran into

Other entry points:

n_shok               Contains an RTS

.OOPS

STA T                  \ Store the amount of damage in T

LDX #0                 \ Fetch byte #8 (z_sign) for the ship attacking us, and
LDY #8                 \ set X = 0
LDA (INF),Y

BMI OO1                \ If A is negative, then we got hit in the rear, so jump
\ to OO1 to process damage to the aft shield

LDA FSH                \ Otherwise the forward shield was damaged, so fetch the
SBC T                  \ shield strength from FSH and subtract the damage in T

BCC OO2                \ If the C flag is clear then this amount of damage was
\ too much for the shields, so jump to OO2 to set the
\ shield level to 0 and start taking damage directly
\ from the energy banks

STA FSH                \ Store the new value of the forward shield in FSH

.n_shok

RTS                    \ Return from the subroutine

.OO2

STX FSH                \ Set the forward shield to 0

BCC OO3                \ Jump to OO3 to start taking damage directly from the
\ energy banks (this BCC is effectively a JMP as the C
\ flag is clear, as we jumped to OO2 with a BCC)

.OO1

LDA ASH                \ The aft shield was damaged, so fetch the shield
SBC T                  \ strength from ASH and subtract the damage in T

BCC OO5                \ If the C flag is clear then this amount of damage was
\ too much for the shields, so jump to OO5 to set the
\ shield level to 0 and start taking damage directly
\ from the energy banks

STA ASH                \ Store the new value of the aft shield in ASH

RTS                    \ Return from the subroutine

.OO5

STX ASH                \ Set the aft shield to 0

.OO3

ADC ENERGY             \ A is negative and contains the amount by which the
STA ENERGY             \ damage overwhelmed the shields, so this drains the
\ energy banks by that amount (and because the energy
\ banks are shown over four indicators rather than one,
\ but with the same value range of 0-255, energy will
\ appear to drain away four times faster than the
\ shields did)

BEQ P%+4               \ If we have just run out of energy, skip the next
\ instruction to jump straight to our death

BCS P%+5               \ If the C flag is set, then subtracting the damage from
\ the energy banks didn't underflow, so we had enough
\ energy to survive, and we can skip the next
\ instruction to make a sound and take some damage

JMP DEATH              \ Otherwise our energy levels are either 0 or negative,
\ and in either case that means we jump to our DEATH,
\ returning from the subroutine using a tail call

JSR EXNO3              \ We didn't die, so call EXNO3 to make the sound of a
\ collision

JMP OUCH               \ And jump to OUCH to take damage and return from the
\ subroutine using a tail call

Type: Subroutine
Category: Maths (Geometry)
Summary: Copy a space coordinate from the K% block into K3
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* SPS1 calls SPS3

Copy one of the space coordinates of the planet, sun or space station into the
corresponding location in the temporary variable K3. The high byte and
absolute value of the sign byte are copied into the first two K3 bytes, and
the sign of the sign byte is copied into the highest K3 byte.

The comments below are written for copying the planet's x-coordinate into
K3(2 1 0).

Arguments:

X                    Determines where to copy the coordinate to:

* X = 0 copies the coordinate into K3(2 1 0)

* X = 3 copies the coordinate into K3(5 4 3)

* X = 6 copies the coordinate into K3(8 7 6)

Y                    Determines which coordinate to copy:

* Y = 0 copies (x_sign, x_hi) of planet

* Y = 3 copies (y_sign, y_hi) of planet

* Y = 6 copies (z_sign, z_hi) of planet

* Y = NI% + 0 copies (x_sign, x_hi) of sun/station

* Y = NI% + 3 copies (y_sign, y_hi) of sun/station

* Y = NI% + 6 copies (z_sign, z_hi) of sun/station

Returns:

X                    X is incremented by 3 to point to the next coordinate

Y                    Y is incremented by 3 to point to the next coordinate

.SPS3

LDA K%+1,Y             \ Copy x_hi into K3+X
STA K3,X

LDA K%+2,Y             \ Set A = x_sign and store it on the stack
PHA

AND #%01111111         \ Set K3+1 = |x_sign|
STA K3+1,X

PLA                    \ Set K3+2 = the sign of x_sign
AND #%10000000
STA K3+2,X

INY                    \ Increment the value of Y by 3 so the next call to SPS3
INY                    \ will copy the next coordinate (i.e. x then y then z)
INY

INX                    \ Increment the value of X by 3 so the next call to SPS3
INX                    \ will store the coordinate in the next 24-bit K3 number
INX

RTS                    \ Return from the subroutine

Type: Variable
Category: Universe
Summary: Table of pointers to the local universe's ship data blocks
Deep dive: The local bubble of universe
Context: See this variable on its own page
References: This variable is used as follows:
* GINF calls UNIV
* KILLSHP calls UNIV
* KS2 calls UNIV
* TACTICS (Part 1 of 7) calls UNIV

See the deep dive on "Ship data blocks" for details on ship data blocks, and
the deep dive on "The local bubble of universe" for details of how Elite
stores the local universe in K%, FRIN and UNIV.

.UNIV

FOR I%, 0, NOSH

EQUW K% + I% * NI%     \ Address of block no. I%, of size NI%, in workspace K%

NEXT

Type: Subroutine
Category: Universe
Summary: Fetch the address of a ship's data block into INF
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* FRMIS calls GINF
* Main flight loop (Part 4 of 16) calls GINF
* NWSHP calls GINF
* WPSHPS calls GINF

Get the address of the data block for ship slot X and store it in INF. This
address is fetched from the UNIV table, which stores the addresses of the 13
ship data blocks in workspace K%.

Arguments:

X                    The ship slot number for which we want the data block

.GINF

TXA                    \ Set Y = X * 2
ASL A
TAY

LDA UNIV,Y             \ Get the high byte of the address of the X-th ship
STA INF                \ from UNIV and store it in INF

LDA UNIV+1,Y           \ Get the low byte of the address of the X-th ship
STA INF+1              \ from UNIV and store it in INF

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Universe
Summary: Add a new space station to our local bubble of universe
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 14 of 16) calls NWSPS
* TT110 calls NWSPS

.NWSPS

JSR SPBLB              \ Light up the space station bulb on the dashboard

LDX #%10000001         \ Set the AI flag in byte #32 to %10000001 (hostile,
STX INWK+32            \ no AI, has an E.C.M.)

LDX #255               \ Set roll counter to 255 (maximum roll with no
STX INWK+29            \ damping)

INX                    \ Set pitch counter to 0 (no pitch, roll only)
STX INWK+30

STX FRIN+1             \ Set the sun/space station slot at FRIN+1 to 0, to
\ indicate we should show the space station rather than
\ the sun

STX INWK+33            \ As part of the setup, we want to point INWK(34 33) to
\ LSO, the line heap for the space station. LSO is at
\ &0E00, so this sets the low byte at byte #33 to 0 (we
\ set the high byte below)

LDA FIST               \ If bit 7 of FIST is clear, i.e. FIST < 128, then jump
BPL n_enemy            \ to n_enemy with X = 0 to skip the following
\ instruction and set the NEWB flags to 0 (so the
\ station is not hostile)

LDX #%00000100         \ Bit 7 of FIST is set, i.e. FIST >= 128 (so our
\ "fugitive/innocent status" is very bad!), so set bit
\ #3 of X so we the following sets the NEWB flags to
\ make the station hostile

.n_enemy

STX NEWB               \ Set the station's NEWB flag with the value in X, so it
\ be hostile if FIST > 127, or friendly otherwise

LDX #10                \ Call NwS1 to flip the sign of nosev_x_hi (byte #10)
JSR NwS1

JSR NwS1               \ And again to flip the sign of nosev_y_hi (byte #12)

\ NwS1 increments X by 2 for each call, so at this point
\ the value of X is 10 + 2 + 2 = 14 = &E, which we can
\ use to set the correct INWK+34 value in the following

STX INWK+34            \ As part of the setup, we want to point INWK(34 33) to
\ LSO, the line heap for the space station. LSO is at
\ &0E00, so this sets the high byte at byte #34 to &0E
\ (we already set the low byte above)

JSR NwS1               \ And again to flip the sign of nosev_z_hi (byte #14)

LDA #SST               \ Set A to the space station type, and fall through
\ into NWSHP to finish adding the space station to the
\ universe

Type: Subroutine
Category: Universe
Summary: Add a new ship to our local bubble of universe
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* BRIEF calls NWSHP
* FRS1 calls NWSHP
* GTHG calls NWSHP
* KS4 calls NWSHP
* Main game loop for flight (Part 4 of 6) calls NWSHP
* SFS1 calls NWSHP
* SOLAR calls NWSHP
* SOS1 calls NWSHP
* TITLE calls NWSHP
* ships_ag calls NWSHP

This creates a new block of ship data in the K% workspace, allocates a new
block in the ship line heap at WP, adds the new ship's type into the first
empty slot in FRIN, and adds a pointer to the ship data into UNIV. If there
isn't enough free memory for the new ship, it isn't added.

Arguments:

A                    The type of the ship to add (see variable XX21 for a
list of ship types)

Returns:

C flag               Set if the ship was successfully added, clear if it
wasn't (as there wasn't enough free memory)

INF                  Points to the new ship's data block in K%

.NWSHP

STA T                  \ Store the ship type in location T

LDX #0                 \ Before we can add a new ship, we need to check
\ whether we have an empty slot we can put it in. To do
\ this, we need to loop through all the slots to look
\ for an empty one, so set a counter in X that starts
\ from the first slot at 0. When ships are killed, then
\ the slots are shuffled down by the KILLSHP routine, so
\ the first empty slot will always come after the last
\ filled slot. This allows us to tack the new ship's
\ data block and ship line heap onto the end of the
\ existing ship data and heap, as shown in the memory
\ map below

.NWL1

LDA FRIN,X             \ Load the ship type for the X-th slot

BEQ NW1                \ If it is zero, then this slot is empty and we can use
\ it for our new ship, so jump down to NW1

INX                    \ Otherwise increment X to point to the next slot

CPX #NOSH              \ If we haven't reached the last slot yet, loop back up
BCC NWL1               \ to NWL1 to check the next slot (note that this means
\ only slots from 0 to #NOSH - 1 are populated by this
\ routine, but there is one more slot reserved in FRIN,
\ which is used to identify the end of the slot list
\ when shuffling the slots down in the KILLSHP routine)

.NW3

CLC                    \ Otherwise we don't have an empty slot, so we can't
RTS                    \ add a new ship, so clear the C flag to indicate that
\ we have not managed to create the new ship, and return
\ from the subroutine

.NW1

\ If we get here, then we have found an empty slot at
\ index X, so we can go ahead and create our new ship.
\ We do that by creating a ship data block at INWK and,
\ when we are done, copying the block from INWK into
\ the K% workspace (specifically, to INF)

JSR GINF               \ Get the address of the data block for ship slot X
\ (which is in workspace K%) and store it in INF

LDA T                  \ If the type of ship that we want to create is
BMI NW2                \ negative, then this indicates a planet or sun, so
\ jump down to NW2, as the next section sets up a ship
\ data block, which doesn't apply to planets and suns,
\ as they don't have things like shields, missiles,
\ vertices and edges

\ This is a ship, so first we need to set up various
\ pointers to the ship blueprint we will need. The
\ blueprints for each ship type in Elite are stored
\ in a table at location XX21, so refer to the comments
\ on that variable for more details on the data we're
\ about to access

ASL A                  \ Set Y = ship type * 2
TAY

LDA XX21-1,Y           \ The ship blueprints at XX21 start with a lookup
\ table that points to the individual ship blueprints,
\ so this fetches the high byte of this particular ship
\ type's blueprint

BEQ NW3                \ If the high byte is 0 then this is not a valid ship
\ type, so jump to NW3 to clear the C flag and return
\ from the subroutine

STA XX0+1              \ This is a valid ship type, so store the high byte in
\ XX0+1

LDA XX21-2,Y           \ Fetch the low byte of this particular ship type's
STA XX0                \ blueprint and store it in XX0, so XX0(1 0) now
\ contains the address of this ship's blueprint

CPY #2*SST             \ If the ship type is a space station (SST), then jump
BEQ NW6                \ to NW6, skipping the heap space steps below, as the
\ space station has its own line heap at LSO (which it
\ shares with the sun)

\ We now want to allocate space for a heap that we can
\ use to store the lines we draw for our new ship (so it
\ can easily be erased from the screen again). SLSP
\ points to the start of the current heap space, and we
\ can extend it downwards with the heap for our new ship
\ (as the heap space always ends just before the WP
\ workspace)

LDY #5                 \ Fetch ship blueprint byte #5, which contains the
LDA (XX0),Y            \ maximum heap size required for plotting the new ship,
STA T1                 \ and store it in T1

LDA SLSP               \ Take the 16-bit address in SLSP and subtract T1,
SEC                    \ storing the 16-bit result in INWK(34 33), so this now
SBC T1                 \ points to the start of the line heap for our new ship
STA INWK+33
LDA SLSP+1
SBC #0
STA INWK+34

\ We now need to check that there is enough free space
\ for both this new line heap and the new data block
\ for our ship. In memory, this is the layout of the
\ ship data blocks and ship line heaps:
\
\   +-----------------------------------+   &0F34
\   |                                   |
\   | WP workspace                      |
\   |                                   |
\   +-----------------------------------+   &0D40 = WP
\   |                                   |
\   | Current ship line heap            |
\   |                                   |
\   +-----------------------------------+   SLSP
\   |                                   |
\   | Proposed heap for new ship        |
\   |                                   |
\   +-----------------------------------+   INWK(34 33)
\   |                                   |
\   .                                   .
\   .                                   .
\   .                                   .
\   .                                   .
\   .                                   .
\   |                                   |
\   +-----------------------------------+   INF + NI%
\   |                                   |
\   | Proposed data block for new ship  |
\   |                                   |
\   +-----------------------------------+   INF
\   |                                   |
\   | Existing ship data blocks         |
\   |                                   |
\   +-----------------------------------+   &0900 = K%
\
\ So, to work out if we have enough space, we have to
\ make sure there is room between the end of our new
\ ship data block at INF + NI%, and the start of the
\ proposed heap for our new ship at the address we
\ stored in INWK(34 33). Or, to put it another way, we
\ and to make sure that:
\
\   INWK(34 33) > INF + NI%
\
\ which is the same as saying:
\
\   INWK+33 - INF > NI%
\
\ because INWK is in zero page, so INWK+34 = 0

LDA INWK+33            \ Calculate INWK+33 - INF, again using 16-bit
SBC INF                \ arithmetic, and put the result in (A Y), so the high
TAY                    \ byte is in A and the low byte in Y. The subtraction
LDA INWK+34            \ works because the previous subtraction will never
SBC INF+1              \ underflow, so we know the C flag is set

BCC NW3+1              \ If we have an underflow from the subtraction, then
\ INF > INWK+33 and we definitely don't have enough
\ room for this ship, so jump to NW3+1, which returns
\ from the subroutine (with the C flag already cleared)

BNE NW4                \ If the subtraction of the high bytes in A is not
\ zero, and we don't have underflow, then we definitely
\ have enough space, so jump to NW4 to continue setting
\ up the new ship

CPY #NI%               \ Otherwise the high bytes are the same in our
BCC NW3+1              \ subtraction, so now we compare the low byte of the
\ result (which is in Y) with NI%. This is the same as
\ doing INWK+33 - INF > NI% (see above). If this isn't
\ true, the C flag will be clear and we don't have
\ enough space, so we jump to NW3+1, which returns
\ from the subroutine (with the C flag already cleared)

.NW4

LDA INWK+33            \ If we get here then we do have enough space for our
STA SLSP               \ new ship, so store the new bottom of the ship line
LDA INWK+34            \ heap (i.e. INWK+33) in SLSP, doing both the high and
STA SLSP+1             \ low bytes

.NW6

LDY #14                \ Fetch ship blueprint byte #14, which contains the
LDA (XX0),Y            \ ship's energy, and store it in byte #35
STA INWK+35

LDY #19                \ Fetch ship blueprint byte #19, which contains the
LDA (XX0),Y            \ number of missiles and laser power, and AND with %111
AND #%00000111         \ to extract the number of missiles before storing in
STA INWK+31            \ byte #31

LDA T                  \ Restore the ship type we stored above

.NW2

STA FRIN,X             \ Store the ship type in the X-th byte of FRIN, so the
\ this slot is now shown as occupied in the index table

TAX                    \ Copy the ship type into X

BMI NW8                \ If the ship type is negative (planet or sun), then
\ jump to NW8 to skip the following instructions

CPX #JL                \ If JL <= X < JH, i.e. the type of ship we killed in X
BCC NW7                \ is junk (escape pod, alloy plate, cargo canister,
CPX #JH                \ asteroid, splinter, Shuttle or Transporter), then keep
BCS NW7                \ going, otherwise jump to NW7

.gangbang

INC JUNK               \ We're adding junk, so increase the junk counter

.NW7

INC MANY,X             \ Increment the total number of ships of type X

.NW8

LDY T                  \ Restore the ship type we stored above

LDA E%-1,Y             \ Fetch the E% byte for this ship to get the default
\ settings for the ship's NEWB flags

AND #%01101111         \ Zero bits 4 and 7 (so the new ship is not docking, has
\ has not been scooped, and has not just docked)

ORA NEWB               \ Apply the result to the ship's NEWB flags, which sets
STA NEWB               \ bits 0-3 and 5-6 in NEWB if they are set in the E%
\ byte

LDY #(NI%-1)           \ The final step is to copy the new ship's data block
\ from INWK to INF, so set up a counter for NI% bytes
\ in Y

.NWL3

LDA INWK,Y             \ Load the Y-th byte of INWK and store in the Y-th byte
STA (INF),Y            \ of the workspace pointed to by INF

DEY                    \ Decrement the loop counter

BPL NWL3               \ Loop back for the next byte until we have copied them
\ all over

SEC                    \ We have successfully created our new ship, so set the
\ C flag to indicate success

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Universe
Summary: Flip the sign and double an INWK byte
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* NWSPS calls NwS1

Flip the sign of the INWK byte at offset X, and increment X by 2. This is
used by the space station creation routine at NWSPS.

Arguments:

X                    The offset of the INWK byte to be flipped

Returns:

X                    X is incremented by 2

.NwS1

LDA INWK,X             \ Load the X-th byte of INWK into A and flip bit 7,
EOR #%10000000         \ storing the result back in the X-th byte of INWK
STA INWK,X

INX                    \ Add 2 to X
INX

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Dashboard
Summary: Disarm missiles and update the dashboard indicators
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* KILLSHP calls ABORT
* Main flight loop (Part 3 of 16) calls ABORT

Arguments:

Y                    The new status of the leftmost missile indicator

.ABORT

LDX #&FF               \ Set X to &FF, which is the value of MSTG when we have
\ no target lock for our missile

\ Fall through into ABORT2 to set the missile lock to
\ the value in X, which effectively disarms the missile

Type: Subroutine
Category: Dashboard
Summary: Set/unset the lock target for a missile and update the dashboard
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 11 of 16) calls ABORT2

Set the lock target for the leftmost missile and update the dashboard.

Arguments:

X                    The slot number of the ship to lock our missile onto, or
&FF to remove missile lock

Y                    The new colour of the missile indicator:

* &00 = black (no missile)

* &0E = red (armed and locked)

* &E0 = yellow/white (armed)

* &EE = green/cyan (disarmed)

.ABORT2

STX MSTG               \ Store the target of our missile lock in MSTG

LDX NOMSL              \ Call MSBAR (via MSBARS) to update the leftmost
DEX                    \ indicator in the dashboard's missile bar, by calling
JSR MSBARS             \ with X = NOMSL - 1 (as the missile indicators are
\ numbered 0-3 in Elite-A rather than the 1-4 in the
\ disc version)
\
\ MSBARS returns with Y = 0

STY MSAR               \ Set MSAR = 0 to indicate that the leftmost missile
\ is no longer seeking a target lock

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Dashboard
Summary: Start up the E.C.M. (indicator, start countdown and make sound)
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 3 of 16) calls ECBLB2
* TACTICS (Part 1 of 7) calls ECBLB2

Light up the E.C.M. indicator bulb on the dashboard, set the E.C.M. countdown
timer to 32, and start making the E.C.M. sound.

.ECBLB2

LDA #32                \ Set the E.C.M. countdown timer in ECMA to 32
STA ECMA

ASL A                  \ Call the NOISE routine with A = 64 to make the sound
JSR NOISE              \ of the E.C.M. being switched on

\ Fall through into ECBLB to light up the E.C.M. bulb

Type: Subroutine
Category: Dashboard
Summary: Light up the E.C.M. indicator bulb ("E") on the dashboard by
sending a draw_E command to the I/O processor
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* ECMOF calls ECBLB

.ECBLB

LDA #&93               \ Send command &93 to the I/O processor:
JMP tube_write         \
\   draw_E()
\
\ which will toggle the E.C.M. indicator bulb ("E") on
\ the dashboard and return from the subroutine using a
\ tail call

Type: Subroutine
Category: Dashboard
Summary: Light up the space station indicator ("S") on the dashboard by
sending a draw_S command to the I/O processor
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* KS4 calls SPBLB
* NWSPS calls SPBLB
* RES2 calls SPBLB

.SPBLB

LDA #&92               \ Send command &92 to the I/O processor:
JMP tube_write         \
\   draw_S()
\
\ which will toggle the space station indicator ("S") on
\ the dashboard and return from the subroutine using a
\ tail call

Type: Subroutine
Category: Dashboard
Summary: Draw a specific indicator in the dashboard's missile bar
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* ABORT2 calls MSBARS
* Main flight loop (Part 3 of 16) calls MSBARS

This routine wraps the standard MSBAR routine and ensures that X is never
greater than 3. This enables ships to support large numbers of missiles, while
still only having four indicators on the dashboard.

Arguments:

X                    The number of the missile indicator to update (counting
from right to left and starting at 0 rather than 1, so
indicator NOMSL - 1 is the leftmost indicator)

Y                    The colour of the missile indicator:

* &00 = black (no missile)

* &0E = red (armed and locked)

* &E0 = yellow/white (armed)

* &EE = green/cyan (disarmed)

Returns:

X                    X is preserved

Y                    Y is set to 0

.MSBARS

CPX #4                 \ If X < 4 then jump to n_mok to skip the following
BCC n_mok              \ instruction

LDX #3                 \ Set X = 3 so X is never bigger than 3

.n_mok

JMP MSBAR              \ Jump to MSBAR to draw the missile indicator

Type: Subroutine
Category: Drawing ships
Summary: Project the current ship onto the screen
Deep dive: Extended screen coordinates
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* PLANET calls PROJ
* SHPPT calls PROJ

Project the current ship's location onto the screen, either returning the
screen coordinates of the projection (if it's on-screen), or returning an
error via the C flag.

In this context, "on-screen" means that the point is projected into the
following range:

centre of screen - 1024 < x < centre of screen + 1024
centre of screen - 1024 < y < centre of screen + 1024

The projection calculation is:

K3(1 0) = #X + x / z
K4(1 0) = #Y + y / z

where #X and #Y are the pixel x-coordinate and y-coordinate of the centre of
the screen.

Arguments:

INWK                 The ship data block for the ship to project on-screen

Returns:

K3(1 0)              The x-coordinate of the ship's projection on-screen

K4(1 0)              The y-coordinate of the ship's projection on-screen

C flag               Set if the ship's projection doesn't fit on the screen,
clear if it does project onto the screen

A                    Contains K4+1, the high byte of the y-coordinate

.PROJ

LDA INWK               \ Set P(1 0) = (x_hi x_lo)
STA P                  \            = x
LDA INWK+1
STA P+1

LDA INWK+2             \ Set A = x_sign

JSR PLS6               \ Call PLS6 to calculate:
\
\   (X K) = (A P) / (z_sign z_hi z_lo)
\         = (x_sign x_hi x_lo) / (z_sign z_hi z_lo)
\         = x / z

BCS PL2-1              \ If the C flag is set then the result overflowed and
\ the coordinate doesn't fit on the screen, so return
\ from the subroutine with the C flag set (as PL2-1
\ contains an RTS)

LDA K                  \ Set K3(1 0) = (X K) + #X
ADC #X                 \             = #X + x / z
STA K3                 \
\ first doing the low bytes

TXA                    \ And then the high bytes. #X is the x-coordinate of
ADC #0                 \ the centre of the space view, so this converts the
STA K3+1               \ space x-coordinate into a screen x-coordinate

LDA INWK+3             \ Set P(1 0) = (y_hi y_lo)
STA P
LDA INWK+4
STA P+1

LDA INWK+5             \ Set A = -y_sign
EOR #%10000000

JSR PLS6               \ Call PLS6 to calculate:
\
\   (X K) = (A P) / (z_sign z_hi z_lo)
\         = -(y_sign y_hi y_lo) / (z_sign z_hi z_lo)
\         = -y / z

BCS PL2-1              \ If the C flag is set then the result overflowed and
\ the coordinate doesn't fit on the screen, so return
\ from the subroutine with the C flag set (as PL2-1
\ contains an RTS)

LDA K                  \ Set K4(1 0) = (X K) + #Y
ADC #Y                 \             = #Y - y / z
STA K4                 \
\ first doing the low bytes

TXA                    \ And then the high bytes. #Y is the y-coordinate of
ADC #0                 \ the centre of the space view, so this converts the
STA K4+1               \ space x-coordinate into a screen y-coordinate

CLC                    \ Clear the C flag to indicate success

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Drawing planets
Summary: Remove the planet or sun from the screen
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* PLANET calls PL2
* PROJ calls entry point PL2-1

Other entry points:

PL2-1                Contains an RTS

.PL2

LDA TYPE               \ Shift bit 0 of the planet/sun's type into the C flag
LSR A

BCS P%+5               \ If the planet/sun's type has bit 0 clear, then it's
\ either 128 or 130, which is a planet; meanwhile, the
\ sun has type 129, which has bit 0 set. So if this is
\ the sun, skip the following instruction

JMP WPLS2              \ This is the planet, so jump to WPLS2 to remove it from
\ screen, returning from the subroutine using a tail
\ call

JMP WPLS               \ This is the sun, so jump to WPLS to remove it from
\ screen, returning from the subroutine using a tail
\ call

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_FLIGHT calls PLANET

Arguments:

INWK                 The planet or sun's ship data block

.PLANET

LDA INWK+8             \ Set A = z_sign (the highest byte in the planet/sun's
\ coordinates)

BMI PL2                \ If A is negative then the planet/sun is behind us, so
\ jump to PL2 to remove it from the screen, returning
\ from the subroutine using a tail call

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 z_sign OR z_hi to get the maximum of the two

BEQ PL2                \ If the maximum is 0, then the planet/sun is too close
\ to be shown, so jump to PL2 to remove it from the
\ screen, returning from the subroutine using a tail
\ call

JSR PROJ               \ Project the planet/sun onto the screen, returning the
\ centre's coordinates in K3(1 0) and K4(1 0)

BCS PL2                \ If the C flag is set by PROJ then the planet/sun is
\ not visible on-screen, so jump to PL2 to remove it
\ from the screen, returning from the subroutine using
\ a tail call

LDA #96                \ Set (A P+1 P) = (0 96 0) = 24576
STA P+1                \
LDA #0                 \ This represents the planet/sun's radius at a distance
STA P                  \ of z = 1

JSR DVID3B2            \ Call DVID3B2 to calculate:
\
\   K(3 2 1 0) = (A P+1 P) / (z_sign z_hi z_lo)
\              = (0 96 0) / z
\              = 24576 / z
\
\ so K now contains the planet/sun's radius, reduced by
\ the actual distance to the planet/sun. We know that
\ K+3 and K+2 will be 0, as the number we are dividing,
\ (0 96 0), fits into the two bottom bytes, so the
\ result is actually in K(1 0)

LDA K+1                \ If the high byte of the reduced radius is zero, jump
BEQ PL82               \ to PL82, as K contains the radius on its own

LDA #248               \ Otherwise set K = 248, to use as our one-byte radius
STA K

.PL82

LDA TYPE               \ If the planet/sun's type has bit 0 clear, then it's
LSR A                  \ either 128 or 130, which is a planet (the sun has type
BCC PL9                \ 129, which has bit 0 set). So jump to PL9 to draw the
\ planet with radius K, returning from the subroutine
\ using a tail call

JMP SUN                \ Otherwise jump to SUN to draw the sun with radius K,
\ returning from the subroutine using a tail call

Name: PL9 (Part 1 of 3)                                       [Show more]
Type: Subroutine
Category: Drawing planets
Summary: Draw the planet, with either an equator and meridian, or a crater
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* PLANET calls PL9

Draw the planet with radius K at pixel coordinate (K3, K4), and with either an
equator and meridian, or a crater.

Arguments:

K(1 0)               The planet's radius

K3(1 0)              Pixel x-coordinate of the centre of the planet

K4(1 0)              Pixel y-coordinate of the centre of the planet

INWK                 The planet's ship data block

.PL9

JSR WPLS2              \ Call WPLS2 to remove the planet from the screen

JSR CIRCLE             \ Call CIRCLE to draw the planet's new circle

BCS PL20               \ If the call to CIRCLE returned with the C flag set,
\ then the circle does not fit on-screen, so jump to
\ PL20 to return from the subroutine

LDA K+1                \ If K+1 is zero, jump to PL25 as K(1 0) < 256, so the
BEQ PL25               \ planet fits on the screen

.PL20

RTS                    \ The planet doesn't fit on-screen, so return from the
\ subroutine

.PL25

LDA TYPE               \ If the planet type is 128 then it has an equator and
CMP #128               \ a meridian, so this jumps to PL26 if this is not a
BNE PL26               \ planet with an equator - in other words, if it is a
\ planet with a crater

\ Otherwise this is a planet with an equator and
\ meridian, so fall through into the following to draw
\ them

Name: PL9 (Part 2 of 3)                                       [Show more]
Type: Subroutine
Category: Drawing planets
Summary: Draw the planet's equator and meridian
Deep dive: Drawing meridians and equators
Context: See this subroutine on its own page
References: No direct references to this subroutine in this source file

Draw the planet's equator and meridian.

Arguments:

K(1 0)               The planet's radius

K3(1 0)              Pixel x-coordinate of the centre of the planet

K4(1 0)              Pixel y-coordinate of the centre of the planet

INWK                 The planet's ship data block

LDA K                  \ If the planet's radius is less than 6, the planet is
CMP #6                 \ too small to show a crater, so jump to PL20 to return
BCC PL20               \ 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 give the result the opposite sign to nosev_z_hi

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 give the result the opposite sign to nosev_z_hi

LDX #21                \ Set X to 21 so the call to PLS5 divides sidev_x

JSR PLS5               \ Call PLS5 to calculate the following:
\
\   (XX16+2 K2+2) = sidev_x / z
\
\   (XX16+3 K2+3) = sidev_y / z

JMP PLS2               \ Jump to PLS2 to draw the second meridian, returning
\ from the subroutine using a tail call

Name: PL9 (Part 3 of 3)                                       [Show more]
Type: Subroutine
Category: Drawing planets
Summary: Draw the planet's crater
Deep dive: Drawing craters
Context: See this subroutine on its own page
References: No direct references to this subroutine in this source file

Draw the planet's crater.

Arguments:

K(1 0)               The planet's radius

K3(1 0)              Pixel x-coordinate of the centre of the planet

K4(1 0)              Pixel y-coordinate of the centre of the planet

INWK                 The planet's ship data block

.PL26

LDA INWK+20            \ Set A = roofv_z_hi

BMI PL20               \ If A is negative, the crater is on the far side of the
\ planet, so return from the subroutine (as PL2
\ contains an RTS)

LDX #15                \ Set X = 15, so the following call to PLS3 operates on
\ roofv

JSR PLS3               \ Call PLS3 to calculate:
\
\   (Y A P) = 222 * roofv_x / z
\
\ to give the x-coordinate of the crater offset and
\ increment X to point to roofv_y for the next call

CLC                    \ Calculate:
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_x / 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 circle in the call to
STA TGT                \ PLS22 below

LDA #0                 \ Set CNT2 = 0 as we are drawing a full circle, so we
STA CNT2               \ don't need to apply an offset

BEQ PLS22              \ Jump to PLS22 to draw the crater, returning from the
\ subroutine using a tail call (this BEQ is effectively
\ a JMP as A is always zero)

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-circle
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-circle, used for the planet's equator and meridian.

.PLS2

LDA #31                \ Set TGT = 31, so we only draw half a circle
STA TGT

\ Fall through into PLS22 to draw the half circle

Type: Subroutine
Category: Drawing planets
Summary: Draw a circle or half-circle
Deep dive: The sine, cosine and arctan tables
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* PL9 (Part 3 of 3) calls PLS22

Draw a circle or half-circle, used for the planet's equator and meridian, or
crater.

This routine is called from parts 2 and 3 of PL9, and does the following:

K6(1 0) = K3(1 0) + (XX16 K2) * cos(CNT2) + (XX16+2 K2+2) * sin(CNT2)

(T X) = - |XX16+1 K2+1| * cos(CNT2) - |XX16+3 K2+3| * sin(CNT2)

before calling BLINE to draw a circle segment to these coordinates.

Arguments:

K(1 0)               The planet's radius

INWK                 The planet's ship data block

TGT                  The number of segments to draw:

* 32 for a half circle (a meridian)

* 64 for a half circle (a crater)

CNT2                 The starting segment for drawing the half-circle

.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 A = CNT2 mod 32
AND #31
TAX

LDA SNE,X              \ Set Q = sin(CNT2)
STA Q

LDA K2+2               \ Set A = K2+2
\       = |roofv_x / z|

JSR FMLTU              \ Set R = A * Q / 256
STA R                  \       = |roofv_x / z| * sin(CNT2) / 256

LDA K2+3               \ Set A = K2+2
\       = |roofv_y / z|

JSR FMLTU              \ Set K = A * Q / 256
STA K                  \       = |roofv_y / z| * sin(CNT2) / 256

LDX CNT2               \ If CNT2 >= 33 then this sets the C flag, otherwise
CPX #33                \ it's clear

LDA #0                 \ Shift the C flag into the sign bit of XX16+5, so:
ROR A                  \
STA XX16+5             \   XX16+5 = +ve if CNT2 < 33
\            -ve if CNT2 >= 33

LDA CNT2               \ Set A = (CNT2 + 16) mod 32
CLC
AND #31
TAX

LDA SNE,X              \ Set Q = sin(CNT2 + 16)
STA Q                  \       = cos(CNT2)

LDA K2+1               \ Set A = K2+1
\       = |nosev_y / z|

JSR FMLTU              \ Set K+2 = A * Q / 256
STA K+2                \         = |nosev_y / z| * cos(CNT2) / 256

LDA K2                 \ Set A = K2
\       = |nosev_x / z|

JSR FMLTU              \ Set P = A * Q / 256
STA P                  \       = |nosev_x / z| * cos(CNT2) / 256

LDA CNT2               \ If (CNT2 + 15) mod 64 >= 33 then this sets the C flag,
ADC #15                \ otherwise it's clear
AND #63
CMP #33

LDA #0                 \ Shift the C flag into the sign bit of XX16+4, so:
ROR A                  \
STA XX16+4             \   XX16+4 = +ve if (CNT2 + 15) mod 64 < 33,
\            -ve if (CNT2 + 15) mod 64 >= 33

LDA XX16+5             \ Set S = the sign of (roofv_x / z * CNT2 < 33 sign)
EOR XX16+2
STA S

LDA XX16+4             \ Set A = the sign of (nosev_x / z * CNT2 + 15 < 33
EOR XX16               \ sign)

JSR ADD                \ Set (A X) = (A P) + (S R)
\           =   (nosev_x / z) * cos(CNT2)
\             + (roofv_x / z) * sin(CNT2)

STA T                  \ Store the high byte in T, so the result is now:
\
\   (T X) =  (nosev_x / z) * cos(CNT2)
\           + (roofv_x / z) * 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

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) + (nosev_x / z) * cos(CNT2)
\           + (roofv_x / z) * sin(CNT2)

LDA K                  \ Set R = K = |roofv_y / z| * sin(CNT2) / 256
STA R

LDA XX16+5             \ Set S = the sign of (roofv_y / z * CNT2 < 33 sign)
EOR XX16+3
STA S

LDA K+2                \ Set P = K+2 = |nosev_y / z| * cos(CNT2) / 256
STA P

LDA XX16+4             \ Set A = the sign of (nosev_y / z * CNT2 + 15 < 33
EOR XX16+1             \ sign)

JSR ADD                \ Set (A X) = (A P) + (S R)
\           =   |nosev_y / z| * cos(CNT2)
\             + |roofv_y / z| * sin(CNT2)

EOR #%10000000         \ Store the negated high byte in T, so the result is
STA T                  \ now:
\
\   (T X) = - |nosev_y / z| * cos(CNT2)
\           - |roofv_y / z| * sin(CNT2)

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

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               \ circle (which is how we draw half-circles)
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 circles
Summary: Draw a circle for the planet
Deep dive: Drawing circles
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* PL9 (Part 1 of 3) calls CIRCLE

Draw a circle with the centre at (K3, K4) and radius K. Used to draw the
planet's main outline.

Arguments:

K                    The planet's radius

K3(1 0)              Pixel x-coordinate of the centre of the planet

K4(1 0)              Pixel y-coordinate of the centre of the planet

.CIRCLE

JSR CHKON              \ Call CHKON to check whether the circle fits on-screen

BCS PL40               \ If CHKON set the C flag then the circle does not fit
\ on-screen, so return from the subroutine (as PL40
\ contains an RTS)

LDA #0                 \ Set LSX2 = 0
STA LSX2

LDX K                  \ Set X = K = radius

LDA #8                 \ Set A = 8

CPX #8                 \ If the radius < 8, skip to PL89
BCC PL89

LSR A                  \ Halve A so A = 4

CPX #60                \ If the radius < 60, skip to PL89
BCC PL89

LSR A                  \ Halve A so A = 2

.PL89

STA STP                \ Set STP = A. STP is the step size for the circle, so
\ the above sets a smaller step size for bigger circles

JMP CIRCLE2            \ Jump to CIRCLE2 to draw the circle with the correct
\ step size and return from the subroutine using a tail
\ call

Type: Subroutine
Category: Drawing planets
Summary: Remove the planet from the screen
Deep dive: The ball line heap
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* PL2 calls WPLS2
* PL9 (Part 1 of 3) calls WPLS2

We do this by redrawing it using the lines stored in the ball line heap when
the planet was originally drawn by the BLINE routine.

.WPLS2

LDY LSX2               \ If LSX2 is non-zero (which indicates the ball line
BNE WP1                \ heap is empty), jump to WP1 to reset the line heap
\ without redrawing the planet

\ Otherwise Y is now 0, so we can use it as a counter to
\ loop through the lines in the line heap, redrawing
\ each one to remove the planet from the screen, before
\ resetting the line heap once we are done

.WPL1

CPY LSP                \ If Y >= LSP then we have reached the end of the line
BCS WP1                \ heap and have finished redrawing the planet (as LSP
\ points to the end of the heap), so jump to WP1 to
\ reset the line heap, returning from the subroutine
\ using a tail call

LDA LSY2,Y             \ Set A to the y-coordinate of the current heap entry

CMP #&FF               \ If the y-coordinate is &FF, this indicates that the
BEQ WP2                \ next point in the heap denotes the start of a line
\ segment, so jump to WP2 to put it into (X1, Y1)

STA Y2                 \ Set (X2, Y2) to the x- and y-coordinates from the
LDA LSX2,Y             \ heap
STA X2

JSR LOIN               \ Draw a line from (X1, Y1) to (X2, Y2)

INY                    \ Increment the loop counter to point to the next point

LDA X2                 \ Swap (X1, Y1) and (X2, Y2), so the next segment will
STA X1                 \ be drawn from the current (X2, Y2) to the next point
LDA Y2                 \ in the heap
STA Y1

JMP WPL1               \ Loop back to WPL1 for the next point in the heap

.WP2

INY                    \ Increment the loop counter to point to the next point

LDA LSX2,Y             \ Set (X1, Y1) to the x- and y-coordinates from the
STA X1                 \ heap
LDA LSY2,Y
STA Y1

INY                    \ Increment the loop counter to point to the next point

JMP WPL1               \ Loop back to WPL1 for the next point in the heap

Type: Subroutine
Category: Drawing planets
Summary: Reset the ball line heap
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* WPLS2 calls WP1

.WP1

LDA #1                 \ Set LSP = 1 to reset the ball line heap pointer
STA LSP

LDA #&FF               \ Set LSX2 = &FF to indicate the ball line heap is empty
STA LSX2

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Drawing suns
Summary: Remove the sun from the screen
Deep dive: Drawing the sun
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 14 of 16) calls WPLS
* PL2 calls WPLS
* SUN (Part 1 of 4) calls WPLS

We do this by redrawing it using the lines stored in the sun line heap when
the sun was originally drawn by the SUN routine.

Arguments:

SUNX(1 0)            The x-coordinate of the vertical centre axis of the sun

Other entry points:

WPLS-1               Contains an RTS

.WPLS

LDA LSX                \ If LSX < 0, the sun line heap is empty, so return from
BMI WPLS-1             \ the subroutine (as WPLS-1 contains an RTS)

LDA SUNX               \ Set YY(1 0) = SUNX(1 0), the x-coordinate of the
STA YY                 \ vertical centre axis of the sun that's currently on
LDA SUNX+1             \ screen
STA YY+1

LDY #2*Y-1             \ #Y is the y-coordinate of the centre of the space
\ view, so this sets Y as a counter for the number of
\ lines in the space view (i.e. 191), which is also the
\ number of lines in the LSO block

.WPL2

LDA LSO,Y              \ Fetch the Y-th point from the sun line heap, which
\ gives us the half-width of the sun's line on this line
\ of the screen

BEQ P%+5               \ If A = 0, skip the following call to HLOIN2 as there
\ is no sun line on this line of the screen

JSR HLOIN2             \ Call HLOIN2 to draw a horizontal line on pixel line Y,
\ with centre point YY(1 0) and half-width A, and remove
\ the line from the sun line heap once done

DEY                    \ Decrement the loop counter

BNE WPL2               \ Loop back for the next line in the line heap until
\ we have gone through the entire heap

DEY                    \ This sets Y to &FF, as we end the loop with Y = 0

STY LSX                \ Set LSX to &FF to indicate the sun line heap is empty

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Drawing planets
Summary: Calculate (Y A P) = 222 * roofv_x / z
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* PL9 (Part 3 of 3) calls PLS3

Calculate the following, with X determining the vector to use:

(Y A P) = 222 * roofv_x / z

though in reality only (Y A) is used.

Although the code below supports a range of values of X, in practice the
routine is only called with X = 15, and then again after X has been
incremented to 17. So the values calculated by PLS1 use roofv_x first, then
roofv_y. The comments below refer to roofv_x, for the first call.

Arguments:

X                    Determines which of the INWK orientation vectors to
divide:

* X = 15: divides roofv_x

* X = 17: divides roofv_y

Returns:

X                    X gets incremented by 2 so it points to the next
coordinate in this orientation vector (so consecutive
calls to the routine will start with x, then move onto y
and then z)

.PLS3

JSR PLS1               \ Call PLS1 to calculate the following:
STA P                  \
\   P = |roofv_x / z|
\   K+3 = sign of roofv_x / z
\
\ and increment X to point to roofv_y for the next call

LDA #222               \ Set Q = 222, the offset to the crater
STA Q

STX U                  \ Store the vector index X in U for retrieval after the
\ call to MULTU

JSR MULTU              \ Call MULTU to calculate
\
\   (A P) = P * Q
\         = 222 * |roofv_x / z|

LDX U                  \ Restore the vector index from U into X

LDY K+3                \ If the sign of the result in K+3 is positive, skip to
BPL PL12               \ PL12 to return with Y = 0

EOR #&FF               \ Otherwise the result should be negative, so negate the
CLC                    \ high byte of the result using two's complement with
ADC #1                 \ A = ~A + 1

BEQ PL12               \ If A = 0, jump to PL12 to return with (Y A) = 0

LDY #&FF               \ Set Y = &FF to be a negative high byte

RTS                    \ Return from the subroutine

.PL12

LDY #0                 \ Set Y = 0 to be a positive high byte

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Drawing planets
Summary: Calculate CNT2 = arctan(P / A) / 4
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* PL9 (Part 2 of 3) calls PLS4

Calculate the following:

CNT2 = arctan(P / A) / 4

giving the result the opposite sign to nosev_z_hi. This is called with the
following arguments when calculating the equator and meridian for planets:

* A = roofv_z_hi, P = -nosev_z_hi

* A = sidev_z_hi, P = -nosev_z_hi

So it calculates the angle between the planet's orientation vectors, in the
z-axis.

.PLS4

STA Q                  \ Set Q = A

JSR ARCTAN             \ Call ARCTAN to calculate:
\
\   A = arctan(P / Q)
\       arctan(P / A)

LDX INWK+14            \ If nosev_z_hi is negative, skip the following
BMI P%+4               \ instruction

EOR #%10000000         \ nosev_z_hi is positive, so make the arctan negative

LSR A                  \ Set CNT2 = A / 4
LSR A
STA CNT2

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Drawing planets
Summary: Calculate roofv_x / z and roofv_y / z
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* PL9 (Part 2 of 3) calls PLS5

Calculate the following divisions of a specified value from one of the
orientation vectors (in this example, roofv):

(XX16+2 K2+2) = roofv_x / z

(XX16+3 K2+3) = roofv_y / z

Arguments:

X                    Determines which of the INWK orientation vectors to
divide:

* X = 15: divides roofv_x and roofv_y

* X = 21: divides sidev_x and sidev_y

INWK                 The planet's ship data block

.PLS5

JSR PLS1               \ Call PLS1 to calculate the following:
STA K2+2               \
STY XX16+2             \   K+2    = |roofv_x / z|
\   XX16+2 = sign of roofv_x / z
\
\ i.e. (XX16+2 K2+2) = roofv_x / z
\
\ and increment X to point to roofv_y for the next call

JSR PLS1               \ Call PLS1 to calculate the following:
STA K2+3               \
STY XX16+3             \   K+3    = |roofv_y / z|
\   XX16+3 = sign of roofv_y / z
\
\ i.e. (XX16+3 K2+3) = roofv_y / z
\
\ and increment X to point to roofv_z for the next call

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Drawing planets
Summary: Calculate (X K) = (A P) / (z_sign z_hi z_lo)
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* PROJ calls PLS6

Calculate the following:

(X K) = (A P) / (z_sign z_hi z_lo)

returning an overflow in the C flag if the result is >= 1024.

Arguments:

INWK                 The planet or sun's ship data block

Returns:

C flag               Set if the result >= 1024, clear otherwise

.PLS6

JSR DVID3B2            \ Call DVID3B2 to calculate:
\
\   K(3 2 1 0) = (A P+1 P) / (z_sign z_hi z_lo)

LDA K+3                \ Set A = |K+3| OR K+2
AND #%01111111
ORA K+2

BNE PL21S              \ If A is non-zero then the two high bytes of K(3 2 1 0)
\ are non-zero, so jump to PL21S to set the C flag and
\ return from the subroutine

\ We can now just consider K(1 0), as we know the top
\ two bytes of K(3 2 1 0) are both 0

LDX K+1                \ Set X = K+1, so now (X K) contains the result in
\ K(1 0), which is the format we want to return the
\ result in

CPX #4                 \ If the high byte of K(1 0) >= 4 then the result is
BCS PL6                \ >= 1024, so return from the subroutine with the C flag
\ set to indicate an overflow (as PL6 contains an RTS)

LDA K+3                \ Fetch the sign of the result from K+3 (which we know
\ has zeroes in bits 0-6, so this just fetches the sign)

BPL PL6                \ If the sign bit is clear and the result is positive,
\ then the result is already correct, so return from
\ the subroutine with the C flag clear to indicate
\ success (as PL6 contains an RTS)

LDA K                  \ Otherwise we need to negate the result, which we do
EOR #%11111111         \ using two's complement, starting with the low byte:
STA K                  \   K = ~K + 1

TXA                    \ And then the high byte:
EOR #%11111111         \
ADC #0                 \   X = ~X
TAX

CLC                    \ Clear the C flag to indicate success

.PL6

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Drawing planets
Summary: Return from a planet/sun-drawing routine with a failure flag
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* PLS6 calls PL21S

This routine is a duplicate of PL21 that is close enough to the PLS6 routine
for it to be called by a branch instruction.

Set the C flag and return from the subroutine. This is used to return from a
planet- or sun-drawing routine with the C flag indicating an overflow in the
calculation.

.PL21S

SEC                    \ Set the C flag to indicate an overflow

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Universe
Summary: Set the SLSP ship heap pointer after shuffling ship slots
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* KS2 calls KS3

The final part of the KILLSHP routine, called after we have shuffled the ship
slots and sorted out our missiles. This simply sets SLSP to the new bottom of
the ship heap space.

Arguments:

P(1 0)               Points to the ship line heap of the ship in the last
occupied slot (i.e. it points to the bottom of the
descending heap)

.KS3

LDA P                  \ After shuffling the ship slots, P(1 0) will point to
STA SLSP               \ the new bottom of the ship heap, so store this in
LDA P+1                \ SLSP(1 0), which stores the bottom of the heap
STA SLSP+1

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Universe
Summary: Remove the current ship from our local bubble of universe
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 12 of 16) calls KS1

Part 12 of the main flight loop calls this routine to remove the ship that is
currently being analysed by the flight loop. Once the ship is removed, it
jumps back to MAL1 to re-join the main flight loop, with X pointing to the
same slot that we just cleared (and which now contains the next ship in the
local bubble of universe).

Arguments:

XX0                  The address of the blueprint for this ship

INF                  The address of the data block for this ship

.KS1

LDX XSAV               \ Store the current ship's slot number in XSAV

JSR KILLSHP            \ Call KILLSHP to remove the ship in slot X from our
\ local bubble of universe

LDX XSAV               \ Restore the current ship's slot number from XSAV,
\ which now points to the next ship in the bubble

JMP MAL1               \ Jump to MAL1 to re-join the main flight loop at the
\ start of the ship analysis loop

Type: Subroutine
Category: Universe
Summary: Remove the space station and replace it with the sun
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* KILLSHP calls KS4

.KS4

JSR ZINF               \ Call ZINF to reset the INWK ship workspace

JSR FLFLLS             \ Reset the LSO block, returns with A = 0

STA FRIN+1             \ Set the second slot in the FRIN table to 0, which
\ sets this slot to empty, so when we call NWSHP below
\ the new sun that gets created will go into FRIN+1

STA SSPR               \ Set the "space station present" flag to 0, as we are
\ no longer in the space station's safe zone

JSR SPBLB              \ Call SPBLB to redraw the space station bulb, which
\ will erase it from the dashboard

LDA #6                 \ Set the sun's y_sign to 6
STA INWK+5

LDA #129               \ Set A = 129, the ship type for the sun

JMP NWSHP              \ Call NWSHP to set up the sun's data block and add it
\ to FRIN, where it will get put in the second slot as
\ we just cleared out the second slot, and the first
\ slot is already taken by the planet

Type: Subroutine
Category: Universe
Summary: Check the local bubble for missiles with target lock
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* KILLSHP calls KS2

Check the local bubble of universe to see if there are any missiles with
target lock in the vicinity. If there are, then check their targets; if we
just removed their target in the KILLSHP routine, then switch off their AI so
they just drift in space, otherwise update their targets to reflect the newly
shuffled slot numbers.

This is called from KILLSHP once the slots have been shuffled down, following
the removal of a ship.

Arguments:

XX4                  The slot number of the ship we removed just before
calling this routine

.KS2

LDX #&FF               \ We want to go through the ships in our local bubble
\ and pick out all the missiles, so set X to &FF to
\ use as a counter

.KSL4

INX                    \ Increment the counter (so it starts at 0 on the first
\ iteration)

LDA FRIN,X             \ If slot X is empty, loop round again until it isn't,
BEQ KS3                \ at which point A contains the ship type in that slot

CMP #MSL               \ If the slot does not contain a missile, loop back to
BNE KSL4               \ KSL4 to check the next slot

\ We have found a slot containing a missile, so now we
\ want to check whether it has target lock

TXA                    \ Set Y = X * 2 and fetch the Y-th address from UNIV
ASL A                  \ and store it in SC and SC+1 - in other words, set
TAY                    \ SC(1 0) to point to the missile's ship data block
LDA UNIV,Y
STA SC
LDA UNIV+1,Y
STA SC+1

LDY #32                \ Fetch byte #32 from the missile's ship data (AI)
LDA (SC),Y

BPL KSL4               \ If bit 7 of byte #32 is clear, then the missile is
\ dumb and has no AI, so loop back to KSL4 to move on
\ to the next slot

AND #%01111111         \ Otherwise this missile has AI, so clear bit 7 and
LSR A                  \ shift right to set the C flag to the missile's "is
\ locked" flag, and A to the target's slot number

CMP XX4                \ If this missile's target is less than XX4, then the
BCC KSL4               \ target's slot isn't being shuffled down, so jump to
\ KSL4 to move on to the next slot

BEQ KS6                \ If this missile was locked onto the ship that we just
\ removed in KILLSHP, jump to KS6 to stop the missile
\ from continuing to hunt it down

SBC #1                 \ Otherwise this missile is locked and has AI enabled,
\ and its target will have moved down a slot, so
\ subtract 1 from the target number (we know C is set
\ from the BCC above)

ASL A                  \ Shift the target number left by 1, so it's in bits
\ 1-6 once again, and also set bit 0 to 1, as the C
\ flag is still set, so this makes sure the missile is
\ still set to being locked

ORA #%10000000         \ Set bit 7, so the missile's AI is enabled

STA (SC),Y             \ Update the missile's AI flag to the value in A

BNE KSL4               \ Loop back to KSL4 to move on to the next slot (this
\ BNE is effectively a JMP as A will never be zero)

.KS6

LDA #0                 \ The missile's target lock just got removed, so set the
STA (SC),Y             \ AI flag to 0 to make it dumb and not locked

BEQ KSL4               \ Loop back to KSL4 to move on to the next slot (this
\ BEQ is effectively a JMP as A is always zero)

Type: Subroutine
Category: Universe
Summary: Remove a ship from our local bubble of universe
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* KS1 calls KILLSHP

Remove the ship in slot X from our local bubble of universe. This happens
when we kill a ship, collide with a ship and destroy it, or when a ship moves
outside our local bubble.

We also use this routine when we move out of range of the space station, in
which case we replace it with the sun.

When removing a ship, this creates a gap in the ship slots at FRIN, so we
shuffle all the later slots down to close the gap. We also shuffle the ship
data blocks at K% and ship line heap at WP, to reclaim all the memory that
the removed ship used to occupy.

Arguments:

X                    The slot number of the ship to remove

XX0                  The address of the blueprint for the ship to remove

INF                  The address of the data block for the ship to remove

.KILLSHP

STX XX4                \ Store the slot number of the ship to remove in XX4

CPX MSTG               \ Check whether this slot matches the slot number in
\ MSTG, which is the target of our missile lock

BNE KS5                \ If our missile is not locked on this ship, jump to KS5

LDY #&EE               \ Otherwise we need to remove our missile lock, so call
JSR ABORT              \ ABORT to disarm the missile and update the missile
\ indicators on the dashboard to green/cyan (Y = &EE)

LDA #200               \ Print recursive token 40 ("TARGET LOST") as an
JSR MESS               \ in-flight message

.KS5

LDY XX4                \ Restore the slot number of the ship to remove into Y

LDX FRIN,Y             \ Fetch the contents of the slot, which contains the
\ ship type

CPX #SST               \ If this is the space station, then jump to KS4 to
BEQ KS4                \ replace the space station with the sun

CPX #CON               \ Did we just kill the Constrictor from mission 1? If
BNE lll                \ not, jump to lll

LDA TP                 \ We just killed the Constrictor from mission 1, so set
ORA #%00000010         \ bit 1 of TP to indicate that we have successfully
STA TP                 \ completed mission 1

.lll

CPX #JL                \ If JL <= X < JH, i.e. the type of ship we killed in X
BCC KS7                \ is junk (escape pod, alloy plate, cargo canister,
CPX #JH                \ asteroid, splinter, Shuttle or Transporter), then keep
BCS KS7                \ going, otherwise jump to KS7

DEC JUNK               \ We just killed junk, so decrease the junk counter

.KS7

DEC MANY,X             \ Decrease the number of this type of ship in our little
\ bubble, which is stored in MANY+X (where X is the ship
\ type)

LDX XX4                \ Restore the slot number of the ship to remove into X

\ We now want to remove this ship and reclaim all the
\ memory that it uses. Removing the ship will leave a
\ gap in three places, which we need to close up:
\
\   * The ship slots in FRIN
\
\   * The ship data blocks in K%
\
\   * The descending ship line heap at WP down
\
\ The rest of this routine closes up these gaps by
\ looping through all the occupied ship slots after the
\ slot we are removing, one by one, and shuffling each
\ ship's slot, data block and line heap down to close
\ up the gaps left by the removed ship. As part of this,
\ we have to make sure we update any address pointers
\ so they point to the newly shuffled data blocks and
\ line heaps
\
\ In the following, when shuffling a ship's data down
\ into the preceding empty slot, we call the ship that
\ we are shuffling down the "source", and we call the
\ empty slot we are shuffling it into the "destination"
\
\ Before we start looping through the ships we need to
\ shuffle down, we need to set up some variables to
\ point to the source and destination line heaps

LDY #5                 \ Fetch byte #5 of the removed ship's blueprint into A,
LDA (XX0),Y            \ which gives the ship's maximum heap size for the ship
\ we are removing (i.e. the size of the gap in the heap
\ created by the ship removal)

\ INF currently contains the ship data for the ship we
\ are removing, and INF(34 33) contains the address of
\ the bottom of the ship's heap, so we can calculate
\ the address of the top of the heap by adding the heap
\ size to this address

LDY #33                \ First we add A and the address in INF+33, to get the
CLC                    \ low byte of the top of the heap, which we store in P
STA P

INY                    \ And next we add A and address in INF+34, with any
LDA (INF),Y            \ from the previous addition, to get the high byte of
ADC #0                 \ the top of the heap, which we store in P+1, so P(1 0)
STA P+1                \ points to the top of this ship's heap

\ Now, we're ready to start looping through the ships
\ we want to move, moving the slots, data blocks and
\ line heap from the source to the destination. In the
\ following, we set up SC to point to the source data,
\ and INF (which currently points to the removed ship's
\ data that we can now overwrite) points to the
\ destination
\
\ So P(1 0) now points to the top of the line heap for
\ the destination

.KSL1

INX                    \ On entry, X points to the empty slot we want to
\ shuffle the next ship into (the destination), so
\ this increment points X to the next slot - i.e. the
\ source slot we want to shuffle down

LDA FRIN,X             \ Copy the contents of the source slot into the
STA FRIN-1,X           \ destination slot

BNE P%+5               \ If the slot we just shuffled down is not empty, then
\ skip the following instruction

JMP KS2                \ The source slot is empty and we are done shuffling,
\ so jump to KS2 to move on to processing missiles

ASL A                  \ Otherwise we have a source ship to shuffle down into
TAY                    \ the destination, so set Y = A * 2 so it can act as an
\ index into the two-byte ship blueprint lookup table
\ at XX21 for the source ship

LDA XX21-2,Y           \ Set SC(0 1) to point to the blueprint data for the
STA SC                 \ source ship
LDA XX21-1,Y
STA SC+1

LDY #5                 \ Fetch blueprint byte #5 for the source ship, which
LDA (SC),Y             \ gives us its maximum heap size, and store it in T
STA T

\ We now subtract T from P(1 0), so P(1 0) will point to
\ the bottom of the line heap for the destination
\ (which we will use later when closing up the gap in
\ the heap space)

LDA P                  \ First, we subtract the low bytes
SEC
SBC T
STA P

LDA P+1                \ And then we do the high bytes, for which we subtract
SBC #0                 \ 0 to include any carry, so this is effectively doing
STA P+1                \ P(1 0) = P(1 0) - (0 T)

\ Next, we want to set SC(1 0) to point to the source
\ ship's data block

TXA                    \ Set Y = X * 2 so it can act as an index into the
ASL A                  \ two-byte lookup table at UNIV, which contains the
TAY                    \ addresses of the ship data blocks. In this case we are
\ multiplying X by 2, and X contains the source ship's
\ slot number so Y is now an index for the source ship's
\ entry in UNIV

LDA UNIV,Y             \ Set SC(1 0) to the address of the data block for the
STA SC                 \ source ship
LDA UNIV+1,Y
STA SC+1

\ We have now set up our variables as follows:
\
\   SC(1 0) points to the source's ship data block
\
\   INF(1 0) points to the destination's ship data block
\
\   P(1 0) points to the destination's line heap
\
\ so let's start copying data from the source to the
\ destination

LDY #36                \ We are going to be using Y as a counter for the 37
\ bytes of ship data we want to copy from the source
\ to the destination, so we set it to 36 to start things
\ off, and will decrement Y for each byte we copy

LDA (SC),Y             \ Fetch byte #36 of the source's ship data block at SC,
STA (INF),Y            \ and store it in byte #36 of the destination's block
DEY                    \ at INF, so that's the ship's NEWB flags copied from
\ the source to the destination. One down, quite a few
\ to go...

LDA (SC),Y             \ Fetch byte #35 of the source's ship data block at SC,
STA (INF),Y            \ and store it in byte #35 of the destination's block
\ at INF, so that's the ship's energy copied from the
\ source to the destination. One down, quite a few to
\ go...

DEY                    \ Fetch byte #34 of the source ship, which is the
LDA (SC),Y             \ high byte of the source ship's line heap, and store
STA K+1                \ in K+1

LDA P+1                \ Set the low byte of the destination's heap pointer
STA (INF),Y            \ to P+1

DEY                    \ Fetch byte #33 of the source ship, which is the
LDA (SC),Y             \ low byte of the source ship's heap, and store in K
STA K                  \ so now we have the following:
\
\   K(1 0) points to the source's line heap

LDA P                  \ Set the low byte of the destination's heap pointer
STA (INF),Y            \ to P, so now the destination's heap pointer is to
\ P(1 0), so that's the heap pointer in bytes #33 and
\ #34 done

DEY                    \ Luckily, we can just copy the rest of the source's
\ ship data block into the destination, as there are no
\ more address pointers, so first we decrement our
\ counter in Y to point to the next byte (the AI flag)
\ in byte #32) and then start looping

.KSL2

LDA (SC),Y             \ Copy the Y-th byte of the source to the Y-th byte of
STA (INF),Y            \ the destination

DEY                    \ Decrement the counter

BPL KSL2               \ Loop back to KSL2 to copy the next byte until we have
\ copied the whole block

\ We have now shuffled the ship's slot and the ship's
\ data block, so we only have the heap data itself to do

LDA SC                 \ First, we copy SC into INF, so when we loop round
STA INF                \ again, INF will correctly point to the destination for
LDA SC+1               \ the next iteration
STA INF+1

LDY T                  \ Now we want to move the contents of the heap, as all
\ we did above was to update the pointers, so first
\ we set a counter in Y that is initially set to T
\ (which we set above to the maximum heap size for the
\ source ship)
\
\ As a reminder, we have already set the following:
\
\   K(1 0) points to the source's line heap
\
\   P(1 0) points to the destination's line heap
\
\ so we can move the heap data by simply copying the
\ correct number of bytes from K(1 0) to P(1 0)
.KSL3

DEY                    \ Decrement the counter

LDA (K),Y              \ Copy the Y-th byte of the source heap at K(1 0) to
STA (P),Y              \ the destination heap at P(1 0)

TYA                    \ Loop back to KSL3 to copy the next byte, until we
BNE KSL3               \ have done them all

BEQ KSL1               \ We have now shuffled everything down one slot, so
\ jump back up to KSL1 to see if there is another slot
\ that needs shuffling down (this BEQ is effectively a
\ JMP as A will always be zero)

Save ELTL.bin

PRINT "ELITE L"
PRINT "Assembled at ", ~CODE_L%
PRINT "Ends at ", ~P%
PRINT "Code size is ", ~(P% - CODE_L%)
PRINT "Execute at ", ~LOAD%