Skip to navigation

Elite on the BBC Micro

Elite M parasite source [Elite-A]

ELITE M FILE
CODE_M% = P% LOAD_M% = LOAD% + P% - CODE%
Name: rand_posn [Show more] Type: Subroutine Category: Universe Summary: Set up the INWK workspace for a ship in a random ship position
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main game loop for flight (Part 2 of 6) calls rand_posn * Ze calls rand_posn

This routine sets up a new ship, with the following coordinates: * (x_sign x_hi x_lo) is a random number in the range -8191 to +8191: * x_sign is randomly positive or negative * x_hi is a random number in the range 0 to 31 * x_lo is a random number in the range 0 to 255 * (y_sign y_hi y_lo) is a random number in the range -8191 to +8191: * y_sign is randomly positive or negative * y_hi is a random number in the range 0 to 31 * y_lo is a random number in the range 0 to 255 * z_sign is a random number in the range 4352 to 20224: * z_sign is 0 (positive) * z_hi is a random number in the range 17 to 79 * z_lo is 0 In other words, the ship is randomly up, down, left or right, but is always in front of us. Returns: A A is set to a random number X X is set to a random number T1 T1 is set to a random number
.rand_posn JSR ZINF \ Call ZINF to reset the INWK ship workspace JSR DORND \ Set A and X to random numbers STA INWK \ Set x_lo to the random number in A STX INWK+3 \ Set y_lo to the random number in X STA T1 \ Store x_lo in T1 LSR A \ Set bit 7 of x_sign randomly (to bit 0 of the random ROR INWK+2 \ number in A), so the x-coordinate it is randomly \ positive or negative LSR A \ Set bit 7 of y_sign randomly (to bit 1 of the random ROR INWK+5 \ number in A), so the y-coordinate it is randomly \ positive or negative LSR A \ Set bits 0-4 of y_hi randomly (to bits 3-7 of the STA INWK+4 \ random number in A), so the high byte of the \ y-coordinate is in the range 0 to 31 TXA \ Set x_hi to the random number X, reduced to the range AND #31 \ 0 to 31 STA INWK+1 LDA #80 \ Set z_hi = 80 - x_hi - y_hi - 1 SBC INWK+1 \ SBC INWK+4 \ The C flag is clear as INWK+4 was 0 before the ROR STA INWK+7 \ above, so this sets z_hi in the range 17 to 79 \ (as x_hi and y_hi are both in the range 0 to 31) JMP DORND \ Set A and X to random numbers and return from the \ subroutine using a tail call
Name: THERE [Show more] Type: Subroutine Category: Missions Summary: Check whether we are in the Constrictor's system in mission 1
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main game loop for flight (Part 4 of 6) calls THERE

The stolen Constrictor is the target of mission 1. We finally track it down to the Orarra system in the second galaxy, which is at galactic coordinates (144, 33). This routine checks whether we are in this system and sets the C flag accordingly. Returns: C flag Set if we are in the Constrictor system, otherwise clear
.THERE LDX GCNT \ Set X = GCNT - 1 DEX BNE THEX \ If X is non-zero (i.e. GCNT is not 1, so we are not in \ the second galaxy), then jump to THEX LDA QQ0 \ Set A = the current system's galactic x-coordinate CMP #144 \ If A <> 144 then jump to THEX BNE THEX LDA QQ1 \ Set A = the current system's galactic y-coordinate CMP #33 \ If A = 33 then set the C flag BEQ THEX+1 \ If A = 33 then jump to THEX+1, so we return from the \ subroutine with the C flag set (otherwise we clear the \ C flag with the next instruction) .THEX CLC \ Clear the C flag RTS \ Return from the subroutine
Name: Ze [Show more] Type: Subroutine Category: Universe Summary: Initialise the INWK workspace to a hostile ship Deep dive: Fixing ship positions
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH calls Ze * GTHG calls Ze * Main game loop for flight (Part 3 of 6) calls Ze

