CODE_F% = P% LOAD_F% = LOAD% + P% - CODE%ELITE F FILE Produces the binary file ELTF.bin that gets loaded by elite-bcfs.asm..KS3 LDA P \ After shuffling the ship slots, P(1 0) will point to STA SLSP \ the new bottom of the ship line heap, so store this in LDA P+1 \ SLSP(1 0), which stores the bottom of the heap STA SLSP+1 RTS \ Return from the subroutineName: KS3 [Show more] Type: Subroutine Category: Universe Summary: Set the SLSP ship line heap pointer after shuffling ship slotsContext: See this subroutine on its own page References: This subroutine is called as follows: * KS2 calls KS3
The final part of the KILLSHP routine, called after we have shuffled the ship slots and sorted out our missiles. This simply sets SLSP to the new bottom of the ship line heap.
Arguments: P(1 0) Points to the ship line heap of the ship in the last occupied slot (i.e. it points to the bottom of the descending heap).KS1 LDX XSAV \ Store the current ship's slot number in XSAV JSR KILLSHP \ Call KILLSHP to remove the ship in slot X from our \ local bubble of universe LDX XSAV \ Restore the current ship's slot number from XSAV, \ which now points to the next ship in the bubble JMP MAL1 \ Jump to MAL1 to rejoin the main flight loop at the \ start of the ship analysis loopName: KS1 [Show more] Type: Subroutine Category: Universe Summary: Remove the current ship from our local bubble of universeContext: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 12 of 16) calls KS1
Part 12 of the main flight loop calls this routine to remove the ship that is currently being analysed by the flight loop. Once the ship is removed, it jumps back to MAL1 to rejoin the main flight loop, with X pointing to the same slot that we just cleared (and which now contains the next ship in the local bubble of universe).
Arguments: XX0 The address of the blueprint for this ship INF The address of the data block for this ship.KS4 JSR ZINF \ Call ZINF to reset the INWK ship workspace LDA #0 \ Set A = 0 so we can zero the following flags STA FRIN+1 \ Set the second slot in the FRIN table to 0, which \ sets this slot to empty, so when we call NWSHP below \ the placeholder that gets created will go into FRIN+1 STA SSPR \ Set the "space station present" flag to 0, as we are \ no longer in the space station's safe zone JSR SPBLB \ Call SPBLB to redraw the space station bulb, which \ will erase it from the dashboard LDA #6 \ Set the placeholder's y_sign to 6 STA INWK+5 LDA #129 \ Set A = 129, the ship type for the placeholder, so \ there isn't a space station, but there is a non-zero \ ship type to indicate this JMP NWSHP \ Call NWSHP to set up the new data block and add it \ to FRIN, where it will get put in the second slot as \ we just cleared out the second slot, and the first \ slot is already taken by the planetName: KS4 [Show more] Type: Subroutine Category: Universe Summary: Remove the space station and replace with a placeholderContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * KILLSHP calls KS4.KS2 LDX #&FF \ We want to go through the ships in our local bubble \ and pick out all the missiles, so set X to &FF to \ use as a counter .KSL4 INX \ Increment the counter (so it starts at 0 on the first \ iteration) LDA FRIN,X \ If slot X is empty then we have worked our way through BEQ KS3 \ all the slots, so jump to KS3 to stop looking CMP #MSL \ If the slot does not contain a missile, loop back to BNE KSL4 \ KSL4 to check the next slot \ We have found a slot containing a missile, so now we \ want to check whether it has target lock TXA \ Set Y = X * 2 and fetch the Y-th address from UNIV ASL A \ and store it in SC and SC+1 - in other words, set TAY \ SC(1 0) to point to the missile's ship data block LDA UNIV,Y STA SC LDA UNIV+1,Y STA SC+1 LDY #32 \ Fetch byte #32 from the missile's ship data (AI) LDA (SC),Y BPL KSL4 \ If bit 7 of byte #32 is clear, then the missile is \ dumb and has no AI, so loop back to KSL4 to move on \ to the next slot AND #%01111111 \ Otherwise this missile has AI, so clear bit 7 and LSR A \ shift right to set the C flag to the missile's "is \ locked" flag, and A to the target's slot number CMP XX4 \ If this missile's target is less than XX4, then the BCC KSL4 \ target's slot isn't being shuffled down, so jump to \ KSL4 to move on to the next slot BEQ KS6 \ If this missile was locked onto the ship that we just \ removed in KILLSHP, jump to KS6 to stop the missile \ from continuing to hunt it down SBC #1 \ Otherwise this missile is locked and has AI enabled, \ and its target will have moved down a slot, so \ subtract 1 from the target number (we know C is set \ from the BCC above) ASL A \ Shift the target number left by 1, so it's in bits \ 1-6 once again, and also set bit 0 to 1, as the C \ flag is still set, so this makes sure the missile is \ still set to being locked ORA #%10000000 \ Set bit 7, so the missile's AI is enabled STA (SC),Y \ Update the missile's AI flag to the value in A BNE KSL4 \ Loop back to KSL4 to move on to the next slot (this \ BNE is effectively a JMP as A will never be zero) .KS6 LDA #0 \ The missile's target lock just got removed, so set the STA (SC),Y \ AI flag to 0 to make it dumb and not locked BEQ KSL4 \ Loop back to KSL4 to move on to the next slot (this \ BEQ is effectively a JMP as A is always zero)Name: KS2 [Show more] Type: Subroutine Category: Universe Summary: Check the local bubble for missiles with target lockContext: See this subroutine on its own page References: This subroutine is called as follows: * KILLSHP calls KS2
Check the local bubble of universe to see if there are any missiles with target lock in the vicinity. If there are, then check their targets; if we just removed their target in the KILLSHP routine, then switch off their AI so they just drift in space, otherwise update their targets to reflect the newly shuffled slot numbers. This is called from KILLSHP once the slots have been shuffled down, following the removal of a ship.
Arguments: XX4 The slot number of the ship we removed just before calling this routine.KILLSHP STX XX4 \ Store the slot number of the ship to remove in XX4 LDA MSTG \ Check whether this slot matches the slot number in CMP XX4 \ MSTG, which is the target of our missile lock BNE KS5 \ If our missile is not locked on this ship, jump to KS5 JSR ABORT-2 \ Otherwise we need to remove our missile lock, so call \ ABORT-2 to disarm the missile and update the missile \ indicators on the dashboard to disarmed (white \ squares) LDA #200 \ Print recursive token 40 ("TARGET LOST") as an JSR MESS \ in-flight message .KS5 LDY XX4 \ Restore the slot number of the ship to remove into Y LDX FRIN,Y \ Fetch the contents of the slot, which contains the \ ship type CPX #SST \ If this is the space station, then jump to KS4 to BEQ KS4 \ remove the space station DEC MANY,X \ Decrease the number of this type of ship in our little \ bubble, which is stored in MANY+X (where X is the ship \ type) LDX XX4 \ Restore the slot number of the ship to remove into X \ We now want to remove this ship and reclaim all the \ memory that it uses. Removing the ship will leave a \ gap in three places, which we need to close up: \ \ * The ship slots in FRIN \ \ * The ship data blocks in K% \ \ * The descending ship line heap at WP down \ \ The rest of this routine closes up these gaps by \ looping through all the occupied ship slots after the \ slot we are removing, one by one, and shuffling each \ ship's slot, data block and line heap down to close \ up the gaps left by the removed ship. As part of this, \ we have to make sure we update any address pointers \ so they point to the newly shuffled data blocks and \ line heaps \ \ In the following, when shuffling a ship's data down \ into the preceding empty slot, we call the ship that \ we are shuffling down the "source", and we call the \ empty slot we are shuffling it into the "destination" \ \ Before we start looping through the ships we need to \ shuffle down, we need to set up some variables to \ point to the source and destination line heaps LDY #5 \ Fetch byte #5 of the removed ship's blueprint into A, LDA (XX0),Y \ which gives the ship's maximum heap size for the ship \ we are removing (i.e. the size of the gap in the heap \ created by the ship removal) \ INF currently contains the ship data for the ship we \ are removing, and INF(34 33) contains the address of \ the bottom of the ship's heap, so we can calculate \ the address of the top of the heap by adding the heap \ size to this address LDY #33 \ First we add A and the address in INF+33, to get the CLC \ low byte of the top of the heap, which we store in P ADC (INF),Y STA P INY \ And next we add A and the address in INF+34, with any LDA (INF),Y \ carry from the previous addition, to get the high byte ADC #0 \ of the top of the heap, which we store in P+1, so STA P+1 \ P(1 0) points to the top of this ship's heap \ Now, we're ready to start looping through the ships \ we want to move, moving the slots, data blocks and \ line heap from the source to the destination. In the \ following, we set up SC to point to the source data, \ and INF (which currently points to the removed ship's \ data that we can now overwrite) points to the \ destination \ \ So P(1 0) now points to the top of the line heap for \ the destination .KSL1 INX \ On entry, X points to the empty slot we want to \ shuffle the next ship into (the destination), so \ this increment points X to the next slot - i.e. the \ source slot we want to shuffle down LDA FRIN,X \ Copy the contents of the source slot into the STA FRIN-1,X \ destination slot BEQ KS2 \ If the slot we just shuffled down contains 0, then \ the source slot is empty and we are done shuffling, \ so jump to KS2 to move on to processing missiles ASL A \ Otherwise we have a source ship to shuffle down into TAY \ the destination, so set Y = A * 2 so it can act as an \ index into the two-byte ship blueprint lookup table \ at XX21 for the source ship LDA XX21-2,Y \ Set SC(0 1) to point to the blueprint data for the STA SC \ source ship LDA XX21-1,Y STA SC+1 LDY #5 \ Fetch blueprint byte #5 for the source ship, which LDA (SC),Y \ gives us its maximum heap size, and store it in T STA T \ We now subtract T from P(1 0), so P(1 0) will point to \ the bottom of the line heap for the destination \ (which we will use later when closing up the gap in \ the heap space) LDA P \ First, we subtract the low bytes SEC SBC T STA P LDA P+1 \ And then we do the high bytes, for which we subtract SBC #0 \ 0 to include any carry, so this is effectively doing STA P+1 \ P(1 0) = P(1 0) - (0 T) \ Next, we want to set SC(1 0) to point to the source \ ship's data block TXA \ Set Y = X * 2 so it can act as an index into the ASL A \ two-byte lookup table at UNIV, which contains the TAY \ addresses of the ship data blocks. In this case we are \ multiplying X by 2, and X contains the source ship's \ slot number so Y is now an index for the source ship's \ entry in UNIV LDA UNIV,Y \ Set SC(1 0) to the address of the data block for the STA SC \ source ship LDA UNIV+1,Y STA SC+1 \ We have now set up our variables as follows: \ \ SC(1 0) points to the source's ship data block \ \ INF(1 0) points to the destination's ship data block \ \ P(1 0) points to the destination's line heap \ \ so let's start copying data from the source to the \ destination LDY #35 \ We are going to be using Y as a counter for the 36 \ bytes of ship data we want to copy from the source \ to the destination, so we set it to 35 to start things \ off, and will decrement Y for each byte we copy LDA (SC),Y \ Fetch byte #35 of the source's ship data block at SC, STA (INF),Y \ and store it in byte #35 of the destination's block \ at INF, so that's the ship's energy copied from the \ source to the destination. One down, quite a few to \ go... DEY \ Fetch byte #34 of the source ship, which is the LDA (SC),Y \ high byte of the source ship's line heap, and store STA K+1 \ in K+1 LDA P+1 \ Set the low byte of the destination's heap pointer STA (INF),Y \ to P+1 DEY \ Fetch byte #33 of the source ship, which is the LDA (SC),Y \ low byte of the source ship's heap, and store in K STA K \ so now we have the following: \ \ K(1 0) points to the source's line heap LDA P \ Set the low byte of the destination's heap pointer STA (INF),Y \ to P, so now the destination's heap pointer is to \ P(1 0), so that's the heap pointer in bytes #33 and \ #34 done DEY \ Luckily, we can just copy the rest of the source's \ ship data block into the destination, as there are no \ more address pointers, so first we decrement our \ counter in Y to point to the next byte (the AI flag) \ in byte #32) and then start looping .KSL2 LDA (SC),Y \ Copy the Y-th byte of the source to the Y-th byte of STA (INF),Y \ the destination DEY \ Decrement the counter BPL KSL2 \ Loop back to KSL2 to copy the next byte until we have \ copied the whole block \ We have now shuffled the ship's slot and the ship's \ data block, so we only have the heap data itself to do LDA SC \ First, we copy SC into INF, so when we loop round STA INF \ again, INF will correctly point to the destination for LDA SC+1 \ the next iteration STA INF+1 LDY T \ Now we want to move the contents of the heap, as all \ we did above was to update the pointers, so first \ we set a counter in Y that is initially set to T \ (which we set above to the maximum heap size for the \ source ship) \ \ As a reminder, we have already set the following: \ \ K(1 0) points to the source's line heap \ \ P(1 0) points to the destination's line heap \ \ so we can move the heap data by simply copying the \ correct number of bytes from K(1 0) to P(1 0) .KSL3 DEY \ Decrement the counter LDA (K),Y \ Copy the Y-th byte of the source heap at K(1 0) to STA (P),Y \ the destination heap at P(1 0) TYA \ Loop back to KSL3 to copy the next byte, until we BNE KSL3 \ have done them all BEQ KSL1 \ We have now shuffled everything down one slot, so \ jump back up to KSL1 to see if there is another slot \ that needs shuffling down (this BEQ is effectively a \ JMP as A will always be zero)Name: KILLSHP [Show more] Type: Subroutine Category: Universe Summary: Remove a ship from our local bubble of universeContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * KS1 calls KILLSHP
Remove the ship in slot X from our local bubble of universe. This happens when we kill a ship, collide with a ship and destroy it, or when a ship moves outside our local bubble. We also use this routine when we move out of range of the space station, in which case we replace it with the placeholder, type 129. When removing a ship, this creates a gap in the ship slots at FRIN, so we shuffle all the later slots down to close the gap. We also shuffle the ship data blocks at K% and ship line heap at WP, to reclaim all the memory that the removed ship used to occupy.
Arguments: X The slot number of the ship to remove XX0 The address of the blueprint for the ship to remove INF The address of the data block for the ship to remove.SFX EQUB &11, &01, &00, &03 \ 0 - Lasers fired by us EQUB &11, &02, &2C, &04 \ 8 - We're being hit by lasers EQUB &11, &03, &F0, &06 \ 16 - We made a hit or kill 2 EQUB &10, &F1, &04, &05 \ 24 - We died / We made a hit or kill 1 EQUB &01, &F1, &BC, &01 \ 32 - Short, high beep EQUB &11, &F4, &0C, &08 \ 40 - Long, low beep EQUB &10, &F1, &04, &06 \ 48 - Missile launched / Ship launched from station EQUB &10, &02, &60, &10 \ 56 - Hyperspace drive engaged EQUB &11, &04, &C2, &FF \ 64 - E.C.M. on EQUB &11, &00, &00, &00 \ 72 - E.C.M. off .SFX2 EQUB &70 \ 0 - Priority 112, minimum duration 0 EQUB &24 \ 8 - Priority 36, minimum duration 4 EQUB &56 \ 16 - Priority 86, minimum duration 6 EQUB &56 \ 24 - Priority 86, minimum duration 6 EQUB &42 \ 32 - Priority 66, minimum duration 2 EQUB &28 \ 40 - Priority 40, minimum duration 8 EQUB &C8 \ 48 - Priority 200, minimum duration 8 EQUB &D0 \ 56 - Priority 208, minimum duration 0 EQUB &F0 \ 64 - Priority 240, minimum duration 0 EQUB &E0 \ 72 - Priority 224, minimum duration 0Name: SFX [Show more] Type: Variable Category: Sound Summary: Sound dataContext: See this variable on its own page Variations: See code variations for this variable in the different versions References: This variable is used as follows: * NOS1 uses SFX
Sound data. To make a sound, the NOS1 routine copies the four relevant sound bytes to XX16, and NO3 then makes the sound. The sound numbers are shown in the table, and are always multiples of 8. Generally, sounds are made by calling the NOISE routine with the sound number in A. These bytes are passed to OSWORD 7, and are the equivalents to the parameters passed to the SOUND keyword in BASIC. The parameters therefore have these meanings: channel/flush, amplitude (or envelope number if 1-4), pitch, duration For the channel/flush parameter, the high nibble of the low byte is the flush control (where a flush control of 0 queues the sound, and a flush control of 1 makes the sound instantly), while the low nibble of the low byte is the channel number. When written in hexadecimal, the first figure gives the flush control, while the second is the channel (so &13 indicates flush control = 1 and channel = 3). So when we call NOISE with A = 40 to make a long, low beep, then this is effectively what the NOISE routine does: SOUND &13, &F4, &0C, &08 which makes a sound with flush control 1 on channel 3, and with amplitude &F4 (-12), pitch &0C (2) and duration &08 (8). Meanwhile, to make the hyperspace sound, the NOISE routine does this: SOUND &10, &02, &60, &10 which makes a sound with flush control 1 on channel 0, using envelope 2, and with pitch &60 (96) and duration &10 (16). The four sound envelopes (1-4) are set up by the loading process. The Electron has an additional layer of sound data for each of the game's sounds - priority and minimum duration. Because the Electron only has one tone channel and one noise channel, it prioritises each sound. If a sound is already playing and a sound of a higher priority needs to be made, the new sound will take over; if, however, the new sound is of a lower priority, it gets discarded and doesn't get made. This system works alongside the minimum duration value; after the minimum duration, the priority system is ignored, so once a high priority sound has sounded for its minimum duration, then even if that high priority sound is still being made, a lower priority sound can come along and take precedence. To put it another way, high priority sounds take control of the sound output and lower priority sounds don't get a look-in, but only for the minimum duration of the higher priority sound..RESET JSR ZERO \ Zero-fill pages &9, &A, &B, &C and &D, which clears \ the ship data blocks, the ship line heap, the ship \ slots for the local bubble of universe, and various \ flight and ship status variables LDX #6 \ Set up a counter for zeroing BETA through BETA+6 .SAL3 STA BETA,X \ Zero the X-th byte after BETA DEX \ Decrement the loop counter BPL SAL3 \ Loop back for the next byte to zero STX QQ12 \ X is now negative - i.e. &FF - so this sets QQ12 to \ &FF to indicate we are docked \ We now fall through into RES4 to restore shields and \ energy, and reset the stardust and ship workspace at \ INWK .RES4 LDA #&FF \ Set A to &FF so we can fill up the shields and energy \ bars with a full charge LDX #2 \ We're now going to recharge both shields and the \ energy bank, which live in the three bytes at FSH, \ ASH (FSH+1) and ENERGY (FSH+2), so set a loop counter \ in X for 3 bytes .REL5 STA FSH,X \ Set the X-th byte of FSH to &FF to charge up that \ shield/bank DEX \ Decrement the loop counter BPL REL5 \ Loop back to REL5 until we have recharged both shields \ and the energy bank \ Fall through into RES2 to reset the stardust and ship \ workspace at INWKName: RESET [Show more] Type: Subroutine Category: Start and end Summary: Reset most variablesContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * ESCAPE calls RESET * TITLE calls RESET * Main flight loop (Part 9 of 16) calls via RES4
Reset our ship and various controls, recharge shields and energy, and then fall through into RES2 to reset the stardust and the ship workspace at INWK. In this subroutine, this means zero-filling the following locations: * Pages &9, &A, &B, &C and &D * BETA to BETA+6, which covers the following: * BETA, BET1 - Set pitch to 0 * XC, YC - Set text cursor to (0, 0) * QQ22 - Set hyperspace counters to 0 * ECMA - Turn E.C.M. off It also sets QQ12 to &FF, to indicate we are docked, recharges the shields and energy banks, and then falls through into RES2.
Other entry points: RES4 Reset the shields and energy banks, then fall through into RES2 to reset the stardust and the ship workspace at INWK.RES2 LDX #&FF \ Reset LSX2 and LSY2, the ball line heaps used by the STX LSX2 \ BLINE routine for drawing circles, to &FF, to set the STX LSY2 \ heap to empty STX MSTG \ Reset MSTG, the missile target, to &FF (no target) LDA #128 \ Set the current pitch rate to the mid-point, 128 STA JSTY STA ALP2 \ Reset ALP2 (roll sign) and BET2 (pitch sign) STA BET2 \ to negative, i.e. pitch and roll negative ASL A \ This sets A to 0 STA ALP2+1 \ Reset ALP2+1 (flipped roll sign) and BET2+1 (flipped STA BET2+1 \ pitch sign) to positive, i.e. pitch and roll negative STA MCNT \ Reset MCNT (the main loop counter) to 0 LDA #3 \ Reset DELTA (speed) to 3 STA DELTA STA ALPHA \ Reset ALPHA (roll angle alpha) to 3 STA ALP1 \ Reset ALP1 (magnitude of roll angle alpha) to 3 LDA SSPR \ Fetch the "space station present" flag, and if we are BEQ P%+5 \ not inside the safe zone, skip the next instruction JSR SPBLB \ Light up the space station bulb on the dashboard LDA ECMA \ Fetch the E.C.M. status flag, and if E.C.M. is off, BEQ yu \ skip the next instruction JSR ECMOF \ Turn off the E.C.M. sound .yu JSR WPSHPS \ Wipe all ships from the scanner JSR ZERO \ Zero-fill pages &9, &A, &B, &C and &D, which clears \ the ship data blocks, the ship line heap, the ship \ slots for the local bubble of universe, and various \ flight and ship status variables LDA #LO(WP-1) \ We have reset the ship line heap, so we now point STA SLSP \ SLSP to the byte before the WP workspace to indicate LDA #HI(WP-1) \ that the heap is empty STA SLSP+1 JSR DIALS \ Update the dashboard \ Finally, fall through into ZINF to reset the INWK \ ship workspaceName: RES2 [Show more] Type: Subroutine Category: Start and end Summary: Reset a number of flight variables and workspacesContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * DEATH calls RES2 * DEATH2 calls RES2 * ESCAPE calls RES2 * TT110 calls RES2 * TT18 calls RES2
This is called after we launch from a space station, arrive in a new system after hyperspace, launch an escape pod, or die a cold, lonely death in the depths of space.
Returns: Y Y is set to &FF.ZINF LDY #NI%-1 \ There are NI% bytes in the INWK workspace, so set a \ counter in Y so we can loop through them LDA #0 \ Set A to 0 so we can zero-fill the workspace .ZI1 STA INWK,Y \ Zero the Y-th byte of the INWK workspace DEY \ Decrement the loop counter BPL ZI1 \ Loop back for the next byte, ending when we have \ zero-filled the last byte at INWK, which leaves Y \ with a value of &FF \ Finally, we reset the orientation vectors as follows: \ \ sidev = (1, 0, 0) \ roofv = (0, 1, 0) \ nosev = (0, 0, -1) \ \ 96 * 256 (&6000) represents 1 in the orientation \ vectors, while -96 * 256 (&E000) represents -1. We \ already set the vectors to zero above, so we just \ need to set up the high bytes of the diagonal values \ and we're done. The negative nosev makes the ship \ point towards us, as the z-axis points into the screen LDA #96 \ Set A to represent a 1 (in vector terms) STA INWK+18 \ Set byte #18 = roofv_y_hi = 96 = 1 STA INWK+22 \ Set byte #22 = sidev_x_hi = 96 = 1 ORA #%10000000 \ Flip the sign of A to represent a -1 STA INWK+14 \ Set byte #14 = nosev_z_hi = -96 = -1 RTS \ Return from the subroutineName: ZINF [Show more] Type: Subroutine Category: Universe Summary: Reset the INWK workspace and orientation vectors Deep dive: Orientation vectorsContext: See this subroutine on its own page References: This subroutine is called as follows: * FRS1 calls ZINF * KS4 calls ZINF * Main game loop (Part 2 of 6) calls ZINF * SOLAR calls ZINF * Ze calls ZINF
Zero-fill the INWK ship workspace and reset the orientation vectors, with nosev pointing out of the screen, towards us.
Returns: Y Y is set to &FF.msblob LDX #4 \ Set up a loop counter in X to count through all four \ missile indicators .ss CPX NOMSL \ If the counter is equal to the number of missiles, BEQ SAL8 \ jump down to SAL8 to draw the remaining missiles, as \ the rest of them are present and should be drawn as \ white squares LDY #&04 \ Draw the missile indicator at position X in black JSR MSBAR DEX \ Decrement the counter to point to the next missile BNE ss \ Loop back to ss if we still have missiles to draw RTS \ Return from the subroutine .SAL8 LDY #&09 \ Draw the missile indicator at position X as a white JSR MSBAR \ square DEX \ Decrement the counter to point to the next missile BNE SAL8 \ Loop back to SAL8 if we still have missiles to draw RTS \ Return from the subroutineName: msblob [Show more] Type: Subroutine Category: Dashboard Summary: Display the dashboard's missile indicators as white squaresContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * BR1 (Part 2 of 2) calls msblob * EQSHP calls msblob * SOS1 calls msblob
Display the dashboard's missile indicators, with all the missiles reset to white squares (i.e. not armed or locked)..me2 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 \ Jump back into the main spawning loop at me3Name: me2 [Show more] Type: Subroutine Category: Flight Summary: Remove an in-flight message from the space viewContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * Main game loop (Part 2 of 6) calls me2.Ze JSR ZINF \ Call ZINF to reset the INWK ship workspace JSR DORND \ Set A and X to random numbers STA T1 \ Store A in T1 AND #%10000000 \ Extract the sign of A and store in x_sign STA INWK+2 TXA \ Extract the sign of X and store in y_sign AND #%10000000 STA INWK+5 LDA #32 \ Set x_hi = y_hi = z_hi = 32, a fair distance away STA INWK+1 STA INWK+4 STA INWK+7 TXA \ Set the C flag if X >= 245 (4% chance) CMP #245 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 \ randomlyName: Ze [Show more] Type: Subroutine Category: Universe Summary: Initialise the INWK workspace to a hostile ship Deep dive: Fixing ship positionsContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * DEATH calls Ze * Main game loop (Part 3 of 6) calls Ze * Main game loop (Part 4 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..DORND2 CLC \ Clear the C flag so the value of the C flag on entry \ doesn't affect the outcome .DORND LDA RAND \ Calculate the next two values f2 and f3 in the feeder ROL A \ sequence: TAX \ ADC RAND+2 \ * f2 = (f1 << 1) mod 256 + C flag on entry STA RAND \ * f3 = f0 + f2 + (1 if bit 7 of f1 is set) STX RAND+2 \ * C flag is set according to the f3 calculation LDA RAND+1 \ Calculate the next value m2 in the main sequence: TAX \ ADC RAND+3 \ * A = m2 = m0 + m1 + C flag from feeder calculation STA RAND+1 \ * X = m1 STX RAND+3 \ * C and V flags set according to the m2 calculation RTS \ Return from the subroutineName: DORND [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Generate random numbers Deep dive: Generating random numbers Fixing ship positionsContext: See this subroutine on its own page References: This subroutine is called as follows: * Ghy calls DORND * GVL calls DORND * LASLI calls DORND * LL9 (Part 1 of 12) calls DORND * Main flight loop (Part 8 of 16) calls DORND * Main flight loop (Part 11 of 16) calls DORND * Main game loop (Part 2 of 6) calls DORND * Main game loop (Part 4 of 6) calls DORND * nWq calls DORND * OUCH calls DORND * SFS1 calls DORND * STARS1 calls DORND * STARS2 calls DORND * STARS6 calls DORND * TACTICS (Part 1 of 7) calls DORND * TACTICS (Part 2 of 7) calls DORND * TACTICS (Part 4 of 7) calls DORND * TACTICS (Part 5 of 7) calls DORND * TACTICS (Part 7 of 7) calls DORND * Ze calls DORND * DOEXP calls via DORND2
Set A and X to random numbers (though note that X is set to the random number that was returned in A the last time DORND was called). The C and V flags are also set randomly. If we want to generate a repeatable sequence of random numbers, when generating explosion clouds, for example, then we call DORND2 to ensure that the value of the C flag on entry doesn't affect the outcome, as otherwise we might not get the same sequence of numbers if the C flag changes.
Other entry points: DORND2 Make sure the C flag doesn't affect the outcome.MTT4 LSR A \ Clear bit 7 of our random number in A and set the C \ flag to bit 0 of A, which is 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 randomly (as the \ C flag was set), giving the ship either no missiles or \ one missile AND #31 \ Set the ship speed to our random number, set to a ORA #16 \ minimum of 16 and a maximum of 31 STA INWK+27 LDA #CYL \ Add a new Cobra Mk III to the local bubble and fall JSR NWSHP \ through into the main game loop againName: Main game loop (Part 1 of 6) [Show more] Type: Subroutine Category: Main loop Summary: Spawn a trader (a peaceful Cobra Mk III) Deep dive: Program flow of the main game loop Ship data blocksContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: No direct references to this subroutine in this source file
This is part of the main game loop. This is where the core loop of the game lives, and it's in two parts. The shorter loop (just parts 5 and 6) is iterated when we are docked, while the entire loop from part 1 to 6 iterates if we are in space. This section covers the following: * Spawn a trader, i.e. a Cobra Mk III that isn't hostile, with a 50% chance of it having a missile, a 50% chance of it having an E.C.M., a speed between 16 and 31, and a gentle clockwise roll We call this from within the main loop, with A set to a random number..TT100 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 \ If DLY is now 0, jump to me2 to remove any in-flight \ message from the space view, and once done, return to \ me3 below, skipping the following two instructions BPL me3 \ If DLY is positive, jump to me3 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 DEC MCNT \ Decrement the main loop counter in MCNT BEQ P%+5 \ 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 JMP MLOOP \ Jump down to MLOOP 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) JSR DORND \ Set A and X to random numbers CMP #35 \ If A >= 35 (87% chance), jump down to MTT1 to skip BCS MTT1 \ the spawning of an asteroid or cargo canister and \ potentially spawn something else LDA MANY+AST \ If we already have 3 or more asteroids in the local CMP #3 \ bubble, jump down to MTT1 to skip the following and BCS MTT1 \ potentially spawn something else JSR ZINF \ Call ZINF to reset the INWK ship workspace LDA #38 \ Set z_hi = 38 (far away) STA INWK+7 JSR DORND \ Set A, X and C flag to random numbers STA INWK \ Set x_lo = random STX INWK+3 \ Set y_lo = random \ \ Note that because we use 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 AND #%10000000 \ Set x_sign = bit 7 of x_lo STA INWK+2 TXA \ Set y_sign = bit 7 of y_lo AND #%10000000 STA INWK+5 ROL INWK+1 \ Set bit 1 of x_hi to the C flag, which is random, so ROL INWK+1 \ this randomly moves us off-centre by 512 (as if x_hi \ is %00000010, then (x_hi x_lo) is 512 + x_lo) JSR DORND \ Set A, X and V flag to random numbers 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 MTT1 \ down to MTT1 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 #31 \ Set the ship speed to our random number, set to a ORA #16 \ minimum of 16 and a maximum of 31 STA INWK+27 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 #5 \ Set A to the ship number of an asteroid, and keep LDA #AST \ this value for 98.5% of the time (i.e. if random BCS P%+4 \ A >= 5 then skip the following instruction) LDA #OIL \ Set A to the ship number of a cargo canister JSR NWSHP \ Add our new asteroid or canister to the universeName: Main game loop (Part 2 of 6) [Show more] Type: Subroutine Category: Main loop Summary: Call the main flight loop, and potentially spawn a trader, an asteroid, or a cargo canister Deep dive: Program flow of the main game loop Ship data blocks Fixing ship positionsContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * Main game loop (Part 6 of 6) calls via TT100 * me2 calls via me3
This section covers the following: * Call M% to do the main flight loop * Potentially spawn a trader, asteroid or cargo canister
Other entry points: TT100 The entry point for the start of the main game loop, which calls the main flight loop and the moves into the spawning routine me3 Used by me2 to jump back into the main game loop after printing an in-flight message.MTT1 LDA SSPR \ If we are inside the space station's safe zone, jump BNE MLOOP \ to MLOOP 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%+7 \ will be the case unless we have been really, really \ bad, then skip the following two 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 \ Add a new police ship to the local bubble JSR NWSHP LDA MANY+COPS \ If we now have at least one cop in the local bubble, BNE MLOOP \ jump down to MLOOP, otherwise fall through into the \ next part to look at spawning something elseName: Main game loop (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 positionsContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions 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 badDEC EV \ Decrement EV, the extra vessels spawning delay, and BPL MLOOP \ jump to MLOOP if it is still positive, 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 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 a \ lone bounty hunter CMP #90 \ If the random number in A >= 90 (65% chance), jump to BCS MLOOP \ MLOOP to stop spawning (so there's a 35% chance of \ spawning pirates or a lone bounty hunter) 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 MLOOP \ to MLOOP 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 lone bounty hunter or a group of \ pirates 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 #200 \ If the random number in A >= 200 (13% chance), jump BCS mt1 \ to mt1 to spawn pirates, otherwise keep going to \ spawn a lone bounty hunter INC EV \ Increase the extra vessels spawning counter, to \ prevent the next attempt to spawn extra vessels AND #3 \ Set A = Y = random number in the range 3-6, which ADC #3 \ we will use to determine the type of ship TAY \ We now build the AI flag for this ship in A TXA \ First, copy the random number in X to A CMP #200 \ First, set the C flag if X >= 200 (22% chance) ROL A \ Set bit 0 of A to the C flag (i.e. there's a 22% \ 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 TYA \ Add a new ship of type Y to the local bubble, so JSR NWSHP \ that's a Mamba, Cobra Mk III or Python .mj1 JMP MLOOP \ Jump down to MLOOP, as we are done spawning ships .mt1 AND #3 \ It's time to spawn a group of pirates, so set A to a \ random number in the range 0-3, which will be the \ loop counter for spawning pirates below (so we will \ spawn 1-4 pirates) STA EV \ Delay further spawnings by this number STA XX13 \ Store the number in XX13, the pirate counter .mt3 JSR DORND \ Set A and X to random numbers AND #3 \ Set A to a random number in the range 0-3 ORA #1 \ Set A to %01 or %11 (Sidewinder or Mamba) JSR NWSHP \ Try adding a new ship of type A to the local bubble DEC XX13 \ Decrement the pirate counter BPL mt3 \ If we need more pirates, loop back up to mt3, \ otherwise we are done spawning, so fall through into \ the end of the main loop at MLOOPName: Main game loop (Part 4 of 6) [Show more] Type: Subroutine Category: Main loop Summary: Potentially spawn a lone bounty hunter or up to four pirates Deep dive: Program flow of the main game loop Ship data blocks Fixing ship positionsContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: No direct references to this subroutine in this source file
This section covers the following: * Potentially spawn (35% chance) either a lone bounty hunter (a Mamba, Python or Cobra Mk III), or a group of up to 4 pirates (Sidewinders and/or Mambas).MLOOP LDA LASCT \ Set A to the value of LASCT, the laser pulse count SBC #4 \ Decrement the value of LASCT by 4 BCS P%+4 \ If we just reduced LASCT below 0, set it to 0 LDA #0 STA LASCT \ Store the decremented value of X in LASCT, so LASCT \ gets reduced by 4, but not into negative territory 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 INX \ Set KEYB = 0 to indicate we are not currently reading STX KEYB \ the keyboard using an OS command 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 LDA QQ11 \ If this is a space view, skip the following four BEQ P%+11 \ instructions (i.e. jump to JSR TT17 below) AND PATG \ If PATG = &FF (author names are shown on start-up) LSR A \ and bit 0 of QQ11 is 1 (the current view is type 1), BCS P%+5 \ then skip the following instruction JSR DELAY-5 \ Wait for 1 delay loop, to slow the main loop down a \ bit JSR TT17 \ Scan the keyboard for the cursor keys, returning the \ cursor's delta values in X and Y and the key pressed \ in AName: Main game loop (Part 5 of 6) [Show more] Type: Subroutine Category: Main loop Summary: Cool down lasers, make calls to update the dashboard Deep dive: Program flow of the main game loop The dashboard indicatorsContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * Main game loop (Part 2 of 6) calls via MLOOP * Main game loop (Part 3 of 6) calls via MLOOP * Main game loop (Part 4 of 6) calls via MLOOP * Main game loop (Part 6 of 6) calls via MLOOP
This is the first half of the minimal game loop, which we iterate when we are docked. This section covers the following: * Cool down lasers * Make calls to update the dashboard
Other entry points: MLOOP The entry point for the main game loop. This entry point comes after the call to the main flight loop and spawning routines, so it marks the start of the main game loop for when we are docked (as we don't need to call the main flight loop or spawning routines if we aren't in space).FRCE JSR TT102 \ Call TT102 to process the key pressed in A LDA QQ12 \ Fetch the docked flag from QQ12 into A BNE MLOOP \ If we are docked, loop back up to MLOOP just above \ to restart the main loop, but skipping all the flight \ and spawning code in the top part of the main loop JMP TT100 \ Otherwise jump to TT100 to restart the main loop from \ the startName: Main game loop (Part 6 of 6) [Show more] Type: Subroutine Category: Main loop Summary: Process non-flight key presses (FUNC keys, docked keys) Deep dive: Program flow of the main game loopContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * BAY calls via FRCE * TT219 calls via FRCE
This is the second half of the minimal game loop, which we iterate when we are docked. This section covers the following: * Process more key presses (FUNC keys, docked keys etc.) It also supports joining the main loop with a key already "pressed", so we can jump into the main game loop to perform a specific action. In practice, this is used when we enter the docking bay in BAY to display Status Mode (FUNC-9), and when we finish buying or selling cargo in BAY2 to jump to the Inventory (FUNC-0).
Other entry points: FRCE The entry point for the main game loop if we want to jump straight to a specific screen, by pretending to "press" a key, in which case A contains the internal key number of the key we want to "press".VKEYS EQUB func2 \ The key to press for showing view 1 (back) EQUB func3 \ The key to press for showing view 2 (left) EQUB func4 \ The key to press for showing view 3 (right) .TT102 CMP #func9 \ If FUNC-9 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 #func5 \ If FUNC-5 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 #func6 \ If FUNC-6 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 #func7 \ If FUNC-7 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 jump to JMP TT25 \ TT25 to show the Data on System screen, returning \ from the subroutine using a tail call .TT92 CMP #func0 \ If FUNC-0 was pressed, jump to TT213 to show the BNE P%+5 \ Inventory screen, returning from the subroutine JMP TT213 \ using a tail call CMP #func8 \ If FUNC-8 was pressed, jump to TT167 to show the BNE P%+5 \ Market Price screen, returning from the subroutine JMP TT167 \ using a tail call CMP #func1 \ If FUNC-1 was pressed, jump to TT110 to launch our BNE fvw \ ship (if docked), returning from the subroutine using JMP TT110 \ a tail call .fvw BIT QQ12 \ If bit 7 of QQ12 is clear (i.e. we are not docked, but BPL INSP \ in space), jump to INSP to skip the following checks \ for FUNC-2 to FUNC-4 and "@" (save commander file) key \ presses CMP #func4 \ If FUNC-4 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 #func2 \ If FUNC-2 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 #&48 \ If "@" was pressed, jump to SVE to save the commander BNE P%+5 \ file, returning from the subroutine using a tail call JMP SVE CMP #func3 \ If FUNC-3 was pressed, jump to TT208 to show the Sell BNE LABEL_3 \ Cargo screen, returning from the subroutine using a JMP TT208 \ tail call .INSP STX T \ Store X in T so we can retrieve it after the following LDX #3 \ We are about to loop through the key presses for the \ four views, so set a counter in X, starting with a \ value of X = 3 (for the right view) .LOOKL CMP VKEYS-1,X \ If the key pressed does not match the value in VKEYS BNE P%+5 \ for view X, skip the following instruction JMP LOOK1 \ The key pressed matches the key in position X, so jump \ to LOOK1 to switch to view X (rear, left or right), \ returning from the subroutine using a tail call DEX \ Decrement the view number in X, so we start with view \ 3 (right), then work backwards through 2 (left) and \ 1 (rear) BNE LOOKL \ Loop back to check the next key until we have checked \ for f3, f2 and f1 LDX T \ Fetch the value of X that we stored in T above .LABEL_3 CMP #&54 \ If "H" was pressed, jump to hyp to do a hyperspace BNE P%+5 \ jump (if we are in space), returning from the JMP hyp \ subroutine using a tail call 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) STA T1 \ Store A (the key that's been pressed) in T1 LDA QQ11 \ If the current view is a chart (QQ11 = 64 or 128), AND #%11000000 \ keep going, otherwise jump down to TT107 to skip the BEQ TT107 \ following LDA QQ22+1 \ If the on-screen hyperspace counter is non-zero, BNE TT107 \ then we are already counting down, so jump to TT107 \ to skip the following LDA T1 \ Restore the original value of A (the key that's been \ pressed) from T1 CMP #&36 \ If "O" was pressed, do the following three jumps, BNE ee2 \ otherwise skip to ee2 to continue 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 JSR TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10), \ which will draw the crosshairs at our current home \ system .ee2 JSR TT16 \ Call TT16 to move the crosshairs by the amount in X \ and Y, which were passed to this subroutine as \ arguments .TT107 LDA QQ22+1 \ If the on-screen hyperspace counter is zero, return BEQ t95 \ from the subroutine (as t95 contains an RTS), as we \ are not currently counting down to a hyperspace jump DEC QQ22 \ Decrement the internal hyperspace counter BNE t95 \ If the internal hyperspace counter is still non-zero, \ then we are still counting down, so return from the \ subroutine (as t95 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 t95 \ If the countdown is not yet at zero, return from the \ subroutine (as t95 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 .t95 RTS \ Return from the subroutine .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 callName: TT102 [Show more] Type: Subroutine Category: Keyboard Summary: Process function key, save key, hyperspace and chart key presses and update the hyperspace counterContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * Main game loop (Part 6 of 6) calls TT102
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 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.40 of the Electron 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: T95 Print the distance to the selected system.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 RTS \ Return from the subroutineName: BAD [Show more] Type: Subroutine Category: Status Summary: Calculate how bad we have beenContext: See this subroutine on its own page References: This subroutine is called as follows: * Main game loop (Part 3 of 6) calls BAD * TT110 calls BAD
Work out how bad we are from the amount of contraband in our hold. The formula is: (slaves + narcotics) * 2 + firearms so slaves and narcotics are twice as illegal as firearms. The value in FIST (our legal status) is set to at least this value whenever we launch from a space station, and a FIST of 50 or more gives us fugitive status, so leaving a station carrying 25 tonnes of slaves/narcotics, or 50 tonnes of firearms across multiple trips, is enough to make us a fugitive.
Returns: A A value that determines how bad we are from the amount of contraband in our hold.FAROF LDA #224 \ Set A = 224 and fall through into FAROF2 to do the \ comparisonName: FAROF [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Compare x_hi, y_hi and z_hi with 224Context: 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).FAROF2 CMP INWK+1 \ If A < x_hi, C will be clear so jump to MA34 to BCC MA34 \ 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 MA34 to BCC MA34 \ 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 .MA34 RTS \ Return from the subroutineName: FAROF2 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Compare x_hi, y_hi and z_hi with AContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions 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).MAS4 ORA INWK+1 \ OR A with x_hi, y_hi and z_hi ORA INWK+4 ORA INWK+7 RTS \ Return from the subroutineName: MAS4 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Calculate a cap on the maximum distance to a shipContext: 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.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 JSR TT66 \ Clear the top part of the screen, draw a border box, \ and set the current view type in QQ11 to 6 (death \ screen) LDX #50 \ Set the laser count to 50 to act as a counter in the STX LASCT \ D2 loop below, so this setting determines how long the \ death animation lasts (LASCT decreases by 4 for each \ iteration round the main loop, and we also decrement \ it by 1 below to give a total of 5, so this makes the \ animation last for 10 iterations of the main loop) JSR BOX \ Call BOX to redraw the same border box (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") STA MCNT \ and reset the main loop counter to 146, so all JSR ex \ timer-based calls will be stopped .D1 JSR Ze \ Call Ze to initialise INWK to a potentially hostile \ ship, and set A and X to random values LDA #32 \ Set x_lo = 32 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 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) TXA \ Set A to the random number in X and keep bits 0-3 and AND #%10001111 \ the sign in bit 7 to get a number between -15 and +15, STA INWK+29 \ and 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 PHP \ Store the processor flags LDX #OIL \ Call fq1 with X set to #OIL, which adds a new cargo JSR fq1 \ canister 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 canister's ship data block in K% PLP \ Restore the processor flags, including our random C \ flag from before LDA #0 \ Set bit 7 of A to our random C flag and store in byte ROR A \ #31 of the ship's data block, so this has a 50% chance LDY #31 \ of marking our new canister as being killed (so it STA (INF),Y \ will explode) LDA FRIN+3 \ 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 fourth slot is empty, and if it \ is we loop back to D1 to add another canister, until \ we have added four 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 DEC LASCT \ Decrement the counter in LASCT, which we set above, \ so for each loop around D2, we decrement LASCT by 5 \ (the main loop decrements it by 4, and this one makes \ it 5) BNE D2 \ Loop back to call the main flight loop again, until we \ have called it 127 times \ Fall through into DEATH2 to reset and restart the gameName: DEATH [Show more] Type: Subroutine Category: Start and end Summary: Display the death screenContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * Main flight loop (Part 9 of 16) calls DEATH * 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..DEATH2 JSR RES2 \ Reset a number of flight variables and workspaces \ and fall through into the entry code for the game \ to restart from the title screenName: DEATH2 [Show more] Type: Subroutine Category: Start and end Summary: Reset most of the game and restart from the title screenContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * DK4 calls DEATH2
This routine is called following death, and when the game is quit by pressing ESCAPE when paused..TT170 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. We need to do this \ because the loader code in elite-loader.asm pushes \ code onto the stack, and this effectively removes that \ code so we start afresh \ Fall through into BR1 to start the gameName: TT170 [Show more] Type: Subroutine Category: Start and end Summary: Main entry point for the Elite game code Deep dive: Program flow of the main game loopContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * S% (Part 1 of 2) calls TT170
This is the main entry point for the main game code..BR1 LDX #3 \ Set XC = 3 (set text cursor to column 3) STX XC JSR FX200 \ Disable the ESCAPE key and clear memory if the BREAK \ key is pressed (*FX 200,3) LDX #CYL \ Call TITLE to show a rotating Cobra Mk III (#CYL) and LDA #128 \ token 128 (" LOAD NEW COMMANDER (Y/N)?{crlf}{crlf}"), JSR TITLE \ returning with the internal number of the key pressed \ in A CMP #&44 \ Did we press "Y"? If not, jump to QU5, otherwise BNE QU5 \ continue on to load a new commander JSR GTNME \ We want to load a new commander, so we need to get \ the commander name to load JSR LOD \ We then call the LOD subroutine to load the commander \ file to address NA%+8, which is where we store the \ commander save file JSR TRNME \ Once loaded, we copy the commander name to NA% JSR TTX66 \ And we clear the top part of the screen and draw a \ border boxName: BR1 (Part 1 of 2) [Show more] Type: Subroutine Category: Start and end Summary: Show the "Load New Commander (Y/N)?" screen and start the gameContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * S% (Part 1 of 2) calls BR1
BRKV is set to point to BR1 by the loading process..QU5 \ By the time we get here, the correct commander name \ is at NA% and the correct commander data is at NA%+8. \ Specifically: \ \ * If we loaded a commander file, then the name and \ data from that file will be at NA% and NA%+8 \ \ * If this is a brand new game, then NA% will contain \ the default starting commander name ("JAMESON") \ and NA%+8 will contain the default commander data \ \ * If this is not a new game (because they died or \ quit) and we didn't want to load a commander file, \ then NA% will contain the last saved commander \ name, and NA%+8 the last saved commander data. If \ the game has never been saved, this will still be \ the default commander LDX #NT% \ The size of the commander data block is NT% bytes, \ and it starts at NA%+8, so we need to copy the data \ from the "last saved" buffer at NA%+8 to the current \ commander workspace at TP. So we set up a counter in X \ for the NT% bytes that we want to copy .QUL1 LDA NA%+7,X \ Copy the X-th byte of NA%+7 to the X-th byte of TP-1, STA TP-1,X \ (the -1 is because X is counting down from NT% to 1) DEX \ Decrement the loop counter BNE QUL1 \ Loop back for the next byte of the commander data \ block STX QQ11 \ X is 0 by the end of the above loop, so this sets QQ11 \ to 0, which means we will be showing a view without a \ boxed title at the top (i.e. we're going to use the \ screen layout of a space view in the following) \ If the commander check below fails, we keep jumping \ back to here to crash the game with an infinite loop JSR CHECK \ Call the CHECK subroutine to calculate the checksum \ for the current commander block at NA%+8 and put it \ in A CMP CHK \ Test the calculated checksum against CHK IF _REMOVE_CHECKSUMS NOP \ If we have disabled checksums, then ignore the result NOP \ of the comparison and fall through into the next part ELSE BNE P%-6 \ If the calculated checksum does not match CHK, then \ loop back to repeat the check - in other words, we \ enter an infinite loop here, as the checksum routine \ will keep returning the same incorrect value ENDIF \ The checksum CHK is correct, so now we check whether \ CHK2 = CHK EOR A9, and if this check fails, bit 7 of \ the competition flags at COK gets set, to indicate \ to Acornsoft via the competition code that there has \ been some hacking going on with this competition entry EOR #&A9 \ X = checksum EOR &A9 TAX LDA COK \ Set A to the competition flags in COK CPX CHK2 \ If X = CHK2, then skip the next instruction BEQ tZ ORA #%10000000 \ Set bit 7 of A to indicate this commander file has \ been tampered with .tZ ORA #%00001000 \ Set bit 3 of A to denote that this is the Electron \ version STA COK \ Store the updated competition flags in COKName: QU5 [Show more] Type: Subroutine Category: Start and end Summary: Reset the current commander data block to the last saved commanderContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * BR1 (Part 1 of 2) calls QU5JSR msblob \ Reset the dashboard's missile indicators so none of \ them are targeted LDA #147 \ Call TITLE to show a rotating Mamba (#3) and token LDX #3 \ 147 ("PRESS FIRE OR SPACE,COMMANDER.{crlf}{crlf}"), JSR TITLE \ returning with the internal number of the key pressed \ in A JSR ping \ Set the target system coordinates (QQ9, QQ10) to the \ current system coordinates (QQ0, QQ1) we just loaded JSR hyp1 \ Arrive in the system closest to (QQ9, QQ10) \ Fall through into the docking bay routine belowName: BR1 (Part 2 of 2) [Show more] Type: Subroutine Category: Start and end Summary: Show the "Press Fire or Space, Commander" screen and start the gameContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: No direct references to this subroutine in this source file
BRKV is set to point to BR1 by the loading process..BAY LDA #&FF \ Set QQ12 = &FF (the docked flag) to indicate that we STA QQ12 \ are docked LDA #func9 \ Jump into the main loop at FRCE, setting the key JMP FRCE \ that's "pressed" to FUNC-9 (so we show the Status \ Mode screen)Name: BAY [Show more] Type: Subroutine Category: Status Summary: Go to the docking bay (i.e. show the Status Mode screen)Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * EQSHP calls BAY * ESCAPE calls BAY * Main flight loop (Part 9 of 16) calls BAY * SVE calls BAY
We end up here after the start-up process (load commander etc.), as well as after a successful save, an escape pod launch, a successful docking, the end of a cargo sell, and various errors (such as not having enough cash, entering too many items when buying, trying to fit an item to your ship when you already have it, running out of cargo space, and so on)..TITLE PHA \ Store the token number on the stack for later STX TYPE \ Store the ship type in location TYPE JSR RESET \ Reset our ship so we can use it for the rotating \ title ship LDA #1 \ Clear the top part of the screen, draw a border box, JSR TT66 \ and set the current view type in QQ11 to 1 DEC QQ11 \ Decrement QQ11 to 0, so from here on we are using a \ space view LDA #96 \ Set nosev_z hi = 96 (96 is the value of unity in the STA INWK+14 \ rotation vector) STA INWK+7 \ Set z_hi, the high byte of the ship's z-coordinate, \ to 96, which is the distance at which the rotating \ ship starts out before coming towards us LDX #127 \ Set roll counter = 127, so don't dampen the roll and STX INWK+29 \ make the roll direction clockwise STX INWK+30 \ Set pitch counter = 127, so don't dampen the pitch and \ set the pitch direction to dive INX \ Set QQ17 to 128 (so bit 7 is set) to switch to STX QQ17 \ Sentence Case, with the next letter printing in upper \ case LDA TYPE \ Set up a new ship, using the ship type in TYPE JSR NWSHP LDY #6 \ Move the text cursor to column 6 STY XC LDA #30 \ Print recursive token 144 ("---- E L I T E ----") JSR plf \ followed by a newline LDY #6 \ Move the text cursor to column 6 again STY XC INC YC \ Move the text cursor down a row LDA PATG \ If PATG = 0, skip the following two lines, which BEQ awe \ print the author credits (PATG can be toggled by \ pausing the game and pressing "X") LDA #254 \ Print recursive token 94 ("BY D.BRABEN & I.BELL") JSR TT27 .awe JSR CLYNS \ Clear the bottom three text rows of the upper screen, \ and move the text cursor to the first cleared row. \ It also returns with Y = 0 STY DELTA \ Set DELTA = 0 (i.e. ship speed = 0) STY JSTK \ Set JSTK = 0 (i.e. keyboard, not joystick) PLA \ Restore the recursive token number we stored on the JSR ex \ stack at the start of this subroutine, and print that \ token LDA #148 \ Set A to recursive token 148 LDX #7 \ Move the text cursor to column 7 STX XC JSR ex \ Print recursive token 148 ("(C) ACORNSOFT 1984") .TLL2 LDA INWK+7 \ If z_hi (the ship's distance) is 1, jump to TL1 to CMP #1 \ skip the following decrement BEQ TL1 DEC INWK+7 \ Decrement the ship's distance, to bring the ship \ a bit closer to us .TL1 JSR MVEIT \ Move the ship in space according to the orientation \ vectors and the new value in z_hi LDA #128 \ Set z_lo = 128, so the closest the ship gets to us is STA INWK+6 \ z_hi = 1, z_lo = 128, or 256 + 128 = 384 ASL A \ Set A = 0 STA INWK \ Set x_lo = 0, so the ship remains in the screen centre STA INWK+3 \ Set y_lo = 0, so the ship remains in the screen centre JSR LL9 \ Call LL9 to display the ship DEC MCNT \ Decrement the main loop counter JSR RDKEY \ Scan the keyboard for a key press BEQ TLL2 \ If no key was pressed, loop back up to move/rotate \ the ship and check again for a key press RTS \ Return from the subroutineName: TITLE [Show more] Type: Subroutine Category: Start and end Summary: Display a title screen with a rotating ship and promptContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * BR1 (Part 1 of 2) calls TITLE * BR1 (Part 2 of 2) calls TITLE
Display the title screen, with a rotating ship and a text token at the bottom of the screen.
Arguments: A The number of the recursive token to show below the rotating ship (see variable QQ18 for details of recursive tokens) X The type of the ship to show (see variable XX21 for a list of ship types)
Returns: X If a key is being pressed, X contains the internal key number, otherwise it contains 0.CHECK LDX #NT%-2 \ Set X to the size of the commander data block, less \ 2 (to omit the checksum bytes and the save count) CLC \ Clear the C flag so we can do addition without the \ C flag affecting the result TXA \ Seed the checksum calculation by setting A to the \ size of the commander data block, less 2 \ We now loop through the commander data block, \ starting at the end and looping down to the start \ (so at the start of this loop, the X-th byte is the \ last byte of the commander data block, i.e. the save \ count) .QUL2 ADC NA%+7,X \ Add the X-1-th byte of the data block to A, plus the \ C flag EOR NA%+8,X \ EOR A with the X-th byte of the data block DEX \ Decrement the loop counter BNE QUL2 \ Loop back for the next byte in the calculation, until \ we have added byte #0 and EOR'd with byte #1 of the \ data block RTS \ Return from the subroutineName: CHECK [Show more] Type: Subroutine Category: Save and load Summary: Calculate the checksum for the last saved commander data block Deep dive: Commander save filesContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * QU5 calls CHECK * SVE calls CHECK
The checksum for the last saved commander data block is saved as part of the commander file, in two places (CHK AND CHK2), to protect against file tampering. This routine calculates the checksum and returns it in A. This algorithm is also implemented in elite-checksum.py.
Returns: A The checksum for the last saved commander data block.TRNME LDX #7 \ The commander's name can contain a maximum of 7 \ characters, and is terminated by a carriage return, \ so set up a counter in X to copy 8 characters .GTL1 LDA INWK,X \ Copy the X-th byte of INWK to the X-th byte of NA% STA NA%,X DEX \ Decrement the loop counter BPL GTL1 \ Loop back until we have copied all 8 bytes \ Fall through into TR1 to copy the name back from NA% \ to INWK. This isn't necessary as the name is already \ there, but it does save one byte, as we don't need an \ RTS hereName: TRNME [Show more] Type: Subroutine Category: Save and load Summary: Copy the last saved commander's name from INWK to NA%Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * BR1 (Part 1 of 2) calls TRNME * SVE calls TRNME.TR1 LDX #7 \ The commander's name can contain a maximum of 7 \ characters, and is terminated by a carriage return, \ so set up a counter in X to copy 8 characters .GTL2 LDA NA%,X \ Copy the X-th byte of NA% to the X-th byte of INWK STA INWK,X DEX \ Decrement the loop counter BPL GTL2 \ Loop back until we have copied all 8 bytes RTS \ Return from the subroutineName: TR1 [Show more] Type: Subroutine Category: Save and load Summary: Copy the last saved commander's name from NA% to INWKContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * GTNME calls TR1.GTNME LDA #1 \ Clear the top part of the screen, draw a border box, JSR TT66 \ and set the current view type in QQ11 to 1 LDA #123 \ Print recursive token 123 ("{crlf}COMMANDER'S NAME? ") JSR TT27 JSR DEL8 \ Call DEL8 to wait for 30 delay loops LDA #15 \ Call OSBYTE with A = 15 (flush all buffers) TAX JSR OSBYTE LDX #LO(RLINE) \ Set (Y X) to point to the RLINE parameter block LDY #HI(RLINE) \ configuration block below LDA #0 \ Set A = 0 for the following OSWORD call DEC KEYB \ Decrement KEYB, so it is now &FF, to indicate that we \ are reading from the keyboard using an OS command JSR OSWORD \ Call OSWORD with A = 0 to read a line from the current \ input stream (i.e. the keyboard) INC KEYB \ Increment KEYB back to 0 to indicate we are done \ reading the keyboard BCS TR1 \ The C flag will be set if we pressed ESCAPE when \ entering the name, in which case jump to TR1 to copy \ the last saved commander's name from NA% to INWK \ and return from the subroutine there TYA \ The OSWORD call returns the length of the commander's \ name in Y, so transfer this to A BEQ TR1 \ If A = 0, no name was entered, so jump to TR1 to copy \ the last saved commander's name from NA% to INWK \ and return from the subroutine there JMP TT67 \ We have a name, so jump to TT67 to print a newline \ and return from the subroutine using a tail callName: GTNME [Show more] Type: Subroutine Category: Save and load Summary: Fetch the name of a commander file to save or loadContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * BR1 (Part 1 of 2) calls GTNME * SVE calls GTNME
Get the commander's name for loading or saving a commander file. The name is stored in the INWK workspace and is terminated by a return character (13). If ESCAPE is pressed or a blank name is entered, then the name stored is set to the name from the last saved commander block.
Returns: INWK The commander name entered, terminated by a return character (13).RLINE EQUW INWK \ The address to store the input, so the commander's \ name will be stored in INWK as it is typed EQUB 7 \ Maximum line length = 7, as that's the maximum size \ for a commander's name EQUB '!' \ Allow ASCII characters from "!" through to "z" in EQUB 'z' \ the nameName: RLINE [Show more] Type: Variable Category: Text Summary: The OSWORD configuration block used to fetch a line of text from the keyboardContext: See this variable on its own page Variations: See code variations for this variable in the different versions References: This variable is used as follows: * GTNME uses RLINE.ZERO LDX #&B \ Point X to page &B JSR ZES1 \ Call ZES1 to zero-fill the page in X DEX \ Decrement X to point to the next page (&A) JSR ZES1 \ Call ZES1 to zero-fill the page in X DEX \ Decrement X to point to the next page \ Then fall through into ZES1 with X set to 9, so we \ clear page &9 tooName: ZERO [Show more] Type: Subroutine Category: Utility routines Summary: Zero-fill pages &9, &A, &B, &C and &DContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * LOD calls ZERO * RES2 calls ZERO * RESET calls ZERO * SVE calls ZERO
This resets the following workspaces to zero: * The ship data blocks ascending from K% at &0900 * The ship line heap descending from WP at &0BE0 * WP workspace variables from FRIN to de, which include the ship slots for the local bubble of universe, and various flight and ship status variables (only a portion of the LSO space station line heap is cleared).ZES1 LDY #0 \ If we set Y = SC = 0 and fall through into ZES2 STY SC \ below, then we will zero-fill 255 bytes starting from \ SC - in other words, we will zero-fill the whole of \ page X STX SC+1 \ We want to zero-fill page X, so store this in the \ high byte of SC, so SC(1 0) is now pointing to page XName: ZES1 [Show more] Type: Subroutine Category: Utility routines Summary: Zero-fill the page whose number is in XContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * LYN calls ZES1 * ZERO calls ZES1
Arguments: X The page we want to zero-fill.ZES2 LDA #0 \ Load A with the byte we want to fill the memory block \ with - i.e. zero .ZEL1 STA (SC),Y \ Zero the Y-th byte of the block pointed to by SC, \ so that's effectively the Y-th byte before SC INY \ Increment the loop counter BNE ZEL1 \ Loop back to zero the next byte RTS \ Return from the subroutineName: ZES2 [Show more] Type: Subroutine Category: Utility routines Summary: Zero-fill a specific pageContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * TT26 calls ZES2
Zero-fill from address SC(1 0) + Y to SC(1 0) + &FF.
Arguments: Y The offset from (X SC) where we start zeroing, counting up to &FF SC The low byte (i.e. the offset into the page) of the starting point of the zero-fill
Returns: Z flag Z flag is set.SVE JSR GTNME \ Clear the screen and ask for the commander filename \ to save, storing the name at INWK JSR TRNME \ Transfer the commander filename from INWK to NA% JSR ZERO \ Zero-fill pages &9, &A, &B, &C and &D, which clears \ the ship data blocks, the ship line heap, the ship \ slots for the local bubble of universe, and various \ flight and ship status variables LSR SVC \ Halve the save count value in SVC LDX #NT% \ We now want to copy the current commander data block \ from location TP to the last saved commander block at \ NA%+8, so set a counter in X to copy the NT% bytes in \ the commander data block \ \ We also want to copy the data block to another \ location &0B00, which is normally used for the ship \ lines heap .SVL1 LDA TP,X \ Copy the X-th byte of TP to the X-th byte of &0900 STA &0900,X \ and NA%+8 STA NA%+8,X DEX \ Decrement the loop counter BPL SVL1 \ Loop back until we have copied all the bytes in the \ commander data block JSR CHECK \ Call CHECK to calculate the checksum for the last \ saved commander and return it in A STA CHK \ Store the checksum in CHK, which is at the end of the \ last saved commander block PHA \ Store the checksum on the stack ORA #%10000000 \ Set K = checksum with bit 7 set STA K EOR COK \ Set K+2 = K EOR COK (the competition flags) STA K+2 EOR CASH+2 \ Set K+1 = K+2 EOR CASH+2 (the third cash byte) STA K+1 EOR #&5A \ Set K+3 = K+1 EOR &5A EOR TALLY+1 (the high byte of EOR TALLY+1 \ the kill tally) STA K+3 JSR BPRNT \ Print the competition number stored in K to K+3. The \ value of U might affect how this is printed, and as \ it's a temporary variable in zero page that isn't \ reset by ZERO, it might have any value, but as the \ competition code is a 10-digit number, this just means \ it may or may not have an extra space of padding JSR TT67 \ Call TT67 twice to print two newlines JSR TT67 PLA \ Restore the checksum from the stack STA &0900+NT% \ Store the checksum in the last byte of the save file \ at &0900 (the equivalent of CHK in the last saved \ block) EOR #&A9 \ Store the checksum EOR &A9 in CHK2, the penultimate STA CHK2 \ byte of the last saved commander block STA &08FF+NT% \ Store the checksum EOR &A9 in the penultimate byte of \ the save file at &0900 (the equivalent of CHK2 in the \ last saved block) LDY #&9 \ Set up an OSFILE block at &0A00, containing: STY &0A0B \ INY \ Start address for save = &00000900 in &0A0A to &0A0D STY &0A0F \ \ End address for save = &00000A00 in &0A0E to &0A11 \ \ Y is left containing &A which we use below LDA #0 \ Call QUS1 with A = 0, Y = &C to save the commander JSR QUS1 \ file with the filename we copied to INWK at the start \ of this routine JMP BAY \ Go to the docking bay (i.e. show Status Mode)Name: SVE [Show more] Type: Subroutine Category: Save and load Summary: Save the commander file Deep dive: Commander save files The competition codeContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * TT102 calls SVE.QUS1 LDX #INWK \ Store a pointer to INWK at the start of the block at STX &0A00 \ &0A00, storing #INWK in the low byte because INWK is \ in zero page LDX #&FF \ Set KEYB = &FF to indicate that we are reading from STX KEYB \ the keyboard using an OS command INX \ Set X = 0 JSR OSFILE \ Call OSFILE to do the file operation specified in \ &0A00 (i.e. save or load a file depending on the value \ of A) INC KEYB \ Increment KEYB back to 0 to indicate we are done \ reading the keyboard RTS \ Return from the subroutineName: QUS1 [Show more] Type: Subroutine Category: Save and load Summary: Save or load the commander file Deep dive: Commander save filesContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * LOD calls QUS1 * SVE calls QUS1
The filename should be stored at INWK, terminated with a carriage return (13). The routine should be called with Y set to &C.
Arguments: A File operation to be performed. Can be one of the following: * 0 (save file) * &FF (load file) Y Points to the page number containing the OSFILE block, which must be &C because that's where the pointer to the filename in INWK is stored below (by the STX &0C00 instruction).LOD LDX #2 \ Enable the ESCAPE key and clear memory if the BREAK JSR FX200 \ key is pressed (*FX 200,2) JSR ZERO \ Zero-fill pages &9, &A, &B, &C and &D, which clears \ the ship data blocks, the ship line heap, the ship \ slots for the local bubble of universe, and various \ flight and ship status variables LDY #&9 \ Set up an OSFILE block at &0A00, containing: STY &0A03 \ INC &0A0B \ Load address = &00000900 in &0A02 to &0A05 \ \ Length of file = &00000100 in &0A0A to &0A0D INY \ Increment Y to &A, which we use next LDA #&FF \ Call QUS1 with A = &FF, Y = &C to load the commander JSR QUS1 \ file to address &0B00 LDA &0900 \ If the first byte of the loaded file has bit 7 set, BMI SPS1+1 \ jump to SPS+1, which is the second byte of an LDA #0 \ instruction, i.e. a BRK instruction, which will force \ an interrupt to call the address in BRKV, which is set \ to BR1... so this instruction restarts the game from \ the title screen. Valid commander files for the \ cassette version of Elite only have 0 for the first \ byte, as there are no missions in this version, so \ having bit 7 set is invalid anyway LDX #NT% \ We have successfully loaded the commander file at \ &0B00, so now we want to copy it to the last saved \ commander data block at NA%+8, so we set up a counter \ in X to copy NT% bytes .LOL1 LDA &0900,X \ Copy the X-th byte of &0900 to the X-th byte of NA%+8 STA NA%+8,X DEX \ Decrement the loop counter BPL LOL1 \ Loop back until we have copied all NT% bytes LDX #3 \ Fall through into FX200 to disable the ESCAPE key and \ clear memory if the BREAK key is pressed (*FX 200,3) \ and return from the subroutine thereName: LOD [Show more] Type: Subroutine Category: Save and load Summary: Load a commander fileContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * BR1 (Part 1 of 2) calls LOD
The filename should be stored at INWK, terminated with a carriage return (13)..FX200 LDY #0 \ Call OSBYTE 200 with Y = 0, so the new value is set to LDA #200 \ X, and return from the subroutine using a tail call JMP OSBYTE RTS \ This instruction has no effect, as we already returned \ from the subroutineName: FX200 [Show more] Type: Subroutine Category: Utility routines Summary: Set the behaviour of the ESCAPE and BREAK keysContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * BR1 (Part 1 of 2) calls FX200 * LOD calls FX200
This is the equivalent of a *FX 200 command, which controls the behaviour of the ESCAPE and BREAK keys.
Arguments: X Controls the behaviour as follows: * 0 = Enable ESCAPE key Normal BREAK key action * 1 = Disable ESCAPE key Normal BREAK key action * 2 = Enable ESCAPE key Clear memory if the BREAK key is pressed * 3 = Disable ESCAPE key Clear memory if the BREAK key is pressed.SPS1 LDX #0 \ Copy the two high bytes of the planet's x-coordinate JSR SPS3 \ into K3(2 1 0), separating out the sign bit into K3+2 LDX #3 \ Copy the two high bytes of the planet's y-coordinate JSR SPS3 \ into K3(5 4 3), separating out the sign bit into K3+5 LDX #6 \ Copy the two high bytes of the planet's z-coordinate JSR SPS3 \ into K3(8 7 6), separating out the sign bit into K3+8 \ Fall through into TAS2 to build XX15 from K3Name: SPS1 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Calculate the vector to the planet and store it in XX15Context: See this subroutine on its own page References: This subroutine is called as follows: * COMPAS calls SPS1 * TACTICS (Part 2 of 7) calls SPS1 * LOD calls via SPS1+1
Other entry points: SPS1+1 A BRK instruction.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 \ Now we have a signed 8-bit version of the vector K3 in \ XX15, so fall through into NORM to normalise itName: TAS2 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Normalise the three-coordinate vector in K3Context: See this subroutine on its own page References: This subroutine is called as follows: * SPS4 calls TAS2 * TACTICS (Part 3 of 7) calls TAS2
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.NORM LDA XX15 \ Fetch the x-coordinate into A JSR SQUA \ Set (A P) = A * A = x^2 STA R \ Set (R Q) = (A P) = x^2 LDA P STA Q LDA XX15+1 \ Fetch the y-coordinate into A JSR SQUA \ Set (A P) = A * A = y^2 STA T \ Set (T P) = (A P) = y^2 LDA P \ Set (R Q) = (R Q) + (T P) = x^2 + y^2 ADC Q \ STA Q \ First, doing the low bytes, Q = Q + P LDA T \ And then the high bytes, R = R + T ADC R STA R LDA XX15+2 \ Fetch the z-coordinate into A JSR SQUA \ Set (A P) = A * A = z^2 STA T \ Set (T P) = (A P) = z^2 LDA P \ Set (R Q) = (R Q) + (T P) = x^2 + y^2 + z^2 ADC Q \ STA Q \ First, doing the low bytes, Q = Q + P LDA T \ And then the high bytes, R = R + T ADC R STA R JSR LL5 \ We now have the following: \ \ (R Q) = x^2 + y^2 + z^2 \ \ so we can call LL5 to use Pythagoras to get: \ \ Q = SQRT(R Q) \ = SQRT(x^2 + y^2 + z^2) \ \ So Q now contains the length of the vector (x, y, z), \ and we can normalise the vector by dividing each of \ the coordinates by this value, which we do by calling \ routine TIS2. TIS2 returns the divided figure, using \ 96 to represent 1 and 96 with bit 7 set for -1 LDA XX15 \ Call TIS2 to divide the x-coordinate in XX15 by Q, JSR TIS2 \ with 1 being represented by 96 STA XX15 LDA XX15+1 \ Call TIS2 to divide the y-coordinate in XX15+1 by Q, JSR TIS2 \ with 1 being represented by 96 STA XX15+1 LDA XX15+2 \ Call TIS2 to divide the z-coordinate in XX15+2 by Q, JSR TIS2 \ with 1 being represented by 96 STA XX15+2 .NO1 RTS \ Return from the subroutineName: NORM [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Normalise the three-coordinate vector in XX15 Deep dive: Tidying orthonormal vectors Orientation vectorsContext: See this subroutine on its own page References: This subroutine is called as follows: * TIDY calls NORM
We do this by dividing each of the three coordinates by the length of the vector, which we can calculate using Pythagoras. Once normalised, 96 (&60) is used to represent a value of 1, and 96 with bit 7 set (&E0) is used to represent -1. This enables us to represent fractional values of less than 1 using integers.
Arguments: XX15 The vector to normalise, with: * The x-coordinate in XX15 * The y-coordinate in XX15+1 * The z-coordinate in XX15+2
Returns: XX15 The normalised vector Q The length of the original XX15 vector
Other entry points: NO1 Contains an RTS.RDKEY LDX #16 \ Start the scan with internal key number 16 ("Q") .Rd1 JSR DKS4 \ Scan the keyboard to see if the key in X is currently \ being pressed, returning the result in A and X BMI Rd2 \ Jump to Rd2 if this key is being pressed (in which \ case DKS4 will have returned the key number with bit \ 7 set, which is negative) INX \ Increment the key number, which was unchanged by the \ above call to DKS4 BPL Rd1 \ Loop back to test the next key, ending the loop when \ X is negative (i.e. 128) TXA \ If we get here, nothing is being pressed, so copy X \ into A so that X = A = 128 = %10000000 .Rd2 EOR #%10000000 \ EOR A with #%10000000 to flip bit 7, so A now contains \ 0 if no key has been pressed, or the internal key \ number if a key has been pressed TAY \ Store A in Y so we can preserve it through the call to \ CAPSL below JSR CAPSL \ Call CAPSL to check whether CAPS LOCK is being pressed \ (if it is, the return value in A is the key number of \ CAPS LOCK, but with bit 7 set) PHP \ Retrieve the value of A we stored in Y, but making TYA \ sure the retrieval doesn't affect the flags PLP BPL P%+4 \ If the result of the call to CAPSL was positive, then \ CAPS LOCK isn't being pressed, so skip the next \ instruction ORA #%10000000 \ CAPS LOCK is being pressed, so set bit 7 of A TAX \ Copy A into X to return the key number of CAPS LOCK \ with bit 7 set RTS \ Return from the subroutineName: RDKEY [Show more] Type: Subroutine Category: Keyboard Summary: Scan the keyboard for key pressesContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * DK4 calls RDKEY * TITLE calls RDKEY
Scan the keyboard, starting with internal key number 16 ("Q") and working through the set of internal key numbers (see p.40 of the Electron Advanced User Guide for a list of internal key numbers). This routine is effectively the same as OSBYTE 122, though the OSBYTE call preserves A, unlike this routine.
Returns: X If a key is being pressed, X contains the internal key number, otherwise it contains 0 A Contains the same as X.ECMOF LDA #0 \ Set ECMA and ECMP to 0 to indicate that no E.C.M. is STA ECMA \ currently running STA ECMP JSR ECBLB \ Update the E.C.M. indicator bulb on the dashboard LDA #72 \ Call the NOISE routine with A = 72 to make the sound BNE NOISE \ of the E.C.M. being turned off and return from the \ subroutine using a tail call (this BNE is effectively \ a JMP as A will never be zero)Name: ECMOF [Show more] Type: Subroutine Category: Sound Summary: Switch off the E.C.M.Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * Main flight loop (Part 16 of 16) calls ECMOF * RES2 calls ECMOF * NO3 calls via ECMOF-1 * SFRMIS calls via ECMOF-1
Switch the E.C.M. off, turn off the dashboard bulb and make the sound of the E.C.M. switching off).
Other entry points: ECMOF-1 Contains an RTS.EXNO3 LDA #24 \ Call the NOISE routine with A = 24 to make the BNE NOISE \ death sound and return from the subroutine using a \ tail call (this BNE is effectively a JMP as A will \ never be zero)Name: EXNO3 [Show more] Type: Subroutine Category: Sound Summary: Make an explosion soundContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions 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..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 ECMOF-1 \ The C flag will be set if the call to SFS1-2 was a \ success, so if it's clear, jump to ECMOF-1 to return \ from the subroutine (as ECMOF-1 contains an RTS) LDA #120 \ Print recursive token 120 ("INCOMING MISSILE") as an JSR MESS \ in-flight message LDA #48 \ Call the NOISE routine with A = 48 to make the sound BNE NOISE \ of the missile being launched and return from the \ subroutine using a tail call (this BNE is effectively \ a JMP as A will never be zero)Name: SFRMIS [Show more] Type: Subroutine Category: Tactics Summary: Add an enemy missile to our local bubble of universeContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * TACTICS (Part 5 of 7) calls SFRMIS
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..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 explodingName: EXNO2 [Show more] Type: Subroutine Category: Status Summary: Process us making a kill Deep dive: Combat rankContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * Main flight loop (Part 5 of 16) calls EXNO2 * 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..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 LDA #16 \ Set A = 16 to denote we have made a hit or kill \ (part 2 of the explosion), and fall through into NOISE \ to make the sound EQUB &2C \ Skip the next instruction by turning it into \ &2C &A9 &20, or BIT &20A9, which does nothing apart \ from affect the flagsName: EXNO [Show more] Type: Subroutine Category: Sound Summary: Make the sound of a laser strike or ship explosionContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * Main flight loop (Part 11 of 16) calls EXNO * EXNO2 calls via 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.BEEP LDA #32 \ Set A = 32 to denote a short, high beep, and fall \ through into the NOISE routine to make the soundName: BEEP [Show more] Type: Subroutine Category: Sound Summary: Make a short, high beepContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * dn2 calls BEEP * Main flight loop (Part 11 of 16) calls BEEP * TT26 calls BEEP.NOISE JSR NOS1 \ Set up the sound block in XX16 for the sound in A and \ fall through into NO3 to make the soundName: NOISE [Show more] Type: Subroutine Category: Sound Summary: Make the sound whose number is in AContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * ECBLB2 calls NOISE * ECMOF calls NOISE * EXNO3 calls NOISE * FRMIS calls NOISE * LAUN calls NOISE * LL164 calls NOISE * Main flight loop (Part 3 of 16) calls NOISE * SFRMIS calls NOISE * TACTICS (Part 6 of 7) calls NOISE * WARP calls NOISE
Arguments: A The number of the sound to be made. See the documentation for variable SFX for a list of sound numbers.NO3 LDX DNOIZ \ Set X to the DNOIZ configuration setting BNE ECMOF-1 \ If DNOIZ is non-zero, then sound is disabled, so \ return from the subroutine (as ECMOF-1 contains an \ RTS) LDA XX16 \ Set X = to bit 0 of the first SOUND parameter, so AND #&01 \ that's the channel number (as the channel is either TAX \ 0 or 1) LDY XX16+8 \ We stored the sound number (0, 8, 16 etc.) in XX16+8 \ back in NOS1, so fetch it into Y LDA SFX2,Y \ Fetch this sound's byte from SFX2 into A \ \ (This seems wrong. Y is a multiple of 8 (0, 8 ... 72) \ rather than the actual sound number (0-9), and there \ are only 10 bytes at SFX2, so this doesn't feel \ correct - surely Y should be divided by 8 before \ fetching the relevant SFX2 byte?) CMP SFXPR,X \ If the new sound's SFX2 byte is less than the current BCC ECMOF-1 \ channel's SFXPR value, return from the subroutine as \ the current sound has a higher priority than the new \ one (as ECMOF-1 contains an RTS) STA SFXPR,X \ Otherwise the new sound is a higher priority sound, so \ store the new sound's SFX2 byte as the channel's new \ SFXPR priority AND #%00001111 \ And store the low nibble of the SFX2 byte in the STA SFXDU,X \ channel's new SFXDU duration LDX #LO(XX16) \ Otherwise set (Y X) to point to the sound block in LDY #HI(XX16) \ XX16 LDA #7 \ Call OSWORD 7 to makes the sound, as described in the JMP OSWORD \ documentation for variable SFX, and return from the \ subroutine using a tail callName: NO3 [Show more] Type: Subroutine Category: Sound Summary: Make a sound from a prepared sound blockContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * EXNO calls NO3
Make a sound from a prepared sound block in XX16 (if sound is enabled). See routine NOS1 for details of preparing the XX16 sound block..NOS1 STA XX16+8 \ Store the sound number in XX16+8, so we can retrieve \ it in the NO3 routine LSR A \ Divide A by 2, and also clear the C flag, as bit 0 of \ A is always zero (as A is a multiple of 8) ADC #3 \ Set Y = A + 3, so Y now points to the last byte of TAY \ four within the block of four-byte values LDX #7 \ We want to copy four bytes, spread out into an 8-byte \ block, so set a counter in Y to cover 8 bytes .NOL1 LDA #0 \ Set the X-th byte of XX16 to 0 STA XX16,X DEX \ Decrement the destination byte pointer LDA SFX,Y \ Set the X-th byte of XX16 to the value from SFX+Y STA XX16,X DEY \ Decrement the source byte pointer again DEX \ Decrement the destination byte pointer again BPL NOL1 \ Loop back for the next source byte \ Fall through into KYTB to return from the subroutine, \ as the first byte of KYTB is an RTSName: NOS1 [Show more] Type: Subroutine Category: Sound Summary: Prepare a sound blockContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * EXNO calls NOS1 * NOISE calls NOS1
Copy four sound bytes from SFX into XX16, interspersing them with null bytes, with Y indicating the sound number to copy (from the values in the sound table at SFX). So, for example, if we call this routine with A = 40 (long, low beep), the following bytes will be set in XX16 to XX16+7: &13 &00 &F4 &00 &0C &00 &08 &00 This block will be passed to OSWORD 7 to make the sound, which expects the four sound attributes as 16-bit big-endian values - in other words, with the low byte first. So the above block would pass the values &0013, &00F4, &000C and &0008 to the SOUND statement when used with OSWORD 7, or: SOUND &13, &F4, &0C, &08 as the high bytes are always zero.
Arguments: A The sound number to copy from SFX to XX16, which is always a multiple of 8.KYTB RTS \ Return from the subroutine (used as an entry point and \ a fall-through from above) \ These are the primary flight controls (pitch, roll, \ speed and lasers): EQUB &68 + 128 \ ? KYTB+1 Slow down EQUB &62 + 128 \ Space KYTB+2 Speed up EQUB &66 + 128 \ < KYTB+3 Roll left EQUB &67 + 128 \ > KYTB+4 Roll right EQUB &42 + 128 \ X KYTB+5 Pull up EQUB &51 + 128 \ S KYTB+6 Pitch down EQUB &41 + 128 \ A KYTB+7 Fire lasers \ These are the secondary flight controls: EQUB &17 \ - KYTB+8 Energy bomb EQUB &70 \ ESCAPE KYTB+9 Launch escape pod EQUB &23 \ T KYTB+10 Arm missile EQUB &35 \ U KYTB+11 Unarm missile EQUB &65 \ M KYTB+12 Fire missile EQUB &22 \ E KYTB+13 E.C.M. EQUB &45 \ J KYTB+14 In-system jump EQUB &52 \ C KYTB+15 Docking computerName: KYTB [Show more] Type: Variable Category: Keyboard Summary: Lookup table for in-flight keyboard controls Deep dive: The key loggerContext: See this variable on its own page Variations: See code variations for this variable in the different versions References: This variable is used as follows: * DK4 uses KYTB * DOKEY uses KYTB
Keyboard table for in-flight controls. This table contains the internal key codes for the flight keys (see p.40 of the Electron Advanced User Guide for a list of internal key numbers). The pitch, roll, speed and laser keys (i.e. the seven primary flight control keys) have bit 7 set, so they have 128 added to their internal values. This doesn't appear to be used anywhere. Note that KYTB actually points to the byte before the start of the table, so the offset of the first key value is 1 (i.e. KYTB+1), not 0.
Other entry points: KYTB Contains an RTS.KSCAN \ This routine is called from below, and performs the \ actual keyboard scan SEC \ Set the C flag and clear the V flag, so when we call CLV \ KEYV, it scans the keyboard just like OSBYTE 121 SEI \ Disable interrupts JMP (S%+4) \ Jump to the original value of KEYV, which is stored in \ S%+4. Because we set the C and V flags as above, this \ will scan the keyboard like OSBYTE 121, which expects \ X to be set to the internal key number to scan for, \ EOR'd with %10000000. Unlike OSBYTE 121, a direct call \ to KEYV will return negative value in both A and X if \ that key is being pressed .CAPSL LDX #&40 \ Set X to the internal key number for CAPS LOCK, and \ fall through into DKS4 to check whether it is being \ pressed .DKS4 TYA \ Store Y on the stack so we can retrieve it when we PHA \ return from the subroutine, thus preserving Y TXA \ Store the key number to check in X on the stack so PHA \ we can retrieve it below ORA #%10000000 \ Set bit 7 of the key to check for and transfer the TAX \ value to X JSR KSCAN \ Call KSCAN to check whether the key in X is being \ pressed, which returns a negative value in A and X \ if it is CLI \ Enable interrupts again (as they are disabled in \ KSCAN) TAX \ Set X to the result of the key press call above PLA \ Fetch the original argument value of X from the stack AND #%01111111 \ into A, and clear bit 7 CPX #%10000000 \ If bit 7 of the result of the key press check above is BCC P%+4 \ set, then the key in X is being pressed, so skip the \ next instruction ORA #%10000000 \ The key in X isn't being pressed, so set bit 7 of A TAX \ By this point, A contains the key number we wanted to \ check for, with bit 7 set if the key is being pressed \ and clear otherwise, which is what we want to return \ from the subroutine, but first we need to restore the \ value of Y from the stack, so we store the result A in \ X while we do that PLA \ Restore the value Y that we stored on the stack, so it TAY \ gets preserved across calls to the subroutine TXA \ And we now retrieve the result that we stored in X \ back into A, so we can return it RTS \ Return from the subroutineName: DKS4 [Show more] Type: Subroutine Category: Keyboard Summary: Scan the keyboard to see if a specific key is being pressed Deep dive: The key loggerContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * DOKEY calls DKS4 * hyp calls DKS4 * RDKEY calls DKS4 * hyp calls via CAPSL * RDKEY calls via CAPSL
Arguments: X The internal number of the key to check (see p.40 of the Electron Advanced User Guide for a list of internal key numbers)
Returns: A If the key in A is being pressed, A contains the original argument A, but with bit 7 set (i.e. A + 128). If the key in A is not being pressed, the value in A is unchanged X Contains the same as A
Other entry points: CAPSL Scan the keyboard to see if CAPS LOCK is being pressed.DKS2 LDA #128 \ Call OSBYTE with A = 128 to fetch the 16-bit value JSR OSBYTE \ from ADC channel X, returning (Y X), i.e. the high \ byte in Y and the low byte in X \ \ * Channel 1 is the x-axis: 0 = right, 65520 = left \ \ * Channel 2 is the y-axis: 0 = down, 65520 = up TYA \ Copy Y to A, so the result is now in (A X) EOR JSTE \ The high byte A is now EOR'd with the value in \ location JSTE, which contains &FF if both joystick \ channels are reversed and 0 otherwise (so A now \ contains the high byte but inverted, if that's what \ the current settings say) RTS \ Return from the subroutineName: DKS2 [Show more] Type: Subroutine Category: Keyboard Summary: Read the joystick positionContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: No direct references to this subroutine in this source file
This routine is never called in the Electron version, as the Electron doesn't have ADC channels as standard and doesn't support joysticks (though a lot of the joystick code from the other versions is still present, it just isn't called). Return the value of ADC channel in X (used to read the joystick). The value will be inverted if the game has been configured to reverse both joystick channels (which can be done by pausing the game and pressing J).
Arguments: X The ADC channel to read: * 1 = joystick X * 2 = joystick Y
Returns: (A X) The 16-bit value read from channel X, with the value inverted if the game has been configured to reverse the joystick
Other entry points: DKS2-1 Contains an RTS.DKS3 STY T \ Store the configuration key argument in T CPX T \ If X <> Y, jump to Dk3 to return from the subroutine BNE Dk3 \ We have a match between X and Y, so now to toggle \ the relevant configuration byte. CAPS LOCK has a key \ value of &40 and has its configuration byte at \ location DAMP, A has a value of &41 and has its byte \ at location DJD, which is DAMP+1, and so on. So we \ can toggle the configuration byte by changing the \ byte at DAMP + (X - &40), or to put it in indexing \ terms, DAMP-&40,X. It's no coincidence that the \ game's configuration bytes are set up in this order \ and with these keys (and this is also why the sound \ on/off keys are dealt with elsewhere, as the internal \ key for S and Q are &51 and &10, which don't fit \ nicely into this approach) LDA DAMP-&40,X \ Fetch the byte from DAMP + (X - &40), invert it and EOR #&FF \ put it back (0 means no and &FF means yes in the STA DAMP-&40,X \ configuration bytes, so this toggles the setting) JSR BELL \ Make a beep sound so we know something has happened JSR DELAY \ Wait for Y delay loops (Y is between 64 and 70) LDY T \ Restore the configuration key argument into Y .Dk3 RTS \ Return from the subroutineName: DKS3 [Show more] Type: Subroutine Category: Keyboard Summary: Toggle a configuration setting and emit a beepContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * DK4 calls DKS3
This is called when the game is paused and a key is pressed that changes the game's configuration. Specifically, this routine toggles the configuration settings for the following keys: * CAPS LOCK toggles keyboard flight damping (&40) * A toggles keyboard auto-recentre (&41) * X toggles author names on start-up screen (&42) * F toggles flashing console bars (&43) * Y toggles reverse joystick Y channel (&44) * J toggles reverse both joystick channels (&45) * K toggles keyboard and joystick (&46) The numbers in brackets are the internal key numbers (see p.40 of the Electron Advanced User Guide for a list of internal key numbers). We pass the key that has been pressed in X, and the configuration option to check it against in Y, so this routine is typically called in a loop that loops through the various configuration options. Note that the Electron version doesn't support joysticks, but you can still configure them (though this does break the chart views, as they still call the joystick routines that are still present in the Electron's codebase).
Arguments: X The internal number of the key that's been pressed Y The internal number of the configuration key to check against, from the list above (i.e. Y must be from &40 to &46).U% LDA #0 \ Set A to 0, as this means "key not pressed" in the \ key logger at KL LDY #15 \ We want to clear the 15 key logger locations from \ KY1 to KY19, 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 subroutineName: U% [Show more] Type: Subroutine Category: Keyboard Summary: Clear the key loggerContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * DEATH calls U% * DOKEY calls U%
Returns: A A is set to 0 Y Y is set to 0.DOKEY JSR U% \ Call U% to clear the key logger 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 LDX KYTB,Y \ Call DKS4 to see if the KYTB key at offset Y is being JSR DKS4 \ pressed BPL P%+6 \ If the key isn't being pressed, skip the following two \ instructions LDX #&FF \ Set the key logger for this key to indicate it's being STX KL,Y \ pressed 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 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 to scan for other keysName: DOKEY [Show more] Type: Subroutine Category: Keyboard Summary: Scan for the seven primary flight controls Deep dive: The key logger The docking computerContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * TT17 calls DOKEY
Scan for the seven primary flight controls, pause and configuration keys, and secondary flight controls, and update the key logger and pitch and roll rates accordingly. Unlike the other versions of Elite, the Electron version doesn't actually read the joystick values from the ADC channels, so although you can configure joysticks using the "K" option when paused, they won't have any effect. All the other joystick code is present, though, so perhaps the intention was to support joysticks at some point?.DK4 JSR RDKEY \ Scan the keyboard for a key press and return the \ internal key number in A and X (or 0 for no key press) STX KL \ Store X in KL, byte #0 of the key logger CPX #&38 \ If COPY is not being pressed, jump to DK2 below, BNE DK2 \ otherwise let's process the configuration keys .FREEZE \ 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 DEL8 \ Call DEL8 to wait for 30 delay loops JSR RDKEY \ Scan the keyboard for a key press and return the \ internal key number in A and X (or 0 for no key press) CPX #&51 \ If "S" is not being pressed, skip to DK6 BNE DK6 LDA #0 \ "S" is being pressed, so set DNOIZ to 0 to turn the STA DNOIZ \ sound on .DK6 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 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 #&47 \ The last toggle key is &46 (K), so check whether we \ have just done that one BNE DKL4 \ If not, loop back to check for the next toggle key .DK55 CPX #&10 \ If "Q" is not being pressed, skip to DK7 BNE DK7 STX DNOIZ \ "Q" is being pressed, so set DNOIZ to X, which is \ non-zero (&10), so this will turn the sound off .DK7 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 #&59 \ If DELETE is not being pressed, we are still paused, BNE FREEZE \ 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 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 #15 \ 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+15, and their key logger locations are from KL+8 \ to KL+15. So set a decreasing counter in Y for the \ index, starting at 15, so we can loop through them LDA #&FF \ Set A to &FF so we can store this in the keyboard \ logger for keys that are being pressed .DKL1 LDX KYTB,Y \ Get the internal key number of the Y-th flight key \ the KYTB keyboard table CPX KL \ We stored the key that's being pressed in KL above, \ so check to see if the Y-th flight key is being \ pressed BNE DK1 \ If it is not being pressed, skip to DK1 below STA KL,Y \ The Y-th flight key is being pressed, so set that \ key's location in the key logger to &FF .DK1 DEY \ Decrement the loop counter CPY #7 \ Have we just done the last key? BNE DKL1 \ If not, loop back to process the next key .DK5 RTS \ Return from the subroutineName: DK4 [Show more] Type: Subroutine Category: Keyboard Summary: Scan for pause, configuration and secondary flight keys Deep dive: The key loggerContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: No direct references to this subroutine in this source file
Scan for pause and configuration keys, and if this is a space view, also scan for secondary flight controls. Specifically: * Scan for the pause button (COPY) and if it's pressed, pause the game and process any configuration key presses until the game is unpaused (DELETE) * If this is a space view, scan for secondary flight keys and update the relevant bytes in the key logger.TT217 STY YSAV \ Store Y in temporary storage, so we can restore it \ later .t DEC KEYB \ Decrement KEYB, so it is now &FF, to indicate that we \ are reading from the keyboard using an OS command JSR OSRDCH \ Call OSRDCH to read a character from the keyboard INC KEYB \ Increment KEYB back to 0 to indicate we are done \ reading the keyboard TAX \ Copy A into X .out RTS \ Return from the subroutineName: TT217 [Show more] Type: Subroutine Category: Keyboard Summary: Scan the keyboard until a key is pressedContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * gnum calls TT217 * qv calls TT217 * TT214 calls TT217 * mes9 calls via out * OUCH calls via out
Scan the keyboard until a key is pressed, and return the key's ASCII code. If, on entry, a key is already being held down, then wait until that key is released first (so this routine detects the first key down event following the subroutine call).
Returns: X The ASCII code of the key that was pressed A Contains the same as X Y Y is preserved
Other entry points: out Contains an RTS.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 flagsName: me1 [Show more] Type: Subroutine Category: Flight Summary: Erase an old in-flight message and display a new oneContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * MESS calls me1
Arguments: A The text token to be printed X Must be set to 0.ou2 LDA #108 \ Set A to recursive token 108 ("E.C.M.SYSTEM") EQUB &2C \ Fall through into ou3 to print the new message, but \ skip the first instruction by turning it into \ &2C &A9 &6F, or BIT &6FA9, which does nothing apart \ from affect the flagsName: ou2 [Show more] Type: Subroutine Category: Flight Summary: Display "E.C.M.SYSTEM DESTROYED" as an in-flight messageContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * OUCH calls ou2.ou3 LDA #111 \ Set A to recursive token 111 ("FUEL SCOOPS")Name: ou3 [Show more] Type: Subroutine Category: Flight Summary: Display "FUEL SCOOPS DESTROYED" as an in-flight messageContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * OUCH calls ou3.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 AName: MESS [Show more] Type: Subroutine Category: Flight Summary: Display an in-flight messageContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * EXNO2 calls MESS * FR1 calls MESS * Ghy calls MESS * KILLSHP calls MESS * Main flight loop (Part 8 of 16) calls MESS * Main flight loop (Part 12 of 16) calls MESS * Main flight loop (Part 15 of 16) calls MESS * me2 calls MESS * OUCH calls MESS * SFRMIS 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.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 out \ subroutine (as out contains an RTS) LDA #253 \ Print recursive token 93 (" DESTROYED") and return JMP TT27 \ from the subroutine using a tail callName: mes9 [Show more] Type: Subroutine Category: Flight Summary: Print a text token, possibly followed by " DESTROYED"Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions 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)..OUCH JSR DORND \ Set A and X to random numbers BMI out \ If A < 0 (50% chance), return from the subroutine \ (as out contains an RTS) CPX #22 \ If X >= 22 (91% chance), return from the subroutine BCS out \ (as out contains an RTS) LDA QQ20,X \ If we do not have any of item QQ20+X, return from the BEQ out \ subroutine (as out contains an RTS). X is in the range \ 0-21, so this not only checks for cargo, but also for \ E.C.M., fuel scoops, energy bomb, energy unit and \ docking computer, all of which can be destroyed LDA DLY \ If there is already an in-flight message on-screen, BNE out \ return from the subroutine (as out 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 QQ20,X \ A is 0 (as we didn't branch with the BNE above), so \ this sets QQ20+X to 0, which destroys any cargo or \ equipment we have of that type CPX #17 \ If X >= 17 then we just lost a piece of equipment, so BCS ou1 \ jump to ou1 to print the relevant message TXA \ Print recursive token 48 + A as an in-flight token, ADC #208 \ which will be in the range 48 ("FOOD") to 64 ("ALIEN BNE MESS \ ITEMS") as the C flag is clear, so this prints the \ destroyed item's name, followed by " DESTROYED" (as we \ set bit 1 of the de flag above), and returns from the \ subroutine using a tail call .ou1 BEQ ou2 \ If X = 17, jump to ou2 to print "E.C.M.SYSTEM \ DESTROYED" and return from the subroutine using a tail \ call CPX #18 \ If X = 18, jump to ou3 to print "FUEL SCOOPS BEQ ou3 \ DESTROYED" and return from the subroutine using a tail \ call TXA \ Otherwise X is in the range 19 to 21 and the C flag is ADC #113-20 \ set (as we got here via a BCS to ou1), so we set A as \ follows: \ \ A = 113 - 20 + X + C \ = 113 - 19 + X \ = 113 to 115 BNE MESS \ Print recursive token A ("ENERGY BOMB", "ENERGY UNIT" \ or "DOCKING COMPUTERS") as an in-flight message, \ followed by " DESTROYED", and return from the \ subroutine using a tail callName: OUCH [Show more] Type: Subroutine Category: Flight Summary: Potentially lose cargo or equipment following damageContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions 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..QQ16 EQUS "AL" \ Token 128 EQUS "LE" \ Token 129 EQUS "XE" \ Token 130 EQUS "GE" \ Token 131 EQUS "ZA" \ Token 132 EQUS "CE" \ Token 133 EQUS "BI" \ Token 134 EQUS "SO" \ Token 135 EQUS "US" \ Token 136 EQUS "ES" \ Token 137 EQUS "AR" \ Token 138 EQUS "MA" \ Token 139 EQUS "IN" \ Token 140 EQUS "DI" \ Token 141 EQUS "RE" \ Token 142 EQUS "A?" \ Token 143 EQUS "ER" \ Token 144 EQUS "AT" \ Token 145 EQUS "EN" \ Token 146 EQUS "BE" \ Token 147 EQUS "RA" \ Token 148 EQUS "LA" \ Token 149 EQUS "VE" \ Token 150 EQUS "TI" \ Token 151 EQUS "ED" \ Token 152 EQUS "OR" \ Token 153 EQUS "QU" \ Token 154 EQUS "AN" \ Token 155 EQUS "TE" \ Token 156 EQUS "IS" \ Token 157 EQUS "RI" \ Token 158 EQUS "ON" \ Token 159Name: QQ16 [Show more] Type: Variable Category: Text Summary: The two-letter token lookup table Deep dive: Printing text tokensContext: See this variable on its own page Variations: See code variations for this variable in the different versions References: This variable is used as follows: * TT43 uses QQ16
Two-letter token lookup table for tokens 128-159. See the deep dive on "Printing text tokens" for details of how the two-letter token system works.MACRO ITEM price, factor, units, quantity, mask IF factor < 0 s = 1 << 7 ELSE s = 0 ENDIF IF units = 't' u = 0 ELIF units = 'k' u = 1 << 5 ELSE u = 1 << 6 ENDIF e = ABS(factor) EQUB price EQUB s + u + e EQUB quantity EQUB mask ENDMACROName: ITEM [Show more] Type: Macro Category: Market Summary: Macro definition for the market prices table Deep dive: Market item prices and availability
The following macro is used to build the market prices table: ITEM price, factor, units, quantity, mask It inserts an item into the market prices table at QQ23. See the deep dive on "Market item prices and availability" for more information on how the market system works.
Arguments: price Base price factor Economic factor units Units: "t", "g" or "k" quantity Base quantity mask Fluctuations mask.QQ23 ITEM 19, -2, 't', 6, %00000001 \ 0 = Food ITEM 20, -1, 't', 10, %00000011 \ 1 = Textiles ITEM 65, -3, 't', 2, %00000111 \ 2 = Radioactives ITEM 40, -5, 't', 226, %00011111 \ 3 = Slaves ITEM 83, -5, 't', 251, %00001111 \ 4 = Liquor/Wines ITEM 196, 8, 't', 54, %00000011 \ 5 = Luxuries ITEM 235, 29, 't', 8, %01111000 \ 6 = Narcotics ITEM 154, 14, 't', 56, %00000011 \ 7 = Computers ITEM 117, 6, 't', 40, %00000111 \ 8 = Machinery ITEM 78, 1, 't', 17, %00011111 \ 9 = Alloys ITEM 124, 13, 't', 29, %00000111 \ 10 = Firearms ITEM 176, -9, 't', 220, %00111111 \ 11 = Furs ITEM 32, -1, 't', 53, %00000011 \ 12 = Minerals ITEM 97, -1, 'k', 66, %00000111 \ 13 = Gold ITEM 171, -2, 'k', 55, %00011111 \ 14 = Platinum ITEM 45, -1, 'g', 250, %00001111 \ 15 = Gem-Stones ITEM 53, 15, 't', 192, %00000111 \ 16 = Alien itemsName: QQ23 [Show more] Type: Variable Category: Market Summary: Market prices tableContext: See this variable on its own page Variations: See code variations for this variable in the different versions References: This variable is used as follows: * GVL uses QQ23 * TT151 uses QQ23 * TT210 uses QQ23
Each item has four bytes of data, like this: Byte #0 = Base price Byte #1 = Economic factor in bits 0-4, with the sign in bit 7 Unit in bits 5-6 Byte #2 = Base quantity Byte #3 = Mask to control price fluctuations To make it easier for humans to follow, we've defined a macro called ITEM that takes the following arguments and builds the four bytes for us: ITEM base price, economic factor, units, base quantity, mask So for food, we have the following: * Base price = 19 * Economic factor = -2 * Unit = tonnes * Base quantity = 6 * Mask = %00000001.TI2 \ Called from below with A = 0, X = 0, Y = 4 when \ nosev_x and nosev_y are small, so we assume that \ nosev_z is big TYA \ A = Y = 4 LDY #2 JSR TIS3 \ Call TIS3 with X = 0, Y = 2, A = 4, to set roofv_z = STA INWK+20 \ -(nosev_x * roofv_x + nosev_y * roofv_y) / nosev_z JMP TI3 \ Jump to TI3 to keep tidying .TI1 \ Called from below with A = 0, Y = 4 when nosev_x is \ small TAX \ Set X = A = 0 LDA XX15+1 \ Set A = nosev_y, and if the top two magnitude bits AND #%01100000 \ are both clear, jump to TI2 with A = 0, X = 0, Y = 4 BEQ TI2 LDA #2 \ Otherwise nosev_y is big, so set up the index values \ to pass to TIS3 JSR TIS3 \ Call TIS3 with X = 0, Y = 4, A = 2, to set roofv_y = STA INWK+18 \ -(nosev_x * roofv_x + nosev_z * roofv_z) / nosev_y JMP TI3 \ Jump to TI3 to keep tidying .TIDY LDA INWK+10 \ Set (XX15, XX15+1, XX15+2) = nosev STA XX15 LDA INWK+12 STA XX15+1 LDA INWK+14 STA XX15+2 JSR NORM \ Call NORM to normalise the vector in XX15, i.e. nosev LDA XX15 \ Set nosev = (XX15, XX15+1, XX15+2) STA INWK+10 LDA XX15+1 STA INWK+12 LDA XX15+2 STA INWK+14 LDY #4 \ Set Y = 4 LDA XX15 \ Set A = nosev_x, and if the top two magnitude bits AND #%01100000 \ are both clear, jump to TI1 with A = 0, Y = 4 BEQ TI1 LDX #2 \ Otherwise nosev_x is big, so set up the index values LDA #0 \ to pass to TIS3 JSR TIS3 \ Call TIS3 with X = 2, Y = 4, A = 0, to set roofv_x = STA INWK+16 \ -(nosev_y * roofv_y + nosev_z * roofv_z) / nosev_x .TI3 LDA INWK+16 \ Set (XX15, XX15+1, XX15+2) = roofv STA XX15 LDA INWK+18 STA XX15+1 LDA INWK+20 STA XX15+2 JSR NORM \ Call NORM to normalise the vector in XX15, i.e. roofv LDA XX15 \ Set roofv = (XX15, XX15+1, XX15+2) STA INWK+16 LDA XX15+1 STA INWK+18 LDA XX15+2 STA INWK+20 LDA INWK+12 \ Set Q = nosev_y STA Q LDA INWK+20 \ Set A = roofv_z JSR MULT12 \ Set (S R) = Q * A = nosev_y * roofv_z LDX INWK+14 \ Set X = nosev_z LDA INWK+18 \ Set A = roofv_y JSR TIS1 \ Set (A ?) = (-X * A + (S R)) / 96 \ = (-nosev_z * roofv_y + nosev_y * roofv_z) / 96 \ \ This also sets Q = nosev_z EOR #%10000000 \ Set sidev_x = -A STA INWK+22 \ = (nosev_z * roofv_y - nosev_y * roofv_z) / 96 LDA INWK+16 \ Set A = roofv_x JSR MULT12 \ Set (S R) = Q * A = nosev_z * roofv_x LDX INWK+10 \ Set X = nosev_x LDA INWK+20 \ Set A = roofv_z JSR TIS1 \ Set (A ?) = (-X * A + (S R)) / 96 \ = (-nosev_x * roofv_z + nosev_z * roofv_x) / 96 \ \ This also sets Q = nosev_x EOR #%10000000 \ Set sidev_y = -A STA INWK+24 \ = (nosev_x * roofv_z - nosev_z * roofv_x) / 96 LDA INWK+18 \ Set A = roofv_y JSR MULT12 \ Set (S R) = Q * A = nosev_x * roofv_y LDX INWK+12 \ Set X = nosev_y LDA INWK+16 \ Set A = roofv_x JSR TIS1 \ Set (A ?) = (-X * A + (S R)) / 96 \ = (-nosev_y * roofv_x + nosev_x * roofv_y) / 96 EOR #%10000000 \ Set sidev_z = -A STA INWK+26 \ = (nosev_y * roofv_x - nosev_x * roofv_y) / 96 LDA #0 \ Set A = 0 so we can clear the low bytes of the \ orientation vectors LDX #14 \ We want to clear the low bytes, so start from sidev_y \ at byte #9+14 (we clear all except sidev_z_lo, though \ I suspect this is in error and that X should be 16) .TIL1 STA INWK+9,X \ Set the low byte in byte #9+X to zero DEX \ Set X = X - 2 to jump down to the next low byte DEX BPL TIL1 \ Loop back until we have zeroed all the low bytes RTS \ Return from the subroutineName: TIDY [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Orthonormalise the orientation vectors for a ship Deep dive: Tidying orthonormal vectors Orientation vectorsContext: See this subroutine on its own page References: This subroutine is called as follows: * MVEIT (Part 1 of 9) calls TIDY
This routine orthonormalises the orientation vectors for a ship. This means making the three orientation vectors orthogonal (perpendicular to each other), and normal (so each of the vectors has length 1). We do this because we use the small angle approximation to rotate these vectors in space. It is not completely accurate, so the three vectors tend to get stretched over time, so periodically we tidy the vectors with this routine to ensure they remain as orthonormal as possible..TIS2 TAY \ Store the argument A in Y AND #%01111111 \ Strip the sign bit from the argument, so A = |A| CMP Q \ If A >= Q then jump to TI4 to return a 1 with the BCS TI4 \ correct sign LDX #%11111110 \ Set T to have bits 1-7 set, so we can rotate through 7 STX T \ loop iterations, getting a 1 each time, and then \ getting a 0 on the 8th iteration... and we can also \ use T to catch our result bits into bit 0 each time .TIL2 ASL A \ Shift A to the left CMP Q \ If A < Q skip the following subtraction BCC P%+4 SBC Q \ A >= Q, so set A = A - Q \ \ Going into this subtraction we know the C flag is \ set as we passed through the BCC above, and we also \ know that A >= Q, so the C flag will still be set once \ we are done ROL T \ Rotate the counter in T to the left, and catch the \ result bit into bit 0 (which will be a 0 if we didn't \ do the subtraction, or 1 if we did) BCS TIL2 \ If we still have set bits in T, loop back to TIL2 to \ do the next iteration of 7 \ We've done the division and now have a result in the \ range 0-255 here, which we need to reduce to the range \ 0-96. We can do that by multiplying the result by 3/8, \ as 256 * 3/8 = 96 LDA T \ Set T = T / 4 LSR A LSR A STA T LSR A \ Set T = T / 8 + T / 4 ADC T \ = 3T / 8 STA T TYA \ Fetch the sign bit of the original argument A AND #%10000000 ORA T \ Apply the sign bit to T RTS \ Return from the subroutine .TI4 TYA \ Fetch the sign bit of the original argument A AND #%10000000 ORA #96 \ Apply the sign bit to 96 (which represents 1) RTS \ Return from the subroutineName: TIS2 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate A = A / Q Deep dive: Shift-and-subtract divisionContext: See this subroutine on its own page References: This subroutine is called as follows: * NORM calls TIS2
Calculate the following division, where A is a sign-magnitude number and Q is a positive integer: A = A / Q The value of A is returned as a sign-magnitude number with 96 representing 1, and the maximum value returned is 1 (i.e. 96). This routine is used when normalising vectors, where we represent fractions using integers, so this gives us an approximation to two decimal places..TIS3 STA P+2 \ Store P+2 in A for later LDA INWK+10,X \ Set Q = nosev_x_hi (plus X) STA Q LDA INWK+16,X \ Set A = roofv_x_hi (plus X) JSR MULT12 \ Set (S R) = Q * A \ = nosev_x_hi * roofv_x_hi LDX INWK+10,Y \ Set Q = nosev_x_hi (plus Y) STX Q LDA INWK+16,Y \ Set A = roofv_x_hi (plus Y) JSR MAD \ Set (A X) = Q * A + (S R) \ = (nosev_x,X * roofv_x,X) + \ (nosev_x,Y * roofv_x,Y) STX P \ Store low byte of result in P, so result is now in \ (A P) LDY P+2 \ Set Q = roofv_x_hi (plus argument A) LDX INWK+10,Y STX Q EOR #%10000000 \ Flip the sign of A \ Fall through into DIVDT to do: \ \ (P+1 A) = (A P) / Q \ \ = -((nosev_x,X * roofv_x,X) + \ (nosev_x,Y * roofv_x,Y)) \ / nosev_x,AName: TIS3 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate -(nosev_1 * roofv_1 + nosev_2 * roofv_2) / nosev_3Context: See this subroutine on its own page References: This subroutine is called as follows: * TIDY calls TIS3
Calculate the following expression: A = -(nosev_1 * roofv_1 + nosev_2 * roofv_2) / nosev_3 where 1, 2 and 3 are x, y, or z, depending on the values of X, Y and A. This routine is called with the following values: X = 0, Y = 2, A = 4 -> A = -(nosev_x * roofv_x + nosev_y * roofv_y) / nosev_z X = 0, Y = 4, A = 2 -> A = -(nosev_x * roofv_x + nosev_z * roofv_z) / nosev_y X = 2, Y = 4, A = 0 -> A = -(nosev_y * roofv_y + nosev_z * roofv_z) / nosev_x
Arguments: X Index 1 (0 = x, 2 = y, 4 = z) Y Index 2 (0 = x, 2 = y, 4 = z) A Index 3 (0 = x, 2 = y, 4 = z).DVIDT STA P+1 \ Set P+1 = A, so P(1 0) = (A P) EOR Q \ Set T = the sign bit of A EOR Q, so it's 1 if A and Q AND #%10000000 \ have different signs, i.e. it's the sign of the result STA T \ of A / Q LDA #0 \ Set A = 0 for us to build a result LDX #16 \ Set a counter in X to count the 16 bits in P(1 0) ASL P \ Shift P(1 0) left ROL P+1 ASL Q \ Clear the sign bit of Q the C flag at the same time LSR Q .DVL2 ROL A \ Shift A to the left CMP Q \ If A < Q skip the following subtraction BCC P%+4 SBC Q \ Set A = A - Q \ \ Going into this subtraction we know the C flag is \ set as we passed through the BCC above, and we also \ know that A >= Q, so the C flag will still be set once \ we are done ROL P \ Rotate P(1 0) to the left, and catch the result bit ROL P+1 \ into the C flag (which will be a 0 if we didn't \ do the subtraction, or 1 if we did) DEX \ Decrement the loop counter BNE DVL2 \ Loop back for the next bit until we have done all 16 \ bits of P(1 0) LDA P \ Set A = P so the low byte is in the result in A ORA T \ Set A to the correct sign bit that we set in T above RTS \ Return from the subroutineName: DVIDT [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate (P+1 A) = (A P) / QContext: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: No direct references to this subroutine in this source file
Calculate the following integer division between sign-magnitude numbers: (P+1 A) = (A P) / Q This uses the same shift-and-subtract algorithm as TIS2.PRINT "ELITE F" PRINT "Assembled at ", ~CODE_F% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_F%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_F% PRINT "S.ELTF ", ~CODE_F%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_F% SAVE "3-assembled-output/ELTF.bin", CODE_F%, P%, LOAD%Save ELTF.bin
[X]
Configuration variable AST
Ship type for an asteroid
[X]
Subroutine BAD (category: Status)
Calculate how bad we have been
[X]
Subroutine BAY (category: Status)
Go to the docking bay (i.e. show the Status Mode screen)
[X]
Subroutine BELL (category: Sound)
Make a standard system beep
[X]
Subroutine BPRNT (category: Text)
Print a 32-bit number, left-padded to a specific number of digits, with an optional decimal point
[X]
Subroutine BUMP2 (category: Dashboard)
Bump up the value of the pitch or roll dashboard indicator
[X]
Subroutine CHECK (category: Save and load)
Calculate the checksum for the last saved commander data block
[X]
Variable CHK (category: Save and load)
First checksum byte for the saved commander data file
[X]
Variable CHK2 (category: Save and load)
Second checksum byte for the saved commander data file
[X]
Subroutine CLYNS (category: Drawing the screen)
Clear the bottom three text rows of the space view
[X]
Configuration variable COPS
Ship type for a Viper
[X]
Configuration variable CYL
Ship type for a Cobra Mk III (trader)
[X]
Variable DAMP in workspace S% (Part 2 of 2)
Keyboard damping configuration setting
[X]
Subroutine DEATH2 (category: Start and end)
Reset most of the game and restart from the title screen
[X]
Subroutine DELAY (category: Utility routines)
Wait for a specified time
[X]
Subroutine DIALS (Part 1 of 4) (category: Dashboard)
Update the dashboard: speed indicator
[X]
Subroutine DKS3 (category: Keyboard)
Toggle a configuration setting and emit a beep
[X]
Subroutine DKS4 (category: Keyboard)
Scan the keyboard to see if a specific key is being pressed
[X]
Variable DNOIZ in workspace S% (Part 2 of 2)
Sound on/off configuration setting
[X]
Subroutine DORND (category: Maths (Arithmetic))
Generate random numbers
[X]
Subroutine ECBLB (category: Dashboard)
Light up the E.C.M. indicator bulb ("E") on the dashboard
[X]
Subroutine ECMOF (category: Sound)
Switch off the E.C.M.
[X]
Label EE20 in subroutine Main game loop (Part 5 of 6)
[X]
Subroutine EQSHP (category: Equipment)
Show the Equip Ship screen (FUNC-4)
[X]
Subroutine EXNO3 (category: Sound)
Make an explosion sound
[X]
Entry point FRCE in subroutine Main game loop (Part 6 of 6) (category: Main loop)
The entry point for the main game loop if we want to jump straight to a specific screen, by pretending to "press" a key, in which case A contains the internal key number of the key we want to "press"
[X]
Subroutine FX200 (category: Utility routines)
Set the behaviour of the ESCAPE and BREAK keys
[X]
Subroutine GTNME (category: Save and load)
Fetch the name of a commander file to save or load
[X]
Macro ITEM (category: Market)
Macro definition for the market prices table
[X]
Variable JSTE in workspace S% (Part 2 of 2)
Reverse both joystick channels configuration setting
[X]
Variable JSTK in workspace S% (Part 2 of 2)
Keyboard or joystick configuration setting
[X]
Variable KEYB in workspace S% (Part 1 of 2)
This flag indicates whether we are currently reading from the keyboard using OSRDCH or OSWORD, so the keyboard interrupt handler at KEY1 knows whether to pass key presses on to the OS
[X]
Subroutine KILLSHP (category: Universe)
Remove a ship from our local bubble of universe
[X]
Subroutine KS2 (category: Universe)
Check the local bubble for missiles with target lock
[X]
Subroutine KS3 (category: Universe)
Set the SLSP ship line heap pointer after shuffling ship slots
[X]
Subroutine KS4 (category: Universe)
Remove the space station and replace with a placeholder
[X]
Variable KYTB (category: Keyboard)
Lookup table for in-flight keyboard controls
[X]
Label LABEL_2 in subroutine Main game loop (Part 4 of 6)
[X]
Subroutine LL5 (category: Maths (Arithmetic))
Calculate Q = SQRT(R Q)
[X]
Subroutine LL9 (Part 1 of 12) (category: Drawing ships)
Draw ship: Check if ship is exploding, check if ship is in front
[X]
Subroutine LOD (category: Save and load)
Load a commander file
[X]
Subroutine LOOK1 (category: Flight)
Initialise the space view
[X]
The ball line heap for storing x-coordinates (see the deep dive on The ball line heap for details)
[X]
The ball line heap for storing y-coordinates (see the deep dive on The ball line heap for details)
[X]
Entry point M% in subroutine Main flight loop (Part 1 of 16) (category: Main loop)
The entry point for the main flight loop
[X]
Subroutine MAD (category: Maths (Arithmetic))
Calculate (A X) = Q * A + (S R)
[X]
Entry point MAL1 in subroutine Main flight loop (Part 4 of 16) (category: Main loop)
Marks the beginning of the ship analysis loop, so we can jump back here from part 12 of the main flight loop to work our way through each ship in the local bubble. We also jump back here when a ship is removed from the bubble, so we can continue processing from the next ship
[X]
Subroutine MESS (category: Flight)
Display an in-flight message
[X]
Entry point MLOOP in subroutine Main game loop (Part 5 of 6) (category: Main loop)
The entry point for the main game loop. This entry point comes after the call to the main flight loop and spawning routines, so it marks the start of the main game loop for when we are docked (as we don't need to call the main flight loop or spawning routines if we aren't in space)
[X]
Subroutine MSBAR (category: Dashboard)
Draw a specific indicator in the dashboard's missile bar
[X]
Configuration variable MSL
Ship type for a missile
[X]
Label MTT1 in subroutine Main game loop (Part 3 of 6)
[X]
Label MTT2 in subroutine Main game loop (Part 2 of 6)
[X]
Label MTT3 in subroutine Main game loop (Part 2 of 6)
[X]
Label MTT4 in subroutine Main game loop (Part 1 of 6)
[X]
Subroutine MULT12 (category: Maths (Arithmetic))
Calculate (S R) = Q * A
[X]
Subroutine MVEIT (Part 1 of 9) (category: Moving)
Move current ship: Tidy the orientation vectors
[X]
Variable NA% (category: Save and load)
The data block for the last saved commander
[X]
Configuration variable NI%
The number of bytes in each ship's data block (as stored in INWK and K%)
[X]
Subroutine NO3 (category: Sound)
Make a sound from a prepared sound block
[X]
Subroutine NOISE (category: Sound)
Make the sound whose number is in A
[X]
Subroutine NORM (category: Maths (Geometry))
Normalise the three-coordinate vector in XX15
[X]
Subroutine NOS1 (category: Sound)
Prepare a sound block
[X]
Subroutine NWSHP (category: Universe)
Add a new ship to our local bubble of universe
[X]
Configuration variable OIL
Ship type for a cargo canister
[X]
Configuration variable OSBYTE
The address for the OSBYTE routine
[X]
Configuration variable OSFILE
The address for the OSFILE routine
[X]
Configuration variable OSRDCH
The address for the OSRDCH routine
[X]
Configuration variable OSWORD
The address for the OSWORD routine
[X]
Variable PATG in workspace S% (Part 2 of 2)
Configuration setting to show the author names on the start-up screen
[X]
Subroutine QU5 (category: Start and end)
Reset the current commander data block to the last saved commander
[X]
Subroutine QUS1 (category: Save and load)
Save or load the commander file
[X]
Subroutine RDKEY (category: Keyboard)
Scan the keyboard for key presses
[X]
Subroutine REDU2 (category: Dashboard)
Reduce the value of the pitch or roll dashboard indicator
[X]
Subroutine RES2 (category: Start and end)
Reset a number of flight variables and workspaces
[X]
Subroutine RESET (category: Start and end)
Reset most variables
[X]
Variable RLINE (category: Text)
The OSWORD configuration block used to fetch a line of text from the keyboard
[X]
Workspace S% (Part 1 of 2) (category: Workspaces)
Vector addresses, compass shape and configuration settings
[X]
Variable SFX (category: Sound)
Sound data
[X]
Subroutine SPBLB (category: Dashboard)
Light up the space station indicator ("S") on the dashboard
[X]
Subroutine SPS3 (category: Maths (Geometry))
Copy a space coordinate from the K% block into K3
[X]
Subroutine SQUA (category: Maths (Arithmetic))
Clear bit 7 of A and calculate (A P) = A * A
[X]
Configuration variable SST
Ship type for the space station
[X]
Subroutine STATUS (category: Status)
Show the Status Mode screen (FUNC-9)
[X]
Subroutine SVE (category: Save and load)
Save the commander file
[X]
Subroutine TIS1 (category: Maths (Arithmetic))
Calculate (A ?) = (-X * A + (S R)) / 96
[X]
Subroutine TIS2 (category: Maths (Arithmetic))
Calculate A = A / Q
[X]
Subroutine TIS3 (category: Maths (Arithmetic))
Calculate -(nosev_1 * roofv_1 + nosev_2 * roofv_2) / nosev_3
[X]
Subroutine TITLE (category: Start and end)
Display a title screen with a rotating ship and prompt
[X]
Subroutine TR1 (category: Save and load)
Copy the last saved commander's name from NA% to INWK
[X]
Subroutine TRNME (category: Save and load)
Copy the last saved commander's name from INWK to NA%
[X]
Entry point TT100 in subroutine Main game loop (Part 2 of 6) (category: Main loop)
The entry point for the start of the main game loop, which calls the main flight loop and the moves into the spawning routine
[X]
Subroutine TT102 (category: Keyboard)
Process function key, save key, hyperspace and chart key presses and update the hyperspace counter
[X]
Subroutine TT103 (category: Charts)
Draw a small set of crosshairs on a chart
[X]
Subroutine TT110 (category: Flight)
Launch from a station or show the front space view
[X]
Subroutine TT111 (category: Universe)
Set the current system to the nearest system to a point
[X]
Subroutine TT146 (category: Universe)
Print the distance to the selected system in light years
[X]
Subroutine TT16 (category: Charts)
Move the crosshairs on a chart
[X]
Subroutine TT167 (category: Market)
Show the Market Price screen (FUNC-8)
[X]
Subroutine TT17 (category: Keyboard)
Scan the keyboard for cursor key movement
[X]
Subroutine TT18 (category: Flight)
Try to initiate a jump into hyperspace
[X]
Subroutine TT208 (category: Market)
Show the Sell Cargo screen (FUNC-3)
[X]
Subroutine TT213 (category: Market)
Show the Inventory screen (FUNC-0)
[X]
Subroutine TT219 (category: Market)
Show the Buy Cargo screen (FUNC-2)
[X]
Subroutine TT22 (category: Charts)
Show the Long-range Chart (FUNC-5)
[X]
Subroutine TT23 (category: Charts)
Show the Short-range Chart (FUNC-6)
[X]
Subroutine TT25 (category: Universe)
Show the Data on System screen (FUNC-7)
[X]
Subroutine TT27 (category: Text)
Print a text token
[X]
Subroutine TT66 (category: Drawing the screen)
Clear the screen and set the current view type
[X]
Subroutine TT67 (category: Text)
Print a newline
[X]
Subroutine TTX66 (category: Drawing the screen)
Clear the top part of the screen and draw a border box
[X]
Subroutine U% (category: Keyboard)
Clear the key logger
[X]
Variable UNIV (category: Universe)
Table of pointers to the local universe's ship data blocks
[X]
Workspace WP (category: Workspaces)
Ship slots, variables
[X]
Subroutine WPSHPS (category: Dashboard)
Clear the scanner and reset the ball line heap
[X]
Temporary storage, used to store the address of a ship blueprint. For example, it is used when we add a new ship to the local bubble in routine NWSHP, and it contains the address of the current ship's blueprint as we loop through all the nearby ships in the main flight loop
[X]
Variable XX21 (category: Drawing ships)
Ship blueprints lookup table
[X]
Subroutine ZERO (category: Utility routines)
Zero-fill pages &9, &A, &B, &C and &D
[X]
Subroutine ZES1 (category: Utility routines)
Zero-fill the page whose number is in X
[X]
Subroutine ZINF (category: Universe)
Reset the INWK workspace and orientation vectors
[X]
Subroutine Ze (category: Universe)
Initialise the INWK workspace to a hostile ship
[X]
Subroutine cpl (category: Universe)
Print the selected system name
[X]
Subroutine ee3 (category: Flight)
Print the hyperspace countdown in the top-left of the screen
[X]
Subroutine ex (category: Text)
Print a recursive token
[X]
Configuration variable func0
Internal key number for FUNC-0 (Inventory)
[X]
Configuration variable func1
Internal key number for FUNC-1 (Launch, Front)
[X]
Configuration variable func2
Internal key number for FUNC-2 (Buy Cargo, Rear)
[X]
Configuration variable func3
Internal key number for FUNC-3 (Sell Cargo, Left)
[X]
Configuration variable func4
Internal key number for FUNC-4 (Equip Ship, Right)
[X]
Configuration variable func5
Internal key number for FUNC-5 (Long-range Chart)
[X]
Configuration variable func6
Internal key number for FUNC-6 (Short-range Chart)
[X]
Configuration variable func7
Internal key number for FUNC-7 (Data on System)
[X]
Configuration variable func8
Internal key number for FUNC-8 (Market Price)
[X]
Configuration variable func9
Internal key number for FUNC-9 (Status Mode)
[X]
Subroutine hm (category: Charts)
Select the closest system and redraw the chart crosshairs
[X]
Subroutine hyp (category: Flight)
Start the hyperspace process
[X]
Subroutine hyp1 (category: Universe)
Process a jump to the system closest to (QQ9, QQ10)
[X]
[X]
Subroutine me1 (category: Flight)
Erase an old in-flight message and display a new one
[X]
Subroutine me2 (category: Flight)
Remove an in-flight message from the space view
[X]
Entry point me3 in subroutine Main game loop (Part 2 of 6) (category: Main loop)
Used by me2 to jump back into the main game loop after printing an in-flight message
[X]
Subroutine mes9 (category: Flight)
Print a text token, possibly followed by " DESTROYED"
[X]
Subroutine msblob (category: Dashboard)
Display the dashboard's missile indicators as white squares
[X]
Label mt1 in subroutine Main game loop (Part 4 of 6)
[X]
Label mt3 in subroutine Main game loop (Part 4 of 6)
[X]
Subroutine nWq (category: Stardust)
Create a random cloud of stardust
[X]
Subroutine ou2 (category: Flight)
Display "E.C.M.SYSTEM DESTROYED" as an in-flight message
[X]
Subroutine ou3 (category: Flight)
Display "FUEL SCOOPS DESTROYED" as an in-flight message
[X]
Subroutine ping (category: Universe)
Set the selected system to the current system
[X]
Subroutine plf (category: Text)
Print a text token followed by a newline
[X]
[X]