Skip to navigation

Elite on the BBC Micro and NES

Bank 0 (Part 1 of 5)

[NES version]

NES ELITE GAME SOURCE (BANK 0) NES Elite was written by Ian Bell and David Braben and is copyright D. Braben and I. Bell 1991/1992 The code on this site has been reconstructed from a disassembly of the version released on Ian Bell's personal website at http://www.elitehomepage.org/ The commentary is copyright Mark Moxon, and any misunderstandings or mistakes in the documentation are entirely my fault The terminology and notations used in this commentary are explained at https://www.bbcelite.com/terminology The deep dive articles referred to in this commentary can be found at https://www.bbcelite.com/deep_dives
This source file produces the following binary file: * bank0.bin
ELITE BANK 0 Produces the binary file bank0.bin.
ORG CODE%
Name: ResetMMC1_b0 [Show more] Type: Subroutine Category: Start and end Summary: The MMC1 mapper reset routine at the start of the ROM bank Deep dive: Splitting NES Elite across multiple ROM banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * Vectors_b0 calls ResetMMC1_b0

When the NES is switched on, it is hardwired to perform a JMP ($FFFC). At this point, there is no guarantee as to which ROM banks are mapped to $8000 and $C000, so to ensure that the game starts up correctly, we put the same code in each ROM at the following locations: * We put $C000 in address $FFFC in every ROM bank, so the NES always jumps to $C000 when it starts up via the JMP ($FFFC), irrespective of which ROM bank is mapped to $C000. * We put the same reset routine (this routine, ResetMMC1) at the start of every ROM bank, so the same routine gets run, whichever ROM bank is mapped to $C000. This ResetMMC1 routine is therefore called when the NES starts up, whatever the bank configuration ends up being. It then switches ROM bank 7 to $C000 and jumps into bank 7 at the game's entry point BEGIN, which starts the game.
.ResetMMC1_b0 SEI ; Disable interrupts INC $C006 ; Reset the MMC1 mapper, which we can do by writing a ; value with bit 7 set into any address in ROM space ; (i.e. any address from $8000 to $FFFF) ; ; The INC instruction does this in a more efficient ; manner than an LDA/STA pair, as it: ; ; * Fetches the contents of address $C006, which ; contains the high byte of the JMP destination ; below, i.e. the high byte of BEGIN, which is $C0 ; ; * Adds 1, to give $C1 ; ; * Writes the value $C1 back to address $C006 ; ; $C006 is in the ROM space and $C1 has bit 7 set, so ; the INC does all that is required to reset the mapper, ; in fewer cycles and bytes than an LDA/STA pair ; ; Resetting MMC1 maps bank 7 to $C000 and enables the ; bank at $8000 to be switched, so this instruction ; ensures that bank 7 is present JMP BEGIN ; Jump to BEGIN in bank 7 to start the game
Name: Interrupts_b0 [Show more] Type: Subroutine Category: Start and end Summary: The IRQ and NMI handler while the MMC1 mapper reset routine is still running
Context: See this subroutine on its own page References: This subroutine is called as follows: * Vectors_b0 calls Interrupts_b0
.Interrupts_b0 IF _NTSC RTI ; Return from the IRQ interrupt without doing anything ; ; This ensures that while the system is starting up and ; the ROM banks are in an unknown configuration, any IRQ ; interrupts that go via the vector at $FFFE and any NMI ; interrupts that go via the vector at $FFFA will end up ; here and be dealt with ; ; Once bank 7 is switched into $C000 by the ResetMMC1 ; routine, the vector is overwritten with the last two ; bytes of bank 7, which point to the IRQ routine ENDIF
Name: versionNumber_b0 [Show more] Type: Variable Category: Text Summary: The game's version number in bank 0
Context: See this variable on its own page References: No direct references to this variable in this source file
IF _NTSC EQUS " 5.0" ELIF _PAL EQUS "<2.8>" ENDIF
Name: ResetShipStatus [Show more] Type: Subroutine Category: Flight Summary: Reset the ship's speed, hyperspace counter, laser temperature, shields and energy banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOENTRY calls ResetShipStatus * TT110 calls ResetShipStatus * WARP calls ResetShipStatus
.ResetShipStatus LDA #0 ; Reduce the speed to 0 STA DELTA STA QQ22+1 ; Reset the on-screen hyperspace counter LDA #0 ; Cool down the lasers completely STA GNTMP LDA #$FF ; Recharge the forward and aft shields STA FSH STA ASH STA ENERGY ; Recharge the energy banks RTS ; Return from the subroutine
Name: DOENTRY [Show more] Type: Subroutine Category: Flight Summary: Dock at the space station, show the ship hangar and work out any mission progression Deep dive: The Constrictor mission The Thargoid Plans mission The Trumbles mission
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 9 of 16) calls DOENTRY
.DOENTRY 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 JSR RES2 ; Reset a number of flight variables and workspaces JSR LAUN ; Show the space station docking tunnel JSR ResetShipStatus ; Reset the ship's speed, hyperspace counter, laser ; temperature, shields and energy banks JSR HALL_b1 ; Show the ship hangar LDY #44 ; Wait until 44 NMI interrupts have passed (i.e. the JSR DELAY ; next 44 VBlanks) LDA TP ; Fetch bits 0 and 1 of TP, and if they are non-zero AND #%00000011 ; (i.e. mission 1 is either in progress or has been BNE EN1 ; completed), skip to EN1 LDA TALLY+1 ; If the high byte of TALLY is zero (so we have a combat BEQ EN4 ; rank below Competent), jump to EN4 as we are not yet ; good enough to qualify for a mission LDA GCNT ; Fetch the galaxy number into A, and if any of bits 1-7 LSR A ; are set (i.e. A > 1), jump to EN4 as mission 1 can BNE EN4 ; only be triggered in the first two galaxies JMP BRIEF ; If we get here, mission 1 hasn't started, we have ; reached a combat rank of Competent, and we are in ; galaxy 0 or 1 (shown in-game as galaxy 1 or 2), so ; it's time to start mission 1 by calling BRIEF .EN1 ; If we get here then mission 1 is either in progress or ; has been completed CMP #%00000011 ; If bits 0 and 1 are not both set, then jump to EN2 BNE EN2 JMP DEBRIEF ; Bits 0 and 1 are both set, so mission 1 is both in ; progress and has been completed, which means we have ; only just completed it, so jump to DEBRIEF to end the ; mission get our reward .EN2 ; Mission 1 has been completed, so now to check for ; mission 2 LDA GCNT ; Fetch the galaxy number into A CMP #2 ; If this is not galaxy 2 (shown in-game as galaxy 3), BNE EN4 ; jump to EN4 as we can only start mission 2 in the ; third galaxy LDA TP ; Extract bits 0-3 of TP into A AND #%00001111 CMP #%00000010 ; If mission 1 is complete and no longer in progress, BNE EN3 ; and mission 2 is not yet started, then bits 0-3 of TP ; will be %0010, so this jumps to EN3 if this is not the ; case LDA TALLY+1 ; If the high byte of TALLY is < 5 (so we have a combat CMP #5 ; rank that is less than 3/8 of the way from Dangerous BCC EN4 ; to Deadly), jump to EN4 as our rank isn't high enough ; for mission 2 JMP BRIEF2 ; If we get here, mission 1 is complete and no longer in ; progress, mission 2 hasn't started, we have reached a ; combat rank of 3/8 of the way from Dangerous to ; Deadly, and we are in galaxy 2 (shown in-game as ; galaxy 3), so it's time to start mission 2 by calling ; BRIEF2 .EN3 CMP #%00000110 ; If mission 1 is complete and no longer in progress, BNE EN5 ; and mission 2 has started but we have not yet been ; briefed and picked up the plans, then bits 0-3 of TP ; will be %0110, so this jumps to EN5 if this is not the ; case LDA QQ0 ; Set A = the current system's galactic x-coordinate CMP #215 ; If A <> 215 then jump to EN4 BNE EN4 LDA QQ1 ; Set A = the current system's galactic y-coordinate CMP #84 ; If A <> 84 then jump to EN4 BNE EN4 JMP BRIEF3 ; If we get here, mission 1 is complete and no longer in ; progress, mission 2 has started but we have not yet ; picked up the plans, and we have just arrived at ; Ceerdi at galactic coordinates (215, 84), so we jump ; to BRIEF3 to get a mission brief and pick up the plans ; that we need to carry to Birera .EN5 CMP #%00001010 ; If mission 1 is complete and no longer in progress, BNE EN4 ; and mission 2 has started and we have picked up the ; plans, then bits 0-3 of TP will be %1010, so this ; jumps to EN5 if this is not the case LDA QQ0 ; Set A = the current system's galactic x-coordinate CMP #63 ; If A <> 63 then jump to EN4 BNE EN4 LDA QQ1 ; Set A = the current system's galactic y-coordinate CMP #72 ; If A <> 72 then jump to EN4 BNE EN4 JMP DEBRIEF2 ; If we get here, mission 1 is complete and no longer in ; progress, mission 2 has started and we have picked up ; the plans, and we have just arrived at Birera at ; galactic coordinates (63, 72), so we jump to DEBRIEF2 ; to end the mission and get our reward .EN4 LDA COK ; If bit 7 of COK is set, then cheat mode has been BMI EN6 ; applied, so jump to EN6 LDA CASH+1 ; If the second most significant byte of CASH(0 1 2 3) BEQ EN6 ; is zero then the cash amount is less than &010000 ; (6553.6 credits), so jump to EN6 LDA TP ; If bit 4 of TP is set, then the Trumbles mission has AND #%00010000 ; already been completed, so jump to EN6 BNE EN6 ; If we get here then cheat mode has not been applied, ; we have at least 6553.6 credits and the Trumble ; mission has not yet been offered, so we do that now JMP TBRIEF ; Jump to TBRIEF to offer the Trumble mission, returning ; from the subroutine using a tail call .EN6 JMP BAY ; If we get here them we didn't start or any missions, ; so jump to BAY to go to the docking bay (i.e. show the ; Status Mode screen) RTS ; Return from the subroutine
Name: Main flight loop (Part 4 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: Copy the ship's data block from K% to the zero-page workspace at INWK Deep dive: Program flow of the main game loop Ship data blocks Splitting the main loop in the NES version
Context: See this subroutine on its own page References: This subroutine is called as follows: * FlightLoop4To16 calls via MAL1

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Start looping through all the ships in the local bubble, and for each one: * Copy the ship's data block from K% to INWK * Set XX0 to point to the ship's blueprint (if this is a ship)
Other entry points: MAL1 Marks the beginning of the ship analysis loop, so we can jump back here from part 12 of the main flight loop to work our way through each ship in the local bubble. We also jump back here when a ship is removed from the bubble, so we can continue processing from the next ship
.MAL1 STX XSAV ; Store the current slot number in XSAV STA TYPE ; Store the ship type in TYPE SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 JSR GINF ; Call GINF to fetch the address of the ship data block ; for the ship in slot X and store it in INF. The data ; block is in the K% workspace, which is where all the ; ship data blocks are stored ; Next we want to copy the ship data block from INF to ; the zero-page workspace at INWK, so we can process it ; more efficiently LDY #NI%-1 ; There are NI% bytes in each ship data block (and in ; the INWK workspace, so we set a counter in Y so we can ; loop through them .MAL2 LDA (INF),Y ; Load the Y-th byte of INF and store it in the Y-th STA INWK,Y ; byte of INWK DEY ; Decrement the loop counter BPL MAL2 ; Loop back for the next byte until we have copied the ; last byte from INF to INWK SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA TYPE ; If the ship type is negative then this indicates a BMI MA21 ; planet or sun, so jump down to MA21, as the next bit ; sets up a pointer to the ship blueprint, and then ; checks for energy bomb damage, and neither of these ; apply to planets and suns CMP #SST ; If this is not the space station, jump to main32 BNE main32 LDA spasto ; Copy the address of the space station's ship blueprint STA XX0 ; from spasto(1 0) to XX0(1 0), which we set up in NWSPS LDA spasto+1 ; when calculating the correct station type (Coriolis or STA XX0+1 ; Dodo) LDY #SST * 2 ; Set Y = ship type * 2 BNE main33 ; Jump to main33 (this BNE is effectively a JMP as Y is ; never zero) .main32 ASL A ; Set Y = ship type * 2 TAY LDA XX21-2,Y ; The ship blueprints at XX21 start with a lookup STA XX0 ; table that points to the individual ship blueprints, ; so this fetches the low byte of this particular ship ; type's blueprint and stores it in XX0 LDA XX21-1,Y ; Fetch the high byte of this particular ship type's STA XX0+1 ; blueprint and store it in XX0+1 .main33 ; We now check whether this ship prevents us from ; performing an in-system jump CPY #ESC * 2 ; If this is an escape pod, jump to main36 to skip the BEQ main36 ; following, as it doesn't prevent jumping CPY #TGL * 2 ; If this is a Thargon, jump to main36 to skip the BEQ main36 ; following, as it doesn't prevent jumping CPY #SST * 2 ; If this is the space station, jump to main35 to check BEQ main35 ; whether it is hostile LDA INWK+32 ; If bit 7 of the ship's byte #32 is clear, then AI is BPL main36 ; not enabled, so jump to main36 to skip the following, ; as it doesn't prevent jumping CPY #MSL * 2 ; If this is a missile, jump to main34 to skip the BEQ main34 ; aggression level check (as this doesn't apply to ; missiles) AND #%00111110 ; If bits 1-5 of the ship's byte #32 are clear, then the BEQ main36 ; ship's aggression level is zero, so jump to main36 to ; skip the following, as it doesn't prevent jumping .main34 LDA INWK+31 ; If either bit 5 or 7 of the ship's byte #31 are set, AND #%10100000 ; then the ship is exploding or has been killed, so jump BNE main36 ; to main36 to skip the following, as it doesn't prevent ; jumping .main35 LDA NEWB ; If bit 2 of the ship's NEWB flag is clear then the AND #%00000100 ; ship is not hostile, so jump to main36 to skip the BEQ main36 ; following, as it doesn't prevent jumping ; If we get here then this is one of the following: ; ; * A missile ; ; * A hostile space station ; ; * An aggressive ship with AI enabled ; ; and it isn't an escape pod or Thargon, and it hasn't ; been killed and isn't exploding ; ; So this ship prevents us from performing an in-system ; jump, so we need to set bit 7 of allowInSystemJump ; to do just that ASL allowInSystemJump ; Set bit 7 of allowInSystemJump to prevent us from SEC ; being able to perform an in-system jump ROR allowInSystemJump .main36
Name: Main flight loop (Part 5 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: If an energy bomb has been set off, potentially kill this ship Deep dive: Program flow of the main game loop
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Continue looping through all the ships in the local bubble, and for each one: * If an energy bomb has been set off and this ship can be killed, kill it and increase the kill tally
LDA BOMB ; If we set off our energy bomb (see MA24 above), then BPL MA21 ; BOMB is now negative, so this skips to MA21 if our ; energy bomb is not going off CPY #2*SST ; If the ship in Y is the space station, jump to BA21 BEQ MA21 ; as energy bombs are useless against space stations CPY #2*THG ; If the ship in Y is a Thargoid, jump to BA21 as energy BEQ MA21 ; bombs have no effect against Thargoids CPY #2*CON ; If the ship in Y is the Constrictor, jump to BA21 BCS MA21 ; as energy bombs are useless against the Constrictor ; (the Constrictor is the target of mission 1, and it ; would be too easy if it could just be blown out of ; the sky with a single key press) LDA INWK+31 ; If the ship we are checking has bit 5 set in its ship AND #%00100000 ; byte #31, then it is already exploding, so jump to BNE MA21 ; BA21 as ships can't explode more than once ASL INWK+31 ; The energy bomb is killing this ship, so set bit 7 of SEC ; the ship byte #31 to indicate that it has now been ROR INWK+31 ; killed LDX TYPE ; Set X to the type of the ship that was killed so the ; following call to EXNO2 can award us the correct ; number of fractional kill points JSR EXNO2 ; Call EXNO2 to process the fact that we have killed a ; ship (so increase the kill tally, make an explosion ; sound and possibly display "RIGHT ON COMMANDER!")
Name: Main flight loop (Part 6 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: Move the ship in space and copy the updated INWK data block back to K% Deep dive: Program flow of the main game loop Program flow of the ship-moving routine Ship data blocks
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

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

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

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

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Process docking with a space station For details on the various docking checks in this routine, see the deep dive on "Docking checks".
Other entry points: GOIN We jump here from part 3 of the main flight loop if the docking computer is activated by pressing "C"
.ISDK LDA K%+NIK%+36 ; 1. Fetch the NEWB flags (byte #36) of the second ship AND #%00000100 ; in the ship data workspace at K%, which is reserved BNE MA622 ; for the sun or the space station (in this case it's ; the latter), and if bit 2 is set, meaning the station ; is hostile, jump down to MA622 to fail docking (so ; trying to dock at a station that we have annoyed does ; not end well) LDA INWK+14 ; 2. If nosev_z_hi < 214, jump down to MA62 to fail CMP #214 ; docking, as the angle of approach is greater than 26 BCC MA62 ; degrees JSR SPS1 ; Call SPS1 to calculate the vector to the planet and ; store it in XX15 LDA XX15+2 ; Set A to the z-axis of the vector ; This version of Elite omits check 3 (which would check ; the sign of the z-axis) CMP #89 ; 4. If z-axis < 89, jump to MA62 to fail docking, as BCC MA62 ; we are not in the 22.0 degree safe cone of approach LDA INWK+16 ; 5. If |roofv_x_hi| < 80, jump to MA62 to fail docking, AND #%01111111 ; as the slot is more than 36.6 degrees from horizontal CMP #80 BCC MA62 .GOIN JSR ResetMusicAfterNMI ; Wait for the next NMI before resetting the current ; tune to 0 (no tune) and stopping the music ; If we arrive here, we just docked successfully JMP DOENTRY ; Go to the docking bay (i.e. show the ship hangar) .MA62 ; If we arrive here, docking has just failed LDA auto ; If the docking computer is engaged, ensure we dock BNE GOIN ; successfully even if the approach isn't correct, as ; the docking computer algorithm isn't perfect (so this ; fixes the issue in the other versions of Elite where ; the docking computer can kill you) .MA622 LDA DELTA ; If the ship's speed is < 5, jump to MA67 to register CMP #5 ; some damage, but not a huge amount BCC MA67 JMP DEATH ; Otherwise we have just crashed into the station, so ; process our death
Name: Main flight loop (Part 10 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: Remove if scooped, or process collisions Deep dive: Program flow of the main game loop
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Continue looping through all the ships in the local bubble, and for each one: * Remove scooped item after both successful and failed scooping attempts * Process collisions
.MA59 ; If we get here then scooping failed JSR EXNO3 ; Make the sound of the cargo canister being destroyed ; and fall through into MA60 to remove the canister ; from our local bubble .MA60 ; If we get here then scooping was successful ASL INWK+31 ; Set bit 7 of the scooped or destroyed item, to denote SEC ; that it has been killed and should be removed from ROR INWK+31 ; the local bubble .MA61 BNE MA26 ; Jump to MA26 to skip over the collision routines and ; to move on to missile targeting (this BNE is ; effectively a JMP as A will never be zero) .MA67 ; If we get here then we have collided with something, ; but not fatally LDA #1 ; Set the speed in DELTA to 1 (i.e. a sudden stop) STA DELTA LDA #5 ; Set the amount of damage in A to 5 (a small dent) and BNE MA63 ; jump down to MA63 to process the damage (this BNE is ; effectively a JMP as A will never be zero) .MA58 ; If we get here, we have collided with something in a ; potentially fatal way ASL INWK+31 ; Set bit 7 of the ship we just collided with, to SEC ; denote that it has been killed and should be removed ROR INWK+31 ; from the local bubble LDA INWK+35 ; Load A with the energy level of the ship we just hit SEC ; Set the amount of damage in A to 128 + A / 2, so ROR A ; this is quite a big dent, and colliding with higher ; energy ships will cause more damage .MA63 JSR OOPS ; The amount of damage is in A, so call OOPS to reduce ; our shields, and if the shields are gone, there's a ; chance of cargo loss or even death JSR EXNO3 ; Make the sound of colliding with the other ship and ; fall through into MA26 to try targeting a missile
Name: Main flight loop (Part 11 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: Process missile lock and firing our laser Deep dive: Program flow of the main game loop Flipping axes between space views
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Continue looping through all the ships in the local bubble, and for each one: * If this is not the front space view, flip the axes of the ship's coordinates in INWK * Process missile lock * Process our laser firing
.MA26 LDA QQ11 ; If this is not a space view, jump to MA15 to skip BEQ P%+5 ; missile and laser locking JMP MA15 JSR PLUT ; Call PLUT to update the geometric axes in INWK to ; match the view (front, rear, left, right) LDA LAS ; If we are firing our laser then LAS will contain the BNE main37 ; laser power (which will be non-zero), so jump to ; main37 to check if the current ship is in the ; crosshairs, to see if we are hitting it LDA MSAR ; If the active missile is not looking for a target then BEQ main38 ; MSAR will be zero, so jump to main38 to skip the ; following, as we can't target lock an unarmed missile LDA MSTG ; If MSTG is positive (i.e. it does not have bit 7 set), BPL main38 ; then it indicates we already have a missile locked on ; a target (in which case MSTG contains the ship number ; of the target), so jump to main38 to skip targeting ; ; Or to put it another way, if MSTG = $FF, which means ; there is no current target lock, then keep going .main37 JSR HITCH ; Call HITCH to see if this ship is in the crosshairs, BCS main39 ; in which case the C flag will be set and we jump to ; main39 (so if there is no missile or laser lock, we ; jump to MA8 to skip the following) .main38 JMP MA8 ; Jump to MA8 to skip the following .main39 LDA MSAR ; We have missile lock, so check whether the leftmost BEQ MA47 ; missile is currently armed, and if not, jump to MA47 ; to process laser fire, as we can't lock an unarmed ; missile LDA MSTG ; If MSTG is positive (i.e. it does not have bit 7 set), BPL MA47 ; then it indicates we already have a missile locked on ; a target (in which case MSTG contains the ship number ; of the target), so jump to MA47 to skip setting the ; target and beeping JSR BEEP_b7 ; We have missile lock and an armed missile, so call ; the BEEP subroutine to make a short, high beep LDX XSAV ; Call ABORT2 to store the details of this missile LDY #109 ; lock, with the targeted ship's slot number in X JSR ABORT2 ; (which we stored in XSAV at the start of this ship's ; loop at MAL1), and set the colour of the missile ; indicator to the pattern number in Y (red ; indicator = pattern 109) .MA47 ; If we get here then the ship is in our sights, but ; we didn't lock a missile, so let's see if we're ; firing the laser LDA LAS ; If we are firing the laser then LAS will contain the BEQ MA8 ; laser power (which we set in MA68 above), so if this ; is zero, jump down to MA8 to skip the following LDX #15 ; We are firing our laser and the ship in INWK is in JSR EXNO ; the crosshairs, so call EXNO to make the sound of ; us making a laser strike on another ship LDA TYPE ; Did we just hit the space station? If so, jump to CMP #SST ; MA14+2 to make the station hostile, skipping the BEQ MA14+2 ; following as we can't destroy a space station CMP #SPL ; Did we just hit a splinter? If not jump to main40 to BNE main40 ; skip the following LDX LAS ; We just hit a splinter, so we now check whether we CPX #Mlas ; have a mining laser fitted (in which case the laser BEQ MA14+2 ; power in LAS will be Mlas), and if so, jump to MA14+2 ; to skip the following, so we can't destroy splinters ; with mining lasers (which is handy, as we probably ; want to scoop them up instead) .main40 CMP #CON ; If the ship we hit is less than #CON - i.e. it's not BCC BURN ; a Constrictor, Cougar, Dodo station or the Elite logo, ; jump to BURN to skip the following LDA LAS ; Set A to the power of the laser we just used to hit ; the ship (i.e. the laser in the current view) CMP #(Armlas AND 127) ; If the laser is not a military laser, jump to MA14+2 BNE MA14+2 ; to skip the following, as only military lasers have ; any effect on the Constrictor or Cougar (or the Elite ; logo, should you ever bump into one of those out there ; in the black...) LSR LAS ; Divide the laser power of the current view by 4, so LSR LAS ; the damage inflicted on the super-ship is a quarter of ; the damage our military lasers would inflict on a ; normal ship .BURN LDA INWK+35 ; Fetch the hit ship's energy from byte #35 and subtract SEC ; our current laser power, and if the result is greater SBC LAS ; than zero, the other ship has survived the hit, so BCS MA14 ; jump down to MA14 to make it angry ASL INWK+31 ; Set bit 7 of the ship byte #31 to indicate that it has SEC ; now been killed ROR INWK+31 JSR HideShip_b1 ; Update the ship so it is no longer shown on the ; scanner LDA LAS ; Are we using mining lasers? If not, jump to nosp to CMP #Mlas ; spawn canisters, otherwise keep going BNE nosp ; If we get here then we are using mining lasers LDA TYPE ; Did we just kill an asteroid? If so, jump to main41 CMP #AST ; to break the asteroid up into splinters BEQ main41 CMP #6 ; Did we just kill a boulder? If not jump to nosp to BNE nosp ; spawn canisters, otherwise keep going ; If we get here then we are using mining lasers and we ; just blasted a boulder JSR DORND ; Set A and X to random numbers BPL main43 ; If bit 7 of A is clear (50% chance), jump to main43 to ; skip spawning any splinters LDA #1 ; Otherwise set A = 1 so we spawn one splinter below BNE main42 ; Jump to main42 to spawn one splinter (this BNE is ; effectively a JMP as A is never zero) .main41 ; If we get here then we are using mining lasers and we ; just blasted an asteroid JSR DORND ; Set A and X to random numbers ORA #1 ; Reduce the random number in A to the range 1-3 AND #3 .main42 LDX #SPL ; Set X to the ship type for a splinter JSR SPIN2 ; Call SPIN2 to spawn A items of type X (so we spawn ; 1-3 splinters if we just destroyed an asteroid, or we ; spawn one splinter 50% of the time if we just ; destroyed a boulder) JMP main43 ; Jump to main43 to skip spawning canisters .nosp LDY #PLT ; Randomly spawn some alloy plates JSR SPIN LDY #OIL ; Randomly spawn some cargo canisters JSR SPIN .main43 LDX TYPE ; Set X to the type of the ship that was killed so the ; following call to EXNO2 can award us the correct ; number of fractional kill points JSR EXNO2 ; Call EXNO2 to process the fact that we have killed a ; ship (so increase the kill tally, make an explosion ; sound and so on) .MA14 STA INWK+35 ; Store the hit ship's updated energy in ship byte #35 LDA TYPE ; Call ANGRY to make this ship hostile, now that we JSR ANGRY ; have hit it
Name: Main flight loop (Part 12 of 16) [Show more] Type: Subroutine Category: Main loop Summary: For each nearby ship: Draw the ship, remove if killed, loop back Deep dive: Program flow of the main game loop Drawing ships
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Continue looping through all the ships in the local bubble, and for each one: * Draw the ship * Process removal of killed ships * Loop back up to MAL1 to move onto the next ship in the local bubble
.MA8 JSR LL9_b1 ; Call LL9 to draw the ship we're processing on-screen .MA15 LDY #35 ; Fetch the ship's energy from byte #35 and copy it to LDA INWK+35 ; byte #35 in INF (so the ship's data in K% gets STA (INF),Y ; updated) LDA INWK+34 ; Fetch the ship's explosion cloud counter from byte #34 LDY #34 ; and copy it to byte #34 in INF (so the ship's data in STA (INF),Y ; K% gets updated) LDA NEWB ; If bit 7 of the ship's NEWB flags is set, which means BMI KS1S ; the ship has docked or been scooped, jump to KS1S to ; skip the following, as we can't get a bounty for a ; ship that's no longer around LDA INWK+31 ; If bit 7 of the ship's byte #31 is clear, then the BPL MAC1 ; ship hasn't been killed by energy bomb, collision or ; laser fire, so jump to MAC1 to skip the following AND #%00100000 ; If bit 5 of the ship's byte #31 is clear then the BEQ MAC1 ; ship is no longer exploding, so jump to MAC1 to skip ; the following LDA NEWB ; Extract bit 6 of the ship's NEWB flags, so A = 64 if AND #%01000000 ; bit 6 is set, or 0 if it is clear. Bit 6 is set if ; this ship is a cop, so A = 64 if we just killed a ; policeman, otherwise it is 0 ORA FIST ; Update our FIST flag ("fugitive/innocent status") to STA FIST ; at least the value in A, which will instantly make us ; a fugitive if we just shot the sheriff, but won't ; affect our status if the enemy wasn't a copper LDA MJ ; If we are in witchspace (in which case MJ > 0) or ORA demoInProgress ; demoInProgress > 0 (in which case we are playing the BNE KS1S ; demo), jump to KS1S to skip showing an on-screen ; bounty for this kill LDY #10 ; Fetch byte #10 of the ship's blueprint, which is the JSR GetShipBlueprint ; low byte of the bounty awarded when this ship is BEQ KS1S ; killed (in Cr * 10), and if it's zero jump to KS1S as ; there is no on-screen bounty to display TAX ; Put the low byte of the bounty into X INY ; Fetch byte #11 of the ship's blueprint, which is the JSR GetShipBlueprint ; high byte of the bounty awarded (in Cr * 10), and put TAY ; it into Y JSR MCASH ; Call MCASH to add (Y X) to the cash pot LDA #0 ; Print control code 0 (current cash, right-aligned to JSR MESS ; width 9, then " CR", newline) as an in-flight message .KS1S JMP KS1 ; Process the killing of this ship (which removes this ; ship from its slot and shuffles all the other ships ; down to close up the gap) .MAC1 LDA TYPE ; If the ship we are processing is a planet or sun, BMI MA27 ; jump to MA27 to skip the following two instructions JSR FAROF ; If the ship we are processing is a long way away (its BCC KS1S ; distance in any one direction is > 224, jump to KS1S ; to remove the ship from our local bubble, as it's just ; left the building .MA27 LDY #31 ; Fetch the ship's explosion/killed state from byte #31, LDA INWK+31 ; clear bit 6 to denote that an explosion has not been AND #%10111111 ; drawn, and copy it to byte #31 in INF (so the ship's STA (INF),Y ; data in K% gets updated) LDX XSAV ; We're done processing this ship, so fetch the ship's ; slot number, which we saved in XSAV back at the start ; of the loop INX ; Increment the slot number to move on to the next slot RTS ; Return from the subroutine
Name: FlightLoop4To16 [Show more] Type: Subroutine Category: Main loop Summary: Display in-flight messages, call parts 4 to 12 of the main flight loop for each slot, and fall through into parts 13 to 16 Deep dive: Splitting the main loop in the NES version The NES combat demo
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEATH calls FlightLoop4To16 * Main flight loop (Part 3 of 16) calls FlightLoop4To16 * RunDemoFlightLoop calls FlightLoop4To16
.main24 DEC DLY ; Decrement the delay counter in DLY, which is used to ; control how long flight messages remain on-screen BMI main27 ; If DLY is now negative, jump to main27 to set DLY to ; zero and skip the following, as there is no flight ; message to display BEQ main25 ; DLY is now zero so it must have been non-zero before ; we decremented it, so jump to main25 to remove the ; flight message from the screen, as its timer has run ; down ; DLY is non-zero, so we need to redraw any flight ; messages we may have, so they remain on-screen while ; DLY is still ticking down JSR PrintFlightMessage ; Print the current in-flight message, if there is one JMP main26 ; Jump to main26 to display the message we just printed ; and continue with the rest of the main loop .main25 JSR CLYNS ; Clear the bottom two text rows of the upper screen, ; and move the text cursor to column 1 on row 21, i.e. ; the start of the top row of the two bottom rows .main26 JSR DrawMessageInNMI ; Configure the NMI to update the in-flight message part ; of the screen (which is either the current in-flight ; message, or the part of the screen that the call to ; CLYNS just cleared) JMP MA16 ; Jump to MA16 to skip the following and continue with ; the rest of the main loop .FlightLoop4To16 LDA QQ11 ; If this is not the space view (i.e. QQ11 is non-zero), BNE main24 ; jump to main24 to print the flight message for ; non-space views, rejoining the main subroutine at MA16 ; below DEC DLY ; Decrement the delay counter in DLY, which is used to ; control how long flight messages remain on-screen BMI main27 ; If DLY is now 0 or negative, jump to main27 to set DLY BEQ main27 ; to zero and skip the following, as there is no flight ; message to display ; DLY is non-zero, so we need to redraw any flight ; messages we may have, so they remain on-screen while ; DLY is still ticking down JSR PrintFlightMessage ; Print the current flight message, if there is one JMP MA16 ; Jump to MA16 to skip the following and continue with ; the rest of the main loop .main27 LDA #0 ; Set DLY to 0 so that it doesn't decrement below zero STA DLY .MA16 LDA ECMP ; If our E.C.M is not on, skip to MA69, otherwise keep BEQ MA69 ; going to drain some energy JSR DENGY ; Call DENGY to deplete our energy banks by 1 BEQ MA70 ; If we have no energy left, jump to MA70 to turn our ; E.C.M. off .MA69 LDA ECMA ; If an E.C.M is going off (ours or an opponent's) then BEQ MA66 ; keep going, otherwise skip to MA66 LDA #128 ; Set K+2 = 128 to send to the DrawLightning routine as STA K+2 ; the x-coordinate of the centre of the lightning, so ; it is centred on-screen LDA #127 ; Set K = 127 to send to the DrawLightning routine as STA K ; half the height of the lightning, so it fills the ; whole screen width LDA halfScreenHeight ; Set K+3 to the y-coordinate of the centre of the STA K+3 ; screen in halfScreenHeight, to send to the ; DrawLightning routine as the y-coordinate of the ; centre of the lightning, so it is centred on-screen STA K+1 ; Set K+1 to the y-coordinate of the centre of the ; screen in halfScreenHeight, to send to the ; DrawLightning routine as half the height of the ; lightning, so it fills the whole screen height JSR DrawLightning_b6 ; Draw the lightning effect of the E.C.M. going off DEC ECMA ; Decrement the E.C.M. countdown timer, and if it has BNE MA66 ; reached zero, keep going, otherwise skip to MA66 .MA70 JSR ECMOF ; If we get here then either we have either run out of ; energy, or the E.C.M. timer has run down, so switch ; off the E.C.M. .MA66 LDX #0 ; We are about to work our way through all the ships in ; the bubble, calling MAL1 (parts 4 to 12 of the main ; flight loop) for each of them, so set X as a ship slot ; counter LDA FRIN ; If slot 0 is empty, jump to main28 to move on to the BEQ main28 ; next slot JSR MAL1 ; Call parts 4 to 12 of the main flight loop to update ; the ship in slot 0 .main28 LDX #2 ; We deal with the sun/space station in slot 1 below, so ; we now skip to slot 2 by setting X accordingly .main29 LDA FRIN,X ; If slot X is empty then we have reached the last slot, BEQ main30 ; so jump to main30 to stop updating the slots JSR MAL1 ; Call parts 4 to 12 of the main flight loop to update ; the ship in slot X JMP main29 ; Loop back until we have updated all the ship slots .main30 LDX #1 ; We now process the sun/space station in slot 1, so we ; set X as the slot number LDA FRIN+1 ; If slot 1 is empty then there is no sun or space BEQ MA18 ; station (which can happen in witchspace), so jump to ; part 13 of the main loop as we are done updating the ; ship slots BPL main31 ; If bit 7 of the ship type is clear, then this is the ; space station rather than the sun, so jump to main31 ; to skip the following LDY #0 ; Set the "space station present" flag to 0, as we are STY SSPR ; no longer in the space station's safe zone .main31 JSR MAL1 ; Call parts 4 to 12 of the main flight loop to update ; the sun or space station in slot 2 ; Fall through into part 13 to continue working through ; the main flight loop
Name: Main flight loop (Part 13 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Show energy bomb effect, charge shields and energy banks Deep dive: Program flow of the main game loop Scheduling tasks with the main loop counter
Context: See this subroutine on its own page References: This subroutine is called as follows: * FlightLoop4To16 calls via MA18

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Show energy bomb effect (if applicable) * Charge shields and energy banks (every 7 iterations of the main loop)
Other entry points: MA18 Entry point for part 13 of the main flight loop
.MA18 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA BOMB ; If we set off our energy bomb (see MA24 above), then BPL MA77 ; BOMB is now negative, so this skips to MA21 if our ; energy bomb is not going off ASL BOMB ; We set off our energy bomb, so rotate BOMB to the ; left by one place. BOMB was rotated left once already ; during this iteration of the main loop, back at MA24, ; so if this is the first pass it will already be ; %11111110, and this will shift it to %11111100 - so ; if we set off an energy bomb, it stays activated ; (BOMB > 0) for four iterations of the main loop BMI MA77 ; If the result has bit 7 set, skip the following ; instruction as the bomb is still going off JSR HideHiddenColour ; Set the hidden colour to black, to switch off the ; energy bomb effect (as the effect works by showing ; hidden content instead of the visible content) JSR UpdateIconBar_b3 ; Update the icon bar to remove the energy bomb icon, ; as it is a single-use weapon .MA77 LDA MCNT ; Fetch the main loop counter and calculate MCNT mod 7, AND #7 ; jumping to MA22 if it is non-zero (so the following BNE MA22 ; code only runs every 8 iterations of the main loop) JSR ChargeShields ; Charge the shields and energy banks
Name: Main flight loop (Part 14 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Spawn a space station if we are close enough to the planet Deep dive: Program flow of the main game loop Scheduling tasks with the main loop counter Ship data blocks The space station safe zone
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Spawn a space station if we are close enough to the planet (every 32 iterations of the main loop)
LDA MJ ; If we are in witchspace, jump down to MA23S to skip BNE MA23S ; the following, as there are no space stations in ; witchspace LDA MCNT ; Fetch the main loop counter and calculate MCNT mod 32, AND #31 ; jumping to MA93 if it is on-zero (so the following BNE MA93 ; code only runs every 32 iterations of the main loop) LDA SSPR ; If we are inside the space station safe zone, jump to BNE MA23S ; MA23S to skip the following, as we already have a ; space station and don't need another TAY ; Set Y = A = 0 (A is 0 as we didn't branch with the ; previous BNE instruction) JSR MAS2 ; Call MAS2 to calculate the largest distance to the BNE MA23S ; planet in any of the three axes, and if it's ; non-zero, jump to MA23S to skip the following, as we ; are too far from the planet to bump into a space ; station ; We now want to spawn a space station, so first we ; need to set up a ship data block for the station in ; INWK that we can then pass to NWSPS to add a new ; station to our bubble of universe. We do this by ; copying the planet data block from K% to INWK so we ; can work on it, but we only need the first 29 bytes, ; as we don't need to worry about bytes #29 to #35 ; for planets (as they don't have rotation counters, ; AI, explosions, missiles or energy levels) LDX #28 ; So we set a counter in X to copy 29 bytes from K%+0 ; to K%+28 .MAL4 LDA K%,X ; Load the X-th byte of K% and store in the X-th byte STA INWK,X ; of the INWK workspace DEX ; Decrement the loop counter BPL MAL4 ; Loop back for the next byte until we have copied the ; first 28 bytes of K% to INWK JSR SpawnSpaceStation ; If we are close enough, add a new space station to our ; local bubble of universe BCS MA23S ; If we spawned the space station, jump to MA23S to skip ; the following ; We didn't manage to spawn the space station, so now ; we rotate the planet around the x-axis and then the ; z-axis, and then have another go ; ; We rotate the planet by swapping the orientation ; vectors as follows (in parallel): ; ; * Set nosev = roofv ; ; * Set roofv = sidev ; ; * Set sidev = nosev ; ; First we need to set up a ship data block for the ; station in INWK again, which we do by copying the ; first nine bytes from the planet data block in K% to ; INWK (we only need the coordinates and the orientation ; vectors for the calculation, and we'll do the latter ; in a minute) LDX #8 ; Set a counter in X to copy 9 bytes from K%+0 to K%+8 .main44 LDA K%,X ; Load the X-th byte of K% and store in the X-th byte STA INWK,X ; of the INWK workspace DEX ; Decrement the loop counter BPL main44 ; Loop back for the next byte until we have copied the ; all 9 bytes of coordinate data from K% to INWK ; Next we rotate the orientation vectors LDX #5 ; Set a counter in X to swap each of the six bytes of ; the orientation vectors (the comments below are for ; when X = 0, in which we swap the x_lo bytes) .main45 LDY INWK+9,X ; Set Y to nosev_x_lo LDA INWK+15,X ; Set nosev_x_lo = roofv_x_lo STA INWK+9,X LDA INWK+21,X ; Set roofv_x_lo = sidev_x_lo STA INWK+15,X STY INWK+21,X ; Set sidev_x_lo = Y = nosev_x_lo DEX ; Decrement the loop counter BPL main45 ; Loop back until we have swapped all six bytes of each ; orientation vector JSR SpawnSpaceStation ; Now we check to see if we are close enough to the ; rotated planet, and if so, add a new space station to ; our local bubble of universe .MA23S JMP MA23 ; Jump to MA23 to skip the following planet and sun ; altitude checks
Name: Main flight loop (Part 15 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Perform altitude checks with the planet and sun and process fuel scooping if appropriate Deep dive: Program flow of the main game loop Scheduling tasks with the main loop counter The NES combat demo The Trumbles mission
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Perform an altitude check with the planet (every 16 iterations of the main loop, on iterations 10 and 20 of each 32) * Perform an altitude check with the sun and process fuel scooping (every 32 iterations of the main loop, on iteration 20 of each 32)
.MA22 LDA MJ ; If we are in witchspace, jump down to MA23S to skip BNE MA23S ; the following, as there are no planets or suns to ; bump into in witchspace .MA93 LDA demoInProgress ; If the demo is not in progress, jump to main46 to skip BEQ main46 ; the following ; If we get here then the demo is in progress, so now we ; check to see if we have destroyed all the demo ships LDA JUNK ; Set Y to the number of pieces of space junk (in JUNK) CLC ; plus the number of missiles (in MANY+1) ADC MANY+1 TAY LDA FRIN+2,Y ; There are Y non-ship items in the bubble, so if slot BNE main46 ; Y+2 is not empty (given that the first two slots are ; the planet and sun), then this means there is at least ; one ship in the bubble along with the junk and ; missiles, so jump to main46 to skip the following as ; we haven't yet destroyed all the ships in the combat ; practice demo LDA #1 ; If we get here then we have destroyed all the ships in JMP ShowScrollText_b6 ; the demo, so jump to ShowScrollText with A = 1 to show ; the results of combat practice, returning from the ; subroutine using a tail call .main46 LDA MCNT ; Fetch the main loop counter and calculate MCNT mod 32, AND #31 ; which tells us the position of this loop in each block ; of 32 iterations CMP #10 ; If this is the tenth or twentieth iteration in this BEQ main47 ; block of 32, do the following, otherwise jump to MA29 CMP #20 ; to skip the planet altitude check and move on to the BNE MA29 ; sun distance check .main47 LDA #80 ; If our energy bank status in ENERGY is >= 80, skip CMP ENERGY ; printing the following message (so the message is BCC main48 ; only shown if our energy is low) LDA #100 ; Print recursive token 100 ("ENERGY LOW{beep}") as an JSR MESS ; in-flight message LDY #7 ; Call the NOISE routine with Y = 7 to make a beep to JSR NOISE ; indicate low energy .main48 JSR CheckAltitude ; Perform an altitude check with the planet, ending the ; game if we hit the ground JMP MA23 ; Jump to MA23 to skip to the next section .MA28 JMP DEATH ; If we get here then we just crashed into the planet ; or got too close to the sun, so jump to DEATH to start ; the funeral preparations and return from the main ; flight loop using a tail call .MA29 CMP #15 ; If this is the 15th iteration in this block of 32, BNE MA33 ; do the following, otherwise jump to MA33 to skip the ; docking computer manoeuvring LDA auto ; If auto is zero, then the docking computer is not BEQ MA23 ; activated, so jump to MA33 to skip the ; docking computer manoeuvring LDA #123 ; Set A = 123 and jump down to MA34 to print token 123 BNE MA34 ; ("DOCKING COMPUTERS ON") as an in-flight message .MA33 AND #15 ; If this is the 6th iteration in this block of 16, CMP #6 ; do the following, otherwise jump to MA23 to skip the BNE MA23 ; sun altitude check LDA #30 ; Set CABTMP to 30, the cabin temperature in deep space STA CABTMP ; (i.e. one notch on the dashboard bar) LDA SSPR ; If we are inside the space station safe zone, jump to BNE MA23 ; MA23 to skip the following, as we can't have both the ; sun and space station at the same time, so we clearly ; can't be flying near the sun LDY #NIK% ; Set Y to NIK%+4, which is the offset in K% for the ; sun's data block, as the second block at K% is ; reserved for the sun (or space station) JSR MAS2 ; Call MAS2 to calculate the largest distance to the BNE MA23 ; sun in any of the three axes, and if it's non-zero, ; jump to MA23 to skip the following, as we are too far ; from the sun for scooping or temperature changes JSR MAS3 ; Set A = x_hi^2 + y_hi^2 + z_hi^2, so using Pythagoras ; we now know that A now contains the square of the ; distance between our ship (at the origin) and the ; heart of the sun at (x_hi, y_hi, z_hi) EOR #%11111111 ; Invert A, so A is now small if we are far from the ; sun and large if we are close to the sun, in the ; range 0 = far away to $FF = extremely close, ouch, ; hot, hot, hot! ADC #30 ; Add the minimum cabin temperature of 30, so we get ; one of the following: ; ; * If the C flag is clear, A contains the cabin ; temperature, ranging from 30 to 255, that's hotter ; the closer we are to the sun ; ; * If the C flag is set, the addition has rolled over ; and the cabin temperature is over 255 STA CABTMP ; Store the updated cabin temperature BCS MA28 ; If the C flag is set then jump to MA28 to die, as ; our temperature is off the scale CMP #224 ; If the cabin temperature < 224 then jump to MA23 to BCC MA23 ; skip fuel scooping, as we aren't close enough CMP #240 ; If the cabin temperature < 240 then jump to nokilltr BCC nokilltr ; as the heat isn't high enough to kill Trumbles LDA TRIBBLE+1 ; If TRIBBLE(1 0) = 0 then there are no Trumbles in the ORA TRIBBLE ; hold, so jump to nokilltr to skip the following BEQ nokilltr LSR TRIBBLE+1 ; Halve the number of Trumbles in TRIBBLE(1 0) as the ROR TRIBBLE ; cabin temperature is high enough to kill them off ; (this will eventually bring the number down to zero) LDY #31 ; Call the NOISE routine with Y = 31 to make the sound JSR NOISE ; of Trumbles being killed off by the heat of the sun .nokilltr LDA BST ; If we don't have fuel scoops fitted, jump to MA23 to BEQ MA23 ; skip fuel scooping, as we can't scoop without fuel ; scoops LDA DELT4+1 ; We are now successfully fuel scooping, so it's time BEQ MA23 ; to work out how much fuel we're scooping. Fetch the ; high byte of DELT4, which contains our current speed ; divided by 4, and if it is zero, jump to BA23 to skip ; skip fuel scooping, as we can't scoop fuel if we are ; not moving LSR A ; If we are moving, halve A to get our current speed ; divided by 8 (so it's now a value between 1 and 5, as ; our speed is normally between 1 and 40). This gives ; us the amount of fuel that's being scooped in A, so ; the faster we go, the more fuel we scoop, and because ; the fuel levels are stored as 10 * the fuel in light ; years, that means we just scooped between 0.1 and 0.5 ; light years of free fuel ADC QQ14 ; Set A = A + the current fuel level * 10 (from QQ14) CMP #70 ; If A > 70 then set A = 70 (as 70 is the maximum fuel BCC P%+4 ; level, or 7.0 light years) LDA #70 STA QQ14 ; Store the updated fuel level in QQ14 BCS MA23 ; If A >= 70, jump to BA23 to skip fuel scooping, as the ; fuel tanks are already full JSR MakeScoopSound ; Make the sound of the fuel scoops working JSR SetSelectionFlags ; Set the selected system flags for the new system and ; update the icon bar if required LDA #160 ; Set A to token 160 ("FUEL SCOOPS ON") .MA34 JSR MESS ; Print the token in A as an in-flight message
Name: Main flight loop (Part 16 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Call stardust routine Deep dive: Program flow of the main game loop
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Jump to the stardust routine if we are in a space view * Return from the main flight loop
.MA23 LDA QQ11 ; If this is not the space view (i.e. QQ11 is non-zero) BNE MA232 ; then jump to MA232 to return from the main flight loop ; (as MA232 is an RTS) JMP STARS_b1 ; This is a space view, so jump to the STARS routine to ; process the stardust, and return from the main flight ; loop using a tail call
Name: ChargeShields [Show more] Type: Subroutine Category: Flight Summary: Charge the shields and energy banks
Context: See this subroutine on its own page References: This subroutine is called as follows: * InSystemJump calls ChargeShields * Main flight loop (Part 13 of 16) calls ChargeShields
.ChargeShields SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDX ENERGY ; Fetch our ship's energy levels and skip to b if bit 7 BPL b ; is not set, i.e. only charge the shields from the ; energy banks if they are at more than 50% charge LDX ASH ; Call SHD to recharge our aft shield and update the JSR SHD ; shield status in ASH STX ASH LDX FSH ; Call SHD to recharge our forward shield and update JSR SHD ; the shield status in FSH STX FSH .b SEC ; Set A = ENERGY + ENGY + 1, so our ship's energy LDA ENGY ; level goes up by 2 if we have an energy unit fitted, ADC ENERGY ; otherwise it goes up by 1 BCS paen1 ; If the value of A did not overflow (the maximum STA ENERGY ; energy level is $FF), then store A in ENERGY .paen1 RTS ; Return from the subroutine
Name: CheckAltitude [Show more] Type: Subroutine Category: Flight Summary: Perform an altitude check with the planet, ending the game if we hit the ground
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 15 of 16) calls CheckAltitude * WARP calls CheckAltitude * Main flight loop (Part 16 of 16) calls via MA232

Other entry points: MA232 Contains an RTS
.CheckAltitude LDY #$FF ; Set our altitude in ALTIT to $FF, the maximum STY ALTIT INY ; Set Y = 0 JSR m ; Call m to calculate the maximum distance to the ; planet in any of the three axes, returned in A BNE MA232 ; If A > 0 then we are a fair distance away from the ; planet in at least one axis, so jump to MA232 to skip ; the rest of the altitude check JSR MAS3 ; Set A = x_hi^2 + y_hi^2 + z_hi^2, so using Pythagoras ; we now know that A now contains the square of the ; distance between our ship (at the origin) and the ; centre of the planet at (x_hi, y_hi, z_hi) BCS MA232 ; If the C flag was set by MAS3, then the result ; overflowed (was greater than $FF) and we are still a ; fair distance from the planet, so jump to MA232 as we ; haven't crashed into the planet SBC #36 ; Subtract 36 from x_hi^2 + y_hi^2 + z_hi^2 ; ; When we do the 3D Pythagoras calculation, we only use ; the high bytes of the coordinates, so that's x_hi, ; y_hi and z_hi and ; ; The planet radius is (0 96 0), as defined in the ; PLANET routine, so the high byte is 96 ; ; When we square the coordinates above and add them, ; the result gets divided by 256 (otherwise the result ; wouldn't fit into one byte), so if we do the same for ; the planet's radius, we get: ; ; 96 * 96 / 256 = 36 ; ; So for the planet, the equivalent figure to test the ; sum of the _hi bytes against is 36, so A now contains ; the high byte of our altitude above the planet ; surface, squared BCC MA282 ; If A < 0 then jump to MA282 as we have crashed into ; the planet STA R ; Set (R Q) = (A Q) SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 JSR LL5 ; We are getting close to the planet, so we need to ; work out how close. We know from the above that A ; contains our altitude squared, so we store A in R ; and call LL5 to calculate: ; ; Q = SQRT(R Q) = SQRT(A Q) ; ; Interestingly, Q doesn't appear to be set to 0 for ; this calculation, so presumably this doesn't make a ; difference LDA Q ; Store the result in ALTIT, our altitude STA ALTIT BNE MA232 ; If our altitude is non-zero then we haven't crashed, ; so jump to MA232 to skip to the next section .MA282 JMP DEATH ; If we get here then we just crashed into the planet ; or got too close to the sun, so jump to DEATH to start ; the funeral preparations and return from the main ; flight loop using a tail call .MA232 RTS ; Return from the subroutine
Name: Main flight loop (Part 1 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Seed the random number generator Deep dive: Program flow of the main game loop Generating random numbers
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main game loop (Part 2 of 6) calls via M%

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Seed the random number generator
Other entry points: M% The entry point for the main flight loop
.M% LDA QQ11 ; If this is not the space view, jump to main1 to skip BNE main1 ; the following, as we only need to flip the drawing ; bitplane for animating the space view JSR FlipDrawingPlane ; Flip the drawing bitplane so we draw into the bitplane ; that isn't visible on-screen .main1 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA K% ; We want to seed the random number generator with a ; pretty random number, so fetch the contents of K%, ; which is the x_lo coordinate of the planet. This value ; will be fairly unpredictable, so it's a pretty good ; candidate EOR nmiTimerLo ; EOR the value of K% with the low byte of the NMI ; timer, which gets updated by the NMI interrupt ; routine, so this will be fairly unpredictable too STA RAND ; Store the seed in the first byte of the four-byte ; random number seed that's stored in RAND
Name: Main flight loop (Part 2 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Calculate the alpha and beta angles from the current pitch and roll of our ship Deep dive: Program flow of the main game loop Pitching and rolling
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Calculate the alpha and beta angles from the current pitch and roll Here we take the current rate of pitch and roll, as set by the controller, and convert them into alpha and beta angles that we can use in the matrix functions to rotate space around our ship. The alpha angle covers roll, while the beta angle covers pitch (there is no yaw in this version of Elite). The angles are in radians, which allows us to use the small angle approximation when moving objects in the sky (see the MVEIT routine for more on this). Also, the signs of the two angles are stored separately, in both the sign and the flipped sign, as this makes calculations easier.
; We now check to see if we are allowed to make an ; in-system jump, and set the allowInSystemJump flag ; accordingly LDA auto ; If auto is zero, then the docking computer is not BEQ main2 ; activated, so jump to main2 to move on to the next ; check CLC ; The docking computer is activated, so clear the C flag ; to put into bit 7 of allowInSystemJump, so the ; Fast-forward button is enabled (so we can use it to ; speed up docking) BCC main5 ; Jump to main5 to update allowInSystemJump (this BCC is ; effectively a JMP as we know the C flag is clear) .main2 LDA MJ ; If MJ is zero then we are not in witchspace, so jump BEQ main3 ; to main3 to move on to the next check SEC ; We are in witchspace, so set the C flag to put into ; bit 7 of allowInSystemJump, so in-system jumps are not ; allowed BCS main5 ; Jump to main5 to update allowInSystemJump (this BCS is ; effectively a JMP as we know the C flag is set) .main3 ; If we get here then the docking computer is not ; activated and we are not in witchspace LDA allowInSystemJump ; If bit 7 of allowInSystemJump is clear then jump to BPL main4 ; to main4 to check whether we are too close to the ; planet or sun to make an in-system jump LDA #176 ; Set A = 176 to use as the distance to check in the ; following call to CheckJumpSafety ; ; The default distance checks use A = 128, so this makes ; the distance checks more stringent, as bit 7 of ; allowInSystemJump is set, so we are need to be further ; away from the danger to make a safe jump JSR CheckJumpSafety+2 ; Check whether we are far enough away from the planet ; and sun to be able to do an in-system (fast-forward) ; jump, returning the result in the C flag (clear means ; we can jump, set means we can't), using the distance ; in A JMP main5 ; Jump to main5 to update allowInSystemJump with the ; result of the distance check .main4 JSR CheckJumpSafety ; Check whether we are far enough away from the planet ; and sun to be able to do an in-system (fast-forward) ; jump, returning the result in the C flag (clear means ; we can jump, set means we can't) .main5 ROR allowInSystemJump ; Rotate the C flag into bit 7 of allowInSystemJump, so ; in-system jumps are enabled or disabled accordingly LDX JSTX ; Set X to the current rate of roll in JSTX LDY numberOfPilots ; Set Y to the configured number of pilots, so the ; following checks controller 1 when only one pilot ; is playing, or controller 2 when two pilots are ; playing LDA controller1Left,Y ; If any of the left or right buttons are being pressed ORA controller1Right,Y ; on the controller that's responsible for steering, ORA KY3 ; jump to main6 to skip the following code so we do not ORA KY4 ; apply roll damping BMI main6 LDA #16 ; Apply damping to the roll rate (if enabled), so the JSR cntr ; roll rate in X creeps towards the centre by 16 .main6 ; The roll rate in JSTX increases if we press the right ; button (and the RL indicator on the dashboard goes to ; the right) ; ; This rolls our ship to the right (clockwise), but we ; actually implement this by rolling everything else ; to the left (anti-clockwise), so a positive roll rate ; in JSTX translates to a negative roll angle alpha TXA ; Set A and Y to the roll rate but with the sign bit EOR #%10000000 ; flipped (i.e. set them to the sign we want for alpha) TAY AND #%10000000 ; Extract the flipped sign of the roll rate and store STA ALP2 ; in ALP2 (so ALP2 contains the sign of the roll angle ; alpha) STX JSTX ; Update JSTX with the damped value that's still in X EOR #%10000000 ; Extract the correct sign of the roll rate and store STA ALP2+1 ; in ALP2+1 (so ALP2+1 contains the flipped sign of the ; roll angle alpha) TYA ; Set A to the roll rate but with the sign bit flipped BPL P%+7 ; If the value of A is positive, skip the following ; three instructions EOR #%11111111 ; A is negative, so change the sign of A using two's CLC ; complement so that A is now positive and contains ADC #1 ; the absolute value of the roll rate, i.e. |JSTX| LSR A ; Divide the (positive) roll rate in A by 4 LSR A STA ALP1 ; Store A in ALP1, so we now have: ; ; ALP1 = |JSTX| / 8 if |JSTX| < 32 ; ; ALP1 = |JSTX| / 4 if |JSTX| >= 32 ; ; This means that at lower roll rates, the roll angle is ; reduced closer to zero than at higher roll rates, ; which gives us finer control over the ship's roll at ; lower roll rates ; ; Because JSTX is in the range -127 to +127, ALP1 is ; in the range 0 to 31 ORA ALP2 ; Store A in ALPHA, but with the sign set to ALP2 (so STA ALPHA ; ALPHA has a different sign to the actual roll rate) LDX JSTY ; Set X to the current rate of pitch in JSTY LDY numberOfPilots ; Set Y to the configured number of pilots, so the ; following checks controller 1 when only one pilot ; is playing, or controller 2 when two pilots are ; playing LDA controller1Up,Y ; If any of the up or down buttons are being pressed ORA controller1Down,Y ; on the controller that's responsible for steering, ORA KY5 ; jump to main6 to skip the following code so we do ORA KY6 ; not apply pitch damping BMI main7 LDA #12 ; Apply damping to the pitch rate (if enabled), so the JSR cntr ; pitch rate in X creeps towards the centre by 12 .main7 TXA ; Set A and Y to the pitch rate but with the sign bit EOR #%10000000 ; flipped TAY AND #%10000000 ; Extract the flipped sign of the pitch rate into A STX JSTY ; Update JSTY with the damped value that's still in X STA BET2+1 ; Store the flipped sign of the pitch rate in BET2+1 EOR #%10000000 ; Extract the correct sign of the pitch rate and store STA BET2 ; it in BET2 TYA ; Set A to the pitch rate but with the sign bit flipped BPL P%+4 ; If the value of A is positive, skip the following ; instruction EOR #%11111111 ; A is negative, so flip the bits ADC #1 ; Add 1 to the (positive) pitch rate, so the maximum ; value is now up to 128 (rather than 127) LSR A ; Divide the (positive) pitch rate in A by 8 LSR A LSR A STA BET1 ; Store A in BET1, so we now have: ; ; BET1 = |JSTY| / 32 if |JSTY| < 48 ; ; BET1 = |JSTY| / 16 if |JSTY| >= 48 ; ; This means that at lower pitch rates, the pitch angle ; is reduced closer to zero than at higher pitch rates, ; which gives us finer control over the ship's pitch at ; lower pitch rates ; ; Because JSTY is in the range -131 to +131, BET1 is in ; the range 0 to 8 ORA BET2 ; Store A in BETA, but with the sign set to BET2 (so STA BETA ; BETA has the same sign as the actual pitch rate)
Name: Main flight loop (Part 3 of 16) [Show more] Type: Subroutine Category: Main loop Summary: Scan for flight keys and process the results Deep dive: Program flow of the main game loop The key logger Splitting the main loop in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The main flight loop covers most of the flight-specific aspects of Elite. This section covers the following: * Scan for flight keys and process the results Flight keys are logged in the key logger at location KY1 onwards, with a non-zero value in the relevant location indicating a key press. See the deep dive on "The key logger" for more details.
.BS2 LDA KY2 ; If both the B and up buttons are being pressed, keep BEQ MA17 ; going, otherwise jump down to MA17 to skip the ; following LDA DELTA ; The "go faster" key is being pressed, so first we CLC ; add 4 to the current speed in DELTA (we also store ADC #4 ; this value in DELTA, though this isn't necessary as STA DELTA ; we are about to do that again) CMP #40 ; If the new speed in A < 40, then this is a valid BCC main8 ; speed, so jump down to main8 to set DELTA to this ; value LDA #40 ; The maximum allowed speed is 40, so set A = 40 .main8 STA DELTA ; Store the updated speed in DELTA .MA17 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA KY1 ; If both the B and down buttons are being pressed, BEQ MA4 ; keep going, otherwise jump down to MA4 to skip the ; following LDA DELTA ; The "slow down" key is being pressed, so subtract 4 SEC ; from the speed in DELTA SBC #4 BEQ main9 ; If the result is zero, jump to main9 to set the speed ; to the minimum value of 1 BCS main10 ; If the subtraction didn't underflow then this is a ; valid speed, so jump down to main10 to set DELTA to ; this value .main9 LDA #1 ; Set A = 1 to use as the minimum speed .main10 STA DELTA ; Store the updated speed in DELTA .MA4 LDA iconBarKeyPress ; Set A to the icon bar key logger entry in ; iconBarKeyPress, which contains the button number of ; the icon bar button (if one has been chosen) CMP #24 ; If the Target Missile button has not been chosen, BNE MA25 ; jump to MA25 to skip the following LDA NOMSL ; If the number of missiles in NOMSL is zero, jump to BEQ MA64S ; MA64 via MA64S to skip the rest of the button checks LDA MSAR ; The "target missile" key is being pressed and we have EOR #$FF ; at least one missile, so flip MSAR between 0 and $FF STA MSAR ; to flip the missile between being disarmed and armed BNE MA20 ; If MSAR is now $FF then the missile is now armed, so ; jump to MA20 to skip the following and process the ; arming of the missile LDY #$6C ; Otherwise we just chose to disarm the missile, so call JSR ABORT ; ABORT to disarm the missile and update the missile ; indicators on the dashboard to the pattern number in ; Y (black indicator = pattern 108) LDY #4 ; Set Y = 4 so the call to NOISE makes a low, long beep ; to indicate the missile is now disarmed .main11 JSR NOISE ; Call the NOISE routine to make the sound in Y (which ; will either be a low, long beep to indicate the ; missile is now disarmed, or a short, high beep to ; indicate that it is looking for a target) JMP MA64 ; Jump to MA64 to skip the rest of the button checks .MA20 LDY #108 ; Set the pattern for the active missile indicator to LDX NOMSL ; 108 (which is a black indicator), so we can flash JSR MSBAR ; it between red and black in the main loop to indicate ; that it is looking for a target LDY #3 ; Set Y = 3 and jump up to main11 to make a short, BNE main11 ; high beep to indicate that it is looking for a target ; (this BNE is effectively a JMP as Y is never zero) .MA25 CMP #25 ; If the Fire Targeted Missile button was chosen on BNE MA24 ; the icon bar, keep going, otherwise jump down to MA24 ; to skip the following LDA MSTG ; If MSTG = $FF then there is no target lock, so jump to BMI MA64S ; MA64 via MA64S to skip the rest of the button checks JSR FRMIS ; The "fire missile" key is being pressed and we have ; a missile lock, so call the FRMIS routine to fire ; the missile JSR UpdateIconBar_b3 ; Update the icon bar to hide the fire button as we have ; just fired a missile .MA64S JMP MA64 ; Jump to MA64 to skip the rest of the button checks .MA24 CMP #26 ; If the Energy Bomb button is being pressed, keep BNE MA76 ; going, otherwise jump down to MA76 to skip the ; following LDA BOMB ; If we already set off our energy bomb, then BOMB is BMI MA64S ; negative, so this jumps to MA64 via MA64S to skip the ; rest of the button checks when our energy bomb is ; already going off ASL BOMB ; The "energy bomb" key is being pressed, so double ; the value in BOMB. If we have an energy bomb fitted, ; BOMB will contain $7F (%01111111) before this shift ; and will contain $FE (%11111110) after the shift; if ; we don't have an energy bomb fitted, BOMB will still ; contain 0. The bomb explosion is dealt with in the ; MAL1 routine below - this just registers the fact that ; we've set the bomb ticking BEQ MA64S ; If BOMB now contains 0, then the bomb is not going off ; any more (or it never was), so jump to MA64 via MA64S ; to skip the rest of the button checks LDA #$28 ; Set hiddenColour to $28, which is green-brown, so this STA hiddenColour ; reveals pixels that use the (no-longer) hidden colour ; in palette 0 LDY #8 ; Call the NOISE routine with Y = 8 to make the sound of JSR NOISE ; the energy bomb going off JMP MA64 ; Jump to MA64 to skip the rest of the button checks .MA76 CMP #27 ; If the Escape Pod button is not being pressed, jump to BNE noescp ; noescp to skip the following LDX ESCP ; If we do not have an escape pod fitted, jump to MA64 BEQ MA64 ; to skip the rest of the button checks LDA MJ ; If we are in witchspace, we can't launch our escape BNE MA64 ; pod, so jump down to MA64 to skip the rest of the ; button checks JMP ESCAPE ; The button is being pressed to launch an escape pod ; and we have an escape pod fitted, so jump to ESCAPE to ; launch it, and exit the main flight loop using a tail ; call .noescp CMP #12 ; If the Fast-forward button is not being pressed, jump BNE main12 ; to main12 to skip the following LDA allowInSystemJump ; If either of bits 6 or 7 of allowInSystemJump are set AND #%11000000 ; then there is something in the vicinity that is BNE MA64 ; preventing in-system jumps, so jump to MA64 to skip ; the rest of the button checks JSR WARP ; Call WARP to process an in-system jump JMP MA64 ; Jump to MA64 to skip the rest of the button checks .main12 CMP #23 ; If the E.C.M. button is not being pressed, jump to BNE MA64 ; to MA64 to skip the following LDA ECM ; If we do not have an E.C.M. fitted, jump to MA64 to BEQ MA64 ; skip the following LDA ECMA ; If ECMA is non-zero, that means an E.C.M. is already BNE MA64 ; operating and is counting down (this can be either ; our E.C.M. or an opponent's), so jump down to MA64 to ; skip the following (as we can't have two E.C.M. ; systems operating at the same time) DEC ECMP ; The E.C.M. button is being pressed and nobody else ; is operating their E.C.M., so decrease the value of ; ECMP to make it non-zero, to denote that our E.C.M. ; is now on JSR ECBLB2 ; Call ECBLB2 to light up the E.C.M. indicator bulb on ; the dashboard, set the E.C.M. countdown timer to 32, ; and start making the E.C.M. sound .MA64 JSR SetupPPUForIconBar ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 .MA68 LDA #0 ; Set LAS = 0, to switch the laser off while we do the STA LAS ; following logic STA DELT4 ; Take the 16-bit value (DELTA 0) - i.e. a two-byte LDA DELTA ; number with DELTA as the high byte and 0 as the low LSR A ; byte - and divide it by 4, storing the 16-bit result ROR DELT4 ; in DELT4(1 0). This has the effect of storing the LSR A ; current speed * 64 in the 16-bit location DELT4(1 0) ROR DELT4 STA DELT4+1 LDA LASCT ; If LASCT is zero and this is the space view, keep ORA QQ11 ; going, otherwise the laser is a pulse laser that is BNE MA3 ; between pulses, or we aren't showing laser fire, so ; jump down to MA3 to skip the following LDA KY7 ; If the A button is being pressed, keep going, BPL MA3 ; otherwise jump down to MA3 to skip the following LDA GNTMP ; If the laser temperature >= 242 then the laser has CMP #242 ; overheated, so jump down to MA3 to skip the following BCS MA3 LDX VIEW ; If the current space view has a laser fitted (i.e. the LDA LASER,X ; laser power for this view is greater than zero), then BEQ MA3 ; keep going, otherwise jump down to MA3 to skip the ; following BMI main13 ; If the current laser power is 128 or greater, then ; this is a beam or military laser, so jump to main13 ; to skip the following two instructions BIT KY7 ; If bit 6 of KY7 is set, then the A button was being BVS MA3 ; pressed in the previous VBlank and it is still being ; pressed now, so jump down to MA3 to skip the following ; so that lower power lasers only fire every other ; VBlank when the A button is held down .main13 ; If we get here, then the "fire" button is being ; pressed, our laser hasn't overheated and isn't already ; being fired, and we actually have a laser fitted to ; the current space view, so it's time to hit me with ; those laser beams PHA ; Store the current view's laser power on the stack AND #%01111111 ; Set LAS and LAS2 to bits 0-6 of the laser power STA LAS STA LAS2 ; We now set Y to the correct sound to pass to the NOISE ; routine to make the sound of the laser firing LDY #18 ; Set Y = 18 to use as the sound number for a pulse ; laser PLA ; Set A to the current view's laser power, which we PHA ; stored on the stack above (and leave the value on ; the stack BMI main15 ; If A >= 128, jump to main15 to check whether this is ; a beam laser or a military laser CMP #Mlas ; If A is not the power for a mining laser, jump to BNE main14 ; main14 to keep Y = 18 LDY #16 ; This is a mining laser, so set Y = 16 to use as the ; sound number .main14 BNE main17 ; Jump to main17 to make the sound in Y (this BNE is ; effectively a JMP as Y is never zero) .main15 ; If we get here then this is either a beam laser or a ; military laser CMP #Armlas ; If this is a military laser, jump to main16 to set BEQ main16 ; Y = 15 LDY #17 ; This is a beam laser, so set Y = 17 to use as the ; sound number EQUB $2C ; Skip the next instruction by turning it into ; $2C $A0 $0F, or BIT $0FA0, which does nothing apart ; from affect the flags .main16 LDY #15 ; This is a military laser, so set Y = 15 to use as the ; sound number .main17 JSR NOISE ; Call the NOISE routine to make the sound in Y, which ; will be one of 15 (military laser), 16 (mining laser), ; 17 (beam laser) or 18 (pulse laser) JSR LASLI ; Call LASLI to draw the laser lines PLA ; Restore the current view's laser power into A BPL ma1 ; If the laser power has bit 7 set, then it's an "always ; on" laser rather than a pulsing laser, so keep going, ; otherwise jump down to ma1 to skip the following ; instruction LDA #0 ; This is an "always on" laser (i.e. a beam laser or a ; military laser), so set A = 0, which will be stored in ; LASCT to denote that this is not a pulsing laser .ma1 AND #%11101111 ; LASCT will be set to 0 for beam lasers, and to the STA LASCT ; laser power AND %11101111 for pulse lasers, which ; comes to comes to 8 (as pulse lasers have a power ; of 24) .MA3 JSR FlightLoop4To16 ; Display in-flight messages, call parts 4 to 12 of the ; main flight loop for each ship slot, and finish off ; with parts 13 to 16 of the main flight loop ; Fall through into DrawSpaceViewInNMI to tell the NMI ; handler to send the updated space view to the PPU
Name: DrawSpaceViewInNMI [Show more] Type: Subroutine Category: Drawing the screen Summary: Configure the NMI handler to draw the space view Deep dive: Drawing vector graphics using NES tiles Splitting the main loop in the NES version
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.DrawSpaceViewInNMI LDA QQ11 ; If this is not the space view, jump to spvw3 to skip BNE spvw3 ; the following, as we only need to send the drawing ; bitplane to the PPU and update the dashboard in the ; space view JSR SetupPPUForIconBar ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 ; ; If bit 7 of setupPPUForIconBar is set, then this also ; affects the C flag as follows: ; ; * If bit 6 of PPU_STATUS is clear then the C flag ; is set to bit 7 of PPU_STATUS (which is set if ; VBlank has started, clear otherwise) ; ; * If bit 6 of PPU_STATUS is set (sprite 0 has been ; hit) then the C flag is cleared ; ; So the C flag is set if we are in VBlank, or if we ; aren't in VBlank but the PPU has not yet reached the ; icon bar (i.e. it hasn't started drawing the ; dashboard yet) ; ; In other words, the C flag is clear if the PPU is ; currently drawing the dashboard, and set if it is not LDA drawingBitplane ; If the drawing bitplane is 1, jump to spvw1 to send BNE spvw1 ; the drawing plane to the PPU without updating the ; whole dashboard first ; If we get here then the drawing bitplane is 0, so now ; we do various checks to determine whether to update ; the whole dashboard before sending to the PPU (as ; updating the whole dashboard takes time) LDA sendDashboardToPPU ; Flip the value of sendDashboardToPPU between 0 and $FF EOR #$FF ; so it flips every time we run the main loop with the STA sendDashboardToPPU ; drawing bitplane set to 0 (so we only update the whole ; dashboard every other iteration that the drawing ; bitplane is set to 0) BMI spvw2 ; If bit 7 of the result is set (so sendDashboardToPPU ; is now $FF), jump to spvw2 to update the whole ; dashboard before sending it to the PPU LDA KY1 ; If the B button is being pressed with either the up or ORA KY2 ; down button (to change our speed), or the C flag is ROR A ; set, jump to spvw2 to update the whole dashboard BNE spvw2 ; before sending it to the PPU ; ; This makes the speed indicator react more quickly to ; speed changes, as it triggers an update of the whole ; dashboard when we are changing speed, and it also ; ensures we do not update the whole dashboard if the ; PPU is currently in the process of drawing it .spvw1 ; If we get here then either the drawing bitplane is 1, ; or it's 0 and the following are true: ; ; * sendDashboardToPPU has just flipped to 0 ; ; * The change speed buttons are not being pressed ; ; * The C flag is clear, so the PPU is currently ; drawing the dashboard ; ; So we can get away with not updating the dashboard ; for two iterations out of four (i.e. when the drawing ; bitplane is 1), plus an extra iteration (i.e. every ; other iteration with bitplane 0) but only if the speed ; buttons aren't being pressed or the PPU is currently ; drawing the dashboard JSR DrawBitplaneInNMI ; Configure the NMI to send the drawing bitplane to the ; PPU after drawing the box edges and setting the next ; free tile number JSR COMPAS ; Call COMPAS to update the compass JMP DrawPitchRollBars ; Update the pitch and roll bars on the dashboard, ; returning from the subroutine using a tail call .spvw2 LDA #%10001000 ; Set the bitplane flags for the drawing bitplane to the JSR SetDrawPlaneFlags ; following: ; ; * Bit 2 clear = send tiles up to configured numbers ; * Bit 3 set = clear buffers after sending data ; * Bit 4 clear = we've not started sending data yet ; * Bit 5 clear = we have not yet sent all the data ; * Bit 6 clear = only send pattern data to the PPU ; * Bit 7 set = send data to the PPU ; ; Bits 0 and 1 are ignored and are always clear ; ; This configures the NMI to send pattern data for the ; drawing bitplane to the PPU during VBlank JSR COMPAS ; Call COMPAS to update the compass JSR DrawPitchRollBars ; Update the pitch and roll bars on the dashboard JSR DIALS_b6 ; Call DIALS to update the dashboard LDX drawingBitplane ; Set X to the drawing bitplane LDA bitplaneFlags,X ; Set bit 6 of the flags for the drawing bitplane, so ORA #%01000000 ; we send both nametable and pattern table data for STA bitplaneFlags,X ; bitplane X to the PPU in the NMI handler RTS ; Return from the subroutine .spvw3 ; We jump here if this is not the space view, with the ; view type in A CMP #$98 ; If this is not the Status Mode screen, jump to spvw6 BNE spvw6 ; to skip the following, as we can only flash the ; commander image background when it's on-screen in the ; Status Mode view JSR GetStatusCondition ; Set X to our ship's status condition CPX previousCondition ; If our condition hasn't changed, jump to spvw4 to BEQ spvw4 ; skip the following instruction JSR STATUS ; Call STATUS to refresh the Status Mode screen, so our ; status updates to show the new condition .spvw4 LDX previousCondition ; Set X to the previous status condition CPX #3 ; If the previous status condition was not red, jump to BNE spvw5 ; spvw5 to show the alert colour for the previous ; condition LDA nmiCounter ; If nmiCounter div 32 is odd (which will happen half AND #32 ; the time, and for 32 VBlanks in a row), jump to spvw5 BNE spvw5 ; to skip the following ; We get here if the previous condition was red, but ; only for every other block of 32 VBlanks, so this ; flashes the commander image background on and off with ; a period of 32 VBlanks INX ; Increment X to 4, which will make the background of ; the commander image flash between the top two alert ; colours (i.e. light red and dark red) .spvw5 LDA alertColours,X ; Change the palette so the visible colour is set to the STA visibleColour ; alert colour for our status condition .spvw6 RTS ; Return from the subroutine
Name: SPIN [Show more] Type: Subroutine Category: Universe Summary: Randomly spawn cargo from a destroyed ship
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 11 of 16) calls SPIN * Main flight loop (Part 11 of 16) calls via SPIN2

Arguments: Y The type of cargo to consider spawning (typically #PLT or #OIL)
Other entry points: SPIN2 Remove any randomness: spawn cargo of a specific type (given in X), and always spawn the number given in A
.SPIN JSR DORND ; Fetch a random number, and jump to oh if it is BPL oh ; positive (50% chance) TYA ; Copy the cargo type from Y into A and X TAX LDY #0 ; Set Y = 0 to use as an index into the ship's blueprint ; in the call to GetShipBlueprint STA CNT ; Store the random number in CNT JSR GetShipBlueprint ; Fetch the first byte of the hit ship's blueprint, ; which determines the maximum number of bits of ; debris shown when the ship is destroyed AND CNT ; AND with the random number we fetched above AND #15 ; Reduce the random number in A to the range 0-15 .SPIN2 STA CNT ; Store the result in CNT, so CNT contains a random ; number between 0 and the maximum number of bits of ; debris that this ship will release when destroyed ; (to a maximum of 15 bits of debris) .spl DEC CNT ; Decrease the loop counter BMI oh ; We're going to go round a loop using CNT as a counter ; so this checks whether the counter was zero and jumps ; to oh when it gets there (which might be straight ; away) LDA #0 ; Call SFS1 to spawn the specified cargo from the now JSR SFS1 ; deceased parent ship, giving the spawned canister an ; AI flag of 0 (no AI, no E.C.M., non-hostile) JMP spl ; Loop back to spawn the next bit of random cargo
Name: HideHiddenColour [Show more] Type: Subroutine Category: Drawing the screen Summary: Set the hidden colour to black, so that pixels in this colour in palette 0 are invisible
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 13 of 16) calls HideHiddenColour * RES2 calls HideHiddenColour * SPIN calls via oh

Returns: A A is set to 15
Other entry points: oh Contains an RTS
.HideHiddenColour LDA #$0F ; Set hiddenColour to $0F, which is black, so this hides STA hiddenColour ; any pixels that use the hidden colour in palette 0 .oh RTS ; Return from the subroutine
Name: scacol [Show more] Type: Variable Category: Drawing ships Summary: Ship colours on the scanner Deep dive: The elusive Cougar
Context: See this variable on its own page References: This variable is used as follows: * NWSHP uses scacol
.scacol EQUB 0 EQUB 3 ; Missile EQUB 0 ; Coriolis space station EQUB 1 ; Escape pod EQUB 1 ; Alloy plate EQUB 1 ; Cargo canister EQUB 1 ; Boulder EQUB 1 ; Asteroid EQUB 1 ; Splinter EQUB 2 ; Shuttle EQUB 2 ; Transporter EQUB 2 ; Cobra Mk III EQUB 2 ; Python EQUB 2 ; Boa EQUB 2 ; Anaconda EQUB 1 ; Rock hermit (asteroid) EQUB 2 ; Viper EQUB 2 ; Sidewinder EQUB 2 ; Mamba EQUB 2 ; Krait EQUB 2 ; Adder EQUB 2 ; Gecko EQUB 2 ; Cobra Mk I EQUB 2 ; Worm EQUB 2 ; Cobra Mk III (pirate) EQUB 2 ; Asp Mk II EQUB 2 ; Python (pirate) EQUB 2 ; Fer-de-lance EQUB 2 ; Moray EQUB 0 ; Thargoid EQUB 3 ; Thargon EQUB 2 ; Constrictor EQUB 255 ; Cougar EQUB 0 ; This byte appears to be unused EQUD 0 ; These bytes appear to be unused
Name: SetAXTo15 [Show more] Type: Subroutine Category: Utility routines Summary: An unused routine that sets A and X to 15
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.SetAXTo15 LDA #15 ; Set A = 15 TAX ; Set X = 15 RTS ; Return from the subroutine
Name: PrintCombatRank [Show more] Type: Subroutine Category: Status Summary: Print the current combat rank
Context: See this subroutine on its own page References: This subroutine is called as follows: * STATUS calls PrintCombatRank

This routine is based on part of the STATUS routine from the original source, so I have kept the original st3 and st4 labels.
.PrintCombatRank LDA #16 ; Print recursive token 130 ("RATING:") followed by JSR TT68 ; a colon LDA languageNumber ; If bit 0 of languageNumber is clear then the chosen AND #%00000001 ; language is not English, so skip the following BEQ P%+5 ; instruction (as the screen has a different layout in ; the other languages) JSR TT162 ; Print a space LDA TALLY+1 ; Fetch the high byte of the kill tally, and if it is BNE st4 ; not zero, then we have more than 256 kills, so jump ; to st4 to work out whether we are Competent, ; Dangerous, Deadly or Elite ; Otherwise we have fewer than 256 kills, so we are one ; of Harmless, Mostly Harmless, Poor, Average or Above ; Average TAX ; Set X to 0 (as A is 0) LDX TALLY ; Set X to the low byte of the kill tally CPX #0 ; Increment A if X >= 0 ADC #0 CPX #2 ; Increment A if X >= 2 ADC #0 CPX #8 ; Increment A if X >= 8 ADC #0 CPX #24 ; Increment A if X >= 24 ADC #0 CPX #44 ; Increment A if X >= 44 ADC #0 CPX #130 ; Increment A if X >= 130 ADC #0 TAX ; Set X to A, which will be as follows: ; ; * 1 (Harmless) when TALLY = 0 or 1 ; ; * 2 (Mostly Harmless) when TALLY = 2 to 7 ; ; * 3 (Poor) when TALLY = 8 to 23 ; ; * 4 (Average) when TALLY = 24 to 43 ; ; * 5 (Above Average) when TALLY = 44 to 129 ; ; * 6 (Competent) when TALLY = 130 to 255 ; ; Note that the Competent range also covers kill counts ; from 256 to 511, but those are covered by st4 below .st3 TXA ; Store the combat rank in X on the stack PHA LDA languageNumber ; If bits 0 and 2 of languageNumber are clear then the AND #%00000101 ; chosen language is not English or French, so skip BEQ P%+8 ; the following two instructions (as the screen has a ; different layout in German) JSR TT162 ; Print two spaces JSR TT162 PLA ; Set A to the combat rank we stored on the stack above CLC ; Print recursive token 135 + A, which will be in the ADC #21 ; range 136 ("HARMLESS") to 144 ("---- E L I T E ----") JMP plf ; followed by a newline, returning from the subroutine ; using a tail call .st4 ; We call this from above with the high byte of the ; kill tally in A, which is non-zero, and want to return ; with the following in X, depending on our rating: ; ; Competent = 6 ; Dangerous = 7 ; Deadly = 8 ; Elite = 9 ; ; The high bytes of the top tier ratings are as follows, ; so this a relatively simple calculation: ; ; Competent = 1 to 2 ; Dangerous = 2 to 9 ; Deadly = 10 to 24 ; Elite = 25 and up LDX #9 ; Set X to 9 for an Elite rating CMP #25 ; If A >= 25, jump to st3 to print out our rating, as we BCS st3 ; are Elite DEX ; Decrement X to 8 for a Deadly rating CMP #10 ; If A >= 10, jump to st3 to print out our rating, as we BCS st3 ; are Deadly DEX ; Decrement X to 7 for a Dangerous rating CMP #2 ; If A >= 2, jump to st3 to print out our rating, as we BCS st3 ; are Dangerous DEX ; Decrement X to 6 for a Competent rating BNE st3 ; Jump to st3 to print out our rating, as we are ; Competent (this BNE is effectively a JMP as A will ; never be zero)
Name: PrintLegalStatus [Show more] Type: Subroutine Category: Status Summary: Print the current legal status (clean, offender or fugitive)
Context: See this subroutine on its own page References: This subroutine is called as follows: * STATUS calls PrintLegalStatus
.PrintLegalStatus LDA #125 ; Print recursive token 125 ("LEGAL STATUS:) followed JSR spc ; by a space LDA #19 ; Set A to token 133 ("CLEAN") LDY FIST ; Fetch our legal status, and if it is 0, we are clean, BEQ st5 ; so jump to st5 to print "Clean" CPY #40 ; Set the C flag if Y >= 40, so C is set if we have ; a legal status of 40+ (i.e. we are a fugitive) ADC #1 ; Add 1 + C to A, so if C is not set (i.e. we have a ; legal status between 1 and 49) then A is set to token ; 134 ("OFFENDER"), and if C is set (i.e. we have a ; legal status of 50+) then A is set to token 135 ; ("FUGITIVE") .st5 JMP plf ; Print the text token in A (which contains our legal ; status) followed by a newline, returning from the ; subroutine using a tail call
Name: STATUS [Show more] Type: Subroutine Category: Status Summary: Show the Status Mode screen Deep dive: Combat rank
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawSpaceViewInNMI calls STATUS * RedrawCurrentView calls STATUS * TT102 calls STATUS
.wearedocked ; We call this from STATUS below if we are docked LDA #205 ; Print extended token 205 ("DOCKED") and return from JSR DETOK_b2 ; the subroutine using a tail call JSR TT67 ; Print a newline JMP st6+3 ; Jump down to st6+3, to print recursive token 125 and ; continue to the rest of the Status Mode screen .STATUS LDA #$98 ; Clear the screen and set the view type in QQ11 to $98 JSR SetNewViewType ; (Status Mode) JSR SetSelectedSystem ; Set the selected system to the nearest system, if we ; don't already have a selected system LDA #7 ; Move the text cursor to column 7 STA XC LDA #126 ; Print recursive token 126, which prints the top JSR NLIN3 ; four lines of the Status Mode screen: ; ; COMMANDER {commander name} ; ; ; Present System : {current system name} ; Hyperspace System : {selected system name} ; Condition : ; ; and draw a horizontal line at pixel row 19 to box ; in the title JSR GetStatusCondition ; Set X to our ship's status condition STX previousCondition ; Store the status condition in previousCondition, so ; we keep an eye on changes in our condition LDA #230 ; Start off by setting A to token 70 ("GREEN") DEX ; If the status condition in X is 0, then we are docked, BMI wearedocked ; so jump to wearedocked BEQ st6 ; So if X = 0, there are no ships in the vicinity, so ; jump to st6 to print "Green" for our ship's condition LDY ENERGY ; Otherwise we have ships in the vicinity, so we load ; our energy levels into Y CPY #128 ; Set the C flag if Y >= 128, so C is set if we have ; more than half of our energy banks charged ADC #1 ; Add 1 + C to A, so if C is not set (i.e. we have low ; energy levels) then A is set to token 231 ("RED"), ; and if C is set (i.e. we have healthy energy levels) ; then A is set to token 232 ("YELLOW") .st6 JSR plf ; Print the text token in A (which contains our ship's ; condition) followed by a newline LDA languageNumber ; If bit 2 of languageNumber is clear then the chosen AND #%00000100 ; language is not French, so jump to stat1 to skip the BEQ stat1 ; following (as the screen has a different layout in ; French) JSR PrintLegalStatus ; Print the current legal status JSR PrintCombatRank ; Print the current combat rank LDA #5 ; Print control code 5, which prints the next JSR plf ; two lines of the Status Mode screen: ; ; Fuel: {fuel level} Light Years ; Cash: {cash} Cr ; ; followed by a newline JMP stat2 ; Jump to stat2 to skip the following .stat1 JSR PrintCombatRank ; Print the current combat rank LDA #5 ; Print control code 5, which prints the next JSR plf ; two lines of the Status Mode screen: ; ; Fuel: {fuel level} Light Years ; Cash: {cash} Cr ; ; followed by a newline JSR PrintLegalStatus ; Print the current legal status .stat2 LDA #18 ; Print recursive token 132, which prints the next bit JSR PrintTokenCrTab ; of the Status Mode screen: ; ; EQUIPMENT: ; ; followed by a newline and the correct indent for ; Status Mode entries in the chosen language INC YC ; Move the text cursor down one row LDA ESCP ; If we don't have an escape pod fitted (i.e. ESCP is BEQ P%+7 ; zero), skip the following two instructions LDA #112 ; We do have an escape pod fitted, so print recursive JSR PrintTokenCrTab ; token 112 ("ESCAPE POD"), followed by a newline and ; the correct indent for Status Mode entries in the ; chosen language LDA BST ; If we don't have fuel scoops fitted, skip the BEQ P%+7 ; following two instructions LDA #111 ; We do have fuel scoops fitted, so print recursive JSR PrintTokenCrTab ; token 111 ("FUEL SCOOPS"), followed by a newline and ; the correct indent for Status Mode entries in the ; chosen language LDA ECM ; If we don't have an E.C.M. fitted, skip the following BEQ P%+7 ; two instructions LDA #108 ; We do have an E.C.M. fitted, so print recursive token JSR PrintTokenCrTab ; 108 ("E.C.M.SYSTEM"), followed by a newline and the ; correct indent for Status Mode entries in the chosen ; language LDA #113 ; We now cover the four pieces of equipment whose flags STA XX4 ; are stored in BOMB through BOMB+3, and whose names ; correspond with text tokens 113 through 116: ; ; BOMB+0 = BOMB = token 113 = Energy bomb ; BOMB+1 = ENGY = token 114 = Energy unit ; BOMB+2 = DKCMP = token 115 = Docking computer ; BOMB+3 = GHYP = token 116 = Galactic hyperdrive ; ; We can print these out using a loop, so we set XX4 to ; 113 as a counter (and we also set A as well, to pass ; through to plf2) .stqv TAY ; Fetch byte BOMB+0 through BOMB+4 for values of XX4 LDX BOMB-113,Y ; from 113 through 117 BEQ P%+5 ; If it is zero then we do not own that piece of ; equipment, so skip the next instruction JSR PrintTokenCrTab ; Print the recursive token in A from 113 ("ENERGY ; BOMB") through 116 ("GALACTIC HYPERSPACE "), followed ; by a newline and the correct indent for Status Mode ; entries in the chosen language INC XX4 ; Increment the counter (and A as well) LDA XX4 CMP #117 ; If A < 117, loop back up to stqv to print the next BCC stqv ; piece of equipment LDX #0 ; Now to print our ship's lasers, so set a counter in X ; to count through the four views (0 = front, 1 = rear, ; 2 = left, 3 = right) .st STX CNT ; Store the view number in CNT LDY LASER,X ; Fetch the laser power for view X, and if we do not BEQ st1 ; have a laser fitted to that view, jump to st1 to move ; on to the next one LDA languageNumber ; If bit 2 of languageNumber is set then the chosen AND #%00000100 ; language is French, so jump to stat3 to skip the BNE stat3 ; following (as the screen has a different layout in ; French) TXA ; Print recursive token 96 + X, which will print from 96 CLC ; ("FRONT") through to 99 ("RIGHT"), followed by a space ADC #96 JSR spc .stat3 LDA #103 ; Set A to token 103 ("PULSE LASER") LDX CNT ; Retrieve the view number from CNT that we stored above LDY LASER,X ; Set Y = the laser power for view X CPY #128+POW ; If the laser power for view X is not #POW+128 (beam BNE P%+4 ; laser), skip the next LDA instruction LDA #104 ; This sets A = 104 if the laser in view X is a beam ; laser (token 104 is "BEAM LASER") CPY #Armlas ; If the laser power for view X is not #Armlas (military BNE P%+4 ; laser), skip the next LDA instruction LDA #117 ; This sets A = 117 if the laser in view X is a military ; laser (token 117 is "MILITARY LASER") CPY #Mlas ; If the laser power for view X is not #Mlas (mining BNE P%+4 ; laser), skip the next LDA instruction LDA #118 ; This sets A = 118 if the laser in view X is a mining ; laser (token 118 is "MINING LASER") JSR TT27_b2 ; Print the text token in A (which contains the laser ; type) LDA languageNumber ; If bit 2 of languageNumber is clear then the chosen AND #%00000100 ; language is not French, so jump to stat4 to skip the BEQ stat4 ; following (as the screen has a different layout in ; French) LDA CNT ; Retrieve the view number from CNT that we stored above CLC ; Print recursive token 96 + A, which will print from 96 ADC #96 ; ("FRONT") through to 99 ("RIGHT"), followed by a space JSR PrintSpaceAndToken .stat4 JSR PrintCrTab ; Print a newline and the correct indent for Status Mode ; entries in the chosen language .st1 LDX CNT ; Increment the counter in X and CNT to point to the INX ; next view CPX #4 ; If this isn't the last of the four views, jump back up BCC st ; to st to print out the next one LDA #24 ; Move the text cursor to column 24 STA XC LDX languageIndex ; Move the text cursor to the correct row for the LDA yHeadshot,X ; commander image in the chosen language STA YC JSR GetHeadshotType_b4 ; Set S to the headshot number for the current combat ; rank and status condition, in the range 0 to 13 LDA S ; Set A to %1000xxxx where %xxxx is the headshot number ORA #%10000000 ; in the range 0 to 13 CMP imageSentToPPU ; Set the processor flags according to whether ; imageSentToPPU already has this value STA imageSentToPPU ; Set imageSentToPPU to A BEQ stat5 ; If imageSentToPPU already had this value then we are ; already showing this image on-screen, so jump to stat5 ; to skip the following instruction as there's no need ; to fade the screen out when the image isn't changing JSR FadeAndHideSprites ; Fade the screen to black and hide all sprites, so we ; can update the screen while it's blacked-out .stat5 JSR DrawCmdrImage_b6 ; Draw the commander image as a coloured face image in ; front of a greyscale headshot image, with optional ; embellishments ; Fall through into UpdateView to update the view
Name: UpdateView [Show more] Type: Subroutine Category: Drawing the screen Summary: Update the view Deep dive: Views and view types in NES Elite
Context: See this subroutine on its own page References: This subroutine is called as follows: * BRIEF2 calls UpdateView * BuyAndSellCargo calls UpdateView * EQSHP calls UpdateView * TBRIEF calls UpdateView * TT22 calls UpdateView * TT23 calls UpdateView * TT25 calls UpdateView * UpdateView_b0 calls UpdateView
.UpdateView LDA firstFreePattern ; If firstFreePattern = 0, set firstFreePattern = 255 BNE upvw1 ; LDA #255 ; This ensures that the call to CopyNameBuffer0To1 below STA firstFreePattern ; tells the NMI handler to send pattern entries up to ; the first free pattern, or to send patterns up to the ; very end if we have run out of free patterns (in which ; case firstFreePattern was zero) .upvw1 LDA #0 ; Tell the NMI handler to send nametable entries from STA firstNameTile ; tile 0 onwards LDA #108 ; Tell the NMI handler to only clear nametable entries STA maxNameTileToClear ; up to tile 108 * 8 = 864 (i.e. up to the end of tile ; row 26) STA lastNameTile ; Tell the PPU to send nametable entries up to tile STA lastNameTile+1 ; 108 * 8 = 864 (i.e. to the end of tile row 26) in both ; bitplanes LDX #37 ; Set X = 37 to use as the first pattern for when there ; is an icon bar LDA QQ11 ; If bit 6 of the view type is clear, then there is an AND #%01000000 ; icon bar, so jump to upvw2 to skip the following BEQ upvw2 ; instruction LDX #4 ; Set X = 4 to use as the first pattern for when there ; is no icon bar .upvw2 STX firstPattern ; Tell the NMI handler to send pattern entries from ; pattern X in the buffer (i.e. from pattern 4 if there ; is no icon bar, or from pattern 37 if there is an ; icon bar) JSR DrawBoxEdges ; Draw the left and right edges of the box along the ; sides of the screen, drawing into the nametable buffer ; for the drawing bitplane JSR CopyNameBuffer0To1 ; Copy the contents of nametable buffer 0 to nametable ; buffer LDA QQ11 ; If the new view in QQ11 is the same as the old view in CMP QQ11a ; QQ11a, then jump to upvw6 to call UpdateScreen before BEQ upvw6 ; jumping back to upvw3 (which will either call ; SendViewToPPU to update the view straight away, if ; it's been faded to black, otherwise it will call ; SetupFullViewInNMI to update the view in VBlank, to ; prevent screen corruption) JSR SendViewToPPU_b3 ; Otherwise the view is changing, so it has already been ; faded out and we can call SendViewToPPU to update the ; view straight away without caring about screen ; corruption .upvw3 LDX #$FF ; Set X = $FF to use as the value of showIconBarPointer ; below (i.e. show the icon bar pointer) LDA QQ11 ; If the view type in QQ11 is $95 (Trumble mission CMP #$95 ; briefing), jump to upvw4 to set showIconBarPointer to BEQ upvw4 ; 0 (i.e. hide the icon bar pointer) CMP #$DF ; If the view type in QQ11 is $DF (Start screen with BEQ upvw4 ; the normal font loaded), jump to upvw4 to set ; showIconBarPointer to 0 (i.e. hide the icon bar ; pointer) CMP #$92 ; If the view type in QQ11 is $92 (Mission 1 rotating BEQ upvw4 ; ship briefing), jump to upvw4 to set ; showIconBarPointer to 0 (i.e. hide the icon bar ; pointer) CMP #$93 ; If the view type in QQ11 is $93 (Mission 1 text BEQ upvw4 ; briefing), jump to upvw4 to set showIconBarPointer ; to 0 (i.e. hide the icon bar pointer) ASL A ; If bit 6 of the view type in QQ11 is clear, then there BPL upvw5 ; is an icon bar, so jump to upvw5 to set ; showIconBarPointer to $FF (i.e. show the icon bar ; pointer) .upvw4 LDX #0 ; Set X = 0 to use as the value of showIconBarPointer ; below (i.e. hide the icon bar pointer) .upvw5 STX showIconBarPointer ; Set showIconBarPointer to X, so we set it as follows: ; ; * 0 if the view is a mission briefing, or the Start ; screen with the normal font loaded, or has no ; icon bar (in which case we hide the icon bar ; pointer) ; ; * $FF otherwise (in which case we show the icon bar ; pointer) LDA firstFreePattern ; Tell the NMI handler to send pattern entries from the STA firstPattern ; first free pattern onwards, so we don't waste time ; resending the static patterns we have already sent RTS ; Return from the subroutine .upvw6 JSR UpdateScreen ; Update the screen by sending data to the PPU, either ; immediately or during VBlank, depending on whether ; the screen is visible JMP upvw3 ; Jump back to upvw3 to continue updating the view
Name: yHeadshot [Show more] Type: Variable Category: Status Summary: The text row for the headshot on the Status Mode page Deep dive: Multi-language support in NES Elite
Context: See this variable on its own page References: This variable is used as follows: * STATUS uses yHeadshot
.yHeadshot EQUB 8 ; English EQUB 8 ; German EQUB 10 ; French EQUB 8 ; There is no fourth language, so this byte is ignored
Name: DrawScreenInNMI [Show more] Type: Subroutine Category: Drawing the screen Summary: Configure the NMI handler to draw the screen Deep dive: Views and view types in NES Elite
Context: See this subroutine on its own page References: This subroutine is called as follows: * BuyAndSellCargo calls DrawScreenInNMI * DrawScreenInNMI_b0 calls DrawScreenInNMI * eq calls DrawScreenInNMI * EQSHP calls DrawScreenInNMI * HighlightLaserView calls DrawScreenInNMI * HME2 calls DrawScreenInNMI * qv calls DrawScreenInNMI * TT102 calls DrawScreenInNMI * TT147 calls DrawScreenInNMI * UpdateEquipment calls DrawScreenInNMI
.DrawScreenInNMI JSR WaitForPPUToFinish ; Wait until both bitplanes of the screen have been ; sent to the PPU, so the screen is fully updated and ; there is no more data waiting to be sent to the PPU LDA #0 ; Tell the NMI handler to send nametable entries from STA firstNameTile ; tile 0 onwards LDA #100 ; Tell the NMI handler to only clear nametable entries STA maxNameTileToClear ; up to tile 100 * 8 = 800 (i.e. up to the end of tile ; row 24) LDA #37 ; Tell the NMI handler to send pattern entries from STA firstPattern ; pattern 37 in the buffer JSR SetupPPUForIconBar ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 JSR DrawBoxEdges ; Draw the left and right edges of the box along the ; sides of the screen, drawing into the nametable buffer ; for the drawing bitplane JSR CopyNameBuffer0To1 ; Copy the contents of nametable buffer 0 to nametable ; buffer LDA #%11000100 ; Set both bitplane flags as follows: STA bitplaneFlags ; STA bitplaneFlags+1 ; * Bit 2 set = send tiles up to end of the buffer ; * Bit 3 clear = don't clear buffers after sending ; * Bit 4 clear = we've not started sending data yet ; * Bit 5 clear = we have not yet sent all the data ; * Bit 6 set = send both pattern and nametable data ; * Bit 7 set = send data to the PPU ; ; Bits 0 and 1 are ignored and are always clear ; ; The NMI handler will now start sending data to the PPU ; according to the above configuration, splitting the ; process across multiple VBlanks if necessary LDA firstFreePattern ; Tell the NMI handler to send pattern entries from the STA firstPattern ; first free pattern onwards, so we don't waste time ; resending the static patterns we have already sent RTS ; Return from the subroutine
Name: PrintTokenCrTab [Show more] Type: Subroutine Category: Text Summary: Print a token, a newline and the correct indent for Status Mode entries in the chosen language
Context: See this subroutine on its own page References: This subroutine is called as follows: * STATUS calls PrintTokenCrTab
.PrintTokenCrTab JSR TT27_b2 ; Print the token in A ; Fall through into PrintCrTab to print a newline and ; the correct indent for the chosen language
Name: PrintCrTab [Show more] Type: Subroutine Category: Text Summary: Print a newline and the correct indent for Status Mode entries in the chosen language
Context: See this subroutine on its own page References: This subroutine is called as follows: * STATUS calls PrintCrTab
.PrintCrTab JSR TT67 ; Print a newline LDX languageIndex ; Move the text cursor to the correct column for the LDA xStatusMode,X ; Status Mode entry in the chosen language STA XC RTS ; Return from the subroutine
Name: xStatusMode [Show more] Type: Variable Category: Status Summary: The text column for the Status Mode entries for each language Deep dive: Multi-language support in NES Elite
Context: See this variable on its own page References: This variable is used as follows: * PrintCrTab uses xStatusMode
.xStatusMode EQUB 3 ; English EQUB 3 ; German EQUB 1 ; French EQUB 3 ; There is no fourth language, so this byte is ignored
Name: MVT3 [Show more] Type: Subroutine Category: Moving Summary: Calculate K(3 2 1) = (x_sign x_hi x_lo) + K(3 2 1)
Context: See this subroutine on its own page References: This subroutine is called as follows: * MAS1 calls MVT3 * MV40 calls MVT3 * TAS1 calls MVT3

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

Pitch or roll a ship by a small, fixed amount (1/16 radians, or 3.6 degrees), in a specified direction, by rotating the orientation vectors. The vectors to rotate are given in X and Y, and the direction of the rotation is given in RAT2. The calculation is as follows: * If the direction is positive: X = X * (1 - 1/512) + Y / 16 Y = Y * (1 - 1/512) - X / 16 * If the direction is negative: X = X * (1 - 1/512) - Y / 16 Y = Y * (1 - 1/512) + X / 16 So if X = 15 (roofv_x), Y = 21 (sidev_x) and RAT2 is positive, it does this: roofv_x = roofv_x * (1 - 1/512) + sidev_x / 16 sidev_x = sidev_x * (1 - 1/512) - roofv_x / 16
Arguments: X The first vector to rotate: * If X = 15, rotate roofv_x * If X = 17, rotate roofv_y * If X = 19, rotate roofv_z * If X = 21, rotate sidev_x * If X = 23, rotate sidev_y * If X = 25, rotate sidev_z Y The second vector to rotate: * If Y = 9, rotate nosev_x * If Y = 11, rotate nosev_y * If Y = 13, rotate nosev_z * If Y = 21, rotate sidev_x * If Y = 23, rotate sidev_y * If Y = 25, rotate sidev_z RAT2 The direction of the pitch or roll to perform, positive or negative (i.e. the sign of the roll or pitch counter in bit 7)
.MVS5 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA INWK+1,X ; Fetch roofv_x_hi, clear the sign bit, divide by 2 and AND #%01111111 ; store in T, so: LSR A ; STA T ; T = |roofv_x_hi| / 2 ; = |roofv_x| / 512 ; ; The above is true because: ; ; |roofv_x| = |roofv_x_hi| * 256 + roofv_x_lo ; ; so: ; ; |roofv_x| / 512 = |roofv_x_hi| * 256 / 512 ; + roofv_x_lo / 512 ; = |roofv_x_hi| / 2 LDA INWK,X ; Now we do the following subtraction: SEC ; SBC T ; (S R) = (roofv_x_hi roofv_x_lo) - |roofv_x| / 512 STA R ; = (1 - 1/512) * roofv_x ; ; by doing the low bytes first LDA INWK+1,X ; And then the high bytes (the high byte of the right SBC #0 ; side of the subtraction being 0) STA S LDA INWK,Y ; Set P = nosev_x_lo STA P LDA INWK+1,Y ; Fetch the sign of nosev_x_hi (bit 7) and store in T AND #%10000000 STA T LDA INWK+1,Y ; Fetch nosev_x_hi into A and clear the sign bit, so AND #%01111111 ; A = |nosev_x_hi| LSR A ; Set (A P) = (A P) / 16 ROR P ; = |nosev_x_hi nosev_x_lo| / 16 LSR A ; = |nosev_x| / 16 ROR P LSR A ROR P LSR A ROR P ORA T ; Set the sign of A to the sign in T (i.e. the sign of ; the original nosev_x), so now: ; ; (A P) = nosev_x / 16 EOR RAT2 ; Give it the sign as if we multiplied by the direction ; by the pitch or roll direction STX Q ; Store the value of X so it can be restored after the ; call to ADD JSR ADD ; (A X) = (A P) + (S R) ; = +/-nosev_x / 16 + (1 - 1/512) * roofv_x STA K+1 ; Set K(1 0) = (1 - 1/512) * roofv_x +/- nosev_x / 16 STX K LDX Q ; Restore the value of X from before the call to ADD LDA INWK+1,Y ; Fetch nosev_x_hi, clear the sign bit, divide by 2 and AND #%01111111 ; store in T, so: LSR A ; STA T ; T = |nosev_x_hi| / 2 ; = |nosev_x| / 512 LDA INWK,Y ; Now we do the following subtraction: SEC ; SBC T ; (S R) = (nosev_x_hi nosev_x_lo) - |nosev_x| / 512 STA R ; = (1 - 1/512) * nosev_x ; ; by doing the low bytes first LDA INWK+1,Y ; And then the high bytes (the high byte of the right SBC #0 ; side of the subtraction being 0) STA S LDA INWK,X ; Set P = roofv_x_lo STA P LDA INWK+1,X ; Fetch the sign of roofv_x_hi (bit 7) and store in T AND #%10000000 STA T LDA INWK+1,X ; Fetch roofv_x_hi into A and clear the sign bit, so AND #%01111111 ; A = |roofv_x_hi| LSR A ; Set (A P) = (A P) / 16 ROR P ; = |roofv_x_hi roofv_x_lo| / 16 LSR A ; = |roofv_x| / 16 ROR P LSR A ROR P LSR A ROR P ORA T ; Set the sign of A to the opposite sign to T (i.e. the EOR #%10000000 ; sign of the original -roofv_x), so now: ; ; (A P) = -roofv_x / 16 EOR RAT2 ; Give it the sign as if we multiplied by the direction ; by the pitch or roll direction STX Q ; Store the value of X so it can be restored after the ; call to ADD JSR ADD ; (A X) = (A P) + (S R) ; = -/+roofv_x / 16 + (1 - 1/512) * nosev_x STA INWK+1,Y ; Set nosev_x = (1-1/512) * nosev_x -/+ roofv_x / 16 STX INWK,Y LDX Q ; Restore the value of X from before the call to ADD LDA K ; Set roofv_x = K(1 0) STA INWK,X ; = (1-1/512) * roofv_x +/- nosev_x / 16 LDA K+1 STA INWK+1,X SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 RTS ; Return from the subroutine
Name: TENS [Show more] Type: Variable Category: Text Summary: A constant used when printing large numbers in BPRNT Deep dive: Printing decimal numbers
Context: See this variable on its own page References: This variable is used as follows: * BPRNT uses TENS

Contains the four low bytes of the value 100,000,000,000 (100 billion). The maximum number of digits that we can print with the BPRNT routine is 11, so the biggest number we can print is 99,999,999,999. This maximum number plus 1 is 100,000,000,000, which in hexadecimal is: & 17 48 76 E8 00 The TENS variable contains the lowest four bytes in this number, with the most significant byte first, i.e. 48 76 E8 00. This value is used in the BPRNT routine when working out which decimal digits to print when printing a number.
.TENS EQUD &00E87648
Name: pr2 [Show more] Type: Subroutine Category: Text Summary: Print an 8-bit number, left-padded to 3 digits, and optional point
Context: See this subroutine on its own page References: This subroutine is called as follows: * fwl calls pr2 * tal calls pr2 * TT210 calls pr2 * TT25 calls pr2 * PrintEquipment calls via pr2+2 * PrintNumberInHold calls via pr2+2 * TT151 calls via pr2+2 * TT25 calls via pr2+2

Print the 8-bit number in X to 3 digits, left-padding with spaces for numbers with fewer than 3 digits (so numbers < 100 are right-aligned). Optionally include a decimal point.
Arguments: X The number to print C flag If set, include a decimal point
Other entry points: pr2+2 Print the 8-bit number in X to the number of digits in A
.pr2 LDA #3 ; Set A to the number of digits (3) LDY #0 ; Zero the Y register, so we can fall through into TT11 ; to print the 16-bit number (Y X) to 3 digits, which ; effectively prints X to 3 digits as the high byte is ; zero
Name: TT11 [Show more] Type: Subroutine Category: Text Summary: Print a 16-bit number, left-padded to n digits, and optional point
Context: See this subroutine on its own page References: This subroutine is called as follows: * MESS calls TT11 * pr5 calls TT11 * PrintEquipment calls TT11 * TT210 calls TT11 * TT25 calls TT11

Print the 16-bit number in (Y X) to a specific number of digits, left-padding with spaces for numbers with fewer digits (so lower numbers will be right- aligned). Optionally include a decimal point.
Arguments: X The low byte of the number to print Y The high byte of the number to print A The number of digits C flag If set, include a decimal point
.TT11 STA U ; We are going to use the BPRNT routine (below) to ; print this number, so we store the number of digits ; in U, as that's what BPRNT takes as an argument LDA #0 ; BPRNT takes a 32-bit number in K to K+3, with the STA K ; most significant byte first (big-endian), so we set STA K+1 ; the two most significant bytes to zero (K and K+1) STY K+2 ; and store (Y X) in the least two significant bytes STX K+3 ; (K+2 and K+3), so we are going to print the 32-bit ; number (0 0 Y X) ; Finally we fall through into BPRNT to print out the ; number in K to K+3, which now contains (Y X), to 3 ; digits (as U = 3), using the same C flag as when pr2 ; was called to control the decimal point
Name: BPRNT [Show more] Type: Subroutine Category: Text Summary: Print a 32-bit number, left-padded to a specific number of digits, with an optional decimal point Deep dive: Printing decimal numbers
Context: See this subroutine on its own page References: This subroutine is called as follows: * csh calls BPRNT

Print the 32-bit number stored in K(0 1 2 3) to a specific number of digits, left-padding with spaces for numbers with fewer digits (so lower numbers are right-aligned). Optionally include a decimal point. See the deep dive on "Printing decimal numbers" for details of the algorithm used in this routine.
Arguments: K(0 1 2 3) The number to print, stored with the most significant byte in K and the least significant in K+3 (i.e. as a big-endian number, which is the opposite way to how the 6502 assembler stores addresses, for example) U The maximum number of digits to print, including the decimal point (spaces will be used on the left to pad out the result to this width, so the number is right- aligned to this width). U must be 11 or less C flag If set, include a decimal point followed by one fractional digit (i.e. show the number to 1 decimal place). In this case, the number in K(0 1 2 3) contains 10 * the number we end up printing, so to print 123.4, we would pass 1234 in K(0 1 2 3) and would set the C flag to include the decimal point
.BPRNT LDX #11 ; Set T to the maximum number of digits allowed (11 STX T ; characters, which is the number of digits in 10 ; billion). We will use this as a flag when printing ; characters in TT37 below PHP ; Make a copy of the status register (in particular ; the C flag) so we can retrieve it later BCC TT30 ; If the C flag is clear, we do not want to print a ; decimal point, so skip the next two instructions DEC T ; As we are going to show a decimal point, decrement DEC U ; both the number of characters and the number of ; digits (as one of them is now a decimal point) .TT30 LDA #11 ; Set A to 11, the maximum number of digits allowed SEC ; Set the C flag so we can do subtraction without the ; C flag affecting the result STA XX17 ; Store the maximum number of digits allowed (11) in ; XX17 SBC U ; Set U = 11 - U + 1, so U now contains the maximum STA U ; number of digits minus the number of digits we want INC U ; to display, plus 1 (so this is the number of digits ; we should skip before starting to print the number ; itself, and the plus 1 is there to ensure we print at ; least one digit) LDY #0 ; In the main loop below, we use Y to count the number ; of times we subtract 10 billion to get the leftmost ; digit, so set this to zero STY S ; In the main loop below, we use location S as an ; 8-bit overflow for the 32-bit calculations, so ; we need to set this to 0 before joining the loop JMP TT36 ; Jump to TT36 to start the process of printing this ; number's digits .TT35 ; This subroutine multiplies K(S 0 1 2 3) by 10 and ; stores the result back in K(S 0 1 2 3), using the fact ; that K * 10 = (K * 2) + (K * 2 * 2 * 2) ASL K+3 ; Set K(S 0 1 2 3) = K(S 0 1 2 3) * 2 by rotating left ROL K+2 ROL K+1 ROL K ROL S LDX #3 ; Now we want to make a copy of the newly doubled K in ; XX15, so we can use it for the first (K * 2) in the ; equation above, so set up a counter in X for copying ; four bytes, starting with the last byte in memory ; (i.e. the least significant) .tt35 LDA K,X ; Copy the X-th byte of K(0 1 2 3) to the X-th byte of STA XX15,X ; XX15(0 1 2 3), so that XX15 will contain a copy of ; K(0 1 2 3) once we've copied all four bytes DEX ; Decrement the loop counter BPL tt35 ; Loop back to copy the next byte until we have copied ; all four LDA S ; Store the value of location S, our overflow byte, in STA XX15+4 ; XX15+4, so now XX15(4 0 1 2 3) contains a copy of ; K(S 0 1 2 3), which is the value of (K * 2) that we ; want to use in our calculation ASL K+3 ; Now to calculate the (K * 2 * 2 * 2) part. We still ROL K+2 ; have (K * 2) in K(S 0 1 2 3), so we just need to shift ROL K+1 ; it twice. This is the first one, so we do this: ROL K ; ROL S ; K(S 0 1 2 3) = K(S 0 1 2 3) * 2 = K * 4 ASL K+3 ; And then we do it again, so that means: ROL K+2 ; ROL K+1 ; K(S 0 1 2 3) = K(S 0 1 2 3) * 2 = K * 8 ROL K ROL S CLC ; Clear the C flag so we can do addition without the ; C flag affecting the result LDX #3 ; By now we've got (K * 2) in XX15(4 0 1 2 3) and ; (K * 8) in K(S 0 1 2 3), so the final step is to add ; these two 32-bit numbers together to get K * 10. ; So we set a counter in X for four bytes, starting ; with the last byte in memory (i.e. the least ; significant) .tt36 LDA K,X ; Fetch the X-th byte of K into A ADC XX15,X ; Add the X-th byte of XX15 to A, with carry STA K,X ; Store the result in the X-th byte of K DEX ; Decrement the loop counter BPL tt36 ; Loop back to add the next byte, moving from the least ; significant byte to the most significant, until we ; have added all four LDA XX15+4 ; Finally, fetch the overflow byte from XX15(4 0 1 2 3) ADC S ; And add it to the overflow byte from K(S 0 1 2 3), ; with carry STA S ; And store the result in the overflow byte from ; K(S 0 1 2 3), so now we have our desired result, i.e. ; ; K(S 0 1 2 3) = K(S 0 1 2 3) * 10 LDY #0 ; In the main loop below, we use Y to count the number ; of times we subtract 10 billion to get the leftmost ; digit, so set this to zero so we can rejoin the main ; loop for another subtraction process .TT36 ; This is the main loop of our digit-printing routine. ; In the following loop, we are going to count the ; number of times that we can subtract 10 million and ; store that count in Y, which we have already set to 0 LDX #3 ; Our first calculation concerns 32-bit numbers, so ; set up a counter for a four-byte loop SEC ; Set the C flag so we can do subtraction without the ; C flag affecting the result .tt37 PHP ; Store the flags on the stack to we can retrieve them ; after the macro SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 PLP ; Retrieve the flags from the stack ; We now loop through each byte in turn to do this: ; ; XX15(4 0 1 2 3) = K(S 0 1 2 3) - 100,000,000,000 LDA K,X ; Subtract the X-th byte of TENS (i.e. 10 billion) from SBC TENS,X ; the X-th byte of K STA XX15,X ; Store the result in the X-th byte of XX15 DEX ; Decrement the loop counter BPL tt37 ; Loop back to subtract the next byte, moving from the ; least significant byte to the most significant, until ; we have subtracted all four LDA S ; Subtract the fifth byte of 10 billion (i.e. $17) from SBC #$17 ; the fifth (overflow) byte of K, which is S STA XX15+4 ; Store the result in the overflow byte of XX15 BCC TT37 ; If subtracting 10 billion took us below zero, jump to ; TT37 to print out this digit, which is now in Y LDX #3 ; We now want to copy XX15(4 0 1 2 3) back into ; K(S 0 1 2 3), so we can loop back up to do the next ; subtraction, so set up a counter for a four-byte loop .tt38 LDA XX15,X ; Copy the X-th byte of XX15(0 1 2 3) to the X-th byte STA K,X ; of K(0 1 2 3), so that K(0 1 2 3) will contain a copy ; of XX15(0 1 2 3) once we've copied all four bytes DEX ; Decrement the loop counter BPL tt38 ; Loop back to copy the next byte, until we have copied ; all four LDA XX15+4 ; Store the value of location XX15+4, our overflow STA S ; byte in S, so now K(S 0 1 2 3) contains a copy of ; XX15(4 0 1 2 3) INY ; We have now managed to subtract 10 billion from our ; number, so increment Y, which is where we are keeping ; a count of the number of subtractions so far JMP TT36 ; Jump back to TT36 to subtract the next 10 billion .TT37 TYA ; If we get here then Y contains the digit that we want ; to print (as Y has now counted the total number of ; subtractions of 10 billion), so transfer Y into A BNE TT32 ; If the digit is non-zero, jump to TT32 to print it LDA T ; Otherwise the digit is zero. If we are already ; printing the number then we will want to print a 0, ; but if we haven't started printing the number yet, ; then we probably don't, as we don't want to print ; leading zeroes unless this is the only digit before ; the decimal point ; ; To help with this, we are going to use T as a flag ; that tells us whether we have already started ; printing digits: ; ; * If T <> 0 we haven't printed anything yet ; ; * If T = 0 then we have started printing digits ; ; We initially set T above to the maximum number of ; characters allowed, less 1 if we are printing a ; decimal point, so the first time we enter the digit ; printing routine at TT37, it is definitely non-zero BEQ TT32 ; If T = 0, jump straight to the print routine at TT32, ; as we have already started printing the number, so we ; definitely want to print this digit too DEC U ; We initially set U to the number of digits we want to BPL TT34 ; skip before starting to print the number. If we get ; here then we haven't printed any digits yet, so ; decrement U to see if we have reached the point where ; we should start printing the number, and if not, jump ; to TT34 to set up things for the next digit LDA #' ' ; We haven't started printing any digits yet, but we BNE tt34 ; have reached the point where we should start printing ; our number, so call TT26 (via tt34) to print a space ; so that the number is left-padded with spaces (this ; BNE is effectively a JMP as A will never be zero) .TT32 LDY #0 ; We are printing an actual digit, so first set T to 0, STY T ; to denote that we have now started printing digits as ; opposed to spaces CLC ; The digit value is in A, so add ASCII "0" to get the ADC #'0' ; ASCII character number to print .tt34 JSR DASC_b2 ; Call DASC to print the character in A and fall through ; into TT34 to get things ready for the next digit .TT34 DEC T ; Decrement T but keep T >= 0 (by incrementing it BPL P%+4 ; again if the above decrement made T negative) INC T DEC XX17 ; Decrement the total number of characters left to ; print, which we stored in XX17 BMI rT10 ; If the result is negative, we have printed all the ; characters, so jump down to rT10 to return from the ; subroutine BNE P%+11 ; If the result is positive (> 0) then we still have ; characters left to print, so loop back to TT35 (via ; the JMP TT35 instruction below) to print the next ; digit PLP ; If we get here then we have printed the exact number ; of digits that we wanted to, so restore the C flag ; that we stored at the start of the routine BCC P%+8 ; If the C flag is clear, we don't want a decimal point, ; so loop back to TT35 (via the JMP TT35 instruction ; below) to print the next digit LDA decimalPoint ; Otherwise the C flag is set, so print the correct JSR DASC_b2 ; decimal point character for the chosen language JMP TT35 ; Loop back to TT35 to print the next digit .rT10 RTS ; Return from the subroutine
Name: DrawPitchRollBars [Show more] Type: Subroutine Category: Dashboard Summary: Update the pitch and roll bars on the dashboard
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawSpaceViewInNMI calls DrawPitchRollBars

The roll indicator uses sprite 11 and the pitch indicator uses sprite 12.
.DrawPitchRollBars ; We start by drawing the roll indicator LDA JSTX ; Set SC2 = 216 + ~JSTX / 8 EOR #$FF ; = 216 - (JSTX + 1) / 8 LSR A ; LSR A ; We use this as the x-coordinate of the roll indicator LSR A ; as the centre of the indicator is at x-coordinate 232 CLC ; and JSTX / 8 is in the range 0 to 31, with a value of ADC #216 ; 16 in the middle of the indicator, so SC2 is in the STA SC2 ; range 216 to 247 with a middle point of 231 (this ; isn't 232 because of the + 1 in the calculation above) LDY #29 ; Set Y = 29 so we draw the indicator sprite on pixel ; y-coordinate 170 + 29 = 199 (which is the y-coordinate ; of the roll indicator) LDA #11 ; Set A = 11 so we draw the indicator using sprite 11 JSR piro1 ; Call piro1 below to draw the roll indicator ; We now draw the pitch indicator LDA JSTY ; Set SC2 = 216 + JSTY / 8 LSR A ; LSR A ; We use this as the x-coordinate of the pitch indicator LSR A ; as the centre of the indicator is at x-coordinate 232 CLC ; and JSTY / 8 is in the range 0 to 31, with a value of ADC #216 ; 16 in the middle of the indicator, so SC2 is in the STA SC2 ; range 216 to 247 with a middle point of 232 LDY #37 ; Set Y = 37 so we draw the indicator sprite on pixel ; y-coordinate 170 + 37 = 207 (which is the y-coordinate ; of the pitch indicator) LDA #12 ; Set A = 11 so we draw the indicator using sprite 12 .piro1 ASL A ; Set X = A * 4 ASL A ; TAX ; So we can use X as an index into the sprite buffer to ; update the sprite data for sprite A (as each sprite ; has four bytes in the buffer) LDA SC2 ; Set the x-coordinate of the relevant indicator's SEC ; sprite to SC2 - 4, so the centre of the eight-pixel SBC #4 ; wide sprite is on x-coordinate SC2 STA xSprite0,X TYA ; Set the y-coordinate of the relevant indicator's CLC ; sprite to 170 + Y, so it appears on the correct row ADC #170+YPAL STA ySprite0,X RTS ; Return from the subroutine
Name: ESCAPE [Show more] Type: Subroutine Category: Flight Summary: Launch our escape pod
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 3 of 16) calls ESCAPE

This routine displays our doomed Cobra Mk III disappearing off into the ether before arranging our replacement ship. Called when we press ESCAPE during flight and have an escape pod fitted.
.ESCAPE JSR RES2 ; Reset a number of flight variables and workspaces LDY #19 ; Call the NOISE routine with Y = 19 to make the sound JSR NOISE ; of the escape pod launching LDA #0 ; The escape pod is a one-use item, so set ESCP to 0 so STA ESCP ; we no longer have one fitted JSR UpdateIconBar_b3 ; Update the icon bar to remove the escape pod button as ; we no longer have one LDA QQ11 ; If this is not the space view, jump to escp1 to skip BNE escp1 ; drawing the escape pod animation LDX #CYL ; Set the current ship type to a Cobra Mk III, so we STX TYPE ; can show our ship disappear into the distance when we ; eject in our pod JSR FRS1 ; Call FRS1 to launch the Cobra Mk III straight ahead, ; like a missile launch, but with our ship instead BCS ES1 ; If the Cobra was successfully added to the local ; bubble, jump to ES1 to skip the following instructions LDX #CYL2 ; The Cobra wasn't added to the local bubble for some JSR FRS1 ; reason, so try launching a pirate Cobra Mk III instead .ES1 LDA #8 ; Set the Cobra's byte #27 (speed) to 8 STA INWK+27 LDA #194 ; Set the Cobra's byte #30 (pitch counter) to 194, so it STA INWK+30 ; pitches up as we pull away LDA #%00101100 ; Set the Cobra's byte #32 (AI flag) to %00101100, so it STA INWK+32 ; has no AI, and we can use this value as a counter to ; do the following loop 44 times .ESL1 JSR MVEIT ; Call MVEIT to move the Cobra in space JSR DrawShipInBitplane ; Flip the drawing bitplane and draw the current ship in ; the newly flipped bitplane DEC INWK+32 ; Decrement the counter in byte #32 BNE ESL1 ; Loop back to keep moving the Cobra until the AI flag ; is 0, which gives it time to drift away from our pod .escp1 LDA #0 ; Set A = 0 so we can use it to zero the contents of ; the cargo hold LDX #16 ; We lose all our cargo when using our escape pod, so ; up a counter in X so we can zero the 17 cargo slots ; in QQ20 .ESL2 STA QQ20,X ; Set the X-th byte of QQ20 to zero, so we no longer ; have any of item type X in the cargo hold DEX ; Decrement the counter BPL ESL2 ; Loop back to ESL2 until we have emptied the entire ; cargo hold STA FIST ; Launching an escape pod also clears our criminal ; record, so set our legal status in FIST to 0 ("clean") LDA TRIBBLE ; If there are no Trumbles in our hold, then both bytes ORA TRIBBLE+1 ; of TRIBBLE(1 0) will be zero, so jump to nosurviv to BEQ nosurviv ; skip the following JSR DORND ; Otherwise set TRIBBLE(1 0) to a random number between AND #7 ; 1 and 7, to determine how many Trumbles manage to ORA #1 ; hitch a ride in the escape pod (so using an escape pod STA TRIBBLE ; is not a solution to the trouble with Trumbles) LDA #0 STA TRIBBLE+1 .nosurviv LDA #70 ; Our replacement ship is delivered with a full tank of STA QQ14 ; fuel, so set the current fuel level in QQ14 to 70, or ; 7.0 light years JMP GOIN ; Go to the docking bay (i.e. show the ship hangar ; screen) and return from the subroutine with a tail ; call
Name: HME2 [Show more] Type: Subroutine Category: Charts Summary: Search the galaxy for a system
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT102 calls HME2
.HME2 JSR CLYNS ; Clear the bottom two text rows of the upper screen, ; and move the text cursor to column 1 on row 21, i.e. ; the start of the top row of the two bottom rows LDA #14 ; Print extended token 14 ("{clear bottom of screen} JSR DETOK_b2 ; PLANET NAME?{fetch line input from keyboard}"). The ; last token calls MT26, which puts the entered search ; term in INWK+5 and the term length in Y LDY #9 ; We start by setting the default name to all A's in the ; buffer at INWK+5, which is where the InputName routine ; expects to find the default name to show, so set a ; counter in Y for ten characters STY inputNameSize ; Set inputNameSize = 9 so we fetch a system name with a ; maximum size of 10 characters in the call to InputName ; below LDA #'A' ; Set A to ASCII "A" so we can fill the name buffer with ; "A" characters .sear1 STA INWK+5,Y ; Set the Y-th character of the name at INWK+5 with "A" DEY ; Decrement the loop counter BPL sear1 ; Loop back until we have filled all ten characters ; of the name JSR InputName_b6 ; Get a system name from the controller into INWK+5, ; where the name will be terminated by ASCII 13 LDA INWK+5 ; If the first character of the entered name is ASCII 13 CMP #13 ; then no name was entered, so jump to sear2 to return BEQ sear2 ; from the subroutine JSR TT81 ; Set the seeds in QQ15 (the selected system) to those ; of system 0 in the current galaxy (i.e. copy the seeds ; from QQ21 to QQ15) LDA #0 ; We now loop through the galaxy's systems in order, STA XX20 ; until we find a match, so set XX20 to act as a system ; counter, starting with system 0 .HME3 SETUP_PPU_FOR_ICON_BAR ; If the PPU has started drawing the icon bar, configure ; the PPU to use nametable 0 and pattern table 0 LDA #%10000000 ; Set the DTW4 flag to %10000000 (justify text, print STA DTW4 ; the contents of the buffer whenever a carriage return ; appears in the token) ASL A ; Set DTW5 = 0, which sets the size of the justified STA DTW5 ; text buffer at BUF to zero JSR cpl ; Print the selected system name into the justified text ; buffer LDX DTW5 ; Fetch DTW5 into X, so X is now equal to the length of ; the selected system name LDA INWK+5,X ; Fetch the X-th character from the entered search term CMP #13 ; If the X-th character is not a carriage return, then BNE HME6 ; the selected system name and the entered search term ; are different lengths, so jump to HME6 to move on to ; the next system .HME4 DEX ; Decrement X so it points to the last letter of the ; selected system name (and, when we loop back here, it ; points to the next letter to the left) LDA INWK+5,X ; Set A to the X-th character of the entered search term ORA #%00100000 ; Set bit 5 of the character to make it lower case CMP BUF,X ; If the character in A matches the X-th character of BEQ HME4 ; the selected system name in BUF, loop back to HME4 to ; check the next letter to the left TXA ; The last comparison didn't match, so copy the letter BMI HME5 ; number into A, and if it's negative, that means we ; managed to go past the first letters of each term ; before we failed to get a match, so the terms are the ; same, so jump to HME5 to process a successful search .HME6 ; If we get here then the selected system name and the ; entered search term did not match JSR DisableJustifyText ; Turn off justified text JSR TT20 ; We want to move on to the next system, so call TT20 ; to twist the three 16-bit seeds in QQ15 INC XX20 ; Increment the system counter in XX20 BNE HME3 ; If we haven't yet checked all 256 systems in the ; current galaxy, loop back to HME3 to check the next ; system ; If we get here then the entered search term did not ; match any systems in the current galaxy JSR TT111 ; Select the system closest to galactic coordinates ; (QQ9, QQ10), so we can put the crosshairs back where ; they were before the search JSR BOOP ; Call the BOOP routine to make a low, long beep to ; indicate a failed search LDA #215 ; Print extended token 215 ("{left align} UNKNOWN JSR DETOK_b2 ; PLANET"), which will print on-screen as the left align ; code disables justified text JMP DrawScreenInNMI ; Configure the NMI handler to draw the screen, ; returning from the subroutine using a tail call .HME5 ; If we get here then we have found a match for the ; entered search JSR DisableJustifyText ; Turn off justified text JSR CLYNS ; Clear the bottom two text rows of the upper screen, ; and move the text cursor to column 1 on row 21, i.e. ; the start of the top row of the two bottom rows LDA #%00000000 ; Set DTW8 = %00000000 (capitalise the next letter) STA DTW8 LDA QQ15+3 ; The x-coordinate of the system described by the seeds STA QQ9 ; in QQ15 is in QQ15+3 (s1_hi), so we copy this to QQ9 ; as the x-coordinate of the search result LDA QQ15+1 ; The y-coordinate of the system described by the seeds STA QQ10 ; in QQ15 is in QQ15+1 (s0_hi), so we copy this to QQ10 ; as the y-coordinate of the search result JMP ReturnFromSearch ; Jump back into TT102 to select the found system and ; return from the subroutine using a tail call .sear2 JSR CLYNS ; Clear the bottom two text rows of the upper screen, ; and move the text cursor to column 1 on row 21, i.e. ; the start of the top row of the two bottom rows JMP DrawScreenInNMI ; Configure the NMI handler to draw the screen, ; returning from the subroutine using a tail call