Specifically, this routine does the following: * Reset the INWK ship workspace * Set the ship to a fair distance away in all axes, in front of us but randomly up or down, left or right * Give the ship a 4% chance of having E.C.M. * Set the ship to hostile, with AI enabled This routine also sets A, X, T1 and the C flag to random values. Note that because this routine uses the value of X returned by DORND, and X contains the value of A returned by the previous call to DORND, this routine does not necessarily set the new ship to a totally random location. See the deep dive on "Fixing ship positions" for details.
.Ze JSR rand_posn \ Call rand_posn to set up the INWK workspace for a ship \ in a random ship position, and set T1 to a random \ number CMP #245 \ Set the C flag if A >= 245 (4% chance) ROL A \ Set bit 0 of A to the C flag (i.e. there's a 4% \ chance of this ship having E.C.M.) ORA #%11000000 \ Set bits 6 and 7 of A, so the ship is hostile (bit 6 \ and has AI (bit 7) STA INWK+32 \ Store A in the AI flag of this ship \ Fall through into DORND2 to set A, X and the C flag \ randomly
Name: DORND2 [Show more] Type: Subroutine Category: Utility routines Summary: Generate random numbers, making sure the C flag doesn't affect the outcome Deep dive: Generating random numbers
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOEXP calls DORND2
.DORND2 CLC \ Clear the C flag so the value of the C flag on entry \ doesn't affect the outcome JMP DORND \ Jump to DORND to generate random numbers in A and X, \ returning from the subroutine using a tail call
Name: Main game loop for flight (Part 1 of 6) [Show more] Type: Subroutine Category: Main loop Summary: Spawn a trader (a Cobra Mk III, Python, Boa or Anaconda) Deep dive: Program flow of the main game loop Ship data blocks
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This is part of the main game loop. This is the loop for when we are in flight, while the main game loop for when we are docked is at TT100. This section covers the following: * Spawn a trader, i.e. a Cobra Mk III, Python, Boa or Anaconda, with a 50% chance of it having a missile, a 50% chance of it having an E.C.M., a 50% chance of it docking and being aggressive if attacked, a speed between 16 and 31, and a gentle clockwise roll We call this from within the main loop.
.MTT4 JSR DORND \ Set A and X to random numbers LSR A \ Clear bit 7 of our random number in A and set the C \ flag to bit 0 of A, which os random STA INWK+32 \ Store this in the ship's AI flag, so this ship does \ not have AI STA INWK+29 \ Store A in the ship's roll counter, giving it a \ clockwise roll (as bit 7 is clear), and a 1 in 127 \ chance of it having no damping ROL INWK+31 \ Set bit 0 of the ship's missile count ramdomly (as the \ C flag was set), giving the ship either no missiles or \ one missile AND #15 \ Set the ship speed to our random number, set to a STA INWK+27 \ minimum of 0 and a maximum of 15 JSR DORND \ Set A and X to random numbers, plus the C flag BMI nodo \ If A is negative (50% chance), jump to nodo to skip \ the following \ If we get here then we are going to spawn a ship that \ is minding its own business and trying to dock LDA INWK+32 \ Set bits 6 and 7 of the ship's AI flag, to make it ORA #%11000000 \ aggressive if attacked, and enable its AI STA INWK+32 LDX #%00010000 \ Set bit 4 of the ship's NEWB flags, to indicate that STX NEWB \ this ship is docking .nodo LDA #11 \ Call hordes to spawn a pack of ships from ship slots LDX #3 \ 11 to 14, which is where the trader ships live in the JMP hordes \ ship files
Name: Main game loop for flight (Part 2 of 6) [Show more] Type: Subroutine Category: Main loop Summary: Update the main loop counters
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.TT100_FLIGHT JSR M% \ Call M% to iterate through the main flight loop DEC DLY \ Decrement the delay counter in DLY, so any in-flight \ messages get removed once the counter reaches zero BEQ me2_flight \ If DLY is now 0, jump to me2_flight to remove any \ in-flight message from the space view, and once done, \ return to me3_flight below, skipping the following \ two instructions BPL me3_flight \ If DLY is positive, jump to me3_flight to skip the \ next instruction INC DLY \ If we get here, DLY is negative, so we have gone too \ and need to increment DLY back to 0 .me3_flight DEC MCNT \ Decrement the main loop counter in MCNT BEQ d_3fd4 \ If the counter has reached zero, which it will do \ every 256 main loops, skip the next JMP instruction \ (or to put it another way, if the counter hasn't \ reached zero, jump down to MLOOP, skipping all the \ following checks) .ytq_flight JMP MLOOP_FLIGHT \ Jump down to MLOOP_FLIGHT to do some end-of-loop \ tidying and restart the main loop \ We only get here once every 256 iterations of the \ main loop. If we aren't in witchspace and don't \ already have 3 or more asteroids in our local bubble, \ then this section has a 13% chance of spawning \ something benign (the other 87% of the time we jump \ down to consider spawning cops, pirates and bounty \ hunters) \ \ If we are in that 13%, then 50% of the time this will \ be a Cobra Mk III trader, and the other 50% of the \ time it will either be an asteroid (98.5% chance) or, \ very rarely, a cargo canister (1.5% chance) .me2_flight LDA MCH \ Fetch the token number of the current message into A JSR MESS \ Call MESS to print the token, which will remove it \ from the screen as printing uses EOR logic LDA #0 \ Set the delay in DLY to 0, so any new in-flight STA DLY \ messages will be shown instantly JMP me3_flight \ Jump back into the main spawning loop at me3_flight .d_3fd4 LDA MJ \ If we are in witchspace following a mis-jump, skip the BNE ytq_flight \ following by jumping down to MLOOP_FLIGHT (via \ ytq_flight above) JSR DORND \ Set A and X to random numbers CMP #51 \ If A >= 51 (80% chance), jump down to MTT1 to skip BCS MTT1 \ the spawning of an asteroid or cargo canister and \ potentially spawn something else LDA JUNK \ If we already have 3 or more bits of junk in the local CMP #3 \ bubble, jump down to MTT1 to skip the following and BCS MTT1 \ potentially spawn something else JSR rand_posn \ Call rand_posn to set up the INWK workspace for a ship \ in a random ship position BVS MTT4 \ If V flag is set (50% chance), jump up to MTT4 to \ spawn a trader ORA #%01101111 \ Take the random number in A and set bits 0-3 and 5-6, STA INWK+29 \ so the result has a 50% chance of being positive or \ negative, and a 50% chance of bits 0-6 being 127. \ Storing this number in the roll counter therefore \ gives our new ship a fast roll speed with a 50% \ chance of having no damping, plus a 50% chance of \ rolling clockwise or anti-clockwise LDA SSPR \ If we are inside the space station safe zone, jump BNE MLOOPS \ down to MLOOPS to skip the following and potentially \ spawn something else TXA \ Set A to the random X we set above, which we haven't BCS MTT2 \ used yet, and if the C flag is set (50% chance) jump \ down to MTT2 to skip the following AND #15 \ Set the ship speed to our random number, reduced to STA INWK+27 \ the range 0 to 15 BCC MTT3 \ Jump down to MTT3, skipping the following (this BCC \ is effectively a JMP as we know the C flag is clear, \ having passed through the BCS above) .MTT2 ORA #%01111111 \ Set bits 0-6 of A to 127, leaving bit 7 as random, so STA INWK+30 \ storing this number in the pitch counter means we have \ full pitch with no damping, with a 50% chance of \ pitching up or down .MTT3 JSR DORND \ Set A and X to random numbers CMP #10 \ If random A >= 10 (96% of the time), set the C flag AND #1 \ Reduce A to a random number that's 0 or 1 ADC #OIL \ Set A = #OIL + A + C, so there's a tiny chance of us \ spawning a cargo canister (#OIL) and an even chance of \ us spawning either a boulder (#OIL + 1) or an asteroid \ (#OIL + 2) BNE horde_plain \ Jump to horde_plain to spawn a whole pack of cargo \ canisters, boulders or asteroids, according to the \ value of A (the BNE is effectivey a JMP, as A will \ never be zero)
Name: Main game loop for flight (Part 3 of 6) [Show more] Type: Subroutine Category: Main loop Summary: Potentially spawn a cop, particularly if we've been bad Deep dive: Program flow of the main game loop Ship data blocks Fixing ship positions
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This section covers the following: * Potentially spawn a cop (in a Viper), very rarely if we have been good, more often if have been naughty, and very often if we have been properly bad
.MTT1 LDA SSPR \ If we are outside the space station's safe zone, skip BEQ P%+5 \ the following instruction .MLOOPS JMP MLOOP_FLIGHT \ Jump to MLOOP_FLIGHT to skip the following JSR BAD \ Call BAD to work out how much illegal contraband we \ are carrying in our hold (A is up to 40 for a \ standard hold crammed with contraband, up to 70 for \ an extended cargo hold full of narcotics and slaves) ASL A \ Double A to a maximum of 80 or 140 LDX MANY+COPS \ If there are no cops in the local bubble, skip the BEQ P%+5 \ next instruction ORA FIST \ There are cops in the vicinity and we've got a hold \ full of jail time, so OR the value in A with FIST to \ get a new value that is at least as high as both \ values, to reflect the fact that they have almost \ certainly scanned our ship STA T \ Store our badness level in T JSR Ze \ Call Ze to initialise INWK to a potentially hostile \ ship, and set A and X to random values \ \ Note that because Ze uses the value of X returned by \ DORND, and X contains the value of A returned by the \ previous call to DORND, this does not set the new ship \ to a totally random location. See the deep dive on \ "Fixing ship positions" for details CMP T \ If the random value in A >= our badness level, which BCS P%+8 \ will be the case unless we have been really, really \ bad, then skip the following three instructions (so \ if we are really bad, there's a higher chance of \ spawning a cop, otherwise we got away with it, for \ now) LDA #COPS \ Set A to the ship type for a cop, so the following \ call to hordes will spawn a pack of cops .horde_plain LDX #0 \ Jump to hordes to spawn a pack of ships of type A, BEQ hordes \ returning from the subroutine using a tail call (the \ BEQ is effectively a JMP as X is always zero) LDA MANY+COPS \ If we now have at least one cop in the local bubble, BNE MLOOPS \ jump down to MLOOPS to stop spawning, otherwise fall \ through into the next part to look at spawning \ something else
Name: Main game loop for flight (Part 4 of 6) [Show more] Type: Subroutine Category: Main loop Summary: Potentially spawn a lone bounty hunter, a Thargoid, or up to four pirates Deep dive: Program flow of the main game loop Ship data blocks Fixing ship positions
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main game loop for flight (Part 1 of 6) calls entry point hordes * Main game loop for flight (Part 3 of 6) calls entry point hordes

This section covers the following: * Potentially spawn (47% chance) either a pack of up to 8 bounty hunters, a Thargoid, or a pack of up to 8 pirates * Also potentially spawn a Constrictor if this is the mission 1 endgame, or Thargoids if mission 2 is in progress Other entry points: hordes Spawn a pack of ships, made up of ships from type A to type A + X, with the pack size normally being one to four ships, but rarely being up to eight ships
DEC EV \ Decrement EV, the extra vessels spawning delay, and if BPL MLOOPS \ it is still positive, jump to MLOOPS to stop spawning, \ so we only do the following when the EV counter runs \ down INC EV \ EV is negative, so bump it up again, setting it back \ to 0 LDA TP \ Fetch bits 2 and 3 of TP, which contain the status of AND #%00001100 \ mission 2 CMP #%00001000 \ If bit 3 is set and bit 2 is clear, keep going to BNE nopl \ spawn a Thargoid as we are transporting the plans in \ mission 2 and the Thargoids are trying to stop us, \ otherwise jump to nopl to skip spawning a Thargoid JSR DORND \ Set A and X to random numbers CMP #200 \ If the random number in A < 200 (78% chance), jump to BCC nopl \ nopl to skip spawning a Thargoid .fothg2 JSR GTHG \ Call GTHG to spawn a Thargoid ship and a Thargon \ companion .nopl JSR DORND \ Set A and X to random numbers LDY gov \ If the government of this system is 0 (anarchy), jump BEQ LABEL_2 \ straight to LABEL_2 to start spawning pirates or \ bounty hunters CMP #120 \ If the random number in A >= 120 (53% chance), jump to BCS MLOOPS \ MLOOPS to stop spawning (so there's a 47% chance of \ spawning pirates or bounty hunters) AND #7 \ Reduce the random number in A to the range 0-7, and CMP gov \ if A is less than government of this system, jump BCC MLOOPS \ to MLOOPS to stop spawning (so safer governments with \ larger gov numbers have a greater chance of jumping \ out, which is another way of saying that more \ dangerous systems spawn pirates and bounty hunters \ more often) .LABEL_2 \ Now to spawn a group of bounty hunters, a Thargoid or \ a pack of pirates CPX #100 \ If the random number in X >= 100 (61% chance), jump BCS mt1 \ to mt1 to spawn pirates, otherwise keep going to \ spawn bounty hunters or a Thargoid INC EV \ Increase the extra vessels spawning counter, to \ prevent the next attempt to spawn extra vessels AND #3 \ Set A = random number in the range 0-3, which we \ will now use to determine the type of ship ADC #25 \ Add A to 25 (we know the C flag is clear as we passed \ through the BCS above), so A is now a ship blueprint \ position in the range 25-28, which is where the bounty \ hunter ships live in the ship files TAY \ Copy the new ship type to Y JSR THERE \ Call THERE to see if we are in the Constrictor's \ system in mission 1 BCC NOCON \ If the C flag is clear then we are not in the \ Constrictor's system, so skip to NOCON LDA #%11111001 \ Set the AI flag of this ship so that it has E.C.M., STA INWK+32 \ has a very high aggression level of 28 out of 31, is \ hostile, and has AI enabled - nasty stuff! LDA TP \ Fetch bits 0 and 1 of TP, which contain the status of AND #%00000011 \ mission 1 LSR A \ Shift bit 0 into the C flag BCC NOCON \ If bit 0 is clear, skip to NOCON as mission 1 is not \ in progress ORA MANY+CON \ Bit 0 of A now contains bit 1 of TP, so this will be \ set if we have already completed mission 1, so this OR \ will be non-zero if we have either completed mission \ 1, or there is already a Constrictor in our local \ bubble of universe (in which case MANY+CON will be \ non-zero) BEQ YESCON \ If A = 0 then mission 1 is in progress, we haven't \ completed it yet, and there is no Constrictor in the \ vicinity, so jump to YESCON to spawn the Constrictor .NOCON TYA \ Set A to the new ship type in Y EQUB &2C \ Skip the next instruction by turning it into \ &2C &A9 &1F, or BIT &1FA9, which does nothing apart \ from affect the flags .YESCON LDA #CON \ If we jump straight here, we are in the mission 1 \ endgame and it's time to spawn the Constrictor, so \ set A to the Constrictor's type .focoug JSR NWSHP \ Spawn the new ship, whether it's a pirate, Thargoid or \ Constrictor .mj1 JMP MLOOP_FLIGHT \ Jump down to MLOOP_FLIGHT, as we are done spawning \ ships .mt1 LDA #17 \ Fall through into hordes to spawn a pack of ships from LDX #7 \ ship blueprint positions 17 to 24, which is where the \ pirate ships live in the ship blueprint files .hordes \ This routine spawns a pack of ships, made up of ships \ from type A to type A + X, with the pack size normally \ being one to four ships, but rarely being up to eight \ ships \ \ Let's call A the "base ship type", and X the "ship \ type range" STA horde_base+1 \ Modify the ADC instruction at horde_base so that it \ adds the base ship type given in A STX horde_mask+1 \ Modify the ADC instruction at horde_mask so that it \ ANDs the ship type range given in X JSR DORND \ Set A and X to random numbers CMP #248 \ If A >= 248 (3.1% chance), jump to horde_large so we BCS horde_large \ potentially spawn a larger pack (i.e. up to 8 ships) STA XX13 \ We are going to spawn a smaller pack (i.e. up to 4 TXA \ ships), so set: AND XX13 \ AND #3 \ A = A AND X AND 3 \ \ to give a random pack size of 0-3, with a greater \ chance of the smaller numbers than the larger ones \ (due to the AND). This will be our pack size, which \ won't be affected further by the following AND \ instruction .horde_large AND #7 \ If we are going to spawn a larger pack, reduce the \ random number in A to the range 0-7, with an equal \ chance of each of the numbers. This will be our pack \ size \ By this point our pack size is in A, and is either \ 0-3 or 0-7 STA EV \ Delay further spawnings by this number STA XX13 \ Store the number in XX13, the pack size counter .mt3 JSR DORND \ Set A and X to random numbers STA T \ Set A = A AND X TXA \ AND T \ which is in the range 0-255, but with a greater chance \ of being a smaller number (due to the AND) .horde_mask AND #&FF \ This instruction gets modified so that it ANDs the \ ship type range given in the argument X to the \ hordes routine, i.e. it turns into AND #type_range, \ which reduces our random number to be between 0 and \ the ship type range (so if we add this number to the \ base ship type, it will pick a random ship type from \ within the range A to A + X, where A and X are the \ arguments to the original call to hordes STA CPIR \ Set CPIR to our random number in the range 0 to \ the ship type .more LDA CPIR \ Set A to the ship type in CPIR CLC \ Clear the C flag for the addition below .horde_base ADC #0 \ This instruction gets modified so that it adds the \ ship type given in the argument A to the hordes \ routine, i.e. it turns into ADC #ship_type, so this \ sets A to a ship type in the range we want INC INWK+27 \ Increment the speed of the ship we are about to spawn, \ so later ships in the pack go faster INC INWK+1 \ Increment the x_hi coordinate of the ship we are about \ to spawn, so later ships in the pack are spread out to \ the sides INC INWK+4 \ Increment the y_hi coordinate of the ship we are about \ to spawn, so later ships in the pack are spread out \ to the top and bottom JSR NWSHP \ Try adding a new ship of type A to the local bubble CMP #24 \ This compares the value of A (which is set to the \ x_sign value of the spawned ship by NWSHP), but the \ result isn't used anywhere, as CMP affects the Z and N \ flags (not the C flag), and these same flags will be \ overwritten by the two DEC instructions below... so \ instruction has no effect BCS P%+7 \ If the ship was successfully added, skip the following \ two instructions DEC CPIR \ The ship wasn't added, which might be because the ship \ blueprint for this ship type isn't in the currently \ loaded ship blueprints file, so decrement CPIR to \ point to the previous ship type, so we can try \ spawning that type of ship instead BPL more \ Loop back to more to have another go at spawning this \ ship, until CPIR is 0, in which case we have tried \ spawning all the ship types in the range, so give up \ and move on to the next pirate to spawn DEC XX13 \ Decrement the pack size counter BPL mt3 \ If we need more ships, loop back up to mt3, \ otherwise we are done spawning, so fall through into \ the end of the main loop at MLOOP
Name: Main game loop for flight (Part 5 of 6) [Show more] Type: Subroutine Category: Main loop Summary: Cool down lasers, make calls to update the dashboard
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.MLOOP_FLIGHT LDX #&FF \ Set the stack pointer to &01FF, which is the standard TXS \ location for the 6502 stack, so this instruction \ effectively resets the stack LDX GNTMP \ If the laser temperature in GNTMP is non-zero, BEQ EE20 \ decrement it (i.e. cool it down a bit) DEC GNTMP .EE20 JSR DIALS \ Call DIALS to update the dashboard JSR COMPAS \ Call COMPAS to update the compass LDA QQ11 \ If this is a space view, skip the following two BEQ P%+7 \ instructions (i.e. jump to JSR TT17 below) \AND PATG \ These instructions are commented out in the original \LSR A \ source \BCS d_40f8 LDY #2 \ Wait for 2/50 of a second (0.04 seconds), to slow the JSR DELAY \ main loop down a bit \JSR WSCAN \ This instruction is commented out in the original \ source JSR DOKEY_FLIGHT \ Scan the keyboard for flight controls and pause keys, \ (or the equivalent on joystick) and update the key \ logger, setting KL to the key pressed JSR chk_dirn \ Call chk_dirn to set the movement variables based on \ the current state of the key logger
Name: Main game loop for flight (Part 6 of 6) [Show more] Type: Subroutine Category: Main loop Summary: Process non-flight key presses (red function keys, docked keys)
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.FRCE_FLIGHT PHA \ Store the key to "press" in A on the stack LDA QQ22+1 \ Fetch QQ22+1, which contains the number that's shown \ on-screen during hyperspace countdown BNE d_locked \ If the hyperspace countdown is non-zero, jump to \ d_locked so the key does not get "pressed" PLA \ Retrieve the key to "press" from the stack into A so \ we can now process it JSR TT102 \ Call TT102 to process the key pressed in A JMP TT100_FLIGHT \ Otherwise jump to TT100_FLIGHT to restart the main \ loop from the start .d_locked PLA \ Retrieve the key to "press" from the stack into A JSR TT107 \ Call TT107 to progress the countdown of the hyperspace \ counter JMP TT100_FLIGHT \ Jump to TT100_FLIGHT to restart the main loop from \ the start
Name: TT102 [Show more] Type: Subroutine Category: Keyboard Summary: Process function key, save key, hyperspace and chart key presses
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main game loop (Part 6 of 6) calls TT102 * Main game loop for flight (Part 6 of 6) calls TT102 * HME2 calls entry point T95 * Main game loop for flight (Part 3 of 6) calls entry point BAD * TT110 calls entry point BAD * Main game loop for flight (Part 6 of 6) calls entry point TT107

Process function key presses, plus "@" (save commander), "H" (hyperspace), "D" (show distance to system) and "O" (move chart cursor back to current system). We can also pass cursor position deltas in X and Y to indicate that the cursor keys or joystick have been used (i.e. the values that are returned by routine TT17). Arguments: A The internal key number of the key pressed (see p.142 of the Advanced User Guide for a list of internal key numbers) X The amount to move the crosshairs in the x-axis Y The amount to move the crosshairs in the y-axis Other entry points: BAD Work out how bad we are from the amount of contraband in our hold T95 Print the distance to the selected system TT107 Progress the countdown of the hyperspace counter
.TT102 CMP #f8 \ If red key f8 was pressed, jump to STATUS to show the BNE P%+5 \ Status Mode screen, returning from the subroutine JMP STATUS \ using a tail call CMP #f4 \ If red key f4 was pressed, jump to TT22 to show the BNE P%+5 \ Long-range Chart, returning from the subroutine using JMP TT22 \ a tail call CMP #f5 \ If red key f5 was pressed, jump to TT23 to show the BNE P%+5 \ Short-range Chart, returning from the subroutine using JMP TT23 \ a tail call CMP #f6 \ If red key f6 was pressed, call TT111 to select the BNE TT92 \ system nearest to galactic coordinates (QQ9, QQ10) JSR TT111 \ (the location of the chart crosshairs) and set ZZ to JMP TT25 \ the system number, and then jump to TT25 to show the \ Data on System screen (along with an extended system \ description for the system in ZZ if we're docked), \ returning from the subroutine using a tail call .TT92 CMP #f9 \ If red key f9 was pressed, jump to TT213 to show the BNE P%+5 \ Inventory screen, returning from the subroutine JMP TT213 \ using a tail call CMP #f7 \ If red key f7 was pressed, jump to TT167 to show the BNE P%+5 \ Market Price screen, returning from the subroutine JMP TT167 \ using a tail call .LABEL_3 CMP #&32 \ If "D" was pressed, jump to T95 to print the distance BEQ T95 \ to a system (if we are in one of the chart screens) CMP #&43 \ If "F" was not pressed, jump down to HME1, otherwise BNE HME1 \ keep going to process searching for systems (when \ docked) or toggle the compass display (when flying) LDA QQ11 \ If the current view is a chart (QQ11 = 64 or 128), AND #%11000000 \ keep going, otherwise jump to n_finder BEQ n_finder LDA dockedp \ If dockedp is non-zero, then we are not docked and BNE t95 \ can't search for a system, so return from the \ subroutine (as t95 contains an RTS) JMP HME2 \ Jump to HME2 to let us search for a system, returning \ from the subroutine using a tail call .n_finder LDA dockedp \ If dockedp is zero, then we are docked and we can't BEQ t95 \ change the compass configuration, so return from the \ subroutine (as t95 contains an RTS) LDA finder \ Set the value of A to finder, which determines whether \ the compass is configured to show the sun or the \ planet EOR #NI% \ The value of finder is 0 (show the planet) or NI% \ (show the sun), so this toggles the value between the \ two STA finder \ Store the toggled value in finder JMP WSCAN \ Jump to WSCAN to wait for the vertical sync and return \ from the subroutine using a tail call .t95 RTS \ Return from the subroutine .HME1 CMP #&36 \ If "O" was pressed, do the following, otherwise skip BNE not_home \ to not_home to continue checking key presses LDA QQ11 \ If both bits 6 or 7 of the view number are clear - so AND #%11000000 \ this is not the Short-range or Long-range Chart - BEQ t95 \ then jump to t95 to return from the subroutine, \ otherwise do the following three jumps JSR TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10), \ which will erase the crosshairs currently there JSR ping \ Set the target system to the current system (which \ will move the location in (QQ9, QQ10) to the current \ home system JMP TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10), \ which will draw the crosshairs at our current home \ system, and return from the subroutine using a tail \ call .not_home CMP #&21 \ If "W" was pressed, continue on to move the crosshairs BNE ee2 \ to the special cargo destination, otherwise skip to \ ee2 to continue LDA QQ11 \ If both bits 6 or 7 of the view number are clear - so AND #%11000000 \ this is not the Short-range or Long-range Chart - BEQ t95 \ then jump to t95 to return from the subroutine, \ otherwise do the following three jumps LDA cmdr_cour \ If there is no special cargo delivery mission in ORA cmdr_cour+1 \ progress, then the mission timer in cmdr_cour(1 0) BEQ t95 \ will be zero, so return from the subroutine (as t95 \ contains an RTS) JSR TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10), \ which will erase the crosshairs currently there LDA cmdr_courx \ Set the galactic coordinates in (QQ9, QQ10) to the STA QQ9 \ current special cargo delivery destination in LDA cmdr_coury \ (cmdr_courx, cmdr_coury) STA QQ10 JSR TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10), \ which will draw the crosshairs at our current home \ system .T95 \ If we get here, "D" was pressed, so we need to show \ the distance to the selected system (if we are in a \ chart view) LDA QQ11 \ If the current view is a chart (QQ11 = 64 or 128), AND #%11000000 \ keep going, otherwise return from the subroutine (as BEQ t95 \ t95 contains an RTS) JSR hm \ Call hm to move the crosshairs to the target system \ in (QQ9, QQ10), returning with A = 0 STA QQ17 \ Set QQ17 = 0 to switch to ALL CAPS JSR cpl \ Print control code 3 (the selected system name) LDA #%10000000 \ Set bit 7 of QQ17 to switch to Sentence Case, with the STA QQ17 \ next letter in capitals LDA #1 \ Move the text cursor to column 1 and down one line STA XC \ (in other words, to the start of the next line) INC YC JMP TT146 \ Print the distance to the selected system and return \ from the subroutine using a tail call .ee2 BIT dockedp \ If bit 7 of dockedp is set, then we are not docked, so BMI flying \ jump to flying CMP #f0 \ If red key f0 was not pressed, jump to fvw to check BNE fvw \ for the next key JSR CTRL \ Red key f0 was pressed, so check whether CTRL was BMI jump_stay \ also pressed, and if so, jump to jump_stay to skip the \ following instruction JMP RSHIPS \ Red key f0 was pressed on its own, so jump to RSHIPS \ to launch our ship, returning from the subroutine \ using a tail call .jump_stay JMP stay_here \ CTRL-f0 was pressed, so jump to stay_here to pay the \ docking fee and refresh prices .fvw CMP #f3 \ If red key f3 was pressed, jump to EQSHP to show the BNE P%+5 \ Equip Ship screen, returning from the subroutine using JMP EQSHP \ a tail call CMP #f1 \ If red key f1 was pressed, jump to TT219 to show the BNE P%+5 \ Buy Cargo screen, returning from the subroutine using JMP TT219 \ a tail call CMP #&47 \ If "@" was not pressed, skip to nosave BNE nosave JSR SVE \ "@" was pressed, so call SVE to show the disc access \ menu BCC P%+5 \ If the C flag was set by SVE, then we loaded a new JMP QU5 \ commander file, so jump to QU5 to restart the game \ with the newly loaded commander JMP BAY \ Otherwise the C flag was clear, so jump to BAY to go \ to the docking bay (i.e. show the Status Mode screen) .nosave CMP #f2 \ If red key f2 was pressed, jump to TT208 to show the BNE not_sell \ Sell Cargo screen, returning from the subroutine using JMP TT208 \ a tail call .not_sell CMP #&54 \ If "H" was not pressed, jump to NWDAV5 to skip the BNE NWDAV5 \ following JSR CLYNS \ "H" was pressed, so clear the bottom three text rows \ of the upper screen, and move the text cursor to \ column 1 on row 21, i.e. the start of the top row of \ the three bottom rows LDA #15 \ Move the text cursor to column 15 (the middle of the STA XC \ screen) LDA #205 \ Print extended token 205 ("DOCKED") and return from JMP DETOK \ the subroutine using a tail call .flying CMP #&20 \ If "D" was pressed, jump to TT110 to print the BNE P%+5 \ distance to a system (if we are in one of the chart JMP TT110 \ screens) CMP #f1 \ If the key pressed is < red key f1 or > red key f3, BCC d_4143 \ jump to d_4143 (so only do the following if the key CMP #f3+1 \ pressed is f1, f2 or f3) BCS d_4143 AND #3 \ If we get here then we are either in space, or we are TAX \ docked and none of f1-f3 were pressed, so we can now JMP LOOK1 \ process f1-f3 with their in-flight functions, i.e. \ switching space views \ \ A will contain &71, &72 or &73 (for f1, f2 or f3), so \ set X to the last digit (1, 2 or 3) and jump to LOOK1 \ to switch to view X (rear, left or right), returning \ from the subroutine using a tail call .d_4143 CMP #&54 \ If "H" was not pressed, jump to NWDAV5 to skip the BNE NWDAV5 \ following JMP hyp \ Jump to hyp to do a hyperspace jump (if we are in \ space), returning from the subroutine using a tail \ call .TT107 LDA QQ22+1 \ If the on-screen hyperspace counter is zero, return BEQ d_418a \ from the subroutine (as d_418a contains an RTS), as we \ are not currently counting down to a hyperspace jump DEC QQ22 \ Decrement the internal hyperspace counter BNE d_418a \ If the internal hyperspace counter is still non-zero, \ then we are still counting down, so return from the \ subroutine (as d_418a contains an RTS) \ If we get here then the internal hyperspace counter \ has just reached zero and it wasn't zero before, so \ we need to reduce the on-screen counter and update \ the screen. We do this by first printing the next \ number in the countdown sequence, and then printing \ the old number, which will erase the old number \ and display the new one because printing uses EOR \ logic LDX QQ22+1 \ Set X = the on-screen hyperspace counter - 1 DEX \ (i.e. the next number in the sequence) JSR ee3 \ Print the 8-bit number in X at text location (0, 1) LDA #5 \ Reset the internal hyperspace counter to 5 STA QQ22 LDX QQ22+1 \ Set X = the on-screen hyperspace counter (i.e. the \ current number in the sequence, which is already \ shown on-screen) JSR ee3 \ Print the 8-bit number in X at text location (0, 1), \ i.e. print the hyperspace countdown in the top-left \ corner DEC QQ22+1 \ Decrement the on-screen hyperspace countdown BNE d_418a \ If the countdown is not yet at zero, return from the \ subroutine (as d_418a contains an RTS) JMP TT18 \ Otherwise the countdown has finished, so jump to TT18 \ to do a hyperspace jump, returning from the subroutine \ using a tail call .BAD LDA QQ20+3 \ Set A to the number of tonnes of slaves in the hold CLC \ Clear the C flag so we can do addition without the \ C flag affecting the result ADC QQ20+6 \ Add the number of tonnes of narcotics in the hold ASL A \ Double the result and add the number of tonnes of ADC QQ20+10 \ firearms in the hold .d_418a RTS \ Return from the subroutine .NWDAV5 LDA QQ11 \ If the current view is a chart (QQ11 = 64 or 128), AND #%11000000 \ keep going, otherwise return from the subroutine (as BEQ d_418a \ d_418a contains an RTS) JMP TT16 \ Jump to TT16 to move the crosshairs by the amount in X \ and Y, which were passed to this subroutine as \ arguments, and return from the subroutine using a tail \ call
Name: FAROF [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Compare x_hi, y_hi and z_hi with 224
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 12 of 16) calls FAROF

Compare x_hi, y_hi and z_hi with 224, and set the C flag if all three <= 224, otherwise clear the C flag. Returns: C flag Set if x_hi <= 224 and y_hi <= 224 and z_hi <= 224 Clear otherwise (i.e. if any one of them are bigger than 224)
.FAROF LDA #224 \ Set A = 224 and fall through into FAROF2 to do the \ comparison
Name: FAROF2 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Compare x_hi, y_hi and z_hi with A
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 14 of 16) calls FAROF2

Compare x_hi, y_hi and z_hi with A, and set the C flag if all three <= A, otherwise clear the C flag. Returns: C flag Set if x_hi <= A and y_hi <= A and z_hi <= A Clear otherwise (i.e. if any one of them are bigger than A)
.FAROF2 CMP INWK+1 \ If A < x_hi, C will be clear so jump to FA1 to BCC FA1 \ return from the subroutine with C clear, otherwise \ C will be set so move on to the next one CMP INWK+4 \ If A < y_hi, C will be clear so jump to FA1 to BCC FA1 \ return from the subroutine with C clear, otherwise \ C will be set so move on to the next one CMP INWK+7 \ If A < z_hi, C will be clear, otherwise C will be set .FA1 RTS \ Return from the subroutine
Name: MAS4 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Calculate a cap on the maximum distance to a ship
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 7 of 16) calls MAS4 * TACTICS (Part 1 of 7) calls MAS4 * TACTICS (Part 6 of 7) calls MAS4

