Elite on the BBC Micro

# Elite M parasite source [Elite-A]

``` ELITE M FILE

CODE_M% = P%

Type: Subroutine
Category: Universe
Summary: Set up the INWK workspace for a ship in a random ship position
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main game loop for flight (Part 2 of 6) calls rand_posn
* Ze calls rand_posn

This routine sets up a new ship, with the following coordinates:

* (x_sign x_hi x_lo) is a random number in the range -8191 to +8191:

* x_sign is randomly positive or negative
* x_hi is a random number in the range 0 to 31
* x_lo is a random number in the range 0 to 255

* (y_sign y_hi y_lo) is a random number in the range -8191 to +8191:

* y_sign is randomly positive or negative
* y_hi is a random number in the range 0 to 31
* y_lo is a random number in the range 0 to 255

* z_sign is a random number in the range 4352 to 20224:

* z_sign is 0 (positive)
* z_hi is a random number in the range 17 to 79
* z_lo is 0

In other words, the ship is randomly up, down, left or right, but is always in
front of us.

Returns:

A                    A is set to a random number

X                    X is set to a random number

T1                   T1 is set to a random number

.rand_posn

JSR ZINF               \ Call ZINF to reset the INWK ship workspace

JSR DORND              \ Set A and X to random numbers

STA INWK               \ Set x_lo to the random number in A

STX INWK+3             \ Set y_lo to the random number in X

STA T1                 \ Store x_lo in T1

LSR A                  \ Set bit 7 of x_sign randomly (to bit 0 of the random
ROR INWK+2             \ number in A), so the x-coordinate it is randomly
\ positive or negative

LSR A                  \ Set bit 7 of y_sign randomly (to bit 1 of the random
ROR INWK+5             \ number in A), so the y-coordinate it is randomly
\ positive or negative

LSR A                  \ Set bits 0-4 of y_hi randomly (to bits 3-7 of the
STA INWK+4             \ random number in A), so the high byte of the
\ y-coordinate is in the range 0 to 31

TXA                    \ Set x_hi to the random number X, reduced to the range
AND #31                \ 0 to 31
STA INWK+1

LDA #80                \ Set z_hi = 80 - x_hi - y_hi - 1
SBC INWK+1             \
SBC INWK+4             \ The C flag is clear as INWK+4 was 0 before the ROR
STA INWK+7             \ above, so this sets z_hi in the range 17 to 79
\ (as x_hi and y_hi are both in the range 0 to 31)

JMP DORND              \ Set A and X to random numbers and return from the
\ subroutine using a tail call

Type: Subroutine
Category: Missions
Summary: Check whether we are in the Constrictor's system in mission 1
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main game loop for flight (Part 4 of 6) calls THERE

The stolen Constrictor is the target of mission 1. We finally track it down to
the Orarra system in the second galaxy, which is at galactic coordinates
(144, 33). This routine checks whether we are in this system and sets the C
flag accordingly.

Returns:

C flag               Set if we are in the Constrictor system, otherwise clear

.THERE

LDX GCNT               \ Set X = GCNT - 1
DEX

BNE THEX               \ If X is non-zero (i.e. GCNT is not 1, so we are not in

LDA QQ0                \ Set A = the current system's galactic x-coordinate

BNE THEX

LDA QQ1                \ Set A = the current system's galactic y-coordinate

CMP #33                \ If A = 33 then set the C flag

BEQ THEX+1             \ If A = 33 then jump to THEX+1, so we return from the
\ subroutine with the C flag set (otherwise we clear the
\ C flag with the next instruction)

.THEX

CLC                    \ Clear the C flag

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Universe
Summary: Initialise the INWK workspace to a hostile ship
Deep dive: Fixing ship positions
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* DEATH calls Ze
* GTHG calls Ze
* Main game loop for flight (Part 3 of 6) calls Ze

Specifically, this routine does the following:

* Reset the INWK ship workspace

* Set the ship to a fair distance away in all axes, in front of us but
randomly up or down, left or right

* Give the ship a 4% chance of having E.C.M.

* Set the ship to hostile, with AI enabled

This routine also sets A, X, T1 and the C flag to random values.

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

.Ze

JSR rand_posn          \ Call rand_posn to set up the INWK workspace for a ship
\ in a random ship position, and set T1 to a random
\ number

CMP #245               \ Set the C flag if A >= 245 (4% chance)

ROL A                  \ Set bit 0 of A to the C flag (i.e. there's a 4%
\ chance of this ship having E.C.M.)

ORA #%11000000         \ Set bits 6 and 7 of A, so the ship is hostile (bit 6
\ and has AI (bit 7)

STA INWK+32            \ Store A in the AI flag of this ship

\ Fall through into DORND2 to set A, X and the C flag
\ randomly

Type: Subroutine
Category: Utility routines
Summary: Generate random numbers, making sure the C flag doesn't affect the
outcome
Deep dive: Generating random numbers
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* DOEXP calls DORND2

.DORND2

CLC                    \ Clear the C flag so the value of the C flag on entry
\ doesn't affect the outcome

JMP DORND              \ Jump to DORND to generate random numbers in A and X,
\ returning from the subroutine using a tail call

Name: Main game loop for flight (Part 1 of 6)                 [Show more]
Type: Subroutine
Category: Main loop
Summary: Spawn a trader (a Cobra Mk III, Python, Boa or Anaconda)
Deep dive: Program flow of the main game loop
Ship data blocks
Context: See this subroutine on its own page
References: No direct references to this subroutine in this source file

This is part of the main game loop. This is the loop for when we are in
flight, while the main game loop for when we are docked is at TT100.

This section covers the following:

* Spawn a trader, i.e. a Cobra Mk III, Python, Boa or Anaconda, with a 50%
chance of it having a missile, a 50% chance of it having an E.C.M., a 50%
chance of it docking and being aggressive if attacked, a speed between 16
and 31, and a gentle clockwise roll

We call this from within the main loop.

.MTT4

JSR DORND              \ Set A and X to random numbers

LSR A                  \ Clear bit 7 of our random number in A and set the C
\ flag to bit 0 of A, which os random

STA INWK+32            \ Store this in the ship's AI flag, so this ship does
\ not have AI

STA INWK+29            \ Store A in the ship's roll counter, giving it a
\ clockwise roll (as bit 7 is clear), and a 1 in 127
\ chance of it having no damping

ROL INWK+31            \ Set bit 0 of the ship's missile count ramdomly (as the
\ C flag was set), giving the ship either no missiles or
\ one missile

AND #15                \ Set the ship speed to our random number, set to a
STA INWK+27            \ minimum of 0 and a maximum of 15

JSR DORND              \ Set A and X to random numbers, plus the C flag

BMI nodo               \ If A is negative (50% chance), jump to nodo to skip
\ the following

\ If we get here then we are going to spawn a ship that
\ is minding its own business and trying to dock

LDA INWK+32            \ Set bits 6 and 7 of the ship's AI flag, to make it
ORA #%11000000         \ aggressive if attacked, and enable its AI
STA INWK+32

LDX #%00010000         \ Set bit 4 of the ship's NEWB flags, to indicate that
STX NEWB               \ this ship is docking

.nodo

LDA #11                \ Call hordes to spawn a pack of ships from ship slots
LDX #3                 \ 11 to 14, which is where the trader ships live in the
JMP hordes             \ ship files

Name: Main game loop for flight (Part 2 of 6)                 [Show more]
Type: Subroutine
Category: Main loop
Summary: Update the main loop counters
Context: See this subroutine on its own page
References: No direct references to this subroutine in this source file

.TT100_FLIGHT

JSR M%                 \ Call M% to iterate through the main flight loop

DEC DLY                \ Decrement the delay counter in DLY, so any in-flight
\ messages get removed once the counter reaches zero

BEQ me2_flight         \ If DLY is now 0, jump to me2_flight to remove any
\ in-flight message from the space view, and once done,
\ two instructions

BPL me3_flight         \ If DLY is positive, jump to me3_flight to skip the
\ next instruction

INC DLY                \ If we get here, DLY is negative, so we have gone too
\ and need to increment DLY back to 0

.me3_flight

DEC MCNT               \ Decrement the main loop counter in MCNT

BEQ d_3fd4             \ If the counter has reached zero, which it will do
\ every 256 main loops, skip the next JMP instruction
\ (or to put it another way, if the counter hasn't
\ reached zero, jump down to MLOOP, skipping all the
\ following checks)

.ytq_flight

JMP MLOOP_FLIGHT       \ Jump down to MLOOP_FLIGHT to do some end-of-loop
\ tidying and restart the main loop

\ We only get here once every 256 iterations of the
\ main loop. If we aren't in witchspace and don't
\ already have 3 or more asteroids in our local bubble,
\ then this section has a 13% chance of spawning
\ something benign (the other 87% of the time we jump
\ down to consider spawning cops, pirates and bounty
\ hunters)
\
\ If we are in that 13%, then 50% of the time this will
\ be a Cobra Mk III trader, and the other 50% of the
\ time it will either be an asteroid (98.5% chance) or,
\ very rarely, a cargo canister (1.5% chance)

.me2_flight

LDA MCH                \ Fetch the token number of the current message into A

JSR MESS               \ Call MESS to print the token, which will remove it
\ from the screen as printing uses EOR logic

LDA #0                 \ Set the delay in DLY to 0, so any new in-flight
STA DLY                \ messages will be shown instantly

JMP me3_flight         \ Jump back into the main spawning loop at me3_flight

.d_3fd4

LDA MJ                 \ If we are in witchspace following a mis-jump, skip the
BNE ytq_flight         \ following by jumping down to MLOOP_FLIGHT (via
\ ytq_flight above)

JSR DORND              \ Set A and X to random numbers

CMP #51                \ If A >= 51 (80% chance), jump down to MTT1 to skip
BCS MTT1               \ the spawning of an asteroid or cargo canister and
\ potentially spawn something else

LDA JUNK               \ If we already have 3 or more bits of junk in the local
CMP #3                 \ bubble, jump down to MTT1 to skip the following and
BCS MTT1               \ potentially spawn something else

JSR rand_posn          \ Call rand_posn to set up the INWK workspace for a ship
\ in a random ship position

BVS MTT4               \ If V flag is set (50% chance), jump up to MTT4 to

ORA #%01101111         \ Take the random number in A and set bits 0-3 and 5-6,
STA INWK+29            \ so the result has a 50% chance of being positive or
\ negative, and a 50% chance of bits 0-6 being 127.
\ Storing this number in the roll counter therefore
\ gives our new ship a fast roll speed with a 50%
\ chance of having no damping, plus a 50% chance of
\ rolling clockwise or anti-clockwise

LDA SSPR               \ If we are inside the space station safe zone, jump
BNE MLOOPS             \ down to MLOOPS to skip the following and potentially
\ spawn something else

TXA                    \ Set A to the random X we set above, which we haven't
BCS MTT2               \ used yet, and if the C flag is set (50% chance) jump
\ down to MTT2 to skip the following

AND #15                \ Set the ship speed to our random number, reduced to
STA INWK+27            \ the range 0 to 15

BCC MTT3               \ Jump down to MTT3, skipping the following (this BCC
\ is effectively a JMP as we know the C flag is clear,
\ having passed through the BCS above)

.MTT2

ORA #%01111111         \ Set bits 0-6 of A to 127, leaving bit 7 as random, so
STA INWK+30            \ storing this number in the pitch counter means we have
\ full pitch with no damping, with a 50% chance of
\ pitching up or down

.MTT3

JSR DORND              \ Set A and X to random numbers

CMP #10                \ If random A >= 10 (96% of the time), set the C flag

AND #1                 \ Reduce A to a random number that's 0 or 1

ADC #OIL               \ Set A = #OIL + A + C, so there's a tiny chance of us
\ spawning a cargo canister (#OIL) and an even chance of
\ us spawning either a boulder (#OIL + 1) or an asteroid
\ (#OIL + 2)

BNE horde_plain        \ Jump to horde_plain to spawn a whole pack of cargo
\ canisters, boulders or asteroids, according to the
\ value of A (the BNE is effectivey a JMP, as A will
\ never be zero)

Name: Main game loop for flight (Part 3 of 6)                 [Show more]
Type: Subroutine
Category: Main loop
Summary: Potentially spawn a cop, particularly if we've been bad
Deep dive: Program flow of the main game loop
Ship data blocks
Fixing ship positions
Context: See this subroutine on its own page
References: No direct references to this subroutine in this source file

This section covers the following:

* Potentially spawn a cop (in a Viper), very rarely if we have been good,
more often if have been naughty, and very often if we have been properly

.MTT1

LDA SSPR               \ If we are outside the space station's safe zone, skip
BEQ P%+5               \ the following instruction

.MLOOPS

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)

ASL A                  \ Double A to a maximum of 80 or 140

LDX MANY+COPS          \ If there are no cops in the local bubble, skip the
BEQ P%+5               \ next instruction

ORA FIST               \ There are cops in the vicinity and we've got a hold
\ full of jail time, so OR the value in A with FIST to
\ get a new value that is at least as high as both
\ values, to reflect the fact that they have almost
\ certainly scanned our ship

STA T                  \ Store our badness level in T

JSR Ze                 \ Call Ze to initialise INWK to a potentially hostile
\ ship, and set A and X to random values
\
\ 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

CMP T                  \ If the random value in A >= our badness level, which
BCS P%+8               \ will be the case unless we have been really, really
\ bad, then skip the following three instructions (so
\ if we are really bad, there's a higher chance of
\ spawning a cop, otherwise we got away with it, for
\ now)

LDA #COPS              \ Set A to the ship type for a cop, so the following
\ call to hordes will spawn a pack of cops

.horde_plain

LDX #0                 \ Jump to hordes to spawn a pack of ships of type A,
BEQ hordes             \ returning from the subroutine using a tail call (the
\ BEQ is effectively a JMP as X is always zero)

LDA MANY+COPS          \ If we now have at least one cop in the local bubble,
BNE MLOOPS             \ jump down to MLOOPS to stop spawning, otherwise fall
\ through into the next part to look at spawning
\ something else

Name: Main game loop for flight (Part 4 of 6)                 [Show more]
Type: Subroutine
Category: Main loop
Summary: Potentially spawn a lone bounty hunter, a Thargoid, or up to four
pirates
Deep dive: Program flow of the main game loop
Ship data blocks
Fixing ship positions
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main game loop for flight (Part 1 of 6) calls entry point hordes
* Main game loop for flight (Part 3 of 6) calls entry point hordes

This section covers the following:

* Potentially spawn (47% chance) either a pack of up to 8 bounty hunters,
a Thargoid, or a pack of up to 8 pirates

* Also potentially spawn a Constrictor if this is the mission 1 endgame, or
Thargoids if mission 2 is in progress

Other entry points:

hordes               Spawn a pack of ships, made up of ships from type A to
type A + X, with the pack size normally being one to
four ships, but rarely being up to eight ships

DEC EV                 \ Decrement EV, the extra vessels spawning delay, and if
BPL MLOOPS             \ it is still positive, jump to MLOOPS to stop spawning,
\ so we only do the following when the EV counter runs
\ down

INC EV                 \ EV is negative, so bump it up again, setting it back
\ to 0

LDA TP                 \ Fetch bits 2 and 3 of TP, which contain the status of
AND #%00001100         \ mission 2

CMP #%00001000         \ If bit 3 is set and bit 2 is clear, keep going to
BNE nopl               \ spawn a Thargoid as we are transporting the plans in
\ mission 2 and the Thargoids are trying to stop us,

JSR DORND              \ Set A and X to random numbers

CMP #200               \ If the random number in A < 200 (78% chance), jump to
BCC nopl               \ nopl to skip spawning a Thargoid

.fothg2

JSR GTHG               \ Call GTHG to spawn a Thargoid ship and a Thargon
\ companion

.nopl

JSR DORND              \ Set A and X to random numbers

LDY gov                \ If the government of this system is 0 (anarchy), jump
BEQ LABEL_2            \ straight to LABEL_2 to start spawning pirates or
\ bounty hunters

CMP #120               \ If the random number in A >= 120 (53% chance), jump to
BCS MLOOPS             \ MLOOPS to stop spawning (so there's a 47% chance of
\ spawning pirates or bounty hunters)

AND #7                 \ Reduce the random number in A to the range 0-7, and
CMP gov                \ if A is less than government of this system, jump
BCC MLOOPS             \ to MLOOPS to stop spawning (so safer governments with
\ larger gov numbers have a greater chance of jumping
\ out, which is another way of saying that more
\ dangerous systems spawn pirates and bounty hunters
\ more often)

.LABEL_2

\ Now to spawn a group of bounty hunters, a Thargoid or
\ a pack of pirates

CPX #100               \ If the random number in X >= 100 (61% chance), jump
BCS mt1                \ to mt1 to spawn pirates, otherwise keep going to
\ spawn bounty hunters or a Thargoid

INC EV                 \ Increase the extra vessels spawning counter, to
\ prevent the next attempt to spawn extra vessels

AND #3                 \ Set A = random number in the range 0-3, which we
\ will now use to determine the type of ship

ADC #25                \ Add A to 25 (we know the C flag is clear as we passed
\ through the BCS above), so A is now a ship blueprint
\ position in the range 25-28, which is where the bounty
\ hunter ships live in the ship files

TAY                    \ Copy the new ship type to Y

JSR THERE              \ Call THERE to see if we are in the Constrictor's
\ system in mission 1

BCC NOCON              \ If the C flag is clear then we are not in the

LDA #%11111001         \ Set the AI flag of this ship so that it has E.C.M.,
STA INWK+32            \ has a very high aggression level of 28 out of 31, is
\ hostile, and has AI enabled - nasty stuff!

LDA TP                 \ Fetch bits 0 and 1 of TP, which contain the status of
AND #%00000011         \ mission 1

LSR A                  \ Shift bit 0 into the C flag

BCC NOCON              \ If bit 0 is clear, skip to NOCON as mission 1 is not
\ in progress

ORA MANY+CON           \ Bit 0 of A now contains bit 1 of TP, so this will be
\ set if we have already completed mission 1, so this OR
\ will be non-zero if we have either completed mission
\ 1, or there is already a Constrictor in our local
\ bubble of universe (in which case MANY+CON will be
\ non-zero)

BEQ YESCON             \ If A = 0 then mission 1 is in progress, we haven't
\ completed it yet, and there is no Constrictor in the

.NOCON

TYA                    \ Set A to the new ship type in Y

EQUB &2C               \ Skip the next instruction by turning it into
\ &2C &A9 &1F, or BIT &1FA9, which does nothing apart
\ from affect the flags

.YESCON

LDA #CON               \ If we jump straight here, we are in the mission 1
\ endgame and it's time to spawn the Constrictor, so
\ set A to the Constrictor's type

.focoug

JSR NWSHP              \ Spawn the new ship, whether it's a pirate, Thargoid or
\ Constrictor

.mj1

JMP MLOOP_FLIGHT       \ Jump down to MLOOP_FLIGHT, as we are done spawning
\ ships

.mt1

LDA #17                \ Fall through into hordes to spawn a pack of ships from
LDX #7                 \ ship blueprint positions 17 to 24, which is where the
\ pirate ships live in the ship blueprint files

.hordes

\ This routine spawns a pack of ships, made up of ships
\ from type A to type A + X, with the pack size normally
\ being one to four ships, but rarely being up to eight
\ ships
\
\ Let's call A the "base ship type", and X the "ship
\ type range"

STA horde_base+1       \ Modify the ADC instruction at horde_base so that it
\ adds the base ship type given in A

\ ANDs the ship type range given in X

JSR DORND              \ Set A and X to random numbers

CMP #248               \ If A >= 248 (3.1% chance), jump to horde_large so we
BCS horde_large        \ potentially spawn a larger pack (i.e. up to 8 ships)

STA XX13               \ We are going to spawn a smaller pack (i.e. up to 4
TXA                    \ ships), so set:
AND XX13               \
AND #3                 \   A = A AND X AND 3
\
\ to give a random pack size of 0-3, with a greater
\ chance of the smaller numbers than the larger ones
\ (due to the AND). This will be our pack size, which
\ won't be affected further by the following AND
\ instruction

.horde_large

AND #7                 \ If we are going to spawn a larger pack, reduce the
\ random number in A to the range 0-7, with an equal
\ chance of each of the numbers. This will be our pack
\ size

\ By this point our pack size is in A, and is either
\ 0-3 or 0-7

STA EV                 \ Delay further spawnings by this number

STA XX13               \ Store the number in XX13, the pack size counter

.mt3

JSR DORND              \ Set A and X to random numbers

STA T                  \ Set A = A AND X
TXA                    \
AND T                  \ which is in the range 0-255, but with a greater chance
\ of being a smaller number (due to the AND)

AND #&FF               \ This instruction gets modified so that it ANDs the
\ ship type range given in the argument X to the
\ hordes routine, i.e. it turns into AND #type_range,
\ which reduces our random number to be between 0 and
\ the ship type range (so if we add this number to the
\ base ship type, it will pick a random ship type from
\ within the range A to A + X, where A and X are the
\ arguments to the original call to hordes

STA CPIR               \ Set CPIR to our random number in the range 0 to
\ the ship type

.more

LDA CPIR               \ Set A to the ship type in CPIR

CLC                    \ Clear the C flag for the addition below

.horde_base

ADC #0                 \ This instruction gets modified so that it adds the
\ ship type given in the argument A to the hordes
\ routine, i.e. it turns into ADC #ship_type, so this
\ sets A to a ship type in the range we want

INC INWK+27            \ Increment the speed of the ship we are about to spawn,
\ so later ships in the pack go faster

INC INWK+1             \ Increment the x_hi coordinate of the ship we are about
\ to spawn, so later ships in the pack are spread out to
\ the sides

INC INWK+4             \ Increment the y_hi coordinate of the ship we are about
\ to spawn, so later ships in the pack are spread out
\ to the top and bottom

JSR NWSHP              \ Try adding a new ship of type A to the local bubble

CMP #24                \ This compares the value of A (which is set to the
\ x_sign value of the spawned ship by NWSHP), but the
\ result isn't used anywhere, as CMP affects the Z and N
\ flags (not the C flag), and these same flags will be
\ overwritten by the two DEC instructions below... so
\ instruction has no effect

BCS P%+7               \ If the ship was successfully added, skip the following
\ two instructions

DEC CPIR               \ The ship wasn't added, which might be because the ship
\ blueprint for this ship type isn't in the currently
\ loaded ship blueprints file, so decrement CPIR to
\ point to the previous ship type, so we can try
\ spawning that type of ship instead

BPL more               \ Loop back to more to have another go at spawning this
\ ship, until CPIR is 0, in which case we have tried
\ spawning all the ship types in the range, so give up
\ and move on to the next pirate to spawn

DEC XX13               \ Decrement the pack size counter

BPL mt3                \ If we need more ships, loop back up to mt3,
\ otherwise we are done spawning, so fall through into
\ the end of the main loop at MLOOP

Name: Main game loop for flight (Part 5 of 6)                 [Show more]
Type: Subroutine
Category: Main loop
Summary: Cool down lasers, make calls to update the dashboard
Context: See this subroutine on its own page
References: No direct references to this subroutine in this source file

.MLOOP_FLIGHT

LDX #&FF               \ Set the stack pointer to &01FF, which is the standard
TXS                    \ location for the 6502 stack, so this instruction
\ effectively resets the stack

LDX GNTMP              \ If the laser temperature in GNTMP is non-zero,
BEQ EE20               \ decrement it (i.e. cool it down a bit)
DEC GNTMP

.EE20

JSR DIALS              \ Call DIALS to update the dashboard

JSR COMPAS             \ Call COMPAS to update the compass

LDA QQ11               \ If this is a space view, skip the following two

\AND PATG               \ These instructions are commented out in the original
\LSR A                  \ source
\BCS d_40f8

LDY #2                 \ Wait for 2/50 of a second (0.04 seconds), to slow the
JSR DELAY              \ main loop down a bit

\JSR WSCAN              \ This instruction is commented out in the original
\ source

JSR DOKEY_FLIGHT       \ Scan the keyboard for flight controls and pause keys,
\ (or the equivalent on joystick) and update the key
\ logger, setting KL to the key pressed

JSR chk_dirn           \ Call chk_dirn to set the movement variables based on
\ the current state of the key logger

Name: Main game loop for flight (Part 6 of 6)                 [Show more]
Type: Subroutine
Category: Main loop
Summary: Process non-flight key presses (red function keys, docked keys)
Context: See this subroutine on its own page
References: No direct references to this subroutine in this source file

.FRCE_FLIGHT

PHA                    \ Store the key to "press" in A on the stack

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

\ d_locked so the key does not get "pressed"

PLA                    \ Retrieve the key to "press" from the stack into A so
\ we can now process it

JSR TT102              \ Call TT102 to process the key pressed in A

\ loop from the start

.d_locked

PLA                    \ Retrieve the key to "press" from the stack into A

JSR TT107              \ Call TT107 to progress the countdown of the hyperspace
\ counter

JMP TT100_FLIGHT       \ Jump to TT100_FLIGHT to restart the main loop from
\ the start

Type: Subroutine
Category: Keyboard
Summary: Process function key, save key, hyperspace and chart key presses
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main game loop (Part 6 of 6) calls TT102
* Main game loop for flight (Part 6 of 6) calls TT102
* HME2 calls entry point T95
* Main game loop for flight (Part 3 of 6) calls entry point BAD
* TT110 calls entry point BAD
* Main game loop for flight (Part 6 of 6) calls entry point TT107

Process function key presses, plus "@" (save commander), "H" (hyperspace),
"D" (show distance to system) and "O" (move chart cursor back to current
system). We can also pass cursor position deltas in X and Y to indicate that
the cursor keys or joystick have been used (i.e. the values that are returned
by routine TT17).

Arguments:

A                    The internal key number of the key pressed (see p.142 of
the Advanced User Guide for a list of internal key
numbers)

X                    The amount to move the crosshairs in the x-axis

Y                    The amount to move the crosshairs in the y-axis

Other entry points:

BAD                  Work out how bad we are from the amount of contraband in
our hold

T95                  Print the distance to the selected system

TT107                Progress the countdown of the hyperspace counter

.TT102

CMP #f8                \ If red key f8 was pressed, jump to STATUS to show the
BNE P%+5               \ Status Mode screen, returning from the subroutine
JMP STATUS             \ using a tail call

CMP #f4                \ If red key f4 was pressed, jump to TT22 to show the
BNE P%+5               \ Long-range Chart, returning from the subroutine using
JMP TT22               \ a tail call

CMP #f5                \ If red key f5 was pressed, jump to TT23 to show the
BNE P%+5               \ Short-range Chart, returning from the subroutine using
JMP TT23               \ a tail call

CMP #f6                \ If red key f6 was pressed, call TT111 to select the
BNE TT92               \ system nearest to galactic coordinates (QQ9, QQ10)
JSR TT111              \ (the location of the chart crosshairs) and set ZZ to
JMP TT25               \ the system number, and then jump to TT25 to show the
\ Data on System screen (along with an extended system
\ description for the system in ZZ if we're docked),
\ returning from the subroutine using a tail call

.TT92

CMP #f9                \ If red key f9 was pressed, jump to TT213 to show the
BNE P%+5               \ Inventory screen, returning from the subroutine
JMP TT213              \ using a tail call

CMP #f7                \ If red key f7 was pressed, jump to TT167 to show the
BNE P%+5               \ Market Price screen, returning from the subroutine
JMP TT167              \ using a tail call

.LABEL_3

CMP #&32               \ If "D" was pressed, jump to T95 to print the distance
BEQ T95                \ to a system (if we are in one of the chart screens)

CMP #&43               \ If "F" was not pressed, jump down to HME1, otherwise
BNE HME1               \ keep going to process searching for systems (when
\ docked) or toggle the compass display (when flying)

LDA QQ11               \ If the current view is a chart (QQ11 = 64 or 128),
BEQ n_finder

LDA dockedp            \ If dockedp is non-zero, then we are not docked and
BNE t95                \ can't search for a system, so return from the
\ subroutine (as t95 contains an RTS)

JMP HME2               \ Jump to HME2 to let us search for a system, returning
\ from the subroutine using a tail call

.n_finder

LDA dockedp            \ If dockedp is zero, then we are docked and we can't
BEQ t95                \ change the compass configuration, so return from the
\ subroutine (as t95 contains an RTS)

LDA finder             \ Set the value of A to finder, which determines whether
\ the compass is configured to show the sun or the
\ planet

EOR #NI%               \ The value of finder is 0 (show the planet) or NI%
\ (show the sun), so this toggles the value between the
\ two

STA finder             \ Store the toggled value in finder

JMP WSCAN              \ Jump to WSCAN to wait for the vertical sync and return
\ from the subroutine using a tail call

.t95

RTS                    \ Return from the subroutine

.HME1

CMP #&36               \ If "O" was pressed, do the following, otherwise skip
BNE not_home           \ to not_home to continue checking key presses

LDA QQ11               \ If both bits 6 or 7 of the view number are clear - so
AND #%11000000         \ this is not the Short-range or Long-range Chart -
BEQ t95                \ then jump to t95 to return from the subroutine,
\ otherwise do the following three jumps

JSR TT103              \ Draw small crosshairs at coordinates (QQ9, QQ10),
\ which will erase the crosshairs currently there

JSR ping               \ Set the target system to the current system (which
\ will move the location in (QQ9, QQ10) to the current
\ home system

JMP TT103              \ Draw small crosshairs at coordinates (QQ9, QQ10),
\ which will draw the crosshairs at our current home
\ system, and return from the subroutine using a tail
\ call

.not_home

CMP #&21               \ If "W" was pressed, continue on to move the crosshairs
\ ee2 to continue

LDA QQ11               \ If both bits 6 or 7 of the view number are clear - so
AND #%11000000         \ this is not the Short-range or Long-range Chart -
BEQ t95                \ then jump to t95 to return from the subroutine,
\ otherwise do the following three jumps

LDA cmdr_cour          \ If there is no special cargo delivery mission in
ORA cmdr_cour+1        \ progress, then the mission timer in cmdr_cour(1 0)
BEQ t95                \ will be zero, so return from the subroutine (as t95
\ contains an RTS)

JSR TT103              \ Draw small crosshairs at coordinates (QQ9, QQ10),
\ which will erase the crosshairs currently there

LDA cmdr_courx         \ Set the galactic coordinates in (QQ9, QQ10) to the
STA QQ9                \ current special cargo delivery destination in
LDA cmdr_coury         \ (cmdr_courx, cmdr_coury)
STA QQ10

JSR TT103              \ Draw small crosshairs at coordinates (QQ9, QQ10),
\ which will draw the crosshairs at our current home
\ system

.T95

\ If we get here, "D" was pressed, so we need to show
\ the distance to the selected system (if we are in a
\ chart view)

LDA QQ11               \ If the current view is a chart (QQ11 = 64 or 128),
AND #%11000000         \ keep going, otherwise return from the subroutine (as
BEQ t95                \ t95 contains an RTS)

JSR hm                 \ Call hm to move the crosshairs to the target system
\ in (QQ9, QQ10), returning with A = 0

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

JSR cpl                \ Print control code 3 (the selected system name)

LDA #%10000000         \ Set bit 7 of QQ17 to switch to Sentence Case, with the
STA QQ17               \ next letter in capitals

LDA #1                 \ Move the text cursor to column 1 and down one line
STA XC                 \ (in other words, to the start of the next line)
INC YC

JMP TT146              \ Print the distance to the selected system and return
\ from the subroutine using a tail call

.ee2

BIT dockedp            \ If bit 7 of dockedp is set, then we are not docked, so

CMP #f0                \ If red key f0 was not pressed, jump to fvw to check
BNE fvw                \ for the next key

JSR CTRL               \ Red key f0 was pressed, so check whether CTRL was
BMI jump_stay          \ also pressed, and if so, jump to jump_stay to skip the
\ following instruction

JMP RSHIPS             \ Red key f0 was pressed on its own, so jump to RSHIPS
\ to launch our ship, returning from the subroutine
\ using a tail call

.jump_stay

JMP stay_here          \ CTRL-f0 was pressed, so jump to stay_here to pay the
\ docking fee and refresh prices

.fvw

CMP #f3                \ If red key f3 was pressed, jump to EQSHP to show the
BNE P%+5               \ Equip Ship screen, returning from the subroutine using
JMP EQSHP              \ a tail call

CMP #f1                \ If red key f1 was pressed, jump to TT219 to show the
BNE P%+5               \ Buy Cargo screen, returning from the subroutine using
JMP TT219              \ a tail call

BNE nosave

JSR SVE                \ "@" was pressed, so call SVE to show the disc access

BCC P%+5               \ If the C flag was set by SVE, then we loaded a new
JMP QU5                \ commander file, so jump to QU5 to restart the game
\ with the newly loaded commander

JMP BAY                \ Otherwise the C flag was clear, so jump to BAY to go
\ to the docking bay (i.e. show the Status Mode screen)

.nosave

CMP #f2                \ If red key f2 was pressed, jump to TT208 to show the
BNE not_sell           \ Sell Cargo screen, returning from the subroutine using
JMP TT208              \ a tail call

.not_sell

CMP #&54               \ If "H" was not pressed, jump to NWDAV5 to skip the
BNE NWDAV5             \ following

JSR CLYNS              \ "H" was pressed, so clear the bottom three text rows
\ of the upper screen, and move the text cursor to
\ column 1 on row 21, i.e. the start of the top row of
\ the three bottom rows

LDA #15                \ Move the text cursor to column 15 (the middle of the
STA XC                 \ screen)

LDA #205               \ Print extended token 205 ("DOCKED") and return from
JMP DETOK              \ the subroutine using a tail call

.flying

CMP #&20               \ If "D" was pressed, jump to TT110 to print the
BNE P%+5               \ distance to a system (if we are in one of the chart
JMP TT110              \ screens)

CMP #f1                \ If the key pressed is < red key f1 or > red key f3,
BCC d_4143             \ jump to d_4143 (so only do the following if the key
CMP #f3+1              \ pressed is f1, f2 or f3)
BCS d_4143

AND #3                 \ If we get here then we are either in space, or we are
TAX                    \ docked and none of f1-f3 were pressed, so we can now
JMP LOOK1              \ process f1-f3 with their in-flight functions, i.e.
\ switching space views
\
\ A will contain &71, &72 or &73 (for f1, f2 or f3), so
\ set X to the last digit (1, 2 or 3) and jump to LOOK1
\ to switch to view X (rear, left or right), returning
\ from the subroutine using a tail call

.d_4143

CMP #&54               \ If "H" was not pressed, jump to NWDAV5 to skip the
BNE NWDAV5             \ following

JMP hyp                \ Jump to hyp to do a hyperspace jump (if we are in
\ space), returning from the subroutine using a tail
\ call

.TT107

LDA QQ22+1             \ If the on-screen hyperspace counter is zero, return
BEQ d_418a             \ from the subroutine (as d_418a contains an RTS), as we
\ are not currently counting down to a hyperspace jump

DEC QQ22               \ Decrement the internal hyperspace counter

BNE d_418a             \ If the internal hyperspace counter is still non-zero,
\ then we are still counting down, so return from the
\ subroutine (as d_418a contains an RTS)

\ If we get here then the internal hyperspace counter
\ has just reached zero and it wasn't zero before, so
\ we need to reduce the on-screen counter and update
\ the screen. We do this by first printing the next
\ number in the countdown sequence, and then printing
\ the old number, which will erase the old number
\ and display the new one because printing uses EOR
\ logic

LDX QQ22+1             \ Set X = the on-screen hyperspace counter - 1
DEX                    \ (i.e. the next number in the sequence)

JSR ee3                \ Print the 8-bit number in X at text location (0, 1)

LDA #5                 \ Reset the internal hyperspace counter to 5
STA QQ22

LDX QQ22+1             \ Set X = the on-screen hyperspace counter (i.e. the
\ current number in the sequence, which is already
\ shown on-screen)

JSR ee3                \ Print the 8-bit number in X at text location (0, 1),
\ i.e. print the hyperspace countdown in the top-left
\ corner

DEC QQ22+1             \ Decrement the on-screen hyperspace countdown

BNE d_418a             \ If the countdown is not yet at zero, return from the
\ subroutine (as d_418a contains an RTS)

JMP TT18               \ Otherwise the countdown has finished, so jump to TT18
\ to do a hyperspace jump, returning from the subroutine
\ using a tail call

LDA QQ20+3             \ Set A to the number of tonnes of slaves in the hold

CLC                    \ Clear the C flag so we can do addition without the
\ C flag affecting the result

ADC QQ20+6             \ Add the number of tonnes of narcotics in the hold

ASL A                  \ Double the result and add the number of tonnes of
ADC QQ20+10            \ firearms in the hold

.d_418a

RTS                    \ Return from the subroutine

.NWDAV5

LDA QQ11               \ If the current view is a chart (QQ11 = 64 or 128),
AND #%11000000         \ keep going, otherwise return from the subroutine (as
BEQ d_418a             \ d_418a contains an RTS)

JMP TT16               \ Jump to TT16 to move the crosshairs by the amount in X
\ and Y, which were passed to this subroutine as
\ arguments, and return from the subroutine using a tail
\ call

Type: Subroutine
Category: Maths (Geometry)
Summary: Compare x_hi, y_hi and z_hi with 224
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 12 of 16) calls FAROF

Compare x_hi, y_hi and z_hi with 224, and set the C flag if all three <= 224,
otherwise clear the C flag.

Returns:

C flag               Set if x_hi <= 224 and y_hi <= 224 and z_hi <= 224

Clear otherwise (i.e. if any one of them are bigger than
224)

.FAROF

LDA #224               \ Set A = 224 and fall through into FAROF2 to do the
\ comparison

Type: Subroutine
Category: Maths (Geometry)
Summary: Compare x_hi, y_hi and z_hi with A
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 14 of 16) calls FAROF2

Compare x_hi, y_hi and z_hi with A, and set the C flag if all three <= A,
otherwise clear the C flag.

Returns:

C flag               Set if x_hi <= A and y_hi <= A and z_hi <= A

Clear otherwise (i.e. if any one of them are bigger than
A)

.FAROF2

CMP INWK+1             \ If A < x_hi, C will be clear so jump to FA1 to
BCC FA1                \ return from the subroutine with C clear, otherwise
\ C will be set so move on to the next one

CMP INWK+4             \ If A < y_hi, C will be clear so jump to FA1 to
BCC FA1                \ return from the subroutine with C clear, otherwise
\ C will be set so move on to the next one

CMP INWK+7             \ If A < z_hi, C will be clear, otherwise C will be set

.FA1

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Maths (Geometry)
Summary: Calculate a cap on the maximum distance to a ship
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 7 of 16) calls MAS4
* TACTICS (Part 1 of 7) calls MAS4
* TACTICS (Part 6 of 7) calls MAS4

Logical OR the value in A with the high bytes of the ship's position (x_hi,
y_hi and z_hi).

Returns:

A                    A OR x_hi OR y_hi OR z_hi

.MAS4

ORA INWK+1             \ OR A with x_hi, y_hi and z_hi
ORA INWK+4
ORA INWK+7

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Start and end
Summary: Display the death screen
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 15 of 16) calls DEATH
* OOPS calls DEATH

We have been killed, so display the chaos of our destruction above a "GAME
OVER" sign, and clean up the mess ready for the next attempt.

.DEATH

JSR EXNO3              \ Make the sound of us dying

JSR RES2               \ Reset a number of flight variables and workspaces

ASL DELTA              \ Divide our speed in DELTA by 4
ASL DELTA

LDX #24                \ Set the screen to only show 24 text rows, which hides
JSR DET1               \ the dashboard, setting A to 6 in the process

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

JSR BOX                \ Call BOX to redraw the same white border (BOX is part
\ of TT66), which removes the border as it is drawn
\ using EOR logic

JSR nWq                \ Create a cloud of stardust containing the correct
\ number of dust particles (i.e. NOSTM of them)

LDA #12                \ Move the text cursor to column 12 on row 12
STA YC
STA XC

LDA #146               \ Print recursive token 146 ("{all caps}GAME OVER")
JSR ex

.D1

JSR Ze                 \ Call Ze to initialise INWK to a potentially hostile
\ ship, and set A and X to random values

LSR A                  \ Set A = A / 4, so A is now between 0 and 63, and
LSR A                  \ store in byte #0 (x_lo)
STA INWK

LDY #0                 \ Set the following to 0: the current view in QQ11
STY QQ11               \ (space view), x_hi, y_hi, z_hi and the AI flag (no AI
STY INWK+1             \ or E.C.M. and not hostile)
STY INWK+4
STY INWK+7
STY INWK+32

DEY                    \ Set Y = 255

STY MCNT               \ Reset the main loop counter to 255, so all timer-based
\ calls will be stopped

EOR #%00101010         \ Flip bits 1, 3 and 5 in A (x_lo) to get another number
STA INWK+3             \ between 48 and 63, and store in byte #3 (y_lo)

ORA #%01010000         \ Set bits 4 and 6 of A to bump it up to between 112 and
STA INWK+6             \ 127, and store in byte #6 (z_lo)

TYA                    \ Tell the I/O processor to set its copy of LASCT to
JSR write_0346         \ 255, to act as a counter in the D2 loop below, so this
\ setting determines how long the death animation lasts
\ (it's 5.1 seconds, as LASCT is decremented every
\ vertical sync, or 50 times a second, and
\ 255 / 50 = 5.1)

TXA                    \ Set A to the random number in X and keep bits 0-3 and
AND #%10001111         \ the bit 7 to get a number between -15 and +15, and
STA INWK+29            \ store in byte #29 (roll counter) to give our ship a
\ gentle roll with damping

ROR A                  \ The C flag is randomly set from the above call to Ze,
AND #%10000111         \ so this sets A to a number between -7 and +7, which
STA INWK+30            \ we store in byte #30 (the pitch counter) to give our
\ ship a very gentle pitch with damping

LDX #OIL               \ Set X to #OIL, the ship type for a cargo canister

BCC D3                 \ If the C flag is clear, which will be random following
\ the above call to Ze, jump to D3 to skip the following
\ instruction

DEX                    \ Decrement X, which sets it to #PLT, the ship type for
\ an alloy plate

.D3

JSR fq1                \ Call fq1 with X set to #OIL or #PLT, which adds a new
\ cargo canister or alloy plate to our local bubble of
\ universe and points it away from us with double DELTA
\ speed (i.e. 6, as DELTA was set to 3 by the call to
\ RES2 above). INF is set to point to the new arrival's
\ ship data block in K%

JSR DORND              \ Set A and X to random numbers and extract bit 7 from A
AND #%10000000

LDY #31                \ Store this in byte #31 of the ship's data block, so it
STA (INF),Y            \ has a 50% chance of marking our new arrival as being
\ killed (so it will explode)

LDA FRIN+4             \ The call we made to RES2 before we entered the loop at
BEQ D1                 \ D1 will have reset all the ship slots at FRIN, so this
\ checks to see if the fifth slot is empty, and if it
\ is we loop back to D1 to add another canister, until
\ we have added five of them

JSR U%                 \ Clear the key logger, which also sets A = 0

STA DELTA              \ Set our speed in DELTA to 0, as we aren't going
\ anywhere any more

.D2

JSR M%                 \ Call the M% routine to do the main flight loop once,
\ which will display our exploding canister scene and

JSR read_0346          \ Get the value of the I/O processor's copy of LASCT

BNE D2                 \ Loop back to D2 to run the main flight loop until
\ LASCT reaches zero (which will take 5.1 seconds, as
\ explained above)

LDX #31                \ Set the screen to show all 31 text rows, which shows
JSR DET1               \ the dashboard

JMP DEATH2             \ Jump to DEATH2 to reset and restart the game

Type: Subroutine
Summary: Launch from the station, load a new set of ship blueprints and
jump into the main game loop
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* TT102 calls RSHIPS

.RSHIPS

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

JSR RESET              \ Call RESET to reset most variables

LDA #&FF               \ Set QQ1 to &FF to indicate we are docked, so when
STA QQ12               \ we reach TT110 after calling FRCE below, it skips the
\ launch tunnel

STA QQ11               \ Set the view number to a non-zero value, so when we
\ reach LOOK1 after calling FRCE below, it sets up a
\ new space view

STA dockedp            \ Set dockedp to &FF to indicate that we are no longer
\ docked

LDA #f0                \ Jump into the main game loop at FRCE_FLIGHT, setting
JMP FRCE_FLIGHT        \ the key "pressed" to red key f0 (so we launch from the
\ station)

Type: Subroutine
Category: Universe
Summary: Populate the ship blueprints table at XX21 with a random selection
of ships and set the compass to point to the planet
Deep dive: Ship blueprints in Elite-A
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* RSHIPS calls LSHIPS
* TT18 calls LSHIPS
* MJP calls entry point SHIPinA

Other entry points:

SHIPinA              Populate the ship blueprints table but without setting
the compass to show the planet

.LSHIPS

LDA #0                 \ Set finder to 0, so the compass shows the planet
STA finder

.SHIPinA

LDX #0                 \ The first task is to fill blueprint position 2, which
\ contains the space station blueprint, so set X = 0,
\ which is the ship_list ship type for a Dodo space
\ station

LDA tek                \ If the current system's tech level is 10 or more, then
CMP #10                \ skip the following instruction, as we already have the
BCS mix_station        \ correct space station type in X

INX                    \ Increment X to 1, the ship_list ship type for a
\ Coriolis space station

.mix_station

LDY #2                 \ Install a ship of type X (Dodo or Coriolis station)
JSR install_ship       \ into blueprint position 2

LDY #9                 \ The next blueprint position we need to fill is number
\ 9, for the shuttle, so set Y to point to this position
\ so we can use it as a counter, starting at position 9
\ and working our way up to position 28 (as positions 29
\ to 31 are already filled)

.mix_retry

LDA #0                 \ Set X1 = 0 to act as a failure counter, so we can
STA X1                 \ have 256 failed attempts to fill each blueprint
\ position before giving up and leaving it blank

.mix_match

JSR DORND              \ Set A and X to random numbers

CMP #ship_total        \ If A >= #ship_total then it is too big to be a
BCS mix_match          \ ship_list ship type, so loop back to choose another
\ random number until it is a valid ship type

ASL A                  \ Set Y1 = A * 4, so we can use it a random index into
ASL A                  \ the ship_bits table, which has four bytes in each
STA Y1                 \ entry

\ Y1 now contains the ship_list ship type of the ship we
\ are going to try installing into position Y, just
\ multiplied by 4 so it can be used as a four-byte index
\
\ We now want to check the ship_bits table to see if the
\ ship type in Y1 is allowed in ship blueprint position
\ Y. The table contains a 32-bit number for each ship
\ type, with the corresponding bits set for allowed
\ positions, so if a ship type were allowed in positions
\ 11 and 17, for example, only bits 11 and 17 would be
\ set in the ship_bits table entry for that type
\
\ To do this, we work out which of the four bytes in the
\ 32-bit number contains the bit we want to match, and
\ then create a byte that has the correct bit set for
\ that particular byte, so we can AND them together to
\ see if there is a match
\
\ For example, say we are trying to populate position 17
\ (so Y = 17), and we want to know whether our ship of
\ type Y1 is allowed in this position. We know what we
\ need to look at the 32-bit number in row Y1 of the
\ ship_bits table, and we also know that bit 17 appears
\ in the third byte of a 32-bit number (i.e. byte #2),
\ so we can set an index in X:
\
\   X = Y1 + 2
\
\ and we can use this as an index into the ship_bits
\ table to fetch the relevant byte from the 32-bit
\ number we want to match, which is at this location:
\
\   ship_bits + X
\
\ Given this byte, we need to check the relevant bit. We
\ know that bit 17 of a 32-bit number corresponds to the
\ second bit of the third byte, so if we create a byte
\ with that bit set:
\
\   A = %00000010
\
\ then we can AND the two together, and if we get a
\ non-zero result, we know that bit 17 in the 32-bit
\ number is set:
\
\   result = ?(ship_bits + X) AND A
\
\
\ This is what we now do, starting with the byte in A,
\ which we can grab from the lookup table at mix_bits,
\ then calculating X, before extracting the relevant
\ byte from the ship_bits table and performing the AND

TYA                    \ Set X = Y mod 8, so as Y works through the positions
AND #7                 \ from 0 to 31, making its way from bit 0 to bit 31 of
TAX                    \ the 32-bit number, X represents the position of the
\ current bit within each of the four bytes that make up
\ the 32-bit number

LDA mix_bits,X         \ Set A to the X-th byte from mix_bits, which contains a
\ table of bytes with the relevant bit sit for each
\ value of X (so the value at mix_bits + X has bit X
\ set). This gives us our value of A in the above
\ explanation, so if we were looking to populate
\ blueprint position 17, A would now be %00000010

LDX Y1                 \ Set X to the ship type we are going to try to install

\ We now want to add to X to point to the correct byte
\ within the 32-bit number, depending on the blueprint
\ position in Y that we are trying to fill:
\
\   * If Y is in the range  0 to  7, X = X + 0
\   * If Y is in the range  8 to 15, X = X + 1
\   * If Y is in the range 16 to 23, X = X + 2
\   * If Y is in the range 24 to 28, X = X + 3
\
\ note that because we are starting at position 9, we
\ can ignore the first case. In our above example, we
\ are filling positon 17, so we would add 2 to X

CPY #16                \ If the blueprint position we are trying to fill is
BCC mix_byte2          \ less than 16, jump to mix_byte2 so we increment X once

CPY #24                \ If the blueprint position we are trying to fill is
BCC mix_byte3          \ less than 24, jump to mix_byte3 so we increment X
\ twice

INX                    \ Increment X as Y is in the range 24 to 28

.mix_byte3

INX                    \ Increment X as Y is in the range 16 to 28

.mix_byte2

INX                    \ Increment X as Y is in the range 9 to 28

\ We now have the correct values of A and X, as per the
\ above calculation, so it's time to do the AND logic:
\
\   result = ?(ship_bits + X) AND A
\
\ which we can do easily in assembly language:
\
\   AND ship_bits,X
\
\ followed by a BEQ to check whether the result is zero

AND ship_bits,X        \ If the X-th byte of ship_bits does not have the same
BEQ mix_fail           \ bit set as our moving bit counter in A, jump to
\ mix_fail to have another go at filling this blueprint
\ position, as the ship in Y1 is not allowed in
\ blueprint position Y

.mix_try

\ If we get here then the ship in Y1 is allowed in
\ blueprint position Y, so now we decide whether or not
\ to go ahead, depending on the probability figure for
\ this ship type, which we fetch from the first entry in
\ the ship_bytes table for this ship type

JSR DORND              \ Set A and X to random numbers

LDX Y1                 \ Set X to the ship type we are going to try to install

CMP ship_bytes,X       \ If A < the X-th entry in ship_bytes, i.e. it is less
BCC mix_ok             \ than the first byte in the ship_bytes entry for this
\ the blueprint position. So, for example, if this ship
\ type has a value of 100 as the first byte in its entry
\ in the ship_bytes table, which is the case for the
\ Mamba and Sidewinder, then we only add it to this
\ blueprint position if A < 100, or a 39% chance. The
\ much rarer Dragon, meanwhile, has a ship_bytes entry
\ of 3, so the calculation is A < 3, or a 1.2% chance

.mix_fail

\ If we get here then either this ship isn't allowed in
\ this position, or it failed the probability test
\ above, so we decrement the failure counter and loop
\ back for another go (up to a maximum number of 256
\ attempts for each position)

DEC X1                 \ Decrement the failure counter in X1

BNE mix_match          \ If we haven't run out of failure attempts, jump back
\ to mix_match to have another go at filling this
\ blueprint position

LDX #ship_total*4      \ Otherwise we have run out of attempts, so set X to
\ point to the last entry in the table, which contains
\ data for an empty position, so this blueprint position
\ will be empty

.mix_ok

STY X2                 \ Store Y, the blueprint position we are trying to fill,
\ in X2 so we can retrieve it later

CPX #13*4              \ If X is the four-byte index for ship number 13, then
\ mix_anaconda to install it as the "large ship", along
\ with a Worm as the "small ship"

CPX #29*4              \ If X is the four-byte index for ship number 29, then
\ to install it as the "large ship", along with a
\ Sidewinder as the "small ship"

TXA                    \ Set X = X / 4, so X is now the type of the ship we
LSR A                  \ want to add, rather than an index into a four-byte
LSR A                  \ table
TAX

.mix_install

JSR install_ship       \ Install a ship of type X into blueprint position Y

LDY X2                 \ Set Y to the blueprint position we are trying to fill,
\ which we stored in X2 above

.mix_next

INY                    \ Increment Y to point to the next blueprint position to
\ fill

CPY #15                \ If the next position is not number 15 (the "small
\ instructions

INY                    \ The next position is 15, so increment Y twice so we
INY                    \ skip over positions 15 (the "small ship") and 16 (the
\ cop), as the first one is only filled if we have an
\ Anaconda or Dragon as the "large ship" (see above),
\ and the second one is already filled with a Viper

.mix_skip

CPY #29                \ If the next blueprint position we are trying to fill
BNE mix_retry          \ is not 29, then loop back to mix_retry to fill the
\ next position

RTS                    \ Otherwise we just filled the last position, number 28,
\ so return from the subroutine as we are done

.mix_anaconda

LDX #13                \ Install ship number 13 (Anaconda) into blueprint
LDY #14                \ position 14 (the "large ship")
JSR install_ship

LDX #14                \ Install ship number 14 (Worm) into blueprint position
LDY #15                \ 15 (the "small ship"), so the Anaconda can spawn
JMP mix_install        \ Worms, and rejoin the mix loop via mix_install

.mix_dragon

LDX #29                \ Install ship number 29 (Dragon) into blueprint
LDY #14                \ position 14 (the "large ship")
JSR install_ship

LDX #17                \ Install ship number 14 (Sidewinder) into blueprint
LDY #15                \ position 15 (the "small ship"), so the Dragon can
JMP mix_install        \ spawn Sidewinders, and rejoin the mix loop via
\ mix_install

Type: Variable
Summary: Lookup table for locating a specific bit in the 32-bit word for a
given ship blueprint position
Deep dive: Ship blueprints in Elite-A
Context: See this variable on its own page
References: This variable is used as follows:
* LSHIPS calls mix_bits

.mix_bits

EQUB %00000001         \ Positions 0,  8, 16, 24
EQUB %00000010         \ Positions 1,  9, 17, 25
EQUB %00000100         \ Positions 2, 10, 18, 26
EQUB %00001000         \ Positions 3, 11, 19, 27
EQUB %00010000         \ Positions 4, 12, 20, 28
EQUB %00100000         \ Positions 5, 13, 21, 29
EQUB %01000000         \ Positions 6, 14, 22, 30
EQUB %10000000         \ Positions 7, 15, 23, 31

Type: Subroutine
Category: Maths (Geometry)
Summary: Calculate the vector to the planet, sun or station and store it in
XX15
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* COMPAS calls SPS1
* Main flight loop (Part 9 of 16) calls SPS1
* TACTICS (Part 3 of 7) calls SPS1

Arguments:

Y                    Determines the object whose vector we are calculating:

* 0 = calculate the vector to the planet

* NI% = calculate the vector to the sun/space station

Other entry points:

SPS1+1               A BRK instruction

.SPS1

LDX #0                 \ Copy the two high bytes of the planet/sun/station's
JSR SPS3               \ x-coordinate into K3(2 1 0), separating out the sign
\ bit into K3+2

JSR SPS3               \ Copy the two high bytes of the planet/sun/station's
\ y-coordinate into K3(5 4 3), separating out the sign
\ bit into K3+5

JSR SPS3               \ Copy the two high bytes of the planet/sun/station's
\ z-coordinate into K3(8 7 6), separating out the sign
\ bit into K3+8

\ Fall through into TAS2 to build XX15 from K3

Type: Subroutine
Category: Maths (Geometry)
Summary: Normalise the three-coordinate vector in K3
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* DOCKIT calls TAS2
* TACTICS (Part 3 of 7) calls TAS2
* DOCKIT calls entry point TA2

Normalise the vector in K3, which has 16-bit values and separate sign bits,
and store the normalised version in XX15 as a signed 8-bit vector.

A normalised vector (also known as a unit vector) has length 1, so this
routine takes an existing vector in K3 and scales it so the length of the
new vector is 1. This is used in two places: when drawing the compass, and
when applying AI tactics to ships.

We do this in two stages. This stage shifts the 16-bit vector coordinates in
K3 to the left as far as they will go without losing any bits off the end, so
we can then take the high bytes and use them as the most accurate 8-bit vector
to normalise. Then the next stage (in routine NORM) does the normalisation.

Arguments:

K3(2 1 0)            The 16-bit x-coordinate as (x_sign x_hi x_lo), where
x_sign is just bit 7

K3(5 4 3)            The 16-bit y-coordinate as (y_sign y_hi y_lo), where
y_sign is just bit 7

K3(8 7 6)            The 16-bit z-coordinate as (z_sign z_hi z_lo), where
z_sign is just bit 7

Returns:

XX15                 The normalised vector, with:

* The x-coordinate in XX15

* The y-coordinate in XX15+1

* The z-coordinate in XX15+2

Other entry points:

TA2                  Calculate the length of the vector in XX15 (ignoring the
low coordinates), returning it in Q

.TAS2

LDA K3                 \ OR the three low bytes and 1 to get a byte that has
ORA K3+3               \ a 1 wherever any of the three low bytes has a 1
ORA K3+6               \ (as well as always having bit 0 set), and store in
ORA #1                 \ K3+9
STA K3+9

LDA K3+1               \ OR the three high bytes to get a byte in A that has a
ORA K3+4               \ 1 wherever any of the three high bytes has a 1
ORA K3+7

\ (A K3+9) now has a 1 wherever any of the 16-bit
\ values in K3 has a 1
.TAL2

ASL K3+9               \ Shift (A K3+9) to the left, so bit 7 of the high byte
ROL A                  \ goes into the C flag

BCS TA2                \ If the left shift pushed a 1 out of the end, then we
\ know that at least one of the coordinates has a 1 in
\ this position, so jump to TA2 as we can't shift the
\ values in K3 any further to the left

ASL K3                 \ Shift K3(1 0), the x-coordinate, to the left
ROL K3+1

ASL K3+3               \ Shift K3(4 3), the y-coordinate, to the left
ROL K3+4

ASL K3+6               \ Shift K3(6 7), the z-coordinate, to the left
ROL K3+7

BCC TAL2               \ Jump back to TAL2 to do another shift left (this BCC
\ is effectively a JMP as we know bit 7 of K3+7 is not a
\ 1, as otherwise bit 7 of A would have been a 1 and we
\ would have taken the BCS above)

.TA2

LDA K3+1               \ Fetch the high byte of the x-coordinate from our left-
LSR A                  \ shifted K3, shift it right to clear bit 7, stick the
ORA K3+2               \ sign bit in there from the x_sign part of K3, and
STA XX15               \ store the resulting signed 8-bit x-coordinate in XX15

LDA K3+4               \ Fetch the high byte of the y-coordinate from our left-
LSR A                  \ shifted K3, shift it right to clear bit 7, stick the
ORA K3+5               \ sign bit in there from the y_sign part of K3, and
STA XX15+1             \ store the resulting signed 8-bit y-coordinate in
\ XX15+1

LDA K3+7               \ Fetch the high byte of the z-coordinate from our left-
LSR A                  \ shifted K3, shift it right to clear bit 7, stick the
ORA K3+8               \ sign bit in there from the z_sign part of K3, and
STA XX15+2             \ store the resulting signed 8-bit  z-coordinate in
\ XX15+2

JMP NORM               \ Now we have a signed 8-bit version of the vector K3 in
\ the subroutine using a tail call

Type: Subroutine
Category: Flight
Summary: Perform an in-system jump
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 3 of 16) calls WARP
* Main flight loop (Part 3 of 16) calls entry point WA1

This is called when we press "J" during flight. The following checks are
performed:

* Make sure we don't have any ships or space stations in the vicinity

* Make sure we are not in witchspace

* If we are facing the planet, make sure we aren't too close

* If we are facing the sun, make sure we aren't too close

If the above checks are passed, then we perform an in-system jump by moving
the sun and planet in the opposite direction to travel, so we appear to jump
in space. This means that any asteroids, cargo canisters or escape pods get
dragged along for the ride.

Other entry points:

WA1                  Make a long, low beep

.WARP

LDX JUNK               \ Set X to the total number of junk items in the
\ vicinity (e.g. asteroids, escape pods, cargo
\ canisters, Shuttles, Transporters and so pn)

LDA FRIN+2,X           \ If the slot at FRIN+2+X is non-zero, then we have
\ something else in the vicinity besides asteroids,
\ escape pods and cargo canisters, so to check whether
\ we can jump, we first grab the slot contents into A

ORA JUNK               \ If there is any junk in the vicinity, then JUNK will
\ be non-zero, so OR'ing with JUNK will produce a
\ a non-zero result if either A or JUNK are non-zero
\ (so this prevents in-system jumps if there is any
\ junk nearby, which is different to the other versions
\ which allow you to jump, dragging any junk along with
\ the ship)

ORA SSPR               \ If there is a space station nearby, then SSPR will
\ be non-zero, so OR'ing with SSPR will produce a
\ a non-zero result if either A or SSPR are non-zero

ORA MJ                 \ If we are in witchspace, then MJ will be non-zero, so
\ OR'ing with MJ will produce a non-zero result if
\ either A or SSPR or MJ are non-zero

BNE WA1                \ A is non-zero if we have either a ship, or a space
\ station, or junk in the vicinity, or we are in
\ witchspace, in which case jump to WA1 to make a low
\ beep to show that we can't do an in-system jump

LDY K%+8               \ Otherwise we can do an in-system jump, so now we fetch
\ the byte at K%+8, which contains the z_sign for the
\ first ship slot, i.e. the distance of the planet

BMI WA3                \ If the planet's z_sign is negative, then the planet
\ is behind us, so jump to WA3 to skip the following

TAY                    \ Set A = Y = 0 (as we didn't BNE above) so the call
\ to MAS2 measures the distance to the planet

JSR MAS2               \ Call MAS2 to set A to the largest distance to the
\ planet in any of the three axes (we could also call
\ routine m to do the same thing, as A = 0)

LSR A                  \ If A < 2 then jump to WA1 to abort the in-system jump
BEQ WA1                \ with a low beep, as we are facing the planet and are
\ too close to jump in that direction

.WA3

LDY K%+NI%+8           \ Fetch the z_sign (byte #8) of the second ship in the
\ ship data workspace at K%, which is reserved for the
\ sun or the space station (in this case it's the
\ former, as we already confirmed there isn't a space
\ station in the vicinity)

BMI WA2                \ If the sun's z_sign is negative, then the sun is

LDY #NI%               \ Set Y to point to the offset of the ship data block
\ for the sun, which is NI% (as each block is NI% bytes
\ long, and the sun is the second block)

JSR m                  \ Call m to set A to the largest distance to the sun
\ in any of the three axes

LSR A                  \ If A < 2 then jump to WA1 to abort the in-system jump
BEQ WA1                \ with a low beep, as we are facing the sun and are too
\ close to jump in that direction

.WA2

\ If we get here, then we can do an in-system jump, as
\ we don't have any ships or space stations in the
\ vicinity, we are not in witchspace, and if we are
\ facing the planet or the sun, we aren't too close to
\
\ We do an in-system jump by moving the sun and planet,
\ rather than moving our own local bubble (this is why
\ in-system jumps drag asteroids, cargo canisters and
\ escape pods along for the ride). Specifically, we move
\ them in the z-axis by a fixed amount in the opposite
\ our destination

LDA #&81               \ Set R = R = P = &81
STA S
STA R
STA P

LDA K%+8               \ Set A = z_sign for the planet

JSR ADD                \ Set (A X) = (A P) + (S R)
\           = (z_sign &81) + &8181
\           = (z_sign &81) - &0181
\
\ This moves the planet against the direction of travel
\ by reducing z_sign by 1, as the above maths is:
\
\         z_sign 00000000
\   +   00000000 10000001
\   -   00000001 10000001
\
\ or:
\
\         z_sign 00000000
\   +   00000000 00000000
\   -   00000001 00000000
\
\ i.e. the high byte is z_sign - 1, making sure the sign
\ is preserved

STA K%+8               \ Set the planet's z_sign to the high byte of the result

LDA K%+NI%+8           \ Set A = z_sign for the sun

JSR ADD                \ Set (A X) = (A P) + (S R)
\           = (z_sign &81) + &8181
\           = (z_sign &81) - &0181
\
\ which moves the sun against the direction of travel
\ by reducing z_sign by 1

STA K%+NI%+8           \ Set the planet's z_sign to the high byte of the result

LDA #1                 \ These instructions have no effect, as the call to
STA QQ11               \ LOOK1 below starts by setting QQ11 to 0; instead they
\ just set the current view type in QQ11 to 1 for the
\ duration of the next three instructions

STA MCNT               \ Set the main loop counter to 1, so the next iteration
\ through the main loop will potentially spawn ships
\ (see part 2 of the main game loop at me3)

LSR A                  \ Set EV, the extra vessels spawning counter, to 0
STA EV                 \ (the LSR produces a 0 as A was previously 1)

LDX VIEW               \ Set X to the current view (front, rear, left or right)
JMP LOOK1              \ and jump to LOOK1 to initialise that view, returning
\ from the subroutine using a tail call

.WA1

LDA #40                \ If we get here then we can't do an in-system jump, so
JMP NOISE              \ call the NOISE routine with A = 40 to make a long, low
\ beep and return from the subroutine using a tail call

Type: Subroutine
Category: Sound
Summary: Make an explosion sound
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* DEATH calls EXNO3
* Main flight loop (Part 10 of 16) calls EXNO3
* OOPS calls EXNO3
* TACTICS (Part 1 of 7) calls EXNO3

Make the sound of death in the cold, hard vacuum of space. Apparently, in
Elite space, everyone can hear you scream.

This routine also makes the sound of a destroyed cargo canister if we don't
get scooping right, the sound of us colliding with another ship, and the sound
of us being hit with depleted shields. It is not a good sound to hear.

.EXNO3

JSR sound_10           \ Call sound_10 make the first death sound

LDA #24                \ Call the NOISE routine with A = 24 to make the
JMP NOISE              \ death sound and return from the subroutine using a
\ tail call

Type: Subroutine
Category: Tactics
Summary: Add an enemy missile to our local bubble of universe
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* TACTICS (Part 5 of 7) calls SFRMIS
* FRMIS calls entry point n_sound30
* LAUN calls entry point n_sound30

An enemy has fired a missile, so add the missile to our universe if there is
room, and if there is, make the appropriate warnings and noises.

Other entry points:

n_sound30            Make the sound of a missile being launched

.SFRMIS

LDX #MSL               \ Set X to the ship type of a missile, and call SFS1-2
JSR SFS1-2             \ to add the missile to our universe with an AI flag
\ of %11111110 (AI enabled, hostile, no E.C.M.)

BCC d_4418             \ The C flag will be set if the call to SFS1-2 was a
\ success, so if it's clear, jump down to d_4418 to
\ return from the subroutine

LDA #120               \ Print recursive token 120 ("INCOMING MISSILE") as an
JSR MESS               \ in-flight message

.n_sound30

LDA #48                \ Call the NOISE routine with A = 48 to make the sound
JMP NOISE              \ of the missile being launched and return from the
\ subroutine using a tail call

.d_4418

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Sound
Summary: Process us making a kill
Deep dive: Combat rank
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 11 of 16) calls EXNO2
* TACTICS (Part 1 of 7) calls EXNO2

We have killed a ship, so increase the kill tally, displaying an iconic
message of encouragement if the kill total is a multiple of 256, and then
make a nearby explosion sound.

.EXNO2

INC TALLY              \ Increment the low byte of the kill count in TALLY

BNE EXNO-2             \ If there is no carry, jump to the LDX #7 below (at
\ EXNO-2)

INC TALLY+1            \ Increment the high byte of the kill count in TALLY

LDA #101               \ The kill total is a multiple of 256, so it's time
JSR MESS               \ for a pat on the back, so print recursive token 101
\ ("RIGHT ON COMMANDER!") as an in-flight message

LDX #7                 \ Set X = 7 and fall through into EXNO to make the
\ sound of a ship exploding

Type: Subroutine
Category: Sound
Summary: Make the sound of a laser strike or ship explosion
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 11 of 16) calls EXNO
* EXNO3 calls entry point sound_10
* EXNO2 calls entry point EXNO-2

Make the two-part explosion sound of us making a laser strike, or of another
ship exploding.

The volume of the first explosion is affected by the distance of the ship
being hit, with more distant ships being quieter. The value in X also affects
the volume of the first explosion, with a higher X giving a quieter sound
(so X can be used to differentiate a laser strike from an explosion).

Arguments:

X                    The larger the value of X, the fainter the explosion.
Allowed values are:

* 7  = explosion is louder (i.e. the ship has just
exploded)

* 15 = explosion is quieter (i.e. this is just a laser
strike)

Other entry points:

EXNO-2               Set X = 7 and fall through into EXNO to make the sound
of a ship exploding

sound_10             Make the first part of the death sound, or the second
part of the explosion sound

.EXNO

STX T                  \ Store the distance in T

LDA #24                \ Set A = 24 to denote the sound of us making a hit or
JSR NOS1               \ kill (part 1 of the explosion), and call NOS1 to set
\ up the sound block in XX16

LDA INWK+7             \ Fetch z_hi, the distance of the ship being hit in
LSR A                  \ terms of the z-axis (in and out of the screen), and
LSR A                  \ divide by 4. If z_hi has either bit 6 or 7 set then
\ that ship is too far away to be shown on the scanner
\ (as per the SCAN routine), so we know the maximum
\ z_hi at this point is %00111111, and shifting z_hi
\ to the right twice gives us a maximum value of
\ %00001111

AND T                  \ This reduces A to a maximum of X; X can be either
\ 7 = %0111 or 15 = %1111, so AND'ing with 15 will
\ not affect A, while AND'ing with 7 will clear bit
\ 3, reducing the maximum value in A to 7

ORA #%11110001         \ The SOUND statement's amplitude ranges from 0 (for no
\ sound) to -15 (full volume), so we can set bits 0 and
\ 4-7 in A, and keep bits 1-3 from the above to get
\ a value between -15 (%11110001) and -1 (%11111111),
\ with lower values of z_hi and argument X leading
\ to a more negative, or quieter number (so the closer
\ the ship, i.e. the smaller the value of X, the louder
\ the sound)

STA XX16+2             \ The amplitude byte of the sound block in XX16 is in
\ byte #3 (where it's the low byte of the amplitude), so
\ this sets the amplitude to the value in A

JSR NO3                \ Make the sound from our updated sound block in XX16

.sound_10

LDA #16                \ Set A = 16 to denote we have made a hit or kill
\ (part 2 of the explosion)

JMP NOISE              \ Jump to NOISE to make the sound and return from the
\ subroutine using a tail call

Type: Subroutine
Category: Keyboard
Summary: Scan the keyboard or joystick for a flight key by sending a scan_y
command to the I/O processor
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* DK4_FLIGHT calls DKS1
* DKJ1 calls DKS1
* DOKEY_FLIGHT calls DKS1

Scan the keyboard for the flight key given in register Y, where Y is the
offset into the KYTB table above (so we can scan for Space by setting Y to
2, for example). If the key is pressed, set the corresponding byte in the
key logger at KL to a negative value (i.e. with bit 7 set).

Arguments:

Y                    The offset into the KYTB table above of the key that we
want to scan on the keyboard

.DKS1

LDA #&96               \ Send command &96 to the I/O processor:
JSR tube_write         \
\   =scan_y(key_offset, delta_14b)
\
\ which will update the roll or pitch dashboard
\ indicator to the specified value

TYA                    \ Send the first parameter to the I/O processor:
JSR tube_write         \
\   * key_offset = Y

LDA BSTK               \ Send the second parameter to the I/O processor:
JSR tube_write         \
\   * delta14b = BTSK

JSR tube_read          \ Set A to the response from the I/O processor, which
\ will be the key number, but with bit 7 clear if the
\ key is not being pressed, or bit 7 set if it is being
\ pressed

BPL b_quit             \ If the response is positive (i.e. bit 7 is clear) then
\ the key is not being pressed, so skip the following
\ instruction

STA KL,Y               \ The response is negative, so store this in the Y-th
\ byte of the key logger at KL to indicate that the key
\ is being pressed

.b_quit

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Keyboard
Summary: Read joystick and flight controls
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* DOKEY_FLIGHT calls DKJ1

Specifically, scan the keyboard for the speed up and slow down keys, and read
the joystick's fire button and X and Y axes, storing the results in the key
logger and the joystick position variables.

This routine is only called if joysticks are enabled (JSTK = non-zero).

.DKJ1

LDA auto               \ If auto is non-zero, then the docking computer is
BNE auton              \ currently activated, so jump to auton in DOKEY so the
\ docking computer can "press" the flight keys for us

LDY #1                 \ Update the key logger for key 1 in the KYTB table, so
JSR DKS1               \ KY1 will be &FF if "?" (slow down) is being pressed

INY                    \ Update the key logger for key 2 in the KYTB table, so
JSR DKS1               \ KY2 will be &FF if Space (speed up) is being pressed

JSR scan_fire          \ Call scan_fire to check whether the joystick's fire
\ button is being pressed, which clears bit 4 in A if
\ the fire button is being pressed, and sets it if it
\ is not being pressed

EOR #%00010000         \ Flip bit 4 so that it's set if the fire button has
STA KY7                \ been pressed, and store the result in the keyboard
\ logger at location KY7, which is also where the A key
\ (fire lasers) key is logged

LDX #1                 \ Call DKS2 to fetch the value of ADC channel 1 (the
JSR DKS2               \ joystick X value) into (A X), and OR A with 1. This
ORA #1                 \ ensures that the high byte is at least 1, and then we
STA JSTX               \ store the result in JSTX

LDX #2                 \ Call DKS2 to fetch the value of ADC channel 2 (the
JSR DKS2               \ joystick Y value) into (A X), and EOR A with JSTGY.
EOR JSTGY              \ JSTGY will be &FF if the game is configured to
STA JSTY               \ reverse the joystick Y channel, so this EOR does
\ exactly that, and then we store the result in JSTY

JMP DK4_FLIGHT         \ We are done scanning the joystick flight controls,
\ so jump to DK4_FLIGHT to scan for other keys, using a
\ tail call so we can return from the subroutine there

Type: Subroutine
Category: Keyboard
Summary: Clear the key logger
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* DEATH calls U%
* DOKEY_FLIGHT calls U%
* RES2 calls U%

Returns:

A                    A is set to 0

Y                    Y is set to 0

.U%

LDA #0                 \ Set A to 0, as this means "key not pressed" in the
\ key logger at KL

LDY #16                \ We want to clear the 16 key logger locations from
\ KY1 to KY20, so set a counter in Y

.DKL3

STA KL,Y               \ Store 0 in the Y-th byte of the key logger

DEY                    \ Decrement the counter

BNE DKL3               \ And loop back for the next key, until we have just
\ KL+1. We don't want to clear the first key logger
\ location at KL, as the keyboard table at KYTB starts
\ with offset 1, not 0, so KL is not technically part of
\ the key logger (it's actually used for logging keys
\ that don't appear in the keyboard table, and which
\ therefore don't use the key logger)

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Keyboard
Summary: Scan for the seven primary flight controls (flight version)
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 DOKEY_FLIGHT

.DOKEY_FLIGHT

JSR U%                 \ Call U% to clear the key logger

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

JMP DK4_FLIGHT         \ DK4_FLIGHT to skip scanning for primary flight keys,
\ and move on to scanning for pause, configuration and
\ secondary flight keys

.l_open

LDA JSTK               \ If JSTK is non-zero, then we are configured to use
BNE DKJ1               \ the joystick rather than keyboard, so jump to DKJ1
\ to read the joystick flight controls, before jumping
\ to DK4 to scan for pause, configuration and secondary
\ flight keys

LDY #7                 \ We're going to work our way through the primary flight
\ control keys (pitch, roll, speed and laser), so set a
\ counter in Y so we can loop through all 7

.DKL2

JSR DKS1               \ Call DKS1 to see if the KYTB key at offset Y is being
\ pressed, and set the key logger accordingly

DEY                    \ Decrement the loop counter

BNE DKL2               \ Loop back for the next key, working our way from A at
\ KYTB+7 down to ? at KYTB+1

LDA auto               \ If auto is 0, then the docking computer is not
BEQ DK15               \ currently activated, so jump to DK15 to skip the
\ docking computer manoeuvring code below

.auton

JSR ZINF               \ Call ZINF to reset the INWK ship workspace

LDA #96                \ Set nosev_z_hi = 96
STA INWK+14

ORA #%10000000         \ Set sidev_x_hi = -96
STA INWK+22

STA TYPE               \ Set the ship type to -96, so the negative value will
\ let us check in the DOCKIT routine whether this is our
\ ship that is activating its docking computer, rather
\ than an NPC ship docking

LDA DELTA              \ Set the ship speed to DELTA (our speed)
STA INWK+27

JSR DOCKIT             \ Call DOCKIT to calculate the docking computer's moves
\ and update INWK with the results

\ We now "press" the relevant flight keys, depending on
\ the results from DOCKIT, starting with the pitch keys

LDA INWK+27            \ Fetch the updated ship speed from byte #27 into A

CMP #22                \ If A < 22, skip the next instruction
BCC P%+4

LDA #22                \ Set A = 22, so the maximum speed during docking is 22

STA DELTA              \ Update DELTA to the new value in A

LDA #&FF               \ Set A = &FF, which we can insert into the key logger
\ to "fake" the docking computer working the keyboard

LDX #0                 \ Set X = 0, so we "press" KY1 below ("?", slow down)

LDY INWK+28            \ If the updated acceleration in byte #28 is zero, skip
BEQ DK11               \ to DK11

BMI P%+3               \ If the updated acceleration is negative, skip the
\ following instruction

INX                    \ The updated acceleration is positive, so increment X
\ to 1, so we "press" KY2 below (Space, speed up)

STA KY1,X              \ Store &FF in either KY1 or KY2 to "press" the relevant
\ key, depending on whether the updated acceleration is
\ negative (in which case we "press" KY1, "?", to slow
\ down) or positive (in which case we "press" KY2,
\ Space, to speed up)

.DK11

\ We now "press" the relevant roll keys, depending on
\ the results from DOCKIT

LDA #128               \ Set A = 128, which indicates no change in roll when
\ stored in JSTX (i.e. the centre of the roll indicator)

LDX #0                 \ Set X = 0, so we "press" KY3 below ("<", increase
\ roll)

ASL INWK+29            \ Shift ship byte #29 left, which shifts bit 7 of the
\ updated roll counter (i.e. the roll direction) into
\ the C flag

BEQ DK12               \ If the remains of byte #29 is zero, then the updated
\ roll counter is zero, so jump to DK12 set JSTX to 128,
\ to indicate there's no change in the roll

BCC P%+3               \ If the C flag is clear, skip the following instruction

INX                    \ The C flag is set, i.e. the direction of the updated
\ roll counter is negative, so increment X to 1 so we
\ "press" KY4 below (">", decrease roll)

BIT INWK+29            \ We shifted the updated roll counter to the left above,
BPL DK14               \ so this tests bit 6 of the original value, and if it
\ is is clear (i.e. the magnitude is less than 64), jump
\ to DK14 to "press" the key and leave JSTX unchanged

LDA #64                \ The magnitude of the updated roll is 64 or more, so
STA JSTX               \ set JSTX to 64 (so the roll decreases at half the
\ maximum rate)

LDA #0                 \ And set A = 0 so we do not "press" any keys (so if the
\ docking computer needs to make a serious roll, it does
\ so by setting JSTX directly rather than by "pressing"
\ a key)

.DK14

STA KY3,X              \ Store A in either KY3 or KY4, depending on whether
\ the updated roll rate is increasing (KY3) or
\ decreasing (KY4)

LDA JSTX               \ Fetch A from JSTX so the next instruction has no
\ effect

.DK12

STA JSTX               \ Store A in JSTX to update the current roll rate

\ We now "press" the relevant pitch keys, depending on
\ the results from DOCKIT

LDA #128               \ Set A = 128, which indicates no change in pitch when
\ stored in JSTX (i.e. the centre of the pitch
\ indicator)

LDX #0                 \ Set X = 0, so we "press" KY5 below ("X", decrease
\ pitch)

ASL INWK+30            \ Shift ship byte #30 left, which shifts bit 7 of the
\ updated pitch counter (i.e. the pitch direction) into
\ the C flag

BEQ DK13               \ If the remains of byte #30 is zero, then the updated
\ pitch counter is zero, so jump to DK13 set JSTY to
\ 128, to indicate there's no change in the pitch

BCS P%+3               \ If the C flag is set, skip the following instruction

INX                    \ The C flag is clear, i.e. the direction of the updated
\ pitch counter is positive, so increment X to 1 so we
\ "press" KY6 below ("S", increase pitch)

STA KY5,X              \ Store 128 in either KY5 or KY6 to "press" the relevant
\ key, depending on whether the pitch direction is
\ negative (in which case we "press" KY5, "X", to
\ decrease the pitch) or positive (in which case we
\ "press" KY6, "S", to increase the pitch)

LDA JSTY               \ Fetch A from JSTY so the next instruction has no
\ effect

.DK13

STA JSTY               \ Store A in JSTY to update the current pitch rate

.DK15

LDX JSTX               \ Set X = JSTX, the current roll rate (as shown in the
\ RL indicator on the dashboard)

LDA #7                 \ Set A to 7, which is the amount we want to alter the
\ roll rate by if the roll keys are being pressed

LDY KL+3               \ If the "<" key is being pressed, then call the BUMP2
BEQ P%+5               \ routine to increase the roll rate in X by A
JSR BUMP2

LDY KL+4               \ If the ">" key is being pressed, then call the REDU2
BEQ P%+5               \ routine to decrease the roll rate in X by A, taking
JSR REDU2              \ the keyboard auto re-centre setting into account

STX JSTX               \ Store the updated roll rate in JSTX

ASL A                  \ Double the value of A, to 14

LDX JSTY               \ Set X = JSTY, the current pitch rate (as shown in the
\ DC indicator on the dashboard)

LDY KL+5               \ If the "X" key is being pressed, then call the REDU2
BEQ P%+5               \ routine to decrease the pitch rate in X by A, taking
JSR REDU2              \ the keyboard auto re-centre setting into account

LDY KL+6               \ If the "S" key is being pressed, then call the BUMP2
BEQ P%+5               \ routine to increase the pitch rate in X by A
JSR BUMP2

STX JSTY               \ Store the updated roll rate in JSTY

\ Fall through into DK4_FLIGHT to scan for other keys

Type: Subroutine
Category: Keyboard
Summary: Scan for pause, configuration and secondary flight keys (flight
version)
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* DKJ1 calls DK4_FLIGHT
* DOKEY_FLIGHT calls DK4_FLIGHT

.DK4_FLIGHT

JSR RDKEY              \ Scan the keyboard for a key press and return the
\ internal key number in X (or 0 for no key press)

STX KL                 \ Store X in KL, byte #0 of the key logger

CPX #&69               \ If COPY is not being pressed, jump to DK2_FLIGHT
BNE DK2_FLIGHT         \ below, otherwise let's process the configuration
\ keys

.FREEZE_FLIGHT

\ COPY is being pressed, so we enter a loop that
\ listens for configuration keys, and we keep looping
\ until we detect a DELETE key press. This effectively
\ pauses the game when COPY is pressed, and unpauses
\ it when DELETE is pressed

JSR WSCAN              \ Call WSCAN to wait for the vertical sync, so the whole
\ screen gets drawn

JSR RDKEY              \ Scan the keyboard for a key press and return the
\ internal key number in X (or 0 for no key press)

CPX #&51               \ If "S" is not being pressed, skip to DK6_FLIGHT
BNE DK6_FLIGHT

LDA #0                 \ "S" is being pressed, so set DNOIZ to 0 to turn the
STA DNOIZ              \ sound on

.DK6_FLIGHT

LDY #&40               \ We now want to loop through the keys that toggle
\ various settings. These have internal key numbers
\ between &40 (CAPS LOCK) and &46 ("K"), so we set up
\ the first key number in Y to act as a loop counter.
\ See subroutine DKS3 for more details on this

.DKL4_FLIGHT

JSR DKS3               \ Call DKS3 to scan for the key given in Y, and toggle
\ the relevant setting if it is pressed

INY                    \ Increment Y to point to the next toggle key

CPY #&48               \ The last toggle key is &47 (@), so check whether we
\ have just done that one

BNE DKL4_FLIGHT        \ If not, loop back to check for the next toggle key

CPX #&10               \ If "Q" is not being pressed, skip to DK7_FLIGHT
BNE DK7_FLIGHT

STX DNOIZ              \ "Q" is being pressed, so set DNOIZ to X, which is
\ non-zero (&10), so this will turn the sound off

.DK7_FLIGHT

CPX #&70               \ If ESCAPE is not being pressed, skip over the next
BNE P%+5               \ instruction

JMP DEATH2             \ ESCAPE is being pressed, so jump to DEATH2 to end
\ the game

\CPX #&37               \ These instructions are commented out in the original
\BNE dont_dump          \ source
\JSR printer
\.dont_dump

CPX #&59               \ If DELETE is not being pressed, we are still paused,
BNE FREEZE_FLIGHT      \ so loop back up to keep listening for configuration
\ keys, otherwise fall through into the rest of the
\ key detection code, which unpauses the game

.DK2_FLIGHT

LDA QQ11               \ If the current view is non-zero (i.e. not a space
BNE DK5                \ view), return from the subroutine (as DK5 contains
\ an RTS)

LDY #16                \ This is a space view, so now we want to check for all
\ the secondary flight keys. The internal key numbers
\ are in the keyboard table KYTB from KYTB+8 to
\ KYTB+16, and their key logger locations are from KL+8
\ to KL+16. So set a decreasing counter in Y for the
\ index, starting at 16, so we can loop through them

.DKL1_FLIGHT

JSR DKS1               \ Call DKS1 to see if the KYTB key at offset Y is being
\ pressed, and set the key logger accordingly

DEY                    \ Decrement the loop counter

CPY #7                 \ Have we just done the last key?

BNE DKL1_FLIGHT        \ If not, loop back to process the next key

.DK5

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Text
Summary: Erase an old in-flight message and display a new one
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* MESS calls me1

Arguments:

A                    The text token to be printed

X                    Must be set to 0

.me1

STX DLY                \ Set the message delay in DLY to 0, so any new
\ in-flight messages will be shown instantly

PHA                    \ Store the new message token we want to print

LDA MCH                \ Set A to the token number of the message that is
JSR mes9               \ currently on-screen, and call mes9 to print it (which
\ will remove it from the screen, as printing is done
\ using EOR logic)

PLA                    \ Restore the new message token

EQUB &2C               \ Fall through into ou2 to print the new message, but
\ skip the first instruction by turning it into
\ &2C &A9 &6C, or BIT &6CA9, which does nothing apart
\ from affect the flags

Type: Subroutine
Category: Text
Summary: Print the name of a specific cargo item
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* OUCH calls cargo_mtok

Arguments:

A                    The number of the cargo item whose name we want to print
(where 0 = food, 1 = textiles, and so on up to 16 for
alien items)

See QQ23 for a list of market item numbers and their
storage units

.cargo_mtok

ADC #208               \ Add 208 to the value in A, so when we fall through
\ into MESS, we print recursive token 48 + A as an
\ in-flight token, which will be in the range 48
\ ("FOOD") to 64 ("ALIEN ITEMS")

Type: Subroutine
Category: Text
Summary: Display an in-flight message
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* EXNO2 calls MESS
* FR1 calls MESS
* Ghy calls MESS
* KILLSHP calls MESS
* Main flight loop (Part 12 of 16) calls MESS
* Main flight loop (Part 15 of 16) calls MESS
* Main flight loop (Part 8 of 16) calls MESS
* Main game loop for flight (Part 2 of 6) calls MESS
* OUCH calls MESS
* SFRMIS calls MESS
* me2 calls MESS

Display an in-flight message in capitals at the bottom of the space view,
erasing any existing in-flight message first.

Arguments:

A                    The text token to be printed

.MESS

LDX #0                 \ Set QQ17 = 0 to switch to ALL CAPS
STX QQ17

LDY #9                 \ Move the text cursor to column 9, row 22, at the
STY XC                 \ bottom middle of the screen, and set Y = 22
LDY #22
STY YC

CPX DLY                \ If the message delay in DLY is not zero, jump up to
BNE me1                \ me1 to erase the current message first (whose token
\ number will be in MCH)

STY DLY                \ Set the message delay in DLY to 22

STA MCH                \ Set MCH to the token we are about to display

\ Fall through into mes9 to print the token in A

Type: Subroutine
Category: Text
Summary: Print a text token, possibly followed by " DESTROYED"
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* me1 calls mes9

Print a text token, followed by " DESTROYED" if the destruction flag is set
(for when a piece of equipment is destroyed).

.mes9

JSR TT27               \ Call TT27 to print the text token in A

LSR de                 \ If bit 0 of variable de is clear, return from the
BCC DK5                \ subroutine (as DK5 contains an RTS)

LDA #253               \ Print recursive token 93 (" DESTROYED") and return
JMP TT27               \ from the subroutine using a tail call

Type: Subroutine
Category: Flight
Summary: Potentially lose cargo or equipment following damage
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* OOPS calls OUCH

Our shields are dead and we are taking damage, so there is a small chance of
losing cargo or equipment.

.OUCH

JSR DORND              \ Set A and X to random numbers

BMI DK5                \ If A < 0 (50% chance), return from the subroutine
\ (as DK5 contains an RTS)

CPX #24                \ If X >= 24 (90% chance), return from the subroutine
BCS DK5                \ (as DK5 contains an RTS)

LDA CRGO,X             \ If we do not have any of item CRGO+X, return from the
BEQ DK5                \ subroutine (as DK5 contains an RTS). X is in the range
\ 0-23, so this not only checks for cargo, but also for
\ the I.F.F. system, E.C.M. system, fuel scoops,
\ hyperspace unit, energy unit, docking computer and
\ galactic hyperdrive, all of which can be destroyed

LDA DLY                \ If there is already an in-flight message on-screen,
BNE DK5                \ return from the subroutine (as DK5 contains an RTS)

LDY #3                 \ Set bit 1 of de, the equipment destruction flag, so
STY de                 \ that when we call MESS below, " DESTROYED" is appended
\ to the in-flight message

STA CRGO,X             \ A is 0 (as we didn't branch with the BNE above), so
\ this sets CRGO+X to 0, which destroys any cargo or
\ equipment we have of that type

DEX                    \ Decrement X, so X is now in the range -1 to 22, and a
\ value of 0 means we just lost some food, 1 means we
\ lost some textiles, and so on

BMI ou1                \ If X is now negative, then we just lost the I.F.F.
\ system (as X was 0 before being decremented), so jump
\ to ou1 to print the relevant message, which will be
\ "I.F.F.SYSTEM DESTROYED" as A = 0 and the C flag is
\ clear (as we passed through the BCS above)

CPX #17                \ If X = 17 then we just lost the E.C.M., so jump to ou1
BEQ ou1                \ to print the relevant message, which will be
\ "E.C.M.SYSTEM DESTROYED" as A = 0 and the C flag is
\ set from the CPX

\ If we get here then X is in the range 0-16 or 18-22

TXA                    \ Copy the value of X into A

BCC cargo_mtok         \ If X < 17 then we just lost some cargo (as opposed to
\ the cargo whose number is in A, plus " DESTROYED", and
\ return from the subroutine using a tail call

\ If we get here then X (and A) are in the range 18-22

CMP #18                \ If A is not 18, jump down to equip_mtok with A in the
BNE equip_mtok         \ range 19-22 and the C flag set from the CMP, to print
\ token 113 ("HYPERSPACE UNIT") through 116 ("GALACTIC
\ HYPERSPACE")

LDA #111-107-1         \ Otherwise A is 18, so we have lost the fuel scoops, so
\ set A to 111-107-1 = 3 and the C flag set from the CMP
\ to print token 111 ("FUEL SCOOPS")

.ou1

ADC #107-93            \ We can reach here with three values of A and the C
\ flag, and then add 93 below to print the following
\ tokens:
\
\   A = 0, C flag clear = token 107 ("I.F.F.SYSTEM")
\   A = 0, C flag set   = token 108 ("E.C.M.SYSTEM")
\   A = 3, C flag set   = token 111 ("FUEL SCOOPS")

.equip_mtok

ADC #93                \ We can either reach here from above, or jump straight
\ here with A = 19-22 and the C flag set, in which case
\ adding 93 will give us token 113 ("HYPERSPACE UNIT")
\ through 116 ("GALACTIC HYPERSPACE ")

INC new_hold           \ We just lost a piece of equipment, so increment the
\ amount of free space in the hold

BNE MESS               \ Print recursive token A as an in-flight message,
\ followed by " DESTROYED", and return from the
\ subroutine using a tail call

Type: Subroutine
Category: Drawing ships
Summary: Draw a ship (flight version)
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* ESCAPE calls LL9_FLIGHT
* Main flight loop (Part 12 of 16) calls LL9_FLIGHT

.LL25

\ subroutine using a tail call

.LL9_FLIGHT

LDA TYPE               \ If the ship type is negative then this indicates a
BMI LL25               \ planet or sun, so jump to PLANET via LL25 above

Type: Subroutine
Category: Moving
Summary: Move current ship (flight version)
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* ESCAPE calls MVEIT_FLIGHT
* Main flight loop (Part 6 of 16) calls MVEIT_FLIGHT

.MVEIT_FLIGHT

LDA INWK+31            \ If bit 5 or 7 of ship byte #31 is set, jump to MV30
AND #%10100000         \ as the ship is either exploding or has been killed, so
BNE MV30               \ we don't need to tidy its orientation vectors or apply
\ tactics

LDA MCNT               \ Fetch the main loop counter

EOR XSAV               \ Fetch the slot number of the ship we are moving, EOR
AND #15                \ with the loop counter and apply mod 15 to the result.
BNE P%+5               \ The result will be zero when "counter mod 15" matches
\ the slot number, so this makes sure we call TIDY 12
\ times every 16 main loop iterations, like this:
\
\   Iteration 0, tidy the ship in slot 0
\   Iteration 1, tidy the ship in slot 1
\   Iteration 2, tidy the ship in slot 2
\     ...
\   Iteration 11, tidy the ship in slot 11
\   Iteration 12, do nothing
\   Iteration 13, do nothing
\   Iteration 14, do nothing
\   Iteration 15, do nothing
\   Iteration 16, tidy the ship in slot 0
\     ...
\
\ and so on

JSR TIDY               \ Call TIDY to tidy up the orientation vectors, to
\ prevent the ship from getting elongated and out of
\ shape due to the imprecise nature of trigonometry
\ in assembly language

Type: Subroutine
Category: Moving
Summary: Move current ship: Call tactics routine, remove ship from scanner
Deep dive: Scheduling tasks with the main loop counter
Context: See this subroutine on its own page
References: No direct references to this subroutine in this source file

This routine has multiple stages. This stage does the following:

* Apply tactics to ships with AI enabled (by calling the TACTICS routine)

* Remove the ship from the scanner, so we can move it

LDX TYPE               \ If the type of the ship we are moving is positive,
BPL P%+5               \ i.e. it is not a planet (types 128 and 130) or sun
\ (type 129), then skip the following instruction

JMP MV40               \ This item is the planet or sun, so jump to MV40 to
\ move it, which ends by jumping back into this routine
\ at MV45 (after all the rotation, tactics and scanner
\ code, which we don't need to apply to planets or suns)

LDA INWK+32            \ Fetch the ship's byte #32 (AI flag) into A

BPL MV30               \ If bit 7 of the AI flag is clear, then if this is a
\ ship or missile it is dumb and has no AI, and if this
\ is the space station it is not hostile, so in both
\ cases skip the following as it has no tactics

CPX #MSL               \ If the ship is a missile, skip straight to MV26 to
BEQ MV26               \ call the TACTICS routine, as we do this every
\ iteration of the main loop for missiles only

LDA MCNT               \ Fetch the main loop counter

EOR XSAV               \ Fetch the slot number of the ship we are moving, EOR
AND #7                 \ with the loop counter and apply mod 8 to the result.
BNE MV30               \ The result will be zero when "counter mod 8" matches
\ the slot number mod 8, so this makes sure we call
\ TACTICS 12 times every 8 main loop iterations, like
\ this:
\
\   Iteration 0, apply tactics to slots 0 and 8
\   Iteration 1, apply tactics to slots 1 and 9
\   Iteration 2, apply tactics to slots 2 and 10
\   Iteration 3, apply tactics to slots 3 and 11
\   Iteration 4, apply tactics to slot 4
\   Iteration 5, apply tactics to slot 5
\   Iteration 6, apply tactics to slot 6
\   Iteration 7, apply tactics to slot 7
\   Iteration 8, apply tactics to slots 0 and 8
\     ...
\
\ and so on

.MV26

JSR TACTICS            \ Call TACTICS to apply AI tactics to this ship

.MV30

JSR SCAN               \ Draw the ship on the scanner, which has the effect of
\ removing it, as it's already at this point and hasn't
\ yet moved

Type: Subroutine
Category: Moving
Summary: Move current ship: Move ship forward according to its speed
Context: See this subroutine on its own page
References: No direct references to this subroutine in this source file

This routine has multiple stages. This stage does the following:

* Move the ship forward (along the vector pointing in the direction of
travel) according to its speed:

(x, y, z) += nosev_hi * speed / 64

LDA INWK+27            \ Set Q = the ship's speed byte #27 * 4
ASL A
ASL A
STA Q

LDA INWK+10            \ Set A = |nosev_x_hi|
AND #%01111111

JSR FMLTU              \ Set R = A * Q / 256
STA R                  \       = |nosev_x_hi| * speed / 64

LDA INWK+10            \ If nosev_x_hi is positive, then:
LDX #0                 \
JSR MVT1-2             \   (x_sign x_hi x_lo) = (x_sign x_hi x_lo) + R
\
\ If nosev_x_hi is negative, then:
\
\   (x_sign x_hi x_lo) = (x_sign x_hi x_lo) - R
\
\ So in effect, this does:
\
\   (x_sign x_hi x_lo) += nosev_x_hi * speed / 64

LDA INWK+12            \ Set A = |nosev_y_hi|
AND #%01111111

JSR FMLTU              \ Set R = A * Q / 256
STA R                  \       = |nosev_y_hi| * speed / 64

LDA INWK+12            \ If nosev_y_hi is positive, then:
LDX #3                 \
JSR MVT1-2             \   (y_sign y_hi y_lo) = (y_sign y_hi y_lo) + R
\
\ If nosev_y_hi is negative, then:
\
\   (y_sign y_hi y_lo) = (y_sign y_hi y_lo) - R
\
\ So in effect, this does:
\
\   (y_sign y_hi y_lo) += nosev_y_hi * speed / 64

LDA INWK+14            \ Set A = |nosev_z_hi|
AND #%01111111

JSR FMLTU              \ Set R = A * Q / 256
STA R                  \       = |nosev_z_hi| * speed / 64

LDA INWK+14            \ If nosev_y_hi is positive, then:
LDX #6                 \
JSR MVT1-2             \   (z_sign z_hi z_lo) = (z_sign z_hi z_lo) + R
\
\ If nosev_z_hi is negative, then:
\
\   (z_sign z_hi z_lo) = (z_sign z_hi z_lo) - R
\
\ So in effect, this does:
\
\   (z_sign z_hi z_lo) += nosev_z_hi * speed / 64

Type: Subroutine
Category: Moving
Summary: Move current ship: Apply acceleration to ship's speed as a one-off
Context: See this subroutine on its own page
References: No direct references to this subroutine in this source file

This routine has multiple stages. This stage does the following:

* Apply acceleration to the ship's speed (if acceleration is non-zero),
and then zero the acceleration as it's a one-off change

LDA INWK+27            \ Set A = the ship's speed in byte #24 + the ship's
CLC                    \ acceleration in byte #28

BPL P%+4               \ If the result is positive, skip the following
\ instruction

LDA #0                 \ Set A to 0 to stop the speed from going negative

LDY #15                \ Fetch byte #15 from the ship's blueprint, which
\ contains the ship's maximum speed

CMP (XX0),Y            \ If A < the ship's maximum speed, skip the following
BCC P%+4               \ instruction

LDA (XX0),Y            \ Set A to the ship's maximum speed

STA INWK+27            \ We have now calculated the new ship's speed after
\ accelerating and keeping the speed within the ship's
\ limits, so store the updated speed in byte #27

LDA #0                 \ We have added the ship's acceleration, so we now set
STA INWK+28            \ it back to 0 in byte #28, as it's a one-off change

Type: Subroutine
Category: Moving
Summary: Move current ship: Rotate ship's location by our pitch and roll
Deep dive: Rotating the universe
Context: See this subroutine on its own page
References: No direct references to this subroutine in this source file

This routine has multiple stages. This stage does the following:

* Rotate the ship's location in space by the amount of pitch and roll of
our ship. See below for a deeper explanation of this routine

LDX ALP1               \ Fetch the magnitude of the current roll into X, so
\ if the roll angle is alpha, X contains |alpha|

LDA INWK               \ Set P = ~x_lo (i.e. with all its bits flipped) so that
EOR #%11111111         \ we can pass x_lo to MLTU2 below)
STA P

LDA INWK+1             \ Set A = x_hi

JSR MLTU2-2            \ Set (A P+1 P) = (A ~P) * X
\               = (x_hi x_lo) * alpha

STA P+2                \ Store the high byte of the result in P+2, so we now
\ have:
\
\ P(2 1 0) = (x_hi x_lo) * alpha

LDA ALP2+1             \ Fetch the flipped sign of the current roll angle alpha
EOR INWK+2             \ from ALP2+1 and EOR with byte #2 (x_sign), so if the
\ flipped roll angle and x_sign have the same sign, A
\ will be positive, else it will be negative. So A will
\ contain the sign bit of x_sign * flipped alpha sign,
\ which is the opposite to the sign of the above result,
\ so we now have:
\
\ (A P+2 P+1) = - (x_sign x_hi x_lo) * alpha / 256

LDX #3                 \ Set (A P+2 P+1) = (y_sign y_hi y_lo) + (A P+2 P+1)
JSR MVT6               \                 = y - x * alpha / 256

STA K2+3               \ Set K2(3) = A = the sign of the result

LDA P+1                \ Set K2(1) = P+1, the low byte of the result
STA K2+1

EOR #%11111111         \ Set P = ~K2+1 (i.e. with all its bits flipped) so
STA P                  \ that we can pass K2+1 to MLTU2 below)

LDA P+2                \ Set K2(2) = A = P+2
STA K2+2

\ So we now have result 1 above:
\
\ K2(3 2 1) = (A P+2 P+1)
\           = y - x * alpha / 256

LDX BET1               \ Fetch the magnitude of the current pitch into X, so
\ if the pitch angle is beta, X contains |beta|

JSR MLTU2-2            \ Set (A P+1 P) = (A ~P) * X
\               = K2(2 1) * beta

STA P+2                \ Store the high byte of the result in P+2, so we now
\ have:
\
\ P(2 1 0) = K2(2 1) * beta

LDA K2+3               \ Fetch the sign of the above result in K(3 2 1) from
EOR BET2               \ K2+3 and EOR with BET2, the sign of the current pitch
\ rate, so if the pitch and K(3 2 1) have the same sign,
\ A will be positive, else it will be negative. So A
\ will contain the sign bit of K(3 2 1) * beta, which is
\ the same as the sign of the above result, so we now
\ have:
\
\ (A P+2 P+1) = K2(3 2 1) * beta / 256

LDX #6                 \ Set (A P+2 P+1) = (z_sign z_hi z_lo) + (A P+2 P+1)
JSR MVT6               \                 = z + K2 * beta / 256

STA INWK+8             \ Set z_sign = A = the sign of the result

LDA P+1                \ Set z_lo = P+1, the low byte of the result
STA INWK+6

EOR #%11111111         \ Set P = ~z_lo (i.e. with all its bits flipped) so that
STA P                  \ we can pass z_lo to MLTU2 below)

LDA P+2                \ Set z_hi = P+2
STA INWK+7

\ So we now have result 2 above:
\
\ (z_sign z_hi z_lo) = (A P+2 P+1)
\                    = z + K2 * beta / 256

JSR MLTU2              \ MLTU2 doesn't change Q, and Q was set to beta in
\ the previous call to MLTU2, so this call does:
\
\ (A P+1 P) = (A ~P) * Q
\           = (z_hi z_lo) * beta

STA P+2                \ Set P+2 = A = the high byte of the result, so we
\ now have:
\
\ P(2 1 0) = (z_hi z_lo) * beta

LDA K2+3               \ Set y_sign = K2+3
STA INWK+5

EOR BET2               \ EOR y_sign with BET2, the sign of the current pitch
EOR INWK+8             \ rate, and z_sign. If the result is positive jump to
BPL MV43               \ MV43, otherwise this means beta * z and y have
\ different signs, i.e. P(2 1) and K2(3 2 1) have
\ different signs, so we need to add them in order to
\ calculate K2(2 1) - P(2 1)

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

.MV43

LDA K2+1               \ Reversing the logic above, we need to subtract P(2 1)
SBC P+1                \ and K2(3 2 1) to calculate K2(2 1) - P(2 1), so this
STA INWK+3             \ sets (y_hi y_lo) = K2(2 1) - P(2 1)
LDA K2+2
SBC P+2
STA INWK+4

BCS MV44               \ If the above subtraction did not underflow, then

LDA #1                 \ Negate (y_sign y_hi y_lo) using two's complement,
SBC INWK+3             \ first doing the low bytes:
STA INWK+3             \
\ y_lo = 1 - y_lo

LDA #0                 \ Then the high bytes:
SBC INWK+4             \
STA INWK+4             \ y_hi = 0 - y_hi

EOR #%10000000
STA INWK+5

.MV44

\ So we now have result 3 above:
\
\ (y_sign y_hi y_lo) = K2(2 1) - P(2 1)
\                    = K2 - beta * z

LDX ALP1               \ Fetch the magnitude of the current roll into X, so
\ if the roll angle is alpha, X contains |alpha|

LDA INWK+3             \ Set P = ~y_lo (i.e. with all its bits flipped) so that
EOR #&FF               \ we can pass y_lo to MLTU2 below)
STA P

LDA INWK+4             \ Set A = y_hi

JSR MLTU2-2            \ Set (A P+1 P) = (A ~P) * X
\               = (y_hi y_lo) * alpha

STA P+2                \ Store the high byte of the result in P+2, so we now
\ have:
\
\ P(2 1 0) = (y_hi y_lo) * alpha

LDA ALP2               \ Fetch the correct sign of the current roll angle alpha
EOR INWK+5             \ from ALP2 and EOR with byte #5 (y_sign), so if the
\ correct roll angle and y_sign have the same sign, A
\ will be positive, else it will be negative. So A will
\ contain the sign bit of x_sign * correct alpha sign,
\ which is the same as the sign of the above result,
\ so we now have:
\
\ (A P+2 P+1) = (y_sign y_hi y_lo) * alpha / 256

LDX #0                 \ Set (A P+2 P+1) = (x_sign x_hi x_lo) + (A P+2 P+1)
JSR MVT6               \                 = x + y * alpha / 256

STA INWK+2             \ Set x_sign = A = the sign of the result

LDA P+2                \ Set x_hi = P+2, the high byte of the result
STA INWK+1

LDA P+1                \ Set x_lo = P+1, the low byte of the result
STA INWK

\ So we now have result 4 above:
\
\ x = x + alpha * y
\
\ and the rotation of (x, y, z) is done

Type: Subroutine
Category: Moving
Summary: Move current ship: Move the ship in space according to our speed
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* MV40 calls entry point MV45

This routine has multiple stages. This stage does the following:

* Move the ship in space according to our speed (we already moved it
according to its own speed in part 3).

We do this by subtracting our speed (i.e. the distance we travel in this
iteration of the loop) from the other ship's z-coordinate. We subtract because
they appear to be "moving" in the opposite direction to us, and the whole
MVEIT routine is about moving the other ships rather than us (even though we
are the one doing the moving).

Other entry points:

MV45                 Rejoin the MVEIT routine after the rotation, tactics and
scanner code

.MV45

LDA DELTA              \ Set R to our speed in DELTA
STA R

LDA #%10000000         \ Set A to zeroes but with bit 7 set, so that (A R) is
\ a 16-bit number containing -R, or -speed

LDX #6                 \ Set X to the z-axis so the call to MVT1 does this:
JSR MVT1               \
\ (z_sign z_hi z_lo) = (z_sign z_hi z_lo) + (A R)
\                    = (z_sign z_hi z_lo) - speed

LDA TYPE               \ If the ship type is the sun (129) then skip the next
AND #%10000001         \ next instruction, otherwise return from the subroutine
CMP #129               \ as we don't need to rotate the sun around its origin.
BEQ P%+5               \ Having both the AND and the CMP is a little odd, as
\ the sun is the only ship type with bits 0 and 7 set,
\ so the AND has no effect and could be removed

JMP MV3                \ The ship type is not the sun, so jump to MV3, skipping
\ the next instruction

RTS                    \ Return from the subroutine, as the ship we are moving
\ is the sun and doesn't need any of the following

Type: Subroutine
Category: Moving
Summary: Calculate (x_sign x_hi x_lo) = (x_sign x_hi x_lo) + (A R)
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* MVEIT_FLIGHT (Part 6 of 6) calls MVT1
* SFS2 calls MVT1
* MVEIT_FLIGHT (Part 3 of 6) calls entry point MVT1-2

Add the signed delta (A R) to a ship's coordinate, along the axis given in X.
Mathematically speaking, this routine translates the ship along a single axis
by a signed delta. Taking the example of X = 0, the x-axis, it does the
following:

(x_sign x_hi x_lo) = (x_sign x_hi x_lo) + (A R)

(In practice, MVT1 is only ever called directly with A = 0 or 128, otherwise
it is always called via MVT-2, which clears A apart from the sign bit. The
routine is written to cope with a non-zero delta_hi, so it supports a full
16-bit delta, but it appears that delta_hi is only ever used to hold the
sign of the delta.)

The comments below assume we are adding delta to the x-axis, though the axis
is determined by the value of X.

Arguments:

(A R)                The signed delta, so A = delta_hi and R = delta_lo

X                    Determines which coordinate axis of INWK to change:

* X = 0 adds the delta to (x_lo, x_hi, x_sign)

* X = 3 adds the delta to (y_lo, y_hi, y_sign)

* X = 6 adds the delta to (z_lo, z_hi, z_sign)

Other entry points:

MVT1-2               Clear bits 0-6 of A before entering MVT1

AND #%10000000         \ Clear bits 0-6 of A

.MVT1

ASL A                  \ Set the C flag to the sign bit of the delta, leaving
\ delta_hi << 1 in A

STA S                  \ Set S = delta_hi << 1
\
\ This also clears bit 0 of S

LDA #0                 \ Set T = just the sign bit of delta (in bit 7)
ROR A
STA T

LSR S                  \ Set S = delta_hi >> 1
\       = |delta_hi|
\
\ This also clear the C flag, as we know that bit 0 of
\ S was clear before the LSR

EOR INWK+2,X           \ If T EOR x_sign has bit 7 set, then x_sign and delta

\ At this point, we know x_sign and delta have the same
\ sign, that sign is in T, and S contains |delta_hi|,
\ so now we want to do:
\
\   (x_sign x_hi x_lo) = (x_sign x_hi x_lo) + (S R)
\
\ and then set the sign of the result to the same sign
\ as x_sign and delta

LDA R                  \ First we add the low bytes, so:
STA INWK,X             \   x_lo = x_lo + R

LDA S                  \ Then we add the high bytes:
STA INWK+1,X           \   x_hi = x_hi + S

LDA INWK+2,X           \ And finally we add any carry into x_sign, and if the
ADC #0                 \ sign of x_sign and delta in T is negative, make sure
ORA T                  \ the result is negative (by OR'ing with T)
STA INWK+2,X

RTS                    \ Return from the subroutine

.MV10

\ If we get here, we know x_sign and delta have
\ |delta_hi| in S, so now we want to do:
\
\   (x_sign x_hi x_lo) = (x_sign x_hi x_lo) - (S R)
\
\ and then set the sign of the result according to
\ the signs of x_sign and delta

LDA INWK,X             \ First we subtract the low bytes, so:
SEC                    \
SBC R                  \   x_lo = x_lo - R
STA INWK,X

LDA INWK+1,X           \ Then we subtract the high bytes:
SBC S                  \
STA INWK+1,X           \   x_hi = x_hi - S

LDA INWK+2,X           \ And finally we subtract any borrow from bits 0-6 of
AND #%01111111         \ x_sign, and give the result the opposite sign bit to T
SBC #0                 \ (i.e. give it the sign of the original x_sign)
ORA #%10000000
EOR T
STA INWK+2,X

BCS MV11               \ If the C flag is set by the above SBC, then our sum
\ above didn't underflow and is correct - to put it
\ another way, (x_sign x_hi x_lo) >= (S R) so the result
\ should indeed have the same sign as x_sign, so jump to
\ MV11 to return from the subroutine

\ Otherwise our subtraction underflowed because
\ (x_sign x_hi x_lo) < (S R), so we now need to flip the
\ subtraction around by using two's complement to this:
\
\   (S R) - (x_sign x_hi x_lo)
\
\ and then we need to give the result the same sign as
\ (S R), the delta, as that's the dominant figure in the
\ sum

LDA #1                 \ First we subtract the low bytes, so:
SBC INWK,X             \
STA INWK,X             \   x_lo = 1 - x_lo

LDA #0                 \ Then we subtract the high bytes:
SBC INWK+1,X           \
STA INWK+1,X           \   x_hi = 0 - x_hi

LDA #0                 \ And then we subtract the sign bytes:
SBC INWK+2,X           \
\   x_sign = 0 - x_sign

AND #%01111111         \ Finally, we set the sign bit to the sign in T, the
ORA T                  \ sign of the original delta, as the delta is the
STA INWK+2,X           \ dominant figure in the sum

.MV11

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Moving
Summary: Calculate (A P+2 P+1) = (x_sign x_hi x_lo) + (A P+2 P+1)
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* MVEIT_FLIGHT (Part 5 of 6) calls MVT6

Do the following calculation, for the coordinate given by X (so this is what
it does for the x-coordinate):

(A P+2 P+1) = (x_sign x_hi x_lo) + (A P+2 P+1)

A is a sign bit and is not included in the calculation, but bits 0-6 of A are
preserved. Bit 7 is set to the sign of the result.

Arguments:

A                    The sign of P(2 1) in bit 7

P(2 1)               The 16-bit value we want to add the coordinate to

X                    The coordinate to add, as follows:

* If X = 0, add to (x_sign x_hi x_lo)

* If X = 3, add to (y_sign y_hi y_lo)

* If X = 6, add to (z_sign z_hi z_lo)

Returns:

A                    The sign of the result (in bit 7)

.MVT6

TAY                    \ Store argument A into Y, for later use

EOR INWK+2,X           \ Set A = A EOR x_sign

BMI MV50               \ If the sign is negative, i.e. A and x_sign have

\ The signs are the same, so we can add the two
\ arguments and keep the sign to get the result

LDA P+1                \ First we add the low bytes:
CLC                    \
ADC INWK,X             \   P+1 = P+1 + x_lo
STA P+1

LDA P+2                \ And then the high bytes:
STA P+2                \   P+2 = P+2 + x_hi

TYA                    \ Restore the original A argument that we stored earlier
\ so that we keep the original sign

RTS                    \ Return from the subroutine

.MV50

LDA INWK,X             \ First we subtract the low bytes:
SEC                    \
SBC P+1                \   P+1 = x_lo - P+1
STA P+1

LDA INWK+1,X           \ And then the high bytes:
SBC P+2                \
STA P+2                \   P+2 = x_hi - P+2

BCC MV51               \ If the last subtraction underflowed, then the C flag
\ will be clear and x_hi < P+2, so jump to MV51 to
\ negate the result

TYA                    \ Restore the original A argument that we stored earlier
EOR #%10000000         \ but flip bit 7, which flips the sign. We do this
\ because x_hi >= P+2 so we want the result to have the
\ same sign as x_hi (as it's the dominant side in this
\ calculation). The sign of x_hi is x_sign, and x_sign
\ has the opposite sign to A, so we flip the sign in A
\ to return the correct result

RTS                    \ Return from the subroutine

.MV51

LDA #1                 \ Our subtraction underflowed, so we negate the result
SBC P+1                \ using two's complement, first with the low byte:
STA P+1                \
\   P+1 = 1 - P+1

LDA #0                 \ And then the high byte:
SBC P+2                \
STA P+2                \   P+2 = 0 - P+2

TYA                    \ Restore the original A argument that we stored earlier
\ as this is the correct sign for the result. This is
\ because x_hi < P+2, so we want to return the same sign
\ as P+2, the dominant side

RTS                    \ Return from the subroutine

Type: Subroutine
Category: Moving
Summary: Rotate the planet or sun's location in space by the amount of
pitch and roll of our ship
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* MVEIT_FLIGHT (Part 2 of 6) calls MV40

We implement this using the same equations as in part 5 of MVEIT, where we
rotated the current ship's location by our pitch and roll. Specifically, the
calculation is as follows:

1. K2 = y - alpha * x
2. z = z + beta * K2
3. y = K2 - beta * z
4. x = x + alpha * y

See the deep dive on "Rotating the universe" for more details on the above.

.MV40

LDA ALPHA              \ Set Q = -ALPHA, so Q contains the angle we want to
EOR #%10000000         \ roll the planet through (i.e. in the opposite
STA Q                  \ direction to our ship's roll angle alpha)

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

LDA INWK+2             \ Set A = x_sign

JSR MULT3              \ Set K(3 2 1 0) = (A P+1 P) * Q
\
\ which also means:
\
\   K(3 2 1) = (A P+1 P) * Q / 256
\            = x * -alpha / 256
\            = - alpha * x / 256

LDX #3                 \ Set K(3 2 1) = (y_sign y_hi y_lo) + K(3 2 1)
JSR MVT3               \              = y - alpha * x / 256

LDA K+1                \ Set K2(2 1) = P(1 0) = K(2 1)
STA K2+1
STA P

LDA K+2                \ Set K2+2 = K+2
STA K2+2

STA P+1                \ Set P+1 = K+2

LDA BETA               \ Set Q = beta, the pitch angle of our ship
STA Q

LDA K+3                \ Set K+3 to K2+3, so now we have result 1 above:
STA K2+3               \
\   K2(3 2 1) = K(3 2 1)
\             = y - alpha * x / 256

\ We also have:
\
\   A = K+3
\
\   P(1 0) = K(2 1)
\
\ so combined, these mean:
\
\   (A P+1 P) = K(3 2 1)
\             = K2(3 2 1)

JSR MULT3              \ Set K(3 2 1 0) = (A P+1 P) * Q
\
\ which also means:
\
\   K(3 2 1) = (A P+1 P) * Q / 256
\            = K2(3 2 1) * beta / 256
\            = beta * K2 / 256

LDX #6                 \ K(3 2 1) = (z_sign z_hi z_lo) + K(3 2 1)
JSR MVT3               \          = z + beta * K2 / 256

LDA K+1                \ Set P = K+1
STA P

STA INWK+6             \ Set z_lo = K+1

LDA K+2                \ Set P+1 = K+2
STA P+1

STA INWK+7             \ Set z_hi = K+2

LDA K+3                \ Set A = z_sign = K+3, so now we have:
STA INWK+8             \
\   (z_sign z_hi z_lo) = K(3 2 1)
\                      = z + beta * K2 / 256

\ So we now have result 2 above:
\
\   z = z + beta * K2

EOR #%10000000         \ Flip the sign bit of A to give A = -z_sign

JSR MULT3              \ Set K(3 2 1 0) = (A P+1 P) * Q
\                = (-z_sign z_hi z_lo) * beta
\                = -z * beta

LDA K+3                \ Set T to the sign bit of K(3 2 1 0), i.e. to the sign
AND #%10000000         \ bit of -z * beta
STA T

EOR K2+3               \ If K2(3 2 1 0) has a different sign to K(3 2 1 0),
BMI MV1                \ then EOR'ing them will produce a 1 in bit 7, so jump
\ to MV1 to take this into account

\ If we get here, K and K2 have the same sign, so we can
\ add them together to get the result we're after, and
\ then set the sign afterwards

LDA K                  \ We now do the following sum:
CLC                    \
ADC K2                 \   (A y_hi y_lo -) = K(3 2 1 0) + K2(3 2 1 0)
\
\ starting with the low bytes (which we don't keep)
\
\ The CLC has no effect because MULT3 clears the C
\ flag, so this instruction could be removed (as it is
\ in the cassette version, for example)

LDA K+1                \ We then do the middle bytes, which go into y_lo
STA INWK+3

LDA K+2                \ And then the high bytes, which go into y_hi
STA INWK+4

LDA K+3                \ And then the sign bytes into A, so overall we have the
ADC K2+3               \ following, if we drop the low bytes from the result:
\
\   (A y_hi y_lo) = (K + K2) / 256

JMP MV2                \ Jump to MV2 to skip the calculation for when K and K2
\ have different signs

.MV1

LDA K                  \ If we get here then K2 and K have different signs, so
SEC                    \ instead of adding, we need to subtract to get the
SBC K2                 \ result we want, like this:
\
\   (A y_hi y_lo -) = K(3 2 1 0) - K2(3 2 1 0)
\
\ starting with the low bytes (which we don't keep)

LDA K+1                \ We then do the middle bytes, which go into y_lo
SBC K2+1
STA INWK+3

LDA K+2                \ And then the high bytes, which go into y_hi
SBC K2+2
STA INWK+4

LDA K2+3               \ Now for the sign bytes, so first we extract the sign
AND #%01111111         \ byte from K2 without the sign bit, so P = |K2+3|
STA P

LDA K+3                \ And then we extract the sign byte from K without the
AND #%01111111         \ sign bit, so A = |K+3|

SBC P                  \ And finally we subtract the sign bytes, so P = A - P
STA P

\ By now we have the following, if we drop the low bytes
\ from the result:
\
\   (A y_hi y_lo) = (K - K2) / 256
\
\ so now we just need to make sure the sign of the
\ result is correct

BCS MV2                \ If the C flag is set, then the last subtraction above
\ MV2 as we are done with this particular stage

LDA #1                 \ Otherwise the subtraction above underflowed, as K2 is
SBC INWK+3             \ the dominant part of the subtraction, so we need to
STA INWK+3             \ negate the result using two's complement, starting
\ with the low bytes:
\
\   y_lo = 1 - y_lo

LDA #0                 \ And then the high bytes:
SBC INWK+4             \
STA INWK+4             \   y_hi = 0 - y_hi

LDA #0                 \ And finally the sign bytes:
SBC P                  \
\   A = 0 - P

ORA #%10000000         \ We now force the sign bit to be negative, so that the
\ final result below gets the opposite sign to K, which
\ we want as K2 is the dominant part of the sum

.MV2

EOR T                  \ T contains the sign bit of K, so if K is negative,
\ this flips the sign of A

STA INWK+5             \ Store A in y_sign

\ So we now have result 3 above:
\
\   y = K2 + K
\     = K2 - beta * z

LDA ALPHA              \ Set A = alpha
STA Q

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

JSR MULT3              \ Set K(3 2 1 0) = (A P+1 P) * Q
\                = (y_sign y_hi y_lo) * alpha
\                = y * alpha

LDX #0                 \ Set K(3 2 1) = (x_sign x_hi x_lo) + K(3 2 1)
JSR MVT3               \              = x + y * alpha / 256

LDA K+1                \ Set (x_sign x_hi x_lo) = K(3 2 1)
STA INWK               \                        = x + y * alpha / 256
LDA K+2
STA INWK+1
LDA K+3
STA INWK+2

\ So we now have result 4 above:
\
\   x = x + y * alpha

JMP MV45               \ We have now finished rotating the planet or sun by
\ our pitch and roll, so jump back into the MVEIT
\ routine at MV45 to apply all the other movements

Type: Subroutine
Category: Flight
Summary: Flip the coordinate axes for the four different views
Deep dive: Flipping axes between space views
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* Main flight loop (Part 11 of 16) calls PU1

This routine flips the relevant geometric axes in INWK depending on which
view we are looking through (front, rear, left, right).

.PU1

DEX                    \ Decrement the view, so now:
\
\   0 = rear
\   1 = left
\   2 = right

BNE PU2                \ If the current view is left or right, jump to PU2,
\ otherwise this is the rear view, so continue on

LDA INWK+2             \ Flip the sign of x_sign
EOR #%10000000
STA INWK+2

LDA INWK+8             \ Flip the sign of z_sign
EOR #%10000000
STA INWK+8

LDA INWK+10            \ Flip the sign of nosev_x_hi
EOR #%10000000
STA INWK+10

LDA INWK+14            \ Flip the sign of nosev_z_hi
EOR #%10000000
STA INWK+14

LDA INWK+16            \ Flip the sign of roofv_x_hi
EOR #%10000000
STA INWK+16

LDA INWK+20            \ Flip the sign of roofv_z_hi
EOR #%10000000
STA INWK+20

LDA INWK+22            \ Flip the sign of sidev_x_hi
EOR #%10000000
STA INWK+22

LDA INWK+26            \ Flip the sign of roofv_z_hi
EOR #%10000000
STA INWK+26

RTS                    \ Return from the subroutine

.PU2

\ We enter this with X set to the view, as follows:
\
\   1 = left
\   2 = right

LDA #0                 \ Set RAT2 = 0 (left view) or -1 (right view)
CPX #2
ROR A
STA RAT2

EOR #%10000000         \ Set RAT = -1 (left view) or 0 (right view)
STA RAT

LDA INWK               \ Swap x_lo and z_lo
LDX INWK+6
STA INWK+6
STX INWK

LDA INWK+1             \ Swap x_hi and z_hi
LDX INWK+7
STA INWK+7
STX INWK+1

LDA INWK+2             \ Swap x_sign and z_sign
EOR RAT                \ If left view, flip sign of new z_sign
TAX                    \ If right view, flip sign of new x_sign
LDA INWK+8
EOR RAT2
STA INWK+2
STX INWK+8

LDY #9                 \ Swap nosev_x_lo and nosev_z_lo
JSR PUS1               \ Swap nosev_x_hi and nosev_z_hi
\ If left view, flip sign of new nosev_z_hi
\ If right view, flip sign of new nosev_x_hi

LDY #15                \ Swap roofv_x_lo and roofv_z_lo
JSR PUS1               \ Swap roofv_x_hi and roofv_z_hi
\ If left view, flip sign of new roofv_z_hi
\ If right view, flip sign of new roofv_x_hi

LDY #21                \ Swap sidev_x_lo and sidev_z_lo
\ Swap sidev_x_hi and sidev_z_hi
\ If left view, flip sign of new sidev_z_hi
\ If right view, flip sign of new sidev_x_hi

.PUS1

LDA INWK,Y             \ Swap the low x and z bytes for the vector in Y:
LDX INWK+4,Y           \
STA INWK+4,Y           \   * For Y =  9 swap nosev_x_lo and nosev_z_lo
STX INWK,Y             \   * For Y = 15 swap roofv_x_lo and roofv_z_lo
\   * For Y = 21 swap sidev_x_lo and sidev_z_lo

LDA INWK+1,Y           \ Swap the high x and z bytes for the offset in Y:
EOR RAT                \
TAX                    \   * If left view, flip sign of new z-coordinate
LDA INWK+5,Y           \   * If right view, flip sign of new x-coordinate
EOR RAT2
STA INWK+1,Y
STX INWK+5,Y

\ Fall through into LOOK1 to return from the subroutine

Type: Subroutine
Category: Flight
Summary: Initialise the space view
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* MJP calls LOOK1
* TT102 calls LOOK1
* TT110 calls LOOK1
* WARP calls LOOK1
* SIGHT calls entry point LO2

Initialise the space view, with the direction of view given in X. This clears
the upper screen and draws the laser crosshairs, if the view in X has lasers
fitted. It also wipes all the ships from the scanner, so we can recalculate
ship positions for the new view (they get put back in the main flight loop).

Arguments:

X                    The space view to set:

* 0 = front

* 1 = rear

* 2 = left

* 3 = right

Other entry points:

LO2                  Contains an RTS

.LO2

RTS                    \ Return from the subroutine

.LQ

STX VIEW               \ Set the current space view to X

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 SIGHT              \ Draw the laser crosshairs

JMP NWSTARS            \ Set up a new stardust field and return from the
\ subroutine using a tail call

.LOOK1

LDA #0                 \ Set A = 0, the type number of a space view

LDY QQ11               \ If the current view is not a space view, jump up to LQ
BNE LQ                 \ to set up a new space view

BEQ LO2                \ to return from the subroutine (as LO2 contains an RTS)

STX VIEW               \ Change the current space view to X

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 FLIP               \ Swap the x- and y-coordinates of all the stardust
\ particles

JSR WPSHPSS            \ Wipe all the ships from the scanner

\ And fall through into SIGHT to draw the laser
\ crosshairs

Type: Subroutine
Category: Flight
Summary: Draw the laser crosshairs
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* LOOK1 calls SIGHT

.SIGHT

LDY VIEW               \ Fetch the laser power for our new view
LDA LASER,Y

BEQ LO2                \ If it is zero (i.e. there is no laser fitted to this
\ LO2 contains an RTS)

LDA #128               \ Set QQ19 to the x-coordinate of the centre of the
STA QQ19               \ screen

LDA #Y-24              \ Set QQ19+1 to the y-coordinate of the centre of the
STA QQ19+1             \ screen, minus 24 (because TT15 will add 24 to the
\ coordinate when it draws the crosshairs)

LDA #20                \ Set QQ19+2 to size 20 for the crosshairs size
STA QQ19+2

JSR TT15               \ Call TT15 to draw crosshairs of size 20 just to the
\ left of the middle of the screen

LDA #10                \ Set QQ19+2 to size 10 for the crosshairs size
STA QQ19+2

JMP TT15               \ Call TT15 to draw crosshairs of size 10 at the same
\ location, which will remove the centre part from the
\ laser crosshairs, leaving a gap in the middle, and
\ return from the subroutine using a tail call

Type: Variable
Category: Dashboard
Summary: The EOR value for different types of ship in the the I.F.F. system
for creating striped sticks in the scanner
Deep dive: The I.F.F. system
Context: See this variable on its own page
References: This variable is used as follows:
* SCAN calls iff_xor

These are the EOR values for the I.F.F. system, which shows ships on the
scanner in the following colours, depending on the type index for this ship
(as returned by the iff_index routine). The EOR values determine whether the
stick is striped.

The colours for the normal dashboard palette are:

Index     Dot colour  Stick colour(s)     Ship types
-----     ----------  ---------------     ----------
0         Green       Green               Clean
1         Yellow      Yellow              Station tracked
2         Green       Green and yellow    Debris
3         Yellow      Yellow and red      Missile
4         Green       Green and red       Offender/fugitive

The colours for the escape pod dashboard palette are:

Index     Dot colour  Stick colour(s)     Ship types
-----     ----------  ---------------     ----------
0         Cyan        Cyan                Clean
1         White       White               Station tracked
2         Cyan        Cyan and white      Debris
3         White       White and red       Missile
4         Cyan        Cyan and red        Offender/fugitive

The EOR values have the following effect on the colour of the stick:

%00000000       Stick is a solid colour, in the base colour
%00001111       Stick is striped, in the base colour and base colour EOR %01
%11110000       Stick is striped, in the base colour and base colour EOR %10
%11111111       Stick is striped, in the base colour and base colour EOR %11

Taking the example of debris, the base colour from iff_base+2 is &FF, which is
%11111111, or a four-pixel byte of colour %11, or colour 3 in mode 5, or
green/cyan (green for the normal palette, cyan in the escape pod palette).

The EOR value from iff_xor+2 is &0F, which is %00001111, or a four-pixel byte
of %01 values. Applying this EOR to the base colour (%11) gives:

%11 EOR %01 = %10 = 2

and colour 2 in mode 5 is yellow/white (yellow for the normal palette, white
in the escape pod palette). So the stick colour for debris when we have an
I.F.F. system fitted is:

Green/cyan (the base colour) striped with yellow/white (the colour after
applying the EOR value)

If there is no I.F.F. system fitted, the index is 0 and the EOR value is 0,
which doesn't affect the default colour.

The last two entries are the same as the first two entries in iff_base, which
is the next variable, so they are commented out here to save two bytes.

.iff_xor

EQUB &00               \ Index 0: Clean = %00000000

EQUB &00               \ Index 1: Station tracked = %00000000

EQUB &0F               \ Index 2: Debris = %00001111

\EQUB &FF               \ Index 3: Missile = %11111111

\EQUB &F0               \ Index 4: Offender/fugitive = %11110000

Type: Variable
Category: Dashboard
Summary: Base colours for different types of ship in the the I.F.F. system
Deep dive: The I.F.F. system
Context: See this variable on its own page
References: This variable is used as follows:
* SCAN calls iff_base

These are the base colours for the I.F.F. system, which shows ships on the
scanner in the following colours, depending on the type index for this ship
(as returned by the iff_index routine). The base colours determine the colour
of the dot, as well as the underlying colour of the stick (which can be
striped, depending on the corresponding EOR colour from iff_xor).

The colours for the normal dashboard palette are:

Index     Dot colour  Stick colour(s)     Ship types
0         Green       Green               Clean
1         Yellow      Yellow              Station tracked
2         Green       Green and yellow    Debris
3         Yellow      Yellow and red      Missile
4         Green       Green and red       Offender/fugitive

The colours for the escape pod dashboard palette are:

Index     Dot colour  Stick colour(s)     Ship types
0         Cyan        Cyan                Clean
1         White       White               Station tracked
2         Cyan        Cyan and white      Debris
3         White       White and red       Missile
4         Cyan        Cyan and red        Offender/fugitive

.iff_base

EQUB &FF               \ Index 0: Clean = green/cyan

EQUB &F0               \ Index 1: Station tracked = yellow/white

EQUB &FF               \ Index 2: Debris = green/cyan

EQUB &F0               \ Index 3: Missile = yellow/white

EQUB &FF               \ Index 4: Offender/fugitive = green/cyan

Type: Subroutine
Category: Dashboard
Summary: Display the current ship on the scanner by sending a draw_tail
command to the I/O processor
Deep dive: The 3D scanner
The I.F.F. system
Context: See this subroutine on its own page
References: This subroutine is called as follows:
* ESCAPE calls SCAN
* MVEIT (Part 9 of 9) calls SCAN
* MVEIT_FLIGHT (Part 2 of 6) calls SCAN
* Main flight loop (Part 11 of 16) calls SCAN
* WPSHPS calls SCAN

This is used both to display a ship on the scanner, and to erase it again.

Arguments:

INWK                 The ship's data block

.SC5

RTS                    \ Return from the subroutine

.SCAN

LDA INWK+31            \ Fetch the ship's scanner flag from byte #31

AND #%00010000         \ If bit 4 is clear then the ship should not be shown
BEQ SC5                \ on the scanner, so return from the subroutine (as SC5
\ contains an RTS)

LDA TYPE               \ Fetch the ship's type from TYPE into A

BMI SC5                \ If this is the planet or the sun, then the type will
\ have bit 7 set and we don't want to display it on the
\ scanner, so return from the subroutine (as SC5
\ contains an RTS)

LDX CRGO               \ If we do not have an I.F.F. system fitted (i.e. CRGO
BEQ iff_not            \ is zero), jump to iff_not to fetch the default
\ colours, which are those for a trader or innocent
\ bystander (i.e. X = 0)

\ If we get here then X = &FF (as CRGO is &FF if we have
\ an I.F.F. system fitted)

LDY #36                \ Set A to byte #36 of the ship's blueprint, i.e. the
LDA (INF),Y            \ NEWB flags

ASL A                  \ If bit 6 is set, i.e. this is a cop, a space station
ASL A                  \ or an escape pod, jump to iff_cop to set X = 1
BCS iff_cop

ASL A                  \ If bit 5 is set, i.e. this is an innocent bystander

LDY TYPE               \ Set Y to the ship's type - 1
DEY

BEQ iff_missle         \ If Y = 0, i.e. this is a missile, then jump to
\ iff_missle to set X = 3

CPY #8                 \ If Y < 8, i.e. this is a cargo canister, alloy plate,
\ to set X = 2

\ If we get here then the ship is not the following:
\
\   * A cop/station/escape pod
\   * An innocent bystander/trader/good bounty hunter
\   * A missile
\   * Cargo or an asteroid
\
\ So it must be a pirate or a non-innocent bounty hunter

INX                    \ X is &FF at this point, so this INX sets X = 0, and we
\ then fall through into the four INX instructions below
\ to set X = 4

.iff_missle

INX                    \ If we jump to this point, then set X = 3

.iff_aster

INX                    \ If we jump to this point, then set X = 2

.iff_cop

INX                    \ If we jump to this point, then set X = 1

INX                    \ If we jump to this point, then set X = 0

.iff_not

LDA iff_base,X         \ Set COL to the base colour for this ship, given the
STA COL                \ I.F.F. colour index in X (this colour is used for both
\ the dot and stick)

LDA iff_xor,X          \ Set Y2 to the alternating colour for this ship, given
STA Y2                 \ the I.F.F. colour index in X (this colour is used for
\ making the stick striped, where appropriate)

LDA INWK+1             \ If any of x_hi, y_hi and z_hi have a 1 in bit 6 or 7,
ORA INWK+4             \ then the ship is too far away to be shown on the
ORA INWK+7             \ scanner, so return from the subroutine (as SC5
AND #%11000000         \ contains an RTS)
BNE SC5

\ If we get here, we know x_hi, y_hi and z_hi are all
\ 63 (%00111111) or less

\ Now, we convert the x_hi coordinate of the ship into
\ the screen x-coordinate of the dot on the scanner,
\ using the following (see the deep dive on "The 3D
\ scanner" for an explanation):
\
\   X1 = 123 + (x_sign x_hi)

LDA INWK+1             \ Set x_hi

CLC                    \ Clear the C flag so we can do addition below

LDX INWK+2             \ Set X = x_sign

BPL SC2                \ If x_sign is positive, skip the following

EOR #%11111111         \ x_sign is negative, so flip the bits in A and subtract
ADC #1                 \ 1 to make it a negative number (bit 7 will now be set
\ as we confirmed above that bits 6 and 7 are clear). So
\ this gives A the sign of x_sign and gives it a value
\ range of -63 (%11000001) to 0

.SC2

ADC #123               \ Set X1 = 123 + x_hi
STA X1

\ Next, we convert the z_hi coordinate of the ship into
\ the y-coordinate of the base of the ship's stick,
\ like this (see the deep dive on "The 3D scanner" for
\ an explanation):
\
\   SC = 220 - (z_sign z_hi) / 4
\
\ though the following code actually does it like this:
\
\   SC = 255 - (35 + z_hi / 4)

LDA INWK+7             \ Set A = z_hi / 4
LSR A                  \
LSR A                  \ So A is in the range 0-15

CLC                    \ Clear the C flag

LDX INWK+8             \ Set X = z_sign

BPL SC3                \ If z_sign is positive, skip the following

EOR #%11111111         \ z_sign is negative, so flip the bits in A and set the
SEC                    \ C flag. As above, this makes A negative, this time
\ with a range of -16 (%11110000) to -1 (%11111111). And
\ another 1 to that value, giving a range of -15 to 0

.SC3

ADC #35                \ Set A = 35 + A to give a number in the range 20 to 50

EOR #%11111111         \ Flip all the bits and store in SC, so SC is in the
STA SC                 \ range 205 to 235, with a higher z_hi giving a lower SC

\ Now for the stick height, which we calculate using the
\ following (see the deep dive on "The 3D scanner" for
\ an explanation):
\
\ A = - (y_sign y_hi) / 2

LDA INWK+4             \ Set A = y_hi / 2
LSR A

CLC                    \ Clear the C flag

LDX INWK+5             \ Set X = y_sign

BMI SCD6               \ If y_sign is negative, skip the following, as we
\ already have a positive value in A

EOR #%11111111         \ y_sign is positive, so flip the bits in A and set the
SEC                    \ C flag. This makes A negative, and as we are about to
\ do an ADC below, the SEC effectively adds another 1 to
\ that value to implement two's complement negation, so
\ we don't need to add another 1 here

.SCD6

\ We now have all the information we need to draw this
\ ship on the scanner, namely:
\
\   X1 = the screen x-coordinate of the ship's dot
\
\   SC = the screen y-coordinate of the base of the
\        stick
\
\   A = the screen height of the ship's stick, with the
\       correct sign for adding to the base of the stick
\       to get the dot's y-coordinate
\
\ First, though, we have to make sure the dot is inside
\ the dashboard, by moving it if necessary

ADC SC                 \ Set A = SC + A, so A now contains the y-coordinate of
\ the end of the stick, plus the length of the stick, to
\ give us the screen y-coordinate of the dot

BPL ld246              \ If the result has bit 0 clear, then the result has
\ overflowed and is bigger than 256, so jump to ld246 to
\ set A to the maximum allowed value of 246 (this
\ instruction isn't required as we test both the maximum
\ and minimum below, but it might save a few cycles)

CMP #194               \ If A >= 194, skip the following instruction, as 194 is
BCS P%+4               \ the minimum allowed value of A

LDA #194               \ A < 194, so set A to 194, the minimum allowed value
\ for the y-coordinate of our ship's dot

CMP #247               \ If A < 247, skip the following instruction, as 246 is
BCC P%+4               \ the maximum allowed value of A

.ld246

LDA #246               \ A >= 247, so set A to 246, the maximum allowed value
\ for the y-coordinate of our ship's dot

STA Y1                 \ Store A in Y1, as it now contains the screen
\ y-coordinate for the ship's dot, clipped so that it
\ fits within the dashboard

SEC                    \ Set A = A - SC to get the stick length, by reversing
SBC SC                 \ the ADC SC we did above. This clears the C flag if the
\ result is negative (i.e. the stick length is negative)
\ and sets it if the result is positive (i.e. the stick
\ length is negative)

\ So now we have the following:
\
\   X1 = the screen x-coordinate of the ship's dot,
\        clipped to fit into the dashboard
\
\   Y1 = the screen y-coordinate of the ship's dot,
\        clipped to fit into the dashboard
\
\   SC = the screen y-coordinate of the base of the
\        stick
\
\   A = the screen height of the ship's stick, with the
\       correct sign for adding to the base of the stick
\       to get the dot's y-coordinate
\
\   C = 0 if A is negative, 1 if A is positive
\
\ and we can get on with drawing the dot and stick

TAX                    \ Copy the stick height in A into X

LDA #&91               \ Send command &91 to the I/O processor:
JSR tube_write         \
\   draw_tail(x, y, base_colour, alt_colour, height)
\
\ which will draw a ship on the 3D scanner with a dot
\ and stick of the specified height and colour, possibly
\ with stripes

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

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

LDA COL                \ Send the third parameter to the I/O processor:
JSR tube_write         \
\   * base_colour = COL

LDA Y2                 \ Send the fourth parameter to the I/O processor:
JSR tube_write         \
\   * alt_colour = Y2

TXA                    \ Send the fifth parameter to the I/O processor:
JSR tube_write         \
\   * height = the stick height that we stored in X

LDX #0                 \ Set X = 0 to ensure we return the same value as the
\ SCAN routine in the non-Tube version

RTS                    \ Return from the subroutine

Save ELTM.bin

PRINT "ELITE M"
PRINT "Assembled at ", ~CODE_M%
PRINT "Ends at ", ~P%
PRINT "Code size is ", ~(P% - CODE_M%)