Skip to navigation

Elite on the BBC Micro and NES

Bank 0 (Part 2 of 5)

[NES version]

Name: TACTICS (Part 1 of 7) [Show more] Type: Subroutine Category: Tactics Summary: Apply tactics: Process missiles, both enemy missiles and our own Deep dive: Program flow of the tactics routine
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This section implements missile tactics and is entered at TA18 from the main entry point below, if the current ship is a missile. Specifically: * If E.C.M. is active, destroy the missile * If the missile is hostile towards us, then check how close it is. If it hasn't reached us, jump to part 3 so it can streak towards us, otherwise we've been hit, so process a large amount of damage to our ship * Otherwise see how close the missile is to its target. If it has not yet reached its target, give the target a chance to activate its E.C.M. if it has one, otherwise jump to TA19 with K3 set to the vector from the target to the missile * If it has reached its target and the target is the space station, destroy the missile, potentially damaging us if we are nearby * If it has reached its target and the target is a ship, destroy the missile and the ship, potentially damaging us if we are nearby
.TA352 ; If we get here, the missile has been destroyed by ; E.C.M. or by the space station LDA INWK ; Set A = x_lo OR y_lo OR z_lo of the missile ORA INWK+3 ORA INWK+6 BNE TA872 ; If A is non-zero then the missile is not near our ; ship, so skip the next two instructions to avoid ; damaging our ship LDA #80 ; Otherwise the missile just got destroyed near us, so JSR OOPS ; call OOPS to damage the ship by 80, which is nowhere ; near as bad as the 250 damage from a missile slamming ; straight into us, but it's still pretty nasty .TA872 LDX #PLT ; Set X to the ship type for plate alloys, so we get ; awarded the kill points for the missile scraps in TA87 BNE TA353 ; Jump to TA353 to process the missile kill tally and ; make an explosion sound .TA34 ; If we get here, the missile is hostile LDA #0 ; Set A to x_hi OR y_hi OR z_hi JSR MAS4 BEQ P%+5 ; If A = 0 then the missile is very close to our ship, ; so skip the following instruction JMP TN4 ; Jump down to part 3 to set up the vectors and skip ; straight to aggressive manoeuvring JSR TA873 ; The missile has hit our ship, so call TA873 to set ; bit 7 of the missile's byte #31, which marks the ; missile as being killed JSR EXNO3 ; Make the sound of the missile exploding LDA #250 ; Call OOPS to damage the ship by 250, which is a pretty JMP OOPS ; big hit, and return from the subroutine using a tail ; call .TA18 ; This is the entry point for missile tactics and is ; called from the main TACTICS routine below LDA ECMA ; If an E.C.M. is currently active (either ours or an BNE TA352 ; opponent's), jump to TA352 to destroy this missile LDA INWK+32 ; Fetch the AI flag from byte #32 and if bit 6 is set ASL A ; (i.e. missile is hostile), jump up to TA34 to check BMI TA34 ; whether the missile has hit us LSR A ; Otherwise shift A right again. We know bits 6 and 7 ; are now clear, so this leaves bits 0-5. Bits 1-5 ; contain the target's slot number, and bit 0 is cleared ; in FRMIS when a missile is launched, so A contains ; the slot number shifted left by 1 (i.e. doubled) so we ; can use it as an index for the two-byte address table ; at UNIV TAX ; Copy the address of the target ship's data block from LDA UNIV,X ; UNIV(X+1 X) to (A V) STA V LDA UNIV+1,X JSR VCSUB ; Calculate vector K3 as follows: ; ; K3(2 1 0) = (x_sign x_hi x_lo) - x-coordinate of ; target ship ; ; K3(5 4 3) = (y_sign y_hi z_lo) - y-coordinate of ; target ship ; ; K3(8 7 6) = (z_sign z_hi z_lo) - z-coordinate of ; target ship ; So K3 now contains the vector from the target ship to ; the missile LDA K3+2 ; Set A = OR of all the sign and high bytes of the ORA K3+5 ; above, clearing bit 7 (i.e. ignore the signs) ORA K3+8 AND #%01111111 ORA K3+1 ORA K3+4 ORA K3+7 BNE TA64 ; If the result is non-zero, then the missile is some ; distance from the target, so jump down to TA64 see if ; the target activates its E.C.M. LDA INWK+32 ; Fetch the AI flag from byte #32 and if only bits 7 and CMP #%10000010 ; 1 are set (AI is enabled and the target is slot 1, the BEQ TA352 ; space station), jump to TA352 to destroy this missile, ; as the space station ain't kidding around LDY #31 ; Fetch byte #31 (the exploding flag) of the target ship LDA (V),Y ; into A BIT M32+1 ; M32 contains an LDY #32 instruction, so M32+1 contains ; 32, so this instruction tests A with %00100000, which ; checks bit 5 of A (the "already exploding?" bit) BNE TA35 ; If the target ship is already exploding, jump to TA35 ; to destroy this missile ORA #%10000000 ; Otherwise set bit 7 of the target's byte #31 to mark STA (V),Y ; the ship as having been killed, so it explodes .TA35 LDA INWK ; Set A = x_lo OR y_lo OR z_lo of the missile ORA INWK+3 ORA INWK+6 BNE P%+7 ; If A is non-zero then the missile is not near our ; ship, so skip the next two instructions to avoid ; damaging our ship LDA #80 ; Otherwise the missile just got destroyed near us, so JSR OOPS ; call OOPS to damage the ship by 80, which is nowhere ; near as bad as the 250 damage from a missile slamming ; straight into us, but it's still pretty nasty .TA87 LDA INWK+32 ; Set X to bits 1-6 of the missile's AI flag in ship AND #%01111111 ; byte #32, so bits 0-4 of X are the target's slot LSR A ; number, and bit 5 is set (as the missile is hostile) TAX ; so X is fairly random and in the range 32-39 (as the ; maximum slot number is 7) LDA MJ-32,X ; Set X to entry X-32 starting from MJ table, which will TAX ; be even more random, as MJ is where we store data like ; the cabin and laser temperature ; ; The value of X is used to determine the number of kill ; points awarded for the destruction of the missile .TA353 JSR EXNO2 ; Call EXNO2 to process the fact that we have killed a ; missile (so increase the kill tally, make an explosion ; sound and so on) .TA873 ASL INWK+31 ; Set bit 7 of the missile's byte #31 flag to mark it as SEC ; having been killed, so it explodes ROR INWK+31 .TA1 RTS ; Return from the subroutine .TA64 ; If we get here then the missile has not reached the ; target JSR DORND ; Set A and X to random numbers CMP #16 ; If A >= 16 (94% chance), jump down to TA19S with the BCS TA19S ; vector from the target to the missile in K3 .M32 LDY #32 ; Fetch byte #32 for the target and shift bit 0 (E.C.M.) LDA (V),Y ; into the C flag LSR A BCS P%+5 ; If the C flag is set then the target has E.C.M. ; fitted, so skip the next instruction .TA19S JMP TA19 ; The target does not have E.C.M. fitted, so jump down ; to TA19 with the vector from the target to the missile ; in K3 JMP ECBLB2 ; The target has E.C.M., so jump to ECBLB2 to set it ; off, returning from the subroutine using a tail call
Name: TACTICS (Part 2 of 7) [Show more] Type: Subroutine Category: Tactics Summary: Apply tactics: Escape pod, station, lone Thargon, safe-zone pirate Deep dive: Program flow of the tactics routine
Context: See this subroutine on its own page References: This subroutine is called as follows: * MVEIT (Part 2 of 9) calls TACTICS