Logical OR the value in A with the high bytes of the ship's position (x_hi, y_hi and z_hi). Returns: A A OR x_hi OR y_hi OR z_hi
.MAS4 ORA INWK+1 \ OR A with x_hi, y_hi and z_hi ORA INWK+4 ORA INWK+7 RTS \ Return from the subroutine
Name: DEATH [Show more] Type: Subroutine Category: Start and end Summary: Display the death screen
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 15 of 16) calls DEATH * OOPS calls DEATH

We have been killed, so display the chaos of our destruction above a "GAME OVER" sign, and clean up the mess ready for the next attempt.
.DEATH JSR EXNO3 \ Make the sound of us dying JSR RES2 \ Reset a number of flight variables and workspaces ASL DELTA \ Divide our speed in DELTA by 4 ASL DELTA LDX #24 \ Set the screen to only show 24 text rows, which hides JSR DET1 \ the dashboard, setting A to 6 in the process JSR TT66 \ Clear the top part of the screen, draw a white border, \ and set the current view type in QQ11 to 6 (death \ screen) JSR BOX \ Call BOX to redraw the same white border (BOX is part \ of TT66), which removes the border as it is drawn \ using EOR logic JSR nWq \ Create a cloud of stardust containing the correct \ number of dust particles (i.e. NOSTM of them) LDA #12 \ Move the text cursor to column 12 on row 12 STA YC STA XC LDA #146 \ Print recursive token 146 ("{all caps}GAME OVER") JSR ex .D1 JSR Ze \ Call Ze to initialise INWK to a potentially hostile \ ship, and set A and X to random values LSR A \ Set A = A / 4, so A is now between 0 and 63, and LSR A \ store in byte #0 (x_lo) STA INWK LDY #0 \ Set the following to 0: the current view in QQ11 STY QQ11 \ (space view), x_hi, y_hi, z_hi and the AI flag (no AI STY INWK+1 \ or E.C.M. and not hostile) STY INWK+4 STY INWK+7 STY INWK+32 DEY \ Set Y = 255 STY MCNT \ Reset the main loop counter to 255, so all timer-based \ calls will be stopped EOR #%00101010 \ Flip bits 1, 3 and 5 in A (x_lo) to get another number STA INWK+3 \ between 48 and 63, and store in byte #3 (y_lo) ORA #%01010000 \ Set bits 4 and 6 of A to bump it up to between 112 and STA INWK+6 \ 127, and store in byte #6 (z_lo) TYA \ Tell the I/O processor to set its copy of LASCT to JSR write_0346 \ 255, to act as a counter in the D2 loop below, so this \ setting determines how long the death animation lasts \ (it's 5.1 seconds, as LASCT is decremented every \ vertical sync, or 50 times a second, and \ 255 / 50 = 5.1) TXA \ Set A to the random number in X and keep bits 0-3 and AND #%10001111 \ the bit 7 to get a number between -15 and +15, and STA INWK+29 \ store in byte #29 (roll counter) to give our ship a \ gentle roll with damping ROR A \ The C flag is randomly set from the above call to Ze, AND #%10000111 \ so this sets A to a number between -7 and +7, which STA INWK+30 \ we store in byte #30 (the pitch counter) to give our \ ship a very gentle pitch with damping LDX #OIL \ Set X to #OIL, the ship type for a cargo canister BCC D3 \ If the C flag is clear, which will be random following \ the above call to Ze, jump to D3 to skip the following \ instruction DEX \ Decrement X, which sets it to #PLT, the ship type for \ an alloy plate .D3 JSR fq1 \ Call fq1 with X set to #OIL or #PLT, which adds a new \ cargo canister or alloy plate to our local bubble of \ universe and points it away from us with double DELTA \ speed (i.e. 6, as DELTA was set to 3 by the call to \ RES2 above). INF is set to point to the new arrival's \ ship data block in K% JSR DORND \ Set A and X to random numbers and extract bit 7 from A AND #%10000000 LDY #31 \ Store this in byte #31 of the ship's data block, so it STA (INF),Y \ has a 50% chance of marking our new arrival as being \ killed (so it will explode) LDA FRIN+4 \ The call we made to RES2 before we entered the loop at BEQ D1 \ D1 will have reset all the ship slots at FRIN, so this \ checks to see if the fifth slot is empty, and if it \ is we loop back to D1 to add another canister, until \ we have added five of them JSR U% \ Clear the key logger, which also sets A = 0 STA DELTA \ Set our speed in DELTA to 0, as we aren't going \ anywhere any more .D2 JSR M% \ Call the M% routine to do the main flight loop once, \ which will display our exploding canister scene and \ move everything about JSR read_0346 \ Get the value of the I/O processor's copy of LASCT BNE D2 \ Loop back to D2 to run the main flight loop until \ LASCT reaches zero (which will take 5.1 seconds, as \ explained above) LDX #31 \ Set the screen to show all 31 text rows, which shows JSR DET1 \ the dashboard JMP DEATH2 \ Jump to DEATH2 to reset and restart the game
Name: RSHIPS [Show more] Type: Subroutine Category: Loader Summary: Launch from the station, load a new set of ship blueprints and jump into the main game loop
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT102 calls RSHIPS
.RSHIPS JSR LSHIPS \ Call LSHIPS to populate the ship blueprints table \ with a random selection of ships JSR RESET \ Call RESET to reset most variables LDA #&FF \ Set QQ1 to &FF to indicate we are docked, so when STA QQ12 \ we reach TT110 after calling FRCE below, it skips the \ launch tunnel STA QQ11 \ Set the view number to a non-zero value, so when we \ reach LOOK1 after calling FRCE below, it sets up a \ new space view STA dockedp \ Set dockedp to &FF to indicate that we are no longer \ docked LDA #f0 \ Jump into the main game loop at FRCE_FLIGHT, setting JMP FRCE_FLIGHT \ the key "pressed" to red key f0 (so we launch from the \ station)
Name: LSHIPS [Show more] Type: Subroutine Category: Universe Summary: Populate the ship blueprints table at XX21 with a random selection of ships and set the compass to point to the planet Deep dive: Ship blueprints in Elite-A
Context: See this subroutine on its own page References: This subroutine is called as follows: * RSHIPS calls LSHIPS * TT18 calls LSHIPS * MJP calls entry point SHIPinA

Other entry points: SHIPinA Populate the ship blueprints table but without setting the compass to show the planet
.LSHIPS LDA #0 \ Set finder to 0, so the compass shows the planet STA finder .SHIPinA LDX #0 \ The first task is to fill blueprint position 2, which \ contains the space station blueprint, so set X = 0, \ which is the ship_list ship type for a Dodo space \ station LDA tek \ If the current system's tech level is 10 or more, then CMP #10 \ skip the following instruction, as we already have the BCS mix_station \ correct space station type in X INX \ Increment X to 1, the ship_list ship type for a \ Coriolis space station .mix_station LDY #2 \ Install a ship of type X (Dodo or Coriolis station) JSR install_ship \ into blueprint position 2 LDY #9 \ The next blueprint position we need to fill is number \ 9, for the shuttle, so set Y to point to this position \ so we can use it as a counter, starting at position 9 \ and working our way up to position 28 (as positions 29 \ to 31 are already filled) .mix_retry LDA #0 \ Set X1 = 0 to act as a failure counter, so we can STA X1 \ have 256 failed attempts to fill each blueprint \ position before giving up and leaving it blank .mix_match JSR DORND \ Set A and X to random numbers CMP #ship_total \ If A >= #ship_total then it is too big to be a BCS mix_match \ ship_list ship type, so loop back to choose another \ random number until it is a valid ship type ASL A \ Set Y1 = A * 4, so we can use it a random index into ASL A \ the ship_bits table, which has four bytes in each STA Y1 \ entry \ Y1 now contains the ship_list ship type of the ship we \ are going to try installing into position Y, just \ multiplied by 4 so it can be used as a four-byte index \ \ We now want to check the ship_bits table to see if the \ ship type in Y1 is allowed in ship blueprint position \ Y. The table contains a 32-bit number for each ship \ type, with the corresponding bits set for allowed \ positions, so if a ship type were allowed in positions \ 11 and 17, for example, only bits 11 and 17 would be \ set in the ship_bits table entry for that type \ \ To do this, we work out which of the four bytes in the \ 32-bit number contains the bit we want to match, and \ then create a byte that has the correct bit set for \ that particular byte, so we can AND them together to \ see if there is a match \ \ For example, say we are trying to populate position 17 \ (so Y = 17), and we want to know whether our ship of \ type Y1 is allowed in this position. We know what we \ need to look at the 32-bit number in row Y1 of the \ ship_bits table, and we also know that bit 17 appears \ in the third byte of a 32-bit number (i.e. byte #2), \ so we can set an index in X: \ \ X = Y1 + 2 \ \ and we can use this as an index into the ship_bits \ table to fetch the relevant byte from the 32-bit \ number we want to match, which is at this location: \ \ ship_bits + X \ \ Given this byte, we need to check the relevant bit. We \ know that bit 17 of a 32-bit number corresponds to the \ second bit of the third byte, so if we create a byte \ with that bit set: \ \ A = %00000010 \ \ then we can AND the two together, and if we get a \ non-zero result, we know that bit 17 in the 32-bit \ number is set: \ \ result = ?(ship_bits + X) AND A \ \ where ?(addr) is the contents of address addr \ \ This is what we now do, starting with the byte in A, \ which we can grab from the lookup table at mix_bits, \ then calculating X, before extracting the relevant \ byte from the ship_bits table and performing the AND TYA \ Set X = Y mod 8, so as Y works through the positions AND #7 \ from 0 to 31, making its way from bit 0 to bit 31 of TAX \ the 32-bit number, X represents the position of the \ current bit within each of the four bytes that make up \ the 32-bit number LDA mix_bits,X \ Set A to the X-th byte from mix_bits, which contains a \ table of bytes with the relevant bit sit for each \ value of X (so the value at mix_bits + X has bit X \ set). This gives us our value of A in the above \ explanation, so if we were looking to populate \ blueprint position 17, A would now be %00000010 LDX Y1 \ Set X to the ship type we are going to try to install \ We now want to add to X to point to the correct byte \ within the 32-bit number, depending on the blueprint \ position in Y that we are trying to fill: \ \ * If Y is in the range 0 to 7, X = X + 0 \ * If Y is in the range 8 to 15, X = X + 1 \ * If Y is in the range 16 to 23, X = X + 2 \ * If Y is in the range 24 to 28, X = X + 3 \ \ note that because we are starting at position 9, we \ can ignore the first case. In our above example, we \ are filling positon 17, so we would add 2 to X CPY #16 \ If the blueprint position we are trying to fill is BCC mix_byte2 \ less than 16, jump to mix_byte2 so we increment X once CPY #24 \ If the blueprint position we are trying to fill is BCC mix_byte3 \ less than 24, jump to mix_byte3 so we increment X \ twice INX \ Increment X as Y is in the range 24 to 28 .mix_byte3 INX \ Increment X as Y is in the range 16 to 28 .mix_byte2 INX \ Increment X as Y is in the range 9 to 28 \ We now have the correct values of A and X, as per the \ above calculation, so it's time to do the AND logic: \ \ result = ?(ship_bits + X) AND A \ \ which we can do easily in assembly language: \ \ AND ship_bits,X \ \ followed by a BEQ to check whether the result is zero AND ship_bits,X \ If the X-th byte of ship_bits does not have the same BEQ mix_fail \ bit set as our moving bit counter in A, jump to \ mix_fail to have another go at filling this blueprint \ position, as the ship in Y1 is not allowed in \ blueprint position Y .mix_try \ If we get here then the ship in Y1 is allowed in \ blueprint position Y, so now we decide whether or not \ to go ahead, depending on the probability figure for \ this ship type, which we fetch from the first entry in \ the ship_bytes table for this ship type JSR DORND \ Set A and X to random numbers LDX Y1 \ Set X to the ship type we are going to try to install CMP ship_bytes,X \ If A < the X-th entry in ship_bytes, i.e. it is less BCC mix_ok \ than the first byte in the ship_bytes entry for this \ ship type, jump to mix_ok to install this ship into \ the blueprint position. So, for example, if this ship \ type has a value of 100 as the first byte in its entry \ in the ship_bytes table, which is the case for the \ Mamba and Sidewinder, then we only add it to this \ blueprint position if A < 100, or a 39% chance. The \ much rarer Dragon, meanwhile, has a ship_bytes entry \ of 3, so the calculation is A < 3, or a 1.2% chance .mix_fail \ If we get here then either this ship isn't allowed in \ this position, or it failed the probability test \ above, so we decrement the failure counter and loop \ back for another go (up to a maximum number of 256 \ attempts for each position) DEC X1 \ Decrement the failure counter in X1 BNE mix_match \ If we haven't run out of failure attempts, jump back \ to mix_match to have another go at filling this \ blueprint position LDX #ship_total*4 \ Otherwise we have run out of attempts, so set X to \ point to the last entry in the table, which contains \ data for an empty position, so this blueprint position \ will be empty .mix_ok STY X2 \ Store Y, the blueprint position we are trying to fill, \ in X2 so we can retrieve it later CPX #13*4 \ If X is the four-byte index for ship number 13, then BEQ mix_anaconda \ we just decided to add an Anaconda, so jump to \ mix_anaconda to install it as the "large ship", along \ with a Worm as the "small ship" CPX #29*4 \ If X is the four-byte index for ship number 29, then BEQ mix_dragon \ we just decided to add a Dragon, so jump to mix_dragon \ to install it as the "large ship", along with a \ Sidewinder as the "small ship" TXA \ Set X = X / 4, so X is now the type of the ship we LSR A \ want to add, rather than an index into a four-byte LSR A \ table TAX .mix_install JSR install_ship \ Install a ship of type X into blueprint position Y LDY X2 \ Set Y to the blueprint position we are trying to fill, \ which we stored in X2 above .mix_next INY \ Increment Y to point to the next blueprint position to \ fill CPY #15 \ If the next position is not number 15 (the "small BNE mix_skip \ ship") then jump to mix_skip to skip the following two \ instructions INY \ The next position is 15, so increment Y twice so we INY \ skip over positions 15 (the "small ship") and 16 (the \ cop), as the first one is only filled if we have an \ Anaconda or Dragon as the "large ship" (see above), \ and the second one is already filled with a Viper .mix_skip CPY #29 \ If the next blueprint position we are trying to fill BNE mix_retry \ is not 29, then loop back to mix_retry to fill the \ next position RTS \ Otherwise we just filled the last position, number 28, \ so return from the subroutine as we are done .mix_anaconda LDX #13 \ Install ship number 13 (Anaconda) into blueprint LDY #14 \ position 14 (the "large ship") JSR install_ship LDX #14 \ Install ship number 14 (Worm) into blueprint position LDY #15 \ 15 (the "small ship"), so the Anaconda can spawn JMP mix_install \ Worms, and rejoin the mix loop via mix_install .mix_dragon LDX #29 \ Install ship number 29 (Dragon) into blueprint LDY #14 \ position 14 (the "large ship") JSR install_ship LDX #17 \ Install ship number 14 (Sidewinder) into blueprint LDY #15 \ position 15 (the "small ship"), so the Dragon can JMP mix_install \ spawn Sidewinders, and rejoin the mix loop via \ mix_install
Name: mix_bits [Show more] Type: Variable Category: Loader Summary: Lookup table for locating a specific bit in the 32-bit word for a given ship blueprint position Deep dive: Ship blueprints in Elite-A
Context: See this variable on its own page References: This variable is used as follows: * LSHIPS calls mix_bits
.mix_bits EQUB %00000001 \ Positions 0, 8, 16, 24 EQUB %00000010 \ Positions 1, 9, 17, 25 EQUB %00000100 \ Positions 2, 10, 18, 26 EQUB %00001000 \ Positions 3, 11, 19, 27 EQUB %00010000 \ Positions 4, 12, 20, 28 EQUB %00100000 \ Positions 5, 13, 21, 29 EQUB %01000000 \ Positions 6, 14, 22, 30 EQUB %10000000 \ Positions 7, 15, 23, 31
Name: SPS1 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Calculate the vector to the planet, sun or station and store it in XX15
Context: See this subroutine on its own page References: This subroutine is called as follows: * COMPAS calls SPS1 * Main flight loop (Part 9 of 16) calls SPS1 * TACTICS (Part 3 of 7) calls SPS1

