Skip to navigation

Elite on the BBC Micro and NES

Elite J parasite source

[Elite-A]

ELITE J FILE
CODE_J% = P% LOAD_J% = LOAD% + P% - CODE%
Name: Main flight loop (Part 1 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Seed the random number generator Deep dive: Program flow of the main game loop Generating random numbers
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH calls via M% * Main game loop for flight (Part 2 of 6) calls via M%

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Seed the random number generator
Other entry points: M% The entry point for the main flight loop
.M% LDA K% \ We want to seed the random number generator with a \ pretty random number, so fetch the contents of K%, \ which is the x_lo coordinate of the planet. This value \ will be fairly unpredictable, so it's a pretty good \ candidate STA RAND \ Store the seed in the first byte of the four-byte \ random number seed that's stored in RAND
Name: Main flight loop (Part 2 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Calculate the alpha and beta angles from the current pitch and roll of our ship Deep dive: Program flow of the main game loop Pitching and rolling
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Calculate the alpha and beta angles from the current pitch and roll Here we take the current rate of pitch and roll, as set by the joystick or keyboard, and convert them into alpha and beta angles that we can use in the matrix functions to rotate space around our ship. The alpha angle covers roll, while the beta angle covers pitch (there is no yaw in this version of Elite). The angles are in radians, which allows us to use the small angle approximation when moving objects in the sky (see the MVEIT routine for more on this). Also, the signs of the two angles are stored separately, in both the sign and the flipped sign, as this makes calculations easier.
LDX JSTX \ Set X to the current rate of roll in JSTX CPX new_max \ If X < new_max (where new_max is our current ship's BCC n_highx \ maximum roll rate), then jump to n_highx to skip the \ following instruction LDX new_max \ X is at least new_max, so set X to new_max so it is \ never higher than our current ship's maximum roll rate .n_highx CPX new_min \ If X >= new_min (where new_min is our current ship's BCS n_lowx \ minimum roll rate), then jump to n_lowx to skip the \ following instruction LDX new_min \ X is less than new_min, so set X to new_min so it is \ never lower than our current ship's minimum roll rate .n_lowx JSR cntr \ Apply keyboard damping twice (if enabled) so the roll JSR cntr \ rate in X creeps towards the centre by 2 \ The roll rate in JSTX increases if we press ">" (and \ the RL indicator on the dashboard goes to the right) \ \ This rolls our ship to the right (clockwise), but we \ actually implement this by rolling everything else \ to the left (anti-clockwise), so a positive roll rate \ in JSTX translates to a negative roll angle alpha TXA \ Set A and Y to the roll rate but with the sign bit EOR #%10000000 \ flipped (i.e. set them to the sign we want for alpha) TAY AND #%10000000 \ Extract the flipped sign of the roll rate and store STA ALP2 \ in ALP2 (so ALP2 contains the sign of the roll angle \ alpha) STX JSTX \ Update JSTX with the damped value that's still in X EOR #%10000000 \ Extract the correct sign of the roll rate and store STA ALP2+1 \ in ALP2+1 (so ALP2+1 contains the flipped sign of the \ roll angle alpha) TYA \ Set A to the roll rate but with the sign bit flipped BPL P%+7 \ If the value of A is positive, skip the following \ three instructions EOR #%11111111 \ A is negative, so change the sign of A using two's CLC \ complement so that A is now positive and contains ADC #1 \ the absolute value of the roll rate, i.e. |JSTX| LSR A \ Divide the (positive) roll rate in A by 4 LSR A CMP #8 \ If A >= 8, skip the following instruction BCS P%+3 LSR A \ A < 8, so halve A again STA ALP1 \ Store A in ALP1, so we now have: \ \ ALP1 = |JSTX| / 8 if |JSTX| < 32 \ \ ALP1 = |JSTX| / 4 if |JSTX| >= 32 \ \ This means that at lower roll rates, the roll angle is \ reduced closer to zero than at higher roll rates, \ which gives us finer control over the ship's roll at \ lower roll rates \ \ Because JSTX is in the range -127 to +127, ALP1 is \ in the range 0 to 31 ORA ALP2 \ Store A in ALPHA, but with the sign set to ALP2 (so STA ALPHA \ ALPHA has a different sign to the actual roll rate) LDX JSTY \ Set X to the current rate of pitch in JSTY CPX new_max \ If X < new_max (where new_max is our current ship's BCC n_highy \ maximum pitch rate), then jump to n_highy to skip the \ following instruction LDX new_max \ X is at least new_max, so set X to new_max so it is \ never higher than our current ship's maximum pitch \ rate .n_highy CPX new_min \ If X >= new_min (where new_min is our current ship's BCS n_lowy \ minimum pitch rate), then jump to n_lowy to skip the \ following instruction LDX new_min \ X is less than new_min, so set X to new_min so it is \ never lower than our current ship's minimum pitch rate .n_lowy JSR cntr \ Apply keyboard damping so the pitch rate in X creeps \ towards the centre by 1 TXA \ Set A and Y to the pitch rate but with the sign bit EOR #%10000000 \ flipped TAY AND #%10000000 \ Extract the flipped sign of the pitch rate into A STX JSTY \ Update JSTY with the damped value that's still in X STA BET2+1 \ Store the flipped sign of the pitch rate in BET2+1 EOR #%10000000 \ Extract the correct sign of the pitch rate and store STA BET2 \ it in BET2 TYA \ Set A to the pitch rate but with the sign bit flipped BPL P%+4 \ If the value of A is positive, skip the following \ instruction EOR #%11111111 \ A is negative, so flip the bits ADC #4 \ Add 4 to the (positive) pitch rate, so the maximum \ value is now up to 131 (rather than 127) LSR A \ Divide the (positive) pitch rate in A by 16 LSR A LSR A LSR A CMP #3 \ If A >= 3, skip the following instruction BCS P%+3 LSR A \ A < 3, so halve A again STA BET1 \ Store A in BET1, so we now have: \ \ BET1 = |JSTY| / 32 if |JSTY| < 48 \ \ BET1 = |JSTY| / 16 if |JSTY| >= 48 \ \ This means that at lower pitch rates, the pitch angle \ is reduced closer to zero than at higher pitch rates, \ which gives us finer control over the ship's pitch at \ lower pitch rates \ \ Because JSTY is in the range -131 to +131, BET1 is in \ the range 0 to 8 ORA BET2 \ Store A in BETA, but with the sign set to BET2 (so STA BETA \ BETA has the same sign as the actual pitch rate)
Name: Main flight loop (Part 3 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Scan for flight keys and process the results Deep dive: Program flow of the main game loop The key logger
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Scan for flight keys and process the results Flight keys are logged in the key logger at location KY1 onwards, with a non-zero value in the relevant location indicating a key press. See the deep dive on "The key logger" for more details. The key presses that are processed are as follows: * Space and "?" to speed up and slow down * "U", "T" and "M" to disarm, arm and fire missiles * TAB to activate the hyperspace unit * ESCAPE to launch an escape pod * "J" to initiate an in-system jump * "E" to deploy E.C.M. anti-missile countermeasures * "C" to use the docking computer * "A" to fire lasers
LDA KY2 \ If Space is being pressed, keep going, otherwise jump BEQ MA17 \ down to MA17 to skip the following LDA DELTA \ The "go faster" key is being pressed, so first we CMP new_speed \ fetch the current speed from DELTA into A, and if BCC speed_up \ A < new_speed (the maximum speed of our current ship), \ then we can go a bit faster, so jump to speed_up to \ accelerate .MA17 LDA KY1 \ If "?" is being pressed, keep going, otherwise jump BEQ MA4 \ down to MA4 to skip the following DEC DELTA \ The "slow down" key is being pressed, so we decrement \ the current ship speed in DELTA BNE MA4 \ If the speed is still greater than zero, jump to MA4 .speed_up INC DELTA \ Otherwise we just braked a little too hard, so bump \ the speed back up to the minimum value of 1 .MA4 LDA KY15 \ If "U" is being pressed and the number of missiles AND NOMSL \ in NOMSL is non-zero, keep going, otherwise jump down BEQ MA20 \ to MA20 to skip the following LDY #&EE \ The "disarm missiles" key is being pressed, so call JSR ABORT \ ABORT to disarm the missile and update the missile \ indicators on the dashboard to green/cyan (Y = &EE) JSR WA1 \ Call the WA1 routine to make a low, long beep to \ indicate the missile is now disarmed LDA #0 \ Set MSAR to 0 to indicate that no missiles are STA MSAR \ currently armed .MA20 LDA MSTG \ If MSTG is positive (i.e. it does not have bit 7 set), BPL MA25 \ then it indicates we already have a missile locked on \ a target (in which case MSTG contains the ship number \ of the target), so jump to MA25 to skip targeting. Or \ to put it another way, if MSTG = &FF, which means \ there is no current target lock, keep going LDA KY14 \ If "T" is being pressed, keep going, otherwise jump BEQ MA25 \ down to MA25 to skip the following LDX NOMSL \ If the number of missiles in NOMSL is zero, jump down BEQ MA25 \ to MA25 to skip the following STA MSAR \ The "target missile" key is being pressed and we have \ at least one missile, so set MSAR = &FF to denote that \ our missile is currently armed (we know A has the \ value &FF, as we just loaded it from MSTG and checked \ that it was negative) LDY #&E0 \ Change the leftmost missile indicator to yellow/white DEX \ on the missile bar (this call changes the leftmost JSR MSBARS \ indicator because we set X to the number of missiles \ in NOMSL above, and the indicators are numbered from \ right to left, starting at 0, so X - 1 is the number \ of the leftmost indicator) .MA25 LDA KY16 \ If "M" is being pressed, keep going, otherwise jump BEQ MA24 \ down to MA24 to skip the following LDA MSTG \ If MSTG = &FF then there is no target lock, so jump to BMI MA64 \ MA64 to skip the following (also skipping the checks \ for TAB, ESCAPE, "J" and "E") JSR FRMIS \ The "fire missile" key is being pressed and we have \ a missile lock, so call the FRMIS routine to fire \ the missile .MA24 LDA KY12 \ If TAB is not being pressed (i.e. KY12 = 0) and we do AND BOMB \ not have a hyperspace unit fitted (i.e. BOMB = 0), BEQ MA76 \ jump down to MA76 to skip the following INC BOMB \ The "hyperspace unit" key is being pressed and we have \ a hyperspace unit fitted, so increment BOMB from &FF \ (hyperspace unit fitted) to 0 (hyperspace unit not \ fitted), as it is a single-use item and we are now \ using it INC new_hold \ Free up one tonne of space in the hold, as we have \ just used up the hyperspace unit JSR DORND \ Set A and X to random numbers STA QQ9 \ Set (QQ9, QQ10) to (A, X), so we jump to a random STX QQ10 \ point in the galaxy JSR TT111 \ Select the system closest to galactic coordinates \ (QQ9, QQ10) JSR hyper_snap \ Call hyper_snap to perform a hyperspace, but without \ using up any fuel .MA76 LDA KY19 \ If "C" is being pressed, and we have a docking AND DKCMP \ computer fitted, then KY19 and DKCMP will both be &FF, BNE dock_toggle \ so jump down to dock_toggle with A set to &FF LDA KY20 \ If "P" is being pressed, keep going, otherwise skip BEQ MA78 \ the next two instructions LDA #0 \ The "cancel docking computer" key is bring pressed, \ so turn it off by setting A to 0, so we set auto to 0 \ in the next instruction .dock_toggle STA auto \ Set auto to the value in A, which will be &FF if we \ just turned on the docking computer, or 0 if we just \ turned it off .MA78 LDA KY13 \ If ESCAPE is being pressed and we have an escape pod AND ESCP \ fitted, keep going, otherwise jump to noescp to skip BEQ noescp \ the following instructions JMP ESCAPE \ The button is being pressed to launch an escape pod \ and we have an escape pod fitted, so jump to ESCAPE to \ launch it, and exit the main flight loop using a tail \ call .noescp LDA KY18 \ If "J" is being pressed, keep going, otherwise skip BEQ P%+5 \ the next instruction JSR WARP \ Call the WARP routine to do an in-system jump LDA KY17 \ If "E" is being pressed and we have an E.C.M. fitted, AND ECM \ keep going, otherwise jump down to MA64 to skip the BEQ MA64 \ following LDA ECMA \ If ECMA is non-zero, that means an E.C.M. is already BNE MA64 \ operating and is counting down (this can be either \ our E.C.M. or an opponent's), so jump down to MA64 to \ skip the following (as we can't have two E.C.M. \ systems operating at the same time) DEC ECMP \ The E.C.M. button is being pressed and nobody else \ is operating their E.C.M., so decrease the value of \ ECMP to make it non-zero, to denote that our E.C.M. \ is now on JSR ECBLB2 \ Call ECBLB2 to light up the E.C.M. indicator bulb on \ the dashboard, set the E.C.M. countdown timer to 32, \ and start making the E.C.M. sound .MA64 .MA68 LDA #0 \ Set LAS = 0, to switch the laser off while we do the STA LAS \ following logic STA DELT4 \ Take the 16-bit value (DELTA 0) - i.e. a two-byte LDA DELTA \ number with DELTA as the high byte and 0 as the low LSR A \ byte - and divide it by 4, storing the 16-bit result ROR DELT4 \ in DELT4(1 0). This has the effect of storing the LSR A \ current speed * 64 in the 16-bit location DELT4(1 0) ROR DELT4 STA DELT4+1 JSR read_0346 \ Get the value of the I/O processor's copy of LASCT BNE MA3 \ If LASCT is zero, keep going, otherwise the laser is \ a pulse laser that is between pulses, so jump down to \ MA3 to skip the following LDA KY7 \ If "A" is being pressed, keep going, otherwise jump BEQ MA3 \ down to MA3 to skip the following LDA GNTMP \ If the laser temperature >= 242 then the laser has CMP #242 \ overheated, so jump down to MA3 to skip the following BCS MA3 LDX VIEW \ If the current space view has a laser fitted (i.e. the LDA LASER,X \ laser power for this view is greater than zero), then BEQ MA3 \ keep going, otherwise jump down to MA3 to skip the \ following \ If we get here, then the "fire" button is being \ pressed, our laser hasn't overheated and isn't already \ being fired, and we actually have a laser fitted to \ the current space view, so it's time to hit me with \ those laser beams PHA \ Store the current view's laser power on the stack AND #%01111111 \ Set LAS and LAS2 to bits 0-6 of the laser power STA LAS2 STA LAS LDA #0 \ Call the NOISE routine with A = 0 to make the sound JSR NOISE \ of our laser firing JSR LASLI \ Call LASLI to draw the laser lines PLA \ Restore the current view's laser power into A BPL ma1 \ If the laser power has bit 7 set, then it's an "always \ on" laser rather than a pulsing laser, so keep going, \ otherwise jump down to ma1 to skip the following \ instruction LDA #0 \ This is an "always on" laser (i.e. a beam laser or a \ military laser), so set A = 0, which will be stored in \ LASCT to denote that this is not a pulsing laser .ma1 JSR write_0346 \ Tell the I/O processor to set its copy of LASCT to A, \ which will be 0 for beam lasers, or the laser power \ (15) for pulse lasers. See MS23 below for more on \ laser pulsing and LASCT
Name: Main flight loop (Part 4 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: Copy the ship's data block from K% to the zero-page workspace at INWK Deep dive: Program flow of the main game loop Ship data blocks
Context: See this subroutine on its own page References: This subroutine is called as follows: * KS1 calls via MAL1 * Main flight loop (Part 12 of 16) calls via MAL1

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Start looping through all the ships in the local bubble, and for each one: * Copy the ship's data block from K% to INWK * Set XX0 to point to the ship's blueprint (if this is a ship)
Other entry points: MAL1 Marks the beginning of the ship analysis loop, so we can jump back here from part 12 of the main flight loop to work our way through each ship in the local bubble. We also jump back here when a ship is removed from the bubble, so we can continue processing from the next ship
.MA3 LDX #0 \ We're about to work our way through all the ships in \ our local bubble of universe, so set a counter in X, \ starting from 0, to refer to each ship slot in turn .MAL1 STX XSAV \ Store the current slot number in XSAV LDA FRIN,X \ Fetch the contents of this slot into A. If it is 0 BNE P%+5 \ then this slot is empty and we have no more ships to JMP MA18 \ process, so jump to MA18 below, otherwise A contains \ the type of ship that's in this slot, so skip over the \ JMP MA18 instruction and keep going STA TYPE \ Store the ship type in TYPE JSR GINF \ Call GINF to fetch the address of the ship data block \ for the ship in slot X and store it in INF. The data \ block is in the K% workspace, which is where all the \ ship data blocks are stored \ Next we want to copy the ship data block from INF to \ the zero-page workspace at INWK, so we can process it \ more efficiently LDY #NI%-1 \ There are NI% bytes in each ship data block (and in \ the INWK workspace, so we set a counter in Y so we can \ loop through them .MAL2 LDA (INF),Y \ Load the Y-th byte of INF and store it in the Y-th STA INWK,Y \ byte of INWK DEY \ Decrement the loop counter BPL MAL2 \ Loop back for the next byte until we have copied the \ last byte from INF to INWK LDA TYPE \ If the ship type is negative then this indicates a BMI MA21 \ planet or sun, so jump down to MA21, as the next bit \ sets up a pointer to the ship blueprint, which doesn't \ apply to planets and suns ASL A \ Set Y = ship type * 2 TAY LDA XX21-2,Y \ The ship blueprints at XX21 start with a lookup STA XX0 \ table that points to the individual ship blueprints, \ so this fetches the low byte of this particular ship \ type's blueprint and stores it in XX0 LDA XX21-1,Y \ Fetch the high byte of this particular ship type's STA XX0+1 \ blueprint and store it in XX0+1 \ We now go straight to part 6, omitting part 5 from the \ original disc version, as part 5 implements the energy \ bomb, and Elite-A replaces the energy bomb with the \ hyperspace unit
Name: Main flight loop (Part 6 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: Move the ship in space and copy the updated INWK data block back to K% Deep dive: Program flow of the main game loop Program flow of the ship-moving routine Ship data blocks
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Continue looping through all the ships in the local bubble, and for each one: * Move the ship in space * Copy the updated ship's data block from INWK back to K%
.MA21 JSR MVEIT_FLIGHT \ Call MVEIT_FLIGHT to move the ship we are processing \ in space \ Now that we are done processing this ship, we need to \ copy the ship data back from INWK to the correct place \ in the K% workspace. We already set INF in part 4 to \ point to the ship's data block in K%, so we can simply \ do the reverse of the copy we did before, this time \ copying from INWK to INF LDY #NI%-1 \ Set a counter in Y so we can loop through the NI% \ bytes in the ship data block .MAL3 LDA INWK,Y \ Load the Y-th byte of INWK and store it in the Y-th STA (INF),Y \ byte of INF DEY \ Decrement the loop counter BPL MAL3 \ Loop back for the next byte, until we have copied the \ last byte from INWK back to INF
Name: Main flight loop (Part 7 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: Check whether we are docking, scooping or colliding with it Deep dive: Program flow of the main game loop
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Continue looping through all the ships in the local bubble, and for each one: * Check how close we are to this ship and work out if we are docking, scooping or colliding with it
LDA INWK+31 \ Fetch the status of this ship from bits 5 (is ship AND #%10100000 \ exploding?) and bit 7 (has ship been killed?) from \ ship byte #31 into A JSR MAS4 \ Or this value with x_hi, y_hi and z_hi BNE MA65 \ If this value is non-zero, then either the ship is \ far away (i.e. has a non-zero high byte in at least \ one of the three axes), or it is already exploding, \ or has been flagged as being killed - in which case \ jump to MA65 to skip the following, as we can't dock \ scoop or collide with it LDA INWK \ Set A = (x_lo OR y_lo OR z_lo), and if bit 7 of the ORA INWK+3 \ result is set, the ship is still a fair distance ORA INWK+6 \ away (further than 127 in at least one axis), so jump BMI MA65 \ to MA65 to skip the following, as it's too far away to \ dock, scoop or collide with LDX TYPE \ If the current ship type is negative then it's either BMI MA65 \ a planet or a sun, so jump down to MA65 to skip the \ following, as we can't dock with it or scoop it CPX #SST \ If this ship is the space station, jump to ISDK to BEQ ISDK \ check whether we are docking with it AND #%11000000 \ If bit 6 of (x_lo OR y_lo OR z_lo) is set, then the BNE MA65 \ ship is still a reasonable distance away (further than \ 63 in at least one axis), so jump to MA65 to skip the \ following, as it's too far away to dock, scoop or \ collide with CPX #MSL \ If this ship is a missile, jump down to MA65 to skip BEQ MA65 \ the following, as we can't scoop or dock with a \ missile, and it has its own dedicated collision \ checks in the TACTICS routine LDA BST \ If we have fuel scoops fitted then BST will be &FF, \ otherwise it will be 0 AND INWK+5 \ Ship byte #5 contains the y_sign of this ship, so a \ negative value here means the canister is below us, \ which means the result of the AND will be negative if \ the canister is below us and we have a fuel scoop \ fitted BPL MA58 \ If the result is positive, then we either have no \ scoop or the canister is above us, and in both cases \ this means we can't scoop the item, so jump to MA58 \ to process a collision
Name: Main flight loop (Part 8 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: Process us potentially scooping this item Deep dive: Program flow of the main game loop
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Continue looping through all the ships in the local bubble, and for each one: * Process us potentially scooping this item
CPX #OIL \ If this is a cargo canister, jump to oily to randomly BEQ oily \ decide the canister's contents LDY #0 \ Fetch byte #0 of the ship's blueprint LDA (XX0),Y LSR A \ Shift it right four times, so A now contains the high LSR A \ nibble (i.e. bits 4-7) LSR A LSR A BEQ MA58 \ If A = 0, jump to MA58 to skip all the docking and \ scooping checks \ Only the Thargon, alloy plate, splinter and escape pod \ have non-zero high nibbles in their blueprint byte #0 \ so if we get here, our ship is one of those, and the \ high nibble gives the market item number of the item \ when scooped, less 1 ADC #1 \ Add 1 to the high nibble to get the market item \ number BNE slvy2 \ Skip to slvy2 so we scoop the ship as a market item .oily JSR DORND \ Set A and X to random numbers and reduce A to a AND #15 \ random number in the range 0-15 .slvy2 \ By the time we get here, we are scooping, and A \ contains the type of item we are scooping (a random \ number 0-15 if we are scooping a cargo canister, 3 if \ we are scooping an escape pod, or 16 if we are \ scooping a Thargon). These numbers correspond to the \ relevant market items (see QQ23 for a list), so a \ cargo canister can contain anything from food to \ gem-stones, while escape pods contain slaves, and \ Thargons become alien items when scooped TAX \ Copy the type of cargo we are scooping into X JSR tnpr1 \ Call tnpr1 to work out whether we have room in the \ hold for the scooped item (the C flag contains the \ result) BCS MA58 \ If the C flag is set then we have no room in the hold \ for the scooped item, so jump down to MA58 to skip all \ the docking and scooping checks INC QQ20,X \ Scooping was successful, so increment the number of \ items of type X that we have in the hold TXA \ Print recursive token 48 + X as an in-flight token, ADC #208 \ which will be in the range 48 ("FOOD") to 64 ("ALIEN JSR MESS \ ITEMS"), so this prints the scooped item's name JSR top_6a \ The item has now been scooped, so call top_6a to set \ bit 7 of its NEWB flags to indicate this .MA65 JMP MA26 \ If we get here, then the ship we are processing was \ too far away to be scooped, docked or collided with, \ so jump to MA26 to skip over the collision routines \ and move on to missile targeting
Name: Main flight loop (Part 9 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: If it is a space station, check whether we are successfully docking with it Deep dive: Program flow of the main game loop Docking checks
Context: See this subroutine on its own page References: This subroutine is called as follows: * ESCAPE calls via GOIN

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Process docking with a space station For details on the various docking checks in this routine, see the deep dive on "Docking checks".
Other entry points: GOIN We jump here from part 3 of the main flight loop if the docking computer is activated by pressing "C"
.ISDK LDA K%+NI%+36 \ 1. Fetch the NEWB flags (byte #36) of the second ship AND #%00000100 \ in the ship data workspace at K%, which is reserved BNE MA62 \ for the sun or the space station (in this case it's \ the latter), and if bit 2 is set, meaning the station \ is hostile, jump down to MA62 to fail docking (so \ trying to dock at a station that we have annoyed does \ not end well) LDA INWK+14 \ 2. If nosev_z_hi < 214, jump down to MA62 to fail CMP #214 \ docking, as the angle of approach is greater than 26 BCC MA62 \ degrees LDY #NI% \ Set Y = NI% so the following call to SPS1 calculates \ the vector to the space station rather than the planet JSR SPS1 \ Call SPS1 to calculate the vector to the space station \ and store it in XX15 LDA XX15+2 \ Set A to the z-axis of the vector \ This version of Elite omits check 3 (which would check \ the sign of the z-axis) CMP #86 \ 4. If z-axis < 86, jump to MA62 to fail docking, as BCC MA62 \ we are not in the 26.3 degree safe cone of approach LDA INWK+16 \ 5. If |roofv_x_hi| < 80, jump to MA62 to fail docking, AND #%01111111 \ as the slot is more than 36.6 degrees from horizontal CMP #80 BCC MA62 .GOIN \ If we arrive here, we just docked successfully JSR RES2 \ Reset a number of flight variables and workspaces LDA #8 \ Set the step size for the launch tunnel rings to 8, so \ there are fewer sections in the rings and they are \ quite polygonal (compared to the step size of 4 used \ in the much rounder hyperspace rings) JSR HFS2 \ Call HFS2 to draw the launch tunnel rings JMP DOENTRYS \ Go to the docking bay (i.e. show the ship hangar) .MA62 \ If we arrive here, docking has just failed LDA DELTA \ If the ship's speed is >= 5, jump to n_crunch to CMP #5 \ register a fair amount of damage to our shields (128) BCS n_crunch \ Otherwise we have just crashed gently into the \ station, so we need to check whether it's our fault or \ the docking computer
Name: Main flight loop (Part 10 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: Remove if scooped, or process collisions Deep dive: Program flow of the main game loop
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Continue looping through all the ships in the local bubble, and for each one: * Remove scooped item after both successful and failed scooping attempts * Process collisions
LDA auto \ If the docking computer is on, then auto will be &FF, AND #%00000100 \ so this will set A = 1, a tiny amount of damage EOR #%00000101 \ \ If the docking computer is off, then auto will be 0, \ so this will set A = 5, a small amount of damage BNE MA63 \ Jump to MA63 to process the damage in A (this BNE is \ effectively a JMP as A will never be zero) .MA58 \ If we get here, we have collided with something in a \ potentially fatal way LDA #64 \ Call n_hit to apply a hit of strength 64 to the ship JSR n_hit \ we just collided with JSR anger_8c \ Call anger_8c to make the ship angry .n_crunch \ If we get here, we have collided with something, so we \ need to take a hit to our shields LDA #128 \ Set A = 128 to indicate a fairly large amount of \ damage .MA63 JSR OOPS \ The amount of damage is in A, so call OOPS to reduce \ our shields, and if the shields are gone, there's a \ chance of cargo loss or even death JSR EXNO3 \ Make the sound of colliding with the other ship and \ fall through into MA26 to try targeting a missile
Name: Main flight loop (Part 11 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: Process missile lock and firing our laser Deep dive: Program flow of the main game loop Flipping axes between space views
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Continue looping through all the ships in the local bubble, and for each one: * If this is not the front space view, flip the axes of the ship's coordinates in INWK * Process missile lock * Process our laser firing
.MA26 LDA NEWB \ If bit 7 of the ship's NEWB flags is clear, skip the BPL P%+5 \ following instruction JSR SCAN \ Bit 7 of the ship's NEWB flags is set, which means the \ ship has docked or been scooped, so we draw the ship \ on the scanner, which has the effect of removing it LDA QQ11 \ If this is not a space view, jump to MA15 to skip BNE MA15 \ missile and laser locking LDX VIEW \ Load the current view into X BEQ P%+5 \ If the current view is the front view, skip the \ following instruction, as the geometry in INWK is \ already correct JSR PU1 \ Call PU1 to update the geometric axes in INWK to \ match the view (front, rear, left, right) JSR HITCH \ Call HITCH to see if this ship is in the crosshairs, BCC MA8 \ in which case the C flag will be set (so if there is \ no missile or laser lock, we jump to MA8 to skip the \ following) LDA MSAR \ We have missile lock, so check whether the leftmost BEQ MA47 \ missile is currently armed, and if not, jump to MA47 \ to process laser fire, as we can't lock an unarmed \ missile JSR BEEP \ We have missile lock and an armed missile, so call \ the BEEP subroutine to make a short, high beep LDX XSAV \ Call ABORT2 to store the details of this missile LDY #&0E \ lock, with the targeted ship's slot number in X JSR ABORT2 \ (which we stored in XSAV at the start of this ship's \ loop at MAL1), and set the colour of the missile \ indicator to the colour in Y (red = &0E) .MA47 \ If we get here then the ship is in our sights, but \ we didn't lock a missile, so let's see if we're \ firing the laser LDA LAS \ If we are firing the laser then LAS will contain the BEQ MA8 \ laser power (which we set in MA68 above), so if this \ is zero, jump down to MA8 to skip the following LDX #15 \ We are firing our laser and the ship in INWK is in JSR EXNO \ the crosshairs, so call EXNO to make the sound of \ us making a laser strike on another ship LDA LAS \ Set A to the power of the laser we just used to hit \ the ship (i.e. the laser in the current view) LDY TYPE \ Did we just hit the space station? If so, jump to CPY #SST \ MA14 to make it angry BEQ MA14 CPY #CON \ If the ship we hit is not a Constrictor, jump to BURN BNE BURN \ to skip the following LSR A \ Divide the laser power of the current view by 2, so \ the damage inflicted on the Constrictor is half of the \ damage our military lasers would inflict on a normal \ ship .BURN LSR A \ Divide the laser power of the current view by 2 JSR n_hit \ Call n_hit to apply a laser strike of strength A to \ the enemy ship BCS MA14 \ If the C flag is set then the enemy ship survived the \ hit, so jump down to MA14 to make it angry LDA TYPE \ Did we just kill an asteroid? If not, jump to nosp, CMP #AST \ otherwise keep going BNE nosp LDA LAS \ Did we kill the asteroid using mining lasers? If so, CMP new_mining \ then our current laser strength in LAS will match the BNE nosp \ strength of mining lasers when fitted to our current \ ship type, which is stored in new_mining. If they \ don't match, which means we didn't use mining lasers, \ then jump to nosp, otherwise keep going JSR DORND \ Set A and X to random numbers LDX #SPL \ Set X to the ship type for a splinter AND #3 \ Reduce the random number in A to the range 0-3 JSR SPIN2 \ Call SPIN2 to spawn A items of type X (i.e. spawn \ 0-3 splinters) .nosp LDY #PLT \ Randomly spawn some alloy plates JSR SPIN LDY #OIL \ Randomly spawn some cargo canisters JSR SPIN JSR EXNO2 \ Call EXNO2 to process the fact that we have killed a \ ship (so increase the kill tally, make an explosion \ sound and so on) .MA14 JSR anger_8c \ Call anger_8c to make this ship hostile angry, now \ that we have hit it
Name: Main flight loop (Part 12 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: Draw the ship, remove if killed, loop back Deep dive: Program flow of the main game loop Drawing ships
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Continue looping through all the ships in the local bubble, and for each one: * Draw the ship * Process removal of killed ships * Loop back up to MAL1 to move onto the next ship in the local bubble
.MA8 JSR LL9_FLIGHT \ Call LL9 to draw the ship we're processing on-screen .MA15 LDY #35 \ Fetch the ship's energy from byte #35 and copy it to LDA INWK+35 \ byte #35 in INF (so the ship's data in K% gets STA (INF),Y \ updated) LDA NEWB \ If bit 7 of the ship's NEWB flags is set, which means BMI KS1S \ the ship has docked or been scooped, jump to KS1S to \ skip the following, as we can't get a bounty for a \ ship that's no longer around LDA INWK+31 \ If bit 7 of the ship's byte #31 is clear, then the BPL MAC1 \ ship hasn't been killed by collision or laser fire, \ so jump to MAC1 to skip the following AND #%00100000 \ If bit 5 of the ship's byte #31 is clear then the BEQ MAC1 \ ship is no longer exploding, so jump to MAC1 to skip \ the following \ We now update our FIST flag ("fugitive/innocent \ status") by 1 if we didn't kill a cop, or by a large \ amount if we did - specifically, if we killed a cop, \ then the most significant bit in FIST that is \ currently clear will be set, which means we increase \ FIST by the highest multiple of 2 that we can add and \ still fit the result in a byte \ \ Also, at this point, we know that A = %00100000 (as we \ didn't take the BEQ branch) BIT NEWB \ If bit 6 of the ship's NEWB flags is set, then this BVS n_badboy \ ship is a cop, so jump to n_badboy as we just killed a \ policeman BEQ n_goodboy \ The BIT NEWB instruction sets the Z flag according to \ the result of: \ \ A AND NEWB = %00100000 AND NEWB \ \ so this jumps to n_goodboy if bit 5 of NEWB is clear, \ so in other words, if the ship is no longer exploding, \ we don't update FIST LDA #%10000000 \ Set A so that the shift and rotate instructions we're \ about to do set A = %00000001, so we increase our FIST \ status by just 1 .n_badboy \ We get here with two possible values of A: \ \ * A = %00100000 if we just killed a cop \ * A = %10000000 otherwise ASL A \ Shift and rotate A so that we get: ROL A \ \ * A = %10000000 if we just killed a cop \ * A = %00000001 otherwise .n_bitlegal LSR A \ We now shift A to the right and AND it with FIST, BIT FIST \ repeating the process until the single set bit in A BNE n_bitlegal \ matches a clear bit in FIST, so this shifts A right \ so that the set bit matches the highest clear bit in \ FIST (if we just killed a cop), or it sets A to 0 and \ sets the C flag (if we didn't) ADC FIST \ Set A = A + C + FIST, so: \ \ * A = A + 0 + FIST if we just killed a cop \ * A = 0 + 1 + FIST otherwise \ \ so if we just killed a cop, this will effectively set \ the highest clear bit in FIST, otherwise we just add 1 \ to FIST BCS KS1S \ If the addition overflowed, jump to KS1S to skip \ showing an on-screen bounty for this kill, and without \ updating FIST first (as we are too bad to get any \ worse) STA FIST \ Otherwise update the value of FIST to the new value BCC KS1S \ Jump to KS1S to skip showing an on-screen bounty for \ this kill (the BCC is effectively a JMP as we just \ passed through a BCS) .n_goodboy LDA DLY \ If we already have an in-flight message on-screen (in ORA MJ \ which case DLY > 0), or we are in witchspace (in BNE KS1S \ which case MJ > 0), jump to KS1S to skip showing an \ on-screen bounty for this kill LDY #10 \ Fetch byte #10 of the ship's blueprint, which is the LDA (XX0),Y \ low byte of the bounty awarded when this ship is \ killed (in Cr * 10) TAX \ Put the low byte of the bounty into X INY \ Fetch byte #11 of the ship's blueprint, which is the LDA (XX0),Y \ high byte of the bounty awarded (in Cr * 10), and put TAY \ it into Y JSR MCASH \ Call MCASH to add (Y X) to the cash pot LDA #0 \ Print control code 0 (current cash, right-aligned to JSR MESS \ width 9, then " CR", newline) as an in-flight message .KS1S JMP KS1 \ Process the killing of this ship (which removes this \ ship from its slot and shuffles all the other ships \ down to close up the gap) .n_hit \ If we get here then we need to apply a hit of strength \ A to the enemy ship STA T \ Store the strength of the hit in T SEC \ Set the C flag so we can do some subtraction LDY #14 \ Fetch byte #14 of the enemy ship's blueprint into A, LDA (XX0),Y \ which gives the ship's maximum energy/shields AND #7 \ Reduce the maximum energy/shields figure to the range \ 0-7 SBC T \ Subtract the hit strength from the maximum shields, so \ A = ship energy - hit strength BCS n_kill \ If the subtraction didn't underflow, then the hit was \ weaker than the ship's shields, so jump to n_kill \ with the C flag set to indicate that the ship has \ survived the attack \BCC n_defense \ These instructions are commented out in the original \LDA #&FF \ source \.n_defense CLC \ Otherwise the hit was stronger than the enemy shields, ADC INWK+35 \ so the ship's energy level needs to register some STA INWK+35 \ damage. A contains a negative number whose magnitude \ is the amount by which the attack is greater than the \ shield defence, so we can simply add this figure to \ the ship's energy levels in the ship's byte #35 to \ reduce the energy by the amount that the attack was \ stronger than the defence (i.e. the shields absorb the \ amount of energy that is defined in the blueprint, and \ the rest of the hit makes it through to damage the \ energy levels) BCS n_kill \ Adding this negative number is the same as subtracting \ a positive number, so having the C flag set indicates \ that the subtraction didn't underflow - in other words \ the damage isn't greater than the energy levels, and \ the ship has survived the hit. In this case we jump to \ n_kill with the C flag set to indicate that the ship \ has survived the attack JSR TA87+3 \ If we get here then the ship has not survived the \ attack, so call TA87+3 to set bit 7 of the ship's byte \ #31, which marks the ship as being killed .n_kill RTS \ Return from the subroutine with the C flag set if the \ ship has survived the onslaught, or clear if it has \ been destroyed .MAC1 LDA TYPE \ If the ship we are processing is a planet or sun, BMI MA27 \ jump to MA27 to skip the following two instructions JSR FAROF \ If the ship we are processing is a long way away (its BCC KS1S \ distance in any one direction is > 224, jump to KS1S \ to remove the ship from our local bubble, as it's just \ left the building .MA27 LDY #31 \ Fetch the ship's explosion/killed state from byte #31 LDA INWK+31 \ and copy it to byte #31 in INF (so the ship's data in STA (INF),Y \ K% gets updated) LDX XSAV \ We're done processing this ship, so fetch the ship's \ slot number, which we saved in XSAV back at the start \ of the loop INX \ Increment the slot number to move on to the next slot JMP MAL1 \ And jump back up to the beginning of the loop to get \ the next ship in the local bubble for processing
Name: Main flight loop (Part 13 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Charge shields and energy banks Deep dive: Program flow of the main game loop 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

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Charge shields and energy banks (every 7 iterations of the main loop)
.MA18 .MA77 LDA MCNT \ Fetch the main loop counter and calculate MCNT mod 7, AND #7 \ jumping to MA22 if it is non-zero (so the following BNE MA22 \ code only runs every 8 iterations of the main loop) LDX ENERGY \ Fetch our ship's energy levels and skip to b if bit 7 BPL b \ is not set, i.e. only charge the shields from the \ energy banks if they are at more than 50% charge LDX ASH \ Call SHD to recharge our aft shield and update the JSR SHD \ shield status in ASH STX ASH LDX FSH \ Call SHD to recharge our forward shield and update JSR SHD \ the shield status in FSH STX FSH .b SEC \ Set A = ENERGY + ENGY + 1, so our ship's energy LDA ENGY \ level goes up by the correct amount for our current ADC ENERGY \ ship, depending on whether we have an energy unit \ fitted BCS P%+5 \ If the value of A did not overflow (the maximum STA ENERGY \ energy level is &FF), then store A in ENERGY
Name: Main flight loop (Part 14 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Spawn a space station if we are close enough to the planet Deep dive: Program flow of the main game loop Scheduling tasks with the main loop counter Ship data blocks The space station safe zone
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Spawn a space station if we are close enough to the planet (every 32 iterations of the main loop)
LDA MJ \ If we are in witchspace, jump down to MA23S to skip BNE MA23S \ the following, as there are no space stations in \ witchspace LDA MCNT \ Fetch the main loop counter and calculate MCNT mod 32, AND #31 \ jumping to MA93 if it is on-zero (so the following BNE MA93 \ code only runs every 32 iterations of the main loop) LDA SSPR \ If we are inside the space station safe zone, jump to BNE MA23S \ MA23S to skip the following, as we already have a \ space station and don't need another TAY \ Set Y = A = 0 (A is 0 as we didn't branch with the \ previous BNE instruction) JSR MAS2 \ Call MAS2 to calculate the largest distance to the BNE MA23S \ planet in any of the three axes, and if it's \ non-zero, jump to MA23S to skip the following, as we \ are too far from the planet to bump into a space \ station \ We now want to spawn a space station, so first we \ need to set up a ship data block for the station in \ INWK that we can then pass to NWSPS to add a new \ station to our bubble of universe. We do this by \ copying the planet data block from K% to INWK so we \ can work on it, but we only need the first 29 bytes, \ as we don't need to worry about bytes #29 to #35 \ for planets (as they don't have rotation counters, \ AI, explosions, missiles, a ship line heap or energy \ levels) LDX #28 \ So we set a counter in X to copy 29 bytes from K%+0 \ to K%+28 .MAL4 LDA K%,X \ Load the X-th byte of K% and store in the X-th byte STA INWK,X \ of the INWK workspace DEX \ Decrement the loop counter BPL MAL4 \ Loop back for the next byte until we have copied the \ first 28 bytes of K% to INWK \ We now check the distance from our ship (at the \ origin) towards the point where we will spawn the \ space station if we are close enough \ \ This point is calculated by starting at the planet's \ centre and adding 2 * nosev, which takes us to a point \ above the planet's surface, at an altitude that \ matches the planet's radius \ \ This point pitches and rolls around the planet as the \ nosev vector rotates with the planet, and if our ship \ is within a distance of (192 0) from this point in all \ three axes, then we spawn the space station at this \ point, with the station's slot facing towards the \ planet, along the nosev vector \ \ This works because in the following, we calculate the \ station's coordinates one axis at a time, and store \ the results in the INWK block, so by the time we have \ calculated and checked all three, the ship data block \ is set up with the correct spawning coordinates INX \ Set X = 0 (as we ended the above loop with X as &FF) LDY #9 \ Call MAS1 with X = 0, Y = 9 to do the following: JSR MAS1 \ \ (x_sign x_hi x_lo) += (nosev_x_hi nosev_x_lo) * 2 \ \ A = |x_sign| BNE MA23S \ If A > 0, jump to MA23S to skip the following, as we \ are too far from the planet in the x-direction to \ bump into a space station LDX #3 \ Call MAS1 with X = 3, Y = 11 to do the following: LDY #11 \ JSR MAS1 \ (y_sign y_hi y_lo) += (nosev_y_hi nosev_y_lo) * 2 \ \ A = |y_sign| BNE MA23S \ If A > 0, jump to MA23S to skip the following, as we \ are too far from the planet in the y-direction to \ bump into a space station LDX #6 \ Call MAS1 with X = 6, Y = 13 to do the following: LDY #13 \ JSR MAS1 \ (z_sign z_hi z_lo) += (nosev_z_hi nosev_z_lo) * 2 \ \ A = |z_sign| BNE MA23S \ If A > 0, jump to MA23S to skip the following, as we \ are too far from the planet in the z-direction to \ bump into a space station LDA #192 \ Call FAROF2 to compare x_hi, y_hi and z_hi with 192, JSR FAROF2 \ which will set the C flag if all three are < 192, or \ clear the C flag if any of them are >= 192 BCC MA23S \ Jump to MA23S if any one of x_hi, y_hi or z_hi are \ >= 192 (i.e. they must all be < 192 for us to be near \ enough to the planet to bump into a space station) JSR WPLS \ Call WPLS to remove the sun from the screen, as we \ can't have both the sun and the space station at the \ same time JSR NWSPS \ Add a new space station to our local bubble of \ universe .MA23S JMP MA23 \ Jump to MA23 to skip the following planet and sun \ altitude checks
Name: Main flight loop (Part 15 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Perform altitude checks with the planet and sun and process fuel scooping if appropriate Deep dive: Program flow of the main game loop 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

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Perform an altitude check with the planet (every 32 iterations of the main loop, on iteration 10 of each 32) * Perform an altitude check with the sun and process fuel scooping (every 32 iterations of the main loop, on iteration 20 of each 32)
.MA22 LDA MJ \ If we are in witchspace, jump down to MA23S to skip BNE MA23S \ the following, as there are no planets or suns to \ bump into in witchspace LDA MCNT \ Fetch the main loop counter and calculate MCNT mod 32, AND #31 \ which tells us the position of this loop in each block \ of 32 iterations .MA93 CMP #10 \ If this is the tenth iteration in this block of 32, BNE MA29 \ do the following, otherwise jump to MA29 to skip the \ planet altitude check and move on to the sun distance \ check LDA #50 \ If our energy bank status in ENERGY is >= 50, skip CMP ENERGY \ printing the following message (so the message is BCC P%+6 \ only shown if our energy is low) ASL A \ Print recursive token 100 ("ENERGY LOW{beep}") as an JSR MESS \ in-flight message LDY #&FF \ Set our altitude in ALTIT to &FF, the maximum STY ALTIT INY \ Set Y = 0 JSR m \ Call m to calculate the maximum distance to the \ planet in any of the three axes, returned in A BNE MA23 \ If A > 0 then we are a fair distance away from the \ planet in at least one axis, so jump to MA23 to skip \ the rest of the altitude check JSR MAS3 \ Set A = x_hi^2 + y_hi^2 + z_hi^2, so using Pythagoras \ we now know that A now contains the square of the \ distance between our ship (at the origin) and the \ centre of the planet at (x_hi, y_hi, z_hi) BCS MA23 \ If the C flag was set by MAS3, then the result \ overflowed (was greater than &FF) and we are still a \ fair distance from the planet, so jump to MA23 as we \ haven't crashed into the planet SBC #36 \ Subtract 36 from x_hi^2 + y_hi^2 + z_hi^2 \ \ When we do the 3D Pythagoras calculation, we only use \ the high bytes of the coordinates, so that's x_hi, \ y_hi and z_hi and \ \ The planet radius is (0 96 0), as defined in the \ PLANET routine, so the high byte is 96 \ \ When we square the coordinates above and add them, \ the result gets divided by 256 (otherwise the result \ wouldn't fit into one byte), so if we do the same for \ the planet's radius, we get: \ \ 96 * 96 / 256 = 36 \ \ So for the planet, the equivalent figure to test the \ sum of the _hi bytes against is 36, so A now contains \ the high byte of our altitude above the planet \ surface, squared BCC MA28 \ If A < 0 then jump to MA28 as we have crashed into \ the planet STA R \ We are getting close to the planet, so we need to JSR LL5 \ work out how close. We know from the above that A \ contains our altitude squared, so we store A in R \ and call LL5 to calculate: \ \ Q = SQRT(R Q) = SQRT(A Q) \ \ Interestingly, Q doesn't appear to be set to 0 for \ this calculation, so presumably this doesn't make a \ difference LDA Q \ Store the result in ALTIT, our altitude STA ALTIT BNE MA23 \ If our altitude is non-zero then we haven't crashed, \ so jump to MA23 to skip to the next section .MA28 JMP DEATH \ If we get here then we just crashed into the planet \ or got too close to the sun, so jump to DEATH to start \ the funeral preparations and return from the main \ flight loop using a tail call .MA29 CMP #15 \ If this is the 15th iteration in this block of 32, BNE MA33 \ do the following, otherwise jump to MA33 to skip the \ docking computer manoeuvring LDA auto \ If auto is zero, then the docking computer is not BEQ MA23 \ activated, so jump to MA23 to skip to the next \ section LDA #123 \ Set A = 123 and jump down to MA34 to print token 123 BNE MA34 \ ("DOCKING COMPUTERS ON") as an in-flight message .MA33 CMP #20 \ If this is the 20th iteration in this block of 32, BNE MA23 \ do the following, otherwise jump to MA23 to skip the \ sun altitude check LDA #30 \ Set CABTMP to 30, the cabin temperature in deep space STA CABTMP \ (i.e. one notch on the dashboard bar) LDA SSPR \ If we are inside the space station safe zone, jump to BNE MA23 \ MA23 to skip the following, as we can't have both the \ sun and space station at the same time, so we clearly \ can't be flying near the sun LDY #NI% \ Set Y to NI%, which is the offset in K% for the sun's \ data block, as the second block at K% is reserved for \ the sun (or space station) JSR MAS2 \ Call MAS2 to calculate the largest distance to the BNE MA23 \ sun in any of the three axes, and if it's non-zero, \ jump to MA23 to skip the following, as we are too far \ from the sun for scooping or temperature changes JSR MAS3 \ Set A = x_hi^2 + y_hi^2 + z_hi^2, so using Pythagoras \ we now know that A now contains the square of the \ distance between our ship (at the origin) and the \ heart of the sun at (x_hi, y_hi, z_hi) EOR #%11111111 \ Invert A, so A is now small if we are far from the \ sun and large if we are close to the sun, in the \ range 0 = far away to &FF = extremely close, ouch, \ hot, hot, hot! ADC #30 \ Add the minimum cabin temperature of 30, so we get \ one of the following: \ \ * If the C flag is clear, A contains the cabin \ temperature, ranging from 30 to 255, that's hotter \ the closer we are to the sun \ \ * If the C flag is set, the addition has rolled over \ and the cabin temperature is over 255 STA CABTMP \ Store the updated cabin temperature BCS MA28 \ If the C flag is set then jump to MA28 to die, as \ our temperature is off the scale CMP #224 \ If the cabin temperature < 224 then jump to MA23 to BCC MA23 \ skip fuel scooping, as we aren't close enough LDA BST \ If we don't have fuel scoops fitted, jump to BA23 to BEQ MA23 \ skip fuel scooping, as we can't scoop without fuel \ scoops LDA DELT4+1 \ We are now successfully fuel scooping, so it's time LSR A \ to work out how much fuel we're scooping. Fetch the \ high byte of DELT4, which contains our current speed \ divided by 4, and halve it to get our current speed \ divided by 8 (so it's now a value between 1 and 5, as \ our speed is normally between 1 and 40). This gives \ us the amount of fuel that's being scooped in A, so \ the faster we go, the more fuel we scoop, and because \ the fuel levels are stored as 10 * the fuel in light \ years, that means we just scooped between 0.1 and 0.5 \ light years of free fuel ADC QQ14 \ Set A = A + the current fuel level * 10 (from QQ14) CMP new_range \ If A > new_range then set A = new_range (as new_range BCC P%+5 \ is the maximum fuel level for our current ship LDA new_range STA QQ14 \ Store the updated fuel level in QQ14 LDA #160 \ Set A to token 160 ("FUEL SCOOPS ON") .MA34 JSR MESS \ Print the token in A as an in-flight message
Name: Main flight loop (Part 16 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Process laser pulsing, E.C.M. energy drain, call stardust routine Deep dive: Program flow of the main game loop
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Process laser pulsing * Process E.C.M. energy drain * Jump to the stardust routine if we are in a space view * Return from the main flight loop
.MA23 LDA LAS2 \ If the current view has no laser, jump to MA16 to skip BEQ MA16 \ the following JSR read_0346 \ Get the value of the I/O processor's copy of LASCT CMP #8 \ If LASCT >= 8, jump to MA16 to skip the following, so BCS MA16 \ for a pulse laser with a LASCT between 8 and 10, the \ laser stays on, but for a LASCT of 7 or less it gets \ turned off and stays off until LASCT reaches zero and \ the next pulse can start (if the fire button is still \ being pressed) \ \ For pulse lasers, LASCT gets set to 10 in ma1 above, \ and it decrements every vertical sync (50 times a \ second), so this means it pulses five times a second, \ with the laser being on for the first 3/10 of each \ pulse and off for the rest of the pulse \ \ If this is a beam laser, LASCT is 0 so we always keep \ going here. This means the laser doesn't pulse, but it \ does get drawn and removed every cycle, in a slightly \ different place each time, so the beams still flicker \ around the screen JSR LASLI2 \ Redraw the existing laser lines, which has the effect \ of removing them from the screen LDA #0 \ Set LAS2 to 0 so if this is a pulse laser, it will STA LAS2 \ skip over the above until the next pulse (this has no \ effect if this is a beam laser) .MA16 LDA ECMP \ If our E.C.M is not on, skip to MA69, otherwise keep BEQ MA69 \ going to drain some energy JSR DENGY \ Call DENGY to deplete our energy banks by 1 BEQ MA70 \ If we have no energy left, jump to MA70 to turn our \ E.C.M. off .MA69 LDA ECMA \ If an E.C.M is going off (ours or an opponent's) then BEQ MA66 \ keep going, otherwise skip to MA66 DEC ECMA \ Decrement the E.C.M. countdown timer, and if it has BNE MA66 \ reached zero, keep going, otherwise skip to MA66 .MA70 JSR ECMOF \ If we get here then either we have either run out of \ energy, or the E.C.M. timer has run down, so switch \ off the E.C.M. .MA66 LDA QQ11 \ If this is not a space view (i.e. QQ11 is non-zero) BNE oh \ then jump to oh to return from the main flight loop \ (as oh is an RTS) JMP STARS \ This is a space view, so jump to the STARS routine to \ process the stardust, and return from the main flight \ loop using a tail call
Name: SPIN [Show more] Type: Subroutine Category: Universe Summary: Randomly spawn cargo from a destroyed ship
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 11 of 16) calls SPIN * Main flight loop (Part 16 of 16) calls via oh * Main flight loop (Part 11 of 16) calls via SPIN2

Arguments: Y The type of cargo to consider spawning (typically #PLT or #OIL)
Other entry points: oh Contains an RTS SPIN2 Remove any randomness: spawn cargo of a specific type (given in X), and always spawn the number given in A
.SPIN JSR DORND \ Fetch a random number, and jump to oh if it is BPL oh \ positive (50% chance) PHA \ Store A on the stack so we can restore it after the \ following transfers TYA \ Copy the cargo type from Y into A and X TAX PLA \ Restore A from the stack LDY #0 \ Fetch the first byte of the hit ship's blueprint, AND (XX0),Y \ which determines the maximum number of bits of \ debris shown when the ship is destroyed, and AND \ with the random number we just fetched AND #15 \ Reduce the random number in A to the range 0-15 .SPIN2 STA CNT \ Store the result in CNT, so CNT contains a random \ number between 0 and the maximum number of bits of \ debris that this ship will release when destroyed \ (to a maximum of 15 bits of debris) .spl BEQ oh \ We're going to go round a loop using CNT as a counter \ so this checks whether the counter is zero and jumps \ to oh when it gets there (which might be straight \ away) LDA #0 \ Call SFS1 to spawn the specified cargo from the now JSR SFS1 \ deceased parent ship, giving the spawned canister an \ AI flag of 0 (no AI, no E.C.M., non-hostile) DEC CNT \ Decrease the loop counter BNE spl+2 \ Jump back up to the LDA &0 instruction above (this BPL \ is effectively a JMP as CNT will never be negative) .oh RTS \ Return from the subroutine
Name: PIX1 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate (YY+1 SYL+Y) = (A P) + (S R) and draw stardust particle
Context: See this subroutine on its own page References: This subroutine is called as follows: * STARS1 calls PIX1 * STARS2 calls PIX1 * STARS6 calls PIX1

Calculate the following: (YY+1 SYL+Y) = (A P) + (S R) and draw a stardust particle at (X1,Y1) with distance ZZ.
Arguments: (A P) A is the angle ALPHA or BETA, P is always 0 (S R) YY(1 0) or YY(1 0) + Q * A Y Stardust particle number X1 The x-coordinate offset Y1 The y-coordinate offset ZZ The distance of the point (further away = smaller point)
.PIX1 JSR ADD \ Set (A X) = (A P) + (S R) STA YY+1 \ Set YY+1 to A, the high byte of the result TXA \ Set SYL+Y to X, the low byte of the result STA SYL,Y \ Fall through into PIX1 to draw the stardust particle \ at (X1,Y1)
Name: PIXEL2 [Show more] Type: Subroutine Category: Drawing pixels Summary: Draw a stardust particle relative to the screen centre
Context: See this subroutine on its own page References: This subroutine is called as follows: * FLIP calls PIXEL2 * nWq calls PIXEL2 * STARS1 calls PIXEL2 * STARS2 calls PIXEL2 * STARS6 calls PIXEL2

Draw a point (X1, Y1) from the middle of the screen with a size determined by a distance value. Used to draw stardust particles.
Arguments: X1 The x-coordinate offset Y1 The y-coordinate offset (positive means up the screen from the centre, negative means down the screen) ZZ The distance of the point (further away = smaller point)
.PIXEL2 LDA X1 \ Fetch the x-coordinate offset into A BPL PX1 \ If the x-coordinate offset is positive, jump to PX1 \ to skip the following negation EOR #%01111111 \ The x-coordinate offset is negative, so flip all the CLC \ bits apart from the sign bit and add 1, to convert it ADC #1 \ from a sign-magnitude number to a signed number .PX1 EOR #%10000000 \ Set X = X1 + 128 TAX \ \ So X is now the offset converted to an x-coordinate, \ centred on x-coordinate 128 LDA Y1 \ Fetch the y-coordinate offset into A and clear the AND #%01111111 \ sign bit, so A = |Y1| CMP #96 \ If |Y1| >= 96 then it's off the screen (as 96 is half BCS PX4 \ the screen height), so return from the subroutine (as \ PX4 contains an RTS) LDA Y1 \ Fetch the y-coordinate offset into A BPL PX2 \ If the y-coordinate offset is positive, jump to PX2 \ to skip the following negation EOR #%01111111 \ The y-coordinate offset is negative, so flip all the ADC #1 \ bits apart from the sign bit and subtract 1, to negate \ it to a positive number, i.e. A is now |Y1| .PX2 STA T \ Set A = 97 - Y1 LDA #97 \ SBC T \ So if Y is positive we display the point up from the \ centre at y-coordinate 97, while a negative Y means \ down from the centre JMP PIXEL \ Jump to PIXEL to draw the stardust at the screen \ coordinates in (X, A), returning from the subroutine \ using a tail call .PX4 RTS \ Return from the subroutine
Name: FLIP [Show more] Type: Subroutine Category: Stardust Summary: Reflect the stardust particles in the screen diagonal and redraw the stardust field
Context: See this subroutine on its own page References: This subroutine is called as follows: * LOOK1 calls FLIP

Swap the x- and y-coordinates of all the stardust particles and draw the new set of particles. Called by LOOK1 when we switch views. This is a quick way of making the stardust field in the new view feel different without having to generate a whole new field. If you look carefully at the stardust field when you switch views, you can just about see that the new field is a reflection of the previous field in the screen diagonal, i.e. in the line from bottom left to top right. This is the line where x = y when the origin is in the middle of the screen, and positive x and y are right and up, which is the coordinate system we use for stardust).
.FLIP LDY NOSTM \ Set Y to the current number of stardust particles, so \ we can use it as a counter through all the stardust .FLL1 LDX SY,Y \ Copy the Y-th particle's y-coordinate from SY+Y into X LDA SX,Y \ Copy the Y-th particle's x-coordinate from SX+Y into STA Y1 \ both Y1 and the particle's y-coordinate STA SY,Y TXA \ Copy the Y-th particle's original y-coordinate into STA X1 \ both X1 and the particle's x-coordinate, so the x- and STA SX,Y \ y-coordinates are now swapped and (X1, Y1) contains \ the particle's new coordinates LDA SZ,Y \ Fetch the Y-th particle's distance from SZ+Y into ZZ STA ZZ JSR PIXEL2 \ Draw a stardust particle at (X1,Y1) with distance ZZ DEY \ Decrement the counter to point to the next particle of \ stardust BNE FLL1 \ Loop back to FLL1 until we have moved all the stardust \ particles RTS \ Return from the subroutine
Name: STARS [Show more] Type: Subroutine Category: Stardust Summary: The main routine for processing the stardust
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 16 of 16) calls STARS

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

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

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

Add a doubled nosev vector coordinate, e.g. (nosev_y_hi nosev_y_lo) * 2, to an INWK coordinate, e.g. (x_sign x_hi x_lo), storing the result in the INWK coordinate. The axes used in each side of the addition are specified by the arguments X and Y. In the comments below, we document the routine as if we are doing the following, i.e. if X = 0 and Y = 11: (x_sign x_hi x_lo) = (x_sign x_hi x_lo) + (nosev_y_hi nosev_y_lo) * 2 as that way the variable names in the comments contain "x" and "y" to match the registers that specify the vector axis to use.
Arguments: X The coordinate to add, as follows: * If X = 0, add (x_sign x_hi x_lo) * If X = 3, add (y_sign y_hi y_lo) * If X = 6, add (z_sign z_hi z_lo) Y The vector to add, as follows: * If Y = 9, add (nosev_x_hi nosev_x_lo) * If Y = 11, add (nosev_y_hi nosev_y_lo) * If Y = 13, add (nosev_z_hi nosev_z_lo)
Returns: A The highest byte of the result with the sign cleared (e.g. |x_sign| when X = 0, etc.)
Other entry points: MA9 Contains an RTS
.MAS1 LDA INWK,Y \ Set K(2 1) = (nosev_y_hi nosev_y_lo) * 2 ASL A STA K+1 LDA INWK+1,Y ROL A STA K+2 LDA #0 \ Set K+3 bit 7 to the C flag, so the sign bit of the ROR A \ above result goes into K+3 STA K+3 JSR MVT3 \ Add (x_sign x_hi x_lo) to K(3 2 1) STA INWK+2,X \ Store the sign of the result in x_sign LDY K+1 \ Store K(2 1) in (x_hi x_lo) STY INWK,X LDY K+2 STY INWK+1,X AND #%01111111 \ Set A to the sign byte with the sign cleared, \ i.e. |x_sign| when X = 0 .MA9 RTS \ Return from the subroutine
Name: MAS2 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Calculate a cap on the maximum distance to the planet or sun
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 14 of 16) calls MAS2 * Main flight loop (Part 15 of 16) calls MAS2 * WARP calls MAS2 * Main flight loop (Part 15 of 16) calls via m * WARP calls via m

Given a value in Y that points to the start of a ship data block as an offset from K%, calculate the following: A = A OR x_sign OR y_sign OR z_sign and clear the sign bit of the result. The K% workspace contains the ship data blocks, so the offset in Y must be 0 or a multiple of NI% (as each block in K% contains NI% bytes). The result effectively contains a maximum cap of the three values (though it might not be one of the three input values - it's just guaranteed to be larger than all of them). If Y = 0 and A = 0, then this calculates the maximum cap of the highest byte containing the distance to the planet, as K%+2 = x_sign, K%+5 = y_sign and K%+8 = z_sign (the first slot in the K% workspace represents the planet).
Arguments: Y The offset from K% for the start of the ship data block to use
Returns: A A OR K%+2+Y OR K%+5+Y OR K%+8+Y, with bit 7 cleared
Other entry points: m Do not include A in the calculation
.m LDA #0 \ Set A = 0 and fall through into MAS2 to calculate the \ OR of the three bytes at K%+2+Y, K%+5+Y and K%+8+Y .MAS2 ORA K%+2,Y \ Set A = A OR x_sign OR y_sign OR z_sign ORA K%+5,Y ORA K%+8,Y AND #%01111111 \ Clear bit 7 in A RTS \ Return from the subroutine
Name: MAS3 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate A = x_hi^2 + y_hi^2 + z_hi^2 in the K% block
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 15 of 16) calls MAS3

Given a value in Y that points to the start of a ship data block as an offset from K%, calculate the following: A = x_hi^2 + y_hi^2 + z_hi^2 returning A = &FF if the calculation overflows a one-byte result. The K% workspace contains the ship data blocks, so the offset in Y must be 0 or a multiple of NI% (as each block in K% contains NI% bytes).
Arguments: Y The offset from K% for the start of the ship data block to use Returns A A = x_hi^2 + y_hi^2 + z_hi^2 A = &FF if the calculation overflows a one-byte result
.MAS3 LDA K%+1,Y \ Set (A P) = x_hi * x_hi JSR SQUA2 STA R \ Store A (high byte of result) in R LDA K%+4,Y \ Set (A P) = y_hi * y_hi JSR SQUA2 ADC R \ Add A (high byte of second result) to R BCS MA30 \ If the addition of the two high bytes caused a carry \ (i.e. they overflowed), jump to MA30 to return A = &FF STA R \ Store A (sum of the two high bytes) in R LDA K%+7,Y \ Set (A P) = z_hi * z_hi JSR SQUA2 ADC R \ Add A (high byte of third result) to R, so R now \ contains the sum of x_hi^2 + y_hi^2 + z_hi^2 BCC P%+4 \ If there is no carry, skip the following instruction \ to return straight from the subroutine .MA30 LDA #&FF \ The calculation has overflowed, so set A = &FF RTS \ Return from the subroutine
Name: MVT3 [Show more] Type: Subroutine Category: Moving Summary: Calculate K(3 2 1) = (x_sign x_hi x_lo) + K(3 2 1)
Context: See this subroutine on its own page References: This subroutine is called as follows: * MAS1 calls MVT3 * MV40 calls MVT3 * TAS1 calls MVT3

Add an INWK position coordinate - i.e. x, y or z - to K(3 2 1), like this: K(3 2 1) = (x_sign x_hi x_lo) + K(3 2 1) The INWK coordinate to add to K(3 2 1) is specified by X.
Arguments: X The coordinate to add to K(3 2 1), as follows: * If X = 0, add (x_sign x_hi x_lo) * If X = 3, add (y_sign y_hi y_lo) * If X = 6, add (z_sign z_hi z_lo)
Returns: A Contains a copy of the high byte of the result, K+3 X X is preserved
.MVT3 LDA K+3 \ Set S = K+3 STA S AND #%10000000 \ Set T = sign bit of K(3 2 1) STA T EOR INWK+2,X \ If x_sign has a different sign to K(3 2 1), jump to BMI MV13 \ MV13 to process the addition as a subtraction LDA K+1 \ Set K(3 2 1) = K(3 2 1) + (x_sign x_hi x_lo) CLC \ starting with the low bytes ADC INWK,X STA K+1 LDA K+2 \ Then the middle bytes ADC INWK+1,X STA K+2 LDA K+3 \ And finally the high bytes ADC INWK+2,X AND #%01111111 \ Setting the sign bit of K+3 to T, the original sign ORA T \ of K(3 2 1) STA K+3 RTS \ Return from the subroutine .MV13 LDA S \ Set S = |K+3| (i.e. K+3 with the sign bit cleared) AND #%01111111 STA S LDA INWK,X \ Set K(3 2 1) = (x_sign x_hi x_lo) - K(3 2 1) SEC \ starting with the low bytes SBC K+1 STA K+1 LDA INWK+1,X \ Then the middle bytes SBC K+2 STA K+2 LDA INWK+2,X \ And finally the high bytes, doing A = |x_sign| - |K+3| AND #%01111111 \ and setting the C flag for testing below SBC S ORA #%10000000 \ Set the sign bit of K+3 to the opposite sign of T, EOR T \ i.e. the opposite sign to the original K(3 2 1) STA K+3 BCS MV14 \ If the C flag is set, i.e. |x_sign| >= |K+3|, then \ the sign of K(3 2 1). In this case, we want the \ result to have the same sign as the largest argument, \ which is (x_sign x_hi x_lo), which we know has the \ opposite sign to K(3 2 1), and that's what we just set \ the sign of K(3 2 1) to... so we can jump to MV14 to \ return from the subroutine LDA #1 \ We need to swap the sign of the result in K(3 2 1), SBC K+1 \ which we do by calculating 0 - K(3 2 1), which we can STA K+1 \ do with 1 - C - K(3 2 1), as we know the C flag is \ clear. We start with the low bytes LDA #0 \ Then the middle bytes SBC K+2 STA K+2 LDA #0 \ And finally the high bytes SBC K+3 AND #%01111111 \ Set the sign bit of K+3 to the same sign as T, ORA T \ i.e. the same sign as the original K(3 2 1), as STA K+3 \ that's the largest argument .MV14 RTS \ Return from the subroutine
Name: ESCAPE [Show more] Type: Subroutine Category: Flight Summary: Launch our escape pod
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 3 of 16) calls ESCAPE

This routine displays our doomed Cobra Mk III disappearing off into the ether before arranging our replacement ship. Called when we press ESCAPE during flight and have an escape pod fitted.
.ESCAPE JSR RES2 \ Reset a number of flight variables and workspaces LDX #ESC \ Set the current ship type to an escape pod, so we can STX TYPE \ show it disappearing into the distance when we eject \ in our pod JSR FRS1 \ Call FRS1 to launch the escape pod straight ahead, \ like a missile launch, but with our ship instead LDA #16 \ Set the escape pod's byte #27 (speed) to 8 STA INWK+27 LDA #194 \ Set the escape pod's byte #30 (pitch counter) to 194, STA INWK+30 \ so it pitches up as we pull away LSR A \ Set the escape pod's byte #32 (AI flag) to %01100001, STA INWK+32 \ so it has no AI, and we can use this value as a \ counter to do the following loop 97 times .ESL1 JSR MVEIT_FLIGHT \ Call MVEIT_FLIGHT to move the escape pod in space JSR LL9_FLIGHT \ Call LL9 to draw the Cobra on-screen DEC INWK+32 \ Decrement the counter in byte #32 BNE ESL1 \ Loop back to keep moving the Cobra until the AI flag \ is 0, which gives it time to drift away from our pod JSR SCAN \ Call SCAN to remove the Cobra from the scanner (by \ redrawing it) LDA #0 \ Set A = 0 so we can use it to zero the contents of \ the cargo hold STA QQ20+16 \ We lose any alien items when using our escape pod, so \ set QQ20+16 to 0 (which is where they are stored) LDX #12 \ We lose all our cargo canisters when using our escape \ pod (i.e. all the cargo except gold, platinum and gem \ stones), so up a counter in X so we can zero cargo \ slots 0-12 in QQ20 .ESL2 STA QQ20,X \ Set the X-th byte of QQ20 to zero, so we no longer \ have any of item type X in the cargo hold DEX \ Decrement the counter BPL ESL2 \ Loop back to ESL2 until we have emptied the entire \ cargo hold STA FIST \ Launching an escape pod also clears our criminal \ record, so set our legal status in FIST to 0 ("clean") STA ESCP \ The escape pod is a one-use item, so set ESCP to 0 so \ we no longer have one fitted INC new_hold \ We just used our escape pod, and as it's a single-use \ item, we no longer have an escape pod, so increment \ the free space in our ship's hold, as the pod is no \ longer taking up space LDA new_range \ Our replacement ship is delivered with a full tank of STA QQ14 \ fuel, so fetch our current ship's hyperspace range \ from new_range and set the current fuel level in QQ14 \ to this value JSR update_pod \ Update the dashboard colours as we no longer have an \ escape pod JSR ping \ Set the target system to the current system (which \ will move the location in (QQ9, QQ10) to the current \ home system JSR TT111 \ Select the system closest to galactic coordinates \ (QQ9, QQ10) JSR jmp \ Set the current system to the selected system JMP GOIN \ Go to the docking bay (i.e. show the ship hangar \ screen) and return from the subroutine with a tail \ call
Save ELTJ.bin
PRINT "ELITE J" PRINT "Assembled at ", ~CODE_J% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_J%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_J% PRINT "S.2.ELTJ ", ~CODE_J%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_J% \SAVE "versions/disc/3-assembled-output/2.ELTJ.bin", CODE_J%, P%, LOAD%