This section contains the main entry point at TACTICS, which is called from part 2 of MVEIT for ships that have the AI flag set (i.e. bit 7 of byte #32). This part does the following: * If this is a missile, jump up to the missile code in part 1 * If this is the space station and it is hostile, consider spawning a cop (6.2% chance, up to a maximum of seven) and we're done * If this is the space station and it is not hostile, consider spawning (0.8% chance if there are no Transporters around) a Transporter or Shuttle (equal odds of each type) and we're done * If this is a rock hermit, consider spawning (22% chance) a highly aggressive and hostile Sidewinder, Mamba, Krait, Adder or Gecko (equal odds of each type) and we're done * Recharge the ship's energy banks by 1
Arguments: X The ship type
.TACTICS LDA #3 ; Set RAT = 3, which is the magnitude we set the pitch STA RAT ; or roll counter to in part 7 when turning a ship ; towards a vector (a higher value giving a longer ; turn). This value is not changed in the TACTICS ; routine, but it is set to different values by the ; DOCKIT routine STA shipIsAggressive ; Set shipIsAggressive = 3 so that bit 7 is clear, so ; the default position is that the ship we are ; processing tactics for is not looking for a fight ; (though this might change, of course) LDA #4 ; Set RAT2 = 4, which is the threshold below which we STA RAT2 ; don't apply pitch and roll to the ship (so a lower ; value means we apply pitch and roll more often, and a ; value of 0 means we always apply them). The value is ; compared with double the high byte of sidev . XX15, ; where XX15 is the vector from the ship to the enemy ; or planet. This value is set to different values by ; both the TACTICS and DOCKIT routines LDA #22 ; Set CNT2 = 22, which is the maximum angle beyond which STA CNT2 ; a ship will slow down to start turning towards its ; prey (a lower value means a ship will start to slow ; down even if its angle with the enemy ship is large, ; which gives a tighter turn). This value is not changed ; in the TACTICS routine, but it is set to different ; values by the DOCKIT routine CPX #MSL ; If this is a missile, jump up to TA18 to implement BEQ TA18 ; missile tactics CPX #SST ; If this is not the space station, jump down to TA13 BNE TA13 LDA NEWB ; This is the space station, so check whether bit 2 of AND #%00000100 ; the ship's NEWB flags is set, and if it is (i.e. the BNE TN5 ; station is hostile), jump to TN5 to spawn some cops LDA MANY+SHU+1 ; Set A to the number of Transporters in the vicinity ORA auto ; If the docking computer is on then auto is $FF, so ; this ensures that A is always non-zero when we are ; auto-docking, so the following jump to TA1 will be ; taken and no Transporters will be spawned from the ; space station (unlike in the disc version, where you ; can get smashed into space dust by a badly timed ; Transporter launch when using the docking computer) BNE TA1 ; The station is not hostile, so check how many ; Transporters there are in the vicinity, and if we ; already have one, return from the subroutine (as TA1 ; contains an RTS) ; If we get here then the station is not hostile, so we ; can consider spawning a Transporter or Shuttle JSR DORND ; Set A and X to random numbers CMP #253 ; If A < 253 (99.2% chance), return from the subroutine BCC TA1 ; (as TA1 contains an RTS) AND #1 ; Set A = a random number that's either 0 or 1 ADC #SHU-1 ; The C flag is set (as we didn't take the BCC above), TAX ; so this sets X to a value of either #SHU or #SHU + 1, ; which is the ship type for a Shuttle or a Transporter BNE TN6 ; Jump to TN6 to spawn this ship type and return from ; the subroutine using a tail call (this BNE is ; effectively a JMP as A is never zero) .TN5 ; We only call the tactics routine for the space station ; when it is hostile, so if we get here then this is the ; station, and we already know it's hostile, so we need ; to spawn some cops JSR DORND ; Set A and X to random numbers CMP #240 ; If A < 240 (93.8% chance), return from the subroutine BCC TA1 ; (as TA1 contains an RTS) LDA MANY+COPS ; Check how many cops there are in the vicinity already, CMP #4 ; and if there are 4 or more, return from the subroutine BCS TA22 ; (as TA22 contains an RTS) LDX #COPS ; Set X to the ship type for a cop .TN6 LDA #%11110001 ; Set the AI flag to give the ship E.C.M., enable AI and ; make it very aggressive (60 out of 63) JMP SFS1 ; Jump to SFS1 to spawn the ship, returning from the ; subroutine using a tail call .TA13 CPX #HER ; If this is not a rock hermit, jump down to TA17 BNE TA17 JSR DORND ; Set A and X to random numbers CMP #200 ; If A < 200 (78% chance), return from the subroutine BCC TA22 ; (as TA22 contains an RTS) LDX #0 ; Set byte #32 to %00000000 to disable AI, aggression STX INWK+32 ; and E.C.M. LDX #%00100100 ; Set the ship's NEWB flags to %00100100 so the ship we STX NEWB ; spawn below will inherit the default values from E% as ; well as having bit 2 (hostile) and bit 5 (innocent ; bystander) set AND #3 ; Set A = a random number that's in the range 0-3 ADC #SH3 ; The C flag is set (as we didn't take the BCC above), TAX ; so this sets X to a random value between #SH3 + 1 and ; #SH3 + 4, so that's a Sidewinder, Mamba, Krait, Adder ; or Gecko JSR TN6 ; Call TN6 to spawn this ship with E.C.M., AI and a high ; aggression (56 out of 63) LDA #0 ; Set byte #32 to %00000000 to disable AI, aggression STA INWK+32 ; and E.C.M. (for the rock hermit) RTS ; Return from the subroutine .TA17 LDY #14 ; If the ship's energy is greater or equal to the JSR GetShipBlueprint ; maximum value from the ship's blueprint pointed to by CMP INWK+35 ; XX0, then skip the next instruction BCC TA21 BEQ TA21 INC INWK+35 ; The ship's energy is not at maximum, so recharge the ; energy banks by 1
Name: TACTICS (Part 3 of 7) [Show more] Type: Subroutine Category: Tactics Summary: Apply tactics: Calculate dot product to determine ship's aim Deep dive: Program flow of the tactics routine
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOCKIT calls via GOPL

This section sets up some vectors and calculates dot products. Specifically: * If this is a lone Thargon without a mothership, set it adrift aimlessly and we're done * If this is a trader, 80% of the time we're done, 20% of the time the trader performs the same checks as the bounty hunter * If this is a bounty hunter (or one of the 20% of traders) and we have been really bad (i.e. a fugitive or serious offender), the ship becomes hostile (if it isn't already) * If the ship is not hostile, then either perform docking manoeuvres (if it's docking) or fly towards the planet (if it isn't docking) and we're done * If the ship is hostile, and a pirate, and we are within the space station safe zone, stop the pirate from attacking by removing all its aggression * Calculate the dot product of the ship's nose vector (i.e. the direction it is pointing) with the vector between us and the ship. This value will help us work out later on whether the enemy ship is pointing towards us, and therefore whether it can hit us with its lasers.
Other entry points: GOPL Make the ship head towards the planet
.TA21 CPX #TGL ; If this is not a Thargon, jump down to TA14 BNE TA14 LDA MANY+THG ; If there is at least one Thargoid in the vicinity, BNE TA14 ; jump down to TA14 LSR INWK+32 ; This is a Thargon but there is no Thargoid mothership, ASL INWK+32 ; so clear bit 0 of the AI flag to disable its E.C.M. LSR INWK+27 ; And halve the Thargon's speed .TA22 RTS ; Return from the subroutine .TA14 JSR DORND ; Set A and X to random numbers LDA NEWB ; Extract bit 0 of the ship's NEWB flags into the C flag LSR A ; and jump to TN1 if it is clear (i.e. if this is not a BCC TN1 ; trader) CPX #50 ; This is a trader, so if X >= 50 (80% chance), return BCS TA22 ; from the subroutine (as TA22 contains an RTS) .TN1 LSR A ; Extract bit 1 of the ship's NEWB flags into the C flag BCC TN2 ; and jump to TN2 if it is clear (i.e. if this is not a ; bounty hunter) LDX FIST ; This is a bounty hunter, so check whether our FIST CPX #40 ; rating is < 40 (where 50 is a fugitive), and jump to BCC TN2 ; TN2 if we are not 100% evil LDA NEWB ; We are a fugitive or a bad offender, and this ship is ORA #%00000100 ; a bounty hunter, so set bit 2 of the ship's NEWB flags STA NEWB ; to make it hostile LSR A ; Shift A right twice so the next test in TN2 will check LSR A ; bit 2 .TN2 LSR A ; Extract bit 2 of the ship's NEWB flags into the C flag BCS TN3 ; and jump to TN3 if it is set (i.e. if this ship is ; hostile) LSR A ; The ship is not hostile, so extract bit 4 of the LSR A ; ship's NEWB flags into the C flag, and jump to GOPL if BCC GOPL ; it is clear (i.e. if this ship is not docking) JMP DOCKIT ; The ship is not hostile and is docking, so jump to ; DOCKIT to apply the docking algorithm to this ship .GOPL JSR SPS1 ; The ship is not hostile and it is not docking, so call ; SPS1 to calculate the vector to the planet and store ; it in XX15 JMP TA151 ; Jump to TA151 to make the ship head towards the planet .TN3 LSR A ; Extract bit 2 of the ship's NEWB flags into the C flag BCC TN4 ; and jump to TN4 if it is clear (i.e. if this ship is ; not a pirate) LDA SSPR ; If we are not inside the space station safe zone, jump BEQ TN4 ; to TN4 ; If we get here then this is a pirate and we are inside ; the space station safe zone LDA INWK+32 ; Set bits 0 and 7 of the AI flag in byte #32 (has AI AND #%10000001 ; enabled and has an E.C.M.) STA INWK+32 .TN4 LDX #8 ; We now want to copy the ship's x, y and z coordinates ; from INWK to K3, so set up a counter for 9 bytes .TAL1 LDA INWK,X ; Copy the X-th byte from INWK to the X-th byte of K3 STA K3,X DEX ; Decrement the counter BPL TAL1 ; Loop back until we have copied all 9 bytes .TA19 ; If this is a missile that's heading for its target ; (not us, one of the other ships), then the missile ; routine at TA18 above jumps here after setting K3 to ; the vector from the target to the missile JSR TAS2 ; Normalise the vector in K3 and store the normalised ; version in XX15, so XX15 contains the normalised ; vector from our ship to the ship we are applying AI ; tactics to (or the normalised vector from the target ; to the missile - in both cases it's the vector from ; the potential victim to the attacker) LDY #10 ; Set (A X) = nosev . XX15 JSR TAS3 STA CNT ; Store the high byte of the dot product in CNT. The ; bigger the value, the more aligned the two ships are, ; with a maximum magnitude of 36 (96 * 96 >> 8). If CNT ; is positive, the ships are facing in a similar ; direction, if it's negative they are facing in ; opposite directions
Name: TACTICS (Part 4 of 7) [Show more] Type: Subroutine Category: Tactics Summary: Apply tactics: Check energy levels, maybe launch escape pod if low Deep dive: Program flow of the tactics routine
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This section works out what kind of condition the ship is in. Specifically: * If this is an Anaconda, consider spawning (22% chance) a Worm (61% of the time) or a Sidewinder (39% of the time) * Rarely (2.5% chance) roll the ship by a noticeable amount * If the ship has at least half its energy banks full, jump to part 6 to consider firing the lasers * If the ship is not into the last 1/8th of its energy, jump to part 5 to consider firing a missile * If the ship is into the last 1/8th of its energy, and this ship type has an escape pod fitted, then rarely (10% chance) the ship launches an escape pod and is left drifting 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 LDA TYPE ; If this is not a missile, skip the following CMP #MSL ; instruction BNE P%+5 JMP TA20 ; This is a missile, so jump down to TA20 to get ; straight into some aggressive manoeuvring CMP #ANA ; If this is not an Anaconda, jump down to TN7 to skip BNE TN7 ; the following JSR DORND ; Set A and X to random numbers CMP #200 ; If A < 200 (78% chance), jump down to TN7 to skip the BCC TN7 ; following JSR DORND ; Set A and X to random numbers LDX #WRM ; Set X to the ship type for a Worm CMP #100 ; If A >= 100 (61% chance), skip the following BCS P%+4 ; instruction LDX #SH3 ; Set X to the ship type for a Sidewinder JMP TN6 ; Jump to TN6 to spawn the Worm or Sidewinder and return ; from the subroutine using a tail call .TN7 JSR DORND ; Set A and X to random numbers CMP #250 ; If A < 250 (97.5% chance), jump down to TA7 to skip BCC TA7 ; the following JSR DORND ; Set A and X to random numbers ORA #104 ; Bump A up to at least 104 and store in the roll STA INWK+29 ; counter, to gives the ship a noticeable roll .TA7 LDY #14 ; Set A = the ship's maximum energy / 2 JSR GetShipBlueprint LSR A CMP INWK+35 ; If the ship's current energy in byte #35 > A, i.e. the BCC TA3 ; ship has at least half of its energy banks charged, ; jump down to TA3 LSR A ; If the ship's current energy in byte #35 > A / 4, i.e. LSR A ; the ship is not into the last 1/8th of its energy, CMP INWK+35 ; jump down to ta3 to consider firing a missile BCC ta3 JSR DORND ; Set A and X to random numbers CMP #230 ; If A < 230 (90% chance), jump down to ta3 to consider BCC ta3 ; firing a missile LDX TYPE ; Fetch the ship blueprint's default NEWB flags from the LDY TYPE ; table at E%, and if bit 7 is clear (i.e. this ship JSR GetDefaultNEWB ; does not have an escape pod), jump to ta3 to skip the BPL ta3 ; spawning of an escape pod ; By this point, the ship has run out of both energy and ; luck, so it's time to bail LDA NEWB ; Clear bits 0-3 of the NEWB flags, so the ship is no AND #%11110000 ; longer a trader, a bounty hunter, hostile or a pirate STA NEWB ; and the escape pod we are about to spawn won't inherit ; any of these traits LDY #36 ; Update the NEWB flags in the ship's data block STA (INF),Y LDA #0 ; Set the AI flag to 0 to disable AI, hostility and STA INWK+32 ; E.C.M., so the ship's a sitting duck JMP SESCP ; Jump to SESCP to spawn an escape pod from the ship, ; returning from the subroutine using a tail call
Name: TACTICS (Part 5 of 7) [Show more] Type: Subroutine Category: Tactics Summary: Apply tactics: Consider whether to launch a missile at us Deep dive: Program flow of the tactics routine
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This section considers whether to launch a missile. Specifically: * If the ship doesn't have any missiles, skip to the next part * If an E.C.M. is firing, skip to the next part * Randomly decide whether to fire a missile (or, in the case of Thargoids, release a Thargon), and if we do, we're done
.ta3 ; If we get here then the ship has less than half energy ; so there may not be enough juice for lasers, but let's ; see if we can fire a missile LDA INWK+31 ; Set A = bits 0-2 of byte #31, the number of missiles AND #%00000111 ; the ship has left BEQ TA3 ; If it doesn't have any missiles, jump to TA3 STA T ; Store the number of missiles in T JSR DORND ; Set A and X to random numbers AND #31 ; Restrict A to a random number in the range 0-31 CMP T ; If A >= T, which is quite likely, though less likely BCS TA3 ; with higher numbers of missiles, jump to TA3 to skip ; firing a missile LDA ECMA ; If an E.C.M. is currently active (either ours or an BNE TA3 ; opponent's), jump to TA3 to skip firing a missile DEC INWK+31 ; We're done with the checks, so it's time to fire off a ; missile, so reduce the missile count in byte #31 by 1 LDA TYPE ; Fetch the ship type into A CMP #THG ; If this is not a Thargoid, jump down to TA16 to launch BNE TA16 ; a missile LDX #TGL ; This is a Thargoid, so instead of launching a missile, LDA INWK+32 ; the mothership launches a Thargon, so call SFS1 to JMP SFS1 ; spawn a Thargon from the parent ship, and return from ; the subroutine using a tail call .TA16 JMP SFRMIS ; Jump to SFRMIS to spawn a missile as a child of the ; current ship, make a noise and print a message warning ; of incoming missiles, and return from the subroutine ; using a tail call
Name: TACTICS (Part 6 of 7) [Show more] Type: Subroutine Category: Tactics Summary: Apply tactics: Consider firing a laser at us, if aim is true Deep dive: Program flow of the tactics routine
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

This section looks at potentially firing the ship's laser at us. Specifically: * If the ship is not pointing at us, skip to the next part * If the ship is pointing at us but not accurately, fire its laser at us and skip to the next part * If we are in the ship's crosshairs, register some damage to our ship, slow down the attacking ship, make the noise of us being hit by laser fire, and we're done
.TA3 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 ; If we get here then the ship either has plenty of ; energy, or levels are low but it couldn't manage to ; launch a missile, so maybe we can fire the laser? LDA #0 ; Set A to x_hi OR y_hi OR z_hi JSR MAS4 AND #%11100000 ; If any of the hi bytes have any of bits 5-7 set, then BNE TA4 ; jump to TA4 to skip the laser checks, as the ship is ; too far away from us to hit us with a laser LDX CNT ; Set X = the dot product set above in CNT. If this is ; positive, this ship and our ship are facing in similar ; directions, but if it's negative then we are facing ; each other, so for us to be in the enemy ship's line ; of fire, X needs to be negative. The value in X can ; have a maximum magnitude of 36, which would mean we ; were facing each other square on, so in the following ; code we check X like this: ; ; X = 0 to -31, we are not in the enemy ship's line ; of fire, so they can't shoot at us ; ; X = -32 to -34, we are in the enemy ship's line ; of fire, so they can shoot at us, but they can't ; hit us as we're not dead in their crosshairs ; ; X = -35 to -36, we are bang in the middle of the ; enemy ship's crosshairs, so they can not only ; shoot us, they can hit us CPX #158 ; If X < 158, i.e. X > -30, then we are not in the enemy BCC TA4 ; ship's line of fire, so jump to TA4 to skip the laser ; checks LDY #19 ; Fetch the enemy ship's byte #19 from their ship's JSR GetShipBlueprint ; blueprint into A AND #%11111000 ; Extract bits 3-7, which contain the enemy's laser ; power BEQ TA4 ; If the enemy has no laser power, jump to TA4 to skip ; the laser checks CPX #161 ; If X < 161, i.e. X > -31, then we are not in the enemy BCC tact1 ; ship's line of fire, so jump to tact1 to skip the ; laser checks LDA INWK+31 ; Set bit 6 in byte #31 to denote that the ship is ORA #%01000000 ; firing its laser at us STA INWK+31 CPX #163 ; If X >= 163, i.e. X <= -35, then we are in the enemy BCS tact2 ; ship's crosshairs, so jump to tact2 to skip the laser ; checks .tact1 JSR TAS6 ; Call TAS6 to negate the vector in XX15 so it points in ; the opposite direction LDA CNT ; Change the sign of the dot product in CNT, so now it's EOR #%10000000 ; positive if the ships are facing each other, and ; negative if they are facing the same way STA CNT ; Update CNT with the new value in A JSR TA15 ; Call TA15 so the ship heads away from us JMP tact3 ; Jump to tact3 to continue with the checks .tact2 JSR GetShipBlueprint ; Fetch the enemy ship's byte #19 from their ship's ; blueprint into A LSR A ; Halve the enemy ship's byte #19 (which contains both ; the laser power and number of missiles) to get the ; amount of damage we should take JSR OOPS ; Call OOPS to take some damage, which could do anything ; from reducing the shields and energy, all the way to ; losing cargo or dying (if the latter, we don't come ; back from this subroutine) LDY #11 ; Call the NOISE routine with Y = 11 to make the sound JSR NOISE ; of us being hit by lasers .tact3 LDA INWK+7 ; If z_hi >= 3 then the ship is quite far away, so jump CMP #3 ; down to tact4 to apply the brakes BCS tact4 JSR DORND ; Set A and X to random numbers ORA #%11000000 ; Set bits 6 and 7 of A, so A is at least 192 CMP INWK+32 ; If A < byte #32 (the ship's AI flag) then jump down BCC tact4 ; to tact4 to apply the brakes ; ; We jump if A < byte #32, and the chances of this ; being true are greater with high values of byte #32, ; as long as they are at least 192 ; ; In other words, higher byte #32 values increase the ; chances of a ship changing direction to head towards ; us - or, to put it another way, ships with higher ; byte #32 values over 192 are spoiling for a fight ; ; Thargoids have byte #32 set to 255, which explains ; an awful lot JSR DORND ; Otherwise set the ship's pitch counter to a random AND #%10000111 ; number in the range 0 to 7, with a random pitch STA INWK+30 ; direction JMP tact8 ; Jump to tact8 to set the ship's acceleration to 3 and ; return from the subroutine .tact4 LDA INWK+1 ; If none of x_hi, y_hi or z_hi has bits 5 to 7 set, ORA INWK+4 ; then they are all less than 31, so jump to tact11 to ORA INWK+7 ; set the ship's acceleration to -1 (or -2 if it is a AND #%11100000 ; missile) BEQ tact11 BNE tact8 ; Otherwise jump to tact8 to set the ship's acceleration ; to 3 and return from the subroutine (this BNE is ; effectively a JMP as we just passed through a BEQ)
Name: TACTICS (Part 7 of 7) [Show more] Type: Subroutine Category: Tactics Summary: Apply tactics: Set pitch, roll, and acceleration Deep dive: Program flow of the tactics routine
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOCKIT calls via TA151 * TACTICS (Part 3 of 7) calls via TA151

This section looks at manoeuvring the ship. Specifically: * Work out which direction the ship should be moving, depending on the type of ship, where it is, which direction it is pointing, and how aggressive it is * Set the pitch and roll counters to head in that direction * Speed up or slow down, depending on where the ship is in relation to us
Other entry points: TA151 Make the ship head towards the planet
.TA4 LDA INWK+7 ; If z_hi >= 3 then the ship is quite far away, so jump CMP #3 ; down to TA5 BCS TA5 LDA INWK+1 ; Otherwise set A = x_hi OR y_hi and extract bits 1-7 ORA INWK+4 AND #%11111110 BEQ tact5 ; If A = 0 then the ship is pretty close to us, so jump ; to tact5 so it heads away from us .TA5 ; If we get here then the ship is quite far away JSR DORND ; Set A and X to random numbers ORA #%10000000 ; Set bit 7 of A, so A is at least 128 CMP INWK+32 ; If A >= byte #32 (the ship's AI flag) then jump down BCS tact5 ; to tact5 so it heads away from us ; We get here if A < byte #32, and the chances of this ; being true are greater with high values of byte #32, ; as long as they are at least 128 ; ; In other words, higher byte #32 values increase the ; chances of a ship changing direction to head towards ; us - or, to put it another way, ships with higher ; byte #32 values of 128 or more are spoiling for a ; fight ; ; Thargoids have byte #32 set to 255, which explains ; an awful lot STA shipIsAggressive ; Store A in shipIsAggressive, so we can check bit 7 of ; the value below to judge whether this ship is spoiling ; for a fight .TA20 ; If this is a missile we will have jumped straight ; here, but we also get here if the ship is either far ; away and aggressive, or not too close JSR TAS6 ; Call TAS6 to negate the vector in XX15 so it points in ; the opposite direction LDA CNT ; Change the sign of the dot product in CNT, so now it's EOR #%10000000 ; positive if the ships are facing each other, and ; negative if they are facing the same way .TA152 STA CNT ; Update CNT with the new value in A .tact5 JSR TA15 ; Call TA15 so the ship heads away from us LDA shipIsAggressive ; If bit 7 of shipIsAggressive is clear then the ship is BPL tact7 ; not spoiling for a fight, so jump to tact7 to skip the ; following LDA INWK+1 ; If any of x_hi, y_hi or z_hi have bits 3 to 7 set, ORA INWK+4 ; then at least one of them is greater than 7, so jump ORA INWK+7 ; to tact7 to consider slowing down to make a turn AND #%11111000 BNE tact7 ; If we get here then x_hi, y_hi and z_hi are all 7 or ; smaller, so the ships are close together LDA CNT ; Fetch the dot product, and if it's negative jump to BMI tact6 ; tact10 via tact6 to consider speeding up or braking, ; as the ships are facing away from each other CMP CNT2 ; The dot product is positive, so the ships are facing BCS tact11 ; each other. If A >= CNT2 then the ships are heading ; directly towards each other, so jump to tact11 to ; accelerate hard .tact6 JMP tact10 ; Jump to tact10 to consider speeding up or braking .tact7 LDA CNT ; Fetch the dot product, and if it's negative jump to BMI tact9 ; tact9, as the ships are facing away from each other ; so we should consider slowing down to make a turn CMP CNT2 ; The dot product is positive, so the ships are facing BCC tact10 ; each other. If A < CNT2 then the ships are not heading ; directly towards each other, so jump to tact10 to ; consider speeding up or braking .tact8 ; Set the ship's acceleration to 3 LDA #3 ; Set A = 3 so we set the acceleration in byte #28 to 3 ; below BNE tact12 ; Jump to tact12 to set the acceleration and return from ; the subroutine (this BNE is effectively a JMP as A is ; never zero) .tact9 AND #%01111111 ; Clear the sign bit of the dot product in A CMP #6 ; If A >= 6 then the ship is not far from the XX15 BCS tact11 ; vector, so jump to tact11 to start slowing down ; Otherwise the ship is way off the XX15 vector, so we ; fall through into tact10 to consider speeding up or ; braking .tact10 ; If we get here then we speed up the ship if it is ; going slowly, otherwise we apply the brakes 22% of the ; time, or do nothing 78% of the time LDA INWK+27 ; Set A to the ship's speed in byte #27 CMP #6 ; If A < 6 then the ship is not going fast, so jump to BCC tact8 ; tact8 to set the ship's acceleration to 3 JSR DORND ; Otherwise the ship is going fast, so set A and X to ; random numbers CMP #200 ; If A < 200 (78% chance), jump to TA10 to return from BCC TA10 ; the subroutine without accelerating ; If we get here the ship is not going that slowly, so ; we only apply the brakes 22% of the time .tact11 ; Set the ship's acceleration to -1, unless it is a ; missile, in which case set it to -2 LDA #$FF ; Set A = -1 LDX TYPE ; If this is not a missile then skip the ASL instruction CPX #MSL BNE tact12 ASL A ; This is a missile, so set A = -2, as missiles are more ; nimble and can brake more quickly .tact12 STA INWK+28 ; Set the ship's acceleration to A .TA10 RTS ; Return from the subroutine .TA151 ; This is called from part 3 with the vector to the ; planet in XX15, when we want the ship to turn towards ; the planet. It does the same dot product calculation ; as part 3, but it can also change the value of RAT2 ; so that roll and pitch is always applied LDY #10 ; Set (A X) = nosev . XX15 JSR TAS3 ; ; The bigger the value of the dot product, the more ; aligned the two vectors are, with a maximum magnitude ; in A of 36 (96 * 96 >> 8). If A is positive, the ; vectors are facing in a similar direction, if it's ; negative they are facing in opposite directions CMP #$98 ; If A is positive or A <= -24, jump to ttt BCC ttt LDX #0 ; A > -24, which means the vectors are facing in STX RAT2 ; opposite directions but are quite aligned, so set ; RAT2 = 0 instead of the default value of 4, so we ; always apply roll and pitch when we turn the ship ; towards the planet .ttt JMP TA152 ; Jump to TA152 to store A in CNT and move the ship in ; the direction of XX15 .TA15 ; If we get here, then one of the following is true: ; ; * This is a trader and XX15 is pointing towards the ; planet ; ; * The ship is pretty close to us, or it's just not ; very aggressive (though there is a random factor ; at play here too). XX15 is still pointing from our ; ship towards the enemy ship ; ; * The ship is aggressive (though again, there's an ; element of randomness here). XX15 is pointing from ; the enemy ship towards our ship ; ; * This is a missile heading for a target. XX15 is ; pointing from the missile towards the target ; ; We now want to move the ship in the direction of XX15, ; which will make aggressive ships head towards us, and ; ships that are too close turn away. Peaceful traders, ; meanwhile, head off towards the planet in search of a ; space station, and missiles home in on their targets LDY #16 ; Set (A X) = roofv . XX15 JSR TAS3 ; ; This will be positive if XX15 is pointing in the same ; direction as an arrow out of the top of the ship, in ; other words if the ship should pull up to head in the ; direction of XX15 TAX ; Copy A into X so we can retrieve it below EOR #%10000000 ; Give the ship's pitch counter the opposite sign to the AND #%10000000 ; dot product result, with a value of 0 STA INWK+30 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 CNT ; Fetch the dot product, and if it's positive jump to BPL tact14 ; tact14, as the ships are facing each other and we ; don't need to pitch to make a turn CMP #159 ; If A < 159, skip to tact14 to leave the pitch counter BCC tact14 ; at zero LDA #7 ; Set the magnitude of the ship's pitch counter to 7 ORA INWK+30 ; (we already set the sign above) STA INWK+30 LDA #0 ; Set A = 0 so when we jump to tact15, we set the ship's ; roll counter to zero BEQ tact15 ; Jump to tact15 to set the roll counter (this BEQ is ; effectively a JMP as A is always zero) .tact14 TXA ; Retrieve the original value of A from X ASL A ; Shift A left to double it and drop the sign bit CMP RAT2 ; If A < RAT2, skip to TA11 (so if RAT2 = 0, we always BCC TA11 ; set the pitch counter to RAT) LDA RAT ; Set the magnitude of the ship's pitch counter to RAT ORA INWK+30 ; (we already set the sign above) STA INWK+30 .TA11 LDA INWK+29 ; Fetch the roll counter from byte #29 into A ASL A ; Shift A left to double it and drop the sign bit CMP #32 ; If A >= 32 then jump to TA6, as the ship is already BCS TA6 ; in the process of rolling LDY #22 ; Set (A X) = sidev . XX15 JSR TAS3 ; ; This will be positive if XX15 is pointing in the same ; direction as an arrow out of the right side of the ; ship, in other words if the ship should roll right to ; head in the direction of XX15 TAX ; Copy A into X so we can retrieve it below EOR INWK+30 ; Give the ship's roll counter a positive sign AND #%10000000 ; (clockwise roll) if the pitch counter and dot product EOR #%10000000 ; have different signs, negative (anti-clockwise roll) STA INWK+29 ; if they have the same sign, with a value of 0 TXA ; Retrieve the original value of A from X ASL A ; Shift A left to double it and drop the sign bit CMP RAT2 ; If A < RAT2, skip to TA6 (so if RAT2 = 0, we always BCC TA6 ; set the roll counter to RAT) LDA RAT ; Set the magnitude of the ship's roll counter to RAT ORA INWK+29 ; (we already set the sign above) .tact15 STA INWK+29 ; Store the magnitude of the ship's roll counter .TA6 RTS ; Return from the subroutine
Name: DOCKIT [Show more] Type: Subroutine Category: Flight Summary: Apply docking manoeuvres to the ship in INWK Deep dive: The docking computer
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOKEY calls DOCKIT * TACTICS (Part 3 of 7) calls DOCKIT
.DOCKIT LDA #6 ; Set RAT2 = 6, which is the threshold below which we STA RAT2 ; don't apply pitch and roll to the ship (so a lower ; value means we apply pitch and roll more often, and a ; value of 0 means we always apply them). The value is ; compared with double the high byte of sidev . XX15, ; where XX15 is the vector from the ship to the station LSR A ; Set RAT = 2, which is the magnitude we set the pitch STA RAT ; or roll counter to in part 7 when turning a ship ; towards a vector (a higher value giving a longer ; turn) LDA #29 ; Set CNT2 = 29, which is the maximum angle beyond which STA CNT2 ; a ship will slow down to start turning towards its ; prey (a lower value means a ship will start to slow ; down even if its angle with the enemy ship is large, ; which gives a tighter turn) LDA SSPR ; If we are inside the space station safe zone, skip the BNE P%+5 ; next instruction .GOPLS JMP GOPL ; Jump to GOPL to make the ship head towards the planet JSR VCSU1 ; If we get here then we are in the space station safe ; zone, so call VCSU1 to calculate the following, where ; the station is at coordinates (station_x, station_y, ; station_z): ; ; K3(2 1 0) = (x_sign x_hi x_lo) - station_x ; ; K3(5 4 3) = (y_sign y_hi z_lo) - station_y ; ; K3(8 7 6) = (z_sign z_hi z_lo) - station_z ; ; so K3 contains the vector from the station to the ship LDA K3+2 ; If any of the top bytes of the K3 results above are ORA K3+5 ; non-zero (after removing the sign bits), jump to GOPL ORA K3+8 ; via GOPLS to make the ship head towards the planet, as AND #%01111111 ; this will aim the ship in the general direction of the BNE GOPLS ; station (it's too far away for anything more accurate) JSR TA2 ; Call TA2 to calculate the length of the vector in K3 ; (ignoring the low coordinates), returning it in Q LDA Q ; Store the value of Q in K, so K now contains the STA K ; distance between station and the ship JSR TAS2 ; Call TAS2 to normalise the vector in K3, returning the ; normalised version in XX15, so XX15 contains the unit ; vector pointing from the station to the ship LDY #10 ; Call TAS4 to calculate: JSR TAS4 ; ; (A X) = nosev . XX15 ; ; where nosev is the nose vector of the space station, ; so this is the dot product of the station to ship ; vector with the station's nosev (which points straight ; out into space, out of the docking slot), and because ; both vectors are unit vectors, the following is also ; true: ; ; (A X) = cos(t) ; ; where t is the angle between the two vectors ; ; If the dot product is positive, that means the vector ; from the station to the ship and the nosev sticking ; out of the docking slot are facing in a broadly ; similar direction (so the ship is essentially heading ; for the slot, which is facing towards the ship), and ; if it's negative they are facing in broadly opposite ; directions (so the station slot is on the opposite ; side of the station as the ship approaches) BMI PH1 ; If the dot product is negative, i.e. the station slot ; is on the opposite side, jump to PH1 to fly towards ; the ideal docking position, some way in front of the ; slot CMP #35 ; If the dot product < 35, jump to PH1 to fly towards BCC PH1 ; the ideal docking position, some way in front of the ; slot, as there is a large angle between the vector ; from the station to the ship and the station's nosev, ; so the angle of approach is not very optimal ; ; Specifically, as the unit vector length is 96 in our ; vector system, ; ; (A X) = cos(t) < 35 / 96 ; ; so: ; ; t > arccos(35 / 96) = 68.6 degrees ; ; so the ship is coming in from the side of the station ; at an angle between 68.6 and 90 degrees off the ; optimal entry angle ; If we get here, the slot is on the same side as the ; ship and the angle of approach is less than 68.6 ; degrees, so we're heading in pretty much the correct ; direction for a good approach to the docking slot LDY #10 ; Call TAS3 to calculate: JSR TAS3 ; ; (A X) = nosev . XX15 ; ; where nosev is the nose vector of the ship, so this is ; the dot product of the station to ship vector with the ; ship's nosev, and is a measure of how close to the ; station the ship is pointing, with negative meaning it ; is pointing at the station, and positive meaning it is ; pointing away from the station CMP #$A2 ; If the dot product is in the range 0 to -34, jump to BCS PH3 ; PH3 to refine our approach, as we are pointing towards ; the station ; If we get here, then we are not pointing straight at ; the station, so check how close we are LDA K ; Fetch the distance to the station into A CMP #157 ; If A < 157, jump to PH2 to turn away from the station, BCC PH2 ; as we are too close LDA TYPE ; Fetch the ship type into A BMI PH3 ; If bit 7 is set, then that means the ship type was set ; to -96 in the DOKEY routine when we switched on our ; docking computer, so this is us auto-docking our ; Cobra, so jump to PH3 to refine our approach ; ; Otherwise this is an NPC trying to dock, so keep going ; to turn away from the station .PH2 ; If we get here then we turn away from the station and ; slow right down, effectively aborting this approach ; attempt JSR TAS6 ; Call TAS6 to negate the vector in XX15 so it points in ; the opposite direction, away from the station and ; towards the ship JSR TA151 ; Call TA151 to make the ship head in the direction of ; XX15, which makes the ship turn away from the station .PH22 ; If we get here then we slam on the brakes and slow ; right down LDX #0 ; Set the acceleration in byte #28 to 0 STX INWK+28 INX ; Set the speed in byte #28 to 1 STX INWK+27 RTS ; Return from the subroutine .PH1 ; If we get here then the slot is on the opposite side ; of the station to the ship, or it's on the same side ; and the approach angle is not optimal, so we just fly ; towards the station, aiming for the ideal docking ; position some distance in front of the slot JSR VCSU1 ; Call VCSU1 to set K3 to the vector from the station to ; the ship JSR DCS1 ; Call DCS1 twice to calculate the vector from the ideal JSR DCS1 ; docking position to the ship, where the ideal docking ; position is straight out of the docking slot at a ; distance of 8 unit vectors from the centre of the ; station JSR TAS2 ; Call TAS2 to normalise the vector in K3, returning the ; normalised version in XX15 JSR TAS6 ; Call TAS6 to negate the vector in XX15 so it points in ; the opposite direction JMP TA151 ; Call TA151 to make the ship head in the direction of ; XX15, which makes the ship turn towards the ideal ; docking position, and return from the subroutine using ; a tail call .TN11 ; If we get here, we accelerate and apply a full ; clockwise roll (which matches the space station's ; roll) INC INWK+28 ; Increment the acceleration in byte #28 LDA #%01111111 ; Set the roll counter to a positive (clockwise) roll STA INWK+29 ; with no damping, to match the space station's roll BNE TN13 ; Jump down to TN13 (this BNE is effectively a JMP as ; A will never be zero) .PH3 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 ; If we get here, we refine our approach using pitch and ; roll to aim for the station LDX #0 ; Set RAT2 = 0 STX RAT2 STX INWK+30 ; Set the pitch counter to 0 to stop any pitching LDA TYPE ; If this is not our ship's docking computer, but is an BPL PH32 ; NPC ship trying to dock, jump to PH32 ; In the following, ship_x and ship_y are the x and ; y-coordinates of XX15, the vector from the station to ; the ship EOR XX15 ; A is negative, so this sets the sign of A to the same EOR XX15+1 ; as -XX15 * XX15+1, or -ship_x * ship_y ASL A ; Shift the sign bit into the C flag, so the C flag has ; the following sign: ; ; * Positive if ship_x and ship_y have different signs ; * Negative if ship_x and ship_y have the same sign LDA #2 ; Set A = +2 or -2, giving it the sign in the C flag, ROR A ; and store it in byte #29, the roll counter, so that STA INWK+29 ; the ship rolls towards the station LDA XX15 ; If |ship_x * 2| >= 12, i.e. |ship_x| >= 6, then jump ASL A ; to PH22 to slow right down and return from the CMP #12 ; subroutine, as the station is not in our sights BCS PH22 LDA XX15+1 ; Set A = +2 or -2, giving it the same sign as ship_y, ASL A ; and store it in byte #30, the pitch counter, so that LDA #2 ; the ship pitches towards the station ROR A STA INWK+30 LDA XX15+1 ; If |ship_y * 2| >= 12, i.e. |ship_y| >= 6, then jump ASL A ; to PH22 to slow right down and return from the CMP #12 ; subroutine, as the station is not in our sights BCS PH22 .PH32 ; If we get here, we try to match the station roll STX INWK+29 ; Set the roll counter to 0 to stop any pitching LDA INWK+22 ; Set XX15 = sidev_x_hi STA XX15 LDA INWK+24 ; Set XX15+1 = sidev_y_hi STA XX15+1 LDA INWK+26 ; Set XX15+2 = sidev_z_hi STA XX15+2 ; ; so XX15 contains the sidev vector of the ship LDY #16 ; Call TAS4 to calculate: JSR TAS4 ; ; (A X) = roofv . XX15 ; ; where roofv is the roof vector of the space station. ; To dock with the slot horizontal, we want roofv to be ; pointing off to the side, i.e. parallel to the ship's ; sidev vector, which means we want the dot product to ; be large (it can be positive or negative, as roofv can ; point left or right - it just needs to be parallel to ; the ship's sidev) ASL A ; If |A * 2| >= 66, i.e. |A| >= 33, then the ship is CMP #66 ; lined up with the slot, so jump to TN11 to accelerate BCS TN11 ; and roll clockwise (a positive roll) before jumping ; down to TN13 to check if we're docked yet JSR PH22 ; Call PH22 to slow right down, as we haven't yet ; matched the station's roll .TN13 ; If we get here, we check to see if we have docked LDA K3+10 ; If K3+10 is non-zero, skip to TNRTS, to return from BNE TNRTS ; the subroutine ; ; I have to say I have no idea what K3+10 contains, as ; it isn't mentioned anywhere in the whole codebase ; apart from here, but it does share a location with ; XX2+10, so it will sometimes be non-zero (specifically ; when face #10 in the ship we're drawing is visible, ; which probably happens quite a lot). This would seem ; to affect whether an NPC ship can dock, as that's the ; code that gets skipped if K3+10 is non-zero, but as ; to what this means... that's not yet clear ASL NEWB ; Set bit 7 of the ship's NEWB flags to indicate that SEC ; the ship has now docked, which only has meaning if ROR NEWB ; this is an NPC trying to dock .TNRTS RTS ; Return from the subroutine
Name: VCSU1 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate vector K3(8 0) = [x y z] - coordinates of the sun or space station
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOCKIT calls VCSU1

Calculate the following: K3(2 1 0) = (x_sign x_hi x_lo) - x-coordinate of the sun or space station K3(5 4 3) = (y_sign y_hi z_lo) - y-coordinate of the sun or space station K3(8 7 6) = (z_sign z_hi z_lo) - z-coordinate of the sun or space station where the first coordinate is from the ship data block in INWK, and the second coordinate is from the sun or space station's ship data block which they share.
.VCSU1 LDA #LO(K%+NIK%) ; Set the low byte of V(1 0) to point to the coordinates STA V ; of the sun or space station LDA #HI(K%+NIK%) ; Set A to the high byte of the address of the ; coordinates of the sun or space station ; Fall through into VCSUB to calculate: ; ; K3(2 1 0) = (x_sign x_hi x_lo) - x-coordinate of sun ; or space station ; ; K3(2 1 0) = (x_sign x_hi x_lo) - x-coordinate of sun ; or space station ; ; K3(8 7 6) = (z_sign z_hi z_lo) - z-coordinate of sun ; or space station
Name: VCSUB [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate vector K3(8 0) = [x y z] - coordinates in (A V)
Context: See this subroutine on its own page References: This subroutine is called as follows: * TACTICS (Part 1 of 7) calls VCSUB

Calculate the following: K3(2 1 0) = (x_sign x_hi x_lo) - x-coordinate in (A V) K3(5 4 3) = (y_sign y_hi z_lo) - y-coordinate in (A V) K3(8 7 6) = (z_sign z_hi z_lo) - z-coordinate in (A V) where the first coordinate is from the ship data block in INWK, and the second coordinate is from the ship data block pointed to by (A V).
.VCSUB STA V+1 ; Set the low byte of V(1 0) to A, so now V(1 0) = (A V) LDY #2 ; K3(2 1 0) = (x_sign x_hi x_lo) - x-coordinate in data JSR TAS1 ; block at V(1 0) LDY #5 ; K3(5 4 3) = (y_sign y_hi z_lo) - y-coordinate of data JSR TAS1 ; block at V(1 0) LDY #8 ; Fall through into TAS1 to calculate the final result: ; ; K3(8 7 6) = (z_sign z_hi z_lo) - z-coordinate of data ; block at V(1 0)
Name: TAS1 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate K3 = (x_sign x_hi x_lo) - V(1 0)
Context: See this subroutine on its own page References: This subroutine is called as follows: * VCSUB calls TAS1

Calculate one of the following, depending on the value in Y: K3(2 1 0) = (x_sign x_hi x_lo) - x-coordinate in V(1 0) K3(5 4 3) = (y_sign y_hi z_lo) - y-coordinate in V(1 0) K3(8 7 6) = (z_sign z_hi z_lo) - z-coordinate in V(1 0) where the first coordinate is from the ship data block in INWK, and the second coordinate is from the ship data block pointed to by V(1 0).
Arguments: V(1 0) The address of the ship data block to subtract Y The coordinate in the V(1 0) block to subtract: * If Y = 2, subtract the x-coordinate and store the result in K3(2 1 0) * If Y = 5, subtract the y-coordinate and store the result in K3(5 4 3) * If Y = 8, subtract the z-coordinate and store the result in K3(8 7 6)
.TAS1 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 (V),Y ; Copy the sign byte of the V(1 0) coordinate into K+3, EOR #%10000000 ; flipping it in the process STA K+3 DEY ; Copy the high byte of the V(1 0) coordinate into K+2 LDA (V),Y STA K+2 DEY ; Copy the high byte of the V(1 0) coordinate into K+1, LDA (V),Y ; so now: STA K+1 ; ; K(3 2 1) = - coordinate in V(1 0) STY U ; Copy the index (now 0, 3 or 6) into U and X LDX U JSR MVT3 ; Call MVT3 to add the same coordinates, but this time ; from INWK, so this would look like this for the ; x-axis: ; ; K(3 2 1) = (x_sign x_hi x_lo) + K(3 2 1) ; = (x_sign x_hi x_lo) - coordinate in V(1 0) LDY U ; Restore the index into Y, though this instruction has ; no effect, as Y is not used again, either here or ; following calls to this routine STA K3+2,X ; Store K(3 2 1) in K3+X(2 1 0), starting with the sign ; byte LDA K+2 ; And then doing the high byte STA K3+1,X LDA K+1 ; And finally the low byte STA K3,X RTS ; Return from the subroutine
Name: TAS4 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Calculate the dot product of XX15 and one of the space station's orientation vectors
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOCKIT calls TAS4

Calculate the dot product of the vector in XX15 and one of the space station's orientation vectors, as determined by the value of Y. If vect is the space station orientation vector, we calculate this: (A X) = vect . XX15 = vect_x * XX15 + vect_y * XX15+1 + vect_z * XX15+2 Technically speaking, this routine can also calculate the dot product between XX15 and the sun's orientation vectors, as the sun and space station share the same ship data slot (the second ship data block at K%). However, the sun doesn't have orientation vectors, so this only gets called when that slot is being used for the space station.
Arguments: Y The space station's orientation vector: * If Y = 10, calculate nosev . XX15 * If Y = 16, calculate roofv . XX15 * If Y = 22, calculate sidev . XX15
Returns: (A X) The result of the dot product
.TAS4 LDX K%+NIK%,Y ; Set Q = the Y-th byte of K%+NIK%, i.e. vect_x from the STX Q ; second ship data block at K% LDA XX15 ; Set A = XX15 JSR MULT12 ; Set (S R) = Q * A ; = vect_x * XX15 LDX K%+NIK%+2,Y ; Set Q = the Y+2-th byte of K%+NIK%, i.e. vect_y STX Q LDA XX15+1 ; Set A = XX15+1 JSR MAD ; Set (A X) = Q * A + (S R) ; = vect_y * XX15+1 + vect_x * XX15 STA S ; Set (S R) = (A X) STX R LDX K%+NIK%+4,Y ; Set Q = the Y+2-th byte of K%+NIK%, i.e. vect_z STX Q LDA XX15+2 ; Set A = XX15+2 JMP MAD ; Set: ; ; (A X) = Q * A + (S R) ; = vect_z * XX15+2 + vect_y * XX15+1 + ; vect_x * XX15 ; ; and return from the subroutine using a tail call
Name: TAS6 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Negate the vector in XX15 so it points in the opposite direction
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOCKIT calls TAS6 * TACTICS (Part 6 of 7) calls TAS6 * TACTICS (Part 7 of 7) calls TAS6
.TAS6 LDA XX15 ; Reverse the sign of the x-coordinate of the vector in EOR #%10000000 ; XX15 STA XX15 LDA XX15+1 ; Then reverse the sign of the y-coordinate EOR #%10000000 STA XX15+1 LDA XX15+2 ; And then the z-coordinate, so now the XX15 vector is EOR #%10000000 ; pointing in the opposite direction STA XX15+2 RTS ; Return from the subroutine
Name: DCS1 [Show more] Type: Subroutine Category: Flight Summary: Calculate the vector from the ideal docking position to the ship
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOCKIT calls DCS1

This routine is called by the docking computer routine in DOCKIT. It works out the vector between the ship and the ideal docking position, which is straight in front of the docking slot, but some distance away. Specifically, it calculates the following: * K3(2 1 0) = K3(2 1 0) - nosev_x_hi * 4 * K3(5 4 3) = K3(5 4 3) - nosev_y_hi * 4 * K3(8 7 6) = K3(8 7 6) - nosev_x_hi * 4 where K3 is the vector from the station to the ship, and nosev is the nose vector for the space station. The nose vector points from the centre of the station through the slot, so -nosev * 4 is the vector from a point in front of the docking slot, but some way from the station, back to the centre of the station. Adding this to the vector from the station to the ship gives the vector from the point in front of the station to the ship. In practice, this routine is called twice, so the ideal docking position is actually at a distance of 8 unit vectors from the centre of the station. Back in DOCKIT, we flip this vector round to get the vector from the ship to the point in front of the station slot.
Arguments: K3 The vector from the station to the ship
Returns: K3 The vector from the ship to the ideal docking position (4 unit vectors from the centre of the station for each call to DCS1, so two calls will return the vector to a point that's 8 unit vectors from the centre of the station)
.DCS1 JSR P%+3 ; Run the following routine twice, so the subtractions ; are all * 4 LDA K%+NIK%+10 ; Set A to the space station's byte #10, nosev_x_hi LDX #0 ; Set K3(2 1 0) = K3(2 1 0) - A * 2 JSR TAS7 ; = K3(2 1 0) - nosev_x_hi * 2 LDA K%+NIK%+12 ; Set A to the space station's byte #12, nosev_y_hi LDX #3 ; Set K3(5 4 3) = K3(5 4 3) - A * 2 JSR TAS7 ; = K3(5 4 3) - nosev_y_hi * 2 LDA K%+NIK%+14 ; Set A to the space station's byte #14, nosev_z_hi LDX #6 ; Set K3(8 7 6) = K3(8 7 6) - A * 2 ; = K3(8 7 6) - nosev_x_hi * 2 .TAS7 ; This routine subtracts A * 2 from one of the K3 ; coordinates, as determined by the value of X: ; ; * X = 0, set K3(2 1 0) = K3(2 1 0) - A * 2 ; ; * X = 3, set K3(5 4 3) = K3(5 4 3) - A * 2 ; ; * X = 6, set K3(8 7 6) = K3(8 7 6) - A * 2 ; ; Let's document it for X = 0, i.e. K3(2 1 0) ASL A ; Shift A left one place and move the sign bit into the ; C flag, so A = |A * 2| STA R ; Set R = |A * 2| LDA #0 ; Rotate the sign bit of A from the C flag into the sign ROR A ; bit of A, so A is now just the sign bit from the ; original value of A. This also clears the C flag EOR #%10000000 ; Flip the sign bit of A, so it has the sign of -A EOR K3+2,X ; Give A the correct sign of K3(2 1 0) * -A BMI TS71 ; If the sign of K3(2 1 0) * -A is negative, jump to ; TS71, as K3(2 1 0) and A have the same sign ; If we get here then K3(2 1 0) and A have different ; signs, so we can add them to do the subtraction LDA R ; Set K3(2 1 0) = K3(2 1 0) + R ADC K3,X ; = K3(2 1 0) + |A * 2| STA K3,X ; ; starting with the low bytes BCC TS72 ; If the above addition didn't overflow, we have the ; result we want, so jump to TS72 to return from the ; subroutine INC K3+1,X ; The above addition overflowed, so increment the high ; byte of K3(2 1 0) .TS72 RTS ; Return from the subroutine .TS71 ; If we get here, then K3(2 1 0) and A have the same ; sign LDA K3,X ; Set K3(2 1 0) = K3(2 1 0) - R SEC ; = K3(2 1 0) - |A * 2| SBC R ; STA K3,X ; starting with the low bytes LDA K3+1,X ; And then the high bytes SBC #0 STA K3+1,X BCS TS72 ; If the subtraction didn't underflow, we have the ; result we want, so jump to TS72 to return from the ; subroutine LDA K3,X ; Negate the result in K3(2 1 0) by flipping all the EOR #%11111111 ; bits and adding 1, i.e. using two's complement to ADC #1 ; give it the opposite sign, starting with the low STA K3,X ; bytes LDA K3+1,X ; Then doing the high bytes EOR #%11111111 ADC #0 STA K3+1,X LDA K3+2,X ; And finally, flipping the sign bit EOR #%10000000 STA K3+2,X JMP TS72 ; Jump to TS72 to return from the subroutine
Name: HITCH [Show more] Type: Subroutine Category: Tactics Summary: Work out if the ship in INWK is in our crosshairs Deep dive: In the crosshairs
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 11 of 16) calls HITCH * ANGRY calls via HI1

This is called by the main flight loop to see if we have laser or missile lock on an enemy ship.
Returns: C flag Set if the ship is in our crosshairs, clear if it isn't
Other entry points: HI1 Contains an RTS
.HITCH CLC ; Clear the C flag so we can return with it cleared if ; our checks fail LDA INWK+8 ; Set A = z_sign BNE HI1 ; If A is non-zero then the ship is behind us and can't ; be in our crosshairs, so return from the subroutine ; with the C flag clear (as HI1 contains an RTS) LDA TYPE ; If the ship type has bit 7 set then it is the planet BMI HI1 ; or sun, which we can't target or hit with lasers, so ; return from the subroutine with the C flag clear (as ; HI1 contains an RTS) LDA INWK+31 ; Fetch bit 5 of byte #31 (the exploding flag) and OR AND #%00100000 ; with x_hi and y_hi ORA INWK+1 ORA INWK+4 BNE HI1 ; If this value is non-zero then either the ship is ; exploding (so we can't target it), or the ship is too ; far away from our line of fire to be targeted, so ; return from the subroutine with the C flag clear (as ; HI1 contains an RTS) LDA INWK ; Set A = x_lo JSR SQUA2 ; Set (A P) = A * A = x_lo^2 STA S ; Set (S R) = (A P) = x_lo^2 LDA P STA R LDA INWK+3 ; Set A = y_lo JSR SQUA2 ; Set (A P) = A * A = y_lo^2 TAX ; Store the high byte in X LDA P ; Add the two low bytes, so: ADC R ; STA R ; R = P + R TXA ; Restore the high byte into A and add S to give the ADC S ; following: ; ; (A R) = (S R) + (A P) = x_lo^2 + y_lo^2 BCS TN10 ; If the addition just overflowed then there is no way ; our crosshairs are within the ship's targetable area, ; so return from the subroutine with the C flag clear ; (as TN10 contains a CLC then an RTS) STA S ; Set (S R) = (A P) = x_lo^2 + y_lo^2 LDY #2 ; Fetch the ship's blueprint and set A to the high byte JSR GetShipBlueprint ; of the targetable area of the ship CMP S ; We now compare the high bytes of the targetable area ; and the calculation in (S R): ; ; * If A >= S then then the C flag will be set ; ; * If A < S then the C flag will be C clear BNE HI1 ; If A <> S we have just set the C flag correctly, so ; return from the subroutine (as HI1 contains an RTS) DEY ; The high bytes were identical, so now we fetch the JSR GetShipBlueprint ; low byte of the targetable area into A CMP R ; We now compare the low bytes of the targetable area ; and the calculation in (S R): ; ; * If A >= R then the C flag will be set ; ; * If A < R then the C flag will be C clear .HI1 RTS ; Return from the subroutine .TN10 CLC ; Clear the C flag to indicate the ship is not in our ; crosshairs RTS ; Return from the subroutine
Name: FRS1 [Show more] Type: Subroutine Category: Tactics Summary: Launch a ship straight ahead of us, below the laser sights
Context: See this subroutine on its own page References: This subroutine is called as follows: * ESCAPE calls FRS1 * FRMIS calls FRS1 * DEATH calls via fq1

This is used in two places: * When we launch a missile, in which case the missile is the ship that is launched ahead of us * When we launch our escape pod, in which case it's our abandoned Cobra Mk III that is launched ahead of us * The fq1 entry point is used to launch a bunch of cargo canisters ahead of us as part of the death screen
Arguments: X The type of ship to launch ahead of us
Returns: C flag Set if the ship was successfully launched, clear if it wasn't (as there wasn't enough free memory)
Other entry points: fq1 Used to add a cargo canister to the universe
.FRS1 JSR ZINF ; Call ZINF to reset the INWK ship workspace LDA #28 ; Set y_lo = 28 STA INWK+3 LSR A ; Set z_lo = 14, so the launched ship starts out STA INWK+6 ; ahead of us LDA #%10000000 ; Set y_sign to be negative, so the launched ship is STA INWK+5 ; launched just below our line of sight LDA MSTG ; Set A to the missile lock target, shifted left so the ASL A ; slot number is in bits 1-5 ORA #%10000000 ; Set bit 7 and store the result in byte #32, the AI STA INWK+32 ; flag launched ship for the launched ship. For missiles ; this enables AI (bit 7), makes it friendly towards us ; (bit 6), sets the target to the value of MSTG (bits ; 1-5), and sets its lock status as launched (bit 0). ; It doesn't matter what it does for our abandoned ; Cobra, as the AI flag gets overwritten once we return ; from the subroutine back to the ESCAPE routine that ; called FRS1 in the first place .fq1 LDA #$60 ; Set byte #14 (nosev_z_hi) to 1 ($60), so the launched STA INWK+14 ; ship is pointing away from us ORA #128 ; Set byte #22 (sidev_x_hi) to -1 ($D0), so the launched STA INWK+22 ; ship has the same orientation as spawned ships, just ; pointing away from us (if we set sidev to +1 instead, ; this ship would be a mirror image of all the other ; ships, which are spawned with -1 in nosev and +1 in ; sidev) LDA DELTA ; Set byte #27 (speed) to 2 * DELTA, so the launched ROL A ; ship flies off at twice our speed STA INWK+27 TXA ; Add a new ship of type X to our local bubble of JMP NWSHP ; universe and return from the subroutine using a tail ; call
Name: FRMIS [Show more] Type: Subroutine Category: Tactics Summary: Fire a missile from our ship Deep dive: The NES combat demo
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 3 of 16) calls FRMIS

We fired a missile, so send it streaking away from us to unleash mayhem and destruction on our sworn enemies.
.FRMIS LDX #MSL ; Call FRS1 to launch a missile straight ahead of us JSR FRS1 BCC FR1 ; If FRS1 returns with the C flag clear, then there ; isn't room in the universe for our missile, so jump ; down to FR1 to display a "missile jammed" message LDX MSTG ; Fetch the slot number of the missile's target JSR GINF ; Get the address of the data block for the target ship ; and store it in INF LDA FRIN,X ; Fetch the ship type of the missile's target into A JSR ANGRY ; Call ANGRY to make the target ship hostile LDY #133 ; We have just launched a missile, so we need to remove JSR ABORT ; missile lock and hide the active indicator on the ; dashboard by setting it to the pattern number in Y ; (no missile indicator = pattern 133) DEC NOMSL ; Reduce the number of missiles we have by 1 LDA demoInProgress ; If the demo is not in progress, jump to frmi1 to skip BEQ frmi1 ; the following ; If we get here then the demo is in progress and we ; just fired a missile, so we get a 60-second penalty ; added to the time taken to complete the demo LDA #147 ; Print recursive token 146 ("60 SECOND PENALTY") in LDY #10 ; the middle of the screen and leave it there for 10 JSR PrintMessage ; ticks of the DLY counter LDA #25 ; Set nmiTimer = 25 to add half a second on top of the STA nmiTimer ; penalty below (as 25 frames is half a second in PAL ; systems) LDA nmiTimerLo ; Add 60 to (nmiTimerHi nmiTimerLo) so the time recorded CLC ; to complete the combat demo is 60 seconds longer than ADC #60 ; it would have been if we hadn't fired the missile STA nmiTimerLo BCC frmi1 INC nmiTimerHi .frmi1 LDY #9 ; Call the NOISE routine with Y = 9 to make the sound JMP NOISE ; of a missile launch, returning from the subroutine ; using a tail call
Name: ANGRY [Show more] Type: Subroutine Category: Tactics Summary: Make a ship hostile
Context: See this subroutine on its own page References: This subroutine is called as follows: * FRMIS calls ANGRY * Main flight loop (Part 11 of 16) calls ANGRY

All this routine does is set the ship's hostile flag, start it turning and give it a kick of acceleration - later calls to TACTICS will make the ship start to attack us.
Arguments: A The type of ship we're going to irritate INF The address of the data block for the ship we're going to infuriate
.ANGRY CMP #SST ; If this is the space station, jump to AN2 to make the BEQ AN2 ; space station hostile LDY #36 ; Fetch the ship's NEWB flags from byte #36 LDA (INF),Y AND #%00100000 ; If bit 5 of the ship's NEWB flags is clear, skip the BEQ P%+5 ; following instruction, otherwise bit 5 is set, meaning ; this ship is an innocent bystander, and attacking it ; will annoy the space station JSR AN2 ; Call AN2 to make the space station hostile LDY #32 ; Fetch the ship's byte #32 (AI flag) LDA (INF),Y BEQ HI1 ; If the AI flag is zero then this ship has no AI and ; it can't get hostile, so return from the subroutine ; (as HI1 contains an RTS) ORA #%10000000 ; Otherwise set bit 7 (AI enabled) to ensure AI is STA (INF),Y ; definitely enabled LDY #28 ; Set the ship's byte #28 (acceleration) to 2, so it LDA #2 ; speeds up STA (INF),Y ASL A ; Set the ship's byte #30 (pitch counter) to 4, so it LDY #30 ; starts diving STA (INF),Y LDA TYPE ; If the ship's type is < #CYL (i.e. a missile, Coriolis CMP #CYL ; space station, escape pod, plate, cargo canister, BCC AN3 ; boulder, asteroid, splinter, Shuttle or Transporter), ; then jump to AN3 to skip the following LDY #36 ; Set bit 2 of the ship's NEWB flags in byte #36 to LDA (INF),Y ; make this ship hostile ORA #%00000100 STA (INF),Y .AN3 RTS ; Return from the subroutine .AN2 LDA K%+NIK%+36 ; Set bit 2 of the NEWB flags in byte #36 of the second ORA #%00000100 ; ship in the ship data workspace at K%, which is STA K%+NIK%+36 ; reserved for the sun or the space station (in this ; case it's the latter), to make it hostile RTS ; Return from the subroutine
Name: FR1 [Show more] Type: Subroutine Category: Tactics Summary: Display the "missile jammed" message
Context: See this subroutine on its own page References: This subroutine is called as follows: * FRMIS calls FR1

This is shown if there isn't room in the local bubble of universe for a new missile.
Other entry points: FR1-2 Clear the C flag and return from the subroutine
.FR1 LDA #201 ; Print recursive token 41 ("MISSILE JAMMED") as an JMP MESS ; in-flight message and return from the subroutine using ; a tail call
Name: SESCP [Show more] Type: Subroutine Category: Flight Summary: Spawn an escape pod from the current (parent) ship
Context: See this subroutine on its own page References: This subroutine is called as follows: * TACTICS (Part 4 of 7) calls SESCP

This is called when an enemy ship has run out of both energy and luck, so it's time to bail.
.SESCP LDX #ESC ; Set X to the ship type for an escape pod LDA #%11111110 ; Set A to an AI flag that has AI enabled, is hostile, ; but has no E.C.M. ; Fall through into SFS1 to spawn the escape pod
Name: SFS1 [Show more] Type: Subroutine Category: Universe Summary: Spawn a child ship from the current (parent) ship
Context: See this subroutine on its own page References: This subroutine is called as follows: * SPIN calls SFS1 * TACTICS (Part 2 of 7) calls SFS1 * TACTICS (Part 5 of 7) calls SFS1 * SFRMIS calls via SFS1-2

If the parent is a space station then the child ship is spawned coming out of the slot, and if the child is a cargo canister, it is sent tumbling through space. Otherwise the child ship is spawned with the same ship data as the parent, just with damping disabled and the ship type and AI flag that are passed in A and X.
Arguments: A AI flag for the new ship (see the documentation on ship data byte #32 for details) X The ship type of the child to spawn INF Address of the parent's ship data block TYPE The type of the parent ship
Returns: C flag Set if ship successfully added, clear if it failed INF INF is preserved XX0 XX0 is preserved INWK The whole INWK workspace is preserved X X is preserved
Other entry points: SFS1-2 Add a missile to the local bubble that has AI enabled, is hostile, but has no E.C.M.
.SFS1 STA T1 ; Store the child ship's AI flag in T1 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 ; Before spawning our child ship, we need to save the ; INF and XX00 variables and the whole INWK workspace, ; so we can restore them later when returning from the ; subroutine TXA ; Store X, the ship type to spawn, on the stack so we PHA ; can preserve it through the routine LDA XX0 ; Store XX0(1 0) on the stack, so we can restore it PHA ; later when returning from the subroutine LDA XX0+1 PHA LDA INF ; Store INF(1 0) on the stack, so we can restore it PHA ; later when returning from the subroutine LDA INF+1 PHA LDY #NI%-1 ; Now we want to store the current INWK data block in ; temporary memory so we can restore it when we are ; done, and we also want to copy the parent's ship data ; into INWK, which we can do at the same time, so set up ; a counter in Y for NI% bytes .FRL2 LDA INWK,Y ; Copy the Y-th byte of INWK to the Y-th byte of STA XX3,Y ; temporary memory in XX3, so we can restore it later ; when returning from the subroutine LDA (INF),Y ; Copy the Y-th byte of the parent ship's data block to STA INWK,Y ; the Y-th byte of INWK DEY ; Decrement the loop counter BPL FRL2 ; Loop back to copy the next byte until we have done ; them all ; INWK now contains the ship data for the parent ship, ; so now we need to tweak the data before creating the ; new child ship (in this way, the child inherits things ; like location from the parent) LDA TYPE ; Fetch the ship type of the parent into A CMP #SST ; If the parent is not a space station, jump to rx to BNE rx ; skip the following ; The parent is a space station, so the child needs to ; launch out of the space station's slot. The space ; station's nosev vector points out of the station's ; slot, so we want to move the ship along this vector. ; We do this by taking the unit vector in nosev and ; doubling it, so we spawn our ship 2 units along the ; vector from the space station's centre TXA ; Store the child's ship type in X on the stack PHA LDA #32 ; Set the child's byte #27 (speed) to 32 STA INWK+27 LDX #0 ; Add 2 * nosev_x_hi to (x_lo, x_hi, x_sign) to get the LDA INWK+10 ; child's x-coordinate JSR SFS2 LDX #3 ; Add 2 * nosev_y_hi to (y_lo, y_hi, y_sign) to get the LDA INWK+12 ; child's y-coordinate JSR SFS2 LDX #6 ; Add 2 * nosev_z_hi to (z_lo, z_hi, z_sign) to get the LDA INWK+14 ; child's z-coordinate JSR SFS2 PLA ; Restore the child's ship type from the stack into X TAX .rx LDA T1 ; Restore the child ship's AI flag from T1 and store it STA INWK+32 ; in the child's byte #32 (AI) LSR INWK+29 ; Clear bit 0 of the child's byte #29 (roll counter) so ASL INWK+29 ; that its roll dampens (so if we are spawning from a ; space station, for example, the spawned ship won't ; keep rolling forever) TXA ; Copy the child's ship type from X into A CMP #SPL+1 ; If the type of the child we are spawning is less than BCS NOIL ; #PLT or greater than #SPL - i.e. not an alloy plate, CMP #PLT ; cargo canister, boulder, asteroid or splinter - then BCC NOIL ; jump to NOIL to skip us setting up some pitch and roll ; for it PHA ; Store the child's ship type on the stack so we can ; retrieve it below JSR DORND ; Set A and X to random numbers ASL A ; Set the child's byte #30 (pitch counter) to a random STA INWK+30 ; value, and at the same time set the C flag randomly TXA ; Set the child's byte #27 (speed) to a random value AND #%00001111 ; between 0 and 15 STA INWK+27 LDA #$FF ; Set the child's byte #29 (roll counter) to a full ROR A ; roll with no damping (as bits 0 to 6 are set), so the STA INWK+29 ; canister tumbles through space, with the direction in ; bit 7 set randomly, depending on the C flag from above PLA ; Retrieve the child's ship type from the stack .NOIL JSR NWSHP ; Add a new ship of type A to the local bubble ; We have now created our child ship, so we need to ; restore all the variables we saved at the start of ; the routine, so they are preserved when we return ; from the subroutine PLA ; Restore INF(1 0) from the stack STA INF+1 PLA STA INF PHP ; Store the flags on the stack to we can retrieve them ; after the macro JSR SetupPPUForIconBar ; 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 LDX #NI%-1 ; Now to restore the INWK workspace that we saved into ; XX3 above, so set a counter in X for NI% bytes .FRL3 LDA XX3,X ; Copy the Y-th byte of XX3 to the Y-th byte of INWK STA INWK,X DEX ; Decrement the loop counter BPL FRL3 ; Loop back to copy the next byte until we have done ; them all PLA ; Restore XX0(1 0) from the stack STA XX0+1 PLA STA XX0 PLA ; Retrieve the ship type to spawn from the stack into X TAX ; so it is preserved through calls to this routine RTS ; Return from the subroutine
Name: SFS2 [Show more] Type: Subroutine Category: Moving Summary: Move a ship in space along one of the coordinate axes
Context: See this subroutine on its own page References: This subroutine is called as follows: * SFS1 calls SFS2

Move a ship's coordinates by a certain amount in the direction of one of the axes, where X determines the axis. Mathematically speaking, this routine translates the ship along a single axis by a signed delta.
Arguments: A The amount of movement, i.e. the signed delta X Determines which coordinate axis of INWK to move: * X = 0 moves the ship along the x-axis * X = 3 moves the ship along the y-axis * X = 6 moves the ship along the z-axis
.SFS2 ASL A ; Set R = |A * 2|, with the C flag set to bit 7 of A STA R LDA #0 ; Set bit 7 of A to the C flag, i.e. the sign bit from ROR A ; the original argument in A JMP MVT1 ; Add the delta R with sign A to (x_lo, x_hi, x_sign) ; (or y or z, depending on the value in X) and return ; from the subroutine using a tail call
Name: LAUN [Show more] Type: Subroutine Category: Flight Summary: Make the launch sound and draw the launch tunnel
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOENTRY calls LAUN * TT110 calls LAUN

This is shown when launching from or docking with the space station. This routine does a similar job to the routine of the same name in the BBC Master version of Elite, but the code is significantly different.
.LAUN LDA #$00 ; Clear the screen and set the view type in QQ11 to $00 JSR ChangeToView ; (Space view with no fonts loaded) JSR HideMostSprites ; Hide all sprites except for sprite 0 and the icon bar ; pointer LDY #12 ; Call the NOISE routine with Y = 12 to make the first JSR NOISE ; sound of the ship launching from the station LDA #128 ; Set K+2 = 128 to send to DrawLaunchBox and STA K+2 ; DrawLightning as the x-coordinate of the centre of the ; boxes and lightning (so they are centred on-screen) LDA halfScreenHeight ; Set K+3 to half the screen height to send to STA K+3 ; DrawLaunchBox and DrawLightning as the y-coordinate of ; the centre of the boxes and lightning (so they are ; centred on-screen) LDA #80 ; Set XP to use as a counter for the duration of the STA XP ; hyperspace effect, so we run the following loop 80 ; times LDA #112 ; Set YP to use as a counter for when we show the STA YP ; lightning effect at the end of the tunnel, which we ; show when YP < 100 (so we wait until 13 frames have ; passed before drawing the lightning) ; ; Also, at the start of each frame, we keep subtracting ; 16 from STP until STP < YP, and only then do we start ; drawing boxes and, possibly, the lightning LDY #4 ; Wait until four NMI interrupts have passed (i.e. the JSR DELAY ; next four VBlanks) LDY #24 ; Call the NOISE routine with Y = 24 to make the second JSR NOISE ; sound of the ship launching from the station .laun1 JSR CheckForPause-3 ; Check whether the pause button has been pressed or an ; icon bar button has been chosen, and process pause or ; unpause if a pause-related button has been pressed JSR FlipDrawingPlane ; Flip the drawing bitplane so we draw into the bitplane ; that isn't visible on-screen LDA XP ; Set STP = 96 + (XP mod 16) AND #15 ; ORA #96 ; STA STP ; So over the course of the 80 iterations around the ; loop, STP starts at 96, then counts down from 112 to ; 96, and keeps repeating this countdown until XP is ; zero and STP is 96 ; ; The higher the value of STP, the closer together the ; lines in the tunnel, so this makes the tunnel lines ; move further away as the animation progresses, giving ; a feeling of moving forwards through the tunnel LDA #%10000000 ; Set bit 7 of firstBox so we can detect when we are on STA firstBox ; the first iteration of the laun2 loop ; We now draw the boxes in the launch tunnel effect, ; looping back to hype2 for each new line ; ; STP gets decremented by 16 for each box, so STP is ; set to the starting point (in the range 96 to 112), ; and gets decremented by 16 for each box until it is ; negative ; ; As STP decreases, the boxes get bigger, so this loop ; draws the boxes from the smallest in the middle and ; working out towards the edges .laun2 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 STP ; Set STP = STP + 16 SEC ; SBC #16 ; And set A to the new value of STP BMI laun4 ; If STP is now negative, then jump to laun4 to move on ; to the next frame, so we stop drawing boxes in this ; frame STA STP ; Update STP with the new value CMP YP ; If STP >= YP, jump to laun2 to keep reducing STP until BCS laun2 ; STP < YP STA Q ; Set Q to the new value of STP ; We now calculate how far the top edge of this box is ; from the centre of the screen in a vertical direction, ; with the result being boxes that are closer together, ; the closer they are to the centre ; ; We space out the boxes using a reciprocal algorithm, ; where the distance of line n from the centre is ; proportional to 1/n, so the boxes get spaced roughly ; in the proportions of 1/2, 1/3, 1/4, 1/5 and so on, so ; the boxes bunch closer together as n increases ; ; STP also includes the iteration number, modded so it ; runs from 15 to 0, so over the course of the animation ; the boxes move away from the centre line, as the ; iteration decreases and the value of R below increases LDA #8 ; Set A = 8 to use in the following division JSR LL28 ; Call LL28 to calculate: ; ; R = 256 * A / Q ; = 256 * 8 / STP ; ; So R is the vertical distance of the current box top ; top from the centre of the screen ; ; The maximum value of STP is 112 - 16 = 96, and the ; minimum is 0 (the latter being enforced by the ; comparison above), so R ranges from 21 to 255 LDA R ; Set A = R - 20 SEC ; SBC #20 ; This sets the range of values in A to 1 to 235 ; We can now use A as half the height of the box to ; draw, to give us an effect where the boxes are more ; spread out as they get taller, and which get bigger ; as the animation progresses, with the difference in ; size between frames being more pronounced with the ; bigger boxes CMP #84 ; If A >= 84, jump to laun4 to move on to the next frame BCS laun4 ; as the box is outside the edges of the screen (so we ; can stop drawing lines in this frame as we have now ; drawn them all) STA K+1 ; Set K+1 = A to send to DrawLaunchBox and DrawLightning ; as half the height of the box/lightning LSR A ; Set K = 1.5 * K+1 to send to DrawLaunchBox and ADC K+1 ; DrawLightning as half the width of the box, so the box STA K ; is 50% wider than it is tall (as it's a space station ; slot) ASL firstBox ; Set the C flag to bit 7 of firstBox and zero firstBox ; (as we know that only bit 7 of firstBox is set before ; the shift) BCC laun3 ; If the C flag is clear then this is not the first ; iteration of the laun2 loop for this frame, so jump to ; laun3 to draw the box, skipping the lightning (so we ; only draw the lightning on the first iteration of ; laun2 for this frame ; If we get here then this is the first iteration of the ; laun2 loop for this frame, so we consider drawing the ; lightning effect at the end of the tunnel LDA YP ; If YP >= 100, jump to laun3 to skip drawing the CMP #100 ; lightning as the tunnel exit is too small to contain BCS laun3 ; the lightning effect LDA K+1 ; If K+1 >= 72, jump to laun5 to draw the lightning but CMP #72 ; without drawing any boxes, as the first box (which is BCS laun5 ; the smallest) doesn't fit on-screen LDA STP ; Store the value of STP on the stack so we can retrieve PHA ; it after the call to DrawLightning (as DrawLightning ; corrupts the value of STP) JSR DrawLightning_b6 ; Call DrawLightning to draw the lightning effect at the ; end of the tunnel, centred on the centre of the screen ; and in a rectangle with a half-height of K and a ; half-width of 1.5 * K (so it fits within the smallest ; launch box) PLA ; Restore the value of STP that we stored on the stack STA STP .laun3 JSR DrawLaunchBox_b6 ; Draw a box centred on the centre of the screen, with a ; half-height of K and a half-width of 1.5 * K JMP laun2 ; Loop back to laun2 to draw the next box .laun4 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 DEC YP ; Decrement the lightning counter in YP DEC XP ; Decrement the frame counter in XP BNE laun1 ; Loop back to laun1 to draw the next frame of the ; animation, until the frame counter runs down to 0 LDY #23 ; Call the NOISE routine with Y = 23 to make the third JMP NOISE ; sound of the ship launching from the station, ; returning from the subroutine using a tail call .laun5 ; We call this from the first iteration of the loop in ; this frame, and when K+1 >= 72, which means that the ; first box in this frame (which will be the smallest) ; is too big for the screen, so we just draw the ; lightning effect and don't draw any boxes LDA #72 ; Set K+1 = 72 to pass to DrawLightning as half the STA K+1 ; height of the lightning effect LDA STP ; Store the value of STP on the stack so we can retrieve PHA ; it after the call to DrawLightning (as DrawLightning ; corrupts the value of STP) JSR DrawLightning_b6 ; Call DrawLightning to draw the lightning effect at the ; end of the tunnel, centred on the centre of the screen ; and in a rectangle with a half-height of K and a ; half-width of 1.5 * K (so it fits within the smallest ; launch box) PLA ; Restore the value of STP that we stored on the stack STA STP JMP laun2 ; Loop back to laun2 to draw the next box RTS ; Return from the subroutine
Name: LASLI [Show more] Type: Subroutine Category: Drawing lines Summary: Draw the laser lines for when we fire our lasers
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 3 of 16) calls LASLI

Draw the laser lines, aiming them to slightly different place each time so they appear to flicker and dance. Also heat up the laser temperature and drain some energy.
Other entry points: LASLI-1 Contains an RTS
.LASLI JSR DORND ; Set A and X to random numbers AND #7 ; Restrict A to a random value in the range 0 to 7 ADC halfScreenHeight ; Set LASY to two pixels above the centre of the SBC #2 ; screen (in halfScreenHeight), plus our random number, STA LASY ; so the laser dances above and below the centre point JSR DORND ; Set A and X to random numbers AND #7 ; Restrict A to a random value in the range 0 to 7 ADC #X-4 ; Set LASX to four pixels left of the centre of the STA LASX ; screen (#X), plus our random number, so the laser ; dances to the left and right of the centre point LDA GNTMP ; Add 6 to the laser temperature in GNTMP ADC #6 STA GNTMP JSR DENGY ; Call DENGY to deplete our energy banks by 1 LDA QQ11 ; If this is not a space view (i.e. QQ11 is non-zero) BNE LASLI-1 ; then jump to MA9 to return from the main flight loop ; (as LASLI-1 is an RTS) LDA #32 ; Set A = 32 and Y = 224 for the first set of laser LDY #224 ; lines (the wider pair of lines) JSR las ; Call las below to draw the first set of laser lines LDA #48 ; Fall through into las with A = 48 and Y = 208 to draw LDY #208 ; a second set of lines (the narrower pair) ; The following routine draws two laser lines, one from ; the centre point down to point A on the bottom row, ; and the other from the centre point down to point Y ; on the bottom row. We therefore get lines from the ; centre point to points 32, 48, 208 and 224 along the ; bottom row, giving us the triangular laser effect ; we're after .las STA X2 ; Set X2 = A LDA LASX ; Set (X1, Y1) to the random centre point we set above STA X1 LDA LASY STA Y1 LDA Yx2M1 ; Set Y2 to the height in pixels of the space view, STA Y2 ; which is in the variable Yx2M1, so this sets Y2 to ; the y-coordinate of the bottom pixel row of the space ; view JSR LOIN ; Draw a line from (X1, Y1) to (X2, Y2), so that's from ; the centre point to (A, 191) LDA LASX ; Set (X1, Y1) to the random centre point we set above STA X1 LDA LASY STA Y1 STY X2 ; Set X2 = Y LDA Yx2M1 ; Set Y2 to the y-coordinate of the bottom pixel row STA Y2 ; of the space view (as before) JMP LOIN ; Draw a line from (X1, Y1) to (X2, Y2), so that's from ; the centre point to (Y, 191), and return from ; the subroutine using a tail call
Name: BRIEF2 [Show more] Type: Subroutine Category: Missions Summary: Start mission 2 Deep dive: The Thargoid Plans mission
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOENTRY calls BRIEF2
.BRIEF2 LDA TP ; Set bit 2 of TP to indicate mission 2 is in progress ORA #%00000100 ; but plans have not yet been picked up STA TP LDA #11 ; Print extended token 11, which is the initial contact JSR DETOK_b2 ; at the start of mission 2, asking us to head for ; Ceerdi for a mission briefing JSR UpdateView ; Update the view JMP BAY ; Jump to BAY to go to the docking bay (i.e. show the ; Status Mode screen) and return from the subroutine ; using a tail call
Name: BRP [Show more] Type: Subroutine Category: Missions Summary: Print an extended token and show the Status Mode screen
Context: See this subroutine on its own page References: This subroutine is called as follows: * BRIEF calls BRP * BRIEF3 calls BRP * DEBRIEF calls BRP * DEBRIEF2 calls BRP * TBRIEF calls via BAYSTEP

Other entry points: BAYSTEP Go to the docking bay (i.e. show the Status Mode screen)
.BRP JSR DETOK_b2 ; Print the extended token in A JSR FadeToBlack_b3 ; Fade the screen to black over the next four VBlanks .BAYSTEP JMP BAY ; Jump to BAY to go to the docking bay (i.e. show the ; Status Mode screen) and return from the subroutine ; using a tail call
Name: BRIEF3 [Show more] Type: Subroutine Category: Missions Summary: Receive the briefing and plans for mission 2 Deep dive: The Thargoid Plans mission
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOENTRY calls BRIEF3
.BRIEF3 LDA TP ; Set bits 1 and 3 of TP to indicate that mission 1 is AND #%11110000 ; complete, and mission 2 is in progress and the plans ORA #%00001010 ; have been picked up STA TP LDA #222 ; Set A = 222 so the call to BRP prints extended token ; 222 (the briefing for mission 2 where we pick up the ; plans we need to take to Birera) BNE BRP ; Jump to BRP to print the extended token in A and show ; the Status Mode screen), returning from the subroutine ; using a tail call (this BNE is effectively a JMP as A ; is never zero)
Name: DEBRIEF2 [Show more] Type: Subroutine Category: Missions Summary: Finish mission 2 Deep dive: The Thargoid Plans mission
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOENTRY calls DEBRIEF2
.DEBRIEF2 LDA TP ; Set bit 2 of TP to indicate mission 2 is complete (so ORA #%00000100 ; both bits 2 and 3 are now set) STA TP LDA #2 ; Set ENGY to 2 so our energy banks recharge at a faster STA ENGY ; rate, as our mission reward is a special navy energy ; unit that recharges at a rate of 3 units of energy on ; each iteration of the main loop, compared to a rate of ; 2 units of energy for the standard energy unit INC TALLY+1 ; Award 256 kill points for completing the mission LDA #223 ; Set A = 223 so the call to BRP prints extended token ; 223 (the thank you message at the end of mission 2) BNE BRP ; Jump to BRP to print the extended token in A and show ; the Status Mode screen), returning from the subroutine ; using a tail call (this BNE is effectively a JMP as A ; is never zero)
Name: DEBRIEF [Show more] Type: Subroutine Category: Missions Summary: Finish mission 1 Deep dive: The Constrictor mission
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOENTRY calls DEBRIEF

Other entry points: BRPS Print the extended token in A, show the Status Mode screen and return from the subroutine
.DEBRIEF LSR TP ; Clear bit 0 of TP to indicate that mission 1 is no ASL TP ; longer in progress, as we have completed it LDX #LO(50000) ; Increase our cash reserves by the generous mission LDY #HI(50000) ; reward of 5,000 CR JSR MCASH LDA #15 ; Set A = 15 so the call to BRP prints extended token 15 ; (the thank you message at the end of mission 1) .BRPS BNE BRP ; Jump to BRP to print the extended token in A and show ; the Status Mode screen, returning from the subroutine ; using a tail call (this BNE is effectively a JMP as A ; is never zero)
Name: TBRIEF [Show more] Type: Subroutine Category: Missions Summary: Start mission 3 Deep dive: The Trumbles mission
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOENTRY calls TBRIEF
.TBRIEF JSR ClearScreen_b3 ; Clear the screen by zeroing patterns 66 to 255 in ; both pattern buffer, and clearing both nametable ; buffers to the background tile LDA #$95 ; Clear the screen and set the view type in QQ11 to $95 JSR TT66 ; (Text-based mission briefing) LDA TP ; Set bit 4 of TP to indicate that mission 3 has been ORA #%00010000 ; triggered STA TP LDA #199 ; Print extended token 199, which is the briefing for JSR DETOK_b2 ; the Trumbles mission JSR UpdateView ; Update the view JSR YESNO ; Call YESNO to wait until either "Y" or "N" is pressed CMP #1 ; If "N" was pressed, then the mission was not accepted, BNE BAYSTEP ; jump to BAYSTEP to go to the docking bay (i.e. show ; the Status Mode screen) LDY #HI(50000) ; Otherwise the mission was accepted, so subtract LDX #LO(50000) ; 50,000 CR from the cash pot to pay for the Trumble JSR LCASH INC TRIBBLE ; Increment the number of Trumbles from 0 to 1, so they ; start breeding JMP BAY ; Go to the docking bay (i.e. show the Status Mode ; screen)
Name: BRIEF [Show more] Type: Subroutine Category: Missions Summary: Start mission 1 and show the mission briefing Deep dive: The Constrictor mission
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOENTRY calls BRIEF

This routine does the following: * Clear the screen * Display "INCOMING MESSAGE" in the middle of the screen * Wait for 2 seconds * Clear the screen * Show the Constrictor rolling and pitching in the middle of the screen * Do this for 64 loop iterations * Move the ship away from us and up until it's near the top of the screen * Show the mission 1 briefing in extended token 10 The mission briefing ends with a "{display ship, wait for key press}" token, which calls the PAUSE routine. This continues to display the rotating ship, waiting until a key is pressed, and then removes the ship from the screen.
.BRIEF LSR TP ; Set bit 0 of TP to indicate that mission 1 is now in SEC ; progress ROL TP JSR BRIS_b0 ; Call BRIS to clear the screen, display "INCOMING ; MESSAGE" and wait for 2 seconds JSR ZINF ; Call ZINF to reset the INWK ship workspace LDA #CON ; Set the ship type in TYPE to the Constrictor STA TYPE JSR NWSHP ; Add a new Constrictor to the local bubble (in this ; case, the briefing screen) JSR HideFromScanner_b1 ; Hide the ship from the scanner LDA #1 ; Move the text cursor to column 1 STA XC LDA #1 ; This instruction has no effect, as A is already 1 STA INWK+7 ; Set z_hi = 1, the distance at which we show the ; rotating ship LDA #80 ; Set z_lo = 80 to set the low byte of the distance at STA INWK+6 ; which we show the rotating ship JSR FadeAndHideSprites ; Fade the screen to black and hide all sprites, so we ; can update the screen while it's blacked-out LDA #$92 ; Clear the screen and set the view type in QQ11 to $92 JSR ChangeToView ; (Mission 1 briefing: rotating ship) LDA #64 ; Set the main loop counter to 64, so the ship rotates STA MCNT ; for 64 iterations through MVEIT .BRL1 LDX #%01111111 ; Set the ship's roll counter to a positive roll that STX INWK+29 ; doesn't dampen (a clockwise roll) STX INWK+30 ; Set the ship's pitch counter to a positive pitch that ; doesn't dampen (a diving pitch) JSR DrawShipInBitplane ; Flip the drawing bitplane and draw the current ship in ; the newly flipped bitplane JSR MVEIT ; Call MVEIT to rotate the ship in space DEC MCNT ; Decrease the counter in MCNT BNE BRL1 ; Loop back to keep moving the ship until we have done ; all 64 iterations .BRL2 LSR INWK ; Halve x_lo so the Constrictor moves towards the centre INC INWK+6 ; Increment z_lo so the Constrictor moves away from us BEQ BR2 ; If z_lo = 0 (i.e. it just went past 255), jump to BR2 ; to show the briefing INC INWK+6 ; Increment z_lo so the Constrictor moves a bit further ; away from us BEQ BR2 ; If z_lo = 0 (i.e. it just went past 255), jump out of ; the loop to BR2 to stop moving the ship up the screen ; and show the briefing LDX INWK+3 ; Set X = y_lo + 1 INX CPX #100 ; If X < 100 then skip the next instruction BCC P%+4 LDX #100 ; X is bigger than 100, so set X = 100 so that X has a ; maximum value of 100 STX INWK+3 ; Set y_lo = X ; = y_lo + 1 ; ; so the ship moves up the screen (as space coordinates ; have the y-axis going up) JSR DrawShipInBitplane ; Flip the drawing bitplane and draw the current ship in ; the newly flipped bitplane JSR MVEIT ; Call MVEIT to move and rotate the ship in space DEC MCNT ; Decrease the counter in MCNT JMP BRL2 ; Loop back to keep moving the ship up the screen and ; away from us .BR2 INC INWK+7 ; Increment z_hi, to keep the ship at the same distance ; as we just incremented z_lo past 255 LDA #$93 ; Clear the screen and set the view type in QQ11 to $93 JSR TT66 ; (Mission 1 briefing: ship and text) LDA #10 ; Set A = 10 so the call to BRP prints extended token 10 ; (the briefing for mission 1 where we find out all ; about the stolen Constrictor) JMP BRP ; Jump to BRP to print the extended token in A and show ; the Status Mode screen, returning from the subroutine ; using a tail call
Name: BRIS_b0 [Show more] Type: Subroutine Category: Missions Summary: Clear the screen, display "INCOMING MESSAGE" and wait for 2 seconds
Context: See this subroutine on its own page References: This subroutine is called as follows: * BRIEF calls BRIS_b0
.BRIS_b0 LDA #216 ; Print extended token 216 ("{clear screen}{tab 6}{move JSR DETOK_b2 ; to row 10, white, lower case}{white}{all caps}INCOMING ; MESSAGE" JSR UpdateViewWithFade ; Update the view, fading the screen to black first if ; required LDY #100 ; Delay for 100 vertical syncs (100/50 = 2 seconds) and JMP DELAY ; return from the subroutine using a tail call
Name: ping [Show more] Type: Subroutine Category: Universe Summary: Set the selected system to the current system
Context: See this subroutine on its own page References: This subroutine is called as follows: * BR1 calls ping * SetupAfterLoad calls ping * TT102 calls ping
.ping LDX #1 ; We want to copy the X- and Y-coordinates of the ; current system in (QQ0, QQ1) to the selected system's ; coordinates in (QQ9, QQ10), so set up a counter to ; copy two bytes .pl1 LDA QQ0,X ; Load byte X from the current system in QQ0/QQ1 STA QQ9,X ; Store byte X in the selected system in QQ9/QQ10 DEX ; Decrement the loop counter BPL pl1 ; Loop back for the next byte to copy RTS ; Return from the subroutine
Name: PlayDemo [Show more] Type: Subroutine Category: Combat demo Summary: Play the combat demo Deep dive: The NES combat demo
Context: See this subroutine on its own page References: This subroutine is called as follows: * PlayDemo_b0 calls PlayDemo
.PlayDemo JSR RES2 ; Reset a number of flight variables and workspaces JSR ResetCommander_b6 ; Reset the current commander to the default "JAMESON" ; commander LDA #0 ; Set the fuel level to zero so we can't hyperspace out STA QQ14 ; of the demo STA CASH ; Zero the two lowest bytes of the cash reserves so no STA CASH+1 ; missions are triggered LDA #$FF ; Give our ship an E.C.M. STA ECM LDA #1 ; Give our ship an energy unit STA ENGY LDA #POW+128 ; Give our ship a beam laser STA LASER LDA #$FF ; Set demoInProgress = $FF to indicate that we are STA demoInProgress ; playing the demo JSR SOLAR ; Set up data blocks and slots for the planet and ; sun LDA #0 ; Set our ship's speed to zero STA DELTA STA ALPHA ; Set ALPHA and ALP1 to 0, so our roll angle is 0 STA ALP1 STA QQ12 ; Set QQ12 = 0 to indicate that we are not docked STA VIEW ; Set the space view to the front view JSR TT66 ; Clear the screen and set the view type in QQ11 to $00 ; (Space view with no fonts loaded) LSR demoInProgress ; Clear bit 7 of demoInProgress JSR CopyNameBuffer0To1 ; Copy the contents of nametable buffer 0 to nametable ; buffer JSR SetupFullViewInNMI ; Configure the PPU to send tiles for the full screen ; during VBlank JSR SetupSpaceView ; Set up the NMI variables for the space view JSR FixRandomNumbers ; Fix the random number seeds to a known value so the ; random numbers generated are always the same when we ; run the demo JSR SetupDemoShip ; Set up the ship workspace for a new ship that's to our ; upper left and in front of us, pointing into the ; screen LDA #6 ; Set the ship's pitch counter to 6 to make it pitch STA INWK+30 ; slightly in a positive direction (pitch down), so it ; starts diving gently towards the middle of the screen LDA #24 ; Set the ship's roll counter to 24 to make it roll in STA INWK+29 ; a positive direction (clockwise), for just under a ; quarter roll (24 * 1/16 radians = 1.5 radians = 86 ; degrees) LDA #18 ; Call NWSHP with A = 18 to add a new Mamba ship to our JSR NWSHP ; local bubble of universe LDA #10 ; Run ten iterations of the main flight loop so the JSR RunDemoFlightLoop ; Mamba flies into the screen for a while LDA #$92 ; Set the ship's pitch counter to -18 to make it pitch STA K%+2*NIK%+30 ; slightly in a negative direction (pull up), so it ; starts flying gently towards the top-left of the ; screen ; ; The ship will have been spawned in ship slot 2, so ; this directly updates byte #30 in the ship's data ; block in K%, where each data block is NIK% bytes long LDA #1 ; Set the ship's acceleration to 1 to make it accelerate STA K%+2*NIK%+28 ; away from us ; ; The ship will have been spawned in ship slot 2, so ; this directly updates byte #28 in the ship's data ; block in K%, where each data block is NIK% bytes long JSR SetupDemoShip ; Set up the ship workspace for a new ship that's to our ; upper left and in front of us, pointing into the ; screen LDA #6 ; Set the ship's pitch counter to 6 to make it pitch STA INWK+30 ; slightly in a positive direction (pitch down), so it ; starts diving gently towards the middle of the screen ASL INWK+2 ; Flip the sign of x_sign so the ship is now to our ; upper right and in front of us LDA #$C0 ; Set the ship's roll counter to -64 to make it roll in STA INWK+29 ; a negative direction (anti-clockwise), for two-thirds ; of a roll (64 * 1/16 radians = 4.0 radians = 229 ; degrees) LDA #KRA ; Call NWSHP to add a new Krait ship to our local bubble JSR NWSHP ; of universe LDA #6 ; Run six iterations of the main flight loop so the JSR RunDemoFlightLoop ; Krait flies into the screen for a while and the Mamba ; starts to pull away from the middle of the screen JSR SetupDemoShip ; Set up the ship workspace for a new ship that's to our ; upper left and in front of us, pointing into the ; screen LDA #6 ; Set the ship's pitch counter to 6 to make it pitch STA INWK+30 ; slightly in a positive direction (pitch down), so it ; starts diving gently towards the middle of the screen ASL INWK+2 ; Flip the sign of x_sign so the ship is now to our ; upper right and in front of us LDA #0 ; Set x_lo = 0 so the ship is directly above us STA INWK LDA #70 ; Set z_lo = 70 so the ship starts out a little further STA INWK+6 ; in front than the others LDA #SH3 ; Call NWSHP to add a new Sidewinder ship to our local JSR NWSHP ; bubble of universe LDA #5 ; Run five iterations of the main flight loop JSR RunDemoFlightLoop ; Sidewinder flies into the screen for a while LDA #$C0 ; Set the ship's pitch counter to -64 to make it pitch STA K%+4*NIK%+30 ; strongly in a negative direction (pull up), so it ; starts flying towards the top-middle of the screen ; ; The ship will have been spawned in ship slot 4, so ; this directly updates byte #30 in the ship's data ; block in K%, where each data block is NIK% bytes long LDA #11 ; Run 11 iterations of the main flight loop so all three JSR RunDemoFlightLoop ; ships pull away from the centre of the screen LDA #50 ; Set the NMI timer so it starts counting down from 50, STA nmiTimer ; so the (nmiTimerHi nmiTimerLo) will tick up to one ; second after 50 VBlanks (which is one second on PAL ; systems or 0.83 seconds on NTSC) LDA #0 ; Set the NMI timer in (nmiTimerHi nmiTimerLo) to zero STA nmiTimerLo ; so we can use it to count how long the combat demo STA nmiTimerHi ; runs for (i.e. how long it takes for us to eliminate ; all three ships) JSR SIGHT_b3 ; Draw the laser crosshairs LSR allowInSystemJump ; Clear bit 7 of allowInSystemJump to allow in-system ; jumps, so the call to UpdateIconBar displays the ; fast-forward icon (though choosing this in the demo ; doesn't do an in-system jump, but skips the rest of ; the demo instead) JSR UpdateIconBar_b3 ; Update the icon bar to show the correct buttons for ; the weapons we have given our ship (i.e. missiles and ; E.C.M.) LDA tuneSpeedCopy ; Set tuneSpeed = tuneSpeedCopy, to ensure that the STA tuneSpeed ; music speed is set correctly for the current tune (so ; this resets any speed-ups that have been applied, ; such as in the combat demo) LDA #16 ; Set our ship's speed to 16, so we start the demo by STA DELTA ; flying forwards JMP MLOOP ; Jump to MLOOP to run the main game loop for the ; duration of the combat demo ; ; Part 15 of the main flight loop contains a check to ; see whether the demo is enabled (which it is) and if ; so, whether we have destroyed all three ships, in ; which case it jumps to ShowScrollText with A = 1 to ; show the scroll text with the results of combat ; practice
Name: RunDemoFlightLoop [Show more] Type: Subroutine Category: Combat demo Summary: Run a fixed number of iterations of the main flight loop for the combat demo 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: * PlayDemo calls RunDemoFlightLoop

Arguments: A The number of iterations of the main flight loop to run
.RunDemoFlightLoop STA LASCT ; Store the number of iterations in LASCT so we can ; access it after running the main flight loop .dlop1 JSR FlipDrawingPlane ; Flip the drawing bitplane so we draw into the bitplane ; that isn't visible on-screen 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 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 LDA iconBarChoice ; Set A to the number of the icon bar button that has ; been chosen from the icon bar (if any) JSR CheckForPause ; If the Start button has been pressed then process the ; pause menu and set the C flag, otherwise clear it DEC LASCT ; Decrement the iteration counter in LASCT BNE dlop1 ; Loop back to run the main flight loop again until we ; have run it the required number of times RTS ; Return from the subroutine
Name: SetupDemoShip [Show more] Type: Subroutine Category: Combat demo Summary: Set up the ship workspace for a new ship in the combat demo Deep dive: The NES combat demo
Context: See this subroutine on its own page References: This subroutine is called as follows: * PlayDemo calls SetupDemoShip
.SetupDemoShip JSR ZINF ; Call ZINF to reset the INWK ship workspace and set up ; the orientation vectors so the ship is pointing ; towards us, out of the screen LDA #96 ; Set byte #14 = nosev_z_hi = 96 = 1 STA INWK+14 ; ; So the ship is pointing into the screen ORA #%10000000 ; Flip the sign of A to represent a -1 STA INWK+22 ; Set byte #22 = sidev_x_hi = -96 = -1 ; ; So the ship doesn't get reflected in the x-axis by the ; flipping of the z-coordinate, but is instead rotated ; around the y-axis to point into the screen LDA #%11111110 ; Set the ship's byte #32 (AI flag) to %11111110, so it STA INWK+32 ; has no E.C.M., is hostile, highly aggressive and has ; AI enabled LDA #32 ; Set the ship's byte #27 (speed) to 32 STA INWK+27 ; We now set the ship's coordinates to (-40, 40, 60) so ; it is to our upper left and in front of us LDA #%10000000 ; Set (x_sign x_lo) = -40 STA INWK+2 LDA #40 STA INWK LDA #40 ; Set y_lo = 40 STA INWK+3 LDA #60 ; Set z_lo = 60 STA INWK+6 RTS ; Return from the subroutine
Name: tnpr1 [Show more] Type: Subroutine Category: Market Summary: Work out if we have space for one tonne of cargo
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 8 of 16) calls tnpr1

Given a market item, work out whether there is room in the cargo hold for one tonne of this item. For standard tonne canisters, the limit is given by the type of cargo hold we have, with a standard cargo hold having a capacity of 20t and an extended cargo bay being 35t. For items measured in kg (gold, platinum), g (gem-stones) and alien items, the individual limit on each of these is 200 units.
Arguments: A The type of market item (see QQ23 for a list of market item numbers)
Returns: A A = 1 C flag Returns the result: * Set if there is no room for this item * Clear if there is room for this item
.tnpr1 STA QQ29 ; Store the type of market item in QQ29 LDA #1 ; Set the number of units of this market item to 1 ; Fall through into tnpr to work out whether there is ; room in the cargo hold for A tonnes of the item of ; type QQ29
Name: tnpr [Show more] Type: Subroutine Category: Market Summary: Work out if we have space for a specific amount of cargo
Context: See this subroutine on its own page References: This subroutine is called as follows: * BuyAndSellCargo calls tnpr

Given a market item and an amount, work out whether there is room in the cargo hold for this item. For standard tonne canisters, the limit is given by the type of cargo hold we have, with a standard cargo hold having a capacity of 20t and an extended cargo bay being 35t. For items measured in kg (gold, platinum), g (gem-stones) and alien items, the individual limit on each of these is 200 units.
Arguments: A The number of units of this market item QQ29 The type of market item (see QQ23 for a list of market item numbers)
Returns: A A is preserved C flag Returns the result: * Set if there is no room for this item * Clear if there is room for this item
.tnpr PHA ; Store A on the stack LDX #12 ; If QQ29 > 12 then jump to kg below, as this cargo CPX QQ29 ; type is gold, platinum, gem-stones or alien items, BCC kg ; and they have different cargo limits to the standard ; tonne canisters .Tml ; Here we count the tonne canisters we have in the hold ; and add to A to see if we have enough room for A more ; tonnes of cargo, using X as the loop counter, starting ; with X = 12 ADC QQ20,X ; Set A = A + the number of tonnes we have in the hold ; of market item number X. Note that the first time we ; go round this loop, the C flag is set (as we didn't ; branch with the BCC above, so the effect of this loop ; is to count the number of tonne canisters in the hold, ; and add 1 DEX ; Decrement the loop counter BPL Tml ; Loop back to add in the next market item in the hold, ; until we have added up all market items from 12 ; (minerals) down to 0 (food) ADC TRIBBLE+1 ; Add the high byte of the number of Trumbles in the ; hold, as 256 Trumbles take up one tonne of cargo space CMP CRGO ; If A < CRGO then the C flag will be clear (we have ; room in the hold) ; ; If A >= CRGO then the C flag will be set (we do not ; have room in the hold) ; ; This works because A contains the number of canisters ; plus 1, while CRGO contains our cargo capacity plus 2, ; so if we actually have "a" canisters and a capacity ; of "c", then: ; ; A < CRGO means: a+1 < c+2 ; a < c+1 ; a <= c ; ; So this is why the value in CRGO is 2 higher than the ; actual cargo bay size, i.e. it's 22 for the standard ; 20-tonne bay, and 37 for the large 35-tonne bay PLA ; Restore A from the stack RTS ; Return from the subroutine .kg ; Here we count the number of items of this type that ; we already have in the hold, and add to A to see if ; we have enough room for A more units LDY QQ29 ; Set Y to the item number we want to add ADC QQ20,Y ; Set A = A + the number of units of this item that we ; already have in the hold CMP #201 ; Is the result greater than 201 (the limit on ; individual stocks of gold, platinum, gem-stones and ; alien items)? ; ; If so, this sets the C flag (no room) ; ; Otherwise it is clear (we have room) PLA ; Restore A from the stack RTS ; Return from the subroutine
Name: SetNewViewType [Show more] Type: Subroutine Category: Drawing the screen Summary: Clear the screen, set the current view type and move the cursor to row 0
Context: See this subroutine on its own page References: This subroutine is called as follows: * EQSHP calls SetNewViewType * STATUS calls SetNewViewType * TT167 calls SetNewViewType * TT213 calls SetNewViewType * TT25 calls SetNewViewType

Arguments: A The type of the new current view (see QQ11 for a list of view types)
.SetNewViewType JSR TT66 ; Clear the screen and set the current view type to A LDA #0 ; Move the text cursor to row 0 STA YC RTS ; Return from the subroutine
Name: TT20 [Show more] Type: Subroutine Category: Universe Summary: Twist the selected system's seeds four times Deep dive: Twisting the system seeds Galaxy and system seeds
Context: See this subroutine on its own page References: This subroutine is called as follows: * HME2 calls TT20 * TT111 calls TT20 * TT22 calls TT20 * TT23 calls TT20

Twist the three 16-bit seeds in QQ15 (selected system) four times, to generate the next system.
.TT20 JSR P%+3 ; This line calls the line below as a subroutine, which ; does two twists before returning here, and then we ; fall through to the line below for another two ; twists, so the net effect of these two consecutive ; JSR calls is four twists, not counting the ones ; inside your head as you try to follow this process JSR P%+3 ; This line calls TT54 as a subroutine to do a twist, ; and then falls through into TT54 to do another twist ; before returning from the subroutine
Name: TT54 [Show more] Type: Subroutine Category: Universe Summary: Twist the selected system's seeds Deep dive: Twisting the system seeds Galaxy and system seeds
Context: See this subroutine on its own page References: This subroutine is called as follows: * cpl calls TT54

This routine twists the three 16-bit seeds in QQ15 once. If we start with seeds s0, s1 and s2 and we want to work out their new values after we perform a twist (let's call the new values s0´, s1´ and s2´), then: s0´ = s1 s1´ = s2 s2´ = s0 + s1 + s2 So given an existing set of seeds in s0, s1 and s2, we can get the new values s0´, s1´ and s2´ simply by doing the above sums. And if we want to do the above in-place without creating three new s´ variables, then we can do the following: tmp = s0 + s1 s0 = s1 s1 = s2 s2 = tmp + s1 So this is what we do in this routine, where each seed is a 16-bit number.
.TT54 LDA QQ15 ; X = tmp_lo = s0_lo + s1_lo CLC ADC QQ15+2 TAX LDA QQ15+1 ; Y = tmp_hi = s1_hi + s1_hi + C ADC QQ15+3 TAY LDA QQ15+2 ; s0_lo = s1_lo STA QQ15 LDA QQ15+3 ; s0_hi = s1_hi STA QQ15+1 LDA QQ15+5 ; s1_hi = s2_hi STA QQ15+3 LDA QQ15+4 ; s1_lo = s2_lo STA QQ15+2 CLC ; s2_lo = X + s1_lo TXA ADC QQ15+2 STA QQ15+4 TYA ; s2_hi = Y + s1_hi + C ADC QQ15+3 STA QQ15+5 RTS ; The twist is complete so return from the subroutine
Name: TT146 [Show more] Type: Subroutine Category: Universe Summary: Print the distance to the selected system in light years
Context: See this subroutine on its own page References: This subroutine is called as follows: * SetSelectedSystem calls TT146 * TT25 calls TT146

If it is non-zero, print the distance to the selected system in light years. If it is zero, just move the text cursor down a line. Specifically, if the distance in QQ8 is non-zero, print token 31 ("DISTANCE"), then a colon, then the distance to one decimal place, then token 35 ("LIGHT YEARS"). If the distance is zero, move the cursor down one line.
.TT146 LDA QQ8 ; Take the two bytes of the 16-bit value in QQ8 and ORA QQ8+1 ; OR them together to check whether there are any BNE TT63 ; non-zero bits, and if so, jump to TT63 to print the ; distance LDA MJ ; If we are in witchspace (i.e. MJ is non-zero), jump to BNE TT63 ; TT63 to print the distance INC YC ; The distance is zero, so we just move the text cursor INC YC ; in YC down by two lines and return from the subroutine RTS .TT63 LDA #191 ; Print recursive token 31 ("DISTANCE") followed by JSR TT68 ; a colon LDX QQ8 ; Load (Y X) from QQ8, which contains the 16-bit LDY QQ8+1 ; distance we want to show SEC ; Set the C flag so that the call to pr5 will include a ; decimal point, and display the value as (Y X) / 10 JSR pr5 ; Print (Y X) to 5 digits, including a decimal point LDA #195 ; Set A to the recursive token 35 (" LIGHT YEARS") and ; fall through into TT60 to print the token followed ; by a paragraph break
Name: TT60 [Show more] Type: Subroutine Category: Text Summary: Print a text token and a paragraph break
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT213 calls TT60 * TT25 calls TT60

Print a text token (i.e. a character, control code, two-letter token or recursive token). Then print a paragraph break (a blank line between paragraphs) by moving the cursor down a line, setting Sentence Case, and then printing a newline.
Arguments: A The text token to be printed
.TT60 JSR TT27_b2 ; Print the text token in A and fall through into TTX69 ; to print the paragraph break
Name: TTX69 [Show more] Type: Subroutine Category: Text Summary: Print a paragraph break
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT25 calls TTX69

Print a paragraph break (a blank line between paragraphs) by moving the cursor down a line, setting Sentence Case, and then printing a newline.
.TTX69 INC YC ; Move the text cursor down a line ; Fall through into TT69 to set Sentence Case and print ; a newline
Name: TT69 [Show more] Type: Subroutine Category: Text Summary: Set Sentence Case and print a newline
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT210 calls TT69
.TT69 LDA #%10000000 ; Set bit 7 of QQ17 to switch to Sentence Case STA QQ17 ; Fall through into TT67 to print a newline
Name: TT67 [Show more] Type: Subroutine Category: Text Summary: Print a newline
Context: See this subroutine on its own page References: This subroutine is called as follows: * plf calls TT67 * PrintCrTab calls TT67 * STATUS calls TT67 * TT213 calls TT67
.TT67 LDA #12 ; Load a newline character into A JMP TT27_b2 ; Print the text token in A and return from the ; subroutine using a tail call
Name: TT70 [Show more] Type: Subroutine Category: Universe Summary: Display "MAINLY " and jump to TT72
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT25 calls TT70

This subroutine is called by TT25 when displaying a system's economy.
.TT70 LDA #173 ; Print recursive token 13 ("MAINLY ") JSR TT27_b2 JMP TT72 ; Jump to TT72 to continue printing system data as part ; of routine TT25
Name: spc [Show more] Type: Subroutine Category: Text Summary: Print a text token followed by a space
Context: See this subroutine on its own page References: This subroutine is called as follows: * EQSHP calls spc * fwl calls spc * PrintLegalStatus calls spc * STATUS calls spc * TT25 calls spc * TT66 calls spc

Print a text token (i.e. a character, control code, two-letter token or recursive token) followed by a space.
Arguments: A The text token to be printed
.spc JSR TT27_b2 ; Print the text token in A JMP TT162 ; Print a space and return from the subroutine using a ; tail call
Name: PrintSpaceAndToken [Show more] Type: Subroutine Category: Text Summary: Print a space followed by a text token
Context: See this subroutine on its own page References: This subroutine is called as follows: * STATUS calls PrintSpaceAndToken * TT25 calls PrintSpaceAndToken

Arguments: A The character to be printed
.PrintSpaceAndToken PHA ; Store the character to print on the stack JSR TT162 ; Print a space PLA ; Retrieve the character to print from the stack JMP TT27_b2 ; Print the character in A, returning from the ; subroutine using a tail call
Name: xDataOnSystem [Show more] Type: Variable Category: Universe Summary: The text column for the Data on System title 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: * TT25 uses xDataOnSystem
.xDataOnSystem EQUB 9 ; English EQUB 9 ; German EQUB 7 ; French EQUB 9 ; There is no fourth language, so this byte is ignored
Name: PrintTokenAndColon [Show more] Type: Subroutine Category: Text Summary: Print a character followed by a colon, ensuring that the colon is always drawn in colour 3 on a black background
Context: See this subroutine on its own page References: This subroutine is called as follows: * fwl calls PrintTokenAndColon * TT25 calls PrintTokenAndColon

The colon is printed using font style 3. This draws the colon in colour 3 on background colour 0 (i.e. green on black), but without using the normal font. This ensures that the colon will be drawn in green when the colon's tile falls within a 2x2 attribute block that's set to draw white text (i.e. where colour 1 is white). This happens in the Status Mode screen in French, and in the Data on System screen in all three languages.
Arguments: A The character to be printed
.PrintTokenAndColon JSR TT27_b2 ; Print the character in A LDA #3 ; Set the font style to green text on a black background STA fontStyle ; (colour 3 on background colour 0) LDA #':' ; Print a colon JSR TT27_b2 LDA #1 ; Set the font style to print in the normal font STA fontStyle RTS ; Return from the subroutine
Name: radiusText [Show more] Type: Variable Category: Universe Summary: The text string "RADIUS" for use in the Data on System screen
Context: See this variable on its own page References: This variable is used as follows: * TT25 uses radiusText
.radiusText EQUS "RADIUS"
Name: TT25 [Show more] Type: Subroutine Category: Universe Summary: Show the Data on System screen Deep dive: Generating system data Galaxy and system seeds
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT102 calls TT25 * TT70 calls via TT72

Other entry points: TT72 Used by TT70 to re-enter the routine after displaying "MAINLY" for the economy type
.TT25 LDA #$96 ; Clear the screen and set the view type in QQ11 to $96 JSR SetNewViewType ; (Data on System) JSR TT111 ; Select the system closest to galactic coordinates ; (QQ9, QQ10) LDX languageIndex ; Move the text cursor to the correct column for the LDA xDataOnSystem,X ; Data on System title in the chosen language STA XC LDA #163 ; Print recursive token 3 ("DATA ON {selected system JSR NLIN3 ; name}" on the top row JSR TTX69 ; Print a paragraph break and set Sentence Case JSR TT146 ; If the distance to this system is non-zero, print ; "DISTANCE", then the distance, "LIGHT YEARS" and a ; paragraph break, otherwise just move the cursor down ; a line LDA languageNumber ; If both bit 1 and bit 2 of languageNumber are clear AND #%00000110 ; then the chosen language is English, so jump to dsys1 BEQ dsys1 ; to skip the following ; If we get here then the chosen language is French or ; German, so we need to print the system economy using ; the PrintTokenAndColon routine to ensure the label is ; in green LDA #194 ; Print recursive token 34 ("ECONOMY") followed by JSR PrintTokenAndColon ; colon, ensuring that the colon is printed in green ; despite being in a 2x2 attribute block set for white ; text JMP dsys2 ; Jump to dsys2 to print the economy type .dsys1 LDA #194 ; Print recursive token 34 ("ECONOMY") followed by JSR TT68 ; a colon JSR TT162 ; Print a space .dsys2 LDA QQ3 ; The system economy is determined by the value in QQ3, ; so fetch it into A. First we work out the system's ; prosperity as follows: ; ; QQ3 = 0 or 5 = %000 or %101 = Rich ; QQ3 = 1 or 6 = %001 or %110 = Average ; QQ3 = 2 or 7 = %010 or %111 = Poor ; QQ3 = 3 or 4 = %011 or %100 = Mainly CLC ; If (QQ3 + 1) >> 1 = %10, i.e. if QQ3 = %011 or %100 ADC #1 ; (3 or 4), then call TT70, which prints "MAINLY " and LSR A ; jumps down to TT72 to print the type of economy CMP #%00000010 BEQ TT70 LDA QQ3 ; If (QQ3 + 1) >> 1 < %10, i.e. if QQ3 = %000, %001 or BCC TT71 ; %010 (0, 1 or 2), then jump to TT71 with A set to the ; original value of QQ3 SBC #5 ; Here QQ3 = %101, %110 or %111 (5, 6 or 7), so subtract CLC ; 5 to bring it down to 0, 1 or 2 (the C flag is already ; set so the SBC will be correct) .TT71 ADC #170 ; A is now 0, 1 or 2, so print recursive token 10 + A. JSR TT27_b2 ; This means that: ; ; QQ3 = 0 or 5 prints token 10 ("RICH ") ; QQ3 = 1 or 6 prints token 11 ("AVERAGE ") ; QQ3 = 2 or 7 prints token 12 ("POOR ") .TT72 LDA QQ3 ; Now to work out the type of economy, which is LSR A ; determined by bit 2 of QQ3, as follows: LSR A ; ; QQ3 bit 2 = 0 = Industrial ; QQ3 bit 2 = 1 = Agricultural ; ; So we fetch QQ3 into A and set A = bit 2 of QQ3 using ; two right shifts (which will work as QQ3 is only a ; 3-bit number) CLC ; Print recursive token 8 + A, followed by a paragraph ADC #168 ; break and Sentence Case, so: JSR TT60 ; ; QQ3 bit 2 = 0 prints token 8 ("INDUSTRIAL") ; QQ3 bit 2 = 1 prints token 9 ("AGRICULTURAL") LDA languageNumber ; If bit 2 of languageNumber is clear then the chosen AND #%00000100 ; language is not French, so jump to dsys3 skip the BEQ dsys3 ; following ; If we get here then the chosen language is French, so ; so we need to print the system government using the ; PrintTokenAndColon routine to ensure the label is in ; green LDA #162 ; Print recursive token 2 ("GOVERNMENT") followed by JSR PrintTokenAndColon ; colon, ensuring that the colon is printed in green ; despite being in a 2x2 attribute block set for white ; text JMP dsys4 ; Jump to dsys4 to print the government type .dsys3 LDA #162 ; Print recursive token 2 ("GOVERNMENT") followed by JSR TT68 ; a colon JSR TT162 ; Print a space .dsys4 LDA QQ4 ; The system's government is determined by the value in ; QQ4, so fetch it into A CLC ; Print recursive token 17 + A, followed by a paragraph ADC #177 ; break and Sentence Case, so: JSR TT60 ; ; QQ4 = 0 prints token 17 ("ANARCHY") ; QQ4 = 1 prints token 18 ("FEUDAL") ; QQ4 = 2 prints token 19 ("MULTI-GOVERNMENT") ; QQ4 = 3 prints token 20 ("DICTATORSHIP") ; QQ4 = 4 prints token 21 ("COMMUNIST") ; QQ4 = 5 prints token 22 ("CONFEDERACY") ; QQ4 = 6 prints token 23 ("DEMOCRACY") ; QQ4 = 7 prints token 24 ("CORPORATE STATE") LDA #196 ; Print recursive token 36 ("TECH.LEVEL") followed by a JSR TT68 ; colon LDX QQ5 ; Fetch the tech level from QQ5 and increment it, as it INX ; is stored in the range 0-14 but the displayed range ; should be 1-15 CLC ; Call pr2 to print the technology level as a 3-digit JSR pr2 ; number without a decimal point (by clearing the C ; flag) JSR TTX69 ; Print a paragraph break and set Sentence Case LDA #193 ; Print recursive token 33 ("TURNOVER"), followed JSR TT68 ; by a colon LDX QQ7 ; Fetch the 16-bit productivity value from QQ7 into LDY QQ7+1 ; (Y X) CLC ; Print (Y X) to 6 digits with no decimal point LDA #6 JSR TT11 JSR TT162 ; Print a space LDA #0 ; Set QQ17 = 0 to switch to ALL CAPS STA QQ17 LDA #'M' ; Print "MCR", followed by a paragraph break and JSR DASC_b2 ; Sentence Case LDA #'C' JSR TT27_b2 LDA #'R' JSR TT60 LDY #0 ; We now print the string in radiusText ("RADIUS"), so ; set a character counter in Y .dsys5 LDA radiusText,Y ; Print the Y-th character from radiusText JSR TT27_b2 INY ; Increment the counter CPY #5 ; Loop back until we have printed the first five letters BCC dsys5 ; of the string LDA radiusText,Y ; Print the last letter of the string, followed by a JSR TT68 ; colon LDA QQ15+5 ; Set A = QQ15+5 LDX QQ15+3 ; Set X = QQ15+3 AND #%00001111 ; Set Y = (A AND %1111) + 11 CLC ADC #11 TAY LDA #5 ; Print (Y X) to 5 digits, not including a decimal JSR TT11 ; point, as the C flag will be clear (as the maximum ; radius will always fit into 16 bits) JSR TT162 ; Print a space LDA #'k' ; Print "km" JSR DASC_b2 LDA #'m' JSR DASC_b2 JSR TTX69 ; Print a paragraph break and set Sentence Case LDA languageNumber ; If both bit 0 and bit 2 of languageNumber are clear AND #%00000101 ; then the chosen language is not English or German, so BEQ dsys6 ; jump to dsys6 skip the following ; If we get here then the chosen language is English or ; German, so we need to print the system population ; using the PrintTokenAndColon routine to ensure the ; label is in green LDA #192 ; Print recursive token 32 ("POPULATION") followed by a JSR PrintTokenAndColon ; colon, ensuring that the colon is printed in green ; despite being in a 2x2 attribute block set for white ; text JMP dsys7 ; Jump to dsys7 to print the population .dsys6 LDA #192 ; Print recursive token 32 ("POPULATION") followed by a JSR TT68 ; colon .dsys7 LDA QQ6 ; Set X = QQ6 / 8 LSR A ; LSR A ; We use this as the population figure, in billions LSR A TAX CLC ; Clear the C flag so we do not print a decimal point in ; the call to pr2+2 LDA #1 ; Set the number of digits to 1 for the call to pr2+2 JSR pr2+2 ; Print the population as a 1-digit number without a ; decimal point LDA #198 ; Print recursive token 38 (" BILLION"), followed by a JSR TT60 ; paragraph break and Sentence Case LDA languageNumber ; If bit 1 of languageNumber is set then the chosen AND #%00000010 ; language is German, so jump to dsys8 skip the BNE dsys8 ; following ; If we get here then the chosen language is French or ; English, so we print the species in brackets LDA #'(' ; Print an opening bracket JSR TT27_b2 .dsys8 LDA QQ15+4 ; Now to calculate the species, so first check bit 7 of BMI TT205 ; s2_lo, and if it is set, jump to TT205 as this is an ; alien species LDA #188 ; Bit 7 of s2_lo is clear, so print recursive token 28 JSR TT27_b2 ; ("HUMAN COLONIAL") JMP TT76 ; Jump to TT76 to print "S)" and a paragraph break, so ; the whole species string is "(HUMAN COLONIALS)" .TT75 LDA QQ15+5 ; This is an alien species, so we take bits 0-1 of AND #%00000011 ; s2_hi, add this to the value of A that we used for CLC ; the third adjective, and take bits 0-2 of the result ADC QQ19 AND #%00000111 ADC #242 ; A = 0 to 7, so print recursive token 82 + A, so: JSR TT27_b2 ; ; A = 0 prints token 82 ("RODENTS") ; A = 1 prints token 83 ("FROGS") ; A = 2 prints token 84 ("LIZARDS") ; A = 3 prints token 85 ("LOBSTERS") ; A = 4 prints token 86 ("BIRDS") ; A = 5 prints token 87 ("HUMANOIDS") ; A = 6 prints token 88 ("FELINES") ; A = 7 prints token 89 ("INSECTS") LDA QQ15+5 ; Now for the second adjective, so shift s2_hi so we get LSR A ; A = bits 5-7 of s2_hi LSR A LSR A LSR A LSR A CMP #6 ; If A >= 6, jump to dsys9 to skip the second adjective BCS dsys9 ADC #230 ; Otherwise A = 0 to 5, so print a space followed by JSR PrintSpaceAndToken ; recursive token 70 + A, so: ; ; A = 0 prints token 70 ("GREEN") and a space ; A = 1 prints token 71 ("RED") and a space ; A = 2 prints token 72 ("YELLOW") and a space ; A = 3 prints token 73 ("BLUE") and a space ; A = 4 prints token 74 ("BLACK") and a space ; A = 5 prints token 75 ("HARMLESS") and a space .dsys9 LDA QQ19 ; Fetch the value that we calculated for the third ; adjective CMP #6 ; If A >= 6, jump to TT76 to skip the third adjective BCS TT76 ADC #236 ; Otherwise A = 0 to 5, so print a space followed by JSR PrintSpaceAndToken ; recursive token 76 + A, so: ; ; A = 0 prints token 76 ("SLIMY") and a space ; A = 1 prints token 77 ("BUG-EYED") and a space ; A = 2 prints token 78 ("HORNED") and a space ; A = 3 prints token 79 ("BONY") and a space ; A = 4 prints token 80 ("FAT") and a space ; A = 5 prints token 81 ("FURRY") and a space JMP TT76 ; Jump to TT76 as we have finished printing the ; species string .TT205 ; In NES Elite, there is no first adjective (in the ; other versions, the first adjective can be "Large", ; "Fierce" or "Small", but this is omitted in NES Elite ; as there isn't space on-screen) LDA QQ15+3 ; In preparation for the third adjective, EOR the high EOR QQ15+1 ; bytes of s0 and s1 and extract bits 0-2 of the result: AND #%00000111 ; STA QQ19 ; A = (s0_hi EOR s1_hi) AND %111 ; ; storing the result in QQ19 so we can use it later LDA languageNumber ; If bit 2 of languageNumber is set, then the chosen AND #%00000100 ; language is French, so jump to TT75 to print the BNE TT75 ; species and then the third adjective, e.g. "Rodents ; Furry" LDA QQ15+5 ; Now for the second adjective, so shift s2_hi so we get LSR A ; A = bits 5-7 of s2_hi LSR A LSR A LSR A LSR A CMP #6 ; If A >= 6, jump to TT206 to skip the second adjective BCS TT206 ADC #230 ; Otherwise A = 0 to 5, so print recursive token JSR spc ; 70 + A, followed by a space, so: ; ; A = 0 prints token 70 ("GREEN") and a space ; A = 1 prints token 71 ("RED") and a space ; A = 2 prints token 72 ("YELLOW") and a space ; A = 3 prints token 73 ("BLUE") and a space ; A = 4 prints token 74 ("BLACK") and a space ; A = 5 prints token 75 ("HARMLESS") and a space .TT206 LDA QQ19 ; Fetch the value that we calculated for the third ; adjective CMP #6 ; If A >= 6, jump to TT207 to skip the third adjective BCS TT207 ADC #236 ; Otherwise A = 0 to 5, so print recursive token JSR spc ; 76 + A, followed by a space, so: ; ; A = 0 prints token 76 ("SLIMY") and a space ; A = 1 prints token 77 ("BUG-EYED") and a space ; A = 2 prints token 78 ("HORNED") and a space ; A = 3 prints token 79 ("BONY") and a space ; A = 4 prints token 80 ("FAT") and a space ; A = 5 prints token 81 ("FURRY") and a space .TT207 LDA QQ15+5 ; Now for the actual species, so take bits 0-1 of AND #%00000011 ; s2_hi, add this to the value of A that we used for CLC ; the third adjective, and take bits 0-2 of the result ADC QQ19 AND #%00000111 ADC #242 ; A = 0 to 7, so print recursive token 82 + A, so: JSR TT27_b2 ; ; A = 0 prints token 82 ("RODENTS") ; A = 1 prints token 83 ("FROGS") ; A = 2 prints token 84 ("LIZARDS") ; A = 3 prints token 85 ("LOBSTERS") ; A = 4 prints token 86 ("BIRDS") ; A = 5 prints token 87 ("HUMANOIDS") ; A = 6 prints token 88 ("FELINES") ; A = 7 prints token 89 ("INSECTS") .TT76 LDA languageNumber ; If bit 1 of languageNumber is set then the chosen AND #%00000010 ; language is German, so jump to dsys10 skip the BNE dsys10 ; following ; If we get here then the chosen language is French or ; English, so we print the species in brackets LDA #')' ; Print a closing bracket JSR TT27_b2 .dsys10 JSR TTX69 ; Print a paragraph break and set Sentence Case ; By this point, ZZ contains the current system number ; which PDESC requires. It gets put there in the TT102 ; routine, which calls TT111 to populate ZZ before ; calling TT25 (this routine) JSR PDESC_b2 ; Call PDESC to print the system's extended description JSR FadeAndHideSprites ; Fade the screen to black and hide all sprites, so we ; can update the screen while it's blacked-out LDA #22 ; Move the text cursor to column 22 STA XC LDA #8 ; Move the text cursor to row 8 STA YC LDA #1 ; These instructions have no effect as the values of K+2 STA K+2 ; and K+3 get overwritten by the call to DrawSystemImage LDA #8 STA K+3 LDX #8 ; Set X = 8 to pass to the call to DrawSystemImage as ; the number of tile columns to draw in the system image LDY #7 ; Set Y = 7 to pass to the call to DrawSystemImage as ; the number of tile rows to draw in the system image JSR DrawSystemImage_b3 ; Call DrawSystemImage to draw the system image JMP UpdateView ; Update the view, returning from the subroutine using ; a tail call
Name: TT22 [Show more] Type: Subroutine Category: Charts Summary: Show the Long-range Chart
Context: See this subroutine on its own page References: This subroutine is called as follows: * RedrawCurrentView calls TT22 * TT102 calls TT22 * TT114 calls TT22
.TT22 LDA #$8D ; Clear the screen and set the view type in QQ11 to $8D JSR TT66 ; (Long-range Chart) LDA #77 ; Set the screen height variables for a screen height of JSR SetScreenHeight ; 154 (i.e. 2 * 77) LDA #7 ; Move the text cursor to column 7 STA XC JSR TT81 ; Set the seeds in QQ15 to those of system 0 in the ; current galaxy (i.e. copy the seeds from QQ21 to QQ15) LDA #199 ; Print recursive token 39 ("GALACTIC CHART{galaxy JSR NLIN3 ; number right-aligned to width 3}") on the top row LDA #152 ; Draw a screen-wide horizontal line at pixel row 152 JSR NLIN2 ; for the bottom edge of the chart, so the chart itself ; is 128 pixels high, starting on row 24 and ending on ; row 151 JSR FadeAndHideSprites ; Fade the screen to black and hide all sprites, so we ; can update the screen while it's blacked-out JSR TT14 ; Call TT14 to draw a circle with crosshairs at the ; current system's galactic coordinates LDX #0 ; We're now going to plot each of the galaxy's systems, ; so set up a counter in X for each system, starting at ; 0 and looping through to 255 .TT83 STX XSAV ; Store the counter in XSAV LDA QQ15+3 ; Fetch the s1_hi seed into A, which gives us the ; galactic x-coordinate of this system LSR A ; Set X = s1_hi - (A / 4) + 31 LSR A ; = s1_hi - (s1_hi / 4) + 31 STA T1 ; = 31 + 0.75 * s1_hi LDA QQ15+3 ; SEC ; So this scales the x-coordinate from a range of 0 to SBC T1 ; 255 into a range from 31 to 222, so it fits nicely CLC ; into the Long-range Chart ADC #31 TAX LDY QQ15+4 ; Fetch the s2_lo seed and set bits 4 and 6, storing the TYA ; result in ZZ to give a random number between 80 and ORA #%01010000 ; (but which will always be the same for this system). STA ZZ ; We use this value to determine the size of the point ; for this system on the chart by passing it as the ; distance argument to the PIXEL routine below LDA QQ15+1 ; Fetch the s0_hi seed into A, which gives us the ; galactic y-coordinate of this system LSR A ; Set A = (s0_hi - (A / 4)) / 2 + 32 LSR A ; = (s0_hi - (s0_hi / 4)) / 2 + 32 STA T1 ; = 32 + 0.375 * s1_hi LDA QQ15+1 ; SEC ; So this scales the y-coordinate from a range of 0 to SBC T1 ; 255 into a range from 32 to 127, so it fits nicely LSR A ; into the Long-range Chart CLC ADC #32 STA Y1 ; Store the y-coordinate in Y1 (though this gets ; overwritten by the call to DrawDash, so this has no ; effect) JSR DrawDash ; Draw a 2-pixel dash at pixel coordinate (X, A) JSR TT20 ; We want to move on to the next system, so call TT20 ; to twist the three 16-bit seeds in QQ15 LDX XSAV ; Restore the loop counter from XSAV INX ; Increment the counter BNE TT83 ; If X > 0 then we haven't done all 256 systems yet, so ; loop back up to TT83 LDA #3 ; Set K+2 = 3 to pass to DrawSmallBox as the text row STA K+2 ; on which to draw the top-left corner of the small box LDA #4 ; Set K+3 = 4 to pass to DrawSmallBox as the text column STA K+3 ; on which to draw the top-left corner of the small box LDA #25 ; Set K = 25 to pass to DrawSmallBox as the width of the STA K ; small box LDA #14 ; Set K+1 = 14 to pass to DrawSmallBox as the height of STA K+1 ; the small box JSR DrawSmallBox_b3 ; Draw a box around the chart, with the top-left corner ; at (3, 4), a height of 14 rows, and a width of 25 rows LDA QQ9 ; Set QQ19 to the selected system's x-coordinate STA QQ19 LDA QQ10 ; Set QQ19+1 to the selected system's y-coordinate, LSR A ; halved to fit it into the chart STA QQ19+1 LDA #4 ; Set QQ19+2 to size 4 for the crosshairs size STA QQ19+2 JSR TT103 ; Draw small crosshairs at coordinates (QQ9, QQ10), ; which will draw the crosshairs at our current home ; system LDA #$9D ; Set the view type in QQ11 to $00 (Long-range Chart STA QQ11 ; with the normal font loaded) LDA #143 ; Set the number of pixel rows in the space view to 143, STA Yx2M1 ; so the screen height is correctly set for the ; Short-range Chart in case we switch to it using the ; icon in the icon bar (which toggles between the two ; charts) JMP UpdateView ; Update the view, returning from the subroutine using ; a tail call
Name: TT15 [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a set of crosshairs
Context: See this subroutine on its own page References: This subroutine is called as follows: * TT14 calls TT15

For all views except the Short-range Chart, the centre is drawn 24 pixels to the right of the y-coordinate given.
Arguments: QQ19 The pixel x-coordinate of the centre of the crosshairs QQ19+1 The pixel y-coordinate of the centre of the crosshairs QQ19+2 The size of the crosshairs
.TT15 LDA #24 ; Set A to 24, which we will use as the minimum ; screen indent for the crosshairs (i.e. the minimum ; distance from the top-left corner of the screen) LDX QQ11 ; If the current view is not the Short-range Chart, CPX #$9C ; which is view type $9C, then jump to TT178 to skip the BNE TT178 ; following instruction LDA #0 ; This is the Short-range Chart, so set A to 0, so the ; crosshairs can go right up against the screen edges .TT178 STA QQ19+5 ; Set QQ19+5 to A, which now contains the correct indent ; for this view LDA QQ19 ; Set A = crosshairs x-coordinate - crosshairs size SEC ; to get the x-coordinate of the left edge of the SBC QQ19+2 ; crosshairs BCS TT84 ; If the above subtraction didn't underflow, then A is ; positive, so skip the next instruction LDA #0 ; The subtraction underflowed, so set A to 0 so the ; crosshairs don't spill out of the left of the screen .TT84 ; In the following, the authors have used XX15 for ; temporary storage. XX15 shares location with X1, Y1, ; X2 and Y2, so in the following, you can consider ; the variables like this: ; ; XX15 is the same as X1 ; XX15+1 is the same as Y1 ; XX15+2 is the same as X2 ; XX15+3 is the same as Y2 ; ; Presumably this routine was written at a different ; time to the line-drawing routine, before the two ; workspaces were merged to save space STA XX15 ; Set XX15 (X1) = A (the x-coordinate of the left edge ; of the crosshairs) LDA QQ19 ; Set A = crosshairs x-coordinate + crosshairs size CLC ; to get the x-coordinate of the right edge of the ADC QQ19+2 ; crosshairs BCC P%+4 ; If the above addition didn't overflow, then A is ; correct, so skip the next instruction LDA #255 ; The addition overflowed, so set A to 255 so the ; crosshairs don't spill out of the right of the screen ; (as 255 is the x-coordinate of the rightmost pixel ; on-screen) STA XX15+2 ; Set XX15+2 (X2) = A (the x-coordinate of the right ; edge of the crosshairs) LDA QQ19+1 ; Set XX15+1 (Y1) = crosshairs y-coordinate + indent CLC ; to get the y-coordinate of the centre of the ADC QQ19+5 ; crosshairs STA XX15+1 STA XX15+3 ; Set XX15+3 (Y2) = crosshairs y-coordinate + indent JSR LOIN ; Draw a line from (X1, Y1) to (X2, Y2), where Y1 = Y2, ; which will draw from the left edge of the crosshairs ; to the right edge, through the centre of the ; crosshairs LDA QQ19+1 ; Set A = crosshairs y-coordinate - crosshairs size SEC ; to get the y-coordinate of the top edge of the SBC QQ19+2 ; crosshairs BCS TT86 ; If the above subtraction didn't underflow, then A is ; correct, so skip the next instruction LDA #0 ; The subtraction underflowed, so set A to 0 so the ; crosshairs don't spill out of the top of the screen .TT86 CLC ; Set XX15+1 (Y1) = A + indent to get the y-coordinate ADC QQ19+5 ; of the top edge of the indented crosshairs STA XX15+1 LDA QQ19+1 ; Set A = crosshairs y-coordinate + crosshairs size CLC ; + indent to get the y-coordinate of the bottom edge ADC QQ19+2 ; of the indented crosshairs ADC QQ19+5 CMP #152 ; If A < 152 then skip the following, as the crosshairs BCC TT87 ; won't spill out of the bottom of the screen LDX QQ11 ; A >= 152, so we need to check whether this will fit in ; this view, so fetch the view type CPX #$9C ; If this is the Short-range Chart then the y-coordinate BEQ TT87 ; is fine, so skip to TT87 LDA #151 ; Otherwise this is the Long-range Chart, so we need to ; clip the crosshairs at a maximum y-coordinate of 151 .TT87 STA XX15+3 ; Set XX15+3 (Y2) = A (the y-coordinate of the bottom ; edge of the crosshairs) LDA QQ19 ; Set XX15 (X1) = the x-coordinate of the centre of the STA XX15 ; crosshairs STA XX15+2 ; Set XX15+2 (X2) = the x-coordinate of the centre of ; the crosshairs JMP LOIN ; Draw a vertical line (X1, Y1) to (X2, Y2), which will ; draw from the top edge of the crosshairs to the bottom ; edge, through the centre of the crosshairs, returning ; from the subroutine using a tail call