Arguments: Y Determines the object whose vector we are calculating: * 0 = calculate the vector to the planet * NI% = calculate the vector to the sun/space station Other entry points: SPS1+1 A BRK instruction
.SPS1 LDX #0 \ Copy the two high bytes of the planet/sun/station's JSR SPS3 \ x-coordinate into K3(2 1 0), separating out the sign \ bit into K3+2 JSR SPS3 \ Copy the two high bytes of the planet/sun/station's \ y-coordinate into K3(5 4 3), separating out the sign \ bit into K3+5 JSR SPS3 \ Copy the two high bytes of the planet/sun/station's \ z-coordinate into K3(8 7 6), separating out the sign \ bit into K3+8 \ Fall through into TAS2 to build XX15 from K3
Name: TAS2 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Normalise the three-coordinate vector in K3
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOCKIT calls TAS2 * TACTICS (Part 3 of 7) calls TAS2 * DOCKIT calls entry point TA2

Normalise the vector in K3, which has 16-bit values and separate sign bits, and store the normalised version in XX15 as a signed 8-bit vector. A normalised vector (also known as a unit vector) has length 1, so this routine takes an existing vector in K3 and scales it so the length of the new vector is 1. This is used in two places: when drawing the compass, and when applying AI tactics to ships. We do this in two stages. This stage shifts the 16-bit vector coordinates in K3 to the left as far as they will go without losing any bits off the end, so we can then take the high bytes and use them as the most accurate 8-bit vector to normalise. Then the next stage (in routine NORM) does the normalisation. Arguments: K3(2 1 0) The 16-bit x-coordinate as (x_sign x_hi x_lo), where x_sign is just bit 7 K3(5 4 3) The 16-bit y-coordinate as (y_sign y_hi y_lo), where y_sign is just bit 7 K3(8 7 6) The 16-bit z-coordinate as (z_sign z_hi z_lo), where z_sign is just bit 7 Returns: XX15 The normalised vector, with: * The x-coordinate in XX15 * The y-coordinate in XX15+1 * The z-coordinate in XX15+2 Other entry points: TA2 Calculate the length of the vector in XX15 (ignoring the low coordinates), returning it in Q
.TAS2 LDA K3 \ OR the three low bytes and 1 to get a byte that has ORA K3+3 \ a 1 wherever any of the three low bytes has a 1 ORA K3+6 \ (as well as always having bit 0 set), and store in ORA #1 \ K3+9 STA K3+9 LDA K3+1 \ OR the three high bytes to get a byte in A that has a ORA K3+4 \ 1 wherever any of the three high bytes has a 1 ORA K3+7 \ (A K3+9) now has a 1 wherever any of the 16-bit \ values in K3 has a 1 .TAL2 ASL K3+9 \ Shift (A K3+9) to the left, so bit 7 of the high byte ROL A \ goes into the C flag BCS TA2 \ If the left shift pushed a 1 out of the end, then we \ know that at least one of the coordinates has a 1 in \ this position, so jump to TA2 as we can't shift the \ values in K3 any further to the left ASL K3 \ Shift K3(1 0), the x-coordinate, to the left ROL K3+1 ASL K3+3 \ Shift K3(4 3), the y-coordinate, to the left ROL K3+4 ASL K3+6 \ Shift K3(6 7), the z-coordinate, to the left ROL K3+7 BCC TAL2 \ Jump back to TAL2 to do another shift left (this BCC \ is effectively a JMP as we know bit 7 of K3+7 is not a \ 1, as otherwise bit 7 of A would have been a 1 and we \ would have taken the BCS above) .TA2 LDA K3+1 \ Fetch the high byte of the x-coordinate from our left- LSR A \ shifted K3, shift it right to clear bit 7, stick the ORA K3+2 \ sign bit in there from the x_sign part of K3, and STA XX15 \ store the resulting signed 8-bit x-coordinate in XX15 LDA K3+4 \ Fetch the high byte of the y-coordinate from our left- LSR A \ shifted K3, shift it right to clear bit 7, stick the ORA K3+5 \ sign bit in there from the y_sign part of K3, and STA XX15+1 \ store the resulting signed 8-bit y-coordinate in \ XX15+1 LDA K3+7 \ Fetch the high byte of the z-coordinate from our left- LSR A \ shifted K3, shift it right to clear bit 7, stick the ORA K3+8 \ sign bit in there from the z_sign part of K3, and STA XX15+2 \ store the resulting signed 8-bit z-coordinate in \ XX15+2 JMP NORM \ Now we have a signed 8-bit version of the vector K3 in \ XX15, so jump to NORM to normalise it, returning from \ the subroutine using a tail call
Name: WARP [Show more] Type: Subroutine Category: Flight Summary: Perform an in-system jump
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 3 of 16) calls WARP * Main flight loop (Part 3 of 16) calls entry point WA1

This is called when we press "J" during flight. The following checks are performed: * Make sure we don't have any ships or space stations in the vicinity * Make sure we are not in witchspace * If we are facing the planet, make sure we aren't too close * If we are facing the sun, make sure we aren't too close If the above checks are passed, then we perform an in-system jump by moving the sun and planet in the opposite direction to travel, so we appear to jump in space. This means that any asteroids, cargo canisters or escape pods get dragged along for the ride. Other entry points: WA1 Make a long, low beep
.WARP LDX JUNK \ Set X to the total number of junk items in the \ vicinity (e.g. asteroids, escape pods, cargo \ canisters, Shuttles, Transporters and so pn) LDA FRIN+2,X \ If the slot at FRIN+2+X is non-zero, then we have \ something else in the vicinity besides asteroids, \ escape pods and cargo canisters, so to check whether \ we can jump, we first grab the slot contents into A ORA JUNK \ If there is any junk in the vicinity, then JUNK will \ be non-zero, so OR'ing with JUNK will produce a \ a non-zero result if either A or JUNK are non-zero \ (so this prevents in-system jumps if there is any \ junk nearby, which is different to the other versions \ which allow you to jump, dragging any junk along with \ the ship) ORA SSPR \ If there is a space station nearby, then SSPR will \ be non-zero, so OR'ing with SSPR will produce a \ a non-zero result if either A or SSPR are non-zero ORA MJ \ If we are in witchspace, then MJ will be non-zero, so \ OR'ing with MJ will produce a non-zero result if \ either A or SSPR or MJ are non-zero BNE WA1 \ A is non-zero if we have either a ship, or a space \ station, or junk in the vicinity, or we are in \ witchspace, in which case jump to WA1 to make a low \ beep to show that we can't do an in-system jump LDY K%+8 \ Otherwise we can do an in-system jump, so now we fetch \ the byte at K%+8, which contains the z_sign for the \ first ship slot, i.e. the distance of the planet BMI WA3 \ If the planet's z_sign is negative, then the planet \ is behind us, so jump to WA3 to skip the following TAY \ Set A = Y = 0 (as we didn't BNE above) so the call \ to MAS2 measures the distance to the planet JSR MAS2 \ Call MAS2 to set A to the largest distance to the \ planet in any of the three axes (we could also call \ routine m to do the same thing, as A = 0) LSR A \ If A < 2 then jump to WA1 to abort the in-system jump BEQ WA1 \ with a low beep, as we are facing the planet and are \ too close to jump in that direction .WA3 LDY K%+NI%+8 \ Fetch the z_sign (byte #8) of the second ship in the \ ship data workspace at K%, which is reserved for the \ sun or the space station (in this case it's the \ former, as we already confirmed there isn't a space \ station in the vicinity) BMI WA2 \ If the sun's z_sign is negative, then the sun is \ behind us, so jump to WA2 to skip the following LDY #NI% \ Set Y to point to the offset of the ship data block \ for the sun, which is NI% (as each block is NI% bytes \ long, and the sun is the second block) JSR m \ Call m to set A to the largest distance to the sun \ in any of the three axes LSR A \ If A < 2 then jump to WA1 to abort the in-system jump BEQ WA1 \ with a low beep, as we are facing the sun and are too \ close to jump in that direction .WA2 \ If we get here, then we can do an in-system jump, as \ we don't have any ships or space stations in the \ vicinity, we are not in witchspace, and if we are \ facing the planet or the sun, we aren't too close to \ jump towards it \ \ We do an in-system jump by moving the sun and planet, \ rather than moving our own local bubble (this is why \ in-system jumps drag asteroids, cargo canisters and \ escape pods along for the ride). Specifically, we move \ them in the z-axis by a fixed amount in the opposite \ direction to travel, thus performing a jump towards \ our destination LDA #&81 \ Set R = R = P = &81 STA S STA R STA P LDA K%+8 \ Set A = z_sign for the planet JSR ADD \ Set (A X) = (A P) + (S R) \ = (z_sign &81) + &8181 \ = (z_sign &81) - &0181 \ \ This moves the planet against the direction of travel \ by reducing z_sign by 1, as the above maths is: \ \ z_sign 00000000 \ + 00000000 10000001 \ - 00000001 10000001 \ \ or: \ \ z_sign 00000000 \ + 00000000 00000000 \ - 00000001 00000000 \ \ i.e. the high byte is z_sign - 1, making sure the sign \ is preserved STA K%+8 \ Set the planet's z_sign to the high byte of the result LDA K%+NI%+8 \ Set A = z_sign for the sun JSR ADD \ Set (A X) = (A P) + (S R) \ = (z_sign &81) + &8181 \ = (z_sign &81) - &0181 \ \ which moves the sun against the direction of travel \ by reducing z_sign by 1 STA K%+NI%+8 \ Set the planet's z_sign to the high byte of the result LDA #1 \ These instructions have no effect, as the call to STA QQ11 \ LOOK1 below starts by setting QQ11 to 0; instead they \ just set the current view type in QQ11 to 1 for the \ duration of the next three instructions STA MCNT \ Set the main loop counter to 1, so the next iteration \ through the main loop will potentially spawn ships \ (see part 2 of the main game loop at me3) LSR A \ Set EV, the extra vessels spawning counter, to 0 STA EV \ (the LSR produces a 0 as A was previously 1) LDX VIEW \ Set X to the current view (front, rear, left or right) JMP LOOK1 \ and jump to LOOK1 to initialise that view, returning \ from the subroutine using a tail call .WA1 LDA #40 \ If we get here then we can't do an in-system jump, so JMP NOISE \ call the NOISE routine with A = 40 to make a long, low \ beep and return from the subroutine using a tail call
Name: EXNO3 [Show more] Type: Subroutine Category: Sound Summary: Make an explosion sound
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH calls EXNO3 * Main flight loop (Part 10 of 16) calls EXNO3 * OOPS calls EXNO3 * TACTICS (Part 1 of 7) calls EXNO3

Make the sound of death in the cold, hard vacuum of space. Apparently, in Elite space, everyone can hear you scream. This routine also makes the sound of a destroyed cargo canister if we don't get scooping right, the sound of us colliding with another ship, and the sound of us being hit with depleted shields. It is not a good sound to hear.
.EXNO3 JSR sound_10 \ Call sound_10 make the first death sound LDA #24 \ Call the NOISE routine with A = 24 to make the JMP NOISE \ death sound and return from the subroutine using a \ tail call
Name: SFRMIS [Show more] Type: Subroutine Category: Tactics Summary: Add an enemy missile to our local bubble of universe
Context: See this subroutine on its own page References: This subroutine is called as follows: * TACTICS (Part 5 of 7) calls SFRMIS * FRMIS calls entry point n_sound30 * LAUN calls entry point n_sound30

An enemy has fired a missile, so add the missile to our universe if there is room, and if there is, make the appropriate warnings and noises. Other entry points: n_sound30 Make the sound of a missile being launched
.SFRMIS LDX #MSL \ Set X to the ship type of a missile, and call SFS1-2 JSR SFS1-2 \ to add the missile to our universe with an AI flag \ of %11111110 (AI enabled, hostile, no E.C.M.) BCC d_4418 \ The C flag will be set if the call to SFS1-2 was a \ success, so if it's clear, jump down to d_4418 to \ return from the subroutine LDA #120 \ Print recursive token 120 ("INCOMING MISSILE") as an JSR MESS \ in-flight message .n_sound30 LDA #48 \ Call the NOISE routine with A = 48 to make the sound JMP NOISE \ of the missile being launched and return from the \ subroutine using a tail call .d_4418 RTS \ Return from the subroutine
Name: EXNO2 [Show more] Type: Subroutine Category: Sound Summary: Process us making a kill Deep dive: Combat rank
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 11 of 16) calls EXNO2 * TACTICS (Part 1 of 7) calls EXNO2

We have killed a ship, so increase the kill tally, displaying an iconic message of encouragement if the kill total is a multiple of 256, and then make a nearby explosion sound.
.EXNO2 INC TALLY \ Increment the low byte of the kill count in TALLY BNE EXNO-2 \ If there is no carry, jump to the LDX #7 below (at \ EXNO-2) INC TALLY+1 \ Increment the high byte of the kill count in TALLY LDA #101 \ The kill total is a multiple of 256, so it's time JSR MESS \ for a pat on the back, so print recursive token 101 \ ("RIGHT ON COMMANDER!") as an in-flight message LDX #7 \ Set X = 7 and fall through into EXNO to make the \ sound of a ship exploding
Name: EXNO [Show more] Type: Subroutine Category: Sound Summary: Make the sound of a laser strike or ship explosion
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 11 of 16) calls EXNO * EXNO3 calls entry point sound_10 * EXNO2 calls entry point EXNO-2

Make the two-part explosion sound of us making a laser strike, or of another ship exploding. The volume of the first explosion is affected by the distance of the ship being hit, with more distant ships being quieter. The value in X also affects the volume of the first explosion, with a higher X giving a quieter sound (so X can be used to differentiate a laser strike from an explosion). Arguments: X The larger the value of X, the fainter the explosion. Allowed values are: * 7 = explosion is louder (i.e. the ship has just exploded) * 15 = explosion is quieter (i.e. this is just a laser strike) Other entry points: EXNO-2 Set X = 7 and fall through into EXNO to make the sound of a ship exploding sound_10 Make the first part of the death sound, or the second part of the explosion sound
.EXNO STX T \ Store the distance in T LDA #24 \ Set A = 24 to denote the sound of us making a hit or JSR NOS1 \ kill (part 1 of the explosion), and call NOS1 to set \ up the sound block in XX16 LDA INWK+7 \ Fetch z_hi, the distance of the ship being hit in LSR A \ terms of the z-axis (in and out of the screen), and LSR A \ divide by 4. If z_hi has either bit 6 or 7 set then \ that ship is too far away to be shown on the scanner \ (as per the SCAN routine), so we know the maximum \ z_hi at this point is %00111111, and shifting z_hi \ to the right twice gives us a maximum value of \ %00001111 AND T \ This reduces A to a maximum of X; X can be either \ 7 = %0111 or 15 = %1111, so AND'ing with 15 will \ not affect A, while AND'ing with 7 will clear bit \ 3, reducing the maximum value in A to 7 ORA #%11110001 \ The SOUND statement's amplitude ranges from 0 (for no \ sound) to -15 (full volume), so we can set bits 0 and \ 4-7 in A, and keep bits 1-3 from the above to get \ a value between -15 (%11110001) and -1 (%11111111), \ with lower values of z_hi and argument X leading \ to a more negative, or quieter number (so the closer \ the ship, i.e. the smaller the value of X, the louder \ the sound) STA XX16+2 \ The amplitude byte of the sound block in XX16 is in \ byte #3 (where it's the low byte of the amplitude), so \ this sets the amplitude to the value in A JSR NO3 \ Make the sound from our updated sound block in XX16 .sound_10 LDA #16 \ Set A = 16 to denote we have made a hit or kill \ (part 2 of the explosion) JMP NOISE \ Jump to NOISE to make the sound and return from the \ subroutine using a tail call
Name: DKS1 [Show more] Type: Subroutine Category: Keyboard Summary: Scan the keyboard or joystick for a flight key by sending a scan_y command to the I/O processor
Context: See this subroutine on its own page References: This subroutine is called as follows: * DK4_FLIGHT calls DKS1 * DKJ1 calls DKS1 * DOKEY_FLIGHT calls DKS1

Scan the keyboard for the flight key given in register Y, where Y is the offset into the KYTB table above (so we can scan for Space by setting Y to 2, for example). If the key is pressed, set the corresponding byte in the key logger at KL to a negative value (i.e. with bit 7 set). Arguments: Y The offset into the KYTB table above of the key that we want to scan on the keyboard
.DKS1 LDA #&96 \ Send command &96 to the I/O processor: JSR tube_write \ \ =scan_y(key_offset, delta_14b) \ \ which will update the roll or pitch dashboard \ indicator to the specified value TYA \ Send the first parameter to the I/O processor: JSR tube_write \ \ * key_offset = Y LDA BSTK \ Send the second parameter to the I/O processor: JSR tube_write \ \ * delta14b = BTSK JSR tube_read \ Set A to the response from the I/O processor, which \ will be the key number, but with bit 7 clear if the \ key is not being pressed, or bit 7 set if it is being \ pressed BPL b_quit \ If the response is positive (i.e. bit 7 is clear) then \ the key is not being pressed, so skip the following \ instruction STA KL,Y \ The response is negative, so store this in the Y-th \ byte of the key logger at KL to indicate that the key \ is being pressed .b_quit RTS \ Return from the subroutine
Name: DKJ1 [Show more] Type: Subroutine Category: Keyboard Summary: Read joystick and flight controls
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOKEY_FLIGHT calls DKJ1

Specifically, scan the keyboard for the speed up and slow down keys, and read the joystick's fire button and X and Y axes, storing the results in the key logger and the joystick position variables. This routine is only called if joysticks are enabled (JSTK = non-zero).
.DKJ1 LDA auto \ If auto is non-zero, then the docking computer is BNE auton \ currently activated, so jump to auton in DOKEY so the \ docking computer can "press" the flight keys for us LDY #1 \ Update the key logger for key 1 in the KYTB table, so JSR DKS1 \ KY1 will be &FF if "?" (slow down) is being pressed INY \ Update the key logger for key 2 in the KYTB table, so JSR DKS1 \ KY2 will be &FF if Space (speed up) is being pressed JSR scan_fire \ Call scan_fire to check whether the joystick's fire \ button is being pressed, which clears bit 4 in A if \ the fire button is being pressed, and sets it if it \ is not being pressed EOR #%00010000 \ Flip bit 4 so that it's set if the fire button has STA KY7 \ been pressed, and store the result in the keyboard \ logger at location KY7, which is also where the A key \ (fire lasers) key is logged LDX #1 \ Call DKS2 to fetch the value of ADC channel 1 (the JSR DKS2 \ joystick X value) into (A X), and OR A with 1. This ORA #1 \ ensures that the high byte is at least 1, and then we STA JSTX \ store the result in JSTX LDX #2 \ Call DKS2 to fetch the value of ADC channel 2 (the JSR DKS2 \ joystick Y value) into (A X), and EOR A with JSTGY. EOR JSTGY \ JSTGY will be &FF if the game is configured to STA JSTY \ reverse the joystick Y channel, so this EOR does \ exactly that, and then we store the result in JSTY JMP DK4_FLIGHT \ We are done scanning the joystick flight controls, \ so jump to DK4_FLIGHT to scan for other keys, using a \ tail call so we can return from the subroutine there
Name: U% [Show more] Type: Subroutine Category: Keyboard Summary: Clear the key logger
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH calls U% * DOKEY_FLIGHT calls U% * RES2 calls U%

Returns: A A is set to 0 Y Y is set to 0
.U% LDA #0 \ Set A to 0, as this means "key not pressed" in the \ key logger at KL LDY #16 \ We want to clear the 16 key logger locations from \ KY1 to KY20, so set a counter in Y .DKL3 STA KL,Y \ Store 0 in the Y-th byte of the key logger DEY \ Decrement the counter BNE DKL3 \ And loop back for the next key, until we have just \ KL+1. We don't want to clear the first key logger \ location at KL, as the keyboard table at KYTB starts \ with offset 1, not 0, so KL is not technically part of \ the key logger (it's actually used for logging keys \ that don't appear in the keyboard table, and which \ therefore don't use the key logger) RTS \ Return from the subroutine
Name: DOKEY_FLIGHT [Show more] Type: Subroutine Category: Keyboard Summary: Scan for the seven primary flight controls (flight version)
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main game loop for flight (Part 5 of 6) calls DOKEY_FLIGHT
.DOKEY_FLIGHT JSR U% \ Call U% to clear the key logger LDA QQ22+1 \ Fetch QQ22+1, which contains the number that's shown \ on-screen during hyperspace countdown BEQ l_open \ If the hyperspace countdown is non-zero, jump to JMP DK4_FLIGHT \ DK4_FLIGHT to skip scanning for primary flight keys, \ and move on to scanning for pause, configuration and \ secondary flight keys .l_open LDA JSTK \ If JSTK is non-zero, then we are configured to use BNE DKJ1 \ the joystick rather than keyboard, so jump to DKJ1 \ to read the joystick flight controls, before jumping \ to DK4 to scan for pause, configuration and secondary \ flight keys LDY #7 \ We're going to work our way through the primary flight \ control keys (pitch, roll, speed and laser), so set a \ counter in Y so we can loop through all 7 .DKL2 JSR DKS1 \ Call DKS1 to see if the KYTB key at offset Y is being \ pressed, and set the key logger accordingly DEY \ Decrement the loop counter BNE DKL2 \ Loop back for the next key, working our way from A at \ KYTB+7 down to ? at KYTB+1 LDA auto \ If auto is 0, then the docking computer is not BEQ DK15 \ currently activated, so jump to DK15 to skip the \ docking computer manoeuvring code below .auton JSR ZINF \ Call ZINF to reset the INWK ship workspace LDA #96 \ Set nosev_z_hi = 96 STA INWK+14 ORA #%10000000 \ Set sidev_x_hi = -96 STA INWK+22 STA TYPE \ Set the ship type to -96, so the negative value will \ let us check in the DOCKIT routine whether this is our \ ship that is activating its docking computer, rather \ than an NPC ship docking LDA DELTA \ Set the ship speed to DELTA (our speed) STA INWK+27 JSR DOCKIT \ Call DOCKIT to calculate the docking computer's moves \ and update INWK with the results \ We now "press" the relevant flight keys, depending on \ the results from DOCKIT, starting with the pitch keys LDA INWK+27 \ Fetch the updated ship speed from byte #27 into A CMP #22 \ If A < 22, skip the next instruction BCC P%+4 LDA #22 \ Set A = 22, so the maximum speed during docking is 22 STA DELTA \ Update DELTA to the new value in A LDA #&FF \ Set A = &FF, which we can insert into the key logger \ to "fake" the docking computer working the keyboard LDX #0 \ Set X = 0, so we "press" KY1 below ("?", slow down) LDY INWK+28 \ If the updated acceleration in byte #28 is zero, skip BEQ DK11 \ to DK11 BMI P%+3 \ If the updated acceleration is negative, skip the \ following instruction INX \ The updated acceleration is positive, so increment X \ to 1, so we "press" KY2 below (Space, speed up) STA KY1,X \ Store &FF in either KY1 or KY2 to "press" the relevant \ key, depending on whether the updated acceleration is \ negative (in which case we "press" KY1, "?", to slow \ down) or positive (in which case we "press" KY2, \ Space, to speed up) .DK11 \ We now "press" the relevant roll keys, depending on \ the results from DOCKIT LDA #128 \ Set A = 128, which indicates no change in roll when \ stored in JSTX (i.e. the centre of the roll indicator) LDX #0 \ Set X = 0, so we "press" KY3 below ("<", increase \ roll) ASL INWK+29 \ Shift ship byte #29 left, which shifts bit 7 of the \ updated roll counter (i.e. the roll direction) into \ the C flag BEQ DK12 \ If the remains of byte #29 is zero, then the updated \ roll counter is zero, so jump to DK12 set JSTX to 128, \ to indicate there's no change in the roll BCC P%+3 \ If the C flag is clear, skip the following instruction INX \ The C flag is set, i.e. the direction of the updated \ roll counter is negative, so increment X to 1 so we \ "press" KY4 below (">", decrease roll) BIT INWK+29 \ We shifted the updated roll counter to the left above, BPL DK14 \ so this tests bit 6 of the original value, and if it \ is is clear (i.e. the magnitude is less than 64), jump \ to DK14 to "press" the key and leave JSTX unchanged LDA #64 \ The magnitude of the updated roll is 64 or more, so STA JSTX \ set JSTX to 64 (so the roll decreases at half the \ maximum rate) LDA #0 \ And set A = 0 so we do not "press" any keys (so if the \ docking computer needs to make a serious roll, it does \ so by setting JSTX directly rather than by "pressing" \ a key) .DK14 STA KY3,X \ Store A in either KY3 or KY4, depending on whether \ the updated roll rate is increasing (KY3) or \ decreasing (KY4) LDA JSTX \ Fetch A from JSTX so the next instruction has no \ effect .DK12 STA JSTX \ Store A in JSTX to update the current roll rate \ We now "press" the relevant pitch keys, depending on \ the results from DOCKIT LDA #128 \ Set A = 128, which indicates no change in pitch when \ stored in JSTX (i.e. the centre of the pitch \ indicator) LDX #0 \ Set X = 0, so we "press" KY5 below ("X", decrease \ pitch) ASL INWK+30 \ Shift ship byte #30 left, which shifts bit 7 of the \ updated pitch counter (i.e. the pitch direction) into \ the C flag BEQ DK13 \ If the remains of byte #30 is zero, then the updated \ pitch counter is zero, so jump to DK13 set JSTY to \ 128, to indicate there's no change in the pitch BCS P%+3 \ If the C flag is set, skip the following instruction INX \ The C flag is clear, i.e. the direction of the updated \ pitch counter is positive, so increment X to 1 so we \ "press" KY6 below ("S", increase pitch) STA KY5,X \ Store 128 in either KY5 or KY6 to "press" the relevant \ key, depending on whether the pitch direction is \ negative (in which case we "press" KY5, "X", to \ decrease the pitch) or positive (in which case we \ "press" KY6, "S", to increase the pitch) LDA JSTY \ Fetch A from JSTY so the next instruction has no \ effect .DK13 STA JSTY \ Store A in JSTY to update the current pitch rate .DK15 LDX JSTX \ Set X = JSTX, the current roll rate (as shown in the \ RL indicator on the dashboard) LDA #7 \ Set A to 7, which is the amount we want to alter the \ roll rate by if the roll keys are being pressed LDY KL+3 \ If the "<" key is being pressed, then call the BUMP2 BEQ P%+5 \ routine to increase the roll rate in X by A JSR BUMP2 LDY KL+4 \ If the ">" key is being pressed, then call the REDU2 BEQ P%+5 \ routine to decrease the roll rate in X by A, taking JSR REDU2 \ the keyboard auto re-centre setting into account STX JSTX \ Store the updated roll rate in JSTX ASL A \ Double the value of A, to 14 LDX JSTY \ Set X = JSTY, the current pitch rate (as shown in the \ DC indicator on the dashboard) LDY KL+5 \ If the "X" key is being pressed, then call the REDU2 BEQ P%+5 \ routine to decrease the pitch rate in X by A, taking JSR REDU2 \ the keyboard auto re-centre setting into account LDY KL+6 \ If the "S" key is being pressed, then call the BUMP2 BEQ P%+5 \ routine to increase the pitch rate in X by A JSR BUMP2 STX JSTY \ Store the updated roll rate in JSTY \ Fall through into DK4_FLIGHT to scan for other keys
Name: DK4_FLIGHT [Show more] Type: Subroutine Category: Keyboard Summary: Scan for pause, configuration and secondary flight keys (flight version)
Context: See this subroutine on its own page References: This subroutine is called as follows: * DKJ1 calls DK4_FLIGHT * DOKEY_FLIGHT calls DK4_FLIGHT
.DK4_FLIGHT JSR RDKEY \ Scan the keyboard for a key press and return the \ internal key number in X (or 0 for no key press) STX KL \ Store X in KL, byte #0 of the key logger CPX #&69 \ If COPY is not being pressed, jump to DK2_FLIGHT BNE DK2_FLIGHT \ below, otherwise let's process the configuration \ keys .FREEZE_FLIGHT \ COPY is being pressed, so we enter a loop that \ listens for configuration keys, and we keep looping \ until we detect a DELETE key press. This effectively \ pauses the game when COPY is pressed, and unpauses \ it when DELETE is pressed JSR WSCAN \ Call WSCAN to wait for the vertical sync, so the whole \ screen gets drawn JSR RDKEY \ Scan the keyboard for a key press and return the \ internal key number in X (or 0 for no key press) CPX #&51 \ If "S" is not being pressed, skip to DK6_FLIGHT BNE DK6_FLIGHT LDA #0 \ "S" is being pressed, so set DNOIZ to 0 to turn the STA DNOIZ \ sound on .DK6_FLIGHT LDY #&40 \ We now want to loop through the keys that toggle \ various settings. These have internal key numbers \ between &40 (CAPS LOCK) and &46 ("K"), so we set up \ the first key number in Y to act as a loop counter. \ See subroutine DKS3 for more details on this .DKL4_FLIGHT JSR DKS3 \ Call DKS3 to scan for the key given in Y, and toggle \ the relevant setting if it is pressed INY \ Increment Y to point to the next toggle key CPY #&48 \ The last toggle key is &47 (@), so check whether we \ have just done that one BNE DKL4_FLIGHT \ If not, loop back to check for the next toggle key CPX #&10 \ If "Q" is not being pressed, skip to DK7_FLIGHT BNE DK7_FLIGHT STX DNOIZ \ "Q" is being pressed, so set DNOIZ to X, which is \ non-zero (&10), so this will turn the sound off .DK7_FLIGHT CPX #&70 \ If ESCAPE is not being pressed, skip over the next BNE P%+5 \ instruction JMP DEATH2 \ ESCAPE is being pressed, so jump to DEATH2 to end \ the game \CPX #&37 \ These instructions are commented out in the original \BNE dont_dump \ source \JSR printer \.dont_dump CPX #&59 \ If DELETE is not being pressed, we are still paused, BNE FREEZE_FLIGHT \ so loop back up to keep listening for configuration \ keys, otherwise fall through into the rest of the \ key detection code, which unpauses the game .DK2_FLIGHT LDA QQ11 \ If the current view is non-zero (i.e. not a space BNE DK5 \ view), return from the subroutine (as DK5 contains \ an RTS) LDY #16 \ This is a space view, so now we want to check for all \ the secondary flight keys. The internal key numbers \ are in the keyboard table KYTB from KYTB+8 to \ KYTB+16, and their key logger locations are from KL+8 \ to KL+16. So set a decreasing counter in Y for the \ index, starting at 16, so we can loop through them .DKL1_FLIGHT JSR DKS1 \ Call DKS1 to see if the KYTB key at offset Y is being \ pressed, and set the key logger accordingly DEY \ Decrement the loop counter CPY #7 \ Have we just done the last key? BNE DKL1_FLIGHT \ If not, loop back to process the next key .DK5 RTS \ Return from the subroutine
Name: me1 [Show more] Type: Subroutine Category: Text Summary: Erase an old in-flight message and display a new one
Context: See this subroutine on its own page References: This subroutine is called as follows: * MESS calls me1

Arguments: A The text token to be printed X Must be set to 0
.me1 STX DLY \ Set the message delay in DLY to 0, so any new \ in-flight messages will be shown instantly PHA \ Store the new message token we want to print LDA MCH \ Set A to the token number of the message that is JSR mes9 \ currently on-screen, and call mes9 to print it (which \ will remove it from the screen, as printing is done \ using EOR logic) PLA \ Restore the new message token EQUB &2C \ Fall through into ou2 to print the new message, but \ skip the first instruction by turning it into \ &2C &A9 &6C, or BIT &6CA9, which does nothing apart \ from affect the flags
Name: cargo_mtok [Show more] Type: Subroutine Category: Text Summary: Print the name of a specific cargo item
Context: See this subroutine on its own page References: This subroutine is called as follows: * OUCH calls cargo_mtok

Arguments: A The number of the cargo item whose name we want to print (where 0 = food, 1 = textiles, and so on up to 16 for alien items) See QQ23 for a list of market item numbers and their storage units
.cargo_mtok ADC #208 \ Add 208 to the value in A, so when we fall through \ into MESS, we print recursive token 48 + A as an \ in-flight token, which will be in the range 48 \ ("FOOD") to 64 ("ALIEN ITEMS")
Name: MESS [Show more] Type: Subroutine Category: Text Summary: Display an in-flight message
Context: See this subroutine on its own page References: This subroutine is called as follows: * EXNO2 calls MESS * FR1 calls MESS * Ghy calls MESS * KILLSHP calls MESS * Main flight loop (Part 12 of 16) calls MESS * Main flight loop (Part 15 of 16) calls MESS * Main flight loop (Part 8 of 16) calls MESS * Main game loop for flight (Part 2 of 6) calls MESS * OUCH calls MESS * SFRMIS calls MESS * me2 calls MESS

Display an in-flight message in capitals at the bottom of the space view, erasing any existing in-flight message first. Arguments: A The text token to be printed
.MESS LDX #0 \ Set QQ17 = 0 to switch to ALL CAPS STX QQ17 LDY #9 \ Move the text cursor to column 9, row 22, at the STY XC \ bottom middle of the screen, and set Y = 22 LDY #22 STY YC CPX DLY \ If the message delay in DLY is not zero, jump up to BNE me1 \ me1 to erase the current message first (whose token \ number will be in MCH) STY DLY \ Set the message delay in DLY to 22 STA MCH \ Set MCH to the token we are about to display \ Fall through into mes9 to print the token in A
Name: mes9 [Show more] Type: Subroutine Category: Text Summary: Print a text token, possibly followed by " DESTROYED"
Context: See this subroutine on its own page References: This subroutine is called as follows: * me1 calls mes9

Print a text token, followed by " DESTROYED" if the destruction flag is set (for when a piece of equipment is destroyed).
.mes9 JSR TT27 \ Call TT27 to print the text token in A LSR de \ If bit 0 of variable de is clear, return from the BCC DK5 \ subroutine (as DK5 contains an RTS) LDA #253 \ Print recursive token 93 (" DESTROYED") and return JMP TT27 \ from the subroutine using a tail call
Name: OUCH [Show more] Type: Subroutine Category: Flight Summary: Potentially lose cargo or equipment following damage
Context: See this subroutine on its own page References: This subroutine is called as follows: * OOPS calls OUCH

Our shields are dead and we are taking damage, so there is a small chance of losing cargo or equipment.
.OUCH JSR DORND \ Set A and X to random numbers BMI DK5 \ If A < 0 (50% chance), return from the subroutine \ (as DK5 contains an RTS) CPX #24 \ If X >= 24 (90% chance), return from the subroutine BCS DK5 \ (as DK5 contains an RTS) LDA CRGO,X \ If we do not have any of item CRGO+X, return from the BEQ DK5 \ subroutine (as DK5 contains an RTS). X is in the range \ 0-23, so this not only checks for cargo, but also for \ the I.F.F. system, E.C.M. system, fuel scoops, \ hyperspace unit, energy unit, docking computer and \ galactic hyperdrive, all of which can be destroyed LDA DLY \ If there is already an in-flight message on-screen, BNE DK5 \ return from the subroutine (as DK5 contains an RTS) LDY #3 \ Set bit 1 of de, the equipment destruction flag, so STY de \ that when we call MESS below, " DESTROYED" is appended \ to the in-flight message STA CRGO,X \ A is 0 (as we didn't branch with the BNE above), so \ this sets CRGO+X to 0, which destroys any cargo or \ equipment we have of that type DEX \ Decrement X, so X is now in the range -1 to 22, and a \ value of 0 means we just lost some food, 1 means we \ lost some textiles, and so on BMI ou1 \ If X is now negative, then we just lost the I.F.F. \ system (as X was 0 before being decremented), so jump \ to ou1 to print the relevant message, which will be \ "I.F.F.SYSTEM DESTROYED" as A = 0 and the C flag is \ clear (as we passed through the BCS above) CPX #17 \ If X = 17 then we just lost the E.C.M., so jump to ou1 BEQ ou1 \ to print the relevant message, which will be \ "E.C.M.SYSTEM DESTROYED" as A = 0 and the C flag is \ set from the CPX \ If we get here then X is in the range 0-16 or 18-22 TXA \ Copy the value of X into A BCC cargo_mtok \ If X < 17 then we just lost some cargo (as opposed to \ equipment), so jump to cargo_mtok to print the name of \ the cargo whose number is in A, plus " DESTROYED", and \ return from the subroutine using a tail call \ If we get here then X (and A) are in the range 18-22 CMP #18 \ If A is not 18, jump down to equip_mtok with A in the BNE equip_mtok \ range 19-22 and the C flag set from the CMP, to print \ token 113 ("HYPERSPACE UNIT") through 116 ("GALACTIC \ HYPERSPACE") LDA #111-107-1 \ Otherwise A is 18, so we have lost the fuel scoops, so \ set A to 111-107-1 = 3 and the C flag set from the CMP \ to print token 111 ("FUEL SCOOPS") .ou1 ADC #107-93 \ We can reach here with three values of A and the C \ flag, and then add 93 below to print the following \ tokens: \ \ A = 0, C flag clear = token 107 ("I.F.F.SYSTEM") \ A = 0, C flag set = token 108 ("E.C.M.SYSTEM") \ A = 3, C flag set = token 111 ("FUEL SCOOPS") .equip_mtok ADC #93 \ We can either reach here from above, or jump straight \ here with A = 19-22 and the C flag set, in which case \ adding 93 will give us token 113 ("HYPERSPACE UNIT") \ through 116 ("GALACTIC HYPERSPACE ") INC new_hold \ We just lost a piece of equipment, so increment the \ amount of free space in the hold BNE MESS \ Print recursive token A as an in-flight message, \ followed by " DESTROYED", and return from the \ subroutine using a tail call
Name: LL9_FLIGHT [Show more] Type: Subroutine Category: Drawing ships Summary: Draw a ship (flight version)
Context: See this subroutine on its own page References: This subroutine is called as follows: * ESCAPE calls LL9_FLIGHT * Main flight loop (Part 12 of 16) calls LL9_FLIGHT
.LL25 JMP PLANET \ Jump to the PLANET routine, returning from the \ subroutine using a tail call .LL9_FLIGHT LDA TYPE \ If the ship type is negative then this indicates a BMI LL25 \ planet or sun, so jump to PLANET via LL25 above JMP LL9 \ Jump to LL9 to draw the ship
Name: MVEIT_FLIGHT (Part 1 of 6) [Show more] Type: Subroutine Category: Moving Summary: Move current ship (flight version)
Context: See this subroutine on its own page References: This subroutine is called as follows: * ESCAPE calls MVEIT_FLIGHT * Main flight loop (Part 6 of 16) calls MVEIT_FLIGHT
.MVEIT_FLIGHT LDA INWK+31 \ If bit 5 or 7 of ship byte #31 is set, jump to MV30 AND #%10100000 \ as the ship is either exploding or has been killed, so BNE MV30 \ we don't need to tidy its orientation vectors or apply \ tactics LDA MCNT \ Fetch the main loop counter EOR XSAV \ Fetch the slot number of the ship we are moving, EOR AND #15 \ with the loop counter and apply mod 15 to the result. BNE P%+5 \ The result will be zero when "counter mod 15" matches \ the slot number, so this makes sure we call TIDY 12 \ times every 16 main loop iterations, like this: \ \ Iteration 0, tidy the ship in slot 0 \ Iteration 1, tidy the ship in slot 1 \ Iteration 2, tidy the ship in slot 2 \ ... \ Iteration 11, tidy the ship in slot 11 \ Iteration 12, do nothing \ Iteration 13, do nothing \ Iteration 14, do nothing \ Iteration 15, do nothing \ Iteration 16, tidy the ship in slot 0 \ ... \ \ and so on JSR TIDY \ Call TIDY to tidy up the orientation vectors, to \ prevent the ship from getting elongated and out of \ shape due to the imprecise nature of trigonometry \ in assembly language
Name: MVEIT_FLIGHT (Part 2 of 6) [Show more] Type: Subroutine Category: Moving Summary: Move current ship: Call tactics routine, remove ship from scanner Deep dive: Scheduling tasks with the main loop counter
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This routine has multiple stages. This stage does the following: * Apply tactics to ships with AI enabled (by calling the TACTICS routine) * Remove the ship from the scanner, so we can move it
LDX TYPE \ If the type of the ship we are moving is positive, BPL P%+5 \ i.e. it is not a planet (types 128 and 130) or sun \ (type 129), then skip the following instruction JMP MV40 \ This item is the planet or sun, so jump to MV40 to \ move it, which ends by jumping back into this routine \ at MV45 (after all the rotation, tactics and scanner \ code, which we don't need to apply to planets or suns) LDA INWK+32 \ Fetch the ship's byte #32 (AI flag) into A BPL MV30 \ If bit 7 of the AI flag is clear, then if this is a \ ship or missile it is dumb and has no AI, and if this \ is the space station it is not hostile, so in both \ cases skip the following as it has no tactics CPX #MSL \ If the ship is a missile, skip straight to MV26 to BEQ MV26 \ call the TACTICS routine, as we do this every \ iteration of the main loop for missiles only LDA MCNT \ Fetch the main loop counter EOR XSAV \ Fetch the slot number of the ship we are moving, EOR AND #7 \ with the loop counter and apply mod 8 to the result. BNE MV30 \ The result will be zero when "counter mod 8" matches \ the slot number mod 8, so this makes sure we call \ TACTICS 12 times every 8 main loop iterations, like \ this: \ \ Iteration 0, apply tactics to slots 0 and 8 \ Iteration 1, apply tactics to slots 1 and 9 \ Iteration 2, apply tactics to slots 2 and 10 \ Iteration 3, apply tactics to slots 3 and 11 \ Iteration 4, apply tactics to slot 4 \ Iteration 5, apply tactics to slot 5 \ Iteration 6, apply tactics to slot 6 \ Iteration 7, apply tactics to slot 7 \ Iteration 8, apply tactics to slots 0 and 8 \ ... \ \ and so on .MV26 JSR TACTICS \ Call TACTICS to apply AI tactics to this ship .MV30 JSR SCAN \ Draw the ship on the scanner, which has the effect of \ removing it, as it's already at this point and hasn't \ yet moved
Name: MVEIT_FLIGHT (Part 3 of 6) [Show more] Type: Subroutine Category: Moving Summary: Move current ship: Move ship forward according to its speed
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This routine has multiple stages. This stage does the following: * Move the ship forward (along the vector pointing in the direction of travel) according to its speed: (x, y, z) += nosev_hi * speed / 64
LDA INWK+27 \ Set Q = the ship's speed byte #27 * 4 ASL A ASL A STA Q LDA INWK+10 \ Set A = |nosev_x_hi| AND #%01111111 JSR FMLTU \ Set R = A * Q / 256 STA R \ = |nosev_x_hi| * speed / 64 LDA INWK+10 \ If nosev_x_hi is positive, then: LDX #0 \ JSR MVT1-2 \ (x_sign x_hi x_lo) = (x_sign x_hi x_lo) + R \ \ If nosev_x_hi is negative, then: \ \ (x_sign x_hi x_lo) = (x_sign x_hi x_lo) - R \ \ So in effect, this does: \ \ (x_sign x_hi x_lo) += nosev_x_hi * speed / 64 LDA INWK+12 \ Set A = |nosev_y_hi| AND #%01111111 JSR FMLTU \ Set R = A * Q / 256 STA R \ = |nosev_y_hi| * speed / 64 LDA INWK+12 \ If nosev_y_hi is positive, then: LDX #3 \ JSR MVT1-2 \ (y_sign y_hi y_lo) = (y_sign y_hi y_lo) + R \ \ If nosev_y_hi is negative, then: \ \ (y_sign y_hi y_lo) = (y_sign y_hi y_lo) - R \ \ So in effect, this does: \ \ (y_sign y_hi y_lo) += nosev_y_hi * speed / 64 LDA INWK+14 \ Set A = |nosev_z_hi| AND #%01111111 JSR FMLTU \ Set R = A * Q / 256 STA R \ = |nosev_z_hi| * speed / 64 LDA INWK+14 \ If nosev_y_hi is positive, then: LDX #6 \ JSR MVT1-2 \ (z_sign z_hi z_lo) = (z_sign z_hi z_lo) + R \ \ If nosev_z_hi is negative, then: \ \ (z_sign z_hi z_lo) = (z_sign z_hi z_lo) - R \ \ So in effect, this does: \ \ (z_sign z_hi z_lo) += nosev_z_hi * speed / 64
Name: MVEIT_FLIGHT (Part 4 of 6) [Show more] Type: Subroutine Category: Moving Summary: Move current ship: Apply acceleration to ship's speed as a one-off
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This routine has multiple stages. This stage does the following: * Apply acceleration to the ship's speed (if acceleration is non-zero), and then zero the acceleration as it's a one-off change
LDA INWK+27 \ Set A = the ship's speed in byte #24 + the ship's CLC \ acceleration in byte #28 ADC INWK+28 BPL P%+4 \ If the result is positive, skip the following \ instruction LDA #0 \ Set A to 0 to stop the speed from going negative LDY #15 \ Fetch byte #15 from the ship's blueprint, which \ contains the ship's maximum speed CMP (XX0),Y \ If A < the ship's maximum speed, skip the following BCC P%+4 \ instruction LDA (XX0),Y \ Set A to the ship's maximum speed STA INWK+27 \ We have now calculated the new ship's speed after \ accelerating and keeping the speed within the ship's \ limits, so store the updated speed in byte #27 LDA #0 \ We have added the ship's acceleration, so we now set STA INWK+28 \ it back to 0 in byte #28, as it's a one-off change
Name: MVEIT_FLIGHT (Part 5 of 6) [Show more] Type: Subroutine Category: Moving Summary: Move current ship: Rotate ship's location by our pitch and roll Deep dive: Rotating the universe
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This routine has multiple stages. This stage does the following: * Rotate the ship's location in space by the amount of pitch and roll of our ship. See below for a deeper explanation of this routine
LDX ALP1 \ Fetch the magnitude of the current roll into X, so \ if the roll angle is alpha, X contains |alpha| LDA INWK \ Set P = ~x_lo (i.e. with all its bits flipped) so that EOR #%11111111 \ we can pass x_lo to MLTU2 below) STA P LDA INWK+1 \ Set A = x_hi JSR MLTU2-2 \ Set (A P+1 P) = (A ~P) * X \ = (x_hi x_lo) * alpha STA P+2 \ Store the high byte of the result in P+2, so we now \ have: \ \ P(2 1 0) = (x_hi x_lo) * alpha LDA ALP2+1 \ Fetch the flipped sign of the current roll angle alpha EOR INWK+2 \ from ALP2+1 and EOR with byte #2 (x_sign), so if the \ flipped roll angle and x_sign have the same sign, A \ will be positive, else it will be negative. So A will \ contain the sign bit of x_sign * flipped alpha sign, \ which is the opposite to the sign of the above result, \ so we now have: \ \ (A P+2 P+1) = - (x_sign x_hi x_lo) * alpha / 256 LDX #3 \ Set (A P+2 P+1) = (y_sign y_hi y_lo) + (A P+2 P+1) JSR MVT6 \ = y - x * alpha / 256 STA K2+3 \ Set K2(3) = A = the sign of the result LDA P+1 \ Set K2(1) = P+1, the low byte of the result STA K2+1 EOR #%11111111 \ Set P = ~K2+1 (i.e. with all its bits flipped) so STA P \ that we can pass K2+1 to MLTU2 below) LDA P+2 \ Set K2(2) = A = P+2 STA K2+2 \ So we now have result 1 above: \ \ K2(3 2 1) = (A P+2 P+1) \ = y - x * alpha / 256 LDX BET1 \ Fetch the magnitude of the current pitch into X, so \ if the pitch angle is beta, X contains |beta| JSR MLTU2-2 \ Set (A P+1 P) = (A ~P) * X \ = K2(2 1) * beta STA P+2 \ Store the high byte of the result in P+2, so we now \ have: \ \ P(2 1 0) = K2(2 1) * beta LDA K2+3 \ Fetch the sign of the above result in K(3 2 1) from EOR BET2 \ K2+3 and EOR with BET2, the sign of the current pitch \ rate, so if the pitch and K(3 2 1) have the same sign, \ A will be positive, else it will be negative. So A \ will contain the sign bit of K(3 2 1) * beta, which is \ the same as the sign of the above result, so we now \ have: \ \ (A P+2 P+1) = K2(3 2 1) * beta / 256 LDX #6 \ Set (A P+2 P+1) = (z_sign z_hi z_lo) + (A P+2 P+1) JSR MVT6 \ = z + K2 * beta / 256 STA INWK+8 \ Set z_sign = A = the sign of the result LDA P+1 \ Set z_lo = P+1, the low byte of the result STA INWK+6 EOR #%11111111 \ Set P = ~z_lo (i.e. with all its bits flipped) so that STA P \ we can pass z_lo to MLTU2 below) LDA P+2 \ Set z_hi = P+2 STA INWK+7 \ So we now have result 2 above: \ \ (z_sign z_hi z_lo) = (A P+2 P+1) \ = z + K2 * beta / 256 JSR MLTU2 \ MLTU2 doesn't change Q, and Q was set to beta in \ the previous call to MLTU2, so this call does: \ \ (A P+1 P) = (A ~P) * Q \ = (z_hi z_lo) * beta STA P+2 \ Set P+2 = A = the high byte of the result, so we \ now have: \ \ P(2 1 0) = (z_hi z_lo) * beta LDA K2+3 \ Set y_sign = K2+3 STA INWK+5 EOR BET2 \ EOR y_sign with BET2, the sign of the current pitch EOR INWK+8 \ rate, and z_sign. If the result is positive jump to BPL MV43 \ MV43, otherwise this means beta * z and y have \ different signs, i.e. P(2 1) and K2(3 2 1) have \ different signs, so we need to add them in order to \ calculate K2(2 1) - P(2 1) LDA P+1 \ Set (y_hi y_lo) = K2(2 1) + P(2 1) ADC K2+1 STA INWK+3 LDA P+2 ADC K2+2 STA INWK+4 JMP MV44 \ Jump to MV44 to continue the calculation .MV43 LDA K2+1 \ Reversing the logic above, we need to subtract P(2 1) SBC P+1 \ and K2(3 2 1) to calculate K2(2 1) - P(2 1), so this STA INWK+3 \ sets (y_hi y_lo) = K2(2 1) - P(2 1) LDA K2+2 SBC P+2 STA INWK+4 BCS MV44 \ If the above subtraction did not underflow, then \ jump to MV44, otherwise we need to negate the result LDA #1 \ Negate (y_sign y_hi y_lo) using two's complement, SBC INWK+3 \ first doing the low bytes: STA INWK+3 \ \ y_lo = 1 - y_lo LDA #0 \ Then the high bytes: SBC INWK+4 \ STA INWK+4 \ y_hi = 0 - y_hi LDA INWK+5 \ And finally flip the sign in y_sign EOR #%10000000 STA INWK+5 .MV44 \ So we now have result 3 above: \ \ (y_sign y_hi y_lo) = K2(2 1) - P(2 1) \ = K2 - beta * z LDX ALP1 \ Fetch the magnitude of the current roll into X, so \ if the roll angle is alpha, X contains |alpha| LDA INWK+3 \ Set P = ~y_lo (i.e. with all its bits flipped) so that EOR #&FF \ we can pass y_lo to MLTU2 below) STA P LDA INWK+4 \ Set A = y_hi JSR MLTU2-2 \ Set (A P+1 P) = (A ~P) * X \ = (y_hi y_lo) * alpha STA P+2 \ Store the high byte of the result in P+2, so we now \ have: \ \ P(2 1 0) = (y_hi y_lo) * alpha LDA ALP2 \ Fetch the correct sign of the current roll angle alpha EOR INWK+5 \ from ALP2 and EOR with byte #5 (y_sign), so if the \ correct roll angle and y_sign have the same sign, A \ will be positive, else it will be negative. So A will \ contain the sign bit of x_sign * correct alpha sign, \ which is the same as the sign of the above result, \ so we now have: \ \ (A P+2 P+1) = (y_sign y_hi y_lo) * alpha / 256 LDX #0 \ Set (A P+2 P+1) = (x_sign x_hi x_lo) + (A P+2 P+1) JSR MVT6 \ = x + y * alpha / 256 STA INWK+2 \ Set x_sign = A = the sign of the result LDA P+2 \ Set x_hi = P+2, the high byte of the result STA INWK+1 LDA P+1 \ Set x_lo = P+1, the low byte of the result STA INWK \ So we now have result 4 above: \ \ x = x + alpha * y \ \ and the rotation of (x, y, z) is done
Name: MVEIT_FLIGHT (Part 6 of 6) [Show more] Type: Subroutine Category: Moving Summary: Move current ship: Move the ship in space according to our speed
Context: See this subroutine on its own page References: This subroutine is called as follows: * MV40 calls entry point MV45

This routine has multiple stages. This stage does the following: * Move the ship in space according to our speed (we already moved it according to its own speed in part 3). We do this by subtracting our speed (i.e. the distance we travel in this iteration of the loop) from the other ship's z-coordinate. We subtract because they appear to be "moving" in the opposite direction to us, and the whole MVEIT routine is about moving the other ships rather than us (even though we are the one doing the moving). Other entry points: MV45 Rejoin the MVEIT routine after the rotation, tactics and scanner code
.MV45 LDA DELTA \ Set R to our speed in DELTA STA R LDA #%10000000 \ Set A to zeroes but with bit 7 set, so that (A R) is \ a 16-bit number containing -R, or -speed LDX #6 \ Set X to the z-axis so the call to MVT1 does this: JSR MVT1 \ \ (z_sign z_hi z_lo) = (z_sign z_hi z_lo) + (A R) \ = (z_sign z_hi z_lo) - speed LDA TYPE \ If the ship type is the sun (129) then skip the next AND #%10000001 \ next instruction, otherwise return from the subroutine CMP #129 \ as we don't need to rotate the sun around its origin. BEQ P%+5 \ Having both the AND and the CMP is a little odd, as \ the sun is the only ship type with bits 0 and 7 set, \ so the AND has no effect and could be removed JMP MV3 \ The ship type is not the sun, so jump to MV3, skipping \ the next instruction RTS \ Return from the subroutine, as the ship we are moving \ is the sun and doesn't need any of the following
Name: MVT1 [Show more] Type: Subroutine Category: Moving Summary: Calculate (x_sign x_hi x_lo) = (x_sign x_hi x_lo) + (A R)
Context: See this subroutine on its own page References: This subroutine is called as follows: * MVEIT_FLIGHT (Part 6 of 6) calls MVT1 * SFS2 calls MVT1 * MVEIT_FLIGHT (Part 3 of 6) calls entry point MVT1-2

Add the signed delta (A R) to a ship's coordinate, along the axis given in X. Mathematically speaking, this routine translates the ship along a single axis by a signed delta. Taking the example of X = 0, the x-axis, it does the following: (x_sign x_hi x_lo) = (x_sign x_hi x_lo) + (A R) (In practice, MVT1 is only ever called directly with A = 0 or 128, otherwise it is always called via MVT-2, which clears A apart from the sign bit. The routine is written to cope with a non-zero delta_hi, so it supports a full 16-bit delta, but it appears that delta_hi is only ever used to hold the sign of the delta.) The comments below assume we are adding delta to the x-axis, though the axis is determined by the value of X. Arguments: (A R) The signed delta, so A = delta_hi and R = delta_lo X Determines which coordinate axis of INWK to change: * X = 0 adds the delta to (x_lo, x_hi, x_sign) * X = 3 adds the delta to (y_lo, y_hi, y_sign) * X = 6 adds the delta to (z_lo, z_hi, z_sign) Other entry points: MVT1-2 Clear bits 0-6 of A before entering MVT1
AND #%10000000 \ Clear bits 0-6 of A .MVT1 ASL A \ Set the C flag to the sign bit of the delta, leaving \ delta_hi << 1 in A STA S \ Set S = delta_hi << 1 \ \ This also clears bit 0 of S LDA #0 \ Set T = just the sign bit of delta (in bit 7) ROR A STA T LSR S \ Set S = delta_hi >> 1 \ = |delta_hi| \ \ This also clear the C flag, as we know that bit 0 of \ S was clear before the LSR EOR INWK+2,X \ If T EOR x_sign has bit 7 set, then x_sign and delta BMI MV10 \ have different signs, so jump to MV10 \ At this point, we know x_sign and delta have the same \ sign, that sign is in T, and S contains |delta_hi|, \ so now we want to do: \ \ (x_sign x_hi x_lo) = (x_sign x_hi x_lo) + (S R) \ \ and then set the sign of the result to the same sign \ as x_sign and delta LDA R \ First we add the low bytes, so: ADC INWK,X \ STA INWK,X \ x_lo = x_lo + R LDA S \ Then we add the high bytes: ADC INWK+1,X \ STA INWK+1,X \ x_hi = x_hi + S LDA INWK+2,X \ And finally we add any carry into x_sign, and if the ADC #0 \ sign of x_sign and delta in T is negative, make sure ORA T \ the result is negative (by OR'ing with T) STA INWK+2,X RTS \ Return from the subroutine .MV10 \ If we get here, we know x_sign and delta have \ different signs, with delta's sign in T, and \ |delta_hi| in S, so now we want to do: \ \ (x_sign x_hi x_lo) = (x_sign x_hi x_lo) - (S R) \ \ and then set the sign of the result according to \ the signs of x_sign and delta LDA INWK,X \ First we subtract the low bytes, so: SEC \ SBC R \ x_lo = x_lo - R STA INWK,X LDA INWK+1,X \ Then we subtract the high bytes: SBC S \ STA INWK+1,X \ x_hi = x_hi - S LDA INWK+2,X \ And finally we subtract any borrow from bits 0-6 of AND #%01111111 \ x_sign, and give the result the opposite sign bit to T SBC #0 \ (i.e. give it the sign of the original x_sign) ORA #%10000000 EOR T STA INWK+2,X BCS MV11 \ If the C flag is set by the above SBC, then our sum \ above didn't underflow and is correct - to put it \ another way, (x_sign x_hi x_lo) >= (S R) so the result \ should indeed have the same sign as x_sign, so jump to \ MV11 to return from the subroutine \ Otherwise our subtraction underflowed because \ (x_sign x_hi x_lo) < (S R), so we now need to flip the \ subtraction around by using two's complement to this: \ \ (S R) - (x_sign x_hi x_lo) \ \ and then we need to give the result the same sign as \ (S R), the delta, as that's the dominant figure in the \ sum LDA #1 \ First we subtract the low bytes, so: SBC INWK,X \ STA INWK,X \ x_lo = 1 - x_lo LDA #0 \ Then we subtract the high bytes: SBC INWK+1,X \ STA INWK+1,X \ x_hi = 0 - x_hi LDA #0 \ And then we subtract the sign bytes: SBC INWK+2,X \ \ x_sign = 0 - x_sign AND #%01111111 \ Finally, we set the sign bit to the sign in T, the ORA T \ sign of the original delta, as the delta is the STA INWK+2,X \ dominant figure in the sum .MV11 RTS \ Return from the subroutine
Name: MVT6 [Show more] Type: Subroutine Category: Moving Summary: Calculate (A P+2 P+1) = (x_sign x_hi x_lo) + (A P+2 P+1)
Context: See this subroutine on its own page References: This subroutine is called as follows: * MVEIT_FLIGHT (Part 5 of 6) calls MVT6

Do the following calculation, for the coordinate given by X (so this is what it does for the x-coordinate): (A P+2 P+1) = (x_sign x_hi x_lo) + (A P+2 P+1) A is a sign bit and is not included in the calculation, but bits 0-6 of A are preserved. Bit 7 is set to the sign of the result. Arguments: A The sign of P(2 1) in bit 7 P(2 1) The 16-bit value we want to add the coordinate to X The coordinate to add, as follows: * If X = 0, add to (x_sign x_hi x_lo) * If X = 3, add to (y_sign y_hi y_lo) * If X = 6, add to (z_sign z_hi z_lo) Returns: A The sign of the result (in bit 7)
.MVT6 TAY \ Store argument A into Y, for later use EOR INWK+2,X \ Set A = A EOR x_sign BMI MV50 \ If the sign is negative, i.e. A and x_sign have \ different signs, jump to MV50 \ The signs are the same, so we can add the two \ arguments and keep the sign to get the result LDA P+1 \ First we add the low bytes: CLC \ ADC INWK,X \ P+1 = P+1 + x_lo STA P+1 LDA P+2 \ And then the high bytes: ADC INWK+1,X \ STA P+2 \ P+2 = P+2 + x_hi TYA \ Restore the original A argument that we stored earlier \ so that we keep the original sign RTS \ Return from the subroutine .MV50 LDA INWK,X \ First we subtract the low bytes: SEC \ SBC P+1 \ P+1 = x_lo - P+1 STA P+1 LDA INWK+1,X \ And then the high bytes: SBC P+2 \ STA P+2 \ P+2 = x_hi - P+2 BCC MV51 \ If the last subtraction underflowed, then the C flag \ will be clear and x_hi < P+2, so jump to MV51 to \ negate the result TYA \ Restore the original A argument that we stored earlier EOR #%10000000 \ but flip bit 7, which flips the sign. We do this \ because x_hi >= P+2 so we want the result to have the \ same sign as x_hi (as it's the dominant side in this \ calculation). The sign of x_hi is x_sign, and x_sign \ has the opposite sign to A, so we flip the sign in A \ to return the correct result RTS \ Return from the subroutine .MV51 LDA #1 \ Our subtraction underflowed, so we negate the result SBC P+1 \ using two's complement, first with the low byte: STA P+1 \ \ P+1 = 1 - P+1 LDA #0 \ And then the high byte: SBC P+2 \ STA P+2 \ P+2 = 0 - P+2 TYA \ Restore the original A argument that we stored earlier \ as this is the correct sign for the result. This is \ because x_hi < P+2, so we want to return the same sign \ as P+2, the dominant side RTS \ Return from the subroutine
Name: MV40 [Show more] Type: Subroutine Category: Moving Summary: Rotate the planet or sun's location in space by the amount of pitch and roll of our ship
Context: See this subroutine on its own page References: This subroutine is called as follows: * MVEIT_FLIGHT (Part 2 of 6) calls MV40

We implement this using the same equations as in part 5 of MVEIT, where we rotated the current ship's location by our pitch and roll. Specifically, the calculation is as follows: 1. K2 = y - alpha * x 2. z = z + beta * K2 3. y = K2 - beta * z 4. x = x + alpha * y See the deep dive on "Rotating the universe" for more details on the above.
.MV40 LDA ALPHA \ Set Q = -ALPHA, so Q contains the angle we want to EOR #%10000000 \ roll the planet through (i.e. in the opposite STA Q \ direction to our ship's roll angle alpha) LDA INWK \ Set P(1 0) = (x_hi x_lo) STA P LDA INWK+1 STA P+1 LDA INWK+2 \ Set A = x_sign JSR MULT3 \ Set K(3 2 1 0) = (A P+1 P) * Q \ \ which also means: \ \ K(3 2 1) = (A P+1 P) * Q / 256 \ = x * -alpha / 256 \ = - alpha * x / 256 LDX #3 \ Set K(3 2 1) = (y_sign y_hi y_lo) + K(3 2 1) JSR MVT3 \ = y - alpha * x / 256 LDA K+1 \ Set K2(2 1) = P(1 0) = K(2 1) STA K2+1 STA P LDA K+2 \ Set K2+2 = K+2 STA K2+2 STA P+1 \ Set P+1 = K+2 LDA BETA \ Set Q = beta, the pitch angle of our ship STA Q LDA K+3 \ Set K+3 to K2+3, so now we have result 1 above: STA K2+3 \ \ K2(3 2 1) = K(3 2 1) \ = y - alpha * x / 256 \ We also have: \ \ A = K+3 \ \ P(1 0) = K(2 1) \ \ so combined, these mean: \ \ (A P+1 P) = K(3 2 1) \ = K2(3 2 1) JSR MULT3 \ Set K(3 2 1 0) = (A P+1 P) * Q \ \ which also means: \ \ K(3 2 1) = (A P+1 P) * Q / 256 \ = K2(3 2 1) * beta / 256 \ = beta * K2 / 256 LDX #6 \ K(3 2 1) = (z_sign z_hi z_lo) + K(3 2 1) JSR MVT3 \ = z + beta * K2 / 256 LDA K+1 \ Set P = K+1 STA P STA INWK+6 \ Set z_lo = K+1 LDA K+2 \ Set P+1 = K+2 STA P+1 STA INWK+7 \ Set z_hi = K+2 LDA K+3 \ Set A = z_sign = K+3, so now we have: STA INWK+8 \ \ (z_sign z_hi z_lo) = K(3 2 1) \ = z + beta * K2 / 256 \ So we now have result 2 above: \ \ z = z + beta * K2 EOR #%10000000 \ Flip the sign bit of A to give A = -z_sign JSR MULT3 \ Set K(3 2 1 0) = (A P+1 P) * Q \ = (-z_sign z_hi z_lo) * beta \ = -z * beta LDA K+3 \ Set T to the sign bit of K(3 2 1 0), i.e. to the sign AND #%10000000 \ bit of -z * beta STA T EOR K2+3 \ If K2(3 2 1 0) has a different sign to K(3 2 1 0), BMI MV1 \ then EOR'ing them will produce a 1 in bit 7, so jump \ to MV1 to take this into account \ If we get here, K and K2 have the same sign, so we can \ add them together to get the result we're after, and \ then set the sign afterwards LDA K \ We now do the following sum: CLC \ ADC K2 \ (A y_hi y_lo -) = K(3 2 1 0) + K2(3 2 1 0) \ \ starting with the low bytes (which we don't keep) \ \ The CLC has no effect because MULT3 clears the C \ flag, so this instruction could be removed (as it is \ in the cassette version, for example) LDA K+1 \ We then do the middle bytes, which go into y_lo ADC K2+1 STA INWK+3 LDA K+2 \ And then the high bytes, which go into y_hi ADC K2+2 STA INWK+4 LDA K+3 \ And then the sign bytes into A, so overall we have the ADC K2+3 \ following, if we drop the low bytes from the result: \ \ (A y_hi y_lo) = (K + K2) / 256 JMP MV2 \ Jump to MV2 to skip the calculation for when K and K2 \ have different signs .MV1 LDA K \ If we get here then K2 and K have different signs, so SEC \ instead of adding, we need to subtract to get the SBC K2 \ result we want, like this: \ \ (A y_hi y_lo -) = K(3 2 1 0) - K2(3 2 1 0) \ \ starting with the low bytes (which we don't keep) LDA K+1 \ We then do the middle bytes, which go into y_lo SBC K2+1 STA INWK+3 LDA K+2 \ And then the high bytes, which go into y_hi SBC K2+2 STA INWK+4 LDA K2+3 \ Now for the sign bytes, so first we extract the sign AND #%01111111 \ byte from K2 without the sign bit, so P = |K2+3| STA P LDA K+3 \ And then we extract the sign byte from K without the AND #%01111111 \ sign bit, so A = |K+3| SBC P \ And finally we subtract the sign bytes, so P = A - P STA P \ By now we have the following, if we drop the low bytes \ from the result: \ \ (A y_hi y_lo) = (K - K2) / 256 \ \ so now we just need to make sure the sign of the \ result is correct BCS MV2 \ If the C flag is set, then the last subtraction above \ didn't underflow and the result is correct, so jump to \ MV2 as we are done with this particular stage LDA #1 \ Otherwise the subtraction above underflowed, as K2 is SBC INWK+3 \ the dominant part of the subtraction, so we need to STA INWK+3 \ negate the result using two's complement, starting \ with the low bytes: \ \ y_lo = 1 - y_lo LDA #0 \ And then the high bytes: SBC INWK+4 \ STA INWK+4 \ y_hi = 0 - y_hi LDA #0 \ And finally the sign bytes: SBC P \ \ A = 0 - P ORA #%10000000 \ We now force the sign bit to be negative, so that the \ final result below gets the opposite sign to K, which \ we want as K2 is the dominant part of the sum .MV2 EOR T \ T contains the sign bit of K, so if K is negative, \ this flips the sign of A STA INWK+5 \ Store A in y_sign \ So we now have result 3 above: \ \ y = K2 + K \ = K2 - beta * z LDA ALPHA \ Set A = alpha STA Q LDA INWK+3 \ Set P(1 0) = (y_hi y_lo) STA P LDA INWK+4 STA P+1 LDA INWK+5 \ Set A = y_sign JSR MULT3 \ Set K(3 2 1 0) = (A P+1 P) * Q \ = (y_sign y_hi y_lo) * alpha \ = y * alpha LDX #0 \ Set K(3 2 1) = (x_sign x_hi x_lo) + K(3 2 1) JSR MVT3 \ = x + y * alpha / 256 LDA K+1 \ Set (x_sign x_hi x_lo) = K(3 2 1) STA INWK \ = x + y * alpha / 256 LDA K+2 STA INWK+1 LDA K+3 STA INWK+2 \ So we now have result 4 above: \ \ x = x + y * alpha JMP MV45 \ We have now finished rotating the planet or sun by \ our pitch and roll, so jump back into the MVEIT \ routine at MV45 to apply all the other movements
Name: PU1 [Show more] Type: Subroutine Category: Flight Summary: Flip the coordinate axes for the four different views Deep dive: Flipping axes between space views
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 11 of 16) calls PU1

This routine flips the relevant geometric axes in INWK depending on which view we are looking through (front, rear, left, right).
.PU1 DEX \ Decrement the view, so now: \ \ 0 = rear \ 1 = left \ 2 = right BNE PU2 \ If the current view is left or right, jump to PU2, \ otherwise this is the rear view, so continue on LDA INWK+2 \ Flip the sign of x_sign EOR #%10000000 STA INWK+2 LDA INWK+8 \ Flip the sign of z_sign EOR #%10000000 STA INWK+8 LDA INWK+10 \ Flip the sign of nosev_x_hi EOR #%10000000 STA INWK+10 LDA INWK+14 \ Flip the sign of nosev_z_hi EOR #%10000000 STA INWK+14 LDA INWK+16 \ Flip the sign of roofv_x_hi EOR #%10000000 STA INWK+16 LDA INWK+20 \ Flip the sign of roofv_z_hi EOR #%10000000 STA INWK+20 LDA INWK+22 \ Flip the sign of sidev_x_hi EOR #%10000000 STA INWK+22 LDA INWK+26 \ Flip the sign of roofv_z_hi EOR #%10000000 STA INWK+26 RTS \ Return from the subroutine .PU2 \ We enter this with X set to the view, as follows: \ \ 1 = left \ 2 = right LDA #0 \ Set RAT2 = 0 (left view) or -1 (right view) CPX #2 ROR A STA RAT2 EOR #%10000000 \ Set RAT = -1 (left view) or 0 (right view) STA RAT LDA INWK \ Swap x_lo and z_lo LDX INWK+6 STA INWK+6 STX INWK LDA INWK+1 \ Swap x_hi and z_hi LDX INWK+7 STA INWK+7 STX INWK+1 LDA INWK+2 \ Swap x_sign and z_sign EOR RAT \ If left view, flip sign of new z_sign TAX \ If right view, flip sign of new x_sign LDA INWK+8 EOR RAT2 STA INWK+2 STX INWK+8 LDY #9 \ Swap nosev_x_lo and nosev_z_lo JSR PUS1 \ Swap nosev_x_hi and nosev_z_hi \ If left view, flip sign of new nosev_z_hi \ If right view, flip sign of new nosev_x_hi LDY #15 \ Swap roofv_x_lo and roofv_z_lo JSR PUS1 \ Swap roofv_x_hi and roofv_z_hi \ If left view, flip sign of new roofv_z_hi \ If right view, flip sign of new roofv_x_hi LDY #21 \ Swap sidev_x_lo and sidev_z_lo \ Swap sidev_x_hi and sidev_z_hi \ If left view, flip sign of new sidev_z_hi \ If right view, flip sign of new sidev_x_hi .PUS1 LDA INWK,Y \ Swap the low x and z bytes for the vector in Y: LDX INWK+4,Y \ STA INWK+4,Y \ * For Y = 9 swap nosev_x_lo and nosev_z_lo STX INWK,Y \ * For Y = 15 swap roofv_x_lo and roofv_z_lo \ * For Y = 21 swap sidev_x_lo and sidev_z_lo LDA INWK+1,Y \ Swap the high x and z bytes for the offset in Y: EOR RAT \ TAX \ * If left view, flip sign of new z-coordinate LDA INWK+5,Y \ * If right view, flip sign of new x-coordinate EOR RAT2 STA INWK+1,Y STX INWK+5,Y \ Fall through into LOOK1 to return from the subroutine
Name: LOOK1 [Show more] Type: Subroutine Category: Flight Summary: Initialise the space view
Context: See this subroutine on its own page References: This subroutine is called as follows: * MJP calls LOOK1 * TT102 calls LOOK1 * TT110 calls LOOK1 * WARP calls LOOK1 * SIGHT calls entry point LO2

Initialise the space view, with the direction of view given in X. This clears the upper screen and draws the laser crosshairs, if the view in X has lasers fitted. It also wipes all the ships from the scanner, so we can recalculate ship positions for the new view (they get put back in the main flight loop). Arguments: X The space view to set: * 0 = front * 1 = rear * 2 = left * 3 = right Other entry points: LO2 Contains an RTS
.LO2 RTS \ Return from the subroutine .LQ STX VIEW \ Set the current space view to X JSR TT66 \ Clear the top part of the screen, draw a white border, \ and set the current view type in QQ11 to 0 (space \ view) JSR SIGHT \ Draw the laser crosshairs JMP NWSTARS \ Set up a new stardust field and return from the \ subroutine using a tail call .LOOK1 LDA #0 \ Set A = 0, the type number of a space view LDY QQ11 \ If the current view is not a space view, jump up to LQ BNE LQ \ to set up a new space view CPX VIEW \ If the current view is already of type X, jump to LO2 BEQ LO2 \ to return from the subroutine (as LO2 contains an RTS) STX VIEW \ Change the current space view to X JSR TT66 \ Clear the top part of the screen, draw a white border, \ and set the current view type in QQ11 to 0 (space \ view) JSR FLIP \ Swap the x- and y-coordinates of all the stardust \ particles JSR WPSHPSS \ Wipe all the ships from the scanner \ And fall through into SIGHT to draw the laser \ crosshairs
Name: SIGHT [Show more] Type: Subroutine Category: Flight Summary: Draw the laser crosshairs
Context: See this subroutine on its own page References: This subroutine is called as follows: * LOOK1 calls SIGHT
.SIGHT LDY VIEW \ Fetch the laser power for our new view LDA LASER,Y BEQ LO2 \ If it is zero (i.e. there is no laser fitted to this \ view), jump to LO2 to return from the subroutine (as \ LO2 contains an RTS) LDA #128 \ Set QQ19 to the x-coordinate of the centre of the STA QQ19 \ screen LDA #Y-24 \ Set QQ19+1 to the y-coordinate of the centre of the STA QQ19+1 \ screen, minus 24 (because TT15 will add 24 to the \ coordinate when it draws the crosshairs) LDA #20 \ Set QQ19+2 to size 20 for the crosshairs size STA QQ19+2 JSR TT15 \ Call TT15 to draw crosshairs of size 20 just to the \ left of the middle of the screen LDA #10 \ Set QQ19+2 to size 10 for the crosshairs size STA QQ19+2 JMP TT15 \ Call TT15 to draw crosshairs of size 10 at the same \ location, which will remove the centre part from the \ laser crosshairs, leaving a gap in the middle, and \ return from the subroutine using a tail call
Name: iff_xor [Show more] Type: Variable Category: Dashboard Summary: The EOR value for different types of ship in the the I.F.F. system for creating striped sticks in the scanner Deep dive: The I.F.F. system
Context: See this variable on its own page References: This variable is used as follows: * SCAN calls iff_xor

These are the EOR values for the I.F.F. system, which shows ships on the scanner in the following colours, depending on the type index for this ship (as returned by the iff_index routine). The EOR values determine whether the stick is striped. The colours for the normal dashboard palette are: Index Dot colour Stick colour(s) Ship types ----- ---------- --------------- ---------- 0 Green Green Clean 1 Yellow Yellow Station tracked 2 Green Green and yellow Debris 3 Yellow Yellow and red Missile 4 Green Green and red Offender/fugitive The colours for the escape pod dashboard palette are: Index Dot colour Stick colour(s) Ship types ----- ---------- --------------- ---------- 0 Cyan Cyan Clean 1 White White Station tracked 2 Cyan Cyan and white Debris 3 White White and red Missile 4 Cyan Cyan and red Offender/fugitive The EOR values have the following effect on the colour of the stick: %00000000 Stick is a solid colour, in the base colour %00001111 Stick is striped, in the base colour and base colour EOR %01 %11110000 Stick is striped, in the base colour and base colour EOR %10 %11111111 Stick is striped, in the base colour and base colour EOR %11 Taking the example of debris, the base colour from iff_base+2 is &FF, which is %11111111, or a four-pixel byte of colour %11, or colour 3 in mode 5, or green/cyan (green for the normal palette, cyan in the escape pod palette). The EOR value from iff_xor+2 is &0F, which is %00001111, or a four-pixel byte of %01 values. Applying this EOR to the base colour (%11) gives: %11 EOR %01 = %10 = 2 and colour 2 in mode 5 is yellow/white (yellow for the normal palette, white in the escape pod palette). So the stick colour for debris when we have an I.F.F. system fitted is: Green/cyan (the base colour) striped with yellow/white (the colour after applying the EOR value) If there is no I.F.F. system fitted, the index is 0 and the EOR value is 0, which doesn't affect the default colour. The last two entries are the same as the first two entries in iff_base, which is the next variable, so they are commented out here to save two bytes.
.iff_xor EQUB &00 \ Index 0: Clean = %00000000 EQUB &00 \ Index 1: Station tracked = %00000000 EQUB &0F \ Index 2: Debris = %00001111 \EQUB &FF \ Index 3: Missile = %11111111 \EQUB &F0 \ Index 4: Offender/fugitive = %11110000
Name: iff_base [Show more] Type: Variable Category: Dashboard Summary: Base colours for different types of ship in the the I.F.F. system Deep dive: The I.F.F. system
Context: See this variable on its own page References: This variable is used as follows: * SCAN calls iff_base

These are the base colours for the I.F.F. system, which shows ships on the scanner in the following colours, depending on the type index for this ship (as returned by the iff_index routine). The base colours determine the colour of the dot, as well as the underlying colour of the stick (which can be striped, depending on the corresponding EOR colour from iff_xor). The colours for the normal dashboard palette are: Index Dot colour Stick colour(s) Ship types 0 Green Green Clean 1 Yellow Yellow Station tracked 2 Green Green and yellow Debris 3 Yellow Yellow and red Missile 4 Green Green and red Offender/fugitive The colours for the escape pod dashboard palette are: Index Dot colour Stick colour(s) Ship types 0 Cyan Cyan Clean 1 White White Station tracked 2 Cyan Cyan and white Debris 3 White White and red Missile 4 Cyan Cyan and red Offender/fugitive
.iff_base EQUB &FF \ Index 0: Clean = green/cyan EQUB &F0 \ Index 1: Station tracked = yellow/white EQUB &FF \ Index 2: Debris = green/cyan EQUB &F0 \ Index 3: Missile = yellow/white EQUB &FF \ Index 4: Offender/fugitive = green/cyan
Name: SCAN [Show more] Type: Subroutine Category: Dashboard Summary: Display the current ship on the scanner by sending a draw_tail command to the I/O processor Deep dive: The 3D scanner The I.F.F. system
Context: See this subroutine on its own page References: This subroutine is called as follows: * ESCAPE calls SCAN * MVEIT (Part 9 of 9) calls SCAN * MVEIT_FLIGHT (Part 2 of 6) calls SCAN * Main flight loop (Part 11 of 16) calls SCAN * WPSHPS calls SCAN

This is used both to display a ship on the scanner, and to erase it again. Arguments: INWK The ship's data block
.SC5 RTS \ Return from the subroutine .SCAN LDA INWK+31 \ Fetch the ship's scanner flag from byte #31 AND #%00010000 \ If bit 4 is clear then the ship should not be shown BEQ SC5 \ on the scanner, so return from the subroutine (as SC5 \ contains an RTS) LDA TYPE \ Fetch the ship's type from TYPE into A BMI SC5 \ If this is the planet or the sun, then the type will \ have bit 7 set and we don't want to display it on the \ scanner, so return from the subroutine (as SC5 \ contains an RTS) LDX CRGO \ If we do not have an I.F.F. system fitted (i.e. CRGO BEQ iff_not \ is zero), jump to iff_not to fetch the default \ colours, which are those for a trader or innocent \ bystander (i.e. X = 0) \ If we get here then X = &FF (as CRGO is &FF if we have \ an I.F.F. system fitted) LDY #36 \ Set A to byte #36 of the ship's blueprint, i.e. the LDA (INF),Y \ NEWB flags ASL A \ If bit 6 is set, i.e. this is a cop, a space station ASL A \ or an escape pod, jump to iff_cop to set X = 1 BCS iff_cop ASL A \ If bit 5 is set, i.e. this is an innocent bystander BCS iff_trade \ (which applies to traders and some bounty hunters), \ jump to iff_trade to set X = 0 LDY TYPE \ Set Y to the ship's type - 1 DEY BEQ iff_missle \ If Y = 0, i.e. this is a missile, then jump to \ iff_missle to set X = 3 CPY #8 \ If Y < 8, i.e. this is a cargo canister, alloy plate, BCC iff_aster \ boulder, asteroid or splinter, then jump to iff_aster \ to set X = 2 \ If we get here then the ship is not the following: \ \ * A cop/station/escape pod \ * An innocent bystander/trader/good bounty hunter \ * A missile \ * Cargo or an asteroid \ \ So it must be a pirate or a non-innocent bounty hunter INX \ X is &FF at this point, so this INX sets X = 0, and we \ then fall through into the four INX instructions below \ to set X = 4 .iff_missle INX \ If we jump to this point, then set X = 3 .iff_aster INX \ If we jump to this point, then set X = 2 .iff_cop INX \ If we jump to this point, then set X = 1 .iff_trade INX \ If we jump to this point, then set X = 0 .iff_not LDA iff_base,X \ Set COL to the base colour for this ship, given the STA COL \ I.F.F. colour index in X (this colour is used for both \ the dot and stick) LDA iff_xor,X \ Set Y2 to the alternating colour for this ship, given STA Y2 \ the I.F.F. colour index in X (this colour is used for \ making the stick striped, where appropriate) LDA INWK+1 \ If any of x_hi, y_hi and z_hi have a 1 in bit 6 or 7, ORA INWK+4 \ then the ship is too far away to be shown on the ORA INWK+7 \ scanner, so return from the subroutine (as SC5 AND #%11000000 \ contains an RTS) BNE SC5 \ If we get here, we know x_hi, y_hi and z_hi are all \ 63 (%00111111) or less \ Now, we convert the x_hi coordinate of the ship into \ the screen x-coordinate of the dot on the scanner, \ using the following (see the deep dive on "The 3D \ scanner" for an explanation): \ \ X1 = 123 + (x_sign x_hi) LDA INWK+1 \ Set x_hi CLC \ Clear the C flag so we can do addition below LDX INWK+2 \ Set X = x_sign BPL SC2 \ If x_sign is positive, skip the following EOR #%11111111 \ x_sign is negative, so flip the bits in A and subtract ADC #1 \ 1 to make it a negative number (bit 7 will now be set \ as we confirmed above that bits 6 and 7 are clear). So \ this gives A the sign of x_sign and gives it a value \ range of -63 (%11000001) to 0 .SC2 ADC #123 \ Set X1 = 123 + x_hi STA X1 \ Next, we convert the z_hi coordinate of the ship into \ the y-coordinate of the base of the ship's stick, \ like this (see the deep dive on "The 3D scanner" for \ an explanation): \ \ SC = 220 - (z_sign z_hi) / 4 \ \ though the following code actually does it like this: \ \ SC = 255 - (35 + z_hi / 4) LDA INWK+7 \ Set A = z_hi / 4 LSR A \ LSR A \ So A is in the range 0-15 CLC \ Clear the C flag LDX INWK+8 \ Set X = z_sign BPL SC3 \ If z_sign is positive, skip the following EOR #%11111111 \ z_sign is negative, so flip the bits in A and set the SEC \ C flag. As above, this makes A negative, this time \ with a range of -16 (%11110000) to -1 (%11111111). And \ as we are about to do an ADC, the SEC effectively adds \ another 1 to that value, giving a range of -15 to 0 .SC3 ADC #35 \ Set A = 35 + A to give a number in the range 20 to 50 EOR #%11111111 \ Flip all the bits and store in SC, so SC is in the STA SC \ range 205 to 235, with a higher z_hi giving a lower SC \ Now for the stick height, which we calculate using the \ following (see the deep dive on "The 3D scanner" for \ an explanation): \ \ A = - (y_sign y_hi) / 2 LDA INWK+4 \ Set A = y_hi / 2 LSR A CLC \ Clear the C flag LDX INWK+5 \ Set X = y_sign BMI SCD6 \ If y_sign is negative, skip the following, as we \ already have a positive value in A EOR #%11111111 \ y_sign is positive, so flip the bits in A and set the SEC \ C flag. This makes A negative, and as we are about to \ do an ADC below, the SEC effectively adds another 1 to \ that value to implement two's complement negation, so \ we don't need to add another 1 here .SCD6 \ We now have all the information we need to draw this \ ship on the scanner, namely: \ \ X1 = the screen x-coordinate of the ship's dot \ \ SC = the screen y-coordinate of the base of the \ stick \ \ A = the screen height of the ship's stick, with the \ correct sign for adding to the base of the stick \ to get the dot's y-coordinate \ \ First, though, we have to make sure the dot is inside \ the dashboard, by moving it if necessary ADC SC \ Set A = SC + A, so A now contains the y-coordinate of \ the end of the stick, plus the length of the stick, to \ give us the screen y-coordinate of the dot BPL ld246 \ If the result has bit 0 clear, then the result has \ overflowed and is bigger than 256, so jump to ld246 to \ set A to the maximum allowed value of 246 (this \ instruction isn't required as we test both the maximum \ and minimum below, but it might save a few cycles) CMP #194 \ If A >= 194, skip the following instruction, as 194 is BCS P%+4 \ the minimum allowed value of A LDA #194 \ A < 194, so set A to 194, the minimum allowed value \ for the y-coordinate of our ship's dot CMP #247 \ If A < 247, skip the following instruction, as 246 is BCC P%+4 \ the maximum allowed value of A .ld246 LDA #246 \ A >= 247, so set A to 246, the maximum allowed value \ for the y-coordinate of our ship's dot STA Y1 \ Store A in Y1, as it now contains the screen \ y-coordinate for the ship's dot, clipped so that it \ fits within the dashboard SEC \ Set A = A - SC to get the stick length, by reversing SBC SC \ the ADC SC we did above. This clears the C flag if the \ result is negative (i.e. the stick length is negative) \ and sets it if the result is positive (i.e. the stick \ length is negative) \ So now we have the following: \ \ X1 = the screen x-coordinate of the ship's dot, \ clipped to fit into the dashboard \ \ Y1 = the screen y-coordinate of the ship's dot, \ clipped to fit into the dashboard \ \ SC = the screen y-coordinate of the base of the \ stick \ \ A = the screen height of the ship's stick, with the \ correct sign for adding to the base of the stick \ to get the dot's y-coordinate \ \ C = 0 if A is negative, 1 if A is positive \ \ and we can get on with drawing the dot and stick TAX \ Copy the stick height in A into X LDA #&91 \ Send command &91 to the I/O processor: JSR tube_write \ \ draw_tail(x, y, base_colour, alt_colour, height) \ \ which will draw a ship on the 3D scanner with a dot \ and stick of the specified height and colour, possibly \ with stripes LDA X1 \ Send the first parameter to the I/O processor: JSR tube_write \ \ * x1 = X1 LDA Y1 \ Send the second parameter to the I/O processor: JSR tube_write \ \ * y1 = Y1 LDA COL \ Send the third parameter to the I/O processor: JSR tube_write \ \ * base_colour = COL LDA Y2 \ Send the fourth parameter to the I/O processor: JSR tube_write \ \ * alt_colour = Y2 TXA \ Send the fifth parameter to the I/O processor: JSR tube_write \ \ * height = the stick height that we stored in X LDX #0 \ Set X = 0 to ensure we return the same value as the \ SCAN routine in the non-Tube version RTS \ Return from the subroutine
Save ELTM.bin
PRINT "ELITE M" PRINT "Assembled at ", ~CODE_M% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_M%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_M% PRINT "S.2.ELTM ", ~CODE_M%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_M% SAVE "versions/disc/3-assembled-output/2.ELTM.bin", CODE_M%, P%, LOAD%