\ ****************************************************************************** \ \ TWO-PLAYER 6502 SECOND PROCESSOR ELITE MAIN GAME SOURCE (PARASITE) \ \ 6502 Second Processor Elite was written by Ian Bell and David Braben and is \ copyright Acornsoft 1985 \ \ The code in this file is identical to the source discs released on Ian Bell's \ personal website at http://www.elitehomepage.org/ (it's just been reformatted \ to be more readable) \ \ The commentary is copyright Mark Moxon, and any misunderstandings or mistakes \ in the documentation are entirely my fault \ \ The terminology and notations used in this commentary are explained at \ https://elite.bbcelite.com/terminology \ \ The deep dive articles referred to in this commentary can be found at \ https://elite.bbcelite.com/deep_dives \ \ ------------------------------------------------------------------------------ \ \ This source file contains the main game code for 6502 Second Processor Elite. \ This is the code that runs in the parasite (i.e. the second processor). It \ also contains the ship blueprints and game text. \ \ ------------------------------------------------------------------------------ \ \ This source file produces the following binary files: \ \ * ELTA.bin \ * ELTB.bin \ * ELTC.bin \ * ELTD.bin \ * ELTE.bin \ * ELTF.bin \ * ELTG.bin \ * ELTH.bin \ * ELTI.bin \ * ELTJ.bin \ * SHIPS.bin \ * WORDS.bin \ \ ****************************************************************************** INCLUDE "1-source-files/main-sources/elite-build-options.asm" CPU 1 \ Switch to 65C02 assembly, as this code runs on the \ 6502 Second Processor _SOURCE_DISC = (_VARIANT = 1) _SNG45 = (_VARIANT = 2) _EXECUTIVE = (_VARIANT = 3) GUARD &F800 \ Guard against assembling over MOS memory \ ****************************************************************************** \ \ Configuration variables \ \ ****************************************************************************** CODE% = &1000 \ The address where the code will be run LOAD% = &1000 \ The address where the code will be loaded CODE_WORDS% = &0400 \ The address where the text data will be run LOAD_WORDS% = &81B0 \ The address where the text data will be loaded IF _SNG45 OR _SOURCE_DISC Q% = _MAX_COMMANDER \ Set Q% to TRUE to max out the default commander, FALSE \ for the standard default commander ELIF _EXECUTIVE Q% = TRUE \ The Executive version starts with a maxed-out default \ commander ENDIF D% = &D000 \ The address where the ship blueprints get moved to \ after loading, so they go from &D000 to &F200 LS% = D%-1 \ The start of the descending ship line heap NOST = 18 \ The number of stardust particles in normal space (this \ goes down to 3 in witchspace) \ --- Mod: Code removed for two-player Elite: ---------> \NOSH = 20 \ The maximum number of ships in our local bubble of \ \ universe \ --- And replaced by: --------------------------------> NOSH = 10 \ The maximum number of ships in our local bubble of \ universe \ --- End of replacement ------------------------------> NTY = 34 \ The number of different ship types MSL = 1 \ Ship type for a missile SST = 2 \ Ship type for a Coriolis space station ESC = 3 \ Ship type for an escape pod PLT = 4 \ Ship type for an alloy plate OIL = 5 \ Ship type for a cargo canister AST = 7 \ Ship type for an asteroid SPL = 8 \ Ship type for a splinter SHU = 9 \ Ship type for a Shuttle CYL = 11 \ Ship type for a Cobra Mk III ANA = 14 \ Ship type for an Anaconda HER = 15 \ Ship type for a rock hermit (asteroid) COPS = 16 \ Ship type for a Viper SH3 = 17 \ Ship type for a Sidewinder KRA = 19 \ Ship type for a Krait ADA = 20 \ Ship type for an Adder WRM = 23 \ Ship type for a Worm CYL2 = 24 \ Ship type for a Cobra Mk III (pirate) ASP = 25 \ Ship type for an Asp Mk II THG = 29 \ Ship type for a Thargoid TGL = 30 \ Ship type for a Thargon CON = 31 \ Ship type for a Constrictor LGO = 32 \ Ship type for the Elite logo COU = 33 \ Ship type for a Cougar DOD = 34 \ Ship type for a Dodecahedron ("Dodo") space station JL = ESC \ Junk is defined as starting from the escape pod JH = SHU+2 \ Junk is defined as ending before the Cobra Mk III \ \ So junk is defined as the following: escape pod, \ alloy plate, cargo canister, asteroid, splinter, \ Shuttle or Transporter PACK = SH3 \ The first of the eight pack-hunter ships, which tend \ to spawn in groups. With the default value of PACK the \ pack-hunters are the Sidewinder, Mamba, Krait, Adder, \ Gecko, Cobra Mk I, Worm and Cobra Mk III (pirate) POW = 15 \ Pulse laser power Mlas = 50 \ Mining laser power Armlas = INT(128.5 + 1.5*POW) \ Military laser power NI% = 37 \ The number of bytes in each ship's data block (as \ stored in INWK and K%) BRKV = &0202 \ The break vector that we intercept to enable us to \ handle and display system errors OSWRCH = &FFEE \ The address for the OSWRCH routine OSBYTE = &FFF4 \ The address for the OSBYTE routine OSWORD = &FFF1 \ The address for the OSWORD routine OSFILE = &FFDD \ The address for the OSFILE routine OSCLI = &FFF7 \ The address for the OSCLI routine DOFE21 = 131 \ The OSWRCH number for the #DOFE21 command DOhfx = 132 \ The OSWRCH number for the #DOhfx command SETXC = 133 \ The OSWRCH number for the #SETXC command SETYC = 134 \ The OSWRCH number for the #SETYC command clyns = 135 \ The OSWRCH number for the #clyns command RDPARAMS = 136 \ The OSWRCH number for the #RDPARAMS command DODIALS = 138 \ The OSWRCH number for the #DODIALS command VIAE = 139 \ The OSWRCH number for the #VIAE command DOBULB = 140 \ The OSWRCH number for the #DOBULB command DOCATF = 141 \ The OSWRCH number for the #DOCATF command SETCOL = 142 \ The OSWRCH number for the #SETCOL command SETVDU19 = 143 \ The OSWRCH number for the #SETVDU19 command DOsvn = 144 \ The OSWRCH number for the #DOsvn command printcode = 146 \ The OSWRCH number for the #printcode command prilf = 147 \ The OSWRCH number for the #prilf command DOmsbar = 242 \ The OSWORD number for the #DOmsbar command wscn = 243 \ The OSWORD number for the #wscn command onescan = 244 \ The OSWORD number for the #onescan command DOdot = 245 \ The OSWORD number for the #DOdot command DODKS4 = 246 \ The OSWORD number for the #DODKS4 command X = 128 \ The centre x-coordinate of the 256 x 192 space view Y = 96 \ The centre y-coordinate of the 256 x 192 space view f0 = &20 \ Internal key number for red key f0 (Launch, Front) f1 = &71 \ Internal key number for red key f1 (Buy Cargo, Rear) f2 = &72 \ Internal key number for red key f2 (Sell Cargo, Left) f3 = &73 \ Internal key number for red key f3 (Equip Ship, Right) f4 = &14 \ Internal key number for red key f4 (Long-range Chart) f5 = &74 \ Internal key number for red key f5 (Short-range Chart) f6 = &75 \ Internal key number for red key f6 (Data on System) f7 = &16 \ Internal key number for red key f7 (Market Price) f8 = &76 \ Internal key number for red key f8 (Status Mode) f9 = &77 \ Internal key number for red key f9 (Inventory) YELLOW = %00001111 \ Four mode 1 pixels of colour 1 (yellow) RED = %11110000 \ Four mode 1 pixels of colour 2 (red, magenta or white) CYAN = %11111111 \ Four mode 1 pixels of colour 3 (cyan or white) GREEN = %10101111 \ Four mode 1 pixels of colour 3, 1, 3, 1 (cyan/yellow) WHITE = %11111010 \ Four mode 1 pixels of colour 3, 2, 3, 2 (cyan/red) MAGENTA = RED \ Four mode 1 pixels of colour 2 (red, magenta or white) DUST = WHITE \ Four mode 1 pixels of colour 3, 2, 3, 2 (cyan/red) RED2 = %00000011 \ Two mode 2 pixels of colour 1 (red) GREEN2 = %00001100 \ Two mode 2 pixels of colour 2 (green) YELLOW2 = %00001111 \ Two mode 2 pixels of colour 3 (yellow) BLUE2 = %00110000 \ Two mode 2 pixels of colour 4 (blue) MAG2 = %00110011 \ Two mode 2 pixels of colour 5 (magenta) CYAN2 = %00111100 \ Two mode 2 pixels of colour 6 (cyan) WHITE2 = %00111111 \ Two mode 2 pixels of colour 7 (white) STRIPE = %00100011 \ Two mode 2 pixels of colour 5, 1 (magenta/red) NRU% = 0 \ The number of planetary systems with extended system \ description overrides in the RUTOK table \ \ NRU% is set to 0 in the original source, but this is a \ bug, as it should match the number of entries in the \ RUGAL table \ \ This bug causes the Data on System screen to crash the \ game for a small number of systems - for example, the \ game will freeze if you bring up the Data on System \ screen after docking at Biarge in the first galaxy \ during the Constrictor mission RE = &23 \ The obfuscation byte used to hide the recursive tokens \ table from crackers viewing the binary code VE = &57 \ The obfuscation byte used to hide the extended tokens \ table from crackers viewing the binary code LL = 30 \ The length of lines (in characters) of justified text \ in the extended tokens system W2 = 16 \ The horizontal character spacing in the scroll text \ (i.e. the difference in x-coordinate between the \ left edges of adjacent characters in words) WY = 12 \ The vertical spacing between points in the scroll text \ grid for each character W2Y = 2.5*WY \ The vertical line spacing in the scroll text (i.e. the \ difference in y-coordinate between the tops of the \ characters in adjacent lines) D = 80 \ The distance from the camera (z-coordinate) of the \ bottom of the visible part of the Star Wars scroll \ text \ --- Mod: Code added for two-player Elite: -----------> PLAYER1SCAN = CYAN2 \ The colour that player 1 should look for in the \ scanner and compass PLAYER2SCAN = YELLOW2 \ The colour that player 2 should look for in the \ scanner and compass SHIPCOL = CYAN \ Ship colours on title screen \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: ZP \ Type: Workspace \ Address: &0000 to &00E1 \ Category: Workspaces \ Summary: Lots of important variables are stored in the zero page workspace \ as it is quicker and more space-efficient to access memory here \ \ ****************************************************************************** ORG &0000 \ Set the assembly address to &0000 .ZP SKIP 0 \ The start of the zero page workspace .RAND SKIP 4 \ Four 8-bit seeds for the random number generation \ system implemented in the DORND routine .T1 SKIP 1 \ Temporary storage, used in a number of places .SC SKIP 1 \ Screen address (low byte) \ \ Elite draws on-screen by poking bytes directly into \ screen memory, and SC(1 0) is typically set to the \ address of the character block containing the pixel \ we want to draw .SCH SKIP 1 \ Screen address (high byte) .XX16 SKIP 18 \ Temporary storage for a block of values, used in a \ number of places .P SKIP 3 \ Temporary storage, used in a number of places .NEEDKEY SKIP 1 \ Flag to ask the I/O processor to update the key logger \ buffer at KTRAN \ \ * 0 = do not update KTRAN \ \ * Non-zero = Ask the I/O processor to update KTRAN \ in the next call to LL9 or DOKEY \ \ A non-zero value has the following effect: \ \ * When DOKEY is called to scan for primary flight \ keys, the key logger buffer is updated before the \ key logger is updated \ \ * When drawing ships in LL9, the keyboard is scanned \ for key presses, which is used in the title screen \ and mission briefings .XX0 SKIP 2 \ Temporary storage, used to store the address of a ship \ blueprint. For example, it is used when we add a new \ ship to the local bubble in routine NWSHP, and it \ contains the address of the current ship's blueprint \ as we loop through all the nearby ships in the main \ flight loop .INF SKIP 2 \ Temporary storage, typically used for storing the \ address of a ship's data block, so it can be copied \ to and from the internal workspace at INWK .V SKIP 2 \ Temporary storage, typically used for storing an \ address pointer .XX SKIP 2 \ Temporary storage, typically used for storing a 16-bit \ x-coordinate .YY SKIP 2 \ Temporary storage, typically used for storing a 16-bit \ y-coordinate \ --- Mod: Code moved for two-player Elite: -----------> \.SUNX \ \SKIP 2 \ The 16-bit x-coordinate of the vertical centre axis \ \ of the sun (which might be off-screen) \ --- And replaced by: --------------------------------> .SUNXS SKIP 2 \ The address of SUNX or SUNXa, depending on which \ player view we are drawing \ --- End of replacement ------------------------------> .BETA SKIP 1 \ The current pitch angle beta, which is reduced from \ JSTY to a sign-magnitude value between -8 and +8 \ \ This describes how fast we are pitching our ship, and \ determines how fast the universe pitches around us \ \ The sign bit is also stored in BET2, while the \ opposite sign is stored in BET2+1 .BET1 SKIP 1 \ The magnitude of the pitch angle beta, i.e. |beta|, \ which is a positive value between 0 and 8 .XC SKIP 1 \ The x-coordinate of the text cursor (i.e. the text \ column), which can be from 0 to 32 \ \ A value of 0 denotes the leftmost column and 32 the \ rightmost column, but because the top part of the \ screen (the space view) has a border box that \ clashes with columns 0 and 32, text is only shown \ in columns 1-31 .YC SKIP 1 \ The y-coordinate of the text cursor (i.e. the text \ row), which can be from 0 to 23 \ \ The screen actually has 31 character rows if you \ include the dashboard, but the text printing routines \ only work on the top part (the space view), so the \ text cursor only goes up to a maximum of 23, the row \ just before the screen splits \ \ A value of 0 denotes the top row, but because the \ top part of the screen has a border box that clashes \ with row 0, text is always shown at row 1 or greater \ --- Mod: Code moved for two-player Elite: -----------> \.QQ22 \ \SKIP 2 \ The two hyperspace countdown counters \ \ \ \ Before a hyperspace jump, both QQ22 and QQ22+1 are \ \ set to 15 \ \ \ \ QQ22 is an internal counter that counts down by 1 \ \ each time TT102 is called, which happens every \ \ iteration of the main game loop. When it reaches \ \ zero, the on-screen counter in QQ22+1 gets \ \ decremented, and QQ22 gets set to 5 and the countdown \ \ continues (so the first tick of the hyperspace counter \ \ takes 15 iterations to happen, but subsequent ticks \ \ take 5 iterations each) \ \ \ \ QQ22+1 contains the number that's shown on-screen \ \ during the countdown. It counts down from 15 to 1, and \ \ when it hits 0, the hyperspace engines kick in \ --- End of moved code -----------------------------> .ECMA SKIP 1 \ The E.C.M. countdown timer, which determines whether \ an E.C.M. system is currently operating \ \ * 0 = E.C.M. is off \ \ * Non-zero = E.C.M. is on and is counting down \ \ The counter starts at 32 when an E.C.M. is activated, \ either by us or by an opponent, and it decreases by 1 \ in each iteration of the main flight loop until it \ reaches zero, at which point the E.C.M. switches off. \ Only one E.C.M. can be active at any one time, so \ there is only one counter \ --- Mod: Code added for two-player Elite: -----------> .LSOS SKIP 0 \ The address of LSO or LSOa, depending on which \ player view we are drawing (LSO is an alias of LSX, \ so the address is the same as LSXS) .LSXS SKIP 2 \ The address of LSX or LSXa, depending on which \ player view we are drawing (LSX is an alias of LSO, \ so the address is the same as LSOS) \ --- End of added code -------------------------------> .ALP1 SKIP 1 \ Magnitude of the roll angle alpha, i.e. |alpha|, \ which is a positive value between 0 and 31 .ALP2 SKIP 2 \ Bit 7 of ALP2 = sign of the roll angle in ALPHA \ \ Bit 7 of ALP2+1 = opposite sign to ALP2 and ALPHA .XX15 SKIP 0 \ Temporary storage, typically used for storing screen \ coordinates in line-drawing routines \ \ There are six bytes of storage, from XX15 TO XX15+5. \ The first four bytes have the following aliases: \ \ X1 = XX15 \ Y1 = XX15+1 \ X2 = XX15+2 \ Y2 = XX15+3 \ \ These are typically used for describing lines in terms \ of screen coordinates, i.e. (X1, Y1) to (X2, Y2) \ \ The last two bytes of XX15 do not have aliases .X1 SKIP 1 \ Temporary storage, typically used for x-coordinates in \ the line-drawing routines .Y1 SKIP 1 \ Temporary storage, typically used for y-coordinates in \ line-drawing routines .X2 SKIP 1 \ Temporary storage, typically used for x-coordinates in \ the line-drawing routines .Y2 SKIP 1 \ Temporary storage, typically used for y-coordinates in \ line-drawing routines SKIP 2 \ The last two bytes of the XX15 block .XX12 SKIP 6 \ Temporary storage for a block of values, used in a \ number of places .K SKIP 4 \ Temporary storage, used in a number of places .LAS SKIP 1 \ Contains the laser power of the laser fitted to the \ current space view (or 0 if there is no laser fitted \ to the current view) \ \ This gets set to bits 0-6 of the laser power byte from \ the commander data block, which contains the laser's \ power (bit 7 doesn't denote laser power, just whether \ or not the laser pulses, so that is not stored here) .MSTG SKIP 1 \ The current missile lock target \ \ * &FF = no target \ \ * 1-20 = the slot number of the ship that our \ missile is locked onto .XX1 SKIP 0 \ This is an alias for INWK that is used in the main \ ship-drawing routine at LL9 .INWK SKIP 33 \ The zero-page internal workspace for the current ship \ data block \ \ As operations on zero page locations are faster and \ have smaller opcodes than operations on the rest of \ the addressable memory, Elite tends to store oft-used \ data here. A lot of the routines in Elite need to \ access and manipulate ship data, so to make this an \ efficient exercise, the ship data is first copied from \ the ship data blocks at K% into INWK (or, when new \ ships are spawned, from the blueprints at XX21) .XX19 SKIP NI% - 34 \ XX19(1 0) shares its location with INWK(34 33), which \ contains the address of the ship line heap .NEWB SKIP 1 \ The ship's "new byte flags" (or NEWB flags) \ \ Contains details about the ship's type and associated \ behaviour, such as whether they are a trader, a bounty \ hunter, a pirate, currently hostile, in the process of \ docking, inside the hold having been scooped, and so \ on. The default values for each ship type are taken \ from the table at E% .LSP SKIP 1 \ The ball line heap pointer, which contains the number \ of the first free byte after the end of the LSX2 and \ LSY2 heaps .QQ15 SKIP 6 \ The three 16-bit seeds for the selected system, i.e. \ the one in the crosshairs in the Short-range Chart .K5 SKIP 0 \ Temporary storage used to store segment coordinates \ across successive calls to BLINE, the ball line \ routine .XX18 SKIP 0 \ Temporary storage used to store coordinates in the \ LL9 ship-drawing routine .QQ17 SKIP 1 \ Contains a number of flags that affect how text tokens \ are printed, particularly capitalisation \ \ * If all bits are set (255) then text printing is \ disabled \ \ * Bit 7: 0 = ALL CAPS \ 1 = Sentence Case, bit 6 determines the \ case of the next letter to print \ \ * Bit 6: 0 = print the next letter in upper case \ 1 = print the next letter in lower case \ \ * Bits 0-5: If any of bits 0-5 are set, print in \ lower case \ \ So: \ \ * QQ17 = 0 means case is set to ALL CAPS \ \ * QQ17 = %10000000 means Sentence Case, currently \ printing upper case \ \ * QQ17 = %11000000 means Sentence Case, currently \ printing lower case \ \ * QQ17 = %11111111 means printing is disabled .QQ19 SKIP 3 \ Temporary storage, used in a number of places .K6 SKIP 5 \ Temporary storage, typically used for storing \ coordinates during vector calculations .BET2 SKIP 2 \ Bit 7 of BET2 = sign of the pitch angle in BETA \ \ Bit 7 of BET2+1 = opposite sign to BET2 and BETA .DELTA SKIP 1 \ Our current speed, in the range 1-40 .DELT4 SKIP 2 \ Our current speed * 64 as a 16-bit value \ \ This is stored as DELT4(1 0), so the high byte in \ DELT4+1 therefore contains our current speed / 4 .U SKIP 1 \ Temporary storage, used in a number of places .Q SKIP 1 \ Temporary storage, used in a number of places .R SKIP 1 \ Temporary storage, used in a number of places .S SKIP 1 \ Temporary storage, used in a number of places .XSAV SKIP 1 \ Temporary storage for saving the value of the X \ register, used in a number of places .YSAV SKIP 1 \ Temporary storage for saving the value of the Y \ register, used in a number of places .XX17 SKIP 1 \ Temporary storage, used in BPRNT to store the number \ of characters to print, and as the edge counter in the \ main ship-drawing routine .QQ11 SKIP 1 \ The type of the current view: \ \ 0 = Space view \ 1 = Data on System screen (red key f6) \ Get commander name ("@", save/load commander) \ In-system jump just arrived ("J") \ Title screen \ 2 = Buy Cargo screen (red key f1) \ 3 = Mis-jump just arrived (witchspace) \ 4 = Sell Cargo screen (red key f2) \ 6 = Death screen \ 8 = Status Mode screen (red key f8) \ Inventory screen (red key f9) \ 16 = Market Price screen (red key f7) \ 32 = Equip Ship screen (red key f3) \ 64 = Long-range Chart (red key f4) \ 128 = Short-range Chart (red key f5) \ 255 = Launch view \ \ This value is typically set by calling routine TT66 .ZZ SKIP 1 \ Temporary storage, typically used for distance values .XX13 SKIP 1 \ Temporary storage, typically used in the line-drawing \ routines .MCNT SKIP 1 \ The main loop counter \ \ This counter determines how often certain actions are \ performed within the main loop .DL SKIP 1 \ Vertical sync flag \ \ DL gets set to 30 every time we reach vertical sync on \ the video system, which happens 50 times a second \ (50Hz). The WSCAN routine uses this to pause until the \ vertical sync, by setting DL to 0 and then monitoring \ its value until it changes to 30 .TYPE SKIP 1 \ The current ship type \ \ This is where we store the current ship type for when \ we are iterating through the ships in the local bubble \ as part of the main flight loop. See the table at XX21 \ for information about ship types .ALPHA SKIP 1 \ The current roll angle alpha, which is reduced from \ JSTX to a sign-magnitude value between -31 and +31 \ \ This describes how fast we are rolling our ship, and \ determines how fast the universe rolls around us \ \ The sign bit is also stored in ALP2, while the \ opposite sign is stored in ALP2+1 .PBUP SKIP 1 \ The size of the pixel buffer at PBUF (including the \ two OSWORD size bytes) .HBUP SKIP 1 \ The size of the horizontal line buffer at HBUF \ (including the two OSWORD size bytes) .LBUP SKIP 1 \ The size of the multi-segment line buffer at LBUF .QQ12 SKIP 1 \ Our "docked" status \ \ * 0 = we are not docked \ \ * &FF = we are docked .TGT SKIP 1 \ Temporary storage, typically used as a target value \ for counters when drawing explosion clouds and partial \ circles .COL SKIP 1 \ Temporary storage, used to store colour information \ when drawing pixels in the dashboard .FLAG SKIP 1 \ A flag that's used to define whether this is the first \ call to the ball line routine in BLINE, so it knows \ whether to wait for the second call before storing \ segment data in the ball line heap .CNT SKIP 1 \ Temporary storage, typically used for storing the \ number of iterations required when looping .CNT2 SKIP 1 \ Temporary storage, used in the planet-drawing routine \ to store the segment number where the arc of a partial \ circle should start .STP SKIP 1 \ The step size for drawing circles \ \ Circles in Elite are split up into 64 points, and the \ step size determines how many points to skip with each \ straight-line segment, so the smaller the step size, \ the smoother the circle. The values used are: \ \ * 2 for big planets and the circles on the charts \ \ * 4 for medium planets and the launch tunnel \ \ * 8 for small planets and the hyperspace tunnel \ \ As the step size increases we move from smoother \ circles at the top to more polygonal at the bottom. \ See the CIRCLE2 routine for more details .XX4 SKIP 1 \ Temporary storage, used in a number of places .XX20 SKIP 1 \ Temporary storage, used in a number of places \ --- Mod: Code removed for flicker-free ships: -------> \.XX14 \ \SKIP 1 \ This byte appears to be unused \ --- And replaced by: --------------------------------> .LSNUM SKIP 1 \ The pointer to the current position in the ship line \ heap as we work our way through the new ship's edges \ (and the corresponding old ship's edges) when drawing \ the ship in the main ship-drawing routine at LL9 .LSNUM2 SKIP 0 \ The size of the existing ship line heap for the ship \ we are drawing in LL9, i.e. the number of lines in the \ old ship that is currently shown on-screen and which \ we need to erase \ --- End of replacement ------------------------------> .RAT SKIP 1 \ Used to store different signs depending on the current \ space view, for use in calculating stardust movement .RAT2 SKIP 1 \ Temporary storage, used to store the pitch and roll \ signs when moving objects and stardust .K2 SKIP 4 \ Temporary storage, used in a number of places .widget SKIP 1 \ Temporary storage, used to store the original argument \ in A in the logarithmic FMLTU and LL28 routines \ --- Mod: Code moved for two-player Elite: -----------> \.safehouse \ \SKIP 6 \ Backup storage for the seeds for the selected system \ \ \ \ The seeds for the current system get stored here as \ \ soon as a hyperspace is initiated, so we can fetch \ \ them in the hyp1 routine. This fixes a bug in an \ \ earlier version where you could hyperspace while \ \ docking and magically appear in your destination \ \ station \ --- And replaced by: --------------------------------> .LSX2S SKIP 2 \ The address of LSX2 or LSX2a, depending on which \ player view we are drawing .LSY2S SKIP 2 \ The address of LSY2 or LSY2a, depending on which \ player view we are drawing .LSPS SKIP 2 \ The address of LSP or LSPa, depending on which player \ view we are drawing \ --- End of moved code -------------------------------> .messXC SKIP 1 \ Temporary storage, used to store the text column \ of the in-flight message in MESS, so it can be erased \ from the screen at the correct time ORG &00D1 \ Set the assembly address to &00D1 .T SKIP 1 \ Temporary storage, used in a number of places .K3 SKIP 0 \ Temporary storage, used in a number of places .XX2 SKIP 14 \ Temporary storage, used to store the visibility of the \ ship's faces during the ship-drawing routine at LL9 .K4 SKIP 2 \ Temporary storage, used in a number of places PRINT "ZP workspace from ", ~ZP, "to ", ~P%-1, "inclusive" \ ****************************************************************************** \ \ Name: XX3 \ Type: Workspace \ Address: &0100 to the top of the descending stack \ Category: Workspaces \ Summary: Temporary storage space for complex calculations \ \ ------------------------------------------------------------------------------ \ \ Used as heap space for storing temporary data during calculations. Shared with \ the descending 6502 stack, which works down from &01FF. \ \ ****************************************************************************** ORG &0100 \ Set the assembly address to &0100 .XX3 SKIP 256 \ Temporary storage, typically used for storing tables \ of values such as screen coordinates or ship data \ ****************************************************************************** \ \ ELITE RECURSIVE TEXT TOKEN FILE \ \ Produces the binary file WORDS.bin that gets loaded by elite-bcfs.asm. \ \ The recursive token table is loaded at &81B0 and is moved down to &0400 as \ part of elite-source.asm. The table binary also includes the sine and arctan \ tables, so the three parts end up as follows: \ \ * Recursive token table: QQ18 = &0400 to &07C0 \ * Sine lookup table: SNE = &07C0 to &07DF \ * Arctan lookup table: ACT = &07E0 to &07FF \ \ ****************************************************************************** ORG CODE_WORDS% \ Set the assembly address to CODE_WORDS% \ ****************************************************************************** \ \ Name: CHAR \ Type: Macro \ Category: Text \ Summary: Macro definition for characters in the recursive token table \ Deep dive: Printing text tokens \ \ ------------------------------------------------------------------------------ \ \ The following macro is used when building the recursive token table: \ \ CHAR 'x' Insert ASCII character "x" \ \ To include an apostrophe, use a backtick character, as in CHAR '`'. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ 'x' The character to insert into the table \ \ ****************************************************************************** MACRO CHAR x IF x = '`' EQUB 39 EOR RE ELSE EQUB x EOR RE ENDIF ENDMACRO \ ****************************************************************************** \ \ Name: TWOK \ Type: Macro \ Category: Text \ Summary: Macro definition for two-letter tokens in the token table \ Deep dive: Printing text tokens \ \ ------------------------------------------------------------------------------ \ \ The following macro is used when building the recursive token table: \ \ TWOK 'x', 'y' Insert two-letter token "xy" \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ 'x' The first letter of the two-letter token to insert into \ the table \ \ 'y' The second letter of the two-letter token to insert into \ the table \ \ ****************************************************************************** MACRO TWOK t, k IF t = 'A' AND k = 'L' EQUB 128 EOR RE ENDIF IF t = 'L' AND k = 'E' EQUB 129 EOR RE ENDIF IF t = 'X' AND k = 'E' EQUB 130 EOR RE ENDIF IF t = 'G' AND k = 'E' EQUB 131 EOR RE ENDIF IF t = 'Z' AND k = 'A' EQUB 132 EOR RE ENDIF IF t = 'C' AND k = 'E' EQUB 133 EOR RE ENDIF IF t = 'B' AND k = 'I' EQUB 134 EOR RE ENDIF IF t = 'S' AND k = 'O' EQUB 135 EOR RE ENDIF IF t = 'U' AND k = 'S' EQUB 136 EOR RE ENDIF IF t = 'E' AND k = 'S' EQUB 137 EOR RE ENDIF IF t = 'A' AND k = 'R' EQUB 138 EOR RE ENDIF IF t = 'M' AND k = 'A' EQUB 139 EOR RE ENDIF IF t = 'I' AND k = 'N' EQUB 140 EOR RE ENDIF IF t = 'D' AND k = 'I' EQUB 141 EOR RE ENDIF IF t = 'R' AND k = 'E' EQUB 142 EOR RE ENDIF IF t = 'A' AND k = '?' EQUB 143 EOR RE ENDIF IF t = 'E' AND k = 'R' EQUB 144 EOR RE ENDIF IF t = 'A' AND k = 'T' EQUB 145 EOR RE ENDIF IF t = 'E' AND k = 'N' EQUB 146 EOR RE ENDIF IF t = 'B' AND k = 'E' EQUB 147 EOR RE ENDIF IF t = 'R' AND k = 'A' EQUB 148 EOR RE ENDIF IF t = 'L' AND k = 'A' EQUB 149 EOR RE ENDIF IF t = 'V' AND k = 'E' EQUB 150 EOR RE ENDIF IF t = 'T' AND k = 'I' EQUB 151 EOR RE ENDIF IF t = 'E' AND k = 'D' EQUB 152 EOR RE ENDIF IF t = 'O' AND k = 'R' EQUB 153 EOR RE ENDIF IF t = 'Q' AND k = 'U' EQUB 154 EOR RE ENDIF IF t = 'A' AND k = 'N' EQUB 155 EOR RE ENDIF IF t = 'T' AND k = 'E' EQUB 156 EOR RE ENDIF IF t = 'I' AND k = 'S' EQUB 157 EOR RE ENDIF IF t = 'R' AND k = 'I' EQUB 158 EOR RE ENDIF IF t = 'O' AND k = 'N' EQUB 159 EOR RE ENDIF ENDMACRO \ ****************************************************************************** \ \ Name: CONT \ Type: Macro \ Category: Text \ Summary: Macro definition for control codes in the recursive token table \ Deep dive: Printing text tokens \ \ ------------------------------------------------------------------------------ \ \ The following macro is used when building the recursive token table: \ \ CONT n Insert control code token {n} \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ n The control code to insert into the table \ \ ****************************************************************************** MACRO CONT n EQUB n EOR RE ENDMACRO \ ****************************************************************************** \ \ Name: RTOK \ Type: Macro \ Category: Text \ Summary: Macro definition for recursive tokens in the recursive token table \ Deep dive: Printing text tokens \ \ ------------------------------------------------------------------------------ \ \ The following macro is used when building the recursive token table: \ \ RTOK n Insert recursive token [n] \ \ * Tokens 0-95 get stored as n + 160 \ \ * Tokens 128-145 get stored as n - 114 \ \ * Tokens 96-127 get stored as n \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ n The number of the recursive token to insert into the \ table, in the range 0 to 145 \ \ ****************************************************************************** MACRO RTOK n IF n >= 0 AND n <= 95 t = n + 160 ELIF n >= 128 t = n - 114 ELSE t = n ENDIF EQUB t EOR RE ENDMACRO \ ****************************************************************************** \ \ Name: QQ18 \ Type: Variable \ Category: Text \ Summary: The recursive token table for tokens 0-148 \ Deep dive: Printing text tokens \ \ ------------------------------------------------------------------------------ \ \ The encodings shown for each recursive text token use the following notation: \ \ {n} Control code n = 0 to 13 \ Two-letter token n = 128 to 159 \ [n] Recursive token n = 0 to 148 \ \ ****************************************************************************** .QQ18 \ --- Mod: Code removed for two-player Elite: ---------> \RTOK 111 \ Token 0: "FUEL SCOOPS ON {beep}" \RTOK 131 \ \CONT 7 \ Encoded as: "[111][131]{7}" \EQUB 0 \ \CHAR ' ' \ Token 1: " CHART" \CHAR 'C' \ \CHAR 'H' \ Encoded as: " CH<138>T" \TWOK 'A', 'R' \CHAR 'T' \EQUB 0 \ \CHAR 'G' \ Token 2: "GOVERNMENT" \CHAR 'O' \ \TWOK 'V', 'E' \ Encoded as: "GO<150>RNM<146>T" \CHAR 'R' \CHAR 'N' \CHAR 'M' \TWOK 'E', 'N' \CHAR 'T' \EQUB 0 \ \CHAR 'D' \ Token 3: "DATA ON {selected system name}" \TWOK 'A', 'T' \ \CHAR 'A' \ Encoded as: "D<145>A[131]{3}" \RTOK 131 \CONT 3 \EQUB 0 \ \TWOK 'I', 'N' \ Token 4: "INVENTORY{cr} \TWOK 'V', 'E' \ " \CHAR 'N' \ \CHAR 'T' \ Encoded as: "<140><150>NT<153>Y{12}" \TWOK 'O', 'R' \CHAR 'Y' \CONT 12 \EQUB 0 \ \CHAR 'S' \ Token 5: "SYSTEM" \CHAR 'Y' \ \CHAR 'S' \ Encoded as: "SYS<156>M" \TWOK 'T', 'E' \CHAR 'M' \EQUB 0 \ \CHAR 'P' \ Token 6: "PRICE" \TWOK 'R', 'I' \ \TWOK 'C', 'E' \ Encoded as: "P<158><133>" \EQUB 0 \ \CONT 2 \ Token 7: "{current system name} MARKET PRICES" \CHAR ' ' \ \TWOK 'M', 'A' \ Encoded as: "{2} <139>RKET [6]S" \CHAR 'R' \CHAR 'K' \CHAR 'E' \CHAR 'T' \CHAR ' ' \RTOK 6 \CHAR 'S' \EQUB 0 \ \TWOK 'I', 'N' \ Token 8: "INDUSTRIAL" \CHAR 'D' \ \TWOK 'U', 'S' \ Encoded as: "<140>D<136>T<158><128>" \CHAR 'T' \TWOK 'R', 'I' \TWOK 'A', 'L' \EQUB 0 \ \CHAR 'A' \ Token 9: "AGRICULTURAL" \CHAR 'G' \ \TWOK 'R', 'I' \ Encoded as: "AG<158>CULTU<148>L" \CHAR 'C' \CHAR 'U' \CHAR 'L' \CHAR 'T' \CHAR 'U' \TWOK 'R', 'A' \CHAR 'L' \EQUB 0 \ \TWOK 'R', 'I' \ Token 10: "RICH " \CHAR 'C' \ \CHAR 'H' \ Encoded as: "<158>CH " \CHAR ' ' \EQUB 0 \ \CHAR 'A' \ Token 11: "AVERAGE " \TWOK 'V', 'E' \ \TWOK 'R', 'A' \ Encoded as: "A<150><148><131> " \TWOK 'G', 'E' \CHAR ' ' \EQUB 0 \ \CHAR 'P' \ Token 12: "POOR " \CHAR 'O' \ \TWOK 'O', 'R' \ Encoded as: "PO<153> " \CHAR ' ' \EQUB 0 \ \TWOK 'M', 'A' \ Token 13: "MAINLY " \TWOK 'I', 'N' \ \CHAR 'L' \ Encoded as: "<139><140>LY " \CHAR 'Y' \CHAR ' ' \EQUB 0 \ \CHAR 'U' \ Token 14: "UNIT" \CHAR 'N' \ \CHAR 'I' \ Encoded as: "UNIT" \CHAR 'T' \EQUB 0 \ --- And replaced by: --------------------------------> EQUB 0 \ Token 0 EQUB 0 \ Token 1 EQUB 0 \ Token 2 EQUB 0 \ Token 3 EQUB 0 \ Token 4 EQUB 0 \ Token 5 EQUB 0 \ Token 6 EQUB 0 \ Token 7 EQUB 0 \ Token 8 EQUB 0 \ Token 9 EQUB 0 \ Token 10 CHAR 'C' \ Token 11: "COBRA" CHAR 'O' CHAR 'B' CHAR 'R' CHAR 'A' CHAR ' ' CHAR 'M' CHAR 'K' CHAR '3' EQUB 0 CHAR 'P' \ Token 12: "PYTHON" CHAR 'Y' CHAR 'T' CHAR 'H' TWOK 'O', 'N' EQUB 0 EQUB 0 \ Token 13 TWOK 'A', 'N' \ Token 14: "ANACONDA" CHAR 'A' CHAR 'C' TWOK 'O', 'N' CHAR 'D' CHAR 'A' EQUB 0 \ --- End of replacement ------------------------------> CHAR 'V' \ Token 15: "VIEW " CHAR 'I' \ CHAR 'E' \ Encoded as: "VIEW " CHAR 'W' CHAR ' ' EQUB 0 \ --- Mod: Code removed for two-player Elite: ---------> \TWOK 'Q', 'U' \ Token 16: "QUANTITY" \TWOK 'A', 'N' \ \TWOK 'T', 'I' \ Encoded as: "<154><155><151>TY" \CHAR 'T' \CHAR 'Y' \EQUB 0 \ \TWOK 'A', 'N' \ Token 17: "ANARCHY" \TWOK 'A', 'R' \ \CHAR 'C' \ Encoded as: "<155><138>CHY" \CHAR 'H' \CHAR 'Y' \EQUB 0 \ \CHAR 'F' \ Token 18: "FEUDAL" \CHAR 'E' \ \CHAR 'U' \ Encoded as: "FEUD<128>" \CHAR 'D' \TWOK 'A', 'L' \EQUB 0 \ \CHAR 'M' \ Token 19: "MULTI-GOVERNMENT" \CHAR 'U' \ \CHAR 'L' \ Encoded as: "MUL<151>-[2]" \TWOK 'T', 'I' \CHAR '-' \RTOK 2 \EQUB 0 \ --- And replaced by: --------------------------------> CHAR 'V' \ Token 16: "VIPER" CHAR 'I' CHAR 'P' TWOK 'E', 'R' EQUB 0 CHAR 'S' \ Token 17: "SIDEWINDER" CHAR 'I' CHAR 'D' CHAR 'E' CHAR 'W' CHAR 'I' CHAR 'N' CHAR 'D' TWOK 'E', 'R' EQUB 0 TWOK 'M', 'A' \ Token 18: "MAMBA" CHAR 'M' CHAR 'B' CHAR 'A' EQUB 0 CHAR 'K' \ Token 19: "KRAIT" CHAR 'R' CHAR 'A' CHAR 'I' CHAR 'T' EQUB 0 \ --- End of replacement ------------------------------> TWOK 'D', 'I' \ Token 20: "DICTATORSHIP" CHAR 'C' \ CHAR 'T' \ Encoded as: "<141>CT<145><153>[25]" TWOK 'A', 'T' TWOK 'O', 'R' RTOK 25 EQUB 0 RTOK 91 \ Token 21: "COMMUNIST" CHAR 'M' \ CHAR 'U' \ Encoded as: "[91]MUN<157>T" CHAR 'N' TWOK 'I', 'S' CHAR 'T' EQUB 0 CHAR 'C' \ Token 22: "CONFEDERACY" TWOK 'O', 'N' \ CHAR 'F' \ Encoded as: "C<159>F<152><144>ACY" TWOK 'E', 'D' TWOK 'E', 'R' CHAR 'A' CHAR 'C' CHAR 'Y' EQUB 0 CHAR 'D' \ Token 23: "DEMOCRACY" CHAR 'E' \ CHAR 'M' \ Encoded as: "DEMOC<148>CY" CHAR 'O' CHAR 'C' TWOK 'R', 'A' CHAR 'C' CHAR 'Y' EQUB 0 CHAR 'C' \ Token 24: "CORPORATE STATE" TWOK 'O', 'R' \ CHAR 'P' \ Encoded as: "C<153>P<153><145>E [43]<145>E" TWOK 'O', 'R' TWOK 'A', 'T' CHAR 'E' CHAR ' ' RTOK 43 TWOK 'A', 'T' CHAR 'E' EQUB 0 CHAR 'S' \ Token 25: "SHIP" CHAR 'H' \ CHAR 'I' \ Encoded as: "SHIP" CHAR 'P' EQUB 0 CHAR 'P' \ Token 26: "PRODUCT" RTOK 94 \ CHAR 'D' \ Encoded as: "P[94]]DUCT" CHAR 'U' CHAR 'C' CHAR 'T' EQUB 0 CHAR ' ' \ Token 27: " LASER" TWOK 'L', 'A' \ CHAR 'S' \ Encoded as: " <149>S<144>" TWOK 'E', 'R' EQUB 0 \ --- Mod: Code removed for two-player Elite: ---------> \CHAR 'H' \ Token 28: "HUMAN COLONIAL" \CHAR 'U' \ \CHAR 'M' \ Encoded as: "HUM<155> COL<159>I<128>" \TWOK 'A', 'N' \CHAR ' ' \CHAR 'C' \CHAR 'O' \CHAR 'L' \TWOK 'O', 'N' \CHAR 'I' \TWOK 'A', 'L' \EQUB 0 \ \CHAR 'H' \ Token 29: "HYPERSPACE " \CHAR 'Y' \ \CHAR 'P' \ Encoded as: "HYP<144>SPA<133> " \TWOK 'E', 'R' \CHAR 'S' \CHAR 'P' \CHAR 'A' \TWOK 'C', 'E' \CHAR ' ' \EQUB 0 \ --- And replaced by: --------------------------------> CHAR 'M' \ Token 28: "MORAY" CHAR 'O' TWOK 'R', 'A' CHAR 'Y' EQUB 0 CHAR 'T' \ Token 29: "THARGOID" CHAR 'H' TWOK 'A', 'R' CHAR 'G' CHAR 'O' CHAR 'I' CHAR 'D' EQUB 0 \ --- End of replacement ------------------------------> CHAR 'S' \ Token 30: "SHORT RANGE CHART" CHAR 'H' \ TWOK 'O', 'R' \ Encoded as: "SH<153>T [42][1]" CHAR 'T' CHAR ' ' RTOK 42 RTOK 1 EQUB 0 TWOK 'D', 'I' \ Token 31: "DISTANCE" RTOK 43 \ TWOK 'A', 'N' \ Encoded as: "<141>[43]<155><133>" TWOK 'C', 'E' EQUB 0 CHAR 'P' \ Token 32: "POPULATION" CHAR 'O' \ CHAR 'P' \ Encoded as: "POPUL<145>I<159>" CHAR 'U' CHAR 'L' TWOK 'A', 'T' CHAR 'I' TWOK 'O', 'N' EQUB 0 CHAR 'G' \ Token 33: "GROSS PRODUCTIVITY" RTOK 94 \ CHAR 'S' \ Encoded as: "G[94]SS [26]IVITY" CHAR 'S' CHAR ' ' RTOK 26 CHAR 'I' CHAR 'V' CHAR 'I' CHAR 'T' CHAR 'Y' EQUB 0 CHAR 'E' \ Token 34: "ECONOMY" CHAR 'C' \ TWOK 'O', 'N' \ Encoded as: "EC<159>OMY" CHAR 'O' CHAR 'M' CHAR 'Y' EQUB 0 IF _SNG45 OR _SOURCE_DISC CHAR ' ' \ Token 35: " LIGHT YEARS" CHAR 'L' \ CHAR 'I' \ Encoded as: " LIGHT YE<138>S" CHAR 'G' CHAR 'H' CHAR 'T' CHAR ' ' CHAR 'Y' CHAR 'E' TWOK 'A', 'R' CHAR 'S' EQUB 0 ELIF _EXECUTIVE CHAR ' ' \ Token 35: " L.Y." CHAR 'L' \ CHAR '.' \ Encoded as: " L.Y." CHAR 'Y' CHAR '.' EQUB 0 ENDIF TWOK 'T', 'E' \ Token 36: "TECH.LEVEL" CHAR 'C' \ CHAR 'H' \ Encoded as: "<156>CH.<129><150>L" CHAR '.' TWOK 'L', 'E' TWOK 'V', 'E' CHAR 'L' EQUB 0 CHAR 'C' \ Token 37: "CASH" CHAR 'A' \ CHAR 'S' \ Encoded as: "CASH" CHAR 'H' EQUB 0 CHAR ' ' \ Token 38: " BILLION" TWOK 'B', 'I' \ RTOK 129 \ Encoded as: " <134>[129]I<159>" CHAR 'I' TWOK 'O', 'N' EQUB 0 RTOK 122 \ Token 39: "GALACTIC CHART{galaxy number}" RTOK 1 \ CONT 1 \ Encoded as: "[122][1]{1}" EQUB 0 \ --- Mod: Code removed for two-player Elite: ---------> \CHAR 'T' \ Token 40: "TARGET LOST" \TWOK 'A', 'R' \ \TWOK 'G', 'E' \ Encoded as: "T<138><131>T LO[43]" \CHAR 'T' \CHAR ' ' \CHAR 'L' \CHAR 'O' \RTOK 43 \EQUB 0 \ --- And replaced by: --------------------------------> CHAR 'T' \ Token 40: "TARGET LOST" TWOK 'A', 'R' \ TWOK 'G', 'E' \ Encoded as: "T<138><131>T LOST" CHAR 'T' CHAR ' ' CHAR 'L' CHAR 'O' CHAR 'S' CHAR 'T' EQUB 0 \ --- End of replacement ------------------------------> RTOK 106 \ Token 41: "MISSILE JAMMED" CHAR ' ' \ CHAR 'J' \ Encoded as: "[106] JAMM<152>" CHAR 'A' CHAR 'M' CHAR 'M' TWOK 'E', 'D' EQUB 0 \ --- Mod: Code removed for two-player Elite: ---------> \CHAR 'R' \ Token 42: "RANGE" \TWOK 'A', 'N' \ \TWOK 'G', 'E' \ Encoded as: "R<155><131>" \EQUB 0 \ \CHAR 'S' \ Token 43: "ST" \CHAR 'T' \ \EQUB 0 \ Encoded as: "ST" \ \RTOK 16 \ Token 44: "QUANTITY OF " \CHAR ' ' \ \CHAR 'O' \ Encoded as: "[16] OF " \CHAR 'F' \CHAR ' ' \EQUB 0 \ \CHAR 'S' \ Token 45: "SELL" \CHAR 'E' \ \RTOK 129 \ Encoded as: "SE[129]" \EQUB 0 \ \CHAR ' ' \ Token 46: " CARGO{sentence case}" \CHAR 'C' \ \TWOK 'A', 'R' \ Encoded as: " C<138>GO{6}" \CHAR 'G' \CHAR 'O' \CONT 6 \EQUB 0 \ \CHAR 'E' \ Token 47: "EQUIP" \TWOK 'Q', 'U' \ \CHAR 'I' \ Encoded as: "E<154>IP" \CHAR 'P' \EQUB 0 \ \CHAR 'F' \ Token 48: "FOOD" \CHAR 'O' \ \CHAR 'O' \ Encoded as: "FOOD" \CHAR 'D' \EQUB 0 \ \TWOK 'T', 'E' \ Token 49: "TEXTILES" \CHAR 'X' \ \TWOK 'T', 'I' \ Encoded as: "<156>X<151>L<137>" \CHAR 'L' \TWOK 'E', 'S' \EQUB 0 \ \TWOK 'R', 'A' \ Token 50: "RADIOACTIVES" \TWOK 'D', 'I' \ \CHAR 'O' \ Encoded as: "<148><141>OAC<151><150>S" \CHAR 'A' \CHAR 'C' \TWOK 'T', 'I' \TWOK 'V', 'E' \CHAR 'S' \EQUB 0 \ \CHAR 'S' \ Token 51: "SLAVES" \TWOK 'L', 'A' \ \TWOK 'V', 'E' \ Encoded as: "S<149><150>S" \CHAR 'S' \EQUB 0 \ \CHAR 'L' \ Token 52: "LIQUOR/WINES" \CHAR 'I' \ \TWOK 'Q', 'U' \ Encoded as: "LI<154><153>/W<140><137>" \TWOK 'O', 'R' \CHAR '/' \CHAR 'W' \TWOK 'I', 'N' \TWOK 'E', 'S' \EQUB 0 \ \CHAR 'L' \ Token 53: "LUXURIES" \CHAR 'U' \ \CHAR 'X' \ Encoded as: "LUXU<158><137>" \CHAR 'U' \TWOK 'R', 'I' \TWOK 'E', 'S' \EQUB 0 \ \CHAR 'N' \ Token 54: "NARCOTICS" \TWOK 'A', 'R' \ \CHAR 'C' \ Encoded as: "N<138>CO<151>CS" \CHAR 'O' \TWOK 'T', 'I' \CHAR 'C' \CHAR 'S' \EQUB 0 \ \RTOK 91 \ Token 55: "COMPUTERS" \CHAR 'P' \ \CHAR 'U' \ Encoded as: "[91]PUT<144>S" \CHAR 'T' \TWOK 'E', 'R' \CHAR 'S' \EQUB 0 \ \TWOK 'M', 'A' \ Token 56: "MACHINERY" \CHAR 'C' \ \CHAR 'H' \ Encoded as: "<139>CH<140><144>Y" \TWOK 'I', 'N' \TWOK 'E', 'R' \CHAR 'Y' \EQUB 0 \ \CHAR 'A' \ Token 57: "ALLOYS" \CHAR 'L' \ \CHAR 'L' \ Encoded as: "ALLOYS" \CHAR 'O' \CHAR 'Y' \CHAR 'S' \EQUB 0 \ \CHAR 'F' \ Token 58: "FIREARMS" \CHAR 'I' \ \TWOK 'R', 'E' \ Encoded as: "FI<142><138>MS" \TWOK 'A', 'R' \CHAR 'M' \CHAR 'S' \EQUB 0 \ \CHAR 'F' \ Token 59: "FURS" \CHAR 'U' \ \CHAR 'R' \ Encoded as: "FURS" \CHAR 'S' \EQUB 0 \ \CHAR 'M' \ Token 60: "MINERALS" \TWOK 'I', 'N' \ \TWOK 'E', 'R' \ Encoded as: "M<140><144><128>S" \TWOK 'A', 'L' \CHAR 'S' \EQUB 0 \ \CHAR 'G' \ Token 61: "GOLD" \CHAR 'O' \ \CHAR 'L' \ Encoded as: "GOLD" \CHAR 'D' \EQUB 0 \ \CHAR 'P' \ Token 62: "PLATINUM" \CHAR 'L' \ \TWOK 'A', 'T' \ Encoded as: "PL<145><140>UM" \TWOK 'I', 'N' \CHAR 'U' \CHAR 'M' \EQUB 0 \ \TWOK 'G', 'E' \ Token 63: "GEM-STONES" \CHAR 'M' \ \CHAR '-' \ Encoded as: "<131>M-[43]<159><137>" \RTOK 43 \TWOK 'O', 'N' \TWOK 'E', 'S' \EQUB 0 \ \TWOK 'A', 'L' \ Token 64: "ALIEN ITEMS" \CHAR 'I' \ \TWOK 'E', 'N' \ Encoded as: "<128>I<146> [127]S" \CHAR ' ' \RTOK 127 \CHAR 'S' \EQUB 0 \ \CONT 12 \ Token 65: "{cr} \CHAR '1' \ 10{cash} CR{cr} \CHAR '0' \ 5{cash} CR{cr} \CONT 0 \ " \CHAR '5' \ \CONT 0 \ Encoded as: "{12}10{0}5{0}" \EQUB 0 \ \CHAR ' ' \ Token 66: " CR" \CHAR 'C' \ \CHAR 'R' \ Encoded as: " CR" \EQUB 0 \ \CHAR 'L' \ Token 67: "LARGE" \TWOK 'A', 'R' \ \TWOK 'G', 'E' \ Encoded as: "L<138><131>" \EQUB 0 \ \CHAR 'F' \ Token 68: "FIERCE" \CHAR 'I' \ \TWOK 'E', 'R' \ Encoded as: "FI<144><133>" \TWOK 'C', 'E' \EQUB 0 \ \CHAR 'S' \ Token 69: "SMALL" \TWOK 'M', 'A' \ \RTOK 129 \ Encoded as: "S<139>[129]" \EQUB 0 \ --- And replaced by: --------------------------------> CONT 6 \ Token 42: "LASERS" TWOK 'L', 'A' CHAR 'S' TWOK 'E', 'R' CHAR 'S' EQUB 0 CONT 6 \ Token 43: "CONTROLS" CHAR 'C' TWOK 'O', 'N' CHAR 'T' CHAR 'R' CHAR 'O' CHAR 'L' CHAR 'S' EQUB 0 CONT 6 \ Token 44: "KEYBOARD" CHAR 'K' CHAR 'E' CHAR 'Y' CHAR 'B' CHAR 'O' TWOK 'A', 'R' CHAR 'D' EQUB 0 CONT 6 \ Token 45: "JOYSTICK" CHAR 'J' CHAR 'O' CHAR 'Y' CHAR 'S' CHAR 'T' CHAR 'I' CHAR 'C' CHAR 'K' EQUB 0 CONT 8 \ Token 46: "AI PILOT" CHAR 'A' CHAR 'I' CHAR ' ' CONT 6 CHAR 'P' CHAR 'I' CHAR 'L' CHAR 'O' CHAR 'T' EQUB 0 CONT 6 \ Token 47: "DELTA 14B" CHAR 'D' CHAR 'E' CHAR 'L' CHAR 'T' CHAR 'A' CHAR ' ' CHAR '1' CHAR '4' CONT 8 CHAR 'B' EQUB 0 CONT 6 \ Token 48: "STANDARD" CHAR 'S' CHAR 'T' TWOK 'A', 'N' CHAR 'D' TWOK 'A', 'R' CHAR 'D' EQUB 0 CONT 6 \ Token 49: "UNIT +1" CHAR 'U' CHAR 'N' CHAR 'I' CHAR 'T' CHAR ' ' CHAR '+' CHAR '1' EQUB 0 CONT 6 \ Token 50: "NAVAL +3" CHAR 'N' CHAR 'A' CHAR 'V' TWOK 'A', 'L' CHAR ' ' CHAR '+' CHAR '2' EQUB 0 CONT 6 \ Token 51: "WINNER" CHAR 'W' TWOK 'I', 'N' CHAR 'N' TWOK 'E', 'R' EQUB 0 CHAR 'S' \ Token 52: "SURVIVOR" CHAR 'U' CHAR 'R' CHAR 'V' CHAR 'I' CHAR 'V' CHAR 'O' CHAR 'R' EQUB 0 CHAR ' ' \ Token 53: " HITS" CHAR 'H' CHAR 'I' CHAR 'T' CHAR 'S' EQUB 0 EQUB 0 \ Token 54: Unused CHAR 'P' \ Token 55: "PLAYER" TWOK 'L', 'A' CHAR 'Y' TWOK 'E', 'R' EQUB 0 RTOK 55 \ Token 56: "PLAYER 1" CHAR ' ' CHAR '1' EQUB 0 RTOK 55 \ Token 57: "PLAYER 2" CHAR ' ' CHAR '2' EQUB 0 CONT 6 \ Token 58: "MISSILES" RTOK 106 CHAR 'S' EQUB 0 CONT 6 \ Token 59: "PRESS" CHAR 'P' TWOK 'R', 'E' CHAR 'S' CHAR 'S' \CHAR ' ' \CHAR 'S' EQUB 0 CHAR ' ' \ Token 60: " F0 TO PLAY" \CHAR 'T' \CHAR 'O' \CHAR ' ' \CHAR 'S' \CHAR 'H' \CHAR 'U' \CHAR 'F' \CHAR 'F' \TWOK 'L', 'E' \CHAR ',' \CHAR ' ' CHAR 'F' CHAR '0' CHAR ' ' CHAR 'T' CHAR 'O' CHAR ' ' CHAR 'P' TWOK 'L', 'A' CHAR 'Y' EQUB 0 CHAR '-' \ Token 61: "--------" CHAR '-' CHAR '-' CHAR '-' CHAR '-' CHAR '-' CHAR '-' CHAR '-' EQUB 0 CONT 6 \ Token 62: "PULSE" CHAR 'P' CHAR 'U' CHAR 'L' CHAR 'S' CHAR 'E' EQUB 0 CONT 6 \ Token 63: "BEAM" TWOK 'B', 'E' CHAR 'A' CHAR 'M' EQUB 0 CONT 6 \ Token 64: "MINING" CHAR 'M' TWOK 'I', 'N' TWOK 'I', 'N' CHAR 'G' EQUB 0 CONT 6 \ Token 65: "MILITARY" CHAR 'M' CHAR 'I' CHAR 'L' CHAR 'I' CHAR 'T' TWOK 'A', 'R' CHAR 'Y' EQUB 0 CHAR 'N' \ Token 66: "NO" CHAR 'O' EQUB 0 CHAR 'Y' \ Token 67: "YES" CHAR 'E' CHAR 'S' EQUB 0 CHAR 'G' \ Token 68: "GAME OVER" CHAR 'A' CHAR 'M' CHAR 'E' CHAR ' ' CHAR 'O' TWOK 'V', 'E' CHAR 'R' EQUB 0 CHAR 'W' \ Token 69: "WINNER!" TWOK 'I', 'N' CHAR 'N' TWOK 'E', 'R' CHAR '!' EQUB 0 \ --- End of replacement ------------------------------> CHAR 'G' \ Token 70: "GREEN" TWOK 'R', 'E' \ TWOK 'E', 'N' \ Encoded as: "G<142><146>" EQUB 0 CHAR 'R' \ Token 71: "RED" TWOK 'E', 'D' \ EQUB 0 \ Encoded as: "R<152>" CHAR 'Y' \ Token 72: "YELLOW" CHAR 'E' \ RTOK 129 \ Encoded as: "YE[129]OW" CHAR 'O' CHAR 'W' EQUB 0 CHAR 'B' \ Token 73: "BLUE" CHAR 'L' \ CHAR 'U' \ Encoded as: "BLUE" CHAR 'E' EQUB 0 CHAR 'B' \ Token 74: "BLACK" TWOK 'L', 'A' \ CHAR 'C' \ Encoded as: "B<149>CK" CHAR 'K' EQUB 0 RTOK 136 \ Token 75: "HARMLESS" EQUB 0 \ \ Encoded as: "[136]" CHAR 'S' \ Token 76: "SLIMY" CHAR 'L' \ CHAR 'I' \ Encoded as: "SLIMY" CHAR 'M' CHAR 'Y' EQUB 0 CHAR 'B' \ Token 77: "BUG-EYED" CHAR 'U' \ CHAR 'G' \ Encoded as: "BUG-EY<152>" CHAR '-' CHAR 'E' CHAR 'Y' TWOK 'E', 'D' EQUB 0 CHAR 'H' \ Token 78: "HORNED" TWOK 'O', 'R' \ CHAR 'N' \ Encoded as: "H<153>N<152>" TWOK 'E', 'D' EQUB 0 CHAR 'B' \ Token 79: "BONY" TWOK 'O', 'N' \ CHAR 'Y' \ Encoded as: "B<159>Y" EQUB 0 CHAR 'F' \ Token 80: "FAT" TWOK 'A', 'T' \ EQUB 0 \ Encoded as: "F<145>" CHAR 'F' \ Token 81: "FURRY" CHAR 'U' \ CHAR 'R' \ Encoded as: "FURRY" CHAR 'R' CHAR 'Y' EQUB 0 RTOK 94 \ Token 82: "RODENT" CHAR 'D' \ TWOK 'E', 'N' \ Encoded as: "[94]D<146>T" CHAR 'T' EQUB 0 CHAR 'F' \ Token 83: "FROG" RTOK 94 \ CHAR 'G' \ Encoded as: "F[94]G" EQUB 0 CHAR 'L' \ Token 84: "LIZARD" CHAR 'I' \ TWOK 'Z', 'A' \ Encoded as: "LI<132>RD" CHAR 'R' CHAR 'D' EQUB 0 CHAR 'L' \ Token 85: "LOBSTER" CHAR 'O' \ CHAR 'B' \ Encoded as: "LOB[43]<144>" RTOK 43 TWOK 'E', 'R' EQUB 0 TWOK 'B', 'I' \ Token 86: "BIRD" CHAR 'R' \ CHAR 'D' \ Encoded as: "<134>RD" EQUB 0 CHAR 'H' \ Token 87: "HUMANOID" CHAR 'U' \ CHAR 'M' \ Encoded as: "HUM<155>OID" TWOK 'A', 'N' CHAR 'O' CHAR 'I' CHAR 'D' EQUB 0 CHAR 'F' \ Token 88: "FELINE" CHAR 'E' \ CHAR 'L' \ Encoded as: "FEL<140>E" TWOK 'I', 'N' CHAR 'E' EQUB 0 TWOK 'I', 'N' \ Token 89: "INSECT" CHAR 'S' \ CHAR 'E' \ Encoded as: "<140>SECT" CHAR 'C' CHAR 'T' EQUB 0 RTOK 11 \ Token 90: "AVERAGE RADIUS" TWOK 'R', 'A' \ TWOK 'D', 'I' \ Encoded as: "[11]<148><141><136>" TWOK 'U', 'S' EQUB 0 CHAR 'C' \ Token 91: "COM" CHAR 'O' \ CHAR 'M' \ Encoded as: "COM" EQUB 0 RTOK 91 \ Token 92: "COMMANDER" CHAR 'M' \ TWOK 'A', 'N' \ Encoded as: "[91]M<155>D<144>" CHAR 'D' TWOK 'E', 'R' EQUB 0 CHAR ' ' \ Token 93: " DESTROYED" CHAR 'D' \ TWOK 'E', 'S' \ Encoded as: " D<137>T[94]Y<152>" CHAR 'T' RTOK 94 CHAR 'Y' TWOK 'E', 'D' EQUB 0 CHAR 'R' \ Token 94: "RO" CHAR 'O' \ EQUB 0 \ Encoded as: "RO" RTOK 14 \ Token 95: "UNIT QUANTITY{cr} CHAR ' ' \ PRODUCT UNIT PRICE FOR SALE{cr}{lf} CHAR ' ' \ " RTOK 16 \ CONT 12 \ Encoded as: "[14] [16]{13} [26] [14] [6] F<153> CHAR ' ' \ SA<129>{12}{10}" RTOK 26 CHAR ' ' CHAR ' ' CHAR ' ' RTOK 14 CHAR ' ' RTOK 6 CHAR ' ' CHAR 'F' TWOK 'O', 'R' CHAR ' ' CHAR 'S' CHAR 'A' TWOK 'L', 'E' CONT 12 CONT 10 EQUB 0 CHAR 'F' \ Token 96: "FRONT" CHAR 'R' \ TWOK 'O', 'N' \ Encoded as: "FR<159>T" CHAR 'T' EQUB 0 TWOK 'R', 'E' \ Token 97: "REAR" TWOK 'A', 'R' \ EQUB 0 \ Encoded as: "<142><138>" TWOK 'L', 'E' \ Token 98: "LEFT" CHAR 'F' \ CHAR 'T' \ Encoded as: "<129>FT" EQUB 0 TWOK 'R', 'I' \ Token 99: "RIGHT" CHAR 'G' \ CHAR 'H' \ Encoded as: "<158>GHT" CHAR 'T' EQUB 0 IF _SNG45 OR _SOURCE_DISC RTOK 121 \ Token 100: "ENERGY LOW{beep}" CHAR 'L' \ CHAR 'O' \ Encoded as: "[121]LOW{7}" CHAR 'W' CONT 7 EQUB 0 ELIF _EXECUTIVE RTOK 121 \ Token 100: "ENERGY LOW,SIR{beep}" CHAR 'L' \ CHAR 'O' \ Encoded as: "[121]LOW,SIR{7}" CHAR 'W' CHAR ',' CHAR 'S' CHAR 'I' CHAR 'R' CONT 7 EQUB 0 ENDIF RTOK 99 \ Token 101: "RIGHT ON COMMANDER!" RTOK 131 \ RTOK 92 \ Encoded as: "[99][131][92]!" CHAR '!' EQUB 0 CHAR 'E' \ Token 102: "EXTRA " CHAR 'X' \ CHAR 'T' \ Encoded as: "EXT<148> " TWOK 'R', 'A' CHAR ' ' EQUB 0 CHAR 'P' \ Token 103: "PULSE LASER" CHAR 'U' \ CHAR 'L' \ Encoded as: "PULSE[27]" CHAR 'S' CHAR 'E' RTOK 27 EQUB 0 TWOK 'B', 'E' \ Token 104: "BEAM LASER" CHAR 'A' \ CHAR 'M' \ Encoded as: "<147>AM[27]" RTOK 27 EQUB 0 CHAR 'F' \ Token 105: "FUEL" CHAR 'U' \ CHAR 'E' \ Encoded as: "FUEL" CHAR 'L' EQUB 0 CHAR 'M' \ Token 106: "MISSILE" TWOK 'I', 'S' \ CHAR 'S' \ Encoded as: "M<157>SI<129>" CHAR 'I' TWOK 'L', 'E' EQUB 0 RTOK 67 \ Token 107: "LARGE CARGO{sentence case} BAY" RTOK 46 \ CHAR ' ' \ Encoded as: "[67][46] BAY" CHAR 'B' CHAR 'A' CHAR 'Y' EQUB 0 \ --- Mod: Code removed for two-player Elite: ---------> \CHAR 'E' \ Token 108: "E.C.M.SYSTEM" \CHAR '.' \ \CHAR 'C' \ Encoded as: "E.C.M.[5]" \CHAR '.' \CHAR 'M' \CHAR '.' \RTOK 5 \EQUB 0 \ --- And replaced by: --------------------------------> CONT 8 \ Token 108: "E.C.M." CHAR 'E' CHAR '.' CHAR 'C' CHAR '.' CHAR 'M' CHAR '.' EQUB 0 \ --- End of replacement ------------------------------> RTOK 102 \ Token 109: "EXTRA PULSE LASERS" RTOK 103 \ CHAR 'S' \ Encoded as: "[102][103]S" EQUB 0 RTOK 102 \ Token 110: "EXTRA BEAM LASERS" RTOK 104 \ CHAR 'S' \ Encoded as: "[102][104]S" EQUB 0 RTOK 105 \ Token 111: "FUEL SCOOPS" CHAR ' ' \ CHAR 'S' \ Encoded as: "[105] SCOOPS" CHAR 'C' CHAR 'O' CHAR 'O' CHAR 'P' CHAR 'S' EQUB 0 TWOK 'E', 'S' \ Token 112: "ESCAPE POD" CHAR 'C' \ CHAR 'A' \ Encoded as: "<137>CAPE POD" CHAR 'P' CHAR 'E' CHAR ' ' CHAR 'P' CHAR 'O' CHAR 'D' EQUB 0 RTOK 121 \ Token 113: "ENERGY BOMB" CHAR 'B' \ CHAR 'O' \ Encoded as: "[121]BOMB" CHAR 'M' CHAR 'B' EQUB 0 IF _SNG45 OR _EXECUTIVE RTOK 121 \ Token 114: "ENERGY UNIT" RTOK 14 \ EQUB 0 \ Encoded as: "[121][14]" ELIF _SOURCE_DISC RTOK 102 \ Token 114: "EXTRA ENERGY UNIT" RTOK 121 \ RTOK 14 \ Encoded as: "[102][121][14]" EQUB 0 ENDIF CHAR 'D' \ Token 115: "DOCKING COMPUTERS" CHAR 'O' \ CHAR 'C' \ Encoded as: "DOCK<140>G [55]" CHAR 'K' TWOK 'I', 'N' CHAR 'G' CHAR ' ' RTOK 55 EQUB 0 RTOK 122 \ Token 116: "GALACTIC HYPERSPACE " CHAR ' ' \ RTOK 29 \ Encoded as: "[122] [29]" EQUB 0 CHAR 'M' \ Token 117: "MILITARY LASER" CHAR 'I' \ CHAR 'L' \ Encoded as: "MILIT<138>Y [27]" CHAR 'I' CHAR 'T' TWOK 'A', 'R' CHAR 'Y' CHAR ' ' RTOK 27 EQUB 0 CHAR 'M' \ Token 118: "MINING LASER" TWOK 'I', 'N' \ TWOK 'I', 'N' \ Encoded as: "M<140><140>G [27]" CHAR 'G' CHAR ' ' RTOK 27 EQUB 0 RTOK 37 \ Token 119: "CASH:{cash} CR{cr} CHAR ':' \ " CONT 0 \ EQUB 0 \ Encoded as: "[37]:{0}" IF _SNG45 OR _SOURCE_DISC TWOK 'I', 'N' \ Token 120: "INCOMING MISSILE" RTOK 91 \ TWOK 'I', 'N' \ Encoded as: "<140>[91]<140>G [106]" CHAR 'G' CHAR ' ' RTOK 106 EQUB 0 ELIF _EXECUTIVE TWOK 'I', 'N' \ Token 120: "INCOMING MISSILE,SIR" RTOK 91 \ TWOK 'I', 'N' \ Encoded as: "<140>[91]<140>G [106],SIR" CHAR 'G' CHAR ' ' RTOK 106 CHAR ',' CHAR 'S' CHAR 'I' CHAR 'R' EQUB 0 ENDIF TWOK 'E', 'N' \ Token 121: "ENERGY " TWOK 'E', 'R' \ CHAR 'G' \ Encoded as: "<146><144>GY " CHAR 'Y' CHAR ' ' EQUB 0 CHAR 'G' \ Token 122: "GALACTIC" CHAR 'A' \ TWOK 'L', 'A' \ Encoded as: "GA<149>C<151>C" CHAR 'C' TWOK 'T', 'I' CHAR 'C' EQUB 0 RTOK 115 \ Token 123: "DOCKING COMPUTERS ON" CHAR ' ' \ CHAR 'O' \ Encoded as: "[115] ON" CHAR 'N' EQUB 0 CHAR 'A' \ Token 124: "ALL" RTOK 129 \ EQUB 0 \ Encoded as: "A[129]" CONT 5 \ Token 125: "FUEL: {fuel level} LIGHT YEARS{cr} TWOK 'L', 'E' \ CASH:{cash} CR{cr} CHAR 'G' \ LEGAL STATUS:" TWOK 'A', 'L' \ CHAR ' ' \ Encoded as: "{5}<129>G<128> [43]<145><136>:" RTOK 43 TWOK 'A', 'T' TWOK 'U', 'S' CHAR ':' EQUB 0 RTOK 92 \ Token 126: "COMMANDER {commander name}{cr} CHAR ' ' \ {cr} CONT 4 \ {cr} CONT 12 \ {sentence case}PRESENT SYSTEM{tab to CONT 12 \ column 21}:{current system name}{cr} CONT 12 \ HYPERSPACE SYSTEM{tab to column 21}: CONT 6 \ {selected system name}{cr} RTOK 145 \ CONDITION{tab to column 21}:" CHAR ' ' \ RTOK 5 \ Encoded as: "[92] {4}{12}{12}{12}{6}[145] [5]{9}{2} CONT 9 \ {12}[29][5]{9}{3}{13}C<159><141><151> CONT 2 \ <159>{9}" CONT 12 RTOK 29 RTOK 5 CONT 9 CONT 3 CONT 12 CHAR 'C' TWOK 'O', 'N' TWOK 'D', 'I' TWOK 'T', 'I' TWOK 'O', 'N' CONT 9 EQUB 0 CHAR 'I' \ Token 127: "ITEM" TWOK 'T', 'E' \ CHAR 'M' \ Encoded as: "I<156>M" EQUB 0 EQUB 0 \ Token 128: "" \ \ Encoded as: "" CHAR 'L' \ Token 129: "LL" CHAR 'L' \ EQUB 0 \ Encoded as: "LL" TWOK 'R', 'A' \ Token 130: "RATING:" TWOK 'T', 'I' \ CHAR 'N' \ Encoded as: "<148><151>NG:" CHAR 'G' CHAR ':' EQUB 0 CHAR ' ' \ Token 131: " ON " TWOK 'O', 'N' \ CHAR ' ' \ Encoded as: " <159> " EQUB 0 CONT 12 \ Token 132: "{cr} CONT 8 \ {all caps}EQUIPMENT: {sentence case}" RTOK 47 \ CHAR 'M' \ Encoded as: "{12}{8}[47]M<146>T:{6}" TWOK 'E', 'N' CHAR 'T' CHAR ':' CONT 6 EQUB 0 CHAR 'C' \ Token 133: "CLEAN" TWOK 'L', 'E' \ TWOK 'A', 'N' \ Encoded as: "C<129><155>" EQUB 0 CHAR 'O' \ Token 134: "OFFENDER" CHAR 'F' \ CHAR 'F' \ Encoded as: "OFF<146>D<144>" TWOK 'E', 'N' CHAR 'D' TWOK 'E', 'R' EQUB 0 CHAR 'F' \ Token 135: "FUGITIVE" CHAR 'U' \ CHAR 'G' \ Encoded as: "FUGI<151><150>" CHAR 'I' TWOK 'T', 'I' TWOK 'V', 'E' EQUB 0 CHAR 'H' \ Token 136: "HARMLESS" TWOK 'A', 'R' \ CHAR 'M' \ Encoded as: "H<138>M<129>SS" TWOK 'L', 'E' CHAR 'S' CHAR 'S' EQUB 0 CHAR 'M' \ Token 137: "MOSTLY HARMLESS" CHAR 'O' \ RTOK 43 \ Encoded as: "MO[43]LY [136]" CHAR 'L' CHAR 'Y' CHAR ' ' RTOK 136 EQUB 0 RTOK 12 \ Token 138: "POOR " EQUB 0 \ \ Encoded as: "[12]" RTOK 11 \ Token 139: "AVERAGE " EQUB 0 \ \ Encoded as: "[11]" CHAR 'A' \ Token 140: "ABOVE AVERAGE " CHAR 'B' \ CHAR 'O' \ Encoded as: "ABO<150> [11]" TWOK 'V', 'E' CHAR ' ' RTOK 11 EQUB 0 RTOK 91 \ Token 141: "COMPETENT" CHAR 'P' \ CHAR 'E' \ Encoded as: "[91]PET<146>T" CHAR 'T' TWOK 'E', 'N' CHAR 'T' EQUB 0 CHAR 'D' \ Token 142: "DANGEROUS" TWOK 'A', 'N' \ TWOK 'G', 'E' \ Encoded as: "D<155><131>[94]<136>" RTOK 94 TWOK 'U', 'S' EQUB 0 CHAR 'D' \ Token 143: "DEADLY" CHAR 'E' \ CHAR 'A' \ Encoded as: "DEADLY" CHAR 'D' CHAR 'L' CHAR 'Y' EQUB 0 CHAR '-' \ Token 144: "---- E L I T E ----" CHAR '-' \ CHAR '-' \ Encoded as: "---- E L I T E ----" CHAR '-' CHAR ' ' CHAR 'E' CHAR ' ' CHAR 'L' CHAR ' ' CHAR 'I' CHAR ' ' CHAR 'T' CHAR ' ' CHAR 'E' CHAR ' ' CHAR '-' CHAR '-' CHAR '-' CHAR '-' EQUB 0 CHAR 'P' \ Token 145: "PRESENT" TWOK 'R', 'E' \ CHAR 'S' \ Encoded as: "P<142>S<146>T" TWOK 'E', 'N' CHAR 'T' EQUB 0 CONT 8 \ Token 146: "{all caps}GAME OVER" CHAR 'G' \ CHAR 'A' \ Encoded as: "{8}GAME O<150>R" CHAR 'M' CHAR 'E' CHAR ' ' CHAR 'O' TWOK 'V', 'E' CHAR 'R' EQUB 0 IF _SNG45 EQUB &00, &00 \ These bytes appear to be unused and just contain EQUB &E4, &63 \ random workspace noise left over from the BBC Micro EQUB &A5 \ assembly process ELIF _EXECUTIVE EQUB &00, &00 \ These bytes appear to be unused and just contain EQUB &A5 \ random workspace noise left over from the BBC Micro \ assembly process ELIF _SOURCE_DISC SKIP 4 \ These bytes appear to be unused ENDIF \ --- Mod: Code added for two-player Elite: -----------> ORG &07C0 \ Set the assembly address back to the original address \ for SNE \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: SNE \ Type: Variable \ Category: Maths (Geometry) \ Summary: Sine/cosine table \ Deep dive: The sine, cosine and arctan tables \ Drawing circles \ Drawing ellipses \ \ ------------------------------------------------------------------------------ \ \ This lookup table contains sine values for the first half of a circle, from 0 \ to 180 degrees (0 to PI radians). In terms of circle or ellipse line segments, \ there are 64 segments in a circle, so this contains sine values for segments \ 0 to 31. \ \ In terms of segments, to calculate the sine of the angle at segment x, we look \ up the value in SNE + x, and to calculate the cosine of the angle we look up \ the value in SNE + ((x + 16) mod 32). \ \ In terms of radians, to calculate the following: \ \ sin(theta) * 256 \ \ where theta is in radians, we look up the value in: \ \ SNE + (theta * 10) \ \ To calculate the following: \ \ cos(theta) * 256 \ \ where theta is in radians, look up the value in: \ \ SNE + ((theta * 10) + 16) mod 32 \ \ Theta must be between 0 and 3.1 radians, so theta * 10 is between 0 and 31. \ \ ****************************************************************************** .SNE FOR I%, 0, 31 N = ABS(SIN((I% / 64) * 2 * PI)) IF N >= 1 EQUB 255 ELSE EQUB INT(256 * N + 0.5) ENDIF NEXT \ ****************************************************************************** \ \ Name: ACT \ Type: Variable \ Category: Maths (Geometry) \ Summary: Arctan table \ Deep dive: The sine, cosine and arctan tables \ \ ------------------------------------------------------------------------------ \ \ This table contains lookup values for arctangent calculations involving angles \ in the range 0 to 45 degrees (or 0 to PI / 4 radians). \ \ To calculate the value of theta in the following: \ \ theta = arctan(t) \ \ where 0 <= t < 1, we look up the value in: \ \ ACT + (t * 32) \ \ The result will be an integer representing the angle in radians, where 256 \ represents a full circle of 360 degrees (2 * PI radians). The result of the \ lookup will therefore be an integer in the range 0 to 31, as this represents \ 0 to 45 degrees (0 to PI / 4 radians). \ \ The table does not support values of t >= 1 or t < 0 directly, so if we need \ to calculate the arctangent for an angle greater than 45 degrees, we can apply \ the following calculation to the result from the table: \ \ * For t > 1, arctan(t) = 64 - arctan(1 / t) \ \ For negative values of t where -1 < t < 0, we can apply the following \ calculation to the result from the table: \ \ * For t < 0, arctan(-t) = 128 - arctan(t) \ \ Finally, if t < -1, we can do the first calculation to get arctan(|t|), and \ the second to get arctan(-|t|). \ \ ****************************************************************************** .ACT FOR I%, 0, 31 EQUB INT((128 / PI) * ATN(I% / 32) + 0.5) NEXT \ ****************************************************************************** \ \ Save WORDS.bin \ \ ****************************************************************************** PRINT "WORDS" PRINT "Assembled at ", ~CODE_WORDS% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_WORDS%) PRINT "Execute at ", ~LOAD_WORDS% PRINT "Reload at ", ~LOAD_WORDS% PRINT "S.WORDS ", ~CODE_WORDS%, " ", ~P%, " ", ~LOAD_WORDS%, " ", ~LOAD_WORDS% SAVE "3-assembled-output/WORDS.bin", CODE_WORDS%, P%, LOAD_WORDS% \ ****************************************************************************** \ \ Name: UP \ Type: Workspace \ Address: &0800 to &0973 \ Category: Workspaces \ Summary: Ship slots, variables \ \ ****************************************************************************** ORG &0800 \ Set the assembly address to &0800 .UP SKIP 0 \ The start of the UP workspace \.QQ16 \ This QQ16 label is present in the original source, but \ it is overridden by the QQ16 label in the ELITE A \ section, so this declaration has no effect. BeebAsm \ does not allow labels to be defined twice, so this one \ is commented out SKIP 65 \ These bytes appear to be unused .KL SKIP 1 \ The following bytes implement a key logger that \ enables Elite to scan for concurrent key presses of \ the primary flight keys, plus a secondary flight key \ \ If a key is being pressed that is not in the keyboard \ table at KYTB, it can be stored here (as seen in \ routine DK4, for example) .KY1 SKIP 1 \ "?" is being pressed (slow down) \ \ * 0 = no \ \ * Non-zero = yes .KY2 SKIP 1 \ Space is being pressed (speed up) \ \ * 0 = no \ \ * Non-zero = yes .KY3 SKIP 1 \ "<" is being pressed (roll left) \ \ * 0 = no \ \ * Non-zero = yes .KY4 SKIP 1 \ ">" is being pressed (roll right) \ \ * 0 = no \ \ * Non-zero = yes .KY5 SKIP 1 \ "X" is being pressed (pull up) \ \ * 0 = no \ \ * Non-zero = yes .KY6 SKIP 1 \ "S" is being pressed (pitch down) \ \ * 0 = no \ \ * Non-zero = yes .KY7 SKIP 1 \ "A" is being pressed (fire lasers) \ \ * 0 = no \ \ * Non-zero = yes \ \ This is also set when the joystick fire button has \ been pressed \ --- Mod: Code removed for two-player Elite: ---------> \.KY12 \ \SKIP 1 \ TAB is being pressed (energy bomb) \ \ \ \ * 0 = no \ \ \ \ * Non-zero = yes \ \.KY13 \ \SKIP 1 \ ESCAPE is being pressed (launch escape pod) \ \ \ \ * 0 = no \ \ \ \ * Non-zero = yes \ --- And replaced by: --------------------------------> .KY12 SKIP 1 \ "[" is being pressed (player 2 arm missile) \ \ * 0 = no \ \ * Non-zero = yes .KY13 SKIP 1 \ "@" is being pressed (player 2 fire missile) \ \ * 0 = no \ \ * Non-zero = yes \ --- End of replacement ------------------------------> .KY14 SKIP 1 \ "T" is being pressed (target missile) \ \ * 0 = no \ \ * Non-zero = yes .KY15 SKIP 1 \ "U" is being pressed (unarm missile) \ \ * 0 = no \ \ * Non-zero = yes .KY16 SKIP 1 \ "M" is being pressed (fire missile) \ \ * 0 = no \ \ * Non-zero = yes .KY17 SKIP 1 \ "E" is being pressed (activate E.C.M.) \ \ * 0 = no \ \ * Non-zero = yes \ --- Mod: Code removed for two-player Elite: ---------> \.KY18 \ \SKIP 1 \ "J" is being pressed (in-system jump) \ \ \ \ * 0 = no \ \ \ \ * Non-zero = yes \ \.KY19 \ \SKIP 1 \ "C" is being pressed (activate docking computer) \ \ \ \ * 0 = no \ \ \ \ * Non-zero = yes \ \.KY20 \ \SKIP 1 \ "P" is being pressed (deactivate docking computer) \ \ \ \ * 0 = no \ \ \ \ * Non-zero = yes \ --- And replaced by: --------------------------------> .KY18 SKIP 1 \ "\" is being pressed (player 2 E.C.M.) \ \ * 0 = no \ \ * Non-zero = yes .KY19 SKIP 1 \ "_" is being pressed (player 2 unarm missile) \ \ * 0 = no \ \ * Non-zero = yes .KY20 SKIP 1 \ RETURN is being pressed (player 2 speed up) \ \ * 0 = no \ \ * Non-zero = yes .KY21 SKIP 1 \ "]" is being pressed (player 2 slow down) \ \ * 0 = no \ \ * Non-zero = yes .KY22 SKIP 1 \ Player 2 fire button is being pressed \ \ * 0 = no \ \ * Non-zero = yes \ --- End of replacement ------------------------------> .FRIN SKIP NOSH + 1 \ Slots for the ships in the local bubble of universe \ \ There are #NOSH + 1 slots, but the ship-spawning \ routine at NWSHP only populates #NOSH of them, so \ there are 21 slots but only 20 are used for ships \ (the last slot is effectively used as a null \ terminator when shuffling the slots down in the \ KILLSHP routine) .MANY SKIP SST \ The number of ships of each type in the local bubble \ of universe \ \ The number of ships of type X in the local bubble is \ stored at MANY+X .SSPR SKIP NTY + 1 - SST \ "Space station present" flag \ \ * Non-zero if we are inside the space station's safe \ zone \ \ * 0 if we aren't (in which case we can show the sun) \ \ This flag is at MANY+SST, which is no coincidence, as \ MANY+SST is a count of how many space stations there \ are in our local bubble, which is the same as saying \ "space station present" .JUNK SKIP 1 \ The amount of junk in the local bubble \ \ "Junk" is defined as being one of these: \ \ * Escape pod \ * Alloy plate \ * Cargo canister \ * Asteroid \ * Splinter \ * Shuttle \ * Transporter \ * Rock hermit \ \ Apart from the rock hermit, junk is the range of ship \ types from #JL to #JH - 1 .auto SKIP 1 \ Docking computer activation status \ \ * 0 = Docking computer is off \ \ * Non-zero = Docking computer is running .ECMP SKIP 1 \ Our E.C.M. status \ \ * 0 = E.C.M. is off \ \ * Non-zero = E.C.M. is on .MJ SKIP 1 \ Are we in witchspace (i.e. have we mis-jumped)? \ \ * 0 = no, we are in normal space \ \ * &FF = yes, we are in witchspace .CABTMP SKIP 1 \ Cabin temperature \ \ The ambient cabin temperature in deep space is 30, \ which is displayed as one notch on the dashboard bar \ \ We get higher temperatures closer to the sun \ \ CABTMP shares a location with MANY, but that's OK as \ MANY+0 would contain the number of ships of type 0, \ and as there is no ship type 0 (they start at 1), the \ byte at MANY+0 is not used for storing a ship type \ and can be used for the cabin temperature instead .LAS2 SKIP 1 \ Laser power for the current laser \ \ * Bits 0-6 contain the laser power of the current \ space view \ \ * Bit 7 denotes whether or not the laser pulses: \ \ * 0 = pulsing laser \ \ * 1 = beam laser (i.e. always on) .MSAR SKIP 1 \ The targeting state of our leftmost missile \ \ * 0 = missile is not looking for a target, or it \ already has a target lock (indicator is not \ yellow/white) \ \ * Non-zero = missile is currently looking for a \ target (indicator is yellow/white) .VIEW SKIP 1 \ The number of the current space view \ \ * 0 = front \ * 1 = rear \ * 2 = left \ * 3 = right .LASCT SKIP 1 \ The laser pulse count for the current laser \ \ This is a counter that defines the gap between the \ pulses of a pulse laser. It is set as follows: \ \ * 0 for a beam laser \ \ * 10 for a pulse laser \ \ It gets decremented by 2 on each iteration round the \ main game loop and is set to a non-zero value for \ pulse lasers only \ \ The laser only fires when the value of LASCT hits \ zero, so for pulse lasers with a value of 10, that \ means the laser fires once every four iterations \ round the main game loop (LASCT = 10, 6, 2, 0) \ \ In comparison, beam lasers fire continuously as the \ value of LASCT is always 0 .GNTMP SKIP 1 \ Laser temperature (or "gun temperature") \ \ If the laser temperature exceeds 242 then the laser \ overheats and cannot be fired again until it has \ cooled down .HFX SKIP 1 \ A flag that toggles the hyperspace colour effect \ \ * 0 = no colour effect \ \ * Non-zero = hyperspace colour effect enabled \ \ When HFX is set to 1, the mode 1 screen that makes \ up the top part of the display is temporarily switched \ to mode 2 (the same screen mode as the dashboard), \ which has the effect of blurring and colouring the \ hyperspace rings in the top part of the screen. The \ code to do this is in the LINSCN routine, which is \ called as part of the screen mode routine at IRQ1. \ It's in LINSCN that HFX is checked, and if it is \ non-zero, the top part of the screen is not switched \ to mode 1, thus leaving the top part of the screen in \ the more colourful mode 2 .EV SKIP 1 \ The "extra vessels" spawning counter \ \ This counter is set to 0 on arrival in a system and \ following an in-system jump, and is bumped up when we \ spawn bounty hunters or pirates (i.e. "extra vessels") \ \ It decreases by 1 each time we consider spawning more \ "extra vessels" in part 4 of the main game loop, so \ increasing the value of EV has the effect of delaying \ the spawning of more vessels \ \ In other words, this counter stops bounty hunters and \ pirates from continually appearing, and ensures that \ there's a delay between spawnings .DLY SKIP 1 \ In-flight message delay \ \ This counter is used to keep an in-flight message up \ for a specified time before it gets removed. The value \ in DLY is decremented each time we start another \ iteration of the main game loop at TT100 .de SKIP 1 \ Equipment destruction flag \ \ * Bit 1 denotes whether or not the in-flight message \ about to be shown by the MESS routine is about \ destroyed equipment: \ \ * 0 = the message is shown normally \ \ * 1 = the string " DESTROYED" gets added to the \ end of the message .JSTX SKIP 1 \ Our current roll rate \ \ This value is shown in the dashboard's RL indicator, \ and determines the rate at which we are rolling \ \ The value ranges from 1 to 255 with 128 as the centre \ point, so 1 means roll is decreasing at the maximum \ rate, 128 means roll is not changing, and 255 means \ roll is increasing at the maximum rate \ \ This value is updated by "<" and ">" key presses, or \ if joysticks are enabled, from the joystick. If \ keyboard damping is enabled (which it is by default), \ the value is slowly moved towards the centre value of \ 128 (no roll) if there are no key presses or joystick \ movement .JSTY SKIP 1 \ Our current pitch rate \ \ This value is shown in the dashboard's DC indicator, \ and determines the rate at which we are pitching \ \ The value ranges from 1 to 255 with 128 as the centre \ point, so 1 means pitch is decreasing at the maximum \ rate, 128 means pitch is not changing, and 255 means \ pitch is increasing at the maximum rate \ \ This value is updated by "S" and "X" key presses, or \ if joysticks are enabled, from the joystick. If \ keyboard damping is enabled (which it is by default), \ the value is slowly moved towards the centre value of \ 128 (no pitch) if there are no key presses or joystick \ movement .XSAV2 SKIP 1 \ This byte appears to be unused .YSAV2 SKIP 1 \ This byte appears to be unused .NAME SKIP 8 \ The current commander name \ \ The commander name can be up to 7 characters (the DFS \ limit for filenames), and is terminated by a carriage \ return .TP SKIP 1 \ The current mission status \ \ * Bits 0-1 = Mission 1 status \ \ * %00 = Mission not started \ * %01 = Mission in progress, hunting for ship \ * %11 = Constrictor killed, not debriefed yet \ * %10 = Mission and debrief complete \ \ * Bits 2-3 = Mission 2 status \ \ * %00 = Mission not started \ * %01 = Mission in progress, plans not picked up \ * %10 = Mission in progress, plans picked up \ * %11 = Mission complete .QQ0 SKIP 1 \ The current system's galactic x-coordinate (0-256) .QQ1 SKIP 1 \ The current system's galactic y-coordinate (0-256) .QQ21 SKIP 6 \ The three 16-bit seeds for the current galaxy \ \ These seeds define system 0 in the current galaxy, so \ they can be used as a starting point to generate all \ 256 systems in the galaxy \ \ Using a galactic hyperdrive rotates each byte to the \ left (rolling each byte within itself) to get the \ seeds for the next galaxy, so after eight galactic \ jumps, the seeds roll around to the first galaxy again .CASH SKIP 4 \ Our current cash pot \ \ The cash stash is stored as a 32-bit unsigned integer, \ with the most significant byte in CASH and the least \ significant in CASH+3. This is big-endian, which is \ the opposite way round to most of the numbers used in \ Elite - to use our notation for multi-byte numbers, \ the amount of cash is CASH(0 1 2 3) .QQ14 SKIP 1 \ Our current fuel level (0-70) \ \ The fuel level is stored as the number of light years \ multiplied by 10, so QQ14 = 1 represents 0.1 light \ years, and the maximum possible value is 70, for 7.0 \ light years .COK SKIP 1 \ Flags used to generate the competition code .GCNT SKIP 1 \ The number of the current galaxy (0-7) \ \ When this is displayed in-game, 1 is added to the \ number, so we start in galaxy 1 in-game, but it's \ stored as galaxy 0 internally \ \ The galaxy number increases by one every time a \ galactic hyperdrive is used, and wraps back around to \ the start after eight galaxies .LASER SKIP 4 \ The specifications of the lasers fitted to each of the \ four space views \ \ * Byte #0 = front view \ \ * Byte #1 = rear view \ \ * Byte #2 = left view \ \ * Byte #3 = right view \ \ The value for each view is as follows: \ \ * 0 = no laser is fitted to this view \ \ * Non-zero = a laser is fitted to this view, with \ the following specification: \ \ * Bits 0-6 contain the laser's power \ \ * Bit 7 determines whether or not the laser pulses \ (0 = pulse or mining laser) or is always on \ (1 = beam or military laser) SKIP 2 \ These bytes appear to be unused (they were originally \ used for up/down lasers, but they were dropped) .CRGO SKIP 1 \ Our ship's cargo capacity \ \ * 22 = standard cargo bay of 20 tonnes \ \ * 37 = large cargo bay of 35 tonnes \ \ The value is two greater than the actual capacity to \ make the maths in tnpr slightly more efficient .QQ20 SKIP 17 \ The contents of our cargo hold \ \ The amount of market item X that we have in our hold \ can be found in the X-th byte of QQ20. For example: \ \ * QQ20 contains the amount of food (item 0) \ \ * QQ20+7 contains the amount of computers (item 7) \ \ See QQ23 for a list of market item numbers and their \ storage units .ECM SKIP 1 \ E.C.M. system \ \ * 0 = not fitted \ \ * &FF = fitted .BST SKIP 1 \ Fuel scoops (BST stands for "barrel status") \ \ * 0 = not fitted \ \ * &FF = fitted .BOMB SKIP 1 \ Energy bomb \ \ * 0 = not fitted \ \ * &7F = fitted .ENGY SKIP 1 \ Energy unit \ \ * 0 = not fitted \ \ * Non-zero = fitted \ \ The actual value determines the refresh rate of our \ energy banks, as they refresh by ENGY+1 each time (so \ our ship's energy level goes up by 2 each time if we \ have an energy unit fitted, otherwise it goes up by 1) \ \ The enhanced versions of Elite set ENGY to 2 as the \ reward for completing mission 2, where we receive a \ special naval energy unit that recharges at a fast \ rate than a standard energy unit, i.e. by 3 each time .DKCMP SKIP 1 \ Docking computer \ \ * 0 = not fitted \ \ * &FF = fitted .GHYP SKIP 1 \ Galactic hyperdrive \ \ * 0 = not fitted \ \ * &FF = fitted .ESCP SKIP 1 \ Escape pod \ \ * 0 = not fitted \ \ * &FF = fitted SKIP 4 \ These bytes appear to be unused .NOMSL SKIP 1 \ The number of missiles we have fitted (0-4) .FIST SKIP 1 \ Our legal status (FIST stands for "fugitive/innocent \ status") \ \ * 0 = Clean \ \ * 1-49 = Offender \ \ * 50+ = Fugitive \ \ You get 64 points if you kill a cop, so that's a fast \ ticket to fugitive status .AVL SKIP 17 \ Market availability in the current system \ \ The available amount of market item X is stored in \ the X-th byte of AVL, so for example: \ \ * AVL contains the amount of food (item 0) \ \ * AVL+7 contains the amount of computers (item 7) \ \ See QQ23 for a list of market item numbers and their \ storage units .QQ26 SKIP 1 \ A random value used to randomise market data \ \ This value is set to a new random number for each \ change of system, so we can add a random factor into \ the calculations for market prices .TALLY SKIP 2 \ Our combat rank \ \ The combat rank is stored as the number of kills, in a \ 16-bit number TALLY(1 0) - so the high byte is in \ TALLY+1 and the low byte in TALLY \ \ If the high byte in TALLY+1 is 0 then we have between \ 0 and 255 kills, so our rank is Harmless, Mostly \ Harmless, Poor, Average Above Average or Competent, \ according to the value of the low byte in TALLY: \ \ Harmless %00000000 to %00000111 = 0 to 7 \ Mostly Harmless %00001000 to %00001111 = 8 to 15 \ Poor %00010000 to %00011111 = 16 to 31 \ Average %00100000 to %00111111 = 32 to 63 \ Above Average %01000000 to %01111111 = 64 to 127 \ Competent %10000000 to %11111111 = 128 to 255 \ \ Note that the Competent range also covers kill counts \ from 256 to 511, as follows \ \ If the high byte in TALLY+1 is non-zero then we are \ Competent, Dangerous, Deadly or Elite, according to \ the value of TALLY(1 0): \ \ Competent (1 0) to (1 255) = 256 to 511 kills \ Dangerous (2 0) to (9 255) = 512 to 2559 kills \ Deadly (10 0) to (24 255) = 2560 to 6399 kills \ Elite (25 0) and up = 6400 kills and up \ \ You can see the rating calculation in the STATUS \ subroutine .SVC SKIP 1 \ The save count \ \ When a new commander is created, the save count gets \ set to 128. This value gets halved each time the \ commander file is saved, but it is otherwise unused. \ It is presumably part of the security system for the \ competition, possibly another flag to catch out \ entries with manually altered commander files SKIP 2 \ The commander file checksum \ \ These two bytes are reserved for the commander file \ checksum, so when the current commander block is \ copied from here to the last saved commander block at \ NA%, CHK and CHK2 get overwritten NT% = SVC + 2 - TP \ This sets the variable NT% to the size of the current \ commander data block, which starts at TP and ends at \ SVC+2 (inclusive) .MCH SKIP 1 \ The text token number of the in-flight message that is \ currently being shown, and which will be removed by \ the me2 routine when the counter in DLY reaches zero .FSH SKIP 1 \ Forward shield status \ \ * 0 = empty \ \ * &FF = full .ASH SKIP 1 \ Aft shield status \ \ * 0 = empty \ \ * &FF = full .ENERGY SKIP 1 \ Energy bank status \ \ * 0 = empty \ \ * &FF = full .COMX SKIP 1 \ The x-coordinate of the compass dot .COMY SKIP 1 \ The y-coordinate of the compass dot .QQ24 SKIP 1 \ Temporary storage, used to store the current market \ item's price in routine TT151 .QQ25 SKIP 1 \ Temporary storage, used to store the current market \ item's availability in routine TT151 .QQ28 SKIP 1 \ The current system's economy (0-7) \ \ * 0 = Rich Industrial \ * 1 = Average Industrial \ * 2 = Poor Industrial \ * 3 = Mainly Industrial \ * 4 = Mainly Agricultural \ * 5 = Rich Agricultural \ * 6 = Average Agricultural \ * 7 = Poor Agricultural .QQ29 SKIP 1 \ Temporary storage, used in a number of places .gov SKIP 1 \ The current system's government type (0-7) .tek SKIP 1 \ The current system's tech level (0-14) .SLSP SKIP 2 \ The address of the bottom of the ship line heap \ \ The ship line heap is a descending block of memory \ that starts at D% and descends down to SLSP. It can be \ extended downwards by the NWSHP routine when adding \ new ships (and their associated ship line heaps), in \ which case SLSP is lowered to provide more heap space, \ assuming there is enough free memory to do so .QQ2 SKIP 6 \ The three 16-bit seeds for the current system, i.e. \ the one we are currently in .QQ3 SKIP 1 \ The selected system's economy (0-7) \ \ * 0 = Rich Industrial \ * 1 = Average Industrial \ * 2 = Poor Industrial \ * 3 = Mainly Industrial \ * 4 = Mainly Agricultural \ * 5 = Rich Agricultural \ * 6 = Average Agricultural \ * 7 = Poor Agricultural .QQ4 SKIP 1 \ The selected system's government (0-7) .QQ5 SKIP 1 \ The selected system's tech level (0-14) .QQ6 SKIP 2 \ The selected system's population in billions * 10 \ (1-71), so the maximum population is 7.1 billion .QQ7 SKIP 2 \ The selected system's productivity in M CR (96-62480) .QQ8 SKIP 2 \ The distance from the current system to the selected \ system in light years * 10, stored as a 16-bit number \ \ The distance will be 0 if the selected system is the \ current system \ \ The galaxy chart is 102.4 light years wide and 51.2 \ light years tall (see the intra-system distance \ calculations in routine TT111 for details), which \ equates to 1024 x 512 in terms of QQ8 .QQ9 SKIP 1 \ The galactic x-coordinate of the crosshairs in the \ galaxy chart (and, most of the time, the selected \ system's galactic x-coordinate) .QQ10 SKIP 1 \ The galactic y-coordinate of the crosshairs in the \ galaxy chart (and, most of the time, the selected \ system's galactic y-coordinate) .NOSTM SKIP 1 \ The number of stardust particles shown on screen, \ which is 18 (#NOST) for normal space, and 3 for \ witchspace .BUF SKIP 100 \ The line buffer used by DASC to print justified text \ --- Mod: Code moved for two-player Elite: -----------> .safehouse SKIP 6 \ Backup storage for the seeds for the selected system \ \ The seeds for the current system get stored here as \ soon as a hyperspace is initiated, so we can fetch \ them in the hyp1 routine. This fixes a bug in an \ earlier version where you could hyperspace while \ docking and magically appear in your destination \ station .QQ22 SKIP 2 \ The two hyperspace countdown counters \ \ Before a hyperspace jump, both QQ22 and QQ22+1 are \ set to 15 \ \ QQ22 is an internal counter that counts down by 1 \ each time TT102 is called, which happens every \ iteration of the main game loop. When it reaches \ zero, the on-screen counter in QQ22+1 gets \ decremented, and QQ22 gets set to 5 and the countdown \ continues (so the first tick of the hyperspace counter \ takes 15 iterations to happen, but subsequent ticks \ take 5 iterations each) \ \ QQ22+1 contains the number that's shown on-screen \ during the countdown. It counts down from 15 to 1, and \ when it hits 0, the hyperspace engines kick in .SUNX SKIP 2 \ The 16-bit x-coordinate of the vertical centre axis \ of the sun (which might be off-screen) \ --- End of moved code -------------------------------> \ --- Mod: Code added for two-player Elite: -----------> .SUNXa SKIP 2 \ The 16-bit x-coordinate of the vertical centre axis \ of the sun for player 2 (which might be off-screen) .LSY2a SKIP 78 \ The ball line heap for storing y-coordinates for \ player 2 .LSPa SKIP 1 \ The ball line heap pointer for player 2 .LSX2a SKIP 78 \ The ball line heap for storing x-coordinates for \ player 2 \ --- End of added code -------------------------------> PRINT "UP workspace from ", ~UP, "to ", ~P%-1, "inclusive" \ ****************************************************************************** \ \ Name: WP \ Type: Workspace \ Address: &0D00 to &0E3B \ Category: Workspaces \ Summary: Variables \ \ ****************************************************************************** ORG &0D00 \ Set the assembly address to &0D00 .WP SKIP 0 \ The start of the WP workspace .LSX SKIP 0 \ LSX is an alias that points to the first byte of the \ sun line heap at LSO \ \ * &FF indicates the sun line heap is empty \ \ * Otherwise the LSO heap contains the line data for \ the sun .LSO \ --- Mod: Code removed for two-player Elite: ---------> \SKIP 192 \ The ship line heap for the space station (see NWSPS) \ \ and the sun line heap (see SUN) \ \ \ \ The spaces can be shared as our local bubble of \ \ universe can support either the sun or a space \ \ station, but not both \ --- And replaced by: --------------------------------> SKIP 96 \ The ship line heap for player 1's sun .LSXa SKIP 0 \ LSX is an alias that points to the first byte of the \ sun line heap at LSO \ \ * &FF indicates the sun line heap is empty \ \ * Otherwise the LSO heap contains the line data for \ the sun .LSOa SKIP 96 \ The ship line heap for player 1's sun \ --- End of replacement ------------------------------> .SX SKIP NOST + 1 \ This is where we store the x_hi coordinates for all \ the stardust particles .SXL SKIP NOST + 1 \ This is where we store the x_lo coordinates for all \ the stardust particles .SY SKIP NOST + 1 \ This is where we store the y_hi coordinates for all \ the stardust particles .SYL SKIP NOST + 1 \ This is where we store the y_lo coordinates for all \ the stardust particles .SZ SKIP NOST + 1 \ This is where we store the z_hi coordinates for all \ the stardust particles .SZL SKIP NOST + 1 \ This is where we store the z_lo coordinates for all \ the stardust particles .LASX SKIP 1 \ The x-coordinate of the tip of the laser line .LASY SKIP 1 \ The y-coordinate of the tip of the laser line .XX24 SKIP 1 \ This byte appears to be unused .ALTIT SKIP 1 \ Our altitude above the surface of the planet or sun \ \ * 255 = we are a long way above the surface \ \ * 1-254 = our altitude as the square root of: \ \ x_hi^2 + y_hi^2 + z_hi^2 - 6^2 \ \ where our ship is at the origin, the centre of the \ planet/sun is at (x_hi, y_hi, z_hi), and the \ radius of the planet/sun is 6 \ \ * 0 = we have crashed into the surface .SWAP SKIP 1 \ Temporary storage, used to store a flag that records \ whether or not we had to swap a line's start and end \ coordinates around when clipping the line in routine \ LL145 (the flag is used in places like BLINE to swap \ them back) .XP SKIP 1 \ The x-coordinate of the current character as we \ construct the lines for the Star Wars scroll text .YP SKIP 1 \ The y-coordinate of the current character as we \ construct the lines for the Star Wars scroll text .YS SKIP 1 \ Temporary storage for saving the index into the TB \ tables in the SLIDE routine .BALI SKIP 1 \ The progress of the Star Wars scroll text as it \ scrolls, from 254 (off the bottom of the screen) to 2 \ (fully scrolled). Can also be thought of as a measure \ of how much of the scroll text has yet to appear \ on-screen .UPO SKIP 1 \ Used as an index into the UB tables when projecting \ the scroll text lines onto the Star Wars perspective \ view and then onto the screen \ --- Mod: Code added for two-player Elite: -----------> .startWP SKIP 0 \ A marker for the start of the player workspace .player2ECMA SKIP 1 \ Player 2's ECMA setting .player2ECMP SKIP 1 \ Player 2's ECMP setting .player2LAS2 SKIP 1 \ Player 2's LAS2 value .player2MSAR SKIP 1 \ Player 2's MSAR value .player2VIEW SKIP 1 \ Player 2's VIEW setting .player2LASCT SKIP 1 \ Player 2's LASCT value .player2GNTMP SKIP 1 \ Player 2's GNTMP value .player2DLY SKIP 1 \ Player 2's DLY value .player2Firing SKIP 1 \ Player 2's laser status when an NPC .player1Missile SKIP 1 \ The slot number of the missile that player 1 launched \ and which is currently in-flight (0 = no missile) .player2Missile SKIP 1 \ The slot number of the missile that player 2 launched \ and which is currently in-flight (0 = no missile) .newShipSlot SKIP 1 \ The slot number of the ship spawned by NWSHP .collisionCounter SKIP 1 \ A counter for ensuring collisions are only detected \ every few iterations around the main loop .gameOver SKIP 1 \ The state of the end of the game: \ \ * Bit 7 clear = we are not on the game over screen \ \ * Bit 7 set = we are on the game over screen .player1Exploding SKIP 1 \ The state of player 1 at the end of the game: \ \ * Bit 7 set = player 1 has just exploded \ \ * Bit 6 set = player 1 is currently exploding .player2Exploding SKIP 1 \ The state of player 2 at the end of the game: \ \ * Bit 7 set = player 2 has just exploded \ \ * Bit 6 set = player 2 is currently exploding .endZero SKIP 0 \ The end of the player variables that are zeroed in \ the ZERO routine .heightInCHKON SKIP 1 \ The height of the screen to check circles against in \ CHKON, which needs to be full height (#2*Y-1) for \ circles but half-height (#Y-1) for the sun .splitScreen SKIP 1 \ Controls the split-screen effect \ \ * Bit 7 set = draw into split-screen \ \ * Bit 7 clear = full screen .drawPlayerView SKIP 1 \ Determines which player's view to draw in the split \ screen \ \ * Bit 7 clear = draw player 1's view (top) \ \ * Bit 7 set = draw player 2's view (bottom) .player1INWK31 SKIP 3 \ Storage for the INWK+31 bytes for player 1 and any \ missiles when shown in player 2's view \ \ * Bits 0-2: %nnn = number of missiles \ \ * Bit 3: 0 = isn't currently being drawn on-screen \ 1 = is currently being drawn on-screen \ \ * Bit 4: 0 = don't show on scanner \ 1 = do show on scanner \ \ * Bit 5: 0 = ship is not exploding \ 1 = ship is exploding \ \ * Bit 6: 0 = ship is not firing lasers \ 1 = ship is firing lasers \ 0 = explosion has not been drawn \ 1 = explosion has been drawn \ \ * Bit 7: 0 = ship has not been killed \ 1 = ship has been killed .player1X SKIP 1 \ The index of the relevant value in player1INWK31 .player2ALPHA SKIP 1 \ Storage for player 2's ALPHA setting .player2ALP1 SKIP 1 \ Storage for player 2's ALP1 setting .player2ALP2 SKIP 2 \ Storage for player 2's ALP2 and APL2+1 settings .player2BETA SKIP 1 \ Storage for player 2's BETA setting .player2BET1 SKIP 1 \ Storage for player 2's BET1 setting .player2BET2 SKIP 2 \ Storage for player 2's BET2 and BET2+1 settings .player2DELTA SKIP 1 \ Storage for player 2's DELTA setting .player2DELT4 SKIP 2 \ Storage for player 2's DELT4(1 0) setting .player2JSTX SKIP 1 \ Player 2's current roll rate .player2JSTY SKIP 1 \ Player 2's current pitch rate .player2JSTK SKIP 1 \ Player 2's control choice \ \ * 0 = AI Pilot (default) \ \ * Non-zero, bit 7 clear = joystick \ \ * Non-zero, bit 7 set = Delta 14B .player2LASER SKIP 4 \ Player 2's fitted lasers .player2LAS SKIP 1 \ Player 2's LAS value .player2LASX SKIP 1 \ Player 2's LASX value .player2LASY SKIP 1 \ Player 2's LASY value .player2MSTG SKIP 1 \ Player 2's MSTG value .player2ENERGY SKIP 1 \ Player 2's ENERGY value .player2ENGY SKIP 1 \ Player 2's energy unit strength .player2FSH SKIP 1 \ Player 2's FSH value .player2ASH SKIP 1 \ Player 2's ASH value .player2ECM SKIP 1 \ Player 2's ECM value .player2NOMSL SKIP 1 \ Player 2's number of missiles .player2INWK32 SKIP 1 \ Player 2's AI flag \ \ * Bit 0: 0 = no E.C.M. \ 1 = has E.C.M. \ \ * Bits 1-6: %nnnnnn = aggression level (0 to 63) \ (see TACTICS part 7) \ \ * Bit 7: 0 = dumb \ 1 = AI enabled (apply TACTICS to ship) .player2MCH SKIP 1 \ Player 2's MCH value .player2messXC SKIP 1 \ Player 2's messXC value .player1Score SKIP 2 \ Player 1's score .player2Score SKIP 2 \ Player 2's score .player1Target SKIP 2 \ Player 1's target score .player2Target SKIP 2 \ Player 2's target score .player1COMX SKIP 1 \ The COMX value for player 1's compass on the left .player1COMY SKIP 1 \ The COMY value for player 1's compass on the left .player1COMC SKIP 1 \ The COMC value for player 1's compass on the left .player1Heap SKIP 2 \ The address of the ship line heap for player 1's ship \ in player 2's view (i.e. the ship in slot #12), which \ is &2000 less than the ship heap for player 2's ship \ in player 1's view .player1ShipType SKIP 1 \ Player 1's ship type .player2ShipType SKIP 1 \ Player 1's ship type .player1Missiles SKIP 1 \ Player 2's number of missiles, as configured in the \ configuration screen .player2Missiles SKIP 1 \ Player 2's number of missiles, as configured in the \ configuration screen .player1GameType SKIP 1 \ Game type: \ \ * 0 = fight to the death \ \ * Non-zero = first to n*10 hits (n = 1 to 100) .player2GameType SKIP 1 \ Game type: \ \ * 0 = fight to the death \ \ * Non-zero = first to n*10 hits (n = 1 to 100) .player2ViewSlot SKIP 1 \ The slot numberof the ship being processed + 10, to \ give the slot number of the ship in player 2's view .configHighlight SKIP 1 \ The number of the currently highlighted field on the \ configuration screen .nextHighlight SKIP 1 \ The number of the next highlighted field on the \ configuration screen .toggleCount SKIP 1 \ The number of toggles to apply to the option we are \ randomising on the configuration screen .coordinateIndex SKIP 1 \ The coordinate index in Multiply16x24 .optionNumber SKIP 1 \ The option number to print in the PrintOption routine .optionDebounce SKIP 1 \ Debounce for key presses on the title screen .tokenNumber SKIP 1 \ The token number to print in the PrintOption routine .titleScreen SKIP 1 \ A flag to denote we are in the title screen rather \ than the game itself .newVectors SKIP 18 \ The orientation vectors for a missile in player 2's \ view during the transformation calculation .newCoords SKIP 9 \ The coordinates for player 1's ship in player 2's view \ during the transformation calculation .storeData SKIP NI% \ Storage area for INWK, ship movement etc. .storeXX0 SKIP 2 \ Storage area for XX0(1 0) .storeINF SKIP 2 \ Storage area for INF(1 0) .storeTYPE SKIP 1 \ Storage area for TYPE .gameOverCounter SKIP 1 \ A counter for the game over animation .damageToApply SKIP 1 \ The damage to apply to a player's ship .digitCount SKIP 1 \ The number of digits in the score being printed .endWP SKIP 0 \ A marker for the end of the player workspace \ --- End of added code -------------------------------> PRINT "WP workspace from ", ~WP, "to ", ~P%-1, "inclusive" \ ****************************************************************************** \ \ Name: K% \ Type: Workspace \ Address: &8200 to &84E3 (&8500 to &87E3 in the Executive version) \ Category: Workspaces \ Summary: Ship data blocks \ Deep dive: Ship data blocks \ The local bubble of universe \ \ ------------------------------------------------------------------------------ \ \ Contains ship data for all the ships, planets, suns and space stations in our \ local bubble of universe. \ \ The blocks are pointed to by the lookup table at location UNIV. The first 720 \ bytes of the K% workspace hold ship data on up to 20 ships, with 37 (NI%) \ bytes per ship. \ \ ****************************************************************************** IF _SNG45 OR _SOURCE_DISC ORG &8200 \ Set the assembly address to &8200 ELIF _EXECUTIVE ORG &8500 \ Set the assembly address to &8500 ENDIF .K% SKIP NOSH * NI% \ Ship data blocks and ship line heap \ --- Mod: Code added for two-player Elite: -----------> SKIP NOSH * NI% \ Ship data blocks and ship line heap for player 2's \ frame of reference \ --- End of added code -------------------------------> PRINT "K% workspace from ", ~K%, "to ", ~P%-1, "inclusive" \ ****************************************************************************** \ \ Name: LP \ Type: Workspace \ Address: &8600 to &91FF (&8900 to &94FF in the Executive version) \ Category: Demo \ Summary: Variables used for displaying the scrolling text in the demo \ \ ****************************************************************************** IF _SNG45 OR _SOURCE_DISC ORG &8600 \ Set the assembly address to &8600 ELIF _EXECUTIVE ORG &8900 \ Set the assembly address to &8900 ENDIF .LP SKIP 0 \ The start of the LP workspace .X1TB SKIP 256 \ The x-coordinates of the start points for character \ lines in the scroll text .Y1TB SKIP 256 \ The y-coordinates of the start points for character \ lines in the scroll text .X2TB SKIP 256 \ The x-coordinates of the end points for character \ lines in the scroll text .Y2TB SKIP 256 \ The y-coordinates of the end points for character \ lines in the scroll text .X1UB SKIP 256 \ The x-coordinates of the start points for character \ lines in the scroll text (as projected screen \ coordinates) .Y1UB SKIP 256 \ The y-coordinates of the start points for character \ lines in the scroll text (as projected screen \ coordinates) .X2UB SKIP 256 \ The x-coordinates of the end points for character \ lines in the scroll text (as projected screen \ coordinates) .Y2UB SKIP 256 \ The y-coordinates of the end points for character \ lines in the scroll text (as projected screen \ coordinates) .X1VB SKIP 256 \ The x-coordinates of the start points for the previous \ set of character lines in the scroll text (as \ projected screen coordinates) so they can be erased .Y1VB SKIP 256 \ The y-coordinates of the start points for the previous \ set of character lines in the scroll text (as \ projected screen coordinates) so they can be erased .X2VB SKIP 256 \ The x-coordinates of the end points for the previous \ set of character lines in the scroll text (as \ projected screen coordinates) so they can be erased .Y2VB SKIP 256 \ The y-coordinates of the end points for the previous \ set of character lines in the scroll text (as \ projected screen coordinates) so they can be erased PRINT "LP workspace (6502sp parasite) from ", ~LP, "to ", ~P%-1, "inclusive" \ ****************************************************************************** \ \ ELITE A FILE \ \ Produces the binary file ELTA.bin that gets loaded by elite-bcfs.asm. \ \ ****************************************************************************** ORG CODE% \ Set the assembly address to CODE% LOAD_A% = LOAD% \ ****************************************************************************** \ \ Name: Parasite variables \ Type: Workspace \ Address: &1000 to &100B (&1000 to &100D in the Executive version) \ Category: Workspaces \ Summary: Various variables used by the parasite \ \ ****************************************************************************** .MOS SKIP 1 \ This variable appears to be unused .COMC SKIP 1 \ The colour of the dot on the compass \ \ * #WHITE2 = the object in the compass is in front of \ us, so the dot is white \ \ * #GREEN2 = the object in the compass is behind us, \ so the dot is green .DNOIZ SKIP 1 \ Sound on/off configuration setting \ \ * 0 = sound is on (default) \ \ * Non-zero = sound is off \ \ Toggled by pressing "S" when paused, see the DK4 \ routine for details .DAMP SKIP 1 \ Keyboard damping configuration setting \ \ * 0 = damping is enabled (default) \ \ * &FF = damping is disabled \ \ Toggled by pressing CAPS LOCK when paused, see the \ DKS3 routine for details .DJD SKIP 1 \ Keyboard auto-recentre configuration setting \ \ * 0 = auto-recentre is enabled (default) \ \ * &FF = auto-recentre is disabled \ \ Toggled by pressing "A" when paused, see the DKS3 \ routine for details .PATG SKIP 1 \ Configuration setting to show the author names on the \ start-up screen and enable manual hyperspace mis-jumps \ \ * 0 = no author names or manual mis-jumps (default) \ \ * &FF = show author names and allow manual mis-jumps \ \ Toggled by pressing "X" when paused, see the DKS3 \ routine for details \ \ This needs to be turned on for manual mis-jumps to be \ possible. To do a manual mis-jump, first toggle the \ author display by pausing the game and pressing "X", \ and during the next hyperspace, hold down CTRL to \ force a mis-jump. See routine ee5 for the "AND PATG" \ instruction that implements this logic .FLH SKIP 1 \ Flashing console bars configuration setting \ \ * 0 = static bars (default) \ \ * &FF = flashing bars \ \ Toggled by pressing "F" when paused, see the DKS3 \ routine for details .JSTGY SKIP 1 \ Reverse joystick Y-channel configuration setting \ \ * 0 = standard Y-channel (default) \ \ * &FF = reversed Y-channel \ \ Toggled by pressing "Y" when paused, see the DKS3 \ routine for details .JSTE SKIP 1 \ Reverse both joystick channels configuration setting \ \ * 0 = standard channels (default) \ \ * &FF = reversed channels \ \ Toggled by pressing "J" when paused, see the DKS3 \ routine for details \ --- Mod: Code removed for two-player Elite: ---------> \.JSTK \ \SKIP 1 \ Keyboard or joystick configuration setting \ \ \ \ * 0 = keyboard (default) \ \ \ \ * &FF = joystick \ \ \ \ Toggled by pressing "K" when paused, see the DKS3 \ \ routine for details \ --- And replaced by: --------------------------------> .JSTK SKIP 1 \ Player 1's control choice \ \ * 0 = keyboard (default) \ \ * Non-zero, bit 7 clear = joystick \ \ * Non-zero, bit 7 set = Delta 14B \ --- End of replacement ------------------------------> IF _EXECUTIVE .JUMP SKIP 1 \ Infinite jump range configuration setting \ \ * 0 = maximum jump range is the standard 7 light \ years (default) \ \ * Non-zero = jump range is infinite \ \ Toggled by pressing "@" when paused, see the DK4 \ routine for details \ \ Not only is the jump range infinite, but you don't use \ any fuel when jumping, either .SPEAK SKIP 1 \ Speech configuration setting \ \ * 0 = speech is disabled (default) \ \ * Non-zero = speech is enabled \ \ Toggled by pressing ":" when paused, see the DK4 \ routine for details \ \ For speech to work, the BBC must be fitted with a \ Watford Electronics Beeb Speech Synthesiser ENDIF .BSTK SKIP 1 \ Bitstik configuration setting \ \ * 0 = keyboard or joystick (default) \ \ * &FF = Bitstik \ \ Toggled by pressing "B" when paused, see the DKS3 \ routine for details .CATF SKIP 1 \ This byte appears to be unused (the CATF variable in \ the I/O processor code is used to store the CATF flag, \ not this one) .ZIP SKIP 0 \ This label is not used but is in the original source PRINT "Parasite variables workspace (6502sp parasite) from ", ~MOS, "to ", ~P%-1, "inclusive" \ ****************************************************************************** \ \ Name: S1% \ Type: Variable \ Category: Save and load \ Summary: The drive and directory number used when saving or loading a \ commander file \ Deep dive: Commander save files \ \ ------------------------------------------------------------------------------ \ \ The drive part of this string (the "0") is updated with the chosen drive in \ the GTNMEW routine, but the directory part (the "E") is fixed. The variable \ is followed directly by the commander file at NA%, which starts with the \ commander name, so the full string at S1% is in the format ":0.E.JAMESON", \ which gives the full filename of the commander file. \ \ ****************************************************************************** .S1% EQUS ":0.E." \ ****************************************************************************** \ \ Name: NA% \ Type: Variable \ Category: Save and load \ Summary: The data block for the last saved commander \ Deep dive: Commander save files \ The competition code \ \ ------------------------------------------------------------------------------ \ \ Contains the last saved commander data, with the name at NA% and the data at \ NA%+8 onwards. The size of the data block is given in NT% (which also includes \ the two checksum bytes that follow this block). This block is initially set up \ with the default commander, which can be maxed out for testing purposes by \ setting Q% to TRUE. \ \ The commander's name is stored at NA%, and can be up to 7 characters long \ (the DFS filename limit). It is terminated with a carriage return character, \ ASCII 13. \ \ The offset of each byte within a saved commander file is also shown as #0, #1 \ and so on, so the kill tally, for example, is in bytes #71 and #72 of the \ saved file. The related variable name from the current commander block is \ also shown. \ \ ****************************************************************************** .NA% IF _SNG45 OR _SOURCE_DISC EQUS "JAMESON" \ The current commander name, which defaults to JAMESON EQUB 13 \ \ The commander name can be up to seven characters (the \ DFS limit for filenames), and is terminated by a \ carriage return ELIF _EXECUTIVE EQUS "FIREBUD" \ The current commander name, which defaults to FIREBUD EQUB 13 \ in the Executive version (this version comes with a \ maxed-out commander by default, so it gets a different \ name, which is presumably a seven-character riff on \ "Firebird", the publishers of the non-Acorn versions \ of Elite) \ \ The commander name can be up to seven characters (the \ DFS limit for filenames), and is terminated by a \ carriage return ENDIF \ NA%+8 is the start of the commander data block \ \ This block contains the last saved commander data \ block. As the game is played it uses an identical \ block at location TP to store the current commander \ state, and that block is copied here when the game is \ saved. Conversely, when the game starts up, the block \ here is copied to TP, which restores the last saved \ commander when we die \ \ The initial state of this block defines the default \ commander. Q% can be set to TRUE to give the default \ commander lots of credits and equipment EQUB 0 \ TP = Mission status, #0 EQUB 20 \ QQ0 = Current system X-coordinate (Lave), #1 EQUB 173 \ QQ1 = Current system Y-coordinate (Lave), #2 EQUW &5A4A \ QQ21 = Seed s0 for system 0, galaxy 0 (Tibedied), #3-4 EQUW &0248 \ QQ21 = Seed s1 for system 0, galaxy 0 (Tibedied), #5-6 EQUW &B753 \ QQ21 = Seed s2 for system 0, galaxy 0 (Tibedied), #7-8 IF Q% EQUD &00CA9A3B \ CASH = Amount of cash (100,000,000 Cr), #9-12 ELSE EQUD &E8030000 \ CASH = Amount of cash (100 Cr), #9-12 ENDIF EQUB 70 \ QQ14 = Fuel level, #13 IF _SNG45 OR _SOURCE_DISC EQUB 0 \ COK = Competition flags, #14 ELIF _EXECUTIVE EQUB %10000000 \ COK = Competition flags, #14 ENDIF EQUB 0 \ GCNT = Galaxy number, 0-7, #15 \ --- Mod: Code removed for two-player Elite: ---------> \EQUB POW+(128 AND Q%) \ LASER = Front laser, #16 \ \EQUB (POW+128) AND Q% \ LASER+1 = Rear laser, #17 \ --- And replaced by: --------------------------------> EQUB POW \ LASER = Front laser, #16 EQUB POW \ LASER+1 = Rear laser, #17 \ --- End of replacement ------------------------------> EQUB 0 \ LASER+2 = Left laser, #18 EQUB 0 \ LASER+3 = Right laser, #19 EQUW 0 \ These bytes appear to be unused (they were originally \ used for up/down lasers, but they were dropped), \ #20-21 EQUB 22 + (15 AND Q%) \ CRGO = Cargo capacity, #22 EQUB 0 \ QQ20+0 = Amount of food in cargo hold, #23 EQUB 0 \ QQ20+1 = Amount of textiles in cargo hold, #24 EQUB 0 \ QQ20+2 = Amount of radioactives in cargo hold, #25 EQUB 0 \ QQ20+3 = Amount of slaves in cargo hold, #26 EQUB 0 \ QQ20+4 = Amount of liquor/Wines in cargo hold, #27 EQUB 0 \ QQ20+5 = Amount of luxuries in cargo hold, #28 EQUB 0 \ QQ20+6 = Amount of narcotics in cargo hold, #29 EQUB 0 \ QQ20+7 = Amount of computers in cargo hold, #30 EQUB 0 \ QQ20+8 = Amount of machinery in cargo hold, #31 EQUB 0 \ QQ20+9 = Amount of alloys in cargo hold, #32 EQUB 0 \ QQ20+10 = Amount of firearms in cargo hold, #33 EQUB 0 \ QQ20+11 = Amount of furs in cargo hold, #34 EQUB 0 \ QQ20+12 = Amount of minerals in cargo hold, #35 EQUB 0 \ QQ20+13 = Amount of gold in cargo hold, #36 EQUB 0 \ QQ20+14 = Amount of platinum in cargo hold, #37 EQUB 0 \ QQ20+15 = Amount of gem-stones in cargo hold, #38 EQUB 0 \ QQ20+16 = Amount of alien items in cargo hold, #39 \ --- Mod: Code removed for two-player Elite: ---------> \EQUB Q% \ ECM = E.C.M. system, #40 \ --- And replaced by: --------------------------------> EQUB &FF \ ECM = E.C.M. system, #40 \ --- End of replacement ------------------------------> EQUB Q% \ BST = Fuel scoops ("barrel status"), #41 EQUB Q% AND 127 \ BOMB = Energy bomb, #42 EQUB Q% AND 1 \ ENGY = Energy/shield level, #43 EQUB Q% \ DKCMP = Docking computer, #44 EQUB Q% \ GHYP = Galactic hyperdrive, #45 EQUB Q% \ ESCP = Escape pod, #46 EQUD 0 \ These four bytes appear to be unused, #47-50 EQUB 3 + (Q% AND 1) \ NOMSL = Number of missiles, #51 EQUB 0 \ FIST = Legal status ("fugitive/innocent status"), #52 EQUB 16 \ AVL+0 = Market availability of food, #53 EQUB 15 \ AVL+1 = Market availability of textiles, #54 EQUB 17 \ AVL+2 = Market availability of radioactives, #55 EQUB 0 \ AVL+3 = Market availability of slaves, #56 EQUB 3 \ AVL+4 = Market availability of liquor/Wines, #57 EQUB 28 \ AVL+5 = Market availability of luxuries, #58 EQUB 14 \ AVL+6 = Market availability of narcotics, #59 EQUB 0 \ AVL+7 = Market availability of computers, #60 EQUB 0 \ AVL+8 = Market availability of machinery, #61 EQUB 10 \ AVL+9 = Market availability of alloys, #62 EQUB 0 \ AVL+10 = Market availability of firearms, #63 EQUB 17 \ AVL+11 = Market availability of furs, #64 EQUB 58 \ AVL+12 = Market availability of minerals, #65 EQUB 7 \ AVL+13 = Market availability of gold, #66 EQUB 9 \ AVL+14 = Market availability of platinum, #67 EQUB 8 \ AVL+15 = Market availability of gem-stones, #68 EQUB 0 \ AVL+16 = Market availability of alien items, #69 EQUB 0 \ QQ26 = Random byte that changes for each visit to a \ system, for randomising market prices, #70 EQUW 0 \ TALLY = Number of kills, #71-72 EQUB 128 \ SVC = Save count, #73 \ ****************************************************************************** \ \ Name: CHK2 \ Type: Variable \ Category: Save and load \ Summary: Second checksum byte for the saved commander data file \ Deep dive: Commander save files \ The competition code \ \ ------------------------------------------------------------------------------ \ \ Second commander checksum byte. If the default commander is changed, a new \ checksum will be calculated and inserted by the elite-checksum.py script. \ \ The offset of this byte within a saved commander file is also shown (it's at \ byte #74). \ \ ****************************************************************************** .CHK2 IF _SNG45 OR _SOURCE_DISC EQUB &03 EOR &A9 \ The checksum value for the default commander, EOR'd \ with &A9 to make it harder to tamper with the checksum \ byte, #74 ELIF _EXECUTIVE EQUB &3F EOR &A9 \ The checksum value for the maxed-out default \ commander, EOR'd with &A9 to make it harder to tamper \ with the checksum byte, #74 ENDIF \ ****************************************************************************** \ \ Name: CHK \ Type: Variable \ Category: Save and load \ Summary: First checksum byte for the saved commander data file \ Deep dive: Commander save files \ The competition code \ \ ------------------------------------------------------------------------------ \ \ Commander checksum byte. If the default commander is changed, a new checksum \ will be calculated and inserted by the elite-checksum.py script. \ \ The offset of this byte within a saved commander file is also shown (it's at \ byte #75). \ \ ****************************************************************************** .CHK IF _SNG45 OR _SOURCE_DISC EQUB &03 \ The checksum value for the default commander, #75 ELIF _EXECUTIVE EQUB &3F \ The checksum value for the maxed-out default \ commander, #75 ENDIF \ ****************************************************************************** \ \ Name: S% \ Type: Subroutine \ Category: Loader \ Summary: Checksum, decrypt and unscramble the main game code, and start the \ game \ \ ------------------------------------------------------------------------------ \ \ This routine reverses the three copy protection mechanisms that the Big Code \ File puts in place: the checksum in Checksum, the encryption in DEEOR, and \ the code reversal in do65c02. In the BeebAsm version here, these three \ protections are applied by elite-checksum.py, and the original 6502 assembly \ language versions of the three encryption routines can be found in the \ elite-checksum.asm file. \ \ It also adds in a bit of Tube-specific copy protection, by transmitting the \ do65c02 routine over the Tube before it is run. It's very crafty stuff! \ \ ****************************************************************************** EQUD 0 \ These bytes appear to be unused RTS \ The checksum byte goes here, at S%-1. In the original \ source this byte is set by the first call to ZP in the \ Big Code File, though in the BeebAsm version this is \ populated by elite-checksum.py .S% CLD \ Clear the D flag to make sure we are in binary mode SEC \ Set the C flag LDA #LO(G%) \ Set (1 0) = SC(1 0) = G% STA 0 STA SC LDA #HI(G%) STA 1 STA SC+1 LDA #LO(F%-1) \ Set (3 2) = F% - 1 STA 2 LDA #HI(F%-1) STA 3 LDX #LO(prtblock) \ Set (Y X) to point to the prtblock parameter block LDY #HI(prtblock) LDA #249 \ Send an OSWORD 249 command to the I/O processor, which JSR OSWORD \ copies the code of the do65c02 routine from the I/O \ processor to prtblock+2 LDX #SC \ Set X = SC, and because SC is in zero page, this means \ that X contains the whole value of SC, so jumping to \ (X), for example, would jump to the address in SC(1 0) EQUB &AD \ This is the opcode for an LDA absolute instruction, so \ it converts the two OSWORD size bytes at prtblock into \ a harmless LDA &2702 instruction .prtblock EQUB 2 \ The number of bytes to transmit with this command EQUB &27 \ The number of bytes to receive with this command JMP (SC,X) \ This block, between here and G%, is overwritten by the PHP \ code of the do65c02 routine from the I/O processor, so PHY \ this code is never run and is presumably just here to LDA #&34 \ throw the crackers off the scent - it just needs to be PHA \ at least as long as the do65c02 routine, which ends LDX #0 \ with a jump to G% below to start the game RTS BRK EQUS "ELITE - By Ian Bell & David Braben" EQUB 10 EQUB 13 BRK LDA SC \ This section of unused code is particularly useful to ADC 2 \ hackers, as it contains the value of F% (in the CMP F%-1 \ CMP F%-1 instruction), which we need to undo the BNE P%-2 \ encryption. We also need the value of G%, which is EQUD &7547534 \ easy enough to work out as it's just after this block. EQUD &452365 \ See the elite-decrypt.py script for more details EQUB &8D .G% JSR DEEOR \ Decrypt the main game code between &1300 and &9FFF JSR COLD \ Copy the recursive tokens and ship blueprints to their \ correct locations JSR Checksum \ Checksum the code from &1000 to &9FFF and check it \ against S%-1 JMP BEGIN \ Jump to BEGIN to start the game NOP \ This instruction is not used \ ****************************************************************************** \ \ Name: DEEOR \ Type: Subroutine \ Category: Copy protection \ Summary: Decrypt bytes between &1300 and &9FFF by EOR'ing them with their \ page offset \ \ ------------------------------------------------------------------------------ \ \ In the original source, the bytes between &1300 and &9FFF are EOR'd by the \ first call to SC in the Big Code File, though in the BeebAsm version they are \ EOR'd by elite-checksum.py. \ \ The original 6502 assembly language version of the SC routine can be found in \ the elite-checksum.asm file. \ \ ****************************************************************************** .DEEOR \ --- Mod: Code removed for two-player Elite: ---------> \LDY #0 \ Set (X Y) = SC(1 0) = &1300 \STY SC \LDX #&13 \ \.DEEL \ \STX SC+1 \ Set SC+1 = X, so now SC(1 0) = (X 0) \ \TYA \ Set A = contents of (SC(1 0) + Y) EOR Y EOR &75 \EOR (SC),Y \ = contents of ((X 0) + Y) EOR Y EOR &75 \EOR #&75 \ = contents of (X Y) EOR Y EOR &75 \ \IF _REMOVE_CHECKSUMS \ \NOP \ If we have disabled checksums, then don't update (X Y) \NOP \ with the result, and just move on to the next byte \ \ELSE \ \STA (SC),Y \ Store the EOR'd value in SC(1 0) + Y, i.e. (X Y) \ \ENDIF \ \DEY \ Decrement the loop counter to process the next byte \ \BNE DEEL \ Loop back until we have done the whole page \ \INX \ Increment the page counter to point to the next page \ \CPX #&A0 \ Loop back to do the next page until X = &A0, when \BNE DEEL \ (X Y) = &A000 \ --- End of removed code -----------------------------> JMP BRKBK \ Jump to BRKBK to set the standard BRKV handler for the \ game and return from the subroutine using a tail call \ ****************************************************************************** \ \ Name: DOENTRY \ Type: Subroutine \ Category: Flight \ Summary: Dock at the space station, show the ship hangar and work out any \ mission progression \ Deep dive: The Constrictor mission \ The Thargoid Plans mission \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.DOENTRY \ \JSR RES2 \ Reset a number of flight variables and workspaces \ \JSR LAUN \ Show the space station docking tunnel \ \STZ DELTA \ Reduce the speed to 0 \ \STZ QQ22+1 \ Reset the on-screen hyperspace counter \ \STZ GNTMP \ Cool down the lasers completely \ \LDA #&FF \ Recharge the forward and aft shields \STA FSH \STA ASH \ \STA ENERGY \ Recharge the energy banks \ \JSR HALL \ Show the ship hangar \ \LDY #44 \ Wait for 44/50 of a second (0.88 seconds) \JSR DELAY \ \LDA TP \ Fetch bits 0 and 1 of TP, and if they are non-zero \AND #%00000011 \ (i.e. mission 1 is either in progress or has been \BNE EN1 \ completed), skip to EN1 \ \LDA TALLY+1 \ If the high byte of TALLY is zero (so we have a combat \BEQ EN4 \ rank below Competent, or we are Competent but have not \ \ yet earned a grand total of at least 256 kill points), \ \ jump to EN4 as we are not yet good enough to qualify \ \ for a mission \ \LDA GCNT \ Fetch the galaxy number into A, and if any of bits 1-7 \LSR A \ are set (i.e. A > 1), jump to EN4 as mission 1 can \BNE EN4 \ only be triggered in the first two galaxies \ \JMP BRIEF \ If we get here then mission 1 hasn't started, we have \ \ reached a combat rank of at least Competent plus 128 \ \ kill points, and we are in galaxy 0 or 1 (shown \ \ in-game as galaxy 1 or 2), so it's time to start \ \ mission 1 by calling BRIEF \ \.EN1 \ \ \ If we get here then mission 1 is either in progress or \ \ has been completed \ \CMP #%00000011 \ If bits 0 and 1 are not both set, then jump to EN2 \BNE EN2 \ \JMP DEBRIEF \ Bits 0 and 1 are both set, so mission 1 is both in \ \ progress and has been completed, which means we have \ \ only just completed it, so jump to DEBRIEF to end the \ \ mission get our reward \ \.EN2 \ \ \ Mission 1 has been completed, so now to check for \ \ mission 2 \ \LDA GCNT \ Fetch the galaxy number into A \ \CMP #2 \ If this is not galaxy 2 (shown in-game as galaxy 3), \BNE EN4 \ jump to EN4 as we can only start mission 2 in the \ \ third galaxy \ \LDA TP \ Extract bits 0-3 of TP into A \AND #%00001111 \ \CMP #%00000010 \ If mission 1 is complete and no longer in progress, \BNE EN3 \ and mission 2 is not yet started, then bits 0-3 of TP \ \ will be %0010, so this jumps to EN3 if this is not the \ \ case \ \LDA TALLY+1 \ If the high byte of TALLY is < 5 (so we have a combat \CMP #5 \ rank that is less than 3/8 of the way from Dangerous \BCC EN4 \ to Deadly), jump to EN4 as our rank isn't high enough \ \ for mission 2 \ \JMP BRIEF2 \ If we get here, mission 1 is complete and no longer in \ \ progress, mission 2 hasn't started, we have reached a \ \ combat rank of 3/8 of the way from Dangerous to \ \ Deadly, and we are in galaxy 2 (shown in-game as \ \ galaxy 3), so it's time to start mission 2 by calling \ \ BRIEF2 \ \.EN3 \ \CMP #%00000110 \ If mission 1 is complete and no longer in progress, \BNE EN5 \ and mission 2 has started but we have not yet been \ \ briefed and picked up the plans, then bits 0-3 of TP \ \ will be %0110, so this jumps to EN5 if this is not the \ \ case \ \LDA QQ0 \ Set A = the current system's galactic x-coordinate \ \CMP #215 \ If A <> 215 then jump to EN4 \BNE EN4 \ \LDA QQ1 \ Set A = the current system's galactic y-coordinate \ \CMP #84 \ If A <> 84 then jump to EN4 \BNE EN4 \ \JMP BRIEF3 \ If we get here, mission 1 is complete and no longer in \ \ progress, mission 2 has started but we have not yet \ \ picked up the plans, and we have just arrived at \ \ Ceerdi at galactic coordinates (215, 84), so we jump \ \ to BRIEF3 to get a mission brief and pick up the plans \ \ that we need to carry to Birera \ \.EN5 \ \CMP #%00001010 \ If mission 1 is complete and no longer in progress, \BNE EN4 \ and mission 2 has started and we have picked up the \ \ plans, then bits 0-3 of TP will be %1010, so this \ \ jumps to EN5 if this is not the case \ \LDA QQ0 \ Set A = the current system's galactic x-coordinate \ \CMP #63 \ If A <> 63 then jump to EN4 \BNE EN4 \ \LDA QQ1 \ Set A = the current system's galactic y-coordinate \ \CMP #72 \ If A <> 72 then jump to EN4 \BNE EN4 \ \JMP DEBRIEF2 \ If we get here, mission 1 is complete and no longer in \ \ progress, mission 2 has started and we have picked up \ \ the plans, and we have just arrived at Birera at \ \ galactic coordinates (63, 72), so we jump to DEBRIEF2 \ \ to end the mission and get our reward \ \.EN4 \ \JMP BAY \ If we get here them we didn't start or any missions, \ \ so jump to BAY to go to the docking bay (i.e. show the \ \ Status Mode screen) \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: BRKBK \ Type: Subroutine \ Category: Save and load \ Summary: Set the standard BRKV handler for the game \ \ ------------------------------------------------------------------------------ \ \ BRKV is set to this routine by the BRKBK routine, which is called by the \ decryption routine at DEEOR just before the game is run for the first time, \ and at the end of the SVE routine after the disc access menu has been \ processed (so this resets BRKV to the standard BRKV handler for the game). \ \ ****************************************************************************** .BRKBK LDA #LO(BRBR) \ Set BRKV to point to the BRBR routine, disabling SEI \ interrupts while we make the change and re-enabling STA BRKV \ them once we are done LDA #HI(BRBR) STA BRKV+1 CLI RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: Main flight loop (Part 1 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: Seed the random number generator \ Deep dive: Program flow of the main game loop \ Generating random numbers \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Seed the random number generator \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ M% The entry point for the main flight loop \ \ ****************************************************************************** .M% LDA K% \ We want to seed the random number generator with a \ pretty random number, so fetch the contents of K%, \ which is the x_lo coordinate of the planet. This value \ will be fairly unpredictable, so it's a pretty good \ candidate STA RAND \ Store the seed in the first byte of the four-byte \ random number seed that's stored in RAND \ ****************************************************************************** \ \ Name: Main flight loop (Part 2 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: Calculate the alpha and beta angles from the current pitch and \ roll of our ship \ Deep dive: Program flow of the main game loop \ Pitching and rolling \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Calculate the alpha and beta angles from the current pitch and roll \ \ Here we take the current rate of pitch and roll, as set by the joystick or \ keyboard, and convert them into alpha and beta angles that we can use in the \ matrix functions to rotate space around our ship. The alpha angle covers \ roll, while the beta angle covers pitch (there is no yaw in this version of \ Elite). The angles are in radians, which allows us to use the small angle \ approximation when moving objects in the sky (see the MVEIT routine for more \ on this). Also, the signs of the two angles are stored separately, in both \ the sign and the flipped sign, as this makes calculations easier. \ \ ****************************************************************************** LDX JSTX \ Set X to the current rate of roll in JSTX JSR cntr \ Apply keyboard damping twice (if enabled) so the roll JSR cntr \ rate in X creeps towards the centre by 2 \ The roll rate in JSTX increases if we press ">" (and \ the RL indicator on the dashboard goes to the right) \ \ This rolls our ship to the right (clockwise), but we \ actually implement this by rolling everything else \ to the left (anti-clockwise), so a positive roll rate \ in JSTX translates to a negative roll angle alpha TXA \ Set A and Y to the roll rate but with the sign bit EOR #%10000000 \ flipped (i.e. set them to the sign we want for alpha) TAY AND #%10000000 \ Extract the flipped sign of the roll rate and store STA ALP2 \ in ALP2 (so ALP2 contains the sign of the roll angle \ alpha) STX JSTX \ Update JSTX with the damped value that's still in X EOR #%10000000 \ Extract the correct sign of the roll rate and store STA ALP2+1 \ in ALP2+1 (so ALP2+1 contains the flipped sign of the \ roll angle alpha) TYA \ Set A to the roll rate but with the sign bit flipped BPL P%+7 \ If the value of A is positive, skip the following \ three instructions EOR #%11111111 \ A is negative, so change the sign of A using two's CLC \ complement so that A is now positive and contains ADC #1 \ the absolute value of the roll rate, i.e. |JSTX| LSR A \ Divide the (positive) roll rate in A by 4 LSR A CMP #8 \ If A >= 8, skip the following instruction BCS P%+3 LSR A \ A < 8, so halve A again STA ALP1 \ Store A in ALP1, so we now have: \ \ ALP1 = |JSTX| / 8 if |JSTX| < 32 \ \ ALP1 = |JSTX| / 4 if |JSTX| >= 32 \ \ This means that at lower roll rates, the roll angle is \ reduced closer to zero than at higher roll rates, \ which gives us finer control over the ship's roll at \ lower roll rates \ \ Because JSTX is in the range -127 to +127, ALP1 is \ in the range 0 to 31 ORA ALP2 \ Store A in ALPHA, but with the sign set to ALP2 (so STA ALPHA \ ALPHA has a different sign to the actual roll rate) LDX JSTY \ Set X to the current rate of pitch in JSTY JSR cntr \ Apply keyboard damping so the pitch rate in X creeps \ towards the centre by 1 TXA \ Set A and Y to the pitch rate but with the sign bit EOR #%10000000 \ flipped TAY AND #%10000000 \ Extract the flipped sign of the pitch rate into A STX JSTY \ Update JSTY with the damped value that's still in X STA BET2+1 \ Store the flipped sign of the pitch rate in BET2+1 EOR #%10000000 \ Extract the correct sign of the pitch rate and store STA BET2 \ it in BET2 TYA \ Set A to the pitch rate but with the sign bit flipped BPL P%+4 \ If the value of A is positive, skip the following \ instruction EOR #%11111111 \ A is negative, so flip the bits ADC #4 \ Add 4 to the (positive) pitch rate, so the maximum \ value is now up to 131 (rather than 127) LSR A \ Divide the (positive) pitch rate in A by 16 LSR A LSR A LSR A CMP #3 \ If A >= 3, skip the following instruction BCS P%+3 LSR A \ A < 3, so halve A again STA BET1 \ Store A in BET1, so we now have: \ \ BET1 = |JSTY| / 32 if |JSTY| < 48 \ \ BET1 = |JSTY| / 16 if |JSTY| >= 48 \ \ This means that at lower pitch rates, the pitch angle \ is reduced closer to zero than at higher pitch rates, \ which gives us finer control over the ship's pitch at \ lower pitch rates \ \ Because JSTY is in the range -131 to +131, BET1 is in \ the range 0 to 8 ORA BET2 \ Store A in BETA, but with the sign set to BET2 (so STA BETA \ BETA has the same sign as the actual pitch rate) \ --- Mod: Code added for two-player Elite: -----------> BIT K%+NI%*2+32 \ If player 2 is an NPC then skip the joystick reading BMI BS2 LDX player2JSTX \ Set X to the current rate of roll in JSTX JSR cntr \ Apply keyboard damping twice (if enabled) so the roll JSR cntr \ rate in X creeps towards the centre by 2 \ The roll rate in JSTX increases if we press ">" (and \ the RL indicator on the dashboard goes to the right) \ \ This rolls our ship to the right (clockwise), but we \ actually implement this by rolling everything else \ to the left (anti-clockwise), so a positive roll rate \ in JSTX translates to a negative roll angle alpha TXA \ Set A and Y to the roll rate but with the sign bit EOR #%10000000 \ flipped (i.e. set them to the sign we want for alpha) TAY AND #%10000000 \ Extract the flipped sign of the roll rate and store STA player2ALP2 \ in ALP2 (so ALP2 contains the sign of the roll angle \ alpha) STX player2JSTX \ Update JSTX with the damped value that's still in X EOR #%10000000 \ Extract the correct sign of the roll rate and store STA player2ALP2+1 \ in ALP2+1 (so ALP2+1 contains the flipped sign of the \ roll angle alpha) TYA \ Set A to the roll rate but with the sign bit flipped BPL P%+7 \ If the value of A is positive, skip the following \ three instructions EOR #%11111111 \ A is negative, so change the sign of A using two's CLC \ complement so that A is now positive and contains ADC #1 \ the absolute value of the roll rate, i.e. |JSTX| LSR A \ Divide the (positive) roll rate in A by 4 LSR A CMP #8 \ If A >= 8, skip the following instruction BCS P%+3 LSR A \ A < 8, so halve A again STA player2ALP1 \ Store A in ALP1, so we now have: \ \ ALP1 = |JSTX| / 8 if |JSTX| < 32 \ \ ALP1 = |JSTX| / 4 if |JSTX| >= 32 \ \ This means that at lower roll rates, the roll angle is \ reduced closer to zero than at higher roll rates, \ which gives us finer control over the ship's roll at \ lower roll rates \ \ Because JSTX is in the range -127 to +127, ALP1 is \ in the range 0 to 31 ORA player2ALP2 \ Store A in ALPHA, but with the sign set to ALP2 (so STA player2ALPHA \ ALPHA has a different sign to the actual roll rate) LDX player2JSTY \ Set X to the current rate of pitch in JSTY JSR cntr \ Apply keyboard damping so the pitch rate in X creeps \ towards the centre by 1 TXA \ Set A and Y to the pitch rate but with the sign bit EOR #%10000000 \ flipped TAY AND #%10000000 \ Extract the flipped sign of the pitch rate into A STX player2JSTY \ Update JSTY with the damped value that's still in X STA player2BET2+1 \ Store the flipped sign of the pitch rate in BET2+1 EOR #%10000000 \ Extract the correct sign of the pitch rate and store STA player2BET2 \ it in BET2 TYA \ Set A to the pitch rate but with the sign bit flipped BPL P%+4 \ If the value of A is positive, skip the following \ instruction EOR #%11111111 \ A is negative, so flip the bits ADC #4 \ Add 4 to the (positive) pitch rate, so the maximum \ value is now up to 131 (rather than 127) LSR A \ Divide the (positive) pitch rate in A by 16 LSR A LSR A LSR A CMP #3 \ If A >= 3, skip the following instruction BCS P%+3 LSR A \ A < 3, so halve A again STA player2BET1 \ Store A in BET1, so we now have: \ \ BET1 = |JSTY| / 32 if |JSTY| < 48 \ \ BET1 = |JSTY| / 16 if |JSTY| >= 48 \ \ This means that at lower pitch rates, the pitch angle \ is reduced closer to zero than at higher pitch rates, \ which gives us finer control over the ship's pitch at \ lower pitch rates \ \ Because JSTY is in the range -131 to +131, BET1 is in \ the range 0 to 8 ORA player2BET2 \ Store A in BETA, but with the sign set to BET2 (so STA player2BETA \ BETA has the same sign as the actual pitch rate) \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \LDA BSTK \ If BSTK = 0 then the Bitstik is not configured, so \BEQ BS2 \ jump to BS2 to skip the following \ \LDA KTRAN+10 \ Fetch the Bitstik rotation value (high byte) from the \ \ key logger buffer \ \LSR A \ Divide A by 4 \LSR A \ \CMP #40 \ If A < 40, skip the following instruction \BCC P%+4 \ \LDA #40 \ Set A = 40, which ensures a maximum speed of 40 \ \STA DELTA \ Update our speed in DELTA \ \BNE MA4 \ If the speed we just set is non-zero, then jump to MA4 \ \ to skip the following, as we don't need to check the \ \ keyboard for speed keys, otherwise do check the \ \ keyboard (so Bitstik users can still use the keyboard \ \ for speed adjustments if they twist the stick to zero) \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Main flight loop (Part 3 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: Scan for flight keys and process the results \ Deep dive: Program flow of the main game loop \ The key logger \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Scan for flight keys and process the results \ \ Flight keys are logged in the key logger at location KY1 onwards, with a \ non-zero value in the relevant location indicating a key press. \ \ The key presses that are processed are as follows: \ \ * Space and "?" to speed up and slow down \ * "U", "T" and "M" to disarm, arm and fire missiles \ * TAB to fire an energy bomb \ * ESCAPE to launch an escape pod \ * "J" to initiate an in-system jump \ * "E" to deploy E.C.M. anti-missile countermeasures \ * "C" to use the docking computer \ * "A" to fire lasers \ \ ****************************************************************************** .BS2 LDA KY2 \ If Space is being pressed, keep going, otherwise jump BEQ MA17 \ down to MA17 to skip the following LDA DELTA \ The "go faster" key is being pressed, so first we CMP #40 \ fetch the current speed from DELTA into A, and if BCS MA17 \ A >= 40, we are already going at full pelt, so jump \ down to MA17 to skip the following INC DELTA \ We can go a bit faster, so increment the speed in \ location DELTA .MA17 LDA KY1 \ If "?" is being pressed, keep going, otherwise jump BEQ MA4 \ down to MA4 to skip the following DEC DELTA \ The "slow down" key is being pressed, so we decrement \ the current ship speed in DELTA BNE MA4 \ If the speed is still greater than zero, jump to MA4 INC DELTA \ Otherwise we just braked a little too hard, so bump \ the speed back up to the minimum value of 1 .MA4 \ --- Mod: Code added for two-player Elite: -----------> LDA KY20 \ If RETURN is being pressed, keep going, otherwise jump BEQ MA17a \ down to MA17 to skip the following LDA player2DELTA \ The "go faster" key is being pressed, so first we CMP #37 \ fetch the current speed from DELTA into A, and if BCS MA17a \ A >= 37, we are already going at full pelt, so jump \ down to MA17 to skip the following \ \ This is a slightly lower maximum than player 1 as \ otherwise the in-game speeds don't quite match (I am \ not sure why, but this seems to work) INC player2DELTA \ We can go a bit faster, so increment the speed in \ location DELTA .MA17a LDA KY21 \ If "]" is being pressed, keep going, otherwise jump BEQ MA4a \ down to MA4 to skip the following DEC player2DELTA \ The "slow down" key is being pressed, so we decrement \ the current ship speed in DELTA BNE MA4a \ If the speed is still greater than zero, jump to MA4 INC player2DELTA \ Otherwise we just braked a little too hard, so bump \ the speed back up to the minimum value of 1 .MA4a \ --- End of added code -------------------------------> LDA KY15 \ If "U" is being pressed and the number of missiles AND NOMSL \ in NOMSL is non-zero, keep going, otherwise jump down BEQ MA20 \ to MA20 to skip the following LDY #GREEN2 \ The "disarm missiles" key is being pressed, so call JSR ABORT \ ABORT to disarm the missile and update the missile \ indicators on the dashboard to green (Y = &EE) LDA #40 \ Call the NOISE routine with A = 40 to make a low, JSR NOISE \ long beep to indicate the missile is now disarmed LDA #0 \ Set MSAR to 0 to indicate that no missiles are STA MSAR \ currently armed .MA20 LDA MSTG \ If MSTG is positive (i.e. it does not have bit 7 set), BPL MA25 \ then it indicates we already have a missile locked on \ a target (in which case MSTG contains the ship number \ of the target), so jump to MA25 to skip targeting. Or \ to put it another way, if MSTG = &FF, which means \ there is no current target lock, keep going LDA KY14 \ If "T" is being pressed, keep going, otherwise jump BEQ MA25 \ down to MA25 to skip the following LDX NOMSL \ If the number of missiles in NOMSL is zero, jump down BEQ MA25 \ to MA25 to skip the following STA MSAR \ The "target missile" key is being pressed and we have \ at least one missile, so set MSAR = &FF to denote that \ our missile is currently armed (we know A has the \ value &FF, as we just loaded it from MSTG and checked \ that it was negative) \ --- Mod: Code added for two-player Elite: -----------> LDA #1 \ Set A to draw player 1's indicators \ --- End of added code -------------------------------> LDY #YELLOW2 \ Change the leftmost missile indicator to yellow JSR MSBAR \ on the missile bar (this call changes the leftmost \ indicator because we set X to the number of missiles \ in NOMSL above, and the indicators are numbered from \ right to left, so X is the number of the leftmost \ indicator) .MA25 LDA KY16 \ If "M" is being pressed, keep going, otherwise jump BEQ MA24 \ down to MA24 to skip the following \ --- Mod: Code removed for two-player Elite: ---------> \LDA MSTG \ If MSTG = &FF then there is no target lock, so jump to \BMI MA64 \ MA64 to skip the following (also skipping the checks \ \ for TAB, ESCAPE, "J" and "E") \ --- And replaced by: --------------------------------> LDA MSTG \ If MSTG = &FF then there is no target lock, so jump to BMI MA24 \ MA24 to move onto player 2's checks \ --- End of replacement ------------------------------> JSR FRMIS \ The "fire missile" key is being pressed and we have \ a missile lock, so call the FRMIS routine to fire \ the missile .MA24 \ --- Mod: Code added for two-player Elite: -----------> LDA KY19 \ If "_" is being pressed and the number of missiles AND player2NOMSL \ in NOMSL is non-zero, keep going, otherwise jump down BEQ MA20a \ to MA20 to skip the following LDY #GREEN2 \ The "disarm missiles" key is being pressed, so call JSR Player2ABORT \ ABORT to disarm the missile and update the missile \ indicators on the dashboard to green (Y = &EE) LDA #40 \ Call the NOISE routine with A = 40 to make a low, JSR NOISE \ long beep to indicate the missile is now disarmed LDA #0 \ Set MSAR to 0 to indicate that no missiles are STA player2MSAR \ currently armed .MA20a LDA player2MSTG \ If MSTG is positive (i.e. it does not have bit 7 set), BPL MA25a \ then it indicates we already have a missile locked on \ a target (in which case MSTG contains the ship number \ of the target), so jump to MA25 to skip targeting. Or \ to put it another way, if MSTG = &FF, which means \ there is no current target lock, keep going LDA KY12 \ If "[" is being pressed, keep going, otherwise jump BEQ MA25a \ down to MA25 to skip the following LDX player2NOMSL \ If the number of missiles in NOMSL is zero, jump down BEQ MA25a \ to MA25 to skip the following STA player2MSAR \ The "target missile" key is being pressed and we have \ at least one missile, so set MSAR = &FF to denote that \ our missile is currently armed (we know A has the \ value &FF, as we just loaded it from MSTG and checked \ that it was negative) LDA #2 \ Set A to draw player 2's indicators LDY #YELLOW2 \ Change the leftmost missile indicator to yellow JSR MSBAR \ on the missile bar (this call changes the leftmost \ indicator because we set X to the number of missiles \ in NOMSL above, and the indicators are numbered from \ right to left, so X is the number of the leftmost \ indicator) .MA25a LDA KY13 \ If "@" is being pressed, keep going, otherwise jump BEQ MA24a \ down to MA24 to skip the following LDA player2MSTG \ If MSTG = &FF then there is no target lock, so jump to BMI MA64a \ MA64a to skip the following (also skipping the checks \ for TAB, ESCAPE, "J" and "E") LDA player2Missile \ If player 2 does not already have a missile in-flight, BEQ miss1 \ then player2Missile will be zero, so jump to miss1 to \ keep going JSR Player2FR1 \ Otherwise call Player2FR1 to display "missile jammed" \ as player 2 can't fire a second missile JMP MA64a \ Jump to MA64a to skip the following (also skipping the \ checks for TAB, ESCAPE, "J" and "E") .miss1 LDX #2 \ Fetch the ship's coordinates in slot #2 JSR GetShipDataToINWK LDA player2MSTG \ If the target is player 2's ship, jump to miss2 to CMP #12 \ spawn a hostile missile (which will attack player 1) BEQ miss2 \ If we get here then the target must be a missile, so \ we need to set that as the target SEC \ Set A to use as an AI flag in SFS1 for a missile SBC #10 \ targeting the ship in slot player2MSTG - 10 ASL A ORA #%10000000 LDX #MSL \ Set X to the ship type of a missile, and call SFS1 JSR SFS1 \ to add a missile with the AI byte in A JSR frmi2 \ Spawn the missile by jumping into SFRMIS at frmi2 (to \ allow us to spawn a missile with a custom AI flag) JMP MA24a \ Skip the following instruction .miss2 JSR SFRMIS \ The "fire missile" key is being pressed and we have \ a missile lock, so call the SFRMIS routine to spawn a \ missile as a child of player 2's ship, make a noise \ and print a message warning of incoming missiles .MA24a \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \LDA KY12 \ If TAB is being pressed, keep going, otherwise jump \BEQ MA76 \ down to MA76 to skip the following \ \ASL BOMB \ The "energy bomb" key is being pressed, so double \ \ the value in BOMB. If we have an energy bomb fitted, \ \ BOMB will contain &7F (%01111111) before this shift \ \ and will contain &FE (%11111110) after the shift; if \ \ we don't have an energy bomb fitted, BOMB will still \ \ contain 0. The bomb explosion is dealt with in the \ \ MAL1 routine below - this just registers the fact that \ \ we've set the bomb ticking \ \.MA76 \ \LDA KY20 \ If "P" is being pressed, keep going, otherwise skip \BEQ MA78 \ the next two instructions \ \LDA #0 \ The "cancel docking computer" key is bring pressed, \STA auto \ so turn it off by setting auto to 0 \ \.MA78 \ \LDA KY13 \ If ESCAPE is being pressed and we have an escape pod \AND ESCP \ fitted, keep going, otherwise jump to noescp to skip \BEQ noescp \ the following instructions \ \LDA MJ \ If we are in witchspace, we can't launch our escape \BNE noescp \ pod, so jump down to noescp \ \JMP ESCAPE \ The button is being pressed to launch an escape pod \ \ and we have an escape pod fitted, so jump to ESCAPE to \ \ launch it, and exit the main flight loop using a tail \ \ call \ \.noescp \ \LDA KY18 \ If "J" is being pressed, keep going, otherwise skip \BEQ P%+5 \ the next instruction \ \JSR WARP \ Call the WARP routine to do an in-system jump \ --- End of removed code -----------------------------> LDA KY17 \ If "E" is being pressed and we have an E.C.M. fitted, AND ECM \ keep going, otherwise jump down to MA64 to skip the BEQ MA64 \ following LDA ECMA \ If ECMA is non-zero, that means an E.C.M. is already BNE MA64 \ operating and is counting down (this can be either \ our E.C.M. or an opponent's), so jump down to MA64 to \ skip the following (as we can't have two E.C.M. \ systems operating at the same time) DEC ECMP \ The E.C.M. button is being pressed and nobody else \ is operating their E.C.M., so decrease the value of \ ECMP to make it non-zero, to denote that our E.C.M. \ is now on JSR ECBLB2 \ Call ECBLB2 to light up the E.C.M. indicator bulb on \ the dashboard, set the E.C.M. countdown timer to 32, \ and start making the E.C.M. sound .MA64 \ --- Mod: Code added for two-player Elite: -----------> LDA KY18 \ If "\" is being pressed and we have an E.C.M. fitted, AND player2ECM \ keep going, otherwise jump down to MA64 to skip the BEQ MA64a \ following LDA player2ECMA \ If ECMA is non-zero, that means an E.C.M. is already BNE MA64a \ operating and is counting down (this can be either \ our E.C.M. or an opponent's), so jump down to MA64 to \ skip the following (as we can't have two E.C.M. \ systems operating at the same time) DEC player2ECMP \ The E.C.M. button is being pressed and nobody else \ is operating their E.C.M., so decrease the value of \ ECMP to make it non-zero, to denote that our E.C.M. \ is now on JSR Player2ECBLB2 \ Call ECBLB2 to light up the E.C.M. indicator bulb on \ the dashboard, set the E.C.M. countdown timer to 32, \ and start making the E.C.M. sound .MA64a \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \LDA KY19 \ If "C" is being pressed, and we have a docking \AND DKCMP \ computer fitted, keep going, otherwise jump down to \BEQ MA68 \ MA68 to skip the following \ \STA auto \ Set auto to the non-zero value of A, so the docking \ \ computer is activated \ \.MA68 \ --- End of removed code -----------------------------> LDA #0 \ Set LAS = 0, to switch the laser off while we do the STA LAS \ following logic STA DELT4 \ Take the 16-bit value (DELTA 0) - i.e. a two-byte LDA DELTA \ number with DELTA as the high byte and 0 as the low LSR A \ byte - and divide it by 4, storing the 16-bit result ROR DELT4 \ in DELT4(1 0). This has the effect of storing the LSR A \ current speed * 64 in the 16-bit location DELT4(1 0) ROR DELT4 STA DELT4+1 \ --- Mod: Code added for two-player Elite: -----------> LDA K%+NI%*2+32 \ If player 2 is not an NPC, skip the following check BPL main6 BIT player2Firing \ If player 2 is not firing its laser then skip the BPL main6 \ following LDA #&FF \ Fire the NPC's front lasers STA KY22 .main6 LDA player2Exploding \ If player2Exploding is non-zero then player 2's ship BEQ P%+5 \ is exploding at the end of the game, so we need to JMP MA3 \ skip the laser checks as bit 6 of INWK+31 is now being \ used for tracking the explosion, so jump to part 4 LDA K%+NI%*2+31 \ Clear bit 6 of player 2's INWK+31 byte so we switch AND #%10111111 \ the lasers off by default STA K%+NI%*2+31 LDA #0 \ Set LAS = 0, to switch the laser off while we do the STA player2LAS \ following logic STA player2DELT4 \ Take the 16-bit value (DELTA 0) - i.e. a two-byte LDA player2DELTA \ number with DELTA as the high byte and 0 as the low LSR A \ byte - and divide it by 4, storing the 16-bit result ROR player2DELT4 \ in DELT4(1 0). This has the effect of storing the LSR A \ current speed * 64 in the 16-bit location DELT4(1 0) ROR player2DELT4 STA player2DELT4+1 LDA player2LASCT \ If LASCT is zero, keep going, otherwise the laser is BNE MA3a \ a pulse laser that is between pulses, so jump down to \ MA3 to skip the following LDA KY22 \ If the player 2 fire button is being pressed, keep BEQ MA3a \ going, otherwise jump down to MA3 to skip the \ following LDA player2GNTMP \ If the laser temperature >= 242 then the laser has CMP #242 \ overheated, so jump down to MA3 to skip the following BCS MA3a LDX player2VIEW \ If the current space view has a laser fitted (i.e. the LDA player2LASER,X \ laser power for this view is greater than zero), then BEQ MA3a \ keep going, otherwise jump down to MA3 to skip the \ following \ If we get here, then the "fire" button is being \ pressed, our laser hasn't overheated and isn't already \ being fired, and we actually have a laser fitted to \ the current space view, so it's time to hit me with \ those laser beams PHA \ Store the current view's laser power on the stack AND #%01111111 \ Set LAS and LAS2 to bits 0-6 of the laser power STA player2LAS STA player2LAS2 LDA #0 \ Call the NOISE routine with A = 0 to make the sound JSR NOISE \ of our laser firing JSR Player2LASLI \ Call LASLI to draw the laser lines LDA K%+NI%*2+31 \ Set bit 6 of player 2's INWK+31 byte in slot #2 so we ORA #%01000000 \ draw a laser line for player 2 STA K%+NI%*2+31 PLA \ Restore the current view's laser power into A BPL ma1a \ If the laser power has bit 7 set, then it's an "always \ on" laser rather than a pulsing laser, so keep going, \ otherwise jump down to ma1 to skip the following \ instruction LDA #0 \ This is an "always on" laser (i.e. a beam laser or a \ military laser), so set A = 0, which will be stored in \ LASCT to denote that this is not a pulsing laser .ma1a AND #%11111010 \ LASCT will be set to 0 for beam lasers, and to the STA player2LASCT \ laser power AND %11111010 for pulse lasers, which \ comes to 10 for pulse lasers (as pulse lasers have a \ power of 15) or 50 for mining lasers (as mining \ lasers hava a power of 50). See MA23 in part 16 for \ more on laser pulsing and LASCT .MA3a \ --- End of added code -------------------------------> LDA player1INWK31 \ Clear bit 6 of player 1's INWK+31 byte so we switch AND #%10111111 \ the lasers off by default STA player1INWK31 LDA LASCT \ If LASCT is zero, keep going, otherwise the laser is BNE MA3 \ a pulse laser that is between pulses, so jump down to \ MA3 to skip the following LDA KY7 \ If "A" is being pressed, keep going, otherwise jump BEQ MA3 \ down to MA3 to skip the following LDA GNTMP \ If the laser temperature >= 242 then the laser has CMP #242 \ overheated, so jump down to MA3 to skip the following BCS MA3 LDX VIEW \ If the current space view has a laser fitted (i.e. the LDA LASER,X \ laser power for this view is greater than zero), then BEQ MA3 \ keep going, otherwise jump down to MA3 to skip the \ following \ If we get here, then the "fire" button is being \ pressed, our laser hasn't overheated and isn't already \ being fired, and we actually have a laser fitted to \ the current space view, so it's time to hit me with \ those laser beams PHA \ Store the current view's laser power on the stack AND #%01111111 \ Set LAS and LAS2 to bits 0-6 of the laser power STA LAS STA LAS2 LDA #0 \ Call the NOISE routine with A = 0 to make the sound JSR NOISE \ of our laser firing JSR LASLI \ Call LASLI to draw the laser lines LDA player1INWK31 \ Set bit 6 of player 1's INWK+31 byte so we draw a ORA #%01000000 \ laser line for player 1 STA player1INWK31 PLA \ Restore the current view's laser power into A BPL ma1 \ If the laser power has bit 7 set, then it's an "always \ on" laser rather than a pulsing laser, so keep going, \ otherwise jump down to ma1 to skip the following \ instruction LDA #0 \ This is an "always on" laser (i.e. a beam laser or a \ military laser), so set A = 0, which will be stored in \ LASCT to denote that this is not a pulsing laser .ma1 AND #%11111010 \ LASCT will be set to 0 for beam lasers, and to the STA LASCT \ laser power AND %11111010 for pulse lasers, which \ comes to 10 for pulse lasers (as pulse lasers have a \ power of 15) or 50 for mining lasers (as mining \ lasers hava a power of 50). See MA23 in part 16 for \ more on laser pulsing and LASCT \ ****************************************************************************** \ \ Name: Main flight loop (Part 4 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: For each nearby ship: Copy the ship's data block from K% to the \ zero-page workspace at INWK \ Deep dive: Program flow of the main game loop \ Ship data blocks \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Start looping through all the ships in the local bubble, and for each \ one: \ \ * Copy the ship's data block from K% to INWK \ \ * Set XX0 to point to the ship's blueprint (if this is a ship) \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ MAL1 Marks the beginning of the ship analysis loop, so we \ can jump back here from part 12 of the main flight loop \ to work our way through each ship in the local bubble. \ We also jump back here when a ship is removed from the \ bubble, so we can continue processing from the next ship \ \ ****************************************************************************** .MA3 LDX #0 \ We're about to work our way through all the ships in \ our local bubble of universe, so set a counter in X, \ starting from 0, to refer to each ship slot in turn .MAL1 STX XSAV \ Store the current slot number in XSAV LDA FRIN,X \ Fetch the contents of this slot into A. If it is 0 BNE P%+5 \ then this slot is empty and we have no more ships to JMP MA18 \ process, so jump to MA18 below, otherwise A contains \ the type of ship that's in this slot, so skip over the \ JMP MA18 instruction and keep going STA TYPE \ Store the ship type in TYPE JSR GINF \ Call GINF to fetch the address of the ship data block \ for the ship in slot X and store it in INF. The data \ block is in the K% workspace, which is where all the \ ship data blocks are stored \ Next we want to copy the ship data block from INF to \ the zero-page workspace at INWK, so we can process it \ more efficiently LDY #NI%-1 \ There are NI% bytes in each ship data block (and in \ the INWK workspace, so we set a counter in Y so we can \ loop through them .MAL2 LDA (INF),Y \ Load the Y-th byte of INF and store it in the Y-th STA INWK,Y \ byte of INWK DEY \ Decrement the loop counter BPL MAL2 \ Loop back for the next byte until we have copied the \ last byte from INF to INWK LDA TYPE \ If the ship type is negative then this indicates a BMI MA21 \ planet or sun, so jump down to MA21, as the next bit \ sets up a pointer to the ship blueprint, and then \ checks for energy bomb damage, and neither of these \ apply to planets and suns ASL A \ Set Y = ship type * 2 TAY LDA XX21-2,Y \ The ship blueprints at XX21 start with a lookup STA XX0 \ table that points to the individual ship blueprints, \ so this fetches the low byte of this particular ship \ type's blueprint and stores it in XX0 LDA XX21-1,Y \ Fetch the high byte of this particular ship type's STA XX0+1 \ blueprint and store it in XX0+1 \ ****************************************************************************** \ \ Name: Main flight loop (Part 5 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: For each nearby ship: If an energy bomb has been set off, \ potentially kill this ship \ Deep dive: Program flow of the main game loop \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Continue looping through all the ships in the local bubble, and for each \ one: \ \ * If an energy bomb has been set off and this ship can be killed, kill it \ and increase the kill tally \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \LDA BOMB \ If we set off our energy bomb (see MA24 above), then \BPL MA21 \ BOMB is now negative, so this skips to MA21 if our \ \ energy bomb is not going off \ \CPY #2*SST \ If the ship in Y is the space station, jump to BA21 \BEQ MA21 \ as energy bombs are useless against space stations \ \CPY #2*CON \ If the ship in Y is the Constrictor, jump to BA21 \BCS MA21 \ as energy bombs are useless against the Constrictor \ \ (the Constrictor is the target of mission 1, and it \ \ would be too easy if it could just be blown out of \ \ the sky with a single key press) \ \LDA INWK+31 \ If the ship we are checking has bit 5 set in its ship \AND #%00100000 \ byte #31, then it is already exploding, so jump to \BNE MA21 \ BA21 as ships can't explode more than once \ \ASL INWK+31 \ The energy bomb is killing this ship, so set bit 7 of \SEC \ the ship byte #31 to indicate that it has now been \ROR INWK+31 \ killed \ \JSR EXNO2 \ Call EXNO2 to process the fact that we have killed a \ \ ship (so increase the kill tally, make an explosion \ \ sound and possibly display "RIGHT ON COMMANDER!") \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Main flight loop (Part 6 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: For each nearby ship: Move the ship in space and copy the updated \ INWK data block back to K% \ Deep dive: Program flow of the main game loop \ Program flow of the ship-moving routine \ Ship data blocks \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Continue looping through all the ships in the local bubble, and for each \ one: \ \ * Move the ship in space \ \ * Copy the updated ship's data block from INWK back to K% \ \ ****************************************************************************** .MA21 JSR MVEIT \ Call MVEIT to move the ship we are processing in space \ Now that we are done processing this ship, we need to \ copy the ship data back from INWK to the correct place \ in the K% workspace. We already set INF in part 4 to \ point to the ship's data block in K%, so we can simply \ do the reverse of the copy we did before, this time \ copying from INWK to INF LDY #NI%-1 \ Set a counter in Y so we can loop through the NI% \ bytes in the ship data block .MAL3 LDA INWK,Y \ Load the Y-th byte of INWK and store it in the Y-th STA (INF),Y \ byte of INF DEY \ Decrement the loop counter BPL MAL3 \ Loop back for the next byte, until we have copied the \ last byte from INWK back to INF \ ****************************************************************************** \ \ Name: Main flight loop (Part 7 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: For each nearby ship: Check whether we are docking, scooping or \ colliding with it \ Deep dive: Program flow of the main game loop \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Continue looping through all the ships in the local bubble, and for each \ one: \ \ * Check how close we are to this ship and work out if we are docking, \ scooping or colliding with it \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \LDA INWK+31 \ Fetch the status of this ship from bits 5 (is ship \AND #%10100000 \ exploding?) and bit 7 (has ship been killed?) from \ \ ship byte #31 into A \ \JSR MAS4 \ Or this value with x_hi, y_hi and z_hi \ \BNE MA65 \ If this value is non-zero, then either the ship is \ \ far away (i.e. has a non-zero high byte in at least \ \ one of the three axes), or it is already exploding, \ \ or has been flagged as being killed - in which case \ \ jump to MA65 to skip the following, as we can't dock \ \ scoop or collide with it \ \LDA INWK \ Set A = (x_lo OR y_lo OR z_lo), and if bit 7 of the \ORA INWK+3 \ result is set, the ship is still a fair distance \ORA INWK+6 \ away (further than 127 in at least one axis), so jump \BMI MA65 \ to MA65 to skip the following, as it's too far away to \ \ dock, scoop or collide with \ \LDX TYPE \ If the current ship type is negative then it's either \BMI MA65 \ a planet or a sun, so jump down to MA65 to skip the \ \ following, as we can't dock with it or scoop it \ \CPX #SST \ If this ship is the space station, jump to ISDK to \BEQ ISDK \ check whether we are docking with it \ \AND #%11000000 \ If bit 6 of (x_lo OR y_lo OR z_lo) is set, then the \BNE MA65 \ ship is still a reasonable distance away (further than \ \ 63 in at least one axis), so jump to MA65 to skip the \ \ following, as it's too far away to dock, scoop or \ \ collide with \ \CPX #MSL \ If this ship is a missile, jump down to MA65 to skip \BEQ MA65 \ the following, as we can't scoop or dock with a \ \ missile, and it has its own dedicated collision \ \ checks in the TACTICS routine \ \LDA BST \ If we have fuel scoops fitted then BST will be &FF, \ \ otherwise it will be 0 \ \AND INWK+5 \ Ship byte #5 contains the y_sign of this ship, so a \ \ negative value here means the canister is below us, \ \ which means the result of the AND will be negative if \ \ the canister is below us and we have a fuel scoop \ \ fitted \ \BPL MA58 \ If the result is positive, then we either have no \ \ scoop or the canister is above us, and in both cases \ \ this means we can't scoop the item, so jump to MA58 \ \ to process a collision \ --- And replaced by: --------------------------------> LDA XSAV \ If this isn't player 2's ship, jump to main4 to skip CMP #2 \ the collision checks, as we're only interested in BNE main4 \ collisions between the two players BIT gameOver \ If bit 7 of gameOver is set then we are on the game BMI main4 \ over screen and collisions are disabled, so jump to \ main4 to skip all the collision code LDA collisionCounter \ Set A to the value of the collision counter BEQ main1 \ If collisionCounter is zero then it's been long enough \ since the last collision, so jump to main1 to check \ whether the ships are colliding DEC collisionCounter \ We recently had a collision, so decrement the \ collision counter BRA main4 \ Jump to main4 to skip all the collision code .main1 LDA #0 \ If any of x_hi, y_hi or z_hi are non-zero, then jump JSR MAS4 \ to main4 as the ships are too far away to collide BNE main4 LDA INWK \ Set A = (x_lo OR y_lo OR z_lo), and if bit 7 of the ORA INWK+3 \ result is set, the ship is still a fair distance ORA INWK+6 \ away (further than 127 in at least one axis), so jump BMI main4 \ to main4 to skip the following, as it's too far away \ to collide with \ If we get here then we have a collision JSR EXNO3 \ Make the sound of a collision LDA #20 \ Set collisionCounter so we don't check for any more STA collisionCounter \ collisions for this many iterations of the main loop \ (to prevent a single collision from cascading the \ scores to zero) LDA player2DELTA \ Set A = player2DELTA * 3 / 4 ASL A \ CLC \ This makes player 2's in-game speed match that of ADC player2DELTA \ player 1 (I'm not sure why this is required, but LSR A \ player 2 is way too fast without this scaling) LSR A CMP DELTA \ If A < DELTA then player 1 is faster than player 2, so BCC main3 \ jump to main3 to process the damage BNE main2 \ If A > DELTA then player 1 is slower than player 2, so \ jump to main2 to process the damage \ If we get here then player 1 and player 2 are going at \ the same speed LDA #80 \ Call oops1 to damage player 1 by 80 and without STA damageToApply \ affecting the scoree JSR oops1 LDA #80 \ Call oopz1 to damage player 2 by 80 and without STA damageToApply \ affecting the scoree JSR oopz1 BRA main4 \ Jump to main4 to keep going .main2 \ If we get here then player 1 is slower than player 2 LDA #80 \ Call oops1 to damage player 1 by 80 and without STA damageToApply \ affecting the scoree JSR oops1 LDA #250 \ Call oopz1 to damage player 2 by 250 and without STA damageToApply \ affecting the scoree JSR oopz1 BRA main4 \ Jump to main4 to keep going .main3 \ If we get here then player 1 is faster than player 2 LDA #250 \ Call oops1 to damage player 1 by 250 and without STA damageToApply \ affecting the scoree JSR oops1 LDA #80 \ Call oopz1 to damage player 2 by 80 and without STA damageToApply \ affecting the scoree JSR oopz1 .main4 \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: Main flight loop (Part 8 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: For each nearby ship: Process us potentially scooping this item \ Deep dive: Program flow of the main game loop \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Continue looping through all the ships in the local bubble, and for each \ one: \ \ * Process us potentially scooping this item \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \CPX #OIL \ If this is a cargo canister, jump to oily to randomly \BEQ oily \ decide the canister's contents \ \LDY #0 \ Fetch byte #0 of the ship's blueprint \LDA (XX0),Y \ \LSR A \ Shift it right four times, so A now contains the high \LSR A \ nibble (i.e. bits 4-7) \LSR A \LSR A \ \BEQ MA58 \ If A = 0, jump to MA58 to skip all the docking and \ \ scooping checks \ \ \ Only the Thargon, alloy plate, splinter and escape pod \ \ have non-zero high nibbles in their blueprint byte #0 \ \ so if we get here, our ship is one of those, and the \ \ high nibble gives the market item number of the item \ \ when scooped, less 1 \ \ADC #1 \ Add 1 to the high nibble to get the market item \ \ number \ \BNE slvy2 \ Skip to slvy2 so we scoop the ship as a market item \ \.oily \ \JSR DORND \ Set A and X to random numbers and reduce A to a \AND #7 \ random number in the range 0-7 \ \.slvy2 \ \ \ By the time we get here, we are scooping, and A \ \ contains the type of item we are scooping (a random \ \ number 0-7 if we are scooping a cargo canister, 3 if \ \ we are scooping an escape pod, or 16 if we are \ \ scooping a Thargon). These numbers correspond to the \ \ relevant market items (see QQ23 for a list), so a \ \ cargo canister can contain anything from food to \ \ computers, while escape pods contain slaves, and \ \ Thargons become alien items when scooped \ \JSR tnpr1 \ Call tnpr1 with the scooped cargo type stored in A \ \ to work out whether we have room in the hold for one \ \ tonne of this cargo (A is set to 1 by this call, and \ \ the C flag contains the result) \ \LDY #78 \ This instruction has no effect, so presumably it used \ \ to do something, but didn't get removed \ \BCS MA59 \ If the C flag is set then we have no room in the hold \ \ for the scooped item, so jump down to MA59 make a \ \ sound to indicate failure, before destroying the \ \ canister \ \LDY QQ29 \ Scooping was successful, so set Y to the type of \ \ item we just scooped, which we stored in QQ29 above \ \ADC QQ20,Y \ Add A (which we set to 1 above) to the number of items \STA QQ20,Y \ of type Y in the cargo hold, as we just successfully \ \ scooped one canister of type Y \ \TYA \ Print recursive token 48 + Y as an in-flight token, \ADC #208 \ which will be in the range 48 ("FOOD") to 64 ("ALIEN \JSR MESS \ ITEMS"), so this prints the scooped item's name \ \ASL NEWB \ The item has now been scooped, so set bit 7 of its \SEC \ NEWB flags to indicate this \ROR NEWB \ \.MA65 \ \JMP MA26 \ If we get here, then the ship we are processing was \ \ too far away to be scooped, docked or collided with, \ \ so jump to MA26 to skip over the collision routines \ \ and move on to missile targeting \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Main flight loop (Part 9 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: For each nearby ship: If it is a space station, check whether we \ are successfully docking with it \ Deep dive: Program flow of the main game loop \ Docking checks \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Process docking with a space station \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ GOIN We jump here from part 3 of the main flight loop if the \ docking computer is activated by pressing "C" \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.ISDK \ \LDA K%+NI%+36 \ 1. Fetch the NEWB flags (byte #36) of the second ship \AND #%00000100 \ in the ship data workspace at K%, which is reserved \BNE MA62 \ for the sun or the space station (in this case it's \ \ the latter), and if bit 2 is set, meaning the station \ \ is hostile, jump down to MA62 to fail docking (so \ \ trying to dock at a station that we have annoyed does \ \ not end well) \ \LDA INWK+14 \ 2. If nosev_z_hi < 214, jump down to MA62 to fail \CMP #214 \ docking, as the angle of approach is greater than 26 \BCC MA62 \ degrees \ \JSR SPS1 \ Call SPS1 to calculate the vector to the planet and \ \ store it in XX15 \ \LDA XX15+2 \ Set A to the z-axis of the vector \ \ \ This version of Elite omits check 3 (which would check \ \ the sign of the z-axis) \ \CMP #89 \ 4. If z-axis < 89, jump to MA62 to fail docking, as \BCC MA62 \ we are not in the 22.0 degree safe cone of approach \ \LDA INWK+16 \ 5. If |roofv_x_hi| < 80, jump to MA62 to fail docking, \AND #%01111111 \ as the slot is more than 36.6 degrees from horizontal \CMP #80 \BCC MA62 \ \.GOIN \ \ \ If we arrive here, we just docked successfully \ \JMP DOENTRY \ Go to the docking bay (i.e. show the ship hangar) \ \.MA62 \ \ \ If we arrive here, docking has just failed \ \LDA DELTA \ If the ship's speed is < 5, jump to MA67 to register \CMP #5 \ some damage, but not a huge amount \BCC MA67 \ \JMP DEATH \ Otherwise we have just crashed into the station, so \ \ process our death \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Main flight loop (Part 10 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: For each nearby ship: Remove if scooped, or process collisions \ Deep dive: Program flow of the main game loop \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Continue looping through all the ships in the local bubble, and for each \ one: \ \ * Remove scooped item after both successful and failed scooping attempts \ \ * Process collisions \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.MA59 \ \ \ If we get here then scooping failed \ \JSR EXNO3 \ Make the sound of the cargo canister being destroyed \ \ and fall through into MA60 to remove the canister \ \ from our local bubble \ \.MA60 \ \ \ If we get here then scooping was successful \ \ASL INWK+31 \ Set bit 7 of the scooped or destroyed item, to denote \SEC \ that it has been killed and should be removed from \ROR INWK+31 \ the local bubble \ \.MA61 \ \BNE MA26 \ Jump to MA26 to skip over the collision routines and \ \ to move on to missile targeting (this BNE is \ \ effectively a JMP as A will never be zero) \ \.MA67 \ \ \ If we get here then we have collided with something, \ \ but not fatally \ \LDA #1 \ Set the speed in DELTA to 1 (i.e. a sudden stop) \STA DELTA \ \LDA #5 \ Set the amount of damage in A to 5 (a small dent) and \BNE MA63 \ jump down to MA63 to process the damage (this BNE is \ \ effectively a JMP as A will never be zero) \ \.MA58 \ \ \ If we get here, we have collided with something in a \ \ potentially fatal way \ \ASL INWK+31 \ Set bit 7 of the ship we just collided with, to \SEC \ denote that it has been killed and should be removed \ROR INWK+31 \ from the local bubble \ \LDA INWK+35 \ Load A with the energy level of the ship we just hit \ \SEC \ Set the amount of damage in A to 128 + A / 2, so \ROR A \ this is quite a big dent, and colliding with higher \ \ energy ships will cause more damage \ \.MA63 \ \JSR OOPS \ The amount of damage is in A, so call OOPS to reduce \ \ our shields, and if the shields are gone, there's a \ \ chance of cargo loss or even death \ \JSR EXNO3 \ Make the sound of colliding with the other ship and \ \ fall through into MA26 to try targeting a missile \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Main flight loop (Part 11 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: For each nearby ship: Process missile lock and firing our laser \ Deep dive: Program flow of the main game loop \ Flipping axes between space views \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Continue looping through all the ships in the local bubble, and for each \ one: \ \ * If this is not the front space view, flip the axes of the ship's \ coordinates in INWK \ \ * Process missile lock \ \ * Process our laser firing \ \ ****************************************************************************** .MA26 \ --- Mod: Code removed for two-player Elite: ---------> \LDA NEWB \ If bit 7 of the ship's NEWB flags is clear, skip the \BPL P%+5 \ following instruction \ \JSR SCAN \ Bit 7 of the ship's NEWB flags is set, which means the \ \ ship has docked or been scooped, so we draw the ship \ \ on the scanner, which has the effect of removing it \ --- End of removed code -----------------------------> LDA QQ11 \ If this is not a space view, jump to MA15 to skip BNE MA15 \ missile and laser locking JSR PLUT \ Call PLUT to update the geometric axes in INWK to \ match the view (front, rear, left, right) JSR HITCH \ Call HITCH to see if this ship is in the crosshairs, BCC MA8 \ in which case the C flag will be set (so if there is \ no missile or laser lock, we jump to MA8 to skip the \ following) LDA MSAR \ We have missile lock, so check whether the leftmost BEQ MA47 \ missile is currently armed, and if not, jump to MA47 \ to process laser fire, as we can't lock an unarmed \ missile JSR BEEP \ We have missile lock and an armed missile, so call \ the BEEP subroutine to make a short, high beep LDX XSAV \ Call ABORT2 to store the details of this missile LDY #RED2 \ lock, with the targeted ship's slot number in X JSR ABORT2 \ (which we stored in XSAV at the start of this ship's \ loop at MAL1), and set the colour of the missile \ indicator to the colour in Y (red = &0E) .MA47 \ If we get here then the ship is in our sights, but \ we didn't lock a missile, so let's see if we're \ firing the laser LDA LAS \ If we are firing the laser then LAS will contain the BEQ MA8 \ laser power (which we set in MA68 above), so if this \ is zero, jump down to MA8 to skip the following LDX #15 \ We are firing our laser and the ship in INWK is in JSR EXNO \ the crosshairs, so call EXNO to make the sound of \ us making a laser strike on another ship \ --- Mod: Code removed for two-player Elite: ---------> \LDA TYPE \ Did we just hit the space station? If so, jump to \CMP #SST \ MA14+2 to make the station hostile, skipping the \BEQ MA14+2 \ following as we can't destroy a space station \ \CMP #CON \ If the ship we hit is less than #CON - i.e. it's not \BCC BURN \ a Constrictor, Cougar, Dodo station or the Elite logo, \ \ jump to BURN to skip the following \ \LDA LAS \ Set A to the power of the laser we just used to hit \ \ the ship (i.e. the laser in the current view) \ \CMP #(Armlas AND 127) \ If the laser is not a military laser, jump to MA14+2 \BNE MA14+2 \ to skip the following, as only military lasers have \ \ any effect on the Constrictor or Cougar (or the Elite \ \ logo, should you ever bump into one of those out there \ \ in the black...) \ \LSR LAS \ Divide the laser power of the current view by 4, so \LSR LAS \ the damage inflicted on the super-ship is a quarter of \ \ the damage our military lasers would inflict on a \ \ normal ship \ \.BURN \ --- End of removed code -----------------------------> \ --- Mod: Code added for two-player Elite: -----------> LDA XSAV \ If this is not player 2, jump to main7 to skip the CMP #2 \ following BNE main7 \ Player 2 has been hit, so process player 2's shields LDA LAS \ Fetch the power of the current laser and clear the AND #%01111111 \ continuous bit to fetch the power per pulse, and then LSR A \ halve the laser power to get the damage level JSR Player2OOPS \ Remove the relevant energy from player 2's shields \ and update the scores JMP MA8 \ Jump to MA8 to skip the following .main7 \ We are not hitting player 2, so we must be hitting a \ missile, so kill it instantly (as the missile's \ maximum energy is only 2, which is way less than any \ individual laser hit) ASL INWK+31 \ Set bit 7 of the ship byte #31 to indicate that it has SEC \ now been killed ROR INWK+31 \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \LDA INWK+35 \ Fetch the hit ship's energy from byte #35 and subtract \SEC \ our current laser power, and if the result is greater \SBC LAS \ than zero, the other ship has survived the hit, so \BCS MA14 \ jump down to MA14 to make it angry \ \ASL INWK+31 \ Set bit 7 of the ship byte #31 to indicate that it has \SEC \ now been killed \ROR INWK+31 \ \LDA TYPE \ Did we just kill an asteroid? If not, jump to nosp, \CMP #AST \ otherwise keep going \BNE nosp \ \LDA LAS \ Did we kill the asteroid using mining lasers? If not, \CMP #Mlas \ jump to nosp, otherwise keep going \BNE nosp \ \JSR DORND \ Set A and X to random numbers \ \LDX #SPL \ Set X to the ship type for a splinter \ \AND #3 \ Reduce the random number in A to the range 0-3 \ \JSR SPIN2 \ Call SPIN2 to spawn A items of type X (i.e. spawn \ \ 0-3 splinters) \ \.nosp \ \LDY #PLT \ Randomly spawn some alloy plates \JSR SPIN \ \LDY #OIL \ Randomly spawn some cargo canisters \JSR SPIN \ \JSR EXNO2 \ Call EXNO2 to process the fact that we have killed a \ \ ship (so increase the kill tally, make an explosion \ \ sound and so on) \ \.MA14 \ \STA INWK+35 \ Store the hit ship's updated energy in ship byte #35 \ \LDA TYPE \ Call ANGRY to make the target ship or station hostile, \JSR ANGRY \ and if this is a ship, wake up its AI and give it a \ \ kick of speed \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Main flight loop (Part 12 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: For each nearby ship: Draw the ship, remove if killed, loop back \ Deep dive: Program flow of the main game loop \ Drawing ships \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Continue looping through all the ships in the local bubble, and for each \ one: \ \ * Draw the ship \ \ * Process removal of killed ships \ \ * Loop back up to MAL1 to move onto the next ship in the local bubble \ \ ****************************************************************************** .MA8 \ --- Mod: Code added for two-player Elite: -----------> STZ drawPlayerView \ Draw ship for player 1 BIT INWK+31 \ If the ship is not exploding, jump to main5 to skip BPL main5 \ the following LDX XSAV \ If this is not a missile, skip the following CMP #3 BCC main5 DEX \ This is a missile, so set bit 7 of player1INWK31 so DEX \ the missile explodes in player 2's view as well as ASL player1INWK31,X \ player 1's view SEC ROR player1INWK31,X .main5 \ --- End of added code -------------------------------> JSR LL9 \ Call LL9 to draw the ship we're processing on-screen \ --- Mod: Code added for two-player Elite: -----------> JSR DrawPlayer2View \ Draw the same ship from the perspective of player 2 \ --- End of added code -------------------------------> .MA15 LDY #35 \ Fetch the ship's energy from byte #35 and copy it to LDA INWK+35 \ byte #35 in INF (so the ship's data in K% gets STA (INF),Y \ updated) LDA NEWB \ If bit 7 of the ship's NEWB flags is set, which means BMI KS1S \ the ship has docked or been scooped, jump to KS1S to \ skip the following, as we can't get a bounty for a \ ship that's no longer around LDA INWK+31 \ If bit 7 of the ship's byte #31 is clear, then the BPL MAC1 \ ship hasn't been killed by energy bomb, collision or \ laser fire, so jump to MAC1 to skip the following AND #%00100000 \ If bit 5 of the ship's byte #31 is clear then the BEQ MAC1 \ ship is no longer exploding, so jump to MAC1 to skip \ the following \ --- Mod: Code removed for two-player Elite: ---------> \LDA NEWB \ Extract bit 6 of the ship's NEWB flags, so A = 64 if \AND #%01000000 \ bit 6 is set, or 0 if it is clear. Bit 6 is set if \ \ this ship is a cop, so A = 64 if we just killed a \ \ policeman, otherwise it is 0 \ \ORA FIST \ Update our FIST flag ("fugitive/innocent status") to \STA FIST \ at least the value in A, which will instantly make us \ \ a fugitive if we just shot the sheriff, but won't \ \ affect our status if the enemy wasn't a copper \ \LDA DLY \ If we already have an in-flight message on-screen (in \ORA MJ \ which case DLY > 0), or we are in witchspace (in \BNE KS1S \ which case MJ > 0), jump to KS1S to skip showing an \ \ on-screen bounty for this kill \ \LDY #10 \ Fetch byte #10 of the ship's blueprint, which is the \LDA (XX0),Y \ low byte of the bounty awarded when this ship is \BEQ KS1S \ killed (in Cr * 10), and if it's zero jump to KS1S as \ \ there is no on-screen bounty to display \ \TAX \ Put the low byte of the bounty into X \ \INY \ Fetch byte #11 of the ship's blueprint, which is the \LDA (XX0),Y \ high byte of the bounty awarded (in Cr * 10), and put \TAY \ it into Y \ \JSR MCASH \ Call MCASH to add (Y X) to the cash pot \ \LDA #0 \ Print control code 0 (current cash, right-aligned to \JSR MESS \ width 9, then " CR", newline) as an in-flight message \ --- End of removed code -----------------------------> .KS1S JMP KS1 \ Process the killing of this ship (which removes this \ ship from its slot and shuffles all the other ships \ down to close up the gap) .MAC1 \ --- Mod: Code removed for two-player Elite: ---------> \LDA TYPE \ If the ship we are processing is a planet or sun, \BMI MA27 \ jump to MA27 to skip the following two instructions \ \JSR FAROF \ If the ship we are processing is a long way away (its \BCC KS1S \ distance in any one direction is > 224, jump to KS1S \ \ to remove the ship from our local bubble, as it's just \ \ left the building \ \.MA27 \ --- End of removed code -----------------------------> LDY #31 \ Fetch the ship's explosion/killed state from byte #31 LDA INWK+31 \ and copy it to byte #31 in INF (so the ship's data in STA (INF),Y \ K% gets updated) LDX XSAV \ We're done processing this ship, so fetch the ship's \ slot number, which we saved in XSAV back at the start \ of the loop INX \ Increment the slot number to move on to the next slot JMP MAL1 \ And jump back up to the beginning of the loop to get \ the next ship in the local bubble for processing \ ****************************************************************************** \ \ Name: Main flight loop (Part 13 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: Show energy bomb effect, charge shields and energy banks \ Deep dive: Program flow of the main game loop \ Scheduling tasks with the main loop counter \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Show energy bomb effect (if applicable) \ \ * Charge shields and energy banks (every 7 iterations of the main loop) \ \ ****************************************************************************** .MA18 \ --- Mod: Code removed for two-player Elite: ---------> \LDA BOMB \ If we set off our energy bomb (see MA24 above), then \BPL MA77 \ BOMB is now negative, so this skips to MA21 if our \ \ energy bomb is not going off \ \ASL BOMB \ We set off our energy bomb, so rotate BOMB to the \ \ left by one place. BOMB was rotated left once already \ \ during this iteration of the main loop, back at MA24, \ \ so if this is the first pass it will already be \ \ %11111110, and this will shift it to %11111100 - so \ \ if we set off an energy bomb, it stays activated \ \ (BOMB > 0) for four iterations of the main loop \ \JSR WSCAN \ Call WSCAN to wait for the vertical sync, so the whole \ \ screen gets drawn and the following palette change \ \ won't kick in while the screen is still refreshing \ \LDA #DOFE21 \ Send a #DOFE21 %00110000 command to the I/O processor \JSR OSWRCH \ to map logical colour 0 to physical colour 7 (white), \LDA #%00110000 \ but with only one mapping (rather than the 7 \JSR OSWRCH \ mappings required to do the mapping properly). This \ \ makes the space screen flash with coloured stripes. \ \ See page 382 of the "Advanced User Guide for the BBC \ \ Micro" by Bray, Dickens and Holmes for details of why \ \ this single palette change creates a special effect \ \.MA77 \ \LDA MCNT \ Fetch the main loop counter and calculate MCNT mod 8, \AND #7 \ jumping to MA22 if it is non-zero (so the following \BNE MA22 \ code only runs every 8 iterations of the main loop) \ --- And replaced by: --------------------------------> LDA MCNT \ Fetch the main loop counter and calculate MCNT mod 8, AND #7 \ jumping to MA22 if it is non-zero (so the following BEQ P%+5 \ code only runs every 8 iterations of the main loop) JMP MA22 \ --- End of replacement ------------------------------> LDX ENERGY \ Fetch our ship's energy levels and skip to b if bit 7 BPL b \ is not set, i.e. only charge the shields from the \ energy banks if they are at more than 50% charge LDX ASH \ Call SHD to recharge our aft shield and update the JSR SHD \ shield status in ASH STX ASH LDX FSH \ Call SHD to recharge our forward shield and update JSR SHD \ the shield status in FSH STX FSH .b \ --- Mod: Code added for two-player Elite: -----------> LDX player2ENERGY \ Fetch our ship's energy levels and skip to ba if bit 7 BPL ba \ is not set, i.e. only charge the shields from the \ energy banks if they are at more than 50% charge LDX player2ASH \ Call SHD to recharge our aft shield and update the JSR Player2SHD \ shield status in ASH STX player2ASH LDX player2FSH \ Call SHD to recharge our forward shield and update JSR Player2SHD \ the shield status in FSH STX player2FSH .ba \ --- End of added code -------------------------------> SEC \ Set A = ENERGY + ENGY + 1, so our ship's energy LDA ENGY \ level goes up by 2 if we have an energy unit fitted, ADC ENERGY \ otherwise it goes up by 1 BCS P%+5 \ If the value of A did not overflow (the maximum STA ENERGY \ energy level is &FF), then store A in ENERGY \ --- Mod: Code added for two-player Elite: -----------> SEC \ Set A = ENERGY + ENGY + 1, so player 2's energy LDA player2ENGY \ level goes up by 2 if we have an energy unit fitted, ADC player2ENERGY \ otherwise it goes up by 1 BCS P%+5 \ If the value of A did not overflow (the maximum STA player2ENERGY \ energy level is &FF), then store A in ENERGY \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: Main flight loop (Part 14 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: Spawn a space station if we are close enough to the planet \ Deep dive: Program flow of the main game loop \ Scheduling tasks with the main loop counter \ Ship data blocks \ The space station safe zone \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Spawn a space station if we are close enough to the planet (every 32 \ iterations of the main loop) \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \LDA MJ \ If we are in witchspace, jump down to MA23S to skip \BNE MA23S \ the following, as there are no space stations in \ \ witchspace \ \LDA MCNT \ Fetch the main loop counter and calculate MCNT mod 32, \AND #31 \ jumping to MA93 if it is on-zero (so the following \BNE MA93 \ code only runs every 32 iterations of the main loop) \ \LDA SSPR \ If we are inside the space station safe zone, jump to \BNE MA23S \ MA23S to skip the following, as we already have a \ \ space station and don't need another \ \TAY \ Set Y = A = 0 (A is 0 as we didn't branch with the \ \ previous BNE instruction) \ \JSR MAS2 \ Call MAS2 to calculate the largest distance to the \BNE MA23S \ planet in any of the three axes, and if it's \ \ non-zero, jump to MA23S to skip the following, as we \ \ are too far from the planet to bump into a space \ \ station \ \ \ We now want to spawn a space station, so first we \ \ need to set up a ship data block for the station in \ \ INWK that we can then pass to NWSPS to add a new \ \ station to our bubble of universe. We do this by \ \ copying the planet data block from K% to INWK so we \ \ can work on it, but we only need the first 29 bytes, \ \ as we don't need to worry about bytes #29 to #35 \ \ for planets (as they don't have rotation counters, \ \ AI, explosions, missiles, a ship line heap or energy \ \ levels) \ \LDX #28 \ So we set a counter in X to copy 29 bytes from K%+0 \ \ to K%+28 \ \.MAL4 \ \LDA K%,X \ Load the X-th byte of K% and store in the X-th byte \STA INWK,X \ of the INWK workspace \ \DEX \ Decrement the loop counter \ \BPL MAL4 \ Loop back for the next byte until we have copied the \ \ first 28 bytes of K% to INWK \ \ \ We now check the distance from our ship (at the \ \ origin) towards the point where we will spawn the \ \ space station if we are close enough \ \ \ \ This point is calculated by starting at the planet's \ \ centre and adding 2 * nosev, which takes us to a point \ \ above the planet's surface, at an altitude that \ \ matches the planet's radius \ \ \ \ This point pitches and rolls around the planet as the \ \ nosev vector rotates with the planet, and if our ship \ \ is within a distance of (192 0) from this point in all \ \ three axes, then we spawn the space station at this \ \ point, with the station's slot facing towards the \ \ planet, along the nosev vector \ \ \ \ This works because in the following, we calculate the \ \ station's coordinates one axis at a time, and store \ \ the results in the INWK block, so by the time we have \ \ calculated and checked all three, the ship data block \ \ is set up with the correct spawning coordinates \ \INX \ Set X = 0 (as we ended the above loop with X as &FF) \ \LDY #9 \ Call MAS1 with X = 0, Y = 9 to do the following: \JSR MAS1 \ \ \ (x_sign x_hi x_lo) += (nosev_x_hi nosev_x_lo) * 2 \ \ \ \ A = |x_sign| \ \BNE MA23S \ If A > 0, jump to MA23S to skip the following, as we \ \ are too far from the planet in the x-direction to \ \ bump into a space station \ \LDX #3 \ Call MAS1 with X = 3, Y = 11 to do the following: \LDY #11 \ \JSR MAS1 \ (y_sign y_hi y_lo) += (nosev_y_hi nosev_y_lo) * 2 \ \ \ \ A = |y_sign| \ \BNE MA23S \ If A > 0, jump to MA23S to skip the following, as we \ \ are too far from the planet in the y-direction to \ \ bump into a space station \ \LDX #6 \ Call MAS1 with X = 6, Y = 13 to do the following: \LDY #13 \ \JSR MAS1 \ (z_sign z_hi z_lo) += (nosev_z_hi nosev_z_lo) * 2 \ \ \ \ A = |z_sign| \ \BNE MA23S \ If A > 0, jump to MA23S to skip the following, as we \ \ are too far from the planet in the z-direction to \ \ bump into a space station \ \LDA #192 \ Call FAROF2 to compare x_hi, y_hi and z_hi with 192, \JSR FAROF2 \ which will set the C flag if all three are < 192, or \ \ clear the C flag if any of them are >= 192 \ \BCC MA23S \ Jump to MA23S if any one of x_hi, y_hi or z_hi are \ \ >= 192 (i.e. they must all be < 192 for us to be near \ \ enough to the planet to bump into a space station) \ \JSR WPLS \ Call WPLS to remove the sun from the screen, as we \ \ can't have both the sun and the space station at the \ \ same time \ \JSR NWSPS \ Add a new space station to our local bubble of \ \ universe \ \.MA23S \ \JMP MA23 \ Jump to MA23 to skip the following planet and sun \ \ altitude checks \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Main flight loop (Part 15 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: Perform altitude checks with the planet and sun and process fuel \ scooping if appropriate \ Deep dive: Program flow of the main game loop \ Scheduling tasks with the main loop counter \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Perform an altitude check with the planet (every 32 iterations of the main \ loop, on iteration 10 of each 32) \ \ * Perform an altitude check with the sun and process fuel scooping (every \ 32 iterations of the main loop, on iteration 20 of each 32) \ \ ****************************************************************************** .MA22 \ --- Mod: Code removed for two-player Elite: ---------> \LDA MJ \ If we are in witchspace, jump down to MA23S to skip \BNE MA23S \ the following, as there are no planets or suns to \ \ bump into in witchspace \ --- End of removed code -----------------------------> LDA MCNT \ Fetch the main loop counter and calculate MCNT mod 32, AND #31 \ which tells us the position of this loop in each block \ of 32 iterations .MA93 \ --- Mod: Code removed for two-player Elite: ---------> \CMP #10 \ If this is the tenth iteration in this block of 32, \BNE MA29 \ do the following, otherwise jump to MA29 to skip the \ \ planet altitude check and move on to the sun distance \ \ check \ --- And replaced by: --------------------------------> CMP #10 \ If this is the tenth iteration in this block of 32, BNE MA23 \ do the following, otherwise jump to MA23 to skip the \ energy checks \ --- End of replacement ------------------------------> IF _SNG45 OR _SOURCE_DISC LDA #50 \ If our energy bank status in ENERGY is >= 50, skip CMP ENERGY \ printing the following message (so the message is BCC P%+6 \ only shown if our energy is low) ASL A \ Print recursive token 100 ("ENERGY LOW{beep}") as an JSR MESS \ in-flight message ELIF _EXECUTIVE LDA #50 \ If our energy bank status in ENERGY is >= 50, skip CMP ENERGY \ printing the following message (so the message is BCC P%+11 \ only shown if our energy is low) ASL A \ Print recursive token 100 ("ENERGY LOW{beep}") as an JSR MESS \ in-flight message LDX #2 \ Call TALK with X = 2 to say "Energy low" using the JSR TALK \ Watford Electronics Beeb Speech Synthesiser (if one \ is fitted and speech has been enabled) ENDIF \ --- Mod: Code added for two-player Elite: -----------> LDA #50 \ If our energy bank status in ENERGY is >= 50, skip CMP player2ENERGY \ printing the following message (so the message is BCC P%+6 \ only shown if our energy is low) ASL A \ Print recursive token 100 ("ENERGY LOW{beep}") as an JSR Player2MESS \ in-flight message \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \LDY #&FF \ Set our altitude in ALTIT to &FF, the maximum \STY ALTIT \ \INY \ Set Y = 0 \ \JSR m \ Call m to calculate the maximum distance to the \ \ planet in any of the three axes, returned in A \ \BNE MA23 \ If A > 0 then we are a fair distance away from the \ \ planet in at least one axis, so jump to MA23 to skip \ \ the rest of the altitude check \ \JSR MAS3 \ Set A = x_hi^2 + y_hi^2 + z_hi^2, so using Pythagoras \ \ we now know that A now contains the square of the \ \ distance between our ship (at the origin) and the \ \ centre of the planet at (x_hi, y_hi, z_hi) \ \BCS MA23 \ If the C flag was set by MAS3, then the result \ \ overflowed (was greater than &FF) and we are still a \ \ fair distance from the planet, so jump to MA23 as we \ \ haven't crashed into the planet \ \SBC #36 \ Subtract 37 from x_hi^2 + y_hi^2 + z_hi^2 \ \ \ \ The SBC subtracts 37 as we just passed through a BCS \ \ so we know the C flag is clear \ \ \ \ When we do the 3D Pythagoras calculation, we only use \ \ the high bytes of the coordinates, so that's x_hi, \ \ y_hi and z_hi and \ \ \ \ The planet radius is (0 96 0), as defined in the \ \ PLANET routine, so the high byte is 96 \ \ \ \ When we square the coordinates above and add them, \ \ the result gets divided by 256 (otherwise the result \ \ wouldn't fit into one byte), so if we do the same for \ \ the planet's radius, we get: \ \ \ \ 96 * 96 / 256 = 36 \ \ \ \ So for the planet, the equivalent figure to test the \ \ sum of the _hi bytes against is 36, so A now contains \ \ the high byte of our altitude above the planet \ \ surface, squared, with an extra 1 subtracted so the \ \ test in the next instruction will ensure we crash \ \ even if we are exactly one planet radius away \ \BCC MA28 \ If A < 0 then jump to MA28 as we have crashed into \ \ the planet \ \STA R \ We are getting close to the planet, so we need to \JSR LL5 \ work out how close. We know from the above that A \ \ contains our altitude squared, so we store A in R \ \ and call LL5 to calculate: \ \ \ \ Q = SQRT(R Q) = SQRT(A Q) \ \ \ \ Interestingly, Q doesn't appear to be set to 0 for \ \ this calculation, so presumably this doesn't make a \ \ difference \ \LDA Q \ Store the result in ALTIT, our altitude \STA ALTIT \ \BNE MA23 \ If our altitude is non-zero then we haven't crashed, \ \ so jump to MA23 to skip to the next section \ \.MA28 \ \JMP DEATH \ If we get here then we just crashed into the planet \ \ or got too close to the sun, so jump to DEATH to start \ \ the funeral preparations and return from the main \ \ flight loop using a tail call \ \.MA29 \ \CMP #15 \ If this is the 15th iteration in this block of 32, \BNE MA33 \ do the following, otherwise jump to MA33 to skip the \ \ docking computer manoeuvring \ \LDA auto \ If auto is zero, then the docking computer is not \BEQ MA23 \ activated, so jump to MA23 to skip to the next \ \ section \ \LDA #123 \ Set A = 123 and jump down to MA34 to print token 123 \BNE MA34 \ ("DOCKING COMPUTERS ON") as an in-flight message \ \.MA33 \ \CMP #20 \ If this is the 20th iteration in this block of 32, \BNE MA23 \ do the following, otherwise jump to MA23 to skip the \ \ sun altitude check \ \LDA #30 \ Set CABTMP to 30, the cabin temperature in deep space \STA CABTMP \ (i.e. one notch on the dashboard bar) \ \LDA SSPR \ If we are inside the space station safe zone, jump to \BNE MA23 \ MA23 to skip the following, as we can't have both the \ \ sun and space station at the same time, so we clearly \ \ can't be flying near the sun \ \LDY #NI% \ Set Y to NI%, which is the offset in K% for the sun's \ \ data block, as the second block at K% is reserved for \ \ the sun (or space station) \ \JSR MAS2 \ Call MAS2 to calculate the largest distance to the \BNE MA23 \ sun in any of the three axes, and if it's non-zero, \ \ jump to MA23 to skip the following, as we are too far \ \ from the sun for scooping or temperature changes \ \JSR MAS3 \ Set (A ?) = x_hi^2 + y_hi^2 + z_hi^2, so using \ \ Pythagoras we now know that A now contains the high \ \ byte of the square of the distance between our ship \ \ (at the origin) and the heart of the sun at coordinate \ \ (x_hi, y_hi, z_hi) \ \ \ \ If the calculation overflows so it doesn't fit into \ \ one byte, then A is set to &FF and the C flag is set \ \EOR #%11111111 \ Invert A, so A is now small if we are far from the \ \ sun and large if we are close to the sun, in the \ \ range 0 = far away to &FF = extremely close, ouch, \ \ hot, hot, hot! \ \ADC #30 \ Add the minimum cabin temperature of 30, plus the C \ \ flag, so we get one of the following: \ \ \ \ * If the MAS3 calculation overflowed then we are a \ \ long way from the sun, A will be zero and the C \ \ flag will be set, so this addition sets A = 31 \ \ and clears the C flag \ \ \ \ * If the result of the MAS3 calculation fitted into \ \ one byte, then A will be in the range 0 to 255 and \ \ the C flag will be clear, so this addition has a \ \ result in the range 0 to 285, with the higher \ \ values overflowing the addition and setting the \ \ C flag \ \ \ \ So the C flag is set if the cabin temperature is too \ \ hot to handle, and if it's clear then A contains the \ \ cabin temperature \ \STA CABTMP \ Store the updated cabin temperature \ \BCS MA28 \ If the C flag is set then jump to MA28 to die, as \ \ our temperature is off the scale \ \CMP #224 \ If the cabin temperature < 224 then jump to MA23 to \BCC MA23 \ skip fuel scooping, as we aren't close enough \ \LDA BST \ If we don't have fuel scoops fitted, jump to BA23 to \BEQ MA23 \ skip fuel scooping, as we can't scoop without fuel \ \ scoops \ \LDA DELT4+1 \ We are now successfully fuel scooping, so it's time \LSR A \ to work out how much fuel we're scooping. Fetch the \ \ high byte of DELT4, which contains our current speed \ \ divided by 4, and halve it to get our current speed \ \ divided by 8 (so it's now a value between 1 and 5, as \ \ our speed is normally between 1 and 40). This gives \ \ us the amount of fuel that's being scooped in A, so \ \ the faster we go, the more fuel we scoop, and because \ \ the fuel levels are stored as 10 * the fuel in light \ \ years, that means we just scooped between 0.1 and 0.5 \ \ light years of free fuel \ \ADC QQ14 \ Set A = A + the current fuel level * 10 (from QQ14) \ \CMP #70 \ If A > 70 then set A = 70 (as 70 is the maximum fuel \BCC P%+4 \ level, or 7.0 light years) \LDA #70 \ \STA QQ14 \ Store the updated fuel level in QQ14 \ \LDA #160 \ Set A to token 160 ("FUEL SCOOPS ON") \ \.MA34 \ \JSR MESS \ Print the token in A as an in-flight message \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Main flight loop (Part 16 of 16) \ Type: Subroutine \ Category: Main loop \ Summary: Process laser pulsing, E.C.M. energy drain, call stardust routine \ Deep dive: Program flow of the main game loop \ \ ------------------------------------------------------------------------------ \ \ The main flight loop covers most of the flight-specific aspects of Elite. This \ section covers the following: \ \ * Process laser pulsing \ \ * Process E.C.M. energy drain \ \ * Jump to the stardust routine if we are in a space view \ \ * Return from the main flight loop \ \ ****************************************************************************** .MA23 LDA LAS2 \ If the current view has no laser, jump to MA16 to skip BEQ MA16 \ the following LDA LASCT \ If LASCT >= 8, jump to MA16 to skip the following, so CMP #8 \ for a pulse laser with a LASCT between 8 and 10, the BCS MA16 \ laser stays on, but for a LASCT of 7 or less it gets \ turned off and stays off until LASCT reaches zero and \ the next pulse can start (if the fire button is still \ being pressed) \ \ For pulse lasers, LASCT gets set to 10 in ma1 above, \ and it decrements every vertical sync (50 times a \ second), so this means it pulses five times a second, \ with the laser being on for the first 3/10 of each \ pulse and off for the rest of the pulse \ \ If this is a beam laser, LASCT is 0 so we always keep \ going here. This means the laser doesn't pulse, but it \ does get drawn and removed every cycle, in a slightly \ different place each time, so the beams still flicker \ around the screen JSR LASLI2 \ Redraw the existing laser lines, which has the effect \ of removing them from the screen LDA #0 \ Set LAS2 to 0 so if this is a pulse laser, it will STA LAS2 \ skip over the above until the next pulse (this has no \ effect if this is a beam laser) .MA16 \ --- Mod: Code added for two-player Elite: -----------> LDA player2LAS2 \ If the current view has no laser, jump to MA16 to skip BEQ MA16a \ the following LDA player2LASCT \ If LASCT >= 8, jump to MA16 to skip the following, so CMP #8 \ for a pulse laser with a LASCT between 8 and 10, the BCS MA16a \ laser stays on, but for a LASCT of 7 or less it gets \ turned off and stays off until LASCT reaches zero and \ the next pulse can start (if the fire button is still \ being pressed) \ \ For pulse lasers, LASCT gets set to 10 in ma1 above, \ and it decrements every vertical sync (50 times a \ second), so this means it pulses five times a second, \ with the laser being on for the first 3/10 of each \ pulse and off for the rest of the pulse \ \ If this is a beam laser, LASCT is 0 so we always keep \ going here. This means the laser doesn't pulse, but it \ does get drawn and removed every cycle, in a slightly \ different place each time, so the beams still flicker \ around the screen JSR Player2LASLI2 \ Redraw the existing laser lines, which has the effect \ of removing them from the screen LDA #0 \ Set LAS2 to 0 so if this is a pulse laser, it will STA player2LAS2 \ skip over the above until the next pulse (this has no \ effect if this is a beam laser) .MA16a \ --- End of added code -------------------------------> LDA ECMP \ If our E.C.M is not on, skip to MA69, otherwise keep BEQ MA69 \ going to drain some energy JSR DENGY \ Call DENGY to deplete our energy banks by 1 BEQ MA70 \ If we have no energy left, jump to MA70 to turn our \ E.C.M. off .MA69 LDA ECMA \ If an E.C.M is going off (ours or an opponent's) then BEQ MA66 \ keep going, otherwise skip to MA66 DEC ECMA \ Decrement the E.C.M. countdown timer, and if it has BNE MA66 \ reached zero, keep going, otherwise skip to MA66 .MA70 JSR ECMOF \ If we get here then either we have either run out of \ energy, or the E.C.M. timer has run down, so switch \ off the E.C.M. .MA66 \ --- Mod: Code added for two-player Elite: -----------> LDA player2ECMP \ If our E.C.M is not on, skip to MA69, otherwise keep BEQ MA69a \ going to drain some energy JSR Player2DENGY \ Call DENGY to deplete our energy banks by 1 BEQ MA70a \ If we have no energy left, jump to MA70 to turn our \ E.C.M. off .MA69a LDA player2ECMA \ If an E.C.M is going off (ours or an opponent's) then BEQ MA66a \ keep going, otherwise skip to MA66 DEC player2ECMA \ Decrement the E.C.M. countdown timer, and if it has BNE MA66a \ reached zero, keep going, otherwise skip to MA66 .MA70a JSR Player2ECMOF \ If we get here then either we have either run out of \ energy, or the E.C.M. timer has run down, so switch \ off the E.C.M. .MA66a \ --- End of added code -------------------------------> LDA QQ11 \ If this is not a space view (i.e. QQ11 is non-zero) BNE oh \ then jump to oh to return from the main flight loop \ (as oh is an RTS) JSR STARS \ This is a space view, so call the STARS routine to \ process the stardust JMP PBFL \ And call PBFL to ask the I/O processor to draw the \ dust particles, returning from the main flight loop \ using a tail call \ ****************************************************************************** \ \ Name: SPIN \ Type: Subroutine \ Category: Universe \ Summary: Randomly spawn cargo from a destroyed ship \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ Y The type of cargo to consider spawning (typically #PLT \ or #OIL) \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ oh Contains an RTS \ \ SPIN2 Remove any randomness: spawn cargo of a specific type \ (given in X), and always spawn the number given in A \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.SPIN \ \JSR DORND \ Fetch a random number, and jump to oh if it is \BPL oh \ positive (50% chance) \ \TYA \ Copy the cargo type from Y into A and X \TAX \ \LDY #0 \ Fetch the first byte of the hit ship's blueprint, \AND (XX0),Y \ which determines the maximum number of bits of \ \ debris shown when the ship is destroyed, and AND \ \ with the random number we just fetched \ \AND #15 \ Reduce the random number in A to the range 0-15 \ \.SPIN2 \ \STA CNT \ Store the result in CNT, so CNT contains a random \ \ number between 0 and the maximum number of bits of \ \ debris that this ship will release when destroyed \ \ (to a maximum of 15 bits of debris) \ \.spl \ \BEQ oh \ We're going to go round a loop using CNT as a counter \ \ so this checks whether the counter is zero and jumps \ \ to oh when it gets there (which might be straight \ \ away) \ \LDA #0 \ Call SFS1 to spawn the specified cargo from the now \JSR SFS1 \ deceased parent ship, giving the spawned canister an \ \ AI flag of 0 (no AI, zero aggression, no E.C.M.) \ \DEC CNT \ Decrease the loop counter \ \BNE spl+2 \ Jump back up to the LDA &0 instruction above (this BPL \ \ is effectively a JMP as CNT will never be negative) \ --- End of removed code -----------------------------> .oh RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MT27 \ Type: Subroutine \ Category: Text \ Summary: Print the captain's name during mission briefings \ Deep dive: Extended text tokens \ The Constrictor mission \ \ ------------------------------------------------------------------------------ \ \ This routine prints the following tokens, depending on the galaxy number: \ \ * Token 217 ("CURRUTHERS") in galaxy 0 \ \ * Token 218 ("FOSDYKE SMYTHE") in galaxy 1 \ \ * Token 219 ("FORTESQUE") in galaxy 2 \ \ This is used when printing extended token 213 as part of the mission \ briefings, which looks like this when printed: \ \ Commander {commander name}, I am Captain {mission captain's name} of Her \ Majesty's Space Navy \ \ where {mission captain's name} is replaced by one of the names above. \ \ ****************************************************************************** .MT27 LDA #217 \ Set A = 217, so when we fall through into MT28, the \ 217 gets added to the current galaxy number, so the \ extended token that is printed is 217-219 (as this is \ only called in galaxies 0 through 2) EQUB &2C \ Skip the next instruction by turning it into \ &2C &A9 &DC, or BIT &DCA9, which does nothing apart \ from affect the flags \ ****************************************************************************** \ \ Name: MT28 \ Type: Subroutine \ Category: Text \ Summary: Print the location hint during the mission 1 briefing \ Deep dive: Extended text tokens \ The Constrictor mission \ \ ------------------------------------------------------------------------------ \ \ This routine prints the following tokens, depending on the galaxy number: \ \ * Token 220 ("WAS LAST SEEN AT {single cap}REESDICE") in galaxy 0 \ \ * Token 221 ("IS BELIEVED TO HAVE JUMPED TO THIS GALAXY") in galaxy 1 \ \ This is used when printing extended token 10 as part of the mission 1 \ briefing, which looks like this when printed: \ \ It went missing from our ship yard on Xeer five months ago and {mission 1 \ location hint} \ \ where {mission 1 location hint} is replaced by one of the names above. \ \ ****************************************************************************** .MT28 LDA #220 \ Set A = galaxy number in GCNT + 220, which is in the CLC \ range 220-221, as this is only called in galaxies 0 ADC GCNT \ and 1 BNE DETOK \ Jump to DETOK to print extended token 220-221, \ returning from the subroutine using a tail call (this \ BNE is effectively a JMP as A is never zero) \ ****************************************************************************** \ \ Name: DETOK3 \ Type: Subroutine \ Category: Text \ Summary: Print an extended recursive token from the RUTOK token table \ Deep dive: Extended system descriptions \ Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The recursive token to be printed, in the range 0-255 \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A A is preserved \ \ Y Y is preserved \ \ V(1 0) V(1 0) is preserved \ \ ****************************************************************************** .DETOK3 PHA \ Store A on the stack, so we can retrieve it later TAX \ Copy the token number from A into X TYA \ Store Y on the stack PHA LDA V \ Store V(1 0) on the stack PHA LDA V+1 PHA LDA #LO(RUTOK) \ Set V to the low byte of RUTOK STA V LDA #HI(RUTOK) \ Set A to the high byte of RUTOK BNE DTEN \ Call DTEN to print token number X from the RUTOK \ table and restore the values of A, Y and V(1 0) from \ the stack, returning from the subroutine using a tail \ call (this BNE is effectively a JMP as A is never \ zero) \ ****************************************************************************** \ \ Name: DETOK \ Type: Subroutine \ Category: Text \ Summary: Print an extended recursive token from the TKN1 token table \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The recursive token to be printed, in the range 1-255 \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A A is preserved \ \ Y Y is preserved \ \ V(1 0) V(1 0) is preserved \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ DTEN Print recursive token number X from the token table \ pointed to by (A V), used to print tokens from the RUTOK \ table via calls to DETOK3 \ \ ****************************************************************************** .DETOK PHA \ Store A on the stack, so we can retrieve it later TAX \ Copy the token number from A into X TYA \ Store Y on the stack PHA LDA V \ Store V(1 0) on the stack PHA LDA V+1 PHA LDA #LO(TKN1) \ Set V to the low byte of TKN1 STA V LDA #HI(TKN1) \ Set A to the high byte of TKN1, so when we fall \ through into DTEN, V(1 0) gets set to the address of \ the TKN1 token table .DTEN STA V+1 \ Set the high byte of V(1 0) to A, so V(1 0) now points \ to the start of the token table to use LDY #0 \ First, we need to work our way through the table until \ we get to the token that we want to print. Tokens are \ delimited by #VE, and VE EOR VE = 0, so we work our \ way through the table in, counting #VE delimiters \ until we have passed X of them, at which point we jump \ down to DTL2 to do the actual printing. So first, we \ set a counter Y to point to the character offset as we \ scan through the table .DTL1 LDA (V),Y \ Load the character at offset Y in the token table, \ which is the next character from the token table EOR #VE \ Tokens are stored in memory having been EOR'd with \ #VE, so we repeat the EOR to get the actual character \ in this token BNE DT1 \ If the result is non-zero, then this is a character \ in a token rather than the delimiter (which is #VE), \ so jump to DT1 DEX \ We have just scanned the end of a token, so decrement \ X, which contains the token number we are looking for BEQ DTL2 \ If X has now reached zero then we have found the token \ we are looking for, so jump down to DTL2 to print it .DT1 INY \ Otherwise this isn't the token we are looking for, so \ increment the character pointer BNE DTL1 \ If Y hasn't just wrapped around to 0, loop back to \ DTL1 to process the next character INC V+1 \ We have just crossed into a new page, so increment \ V+1 so that V points to the start of the new page BNE DTL1 \ Jump back to DTL1 to process the next character (this \ BNE is effectively a JMP as V+1 won't reach zero \ before we reach the end of the token table) .DTL2 INY \ We just detected the delimiter byte before the token \ that we want to print, so increment the character \ pointer to point to the first character of the token, \ rather than the delimiter BNE P%+4 \ If Y hasn't just wrapped around to 0, skip the next \ instruction INC V+1 \ We have just crossed into a new page, so increment \ V+1 so that V points to the start of the new page LDA (V),Y \ Load the character at offset Y in the token table, \ which is the next character from the token we want to \ print EOR #VE \ Tokens are stored in memory having been EOR'd with \ #VE, so we repeat the EOR to get the actual character \ in this token BEQ DTEX \ If the result is zero, then this is the delimiter at \ the end of the token to print (which is #VE), so jump \ to DTEX to return from the subroutine, as we are done \ printing JSR DETOK2 \ Otherwise call DETOK2 to print this part of the token JMP DTL2 \ Jump back to DTL2 to process the next character .DTEX PLA \ Restore V(1 0) from the stack, so it is preserved STA V+1 \ through calls to this routine PLA STA V PLA \ Restore Y from the stack, so it is preserved through TAY \ calls to this routine PLA \ Restore A from the stack, so it is preserved through \ calls to this routine RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: DETOK2 \ Type: Subroutine \ Category: Text \ Summary: Print an extended text token (1-255) \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The token to be printed (1-255) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A A is preserved \ \ Y Y is preserved \ \ V(1 0) V(1 0) is preserved \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ DTS Print a single letter in the correct case \ \ ****************************************************************************** .DETOK2 CMP #32 \ If A < 32 then this is a jump token, so skip to DT3 to BCC DT3 \ process it BIT DTW3 \ If bit 7 of DTW3 is clear, then extended tokens are BPL DT8 \ enabled, so jump to DT8 to process them \ If we get there then this is not a jump token and \ extended tokens are not enabled, so we can call the \ standard text token routine at TT27 to print the token TAX \ Copy the token number from A into X TYA \ Store Y on the stack PHA LDA V \ Store V(1 0) on the stack PHA LDA V+1 PHA TXA \ Copy the token number from X back into A JSR TT27 \ Call TT27 to print the text token JMP DT7 \ Jump to DT7 to restore V(1 0) and Y from the stack and \ return from the subroutine .DT8 \ If we get here then this is not a jump token and \ extended tokens are enabled CMP #'[' \ If A < ASCII "[" (i.e. A <= ASCII "Z", or 90) then BCC DTS \ this is a printable ASCII character, so jump down to \ DTS to print it CMP #129 \ If A < 129, so A is in the range 91-128, jump down to BCC DT6 \ DT6 to print a randomised token from the MTIN table CMP #215 \ If A < 215, so A is in the range 129-214, jump to BCC DETOK \ DETOK as this is a recursive token, returning from the \ subroutine using a tail call \ If we get here then A >= 215, so this is a two-letter \ token from the extended TKN2/QQ16 table SBC #215 \ Subtract 215 to get a token number in the range 0-12 \ (the C flag is set as we passed through the BCC above, \ so this subtraction is correct) ASL A \ Set A = A * 2, so it can be used as a pointer into the \ two-letter token tables at TKN2 and QQ16 PHA \ Store A on the stack, so we can restore it for the \ second letter below TAX \ Fetch the first letter of the two-letter token from LDA TKN2,X \ TKN2, which is at TKN2 + X JSR DTS \ Call DTS to print it PLA \ Restore A from the stack and transfer it into X TAX LDA TKN2+1,X \ Fetch the second letter of the two-letter token from \ TKN2, which is at TKN2 + X + 1, and fall through into \ DTS to print it .DTS CMP #'A' \ If A < ASCII "A", jump to DT9 to print this as ASCII BCC DT9 BIT DTW6 \ If bit 7 of DTW6 is set, then lower case has been BMI DT10 \ enabled by jump token 13, {lower case}, so jump to \ DT10 to apply the lower case and single cap masks BIT DTW2 \ If bit 7 of DTW2 is set, then we are not currently BMI DT5 \ printing a word, so jump to DT5 so we skip the setting \ of lower case in Sentence Case (which we only want to \ do when we are already printing a word) .DT10 ORA DTW1 \ Convert the character to lower case if DTW1 is \ %00100000 (i.e. if we are in {sentence case} mode) .DT5 AND DTW8 \ Convert the character to upper case if DTW8 is \ %11011111 (i.e. after a {single cap} token) .DT9 JMP DASC \ Jump to DASC to print the ASCII character in A, \ returning from the routine using a tail call .DT3 \ If we get here then the token number in A is in the \ range 1 to 32, so this is a jump token that should \ call the corresponding address in the jump table at \ JMTB TAX \ Copy the token number from A into X TYA \ Store Y on the stack PHA LDA V \ Store V(1 0) on the stack PHA LDA V+1 PHA TXA \ Copy the token number from X back into A ASL A \ Set A = A * 2, so it can be used as a pointer into the \ jump table at JMTB, though because the original range \ of values is 1-32, so the doubled range is 2-64, we \ need to take the offset into the jump table from \ JMTB-2 rather than JMTB TAX \ Copy the doubled token number from A into X LDA JMTB-2,X \ Set DTM(2 1) to the X-th address from the table at STA DTM+1 \ JTM-2, which modifies the JSR DASC instruction at LDA JMTB-1,X \ label DTM below so that it calls the subroutine at the STA DTM+2 \ relevant address from the JMTB table TXA \ Copy the doubled token number from X back into A LSR A \ Halve A to get the original token number .DTM JSR DASC \ Call the relevant JMTB subroutine, as this instruction \ will have been modified by the above to point to the \ relevant address .DT7 PLA \ Restore V(1 0) from the stack, so it is preserved STA V+1 \ through calls to this routine PLA STA V PLA \ Restore Y from the stack, so it is preserved through TAY \ calls to this routine RTS \ Return from the subroutine .DT6 \ If we get here then the token number in A is in the \ range 91-128, which means we print a randomly picked \ token from the token range given in the corresponding \ entry in the MTIN table STA SC \ Store the token number in SC TYA \ Store Y on the stack PHA LDA V \ Store V(1 0) on the stack PHA LDA V+1 PHA JSR DORND \ Set X to a random number TAX LDA #0 \ Set A to 0, so we can build a random number from 0 to \ 4 in A plus the C flag, with each number being equally \ likely CPX #51 \ Add 1 to A if X >= 51 ADC #0 CPX #102 \ Add 1 to A if X >= 102 ADC #0 CPX #153 \ Add 1 to A if X >= 153 ADC #0 CPX #204 \ Set the C flag if X >= 204 LDX SC \ Fetch the token number from SC into X, so X is now in \ the range 91-128 ADC MTIN-91,X \ Set A = MTIN-91 + token number (91-128) + random (0-4) \ = MTIN + token number (0-37) + random (0-4) JSR DETOK \ Call DETOK to print the extended recursive token in A JMP DT7 \ Jump to DT7 to restore V(1 0) and Y from the stack and \ return from the subroutine using a tail call \ ****************************************************************************** \ \ Name: Firebird \ Type: Variable \ Category: Copy protection \ Summary: The name "Firebird", buried in the code of the Executive version \ \ ****************************************************************************** IF _EXECUTIVE EQUS "Firebird" EQUB 13 ENDIF \ ****************************************************************************** \ \ Name: MT1 \ Type: Subroutine \ Category: Text \ Summary: Switch to ALL CAPS when printing extended tokens \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This routine sets the following: \ \ * DTW1 = %00000000 (do not change case to lower case) \ \ * DTW6 = %00000000 (lower case is not enabled) \ \ ****************************************************************************** .MT1 LDA #%00000000 \ Set A = %00000000, so when we fall through into MT2, \ both DTW1 and DTW6 get set to %00000000 EQUB &2C \ Skip the next instruction by turning it into \ &2C &A9 &20, or BIT &20A9, which does nothing apart \ from affect the flags \ ****************************************************************************** \ \ Name: MT2 \ Type: Subroutine \ Category: Text \ Summary: Switch to Sentence Case when printing extended tokens \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This routine sets the following: \ \ * DTW1 = %00100000 (apply lower case to the second letter of a word onwards) \ \ * DTW6 = %00000000 (lower case is not enabled) \ \ ****************************************************************************** .MT2 LDA #%00100000 \ Set DTW1 = %00100000 STA DTW1 LDA #00000000 \ Set DTW6 = %00000000 STA DTW6 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MT8 \ Type: Subroutine \ Category: Text \ Summary: Tab to column 6 and start a new word when printing extended tokens \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This routine sets the following: \ \ * XC = 6 (tab to column 6) \ \ * DTW2 = %11111111 (we are not currently printing a word) \ \ ****************************************************************************** .MT8 LDA #6 \ Move the text cursor to column 6 JSR DOXC LDA #%11111111 \ Set all the bits in DTW2 STA DTW2 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MT9 \ Type: Subroutine \ Category: Text \ Summary: Clear the screen and set the current view type to 1 \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This routine sets the following: \ \ * XC = 1 (tab to column 1) \ \ before calling TT66 to clear the screen and set the view type to 1. \ \ ****************************************************************************** .MT9 LDA #1 \ Call DOXC to move the text cursor to column 1 JSR DOXC JMP TT66 \ Jump to TT66 to clear the screen and set the current \ view type to 1, returning from the subroutine using a \ tail call \ ****************************************************************************** \ \ Name: MT13 \ Type: Subroutine \ Category: Text \ Summary: Switch to lower case when printing extended tokens \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This routine sets the following: \ \ * DTW1 = %00100000 (apply lower case to the second letter of a word onwards) \ \ * DTW6 = %10000000 (lower case is enabled) \ \ ****************************************************************************** .MT13 LDA #%10000000 \ Set DTW6 = %10000000 STA DTW6 LDA #%00100000 \ Set DTW1 = %00100000 STA DTW1 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MT6 \ Type: Subroutine \ Category: Text \ Summary: Switch to standard tokens in Sentence Case \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This routine sets the following: \ \ * QQ17 = %10000000 (set Sentence Case for standard tokens) \ \ * DTW3 = %11111111 (print standard tokens) \ \ ****************************************************************************** .MT6 LDA #%10000000 \ Set bit 7 of QQ17 to switch standard tokens to STA QQ17 \ Sentence Case LDA #%11111111 \ Set A = %11111111, so when we fall through into MT5, \ DTW3 gets set to %11111111 and calls to DETOK print \ standard tokens EQUB &2C \ Skip the next instruction by turning it into \ &2C &A9 &00, or BIT &00A9, which does nothing apart \ from affect the flags \ ****************************************************************************** \ \ Name: MT5 \ Type: Subroutine \ Category: Text \ Summary: Switch to extended tokens \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This routine sets the following: \ \ * DTW3 = %00000000 (print extended tokens) \ \ ****************************************************************************** .MT5 LDA #%00000000 \ Set DTW3 = %00000000, so that calls to DETOK print STA DTW3 \ extended tokens RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MT14 \ Type: Subroutine \ Category: Text \ Summary: Switch to justified text when printing extended tokens \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This routine sets the following: \ \ * DTW4 = %10000000 (justify text, print buffer on carriage return) \ \ * DTW5 = 0 (reset line buffer size) \ \ ****************************************************************************** .MT14 LDA #%10000000 \ Set A = %10000000, so when we fall through into MT15, \ DTW4 gets set to %10000000 EQUB &2C \ Skip the next instruction by turning it into \ &2C &A9 &00, or BIT &00A9, which does nothing apart \ from affect the flags \ ****************************************************************************** \ \ Name: MT15 \ Type: Subroutine \ Category: Text \ Summary: Switch to left-aligned text when printing extended tokens \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This routine sets the following: \ \ * DTW4 = %00000000 (do not justify text, print buffer on carriage return) \ \ * DTW5 = 0 (reset line buffer size) \ \ ****************************************************************************** .MT15 LDA #0 \ Set DTW4 = %00000000 STA DTW4 ASL A \ Set DTW5 = 0 (even when we fall through from MT14 with STA DTW5 \ A set to %10000000) RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MT17 \ Type: Subroutine \ Category: Text \ Summary: Print the selected system's adjective, e.g. Lavian for Lave \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ The adjective for the current system is generated by taking the system name, \ removing the last character if it is a vowel, and adding "-ian" to the end, \ so: \ \ * Lave gives Lavian (as in "Lavian tree grub") \ \ * Leesti gives Leestian (as in "Leestian Evil Juice") \ \ This routine is called by jump token 17, {system name adjective}, and it can \ only be used when justified text is being printed - i.e. following jump token \ 14, {justify} - because the routine needs to use the line buffer to work. \ \ ****************************************************************************** .MT17 LDA QQ17 \ Set QQ17 = %10111111 to switch to Sentence Case AND #%10111111 STA QQ17 LDA #3 \ Print control code 3 (selected system name) into the JSR TT27 \ line buffer LDX DTW5 \ Load the last character of the line buffer BUF into A LDA BUF-1,X \ (as DTW5 contains the buffer size, so character DTW5-1 \ is the last character in the buffer BUF) JSR VOWEL \ Test whether the character is a vowel, in which case \ this will set the C flag BCC MT171 \ If the character is not a vowel, skip the following \ instruction DEC DTW5 \ The character is a vowel, so decrement DTW5, which \ removes the last character from the line buffer (i.e. \ it removes the trailing vowel from the system name) .MT171 LDA #153 \ Print extended token 153 ("IAN"), returning from the JMP DETOK \ subroutine using a tail call \ ****************************************************************************** \ \ Name: MT18 \ Type: Subroutine \ Category: Text \ Summary: Print a random 1-8 letter word in Sentence Case \ Deep dive: Extended text tokens \ \ ****************************************************************************** .MT18 JSR MT19 \ Call MT19 to capitalise the next letter (i.e. set \ Sentence Case for this word only) JSR DORND \ Set A and X to random numbers and reduce A to a AND #3 \ random number in the range 0-3 TAY \ Copy the random number into Y, so we can use Y as a \ loop counter to print 1-4 words (i.e. Y+1 words) .MT18L JSR DORND \ Set A and X to random numbers and reduce A to an even AND #62 \ random number in the range 0-62 (as bit 0 of 62 is 0) TAX \ Copy the random number into X, so X contains the table \ offset of a random extended two-letter token from 0-31 \ which we can now use to pick a token from the combined \ tables at TKN2+2 and QQ16 (we intentionally exclude \ the first token in TKN2, which contains a newline) LDA TKN2+2,X \ Print the first letter of the token at TKN2+2 + X JSR DTS LDA TKN2+3,X \ Print the second letter of the token at TKN2+2 + X JSR DTS DEY \ Decrement the loop counter BPL MT18L \ Loop back to MT18L to print another two-letter token \ until we have printed Y+1 of them RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MT19 \ Type: Subroutine \ Category: Text \ Summary: Capitalise the next letter \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This routine sets the following: \ \ * DTW8 = %11011111 (capitalise the next letter) \ \ ****************************************************************************** .MT19 LDA #%11011111 \ Set DTW8 = %11011111 STA DTW8 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: VOWEL \ Type: Subroutine \ Category: Text \ Summary: Test whether a character is a vowel \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The character to be tested \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag The C flag is set if the character is a vowel, otherwise \ it is clear \ \ ****************************************************************************** .VOWEL ORA #%00100000 \ Set bit 5 of the character to make it lower case CMP #'a' \ If the letter is a vowel, jump to VRTS to return from BEQ VRTS \ the subroutine with the C flag set (as the CMP will CMP #'e' \ set the C flag if the comparison is equal) BEQ VRTS CMP #'i' BEQ VRTS CMP #'o' BEQ VRTS CMP #'u' BEQ VRTS CLC \ The character is not a vowel, so clear the C flag .VRTS RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: WHITETEXT \ Type: Subroutine \ Category: Text \ Summary: Switch to white text \ \ ****************************************************************************** .WHITETEXT LDA #32 \ Send a #SETVDU19 32 command to the I/O processor to JSR DOVDU19 \ switch to the mode 1 palette for the title screen, \ which is yellow (colour 1), white (colour 2) and cyan \ (colour 3) LDA #RED \ Send a #SETCOL RED command to the I/O processor to JMP DOCOL \ switch to colour 2, which is white in the title \ screen, and return from the subroutine using a tail \ call \ ****************************************************************************** \ \ Name: JMTB \ Type: Variable \ Category: Text \ Summary: The extended token table for jump tokens 1-32 (DETOK) \ Deep dive: Extended text tokens \ \ ****************************************************************************** .JMTB EQUW MT1 \ Token 1: Switch to ALL CAPS EQUW MT2 \ Token 2: Switch to Sentence Case EQUW TT27 \ Token 3: Print the selected system name EQUW TT27 \ Token 4: Print the commander's name EQUW MT5 \ Token 5: Switch to extended tokens EQUW MT6 \ Token 6: Switch to standard tokens, in Sentence Case EQUW DASC \ Token 7: Beep EQUW MT8 \ Token 8: Tab to column 6 EQUW MT9 \ Token 9: Clear screen, tab to column 1, view type = 1 EQUW DASC \ Token 10: Line feed EQUW NLIN4 \ Token 11: Draw box around title (line at pixel row 19) EQUW DASC \ Token 12: Carriage return EQUW MT13 \ Token 13: Switch to lower case EQUW MT14 \ Token 14: Switch to justified text EQUW MT15 \ Token 15: Switch to left-aligned text EQUW MT16 \ Token 16: Print the character in DTW7 (drive number) EQUW MT17 \ Token 17: Print system name adjective in Sentence Case EQUW MT18 \ Token 18: Randomly print 1 to 4 two-letter tokens EQUW MT19 \ Token 19: Capitalise first letter of next word only EQUW DASC \ Token 20: Unused EQUW CLYNS \ Token 21: Clear the bottom few lines of the space view EQUW PAUSE \ Token 22: Display ship and wait for key press EQUW MT23 \ Token 23: Move to row 10, white text, set lower case EQUW PAUSE2 \ Token 24: Wait for a key press EQUW BRIS \ Token 25: Show incoming message screen, wait 2 seconds EQUW MT26 \ Token 26: Fetch line input from keyboard (filename) EQUW MT27 \ Token 27: Print mission captain's name (217-219) EQUW MT28 \ Token 28: Print mission 1 location hint (220-221) EQUW MT29 \ Token 29: Column 6, white text, lower case in words EQUW WHITETEXT \ Token 30: White text EQUW DASC \ Token 31: Unused EQUW DASC \ Token 32: Unused \ ****************************************************************************** \ \ Name: TKN2 \ Type: Variable \ Category: Text \ Summary: The extended two-letter token lookup table \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ Two-letter token lookup table for extended tokens 215-227. \ \ ****************************************************************************** .TKN2 EQUB 12, 10 \ Token 215 = {crlf} EQUS "AB" \ Token 216 EQUS "OU" \ Token 217 EQUS "SE" \ Token 218 EQUS "IT" \ Token 219 EQUS "IL" \ Token 220 EQUS "ET" \ Token 221 EQUS "ST" \ Token 222 EQUS "ON" \ Token 223 EQUS "LO" \ Token 224 EQUS "NU" \ Token 225 EQUS "TH" \ Token 226 EQUS "NO" \ Token 227 \ ****************************************************************************** \ \ Name: QQ16 \ Type: Variable \ Category: Text \ Summary: The two-letter token lookup table \ Deep dive: Printing text tokens \ \ ------------------------------------------------------------------------------ \ \ Two-letter token lookup table for tokens 128-159. \ \ These two-letter tokens can also be used in the extended text token system, by \ adding 100 to the token number. So the extended two-letter token 228 is "AL", \ the same as the standard two-letter token 128. In this system, the last four \ tokens are not available, as they would have numbers greater than 255. \ \ ****************************************************************************** .QQ16 EQUS "AL" \ Token 128 EQUS "LE" \ Token 129 EQUS "XE" \ Token 130 EQUS "GE" \ Token 131 EQUS "ZA" \ Token 132 EQUS "CE" \ Token 133 EQUS "BI" \ Token 134 EQUS "SO" \ Token 135 EQUS "US" \ Token 136 EQUS "ES" \ Token 137 EQUS "AR" \ Token 138 EQUS "MA" \ Token 139 EQUS "IN" \ Token 140 EQUS "DI" \ Token 141 EQUS "RE" \ Token 142 EQUS "A?" \ Token 143 EQUS "ER" \ Token 144 EQUS "AT" \ Token 145 EQUS "EN" \ Token 146 EQUS "BE" \ Token 147 EQUS "RA" \ Token 148 EQUS "LA" \ Token 149 EQUS "VE" \ Token 150 EQUS "TI" \ Token 151 EQUS "ED" \ Token 152 EQUS "OR" \ Token 153 EQUS "QU" \ Token 154 EQUS "AN" \ Token 155 EQUS "TE" \ Token 156 EQUS "IS" \ Token 157 EQUS "RI" \ Token 158 EQUS "ON" \ Token 159 \ ****************************************************************************** \ \ Name: shpcol \ Type: Variable \ Category: Drawing ships \ Summary: Ship colours \ \ ****************************************************************************** .shpcol EQUB 0 EQUB YELLOW \ Missile EQUB CYAN \ Coriolis space station EQUB CYAN \ Escape pod EQUB CYAN \ Alloy plate EQUB CYAN \ Cargo canister EQUB RED \ Boulder EQUB RED \ Asteroid EQUB RED \ Splinter EQUB CYAN \ Shuttle EQUB CYAN \ Transporter EQUB CYAN \ Cobra Mk III EQUB CYAN \ Python EQUB CYAN \ Boa EQUB CYAN \ Anaconda EQUB RED \ Rock hermit (asteroid) EQUB CYAN \ Viper EQUB CYAN \ Sidewinder EQUB CYAN \ Mamba EQUB CYAN \ Krait EQUB CYAN \ Adder EQUB CYAN \ Gecko EQUB CYAN \ Cobra Mk I EQUB CYAN \ Worm EQUB CYAN \ Cobra Mk III (pirate) EQUB CYAN \ Asp Mk II EQUB CYAN \ Python (pirate) EQUB CYAN \ Fer-de-lance EQUB %11001001 \ Moray (colour 3, 2, 0, 1 = cyan/red/black/yellow) EQUB WHITE \ Thargoid EQUB WHITE \ Thargon EQUB CYAN \ Constrictor EQUB CYAN \ The Elite logo EQUB CYAN \ Cougar \ ****************************************************************************** \ \ Name: scacol \ Type: Variable \ Category: Drawing ships \ Summary: Ship colours on the scanner \ Deep dive: The elusive Cougar \ \ ****************************************************************************** .scacol EQUB 0 \ This byte appears to be unused EQUB YELLOW2 \ Missile EQUB GREEN2 \ Coriolis space station EQUB BLUE2 \ Escape pod EQUB BLUE2 \ Alloy plate EQUB BLUE2 \ Cargo canister EQUB RED2 \ Boulder EQUB RED2 \ Asteroid EQUB RED2 \ Splinter EQUB CYAN2 \ Shuttle EQUB CYAN2 \ Transporter EQUB CYAN2 \ Cobra Mk III EQUB MAG2 \ Python EQUB MAG2 \ Boa EQUB MAG2 \ Anaconda EQUB RED2 \ Rock hermit (asteroid) EQUB CYAN2 \ Viper EQUB CYAN2 \ Sidewinder EQUB CYAN2 \ Mamba EQUB CYAN2 \ Krait EQUB CYAN2 \ Adder EQUB CYAN2 \ Gecko EQUB CYAN2 \ Cobra Mk I EQUB BLUE2 \ Worm EQUB CYAN2 \ Cobra Mk III (pirate) EQUB CYAN2 \ Asp Mk II EQUB MAG2 \ Python (pirate) EQUB CYAN2 \ Fer-de-lance EQUB CYAN2 \ Moray EQUB WHITE2 \ Thargoid EQUB CYAN2 \ Thargon EQUB CYAN2 \ Constrictor EQUB 0 \ The Elite logo EQUB CYAN2 \ Cougar EQUD 0 \ These bytes appear to be unused \ ****************************************************************************** \ \ Name: LSX2 \ Type: Variable \ Category: Drawing lines \ Summary: The ball line heap for storing x-coordinates \ Deep dive: The ball line heap \ \ ****************************************************************************** .LSX2 \ --- Mod: Code removed for two-player Elite: ---------> \IF _MATCH_ORIGINAL_BINARIES \ \IF _SNG45 \ \ EQUB &16, &01, &0E, &77, &1F, &16, &AD, &77 \ These bytes appear to be \ EQUB &A0, &A1, &77, &1C, &12, &12, &07, &94 \ unused and just contain random \ EQUB &C4, &44, &B5, &B9, &10, &18, &1E, &13 \ workspace noise left over from \ EQUB &04, &77, &18, &11, &11, &77, &E4, &05 \ the BBC Micro assembly process \ EQUB &77, &16, &04, &04, &77, &8E, &03, &77 \ EQUB &A7, &77, &13, &12, &12, &07, &77, &04 \ EQUB &07, &16, &BE, &77, &11, &AA, &77, &B8 \ EQUB &19, &0E, &77, &0E, &12, &B9, &04, &77 \ EQUB &B4, &00, &79, &77, &44, &00, &12, &1B \ EQUB &1B, &77, &C4, &04, &8C, &02, &16, &AC \ EQUB &88, &77, &1F, &16, &04, &77, &14, &1F \ EQUB &A8, &10, &AB, &9B, &8E, &05, &77, &15 \ EQUB &18, &0E, &04, &77, &B9, &12, &77, &A5 \ EQUB &16, &13, &0E, &77, &11, &AA, &87, &07 \ EQUB &02, &04, &1F, &77, &05, &1E, &10, &1F \ EQUB &03, &9E, &C4, &1F, &18, &1A, &12, &77 \ EQUB &04, &0E, &04, &03, &12, &1A, &77, &18 \ EQUB &11, &77, &B5, &18, &8D, &77, &1A, &18 \ EQUB &B5, &A3, &04, &9B, &4F, &5E, &49, &4A \ EQUB &1E, &5A, &77, &1F, &16, &AD, &77, &18 \ EQUB &15, &03, &16, &A7, &93, &C4, &13, &12 \ EQUB &11, &A1, &BE, &77, &07, &AE, &19, &04 \ EQUB &77, &11, &AA, &77, &B5, &12, &1E, &05 \ EQUB &77, &44, &1F, &1E, &AD, &77, &44, &00 \ EQUB &AA, &1B, &13, &04, &9B, &C4, &A0, &8A \ EQUB &B2, &04, &77, &1C, &B4, &00, &77, &00 \ EQUB &12, &70, &AD, &77, &10, &18, &03, &77 \ EQUB &BC, &1A, &12, &B5, &94, &15, &02, &03 \ EQUB &77, &B4, &03, &77, &00, &1F, &A2, &9B \ EQUB &1E, &11, &77, &44, &1E, &77, &03, &AF \ EQUB &19, &04, &1A, &8C, &77, &C4, &07, &AE \ EQUB &19, &04, &9E, &8E, &05, &77, &15, &16 \ \ELIF _EXECUTIVE \ \ EQUB &C4, &44, &B5, &B9, &10, &18, &1E, &13 \ These bytes appear to be \ EQUB &04, &77, &18, &11, &11, &77, &E4, &05 \ unused and just contain random \ EQUB &77, &16, &04, &04, &77, &8E, &03, &77 \ workspace noise left over from \ EQUB &A7, &77, &13, &12, &12, &07, &77, &04 \ the BBC Micro assembly process \ EQUB &07, &16, &BE, &77, &11, &AA, &77, &B8 \ EQUB &19, &0E, &77, &0E, &12, &B9, &04, &77 \ EQUB &B4, &00, &79, &77, &44, &00, &12, &1B \ EQUB &1B, &77, &C4, &04, &8C, &02, &16, &AC \ EQUB &88, &77, &1F, &16, &04, &77, &14, &1F \ EQUB &A8, &10, &AB, &9B, &8E, &05, &77, &15 \ EQUB &18, &0E, &04, &77, &B9, &12, &77, &A5 \ EQUB &16, &13, &0E, &77, &11, &AA, &87, &07 \ EQUB &02, &04, &1F, &77, &05, &1E, &10, &1F \ EQUB &03, &9E, &C4, &1F, &18, &1A, &12, &77 \ EQUB &04, &0E, &04, &03, &12, &1A, &77, &18 \ EQUB &11, &77, &B5, &18, &8D, &77, &1A, &18 \ EQUB &B5, &A3, &04, &9B, &4F, &5E, &49, &4A \ EQUB &1E, &5A, &77, &1F, &16, &AD, &77, &18 \ EQUB &15, &03, &16, &A7, &93, &C4, &13, &12 \ EQUB &11, &A1, &BE, &77, &07, &AE, &19, &04 \ EQUB &77, &11, &AA, &77, &B5, &12, &1E, &05 \ EQUB &77, &44, &1F, &1E, &AD, &77, &44, &00 \ EQUB &AA, &1B, &13, &04, &9B, &C4, &A0, &8A \ EQUB &B2, &04, &77, &1C, &B4, &00, &77, &00 \ EQUB &12, &70, &AD, &77, &10, &18, &03, &77 \ EQUB &BC, &1A, &12, &B5, &94, &15, &02, &03 \ EQUB &77, &B4, &03, &77, &00, &1F, &A2, &9B \ EQUB &1E, &11, &77, &44, &1E, &77, &03, &AF \ EQUB &19, &04, &1A, &8C, &77, &C4, &07, &AE \ EQUB &19, &04, &9E, &8E, &05, &77, &15, &16 \ EQUB &8D, &77, &88, &77, &44, &BD, &A5, &AF \ EQUB &77, &B5, &12, &0E, &70, &1B, &1B, &77 \ \ELIF _SOURCE_DISC \ \ EQUB &16, &01, &0E, &77, &1F, &16, &AD, &77 \ These bytes appear to be \ EQUB &A0, &A1, &77, &1C, &12, &12, &07, &94 \ unused and just contain random \ EQUB &C4, &44, &B5, &B9, &10, &18, &1E, &13 \ workspace noise left over from \ EQUB &04, &77, &18, &11, &11, &77, &E4, &05 \ the BBC Micro assembly process \ EQUB &77, &16, &04, &04, &77, &8E, &03, &77 \ EQUB &A7, &77, &13, &12, &12, &07, &77, &04 \ EQUB &07, &16, &BE, &77, &11, &AA, &77, &B8 \ EQUB &19, &0E, &77, &0E, &12, &B9, &04, &77 \ EQUB &B4, &00, &79, &77, &44, &00, &12, &1B \ EQUB &1B, &77, &C4, &04, &8C, &02, &16, &AC \ EQUB &88, &77, &1F, &16, &04, &77, &14, &1F \ EQUB &A8, &10, &AB, &9B, &8E, &05, &77, &15 \ EQUB &18, &0E, &04, &77, &B9, &12, &77, &A5 \ EQUB &16, &13, &0E, &77, &11, &AA, &87, &07 \ EQUB &02, &04, &1F, &77, &05, &1E, &10, &1F \ EQUB &03, &9E, &C4, &1F, &18, &1A, &12, &77 \ EQUB &04, &0E, &04, &03, &12, &1A, &77, &18 \ EQUB &11, &77, &B5, &18, &8D, &77, &1A, &18 \ EQUB &B5, &A3, &04, &9B, &4F, &5E, &49, &4A \ EQUB &1E, &5A, &77, &1F, &16, &AD, &77, &18 \ EQUB &15, &03, &16, &A7, &93, &C4, &13, &12 \ EQUB &11, &A1, &BE, &77, &07, &AE, &19, &04 \ EQUB &77, &11, &AA, &77, &B5, &12, &1E, &05 \ EQUB &77, &44, &1F, &1E, &AD, &77, &44, &00 \ EQUB &AA, &1B, &13, &04, &9B, &C4, &A0, &8A \ EQUB &B2, &04, &77, &1C, &B4, &00, &77, &00 \ EQUB &12, &70, &AD, &77, &10, &18, &03, &77 \ EQUB &BC, &1A, &12, &B5, &94, &15, &02, &03 \ EQUB &77, &B4, &03, &77, &00, &1F, &A2, &9B \ EQUB &1E, &11, &77, &44, &1E, &77, &03, &AF \ EQUB &19, &04, &1A, &8C, &77, &C4, &07, &AE \ EQUB &19, &04, &9E, &8E, &05, &77, &15, &16 \ \ENDIF \ \ELSE \ \SKIP 256 \ The ball line heap for storing x-coordinates (see the \ \ deep dive on "The ball line heap" for details) \ \ENDIF \ --- And replaced by: --------------------------------> SKIP 78 \ The ball line heap for storing x-coordinates \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: LSY2 \ Type: Variable \ Category: Drawing lines \ Summary: The ball line heap for storing y-coordinates \ Deep dive: The ball line heap \ \ ****************************************************************************** .LSY2 \ --- Mod: Code removed for two-player Elite: ---------> \IF _MATCH_ORIGINAL_BINARIES \ \IF _SNG45 \ \ EQUB &8D, &77, &88, &77, &44, &BD, &A5, &AF \ These bytes appear to be \ EQUB &77, &B5, &12, &0E, &70, &1B, &1B, &77 \ unused and just contain random \ EQUB &A7, &03, &A3, &BE, &07, &03, &77, &C4 \ workspace noise left over from \ EQUB &03, &05, &A8, &04, &1A, &1E, &04, &04 \ the BBC Micro assembly process \ EQUB &1E, &88, &79, &77, &44, &1E, &77, &19 \ EQUB &12, &AB, &87, &98, &9E, &B8, &1C, &12 \ EQUB &77, &C4, &05, &02, &19, &9B, &E4, &70 \ EQUB &A5, &77, &12, &B2, &14, &03, &AB, &9B \ EQUB &C4, &07, &AE, &19, &04, &77, &16, &A5 \ EQUB &77, &02, &19, &1E, &07, &02, &1B, &8D \ EQUB &77, &14, &18, &13, &93, &00, &1E, &B5 \ EQUB &A7, &77, &C3, &03, &05, &A8, &04, &1A \ EQUB &1E, &04, &04, &1E, &88, &9B, &5F, &E4 \ EQUB &77, &00, &8B, &1B, &77, &A0, &77, &07 \ EQUB &16, &1E, &13, &9B, &77, &77, &77, &77 \ EQUB &44, &10, &18, &18, &13, &77, &1B, &02 \ EQUB &14, &1C, &77, &CD, &83, &4F, &57, &4E \ EQUB &5E, &4A, &49, &5F, &59, &5A, &44, &00 \ EQUB &12, &1B, &1B, &77, &13, &88, &12, &77 \ EQUB &CD, &9B, &E4, &77, &1F, &16, &AD, &77 \ EQUB &8D, &05, &01, &93, &02, &04, &77, &00 \ EQUB &12, &1B, &1B, &E5, &00, &12, &77, &04 \ EQUB &1F, &B3, &1B, &77, &A5, &1A, &12, &1A \ EQUB &15, &A3, &9B, &00, &12, &77, &13, &1E \ EQUB &13, &77, &B4, &03, &77, &12, &0F, &07 \ EQUB &12, &14, &03, &77, &C4, &44, &B5, &B9 \ EQUB &10, &18, &1E, &13, &04, &9E, &11, &A7 \ EQUB &13, &77, &8E, &03, &77, &16, &15, &8E \ EQUB &03, &77, &E4, &9B, &11, &AA, &77, &C4 \ EQUB &1A, &18, &1A, &A1, &03, &77, &07, &B2 \ EQUB &16, &8D, &77, &16, &14, &BE, &07, &03 \ EQUB &77, &C3, &44, &19, &16, &01, &0E, &77 \ \ELIF _EXECUTIVE \ \ EQUB &A7, &03, &A3, &BE, &07, &03, &77, &C4 \ These bytes appear to be \ EQUB &03, &05, &A8, &04, &1A, &1E, &04, &04 \ unused and just contain random \ EQUB &1E, &88, &79, &77, &44, &1E, &77, &19 \ workspace noise left over from \ EQUB &12, &AB, &87, &98, &9E, &B8, &1C, &12 \ the BBC Micro assembly process \ EQUB &77, &C4, &05, &02, &19, &9B, &E4, &70 \ EQUB &A5, &77, &12, &B2, &14, &03, &AB, &9B \ EQUB &C4, &07, &AE, &19, &04, &77, &16, &A5 \ EQUB &77, &02, &19, &1E, &07, &02, &1B, &8D \ EQUB &77, &14, &18, &13, &93, &00, &1E, &B5 \ EQUB &A7, &77, &C3, &03, &05, &A8, &04, &1A \ EQUB &1E, &04, &04, &1E, &88, &9B, &5F, &E4 \ EQUB &77, &00, &8B, &1B, &77, &A0, &77, &07 \ EQUB &16, &1E, &13, &9B, &77, &77, &77, &77 \ EQUB &44, &10, &18, &18, &13, &77, &1B, &02 \ EQUB &14, &1C, &77, &CD, &83, &4F, &57, &4E \ EQUB &5E, &4A, &49, &5F, &59, &5A, &44, &00 \ EQUB &12, &1B, &1B, &77, &13, &88, &12, &77 \ EQUB &CD, &9B, &E4, &77, &1F, &16, &AD, &77 \ EQUB &8D, &05, &01, &93, &02, &04, &77, &00 \ EQUB &12, &1B, &1B, &E5, &00, &12, &77, &04 \ EQUB &1F, &B3, &1B, &77, &A5, &1A, &12, &1A \ EQUB &15, &A3, &9B, &00, &12, &77, &13, &1E \ EQUB &13, &77, &B4, &03, &77, &12, &0F, &07 \ EQUB &12, &14, &03, &77, &C4, &44, &B5, &B9 \ EQUB &10, &18, &1E, &13, &04, &9E, &11, &A7 \ EQUB &13, &77, &8E, &03, &77, &16, &15, &8E \ EQUB &03, &77, &E4, &9B, &11, &AA, &77, &C4 \ EQUB &1A, &18, &1A, &A1, &03, &77, &07, &B2 \ EQUB &16, &8D, &77, &16, &14, &BE, &07, &03 \ EQUB &77, &C3, &44, &19, &16, &01, &0E, &77 \ EQUB &51, &25, &52, &77, &16, &04, &77, &07 \ EQUB &16, &0E, &1A, &A1, &03, &83, &4F, &57 \ \ELIF _SOURCE_DISC \ \ EQUB &8D, &77, &88, &77, &44, &BD, &A5, &AF \ These bytes appear to be \ EQUB &77, &B5, &12, &0E, &70, &1B, &1B, &77 \ unused and just contain random \ EQUB &A7, &03, &A3, &BE, &07, &03, &77, &C4 \ workspace noise left over from \ EQUB &03, &05, &A8, &04, &1A, &1E, &04, &04 \ the BBC Micro assembly process \ EQUB &1E, &88, &79, &77, &44, &1E, &77, &19 \ EQUB &12, &AB, &87, &98, &9E, &B8, &1C, &12 \ EQUB &77, &C4, &05, &02, &19, &9B, &E4, &70 \ EQUB &A5, &77, &12, &B2, &14, &03, &AB, &9B \ EQUB &C4, &07, &AE, &19, &04, &77, &16, &A5 \ EQUB &77, &02, &19, &1E, &07, &02, &1B, &8D \ EQUB &77, &14, &18, &13, &93, &00, &1E, &B5 \ EQUB &A7, &77, &C3, &03, &05, &A8, &04, &1A \ EQUB &1E, &04, &04, &1E, &88, &9B, &5F, &E4 \ EQUB &77, &00, &8B, &1B, &77, &A0, &77, &07 \ EQUB &16, &1E, &13, &9B, &77, &77, &77, &77 \ EQUB &44, &10, &18, &18, &13, &77, &1B, &02 \ EQUB &14, &1C, &77, &CD, &83, &4F, &57, &4E \ EQUB &5E, &4A, &49, &5F, &59, &5A, &44, &00 \ EQUB &12, &1B, &1B, &77, &13, &88, &12, &77 \ EQUB &CD, &9B, &E4, &77, &1F, &16, &AD, &77 \ EQUB &8D, &05, &01, &93, &02, &04, &77, &00 \ EQUB &12, &1B, &1B, &E5, &00, &12, &77, &04 \ EQUB &1F, &B3, &1B, &77, &A5, &1A, &12, &1A \ EQUB &15, &A3, &9B, &00, &12, &77, &13, &1E \ EQUB &13, &77, &B4, &03, &77, &12, &0F, &07 \ EQUB &12, &14, &03, &77, &C4, &44, &B5, &B9 \ EQUB &10, &18, &1E, &13, &04, &9E, &11, &A7 \ EQUB &13, &77, &8E, &03, &77, &16, &15, &8E \ EQUB &03, &77, &E4, &9B, &11, &AA, &77, &C4 \ EQUB &1A, &18, &1A, &A1, &03, &77, &07, &B2 \ EQUB &16, &8D, &77, &16, &14, &BE, &07, &03 \ EQUB &77, &C3, &44, &19, &16, &01, &0E, &77 \ \ENDIF \ \ELSE \ \SKIP 256 \ The ball line heap for storing y-coordinates (see the \ \ deep dive on "The ball line heap" for details) \ \ENDIF \ --- And replaced by: --------------------------------> SKIP 78 \ The ball line heap for storing y-coordinates \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Save ELTA.bin \ \ ****************************************************************************** PRINT "ELITE A" PRINT "Assembled at ", ~S1% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - S1%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_A% PRINT "S.ELTA ", ~S1%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_A% SAVE "3-assembled-output/ELTA.bin", S1%, P%, LOAD% \ ****************************************************************************** \ \ ELITE B FILE \ \ Produces the binary file ELTB.bin that gets loaded by elite-bcfs.asm. \ \ ****************************************************************************** CODE_B% = P% LOAD_B% = LOAD% + P% - CODE% \ ****************************************************************************** \ \ Name: UNIV \ Type: Variable \ Category: Universe \ Summary: Table of pointers to the local universe's ship data blocks \ Deep dive: The local bubble of universe \ Ship data blocks \ \ ****************************************************************************** .UNIV FOR I%, 0, NOSH EQUW K% + I% * NI% \ Address of block no. I%, of size NI%, in workspace K% NEXT \ --- Mod: Code added for two-player Elite: -----------> FOR I%, NOSH+1, NOSH*2 EQUW K% + I% * NI% \ Address of block no. I%, of size NI%, in workspace K% NEXT \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: TWOS \ Type: Variable \ Category: Drawing pixels \ Summary: Ready-made single-pixel character row bytes for mode 4 \ Deep dive: Drawing monochrome pixels on the BBC Micro \ \ ------------------------------------------------------------------------------ \ \ This table is not used by the 6502 Second Processor version of Elite. Instead, \ the TWOS table in the I/O processor code is used, which contains single-pixel \ character row bytes for the mode 1 screen. \ \ ****************************************************************************** .TWOS EQUB %10000000 EQUB %01000000 EQUB %00100000 EQUB %00010000 EQUB %00001000 EQUB %00000100 EQUB %00000010 EQUB %00000001 \ ****************************************************************************** \ \ Name: TWOS2 \ Type: Variable \ Category: Drawing pixels \ Summary: Ready-made double-pixel character row bytes for mode 4 \ Deep dive: Drawing monochrome pixels on the BBC Micro \ \ ------------------------------------------------------------------------------ \ \ This table is not used by the 6502 Second Processor version of Elite. Instead, \ the TWOS2 table in the I/O processor code is used, which contains double-pixel \ character row bytes for the mode 1 screen. \ \ ****************************************************************************** .TWOS2 EQUB %11000000 EQUB %01100000 EQUB %00110000 EQUB %00011000 EQUB %00001100 EQUB %00000110 EQUB %00000011 EQUB %00000011 \ ****************************************************************************** \ \ Name: CTWOS \ Type: Variable \ Category: Drawing pixels \ Summary: Ready-made single-pixel character row bytes for mode 5 \ Deep dive: Drawing colour pixels on the BBC Micro \ \ ------------------------------------------------------------------------------ \ \ This table is not used by the 6502 Second Processor version of Elite. Instead, \ the CTWOS table in the I/O processor code is used, which contains single-pixel \ character row bytes for the mode 2 dashboard. \ \ There is one extra row to support the use of CTWOS+1,X indexing in the CPIX2 \ routine. The extra row is a repeat of the first row, and saves us from having \ to work out whether CTWOS+1+X needs to be wrapped around when drawing a \ two-pixel dash that crosses from one character block into another. See CPIX2 \ for more details. \ \ ****************************************************************************** .CTWOS EQUB %10001000 EQUB %01000100 EQUB %00100010 EQUB %00010001 EQUB %10001000 \ ****************************************************************************** \ \ Name: LL30 \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw a one-segment line by sending an OSWRCH 129 command to the \ I/O processor \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X1 The screen x-coordinate of the start of the line \ \ Y1 The screen y-coordinate of the start of the line \ \ X2 The screen x-coordinate of the end of the line \ \ Y2 The screen y-coordinate of the end of the line \ \ ****************************************************************************** .LL30 LDA #129 \ Send an OSWRCH 129 command to the I/O processor to JSR OSWRCH \ tell it to start receiving a new line to draw. The \ parameter to this call needs to contain the number of \ bytes we are going to send for the line's coordinates, \ plus 1, which we send next LDA #5 \ Send 5 to the I/O processor as the argument to the JSR OSWRCH \ OSWRCH 129 command, so the I/O processor should expect \ 4 bytes (as we send the count plus 1) LDA X1 \ Send X1, Y1, X2 and Y2 to the I/O processor, so the JSR OSWRCH \ I/O processor will draw a line from (X1, Y1) to LDA Y1 \ (X2, Y2), returning from the subroutine using a tail JSR OSWRCH \ call LDA X2 JSR OSWRCH LDA Y2 JMP OSWRCH \ ****************************************************************************** \ \ Name: LOIN \ Type: Subroutine \ Category: Drawing lines \ Summary: Add a line segment to the multi-segment line buffer \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X1 The screen x-coordinate of the start of the segment \ \ Y1 The screen y-coordinate of the start of the segment \ \ X2 The screen x-coordinate of the end of the segment \ \ Y2 The screen y-coordinate of the end of the segment \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ Y Y is preserved \ \ ****************************************************************************** .LOIN STY T1 \ Store Y in T1 so we can preserve it through the call \ to LOIN LDY LBUP \ Set Y to the size of the line buffer LDA X1 \ Store X1, Y1, X2 and Y2 in the Y-th to Y+3-th bytes of STA LBUF,Y \ the line buffer at LBUF LDA Y1 STA LBUF+1,Y LDA X2 STA LBUF+2,Y LDA Y2 STA LBUF+3,Y TYA \ Set A = Y + 4 CLC \ = LBUP + 4 ADC #4 STA LBUP \ Update LBUP with the value in A, to grow the line \ buffer by the four bytes we just added CMP #250 \ If A >= 250, jump to LBFL to draw the line in the BCS LBFL \ line buffer LDY T1 \ Restore the value of Y from T1, so it is preserved RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LBFL \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw the lines in the multi-segment line buffer by sending an \ OSWRCH 129 command to the I/O processor \ \ ****************************************************************************** .LBFL LDY LBUP \ Set Y to LBUP, the size of the multi-segment line \ buffer BEQ LBZE2 \ If LBUP = 0 then jump to LBZE2 as there is no line \ data to transmit to the I/O processor INY \ Increment Y, as we need to send the number of points \ in the new line, plus 1, to OSWRCH 129 LDA #129 \ Send an OSWRCH 129 command to the I/O processor to JSR OSWRCH \ tell it to start receiving a new line to draw. The \ parameter to this call needs to contain the number of \ bytes we are going to send for the line's coordinates, \ plus 1, so let's calculate that now TYA \ Transfer the Y counter into A, so A now contains the \ number of bytes to send to the I/O processor, plus 1 JSR OSWRCH \ Send A to the I/O processor as the argument to the \ OSWRCH 129 command, so the I/O processor can set the \ LINMAX variable in the BEGINLIN routine LDY #0 \ Set Y = 0 to act as a loop through the points in LBUF .LBFLL LDA LBUF,Y \ Send the Y-th byte of LBUF to the I/O processor JSR OSWRCH INY \ Increment the pointer to point to the next coordinate CPY LBUP \ If Y < LBUP then loop back to send the next byte, BNE LBFLL \ until we have sent them all. The I/O processor will \ now draw the line .LBZE2 STZ LBUP \ Set LBUP = 0 to reset the line buffer LDY T1 \ Restore the value of Y from T1, so it is preserved RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LBUF \ Type: Variable \ Category: Drawing lines \ Summary: The multi-segment line buffer used by LOIN \ \ ------------------------------------------------------------------------------ \ \ This buffer contains a hidden message containing the authors' names, which is \ overwritten when the buffer is used. \ \ ****************************************************************************** .LBUF IF _MATCH_ORIGINAL_BINARIES IF _SNG45 EQUS "By Ian Bell & David Braben" EQUB 10 EQUB 13 EQUB &00, &A5, &05, &65, &02, &CD, &8E, &81 \ These bytes appear to be EQUB &D0, &FC, &34, &75, &54, &07, &65, &23 \ unused and just contain random EQUB &45, &00, &8D, &20, &DE, &10, &20, &57 \ workspace noise left over from EQUB &81, &20, &DB, &6B, &4C, &8E, &50, &EA \ the BBC Micro assembly process EQUB &A0, &00, &84, &05, &A2, &13, &86, &06 EQUB &98, &51, &05, &49, &75, &91, &05, &88 EQUB &D0, &F4, &E8, &E0, &A0, &D0, &EF, &4C EQUB &7B, &11, &20, &79, &4C, &20, &EE, &2C EQUB &64, &7C, &64, &2E, &9C, &93, &08, &A9 EQUB &FF, &8D, &F1, &08, &8D, &F2, &08, &8D EQUB &F3, &08, &20, &B2, &26, &A0, &2C, &20 EQUB &61, &6D, &AD, &A4, &08, &29, &03, &D0 EQUB &0E, &AD, &EC, &08, &F0, &54, &AD, &B3 EQUB &08, &4A, &D0, &4E, &4C, &C2, &32, &C9 EQUB &03, &D0, &03, &4C, &AE, &32, &AD, &B3 EQUB &08, &C9, &02, &D0, &3D, &AD, &A4, &08 EQUB &29, &0F, &C9, &02, &D0, &0A, &AD, &EC EQUB &08, &C9, &05, &90, &2D, &4C, &7C, &32 EQUB &C9, &06, &D0, &11, &AD, &A5, &08, &C9 EQUB &D7, &D0, &1F, &AD, &A6, &08, &C9, &54 EQUB &D0, &18, &4C, &8C, &32, &C9, &0A, &D0 EQUB &11, &AD, &A5, &08, &C9, &3F, &D0, &0A EQUB &AD, &A6, &08, &C9, &48, &D0, &03, &4C EQUB &9A, &32, &4C, &0B, &51, &A9, &ED, &78 EQUB &8D, &02, &02, &A9, &4F, &8D, &03, &02 EQUB &58, &60, &AD, &00, &82, &85, &00, &AE EQUB &98, &08, &20, &4C, &31, &20, &4C, &31 EQUB &8A, &49, &80, &A8, &29, &80, &85, &31 EQUB &8E, &98, &08, &49 ELIF _EXECUTIVE EQUS "- By Ian Bell & David Braben" EQUB 10 EQUB 13 EQUB &00, &A5, &05, &65, &02, &CD, &E6, &82 \ These bytes appear to be EQUB &D0, &FC, &34, &75, &54, &07, &65, &23 \ unused and just contain random EQUB &45, &00, &8D, &20, &E0, &10, &20, &AF \ workspace noise left over from EQUB &82, &20, &DB, &6B, &4C, &B3, &50, &EA \ the BBC Micro assembly process EQUB &A0, &00, &84, &05, &A2, &13, &86, &06 EQUB &98, &51, &05, &49, &75, &91, &05, &88 EQUB &D0, &F4, &E8, &E0, &A0, &D0, &EF, &4C EQUB &7D, &11, &20, &9E, &4C, &20, &0A, &2D EQUB &64, &7C, &64, &2E, &9C, &93, &08, &A9 EQUB &FF, &8D, &F1, &08, &8D, &F2, &08, &8D EQUB &F3, &08, &20, &CE, &26, &A0, &2C, &20 EQUB &61, &6D, &AD, &A4, &08, &29, &03, &D0 EQUB &0E, &AD, &EC, &08, &F0, &54, &AD, &B3 EQUB &08, &4A, &D0, &4E, &4C, &D8, &32, &C9 EQUB &03, &D0, &03, &4C, &C4, &32, &AD, &B3 EQUB &08, &C9, &02, &D0, &3D, &AD, &A4, &08 EQUB &29, &0F, &C9, &02, &D0, &0A, &AD, &EC EQUB &08, &C9, &05, &90, &2D, &4C, &92, &32 EQUB &C9, &06, &D0, &11, &AD, &A5, &08, &C9 EQUB &D7, &D0, &1F, &AD, &A6, &08, &C9, &54 EQUB &D0, &18, &4C, &A2, &32, &C9, &0A, &D0 EQUB &11, &AD, &A5, &08, &C9, &3F, &D0, &0A EQUB &AD, &A6, &08, &C9, &48, &D0, &03, &4C EQUB &B0, &32, &4C, &3E, &51, &A9, &12, &78 EQUB &8D, &02, &02, &A9, &50, &8D, &03, &02 EQUB &58, &60, &AD, &00, &85, &85, &00, &AE EQUB &98, &08, &20, &68, &31, &20, &68, &31 EQUB &8A, &49, &80, &A8, &29, &80, &85, &31 EQUB &8E, &98 ELIF _SOURCE_DISC EQUS "By Ian Bell & David Braben" EQUB 10 EQUB 13 EQUB &00, &A5, &05, &65, &02, &CD, &AF, &81 \ These bytes appear to be EQUB &D0, &FC, &34, &75, &54, &07, &65, &23 \ unused and just contain random EQUB &45, &00, &8D, &20, &DE, &10, &20, &78 \ workspace noise left over from EQUB &81, &20, &DB, &6B, &4C, &88, &50, &EA \ the BBC Micro assembly process EQUB &A0, &00, &84, &05, &A2, &13, &86, &06 EQUB &98, &51, &05, &49, &75, &91, &05, &88 EQUB &D0, &F4, &E8, &E0, &A0, &D0, &EF, &4C EQUB &7B, &11, &20, &73, &4C, &20, &EE, &2C EQUB &64, &7C, &64, &2E, &9C, &93, &08, &A9 EQUB &FF, &8D, &F1, &08, &8D, &F2, &08, &8D EQUB &F3, &08, &20, &B2, &26, &A0, &2C, &20 EQUB &61, &6D, &AD, &A4, &08, &29, &03, &D0 EQUB &0E, &AD, &EC, &08, &F0, &54, &AD, &B3 EQUB &08, &4A, &D0, &4E, &4C, &BC, &32, &C9 EQUB &03, &D0, &03, &4C, &A8, &32, &AD, &B3 EQUB &08, &C9, &02, &D0, &3D, &AD, &A4, &08 EQUB &29, &0F, &C9, &02, &D0, &0A, &AD, &EC EQUB &08, &C9, &05, &90, &2D, &4C, &76, &32 EQUB &C9, &06, &D0, &11, &AD, &A5, &08, &C9 EQUB &D7, &D0, &1F, &AD, &A6, &08, &C9, &54 EQUB &D0, &18, &4C, &86, &32, &C9, &0A, &D0 EQUB &11, &AD, &A5, &08, &C9, &3F, &D0, &0A EQUB &AD, &A6, &08, &C9, &48, &D0, &03, &4C EQUB &94, &32, &4C, &05, &51, &A9, &E7, &78 EQUB &8D, &02, &02, &A9, &4F, &8D, &03, &02 EQUB &58, &60, &AD, &00, &82, &85, &00, &AE EQUB &98, &08, &20, &4C, &31, &20, &4C, &31 EQUB &8A, &49, &80, &A8, &29, &80, &85, &31 EQUB &8E, &98, &08, &49 ENDIF ELSE SKIP 256 \ The line buffer to send with this command ENDIF \ ****************************************************************************** \ \ Name: FLKB \ Type: Subroutine \ Category: Keyboard \ Summary: Flush the keyboard buffer \ \ ****************************************************************************** .FLKB LDA #15 \ Call OSBYTE with A = 15 and Y <> 0 to flush the input TAX \ buffers (i.e. flush the operating system's keyboard JMP OSBYTE \ buffer) and return from the subroutine using a tail \ call \ ****************************************************************************** \ \ Name: NLIN3 \ Type: Subroutine \ Category: Drawing lines \ Summary: Print a title and draw a horizontal line at row 19 to box it in \ \ ------------------------------------------------------------------------------ \ \ This routine print a text token at the cursor position and draws a horizontal \ line at pixel row 19. It is used for the Status Mode screen, the Short-range \ Chart, the Market Price screen and the Equip Ship screen. \ \ ****************************************************************************** .NLIN3 JSR TT27 \ Print the text token in A \ Fall through into NLIN4 to draw a horizontal line at \ pixel row 19 \ ****************************************************************************** \ \ Name: NLIN4 \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw a horizontal line at pixel row 19 to box in a title \ \ ------------------------------------------------------------------------------ \ \ This routine is used on the Inventory screen to draw a horizontal line at \ pixel row 19 to box in the title. \ \ ****************************************************************************** .NLIN4 LDA #19 \ Jump to NLIN2 to draw a horizontal line at pixel row BNE NLIN2 \ 19, returning from the subroutine with using a tail \ call (this BNE is effectively a JMP as A will never \ be zero) \ ****************************************************************************** \ \ Name: NLIN \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw a horizontal line at pixel row 23 to box in a title \ \ ------------------------------------------------------------------------------ \ \ Draw a horizontal line at pixel row 23 and move the text cursor down one \ line. \ \ ****************************************************************************** .NLIN LDA #23 \ Set A = 23 so NLIN2 below draws a horizontal line at \ pixel row 23 JSR INCYC \ Move the text cursor down one line \ Fall through into NLIN2 to draw the horizontal line \ at row 23 \ ****************************************************************************** \ \ Name: NLIN2 \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw a screen-wide horizontal line at the pixel row in A \ \ ------------------------------------------------------------------------------ \ \ This draws a line from (2, A) to (254, A), which is almost screen-wide and \ fits in nicely between the border boxes without clashing with it. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The pixel row on which to draw the horizontal line \ \ ****************************************************************************** .NLIN2 STA Y1 \ Set Y1 = A STA Y2 \ Set Y2 = A LDA #YELLOW \ Send a #SETCOL YELLOW command to the I/O processor to JSR DOCOL \ switch to colour 1, which is yellow LDX #2 \ Set X1 = 2, so (X1, Y1) = (2, A) STX X1 LDX #254 \ Set X2 = 254, so (X2, Y2) = (254, A) STX X2 JSR LL30 \ Call LL30 to draw a line from (2, A) to (254, A) LDA #CYAN \ Send a #SETCOL CYAN command to the I/O processor to JMP DOCOL \ switch to colour 3, which is cyan or white \ ****************************************************************************** \ \ Name: HLOIN2 \ Type: Subroutine \ Category: Drawing lines \ Summary: Remove a line from the sun line heap and draw it on-screen \ \ ------------------------------------------------------------------------------ \ \ Specifically, this does the following: \ \ * Set X1 and X2 to the x-coordinates of the ends of the horizontal line with \ centre YY(1 0) and length A to the left and right \ \ * Set the Y-th byte of the LSO block to 0 (i.e. remove this line from the \ sun line heap) \ \ * Draw a horizontal line from (X1, Y) to (X2, Y) \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ YY(1 0) The x-coordinate of the centre point of the line \ \ A The half-width of the line, i.e. the contents of the \ Y-th byte of the sun line heap \ \ Y The number of the entry in the sun line heap (which is \ also the y-coordinate of the line) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ Y Y is preserved \ \ ****************************************************************************** .HLOIN2 JSR EDGES \ Call EDGES to calculate X1 and X2 for the horizontal \ line centred on YY(1 0) and with half-width A STY Y1 \ Set Y1 = Y \ --- Mod: Code removed for two-player Elite: ---------> \LDA #0 \ Set the Y-th byte of the LSO block to 0 \STA LSO,Y \ --- And replaced by: --------------------------------> LDA #0 \ Set the Y-th byte of the LSO block to 0 STA (LSOS),Y \ --- End of replacement ------------------------------> \ Fall through into HLOIN to draw a horizontal line from \ (X1, Y) to (X2, Y) \ ****************************************************************************** \ \ Name: HLOIN \ Type: Subroutine \ Category: Drawing lines \ Summary: Add a sun line to the horizontal line buffer \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X1 The screen x-coordinate of the start of the line \ \ X2 The screen x-coordinate of the end of the line \ \ Y1 The screen y-coordinate of the line \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ Y Y is preserved \ \ ****************************************************************************** .HLOIN STY T1 \ Store Y in T1 so we can preserve it through the call \ to HLOIN LDY HBUP \ Set Y to the size of the horizontal line buffer \ --- Mod: Code added for two-player Elite: -----------> BIT drawPlayerView \ If this is player 1's view, skip the following BPL hlin1 LDA X1 \ Store X1, X2 and Y1 in the Y-th to Y+2-th bytes of STA HBUF,Y \ the horizontal line buffer at HBUF LDA X2 STA HBUF+1,Y LDA Y1 \ This is player 2's view, so move the line to the CLC \ bottom half of the screen ADC #Y STA HBUF+2,Y JMP hlin2 \ Jump to hlin2 to draw the line .hlin1 \ --- End of added code -------------------------------> LDA X1 \ Store X1, X2 and Y1 in the Y-th to Y+2-th bytes of STA HBUF,Y \ the horizontal line buffer at HBUF LDA X2 STA HBUF+1,Y LDA Y1 STA HBUF+2,Y \ --- Mod: Code added for two-player Elite: -----------> .hlin2 \ --- End of added code -------------------------------> TYA \ Set A = Y + 3 CLC \ = HBUP + 3 ADC #3 STA HBUP BMI HBFL \ If A > 127, jump to HBFL to draw the lines in the \ horizontal line buffer as the buffer is full LDY T1 \ Restore the value of Y from T1, so it is preserved RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: HBFL \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw the sun lines in the horizontal line buffer in orange by \ sending an OSWORD 247 command to the I/O processor \ \ ****************************************************************************** .HBFL LDA HBUP \ Set the first byte in HBUF (the number of bytes to STA HBUF \ transmit with the OSWORD 247 command) to HBUP, the \ size of the horizontal line buffer CMP #2 \ If HBUP = 2 then jump to HBZE2 as there is no line BEQ HBZE2 \ data to transmit to the I/O processor LDA #2 \ Set HBUP = 2 to reset the line buffer (as the size in STA HBUP \ HBUP includes the two OSWORD size bytes) LDA #247 \ Set A in preparation for sending an OSWORD 247 command LDX #LO(HBUF) \ Set (Y X) to point to the HBUF parameter block LDY #HI(HBUF) JSR OSWORD \ Send an OSWORD 247 command to the I/O processor to \ draw the horizontal lines described in the HBUF block, \ in orange .HBZE2 LDY T1 \ Restore Y to the value in T1, so if we jump here from \ the HLOIN routine, Y will be preserved from the \ original call to that routine RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: HBZE \ Type: Subroutine \ Category: Drawing lines \ Summary: Reset the horizontal line buffer \ \ ****************************************************************************** .HBZE LDA #2 \ Set HBUP = 2 to reset the horizontal line buffer (as STA HBUP \ the size in HBUP includes the two OSWORD size bytes) RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: HBUF \ Type: Variable \ Category: Drawing lines \ Summary: The horizontal line buffer to send with the OSWORD 247 command \ \ ****************************************************************************** .HBUF EQUB 0 \ The number of bytes to transmit with this command EQUB 0 \ The number of bytes to receive with this command IF _MATCH_ORIGINAL_BINARIES IF _SNG45 EQUB &D7, &08, &F0, &0F, &A0, &0C, &20, &62 \ These bytes appear to be EQUB &45, &A9, &28, &20, &C3, &55, &A9, &00 \ unused and just contain random EQUB &8D, &90, &08, &A5, &44, &10, &12, &AD \ workspace noise left over from EQUB &4B, &08, &F0, &0D, &AE, &D7, &08, &F0 \ the BBC Micro assembly process EQUB &08, &8D, &90, &08, &A0, &0F, &20, &91 EQUB &45, &AD, &4D, &08, &F0, &07, &A5, &44 EQUB &30, &3F, &20, &E1, &2B, &AD, &49, &08 EQUB &F0, &03, &0E, &CE, &08, &AD, &51, &08 EQUB &F0, &05, &A9, &00, &8D, &8B, &08, &AD EQUB &4A, &08, &2D, &D2, &08, &F0, &08, &AD EQUB &8D, &08, &D0, &03, &4C, &F0, &25, &AD EQUB &4F, &08, &F0, &03, &20, &14, &55, &AD EQUB &4E, &08, &2D, &CC, &08, &F0, &0A, &A5 EQUB &2F, &D0, &06, &CE, &8C, &08, &20, &70 EQUB &45, &AD, &50, &08, &2D, &D0, &08, &F0 EQUB &03, &8D, &8B, &08, &A9, &00, &85, &43 EQUB &85, &7D, &A5, &7C, &4A, &66, &7D, &4A EQUB &66, &7D, &85, &7E, &AD, &92, &08, &D0 EQUB &2E, &AD, &48, &08, &F0, &29, &AD, &93 EQUB &08, &C9, &F2, &B0, &22, &AE, &91, &08 EQUB &BD, &B4, &08, &F0, &1A, &48, &29, &7F EQUB &85, &43, &8D, &8F, &08, &A9, &00, &20 EQUB &C3, &55, &20, &CF, &31, &68, &10, &02 EQUB &A9, &00, &29, &FA, &8D, &92, &08, &A2 EQUB &00, &86, &83, &BD, &52, &08, &D0, &03 EQUB &4C, &96, &14, &85, &8B, &20, &6C, &44 EQUB &A0, &24, &B1, &1F, &99, &45, &00, &88 EQUB &10, &F8, &A5, &8B, &30, &27, &0A, &A8 EQUB &B9, &FE, &CF, &85, &1D, &B9, &FF, &CF EQUB &85, &1E, &AD, &CE, &08, &10, &16, &C0 EQUB &04, &F0, &12, &C0, &3E, &B0, &0E, &A5 EQUB &64, &29, &20, &D0, &08, &06, &64, &38 ELIF _EXECUTIVE EQUB &08, &2D, &D7, &08, &F0, &0F, &A0, &0C \ These bytes appear to be EQUB &20, &87, &45, &A9, &28, &20, &F8, &55 \ unused and just contain random EQUB &A9, &00, &8D, &90, &08, &A5, &44, &10 \ workspace noise left over from EQUB &12, &AD, &4B, &08, &F0, &0D, &AE, &D7 \ the BBC Micro assembly process EQUB &08, &F0, &08, &8D, &90, &08, &A0, &0F EQUB &20, &B6, &45, &AD, &4D, &08, &F0, &07 EQUB &A5, &44, &30, &3F, &20, &FD, &2B, &AD EQUB &49, &08, &F0, &03, &0E, &CE, &08, &AD EQUB &51, &08, &F0, &05, &A9, &00, &8D, &8B EQUB &08, &AD, &4A, &08, &2D, &D2, &08, &F0 EQUB &08, &AD, &8D, &08, &D0, &03, &4C, &0C EQUB &26, &AD, &4F, &08, &F0, &03, &20, &44 EQUB &55, &AD, &4E, &08, &2D, &CC, &08, &F0 EQUB &0A, &A5, &2F, &D0, &06, &CE, &8C, &08 EQUB &20, &95, &45, &AD, &50, &08, &2D, &D0 EQUB &08, &F0, &03, &8D, &8B, &08, &A9, &00 EQUB &85, &43, &85, &7D, &A5, &7C, &4A, &66 EQUB &7D, &4A, &66, &7D, &85, &7E, &AD, &92 EQUB &08, &D0, &2E, &AD, &48, &08, &F0, &29 EQUB &AD, &93, &08, &C9, &F2, &B0, &22, &AE EQUB &91, &08, &BD, &B4, &08, &F0, &1A, &48 EQUB &29, &7F, &85, &43, &8D, &8F, &08, &A9 EQUB &00, &20, &F8, &55, &20, &EB, &31, &68 EQUB &10, &02, &A9, &00, &29, &FA, &8D, &92 EQUB &08, &A2, &00, &86, &83, &BD, &52, &08 EQUB &D0, &03, &4C, &98, &14, &85, &8B, &20 EQUB &91, &44, &A0, &24, &B1, &1F, &99, &45 EQUB &00, &88, &10, &F8, &A5, &8B, &30, &27 EQUB &0A, &A8, &B9, &FE, &CF, &85, &1D, &B9 EQUB &FF, &CF, &85, &1E, &AD, &CE, &08, &10 EQUB &16, &C0, &04, &F0, &12, &C0, &3E, &B0 EQUB &0E, &A5, &64, &29, &20, &D0, &08, &06 ELIF _SOURCE_DISC EQUB &D7, &08, &F0, &0F, &A0, &0C, &20, &5C \ These bytes appear to be EQUB &45, &A9, &28, &20, &BA, &55, &A9, &00 \ unused and just contain random EQUB &8D, &90, &08, &A5, &44, &10, &12, &AD \ workspace noise left over from EQUB &4B, &08, &F0, &0D, &AE, &D7, &08, &F0 \ the BBC Micro assembly process EQUB &08, &8D, &90, &08, &A0, &0F, &20, &8B EQUB &45, &AD, &4D, &08, &F0, &07, &A5, &44 EQUB &30, &3F, &20, &E1, &2B, &AD, &49, &08 EQUB &F0, &03, &0E, &CE, &08, &AD, &51, &08 EQUB &F0, &05, &A9, &00, &8D, &8B, &08, &AD EQUB &4A, &08, &2D, &D2, &08, &F0, &08, &AD EQUB &8D, &08, &D0, &03, &4C, &F0, &25, &AD EQUB &4F, &08, &F0, &03, &20, &0B, &55, &AD EQUB &4E, &08, &2D, &CC, &08, &F0, &0A, &A5 EQUB &2F, &D0, &06, &CE, &8C, &08, &20, &6A EQUB &45, &AD, &50, &08, &2D, &D0, &08, &F0 EQUB &03, &8D, &8B, &08, &A9, &00, &85, &43 EQUB &85, &7D, &A5, &7C, &4A, &66, &7D, &4A EQUB &66, &7D, &85, &7E, &AD, &92, &08, &D0 EQUB &2E, &AD, &48, &08, &F0, &29, &AD, &93 EQUB &08, &C9, &F2, &B0, &22, &AE, &91, &08 EQUB &BD, &B4, &08, &F0, &1A, &48, &29, &7F EQUB &85, &43, &8D, &8F, &08, &A9, &00, &20 EQUB &BA, &55, &20, &CF, &31, &68, &10, &02 EQUB &A9, &00, &29, &FA, &8D, &92, &08, &A2 EQUB &00, &86, &83, &BD, &52, &08, &D0, &03 EQUB &4C, &96, &14, &85, &8B, &20, &66, &44 EQUB &A0, &24, &B1, &1F, &99, &45, &00, &88 EQUB &10, &F8, &A5, &8B, &30, &27, &0A, &A8 EQUB &B9, &FE, &CF, &85, &1D, &B9, &FF, &CF EQUB &85, &1E, &AD, &CE, &08, &10, &16, &C0 EQUB &04, &F0, &12, &C0, &3E, &B0, &0E, &A5 EQUB &64, &29, &20, &D0, &08, &06, &64, &38 ENDIF ELSE SKIP 256 \ The horizontal line buffer to send with this command ENDIF \ ****************************************************************************** \ \ Name: TWFL \ Type: Variable \ Category: Drawing lines \ Summary: Ready-made character rows for the left end of a horizontal line in \ mode 4 \ \ ------------------------------------------------------------------------------ \ \ This table is not used by the 6502 Second Processor version of Elite. Instead, \ the TWFL table in the I/O processor code is used, which contains ready-made \ bytes for plotting horizontal line end caps in mode 1 (the top part of the \ split screen). \ \ ****************************************************************************** .TWFL EQUB %10000000 EQUB %11000000 EQUB %11100000 EQUB %11110000 EQUB %11111000 EQUB %11111100 EQUB %11111110 \ ****************************************************************************** \ \ Name: TWFR \ Type: Variable \ Category: Drawing lines \ Summary: Ready-made character rows for the right end of a horizontal line \ in mode 4 \ \ ------------------------------------------------------------------------------ \ \ This table is not used by the 6502 Second Processor version of Elite. Instead, \ the TWFR table in the I/O processor code is used, which contains ready-made \ bytes for plotting horizontal line end caps in mode 1 (the top part of the \ split screen). \ \ ****************************************************************************** .TWFR EQUB %11111111 EQUB %01111111 EQUB %00111111 EQUB %00011111 EQUB %00001111 EQUB %00000111 EQUB %00000011 EQUB %00000001 \ ****************************************************************************** \ \ Name: PIX1 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (YY+1 SYL+Y) = (A P) + (S R) and draw stardust particle \ \ ------------------------------------------------------------------------------ \ \ Calculate the following: \ \ (YY+1 SYL+Y) = (A P) + (S R) \ \ and draw a stardust particle at (X1,Y1) with distance ZZ. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ (A P) A is the angle ALPHA or BETA, P is always 0 \ \ (S R) YY(1 0) or YY(1 0) + Q * A \ \ Y Stardust particle number \ \ X1 The x-coordinate offset \ \ Y1 The y-coordinate offset \ \ ZZ The distance of the point, with bigger distances drawing \ smaller points: \ \ * ZZ < 80 Double-height four-pixel square \ \ * 80 <= ZZ <= 143 Single-height two-pixel dash \ \ * ZZ > 143 Single-height one-pixel dot \ \ ****************************************************************************** .PIX1 JSR ADD \ Set (A X) = (A P) + (S R) STA YY+1 \ Set YY+1 to A, the high byte of the result TXA \ Set SYL+Y to X, the low byte of the result STA SYL,Y \ Fall through into PIXEL2 to draw the stardust particle \ at (X1,Y1) \ ****************************************************************************** \ \ Name: PIXEL2 \ Type: Subroutine \ Category: Drawing pixels \ Summary: Draw a stardust particle relative to the screen centre \ \ ------------------------------------------------------------------------------ \ \ Draw a point (X1, Y1) from the middle of the screen with a size determined by \ a distance value. Used to draw stardust particles. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X1 The x-coordinate offset \ \ Y1 The y-coordinate offset (positive means up the screen \ from the centre, negative means down the screen) \ \ ZZ The distance of the point, with bigger distances drawing \ smaller points: \ \ * ZZ < 80 Double-height four-pixel square \ \ * 80 <= ZZ <= 143 Single-height two-pixel dash \ \ * ZZ > 143 Single-height one-pixel dot \ \ ****************************************************************************** .PIXEL2 LDA X1 \ Fetch the x-coordinate offset into A BPL PX1 \ If the x-coordinate offset is positive, jump to PX1 \ to skip the following negation EOR #%01111111 \ The x-coordinate offset is negative, so flip all the CLC \ bits apart from the sign bit and add 1, to convert it ADC #1 \ from a sign-magnitude number to a signed number .PX1 EOR #%10000000 \ Set X = X1 + 128 TAX \ \ So X is now the offset converted to an x-coordinate, \ centred on x-coordinate 128 LDA Y1 \ Fetch the y-coordinate offset into A and clear the AND #%01111111 \ sign bit, so A = |Y1| \ --- Mod: Code removed for two-player Elite: ---------> \CMP #96 \ If |Y1| >= 96 then it's off the screen (as 96 is half \BCS PX4 \ the screen height), so return from the subroutine (as \ \ PX4 contains an RTS) \ --- And replaced by: --------------------------------> CMP #48 \ If |Y1| >= 48 then it's off the screen (as 48 is half BCS PX4 \ the screen height), so return from the subroutine (as \ PX4 contains an RTS) \ --- End of replacement ------------------------------> LDA Y1 \ Fetch the y-coordinate offset into A BPL PX2 \ If the y-coordinate offset is positive, jump to PX2 \ to skip the following negation EOR #%01111111 \ The y-coordinate offset is negative, so flip all the ADC #1 \ bits apart from the sign bit and subtract 1 to convert \ A from a sign-magnitude number into a traditional \ signed number, so A is now Y1 in a form that can be \ used with the SBC instruction .PX2 \ --- Mod: Code removed for two-player Elite: ---------> \STA T \ Set A = #Y + 1 - Y1 \LDA #Y+1 \ \SBC T \ So if Y1 is positive we display the point up from the \ \ centre at y-coordinate 97, while a negative Y1 means \ \ down from the centre \ --- And replaced by: --------------------------------> STA T \ Set A = #Y/2 + 1 - Y1 LDA #(Y/2)+1 \ SBC T \ So if Y1 is positive we display the point up from the \ centre at y-coordinate Y/2, while a negative Y1 means \ down from the centre BIT drawPlayerView \ If we are drawing player 2's stars, draw them in the BPL P%+5 \ bottom half of the space view CLC ADC #Y \ --- End of replacement ------------------------------> \ Fall through into PIXEL to draw the stardust at the \ screen coordinates in (X, A) \ ****************************************************************************** \ \ Name: PIXEL \ Type: Subroutine \ Category: Drawing pixels \ Summary: Add a white dot at a specific distance to the pixel buffer \ (two-pixel dash or four-pixel square) \ \ ------------------------------------------------------------------------------ \ \ Draw a point in white (cyan/red) at screen coordinate (X, A), with the point \ size determined by the distance in ZZ, by adding it to the pixel buffer. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The screen x-coordinate of the point to draw \ \ A The screen y-coordinate of the point to draw \ \ ZZ The distance of the point, with bigger distances drawing \ smaller points: \ \ * ZZ < 80 Double-height four-pixel square \ \ * 80 <= ZZ <= 143 Single-height two-pixel dash \ \ * ZZ > 143 Single-height one-pixel dot \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ Y Y is preserved \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ PX4 Contains an RTS \ \ ****************************************************************************** .PIXEL STY T1 \ Store Y in T1 so we can preserve it through the call \ to PIXEL LDY PBUP \ Set Y to the size of the pixel buffer STA PBUF+2,Y \ Store the y-coordinate in PBUF+2 TXA \ Store the x-coordinate in PBUF+1 STA PBUF+1,Y LDA ZZ \ Store the distance in PBUF, with bits 0-2 cleared so AND #%11111000 \ the parasite's PIXEL routine will draw this point as a STA PBUF,Y \ white two-pixel dash or four-pixel square TYA \ Set A = Y + 3 CLC \ = PBUP + 3 ADC #3 STA PBUP \ Update PBUP with the value in A, to grow the line \ buffer by the three bytes we just added BMI PBFL \ If A > 127, jump to PBFL to draw the pixels in the \ pixel buffer LDY T1 \ Restore the value of Y from T1, so it is preserved .PX4 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PBFL \ Type: Subroutine \ Category: Drawing pixels \ Summary: Draw the pixel in the pixel buffer by sending an OSWORD 241 \ command to the I/O processor \ \ ****************************************************************************** .PBFL LDA PBUP \ Set the first byte in pixbl (the number of bytes to STA pixbl \ transmit with the OSWORD 241 command) to PBUP, the \ size of the pixel buffer CMP #2 \ If PBUP = 2 then jump to PBZE2 as there is no pixel BEQ PBZE2 \ data to transmit to the I/O processor LDA #2 \ Set PBUP = 2 to reset the pixel buffer (as the size in STA PBUP \ PBUP includes the two OSWORD size bytes) LDA #DUST \ Send a #SETCOL DUST command to the I/O processor to JSR DOCOL \ switch to stripe 3-2-3-2, which is cyan/red in the \ space view LDA #241 \ Set A in preparation for sending an OSWORD 241 command LDX #LO(pixbl) \ Set (Y X) to point to the pixbl parameter block LDY #HI(pixbl) JSR OSWORD \ Send an OSWORD 241 command to the I/O processor to \ draw the pixel described in the pixbl block .PBZE2 LDY T1 \ Restore Y to the value in T1, so if we jump here from \ the PIXEL or PIXEL3 routines, Y will be preserved from \ the original call to that routine RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PBZE \ Type: Subroutine \ Category: Drawing pixels \ Summary: Reset the pixel buffer \ \ ****************************************************************************** .PBZE LDA #2 \ Set PBUP = 2 to reset the pixel buffer (as the size in STA PBUP \ PBUP includes the two OSWORD size bytes) RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PIXEL3 \ Type: Subroutine \ Category: Drawing pixels \ Summary: Add a coloured dot at a specific distance to the pixel buffer \ (one-pixel dot, two-pixel dash or four-pixel square) \ \ ------------------------------------------------------------------------------ \ \ Draw a point at screen coordinate (X, A), with the point size and colour being \ determined by the distance in ZZ, by adding it to the pixel buffer. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The screen x-coordinate of the point to draw \ \ A The screen y-coordinate of the point to draw \ \ ZZ The distance of the point, with bigger distances drawing \ smaller points: \ \ * ZZ < 80 Double-height four-pixel square \ \ * 80 <= ZZ <= 143 Single-height two-pixel dash \ \ * ZZ > 143 Single-height one-pixel dot \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ Y Y is preserved \ \ ****************************************************************************** .PIXEL3 STY T1 \ Store Y in T1 so we can preserve it through the call \ to PIXEL3 LDY PBUP \ Set Y to the size of the pixel buffer STA PBUF+2,Y \ Store the y-coordinate in PBUF+2 TXA \ Store the x-coordinate in PBUF+2 STA PBUF+1,Y LDA ZZ \ Store the distance in PBUF, making sure bit 0 is set ORA #1 \ so the parasite's PIXEL routine will draw this dot STA PBUF,Y \ using the distance to determine the dot's size and \ colour TYA \ Set A = Y + 3 CLC \ = PBUP + 3 ADC #3 STA PBUP \ Update PBUP with the value in A, to grow the line \ buffer by the three bytes we just added BMI PBFL \ If A > 127, jump to PBFL to draw the pixels in the \ pixel buffer LDY T1 \ Restore the value of Y from T1, so it is preserved RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PBUF \ Type: Variable \ Category: Drawing pixels \ Summary: The pixel buffer to send with the OSWORD 241 command \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ pixbl Points to the first byte of the PBUF block, which is \ where the OSWORD transmission size goes \ \ ****************************************************************************** .pixbl .PBUF EQUB 0 \ The number of bytes to transmit with this command EQUB 0 \ The number of bytes to receive with this command IF _MATCH_ORIGINAL_BINARIES IF _SNG45 EQUB &20, &79, &55, &06, &64, &38, &66, &64 \ These bytes appear to be EQUB &D0, &17, &A9, &01, &85, &7C, &A9, &05 \ unused and just contain random EQUB &D0, &09, &06, &64, &38, &66, &64, &A5 \ workspace noise left over from EQUB &68, &38, &6A, &20, &1E, &44, &20, &79 \ the BBC Micro assembly process EQUB &55, &A5, &69, &10, &03, &20, &8C, &6D EQUB &A5, &86, &D0, &6D, &20, &FD, &6B, &20 EQUB &7A, &2B, &90, &62, &AD, &90, &08, &F0 EQUB &0A, &20, &82, &55, &A6, &83, &A0, &03 EQUB &20, &64, &45, &A5, &43, &F0, &4F, &A2 EQUB &0F, &20, &A5, &55, &A5, &8B, &C9, &02 EQUB &F0, &3F, &C9, &1F, &90, &0A, &A5, &43 EQUB &C9, &17, &D0, &35, &46, &43, &46, &43 EQUB &A5, &68, &38, &E5, &43, &B0, &28, &06 EQUB &64, &38, &66, &64, &A5, &8B, &C9, &07 EQUB &D0, &10, &A5, &43, &C9, &32, &D0, &0A EQUB &20, &24, &4D, &A2, &08, &29, &03, &20 EQUB &E4, &15, &A0, &04, &20, &D7, &15, &A0 EQUB &05, &20, &D7, &15, &20, &96, &55, &85 EQUB &68, &A5, &8B, &20, &00, &2C, &20, &4E EQUB &60, &A0, &23, &A5, &68, &91, &1F, &A5 EQUB &69, &30, &2D, &A5, &64, &10, &2C, &29 EQUB &20, &F0, &28, &A5, &69, &29, &40, &0D EQUB &D8, &08, &8D, &D8, &08, &AD, &96, &08 EQUB &0D, &8D, &08, &D0, &13, &A0, &0A, &B1 EQUB &1D, &F0, &0D, &AA, &C8, &B1, &1D, &A8 EQUB &20, &08, &3E, &A9, &00, &20, &C7, &57 EQUB &4C, &0F, &4B, &A5, &8B, &30, &05, &20 EQUB &D8, &4F, &90, &F4, &A0, &1F, &A5, &64 EQUB &91, &1F, &A6, &83, &E8, &4C, &D6, &12 EQUB &AD, &CE, &08, &10, &10, &0E, &CE, &08 EQUB &20, &FC, &6D, &A9, &83, &20, &EE, &FF EQUB &A9, &30, &20, &EE, &FF, &A5, &89, &29 ELIF _EXECUTIVE EQUB &29, &50, &20, &A9, &55, &06, &64, &38 \ These bytes appear to be EQUB &66, &64, &D0, &17, &A9, &01, &85, &7C \ unused and just contain random EQUB &A9, &05, &D0, &09, &06, &64, &38, &66 \ workspace noise left over from EQUB &64, &A5, &68, &38, &6A, &20, &43, &44 \ the BBC Micro assembly process EQUB &20, &A9, &55, &A5, &69, &10, &03, &20 EQUB &8C, &6D, &A5, &86, &D0, &6D, &20, &FD EQUB &6B, &20, &96, &2B, &90, &62, &AD, &90 EQUB &08, &F0, &0A, &20, &B2, &55, &A6, &83 EQUB &A0, &03, &20, &89, &45, &A5, &43, &F0 EQUB &4F, &A2, &0F, &20, &DA, &55, &A5, &8B EQUB &C9, &02, &F0, &3F, &C9, &1F, &90, &0A EQUB &A5, &43, &C9, &17, &D0, &35, &46, &43 EQUB &46, &43, &A5, &68, &38, &E5, &43, &B0 EQUB &28, &06, &64, &38, &66, &64, &A5, &8B EQUB &C9, &07, &D0, &10, &A5, &43, &C9, &32 EQUB &D0, &0A, &20, &49, &4D, &A2, &08, &29 EQUB &03, &20, &EB, &15, &A0, &04, &20, &DE EQUB &15, &A0, &05, &20, &DE, &15, &20, &CB EQUB &55, &85, &68, &A5, &8B, &20, &1C, &2C EQUB &20, &4E, &60, &A0, &23, &A5, &68, &91 EQUB &1F, &A5, &69, &30, &2D, &A5, &64, &10 EQUB &2C, &29, &20, &F0, &28, &A5, &69, &29 EQUB &40, &0D, &D8, &08, &8D, &D8, &08, &AD EQUB &96, &08, &0D, &8D, &08, &D0, &13, &A0 EQUB &0A, &B1, &1D, &F0, &0D, &AA, &C8, &B1 EQUB &1D, &A8, &20, &2D, &3E, &A9, &00, &20 EQUB &FC, &57, &4C, &34, &4B, &A5, &8B, &30 EQUB &05, &20, &FD, &4F, &90, &F4, &A0, &1F EQUB &A5, &64, &91, &1F, &A6, &83, &E8, &4C EQUB &D8, &12, &AD, &CE, &08, &10, &10, &0E EQUB &CE, &08, &20, &FC, &6D, &A9, &83, &20 EQUB &EE, &FF, &A9, &30, &20, &EE, &FF, &A5 ELIF _SOURCE_DISC EQUB &20, &70, &55, &06, &64, &38, &66, &64 \ These bytes appear to be EQUB &D0, &17, &A9, &01, &85, &7C, &A9, &05 \ unused and just contain random EQUB &D0, &09, &06, &64, &38, &66, &64, &A5 \ workspace noise left over from EQUB &68, &38, &6A, &20, &18, &44, &20, &70 \ the BBC Micro assembly process EQUB &55, &A5, &69, &10, &03, &20, &8C, &6D EQUB &A5, &86, &D0, &6D, &20, &FD, &6B, &20 EQUB &7A, &2B, &90, &62, &AD, &90, &08, &F0 EQUB &0A, &20, &79, &55, &A6, &83, &A0, &03 EQUB &20, &5E, &45, &A5, &43, &F0, &4F, &A2 EQUB &0F, &20, &9C, &55, &A5, &8B, &C9, &02 EQUB &F0, &3F, &C9, &1F, &90, &0A, &A5, &43 EQUB &C9, &17, &D0, &35, &46, &43, &46, &43 EQUB &A5, &68, &38, &E5, &43, &B0, &28, &06 EQUB &64, &38, &66, &64, &A5, &8B, &C9, &07 EQUB &D0, &10, &A5, &43, &C9, &32, &D0, &0A EQUB &20, &1E, &4D, &A2, &08, &29, &03, &20 EQUB &E4, &15, &A0, &04, &20, &D7, &15, &A0 EQUB &05, &20, &D7, &15, &20, &8D, &55, &85 EQUB &68, &A5, &8B, &20, &00, &2C, &20, &4E EQUB &60, &A0, &23, &A5, &68, &91, &1F, &A5 EQUB &69, &30, &2D, &A5, &64, &10, &2C, &29 EQUB &20, &F0, &28, &A5, &69, &29, &40, &0D EQUB &D8, &08, &8D, &D8, &08, &AD, &96, &08 EQUB &0D, &8D, &08, &D0, &13, &A0, &0A, &B1 EQUB &1D, &F0, &0D, &AA, &C8, &B1, &1D, &A8 EQUB &20, &02, &3E, &A9, &00, &20, &BE, &57 EQUB &4C, &09, &4B, &A5, &8B, &30, &05, &20 EQUB &D2, &4F, &90, &F4, &A0, &1F, &A5, &64 EQUB &91, &1F, &A6, &83, &E8, &4C, &D6, &12 EQUB &AD, &CE, &08, &10, &10, &0E, &CE, &08 EQUB &20, &FC, &6D, &A9, &83, &20, &EE, &FF EQUB &A9, &30, &20, &EE, &FF, &A5, &89, &29 ENDIF ELSE SKIP 256 \ The pixel buffer to send with this command ENDIF \ ****************************************************************************** \ \ Name: BLINE \ Type: Subroutine \ Category: Drawing circles \ Summary: Draw a circle segment and add it to the ball line heap \ Deep dive: The ball line heap \ Drawing circles \ \ ------------------------------------------------------------------------------ \ \ Draw a single segment of a circle by adding the point to the ball line heap, \ so it can be sent to the I/O processor for drawing once the whole circle has \ been added to the heap. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ CNT The number of this segment \ \ STP The step size for the circle \ \ K6(1 0) The x-coordinate of the new point on the circle, as \ a screen coordinate \ \ (T X) The y-coordinate of the new point on the circle, as \ an offset from the centre of the circle \ \ FLAG Set to &FF for the first call, so it sets up the first \ point in the heap but waits until the second call before \ drawing anything (as we need two points, i.e. two calls, \ before we can draw a line) \ \ K4(1 0) Pixel y-coordinate of the centre of the circle \ \ K5(1 0) Screen x-coordinate of the previous point added to the \ ball line heap (if this is not the first point) \ \ K5(3 2) Screen y-coordinate of the previous point added to the \ ball line heap (if this is not the first point) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ CNT CNT is updated to CNT + STP \ \ A The new value of CNT \ \ K5(1 0) Screen x-coordinate of the point that we just added to \ the ball line heap \ \ K5(3 2) Screen y-coordinate of the point that we just added to \ the ball line heap \ \ FLAG Set to 0 \ \ ****************************************************************************** .BLINE TXA \ Set K6(3 2) = (T X) + K4(1 0) ADC K4 \ = y-coord of centre + y-coord of new point STA K6+2 \ LDA K4+1 \ so K6(3 2) now contains the y-coordinate of the new ADC T \ point on the circle but as a screen coordinate, to go STA K6+3 \ along with the screen x-coordinate in K6(1 0) LDA FLAG \ If FLAG = 0, jump down to BL1 BEQ BL1 INC FLAG \ Flag is &FF so this is the first call to BLINE, so \ increment FLAG to set it to 0, as then the next time \ we call BLINE it can draw the first line, from this \ point to the next \ --- Mod: Code removed for flicker-free planets: -----> \BEQ BL5 \ This is the first call to BLINE, so we don't need to \ \ copy the previous point to XX15 as there isn't one, \ \ so we jump to BL5 to tidy up and return from the \ \ subroutine (this BEQ is effectively a JMP, as we just \ \ incremented FLAG to 0) \ --- And replaced by: --------------------------------> .BL5 JSR DrawPlanetLine \ Draw the current line from the old planet \ The following inserts a &FF marker into the LSY2 line \ heap to indicate that the next call to BLINE should \ store both the (X1, Y1) and (X2, Y2) points. We do \ this on the very first call to BLINE (when FLAG is \ &FF), and on subsequent calls if the segment does not \ fit on-screen, in which case we don't draw or store \ that segment, and we start a new segment with the next \ call to BLINE that does fit on-screen \ --- Mod: Code removed for two-player Elite: ---------> \LDY LSP \ If byte LSP-1 of LSY2 = &FF, jump to BL7 to tidy up \LDA #&FF \ and return from the subroutine, as the point that has \CMP LSY2-1,Y \ been passed to BLINE is the start of a segment, so all \BEQ BL7 \ we need to do is save the coordinate in K5, without \ \ moving the pointer in LSP \ \STA LSY2,Y \ Otherwise we just tried to plot a segment but it \ \ didn't fit on-screen, so put the &FF marker into the \ \ heap for this point, so the next call to BLINE starts \ \ a new segment \ \INC LSP \ Increment LSP to point to the next point in the heap \ --- And replaced by: --------------------------------> LDA (LSPS) \ If byte LSP-1 of LSY2 = &FF, jump to BL7 to tidy up TAY \ and return from the subroutine, as the point that has DEY \ been passed to BLINE is the start of a segment, so all LDA #&FF \ we need to do is save the coordinate in K5, without CMP (LSY2S),Y \ moving the pointer in LSP BNE P%+6 INY JMP BL7 INY \ Otherwise we just tried to plot a segment but it STA (LSY2S),Y \ didn't fit on-screen, so put the &FF marker into the \ heap for this point, so the next call to BLINE starts \ a new segment LDA (LSPS) \ Increment LSP to point to the next point in the heap CLC ADC #1 STA (LSPS) \ --- End of replacement ------------------------------> BNE BL7 \ Jump to BL7 to tidy up and return from the subroutine \ (this BNE is effectively a JMP, as LSP will never be \ zero) \ --- End of replacement ------------------------------> .BL1 LDA K5 \ Set XX15 = K5 = x_lo of previous point STA XX15 LDA K5+1 \ Set XX15+1 = K5+1 = x_hi of previous point STA XX15+1 LDA K5+2 \ Set XX15+2 = K5+2 = y_lo of previous point STA XX15+2 LDA K5+3 \ Set XX15+3 = K5+3 = y_hi of previous point STA XX15+3 LDA K6 \ Set XX15+4 = x_lo of new point STA XX15+4 LDA K6+1 \ Set XX15+5 = x_hi of new point STA XX15+5 LDA K6+2 \ Set XX12 = y_lo of new point STA XX12 LDA K6+3 \ Set XX12+1 = y_hi of new point STA XX12+1 JSR LL145 \ Call LL145 to see if the new line segment needs to be \ clipped to fit on-screen, returning the clipped line's \ end-points in (X1, Y1) and (X2, Y2) BCS BL5 \ If the C flag is set then the line is not visible on \ screen anyway, so jump to BL5, to avoid drawing and \ storing this line \ --- Mod: Code added for flicker-free planets: -------> LDA SWAP \ If SWAP = 0, then we didn't have to swap the line BEQ BL9 \ coordinates around during the clipping process, so \ jump to BL9 to skip the following swap LDA X1 \ Otherwise the coordinates were swapped by the call to LDY X2 \ LL145 above, so we swap (X1, Y1) and (X2, Y2) back STA X2 \ again STY X1 LDA Y1 LDY Y2 STA Y2 STY Y1 .BL9 \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \LDY LSP \ Set Y = LSP \ --- And replaced by: --------------------------------> LDA (LSPS) \ Set Y = LSP TAY \ --- End of replacement ------------------------------> \ --- Mod: Code added for flicker-free planets: -------> \ --- Mod: Code removed for two-player Elite: ---------> \LDA LSY2-1,Y \ If byte LSP-1 of LSY2 is not &FF, jump down to BL8 \CMP #&FF \ to skip the following (X1, Y1) code \BNE BL8 \ --- And replaced by: --------------------------------> DEY \ If byte LSP-1 of LSY2 is not &FF, jump down to BL8 LDA (LSY2S),Y \ to skip the following (X1, Y1) code CMP #&FF PHP INY PLP BNE BL8 \ --- End of replacement ------------------------------> \ Byte LSP-1 of LSY2 is &FF, which indicates that we \ need to store (X1, Y1) in the heap JSR DrawPlanetLine \ Draw the current line from the old planet \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \LDA X1 \ Store X1 in the LSP-th byte of LSX2 \STA LSX2,Y \ \LDA Y1 \ Store Y1 in the LSP-th byte of LSY2 \STA LSY2,Y \ --- And replaced by: --------------------------------> LDA X1 \ Store X1 in the LSP-th byte of LSX2 STA (LSX2S),Y LDA Y1 \ Store Y1 in the LSP-th byte of LSY2 STA (LSY2S),Y \ --- End of replacement ------------------------------> INY \ Increment Y to point to the next byte in LSX2/LSY2 \ --- Mod: Code added for flicker-free planets: -------> .BL8 LDA #&FF \ Set bit 7 of K3+8 so we do not draw the current line STA K3+8 \ in the call to DrawPlanetLine, but store the \ coordinates so we we can check them below JSR DrawPlanetLine+2 \ Calculate the current line from the old heap, but do \ not draw it, but store the coordinates (X1, Y1) and \ (X2, Y2) in K3+4 to K3+7 \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \LDA X2 \ Store X2 in the LSP-th byte of LSX2 \STA LSX2,Y \ \LDA Y2 \ Store Y2 in the LSP-th byte of LSX2 \STA LSY2,Y \ --- And replaced by: --------------------------------> LDA X2 \ Store X2 in the LSP-th byte of LSX2 STA (LSX2S),Y LDA Y2 \ Store Y2 in the LSP-th byte of LSY2 STA (LSY2S),Y \ --- End of replacement ------------------------------> INY \ Increment Y to point to the next byte in LSX2/LSY2 \ --- Mod: Code removed for two-player Elite: ---------> \STY LSP \ Update LSP to point to the same as Y \ --- And replaced by: --------------------------------> TYA \ Update LSP to point to the same as Y STA (LSPS) TAY \ --- End of replacement ------------------------------> \ --- Mod: Code removed for flicker-free planets: -----> \.BL5 \ --- And replaced by: --------------------------------> JSR DrawNewPlanetLine \ Draw a line from (X1, Y1) to (X2, Y2), but only if it \ is different to the old line in K3+4 to K3+7 \ --- Mod: Code removed for two-player Elite: ---------> \LDA XX13 \ If XX13 is non-zero, jump up to BL5 to add a &FF \BNE BL5 \ marker to the end of the line heap. XX13 is non-zero \ \ after the call to the clipping routine LL145 above if \ \ the end of the line was clipped, meaning the next line \ \ sent to BLINE can't join onto the end but has to start \ \ a new segment, and that's what inserting the &FF \ \ marker does \ --- And replaced by: --------------------------------> LDA XX13 \ If XX13 is non-zero, jump up to BL5 to add a &FF BEQ P%+5 \ marker to the end of the line heap. XX13 is non-zero JMP BL5 \ after the call to the clipping routine LL145 above if \ the end of the line was clipped, meaning the next line \ sent to BLINE can't join onto the end but has to start \ a new segment, and that's what inserting the &FF \ marker does \ --- End of replacement ------------------------------> .BL7 \ --- End of replacement ------------------------------> LDA K6 \ Copy the data for this step point from K6(3 2 1 0) STA K5 \ into K5(3 2 1 0), for use in the next call to BLINE: LDA K6+1 \ STA K5+1 \ * K5(1 0) = screen x-coordinate of this point LDA K6+2 \ STA K5+2 \ * K5(3 2) = screen y-coordinate of this point LDA K6+3 \ STA K5+3 \ They now become the "previous point" in the next call LDA CNT \ Set CNT = CNT + STP CLC ADC STP STA CNT RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: FLIP \ Type: Subroutine \ Category: Stardust \ Summary: Reflect the stardust particles in the screen diagonal and redraw \ the stardust field \ \ ------------------------------------------------------------------------------ \ \ Swap the x- and y-coordinates of all the stardust particles and draw the new \ set of particles. Called by LOOK1 when we switch views. \ \ This is a quick way of making the stardust field in the new view feel \ different without having to generate a whole new field. If you look carefully \ at the stardust field when you switch views, you can just about see that the \ new field is a reflection of the previous field in the screen diagonal, i.e. \ in the line from bottom left to top right. This is the line where x = y when \ the origin is in the middle of the screen, and positive x and y are right and \ up, which is the coordinate system we use for stardust). \ \ ****************************************************************************** .FLIP LDY NOSTM \ Set Y to the current number of stardust particles, so \ we can use it as a counter through all the stardust \ --- Mod: Code added for two-player Elite: -----------> BIT drawPlayerView \ If we are drawing player 1's stars, skip the following BPL flip1 TYA \ We are drawing player 2's stars, so use the second CLC \ half of the table ADC #NOST/2 TAY .flip1 \ --- End of added code -------------------------------> .FLL1 LDX SY,Y \ Copy the Y-th particle's y-coordinate from SY+Y into X \ --- Mod: Code removed for two-player Elite: ---------> \LDA SX,Y \ Copy the Y-th particle's x-coordinate from SX+Y into \STA Y1 \ both Y1 and the particle's y-coordinate \STA SY,Y \ \TXA \ Copy the Y-th particle's original y-coordinate into \STA X1 \ both X1 and the particle's x-coordinate, so the x- and \STA SX,Y \ y-coordinates are now swapped and (X1, Y1) contains \ \ the particle's new coordinates \ --- And replaced by: --------------------------------> LDA SX,Y \ Set A to the Y-th particle's x-coordinate from SX+Y PHA \ Set the C flag to bit 6 of the Y-th particle's ASL A \ x-coordinate, so we can move it from the x-coordinate ASL A \ into the y-coordinate PLA AND #%10111111 \ Halve the vertical range for stardust STA Y1 \ Copy the Y-th particle's x-coordinate from SX+Y into STA SY,Y \ both Y1 and the particle's y-coordinate TXA \ Copy the Y-th particle's original y-coordinate into BCC P%+4 \ Set bit 6 of the y-coordinate to the C flag, so we ORA #%01000000 \ reuse bit 6 of the x-coordinate here to double the \ horizontal range of the particle STA X1 \ Copy the Y-th particle's original y-coordinate into STA SX,Y \ both X1 and the particle's x-coordinate, so the x- and \ y-coordinates are now swapped and (X1, Y1) contains \ the particle's new coordinates \ --- End of replacement ------------------------------> LDA SZ,Y \ Fetch the Y-th particle's distance from SZ+Y into ZZ STA ZZ JSR PIXEL2 \ Draw a stardust particle at (X1,Y1) with distance ZZ \ --- Mod: Code added for two-player Elite: -----------> BIT drawPlayerView \ If we are drawing player 1's stars, jump to flip2 to BPL flip2 \ check against zero \ We are drawing player 2's stars, so check against the \ table's halfway point DEY \ Decrement the counter to point to the next particle of \ stardust CPY #NOST/2 \ Loop back to FLL1 until we have moved all the stardust BNE FLL1 \ particles BEQ flip3 \ We are done initialising stars so jump to news3 to \ keep going (this JMP is effectively a BEQ as we just \ passed through a BNE) .flip2 \ --- End of added code -------------------------------> DEY \ Decrement the counter to point to the next particle of \ stardust BNE FLL1 \ Loop back to FLL1 until we have moved all the stardust \ particles \ --- Mod: Code added for two-player Elite: -----------> .flip3 \ --- End of added code -------------------------------> RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: STARS \ Type: Subroutine \ Category: Stardust \ Summary: The main routine for processing the stardust \ \ ------------------------------------------------------------------------------ \ \ Called at the very end of the main flight loop. \ \ ****************************************************************************** .STARS \ --- Mod: Code added for two-player Elite: -----------> JSR SaveShipMovement \ Switch to player 2's movement data JSR GetPlayer2Movement SEC \ Update player 2 stars ROR drawPlayerView ASL NOSTM \ Use star data from second half of table LDX player2VIEW \ Fetch player 2's view JSR dstr1 \ Update stars LSR NOSTM \ Back to first half of table JSR LoadShipMovement \ Switch to player 1's movement data STZ drawPlayerView \ Update player 1 stars \ --- End of added code -------------------------------> LDX VIEW \ Load the current view into X: \ \ 0 = front \ 1 = rear \ 2 = left \ 3 = right \ --- Mod: Code added for two-player Elite: -----------> .dstr1 \ --- End of added code -------------------------------> BEQ STARS1 \ If this is view 0, jump to STARS1 to process the \ stardust for the front view DEX \ If this is view 2 or 3, jump to STARS2 (via ST11) to BNE ST11 \ process the stardust for the left or right views JMP STARS6 \ Otherwise this is the rear view, so jump to STARS6 to \ process the stardust for the rear view .ST11 JMP STARS2 \ Jump to STARS2 for the left or right views, as it's \ too far for the branch instruction above \ ****************************************************************************** \ \ Name: STARS1 \ Type: Subroutine \ Category: Stardust \ Summary: Process the stardust for the front view \ Deep dive: Stardust in the front view \ \ ------------------------------------------------------------------------------ \ \ This moves the stardust towards us according to our speed (so the dust rushes \ past us), and applies our current pitch and roll to each particle of dust, so \ the stardust moves correctly when we steer our ship. \ \ When a stardust particle rushes past us and falls off the side of the screen, \ its memory is recycled as a new particle that's positioned randomly on-screen. \ \ These are the calculations referred to in the commentary: \ \ 1. q = 64 * speed / z_hi \ 2. z = z - speed * 64 \ 3. y = y + |y_hi| * q \ 4. x = x + |x_hi| * q \ \ 5. y = y + alpha * x / 256 \ 6. x = x - alpha * y / 256 \ \ 7. x = x + 2 * (beta * y / 256) ^ 2 \ 8. y = y - beta * 256 \ \ For more information see the associated deep dive. \ \ ****************************************************************************** .STARS1 LDY NOSTM \ Set Y to the current number of stardust particles, so \ we can use it as a counter through all the stardust \ In the following, we're going to refer to the 16-bit \ space coordinates of the current particle of stardust \ (i.e. the Y-th particle) like this: \ \ x = (x_hi x_lo) \ y = (y_hi y_lo) \ z = (z_hi z_lo) \ \ These values are stored in (SX+Y SXL+Y), (SY+Y SYL+Y) \ and (SZ+Y SZL+Y) respectively .STL1 JSR DV42 \ Call DV42 to set the following: \ \ (P R) = 256 * DELTA / z_hi \ = 256 * speed / z_hi \ \ The maximum value returned is P = 2 and R = 128 (see \ DV42 for an explanation) LDA R \ Set A = R, so now: \ \ (P A) = 256 * speed / z_hi LSR P \ Rotate (P A) right by 2 places, which sets P = 0 (as P ROR A \ has a maximum value of 2) and leaves: LSR P \ ROR A \ A = 64 * speed / z_hi ORA #1 \ Make sure A is at least 1, and store it in Q, so we STA Q \ now have result 1 above: \ \ Q = 64 * speed / z_hi LDA SZL,Y \ We now calculate the following: SBC DELT4 \ STA SZL,Y \ (z_hi z_lo) = (z_hi z_lo) - DELT4(1 0) \ \ starting with the low bytes LDA SZ,Y \ And then we do the high bytes STA ZZ \ SBC DELT4+1 \ We also set ZZ to the original value of z_hi, which we STA SZ,Y \ use below to remove the existing particle \ \ So now we have result 2 above: \ \ z = z - DELT4(1 0) \ = z - speed * 64 JSR MLU1 \ Call MLU1 to set: \ \ Y1 = y_hi \ \ (A P) = |y_hi| * Q \ \ So Y1 contains the original value of y_hi, which we \ use below to remove the existing particle \ We now calculate: \ \ (S R) = YY(1 0) = (A P) + y STA YY+1 \ First we do the low bytes with: LDA P \ ADC SYL,Y \ YY+1 = A STA YY \ R = YY = P + y_lo STA R \ \ so we get this: \ \ (? R) = YY(1 0) = (A P) + y_lo LDA Y1 \ And then we do the high bytes with: ADC YY+1 \ STA YY+1 \ S = YY+1 = y_hi + YY+1 STA S \ \ so we get our result: \ \ (S R) = YY(1 0) = (A P) + (y_hi y_lo) \ = |y_hi| * Q + y \ \ which is result 3 above, and (S R) is set to the new \ value of y LDA SX,Y \ Set X1 = A = x_hi STA X1 \ \ So X1 contains the original value of x_hi, which we \ use below to remove the existing particle JSR MLU2 \ Set (A P) = |x_hi| * Q \ We now calculate: \ \ XX(1 0) = (A P) + x STA XX+1 \ First we do the low bytes: LDA P \ ADC SXL,Y \ XX(1 0) = (A P) + x_lo STA XX LDA X1 \ And then we do the high bytes: ADC XX+1 \ STA XX+1 \ XX(1 0) = XX(1 0) + (x_hi 0) \ \ so we get our result: \ \ XX(1 0) = (A P) + x \ = |x_hi| * Q + x \ \ which is result 4 above, and we also have: \ \ A = XX+1 = (|x_hi| * Q + x) / 256 \ \ i.e. A is the new value of x, divided by 256 EOR ALP2+1 \ EOR with the flipped sign of the roll angle alpha, so \ A has the opposite sign to the flipped roll angle \ alpha, i.e. it gets the same sign as alpha JSR MLS1 \ Call MLS1 to calculate: \ \ (A P) = A * ALP1 \ = (x / 256) * alpha JSR ADD \ Call ADD to calculate: \ \ (A X) = (A P) + (S R) \ = (x / 256) * alpha + y \ = y + alpha * x / 256 STA YY+1 \ Set YY(1 0) = (A X) to give: STX YY \ \ YY(1 0) = y + alpha * x / 256 \ \ which is result 5 above, and we also have: \ \ A = YY+1 = y + alpha * x / 256 \ \ i.e. A is the new value of y, divided by 256 EOR ALP2 \ EOR A with the correct sign of the roll angle alpha, \ so A has the opposite sign to the roll angle alpha JSR MLS2 \ Call MLS2 to calculate: \ \ (S R) = XX(1 0) \ = x \ \ (A P) = A * ALP1 \ = -y / 256 * alpha JSR ADD \ Call ADD to calculate: \ \ (A X) = (A P) + (S R) \ = -y / 256 * alpha + x STA XX+1 \ Set XX(1 0) = (A X), which gives us result 6 above: STX XX \ \ x = x - alpha * y / 256 LDX BET1 \ Fetch the pitch magnitude into X LDA YY+1 \ Set A to y_hi and set it to the flipped sign of beta EOR BET2+1 JSR MULTS-2 \ Call MULTS-2 to calculate: \ \ (A P) = X * A \ = -beta * y_hi STA Q \ Store the high byte of the result in Q, so: \ \ Q = -beta * y_hi / 256 JSR MUT2 \ Call MUT2 to calculate: \ \ (S R) = XX(1 0) = x \ \ (A P) = Q * A \ = (-beta * y_hi / 256) * (-beta * y_hi / 256) \ = (beta * y / 256) ^ 2 ASL P \ Double (A P), store the top byte in A and set the C ROL A \ flag to bit 7 of the original A, so this does: STA T \ \ (T P) = (A P) << 1 \ = 2 * (beta * y / 256) ^ 2 LDA #0 \ Set bit 7 in A to the sign bit from the A in the ROR A \ calculation above and apply it to T, so we now have: ORA T \ \ (A P) = (A P) * 2 \ = 2 * (beta * y / 256) ^ 2 \ \ with the doubling retaining the sign of (A P) JSR ADD \ Call ADD to calculate: \ \ (A X) = (A P) + (S R) \ = 2 * (beta * y / 256) ^ 2 + x STA XX+1 \ Store the high byte A in XX+1 TXA \ Store the low byte X in x_lo STA SXL,Y \ So (XX+1 x_lo) now contains: \ \ x = x + 2 * (beta * y / 256) ^ 2 \ \ which is result 7 above LDA YY \ Set (S R) = YY(1 0) = y STA R LDA YY+1 STA S LDA #0 \ Set P = 0 STA P LDA BETA \ Set A = -beta, so: EOR #%10000000 \ \ (A P) = (-beta 0) \ = -beta * 256 JSR PIX1 \ Call PIX1 to calculate the following: \ \ (YY+1 y_lo) = (A P) + (S R) \ = -beta * 256 + y \ \ i.e. y = y - beta * 256, which is result 8 above \ \ PIX1 also draws a particle at (X1, Y1) with distance \ ZZ, which will remove the old stardust particle, as we \ set X1, Y1 and ZZ to the original values for this \ particle during the calculations above \ We now have our newly moved stardust particle at \ x-coordinate (XX+1 x_lo) and y-coordinate (YY+1 y_lo) \ and distance z_hi, so we draw it if it's still on \ screen, otherwise we recycle it as a new bit of \ stardust and draw that LDA XX+1 \ Set X1 and x_hi to the high byte of XX in XX+1, so STA X1 \ the new x-coordinate is in (x_hi x_lo) and the high STA SX,Y \ byte is in X1 AND #%01111111 \ If |x_hi| >= 120 then jump to KILL1 to recycle this CMP #120 \ particle, as it's gone off the side of the screen, BCS KILL1 \ and rejoin at STC1 with the new particle LDA YY+1 \ Set Y1 and y_hi to the high byte of YY in YY+1, so STA SY,Y \ the new x-coordinate is in (y_hi y_lo) and the high STA Y1 \ byte is in Y1 \ --- Mod: Code removed for two-player Elite: ---------> \AND #%01111111 \ If |y_hi| >= 120 then jump to KILL1 to recycle this \CMP #120 \ particle, as it's gone off the top or bottom of the \BCS KILL1 \ screen, and rejoin at STC1 with the new particle \ --- And replaced by: --------------------------------> AND #%01111111 \ If |y_hi| >= 60 then jump to KILL1 to recycle this CMP #60 \ particle, as it's gone off the top or bottom of the BCS KILL1 \ screen, and rejoin at STC1 with the new particle \ --- End of replacement ------------------------------> LDA SZ,Y \ If z_hi < 16 then jump to KILL1 to recycle this CMP #16 \ particle, as it's so close that it's effectively gone BCC KILL1 \ past us, and rejoin at STC1 with the new particle STA ZZ \ Set ZZ to the z-coordinate in z_hi .STC1 JSR PIXEL2 \ Draw a stardust particle at (X1,Y1) with distance ZZ, \ i.e. draw the newly moved particle at (x_hi, y_hi) \ with distance z_hi \ --- Mod: Code added for two-player Elite: -----------> BIT drawPlayerView \ If we are drawing player 1's stars, jump to star1 to BPL star1 \ check against zero \ We are drawing player 2's stars, so check against the \ table's halfway point DEY \ Decrement the loop counter to point to the next \ stardust particle CPY #NOST/2 \ Is this the last star for player 2's view? BEQ P%+5 \ If we have just done the last particle, skip the next \ instruction to return from the subroutine JMP STL1 \ We have more stardust to process, so jump back up to \ STL1 for the next particle RTS \ Return from the subroutine .star1 \ --- End of added code -------------------------------> DEY \ Decrement the loop counter to point to the next \ stardust particle BEQ P%+5 \ If we have just done the last particle, skip the next \ instruction to return from the subroutine JMP STL1 \ We have more stardust to process, so jump back up to \ STL1 for the next particle RTS \ Return from the subroutine .KILL1 \ Our particle of stardust just flew past us, so let's \ recycle that particle, starting it at a random \ position that isn't too close to the centre point JSR DORND \ Set A and X to random numbers \ --- Mod: Code added for two-player Elite: -----------> AND #%10111111 \ Halve the vertical range for stardust \ --- End of added code -------------------------------> ORA #4 \ Make sure A is at least 4 and store it in Y1 and y_hi, STA Y1 \ so the new particle starts at least 4 pixels above or STA SY,Y \ below the centre of the screen JSR DORND \ Set A and X to random numbers ORA #8 \ Make sure A is at least 8 and store it in X1 and x_hi, STA X1 \ so the new particle starts at least 8 pixels either STA SX,Y \ side of the centre of the screen JSR DORND \ Set A and X to random numbers ORA #144 \ Make sure A is at least 144 and store it in ZZ and STA SZ,Y \ z_hi so the new particle starts in the far distance STA ZZ LDA Y1 \ Set A to the new value of y_hi. This has no effect as \ STC1 starts with a jump to PIXEL2, which starts with a \ LDA instruction JMP STC1 \ Jump up to STC1 to draw this new particle \ ****************************************************************************** \ \ Name: STARS6 \ Type: Subroutine \ Category: Stardust \ Summary: Process the stardust for the rear view \ Deep dive: Stardust in the front view \ \ ------------------------------------------------------------------------------ \ \ This routine is very similar to STARS1, which processes stardust for the front \ view. The main difference is that the direction of travel is reversed, so the \ signs in the calculations are different, as well as the order of the first \ batch of calculations. \ \ When a stardust particle falls away into the far distance, it is removed from \ the screen and its memory is recycled as a new particle, positioned randomly \ along one of the four edges of the screen. \ \ These are the calculations referred to in the commentary: \ \ 1. q = 64 * speed / z_hi \ 2. z = z - speed * 64 \ 3. y = y + |y_hi| * q \ 4. x = x + |x_hi| * q \ \ 5. y = y + alpha * x / 256 \ 6. x = x - alpha * y / 256 \ \ 7. x = x + 2 * (beta * y / 256) ^ 2 \ 8. y = y - beta * 256 \ \ For more information see the associated deep dive. \ \ ****************************************************************************** .STARS6 LDY NOSTM \ Set Y to the current number of stardust particles, so \ we can use it as a counter through all the stardust .STL6 JSR DV42 \ Call DV42 to set the following: \ \ (P R) = 256 * DELTA / z_hi \ = 256 * speed / z_hi \ \ The maximum value returned is P = 2 and R = 128 (see \ DV42 for an explanation) LDA R \ Set A = R, so now: \ \ (P A) = 256 * speed / z_hi LSR P \ Rotate (P A) right by 2 places, which sets P = 0 (as P ROR A \ has a maximum value of 2) and leaves: LSR P \ ROR A \ A = 64 * speed / z_hi ORA #1 \ Make sure A is at least 1, and store it in Q, so we STA Q \ now have result 1 above: \ \ Q = 64 * speed / z_hi LDA SX,Y \ Set X1 = A = x_hi STA X1 \ \ So X1 contains the original value of x_hi, which we \ use below to remove the existing particle JSR MLU2 \ Set (A P) = |x_hi| * Q \ We now calculate: \ \ XX(1 0) = x - (A P) STA XX+1 \ First we do the low bytes: LDA SXL,Y \ SBC P \ XX(1 0) = x_lo - (A P) STA XX LDA X1 \ And then we do the high bytes: SBC XX+1 \ STA XX+1 \ XX(1 0) = (x_hi 0) - XX(1 0) \ \ so we get our result: \ \ XX(1 0) = x - (A P) \ = x - |x_hi| * Q \ \ which is result 2 above, and we also have: JSR MLU1 \ Call MLU1 to set: \ \ Y1 = y_hi \ \ (A P) = |y_hi| * Q \ \ So Y1 contains the original value of y_hi, which we \ use below to remove the existing particle \ We now calculate: \ \ (S R) = YY(1 0) = y - (A P) STA YY+1 \ First we do the low bytes with: LDA SYL,Y \ SBC P \ YY+1 = A STA YY \ R = YY = y_lo - P STA R \ \ so we get this: \ \ (? R) = YY(1 0) = y_lo - (A P) LDA Y1 \ And then we do the high bytes with: SBC YY+1 \ STA YY+1 \ S = YY+1 = y_hi - YY+1 STA S \ \ so we get our result: \ \ (S R) = YY(1 0) = (y_hi y_lo) - (A P) \ = y - |y_hi| * Q \ \ which is result 3 above, and (S R) is set to the new \ value of y LDA SZL,Y \ We now calculate the following: ADC DELT4 \ STA SZL,Y \ (z_hi z_lo) = (z_hi z_lo) + DELT4(1 0) \ \ starting with the low bytes LDA SZ,Y \ And then we do the high bytes STA ZZ \ ADC DELT4+1 \ We also set ZZ to the original value of z_hi, which we STA SZ,Y \ use below to remove the existing particle \ \ So now we have result 4 above: \ \ z = z + DELT4(1 0) \ = z + speed * 64 LDA XX+1 \ EOR x with the correct sign of the roll angle alpha, EOR ALP2 \ so A has the opposite sign to the roll angle alpha JSR MLS1 \ Call MLS1 to calculate: \ \ (A P) = A * ALP1 \ = (-x / 256) * alpha JSR ADD \ Call ADD to calculate: \ \ (A X) = (A P) + (S R) \ = (-x / 256) * alpha + y \ = y - alpha * x / 256 STA YY+1 \ Set YY(1 0) = (A X) to give: STX YY \ \ YY(1 0) = y - alpha * x / 256 \ \ which is result 5 above, and we also have: \ \ A = YY+1 = y - alpha * x / 256 \ \ i.e. A is the new value of y, divided by 256 EOR ALP2+1 \ EOR with the flipped sign of the roll angle alpha, so \ A has the opposite sign to the flipped roll angle \ alpha, i.e. it gets the same sign as alpha JSR MLS2 \ Call MLS2 to calculate: \ \ (S R) = XX(1 0) \ = x \ \ (A P) = A * ALP1 \ = y / 256 * alpha JSR ADD \ Call ADD to calculate: \ \ (A X) = (A P) + (S R) \ = y / 256 * alpha + x STA XX+1 \ Set XX(1 0) = (A X), which gives us result 6 above: STX XX \ \ x = x + alpha * y / 256 LDA YY+1 \ Set A to y_hi and set it to the flipped sign of beta EOR BET2+1 LDX BET1 \ Fetch the pitch magnitude into X JSR MULTS-2 \ Call MULTS-2 to calculate: \ \ (A P) = X * A \ = beta * y_hi STA Q \ Store the high byte of the result in Q, so: \ \ Q = beta * y_hi / 256 LDA XX+1 \ Set S = x_hi STA S EOR #%10000000 \ Flip the sign of A, so A now contains -x JSR MUT1 \ Call MUT1 to calculate: \ \ R = XX = x_lo \ \ (A P) = Q * A \ = (beta * y_hi / 256) * (-beta * y_hi / 256) \ = (-beta * y / 256) ^ 2 ASL P \ Double (A P), store the top byte in A and set the C ROL A \ flag to bit 7 of the original A, so this does: STA T \ \ (T P) = (A P) << 1 \ = 2 * (-beta * y / 256) ^ 2 LDA #0 \ Set bit 7 in A to the sign bit from the A in the ROR A \ calculation above and apply it to T, so we now have: ORA T \ \ (A P) = -2 * (beta * y / 256) ^ 2 \ \ with the doubling retaining the sign of (A P) JSR ADD \ Call ADD to calculate: \ \ (A X) = (A P) + (S R) \ = -2 * (beta * y / 256) ^ 2 + x STA XX+1 \ Store the high byte A in XX+1 TXA \ Store the low byte X in x_lo STA SXL,Y \ So (XX+1 x_lo) now contains: \ \ x = x - 2 * (beta * y / 256) ^ 2 \ \ which is result 7 above LDA YY \ Set (S R) = YY(1 0) = y STA R LDA YY+1 STA S LDA #0 \ Set P = 0 STA P LDA BETA \ Set A = beta, so (A P) = (beta 0) = beta * 256 JSR PIX1 \ Call PIX1 to calculate the following: \ \ (YY+1 y_lo) = (A P) + (S R) \ = beta * 256 + y \ \ i.e. y = y + beta * 256, which is result 8 above \ \ PIX1 also draws a particle at (X1, Y1) with distance \ ZZ, which will remove the old stardust particle, as we \ set X1, Y1 and ZZ to the original values for this \ particle during the calculations above \ We now have our newly moved stardust particle at \ x-coordinate (XX+1 x_lo) and y-coordinate (YY+1 y_lo) \ and distance z_hi, so we draw it if it's still on \ screen, otherwise we recycle it as a new bit of \ stardust and draw that LDA XX+1 \ Set X1 and x_hi to the high byte of XX in XX+1, so STA X1 \ the new x-coordinate is in (x_hi x_lo) and the high STA SX,Y \ byte is in X1 LDA YY+1 \ Set Y1 and y_hi to the high byte of YY in YY+1, so STA SY,Y \ the new x-coordinate is in (y_hi y_lo) and the high STA Y1 \ byte is in Y1 AND #%01111111 \ If |y_hi| >= 110 then jump to KILL6 to recycle this CMP #110 \ particle, as it's gone off the top or bottom of the BCS KILL6 \ screen, and rejoin at STC6 with the new particle LDA SZ,Y \ If z_hi >= 160 then jump to KILL6 to recycle this CMP #160 \ particle, as it's so far away that it's too far to BCS KILL6 \ see, and rejoin at STC1 with the new particle STA ZZ \ Set ZZ to the z-coordinate in z_hi .STC6 JSR PIXEL2 \ Draw a stardust particle at (X1,Y1) with distance ZZ, \ i.e. draw the newly moved particle at (x_hi, y_hi) \ with distance z_hi \ --- Mod: Code added for two-player Elite: -----------> BIT drawPlayerView \ If we are drawing player 1's stars, jump to star2 to BPL star2 \ check against zero \ We are drawing player 2's stars, so check against the \ table's halfway point DEY \ Decrement the loop counter to point to the next \ stardust particle CPY #NOST/2 \ Is this the last star for player 2's view? BEQ P%+5 \ If we have just done the last particle, skip the next \ instruction to return from the subroutine JMP STL6 \ We have more stardust to process, so jump back up to \ STL6 for the next particle RTS \ Return from the subroutine .star2 \ --- End of added code -------------------------------> DEY \ Decrement the loop counter to point to the next \ stardust particle BEQ ST3 \ If we have just done the last particle, skip the next \ instruction to return from the subroutine JMP STL6 \ We have more stardust to process, so jump back up to \ STL6 for the next particle .ST3 RTS \ Return from the subroutine .KILL6 JSR DORND \ Set A and X to random numbers AND #%01111111 \ Clear the sign bit of A to get |A| ADC #10 \ Make sure A is at least 10 and store it in z_hi and STA SZ,Y \ ZZ, so the new particle starts close to us STA ZZ LSR A \ Divide A by 2 and randomly set the C flag BCS ST4 \ Jump to ST4 half the time LSR A \ Randomly set the C flag again LDA #252 \ Set A to either +126 or -126 (252 >> 1) depending on ROR A \ the C flag, as this is a sign-magnitude number with \ the C flag rotated into its sign bit STA X1 \ Set x_hi and X1 to A, so this particle starts on STA SX,Y \ either the left or right edge of the screen JSR DORND \ Set A and X to random numbers \ --- Mod: Code added for two-player Elite: -----------> AND #%10111111 \ Halve the vertical range for stardust \ --- End of added code -------------------------------> STA Y1 \ Set y_hi and Y1 to random numbers, so the particle STA SY,Y \ starts anywhere along either the left or right edge JMP STC6 \ Jump up to STC6 to draw this new particle .ST4 JSR DORND \ Set A and X to random numbers STA X1 \ Set x_hi and X1 to random numbers, so the particle STA SX,Y \ starts anywhere along the x-axis LSR A \ Randomly set the C flag LDA #230 \ Set A to either +115 or -115 (230 >> 1) depending on ROR A \ the C flag, as this is a sign-magnitude number with \ the C flag rotated into its sign bit \ --- Mod: Code added for two-player Elite: -----------> AND #%10111111 \ Halve the vertical range for stardust \ --- End of added code -------------------------------> STA Y1 \ Set y_hi and Y1 to A, so the particle starts anywhere STA SY,Y \ along either the top or bottom edge of the screen BNE STC6 \ Jump up to STC6 to draw this new particle (this BNE is \ effectively a JMP as A will never be zero) \ ****************************************************************************** \ \ Name: MAS1 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Add an orientation vector coordinate to an INWK coordinate \ Deep dive: The space station safe zone \ \ ------------------------------------------------------------------------------ \ \ Add a doubled nosev vector coordinate, e.g. (nosev_y_hi nosev_y_lo) * 2, to \ an INWK coordinate, e.g. (x_sign x_hi x_lo), storing the result in the INWK \ coordinate. The axes used in each side of the addition are specified by the \ arguments X and Y. \ \ In the comments below, we document the routine as if we are doing the \ following, i.e. if X = 0 and Y = 11: \ \ (x_sign x_hi x_lo) = (x_sign x_hi x_lo) + (nosev_y_hi nosev_y_lo) * 2 \ \ as that way the variable names in the comments contain "x" and "y" to match \ the registers that specify the vector axis to use. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The coordinate to add, as follows: \ \ * If X = 0, add (x_sign x_hi x_lo) \ * If X = 3, add (y_sign y_hi y_lo) \ * If X = 6, add (z_sign z_hi z_lo) \ \ Y The vector to add, as follows: \ \ * If Y = 9, add (nosev_x_hi nosev_x_lo) \ * If Y = 11, add (nosev_y_hi nosev_y_lo) \ * If Y = 13, add (nosev_z_hi nosev_z_lo) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A The highest byte of the result with the sign cleared \ (e.g. |x_sign| when X = 0, etc.) \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ MA9 Contains an RTS \ \ ****************************************************************************** .MAS1 LDA INWK,Y \ Set K(2 1) = (nosev_y_hi nosev_y_lo) * 2 ASL A STA K+1 LDA INWK+1,Y ROL A STA K+2 LDA #0 \ Set K+3 bit 7 to the C flag, so the sign bit of the ROR A \ above result goes into K+3 STA K+3 JSR MVT3 \ Add (x_sign x_hi x_lo) to K(3 2 1) STA INWK+2,X \ Store the sign of the result in x_sign LDY K+1 \ Store K(2 1) in (x_hi x_lo) STY INWK,X LDY K+2 STY INWK+1,X AND #%01111111 \ Set A to the sign byte with the sign cleared, \ i.e. |x_sign| when X = 0 .MA9 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MAS2 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Calculate a cap on the maximum distance to the planet or sun \ \ ------------------------------------------------------------------------------ \ \ Given a value in Y that points to the start of a ship data block as an offset \ from K%, calculate the following: \ \ A = A OR x_sign OR y_sign OR z_sign \ \ and clear the sign bit of the result. The K% workspace contains the ship data \ blocks, so the offset in Y must be 0 or a multiple of NI% (as each block in \ K% contains NI% bytes). \ \ The result effectively contains a maximum cap of the three values (though it \ might not be one of the three input values - it's just guaranteed to be \ larger than all of them). \ \ If Y = 0 and A = 0, then this calculates the maximum cap of the highest byte \ containing the distance to the planet, as K%+2 = x_sign, K%+5 = y_sign and \ K%+8 = z_sign (the first slot in the K% workspace represents the planet). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ Y The offset from K% for the start of the ship data block \ to use \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A A OR K%+2+Y OR K%+5+Y OR K%+8+Y, with bit 7 cleared \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ m Do not include A in the calculation \ \ ****************************************************************************** .m LDA #0 \ Set A = 0 and fall through into MAS2 to calculate the \ OR of the three bytes at K%+2+Y, K%+5+Y and K%+8+Y .MAS2 ORA K%+2,Y \ Set A = A OR x_sign OR y_sign OR z_sign ORA K%+5,Y ORA K%+8,Y AND #%01111111 \ Clear bit 7 in A RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MAS3 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate A = x_hi^2 + y_hi^2 + z_hi^2 in the K% block \ \ ------------------------------------------------------------------------------ \ \ Given a value in Y that points to the start of a ship data block as an offset \ from K%, calculate the following: \ \ (A ?) = x_hi^2 + y_hi^2 + z_hi^2 \ \ returning A = &FF if the calculation overflows a one-byte result. The K% \ workspace contains the ship data blocks, so the offset in Y must be 0 or a \ multiple of NI% (as each block in K% contains NI% bytes). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ Y The offset from K% for the start of the ship data block \ to use \ \ Returns \ \ A The high byte of x_hi^2 + y_hi^2 + z_hi^2 \ \ C flag The overflow status (i.e. did the result fit into one \ byte): \ \ * Clear if the calculation didn't overflow \ \ * Set if the calculation overflowed (in which case A \ is set to &FF) \ \ ****************************************************************************** .MAS3 LDA K%+1,Y \ Set (A P) = x_hi * x_hi JSR SQUA2 STA R \ Store A (high byte of result) in R LDA K%+4,Y \ Set (A P) = y_hi * y_hi JSR SQUA2 ADC R \ Add A (high byte of second result) to R BCS MA30 \ If the addition of the two high bytes caused a carry \ (i.e. they overflowed), jump to MA30 to return A = &FF STA R \ Store A (sum of the two high bytes) in R LDA K%+7,Y \ Set (A P) = z_hi * z_hi JSR SQUA2 ADC R \ Add A (high byte of third result) to R, so R now \ contains the high byte of the entire sum, i.e. of \ x_hi^2 + y_hi^2 + z_hi^2 BCC P%+4 \ If there is no carry, skip the following instruction \ to return straight from the subroutine .MA30 LDA #&FF \ The calculation has overflowed, so set A = &FF RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: STATUS \ Type: Subroutine \ Category: Status \ Summary: Show the Status Mode screen (red key f8) \ Deep dive: Combat rank \ \ ****************************************************************************** .wearedocked \ We call this from STATUS below if we are docked LDA #205 \ Print extended token 205 ("DOCKED") and return from JSR DETOK \ the subroutine using a tail call JSR TT67 \ Print a newline JMP st6+3 \ Jump down to st6+3, to print recursive token 125 and \ continue to the rest of the Status Mode screen .st4 \ We call this from st5 below with the high byte of the \ kill tally in A, which is non-zero, and want to return \ with the following in X, depending on our rating: \ \ Competent = 6 \ Dangerous = 7 \ Deadly = 8 \ Elite = 9 \ \ The high bytes of the top tier ratings are as follows, \ so this a relatively simple calculation: \ \ Competent = 1 \ Dangerous = 2 to 9 \ Deadly = 10 to 24 \ Elite = 25 and up LDX #9 \ Set X to 9 for an Elite rating CMP #25 \ If A >= 25, jump to st3 to print out our rating, as we BCS st3 \ are Elite DEX \ Decrement X to 8 for a Deadly rating CMP #10 \ If A >= 10, jump to st3 to print out our rating, as we BCS st3 \ are Deadly DEX \ Decrement X to 7 for a Dangerous rating CMP #2 \ If A >= 2, jump to st3 to print out our rating, as we BCS st3 \ are Dangerous DEX \ Decrement X to 6 for a Competent rating BNE st3 \ Jump to st3 to print out our rating, as we are \ Competent (this BNE is effectively a JMP as A will \ never be zero) .STATUS LDA #8 \ Clear the top part of the screen, draw a border box, JSR TRADEMODE \ and set up a printable trading screen with a view type \ in QQ11 of 8 (Status Mode screen) JSR TT111 \ Select the system closest to galactic coordinates \ (QQ9, QQ10) LDA #7 \ Move the text cursor to column 7 JSR DOXC LDA #126 \ Print recursive token 126, which prints the top JSR NLIN3 \ four lines of the Status Mode screen: \ \ COMMANDER {commander name} \ \ \ Present System : {current system name} \ Hyperspace System : {selected system name} \ Condition : \ \ and draw a horizontal line at pixel row 19 to box \ in the title LDA #15 \ This instruction is left over from the cassette \ version, where it sets the token number for the \ "DOCKED" text, but it has no effect in this version \ as the "DOCKED" text is now an extended token LDY QQ12 \ Fetch the docked status from QQ12, and if we are BNE wearedocked \ docked, jump to wearedocked LDA #230 \ Otherwise we are in space, so start off by setting A \ to token 70 ("GREEN") LDY JUNK \ Set Y to the number of junk items in our local bubble \ of universe (where junk is asteroids, canisters, \ escape pods and so on) LDX FRIN+2,Y \ The ship slots at FRIN are ordered with the first two \ slots reserved for the planet and sun/space station, \ and then any ships, so if the slot at FRIN+2+Y is not \ empty (i.e. is non-zero), then that means the number \ of non-asteroids in the vicinity is at least 1 BEQ st6 \ So if X = 0, there are no ships in the vicinity, so \ jump to st6 to print "Green" for our ship's condition LDY ENERGY \ Otherwise we have ships in the vicinity, so we load \ our energy levels into Y CPY #128 \ Set the C flag if Y >= 128, so C is set if we have \ more than half of our energy banks charged ADC #1 \ Add 1 + C to A, so if C is not set (i.e. we have low \ energy levels) then A is set to token 231 ("RED"), \ and if C is set (i.e. we have healthy energy levels) \ then A is set to token 232 ("YELLOW") .st6 JSR plf \ Print the text token in A (which contains our ship's \ condition) followed by a newline LDA #125 \ Print recursive token 125, which prints the next JSR spc \ three lines of the Status Mode screen: \ \ Fuel: {fuel level} Light Years \ Cash: {cash} Cr \ Legal Status: \ \ followed by a space LDA #19 \ Set A to token 133 ("CLEAN") LDY FIST \ Fetch our legal status, and if it is 0, we are clean, BEQ st5 \ so jump to st5 to print "Clean" CPY #50 \ Set the C flag if Y >= 50, so C is set if we have \ a legal status of 50+ (i.e. we are a fugitive) ADC #1 \ Add 1 + C to A, so if C is not set (i.e. we have a \ legal status between 1 and 49) then A is set to token \ 134 ("OFFENDER"), and if C is set (i.e. we have a \ legal status of 50+) then A is set to token 135 \ ("FUGITIVE") .st5 JSR plf \ Print the text token in A (which contains our legal \ status) followed by a newline LDA #16 \ Print recursive token 130 ("RATING:") followed by a JSR spc \ space LDA TALLY+1 \ Fetch the high byte of the kill tally, and if it is BNE st4 \ not zero, then we have more than 256 kill points, so \ jump to st4 to work out whether we are Competent, \ Dangerous, Deadly or Elite \ Otherwise we have fewer than 256 kill pointss, so we \ are one of Harmless, Mostly Harmless, Poor, Average, \ Above Average or Competent TAX \ Set X to 0 (as A is 0) LDA TALLY \ Set A to the lower byte of tally, with bits 0 and 1 LSR A \ shifted off to the right, so we can now analyse bits LSR A \ 2 to 7 by shifting A to the right one bit at a time \ We now loop through bits 2 to 7, shifting each of them \ off the end of A until there are no set bits left, and \ incrementing X before each shift, so at the end of the \ process, X contains the position of the leftmost 1 in \ A. Looking at the rank values in TALLY: \ \ Harmless = %00000000 to %00000111 \ Mostly Harmless = %00001000 to %00001111 \ Poor = %00010000 to %00011111 \ Average = %00100000 to %00111111 \ Above Average = %01000000 to %01111111 \ Competent = %10000000 to %11111111 \ \ we can see that the values returned by this process \ are: \ \ Harmless = 1 \ Mostly Harmless = 2 \ Poor = 3 \ Average = 4 \ Above Average = 5 \ Competent = 6 INX \ Increment X to count the number of shifts LSR A \ Shift A to the right BNE P%-2 \ Keep looping back two instructions (i.e. to the INX \ instruction) until A = 0, which means there are no set \ bits left in A .st3 TXA \ A now contains our rating as a value of 1 to 9, so \ transfer X to A, so we can print it out CLC \ Print recursive token 135 + A, which will be in the ADC #21 \ range 136 ("HARMLESS") to 144 ("---- E L I T E ----") JSR plf \ followed by a newline LDA #18 \ Print recursive token 132, which prints the next bit JSR plf2 \ of the Status Mode screen: \ \ EQUIPMENT: \ \ followed by a newline and an indent of 6 characters LDA CRGO \ If our ship's cargo capacity is < 26 (i.e. we do not CMP #26 \ have a cargo bay extension), skip the following two BCC P%+7 \ instructions LDA #107 \ We do have a cargo bay extension, so print recursive JSR plf2 \ token 107 ("LARGE CARGO{sentence case} BAY"), followed \ by a newline and an indent of 6 characters LDA BST \ If we don't have fuel scoops fitted, skip the BEQ P%+7 \ following two instructions LDA #111 \ We do have fuel scoops fitted, so print recursive JSR plf2 \ token 111 ("FUEL SCOOPS"), followed by a newline and \ an indent of 6 characters LDA ECM \ If we don't have an E.C.M. fitted, skip the following BEQ P%+7 \ two instructions LDA #108 \ We do have an E.C.M. fitted, so print recursive token JSR plf2 \ 108 ("E.C.M.SYSTEM"), followed by a newline and an \ indent of 6 characters LDA #113 \ We now cover the four pieces of equipment whose flags STA XX4 \ are stored in BOMB through BOMB+3, and whose names \ correspond with text tokens 113 through 116: \ \ BOMB+0 = BOMB = token 113 = Energy bomb \ BOMB+1 = ENGY = token 114 = Energy unit \ BOMB+2 = DKCMP = token 115 = Docking computer \ BOMB+3 = GHYP = token 116 = Galactic hyperdrive \ \ We can print these out using a loop, so we set XX4 to \ 113 as a counter (and we also set A as well, to pass \ through to plf2) .stqv TAY \ Fetch byte BOMB+0 through BOMB+4 for values of XX4 LDX BOMB-113,Y \ from 113 through 117 BEQ P%+5 \ If it is zero then we do not own that piece of \ equipment, so skip the next instruction JSR plf2 \ Print the recursive token in A from 113 ("ENERGY \ BOMB") through 116 ("GALACTIC HYPERSPACE "), followed \ by a newline and an indent of 6 characters INC XX4 \ Increment the counter (and A as well) LDA XX4 CMP #117 \ If A < 117, loop back up to stqv to print the next BCC stqv \ piece of equipment LDX #0 \ Now to print our ship's lasers, so set a counter in X \ to count through the four views (0 = front, 1 = rear, \ 2 = left, 3 = right) .st STX CNT \ Store the view number in CNT LDY LASER,X \ Fetch the laser power for view X, and if we do not BEQ st1 \ have a laser fitted to that view, jump to st1 to move \ on to the next one TXA \ Print recursive token 96 + X, which will print from 96 CLC \ ("FRONT") through to 99 ("RIGHT"), followed by a space ADC #96 JSR spc LDA #103 \ Set A to token 103 ("PULSE LASER") LDX CNT \ Retrieve the view number from CNT that we stored above LDY LASER,X \ Set Y = the laser power for view X CPY #128+POW \ If the laser power for view X is not #POW+128 (beam BNE P%+4 \ laser), skip the next LDA instruction LDA #104 \ This sets A = 104 if the laser in view X is a beam \ laser (token 104 is "BEAM LASER") CPY #Armlas \ If the laser power for view X is not #Armlas (military BNE P%+4 \ laser), skip the next LDA instruction LDA #117 \ This sets A = 117 if the laser in view X is a military \ laser (token 117 is "MILITARY LASER") CPY #Mlas \ If the laser power for view X is not #Mlas (mining BNE P%+4 \ laser), skip the next LDA instruction LDA #118 \ This sets A = 118 if the laser in view X is a mining \ laser (token 118 is "MINING LASER") JSR plf2 \ Print the text token in A (which contains the laser \ type) followed by a newline and an indent of 6 \ characters .st1 LDX CNT \ Increment the counter in X and CNT to point to the INX \ next view CPX #4 \ If this isn't the last of the four views, jump back up BCC st \ to st to print out the next one RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: plf2 \ Type: Subroutine \ Category: Text \ Summary: Print text followed by a newline and indent of 6 characters \ \ ------------------------------------------------------------------------------ \ \ Print a text token followed by a newline, and indent the next line to text \ column 6. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The text token to be printed \ \ ****************************************************************************** .plf2 JSR plf \ Print the text token in A followed by a newline LDA #6 \ Move the text cursor to column 6 and return from the JMP DOXC \ subroutine using a tail call \ ****************************************************************************** \ \ Name: MVT3 \ Type: Subroutine \ Category: Moving \ Summary: Calculate K(3 2 1) = (x_sign x_hi x_lo) + K(3 2 1) \ \ ------------------------------------------------------------------------------ \ \ Add an INWK position coordinate - i.e. x, y or z - to K(3 2 1), like this: \ \ K(3 2 1) = (x_sign x_hi x_lo) + K(3 2 1) \ \ The INWK coordinate to add to K(3 2 1) is specified by X. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The coordinate to add to K(3 2 1), as follows: \ \ * If X = 0, add (x_sign x_hi x_lo) \ \ * If X = 3, add (y_sign y_hi y_lo) \ \ * If X = 6, add (z_sign z_hi z_lo) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A Contains a copy of the high byte of the result, K+3 \ \ X X is preserved \ \ ****************************************************************************** .MVT3 LDA K+3 \ Set S = K+3 STA S AND #%10000000 \ Set T = sign bit of K(3 2 1) STA T EOR INWK+2,X \ If x_sign has a different sign to K(3 2 1), jump to BMI MV13 \ MV13 to process the addition as a subtraction LDA K+1 \ Set K(3 2 1) = K(3 2 1) + (x_sign x_hi x_lo) CLC \ starting with the low bytes ADC INWK,X STA K+1 LDA K+2 \ Then the middle bytes ADC INWK+1,X STA K+2 LDA K+3 \ And finally the high bytes ADC INWK+2,X AND #%01111111 \ Setting the sign bit of K+3 to T, the original sign ORA T \ of K(3 2 1) STA K+3 RTS \ Return from the subroutine .MV13 LDA S \ Set S = |K+3| (i.e. K+3 with the sign bit cleared) AND #%01111111 STA S LDA INWK,X \ Set K(3 2 1) = (x_sign x_hi x_lo) - K(3 2 1) SEC \ starting with the low bytes SBC K+1 STA K+1 LDA INWK+1,X \ Then the middle bytes SBC K+2 STA K+2 LDA INWK+2,X \ And finally the high bytes, doing A = |x_sign| - |K+3| AND #%01111111 \ and setting the C flag for testing below SBC S ORA #%10000000 \ Set the sign bit of K+3 to the opposite sign of T, EOR T \ i.e. the opposite sign to the original K(3 2 1) STA K+3 BCS MV14 \ If the C flag is set, i.e. |x_sign| >= |K+3|, then \ the sign of K(3 2 1). In this case, we want the \ result to have the same sign as the largest argument, \ which is (x_sign x_hi x_lo), which we know has the \ opposite sign to K(3 2 1), and that's what we just set \ the sign of K(3 2 1) to... so we can jump to MV14 to \ return from the subroutine LDA #1 \ We need to swap the sign of the result in K(3 2 1), SBC K+1 \ which we do by calculating 0 - K(3 2 1), which we can STA K+1 \ do with 1 - C - K(3 2 1), as we know the C flag is \ clear. We start with the low bytes LDA #0 \ Then the middle bytes SBC K+2 STA K+2 LDA #0 \ And finally the high bytes SBC K+3 AND #%01111111 \ Set the sign bit of K+3 to the same sign as T, ORA T \ i.e. the same sign as the original K(3 2 1), as STA K+3 \ that's the largest argument .MV14 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MVS5 \ Type: Subroutine \ Category: Moving \ Summary: Apply a 3.6 degree pitch or roll to an orientation vector \ Deep dive: Orientation vectors \ Pitching and rolling by a fixed angle \ \ ------------------------------------------------------------------------------ \ \ Pitch or roll a ship by a small, fixed amount (1/16 radians, or 3.6 degrees), \ in a specified direction, by rotating the orientation vectors. The vectors to \ rotate are given in X and Y, and the direction of the rotation is given in \ RAT2. The calculation is as follows: \ \ * If the direction is positive: \ \ X = X * (1 - 1/512) + Y / 16 \ Y = Y * (1 - 1/512) - X / 16 \ \ * If the direction is negative: \ \ X = X * (1 - 1/512) - Y / 16 \ Y = Y * (1 - 1/512) + X / 16 \ \ So if X = 15 (roofv_x), Y = 21 (sidev_x) and RAT2 is positive, it does this: \ \ roofv_x = roofv_x * (1 - 1/512) + sidev_x / 16 \ sidev_x = sidev_x * (1 - 1/512) - roofv_x / 16 \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The first vector to rotate: \ \ * If X = 15, rotate roofv_x \ \ * If X = 17, rotate roofv_y \ \ * If X = 19, rotate roofv_z \ \ * If X = 21, rotate sidev_x \ \ * If X = 23, rotate sidev_y \ \ * If X = 25, rotate sidev_z \ \ Y The second vector to rotate: \ \ * If Y = 9, rotate nosev_x \ \ * If Y = 11, rotate nosev_y \ \ * If Y = 13, rotate nosev_z \ \ * If Y = 21, rotate sidev_x \ \ * If Y = 23, rotate sidev_y \ \ * If Y = 25, rotate sidev_z \ \ RAT2 The direction of the pitch or roll to perform, positive \ or negative (i.e. the sign of the roll or pitch counter \ in bit 7) \ \ ****************************************************************************** .MVS5 LDA INWK+1,X \ Fetch roofv_x_hi, clear the sign bit, divide by 2 and AND #%01111111 \ store in T, so: LSR A \ STA T \ T = |roofv_x_hi| / 2 \ = |roofv_x| / 512 \ \ The above is true because: \ \ |roofv_x| = |roofv_x_hi| * 256 + roofv_x_lo \ \ so: \ \ |roofv_x| / 512 = |roofv_x_hi| * 256 / 512 \ + roofv_x_lo / 512 \ = |roofv_x_hi| / 2 LDA INWK,X \ Now we do the following subtraction: SEC \ SBC T \ (S R) = (roofv_x_hi roofv_x_lo) - |roofv_x| / 512 STA R \ = (1 - 1/512) * roofv_x \ \ by doing the low bytes first LDA INWK+1,X \ And then the high bytes (the high byte of the right SBC #0 \ side of the subtraction being 0) STA S LDA INWK,Y \ Set P = nosev_x_lo STA P LDA INWK+1,Y \ Fetch the sign of nosev_x_hi (bit 7) and store in T AND #%10000000 STA T LDA INWK+1,Y \ Fetch nosev_x_hi into A and clear the sign bit, so AND #%01111111 \ A = |nosev_x_hi| LSR A \ Set (A P) = (A P) / 16 ROR P \ = |nosev_x_hi nosev_x_lo| / 16 LSR A \ = |nosev_x| / 16 ROR P LSR A ROR P LSR A ROR P ORA T \ Set the sign of A to the sign in T (i.e. the sign of \ the original nosev_x), so now: \ \ (A P) = nosev_x / 16 EOR RAT2 \ Give it the sign as if we multiplied by the direction \ by the pitch or roll direction STX Q \ Store the value of X so it can be restored after the \ call to ADD JSR ADD \ (A X) = (A P) + (S R) \ = +/-nosev_x / 16 + (1 - 1/512) * roofv_x STA K+1 \ Set K(1 0) = (1 - 1/512) * roofv_x +/- nosev_x / 16 STX K LDX Q \ Restore the value of X from before the call to ADD LDA INWK+1,Y \ Fetch nosev_x_hi, clear the sign bit, divide by 2 and AND #%01111111 \ store in T, so: LSR A \ STA T \ T = |nosev_x_hi| / 2 \ = |nosev_x| / 512 LDA INWK,Y \ Now we do the following subtraction: SEC \ SBC T \ (S R) = (nosev_x_hi nosev_x_lo) - |nosev_x| / 512 STA R \ = (1 - 1/512) * nosev_x \ \ by doing the low bytes first LDA INWK+1,Y \ And then the high bytes (the high byte of the right SBC #0 \ side of the subtraction being 0) STA S LDA INWK,X \ Set P = roofv_x_lo STA P LDA INWK+1,X \ Fetch the sign of roofv_x_hi (bit 7) and store in T AND #%10000000 STA T LDA INWK+1,X \ Fetch roofv_x_hi into A and clear the sign bit, so AND #%01111111 \ A = |roofv_x_hi| LSR A \ Set (A P) = (A P) / 16 ROR P \ = |roofv_x_hi roofv_x_lo| / 16 LSR A \ = |roofv_x| / 16 ROR P LSR A ROR P LSR A ROR P ORA T \ Set the sign of A to the opposite sign to T (i.e. the EOR #%10000000 \ sign of the original -roofv_x), so now: \ \ (A P) = -roofv_x / 16 EOR RAT2 \ Give it the sign as if we multiplied by the direction \ by the pitch or roll direction STX Q \ Store the value of X so it can be restored after the \ call to ADD JSR ADD \ (A X) = (A P) + (S R) \ = -/+roofv_x / 16 + (1 - 1/512) * nosev_x STA INWK+1,Y \ Set nosev_x = (1-1/512) * nosev_x -/+ roofv_x / 16 STX INWK,Y LDX Q \ Restore the value of X from before the call to ADD LDA K \ Set roofv_x = K(1 0) STA INWK,X \ = (1-1/512) * roofv_x +/- nosev_x / 16 LDA K+1 STA INWK+1,X RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TENS \ Type: Variable \ Category: Text \ Summary: A constant used when printing large numbers in BPRNT \ Deep dive: Printing decimal numbers \ \ ------------------------------------------------------------------------------ \ \ Contains the four low bytes of the value 100,000,000,000 (100 billion). \ \ The maximum number of digits that we can print with the BPRNT routine is 11, \ so the biggest number we can print is 99,999,999,999. This maximum number \ plus 1 is 100,000,000,000, which in hexadecimal is: \ \ 17 48 76 E8 00 \ \ The TENS variable contains the lowest four bytes in this number, with the \ most significant byte first, i.e. 48 76 E8 00. This value is used in the \ BPRNT routine when working out which decimal digits to print when printing a \ number. \ \ ****************************************************************************** .TENS EQUD &00E87648 \ ****************************************************************************** \ \ Name: pr2 \ Type: Subroutine \ Category: Text \ Summary: Print an 8-bit number, left-padded to 3 digits, and optional point \ \ ------------------------------------------------------------------------------ \ \ Print the 8-bit number in X to 3 digits, left-padding with spaces for numbers \ with fewer than 3 digits (so numbers < 100 are right-aligned). Optionally \ include a decimal point. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The number to print \ \ C flag If set, include a decimal point \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ pr2+2 Print the 8-bit number in X to the number of digits in A \ \ ****************************************************************************** .pr2 LDA #3 \ Set A to the number of digits (3) LDY #0 \ Zero the Y register, so we can fall through into TT11 \ to print the 16-bit number (Y X) to 3 digits, which \ effectively prints X to 3 digits as the high byte is \ zero \ ****************************************************************************** \ \ Name: TT11 \ Type: Subroutine \ Category: Text \ Summary: Print a 16-bit number, left-padded to n digits, and optional point \ \ ------------------------------------------------------------------------------ \ \ Print the 16-bit number in (Y X) to a specific number of digits, left-padding \ with spaces for numbers with fewer digits (so lower numbers will be right- \ aligned). Optionally include a decimal point. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The low byte of the number to print \ \ Y The high byte of the number to print \ \ A The number of digits \ \ C flag If set, include a decimal point \ \ ****************************************************************************** .TT11 STA U \ We are going to use the BPRNT routine (below) to \ print this number, so we store the number of digits \ in U, as that's what BPRNT takes as an argument LDA #0 \ BPRNT takes a 32-bit number in K to K+3, with the STA K \ most significant byte first (big-endian), so we set STA K+1 \ the two most significant bytes to zero (K and K+1) STY K+2 \ and store (Y X) in the least two significant bytes STX K+3 \ (K+2 and K+3), so we are going to print the 32-bit \ number (0 0 Y X) \ Finally we fall through into BPRNT to print out the \ number in K to K+3, which now contains (Y X), to A \ digits (as U = A), using the same C flag as when pr2 \ was called to control the decimal point \ ****************************************************************************** \ \ Name: BPRNT \ Type: Subroutine \ Category: Text \ Summary: Print a 32-bit number, left-padded to a specific number of digits, \ with an optional decimal point \ Deep dive: Printing decimal numbers \ \ ------------------------------------------------------------------------------ \ \ Print the 32-bit number stored in K(0 1 2 3) to a specific number of digits, \ left-padding with spaces for numbers with fewer digits (so lower numbers are \ right-aligned). Optionally include a decimal point. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ K(0 1 2 3) The number to print, stored with the most significant \ byte in K and the least significant in K+3 (i.e. as a \ big-endian number, which is the opposite way to how the \ 6502 assembler stores addresses, for example) \ \ U The maximum number of digits to print, including the \ decimal point (spaces will be used on the left to pad \ out the result to this width, so the number is right- \ aligned to this width). U must be 11 or less \ \ C flag If set, include a decimal point followed by one \ fractional digit (i.e. show the number to 1 decimal \ place). In this case, the number in K(0 1 2 3) contains \ 10 * the number we end up printing, so to print 123.4, \ we would pass 1234 in K(0 1 2 3) and would set the C \ flag to include the decimal point \ \ ****************************************************************************** .BPRNT LDX #11 \ Set T to the maximum number of digits allowed (11 STX T \ characters, which is the number of digits in 10 \ billion). We will use this as a flag when printing \ characters in TT37 below PHP \ Make a copy of the status register (in particular \ the C flag) so we can retrieve it later BCC TT30 \ If the C flag is clear, we do not want to print a \ decimal point, so skip the next two instructions DEC T \ As we are going to show a decimal point, decrement DEC U \ both the number of characters and the number of \ digits (as one of them is now a decimal point) .TT30 LDA #11 \ Set A to 11, the maximum number of digits allowed SEC \ Set the C flag so we can do subtraction without the \ C flag affecting the result STA XX17 \ Store the maximum number of digits allowed (11) in \ XX17 SBC U \ Set U = 11 - U + 1, so U now contains the maximum STA U \ number of digits minus the number of digits we want INC U \ to display, plus 1 (so this is the number of digits \ we should skip before starting to print the number \ itself, and the plus 1 is there to ensure we print at \ least one digit) LDY #0 \ In the main loop below, we use Y to count the number \ of times we subtract 10 billion to get the leftmost \ digit, so set this to zero STY S \ In the main loop below, we use location S as an \ 8-bit overflow for the 32-bit calculations, so \ we need to set this to 0 before joining the loop JMP TT36 \ Jump to TT36 to start the process of printing this \ number's digits .TT35 \ This subroutine multiplies K(S 0 1 2 3) by 10 and \ stores the result back in K(S 0 1 2 3), using the fact \ that K * 10 = (K * 2) + (K * 2 * 2 * 2) ASL K+3 \ Set K(S 0 1 2 3) = K(S 0 1 2 3) * 2 by rotating left ROL K+2 ROL K+1 ROL K ROL S LDX #3 \ Now we want to make a copy of the newly doubled K in \ XX15, so we can use it for the first (K * 2) in the \ equation above, so set up a counter in X for copying \ four bytes, starting with the last byte in memory \ (i.e. the least significant) .tt35 LDA K,X \ Copy the X-th byte of K(0 1 2 3) to the X-th byte of STA XX15,X \ XX15(0 1 2 3), so that XX15 will contain a copy of \ K(0 1 2 3) once we've copied all four bytes DEX \ Decrement the loop counter BPL tt35 \ Loop back to copy the next byte until we have copied \ all four LDA S \ Store the value of location S, our overflow byte, in STA XX15+4 \ XX15+4, so now XX15(4 0 1 2 3) contains a copy of \ K(S 0 1 2 3), which is the value of (K * 2) that we \ want to use in our calculation ASL K+3 \ Now to calculate the (K * 2 * 2 * 2) part. We still ROL K+2 \ have (K * 2) in K(S 0 1 2 3), so we just need to shift ROL K+1 \ it twice. This is the first one, so we do this: ROL K \ ROL S \ K(S 0 1 2 3) = K(S 0 1 2 3) * 2 = K * 4 ASL K+3 \ And then we do it again, so that means: ROL K+2 \ ROL K+1 \ K(S 0 1 2 3) = K(S 0 1 2 3) * 2 = K * 8 ROL K ROL S CLC \ Clear the C flag so we can do addition without the \ C flag affecting the result LDX #3 \ By now we've got (K * 2) in XX15(4 0 1 2 3) and \ (K * 8) in K(S 0 1 2 3), so the final step is to add \ these two 32-bit numbers together to get K * 10. \ So we set a counter in X for four bytes, starting \ with the last byte in memory (i.e. the least \ significant) .tt36 LDA K,X \ Fetch the X-th byte of K into A ADC XX15,X \ Add the X-th byte of XX15 to A, with carry STA K,X \ Store the result in the X-th byte of K DEX \ Decrement the loop counter BPL tt36 \ Loop back to add the next byte, moving from the least \ significant byte to the most significant, until we \ have added all four LDA XX15+4 \ Finally, fetch the overflow byte from XX15(4 0 1 2 3) ADC S \ And add it to the overflow byte from K(S 0 1 2 3), \ with carry STA S \ And store the result in the overflow byte from \ K(S 0 1 2 3), so now we have our desired result, i.e. \ \ K(S 0 1 2 3) = K(S 0 1 2 3) * 10 LDY #0 \ In the main loop below, we use Y to count the number \ of times we subtract 10 billion to get the leftmost \ digit, so set this to zero so we can rejoin the main \ loop for another subtraction process .TT36 \ This is the main loop of our digit-printing routine. \ In the following loop, we are going to count the \ number of times that we can subtract 10 million and \ store that count in Y, which we have already set to 0 LDX #3 \ Our first calculation concerns 32-bit numbers, so \ set up a counter for a four-byte loop SEC \ Set the C flag so we can do subtraction without the \ C flag affecting the result .tt37 \ We now loop through each byte in turn to do this: \ \ XX15(4 0 1 2 3) = K(S 0 1 2 3) - 100,000,000,000 LDA K,X \ Subtract the X-th byte of TENS (i.e. 10 billion) from SBC TENS,X \ the X-th byte of K STA XX15,X \ Store the result in the X-th byte of XX15 DEX \ Decrement the loop counter BPL tt37 \ Loop back to subtract the next byte, moving from the \ least significant byte to the most significant, until \ we have subtracted all four LDA S \ Subtract the fifth byte of 10 billion (i.e. &17) from SBC #&17 \ the fifth (overflow) byte of K, which is S STA XX15+4 \ Store the result in the overflow byte of XX15 BCC TT37 \ If subtracting 10 billion took us below zero, jump to \ TT37 to print out this digit, which is now in Y LDX #3 \ We now want to copy XX15(4 0 1 2 3) back into \ K(S 0 1 2 3), so we can loop back up to do the next \ subtraction, so set up a counter for a four-byte loop .tt38 LDA XX15,X \ Copy the X-th byte of XX15(0 1 2 3) to the X-th byte STA K,X \ of K(0 1 2 3), so that K(0 1 2 3) will contain a copy \ of XX15(0 1 2 3) once we've copied all four bytes DEX \ Decrement the loop counter BPL tt38 \ Loop back to copy the next byte, until we have copied \ all four LDA XX15+4 \ Store the value of location XX15+4, our overflow STA S \ byte in S, so now K(S 0 1 2 3) contains a copy of \ XX15(4 0 1 2 3) INY \ We have now managed to subtract 10 billion from our \ number, so increment Y, which is where we are keeping \ a count of the number of subtractions so far JMP TT36 \ Jump back to TT36 to subtract the next 10 billion .TT37 TYA \ If we get here then Y contains the digit that we want \ to print (as Y has now counted the total number of \ subtractions of 10 billion), so transfer Y into A BNE TT32 \ If the digit is non-zero, jump to TT32 to print it LDA T \ Otherwise the digit is zero. If we are already \ printing the number then we will want to print a 0, \ but if we haven't started printing the number yet, \ then we probably don't, as we don't want to print \ leading zeroes unless this is the only digit before \ the decimal point \ \ To help with this, we are going to use T as a flag \ that tells us whether we have already started \ printing digits: \ \ * If T <> 0 we haven't printed anything yet \ \ * If T = 0 then we have started printing digits \ \ We initially set T above to the maximum number of \ characters allowed, less 1 if we are printing a \ decimal point, so the first time we enter the digit \ printing routine at TT37, it is definitely non-zero BEQ TT32 \ If T = 0, jump straight to the print routine at TT32, \ as we have already started printing the number, so we \ definitely want to print this digit too DEC U \ We initially set U to the number of digits we want to BPL TT34 \ skip before starting to print the number. If we get \ here then we haven't printed any digits yet, so \ decrement U to see if we have reached the point where \ we should start printing the number, and if not, jump \ to TT34 to set up things for the next digit LDA #' ' \ We haven't started printing any digits yet, but we BNE tt34 \ have reached the point where we should start printing \ our number, so call TT26 (via tt34) to print a space \ so that the number is left-padded with spaces (this \ BNE is effectively a JMP as A will never be zero) .TT32 LDY #0 \ We are printing an actual digit, so first set T to 0, STY T \ to denote that we have now started printing digits as \ opposed to spaces CLC \ The digit value is in A, so add ASCII "0" to get the ADC #'0' \ ASCII character number to print .tt34 JSR TT26 \ Call TT26 to print the character in A and fall through \ into TT34 to get things ready for the next digit .TT34 DEC T \ Decrement T but keep T >= 0 (by incrementing it BPL P%+4 \ again if the above decrement made T negative) INC T DEC XX17 \ Decrement the total number of characters left to \ print, which we stored in XX17 BMI rT10 \ If the result is negative, we have printed all the \ characters, so jump down to rT10 to return from the \ subroutine BNE P%+10 \ If the result is positive (> 0) then we still have \ characters left to print, so loop back to TT35 (via \ the JMP TT35 instruction below) to print the next \ digit PLP \ If we get here then we have printed the exact number \ of digits that we wanted to, so restore the C flag \ that we stored at the start of the routine BCC P%+7 \ If the C flag is clear, we don't want a decimal point, \ so loop back to TT35 (via the JMP TT35 instruction \ below) to print the next digit LDA #'.' \ Otherwise the C flag is set, so print the decimal JSR TT26 \ point JMP TT35 \ Loop back to TT35 to print the next digit .rT10 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: DTW1 \ Type: Variable \ Category: Text \ Summary: A mask for applying the lower case part of Sentence Case to \ extended text tokens \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This variable is used to change characters to lower case as part of applying \ Sentence Case to extended text tokens. It has two values: \ \ * %00100000 = apply lower case to the second letter of a word onwards \ \ * %00000000 = do not change case to lower case \ \ The default value is %00100000 (apply lower case). \ \ The flag is set to %00100000 (apply lower case) by jump token 2, {sentence \ case}, which calls routine MT2 to change the value of DTW1. \ \ The flag is set to %00000000 (do not change case to lower case) by jump token \ 1, {all caps}, which calls routine MT1 to change the value of DTW1. \ \ The letter to print is OR'd with DTW1 in DETOK2, which lower-cases the letter \ by setting bit 5 (if DTW1 is %00100000). However, this OR is only done if bit \ 7 of DTW2 is clear, i.e. we are printing a word, so this doesn't affect the \ first letter of the word, which remains capitalised. \ \ ****************************************************************************** .DTW1 EQUB %00100000 \ ****************************************************************************** \ \ Name: DTW2 \ Type: Variable \ Category: Text \ Summary: A flag that indicates whether we are currently printing a word \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This variable is used to indicate whether we are currently printing a word. It \ has two values: \ \ * 0 = we are currently printing a word \ \ * Non-zero = we are not currently printing a word \ \ The default value is %11111111 (we are not currently printing a word). \ \ The flag is set to %00000000 (we are currently printing a word) whenever a \ non-terminator character is passed to DASC for printing. \ \ The flag is set to %11111111 (we are not currently printing a word) whenever a \ terminator character (full stop, colon, carriage return, line feed, space) is \ passed to DASC for printing. It is also set to %11111111 by jump token 8, \ {tab 6}, which calls routine MT8 to change the value of DTW2, and to %10000000 \ by TTX66 when we clear the screen. \ \ ****************************************************************************** .DTW2 EQUB %11111111 \ ****************************************************************************** \ \ Name: DTW3 \ Type: Variable \ Category: Text \ Summary: A flag for switching between standard and extended text tokens \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This variable is used to indicate whether standard or extended text tokens \ should be printed by calls to DETOK. It allows us to mix standard tokens in \ with extended tokens. It has two values: \ \ * %00000000 = print extended tokens (i.e. those in TKN1 and RUTOK) \ \ * %11111111 = print standard tokens (i.e. those in QQ18) \ \ The default value is %00000000 (extended tokens). \ \ Standard tokens are set by jump token {6}, which calls routine MT6 to change \ the value of DTW3 to %11111111. \ \ Extended tokens are set by jump token {5}, which calls routine MT5 to change \ the value of DTW3 to %00000000. \ \ ****************************************************************************** .DTW3 EQUB %00000000 \ ****************************************************************************** \ \ Name: DTW4 \ Type: Variable \ Category: Text \ Summary: Flags that govern how justified extended text tokens are printed \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This variable is used to control how justified text tokens are printed as part \ of the extended text token system. There are two bits that affect justified \ text: \ \ * Bit 7: 1 = justify text \ 0 = do not justify text \ \ * Bit 6: 1 = buffer the entire token before printing, including carriage \ returns (used for in-flight messages only) \ 0 = print the contents of the buffer whenever a carriage return \ appears in the token \ \ The default value is %00000000 (do not justify text, print buffer on carriage \ return). \ \ The flag is set to %10000000 (justify text, print buffer on carriage return) \ by jump token 14, {justify}, which calls routine MT14 to change the value of \ DTW4. \ \ The flag is set to %11000000 (justify text, buffer entire token) by routine \ MESS, which prints in-flight messages. \ \ The flag is set to %00000000 (do not justify text, print buffer on carriage \ return) by jump token 15, {left align}, which calls routine MT1 to change the \ value of DTW4. \ \ ****************************************************************************** .DTW4 EQUB 0 \ ****************************************************************************** \ \ Name: DTW5 \ Type: Variable \ Category: Text \ Summary: The size of the justified text buffer at BUF \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ When justified text is enabled by jump token 14, {justify}, during printing of \ extended text tokens, text is fed into a buffer at BUF instead of being \ printed straight away, so it can be padded out with spaces to justify the \ text. DTW5 contains the size of the buffer, so BUF + DTW5 points to the first \ free byte after the end of the buffer. \ \ ****************************************************************************** .DTW5 EQUB 0 \ ****************************************************************************** \ \ Name: DTW6 \ Type: Variable \ Category: Text \ Summary: A flag to denote whether printing in lower case is enabled for \ extended text tokens \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This variable is used to indicate whether lower case is currently enabled. It \ has two values: \ \ * %10000000 = lower case is enabled \ \ * %00000000 = lower case is not enabled \ \ The default value is %00000000 (lower case is not enabled). \ \ The flag is set to %10000000 (lower case is enabled) by jump token 13 {lower \ case}, which calls routine MT10 to change the value of DTW6. \ \ The flag is set to %00000000 (lower case is not enabled) by jump token 1, {all \ caps}, and jump token 2, {sentence case}, which call routines MT1 and MT2 to \ change the value of DTW6. \ \ ****************************************************************************** .DTW6 EQUB %00000000 \ ****************************************************************************** \ \ Name: DTW8 \ Type: Variable \ Category: Text \ Summary: A mask for capitalising the next letter in an extended text token \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This variable is only used by one specific extended token, the {single cap} \ jump token, which capitalises the next letter only. It has two values: \ \ * %11011111 = capitalise the next letter \ \ * %11111111 = do not change case \ \ The default value is %11111111 (do not change case). \ \ The flag is set to %11011111 (capitalise the next letter) by jump token 19, \ {single cap}, which calls routine MT19 to change the value of DTW. \ \ The flag is set to %11111111 (do not change case) at the start of DASC, after \ the letter has been capitalised in DETOK2, so the effect is to capitalise one \ letter only. \ \ The letter to print is AND'd with DTW8 in DETOK2, which capitalises the letter \ by clearing bit 5 (if DTW8 is %11011111). However, this AND is only done if at \ least one of the following is true: \ \ * Bit 7 of DTW2 is set (we are not currently printing a word) \ \ * Bit 7 of DTW6 is set (lower case has been enabled by jump token 13, {lower \ case} \ \ In other words, we only capitalise the next letter if it's the first letter in \ a word, or we are printing in lower case. \ \ ****************************************************************************** .DTW8 EQUB %11111111 \ ****************************************************************************** \ \ Name: FEED \ Type: Subroutine \ Category: Text \ Summary: Print a newline \ \ ****************************************************************************** .FEED LDA #12 \ Set A = 12, so when we skip MT16 and fall through into \ TT26, we print character 12, which is a newline EQUB &2C \ Skip the next instruction by turning it into \ &2C &A9 &41, or BIT &41A9, which does nothing apart \ from affect the flags \ Fall through into TT26 (skipping MT16) to print the \ newline character \ ****************************************************************************** \ \ Name: MT16 \ Type: Subroutine \ Category: Text \ Summary: Print the character in variable DTW7 \ Deep dive: Extended text tokens \ \ ****************************************************************************** .MT16 LDA #'A' \ Set A to the contents of DTW7, as DTW7 points to the \ second byte of this instruction, so updating DTW7 will \ modify this instruction (the default value of DTW7 is \ an "A") DTW7 = MT16 + 1 \ Point DTW7 to the second byte of the instruction above \ so that modifying DTW7 changes the value loaded into A \ Fall through into TT26 to print the character in A \ ****************************************************************************** \ \ Name: TT26 \ Type: Subroutine \ Category: Text \ Summary: Print a character at the text cursor, with support for verified \ text in extended tokens \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The character to print \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X X is preserved \ \ C flag The C flag is cleared \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ DASC DASC does exactly the same as TT26 and prints a \ character at the text cursor, with support for verified \ text in extended tokens \ \ rT9 Contains an RTS \ \ ****************************************************************************** .DASC .TT26 STX SC \ Store X in SC, so we can retrieve it below LDX #%11111111 \ Set DTW8 = %11111111, to disable the effect of {19} if STX DTW8 \ it was set (as {19} capitalises one character only) CMP #'.' \ If the character in A is a word terminator: BEQ DA8 \ CMP #':' \ * Full stop BEQ DA8 \ * Colon CMP #10 \ * Line feed BEQ DA8 \ * Carriage return CMP #12 \ * Space BEQ DA8 \ CMP #' ' \ then skip the following instruction BEQ DA8 INX \ Increment X to 0, so DTW2 gets set to %00000000 below .DA8 STX DTW2 \ Store X in DTW2, so DTW2 is now: \ \ * %00000000 if this character is a word terminator \ \ * %11111111 if it isn't \ \ so DTW2 indicates whether or not we are currently \ printing a word LDX SC \ Retrieve the original value of X from SC BIT DTW4 \ If bit 7 of DTW4 is set then we are currently printing BMI P%+5 \ justified text, so skip the next instruction JMP CHPR \ Bit 7 of DTW4 is clear, so jump down to CHPR to print \ this character, as we are not printing justified text \ If we get here then we are printing justified text, so \ we need to buffer the text until we reach the end of \ the paragraph, so we can then pad it out with spaces BVS P%+6 \ If bit 6 of DTW4 is set, then this is an in-flight \ message and we should buffer the carriage return \ character {12}, so skip the following two instructions CMP #12 \ If the character in A is a carriage return, then we BEQ DA1 \ have reached the end of the paragraph, so jump down to \ DA1 to print out the contents of the buffer, \ justifying it as we go \ If we get here then we need to buffer this character \ in the line buffer at BUF LDX DTW5 \ DTW5 contains the current size of the buffer, so this STA BUF,X \ stores the character in A at BUF + DTW5, the next free \ space in the buffer LDX SC \ Retrieve the original value of X from SC so we can \ preserve it through this subroutine call INC DTW5 \ Increment the size of the BUF buffer that is stored in \ DTW5 CLC \ Clear the C flag RTS \ Return from the subroutine .DA1 \ If we get here then we are justifying text and we have \ reached the end of the paragraph, so we need to print \ out the contents of the buffer, justifying it as we go TXA \ Store X and Y on the stack PHA TYA PHA .DA5 LDX DTW5 \ Set X = DTW5, which contains the size of the buffer BEQ DA6+3 \ If X = 0 then the buffer is empty, so jump down to \ DA6+3 to print a newline CPX #(LL+1) \ If X < LL+1, i.e. X <= LL, then the buffer contains BCC DA6 \ fewer than LL characters, which is less than a line \ length, so jump down to DA6 to print the contents of \ BUF followed by a newline, as we don't justify the \ last line of the paragraph \ Otherwise X > LL, so the buffer does not fit into one \ line, and we therefore need to justify the text, which \ we do one line at a time LSR SC+1 \ Shift SC+1 to the right, which clears bit 7 of SC+1, \ so we pass through the following comparison on the \ first iteration of the loop and set SC+1 to %01000000 .DA11 LDA SC+1 \ If bit 7 of SC+1 is set, skip the following two BMI P%+6 \ instructions LDA #%01000000 \ Set SC+1 = %01000000 STA SC+1 LDY #(LL-1) \ Set Y = line length, so we can loop backwards from the \ end of the first line in the buffer using Y as the \ loop counter .DAL1 LDA BUF+LL \ If the LL-th byte in BUF is a space, jump down to DA2 CMP #' ' \ to print out the first line from the buffer, as it BEQ DA2 \ fits the line width exactly (i.e. it's justified) \ We now want to find the last space character in the \ first line in the buffer, so we loop through the line \ using Y as a counter .DAL2 DEY \ Decrement the loop counter in Y BMI DA11 \ If Y <= 0, loop back to DA11, as we have now looped BEQ DA11 \ through the whole line LDA BUF,Y \ If the Y-th byte in BUF is not a space, loop back up CMP #' ' \ to DAL2 to check the next character BNE DAL2 \ Y now points to a space character in the line buffer ASL SC+1 \ Shift SC+1 to the left BMI DAL2 \ If bit 7 of SC+1 is set, jump to DAL2 to find the next \ space character \ We now want to insert a space into the line buffer at \ position Y, which we do by shifting every character \ after position Y along by 1, and then inserting the \ space STY SC \ Store Y in SC, so we want to insert the space at \ position SC LDY DTW5 \ Fetch the buffer size from DTW5 into Y, to act as a \ loop counter for moving the line buffer along by 1 .DAL6 LDA BUF,Y \ Copy the Y-th character from BUF into the Y+1-th STA BUF+1,Y \ position DEY \ Decrement the loop counter in Y CPY SC \ Loop back to shift the next character along, until we BCS DAL6 \ have moved the SC-th character (i.e. Y < SC) INC DTW5 \ Increment the buffer size in DTW5 \LDA #' ' \ This instruction is commented out in the original \ source, as it has no effect because A already contains \ ASCII " ". This is because the last character that is \ tested in the above loop is at position SC, which we \ know contains a space, so we know A contains a space \ character when the loop finishes \ We've now shifted the line to the right by 1 from \ position SC onwards, so SC and SC+1 both contain \ spaces, and Y is now SC-1 as we did a DEY just before \ the end of the loop - in other words, we have inserted \ a space at position SC, and Y points to the character \ before the newly inserted space \ We now want to move the pointer Y left to find the \ next space in the line buffer, before looping back to \ check whether we are done, and if not, insert another \ space .DAL3 CMP BUF,Y \ If the character at position Y is not a space, jump to BNE DAL1 \ DAL1 to see whether we have now justified the line DEY \ Decrement the loop counter in Y BPL DAL3 \ Loop back to check the next character to the left, \ until we have found a space BMI DA11 \ Jump back to DA11 (this BMI is effectively a JMP as \ we already passed through a BPL to get here) .DA2 \ This subroutine prints out a full line of characters \ from the start of the line buffer in BUF, followed by \ a newline. It then removes that line from the buffer, \ shuffling the rest of the buffer contents down LDX #LL \ Call DAS1 to print out the first LL characters from JSR DAS1 \ the line buffer in BUF LDA #12 \ Print a newline JSR CHPR LDA DTW5 \ Subtract #LL from the end-of-buffer pointer in DTW5 \CLC \ SBC #LL \ The CLC instruction is commented out in the original STA DTW5 \ source. It isn't needed as CHPR clears the C flag TAX \ Copy the new value of DTW5 into X BEQ DA6+3 \ If DTW5 = 0 then jump down to DA6+3 to print a newline \ as the buffer is now empty \ If we get here then we have printed our line but there \ is more in the buffer, so we now want to remove the \ line we just printed from the start of BUF LDY #0 \ Set Y = 0 to count through the characters in BUF INX \ Increment X, so it now contains the number of \ characters in the buffer (as DTW5 is a zero-based \ pointer and is therefore equal to the number of \ characters minus 1) .DAL4 LDA BUF+LL+1,Y \ Copy the Y-th character from BUF+LL to BUF STA BUF,Y INY \ Increment the character pointer DEX \ Decrement the character count BNE DAL4 \ Loop back to copy the next character until we have \ shuffled down the whole buffer BEQ DA5 \ Jump back to DA5 (this BEQ is effectively a JMP as we \ have already passed through the BNE above) .DAS1 \ This subroutine prints out X characters from BUF, \ returning with X = 0 LDY #0 \ Set Y = 0 to point to the first character in BUF .DAL5 LDA BUF,Y \ Print the Y-th character in BUF using CHPR, which also JSR CHPR \ clears the C flag for when we return from the \ subroutine below INY \ Increment Y to point to the next character DEX \ Decrement the loop counter BNE DAL5 \ Loop back for the next character until we have printed \ X characters from BUF .rT9 RTS \ Return from the subroutine .DA6 JSR DAS1 \ Call DAS1 to print X characters from BUF, returning \ with X = 0 STX DTW5 \ Set the buffer size in DTW5 to 0, as the buffer is now \ empty PLA \ Restore Y and X from the stack TAY PLA TAX LDA #12 \ Set A = 12, so when we skip BELL and fall through into \ CHPR, we print character 12, which is a newline .DA7 EQUB &2C \ Skip the next instruction by turning it into \ &2C &A9 &07, or BIT &07A9, which does nothing apart \ from affect the flags \ Fall through into CHPR (skipping BELL) to print the \ character and return with the C flag cleared \ ****************************************************************************** \ \ Name: BELL \ Type: Subroutine \ Category: Sound \ Summary: Make a standard system beep \ \ ------------------------------------------------------------------------------ \ \ This is the standard system beep, as made by the ASCII 7 "BELL" control code. \ \ ****************************************************************************** .BELL LDA #7 \ Control code 7 makes a beep, so load this into A \ Fall through into the CHPR print routine to \ actually make the sound IF _EXECUTIVE BNE CHPRD \ Jump down to CHPRD to actually make the sound, \ skipping the code that prevents CHPR from beeping if \ speech is enabled, so the beep gets made even if \ speech is enabled (this BNE is effectively a JMP as \ A is never 0) ENDIF \ ****************************************************************************** \ \ Name: CHPR \ Type: Subroutine \ Category: Text \ Summary: Send a character to the I/O processor for printing or processing \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The character to print \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag The C flag is cleared \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ CHPRD Make a beep even if speech is enabled (Executive version \ only) \ \ ****************************************************************************** .CHPR IF _EXECUTIVE CMP #7 \ If this is not a beep character, jump to CHPRD to BNE CHPRD \ print the character BIT SPEAK \ If speech is disabled, jump to CHPRD to print the BPL CHPRD \ character RTS \ If we get here then this is a beep character and \ speech is enabled, so return from the subroutine to \ prevent the beep from being made ENDIF .CHPRD STA K3 \ Store the character to print in K3 \ --- Mod: Code added for silent beeps: ---------------> CMP #7 \ If this is not a beep, jump to chpr2 to print the BNE chpr2 \ character in A LDA DNOIZ \ Set A to the DNOIZ configuration setting BEQ chpr1 \ If DNOIZ is zero then sound is enabled, so jump to \ chpr1 to make the beep \ If we get here then this is a beep but sound is \ disabled, so we do not make the beep CLC \ Clear the C flag RTS \ Return from the subroutine .chpr1 LDA #7 \ Set A = 7 so we make the beep by printing character 7 \ in the following .chpr2 \ --- End of added code -------------------------------> CMP #' ' \ If A < ASCII " ", i.e. this is a control character, BCC P%+4 \ skip the following instruction so the text cursor \ doesn't move to the right INC XC \ We are printing a visible character, so increment XC \ to move the text cursor one character to the right LDA QQ17 \ If all bits of QQ17 are set, i.e. text printing is INA \ disabled, then return from the subroutine without BEQ rT9 \ printing anything (as rT9 contains an RTS) BIT printflag \ If bit 7 of printflag is clear (printer output is not BPL noprinter \ enabled), jump to noprinter LDA #printcode \ Bit 7 of printflag is set, which means we should send JSR OSWRCH \ the output to the printer as well as the screen, so \ send a #printcode command to the I/O processor to do \ this .noprinter LDA K3 \ Send the character we want to print to the I/O JSR OSWRCH \ processor CLC \ Clear the C flag RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: printflag \ Type: Variable \ Category: Text \ Summary: A flag that determines whether to send text output to the printer \ as well as the screen \ \ ------------------------------------------------------------------------------ \ \ This flag can have the following values: \ \ * Bit 7: 1 = printer enabled \ 0 = printer disabled \ \ If the printer is enabled, then text output is sent to the printer as well as \ the screen. This is set when CTRL is held down when displaying a text screen, \ such as CTRL-f7 to print out the market prices for the current system. \ \ ****************************************************************************** .printflag EQUB 0 \ ****************************************************************************** \ \ Name: DIALS \ Type: Subroutine \ Category: Dashboard \ Summary: Update the dashboard indicators and flight variables by sending a \ #RDPARAMS command to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ The parameters sent by this command not only update the dashboard, they also \ update all the flight variables in the I/O processor, from ENERGY to ESCP. \ \ ****************************************************************************** .DIALS LDA #RDPARAMS \ Send a #RDPARAMS command to the I/O processor to tell JSR OSWRCH \ it to expect a sequence of parameters containing the JSR OSWRCH \ data it needs to update the dashboard dials LDA ENERGY \ Send the energy bank status to the I/O processor JSR OSWRCH LDA ALP1 \ Send the magnitude of the roll angle to the I/O JSR OSWRCH \ processor LDA ALP2 \ Send the sign of the roll angle to the I/O processor JSR OSWRCH LDA BETA \ Send the signed pitch angle to the I/O processor JSR OSWRCH LDA BET1 \ Send the magnitude of the pitch angle to the I/O JSR OSWRCH \ processor LDA DELTA \ Send the current speed to the I/O processor JSR OSWRCH \ --- Mod: Code removed for two-player Elite: ---------> \LDA ALTIT \ Send the current altitude to the I/O processor \JSR OSWRCH \ --- End of removed code -----------------------------> LDA MCNT \ Send the value of the main loop counter to the I/O JSR OSWRCH \ processor LDA FSH \ Send the front shield status to the I/O processor JSR OSWRCH LDA ASH \ Send the aft shield status to the I/O processor JSR OSWRCH \ --- Mod: Code removed for two-player Elite: ---------> \LDA QQ14 \ Send the current fuel level to the I/O processor \JSR OSWRCH \ --- End of removed code -----------------------------> LDA GNTMP \ Send the laser temperature to the I/O processor JSR OSWRCH \ --- Mod: Code removed for two-player Elite: ---------> \LDA CABTMP \ Send the cabin temperature to the I/O processor \JSR OSWRCH \ --- End of removed code -----------------------------> LDA FLH \ Send the flashing console bars configuration setting JSR OSWRCH \ to the I/O processor \ --- Mod: Code removed for two-player Elite: ---------> \LDA ESCP \ Send the escape pod status to the I/O processor \JSR OSWRCH \ --- End of removed code -----------------------------> \ --- Mod: Code added for two-player Elite: -----------> LDA player2ENERGY \ Send the energy bank status to the I/O processor JSR OSWRCH LDA player2ALP1 \ Send the magnitude of the roll angle to the I/O JSR OSWRCH \ processor LDA player2ALP2 \ Send the sign of the roll angle to the I/O processor JSR OSWRCH LDA player2JSTK \ If this is a human player, jump to dial1 to display BNE dial1 \ the pitch angle in player2BETA as-is LDA player2BETA \ Send the signed pitch angle to the I/O processor AND #%10000000 \ divided by 4 while retaining the sign STA T LDA player2BETA AND #%01111111 LSR A LSR A ORA T JSR OSWRCH LDA #0 \ Send 0 for player2BET1 so we don't adjust the value JSR OSWRCH \ in the I/O processor routine JMP dial2 \ Jump to dial2 to skip the following .dial1 LDA player2BETA \ Send the signed pitch angle to the I/O processor JSR OSWRCH LDA player2BET1 \ Send the magnitude of the pitch angle to the I/O JSR OSWRCH \ processor .dial2 LDA player2DELTA \ Send the current speed to the I/O processor JSR OSWRCH LDA player2FSH \ Send the front shield status to the I/O processor JSR OSWRCH LDA player2ASH \ Send the aft shield status to the I/O processor JSR OSWRCH LDA player2GNTMP \ Send the laser temperature to the I/O processor JSR OSWRCH \ --- End of added code -------------------------------> LDA MCNT \ This value will be zero on one out of every four AND #3 \ iterations of the main loop, so skip the following BEQ P%+3 \ instruction when that happens (so we only update the \ compass once every four iterations of the main loop) RTS \ Return from the subroutine JMP COMPAS \ Jump to COMPAS to update the compass, returning from \ the subroutine using a tail call \ ****************************************************************************** \ \ Name: ESCAPE \ Type: Subroutine \ Category: Flight \ Summary: Launch our escape pod \ \ ------------------------------------------------------------------------------ \ \ This routine displays our doomed Cobra Mk III disappearing off into the ether \ before arranging our replacement ship. Called when we press ESCAPE during \ flight and have an escape pod fitted. \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.ESCAPE \ \JSR RES2 \ Reset a number of flight variables and workspaces \ \LDX #CYL \ Set the current ship type to a Cobra Mk III, so we \STX TYPE \ can show our ship disappear into the distance when we \ \ eject in our pod \ \JSR FRS1 \ Call FRS1 to launch the Cobra Mk III straight ahead, \ \ like a missile launch, but with our ship instead \ \BCS ES1 \ If the Cobra was successfully added to the local \ \ bubble, jump to ES1 to skip the following instructions \ \LDX #CYL2 \ The Cobra wasn't added to the local bubble for some \JSR FRS1 \ reason, so try launching a pirate Cobra Mk III instead \ \.ES1 \ \LDA #8 \ Set the Cobra's byte #27 (speed) to 8 \STA INWK+27 \ \LDA #194 \ Set the Cobra's byte #30 (pitch counter) to 194, so it \STA INWK+30 \ pitches up as we pull away \ \LSR A \ Set the Cobra's byte #32 (AI flag) to %01100001, so it \STA INWK+32 \ has no AI, and we can use this value as a counter to \ \ do the following loop 97 times \ \.ESL1 \ \JSR MVEIT \ Call MVEIT to move the Cobra in space \ \JSR LL9 \ Call LL9 to draw the Cobra on-screen \ \DEC INWK+32 \ Decrement the counter in byte #32 \ \BNE ESL1 \ Loop back to keep moving the Cobra until the AI flag \ \ is 0, which gives it time to drift away from our pod \ \JSR SCAN \ Call SCAN to remove the Cobra from the scanner (by \ \ redrawing it) \ \LDA #0 \ Set A = 0 so we can use it to zero the contents of \ \ the cargo hold \ \LDX #16 \ We lose all our cargo when using our escape pod, so \ \ up a counter in X so we can zero the 17 cargo slots \ \ in QQ20 \ \.ESL2 \ \STA QQ20,X \ Set the X-th byte of QQ20 to zero, so we no longer \ \ have any of item type X in the cargo hold \ \DEX \ Decrement the counter \ \BPL ESL2 \ Loop back to ESL2 until we have emptied the entire \ \ cargo hold \ \STA FIST \ Launching an escape pod also clears our criminal \ \ record, so set our legal status in FIST to 0 ("clean") \ \STA ESCP \ The escape pod is a one-use item, so set ESCP to 0 so \ \ we no longer have one fitted \ \LDA #70 \ Our replacement ship is delivered with a full tank of \STA QQ14 \ fuel, so set the current fuel level in QQ14 to 70, or \ \ 7.0 light years \ \JMP GOIN \ Go to the docking bay (i.e. show the ship hangar \ \ screen) and return from the subroutine with a tail \ \ call \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: HME2 \ Type: Subroutine \ Category: Charts \ Summary: Search the galaxy for a system \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.HME2 \ \LDA #CYAN \ Send a #SETCOL CYAN command to the I/O processor to \JSR DOCOL \ switch to colour 3, which is white in the chart view \ \LDA #14 \ Print extended token 14 ("{clear bottom of screen} \JSR DETOK \ PLANET NAME?{fetch line input from keyboard}"). The \ \ last token calls MT26, which puts the entered search \ \ term in INWK+5 and the term length in Y \ \JSR TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10), \ \ which will erase the crosshairs currently there \ \JSR TT81 \ Set the seeds in QQ15 (the selected system) to those \ \ of system 0 in the current galaxy (i.e. copy the seeds \ \ from QQ21 to QQ15) \ \LDA #0 \ We now loop through the galaxy's systems in order, \STA XX20 \ until we find a match, so set XX20 to act as a system \ \ counter, starting with system 0 \ \.HME3 \ \JSR MT14 \ Switch to justified text when printing extended \ \ tokens, so the call to cpl prints into the justified \ \ text buffer at BUF instead of the screen, and DTW5 \ \ gets set to the length of the system name \ \JSR cpl \ Print the selected system name into the justified text \ \ buffer \ \LDX DTW5 \ Fetch DTW5 into X, so X is now equal to the length of \ \ the selected system name \ \LDA INWK+5,X \ Fetch the X-th character from the entered search term \ \CMP #13 \ If the X-th character is not a carriage return, then \BNE HME6 \ the selected system name and the entered search term \ \ are different lengths, so jump to HME6 to move on to \ \ the next system \ \.HME4 \ \DEX \ Decrement X so it points to the last letter of the \ \ selected system name (and, when we loop back here, it \ \ points to the next letter to the left) \ \LDA INWK+5,X \ Set A to the X-th character of the entered search term \ \ORA #%00100000 \ Set bit 5 of the character to make it lower case \ \CMP BUF,X \ If the character in A matches the X-th character of \BEQ HME4 \ the selected system name in BUF, loop back to HME4 to \ \ check the next letter to the left \ \TXA \ The last comparison didn't match, so copy the letter \BMI HME5 \ number into A, and if it's negative, that means we \ \ managed to go past the first letters of each term \ \ before we failed to get a match, so the terms are the \ \ same, so jump to HME5 to process a successful search \ \.HME6 \ \ \ If we get here then the selected system name and the \ \ entered search term did not match \ \JSR TT20 \ We want to move on to the next system, so call TT20 \ \ to twist the three 16-bit seeds in QQ15 \ \INC XX20 \ Increment the system counter in XX20 \ \BNE HME3 \ If we haven't yet checked all 256 systems in the \ \ current galaxy, loop back to HME3 to check the next \ \ system \ \ \ If we get here then the entered search term did not \ \ match any systems in the current galaxy \ \JSR TT111 \ Select the system closest to galactic coordinates \ \ (QQ9, QQ10), so we can put the crosshairs back where \ \ they were before the search \ \JSR TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10) \ \LDA #40 \ Call the NOISE routine with A = 40 to make a low, \JSR NOISE \ long beep to indicate a failed search \ \LDA #215 \ Print extended token 215 ("{left align} UNKNOWN \JMP DETOK \ PLANET"), which will print on-screen as the left align \ \ code disables justified text, and return from the \ \ subroutine using a tail call \ \.HME5 \ \ \ If we get here then we have found a match for the \ \ entered search \ \LDA QQ15+3 \ The x-coordinate of the system described by the seeds \STA QQ9 \ in QQ15 is in QQ15+3 (s1_hi), so we copy this to QQ9 \ \ as the x-coordinate of the search result \ \LDA QQ15+1 \ The y-coordinate of the system described by the seeds \STA QQ10 \ in QQ15 is in QQ15+1 (s0_hi), so we copy this to QQ10 \ \ as the y-coordinate of the search result \ \JSR TT111 \ Select the system closest to galactic coordinates \ \ (QQ9, QQ10) \ \JSR TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10) \ \JSR MT15 \ Switch to left-aligned text when printing extended \ \ tokens so future tokens will print to the screen (as \ \ this disables justified text) \ \JMP T95 \ Jump to T95 to print the distance to the selected \ \ system and return from the subroutine using a tail \ \ call \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Save ELTB.bin \ \ ****************************************************************************** PRINT "ELITE B" PRINT "Assembled at ", ~CODE_B% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_B%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_B% PRINT "S.ELTB ", ~CODE_B%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_B% SAVE "3-assembled-output/ELTB.bin", CODE_B%, P%, LOAD% \ ****************************************************************************** \ \ ELITE C FILE \ \ Produces the binary file ELTC.bin that gets loaded by elite-bcfs.asm. \ \ ****************************************************************************** CODE_C% = P% LOAD_C% = LOAD% +P% - CODE% \ ****************************************************************************** \ \ Name: HATB \ Type: Variable \ Category: Ship hangar \ Summary: Ship hangar group table \ \ ------------------------------------------------------------------------------ \ \ This table contains groups of ships to show in the ship hangar. A group of \ ships is shown half the time (the other half shows a solo ship), and each of \ the four groups is equally likely. \ \ The bytes for each ship in the group contain the following information: \ \ Byte #0 Non-zero = Ship type to draw \ 0 = don't draw anything \ \ Byte #1 Bits 0-7 = Ship's x_hi \ Bit 0 = Ship's z_hi (1 if clear, or 2 if set) \ \ Byte #2 Bits 0-7 = Ship's z_lo \ Bit 0 = Ship's x_sign \ \ The ship's y-coordinate is calculated in the has1 routine from the size of \ its targetable area. Ships of type 0 are not shown. \ \ ****************************************************************************** .HATB \ Hangar group for X = 0 \ \ Shuttle (left) and Transporter (right) EQUB 9 \ Ship type = 9 = Shuttle EQUB %01010100 \ x_hi = %01010100 = 84, z_hi = 1 -> x = -84 EQUB %00111011 \ z_lo = %00111011 = 59, x_sign = 1 z = +315 EQUB 10 \ Ship type = 10 = Transporter EQUB %10000010 \ x_hi = %10000010 = 130, z_hi = 1 -> x = +130 EQUB %10110000 \ z_lo = %10110000 = 176, x_sign = 0 z = +432 EQUB 0 \ No third ship EQUB 0 EQUB 0 \ Hangar group for X = 9 \ \ Three cargo canisters (left, far right and forward, \ right) EQUB OIL \ Ship type = OIL = Cargo canister EQUB %01010000 \ x_hi = %01010000 = 80, z_hi = 1 -> x = -80 EQUB %00010001 \ z_lo = %00010001 = 17, x_sign = 1 z = +273 EQUB OIL \ Ship type = OIL = Cargo canister EQUB %11010001 \ x_hi = %11010001 = 209, z_hi = 2 -> x = +209 EQUB %00101000 \ z_lo = %00101000 = 40, x_sign = 0 z = +552 EQUB OIL \ Ship type = OIL = Cargo canister EQUB %01000000 \ x_hi = %01000000 = 64, z_hi = 1 -> x = +64 EQUB %00000110 \ z_lo = %00000110 = 6, x_sign = 0 z = +262 \ Hangar group for X = 18 \ \ Viper (right) and Krait (left) EQUB COPS \ Ship type = COPS = Viper EQUB %01100000 \ x_hi = %01100000 = 96, z_hi = 1 -> x = +96 EQUB %10010000 \ z_lo = %10010000 = 144, x_sign = 0 z = +400 EQUB KRA \ Ship type = KRA = Krait EQUB %00010000 \ x_hi = %00010000 = 16, z_hi = 1 -> x = -16 EQUB %11010001 \ z_lo = %11010001 = 209, x_sign = 1 z = +465 EQUB 0 \ No third ship EQUB 0 EQUB 0 \ Hangar group for X = 27 \ \ Viper (right and forward) and Krait (left) EQUB 16 \ Ship type = 16 = Viper EQUB %01010001 \ x_hi = %01010001 = 81, z_hi = 2 -> x = +81 EQUB %11111000 \ z_lo = %11111000 = 248, x_sign = 0 z = +760 EQUB 19 \ Ship type = 19 = Krait EQUB %01100000 \ x_hi = %01100000 = 96, z_hi = 1 -> x = -96 EQUB %01110101 \ z_lo = %01110101 = 117, x_sign = 1 z = +373 EQUB 0 \ No third ship EQUB 0 EQUB 0 \ ****************************************************************************** \ \ Name: HALL \ Type: Subroutine \ Category: Ship hangar \ Summary: Draw the ships in the ship hangar, then draw the hangar by sending \ an OSWORD 248 command to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ Half the time this will draw one of the four pre-defined ship hangar groups in \ HATB, and half the time this will draw a solitary Sidewinder, Mamba, Krait or \ Adder on a random position. In all cases, the ships will be randomly spun \ around on the ground so they can face in any direction, and larger ships are \ drawn higher up off the ground than smaller ships. \ \ The ships are drawn by the HAS1 routine, which uses the normal ship-drawing \ routine in LL9, and then the hangar background is drawn by sending an OSWORD \ 248 command to the I/O processor. \ \ ****************************************************************************** .HALL LDA #0 \ Send a #SETVDU19 0 command to the I/O processor to JSR DOVDU19 \ switch to the mode 1 palette for the space view, \ which is yellow (colour 1), red (colour 2) and cyan \ (colour 3) JSR UNWISE \ Call UNWISE, which does nothing in the 6502 Second \ Processor version of Elite (this routine does have a \ function in the disc version that isn't required here, \ so the authors presumably just cleared out the UNWISE \ routine rather than unplumbing it from the code) LDA #0 \ Clear the top part of the screen, draw a border box, JSR TT66 \ and set the current view type in QQ11 to 0 (space \ view) JSR DORND \ Set A and X to random numbers BPL HA7 \ Jump to HA7 if A is positive (50% chance) AND #3 \ Reduce A to a random number in the range 0-3 STA T \ Set X = A * 8 + A ASL A \ = 9 * A ASL A \ ASL A \ so X is a random number, either 0, 9, 18 or 27 ADC T TAX \ The following double loop calls the HAS1 routine three \ times to display three ships on screen. For each call, \ the values passed to HAS1 in XX15+2 to XX15 are taken \ from the HATB table, depending on the value in X, as \ follows: \ \ * If X = 0, pass bytes #0 to #2 of HATB to HAS1 \ then bytes #3 to #5 \ then bytes #6 to #8 \ \ * If X = 9, pass bytes #9 to #11 of HATB to HAS1 \ then bytes #12 to #14 \ then bytes #15 to #17 \ \ * If X = 18, pass bytes #18 to #20 of HATB to HAS1 \ then bytes #21 to #23 \ then bytes #24 to #26 \ \ * If X = 27, pass bytes #27 to #29 of HATB to HAS1 \ then bytes #30 to #32 \ then bytes #33 to #35 \ \ Note that the values are passed in reverse, so for the \ first call, for example, where we pass bytes #0 to #2 \ of HATB to HAS1, we call HAS1 with: \ \ XX15 = HATB+2 \ XX15+1 = HATB+1 \ XX15+2 = HATB LDY #3 \ Set CNT2 = 3 to act as an outer loop counter going STY CNT2 \ from 3 to 1, so the HAL8 loop is run 3 times .HAL8 LDY #2 \ Set Y = 2 to act as an inner loop counter going from \ 2 to 0 .HAL9 LDA HATB,X \ Copy the X-th byte of HATB to the Y-th byte of XX15, STA XX15,Y \ as described above INX \ Increment X to point to the next byte in HATB DEY \ Decrement Y to point to the previous byte in XX15 BPL HAL9 \ Loop back to copy the next byte until we have copied \ three of them (i.e. Y was 3 before the DEY) TXA \ Store X on the stack so we can retrieve it after the PHA \ call to HAS1 (as it contains the index of the next \ byte in HATB JSR HAS1 \ Call HAS1 to draw this ship in the hangar PLA \ Restore the value of X, so X points to the next byte TAX \ in HATB after the three bytes we copied into XX15 DEC CNT2 \ Decrement the outer loop counter in CNT2 BNE HAL8 \ Loop back to HAL8 to do it 3 times, once for each ship \ in the HATB table LDY #128 \ Set Y = 128 to send as byte #2 of the parameter block \ to the OSWORD 248 command below, to tell the I/O \ processor that there are multiple ships in the hangar BNE HA9 \ Jump to HA9 to display the ship hangar (this BNE is \ effectively a JMP as Y is never zero) .HA7 \ If we get here, A is a positive random number in the \ range 0-127 LSR A \ Set XX15+1 = A / 2 (random number 0-63) STA XX15+1 JSR DORND \ Set XX15 = random number 0-255 STA XX15 JSR DORND \ Set XX15+2 = #SH3 + random number 0-3 AND #3 \ ADC #SH3 \ which is the ship type of a Sidewinder, Mamba, Krait STA XX15+2 \ or Adder JSR HAS1 \ Call HAS1 to draw this ship in the hangar, with the \ following properties: \ \ * Random x-coordinate from -63 to +63 \ \ * Randomly chosen Sidewinder, Mamba, Krait or Adder \ \ * Random z-coordinate from +256 to +639 LDY #0 \ Set Y = 0 to use in the following instruction, to tell \ the hangar-drawing routine that there is just one ship \ in the hangar, so it knows not to draw between the \ ships .HA9 STY HANG+2 \ Store Y in byte #2 of the parameter block to the \ OSWORD 248 command below, to specify whether there \ are multiple ships in the hangar JSR UNWISE \ Call UNWISE, which (as noted above) does nothing in \ the 6502 Second Processor version of Elite LDA #248 \ Set A in preparation for sending an OSWORD 248 command LDX #LO(HANG) \ Set (Y X) to point to the HANG parameter block LDY #HI(HANG) JMP OSWORD \ Send an OSWORD 248 command to the I/O processor to \ draw the ship hangar, returning from the subroutine \ using a tail call .HANG EQUB 3 \ The number of bytes to transmit with this command EQUB 0 \ The number of bytes to receive with this command EQUB 0 \ Multiple ship flag: \ \ * 0 = there is just one ship in the hangar \ \ * 128 = there are multiple ships in the hangar \ ****************************************************************************** \ \ Name: HAS1 \ Type: Subroutine \ Category: Ship hangar \ Summary: Draw a ship in the ship hangar \ \ ------------------------------------------------------------------------------ \ \ The ship's position within the hangar is determined by the arguments and the \ size of the ship's targetable area, as follows: \ \ * The x-coordinate is (x_sign x_hi 0) from the arguments, so the ship can be \ left of centre or right of centre \ \ * The y-coordinate is negative and is lower down the screen for smaller \ ships, so smaller ships are drawn closer to the ground (because they are) \ \ * The z-coordinate is positive, with both z_hi (which is 1 or 2) and z_lo \ coming from the arguments \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ XX15 Bits 0-7 = Ship's z_lo \ Bit 0 = Ship's x_sign \ \ XX15+1 Bits 0-7 = Ship's x_hi \ Bit 0 = Ship's z_hi (1 if clear, or 2 if set) \ \ XX15+2 Non-zero = Ship type to draw \ 0 = Don't draw anything \ \ ****************************************************************************** .HAS1 JSR ZINF \ Call ZINF to reset the INWK ship workspace and reset \ the orientation vectors, with nosev pointing out of \ the screen, so this puts the ship flat on the \ horizontal deck (the y = 0 plane) with its nose \ pointing towards us LDA XX15 \ Set z_lo = XX15 STA INWK+6 LSR A \ Set the sign bit of x_sign to bit 0 of A ROR INWK+2 LDA XX15+1 \ Set x_hi = XX15+1 STA INWK LSR A \ Set z_hi = 1 + bit 0 of XX15+1 LDA #1 ADC #0 STA INWK+7 LDA #%10000000 \ Set bit 7 of y_sign, so y is negative STA INWK+5 STA RAT2 \ Set RAT2 = %10000000, so the yaw calls in HAL5 below \ are negative LDA #&0B \ Set the ship line heap pointer in INWK(34 33) to point STA INWK+34 \ to &0B00 \ --- Mod: Code added for red enemy lasers: -----------> LDY #2 \ Set the Y2 coordinate of the laser line in the ship LDA #255 \ line heap to 255 so there is no laser line STA (INWK+33),Y \ --- End of added code -------------------------------> JSR DORND \ We now perform a random number of small angle (3.6 STA XSAV \ degree) rotations to spin the ship on the deck while \ keeping it flat on the deck (a bit like spinning a \ bottle), so we set XSAV to a random number between 0 \ and 255 for the number of small yaw rotations to \ perform, so the ship could be pointing in any \ direction by the time we're done .HAL5 LDX #21 \ Rotate (sidev_x, nosev_x) by a small angle (yaw) LDY #9 JSR MVS5 LDX #23 \ Rotate (sidev_y, nosev_y) by a small angle (yaw) LDY #11 JSR MVS5 LDX #25 \ Rotate (sidev_z, nosev_z) by a small angle (yaw) LDY #13 JSR MVS5 DEC XSAV \ Decrement the yaw counter in XSAV BNE HAL5 \ Loop back to yaw a little more until we have yawed \ by the number of times in XSAV LDY XX15+2 \ Set Y = XX15+2, the ship type of the ship we need to \ draw BEQ HA1 \ If Y = 0, return from the subroutine (as HA1 contains \ an RTS) TYA \ Set X = 2 * Y ASL A TAX LDA XX21-2,X \ Set XX0(1 0) to the X-th address in the ship blueprint STA XX0 \ address lookup table at XX21, so XX0(1 0) now points LDA XX21-1,X \ to the blueprint for the ship we need to draw STA XX0+1 BEQ HA1 \ If the high byte of the blueprint address is 0, then \ this is not a valid blueprint address, so return from \ the subroutine (as HA1 contains an RTS) LDY #1 \ Set Q = ship byte #1 LDA (XX0),Y STA Q INY \ Set R = ship byte #2 LDA (XX0),Y \ STA R \ so (R Q) contains the ship's targetable area, which is \ a square number JSR LL5 \ Set Q = SQRT(R Q) LDA #100 \ Set y_lo = (100 - Q) / 2 SBC Q \ LSR A \ so the bigger the ship's targetable area, the smaller STA INWK+3 \ the magnitude of the y-coordinate, so because we set \ y_sign to be negative above, this means smaller ships \ are drawn lower down, i.e. closer to the ground, while \ larger ships are drawn higher up, as you would expect JSR TIDY \ Call TIDY to tidy up the orientation vectors, to \ prevent the ship from getting elongated and out of \ shape due to the imprecise nature of trigonometry \ in assembly language JMP LL9 \ Jump to LL9 to display the ship and return from the \ subroutine using a tail call \ ****************************************************************************** \ \ Name: UNWISE \ Type: Subroutine \ Category: Ship hangar \ Summary: Switch the main line-drawing routine between EOR and OR logic \ \ ------------------------------------------------------------------------------ \ \ This routine does nothing in the 6502 Second Processor version of Elite. It \ does have a function in the disc version, so the authors presumably just \ cleared out the UNWISE routine for the Second Processor version, rather than \ unplumbing it from the code. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ HA1 Contains an RTS \ \ ****************************************************************************** .UNWISE .HA1 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TACTICS (Part 1 of 7) \ Type: Subroutine \ Category: Tactics \ Summary: Apply tactics: Process missiles, both enemy missiles and our own \ Deep dive: Program flow of the tactics routine \ \ ------------------------------------------------------------------------------ \ \ 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 \ \ ****************************************************************************** .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 TA21 \ Jump down to part 3 to set up the vectors and skip \ straight to aggressive manoeuvring JSR TA87+3 \ The missile has hit our ship, so call TA87+3 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 \ --- Mod: Code removed for two-player Elite: ---------> \LDA ECMA \ If an E.C.M. is currently active (either ours or an \BNE TA35 \ opponent's), jump to TA35 to destroy this missile \ --- And replaced by: --------------------------------> LDA ECMA \ If either player's E.C.M. is currently active, jump ORA player2ECMA \ to TA35 to destroy this missile BNE TA35 \ --- End of replacement ------------------------------> 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. \ --- Mod: Code removed for two-player Elite: ---------> \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 TA35 \ space station), jump to TA35 to destroy this missile, \ \ as the space station ain't kidding around \ --- And replaced by: --------------------------------> LDA INWK+32 \ Fetch the AI flag from byte #32 and if only bits 7 and CMP #%10000100 \ a target of slot #2 are set then the missile is BEQ tact1 \ heading for player 2, so jump to tact1 to apply damage \ to player 2 \ If we get here then the missile's target is another \ missile, so we need to destroy the target \ --- End of replacement ------------------------------> 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 \ --- Mod: Code added for two-player Elite: -----------> BNE TA35 \ Jump to TA35 to destroy this missile (this BNE is \ effectively a JMP as A is never zero) .tact1 \ If we get here then the missile has exploded near to \ player 2's ship LDA #250 \ Call OOPS to damage the ship by 250, which is a pretty JSR Player2OOPS \ big hit, and update the scores \ --- End of added code -------------------------------> .TA35 \ --- Mod: Code removed for two-player Elite: ---------> \LDA INWK \ Set A = x_lo OR y_lo OR z_lo of the missile \ORA INWK+3 \ORA INWK+6 \ \BNE TA87 \ If A is non-zero then the missile is not near our \ \ ship, so jump to TA87 to skip 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 \ --- End of removed code -----------------------------> .TA87 \ --- Mod: Code removed for two-player Elite: ---------> \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) \ --- And replaced by: --------------------------------> LDX #7 \ Call EXNO to make the sound of us blowing up a missile JSR EXNO \ --- End of replacement ------------------------------> 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 \ --- Mod: Code removed for two-player Elite: ---------> \JMP ECBLB2 \ The target has E.C.M., so jump to ECBLB2 to set it \ \ off, returning from the subroutine using a tail call \ --- And replaced by: --------------------------------> LDA player2JSTK \ If player 2 is not an AI Pilot, jump tp TA19S so we BNE TA19S \ don't set off player 2's E.C.M. JMP Player2ECBLB2 \ The target has E.C.M. so it must be player 2, and we \ know it is an AI Pilot, so jump to ECBLB2 to set off \ the E.C.M., returning from the subroutine using a tail \ call \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: TACTICS (Part 2 of 7) \ Type: Subroutine \ Category: Tactics \ Summary: Apply tactics: Escape pod, station, lone Thargon, safe-zone pirate \ Deep dive: Program flow of the tactics routine \ Aggression and hostility in ship 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 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 \ --- Mod: Code removed for two-player Elite: ---------> \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 \ The station is not hostile, so check how many \BNE TA1 \ 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 \ \ \ If we get here then this is the space station and it \ \ is 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 #7 \ and if there are 7 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 (56 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, zero the \STX INWK+32 \ aggression level and remove E.C.M. \ \STX NEWB \ Set the ship's NEWB flags to %00000000 so the ship we \ \ spawn below will inherit the default values from E% \ \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), though we override this in \ \ the next instructions \ \LDA #0 \ Set byte #32 to %00000000 to disable AI, zero the \STA INWK+32 \ aggression level and remove 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 \LDA INWK+35 \ maximum value from the ship's blueprint pointed to by \CMP (XX0),Y \ XX0, then skip the next instruction \BCS TA21 \ \INC INWK+35 \ The ship's energy is not at maximum, so recharge the \ \ energy banks by 1 \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: TACTICS (Part 3 of 7) \ Type: Subroutine \ Category: Tactics \ Summary: Apply tactics: Calculate dot product to determine ship's aim \ Deep dive: Program flow of the tactics routine \ Aggression and hostility in ship tactics \ \ ------------------------------------------------------------------------------ \ \ 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 \ --- Mod: Code removed for two-player Elite: ---------> \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 \ --- End of removed code -----------------------------> .TA14 \ --- Mod: Code added for two-player Elite: -----------> LDA XSAV \ If this is not player 2, skip the following CMP #2 BNE tact2 STZ player2Firing \ Reset player2Firing to denote that player 2 is not \ firing its laser at us .tact2 \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \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 3 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 \ Clear bits 1 to 6 of the AI flag in byte #32 (to set \AND #%10000001 \ the aggression level to zero) \STA INWK+32 \ \.TN4 \ --- End of removed code -----------------------------> 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) \ Type: Subroutine \ Category: Tactics \ Summary: Apply tactics: Check energy levels, maybe launch escape pod if low \ Deep dive: Program flow of the tactics routine \ \ ------------------------------------------------------------------------------ \ \ 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 \ \ ****************************************************************************** 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 \ --- Mod: Code removed for two-player Elite: ---------> \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 \ --- End of removed code -----------------------------> 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 \ --- Mod: Code removed for two-player Elite: ---------> \LDY #14 \ Set A = the ship's maximum energy / 2 \LDA (XX0),Y \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 \LDA E%-1,X \ table at E%, and if bit 7 is clear (i.e. this ship \BPL ta3 \ does not have an escape pod), jump to ta3 to skip the \ \ 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 #%00000000 \ Set the AI flag to 0 to disable AI, set aggression to \STA INWK+32 \ zero and disable any 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 \ --- And replaced by: --------------------------------> LDA player2ENERGY \ If the ship's current energy is at least half, jump BMI TA3 \ down to TA3 to skip launching a missile \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: TACTICS (Part 5 of 7) \ Type: Subroutine \ Category: Tactics \ Summary: Apply tactics: Consider whether to launch a missile at us \ Deep dive: Program flow of the tactics routine \ \ ------------------------------------------------------------------------------ \ \ 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 \ --- Mod: Code removed for two-player Elite: ---------> \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 \ --- End of removed code -----------------------------> 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) \ 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 \ \ ------------------------------------------------------------------------------ \ \ 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 \ 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 at us, they can hit us CPX #160 \ If X < 160, i.e. X > -32, 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 LDA (XX0),Y \ 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 \ --- Mod: Code added for two-player Elite: -----------> LDA XSAV \ If this is not player 2, skip the following CMP #2 BNE tact3 SEC \ Set bit 7 of player2Firing to denote that NPC player 2 ROR player2Firing \ is firing its lasers .tact3 \ --- End of added code -------------------------------> 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 not in the enemy BCC TA4 \ ship's crosshairs, so jump to TA4 to skip the laser \ checks LDA (XX0),Y \ 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) \ --- Mod: Code added for two-player Elite: -----------> LDA XSAV \ If this is not player 2, skip the following CMP #2 BNE tact4 BIT gameOver \ If this is the game over screen, jump to tact4 to skip BMI tact4 \ processing the scores LDA player2GameType \ If player 2 is playing survival, they don't have a BEQ tact4 \ score, so jump to tact4 to skip updating the score JSR Player2ee3 \ Print player 2's score to remove it from the screen INC player2Score \ Increment player 2's score (as player 2 is the AI) BNE P%+5 INC player2Score+1 JSR Player2ee3 \ Print player 2's score LDA player2Score+1 \ If the score is less than the target, jump to tact4 to CMP player2Target+1 \ keep playing BCC tact4 LDA player2Score CMP player2Target BCC tact4 LDA #2 \ If we get here then player 2 has reached their target, JMP DEATH \ so set A to indicate that player 2 has won and jump to \ DEATH to end the game .tact4 \ --- End of added code -------------------------------> DEC INWK+28 \ Halve the attacking ship's acceleration in byte #28 LDA ECMA \ If an E.C.M. is currently active (either ours or an BNE TA9-1 \ opponent's), return from the subroutine without making \ the laser-strike sound (as TA9-1 contains an RTS) LDA #8 \ Call the NOISE routine with A = 8 to make the sound JMP NOISE \ of us being hit by lasers, returning from the \ subroutine using a tail call \ ****************************************************************************** \ \ Name: TACTICS (Part 7 of 7) \ Type: Subroutine \ Category: Tactics \ Summary: Apply tactics: Set pitch, roll, and acceleration \ Deep dive: Program flow of the tactics routine \ Aggression and hostility in ship tactics \ \ ------------------------------------------------------------------------------ \ \ 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 \ \ TA9-1 Contains an RTS \ \ ****************************************************************************** .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 TA15 \ If A = 0 then the ship is pretty close to us, so jump \ to TA15 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 the following comparison ignores \ the AI flag in bit 7 (as we already know bit 7 is set \ in byte #32) CMP INWK+32 \ If A >= byte #32 (the ship's AI flag) then jump down BCS TA15 \ to TA15 so it heads away from us \ We get here if byte #32 > A, where byte #32 is \ composed of the following: \ \ * Bit 7 set = AI is enabled \ \ * Bits 1-6 = aggression level (0 to 63) \ \ * Bit 0 set = ship has E.C.M. \ \ We set bit 7 of A above, so if we get here we know the \ ship has AI enabled, and the comparison then boils \ down to the following: \ \ Aggression level * 2 + E.C.M. > random number 0-127 \ \ In other words, higher aggression levels increase the \ chances of a ship changing direction to head towards \ us - or, to put it another way, ships with higher \ aggression levels are spoiling for a fight, with \ E.C.M. making them even more aggressive \ \ Thargoids and missiles both have an aggression level \ of 63 out of 63, which explains an awful lot \ \ Interestingly, escape pods also have a maximum \ agression level, but in this case it makes them fly \ towards the planet rather than towards us .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 .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 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 TA12 (so if RAT2 = 0, we always BCC TA12 \ 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) STA INWK+29 .TA12 .TA6 LDA CNT \ Fetch the dot product, and if it's negative jump to BMI TA9 \ TA9, as the ships are facing away from each other and \ the ship might want to slow down to take another shot CMP CNT2 \ The dot product is positive, so the ships are facing BCC TA9 \ each other. If A < CNT2 then the ships are not heading \ directly towards each other, so jump to TA9 to slow \ down .PH10E LDA #3 \ Otherwise set the acceleration in byte #28 to 3 STA INWK+28 RTS \ Return from the subroutine .TA9 AND #%01111111 \ Clear the sign bit of the dot product in A CMP #18 \ If A < 18 then the ship is way off the XX15 vector, so BCC TA10 \ return from the subroutine (TA10 contains an RTS) \ without slowing down, as it still has quite a bit of \ turning to do to get on course LDA #&FF \ Otherwise set A = -1 LDX TYPE \ If this is not a missile then skip the ASL instruction CPX #MSL BNE P%+3 ASL A \ This is a missile, so set A = -2, as missiles are more \ nimble and can brake more quickly 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 \ ****************************************************************************** \ \ Name: DOCKIT \ Type: Subroutine \ Category: Flight \ Summary: Apply docking manoeuvres to the ship in INWK \ Deep dive: The docking computer \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.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 \ \\BEQ PH10 \ This instruction is commented out in the original \ \ source \ \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 \ \ \ 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 \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: VCSU1 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate vector K3(8 0) = [x y z] - coordinates of the sun or \ space station \ \ ------------------------------------------------------------------------------ \ \ 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. \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.VCSU1 \ \LDA #LO(K%+NI%) \ Set the low byte of V(1 0) to point to the coordinates \STA V \ of the sun or space station \ \LDA #HI(K%+NI%) \ 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 \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: VCSUB \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate vector K3(8 0) = [x y z] - coordinates in (A V) \ \ ------------------------------------------------------------------------------ \ \ 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 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate K3 = (x_sign x_hi x_lo) - V(1 0) \ \ ------------------------------------------------------------------------------ \ \ 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 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 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Calculate the dot product of XX15 and one of the space station's \ orientation vectors \ \ ------------------------------------------------------------------------------ \ \ 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%+NI%,Y \ Set Q = the Y-th byte of K%+NI%, 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%+NI%+2,Y \ Set Q = the Y+2-th byte of K%+NI%, 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%+NI%+4,Y \ Set Q = the Y+2-th byte of K%+NI%, 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 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Negate the vector in XX15 so it points in the opposite direction \ \ ****************************************************************************** .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 \ Type: Subroutine \ Category: Flight \ Summary: Calculate the vector from the ideal docking position to the ship \ \ ------------------------------------------------------------------------------ \ \ 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%+NI%+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%+NI%+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%+NI%+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 \ Type: Subroutine \ Category: Tactics \ Summary: Work out if the ship in INWK is in our crosshairs \ Deep dive: In the crosshairs \ \ ------------------------------------------------------------------------------ \ \ 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 LDA (XX0),Y \ 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 LDA (XX0),Y \ 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 \ Type: Subroutine \ Category: Tactics \ Summary: Launch a ship straight ahead of us, below the laser sights \ \ ------------------------------------------------------------------------------ \ \ This is used in three 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 \ Type: Subroutine \ Category: Tactics \ Summary: Fire a missile from our ship \ \ ------------------------------------------------------------------------------ \ \ We fired a missile, so send it streaking away from us to unleash mayhem and \ destruction on our sworn enemies. \ \ ****************************************************************************** .FRMIS \ --- Mod: Code added for two-player Elite: -----------> LDX player1Missile \ If player 1 already has a missile in-flight, then its BNE FR1 \ non-zero slot number will be in player1Missile, so \ jump to FR1 to display a "missile jammed" message \ --- End of added code -------------------------------> 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 \ --- Mod: Code added for two-player Elite: -----------> LDA newShipSlot \ Set player1Missile to the slot number of the new STA player1Missile \ missile \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \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 or station hostile, \ \ and if this is a ship, wake up its AI and give it a \ \ kick of speed \ --- End of removed code -----------------------------> LDY #0 \ We have just launched a missile, so we need to remove JSR ABORT \ missile lock and hide the leftmost indicator on the \ dashboard by setting it to black (Y = 0) DEC NOMSL \ Reduce the number of missiles we have by 1 \ --- Mod: Code added for two-player Elite: -----------> LDA #120 \ Print recursive token 120 ("INCOMING MISSILE") as an JSR Player2MESS \ in-flight message \ --- End of added code -------------------------------> LDA #48 \ Call the NOISE routine with A = 48 to make the sound JMP NOISE \ of a missile launch, returning from the subroutine \ using a tail call \ ****************************************************************************** \ \ Name: ANGRY \ Type: Subroutine \ Category: Tactics \ Summary: Make a ship or station hostile, and if this is a ship then enable \ the ship's AI and give it a kick of speed \ Deep dive: Aggression and hostility in ship tactics \ \ ------------------------------------------------------------------------------ \ \ This routine makes a ship or station angry by setting the hostile flag in \ NEWB, and for ships it also means enabling the ship's AI and giving it a kick \ of turning acceleration. Later calls to TACTICS may make the ship start to \ attack us if it has a high enough aggression level. \ \ ------------------------------------------------------------------------------ \ \ 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 \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.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 \ \ zero aggression, 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, so the ship can start acting \ \ according to its aggression level \ \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%+NI%+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%+NI%+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 \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: FR1 \ Type: Subroutine \ Category: Tactics \ Summary: Display the "missile jammed" message \ \ ------------------------------------------------------------------------------ \ \ 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: Player2FR1 \ Type: Subroutine \ Category: Tactics \ Summary: Display the "missile jammed" message for player 2 \ \ ------------------------------------------------------------------------------ \ \ 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 \ \ ****************************************************************************** .Player2FR1 LDA #201 \ Print recursive token 41 ("MISSILE JAMMED") as an JMP Player2MESS \ in-flight message and return from the subroutine using \ a tail call \ --- Mod: Code added for two-player Elite: -----------> \ ****************************************************************************** \ \ Name: RemoveMissile \ Type: Subroutine \ Category: Tactics \ Summary: Remove a missile from the player counts so the relevant player can \ launch another one \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ Y The number of the missile slot that we are removing \ (4 or 5) \ ****************************************************************************** .RemoveMissile CPY player1Missile \ If player1Missile matches Y then we are removing the BNE rmis1 \ missile launched by player 1, so zero player1Missile STZ player1Missile .rmis1 CPY player2Missile \ If player2Missile matches Y then we are removing the BNE rmis2 \ missile launched by player 2, so zero player2Missile STZ player2Missile .rmis2 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: SESCP \ Type: Subroutine \ Category: Flight \ Summary: Spawn an escape pod from the current (parent) ship \ Deep dive: Aggression and hostility in ship tactics \ \ ------------------------------------------------------------------------------ \ \ 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 use as an AI flag that has AI enabled, an \ aggression level of 63 out of 63, and no E.C.M. \ \ When spawning an escape pod, this high agression level \ makes the pod turn towards the planet rather than \ towards us \ \ This instruction is also used as an entry point to \ spawn missile (when calling via the SFS1-2 entry \ point), in which case the missile has AI (bit 7 set), \ is hostile (bit 6 set) and has been launched (bit 0 \ clear); the target slot number is set to 31, but this \ is ignored as the hostile flag means we are the target \ Fall through into SFS1 to spawn the escape pod or \ missile \ ****************************************************************************** \ \ Name: SFS1 \ Type: Subroutine \ Category: Universe \ Summary: Spawn a child ship from the current (parent) ship \ \ ------------------------------------------------------------------------------ \ \ 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 Used to add a missile to the local bubble that that has \ AI (bit 7 set), is hostile (bit 6 set) and has been \ launched (bit 0 clear); the target slot number is set to \ 31, but this is ignored as the hostile flags means we \ are the target \ \ ****************************************************************************** .SFS1 STA T1 \ Store the child ship's AI flag in T1 \ 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) \ --- Mod: Code removed for two-player Elite: ---------> \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 \ --- And replaced by: --------------------------------> \ Spawn the missile below the ship TXA \ Store the child's ship type in X on the stack PHA LDX #0 \ Add -roofv_x_hi / 2 to (x_lo, x_hi, x_sign) to get the LDA INWK+16 \ child's x-coordinate TAY LSR A STA R TYA EOR #%10000000 JSR MVT1-2 LDX #3 \ Add -roofv_y_hi / 2 to (y_lo, y_hi, y_sign) to get the LDA INWK+18 \ child's y-coordinate TAY LSR A STA R TYA EOR #%10000000 JSR MVT1-2 LDX #6 \ Add -roofv_z_hi / 2 to (z_lo, z_hi, z_sign) to get the LDA INWK+20 \ child's z-coordinate TAY LSR A STA R TYA EOR #%10000000 JSR MVT1-2 PLA \ Restore the child's ship type from the stack into X TAX \ --- End of replacement ------------------------------> 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 \ --- Mod: Code removed for two-player Elite: ---------> \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 \ --- End of removed code -----------------------------> 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 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 \ Type: Subroutine \ Category: Moving \ Summary: Move a ship in space along one of the coordinate axes \ \ ------------------------------------------------------------------------------ \ \ 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: LL164 \ Type: Subroutine \ Category: Drawing circles \ Summary: Make the hyperspace sound and draw the hyperspace tunnel \ \ ------------------------------------------------------------------------------ \ \ See the IRQ1 routine for details on the multi-coloured effect that's used. \ \ ****************************************************************************** .LL164 LDA #56 \ Call the NOISE routine with A = 56 to make the sound JSR NOISE \ of the hyperspace drive being engaged LDA #DOhfx \ Send a #DOhfx 1 command to the I/O processor to tell JSR OSWRCH \ it to show hyperspace colours in the top part of the LDA #1 \ screen JSR OSWRCH LDA #4 \ Set the step size for the hyperspace rings to 4, so \ there are more sections in the rings and they are \ quite round (compared to the step size of 8 used in \ the much more polygonal launch rings) JSR HFS2 \ Call HFS2 to draw the hyperspace tunnel rings LDA #DOhfx \ Send a #DOhfx 0 command to the I/O processor to tell JSR OSWRCH \ it to show normal colours in the top part of the LDA #0 \ screen, returning from the subroutine using a tail JMP OSWRCH \ call \ ****************************************************************************** \ \ Name: LAUN \ Type: Subroutine \ Category: Drawing circles \ Summary: Make the launch sound and draw the launch tunnel \ \ ------------------------------------------------------------------------------ \ \ This is shown when launching from or docking with the space station. \ \ ****************************************************************************** .LAUN LDA #48 \ Call the NOISE routine with A = 48 to make the sound JSR NOISE \ of the ship launching from the station JSR ball1 \ Use the standard ball line heap LDA #8 \ Set the step size for the launch tunnel rings to 8, so \ there are fewer sections in the rings and they are \ quite polygonal (compared to the step size of 4 used \ in the much rounder hyperspace rings) \ Fall through into HFS2 to draw the launch tunnel rings \ ****************************************************************************** \ \ Name: HFS2 \ Type: Subroutine \ Category: Drawing circles \ Summary: Draw the launch or hyperspace tunnel \ \ ------------------------------------------------------------------------------ \ \ The animation gets drawn like this. First, we draw a circle of radius 8 at the \ centre, and then double the radius, draw another circle, double the radius \ again and draw a circle, and we keep doing this until the radius is bigger \ than 160 (which goes beyond the edge of the screen, which is 256 pixels wide, \ equivalent to a radius of 128). We then repeat this whole process for an \ initial circle of radius 9, then radius 10, all the way up to radius 15. \ \ This has the effect of making the tunnel appear to be racing towards us as we \ hurtle out into hyperspace or through the space station's docking tunnel. \ \ The hyperspace effect is done in a full mode 2 screen, which makes the rings \ all coloured and zig-zaggy, while the launch screen is in the normal \ four-colour mode 1 screen. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The step size of the straight lines making up the rings \ (4 for launch, 8 for hyperspace) \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ HFS1 Don't clear the screen, and draw 8 concentric rings \ with the step size in STP \ \ ****************************************************************************** .HFS2 STA STP \ Store the step size in A JSR TTX66 \ Clear the screen and draw a border box .HFS1 LDX #X \ Set K3 = #X (the x-coordinate of the centre of the STX K3 \ screen) LDX #Y \ Set K4 = #Y (the y-coordinate of the centre of the STX K4 \ screen) LDX #0 \ Set X = 0 STX XX4 \ Set XX4 = 0, which we will use as a counter for \ drawing eight concentric rings STX K3+1 \ Set the high bytes of K3(1 0) and K4(1 0) to 0 STX K4+1 .HFL5 JSR HFL1 \ Call HFL1 below to draw a set of rings, with each one \ twice the radius of the previous one, until they won't \ fit on-screen INC XX4 \ Increment the counter and fetch it into X LDX XX4 CPX #8 \ If we haven't drawn 8 sets of rings yet, loop back to BNE HFL5 \ HFL5 to draw the next ring RTS \ Return from the subroutine .HFL1 LDA XX4 \ Set K to the ring number in XX4 (0-7) + 8, so K has AND #7 \ a value of 8 to 15, which we will use as the starting CLC \ radius for our next set of rings ADC #8 STA K .HFL2 LDA #1 \ Set LSP = 1 to reset the ball line heap STA LSP JSR CIRCLE2 \ Call CIRCLE2 to draw a circle with the centre at \ (K3(1 0), K4(1 0)) and radius K ASL K \ Double the radius in K BCS HF8 \ If the radius had a 1 in bit 7 before the above shift, \ then doubling K will means the circle will no longer \ fit on the screen (which is width 256), so jump to \ HF8 to stop drawing circles LDA K \ If the radius in K <= 160, loop back to HFL2 to draw CMP #160 \ another one BCC HFL2 .HF8 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: STARS2 \ Type: Subroutine \ Category: Stardust \ Summary: Process the stardust for the left or right view \ Deep dive: Stardust in the side views \ \ ------------------------------------------------------------------------------ \ \ This moves the stardust sideways according to our speed and which side we are \ looking out of, and applies our current pitch and roll to each particle of \ dust, so the stardust moves correctly when we steer our ship. \ \ These are the calculations referred to in the commentary: \ \ 1. delta_x = 8 * 256 * speed / z_hi \ 2. x = x + delta_x \ \ 3. x = x + beta * y \ 4. y = y - beta * x \ \ 5. x = x - alpha * x * y \ 6. y = y + alpha * y * y + alpha \ \ For more information see the associated deep dive. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The view to process: \ \ * X = 1 for left view \ \ * X = 2 for right view \ \ ****************************************************************************** .STARS2 LDA #0 \ Set A to 0 so we can use it to capture a sign bit CPX #2 \ If X >= 2 then the C flag is set ROR A \ Roll the C flag into the sign bit of A and store in STA RAT \ RAT, so: \ \ * Left view, C is clear so RAT = 0 (positive) \ \ * Right view, C is set so RAT = 128 (negative) \ \ RAT represents the end of the x-axis where we want new \ stardust particles to come from: positive for the left \ view where new particles come in from the right, \ negative for the right view where new particles come \ in from the left EOR #%10000000 \ Set RAT2 to the opposite sign, so: STA RAT2 \ \ * Left view, RAT2 = 128 (negative) \ \ * Right view, RAT2 = 0 (positive) \ \ RAT2 represents the direction in which stardust \ particles should move along the x-axis: negative for \ the left view where particles go from right to left, \ positive for the right view where particles go from \ left to right JSR ST2 \ Call ST2 to flip the signs of the following if this is \ the right view: ALPHA, ALP2, ALP2+1, BET2 and BET2+1 LDY NOSTM \ Set Y to the current number of stardust particles, so \ we can use it as a counter through all the stardust .STL2 LDA SZ,Y \ Set A = ZZ = z_hi STA ZZ \ We also set ZZ to the original value of z_hi, which we \ use below to remove the existing particle LSR A \ Set A = z_hi / 8 LSR A LSR A JSR DV41 \ Call DV41 to set the following: \ \ (P R) = 256 * DELTA / A \ = 256 * speed / (z_hi / 8) \ = 8 * 256 * speed / z_hi \ \ This represents the distance we should move this \ particle along the x-axis, let's call it delta_x LDA P \ Set S = P but with the sign from RAT2, so we now have EOR RAT2 \ the distance delta_x with the correct sign in (S R): STA S \ \ (S R) = delta_x \ = 8 * 256 * speed / z_hi \ \ So (S R) is the delta, signed to match the direction \ the stardust should move in, which is result 1 above LDA SXL,Y \ Set (A P) = (x_hi x_lo) STA P \ = x LDA SX,Y STA X1 \ Set X1 = A, so X1 contains the original value of x_hi, \ which we use below to remove the existing particle JSR ADD \ Call ADD to calculate: \ \ (A X) = (A P) + (S R) \ = x + delta_x STA S \ Set (S R) = (A X) STX R \ = x + delta_x LDA SY,Y \ Set A = y_hi STA Y1 \ Set Y1 = A, so Y1 contains the original value of y_hi, \ which we use below to remove the existing particle EOR BET2 \ Give A the correct sign of A * beta, i.e. y_hi * beta LDX BET1 \ Fetch |beta| from BET1, the pitch angle JSR MULTS-2 \ Call MULTS-2 to calculate: \ \ (A P) = X * A \ = beta * y_hi JSR ADD \ Call ADD to calculate: \ \ (A X) = (A P) + (S R) \ = beta * y + x + delta_x STX XX \ Set XX(1 0) = (A X), which gives us results 2 and 3 STA XX+1 \ above, done at the same time: \ \ x = x + delta_x + beta * y LDX SYL,Y \ Set (S R) = (y_hi y_lo) STX R \ = y LDX Y1 STX S LDX BET1 \ Fetch |beta| from BET1, the pitch angle EOR BET2+1 \ Give A the opposite sign to x * beta JSR MULTS-2 \ Call MULTS-2 to calculate: \ \ (A P) = X * A \ = -beta * x JSR ADD \ Call ADD to calculate: \ \ (A X) = (A P) + (S R) \ = -beta * x + y STX YY \ Set YY(1 0) = (A X), which gives us result 4 above: STA YY+1 \ \ y = y - beta * x LDX ALP1 \ Set X = |alpha| from ALP2, the roll angle EOR ALP2 \ Give A the correct sign of A * alpha, i.e. y_hi * \ alpha JSR MULTS-2 \ Call MULTS-2 to calculate: \ \ (A P) = X * A \ = alpha * y STA Q \ Set Q = high byte of alpha * y LDA XX \ Set (S R) = XX(1 0) STA R \ = x LDA XX+1 \ STA S \ and set A = y_hi at the same time EOR #%10000000 \ Flip the sign of A = -x_hi JSR MAD \ Call MAD to calculate: \ \ (A X) = Q * A + (S R) \ = alpha * y * -x + x STA XX+1 \ Store the high byte A in XX+1 TXA \ Store the low byte X in x_lo STA SXL,Y \ So (XX+1 x_lo) now contains result 5 above: \ \ x = x - alpha * x * y LDA YY \ Set (S R) = YY(1 0) STA R \ = y LDA YY+1 \ STA S \ and set A = y_hi at the same time JSR MAD \ Call MAD to calculate: \ \ (A X) = Q * A + (S R) \ = alpha * y * y_hi + y STA S \ Set (S R) = (A X) STX R \ = y + alpha * y * y LDA #0 \ Set P = 0 STA P LDA ALPHA \ Set A = alpha, so: \ \ (A P) = (alpha 0) \ = alpha / 256 JSR PIX1 \ Call PIX1 to calculate the following: \ \ (YY+1 y_lo) = (A P) + (S R) \ = alpha * 256 + y + alpha * y * y \ \ i.e. y = y + alpha / 256 + alpha * y^2, which is \ result 6 above \ \ PIX1 also draws a particle at (X1, Y1) with distance \ ZZ, which will remove the old stardust particle, as we \ set X1, Y1 and ZZ to the original values for this \ particle during the calculations above \ We now have our newly moved stardust particle at \ x-coordinate (XX+1 x_lo) and y-coordinate (YY+1 y_lo) \ and distance z_hi, so we draw it if it's still on \ screen, otherwise we recycle it as a new bit of \ stardust and draw that LDA XX+1 \ Set X1 and x_hi to the high byte of XX in XX+1, so STA SX,Y \ the new x-coordinate is in (x_hi x_lo) and the high STA X1 \ byte is in X1 AND #%01111111 \ If |x_hi| >= 116 then jump to KILL2 to recycle this CMP #116 \ particle, as it's gone off the side of the screen, BCS KILL2 \ and rejoin at STC2 with the new particle LDA YY+1 \ Set Y1 and y_hi to the high byte of YY in YY+1, so STA SY,Y \ the new x-coordinate is in (y_hi y_lo) and the high STA Y1 \ byte is in Y1 AND #%01111111 \ If |y_hi| >= 116 then jump to ST5 to recycle this CMP #116 \ particle, as it's gone off the top or bottom of the BCS ST5 \ screen, and rejoin at STC2 with the new particle .STC2 JSR PIXEL2 \ Draw a stardust particle at (X1,Y1) with distance ZZ, \ i.e. draw the newly moved particle at (x_hi, y_hi) \ with distance z_hi \ --- Mod: Code added for two-player Elite: -----------> BIT drawPlayerView \ If we are drawing player 1's stars, jump to star3 to BPL star3 \ check against zero \ We are drawing player 2's stars, so check against the \ table's halfway point DEY \ Decrement the loop counter to point to the next \ stardust particle CPY #NOST/2 \ Is this the last star for player 2's view? BEQ P%+5 \ If we have just done the last particle, skip the next \ instruction to return from the subroutine JMP STL2 \ We have more stardust to process, so jump back up to \ STL2 for the next particle RTS \ Return from the subroutine .star3 \ --- End of added code -------------------------------> DEY \ Decrement the loop counter to point to the next \ stardust particle BEQ ST2 \ If we have just done the last particle, skip the next \ instruction to return from the subroutine JMP STL2 \ We have more stardust to process, so jump back up to \ STL2 for the next particle \ Fall through into ST2 to restore the signs of the \ following if this is the right view: ALPHA, ALP2, \ ALP2+1, BET2 and BET2+1 .ST2 LDA ALPHA \ If this is the right view, flip the sign of ALPHA EOR RAT STA ALPHA LDA ALP2 \ If this is the right view, flip the sign of ALP2 EOR RAT STA ALP2 EOR #%10000000 \ If this is the right view, flip the sign of ALP2+1 STA ALP2+1 LDA BET2 \ If this is the right view, flip the sign of BET2 EOR RAT STA BET2 EOR #%10000000 \ If this is the right view, flip the sign of BET2+1 STA BET2+1 RTS \ Return from the subroutine .KILL2 JSR DORND \ Set A and X to random numbers \ --- Mod: Code added for two-player Elite: -----------> AND #%10111111 \ Halve the vertical range for stardust \ --- End of added code -------------------------------> STA Y1 \ Set y_hi and Y1 to random numbers, so the particle STA SY,Y \ starts anywhere along the y-axis LDA #115 \ Make sure A is at least 115 and has the sign in RAT ORA RAT STA X1 \ Set x_hi and X1 to A, so this particle starts on the STA SX,Y \ correct edge of the screen for new particles BNE STF1 \ Jump down to STF1 to set the z-coordinate (this BNE is \ effectively a JMP as A will never be zero) .ST5 JSR DORND \ Set A and X to random numbers STA X1 \ Set x_hi and X1 to random numbers, so the particle STA SX,Y \ starts anywhere along the x-axis LDA #110 \ Make sure A is at least 110 and has the sign in AL2+1, ORA ALP2+1 \ the flipped sign of the roll angle alpha \ --- Mod: Code added for two-player Elite: -----------> AND #%10111111 \ Halve the vertical range for stardust \ --- End of added code -------------------------------> STA Y1 \ Set y_hi and Y1 to A, so the particle starts at the STA SY,Y \ top or bottom edge, depending on the current roll \ angle alpha .STF1 JSR DORND \ Set A and X to random numbers ORA #8 \ Make sure A is at least 8 and store it in z_hi and STA ZZ \ ZZ, so the new particle starts at any distance from STA SZ,Y \ us, but not too close BNE STC2 \ Jump up to STC2 to draw this new particle (this BNE is \ effectively a JMP as A will never be zero) \ ****************************************************************************** \ \ Name: MU5 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Set K(3 2 1 0) = (A A A A) and clear the C flag \ \ ------------------------------------------------------------------------------ \ \ In practice this is only called via a BEQ following an AND instruction, in \ which case A = 0, so this routine effectively does this: \ \ K(3 2 1 0) = 0 \ \ ****************************************************************************** .MU5 STA K \ Set K(3 2 1 0) to (A A A A) STA K+1 STA K+2 STA K+3 CLC \ Clear the C flag RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MULT3 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate K(3 2 1 0) = (A P+1 P) * Q \ Deep dive: Shift-and-add multiplication \ \ ------------------------------------------------------------------------------ \ \ Calculate the following multiplication between a signed 24-bit number and a \ signed 8-bit number, returning the result as a signed 32-bit number: \ \ K(3 2 1 0) = (A P+1 P) * Q \ \ The algorithm is the same shift-and-add algorithm as in routine MULT1, but \ extended to cope with more bits. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag The C flag is cleared \ \ ****************************************************************************** .MULT3 STA R \ Store the high byte of (A P+1 P) in R AND #%01111111 \ Set K+2 to |A|, the high byte of K(2 1 0) STA K+2 LDA Q \ Set A to bits 0-6 of Q, so A = |Q| AND #%01111111 BEQ MU5 \ If |Q| = 0, jump to MU5 to set K(3 2 1 0) to 0, \ returning from the subroutine using a tail call SEC \ Set T = |Q| - 1 SBC #1 STA T \ We now use the same shift-and-add algorithm as MULT1 \ to calculate the following: \ \ K(2 1 0) = K(2 1 0) * |Q| \ \ so we start with the first shift right, in which we \ take (K+2 P+1 P) and shift it right, storing the \ result in K(2 1 0), ready for the multiplication loop \ (so the multiplication loop actually calculates \ (|A| P+1 P) * |Q|, as the following sets K(2 1 0) to \ (|A| P+1 P) shifted right) LDA P+1 \ Set A = P+1 LSR K+2 \ Shift the high byte in K+2 to the right ROR A \ Shift the middle byte in A to the right and store in STA K+1 \ K+1 (so K+1 contains P+1 shifted right) LDA P \ Shift the middle byte in P to the right and store in ROR A \ K, so K(2 1 0) now contains (|A| P+1 P) shifted right STA K \ We now use the same shift-and-add algorithm as MULT1 \ to calculate the following: \ \ K(2 1 0) = K(2 1 0) * |Q| LDA #0 \ Set A = 0 so we can start building the answer in A LDX #24 \ Set up a counter in X to count the 24 bits in K(2 1 0) .MUL2 BCC P%+4 \ If C (i.e. the next bit from K) is set, do the ADC T \ addition for this bit of K: \ \ A = A + T + C \ = A + |Q| - 1 + 1 \ = A + |Q| ROR A \ Shift A right by one place to catch the next digit ROR K+2 \ next digit of our result in the left end of K(2 1 0), ROR K+1 \ while also shifting K(2 1 0) right to fetch the next ROR K \ bit for the calculation into the C flag \ \ On the last iteration of this loop, the bit falling \ off the end of K will be bit 0 of the original A, as \ we did one shift before the loop and we are doing 24 \ iterations. We set A to 0 before looping, so this \ means the loop exits with the C flag clear DEX \ Decrement the loop counter BNE MUL2 \ Loop back for the next bit until K(2 1 0) has been \ rotated all the way \ The result (|A| P+1 P) * |Q| is now in (A K+2 K+1 K), \ but it is positive and doesn't have the correct sign \ of the final result yet STA T \ Save the high byte of the result into T LDA R \ Fetch the sign byte from the original (A P+1 P) \ argument that we stored in R EOR Q \ EOR with Q so the sign bit is the same as that of \ (A P+1 P) * Q AND #%10000000 \ Extract the sign bit ORA T \ Apply this to the high byte of the result in T, so \ that A now has the correct sign for the result, and \ (A K+2 K+1 K) therefore contains the correctly signed \ result STA K+3 \ Store A in K+3, so K(3 2 1 0) now contains the result RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MLS2 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (S R) = XX(1 0) and (A P) = A * ALP1 \ \ ------------------------------------------------------------------------------ \ \ Calculate the following: \ \ (S R) = XX(1 0) \ \ (A P) = A * ALP1 \ \ where ALP1 is the magnitude of the current roll angle alpha, in the range \ 0-31. \ \ ****************************************************************************** .MLS2 LDX XX \ Set (S R) = XX(1 0), starting with the low bytes STX R LDX XX+1 \ And then doing the high bytes STX S \ Fall through into MLS1 to calculate (A P) = A * ALP1 \ ****************************************************************************** \ \ Name: MLS1 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (A P) = ALP1 * A \ \ ------------------------------------------------------------------------------ \ \ Calculate the following: \ \ (A P) = ALP1 * A \ \ where ALP1 is the magnitude of the current roll angle alpha, in the range \ 0-31. \ \ This routine uses an unrolled version of MU11. MU11 calculates P * X, so we \ use the same algorithm but with P set to ALP1 and X set to A. The unrolled \ version here can skip the bit tests for bits 5-7 of P as we know P < 32, so \ only 5 shifts with bit tests are needed (for bits 0-4), while the other 3 \ shifts can be done without a test (for bits 5-7). \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ MULTS-2 Calculate (A P) = X * A \ \ ****************************************************************************** .MLS1 LDX ALP1 \ Set P to the roll angle alpha magnitude in ALP1 STX P \ (0-31), so now we calculate P * A .MULTS TAX \ Set X = A, so now we can calculate P * X instead of \ P * A to get our result, and we can use the algorithm \ from MU11 to do that, just unrolled (as MU11 returns \ P * X) AND #%10000000 \ Set T to the sign bit of A STA T TXA \ Set A = |A| AND #127 BEQ MU6 \ If A = 0, jump to MU6 to set P(1 0) = 0 and return \ from the subroutine using a tail call TAX \ Set T1 = X - 1 DEX \ STX T1 \ We subtract 1 as the C flag will be set when we want \ to do an addition in the loop below LDA #0 \ Set A = 0 so we can start building the answer in A LSR P \ Set P = P >> 1 \ and C flag = bit 0 of P \ We are now going to work our way through the bits of \ P, and do a shift-add for any bits that are set, \ keeping the running total in A, but instead of using a \ loop like MU11, we just unroll it, starting with bit 0 BCC P%+4 \ If C (i.e. the next bit from P) is set, do the ADC T1 \ addition for this bit of P: \ \ A = A + T1 + C \ = A + X - 1 + 1 \ = A + X ROR A \ Shift A right to catch the next digit of our result, \ which the next ROR sticks into the left end of P while \ also extracting the next bit of P ROR P \ Add the overspill from shifting A to the right onto \ the start of P, and shift P right to fetch the next \ bit for the calculation into the C flag BCC P%+4 \ Repeat the shift-and-add loop for bit 1 ADC T1 ROR A ROR P BCC P%+4 \ Repeat the shift-and-add loop for bit 2 ADC T1 ROR A ROR P BCC P%+4 \ Repeat the shift-and-add loop for bit 3 ADC T1 ROR A ROR P BCC P%+4 \ Repeat the shift-and-add loop for bit 4 ADC T1 ROR A ROR P LSR A \ Just do the "shift" part for bit 5 ROR P LSR A \ Just do the "shift" part for bit 6 ROR P LSR A \ Just do the "shift" part for bit 7 ROR P ORA T \ Give A the sign bit of the original argument A that \ we put into T above RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MU6 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Set P(1 0) = (A A) \ \ ------------------------------------------------------------------------------ \ \ In practice this is only called via a BEQ following an AND instruction, in \ which case A = 0, so this routine effectively does this: \ \ P(1 0) = 0 \ \ ****************************************************************************** .MU6 STA P+1 \ Set P(1 0) = (A A) STA P RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: SQUA \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Clear bit 7 of A and calculate (A P) = A * A \ \ ------------------------------------------------------------------------------ \ \ Do the following multiplication of unsigned 8-bit numbers, after first \ clearing bit 7 of A: \ \ (A P) = A * A \ \ ****************************************************************************** .SQUA AND #%01111111 \ Clear bit 7 of A and fall through into SQUA2 to set \ (A P) = A * A \ ****************************************************************************** \ \ Name: SQUA2 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (A P) = A * A \ \ ------------------------------------------------------------------------------ \ \ Do the following multiplication of unsigned 8-bit numbers: \ \ (A P) = A * A \ \ ****************************************************************************** .SQUA2 STA P \ Copy A into P and X TAX BNE MU11 \ If X = 0 fall through into MU1 to return a 0, \ otherwise jump to MU11 to return P * X \ ****************************************************************************** \ \ Name: MU1 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Copy X into P and A, and clear the C flag \ \ ------------------------------------------------------------------------------ \ \ Used to return a 0 result quickly from MULTU below. \ \ ****************************************************************************** .MU1 CLC \ Clear the C flag STX P \ Copy X into P and A TXA RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MLU1 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate Y1 = y_hi and (A P) = |y_hi| * Q for Y-th stardust \ \ ------------------------------------------------------------------------------ \ \ Do the following assignment, and multiply the Y-th stardust particle's \ y-coordinate with an unsigned number Q: \ \ Y1 = y_hi \ \ (A P) = |y_hi| * Q \ \ ****************************************************************************** .MLU1 LDA SY,Y \ Set Y1 the Y-th byte of SY STA Y1 \ Fall through into MLU2 to calculate: \ \ (A P) = |A| * Q \ ****************************************************************************** \ \ Name: MLU2 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (A P) = |A| * Q \ \ ------------------------------------------------------------------------------ \ \ Do the following multiplication of a sign-magnitude 8-bit number P with an \ unsigned number Q: \ \ (A P) = |A| * Q \ \ ****************************************************************************** .MLU2 AND #%01111111 \ Clear the sign bit in P, so P = |A| STA P \ Fall through into MULTU to calculate: \ \ (A P) = P * Q \ = |A| * Q \ ****************************************************************************** \ \ Name: MULTU \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (A P) = P * Q \ \ ------------------------------------------------------------------------------ \ \ Do the following multiplication of unsigned 8-bit numbers: \ \ (A P) = P * Q \ \ ****************************************************************************** .MULTU LDX Q \ Set X = Q BEQ MU1 \ If X = Q = 0, jump to MU1 to copy X into P and A, \ clear the C flag and return from the subroutine using \ a tail call \ Otherwise fall through into MU11 to set (A P) = P * X \ ****************************************************************************** \ \ Name: MU11 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (A P) = P * X \ Deep dive: Shift-and-add multiplication \ \ ------------------------------------------------------------------------------ \ \ Do the following multiplication of two unsigned 8-bit numbers: \ \ (A P) = P * X \ \ This uses the same shift-and-add approach as MULT1, but it's simpler as we \ are dealing with unsigned numbers in P and X. \ \ ****************************************************************************** .MU11 DEX \ Set T = X - 1 STX T \ \ We subtract 1 as the C flag will be set when we want \ to do an addition in the loop below LDA #0 \ Set A = 0 so we can start building the answer in A \LDX #8 \ This instruction is commented out in the original \ source TAX \ Copy A into X. There is a comment in the original \ source here that says "just in case", which refers to \ the MU11 routine in the BBC Micro cassette and disc \ versions, which set X to 0 (as they use X as a loop \ counter) \ \ The version here doesn't use a loop, but this \ instruction makes sure the unrolled version returns \ the same results as the loop versions, just in case \ something out there relies on MU11 returning X = 0 LSR P \ Set P = P >> 1 \ and C flag = bit 0 of P \ We now repeat the following four instruction block \ eight times, one for each bit in P. In the BBC Micro \ cassette and disc versions of Elite the following is \ done with a loop, but it is marginally faster to \ unroll the loop and have eight copies of the code, \ though it does take up a bit more memory (though that \ isn't a big concern when you have a 6502 Second \ Processor) BCC P%+4 \ If C (i.e. bit 0 of P) is set, do the ADC T \ addition for this bit of P: \ \ A = A + T + C \ = A + X - 1 + 1 \ = A + X ROR A \ Shift A right to catch the next digit of our result, \ which the next ROR sticks into the left end of P while \ also extracting the next bit of P ROR P \ Add the overspill from shifting A to the right onto \ the start of P, and shift P right to fetch the next \ bit for the calculation into the C flag BCC P%+4 \ Repeat for the second time ADC T ROR A ROR P BCC P%+4 \ Repeat for the third time ADC T ROR A ROR P BCC P%+4 \ Repeat for the fourth time ADC T ROR A ROR P BCC P%+4 \ Repeat for the fifth time ADC T ROR A ROR P BCC P%+4 \ Repeat for the sixth time ADC T ROR A ROR P BCC P%+4 \ Repeat for the seventh time ADC T ROR A ROR P BCC P%+4 \ Repeat for the eighth time ADC T ROR A ROR P RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: FMLTU2 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate A = K * sin(A) \ Deep dive: The sine, cosine and arctan tables \ \ ------------------------------------------------------------------------------ \ \ Calculate the following: \ \ A = K * sin(A) \ \ Because this routine uses the sine lookup table SNE, we can also call this \ routine to calculate cosine multiplication. To calculate the following: \ \ A = K * cos(B) \ \ call this routine with B + 16 in the accumulator, as sin(B + 16) = cos(B). \ \ ****************************************************************************** .FMLTU2 AND #%00011111 \ Restrict A to bits 0-5 (so it's in the range 0-31) TAX \ Set Q = sin(A) * 256 LDA SNE,X STA Q LDA K \ Set A to the radius in K \ Fall through into FMLTU to do the following: \ \ (A ?) = A * Q \ = K * sin(A) * 256 \ \ which is equivalent to: \ \ A = K * sin(A) \ ****************************************************************************** \ \ Name: FMLTU \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate A = A * Q / 256 \ Deep dive: Multiplication and division using logarithms \ \ ------------------------------------------------------------------------------ \ \ Do the following multiplication of two unsigned 8-bit numbers, returning only \ the high byte of the result: \ \ (A ?) = A * Q \ \ or, to put it another way: \ \ A = A * Q / 256 \ \ The advanced versions of Elite use logarithms to speed up the multiplication \ process. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag The C flag is clear if A = 0, or set if we return a \ result from one of the log tables \ \ ****************************************************************************** .FMLTU STX P \ Store X in P so we can preserve it through the call to \ FMLTU STA widget \ Store A in widget, so now widget = argument A TAX \ Transfer A into X, so now X = argument A BEQ MU3 \ If A = 0, jump to MU3 to return a result of 0, as \ 0 * Q / 256 is always 0 \ We now want to calculate La + Lq, first adding the low \ bytes (from the logL table), and then the high bytes \ (from the log table) LDA logL,X \ Set A = low byte of La \ = low byte of La (as we set X to A above) LDX Q \ Set X = Q BEQ MU3again \ If X = 0, jump to MU3again to return a result of 0, as \ A * 0 / 256 is always 0 CLC \ Set A = A + low byte of Lq ADC logL,X \ = low byte of La + low byte of Lq BMI oddlog \ If A > 127, jump to oddlog LDA log,X \ Set A = high byte of Lq LDX widget \ Set A = A + C + high byte of La ADC log,X \ = high byte of Lq + high byte of La + C \ \ so we now have: \ \ A = high byte of (La + Lq) BCC MU3again \ If the addition fitted into one byte and didn't carry, \ then La + Lq < 256, so we jump to MU3again to return a \ result of 0 and the C flag clear \ If we get here then the C flag is set, ready for when \ we return from the subroutine below TAX \ Otherwise La + Lq >= 256, so we return the A-th entry LDA antilog,X \ from the antilog table LDX P \ Restore X from P so it is preserved RTS \ Return from the subroutine .oddlog LDA log,X \ Set A = high byte of Lq LDX widget \ Set A = A + C + high byte of La ADC log,X \ = high byte of Lq + high byte of La + C \ \ so we now have: \ \ A = high byte of (La + Lq) BCC MU3again \ If the addition fitted into one byte and didn't carry, \ then La + Lq < 256, so we jump to MU3again to return a \ result of 0 and the C flag clear \ If we get here then the C flag is set, ready for when \ we return from the subroutine below TAX \ Otherwise La + Lq >= 256, so we return the A-th entry LDA antilogODD,X \ from the antilogODD table .MU3 \ If we get here then A (our result) is already 0 LDX P \ Restore X from P so it is preserved RTS \ Return from the subroutine .MU3again LDA #0 \ Set A = 0 LDX P \ Restore X from P so it is preserved RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MLTU2 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (A P+1 P) = (A ~P) * Q \ Deep dive: Shift-and-add multiplication \ \ ------------------------------------------------------------------------------ \ \ Do the following multiplication of an unsigned 16-bit number and an unsigned \ 8-bit number: \ \ (A P+1 P) = (A ~P) * Q \ \ where ~P means P EOR %11111111 (i.e. P with all its bits flipped). In other \ words, if you wanted to calculate &1234 * &56, you would: \ \ * Set A to &12 \ * Set P to &34 EOR %11111111 = &CB \ * Set Q to &56 \ \ before calling MLTU2. \ \ This routine is like a mash-up of MU11 and FMLTU. It uses part of FMLTU's \ inverted argument trick to work out whether or not to do an addition, and like \ MU11 it sets up a counter in X to extract bits from (P+1 P). But this time we \ extract 16 bits from (P+1 P), so the result is a 24-bit number. The core of \ the algorithm is still the shift-and-add approach explained in MULT1, just \ with more bits. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ Q Q is preserved \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ MLTU2-2 Set Q to X, so this calculates (A P+1 P) = (A ~P) * X \ \ ****************************************************************************** STX Q \ Store X in Q .MLTU2 EOR #%11111111 \ Flip the bits in A and rotate right, storing the LSR A \ result in P+1, so we now calculate (P+1 P) * Q STA P+1 LDA #0 \ Set A = 0 so we can start building the answer in A LDX #16 \ Set up a counter in X to count the 16 bits in (P+1 P) ROR P \ Set P = P >> 1 with bit 7 = bit 0 of A \ and C flag = bit 0 of P .MUL7 BCS MU21 \ If C (i.e. the next bit from P) is set, do not do the \ addition for this bit of P, and instead skip to MU21 \ to just do the shifts ADC Q \ Do the addition for this bit of P: \ \ A = A + Q + C \ = A + Q ROR A \ Rotate (A P+1 P) to the right, so we capture the next ROR P+1 \ digit of the result in P+1, and extract the next digit ROR P \ of (P+1 P) in the C flag DEX \ Decrement the loop counter BNE MUL7 \ Loop back for the next bit until P has been rotated \ all the way RTS \ Return from the subroutine .MU21 LSR A \ Shift (A P+1 P) to the right, so we capture the next ROR P+1 \ digit of the result in P+1, and extract the next digit ROR P \ of (P+1 P) in the C flag DEX \ Decrement the loop counter BNE MUL7 \ Loop back for the next bit until P has been rotated \ all the way RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MUT3 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: An unused routine that does the same as MUT2 \ \ ------------------------------------------------------------------------------ \ \ This routine is never actually called, but it is identical to MUT2, as the \ extra instructions have no effect. \ \ ****************************************************************************** .MUT3 LDX ALP1 \ Set P = ALP1, though this gets overwritten by the STX P \ following, so this has no effect \ Fall through into MUT2 to do the following: \ \ (S R) = XX(1 0) \ (A P) = Q * A \ ****************************************************************************** \ \ Name: MUT2 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (S R) = XX(1 0) and (A P) = Q * A \ \ ------------------------------------------------------------------------------ \ \ Do the following assignment, and multiplication of two signed 8-bit numbers: \ \ (S R) = XX(1 0) \ (A P) = Q * A \ \ ****************************************************************************** .MUT2 LDX XX+1 \ Set S = XX+1 STX S \ Fall through into MUT1 to do the following: \ \ R = XX \ (A P) = Q * A \ ****************************************************************************** \ \ Name: MUT1 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate R = XX and (A P) = Q * A \ \ ------------------------------------------------------------------------------ \ \ Do the following assignment, and multiplication of two signed 8-bit numbers: \ \ R = XX \ (A P) = Q * A \ \ ****************************************************************************** .MUT1 LDX XX \ Set R = XX STX R \ Fall through into MULT1 to do the following: \ \ (A P) = Q * A \ ****************************************************************************** \ \ Name: MULT1 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (A P) = Q * A \ Deep dive: Shift-and-add multiplication \ \ ------------------------------------------------------------------------------ \ \ Do the following multiplication of two 8-bit sign-magnitude numbers: \ \ (A P) = Q * A \ \ ****************************************************************************** .MULT1 TAX \ Store A in X AND #%01111111 \ Set P = |A| >> 1 LSR A \ and C flag = bit 0 of A STA P TXA \ Restore argument A EOR Q \ Set bit 7 of A and T if Q and A have different signs, AND #%10000000 \ clear bit 7 if they have the same signs, 0 all other STA T \ bits, i.e. T contains the sign bit of Q * A LDA Q \ Set A = |Q| AND #%01111111 BEQ mu10 \ If |Q| = 0 jump to mu10 (with A set to 0) TAX \ Set T1 = |Q| - 1 DEX \ STX T1 \ We subtract 1 as the C flag will be set when we want \ to do an addition in the loop below \ We are now going to work our way through the bits of \ P, and do a shift-add for any bits that are set, \ keeping the running total in A. We already set up \ the first shift at the start of this routine, as \ P = |A| >> 1 and C = bit 0 of A, so we now need to set \ up a loop to sift through the other 7 bits in P LDA #0 \ Set A = 0 so we can start building the answer in A TAX \ Copy A into X. There is a comment in the original \ source here that says "just in case", which refers to \ the MULT1 routine in the BBC Micro cassette and disc \ versions, which set X to 0 (as they use X as a loop \ counter) \ \ The version here doesn't use a loop, but this \ instruction makes sure the unrolled version returns \ the same results as the loop versions, just in case \ something out there relies on MULT1 returning X = 0 \.MUL4 \ These instructions are commented out in the original \ \ source. They contain the original loop version of the \BCC P%+4 \ code that's used in the BBC Micro cassette and disc \ADC T1 \ versions \ \ROR A \ROR P \ \DEX \ \BNE MUL4 \ \LSR A \ROR P \ \ORA T \ \RTS \ \.mu10 \ \STA P \ \RTS \ We now repeat the following four instruction block \ seven times, one for each remaining bit in P. In the \ BBC Micro cassette and disc versions of Elite the \ following is done with a loop, but it is marginally \ faster to unroll the loop and have seven copies of \ the code, though it does take up a bit more memory BCC P%+4 \ If C (i.e. the next bit from P) is set, do the ADC T1 \ addition for this bit of P: \ \ A = A + T1 + C \ = A + |Q| - 1 + 1 \ = A + |Q| ROR A \ As mentioned above, this ROR shifts A right and \ catches bit 0 in C - giving another digit for our \ result - and the next ROR sticks that bit into the \ left end of P while also extracting the next bit of P \ for the next addition ROR P \ Add the overspill from shifting A to the right onto \ the start of P, and shift P right to fetch the next \ bit for the calculation BCC P%+4 \ Repeat for the second time ADC T1 ROR A ROR P BCC P%+4 \ Repeat for the third time ADC T1 ROR A ROR P BCC P%+4 \ Repeat for the fourth time ADC T1 ROR A ROR P BCC P%+4 \ Repeat for the fifth time ADC T1 ROR A ROR P BCC P%+4 \ Repeat for the sixth time ADC T1 ROR A ROR P BCC P%+4 \ Repeat for the seventh time ADC T1 ROR A ROR P LSR A \ Rotate (A P) once more to get the final result, as ROR P \ we only pushed 7 bits through the above process ORA T \ Set the sign bit of the result that we stored in T RTS \ Return from the subroutine .mu10 STA P \ If we get here, the result is 0 and A = 0, so set \ P = 0 so (A P) = 0 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MULT12 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (S R) = Q * A \ \ ------------------------------------------------------------------------------ \ \ Calculate: \ \ (S R) = Q * A \ \ ****************************************************************************** .MULT12 JSR MULT1 \ Set (A P) = Q * A STA S \ Set (S R) = (A P) LDA P \ = Q * A STA R RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TAS3 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Calculate the dot product of XX15 and an orientation vector \ \ ------------------------------------------------------------------------------ \ \ Calculate the dot product of the vector in XX15 and one of the orientation \ vectors, as determined by the value of Y. If vect is the orientation vector, \ we calculate this: \ \ (A X) = vect . XX15 \ = vect_x * XX15 + vect_y * XX15+1 + vect_z * XX15+2 \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ Y The 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 \ \ ****************************************************************************** .TAS3 LDX INWK,Y \ Set Q = the Y-th byte of INWK, i.e. vect_x STX Q LDA XX15 \ Set A = XX15 JSR MULT12 \ Set (S R) = Q * A \ = vect_x * XX15 LDX INWK+2,Y \ Set Q = the Y+2-th byte of INWK, 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 INWK+4,Y \ Set Q = the Y+2-th byte of INWK, i.e. vect_z STX Q LDA XX15+2 \ Set A = XX15+2 \ Fall through into MAD to set: \ \ (A X) = Q * A + (S R) \ = vect_z * XX15+2 + vect_y * XX15+1 + \ vect_x * XX15 \ ****************************************************************************** \ \ Name: MAD \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (A X) = Q * A + (S R) \ \ ------------------------------------------------------------------------------ \ \ Calculate \ \ (A X) = Q * A + (S R) \ \ ****************************************************************************** .MAD JSR MULT1 \ Call MULT1 to set (A P) = Q * A \ Fall through into ADD to do: \ \ (A X) = (A P) + (S R) \ = Q * A + (S R) \ ****************************************************************************** \ \ Name: ADD \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (A X) = (A P) + (S R) \ Deep dive: Adding sign-magnitude numbers \ \ ------------------------------------------------------------------------------ \ \ Add two 16-bit sign-magnitude numbers together, calculating: \ \ (A X) = (A P) + (S R) \ \ ****************************************************************************** .ADD STA T1 \ Store argument A in T1 AND #%10000000 \ Extract the sign (bit 7) of A and store it in T STA T EOR S \ EOR bit 7 of A with S. If they have different bit 7s BMI MU8 \ (i.e. they have different signs) then bit 7 in the \ EOR result will be 1, which means the EOR result is \ negative. So the AND, EOR and BMI together mean "jump \ to MU8 if A and S have different signs" \ If we reach here, then A and S have the same sign, so \ we can add them and set the sign to get the result LDA R \ Add the least significant bytes together into X: CLC \ ADC P \ X = P + R TAX LDA S \ Add the most significant bytes together into A. We ADC T1 \ stored the original argument A in T1 earlier, so we \ can do this with: \ \ A = A + S + C \ = T1 + S + C ORA T \ If argument A was negative (and therefore S was also \ negative) then make sure result A is negative by \ OR'ing the result with the sign bit from argument A \ (which we stored in T) RTS \ Return from the subroutine .MU8 \ If we reach here, then A and S have different signs, \ so we can subtract their absolute values and set the \ sign to get the result LDA S \ Clear the sign (bit 7) in S and store the result in AND #%01111111 \ U, so U now contains |S| STA U LDA P \ Subtract the least significant bytes into X: SEC \ SBC R \ X = P - R TAX LDA T1 \ Restore the A of the argument (A P) from T1 and AND #%01111111 \ clear the sign (bit 7), so A now contains |A| SBC U \ Set A = |A| - |S| \ At this point we have |A P| - |S R| in (A X), so we \ need to check whether the subtraction above was the \ right way round (i.e. that we subtracted the smaller \ absolute value from the larger absolute value) BCS MU9 \ If |A| >= |S|, our subtraction was the right way \ round, so jump to MU9 to set the sign \ If we get here, then |A| < |S|, so our subtraction \ above was the wrong way round (we actually subtracted \ the larger absolute value from the smaller absolute \ value). So let's subtract the result we have in (A X) \ from zero, so that the subtraction is the right way \ round STA U \ Store A in U TXA \ Set X = 0 - X using two's complement (to negate a EOR #&FF \ number in two's complement, you can invert the bits ADC #1 \ and add one - and we know the C flag is clear as we TAX \ didn't take the BCS branch above, so the ADC will do \ the correct addition) LDA #0 \ Set A = 0 - A, which we can do this time using a SBC U \ subtraction with the C flag clear ORA #%10000000 \ We now set the sign bit of A, so that the EOR on the \ next line will give the result the opposite sign to \ argument A (as T contains the sign bit of argument \ A). This is the same as giving the result the same \ sign as argument S (as A and S have different signs), \ which is what we want, as S has the larger absolute \ value .MU9 EOR T \ If we get here from the BCS above, then |A| >= |S|, \ so we want to give the result the same sign as \ argument A, so if argument A was negative, we flip \ the sign of the result with an EOR (to make it \ negative) RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TIS1 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (A ?) = (-X * A + (S R)) / 96 \ Deep dive: Shift-and-subtract division \ \ ------------------------------------------------------------------------------ \ \ Calculate the following expression between sign-magnitude numbers, ignoring \ the low byte of the result: \ \ (A ?) = (-X * A + (S R)) / 96 \ \ This uses the same shift-and-subtract algorithm as TIS2, just with the \ quotient A hard-coded to 96. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ Q Gets set to the value of argument X \ \ ****************************************************************************** .TIS1 STX Q \ Set Q = X EOR #%10000000 \ Flip the sign bit in A JSR MAD \ Set (A X) = Q * A + (S R) \ = X * -A + (S R) .DVID96 TAX \ Set T to the sign bit of the result AND #%10000000 STA T TXA \ Set A to the high byte of the result with the sign bit AND #%01111111 \ cleared, so (A ?) = |X * A + (S R)| \ The following is identical to TIS2, except Q is \ hard-coded to 96, so this does A = A / 96 LDX #254 \ Set T1 to have bits 1-7 set, so we can rotate through STX T1 \ 7 loop iterations, getting a 1 each time, and then \ getting a 0 on the 8th iteration... and we can also \ use T1 to catch our result bits into bit 0 each time .DVL3 ASL A \ Shift A to the left CMP #96 \ If A < 96 skip the following subtraction BCC DV4 SBC #96 \ Set A = A - 96 \ \ Going into this subtraction we know the C flag is \ set as we passed through the BCC above, and we also \ know that A >= 96, so the C flag will still be set \ once we are done .DV4 ROL T1 \ Rotate the counter in T1 to the left, and catch the \ result bit into bit 0 (which will be a 0 if we didn't \ do the subtraction, or 1 if we did) BCS DVL3 \ If we still have set bits in T1, loop back to DVL3 to \ do the next iteration of 7 LDA T1 \ Fetch the result from T1 into A ORA T \ Give A the sign of the result that we stored above RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: DV42 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (P R) = 256 * DELTA / z_hi \ \ ------------------------------------------------------------------------------ \ \ Calculate the following division and remainder: \ \ P = DELTA / (the Y-th stardust particle's z_hi coordinate) \ \ R = remainder as a fraction of A, where 1.0 = 255 \ \ Another way of saying the above is this: \ \ (P R) = 256 * DELTA / z_hi \ \ DELTA is a value between 1 and 40, and the minimum z_hi is 16 (dust particles \ are removed at lower values than this), so this means P is between 0 and 2 \ (as 40 / 16 = 2.5, so the maximum result is P = 2 and R = 128. \ \ This uses the same shift-and-subtract algorithm as TIS2, but this time we \ keep the remainder. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ Y The number of the stardust particle to process \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag The C flag is cleared \ \ ****************************************************************************** .DV42 LDA SZ,Y \ Fetch the Y-th dust particle's z_hi coordinate into A \ Fall through into DV41 to do: \ \ (P R) = 256 * DELTA / A \ = 256 * DELTA / Y-th stardust particle's z_hi \ ****************************************************************************** \ \ Name: DV41 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (P R) = 256 * DELTA / A \ \ ------------------------------------------------------------------------------ \ \ Calculate the following division and remainder: \ \ P = DELTA / A \ \ R = remainder as a fraction of A, where 1.0 = 255 \ \ Another way of saying the above is this: \ \ (P R) = 256 * DELTA / A \ \ This uses the same shift-and-subtract algorithm as TIS2, but this time we \ keep the remainder. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag The C flag is cleared \ \ ****************************************************************************** .DV41 STA Q \ Store A in Q LDA DELTA \ Fetch the speed from DELTA into A \ Fall through into DVID4 to do: \ \ (P R) = 256 * A / Q \ = 256 * DELTA / A \ ****************************************************************************** \ \ Name: DVID4 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (P R) = 256 * A / Q \ Deep dive: Shift-and-subtract division \ \ ------------------------------------------------------------------------------ \ \ Calculate the following division and remainder: \ \ P = A / Q \ \ R = remainder as a fraction of Q, where 1.0 = 255 \ \ Another way of saying the above is this: \ \ (P R) = 256 * A / Q \ \ This uses the same shift-and-subtract algorithm as TIS2, but this time we \ keep the remainder and the loop is unrolled. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag The C flag is cleared \ \ ****************************************************************************** .DVID4 \LDX #8 \ This instruction is commented out in the original \ source ASL A \ Shift A left and store in P (we will build the result STA P \ in P) LDA #0 \ Set A = 0 for us to build a remainder \.DVL4 \ This label is commented out in the original source \ We now repeat the following five instruction block \ eight times, one for each bit in P. In the BBC Micro \ cassette and disc versions of Elite the following is \ done with a loop, but it is marginally faster to \ unroll the loop and have eight copies of the code, \ though it does take up a bit more memory (though that \ isn't a big concern when you have a 6502 Second \ Processor) ROL A \ Shift A to the left CMP Q \ If A < Q skip the following subtraction BCC P%+4 SBC Q \ A >= Q, so set A = A - Q ROL P \ Shift P to the left, pulling the C flag into bit 0 ROL A \ Repeat for the second time CMP Q BCC P%+4 SBC Q ROL P ROL A \ Repeat for the third time CMP Q BCC P%+4 SBC Q ROL P ROL A \ Repeat for the fourth time CMP Q BCC P%+4 SBC Q ROL P ROL A \ Repeat for the fifth time CMP Q BCC P%+4 SBC Q ROL P ROL A \ Repeat for the sixth time CMP Q BCC P%+4 SBC Q ROL P ROL A \ Repeat for the seventh time CMP Q BCC P%+4 SBC Q ROL P ROL A \ Repeat for the eighth time CMP Q BCC P%+4 SBC Q ROL P LDX #0 \ Set X = 0 so this unrolled version of DVID4 also \ returns X = 0 JMP LL28+4 \ Jump to LL28+4 to convert the remainder in A into an \ integer representation of the fractional value A / Q, \ in R, where 1.0 = 255. LL28+4 always returns with the \ C flag cleared, and we return from the subroutine \ using a tail call \ ****************************************************************************** \ \ Name: DVID3B2 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate K(3 2 1 0) = (A P+1 P) / (z_sign z_hi z_lo) \ Deep dive: Shift-and-subtract division \ \ ------------------------------------------------------------------------------ \ \ Calculate the following: \ \ K(3 2 1 0) = (A P+1 P) / (z_sign z_hi z_lo) \ \ The actual division here is done as an 8-bit calculation using LL31, but this \ routine shifts both the numerator (the top part of the division) and the \ denominator (the bottom part of the division) around to get the multi-byte \ result we want. \ \ Specifically, it shifts both of them to the left as far as possible, keeping a \ tally of how many shifts get done in each one - and specifically, the \ difference in the number of shifts between the top and bottom (as shifting \ both of them once in the same direction won't change the result). It then \ divides the two highest bytes with the simple 8-bit routine in LL31, and \ shifts the result by the difference in the number of shifts, which acts as a \ scale factor to get the correct result. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ K(3 2 1 0) The result of the division \ \ X X is preserved \ \ ****************************************************************************** .DVID3B2 STA P+2 \ Set P+2 = A LDA INWK+6 \ Set Q = z_lo, making sure Q is at least 1 ORA #1 STA Q LDA INWK+7 \ Set R = z_hi STA R LDA INWK+8 \ Set S = z_sign STA S .DVID3B \ Given the above assignments, we now want to calculate \ the following to get the result we want: \ \ K(3 2 1 0) = P(2 1 0) / (S R Q) LDA P \ Make sure P(2 1 0) is at least 1 ORA #1 STA P LDA P+2 \ Set T to the sign of P+2 * S (i.e. the sign of the EOR S \ result) and store it in T AND #%10000000 STA T LDY #0 \ Set Y = 0 to store the scale factor LDA P+2 \ Clear the sign bit of P+2, so the division can be done AND #%01111111 \ with positive numbers and we'll set the correct sign \ below, once all the maths is done \ \ This also leaves A = P+2, which we use below .DVL9 \ We now shift (A P+1 P) left until A >= 64, counting \ the number of shifts in Y. This makes the top part of \ the division as large as possible, thus retaining as \ much accuracy as we can. When we come to return the \ final result, we shift the result by the number of \ places in Y, and in the correct direction CMP #64 \ If A >= 64, jump down to DV14 BCS DV14 ASL P \ Shift (A P+1 P) to the left ROL P+1 ROL A INY \ Increment the scale factor in Y BNE DVL9 \ Loop up to DVL9 (this BNE is effectively a JMP, as Y \ will never be zero) .DV14 \ If we get here, A >= 64 and contains the highest byte \ of the numerator, scaled up by the number of left \ shifts in Y STA P+2 \ Store A in P+2, so we now have the scaled value of \ the numerator in P(2 1 0) LDA S \ Set A = |S| AND #%01111111 \BMI DV9 \ This label is commented out in the original source .DVL6 \ We now shift (S R Q) left until bit 7 of S is set, \ reducing Y by the number of shifts. This makes the \ bottom part of the division as large as possible, thus \ retaining as much accuracy as we can. When we come to \ return the final result, we shift the result by the \ total number of places in Y, and in the correct \ direction, to give us the correct result \ \ We set A to |S| above, so the following actually \ shifts (A R Q) DEY \ Decrement the scale factor in Y ASL Q \ Shift (A R Q) to the left ROL R ROL A BPL DVL6 \ Loop up to DVL6 to do another shift, until bit 7 of A \ is set and we can't shift left any further .DV9 \ We have now shifted both the numerator and denominator \ left as far as they will go, keeping a tally of the \ overall scale factor of the various shifts in Y. We \ can now divide just the two highest bytes to get our \ result STA Q \ Set Q = A, the highest byte of the denominator LDA #254 \ Set R to have bits 1-7 set, so we can pass this to STA R \ LL31 to act as the bit counter in the division LDA P+2 \ Set A to the highest byte of the numerator JSR LL31 \ Call LL31 to calculate: \ \ R = 256 * A / Q \ = 256 * numerator / denominator \ The result of our division is now in R, so we just \ need to shift it back by the scale factor in Y LDA #0 \ Set K(3 2 1) = 0 to hold the result (we populate K STA K+1 \ next) STA K+2 STA K+3 TYA \ If Y is positive, jump to DV12 BPL DV12 \ If we get here then Y is negative, so we need to shift \ the result R to the left by Y places, and then set the \ correct sign for the result LDA R \ Set A = R .DVL8 ASL A \ Shift (K+3 K+2 K+1 A) left ROL K+1 ROL K+2 ROL K+3 INY \ Increment the scale factor in Y BNE DVL8 \ Loop back to DVL8 until we have shifted left by Y \ places STA K \ Store A in K so the result is now in K(3 2 1 0) LDA K+3 \ Set K+3 to the sign in T, which we set above to the ORA T \ correct sign for the result STA K+3 RTS \ Return from the subroutine .DV13 \ If we get here then Y is zero, so we don't need to \ shift the result R, we just need to set the correct \ sign for the result LDA R \ Store R in K so the result is now in K(3 2 1 0) STA K LDA T \ Set K+3 to the sign in T, which we set above to the STA K+3 \ correct sign for the result RTS \ Return from the subroutine .DV12 BEQ DV13 \ We jumped here having set A to the scale factor in Y, \ so this jumps up to DV13 if Y = 0 \ If we get here then Y is positive and non-zero, so we \ need to shift the result R to the right by Y places \ and then set the correct sign for the result. We also \ know that K(3 2 1) will stay 0, as we are shifting the \ lowest byte to the right, so no set bits will make \ their way into the top three bytes LDA R \ Set A = R .DVL10 LSR A \ Shift A right DEY \ Decrement the scale factor in Y BNE DVL10 \ Loop back to DVL10 until we have shifted right by Y \ places STA K \ Store the shifted A in K so the result is now in \ K(3 2 1 0) LDA T \ Set K+3 to the sign in T, which we set above to the STA K+3 \ correct sign for the result RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: cntr \ Type: Subroutine \ Category: Dashboard \ Summary: Apply damping to the pitch or roll dashboard indicator \ \ ------------------------------------------------------------------------------ \ \ Apply damping to the value in X, where X ranges from 1 to 255 with 128 as the \ centre point (so X represents a position on a centre-based dashboard slider, \ such as pitch or roll). If the value is in the left-hand side of the slider \ (1-127) then it bumps the value up by 1 so it moves towards the centre, and \ if it's in the right-hand side, it reduces it by 1, also moving it towards the \ centre. \ \ ****************************************************************************** .cntr LDA auto \ If the docking computer is currently activated, jump BNE cnt2 \ to cnt2 to skip the following as we always want to \ enable damping for the docking computer LDA DAMP \ If DAMP is non-zero, then keyboard damping is not BNE RE1 \ enabled, so jump to RE1 to return from the subroutine .cnt2 TXA \ If X < 128, then it's in the left-hand side of the BPL BUMP \ dashboard slider, so jump to BUMP to bump it up by 1, \ to move it closer to the centre DEX \ Otherwise X >= 128, so it's in the right-hand side BMI RE1 \ of the dashboard slider, so decrement X by 1, and if \ it's still >= 128, jump to RE1 to return from the \ subroutine, otherwise fall through to BUMP to undo \ the bump and then return .BUMP INX \ Bump X up by 1, and if it hasn't overshot the end of BNE RE1 \ the dashboard slider, jump to RE1 to return from the \ subroutine, otherwise fall through to REDU to drop \ it down by 1 again .REDU DEX \ Reduce X by 1, and if we have reached 0 jump up to BEQ BUMP \ BUMP to add 1, because we need the value to be in the \ range 1 to 255 .RE1 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: BUMP2 \ Type: Subroutine \ Category: Dashboard \ Summary: Bump up the value of the pitch or roll dashboard indicator \ \ ------------------------------------------------------------------------------ \ \ Increase ("bump up") X by A, where X is either the current rate of pitch or \ the current rate of roll. \ \ The rate of pitch or roll ranges from 1 to 255 with 128 as the centre point. \ This is the amount by which the pitch or roll is currently changing, so 1 \ means it is decreasing at the maximum rate, 128 means it is not changing, \ and 255 means it is increasing at the maximum rate. These values correspond \ to the line on the DC or RL indicators on the dashboard, with 1 meaning full \ left, 128 meaning the middle, and 255 meaning full right. \ \ If bumping up X would push it past 255, then X is set to 255. \ \ If keyboard auto-recentre is configured and the result is less than 128, we \ bump X up to the mid-point, 128. This is the equivalent of having a roll or \ pitch in the left half of the indicator, when increasing the roll or pitch \ should jump us straight to the mid-point. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ RE2+2 Restore A from T and return from the subroutine \ \ ****************************************************************************** .BUMP2 STA T \ Store argument A in T so we can restore it later TXA \ Copy argument X into A CLC \ Clear the C flag so we can do addition without the \ C flag affecting the result ADC T \ Set X = A = argument X + argument A TAX BCC RE2 \ If the C flag is clear, then we didn't overflow, so \ jump to RE2 to auto-recentre and return the result LDX #255 \ We have an overflow, so set X to the maximum possible \ value of 255 .RE2 BPL djd1 \ If X has bit 7 clear (i.e. the result < 128), then \ jump to djd1 in routine REDU2 to do an auto-recentre, \ if configured, because the result is on the left side \ of the centre point of 128 \ Jumps to RE2+2 end up here LDA T \ Restore the original argument A from T into A RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: REDU2 \ Type: Subroutine \ Category: Dashboard \ Summary: Reduce the value of the pitch or roll dashboard indicator \ \ ------------------------------------------------------------------------------ \ \ Reduce X by A, where X is either the current rate of pitch or the current \ rate of roll. \ \ The rate of pitch or roll ranges from 1 to 255 with 128 as the centre point. \ This is the amount by which the pitch or roll is currently changing, so 1 \ means it is decreasing at the maximum rate, 128 means it is not changing, \ and 255 means it is increasing at the maximum rate. These values correspond \ to the line on the DC or RL indicators on the dashboard, with 1 meaning full \ left, 128 meaning the middle, and 255 meaning full right. \ \ If reducing X would bring it below 1, then X is set to 1. \ \ If keyboard auto-recentre is configured and the result is greater than 128, we \ reduce X down to the mid-point, 128. This is the equivalent of having a roll \ or pitch in the right half of the indicator, when decreasing the roll or pitch \ should jump us straight to the mid-point. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ djd1 Auto-recentre the value in X, if keyboard auto-recentre \ is configured \ \ ****************************************************************************** .REDU2 STA T \ Store argument A in T so we can restore it later TXA \ Copy argument X into A SEC \ Set the C flag so we can do subtraction without the \ C flag affecting the result SBC T \ Set X = A = argument X - argument A TAX BCS RE3 \ If the C flag is set, then we didn't underflow, so \ jump to RE3 to auto-recentre and return the result LDX #1 \ We have an underflow, so set X to the minimum possible \ value, 1 .RE3 BPL RE2+2 \ If X has bit 7 clear (i.e. the result < 128), then \ jump to RE2+2 above to return the result as is, \ because the result is on the left side of the centre \ point of 128, so we don't need to auto-centre .djd1 \ If we get here, then we need to apply auto-recentre, \ if it is configured LDA DJD \ If keyboard auto-recentre is disabled, then jump to BNE RE2+2 \ RE2+2 to restore A and return LDX #128 \ If we get here then keyboard auto-recentre is enabled, BMI RE2+2 \ so set X to 128 (the middle of our range) and jump to \ RE2+2 to restore A and return from the subroutine \ (this BMI is effectively a JMP as bit 7 of X is always \ set) \ ****************************************************************************** \ \ Name: ARCTAN \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Calculate A = arctan(P / Q) \ Deep dive: The sine, cosine and arctan tables \ \ ------------------------------------------------------------------------------ \ \ Calculate the following: \ \ A = arctan(P / Q) \ \ In other words, this finds the angle in the right-angled triangle where the \ opposite side to angle A is length P and the adjacent side to angle A has \ length Q, so: \ \ tan(A) = P / Q \ \ The result in A is an integer representing the angle in radians. The routine \ returns values in the range 0 to 128, which covers 0 to 180 degrees (or 0 to \ PI radians). \ \ ****************************************************************************** .ARCTAN LDA P \ Set T1 = P EOR Q, which will have the sign of P * Q EOR Q \ \AND #%10000000 \ The AND is commented out in the original source STA T1 LDA Q \ If Q = 0, jump to AR2 to return a right angle BEQ AR2 ASL A \ Set Q = |Q| * 2 (this is a quick way of clearing the STA Q \ sign bit, and we don't need to shift right again as we \ only ever use this value in the division with |P| * 2, \ which we set next) LDA P \ Set A = |P| * 2 ASL A CMP Q \ If A >= Q, i.e. |P| > |Q|, jump to AR1 to swap P BCS AR1 \ and Q around, so we can still use the lookup table JSR ARS1 \ Call ARS1 to set the following from the lookup table: \ \ A = arctan(A / Q) \ = arctan(|P / Q|) SEC \ Set the C flag so the SBC instruction in AR3 will be \ correct, should we jump there .AR4 LDX T1 \ If T1 is negative, i.e. P and Q have different signs, BMI AR3 \ jump down to AR3 to return arctan(-|P / Q|) RTS \ Otherwise P and Q have the same sign, so our result is \ correct and we can return from the subroutine .AR1 \ We want to calculate arctan(t) where |t| > 1, so we \ can use the calculation described in the documentation \ for the ACT table, i.e. 64 - arctan(1 / t) LDX Q \ Swap the values in Q and P, using the fact that we STA Q \ called AR1 with A = P STX P \ TXA \ This also sets A = P (which now contains the original \ argument |Q|) JSR ARS1 \ Call ARS1 to set the following from the lookup table: \ \ A = arctan(A / Q) \ = arctan(|Q / P|) \ = arctan(1 / |P / Q|) STA T \ Set T = 64 - T LDA #64 SBC T BCS AR4 \ Jump to AR4 to continue the calculation (this BCS is \ effectively a JMP as the subtraction will never \ underflow, as ARS1 returns values in the range 0-31) .AR2 \ If we get here then Q = 0, so tan(A) = infinity and \ A is a right angle, or 0.25 of a circle. We allocate \ 255 to a full circle, so we should return 63 for a \ right angle LDA #63 \ Set A to 63, to represent a right angle RTS \ Return from the subroutine .AR3 \ A contains arctan(|P / Q|) but P and Q have different \ signs, so we need to return arctan(-|P / Q|), using \ the calculation described in the documentation for the \ ACT table, i.e. 128 - A STA T \ Set A = 128 - A LDA #128 \ \SEC \ The SEC instruction is commented out in the original SBC T \ source, and isn't required as we did a SEC before \ calling AR3 RTS \ Return from the subroutine .ARS1 \ This routine fetches arctan(A / Q) from the ACT table, \ so A will be set to an integer in the range 0 to 31 \ that represents an angle from 0 to 45 degrees (or 0 to \ PI / 4 radians) JSR LL28 \ Call LL28 to calculate: \ \ R = 256 * A / Q LDA R \ Set X = R / 8 LSR A \ = 32 * A / Q LSR A \ LSR A \ so X has the value t * 32 where t = A / Q, which is TAX \ what we need to look up values in the ACT table LDA ACT,X \ Fetch ACT+X from the ACT table into A, so now: \ \ A = value in ACT + X \ = value in ACT + (32 * A / Q) \ = arctan(A / Q) RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LASLI \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw the laser lines for when we fire our lasers \ \ ------------------------------------------------------------------------------ \ \ 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: \ \ LASLI2 Just draw the current laser lines without moving the \ centre point, draining energy or heating up. This has \ the effect of removing the lines from the screen \ \ 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 \ --- Mod: Code removed for two-player Elite: ---------> \ADC #Y-4 \ Set LASY to four pixels above the centre of the \STA LASY \ screen (#Y), plus our random number, so the laser \ \ dances above and below the centre point \ --- And replaced by: --------------------------------> ADC #(Y/2)-4 \ Set LASY to four pixels above the centre of the STA LASY \ screen (#Y), plus our random number, so the laser \ dances above and below the centre point \ --- End of replacement ------------------------------> 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 8 to the laser temperature in GNTMP ADC #8 STA GNTMP JSR DENGY \ Call DENGY to deplete our energy banks by 1 .LASLI2 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 #RED \ Send a #SETCOL RED command to the I/O processor to JSR DOCOL \ switch to colour 2, which is red in the space view LDA #32 \ Set A = 32 and Y = 224 for the first set of laser LDY #224 \ lines (the wider pair of lines) IF _SNG45 DEC LASY \ Decrement the y-coordinate of the centre point to move \ it up the screen by a pixel for the top set of lines, \ so the wider set of lines aim slightly higher than the \ narrower set ENDIF JSR las \ Call las below to draw the first set of laser lines IF _SNG45 INC LASY \ Increment the y-coordinate of the centre point to put \ it back to the original position ENDIF 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 \ --- Mod: Code removed for two-player Elite: ---------> \LDA #2*Y-1 \ Set Y2 = 2 * #Y - 1. The constant #Y is 96, the \STA Y2 \ y-coordinate of the mid-point of the space view, so \ \ this sets Y2 to 191, the y-coordinate of the bottom \ \ pixel row of the space view \ --- And replaced by: --------------------------------> LDA #Y-1 \ Set Y2 = 2 * #Y - 1. The constant #Y is 96, the STA Y2 \ y-coordinate of the mid-point of the space view, so \ this sets Y2 to 191, the y-coordinate of the bottom \ pixel row of the space view \ --- End of replacement ------------------------------> JSR LL30 \ 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 \ --- Mod: Code removed for two-player Elite: ---------> \LDA #2*Y-1 \ Set Y2 = 2 * #Y - 1, the y-coordinate of the bottom \STA Y2 \ pixel row of the space view (as before) \ --- And replaced by: --------------------------------> LDA #Y-1 \ Set Y2 = 2 * #Y - 1, the y-coordinate of the bottom STA Y2 \ pixel row of the space view (as before) \ --- End of replacement ------------------------------> JMP LL30 \ 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: Player2LASLI \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw the laser lines for when we fire our lasers \ \ ------------------------------------------------------------------------------ \ \ 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: \ \ Player2LASLI2 Just draw the current laser lines without moving the \ centre point, draining energy or heating up. This has \ the effect of removing the lines from the screen \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Player2LASLI JSR DORND \ Set A and X to random numbers AND #7 \ Restrict A to a random value in the range 0 to 7 ADC #Y+(Y/2)-4 \ Set LASY to four pixels above the centre of the STA player2LASY \ screen, plus our random number, 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 player2LASX \ screen (#X), plus our random number, so the laser \ dances to the left and right of the centre point LDA player2GNTMP \ Add 8 to the laser temperature in GNTMP ADC #8 STA player2GNTMP JSR Player2DENGY \ Call DENGY to deplete player 2's energy banks by 1 .Player2LASLI2 LDA QQ11 \ If this is not a space view (i.e. QQ11 is non-zero) BNE lasi2 \ then jump to MA9 to return from the main flight loop \ (as lasi2 is an RTS) LDA #RED \ Send a #SETCOL RED command to the I/O processor to JSR DOCOL \ switch to colour 2, which is red in the space view LDA #32 \ Set A = 32 and Y = 224 for the first set of laser LDY #224 \ lines (the wider pair of lines) IF _SNG45 DEC player2LASY \ Decrement the y-coordinate of the centre point to move \ it up the screen by a pixel for the top set of lines, \ so the wider set of lines aim slightly higher than the \ narrower set ENDIF JSR lasi1 \ Call lasi1 below to draw the first set of laser lines IF _SNG45 INC player2LASY \ Increment the y-coordinate of the centre point to put \ it back to the original position ENDIF LDA #48 \ Fall through into lasi1 with A = 48 and Y = 208 to LDY #208 \ draw 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 .lasi1 STA X2 \ Set X2 = A LDA player2LASX \ Set (X1, Y1) to the random centre point we set above STA X1 LDA player2LASY STA Y1 LDA #2*Y-1 \ Set Y2 = 2 * #Y - 1. The constant #Y is 96, the STA Y2 \ y-coordinate of the mid-point of the space view, so \ this sets Y2 to 191, the y-coordinate of the bottom \ pixel row of the space view JSR LL30 \ Draw a line from (X1, Y1) to (X2, Y2), so that's from \ the centre point to (A, 191) LDA player2LASX \ Set (X1, Y1) to the random centre point we set above STA X1 LDA player2LASY STA Y1 STY X2 \ Set X2 = Y LDA #2*Y-1 \ Set Y2 = 2 * #Y - 1, the y-coordinate of the bottom STA Y2 \ pixel row of the space view (as before) JMP LL30 \ 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 .lasi2 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: PDESC \ Type: Subroutine \ Category: Universe \ Summary: Print the system's extended description or a mission 1 directive \ Deep dive: Extended system descriptions \ Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ This prints a specific system's extended description. This is called the "pink \ volcanoes string" in a comment in the original source, and the "goat soup" \ recipe by Ian Bell on his website (where he also refers to the species string \ as the "pink felines" string). \ \ For some special systems, when you are docked at them, the procedurally \ generated extended description is overridden and a text token from the RUTOK \ table is shown instead. If mission 1 is in progress, then a number of systems \ along the route of that mission's story will show custom mission-related \ directives in place of that system's normal "goat soup" phrase. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ ZZ The system number (0-255) \ \ ****************************************************************************** .PDESC LDA QQ8 \ If either byte in QQ18(1 0) is non-zero, meaning that ORA QQ8+1 \ the distance from the current system to the selected BNE PD1 \ is non-zero, jump to PD1 to show the standard "goat \ soup" description LDA QQ12 \ If QQ12 does not have bit 7 set, which means we are BPL PD1 \ not docked, jump to PD1 to show the standard "goat \ soup" description \ If we get here, then the current system is the same as \ the selected system and we are docked, so now to check \ whether there is a special override token for this \ system LDY #NRU% \ Set Y as a loop counter as we work our way through the \ system numbers in RUPLA, starting at NRU% (which is \ the number of entries in RUPLA, 26) and working our \ way down to 1 .PDL1 LDA RUPLA-1,Y \ Fetch the Y-th byte from RUPLA-1 into A (we use \ RUPLA-1 because Y is looping from 26 to 1) CMP ZZ \ If A doesn't match the system whose description we BNE PD2 \ are printing (in ZZ), jump to PD2 to keep looping \ through the system numbers in RUPLA \ If we get here we have found a match for this system \ number in RUPLA LDA RUGAL-1,Y \ Fetch the Y-th byte from RUGAL-1 into A AND #%01111111 \ Extract bits 0-6 of A CMP GCNT \ If the result does not equal the current galaxy BNE PD2 \ number, jump to PD2 to keep looping through the system \ numbers in RUPLA LDA RUGAL-1,Y \ Fetch the Y-th byte from RUGAL-1 into A, once again BMI PD3 \ If bit 7 is set, jump to PD3 to print the extended \ token in A from the second table in RUTOK LDA TP \ Fetch bit 0 of TP into the C flag, and skip to PD1 if LSR A \ it is clear (i.e. if mission 1 is not in progress) to BCC PD1 \ print the "goat soup" extended description \ If we get here then mission 1 is in progress, so we \ print out the corresponding token from RUTOK JSR MT14 \ Call MT14 to switch to justified text LDA #1 \ Set A = 1 so that extended token 1 (an empty string) \ gets printed below instead of token 176, followed by \ the Y-th token in RUTOK EQUB &2C \ Skip the next instruction by turning it into \ &2C &A9 &B0, or BIT &B0A9, which does nothing apart \ from affect the flags .PD3 LDA #176 \ Print extended token 176 ("{lower case}{justify} JSR DETOK2 \ {single cap}") TYA \ Print the extended token in Y from the second table JSR DETOK3 \ in RUTOK LDA #177 \ Set A = 177 so when we jump to PD4 in the next \ instruction, we print token 177 (".{cr}{left align}") BNE PD4 \ Jump to PD4 to print the extended token in A and \ return from the subroutine using a tail call .PD2 DEY \ Decrement the byte counter in Y BNE PDL1 \ Loop back to check the next byte in RUPLA until we \ either find a match for the system in ZZ, or we fall \ through into the "goat soup" extended description \ routine .PD1 \ We now print the "goat soup" extended description LDX #3 \ We now want to seed the random number generator with \ the s1 and s2 16-bit seeds from the current system, so \ we get the same extended description for each system \ every time we call PDESC, so set a counter in X for \ copying 4 bytes .PDL1K \ This label is a duplicate of the label above \ \ In the original source this label is PDL1, but \ because BeebAsm doesn't allow us to redefine labels, \ I have renamed it to PDL1K LDA QQ15+2,X \ Copy QQ15+2 to QQ15+5 (s1 and s2) to RAND to RAND+3 STA RAND,X DEX \ Decrement the loop counter BPL PDL1K \ Loop back to PDL1K until we have copied all LDA #5 \ Set A = 5, so we print extended token 5 in the next \ instruction ("{lower case}{justify}{single cap}[86-90] \ IS [140-144].{cr}{left align}" .PD4 JMP DETOK \ Print the extended token given in A, and return from \ the subroutine using a tail call \ ****************************************************************************** \ \ Name: BRIEF2 \ Type: Subroutine \ Category: Missions \ Summary: Start mission 2 \ Deep dive: The Thargoid Plans mission \ \ ****************************************************************************** .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 \ Set A = 11 so the call to BRP prints extended token 11 \ (the initial contact at the start of mission 2, asking \ us to head for Ceerdi for a mission briefing) \ Fall through into BRP to print the extended token in A \ and show the Status Mode screen \ ****************************************************************************** \ \ Name: BRP \ Type: Subroutine \ Category: Missions \ Summary: Print an extended token and show the Status Mode screen \ \ ****************************************************************************** .BRP JSR DETOK \ Print the extended token in A 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 \ Type: Subroutine \ Category: Missions \ Summary: Receive the briefing and plans for mission 2 \ Deep dive: The Thargoid Plans mission \ \ ****************************************************************************** .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 \ Type: Subroutine \ Category: Missions \ Summary: Finish mission 2 \ Deep dive: The Thargoid Plans mission \ \ ****************************************************************************** .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 \ Type: Subroutine \ Category: Missions \ Summary: Finish mission 1 \ Deep dive: The Constrictor mission \ \ ------------------------------------------------------------------------------ \ \ 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 INC TALLY+1 \ Award 256 kill points for completing the mission 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: BRIEF \ Type: Subroutine \ Category: Missions \ Summary: Start mission 1 and show the mission briefing \ Deep dive: The Constrictor mission \ \ ------------------------------------------------------------------------------ \ \ 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 \ 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) LDA #1 \ Move the text cursor to column 1 JSR DOXC STA INWK+7 \ Set z_hi = 1, the distance at which we show the \ rotating ship JSR TT66 \ Clear the top part of the screen, draw a border box, \ and set the current view type in QQ11 to 1 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 LL9 \ Draw the ship on screen 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 #112 \ If X < 112 then skip the next instruction BCC P%+4 LDX #112 \ X is bigger than 112, so set X = 112 so that X has a \ maximum value of 112 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 LL9 \ Draw the ship on screen JSR MVEIT \ Call MVEIT to move and rotate the ship in space 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 #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) BNE BRPS \ Jump to BRP via BRPS 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: BRIS \ Type: Subroutine \ Category: Missions \ Summary: Clear the screen, display "INCOMING MESSAGE" and wait for 2 \ seconds \ \ ****************************************************************************** .BRIS LDA #216 \ Print extended token 216 ("{clear screen}{tab 6}{move JSR DETOK \ to row 10, white, lower case}{white}{all caps}INCOMING \ MESSAGE" LDY #100 \ Wait for 100/50 of a second (2 seconds) and return JMP DELAY \ from the subroutine using a tail call \ ****************************************************************************** \ \ Name: PAUSE \ Type: Subroutine \ Category: Missions \ Summary: Display a rotating ship, waiting until a key is pressed, then \ remove the ship from the screen \ \ ****************************************************************************** .PAUSE JSR PAS1 \ Call PAS1 to display the rotating ship at space \ coordinates (0, 112, 256) and scan the keyboard, \ returning the internal key number in X (or 0 for no \ key press) BNE PAUSE \ If a key was already being held down when we entered \ this routine, keep looping back up to PAUSE, until \ the key is released .PAL1 JSR PAS1 \ Call PAS1 to display the rotating ship at space \ coordinates (0, 112, 256) and scan the keyboard, \ returning the internal key number in X (or 0 for no \ key press) BEQ PAL1 \ Keep looping up to PAL1 until a key is pressed LDA #0 \ Set the ship's AI flag to 0 (no AI) so it doesn't get STA INWK+31 \ any ideas of its own LDA #1 \ Clear the top part of the screen, draw a border box, JSR TT66 \ and set the current view type in QQ11 to 1 JSR LL9 \ Draw the ship on screen to redisplay it \ Fall through into MT23 to move to row 10, switch to \ white text, and switch to lower case when printing \ extended tokens \ ****************************************************************************** \ \ Name: MT23 \ Type: Subroutine \ Category: Text \ Summary: Move to row 10, switch to white text, and switch to lower case \ when printing extended tokens \ Deep dive: Extended text tokens \ \ ****************************************************************************** .MT23 LDA #10 \ Set A = 10, so when we fall through into MT29, the \ text cursor gets moved to row 10 EQUB &2C \ Skip the next instruction by turning it into \ &2C &A9 &06, or BIT &06A9, which does nothing apart \ from affect the flags \ Fall through into MT29 to move to the row in A, switch \ to white text, and switch to lower case \ ****************************************************************************** \ \ Name: MT29 \ Type: Subroutine \ Category: Text \ Summary: Move to row 6, switch to white text, and switch to lower case when \ printing extended tokens \ Deep dive: Extended text tokens \ \ ****************************************************************************** .MT29 LDA #6 \ Move the text cursor to row 6 JSR DOYC JSR WHITETEXT \ Set white text JMP MT13 \ Jump to MT13 to set bit 7 of DTW6 and bit 5 of DTW1, \ returning from the subroutine using a tail call \ ****************************************************************************** \ \ Name: PAS1 \ Type: Subroutine \ Category: Missions \ Summary: Display a rotating ship at space coordinates (0, 112, 256) and \ scan the keyboard \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X If a key is being pressed, X contains the internal key \ number, otherwise it contains 0 \ \ A Contains the same as X \ \ ****************************************************************************** .PAS1 LDA #112 \ Set y_lo = 112 STA INWK+3 LDA #0 \ Set x_lo = 0 STA INWK STA INWK+6 \ Set z_lo = 0 LDA #2 \ Set z_hi = 1, so (z_hi z_lo) = 256 STA INWK+7 JSR LL9 \ Draw the ship on screen JSR MVEIT \ Call MVEIT to move and rotate the ship in space JMP RDKEY \ Scan the keyboard for a key press and return the \ internal key number in X (or 0 for no key press), \ returning from the subroutine using a tail call \ ****************************************************************************** \ \ Name: PAUSE2 \ Type: Subroutine \ Category: Keyboard \ Summary: Wait until a key is pressed, ignoring any existing key press \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X The internal key number of the key that was pressed \ \ ****************************************************************************** .PAUSE2 JSR RDKEY \ Scan the keyboard for a key press and return the \ internal key number in A and X (or 0 for no key press) BNE PAUSE2 \ If a key was already being held down when we entered \ this routine, keep looping back up to PAUSE2, until \ the key is released JSR RDKEY \ Any pre-existing key press is now gone, so we can \ start scanning the keyboard again, returning the \ internal key number in A and X (or 0 for no key press) BEQ PAUSE2 \ Keep looping up to PAUSE2 until a key is pressed RTS \ Return from the subroutine \ ****************************************************************************** \ \ Save ELTC.bin \ \ ****************************************************************************** PRINT "ELITE C" PRINT "Assembled at ", ~CODE_C% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_C%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_C% PRINT "S.ELTC ", ~CODE_C%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_C% SAVE "3-assembled-output/ELTC.bin", CODE_C%, P%, LOAD% \ ****************************************************************************** \ \ ELITE D FILE \ \ Produces the binary file ELTD.bin that gets loaded by elite-bcfs.asm. \ \ ****************************************************************************** CODE_D% = P% LOAD_D% = LOAD% + P% - CODE% \ ****************************************************************************** \ \ Name: tnpr1 \ Type: Subroutine \ Category: Market \ Summary: Work out if we have space for one tonne of cargo \ \ ------------------------------------------------------------------------------ \ \ 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 \ Type: Subroutine \ Category: Market \ Summary: Work out if we have space for a specific amount of cargo \ \ ------------------------------------------------------------------------------ \ \ 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) 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 #200 \ Is the result greater than 200 (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 NOP \ This instruction appears to have no effect \ ****************************************************************************** \ \ Name: DOXC \ Type: Subroutine \ Category: Text \ Summary: Move the text cursor to a specified column by sending a #SETXC \ command to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The new column number \ \ ****************************************************************************** .DOXC PHA \ Store the new column number on the stack BIT printflag \ If bit 7 of printflag is clear (printer output is not BPL DOX1 \ enabled), jump to DOX1 to skip the printer-specific \ code below \ The following code moves the printer text cursor CMP XC \ If the new column in A is less than the current column BCC DOXLF \ in XC, jump down to DOXLF to do a carriage return, as \ we need to move the print head to the left, which we \ do by moving it all the way to the left first, before \ tabbing along to the new column value BEQ DOX1 \ If the new column in A is equal to the current column \ in XC, we don't need to move the print head, so jump \ to DOX1 to move on to the screen code PHY \ Store X and Y on the stack, so we can restore them PHX \ after the following loop SBC XC \ Set X = A - XC, which is the number of spaces by which TAX \ we should move the print head to the right .DOXL1 LDA #32 \ Print a space to move the print head to the right JSR TT26 DEX \ Decrement the number of spaces to move BNE DOXL1 \ Loop back until we have moved the print head to the \ right by X spaces PLX \ Retrieve X and Y from the stack PLY .DOX1 \ The following moves the screen text cursor LDA #SETXC \ Send the first part of a #SETXC command to the I/O JSR OSWRCH \ processor PLA \ Retrieve the new column number from the stack STA XC \ Set the text cursor x-coordinate in XC to the new \ column number JMP OSWRCH \ Send the column number to the I/O processor, so \ we've now sent a #SETXC command, and return \ from the subroutine using a tail call .DOXLF LDA #13 \ Print a carriage return to send the print head to the JSR TT26 \ beginning of the current line (note, this is not a \ newline, which is both a carriage return and a line \ feed, it's just a carriage return) JMP DOX1 \ Jump up to DOX1 to update the screen text cursor \ ****************************************************************************** \ \ Name: DOYC \ Type: Subroutine \ Category: Text \ Summary: Move the text cursor to a specified row by sending a #SETYC \ command to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The new row number \ \ ****************************************************************************** .DOYC STA YC \ Store A in YC, which sets the text cursor row number PHA \ Store the new row number on the stack LDA #SETYC \ Set A to #SETYC, ready to send to the I/O processor \ Fall through into label to send a #SETYC command \ to the I/O processor \ ****************************************************************************** \ \ Name: label \ Type: Subroutine \ Category: Text \ Summary: Send a two-byte OSWRCH command to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ This routine sends a command to the I/O processor, along with the parameter \ byte from the top of the stack. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The command byte to send to the I/O processor \ \ Top of stack The parameter to send to the I/O processor \ \ ****************************************************************************** .label JSR OSWRCH \ Send the command byte in A to the I/O processor PLA \ Retrieve the parameter from the stack JMP OSWRCH \ Send the parameter in A to the I/O processor, and \ return from the subroutine using a tail call \ ****************************************************************************** \ \ Name: INCYC \ Type: Subroutine \ Category: Text \ Summary: Move the text cursor to the next row \ \ ****************************************************************************** .INCYC PHA \ Store A on the stack so we can preserve it LDA YC \ Set A = YC + 1, so A is the number of the next row INA JSR DOYC \ Call DOYC to move the text cursor to the new row in A PLA \ Retrieve the original value of A from the stack RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: DOCOL \ Type: Subroutine \ Category: Text \ Summary: Set the text colour by sending a #SETCOL command to the I/O \ processor \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The text colour \ \ ****************************************************************************** .DOCOL PHA \ Store A, the colour number, on the stack LDA #SETCOL \ Set A to #SETCOL, ready to send to the I/O processor BNE label \ Jump to label to send a #SETCOL command to \ the I/O processor, returning from the subroutine \ using a tail call (this BNE is effectively a JMP as A \ is never zero) \ ****************************************************************************** \ \ Name: DOVDU19 \ Type: Subroutine \ Category: Drawing the screen \ Summary: Change the mode 1 palette by sending a #SETVDU19 command to the \ I/O processor \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The offset of the palette to set \ \ ****************************************************************************** .DOVDU19 PHA \ Store A, the palette offset, on the stack LDA #SETVDU19 \ Set A to #SETVDU19, ready to write to the I/O \ processor BNE label \ Jump to label to write #SETVDU19 to the I/O \ processor, returning from the subroutine using a tail \ call (this BNE is effectively a JMP as A is never \ zero) \ ****************************************************************************** \ \ Name: TRADEMODE \ Type: Subroutine \ Category: Drawing the screen \ Summary: Clear the screen and set up a printable trading screen \ \ ------------------------------------------------------------------------------ \ \ Clear the top part of the screen, draw a border box, set the print flag if \ CTRL is being pressed, set the palette for trading screens, and set the \ current view type in QQ11 to A. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The type of the new current view (see QQ11 for a list of \ view types) \ \ ****************************************************************************** .TRADEMODE PHA \ Store the view type on the stack so we can restore it \ after the call to CTRL JSR CTRL \ Scan the keyboard to see if CTRL is currently pressed STA printflag \ Store the result in printflag, which will have bit 7 \ set (and will therefore enable printing) if CTRL is \ being pressed PLA \ Restore the view type from the stack JSR TT66 \ Clear the top part of the screen, draw a border box, \ and set the current view type in QQ11 to A JSR FLKB \ Call FLKB to flush the keyboard buffer LDA #48 \ Send a #SETVDU19 48 command to the I/O processor to JSR DOVDU19 \ switch to the mode 1 palette for trading screens, \ which is yellow (colour 1), magenta (colour 2) and \ white (colour 3) LDA #CYAN \ Send a #SETCOL CYAN command to the I/O processor to JMP DOCOL \ switch to colour 3, which is white in the trade view, \ and return from the subroutine using a tail call \ ****************************************************************************** \ \ Name: TT20 \ Type: Subroutine \ Category: Universe \ Summary: Twist the selected system's seeds four times \ Deep dive: Twisting the system seeds \ Galaxy and system seeds \ \ ------------------------------------------------------------------------------ \ \ 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 \ Type: Subroutine \ Category: Universe \ Summary: Twist the selected system's seeds \ Deep dive: Twisting the system seeds \ Galaxy and system seeds \ \ ------------------------------------------------------------------------------ \ \ 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 \ Type: Subroutine \ Category: Universe \ Summary: Print the distance to the selected system in light years \ \ ------------------------------------------------------------------------------ \ \ 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 JMP INCYC \ Move the text cursor down by one line and return from \ the subroutine using a tail call \RTS \ This instruction is commented out in the original \ source .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 \ Type: Subroutine \ Category: Text \ Summary: Print a text token and a paragraph break \ \ ------------------------------------------------------------------------------ \ \ 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 \ Print the text token in A and fall through into TTX69 \ to print the paragraph break \ ****************************************************************************** \ \ Name: TTX69 \ Type: Subroutine \ Category: Text \ Summary: Print a paragraph break \ \ ------------------------------------------------------------------------------ \ \ 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 JSR INCYC \ Move the text cursor down a line \ Fall through into TT69 to set Sentence Case and print \ a newline \ ****************************************************************************** \ \ Name: TT69 \ Type: Subroutine \ Category: Text \ Summary: Set Sentence Case and print a newline \ \ ****************************************************************************** .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 \ Type: Subroutine \ Category: Text \ Summary: Print a newline \ \ ****************************************************************************** .TT67 INC YC \ Move the text cursor counter in YC down a line LDA #12 \ Load a newline character into A JMP TT27 \ Print the text token in A and return from the \ subroutine using a tail call \ ****************************************************************************** \ \ Name: TT70 \ Type: Subroutine \ Category: Universe \ Summary: Display "MAINLY " and jump to TT72 \ \ ------------------------------------------------------------------------------ \ \ This subroutine is called by TT25 when displaying a system's economy. \ \ ****************************************************************************** .TT70 LDA #173 \ Print recursive token 13 ("MAINLY ") JSR TT27 JMP TT72 \ Jump to TT72 to continue printing system data as part \ of routine TT25 \ ****************************************************************************** \ \ Name: spc \ Type: Subroutine \ Category: Text \ Summary: Print a text token followed by a space \ \ ------------------------------------------------------------------------------ \ \ 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 \ Print the text token in A JMP TT162 \ Print a space and return from the subroutine using a \ tail call \ ****************************************************************************** \ \ Name: TT25 \ Type: Subroutine \ Category: Universe \ Summary: Show the Data on System screen (red key f6) \ Deep dive: Generating system data \ Galaxy and system seeds \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ TT72 Used by TT70 to re-enter the routine after displaying \ "MAINLY" for the economy type \ \ ****************************************************************************** .TT25 LDA #1 \ Clear the top part of the screen, draw a border box, JSR TRADEMODE \ and set up a printable trading screen with a view type \ in QQ11 of 1 LDA #9 \ Move the text cursor to column 9 JSR DOXC LDA #163 \ Print recursive token 3 ("DATA ON {selected system JSR NLIN3 \ name}" and draw a horizontal line at pixel row 19 \ to box in the title 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 #194 \ Print recursive token 34 ("ECONOMY") followed by JSR TT68 \ a colon 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 \ 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 #162 \ Print recursive token 2 ("GOVERNMENT") followed by JSR TT68 \ a colon 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 JSR pr2 \ three-digit number without a decimal point (by \ clearing the C flag) JSR TTX69 \ Print a paragraph break and set Sentence Case LDA #192 \ Print recursive token 32 ("POPULATION") followed by a JSR TT68 \ colon SEC \ Call pr2 to print the population as a three-digit LDX QQ6 \ number with a decimal point (by setting the C flag), JSR pr2 \ so the number printed will be population / 10 LDA #198 \ Print recursive token 38 (" BILLION"), followed by a JSR TT60 \ paragraph break and Sentence Case LDA #'(' \ Print an opening bracket JSR TT27 LDA QQ15+4 \ Now to calculate the species, so first check bit 7 of BMI TT75 \ s2_lo, and if it is set, jump to TT75 as this is an \ alien species LDA #188 \ Bit 7 of s2_lo is clear, so print recursive token 28 JSR TT27 \ ("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, and we start with the first LSR A \ adjective, so fetch bits 2-7 of s2_hi into A and push LSR A \ onto the stack so we can use this later PHA AND #%00000111 \ Set A = bits 0-2 of A (so that's bits 2-4 of s2_hi) CMP #3 \ If A >= 3, jump to TT205 to skip the first adjective, BCS TT205 ADC #227 \ Otherwise A = 0, 1 or 2, so print recursive token JSR spc \ 67 + A, followed by a space, so: \ \ A = 0 prints token 67 ("LARGE") and a space \ A = 1 prints token 68 ("FIERCE") and a space \ A = 2 prints token 69 ("SMALL") and a space .TT205 PLA \ Now for the second adjective, so restore A to bits LSR A \ 2-7 of s2_hi, and throw away bits 2-4 to leave LSR A \ A = bits 5-7 of s2_hi 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 QQ15+3 \ Now for the third adjective, so EOR the high bytes of EOR QQ15+1 \ 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 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 \ \ A = 0 prints token 82 ("RODENT") \ A = 1 prints token 83 ("FROG") \ A = 2 prints token 84 ("LIZARD") \ A = 3 prints token 85 ("LOBSTER") \ A = 4 prints token 86 ("BIRD") \ A = 5 prints token 87 ("HUMANOID") \ A = 6 prints token 88 ("FELINE") \ A = 7 prints token 89 ("INSECT") .TT76 LDA #'S' \ Print an "S" to pluralise the species JSR TT27 LDA #')' \ And finally, print a closing bracket, followed by a JSR TT60 \ paragraph break and Sentence Case, to end the species \ section LDA #193 \ Print recursive token 33 ("GROSS PRODUCTIVITY"), JSR TT68 \ followed by a colon LDX QQ7 \ Fetch the 16-bit productivity value from QQ7 into LDY QQ7+1 \ (Y X) JSR pr6 \ Print (Y X) to 5 digits with no decimal point JSR TT162 \ Print a space LDA #0 \ Set QQ17 = 0 to switch to ALL CAPS STA QQ17 LDA #'M' \ Print "M" JSR TT27 LDA #226 \ Print recursive token 66 (" CR"), followed by a JSR TT60 \ paragraph break and Sentence Case LDA #250 \ Print recursive token 90 ("AVERAGE RADIUS"), followed JSR TT68 \ by a colon \ The average radius is calculated like this: \ \ ((s2_hi AND %1111) + 11) * 256 + s1_hi \ \ or, in terms of memory locations: \ \ ((QQ15+5 AND %1111) + 11) * 256 + QQ15+3 \ \ Because the multiplication is by 256, this is the \ same as saying a 16-bit number, with high byte: \ \ (QQ15+5 AND %1111) + 11 \ \ and low byte: \ \ QQ15+3 \ \ so we can set this up in (Y X) and call the pr5 \ routine to print it out 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 JSR pr5 \ Print (Y X) to 5 digits, not including a decimal \ 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 TT26 LDA #'m' JSR TT26 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) JMP PDESC \ Jump to PDESC to print the system's extended \ description, returning from the subroutine using a \ tail call \ The following code doesn't appear to be called from \ anywhere, so it's presumably a remnant of code from \ an earlier version of the extended description code LDX ZZ \ Fetch the system number from ZZ into X \LDY #LO(PTEXT) \ These instructions are commented out in the original \STY INWK \ source. The variable PTEXT doesn't exist, so it isn't \LDY #HI(PTEXT)-1 \ entirely obvious what this code does, though it looks \STY INWK+1 \ like it loops through a table of text tokens in PTEXT \ \ until we get to the entry for the current system, \LDY #&FF \ which it prints out as text tokens (so perhaps PTEXT \ \ used to be a token table for the system's extended \.PDT1 \ descriptions before PDESC took over) \ \INY \BNE P%+4 \INC INWK+1 \ \LDA (INWK),Y \ \BNE PDT1 \ \DEX \ \BNE PDT1 \ \.PDT2 \ \INY \BNE P%+4 \INC INWK+1 \ \STY INWK+2 \ \LDA (INWK),Y \ \BEQ TT24-1 \ \JSR TT27 \ \LDY INWK+2 \ \JMP PDT2 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TT24 \ Type: Subroutine \ Category: Universe \ Summary: Calculate system data from the system seeds \ Deep dive: Generating system data \ Galaxy and system seeds \ \ ------------------------------------------------------------------------------ \ \ Calculate system data from the seeds in QQ15 and store them in the relevant \ locations. Specifically, this routine calculates the following from the three \ 16-bit seeds in QQ15 (using only s0_hi, s1_hi and s1_lo): \ \ QQ3 = economy (0-7) \ QQ4 = government (0-7) \ QQ5 = technology level (0-14) \ QQ6 = population * 10 (1-71) \ QQ7 = productivity (96-62480) \ \ The ranges of the various values are shown in brackets. Note that the radius \ and type of inhabitant are calculated on-the-fly in the TT25 routine when \ the system data gets displayed, so they aren't calculated here. \ \ ****************************************************************************** .TT24 LDA QQ15+1 \ Fetch s0_hi and extract bits 0-2 to determine the AND #%00000111 \ system's economy, and store in QQ3 STA QQ3 LDA QQ15+2 \ Fetch s1_lo and extract bits 3-5 to determine the LSR A \ system's government, and store in QQ4 LSR A LSR A AND #%00000111 STA QQ4 LSR A \ If government isn't anarchy or feudal, skip to TT77, BNE TT77 \ as we need to fix the economy of anarchy and feudal \ systems so they can't be rich LDA QQ3 \ Set bit 1 of the economy in QQ3 to fix the economy ORA #%00000010 \ for anarchy and feudal governments STA QQ3 .TT77 LDA QQ3 \ Now to work out the tech level, which we do like this: EOR #%00000111 \ CLC \ flipped_economy + (s1_hi AND %11) + (government / 2) STA QQ5 \ \ or, in terms of memory locations: \ \ QQ5 = (QQ3 EOR %111) + (QQ15+3 AND %11) + (QQ4 / 2) \ \ We start by setting QQ5 = QQ3 EOR %111 LDA QQ15+3 \ We then take the first 2 bits of s1_hi (QQ15+3) and AND #%00000011 \ add it into QQ5 ADC QQ5 STA QQ5 LDA QQ4 \ And finally we add QQ4 / 2 and store the result in LSR A \ QQ5, using LSR then ADC to divide by 2, which rounds ADC QQ5 \ up the result for odd-numbered government types STA QQ5 ASL A \ Now to work out the population, like so: ASL A \ ADC QQ3 \ (tech level * 4) + economy + government + 1 ADC QQ4 \ ADC #1 \ or, in terms of memory locations: STA QQ6 \ \ QQ6 = (QQ5 * 4) + QQ3 + QQ4 + 1 LDA QQ3 \ Finally, we work out productivity, like this: EOR #%00000111 \ ADC #3 \ (flipped_economy + 3) * (government + 4) STA P \ * population LDA QQ4 \ * 8 ADC #4 \ STA Q \ or, in terms of memory locations: JSR MULTU \ \ QQ7 = (QQ3 EOR %111 + 3) * (QQ4 + 4) * QQ6 * 8 \ \ We do the first step by setting P to the first \ expression in brackets and Q to the second, and \ calling MULTU, so now (A P) = P * Q. The highest this \ can be is 10 * 11 (as the maximum values of economy \ and government are 7), so the high byte of the result \ will always be 0, so we actually have: \ \ P = P * Q \ = (flipped_economy + 3) * (government + 4) LDA QQ6 \ We now take the result in P and multiply by the STA Q \ population to get the productivity, by setting Q to JSR MULTU \ the population from QQ6 and calling MULTU again, so \ now we have: \ \ (A P) = P * population ASL P \ Next we multiply the result by 8, as a 16-bit number, ROL A \ so we shift both bytes to the left three times, using ASL P \ the C flag to carry bits from bit 7 of the low byte ROL A \ into bit 0 of the high byte ASL P ROL A STA QQ7+1 \ Finally, we store the productivity in two bytes, with LDA P \ the low byte in QQ7 and the high byte in QQ7+1 STA QQ7 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TT22 \ Type: Subroutine \ Category: Charts \ Summary: Show the Long-range Chart (red key f4) \ Deep dive: A sense of scale \ \ ****************************************************************************** .TT22 LDA #64 \ Clear the top part of the screen, draw a border box, JSR TT66 \ and set the current view type in QQ11 to 32 (Long- \ range Chart) LDA #CYAN \ Send a #SETCOL CYAN command to the I/O processor to JSR DOCOL \ switch to colour 3, which is white in the chart view LDA #16 \ Send a #SETVDU19 16 command to the I/O processor to JSR DOVDU19 \ switch to the mode 1 palette for the trade view, which \ is yellow (colour 1), magenta (colour 2) and white \ (colour 3) LDA #7 \ Move the text cursor to column 7 JSR DOXC 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 TT27 \ number right-aligned to width 3}") JSR NLIN \ Draw a horizontal line at pixel row 23 to box in the \ title and act as the top frame of the chart, and move \ the text cursor down one line 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 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 LDX QQ15+3 \ Fetch the s1_hi seed into X, which gives us the \ galactic x-coordinate of this system 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 \ We halve the y-coordinate because the galaxy in \ in Elite is rectangular rather than square, and is \ twice as wide (x-axis) as it is high (y-axis), so the \ chart is 256 pixels wide and 128 high CLC \ Add 24 to the halved y-coordinate and store in XX15+1 ADC #24 \ (as the top of the chart is on pixel row 24, just STA XX15+1 \ below the line we drew on row 23 above) JSR PIXEL \ Call PIXEL to draw a point at (X, A), with the size of \ the point dependent on the distance specified in ZZ \ (so a high value of ZZ will produce a one-pixel point, \ a medium value will produce a two-pixel dash, and a \ small value will produce a four-pixel square) 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 JSR PBFL \ Call PBFL to send the contents of the pixel buffer to \ the I/O processor for plotting on-screen 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 \ Fall through into TT15 to draw crosshairs of size 4 at \ the selected system's coordinates \ ****************************************************************************** \ \ Name: TT15 \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw a set of crosshairs \ \ ------------------------------------------------------------------------------ \ \ For all views except the Short-range Chart, the centre is drawn 24 pixels to \ the right of the y-coordinate given. \ \ The crosshairs are drawn in colour 3, which is white in the chart view and \ cyan elsewhere. We can draw them in the current colour by calling the TT15b \ entry point. \ \ ------------------------------------------------------------------------------ \ \ 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 \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ TT15b Draw the crosshairs in the current colour \ \ ****************************************************************************** .TT15 LDA #CYAN \ Send a #SETCOL CYAN command to the I/O processor to JSR DOCOL \ switch to colour 3, which is white in the chart view \ and cyan elsewhere .TT15b 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, BPL TT178 \ which is the only view with bit 7 set, then jump to \ TT178 to skip the 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 TT85 \ If the above addition didn't overflow, then A is \ correct, so jump to TT85 to 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) .TT85 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 LL30 \ 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 \ --- Mod: Code added for two-player Elite: -----------> BEQ TT87 \ If this is the space view then the y-coordinate is \ fine, so skip to TT87 \ --- End of added code -------------------------------> BMI TT87 \ If this is the Short-range Chart then the y-coordinate \ 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 LL30 \ Draw a vertical line from (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, \ and returning from the subroutine using a tail call \ ****************************************************************************** \ \ Name: TT14 \ Type: Subroutine \ Category: Drawing circles \ Summary: Draw a circle with crosshairs on a chart \ \ ------------------------------------------------------------------------------ \ \ Draw a circle with crosshairs at the current system's galactic coordinates. \ \ ****************************************************************************** .TT126 LDA #104 \ Set QQ19 = 104, for the x-coordinate of the centre of STA QQ19 \ the fixed circle on the Short-range Chart LDA #90 \ Set QQ19+1 = 90, for the y-coordinate of the centre of STA QQ19+1 \ the fixed circle on the Short-range Chart LDA #16 \ Set QQ19+2 = 16, the size of the crosshairs on the STA QQ19+2 \ Short-range Chart JSR TT15 \ Draw the set of crosshairs defined in QQ19, at the \ exact coordinates as this is the Short-range Chart LDA QQ14 \ Set K to the fuel level from QQ14, so this can act as STA K \ the circle's radius (70 being a full tank) JMP TT128 \ Jump to TT128 to draw a circle with the centre at the \ same coordinates as the crosshairs, (QQ19, QQ19+1), \ and radius K that reflects the current fuel levels, \ returning from the subroutine using a tail call .TT14 LDA #CYAN \ Send a #SETCOL CYAN command to the I/O processor to JSR DOCOL \ switch to colour 3, which is white in the chart view LDA QQ11 \ If the current view is the Short-range Chart, which BMI TT126 \ is the only view with bit 7 set, then jump up to TT126 \ to draw the crosshairs and circle for that view \ Otherwise this is the Long-range Chart, so we draw the \ crosshairs and circle for that view instead LDA QQ14 \ Set K to the fuel level from QQ14 divided by 4, so LSR A \ this can act as the circle's radius (70 being a full LSR A \ tank, which divides down to a radius of 17) STA K LDA QQ0 \ Set QQ19 to the x-coordinate of the current system, STA QQ19 \ which will be the centre of the circle and crosshairs \ we draw LDA QQ1 \ Set QQ19+1 to the y-coordinate of the current system, LSR A \ halved because the galactic chart is half as high as STA QQ19+1 \ it is wide, which will again be the centre of the \ circle and crosshairs we draw LDA #7 \ Set QQ19+2 = 7, the size of the crosshairs on the STA QQ19+2 \ Long-range Chart JSR TT15 \ Draw the set of crosshairs defined in QQ19, which will \ be drawn 24 pixels to the right of QQ19+1 LDA QQ19+1 \ Add 24 to the y-coordinate of the crosshairs in QQ19+1 CLC \ so that the centre of the circle matches the centre ADC #24 \ of the crosshairs STA QQ19+1 \ Fall through into TT128 to draw a circle with the \ centre at the same coordinates as the crosshairs, \ (QQ19, QQ19+1), and radius K that reflects the \ current fuel levels \ ****************************************************************************** \ \ Name: TT128 \ Type: Subroutine \ Category: Drawing circles \ Summary: Draw a circle on a chart \ Deep dive: Drawing circles \ \ ------------------------------------------------------------------------------ \ \ Draw a circle with the centre at (QQ19, QQ19+1) and radius K. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ QQ19 The x-coordinate of the centre of the circle \ \ QQ19+1 The y-coordinate of the centre of the circle \ \ K The radius of the circle \ \ ****************************************************************************** .TT128 LDA QQ19 \ Set K3 = the x-coordinate of the centre STA K3 LDA QQ19+1 \ Set K4 = the y-coordinate of the centre STA K4 LDX #0 \ Set the high bytes of K3(1 0) and K4(1 0) to 0 STX K4+1 STX K3+1 INX \ Set LSP = 1 to reset the ball line heap STX LSP LDX #2 \ Set STP = 2, the step size for the circle STX STP LDA #RED \ Send a #SETCOL RED command to the I/O processor to JSR DOCOL \ switch to colour 2, which is red in the chart view JMP CIRCLE2 \ Jump to CIRCLE2 to draw a circle with the centre at \ (K3(1 0), K4(1 0)) and radius K, returning from the \ subroutine using a tail call \ ****************************************************************************** \ \ Name: TT219 \ Type: Subroutine \ Category: Market \ Summary: Show the Buy Cargo screen (red key f1) \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ BAY2 Jump into the main loop at FRCE, setting the key \ "pressed" to red key f9 (so we show the Inventory \ screen) \ \ ****************************************************************************** .TT219 LDA #2 \ Clear the top part of the screen, draw a border box, JSR TRADEMODE \ and set up a printable trading screen with a view type \ in QQ11 of 2 (Buy Cargo screen) JSR TT163 \ Print the column headers for the prices table LDA #%10000000 \ Set bit 7 of QQ17 to switch to Sentence Case, with the STA QQ17 \ next letter in capitals LDA #0 \ We're going to loop through all the available market STA QQ29 \ items, so we set up a counter in QQ29 to denote the \ current item and start it at 0 .TT220 JSR TT151 \ Call TT151 to print the item name, market price and \ availability of the current item, and set QQ24 to the \ item's price / 4, QQ25 to the quantity available and \ QQ19+1 to byte #1 from the market prices table for \ this item LDA QQ25 \ If there are some of the current item available, jump BNE TT224 \ to TT224 below to see if we want to buy any JMP TT222 \ Otherwise there are none available, so jump down to \ TT222 to skip this item .TQ4 LDY #176 \ Set Y to the recursive token 16 ("QUANTITY") .Tc JSR TT162 \ Print a space TYA \ Print the recursive token in Y followed by a question JSR prq \ mark .TTX224 JSR dn2 \ Call dn2 to make a short, high beep and delay for 1 \ second .TT224 JSR CLYNS \ Clear the bottom three text rows of the upper screen, \ and move the text cursor to the first cleared row LDA #204 \ Print recursive token 44 ("QUANTITY OF ") JSR TT27 LDA QQ29 \ Print recursive token 48 + QQ29, which will be in the CLC \ range 48 ("FOOD") to 64 ("ALIEN ITEMS"), so this ADC #208 \ prints the current item's name JSR TT27 LDA #'/' \ Print "/" JSR TT27 JSR TT152 \ Print the unit ("t", "kg" or "g") for the current item \ (as the call to TT151 above set QQ19+1 with the \ appropriate value) LDA #'?' \ Print "?" JSR TT27 JSR TT67 \ Print a newline LDX #0 \ These instructions have no effect, as they are STX R \ repeated at the start of gnum, which we call next. LDX #12 \ Perhaps they were left behind when code was moved from STX T1 \ here into gnum, and weren't deleted? .TT223K \ This label is a duplicate of a label in the gnum \ routine, so this could also be a remnant from code \ that got moved into the gnum subroutine \ \ In the original source this label is TT223, but \ because BeebAsm doesn't allow us to redefine labels, \ I have renamed it to TT223K JSR gnum \ Call gnum to get a number from the keyboard, which \ will be the quantity of this item we want to purchase, \ returning the number entered in A and R BCS TQ4 \ If gnum set the C flag, the number entered is greater \ than the quantity available, so jump up to TQ4 to \ display a "Quantity?" error, beep, clear the number \ and try again STA P \ Otherwise we have a valid purchase quantity entered, \ so store the amount we want to purchase in P JSR tnpr \ Call tnpr to work out whether there is room in the \ cargo hold for this item LDY #206 \ Set Y to recursive token 46 (" CARGO{sentence case}") \ to pass to the Tc routine if we call it BCS Tc \ If the C flag is set, then there is no room in the \ cargo hold, jump up to Tc to print a "Cargo?" error, \ beep, clear the number and try again LDA QQ24 \ There is room in the cargo hold, so now to check STA Q \ whether we have enough cash, so fetch the item's \ price / 4, which was returned in QQ24 by the call \ to TT151 above and store it in Q JSR GCASH \ Call GCASH to calculate: \ \ (Y X) = P * Q * 4 \ \ which will be the total price of this transaction \ (as P contains the purchase quantity and Q contains \ the item's price / 4) JSR LCASH \ Subtract (Y X) cash from the cash pot in CASH LDY #197 \ If the C flag is clear, we didn't have enough cash, BCC Tc \ so set Y to the recursive token 37 ("CASH") and jump \ up to Tc to print a "Cash?" error, beep, clear the \ number and try again LDY QQ29 \ Fetch the current market item number from QQ29 into Y LDA R \ Set A to the number of items we just purchased (this \ was set by gnum above) PHA \ Store the quantity just purchased on the stack CLC \ Add the number purchased to the Y-th byte of QQ20, ADC QQ20,Y \ which contains the number of items of this type in STA QQ20,Y \ our hold (so this transfers the bought items into our \ cargo hold) LDA AVL,Y \ Subtract the number of items from the Y-th byte of SEC \ AVL, which contains the number of items of this type SBC R \ that are available on the market STA AVL,Y PLA \ Restore the quantity just purchased BEQ TT222 \ If we didn't buy anything, jump to TT222 to skip the \ following instruction JSR dn \ Call dn to print the amount of cash left in the cash \ pot, then make a short, high beep to confirm the \ purchase, and delay for 1 second .TT222 LDA QQ29 \ Move the text cursor to row QQ29 + 5 (where QQ29 is CLC \ the item number, starting from 0) ADC #5 JSR DOYC LDA #0 \ Move the text cursor to column 0 JSR DOXC INC QQ29 \ Increment QQ29 to point to the next item LDA QQ29 \ If QQ29 >= 17 then jump to BAY2 as we have done the CMP #17 \ last item BCS BAY2 JMP TT220 \ Otherwise loop back to TT220 to print the next market \ item .BAY2 LDA #f9 \ Jump into the main loop at FRCE, setting the key JMP FRCE \ "pressed" to red key f9 (so we show the Inventory \ screen) \ ****************************************************************************** \ \ Name: gnum \ Type: Subroutine \ Category: Market \ Summary: Get a number from the keyboard \ \ ------------------------------------------------------------------------------ \ \ Get a number from the keyboard, up to the maximum number in QQ25, for the \ buying and selling of cargo and equipment. \ \ Pressing "Y" will return the maximum number (i.e. buy/sell all items), while \ pressing "N" will abort the sale and return a 0. \ \ Pressing a key with an ASCII code less than ASCII "0" will return a 0 in A (so \ that includes pressing Space or Return), while pressing a key with an ASCII \ code greater than ASCII "9" will jump to the Inventory screen (so that \ includes all letters and most punctuation). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ QQ25 The maximum number allowed \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A The number entered \ \ R Also contains the number entered \ \ C flag Set if the number is too large (> QQ25), clear otherwise \ \ ****************************************************************************** .gnum LDA #MAGENTA \ Send a #SETCOL MAGENTA command to the I/O processor to JSR DOCOL \ switch to colour 2, which is magenta in the trade view LDX #0 \ We will build the number entered in R, so initialise STX R \ it with 0 LDX #12 \ We will check for up to 12 key presses, so set a STX T1 \ counter in T1 .TT223 JSR TT217 \ Scan the keyboard until a key is pressed, and return \ the key's ASCII code in A (and X) LDX R \ If R is non-zero then skip to NWDAV2, as we are BNE NWDAV2 \ already building a number CMP #'y' \ If "Y" was pressed, jump to NWDAV1 to return the BEQ NWDAV1 \ maximum number allowed (i.e. buy/sell the whole stock) CMP #'n' \ If "N" was pressed, jump to NWDAV3 to return from the BEQ NWDAV3 \ subroutine with a result of 0 (i.e. abort transaction) .NWDAV2 STA Q \ Store the key pressed in Q SEC \ Subtract ASCII "0" from the key pressed, to leave the SBC #'0' \ numeric value of the key in A (if it was a number key) BCC OUT \ If A < 0, jump to OUT to load the current number and \ return from the subroutine, as the key pressed was \ RETURN (or some other character with a value less than \ ASCII "0") CMP #10 \ If A >= 10, jump to BAY2 to display the Inventory BCS BAY2 \ screen, as the key pressed was a letter or other \ non-digit and is greater than ASCII "9" STA S \ Store the numeric value of the key pressed in S LDA R \ Fetch the result so far into A CMP #26 \ If A >= 26, where A is the number entered so far, then BCS OUT \ adding a further digit will make it bigger than 256, \ so jump to OUT to return from the subroutine with the \ result in R (i.e. ignore the last key press) ASL A \ Set A = (A * 2) + (A * 8) = A * 10 STA T ASL A ASL A ADC T ADC S \ Add the pressed digit to A and store in R, so R now STA R \ contains its previous value with the new key press \ tacked onto the end CMP QQ25 \ If the result in R = the maximum allowed in QQ25, jump BEQ TT226 \ to TT226 to print the key press and keep looping (the \ BEQ is needed because the BCS below would jump to OUT \ if R >= QQ25, which we don't want) BCS OUT \ If the result in R > QQ25, jump to OUT to return from \ the subroutine with the result in R .TT226 LDA Q \ Print the character in Q (i.e. the key that was JSR TT26 \ pressed, as we stored the ASCII value in Q earlier) DEC T1 \ Decrement the loop counter BNE TT223 \ Loop back to TT223 until we have checked for 12 digits .OUT PHP \ Store the processor flags, so we can return the C flag \ without the call to DOCOL corrupting it LDA #CYAN \ Send a #SETCOL CYAN command to the I/O processor to JSR DOCOL \ switch to colour 3, which is white in the trade view PLP \ Restore the processor flags, in particular the C flag LDA R \ Set A to the result we have been building in R RTS \ Return from the subroutine .NWDAV1 \ If we get here then "Y" was pressed, so we return the \ maximum number allowed, which is in QQ25 JSR TT26 \ Print the character for the key that was pressed LDA QQ25 \ Set R = QQ25, so we return the maximum value allowed STA R BRA OUT \ Jump to OUT to return from the subroutine .NWDAV3 \ If we get here then "N" was pressed, so we return 0 JSR TT26 \ Print the character for the key that was pressed LDA #0 \ Set R = 0, so we return 0 STA R BRA OUT \ Jump to OUT to return from the subroutine \ ****************************************************************************** \ \ Name: NWDAV4 \ Type: Subroutine \ Category: Market \ Summary: Print an "ITEM?" error, make a beep and rejoin the TT210 routine \ \ ****************************************************************************** .NWDAV4 JSR TT67 \ Print a newline LDA #176 \ Print recursive token 127 ("ITEM") followed by a JSR prq \ question mark JSR dn2 \ Call dn2 to make a short, high beep and delay for 1 \ second LDY QQ29 \ Fetch the item number we are selling from QQ29 JMP NWDAVxx \ Jump back into the TT210 routine that called NWDAV4 \ ****************************************************************************** \ \ Name: TT208 \ Type: Subroutine \ Category: Market \ Summary: Show the Sell Cargo screen (red key f2) \ \ ****************************************************************************** .TT208 LDA #4 \ Clear the top part of the screen, draw a border box, JSR TRADEMODE \ and set up a printable trading screen with a view type \ in QQ11 of 4 (Sell Cargo screen) LDA #10 \ Move the text cursor to column 10 JSR DOXC LDA #205 \ Print recursive token 45 ("SELL") JSR TT27 LDA #206 \ Print recursive token 46 (" CARGO{sentence case}") JSR NLIN3 \ draw a horizontal line at pixel row 19 to box in the \ title JSR TT67 \ Print a newline \ Fall through into TT210 to show the Inventory screen \ with the option to sell \ ****************************************************************************** \ \ Name: TT210 \ Type: Subroutine \ Category: Market \ Summary: Show a list of current cargo in our hold, optionally to sell \ \ ------------------------------------------------------------------------------ \ \ Show a list of current cargo in our hold, either with the ability to sell (the \ Sell Cargo screen) or without (the Inventory screen), depending on the current \ view. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ QQ11 The current view: \ \ * 4 = Sell Cargo \ \ * 8 = Inventory \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ NWDAVxx Used to rejoin this routine from the call to NWDAV4 \ \ ****************************************************************************** .TT210 LDY #0 \ We're going to loop through all the available market \ items and check whether we have any in the hold (and, \ if we are in the Sell Cargo screen, whether we want \ to sell any items), so we set up a counter in Y to \ denote the current item and start it at 0 .TT211 STY QQ29 \ Store the current item number in QQ29 .NWDAVxx LDX QQ20,Y \ Fetch into X the amount of the current item that we BEQ TT212 \ have in our cargo hold, which is stored in QQ20+Y, \ and if there are no items of this type in the hold, \ jump down to TT212 to skip to the next item TYA \ Set Y = Y * 4, so this will act as an index into the ASL A \ market prices table at QQ23 for this item (as there ASL A \ are four bytes per item in the table) TAY LDA QQ23+1,Y \ Fetch byte #1 from the market prices table for the STA QQ19+1 \ current item and store it in QQ19+1, for use by the \ call to TT152 below TXA \ Store the amount of item in the hold (in X) on the PHA \ stack JSR TT69 \ Call TT69 to set Sentence Case and print a newline CLC \ Print recursive token 48 + QQ29, which will be in the LDA QQ29 \ range 48 ("FOOD") to 64 ("ALIEN ITEMS"), so this ADC #208 \ prints the current item's name JSR TT27 LDA #14 \ Move the text cursor to column 14, for the item's JSR DOXC \ quantity PLA \ Restore the amount of item in the hold into X TAX STA QQ25 \ Store the amount of this item in the hold in QQ25 CLC \ Print the 8-bit number in X to 3 digits, without a JSR pr2 \ decimal point JSR TT152 \ Print the unit ("t", "kg" or "g") for the market item \ whose byte #1 from the market prices table is in \ QQ19+1 (which we set up above) LDA QQ11 \ If the current view type in QQ11 is not 4 (Sell Cargo CMP #4 \ screen), jump to TT212 to skip the option to sell BNE TT212 \ items \JSR TT162 \ This instruction is commented out in the original \ source LDA #205 \ Print recursive token 45 ("SELL") JSR TT27 LDA #206 \ Print extended token 206 ("{all caps}(Y/N)?") JSR DETOK JSR gnum \ Call gnum to get a number from the keyboard, which \ will be the number of the item we want to sell, \ returning the number entered in A and R, and setting \ the C flag if the number is bigger than the available \ amount of this item in QQ25 BEQ TT212 \ If no number was entered, jump to TT212 to move on to \ the next item BCS NWDAV4 \ If the number entered was too big, jump to NWDAV4 to \ print an "ITEM?" error, make a beep and rejoin the \ routine at NWDAVxx above LDA QQ29 \ We are selling this item, so fetch the item number \ from QQ29 LDX #255 \ Set QQ17 = 255 to disable printing STX QQ17 JSR TT151 \ Call TT151 to set QQ24 to the item's price / 4 (the \ routine doesn't print the item details, as we just \ disabled printing) LDY QQ29 \ Subtract R (the number of items we just asked to buy) LDA QQ20,Y \ from the available amount of this item in QQ20, as we SEC \ just bought them SBC R STA QQ20,Y LDA R \ Set P to the amount of this item we just bought STA P LDA QQ24 \ Set Q to the item's price / 4 STA Q JSR GCASH \ Call GCASH to calculate \ \ (Y X) = P * Q * 4 \ \ which will be the total price we make from this sale \ (as P contains the quantity we're selling and Q \ contains the item's price / 4) JSR MCASH \ Add (Y X) cash to the cash pot in CASH LDA #0 \ We've made the sale, so set the amount STA QQ17 \ Set QQ17 = 0, which enables printing again .TT212 LDY QQ29 \ Fetch the item number from QQ29 into Y, and increment INY \ Y to point to the next item CPY #17 \ Loop back to TT211 to print the next item in the hold BCC TT211 \ until Y = 17 (at which point we have done the last \ item) LDA QQ11 \ If the current view type in QQ11 is not 4 (Sell Cargo CMP #4 \ screen), skip the next two instructions and just BNE P%+8 \ return from the subroutine JSR dn2 \ This is the Sell Cargo screen, so call dn2 to make a \ short, high beep and delay for 1 second JMP BAY2 \ And then jump to BAY2 to display the Inventory \ screen, as we have finished selling cargo RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TT213 \ Type: Subroutine \ Category: Market \ Summary: Show the Inventory screen (red key f9) \ \ ****************************************************************************** .TT213 LDA #8 \ Clear the top part of the screen, draw a border box, JSR TRADEMODE \ and set up a printable trading screen with a view type \ in QQ11 of 4 (Inventory screen) LDA #11 \ Move the text cursor to column 11 to print the screen JSR DOXC \ title LDA #164 \ Print recursive token 4 ("INVENTORY{crlf}") followed JSR TT60 \ by a paragraph break and Sentence Case JSR NLIN4 \ Draw a horizontal line at pixel row 19 to box in the \ title. The authors could have used a call to NLIN3 \ instead and saved the above call to TT60, but you \ just can't optimise everything JSR fwl \ Call fwl to print the fuel and cash levels on two \ separate lines LDA CRGO \ If our ship's cargo capacity is < 26 (i.e. we do not CMP #26 \ have a cargo bay extension), skip the following two BCC P%+7 \ instructions LDA #107 \ We do have a cargo bay extension, so print recursive JSR TT27 \ token 107 ("LARGE CARGO{sentence case} BAY") JMP TT210 \ Jump to TT210 to print the contents of our cargo bay \ and return from the subroutine using a tail call \ ****************************************************************************** \ \ Name: TT214 \ Type: Subroutine \ Category: Keyboard \ Summary: Ask a question with a "Y/N?" prompt and return the response \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The text token to print before the "Y/N?" prompt \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag Set if the response was "yes", clear otherwise \ \ ****************************************************************************** .TT214 \.TT214 \ These instructions are commented out in the original \ \ source \PHA \JSR TT162 \PLA .TT221 JSR TT27 \ Print the text token in A LDA #206 \ Print extended token 206 ("{all caps}(Y/N)?") JSR DETOK JSR TT217 \ Scan the keyboard until a key is pressed, and return \ the key's ASCII code in A and X ORA #%00100000 \ Set bit 5 in the value of the key pressed, which \ converts it to lower case CMP #'y' \ If "y" was pressed, jump to TT218 BEQ TT218 LDA #'n' \ Otherwise jump to TT26 to print "n" and return from JMP TT26 \ the subroutine using a tail call (so all other \ responses apart from "y" indicate a no) .TT218 JSR TT26 \ Print the character in A, i.e. print "y" SEC \ Set the C flag to indicate a "yes" response RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TT16 \ Type: Subroutine \ Category: Charts \ Summary: Move the crosshairs on a chart \ \ ------------------------------------------------------------------------------ \ \ Move the chart crosshairs by the amount in X and Y. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The amount to move the crosshairs in the x-axis \ \ Y The amount to move the crosshairs in the y-axis \ \ ****************************************************************************** .TT16 TXA \ Push the change in X onto the stack (let's call this PHA \ the x-delta) DEY \ Negate the change in Y and push it onto the stack TYA \ (let's call this the y-delta) EOR #&FF PHA JSR WSCAN \ Call WSCAN to wait for the vertical sync, so the whole \ screen gets drawn and we can move the crosshairs with \ no screen flicker JSR TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10), \ which will erase the crosshairs currently there PLA \ Store the y-delta in QQ19+3 and fetch the current STA QQ19+3 \ y-coordinate of the crosshairs from QQ10 into A, ready LDA QQ10 \ for the call to TT123 JSR TT123 \ Call TT123 to move the selected system's galactic \ y-coordinate by the y-delta, putting the new value in \ QQ19+4 LDA QQ19+4 \ Store the updated y-coordinate in QQ10 (the current STA QQ10 \ y-coordinate of the crosshairs) STA QQ19+1 \ This instruction has no effect, as QQ19+1 is \ overwritten below, both in TT103 and TT105 PLA \ Store the x-delta in QQ19+3 and fetch the current STA QQ19+3 \ x-coordinate of the crosshairs from QQ10 into A, ready LDA QQ9 \ for the call to TT123 JSR TT123 \ Call TT123 to move the selected system's galactic \ x-coordinate by the x-delta, putting the new value in \ QQ19+4 LDA QQ19+4 \ Store the updated x-coordinate in QQ9 (the current STA QQ9 \ x-coordinate of the crosshairs) STA QQ19 \ This instruction has no effect, as QQ19 is overwritten \ below, both in TT103 and TT105 \ Now we've updated the coordinates of the crosshairs, \ fall through into TT103 to redraw them at their new \ location \ ****************************************************************************** \ \ Name: TT103 \ Type: Subroutine \ Category: Charts \ Summary: Draw a small set of crosshairs on a chart \ \ ------------------------------------------------------------------------------ \ \ Draw a small set of crosshairs on a galactic chart at the coordinates in \ (QQ9, QQ10). \ \ ****************************************************************************** .TT103 LDA QQ11 \ Fetch the current view type into A BMI TT105 \ If this is the Short-range Chart screen, jump to TT105 LDA QQ9 \ Store the crosshairs x-coordinate in QQ19 STA QQ19 LDA QQ10 \ Halve the crosshairs y-coordinate and store it in QQ19 LSR A \ (we halve it because the Long-range Chart is half as STA QQ19+1 \ high as it is wide) LDA #4 \ Set QQ19+2 to 4 denote crosshairs of size 4 STA QQ19+2 JMP TT15 \ Jump to TT15 to draw crosshairs of size 4 at the \ crosshairs coordinates, returning from the subroutine \ using a tail call \ ****************************************************************************** \ \ Name: TT123 \ Type: Subroutine \ Category: Charts \ Summary: Move galactic coordinates by a signed delta \ \ ------------------------------------------------------------------------------ \ \ Move an 8-bit galactic coordinate by a certain distance in either direction \ (i.e. a signed 8-bit delta), but only if it doesn't cause the coordinate to \ overflow. The coordinate is in a single axis, so it's either an x-coordinate \ or a y-coordinate. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The galactic coordinate to update \ \ QQ19+3 The delta (can be positive or negative) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ QQ19+4 The updated coordinate after moving by the delta (this \ will be the same as A if moving by the delta overflows) \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ TT180 Contains an RTS \ \ ****************************************************************************** .TT123 STA QQ19+4 \ Store the original coordinate in temporary storage at \ QQ19+4 CLC \ Set A = A + QQ19+3, so A now contains the original ADC QQ19+3 \ coordinate, moved by the delta LDX QQ19+3 \ If the delta is negative, jump to TT124 BMI TT124 BCC TT125 \ If the C flag is clear, then the above addition didn't \ overflow, so jump to TT125 to return the updated value RTS \ Otherwise the C flag is set and the above addition \ overflowed, so do not update the return value .TT124 BCC TT180 \ If the C flag is clear, then because the delta is \ negative, this indicates the addition (which is \ effectively a subtraction) underflowed, so jump to \ TT180 to return from the subroutine without updating \ the return value .TT125 STA QQ19+4 \ Store the updated coordinate in QQ19+4 .TT180 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TT105 \ Type: Subroutine \ Category: Charts \ Summary: Draw crosshairs on the Short-range Chart, with clipping \ \ ------------------------------------------------------------------------------ \ \ Check whether the crosshairs are close enough to the current system to appear \ on the Short-range Chart, and if so, draw them. \ \ ****************************************************************************** .TT105 LDA QQ9 \ Set A = QQ9 - QQ0, the horizontal distance between the SEC \ crosshairs (QQ9) and the current system (QQ0) SBC QQ0 CMP #38 \ If the horizontal distance in A < 38, then the BCC TT179 \ crosshairs are close enough to the current system to \ appear in the Short-range Chart, so jump to TT179 to \ check the vertical distance CMP #230 \ If the horizontal distance in A < -26, then the BCC TT180 \ crosshairs are too far from the current system to \ appear in the Short-range Chart, so jump to TT180 to \ return from the subroutine (as TT180 contains an RTS) .TT179 ASL A \ Set QQ19 = 104 + A * 4 ASL A \ CLC \ 104 is the x-coordinate of the centre of the chart, ADC #104 \ so this sets QQ19 to the screen pixel x-coordinate STA QQ19 \ of the crosshairs LDA QQ10 \ Set A = QQ10 - QQ1, the vertical distance between the SEC \ crosshairs (QQ10) and the current system (QQ1) SBC QQ1 CMP #38 \ If the vertical distance in A is < 38, then the BCC P%+6 \ crosshairs are close enough to the current system to \ appear in the Short-range Chart, so skip the next two \ instructions CMP #220 \ If the horizontal distance in A is < -36, then the BCC TT180 \ crosshairs are too far from the current system to \ appear in the Short-range Chart, so jump to TT180 to \ return from the subroutine (as TT180 contains an RTS) ASL A \ Set QQ19+1 = 90 + A * 2 CLC \ ADC #90 \ 90 is the y-coordinate of the centre of the chart, STA QQ19+1 \ so this sets QQ19+1 to the screen pixel x-coordinate \ of the crosshairs LDA #8 \ Set QQ19+2 to 8 denote crosshairs of size 8 STA QQ19+2 JMP TT15 \ Jump to TT15 to draw crosshairs of size 8 at the \ crosshairs coordinates, returning from the subroutine \ using a tail call \ ****************************************************************************** \ \ Name: TT23 \ Type: Subroutine \ Category: Charts \ Summary: Show the Short-range Chart (red key f5) \ \ ****************************************************************************** .TT23 LDA #128 \ Clear the top part of the screen, draw a border box, JSR TT66 \ and set the current view type in QQ11 to 128 (Short- \ range Chart) LDA #16 \ Send a #SETVDU19 16 command to the I/O processor to JSR DOVDU19 \ switch to the mode 1 palette for the trade view, which \ is yellow (colour 1), magenta (colour 2) and white \ (colour 3) LDA #CYAN \ Send a #SETCOL CYAN command to the I/O processor to JSR DOCOL \ switch to colour 3, which is white in the chart view LDA #7 \ Move the text cursor to column 7 JSR DOXC LDA #190 \ Print recursive token 30 ("SHORT RANGE CHART") and JSR NLIN3 \ draw a horizontal line at pixel row 19 to box in the \ title JSR TT14 \ Call TT14 to draw a circle with crosshairs at the \ current system's galactic coordinates JSR TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10), \ i.e. at the selected system 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 #CYAN \ Send a #SETCOL CYAN command to the I/O processor to JSR DOCOL \ switch to colour 3, which is white in the chart view LDA #0 \ Set A = 0, which we'll use below to zero out the INWK \ workspace STA XX20 \ We're about to start working our way through each of \ the galaxy's systems, so set up a counter in XX20 for \ each system, starting at 0 and looping through to 255 LDX #24 \ First, though, we need to zero out the 25 bytes at \ INWK so we can use them to work out which systems have \ room for a label, so set a counter in X for 25 bytes .EE3 STA INWK,X \ Set the X-th byte of INWK to zero DEX \ Decrement the counter BPL EE3 \ Loop back to EE3 for the next byte until we've zeroed \ all 25 bytes \ We now loop through every single system in the galaxy \ and check the distance from the current system whose \ coordinates are in (QQ0, QQ1). We get the galactic \ coordinates of each system from the system's seeds, \ like this: \ \ x = s1_hi (which is stored in QQ15+3) \ y = s0_hi (which is stored in QQ15+1) \ \ so the following loops through each system in the \ galaxy in turn and calculates the distance between \ (QQ0, QQ1) and (s1_hi, s0_hi) to find the closest one .TT182 LDA QQ15+3 \ Set A = s1_hi - QQ0, the horizontal distance between SEC \ (s1_hi, s0_hi) and (QQ0, QQ1) SBC QQ0 BCS TT184 \ If a borrow didn't occur, i.e. s1_hi >= QQ0, then the \ result is positive, so jump to TT184 and skip the \ following two instructions EOR #&FF \ Otherwise negate the result in A, so A is always ADC #1 \ positive (i.e. A = |s1_hi - QQ0|) .TT184 CMP #20 \ If the horizontal distance in A is >= 20, then this BCS TT187 \ system is too far away from the current system to \ appear in the Short-range Chart, so jump to TT187 to \ move on to the next system LDA QQ15+1 \ Set A = s0_hi - QQ1, the vertical distance between SEC \ (s1_hi, s0_hi) and (QQ0, QQ1) SBC QQ1 BCS TT186 \ If a borrow didn't occur, i.e. s0_hi >= QQ1, then the \ result is positive, so jump to TT186 and skip the \ following two instructions EOR #&FF \ Otherwise negate the result in A, so A is always ADC #1 \ positive (i.e. A = |s0_hi - QQ1|) .TT186 CMP #38 \ If the vertical distance in A is >= 38, then this BCS TT187 \ system is too far away from the current system to \ appear in the Short-range Chart, so jump to TT187 to \ move on to the next system \ This system should be shown on the Short-range Chart, \ so now we need to work out where the label should go, \ and set up the various variables we need to draw the \ system's filled circle on the chart LDA QQ15+3 \ Set A = s1_hi - QQ0, the horizontal distance between SEC \ this system and the current system, where |A| < 20. SBC QQ0 \ Let's call this the x-delta, as it's the horizontal \ difference between the current system at the centre of \ the chart, and this system (and this time we keep the \ sign of A, so it can be negative if it's to the left \ of the chart's centre, or positive if it's to the \ right) ASL A \ Set XX12 = 104 + x-delta * 4 ASL A \ ADC #104 \ 104 is the x-coordinate of the centre of the chart, STA XX12 \ so this sets XX12 to the centre 104 +/- 76, the pixel \ x-coordinate of this system LSR A \ Move the text cursor to column x-delta / 2 + 1 LSR A \ which will be in the range 1-10 LSR A INA JSR DOXC LDA QQ15+1 \ Set A = s0_hi - QQ1, the vertical distance between SEC \ this system and the current system, where |A| < 38. SBC QQ1 \ Let's call this the y-delta, as it's the vertical \ difference between the current system at the centre of \ the chart, and this system (and this time we keep the \ sign of A, so it can be negative if it's above the \ chart's centre, or positive if it's below) ASL A \ Set K4 = 90 + y-delta * 2 ADC #90 \ STA K4 \ 90 is the y-coordinate of the centre of the chart, \ so this sets K4 to the centre 90 +/- 74, the pixel \ y-coordinate of this system LSR A \ Set Y = A >> 3 LSR A \ = K4 div 8 LSR A \ TAY \ So Y now contains the number of the character row \ that contains this system \ Now to see if there is room for this system's label. \ Ideally we would print the system name on the same \ text row as the system, but we only want to print one \ label per row, to prevent overlap, so now we check \ this system's row, and if that's already occupied, \ the row above, and if that's already occupied, the \ row below... and if that's already occupied, we give \ up and don't print a label for this system LDX INWK,Y \ If the value in INWK+Y is 0 (i.e. the text row BEQ EE4 \ containing this system does not already have another \ system's label on it), jump to EE4 to store this \ system's label on this row INY \ If the value in INWK+Y+1 is 0 (i.e. the text row below LDX INWK,Y \ the one containing this system does not already have BEQ EE4 \ another system's label on it), jump to EE4 to store \ this system's label on this row DEY \ If the value in INWK+Y-1 is 0 (i.e. the text row above DEY \ the one containing this system does not already have LDX INWK,Y \ another system's label on it), fall through into to BNE ee1 \ EE4 to store this system's label on this row, \ otherwise jump to ee1 to skip printing a label for \ this system (as there simply isn't room) .EE4 TYA \ Now to print the label, so move the text cursor to row JSR DOYC \ Y (which contains the row where we can print this \ system's label) CPY #3 \ If Y < 3, then the system would clash with the chart BCC TT187 \ title, so jump to TT187 to skip showing the system LDA #&FF \ Store &FF in INWK+Y, to denote that this row is now STA INWK,Y \ occupied so we don't try to print another system's \ label on this row LDA #%10000000 \ Set bit 7 of QQ17 to switch to Sentence Case STA QQ17 JSR cpl \ Call cpl to print out the system name for the seeds \ in QQ15 (which now contains the seeds for the current \ system) .ee1 LDA #0 \ Now to plot the star, so set the high bytes of K, K3 STA K3+1 \ and K4 to 0 STA K4+1 STA K+1 LDA XX12 \ Set the low byte of K3 to XX12, the pixel x-coordinate STA K3 \ of this system LDA QQ15+5 \ Fetch s2_hi for this system from QQ15+5, extract bit 0 AND #1 \ and add 2 to get the size of the star, which we store ADC #2 \ in K. This will be either 2, 3 or 4, depending on the STA K \ value of bit 0, and whether the C flag is set (which \ will vary depending on what happens in the above call \ to cpl). Incidentally, the planet's average radius \ also uses s2_hi, bits 0-3 to be precise, but that \ doesn't mean the two sizes affect each other \ We now have the following: \ \ K(1 0) = radius of star (2, 3 or 4) \ \ K3(1 0) = pixel x-coordinate of system \ \ K4(1 0) = pixel y-coordinate of system \ \ which we can now pass to the SUN routine to draw a \ small "sun" on the Short-range Chart for this system JSR FLFLLS \ Call FLFLLS to reset the LSO block JSR SUN \ Call SUN to plot a sun with radius K at pixel \ coordinate (K3, K4) JSR FLFLLS \ Call FLFLLS to reset the LSO block .TT187 JSR TT20 \ We want to move on to the next system, so call TT20 \ to twist the three 16-bit seeds in QQ15 INC XX20 \ Increment the counter BNE P%+5 \ If X = 0 then we have done all 256 systems, so jump JMP HBFL \ to HBFL to send the contents of the horizontal line \ buffer to the I/O processor for drawing on-screen, \ returning from the subroutine using a tail call JMP TT182 \ Otherwise jump back up to TT182 to process the next \ system \ ****************************************************************************** \ \ Name: TT81 \ Type: Subroutine \ Category: Universe \ Summary: Set the selected system's seeds to those of system 0 \ \ ------------------------------------------------------------------------------ \ \ Copy the three 16-bit seeds for the current galaxy's system 0 (QQ21) into the \ seeds for the selected system (QQ15) - in other words, set the selected \ system's seeds to those of system 0. \ \ ****************************************************************************** .TT81 LDX #5 \ Set up a counter in X to copy six bytes (for three \ 16-bit numbers) LDA QQ21,X \ Copy the X-th byte in QQ21 to the X-th byte in QQ15 STA QQ15,X DEX \ Decrement the counter BPL TT81+2 \ Loop back up to the LDA instruction if we still have \ more bytes to copy RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TT111 \ Type: Subroutine \ Category: Universe \ Summary: Set the current system to the nearest system to a point \ \ ------------------------------------------------------------------------------ \ \ Given a set of galactic coordinates in (QQ9, QQ10), find the nearest system \ to this point in the galaxy, and set this as the currently selected system. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ QQ9 The x-coordinate near which we want to find a system \ \ QQ10 The y-coordinate near which we want to find a system \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ QQ8(1 0) The distance from the current system to the nearest \ system to the original coordinates \ \ QQ9 The x-coordinate of the nearest system to the original \ coordinates \ \ QQ10 The y-coordinate of the nearest system to the original \ coordinates \ \ QQ15 to QQ15+5 The three 16-bit seeds of the nearest system to the \ original coordinates \ \ ZZ The system number of the nearest system \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ TT111-1 Contains an RTS \ \ ****************************************************************************** .TT111 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) \ We now loop through every single system in the galaxy \ and check the distance from (QQ9, QQ10). We get the \ galactic coordinates of each system from the system's \ seeds, like this: \ \ x = s1_hi (which is stored in QQ15+3) \ y = s0_hi (which is stored in QQ15+1) \ \ so the following loops through each system in the \ galaxy in turn and calculates the distance between \ (QQ9, QQ10) and (s1_hi, s0_hi) to find the closest one LDY #127 \ Set Y = T = 127 to hold the shortest distance we've STY T \ found so far, which we initially set to half the \ distance across the galaxy, or 127, as our coordinate \ system ranges from (0,0) to (255, 255) LDA #0 \ Set A = U = 0 to act as a counter for each system in STA U \ the current galaxy, which we start at system 0 and \ loop through to 255, the last system .TT130 LDA QQ15+3 \ Set A = s1_hi - QQ9, the horizontal distance between SEC \ (s1_hi, s0_hi) and (QQ9, QQ10) SBC QQ9 BCS TT132 \ If a borrow didn't occur, i.e. s1_hi >= QQ9, then the \ result is positive, so jump to TT132 and skip the \ following two instructions EOR #&FF \ Otherwise negate the result in A, so A is always ADC #1 \ positive (i.e. A = |s1_hi - QQ9|) .TT132 LSR A \ Set S = A / 2 STA S \ = |s1_hi - QQ9| / 2 LDA QQ15+1 \ Set A = s0_hi - QQ10, the vertical distance between SEC \ (s1_hi, s0_hi) and (QQ9, QQ10) SBC QQ10 BCS TT134 \ If a borrow didn't occur, i.e. s0_hi >= QQ10, then the \ result is positive, so jump to TT134 and skip the \ following two instructions EOR #&FF \ Otherwise negate the result in A, so A is always ADC #1 \ positive (i.e. A = |s0_hi - QQ10|) .TT134 LSR A \ Set A = S + A / 2 CLC \ = |s1_hi - QQ9| / 2 + |s0_hi - QQ10| / 2 ADC S \ \ So A now contains the sum of the horizontal and \ vertical distances, both divided by 2 so the result \ fits into one byte, and although this doesn't contain \ the actual distance between the systems, it's a good \ enough approximation to use for comparing distances CMP T \ If A >= T, then this system's distance is bigger than BCS TT135 \ our "minimum distance so far" stored in T, so it's no \ closer than the systems we have already found, so \ skip to TT135 to move on to the next system STA T \ This system is the closest to (QQ9, QQ10) so far, so \ update T with the new "distance" approximation LDX #5 \ As this system is the closest we have found yet, we \ want to store the system's seeds in case it ends up \ being the closest of all, so we set up a counter in X \ to copy six bytes (for three 16-bit numbers) .TT136 LDA QQ15,X \ Copy the X-th byte in QQ15 to the X-th byte in QQ19, STA QQ19,X \ where QQ15 contains the seeds for the system we just \ found to be the closest so far, and QQ19 is temporary \ storage DEX \ Decrement the counter BPL TT136 \ Loop back to TT136 if we still have more bytes to \ copy LDA U \ Store the system number U in ZZ, so when we are done STA ZZ \ looping through all the candidates, the winner's \ number will be in ZZ .TT135 JSR TT20 \ We want to move on to the next system, so call TT20 \ to twist the three 16-bit seeds in QQ15 INC U \ Increment the system counter in U BNE TT130 \ If U > 0 then we haven't done all 256 systems yet, so \ loop back up to TT130 \ We have now finished checking all the systems in the \ galaxy, and the seeds for the closest system are in \ QQ19, so now we want to copy these seeds to QQ15, \ to set the selected system to this closest system LDX #5 \ So we set up a counter in X to copy six bytes (for \ three 16-bit numbers) .TT137 LDA QQ19,X \ Copy the X-th byte in QQ19 to the X-th byte in QQ15 STA QQ15,X DEX \ Decrement the counter BPL TT137 \ Loop back to TT137 if we still have more bytes to \ copy LDA QQ15+1 \ The y-coordinate of the system described by the seeds STA QQ10 \ in QQ15 is in QQ15+1 (s0_hi), so we copy this to QQ10 \ as this is where we store the selected system's \ y-coordinate LDA QQ15+3 \ The x-coordinate of the system described by the seeds STA QQ9 \ in QQ15 is in QQ15+3 (s1_hi), so we copy this to QQ9 \ as this is where we store the selected system's \ x-coordinate \ We have now found the closest system to (QQ9, QQ10) \ and have set it as the selected system, so now we \ need to work out the distance between the selected \ system and the current system SEC \ Set A = QQ9 - QQ0, the horizontal distance between SBC QQ0 \ the selected system's x-coordinate (QQ9) and the \ current system's x-coordinate (QQ0) BCS TT139 \ If a borrow didn't occur, i.e. QQ9 >= QQ0, then the \ result is positive, so jump to TT139 and skip the \ following two instructions EOR #&FF \ Otherwise negate the result in A, so A is always ADC #1 \ positive (i.e. A = |QQ9 - QQ0|) \ A now contains the difference between the two \ systems' x-coordinates, with the sign removed. We \ will refer to this as the x-delta ("delta" means \ change or difference in maths) .TT139 JSR SQUA2 \ Set (A P) = A * A \ = |QQ9 - QQ0| ^ 2 \ = x_delta ^ 2 STA K+1 \ Store (A P) in K(1 0) LDA P STA K LDA QQ10 \ Set A = QQ10 - QQ1, the vertical distance between the SEC \ selected system's y-coordinate (QQ10) and the current SBC QQ1 \ system's y-coordinate (QQ1) BCS TT141 \ If a borrow didn't occur, i.e. QQ10 >= QQ1, then the \ result is positive, so jump to TT141 and skip the \ following two instructions EOR #&FF \ Otherwise negate the result in A, so A is always ADC #1 \ positive (i.e. A = |QQ10 - QQ1|) .TT141 LSR A \ Set A = A / 2 \ A now contains the difference between the two \ systems' y-coordinates, with the sign removed, and \ halved. We halve the value because the galaxy in \ in Elite is rectangular rather than square, and is \ twice as wide (x-axis) as it is high (y-axis), so to \ get a distance that matches the shape of the \ long-range galaxy chart, we need to halve the \ distance between the vertical y-coordinates. We will \ refer to this as the y-delta JSR SQUA2 \ Set (A P) = A * A \ = (|QQ10 - QQ1| / 2) ^ 2 \ = y_delta ^ 2 \ By this point we have the following results: \ \ K(1 0) = x_delta ^ 2 \ (A P) = y_delta ^ 2 \ \ so to find the distance between the two points, we \ can use Pythagoras - so first we need to add the two \ results together, and then take the square root PHA \ Store the high byte of the y-axis value on the stack, \ so we can use A for another purpose LDA P \ Set Q = P + K, which adds the low bytes of the two CLC \ calculated values ADC K STA Q PLA \ Restore the high byte of the y-axis value from the \ stack into A again ADC K+1 \ Set R = A + K+1, which adds the high bytes of the two STA R \ calculated values, so we now have: \ \ (R Q) = K(1 0) + (A P) \ = (x_delta ^ 2) + (y_delta ^ 2) JSR LL5 \ Set Q = SQRT(R Q), so Q now contains the distance \ between the two systems, in terms of coordinates \ We now store the distance to the selected system * 4 \ in the two-byte location QQ8, by taking (0 Q) and \ shifting it left twice, storing it in QQ8(1 0) LDA Q \ First we shift the low byte left by setting ASL A \ A = Q * 2, with bit 7 of A going into the C flag LDX #0 \ Now we set the high byte in QQ8+1 to 0 and rotate STX QQ8+1 \ the C flag into bit 0 of QQ8+1 ROL QQ8+1 ASL A \ And then we repeat the shift left of (QQ8+1 A) ROL QQ8+1 STA QQ8 \ And store A in the low byte, QQ8, so QQ8(1 0) now \ contains Q * 4. Given that the width of the galaxy is \ 256 in coordinate terms, the width of the galaxy \ would be 1024 in the units we store in QQ8 JMP TT24 \ Call TT24 to calculate system data from the seeds in \ QQ15 and store them in the relevant locations, so our \ new selected system is fully set up, and return from \ the subroutine using a tail call \ ****************************************************************************** \ \ Name: dockEd \ Type: Subroutine \ Category: Flight \ Summary: Print a message to say there is no hyperspacing allowed inside the \ station \ \ ------------------------------------------------------------------------------ \ \ Print "Docked" at the bottom of the screen to indicate we can't hyperspace \ when docked. \ \ ****************************************************************************** .dockEd JSR CLYNS \ Clear the bottom three text rows of the upper screen, \ and move the text cursor to the first cleared row LDA #15 \ Move the text cursor to column 15 JSR DOXC LDA #RED \ Send a #SETCOL RED command to the I/O processor to JSR DOCOL \ switch to colour 2, which is magenta in the trade view \ or red in the chart view LDA #205 \ Print extended token 205 ("DOCKED") and return from JMP DETOK \ the subroutine using a tail call \ ****************************************************************************** \ \ Name: hyp \ Type: Subroutine \ Category: Flight \ Summary: Start the hyperspace process \ \ ------------------------------------------------------------------------------ \ \ Called when "H" or CTRL-H is pressed during flight. Checks the following: \ \ * We are in space \ \ * We are not already in a hyperspace countdown \ \ If CTRL is being held down, we jump to Ghy to engage the galactic hyperdrive, \ otherwise we check that: \ \ * The selected system is not the current system \ \ * We have enough fuel to make the jump \ \ and if all the pre-jump checks are passed, we print the destination on-screen \ and start the countdown. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ TTX111 Used to rejoin this routine from the call to TTX110 \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.hyp \ \LDA QQ12 \ If we are docked (QQ12 = &FF) then jump to dockEd to \BNE dockEd \ print an error message and return from the subroutine \ \ using a tail call (as we can't hyperspace when docked) \ \LDA QQ22+1 \ Fetch QQ22+1, which contains the number that's shown \ \ on-screen during hyperspace countdown \ \BEQ P%+3 \ If it is zero, skip the next instruction \ \RTS \ The count is non-zero, so return from the subroutine \ \LDA #CYAN \ The count is zero, so send a #SETCOL CYAN command to \JSR DOCOL \ the I/O processor to switch to colour 3, which is cyan \ \ in the space view \ \JSR CTRL \ Scan the keyboard to see if CTRL is currently pressed \ \BMI Ghy \ If it is, then the galactic hyperdrive has been \ \ activated, so jump to Ghy to process it \ \LDA QQ11 \ If the current view is 0 (i.e. the space view) then \BEQ TTX110 \ jump to TTX110, which calls TT111 to set the current \ \ system to the nearest system to (QQ9, QQ10), and jumps \ \ back into this routine at TTX111 below \ \AND #%11000000 \ If either bit 6 or 7 of the view type is set - so \BNE P%+3 \ this is either the Short-range or Long-range Chart - \ \ then skip the following instruction \ \RTS \ This is not a chart view, so return from the \ \ subroutine \ \JSR hm \ This is a chart view, so call hm to redraw the chart \ \ crosshairs \ \.TTX111 \ \ \ If we get here then the current view is either the \ \ space view or a chart \ \LDA QQ8 \ If either byte of the distance to the selected system \ORA QQ8+1 \ in QQ8 are zero, skip the next instruction to make a \BNE P%+3 \ copy of the destination seeds in safehouse \ \RTS \ The selected system is the same as the current system, \ \ so return from the subroutine \ \LDX #5 \ We now want to copy those seeds into safehouse, so we \ \ so set a counter in X to copy 6 bytes \ \.sob \ \LDA QQ15,X \ Copy the X-th byte of QQ15 into the X-th byte of \STA safehouse,X \ safehouse \ \DEX \ Decrement the loop counter \ \BPL sob \ Loop back to copy the next byte until we have copied \ \ all six seed bytes \ \LDA #7 \ Move the text cursor to column 7, row 23 (in the \JSR DOXC \ middle of the bottom text row) \LDA #23 \JSR DOYC \ \LDA #0 \ Set QQ17 = 0 to switch to ALL CAPS \STA QQ17 \ \LDA #189 \ Print recursive token 29 ("HYPERSPACE ") \JSR TT27 \ \IF _EXECUTIVE \ \BIT JUMP \ If infinite jump range is configured, then jump down \BMI goTT147+3 \ to IJUMP so we do the jump whatever the distance \ \ENDIF \ \LDA QQ8+1 \ If the high byte of the distance to the selected \BNE goTT147 \ system in QQ8 is > 0, then it is definitely too far to \ \ jump (as our maximum range is 7.0 light years, or a \ \ value of 70 in QQ8(1 0)), so jump to TT147 via goTT147 \ \ to print "RANGE?" and return from the subroutine using \ \ a tail call \ \LDA QQ14 \ Fetch our current fuel level from Q114 into A \ \CMP QQ8 \ If our fuel reserves are greater than or equal to the \BCS P%+5 \ distance to the selected system, then we have enough \ \ fuel for this jump, so skip the following instruction \ \ to start the hyperspace countdown \ \.goTT147 \ \JMP TT147 \ We don't have enough fuel to reach the destination, so \ \ jump to TT147 to print "RANGE?" and return from the \ \ subroutine using a tail call \ \LDA #'-' \ Print a hyphen \JSR TT27 \ \JSR cpl \ Call cpl to print the name of the selected system \ \ \ Fall through into wW to start the hyperspace countdown \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: wW \ Type: Subroutine \ Category: Flight \ Summary: Start a hyperspace countdown \ \ ------------------------------------------------------------------------------ \ \ Start the hyperspace countdown (for both inter-system hyperspace and the \ galactic hyperdrive). \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ wW2 Start the hyperspace countdown, starting the countdown \ from the value in A \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.wW \ \LDA #15 \ The hyperspace countdown starts from 15, so set A to \ \ 15 so we can set the two hyperspace counters \ \.wW2 \ \STA QQ22+1 \ Set the number in QQ22+1 to A, which is the number \ \ that's shown on-screen during the hyperspace countdown \ \STA QQ22 \ Set the number in QQ22 to 15, which is the internal \ \ counter that counts down by 1 each iteration of the \ \ main game loop, and each time it reaches zero, the \ \ on-screen counter gets decremented, and QQ22 gets set \ \ to 5, so setting QQ22 to 15 here makes the first tick \ \ of the hyperspace counter longer than subsequent ticks \ \TAX \ Print the 8-bit number in X (i.e. 15) at text location \JMP ee3 \ (0, 1), padded to 5 digits, so it appears in the top \ \ left corner of the screen, and return from the \ \ subroutine using a tail call \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: TTX110 \ Type: Subroutine \ Category: Flight \ Summary: Set the current system to the nearest system and return to hyp \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.TTX110 \ \ \ This routine is only called from the hyp routine, and \ \ it jumps back into hyp at label TTX111 \ \JSR TT111 \ Call TT111 to set the current system to the nearest \ \ system to (QQ9, QQ10), and put the seeds of the \ \ nearest system into QQ15 to QQ15+5 \ \JMP TTX111 \ Return to TTX111 in the hyp routine \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Ghy \ Type: Subroutine \ Category: Flight \ Summary: Perform a galactic hyperspace jump \ Deep dive: Twisting the system seeds \ Galaxy and system seeds \ \ ------------------------------------------------------------------------------ \ \ Engage the galactic hyperdrive. Called from the hyp routine above if CTRL-H is \ being pressed. \ \ This routine also updates the galaxy seeds to point to the next galaxy. Using \ a galactic hyperdrive rotates each seed byte to the left, rolling each byte \ left within itself like this: \ \ 01234567 -> 12345670 \ \ to get the seeds for the next galaxy. So after 8 galactic jumps, the seeds \ roll round to those of the first galaxy again. \ \ We always arrive in a new galaxy at galactic coordinates (96, 96), and then \ find the nearest system and set that as our location. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ zZ+1 Contains an RTS \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.Ghy \ \LDX GHYP \ Fetch GHYP, which tells us whether we own a galactic \BEQ zZ+1 \ hyperdrive, and if it is zero, which means we don't, \ \ return from the subroutine (as zZ+1 contains an RTS) \ \INX \ We own a galactic hyperdrive, so X is &FF, so this \ \ instruction sets X = 0 \ \\STX QQ8 \ These instructions are commented out in the original \\STX QQ8+1 \ source \ \STX GHYP \ The galactic hyperdrive is a one-use item, so set GHYP \ \ to 0 so we no longer have one fitted \ \STX FIST \ Changing galaxy also clears our criminal record, so \ \ set our legal status in FIST to 0 ("clean") \ \LDA #2 \ Call wW2 with A = 2 to start the hyperspace countdown, \JSR wW2 \ but starting the countdown from 2 \ \LDX #5 \ To move galaxy, we rotate the galaxy's seeds left, so \ \ set a counter in X for the 6 seed bytes \ \INC GCNT \ Increment the current galaxy number in GCNT \ \LDA GCNT \ Set GCNT = GCNT mod 8, so we jump from galaxy 7 back \AND #7 \ to galaxy 0 (shown in-game as going from galaxy 8 back \STA GCNT \ to the starting point in galaxy 1) \ \.G1 \ \LDA QQ21,X \ Load the X-th seed byte into A \ \ASL A \ Set the C flag to bit 7 of the seed \ \ROL QQ21,X \ Rotate the seed in memory, which will add bit 7 back \ \ in as bit 0, so this rolls the seed around on itself \ \DEX \ Decrement the counter \ \BPL G1 \ Loop back for the next seed byte, until we have \ \ rotated them all \ \\JSR DORND \ This instruction is commented out in the original \ \ source, and would set A and X to random numbers, so \ \ perhaps the original plan was to arrive in each new \ \ galaxy in a random place? \ \.zZ \ \LDA #96 \ Set (QQ9, QQ10) to (96, 96), which is where we always \STA QQ9 \ arrive in a new galaxy (the selected system will be \STA QQ10 \ set to the nearest actual system later on) \ \JSR TT110 \ Call TT110 to show the front space view \ \JSR TT111 \ Call TT111 to set the current system to the nearest \ \ system to (QQ9, QQ10), and put the seeds of the \ \ nearest system into QQ15 to QQ15+5 \ \ \ \ This call fixes a bug in the cassette version, where \ \ the galactic hyperdrive will take us to coordinates \ \ (96, 96) in the new galaxy, even if there isn't \ \ actually a system there, so if we jump when we are \ \ low on fuel, it is possible to get stuck in the \ \ middle of nowhere when changing galaxy \ \ \ \ This call sets the current system correctly, so we \ \ always arrive at the nearest system to (96, 96) \ \LDX #5 \ We now want to copy those seeds into safehouse, so we \ \ so set a counter in X to copy 6 bytes \ \.dumdeedum \ \LDA QQ15,X \ Copy the X-th byte of QQ15 into the X-th byte of \STA safehouse,X \ safehouse \ \DEX \ Decrement the loop counter \ \BPL dumdeedum \ Loop back to copy the next byte until we have copied \ \ all six seed bytes \ \LDX #0 \ Set the distance to the selected system in QQ8(1 0) \STX QQ8 \ to 0 \STX QQ8+1 \ \LDA #116 \ Print recursive token 116 (GALACTIC HYPERSPACE ") \JSR MESS \ as an in-flight message \ \ \ Fall through into jmp to set the system to the \ \ current system and return from the subroutine there \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: jmp \ Type: Subroutine \ Category: Universe \ Summary: Set the current system to the selected system \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ (QQ0, QQ1) The galactic coordinates of the new system \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ hy5 Contains an RTS \ \ ****************************************************************************** .jmp LDA QQ9 \ Set the current system's galactic x-coordinate to the STA QQ0 \ x-coordinate of the selected system LDA QQ10 \ Set the current system's galactic y-coordinate to the STA QQ1 \ y-coordinate of the selected system .hy5 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: Player2ee3 \ Type: Subroutine \ Category: Flight \ Summary: Print the hyperspace countdown in the top-left of the screen \ \ ------------------------------------------------------------------------------ \ \ Print the 8-bit number in X at text location (1, 1). Print the number to \ 5 digits, left-padding with spaces for numbers with fewer than 3 digits (so \ numbers < 10000 are right-aligned), with no decimal point. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The number to print \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Player2ee3 LDX player2GameType \ If player 2 is playing survival, they don't need a BNE P%+3 \ score, so return from the subroutine without printing RTS \ the score LDA #RED \ Send a #SETCOL RED command to the I/O processor to JSR DOCOL \ switch to colour 2, which is red in the space view LDY #1 \ Set Y to the number of digits in X (1 to 3), so that's CPX #10 \ the number of digits in the target score BCC qscr1 INY CPX #100 BCC qscr1 INY .qscr1 STY digitCount \ Set digitCount to the number of digits in the target \ score (1 to 3) LDA #13 \ Move the text cursor to row 13, column 25 JSR DOYC LDA #25 JSR DOXC LDY #0 \ Set (Y X) = player2GameType (the target score) LDX player2GameType JSR pr6 \ Call pr6 to print (Y X) to 5 digits LDA #30 \ Move the text cursor to column 30 JSR DOXC LDA #'0' \ Print a trailing 0 for the target score JSR TT27 LDA #29 \ Move the text cursor to column 29 - digitCount SEC SBC digitCount JSR DOXC LDA #'/' \ Print a "/" between the two scores JSR TT27 LDA #24 \ Move the text cursor to column 24 - digitCount SEC SBC digitCount JSR DOXC LDX player2Score \ Set (Y X) to the score to print LDY player2Score+1 BRA pr6 \ Jump to pr6 to print (Y X) to 5 digits \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: ee3 \ Type: Subroutine \ Category: Flight \ Summary: Print the hyperspace countdown in the top-left of the screen \ \ ------------------------------------------------------------------------------ \ \ Print the 8-bit number in X at text location (1, 1). Print the number to \ 5 digits, left-padding with spaces for numbers with fewer than 3 digits (so \ numbers < 10000 are right-aligned), with no decimal point. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The number to print \ \ ****************************************************************************** .ee3 \ --- Mod: Code added for two-player Elite: -----------> LDX player1GameType \ If player 1 is playing survival, they don't need a BNE P%+3 \ score, so return from the subroutine without printing RTS \ the score \ --- End of added code -------------------------------> LDA #RED \ Send a #SETCOL RED command to the I/O processor to JSR DOCOL \ switch to colour 2, which is red in the space view \ --- Mod: Code removed for two-player Elite: ---------> \LDA #1 \ Move the text cursor to column 1 on row 1 \JSR DOXC \JSR DOYC \ \LDY #0 \ Set Y = 0 for the high byte in pr6 \ \ \ Fall through into pr6 to print X to 5 digits, as the \ \ high byte in Y is 0 \ --- And replaced by: --------------------------------> LDY #1 \ Set Y to the number of digits in X (1 to 3), so that's CPX #10 \ the number of digits in the target score BCC pscr1 INY CPX #100 BCC pscr1 INY .pscr1 STY digitCount \ Set digitCount to the number of digits in the target \ score (1 to 3) LDA #1 \ Move the text cursor to row 1, column 25 JSR DOYC LDA #25 JSR DOXC LDY #0 \ Set (Y X) = player1GameType (the target score) LDX player1GameType JSR pr6 \ Call pr6 to print (Y X) to 5 digits LDA #30 \ Move the text cursor to column 30 JSR DOXC LDA #'0' \ Print a trailing 0 for the target score JSR TT27 LDA #29 \ Move the text cursor to column 29 - digitCount SEC SBC digitCount JSR DOXC LDA #'/' \ Print a "/" between the two scores JSR TT27 LDA #24 \ Move the text cursor to column 24 - digitCount SEC SBC digitCount JSR DOXC LDX player1Score \ Set (Y X) to the score to print LDY player1Score+1 \ Fall through into pr6 to print (Y X) to 5 digits \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: pr6 \ Type: Subroutine \ Category: Text \ Summary: Print 16-bit number, left-padded to 5 digits, no point \ \ ------------------------------------------------------------------------------ \ \ Print the 16-bit number in (Y X) to 5 digits, left-padding with spaces for \ numbers with fewer than 3 digits (so numbers < 10000 are right-aligned), \ with no decimal point. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The low byte of the number to print \ \ Y The high byte of the number to print \ \ ****************************************************************************** .pr6 CLC \ Do not display a decimal point when printing \ Fall through into pr5 to print X to 5 digits \ ****************************************************************************** \ \ Name: pr5 \ Type: Subroutine \ Category: Text \ Summary: Print a 16-bit number, left-padded to 5 digits, and optional point \ \ ------------------------------------------------------------------------------ \ \ Print the 16-bit number in (Y X) to 5 digits, left-padding with spaces for \ numbers with fewer than 3 digits (so numbers < 10000 are right-aligned). \ Optionally include a decimal point. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The low byte of the number to print \ \ Y The high byte of the number to print \ \ C flag If set, include a decimal point \ \ ****************************************************************************** .pr5 LDA #5 \ Set the number of digits to print to 5 JMP TT11 \ Call TT11 to print (Y X) to 5 digits and return from \ the subroutine using a tail call \ ****************************************************************************** \ \ Name: TT147 \ Type: Subroutine \ Category: Flight \ Summary: Print an error when a system is out of hyperspace range \ \ ------------------------------------------------------------------------------ \ \ Print "RANGE?" for when the hyperspace distance is too far \ \ ****************************************************************************** .TT147 LDA #202 \ Load A with token 42 ("RANGE") and fall through into \ prq to print it, followed by a question mark \ ****************************************************************************** \ \ Name: prq \ Type: Subroutine \ Category: Text \ Summary: Print a text token followed by a question mark \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The text token to be printed \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ prq+3 Print a question mark \ \ ****************************************************************************** .prq JSR TT27 \ Print the text token in A LDA #'?' \ Print a question mark and return from the JMP TT27 \ subroutine using a tail call \ ****************************************************************************** \ \ Name: TT151 \ Type: Subroutine \ Category: Market \ Summary: Print the name, price and availability of a market item \ Deep dive: Market item prices and availability \ Galaxy and system seeds \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The number of the market item to print, 0-16 (see QQ23 \ for details of item numbers) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ QQ19+1 Byte #1 from the market prices table for this item \ \ QQ24 The item's price / 4 \ \ QQ25 The item's availability \ \ ****************************************************************************** .TT151q \ We jump here from below if we are in witchspace PLA \ Restore the item number from the stack RTS \ Return from the subroutine .TT151 PHA \ Store the item number on the stack and in QQ19+4 STA QQ19+4 ASL A \ Store the item number * 4 in QQ19, so this will act as ASL A \ an index into the market prices table at QQ23 for this STA QQ19 \ item (as there are four bytes per item in the table) LDA MJ \ If we are in witchspace, we can't trade items, so jump BNE TT151q \ up to TT151q to return from the subroutine LDA #1 \ Move the text cursor to column 1, for the item's name JSR DOXC PLA \ Restore the item number ADC #208 \ Print recursive token 48 + A, which will be in the JSR TT27 \ range 48 ("FOOD") to 64 ("ALIEN ITEMS"), so this \ prints the item's name LDA #14 \ Move the text cursor to column 14, for the price JSR DOXC LDX QQ19 \ Fetch byte #1 from the market prices table (units and LDA QQ23+1,X \ economic_factor) for this item and store in QQ19+1 STA QQ19+1 LDA QQ26 \ Fetch the random number for this system visit and AND QQ23+3,X \ AND with byte #3 from the market prices table (mask) \ to give: \ \ A = random AND mask CLC \ Add byte #0 from the market prices table (base_price), ADC QQ23,X \ so we now have: STA QQ24 \ \ A = base_price + (random AND mask) JSR TT152 \ Call TT152 to print the item's unit ("t", "kg" or \ "g"), padded to a width of two characters JSR var \ Call var to set QQ19+3 = economy * |economic_factor| \ (and set the availability of alien items to 0) LDA QQ19+1 \ Fetch the byte #1 that we stored above and jump to BMI TT155 \ TT155 if it is negative (i.e. if the economic_factor \ is negative) LDA QQ24 \ Set A = QQ24 + QQ19+3 ADC QQ19+3 \ \ = base_price + (random AND mask) \ + (economy * |economic_factor|) \ \ which is the result we want, as the economic_factor \ is positive JMP TT156 \ Jump to TT156 to multiply the result by 4 .TT155 LDA QQ24 \ Set A = QQ24 - QQ19+3 SEC \ SBC QQ19+3 \ = base_price + (random AND mask) \ - (economy * |economic_factor|) \ \ which is the result we want, as economic_factor \ is negative .TT156 STA QQ24 \ Store the result in QQ24 and P STA P LDA #0 \ Set A = 0 and call GC2 to calculate (Y X) = (A P) * 4, JSR GC2 \ which is the same as (Y X) = P * 4 because A = 0 SEC \ We now have our final price, * 10, so we can call pr5 JSR pr5 \ to print (Y X) to 5 digits, including a decimal \ point, as the C flag is set LDY QQ19+4 \ We now move on to availability, so fetch the market \ item number that we stored in QQ19+4 at the start LDA #5 \ Set A to 5 so we can print the availability to 5 \ digits (right-padded with spaces) LDX AVL,Y \ Set X to the item's availability, which is given in \ the AVL table STX QQ25 \ Store the availability in QQ25 CLC \ Clear the C flag BEQ TT172 \ If none are available, jump to TT172 to print a tab \ and a "-" JSR pr2+2 \ Otherwise print the 8-bit number in X to 5 digits, \ right-aligned with spaces. This works because we set \ A to 5 above, and we jump into the pr2 routine just \ after the first instruction, which would normally \ set the number of digits to 3 JMP TT152 \ Print the unit ("t", "kg" or "g") for the market item, \ with a following space if required to make it two \ characters long, and return from the subroutine using \ a tail call .TT172 LDA #25 \ Move the text cursor to column 25 JSR DOXC LDA #'-' \ Print a "-" character by jumping to TT162+2, which BNE TT162+2 \ contains JMP TT27 (this BNE is effectively a JMP as A \ will never be zero), and return from the subroutine \ using a tail call \ ****************************************************************************** \ \ Name: TT152 \ Type: Subroutine \ Category: Market \ Summary: Print the unit ("t", "kg" or "g") for a market item \ \ ------------------------------------------------------------------------------ \ \ Print the unit ("t", "kg" or "g") for the market item whose byte #1 from the \ market prices table is in QQ19+1, right-padded with spaces to a width of two \ characters (so that's "t ", "kg" or "g "). \ \ ****************************************************************************** .TT152 LDA QQ19+1 \ Fetch the economic_factor from QQ19+1 AND #96 \ If bits 5 and 6 are both clear, jump to TT160 to BEQ TT160 \ print "t" for tonne, followed by a space, and return \ from the subroutine using a tail call CMP #32 \ If bit 5 is set, jump to TT161 to print "kg" for BEQ TT161 \ kilograms, and return from the subroutine using a tail \ call JSR TT16a \ Otherwise call TT16a to print "g" for grams, and fall \ through into TT162 to print a space and return from \ the subroutine \ ****************************************************************************** \ \ Name: TT162 \ Type: Subroutine \ Category: Text \ Summary: Print a space \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ TT162+2 Jump to TT27 to print the text token in A \ \ ****************************************************************************** .TT162 LDA #' ' \ Load a space character into A JMP TT27 \ Print the text token in A and return from the \ subroutine using a tail call \ ****************************************************************************** \ \ Name: TT160 \ Type: Subroutine \ Category: Market \ Summary: Print "t" (for tonne) and a space \ \ ****************************************************************************** .TT160 LDA #'t' \ Load a "t" character into A JSR TT26 \ Print the character, using TT216 so that it doesn't \ change the character case BCC TT162 \ Jump to TT162 to print a space and return from the \ subroutine using a tail call (this BCC is effectively \ a JMP as the C flag is cleared by TT26) \ ****************************************************************************** \ \ Name: TT161 \ Type: Subroutine \ Category: Market \ Summary: Print "kg" (for kilograms) \ \ ****************************************************************************** .TT161 LDA #'k' \ Load a "k" character into A JSR TT26 \ Print the character, using TT216 so that it doesn't \ change the character case, and fall through into \ TT16a to print a "g" character \ ****************************************************************************** \ \ Name: TT16a \ Type: Subroutine \ Category: Market \ Summary: Print "g" (for grams) \ \ ****************************************************************************** .TT16a LDA #'g' \ Load a "g" character into A JMP TT26 \ Print the character, using TT216 so that it doesn't \ change the character case, and return from the \ subroutine using a tail call \ ****************************************************************************** \ \ Name: TT163 \ Type: Subroutine \ Category: Market \ Summary: Print the headers for the table of market prices \ \ ------------------------------------------------------------------------------ \ \ Print the column headers for the prices table in the Buy Cargo and Market \ Price screens. \ \ ****************************************************************************** .TT163 LDA #17 \ Move the text cursor in XC to column 17 JSR DOXC LDA #255 \ Print recursive token 95 token ("UNIT QUANTITY BNE TT162+2 \ {crlf} PRODUCT UNIT PRICE FOR SALE{crlf}{lf}") by \ jumping to TT162+2, which contains JMP TT27 (this BNE \ is effectively a JMP as A will never be zero), and \ return from the subroutine using a tail call \ ****************************************************************************** \ \ Name: TT167 \ Type: Subroutine \ Category: Market \ Summary: Show the Market Price screen (red key f7) \ \ ****************************************************************************** .TT167 LDA #16 \ Clear the top part of the screen, draw a border box, JSR TRADEMODE \ and set up a printable trading screen with a view type \ in QQ11 of 32 (Market Price screen) LDA #5 \ Move the text cursor to column 5 JSR DOXC LDA #167 \ Print recursive token 7 ("{current system name} MARKET JSR NLIN3 \ PRICES") and draw a horizontal line at pixel row 19 \ to box in the title LDA #3 \ Move the text cursor to row 3 JSR DOYC JSR TT163 \ Print the column headers for the prices table LDA #6 \ Move the text cursor to row 6 JSR DOYC LDA #0 \ We're going to loop through all the available market STA QQ29 \ items, so we set up a counter in QQ29 to denote the \ current item and start it at 0 .TT168 LDX #%10000000 \ Set bit 7 of QQ17 to switch to Sentence Case, with the STX QQ17 \ next letter in capitals JSR TT151 \ Call TT151 to print the item name, market price and \ availability of the current item, and set QQ24 to the \ item's price / 4, QQ25 to the quantity available and \ QQ19+1 to byte #1 from the market prices table for \ this item JSR INCYC \ Move the text cursor down one row INC QQ29 \ Increment QQ29 to point to the next item LDA QQ29 \ If QQ29 >= 17 then jump to TT168 as we have done the CMP #17 \ last item BCC TT168 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: var \ Type: Subroutine \ Category: Market \ Summary: Calculate QQ19+3 = economy * |economic_factor| \ \ ------------------------------------------------------------------------------ \ \ Set QQ19+3 = economy * |economic_factor|, given byte #1 of the market prices \ table for an item. Also sets the availability of alien items to 0. \ \ This routine forms part of the calculations for market item prices (TT151) \ and availability (GVL). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ QQ19+1 Byte #1 of the market prices table for this market item \ (which contains the economic_factor in bits 0-5, and the \ sign of the economic_factor in bit 7) \ \ ****************************************************************************** .var LDA QQ19+1 \ Extract bits 0-5 from QQ19+1 into A, to get the AND #31 \ economic_factor without its sign, in other words: \ \ A = |economic_factor| LDY QQ28 \ Set Y to the economy byte of the current system STA QQ19+2 \ Store A in QQ19+2 CLC \ Clear the C flag so we can do additions below LDA #0 \ Set AVL+16 (availability of alien items) to 0, STA AVL+16 \ setting A to 0 in the process .TT153 \ We now do the multiplication by doing a series of \ additions in a loop, building the result in A. Each \ loop adds QQ19+2 (|economic_factor|) to A, and it \ loops the number of times given by the economy byte; \ in other words, because A starts at 0, this sets: \ \ A = economy * |economic_factor| DEY \ Decrement the economy in Y, exiting the loop when it BMI TT154 \ becomes negative ADC QQ19+2 \ Add QQ19+2 to A JMP TT153 \ Loop back to TT153 to do another addition .TT154 STA QQ19+3 \ Store the result in QQ19+3 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: hyp1 \ Type: Subroutine \ Category: Universe \ Summary: Process a jump to the system closest to (QQ9, QQ10) \ \ ------------------------------------------------------------------------------ \ \ Do a hyperspace jump to the system closest to galactic coordinates \ (QQ9, QQ10), and set up the current system's state to those of the new system. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ (QQ0, QQ1) The galactic coordinates of the new system \ \ QQ2 to QQ2+6 The seeds of the new system \ \ EV Set to 0 \ \ QQ28 The new system's economy \ \ tek The new system's tech level \ \ gov The new system's government \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ hyp1+3 Jump straight to the system at (QQ9, QQ10) without \ first calculating which system is closest. We do this \ if we already know that (QQ9, QQ10) points to a system \ \ ****************************************************************************** .hyp1 JSR TT111 \ Select the system closest to galactic coordinates \ (QQ9, QQ10) JSR jmp \ Set the current system to the selected system LDX #5 \ We now want to copy the seeds for the selected system \ in QQ15 into QQ2, where we store the seeds for the \ current system, so set up a counter in X for copying \ 6 bytes (for three 16-bit seeds) .TT112 LDA safehouse,X \ Copy the X-th byte in safehouse to the X-th byte in STA QQ2,X \ QQ2 DEX \ Decrement the counter BPL TT112 \ Loop back to TT112 if we still have more bytes to \ copy INX \ Set X = 0 (as we ended the above loop with X = &FF) STX EV \ Set EV, the extra vessels spawning counter, to 0, as \ we are entering a new system with no extra vessels \ spawned LDA QQ3 \ Set the current system's economy in QQ28 to the STA QQ28 \ selected system's economy from QQ3 LDA QQ5 \ Set the current system's tech level in tek to the STA tek \ selected system's economy from QQ5 LDA QQ4 \ Set the current system's government in gov to the STA gov \ selected system's government from QQ4 \ Fall through into GVL to calculate the availability of \ market items in the new system \ ****************************************************************************** \ \ Name: GVL \ Type: Subroutine \ Category: Universe \ Summary: Calculate the availability of market items \ Deep dive: Market item prices and availability \ Galaxy and system seeds \ \ ------------------------------------------------------------------------------ \ \ Calculate the availability for each market item and store it in AVL. This is \ called on arrival in a new system. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ hyR Contains an RTS \ \ ****************************************************************************** .GVL JSR DORND \ Set A and X to random numbers STA QQ26 \ Set QQ26 to the random byte that's used in the market \ calculations LDX #0 \ We are now going to loop through the market item STX XX4 \ availability table in AVL, so set a counter in XX4 \ (and X) for the market item number, starting with 0 .hy9 LDA QQ23+1,X \ Fetch byte #1 from the market prices table (units and STA QQ19+1 \ economic_factor) for item number X and store it in \ QQ19+1 JSR var \ Call var to set QQ19+3 = economy * |economic_factor| \ (and set the availability of alien items to 0) LDA QQ23+3,X \ Fetch byte #3 from the market prices table (mask) and AND QQ26 \ AND with the random number for this system visit \ to give: \ \ A = random AND mask CLC \ Add byte #2 from the market prices table ADC QQ23+2,X \ (base_quantity) so we now have: \ \ A = base_quantity + (random AND mask) LDY QQ19+1 \ Fetch the byte #1 that we stored above and jump to BMI TT157 \ TT157 if it is negative (i.e. if the economic_factor \ is negative) SEC \ Set A = A - QQ19+3 SBC QQ19+3 \ \ = base_quantity + (random AND mask) \ - (economy * |economic_factor|) \ \ which is the result we want, as the economic_factor \ is positive JMP TT158 \ Jump to TT158 to skip TT157 .TT157 CLC \ Set A = A + QQ19+3 ADC QQ19+3 \ \ = base_quantity + (random AND mask) \ + (economy * |economic_factor|) \ \ which is the result we want, as the economic_factor \ is negative .TT158 BPL TT159 \ If A < 0, then set A = 0, so we don't have negative LDA #0 \ availability .TT159 LDY XX4 \ Fetch the counter (the market item number) into Y AND #%00111111 \ Take bits 0-5 of A, i.e. A mod 64, and store this as STA AVL,Y \ this item's availability in the Y=th byte of AVL, so \ each item has a maximum availability of 63t INY \ Increment the counter into XX44, Y and A TYA STA XX4 ASL A \ Set X = counter * 4, so that X points to the next ASL A \ item's entry in the four-byte market prices table, TAX \ ready for the next loop CMP #63 \ If A < 63, jump back up to hy9 to set the availability BCC hy9 \ for the next market item .hyR RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: GTHG \ Type: Subroutine \ Category: Universe \ Summary: Spawn a Thargoid ship and a Thargon companion \ Deep dive: Fixing ship positions \ Aggression and hostility in ship tactics \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.GTHG \ \JSR Ze \ Call Ze to initialise INWK to a fairly aggressive \ \ ship (though we increase this below) \ \ \ \ Note that because Ze uses the value of X returned by \ \ DORND, and X contains the value of A returned by the \ \ previous call to DORND, this does not set the new ship \ \ to a totally random location \ \LDA #%11111111 \ Set the AI flag in byte #32 so that the ship has AI, \STA INWK+32 \ an aggression level of 63 out of 63, and E.C.M. \ \LDA #THG \ Call NWSHP to add a new Thargoid ship to our local \JSR NWSHP \ bubble of universe \ \LDA #TGL \ Call NWSHP to add a new Thargon ship to our local \JMP NWSHP \ bubble of universe, and return from the subroutine \ \ using a tail call \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: MJP \ Type: Subroutine \ Category: Flight \ Summary: Process a mis-jump into witchspace \ \ ------------------------------------------------------------------------------ \ \ Process a mis-jump into witchspace (which happens very rarely). Witchspace has \ a strange, almost dust-free aspect to it, and it is populated by aggressive \ Thargoids. Using our escape pod will be fatal, and our position on the \ galactic chart is in-between systems. It is a scary place... \ \ There is a 0.78% chance that this routine is called from TT18 instead of doing \ a normal hyperspace, or we can manually trigger a mis-jump by holding down \ CTRL after first enabling the "author display" configuration option ("X") when \ paused. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ ptg Called when the user manually forces a mis-jump \ \ RTS111 Contains an RTS \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.ptg \ \LSR COK \ Set bit 0 of the competition flags in COK, so that the \SEC \ competition code will include the fact that we have \ROL COK \ manually forced a mis-jump into witchspace \ \.MJP \ \\JSR CATLOD \ This instruction is commented out in the original \ \ source \ \LDA #3 \ Clear the top part of the screen, draw a border box, \JSR TT66 \ and set the current view type in QQ11 to 3 \ \JSR LL164 \ Call LL164 to show the hyperspace tunnel and make the \ \ hyperspace sound for a second time (as we already \ \ called LL164 in TT18) \ \JSR RES2 \ Reset a number of flight variables and workspaces, as \ \ well as setting Y to &FF \ \STY MJ \ Set the mis-jump flag in MJ to &FF, to indicate that \ \ we are now in witchspace \ \IF _EXECUTIVE \ \LDX #4 \ Call TALK with X = 4 to say "Oh shit, it's a mis-jump" \JSR TALK \ using the Watford Electronics Beeb Speech Synthesiser \ \ (if one is fitted and speech has been enabled) \ \ENDIF \ \.MJP1 \ \JSR GTHG \ Call GTHG to spawn a Thargoid ship and a Thargon \ \ companion \ \LDA #3 \ Fetch the number of Thargoid ships from MANY+THG, and \CMP MANY+THG \ if it is less than or equal to 3, loop back to MJP1 to \BCS MJP1 \ spawn another one, until we have four Thargoids \ \STA NOSTM \ Set NOSTM (the maximum number of stardust particles) \ \ to 3, so there are fewer bits of stardust in \ \ witchspace (normal space has a maximum of 18) \ \LDX #0 \ Initialise the front space view \JSR LOOK1 \ \LDA QQ1 \ Fetch the current system's galactic y-coordinate in \EOR #%00011111 \ QQ1 and flip bits 0-5, so we end up somewhere in the \STA QQ1 \ vicinity of our original destination, but above or \ \ below it in the galactic chart \ \.RTS111 \ \RTS \ Return from the subroutine \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: TT18 \ Type: Subroutine \ Category: Flight \ Summary: Try to initiate a jump into hyperspace \ \ ------------------------------------------------------------------------------ \ \ Try to go through hyperspace. Called from TT102 in the main loop when the \ hyperspace countdown has finished. \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.TT18 \ \IF _SNG45 OR _SOURCE_DISC \ \LDA QQ14 \ Subtract the distance to the selected system (in QQ8) \SEC \ from the amount of fuel in our tank (in QQ14) into A \SBC QQ8 \ \ELIF _EXECUTIVE \ \LDA QQ14 \ Subtract the distance to the selected system (in QQ8) \ \BIT JUMP \ If infinite jump range is configured, then jump down \BMI IJUMP \ to IJUMP so we don't subtract any fuel for this jump \ \SEC \ from the amount of fuel in our tank (in QQ14) into A \SBC QQ8 \ \ENDIF \ \BCS P%+4 \ If the subtraction didn't overflow, skip the next \ \ instruction \ \LDA #0 \ The subtraction overflowed, so set A = 0 so we don't \ \ end up with a negative amount of fuel \ \STA QQ14 \ Store the updated fuel amount in QQ14 \ \IF _EXECUTIVE \ \.IJUMP \ \ENDIF \ \LDA QQ11 \ If the current view is not a space view, jump to ee5 \BNE ee5 \ to skip the following \ \JSR TT66 \ Clear the top part of the screen, draw a border box, \ \ and set the current view type in QQ11 to 0 (space \ \ view) \ \JSR LL164 \ Call LL164 to show the hyperspace tunnel and make the \ \ hyperspace sound \ \.ee5 \ \JSR CTRL \ Scan the keyboard to see if CTRL is currently pressed, \ \ returning a negative value in A if it is \ \AND PATG \ If the game is configured to show the author's names \ \ on the start-up screen, then PATG will contain &FF, \ \ otherwise it will be 0 \ \BMI ptg \ By now, A will be negative if we are holding down CTRL \ \ and author names are configured, which is what we have \ \ to do in order to trigger a manual mis-jump, so jump \ \ to ptg to do a mis-jump (ptg not only mis-jumps, but \ \ updates the competition flags, so Acornsoft could tell \ \ from the competition code whether this feature had \ \ been used) \ \JSR DORND \ Set A and X to random numbers \ \CMP #253 \ If A >= 253 (0.78% chance) then jump to MJP to trigger \BCS MJP \ a mis-jump into witchspace \ \\JSR TT111 \ This instruction is commented out in the original \ \ source. It finds the closest system to coordinates \ \ (QQ9, QQ10), but we don't need to do this as the \ \ crosshairs will already be on a system by this point \ \JSR hyp1+3 \ Jump straight to the system at (QQ9, QQ10) without \ \ first calculating which system is closest \ \JSR RES2 \ Reset a number of flight variables and workspaces \ \JSR SOLAR \ Halve our legal status, update the missile indicators, \ \ and set up data blocks and slots for the planet and \ \ sun \ \\JSR CATLOD \ These instructions are commented out in the original \\ \ source \\JSR LOMOD \ \LDA QQ11 \ If the current view in QQ11 is not a space view (0) or \AND #%00111111 \ one of the charts (64 or 128), return from the \BNE RTS111 \ subroutine (as RTS111 contains an RTS) \ \JSR TTX66 \ Otherwise clear the screen and draw a border box \ \LDA QQ11 \ If the current view is one of the charts, jump to \BNE TT114 \ TT114 (from which we jump to the correct routine to \ \ display the chart) \ \INC QQ11 \ This is a space view, so increment QQ11 to 1 \ \ \ Fall through into TT110 to show the front space view \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: TT110 \ Type: Subroutine \ Category: Flight \ Summary: Launch from a station or show the front space view \ \ ------------------------------------------------------------------------------ \ \ Launch the ship (if we are docked), or show the front space view (if we are \ already in space). \ \ Called when red key f0 is pressed while docked (launch), after we arrive in a \ new galaxy, or after a hyperspace if the current view is a space view. \ \ ****************************************************************************** .TT110 LDX QQ12 \ If we are not docked (QQ12 = 0) then jump to NLUNCH BEQ NLUNCH \ to skip the launch tunnel and setup process JSR LAUN \ Show the space station launch tunnel JSR RES2 \ Reset a number of flight variables and workspaces JSR TT111 \ Select the system closest to galactic coordinates \ (QQ9, QQ10) \ --- Mod: Code removed for two-player Elite: ---------> \INC INWK+8 \ Increment z_sign ready for the call to SOS, so the \ \ planet appears at a z_sign of 1 in front of us when \ \ we launch \ \JSR SOS1 \ Call SOS1 to set up the planet's data block and add it \ \ to FRIN, where it will get put in the first slot as \ \ it's the first one to be added to our local bubble of \ \ universe following the call to RES2 above \ \LDA #128 \ For the space station, set z_sign to &80, so it's \STA INWK+8 \ behind us (&80 is negative) \ \INC INWK+7 \ And increment z_hi, so it's only just behind us \ \JSR NWSPS \ Add a new space station to our local bubble of \ \ universe \ \LDA #12 \ Set our launch speed in DELTA to 12 \STA DELTA \ \JSR BAD \ Call BAD to work out how much illegal contraband we \ \ are carrying in our hold (A is up to 40 for a \ \ standard hold crammed with contraband, up to 70 for \ \ an extended cargo hold full of narcotics and slaves) \ \ORA FIST \ OR the value in A with our legal status in FIST to \ \ get a new value that is at least as high as both \ \ values, to reflect the fact that launching with a \ \ hold full of contraband can only make matters worse \ \STA FIST \ Update our legal status with the new value \ \LDA #255 \ Set the view type in QQ11 to 255 \STA QQ11 \ \JSR HFS1 \ Call HFS1 to draw 8 concentric rings to remove the \ \ launch tunnel that we drew above \ --- And replaced by: --------------------------------> JSR SOLAR \ Halve our legal status, update the missile indicators, \ and set up data blocks and slots for the planet and \ sun JSR ZINF \ Call ZINF to reset the INWK ship workspace STA INWK+22 \ Fix the sign of sidev so the ship the same orientation \ as player 1, otherwise rotations won't work \ \ In other words, we set the orientation vectors as \ follows: \ \ sidev = (-1, 0, 0) \ roofv = ( 0, 1, 0) \ nosev = ( 0, 0, -1) \ \ So player 2's ship points backwards, in the opposite \ direction to player 1's ship LDA #1 \ Set z_hi = -1, so player 2's ship is behind player 1 STA INWK+7 \ and they start out back to back, like a proper LDA #%10000000 \ gunfight STA INWK+8 LDA player2ECM \ Set player 2's AI flag to the configured value, which AND #%00000001 \ will either be 0 or %11111110, and set bit 0 if player ORA player2INWK32 \ 2 has E.C.M. STA INWK+32 LDA #%00000100 \ Set bit 2 of the NEWB flags for player 2 to make them STA NEWB \ hostile by default \ \ This means that any missiles launched by player 2 \ inherit this value and will therefore attack player 1, \ and if player 2 is set to the AI Pilot then they will \ be hostile LDA player2ShipType \ Spawn a ship for player 2 in slot 2 JSR NWSHP LDA INWK+34 \ Calculate the address of the ship heap for player 1's PHA \ ship in player 2's view, which is &2000 below the ship SEC \ heap for slot #2 SBC #&20 STA INWK+34 STA player1Heap+1 \ Store the heap address in player1Heap(1 0) for future LDA INWK+33 \ reference STA player1Heap LDY #2 \ Set the Y2 coordinate of the laser line in the ship LDA #255 \ line heap to 255 so there is no laser line for player STA (INWK+33),Y \ 1's ship in player 2's view PLA \ Restore the address of the original ship heap STA INWK+34 STZ player1INWK31 \ Reset byte #31 for player 1's ship in player 2's view, \ so it is not visible on the scanner or the screen STZ player1INWK31+1 \ Do the same for both missiles STZ player1INWK31+2 \ --- End of replacement ------------------------------> .NLUNCH LDX #0 \ Set QQ12 to 0 to indicate we are not docked STX QQ12 \ --- Mod: Code added for two-player Elite: -----------> STZ drawPlayerView \ Redraw player 1's view LDX #0 \ Set player 1's view to the front view (as we only get \ here by pressing f0) \ --- End of added code -------------------------------> JMP LOOK1 \ Jump to LOOK1 to switch to the front view (X = 0), \ returning from the subroutine using a tail call \ ****************************************************************************** \ \ Name: TT114 \ Type: Subroutine \ Category: Charts \ Summary: Display either the Long-range or Short-range Chart \ \ ------------------------------------------------------------------------------ \ \ Display either the Long-range or Short-range Chart, depending on the current \ view setting. Called from TT18 once we know the current view is one of the \ charts. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The current view, loaded from QQ11 \ \ ****************************************************************************** .TT114 BMI TT115 \ If bit 7 of the current view is set (i.e. the view is \ the Short-range Chart, 128), skip to TT115 below to \ jump to TT23 to display the chart JMP TT22 \ Otherwise the current view is the Long-range Chart, so \ jump to TT22 to display it .TT115 JMP TT23 \ Jump to TT23 to display the Short-range Chart \ ****************************************************************************** \ \ Name: LCASH \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Subtract an amount of cash from the cash pot \ \ ------------------------------------------------------------------------------ \ \ Subtract (Y X) cash from the cash pot in CASH, but only if there is enough \ cash in the pot. As CASH is a four-byte number, this calculates: \ \ CASH(0 1 2 3) = CASH(0 1 2 3) - (0 0 Y X) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag If set, there was enough cash to do the subtraction \ \ If clear, there was not enough cash to do the \ subtraction \ \ ****************************************************************************** .LCASH STX T1 \ Subtract the least significant bytes: LDA CASH+3 \ SEC \ CASH+3 = CASH+3 - X SBC T1 STA CASH+3 STY T1 \ Then the second most significant bytes: LDA CASH+2 \ SBC T1 \ CASH+2 = CASH+2 - Y STA CASH+2 LDA CASH+1 \ Then the third most significant bytes (which are 0): SBC #0 \ STA CASH+1 \ CASH+1 = CASH+1 - 0 LDA CASH \ And finally the most significant bytes (which are 0): SBC #0 \ STA CASH \ CASH = CASH - 0 BCS TT113 \ If the C flag is set then the subtraction didn't \ underflow, so the value in CASH is correct and we can \ jump to TT113 to return from the subroutine with the \ C flag set to indicate success (as TT113 contains an \ RTS) \ Otherwise we didn't have enough cash in CASH to \ subtract (Y X) from it, so fall through into \ MCASH to reverse the sum and restore the original \ value in CASH, and returning with the C flag clear \ ****************************************************************************** \ \ Name: MCASH \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Add an amount of cash to the cash pot \ \ ------------------------------------------------------------------------------ \ \ Add (Y X) cash to the cash pot in CASH. As CASH is a four-byte number, this \ calculates: \ \ CASH(0 1 2 3) = CASH(0 1 2 3) + (Y X) \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ TT113 Contains an RTS \ \ ****************************************************************************** .MCASH TXA \ Add the least significant bytes: CLC \ ADC CASH+3 \ CASH+3 = CASH+3 + X STA CASH+3 TYA \ Then the second most significant bytes: ADC CASH+2 \ STA CASH+2 \ CASH+2 = CASH+2 + Y LDA CASH+1 \ Then the third most significant bytes (which are 0): ADC #0 \ STA CASH+1 \ CASH+1 = CASH+1 + 0 LDA CASH \ And finally the most significant bytes (which are 0): ADC #0 \ STA CASH \ CASH = CASH + 0 CLC \ Clear the C flag, so if the above was done following \ a failed LCASH call, the C flag correctly indicates \ failure .TT113 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: GCASH \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (Y X) = P * Q * 4 \ \ ------------------------------------------------------------------------------ \ \ Calculate the following multiplication of unsigned 8-bit numbers: \ \ (Y X) = P * Q * 4 \ \ ****************************************************************************** .GCASH JSR MULTU \ Call MULTU to calculate (A P) = P * Q \ ****************************************************************************** \ \ Name: GC2 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (Y X) = (A P) * 4 \ \ ------------------------------------------------------------------------------ \ \ Calculate the following multiplication of unsigned 16-bit numbers: \ \ (Y X) = (A P) * 4 \ \ ****************************************************************************** .GC2 ASL P \ Set (A P) = (A P) * 4 ROL A ASL P ROL A TAY \ Set (Y X) = (A P) LDX P RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: RDLI \ Type: Variable \ Category: Loader \ Summary: The OS command string for running the flight code in file D.CODE \ in the disc version of Elite \ Deep dive: Swapping between the docked and flight code \ \ ------------------------------------------------------------------------------ \ \ This command is not used in the 6502 Second Processor version of Elite; it is \ left over from the BBC Micro disc version. \ \ ****************************************************************************** .RDLI EQUS "R.D.CODE" \ This is short for "*RUN D.CODE" EQUB 13 \ ****************************************************************************** \ \ Name: EQSHP \ Type: Subroutine \ Category: Equipment \ Summary: Show the Equip Ship screen (red key f3) \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ err Beep, pause and go to the docking bay (i.e. show the \ Status Mode screen) \ \ pres Given an item number A with the item name in recursive \ token Y, show an error to say that the item is already \ present, refund the cost of the item, and then beep and \ exit to the docking bay (i.e. show the Status Mode \ screen) \ \ ****************************************************************************** .bay JMP BAY \ Go to the docking bay (i.e. show the Status Mode \ screen) .EQSHP LDA #32 \ Clear the top part of the screen, draw a border box, JSR TRADEMODE \ and set up a printable trading screen with a view type \ in QQ11 of 32 (Equip Ship screen) LDA #12 \ Move the text cursor to column 12 JSR DOXC LDA #207 \ Print recursive token 47 ("EQUIP") followed by a space JSR spc LDA #185 \ Print recursive token 25 ("SHIP") and draw a JSR NLIN3 \ horizontal line at pixel row 19 to box in the title LDA #%10000000 \ Set bit 7 of QQ17 to switch to Sentence Case, with the STA QQ17 \ next letter in capitals JSR INCYC \ Move the text cursor down one line LDA tek \ Fetch the tech level of the current system from tek CLC \ and add 3 (the tech level is stored as 0-14, so A is ADC #3 \ now set to between 3 and 17) CMP #12 \ If A >= 12 then set A = 14, so A is now set to between BCC P%+4 \ 3 and 14 LDA #14 STA Q \ Set QQ25 = A (so QQ25 is in the range 3-14 and STA QQ25 \ represents the number of the most advanced item INC Q \ available in this system, which we can pass to gnum \ below when asking which item we want to buy) \ \ Set Q = A + 1 (so Q is in the range 4-15 and contains \ QQ25 + 1, i.e. the highest item number on sale + 1) LDA #70 \ Set A = 70 - QQ14, where QQ14 contains the current SEC \ fuel in light years * 10, so this leaves the amount SBC QQ14 \ of fuel we need to fill 'er up (in light years * 10) ASL A \ The price of fuel is always 2 Cr per light year, so we STA PRXS \ double A and store it in PRXS, as the first price in \ the price list (which is reserved for fuel), and \ because the table contains prices as price * 10, it's \ in the right format (so tank containing 7.0 light \ years of fuel would be 14.0 Cr, or a PRXS value of \ 140) LDX #1 \ We are now going to work our way through the equipment \ price list at PRXS, printing out the equipment that is \ available at this station, so set a counter in X, \ starting at 1, to hold the number of the current item \ plus 1 (so the item number in X loops through 1-13) .EQL1 STX XX13 \ Store the current item number + 1 in XX13 JSR TT67 \ Print a newline LDX XX13 \ Print the current item number + 1 to 3 digits, left- CLC \ padding with spaces, and with no decimal point, so the JSR pr2 \ items are numbered from 1 JSR TT162 \ Print a space LDA XX13 \ Print recursive token 104 + XX13, which will be in the CLC \ range 105 ("FUEL") to 116 ("GALACTIC HYPERSPACE ") ADC #104 \ so this prints the current item's name JSR TT27 LDA XX13 \ Call prx-3 to set (Y X) to the price of the item with JSR prx-3 \ number XX13 - 1 (as XX13 contains the item number + 1) SEC \ Set the C flag so we will print a decimal point when \ we print the price LDA #25 \ Move the text cursor to column 25 JSR DOXC LDA #6 \ Print the number in (Y X) to 6 digits, left-padding JSR TT11 \ with spaces and including a decimal point, which will \ be the correct price for this item as (Y X) contains \ the price * 10, so the trailing zero will go after the \ decimal point (i.e. 5250 will be printed as 525.0) LDX XX13 \ Increment the current item number in XX13 INX CPX Q \ If X < Q, loop back up to print the next item on the BCC EQL1 \ list of equipment available at this station JSR CLYNS \ Clear the bottom three text rows of the upper screen, \ and move the text cursor to the first cleared row LDA #127 \ Print recursive token 127 ("ITEM") followed by a JSR prq \ question mark JSR gnum \ Call gnum to get a number from the keyboard, which \ will be the number of the item we want to purchase, \ returning the number entered in A and R, and setting \ the C flag if the number is bigger than the highest \ item number in QQ25 BEQ bay \ If no number was entered, jump up to bay to go to the \ docking bay (i.e. show the Status Mode screen) BCS bay \ If the number entered was too big, jump up to bay to \ go to the docking bay (i.e. show the Status Mode \ screen) SBC #0 \ Set A to the number entered - 1 (because the C flag is \ clear), which will be the actual item number we want \ to buy PHA \ Store A on the stack so we can restore it after the \ following call to DOXC LDA #2 \ Move the text cursor to column 2 JSR DOXC JSR INCYC \ Move the text cursor down one line PLA \ Restore A from the stack PHA \ While preserving the value in A, call eq to subtract JSR eq \ the price of the item we want to buy (which is in A) PLA \ from our cash pot, but only if we have enough cash in \ the pot. If we don't have enough cash, exit to the \ docking bay (i.e. show the Status Mode screen) BNE et0 \ If A is not 0 (i.e. the item we've just bought is not \ fuel), skip to et0 LDX #70 \ Set the current fuel level * 10 in QQ14 to 70, or 7.0 STX QQ14 \ light years (a full tank) .et0 CMP #1 \ If A is not 1 (i.e. the item we've just bought is not BNE et1 \ a missile), skip to et1 LDX NOMSL \ Fetch the current number of missiles from NOMSL into X INX \ Increment X to the new number of missiles LDY #124 \ Set Y to recursive token 124 ("ALL") CPX #5 \ If buying this missile would give us 5 missiles, this BCS pres \ is more than the maximum of 4 missiles that we can \ fit, so jump to pres to show the error "All Present", \ beep and exit to the docking bay (i.e. show the Status \ Mode screen) STX NOMSL \ Otherwise update the number of missiles in NOMSL JSR msblob \ Reset the dashboard's missile indicators so none of \ them are targeted LDA #1 \ Set A to 1 as the call to msblob will have overwritten \ the original value, and we still need it set \ correctly so we can continue through the conditional \ statements for all the other equipment .et1 LDY #107 \ Set Y to recursive token 107 ("LARGE CARGO{sentence \ case} BAY") CMP #2 \ If A is not 2 (i.e. the item we've just bought is not BNE et2 \ a large cargo bay), skip to et2 LDX #37 \ If our current cargo capacity in CRGO is 37, then we CPX CRGO \ already have a large cargo bay fitted, so jump to pres BEQ pres \ to show the error "Large Cargo Bay Present", beep and \ exit to the docking bay (i.e. show the Status Mode \ screen) STX CRGO \ Otherwise we just scored ourselves a large cargo bay, \ so update our current cargo capacity in CRGO to 37 .et2 CMP #3 \ If A is not 3 (i.e. the item we've just bought is not BNE et3 \ an E.C.M. system), skip to et3 INY \ Increment Y to recursive token 108 ("E.C.M.SYSTEM") LDX ECM \ If we already have an E.C.M. fitted (i.e. ECM is BNE pres \ non-zero), jump to pres to show the error "E.C.M. \ System Present", beep and exit to the docking bay \ (i.e. show the Status Mode screen) DEC ECM \ Otherwise we just took delivery of a brand new E.C.M. \ system, so set ECM to &FF (as ECM was 0 before the DEC \ instruction) .et3 CMP #4 \ If A is not 4 (i.e. the item we've just bought is not BNE et4 \ an extra pulse laser), skip to et4 JSR qv \ Print a menu listing the four views, with a "View ?" \ prompt, and ask for a view number, which is returned \ in X (which now contains 0-3) LDA #POW \ Call refund with A set to the power of the new pulse JSR refund \ laser to install the new laser and process a refund if \ we already have a laser fitted to this view LDA #4 \ Set A to 4 as we just overwrote the original value, \ and we still need it set correctly so we can continue \ through the conditional statements for all the other \ equipment .et4 CMP #5 \ If A is not 5 (i.e. the item we've just bought is not BNE et5 \ an extra beam laser), skip to et5 JSR qv \ Print a menu listing the four views, with a "View ?" \ prompt, and ask for a view number, which is returned \ in X (which now contains 0-3) LDA #POW+128 \ Call refund with A set to the power of the new beam JSR refund \ laser to install the new laser and process a refund if \ we already have a laser fitted to this view .et5 LDY #111 \ Set Y to recursive token 111 ("FUEL SCOOPS") CMP #6 \ If A is not 6 (i.e. the item we've just bought is not BNE et6 \ a fuel scoop), skip to et6 LDX BST \ If we already have fuel scoops fitted (i.e. BST is BEQ ed9 \ zero), jump to ed9, otherwise fall through into pres \ to show the error "Fuel Scoops Present", beep and \ exit to the docking bay (i.e. show the Status Mode \ screen) .pres \ If we get here we need to show an error to say that \ the item whose name is in recursive token Y is already \ present, and then process a refund for the cost of \ item number A STY K \ Store the item's name in K JSR prx \ Call prx to set (Y X) to the price of equipment item \ number A JSR MCASH \ Add (Y X) cash to the cash pot in CASH, as the station \ already took the money for this item in the JSR eq \ instruction above, but we can't fit the item, so need \ our money back LDA K \ Print the recursive token in K (the item's name) JSR spc \ followed by a space LDA #31 \ Print recursive token 145 ("PRESENT") JSR TT27 .err JSR dn2 \ Call dn2 to make a short, high beep and delay for 1 \ second JMP BAY \ Jump to BAY to go to the docking bay (i.e. show the \ Status Mode screen) .ed9 DEC BST \ We just bought a shiny new fuel scoop, so set BST to \ &FF (as BST was 0 before the jump to ed9 above) .et6 INY \ Increment Y to recursive token 112 ("E.C.M.SYSTEM") CMP #7 \ If A is not 7 (i.e. the item we've just bought is not BNE et7 \ an escape pod), skip to et7 LDX ESCP \ If we already have an escape pod fitted (i.e. ESCP is BNE pres \ non-zero), jump to pres to show the error "Escape Pod \ Present", beep and exit to the docking bay (i.e. show \ the Status Mode screen) DEC ESCP \ Otherwise we just bought an escape pod, so set ESCP \ to &FF (as ESCP was 0 before the DEC instruction) .et7 INY \ Increment Y to recursive token 113 ("ENERGY BOMB") CMP #8 \ If A is not 8 (i.e. the item we've just bought is not BNE et8 \ an energy bomb), skip to et8 LDX BOMB \ If we already have an energy bomb fitted (i.e. BOMB BNE pres \ is non-zero), jump to pres to show the error "Energy \ Bomb Present", beep and exit to the docking bay (i.e. \ show the Status Mode screen) LDX #&7F \ Otherwise we just bought an energy bomb, so set BOMB STX BOMB \ to &7F .et8 INY \ Increment Y to recursive token 114 ("ENERGY UNIT") CMP #9 \ If A is not 9 (i.e. the item we've just bought is not BNE etA \ an energy unit), skip to etA LDX ENGY \ If we already have an energy unit fitted (i.e. ENGY is BNE pres \ non-zero), jump to pres to show the error "Energy Unit \ Present", beep and exit to the docking bay (i.e. show \ the Status Mode screen) INC ENGY \ Otherwise we just picked up an energy unit, so set \ ENGY to 1 (as ENGY was 0 before the INC instruction) .etA INY \ Increment Y to recursive token 115 ("DOCKING \ COMPUTERS") CMP #10 \ If A is not 10 (i.e. the item we've just bought is not BNE etB \ a docking computer), skip to etB LDX DKCMP \ If we already have a docking computer fitted (i.e. BNE pres \ DKCMP is non-zero), jump to pres to show the error \ "Docking Computer Present", beep and exit to the \ docking bay (i.e. show the Status Mode screen) DEC DKCMP \ Otherwise we just got hold of a docking computer, so \ set DKCMP to &FF (as DKCMP was 0 before the DEC \ instruction) .etB INY \ Increment Y to recursive token 116 ("GALACTIC \ HYPERSPACE ") CMP #11 \ If A is not 11 (i.e. the item we've just bought is not BNE et9 \ a galactic hyperdrive), skip to et9 LDX GHYP \ If we already have a galactic hyperdrive fitted (i.e. BNE pres \ GHYP is non-zero), jump to pres to show the error \ "Galactic Hyperspace Present", beep and exit to the \ docking bay (i.e. show the Status Mode screen) DEC GHYP \ Otherwise we just splashed out on a galactic \ hyperdrive, so set GHYP to &FF (as GHYP was 0 before \ the DEC instruction) .et9 INY \ Increment Y to recursive token 117 ("MILITARY LASER") CMP #12 \ If A is not 12 (i.e. the item we've just bought is not BNE et10 \ a military laser), skip to et10 JSR qv \ Print a menu listing the four views, with a "View ?" \ prompt, and ask for a view number, which is returned \ in X (which now contains 0-3) LDA #Armlas \ Call refund with A set to the power of the new JSR refund \ military laser to install the new laser and process a \ refund if we already have a laser fitted to this view .et10 INY \ Increment Y to recursive token 118 ("MINING LASER") CMP #13 \ If A is not 13 (i.e. the item we've just bought is not BNE et11 \ a mining laser), skip to et11 JSR qv \ Print a menu listing the four views, with a "View ?" \ prompt, and ask for a view number, which is returned \ in X (which now contains 0-3) LDA #Mlas \ Call refund with A set to the power of the new mining JSR refund \ laser to install the new laser and process a refund if \ we already have a laser fitted to this view .et11 JSR dn \ We are done buying equipment, so print the amount of \ cash left in the cash pot, then make a short, high \ beep to confirm the purchase, and delay for 1 second JMP EQSHP \ Jump back up to EQSHP to show the Equip Ship screen \ again and see if we can't track down another bargain \ ****************************************************************************** \ \ Name: dn \ Type: Subroutine \ Category: Market \ Summary: Print the amount of money we have left in the cash pot, then make \ a short, high beep and delay for 1 second \ \ ****************************************************************************** .dn JSR TT162 \ Print a space LDA #119 \ Print recursive token 119 ("CASH:{cash} CR{crlf}") JSR spc \ followed by a space \ Fall through into dn2 to make a beep and delay for \ 1 second before returning from the subroutine \ ****************************************************************************** \ \ Name: dn2 \ Type: Subroutine \ Category: Text \ Summary: Make a short, high beep and delay for 1 second \ \ ****************************************************************************** .dn2 JSR BEEP \ Call the BEEP subroutine to make a short, high beep LDY #50 \ Wait for 50/50 of a second (1 second) and return JMP DELAY \ from the subroutine using a tail call \ ****************************************************************************** \ \ Name: eq \ Type: Subroutine \ Category: Equipment \ Summary: Subtract the price of equipment from the cash pot \ \ ------------------------------------------------------------------------------ \ \ If we have enough cash, subtract the price of a specified piece of equipment \ from our cash pot and return from the subroutine. If we don't have enough \ cash, exit to the docking bay (i.e. show the Status Mode screen). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The item number of the piece of equipment (0-11) as \ shown in the table at PRXS \ \ ****************************************************************************** .eq JSR prx \ Call prx to set (Y X) to the price of equipment item \ number A JSR LCASH \ Subtract (Y X) cash from the cash pot, but only if \ we have enough cash BCS c \ If the C flag is set then we did have enough cash for \ the transaction, so jump to c to return from the \ subroutine (as c contains an RTS) LDA #197 \ Otherwise we don't have enough cash to buy this piece JSR prq \ of equipment, so print recursive token 37 ("CASH") \ followed by a question mark JMP err \ Jump to err to beep, pause and go to the docking bay \ (i.e. show the Status Mode screen) \ ****************************************************************************** \ \ Name: prx \ Type: Subroutine \ Category: Equipment \ Summary: Return the price of a piece of equipment \ \ ------------------------------------------------------------------------------ \ \ This routine returns the price of equipment as listed in the table at PRXS. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The item number of the piece of equipment (0-13) as \ shown in the table at PRXS \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ (Y X) The item price in Cr * 10 (Y = high byte, X = low byte) \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ prx-3 Return the price of the item with number A - 1 \ \ c Contains an RTS \ \ ****************************************************************************** SEC \ Decrement A (for when this routine is called via SBC #1 \ prx-3) .prx ASL A \ Set Y = A * 2, so it can act as an index into the TAY \ PRXS table, which has two bytes per entry LDX PRXS,Y \ Fetch the low byte of the price into X LDA PRXS+1,Y \ Fetch the high byte of the price into A and transfer TAY \ it to X, so the price is now in (Y X) .c RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: qv \ Type: Subroutine \ Category: Equipment \ Summary: Print a menu of the four space views, for buying lasers \ \ ------------------------------------------------------------------------------ \ \ Print a menu in the bottom-middle of the screen, at row 16, column 12, that \ lists the four available space views, like this: \ \ 0 Front \ 1 Rear \ 2 Left \ 3 Right \ \ Also print a "View ?" prompt and ask for a view number. The menu is shown \ when we choose to buy a new laser in the Equip Ship screen. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X The chosen view number (0-3) \ \ ****************************************************************************** .qv LDA tek \ If the current system's tech level is less than 8, CMP #8 \ skip the next two instructions, otherwise we clear the BCC P%+7 \ screen to prevent the view menu from clashing with the \ longer equipment menu available in higher tech systems LDA #32 \ Clear the top part of the screen, draw a border box, JSR TT66 \ and set the current view type in QQ11 to 32 (Equip \ Ship screen) LDA #16 \ Move the text cursor to row 16, and at the same time TAY \ set Y to a counter going from 16 to 19 in the loop JSR DOYC \ below .qv1 LDA #12 \ Move the text cursor to column 12 JSR DOXC TYA \ Transfer the counter value from Y to A CLC \ Print ASCII character "0" - 16 + A, so as A goes from ADC #'0'-16 \ 16 to 19, this prints "0" through "3" followed by a JSR spc \ space LDA YC \ Print recursive text token 80 + YC, so as YC goes from CLC \ 16 to 19, this prints "FRONT", "REAR", "LEFT" and ADC #80 \ "RIGHT" JSR TT27 JSR INCYC \ Move the text cursor down a row, and increment the \ counter in YC at the same time LDY YC \ Update Y with the incremented counter in YC CPY #20 \ If Y < 20 then loop back up to qv1 to print the next BCC qv1 \ view in the menu JSR CLYNS \ Clear the bottom three text rows of the upper screen, \ and move the text cursor to the first cleared row .qv2 LDA #175 \ Print recursive text token 15 ("VIEW ") followed by JSR prq \ a question mark JSR TT217 \ Scan the keyboard until a key is pressed, and return \ the key's ASCII code in A (and X) SEC \ Subtract ASCII "0" from the key pressed, to leave the SBC #'0' \ numeric value of the key in A (if it was a number key) CMP #4 \ If the number entered in A < 4, then it is a valid BCC qv3 \ view number, so jump down to qv3 as we are done JSR CLYNS \ Otherwise we didn't get a valid view number, so clear \ the bottom three text rows of the upper screen, and \ move the text cursor to column 1 on row 21 JMP qv2 \ Jump back to qv2 to try again .qv3 TAX \ We have a valid view number, so transfer it to X RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: hm \ Type: Subroutine \ Category: Charts \ Summary: Select the closest system and redraw the chart crosshairs \ \ ------------------------------------------------------------------------------ \ \ Set the system closest to galactic coordinates (QQ9, QQ10) as the selected \ system, redraw the crosshairs on the chart accordingly (if they are being \ shown), and if this is not the space view, clear the bottom three text rows of \ the screen. \ \ ****************************************************************************** .hm JSR TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10), \ which will erase the crosshairs currently there JSR TT111 \ Select the system closest to galactic coordinates \ (QQ9, QQ10) JSR TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10), \ which will draw the crosshairs at our current home \ system JMP CLYNS \ Clear the bottom three text rows of the upper screen, \ move the text cursor to the first cleared row, and \ return from the subroutine using a tail call \ ****************************************************************************** \ \ Name: refund \ Type: Subroutine \ Category: Equipment \ Summary: Install a new laser, processing a refund if applicable \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The power of the new laser to be fitted \ \ X The view number for fitting the new laser (0-3) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A A is preserved \ \ X X is preserved \ \ ****************************************************************************** \.ref2 \ These instructions are commented out in the original \ \ source, but they would jump to pres in the EQSHP \LDY #187 \ routine with Y = 187, which would show the error: \JMP pres \ "LASER PRESENT" (this code was part of the refund \ \ bug in the BBC Micro disc version of Elite, which \Belgium \ is why it is commented out) \ \ There is also a comment in the original source - the \ solitary word "Belgium" \ \ This is presumably a reference to the Hitchhiker's \ Guide to the Galaxy, which says that Belgium is the \ galaxy's most unspeakably rude word, so this no doubt \ reflects the authors' strong feelings on the refund \ bug .refund STA T1 \ Store A in T1 so we can retrieve it later LDA LASER,X \ If there is no laser in view X (i.e. the laser power BEQ ref3 \ is zero), jump to ref3 to skip the refund code \CMP T1 \ These instructions are commented out in the original \BEQ ref2 \ source, but they would jump to ref2 above if we were \ trying to replace a laser with one of the same type \ (this code was part of the refund bug in the BBC Micro \ disc version of Elite, which is why it is commented \ out) LDY #4 \ If the current laser has power #POW (pulse laser), CMP #POW \ jump to ref1 with Y = 4 (the item number of a pulse BEQ ref1 \ laser in the table at PRXS) LDY #5 \ If the current laser has power #POW+128 (beam laser), CMP #POW+128 \ jump to ref1 with Y = 5 (the item number of a beam BEQ ref1 \ laser in the table at PRXS) LDY #12 \ If the current laser has power #Armlas (military CMP #Armlas \ laser), jump to ref1 with Y = 12 (the item number of a BEQ ref1 \ military laser in the table at PRXS) LDY #13 \ Otherwise this is a mining laser, so fall through into \ ref1 with Y = 13 (the item number of a mining laser in \ the table at PRXS) .ref1 \ We now want to refund the laser of type Y that we are \ exchanging for the new laser STX ZZ \ Store the view number in ZZ so we can retrieve it \ later TYA \ Copy the laser type to be refunded from Y to A JSR prx \ Call prx to set (Y X) to the price of equipment item \ number A JSR MCASH \ Call MCASH to add (Y X) to the cash pot LDX ZZ \ Retrieve the view number from ZZ .ref3 \ Finally, we install the new laser LDA T1 \ Retrieve the new laser's power from T1 into A STA LASER,X \ Set the laser view to the new laser's power RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PRXS \ Type: Variable \ Category: Equipment \ Summary: Equipment prices \ \ ------------------------------------------------------------------------------ \ \ Equipment prices are stored as 10 * the actual value, so we can support prices \ with fractions of credits (0.1 Cr). This is used for the price of fuel only. \ \ ****************************************************************************** .PRXS EQUW 1 \ 0 Fuel, calculated in EQSHP 140.0 Cr (full tank) EQUW 300 \ 1 Missile 30.0 Cr EQUW 4000 \ 2 Large Cargo Bay 400.0 Cr EQUW 6000 \ 3 E.C.M. System 600.0 Cr EQUW 4000 \ 4 Extra Pulse Lasers 400.0 Cr EQUW 10000 \ 5 Extra Beam Lasers 1000.0 Cr EQUW 5250 \ 6 Fuel Scoops 525.0 Cr EQUW 10000 \ 7 Escape Pod 1000.0 Cr EQUW 9000 \ 8 Energy Bomb 900.0 Cr EQUW 15000 \ 9 Energy Unit 1500.0 Cr EQUW 10000 \ 10 Docking Computer 1000.0 Cr EQUW 50000 \ 11 Galactic Hyperspace 5000.0 Cr EQUW 60000 \ 12 Extra Military Lasers 6000.0 Cr EQUW 8000 \ 13 Extra Mining Lasers 800.0 Cr \ ****************************************************************************** \ \ Save ELTD.bin \ \ ****************************************************************************** PRINT "ELITE D" PRINT "Assembled at ", ~CODE_D% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_D%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_D% PRINT "S.ELTD ", ~CODE_D%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_D% SAVE "3-assembled-output/ELTD.bin", CODE_D%, P%, LOAD% \ ****************************************************************************** \ \ ELITE E FILE \ \ Produces the binary file ELTE.bin that gets loaded by elite-bcfs.asm. \ \ ****************************************************************************** CODE_E% = P% LOAD_E% = LOAD% + P% - CODE% \ ****************************************************************************** \ \ Name: cpl \ Type: Subroutine \ Category: Universe \ Summary: Print the selected system name \ Deep dive: Generating system names \ Galaxy and system seeds \ \ ------------------------------------------------------------------------------ \ \ Print control code 3 (the selected system name, i.e. the one in the crosshairs \ in the Short-range Chart). \ \ ****************************************************************************** .cpl LDX #5 \ First we need to back up the seeds in QQ15, so set up \ a counter in X to cover three 16-bit seeds (i.e. \ 6 bytes) .TT53 LDA QQ15,X \ Copy byte X from QQ15 to QQ19 STA QQ19,X DEX \ Decrement the loop counter BPL TT53 \ Loop back for the next byte to back up LDY #3 \ Step 1: Now that the seeds are backed up, we can \ start the name-generation process. We will either \ need to loop three or four times, so for now set \ up a counter in Y to loop four times BIT QQ15 \ Check bit 6 of s0_lo, which is stored in QQ15 BVS P%+3 \ If bit 6 is set then skip over the next instruction DEY \ Bit 6 is clear, so we only want to loop three times, \ so decrement the loop counter in Y STY T \ Store the loop counter in T .TT55 LDA QQ15+5 \ Step 2: Load s2_hi, which is stored in QQ15+5, and AND #%00011111 \ extract bits 0-4 by AND'ing with %11111 BEQ P%+7 \ If all those bits are zero, then skip the following \ two instructions to go to step 3 ORA #%10000000 \ We now have a number in the range 1-31, which we can \ easily convert into a two-letter token, but first we \ need to add 128 (or set bit 7) to get a range of \ 129-159 JSR TT27 \ Print the two-letter token in A JSR TT54 \ Step 3: twist the seeds in QQ15 DEC T \ Decrement the loop counter BPL TT55 \ Loop back for the next two letters LDX #5 \ We have printed the system name, so we can now \ restore the seeds we backed up earlier. Set up a \ counter in X to cover three 16-bit seeds (i.e. 6 \ bytes) .TT56 LDA QQ19,X \ Copy byte X from QQ19 to QQ15 STA QQ15,X DEX \ Decrement the loop counter BPL TT56 \ Loop back for the next byte to restore RTS \ Once all the seeds are restored, return from the \ subroutine \ ****************************************************************************** \ \ Name: cmn \ Type: Subroutine \ Category: Status \ Summary: Print the commander's name \ \ ------------------------------------------------------------------------------ \ \ Print control code 4 (the commander's name). \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ cmn-1 Contains an RTS \ \ ****************************************************************************** .cmn LDY #0 \ Set up a counter in Y, starting from 0 .QUL4 LDA NAME,Y \ The commander's name is stored at NAME, so load the \ Y-th character from NAME CMP #13 \ If we have reached the end of the name, return from BEQ ypl-1 \ the subroutine (ypl-1 points to the RTS below) JSR TT26 \ Print the character we just loaded INY \ Increment the loop counter BNE QUL4 \ Loop back for the next character RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: ypl \ Type: Subroutine \ Category: Universe \ Summary: Print the current system name \ \ ------------------------------------------------------------------------------ \ \ Print control code 2 (the current system name). \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ ypl-1 Contains an RTS \ \ ****************************************************************************** .ypl BIT MJ \ Check the mis-jump flag at MJ, and if bit 7 is set BMI ypl16 \ then we are in witchspace, and witchspace doesn't have \ a system name, so jump to ypl16 to return from the \ subroutine JSR TT62 \ Call TT62 below to swap the three 16-bit seeds in \ QQ2 and QQ15 (before the swap, QQ2 contains the seeds \ for the current system, while QQ15 contains the seeds \ for the selected system) JSR cpl \ Call cpl to print out the system name for the seeds \ in QQ15 (which now contains the seeds for the current \ system) \ Now we fall through into the TT62 subroutine, which \ will swap QQ2 and QQ15 once again, so everything goes \ back into the right place, and the RTS at the end of \ TT62 will return from the subroutine .TT62 LDX #5 \ Set up a counter in X for the three 16-bit seeds we \ want to swap (i.e. 6 bytes) .TT78 LDA QQ15,X \ Swap byte X between QQ2 and QQ15 LDY QQ2,X STA QQ2,X STY QQ15,X DEX \ Decrement the loop counter BPL TT78 \ Loop back for the next byte to swap .ypl16 RTS \ Once all bytes are swapped, return from the \ subroutine \ ****************************************************************************** \ \ Name: tal \ Type: Subroutine \ Category: Universe \ Summary: Print the current galaxy number \ \ ------------------------------------------------------------------------------ \ \ Print control code 1 (the current galaxy number, right-aligned to width 3). \ \ ****************************************************************************** .tal CLC \ We don't want to print the galaxy number with a \ decimal point, so clear the C flag for pr2 to take as \ an argument LDX GCNT \ Load the current galaxy number from GCNT into X INX \ Add 1 to the galaxy number, as the galaxy numbers \ are 0-7 internally, but we want to display them as \ galaxy 1 through 8 JMP pr2 \ Jump to pr2, which prints the number in X to a width \ of 3 figures, left-padding with spaces to a width of \ 3, and return from the subroutine using a tail call \ ****************************************************************************** \ \ Name: fwl \ Type: Subroutine \ Category: Status \ Summary: Print fuel and cash levels \ \ ------------------------------------------------------------------------------ \ \ Print control code 5 ("FUEL: ", fuel level, " LIGHT YEARS", newline, "CASH:", \ control code 0). \ \ ****************************************************************************** .fwl LDA #105 \ Print recursive token 105 ("FUEL") followed by a JSR TT68 \ colon LDX QQ14 \ Load the current fuel level from QQ14 SEC \ We want to print the fuel level with a decimal point, \ so set the C flag for pr2 to take as an argument JSR pr2 \ Call pr2, which prints the number in X to a width of \ 3 figures (i.e. in the format x.x, which will always \ be exactly 3 characters as the maximum fuel is 7.0) LDA #195 \ Print recursive token 35 ("LIGHT YEARS") followed by JSR plf \ a newline .PCASH LDA #119 \ Print recursive token 119 ("CASH:" then control code BNE TT27 \ 0, which prints cash levels, then " CR" and newline) \ ****************************************************************************** \ \ Name: csh \ Type: Subroutine \ Category: Status \ Summary: Print the current amount of cash \ \ ------------------------------------------------------------------------------ \ \ Print control code 0 (the current amount of cash, right-aligned to width 9, \ followed by " CR" and a newline). \ \ ****************************************************************************** .csh LDX #3 \ We are going to use the BPRNT routine to print out \ the current amount of cash, which is stored as a \ 32-bit number at location CASH. BPRNT prints out \ the 32-bit number stored in K, so before we call \ BPRNT, we need to copy the four bytes from CASH into \ K, so first we set up a counter in X for the 4 bytes .pc1 LDA CASH,X \ Copy byte X from CASH to K STA K,X DEX \ Decrement the loop counter BPL pc1 \ Loop back for the next byte to copy LDA #9 \ We want to print the cash amount using up to 9 digits STA U \ (including the decimal point), so store this in U \ for BRPNT to take as an argument SEC \ We want to print the cash amount with a decimal point, \ so set the C flag for BRPNT to take as an argument JSR BPRNT \ Print the amount of cash to 9 digits with a decimal \ point LDA #226 \ Print recursive token 66 (" CR") followed by a \ newline by falling through into plf \ ****************************************************************************** \ \ Name: plf \ Type: Subroutine \ Category: Text \ Summary: Print a text token followed by a newline \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The text token to be printed \ \ ****************************************************************************** .plf JSR TT27 \ Print the text token in A JMP TT67 \ Jump to TT67 to print a newline and return from the \ subroutine using a tail call \ ****************************************************************************** \ \ Name: TT68 \ Type: Subroutine \ Category: Text \ Summary: Print a text token followed by a colon \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The text token to be printed \ \ ****************************************************************************** .TT68 JSR TT27 \ Print the text token in A and fall through into TT73 \ to print a colon \ ****************************************************************************** \ \ Name: TT73 \ Type: Subroutine \ Category: Text \ Summary: Print a colon \ \ ****************************************************************************** .TT73 LDA #':' \ Set A to ASCII ":" and fall through into TT27 to \ actually print the colon \ ****************************************************************************** \ \ Name: TT27 \ Type: Subroutine \ Category: Text \ Summary: Print a text token \ Deep dive: Printing text tokens \ \ ------------------------------------------------------------------------------ \ \ Print a text token (i.e. a character, control code, two-letter token or \ recursive token). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The text token to be printed \ \ ****************************************************************************** .TT27 TAX \ Copy the token number from A to X. We can then keep \ decrementing X and testing it against zero, while \ keeping the original token number intact in A; this \ effectively implements a switch statement on the \ value of the token BEQ csh \ If token = 0, this is control code 0 (current amount \ of cash and newline), so jump to csh to print the \ amount of cash and return from the subroutine using \ a tail call BMI TT43 \ If token > 127, this is either a two-letter token \ (128-159) or a recursive token (160-255), so jump \ to TT43 to process tokens DEX \ If token = 1, this is control code 1 (current galaxy BEQ tal \ number), so jump to tal to print the galaxy number and \ return from the subroutine using a tail call DEX \ If token = 2, this is control code 2 (current system BEQ ypl \ name), so jump to ypl to print the current system name \ and return from the subroutine using a tail call DEX \ If token > 3, skip the following instruction BNE P%+5 JMP cpl \ This token is control code 3 (selected system name) \ so jump to cpl to print the selected system name \ and return from the subroutine using a tail call DEX \ If token = 4, this is control code 4 (commander BEQ cmn \ name), so jump to cmn to print the commander name \ and return from the subroutine using a tail call DEX \ If token = 5, this is control code 5 (fuel, newline, BEQ fwl \ cash, newline), so jump to fwl to print the fuel level \ and return from the subroutine using a tail call DEX \ If token > 6, skip the following three instructions BNE P%+7 LDA #%10000000 \ This token is control code 6 (switch to Sentence STA QQ17 \ Case), so set bit 7 of QQ17 to switch to Sentence Case RTS \ and return from the subroutine as we are done DEX \ If token > 8, skip the following two instructions DEX BNE P%+5 STX QQ17 \ This token is control code 8 (switch to ALL CAPS), so RTS \ set QQ17 to 0 to switch to ALL CAPS and return from \ the subroutine as we are done DEX \ If token = 9, this is control code 9 (tab to column BEQ crlf \ 21 and print a colon), so jump to crlf CMP #96 \ By this point, token is either 7, or in 10-127. BCS ex \ Check token number in A and if token >= 96, then the \ token is in 96-127, which is a recursive token, so \ jump to ex, which prints recursive tokens in this \ range (i.e. where the recursive token number is \ correct and doesn't need correcting) CMP #14 \ If token < 14, skip the following two instructions BCC P%+6 CMP #32 \ If token < 32, then this means token is in 14-31, so BCC qw \ this is a recursive token that needs 114 adding to it \ to get the recursive token number, so jump to qw \ which will do this \ By this point, token is either 7 (beep) or in 10-13 \ (line feeds and carriage returns), or in 32-95 \ (ASCII letters, numbers and punctuation) LDX QQ17 \ Fetch QQ17, which controls letter case, into X BEQ TT74 \ If QQ17 = 0, then ALL CAPS is set, so jump to TT74 \ to print this character as is (i.e. as a capital) BMI TT41 \ If QQ17 has bit 7 set, then we are using Sentence \ Case, so jump to TT41, which will print the \ character in upper or lower case, depending on \ whether this is the first letter in a word BIT QQ17 \ If we get here, QQ17 is not 0 and bit 7 is clear, so BVS TT46 \ either it is bit 6 that is set, or some other flag in \ QQ17 is set (bits 0-5). So check whether bit 6 is set. \ If it is, then ALL CAPS has been set (as bit 7 is \ clear) but bit 6 is still indicating that the next \ character should be printed in lower case, so we need \ to fix this. We do this with a jump to TT46, which \ will print this character in upper case and clear bit \ 6, so the flags are consistent with ALL CAPS going \ forward \ If we get here, some other flag is set in QQ17 (one \ of bits 0-5 is set), which shouldn't happen in this \ version of Elite. If this were the case, then we \ would fall through into TT42 to print in lower case, \ which is how printing all words in lower case could \ be supported (by setting QQ17 to 1, say) \ ****************************************************************************** \ \ Name: TT42 \ Type: Subroutine \ Category: Text \ Summary: Print a letter in lower case \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The character to be printed. Can be one of the \ following: \ \ * 7 (beep) \ \ * 10-13 (line feeds and carriage returns) \ \ * 32-95 (ASCII capital letters, numbers and \ punctuation) \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ TT44 Jumps to TT26 to print the character in A (used to \ enable us to use a branch instruction to jump to TT26) \ \ ****************************************************************************** .TT42 CMP #'A' \ If A < ASCII "A", then this is punctuation, so jump BCC TT44 \ to TT26 (via TT44) to print the character as is, as \ we don't care about the character's case CMP #'Z'+1 \ If A >= (ASCII "Z" + 1), then this is also BCS TT44 \ punctuation, so jump to TT26 (via TT44) to print the \ character as is, as we don't care about the \ character's case ADC #32 \ Add 32 to the character, to convert it from upper to \ lower case .TT44 JMP TT26 \ Print the character in A \ ****************************************************************************** \ \ Name: TT41 \ Type: Subroutine \ Category: Text \ Summary: Print a letter according to Sentence Case \ \ ------------------------------------------------------------------------------ \ \ The rules for printing in Sentence Case are as follows: \ \ * If QQ17 bit 6 is set, print lower case (via TT45) \ \ * If QQ17 bit 6 is clear, then: \ \ * If character is punctuation, just print it \ \ * If character is a letter, set QQ17 bit 6 and print letter as a capital \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The character to be printed. Can be one of the \ following: \ \ * 7 (beep) \ \ * 10-13 (line feeds and carriage returns) \ \ * 32-95 (ASCII capital letters, numbers and \ punctuation) \ \ X Contains the current value of QQ17 \ \ QQ17 Bit 7 is set \ \ ****************************************************************************** .TT41 \ If we get here, then QQ17 has bit 7 set, so we are in \ Sentence Case BIT QQ17 \ If QQ17 also has bit 6 set, jump to TT45 to print BVS TT45 \ this character in lower case \ If we get here, then QQ17 has bit 6 clear and bit 7 \ set, so we are in Sentence Case and we need to print \ the next letter in upper case CMP #'A' \ If A < ASCII "A", then this is punctuation, so jump BCC TT74 \ to TT26 (via TT44) to print the character as is, as \ we don't care about the character's case PHA \ Otherwise this is a letter, so store the token number TXA \ Set bit 6 in QQ17 (X contains the current QQ17) ORA #%1000000 \ so the next letter after this one is printed in lower STA QQ17 \ case PLA \ Restore the token number into A BNE TT44 \ Jump to TT26 (via TT44) to print the character in A \ (this BNE is effectively a JMP as A will never be \ zero) \ ****************************************************************************** \ \ Name: qw \ Type: Subroutine \ Category: Text \ Summary: Print a recursive token in the range 128-145 \ \ ------------------------------------------------------------------------------ \ \ Print a recursive token where the token number is in 128-145 (so the value \ passed to TT27 is in the range 14-31). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A A value from 128-145, which refers to a recursive token \ in the range 14-31 \ \ ****************************************************************************** .qw ADC #114 \ This is a recursive token in the range 0-95, so add BNE ex \ 114 to the argument to get the token number 128-145 \ and jump to ex to print it \ ****************************************************************************** \ \ Name: crlf \ Type: Subroutine \ Category: Text \ Summary: Tab to column 21 and print a colon \ \ ------------------------------------------------------------------------------ \ \ Print control code 9 (tab to column 21 and print a colon). The subroutine \ name is pretty misleading, as it doesn't have anything to do with carriage \ returns or line feeds. \ \ ****************************************************************************** .crlf LDA #21 \ Set the X-column in XC to 21 JSR DOXC JMP TT73 \ Jump to TT73, which prints a colon (this BNE is \ effectively a JMP as A will never be zero) \ ****************************************************************************** \ \ Name: TT45 \ Type: Subroutine \ Category: Text \ Summary: Print a letter in lower case \ \ ------------------------------------------------------------------------------ \ \ This routine prints a letter in lower case. Specifically: \ \ * If QQ17 = 255, abort printing this character as printing is disabled \ \ * If this is a letter then print in lower case \ \ * Otherwise this is punctuation, so clear bit 6 in QQ17 and print \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The character to be printed. Can be one of the \ following: \ \ * 7 (beep) \ \ * 10-13 (line feeds and carriage returns) \ \ * 32-95 (ASCII capital letters, numbers and \ punctuation) \ \ X Contains the current value of QQ17 \ \ QQ17 Bits 6 and 7 are set \ \ ****************************************************************************** .TT45 \ If we get here, then QQ17 has bit 6 and 7 set, so we \ are in Sentence Case and we need to print the next \ letter in lower case CPX #255 \ If QQ17 = 255 then printing is disabled, so return BEQ TT48 \ from the subroutine (as TT48 contains an RTS) CMP #'A' \ If A >= ASCII "A", then jump to TT42, which will BCS TT42 \ print the letter in lowercase \ Otherwise this is not a letter, it's punctuation, so \ this is effectively a word break. We therefore fall \ through to TT46 to print the character and set QQ17 \ to ensure the next word starts with a capital letter \ ****************************************************************************** \ \ Name: TT46 \ Type: Subroutine \ Category: Text \ Summary: Print a character and switch to capitals \ \ ------------------------------------------------------------------------------ \ \ Print a character and clear bit 6 in QQ17, so that the next letter that gets \ printed after this will start with a capital letter. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The character to be printed. Can be one of the \ following: \ \ * 7 (beep) \ \ * 10-13 (line feeds and carriage returns) \ \ * 32-95 (ASCII capital letters, numbers and \ punctuation) \ \ X Contains the current value of QQ17 \ \ QQ17 Bits 6 and 7 are set \ \ ****************************************************************************** .TT46 PHA \ Store the token number TXA \ Clear bit 6 in QQ17 (X contains the current QQ17) so AND #%10111111 \ the next letter after this one is printed in upper STA QQ17 \ case PLA \ Restore the token number into A \ Now fall through into TT74 to print the character \ ****************************************************************************** \ \ Name: TT74 \ Type: Subroutine \ Category: Text \ Summary: Print a character \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The character to be printed \ \ ****************************************************************************** .TT74 JMP TT26 \ Print the character in A \ ****************************************************************************** \ \ Name: TT43 \ Type: Subroutine \ Category: Text \ Summary: Print a two-letter token or recursive token 0-95 \ \ ------------------------------------------------------------------------------ \ \ Print a two-letter token, or a recursive token where the token number is in \ 0-95 (so the value passed to TT27 is in the range 160-255). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A One of the following: \ \ * 128-159 (two-letter token) \ \ * 160-255 (the argument to TT27 that refers to a \ recursive token in the range 0-95) \ \ ****************************************************************************** .TT43 CMP #160 \ If token >= 160, then this is a recursive token, so BCS TT47 \ jump to TT47 below to process it AND #127 \ This is a two-letter token with number 128-159. The ASL A \ set of two-letter tokens is stored in a lookup table \ at QQ16, with each token taking up two bytes, so to \ convert this into the token's position in the table, \ we subtract 128 (or just clear bit 7) and multiply \ by 2 (or shift left) TAY \ Transfer the token's position into Y so we can look \ up the token using absolute indexed mode LDA QQ16,Y \ Get the first letter of the token and print it JSR TT27 LDA QQ16+1,Y \ Get the second letter of the token CMP #'?' \ If the second letter of the token is a question mark BEQ TT48 \ then this is a one-letter token, so just return from \ the subroutine without printing (as TT48 contains an \ RTS) JMP TT27 \ Print the second letter and return from the \ subroutine .TT47 SBC #160 \ This is a recursive token in the range 160-255, so \ subtract 160 from the argument to get the token \ number 0-95 and fall through into ex to print it \ ****************************************************************************** \ \ Name: ex \ Type: Subroutine \ Category: Text \ Summary: Print a recursive token \ Deep dive: Printing text tokens \ \ ------------------------------------------------------------------------------ \ \ This routine works its way through the recursive text tokens that are stored \ in tokenised form in the table at QQ18, and when it finds token number A, \ it prints it. Tokens are null-terminated in memory and fill three pages, \ but there is no lookup table as that would consume too much memory, so the \ only way to find the correct token is to start at the beginning and look \ through the table byte by byte, counting tokens as we go until we are in the \ right place. This approach might not be terribly speed efficient, but it is \ certainly memory-efficient. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The recursive token to be printed, in the range 0-148 \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ TT48 Contains an RTS \ \ ****************************************************************************** .ex TAX \ Copy the token number into X LDA #LO(QQ18) \ Set V(1 0) to point to the recursive token table at STA V \ location QQ18 LDA #HI(QQ18) STA V+1 LDY #0 \ Set a counter Y to point to the character offset \ as we scan through the table TXA \ Copy the token number back into A, so both A and X \ now contain the token number we want to print BEQ TT50 \ If the token number we want is 0, then we have \ already found the token we are looking for, so jump \ to TT50, otherwise start working our way through the \ null-terminated token table until we find the X-th \ token .TT51 LDA (V),Y \ Fetch the Y-th character from the token table page \ we are currently scanning BEQ TT49 \ If the character is null, we've reached the end of \ this token, so jump to TT49 INY \ Increment character pointer and loop back around for BNE TT51 \ the next character in this token, assuming Y hasn't \ yet wrapped around to 0 INC V+1 \ If it has wrapped round to 0, we have just crossed BNE TT51 \ into a new page, so increment V+1 so that V points \ to the start of the new page .TT49 INY \ Increment the character pointer BNE TT59 \ If Y hasn't just wrapped around to 0, skip the next \ instruction INC V+1 \ We have just crossed into a new page, so increment \ V+1 so that V points to the start of the new page .TT59 DEX \ We have just reached a new token, so decrement the \ token number we are looking for BNE TT51 \ Assuming we haven't yet reached the token number in \ X, look back up to keep fetching characters .TT50 \ We have now reached the correct token in the token \ table, with Y pointing to the start of the token as \ an offset within the page pointed to by V, so let's \ print the recursive token. Because recursive tokens \ can contain other recursive tokens, we need to store \ our current state on the stack, so we can retrieve \ it after printing each character in this token TYA \ Store the offset in Y on the stack PHA LDA V+1 \ Store the high byte of V (the page containing the PHA \ token we have found) on the stack, so the stack now \ contains the address of the start of this token LDA (V),Y \ Load the character at offset Y in the token table, \ which is the next character of this token that we \ want to print EOR #RE \ Tokens are stored in memory having been EOR'd with the \ value of RE - which is 35 for all versions of Elite \ except for NES, where RE is 62 - so we repeat the \ EOR to get the actual character to print JSR TT27 \ Print the text token in A, which could be a letter, \ number, control code, two-letter token or another \ recursive token PLA \ Restore the high byte of V (the page containing the STA V+1 \ token we have found) into V+1 PLA \ Restore the offset into Y TAY INY \ Increment Y to point to the next character in the \ token we are printing BNE P%+4 \ If Y is zero then we have just crossed into a new INC V+1 \ page, so increment V+1 so that V points to the start \ of the new page LDA (V),Y \ Load the next character we want to print into A BNE TT50 \ If this is not the null character at the end of the \ token, jump back up to TT50 to print the next \ character, otherwise we are done printing .TT48 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: DOEXP \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw an exploding ship \ Deep dive: Drawing explosion clouds \ Generating random numbers \ \ ****************************************************************************** .EX2 LDA INWK+31 \ Set bits 5 and 7 of the ship's byte #31 to denote that ORA #%10100000 \ the ship is exploding and has been killed STA INWK+31 RTS \ Return from the subroutine .DOEXP LDA INWK+31 \ If bit 6 of the ship's byte #31 is clear, then the AND #%01000000 \ ship is not already exploding so there is no existing BEQ P%+5 \ explosion cloud to remove, so skip the following \ instruction JSR PTCLS \ Call PTCLS to remove the existing cloud by drawing it \ again LDA INWK+6 \ Set T = z_lo STA T LDA INWK+7 \ Set A = z_hi, so (A T) = z CMP #32 \ If z_hi < 32, skip the next two instructions BCC P%+6 LDA #&FE \ Set A = 254 and jump to yy (this BNE is effectively a BNE yy \ JMP, as A is never zero) ASL T \ Shift (A T) left twice ROL A ASL T ROL A SEC \ And then shift A left once more, inserting a 1 into ROL A \ bit 0 \ Overall, the above multiplies A by 8 and makes sure it \ is at least 1, to leave a one-byte distance in A. We \ can use this as the distance for our cloud, to ensure \ that the explosion cloud is visible even for ships \ that blow up a long way away .yy STA Q \ Store the distance to the explosion in Q LDY #1 \ Fetch byte #1 of the ship line heap, which contains LDA (XX19),Y \ the cloud counter ADC #4 \ Add 4 to the cloud counter, so it ticks onwards every \ we redraw it BCS EX2 \ If the addition overflowed, jump up to EX2 to update \ the explosion flags and return from the subroutine STA (XX19),Y \ Store the updated cloud counter in byte #1 of the ship \ line heap JSR DVID4 \ Calculate the following: \ \ (P R) = 256 * A / Q \ = 256 * cloud counter / distance \ \ We are going to use this as our cloud size, so the \ further away the cloud, the smaller it is, and as the \ cloud counter ticks onward, the cloud expands LDA P \ Set A = P, so we now have: \ \ (A R) = 256 * cloud counter / distance CMP #&1C \ If A < 28, skip the next two instructions BCC P%+6 LDA #&FE \ Set A = 254 and skip the following (this BNE is BNE LABEL_1 \ effectively a JMP as A is never zero) ASL R \ Shift (A R) left three times to multiply by 8 ROL A ASL R ROL A ASL R ROL A \ Overall, the above multiplies (A R) by 8 to leave a \ one-byte cloud size in A, given by the following: \ \ A = 8 * cloud counter / distance .LABEL_1 \ In the 6502 Second Processor version, the LABEL_1 \ label is actually `_ (a backtick followed by an \ underscore), but that doesn't compile in BeebAsm and \ it's pretty cryptic, so instead this version sticks \ with the label LABEL_1 from the cassette version DEY \ Decrement Y to 0 STA (XX19),Y \ Store the cloud size in byte #0 of the ship line heap LDA INWK+31 \ Clear bit 6 of the ship's byte #31 to denote that the AND #%10111111 \ explosion has not yet been drawn STA INWK+31 AND #%00001000 \ If bit 3 of the ship's byte #31 is clear, then nothing BEQ TT48 \ is being drawn on-screen for this ship anyway, so \ return from the subroutine (as TT48 contains an RTS) LDY #2 \ Otherwise it's time to draw an explosion cloud, so LDA (XX19),Y \ fetch byte #2 of the ship line heap into Y, which we TAY \ set to the explosion count for this ship (i.e. the \ number of vertices used as origins for explosion \ clouds) \ \ The explosion count is stored as 4 * n + 6, where n is \ the number of vertices, so the following loop copies \ the coordinates of the first n vertices from the heap \ at XX3, which is where we stored all the visible \ vertex coordinates in part 8 of the LL9 routine, and \ sticks them in the ship line heap pointed to by XX19, \ starting at byte #7 (so it leaves the first 6 bytes of \ the ship line heap alone) .EXL1 LDA XX3-7,Y \ Copy byte Y-7 from the XX3 heap, into the Y-th byte of STA (XX19),Y \ the ship line heap DEY \ Decrement the loop counter CPY #6 \ Keep copying vertex coordinates into the ship line BNE EXL1 \ heap until Y = 6 (which will copy n vertices, where n \ is the number of vertices we should be exploding) LDA INWK+31 \ Set bit 6 of the ship's byte #31 to denote that the ORA #%01000000 \ explosion has been drawn (as it's about to be) STA INWK+31 .PTCLS \ This part of the routine actually draws the explosion \ cloud LDY #0 \ Fetch byte #0 of the ship line heap, which contains LDA (XX19),Y \ the cloud size we stored above, and store it in Q STA Q INY \ Increment the index in Y to point to byte #1 LDA (XX19),Y \ Fetch byte #1 of the ship line heap, which contains \ the cloud counter. We are now going to process this \ into the number of particles in each vertex's cloud BPL P%+4 \ If the cloud counter < 128, then we are in the first \ half of the cloud's existence, so skip the next \ instruction EOR #&FF \ Flip the value of A so that in the second half of the \ cloud's existence, A counts down instead of up LSR A \ Divide A by 8 so that is has a maximum value of 15 LSR A LSR A ORA #1 \ Make sure A is at least 1 and store it in U, to STA U \ give us the number of particles in the explosion for \ each vertex INY \ Increment the index in Y to point to byte #2 LDA (XX19),Y \ Fetch byte #2 of the ship line heap, which contains STA TGT \ the explosion count for this ship (i.e. the number of \ vertices used as origins for explosion clouds) and \ store it in TGT LDA RAND+1 \ Fetch the current random number seed in RAND+1 and PHA \ store it on the stack, so we can re-randomise the \ seeds when we are done LDY #6 \ Set Y = 6 to point to the byte before the first vertex \ coordinate we stored on the ship line heap above (we \ increment it below so it points to the first vertex) .EXL5 LDX #3 \ We are about to fetch a pair of coordinates from the \ ship line heap, so set a counter in X for 4 bytes .EXL3 INY \ Increment the index in Y so it points to the next byte \ from the coordinate we are copying LDA (XX19),Y \ Copy the Y-th byte from the ship line heap to the X-th STA K3,X \ byte of K3 DEX \ Decrement the X index BPL EXL3 \ Loop back to EXL3 until we have copied all four bytes \ The above loop copies the vertex coordinates from the \ ship line heap to K3, reversing them as we go, so it \ sets the following: \ \ K3+3 = x_lo \ K3+2 = x_hi \ K3+1 = y_lo \ K3+0 = y_hi STY CNT \ Set CNT to the index that points to the next vertex on \ the ship line heap LDY #2 \ Set Y = 2, which we will use to point to bytes #3 to \ #6, after incrementing it \ This next loop copies bytes #3 to #6 from the ship \ line heap into the four random number seeds in RAND to \ RAND+3, EOR'ing them with the vertex index so they are \ different for every vertex. This enables us to \ generate random numbers for drawing each vertex that \ are random but repeatable, which we need when we \ redraw the cloud to remove it \ \ Note that we haven't actually set the values of bytes \ #3 to #6 in the ship line heap, so we have no idea \ what they are, we just use what's already there. But \ the fact that those bytes are stored for this ship \ means we can repeat the random generation of the \ cloud, which is the important bit .EXL2 INY \ Increment the index in Y so it points to the next \ random number seed to copy LDA (XX19),Y \ Fetch the Y-th byte from the ship line heap EOR CNT \ EOR with the vertex index, so the seeds are different \ for each vertex STA &FFFD,Y \ Y is going from 3 to 6, so this stores the four bytes \ in memory locations &00, &01, &02 and &03, which are \ the memory locations of RAND through RAND+3 CPY #6 \ Loop back to EXL2 until Y = 6, which means we have BNE EXL2 \ copied four bytes LDY U \ Set Y to the number of particles in the explosion for \ each vertex, which we stored in U above. We will now \ use this as a loop counter to iterate through all the \ particles in the explosion .EXL4 JSR DORND2 \ Set ZZ to a random number, making sure the C flag STA ZZ \ doesn't affect the outcome LDA K3+1 \ Set (A R) = (y_hi y_lo) STA R \ = y LDA K3 JSR EXS1 \ Set (A X) = (A R) +/- random * cloud size \ = y +/- random * cloud size \ --- Mod: Code removed for two-player Elite: ---------> \BNE EX11 \ If A is non-zero, the particle is off-screen as the \ \ coordinate is bigger than 255), so jump to EX11 to do \ \ the next particle \ \CPX #2*Y-1 \ If X > the y-coordinate of the bottom of the screen, \BCS EX11 \ the particle is off the bottom of the screen, so jump \ \ to EX11 to do the next particle \ \ \ Otherwise X contains a random y-coordinate within the \ \ cloud \ --- And replaced by: --------------------------------> STA S \ Set (S R) = (A X) STX R \ \ So (S R) contains the y-coordinate of the particle as \ a signed 16-bit value BIT drawPlayerView \ If we are drawing player 1's view, jump to expl1 to BPL expl1 \ move the particle up into the top half of the screen \ We are drawing player 2's view, so move the particle \ down into the bottom half of the screen and check the \ bounds LDA R \ Set (A X) = (S R) + #Y/2 CLC ADC #Y/2 TAX LDA S ADC #0 BNE EX11 \ If A is non-zero, the particle is off-screen as the \ coordinate is either negative or bigger than 255, so \ jump to EX11 to do the next particle CPX #Y \ If the particle is in the top half of the screen, jump BCC EX11 \ to EX11 to do the next particle CPX #2*Y-1 \ If the particle is off the bottom of the screen, jump BCS EX11 \ to EX11 to do the next particle BCC expl2 \ Otherwise jump to expl2 to draw this particle (this \ BCC is effectiely a JMP as we just passed through a \ BCS) .expl1 \ We are drawing player 1's view, so move the particle \ up into the top half of the screen and check the \ bounds LDA R \ Set (A X) = (S R) - #Y/2 SEC SBC #Y/2 TAX LDA S SBC #0 BNE EX11 \ If A is non-zero, the particle is off-screen as the \ coordinate is either negative or bigger than 255, so \ jump to EX11 to do the next particle CPX #Y \ If the particle is in the bottom half of the screen, BCS EX11 \ jump to EX11 to do the next particle \ Otherwise keep going to draw the particle .expl2 \ --- End of replacement ------------------------------> STX Y1 \ Set Y1 = our random y-coordinate within the cloud LDA K3+3 \ Set (A R) = (x_hi x_lo) STA R LDA K3+2 JSR EXS1 \ Set (A X) = (A R) +/- random * cloud size \ = x +/- random * cloud size BNE EX4 \ If A is non-zero, the particle is off-screen as the \ coordinate is bigger than 255), so jump to EX4 to do \ the next particle \ Otherwise X contains a random x-coordinate within the \ cloud LDA Y1 \ Set A = our random y-coordinate within the cloud JSR PIXEL3 \ Draw a point at screen coordinate (X, A) with the \ point size determined by the distance in ZZ .EX4 DEY \ Decrement the loop counter for the next particle BPL EXL4 \ Loop back to EXL4 until we have done all the particles \ in the cloud LDY CNT \ Set Y to the index that points to the next vertex on \ the ship line heap CPY TGT \ If Y < TGT, which we set to the explosion count for BCC EXL5 \ this ship (i.e. the number of vertices used as origins \ for explosion clouds), loop back to EXL5 to do a cloud \ for the next vertex PLA \ Restore the current random number seed to RAND+1 that STA RAND+1 \ we stored at the start of the routine LDA K%+6 \ Store the z_lo coordinate for the planet (which will STA RAND+3 \ be pretty random) in the RAND+3 seed RTS \ Return from the subroutine .EX11 JSR DORND2 \ Set A and X to random numbers, making sure the C flag \ doesn't affect the outcome JMP EX4 \ We just skipped a particle, so jump up to EX4 to do \ the next one .EXS1 \ This routine calculates the following: \ \ (A X) = (A R) +/- random * cloud size \ \ returning with the flags set for the high byte in A STA S \ Store A in S so we can use it later JSR DORND2 \ Set A and X to random numbers, making sure the C flag \ doesn't affect the outcome ROL A \ Set A = A * 2 BCS EX5 \ If bit 7 of A was set (50% chance), jump to EX5 JSR FMLTU \ Set A = A * Q / 256 \ = random << 1 * projected cloud size / 256 ADC R \ Set (A X) = (S R) + A TAX \ = (S R) + random * projected cloud size \ \ where S contains the argument A, starting with the low \ bytes LDA S \ And then the high bytes ADC #0 RTS \ Return from the subroutine .EX5 JSR FMLTU \ Set T = A * Q / 256 STA T \ = random << 1 * projected cloud size / 256 LDA R \ Set (A X) = (S R) - T SBC T \ TAX \ where S contains the argument A, starting with the low \ bytes LDA S \ And then the high bytes SBC #0 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: SOS1 \ Type: Subroutine \ Category: Universe \ Summary: Update the missile indicators, set up the planet data block \ \ ------------------------------------------------------------------------------ \ \ Update the missile indicators, and set up a data block for the planet, but \ only setting the pitch and roll counters to 127 (no damping). \ \ ****************************************************************************** .SOS1 JSR msblob \ Reset the dashboard's missile indicators so none of \ them are targeted LDA #127 \ Set the pitch and roll counters to 127, so that's a STA INWK+29 \ clockwise roll and a diving pitch with no damping, so STA INWK+30 \ the planet's rotation doesn't slow down LDA tek \ Set A = 128 or 130 depending on bit 1 of the system's AND #%00000010 \ tech level in tek ORA #%10000000 JMP NWSHP \ Add a new planet to our local bubble of universe, \ with the planet type defined by A (128 is a planet \ with an equator and meridian, 130 is a planet with \ a crater) \ ****************************************************************************** \ \ Name: SOLAR \ Type: Subroutine \ Category: Universe \ Summary: Set up various aspects of arriving in a new system \ Deep dive: A sense of scale \ \ ------------------------------------------------------------------------------ \ \ Halve our legal status, update the missile indicators, and set up data blocks \ and slots for the planet and sun. \ \ ****************************************************************************** .SOLAR LSR FIST \ Halve our legal status in FIST, making us less bad, \ and moving bit 0 into the C flag (so every time we \ arrive in a new system, our legal status improves a \ bit) JSR ZINF \ Call ZINF to reset the INWK ship workspace, which \ doesn't affect the C flag LDA QQ15+1 \ Fetch s0_hi AND #%00000011 \ Extract bits 0-1 (which also help to determine the \ economy), which will be between 0 and 3 ADC #3 \ Add 3 + C, to get a result between 3 and 7, clearing \ the C flag in the process STA INWK+8 \ Store the result in z_sign in byte #6 ROR A \ Halve A, rotating in the C flag (which is clear) and STA INWK+2 \ store in both x_sign and y_sign, moving the planet to STA INWK+5 \ the upper right JSR SOS1 \ Call SOS1 to set up the planet's data block and add it \ to FRIN, where it will get put in the first slot as \ it's the first one to be added to our local bubble of \ this new system's universe LDA QQ15+3 \ Fetch s1_hi, extract bits 0-2, set bits 0 and 7 and AND #%00000111 \ store in z_sign, so the sun is behind us at a distance ORA #%10000001 \ of 1 to 7 STA INWK+8 LDA QQ15+5 \ Fetch s2_hi, extract bits 0-1 and store in x_sign and AND #%00000011 \ y_sign, so the sun is either dead centre in our rear STA INWK+2 \ laser crosshairs, or off to the top left by a distance STA INWK+1 \ of 1 or 2 when we look out the back LDA #0 \ Set the pitch and roll counters to 0 (no rotation) STA INWK+29 STA INWK+30 LDA #129 \ Set A = 129, the ship type for the sun JSR NWSHP \ Call NWSHP to set up the sun's data block and add it \ to FRIN, where it will get put in the second slot as \ it's the second one to be added to our local bubble \ of this new system's universe \ ****************************************************************************** \ \ Name: NWSTARS \ Type: Subroutine \ Category: Stardust \ Summary: Initialise the stardust field \ \ ------------------------------------------------------------------------------ \ \ This routine is called when the space view is initialised in routine LOOK1. \ \ ****************************************************************************** .NWSTARS LDA QQ11 \ If this is not a space view, jump to WPSHPS to skip \ORA MJ \ the initialisation of the SX, SY and SZ tables. The OR BNE WPSHPS \ instruction is commented out in the original source, \ but it would have the effect of also skipping the \ initialisation if we had mis-jumped into witchspace \ ****************************************************************************** \ \ Name: nWq \ Type: Subroutine \ Category: Stardust \ Summary: Create a random cloud of stardust \ \ ------------------------------------------------------------------------------ \ \ Create a random cloud of stardust containing the correct number of dust \ particles, i.e. NOSTM of them, which is 3 in witchspace and 18 (#NOST) in \ normal space. Also clears the scanner and initialises the LSO block. \ \ This is called by the DEATH routine when it displays our untimely demise. \ \ ****************************************************************************** .nWq LDY NOSTM \ Set Y to the current number of stardust particles, so \ we can use it as a counter through all the stardust \ --- Mod: Code added for two-player Elite: -----------> BIT drawPlayerView \ If we are drawing player 1's stars, skip the following BPL news1 TYA \ We are drawing player 2's stars, so use the second CLC \ half of the table ADC #NOST/2 TAY .news1 \ --- End of added code -------------------------------> .SAL4 JSR DORND \ Set A and X to random numbers ORA #8 \ Set A so that it's at least 8 STA SZ,Y \ Store A in the Y-th particle's z_hi coordinate at \ SZ+Y, so the particle appears in front of us STA ZZ \ Set ZZ to the particle's z_hi coordinate JSR DORND \ Set A and X to random numbers STA SX,Y \ Store A in the Y-th particle's x_hi coordinate at \ SX+Y, so the particle appears in front of us STA X1 \ Set X1 to the particle's x_hi coordinate JSR DORND \ Set A and X to random numbers \ --- Mod: Code added for two-player Elite: -----------> AND #%10111111 \ Halve the vertical range for stardust \ --- End of added code -------------------------------> STA SY,Y \ Store A in the Y-th particle's y_hi coordinate at \ SY+Y, so the particle appears in front of us STA Y1 \ Set Y1 to the particle's y_hi coordinate JSR PIXEL2 \ Draw a stardust particle at (X1,Y1) with distance ZZ \ --- Mod: Code added for two-player Elite: -----------> BIT drawPlayerView \ If we are drawing player 1's stars, jump to news2 to BPL news2 \ check against zero \ We are drawing player 2's stars, so check against the \ table's halfway point DEY \ Decrement the counter to point to the next particle of \ stardust CPY #NOST/2 \ Loop back to SAL4 until we have randomised all the BNE SAL4 \ stardust particles BEQ news3 \ We are done initialising stars so jump to news3 to \ keep going (this JMP is effectively a BEQ as we just \ passed through a BNE) .news2 \ --- End of added code -------------------------------> DEY \ Decrement the counter to point to the next particle of \ stardust BNE SAL4 \ Loop back to SAL4 until we have randomised all the \ stardust particles \ --- Mod: Code added for two-player Elite: -----------> .news3 \ --- End of added code -------------------------------> JSR PBFL \ Call PBFL to send the contents of the pixel buffer to \ the I/O processor for plotting on-screen \ Fall through into WPSHPS to clear the scanner and \ reset the LSO block \ ****************************************************************************** \ \ Name: WPSHPS \ Type: Subroutine \ Category: Dashboard \ Summary: Clear the scanner, reset the ball line and sun line heaps \ \ ------------------------------------------------------------------------------ \ \ Remove all ships from the scanner, reset the sun line heap at LSO, and reset \ the ball line heap at LSX2 and LSY2. \ \ ****************************************************************************** .WPSHPS \ --- Mod: Code added for two-player Elite: -----------> BIT drawPlayerView \ If we are drawing player 1's view, skip the following BPL wipe1 LDA player1INWK31 \ Clear bits 3 and 6 in the ship's byte #31 for the AND #%10110111 \ player 1 ship as it appears in player 2's view STA player1INWK31 \ (bit 3 = on-screen, bit 6 = lasers) LDA player1INWK31+1 \ Clear bits 3 and 6 in the ship's byte #31 for the AND #%10110111 \ first missile as it appears in player 2's view STA player1INWK31+1 \ (bit 3 = on-screen, bit 6 = lasers) LDA player1INWK31+2 \ Clear bits 3 and 6 in the ship's byte #31 for the AND #%10110111 \ second missile as it appears in player 2's view STA player1INWK31+2 \ (bit 3 = on-screen, bit 6 = lasers) LDA player1Heap \ Set P(1 0) to the address of the ship heap for player STA P \ 1's ship in player 2's view LDA player1Heap+1 STA P+1 LDY #2 \ Set the Y2 coordinate of the laser line in the ship LDA #255 \ line heap to 255 so there is no laser line for player STA (P),Y \ 1's ship in player 2's view JMP WS2 \ Jump to WS2 to keep going .wipe1 \ --- End of added code -------------------------------> LDX #0 \ Set up a counter in X to work our way through all the \ ship slots in FRIN .WSL1 LDA FRIN,X \ Fetch the ship type in slot X BEQ WS2 \ If the slot contains 0 then it is empty and we have \ checked all the slots (as they are always shuffled \ down in the main loop to close up and gaps), so jump \ to WS2 as we are done BMI WS1 \ If the slot contains a ship type with bit 7 set, then \ it contains the planet or the sun, so jump down to WS1 \ to skip this slot, as the planet and sun don't appear \ on the scanner \ --- Mod: Code added for two-player Elite: -----------> LDY #CYAN2 \ Set the scanner colour to cyan so we remove ships in \ this colour JSR WipeShip \ Remove the ship from the scanner JMP WS1 \ Jump to WS1 to move on to the next slot .WipeShip STY SCANcol \ Store the scanner colour in SCANcol so we can retrieve \ it below \ --- End of added code -------------------------------> STA TYPE \ Store the ship type in TYPE JSR GINF \ Call GINF to get the address of the data block for \ ship slot X and store it in INF LDY #31 \ We now want to copy the first 32 bytes from the ship's \ data block into INWK, so set a counter in Y .WSL2 LDA (INF),Y \ Copy the Y-th byte from the data block pointed to by STA INWK,Y \ INF into the Y-th byte of INWK workspace DEY \ Decrement the counter to point at the next byte BPL WSL2 \ Loop back to WSL2 until we have copied all 32 bytes STX XSAV \ Store the ship slot number in XSAV while we call SCAN \ --- Mod: Code added for two-player Elite: -----------> LDA SCANcol \ Set the correct scanner colour (as passed to WipeShip \ in Y \ --- End of added code -------------------------------> JSR SCAN \ Call SCAN to plot this ship on the scanner, which will \ remove it as it's plotted with EOR logic LDX XSAV \ Restore the ship slot number from XSAV into X LDY #31 \ Clear bits 3, 4 and 6 in the ship's byte #31, which LDA (INF),Y \ stops drawing the ship on-screen (bit 3), hides it AND #%10100111 \ from the scanner (bit 4) and stops any lasers firing STA (INF),Y \ (bit 6) \ --- Mod: Code added for two-player Elite: -----------> LDY #33 \ Set P(1 0) to the address of the ship's line heap LDA (INF),Y STA P INY LDA (INF),Y STA P+1 LDY #2 \ Set the Y2 coordinate of the laser line in the ship LDA #255 \ line heap to 255 so there is no laser line for player STA (P),Y \ 1's ship in player 2's view RTS \ Return from the subroutine \ --- End of added code -------------------------------> .WS1 INX \ Increment X to point to the next ship slot BNE WSL1 \ Loop back up to process the next slot (this BNE is \ effectively a JMP as X will never be zero) .WS2 \ --- Mod: Code removed for two-player Elite: ---------> \STZ LSP \ Reset the ball line heap by setting the ball line heap \ \ pointer to 0 \ --- And replaced by: --------------------------------> JSR ResetBallLine \ Reset the ball line heap for the view we are currently \ drawing \ --- End of replacement ------------------------------> LDX #&FF \ Set X = &FF (though this appears not to be used) \ Fall through into FLFLLS to reset the LSO block \ ****************************************************************************** \ \ Name: FLFLLS \ Type: Subroutine \ Category: Drawing suns \ Summary: Reset the sun line heap \ \ ------------------------------------------------------------------------------ \ \ Reset the sun line heap at LSO by zero-filling it and setting the first byte \ to &FF. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A A is set to 0 \ \ ****************************************************************************** .FLFLLS \ --- Mod: Code removed for two-player Elite: ---------> \LDY #2*Y-1 \ #Y is the y-coordinate of the centre of the space \ \ view, so this sets Y as a counter for the number of \ \ lines in the space view (i.e. 191), which is also the \ \ number of lines in the LSO block \ --- And replaced by: --------------------------------> JSR SetPlayerSunHeap \ Set up the sun line heap pointers for the current \ player LDY #Y-1 \ Set Y = y-coordinate of the bottom of the screen, \ so we only reset one sun buffer \ --- End of replacement ------------------------------> LDA #0 \ Set A to 0 so we can zero-fill the LSO block .SAL6 \ --- Mod: Code removed for two-player Elite: ---------> \STA LSO,Y \ Set the Y-th byte of the LSO block to 0 \ --- And replaced by: --------------------------------> STA (LSOS),Y \ Set the Y-th byte of the LSO block to 0 \ --- End of replacement ------------------------------> DEY \ Decrement the counter BNE SAL6 \ Loop back until we have filled all the way to LSO+1 DEY \ Decrement Y to value of &FF (as we exit the above loop \ with Y = 0) \ --- Mod: Code removed for two-player Elite: ---------> \STY LSX \ Set the first byte of the LSO block, which has its own \ \ label LSX, to &FF, to indicate that the sun line heap \ \ is empty \ --- And replaced by: --------------------------------> TYA \ Set the first byte of the LSO block, which has its own STA (LSXS) \ label LSX, to &FF, to indicate that the sun line heap \ is empty LDA #0 \ Set A = 0 as FLFLLS needs to return that value \ --- End of replacement ------------------------------> RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: DET1 \ Type: Subroutine \ Category: Drawing the screen \ Summary: Show or hide the dashboard (for when we die) by sending a #DODIALS \ command to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The number of text rows to display on the screen (24 \ will hide the dashboard, 31 will make it reappear) \ \ ****************************************************************************** .DET1 LDA #DODIALS \ Send the first part of a #DODIALS command to the I/O JSR OSWRCH \ processor TXA \ Send the new number of rows to the I/O processor, so JMP OSWRCH \ we've now sent a #DODIALS command \ ****************************************************************************** \ \ Name: Player2SHD \ Type: Subroutine \ Category: Flight \ Summary: Charge a shield and drain some energy from the energy banks \ \ ------------------------------------------------------------------------------ \ \ Charge up a shield, and if it needs charging, drain some energy from the \ energy banks. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The value of the shield to recharge \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Player2SHD INX \ Increment the shield value BNE Player2DENGY \ If the shield is non-zero then jump to Player2DENGY \ to drain our energy to pay for all this shield \ charging \ If the shield value is 0 then this means it was 255 \ before, which is the maximum value, so keep going to \ bring it back down to 255 and return without draining \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: SHD \ Type: Subroutine \ Category: Flight \ Summary: Charge a shield and drain some energy from the energy banks \ \ ------------------------------------------------------------------------------ \ \ Charge up a shield, and if it needs charging, drain some energy from the \ energy banks. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The value of the shield to recharge \ \ ****************************************************************************** DEX \ If we get here then we just incremented the shield \ value back around to zero, so decrement it back down \ to 255 so it stays at the maximum value of 255 RTS \ Return from the subroutine .SHD INX \ Increment the shield value BEQ SHD-2 \ If the shield value is 0 then this means it was 255 \ before, which is the maximum value, so jump to SHD-2 \ to bring it back down to 255 and return without \ draining our energy banks \ Otherwise fall through into DENGY to drain our energy \ to pay for all this shield charging \ ****************************************************************************** \ \ Name: DENGY \ Type: Subroutine \ Category: Flight \ Summary: Drain some energy from the energy banks \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ Z flag Set if we have no energy left, clear otherwise \ \ ****************************************************************************** .DENGY DEC ENERGY \ Decrement the energy banks in ENERGY PHP \ Save the flags on the stack BNE P%+5 \ If the energy levels are not yet zero, skip the \ following instruction INC ENERGY \ The minimum allowed energy level is 1, and we just \ reached 0, so increment ENERGY back to 1 PLP \ Restore the flags from the stack, so we return with \ the Z flag from the DEC instruction above RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: Player2DENGY \ Type: Subroutine \ Category: Flight \ Summary: Drain some energy from the energy banks for player 2 \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ Z flag Set if we have no energy left, clear otherwise \ \ ****************************************************************************** .Player2DENGY \ --- Mod: Code added for two-player Elite: -----------> DEC player2ENERGY \ Decrement the energy banks in ENERGY PHP \ Save the flags on the stack BNE P%+5 \ If the energy levels are not yet zero, skip the \ following instruction INC player2ENERGY \ The minimum allowed energy level is 1, and we just \ reached 0, so increment ENERGY back to 1 PLP \ Restore the flags from the stack, so we return with \ the Z flag from the DEC instruction above RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: COMPAS \ Type: Subroutine \ Category: Dashboard \ Summary: Update the compass \ \ ****************************************************************************** .COMPAS \ --- Mod: Code removed for two-player Elite: ---------> \JSR DOT \ Call DOT to redraw (i.e. remove) the current compass \ \ dot \ \LDA SSPR \ If we are inside the space station safe zone, jump to \BNE SP1 \ SP1 to draw the space station on the compass \ \JSR SPS1 \ Otherwise we need to draw the planet on the compass, \ \ so first call SPS1 to calculate the vector to the \ \ planet and store it in XX15 \ --- And replaced by: --------------------------------> BIT titleScreen \ If bit 7 of titleScreen is set then the compass is BMI COMPAS-1 \ disabled for the title screen, so return from the \ subroutine JSR Player1DOT \ Call DOT to redraw (i.e. remove) the current compass \ dot from player 1's compass on the left JSR Player2Vector \ Call Player2Vector to calculate the vector to player \ 2 and store it in XX15 JSR Player1SP2 \ Call Player1SP2 to draw XX15 on the left compass JSR DOT \ Call DOT to redraw (i.e. remove) the current compass \ dot from player 2's compass on the right JSR Player1Vector \ Call Player1Vector to calculate the vector to player \ 1 and store it in XX15 \ --- End of replacement ------------------------------> BRA SP2 \ Jump to SP2 to draw XX15 on the compass, returning \ from the subroutine using a tail call \ ****************************************************************************** \ \ Name: SPS2 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (Y X) = A / 10 \ \ ------------------------------------------------------------------------------ \ \ Calculate the following, where A is a sign-magnitude 8-bit integer and the \ result is a signed 16-bit integer: \ \ (Y X) = A / 10 \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag The C flag is cleared \ \ ****************************************************************************** .SPS2 ASL A \ Set X = |A| * 2, and set the C flag to the sign bit of TAX \ A LDA #0 \ Set Y to have the sign bit from A in bit 7, with the ROR A \ rest of its bits zeroed, so Y now contains the sign of TAY \ the original argument LDA #20 \ Set Q = 20 STA Q TXA \ Copy X into A, so A now contains the argument A * 2 JSR DVID4 \ Calculate the following: \ \ P = A / Q \ = |argument A| * 2 / 20 \ = |argument A| / 10 LDX P \ Set X to the result TYA \ If the sign of the original argument A is negative, BMI LL163 \ jump to LL163 to flip the sign of the result LDY #0 \ Set the high byte of the result to 0, as the result is \ positive RTS \ Return from the subroutine .LL163 LDY #&FF \ The result is negative, so set the high byte to &FF TXA \ Flip the low byte and add 1 to get the negated low EOR #&FF \ byte, using two's complement TAX INX RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: SPS4 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Calculate the vector to the space station \ \ ------------------------------------------------------------------------------ \ \ Calculate the vector between our ship and the space station and store it in \ XX15. \ \ ****************************************************************************** .SPS4 LDX #8 \ First we need to copy the space station's coordinates \ into K3, so set a counter to copy the first 9 bytes \ (the three-byte x, y and z coordinates) from the \ station's data block at K% + NI% into K3 .SPL1 LDA K%+NI%,X \ Copy the X-th byte from the station's data block at STA K3,X \ K% + NI% to the X-th byte of K3 DEX \ Decrement the loop counter BPL SPL1 \ Loop back to SPL1 until we have copied all 9 bytes JMP TAS2 \ Call TAS2 to build XX15 from K3, returning from the \ subroutine using a tail call \ ****************************************************************************** \ \ Name: SP1 \ Type: Subroutine \ Category: Dashboard \ Summary: Draw the space station on the compass \ \ ****************************************************************************** .SP1 JSR SPS4 \ Call SPS4 to calculate the vector to the space station \ and store it in XX15 \ Fall through into SP2 to draw XX15 on the compass \ ****************************************************************************** \ \ Name: SP2 \ Type: Subroutine \ Category: Dashboard \ Summary: Draw a dot on the compass, given the planet/station vector \ \ ------------------------------------------------------------------------------ \ \ Draw a dot on the compass to represent the planet or station, whose normalised \ vector is in XX15. \ \ XX15 to XX15+2 The normalised vector to the planet or space station, \ stored as x in XX15, y in XX15+1 and z in XX15+2 \ \ ****************************************************************************** .SP2 LDA XX15 \ Set A to the x-coordinate of the planet or station to \ show on the compass, which will be in the range -96 to \ +96 as the vector has been normalised JSR SPS2 \ Set (Y X) = A / 10, so X will be from -9 to +9, which \ is the x-offset from the centre of the compass of the \ dot we want to draw. Returns with the C flag clear TXA \ Set COMX = 195 + X, as 186 is the pixel x-coordinate ADC #195 \ of the leftmost dot possible on the compass, and X can STA COMX \ be -9, which would be 195 - 9 = 186. This also means \ that the highest value for COMX is 195 + 9 = 204, \ which is the pixel x-coordinate of the rightmost dot \ in the compass... but the compass dot is actually two \ pixels wide, so the compass dot can overlap the right \ edge of the compass, but not the left edge LDA XX15+1 \ Set A to the y-coordinate of the planet or station to \ show on the compass, which will be in the range -96 to \ +96 as the vector has been normalised JSR SPS2 \ Set (Y X) = A / 10, so X will be from -9 to +9, which \ is the x-offset from the centre of the compass of the \ dot we want to draw. Returns with the C flag clear STX T \ Set COMY = 204 - X, as 203 is the pixel y-coordinate LDA #204 \ of the centre of the compass, the C flag is clear, SBC T \ and the y-axis needs to be flipped around (because STA COMY \ when the planet or station is above us, and the \ vector is therefore positive, we want to show the dot \ higher up on the compass, which has a smaller pixel \ y-coordinate). So this calculation does this: \ \ COMY = 204 - X - (1 - 0) = 203 - X \ --- Mod: Code removed for two-player Elite: ---------> \LDA #WHITE2 \ Set A to white, the colour for when the planet or \ \ station in the compass is in front of us \ \LDX XX15+2 \ If the z-coordinate of the XX15 vector is positive, \BPL P%+4 \ skip the following instruction \ \LDA #GREEN2 \ The z-coordinate of XX15 is negative, so the planet or \ \ station is behind us and the compass dot should be in \ \ green, so set A accordingly \ --- And replaced by: --------------------------------> LDA #PLAYER2SCAN \ Set A to the colour for when the ship in the compass \ is in front of us LDX XX15+2 \ If the z-coordinate of the XX15 vector is positive, BPL P%+4 \ skip the following instruction LDA #PLAYER2SCAN+128 \ The z-coordinate of XX15 is negative, so the ship is \ behind us and the compass dot should be small, so set \ bit 7 of the colour to send this to the I/O processor \ --- End of replacement ------------------------------> STA COMC \ Store the compass colour in COMC \ Fall through into DOT to draw the dot on the compass \ ****************************************************************************** \ \ Name: DOT \ Type: Subroutine \ Category: Drawing pixels \ Summary: Draw a dash on the compass by sending a #DOdot command to the I/O \ processor \ \ ------------------------------------------------------------------------------ \ \ Draw a dash on the compass. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ COMX The screen pixel x-coordinate of the dash \ \ COMY The screen pixel y-coordinate of the dash \ \ COMC The colour and thickness of the dash: \ \ * #WHITE2 = a double-height dash in white, for when \ the object in the compass is in front of us \ \ * #GREEN2 = a single-height dash in green, for when \ the object in the compass is behind us \ \ ****************************************************************************** .DOT LDA COMY \ Store the y-coordinate of the dash in byte #0 of the STA DOTY1 \ parameter block below LDA COMX \ Store the x-coordinate of the dash in byte #1 of the STA DOTX1 \ parameter block below LDA COMC \ Store the dash colour in byte #2 of the parameter STA DOTCOL \ block below LDX #LO(DOTpars) \ Set (Y X) to point to the parameter block below LDY #HI(DOTpars) LDA #DOdot \ Send a #DOdot command to the I/O processor to draw JMP OSWORD \ the dash on-screen, returning from the subroutine \ using a tail call .DOTpars EQUB 5 \ The number of bytes to transmit with this command EQUB 0 \ The number of bytes to receive with this command .DOTX1 EQUB 0 \ The x-coordinate of the dash .DOTY1 EQUB 0 \ The y-coordinate of the dash .DOTCOL EQUB 0 \ The colour of the dash RTS \ End of the parameter block \ ****************************************************************************** \ \ Name: Player1SP2 \ Type: Subroutine \ Category: Dashboard \ Summary: Draw a dot on the compass, given the planet/station vector \ \ ------------------------------------------------------------------------------ \ \ Draw a dot on the compass to represent the planet or station, whose normalised \ vector is in XX15. \ \ XX15 to XX15+2 The normalised vector to the planet or space station, \ stored as x in XX15, y in XX15+1 and z in XX15+2 \ \ ****************************************************************************** .Player1SP2 LDA XX15 \ Set A to the x-coordinate of the planet or station to \ show on the compass, which will be in the range -96 to \ +96 as the vector has been normalised JSR SPS2 \ Set (Y X) = A / 10, so X will be from -9 to +9, which \ is the x-offset from the centre of the compass of the \ dot we want to draw. Returns with the C flag clear TXA \ Set COMX = 195 + X, as 186 is the pixel x-coordinate ADC #59 \ of the leftmost dot possible on the compass, and X can STA player1COMX \ be -9, which would be 195 - 9 = 186. This also means \ that the highest value for COMX is 195 + 9 = 204, \ which is the pixel x-coordinate of the rightmost dot \ in the compass... but the compass dot is actually two \ pixels wide, so the compass dot can overlap the right \ edge of the compass, but not the left edge LDA XX15+1 \ Set A to the y-coordinate of the planet or station to \ show on the compass, which will be in the range -96 to \ +96 as the vector has been normalised JSR SPS2 \ Set (Y X) = A / 10, so X will be from -9 to +9, which \ is the x-offset from the centre of the compass of the \ dot we want to draw. Returns with the C flag clear STX T \ Set COMY = 204 - X, as 203 is the pixel y-coordinate LDA #204 \ of the centre of the compass, the C flag is clear, SBC T \ and the y-axis needs to be flipped around (because STA player1COMY \ when the planet or station is above us, and the \ vector is therefore positive, we want to show the dot \ higher up on the compass, which has a smaller pixel \ y-coordinate). So this calculation does this: \ \ COMY = 204 - X - (1 - 0) = 203 - X LDA #PLAYER1SCAN \ Set A to the colour for when the ship in the compass \ is in front of us LDX XX15+2 \ If the z-coordinate of the XX15 vector is positive, BPL P%+4 \ skip the following instruction LDA #PLAYER1SCAN+128 \ The z-coordinate of XX15 is negative, so the ship is \ behind us and the compass dot should be small, so set \ bit 7 of the colour to send this to the I/O processor STA player1COMC \ Store the compass colour in COMC \ Fall through into DOT to draw the dot on the compass \ ****************************************************************************** \ \ Name: Player1DOT \ Type: Subroutine \ Category: Drawing pixels \ Summary: Draw a dash on the compass by sending a #DOdot command to the I/O \ processor \ \ ------------------------------------------------------------------------------ \ \ Draw a dash on the compass for player 1 (on the left). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ COMX The screen pixel x-coordinate of the dash \ \ COMY The screen pixel y-coordinate of the dash \ \ COMC The colour and thickness of the dash: \ \ * #WHITE2 = a double-height dash in white, for when \ the object in the compass is in front of us \ \ * #GREEN2 = a single-height dash in green, for when \ the object in the compass is behind us \ \ ****************************************************************************** .Player1DOT LDA player1COMY \ Store the y-coordinate of the dash in byte #0 of the STA DOTY1 \ parameter block below LDA player1COMX \ Store the x-coordinate of the dash in byte #1 of the STA DOTX1 \ parameter block below LDA player1COMC \ Store the dash colour in byte #2 of the parameter STA DOTCOL \ block below LDX #LO(DOTpars) \ Set (Y X) to point to the parameter block below LDY #HI(DOTpars) LDA #DOdot \ Send a #DOdot command to the I/O processor to draw JMP OSWORD \ the dash on-screen, returning from the subroutine \ using a tail call \ ****************************************************************************** \ \ Name: OOPS \ Type: Subroutine \ Category: Flight \ Summary: Take some damage \ \ ------------------------------------------------------------------------------ \ \ We just took some damage, so reduce the shields if we have any, or reduce the \ energy levels and potentially take some damage to the cargo if we don't. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The amount of damage to take \ \ INF The address of the ship block for the ship that attacked \ us, or the ship that we just ran into \ \ ****************************************************************************** .OOPS \ --- Mod: Code removed for two-player Elite: ---------> \STA T \ Store the amount of damage in T \ --- And replaced by: --------------------------------> BIT gameOver \ If this is the game over screen, return from the BPL P%+3 \ subroutine without processing damage RTS STA damageToApply \ Store the amount of damage in damageToApply LDA player2GameType \ If player 2 is playing survival, they don't have a BEQ oops1 \ score, so jump to oops1 to skip updating the score \ Player 1 has been hit, so increment player 2's score JSR Player2ee3 \ Print player 2's score to remove it from the screen INC player2Score \ Increment player 2's score BNE P%+5 INC player2Score+1 JSR Player2ee3 \ Print player 2's score LDA player2Score+1 \ If the score is less than the target, jump to oops1 to CMP player2Target+1 \ keep playing BCC oops1 LDA player2Score CMP player2Target BCC oops1 LDA #2 \ If we get here then player 2 has reached their target, JMP DEATH \ so set A to indicate that player 2 has won and jump to \ DEATH to end the game .oops1 \ --- End of added code -------------------------------> LDX #0 \ Fetch byte #8 (z_sign) for the ship attacking us, and LDY #8 \ set X = 0 LDA (INF),Y BMI OO1 \ If A is negative, then we got hit in the rear, so jump \ to OO1 to process damage to the aft shield \ --- Mod: Code removed for two-player Elite: ---------> \LDA FSH \ Otherwise the forward shield was damaged, so fetch the \SBC T \ shield strength from FSH and subtract the damage in T \ --- And replaced by: --------------------------------> LDA FSH \ Otherwise the forward shield was damaged, so fetch the SBC damageToApply \ shield strength from FSH and subtract the damage to \ apply \ --- End of replacement ------------------------------> BCC OO2 \ If the C flag is clear then this amount of damage was \ too much for the shields, so jump to OO2 to set the \ shield level to 0 and start taking damage directly \ from the energy banks STA FSH \ Store the new value of the forward shield in FSH RTS \ Return from the subroutine .OO2 STZ FSH \ Set the forward shield to 0 BCC OO3 \ Jump to OO3 to start taking damage directly from the \ energy banks (this BCC is effectively a JMP as the C \ flag is clear, as we jumped to OO2 with a BCC) .OO1 \ --- Mod: Code removed for two-player Elite: ---------> \LDA ASH \ The aft shield was damaged, so fetch the shield \SBC T \ strength from ASH and subtract the damage in T \ --- And replaced by: --------------------------------> LDA ASH \ The aft shield was damaged, so fetch the shield SBC damageToApply \ strength from ASH and subtract the damage to apply \ --- End of replacement ------------------------------> BCC OO5 \ If the C flag is clear then this amount of damage was \ too much for the shields, so jump to OO5 to set the \ shield level to 0 and start taking damage directly \ from the energy banks STA ASH \ Store the new value of the aft shield in ASH RTS \ Return from the subroutine .OO5 STZ ASH \ Set the aft shield to 0 .OO3 ADC ENERGY \ A is negative and contains the amount by which the STA ENERGY \ damage overwhelmed the shields, so this drains the \ energy banks by that amount (and because the energy \ banks are shown over four indicators rather than one, \ but with the same value range of 0-255, energy will \ appear to drain away four times faster than the \ shields did) \ --- Mod: Code removed for two-player Elite: ---------> \BEQ P%+4 \ If we have just run out of energy, skip the next \ \ instruction to jump straight to our death \ \BCS P%+5 \ If the C flag is set, then subtracting the damage from \ \ the energy banks didn't underflow, so we had enough \ \ energy to survive, and we can skip the next \ \ instruction to make a sound and take some damage \ \JMP DEATH \ Otherwise our energy levels are either 0 or negative, \ \ and in either case that means we jump to our DEATH, \ \ returning from the subroutine using a tail call \ \JSR EXNO3 \ We didn't die, so call EXNO3 to make the sound of a \ \ collision \ \JMP OUCH \ And jump to OUCH to take damage and return from the \ \ subroutine using a tail call \ --- And replaced by: --------------------------------> BEQ oops2 \ If we have just run out of energy, skip the next \ instruction to jump straight to our death BCS oops3 \ If the C flag is set, then subtracting the damage from \ the energy banks didn't underflow, so we had enough \ energy to survive, and we can jump to oops3 to make a \ damage sound .oops2 \ --- Mod: Code added for two-player Elite: -----------> STZ ENERGY \ Zero the energy levels JSR DIALS \ Update the dashboard to show the new energy levels \ --- End of added code -------------------------------> LDA #2 \ Set A to indicate that player 2 has won (as player 1 \ just ran out of energy) JMP DEATH \ Otherwise our energy levels are either 0 or negative, \ and in either case that means we jump to our DEATH, \ returning from the subroutine using a tail call .oops3 JMP EXNO3 \ We didn't die, so call EXNO3 to make the sound of a \ collision and return from the subroutine using a tail \ call \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: Player2OOPS \ Type: Subroutine \ Category: Flight \ Summary: Take some damage \ \ ------------------------------------------------------------------------------ \ \ We just took some damage, so reduce the shields if we have any, or reduce the \ energy levels and potentially take some damage to the cargo if we don't. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The amount of damage to take \ \ INF The address of the ship block for the ship that attacked \ us, or the ship that we just ran into \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Player2OOPS BIT gameOver \ If this is the game over screen, return from the BPL P%+3 \ subroutine without processing damage RTS STA damageToApply \ Store the amount of damage in damageToApply LDA player1GameType \ If player 1 is playing survival, they don't have a BEQ oopz1 \ score, so jump to oopz1 to skip updating the score \ Player 2 has been hit, so increment player 1's score JSR ee3 \ Print player 1's score to remove it from the screen INC player1Score \ Increment player 1's score BNE P%+5 INC player1Score+1 JSR ee3 \ Print player 1's score LDA player1Score+1 \ If the score is less than the target, jump to oopz1 to CMP player1Target+1 \ keep playing BCC oopz1 LDA player1Score CMP player1Target BCC oopz1 LDA #1 \ If we get here then player 1 has reached their target, JMP DEATH \ so set A to indicate that player 1 has won and jump to \ DEATH to end the game .oopz1 LDA K%+NI%*12+8 \ Fetch byte #8 (z_sign) for player 1 in player 2's \ frame of reference, so we can work out whether player \ 1 is in front of or behind player 2 when it hits \ player 2 with its lasers BMI oopz3 \ If A is negative, then player 2 got hit in the rear, \ so jump to oopz3 to process damage to the aft shield LDA player2FSH \ Otherwise the forward shield was damaged, so fetch the SBC damageToApply \ shield strength from FSH and subtract the damage to \ apply BCC oopz2 \ If the C flag is clear then this amount of damage was \ too much for the shields, so jump to oopz2 to set the \ shield level to 0 and start taking damage directly \ from the energy banks STA player2FSH \ Store the new value of the forward shield in FSH RTS \ Return from the subroutine .oopz2 STZ player2FSH \ Set the forward shield to 0 BCC oopz5 \ Jump to oopz5 to start taking damage directly from the \ energy banks (this BCC is effectively a JMP as the C \ flag is clear, as we jumped to oopz2 with a BCC) .oopz3 LDA player2ASH \ The aft shield was damaged, so fetch the shield SBC damageToApply \ strength from ASH and subtract the damage to apply BCC oopz4 \ If the C flag is clear then this amount of damage was \ too much for the shields, so jump to oopz4 to set the \ shield level to 0 and start taking damage directly \ from the energy banks STA player2ASH \ Store the new value of the aft shield in ASH RTS \ Return from the subroutine .oopz4 STZ player2ASH \ Set the aft shield to 0 .oopz5 ADC player2ENERGY \ A is negative and contains the amount by which the STA player2ENERGY \ damage overwhelmed the shields, so this drains the \ energy banks by that amount (and because the energy \ banks are shown over four indicators rather than one, \ but with the same value range of 0-255, energy will \ appear to drain away four times faster than the \ shields did) BEQ oopz6 \ If we have just run out of energy, skip the next \ instruction to jump straight to our death BCS oopz7 \ If the C flag is set, then subtracting the damage from \ the energy banks didn't underflow, so we had enough \ energy to survive, and we can jump to oopz7 to make a \ damage sound .oopz6 STZ player2ENERGY \ Zero the energy levels JSR DIALS \ Update the dashboard to show the new energy levels LDA #1 \ Set A to indicate that player 1 has won (as player 2 \ just ran out of energy) JMP DEATH \ Otherwise our energy levels are either 0 or negative, \ and in either case that means we jump to our DEATH, \ returning from the subroutine using a tail call .oopz7 JMP EXNO3 \ We didn't die, so call EXNO3 to make the sound of a \ collision and return from the subroutine using a tail \ call \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: SPS3 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Copy a space coordinate from the K% block into K3 \ \ ------------------------------------------------------------------------------ \ \ Copy one of the planet's coordinates into the corresponding location in the \ temporary variable K3. The high byte and absolute value of the sign byte are \ copied into the first two K3 bytes, and the sign of the sign byte is copied \ into the highest K3 byte. \ \ The comments below are written for copying the planet's x-coordinate into \ K3(2 1 0). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X Determines which coordinate to copy, and to where: \ \ * X = 0 copies (x_sign, x_hi) into K3(2 1 0) \ \ * X = 3 copies (y_sign, y_hi) into K3(5 4 3) \ \ * X = 6 copies (z_sign, z_hi) into K3(8 7 6) \ \ ****************************************************************************** .SPS3 LDA K%+1,X \ Copy x_hi into K3+X STA K3,X LDA K%+2,X \ Set A = Y = x_sign TAY AND #%01111111 \ Set K3+1 = |x_sign| STA K3+1,X TYA \ Set K3+2 = the sign of x_sign AND #%10000000 STA K3+2,X RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: Player1SPS3 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Copy a space coordinate from the K% block into K3 \ \ ------------------------------------------------------------------------------ \ \ Copy one of the planet's coordinates into the corresponding location in the \ temporary variable K3. The high byte and absolute value of the sign byte are \ copied into the first two K3 bytes, and the sign of the sign byte is copied \ into the highest K3 byte. \ \ The comments below are written for copying the planet's x-coordinate into \ K3(2 1 0). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X Determines which coordinate to copy, and to where: \ \ * X = 0 copies (x_sign, x_hi) into K3(2 1 0) \ \ * X = 3 copies (y_sign, y_hi) into K3(5 4 3) \ \ * X = 6 copies (z_sign, z_hi) into K3(8 7 6) \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Player1SPS3 LDA K%+NI%*12+1,X \ Copy x_hi into K3+X STA K3,X LDA K%+NI%*12+2,X \ Set A = Y = x_sign TAY AND #%01111111 \ Set K3+1 = |x_sign| STA K3+1,X TYA \ Set K3+2 = the sign of x_sign AND #%10000000 STA K3+2,X RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: Player2SPS3 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Copy a space coordinate from the K% block into K3 \ \ ------------------------------------------------------------------------------ \ \ Copy one of the planet's coordinates into the corresponding location in the \ temporary variable K3. The high byte and absolute value of the sign byte are \ copied into the first two K3 bytes, and the sign of the sign byte is copied \ into the highest K3 byte. \ \ The comments below are written for copying the planet's x-coordinate into \ K3(2 1 0). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X Determines which coordinate to copy, and to where: \ \ * X = 0 copies (x_sign, x_hi) into K3(2 1 0) \ \ * X = 3 copies (y_sign, y_hi) into K3(5 4 3) \ \ * X = 6 copies (z_sign, z_hi) into K3(8 7 6) \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Player2SPS3 LDA K%+NI%*2+1,X \ Copy x_hi into K3+X STA K3,X LDA K%+NI%*2+2,X \ Set A = Y = x_sign TAY AND #%01111111 \ Set K3+1 = |x_sign| STA K3+1,X TYA \ Set K3+2 = the sign of x_sign AND #%10000000 STA K3+2,X RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: GINF \ Type: Subroutine \ Category: Universe \ Summary: Fetch the address of a ship's data block into INF \ \ ------------------------------------------------------------------------------ \ \ Get the address of the data block for ship slot X and store it in INF. This \ address is fetched from the UNIV table, which stores the addresses of the 13 \ ship data blocks in workspace K%. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The ship slot number for which we want the data block \ address \ \ ****************************************************************************** .GINF TXA \ Set Y = X * 2 ASL A TAY LDA UNIV,Y \ Get the high byte of the address of the X-th ship STA INF \ from UNIV and store it in INF LDA UNIV+1,Y \ Get the low byte of the address of the X-th ship STA INF+1 \ from UNIV and store it in INF RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: NWSPS \ Type: Subroutine \ Category: Universe \ Summary: Add a new space station to our local bubble of universe \ \ ****************************************************************************** .NWSPS JSR SPBLB \ Light up the space station bulb on the dashboard LDX #%10000001 \ Set the AI flag in byte #32 to %10000001 (AI enabled, STX INWK+32 \ has an E.C.M.) LDX #0 \ Set pitch counter to 0 (no pitch, roll only) STX INWK+30 STX NEWB \ Set NEWB to %00000000, though this gets overridden by \ the default flags from E% in NWSHP below \STX INWK+31 \ This instruction is commented out in the original \ source. It would set the exploding state and missile \ count to 0 STX FRIN+1 \ Set the second slot in the FRIN table to 0, so when we \ fall through into NWSHP below, the new station that \ gets created will go into slot FRIN+1, as this will be \ the first empty slot that the routine finds DEX \ Set the roll counter to 255 (maximum anti-clockwise STX INWK+29 \ roll with no damping) LDX #10 \ Call NwS1 to flip the sign of nosev_x_hi (byte #10) JSR NwS1 JSR NwS1 \ And again to flip the sign of nosev_y_hi (byte #12) JSR NwS1 \ And again to flip the sign of nosev_z_hi (byte #14) LDA spasto \ Copy the address of the Coriolis space station's ship STA XX21+2*SST-2 \ blueprint from spasto to the #SST entry in the LDA spasto+1 \ blueprint lookup table at XX21, so when we spawn a STA XX21+2*SST-1 \ ship of type #SST, it will be a Coriolis station LDA tek \ If the system's tech level in tek is less than 10, CMP #10 \ jump to notadodo, so tech levels 0 to 9 have Coriolis BCC notadodo \ stations, while 10 and above will have Dodo stations LDA XX21+2*DOD-2 \ Copy the address of the Dodo space station's ship STA XX21+2*SST-2 \ blueprint from spasto to the #SST entry in the LDA XX21+2*DOD-1 \ blueprint lookup table at XX21, so when we spawn a STA XX21+2*SST-1 \ ship of type #SST, it will be a Dodo station .notadodo LDA #LO(LSO) \ Set bytes #33 and #34 to point to LSO for the ship STA INWK+33 \ line heap for the space station LDA #HI(LSO) STA INWK+34 LDA #SST \ Set A to the space station type, and fall through \ into NWSHP to finish adding the space station to the \ universe \ ****************************************************************************** \ \ Name: NWSHP \ Type: Subroutine \ Category: Universe \ Summary: Add a new ship to our local bubble of universe \ \ ------------------------------------------------------------------------------ \ \ This creates a new block of ship data in the K% workspace, allocates a new \ block in the ship line heap at WP, adds the new ship's type into the first \ empty slot in FRIN, and adds a pointer to the ship data into UNIV. If there \ isn't enough free memory for the new ship, it isn't added. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The type of the ship to add (see variable XX21 for a \ list of ship types) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag Set if the ship was successfully added, clear if it \ wasn't (as there wasn't enough free memory) \ \ INF Points to the new ship's data block in K% \ \ ****************************************************************************** .NWSHP STA T \ Store the ship type in location T LDX #0 \ Before we can add a new ship, we need to check \ whether we have an empty slot we can put it in. To do \ this, we need to loop through all the slots to look \ for an empty one, so set a counter in X that starts \ from the first slot at 0. When ships are killed, then \ the slots are shuffled down by the KILLSHP routine, so \ the first empty slot will always come after the last \ filled slot. This allows us to tack the new ship's \ data block and ship line heap onto the end of the \ existing ship data and heap, as shown in the memory \ map below .NWL1 LDA FRIN,X \ Load the ship type for the X-th slot BEQ NW1 \ If it is zero, then this slot is empty and we can use \ it for our new ship, so jump down to NW1 INX \ Otherwise increment X to point to the next slot \ --- Mod: Code removed for two-player Elite: ---------> \CPX #NOSH \ If we haven't reached the last slot yet, loop back up \BCC NWL1 \ to NWL1 to check the next slot (note that this means \ \ only slots from 0 to #NOSH - 1 are populated by this \ \ routine, but there is one more slot reserved in FRIN, \ \ which is used to identify the end of the slot list \ \ when shuffling the slots down in the KILLSHP routine) \ --- And replaced by: --------------------------------> CPX #5 \ If we haven't reached the last slot yet, loop back up BCC NWL1 \ to NWL1 to check the next slot \ \ For two-player Elite, the slots are fixed, like this: \ \ 0 = planet \ 1 = sun \ 2 = player 2 ship \ 3 = missile 1 \ 4 = missile 2 \ \ We set NOSH to 10 so that we can use the following \ slots for player 2's view: \ \ 10 = planet \ 11 = sun \ 12 = player 2 ship \ 13 = missile 1 \ 14 = missile 2 \ \ But we don't allow slots 5 and up to be used, so we \ need to check against slot #5 rather than #NOSH \ --- End of replacement ------------------------------> .NW3 CLC \ Otherwise we don't have an empty slot, so we can't RTS \ add a new ship, so clear the C flag to indicate that \ we have not managed to create the new ship, and return \ from the subroutine .NW1 \ If we get here, then we have found an empty slot at \ index X, so we can go ahead and create our new ship. \ We do that by creating a ship data block at INWK and, \ when we are done, copying the block from INWK into \ the K% workspace (specifically, to INF) \ --- Mod: Code added for two-player Elite: -----------> STX newShipSlot \ Store the slot number of the new ship in newShipSlot, \ so we return it from the subroutine \ --- End of added code -------------------------------> JSR GINF \ Get the address of the data block for ship slot X \ (which is in workspace K%) and store it in INF LDA T \ If the type of ship that we want to create is BMI NW2 \ negative, then this indicates a planet or sun, so \ jump down to NW2, as the next section sets up a ship \ data block, which doesn't apply to planets and suns, \ as they don't have things like shields, missiles, \ vertices and edges \ This is a ship, so first we need to set up various \ pointers to the ship blueprint we will need. The \ blueprints for each ship type in Elite are stored \ in a table at location XX21, so refer to the comments \ on that variable for more details on the data we're \ about to access ASL A \ Set Y = ship type * 2 TAY LDA XX21-1,Y \ The ship blueprints at XX21 start with a lookup \ table that points to the individual ship blueprints, \ so this fetches the high byte of this particular ship \ type's blueprint BEQ NW3 \ If the high byte is 0 then this is not a valid ship \ type, so jump to NW3 to clear the C flag and return \ from the subroutine STA XX0+1 \ This is a valid ship type, so store the high byte in \ XX0+1 LDA XX21-2,Y \ Fetch the low byte of this particular ship type's STA XX0 \ blueprint and store it in XX0, so XX0(1 0) now \ contains the address of this ship's blueprint CPY #2*SST \ If the ship type is a space station (SST), then jump BEQ NW6 \ to NW6, skipping the heap space steps below, as the \ space station has its own line heap at LSO (which it \ shares with the sun) \ We now want to allocate space for a heap that we can \ use to store the lines we draw for our new ship (so it \ can easily be erased from the screen again). SLSP \ points to the start of the current heap space, and we \ can extend it downwards with the heap for our new ship \ (as the heap space always ends just before the ship \ blueprints at D%) LDY #5 \ Fetch ship blueprint byte #5, which contains the LDA (XX0),Y \ maximum heap size required for plotting the new ship, STA T1 \ and store it in T1 LDA SLSP \ Take the 16-bit address in SLSP and subtract T1, SEC \ storing the 16-bit result in INWK(34 33), so this now SBC T1 \ points to the start of the line heap for our new ship STA INWK+33 LDA SLSP+1 SBC #0 STA INWK+34 \ We now need to check that there is enough free space \ for both this new line heap and the new data block \ for our ship. In memory, this is the layout of the \ ship data blocks and ship line heaps: \ \ +-----------------------------------+ \ | | \ | Ship blueprints | \ | | \ +-----------------------------------+ &D000 = D% \ | | \ | Current ship line heap | \ | | \ +-----------------------------------+ SLSP \ | | \ | Proposed heap for new ship | \ | | \ +-----------------------------------+ INWK(34 33) \ | | \ . . \ . . \ . . \ . . \ . . \ | | \ +-----------------------------------+ INF + NI% \ | | \ | Proposed data block for new ship | \ | | \ +-----------------------------------+ INF \ | | \ | Existing ship data blocks | \ | | \ +-----------------------------------+ &8200 = K% \ \ So, to work out if we have enough space, we have to \ make sure there is room between the end of our new \ ship data block at INF + NI%, and the start of the \ proposed heap for our new ship at the address we \ stored in INWK(34 33). Or, to put it another way, we \ and to make sure that: \ \ INWK(34 33) > INF + NI% \ \ which is the same as saying: \ \ INWK+33 - INF > NI% \ \ because INWK is in zero page, so INWK+34 = 0 LDA INWK+33 \ Calculate INWK+33 - INF, again using 16-bit \SEC \ arithmetic, and put the result in (A Y), so the high SBC INF \ byte is in A and the low byte in Y. The SEC TAY \ instruction is commented out in the original source; LDA INWK+34 \ as the previous subtraction will never underflow, it SBC INF+1 \ is superfluous BCC NW3+1 \ If we have an underflow from the subtraction, then \ INF > INWK+33 and we definitely don't have enough \ room for this ship, so jump to NW3+1, which returns \ from the subroutine (with the C flag already cleared) BNE NW4 \ If the subtraction of the high bytes in A is not \ zero, and we don't have underflow, then we definitely \ have enough space, so jump to NW4 to continue setting \ up the new ship CPY #NI% \ Otherwise the high bytes are the same in our BCC NW3+1 \ subtraction, so now we compare the low byte of the \ result (which is in Y) with NI%. This is the same as \ doing INWK+33 - INF > NI% (see above). If this isn't \ true, the C flag will be clear and we don't have \ enough space, so we jump to NW3+1, which returns \ from the subroutine (with the C flag already cleared) .NW4 LDA INWK+33 \ If we get here then we do have enough space for our STA SLSP \ new ship, so store the new bottom of the ship line LDA INWK+34 \ heap (i.e. INWK+33) in SLSP, doing both the high and STA SLSP+1 \ low bytes \ --- Mod: Code added for red enemy lasers: -----------> LDY #2 \ Set the Y2 coordinate of the laser line in the ship LDA #255 \ line heap to 255 so there is no laser line STA (INWK+33),Y \ --- End of added code -------------------------------> .NW6 LDY #14 \ Fetch ship blueprint byte #14, which contains the LDA (XX0),Y \ ship's energy, and store it in byte #35 STA INWK+35 \ --- Mod: Code removed for two-player Elite: ---------> \LDY #19 \ Fetch ship blueprint byte #19, which contains the \LDA (XX0),Y \ number of missiles and laser power, and AND with %111 \AND #%00000111 \ to extract the number of missiles before storing in \STA INWK+31 \ byte #31 \ --- And replaced by: --------------------------------> LDY #19 \ Fetch the configured number of missiles for player 2 LDA player2Missiles \ and store it in byte #31 STA INWK+31 \ --- End of replacement ------------------------------> LDA T \ Restore the ship type we stored above .NW2 STA FRIN,X \ Store the ship type in the X-th byte of FRIN, so the \ slot is now shown as occupied in the index table TAX \ Copy the ship type into X BMI NW8 \ If the ship type is negative (planet or sun), then \ jump to NW8 to skip the following instructions CPX #HER \ If the ship type is a rock hermit, jump to gangbang BEQ gangbang \ to increase the junk count CPX #JL \ If JL <= X < JH, i.e. the type of ship we added in X BCC NW7 \ is junk (escape pod, alloy plate, cargo canister, CPX #JH \ asteroid, splinter, Shuttle or Transporter), then keep BCS NW7 \ going, otherwise jump to NW7 .gangbang INC JUNK \ We're adding junk, so increase the junk counter .NW7 INC MANY,X \ Increment the total number of ships of type X .NW8 LDY T \ Restore the ship type we stored above LDA E%-1,Y \ Fetch the E% byte for this ship to get the default \ settings for the ship's NEWB flags AND #%01101111 \ Zero bits 4 and 7 (so the new ship is not docking, has \ not been scooped, and has not just docked) ORA NEWB \ Apply the result to the ship's NEWB flags, which sets STA NEWB \ bits 0-3 and 5-6 in NEWB if they are set in the E% \ byte LDY #NI%-1 \ The final step is to copy the new ship's data block \ from INWK to INF, so set up a counter for NI% bytes \ in Y .NWL3 LDA INWK,Y \ Load the Y-th byte of INWK and store in the Y-th byte STA (INF),Y \ of the workspace pointed to by INF DEY \ Decrement the loop counter BPL NWL3 \ Loop back for the next byte until we have copied them \ all over SEC \ We have successfully created our new ship, so set the \ C flag to indicate success RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: NwS1 \ Type: Subroutine \ Category: Universe \ Summary: Flip the sign and double an INWK byte \ \ ------------------------------------------------------------------------------ \ \ Flip the sign of the INWK byte at offset X, and increment X by 2. This is \ used by the space station creation routine at NWSPS. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The offset of the INWK byte to be flipped \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X X is incremented by 2 \ \ ****************************************************************************** .NwS1 LDA INWK,X \ Load the X-th byte of INWK into A and flip bit 7, EOR #%10000000 \ storing the result back in the X-th byte of INWK STA INWK,X INX \ Add 2 to X INX RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: ABORT \ Type: Subroutine \ Category: Dashboard \ Summary: Disarm missiles and update the dashboard indicators \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ Y The new colour of the missile indicator: \ \ * &00 = black (no missile) \ \ * #RED2 = red (armed and locked) \ \ * #YELLOW2 = yellow/white (armed) \ \ * #GREEN2 = green (disarmed) \ \ ****************************************************************************** .ABORT LDX #&FF \ Set X to &FF, which is the value of MSTG when we have \ no target lock for our missile \ Fall through into ABORT2 to set the missile lock to \ the value in X, which effectively disarms the missile \ ****************************************************************************** \ \ Name: ABORT2 \ Type: Subroutine \ Category: Dashboard \ Summary: Set/unset the lock target for a missile and update the dashboard \ \ ------------------------------------------------------------------------------ \ \ Set the lock target for the leftmost missile and update the dashboard. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The slot number of the ship to lock our missile onto, or \ &FF to remove missile lock \ \ Y The new colour of the missile indicator: \ \ * &00 = black (no missile) \ \ * #RED2 = red (armed and locked) \ \ * #YELLOW2 = yellow/white (armed) \ \ * #GREEN2 = green (disarmed) \ \ ****************************************************************************** .ABORT2 STX MSTG \ Store the target of our missile lock in MSTG \ --- Mod: Code added for two-player Elite: -----------> LDA #1 \ Set A to draw player 1's indicators \ --- End of added code -------------------------------> LDX NOMSL \ Call MSBAR to update the leftmost indicator in the JSR MSBAR \ dashboard's missile bar, which returns with Y = 0 STY MSAR \ Set MSAR = 0 to indicate that the leftmost missile \ is no longer seeking a target lock RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: Player2ABORT \ Type: Subroutine \ Category: Dashboard \ Summary: Disarm missiles and update the dashboard indicators \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ Y The new colour of the missile indicator: \ \ * &00 = black (no missile) \ \ * #RED2 = red (armed and locked) \ \ * #YELLOW2 = yellow/white (armed) \ \ * #GREEN2 = green (disarmed) \ \ ****************************************************************************** .Player2ABORT LDX #&FF \ Set X to &FF, which is the value of MSTG when we have \ no target lock for our missile \ Fall through into ABORT2 to set the missile lock to \ the value in X, which effectively disarms the missile \ ****************************************************************************** \ \ Name: Player2ABORT2 \ Type: Subroutine \ Category: Dashboard \ Summary: Set/unset the lock target for a missile and update the dashboard \ \ ------------------------------------------------------------------------------ \ \ Set the lock target for the leftmost missile and update the dashboard. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The slot number of the ship to lock our missile onto, or \ &FF to remove missile lock \ \ Y The new colour of the missile indicator: \ \ * &00 = black (no missile) \ \ * #RED2 = red (armed and locked) \ \ * #YELLOW2 = yellow/white (armed) \ \ * #GREEN2 = green (disarmed) \ \ ****************************************************************************** .Player2ABORT2 STX player2MSTG \ Store the target of our missile lock in MSTG LDA #2 \ Set A to draw player 2's indicators LDX player2NOMSL \ Call MSBAR to update the leftmost indicator in the JSR MSBAR \ dashboard's missile bar, which returns with Y = 0 STY player2MSAR \ Set MSAR = 0 to indicate that the leftmost missile \ is no longer seeking a target lock RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: Player2ECBLB2 \ Type: Subroutine \ Category: Dashboard \ Summary: Start up the E.C.M. (light up the indicator, start the countdown \ and make the E.C.M. sound) \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Player2ECBLB2 LDA #32 \ Set the E.C.M. countdown timer in ECMA to 32 STA player2ECMA ASL A \ Call the NOISE routine with A = 64 to make the sound JSR NOISE \ of the E.C.M. being switched on JMP SPBLB \ Jump to SPBLB to light up the E.C.M. bulb \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: ECBLB2 \ Type: Subroutine \ Category: Dashboard \ Summary: Start up the E.C.M. (light up the indicator, start the countdown \ and make the E.C.M. sound) \ \ ****************************************************************************** .ECBLB2 LDA #32 \ Set the E.C.M. countdown timer in ECMA to 32 STA ECMA ASL A \ Call the NOISE routine with A = 64 to make the sound JSR NOISE \ of the E.C.M. being switched on \ Fall through into ECBLB to light up the E.C.M. bulb \ ****************************************************************************** \ \ Name: ECBLB \ Type: Subroutine \ Category: Dashboard \ Summary: Light up the E.C.M. indicator bulb ("E") on the dashboard by \ sending a #DOBULB 255 command to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ This draws (or erases) the E.C.M. indicator bulb ("E") on the dashboard. \ \ ****************************************************************************** .ECBLB LDA #DOBULB \ Send a #DOBULB 255 command to the I/O processor to JSR OSWRCH \ tell it to draw the E.C.M. indicator bulb on the LDA #255 \ dashboard, and return from the subroutine using a tail JMP OSWRCH \ call \ ****************************************************************************** \ \ Name: SPBLB \ Type: Subroutine \ Category: Dashboard \ Summary: Light up the space station indicator ("S") on the dashboard by \ sending a #DOBULB 0 command to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ This draws (or erases) the space station indicator bulb ("S") on the \ dashboard. \ \ ****************************************************************************** .SPBLB LDA #DOBULB \ Send a #DOBULB 0 command to the I/O processor to JSR OSWRCH \ tell it to draw the E.C.M. indicator bulb on the LDA #0 \ dashboard, and return from the subroutine using a tail JMP OSWRCH \ call \ ****************************************************************************** \ \ Name: MSBAR \ Type: Subroutine \ Category: Dashboard \ Summary: Draw a specific indicator in the dashboard's missile bar by \ sending a #DOmsbar command to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ Each indicator is a rectangle that's 3 pixels wide and 5 pixels high. If the \ indicator is set to black, this effectively removes a missile. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The number of the missile indicator to update (counting \ from right to left, so indicator NOMSL is the leftmost \ indicator) \ \ Y The new colour of the missile indicator: \ \ * &00 = black (no missile) \ \ * &0E = red (armed and locked) \ \ * &E0 = yellow/white (armed) \ \ * &EE = green/cyan (disarmed) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X X is preserved \ \ Y Y is set to 0 \ \ ****************************************************************************** .msbpars \ --- Mod: Code removed for two-player Elite: ---------> \EQUB 4 \ The number of bytes to transmit with this command \ --- And replaced by: --------------------------------> EQUB 5 \ The number of bytes to transmit with this command \ --- End of replacement ------------------------------> EQUB 0 \ The number of bytes to receive with this command EQUB 0 \ The number of the missile indicator to update EQUB 0 \ The colour of the missile indicator \ --- Mod: Code added for two-player Elite: -----------> EQUB 0 \ The player number (1 or 2), passed in A \ --- End of added code -------------------------------> EQUB 0 \ End of the parameter block .MSBAR \ --- Mod: Code added for two-player Elite: -----------> STA msbpars+4 \ Store the player number in byte #4 of the parameter \ block above \ --- End of added code -------------------------------> PHX \ Store the indicator number on the stack so we can \ retrieve it later STX msbpars+2 \ Store the indicator number in byte #2 of the parameter \ block above STY msbpars+3 \ Store the indicator colour in byte #3 of the parameter \ block above PHY \ Store the indicator colour on the stack so we can \ retrieve it later LDX #LO(msbpars) \ Set (Y X) to point to the parameter block above LDY #HI(msbpars) LDA #DOmsbar \ Send a #DOmsbar command to the I/O processor to update JSR OSWORD \ the missile indicator on the dashboard LDY #0 \ Set Y = 0, so we can return it from the subroutine PLA \ Restore the indicator colour from the stack into A PLX \ Restore the indicator number from the stack into X RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PROJ \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Project the current ship or planet onto the screen \ Deep dive: Extended screen coordinates \ \ ------------------------------------------------------------------------------ \ \ Project the current ship's location or the planet onto the screen, either \ returning the screen coordinates of the projection (if it's on-screen), or \ returning an error via the C flag. \ \ In this context, "on-screen" means that the point is projected into the \ following range: \ \ centre of screen - 1024 < x < centre of screen + 1024 \ centre of screen - 1024 < y < centre of screen + 1024 \ \ This is to cater for ships (and, more likely, planets and suns) whose centres \ are off-screen but whose edges may still be visible. \ \ The projection calculation is: \ \ K3(1 0) = #X + x / z \ K4(1 0) = #Y + y / z \ \ where #X and #Y are the pixel x-coordinate and y-coordinate of the centre of \ the screen. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ INWK The ship data block for the ship to project on-screen \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ K3(1 0) The x-coordinate of the ship's projection on-screen \ \ K4(1 0) The y-coordinate of the ship's projection on-screen \ \ C flag Set if the ship's projection doesn't fit on the screen, \ clear if it does project onto the screen \ \ A Contains K4+1, the high byte of the y-coordinate \ \ ****************************************************************************** .PROJ LDA INWK \ Set P(1 0) = (x_hi x_lo) STA P \ = x LDA INWK+1 STA P+1 LDA INWK+2 \ Set A = x_sign JSR PLS6 \ Call PLS6 to calculate: \ \ (X K) = (A P+1 P) / (z_sign z_hi z_lo) \ = (x_sign x_hi x_lo) / (z_sign z_hi z_lo) \ = x / z BCS PL2-1 \ If the C flag is set then the result overflowed and \ the coordinate doesn't fit on the screen, so return \ from the subroutine with the C flag set (as PL2-1 \ contains an RTS) LDA K \ Set K3(1 0) = (X K) + #X ADC #X \ = #X + x / z STA K3 \ \ first doing the low bytes TXA \ And then the high bytes. #X is the x-coordinate of ADC #0 \ the centre of the space view, so this converts the STA K3+1 \ space x-coordinate into a screen x-coordinate LDA INWK+3 \ Set P(1 0) = (y_hi y_lo) STA P LDA INWK+4 STA P+1 LDA INWK+5 \ Set A = -y_sign EOR #%10000000 JSR PLS6 \ Call PLS6 to calculate: \ \ (X K) = (A P+1 P) / (z_sign z_hi z_lo) \ = -(y_sign y_hi y_lo) / (z_sign z_hi z_lo) \ = -y / z BCS PL2-1 \ If the C flag is set then the result overflowed and \ the coordinate doesn't fit on the screen, so return \ from the subroutine with the C flag set (as PL2-1 \ contains an RTS) LDA K \ Set K4(1 0) = (X K) + #Y ADC #Y \ = #Y - y / z STA K4 \ \ first doing the low bytes TXA \ And then the high bytes. #Y is the y-coordinate of ADC #0 \ the centre of the space view, so this converts the STA K4+1 \ space y-coordinate into a screen y-coordinate CLC \ Clear the C flag to indicate success RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PL2 \ Type: Subroutine \ Category: Drawing planets \ Summary: Remove the planet or sun from the screen \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ PL2-1 Contains an RTS \ \ ****************************************************************************** .PL2 LDA TYPE \ Shift bit 0 of the planet/sun's type into the C flag LSR A BCS PL57 \ If the planet/sun's type has bit 0 clear, then it's \ either 128 or 130, which is a planet; meanwhile, the \ sun has type 129, which has bit 0 set. So if this is \ the sun, jump to PL57 to skip the following \ instructions \ --- Mod: Code removed for flicker-free planets: -----> \JSR LS2FL \ Call LS2FL to send the ball line heap to the I/O \ \ processor for drawing on-screen, which redraws the \ \ planet and this removes it from the screen \ \STZ LSP \ Reset the ball line heap by setting the ball line heap \ \ pointer to 0 \ \RTS \ Return from the subroutine \ --- And replaced by: --------------------------------> JMP WPLS2 \ This is the planet, so jump to WPLS2 to remove it from \ screen, returning from the subroutine using a tail \ call \ --- End of replacement ------------------------------> .PL57 JMP WPLS \ This is the sun, so jump to WPLS to remove it from \ screen, returning from the subroutine using a tail \ call \ ****************************************************************************** \ \ Name: PLANET \ Type: Subroutine \ Category: Drawing planets \ Summary: Draw the planet or sun \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ INWK The planet or sun's ship data block \ \ ****************************************************************************** .PLANET LDA #GREEN \ Send a #SETCOL GREEN command to the I/O processor to JSR DOCOL \ switch to stripe 3-1-3-1, which is cyan/yellow in the \ space view LDA INWK+8 \ Set A = z_sign (the highest byte in the planet/sun's \ coordinates) \BMI PL2 \ This instruction is commented out in the original \ source. It would remove the planet from the screen \ when it's behind us CMP #48 \ If A >= 48 then the planet/sun is too far away to be BCS PL2 \ seen, so jump to PL2 to remove it from the screen, \ returning from the subroutine using a tail call ORA INWK+7 \ Set A to 0 if both z_sign and z_hi are 0 BEQ PL2 \ If both z_sign and z_hi are 0, then the planet/sun is \ too close to be shown, so jump to PL2 to remove it \ from the screen, returning from the subroutine using a \ tail call JSR PROJ \ Project the planet/sun onto the screen, returning the \ centre's coordinates in K3(1 0) and K4(1 0) BCS PL2 \ If the C flag is set by PROJ then the planet/sun is \ not visible on-screen, so jump to PL2 to remove it \ from the screen, returning from the subroutine using \ a tail call \ --- Mod: Code added for two-player Elite: -----------> LDA K4 \ We know that the planet's radius is 19 pixels (as it CMP #Y/2-20 \ is a fixed size), so we can add an extra check to BCC PL2 \ make sure the centre of the planet is within the CMP #Y+Y/2+20 \ height of the player's view and, say, 20 pixels either BCS PL2 \ side, and if it isn't jump to PL2 to remove it from \ the screen \ --- End of added code -------------------------------> LDA #96 \ Set (A P+1 P) = (0 96 0) = 24576 STA P+1 \ LDA #0 \ This represents the planet/sun's radius at a distance STA P \ of z = 1 JSR DVID3B2 \ Call DVID3B2 to calculate: \ \ K(3 2 1 0) = (A P+1 P) / (z_sign z_hi z_lo) \ = (0 96 0) / z \ = 24576 / z \ \ so K now contains the planet/sun's radius, reduced by \ the actual distance to the planet/sun. We know that \ K+3 and K+2 will be 0, as the number we are dividing, \ (0 96 0), fits into the two bottom bytes, so the \ result is actually in K(1 0) LDA K+1 \ If the high byte of the reduced radius is zero, jump BEQ PL82 \ to PL82, as K contains the radius on its own LDA #248 \ Otherwise set K = 248, to round up the radius in STA K \ K(1 0) to the nearest integer (if we consider the low \ byte to be the fractional part) .PL82 LDA TYPE \ If the planet/sun's type has bit 0 clear, then it's LSR A \ either 128 or 130, which is a planet (the sun has type BCC PL9 \ 129, which has bit 0 set). So jump to PL9 to draw the \ planet with radius K, returning from the subroutine \ using a tail call JMP SUN \ Otherwise jump to SUN to draw the sun with radius K, \ returning from the subroutine using a tail call \ ****************************************************************************** \ \ Name: PL9 (Part 1 of 3) \ Type: Subroutine \ Category: Drawing planets \ Summary: Draw the planet, with either an equator and meridian, or a crater \ \ ------------------------------------------------------------------------------ \ \ Draw the planet with radius K at pixel coordinate (K3, K4), and with either an \ equator and meridian, or a crater. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ K(1 0) The planet's radius \ \ K3(1 0) Pixel x-coordinate of the centre of the planet \ \ K4(1 0) Pixel y-coordinate of the centre of the planet \ \ INWK The planet's ship data block \ \ ****************************************************************************** .PL9 \ --- Mod: Code removed for flicker-free planets: -----> \JSR LS2FL \ Call LS2FL to send the ball line heap to the I/O \ \ processor for drawing on-screen, which will erase the \ \ planet from the screen \ \STZ LSP \ Reset the ball line heap by setting the ball line heap \ \ pointer to 0 \ \JSR CIRCLE \ Call CIRCLE to draw the planet's new circle \ \BCS PL20 \ If the call to CIRCLE returned with the C flag set, \ \ then the circle does not fit on-screen, so jump to \ \ PL20 to return from the subroutine \ --- And replaced by: --------------------------------> JSR SetPlayerBallLine \ Use the correct ball line for the player view we are \ drawing JSR CIRCLE \ Call CIRCLE to draw the planet's new circle BCS PL20A \ If the call to CIRCLE returned with the C flag set, \ then the circle does not fit on-screen, so jump to \ PL20A to remove the planet from the screen and return \ from the subroutine \ --- End of replacement ------------------------------> LDA K+1 \ If K+1 is zero, jump to PL25 as K(1 0) < 256, so the BEQ PL25 \ planet fits on the screen and we can draw meridians or \ craters .PL20 \ --- Mod: Code removed for flicker-free planets: -----> \JMP LS2FL \ The planet doesn't fit on-screen, so jump to LS2FL to \ \ send the ball line heap to the I/O processor for \ \ drawing on-screen, returning from the subroutine using \ \ a tail call \ --- And replaced by: --------------------------------> JMP EraseRestOfPlanet \ We have drawn the new circle, so now we need to erase \ any lines that are left in the ball line heap, before \ returning from the subroutine using a tail call .PL20A JMP WPLS2 \ Call WPLS2 to remove the planet from the screen \ --- End of replacement ------------------------------> .PL25 LDA TYPE \ If the planet type is 128 then it has an equator and CMP #128 \ a meridian, so this jumps to PL26 if this is not a BNE PL26 \ planet with an equator - in other words, if it is a \ planet with a crater \ Otherwise this is a planet with an equator and \ meridian, so fall through into the following to draw \ them \ ****************************************************************************** \ \ Name: PL9 (Part 2 of 3) \ Type: Subroutine \ Category: Drawing planets \ Summary: Draw the planet's equator and meridian \ Deep dive: Drawing meridians and equators \ \ ------------------------------------------------------------------------------ \ \ Draw the planet's equator and meridian. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ K(1 0) The planet's radius \ \ K3(1 0) Pixel x-coordinate of the centre of the planet \ \ K4(1 0) Pixel y-coordinate of the centre of the planet \ \ INWK The planet's ship data block \ \ ****************************************************************************** LDA K \ If the planet's radius is less than 6, the planet is CMP #6 \ too small to show a meridian, so jump to PL20 to BCC PL20 \ return from the subroutine LDA INWK+14 \ Set P = -nosev_z_hi EOR #%10000000 STA P LDA INWK+20 \ Set A = roofv_z_hi JSR PLS4 \ Call PLS4 to calculate the following: \ \ CNT2 = arctan(P / A) / 4 \ = arctan(-nosev_z_hi / roofv_z_hi) / 4 \ \ and do the following if nosev_z_hi >= 0: \ \ CNT2 = CNT2 + PI LDX #9 \ Set X to 9 so the call to PLS1 divides nosev_x JSR PLS1 \ Call PLS1 to calculate the following: STA K2 \ STY XX16 \ (XX16 K2) = nosev_x / z \ \ and increment X to point to nosev_y for the next call JSR PLS1 \ Call PLS1 to calculate the following: STA K2+1 \ STY XX16+1 \ (XX16+1 K2+1) = nosev_y / z LDX #15 \ Set X to 15 so the call to PLS5 divides roofv_x JSR PLS5 \ Call PLS5 to calculate the following: \ \ (XX16+2 K2+2) = roofv_x / z \ \ (XX16+3 K2+3) = roofv_y / z JSR PLS2 \ Call PLS2 to draw the first meridian LDA INWK+14 \ Set P = -nosev_z_hi EOR #%10000000 STA P LDA INWK+26 \ Set A = sidev_z_hi, so the second meridian will be at \ 90 degrees to the first JSR PLS4 \ Call PLS4 to calculate the following: \ \ CNT2 = arctan(P / A) / 4 \ = arctan(-nosev_z_hi / sidev_z_hi) / 4 \ \ and do the following if nosev_z_hi >= 0: \ \ CNT2 = CNT2 + PI LDX #21 \ Set X to 21 so the call to PLS5 divides sidev_x JSR PLS5 \ Call PLS5 to calculate the following: \ \ (XX16+2 K2+2) = sidev_x / z \ \ (XX16+3 K2+3) = sidev_y / z \ --- Mod: Code removed for flicker-free planets: -----> \JSR PLS2 \ Call PLS2 to draw the second meridian \ \JMP LS2FL \ Jump to LS2FL to send the ball line heap to the I/O \ \ processor for drawing on-screen, returning from the \ \ subroutine using a tail call \ --- And replaced by: --------------------------------> JMP PLS2 \ Jump to PLS2 to draw the second meridian, returning \ from the subroutine using a tail call \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: PL9 (Part 3 of 3) \ Type: Subroutine \ Category: Drawing planets \ Summary: Draw the planet's crater \ Deep dive: Drawing craters \ \ ------------------------------------------------------------------------------ \ \ Draw the planet's crater. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ K(1 0) The planet's radius \ \ K3(1 0) Pixel x-coordinate of the centre of the planet \ \ K4(1 0) Pixel y-coordinate of the centre of the planet \ \ INWK The planet's ship data block \ \ ****************************************************************************** .PL26 LDA INWK+20 \ Set A = roofv_z_hi BMI PL20 \ If A is negative, the crater is on the far side of the \ planet, so return from the subroutine (as PL2 \ contains an RTS) LDX #15 \ Set X = 15, so the following call to PLS3 operates on \ roofv JSR PLS3 \ Call PLS3 to calculate: \ \ (Y A P) = 222 * roofv_x / z \ \ to give the x-coordinate of the crater offset and \ increment X to point to roofv_y for the next call CLC \ Calculate: ADC K3 \ STA K3 \ K3(1 0) = (Y A) + K3(1 0) \ = 222 * roofv_x / z + x-coordinate of planet \ centre \ \ starting with the high bytes TYA \ And then doing the low bytes, so now K3(1 0) contains ADC K3+1 \ the x-coordinate of the crater offset plus the planet STA K3+1 \ centre to give the x-coordinate of the crater's centre JSR PLS3 \ Call PLS3 to calculate: \ \ (Y A P) = 222 * roofv_y / z \ \ to give the y-coordinate of the crater offset STA P \ Calculate: LDA K4 \ SEC \ K4(1 0) = K4(1 0) - (Y A) SBC P \ = 222 * roofv_y / z - y-coordinate of planet STA K4 \ centre \ \ starting with the low bytes STY P \ And then doing the low bytes, so now K4(1 0) contains LDA K4+1 \ the y-coordinate of the crater offset plus the planet SBC P \ centre to give the y-coordinate of the crater's centre STA K4+1 LDX #9 \ Set X = 9, so the following call to PLS1 operates on \ nosev JSR PLS1 \ Call PLS1 to calculate the following: \ \ (Y A) = nosev_x / z \ \ and increment X to point to nosev_y for the next call LSR A \ Set (XX16 K2) = (Y A) / 2 STA K2 STY XX16 JSR PLS1 \ Call PLS1 to calculate the following: \ \ (Y A) = nosev_y / z \ \ and increment X to point to nosev_z for the next call LSR A \ Set (XX16+1 K2+1) = (Y A) / 2 STA K2+1 STY XX16+1 LDX #21 \ Set X = 21, so the following call to PLS1 operates on \ sidev JSR PLS1 \ Call PLS1 to calculate the following: \ \ (Y A) = sidev_x / z \ \ and increment X to point to sidev_y for the next call LSR A \ Set (XX16+2 K2+2) = (Y A) / 2 STA K2+2 STY XX16+2 JSR PLS1 \ Call PLS1 to calculate the following: \ \ (Y A) = sidev_y / z \ \ and increment X to point to sidev_z for the next call LSR A \ Set (XX16+3 K2+3) = (Y A) / 2 STA K2+3 STY XX16+3 LDA #64 \ Set TGT = 64, so we draw a full ellipse in the call to STA TGT \ PLS22 below STZ CNT2 \ Set CNT2 = 0 as we are drawing a full ellipse, so we \ don't need to apply an offset \ --- Mod: Code removed for flicker-free planets: -----> \JSR PLS22 \ Call PLS22 to draw the crater \ \JMP LS2FL \ Jump to LS2FL to send the ball line heap to the I/O \ \ processor for drawing on-screen, returning from the \ \ subroutine using a tail call \ --- And replaced by: --------------------------------> JMP PLS22 \ Jump to PLS22 to draw the crater, returning from the \ subroutine using a tail call \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: PLS1 \ Type: Subroutine \ Category: Drawing planets \ Summary: Calculate (Y A) = nosev_x / z \ \ ------------------------------------------------------------------------------ \ \ Calculate the following division of a specified value from one of the \ orientation vectors (in this example, nosev_x): \ \ (Y A) = nosev_x / z \ \ where z is the z-coordinate of the planet from INWK. The result is an 8-bit \ magnitude in A, with maximum value 254, and just a sign bit (bit 7) in Y. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X Determines which of the INWK orientation vectors to \ divide: \ \ * X = 9, 11, 13: divides nosev_x, nosev_y, nosev_z \ \ * X = 15, 17, 19: divides roofv_x, roofv_y, roofv_z \ \ * X = 21, 23, 25: divides sidev_x, sidev_y, sidev_z \ \ INWK The planet's ship data block \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A The result as an 8-bit magnitude with maximum value 254 \ \ Y The sign of the result in bit 7 \ \ K+3 Also the sign of the result in bit 7 \ \ X X gets incremented by 2 so it points to the next \ coordinate in this orientation vector (so consecutive \ calls to the routine will start with x, then move onto y \ and then z) \ \ ****************************************************************************** .PLS1 LDA INWK,X \ Set P = nosev_x_lo STA P LDA INWK+1,X \ Set P+1 = |nosev_x_hi| AND #%01111111 STA P+1 LDA INWK+1,X \ Set A = sign bit of nosev_x_lo AND #%10000000 JSR DVID3B2 \ Call DVID3B2 to calculate: \ \ K(3 2 1 0) = (A P+1 P) / (z_sign z_hi z_lo) LDA K \ Fetch the lowest byte of the result into A LDY K+1 \ Fetch the second byte of the result into Y BEQ P%+4 \ If the second byte is 0, skip the next instruction LDA #254 \ The second byte is non-zero, so the result won't fit \ into one byte, so set A = 254 as our maximum one-byte \ value to return LDY K+3 \ Fetch the sign of the result from K+3 into Y INX \ Add 2 to X so the index points to the next coordinate INX \ in this orientation vector (so consecutive calls to \ the routine will start with x, then move onto y and z) RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PLS2 \ Type: Subroutine \ Category: Drawing planets \ Summary: Draw a half-ellipse \ Deep dive: Drawing ellipses \ Drawing meridians and equators \ \ ------------------------------------------------------------------------------ \ \ Draw a half-ellipse, used for the planet's equator and meridian. \ \ ****************************************************************************** .PLS2 LDA #31 \ Set TGT = 31, so we only draw half an ellipse STA TGT \ Fall through into PLS22 to draw the half-ellipse \ ****************************************************************************** \ \ Name: PLS22 \ Type: Subroutine \ Category: Drawing planets \ Summary: Draw an ellipse or half-ellipse \ Deep dive: Drawing ellipses \ Drawing meridians and equators \ Drawing craters \ \ ------------------------------------------------------------------------------ \ \ Draw an ellipse or half-ellipse, to be used for the planet's equator and \ meridian (in which case we draw half an ellipse), or crater (in which case we \ draw a full ellipse). \ \ The ellipse is defined by a centre point, plus two conjugate radius vectors, \ u and v, where: \ \ u = [ u_x ] v = [ v_x ] \ [ u_y ] [ v_y ] \ \ The individual components of these 2D vectors (i.e. u_x, u_y etc.) are 16-bit \ sign-magnitude numbers, where the high bytes contain only the sign bit (in \ bit 7), with bits 0 to 6 being clear. This means that as we store u_x as \ (XX16 K2), for example, we know that |u_x| = K2. \ \ This routine calls BLINE to draw each line segment in the ellipse, passing the \ coordinates as follows: \ \ K6(1 0) = K3(1 0) + u_x * cos(CNT2) + v_x * sin(CNT2) \ \ K6(3 2) = K4(1 0) - u_y * cos(CNT2) - v_y * sin(CNT2) \ \ The y-coordinates are negated because BLINE expects pixel coordinates but the \ u and v vectors are extracted from the orientation vector. The y-axis runs \ in the opposite direction in 3D space to that on the screen, so we need to \ negate the 3D space coordinates before we can combine them with the ellipse's \ centre coordinates. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ K(1 0) The planet's radius \ \ K3(1 0) The pixel x-coordinate of the centre of the ellipse \ \ K4(1 0) The pixel y-coordinate of the centre of the ellipse \ \ (XX16 K2) The x-component of u (i.e. u_x), where XX16 contains \ just the sign of the sign-magnitude number \ \ (XX16+1 K2+1) The y-component of u (i.e. u_y), where XX16+1 contains \ just the sign of the sign-magnitude number \ \ (XX16+2 K2+2) The x-component of v (i.e. v_x), where XX16+2 contains \ just the sign of the sign-magnitude number \ \ (XX16+3 K2+3) The y-component of v (i.e. v_y), where XX16+3 contains \ just the sign of the sign-magnitude number \ \ TGT The number of segments to draw: \ \ * 32 for a half ellipse (a meridian) \ \ * 64 for a full ellipse (a crater) \ \ CNT2 The starting segment for drawing the half-ellipse \ \ ****************************************************************************** .PLS22 LDX #0 \ Set CNT = 0 STX CNT DEX \ Set FLAG = &FF to start a new line in the ball line STX FLAG \ heap when calling BLIN below, so the crater or \ meridian is separate from any previous ellipses .PLL4 LDA CNT2 \ Set X = CNT2 mod 32 AND #31 \ TAX \ So X is the starting segment, reduced to the range 0 \ to 32, so as there are 64 segments in the circle, this \ reduces the starting angle to 0 to 180 degrees, so we \ can use X as an index into the sine table (which only \ contains values for segments 0 to 31) \ \ Also, because CNT2 mod 32 is in the range 0 to 180 \ degrees, we know that sin(CNT2 mod 32) is always \ positive, or to put it another way: \ \ sin(CNT2 mod 32) = |sin(CNT2)| LDA SNE,X \ Set Q = sin(X) STA Q \ = sin(CNT2 mod 32) \ = |sin(CNT2)| LDA K2+2 \ Set A = K2+2 \ = |v_x| JSR FMLTU \ Set R = A * Q / 256 STA R \ = |v_x| * |sin(CNT2)| LDA K2+3 \ Set A = K2+3 \ = |v_y| JSR FMLTU \ Set K = A * Q / 256 STA K \ = |v_y| * |sin(CNT2)| LDX CNT2 \ If CNT2 >= 33 then this sets the C flag, otherwise CPX #33 \ it's clear, so this means that: \ \ * C is clear if the segment starts in the first half \ of the circle, 0 to 180 degrees \ \ * C is set if the segment starts in the second half \ of the circle, 180 to 360 degrees \ \ In other words, the C flag contains the sign bit for \ sin(CNT2), which is positive for 0 to 180 degrees \ and negative for 180 to 360 degrees LDA #0 \ Shift the C flag into the sign bit of XX16+5, so ROR A \ XX16+5 has the correct sign for sin(CNT2) STA XX16+5 \ \ Because we set the following above: \ \ K = |v_y| * |sin(CNT2)| \ R = |v_x| * |sin(CNT2)| \ \ we can add XX16+5 as the high byte to give us the \ following: \ \ (XX16+5 K) = |v_y| * sin(CNT2) \ (XX16+5 R) = |v_x| * sin(CNT2) LDA CNT2 \ Set X = (CNT2 + 16) mod 32 CLC \ ADC #16 \ So we can use X as a lookup index into the SNE table AND #31 \ to get the cosine (as there are 16 segments in a TAX \ quarter-circle) \ \ Also, because the sine table only contains positive \ values, we know that sin((CNT2 + 16) mod 32) will \ always be positive, or to put it another way: \ \ sin((CNT2 + 16) mod 32) = |cos(CNT2)| LDA SNE,X \ Set Q = sin(X) STA Q \ = sin((CNT2 + 16) mod 32) \ = |cos(CNT2)| LDA K2+1 \ Set A = K2+1 \ = |u_y| JSR FMLTU \ Set K+2 = A * Q / 256 STA K+2 \ = |u_y| * |cos(CNT2)| LDA K2 \ Set A = K2 \ = |u_x| JSR FMLTU \ Set P = A * Q / 256 STA P \ = |u_x| * |cos(CNT2)| \ \ The call to FMLTU also sets the C flag, so in the \ following, ADC #15 adds 16 rather than 15 LDA CNT2 \ If (CNT2 + 16) mod 64 >= 33 then this sets the C flag, ADC #15 \ otherwise it's clear, so this means that: AND #63 \ CMP #33 \ * C is clear if the segment starts in the first or \ last quarter of the circle, 0 to 90 degrees or 270 \ to 360 degrees \ \ * C is set if the segment starts in the second or \ third quarter of the circle, 90 to 270 degrees \ \ In other words, the C flag contains the sign bit for \ cos(CNT2), which is positive for 0 to 90 degrees or \ 270 to 360 degrees, and negative for 90 to 270 degrees LDA #0 \ Shift the C flag into the sign bit of XX16+4, so: ROR A \ XX16+4 has the correct sign for cos(CNT2) STA XX16+4 \ \ Because we set the following above: \ \ K+2 = |u_y| * |cos(CNT2)| \ P = |u_x| * |cos(CNT2)| \ \ we can add XX16+4 as the high byte to give us the \ following: \ \ (XX16+4 K+2) = |u_y| * cos(CNT2) \ (XX16+4 P) = |u_x| * cos(CNT2) LDA XX16+5 \ Set S = the sign of XX16+2 * XX16+5 EOR XX16+2 \ = the sign of v_x * XX16+5 STA S \ \ So because we set this above: \ \ (XX16+5 R) = |v_x| * sin(CNT2) \ \ we now have this: \ \ (S R) = v_x * sin(CNT2) LDA XX16+4 \ Set A = the sign of XX16 * XX16+4 EOR XX16 \ = the sign of u_x * XX16+4 \ \ So because we set this above: \ \ (XX16+4 P) = |u_x| * cos(CNT2) \ \ we now have this: \ \ (A P) = u_x * cos(CNT2) JSR ADD \ Set (A X) = (A P) + (S R) \ = u_x * cos(CNT2) + v_x * sin(CNT2) STA T \ Store the high byte in T, so the result is now: \ \ (T X) = u_x * cos(CNT2) + v_x * sin(CNT2) BPL PL42 \ If the result is positive, jump down to PL42 TXA \ The result is negative, so we need to negate the EOR #%11111111 \ magnitude using two's complement, first doing the low CLC \ byte in X ADC #1 TAX LDA T \ And then the high byte in T, making sure to leave the EOR #%01111111 \ sign bit alone ADC #0 STA T .PL42 TXA \ Set K6(1 0) = K3(1 0) + (T X) ADC K3 \ STA K6 \ starting with the low bytes LDA T \ And then doing the high bytes, so we now get: ADC K3+1 \ STA K6+1 \ K6(1 0) = K3(1 0) + (T X) \ = K3(1 0) + u_x * cos(CNT2) \ + v_x * sin(CNT2) \ \ K3(1 0) is the x-coordinate of the centre of the \ ellipse, so we now have the correct x-coordinate for \ our ellipse segment that we can pass to BLINE below LDA K \ Set R = K = |v_y| * sin(CNT2) STA R LDA XX16+5 \ Set S = the sign of XX16+3 * XX16+5 EOR XX16+3 \ = the sign of v_y * XX16+5 STA S \ \ So because we set this above: \ \ (XX16+5 K) = |v_y| * sin(CNT2) \ \ and we just set R = K, we now have this: \ \ (S R) = v_y * sin(CNT2) LDA K+2 \ Set P = K+2 = |u_y| * cos(CNT2) STA P LDA XX16+4 \ Set A = the sign of XX16+1 * XX16+4 EOR XX16+1 \ = the sign of u_y * XX16+4 \ \ So because we set this above: \ \ (XX16+4 K+2) = |u_y| * cos(CNT2) \ \ and we just set P = K+2, we now have this: \ \ (A P) = u_y * cos(CNT2) JSR ADD \ Set (A X) = (A P) + (S R) \ = u_y * cos(CNT2) + v_y * sin(CNT2) EOR #%10000000 \ Store the negated high byte in T, so the result is STA T \ now: \ \ (T X) = - u_y * cos(CNT2) - v_y * sin(CNT2) \ \ This negation is necessary because BLINE expects us \ to pass pixel coordinates, where y-coordinates get \ larger as we go down the screen; u_y and v_y, on the \ other hand, are extracted from the orientation \ vectors, where y-coordinates get larger as we go up \ in space, so to rectify this we need to negate the \ result in (T X) before we can add it to the \ y-coordinate of the ellipse's centre in BLINE BPL PL43 \ If the result is positive, jump down to PL43 TXA \ The result is negative, so we need to negate the EOR #%11111111 \ magnitude using two's complement, first doing the low CLC \ byte in X ADC #1 TAX LDA T \ And then the high byte in T, making sure to leave the EOR #%01111111 \ sign bit alone ADC #0 STA T .PL43 \ We now call BLINE to draw the ellipse line segment \ \ The first few instructions of BLINE do the following: \ \ K6(3 2) = K4(1 0) + (T X) \ \ which gives: \ \ K6(3 2) = K4(1 0) - u_y * cos(CNT2) \ - v_y * sin(CNT2) \ \ K4(1 0) is the pixel y-coordinate of the centre of the \ ellipse, so this gives us the correct y-coordinate for \ our ellipse segment (we already calculated the \ x-coordinate in K3(1 0) above) JSR BLINE \ Call BLINE to draw this segment, which also returns \ the updated value of CNT in A CMP TGT \ If CNT > TGT then jump to PL40 to stop drawing the BEQ P%+4 \ ellipse (which is how we draw half-ellipses) BCS PL40 LDA CNT2 \ Set CNT2 = (CNT2 + STP) mod 64 CLC ADC STP AND #63 STA CNT2 JMP PLL4 \ Jump back to PLL4 to draw the next segment .PL40 \ --- Mod: Code removed for flicker-free planets: -----> \RTS \ Return from the subroutine \ --- And replaced by: --------------------------------> JMP EraseRestOfPlanet \ We have drawn the new circle, so now we need to erase \ any lines that are left in the ball line heap, \ returning from the subroutine using a tail call \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: SUN (Part 1 of 4) \ Type: Subroutine \ Category: Drawing suns \ Summary: Draw the sun: Set up all the variables needed to draw the sun \ Deep dive: Drawing the sun \ \ ------------------------------------------------------------------------------ \ \ Draw a new sun with radius K at pixel coordinate (K3, K4), removing the old \ sun if there is one. This routine is used to draw the sun, as well as the \ star systems on the Short-range Chart. \ \ The first part sets up all the variables needed to draw the new sun. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ K The new sun's radius \ \ K3(1 0) Pixel x-coordinate of the centre of the new sun \ \ K4(1 0) Pixel y-coordinate of the centre of the new sun \ \ SUNX(1 0) The x-coordinate of the vertical centre axis of the old \ sun (the one currently on-screen) \ \ ****************************************************************************** JMP WPLS \ Jump to WPLS to remove the old sun from the screen. We \ only get here via the BCS just after the SUN entry \ point below, when there is no new sun to draw .PLF3 \ This is called from below to negate X and set A to \ &FF, for when the new sun's centre is off the bottom \ of the screen (so we don't need to draw its bottom \ half) \ \ This happens when the y-coordinate of the centre of \ the sun is bigger than the y-coordinate of the bottom \ of the space view TXA \ Negate X using two's complement, so X = ~X + 1 EOR #%11111111 CLC ADC #1 TAX .PLF17 \ This is called from below to set A to &FF, for when \ the new sun's centre is right on the bottom of the \ screen (so we don't need to draw its bottom half) LDA #&FF \ Set A = &FF BNE PLF5 \ Jump to PLF5 (this BNE is effectively a JMP as A is \ never zero) .SUN \ --- Mod: Code added for two-player Elite: -----------> JSR SetPlayerSunHeap \ Set up the sun line heap pointers for the current \ player BIT splitScreen \ Skip the following if split screen is disabled BPL dsun1 LDA K4 \ Move the sun into the top half of the screen by moving SEC \ it up by a quarter screen: SBC #Y/2 \ STA K4 \ K4(1 0) = K4(1 0) - #Y/2 LDA K4+1 SBC #0 STA K4+1 .dsun1 \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \LDA #1 \ Set LSX = 1 to indicate the sun line heap is about to \STA LSX \ be filled up \ --- And replaced by: --------------------------------> LDA #1 \ Set LSX = 1 to indicate the sun line heap is about to STA (LSXS) \ be filled up \ --- End of replacement ------------------------------> \ --- Mod: Code added for two-player Elite: -----------> LDA #Y-1 \ Perform a half-screen check instead of CHKON's default STA heightInCHKON \ full-screen check, as sun coordinates are clipped to \ the player view (unlike circles) \ --- End of added code -------------------------------> JSR CHKON \ Call CHKON to check whether any part of the new sun's \ circle appears on-screen, and if it does, set P(2 1) \ to the maximum y-coordinate of the new sun on-screen BCS PLF3-3 \ If CHKON set the C flag then the new sun's circle does \ not appear on-screen, so jump to WPLS (via the JMP at \ the top of this routine) to remove the sun from the \ screen, returning from the subroutine using a tail \ call LDA #0 \ Set A = 0 LDX K \ Set X = K = radius of the new sun CPX #96 \ If X >= 96, set the C flag and rotate it into bit 0 ROL A \ of A, otherwise rotate a 0 into bit 0 CPX #40 \ If X >= 40, set the C flag and rotate it into bit 0 ROL A \ of A, otherwise rotate a 0 into bit 0 CPX #16 \ If X >= 16, set the C flag and rotate it into bit 0 ROL A \ of A, otherwise rotate a 0 into bit 0 \ By now, A contains the following: \ \ * If radius is 96-255 then A = %111 = 7 \ \ * If radius is 40-95 then A = %11 = 3 \ \ * If radius is 16-39 then A = %1 = 1 \ \ * If radius is 0-15 then A = %0 = 0 \ \ The value of A determines the size of the new sun's \ ragged fringes - the bigger the sun, the bigger the \ fringes .PLF18 STA CNT \ Store the fringe size in CNT \ We now calculate the highest pixel y-coordinate of the \ new sun, given that P(2 1) contains the 16-bit maximum \ y-coordinate of the new sun on-screen \ --- Mod: Code removed for two-player Elite: ---------> \LDA #2*Y-1 \ #Y is the y-coordinate of the centre of the space \ \ view, so this sets Y to the y-coordinate of the bottom \ \ of the space view \ --- And replaced by: --------------------------------> LDA #Y-1 \ #Y is the y-coordinate of the centre of the space \ view, so this sets Y to the y-coordinate of the bottom \ of the player view \ --- End of replacement ------------------------------> LDX P+2 \ If P+2 is non-zero, the maximum y-coordinate is off BNE PLF2 \ the bottom of the screen, so skip to PLF2 with A set \ to the y-coordinate of the bottom of the space view CMP P+1 \ If A < P+1, the maximum y-coordinate is underneath the BCC PLF2 \ dashboard, so skip to PLF2 with A set to the \ y-coordinate of the bottom of the space view LDA P+1 \ Set A = P+1, the low byte of the maximum y-coordinate \ of the sun on-screen BNE PLF2 \ If A is non-zero, skip to PLF2 as it contains the \ value we are after LDA #1 \ Otherwise set A = 1, the top line of the screen .PLF2 STA TGT \ Set TGT to A, the maximum y-coordinate of the sun on \ screen \ We now calculate the number of lines we need to draw \ and the direction in which we need to draw them, both \ from the centre of the new sun \ --- Mod: Code removed for two-player Elite: ---------> \LDA #2*Y-1 \ Set (A X) = y-coordinate of bottom of screen - K4(1 0) \SEC \ \SBC K4 \ Starting with the low bytes \TAX \ --- And replaced by: --------------------------------> LDA #Y-1 \ Set (A X) = y-coordinate of bottom of screen - K4(1 0) SEC \ SBC K4 \ Starting with the low bytes TAX \ --- End of replacement ------------------------------> LDA #0 \ And then doing the high bytes, so (A X) now contains SBC K4+1 \ the number of lines between the centre of the sun and \ the bottom of the screen. If it is positive then the \ centre of the sun is above the bottom of the screen, \ if it is negative then the centre of the sun is below \ the bottom of the screen BMI PLF3 \ If A < 0, then this means the new sun's centre is off \ the bottom of the screen, so jump up to PLF3 to negate \ the height in X (so it becomes positive), set A to &FF \ and jump down to PLF5 BNE PLF4 \ If A > 0, then the new sun's centre is at least a full \ screen above the bottom of the space view, so jump \ down to PLF4 to set X = radius and A = 0 INX \ Set the flags depending on the value of X DEX BEQ PLF17 \ If X = 0 (we already know A = 0 by this point) then \ jump up to PLF17 to set A to &FF before jumping down \ to PLF5 CPX K \ If X < the radius in K, jump down to PLF5, so if BCC PLF5 \ X >= the radius in K, we set X = radius and A = 0 .PLF4 LDX K \ Set X to the radius LDA #0 \ Set A = 0 .PLF5 STX V \ Store the height in V STA V+1 \ Store the direction in V+1 LDA K \ Set (A P) = K * K JSR SQUA2 STA K2+1 \ Set K2(1 0) = (A P) = K * K LDA P STA K2 \ By the time we get here, the variables should be set \ up as shown in the header for part 3 below \ ****************************************************************************** \ \ Name: SUN (Part 2 of 4) \ Type: Subroutine \ Category: Drawing suns \ Summary: Draw the sun: Start from the bottom of the screen and erase the \ old sun line by line \ Deep dive: Drawing the sun \ \ ------------------------------------------------------------------------------ \ \ This part erases the old sun, starting at the bottom of the screen and working \ upwards until we reach the bottom of the new sun. \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \LDY #2*Y-1 \ Set Y = y-coordinate of the bottom of the screen, \ \ which we use as a counter in the following routine to \ \ redraw the old sun \ \LDA SUNX \ Set YY(1 0) = SUNX(1 0), the x-coordinate of the \STA YY \ vertical centre axis of the old sun that's currently \LDA SUNX+1 \ on-screen \STA YY+1 \ --- And replaced by: --------------------------------> LDA (SUNXS) \ Set YY(1 0) = SUNX(1 0), the x-coordinate of the STA YY \ vertical centre axis of the old sun that's currently LDY #1 \ on-screen LDA (SUNXS),Y STA YY+1 LDY #Y-1 \ Set Y = y-coordinate of the bottom of the screen, \ which we use as a counter in the following routine to \ redraw the old sun \ --- End of replacement ------------------------------> .PLFL2 CPY TGT \ If Y = TGT, we have reached the line where we will BEQ PLFL \ start drawing the new sun, so there is no need to \ keep erasing the old one, so jump down to PLFL \ --- Mod: Code removed for two-player Elite: ---------> \LDA LSO,Y \ Fetch the Y-th point from the sun line heap, which \ \ gives us the half-width of the old sun's line on this \ \ line of the screen \ --- And replaced by: --------------------------------> LDA (LSOS),Y \ Fetch the Y-th point from the sun line heap, which \ gives us the half-width of the old sun's line on this \ line of the screen \ --- End of replacement ------------------------------> BEQ PLF13 \ If A = 0, skip the following call to HLOIN2 as there \ is no sun line on this line of the screen JSR HLOIN2 \ Call HLOIN2 to draw a horizontal line on pixel line Y, \ with centre point YY(1 0) and half-width A, and remove \ the line from the sun line heap once done .PLF13 DEY \ Decrement the loop counter BNE PLFL2 \ Loop back for the next line in the line heap until \ we have either gone through the entire heap, or \ reached the bottom row of the new sun \ ****************************************************************************** \ \ Name: SUN (Part 3 of 4) \ Type: Subroutine \ Category: Drawing suns \ Summary: Draw the sun: Continue to move up the screen, drawing the new sun \ line by line \ Deep dive: Drawing the sun \ \ ------------------------------------------------------------------------------ \ \ This part draws the new sun. By the time we get to this point, the following \ variables should have been set up by parts 1 and 2: \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ V As we draw lines for the new sun, V contains the \ vertical distance between the line we're drawing and the \ centre of the new sun. As we draw lines and move up the \ screen, we either decrement (bottom half) or increment \ (top half) this value \ \ V+1 This determines which half of the new sun we are drawing \ as we work our way up the screen, line by line: \ \ * 0 means we are drawing the bottom half, so the lines \ get wider as we work our way up towards the centre, \ at which point we will move into the top half, and \ V+1 will switch to &FF \ \ * &FF means we are drawing the top half, so the lines \ get smaller as we work our way up, away from the \ centre \ \ TGT The maximum y-coordinate of the new sun on-screen (i.e. \ the screen y-coordinate of the bottom row of the new \ sun) \ \ CNT The fringe size of the new sun \ \ K2(1 0) The new sun's radius squared, i.e. K^2 \ \ Y The y-coordinate of the bottom row of the new sun \ \ ****************************************************************************** .PLFL LDA V \ Set (T P) = V * V JSR SQUA2 \ = V^2 STA T LDA K2 \ Set (R Q) = K^2 - V^2 SEC \ SBC P \ First calculating the low bytes STA Q LDA K2+1 \ And then doing the high bytes SBC T STA R STY Y1 \ Store Y in Y1, so we can restore it after the call to \ LL5 JSR LL5 \ Set Q = SQRT(R Q) \ = SQRT(K^2 - V^2) \ \ So Q contains the half-width of the new sun's line at \ height V from the sun's centre - in other words, it \ contains the half-width of the sun's line on the \ current pixel row Y LDY Y1 \ Restore Y from Y1 JSR DORND \ Set A and X to random numbers AND CNT \ Reduce A to a random number in the range 0 to CNT, \ where CNT is the fringe size of the new sun CLC \ Set A = A + Q ADC Q \ \ So A now contains the half-width of the sun on row \ V, plus a random variation based on the fringe size BCC PLF44 \ If the above addition did not overflow, skip the \ following instruction LDA #255 \ The above overflowed, so set the value of A to 255 \ So A contains the half-width of the new sun on pixel \ line Y, changed by a random amount within the size of \ the sun's fringe .PLF44 \ --- Mod: Code removed for two-player Elite: ---------> \LDX LSO,Y \ Set X to the line heap value for the old sun's line \ \ at row Y \ \STA LSO,Y \ Store the half-width of the new row Y line in the line \ \ heap \ --- And replaced by: --------------------------------> PHA \ Store A on the stack LDA (LSOS),Y \ Set X to the line heap value for the old sun's line TAX \ at row Y PLA \ Retrieve A from the stack STA (LSOS),Y \ Store the half-width of the new row Y line in the line \ heap INX \ Set the flags depending on the value of X, for the DEX \ next instruction \ --- End of replacement ------------------------------> BEQ PLF11 \ If X = 0 then there was no sun line on pixel row Y, so \ jump to PLF11 \ --- Mod: Code removed for two-player Elite: ---------> \LDA SUNX \ Set YY(1 0) = SUNX(1 0), the x-coordinate of the \STA YY \ vertical centre axis of the old sun that's currently \LDA SUNX+1 \ on-screen \STA YY+1 \ --- And replaced by: --------------------------------> LDA (SUNXS) \ Set YY(1 0) = SUNX(1 0), the x-coordinate of the STA YY \ vertical centre axis of the old sun that's currently PHY \ on-screen LDY #1 LDA (SUNXS),Y STA YY+1 PLY \ --- End of replacement ------------------------------> TXA \ Transfer the line heap value for the old sun's line \ from X into A JSR EDGES \ Call EDGES to calculate X1 and X2 for the horizontal \ line centred on YY(1 0) and with half-width A, i.e. \ the line for the old sun LDA X1 \ Store X1 and X2, the ends of the line for the old sun, STA XX \ in XX and XX+1 LDA X2 STA XX+1 LDA K3 \ Set YY(1 0) = K3(1 0), the x-coordinate of the centre STA YY \ of the new sun LDA K3+1 STA YY+1 \ --- Mod: Code removed for two-player Elite: ---------> \LDA LSO,Y \ Fetch the half-width of the new row Y line from the \ \ line heap (which we stored above) \ --- And replaced by: --------------------------------> LDA (LSOS),Y \ Fetch the half-width of the new row Y line from the \ line heap (which we stored above) \ --- End of replacement ------------------------------> JSR EDGES \ Call EDGES to calculate X1 and X2 for the horizontal \ line centred on YY(1 0) and with half-width A, i.e. \ the line for the new sun BCS PLF23 \ If the C flag is set, the new line doesn't fit on the \ screen, so jump to PLF23 to just draw the old line \ without drawing the new one \ At this point the old line is from XX to XX+1 and the \ new line is from X1 to X2, and both fit on-screen. We \ now want to remove the old line and draw the new one. \ We could do this by simply drawing the old one then \ drawing the new one, but instead Elite does this by \ drawing first from X1 to XX and then from X2 to XX+1, \ which you can see in action by looking at all the \ permutations below of the four points on the line and \ imagining what happens if you draw from X1 to XX and \ X2 to XX+1 using EOR logic. The six possible \ permutations are as follows, along with the result of \ drawing X1 to XX and then X2 to XX+1: \ \ X1 X2 XX____XX+1 -> +__+ + + \ \ X1 XX____X2____XX+1 -> +__+__+ + \ \ X1 XX____XX+1 X2 -> +__+__+__+ \ \ XX____X1____XX+1 X2 -> + +__+__+ \ \ XX____XX+1 X1 X2 -> + + +__+ \ \ XX____X1____X2____XX+1 -> + +__+ + \ \ They all end up with a line between X1 and X2, which \ is what we want. There's probably a mathematical proof \ of why this works somewhere, but the above is probably \ easier to follow. \ \ We can draw from X1 to XX and X2 to XX+1 by swapping \ XX and X2 and drawing from X1 to X2, and then drawing \ from XX to XX+1, so let's do this now LDA X2 \ Swap XX and X2 LDX XX STX X2 STA XX JSR HLOIN \ Draw a horizontal line from (X1, Y1) to (X2, Y1) .PLF23 \ If we jump here from the BCS above when there is no \ new line this will just draw the old line LDA XX \ Set X1 = XX STA X1 LDA XX+1 \ Set X2 = XX+1 STA X2 .PLF16 JSR HLOIN \ Draw a horizontal line from (X1, Y1) to (X2, Y1) .PLF6 DEY \ Decrement the line number in Y to move to the line \ above BEQ PLF8 \ If we have reached the top of the screen, jump to PLF8 \ as we are done drawing (the top line of the screen is \ the border, so we don't draw there) LDA V+1 \ If V+1 is non-zero then we are doing the top half of BNE PLF10 \ the new sun, so jump down to PLF10 to increment V and \ decrease the width of the line we draw DEC V \ Decrement V, the height of the sun that we use to work \ out the width, so this makes the line get wider, as we \ move up towards the sun's centre BNE PLFL \ If V is non-zero, jump back up to PLFL to do the next \ screen line up DEC V+1 \ Otherwise V is 0 and we have reached the centre of the \ sun, so decrement V+1 to -1 so we start incrementing V \ each time, thus doing the top half of the new sun .PLFLS JMP PLFL \ Jump back up to PLFL to do the next screen line up .PLF11 \ If we get here then there is no old sun line on this \ line, so we can just draw the new sun's line LDX K3 \ Set YY(1 0) = K3(1 0), the x-coordinate of the centre STX YY \ of the new sun's line LDX K3+1 STX YY+1 JSR EDGES \ Call EDGES to calculate X1 and X2 for the horizontal \ line centred on YY(1 0) and with half-width A, i.e. \ the line for the new sun BCC PLF16 \ If the line is on-screen, jump up to PLF16 to draw the \ line and loop round for the next line up \ --- Mod: Code removed for two-player Elite: ---------> \LDA #0 \ The line is not on-screen, so set the line heap for \STA LSO,Y \ line Y to 0, which means there is no sun line here \ --- And replaced by: --------------------------------> LDA #0 \ The line is not on-screen, so set the line heap for STA (LSOS),Y \ line Y to 0, which means there is no sun line here \ --- End of replacement ------------------------------> BEQ PLF6 \ Jump up to PLF6 to loop round for the next line up \ (this BEQ is effectively a JMP as A is always zero) .PLF10 LDX V \ Increment V, the height of the sun that we use to work INX \ out the width, so this makes the line get narrower, as STX V \ we move up and away from the sun's centre CPX K \ If V <= the radius of the sun, we still have lines to BCC PLFLS \ draw, so jump up to PLFL (via PLFLS) to do the next BEQ PLFLS \ screen line up \ ****************************************************************************** \ \ Name: SUN (Part 4 of 4) \ Type: Subroutine \ Category: Drawing suns \ Summary: Draw the sun: Continue to the top of the screen, erasing the old \ sun line by line \ Deep dive: Drawing the sun \ \ ------------------------------------------------------------------------------ \ \ This part erases any remaining traces of the old sun, now that we have drawn \ all the way to the top of the new sun. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ RTS2 Contains an RTS \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \LDA SUNX \ Set YY(1 0) = SUNX(1 0), the x-coordinate of the \STA YY \ vertical centre axis of the old sun that's currently \LDA SUNX+1 \ on-screen \STA YY+1 \ --- And replaced by: --------------------------------> LDA (SUNXS) \ Set YY(1 0) = SUNX(1 0), the x-coordinate of the STA YY \ vertical centre axis of the old sun that's currently PHY \ on-screen LDY #1 LDA (SUNXS),Y STA YY+1 PLY \ --- End of replacement ------------------------------> .PLFL3 \ --- Mod: Code removed for two-player Elite: ---------> \LDA LSO,Y \ Fetch the Y-th point from the sun line heap, which \ \ gives us the half-width of the old sun's line on this \ \ line of the screen \ --- And replaced by: --------------------------------> LDA (LSOS),Y \ Fetch the Y-th point from the sun line heap, which \ gives us the half-width of the old sun's line on this \ line of the screen \ --- End of replacement ------------------------------> BEQ PLF9 \ If A = 0, skip the following call to HLOIN2 as there \ is no sun line on this line of the screen JSR HLOIN2 \ Call HLOIN2 to draw a horizontal line on pixel line Y, \ with centre point YY(1 0) and half-width A, and remove \ the line from the sun line heap once done .PLF9 DEY \ Decrement the line number in Y to move to the line \ above BNE PLFL3 \ Jump up to PLFL3 to redraw the next line up, until we \ have reached the top of the screen .PLF8 \ If we get here, we have successfully made it from the \ bottom line of the screen to the top, and the old sun \ has been replaced by the new one CLC \ Clear the C flag to indicate success in drawing the \ sun \ --- Mod: Code removed for two-player Elite: ---------> \LDA K3 \ Set SUNX(1 0) = K3(1 0) \STA SUNX \LDA K3+1 \STA SUNX+1 \ --- And replaced by: --------------------------------> LDA K3 \ Set SUNX(1 0) = K3(1 0) STA (SUNXS) LDA K3+1 LDY #1 STA (SUNXS),Y \ --- End of replacement ------------------------------> JSR HBFL \ Call HBFL to send the contents of the horizontal line \ buffer to the I/O processor for drawing on-screen .RTS2 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: CIRCLE \ Type: Subroutine \ Category: Drawing circles \ Summary: Draw a circle for the planet \ Deep dive: Drawing circles \ \ ------------------------------------------------------------------------------ \ \ Draw a circle with the centre at (K3, K4) and radius K. Used to draw the \ planet's main outline. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ K The planet's radius \ \ K3(1 0) Pixel x-coordinate of the centre of the planet \ \ K4(1 0) Pixel y-coordinate of the centre of the planet \ \ ****************************************************************************** .CIRCLE \ --- Mod: Code added for two-player Elite: -----------> LDA #2*Y-1 \ Perform a full-screen check in CHKON's full-screen STA heightInCHKON \ check, as circles use full-screen coordinates \ --- End of added code -------------------------------> JSR CHKON \ Call CHKON to check whether the circle fits on-screen BCS RTS2 \ If CHKON set the C flag then the circle does not fit \ on-screen, so return from the subroutine (as RTS2 \ contains an RTS) \ --- Mod: Code added for flicker-free planets: -------> \ --- Mod: Code removed for two-player Elite: ---------> \LDA #0 \ Set LSX2 = 0 to indicate that the ball line heap is \STA LSX2 \ not empty, as we are about to fill it \ --- And replaced by: --------------------------------> LDA #0 \ Set LSX2 = 0 to indicate that the ball line heap is STA (LSX2S) \ not empty, as we are about to fill it \ --- End of replacement ------------------------------> \ --- End of added code -------------------------------> LDX K \ Set X = K = radius LDA #8 \ Set A = 8 CPX #4 \ If the radius < 4, skip to PL89 BCC PL89 LSR A \ Halve A so A = 4 CPX #50 \ If the radius < 50, skip to PL89 BCC PL89 LSR A \ Halve A so A = 2 .PL89 STA STP \ Set STP = A. STP is the step size for the circle, so \ the above sets a smaller step size for bigger circles \ Fall through into CIRCLE3 to draw the circle with the \ correct step size \ ****************************************************************************** \ \ Name: CIRCLE2 \ Type: Subroutine \ Category: Drawing circles \ Summary: Draw a circle (for the planet or chart) \ Deep dive: Drawing circles \ \ ------------------------------------------------------------------------------ \ \ Draw a circle with the centre at (K3, K4) and radius K. Used to draw the \ planet and the chart circles. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ STP The step size for the circle \ \ K The circle's radius \ \ K3(1 0) Pixel x-coordinate of the centre of the circle \ \ K4(1 0) Pixel y-coordinate of the centre of the circle \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag The C flag is cleared \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ CIRCLE3 Just add the circle segments to the existing ball line \ heap - do not send the send the ball line heap to the \ I/O processor for drawing on-screen \ \ ****************************************************************************** .CIRCLE3 \ This gets called from CIRCLE2 below to calculate the \ line segments, which CIRCLE2 then sends to the I/O \ processor for drawing \ --- Mod: Code added for flicker-free planets: -------> \ We now set things up for flicker-free circle plotting, \ by setting the following: \ \ LSNUM = offset to the first coordinate in the ball \ line heap \ \ LSNUM2 = the number of bytes in the heap for the \ circle that's currently on-screen (or 0 if \ there is no ship currently on-screen) STZ LSNUM \ Set LSNUM = 0, to point to the offset before the first \ set of circle coordinates in the ball line heap \ --- Mod: Code removed for two-player Elite: ---------> \LDX LSP \ Set LSNUM2 to the last byte of the ball line heap \STX LSNUM2 \ \LDX #1 \ Set LSP = 1 to reset the ball line heap pointer \STX LSP \ --- And replaced by: --------------------------------> LDA (LSPS) \ Set LSNUM2 to the last byte of the ball line heap STA LSNUM2 LDA #1 \ Set LSP = 1 to reset the ball line heap pointer STA (LSPS) .circ1 \ --- End of replacement ------------------------------> \ --- End of added code -------------------------------> LDX #&FF \ Set FLAG = &FF to reset the ball line heap in the call STX FLAG \ to the BLINE routine below INX \ Set CNT = 0, our counter that goes up to 64, counting STX CNT \ segments in our circle .PLL3 LDA CNT \ Set A = CNT JSR FMLTU2 \ Call FMLTU2 to calculate: \ \ A = K * sin(A) \ = K * sin(CNT) LDX #0 \ Set T = 0, so we have the following: STX T \ \ (T A) = K * sin(CNT) \ \ which is the x-coordinate of the circle for this count LDX CNT \ If CNT < 33 then jump to PL37, as this is the right CPX #33 \ half of the circle and the sign of the x-coordinate is BCC PL37 \ correct EOR #%11111111 \ This is the left half of the circle, so we want to ADC #0 \ flip the sign of the x-coordinate in (T A) using two's TAX \ complement, so we start with the low byte and store it \ in X (the ADC adds 1 as we know the C flag is set) LDA #&FF \ And then we flip the high byte in T ADC #0 STA T TXA \ Finally, we restore the low byte from X, so we have \ now negated the x-coordinate in (T A) CLC \ Clear the C flag so we can do some more addition below .PL37 ADC K3 \ We now calculate the following: STA K6 \ \ K6(1 0) = (T A) + K3(1 0) \ \ to add the coordinates of the centre to our circle \ point, starting with the low bytes LDA K3+1 \ And then doing the high bytes, so we now have: ADC T \ STA K6+1 \ K6(1 0) = K * sin(CNT) + K3(1 0) \ \ which is the result we want for the x-coordinate LDA CNT \ Set A = CNT + 16 CLC ADC #16 JSR FMLTU2 \ Call FMLTU2 to calculate: \ \ A = K * sin(A) \ = K * sin(CNT + 16) \ = K * cos(CNT) TAX \ Set X = A \ = K * cos(CNT) LDA #0 \ Set T = 0, so we have the following: STA T \ \ (T X) = K * cos(CNT) \ \ which is the y-coordinate of the circle for this count LDA CNT \ Set A = (CNT + 15) mod 64 ADC #15 AND #63 CMP #33 \ If A < 33 (i.e. CNT is 0-16 or 48-64) then jump to BCC PL38 \ PL38, as this is the bottom half of the circle and the \ sign of the y-coordinate is correct TXA \ This is the top half of the circle, so we want to EOR #%11111111 \ flip the sign of the y-coordinate in (T X) using two's ADC #0 \ complement, so we start with the low byte in X (the TAX \ ADC adds 1 as we know the C flag is set) LDA #&FF \ And then we flip the high byte in T, so we have ADC #0 \ now negated the y-coordinate in (T X) STA T CLC \ Clear the C flag so the addition at the start of BLINE \ will work .PL38 JSR BLINE \ Call BLINE to draw this segment, which also increases \ CNT by STP, the step size CMP #65 \ If CNT >= 65 then skip the next instruction BCS P%+5 JMP PLL3 \ Jump back for the next segment CLC \ Clear the C flag to indicate success RTS \ Return from the subroutine .CIRCLE2 \ This is the entry point for this subroutine \ --- Mod: Code removed for two-player Elite: ---------> \STZ LSP \ Reset the ball line heap by setting the ball line heap \ \ pointer to 0 \ --- And replaced by: --------------------------------> JSR ResetBallLine \ Reset the ball line heap for the view we are currently \ drawing \ --- End of replacement ------------------------------> \ --- Mod: Code removed for flicker-free planets: -----> \JSR CIRCLE3 \ Call CIRCLE3 to populate the ball line heap \ \ \ Fall through into LS2FL to send the ball line heap to \ \ the I/O processor for drawing on-screen \ --- And replaced by: --------------------------------> JMP CIRCLE3 \ Call CIRCLE3 to draw the circle, returning from the \ subroutine using a tail call \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: LS2FL \ Type: Subroutine \ Category: Drawing circles \ Summary: Draw the contents of the ball line heap by sending an OSWRCH 129 \ command to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ If there are too many points for one batch of OSWRCH 129 calls, the line is \ split into two batches, with the last coordinate of the first batch being \ duplicated as the first coordinate of the second batch, so the two lines join \ up to make a complete circle. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ WP1 Contains an RTS \ \ ****************************************************************************** \ --- Mod: Code removed for flicker-free planets: -----> \.LS2FL \ \LDY LSP \ Set Y to the ball line heap pointer, which contains \ \ the number of the first free byte after the end of the \ \ LSX2 and LSY2 heaps - in other words, the number of \ \ points in the ball line heap \ \ \ We now loop through the ball line heap using Y as a \ \ pointer \ \.WP3 \ \STY T \ Set T = the number of points in the heap \ \BEQ WP1 \ If there are no points in the heap, jump down to WP1 \ \ to return from the subroutine \ \LDA #129 \ Send an OSWRCH 129 command to the I/O processor to \JSR OSWRCH \ tell it to start receiving a new line to draw. The \ \ parameter to this call needs to contain the number of \ \ bytes we are going to send for the line's coordinates, \ \ so let's calculate that now \ \TYA \ Transfer the Y counter into A, so A now contains the \ \ number of coordinates to send to the I/O processor \ \BMI WP2 \ If the counter in A > 127, then jump to WP2, as we \ \ need to send the points in two batches (as the line \ \ buffer in the I/O processor can hold 256 bytes, and \ \ each coordinate occupies two bytes) \ \SEC \ Set A = (A * 2) + 1 \ROL A \ \ \ so A now contains the number of bytes we are going to \ \ send, plus 1 (the extra 1 is required as the value \ \ sent needs to point to the first free byte after the \ \ end of the byte list) \ \JSR OSWRCH \ Send A to the I/O processor as the argument to the \ \ OSWRCH 129 command, so the I/O processor can set the \ \ LINMAX variable in the BEGINLIN routine \ \ \ We now want to send the points themselves to the I/O \ \ processor \ \LDY #0 \ Set Y = 0 to act as a loop through the first T points \ \.WPL1 \ \LDA LSX2,Y \ Send the x-coordinate of the start of the line segment \JSR OSWRCH \ \LDA LSY2,Y \ Send the y-coordinate of the start of the line segment \JSR OSWRCH \ \INY \ Increment the pointer to point to the next coordinate \ \LDA LSX2,Y \ Send the x-coordinate of the end of the line segment \JSR OSWRCH \ \LDA LSY2,Y \ Send the y-coordinate of the end of the line segment \JSR OSWRCH \ \INY \ Increment the pointer to point to the next coordinate \ \CPY T \ If Y < T then loop back to send the next coordinate, \BCC WPL1 \ until we have sent them all. The I/O processor will \ \ now draw the line \ \.WP1 \ \RTS \ Return from the subroutine \ \.WP2 \ \ \ If we get here then there are more than 127 points in \ \ the line heap to send to the I/O processor, so we need \ \ to send them in two batches. We start by sending the \ \ second half of the coordinates, making sure we include \ \ the last coordinate from the first batch to make sure \ \ the circles drawn by each batch join up \ \ASL A \ Shift A left, shifting bit 7 (which we know is set) \ \ into the C flag, so this sets: \ \ \ \ A = (A * 2) mod 256 \ \ \ \ So A contains the number of bytes left over in the \ \ second batch if we send a full first batch \ \ADC #4 \ Set A = A + 4 + C \ \ = A + 4 + 1 \ \ \ \ so A now contains the number of bytes we are going to \ \ send in each batch, plus 4 (because we need to send \ \ the extra coordinate at the start of the second \ \ batch), plus 1 (the extra 1 is required as the value \ \ sent needs to point to the first free byte after the \ \ end of the byte list) \ \JSR OSWRCH \ Send A to the I/O processor as the argument to the \ \ OSWRCH 129 command, so the I/O processor can set the \ \ LINMAX variable in the BEGINLIN routine \ \LDY #126 \ Call WPL1 above with Y = 126 to send the second batch \JSR WPL1 \ of points from the ball line heap to the I/O \ \ processor, starting from the last coordinate of the \ \ first batch, so that gets sent in both batches (this \ \ is why Y = 126 rather than 127) \ \LDY #126 \ Jump to WP3 above to send a whole new OSWRCH 129 \JMP WP3 \ command to draw the first batch of points \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: WPLS2 \ Type: Subroutine \ Category: Drawing planets \ Summary: Remove the planet from the screen \ Deep dive: The ball line heap \ \ ------------------------------------------------------------------------------ \ \ We do this by redrawing it using the lines stored in the ball line heap when \ the planet was originally drawn by the BLINE routine. \ \ ****************************************************************************** \ --- Mod: Code added for flicker-free planets: -------> .WPLS2 \ --- Mod: Code removed for two-player Elite: ---------> \LDY LSX2 \ If LSX2 is non-zero (which indicates the ball line \BNE WP1 \ heap is empty), jump to WP1 to reset the line heap \ \ without redrawing the planet \ --- And replaced by: --------------------------------> JSR SetPlayerBallLine \ Use the correct ball line for the player view we are \ drawing LDA (LSX2S) \ If LSX2 is non-zero (which indicates the ball line BNE WP1 \ heap is empty), jump to WP1 to reset the line heap \ without redrawing the planet \ --- End of replacement ------------------------------> STY LSNUM \ Reset LSNUM to the start of the ball line heap (we can \ set this to 0 rather than 1 to take advantage of the \ fact that Y is 0 - the effect is the same) \ --- Mod: Code removed for two-player Elite: ---------> \LDA LSP \ Set LSNUM2 to the end of the ball line heap \STA LSNUM2 \ --- And replaced by: --------------------------------> LDA (LSPS) \ Set LSNUM2 to the end of the ball line heap STA LSNUM2 \ --- End of replacement ------------------------------> JSR EraseRestOfPlanet \ Draw the contents of the ball line heap to erase the \ old planet \ --- Mod: Code removed for two-player Elite: ---------> \LDA #1 \ Set LSP = 1 to reset the ball line heap pointer \STA LSP \ \LDA #&FF \ Set LSX2 = &FF to indicate the ball line heap is empty \STA LSX2 \ --- And replaced by: --------------------------------> LDA #1 \ Set LSP = 1 to reset the ball line heap pointer STA (LSPS) LDA #&FF \ Set LSX2 = &FF to indicate the ball line heap is empty STA (LSX2S) \ --- End of replacement ------------------------------> .WP1 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: WPLS \ Type: Subroutine \ Category: Drawing suns \ Summary: Remove the sun from the screen \ Deep dive: Drawing the sun \ \ ------------------------------------------------------------------------------ \ \ We do this by redrawing it using the lines stored in the sun line heap when \ the sun was originally drawn by the SUN routine. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ SUNX(1 0) The x-coordinate of the vertical centre axis of the sun \ \ ****************************************************************************** .WPLS \ --- Mod: Code removed for two-player Elite: ---------> \LDA LSX \ If LSX < 0, the sun line heap is empty, so return from \BMI WP1 \ the subroutine (as WP1 contains an RTS) \ \LDA SUNX \ Set YY(1 0) = SUNX(1 0), the x-coordinate of the \STA YY \ vertical centre axis of the sun that's currently on \LDA SUNX+1 \ screen \STA YY+1 \ --- And replaced by: --------------------------------> JSR SetPlayerSunHeap \ Set up the sun line heap pointers for the current \ player LDA (LSXS) \ If LSX < 0, the sun line heap is empty, so return from BMI WP1 \ the subroutine (as WP1 contains an RTS) LDA (SUNXS) \ Set YY(1 0) = SUNX(1 0), the x-coordinate of the STA YY \ vertical centre axis of the old sun that's currently LDY #1 \ on-screen LDA (SUNXS),Y STA YY+1 \ --- End of replacement ------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \LDY #2*Y-1 \ #Y is the y-coordinate of the centre of the space \ \ view, so this sets Y as a counter for the number of \ \ lines in the space view (i.e. 191), which is also the \ \ number of lines in the LSO block \ --- And replaced by: --------------------------------> LDY #Y-1 \ #Y is the y-coordinate of the centre of the space \ view, so this sets Y as a counter for the number of \ lines in the space view (i.e. 191), which is also the \ number of lines in the LSO block \ --- End of replacement ------------------------------> .WPL2 \ --- Mod: Code removed for two-player Elite: ---------> \LDA LSO,Y \ Fetch the Y-th point from the sun line heap, which \ \ gives us the half-width of the sun's line on this line \ \ of the screen \ --- And replaced by: --------------------------------> LDA (LSOS),Y \ Fetch the Y-th point from the sun line heap, which \ gives us the half-width of the sun's line on this line \ of the screen \ --- End of replacement ------------------------------> BEQ P%+5 \ If A = 0, skip the following call to HLOIN2 as there \ is no sun line on this line of the screen JSR HLOIN2 \ Call HLOIN2 to draw a horizontal line on pixel line Y, \ with centre point YY(1 0) and half-width A, and remove \ the line from the sun line heap once done DEY \ Decrement the loop counter BNE WPL2 \ Loop back for the next line in the line heap until \ we have gone through the entire heap DEY \ This sets Y to &FF, as we end the loop with Y = 0 \ --- Mod: Code removed for two-player Elite: ---------> \STY LSX \ Set LSX to &FF to indicate the sun line heap is empty \ --- And replaced by: --------------------------------> TYA \ Set LSX to &FF to indicate the sun line heap is empty STA (LSXS) \ --- End of replacement ------------------------------> JMP HBFL \ Call HBFL to send the contents of the horizontal line \ buffer to the I/O processor for drawing on-screen, \ returning from the subroutine using a tail call \ ****************************************************************************** \ \ Name: EDGES \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw a horizontal line given a centre and a half-width \ \ ------------------------------------------------------------------------------ \ \ Set X1 and X2 to the x-coordinates of the ends of the horizontal line with \ centre x-coordinate YY(1 0), and length A in either direction from the centre \ (so a total line length of 2 * A). In other words, this line: \ \ X1 YY(1 0) X2 \ +-----------------+-----------------+ \ <- A -> <- A -> \ \ The resulting line gets clipped to the edges of the screen, if needed. If the \ calculation doesn't overflow, we return with the C flag clear, otherwise the C \ flag gets set to indicate failure and the Y-th LSO entry gets set to 0. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The half-length of the line \ \ YY(1 0) The centre x-coordinate \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag Clear if the line fits on-screen, set if it doesn't \ \ X1, X2 The x-coordinates of the clipped line \ \ LSO+Y If the line doesn't fit, LSO+Y is set to 0 \ \ Y Y is preserved \ \ ****************************************************************************** .EDGES STA T \ Set T to the line's half-length in argument A CLC \ We now calculate: ADC YY \ STA X2 \ (A X2) = YY(1 0) + A \ \ to set X2 to the x-coordinate of the right end of the \ line, starting with the low bytes LDA YY+1 \ And then adding the high bytes ADC #0 BMI ED1 \ If the addition is negative then the calculation has \ overflowed, so jump to ED1 to return a failure BEQ P%+6 \ If the high byte A from the result is 0, skip the \ next two instructions, as the result already fits on \ the screen LDA #254 \ The high byte is positive and non-zero, so we went STA X2 \ past the right edge of the screen, so clip X2 to the \ x-coordinate of the right edge of the screen LDA YY \ We now calculate: SEC \ SBC T \ (A X1) = YY(1 0) - argument A STA X1 \ \ to set X1 to the x-coordinate of the left end of the \ line, starting with the low bytes LDA YY+1 \ And then subtracting the high bytes SBC #0 BNE ED3 \ If the high byte subtraction is non-zero, then skip \ to ED3 CLC \ Otherwise the high byte of the subtraction was zero, \ so the line fits on-screen and we clear the C flag to \ indicate success RTS \ Return from the subroutine .ED3 BPL ED1 \ If the addition is positive then the calculation has \ underflowed, so jump to ED1 to return a failure LDA #2 \ The high byte is negative and non-zero, so we went STA X1 \ past the left edge of the screen, so clip X1 to the \ x-coordinate of the left edge of the screen CLC \ The line does fit on-screen, so clear the C flag to \ indicate success RTS \ Return from the subroutine .ED1 \ --- Mod: Code removed for two-player Elite: ---------> \LDA #0 \ Set the Y-th byte of the LSO block to 0 \STA LSO,Y \ --- And replaced by: --------------------------------> LDA #0 \ Set the Y-th byte of the LSO block to 0 STA (LSOS),Y \ --- End of replacement ------------------------------> SEC \ The line does not fit on the screen, so set the C flag \ to indicate this result RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: CHKON \ Type: Subroutine \ Category: Drawing circles \ Summary: Check whether any part of a circle appears on the extended screen \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ K The circle's radius \ \ K3(1 0) Pixel x-coordinate of the centre of the circle \ \ K4(1 0) Pixel y-coordinate of the centre of the circle \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag Clear if any part of the circle appears on-screen, set \ if none of the circle appears on-screen \ \ (A X) Minimum y-coordinate of the circle on-screen (i.e. the \ y-coordinate of the top edge of the circle) \ \ P(2 1) Maximum y-coordinate of the circle on-screen (i.e. the \ y-coordinate of the bottom edge of the circle) \ \ ****************************************************************************** .CHKON LDA K3 \ Set A = K3 + K CLC ADC K LDA K3+1 \ Set A = K3+1 + 0 + any carry from above, so this ADC #0 \ effectively sets A to the high byte of K3(1 0) + K: \ \ (A ?) = K3(1 0) + K \ \ so A is the high byte of the x-coordinate of the right \ edge of the circle BMI PL21 \ If A is negative then the right edge of the circle is \ to the left of the screen, so jump to PL21 to set the \ C flag and return from the subroutine, as the whole \ circle is off-screen to the left LDA K3 \ Set A = K3 - K SEC SBC K LDA K3+1 \ Set A = K3+1 - 0 - any carry from above, so this SBC #0 \ effectively sets A to the high byte of K3(1 0) - K: \ \ (A ?) = K3(1 0) - K \ \ so A is the high byte of the x-coordinate of the left \ edge of the circle BMI PL31 \ If A is negative then the left edge of the circle is \ to the left of the screen, and we already know the \ right edge is either on-screen or off-screen to the \ right, so skip to PL31 to move on to the y-coordinate \ checks, as at least part of the circle is on-screen in \ terms of the x-axis BNE PL21 \ If A is non-zero, then the left edge of the circle is \ to the right of the screen, so jump to PL21 to set the \ C flag and return from the subroutine, as the whole \ circle is off-screen to the right .PL31 LDA K4 \ Set P+1 = K4 + K CLC ADC K STA P+1 LDA K4+1 \ Set A = K4+1 + 0 + any carry from above, so this ADC #0 \ does the following: \ \ (A P+1) = K4(1 0) + K \ \ so A is the high byte of the y-coordinate of the \ bottom edge of the circle BMI PL21 \ If A is negative then the bottom edge of the circle is \ above the top of the screen, so jump to PL21 to set \ the C flag and return from the subroutine, as the \ whole circle is off-screen to the top STA P+2 \ Store the high byte in P+2, so now we have: \ \ P(2 1) = K4(1 0) + K \ \ i.e. the maximum y-coordinate of the circle on-screen \ (which we return) LDA K4 \ Set X = K4 - K SEC SBC K TAX LDA K4+1 \ Set A = K4+1 - 0 - any carry from above, so this SBC #0 \ does the following: \ \ (A X) = K4(1 0) - K \ \ so A is the high byte of the y-coordinate of the top \ edge of the circle BMI PL44 \ If A is negative then the top edge of the circle is \ above the top of the screen, and we already know the \ bottom edge is either on-screen or below the bottom \ of the screen, so skip to PL44 to clear the C flag and \ return from the subroutine using a tail call, as part \ of the circle definitely appears on-screen BNE PL21 \ If A is non-zero, then the top edge of the circle is \ below the bottom of the screen, so jump to PL21 to set \ the C flag and return from the subroutine, as the \ whole circle is off-screen to the bottom \ --- Mod: Code removed for two-player Elite: ---------> \CPX #2*Y-1 \ If we get here then A is zero, which means the top \ \ edge of the circle is within the screen boundary, so \ \ now we need to check whether it is in the space view \ \ (in which case it is on-screen) or the dashboard (in \ \ which case the top of the circle is hidden by the \ \ dashboard, so the circle isn't on-screen). We do this \ \ by checking the low byte of the result in X against \ \ 2 * #Y - 1, and returning the C flag from this \ \ comparison. The constant #Y is the y-coordinate of the \ \ mid-point of the space view, so 2 * #Y - 1, the \ \ y-coordinate of the bottom pixel row of the space \ \ view. So this does the following: \ \ \ \ * The C flag is set if coordinate (A X) is below the \ \ bottom row of the space view, i.e. the top edge of \ \ the circle is hidden by the dashboard \ \ \ \ * The C flag is clear if coordinate (A X) is above \ \ the bottom row of the space view, i.e. the top \ \ edge of the circle is on-screen \ --- And replaced by: --------------------------------> CPX heightInCHKON \ Compare the y-coordinate against the bottom of the \ screen, which will either be full height (#2*Y-1) for \ circles or half-height (#Y-1) for the sun \ --- End of replacement ------------------------------> RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PL21 \ Type: Subroutine \ Category: Drawing planets \ Summary: Return from a planet/sun-drawing routine with a failure flag \ \ ------------------------------------------------------------------------------ \ \ Set the C flag and return from the subroutine. This is used to return from a \ planet- or sun-drawing routine with the C flag indicating an overflow in the \ calculation. \ \ ****************************************************************************** .PL21 SEC \ Set the C flag to indicate an overflow RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PLS3 \ Type: Subroutine \ Category: Drawing planets \ Summary: Calculate (Y A P) = 222 * roofv_x / z \ \ ------------------------------------------------------------------------------ \ \ Calculate the following, with X determining the vector to use: \ \ (Y A P) = 222 * roofv_x / z \ \ though in reality only (Y A) is used. \ \ Although the code below supports a range of values of X, in practice the \ routine is only called with X = 15, and then again after X has been \ incremented to 17. So the values calculated by PLS1 use roofv_x first, then \ roofv_y. The comments below refer to roofv_x, for the first call. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X Determines which of the INWK orientation vectors to \ divide: \ \ * X = 15: divides roofv_x \ \ * X = 17: divides roofv_y \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X X gets incremented by 2 so it points to the next \ coordinate in this orientation vector (so consecutive \ calls to the routine will start with x, then move onto y \ and then z) \ \ ****************************************************************************** .PLS3 JSR PLS1 \ Call PLS1 to calculate the following: STA P \ \ P = |roofv_x / z| \ K+3 = sign of roofv_x / z \ \ and increment X to point to roofv_y for the next call LDA #222 \ Set Q = 222, the offset to the crater STA Q STX U \ Store the vector index X in U for retrieval after the \ call to MULTU JSR MULTU \ Call MULTU to calculate \ \ (A P) = P * Q \ = 222 * |roofv_x / z| LDX U \ Restore the vector index from U into X LDY K+3 \ If the sign of the result in K+3 is positive, skip to BPL PL12 \ PL12 to return with Y = 0 EOR #&FF \ Otherwise the result should be negative, so negate the CLC \ high byte of the result using two's complement with ADC #1 \ A = ~A + 1 BEQ PL12 \ If A = 0, jump to PL12 to return with (Y A) = 0 LDY #&FF \ Set Y = &FF to be a negative high byte RTS \ Return from the subroutine .PL12 LDY #0 \ Set Y = 0 to be a positive high byte RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PLS4 \ Type: Subroutine \ Category: Drawing planets \ Summary: Calculate CNT2 = arctan(P / A) / 4 \ \ ------------------------------------------------------------------------------ \ \ Calculate the following: \ \ CNT2 = arctan(P / A) / 4 \ \ and do the following if nosev_z_hi >= 0: \ \ CNT2 = CNT2 + 32 \ \ which is the equivalent of adding 180 degrees to the result (or PI radians), \ as there are 64 segments in a full circle. \ \ This routine is called with the following arguments when calculating the \ equator and meridian for planets: \ \ * A = roofv_z_hi, P = -nosev_z_hi \ \ * A = sidev_z_hi, P = -nosev_z_hi \ \ So it calculates the angle between the planet's orientation vectors, in the \ z-axis. \ \ ****************************************************************************** .PLS4 STA Q \ Set Q = A JSR ARCTAN \ Call ARCTAN to calculate: \ \ A = arctan(P / Q) \ arctan(P / A) \ \ The result in A will be in the range 0 to 128, which \ represents an angle of 0 to 180 degrees (or 0 to PI \ radians) LDX INWK+14 \ If nosev_z_hi is negative, skip the following BMI P%+4 \ instruction to leave the angle in A as a positive \ integer in the range 0 to 128 (so when we calculate \ CNT2 below, it will be in the right half of the \ anti-clockwise arc that we describe when drawing \ circles, i.e. from 6 o'clock, through 3 o'clock and \ on to 12 o'clock) EOR #%10000000 \ If we get here then nosev_z_hi is positive, so flip \ bit 7 of the angle in A, which is the same as adding \ 128 to give a result in the range 129 to 256 (i.e. 129 \ to 0), or 180 to 360 degrees (so when we calculate \ CNT2 below, it will be in the left half of the \ anti-clockwise arc that we describe when drawing \ circles, i.e. from 12 o'clock, through 9 o'clock and \ on to 6 o'clock) LSR A \ Set CNT2 = A / 4 LSR A STA CNT2 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PLS5 \ Type: Subroutine \ Category: Drawing planets \ Summary: Calculate roofv_x / z and roofv_y / z \ \ ------------------------------------------------------------------------------ \ \ Calculate the following divisions of a specified value from one of the \ orientation vectors (in this example, roofv): \ \ (XX16+2 K2+2) = roofv_x / z \ \ (XX16+3 K2+3) = roofv_y / z \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X Determines which of the INWK orientation vectors to \ divide: \ \ * X = 15: divides roofv_x and roofv_y \ \ * X = 21: divides sidev_x and sidev_y \ \ INWK The planet's ship data block \ \ ****************************************************************************** .PLS5 JSR PLS1 \ Call PLS1 to calculate the following: STA K2+2 \ STY XX16+2 \ K+2 = |roofv_x / z| \ XX16+2 = sign of roofv_x / z \ \ i.e. (XX16+2 K2+2) = roofv_x / z \ \ and increment X to point to roofv_y for the next call JSR PLS1 \ Call PLS1 to calculate the following: STA K2+3 \ STY XX16+3 \ K+3 = |roofv_y / z| \ XX16+3 = sign of roofv_y / z \ \ i.e. (XX16+3 K2+3) = roofv_y / z \ \ and increment X to point to roofv_z for the next call RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PLS6 \ Type: Subroutine \ Category: Drawing planets \ Summary: Calculate (X K) = (A P+1 P) / (z_sign z_hi z_lo) \ \ ------------------------------------------------------------------------------ \ \ Calculate the following: \ \ (X K) = (A P+1 P) / (z_sign z_hi z_lo) \ \ returning an overflow in the C flag if the result is >= 1024. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ INWK The planet or sun's ship data block \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag Set if the result >= 1024, clear otherwise \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ PL44 Clear the C flag and return from the subroutine \ \ ****************************************************************************** .PLS6 JSR DVID3B2 \ Call DVID3B2 to calculate: \ \ K(3 2 1 0) = (A P+1 P) / (z_sign z_hi z_lo) LDA K+3 \ Set A = |K+3| OR K+2 AND #%01111111 ORA K+2 BNE PL21 \ If A is non-zero then the two high bytes of K(3 2 1 0) \ are non-zero, so jump to PL21 to set the C flag and \ return from the subroutine \ We can now just consider K(1 0), as we know the top \ two bytes of K(3 2 1 0) are both 0 LDX K+1 \ Set X = K+1, so now (X K) contains the result in \ K(1 0), which is the format we want to return the \ result in CPX #4 \ If the high byte of K(1 0) >= 4 then the result is BCS PL6 \ >= 1024, so return from the subroutine with the C flag \ set to indicate an overflow (as PL6 contains an RTS) LDA K+3 \ Fetch the sign of the result from K+3 (which we know \ has zeroes in bits 0-6, so this just fetches the sign) \CLC \ This instruction is commented out in the original \ source. It would have no effect as we know the C flag \ is already clear, as we skipped past the BCS above BPL PL6 \ If the sign bit is clear and the result is positive, \ then the result is already correct, so return from \ the subroutine with the C flag clear to indicate \ success (as PL6 contains an RTS) LDA K \ Otherwise we need to negate the result, which we do EOR #%11111111 \ using two's complement, starting with the low byte: ADC #1 \ STA K \ K = ~K + 1 TXA \ And then the high byte: EOR #%11111111 \ ADC #0 \ X = ~X TAX .PL44 CLC \ Clear the C flag to indicate success .PL6 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TT17 \ Type: Subroutine \ Category: Keyboard \ Summary: Scan the keyboard for cursor key or joystick movement \ \ ------------------------------------------------------------------------------ \ \ Scan the keyboard and joystick for cursor key or stick movement, and return \ the result as deltas (changes) in x- and y-coordinates as follows: \ \ * For joystick, X and Y are integers between -2 and +2 depending on how far \ the stick has moved \ \ * For keyboard, X and Y are integers between -1 and +1 depending on which \ keys are pressed \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A The key pressed, if the arrow keys were used \ \ X Change in the x-coordinate according to the cursor keys \ being pressed or joystick movement, as an integer (see \ above) \ \ Y Change in the y-coordinate according to the cursor keys \ being pressed or joystick movement, as an integer (see \ above) \ \ ****************************************************************************** .TT17 JSR DOKEY \ Scan the keyboard for flight controls and pause keys, \ (or the equivalent on joystick) and update the key \ logger, setting KL to the key pressed LDX #0 \ Call DKS4 to check whether the SHIFT key is being JSR DKS4 \ pressed STA newlocn \ Store the result (which will have bit 7 set if SHIFT \ is being pressed) in newlocn LDA JSTK \ If the joystick is not configured, jump down to TJ1, BEQ TJ1 \ otherwise we move the cursor with the joystick LDA JSTX \ Fetch the joystick roll, ranging from 1 to 255 with \ 128 as the centre point EOR #&FF \ Flip the sign so A = -JSTX, because the joystick roll \ works in the opposite way to moving a cursor on-screen \ in terms of left and right JSR TJS1 \ Call TJS1 just below to set A to a value between -2 \ and +2 depending on the joystick roll value (moving \ the stick sideways) TYA \ Copy Y to A BIT newlocn \ If bit 7 of newlocn is clear - in other words, if BPL P%+3 \ SHIFT is not being pressed - then skip the following \ instruction ASL A \ SHIFT is being held down, so double the value of A \ (i.e. SHIFT moves the cursor at double the speed \ when using the joystick) TAX \ Copy A to X, so X contains the joystick roll value LDA JSTY \ Fetch the joystick pitch, ranging from 1 to 255 with \ 128 as the centre point, and fall through into TJS1 to \ set Y to the joystick pitch value (moving the stick up \ and down) .TJS1 TAY \ Store A in Y LDA #0 \ Set the result, A = 0 CPY #16 \ If Y >= 16 set the C flag, so A = A - 1 SBC #0 CPY #64 \ If Y >= 64 set the C flag, so A = A - 1 SBC #0 CPY #192 \ If Y >= 192 set the C flag, so A = A + 1 ADC #0 CPY #224 \ If Y >= 224 set the C flag, so A = A + 1 ADC #0 BIT newlocn \ If bit 7 of newlocn is clear - in other words, if BPL P%+3 \ SHIFT is not being pressed - then skip the following \ instruction ASL A \ SHIFT is being held down, so double the value of A \ (i.e. SHIFT moves the cursor at double the speed \ when using the joystick TAY \ Copy the value of A into Y LDA KL \ Set A to the value of KL (the key pressed) RTS \ Return from the subroutine .newlocn EQUB 0 \ The current key press is stored here in the above code \ when we check whether SHIFT is being held down .TJ1 LDA KL \ Set A to the value of KL (the key pressed) LDX #0 \ Set the initial values for the results, X = Y = 0, LDY #0 \ which we now increase or decrease appropriately CMP #&19 \ If left arrow was pressed, set X = X - 1 BNE P%+3 DEX CMP #&79 \ If right arrow was pressed, set X = X + 1 BNE P%+3 INX CMP #&39 \ If up arrow was pressed, set Y = Y + 1 BNE P%+3 INY CMP #&29 \ If down arrow was pressed, set Y = Y - 1 BNE P%+3 DEY TXA \ Transfer the value of X into A BIT newlocn \ If bit 7 of newlocn is clear - in other words, if BPL P%+4 \ SHIFT is not being pressed - then skip the following \ two instructions ASL A \ SHIFT is being held down, so quadruple the value of A ASL A \ (i.e. SHIFT moves the cursor at four times the speed \ when using the keyboard) TAX \ Transfer the amended value of A back into X TYA \ Transfer the value of Y into A BIT newlocn \ If bit 7 of newlocn is clear - in other words, if BPL P%+4 \ SHIFT is not being pressed - then skip the following \ two instructions ASL A \ SHIFT is being held down, so quadruple the value of A ASL A \ (i.e. SHIFT moves the cursor at four times the speed \ when using the keyboard) TAY \ Transfer the amended value of A back into Y LDA KL \ Set A to the value of KL (the key pressed) RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: ping \ Type: Subroutine \ Category: Universe \ Summary: Set the selected system to the current system \ \ ****************************************************************************** .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 \ ****************************************************************************** \ \ Save ELTE.bin \ \ ****************************************************************************** PRINT "ELITE E" PRINT "Assembled at ", ~CODE_E% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_E%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_E% PRINT "S.ELTE ", ~CODE_E%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_E% SAVE "3-assembled-output/ELTE.bin", CODE_E%, P%, LOAD% \ ****************************************************************************** \ \ ELITE F FILE \ \ Produces the binary file ELTF.bin that gets loaded by elite-bcfs.asm. \ \ ****************************************************************************** CODE_F% = P% LOAD_F% = LOAD% + P% - CODE% \ ****************************************************************************** \ \ Name: KS3 \ Type: Subroutine \ Category: Universe \ Summary: Set the SLSP ship line heap pointer after shuffling ship slots \ \ ------------------------------------------------------------------------------ \ \ The final part of the KILLSHP routine, called after we have shuffled the ship \ slots and sorted out our missiles. This simply sets SLSP to the new bottom of \ the ship line heap. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ P(1 0) Points to the ship line heap of the ship in the last \ occupied slot (i.e. it points to the bottom of the \ descending heap) \ \ ****************************************************************************** .KS3 LDA P \ After shuffling the ship slots, P(1 0) will point to STA SLSP \ the new bottom of the ship line heap, so store this in LDA P+1 \ SLSP(1 0), which stores the bottom of the heap STA SLSP+1 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: KS1 \ Type: Subroutine \ Category: Universe \ Summary: Remove the current ship from our local bubble of universe \ \ ------------------------------------------------------------------------------ \ \ Part 12 of the main flight loop calls this routine to remove the ship that is \ currently being analysed by the flight loop. Once the ship is removed, it \ jumps back to MAL1 to rejoin the main flight loop, with X pointing to the \ same slot that we just cleared (and which now contains the next ship in the \ local bubble of universe). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ XX0 The address of the blueprint for this ship \ \ INF The address of the data block for this ship \ \ ****************************************************************************** .KS1 LDX XSAV \ Fetch the current ship's slot number from XSAV JSR KILLSHP \ Call KILLSHP to remove the ship in slot X from our \ local bubble of universe LDX XSAV \ Restore the current ship's slot number from XSAV, \ which now points to the next ship in the bubble JMP MAL1 \ Jump to MAL1 to rejoin the main flight loop at the \ start of the ship analysis loop \ ****************************************************************************** \ \ Name: KS4 \ Type: Subroutine \ Category: Universe \ Summary: Remove the space station and replace it with the sun \ \ ****************************************************************************** .KS4 JSR ZINF \ Call ZINF to reset the INWK ship workspace JSR FLFLLS \ Reset the LSO block, returns with A = 0 STA FRIN+1 \ Set the second slot in the FRIN table to 0, which \ sets this slot to empty, so when we call NWSHP below \ the new sun that gets created will go into FRIN+1 STA SSPR \ Set the "space station present" flag to 0, as we are \ no longer in the space station's safe zone JSR SPBLB \ Call SPBLB to redraw the space station bulb, which \ will erase it from the dashboard LDA #6 \ Set the sun's y_sign to 6 STA INWK+5 LDA #129 \ Set A = 129, the ship type for the sun JMP NWSHP \ Call NWSHP to set up the sun's data block and add it \ to FRIN, where it will get put in the second slot as \ we just cleared out the second slot, and the first \ slot is already taken by the planet \ ****************************************************************************** \ \ Name: KS2 \ Type: Subroutine \ Category: Universe \ Summary: Check the local bubble for missiles with target lock \ \ ------------------------------------------------------------------------------ \ \ Check the local bubble of universe to see if there are any missiles with \ target lock in the vicinity. If there are, then check their targets; if we \ just removed their target in the KILLSHP routine, then switch off their AI so \ they just drift in space, otherwise update their targets to reflect the newly \ shuffled slot numbers. \ \ This is called from KILLSHP once the slots have been shuffled down, following \ the removal of a ship. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ XX4 The slot number of the ship we removed just before \ calling this routine \ \ ****************************************************************************** .KS2 LDX #&FF \ We want to go through the ships in our local bubble \ and pick out all the missiles, so set X to &FF to \ use as a counter .KSL4 INX \ Increment the counter (so it starts at 0 on the first \ iteration) LDA FRIN,X \ If slot X is empty then we have worked our way through BEQ KS3 \ all the slots, so jump to KS3 to stop looking CMP #MSL \ If the slot does not contain a missile, loop back to BNE KSL4 \ KSL4 to check the next slot \ We have found a slot containing a missile, so now we \ want to check whether it has target lock TXA \ Set Y = X * 2 and fetch the Y-th address from UNIV ASL A \ and store it in SC and SC+1 - in other words, set TAY \ SC(1 0) to point to the missile's ship data block LDA UNIV,Y STA SC LDA UNIV+1,Y STA SC+1 LDY #32 \ Fetch byte #32 from the missile's ship data (AI) LDA (SC),Y BPL KSL4 \ If bit 7 of byte #32 is clear, then the missile is \ dumb and has no AI, so loop back to KSL4 to move on \ to the next slot AND #%01111111 \ Otherwise this missile has AI, so clear bit 7 and LSR A \ shift right to set the C flag to the missile's "is \ locked" flag, and A to the target's slot number CMP XX4 \ If this missile's target is less than XX4, then the BCC KSL4 \ target's slot isn't being shuffled down, so jump to \ KSL4 to move on to the next slot BEQ KS6 \ If this missile was locked onto the ship that we just \ removed in KILLSHP, jump to KS6 to stop the missile \ from continuing to hunt it down SBC #1 \ Otherwise this missile is locked and has AI enabled, \ and its target will have moved down a slot, so \ subtract 1 from the target number (we know C is set \ from the BCC above) ASL A \ Shift the target number left by 1, so it's in bits \ 1-6 once again, and also set bit 0 to 1, as the C \ flag is still set, so this makes sure the missile is \ still set to being locked ORA #%10000000 \ Set bit 7, so the missile's AI is enabled STA (SC),Y \ Update the missile's AI flag to the value in A BNE KSL4 \ Loop back to KSL4 to move on to the next slot (this \ BNE is effectively a JMP as A will never be zero) .KS6 LDA #0 \ The missile's target lock just got removed, so set the STA (SC),Y \ AI flag to 0 to make it dumb and not locked BEQ KSL4 \ Loop back to KSL4 to move on to the next slot (this \ BEQ is effectively a JMP as A is always zero) \ ****************************************************************************** \ \ Name: KILLSHP \ Type: Subroutine \ Category: Universe \ Summary: Remove a ship from our local bubble of universe \ \ ------------------------------------------------------------------------------ \ \ Remove the ship in slot X from our local bubble of universe. This happens \ when we kill a ship, collide with a ship and destroy it, or when a ship moves \ outside our local bubble. \ \ We also use this routine when we move out of range of the space station, in \ which case we replace it with the sun. \ \ When removing a ship, this creates a gap in the ship slots at FRIN, so we \ shuffle all the later slots down to close the gap. We also shuffle the ship \ data blocks at K% and ship line heap at WP, to reclaim all the memory that \ the removed ship used to occupy. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The slot number of the ship to remove \ \ XX0 The address of the blueprint for the ship to remove \ \ INF The address of the data block for the ship to remove \ \ ****************************************************************************** .KILLSHP \ --- Mod: Code added for two-player Elite: -----------> BIT gameOver \ If this is the game over screen, jump to MAC1 to skip BPL P%+5 \ the ship removal and rejoin the main loop JMP MAC1 \ --- End of added code -------------------------------> STX XX4 \ Store the slot number of the ship to remove in XX4 LDA MSTG \ Check whether this slot matches the slot number in CMP XX4 \ MSTG, which is the target of our missile lock BNE KS5 \ If our missile is not locked on this ship, jump to KS5 LDY #GREEN2 \ Otherwise we need to remove our missile lock, so call JSR ABORT \ ABORT to disarm the missile and update the missile \ indicators on the dashboard to green (Y = #GREEN2) LDA #200 \ Print recursive token 40 ("TARGET LOST") as an JSR MESS \ in-flight message .KS5 \ --- Mod: Code added for two-player Elite: -----------> LDA player2MSTG \ Check whether this slot matches the slot number in CMP XX4 \ MSTG, which is the target of our missile lock BNE kshp1 \ If our missile is not locked on this ship, jump to \ kshp1 LDY #GREEN2 \ Otherwise we need to remove our missile lock, so call JSR Player2ABORT \ ABORT to disarm the missile and update the missile \ indicators on the dashboard to green (Y = #GREEN2) LDA #200 \ Print recursive token 40 ("TARGET LOST") as an JSR Player2MESS \ in-flight message .kshp1 LDY XX4 \ Restore the slot number of the ship to remove into Y CPY #4 \ If we are not removing the missile in slot #4, jump to BNE kshp2 \ kshp2 to keep checking JSR RemoveMissile \ Remove this missile from the relevant player's count, \ so they can fire another one if they want STZ player1INWK31+2 \ Reset the INWK+31 value for the missile to remove it \ from the screen .kshp2 CPY #3 \ If we are not removing the missile in slot #3, jump to BNE kshp3 \ kshp3 to move onto the normal KILLSHP routine \ If we get here then we are removing the missile in \ slot #3, so KILLSHP will shuffle slot #4 into slot #3 \ \ We therefore need to shuffle the player1INWK31 value \ for the missile down, and if there is an object in \ slot #4 then we will also need to shuffle the data \ for slot #14 down into slot #13, so we keep the slots \ for the player 1 and player 2 view in sync JSR RemoveMissile \ Remove this missile from the relevant player's count, \ so they can fire another one if they want LDA player1INWK31+2 \ First, shuffle the INWK+31 value for slot #4 down into STA player1INWK31+1 \ slot #3 STZ player1INWK31+2 LDA FRIN+4 \ If slot #4 is empty then there is no data to shuffle, BEQ kshp3 \ so jump to kshp3 to skip the following \ There is data in slot #4, so there is also data in \ slot #14, so now we need to shuffle the data for slot \ #14 into slot #13 \ \ We can do this by calling the normal KILLSHP routine \ below for slot #13, and then we can continue on with \ running KILLSHP for slot #3 LDX #13 \ Set XX4 so we remove slot #13 STX XX4 JSR GINF \ Set up INF(1 0) for slot #13 LDA #1 \ Set the type of slot #14 to non-zero so we shuffle it STA FRIN+14 \ down into slot #13 STZ FRIN+15 \ Zero slot #15 so we don't shuffle that one down LDA XX0 \ Store XX0(1 0) on the stack PHA LDA XX0+1 PHA LDA SLSP \ Store SLSP(1 0) on the stack PHA LDA SLSP+1 PHA LDA XX21 \ Set XX0 to the missile ship blueprint STA XX0 LDA XX21+1 STA XX0+1 JSR kshp3 \ By this point: \ \ * XX4 is set to 13 so we remove slot #13 \ \ * Slot #14 is set to a non-empty missile \ \ * Slot #15 is set to empty \ \ * INF(1 0) points to slot #13's ship data block \ \ * XX0(1 0) points to the missile ship blueprint \ \ So now we can call the normal KILLSHP routine at kshp3 \ to shuffle slot #14 down into slot #13 STZ FRIN+14 \ Zero the slot types for slot #13 and #14 once again STZ FRIN+13 PLA \ Retrieve SLSP(1 0) from the stack STA SLSP+1 PLA STA SLSP PLA \ Retrieve XX0(1 0) from the stack STA XX0+1 PLA STA XX0 LDX #3 \ Set up INF(1 0) for slot #3 JSR GINF LDY #3 \ Set XX4 so we remove slot #3 STY XX4 \ Fall through into the normal KILLSHP routine at kshp3 \ to shuffle slot #4 down into slot #3 .kshp3 CPY #2 \ If we are not removing player 2's ship in slot #2, BNE kshp4 \ jump to kshp4 to skip the following JMP MAC1 \ We must be in the game over screen by this point, so \ don't remove player 2's ship or it will mess up the \ missile slots, so just jump back to the main loop \ without emptying slot #2 .kshp4 \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \LDY XX4 \ Restore the slot number of the ship to remove into Y \ \LDX FRIN,Y \ Fetch the contents of the slot, which contains the \ \ ship type \ \CPX #SST \ If this is the space station, then jump to KS4 to \BEQ KS4 \ replace the space station with the sun \ \CPX #CON \ Did we just kill the Constrictor from mission 1? If \BNE lll \ not, jump to lll \ \LDA TP \ We just killed the Constrictor from mission 1, so set \ORA #%00000010 \ bit 1 of TP to indicate that we have successfully \STA TP \ completed mission 1 \ \.lll \ \CPX #HER \ Did we just kill a rock hermit? If we did, jump to \BEQ blacksuspenders \ blacksuspenders to decrease the junk count \ \CPX #JL \ If JL <= X < JH, i.e. the type of ship we killed in X \BCC KS7 \ is junk (escape pod, alloy plate, cargo canister, \CPX #JH \ asteroid, splinter, Shuttle or Transporter), then keep \BCS KS7 \ going, otherwise jump to KS7 \ \.blacksuspenders \ \DEC JUNK \ We just killed junk, so decrease the junk counter \ \.KS7 \ \DEC MANY,X \ Decrease the number of this type of ship in our little \ \ bubble, which is stored in MANY+X (where X is the ship \ \ type) \ --- End of removed code -----------------------------> LDX XX4 \ Restore the slot number of the ship to remove into X \ We now want to remove this ship and reclaim all the \ memory that it uses. Removing the ship will leave a \ gap in three places, which we need to close up: \ \ * The ship slots in FRIN \ \ * The ship data blocks in K% \ \ * The descending ship line heap at WP down \ \ The rest of this routine closes up these gaps by \ looping through all the occupied ship slots after the \ slot we are removing, one by one, and shuffling each \ ship's slot, data block and line heap down to close \ up the gaps left by the removed ship. As part of this, \ we have to make sure we update any address pointers \ so they point to the newly shuffled data blocks and \ line heaps \ \ In the following, when shuffling a ship's data down \ into the preceding empty slot, we call the ship that \ we are shuffling down the "source", and we call the \ empty slot we are shuffling it into the "destination" \ \ Before we start looping through the ships we need to \ shuffle down, we need to set up some variables to \ point to the source and destination line heaps LDY #5 \ Fetch byte #5 of the removed ship's blueprint into A, LDA (XX0),Y \ which gives the ship's maximum heap size for the ship \ we are removing (i.e. the size of the gap in the heap \ created by the ship removal) \ INF currently contains the ship data for the ship we \ are removing, and INF(34 33) contains the address of \ the bottom of the ship's heap, so we can calculate \ the address of the top of the heap by adding the heap \ size to this address LDY #33 \ First we add A and the address in INF+33, to get the CLC \ low byte of the top of the heap, which we store in P ADC (INF),Y STA P INY \ And next we add A and the address in INF+34, with any LDA (INF),Y \ carry from the previous addition, to get the high byte ADC #0 \ of the top of the heap, which we store in P+1, so STA P+1 \ P(1 0) points to the top of this ship's heap \ Now, we're ready to start looping through the ships \ we want to move, moving the slots, data blocks and \ line heap from the source to the destination. In the \ following, we set up SC to point to the source data, \ and INF (which currently points to the removed ship's \ data that we can now overwrite) points to the \ destination \ \ So P(1 0) now points to the top of the line heap for \ the destination .KSL1 INX \ On entry, X points to the empty slot we want to \ shuffle the next ship into (the destination), so \ this increment points X to the next slot - i.e. the \ source slot we want to shuffle down LDA FRIN,X \ Copy the contents of the source slot into the STA FRIN-1,X \ destination slot BNE P%+5 \ If the slot we just shuffled down is not empty, then \ skip the following instruction JMP KS2 \ The source slot is empty and we are done shuffling, \ so jump to KS2 to move on to processing missiles ASL A \ Otherwise we have a source ship to shuffle down into TAY \ the destination, so set Y = A * 2 so it can act as an \ index into the two-byte ship blueprint lookup table \ at XX21 for the source ship LDA XX21-2,Y \ Set SC(0 1) to point to the blueprint data for the STA SC \ source ship LDA XX21-1,Y STA SC+1 LDY #5 \ Fetch blueprint byte #5 for the source ship, which LDA (SC),Y \ gives us its maximum heap size, and store it in T STA T \ We now subtract T from P(1 0), so P(1 0) will point to \ the bottom of the line heap for the destination \ (which we will use later when closing up the gap in \ the heap space) LDA P \ First, we subtract the low bytes SEC SBC T STA P LDA P+1 \ And then we do the high bytes, for which we subtract SBC #0 \ 0 to include any carry, so this is effectively doing STA P+1 \ P(1 0) = P(1 0) - (0 T) \ Next, we want to set SC(1 0) to point to the source \ ship's data block TXA \ Set Y = X * 2 so it can act as an index into the ASL A \ two-byte lookup table at UNIV, which contains the TAY \ addresses of the ship data blocks. In this case we are \ multiplying X by 2, and X contains the source ship's \ slot number so Y is now an index for the source ship's \ entry in UNIV LDA UNIV,Y \ Set SC(1 0) to the address of the data block for the STA SC \ source ship LDA UNIV+1,Y STA SC+1 \ We have now set up our variables as follows: \ \ SC(1 0) points to the source's ship data block \ \ INF(1 0) points to the destination's ship data block \ \ P(1 0) points to the destination's line heap \ \ so let's start copying data from the source to the \ destination LDY #36 \ We are going to be using Y as a counter for the 37 \ bytes of ship data we want to copy from the source \ to the destination, so we set it to 36 to start things \ off, and will decrement Y for each byte we copy LDA (SC),Y \ Fetch byte #36 of the source's ship data block at SC, STA (INF),Y \ and store it in byte #36 of the destination's block DEY \ at INF, so that's the ship's NEWB flags copied from \ the source to the destination. One down, quite a few \ to go... LDA (SC),Y \ Fetch byte #35 of the source's ship data block at SC, STA (INF),Y \ and store it in byte #35 of the destination's block \ at INF, so that's the ship's energy copied from the \ source to the destination DEY \ Fetch byte #34 of the source ship, which is the LDA (SC),Y \ high byte of the source ship's line heap, and store STA K+1 \ in K+1 LDA P+1 \ Set the low byte of the destination's heap pointer STA (INF),Y \ to P+1 DEY \ Fetch byte #33 of the source ship, which is the LDA (SC),Y \ low byte of the source ship's heap, and store in K STA K \ so now we have the following: \ \ K(1 0) points to the source's line heap LDA P \ Set the low byte of the destination's heap pointer STA (INF),Y \ to P, so now the destination's heap pointer is to \ P(1 0), so that's the heap pointer in bytes #33 and \ #34 done DEY \ Luckily, we can just copy the rest of the source's \ ship data block into the destination, as there are no \ more address pointers, so first we decrement our \ counter in Y to point to the next byte (the AI flag) \ in byte #32) and then start looping .KSL2 LDA (SC),Y \ Copy the Y-th byte of the source to the Y-th byte of STA (INF),Y \ the destination DEY \ Decrement the counter BPL KSL2 \ Loop back to KSL2 to copy the next byte until we have \ copied the whole block \ We have now shuffled the ship's slot and the ship's \ data block, so we only have the heap data itself to do LDA SC \ First, we copy SC into INF, so when we loop round STA INF \ again, INF will correctly point to the destination for LDA SC+1 \ the next iteration STA INF+1 LDY T \ Now we want to move the contents of the heap, as all \ we did above was to update the pointers, so first \ we set a counter in Y that is initially set to T \ (which we set above to the maximum heap size for the \ source ship) \ \ As a reminder, we have already set the following: \ \ K(1 0) points to the source's line heap \ \ P(1 0) points to the destination's line heap \ \ so we can move the heap data by simply copying the \ correct number of bytes from K(1 0) to P(1 0) .KSL3 DEY \ Decrement the counter LDA (K),Y \ Copy the Y-th byte of the source heap at K(1 0) to STA (P),Y \ the destination heap at P(1 0) TYA \ Loop back to KSL3 to copy the next byte, until we BNE KSL3 \ have done them all BEQ KSL1 \ We have now shuffled everything down one slot, so \ jump back up to KSL1 to see if there is another slot \ that needs shuffling down (this BEQ is effectively a \ JMP as A will always be zero) \ ****************************************************************************** \ \ Name: SFX \ Type: Variable \ Category: Sound \ Summary: Sound data \ \ ------------------------------------------------------------------------------ \ \ Sound data. To make a sound, the NOS1 routine copies the four relevant sound \ bytes to XX16, and NO3 then makes the sound. The sound numbers are shown in \ the table, and are always multiples of 8. Generally, sounds are made by \ calling the NOISE routine with the sound number in A. \ \ These bytes are passed to OSWORD 7, and are the equivalents to the parameters \ passed to the SOUND keyword in BASIC. The parameters therefore have these \ meanings: \ \ channel/flush, amplitude (or envelope number if 1-4), pitch, duration \ \ For the channel/flush parameter, the high nibble of the low byte is the flush \ control (where a flush control of 0 queues the sound, and a flush control of \ 1 makes the sound instantly), while the low nibble of the low byte is the \ channel number. When written in hexadecimal, the first figure gives the flush \ control, while the second is the channel (so &13 indicates flush control = 1 \ and channel = 3). \ \ So when we call NOISE with A = 40 to make a long, low beep, then this is \ effectively what the NOISE routine does: \ \ SOUND &13, &F4, &0C, &08 \ \ which makes a sound with flush control 1 on channel 3, and with amplitude &F4 \ (-12), pitch &0C (2) and duration &08 (8). Meanwhile, to make the hyperspace \ sound, the NOISE routine does this: \ \ SOUND &10, &02, &60, &10 \ \ which makes a sound with flush control 1 on channel 0, using envelope 2, \ and with pitch &60 (96) and duration &10 (16). The four sound envelopes (1-4) \ are set up by the loading process. \ \ ****************************************************************************** .SFX EQUB &12, &01, &00, &10 \ 0 - Lasers fired by us EQUB &12, &02, &2C, &08 \ 8 - We're being hit by lasers EQUB &11, &03, &F0, &18 \ 16 - We died 1 / We made a hit or kill 2 EQUB &10, &F1, &07, &1A \ 24 - We died 2 / We made a hit or kill 1 EQUB &03, &F1, &BC, &01 \ 32 - Short, high beep EQUB &13, &F4, &0C, &08 \ 40 - Long, low beep EQUB &10, &F1, &06, &0C \ 48 - Missile launched / Ship launched from station EQUB &10, &02, &60, &10 \ 56 - Hyperspace drive engaged EQUB &13, &04, &C2, &FF \ 64 - E.C.M. on EQUB &13, &00, &00, &00 \ 72 - E.C.M. off \ ****************************************************************************** \ \ Name: THERE \ Type: Subroutine \ Category: Missions \ Summary: Check whether we are in the Constrictor's system in mission 1 \ \ ------------------------------------------------------------------------------ \ \ The stolen Constrictor is the target of mission 1. We finally track it down to \ the Orarra system in the second galaxy, which is at galactic coordinates \ (144, 33). This routine checks whether we are in this system and sets the C \ flag accordingly. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag Set if we are in the Constrictor system, otherwise clear \ \ ****************************************************************************** .THERE LDX GCNT \ Set X = GCNT - 1 DEX BNE THEX \ If X is non-zero (i.e. GCNT is not 1, so we are not in \ the second galaxy), then jump to THEX LDA QQ0 \ Set A = the current system's galactic x-coordinate CMP #144 \ If A <> 144 then jump to THEX BNE THEX LDA QQ1 \ Set A = the current system's galactic y-coordinate CMP #33 \ If A = 33 then set the C flag BEQ THEX+1 \ If A = 33 then jump to THEX+1, so we return from the \ subroutine with the C flag set (otherwise we clear the \ C flag with the next instruction) .THEX CLC \ Clear the C flag RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: RESET \ Type: Subroutine \ Category: Start and end \ Summary: Reset most variables \ \ ------------------------------------------------------------------------------ \ \ Reset our ship and various controls, recharge shields and energy, and then \ fall through into RES2 to reset the stardust and the ship workspace at INWK. \ \ In this subroutine, this means zero-filling the following locations: \ \ * Pages &9, &A, &B, &C and &D \ \ * BETA to BETA+6, which covers the following: \ \ * BETA, BET1 - Set pitch to 0 \ \ * XC, YC - Set text cursor to (0, 0) \ \ * QQ22 - Set hyperspace counters to 0 \ \ * ECMA - Turn E.C.M. off \ \ It also sets QQ12 to &FF, to indicate we are docked, recharges the shields and \ energy banks, and then falls through into RES2. \ \ ****************************************************************************** .RESET JSR ZERO \ Reset the ship slots for the local bubble of universe, \ and various flight and ship status variables \ --- Mod: Code added for two-player Elite: -----------> LDX #&FF \ Recharge the forward and aft shields STX player2FSH STX player2ASH STX player2ENERGY \ Recharge the energy banks STZ GNTMP \ Cool down the lasers completely STZ player2GNTMP STZ player1Score \ Reset the scores STZ player1Score+1 STZ player2Score STZ player2Score+1 LDX #%10000000 \ Set bit 7 of titleScreen to denote we are drawing the STX titleScreen \ title screen, so the compass and scanner are disabled \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \LDX #6 \ Set up a counter for zeroing BETA through BETA+6 \ --- And replaced by: --------------------------------> LDX #4 \ Set up a counter for zeroing BETA through BETA+4 (as \ we have moved the unused QQ22 elsewhere \ --- End of replacement ------------------------------> .SAL3 STA BETA,X \ Zero the X-th byte after BETA DEX \ Decrement the loop counter BPL SAL3 \ Loop back for the next byte to zero \ --- Mod: Code added for two-player Elite: -----------> STA player2BETA \ Zero player 2's movement variables STA player2BET1 STA player2ECMA \ Turn off player 2's E.C.M. \ --- End of added code -------------------------------> TXA \ X is now negative - i.e. &FF - so this sets A and QQ12 STA QQ12 \ to &FF to indicate we are docked LDX #2 \ We're now going to recharge both shields and the \ energy bank, which live in the three bytes at FSH, \ ASH (FSH+1) and ENERGY (FSH+2), so set a loop counter \ in X for 3 bytes .REL5 STA FSH,X \ Set the X-th byte of FSH to &FF to charge up that \ shield/bank DEX \ Decrement the loop counter BPL REL5 \ Loop back to REL5 until we have recharged both shields \ and the energy bank \ Fall through into RES2 to reset the stardust and ship \ workspace at INWK \ ****************************************************************************** \ \ Name: RES2 \ Type: Subroutine \ Category: Start and end \ Summary: Reset a number of flight variables and workspaces \ \ ------------------------------------------------------------------------------ \ \ This is called after we launch from a space station, arrive in a new system \ after hyperspace, launch an escape pod, or die a cold, lonely death in the \ depths of space. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ Y Y is set to &FF \ \ ****************************************************************************** .RES2 \ --- Mod: Code removed for two-player Elite: ---------> \LDA #NOST \ Reset NOSTM, the number of stardust particles, to the \STA NOSTM \ maximum allowed (18) \ --- And replaced by: --------------------------------> LDA #NOST/2 \ Reset NOSTM, the number of stardust particles, to the STA NOSTM \ maximum allowed (9) \ --- End of replacement ------------------------------> LDX #&FF \ Reset LSX2 and LSY2, the ball line heaps used by the STX LSX2 \ BLINE routine for drawing circles, to &FF, to set the STX LSY2 \ heap to empty \ --- Mod: Code added for two-player Elite: -----------> STX LSX2a \ Reset LSX2a and LSY2a, the ball line heaps used by the STX LSY2a \ BLINE routine for drawing circles, to &FF, to set the \ player 2 ball line heap to empty \ --- End of added code -------------------------------> STX MSTG \ Reset MSTG, the missile target, to &FF (no target) \ --- Mod: Code added for two-player Elite: -----------> STX player2MSTG \ Reset player2MSTG, the missile target, to &FF (no \ target) \ --- End of added code -------------------------------> LDA #128 \ Set the current pitch rate to the mid-point, 128 STA JSTY STA ALP2 \ Reset ALP2 (roll sign) and BET2 (pitch sign) STA BET2 \ to negative, i.e. pitch and roll negative \ --- Mod: Code added for two-player Elite: -----------> STA JSTX \ Set the current roll rate to the mid-point, 128, to \ prevent random roll on launch STA player2JSTY \ Set player 2's pitch rate to the mid-point, 128 STA player2JSTX \ Set player 2's roll rate to the mid-point, 128 STA player2ALP2 \ Reset player 2's roll sign to negative STA player2BET2 \ Reset player 2's roll pitch to negative \ --- End of added code -------------------------------> ASL A \ This sets A to 0 STA BETA \ Reset BETA (pitch angle alpha) to 0 STA BET1 \ Reset BET1 (magnitude of the pitch angle) to 0 STA ALP2+1 \ Reset ALP2+1 (flipped roll sign) and BET2+1 (flipped STA BET2+1 \ pitch sign) to positive, i.e. pitch and roll negative \ --- Mod: Code added for two-player Elite: -----------> STA player2BETA \ Reset player 2's pitch angle to 0 STA player2BET1 \ Reset player 2's pitch angle magnitude to 0 STA player2ALP2+1 \ Reset player 2's flipped roll sign to positive STA player2BET2+1 \ Reset player 2's flipped pitch sign to positive \ --- End of added code -------------------------------> STA MCNT \ Reset MCNT (the main loop counter) to 0 \ --- Mod: Code removed for two-player Elite: ---------> \LDA #3 \ Reset DELTA (speed) to 3 \STA DELTA \ --- And replaced by: --------------------------------> LDA #1 \ Reset DELTA (speed) to 1 STA DELTA STA player2DELTA \ Reset player 2's speed to 1 \ --- End of replacement ------------------------------> \ --- Mod: Code added for two-player Elite: -----------> LDA #0 \ Set initial roll angles to zero, rather than 3, as we \ are not leaving the space station \ --- End of added code -------------------------------> STA ALPHA \ Reset ALPHA (roll angle alpha) to 3 STA ALP1 \ Reset ALP1 (magnitude of roll angle alpha) to 3 \ --- Mod: Code added for two-player Elite: -----------> STA player2ALPHA \ Reset player 2's roll angle to 0 STA player2ALP1 \ Reset player 2's roll angle magnitude to 0 STA drawPlayerView \ Clear bit 7 of drawPlayerView to reset the view \ drawing flag to player 1's view, so the call to WPSHPS \ clears the ships from the scanner properly \ --- End of added code -------------------------------> LDA SSPR \ Fetch the "space station present" flag, and if we are BEQ P%+5 \ not inside the safe zone, skip the next instruction JSR SPBLB \ Light up the space station bulb on the dashboard LDA ECMA \ Fetch the E.C.M. status flag, and if E.C.M. is off, BEQ yu \ skip the next instruction JSR ECMOF \ Turn off the E.C.M. sound .yu \ --- Mod: Code added for two-player Elite: -----------> LDA player2ECMA \ Fetch the E.C.M. status flag, and if E.C.M. is off, BEQ yu2 \ skip the next instruction JSR Player2ECMOF \ Turn off the E.C.M. sound .yu2 \ --- End of added code -------------------------------> JSR WPSHPS \ Wipe all ships from the scanner JSR ZERO \ Reset the ship slots for the local bubble of universe, \ and various flight and ship status variables LDA #LO(LS%) \ We have reset the ship line heap, so we now point STA SLSP \ SLSP to LS% (the byte below the ship blueprints at D%) LDA #HI(LS%) \ to indicate that the heap is empty STA SLSP+1 JSR DIALS \ Update the dashboard \ Finally, fall through into ZINF to reset the INWK \ ship workspace \ ****************************************************************************** \ \ Name: ZINF \ Type: Subroutine \ Category: Universe \ Summary: Reset the INWK workspace and orientation vectors \ Deep dive: Orientation vectors \ \ ------------------------------------------------------------------------------ \ \ Zero-fill the INWK ship workspace and reset the orientation vectors, with \ nosev pointing out of the screen, towards us. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ Y Y is set to &FF \ \ ****************************************************************************** .ZINF LDY #NI%-1 \ There are NI% bytes in the INWK workspace, so set a \ counter in Y so we can loop through them LDA #0 \ Set A to 0 so we can zero-fill the workspace .ZI1 STA INWK,Y \ Zero the Y-th byte of the INWK workspace DEY \ Decrement the loop counter BPL ZI1 \ Loop back for the next byte, ending when we have \ zero-filled the last byte at INWK, which leaves Y \ with a value of &FF \ Finally, we reset the orientation vectors as follows: \ \ sidev = (1, 0, 0) \ roofv = (0, 1, 0) \ nosev = (0, 0, -1) \ \ 96 * 256 (&6000) represents 1 in the orientation \ vectors, while -96 * 256 (&E000) represents -1. We \ already set the vectors to zero above, so we just \ need to set up the high bytes of the diagonal values \ and we're done. The negative nosev makes the ship \ point towards us, as the z-axis points into the screen LDA #96 \ Set A to represent a 1 (in vector terms) STA INWK+18 \ Set byte #18 = roofv_y_hi = 96 = 1 STA INWK+22 \ Set byte #22 = sidev_x_hi = 96 = 1 ORA #%10000000 \ Flip the sign of A to represent a -1 STA INWK+14 \ Set byte #14 = nosev_z_hi = -96 = -1 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: msblob \ Type: Subroutine \ Category: Dashboard \ Summary: Display the dashboard's missile indicators in green \ \ ------------------------------------------------------------------------------ \ \ Display the dashboard's missile indicators, with all the missiles reset to \ green (i.e. not armed or locked). \ \ ****************************************************************************** .msblob LDX #4 \ Set up a loop counter in X to count through all four \ missile indicators .ss CPX NOMSL \ If the counter is equal to the number of missiles, BEQ SAL8 \ jump down to SAL8 to draw the remaining missiles, as \ the rest of them are present and should be drawn in \ green \ --- Mod: Code added for two-player Elite: -----------> LDA #1 \ Set A to draw player 1's indicators \ --- End of added code -------------------------------> LDY #0 \ Draw the missile indicator at position X in black JSR MSBAR DEX \ Decrement the counter to point to the next missile BNE ss \ Loop back to ss if we still have missiles to draw RTS \ Return from the subroutine .SAL8 \ --- Mod: Code added for two-player Elite: -----------> LDA #1 \ Set A to draw player 1's indicators \ --- End of added code -------------------------------> LDY #GREEN2 \ Draw the missile indicator at position X in green JSR MSBAR DEX \ Decrement the counter to point to the next missile BNE SAL8 \ Loop back to SAL8 if we still have missiles to draw \ --- Mod: Code added for two-player Elite: -----------> LDX #4 \ Set up a loop counter in X to count through all four \ missile indicators .msbl1 CPX player2NOMSL \ If the counter is equal to the number of missiles, BEQ msbl2 \ jump down to msbl2 to draw the remaining missiles, as \ the rest of them are present and should be drawn in \ green LDA #2 \ Set A to draw player 2's indicators LDY #0 \ Draw the missile indicator at position X in black JSR MSBAR DEX \ Decrement the counter to point to the next missile BNE msbl1 \ Loop back to msbl1 if we still have missiles to draw RTS \ Return from the subroutine .msbl2 LDA #2 \ Set A to draw player 2's indicators LDY #GREEN2 \ Draw the missile indicator at position X in green JSR MSBAR DEX \ Decrement the counter to point to the next missile BNE msbl2 \ Loop back to msbl2 if we still have missiles to draw \ --- End of added code -------------------------------> RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: Player2me2 \ Type: Subroutine \ Category: Flight \ Summary: Remove an in-flight message from the space view \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Player2me2 LDA player2MCH \ Fetch the token number of the current message into A JSR Player2MESS \ Call MESS to print the token, which will remove it \ from the screen as printing uses EOR logic LDA #0 \ Set the delay in DLY to 0, so any new in-flight STA player2DLY \ messages will be shown instantly JMP Player2me3 \ Jump back into the main spawning loop at me3 \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: me2 \ Type: Subroutine \ Category: Flight \ Summary: Remove an in-flight message from the space view \ \ ****************************************************************************** .me2 LDA MCH \ Fetch the token number of the current message into A JSR MESS \ Call MESS to print the token, which will remove it \ from the screen as printing uses EOR logic LDA #0 \ Set the delay in DLY to 0, so any new in-flight STA DLY \ messages will be shown instantly JMP me3 \ Jump back into the main spawning loop at me3 \ ****************************************************************************** \ \ Name: Ze \ Type: Subroutine \ Category: Universe \ Summary: Initialise the INWK workspace to a fairly aggressive ship \ Deep dive: Fixing ship positions \ Aggression and hostility in ship tactics \ \ ------------------------------------------------------------------------------ \ \ Specifically, this routine does the following: \ \ * Reset the INWK ship workspace \ \ * Set the ship to a fair distance away in all axes, in front of us but \ randomly up or down, left or right \ \ * Give the ship a 4% chance of having E.C.M. \ \ * Set the ship's aggression level to at least 32 out of 63, with AI enabled \ \ This routine also sets A, X, T1 and the C flag to random values. \ \ Note that because this routine uses the value of X returned by DORND, and X \ contains the value of A returned by the previous call to DORND, this routine \ does not necessarily set the new ship to a totally random location. \ \ ****************************************************************************** .Ze JSR ZINF \ Call ZINF to reset the INWK ship workspace JSR DORND \ Set A and X to random numbers STA T1 \ Store A in T1 AND #%10000000 \ Extract the sign of A and store in x_sign STA INWK+2 TXA \ Extract the sign of X and store in y_sign AND #%10000000 STA INWK+5 LDA #25 \ Set x_hi = y_hi = z_hi = 25, a fair distance away STA INWK+1 STA INWK+4 STA INWK+7 TXA \ Set the C flag if X >= 245 (4% chance) CMP #245 ROL A \ Set bit 0 of A to the C flag (i.e. there's a 4% \ chance of this ship having E.C.M.) ORA #%11000000 \ Set bits 6 and 7 of A, so the ship has AI (bit 7) and \ an aggression level of at least 32 out of 63 STA INWK+32 \ Store A in the AI flag of this ship \ Fall through into DORND2 to set A, X and the C flag \ randomly \ ****************************************************************************** \ \ Name: DORND \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Generate random numbers \ Deep dive: Generating random numbers \ Fixing ship positions \ \ ------------------------------------------------------------------------------ \ \ Set A and X to random numbers (though note that X is set to the random number \ that was returned in A the last time DORND was called). \ \ The C and V flags are also set randomly. \ \ If we want to generate a repeatable sequence of random numbers, when \ generating explosion clouds, for example, then we call DORND2 to ensure that \ the value of the C flag on entry doesn't affect the outcome, as otherwise we \ might not get the same sequence of numbers if the C flag changes. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ DORND2 Make sure the C flag doesn't affect the outcome \ \ ****************************************************************************** .DORND2 CLC \ Clear the C flag so the value of the C flag on entry \ doesn't affect the outcome .DORND LDA RAND \ Calculate the next two values f2 and f3 in the feeder ROL A \ sequence: TAX \ ADC RAND+2 \ * f2 = (f1 << 1) mod 256 + C flag on entry STA RAND \ * f3 = f0 + f2 + (1 if bit 7 of f1 is set) STX RAND+2 \ * C flag is set according to the f3 calculation LDA RAND+1 \ Calculate the next value m2 in the main sequence: TAX \ ADC RAND+3 \ * A = m2 = m0 + m1 + C flag from feeder calculation STA RAND+1 \ * X = m1 STX RAND+3 \ * C and V flags set according to the m2 calculation RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: Main game loop (Part 1 of 6) \ Type: Subroutine \ Category: Main loop \ Summary: Spawn a trader (a Cobra Mk III, Python, Boa or Anaconda) \ Deep dive: Program flow of the main game loop \ Ship data blocks \ Aggression and hostility in ship tactics \ \ ------------------------------------------------------------------------------ \ \ This is part of the main game loop. This is where the core loop of the game \ lives, and it's in two parts. The shorter loop (just parts 5 and 6) is \ iterated when we are docked, while the entire loop from part 1 to 6 iterates \ if we are in space. \ \ This section covers the following: \ \ * Spawn a trader, i.e. a Cobra Mk III, Python, Boa or Anaconda, with a 50% \ chance of it having an E.C.M., a 50% chance of it docking, a random \ aggression level, a speed between 16 and 31, and a gentle clockwise roll \ \ We call this from within the main loop. \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.MTT4 \ \JSR DORND \ Set A and X to random numbers \ \LSR A \ Clear bit 7 of our random number in A and set the C \ \ flag to bit 0 of A, which is random \ \STA INWK+32 \ Store this in the ship's AI flag, so this ship does \ \ not have AI \ \STA INWK+29 \ Store A in the ship's roll counter, giving it a \ \ clockwise roll (as bit 7 is clear), and a 1 in 127 \ \ chance of it having no damping \ \ROL INWK+31 \ This instruction would appear to set bit 0 of the \ \ ship's missile count randomly (as the C flag was set), \ \ giving the ship either no missiles or one missile \ \ \ \ However, INWK+31 is overwritten in the call to the \ \ NWSHP routine below, where it is set to the number of \ \ missiles from the ship blueprint, and the value of the \ \ C flag is not used, so this instruction actually has \ \ no effect \ \ \ \ Interestingly, the original source code for the NWSPS \ \ routine also has an instruction that sets INWK+31 and \ \ which gets overwritten when it falls through into \ \ NWSHP, but in this case the instruction is commented \ \ out in the source. Perhaps the original version of \ \ NWSHP didn't set the missile count and instead relied \ \ on the calling code to set it, and when the authors \ \ changed it, they commented out the INWK+31 instruction \ \ in NWSPS and forgot about this one. Who knows? \ \AND #31 \ Set the ship speed to our random number, set to a \ORA #16 \ minimum of 16 and a maximum of 31 \STA INWK+27 \ \JSR DORND \ Set A and X to random numbers, plus the C flag \ \BMI nodo \ If A is negative (50% chance), jump to nodo to skip \ \ the following \ \ \ If we get here then we are going to spawn a ship that \ \ is minding its own business and trying to dock \ \LDA INWK+32 \ Set bits 6 and 7 of A, so the ship has AI (bit 7) and \ORA #%11000000 \ an aggression level of at least 32 out of 63 (this \STA INWK+32 \ makes the ship more likely to turn towards its target, \ \ which in this case is the space station, as we are \ \ about to set the ship flags so it is docking) \ \LDX #%00010000 \ Set bit 4 of the ship's NEWB flags, to indicate that \STX NEWB \ this ship is docking \ \.nodo \ \AND #2 \ This reduces A to a random value of either 0 or 2 \ \ADC #CYL \ Set A = A + C + #CYL \ \ \ \ where A is 0 or 2 and C is 0 or 1, so this gives us a \ \ ship type from the following: Cobra Mk III, Python, \ \ Boa or Anaconda \ \CMP #HER \ If A is now the ship type of a rock hermit, jump to \BEQ TT100 \ TT100 to skip the following instruction \ \JSR NWSHP \ Add a new ship of type A to the local bubble and fall \ \ through into the main game loop again \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Main game loop (Part 2 of 6) \ Type: Subroutine \ Category: Main loop \ Summary: Call the main flight loop, and potentially spawn a trader, an \ asteroid, or a cargo canister \ Deep dive: Program flow of the main game loop \ Ship data blocks \ Fixing ship positions \ \ ------------------------------------------------------------------------------ \ \ This section covers the following: \ \ * Call M% to do the main flight loop \ \ * Potentially spawn a trader, asteroid or cargo canister \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ TT100 The entry point for the start of the main game loop, \ which calls the main flight loop and the moves into the \ spawning routine \ \ me3 Used by me2 to jump back into the main game loop after \ printing an in-flight message \ \ ****************************************************************************** .TT100 \ --- Mod: Code added for speed control: --------------> JSR RestartSync \ Restart the sync counter \ --- End of added code -------------------------------> JSR M% \ Call M% to iterate through the main flight loop \ --- Mod: Code added for speed control: --------------> JSR WaitForSync \ Wait for the sync counter to count down \ --- End of added code -------------------------------> DEC DLY \ Decrement the delay counter in DLY, so any in-flight \ messages get removed once the counter reaches zero BEQ me2 \ If DLY is now 0, jump to me2 to remove any in-flight \ message from the space view, and once done, return to \ me3 below, skipping the following two instructions BPL me3 \ If DLY is positive, jump to me3 to skip the next \ instruction INC DLY \ If we get here, DLY is negative, so we have gone too \ and need to increment DLY back to 0 .me3 DEC player2DLY \ Decrement the delay counter in DLY, so any in-flight \ messages get removed once the counter reaches zero BEQ Player2me2 \ If DLY is now 0, jump to me2 to remove any in-flight \ message from the space view, and once done, return to \ me3 below, skipping the following two instructions BPL Player2me3 \ If DLY is positive, jump to me3 to skip the next \ instruction INC player2DLY \ If we get here, DLY is negative, so we have gone too \ and need to increment DLY back to 0 .Player2me3 DEC MCNT \ Decrement the main loop counter in MCNT \ Fall through into MLOOP \ --- Mod: Code removed for two-player Elite: ---------> \BEQ P%+5 \ If the counter has reached zero, which it will do \ \ every 256 main loops, skip the next JMP instruction \ \ (or to put it another way, if the counter hasn't \ \ reached zero, jump down to MLOOP, skipping all the \ \ following checks) \ \.ytq \ \JMP MLOOP \ Jump down to MLOOP to do some end-of-loop tidying and \ \ restart the main loop \ \ \ We only get here once every 256 iterations of the \ \ main loop. If we aren't in witchspace and don't \ \ already have 3 or more asteroids in our local bubble, \ \ then this section has a 13% chance of spawning \ \ something benign (the other 87% of the time we jump \ \ down to consider spawning cops, pirates and bounty \ \ hunters) \ \ \ \ If we are in that 13%, then 50% of the time this will \ \ be a trader, and the other 50% of the time it will \ \ either be an asteroid (98.5% chance) or, very rarely, \ \ a cargo canister (1.5% chance) \ \LDA MJ \ If we are in witchspace following a mis-jump, skip the \BNE ytq \ following by jumping down to MLOOP (via ytq above) \ \JSR DORND \ Set A and X to random numbers \ \CMP #35 \ If A >= 35 (87% chance), jump down to MTT1 to skip \BCS MTT1 \ the spawning of an asteroid or cargo canister and \ \ potentially spawn something else \ \LDA JUNK \ If we already have 3 or more bits of junk in the local \CMP #3 \ bubble, jump down to MTT1 to skip the following and \BCS MTT1 \ potentially spawn something else \ \JSR ZINF \ Call ZINF to reset the INWK ship workspace \ \LDA #38 \ Set z_hi = 38 (far away) \STA INWK+7 \ \JSR DORND \ Set A, X and C flag to random numbers \ \STA INWK \ Set x_lo = random \ \STX INWK+3 \ Set y_lo = random \ \ \ \ Note that because we use the value of X returned by \ \ DORND, and X contains the value of A returned by the \ \ previous call to DORND, this does not set the new ship \ \ to a totally random location \ \AND #%10000000 \ Set x_sign = bit 7 of x_lo \STA INWK+2 \ \TXA \ Set y_sign = bit 7 of y_lo \AND #%10000000 \STA INWK+5 \ \ROL INWK+1 \ Set bit 1 of x_hi to the C flag, which is random, so \ROL INWK+1 \ this randomly moves us off-centre by 512 (as if x_hi \ \ is %00000010, then (x_hi x_lo) is 512 + x_lo) \ \JSR DORND \ Set A, X and V flag to random numbers \ \BVS MTT4 \ If V flag is set (50% chance), jump up to MTT4 to \ \ spawn a trader \ \ORA #%01101111 \ Take the random number in A and set bits 0-3 and 5-6, \STA INWK+29 \ so the result has a 50% chance of being positive or \ \ negative, and a 50% chance of bits 0-6 being 127. \ \ Storing this number in the roll counter therefore \ \ gives our new ship a fast roll speed with a 50% \ \ chance of having no damping, plus a 50% chance of \ \ rolling clockwise or anti-clockwise \ \LDA SSPR \ If we are inside the space station safe zone, jump \BNE MTT1 \ down to MTT1 to skip the following and potentially \ \ spawn something else \ \TXA \ Set A to the random X we set above, which we haven't \BCS MTT2 \ used yet, and if the C flag is set (50% chance) jump \ \ down to MTT2 to skip the following \ \AND #31 \ Set the ship speed to our random number, set to a \ORA #16 \ minimum of 16 and a maximum of 31 \STA INWK+27 \ \BCC MTT3 \ Jump down to MTT3, skipping the following (this BCC \ \ is effectively a JMP as we know the C flag is clear, \ \ having passed through the BCS above) \ \.MTT2 \ \ORA #%01111111 \ Set bits 0-6 of A to 127, leaving bit 7 as random, so \STA INWK+30 \ storing this number in the pitch counter means we have \ \ full pitch with no damping, with a 50% chance of \ \ pitching up or down \ \.MTT3 \ \JSR DORND \ Set A and X to random numbers \ \CMP #252 \ If random A < 252 (98.8% of the time), jump to thongs \BCC thongs \ to skip the following \ \LDA #HER \ Set A to #HER so we spawn a rock hermit 1.2% of the \ \ time \ \STA INWK+32 \ Set byte #32 to %00001111 to give the rock hermit an \ \ E.C.M. \ \BNE whips \ Jump to whips (this BNE is effectively a JMP as A will \ \ never be zero) \ \.thongs \ \CMP #10 \ If random A >= 10 (96% of the time), set the C flag \ \AND #1 \ Reduce A to a random number that's 0 or 1 \ \ADC #OIL \ Set A = #OIL + A + C, so there's a 2% chance of us \ \ spawning a cargo canister (#OIL), a 50% chance of \ \ us spawning a boulder (#OIL + 1), and a 48% chance of \ \ us spawning an asteroid (#OIL + 2) \ \.whips \ \JSR NWSHP \ Add our new asteroid or canister to the universe \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Main game loop (Part 3 of 6) \ Type: Subroutine \ Category: Main loop \ Summary: Potentially spawn a cop, particularly if we've been bad \ Deep dive: Program flow of the main game loop \ Ship data blocks \ Fixing ship positions \ \ ------------------------------------------------------------------------------ \ \ This section covers the following: \ \ * Potentially spawn a cop (in a Viper), very rarely if we have been good, \ more often if have been naughty, and very often if we have been properly \ bad \ \ * Very rarely, consider spawning a Thargoid, or vanishingly rarely, a Cougar \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.MTT1 \ \LDA SSPR \ If we are outside the space station's safe zone, skip \BEQ P%+5 \ the following instruction \ \.MLOOPS \ \JMP MLOOP \ Jump to MLOOP to skip the following \ \JSR BAD \ Call BAD to work out how much illegal contraband we \ \ are carrying in our hold (A is up to 40 for a \ \ standard hold crammed with contraband, up to 70 for \ \ an extended cargo hold full of narcotics and slaves) \ \ASL A \ Double A to a maximum of 80 or 140 \ \LDX MANY+COPS \ If there are no cops in the local bubble, skip the \BEQ P%+5 \ next instruction \ \ORA FIST \ There are cops in the vicinity and we've got a hold \ \ full of jail time, so OR the value in A with FIST to \ \ get a new value that is at least as high as both \ \ values, to reflect the fact that they have almost \ \ certainly scanned our ship \ \STA T \ Store our badness level in T \ \JSR Ze \ Call Ze to initialise INWK to a fairly aggressive \ \ ship, and set A and X to random values \ \ \ \ Note that because Ze uses the value of X returned by \ \ DORND, and X contains the value of A returned by the \ \ previous call to DORND, this does not set the new ship \ \ to a totally random location \ \CMP #136 \ If the random number in A = 136 (0.4% chance), jump \BEQ fothg \ to fothg in part 4 to spawn either a Thargoid or, very \ \ rarely, a Cougar \ \CMP T \ If the random value in A >= our badness level, which \BCS P%+7 \ will be the case unless we have been really, really \ \ bad, then skip the following two instructions (so \ \ if we are really bad, there's a higher chance of \ \ spawning a cop, otherwise we got away with it, for \ \ now) \ \LDA #COPS \ Add a new police ship to the local bubble \JSR NWSHP \ \LDA MANY+COPS \ If we now have at least one cop in the local bubble, \BNE MLOOPS \ jump down to MLOOPS to stop spawning, otherwise fall \ \ through into the next part to look at spawning \ \ something else \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Main game loop (Part 4 of 6) \ Type: Subroutine \ Category: Main loop \ Summary: Potentially spawn a lone bounty hunter, a Thargoid, or up to four \ pirates \ Deep dive: Program flow of the main game loop \ Ship data blocks \ Fixing ship positions \ The elusive Cougar \ Aggression and hostility in ship tactics \ \ ------------------------------------------------------------------------------ \ \ This section covers the following: \ \ * Potentially spawn (35% chance) either a lone bounty hunter (a Cobra Mk \ III, Asp Mk II, Python or Fer-de-lance), a Thargoid, or a group of up to 4 \ pirates (a mix of Sidewinders, Mambas, Kraits, Adders, Geckos, Cobras Mk I \ and III, and Worms) \ \ * Also potentially spawn a Constrictor if this is the mission 1 endgame, or \ Thargoids if mission 2 is in progress \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \DEC EV \ Decrement EV, the extra vessels spawning delay, and if \BPL MLOOPS \ it is still positive, jump to MLOOPS to stop spawning, \ \ so we only do the following when the EV counter runs \ \ down \ \INC EV \ EV is negative, so bump it up again, setting it back \ \ to 0 \ \LDA TP \ Fetch bits 2 and 3 of TP, which contain the status of \AND #%00001100 \ mission 2 \ \CMP #%00001000 \ If bit 3 is set and bit 2 is clear, keep going to \BNE nopl \ spawn a Thargoid as we are transporting the plans in \ \ mission 2 and the Thargoids are trying to stop us, \ \ otherwise jump to nopl to skip spawning a Thargoid \ \JSR DORND \ Set A and X to random numbers \ \CMP #200 \ If the random number in A < 200 (78% chance), jump to \BCC nopl \ nopl to skip spawning a Thargoid \ \.fothg2 \ \JSR GTHG \ Call GTHG to spawn a Thargoid ship and a Thargon \ \ companion \ \.nopl \ \JSR DORND \ Set A and X to random numbers \ \LDY gov \ If the government of this system is 0 (anarchy), jump \BEQ LABEL_2 \ straight to LABEL_2 to start spawning pirates or a \ \ lone bounty hunter \ \CMP #90 \ If the random number in A >= 90 (65% chance), jump to \BCS MLOOPS \ MLOOPS to stop spawning (so there's a 35% chance of \ \ spawning pirates or a lone bounty hunter) \ \AND #7 \ Reduce the random number in A to the range 0-7, and \CMP gov \ if A is less than government of this system, jump \BCC MLOOPS \ to MLOOPS to stop spawning (so safer governments with \ \ larger gov numbers have a greater chance of jumping \ \ out, which is another way of saying that more \ \ dangerous systems spawn pirates and bounty hunters \ \ more often) \ \.LABEL_2 \ \ \ In the 6502 Second Processor version, the LABEL_2 \ \ label is actually ` (a backtick), but that doesn't \ \ compile in BeebAsm and it's pretty cryptic, so \ \ instead this version sticks with the label LABEL_2 \ \ from the cassette version \ \ \ Now to spawn a lone bounty hunter, a Thargoid or a \ \ group of pirates \ \JSR Ze \ Call Ze to initialise INWK to a fairly aggressive \ \ ship, and set A and X to random values \ \ \ \ Note that because Ze uses the value of X returned by \ \ DORND, and X contains the value of A returned by the \ \ previous call to DORND, this does not set the new ship \ \ to a totally random location \ \CMP #100 \ If the random number in A >= 100 (61% chance), jump \BCS mt1 \ to mt1 to spawn pirates, otherwise keep going to \ \ spawn a lone bounty hunter or a Thargoid \ \INC EV \ Increase the extra vessels spawning counter, to \ \ prevent the next attempt to spawn extra vessels \ \AND #3 \ Set A = random number in the range 0-3, which we \ \ will now use to determine the type of ship \ \ADC #CYL2 \ Add A to #CYL2 (we know the C flag is clear as we \ \ passed through the BCS above), so A is now one of the \ \ lone bounty hunter ships, i.e. Cobra Mk III (pirate), \ \ Asp Mk II, Python (pirate) or Fer-de-lance \ \ \ \ Interestingly, this logic means that the Moray, which \ \ is the ship after the Fer-de-lance in the XX21 table, \ \ never spawns, as the above logic chooses a blueprint \ \ number in the range CYL2 to CYL2+3 (i.e. 24 to 27), \ \ and the Moray is blueprint 28 \ \ \ \ No other code spawns the ship with blueprint 28, so \ \ this means the Moray is never seen in Elite \ \ \ \ This is presumably a bug, which could be very easily \ \ fixed by inserting one of the following instructions \ \ before the ADC #CYL2 instruction above: \ \ \ \ * SEC would change the range to 25 to 28, which \ \ would cover the Asp Mk II, Python (pirate), \ \ Fer-de-lance and Moray \ \ \ \ * LSR A would set the C flag to a random number to \ \ give a range of 24 to 28, which would cover the \ \ Cobra Mk III (pirate), Asp Mk II, Python (pirate), \ \ Fer-de-lance and Moray \ \ \ \ It's hard to know what the authors' original intent \ \ was, but the second approach makes the Moray and Cobra \ \ Mk III the rarest choices, with the Asp Mk II, Python \ \ and Fer-de-Lance being more likely, and as the Moray \ \ is described in the literature as a rare ship, and the \ \ Cobra can already be spawned as part of a group of \ \ pirates (see mt1 below), I tend to favour the LSR A \ \ solution over the SEC approach \ \TAY \ Copy the new ship type to Y \ \JSR THERE \ Call THERE to see if we are in the Constrictor's \ \ system in mission 1 \ \BCC NOCON \ If the C flag is clear then we are not in the \ \ Constrictor's system, so skip to NOCON \ \LDA #%11111001 \ Set the AI flag of this ship so that it has E.C.M., \STA INWK+32 \ has a very high aggression level of 60 out of 63, is \ \ hostile, and has AI enabled - nasty stuff! \ \LDA TP \ Fetch bits 0 and 1 of TP, which contain the status of \AND #%00000011 \ mission 1 \ \LSR A \ Shift bit 0 into the C flag \ \BCC NOCON \ If bit 0 is clear, skip to NOCON as mission 1 is not \ \ in progress \ \ORA MANY+CON \ Bit 0 of A now contains bit 1 of TP, so this will be \ \ set if we have already completed mission 1, so this OR \ \ will be non-zero if we have either completed mission \ \ 1, or there is already a Constrictor in our local \ \ bubble of universe (in which case MANY+CON will be \ \ non-zero) \ \BEQ YESCON \ If A = 0 then mission 1 is in progress, we haven't \ \ completed it yet, and there is no Constrictor in the \ \ vicinity, so jump to YESCON to spawn the Constrictor \ \.NOCON \ \LDA #%00000100 \ Set bit 2 of the NEWB flags and clear all other bits, \STA NEWB \ so the ship we are about to spawn is hostile \ \ \ We now build the AI flag for this ship in A \ \JSR DORND \ Set A and X to random numbers \ \CMP #200 \ First, set the C flag if X >= 200 (22% chance) \ \ROL A \ Set bit 0 of A to the C flag (i.e. there's a 22% \ \ chance of this ship having E.C.M.) \ \ORA #%11000000 \ Set bits 6 and 7 of A, so the ship has AI (bit 7) and \ \ an aggression level of at least 32 out of 63 \ \STA INWK+32 \ Store A in the AI flag of this ship \ \TYA \ Set A to the new ship type in Y \ \EQUB &2C \ Skip the next instruction by turning it into \ \ &2C &A9 &1F, or BIT &1FA9, which does nothing apart \ \ from affect the flags \ \.YESCON \ \LDA #CON \ If we jump straight here, we are in the mission 1 \ \ endgame and it's time to spawn the Constrictor, so \ \ set A to the Constrictor's type \ \.focoug \ \JSR NWSHP \ Spawn the new ship, whether it's a pirate, Thargoid, \ \ Cougar or Constrictor \ \.mj1 \ \JMP MLOOP \ Jump down to MLOOP, as we are done spawning ships \ \.fothg \ \LDA K%+6 \ Fetch the z_lo coordinate of the first ship in the K% \AND #%00111110 \ block (i.e. the planet) and extract bits 1-5 \ \BNE fothg2 \ If any of bits 1-5 are set (96.8% chance), jump up to \ \ fothg2 to spawn a Thargoid \ \ \ If we get here then we're going to spawn a Cougar, a \ \ very rare event indeed. How rare? Well, all the \ \ following have to happen in sequence: \ \ \ \ * Main loop iteration = 0 (1 in 256 iterations) \ \ * Skip asteroid spawning (87% chance) \ \ * Skip cop spawning (0.4% chance) \ \ * Skip Thargoid spawning (3.2% chance) \ \ \ \ so the chances of spawning a Cougar on any single main \ \ loop iteration are slim, to say the least \ \LDA #18 \ Give the ship we're about to spawn a speed of 27 \STA INWK+27 \ \LDA #%01111001 \ Give it an E.C.M. and an aggression level of 60 out of \STA INWK+32 \ 63, but don't enable its AI, so the ship will sit \ \ still in space unless it is hit, at which point it \ \ will defend itself vigorously \ \ \ \ This ensures the Cougar behaves like a ship with a \ \ cloaking device that hides it from the scanner, so it \ \ minds its own business until it's discovered \ \LDA #COU \ Set the ship type to a Cougar and jump up to focoug \BNE focoug \ to spawn it \ \.mt1 \ \AND #3 \ It's time to spawn a group of pirates, so set A to a \ \ random number in the range 0-3, which will be the \ \ loop counter for spawning pirates below (so we will \ \ spawn 1-4 pirates) \ \STA EV \ Delay further spawnings by this number \ \STA XX13 \ Store the number in XX13, the pirate counter \ \.mt3 \ \JSR DORND \ Set A and X to random numbers \ \STA T \ Set T to a random number \ \JSR DORND \ Set A and X to random numbers \ \AND T \ Set A to the AND of two random numbers, so each bit \ \ has 25% chance of being set which makes the chances \ \ of a smaller number higher \ \AND #7 \ Reduce A to a random number in the range 0-7, though \ \ with a bigger chance of a smaller number in this range \ \ADC #PACK \ #PACK is set to #SH3, the ship type for a Sidewinder, \ \ so this sets our new ship type to one of the pack \ \ hunters, namely a Sidewinder, Mamba, Krait, Adder, \ \ Gecko, Cobra Mk I, Worm or Cobra Mk III (pirate) \ \JSR NWSHP \ Try adding a new ship of type A to the local bubble \ \DEC XX13 \ Decrement the pirate counter \ \BPL mt3 \ If we need more pirates, loop back up to mt3, \ \ otherwise we are done spawning, so fall through into \ \ the end of the main loop at MLOOP \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Main game loop (Part 5 of 6) \ Type: Subroutine \ Category: Main loop \ Summary: Cool down lasers, make calls to update the dashboard \ Deep dive: Program flow of the main game loop \ The dashboard indicators \ \ ------------------------------------------------------------------------------ \ \ This is the first half of the minimal game loop, which we iterate when we are \ docked. This section covers the following: \ \ * Cool down lasers \ \ * Make calls to update the dashboard \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ MLOOP The entry point for the main game loop. This entry point \ comes after the call to the main flight loop and \ spawning routines, so it marks the start of the main \ game loop for when we are docked (as we don't need to \ call the main flight loop or spawning routines if we \ aren't in space) \ \ ****************************************************************************** .MLOOP LDX #&FF \ Set the stack pointer to &01FF, which is the standard TXS \ location for the 6502 stack, so this instruction \ effectively resets the stack LDX GNTMP \ If the laser temperature in GNTMP is non-zero, BEQ EE20 \ decrement it (i.e. cool it down a bit) DEC GNTMP .EE20 LDX LASCT \ Set X to the value of LASCT, the laser pulse count BEQ NOLASCT \ If X = 0 then jump to NOLASCT to skip reducing LASCT, \ as it can't be reduced any further DEX \ Decrement the value of LASCT in X BEQ P%+3 \ If X = 0, skip the next instruction DEX \ Decrement the value of LASCT in X again STX LASCT \ Store the decremented value of X in LASCT, so LASCT \ gets reduced by 2, but not into negative territory .NOLASCT \ --- Mod: Code added for two-player Elite: -----------> LDX player2GNTMP \ If the laser temperature in GNTMP is non-zero, BEQ game1 \ decrement it (i.e. cool it down a bit) DEC player2GNTMP .game1 LDX player2LASCT \ Set X to the value of LASCT, the laser pulse count BEQ game2 \ If X = 0 then jump to game2 to skip reducing LASCT, \ as it can't be reduced any further DEX \ Decrement the value of LASCT in X BEQ P%+3 \ If X = 0, skip the next instruction DEX \ Decrement the value of LASCT in X again STX player2LASCT \ Store the decremented value of X in LASCT, so LASCT \ gets reduced by 2, but not into negative territory .game2 \ --- End of added code -------------------------------> JSR DIALS \ Call DIALS to update the dashboard BIT printflag \ If bit 7 of printflag is clear (printer output is not \ enabled), jump to dontdolinefeedontheprinternow to \ skip the following (and en route, why not take a \ short moment to admire this, the longest label name in \ the original Elite source code - presumably they got \ longer when development moved to a 6502 second \ processor system, with all that extra memory...) BPL dontdolinefeedontheprinternow LDA #prilf \ Send a #prilf command to the I/O processor to print a JSR OSWRCH \ blank line on the printer JSR OSWRCH .dontdolinefeedontheprinternow STZ printflag \ Set the printflag to 0 to disable printing LDA QQ11 \ If this is a space view, skip the following five BEQ P%+13 \ instructions (i.e. jump to JSR TT17 below) AND PATG \ If PATG = &FF (author names are shown on start-up) LSR A \ and bit 0 of QQ11 is 1 (the current view is type 1), BCS P%+7 \ then skip the following two instructions LDY #2 \ Wait for 2/50 of a second (0.04 seconds), to slow the JSR DELAY \ main loop down a bit JSR TT17 \ Scan the keyboard for the cursor keys or joystick, \ returning the cursor's delta values in X and Y and \ the key pressed in A \ ****************************************************************************** \ \ Name: Main game loop (Part 6 of 6) \ Type: Subroutine \ Category: Main loop \ Summary: Process non-flight key presses (red function keys, docked keys) \ Deep dive: Program flow of the main game loop \ \ ------------------------------------------------------------------------------ \ \ This is the second half of the minimal game loop, which we iterate when we are \ docked. This section covers the following: \ \ * Process more key presses (red function keys, docked keys etc.) \ \ It also supports joining the main loop with a key already "pressed", so we can \ jump into the main game loop to perform a specific action. In practice, this \ is used when we enter the docking bay in BAY to display Status Mode (red key \ f8), and when we finish buying or selling cargo in BAY2 to jump to the \ Inventory (red key f9). \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ FRCE The entry point for the main game loop if we want to \ jump straight to a specific screen, by pretending to \ "press" a key, in which case A contains the internal key \ number of the key we want to "press" \ \ ****************************************************************************** .FRCE JSR TT102 \ Call TT102 to process the key pressed in A LDA QQ12 \ Fetch the docked flag from QQ12 into A BNE MLOOP \ If we are docked, loop back up to MLOOP just above \ to restart the main loop, but skipping all the flight \ and spawning code in the top part of the main loop JMP TT100 \ Otherwise jump to TT100 to restart the main loop from \ the start \ ****************************************************************************** \ \ Name: TT102 \ Type: Subroutine \ Category: Keyboard \ Summary: Process function key, save key, hyperspace and chart key presses \ and update the hyperspace counter \ \ ------------------------------------------------------------------------------ \ \ Process function key presses, plus "@" (save commander), "H" (hyperspace), \ "D" (show distance to system) and "O" (move chart cursor back to current \ system). We can also pass cursor position deltas in X and Y to indicate that \ the cursor keys or joystick have been used (i.e. the values that are returned \ by routine TT17). \ \ This routine also checks for the "F" key press (search for a system), which \ applies to enhanced versions only. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The internal key number of the key pressed (see page 142 \ of the "Advanced User Guide for the BBC Micro" by Bray, \ Dickens and Holmes for a list of internal key numbers) \ \ X The amount to move the crosshairs in the x-axis \ \ Y The amount to move the crosshairs in the y-axis \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ T95 Print the distance to the selected system \ \ ****************************************************************************** .TT102 \ --- Mod: Code removed for two-player Elite: ---------> \CMP #f8 \ If red key f8 was pressed, jump to STATUS to show the \BNE P%+5 \ Status Mode screen, returning from the subroutine \JMP STATUS \ using a tail call \ \CMP #f4 \ If red key f4 was pressed, jump to TT22 to show the \BNE P%+5 \ Long-range Chart, returning from the subroutine using \JMP TT22 \ a tail call \ \CMP #f5 \ If red key f5 was pressed, jump to TT23 to show the \BNE P%+5 \ Short-range Chart, returning from the subroutine using \JMP TT23 \ a tail call \ \CMP #f6 \ If red key f6 was pressed, call TT111 to select the \BNE TT92 \ system nearest to galactic coordinates (QQ9, QQ10) \JSR TT111 \ (the location of the chart crosshairs) and set ZZ to \JMP TT25 \ the system number, and then jump to TT25 to show the \ \ Data on System screen (along with an extended system \ \ description for the system in ZZ if we're docked), \ \ returning from the subroutine using a tail call \ \.TT92 \ \CMP #f9 \ If red key f9 was pressed, jump to TT213 to show the \BNE P%+5 \ Inventory screen, returning from the subroutine \JMP TT213 \ using a tail call \ \CMP #f7 \ If red key f7 was pressed, jump to TT167 to show the \BNE P%+5 \ Market Price screen, returning from the subroutine \JMP TT167 \ using a tail call \ --- End of removed code -----------------------------> CMP #f0 \ If red key f0 was pressed, jump to TT110 to launch our BNE fvw \ ship (if docked), returning from the subroutine using JMP TT110 \ a tail call .fvw BIT QQ12 \ If bit 7 of QQ12 is clear (i.e. we are not docked, but BPL INSP \ in space), jump to INSP to skip the following checks \ for f1-f3 and "@" (save commander file) key presses \ --- Mod: Code removed for two-player Elite: ---------> \CMP #f3 \ If red key f3 was pressed, jump to EQSHP to show the \BNE P%+5 \ Equip Ship screen, returning from the subroutine using \JMP EQSHP \ a tail call \ \CMP #f1 \ If red key f1 was pressed, jump to TT219 to show the \BNE P%+5 \ Buy Cargo screen, returning from the subroutine using \JMP TT219 \ a tail call \ \CMP #&47 \ If "@" was not pressed, skip to nosave \BNE nosave \ \JSR SVE \ "@" was pressed, so call SVE to show the disc access \ \ menu \ \BCC P%+5 \ If the C flag was set by SVE, then we loaded a new \JMP QU5 \ commander file, so jump to QU5 to restart the game \ \ with the newly loaded commander \ \JMP BAY \ Otherwise the C flag was clear, so jump to BAY to go \ \ to the docking bay (i.e. show the Status Mode screen) \ \.nosave \ \CMP #f2 \ If red key f2 was pressed, jump to TT208 to show the \BNE LABEL_3 \ Sell Cargo screen, returning from the subroutine using \JMP TT208 \ a tail call \ \RTS \ Return from the subroutine \ --- And replaced by: --------------------------------> CMP #f8 \ If red key f8 was pressed, jump to STATUS to show the BNE P%+5 \ Status Mode screen, returning from the subroutine JMP TITLE \ using a tail call \ --- End of replacement ------------------------------> .INSP CMP #f1 \ If the key pressed is < red key f1 or > red key f3, BCC LABEL_3 \ jump to LABEL_3 (so only do the following if the key CMP #f3+1 \ pressed is f1, f2 or f3) BCS LABEL_3 \ --- Mod: Code added for two-player Elite: -----------> STZ drawPlayerView \ Apply f0-f3 to player 1's view only \ --- End of added code -------------------------------> AND #3 \ If we get here then we are either in space, or we are TAX \ docked and none of f1-f3 were pressed, so we can now JMP LOOK1 \ process f1-f3 with their in-flight functions, i.e. \ switching space views \ \ A will contain &71, &72 or &73 (for f1, f2 or f3), so \ set X to the last digit (1, 2 or 3) and jump to LOOK1 \ to switch to view X (rear, left or right), returning \ from the subroutine using a tail call .LABEL_3 \ In the 6502 Second Processor version, the LABEL_3 \ label is actually `` (two backticks), but that doesn't \ compile in BeebAsm and it's pretty cryptic, so instead \ this version sticks with the label LABEL_3 from the \ cassette version \ --- Mod: Code added for two-player Elite: -----------> SEC \ Get ready for changing player 2's view ROR drawPlayerView CMP #&39 \ Up arrow = front BNE keys1 LDX #0 \ Switch to front view JMP LOOK1 .keys1 CMP #&29 \ Down arrow = rear BNE keys2 LDX #1 \ Switch to rear view JMP LOOK1 .keys2 CMP #&19 \ Left arrow = left BNE keys3 LDX #2 \ Switch to left view JMP LOOK1 .keys3 CMP #&79 \ Right arrow = right BNE keys4 LDX #3 \ Switch to right view JMP LOOK1 .keys4 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \CMP #&54 \ If "H" was pressed, jump to hyp to do a hyperspace \BNE P%+5 \ jump (if we are in space), returning from the \JMP hyp \ subroutine using a tail call \ \.NWDAV5 \ \CMP #&32 \ If "D" was pressed, jump to T95 to print the distance \BEQ T95 \ to a system (if we are in one of the chart screens) \ \CMP #&43 \ If "F" was not pressed, jump down to HME1, otherwise \BNE HME1 \ keep going to process searching for systems \ \LDA QQ12 \ If QQ12 = 0 (we are not docked), we can't search for \BEQ t95 \ systems, so return from the subroutine (as t95 \ \ contains an RTS) \ \LDA QQ11 \ If the current view is a chart (QQ11 = 64 or 128), \AND #%11000000 \ keep going, otherwise return from the subroutine (as \BEQ t95 \ t95 contains an RTS) \ \JMP HME2 \ Jump to HME2 to let us search for a system, returning \ \ from the subroutine using a tail call \ \.HME1 \ \STA T1 \ Store A (the key that's been pressed) in T1 \ \LDA QQ11 \ If the current view is a chart (QQ11 = 64 or 128), \AND #%11000000 \ keep going, otherwise jump down to TT107 to skip the \BEQ TT107 \ following \ \LDA QQ22+1 \ If the on-screen hyperspace counter is non-zero, \BNE TT107 \ then we are already counting down, so jump to TT107 \ \ to skip the following \ \LDA T1 \ Restore the original value of A (the key that's been \ \ pressed) from T1 \ \CMP #&36 \ If "O" was pressed, do the following three jumps, \BNE ee2 \ otherwise skip to ee2 to continue \ \JSR TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10), \ \ which will erase the crosshairs currently there \ \JSR ping \ Set the target system to the current system (which \ \ will move the location in (QQ9, QQ10) to the current \ \ home system \ \JMP TT103 \ Draw small crosshairs at coordinates (QQ9, QQ10), \ \ which will draw the crosshairs at our current home \ \ system, and return from the subroutine using a tail \ \ call \ \.ee2 \ \JSR TT16 \ Call TT16 to move the crosshairs by the amount in X \ \ and Y, which were passed to this subroutine as \ \ arguments \ \.TT107 \ \LDA QQ22+1 \ If the on-screen hyperspace counter is zero, return \BEQ t95 \ from the subroutine (as t95 contains an RTS), as we \ \ are not currently counting down to a hyperspace jump \ \DEC QQ22 \ Decrement the internal hyperspace counter \ \BNE t95 \ If the internal hyperspace counter is still non-zero, \ \ then we are still counting down, so return from the \ \ subroutine (as t95 contains an RTS) \ \ \ If we get here then the internal hyperspace counter \ \ has just reached zero and it wasn't zero before, so \ \ we need to reduce the on-screen counter and update \ \ the screen. We do this by first printing the next \ \ number in the countdown sequence, and then printing \ \ the old number, which will erase the old number \ \ and display the new one because printing uses EOR \ \ logic \ \LDX QQ22+1 \ Set X = the on-screen hyperspace counter - 1 \DEX \ (i.e. the next number in the sequence) \ \JSR ee3 \ Print the 8-bit number in X at text location (0, 1) \ \LDA #5 \ Reset the internal hyperspace counter to 5 \STA QQ22 \ \LDX QQ22+1 \ Set X = the on-screen hyperspace counter (i.e. the \ \ current number in the sequence, which is already \ \ shown on-screen) \ \JSR ee3 \ Print the 8-bit number in X at text location (0, 1), \ \ i.e. print the hyperspace countdown in the top-left \ \ corner \ \DEC QQ22+1 \ Decrement the on-screen hyperspace countdown \ \BNE t95 \ If the countdown is not yet at zero, return from the \ \ subroutine (as t95 contains an RTS) \ \JMP TT18 \ Otherwise the countdown has finished, so jump to TT18 \ \ to do a hyperspace jump, returning from the subroutine \ \ using a tail call \ \.t95 \ \RTS \ Return from the subroutine \ \.T95 \ \ \ If we get here, "D" was pressed, so we need to show \ \ the distance to the selected system (if we are in a \ \ chart view) \ \LDA QQ11 \ If the current view is a chart (QQ11 = 64 or 128), \AND #%11000000 \ keep going, otherwise return from the subroutine (as \BEQ t95 \ t95 contains an RTS) \ \LDA #CYAN \ Send a #SETCOL CYAN command to the I/O processor to \JSR DOCOL \ switch to colour 3, which is white in the chart view \ \JSR hm \ Call hm to move the crosshairs to the target system \ \ in (QQ9, QQ10), returning with A = 0 \ \STA QQ17 \ Set QQ17 = 0 to switch to ALL CAPS \ \JSR cpl \ Print control code 3 (the selected system name) \ \LDA #%10000000 \ Set bit 7 of QQ17 to switch to Sentence Case, with the \STA QQ17 \ next letter in capitals \ \LDA #10 \ Print a line feed to move the text cursor down a line \JSR TT26 \ \LDA #1 \ Move the text cursor to column 1 \JSR DOXC \ \JSR INCYC \ Move the text cursor down one line \ \JMP TT146 \ Print the distance to the selected system and return \ \ from the subroutine using a tail call \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: BAD \ Type: Subroutine \ Category: Status \ Summary: Calculate how bad we have been \ \ ------------------------------------------------------------------------------ \ \ Work out how bad we are from the amount of contraband in our hold. The \ formula is: \ \ (slaves + narcotics) * 2 + firearms \ \ so slaves and narcotics are twice as illegal as firearms. The value in FIST \ (our legal status) is set to at least this value whenever we launch from a \ space station, and a FIST of 50 or more gives us fugitive status, so leaving a \ station carrying 25 tonnes of slaves/narcotics, or 50 tonnes of firearms \ across multiple trips, is enough to make us a fugitive. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A A value that determines how bad we are from the amount \ of contraband in our hold \ \ ****************************************************************************** .BAD LDA QQ20+3 \ Set A to the number of tonnes of slaves in the hold CLC \ Clear the C flag so we can do addition without the \ C flag affecting the result ADC QQ20+6 \ Add the number of tonnes of narcotics in the hold ASL A \ Double the result and add the number of tonnes of ADC QQ20+10 \ firearms in the hold RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: FAROF \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Compare x_hi, y_hi and z_hi with 224 \ Deep dive: A sense of scale \ \ ------------------------------------------------------------------------------ \ \ Compare x_hi, y_hi and z_hi with 224, and set the C flag if all three <= 224, \ otherwise clear the C flag. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag Set if x_hi <= 224 and y_hi <= 224 and z_hi <= 224 \ \ Clear otherwise (i.e. if any one of them are bigger than \ 224) \ \ ****************************************************************************** .FAROF LDA #224 \ Set A = 224 and fall through into FAROF2 to do the \ comparison \ ****************************************************************************** \ \ Name: FAROF2 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Compare x_hi, y_hi and z_hi with A \ \ ------------------------------------------------------------------------------ \ \ Compare x_hi, y_hi and z_hi with A, and set the C flag if all three <= A, \ otherwise clear the C flag. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag Set if x_hi <= A and y_hi <= A and z_hi <= A \ \ Clear otherwise (i.e. if any one of them are bigger than \ A) \ \ ****************************************************************************** .FAROF2 CMP INWK+1 \ If A < x_hi, C will be clear so jump to FA1 to BCC FA1 \ return from the subroutine with C clear, otherwise \ C will be set so move on to the next one CMP INWK+4 \ If A < y_hi, C will be clear so jump to FA1 to BCC FA1 \ return from the subroutine with C clear, otherwise \ C will be set so move on to the next one CMP INWK+7 \ If A < z_hi, C will be clear, otherwise C will be set .FA1 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MAS4 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Calculate a cap on the maximum distance to a ship \ \ ------------------------------------------------------------------------------ \ \ Logical OR the value in A with the high bytes of the ship's position (x_hi, \ y_hi and z_hi). \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A A OR x_hi OR y_hi OR z_hi \ \ ****************************************************************************** .MAS4 ORA INWK+1 \ OR A with x_hi, y_hi and z_hi ORA INWK+4 ORA INWK+7 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: brkd \ Type: Variable \ Category: Utility routines \ Summary: A flag that indicates whether a system error has occurred \ \ ****************************************************************************** .brkd EQUB 0 \ A flag to record whether a system error has occurred, \ so we can print it out \ \ * 0 = no system error has occurred \ \ * &FF = a system error has occurred \ ****************************************************************************** \ \ Name: BRBR \ Type: Subroutine \ Category: Utility routines \ Summary: The standard BRKV handler for the game \ \ ------------------------------------------------------------------------------ \ \ This routine is used to display error messages, before restarting the game. \ When called, it makes a beep and prints the system error message in the block \ pointed to by (&FD &FE), which is where the MOS will put any system errors. It \ then waits for a key press and restarts the game. \ \ BRKV is set to this routine in the decryption routine at DEEOR just before the \ game is run for the first time, and at the end of the SVE routine after the \ disc access menu has been processed. In other words, this is the standard \ BRKV handler for the game, and it's swapped out to MEBRK for disc access \ operations only. \ \ When it is the BRKV handler, the routine can be triggered using a BRK \ instruction. The main differences between this routine and the MEBRK handler \ that is used during disc access operations are that this routine restarts the \ game rather than returning to the disc access menu. \ \ ****************************************************************************** .BRBR \ When we call this routine, we know that brkd will be \ zero, as it is initialised to zero and the only other \ place it gets changed is in the TITLE routine, where \ it also gets set to 0 DEC brkd \ Set brkd = &FF to indicate that there is a system \ error that needs to be printed out on the title screen \ by the TITLE routine LDX #&FF \ Set the stack pointer to &01FF, which is the standard TXS \ location for the 6502 stack, so this instruction \ effectively resets the stack JSR backtonormal \ Disable the keyboard and set the SVN flag to 0 TAY \ The call to backtonormal sets A to 0, so this sets Y \ to 0, which we use as a loop counter below LDA #7 \ Set A = 7 to generate a beep before we print the error \ message .BRBRLOOP JSR OSWRCH \ Print the character in A, which contains a line feed \ on the first loop iteration, and then any non-zero \ characters we fetch from the error message INY \ Increment the loop counter LDA (&FD),Y \ Fetch the Y-th byte of the block pointed to by \ (&FD &FE), so that's the Y-th character of the message \ pointed to by the error message pointer BNE BRBRLOOP \ If the fetched character is non-zero, loop back to the \ JSR OSWRCH above to print the it, and keep looping \ until we fetch a zero (which marks the end of the \ message) JMP BR1 \ Jump to BR1 to restart the game \ ****************************************************************************** \ \ Name: DEATH \ Type: Subroutine \ Category: Start and end \ Summary: Display the death screen \ \ ------------------------------------------------------------------------------ \ \ We have been killed, so display the chaos of our destruction above a "GAME \ OVER" sign, and clean up the mess ready for the next attempt. \ \ ****************************************************************************** .DEATH \ --- Mod: Code added for two-player Elite: -----------> PHA \ Store the winning player number on the stack LDA player2ShipType \ Remove the red laser line from player 2's ship, if LDX #2 \ present JSR RemoveLaserLineX LDA player1ShipType \ Remove the red laser line from player 1's ship, if LDX #12 \ present JSR RemoveLaserLineX JSR WipePlayer2Scanner \ Clear all player 2 (yellow) ships from the scanner, as \ we don't draw player 2's scanner ships once bit 7 of \ gameOver is set (as they tend to go a bit wrong on the \ game over screen) LDA #%10000000 \ Set bit 7 of gameOver to record that this is the game STA gameOver \ over screen \ --- End of added code -------------------------------> JSR EXNO3 \ Make the sound of us dying \ --- Mod: Code removed for two-player Elite: ---------> \JSR RES2 \ Reset a number of flight variables and workspaces \ \ASL DELTA \ Divide our speed in DELTA by 4 \ASL DELTA \ \LDX #24 \ Set the screen to only show 24 text rows, which hides \JSR DET1 \ the dashboard, setting A to 6 in the process \ \JSR TT66 \ Clear the top part of the screen, draw a border box, \ \ and set the current view type in QQ11 to 6 (death \ \ screen) \ \JSR BOX \ Call BOX to redraw the same border box (BOX is part \ \ of TT66), which removes the border as it is drawn \ \ using EOR logic \ \JSR nWq \ Create a cloud of stardust containing the correct \ \ number of dust particles (i.e. NOSTM of them) \ \LDA #12 \ Move the text cursor to column 12 on row 12 \JSR DOYC \JSR DOXC \ \LDA #YELLOW \ Send a #SETCOL YELLOW command to the I/O processor to \JSR DOCOL \ change the current colour to yellow \ \LDA #146 \ Print recursive token 146 ("{all caps}GAME OVER") \JSR ex \ \.D1 \ \JSR Ze \ Call Ze to initialise INWK to a fairly aggressive \ \ ship, and set A and X to random values \ \LSR A \ Set A = A / 4, so A is now between 0 and 63, and \LSR A \ store in byte #0 (x_lo) \STA INWK \ \LDY #0 \ Set the following to 0: the current view in QQ11 \STY QQ11 \ (space view), x_hi, y_hi, z_hi and the AI flag (no AI \STY INWK+1 \ or E.C.M. and zero aggression) \STY INWK+4 \STY INWK+7 \STY INWK+32 \ \DEY \ Set Y = 255 \ \STY MCNT \ Reset the main loop counter to 255, so all timer-based \ \ calls will be stopped \ \EOR #%00101010 \ Flip bits 1, 3 and 5 in A (x_lo) to get another number \STA INWK+3 \ between 48 and 63, and store in byte #3 (y_lo) \ \ORA #%01010000 \ Set bits 4 and 6 of A to bump it up to between 112 and \STA INWK+6 \ 127, and store in byte #6 (z_lo) \ \TXA \ Set A to the random number in X and keep bits 0-3 and \AND #%10001111 \ the sign in bit 7 to get a number between -15 and +15, \STA INWK+29 \ and store in byte #29 (roll counter) to give our ship \ \ a gentle roll with damping \ \STY LASCT \ Set the laser count to 127 to act as a counter in the \LSR LASCT \ D2 loop below, so this setting determines how long the \ \ death animation lasts (it's 127 iterations of the main \ \ flight loop) \ \ROR A \ The C flag is randomly set from the above call to Ze, \AND #%10000111 \ so this sets A to a number between -7 and +7, which \STA INWK+30 \ we store in byte #30 (the pitch counter) to give our \ \ ship a very gentle pitch with damping \ \LDX #OIL \ Set X to #OIL, the ship type for a cargo canister \ \LDA XX21-1+2*PLT \ Fetch the byte from location XX21 - 1 + 2 * PLT, which \ \ equates to XX21 + 7 (the high byte of the address of \ \ SHIP_PLATE), which seems a bit odd. It might make more \ \ sense to do LDA (XX21-2+2*PLT) as this would fetch the \ \ first byte of the alloy plate's blueprint (which \ \ determines what happens when alloys are destroyed), \ \ but there aren't any brackets, so instead this always \ \ returns &D0, which is never zero, so the following \ \ BEQ is never true. (If the brackets were there, then \ \ we could stop plates from spawning on death by setting \ \ byte #0 of the blueprint to 0... but then scooping \ \ plates wouldn't give us alloys, so who knows what this \ \ is all about?) \ \BEQ D3 \ If A = 0, jump to D3 to skip the following instruction \ \BCC D3 \ If the C flag is clear, which will be random following \ \ the above call to Ze, jump to D3 to skip the following \ \ instruction \ \DEX \ Decrement X, which sets it to #PLT, the ship type for \ \ an alloy plate \ \.D3 \ \JSR fq1 \ Call fq1 with X set to #OIL or #PLT, which adds a new \ \ cargo canister or alloy plate to our local bubble of \ \ universe and points it away from us with double DELTA \ \ speed (i.e. 6, as DELTA was set to 3 by the call to \ \ RES2 above). INF is set to point to the new arrival's \ \ ship data block in K% \ \JSR DORND \ Set A and X to random numbers and extract bit 7 from A \AND #%10000000 \ \LDY #31 \ Store this in byte #31 of the ship's data block, so it \STA (INF),Y \ has a 50% chance of marking our new arrival as being \ \ killed (so it will explode) \ \LDA FRIN+4 \ The call we made to RES2 before we entered the loop at \BEQ D1 \ D1 will have reset all the ship slots at FRIN, so this \ \ checks to see if the fifth slot is empty, and if it \ \ is we loop back to D1 to add another canister, until \ \ we have added five of them \ \JSR U% \ Clear the key logger, which also sets A = 0 \ \STA DELTA \ Set our speed in DELTA to 0, as we aren't going \ \ anywhere any more \ \.D2 \ \JSR M% \ Call the M% routine to do the main flight loop once, \ \ which will display our exploding canister scene and \ \ move everything about, as well as decrementing the \ \ value in LASCT \ \DEC LASCT \ Decrement the counter in LASCT, which we set above, \ \ so for each loop around D2, we decrement LASCT by 5 \ \ (the main loop decrements it by 4, and this one makes \ \ it 5) \ \BNE D2 \ Loop back to call the main flight loop again, until we \ \ have called it 127 times \ \LDX #31 \ Set the screen to show all 31 text rows, which shows \JSR DET1 \ the dashboard \ --- And replaced by: --------------------------------> LDA ECMA \ Fetch the E.C.M. status flag, and if E.C.M. is off, BEQ deaf1 \ skip the next instruction JSR ECMOF \ Turn off the E.C.M. sound .deaf1 LDA player2ECMA \ Fetch the E.C.M. status flag, and if E.C.M. is off, BEQ deaf2 \ skip the next instruction JSR Player2ECMOF \ Turn off the E.C.M. sound .deaf2 LDA LAS2 \ If player 1's main laser lines are not on-screen, jump BEQ deaf3 \ to deaf3 to skip the following JSR LASLI2 \ Redraw the existing laser lines, which has the effect \ of removing them from the screen .deaf3 LDA player2LAS2 \ If player 2's main laser lines are not on-screen, jump BEQ deaf4 \ to deaf4 to skip the following JSR Player2LASLI2 \ Redraw the existing laser lines, which has the effect \ of removing them from the screen .deaf4 STZ LAS2 \ Zero LAS2 for both players so the main laser lines STZ player2LAS2 \ don't get redrawn in the main loop PLA \ Set A to the winning player CMP #2 \ If A = 2 then player 2 has won, so jump to deaf5 to BEQ deaf5 \ process this \ Player 1 wins LDA #160+69 \ Print recursive token 69 ("WINNER!") as a player 1 JSR MESS \ in-flight message LDA #160+68 \ Print recursive token 68 ("GAME OVER") as a player 2 JSR Player2MESS \ in-flight message LDA K%+NI%*2+31 \ Set bit 7 of player 2's INWK+31 byte so it explodes ORA #%10000000 STA K%+NI%*2+31 LDA #%10000000 \ Set bit 7 of player2Exploding to record the start of STA player2Exploding \ the explosion JMP deaf6 \ Jump to deaf6 to wait for a key press .deaf5 \ Player 2 wins LDA #160+69 \ Print recursive token 69 ("WINNER!") as a player 2 JSR Player2MESS \ in-flight message LDA #160+68 \ Print recursive token 68 ("GAME OVER") as a player 1 JSR MESS \ in-flight message ASL player1INWK31 \ Set bit 7 of player 1's INWK+31 byte so it explodes SEC ROR player1INWK31 LDA #%10000000 \ Set bit 7 of player1Exploding to record the start of STA player1Exploding \ the explosion .deaf6 LDA #64 \ Set gameOverCounter to set the length of the game over STA gameOverCounter \ sequence .deaf7 \ We now continue to run the game for a short period, \ but with controls disabled JSR U% \ Clear the key logger JSR RestartSync \ Restart the sync counter JSR M% \ Call M% to iterate through the main flight loop JSR WaitForSync \ Wait for the sync counter to count down JSR DIALS \ Call DIALS to update the dashboard DEC gameOverCounter \ Decrement the loop counter BNE deaf7 \ Loop back until gameOverCounter runs down, at which \ point we stop looping and return to the main menu \ --- End of replacement ------------------------------> JMP DEATH2 \ Jump to DEATH2 to reset and restart the game \ ****************************************************************************** \ \ Name: WipePlayer2Scanner \ Type: Subroutine \ Category: Two-player Elite \ Summary: Clear all player 2 (yellow) ships from the scanner \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .WipePlayer2Scanner LDA #%00010000 \ If bit #4 of INWK+31 is clear for player 1's ship in BIT player1INWK31 \ player 2's view then the ship is not on the scanner, BEQ wscn1 \ so skip the following LDA #%00100000 \ If bit #5 of INWK+31 is set for player 1's ship in BIT player1INWK31 \ player 2's view then the ship is exploding, BNE wscn1 \ so skip the following LDX #12 \ Remove the ship in slot #12 from the scanner, drawing LDA player1ShipType \ it in yellow LDY #YELLOW2 JSR WipeShip .wscn1 LDA FRIN+3 \ If slot #3 is empty, skip the following BEQ wscn2 LDA #%00010000 \ If bit #4 of INWK+31 is clear for ship slot #3 in BIT player1INWK31+1 \ player 2's view then the ship is not on the scanner, BEQ wscn2 \ so skip the following LDA #%00100000 \ If bit #5 of INWK+31 is set for ship slot #3 in BIT player1INWK31+1 \ player 2's view then the ship is exploding, BNE wscn2 \ so skip the following LDX #13 \ Remove the ship in slot #13 from the scanner, drawing LDA FRIN+3 \ it in yellow LDY #YELLOW2 JSR WipeShip .wscn2 LDA FRIN+4 \ If slot #4 is empty, skip the following BEQ wscn3 LDA #%00010000 \ If bit #4 of INWK+31 is clear for ship slot #4 in BIT player1INWK31+2 \ player 2's view then the ship is not on the scanner, BEQ wscn3 \ so skip the following LDA #%00100000 \ If bit #5 of INWK+31 is set for ship slot #4 in BIT player1INWK31+2 \ player 2's view then the ship is exploding, BNE wscn3 \ so skip the following LDX #14 \ Remove the ship in slot #14 from the scanner, drawing LDA FRIN+4 \ it in yellow LDY #YELLOW2 JSR WipeShip .wscn3 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: spasto \ Type: Variable \ Category: Universe \ Summary: Contains the address of the Coriolis space station's ship \ blueprint \ \ ****************************************************************************** .spasto EQUW &8888 \ This variable is set by routine BEGIN to the address \ of the Coriolis space station's ship blueprint \ ****************************************************************************** \ \ Name: BEGIN \ Type: Subroutine \ Category: Loader \ Summary: Initialise the configuration variables and start the game \ \ ****************************************************************************** .BEGIN \JSR BRKBK \ This instruction is commented out in the original \ source LDX #(CATF-COMC) \ We start by zeroing all the configuration variables \ between COMC and CATF, to set them to their default \ values, so set a counter in X for CATF - COMC bytes LDA #0 \ Set A = 0 so we can zero the variables .BEL1 STA COMC,X \ Zero the X-th configuration variable DEX \ Decrement the loop counter BPL BEL1 \ Loop back to BEL1 to zero the next byte, until we have \ zeroed them all LDA XX21+SST*2-2 \ Set spasto(1 0) to the Coriolis space station entry STA spasto \ from the ship blueprint lookup table at XX21 (so LDA XX21+SST*2-1 \ spasto(1 0) points to the Coriolis blueprint) STA spasto+1 \ Fall through into TT170 to start the game \ ****************************************************************************** \ \ Name: TT170 \ Type: Subroutine \ Category: Start and end \ Summary: Main entry point for the Elite game code \ Deep dive: Program flow of the main game loop \ \ ------------------------------------------------------------------------------ \ \ This is the main entry point for the main game code. It is called after the \ various setup, decryption and checksum routines in S%, G% and BEGIN have \ successfully completed. \ \ ****************************************************************************** .TT170 LDX #&FF \ Set the stack pointer to &01FF, which is the standard TXS \ location for the 6502 stack, so this instruction \ effectively resets the stack JSR RESET \ Call RESET to initialise most of the game variables \ --- Mod: Code added for two-player Elite: -----------> LDA #0 \ Set A = 0 to use as a zero reset LDX #(endWP-startWP) \ We're going to zero the player workspace variables \ from startWP to endWP (not including endWP), so set a \ counter in X for the correct number of bytes .zero1 STA startWP-1,X \ Zero the X-1-th byte of startWP DEX \ Decrement the loop counter BNE zero1 \ Loop back to zero the next variable until we have done \ them all LDX #POW \ Give player 2 front and rear pulse lasers by default STX player2LASER \ (player 1 gets them via NA%) STX player2LASER+1 LDX #CYL \ Set player ships to Cobras by default STX player1ShipType STX player2ShipType LDX #3 \ Set player 2's missiles to 3 by default STX player2NOMSL STX player1Missiles \ Set the number of missiles on the configuration screen STX player2Missiles \ to 3 by default LDX #&FF \ Give player 2 an E.C.M. STX player2ECM LDA #%11111110 \ Set player 2 to AI Pilot by default STA player2INWK32 JSR DFAULT \ Call DFAULT to reset the current commander data block \ to the last saved commander \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \IF _EXECUTIVE \ \JSR DEMON \ Call DEMON to show the demo \ \LDX #&FF \ Set the stack pointer to &01FF, which is the standard \TXS \ location for the 6502 stack, so this instruction \ \ effectively resets the stack \ \JSR RESET \ Call RESET to initialise most of the game variables \ \ENDIF \ --- End of removed code -----------------------------> \ Fall through into DEATH2 to start the game \ ****************************************************************************** \ \ Name: DEATH2 \ Type: Subroutine \ Category: Start and end \ Summary: Reset most of the game and restart from the title screen \ \ ------------------------------------------------------------------------------ \ \ This routine is called following death, and when the game is quit by pressing \ ESCAPE when paused. \ \ ****************************************************************************** .DEATH2 LDX #&FF \ Set the stack pointer to &01FF, which is the standard TXS \ location for the 6502 stack, so this instruction \ effectively resets the stack JSR RES2 \ Reset a number of flight variables and workspaces \ and fall through into the entry code for the game \ to restart from the title screen \ ****************************************************************************** \ \ Name: BR1 (Part 1 of 2) \ Type: Subroutine \ Category: Start and end \ Summary: Show the "Load New Commander (Y/N)?" screen and start the game \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ QU5 Restart the game using the last saved commander without \ asking whether to load a new commander file \ \ ****************************************************************************** .BR1 JSR ZEKTRAN \ Reset the key logger buffer that gets returned from \ the I/O processor LDA #3 \ Move the text cursor to column 3 JSR DOXC LDX #3 \ Set X = 3 for the call to FX200 JSR FX200 \ Disable the ESCAPE key and clear memory if the BREAK \ key is pressed (*FX 200,3) \ --- Mod: Code removed for two-player Elite: ---------> \IF _EXECUTIVE \ \LDX #3 \ Call TALK with X = 3 to say "Elite" using the Watford \JSR TALK \ Electronics Beeb Speech Synthesiser (if one is fitted \ \ and speech has been enabled) \ \ENDIF \ \LDX #CYL \ Call TITLE to show a rotating Cobra Mk III (#CYL) and \LDA #6 \ token 6 ("LOAD NEW {single cap}COMMANDER {all caps} \JSR TITLE \ (Y/N)?{sentence case}{cr}{cr}"), returning with the \ \ internal number of the key pressed in A \ \CMP #&60 \ Did we press TAB? If not, skip the following \BNE P%+5 \ instruction \ \.BRGO \ \JMP DEMON \ We pressed TAB, so jump to DEMON to show the demo \ \CMP #&44 \ Did we press "Y"? If not, jump to QU5, otherwise \BNE QU5 \ continue on to load a new commander \ \JSR DFAULT \ Call DFAULT to reset the current commander data block \ \ to the last saved commander \ \JSR SVE \ Call SVE to load a new commander into the last saved \ \ commander data block \ \.QU5 \ \JSR DFAULT \ Call DFAULT to reset the current commander data block \ \ to the last saved commander \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: BR1 (Part 2 of 2) \ Type: Subroutine \ Category: Start and end \ Summary: Show the "Press Fire or Space, Commander" screen and start the \ game \ \ ------------------------------------------------------------------------------ \ \ BRKV is set to point to BR1 by the loading process. \ \ ****************************************************************************** JSR msblob \ Reset the dashboard's missile indicators so none of \ them are targeted \ --- Mod: Code removed for two-player Elite: ---------> \LDA #7 \ Call TITLE to show a rotating Asp Mk II (#ASP) and \LDX #ASP \ token 7 ("PRESS SPACE OR FIRE,{single cap}COMMANDER. \JSR TITLE \ {cr}{cr}"), returning with the internal number of the \ \ key pressed in A \ --- End of removed code -----------------------------> JSR ping \ Set the target system coordinates (QQ9, QQ10) to the \ current system coordinates (QQ0, QQ1) we just loaded JSR TT111 \ Select the system closest to galactic coordinates \ (QQ9, QQ10) JSR jmp \ Set the current system to the selected system LDX #5 \ We now want to copy the seeds for the selected system \ in QQ15 into QQ2, where we store the seeds for the \ current system, so set up a counter in X for copying \ 6 bytes (for three 16-bit seeds) \ The label below is called likeTT112 because this code \ is almost identical to the TT112 loop in the hyp1 \ routine .likeTT112 LDA QQ15,X \ Copy the X-th byte in QQ15 to the X-th byte in QQ2 STA QQ2,X DEX \ Decrement the counter BPL likeTT112 \ Loop back to likeTT112 if we still have more bytes to \ copy INX \ Set X = 0 (as we ended the above loop with X = &FF) STX EV \ Set EV, the extra vessels spawning counter, to 0, as \ we are entering a new system with no extra vessels \ spawned LDA QQ3 \ Set the current system's economy in QQ28 to the STA QQ28 \ selected system's economy from QQ3 LDA QQ5 \ Set the current system's tech level in tek to the STA tek \ selected system's economy from QQ5 LDA QQ4 \ Set the current system's government in gov to the STA gov \ selected system's government from QQ4 \ Fall through into the docking bay routine below \ ****************************************************************************** \ \ Name: BAY \ Type: Subroutine \ Category: Status \ Summary: Go to the docking bay (i.e. show the Status Mode screen) \ \ ------------------------------------------------------------------------------ \ \ We end up here after the start-up process (load commander etc.), as well as \ after a successful save, an escape pod launch, a successful docking, the end \ of a cargo sell, and various errors (such as not having enough cash, entering \ too many items when buying, trying to fit an item to your ship when you \ already have it, running out of cargo space, and so on). \ \ ****************************************************************************** .BAY LDA #&FF \ Set QQ12 = &FF (the docked flag) to indicate that we STA QQ12 \ are docked LDA #f8 \ Jump into the main loop at FRCE, setting the key JMP FRCE \ that's "pressed" to red key f8 (so we show the Status \ Mode screen) \ ****************************************************************************** \ \ Name: DFAULT \ Type: Subroutine \ Category: Start and end \ Summary: Reset the current commander data block to the last saved commander \ \ ****************************************************************************** .DFAULT LDX #NT%+8 \ The size of the last saved commander data block is NT% \ bytes, and it is preceded by the 8 bytes of the \ commander name (seven characters plus a carriage \ return). The commander data block at NAME is followed \ by the commander data block, so we need to copy the \ name and data from the "last saved" buffer at NA% to \ the current commander workspace at NAME. So we set up \ a counter in X for the NT% + 8 bytes that we want to \ copy .QUL1 LDA NA%-1,X \ Copy the X-th byte of NA%-1 to the X-th byte of STA NAME-1,X \ NAME-1 (the -1 is because X is counting down from \ NT% + 8 to 1) DEX \ Decrement the loop counter BNE QUL1 \ Loop back for the next byte of the commander data \ block STX QQ11 \ X is 0 by the end of the above loop, so this sets QQ11 \ to 0, which means we will be showing a view without a \ boxed title at the top (i.e. we're going to use the \ screen layout of a space view in the following) \ If the commander check below fails, we keep jumping \ back to here to crash the game with an infinite loop JSR CHECK \ Call the CHECK subroutine to calculate the checksum \ for the current commander block at NA%+8 and put it \ in A CMP CHK \ Test the calculated checksum against CHK IF _REMOVE_CHECKSUMS NOP \ If we have disabled checksums, then ignore the result NOP \ of the comparison and fall through into the next part ELSE BNE P%-6 \ If the calculated checksum does not match CHK, then \ loop back to repeat the check - in other words, we \ enter an infinite loop here, as the checksum routine \ will keep returning the same incorrect value ENDIF \JSR BELL \ This instruction is commented out in the original \ source; it would make a standard system beep \ The checksum CHK is correct, so now we check whether \ CHK2 = CHK EOR A9, and if this check fails, bit 7 of \ the competition flags at COK gets set, to indicate \ to Acornsoft via the competition code that there has \ been some hacking going on with this competition entry EOR #&A9 \ X = checksum EOR &A9 TAX LDA COK \ Set A to the competition flags in COK CPX CHK2 \ If X = CHK2, then skip the next instruction BEQ tZ ORA #%10000000 \ Set bit 7 of A to indicate this commander file has \ been tampered with .tZ ORA #%00000100 \ Set bit 2 of A to denote that this is the 6502 second \ processor version (which is the same bit as for the \ original disc version, before the refund bug was \ fixed) STA COK \ Store the updated competition flags in COK RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TITLE \ Type: Subroutine \ Category: Start and end \ Summary: Display a title screen with a rotating ship and prompt \ \ ------------------------------------------------------------------------------ \ \ Display the title screen, with a rotating ship and a text token at the bottom \ of the screen. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The number of the extended token to show below the \ rotating ship (see variable TKN1 for details of \ recursive tokens) \ \ X The type of the ship to show (see variable XX21 for a \ list of ship types) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X If a key is being pressed, X contains the internal key \ number, otherwise it contains 0 \ \ ****************************************************************************** .TITLE \ --- Mod: Code removed for two-player Elite: ---------> \PHA \ Store the token number on the stack for later \ \STX TYPE \ Store the ship type in location TYPE \ --- End of removed code -----------------------------> JSR RESET \ Reset our ship so we can use it for the rotating \ title ship JSR ZEKTRAN \ Reset the key logger buffer that gets returned from \ the I/O processor \ --- Mod: Code removed for two-player Elite: ---------> \LDA #32 \ Send a #SETVDU19 32 command to the I/O processor to \JSR DOVDU19 \ set the mode 1 palette to yellow (colour 1), white \ \ (colour 2) and cyan (colour 3) \ --- And replaced by: --------------------------------> LDA #0 \ Send a #SETVDU19 0 command to the I/O processor to JSR DOVDU19 \ switch to the mode 1 palette for the space view, \ which is yellow (colour 1), red (colour 2) and cyan \ (colour 3) \ --- End of replacement ------------------------------> LDA #1 \ Clear the top part of the screen, draw a border box, JSR TT66 \ and set the current view type in QQ11 to 1 \ --- Mod: Code removed for two-player Elite: ---------> \LDA #RED \ Send a #SETCOL RED command to the I/O processor to \JSR DOCOL \ switch to colour 2, which is white in the title screen \ \STZ QQ11 \ Set QQ11 to 0, so from here on we are using a space \ view \ --- And replaced by: --------------------------------> LDA player1Missiles \ Reset the number of missiles for each player to the STA NOMSL \ configured amount (so this restores any missiles fired LDA player2Missiles \ in the last game) STA player2NOMSL JSR msblob \ Update the missiles in the dashboard LDA #CYAN \ Send a #SETCOL CYAN command to the I/O processor to JSR DOCOL \ switch to colour 3, which is cyan \ --- End of replacement ------------------------------> LDA #96 \ Set nosev_z hi = 96 (96 is the value of unity in the STA INWK+14 \ rotation vector) STA INWK+7 \ Set z_hi, the high byte of the ship's z-coordinate, \ to 96, which is the distance at which the rotating \ ship starts out before coming towards us LDX #127 \ Set roll counter = 127, so don't dampen the roll and STX INWK+29 \ make the roll direction clockwise STX INWK+30 \ Set pitch counter = 127, so don't dampen the pitch and \ set the pitch direction to dive INX \ Set QQ17 to 128 (so bit 7 is set) to switch to STX QQ17 \ Sentence Case, with the next letter printing in upper \ case \ --- Mod: Code removed for two-player Elite: ---------> \LDA TYPE \ Set up a new ship, using the ship type in TYPE \JSR NWSHP \ --- And replaced by: --------------------------------> STX titleScreen \ Set bit 7 of titleScreen to denote we are drawing the \ title screen, so ships are drawn in cyan LDX player1ShipType \ Set x = -xShipOffset, so ship starts on left LDA xShipOffset,X STA INWK STZ INWK+1 LDX #%10000000 STX INWK+2 LDA yShipOffset,X \ Set y = -yShipOffset, to move ship down a little STA INWK+3 STZ INWK+4 LDA #%10000000 STA INWK+5 LDA #CYL \ Set up a new ship block for player 1 in slot #0, using JSR NWSHP \ the Cobra Mk III blueprint as it has the largest ship \ line heap (so all the other ships will fit) LDA player1ShipType \ Set the ship to the configured ship for player 1 STA FRIN LDX #0 \ Save player 1's ship data JSR SaveShipDataInSlot LDX player2ShipType \ Set x = +xShipOffset, so ship starts on right LDA xShipOffset,X STA INWK STZ INWK+1 STZ INWK+2 LDA #CYL \ Set up a new ship block for player 2 in slot #1, using JSR NWSHP \ the Cobra Mk III blueprint as it has the largest ship \ line heap (so all the other ships will fit) LDA player2ShipType \ Set the ship to the configured ship for player 2 STA FRIN+1 LDX #1 \ Save player 2's ship data JSR SaveShipDataInSlot \ --- End of replacement ------------------------------> LDA #6 \ Move the text cursor to column 6 JSR DOXC LDA #30 \ Print recursive token 144 ("---- E L I T E ----") JSR plf \ followed by a newline \ --- Mod: Code removed for two-player Elite: ---------> \LDA #10 \ Print a line feed to move the text cursor down a line \JSR TT26 \ \LDA #6 \ Move the text cursor to column 6 again \JSR DOXC \ \LDA PATG \ If PATG = 0, skip the following two lines, which \BEQ awe \ print the author credits (PATG can be toggled by \ \ pausing the game and pressing "X") \ \LDA #13 \ Print extended token 13 ("BY D.BRABEN & I.BELL") \JSR DETOK \ \.awe \ --- End of removed code -----------------------------> LDA brkd \ If brkd = 0, jump to BRBR2 to skip the following, as BEQ BRBR2 \ we do not have a system error message to display \ If we get here then brkd = &FF, which indicates that \ we have a system error we need to display INC brkd \ Set brkd = 0 to clear the error flag and indicate that \ the error has been processed LDA #7 \ Move the text cursor to column 7 JSR DOXC LDA #10 \ Move the text cursor to row 10 JSR DOYC \ The following loop prints out the null-terminated \ message pointed to by (&FD &FE), which is the MOS \ error message pointer - so this prints the error \ message on the next line LDY #0 \ Set Y = 0 to act as a character counter JSR OSWRCH \ Print the character in A (which contains a line feed \ on the first loop iteration), and then any non-zero \ characters we fetch from the error message INY \ Increment the loop counter LDA (&FD),Y \ Fetch the Y-th byte of the block pointed to by \ (&FD &FE), so that's the Y-th character of the message \ pointed to by the MOS error message pointer BNE P%-6 \ If the fetched character is non-zero, loop back to the \ JSR OSWRCH above to print it, and keep looping until \ we fetch a zero (which marks the end of the message) .BRBR2 \ --- Mod: Code removed for two-player Elite: ---------> \JSR CLYNS \ Clear the bottom three text rows of the upper screen, \ \ and move the text cursor to the first cleared row. \ \ It also returns with Y = 0 \ \STY DELTA \ Set DELTA = 0 (i.e. ship speed = 0) \ \STY JSTK \ Set JSTK = 0 (i.e. keyboard, not joystick) \ \PLA \ Restore the recursive token number we stored on the \ \ stack at the start of this subroutine \ \\JSR ex \ This instruction is commented out in the original \ \ source (it would print the recursive token in A) \ \JSR DETOK \ Print the extended token in A \ \LDA #7 \ Move the text cursor to column 7 \JSR DOXC \ \LDA #12 \ Print extended token 12 ("({single cap}C) ACORNSOFT \JSR DETOK \ 1984") \ \LDA #12 \ Set CNT2 = 12 as the outer loop counter for the loop \STA CNT2 \ starting at TLL2 \ --- And replaced by: --------------------------------> LDA #1 \ Print token 56 ("PLAYER 1") at (1, 12) JSR DOXC LDA #12 JSR DOYC LDA #160+56 JSR TT27 LDA #23 \ Print token 57 ("PLAYER 2") at (23, 12) JSR DOXC LDA #160+57 JSR TT27 LDA #1 \ Print token 61 ("--------") at (1, 13) JSR DOXC JSR INCYC LDA #160+61 JSR TT27 LDA #23 \ Print token 61 ("--------") at (23, 13) JSR DOXC LDA #160+61 JSR TT27 LDA #14 \ Print token 25 ("SHIP") at (14, 14) JSR DOXC LDA #14 JSR DOYC LDA #160+25 JSR TT27 LDA #13 \ Print token 42 ("LASERS") at (13, 15) JSR DOXC JSR INCYC LDA #160+42 JSR TT27 LDA #%10000000 \ Set bit 7 of QQ17 to switch standard tokens to STA QQ17 \ Sentence Case LDA #13 \ Print token 121 ("ENERGY") at (13, 16) JSR DOXC JSR INCYC LDA #121 JSR TT27 LDA #12 \ Print token 58 ("MISSILES") at (12, 17) JSR DOXC JSR INCYC LDA #160+58 JSR TT27 LDA #13 \ Print token 108 ("E.C.M.") at (13, 18) JSR DOXC JSR INCYC LDA #108 JSR TT27 LDA #12 \ Print token 43 ("CONTROLS") at (12, 19) JSR DOXC JSR INCYC LDA #160+43 JSR TT27 LDA #13 \ Print token 51 ("WINNER") at (13, 20) JSR DOXC JSR INCYC LDA #160+51 JSR TT27 LDA #8 \ Print token 59 ("PRESS") at (8, 22) JSR DOXC LDA #22 JSR DOYC LDA #160+59 JSR TT27 LDA #1 \ Set QQ17 = 1 to switch standard tokens to lower STA QQ17 \ case LDA #160+60 \ Print token 60 (" F0 TO PLAY") JSR TT27 LDA #0 \ Set YSAV to 0 to use as a loop counter in the STA YSAV \ following loop to print all options .titl1 JSR PrintOptionA \ Print option A INC YSAV \ Move on to the next option LDA YSAV \ Loop back until we have printed all options CMP #14 BNE titl1 STZ DELTA \ Set DELTA = 0 (i.e. ship speed = 0) LDA #%10000000 \ Move the ships into the top half of the screen STA splitScreen STZ drawPlayerView \ --- End of replacement ------------------------------> LDA #5 \ Set the main loop counter in MCNT to 5, to act as the STA MCNT \ inner loop counter for the loop starting at TLL2 .TLL2 \ --- Mod: Code added for two-player Elite: -----------> LDA player1ShipType \ Switch to player 1's ship STA TYPE ASL A \ Set Y = ship type * 2 TAY LDA XX21-2,Y \ The ship blueprints at XX21 start with a lookup STA XX0 \ table that points to the individual ship blueprints, \ so this fetches the low byte of this particular ship \ type's blueprint and stores it in XX0 LDA XX21-1,Y \ Fetch the high byte of this particular ship type's STA XX0+1 \ blueprint and store it in XX0+1 LDX #0 \ Fetch player 1's ship data from slot #0 STX XSAV JSR GetShipDataToINWK \ --- End of added code -------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \LDA INWK+7 \ If z_hi (the ship's distance) is 1, jump to TL1 to \CMP #1 \ skip the following decrement \BEQ TL1 \ --- And replaced by: --------------------------------> LDX player1ShipType \ If z_hi matches this ship type's entry in shipDistance LDA shipDistance,X \ then skip the following decrement CMP INWK+7 BEQ TL1 \ --- End of replacement ------------------------------> DEC INWK+7 \ Decrement the ship's distance, to bring the ship \ a bit closer to us .TL1 JSR MVEIT \ Move the ship in space according to the orientation \ vectors and the new value in z_hi LDX #128 \ Set z_lo = 128, so the closest the ship gets to us is STX INWK+6 \ z_hi = 1, z_lo = 128, or 256 + 128 = 384 \ --- Mod: Code removed for two-player Elite: ---------> \LDA MCNT \ This value will be zero on one out of every four \AND #3 \ iterations, so for the other three, skip to nodesire \BNE nodesire \ so we only scan for key presses once every four loops \ --- End of removed code -----------------------------> STX NEEDKEY \ Set NEEDKEY = 128, so the call to LL9 below draws the \ ship and scans for key presses (LL9 resets NEEDKEY to \ 0 so we have to reset NEEDKEY every four iterations \ round the inner loop) .nodesire \ --- Mod: Code removed for two-player Elite: ---------> \STZ INWK \ Set x_lo = 0, so the ship remains in the screen centre \ \STZ INWK+3 \ Set y_lo = 0, so the ship remains in the screen centre \ \JSR LL9 \ Call LL9 to display the ship \ \LDA KTRAN+12 \ Fetch the key press state for the joystick 1 fire \ \ button from the key logger buffer, which contains \ \ the value of the 6522 System VIA input register IRB \ \ (SHEILA &40) \ \AND #%00010000 \ Bit 4 of IRB (PB4) is clear if joystick 1's fire \ \ button is pressed, otherwise it is set, so AND'ing \ \ the value of IRB with %10000 extracts this bit \ \TAX \ Copy the joystick fire button state to X, though this \ \ instruction has no effect, as the comparison flags are \ \ already set by the AND, and the value of X is not used \ \ anywhere \ \BEQ TL2 \ If the joystick fire button is pressed, jump to TL2 \ --- And replaced by: --------------------------------> LDX player1ShipType \ Set x = -xShipOffset, so ship stays on left LDA xShipOffset,X STA INWK STZ INWK+1 LDA #%10000000 STA INWK+2 LDA yShipOffset,X \ Set y = -yShipOffset, to move ship down a little STA INWK+3 STZ INWK+4 LDA #%10000000 STA INWK+5 \ --- End of replacement ------------------------------> JSR LL9 \ Call LL9 to display player 1's ship LDX #0 \ Save player 1's ship JSR SaveShipDataInSlot LDA player2ShipType \ Switch to player 2's ship STA TYPE ASL A \ Set Y = ship type * 2 TAY LDA XX21-2,Y \ The ship blueprints at XX21 start with a lookup STA XX0 \ table that points to the individual ship blueprints, \ so this fetches the low byte of this particular ship \ type's blueprint and stores it in XX0 LDA XX21-1,Y \ Fetch the high byte of this particular ship type's STA XX0+1 \ blueprint and store it in XX0+1 LDX #1 \ Fetch player 2's ship data from slot #1 STX XSAV JSR GetShipDataToINWK LDX player2ShipType \ If z_hi matches this ship type's entry in shipDistance LDA shipDistance,X \ then skip the following decrement CMP INWK+7 BEQ TL1a DEC INWK+7 \ Decrement the ship's distance, to bring the ship \ a bit closer to us .TL1a JSR MVEIT \ Move the ship in space according to the orientation \ vectors and the new value in z_hi LDX #128 \ Set z_lo = 128, so the closest the ship gets to us is STX INWK+6 \ z_hi = 1, z_lo = 128, or 256 + 128 = 384 STX NEEDKEY \ Set NEEDKEY = 128, so the call to LL9 below draws the \ ship and scans for key presses (LL9 resets NEEDKEY to \ 0 so we have to reset NEEDKEY every iteration) LDX player2ShipType \ Set x = +xShipOffset, so ship stays on right LDA xShipOffset,X STA INWK STZ INWK+1 STZ INWK+2 LDA yShipOffset,X \ Set y = -yShipOffset, to move ship down a little STA INWK+3 STZ INWK+4 LDA #%10000000 STA INWK+5 JSR LL9 \ Call LL9 to display player 2's ship LDX #1 \ Save player 1's ship JSR SaveShipDataInSlot \ --- End of replacement ------------------------------> LDA KTRAN \ Fetch the internal key number of the current key \ press from the key logger buffer BNE TL3 \ If a key is being pressed, jump to TL3 STZ optionDebounce \ Clear the debounce variable DEC MCNT \ Decrement the inner loop counter \ --- Mod: Code removed for two-player Elite: ---------> \BNE TLL2 \ Loop back to keep the ship rotating, until the inner \ \ loop counter is zero \ \DEC CNT2 \ Decrement the outer loop counter in CNT2 \ \BNE TLL2 \ Loop back to keep the ship rotating, until the outer \ \ loop counter is zero \ \JMP DEMON \ Once we have iterated through CNT2 iterations of MCNT, \ \ jump to DEMON to start the demo \ \.TL2 \ \DEC JSTK \ Joystick fire button was pressed, so set JSTK to &FF \ \ (it was set to 0 above), to disable keyboard and \ \ enable joysticks \ \.TL3 \ \RTS \ Return from the subroutine \ --- And replaced by: --------------------------------> .titl2 JMP TLL2 \ Loop back to keep rotating the ships .TL3 CMP optionDebounce \ If the same key is being pressed, jump to TLL2 via BEQ titl2 \ titl2 to skip processing the key until it is released \ (so this implements debounce) STA optionDebounce \ Store the key press in optionDebounce CMP #f0 \ If f0 is not being pressed, jump to titl3 to keep BNE titl3 \ checking for keys STZ splitScreen \ Undo the ship clipping used on the title screen and STZ drawPlayerView \ denote we are no longer drawing the title screen STZ titleScreen STZ FRIN \ Despawn the title ships STZ FRIN+1 LDA #LO(LS%) \ Reset the ship line heap STA SLSP LDA #HI(LS%) STA SLSP+1 JSR ZEKTRAN \ Reset the key logger buffer that gets returned from JSR U% \ the I/O processor and the key logger itself, to stop \ the game starting with any logged key presses left \ over from the above LDA #f0 \ Start the game by "pressing" f0 JMP FRCE .titl3 CMP #&19 \ If the left arrow was pressed, jump to titl4 BEQ titl4 CMP #&79 \ If the right arrow was not pressed, jump to titl5 to BNE titl5 \ keep checking .titl4 \ If we get here then either the left or right arrow was \ pressed, so we move the highlight into the other \ column LDA configHighlight \ Redraw the first highlight to remove it JSR PrintOptionA LDA configHighlight \ Redraw the second highlight to remove it EOR #1 JSR PrintOptionA LDA configHighlight \ Flip the side of the highlight EOR #1 STA configHighlight JSR PrintOptionA \ Redraw the first highlight to show it LDA configHighlight \ Redraw the second highlight to show it EOR #1 JSR PrintOptionA BRA titl11 \ Jump to titl11 to wait for any keys to be releases and \ skip any other key checks .titl5 CMP #&39 \ If the up arrow was not pressed, jump to titl8 to BNE titl8 \ keep checking LDX configHighlight \ Set nextHighlight to the new highlight option, DEX \ wrapping around from top to bottom DEX BPL titl6 \ If we have not fallen off the top of the column, jump \ to titl6 TXA \ Set X to 12 or 13, to move to the bottom of the AND #1 \ relevant column CLC ADC #12 TAX .titl6 STX nextHighlight \ Store the number of the next highlight so we can move \ the highlight there .titl7 LDA configHighlight \ Redraw the original highlight to remove it JSR PrintOptionA LDA nextHighlight \ Redraw the next highlight to remove it JSR PrintOptionA LDX nextHighlight \ Move the highlight LDA configHighlight STX configHighlight JSR PrintOptionA \ Redraw the original highlight to show it LDA nextHighlight \ Redraw the next highlight to show it JSR PrintOptionA BRA titl11 \ Jump to titl11 to skip any other key checks .titl8 CMP #&29 \ If the down arrow was not pressed, jump to titl10 to BNE titl10 \ keep checking LDX configHighlight \ Set nextHighlight to the new highlight option, INX \ wrapping around from top to bottom INX CPX #14 \ If we have not fallen off the bottom of the column, BCC titl9 \ jump to titl9 TXA \ Set X to 0 or 1, to move to the top of the relevant AND #1 \ column TAX .titl9 STX nextHighlight \ Store the number of the next highlight so we can move \ the highlight there BRA titl7 \ Jump to titl7 to draw the new highlight .titl10 CMP #&33 \ If "R" was pressed, jump to titl12 to randomise the BEQ titl12 \ loadout options LDA configHighlight \ Redraw the highlight to remove it JSR PrintOptionA JSR ToggleHighlight \ Call ToggleHighlight to toggle the highlighted option LDA configHighlight \ Draw the highlight to update it JSR PrintOptionA .titl11 JMP TLL2 \ Loop back to keep rotating the ships .titl12 \ If we get here then "R" was pressed, so we now \ randomise the loadout in options 0 to 9 LDA #150 \ Read the 6522 System VIA T1C-L timer 1 low-order LDX #&44 \ counter (SHEILA &44) and use it to seed the random JSR OSBYTE \ number generator, along with the current value of TYA \ MCNT, to introduce some entropy into the DORND EOR MCNT \ routine STY RAND+1 JSR DORND \ Set A and X to random numbers four times, to push the JSR DORND \ timer setting into the random number pipeline JSR DORND JSR DORND LDA #0 \ Set YSAV to 0 to use as a loop counter in the STA YSAV \ following loop to work through all ten options .titl13 LSR A \ Set Y = A / 2 so we can use it as an index into the TAY \ option tables below .titl14 JSR DORND \ Set A and X to random numbers AND optionsMask,Y \ Reduce A to the nearest power-of-2 above the required \ range for this option CMP optionsCount,Y \ If A is in the range specified by the optionsCount BCC titl15 \ table (i.e. in the range 0 <= A < optionsCount) then \ we can use this as our random choice, so jump to \ titl15 to toggle the option this many times BCS titl14 \ Otherwise A is out of range, so fetch another random \ number .titl15 \ If we get here then A is in the range 0 to n-1 for \ an option with n choices, so now we toggle the choice \ this many times ADC optionsCount,Y \ Add the number of options for this configuration to A, \ so we always run through the whole set of options \ before applying the random number of toggles (this is \ purely aesthetic, to make the options look a bit more \ like a fruit machine) \ \ The addition works because we know the C flag is clear \ as we got here via a BCC STA toggleCount \ Set toggleCount to the number of toggles to apply to \ this option in the following loop .titl16 LDA YSAV \ Print the current option to remove it JSR PrintOptionA LDA YSAV \ Call ToggleHighlight+3 to toggle the current option JSR ToggleHighlight+3 LDA YSAV \ Print the current option to show the updated value JSR PrintOptionA LDY #3 \ Wait for 3/50 of a second (0.06 seconds), to slow the JSR DELAY \ toggling down a bit DEC toggleCount \ Decrement the toggle counter BNE titl16 .titl17 INC YSAV \ Move on to the next option LDA YSAV \ Loop back until we have printed all options CMP #10 BNE titl13 JMP TLL2 \ Loop back to keep rotating the ships \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: optionsCount \ Type: Variable \ Category: Two-player Elite \ Summary: The number of available options for each configuration \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .optionsCount EQUB 9 \ Ship EQUB 4 \ Lasers EQUB 3 \ Energy EQUB 5 \ Missiles EQUB 2 \ E.C.M. \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: optionsMask \ Type: Variable \ Category: Two-player Elite \ Summary: Bitmask to reduce a random number into the correct power-of-2 \ range for the specified option \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .optionsMask EQUB %00001111 \ Ship EQUB %00000011 \ Lasers EQUB %00000010 \ Energy EQUB %00000111 \ Missiles EQUB %00000001 \ E.C.M. \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: shipDistance \ Type: Variable \ Category: Two-player Elite \ Summary: The distance for each ship on the title screen \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .shipDistance SKIP 11 EQUB 2 \ Cobra Mk III EQUB 3 \ Python EQUB 0 EQUB 3 \ Anaconda EQUB 0 EQUB 1 \ Viper EQUB 1 \ Sidewinder EQUB 1 \ Mamba EQUB 2 \ Krait EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 1 \ Moray EQUB 3 \ Thargoid \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: xShipOffset \ Type: Variable \ Category: Two-player Elite \ Summary: The sideways offset for each ship on the title screen (low byte) \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .xShipOffset SKIP 11 EQUB 160 \ Cobra Mk III EQUB 200 \ Python EQUB 0 EQUB 180 \ Anaconda EQUB 0 EQUB 96 \ Viper EQUB 96 \ Sidewinder EQUB 96 \ Mamba EQUB 150 \ Krait EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 96 \ Moray EQUB 220 \ Thargoid \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: yShipOffset \ Type: Variable \ Category: Two-player Elite \ Summary: The sideways offset for each ship on the title screen (low byte) \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .yShipOffset SKIP 11 EQUB 18 \ Cobra Mk III EQUB 18 \ Python EQUB 0 EQUB 18 \ Anaconda EQUB 0 EQUB 6 \ Viper EQUB 6 \ Sidewinder EQUB 6 \ Mamba EQUB 18 \ Krait EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 0 EQUB 6 \ Moray EQUB 18 \ Thargoid \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: CHECK \ Type: Subroutine \ Category: Save and load \ Summary: Calculate the checksum for the last saved commander data block \ Deep dive: Commander save files \ \ ------------------------------------------------------------------------------ \ \ The checksum for the last saved commander data block is saved as part of the \ commander file, in two places (CHK AND CHK2), to protect against file \ tampering. This routine calculates the checksum and returns it in A. \ \ This algorithm is also implemented in elite-checksum.py. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A The checksum for the last saved commander data block \ \ ****************************************************************************** .CHECK LDX #NT%-2 \ Set X to the size of the commander data block, less \ 2 (to omit the checksum bytes and the save count) CLC \ Clear the C flag so we can do addition without the \ C flag affecting the result TXA \ Seed the checksum calculation by setting A to the \ size of the commander data block, less 2 \ We now loop through the commander data block, \ starting at the end and looping down to the start \ (so at the start of this loop, the X-th byte is the \ last byte of the commander data block, i.e. the save \ count) .QUL2 ADC NA%+7,X \ Add the X-1-th byte of the data block to A, plus the \ C flag EOR NA%+8,X \ EOR A with the X-th byte of the data block DEX \ Decrement the loop counter BNE QUL2 \ Loop back for the next byte in the calculation, until \ we have added byte #0 and EOR'd with byte #1 of the \ data block RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TRNME \ Type: Subroutine \ Category: Save and load \ Summary: Copy the last saved commander's name from INWK to NA% \ \ ****************************************************************************** .TRNME LDX #7 \ The commander's name can contain a maximum of 7 \ characters, and is terminated by a carriage return, \ so set up a counter in X to copy 8 characters .GTL1 LDA INWK+5,X \ Copy the X-th byte of INWK+5 to the X-th byte of NA% STA NA%,X DEX \ Decrement the loop counter BPL GTL1 \ Loop back until we have copied all 8 bytes \ Fall through into TR1 to copy the name back from NA% \ to INWK. This isn't necessary as the name is already \ there, but it does save one byte, as we don't need an \ RTS here \ ****************************************************************************** \ \ Name: TR1 \ Type: Subroutine \ Category: Save and load \ Summary: Copy the last saved commander's name from NA% to INWK \ \ ****************************************************************************** .TR1 LDX #7 \ The commander's name can contain a maximum of 7 \ characters, and is terminated by a carriage return, \ so set up a counter in X to copy 8 characters .GTL2 LDA NA%,X \ Copy the X-th byte of NA% to the X-th byte of INWK+5 STA INWK+5,X DEX \ Decrement the loop counter BPL GTL2 \ Loop back until we have copied all 8 bytes RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: GTNMEW \ Type: Subroutine \ Category: Save and load \ Summary: Fetch the name of a commander file to save or load \ \ ------------------------------------------------------------------------------ \ \ Get the commander's name for loading or saving a commander file. The name is \ stored in the INWK workspace and is terminated by a return character (13). \ \ If ESCAPE is pressed or a blank name is entered, then the name stored is set \ to the name from the last saved commander block. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ INWK The full filename, including drive and directory, in \ the form ":0.E.JAMESON", for example, terminated by a \ return character (13) \ \ ****************************************************************************** .GTNMEW \LDY #8 \ These instructions are commented out in the original \JSR DELAY \ source .GTNME LDX #4 \ First we want to copy the drive and directory part of \ the commander file from S1% (which equals NA%-5), so \ set a counter in x for 5 bytes, as the string is of \ the form ":0.E." .GTL3 LDA NA%-5,X \ Copy the X-th byte from NA%-5 to INWK STA INWK,X DEX \ Decrement the loop counter BPL GTL3 \ Loop back until the whole drive and directory string \ has been copied to INWK to INWK+4 LDA #7 \ The call to MT26 below uses the OSWORD block at RLINE STA RLINE+2 \ to fetch the line, and RLINE+2 defines the maximum \ line length allowed, so this changes the maximum \ length to 7 (as that's the longest commander name \ allowed) LDA #8 \ Print extended token 8 ("{single cap}COMMANDER'S JSR DETOK \ NAME? ") JSR MT26 \ Call MT26 to fetch a line of text from the keyboard \ to INWK+5, with the text length in Y, so INWK now \ contains the full pathname of the file, as in \ ":0.E.JAMESON", for example LDA #9 \ Reset the maximum length in RLINE+2 to the original STA RLINE+2 \ value of 9 TYA \ The OSWORD call returns the length of the commander's \ name in Y, so transfer this to A BEQ TR1 \ If A = 0, no name was entered, so jump to TR1 to copy \ the last saved commander's name from NA% to INWK \ and return from the subroutine there RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MT26 \ Type: Subroutine \ Category: Text \ Summary: Fetch a line of text from the keyboard \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ If ESCAPE is pressed or a blank name is entered, then an empty string is \ returned. \ \ Returns: \ \ Y The size of the entered text, or 0 if none was entered \ or if ESCAPE was pressed \ \ INWK+5 The entered text, terminated by a carriage return \ \ C flag Set if ESCAPE was pressed \ \ ****************************************************************************** .MT26 LDA #VIAE \ Send a #VIAE %10000001 command to the I/O processor to JSR OSWRCH \ clear 6522 System VIA interrupt enable register IER LDA #%10000001 \ (SHEILA &4E) bit 1 (i.e. enable the CA2 interrupt, JSR OSWRCH \ which comes from the keyboard) LDY #8 \ Wait for 8/50 of a second (0.16 seconds) JSR DELAY JSR FLKB \ Call FLKB to flush the keyboard buffer LDX #LO(RLINE) \ Set (Y X) to point to the RLINE parameter block LDY #HI(RLINE) LDA #0 \ Call OSWORD with A = 0 to read a line from the current JSR OSWORD \ input stream (i.e. the keyboard) BCC P%+4 \ The C flag will be set if we pressed ESCAPE when \ entering the name, otherwise it will be clear, so \ skip the next instruction if ESCAPE is not pressed LDY #0 \ ESCAPE was pressed, so set Y = 0 (as the OSWORD call \ returns the length of the entered string in Y) LDA #VIAE \ Send a #VIAE %00000001 command to the I/O processor to JSR OSWRCH \ set 6522 System VIA interrupt enable register IER LDA #%00000001 \ (SHEILA &4E) bit 1 (i.e. disable the CA2 interrupt, JSR OSWRCH \ which comes from the keyboard) JMP FEED \ Jump to FEED to print a newline, returning from the \ subroutine using a tail call \ ****************************************************************************** \ \ Name: RLINE \ Type: Variable \ Category: Text \ Summary: The OSWORD configuration block used to fetch a line of text from \ the keyboard \ \ ****************************************************************************** .RLINE EQUW INWK+5 \ The address to store the input, so the text entered \ will be stored in INWK+5 as it is typed EQUB 9 \ Maximum line length = 9, as that's the maximum size \ for a commander's name including a directory name EQUB '!' \ Allow ASCII characters from "!" through to "{" in EQUB '{' \ the input \ ****************************************************************************** \ \ Name: ZERO \ Type: Subroutine \ Category: Utility routines \ Summary: Reset the local bubble of universe and ship status \ \ ------------------------------------------------------------------------------ \ \ This resets the following workspaces to zero: \ \ * UP workspace variables from FRIN to de, which include the ship slots for \ the local bubble of universe, and various flight and ship status variables \ \ ****************************************************************************** .ZERO LDX #(de-FRIN) \ We're going to zero the UP workspace variables from \ FRIN to de, so set a counter in X for the correct \ number of bytes LDA #0 \ Set A = 0 so we can zero the variables .ZEL2 STA FRIN,X \ Zero the X-th byte of FRIN to de DEX \ Decrement the loop counter BPL ZEL2 \ Loop back to zero the next variable until we have done \ them all \ --- Mod: Code added for two-player Elite: -----------> LDX #(endZero-startWP) \ We're going to zero the workspace variables from \ startWP to endZero (not including endZero), so set a \ counter in X for the correct number of bytes .ZEL2a STA startWP-1,X \ Zero the X-1-th byte of startWP to endZero DEX \ Decrement the loop counter BNE ZEL2a \ Loop back to zero the next variable until we have done \ them all \ --- End of added code -------------------------------> RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: ZEBC \ Type: Subroutine \ Category: Utility routines \ Summary: Zero-fill pages &B and &C \ \ ****************************************************************************** .ZEBC LDX #&C \ Call ZES1 with X = &C to zero-fill page &C JSR ZES1 DEX \ Decrement X to &B \ Fall through into ZES1 to zero-fill page &B \ ****************************************************************************** \ \ Name: ZES1 \ Type: Subroutine \ Category: Utility routines \ Summary: Zero-fill the page whose number is in X \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The page we want to zero-fill \ \ ****************************************************************************** .ZES1 LDY #0 \ If we set Y = SC = 0 and fall through into ZES2 STY SC \ below, then we will zero-fill 255 bytes starting from \ SC - in other words, we will zero-fill the whole of \ page X \ ****************************************************************************** \ \ Name: ZES2 \ Type: Subroutine \ Category: Utility routines \ Summary: Zero-fill a specific page \ \ ------------------------------------------------------------------------------ \ \ Zero-fill from address (X SC) + Y to (X SC) + &FF. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The high byte (i.e. the page) of the starting point of \ the zero-fill \ \ Y The offset from (X SC) where we start zeroing, counting \ up to &FF \ \ SC The low byte (i.e. the offset into the page) of the \ starting point of the zero-fill \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ Z flag Z flag is set \ \ ****************************************************************************** .ZES2 LDA #0 \ Load A with the byte we want to fill the memory block \ with - i.e. zero STX SC+1 \ We want to zero-fill page X, so store this in the \ high byte of SC, so the 16-bit address in SC and \ SC+1 is now pointing to the SC-th byte of page X .ZEL1 STA (SC),Y \ Zero the Y-th byte of the block pointed to by SC, \ so that's effectively the Y-th byte before SC INY \ Increment the loop counter BNE ZEL1 \ Loop back to zero the next byte RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: CTLI \ Type: Variable \ Category: Save and load \ Summary: The OS command string for cataloguing a disc \ \ ****************************************************************************** .CTLI EQUS ".0" \ The "0" part of the string is overwritten with the EQUB 13 \ actual drive number by the CATS routine \ ****************************************************************************** \ \ Name: DELI \ Type: Variable \ Category: Save and load \ Summary: The OS command string for deleting a file \ \ ****************************************************************************** .DELI EQUS "DELETE:0.E.1234567" EQUB 13 \ ****************************************************************************** \ \ Name: CATS \ Type: Subroutine \ Category: Save and load \ Summary: Ask for a disc drive number and print a catalogue of that drive \ \ ------------------------------------------------------------------------------ \ \ This routine asks for a disc drive number, and if it is a valid number (0-3) \ it displays a catalogue of the disc in that drive. It also updates the OS \ command at CTLI so that when that command is run, it catalogues the correct \ drive. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag Clear if a valid drive number was entered (0-3), set \ otherwise \ \ ****************************************************************************** .CATS JSR GTDRV \ Get an ASCII disc drive number from the keyboard in A, \ setting the C flag if an invalid drive number was \ entered BCS DELT-1 \ If the C flag is set, then an invalid drive number was \ entered, so return from the subroutine (as DELT-1 \ contains an RTS) STA CTLI+1 \ Store the drive number in the second byte of the \ command string at CTLI, so it overwrites the "0" in \ ".0" with the drive number to catalogue STA DTW7 \ Store the drive number in DTW7, so printing extended \ token 4 will show the correct drive number (as token 4 \ contains the {drive number} jump code, which calls \ MT16 to print the character in DTW7) LDA #4 \ Print extended token 4, which clears the screen and JSR DETOK \ prints the boxed-out title "DRIVE {drive number} \ CATALOGUE" LDA #DOCATF \ Send a #DOCATF 1 command to the I/O processor to set JSR OSWRCH \ the CATF flag to 1, so that the TT26 routine on the LDA #1 \ I/O processor prints out the disc catalogue correctly JSR OSWRCH STA XC \ Move the text cursor to column 1 LDX #LO(CTLI) \ Set (Y X) to point to the OS command at CTLI, which LDY #HI(CTLI) \ contains a dot and the drive number, which is the \ DFS command for cataloguing that drive (*. being short \ for *CAT) JSR SCLI2 \ Call SCLI2 to execute the OS command at (Y X), which \ catalogues the disc, setting the SVN flag while it's \ running to indicate disc access is in progress LDA #DOCATF \ Send a #DOCATF 0 command to the I/O processor to set JSR OSWRCH \ the CATF flag to 0, so that TT26 returns to normal LDA #0 \ printing JSR OSWRCH CLC \ Clear the C flag RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: DELT \ Type: Subroutine \ Category: Save and load \ Summary: Catalogue a disc, ask for a filename to delete, and delete the \ file \ \ ------------------------------------------------------------------------------ \ \ This routine asks for a disc drive number, and if it is a valid number (0-3) \ it displays a catalogue of the disc in that drive. It then asks for a filename \ to delete, updates the OS command at DELI so that when that command is run, it \ deletes the correct file, and then it does the deletion. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ DELT-1 Contains an RTS \ \ ****************************************************************************** .DELT JSR CATS \ Call CATS to ask for a drive number (or a directory \ name on the Master Compact) and catalogue that disc \ or directory BCS SVE \ If the C flag is set then an invalid drive number was \ entered as part of the catalogue process, so jump to \ SVE to display the disc access menu LDA CTLI+1 \ The call to CATS above put the drive number into STA DELI+7 \ CTLI+1, so copy the drive number into DELI+7 so that \ the drive number in the "DELETE:0.E.1234567" string \ gets updated (i.e. the number after the colon) LDA #9 \ Print extended token 9 ("{clear bottom of screen}FILE JSR DETOK \ TO DELETE?") JSR MT26 \ Call MT26 to fetch a line of text from the keyboard \ to INWK+5, with the text length in Y TYA \ If no text was entered (Y = 0) then jump to SVE to BEQ SVE \ display the disc access menu \ We now copy the entered filename from INWK to DELI, so \ that it overwrites the filename part of the string, \ i.e. the "E.1234567" part of "DELETE:0.E.1234567" LDX #9 \ Set up a counter in X to count from 9 to 1, so that we \ copy the string starting at INWK+4+1 (i.e. INWK+5) to \ DELI+8+1 (i.e. DELI+9 onwards, or "E.1234567") .DELL1 LDA INWK+4,X \ Copy the X-th byte of INWK+4 to the X-th byte of STA DELI+8,X \ DELI+8 DEX \ Decrement the loop counter BNE DELL1 \ Loop back to DELL1 to copy the next character until we \ have copied the whole filename LDX #LO(DELI) \ Set (Y X) to point to the OS command at DELI, which LDY #HI(DELI) \ contains the DFS command for deleting this file JSR SCLI2 \ Call SCLI2 to execute the OS command at (Y X), which \ deletes the file, setting the SVN flag while it's \ running to indicate disc access is in progress JMP SVE \ Jump to SVE to display the disc access menu and return \ from the subroutine using a tail call \ ****************************************************************************** \ \ Name: stack \ Type: Variable \ Category: Save and load \ Summary: Temporary storage for the stack pointer when switching the BRKV \ handler between BRBR and MEBRK \ \ ****************************************************************************** .stack EQUB 0 \ ****************************************************************************** \ \ Name: MEBRK \ Type: Subroutine \ Category: Save and load \ Summary: The BRKV handler for disc access operations \ Deep dive: Swapping between the docked and flight code \ \ ------------------------------------------------------------------------------ \ \ This routine is used to display error messages from the disc filing system \ while disc access operations are being performed. When called, it makes a beep \ and prints the system error message in the block pointed to by (&FD &FE), \ which is where the disc filing system will put any disc errors (such as "File \ not found", "Disc error" and so on). It then waits for a key press and returns \ to the disc access menu. \ \ BRKV is set to this routine at the start of the SVE routine, just before the \ disc access menu is shown, and it reverts to BRBR at the end of the SVE \ routine after the disc access menu has been processed. In other words, BRBR is \ the standard BRKV handler for the game, and it's swapped out to MEBRK for disc \ access operations only. \ \ When it is the BRKV handler, the routine can be triggered using a BRK \ instruction. The main difference between this routine and the standard BRKV \ handler in BRBR is that this routine returns to the disc access menu rather \ than restarting the game. \ \ ****************************************************************************** .MEBRK LDX stack \ Set the stack pointer to the value that we stored in TXS \ the stack variable, so that's back to the value it had \ before we set BRKV to point to MEBRK in the SVE \ routine JSR backtonormal \ Disable the keyboard and set the SVN flag to 0 TAY \ The call to backtonormal sets A to 0, so this sets Y \ to 0, which we use as a loop counter below LDA #7 \ Set A = 7 to generate a beep before we print the error \ message .MEBRKL JSR OSWRCH \ Print the character in A (which contains a beep on the \ first loop iteration), and then any non-zero \ characters we fetch from the error message INY \ Increment the loop counter BEQ retry \ If Y = 0 then we have worked our way through a whole \ page, so jump to retry to wait for a key press and \ display the disc access menu (this BEQ is effectively \ a JMP, as we didn't take the BNE branch above) LDA (&FD),Y \ Fetch the Y-th byte of the block pointed to by \ (&FD &FE), so that's the Y-th character of the message \ pointed to by the MOS error message pointer BNE MEBRKL \ If the fetched character is non-zero, loop back to the \ JSR OSWRCH above to print the it, and keep looping \ until we fetch a zero (which marks the end of the \ message) BEQ retry \ Jump to retry to wait for a key press and display the \ disc access menu (this BEQ is effectively a JMP, as we \ didn't take the BNE branch above) \ ****************************************************************************** \ \ Name: CAT \ Type: Subroutine \ Category: Save and load \ Summary: Catalogue a disc, wait for a key press and display the disc access \ menu \ \ ****************************************************************************** .CAT JSR CATS \ Call CATS to ask for a drive number, catalogue that \ disc and update the catalogue command at CTLI \ Fall through into retry to wait for a key press and \ display the disc access menu \ ****************************************************************************** \ \ Name: retry \ Type: Subroutine \ Category: Save and load \ Summary: Scan the keyboard until a key is pressed and display the disc \ access menu \ \ ****************************************************************************** .retry JSR t \ Scan the keyboard until a key is pressed, returning \ the ASCII code in A and X \ Fall through into SVE to display the disc access menu \ ****************************************************************************** \ \ Name: SVE \ Type: Subroutine \ Category: Save and load \ Summary: Display the disc access menu and process saving of commander files \ Deep dive: Commander save files \ The competition code \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag Set if we loaded a new file, clear otherwise \ \ ****************************************************************************** .SVE JSR ZEBC \ Call ZEBC to zero-fill pages &B and &C TSX \ Transfer the stack pointer to X and store it in stack, STX stack \ so we can restore it in the MEBRK routine LDA #LO(MEBRK) \ Set BRKV to point to the MEBRK routine, disabling SEI \ interrupts while we make the change and re-enabling STA BRKV \ them once we are done. MEBRK is the BRKV handler for LDA #HI(MEBRK) \ disc access operations, and replaces the standard BRKV STA BRKV+1 \ handler in BRBR while disc access operations are CLI \ happening LDA #1 \ Print extended token 1, the disc access menu, which JSR DETOK \ presents these options: \ \ 1. Load New Commander \ 2. Save Commander {commander name} \ 3. Catalogue \ 4. Delete A File \ 5. Exit JSR t \ Scan the keyboard until a key is pressed, returning \ the ASCII code in A and X CMP #'1' \ If A < ASCII "1", jump to SVEX to exit as the key BCC SVEX \ press doesn't match a menu option CMP #'4' \ If "4" was pressed, jump to DELT to process option 4 BEQ DELT \ (delete a file) BCS SVEX \ If A >= ASCII "4", jump to SVEX to exit as the key \ press is either option 5 (exit), or it doesn't match a \ menu option (as we already checked for "4" above) CMP #'2' \ If A >= ASCII "2" (i.e. save or catalogue), skip to BCS SV1 \ SV1 JSR GTNMEW \ If we get here then option 1 (load) was chosen, so \ call GTNMEW to fetch the name of the commander file \ to load (including drive number and directory) into \ INWK JSR LOD \ Call LOD to load the commander file JSR TRNME \ Transfer the commander filename from INWK to NA% SEC \ Set the C flag to indicate we loaded a new commander BCS SVEX+1 \ file, and return from the subroutine (as SVEX+1 \ contains an RTS) .SV1 BNE CAT \ We get here following the CMP #'2' above, so this \ jumps to CAT if option 2 was not chosen - in other \ words, if option 3 (catalogue) was chosen JSR GTNMEW \ If we get here then option 2 (save) was chosen, so \ call GTNMEW to fetch the name of the commander file \ to save (including drive number and directory) into \ INWK JSR TRNME \ Transfer the commander filename from INWK to NA% LSR SVC \ Halve the save count value in SVC LDA #3 \ Print extended token 3 ("COMPETITION NUMBER:") JSR DETOK LDX #NT% \ We now want to copy the current commander data block \ from location TP to the last saved commander block at \ NA%+8, so set a counter in X to copy the NT% bytes in \ the commander data block \ \ We also want to copy the data block to another \ location &0B00, which is normally used for the ship \ lines heap .SVL1 LDA TP,X \ Copy the X-th byte of TP to the X-th byte of &0B00 STA &0B00,X \ and NA%+8 STA NA%+8,X DEX \ Decrement the loop counter BPL SVL1 \ Loop back until we have copied all the bytes in the \ commander data block JSR CHECK \ Call CHECK to calculate the checksum for the last \ saved commander and return it in A STA CHK \ Store the checksum in CHK, which is at the end of the \ last saved commander block PHA \ Store the checksum on the stack ORA #%10000000 \ Set K = checksum with bit 7 set STA K EOR COK \ Set K+2 = K EOR COK (the competition flags) STA K+2 EOR CASH+2 \ Set K+1 = K+2 EOR CASH+2 (the third cash byte) STA K+1 EOR #&5A \ Set K+3 = K+1 EOR &5A EOR TALLY+1 (the high byte of EOR TALLY+1 \ the kill tally) STA K+3 CLC \ Clear the C flag so the call to BPRNT does not include \ a decimal point JSR BPRNT \ Print the competition number stored in K to K+3. The \ value of U might affect how this is printed, and as \ it's a temporary variable in zero page that isn't \ reset by ZERO, it might have any value, but as the \ competition code is a 10-digit number, this just means \ it may or may not have an extra space of padding JSR TT67 \ Print a newline PLA \ Restore the checksum from the stack STA &0B00+NT% \ Store the checksum in the last byte of the save file \ at &0B00 (the equivalent of CHK in the last saved \ block) EOR #&A9 \ Store the checksum EOR &A9 in CHK2, the penultimate STA CHK2 \ byte of the last saved commander block STA &0AFF+NT% \ Store the checksum EOR &A9 in the penultimate byte of \ the save file at &0B00 (the equivalent of CHK2 in the \ last saved block) LDY #&B \ Set up an OSFILE block at &0C00, containing: STY &0C0B \ INY \ Start address for save = &00000B00 in &0C0A to &0C0D STY &0C0F \ \ End address for save = &00000C00 in &0C0E to &0C11 \ \ Y is left containing &C which we use below LDA #0 \ Call QUS1 with A = 0, Y = &C to save the commander JSR QUS1 \ file with the filename we copied to INWK at the start \ of this routine IF _SNG45 JSR DFAULT \ Call DFAULT to reset the current commander data block \ to the last saved commander ENDIF .SVEX CLC \ Clear the C flag to indicate we didn't just load a new \ commander file JMP BRKBK \ Jump to BRKBK to set BRKV back to the standard BRKV \ handler for the game, and return from the subroutine \ using a tail call \ ****************************************************************************** \ \ Name: QUS1 \ Type: Subroutine \ Category: Save and load \ Summary: Save or load the commander file \ Deep dive: Commander save files \ \ ------------------------------------------------------------------------------ \ \ The filename should be stored at INWK, terminated with a carriage return (13). \ The routine asks for a drive number and updates the filename accordingly \ before performing the load or save. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A File operation to be performed. Can be one of the \ following: \ \ * 0 (save file) \ \ * &FF (load file) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag Set if an invalid drive number was entered \ \ ****************************************************************************** .QUS1 PHA \ Store A on the stack so we can restore it after the \ call to GTDRV JSR GTDRV \ Get an ASCII disc drive number from the keyboard in A, \ setting the C flag if an invalid drive number was \ entered STA INWK+1 \ Store the ASCII drive number in INWK+1, which is the \ drive character of the filename string ":0.E." PLA \ Restore A from the stack BCS QUR \ If the C flag is set, then an invalid drive number was \ entered, so jump to QUR to return from the subroutine PHA \ Store A on the stack so we can restore it after the \ call to DODOSVN LDA #255 \ Set the SVN flag to 255 to indicate that disc access JSR DODOSVN \ is in progress PLA \ Restore A from the stack LDX #INWK \ Store a pointer to INWK at the start of the block at STX &0C00 \ &0C00, storing #INWK in the low byte because INWK is \ in zero page LDX #0 \ Set (Y X) = &0C00 LDY #&C JSR OSFILE \ Call OSFILE to do the file operation specified in \ &0C00 (i.e. save or load a file depending on the value \ of A) JSR CLDELAY \ Pause for 1280 empty loops LDA #0 \ Set the SVN flag to 0 indicate that disc access has JSR DODOSVN \ finished CLC \ Clear the C flag .QUR RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: GTDRV \ Type: Subroutine \ Category: Save and load \ Summary: Get an ASCII disc drive number from the keyboard \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A The ASCII value of the entered drive number ("0" to "3") \ \ C flag Clear if a valid drive number was entered (0-3), set \ otherwise \ \ ****************************************************************************** .GTDRV LDA #2 \ Print extended token 2 ("{cr}WHICH DRIVE?") JSR DETOK JSR t \ Scan the keyboard until a key is pressed, returning \ the ASCII code in A and X ORA #%00010000 \ Set bit 4 of A, perhaps to avoid printing any control \ characters in the next instruction JSR CHPR \ Print the character in A PHA \ Store A on the stack so we can retrieve it after the \ call to FEED JSR FEED \ Print a newline PLA \ Restore A from the stack CMP #'0' \ If A < ASCII "0", then it is not a valid drive number, BCC LOR \ so jump to LOR to set the C flag and return from the \ subroutine CMP #'4' \ If A >= ASCII "4", then it is not a valid drive \ number, and this CMP sets the C flag, otherwise it is \ a valid drive number in the range 0-3, so clear it RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LOD \ Type: Subroutine \ Category: Save and load \ Summary: Load a commander file \ \ ------------------------------------------------------------------------------ \ \ The filename should be stored at INWK, terminated with a carriage return (13). \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ LOR Set the C flag and return from the subroutine \ \ ****************************************************************************** .LOD \LDX #LO(MINI) \ These instructions are commented out in the original \LDY #HI(MINI) \ source, but they would load a commander file called \JSR OSCLI \ "E.MINING" and continue below, so presumably this is \JMP LOL1-2 \ code for loading a test commander file \ \LDX #2 \ These instructions are commented out in the original \JSR FX200 \ source, but they would enable the ESCAPE key and clear \ memory if the BREAK key is pressed (*FX 200,2) JSR ZEBC \ Call ZEBC to zero-fill pages &B and &C LDY #&B \ Set up an OSFILE block at &0C00, containing: STY &0C03 \ INC &0C0B \ Load address = &00000B00 in &0C02 to &0C05 \ \ Length of file = &00000100 in &0C0A to &0C0D LDA #&FF \ Call QUS1 with A = &FF, Y = &C to load the commander JSR QUS1 \ file to address &0B00 BCS LOR \ If the C flag is set then an invalid drive number was \ entered during the call to QUS1 and the file wasn't \ loaded, so jump to LOR to return from the subroutine LDA &0B00 \ If the first byte of the loaded file has bit 7 set, BMI ELT2F \ jump to ELT2F, as this is an invalid commander file \ \ ELT2F contains a BRK instruction, which will force an \ interrupt to call the address in BRKV, which will \ print out the system error at ELT2F LDX #NT% \ We have successfully loaded the commander file at \ &0B00, so now we want to copy it to the last saved \ commander data block at NA%+8, so we set up a counter \ in X to copy NT% bytes .LOL1 LDA &0B00,X \ Copy the X-th byte of &0B00 to the X-th byte of NA%+8 STA NA%+8,X DEX \ Decrement the loop counter BPL LOL1 \ Loop back until we have copied all NT% bytes .LOR SEC \ Set the C flag RTS \ Return from the subroutine .ELT2F BRK \ The error that is printed if we try to load an EQUB &49 \ invalid commander file with bit 7 of byte #0 set EQUS "Illegal " \ (&49 is the error number) EQUS "ELITE II file" BRK \.MINI \ These instructions are commented out in the original \ \ source, and form part of the commented section above \EQUS "L.E.MINING B00" \EQUB 13 \ ****************************************************************************** \ \ Name: FX200 \ Type: Subroutine \ Category: Utility routines \ Summary: Set the behaviour of the ESCAPE and BREAK keys \ \ ------------------------------------------------------------------------------ \ \ This is the equivalent of a *FX 200 command, which controls the behaviour of \ the ESCAPE and BREAK keys. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X Controls the behaviour as follows: \ \ * 0 = Enable ESCAPE key \ Normal BREAK key action \ \ * 1 = Disable ESCAPE key \ Normal BREAK key action \ \ * 2 = Enable ESCAPE key \ Clear memory if the BREAK key is pressed \ \ * 3 = Disable ESCAPE key \ Clear memory if the BREAK key is pressed \ \ ****************************************************************************** .FX200 LDY #0 \ Call OSBYTE 200 with Y = 0, so the new value is set to LDA #200 \ X, and return from the subroutine using a tail call JMP OSBYTE \ ****************************************************************************** \ \ Name: backtonormal \ Type: Subroutine \ Category: Utility routines \ Summary: Disable the keyboard, set the SVN flag to 0, and return with A = 0 \ \ ****************************************************************************** .backtonormal LDA #VIAE \ Send a #VIAE %00000001 command to the I/O processor to JSR OSWRCH \ set 6522 System VIA interrupt enable register IER LDA #%00000001 \ (SHEILA &4E) bit 1 (i.e. disable the CA2 interrupt, JSR OSWRCH \ which comes from the keyboard) LDA #0 \ Set the SVN flag to 0 and return from the subroutine BEQ DODOSVN \ using a tail call (this BEQ is effectively a JMP as A \ is always zero) \ ****************************************************************************** \ \ Name: SCLI2 \ Type: Subroutine \ Category: Utility routines \ Summary: Execute an OS command, setting the SVN flag while it's running \ \ ------------------------------------------------------------------------------ \ \ SVN is set to 255 before the command is run, to indicate that disc access is \ in progress, and is reset to 0 once it has finished. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ (Y X) The address of a string containing the command to run, \ terminated by a carriage return (ASCII 13) \ \ ****************************************************************************** .SCLI2 LDA #255 \ Set the SVN flag to 255 JSR DODOSVN JSR OSCLI \ Call OSCLI to execute the OS command at (Y X) LDA #0 \ Set A = 0 for the new value of the SVN flag \ Fall through into DODOSVN to set the SVN flag to 0 \ ****************************************************************************** \ \ Name: DODOSVN \ Type: Subroutine \ Category: Save and load \ Summary: Set the SVN ("save in progress") flag by sending a #DOsvn command \ to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The new value of SVN \ \ ****************************************************************************** .DODOSVN PHA \ Store A and Y on the stack PHY LDA #DOsvn \ Send the first part of a #DOsvn command to the I/O JSR OSWRCH \ processor PLY \ Retrieve the values of A and Y from the stack PLA JSR OSWRCH \ Send the new value of SVN to the I/O processor, so \ we've now sent a #DOsvn command \ Fall through into CLDELAY to pause for 1280 empty \ loops \ ****************************************************************************** \ \ Name: CLDELAY \ Type: Subroutine \ Category: Utility routines \ Summary: Delay by iterating through 5 * 256 (1280) empty loops \ \ ****************************************************************************** .CLDELAY PHX \ Store A, X and Y on the stack PHY PHA LDY #5 \ We are going to loop for 5 * 256 empty loops, so set a \ counter in Y for the outer loop LDX #0 \ And set a counter in X for the inner loop .CLDEL1 DEX \ Decrement the inner loop counter BNE CLDEL1 \ Loop back to CLDEL1 until the inner loop counter has \ rolled around to 0 again DEY \ Decrement the outer loop counter BNE CLDEL1 \ Loop back to CLDEL1 until the outer loop counter has \ reached 0 PLA \ Retrieve A, X and Y from the stack PLY PLX RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: ZEKTRAN \ Type: Subroutine \ Category: Keyboard \ Summary: Reset the key logger buffer at KTRAN \ \ ****************************************************************************** .ZEKTRAN \ --- Mod: Code added for two-player Elite: -----------> LDA #%00110000 \ Reset the joystick fire entry to "not pressed" STA KTRAN+12 \ --- End of added code -------------------------------> LDX #11 \ We use the first 12 bytes of the key logger buffer at \ KTRAN, so set a loop counter accordingly LDA #0 \ We want to zero the key logger buffer, so set A % 0 .ZEKLOOP STA KTRAN,X \ Reset the X-th byte of the key logger buffer to 0 DEX \ Decrement the loop counter BPL ZEKLOOP \ Loop back until we have zeroed bytes #11 through #0 RTS \ Return from the subroutine RTS \ This instruction has no effect as we already returned \ from the subroutine \ ****************************************************************************** \ \ Name: Player1Vector \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Calculate the vector to player 1 and store it in XX15 \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ SPS1+1 A BRK instruction \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Player1Vector LDX #0 \ Copy the two high bytes of player 1's x-coordinate JSR Player1SPS3 \ into K3(2 1 0), separating out the sign bit into K3+2 LDX #3 \ Copy the two high bytes of player 2's y-coordinate JSR Player1SPS3 \ into K3(5 4 3), separating out the sign bit into K3+5 LDX #6 \ Copy the two high bytes of player 2's z-coordinate JSR Player1SPS3 \ into K3(8 7 6), separating out the sign bit into K3+8 JMP TAS2 \ Jump to TAS2 to build XX15 from K3 \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: Player2Vector \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Calculate the vector to player 2 and store it in XX15 \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ SPS1+1 A BRK instruction \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Player2Vector LDX #0 \ Copy the two high bytes of player 2's x-coordinate JSR Player2SPS3 \ into K3(2 1 0), separating out the sign bit into K3+2 LDX #3 \ Copy the two high bytes of player 2's y-coordinate JSR Player2SPS3 \ into K3(5 4 3), separating out the sign bit into K3+5 LDX #6 \ Copy the two high bytes of player 2's z-coordinate JSR Player2SPS3 \ into K3(8 7 6), separating out the sign bit into K3+8 JMP TAS2 \ Jump to TAS2 to build XX15 from K3 \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: SPS1 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Calculate the vector to the planet and store it in XX15 \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ SPS1+1 A BRK instruction \ \ ****************************************************************************** .SPS1 LDX #0 \ Copy the two high bytes of the planet's x-coordinate JSR SPS3 \ into K3(2 1 0), separating out the sign bit into K3+2 LDX #3 \ Copy the two high bytes of the planet's y-coordinate JSR SPS3 \ into K3(5 4 3), separating out the sign bit into K3+5 LDX #6 \ Copy the two high bytes of the planet's z-coordinate JSR SPS3 \ into K3(8 7 6), separating out the sign bit into K3+8 \ Fall through into TAS2 to build XX15 from K3 \ ****************************************************************************** \ \ Name: TAS2 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Normalise the three-coordinate vector in K3 \ \ ------------------------------------------------------------------------------ \ \ Normalise the vector in K3, which has 16-bit values and separate sign bits, \ and store the normalised version in XX15 as a signed 8-bit vector. \ \ A normalised vector (also known as a unit vector) has length 1, so this \ routine takes an existing vector in K3 and scales it so the length of the \ new vector is 1. This is used in a number of places: when drawing the compass, \ when applying AI tactics to ships (so traders fly towards planets and missiles \ fly towards their targets, for example), and when implementing the docking \ computer in the enhanced versions of Elite. \ \ We do this in two stages. This stage shifts the 16-bit vector coordinates in \ K3 to the left as far as they will go without losing any bits off the end, so \ we can then take the high bytes and use them as the most accurate 8-bit vector \ to normalise. Then the next stage (in routine NORM) does the normalisation. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ K3(2 1 0) The 16-bit x-coordinate as (x_sign x_hi x_lo), where \ x_sign is just bit 7 \ \ K3(5 4 3) The 16-bit y-coordinate as (y_sign y_hi y_lo), where \ y_sign is just bit 7 \ \ K3(8 7 6) The 16-bit z-coordinate as (z_sign z_hi z_lo), where \ z_sign is just bit 7 \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ XX15 The normalised vector, with: \ \ * The x-coordinate in XX15 \ \ * The y-coordinate in XX15+1 \ \ * The z-coordinate in XX15+2 \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ TA2 Calculate the length of the vector in XX15 (ignoring the \ low coordinates), returning it in Q \ \ ****************************************************************************** .TAS2 LDA K3 \ OR the three low bytes and 1 to get a byte that has ORA K3+3 \ a 1 wherever any of the three low bytes has a 1 ORA K3+6 \ (as well as always having bit 0 set), and store in ORA #1 \ K3+9 STA K3+9 LDA K3+1 \ OR the three high bytes to get a byte in A that has a ORA K3+4 \ 1 wherever any of the three high bytes has a 1 ORA K3+7 \ (A K3+9) now has a 1 wherever any of the 16-bit \ values in K3 has a 1 .TAL2 ASL K3+9 \ Shift (A K3+9) to the left, so bit 7 of the high byte ROL A \ goes into the C flag BCS TA2 \ If the left shift pushed a 1 out of the end, then we \ know that at least one of the coordinates has a 1 in \ this position, so jump to TA2 as we can't shift the \ values in K3 any further to the left ASL K3 \ Shift K3(1 0), the x-coordinate, to the left ROL K3+1 ASL K3+3 \ Shift K3(4 3), the y-coordinate, to the left ROL K3+4 ASL K3+6 \ Shift K3(6 7), the z-coordinate, to the left ROL K3+7 BCC TAL2 \ Jump back to TAL2 to do another shift left (this BCC \ is effectively a JMP as we know bit 7 of K3+7 is not a \ 1, as otherwise bit 7 of A would have been a 1 and we \ would have taken the BCS above) .TA2 LDA K3+1 \ Fetch the high byte of the x-coordinate from our left- LSR A \ shifted K3, shift it right to clear bit 7, stick the ORA K3+2 \ sign bit in there from the x_sign part of K3, and STA XX15 \ store the resulting signed 8-bit x-coordinate in XX15 LDA K3+4 \ Fetch the high byte of the y-coordinate from our left- LSR A \ shifted K3, shift it right to clear bit 7, stick the ORA K3+5 \ sign bit in there from the y_sign part of K3, and STA XX15+1 \ store the resulting signed 8-bit y-coordinate in \ XX15+1 LDA K3+7 \ Fetch the high byte of the z-coordinate from our left- LSR A \ shifted K3, shift it right to clear bit 7, stick the ORA K3+8 \ sign bit in there from the z_sign part of K3, and STA XX15+2 \ store the resulting signed 8-bit z-coordinate in \ XX15+2 \ Now we have a signed 8-bit version of the vector K3 in \ XX15, so fall through into NORM to normalise it \ ****************************************************************************** \ \ Name: NORM \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Normalise the three-coordinate vector in XX15 \ Deep dive: Tidying orthonormal vectors \ Orientation vectors \ \ ------------------------------------------------------------------------------ \ \ We do this by dividing each of the three coordinates by the length of the \ vector, which we can calculate using Pythagoras. Once normalised, 96 (&60) is \ used to represent a value of 1, and 96 with bit 7 set (&E0) is used to \ represent -1. This enables us to represent fractional values of less than 1 \ using integers. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ XX15 The vector to normalise, with: \ \ * The x-coordinate in XX15 \ \ * The y-coordinate in XX15+1 \ \ * The z-coordinate in XX15+2 \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ XX15 The normalised vector \ \ Q The length of the original XX15 vector \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ NO1 Contains an RTS \ \ ****************************************************************************** .NORM LDA XX15 \ Fetch the x-coordinate into A JSR SQUA \ Set (A P) = A * A = x^2 STA R \ Set (R Q) = (A P) = x^2 LDA P STA Q LDA XX15+1 \ Fetch the y-coordinate into A JSR SQUA \ Set (A P) = A * A = y^2 STA T \ Set (T P) = (A P) = y^2 LDA P \ Set (R Q) = (R Q) + (T P) = x^2 + y^2 ADC Q \ STA Q \ First, doing the low bytes, Q = Q + P LDA T \ And then the high bytes, R = R + T ADC R STA R LDA XX15+2 \ Fetch the z-coordinate into A JSR SQUA \ Set (A P) = A * A = z^2 STA T \ Set (T P) = (A P) = z^2 LDA P \ Set (R Q) = (R Q) + (T P) = x^2 + y^2 + z^2 ADC Q \ STA Q \ First, doing the low bytes, Q = Q + P LDA T \ And then the high bytes, R = R + T ADC R STA R JSR LL5 \ We now have the following: \ \ (R Q) = x^2 + y^2 + z^2 \ \ so we can call LL5 to use Pythagoras to get: \ \ Q = SQRT(R Q) \ = SQRT(x^2 + y^2 + z^2) \ \ So Q now contains the length of the vector (x, y, z), \ and we can normalise the vector by dividing each of \ the coordinates by this value, which we do by calling \ routine TIS2. TIS2 returns the divided figure, using \ 96 to represent 1 and 96 with bit 7 set for -1 LDA XX15 \ Call TIS2 to divide the x-coordinate in XX15 by Q, JSR TIS2 \ with 1 being represented by 96 STA XX15 LDA XX15+1 \ Call TIS2 to divide the y-coordinate in XX15+1 by Q, JSR TIS2 \ with 1 being represented by 96 STA XX15+1 LDA XX15+2 \ Call TIS2 to divide the z-coordinate in XX15+2 by Q, JSR TIS2 \ with 1 being represented by 96 STA XX15+2 .NO1 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: RDKEY \ Type: Subroutine \ Category: Keyboard \ Summary: Scan the keyboard for key presses by sending an OSWORD 240 command \ to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ This routine sends an OSWORD 240 command to the I/O processor to ask it to \ scan the keyboard, starting with internal key number 16 ("Q") and working \ through the set of internal key numbers (see page 142 of the "Advanced User \ Guide for the BBC Micro" by Bray, Dickens and Holmes for a list of internal \ key numbers). The results are copied from the I/O processor into the key \ logger buffer at KTRAN. \ \ This routine is effectively the same as OSBYTE 122, though the OSBYTE call \ preserves A, unlike this routine. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X If a key is being pressed, X contains the internal key \ number, otherwise it contains 0 \ \ A Contains the same as X \ \ ****************************************************************************** .RDKEY \ --- Mod: Code added for Delta 14B: ------------------> BIT titleScreen \ If this is not the title screen, jump to rdky1 to send BPL rdky1 \ the joystick configuration to the I/O processor STZ KTRAN \ This is the title screen, so zero the two joystick STZ KTRAN+1 \ configuration bytes that we send to the I/O processor \ so it ignores the joystick and Delta 14B, as otherwise \ buttons on the latter will change the configuration \ options, which might be confusing BRA rdky2 \ Jump to rdky2 to read the keyboard .rdky1 \ If we get here then this is not the title screen, so \ send the joystick configurations to the I/O processor \ so it reads the joystick and Delta 14B buttons when \ configured LDA JSTK \ Send the configuration of player 1's controls to the STA KTRAN \ I/O processor LDA player2JSTK \ Send the configuration of player 2's controls to the STA KTRAN+1 \ I/O processor .rdky2 \ --- End of added code -------------------------------> LDA #240 \ Set A in preparation for sending an OSWORD 240 command LDY #HI(buf) \ Set (Y X) to point to the parameter block at buf LDX #LO(buf) JSR OSWORD \ Send an OSWORD 240 command to the I/O processor to \ scan the keyboard and joysticks, and populate the key \ logger buffer in KTRAN, which is the part of the buf \ buffer just after the two OSWORD size bytes LDX KTRAN \ Set X to the first byte of the updated KTRAN, which \ contains the internal key number of the key being \ pressed, or 0 if there is no key press TXA \ Copy X into A RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: WARP \ Type: Subroutine \ Category: Flight \ Summary: Perform an in-system jump \ Deep dive: A sense of scale \ \ ------------------------------------------------------------------------------ \ \ This is called when we press "J" during flight. The following checks are \ performed: \ \ * Make sure we don't have any ships or space stations in the vicinity \ \ * Make sure we are not in witchspace \ \ * If we are facing the planet, make sure we aren't too close \ \ * If we are facing the sun, make sure we aren't too close \ \ If the above checks are passed, then we perform an in-system jump by moving \ the sun and planet in the opposite direction to travel, so we appear to jump \ in space. This means that any asteroids, cargo canisters or escape pods get \ dragged along for the ride. \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.WARP \ \LDX JUNK \ Set X to the total number of junk items in the \ \ vicinity (e.g. asteroids, escape pods, cargo \ \ canisters, Shuttles, Transporters and so on) \ \LDA FRIN+2,X \ If the slot at FRIN+2+X is non-zero, then we have \ \ something else in the vicinity besides asteroids, \ \ escape pods and cargo canisters, so to check whether \ \ we can jump, we first grab the slot contents into A \ \ORA SSPR \ If there is a space station nearby, then SSPR will \ \ be non-zero, so OR'ing with SSPR will produce a \ \ non-zero result if either A or SSPR are non-zero \ \ORA MJ \ If we are in witchspace, then MJ will be non-zero, so \ \ OR'ing with MJ will produce a non-zero result if \ \ either A or SSPR or MJ are non-zero \ \BNE WA1 \ A is non-zero if we have either a ship or a space \ \ station in the vicinity, or we are in witchspace, in \ \ which case jump to WA1 to make a low beep to show that \ \ we can't do an in-system jump \ \LDY K%+8 \ Otherwise we can do an in-system jump, so now we fetch \ \ the byte at K%+8, which contains the z_sign for the \ \ first ship slot, i.e. the distance of the planet \ \BMI WA3 \ If the planet's z_sign is negative, then the planet \ \ is behind us, so jump to WA3 to skip the following \ \TAY \ Set A = Y = 0 (as we didn't BNE above) so the call \ \ to MAS2 measures the distance to the planet \ \JSR MAS2 \ Call MAS2 to set A to the largest distance to the \ \ planet in any of the three axes (we could also call \ \ routine m to do the same thing, as A = 0) \ \CMP #2 \ If A < 2 then jump to WA1 to abort the in-system jump \BCC WA1 \ with a low beep, as we are facing the planet and are \ \ too close to jump in that direction \ \.WA3 \ \LDY K%+NI%+8 \ Fetch the z_sign (byte #8) of the second ship in the \ \ ship data workspace at K%, which is reserved for the \ \ sun or the space station (in this case it's the \ \ former, as we already confirmed there isn't a space \ \ station in the vicinity) \ \BMI WA2 \ If the sun's z_sign is negative, then the sun is \ \ behind us, so jump to WA2 to skip the following \ \LDY #NI% \ Set Y to point to the offset of the ship data block \ \ for the sun, which is NI% (as each block is NI% bytes \ \ long, and the sun is the second block) \ \JSR m \ Call m to set A to the largest distance to the sun \ \ in any of the three axes \ \CMP #2 \ If A < 2 then jump to WA1 to abort the in-system jump \BCC WA1 \ with a low beep, as we are facing the sun and are too \ \ close to jump in that direction \ \.WA2 \ \ \ If we get here, then we can do an in-system jump, as \ \ we don't have any ships or space stations in the \ \ vicinity, we are not in witchspace, and if we are \ \ facing the planet or the sun, we aren't too close to \ \ jump towards it \ \ \ \ We do an in-system jump by moving the sun and planet, \ \ rather than moving our own local bubble (this is why \ \ in-system jumps drag asteroids, cargo canisters and \ \ escape pods along for the ride). Specifically, we move \ \ them in the z-axis by a fixed amount in the opposite \ \ direction to travel, thus performing a jump towards \ \ our destination \ \LDA #&81 \ Set R = R = P = &81 \STA S \STA R \STA P \ \LDA K%+8 \ Set A = z_sign for the planet \ \JSR ADD \ Set (A X) = (A P) + (S R) \ \ = (z_sign &81) + &8181 \ \ = (z_sign &81) - &0181 \ \ \ \ This moves the planet against the direction of travel \ \ by reducing z_sign by 1, as the above maths is: \ \ \ \ z_sign 00000000 \ \ + 00000000 10000001 \ \ - 00000001 10000001 \ \ \ \ or: \ \ \ \ z_sign 00000000 \ \ + 00000000 00000000 \ \ - 00000001 00000000 \ \ \ \ i.e. the high byte is z_sign - 1, making sure the sign \ \ is preserved \ \STA K%+8 \ Set the planet's z_sign to the high byte of the result \ \LDA K%+NI%+8 \ Set A = z_sign for the sun \ \JSR ADD \ Set (A X) = (A P) + (S R) \ \ = (z_sign &81) + &8181 \ \ = (z_sign &81) - &0181 \ \ \ \ which moves the sun against the direction of travel \ \ by reducing z_sign by 1 \ \STA K%+NI%+8 \ Set the planet's z_sign to the high byte of the result \ \LDA #1 \ Temporarily set the view type to a non-zero value, so \STA QQ11 \ the call to LOOK1 below clears the screen before \ \ switching to the space view \ \STA MCNT \ Set the main loop counter to 1, so the next iteration \ \ through the main loop will potentially spawn ships \ \ (see part 2 of the main game loop at me3) \ \LSR A \ Set EV, the extra vessels spawning counter, to 0 \STA EV \ (the LSR produces a 0 as A was previously 1) \ \LDX VIEW \ Set X to the current view (front, rear, left or right) \JMP LOOK1 \ and jump to LOOK1 to initialise that view, returning \ \ from the subroutine using a tail call \ \.WA1 \ \LDA #40 \ If we get here then we can't do an in-system jump, so \BNE NOISE \ call the NOISE routine with A = 40 to make a long, low \ \ beep and return from the subroutine using a tail call \ \ (the BNE is effectively a JMP as A is never zero) \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Player2ECMOF \ Type: Subroutine \ Category: Dashboard \ Summary: Switch off the E.C.M. and turn off the dashboard bulb \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Player2ECMOF LDA #0 \ Set ECMA and ECMP to 0 to indicate that no E.C.M. is STA player2ECMA \ currently running STA player2ECMP JSR SPBLB \ Update the E.C.M. indicator bulb on the dashboard LDA ECMA \ If player 1's E.C.M. is active, leave the sound BEQ ecmb1 \ running RTS .ecmb1 LDA #72 \ Call the NOISE routine with A = 72 to make the sound BNE NOISE \ of the E.C.M. being turned off and return from the \ subroutine using a tail call (this BNE is effectively \ a JMP as A will never be zero) \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: ECMOF \ Type: Subroutine \ Category: Dashboard \ Summary: Switch off the E.C.M. and turn off the dashboard bulb \ \ ****************************************************************************** .ECMOF LDA #0 \ Set ECMA and ECMP to 0 to indicate that no E.C.M. is STA ECMA \ currently running STA ECMP JSR ECBLB \ Update the E.C.M. indicator bulb on the dashboard \ --- Mod: Code added for two-player Elite: -----------> LDA player2ECMA \ If player 2's E.C.M. is active, leave the sound BEQ ecma1 \ running RTS .ecma1 \ --- End of added code -------------------------------> LDA #72 \ Call the NOISE routine with A = 72 to make the sound BNE NOISE \ of the E.C.M. being turned off and return from the \ subroutine using a tail call (this BNE is effectively \ a JMP as A will never be zero) \ ****************************************************************************** \ \ Name: EXNO3 \ Type: Subroutine \ Category: Sound \ Summary: Make an explosion sound \ \ ------------------------------------------------------------------------------ \ \ Make the sound of death in the cold, hard vacuum of space. Apparently, in \ Elite space, everyone can hear you scream. \ \ This routine also makes the sound of a destroyed cargo canister if we don't \ get scooping right, the sound of us colliding with another ship, and the sound \ of us being hit with depleted shields. It is not a good sound to hear. \ \ ****************************************************************************** .EXNO3 LDA #16 \ Call the NOISE routine with A = 16 to make the first JSR NOISE \ death sound LDA #24 \ Call the NOISE routine with A = 24 to make the second BNE NOISE \ death sound and return from the subroutine using a \ tail call (this BNE is effectively a JMP as A will \ never be zero) \ ****************************************************************************** \ \ Name: BEEP \ Type: Subroutine \ Category: Sound \ Summary: Make a short, high beep \ \ ****************************************************************************** .BEEP LDA #32 \ Call the NOISE routine with A = 32 to make a short, BNE NOISE \ high beep, returning from the subroutine using a tail \ call (this BNE is effectively a JMP as A will never be \ zero) \ ****************************************************************************** \ \ Name: SFRMIS \ Type: Subroutine \ Category: Tactics \ Summary: Add an enemy missile to our local bubble of universe \ \ ------------------------------------------------------------------------------ \ \ An enemy has fired a missile, so add the missile to our universe if there is \ room, and if there is, make the appropriate warnings and noises. \ \ ****************************************************************************** .SFRMIS \ --- Mod: Code added for two-player Elite: -----------> LDX player2Missile \ If player 2 does not already have a missile in-flight, BEQ frmi1 \ then player2Missile will be zero, so jump to frmi1 to \ keep going JMP Player2FR1 \ Otherwise call Player2FR1 to display "missile jammed" \ as player 2 can't fire a second missile, returning \ from the subroutine using a tail call .frmi1 \ --- End of added code -------------------------------> LDX #MSL \ Set X to the ship type of a missile, and call SFS1-2 JSR SFS1-2 \ to add a missile to our universe that has AI (bit 7 \ set), is hostile (bit 6 set) and has been launched \ (bit 0 clear); the target slot number is set to 31, \ but this is ignored as the hostile flags means we \ are the target \ --- Mod: Code added for two-player Elite: -----------> .frmi2 \ --- End of added code -------------------------------> BCC KYTB \ The C flag will be set if the call to SFS1-2 was a \ success, so if it's clear, jump to KYTB to return from \ the subroutine (as KYTB contains an RTS) \ --- Mod: Code added for two-player Elite: -----------> LDA newShipSlot \ Set player2Missile to the slot number of the new STA player2Missile \ missile, to record the fact that player 2 has fired a \ missile LDY #0 \ We have just launched a missile, so we need to remove JSR Player2ABORT \ missile lock and hide the leftmost indicator on the \ dashboard by setting it to black (Y = 0) DEC player2NOMSL \ Reduce the number of missiles we have by 1 \ --- End of added code -------------------------------> IF _EXECUTIVE LDX #1 \ Call TALK with X = 1 to say "Incoming missile" using JSR TALK \ the Watford Electronics Beeb Speech Synthesiser (if \ one is fitted and speech has been enabled) ENDIF LDA #120 \ Print recursive token 120 ("INCOMING MISSILE") as an JSR MESS \ in-flight message LDA #48 \ Call the NOISE routine with A = 48 to make the sound BNE NOISE \ of the missile being launched and return from the \ subroutine using a tail call (this BNE is effectively \ a JMP as A will never be zero) \ ****************************************************************************** \ \ Name: EXNO2 \ Type: Subroutine \ Category: Status \ Summary: Process us making a kill \ Deep dive: Combat rank \ \ ------------------------------------------------------------------------------ \ \ We have killed a ship, so increase the kill tally, displaying an iconic \ message of encouragement if the kill total is a multiple of 256, and then \ make a nearby explosion sound. \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.EXNO2 \ \INC TALLY \ Increment the low byte of the kill count in TALLY \ \BNE EXNO-2 \ If there is no carry, jump to the LDX #7 below (at \ \ EXNO-2) \ \INC TALLY+1 \ Increment the high byte of the kill count in TALLY \ \LDA #101 \ The kill total is a multiple of 256, so it's time \JSR MESS \ for a pat on the back, so print recursive token 101 \ \ ("RIGHT ON COMMANDER!") as an in-flight message \ \LDX #7 \ Set X = 7 and fall through into EXNO to make the \ \ sound of a ship exploding \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: EXNO \ Type: Subroutine \ Category: Sound \ Summary: Make the sound of a laser strike on another ship or a ship \ explosion \ \ ------------------------------------------------------------------------------ \ \ Make the two-part explosion sound of us making a laser strike, or of another \ ship exploding. \ \ The volume of the first explosion is affected by the distance of the ship \ being hit, with more distant ships being quieter. The value in X also affects \ the volume of the first explosion, with a higher X giving a quieter sound \ (so X can be used to differentiate a laser strike from an explosion). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The larger the value of X, the fainter the explosion. \ Allowed values are: \ \ * 7 = explosion is louder (i.e. the ship has just \ exploded) \ \ * 15 = explosion is quieter (i.e. this is just a laser \ strike) \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ EXNO-2 Set X = 7 and fall through into EXNO to make the sound \ of a ship exploding \ \ ****************************************************************************** .EXNO STX T \ Store the distance in T LDA #24 \ Set A = 24 to denote the sound of us making a hit or JSR NOS1 \ kill (part 1 of the explosion), and call NOS1 to set \ up the sound block in XX16 LDA INWK+8 \ Fetch z_sign, the distance of the ship being hit in ASL A \ terms of the z-axis (in and out of the screen), and \ shift it left by 1 to get rid of the sign bit BEQ P%+5 \ If the result is 0, skip the next two instructions \ so that we load A with z_hi below LDA #0 \ Set A = 0 EQUB &2C \ Skip the next instruction by turning it into \ &2C &A5 &4C, or BIT &4CA5, which does nothing apart \ from affect the flags \ So, by this point, if any of bits 0-6 of z_sign are \ non-zero, which means the ship is a long way away, \ then A will be set to 0 rather than z_hi and the next \ instruction gets skipped, so we end up with a volume \ of 0. This fixes a bug in the other versions which \ ignore the value of z_sign when calculating explosion \ volume, which means very distant ships can still be \ heard LDA INWK+7 \ Fetch z_hi, the distance of the ship being hit in LSR A \ terms of the z-axis (in and out of the screen), and LSR A \ divide by 4. If z_hi has either bit 6 or 7 set then \ that ship is too far away to be shown on the scanner \ (as per the SCAN routine), so we know the maximum \ z_hi at this point is %00111111, and shifting z_hi \ to the right twice gives us a maximum value of \ %00001111 AND T \ This reduces A to a maximum of X; X can be either \ 7 = %0111 or 15 = %1111, so AND'ing with 15 will \ not affect A, while AND'ing with 7 will clear bit \ 3, reducing the maximum value in A to 7 ORA #%11110001 \ The SOUND statement's amplitude ranges from 0 (for no \ sound) to -15 (full volume), so we can set bits 0 and \ 4-7 in A, and keep bits 1-3 from the above to get \ a value between -15 (%11110001) and -1 (%11111111), \ with lower values of z_hi and argument X leading \ to a more negative, or quieter number (so the closer \ the ship, i.e. the smaller the value of X, the louder \ the sound) STA XX16+2 \ The amplitude byte of the sound block in XX16 is in \ byte #3 (where it's the low byte of the amplitude), so \ this sets the amplitude to the value in A JSR NO3 \ Make the sound from our updated sound block in XX16 LDA #16 \ Set A = 16 to denote we have made a hit or kill \ (part 2 of the explosion), and fall through into NOISE \ to make the sound \ ****************************************************************************** \ \ Name: NOISE \ Type: Subroutine \ Category: Sound \ Summary: Make the sound whose number is in A \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The number of the sound to be made. See the \ documentation for variable SFX for a list of sound \ numbers \ \ ****************************************************************************** .NOISE JSR NOS1 \ Set up the sound block in XX16 for the sound in A and \ fall through into NO3 to make the sound \ ****************************************************************************** \ \ Name: NO3 \ Type: Subroutine \ Category: Sound \ Summary: Make a sound from a prepared sound block \ \ ------------------------------------------------------------------------------ \ \ Make a sound from a prepared sound block in XX16 (if sound is enabled). See \ routine NOS1 for details of preparing the XX16 sound block. \ \ ****************************************************************************** .NO3 LDX DNOIZ \ Set X to the DNOIZ configuration setting BNE KYTB \ If DNOIZ is non-zero, then sound is disabled, so \ return from the subroutine (as KYTB contains an RTS) LDX #LO(XX16) \ Otherwise set (Y X) to point to the sound block in LDY #HI(XX16) \ XX16 LDA #7 \ Call OSWORD 7 to makes the sound, as described in the JMP OSWORD \ documentation for variable SFX, and return from the \ subroutine using a tail call \ ****************************************************************************** \ \ Name: NOS1 \ Type: Subroutine \ Category: Sound \ Summary: Prepare a sound block \ \ ------------------------------------------------------------------------------ \ \ Copy four sound bytes from SFX into XX16, interspersing them with null bytes, \ with Y indicating the sound number to copy (from the values in the sound \ table at SFX). So, for example, if we call this routine with A = 40 (long, \ low beep), the following bytes will be set in XX16 to XX16+7: \ \ &13 &00 &F4 &00 &0C &00 &08 &00 \ \ This block will be passed to OSWORD 7 to make the sound, which expects the \ four sound attributes as 16-bit big-endian values - in other words, with the \ low byte first. So the above block would pass the values &0013, &00F4, &000C \ and &0008 to the SOUND statement when used with OSWORD 7, or: \ \ SOUND &13, &F4, &0C, &08 \ \ as the high bytes are always zero. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The sound number to copy from SFX to XX16, which is \ always a multiple of 8 \ \ ****************************************************************************** .NOS1 LSR A \ Divide A by 2, and also clear the C flag, as bit 0 of \ A is always zero (as A is a multiple of 8) ADC #3 \ Set Y = A + 3, so Y now points to the last byte of TAY \ four within the block of four-byte values LDX #7 \ We want to copy four bytes, spread out into an \ eight-byte block, so set a counter in Y to cover eight \ bytes .NOL1 LDA #0 \ Set the X-th byte of XX16 to 0 STA XX16,X DEX \ Decrement the destination byte pointer LDA SFX,Y \ Set the X-th byte of XX16 to the value from SFX+Y STA XX16,X DEY \ Decrement the source byte pointer again DEX \ Decrement the destination byte pointer again BPL NOL1 \ Loop back for the next source byte \ Fall through into KYTB to return from the subroutine, \ as the first byte of KYTB is an RTS \ ****************************************************************************** \ \ Name: KYTB \ Type: Variable \ Category: Keyboard \ Summary: Lookup table for in-flight keyboard controls \ Deep dive: The key logger \ \ ------------------------------------------------------------------------------ \ \ Keyboard table for in-flight controls. This table contains the internal key \ codes for the flight keys (see page 142 of the "Advanced User Guide for the \ BBC Micro" by Bray, Dickens and Holmes for a list of internal key numbers). \ \ The pitch, roll, speed and laser keys (i.e. the seven primary flight \ control keys) have bit 7 set, so they have 128 added to their internal \ values. This doesn't appear to be used anywhere. \ \ Note that KYTB actually points to the byte before the start of the table, so \ the offset of the first key value is 1 (i.e. KYTB+1), not 0. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ KYTB Contains an RTS \ \ ****************************************************************************** .KYTB RTS \ Return from the subroutine (used as an entry point and \ a fall-through from above) \ These are the primary flight controls (pitch, roll, \ speed and lasers): EQUB &68 + 128 \ ? KYTB+1 Slow down EQUB &62 + 128 \ Space KYTB+2 Speed up EQUB &66 + 128 \ < KYTB+3 Roll left EQUB &67 + 128 \ > KYTB+4 Roll right EQUB &42 + 128 \ X KYTB+5 Pull up EQUB &51 + 128 \ S KYTB+6 Pitch down EQUB &41 + 128 \ A KYTB+7 Fire lasers \ These are the secondary flight controls: \ --- Mod: Code removed for two-player Elite: ---------> \EQUB &60 \ TAB KYTB+8 Energy bomb \EQUB &70 \ ESCAPE KYTB+9 Launch escape pod \EQUB &23 \ T KYTB+10 Arm missile \EQUB &35 \ U KYTB+11 Unarm missile \EQUB &65 \ M KYTB+12 Fire missile \EQUB &22 \ E KYTB+13 E.C.M. \EQUB &45 \ J KYTB+14 In-system jump \EQUB &52 \ C KYTB+15 Docking computer \ \EQUB &37 \ P KYTB+16 Cancel docking computer \ --- And replaced by: --------------------------------> EQUB &38 \ [ KYTB+8 Player 2 arm missile EQUB &47 \ @ KYTB+9 Player 2 fire missile EQUB &23 \ T KYTB+10 Arm missile EQUB &35 \ U KYTB+11 Unarm missile EQUB &65 \ M KYTB+12 Fire missile EQUB &22 \ E KYTB+13 E.C.M. EQUB &78 \ \ KYTB+14 Player 2 E.C.M. EQUB &28 \ _ KYTB+15 Player 2 unarm missile EQUB &49 \ RETURN KYTB+16 Player 2 speed up EQUB &58 \ ] KYTB+17 Player 2 slow down \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: CTRL \ Type: Subroutine \ Category: Keyboard \ Summary: Scan the keyboard to see if CTRL is currently pressed \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X X = %10000001 (i.e. 129 or -127) if CTRL is being \ pressed \ \ X = 1 if CTRL is not being pressed \ \ A Contains the same as X \ \ ****************************************************************************** .CTRL LDX #1 \ Set X to the internal key number for CTRL and fall \ through into DKS4 to scan the keyboard \ ****************************************************************************** \ \ Name: DKS4 \ Type: Subroutine \ Category: Keyboard \ Summary: Scan for a particular key press by sending a #DODKS4 command to \ the I/O processor \ \ ------------------------------------------------------------------------------ \ \ This routine sends a #DODKS4 command to the I/O processor to ask it to scan \ the keyboard, to see if the key specified in X is currently being pressed. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The internal number of the key to check \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A If the key is being pressed, A contains the original \ key number in X but with bit 7 set (i.e. key number + \ 128). If the key is not being pressed, A contains the \ unchanged key number \ \ ****************************************************************************** .DKS4 STX DKS4pars+2 \ Store the key number in byte #2 of the parameter block \ below LDX #LO(DKS4pars) \ Set (Y X) to point to the parameter block below LDY #HI(DKS4pars) LDA #DODKS4 \ Send a #DODKS4 command to the I/O processor to check JSR OSWORD \ whether the key in byte #2 of the parameter block is \ being pressed LDA DKS4pars+2 \ Fetch the result from byte #2 of the parameter block, \ which will have bit 7 set if the key is being pressed RTS \ Return from the subroutine .DKS4pars EQUB 3 \ The number of bytes to transmit with this command EQUB 3 \ The number of bytes to receive with this command EQUB 0 \ The key number to check RTS \ End of the parameter block \ ****************************************************************************** \ \ Name: DKS2 \ Type: Subroutine \ Category: Keyboard \ Summary: Read the joystick position \ \ ------------------------------------------------------------------------------ \ \ Return the value of ADC channel in X (used to read the joystick). The value \ will be inverted if the game has been configured to reverse both joystick \ channels (which can be done by pausing the game and pressing J). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The ADC channel to read: \ \ * 1 = joystick X \ \ * 2 = joystick Y \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ (A X) The 16-bit value read from channel X, with the value \ inverted if the game has been configured to reverse the \ joystick \ \ ****************************************************************************** .DKS2 LDA KTRAN+7,X \ Fetch either the joystick X value or joystick Y value \ from the key logger buffer, depending on the value of \ X (i.e. fetch either KTRAN+8 or KTRAN+0) EOR JSTE \ The high byte A is now EOR'd with the value in \ location JSTE, which contains &FF if both joystick \ channels are reversed and 0 otherwise (so A now \ contains the high byte but inverted, if that's what \ the current settings say) RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: DKS3 \ Type: Subroutine \ Category: Keyboard \ Summary: Toggle a configuration setting and emit a beep \ \ ------------------------------------------------------------------------------ \ \ This is called when the game is paused and a key is pressed that changes the \ game's configuration. \ \ Specifically, this routine toggles the configuration settings for the \ following keys: \ \ * CAPS LOCK toggles keyboard flight damping (&40) \ * A toggles keyboard auto-recentre (&41) \ * X toggles author names on start-up screen (&42) \ * F toggles flashing console bars (&43) \ * Y toggles reverse joystick Y channel (&44) \ * J toggles reverse both joystick channels (&45) \ * K toggles keyboard and joystick (&46) \ \ The Executive version supports two additional configuration options: \ \ * @ toggles infinite jump range and fuel (&47) \ * : toggles speech (&48) \ \ The numbers in brackets are the internal key numbers (see page 142 of the \ "Advanced User Guide for the BBC Micro" by Bray, Dickens and Holmes for a list \ of internal key numbers). We pass the key that has been pressed in X, and the \ configuration option to check it against in Y, so this routine is typically \ called in a loop that loops through the various configuration options. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The internal number of the key that's been pressed \ \ Y The internal number of the configuration key to check \ against, from the list above (i.e. Y must be from &40 to \ &46) \ \ ****************************************************************************** .DKS3 STY T \ Store the configuration key argument in T CPX T \ If X <> Y, jump to Dk3 to return from the subroutine BNE Dk3 \ We have a match between X and Y, so now to toggle \ the relevant configuration byte. CAPS LOCK has a key \ value of &40 and has its configuration byte at \ location DAMP, A has a value of &41 and has its byte \ at location DJD, which is DAMP+1, and so on. So we \ can toggle the configuration byte by changing the \ byte at DAMP + (X - &40), or to put it in indexing \ terms, DAMP-&40,X. It's no coincidence that the \ game's configuration bytes are set up in this order \ and with these keys (and this is also why the sound \ on/off keys are dealt with elsewhere, as the internal \ key for S and Q are &51 and &10, which don't fit \ nicely into this approach) LDA DAMP-&40,X \ Fetch the byte from DAMP + (X - &40), invert it and EOR #&FF \ put it back (0 means no and &FF means yes in the STA DAMP-&40,X \ configuration bytes, so this toggles the setting) JSR BELL \ Make a beep sound so we know something has happened JSR DELAY \ Wait for Y/50 seconds (Y is between 64 and 70, so this \ is always a bit longer than a second) LDY T \ Restore the configuration key argument into Y .Dk3 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: DKJ1 \ Type: Subroutine \ Category: Keyboard \ Summary: Read joystick and flight controls \ \ ------------------------------------------------------------------------------ \ \ Specifically, scan the keyboard for the speed up and slow down keys, and read \ the joystick's fire button and X and Y axes, storing the results in the key \ logger and the joystick position variables. \ \ This routine is only called if joysticks are enabled (JSTK = non-zero). \ \ ****************************************************************************** .DKJ1 \ --- Mod: Code removed for two-player Elite: ---------> \LDA auto \ If auto is non-zero, then the docking computer is \BNE auton \ currently activated, so jump to auton in DOKEY so the \ \ docking computer can "press" the flight keys for us \ --- End of removed code -----------------------------> LDA KTRAN+1 \ Copy the key press state for the "?" key from the STA KL+1 \ key logger buffer to the key logger LDA KTRAN+2 \ Copy the key press state for the Space key from the STA KL+2 \ key logger buffer to the key logger .BS1 LDA KTRAN+12 \ Fetch the key press state for the joystick 1 fire \ button from the key logger buffer, which contains \ the value of the 6522 System VIA input register IRB \ (SHEILA &40) TAX \ This instruction doesn't seem to have any effect, as \ X is overwritten in a few instructions AND #%00010000 \ Bit 4 of IRB (PB4) is clear if joystick 1's fire \ button is pressed, otherwise it is set, so AND'ing \ the value of IRB with %10000 extracts this bit EOR #%00010000 \ Flip bit 4 so that it's set if the fire button has STA KY7 \ been pressed, and store the result in the keyboard \ logger at location KY7, which is also where the A key \ (fire lasers) key is logged LDX #1 \ Call DKS2 to fetch the value of ADC channel 1 (the JSR DKS2 \ joystick X value) into (A X), and OR A with 1. This ORA #1 \ ensures that the high byte is at least 1, and then we STA JSTX \ store the result in JSTX LDX #2 \ Call DKS2 to fetch the value of ADC channel 2 (the JSR DKS2 \ joystick Y value) into (A X), and EOR A with JSTGY. EOR JSTGY \ JSTGY will be &FF if the game is configured to STA JSTY \ reverse the joystick Y channel, so this EOR does \ exactly that, and then we store the result in JSTY \ --- Mod: Code added for two-player Elite: -----------> \ If we get here then player 1 is configured for the \ joystick (as we only call DKJ1 when JSTK is non-zero) LDA player2JSTK \ If player2JSTK is zero, then player 2 is the AI Pilot, BEQ dokj1 \ so jump to doke1 to skip reading joystick 2 \ If we get here then player 1 is configured for the \ joystick and player 2 is configured for the joystick, \ so we now read the joystick 2 position into player 2's \ KY22 (fire), player2JSTX and player2JSTY variables LDA KTRAN+12 \ Fetch the key press state for the joystick 1 fire \ button from the key logger buffer, which contains \ the value of the 6522 System VIA input register IRB \ (SHEILA &40) TAX \ This instruction doesn't seem to have any effect, as \ X is overwritten in a few instructions AND #%00100000 \ Bit 5 of IRB (PB5) is clear if joystick 2's fire \ button is pressed, otherwise it is set, so AND'ing \ the value of IRB with %100000 extracts this bit EOR #%00100000 \ Flip bit 5 so that it's set if the fire button has STA KY22 \ been pressed, and store the result in the keyboard \ logger at location KY22 LDX #3 \ Call DKS2 to fetch the value of ADC channel 3 (the JSR DKS2 \ joystick 2 X value) into (A X), and OR A with 1. This ORA #1 \ ensures that the high byte is at least 1, and then we STA player2JSTX \ store the result in player2JSTX LDX #4 \ Call DKS2 to fetch the value of ADC channel 4 (the JSR DKS2 \ joystick 2 Y value) into (A X), and EOR A with JSTGY. EOR JSTGY \ JSTGY will be &FF if the game is configured to STA player2JSTY \ reverse the joystick Y channel, so this EOR does \ exactly that, and then we store the result in \ player2JSTY .dokj1 \ --- End of added code -------------------------------> JMP DK4 \ We are done scanning the joystick flight controls, \ so jump to DK4 to scan for other keys, using a tail \ call so we can return from the subroutine there \ ****************************************************************************** \ \ Name: U% \ Type: Subroutine \ Category: Keyboard \ Summary: Clear the key logger \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A A is set to 0 \ \ Y Y is set to 0 \ \ ****************************************************************************** .U% LDA #0 \ Set A to 0, as this means "key not pressed" in the \ key logger at KL \ --- Mod: Code removed for two-player Elite: ---------> \LDY #16 \ We want to clear the 16 key logger locations from \ \ KY1 to KY20, so set a counter in Y \ --- And replaced by: --------------------------------> LDY #18 \ We want to clear the 18 key logger locations from \ KY1 to KY22, so set a counter in Y \ --- End of replacement ------------------------------> .DKL3 STA KL,Y \ Store 0 in the Y-th byte of the key logger DEY \ Decrement the counter BNE DKL3 \ And loop back for the next key, until we have just \ cleared KL+1. We don't want to clear the first key \ logger location at KL, as the keyboard table at KYTB \ starts with offset 1, not 0, so KL is not technically \ part of the key logger (it's actually used for logging \ keys that don't appear in the keyboard table, and \ which therefore don't use the key logger) RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: DOKEY \ Type: Subroutine \ Category: Keyboard \ Summary: Scan for the seven primary flight controls and apply the docking \ computer manoeuvring code \ Deep dive: The key logger \ The docking computer \ \ ------------------------------------------------------------------------------ \ \ Scan for the seven primary flight controls (or the equivalent on joystick), \ pause and configuration keys, and secondary flight controls, and update the \ key logger accordingly. Specifically: \ \ * If we are on keyboard configuration, clear the key logger and update it \ for the seven primary flight controls, and update the pitch and roll \ rates accordingly. \ \ * If we are on joystick configuration, clear the key logger and jump to \ DKJ1, which reads the joystick equivalents of the primary flight \ controls. \ \ Both options end up at DK4 to scan for other keys, beyond the seven primary \ flight controls. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ auton Get the docking computer to "press" the flight keys to \ dock the ship \ \ ****************************************************************************** .DOKEY LDA NEEDKEY \ If NEEDKEY is zero, skip the next instruction BEQ P%+5 JSR RDKEY \ NEEDKEY is non-zero, so call RDKEY to ask the I/O \ processor to scan the keyboard for key presses and \ update the key logger buffer at KTRAN LDA #&FF \ Set NEEDKEY to &FF, so the next call to DOKEY updates STA NEEDKEY \ the key logger buffer JSR U% \ Call U% to clear the key logger \ --- Mod: Code added for two-player Elite: -----------> LDA KTRAN+13 \ Store player 2's speed controls in the key logger, so STA KY21 \ they get updated as primary controls even if joysticks LDA KTRAN+14 \ are enabled for player 1 STA KY20 \ --- End of added code -------------------------------> LDA JSTK \ If JSTK is non-zero, then we are configured to use BNE DKJ1 \ the joystick rather than keyboard, so jump to DKJ1 \ to read the joystick flight controls, before jumping \ to DK4 to scan for pause, configuration and secondary \ flight keys STA BSTK \ Set BSTK = 0 to disable the Bitstik \ --- Mod: Code added for two-player Elite: -----------> \ If we get here then player 1 is configured for the \ keyboard LDA player2JSTK \ If player2JSTK is zero, then player 2 is the AI Pilot, BEQ doke1 \ so jump to doke1 to skip reading joystick 1 \ If we get here then player 1 is configured for the \ keyboard and player 2 is configured for the joystick, \ so we now read the joystick 1 position into player 2's \ KY22 (fire), player2JSTX and player2JSTY variables LDA KTRAN+12 \ Fetch the key press state for the joystick 1 fire \ button from the key logger buffer, which contains \ the value of the 6522 System VIA input register IRB \ (SHEILA &40) TAX \ This instruction doesn't seem to have any effect, as \ X is overwritten in a few instructions AND #%00010000 \ Bit 4 of IRB (PB4) is clear if joystick 1's fire \ button is pressed, otherwise it is set, so AND'ing \ the value of IRB with %10000 extracts this bit EOR #%00010000 \ Flip bit 4 so that it's set if the fire button has STA KY22 \ been pressed, and store the result in the keyboard \ logger at location KY22 LDX #1 \ Call DKS2 to fetch the value of ADC channel 1 (the JSR DKS2 \ joystick 1 X value) into (A X), and OR A with 1. This ORA #1 \ ensures that the high byte is at least 1, and then we STA player2JSTX \ store the result in player2JSTX LDX #2 \ Call DKS2 to fetch the value of ADC channel 2 (the JSR DKS2 \ joystick 1 Y value) into (A X), and EOR A with JSTGY. EOR JSTGY \ JSTGY will be &FF if the game is configured to STA player2JSTY \ reverse the joystick Y channel, so this EOR does \ exactly that, and then we store the result in \ player2JSTY .doke1 \ --- End of added code -------------------------------> LDX #7 \ We're now going to copy key press data for the primary \ flight keys from the key logger buffer at KTRAN to the \ key logger at KL, so set a loop counter in X so we can \ count down from KTRAN + 7 to KTRAN + 1 .DKL2 LDA KTRAN,X \ Copy the X-th byte of KTRAN to the X-th byte of KL STA KL,X DEX \ Decrement the loop counter BNE DKL2 \ Loop back until we have copied all seven primary \ flight control key presses to KL \ --- Mod: Code removed for two-player Elite: ---------> \LDA auto \ If auto is 0, then the docking computer is not \BEQ DK15 \ currently activated, so jump to DK15 to skip the \ \ docking computer manoeuvring code below \ \.auton \ \JSR ZINF \ Call ZINF to reset the INWK ship workspace \ \LDA #96 \ Set nosev_z_hi = 96 \STA INWK+14 \ \ORA #%10000000 \ Set sidev_x_hi = -96 \STA INWK+22 \ \STA TYPE \ Set the ship type to -96, so the negative value will \ \ let us check in the DOCKIT routine whether this is our \ \ ship that is activating its docking computer, rather \ \ than an NPC ship docking \ \LDA DELTA \ Set the ship speed to DELTA (our speed) \STA INWK+27 \ \JSR DOCKIT \ Call DOCKIT to calculate the docking computer's moves \ \ and update INWK with the results \ \ \ We now "press" the relevant flight keys, depending on \ \ the results from DOCKIT, starting with the pitch keys \ \LDA INWK+27 \ Fetch the updated ship speed from byte #27 into A \ \CMP #22 \ If A < 22, skip the next instruction \BCC P%+4 \ \LDA #22 \ Set A = 22, so the maximum speed during docking is 22 \ \STA DELTA \ Update DELTA to the new value in A \ \LDA #&FF \ Set A = &FF, which we can insert into the key logger \ \ to "fake" the docking computer working the keyboard \ \LDX #0 \ Set X = 0, so we "press" KY1 below ("?", slow down) \ \LDY INWK+28 \ If the updated acceleration in byte #28 is zero, skip \BEQ DK11 \ to DK11 \ \BMI P%+3 \ If the updated acceleration is negative, skip the \ \ following instruction \ \INX \ The updated acceleration is positive, so increment X \ \ to 1, so we "press" KY2 below (Space, speed up) \ \STA KY1,X \ Store &FF in either KY1 or KY2 to "press" the relevant \ \ key, depending on whether the updated acceleration is \ \ negative (in which case we "press" KY1, "?", to slow \ \ down) or positive (in which case we "press" KY2, \ \ Space, to speed up) \ \.DK11 \ \ \ We now "press" the relevant roll keys, depending on \ \ the results from DOCKIT \ \LDA #128 \ Set A = 128, which indicates no change in roll when \ \ stored in JSTX (i.e. the centre of the roll indicator) \ \LDX #0 \ Set X = 0, so we "press" KY3 below ("<", increase \ \ roll) \ \ASL INWK+29 \ Shift ship byte #29 left, which shifts bit 7 of the \ \ updated roll counter (i.e. the roll direction) into \ \ the C flag \ \BEQ DK12 \ If the remains of byte #29 is zero, then the updated \ \ roll counter is zero, so jump to DK12 set JSTX to 128, \ \ to indicate there's no change in the roll \ \BCC P%+3 \ If the C flag is clear, skip the following instruction \ \INX \ The C flag is set, i.e. the direction of the updated \ \ roll counter is negative, so increment X to 1 so we \ \ "press" KY4 below (">", decrease roll) \ \BIT INWK+29 \ We shifted the updated roll counter to the left above, \BPL DK14 \ so this tests bit 6 of the original value, and if it \ \ is clear (i.e. the magnitude is less than 64), jump to \ \ DK14 to "press" the key and leave JSTX unchanged \ \LDA #64 \ The magnitude of the updated roll is 64 or more, so \STA JSTX \ set JSTX to 64 (so the roll decreases at half the \ \ maximum rate) \ \LDA #0 \ And set A = 0 so we do not "press" any keys (so if the \ \ docking computer needs to make a serious roll, it does \ \ so by setting JSTX directly rather than by "pressing" \ \ a key) \ \.DK14 \ \STA KY3,X \ Store A in either KY3 or KY4, depending on whether \ \ the updated roll rate is increasing (KY3) or \ \ decreasing (KY4) \ \LDA JSTX \ Fetch A from JSTX so the next instruction has no \ \ effect \ \.DK12 \ \STA JSTX \ Store A in JSTX to update the current roll rate \ \ \ We now "press" the relevant pitch keys, depending on \ \ the results from DOCKIT \ \LDA #128 \ Set A = 128, which indicates no change in pitch when \ \ stored in JSTX (i.e. the centre of the pitch \ \ indicator) \ \LDX #0 \ Set X = 0, so we "press" KY5 below ("X", decrease \ \ pitch, pulling the nose up) \ \ASL INWK+30 \ Shift ship byte #30 left, which shifts bit 7 of the \ \ updated pitch counter (i.e. the pitch direction) into \ \ the C flag \ \BEQ DK13 \ If the remains of byte #30 is zero, then the updated \ \ pitch counter is zero, so jump to DK13 set JSTY to \ \ 128, to indicate there's no change in the pitch \ \BCS P%+3 \ If the C flag is set, skip the following instruction \ \INX \ The C flag is clear, i.e. the direction of the updated \ \ pitch counter is positive (dive), so increment X to 1 \ \ so we "press" KY6 below ("S", increase pitch, so the \ \ nose dives) \ \STA KY5,X \ Store 128 in either KY5 or KY6 to "press" the relevant \ \ key, depending on whether the pitch direction is \ \ negative (in which case we "press" KY5, "X", to \ \ decrease the pitch, pulling the nose up) or positive \ \ (in which case we "press" KY6, "S", to increase the \ \ pitch, pushing the nose down) \ \LDA JSTY \ Fetch A from JSTY so the next instruction has no \ \ effect \ \.DK13 \ \STA JSTY \ Store A in JSTY to update the current pitch rate \ \.DK15 \ --- End of removed code -----------------------------> LDX JSTX \ Set X = JSTX, the current roll rate (as shown in the \ RL indicator on the dashboard) LDA #7 \ Set A to 7, which is the amount we want to alter the \ roll rate by if the roll keys are being pressed LDY KL+3 \ If the "<" key is being pressed, then call the BUMP2 BEQ P%+5 \ routine to increase the roll rate in X by A JSR BUMP2 LDY KL+4 \ If the ">" key is being pressed, then call the REDU2 BEQ P%+5 \ routine to decrease the roll rate in X by A, taking JSR REDU2 \ the keyboard auto re-centre setting into account STX JSTX \ Store the updated roll rate in JSTX ASL A \ Double the value of A, to 14 LDX JSTY \ Set X = JSTY, the current pitch rate (as shown in the \ DC indicator on the dashboard) LDY KL+5 \ If the "X" key is being pressed, then call the REDU2 BEQ P%+5 \ routine to decrease the pitch rate in X by A, taking JSR REDU2 \ the keyboard auto re-centre setting into account LDY KL+6 \ If the "S" key is being pressed, then call the BUMP2 BEQ P%+5 \ routine to increase the pitch rate in X by A JSR BUMP2 STX JSTY \ Store the updated roll rate in JSTY \ Fall through into DK4 to scan for other keys \ ****************************************************************************** \ \ Name: DK4 \ Type: Subroutine \ Category: Keyboard \ Summary: Scan for pause, configuration and secondary flight keys \ Deep dive: The key logger \ \ ------------------------------------------------------------------------------ \ \ Scan for pause and configuration keys, and if this is a space view, also scan \ for secondary flight controls. \ \ Specifically: \ \ * Scan for the pause button (COPY) and if it's pressed, pause the game and \ process any configuration key presses until the game is unpaused (DELETE) \ \ * If this is a space view, scan for secondary flight keys and update the \ relevant bytes in the key logger \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ FREEZE Rejoin the pause routine after processing a screen save \ \ ****************************************************************************** .DK4 LDX KTRAN \ Fetch the internal key number of the current key \ press from the key logger buffer STX KL \ Store X in KL, byte #0 of the key logger CPX #&69 \ If COPY is not being pressed, jump to DK2 below, BNE DK2 \ otherwise let's process the configuration keys .FREEZE \ COPY is being pressed, so we enter a loop that \ listens for configuration keys, and we keep looping \ until we detect a DELETE key press. This effectively \ pauses the game when COPY is pressed, and unpauses \ it when DELETE is pressed JSR WSCAN \ Call WSCAN to wait for the vertical sync, so the whole \ screen gets drawn JSR RDKEY \ Scan the keyboard for a key press and return the \ internal key number in A and X (or 0 for no key press) CPX #&51 \ If "S" is not being pressed, skip to DK6 BNE DK6 LDA #0 \ "S" is being pressed, so set DNOIZ to 0 to turn the STA DNOIZ \ sound on .DK6 LDY #&40 \ We now want to loop through the keys that toggle \ various settings. These have internal key numbers \ between &40 (CAPS LOCK) and &46 ("K"), so we set up \ the first key number in Y to act as a loop counter. \ See subroutine DKS3 for more details on this .DKL4 JSR DKS3 \ Call DKS3 to scan for the key given in Y, and toggle \ the relevant setting if it is pressed INY \ Increment Y to point to the next toggle key \ --- Mod: Code removed for two-player Elite: ---------> \IF _SNG45 OR _SOURCE_DISC \ \CPY #&47 \ The last toggle key is &46 (K), so check whether we \ \ have just done that one \ \ELIF _EXECUTIVE \ \CPY #&49 \ The last toggle key is &48 (:), so check whether we \ \ have just done that one \ \ENDIF \ --- And replaced by: --------------------------------> CPY #&46 \ The last toggle key is &45 (J), so check whether we \ have just done that one \ --- End of replacement ------------------------------> BNE DKL4 \ If not, loop back to check for the next toggle key CPX #&10 \ If "Q" is not being pressed, skip to DK7 BNE DK7 STX DNOIZ \ "Q" is being pressed, so set DNOIZ to X, which is \ non-zero (&10), so this will turn the sound off .DK7 \ --- Mod: Code removed for two-player Elite: ---------> \CPX #&70 \ If ESCAPE is not being pressed, skip over the next \BNE P%+5 \ instruction \ \JMP DEATH2 \ ESCAPE is being pressed, so jump to DEATH2 to end \ \ the game \ \CPX #&64 \ If "B" is not being pressed, skip to nobit \BNE nobit \ \LDA BSTK \ Toggle the value of BSTK between 0 and &FF \EOR #&FF \STA BSTK \ \STA JSTK \ Configure JSTK to the same value, so when the Bitstik \ \ is enabled, so is the joystick \ \STA JSTE \ Configure JSTE to the same value, so when the Bitstik \ \ is enabled, the joystick is configured with reversed \ \ channels \ \.nobit \ --- And replaced by: --------------------------------> CPX #&70 \ If ESCAPE is not being pressed, skip over the next BNE deat1 \ set of instructions JSR WipePlayer2Scanner \ Clear all player 2 (yellow) ships from the scanner JMP DEATH2 \ ESCAPE is being pressed, so jump to DEATH2 to end \ the game .deat1 \ --- End of replacement ------------------------------> CPX #&32 \ If "D" is being pressed, jump to savscr to save a BEQ savscr \ screenshot CPX #&59 \ If DELETE is not being pressed, we are still paused, BNE FREEZE \ so loop back up to keep listening for configuration \ keys, otherwise fall through into the rest of the \ key detection code, which unpauses the game .DK2 LDA QQ11 \ If the current view is non-zero (i.e. not a space BNE out \ view), return from the subroutine (as out contains \ an RTS) \ --- Mod: Code removed for two-player Elite: ---------> \LDY #16 \ This is a space view, so now we want to check for all \ \ the secondary flight keys. The internal key numbers \ \ are in the keyboard table KYTB from KYTB+8 to \ \ KYTB+16, and their key logger locations are from KL+8 \ \ to KL+16. So set a decreasing counter in Y for the \ \ index, starting at 16, so we can loop through them \ --- And replaced by: --------------------------------> LDY player2JSTK \ If player 2 is not an AI Pilot, jump to dkey1 BNE dkey1 \ Player 2 is an AI Pilot, so we don't check secondary \ flight keys for player 2 LDY #9 \ Modify the CMP #7 after DK1 to CMP #9 so we don't STY DK1+2 \ check KL+8 (player 2 arm missile) or KL+9 (player 2 \ fire missile) LDY #13 \ Set the loop so we don't check KL+14 (player 2 E.C.M.) \ or KY+15 (player 2 unarm missile) BNE dkey2 \ Jump to dkey2 to perform the loop (this BNE is \ effectively a JMP as Y is never zero) .dkey1 LDY #7 \ Set the CMP #7 after DK1 correctly to undo the above STY DK1+2 \ modification LDY #15 \ This is a space view, so now we want to check for all \ the secondary flight keys. The internal key numbers \ are in the keyboard table KYTB from KYTB+8 to \ KYTB+15, and their key logger locations are from KL+8 \ to KL+15. So set a decreasing counter in Y for the \ index, starting at 15, so we can loop through them \ \ We don't check for KY20, KY21 or KY22 (at KL+16, 17 or \ 18) as those are primary controls for player 2 and are \ dealt with separately (they are speed up, speed down \ and joystick fire button) .dkey2 \ --- End of replacement ------------------------------> LDA #&FF \ Set A to &FF so we can store this in the keyboard \ logger for keys that are being pressed .DKL1 LDX KYTB,Y \ Get the internal key number of the Y-th flight key \ the KYTB keyboard table CPX KL \ We stored the key that's being pressed in KL above, \ so check to see if the Y-th flight key is being \ pressed BNE DK1 \ If it is not being pressed, skip to DK1 below STA KL,Y \ The Y-th flight key is being pressed, so set that \ key's location in the key logger to &FF .DK1 DEY \ Decrement the loop counter CPY #7 \ Have we just done the last key? BNE DKL1 \ If not, loop back to process the next key RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: savscr \ Type: Subroutine \ Category: Save and load \ Summary: Save a screenshot if CTRL-D is pressed when the game is paused \ \ ------------------------------------------------------------------------------ \ \ Screen memory from &4000 to &7FFF is saved to disc with an incremental \ filename, starting with ":0.X.SCREEN1" for the first screenshot, then \ ":0.X.SCREEN2" for the next, and so on. \ \ ****************************************************************************** .savscr JSR CTRL \ Scan the keyboard to see if CTRL is currently pressed, \ returning a negative value in A if it is BPL FREEZE \ If CTRL is not being pressed, jump to FREEZE to keep \ listening for configuration keys while we're paused LDX #17 \ We start by copying the 18 bytes in oscobl2 to oscobl, \ so set a counter in X for 18 bytes. The oscobl block \ is not altered by this routine or any other, so it \ isn't clear why we copy oscobl2 to oscobl, but perhaps \ there was a reason at some point .savscl LDA oscobl2,X \ Copy the X-th byte of oscobl2 to the X-th byte of STA oscobl,X \ oscobl DEX \ Decrement the byte counter BPL savscl \ Loop back for the next byte until we have copied all \ 18 bytes LDX #LO(oscobl) \ Set (Y X) to point to the oscobl parameter block LDY #HI(oscobl) LDA #0 \ Call OSFILE with A = 0 to save a file containing the JSR OSFILE \ screen memory from &4000 to &7FFF INC scname+11 \ Increment the screenshot number in the filename at \ scname, so ":0.X.SCREEN1" becomes ":0.X.SCREEN2" and \ so on JMP FREEZE \ Jump back into the pause loop to keep listening for \ configuration key presses \ ****************************************************************************** \ \ Name: TT217 \ Type: Subroutine \ Category: Keyboard \ Summary: Scan the keyboard until a key is pressed \ \ ------------------------------------------------------------------------------ \ \ Scan the keyboard until a key is pressed, and return the key's ASCII code. \ If, on entry, a key is already being held down, then wait until that key is \ released first (so this routine detects the first key down event following \ the subroutine call). \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X The ASCII code of the key that was pressed \ \ A Contains the same as X \ \ Y Y is preserved \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ out Contains an RTS \ \ t As TT217 but don't preserve Y, set it to YSAV instead \ \ ****************************************************************************** .TT217 STY YSAV \ Store Y in temporary storage, so we can restore it \ later .t LDY #2 \ Wait for 2/50 of a second (0.04 seconds) to implement JSR DELAY \ a simple keyboard debounce and prevent multiple key \ presses being recorded JSR RDKEY \ Scan the keyboard for a key press and return the \ internal key number in A and X (or 0 for no key press) BNE t \ If a key was already being held down when we entered \ this routine, keep looping back up to t, until the \ key is released .t2 JSR RDKEY \ Any pre-existing key press is now gone, so we can \ start scanning the keyboard again, returning the \ internal key number in A and X (or 0 for no key press) BEQ t2 \ Keep looping up to t2 until a key is pressed TAY \ Copy A to Y, so Y contains the internal key number \ of the key pressed LDA TRANTABLE,Y \ TRANTABLE points to the MOS key translation table, \ which is used to translate internal key numbers to \ ASCII, so this fetches the key's ASCII code into A LDY YSAV \ Restore the original value of Y we stored above TAX \ Copy A into X .out RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: me1 \ Type: Subroutine \ Category: Flight \ Summary: Erase an old in-flight message and display a new one \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The text token to be printed \ \ X Must be set to 0 \ \ ****************************************************************************** .me1 STX DLY \ Set the message delay in DLY to 0, so any new \ in-flight messages will be shown instantly PHA \ Store the new message token we want to print LDA MCH \ Set A to the token number of the message that is JSR mes9 \ currently on-screen, and call mes9 to print it (which \ will remove it from the screen, as printing is done \ using EOR logic) PLA \ Restore the new message token \ ****************************************************************************** \ \ Name: MESS \ Type: Subroutine \ Category: Flight \ Summary: Display an in-flight message \ \ ------------------------------------------------------------------------------ \ \ Display an in-flight message in capitals at the bottom of the space view, \ erasing any existing in-flight message first. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The text token to be printed \ \ ****************************************************************************** .MESS PHA \ Store A on the stack so we can restore it after the \ call to DOCOL LDA #YELLOW \ Send a #SETCOL YELLOW command to the I/O processor to JSR DOCOL \ switch to colour 1, which is yellow PLA \ Restore A from the stack LDX #0 \ Set QQ17 = 0 to switch to ALL CAPS STX QQ17 PHA \ Store A on the stack so we can restore it after the \ calls to DOXC and DOYC LDA messXC \ Move the text cursor to column messXC, in case we JSR DOXC \ jump to me1 below to erase the current in-flight \ message (whose column we stored in messXC when we \ called MESS to put it there in the first place) \ --- Mod: Code removed for two-player Elite: ---------> \LDA #22 \ Move the text cursor to row 22, and set Y = 22 \TAY \JSR DOYC \ --- And replaced by: --------------------------------> LDA #10 \ Move the text cursor to row 10 JSR DOYC LDY #22 \ Set Y = 22 \ --- End of replacement ------------------------------> PLA \ Restore A from the stack CPX DLY \ If the message delay in DLY is not zero, jump up to BNE me1 \ me1 to erase the current message first (whose token \ number will be in MCH) STY DLY \ Set the message delay in DLY to 22 STA MCH \ Set MCH to the token we are about to display \ Before we fall through into mes9 to print the token, \ we need to work out the starting column for the \ message we want to print, so it's centred on-screen, \ so the following doesn't print anything, it just uses \ the justified text mechanism to work out the number of \ characters in the message we are going to print LDA #%11000000 \ Set the DTW4 flag to %11000000 (justify text, buffer STA DTW4 \ entire token including carriage returns) \ --- Mod: Code removed for two-player Elite: ---------> \LDA de \ Set the C flag to bit 1 of the destruction flag in de \LSR A \ --- End of removed code -----------------------------> LDA #0 \ Set A = 0 \ --- Mod: Code removed for two-player Elite: ---------> \BCC P%+4 \ If the destruction flag in de is not set, skip the \ \ following instruction \ \LDA #10 \ Set A = 10 \ --- End of removed code -----------------------------> STA DTW5 \ Store A in DTW5, so DTW5 (which holds the size of the \ justified text buffer at BUF) is set to 0 if the \ destruction flag is not set, or 10 if it is (10 being \ the number of characters in the " DESTROYED" token) LDA MCH \ Call TT27 to print the token in MCH into the buffer JSR TT27 \ (this doesn't print it on-screen, it just puts it into \ the buffer and moves the DTW5 pointer along, so DTW5 \ now contains the size of the message we want to print, \ including the " DESTROYED" part if that's going to be \ included) \ --- Mod: Code removed for two-player Elite: ---------> \LDA #32 \ Set A = (32 - DTW5) / 2 \SEC \ \SBC DTW5 \ so A now contains the column number we need to print \LSR A \ our message at for it to be centred on-screen (as \ \ there are 32 columns) \ --- And replaced by: --------------------------------> LDA #34 \ Set A = (34 - DTW5) / 2 SEC \ SBC DTW5 \ so A now contains the column number we need to print LSR A \ our message at for it to be centred on-screen, moved \ to the left by one character \ --- End of replacement ------------------------------> STA messXC \ Store A in messXC, so when we erase the message via \ the branch to me1 above, messXC will tell us where to \ print it JSR DOXC \ Move the text cursor to column messXC JSR MT15 \ Call MT15 to switch to left-aligned text when printing \ extended tokens disabling the justify text setting we \ set above LDA MCH \ Set MCH to the token we are about to display \ Fall through into mes9 to print the token in A \ ****************************************************************************** \ \ Name: mes9 \ Type: Subroutine \ Category: Flight \ Summary: Print a text token, possibly followed by " DESTROYED" \ \ ------------------------------------------------------------------------------ \ \ Print a text token, followed by " DESTROYED" if the destruction flag is set \ (for when a piece of equipment is destroyed). \ \ ****************************************************************************** .mes9 \ --- Mod: Code removed for two-player Elite: ---------> \JSR TT27 \ Call TT27 to print the text token in A \ \LSR de \ If bit 0 of variable de is clear, return from the \BCC out \ subroutine (as out contains an RTS) \ \LDA #253 \ Print recursive token 93 (" DESTROYED") and return \JMP TT27 \ from the subroutine using a tail call \ --- And replaced by: --------------------------------> JMP TT27 \ Call TT27 to print the text token in A and return from \ the subroutine using a tail call \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: OUCH \ Type: Subroutine \ Category: Flight \ Summary: Potentially lose cargo or equipment following damage \ \ ------------------------------------------------------------------------------ \ \ Our shields are dead and we are taking damage, so there is a small chance of \ losing cargo or equipment. \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.OUCH \ \JSR DORND \ Set A and X to random numbers \ \BMI out \ If A < 0 (50% chance), return from the subroutine \ \ (as out contains an RTS) \ \CPX #22 \ If X >= 22 (91% chance), return from the subroutine \BCS out \ (as out contains an RTS) \ \LDA QQ20,X \ If we do not have any of item QQ20+X, return from the \BEQ out \ subroutine (as out contains an RTS). X is in the range \ \ 0-21, so this not only checks for cargo, but also for \ \ E.C.M., fuel scoops, energy bomb, energy unit and \ \ docking computer, all of which can be destroyed \ \LDA DLY \ If there is already an in-flight message on-screen, \BNE out \ return from the subroutine (as out contains an RTS) \ \LDY #3 \ Set bit 1 of de, the equipment destruction flag, so \STY de \ that when we call MESS below, " DESTROYED" is appended \ \ to the in-flight message \ \STA QQ20,X \ A is 0 (as we didn't branch with the BNE above), so \ \ this sets QQ20+X to 0, which destroys any cargo or \ \ equipment we have of that type \ \CPX #17 \ If X >= 17 then we just lost a piece of equipment, so \BCS ou1 \ jump to ou1 to print the relevant message \ \TXA \ Print recursive token 48 + A as an in-flight token, \ADC #208 \ which will be in the range 48 ("FOOD") to 64 ("ALIEN \BNE MESS \ ITEMS") as the C flag is clear, so this prints the \ \ destroyed item's name, followed by " DESTROYED" (as we \ \ set bit 1 of the de flag above), and returns from the \ \ subroutine using a tail call \ \.ou1 \ \BEQ ou2 \ If X = 17, jump to ou2 to print "E.C.M.SYSTEM \ \ DESTROYED" and return from the subroutine using a tail \ \ call \ \CPX #18 \ If X = 18, jump to ou3 to print "FUEL SCOOPS \BEQ ou3 \ DESTROYED" and return from the subroutine using a tail \ \ call \ \TXA \ Otherwise X is in the range 19 to 21 and the C flag is \ADC #113-20 \ set (as we got here via a BCS to ou1), so we set A as \ \ follows: \ \ \ \ A = 113 - 20 + X + C \ \ = 113 - 19 + X \ \ = 113 to 115 \ \JMP MESS \ Print recursive token A ("ENERGY BOMB", "ENERGY UNIT" \ \ or "DOCKING COMPUTERS") as an in-flight message, \ \ followed by " DESTROYED", and return from the \ \ subroutine using a tail call \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: Player2me1 \ Type: Subroutine \ Category: Flight \ Summary: Erase an old in-flight message and display a new one \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The text token to be printed \ \ X Must be set to 0 \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Player2me1 STX player2DLY \ Set the message delay in DLY to 0, so any new \ in-flight messages will be shown instantly PHA \ Store the new message token we want to print LDA player2MCH \ Set A to the token number of the message that is JSR mes9 \ currently on-screen, and call mes9 to print it (which \ will remove it from the screen, as printing is done \ using EOR logic) PLA \ Restore the new message token \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: Player2MESS \ Type: Subroutine \ Category: Flight \ Summary: Display an in-flight message \ \ ------------------------------------------------------------------------------ \ \ Display an in-flight message in capitals at the bottom of the space view, \ erasing any existing in-flight message first. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The text token to be printed \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Player2MESS PHA \ Store A on the stack so we can restore it after the \ call to DOCOL LDA #YELLOW \ Send a #SETCOL YELLOW command to the I/O processor to JSR DOCOL \ switch to colour 1, which is yellow PLA \ Restore A from the stack LDX #0 \ Set QQ17 = 0 to switch to ALL CAPS STX QQ17 PHA \ Store A on the stack so we can restore it after the \ calls to DOXC and DOYC LDA player2messXC \ Move the text cursor to column messXC, in case we JSR DOXC \ jump to me1 below to erase the current in-flight \ message (whose column we stored in messXC when we \ called MESS to put it there in the first place) LDA #22 \ Move the text cursor to row 22, and set Y = 22 TAY JSR DOYC PLA \ Restore A from the stack CPX player2DLY \ If the message delay in DLY is not zero, jump up to BNE Player2me1 \ me1 to erase the current message first (whose token \ number will be in MCH) STY player2DLY \ Set the message delay in DLY to 22 STA player2MCH \ Set MCH to the token we are about to display \ Before we fall through into mes9 to print the token, \ we need to work out the starting column for the \ message we want to print, so it's centred on-screen, \ so the following doesn't print anything, it just uses \ the justified text mechanism to work out the number of \ characters in the message we are going to print LDA #%11000000 \ Set the DTW4 flag to %11000000 (justify text, buffer STA DTW4 \ entire token including carriage returns) LDA #0 \ Set A = 0 STA DTW5 \ Store A in DTW5, so DTW5 (which holds the size of the \ justified text buffer at BUF) is set to 0 if the \ destruction flag is not set, or 10 if it is (10 being \ the number of characters in the " DESTROYED" token) LDA player2MCH \ Call TT27 to print the token in MCH into the buffer JSR TT27 \ (this doesn't print it on-screen, it just puts it into \ the buffer and moves the DTW5 pointer along, so DTW5 \ now contains the size of the message we want to print, \ including the " DESTROYED" part if that's going to be \ included) \ --- Mod: Code removed for two-player Elite: ---------> \LDA #32 \ Set A = (32 - DTW5) / 2 \SEC \ \SBC DTW5 \ so A now contains the column number we need to print \LSR A \ our message at for it to be centred on-screen (as \ \ there are 32 columns) \ --- And replaced by: --------------------------------> LDA #34 \ Set A = (34 - DTW5) / 2 SEC \ SBC DTW5 \ so A now contains the column number we need to print LSR A \ our message at for it to be centred on-screen, moved \ to the left by one character \ --- End of replacement ------------------------------> STA player2messXC \ Store A in messXC, so when we erase the message via \ the branch to me1 above, messXC will tell us where to \ print it JSR DOXC \ Move the text cursor to column messXC JSR MT15 \ Call MT15 to switch to left-aligned text when printing \ extended tokens disabling the justify text setting we \ set above LDA player2MCH \ Set MCH to the token we are about to display JMP mes9 \ Jump to mes9 to print the token in A \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: ou2 \ Type: Subroutine \ Category: Flight \ Summary: Display "E.C.M.SYSTEM DESTROYED" as an in-flight message \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.ou2 \ \LDA #108 \ Set A to recursive token 108 ("E.C.M.SYSTEM") \ \JMP MESS \ Print recursive token A as an in-flight message, \ \ followed by " DESTROYED", and return from the \ \ subroutine using a tail call \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: ou3 \ Type: Subroutine \ Category: Flight \ Summary: Display "FUEL SCOOPS DESTROYED" as an in-flight message \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.ou3 \ \LDA #111 \ Set A to recursive token 111 ("FUEL SCOOPS") \ \JMP MESS \ Print recursive token A as an in-flight message, \ \ followed by " DESTROYED", and return from the \ \ subroutine using a tail call \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: ITEM \ Type: Macro \ Category: Market \ Summary: Macro definition for the market prices table \ Deep dive: Market item prices and availability \ \ ------------------------------------------------------------------------------ \ \ The following macro is used to build the market prices table: \ \ ITEM price, factor, units, quantity, mask \ \ It inserts an item into the market prices table at QQ23. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ price Base price \ \ factor Economic factor \ \ units Units: "t", "g" or "k" \ \ quantity Base quantity \ \ mask Fluctuations mask \ \ ****************************************************************************** MACRO ITEM price, factor, units, quantity, mask IF factor < 0 s = 1 << 7 ELSE s = 0 ENDIF IF units = 't' u = 0 ELIF units = 'k' u = 1 << 5 ELSE u = 1 << 6 ENDIF e = ABS(factor) EQUB price EQUB s + u + e EQUB quantity EQUB mask ENDMACRO \ ****************************************************************************** \ \ Name: QQ23 \ Type: Variable \ Category: Market \ Summary: Market prices table \ Deep dive: Market item prices and availability \ \ ------------------------------------------------------------------------------ \ \ Each item has four bytes of data, like this: \ \ Byte #0 = Base price \ Byte #1 = Economic factor in bits 0-4, with the sign in bit 7 \ Unit in bits 5-6 \ Byte #2 = Base quantity \ Byte #3 = Mask to control price fluctuations \ \ To make it easier for humans to follow, I've defined a macro called ITEM \ that takes the following arguments and builds the four bytes for us: \ \ ITEM base price, economic factor, units, base quantity, mask \ \ So for food, we have the following, for example: \ \ * Base price = 19 \ * Economic factor = -2 \ * Unit = tonnes \ * Base quantity = 6 \ * Mask = %00000001 \ \ ****************************************************************************** .QQ23 ITEM 19, -2, 't', 6, %00000001 \ 0 = Food ITEM 20, -1, 't', 10, %00000011 \ 1 = Textiles ITEM 65, -3, 't', 2, %00000111 \ 2 = Radioactives ITEM 40, -5, 't', 226, %00011111 \ 3 = Slaves ITEM 83, -5, 't', 251, %00001111 \ 4 = Liquor/Wines ITEM 196, 8, 't', 54, %00000011 \ 5 = Luxuries ITEM 235, 29, 't', 8, %01111000 \ 6 = Narcotics ITEM 154, 14, 't', 56, %00000011 \ 7 = Computers ITEM 117, 6, 't', 40, %00000111 \ 8 = Machinery ITEM 78, 1, 't', 17, %00011111 \ 9 = Alloys ITEM 124, 13, 't', 29, %00000111 \ 10 = Firearms ITEM 176, -9, 't', 220, %00111111 \ 11 = Furs ITEM 32, -1, 't', 53, %00000011 \ 12 = Minerals ITEM 97, -1, 'k', 66, %00000111 \ 13 = Gold \EQUD &360A118 \ This data is commented out in the original source \ \ It would have inserted an item as follows: \ \ ITEM 24, -1, 'k', 96, %00000011 \ \ So that's an item with a base price of 24 credits that \ is slightly cheaper than average in agricultural \ economies but closer to average in rich industrial \ ones, with a base quantity of 96kg and a reasonably \ stable price \ \ I wonder what this mysterious item was going to be? ITEM 171, -2, 'k', 55, %00011111 \ 14 = Platinum ITEM 45, -1, 'g', 250, %00001111 \ 15 = Gem-Stones ITEM 53, 15, 't', 192, %00000111 \ 16 = Alien items \ ****************************************************************************** \ \ Name: oscobl \ Type: Variable \ Category: Save and load \ Summary: OSFILE configuration block for saving a screenshot \ \ ------------------------------------------------------------------------------ \ \ This OSFILE configuration block is overwritten by the block at oscobl2 before \ being passed to OSFILE to save a screenshot. \ \ ****************************************************************************** .oscobl EQUW scname \ The address of the filename to save EQUD &FFFF4000 \ Load address of the saved file EQUD &FFFF4000 \ Execution address of the saved file EQUD &FFFF4000 \ Start address of the memory block to save EQUD &FFFF8000 \ End address of the memory block to save \ ****************************************************************************** \ \ Name: scname \ Type: Variable \ Category: Save and load \ Summary: Filename to be used when saving a screenshot \ \ ****************************************************************************** .scname EQUS ":0.X.SCREEN1" EQUB 13 \ ****************************************************************************** \ \ Name: oscobl2 \ Type: Variable \ Category: Save and load \ Summary: Master OSFILE configuration block for saving a screenshot \ \ ------------------------------------------------------------------------------ \ \ This OSFILE configuration block is copied from oscobl2 to oscobl in order to \ save a screenshot. \ \ ****************************************************************************** .oscobl2 EQUW scname \ The address of the filename to save EQUD &FFFF4000 \ Load address of the saved file EQUD &FFFF4000 \ Execution address of the saved file EQUD &FFFF4000 \ Start address of the memory block to save EQUD &FFFF8000 \ End address of the memory block to save \ ****************************************************************************** \ \ Name: TIDY \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Orthonormalise the orientation vectors for a ship \ Deep dive: Tidying orthonormal vectors \ Orientation vectors \ \ ------------------------------------------------------------------------------ \ \ This routine orthonormalises the orientation vectors for a ship. This means \ making the three orientation vectors orthogonal (perpendicular to each other), \ and normal (so each of the vectors has length 1). \ \ We do this because we use the small angle approximation to rotate these \ vectors in space. It is not completely accurate, so the three vectors tend \ to get stretched over time, so periodically we tidy the vectors with this \ routine to ensure they remain as orthonormal as possible. \ \ ****************************************************************************** .TI2 \ Called from below with A = 0, X = 0, Y = 4 when \ nosev_x and nosev_y are small, so we assume that \ nosev_z is big TYA \ A = Y = 4 LDY #2 JSR TIS3 \ Call TIS3 with X = 0, Y = 2, A = 4, to set roofv_z = STA INWK+20 \ -(nosev_x * roofv_x + nosev_y * roofv_y) / nosev_z JMP TI3 \ Jump to TI3 to keep tidying .TI1 \ Called from below with A = 0, Y = 4 when nosev_x is \ small TAX \ Set X = A = 0 LDA XX15+1 \ Set A = nosev_y, and if the top two magnitude bits AND #%01100000 \ are both clear, jump to TI2 with A = 0, X = 0, Y = 4 BEQ TI2 LDA #2 \ Otherwise nosev_y is big, so set up the index values \ to pass to TIS3 JSR TIS3 \ Call TIS3 with X = 0, Y = 4, A = 2, to set roofv_y = STA INWK+18 \ -(nosev_x * roofv_x + nosev_z * roofv_z) / nosev_y JMP TI3 \ Jump to TI3 to keep tidying .TIDY LDA INWK+10 \ Set (XX15, XX15+1, XX15+2) = nosev STA XX15 LDA INWK+12 STA XX15+1 LDA INWK+14 STA XX15+2 JSR NORM \ Call NORM to normalise the vector in XX15, i.e. nosev LDA XX15 \ Set nosev = (XX15, XX15+1, XX15+2) STA INWK+10 LDA XX15+1 STA INWK+12 LDA XX15+2 STA INWK+14 LDY #4 \ Set Y = 4 LDA XX15 \ Set A = nosev_x, and if the top two magnitude bits AND #%01100000 \ are both clear, jump to TI1 with A = 0, Y = 4 BEQ TI1 LDX #2 \ Otherwise nosev_x is big, so set up the index values LDA #0 \ to pass to TIS3 JSR TIS3 \ Call TIS3 with X = 2, Y = 4, A = 0, to set roofv_x = STA INWK+16 \ -(nosev_y * roofv_y + nosev_z * roofv_z) / nosev_x .TI3 LDA INWK+16 \ Set (XX15, XX15+1, XX15+2) = roofv STA XX15 LDA INWK+18 STA XX15+1 LDA INWK+20 STA XX15+2 JSR NORM \ Call NORM to normalise the vector in XX15, i.e. roofv LDA XX15 \ Set roofv = (XX15, XX15+1, XX15+2) STA INWK+16 LDA XX15+1 STA INWK+18 LDA XX15+2 STA INWK+20 LDA INWK+12 \ Set Q = nosev_y STA Q LDA INWK+20 \ Set A = roofv_z JSR MULT12 \ Set (S R) = Q * A = nosev_y * roofv_z LDX INWK+14 \ Set X = nosev_z LDA INWK+18 \ Set A = roofv_y JSR TIS1 \ Set (A ?) = (-X * A + (S R)) / 96 \ = (-nosev_z * roofv_y + nosev_y * roofv_z) / 96 \ \ This also sets Q = nosev_z EOR #%10000000 \ Set sidev_x = -A STA INWK+22 \ = (nosev_z * roofv_y - nosev_y * roofv_z) / 96 LDA INWK+16 \ Set A = roofv_x JSR MULT12 \ Set (S R) = Q * A = nosev_z * roofv_x LDX INWK+10 \ Set X = nosev_x LDA INWK+20 \ Set A = roofv_z JSR TIS1 \ Set (A ?) = (-X * A + (S R)) / 96 \ = (-nosev_x * roofv_z + nosev_z * roofv_x) / 96 \ \ This also sets Q = nosev_x EOR #%10000000 \ Set sidev_y = -A STA INWK+24 \ = (nosev_x * roofv_z - nosev_z * roofv_x) / 96 LDA INWK+18 \ Set A = roofv_y JSR MULT12 \ Set (S R) = Q * A = nosev_x * roofv_y LDX INWK+12 \ Set X = nosev_y LDA INWK+16 \ Set A = roofv_x JSR TIS1 \ Set (A ?) = (-X * A + (S R)) / 96 \ = (-nosev_y * roofv_x + nosev_x * roofv_y) / 96 EOR #%10000000 \ Set sidev_z = -A STA INWK+26 \ = (nosev_y * roofv_x - nosev_x * roofv_y) / 96 LDA #0 \ Set A = 0 so we can clear the low bytes of the \ orientation vectors \ --- Mod: Code removed for two-player Elite: ---------> \LDX #14 \ We want to clear the low bytes, so start from sidev_y \ \ at byte #9+14 (we clear all except sidev_z_lo, though \ \ I suspect this is in error and that X should be 16) \ --- And replaced by: --------------------------------> LDX #16 \ We want to clear the low bytes, so start from sidev_y \ at byte #9+14 \ --- End of replacement ------------------------------> .TIL1 STA INWK+9,X \ Set the low byte in byte #9+X to zero DEX \ Set X = X - 2 to jump down to the next low byte DEX BPL TIL1 \ Loop back until we have zeroed all the low bytes RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TIS2 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate A = A / Q \ Deep dive: Shift-and-subtract division \ \ ------------------------------------------------------------------------------ \ \ Calculate the following division, where A is a sign-magnitude number and Q is \ a positive integer: \ \ A = A / Q \ \ The value of A is returned as a sign-magnitude number with 96 representing 1, \ and the maximum value returned is 1 (i.e. 96). This routine is used when \ normalising vectors, where we represent fractions using integers, so this \ gives us an approximation to two decimal places. \ \ ****************************************************************************** .TIS2 TAY \ Store the argument A in Y AND #%01111111 \ Strip the sign bit from the argument, so A = |A| CMP Q \ If A >= Q then jump to TI4 to return a 1 with the BCS TI4 \ correct sign LDX #%11111110 \ Set T to have bits 1-7 set, so we can rotate through 7 STX T \ loop iterations, getting a 1 each time, and then \ getting a 0 on the 8th iteration... and we can also \ use T to catch our result bits into bit 0 each time .TIL2 ASL A \ Shift A to the left CMP Q \ If A < Q skip the following subtraction BCC P%+4 SBC Q \ A >= Q, so set A = A - Q \ \ Going into this subtraction we know the C flag is \ set as we passed through the BCC above, and we also \ know that A >= Q, so the C flag will still be set once \ we are done ROL T \ Rotate the counter in T to the left, and catch the \ result bit into bit 0 (which will be a 0 if we didn't \ do the subtraction, or 1 if we did) BCS TIL2 \ If we still have set bits in T, loop back to TIL2 to \ do the next iteration of 7 \ We've done the division and now have a result in the \ range 0-255 here, which we need to reduce to the range \ 0-96. We can do that by multiplying the result by 3/8, \ as 256 * 3/8 = 96 LDA T \ Set T = T / 4 LSR A LSR A STA T LSR A \ Set T = T / 8 + T / 4 ADC T \ = 3T / 8 STA T TYA \ Fetch the sign bit of the original argument A AND #%10000000 ORA T \ Apply the sign bit to T RTS \ Return from the subroutine .TI4 TYA \ Fetch the sign bit of the original argument A AND #%10000000 ORA #96 \ Apply the sign bit to 96 (which represents 1) RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TIS3 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate -(nosev_1 * roofv_1 + nosev_2 * roofv_2) / nosev_3 \ \ ------------------------------------------------------------------------------ \ \ Calculate the following expression: \ \ A = -(nosev_1 * roofv_1 + nosev_2 * roofv_2) / nosev_3 \ \ where 1, 2 and 3 are x, y, or z, depending on the values of X, Y and A. This \ routine is called with the following values: \ \ X = 0, Y = 2, A = 4 -> \ A = -(nosev_x * roofv_x + nosev_y * roofv_y) / nosev_z \ \ X = 0, Y = 4, A = 2 -> \ A = -(nosev_x * roofv_x + nosev_z * roofv_z) / nosev_y \ \ X = 2, Y = 4, A = 0 -> \ A = -(nosev_y * roofv_y + nosev_z * roofv_z) / nosev_x \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X Index 1 (0 = x, 2 = y, 4 = z) \ \ Y Index 2 (0 = x, 2 = y, 4 = z) \ \ A Index 3 (0 = x, 2 = y, 4 = z) \ \ ****************************************************************************** .TIS3 STA P+2 \ Store P+2 in A for later LDA INWK+10,X \ Set Q = nosev_x_hi (plus X) STA Q LDA INWK+16,X \ Set A = roofv_x_hi (plus X) JSR MULT12 \ Set (S R) = Q * A \ = nosev_x_hi * roofv_x_hi LDX INWK+10,Y \ Set Q = nosev_x_hi (plus Y) STX Q LDA INWK+16,Y \ Set A = roofv_x_hi (plus Y) JSR MAD \ Set (A X) = Q * A + (S R) \ = (nosev_x,X * roofv_x,X) + \ (nosev_x,Y * roofv_x,Y) STX P \ Store low byte of result in P, so result is now in \ (A P) LDY P+2 \ Set Q = roofv_x_hi (plus argument A) LDX INWK+10,Y STX Q EOR #%10000000 \ Flip the sign of A \ Fall through into DIVDT to do: \ \ (P+1 A) = (A P) / Q \ \ = -((nosev_x,X * roofv_x,X) + \ (nosev_x,Y * roofv_x,Y)) \ / nosev_x,A \ ****************************************************************************** \ \ Name: DVIDT \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (P+1 A) = (A P) / Q \ \ ------------------------------------------------------------------------------ \ \ Calculate the following integer division between sign-magnitude numbers: \ \ (P+1 A) = (A P) / Q \ \ This uses the same shift-and-subtract algorithm as TIS2. \ \ ****************************************************************************** .DVIDT STA P+1 \ Set P+1 = A, so P(1 0) = (A P) EOR Q \ Set T = the sign bit of A EOR Q, so it's 1 if A and Q AND #%10000000 \ have different signs, i.e. it's the sign of the result STA T \ of A / Q LDA #0 \ Set A = 0 for us to build a result LDX #16 \ Set a counter in X to count the 16 bits in P(1 0) ASL P \ Shift P(1 0) left ROL P+1 ASL Q \ Clear the sign bit of Q the C flag at the same time LSR Q .DVL2 ROL A \ Shift A to the left CMP Q \ If A < Q skip the following subtraction BCC P%+4 SBC Q \ Set A = A - Q \ \ Going into this subtraction we know the C flag is \ set as we passed through the BCC above, and we also \ know that A >= Q, so the C flag will still be set once \ we are done ROL P \ Rotate P(1 0) to the left, and catch the result bit ROL P+1 \ into the C flag (which will be a 0 if we didn't \ do the subtraction, or 1 if we did) DEX \ Decrement the loop counter BNE DVL2 \ Loop back for the next bit until we have done all 16 \ bits of P(1 0) LDA P \ Set A = P so the low byte is in the result in A ORA T \ Set A to the correct sign bit that we set in T above RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: KTRAN \ Type: Variable \ Category: Keyboard \ Summary: The key logger buffer that gets updated by the OSWORD 240 command \ \ ------------------------------------------------------------------------------ \ \ KTRAN is a buffer that is filled with key logger information by the KEYBOARD \ routine in the I/O processor, which is run when the parasite sends an OSWORD \ &F0 command to the I/O processor. The buffer contains details of keys being \ pressed, with KTRAN being filled with bytes #2 to #14 from the KEYBOARD \ routine (because KEYBOARD is called with OSSC pointing to buf, and buf is \ equal to KTRAN - 2). \ \ The key logger buffer is filled as follows: \ \ KTRAN + 0 Internal key number of any non-primary flight control \ key that is being pressed \ \ KTRAN + 1 "?" is being pressed (0 = no, &FF = yes) \ \ KTRAN + 2 Space is being pressed (0 = no, &FF = yes) \ \ KTRAN + 3 "<" is being pressed (0 = no, &FF = yes) \ \ KTRAN + 4 ">" is being pressed (0 = no, &FF = yes) \ \ KTRAN + 5 "X" is being pressed (0 = no, &FF = yes) \ \ KTRAN + 6 "S" is being pressed (0 = no, &FF = yes) \ \ KTRAN + 7 "A" is being pressed (0 = no, &FF = yes) \ \ KTRAN + 8 Joystick X value (high byte) \ \ KTRAN + 9 Joystick Y value (high byte) \ \ KTRAN + 10 Bitstik rotation value (high byte) \ \ KTRAN + 12 Joystick 1 fire button is being pressed (Bit 4 set = no, \ Bit 4 clear = yes) \ \ --- Mod: Code added for two-player Elite: -----------> \ Changes for two-player Elite: \ \ KTRAN + 10 Joystick 2 X value (high byte) \ \ KTRAN + 11 Joystick 2 Y value (high byte) \ \ KTRAN + 12 Joystick fire button is being pressed \ \ * Joystick 1: Bit 4 set = no, Bit 4 clear = yes \ \ * Joystick 2: Bit 5 set = no, Bit 5 clear = yes \ \ KTRAN + 13 "]" is being pressed (0 = no, &FF = yes) \ \ KTRAN + 14 RETURN is being pressed (0 = no, &FF = yes) \ \ --- End of added code -------------------------------> \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ buf The two OSWORD size bytes for transmitting the key \ logger from the I/O processor to the parasite \ \ ****************************************************************************** .buf \ --- Mod: Code removed for Delta 14B: ----------------> \EQUB 2 \ Transmit 2 bytes as part of this command \ --- And replaced by: --------------------------------> EQUB 4 \ Transmit 4 bytes as part of this command \ --- End of replacement ------------------------------> \ --- Mod: Code removed for two-player Elite: ---------> \EQUB 15 \ Receive 15 bytes as part of this command \ --- And replaced by: --------------------------------> EQUB 17 \ Receive 17 bytes as part of this command \ --- End of replacement ------------------------------> .KTRAN EQUS "1234567890" \ A 17-byte buffer to hold the key logger data from the EQUS "1234567" \ KEYBOARD routine in the I/O processor (note that only \ 12 of these bytes are actually updated by the KEYBOARD \ routine) \ --- Mod: Code added for two-player Elite: -----------> EQUS "89" \ Extend the key logger data buffer for player 2's \ primary flight controls \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: TRANTABLE \ Type: Variable \ Category: Keyboard \ Summary: Translation table from internal key number to ASCII \ \ ------------------------------------------------------------------------------ \ \ This is a copy of the keyboard translation table from the BBC Micro's MOS 1.20 \ ROM. The value at offset n is the lower-case ASCII value of the key with \ internal key number n, so for example the value at offset &10 is &71, which is \ 113, or ASCII "q", so internal key number &10 is the key number of the "Q" \ key. \ \ Valid internal key numbers are Binary Coded Decimal (BCD) numbers in the range \ &10 top &79, so they're in the ranges &10 to &19, then &20 to &29, then &30 to \ &39, and so on. This means that the other locations - i.e. &1A to &1F, &2A to \ &2F and so on - aren't used by the lookup table, but the MOS doesn't let this \ space go to waste; instead, those gaps contain MOS code, which is replicated \ below as the table contains a copy of this entire block of the MOS, not just \ the table entries. \ \ This table allows code running on the parasite to convert internal key numbers \ into ASCII codes in an efficient way. Without this table we would have to do a \ lookup from the table in the I/O processor's MOS ROM, which we would have to \ access from across the Tube, and this would be a lot slower than doing a \ simple table lookup in the parasite's user RAM. \ \ ****************************************************************************** .TRANTABLE EQUB &03, &8C, &40 \ MOS code EQUB &FE, &A0, &7F EQUB &8C, &43, &FE EQUB &8E, &4F, &FE EQUB &AE, &4F, &FE EQUB &60 \ Internal key numbers &10 to &19: \ EQUB &71, &33 \ Q 3 EQUB &34, &35 \ 4 5 EQUB &84, &38 \ f4 8 EQUB &87, &2D \ f7 - EQUB &5E, &8C \ ^ Left arrow EQUB &84, &EC, &86 \ MOS code EQUB &ED, &60, &00 \ Internal key numbers &20 to &29: \ EQUB &80, &77 \ f0 W EQUB &65, &74 \ E T EQUB &37, &69 \ 7 I EQUB &39, &30 \ 9 0 EQUB &5F, &8E \ _ Down arrow EQUB &6C, &FE, &FD \ MOS code EQUB &6C, &FA, &00 \ Internal key numbers &30 to &39: \ EQUB &31, &32 \ 1 2 EQUB &64, &72 \ D R EQUB &36, &75 \ 6 U EQUB &6F, &70 \ O P EQUB &5B, &8F \ [ Up arrow EQUB &2C, &B7, &D9 \ MOS code EQUB &6C, &28, &02 \ Internal key numbers &40 to &49: \ EQUB &01, &61 \ CAPS LOCK A EQUB &78, &66 \ X F EQUB &79, &6A \ Y J EQUB &6B, &40 \ K @ EQUB &3A, &0D \ : RETURN EQUB &00, &FF, &01 \ MOS code EQUB &02, &09, &0A \ Internal key numbers &50 to &59: \ EQUB &02, &73 \ SHIFT LOCK S EQUB &63, &67 \ C G EQUB &68, &6E \ H N EQUB &6C, &3B \ L ; EQUB &5D, &7F \ ] DELETE EQUB &AC, &44, &02 \ MOS code EQUB &A2, &00, &60 \ Internal key numbers &60 to &69: \ EQUB &00, &7A \ TAB Z EQUB &20, &76 \ Space V EQUB &62, &6D \ B M EQUB &2C, &2E \ , . EQUB &2F, &8B \ / COPY EQUB &AE, &41, &02 \ MOS code EQUB &4C, &AD, &E1 \ Internal key numbers &70 to &79: \ EQUB &1B, &81 \ ESCAPE f1 EQUB &82, &83 \ f2 f3 EQUB &85, &86 \ f5 f6 EQUB &88, &89 \ f8 f9 EQUB &5C, &8D \ \ Right arrow EQUB &6C, &20, &02 \ MOS code EQUB &D0, &EB, &A2 EQUB &08 \ ****************************************************************************** \ \ Name: RestartSync \ Type: Subroutine \ Category: Main loop \ Summary: Restart the sync counter \ \ ****************************************************************************** \ --- Mod: Code added for speed control: --------------> .RestartSync STZ musicBuff+2 \ Set the parameter to zero to restart the sync counter LDX #LO(musicBuff) \ Set (Y X) to point to the musicBuff parameter LDY #HI(musicBuff) \ block LDA #255 \ Set A = 255 for the SpeedControl OSWORD call JMP OSWORD \ Send an OSWORD command to the I/O processor to reset \ the sync counter, returning from the subroutine \ using a tail call \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: WaitForSync \ Type: Subroutine \ Category: Main loop \ Summary: Pause until the sync counter reaches zero \ \ ****************************************************************************** \ --- Mod: Code added for speed control: --------------> .WaitForSync LDA #1 \ Set the parameter to 1 to wait for the sync counter to STA musicBuff+2 \ count down LDX #LO(musicBuff) \ Set (Y X) to point to the musicBuff parameter LDY #HI(musicBuff) \ block LDA #255 \ Set A = 255 for the SpeedControl OSWORD call JMP OSWORD \ Send an OSWORD command to the I/O processor to reset \ the sync counter, returning from the subroutine \ using a tail call \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: musicBuff \ Type: Variable \ Category: Universe editor \ Summary: Buffer for music OSWORD calls \ \ ****************************************************************************** \ --- Mod: Code added for speed control: --------------> .musicBuff EQUB 3 \ The number of bytes to transmit with this command EQUB 2 \ The number of bytes to receive with this command EQUB 0 \ The parameter to send \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Save ELTF.bin \ \ ****************************************************************************** PRINT "ELITE F" PRINT "Assembled at ", ~CODE_F% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_F%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_F% PRINT "S.ELTF ", ~CODE_F%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_F% SAVE "3-assembled-output/ELTF.bin", CODE_F%, P%, LOAD% \ ****************************************************************************** \ \ ELITE G FILE \ \ Produces the binary file ELTG.bin that gets loaded by elite-bcfs.asm. \ \ ****************************************************************************** CODE_G% = P% LOAD_G% = LOAD% + P% - CODE% \ --- Mod: Code removed for two-player Elite: ---------> \IF _MATCH_ORIGINAL_BINARIES \ \IF _SNG45 \ \ EQUB &A5, &19, &8D, &FC, &08, &A5, &1A, &8D \ These bytes appear to be \ EQUB &FD, &08, &60, &A6, &83, &20, &68, &4B \ unused and just contain random \ EQUB &A6, &83, &4C, &D6, &12, &20, &C6, &4C \ workspace noise left over from \ EQUB &20, &76, &43, &8D, &53, &08, &8D, &69 \ the BBC Micro assembly process \ EQUB &08, &20, &82, &45, &A9, &06, &85, &4A \ EQUB &A9, &81, &4C, &C1, &44, &A2, &FF, &E8 \ EQUB &BD, &52, &08, &F0, &CB, &C9, &01, &D0 \ EQUB &F6, &8A, &0A, &A8, &B9, &76, &1A, &85 \ EQUB &05, &B9, &77, &1A, &85, &06, &A0, &20 \ EQUB &B1, &05, &10, &E3, &29, &7F, &4A, &C5 \ EQUB &97, &90, &DC, &F0, &09, &E9, &01, &0A \ EQUB &09, &80, &91, &05, &D0, &D1, &A9, &00 \ EQUB &91, &05, &F0, &CB, &86, &97, &A5, &44 \ EQUB &C5, &97, &D0, &0A, &A0, &0C, &20, &62 \ EQUB &45, &A9, &C8, &20, &C7, &57, &A4, &97 \ EQUB &BE, &52, &08, &E0, &02, &F0, &96, &E0 \ EQUB &1F, &D0, &08, &AD, &A4, &08, &09 \ \ELIF _EXECUTIVE \ \ EQUB &A5, &19, &8D, &FC, &08, &A5, &1A, &8D \ These bytes appear to be \ EQUB &FD, &08, &60, &A6, &83, &20, &8D, &4B \ unused and just contain random \ EQUB &A6, &83, &4C, &D8, &12, &20, &EB, &4C \ workspace noise left over from \ EQUB &20, &9B, &43, &8D, &53, &08, &8D, &69 \ the BBC Micro assembly process \ EQUB &08, &20, &A7, &45, &A9, &06, &85, &4A \ EQUB &A9, &81, &4C, &E6, &44, &A2, &FF, &E8 \ EQUB &BD, &52, &08, &F0, &CB, &C9, &01, &D0 \ EQUB &F6, &8A, &0A, &A8, &B9, &86, &1A, &85 \ EQUB &05, &B9, &87, &1A, &85, &06, &A0, &20 \ EQUB &B1, &05, &10, &E3, &29, &7F, &4A, &C5 \ EQUB &97, &90 \ \ELIF _SOURCE_DISC \ \ EQUB &A5, &19, &8D, &FC, &08, &A5, &1A, &8D \ These bytes appear to be \ EQUB &FD, &08, &60, &A6, &83, &20, &62, &4B \ unused and just contain random \ EQUB &A6, &83, &4C, &D6, &12, &20, &C0, &4C \ workspace noise left over from \ EQUB &20, &70, &43, &8D, &53, &08, &8D, &69 \ the BBC Micro assembly process \ EQUB &08, &20, &7C, &45, &A9, &06, &85, &4A \ EQUB &A9, &81, &4C, &BB, &44, &A2, &FF, &E8 \ EQUB &BD, &52, &08, &F0, &CB, &C9, &01, &D0 \ EQUB &F6, &8A, &0A, &A8, &B9, &76, &1A, &85 \ EQUB &05, &B9, &77, &1A, &85, &06, &A0, &20 \ EQUB &B1, &05, &10, &E3, &29, &7F, &4A, &C5 \ EQUB &97, &90, &DC, &F0, &09, &E9, &01, &0A \ EQUB &09, &80, &91, &05, &D0, &D1, &A9, &00 \ EQUB &91, &05, &F0, &CB, &86, &97, &A5, &44 \ EQUB &C5, &97, &D0, &0A, &A0, &0C, &20, &5C \ EQUB &45, &A9, &C8, &20, &BE, &57, &A4, &97 \ EQUB &BE, &52, &08, &E0, &02, &F0, &96, &E0 \ EQUB &1F, &D0, &08, &AD, &A4, &08, &09, &02 \ EQUB &8D, &A4, &08, &E0, &0F, &F0, &08, &E0 \ \ENDIF \ \ELSE \ \ALIGN 256 \ Align the log tables so they start on page boundaries \ \ENDIF \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: log \ Type: Variable \ Category: Maths (Arithmetic) \ Summary: Binary logarithm table (high byte) \ \ ------------------------------------------------------------------------------ \ \ At byte n, the table contains the high byte of: \ \ &2000 * log10(n) / log10(2) = 32 * 256 * log10(n) / log10(2) \ \ where log10 is the logarithm to base 10. The change-of-base formula says that: \ \ log2(n) = log10(n) / log10(2) \ \ so byte n contains the high byte of: \ \ 32 * log2(n) * 256 \ \ ****************************************************************************** .log IF _MATCH_ORIGINAL_BINARIES IF _SNG45 EQUB &02 \ This byte appears to be unused and just contains \ random workspace noise left over from the BBC Micro \ assembly process ELIF _EXECUTIVE EQUB &DC \ This byte appears to be unused and just contains \ random workspace noise left over from the BBC Micro \ assembly process ELIF _SOURCE_DISC EQUB &03 \ This byte appears to be unused and just contains \ random workspace noise left over from the BBC Micro \ assembly process ENDIF EQUB &00, &20, &32, &40, &4A, &52, &59 EQUB &5F, &65, &6A, &6E, &72, &76, &79, &7D EQUB &80, &82, &85, &87, &8A, &8C, &8E, &90 EQUB &92, &94, &96, &98, &99, &9B, &9D, &9E EQUB &A0, &A1, &A2, &A4, &A5, &A6, &A7, &A9 EQUB &AA, &AB, &AC, &AD, &AE, &AF, &B0, &B1 EQUB &B2, &B3, &B4, &B5, &B6, &B7, &B8, &B9 EQUB &B9, &BA, &BB, &BC, &BD, &BD, &BE, &BF EQUB &BF, &C0, &C1, &C2, &C2, &C3, &C4, &C4 EQUB &C5, &C6, &C6, &C7, &C7, &C8, &C9, &C9 EQUB &CA, &CA, &CB, &CC, &CC, &CD, &CD, &CE EQUB &CE, &CF, &CF, &D0, &D0, &D1, &D1, &D2 EQUB &D2, &D3, &D3, &D4, &D4, &D5, &D5, &D5 EQUB &D6, &D6, &D7, &D7, &D8, &D8, &D9, &D9 EQUB &D9, &DA, &DA, &DB, &DB, &DB, &DC, &DC EQUB &DD, &DD, &DD, &DE, &DE, &DE, &DF, &DF EQUB &E0, &E0, &E0, &E1, &E1, &E1, &E2, &E2 EQUB &E2, &E3, &E3, &E3, &E4, &E4, &E4, &E5 EQUB &E5, &E5, &E6, &E6, &E6, &E7, &E7, &E7 EQUB &E7, &E8, &E8, &E8, &E9, &E9, &E9, &EA EQUB &EA, &EA, &EA, &EB, &EB, &EB, &EC, &EC EQUB &EC, &EC, &ED, &ED, &ED, &ED, &EE, &EE EQUB &EE, &EE, &EF, &EF, &EF, &EF, &F0, &F0 EQUB &F0, &F1, &F1, &F1, &F1, &F1, &F2, &F2 EQUB &F2, &F2, &F3, &F3, &F3, &F3, &F4, &F4 EQUB &F4, &F4, &F5, &F5, &F5, &F5, &F5, &F6 EQUB &F6, &F6, &F6, &F7, &F7, &F7, &F7, &F7 EQUB &F8, &F8, &F8, &F8, &F9, &F9, &F9, &F9 EQUB &F9, &FA, &FA, &FA, &FA, &FA, &FB, &FB EQUB &FB, &FB, &FB, &FC, &FC, &FC, &FC, &FC EQUB &FD, &FD, &FD, &FD, &FD, &FD, &FE, &FE EQUB &FE, &FE, &FE, &FF, &FF, &FF, &FF, &FF ELSE SKIP 1 FOR I%, 1, 255 EQUB HI(INT(&2000 * LOG(I%) / LOG(2) + 0.5)) NEXT ENDIF \ ****************************************************************************** \ \ Name: logL \ Type: Variable \ Category: Maths (Arithmetic) \ Summary: Binary logarithm table (low byte) \ \ ------------------------------------------------------------------------------ \ \ Byte n contains the low byte of: \ \ 32 * log2(n) * 256 \ \ ****************************************************************************** .logL IF _MATCH_ORIGINAL_BINARIES IF _SNG45 EQUB &99 \ This byte appears to be unused and just contains \ random workspace noise left over from the BBC Micro \ assembly process ELIF _EXECUTIVE EQUB &08 \ This byte appears to be unused and just contains \ random workspace noise left over from the BBC Micro \ assembly process ELIF _SOURCE_DISC EQUB &85 \ This byte appears to be unused and just contains \ random workspace noise left over from the BBC Micro \ assembly process ENDIF EQUB &00, &00, &B8, &00, &4D, &B8, &D5 EQUB &FF, &70, &4D, &B3, &B8, &6A, &D5, &05 EQUB &00, &CC, &70, &EF, &4D, &8D, &B3, &C1 EQUB &B8, &9A, &6A, &28, &D5, &74, &05, &88 EQUB &00, &6B, &CC, &23, &70, &B3, &EF, &22 EQUB &4D, &71, &8D, &A3, &B3, &BD, &C1, &BF EQUB &B8, &AB, &9A, &84, &6A, &4B, &28, &00 EQUB &D5, &A7, &74, &3E, &05, &C8, &88, &45 EQUB &FF, &B7, &6B, &1D, &CC, &79, &23, &CA EQUB &70, &13, &B3, &52, &EF, &89, &22, &B8 EQUB &4D, &E0, &71, &00, &8D, &19, &A3, &2C EQUB &B3, &39, &BD, &3F, &C1, &40, &BF, &3C EQUB &B8, &32, &AB, &23, &9A, &10, &84, &F7 EQUB &6A, &DB, &4B, &BA, &28, &94, &00, &6B EQUB &D5, &3E, &A7, &0E, &74, &DA, &3E, &A2 EQUB &05, &67, &C8, &29, &88, &E7, &45, &A3 EQUB &00, &5B, &B7, &11, &6B, &C4, &1D, &75 EQUB &CC, &23, &79, &CE, &23, &77, &CA, &1D EQUB &70, &C1, &13, &63, &B3, &03, &52, &A1 EQUB &EF, &3C, &89, &D6, &22, &6D, &B8, &03 EQUB &4D, &96, &E0, &28, &71, &B8, &00, &47 EQUB &8D, &D4, &19, &5F, &A3, &E8, &2C, &70 EQUB &B3, &F6, &39, &7B, &BD, &FE, &3F, &80 EQUB &C1, &01, &40, &80, &BF, &FD, &3C, &7A EQUB &B8, &F5, &32, &6F, &AB, &E7, &23, &5F EQUB &9A, &D5, &10, &4A, &84, &BE, &F7, &31 EQUB &6A, &A2, &DB, &13, &4B, &82, &BA, &F1 EQUB &28, &5E, &94, &CB, &00, &36, &6B, &A0 EQUB &D5, &0A, &3E, &73, &A7, &DA, &0E, &41 EQUB &74, &A7, &DA, &0C, &3E, &70, &A2, &D3 EQUB &05, &36, &67, &98, &C8, &F8, &29, &59 EQUB &88, &B8, &E7, &16, &45, &74, &A3, &D1 ELSE SKIP 1 FOR I%, 1, 255 EQUB LO(INT(&2000 * LOG(I%) / LOG(2) + 0.5)) NEXT ENDIF \ ****************************************************************************** \ \ Name: antilog \ Type: Variable \ Category: Maths (Arithmetic) \ Summary: Binary antilogarithm table \ \ ------------------------------------------------------------------------------ \ \ At byte n, the table contains: \ \ 2^((n / 2 + 128) / 16) / 256 \ \ which equals: \ \ 2^(n / 32 + 8) / 256 \ \ ****************************************************************************** .antilog FOR I%, 0, 255 EQUB HI(INT(2^((I% / 2 + 128) / 16) + 0.5)) NEXT \ ****************************************************************************** \ \ Name: antilogODD \ Type: Variable \ Category: Maths (Arithmetic) \ Summary: Binary antilogarithm table \ \ ------------------------------------------------------------------------------ \ \ At byte n, the table contains: \ \ 2^((n / 2 + 128.25) / 16) / 256 \ \ which equals: \ \ 2^(n / 32 + 8.015625) / 256 = 2^(n / 32 + 8) * 2^(.015625) / 256 \ = (2^(n / 32 + 8) + 1) / 256 \ \ ****************************************************************************** .antilogODD FOR I%, 0, 255 EQUB HI(INT(2^((I% / 2 + 128.25) / 16) + 0.5)) NEXT \ ****************************************************************************** \ \ Name: SHPPT \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw a distant ship as a point rather than a full wireframe \ \ ****************************************************************************** .SHPPT \ --- Mod: Code removed for flicker-free ships: -------> \JSR EE51 \ Call EE51 to remove the ship's wireframe from the \ \ screen, if there is one \ --- End of removed code -----------------------------> JSR PROJ \ Project the ship onto the screen, returning: \ \ * K3(1 0) = the screen x-coordinate \ * K4(1 0) = the screen y-coordinate \ * A = K4+1 ORA K3+1 \ If either of the high bytes of the screen coordinates BNE nono \ are non-zero, jump to nono as the ship is off-screen LDA K4 \ Set A = the y-coordinate of the dot \ --- Mod: Code added for two-player Elite: -----------> BIT splitScreen \ Skip the following if split screen is disabled BPL poin2 SEC \ Move dot into top half of screen by moving it up by a SBC #Y/2 \ quarter screen BCC nono \ If it's now off the top of the screen, jump to nono CMP #Y-2 \ If the y-coordinate is bigger than the y-coordinate of BCS nono \ the bottom of the screen, jump to nono as the ship's \ dot is off the bottom of the space view BIT drawPlayerView \ If this is player 1's view, skip the following BPL poin1 CLC \ This is player 2's view, so move the dot to the bottom ADC #Y \ half of the screen .poin1 STA K4 \ Store the updated y-coordinate JMP poin3 \ Skip the normal checks .poin2 \ --- End of added code -------------------------------> CMP #Y*2-2 \ If the y-coordinate is bigger than the y-coordinate of BCS nono \ the bottom of the screen, jump to nono as the ship's \ dot is off the bottom of the space view \ --- Mod: Code added for two-player Elite: -----------> .poin3 \ --- End of added code -------------------------------> \ --- Mod: Code removed for flicker-free ships: -------> \LDY #2 \ Call Shpt with Y = 2 to set up bytes 1-4 in the ship \JSR Shpt \ lines space, aborting the call to LL9 if the dot is \ \ off the side of the screen. This call sets up the \ \ first row of the dot (i.e. a four-pixel dash) \ \LDY #6 \ Set Y to 6 for the next call to Shpt \ \LDA K4 \ Set A = y-coordinate of dot + 1 (so this is the second \ADC #1 \ row of the two-pixel-high dot) \ \ \ \ The addition works as the Shpt routine clears the C \ \ flag \ --- And replaced by: --------------------------------> JSR Shpt \ Call Shpt with Y = 2 to set up bytes 1-4 in the ship \ lines space, aborting the call to LL9 if the dot is \ off the side of the screen. This call sets up the \ first row of the dot (i.e. a four-pixel dash) LDA K4 \ Set A = y-coordinate of dot + 1 (so this is the second CLC \ row of the two-pixel-high dot) ADC #1 \ --- End of replacement ------------------------------> JSR Shpt \ Call Shpt with Y = 6 to set up bytes 5-8 in the ship \ lines space, aborting the call to LL9 if the dot is \ off the side of the screen. This call sets up the \ second row of the dot (i.e. another four-pixel dash, \ on the row below the first one) LDA #%00001000 \ Set bit 3 of the ship's byte #31 to record that we ORA XX1+31 \ have now drawn something on-screen for this ship STA XX1+31 \ --- Mod: Code removed for flicker-free ships: -------> \LDA #9 \ Set A = 9 so when we call LL18+2 next, byte #0 of the \ \ heap gets set to 9, to cover the 9 bytes we just stuck \ \ the heap \ \JMP LL81+2 \ Call LL81+2 to draw the ship's dot, returning from the \ \ subroutine using a tail call \ \PLA \ Pull the return address from the stack, so the RTS \PLA \ below actually returns from the subroutine that called \ \ LL9 (as we called SHPPT from LL9 with a JMP) \ --- And replaced by: --------------------------------> JMP LL155 \ Jump to LL155 to draw any remaining lines that are \ still in the ship line heap and return from the \ subroutine using a tail call \ --- End of replacement ------------------------------> .nono LDA #%11110111 \ Clear bit 3 of the ship's byte #31 to record that AND XX1+31 \ nothing is being drawn on-screen for this ship STA XX1+31 \ --- Mod: Code removed for flicker-free ships: -------> \RTS \ Return from the subroutine \ --- And replaced by: --------------------------------> JMP LL155 \ Jump to LL155 to draw any remaining lines that are \ still in the ship line heap and return from the \ subroutine using a tail call \ --- End of replacement ------------------------------> .Shpt \ --- Mod: Code removed for flicker-free ships: -------> \ \ This routine sets up four bytes in the ship line heap, \ \ from byte Y-1 to byte Y+2. If the ship's screen point \ \ turns out to be off-screen, then this routine aborts \ \ the entire call to LL9, exiting via nono. The four \ \ bytes define a horizontal 4-pixel dash, for either the \ \ top or the bottom of the ship's dot \ \STA (XX19),Y \ Store A in byte Y of the ship line heap (i.e. Y1) \ \INY \ Store A in byte Y+2 of the ship line heap (i.e. Y2) \INY \STA (XX19),Y \ --- And replaced by: --------------------------------> \ This routine draws a horizontal 4-pixel dash, for \ either the top or the bottom of the ship's dot STA Y1 \ Store A in both y-coordinates, as this is a horizontal STA Y2 \ dash at y-coordinate A \ --- End of replacement ------------------------------> LDA K3 \ Set A = screen x-coordinate of the ship dot \ --- Mod: Code removed for flicker-free ships: -------> \DEY \ Store A in byte Y+1 of the ship line heap (i.e. X2) \STA (XX19),Y \ \ADC #3 \ Set A = screen x-coordinate of the ship dot + 3 \ \BCS nono-2 \ If the addition pushed the dot off the right side of \ \ the screen, jump to nono-2 to return from the parent \ \ subroutine early (i.e. LL9). This works because we \ \ called Shpt from above with a JSR, so nono-2 removes \ \ that return address from the stack, leaving the next \ \ return address exposed. LL9 called SHPPT with a JMP. \ \ so the next return address is the one that was put on \ \ the stack by the original call to LL9. So the RTS in \ \ nono will actually return us from the original call \ \ to LL9, thus aborting the entire drawing process \ \DEY \ Store A in byte Y-1 of the ship line heap (i.e. X1) \DEY \STA (XX19),Y \ \RTS \ Return from the subroutine \ --- And replaced by: --------------------------------> STA X1 \ Store the x-coordinate of the ship dot in X1, as this \ is where the dash starts CLC \ Set A = screen x-coordinate of the ship dot + 3 ADC #3 BCC P%+4 \ If the addition overflowed, set A = 255, the LDA #255 \ x-coordinate of the right edge of the screen STA X2 \ Store the x-coordinate of the ship dot in X1, as this \ is where the dash starts JMP LSPUT \ Draw this edge using smooth animation, by first \ drawing the ship's new line and then erasing the \ corresponding old line from the screen, and return \ from the subroutine using a tail call \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: LL5 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate Q = SQRT(R Q) \ Deep dive: Calculating square roots \ \ ------------------------------------------------------------------------------ \ \ Calculate the following square root: \ \ Q = SQRT(R Q) \ \ ****************************************************************************** .LL5 LDY R \ Set (Y S) = (R Q) LDA Q STA S \ So now to calculate Q = SQRT(Y S) LDX #0 \ Set X = 0, to hold the remainder STX Q \ Set Q = 0, to hold the result LDA #8 \ Set T = 8, to use as a loop counter STA T .LL6 CPX Q \ If X < Q, jump to LL7 BCC LL7 BNE LL8 \ If X > Q, jump to LL8 CPY #64 \ If Y < 64, jump to LL7 with the C flag clear, BCC LL7 \ otherwise fall through into LL8 with the C flag set .LL8 TYA \ Set Y = Y - 64 SBC #64 \ TAY \ This subtraction will work as we know C is set from \ the BCC above, and the result will not underflow as we \ already checked that Y >= 64, so the C flag is also \ set for the next subtraction TXA \ Set X = X - Q SBC Q TAX .LL7 ROL Q \ Shift the result in Q to the left, shifting the C flag \ into bit 0 and bit 7 into the C flag ASL S \ Shift the dividend in (Y S) to the left, inserting TYA \ bit 7 from above into bit 0 ROL A TAY TXA \ Shift the remainder in X to the left ROL A TAX ASL S \ Shift the dividend in (Y S) to the left TYA ROL A TAY TXA \ Shift the remainder in X to the left ROL A TAX DEC T \ Decrement the loop counter BNE LL6 \ Loop back to LL6 until we have done 8 loops RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LL28 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate R = 256 * A / Q \ Deep dive: Multiplication and division using logarithms \ \ ------------------------------------------------------------------------------ \ \ Calculate the following, where A < Q: \ \ R = 256 * A / Q \ \ This is a sister routine to LL61, which does the division when A >= Q. \ \ If A >= Q then 255 is returned and the C flag is set to indicate an overflow \ (the C flag is clear if the division was a success). \ \ The result is returned in one byte as the result of the division multiplied \ by 256, so we can return fractional results using integers. \ \ This routine uses the same logarithm algorithm that's documented in FMLTU, \ except it subtracts the logarithm values, to do a division instead of a \ multiplication. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag Set if the answer is too big for one byte, clear if the \ division was a success \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ LL28+4 Skips the A >= Q check and always returns with C flag \ cleared, so this can be called if we know the division \ will work \ \ LL31 Skips the A >= Q check and does not set the R counter, \ so this can be used for jumping straight into the \ division loop if R is already set to 254 and we know the \ division will work \ \ ****************************************************************************** .LL28 CMP Q \ If A >= Q, then the answer will not fit in one byte, BCS LL2 \ so jump to LL2 to return 255 STA widget \ Store A in widget, so now widget = argument A TAX \ Transfer A into X, so now X = argument A BEQ LLfix \ If A = 0, jump to LLfix to return a result of 0, as \ 0 * Q / 256 is always 0 \ We now want to calculate log(A) - log(Q), first adding \ the low bytes (from the logL table), and then the high \ bytes (from the log table) LDA logL,X \ Set A = low byte of log(X) \ = low byte of log(A) (as we set X to A above) LDX Q \ Set X = Q SEC \ Set A = A - low byte of log(Q) SBC logL,X \ = low byte of log(A) - low byte of log(Q) BMI noddlog \ If the subtraction is negative, jump to noddlog LDX widget \ Set A = high byte of log(A) - high byte of log(Q) LDA log,X LDX Q SBC log,X BCS LL2 \ If the subtraction fitted into one byte and didn't \ underflow, then log(A) - log(Q) < 256, so we jump to \ LL2 return a result of 255 TAX \ Otherwise we return the A-th entry from the antilog LDA antilog,X \ table .LLfix STA R \ Set the result in R to the value of A RTS \ Return from the subroutine .noddlog LDX widget \ Set A = high byte of log(A) - high byte of log(Q) LDA log,X LDX Q SBC log,X BCS LL2 \ If the subtraction fitted into one byte and didn't \ underflow, then log(A) - log(Q) < 256, so we jump to \ LL2 to return a result of 255 TAX \ Otherwise we return the A-th entry from the antilogODD LDA antilogODD,X \ table STA R \ Set the result in R to the value of A RTS \ Return from the subroutine .LL31 ASL A \ Shift A to the left BCS LL29 \ If bit 7 of A was set, then jump straight to the \ subtraction CMP Q \ If A < Q, skip the following subtraction BCC P%+4 SBC Q \ A >= Q, so set A = A - Q ROL R \ Rotate the counter in R to the left, and catch the \ result bit into bit 0 (which will be a 0 if we didn't \ do the subtraction, or 1 if we did) BCS LL31 \ If we still have set bits in R, loop back to LL31 to \ do the next iteration of 7 RTS \ R left with remainder of division .LL29 SBC Q \ A >= Q, so set A = A - Q SEC \ Set the C flag to rotate into the result in R ROL R \ Rotate the counter in R to the left, and catch the \ result bit into bit 0 (which will be a 0 if we didn't \ do the subtraction, or 1 if we did) BCS LL31 \ If we still have set bits in R, loop back to LL31 to \ do the next iteration of 7 LDA R \ Set A to the remainder in R RTS \ Return from the subroutine with R containing the \ remainder of the division .LL2 LDA #255 \ The division is very close to 1, so return the closest STA R \ possible answer to 256, i.e. R = 255 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LL38 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (S A) = (S R) + (A Q) \ \ ------------------------------------------------------------------------------ \ \ Calculate the following between sign-magnitude numbers: \ \ (S A) = (S R) + (A Q) \ \ where the sign bytes only contain the sign bits, not magnitudes. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ C flag Set if the addition overflowed, clear otherwise \ \ ****************************************************************************** .LL38 EOR S \ If the sign of A * S is negative, skip to LL35, as BMI LL39 \ A and S have different signs so we need to subtract LDA Q \ Otherwise set A = R + Q, which is the result we need, CLC \ as S already contains the correct sign ADC R RTS \ Return from the subroutine .LL39 LDA R \ Set A = R - Q SEC SBC Q BCC P%+4 \ If the subtraction underflowed, skip the next two \ instructions so we can negate the result CLC \ Otherwise the result is correct, and S contains the \ correct sign of the result as R is the dominant side \ of the subtraction, so clear the C flag RTS \ And return from the subroutine \ If we get here we need to negate both the result and \ the sign in S, as both are the wrong sign PHA \ Store the result of the subtraction on the stack LDA S \ Flip the sign of S EOR #%10000000 STA S PLA \ Restore the subtraction result into A EOR #%11111111 \ Negate the result in A using two's complement, i.e. ADC #1 \ set A = ~A + 1 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LL51 \ Type: Subroutine \ Category: Maths (Geometry) \ Summary: Calculate the dot product of XX15 and XX16 \ \ ------------------------------------------------------------------------------ \ \ Calculate the following dot products: \ \ XX12(1 0) = XX15(5 0) . XX16(5 0) \ XX12(3 2) = XX15(5 0) . XX16(11 6) \ XX12(5 4) = XX15(5 0) . XX16(12 17) \ \ storing the results as sign-magnitude numbers in XX12 through XX12+5. \ \ When called from part 5 of LL9, XX12 contains the vector [x y z] to the ship \ we're drawing, and XX16 contains the orientation vectors, so it returns: \ \ [ x ] [ sidev_x ] [ x ] [ roofv_x ] [ x ] [ nosev_x ] \ [ y ] . [ sidev_y ] [ y ] . [ roofv_y ] [ y ] . [ nosev_y ] \ [ z ] [ sidev_z ] [ z ] [ roofv_z ] [ z ] [ nosev_z ] \ \ When called from part 6 of LL9, XX12 contains the vector [x y z] of the vertex \ we're analysing, and XX16 contains the transposed orientation vectors with \ each of them containing the x, y and z elements of the original vectors, so it \ returns: \ \ [ x ] [ sidev_x ] [ x ] [ sidev_y ] [ x ] [ sidev_z ] \ [ y ] . [ roofv_x ] [ y ] . [ roofv_y ] [ y ] . [ roofv_z ] \ [ z ] [ nosev_x ] [ z ] [ nosev_y ] [ z ] [ nosev_z ] \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ XX15(1 0) The ship (or vertex)'s x-coordinate as (x_sign x_lo) \ \ XX15(3 2) The ship (or vertex)'s y-coordinate as (y_sign y_lo) \ \ XX15(5 4) The ship (or vertex)'s z-coordinate as (z_sign z_lo) \ \ XX16 to XX16+5 The scaled sidev (or _x) vector, with: \ \ * x, y, z magnitudes in XX16, XX16+2, XX16+4 \ \ * x, y, z signs in XX16+1, XX16+3, XX16+5 \ \ XX16+6 to XX16+11 The scaled roofv (or _y) vector, with: \ \ * x, y, z magnitudes in XX16+6, XX16+8, XX16+10 \ \ * x, y, z signs in XX16+7, XX16+9, XX16+11 \ \ XX16+12 to XX16+17 The scaled nosev (or _z) vector, with: \ \ * x, y, z magnitudes in XX16+12, XX16+14, XX16+16 \ \ * x, y, z signs in XX16+13, XX16+15, XX16+17 \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ XX12(1 0) The dot product of [x y z] vector with the sidev (or _x) \ vector, with the sign in XX12+1 and magnitude in XX12 \ \ XX12(3 2) The dot product of [x y z] vector with the roofv (or _y) \ vector, with the sign in XX12+3 and magnitude in XX12+2 \ \ XX12(5 4) The dot product of [x y z] vector with the nosev (or _z) \ vector, with the sign in XX12+5 and magnitude in XX12+4 \ \ ****************************************************************************** .LL51 LDX #0 \ Set X = 0, which will contain the offset of the vector \ to use in the calculation, increasing by 6 for each \ new vector LDY #0 \ Set Y = 0, which will contain the offset of the \ result bytes in XX12, increasing by 2 for each new \ result .ll51 LDA XX15 \ Set Q = x_lo STA Q LDA XX16,X \ Set A = |sidev_x| JSR FMLTU \ Set T = A * Q / 256 STA T \ = |sidev_x| * x_lo / 256 LDA XX15+1 \ Set S to the sign of x_sign * sidev_x EOR XX16+1,X STA S LDA XX15+2 \ Set Q = y_lo STA Q LDA XX16+2,X \ Set A = |sidev_y| JSR FMLTU \ Set Q = A * Q / 256 STA Q \ = |sidev_y| * y_lo / 256 LDA T \ Set R = T STA R \ = |sidev_x| * x_lo / 256 LDA XX15+3 \ Set A to the sign of y_sign * sidev_y EOR XX16+3,X JSR LL38 \ Set (S T) = (S R) + (A Q) STA T \ = |sidev_x| * x_lo + |sidev_y| * y_lo LDA XX15+4 \ Set Q = z_lo STA Q LDA XX16+4,X \ Set A = |sidev_z| JSR FMLTU \ Set Q = A * Q / 256 STA Q \ = |sidev_z| * z_lo / 256 LDA T \ Set R = T STA R \ = |sidev_x| * x_lo + |sidev_y| * y_lo LDA XX15+5 \ Set A to the sign of z_sign * sidev_z EOR XX16+5,X JSR LL38 \ Set (S A) = (S R) + (A Q) \ = |sidev_x| * x_lo + |sidev_y| * y_lo \ + |sidev_z| * z_lo STA XX12,Y \ Store the result in XX12+Y(1 0) LDA S STA XX12+1,Y INY \ Set Y = Y + 2 INY TXA \ Set X = X + 6 CLC ADC #6 TAX CMP #17 \ If X < 17, loop back to ll51 for the next vector BCC ll51 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LL9 (Part 1 of 12) \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw ship: Check if ship is exploding, check if ship is in front \ Deep dive: Drawing ships \ \ ------------------------------------------------------------------------------ \ \ This routine draws the current ship on the screen. This part checks to see if \ the ship is exploding, or if it should start exploding, and if it does it sets \ things up accordingly. \ \ It also does some basic checks to see if we can see the ship, and if not it \ removes it from the screen. \ \ In this code, XX1 is used to point to the current ship's data block at INWK \ (the two labels are interchangeable). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ XX1 XX1 shares its location with INWK, which contains the \ zero-page copy of the data block for this ship from the \ K% workspace \ \ INF The address of the data block for this ship in workspace \ K% \ \ XX19(1 0) XX19(1 0) shares its location with INWK(34 33), which \ contains the ship line heap address pointer \ \ XX0 The address of the blueprint for this ship \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X If NEEDKEY is non-zero, scan the keyboard for a key \ press and return the internal key number in X (or 0 for \ no key press) \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ EE51 Remove the current ship from the screen, called from \ SHPPT before drawing the ship as a point \ \ ****************************************************************************** .LL25 JMP PLANET \ Jump to the PLANET routine, returning from the \ subroutine using a tail call .LL9 LDX TYPE \ If the ship type is negative then this indicates a BMI LL25 \ planet or sun, so jump to PLANET via LL25 above LDA shpcol,X \ Set A to the ship colour for this type, from the X-th \ entry in the shpcol table \ --- Mod: Code added for two-player Elite: -----------> BIT titleScreen \ If this is the title screen, force the ship colour to BPL P%+4 \ the configured colour LDA #SHIPCOL \ --- End of added code -------------------------------> JSR DOCOL \ Send a #SETCOL command to the I/O processor to switch \ to this colour LDA #31 \ Set XX4 = 31 to store the ship's distance for later STA XX4 \ comparison with the visibility distance. We will \ update this value below with the actual ship's \ distance if it turns out to be visible on-screen \ --- Mod: Code added for flicker-free ships: ---------> \ --- Mod: Code added for red enemy lasers: -----------> \ We now set things up for smooth ship plotting, by \ setting the following: \ \ LSNUM = offset to the first coordinate in the ship's \ line heap \ \ LSNUM2 = the number of bytes in the heap for the \ ship that's currently on-screen (or 0 if \ there is no ship currently on-screen) LDY #5 \ Set LSNUM = 5, the offset of the first set of line STY LSNUM \ coordinates in the ship line heap, after the four \ coordinates for the laser line LDY #0 \ Set Y to 0 \ --- End of added code -------------------------------> LDA #%00001000 \ If bit 3 of the ship's byte #31 is set, then the ship BIT INWK+31 \ is currently being drawn on-screen, so skip the BNE P%+5 \ following two instructions LDA #0 \ The ship is not being drawn on screen, so set A = 0 \ so that LSNUM2 gets set to 0 below (as there are no \ existing coordinates on the ship line heap for this \ ship) EQUB &2C \ Skip the next instruction by turning it into \ &2C &B1 &BD, or BIT &BDB1 which does nothing apart \ from affect the flags LDA (XX19),Y \ Set LSNUM2 to the first byte of the ship's line heap, STA LSNUM2 \ which contains the number of bytes in the heap \ --- End of added code -------------------------------> LDA NEWB \ If bit 7 of the ship's NEWB flags is set, then the BMI EE51 \ ship has been scooped or has docked, so jump down to \ EE51 to redraw its wireframe, to remove it from the \ screen LDA #%00100000 \ If bit 5 of the ship's byte #31 is set, then the ship BIT XX1+31 \ is currently exploding, so jump down to EE28 BNE EE28 BPL EE28 \ If bit 7 of the ship's byte #31 is clear then the ship \ has not just been killed, so jump down to EE28 \ Otherwise bit 5 is clear and bit 7 is set, so the ship \ is not yet exploding but it has been killed, so we \ need to start an explosion ORA XX1+31 \ Clear bits 6 and 7 of the ship's byte #31, to stop the AND #%00111111 \ ship from firing its laser and to mark it as no longer STA XX1+31 \ having just been killed LDA #0 \ Set the ship's acceleration in byte #31 to 0, updating LDY #28 \ the byte in the workspace K% data block so we don't STA (INF),Y \ have to copy it back from INWK later LDY #30 \ Set the ship's pitch counter in byte #30 to 0, to stop STA (INF),Y \ the ship from pitching JSR EE51 \ Call EE51 to remove the ship from the screen \ We now need to set up a new explosion cloud. We \ initialise it with a size of 18 (which gets increased \ by 4 every time the cloud gets redrawn), and the \ explosion count (i.e. the number of particles in the \ explosion), which go into bytes 1 and 2 of the ship \ line heap. See DOEXP for more details of explosion \ clouds LDY #1 \ Set byte #1 of the ship line heap to 18, the initial LDA #18 \ size of the explosion cloud STA (XX19),Y LDY #7 \ Fetch byte #7 from the ship's blueprint, which LDA (XX0),Y \ determines the explosion count (i.e. the number of LDY #2 \ vertices used as origins for explosion clouds), and STA (XX19),Y \ store it in byte #2 of the ship line heap \ The following loop sets bytes 3-6 of the of the ship \ line heap to random numbers .EE55 INY \ Increment Y (so the loop starts at 3) JSR DORND \ Set A and X to random numbers STA (XX19),Y \ Store A in the Y-th byte of the ship line heap CPY #6 \ Loop back until we have randomised the 6th byte BNE EE55 .EE28 LDA XX1+8 \ Set A = z_sign .EE49 BPL LL10 \ If A is positive, i.e. the ship is in front of us, \ jump down to LL10 .LL14 \ The following removes the ship from the screen by \ redrawing it (or, if it is exploding, by redrawing the \ explosion cloud). We call it when the ship is no \ longer on-screen, is too far away to be fully drawn, \ and so on LDA XX1+31 \ If bit 5 of the ship's byte #31 is clear, then the AND #%00100000 \ ship is not currently exploding, so jump down to EE51 BEQ EE51 \ to redraw its wireframe LDA XX1+31 \ The ship is exploding, so clear bit 3 of the ship's AND #%11110111 \ byte #31 to denote that the ship is no longer being STA XX1+31 \ drawn on-screen JMP DOEXP \ Jump to DOEXP to display the explosion cloud, which \ will remove it from the screen, returning from the \ subroutine using a tail call .EE51 LDA #%00001000 \ If bit 3 of the ship's byte #31 is clear, then there BIT XX1+31 \ is already nothing being shown for this ship, so BEQ LL10-1 \ return from the subroutine (as LL10-1 contains an RTS) EOR XX1+31 \ Otherwise flip bit 3 of byte #31 and store it (which STA XX1+31 \ clears bit 3 as we know it was set before the EOR), so \ this sets this ship as no longer being drawn on-screen \ --- Mod: Code added for two-player Elite: -----------> JSR RemoveLaserLine \ Check whether there is a laser line on-screen, and if \ there is, remove it \ --- End of added code -------------------------------> JMP LL155 \ Jump to LL155 to draw the ship, which removes it from \ the screen, returning from the subroutine using a \ tail call RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LL9 (Part 2 of 12) \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw ship: Check if ship is in field of view, close enough to draw \ Deep dive: Drawing ships \ \ ------------------------------------------------------------------------------ \ \ This part checks whether the ship is in our field of view, and whether it is \ close enough to be fully drawn (if not, we jump to SHPPT to draw it as a dot). \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ LL10-1 Contains an RTS \ \ ****************************************************************************** .LL10 LDA XX1+7 \ Set A = z_hi CMP #192 \ If A >= 192 then the ship is a long way away, so jump BCS LL14 \ to LL14 to remove the ship from the screen LDA XX1 \ If x_lo >= z_lo, set the C flag, otherwise clear it CMP XX1+6 LDA XX1+1 \ Set A = x_hi - z_hi using the carry from the low SBC XX1+7 \ bytes, which sets the C flag as if we had done a full \ two-byte subtraction (x_hi x_lo) - (z_hi z_lo) BCS LL14 \ If the C flag is set then x >= z, so the ship is \ further to the side than it is in front of us, so it's \ outside our viewing angle of 45 degrees, and we jump \ to LL14 to remove it from the screen LDA XX1+3 \ If y_lo >= z_lo, set the C flag, otherwise clear it CMP XX1+6 LDA XX1+4 \ Set A = y_hi - z_hi using the carry from the low SBC XX1+7 \ bytes, which sets the C flag as if we had done a full \ two-byte subtraction (y_hi y_lo) - (z_hi z_lo) BCS LL14 \ If the C flag is set then y >= z, so the ship is \ further above us than it is in front of us, so it's \ outside our viewing angle of 45 degrees, and we jump \ to LL14 to remove it from the screen LDY #6 \ Fetch byte #6 from the ship's blueprint into X, which LDA (XX0),Y \ is the number * 4 of the vertex used for the ship's TAX \ laser LDA #255 \ Set bytes X and X+1 of the XX3 heap to 255. We're STA XX3,X \ going to use XX3 to store the screen coordinates of STA XX3+1,X \ all the visible vertices of this ship, so setting the \ laser vertex to 255 means that if we don't update this \ vertex with its screen coordinates in parts 6 and 7, \ this vertex's entry in the XX3 heap will still be 255, \ which we can check in part 9 to see if the laser \ vertex is visible (and therefore whether we should \ draw laser lines if the ship is firing at us) LDA XX1+6 \ Set (A T) = (z_hi z_lo) STA T LDA XX1+7 LSR A \ Set (A T) = (A T) / 8 ROR T LSR A ROR T LSR A ROR T LSR A \ If A >> 4 is non-zero, i.e. z_hi >= 16, jump to LL13 BNE LL13 \ as the ship is possibly far away enough to be shown as \ a dot LDA T \ Otherwise the C flag contains the previous bit 0 of A, ROR A \ which could have been set, so rotate A right four LSR A \ times so it's in the form %000xxxxx, i.e. z_hi reduced LSR A \ to a maximum value of 31 LSR A STA XX4 \ Store A in XX4, which is now the distance of the ship \ we can use for visibility testing BPL LL17 \ Jump down to LL17 (this BPL is effectively a JMP as we \ know bit 7 of A is definitely clear) .LL13 \ If we get here then the ship is possibly far enough \ away to be shown as a dot LDY #13 \ Fetch byte #13 from the ship's blueprint, which gives LDA (XX0),Y \ the ship's visibility distance, beyond which we show \ the ship as a dot CMP XX1+7 \ If z_hi <= the visibility distance, skip to LL17 to BCS LL17 \ draw the ship fully, rather than as a dot, as it is \ closer than the visibility distance LDA #%00100000 \ If bit 5 of the ship's byte #31 is set, then the AND XX1+31 \ ship is currently exploding, so skip to LL17 to draw BNE LL17 \ the ship's explosion cloud JMP SHPPT \ Otherwise jump to SHPPT to draw the ship as a dot, \ returning from the subroutine using a tail call \ ****************************************************************************** \ \ Name: LL9 (Part 3 of 12) \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw ship: Set up orientation vector, ship coordinate variables \ Deep dive: Drawing ships \ \ ------------------------------------------------------------------------------ \ \ This part sets up the following variable blocks: \ \ * XX16 contains the orientation vectors, divided to normalise them \ \ * XX18 contains the ship's x, y and z coordinates in space \ \ ****************************************************************************** .LL17 LDX #5 \ First we copy the three orientation vectors into XX16, \ so set up a counter in X for the 6 bytes in each \ vector .LL15 LDA XX1+21,X \ Copy the X-th byte of sidev to the X-th byte of XX16 STA XX16,X LDA XX1+15,X \ Copy the X-th byte of roofv to XX16+6 to the X-th byte STA XX16+6,X \ of XX16+6 LDA XX1+9,X \ Copy the X-th byte of nosev to XX16+12 to the X-th STA XX16+12,X \ byte of XX16+12 DEX \ Decrement the counter BPL LL15 \ Loop back to copy the next byte of each vector, until \ we have the following: \ \ * XX16(1 0) = sidev_x \ * XX16(3 2) = sidev_y \ * XX16(5 4) = sidev_z \ \ * XX16(7 6) = roofv_x \ * XX16(9 8) = roofv_y \ * XX16(11 10) = roofv_z \ \ * XX16(13 12) = nosev_x \ * XX16(15 14) = nosev_y \ * XX16(17 16) = nosev_z LDA #197 \ Set Q = 197 STA Q LDY #16 \ Set Y to be a counter that counts down by 2 each time, \ starting with 16, then 14, 12 and so on. We use this \ to work through each of the coordinates in each of the \ orientation vectors .LL21 LDA XX16,Y \ Set A = the low byte of the vector coordinate, e.g. \ nosev_z_lo when Y = 16 ASL A \ Shift bit 7 into the C flag LDA XX16+1,Y \ Set A = the high byte of the vector coordinate, e.g. \ nosev_z_hi when Y = 16 ROL A \ Rotate A left, incorporating the C flag, so A now \ contains the original high byte, doubled, and without \ a sign bit, e.g. A = |nosev_z_hi| * 2 JSR LL28 \ Call LL28 to calculate: \ \ R = 256 * A / Q \ \ so, for nosev, this would be: \ \ R = 256 * |nosev_z_hi| * 2 / 197 \ = 2.6 * |nosev_z_hi| LDX R \ Store R in the low byte's location, so we can keep the STX XX16,Y \ old, unscaled high byte intact for the sign DEY \ Decrement the loop counter twice DEY BPL LL21 \ Loop back for the next vector coordinate until we have \ divided them all \ By this point, the vectors have been turned into \ scaled magnitudes, so we have the following: \ \ * XX16 = scaled |sidev_x| \ * XX16+2 = scaled |sidev_y| \ * XX16+4 = scaled |sidev_z| \ \ * XX16+6 = scaled |roofv_x| \ * XX16+8 = scaled |roofv_y| \ * XX16+10 = scaled |roofv_z| \ \ * XX16+12 = scaled |nosev_x| \ * XX16+14 = scaled |nosev_y| \ * XX16+16 = scaled |nosev_z| LDX #8 \ Next we copy the ship's coordinates into XX18, so set \ up a counter in X for 9 bytes .ll91 LDA XX1,X \ Copy the X-th byte from XX1 to XX18 STA XX18,X DEX \ Decrement the loop counter BPL ll91 \ Loop back for the next byte until we have copied all \ three coordinates \ So we now have the following: \ \ * XX18(2 1 0) = (x_sign x_hi x_lo) \ \ * XX18(5 4 3) = (y_sign y_hi y_lo) \ \ * XX18(8 7 6) = (z_sign z_hi z_lo) LDA #255 \ Set the 15th byte of XX2 to 255, so that face 15 is STA XX2+15 \ always visible. No ship definitions actually have this \ number of faces, but this allows us to force a vertex \ to always be visible by associating it with face 15 \ (see the ship blueprints for the Cobra Mk III at \ SHIP_COBRA_MK_3 and the asteroid at SHIP_ASTEROID for \ examples of vertices that are associated with face 15) LDY #12 \ Set Y = 12 to point to the ship blueprint byte #12, LDA XX1+31 \ If bit 5 of the ship's byte #31 is clear, then the AND #%00100000 \ ship is not currently exploding, so jump down to EE29 BEQ EE29 \ to skip the following \ Otherwise we fall through to set up the visibility \ block for an exploding ship \ ****************************************************************************** \ \ Name: LL9 (Part 4 of 12) \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw ship: Set visibility for exploding ship (all faces visible) \ Deep dive: Drawing ships \ \ ------------------------------------------------------------------------------ \ \ This part sets up the visibility block in XX2 for a ship that is exploding. \ \ The XX2 block consists of one byte for each face in the ship's blueprint, \ which holds the visibility of that face. Because the ship is exploding, we \ want to set all the faces to be visible. A value of 255 in the visibility \ table means the face is visible, so the following code sets each face to 255 \ and then skips over the face visibility calculations that we would apply to a \ non-exploding ship. \ \ ****************************************************************************** LDA (XX0),Y \ Fetch byte #12 of the ship's blueprint, which contains \ the number of faces * 4 LSR A \ Set X = A / 4 LSR A \ = the number of faces TAX LDA #255 \ Set A = 255 .EE30 STA XX2,X \ Set the X-th byte of XX2 to 255 DEX \ Decrement the loop counter BPL EE30 \ Loop back for the next byte until there is one byte \ set to 255 for each face INX \ Set XX4 = 0 for the distance value we use to test STX XX4 \ for visibility, so we always shows everything .LL41 JMP LL42 \ Jump to LL42 to skip the face visibility calculations \ as we don't need to do them now we've set up the XX2 \ block for the explosion \ ****************************************************************************** \ \ Name: LL9 (Part 5 of 12) \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw ship: Calculate the visibility of each of the ship's faces \ Deep dive: Drawing ships \ Back-face culling \ \ ****************************************************************************** .EE29 LDA (XX0),Y \ We set Y to 12 above before jumping down to EE29, so \ this fetches byte #12 of the ship's blueprint, which \ contains the number of faces * 4 BEQ LL41 \ If there are no faces in this ship, jump to LL42 (via \ LL41) to skip the face visibility calculations STA XX20 \ Set A = the number of faces * 4 LDY #18 \ Fetch byte #18 of the ship's blueprint, which contains LDA (XX0),Y \ the factor by which we scale the face normals, into X TAX LDA XX18+7 \ Set A = z_hi .LL90 TAY \ Set Y = z_hi BEQ LL91 \ If z_hi = 0 then jump to LL91 \ The following is a loop that jumps back to LL90+3, \ i.e. here. LL90 is only used for this loop, so it's a \ bit of a strange use of the label here INX \ Increment the scale factor in X LSR XX18+4 \ Divide (y_hi y_lo) by 2 ROR XX18+3 LSR XX18+1 \ Divide (x_hi x_lo) by 2 ROR XX18 LSR A \ Divide (z_hi z_lo) by 2 (as A contains z_hi) ROR XX18+6 TAY \ Set Y = z_hi BNE LL90+3 \ If Y is non-zero, loop back to LL90+3 to divide the \ three coordinates until z_hi is 0 .LL91 \ By this point z_hi is 0 and X contains the number of \ right shifts we had to do, plus the scale factor from \ the blueprint STX XX17 \ Store the updated scale factor in XX17 LDA XX18+8 \ Set XX15+5 = z_sign STA XX15+5 LDA XX18 \ Set XX15(1 0) = (x_sign x_lo) STA XX15 LDA XX18+2 STA XX15+1 LDA XX18+3 \ Set XX15(3 2) = (y_sign y_lo) STA XX15+2 LDA XX18+5 STA XX15+3 LDA XX18+6 \ Set XX15+4 = z_lo, so now XX15(5 4) = (z_sign z_lo) STA XX15+4 JSR LL51 \ Call LL51 to set XX12 to the dot products of XX15 and \ XX16, which we'll call dot_sidev, dot_roofv and \ dot_nosev: \ \ XX12(1 0) = [x y z] . sidev \ = (dot_sidev_sign dot_sidev_lo) \ = dot_sidev \ \ XX12(3 2) = [x y z] . roofv \ = (dot_roofv_sign dot_roofv_lo) \ = dot_roofv \ \ XX12(5 4) = [x y z] . nosev \ = (dot_nosev_sign dot_nosev_lo) \ = dot_nosev LDA XX12 \ Set XX18(2 0) = dot_sidev STA XX18 LDA XX12+1 STA XX18+2 LDA XX12+2 \ Set XX18(5 3) = dot_roofv STA XX18+3 LDA XX12+3 STA XX18+5 LDA XX12+4 \ Set XX18(8 6) = dot_nosev STA XX18+6 LDA XX12+5 STA XX18+8 LDY #4 \ Fetch byte #4 of the ship's blueprint, which contains LDA (XX0),Y \ the low byte of the offset to the faces data CLC \ Set V = low byte faces offset + XX0 ADC XX0 STA V LDY #17 \ Fetch byte #17 of the ship's blueprint, which contains LDA (XX0),Y \ the high byte of the offset to the faces data ADC XX0+1 \ Set V+1 = high byte faces offset + XX0+1 STA V+1 \ \ So V(1 0) now points to the start of the faces data \ for this ship LDY #0 \ We're now going to loop through all the faces for this \ ship, so set a counter in Y, starting from 0, which we \ will increment by 4 each loop to step through the \ four bytes of data for each face .LL86 LDA (V),Y \ Fetch byte #0 for this face into A, so: \ \ A = %xyz vvvvv, where: \ \ * Bits 0-4 = visibility distance, beyond which the \ face is always shown \ \ * Bits 7-5 = the sign bits of normal_x, normal_y \ and normal_z STA XX12+1 \ Store byte #0 in XX12+1, so XX12+1 now has the sign of \ normal_x AND #%00011111 \ Extract bits 0-4 to give the visibility distance CMP XX4 \ If XX4 <= the visibility distance, where XX4 contains BCS LL87 \ the ship's z-distance reduced to 0-31 (which we set in \ part 2), skip to LL87 as this face is close enough \ that we have to test its visibility using the face \ normals \ Otherwise this face is within range and is therefore \ always shown TYA \ Set X = Y / 4 LSR A \ = the number of this face * 4 /4 LSR A \ = the number of this face TAX LDA #255 \ Set the X-th byte of XX2 to 255 to denote that this STA XX2,X \ face is visible TYA \ Set Y = Y + 4 to point to the next face ADC #4 TAY JMP LL88 \ Jump down to LL88 to skip the following, as we don't \ need to test the face normals .LL87 LDA XX12+1 \ Fetch byte #0 for this face into A ASL A \ Shift A left and store it, so XX12+3 now has the sign STA XX12+3 \ of normal_y ASL A \ Shift A left and store it, so XX12+5 now has the sign STA XX12+5 \ of normal_z INY \ Increment Y to point to byte #1 LDA (V),Y \ Fetch byte #1 for this face and store in XX12, so STA XX12 \ XX12 = normal_x INY \ Increment Y to point to byte #2 LDA (V),Y \ Fetch byte #2 for this face and store in XX12+2, so STA XX12+2 \ XX12+2 = normal_y INY \ Increment Y to point to byte #3 LDA (V),Y \ Fetch byte #3 for this face and store in XX12+4, so STA XX12+4 \ XX12+4 = normal_z \ So we now have: \ \ XX12(1 0) = (normal_x_sign normal_x) \ \ XX12(3 2) = (normal_y_sign normal_y) \ \ XX12(5 4) = (normal_z_sign normal_z) LDX XX17 \ If XX17 < 4 then jump to LL92, otherwise we stored a CPX #4 \ larger scale factor above BCC LL92 .LL143 LDA XX18 \ Set XX15(1 0) = XX18(2 0) STA XX15 \ = dot_sidev LDA XX18+2 STA XX15+1 LDA XX18+3 \ Set XX15(3 2) = XX18(5 3) STA XX15+2 \ = dot_roofv LDA XX18+5 STA XX15+3 LDA XX18+6 \ Set XX15(5 4) = XX18(8 6) STA XX15+4 \ = dot_nosev LDA XX18+8 STA XX15+5 JMP LL89 \ Jump down to LL89 .ovflw \ If we get here then the addition below overflowed, so \ we halve the dot products and normal vector LSR XX18 \ Divide dot_sidev_lo by 2, so dot_sidev = dot_sidev / 2 LSR XX18+6 \ Divide dot_nosev_lo by 2, so dot_nosev = dot_nosev / 2 LSR XX18+3 \ Divide dot_roofv_lo by 2, so dot_roofv = dot_roofv / 2 LDX #1 \ Set X = 1 so when we fall through into LL92, we divide \ the normal vector by 2 as well .LL92 \ We jump here from above with the scale factor in X, \ and now we apply it by scaling the normal vector down \ by a factor of 2^X (i.e. divide by 2^X) LDA XX12 \ Set XX15 = normal_x STA XX15 LDA XX12+2 \ Set XX15+2 = normal_y STA XX15+2 LDA XX12+4 \ Set A = normal_z .LL93 DEX \ Decrement the scale factor in X BMI LL94 \ If X was 0 before the decrement, there is no scaling \ to do, so jump to LL94 to exit the loop LSR XX15 \ Set XX15 = XX15 / 2 \ = normal_x / 2 LSR XX15+2 \ Set XX15+2 = XX15+2 / 2 \ = normal_y / 2 LSR A \ Set A = A / 2 \ = normal_z / 2 DEX \ Decrement the scale factor in X BPL LL93+3 \ If we have more scaling to do, loop back up to the \ first LSR above until the normal vector is scaled down .LL94 STA R \ Set R = normal_z LDA XX12+5 \ Set S = normal_z_sign STA S LDA XX18+6 \ Set Q = dot_nosev_lo STA Q LDA XX18+8 \ Set A = dot_nosev_sign JSR LL38 \ Set (S A) = (S R) + (A Q) \ = normal_z + dot_nosev \ \ setting the sign of the result in S BCS ovflw \ If the addition overflowed, jump up to ovflw to divide \ both the normal vector and dot products by 2 and try \ again STA XX15+4 \ Set XX15(5 4) = (S A) LDA S \ = normal_z + dot_nosev STA XX15+5 LDA XX15 \ Set R = normal_x STA R LDA XX12+1 \ Set S = normal_x_sign STA S LDA XX18 \ Set Q = dot_sidev_lo STA Q LDA XX18+2 \ Set A = dot_sidev_sign JSR LL38 \ Set (S A) = (S R) + (A Q) \ = normal_x + dot_sidev \ \ setting the sign of the result in S BCS ovflw \ If the addition overflowed, jump up to ovflw to divide \ both the normal vector and dot products by 2 and try \ again STA XX15 \ Set XX15(1 0) = (S A) LDA S \ = normal_x + dot_sidev STA XX15+1 LDA XX15+2 \ Set R = normal_y STA R LDA XX12+3 \ Set S = normal_y_sign STA S LDA XX18+3 \ Set Q = dot_roofv_lo STA Q LDA XX18+5 \ Set A = dot_roofv_sign JSR LL38 \ Set (S A) = (S R) + (A Q) \ = normal_y + dot_roofv BCS ovflw \ If the addition overflowed, jump up to ovflw to divide \ both the normal vector and dot products by 2 and try \ again STA XX15+2 \ Set XX15(3 2) = (S A) LDA S \ = normal_y + dot_roofv STA XX15+3 .LL89 \ When we get here, we have set up the following: \ \ XX15(1 0) = normal_x + dot_sidev \ = normal_x + [x y z] . sidev \ \ XX15(3 2) = normal_y + dot_roofv \ = normal_y + [x y z] . roofv \ \ XX15(5 4) = normal_z + dot_nosev \ = normal_z + [x y z] . nosev \ \ and: \ \ XX12(1 0) = (normal_x_sign normal_x) \ \ XX12(3 2) = (normal_y_sign normal_y) \ \ XX12(5 4) = (normal_z_sign normal_z) \ \ We now calculate the dot product XX12 . XX15 to tell \ us whether or not this face is visible LDA XX12 \ Set Q = XX12 STA Q LDA XX15 \ Set A = XX15 JSR FMLTU \ Set T = A * Q / 256 STA T \ = XX15 * XX12 / 256 LDA XX12+1 \ Set S = sign of XX15(1 0) * XX12(1 0), so: EOR XX15+1 \ STA S \ (S T) = XX15(1 0) * XX12(1 0) / 256 LDA XX12+2 \ Set Q = XX12+2 STA Q LDA XX15+2 \ Set A = XX15+2 JSR FMLTU \ Set Q = A * Q STA Q \ = XX15+2 * XX12+2 / 256 LDA T \ Set T = R, so now: STA R \ \ (S R) = XX15(1 0) * XX12(1 0) / 256 LDA XX12+3 \ Set A = sign of XX15+3 * XX12+3, so: EOR XX15+3 \ \ (A Q) = XX15(3 2) * XX12(3 2) / 256 JSR LL38 \ Set (S T) = (S R) + (A Q) STA T \ = XX15(1 0) * XX12(1 0) / 256 \ + XX15(3 2) * XX12(3 2) / 256 LDA XX12+4 \ Set Q = XX12+4 STA Q LDA XX15+4 \ Set A = XX15+4 JSR FMLTU \ Set Q = A * Q STA Q \ = XX15+4 * XX12+4 / 256 LDA T \ Set T = R, so now: STA R \ \ (S R) = XX15(1 0) * XX12(1 0) / 256 \ + XX15(3 2) * XX12(3 2) / 256 LDA XX15+5 \ Set A = sign of XX15+5 * XX12+5, so: EOR XX12+5 \ \ (A Q) = XX15(5 4) * XX12(5 4) / 256 JSR LL38 \ Set (S A) = (S R) + (A Q) \ = XX15(1 0) * XX12(1 0) / 256 \ + XX15(3 2) * XX12(3 2) / 256 \ + XX15(5 4) * XX12(5 4) / 256 PHA \ Push the result A onto the stack, so the stack now \ contains the dot product XX12 . XX15 TYA \ Set X = Y / 4 LSR A \ = the number of this face * 4 /4 LSR A \ = the number of this face TAX PLA \ Pull the dot product off the stack into A BIT S \ If bit 7 of S is set, i.e. the dot product is BMI P%+4 \ negative, then this face is visible as its normal is \ pointing towards us, so skip the following instruction LDA #0 \ Otherwise the face is not visible, so set A = 0 so we \ can store this to mean "not visible" STA XX2,X \ Store the face's visibility in the X-th byte of XX2 INY \ Above we incremented Y to point to byte #3, so this \ increments Y to point to byte #4, i.e. byte #0 of the \ next face .LL88 CPY XX20 \ If Y >= XX20, the number of faces * 4, jump down to BCS LL42 \ LL42 to move on to the JMP LL86 \ Otherwise loop back to LL86 to work out the visibility \ of the next face \ ****************************************************************************** \ \ Name: LL9 (Part 6 of 12) \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw ship: Calculate the visibility of each of the ship's vertices \ Deep dive: Drawing ships \ Calculating vertex coordinates \ \ ------------------------------------------------------------------------------ \ \ This section calculates the visibility of each of the ship's vertices, and for \ those that are visible, it starts the process of calculating the screen \ coordinates of each vertex \ \ ****************************************************************************** .LL42 \ The first task is to set up the inverse matrix, ready \ for us to send to the dot product routine at LL51. \ Back up in part 3, we set up the following variables: \ \ * XX16(1 0) = sidev_x \ * XX16(3 2) = sidev_y \ * XX16(5 4) = sidev_z \ \ * XX16(7 6) = roofv_x \ * XX16(9 8) = roofv_y \ * XX16(11 10) = roofv_z \ \ * XX16(13 12) = nosev_x \ * XX16(15 14) = nosev_y \ * XX16(17 16) = nosev_z \ \ and we then scaled the vectors to give the following: \ \ * XX16 = scaled |sidev_x| \ * XX16+2 = scaled |sidev_y| \ * XX16+4 = scaled |sidev_z| \ \ * XX16+6 = scaled |roofv_x| \ * XX16+8 = scaled |roofv_y| \ * XX16+10 = scaled |roofv_z| \ \ * XX16+12 = scaled |nosev_x| \ * XX16+14 = scaled |nosev_y| \ * XX16+16 = scaled |nosev_z| \ \ We now need to rearrange these locations so they \ effectively transpose the matrix into its inverse LDY XX16+2 \ Set XX16+2 = XX16+6 = scaled |roofv_x| LDX XX16+3 \ Set XX16+3 = XX16+7 = roofv_x_hi LDA XX16+6 \ Set XX16+6 = XX16+2 = scaled |sidev_y| STA XX16+2 \ Set XX16+7 = XX16+3 = sidev_y_hi LDA XX16+7 STA XX16+3 STY XX16+6 STX XX16+7 LDY XX16+4 \ Set XX16+4 = XX16+12 = scaled |nosev_x| LDX XX16+5 \ Set XX16+5 = XX16+13 = nosev_x_hi LDA XX16+12 \ Set XX16+12 = XX16+4 = scaled |sidev_z| STA XX16+4 \ Set XX16+13 = XX16+5 = sidev_z_hi LDA XX16+13 STA XX16+5 STY XX16+12 STX XX16+13 LDY XX16+10 \ Set XX16+10 = XX16+14 = scaled |nosev_y| LDX XX16+11 \ Set XX16+11 = XX16+15 = nosev_y_hi LDA XX16+14 \ Set XX16+14 = XX16+10 = scaled |roofv_z| STA XX16+10 \ Set XX16+15 = XX16+11 = roofv_z LDA XX16+15 STA XX16+11 STY XX16+14 STX XX16+15 \ So now we have the following sign-magnitude variables \ containing parts of the scaled orientation vectors: \ \ XX16(1 0) = scaled sidev_x \ XX16(3 2) = scaled roofv_x \ XX16(5 4) = scaled nosev_x \ \ XX16(7 6) = scaled sidev_y \ XX16(9 8) = scaled roofv_y \ XX16(11 10) = scaled nosev_y \ \ XX16(13 12) = scaled sidev_z \ XX16(15 14) = scaled roofv_z \ XX16(17 16) = scaled nosev_z \ \ which is what we want, as the various vectors are now \ arranged so we can use LL51 to multiply by the \ transpose (i.e. the inverse of the matrix) LDY #8 \ Fetch byte #8 of the ship's blueprint, which is the LDA (XX0),Y \ number of vertices * 8, and store it in XX20 STA XX20 \ We now set V(1 0) = XX0(1 0) + 20, so V(1 0) points \ to byte #20 of the ship's blueprint, which is always \ where the vertex data starts (i.e. just after the 20 \ byte block that define the ship's characteristics) LDA XX0 \ We start with the low bytes CLC ADC #20 STA V LDA XX0+1 \ And then do the high bytes ADC #0 STA V+1 LDY #0 \ We are about to step through all the vertices, using \ Y as a counter. There are six data bytes for each \ vertex, so we will increment Y by 6 for each iteration \ so it can act as an offset from V(1 0) to the current \ vertex's data STY CNT \ Set CNT = 0, which we will use as a pointer to the \ heap at XX3, starting it at zero so the heap starts \ out empty .LL48 STY XX17 \ Set XX17 = Y, so XX17 now contains the offset of the \ current vertex's data LDA (V),Y \ Fetch byte #0 for this vertex into XX15, so: STA XX15 \ \ XX15 = magnitude of the vertex's x-coordinate INY \ Increment Y to point to byte #1 LDA (V),Y \ Fetch byte #1 for this vertex into XX15+2, so: STA XX15+2 \ \ XX15+2 = magnitude of the vertex's y-coordinate INY \ Increment Y to point to byte #2 LDA (V),Y \ Fetch byte #2 for this vertex into XX15+4, so: STA XX15+4 \ \ XX15+4 = magnitude of the vertex's z-coordinate INY \ Increment Y to point to byte #3 LDA (V),Y \ Fetch byte #3 for this vertex into T, so: STA T \ \ T = %xyz vvvvv, where: \ \ * Bits 0-4 = visibility distance, beyond which the \ vertex is not shown \ \ * Bits 7-5 = the sign bits of x, y and z AND #%00011111 \ Extract bits 0-4 to get the visibility distance CMP XX4 \ If XX4 > the visibility distance, where XX4 contains BCC LL49-3 \ the ship's z-distance reduced to 0-31 (which we set in \ part 2), then this vertex is too far away to be \ visible, so jump down to LL50 (via the JMP instruction \ in LL49-3) to move on to the next vertex INY \ Increment Y to point to byte #4 LDA (V),Y \ Fetch byte #4 for this vertex into P, so: STA P \ \ P = %ffff ffff, where: \ \ * Bits 0-3 = the number of face 1 \ \ * Bits 4-7 = the number of face 2 AND #%00001111 \ Extract the number of face 1 into X TAX LDA XX2,X \ If XX2+X is non-zero then we decided in part 5 that BNE LL49 \ face 1 is visible, so jump to LL49 LDA P \ Fetch byte #4 for this vertex into A LSR A \ Shift right four times to extract the number of face 2 LSR A \ from bits 4-7 into X LSR A LSR A TAX LDA XX2,X \ If XX2+X is non-zero then we decided in part 5 that BNE LL49 \ face 2 is visible, so jump to LL49 INY \ Increment Y to point to byte #5 LDA (V),Y \ Fetch byte #5 for this vertex into P, so: STA P \ \ P = %ffff ffff, where: \ \ * Bits 0-3 = the number of face 3 \ \ * Bits 4-7 = the number of face 4 AND #%00001111 \ Extract the number of face 1 into X TAX LDA XX2,X \ If XX2+X is non-zero then we decided in part 5 that BNE LL49 \ face 3 is visible, so jump to LL49 LDA P \ Fetch byte #5 for this vertex into A LSR A \ Shift right four times to extract the number of face 4 LSR A \ from bits 4-7 into X LSR A LSR A TAX LDA XX2,X \ If XX2+X is non-zero then we decided in part 5 that BNE LL49 \ face 4 is visible, so jump to LL49 JMP LL50 \ If we get here then none of the four faces associated \ with this vertex are visible, so this vertex is also \ not visible, so jump to LL50 to move on to the next \ vertex .LL49 LDA T \ Fetch byte #5 for this vertex into A and store it, so STA XX15+1 \ XX15+1 now has the sign of the vertex's x-coordinate ASL A \ Shift A left and store it, so XX15+3 now has the sign STA XX15+3 \ of the vertex's y-coordinate ASL A \ Shift A left and store it, so XX15+5 now has the sign STA XX15+5 \ of the vertex's z-coordinate \ By this point we have the following: \ \ XX15(1 0) = vertex x-coordinate \ XX15(3 2) = vertex y-coordinate \ XX15(5 4) = vertex z-coordinate \ \ XX16(1 0) = scaled sidev_x \ XX16(3 2) = scaled roofv_x \ XX16(5 4) = scaled nosev_x \ \ XX16(7 6) = scaled sidev_y \ XX16(9 8) = scaled roofv_y \ XX16(11 10) = scaled nosev_y \ \ XX16(13 12) = scaled sidev_z \ XX16(15 14) = scaled roofv_z \ XX16(17 16) = scaled nosev_z JSR LL51 \ Call LL51 to set XX12 to the dot products of XX15 and \ XX16, as follows: \ \ XX12(1 0) = [ x y z ] . [ sidev_x roofv_x nosev_x ] \ \ XX12(3 2) = [ x y z ] . [ sidev_y roofv_y nosev_y ] \ \ XX12(5 4) = [ x y z ] . [ sidev_z roofv_z nosev_z ] \ \ XX12 contains the vector from the ship's centre to \ the vertex, transformed from the orientation vector \ space to the universe orientated around our ship. So \ we can refer to this vector below, let's call it \ vertv, so: \ \ vertv_x = [ x y z ] . [ sidev_x roofv_x nosev_x ] \ \ vertv_y = [ x y z ] . [ sidev_y roofv_y nosev_y ] \ \ vertv_z = [ x y z ] . [ sidev_z roofv_z nosev_z ] \ \ To finish the calculation, we now want to calculate: \ \ vertv + [ x y z ] \ \ So let's start with the vertv_x + x LDA XX1+2 \ Set A = x_sign of the ship's location STA XX15+2 \ Set XX15+2 = x_sign EOR XX12+1 \ If the sign of x_sign * the sign of vertv_x is BMI LL52 \ negative (i.e. they have different signs), skip to \ LL52 CLC \ Set XX15(2 1 0) = XX1(2 1 0) + XX12(1 0) LDA XX12 \ = (x_sign x_hi x_lo) + vertv_x ADC XX1 \ STA XX15 \ Starting with the low bytes LDA XX1+1 \ And then doing the high bytes (we can add 0 here as ADC #0 \ we know the sign byte of vertv_x is 0) STA XX15+1 JMP LL53 \ We've added the x-coordinates, so jump to LL53 to do \ the y-coordinates .LL52 \ If we get here then x_sign and vertv_x have different \ signs, so we need to subtract them to get the result LDA XX1 \ Set XX15(2 1 0) = XX1(2 1 0) - XX12(1 0) SEC \ = (x_sign x_hi x_lo) - vertv_x SBC XX12 \ STA XX15 \ Starting with the low bytes LDA XX1+1 \ And then doing the high bytes (we can subtract 0 here SBC #0 \ as we know the sign byte of vertv_x is 0) STA XX15+1 BCS LL53 \ If the subtraction didn't underflow, then the sign of \ the result is the same sign as x_sign, and that's what \ we want, so we can jump down to LL53 to do the \ y-coordinates EOR #%11111111 \ Otherwise we need to negate the result using two's STA XX15+1 \ complement, so first we flip the bits of the high byte LDA #1 \ And then subtract the low byte from 1 SBC XX15 STA XX15 BCC P%+4 \ If the above subtraction underflowed then we need to INC XX15+1 \ bump the high byte of the result up by 1 LDA XX15+2 \ And now we flip the sign of the result to get the EOR #%10000000 \ correct result STA XX15+2 .LL53 \ Now for the y-coordinates, vertv_y + y LDA XX1+5 \ Set A = y_sign of the ship's location STA XX15+5 \ Set XX15+5 = y_sign EOR XX12+3 \ If the sign of y_sign * the sign of vertv_y is BMI LL54 \ negative (i.e. they have different signs), skip to \ LL54 CLC \ Set XX15(5 4 3) = XX1(5 4 3) + XX12(3 2) LDA XX12+2 \ = (y_sign y_hi y_lo) + vertv_y ADC XX1+3 \ STA XX15+3 \ Starting with the low bytes LDA XX1+4 \ And then doing the high bytes (we can add 0 here as ADC #0 \ we know the sign byte of vertv_y is 0) STA XX15+4 JMP LL55 \ We've added the y-coordinates, so jump to LL55 to do \ the z-coordinates .LL54 \ If we get here then y_sign and vertv_y have different \ signs, so we need to subtract them to get the result LDA XX1+3 \ Set XX15(5 4 3) = XX1(5 4 3) - XX12(3 2) SEC \ = (y_sign y_hi y_lo) - vertv_y SBC XX12+2 \ STA XX15+3 \ Starting with the low bytes LDA XX1+4 \ And then doing the high bytes (we can subtract 0 here SBC #0 \ as we know the sign byte of vertv_z is 0) STA XX15+4 BCS LL55 \ If the subtraction didn't underflow, then the sign of \ the result is the same sign as y_sign, and that's what \ we want, so we can jump down to LL55 to do the \ z-coordinates EOR #%11111111 \ Otherwise we need to negate the result using two's STA XX15+4 \ complement, so first we flip the bits of the high byte LDA XX15+3 \ And then flip the bits of the low byte and add 1 EOR #%11111111 ADC #1 STA XX15+3 LDA XX15+5 \ And now we flip the sign of the result to get the EOR #%10000000 \ correct result STA XX15+5 BCC LL55 \ If the above subtraction underflowed then we need to INC XX15+4 \ bump the high byte of the result up by 1 .LL55 \ Now for the z-coordinates, vertv_z + z LDA XX12+5 \ If vertv_z_hi is negative, jump down to LL56 BMI LL56 LDA XX12+4 \ Set (U T) = XX1(7 6) + XX12(5 4) CLC \ = (z_hi z_lo) + vertv_z ADC XX1+6 \ STA T \ Starting with the low bytes LDA XX1+7 \ And then doing the high bytes (we can add 0 here as ADC #0 \ we know the sign byte of vertv_y is 0) STA U JMP LL57 \ We've added the z-coordinates, so jump to LL57 \ The adding process is continued in part 7, after a \ couple of subroutines that we don't need quite yet \ ****************************************************************************** \ \ Name: LL61 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (U R) = 256 * A / Q \ \ ------------------------------------------------------------------------------ \ \ Calculate the following, where A >= Q: \ \ (U R) = 256 * A / Q \ \ This is a sister routine to LL28, which does the division when A < Q. \ \ ****************************************************************************** .LL61 LDX Q \ If Q = 0, jump down to LL84 to return a division BEQ LL84 \ error \ The LL28 routine returns A / Q, but only if A < Q. In \ our case A >= Q, but we still want to use the LL28 \ routine, so we halve A until it's less than Q, call \ the division routine, and then double A by the same \ number of times LDX #0 \ Set X = 0 to count the number of times we halve A .LL63 LSR A \ Halve A by shifting right INX \ Increment X CMP Q \ If A >= Q, loop back to LL63 to halve it again BCS LL63 STX S \ Otherwise store the number of times we halved A in S JSR LL28 \ Call LL28 to calculate: \ \ R = 256 * A / Q \ \ which we can do now as A < Q LDX S \ Otherwise restore the number of times we halved A \ above into X LDA R \ Set A = our division result .LL64 ASL A \ Double (U A) by shifting left ROL U BMI LL84 \ If bit 7 of U is set, the doubling has overflowed, so \ jump to LL84 to return a division error DEX \ Decrement X BNE LL64 \ If X is not yet zero then we haven't done as many \ doublings as we did halvings earlier, so loop back for \ another doubling STA R \ Store the low byte of the division result in R RTS \ Return from the subroutine .LL84 LDA #50 \ If we get here then either we tried to divide by 0, or STA R \ the result overflowed, so we set U and R to 50 STA U RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LL62 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate 128 - (U R) \ \ ------------------------------------------------------------------------------ \ \ Calculate the following for a positive sign-magnitude number (U R): \ \ 128 - (U R) \ \ and then store the result, low byte then high byte, on the end of the heap at \ XX3, where X points to the first free byte on the heap. Return by jumping down \ to LL66. \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X X is incremented by 1 \ \ ****************************************************************************** .LL62 LDA #128 \ Calculate 128 - (U R), starting with the low bytes SEC SBC R STA XX3,X \ Store the low byte of the result in the X-th byte of \ the heap at XX3 INX \ Increment the heap pointer in X to point to the next \ byte LDA #0 \ And then subtract the high bytes SBC U STA XX3,X \ Store the low byte of the result in the X-th byte of \ the heap at XX3 JMP LL66 \ Jump down to LL66 \ ****************************************************************************** \ \ Name: LL9 (Part 7 of 12) \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw ship: Calculate the visibility of each of the ship's vertices \ Deep dive: Drawing ships \ Calculating vertex coordinates \ \ ------------------------------------------------------------------------------ \ \ This section continues the coordinate adding from part 6 by finishing off the \ calculation that we started above: \ \ [ sidev_x roofv_x nosev_x ] [ x ] [ x ] \ vector to vertex = [ sidev_y roofv_y nosev_y ] . [ y ] + [ y ] \ [ sidev_z roofv_z nosev_z ] [ z ] [ z ] \ \ The gets stored as follows, in sign-magnitude values with the magnitudes \ fitting into the low bytes: \ \ XX15(2 0) [ x y z ] . [ sidev_x roofv_x nosev_x ] + [ x y z ] \ \ XX15(5 3) [ x y z ] . [ sidev_y roofv_y nosev_y ] + [ x y z ] \ \ (U T) [ x y z ] . [ sidev_z roofv_z nosev_z ] + [ x y z ] \ \ Finally, because this vector is from our ship to the vertex, and we are at the \ origin, this vector is the same as the coordinates of the vertex. In other \ words, we have just worked out: \ \ XX15(2 0) x-coordinate of the current vertex \ \ XX15(5 3) y-coordinate of the current vertex \ \ (U T) z-coordinate of the current vertex \ \ ****************************************************************************** .LL56 LDA XX1+6 \ Set (U T) = XX1(7 6) - XX12(5 4) SEC \ = (z_hi z_lo) - vertv_z SBC XX12+4 \ STA T \ Starting with the low bytes LDA XX1+7 \ And then doing the high bytes (we can subtract 0 here SBC #0 \ as we know the sign byte of vertv_z is 0) STA U BCC LL140 \ If the subtraction just underflowed, skip to LL140 to \ set (U T) to the minimum value of 4 BNE LL57 \ If U is non-zero, jump down to LL57 LDA T \ If T >= 4, jump down to LL57 CMP #4 BCS LL57 .LL140 LDA #0 \ If we get here then either (U T) < 4 or the STA U \ subtraction underflowed, so set (U T) = 4 LDA #4 STA T .LL57 \ By this point we have our results, so now to scale \ the 16-bit results down into 8-bit values LDA U \ If the high bytes of the result are all zero, we are ORA XX15+1 \ done, so jump down to LL60 for the next stage ORA XX15+4 BEQ LL60 LSR XX15+1 \ Shift XX15(1 0) to the right ROR XX15 LSR XX15+4 \ Shift XX15(4 3) to the right ROR XX15+3 LSR U \ Shift (U T) to the right ROR T JMP LL57 \ Jump back to LL57 to see if we can shift the result \ any more \ ****************************************************************************** \ \ Name: LL9 (Part 8 of 12) \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw ship: Calculate the screen coordinates of visible vertices \ Deep dive: Drawing ships \ \ ------------------------------------------------------------------------------ \ \ This section projects the coordinate of the vertex into screen coordinates and \ stores them on the XX3 heap. By the end of this part, the XX3 heap contains \ four bytes containing the 16-bit screen coordinates of the current vertex, in \ the order: x_lo, x_hi, y_lo, y_hi. \ \ When we reach here, we are looping through the vertices, and we've just worked \ out the coordinates of the vertex in our normal coordinate system, as follows \ \ XX15(2 0) (x_sign x_lo) = x-coordinate of the current vertex \ \ XX15(5 3) (y_sign y_lo) = y-coordinate of the current vertex \ \ (U T) (z_sign z_lo) = z-coordinate of the current vertex \ \ Note that U is always zero when we get to this point, as the vertex is always \ in front of us (so it has a positive z-coordinate, into the screen). \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ LL70+1 Contains an RTS (as the first byte of an LDA \ instruction) \ \ LL66 A re-entry point into the ship-drawing routine, used by \ the LL62 routine to store 128 - (U R) on the XX3 heap \ \ ****************************************************************************** .LL60 LDA T \ Set Q = z_lo STA Q LDA XX15 \ Set A = x_lo CMP Q \ If x_lo < z_lo jump to LL69 BCC LL69 JSR LL61 \ Call LL61 to calculate: \ \ (U R) = 256 * A / Q \ = 256 * x / z \ \ which we can do as x >= z JMP LL65 \ Jump to LL65 to skip the division for x_lo < z_lo .LL69 JSR LL28 \ Call LL28 to calculate: \ \ R = 256 * A / Q \ = 256 * x / z \ \ Because x < z, the result fits into one byte, and we \ also know that U = 0, so (U R) also contains the \ result .LL65 \ At this point we have: \ \ (U R) = x / z \ \ so (U R) contains the vertex's x-coordinate projected \ on screen \ \ The next task is to convert (U R) to a pixel screen \ coordinate and stick it on the XX3 heap. \ \ We start with the x-coordinate. To convert the \ x-coordinate to a screen pixel we add 128, the \ x-coordinate of the centre of the screen, because the \ projected value is relative to an origin at the centre \ of the screen, but the origin of the screen pixels is \ at the top-left of the screen LDX CNT \ Fetch the pointer to the end of the XX3 heap from CNT \ into X LDA XX15+2 \ If x_sign is negative, jump up to LL62, which will BMI LL62 \ store 128 - (U R) on the XX3 heap and return by \ jumping down to LL66 below LDA R \ Calculate 128 + (U R), starting with the low bytes CLC ADC #128 STA XX3,X \ Store the low byte of the result in the X-th byte of \ the heap at XX3 INX \ Increment the heap pointer in X to point to the next \ byte LDA U \ And then add the high bytes ADC #0 STA XX3,X \ Store the high byte of the result in the X-th byte of \ the heap at XX3 .LL66 \ We've just stored the screen x-coordinate of the \ vertex on the XX3 heap, so now for the y-coordinate TXA \ Store the heap pointer in X on the stack (at this PHA \ it points to the last entry on the heap, not the first \ free byte) LDA #0 \ Set U = 0 STA U LDA T \ Set Q = z_lo STA Q LDA XX15+3 \ Set A = y_lo CMP Q \ If y_lo < z_lo jump to LL67 BCC LL67 JSR LL61 \ Call LL61 to calculate: \ \ (U R) = 256 * A / Q \ = 256 * y / z \ \ which we can do as y >= z JMP LL68 \ Jump to LL68 to skip the division for y_lo < z_lo .LL70 \ This gets called from below when y_sign is negative LDA #Y \ Calculate #Y + (U R), starting with the low bytes CLC ADC R STA XX3,X \ Store the low byte of the result in the X-th byte of \ the heap at XX3 INX \ Increment the heap pointer in X to point to the next \ byte LDA #0 \ And then add the high bytes ADC U STA XX3,X \ Store the high byte of the result in the X-th byte of \ the heap at XX3 JMP LL50 \ Jump to LL50 to move on to the next vertex .LL67 JSR LL28 \ Call LL28 to calculate: \ \ R = 256 * A / Q \ = 256 * y / z \ \ Because y < z, the result fits into one byte, and we \ also know that U = 0, so (U R) also contains the \ result .LL68 \ At this point we have: \ \ (U R) = y / z \ \ so (U R) contains the vertex's y-coordinate projected \ on screen \ \ We now want to convert this to a screen y-coordinate \ and stick it on the XX3 heap, much like we did with \ the x-coordinate above. Again, we convert the \ coordinate by adding or subtracting the y-coordinate \ of the centre of the screen, which is in the constant \ #Y, but this time we do the opposite, as a positive \ projected y-coordinate, i.e. up the space y-axis and \ up the screen, converts to a low y-coordinate, which \ is the opposite way round to the x-coordinates PLA \ Restore the heap pointer from the stack into X TAX INX \ When we stored the heap pointer, it pointed to the \ last entry on the heap, not the first free byte, so we \ increment it so it does point to the next free byte LDA XX15+5 \ If y_sign is negative, jump up to LL70, which will BMI LL70 \ store #Y + (U R) on the XX3 heap and return by jumping \ down to LL50 below LDA #Y \ Calculate #Y - (U R), starting with the low bytes SEC SBC R STA XX3,X \ Store the low byte of the result in the X-th byte of \ the heap at XX3 INX \ Increment the heap pointer in X to point to the next \ byte LDA #0 \ And then subtract the high bytes SBC U STA XX3,X \ Store the high byte of the result in the X-th byte of \ the heap at XX3 .LL50 \ By the time we get here, the XX3 heap contains four \ bytes containing the screen coordinates of the current \ vertex, in the order: x_lo, x_hi, y_lo, y_hi CLC \ Set CNT = CNT + 4, so the heap pointer points to the LDA CNT \ next free byte on the heap ADC #4 STA CNT LDA XX17 \ Set A to the offset of the current vertex's data, \ which we set in part 6 ADC #6 \ Set Y = A + 6, so Y now points to the data for the TAY \ next vertex BCS LL72 \ If the addition just overflowed, meaning we just tried \ to access vertex #43, jump to LL72, as the maximum \ number of vertices allowed is 42 CMP XX20 \ If Y >= number of vertices * 6 (which we stored in BCS LL72 \ XX20 in part 6), jump to LL72, as we have processed \ all the vertices for this ship JMP LL48 \ Loop back to LL48 in part 6 to calculate visibility \ and screen coordinates for the next vertex \ ****************************************************************************** \ \ Name: LL9 (Part 9 of 12) \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw ship: Draw laser beams if the ship is firing its laser at us \ Deep dive: Drawing ships \ \ ------------------------------------------------------------------------------ \ \ This part sets things up so we can loop through the edges in the next part. It \ also adds a line to the ship line heap, if the ship is firing at us. \ \ When we get here, the heap at XX3 contains all the visible vertex screen \ coordinates. \ \ ****************************************************************************** .LL72 LDA XX1+31 \ If bit 5 of the ship's byte #31 is clear, then the AND #%00100000 \ ship is not currently exploding, so jump down to EE31 BEQ EE31 LDA XX1+31 \ The ship is exploding, so set bit 3 of the ship's byte ORA #%00001000 \ #31 to denote that we are drawing something on-screen STA XX1+31 \ for this ship JMP DOEXP \ Jump to DOEXP to display the explosion cloud, \ returning from the subroutine using a tail call .EE31 \ --- Mod: Code removed for flicker-free ships: -------> \LDA #%00001000 \ If bit 3 of the ship's byte #31 is clear, then there \BIT XX1+31 \ is nothing already being shown for this ship, so skip \BEQ LL74 \ to LL74 as we don't need to erase anything from the \ \ screen \ \JSR LL155 \ Otherwise call LL155 to draw the existing ship, which \ \ removes it from the screen \ --- And replaced by: --------------------------------> LDY #9 \ Fetch byte #9 of the ship's blueprint, which is the LDA (XX0),Y \ number of edges, and store it in XX20 STA XX20 \ --- End of replacement ------------------------------> LDA #%00001000 \ Set bit 3 of A so the next instruction sets bit 3 of \ the ship's byte #31 to denote that we are drawing \ something on-screen for this ship .LL74 TSB XX1+31 \ Apply bit 3 of A to the ship's byte #31, so if there \ was no ship already on screen, the bit is clear, \ otherwise it is set (the TSB instruction applies the \ accumulator to the memory location using an OR) \ --- Mod: Code removed for flicker-free ships: -------> \LDY #9 \ Fetch byte #9 of the ship's blueprint, which is the \LDA (XX0),Y \ number of edges, and store it in XX20 \STA XX20 \ \STZ U \ Set U = 0 (though we increment it to 1 below) \ \STZ XX17 \ Set XX17 = 0, which we are going to use as a counter \ \ for stepping through the ship's edges \ \INC U \ We are going to start calculating the lines we need to \ \ draw for this ship, and will store them in the ship \ \ line heap, using U to point to the end of the heap, so \ \ we start by setting U = 1 \ --- And replaced by: --------------------------------> LDY #0 \ Set XX17 = 0, which we are going to use as a counter STY XX17 \ for stepping through the ship's edges \ --- End of replacement ------------------------------> \ --- Mod: Code added for red enemy lasers: -----------> JSR RemoveLaserLine \ Check whether there is a laser line on-screen, and if \ there is, remove it \ --- End of added code -------------------------------> BIT XX1+31 \ If bit 6 of the ship's byte #31 is clear, then the BVC LL170 \ ship is not firing its lasers, so jump to LL170 to \ skip the drawing of laser lines \ The ship is firing its laser at us, so we need to draw \ the laser lines LDA #%01000000 \ Clear bit 6 of the ship's byte #31 so the ship doesn't TRB XX1+31 \ keep firing endlessly (the TRB instruction applies the \ inverse of the accumulator, %10111111, to the memory \ location using an AND) LDY #6 \ Fetch byte #6 of the ship's blueprint, which is the LDA (XX0),Y \ number * 4 of the vertex where the ship has its lasers TAY \ Put the vertex number into Y, where it can act as an \ index into list of vertex screen coordinates we added \ to the XX3 heap LDX XX3,Y \ Fetch the x_lo coordinate of the laser vertex from the STX XX15 \ XX3 heap into XX15 INX \ If X = 255 then the laser vertex is not visible, as BEQ LL170 \ the value we stored in part 2 wasn't overwritten by \ the vertex calculation in part 6 and 7, so jump to \ LL170 to skip drawing the laser lines \ We now build a laser beam from the ship's laser vertex \ towards our ship, as follows: \ \ XX15(1 0) = laser vertex x-coordinate \ \ XX15(3 2) = laser vertex y-coordinate \ \ XX15(5 4) = x-coordinate of the end of the beam \ \ XX12(1 0) = y-coordinate of the end of the beam \ \ The end of the laser beam will be positioned to look \ good, rather than being directly aimed at us, as \ otherwise we would only see a flashing point of light \ as they unleashed their attack LDX XX3+1,Y \ Fetch the x_hi coordinate of the laser vertex from the STX XX15+1 \ XX3 heap into XX15+1 INX \ If X = 255 then the laser vertex is not visible, as BEQ LL170 \ the value we stored in part 2 wasn't overwritten by \ a vertex calculation in part 6 and 7, so jump to LL170 \ to skip drawing the laser beam LDX XX3+2,Y \ Fetch the y_lo coordinate of the laser vertex from the STX XX15+2 \ XX3 heap into XX15+2 LDX XX3+3,Y \ Fetch the y_hi coordinate of the laser vertex from the STX XX15+3 \ XX3 heap into XX15+3 LDA #0 \ Set XX15(5 4) = 0, so their laser beam fires to the STA XX15+4 \ left edge of the screen STA XX15+5 STA XX12+1 \ Set XX12(1 0) = the ship's z_lo coordinate, which will LDA XX1+6 \ effectively make the vertical position of the end of STA XX12 \ the laser beam move around as the ship moves in space LDA XX1+2 \ If the ship's x_sign is positive, skip the next BPL P%+4 \ instruction DEC XX15+4 \ The ship's x_sign is negative (i.e. it's on the left \ side of the screen), so switch the laser beam so it \ goes to the right edge of the screen by decrementing \ XX15(5 4) to 255 JSR LL145 \ Call LL145 to see if the laser beam needs to be \ clipped to fit on-screen, returning the clipped line's \ end-points in (X1, Y1) and (X2, Y2) BCS LL170 \ If the C flag is set then the line is not visible on \ screen, so jump to LL170 so we don't store this line \ in the ship line heap \ --- Mod: Code removed for flicker-free ships: -------> \ --- Mod: Code removed for red enemy lasers: ---------> \LDA U \ Fetch the ship line heap pointer, which points to the \ \ next free byte on the heap, into A \ \ADC #3 \ Set Y = A + 3, so Y now points to the fourth byte in \TAY \ this coordinate \ \LDA #255 \ Set the fourth byte to 255 to act as a flag to the I/O \STA (XX19),Y \ processor to draw the following line in red, as it is \ \ a laser (this flag is read and acted on in the ADDBYT \ \ routine in the I/O processor \ \INY \ Increment Y to point to the next coordinate block \ \LDA XX15 \ Add X1 to the end of the heap \STA (XX19),Y \ \INY \ Increment the heap pointer \ \LDA XX15+1 \ Add Y1 to the end of the heap \STA (XX19),Y \ \INY \ Increment the heap pointer \ \LDA XX15+2 \ Add X2 to the end of the heap \STA (XX19),Y \ \INY \ Increment the heap pointer \ \LDA XX15+3 \ Add Y2 to the end of the heap \STA (XX19),Y \ \INY \ Increment the heap pointer \ \STY U \ Store the updated ship line heap pointer in U \ --- And replaced by: --------------------------------> BIT gameOver \ If this is the game over screen, do not draw the laser BMI LL170 \ line \ If we get here then there is a laser line, so now we \ store it and draw it LDY #1 \ Store X1 as the first coordinate on the ship line heap LDA X1 STA (XX19),Y INY \ Increment the index to point to the Y1 coordinate LDA Y1 \ Store Y1 as the first coordinate on the ship line heap STA (XX19),Y INY \ Increment the index to point to the X2 coordinate LDA X2 \ Store X2 as the second coordinate on the ship line STA (XX19),Y \ heap INY \ Increment the index to point to the Y2 coordinate LDA Y2 \ Store Y2 as the second coordinate on the ship line STA (XX19),Y \ heap LDA #RED \ Send a #SETCOL RED command to the I/O processor to JSR DOCOL \ switch to colour 2, which is red in the space view JSR LL30 \ Draw the new laser line LDX TYPE \ Set A to the ship colour for this type, from the LDA shpcol,X \ relevant entry in the shpcol table JSR DOCOL \ Send a #SETCOL command to the I/O processor to switch \ back to the ship's colour \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: LL9 (Part 10 of 12) \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw ship: Calculate the visibility of each of the ship's edges \ Deep dive: Drawing ships \ \ ------------------------------------------------------------------------------ \ \ This part calculates which edges are visible - in other words, which lines we \ should draw - and clips them to fit on the screen. \ \ When we get here, the heap at XX3 contains all the visible vertex screen \ coordinates. \ \ ****************************************************************************** .LL170 LDY #3 \ Fetch byte #3 of the ship's blueprint, which contains CLC \ the low byte of the offset to the edges data LDA (XX0),Y ADC XX0 \ Set V = low byte edges offset + XX0 STA V LDY #16 \ Fetch byte #16 of the ship's blueprint, which contains LDA (XX0),Y \ the high byte of the offset to the edges data ADC XX0+1 \ Set V+1 = high byte edges offset + XX0+1 STA V+1 \ \ So V(1 0) now points to the start of the edges data \ for this ship \ --- Mod: Code removed for flicker-free ships: -------> \LDY #5 \ Fetch byte #5 of the ship's blueprint, which contains \LDA (XX0),Y \ the maximum heap size for plotting the ship (which is \STA T1 \ 1 + 4 * the maximum number of visible edges) and store \ \ it in T1 \ \LDY XX17 \ Set Y to the edge counter in XX17 \ \.LL75 \ --- And replaced by: --------------------------------> LDY #5 \ Fetch byte #5 of the ship's blueprint, which contains LDA (XX0),Y \ the maximum heap size for plotting the ship (which is STA CNT \ 1 + 4 * the maximum number of visible edges) and store \ it in CNT .LL75 LDY #0 \ Set Y = 0 so we start with byte #0 \ --- End of replacement ------------------------------> LDA (V),Y \ Fetch byte #0 for this edge, which contains the \ visibility distance for this edge, beyond which the \ edge is not shown CMP XX4 \ If XX4 > the visibility distance, where XX4 contains BCC LL78 \ the ship's z-distance reduced to 0-31 (which we set in \ part 2), then this edge is too far away to be visible, \ so jump down to LL78 to move on to the next edge INY \ Increment Y to point to byte #1 LDA (V),Y \ Fetch byte #1 for this edge into A, so: \ \ A = %ffff ffff, where: \ \ * Bits 0-3 = the number of face 1 \ \ * Bits 4-7 = the number of face 2 \ --- Mod: Code removed for flicker-free ships: -------> \INY \ Increment Y to point to byte #2 \ --- End of removed code -----------------------------> STA P \ Store byte #1 into P AND #%00001111 \ Extract the number of face 1 into X TAX LDA XX2,X \ If XX2+X is non-zero then we decided in part 5 that BNE LL79 \ face 1 is visible, so jump to LL79 LDA P \ Fetch byte #1 for this edge into A LSR A \ Shift right four times to extract the number of face 2 LSR A \ from bits 4-7 into X LSR A LSR A TAX LDA XX2,X \ If XX2+X is zero then we decided in part 5 that BEQ LL78 \ face 2 is hidden, so jump to LL78 .LL79 \ We now build the screen line for this edge, as \ follows: \ \ XX15(1 0) = start x-coordinate \ \ XX15(3 2) = start y-coordinate \ \ XX15(5 4) = end x-coordinate \ \ XX12(1 0) = end y-coordinate \ \ We can then pass this to the line clipping routine \ before storing the resulting line in the ship line \ heap \ --- Mod: Code removed for flicker-free ships: -------> \LDA (V),Y \ Fetch byte #2 for this edge into X, which contains \TAX \ the number of the vertex at the start of the edge \ \INY \ Increment Y to point to byte #3 \ \LDA (V),Y \ Fetch byte #3 for this edge into Q, which contains \STA Q \ the number of the vertex at the end of the edge \ --- And replaced by: --------------------------------> INY \ Increment Y to point to byte #2 LDA (V),Y \ Fetch byte #2 for this edge into X, which contains TAX \ the number of the vertex at the start of the edge \ \ Byte #2 contains the vertex number multiplied by 4, \ so we can use it as an index into the heap at XX3 to \ fetch the vertex's screen coordinates, which are \ stored as four bytes containing two 16-bit numbers \ --- End of replacement ------------------------------> LDA XX3+1,X \ Fetch the x_hi coordinate of the edge's start vertex STA XX15+1 \ from the XX3 heap into XX15+1 LDA XX3,X \ Fetch the x_lo coordinate of the edge's start vertex STA XX15 \ from the XX3 heap into XX15 LDA XX3+2,X \ Fetch the y_lo coordinate of the edge's start vertex STA XX15+2 \ from the XX3 heap into XX15+2 LDA XX3+3,X \ Fetch the y_hi coordinate of the edge's start vertex STA XX15+3 \ from the XX3 heap into XX15+3 \ --- Mod: Code removed for flicker-free ships: -------> \LDX Q \ Set X to the number of the vertex at the end of the \ \ edge, which we stored in Q \ --- And replaced by: --------------------------------> INY \ Increment Y to point to byte #3 LDA (V),Y \ Fetch byte #3 for this edge into X, which contains TAX \ the number of the vertex at the end of the edge \ --- End of replacement ------------------------------> LDA XX3,X \ Fetch the x_lo coordinate of the edge's end vertex STA XX15+4 \ from the XX3 heap into XX15+4 LDA XX3+3,X \ Fetch the y_hi coordinate of the edge's end vertex STA XX12+1 \ from the XX3 heap into XX12+1 LDA XX3+2,X \ Fetch the y_lo coordinate of the edge's end vertex STA XX12 \ from the XX3 heap into XX12 LDA XX3+1,X \ Fetch the x_hi coordinate of the edge's end vertex STA XX15+5 \ from the XX3 heap into XX15+5 \ --- Mod: Code removed for two-player Elite: ---------> \JSR LL147 \ Call LL147 to see if the new line segment needs to be \ \ clipped to fit on-screen, returning the clipped line's \ \ end-points in (X1, Y1) and (X2, Y2) \ --- And replaced by: --------------------------------> JSR LL145 \ Call LL145 to see if the new line segment needs to be \ clipped to fit on-screen, returning the clipped line's \ end-points in (X1, Y1) and (X2, Y2) \ --- End of replacement ------------------------------> BCS LL78 \ If the C flag is set then the line is not visible on \ screen, so jump to LL78 so we don't store this line \ in the ship line heap \ --- Mod: Code added for flicker-free ships: ---------> JSR LSPUT \ Draw this edge using smooth animation, by first \ drawing the ship's new line and then erasing the \ corresponding old line from the screen \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: LL9 (Part 11 of 12) \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw ship: Add all visible edges to the ship line heap \ Deep dive: Drawing ships \ \ ------------------------------------------------------------------------------ \ \ This part adds all the visible edges to the ship line heap, so we can draw \ them in part 12. \ \ Other entry points: \ \ LL81+2 Draw the contents of the ship line heap, used to draw \ the ship as a dot from SHPPT \ \ ****************************************************************************** \ --- Mod: Code removed for flicker-free ships: -------> \.LL80 \ \LDY U \ Fetch the ship line heap pointer, which points to the \ \ next free byte on the heap, into Y \ \LDA XX15 \ Add X1 to the end of the heap \STA (XX19),Y \ \INY \ Increment the heap pointer \ \LDA XX15+1 \ Add Y1 to the end of the heap \STA (XX19),Y \ \INY \ Increment the heap pointer \ \LDA XX15+2 \ Add X2 to the end of the heap \STA (XX19),Y \ \INY \ Increment the heap pointer \ \LDA XX15+3 \ Add Y2 to the end of the heap \STA (XX19),Y \ \INY \ Increment the heap pointer \ \STY U \ Store the updated ship line heap pointer in U \ \CPY T1 \ If Y >= T1 then we have reached the maximum number of \BCS LL81 \ edge lines that we can store in the ship line heap, so \ \ skip to LL81 so we don't loop back for the next edge \ \.LL78 \ \INC XX17 \ Increment the edge counter to point to the next edge \ \LDY XX17 \ If Y >= XX20, which contains the number of edges in \CPY XX20 \ the blueprint, jump to LL81 as we have processed all \BCS LL81 \ the edges and don't need to loop back for the next one \ \LDY #0 \ Set Y to point to byte #0 again, ready for the next \ \ edge \ \LDA V \ Increment V by 4 so V(1 0) points to the data for the \ADC #4 \ next edge \STA V \ --- And replaced by: --------------------------------> .LL78 LDA LSNUM \ If LSNUM >= CNT, skip to LL81 so we don't loop back CMP CNT \ for the next edge (CNT was set to the maximum heap BCS LL81 \ size for this ship in part 10, so this checks whether \ we have just run out of space in the ship line heap, \ and stops drawing edges if we have) LDA V \ Increment V by 4 so V(1 0) points to the data for the CLC \ next edge ADC #4 STA V \ --- End of replacement ------------------------------> BCC ll81 \ If the above addition didn't overflow, jump to ll81 to \ skip the following instruction INC V+1 \ Otherwise increment the high byte of V(1 0), as we \ just moved the V(1 0) pointer past a page boundary .ll81 \ --- Mod: Code removed for flicker-free ships: -------> \JMP LL75 \ Loop back to LL75 to process the next edge \ \.LL81 \ \ \ We have finished adding lines to the ship line heap, \ \ so now we need to set the first byte of the heap to \ \ the number of bytes stored there \ \LDA U \ Fetch the ship line heap pointer from U into A, which \ \ points to the end of the heap, and therefore contains \ \ the heap size \ \STA (XX19) \ Store A as the first byte of the ship line heap, so \ \ the heap is now correctly set up \ --- And replaced by: --------------------------------> INC XX17 \ Increment the edge counter to point to the next edge LDY XX17 \ If Y < XX20, which contains the number of edges in CPY XX20 \ the blueprint, loop back to LL75 to process the next BCC LL75 \ edge .LL81 JMP LL155 \ Jump down to part 12 below to draw any remaining lines \ from the old ship that are still in the ship line heap \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: LL9 (Part 12 of 12) \ Type: Subroutine \ Category: Drawing ships \ Summary: Draw ship: Draw all the visible edges from the ship line heap \ Deep dive: Drawing ships \ \ ------------------------------------------------------------------------------ \ \ This part draws the lines in the ship line heap, which is used both to draw \ the ship, and to remove it from the screen. \ \ If NEEDKEY is non-zero, then this routine also scans the keyboard for a key \ press and returns the internal key number in X (or 0 for no key press). \ \ ****************************************************************************** .LL155 LDA NEEDKEY \ If NEEDKEY is zero, jump to notneed to skip the next BEQ notneed \ two instructions, so we only read the keyboard if \ NEEDKEY is non-zero STZ NEEDKEY \ Set NEEDKEY = 0 JSR RDKEY \ Scan the keyboard for a key press and return the \ internal key number in A and X (or 0 for no key press) .notneed \ --- Mod: Code removed for flicker-free ships: -------> \LDA (XX19) \ Fetch the first byte from the ship line heap into A, \ \ which contains the number of bytes in the heap \ \CMP #5 \ If the heap size is less than 5, there is nothing to \BCC nolines \ draw, so return from the subroutine (as nolines \ \ contains an RTS) \ \LDA #129 \ Send an OSWRCH 129 command to the I/O processor to \JSR OSWRCH \ tell it to start receiving a new line to draw (so \ \ when we send it OSWRCH commands from now on, the I/O \ \ processor will add these bytes to this line until \ \ they are all sent, at which point it will draw the \ \ line) \ \LDY #0 \ Fetch the first byte from the ship line heap into A, \LDA (XX19),Y \ which contains the number of bytes in the heap \ \STA XX20 \ Store the heap size in XX20 \ \.LL27 \ \LDA (XX19),Y \ Fetch the Y-th line coordinate from the heap and send \JSR OSWRCH \ it to the I/O processor to add to the line buffer \ \INY \ Increment the heap pointer \ \CPY XX20 \ If the heap counter is less than the size of the heap, \BCC LL27 \ loop back to LL27 to draw the next line from the heap \ \ \ By the time we get here, we have sent all the line \ \ coordinates to the I/O processor, so it will have \ \ drawn the line after we sent the last one \ \.nolines \ --- And replaced by: --------------------------------> LDY LSNUM \ Set Y to the offset in the line heap LSNUM .LL27 CPY LSNUM2 \ If Y >= LSNUM2, jump to LLEX to return from the ship BCS LLEX \ drawing routine, because the index in Y is greater \ than the size of the existing ship line heap, which \ means we have alrady erased all the old ships lines \ when drawing the new ship \ If we get here then Y < LSNUM2, which means Y is \ pointing to an on-screen line from the old ship that \ we need to erase LDA (XX19),Y \ Fetch the X1 line coordinate from the heap and store STA XX15 \ it in XX15 INY \ Increment the heap pointer LDA (XX19),Y \ Fetch the Y1 line coordinate from the heap and store STA XX15+1 \ it in XX15+1 INY \ Increment the heap pointer LDA (XX19),Y \ Fetch the X2 line coordinate from the heap and store STA XX15+2 \ it in XX15+2 INY \ Increment the heap pointer LDA (XX19),Y \ Fetch the Y2 line coordinate from the heap and store STA XX15+3 \ it in XX15+3 JSR LL30 \ Draw a line from (X1, Y1) to (X2, Y2) to erase it from \ the screen INY \ Increment the heap pointer JMP LL27 \ Loop back to LL27 to draw (i.e. erase) the next line \ from the heap .LLEX LDA LSNUM \ Store LSNUM in the first byte of the ship line heap LDY #0 STA (XX19),Y .LL82 \ --- End of replacement ------------------------------> RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: RemoveLaserLineX \ Type: Subroutine \ Category: Drawing lines \ Summary: Remove a red laser line from the ship in slot X, if there is one \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .RemoveLaserLineX STA TYPE \ Set the ship type to A, to pass to RemoveLaserLine JSR GINF \ Fetch INF(1 0) for slot X LDY #33 \ Set XX19(1 0) to the ship line heap address for the LDA (INF),Y \ ship in slot X STA XX19 INY LDA (INF),Y STA XX19+1 \ Fall through into RemoveLaserLine to remove the line \ from the screen \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: RemoveLaserLine \ Type: Subroutine \ Category: Drawing lines \ Summary: Remove the laser line from the screen, if there is one \ \ ****************************************************************************** \ --- Mod: Code added for red enemy lasers: -----------> .RemoveLaserLine \ We now need to check whether there is a laser line \ on-screen, and if so remove it LDY #1 \ Set X1 to the first coordinate on the ship line heap, LDA (XX19),Y \ which is the start of the laser line STA X1 INY \ Increment the index to point to the Y1 coordinate LDA (XX19),Y \ Set Y1 to the first coordinate on the ship line heap STA Y1 \ which is the start of the laser line CMP #255 \ If the Y1 coordinate is 255 then there is no laser BEQ noLaserLine \ line currently on-screen, so jump to noLaserLine to \ skip the removal of the old line LDA #255 \ Set the Y2 coordinate of the laser line in the ship STA (XX19),Y \ line heap to 255 to remove the laser line from the \ heap INY \ Increment the index to point to the X2 coordinate LDA (XX19),Y \ Set X2 to the second coordinate on the ship line heap, STA X2 \ which is the end of the laser line INY \ Increment the index to point to the Y2 coordinate LDA (XX19),Y \ Set Y2 to the second coordinate on the ship line heap, STA Y2 \ which is the end of the laser line LDA #RED \ Send a #SETCOL RED command to the I/O processor to JSR DOCOL \ switch to colour 2, which is red in the space view JSR LL30 \ Draw the old laser line to remove it from the screen LDX TYPE \ Set A to the ship colour for this type, from the LDA shpcol,X \ relevant entry in the shpcol table \ --- Mod: Code added for two-player Elite: -----------> BIT titleScreen \ If this is the title screen, force the ship colour to BPL P%+4 \ the configured colour LDA #SHIPCOL \ --- End of added code -------------------------------> JSR DOCOL \ Send a #SETCOL command to the I/O processor to switch \ back to the ship's colour .noLaserLine RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: LSPUT \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw a ship line using smooth animation, by drawing the ship's new \ line and erasing the corresponding old line from the screen \ \ ------------------------------------------------------------------------------ \ \ This routine implements smoother ship animation by erasing and redrawing each \ individual line in the ship, rather than the approach in the other Acornsoft \ versions of the game, which erase the entire existing ship before drawing the \ new one. \ \ Here's the new approach in this routine: \ \ * Draw the new line \ \ * Fetch the corresponding existing line (in position LSNUM) from the heap \ \ * Store the new line in the heap at this position, replacing the old one \ \ * If the existing line we just took from the heap is on-screen, erase it \ \ Arguments: \ \ LSNUM The offset within the line heap where we add the new \ line's coordinates \ \ X1 The screen x-coordinate of the start of the line to add \ to the ship line heap \ \ Y1 The screen y-coordinate of the start of the line to add \ to the ship line heap \ \ X2 The screen x-coordinate of the end of the line to add \ to the ship line heap \ \ Y2 The screen y-coordinate of the end of the line to add \ to the ship line heap \ \ XX19(1 0) XX19(1 0) shares its location with INWK(34 33), which \ contains the ship line heap address pointer \ \ Returns: \ \ LSNUM The offset of the next line in the line heap \ \ ****************************************************************************** \ --- Mod: Code added for flicker-free ships: ---------> .LSPUT LDY LSNUM \ Set Y = LSNUM, to get the offset within the ship line \ heap where we want to insert our new line CPY LSNUM2 \ Compare LSNUM and LSNUM2 and store the flags on the PHP \ stack so we can retrieve them later LDX #3 \ We now want to copy the line coordinates (X1, Y1) and \ (X2, Y2) to XX12...XX12+3, so set a counter to copy \ 4 bytes .LLXL LDA X1,X \ Copy the X-th byte of X1/Y1/X2/Y2 to the X-th byte of STA XX12,X \ XX12 DEX \ Decrement the loop counter BPL LLXL \ Loop back until we have copied all four bytes JSR LL30 \ Draw a line from (X1, Y1) to (X2, Y2) LDA (XX19),Y \ Set X1 to the Y-th coordinate on the ship line heap, STA X1 \ i.e. one we are replacing in the heap LDA XX12 \ Replace it with the X1 coordinate in XX12 STA (XX19),Y INY \ Increment the index to point to the Y1 coordinate LDA (XX19),Y \ Set Y1 to the Y-th coordinate on the ship line heap, STA Y1 \ i.e. one we are replacing in the heap LDA XX12+1 \ Replace it with the Y1 coordinate in XX12+1 STA (XX19),Y INY \ Increment the index to point to the X2 coordinate LDA (XX19),Y \ Set X1 to the Y-th coordinate on the ship line heap, STA X2 LDA XX12+2 \ Replace it with the X2 coordinate in XX12+2 STA (XX19),Y INY \ Increment the index to point to the Y2 coordinate LDA (XX19),Y \ Set Y2 to the Y-th coordinate on the ship line heap, STA Y2 LDA XX12+3 \ Replace it with the Y2 coordinate in XX12+3 STA (XX19),Y INY \ Increment the index to point to the next coordinate STY LSNUM \ and store the updated index in LSNUM PLP \ Restore the result of the comparison above, so if the BCS LL82 \ original value of LSNUM >= LSNUM2, then we have \ already redrawn all the lines from the old ship's line \ heap, so return from the subroutine (as LL82 contains \ an RTS) JMP LL30 \ Otherwise there are still more lines to erase from the \ old ship on-screen, so the coordinates in (X1, Y1) and \ (X2, Y2) that we just pulled from the ship line heap \ point to a line that is still on-screen, so call LL30 \ to draw this line and erase it from the screen, \ returning from the subroutine using a tail call \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: LL118 \ Type: Subroutine \ Category: Drawing lines \ Summary: Move a point along a line until it is on-screen \ Deep dive: Line-clipping \ \ ------------------------------------------------------------------------------ \ \ Given a point (x1, y1), a gradient and a direction of slope, move the point \ along the line until it is on-screen, so this effectively clips the (x1, y1) \ end of a line to be on the screen. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ XX15(1 0) x1 as a 16-bit coordinate (x1_hi x1_lo) \ \ XX15(3 2) y1 as a 16-bit coordinate (y1_hi y1_lo) \ \ XX12+2 The line's gradient * 256 (so 1.0 = 256) \ \ XX12+3 The direction of slope: \ \ * Positive (bit 7 clear) = top left to bottom right \ \ * Negative (bit 7 set) = top right to bottom left \ \ T The gradient of slope: \ \ * 0 if it's a shallow slope \ \ * &FF if it's a steep slope \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ XX15 x1 as an 8-bit coordinate \ \ XX15+2 y1 as an 8-bit coordinate \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ LL118-1 Contains an RTS \ \ ****************************************************************************** .LL118 LDA XX15+1 \ If x1_hi is positive, jump down to LL119 to skip the BPL LL119 \ following STA S \ Otherwise x1_hi is negative, i.e. off the left of the \ screen, so set S = x1_hi JSR LL120 \ Call LL120 to calculate: \ \ (Y X) = (S x1_lo) * XX12+2 if T = 0 \ = x1 * gradient \ \ (Y X) = (S x1_lo) / XX12+2 if T <> 0 \ = x1 / gradient \ \ with the sign of (Y X) set to the opposite of the \ line's direction of slope TXA \ Set y1 = y1 + (Y X) CLC \ ADC XX15+2 \ starting with the low bytes STA XX15+2 TYA \ And then adding the high bytes ADC XX15+3 STA XX15+3 LDA #0 \ Set x1 = 0 STA XX15 STA XX15+1 TAX \ Set X = 0 so the next instruction becomes a JMP .LL119 BEQ LL134 \ If x1_hi = 0 then jump down to LL134 to skip the \ following, as the x-coordinate is already on-screen \ (as 0 <= (x_hi x_lo) <= 255) STA S \ Otherwise x1_hi is positive, i.e. x1 >= 256 and off DEC S \ the right side of the screen, so set S = x1_hi - 1 JSR LL120 \ Call LL120 to calculate: \ \ (Y X) = (S x1_lo) * XX12+2 if T = 0 \ = (x1 - 256) * gradient \ \ (Y X) = (S x1_lo) / XX12+2 if T <> 0 \ = (x1 - 256) / gradient \ \ with the sign of (Y X) set to the opposite of the \ line's direction of slope TXA \ Set y1 = y1 + (Y X) CLC \ ADC XX15+2 \ starting with the low bytes STA XX15+2 TYA \ And then adding the high bytes ADC XX15+3 STA XX15+3 LDX #255 \ Set x1 = 255 STX XX15 INX STX XX15+1 .LL134 \ We have moved the point so the x-coordinate is on \ screen (i.e. in the range 0-255), so now for the \ y-coordinate LDA XX15+3 \ If y1_hi is positive, jump down to LL119 to skip BPL LL135 \ the following STA S \ Otherwise y1_hi is negative, i.e. off the top of the \ screen, so set S = y1_hi LDA XX15+2 \ Set R = y1_lo STA R JSR LL123 \ Call LL123 to calculate: \ \ (Y X) = (S R) / XX12+2 if T = 0 \ = y1 / gradient \ \ (Y X) = (S R) * XX12+2 if T <> 0 \ = y1 * gradient \ \ with the sign of (Y X) set to the opposite of the \ line's direction of slope TXA \ Set x1 = x1 + (Y X) CLC \ ADC XX15 \ starting with the low bytes STA XX15 TYA \ And then adding the high bytes ADC XX15+1 STA XX15+1 LDA #0 \ Set y1 = 0 STA XX15+2 STA XX15+3 .LL135 \BNE LL139 \ This instruction is commented out in the original \ source LDA XX15+2 \ Set (S R) = (y1_hi y1_lo) - screen height SEC \ SBC #Y*2 \ starting with the low bytes STA R LDA XX15+3 \ And then subtracting the high bytes SBC #0 STA S BCC LL136 \ If the subtraction underflowed, i.e. if y1 < screen \ height, then y1 is already on-screen, so jump to LL136 \ to return from the subroutine, as we are done .LL139 \ If we get here then y1 >= screen height, i.e. off the \ bottom of the screen JSR LL123 \ Call LL123 to calculate: \ \ (Y X) = (S R) / XX12+2 if T = 0 \ = (y1 - screen height) / gradient \ \ (Y X) = (S R) * XX12+2 if T <> 0 \ = (y1 - screen height) * gradient \ \ with the sign of (Y X) set to the opposite of the \ line's direction of slope TXA \ Set x1 = x1 + (Y X) CLC \ ADC XX15 \ starting with the low bytes STA XX15 TYA \ And then adding the high bytes ADC XX15+1 STA XX15+1 LDA #Y*2-1 \ Set y1 = 2 * #Y - 1. The constant #Y is 96, the STA XX15+2 \ y-coordinate of the mid-point of the space view, so LDA #0 \ this sets Y2 to 191, the y-coordinate of the bottom STA XX15+3 \ pixel row of the space view .LL136 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LL120 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (Y X) = (S x1_lo) * XX12+2 or (S x1_lo) / XX12+2 \ \ ------------------------------------------------------------------------------ \ \ Calculate the following: \ \ * If T = 0, this is a shallow slope, so calculate (Y X) = (S x1_lo) * XX12+2 \ \ * If T <> 0, this is a steep slope, so calculate (Y X) = (S x1_lo) / XX12+2 \ \ giving (Y X) the opposite sign to the slope direction in XX12+3. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ T The gradient of slope: \ \ * 0 if it's a shallow slope \ \ * &FF if it's a steep slope \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ LL122 Calculate (Y X) = (S R) * Q and set the sign to the \ opposite of the top byte on the stack \ \ ****************************************************************************** .LL120 LDA XX15 \ Set R = x1_lo STA R \.LL120 \ This label is commented out in the original source JSR LL129 \ Call LL129 to do the following: \ \ Q = XX12+2 \ = line gradient \ \ A = S EOR XX12+3 \ = S EOR slope direction \ \ (S R) = |S R| \ \ So A contains the sign of S * slope direction PHA \ Store A on the stack so we can use it later LDX T \ If T is non-zero, then it's a steep slope, so jump BNE LL121 \ down to LL121 to calculate this instead: \ \ (Y X) = (S R) / Q .LL122 \ The following calculates: \ \ (Y X) = (S R) * Q \ \ using the same shift-and-add algorithm that's \ documented in MULT1 LDA #0 \ Set A = 0 TAX \ Set (Y X) = 0 so we can start building the answer here TAY LSR S \ Shift (S R) to the right, so we extract bit 0 of (S R) ROR R \ into the C flag ASL Q \ Shift Q to the left, catching bit 7 in the C flag BCC LL126 \ If C (i.e. the next bit from Q) is clear, do not do \ the addition for this bit of Q, and instead skip to \ LL126 to just do the shifts .LL125 TXA \ Set (Y X) = (Y X) + (S R) CLC \ ADC R \ starting with the low bytes TAX TYA \ And then doing the high bytes ADC S TAY .LL126 LSR S \ Shift (S R) to the right ROR R ASL Q \ Shift Q to the left, catching bit 7 in the C flag BCS LL125 \ If C (i.e. the next bit from Q) is set, loop back to \ LL125 to do the addition for this bit of Q BNE LL126 \ If Q has not yet run out of set bits, loop back to \ LL126 to do the "shift" part of shift-and-add until \ we have done additions for all the set bits in Q, to \ give us our multiplication result PLA \ Restore A, which we calculated above, from the stack BPL LL133 \ If A is positive jump to LL133 to negate (Y X) and \ return from the subroutine using a tail call RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LL123 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate (Y X) = (S R) / XX12+2 or (S R) * XX12+2 \ \ ------------------------------------------------------------------------------ \ \ Calculate the following: \ \ * If T = 0, this is a shallow slope, so calculate (Y X) = (S R) / XX12+2 \ \ * If T <> 0, this is a steep slope, so calculate (Y X) = (S R) * XX12+2 \ \ giving (Y X) the opposite sign to the slope direction in XX12+3. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ XX12+2 The line's gradient * 256 (so 1.0 = 256) \ \ XX12+3 The direction of slope: \ \ * Bit 7 clear means top left to bottom right \ \ * Bit 7 set means top right to bottom left \ \ T The gradient of slope: \ \ * 0 if it's a shallow slope \ \ * &FF if it's a steep slope \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ LL121 Calculate (Y X) = (S R) / Q and set the sign to the \ opposite of the top byte on the stack \ \ LL133 Negate (Y X) and return from the subroutine \ \ LL128 Contains an RTS \ \ ****************************************************************************** .LL123 JSR LL129 \ Call LL129 to do the following: \ \ Q = XX12+2 \ = line gradient \ \ A = S EOR XX12+3 \ = S EOR slope direction \ \ (S R) = |S R| \ \ So A contains the sign of S * slope direction PHA \ Store A on the stack so we can use it later LDX T \ If T is non-zero, then it's a steep slope, so jump up BNE LL122 \ to LL122 to calculate this instead: \ \ (Y X) = (S R) * Q .LL121 \ The following calculates: \ \ (Y X) = (S R) / Q \ \ using the same shift-and-subtract algorithm that's \ documented in TIS2 LDA #%11111111 \ Set Y = %11111111 TAY ASL A \ Set X = %11111110 TAX \ This sets (Y X) = %1111111111111110, so we can rotate \ through 15 loop iterations, getting a 1 each time, and \ then getting a 0 on the 16th iteration... and we can \ also use it to catch our result bits into bit 0 each \ time .LL130 ASL R \ Shift (S R) to the left ROL S LDA S \ Set A = S BCS LL131 \ If bit 7 of S was set, then jump straight to the \ subtraction CMP Q \ If A < Q (i.e. S < Q), skip the following subtractions BCC LL132 .LL131 SBC Q \ A >= Q (i.e. S >= Q) so set: STA S \ \ S = (A R) - Q \ = (S R) - Q \ \ starting with the low bytes (we know the C flag is \ set so the subtraction will be correct) LDA R \ And then doing the high bytes SBC #0 STA R SEC \ Set the C flag to rotate into the result in (Y X) .LL132 TXA \ Rotate the counter in (Y X) to the left, and catch the ROL A \ result bit into bit 0 (which will be a 0 if we didn't TAX \ do the subtraction, or 1 if we did) TYA ROL A TAY BCS LL130 \ If we still have set bits in (Y X), loop back to LL130 \ to do the next iteration of 15, until we have done the \ whole division PLA \ Restore A, which we calculated above, from the stack BMI LL128 \ If A is negative jump to LL128 to return from the \ subroutine with (Y X) as is .LL133 TXA \ Otherwise negate (Y X) using two's complement by first EOR #%11111111 \ setting the low byte to ~X + 1 \CLC \ ADC #1 \ The CLC instruction is commented out in the original TAX \ source. It would have no effect as we know the C flag \ is clear from when we passed through the BCS above TYA \ Then set the high byte to ~Y + C EOR #%11111111 ADC #0 TAY .LL128 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LL129 \ Type: Subroutine \ Category: Maths (Arithmetic) \ Summary: Calculate Q = XX12+2, A = S EOR XX12+3 and (S R) = |S R| \ \ ------------------------------------------------------------------------------ \ \ Do the following, in this order: \ \ Q = XX12+2 \ \ A = S EOR XX12+3 \ \ (S R) = |S R| \ \ This sets up the variables required above to calculate (S R) / XX12+2 and give \ the result the opposite sign to XX12+3. \ \ ****************************************************************************** .LL129 LDX XX12+2 \ Set Q = XX12+2 STX Q LDA S \ If S is positive, jump to LL127 BPL LL127 LDA #0 \ Otherwise set R = -R SEC SBC R STA R LDA S \ Push S onto the stack PHA EOR #%11111111 \ Set S = ~S + 1 + C ADC #0 STA S PLA \ Pull the original, negative S from the stack into A .LL127 EOR XX12+3 \ Set A = original argument S EOR'd with XX12+3 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: LL145 (Part 1 of 4) \ Type: Subroutine \ Category: Drawing lines \ Summary: Clip line: Work out which end-points are on-screen, if any \ Deep dive: Line-clipping \ Extended screen coordinates \ \ ------------------------------------------------------------------------------ \ \ This routine clips the line from (x1, y1) to (x2, y2) so it fits on-screen, or \ returns an error if it can't be clipped to fit. The arguments are 16-bit \ coordinates, and the clipped line is returned using 8-bit screen coordinates. \ \ This part sets XX13 to reflect which of the two points are on-screen and \ off-screen. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ XX15(1 0) x1 as a 16-bit coordinate (x1_hi x1_lo) \ \ XX15(3 2) y1 as a 16-bit coordinate (y1_hi y1_lo) \ \ XX15(5 4) x2 as a 16-bit coordinate (x2_hi x2_lo) \ \ XX12(1 0) y2 as a 16-bit coordinate (y2_hi y2_lo) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ (X1, Y1) Screen coordinate of the start of the clipped line \ \ (X2, Y2) Screen coordinate of the end of the clipped line \ \ C flag Clear if the clipped line fits on-screen, set if it \ doesn't \ \ XX13 The state of the original coordinates on-screen: \ \ * 0 = (x2, y2) on-screen \ \ * 95 = (x1, y1) on-screen, (x2, y2) off-screen \ \ * 191 = (x1, y1) off-screen, (x2, y2) off-screen \ \ So XX13 is non-zero if the end of the line was clipped, \ meaning the next line sent to BLINE can't join onto the \ end but has to start a new segment \ \ SWAP The swap status of the returned coordinates: \ \ * &FF if we swapped the values of (x1, y1) and \ (x2, y2) as part of the clipping process \ \ * 0 if the coordinates are still in the same order \ \ Y Y is preserved \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ LL147 Don't initialise the values in SWAP or A \ \ ****************************************************************************** .LL145 \ --- Mod: Code added for two-player Elite: -----------> BIT splitScreen \ Jump to LL145a if the split screen is disabled, so we BPL LL145a \ do the normal line-clipping for normal screens \ This is the sp[lit screen, so we clip to a half screen \ height by moving the y-coordinate up by a quarter \ screen height, doubling the y-coordinate, clipping the \ coordinates as normal, and halving the y-coordinate LDA XX15+2 \ Subtract #Y/2 from y1 to move up a quarter screen SEC SBC #Y/2 STA XX15+2 LDA XX15+3 SBC #0 STA XX15+3 ASL XX15+2 \ Double y1 so we clip to the correct range ROL XX15+3 LDA XX12 \ Subtract #Y/2 from y2 to move up a quarter screen SEC SBC #Y/2 STA XX12 LDA XX12+1 SBC #0 STA XX12+1 ASL XX12 \ Double y2 so we clip to the correct range ROL XX12+1 JSR LL145a \ Clip the coordinates using the normal clipping routine PHP \ Store the clipping result, which is in the C flag LSR Y1 \ Halve y1 to move the coordinate back in range LSR Y2 \ Halve y2 to move the coordinate back in range BIT drawPlayerView \ If we are drawing player 1's view, skip the following BPL clip1 \ We are drawing player 2's view, so move the clipped \ line down to the bottom half of the screen LDA Y1 \ Move y1 down into player 2's view CLC ADC #Y STA Y1 LDA Y2 \ Move y1 down into player 2's view CLC ADC #Y STA Y2 .clip1 PLP \ Retrieve the clipping result from the stack into the \ C flag RTS \ Return from the subroutine .LL145a \ --- End of added code -------------------------------> LDA #0 \ Set SWAP = 0 STA SWAP LDA XX15+5 \ Set A = x2_hi .LL147 LDX #Y*2-1 \ Set X = #Y * 2 - 1. The constant #Y is 96, the \ y-coordinate of the mid-point of the space view, so \ this sets Y2 to 191, the y-coordinate of the bottom \ pixel row of the space view ORA XX12+1 \ If one or both of x2_hi and y2_hi are non-zero, jump BNE LL107 \ to LL107 to skip the following, leaving X at 191 CPX XX12 \ If y2_lo > the y-coordinate of the bottom of screen BCC LL107 \ then (x2, y2) is off the bottom of the screen, so skip \ the following instruction, leaving X at 191 LDX #0 \ Set X = 0 .LL107 STX XX13 \ Set XX13 = X, so we have: \ \ * XX13 = 0 if x2_hi = y2_hi = 0, y2_lo is on-screen \ \ * XX13 = 191 if x2_hi or y2_hi are non-zero or y2_lo \ is off the bottom of the screen \ \ In other words, XX13 is 191 if (x2, y2) is off-screen, \ otherwise it is 0 LDA XX15+1 \ If one or both of x1_hi and y1_hi are non-zero, jump ORA XX15+3 \ to LL83 BNE LL83 LDA #Y*2-1 \ If y1_lo > the y-coordinate of the bottom of screen CMP XX15+2 \ then (x1, y1) is off the bottom of the screen, so jump BCC LL83 \ to LL83 \ If we get here, (x1, y1) is on-screen LDA XX13 \ If XX13 is non-zero, i.e. (x2, y2) is off-screen, jump BNE LL108 \ to LL108 to halve it before continuing at LL83 \ If we get here, the high bytes are all zero, which \ means the x-coordinates are < 256 and therefore fit on \ screen, and neither coordinate is off the bottom of \ the screen. That means both coordinates are already on \ screen, so we don't need to do any clipping, all we \ need to do is move the low bytes into (X1, Y1) and \ X2, Y2) and return .LL146 \ If we get here then we have clipped our line to the \ screen edge (if we had to clip it at all), so we move \ the low bytes from (x1, y1) and (x2, y2) into (X1, Y1) \ and (X2, Y2), remembering that they share locations \ with XX15: \ \ X1 = XX15 \ Y1 = XX15+1 \ X2 = XX15+2 \ Y2 = XX15+3 \ \ X1 already contains x1_lo, so now we do the rest LDA XX15+2 \ Set Y1 (aka XX15+1) = y1_lo STA XX15+1 LDA XX15+4 \ Set X2 (aka XX15+2) = x2_lo STA XX15+2 LDA XX12 \ Set Y2 (aka XX15+3) = y2_lo STA XX15+3 \ --- Mod: Code removed for flicker-free planets: -----> \LDA SWAP \ If SWAP = 0, then we didn't have to swap the line \BEQ noswap \ coordinates around during the clipping process, so \ \ jump to noswap to skip the following swap \ \LDA X1 \ Otherwise the coordinates were swapped above, \LDY X2 \ so we swap (X1, Y1) and (X2, Y2) back again \STA X2 \STY X1 \LDA Y1 \LDY Y2 \STA Y2 \STY Y1 \ \.noswap \ --- End of removed code -----------------------------> CLC \ Clear the C flag as the clipped line fits on-screen RTS \ Return from the subroutine .LL109 SEC \ Set the C flag to indicate the clipped line does not \ fit on-screen RTS \ Return from the subroutine .LL108 LSR XX13 \ If we get here then (x2, y2) is off-screen and XX13 is \ 191, so shift XX13 right to halve it to 95 \ ****************************************************************************** \ \ Name: LL145 (Part 2 of 4) \ Type: Subroutine \ Category: Drawing lines \ Summary: Clip line: Work out if any part of the line is on-screen \ Deep dive: Line-clipping \ Extended screen coordinates \ \ ------------------------------------------------------------------------------ \ \ This part does a number of tests to see if the line is on or off the screen. \ \ If we get here then at least one of (x1, y1) and (x2, y2) is off-screen, with \ XX13 set as follows: \ \ * 0 = (x1, y1) off-screen, (x2, y2) on-screen \ \ * 95 = (x1, y1) on-screen, (x2, y2) off-screen \ \ * 191 = (x1, y1) off-screen, (x2, y2) off-screen \ \ where "off-screen" is defined as having a non-zero high byte in one of the \ coordinates, or in the case of y-coordinates, having a low byte > 191, the \ y-coordinate of the bottom of the space view. \ \ ****************************************************************************** .LL83 LDA XX13 \ If XX13 < 128 then only one of the points is on-screen BPL LL115 \ so jump down to LL115 to skip the checks of whether \ both points are in the strips to the right or bottom \ of the screen \ If we get here, both points are off-screen LDA XX15+1 \ If both x1_hi and x2_hi have bit 7 set, jump to LL109 AND XX15+5 \ to return from the subroutine with the C flag set, as BMI LL109 \ the entire line is above the top of the screen LDA XX15+3 \ If both y1_hi and y2_hi have bit 7 set, jump to LL109 AND XX12+1 \ to return from the subroutine with the C flag set, as BMI LL109 \ the entire line is to the left of the screen LDX XX15+1 \ Set A = X = x1_hi - 1 DEX TXA LDX XX15+5 \ Set XX12+2 = x2_hi - 1 DEX STX XX12+2 ORA XX12+2 \ If neither (x1_hi - 1) or (x2_hi - 1) have bit 7 set, BPL LL109 \ jump to LL109 to return from the subroutine with the C \ flag set, as the line doesn't fit on-screen LDA XX15+2 \ If y1_lo < y-coordinate of screen bottom, clear the C CMP #Y*2 \ flag, otherwise set it LDA XX15+3 \ Set XX12+2 = y1_hi - (1 - C), so: SBC #0 \ STA XX12+2 \ * Set XX12+2 = y1_hi - 1 if y1_lo is on-screen \ * Set XX12+2 = y1_hi otherwise \ \ We do this subtraction because we are only interested \ in trying to move the points up by a screen if that \ might move the point into the space view portion of \ the screen, i.e. if y1_lo is on-screen LDA XX12 \ If y2_lo < y-coordinate of screen bottom, clear the C CMP #Y*2 \ flag, otherwise set it LDA XX12+1 \ Set XX12+2 = y2_hi - (1 - C), so: SBC #0 \ \ * Set XX12+1 = y2_hi - 1 if y2_lo is on-screen \ * Set XX12+1 = y2_hi otherwise \ \ We do this subtraction because we are only interested \ in trying to move the points up by a screen if that \ might move the point into the space view portion of \ the screen, i.e. if y1_lo is on-screen ORA XX12+2 \ If neither XX12+1 or XX12+2 have bit 7 set, jump to BPL LL109 \ LL109 to return from the subroutine with the C flag \ set, as the line doesn't fit on-screen \ ****************************************************************************** \ \ Name: LL145 (Part 3 of 4) \ Type: Subroutine \ Category: Drawing lines \ Summary: Clip line: Calculate the line's gradient \ Deep dive: Line-clipping \ Extended screen coordinates \ \ ****************************************************************************** .LL115 TYA \ Store Y on the stack so we can preserve it through the PHA \ call to this subroutine LDA XX15+4 \ Set XX12+2 = x2_lo - x1_lo SEC SBC XX15 STA XX12+2 LDA XX15+5 \ Set XX12+3 = x2_hi - x1_hi SBC XX15+1 STA XX12+3 LDA XX12 \ Set XX12+4 = y2_lo - y1_lo SEC SBC XX15+2 STA XX12+4 LDA XX12+1 \ Set XX12+5 = y2_hi - y1_hi SBC XX15+3 STA XX12+5 \ So we now have: \ \ delta_x in XX12(3 2) \ delta_y in XX12(5 4) \ \ where the delta is (x1, y1) - (x2, y2)) EOR XX12+3 \ Set S = the sign of delta_x * the sign of delta_y, so STA S \ if bit 7 of S is set, the deltas have different signs LDA XX12+5 \ If delta_y_hi is positive, jump down to LL110 to skip BPL LL110 \ the following LDA #0 \ Otherwise flip the sign of delta_y to make it SEC \ positive, starting with the low bytes SBC XX12+4 STA XX12+4 LDA #0 \ And then doing the high bytes, so now: SBC XX12+5 \ STA XX12+5 \ XX12(5 4) = |delta_y| .LL110 LDA XX12+3 \ If delta_x_hi is positive, jump down to LL111 to skip BPL LL111 \ the following SEC \ Otherwise flip the sign of delta_x to make it LDA #0 \ positive, starting with the low bytes SBC XX12+2 STA XX12+2 LDA #0 \ And then doing the high bytes, so now: SBC XX12+3 \ \ (A XX12+2) = |delta_x| .LL111 \ We now keep halving |delta_x| and |delta_y| until \ both of them have zero in their high bytes TAX \ If |delta_x_hi| is non-zero, skip the following BNE LL112 LDX XX12+5 \ If |delta_y_hi| = 0, jump down to LL113 (as both BEQ LL113 \ |delta_x_hi| and |delta_y_hi| are 0) .LL112 LSR A \ Halve the value of delta_x in (A XX12+2) ROR XX12+2 LSR XX12+5 \ Halve the value of delta_y XX12(5 4) ROR XX12+4 JMP LL111 \ Loop back to LL111 .LL113 \ By now, the high bytes of both |delta_x| and |delta_y| \ are zero STX T \ We know that X = 0 as that's what we tested with a BEQ \ above, so this sets T = 0 LDA XX12+2 \ If delta_x_lo < delta_y_lo, so our line is more CMP XX12+4 \ vertical than horizontal, jump to LL114 BCC LL114 \ If we get here then our line is more horizontal than \ vertical, so it is a shallow slope STA Q \ Set Q = delta_x_lo LDA XX12+4 \ Set A = delta_y_lo JSR LL28 \ Call LL28 to calculate: \ \ R = 256 * A / Q \ = 256 * delta_y_lo / delta_x_lo JMP LL116 \ Jump to LL116, as we now have the line's gradient in R .LL114 \ If we get here then our line is more vertical than \ horizontal, so it is a steep slope LDA XX12+4 \ Set Q = delta_y_lo STA Q LDA XX12+2 \ Set A = delta_x_lo JSR LL28 \ Call LL28 to calculate: \ \ R = 256 * A / Q \ = 256 * delta_x_lo / delta_y_lo DEC T \ T was set to 0 above, so this sets T = &FF when our \ line is steep \ ****************************************************************************** \ \ Name: LL145 (Part 4 of 4) \ Type: Subroutine \ Category: Drawing lines \ Summary: Clip line: Call the routine in LL188 to do the actual clipping \ Deep dive: Line-clipping \ Extended screen coordinates \ \ ------------------------------------------------------------------------------ \ \ This part sets things up to call the routine in LL188, which does the actual \ clipping. \ \ If we get here, then R has been set to the gradient of the line (x1, y1) to \ (x2, y2), with T indicating the gradient of slope: \ \ * 0 = shallow slope (more horizontal than vertical) \ \ * &FF = steep slope (more vertical than horizontal) \ \ and XX13 has been set as follows: \ \ * 0 = (x1, y1) off-screen, (x2, y2) on-screen \ \ * 95 = (x1, y1) on-screen, (x2, y2) off-screen \ \ * 191 = (x1, y1) off-screen, (x2, y2) off-screen \ \ ****************************************************************************** .LL116 LDA R \ Store the gradient in XX12+2 STA XX12+2 LDA S \ Store the type of slope in XX12+3, bit 7 clear means STA XX12+3 \ top left to bottom right, bit 7 set means top right to \ bottom left LDA XX13 \ If XX13 = 0, skip the following instruction BEQ LL138 BPL LLX117 \ If XX13 is positive, it must be 95. This means \ (x1, y1) is on-screen but (x2, y2) isn't, so we jump \ to LLX117 to swap the (x1, y1) and (x2, y2) \ coordinates around before doing the actual clipping, \ because we need to clip (x2, y2) but the clipping \ routine at LL118 only clips (x1, y1) .LL138 \ If we get here, XX13 = 0 or 191, so (x1, y1) is \ off-screen and needs clipping JSR LL118 \ Call LL118 to move (x1, y1) along the line onto the \ screen, i.e. clip the line at the (x1, y1) end LDA XX13 \ If XX13 = 0, i.e. (x2, y2) is on-screen, jump down to BPL LL124 \ LL124 to return with a successfully clipped line .LL117 \ If we get here, XX13 = 191 (both coordinates are \ off-screen) LDA XX15+1 \ If either of x1_hi or y1_hi are non-zero, jump to ORA XX15+3 \ LL137 to return from the subroutine with the C flag BNE LL137 \ set, as the line doesn't fit on-screen LDA XX15+2 \ If y1_lo > y-coordinate of the bottom of the screen CMP #Y*2 \ jump to LL137 to return from the subroutine with the BCS LL137 \ C flag set, as the line doesn't fit on-screen .LLX117 \ If we get here, XX13 = 95 or 191, and in both cases \ (x2, y2) is off-screen, so we now need to swap the \ (x1, y1) and (x2, y2) coordinates around before doing \ the actual clipping, because we need to clip (x2, y2) \ but the clipping routine at LL118 only clips (x1, y1) LDX XX15 \ Swap x1_lo = x2_lo LDA XX15+4 STA XX15 STX XX15+4 LDA XX15+5 \ Swap x2_lo = x1_lo LDX XX15+1 STX XX15+5 STA XX15+1 LDX XX15+2 \ Swap y1_lo = y2_lo LDA XX12 STA XX15+2 STX XX12 LDA XX12+1 \ Swap y2_lo = y1_lo LDX XX15+3 STX XX12+1 STA XX15+3 JSR LL118 \ Call LL118 to move (x1, y1) along the line onto the \ screen, i.e. clip the line at the (x1, y1) end DEC SWAP \ Set SWAP = &FF to indicate that we just clipped the \ line at the (x2, y2) end by swapping the coordinates \ (the DEC does this as we set SWAP to 0 at the start of \ this subroutine) .LL124 PLA \ Restore Y from the stack so it gets preserved through TAY \ the call to this subroutine JMP LL146 \ Jump up to LL146 to move the low bytes of (x1, y1) and \ (x2, y2) into (X1, Y1) and (X2, Y2), and return from \ the subroutine with a successfully clipped line .LL137 PLA \ Restore Y from the stack so it gets preserved through TAY \ the call to this subroutine SEC \ Set the C flag to indicate the clipped line does not \ fit on-screen RTS \ Return from the subroutine \ ****************************************************************************** \ \ Save ELTG.bin \ \ ****************************************************************************** PRINT "ELITE G" PRINT "Assembled at ", ~CODE_G% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_G%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_G% PRINT "S.ELTG ", ~CODE_G%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_G% SAVE "3-assembled-output/ELTG.bin", CODE_G%, P%, LOAD% \ ****************************************************************************** \ \ ELITE H FILE \ \ Produces the binary file ELTH.bin that gets loaded by elite-bcfs.asm. \ \ ****************************************************************************** CODE_H% = P% LOAD_H% = LOAD% + P% - CODE% \ ****************************************************************************** \ \ Name: CATLOD \ Type: Subroutine \ Category: Save and load \ Summary: This routine is commented out in the original source \ \ ****************************************************************************** \.CATLOD \ These instructions are commented out in the original \ \ source \DEC CTLDL+8 \JSR CATLODS \INC CTLDL+8 \ \.CATLODS \ \LDA #127 \LDX #LO(CTLDL) \LDY #HI(CTLDL) \JMP OSWORD \ \.CTLDL \ \EQUB 0 \EQUD &0E00 \EQUB 3 \EQUB &53 \EQUB 0 \EQUB 1 \EQUB &21 \EQUB 0 \ ****************************************************************************** \ \ Name: MVEIT (Part 1 of 9) \ Type: Subroutine \ Category: Moving \ Summary: Move current ship: Tidy the orientation vectors \ Deep dive: Program flow of the ship-moving routine \ Scheduling tasks with the main loop counter \ \ ------------------------------------------------------------------------------ \ \ This routine has multiple stages. This stage does the following: \ \ * Tidy the orientation vectors for one of the ship slots \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ INWK The current ship/planet/sun's data block \ \ XSAV The slot number of the current ship/planet/sun \ \ TYPE The type of the current ship/planet/sun \ \ ****************************************************************************** .MVEIT LDA INWK+31 \ If bits 5 or 7 of ship byte #31 are set, jump to MV30 AND #%10100000 \ as the ship is either exploding or has been killed, so BNE MV30 \ we don't need to tidy its orientation vectors or apply \ tactics LDA MCNT \ Fetch the main loop counter EOR XSAV \ Fetch the slot number of the ship we are moving, EOR AND #15 \ with the loop counter and apply mod 15 to the result. BNE MV3 \ The result will be zero when "counter mod 15" matches \ the slot number, so this makes sure we call TIDY 12 \ times every 16 main loop iterations, like this: \ \ Iteration 0, tidy the ship in slot 0 \ Iteration 1, tidy the ship in slot 1 \ Iteration 2, tidy the ship in slot 2 \ ... \ Iteration 11, tidy the ship in slot 11 \ Iteration 12, do nothing \ Iteration 13, do nothing \ Iteration 14, do nothing \ Iteration 15, do nothing \ Iteration 16, tidy the ship in slot 0 \ ... \ \ and so on \ --- Mod: Code added for two-player Elite: -----------> BIT titleScreen \ If bit 7 of titleScreen is set then this is the title BMI move3 \ screen, so skip straight to tidying LDA XSAV \ If this is not the planet, jump to move1 to keep BNE move1 \ checking \ If we get here then this is the planet LDA LSX2a \ If LSX2a is non-zero then the ball line heap for BNE move3 \ player 2's view is empty, so the planet is not \ currently visible to player 2, so jump to move3 to \ tidy the planet BEQ MV3 \ Otherwise the planet is visible in player 2's view, \ so jump to MV3 to skip tidying it, as otherwise it \ will jump around in player 2's view .move1 CMP #1 \ If this is not the sun, jump to move2 to keep BNE move2 \ checking \ If we get here then this is the sun LDA LSXa \ If LSXa < 0 then the sun line heap for player 2's view BMI move3 \ is empty, so the sun is not currently visible to \ player 2, so jump to move3 to tidy the sun BPL MV3 \ Otherwise the sun is visible in player 2's view, \ so jump to MV3 to skip tidying it, as otherwise it \ will jump around in player 2's view .move2 CMP #2 \ If this is not player 2's ship, jump to move3 to tidy BNE move3 \ the slot contents \ If we get here then this is player 2's ship LDA #%00001000 \ If player 1 is visible in player 2's view, jump to BIT player1INWK31 \ MV3 to skip tidying it, to avoid making the ship jump BNE MV3 \ around (as tidying player 2's ship will make player \ 2's view jump around) .move3 \ --- End of added code -------------------------------> JSR TIDY \ Call TIDY to tidy up the orientation vectors, to \ prevent the ship from getting elongated and out of \ shape due to the imprecise nature of trigonometry \ in assembly language \ ****************************************************************************** \ \ Name: MVEIT (Part 2 of 9) \ Type: Subroutine \ Category: Moving \ Summary: Move current ship: Call tactics routine, remove ship from scanner \ Deep dive: Scheduling tasks with the main loop counter \ \ ------------------------------------------------------------------------------ \ \ This routine has multiple stages. This stage does the following: \ \ * Apply tactics to ships with AI enabled (by calling the TACTICS routine) \ \ * Remove the ship from the scanner, so we can move it \ \ ****************************************************************************** .MV3 LDX TYPE \ If the type of the ship we are moving is positive, BPL P%+5 \ i.e. it is not a planet (types 128 and 130) or sun \ (type 129), then skip the following instruction JMP MV40 \ This item is the planet or sun, so jump to MV40 to \ move it, which ends by jumping back into this routine \ at MV45 (after all the rotation, tactics and scanner \ code, which we don't need to apply to planets or suns) LDA INWK+32 \ Fetch the ship's byte #32 (AI flag) into A BPL MV30 \ If bit 7 of the AI flag is clear, then skip the \ following as AI is disabled and the ship has no \ tactics \ --- Mod: Code added for two-player Elite: -----------> BIT gameOver \ Skip applying tactics if this is the game over screen BMI MV30 \ --- End of added code -------------------------------> CPX #MSL \ If the ship is a missile, skip straight to MV26 to BEQ MV26 \ call the TACTICS routine, as we do this every \ iteration of the main loop for missiles only LDA MCNT \ Fetch the main loop counter EOR XSAV \ Fetch the slot number of the ship we are moving, EOR AND #7 \ with the loop counter and apply mod 8 to the result. BNE MV30 \ The result will be zero when "counter mod 8" matches \ the slot number mod 8, so this makes sure we call \ TACTICS 12 times every 8 main loop iterations, like \ this: \ \ Iteration 0, apply tactics to slots 0 and 8 \ Iteration 1, apply tactics to slots 1 and 9 \ Iteration 2, apply tactics to slots 2 and 10 \ Iteration 3, apply tactics to slots 3 and 11 \ Iteration 4, apply tactics to slot 4 \ Iteration 5, apply tactics to slot 5 \ Iteration 6, apply tactics to slot 6 \ Iteration 7, apply tactics to slot 7 \ Iteration 8, apply tactics to slots 0 and 8 \ ... \ \ and so on .MV26 JSR TACTICS \ Call TACTICS to apply AI tactics to this ship .MV30 \ --- Mod: Code added for two-player Elite: -----------> LDA #CYAN2 \ Set the scanner colour to cyan \ --- End of added code -------------------------------> JSR SCAN \ Draw the ship on the scanner, which has the effect of \ removing it, as it's already at this point and hasn't \ yet moved \ ****************************************************************************** \ \ Name: MVEIT (Part 3 of 9) \ Type: Subroutine \ Category: Moving \ Summary: Move current ship: Move ship forward according to its speed \ \ ------------------------------------------------------------------------------ \ \ This routine has multiple stages. This stage does the following: \ \ * Move the ship forward (along the vector pointing in the direction of \ travel) according to its speed: \ \ (x, y, z) += nosev_hi * speed / 64 \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> BIT splitScreen \ Skip the following if split screen is disabled BPL move5 LDA XSAV \ If we are moving player 2's ship CMP #2 BNE move5 BIT INWK+32 \ If player 2 is an NPC then jump to move4 BMI move4 LDA player2DELTA \ Set INWK+27 = player2DELTA * 3 / 4 ASL A \ CLC \ This makes player 2's in-game speed match that of ADC player2DELTA \ player 1 (I'm not sure why this is required, but LSR A \ player 2 is way too fast without this scaling) LSR A STA INWK+27 JMP move5 \ Jump to move5 to skip the following .move4 LDA INWK+27 \ Player 2 is an NPC so set player2DELTA to the NPC's STA player2DELTA \ speed so the stardust field gets drawn correctly .move5 \ --- End of added code -------------------------------> LDA INWK+27 \ Set Q = the ship's speed byte #27 * 4 ASL A ASL A STA Q LDA INWK+10 \ Set A = |nosev_x_hi| AND #%01111111 JSR FMLTU \ Set R = A * Q / 256 STA R \ = |nosev_x_hi| * speed / 64 LDA INWK+10 \ If nosev_x_hi is positive, then: LDX #0 \ JSR MVT1-2 \ (x_sign x_hi x_lo) = (x_sign x_hi x_lo) + R \ \ If nosev_x_hi is negative, then: \ \ (x_sign x_hi x_lo) = (x_sign x_hi x_lo) - R \ \ So in effect, this does: \ \ (x_sign x_hi x_lo) += nosev_x_hi * speed / 64 LDA INWK+12 \ Set A = |nosev_y_hi| AND #%01111111 JSR FMLTU \ Set R = A * Q / 256 STA R \ = |nosev_y_hi| * speed / 64 LDA INWK+12 \ If nosev_y_hi is positive, then: LDX #3 \ JSR MVT1-2 \ (y_sign y_hi y_lo) = (y_sign y_hi y_lo) + R \ \ If nosev_y_hi is negative, then: \ \ (y_sign y_hi y_lo) = (y_sign y_hi y_lo) - R \ \ So in effect, this does: \ \ (y_sign y_hi y_lo) += nosev_y_hi * speed / 64 LDA INWK+14 \ Set A = |nosev_z_hi| AND #%01111111 JSR FMLTU \ Set R = A * Q / 256 STA R \ = |nosev_z_hi| * speed / 64 LDA INWK+14 \ If nosev_y_hi is positive, then: LDX #6 \ JSR MVT1-2 \ (z_sign z_hi z_lo) = (z_sign z_hi z_lo) + R \ \ If nosev_z_hi is negative, then: \ \ (z_sign z_hi z_lo) = (z_sign z_hi z_lo) - R \ \ So in effect, this does: \ \ (z_sign z_hi z_lo) += nosev_z_hi * speed / 64 \ ****************************************************************************** \ \ Name: MVEIT (Part 4 of 9) \ Type: Subroutine \ Category: Moving \ Summary: Move current ship: Apply acceleration to ship's speed as a one-off \ \ ------------------------------------------------------------------------------ \ \ This routine has multiple stages. This stage does the following: \ \ * Apply acceleration to the ship's speed (if acceleration is non-zero), \ and then zero the acceleration as it's a one-off change \ \ ****************************************************************************** LDA INWK+27 \ Set A = the ship's speed in byte #24 + the ship's CLC \ acceleration in byte #28 ADC INWK+28 BPL P%+4 \ If the result is positive, skip the following \ instruction LDA #0 \ Set A to 0 to stop the speed from going negative LDY #15 \ We now fetch byte #15 from the ship's blueprint, which \ contains the ship's maximum speed, so set Y = 15 to \ use as an index CMP (XX0),Y \ If A < the ship's maximum speed, skip the following BCC P%+4 \ instruction LDA (XX0),Y \ Set A to the ship's maximum speed STA INWK+27 \ We have now calculated the new ship's speed after \ accelerating and keeping the speed within the ship's \ limits, so store the updated speed in byte #27 LDA #0 \ We have added the ship's acceleration, so we now set STA INWK+28 \ it back to 0 in byte #28, as it's a one-off change \ ****************************************************************************** \ \ Name: MVEIT (Part 5 of 9) \ Type: Subroutine \ Category: Moving \ Summary: Move current ship: Rotate ship's location by our pitch and roll \ Deep dive: Rotating the universe \ \ ------------------------------------------------------------------------------ \ \ This routine has multiple stages. This stage does the following: \ \ * Rotate the ship's location in space by the amount of pitch and roll of \ our ship \ \ We implement this as follows: \ \ 1. K2 = y - alpha * x \ 2. z = z + beta * K2 \ 3. y = K2 - beta * z \ 4. x = x + alpha * y \ \ ****************************************************************************** LDX ALP1 \ Fetch the magnitude of the current roll into X, so \ if the roll angle is alpha, X contains |alpha| LDA INWK \ Set P = ~x_lo (i.e. with all its bits flipped) so that EOR #%11111111 \ we can pass x_lo to MLTU2 below) STA P LDA INWK+1 \ Set A = x_hi JSR MLTU2-2 \ Set (A P+1 P) = (A ~P) * X \ = (x_hi x_lo) * alpha STA P+2 \ Store the high byte of the result in P+2, so we now \ have: \ \ P(2 1 0) = (x_hi x_lo) * alpha LDA ALP2+1 \ Fetch the flipped sign of the current roll angle alpha EOR INWK+2 \ from ALP2+1 and EOR with byte #2 (x_sign), so if the \ flipped roll angle and x_sign have the same sign, A \ will be positive, else it will be negative. So A will \ contain the sign bit of x_sign * flipped alpha sign, \ which is the opposite to the sign of the above result, \ so we now have: \ \ (A P+2 P+1) = - (x_sign x_hi x_lo) * alpha / 256 LDX #3 \ Set (A P+2 P+1) = (y_sign y_hi y_lo) + (A P+2 P+1) JSR MVT6 \ = y - x * alpha / 256 STA K2+3 \ Set K2(3) = A = the sign of the result LDA P+1 \ Set K2(1) = P+1, the low byte of the result STA K2+1 EOR #%11111111 \ Set P = ~K2+1 (i.e. with all its bits flipped) so STA P \ that we can pass K2+1 to MLTU2 below) LDA P+2 \ Set K2(2) = A = P+2 STA K2+2 \ So we now have result 1 above: \ \ K2(3 2 1) = (A P+2 P+1) \ = y - x * alpha / 256 LDX BET1 \ Fetch the magnitude of the current pitch into X, so \ if the pitch angle is beta, X contains |beta| JSR MLTU2-2 \ Set (A P+1 P) = (A ~P) * X \ = K2(2 1) * beta STA P+2 \ Store the high byte of the result in P+2, so we now \ have: \ \ P(2 1 0) = K2(2 1) * beta LDA K2+3 \ Fetch the sign of the above result in K(3 2 1) from EOR BET2 \ K2+3 and EOR with BET2, the sign of the current pitch \ rate, so if the pitch and K(3 2 1) have the same sign, \ A will be positive, else it will be negative. So A \ will contain the sign bit of K(3 2 1) * beta, which is \ the same as the sign of the above result, so we now \ have: \ \ (A P+2 P+1) = K2(3 2 1) * beta / 256 LDX #6 \ Set (A P+2 P+1) = (z_sign z_hi z_lo) + (A P+2 P+1) JSR MVT6 \ = z + K2 * beta / 256 STA INWK+8 \ Set z_sign = A = the sign of the result LDA P+1 \ Set z_lo = P+1, the low byte of the result STA INWK+6 EOR #%11111111 \ Set P = ~z_lo (i.e. with all its bits flipped) so that STA P \ we can pass z_lo to MLTU2 below) LDA P+2 \ Set z_hi = P+2 STA INWK+7 \ So we now have result 2 above: \ \ (z_sign z_hi z_lo) = (A P+2 P+1) \ = z + K2 * beta / 256 JSR MLTU2 \ MLTU2 doesn't change Q, and Q was set to beta in \ the previous call to MLTU2, so this call does: \ \ (A P+1 P) = (A ~P) * Q \ = (z_hi z_lo) * beta STA P+2 \ Set P+2 = A = the high byte of the result, so we \ now have: \ \ P(2 1 0) = (z_hi z_lo) * beta LDA K2+3 \ Set y_sign = K2+3 STA INWK+5 EOR BET2 \ EOR y_sign with BET2, the sign of the current pitch EOR INWK+8 \ rate, and z_sign. If the result is positive jump to BPL MV43 \ MV43, otherwise this means beta * z and y have \ different signs, i.e. P(2 1) and K2(3 2 1) have \ different signs, so we need to add them in order to \ calculate K2(2 1) - P(2 1) LDA P+1 \ Set (y_hi y_lo) = K2(2 1) + P(2 1) ADC K2+1 STA INWK+3 LDA P+2 ADC K2+2 STA INWK+4 JMP MV44 \ Jump to MV44 to continue the calculation .MV43 LDA K2+1 \ Reversing the logic above, we need to subtract P(2 1) SBC P+1 \ and K2(3 2 1) to calculate K2(2 1) - P(2 1), so this STA INWK+3 \ sets (y_hi y_lo) = K2(2 1) - P(2 1) LDA K2+2 SBC P+2 STA INWK+4 BCS MV44 \ If the above subtraction did not underflow, then \ jump to MV44, otherwise we need to negate the result LDA #1 \ Negate (y_sign y_hi y_lo) using two's complement, SBC INWK+3 \ first doing the low bytes: STA INWK+3 \ \ y_lo = 1 - y_lo LDA #0 \ Then the high bytes: SBC INWK+4 \ STA INWK+4 \ y_hi = 0 - y_hi LDA INWK+5 \ And finally flip the sign in y_sign EOR #%10000000 STA INWK+5 .MV44 \ So we now have result 3 above: \ \ (y_sign y_hi y_lo) = K2(2 1) - P(2 1) \ = K2 - beta * z LDX ALP1 \ Fetch the magnitude of the current roll into X, so \ if the roll angle is alpha, X contains |alpha| LDA INWK+3 \ Set P = ~y_lo (i.e. with all its bits flipped) so that EOR #&FF \ we can pass y_lo to MLTU2 below) STA P LDA INWK+4 \ Set A = y_hi JSR MLTU2-2 \ Set (A P+1 P) = (A ~P) * X \ = (y_hi y_lo) * alpha STA P+2 \ Store the high byte of the result in P+2, so we now \ have: \ \ P(2 1 0) = (y_hi y_lo) * alpha LDA ALP2 \ Fetch the correct sign of the current roll angle alpha EOR INWK+5 \ from ALP2 and EOR with byte #5 (y_sign), so if the \ correct roll angle and y_sign have the same sign, A \ will be positive, else it will be negative. So A will \ contain the sign bit of x_sign * correct alpha sign, \ which is the same as the sign of the above result, \ so we now have: \ \ (A P+2 P+1) = (y_sign y_hi y_lo) * alpha / 256 LDX #0 \ Set (A P+2 P+1) = (x_sign x_hi x_lo) + (A P+2 P+1) JSR MVT6 \ = x + y * alpha / 256 STA INWK+2 \ Set x_sign = A = the sign of the result LDA P+2 \ Set x_hi = P+2, the high byte of the result STA INWK+1 LDA P+1 \ Set x_lo = P+1, the low byte of the result STA INWK \ So we now have result 4 above: \ \ x = x + alpha * y \ \ and the rotation of (x, y, z) is done \ ****************************************************************************** \ \ Name: MVEIT (Part 6 of 9) \ Type: Subroutine \ Category: Moving \ Summary: Move current ship: Move the ship in space according to our speed \ Deep dive: A sense of scale \ \ ------------------------------------------------------------------------------ \ \ This routine has multiple stages. This stage does the following: \ \ * Move the ship in space according to our speed (we already moved it \ according to its own speed in part 3). \ \ We do this by subtracting our speed (i.e. the distance we travel in this \ iteration of the loop) from the other ship's z-coordinate. We subtract because \ they appear to be "moving" in the opposite direction to us, and the whole \ MVEIT routine is about moving the other ships rather than us (even though we \ are the one doing the moving). \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ MV45 Rejoin the MVEIT routine after the rotation, tactics and \ scanner code \ \ ****************************************************************************** .MV45 \ --- Mod: Code added for two-player Elite: -----------> LDA TYPE \ If the ship type is not the sun (129) then skip the AND #%10000001 \ next instruction, otherwise return from the subroutine CMP #129 \ as we don't need to rotate the sun around its origin BNE P%+3 \ or apply player 1's speed (as we want the sun and \ planet to remain distant and unreachable) RTS \ Return from the subroutine, as the ship we are moving \ is the sun and doesn't need any of the following CMP #128 \ If the ship type is 128 or 130, then this is the BEQ move6 \ planet, so skip the following so we don't apply player \ 1's speed to the planet's coordinate, but instead move \ on to the pitch and roll (as we still want the planet \ to rotate) \ --- End of added code -------------------------------> LDA DELTA \ Set R to our speed in DELTA STA R LDA #%10000000 \ Set A to zeroes but with bit 7 set, so that (A R) is \ a 16-bit number containing -R, or -speed LDX #6 \ Set X to the z-axis so the call to MVT1 does this: JSR MVT1 \ \ (z_sign z_hi z_lo) = (z_sign z_hi z_lo) + (A R) \ = (z_sign z_hi z_lo) - speed \ --- Mod: Code removed for two-player Elite: ---------> \LDA TYPE \ If the ship type is not the sun (129) then skip the \AND #%10000001 \ next instruction, otherwise return from the subroutine \CMP #129 \ as we don't need to rotate the sun around its origin. \BNE P%+3 \ Having both the AND and the CMP is a little odd, as \ \ the sun is the only ship type with bits 0 and 7 set, \ \ so the AND has no effect and could be removed \ \RTS \ Return from the subroutine, as the ship we are moving \ \ is the sun and doesn't need any of the following \ --- And replaced by: --------------------------------> .move6 \ --- End of replacement ------------------------------> \ ****************************************************************************** \ \ Name: MVEIT (Part 7 of 9) \ Type: Subroutine \ Category: Moving \ Summary: Move current ship: Rotate ship's orientation vectors by pitch/roll \ Deep dive: Orientation vectors \ Pitching and rolling \ \ ------------------------------------------------------------------------------ \ \ This routine has multiple stages. This stage does the following: \ \ * Rotate the ship's orientation vectors according to our pitch and roll \ \ As with the previous step, this is all about moving the other ships rather \ than us (even though we are the one doing the moving). So we rotate the \ current ship's orientation vectors (which defines its orientation in space), \ by the angles we are "moving" the rest of the sky through (alpha and beta, our \ roll and pitch), so the ship appears to us to be stationary while we rotate. \ \ ****************************************************************************** LDY #9 \ Apply our pitch and roll rotations to the current JSR MVS4 \ ship's nosev vector LDY #15 \ Apply our pitch and roll rotations to the current JSR MVS4 \ ship's roofv vector LDY #21 \ Apply our pitch and roll rotations to the current JSR MVS4 \ ship's sidev vector \ ****************************************************************************** \ \ Name: MVEIT (Part 8 of 9) \ Type: Subroutine \ Category: Moving \ Summary: Move current ship: Rotate ship about itself by its own pitch/roll \ Deep dive: Orientation vectors \ Pitching and rolling by a fixed angle \ \ ------------------------------------------------------------------------------ \ \ This routine has multiple stages. This stage does the following: \ \ * If the ship we are processing is rolling or pitching itself, rotate it and \ apply damping if required \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> BIT splitScreen \ Skip the following if split screen is disabled BPL move8 LDA XSAV \ If we are not moving player 2's ship, jump to move8 to CMP #2 \ skip the following BNE move8 BIT INWK+32 \ If player 2 is an NPC, jump to move7 to prepare player BMI move7 \ 2's angles for any pitch counter rotations \ If we get here then we are moving player 2's ship and \ player 2 is human-controlled, so we need to rotate the \ ship according to player 2's pitch and roll \ Apply player 2's pitch and roll to player 2's ship \ \ * player2ALPHA = roll \ \ * player2BETA = pitch LDA ALPHA \ Save current ship's pitch and roll angles (MVS4 only STA storeData \ uses ALPHA and BETA) LDA BETA STA storeData+4 LDA player2ALPHA \ Switch to player 2's pitch and roll angles STA ALPHA LDA player2BETA STA BETA JSR TransposeMatrix \ Transpose the orientation matrix to move into player \ 2's frame of reference LDY #9 \ Apply player 2 pitch and roll rotations to the current JSR MVS4 \ ship's nosev vector LDY #15 \ Apply player 2 pitch and roll rotations to the current JSR MVS4 \ ship's roofv vector LDY #21 \ Apply player 2 pitch and roll rotations to the current JSR MVS4 \ ship's sidev vector JSR TransposeMatrix \ Transpose the orientation matrix again to move back \ into player 1's frame of reference LDA storeData \ Restore pitch and roll angles to the current ship STA ALPHA LDA storeData+4 STA BETA JMP MV5 \ Skip the following as this only applies to NPC ships .move7 \ If we get here then we are moving player 2's ship and \ player 2 is an NPC STZ player2ALPHA \ Zero the angles, as the following code only sets these STZ player2ALP1 \ angles if the ship is rotating STZ player2ALP2 STZ player2BETA STZ player2BET1 STZ player2BET2 LDA #%10000000 STA player2ALP2+1 STA player2BET2+1 .move8 \ --- End of added code -------------------------------> LDA INWK+30 \ Fetch the ship's pitch counter and extract the sign AND #%10000000 \ into RAT2 STA RAT2 LDA INWK+30 \ Fetch the ship's pitch counter and extract the value AND #%01111111 \ without the sign bit into A BEQ MV8 \ If the pitch counter is 0, then jump to MV8 to skip \ the following, as the ship is not pitching CMP #%01111111 \ If bits 0-6 are set in the pitch counter (i.e. the \ ship's pitch is not damping down), then the C flag \ will be set by this instruction SBC #0 \ Set A = A - 0 - (1 - C), so if we are damping then we \ reduce A by 1, otherwise it is unchanged ORA RAT2 \ Change bit 7 of A to the sign we saved in RAT2, so \ the updated pitch counter in A retains its sign STA INWK+30 \ Store the updated pitch counter in byte #30 LDX #15 \ Rotate (roofv_x, nosev_x) by a small angle (pitch) LDY #9 JSR MVS5 LDX #17 \ Rotate (roofv_y, nosev_y) by a small angle (pitch) LDY #11 JSR MVS5 LDX #19 \ Rotate (roofv_z, nosev_z) by a small angle (pitch) LDY #13 JSR MVS5 \ --- Mod: Code added for two-player Elite: -----------> BIT splitScreen \ Skip the following if split screen is disabled BPL MV8 LDA XSAV \ If we are moving player 2's ship, then it must be an CMP #2 \ NPC, so keep going, otherwise jump to MV8 BNE MV8 \ Set player 2's beta angles according to the roll, so \ the stardust field looks correct \ \ The angle is 1/16 radians LDA #16 \ Set BET1 to the magnitude STA player2BET1 ORA RAT2 \ Set BETA to the signed magnitude STA player2BETA LDA RAT2 \ Set BET2 to the sign of BETA STA player2BET2 EOR #%10000000 \ Set BET2+1 to the opposite sign to BETA STA player2BET2+1 \ --- End of added code -------------------------------> .MV8 LDA INWK+29 \ Fetch the ship's roll counter and extract the sign AND #%10000000 \ into RAT2 STA RAT2 LDA INWK+29 \ Fetch the ship's roll counter and extract the value AND #%01111111 \ without the sign bit into A BEQ MV5 \ If the roll counter is 0, then jump to MV5 to skip the \ following, as the ship is not rolling CMP #%01111111 \ If bits 0-6 are set in the roll counter (i.e. the \ ship's roll is not damping down), then the C flag \ will be set by this instruction SBC #0 \ Set A = A - 0 - (1 - C), so if we are damping then we \ reduce A by 1, otherwise it is unchanged ORA RAT2 \ Change bit 7 of A to the sign we saved in RAT2, so \ the updated roll counter in A retains its sign STA INWK+29 \ Store the updated pitch counter in byte #29 LDX #15 \ Rotate (roofv_x, sidev_x) by a small angle (roll) LDY #21 JSR MVS5 LDX #17 \ Rotate (roofv_y, sidev_y) by a small angle (roll) LDY #23 JSR MVS5 LDX #19 \ Rotate (roofv_z, sidev_z) by a small angle (roll) LDY #25 JSR MVS5 \ --- Mod: Code added for two-player Elite: -----------> BIT splitScreen \ Skip the following if split screen is disabled BPL MV5 LDA XSAV \ If we are moving player 2's ship, then it must be an CMP #2 \ NPC, so keep going, otherwise jump to part 9 BNE MV5 \ Set player 2's beta angles according to the roll, so \ the stardust field looks correct \ \ The angle is 1/16 radians LDA #16 \ Set ALP1 to the magnitude STA player2ALP1 ORA RAT2 \ Set ALPHA to the signed magnitude STA player2ALPHA LDA RAT2 \ Set ALP2 to the sign of ALPHA STA player2ALP2 EOR #%10000000 \ Set ALP2+1 to the opposite sign to ALPHA STA player2ALP2+1 \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: MVEIT (Part 9 of 9) \ Type: Subroutine \ Category: Moving \ Summary: Move current ship: Redraw on scanner, if it hasn't been destroyed \ \ ------------------------------------------------------------------------------ \ \ This routine has multiple stages. This stage does the following: \ \ * If the ship is exploding or being removed, hide it on the scanner \ \ * Otherwise redraw the ship on the scanner, now that it's been moved \ \ ****************************************************************************** .MV5 LDA INWK+31 \ Fetch the ship's exploding/killed state from byte #31 AND #%10100000 \ If we are exploding or removing this ship then jump to BNE MVD1 \ MVD1 to remove it from the scanner permanently LDA INWK+31 \ Set bit 4 to keep the ship visible on the scanner ORA #%00010000 STA INWK+31 \ --- Mod: Code added for two-player Elite: -----------> LDA #CYAN2 \ Set the scanner colour to cyan \ --- End of added code -------------------------------> JMP SCAN \ Display the ship on the scanner, returning from the \ subroutine using a tail call .MVD1 LDA INWK+31 \ Clear bit 4 to hide the ship on the scanner AND #%11101111 STA INWK+31 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MVT1 \ Type: Subroutine \ Category: Moving \ Summary: Calculate (x_sign x_hi x_lo) = (x_sign x_hi x_lo) + (A R) \ \ ------------------------------------------------------------------------------ \ \ Add the signed delta (A R) to a ship's coordinate, along the axis given in X. \ Mathematically speaking, this routine translates the ship along a single axis \ by a signed delta. Taking the example of X = 0, the x-axis, it does the \ following: \ \ (x_sign x_hi x_lo) = (x_sign x_hi x_lo) + (A R) \ \ (In practice, MVT1 is only ever called directly with A = 0 or 128, otherwise \ it is always called via MVT-2, which clears A apart from the sign bit. The \ routine is written to cope with a non-zero delta_hi, so it supports a full \ 16-bit delta, but it appears that delta_hi is only ever used to hold the \ sign of the delta.) \ \ The comments below assume we are adding delta to the x-axis, though the axis \ is determined by the value of X. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ (A R) The signed delta, so A = delta_hi and R = delta_lo \ \ X Determines which coordinate axis of INWK to change: \ \ * X = 0 adds the delta to (x_lo, x_hi, x_sign) \ \ * X = 3 adds the delta to (y_lo, y_hi, y_sign) \ \ * X = 6 adds the delta to (z_lo, z_hi, z_sign) \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ MVT1-2 Clear bits 0-6 of A before entering MVT1 \ \ ****************************************************************************** AND #%10000000 \ Clear bits 0-6 of A .MVT1 ASL A \ Set the C flag to the sign bit of the delta, leaving \ delta_hi << 1 in A STA S \ Set S = delta_hi << 1 \ \ This also clears bit 0 of S LDA #0 \ Set T = just the sign bit of delta (in bit 7) ROR A STA T LSR S \ Set S = delta_hi >> 1 \ = |delta_hi| \ \ This also clear the C flag, as we know that bit 0 of \ S was clear before the LSR EOR INWK+2,X \ If T EOR x_sign has bit 7 set, then x_sign and delta BMI MV10 \ have different signs, so jump to MV10 \ At this point, we know x_sign and delta have the same \ sign, that sign is in T, and S contains |delta_hi|, \ so now we want to do: \ \ (x_sign x_hi x_lo) = (x_sign x_hi x_lo) + (S R) \ \ and then set the sign of the result to the same sign \ as x_sign and delta LDA R \ First we add the low bytes, so: ADC INWK,X \ STA INWK,X \ x_lo = x_lo + R LDA S \ Then we add the high bytes: ADC INWK+1,X \ STA INWK+1,X \ x_hi = x_hi + S LDA INWK+2,X \ And finally we add any carry into x_sign, and if the ADC #0 \ sign of x_sign and delta in T is negative, make sure ORA T \ the result is negative (by OR'ing with T) STA INWK+2,X RTS \ Return from the subroutine .MV10 \ If we get here, we know x_sign and delta have \ different signs, with delta's sign in T, and \ |delta_hi| in S, so now we want to do: \ \ (x_sign x_hi x_lo) = (x_sign x_hi x_lo) - (S R) \ \ and then set the sign of the result according to \ the signs of x_sign and delta LDA INWK,X \ First we subtract the low bytes, so: SEC \ SBC R \ x_lo = x_lo - R STA INWK,X LDA INWK+1,X \ Then we subtract the high bytes: SBC S \ STA INWK+1,X \ x_hi = x_hi - S LDA INWK+2,X \ And finally we subtract any borrow from bits 0-6 of AND #%01111111 \ x_sign, and give the result the opposite sign bit to T SBC #0 \ (i.e. give it the sign of the original x_sign) ORA #%10000000 EOR T STA INWK+2,X BCS MV11 \ If the C flag is set by the above SBC, then our sum \ above didn't underflow and is correct - to put it \ another way, (x_sign x_hi x_lo) >= (S R) so the result \ should indeed have the same sign as x_sign, so jump to \ MV11 to return from the subroutine \ Otherwise our subtraction underflowed because \ (x_sign x_hi x_lo) < (S R), so we now need to flip the \ subtraction around by using two's complement to this: \ \ (S R) - (x_sign x_hi x_lo) \ \ and then we need to give the result the same sign as \ (S R), the delta, as that's the dominant figure in the \ sum LDA #1 \ First we subtract the low bytes, so: SBC INWK,X \ STA INWK,X \ x_lo = 1 - x_lo LDA #0 \ Then we subtract the high bytes: SBC INWK+1,X \ STA INWK+1,X \ x_hi = 0 - x_hi LDA #0 \ And then we subtract the sign bytes: SBC INWK+2,X \ \ x_sign = 0 - x_sign AND #%01111111 \ Finally, we set the sign bit to the sign in T, the ORA T \ sign of the original delta, as the delta is the STA INWK+2,X \ dominant figure in the sum .MV11 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MVS4 \ Type: Subroutine \ Category: Moving \ Summary: Apply pitch and roll to an orientation vector \ Deep dive: Orientation vectors \ Pitching and rolling \ \ ------------------------------------------------------------------------------ \ \ Apply pitch and roll angles alpha and beta to the orientation vector in Y. \ \ Specifically, this routine rotates a point (x, y, z) around the origin by \ pitch alpha and roll beta, using the small angle approximation to make the \ maths easier, and incorporating the Minsky circle algorithm to make the \ rotation more stable (though more elliptic). \ \ If that paragraph makes sense to you, then you should probably be writing \ this commentary! For the rest of us, see the associated deep dives. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ Y Determines which of the INWK orientation vectors to \ transform: \ \ * Y = 9 rotates nosev: (nosev_x, nosev_y, nosev_z) \ \ * Y = 15 rotates roofv: (roofv_x, roofv_y, roofv_z) \ \ * Y = 21 rotates sidev: (sidev_x, sidev_y, sidev_z) \ \ ****************************************************************************** .MVS4 LDA ALPHA \ Set Q = alpha (the roll angle to rotate through) STA Q LDX INWK+2,Y \ Set (S R) = nosev_y STX R LDX INWK+3,Y STX S LDX INWK,Y \ These instructions have no effect as MAD overwrites STX P \ X and P when called, but they set X = P = nosev_x_lo LDA INWK+1,Y \ Set A = -nosev_x_hi EOR #%10000000 JSR MAD \ Set (A X) = Q * A + (S R) STA INWK+3,Y \ = alpha * -nosev_x_hi + nosev_y STX INWK+2,Y \ \ and store (A X) in nosev_y, so this does: \ \ nosev_y = nosev_y - alpha * nosev_x_hi STX P \ This instruction has no effect as MAD overwrites P, \ but it sets P = nosev_y_lo LDX INWK,Y \ Set (S R) = nosev_x STX R LDX INWK+1,Y STX S LDA INWK+3,Y \ Set A = nosev_y_hi JSR MAD \ Set (A X) = Q * A + (S R) STA INWK+1,Y \ = alpha * nosev_y_hi + nosev_x STX INWK,Y \ \ and store (A X) in nosev_x, so this does: \ \ nosev_x = nosev_x + alpha * nosev_y_hi STX P \ This instruction has no effect as MAD overwrites P, \ but it sets P = nosev_x_lo LDA BETA \ Set Q = beta (the pitch angle to rotate through) STA Q LDX INWK+2,Y \ Set (S R) = nosev_y STX R LDX INWK+3,Y STX S LDX INWK+4,Y STX P \ This instruction has no effect as MAD overwrites P, \ but it sets P = nosev_y LDA INWK+5,Y \ Set A = -nosev_z_hi EOR #%10000000 JSR MAD \ Set (A X) = Q * A + (S R) STA INWK+3,Y \ = beta * -nosev_z_hi + nosev_y STX INWK+2,Y \ \ and store (A X) in nosev_y, so this does: \ \ nosev_y = nosev_y - beta * nosev_z_hi STX P \ This instruction has no effect as MAD overwrites P, \ but it sets P = nosev_y_lo LDX INWK+4,Y \ Set (S R) = nosev_z STX R LDX INWK+5,Y STX S LDA INWK+3,Y \ Set A = nosev_y_hi JSR MAD \ Set (A X) = Q * A + (S R) STA INWK+5,Y \ = beta * nosev_y_hi + nosev_z STX INWK+4,Y \ \ and store (A X) in nosev_z, so this does: \ \ nosev_z = nosev_z + beta * nosev_y_hi RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MVT6 \ Type: Subroutine \ Category: Moving \ Summary: Calculate (A P+2 P+1) = (x_sign x_hi x_lo) + (A P+2 P+1) \ \ ------------------------------------------------------------------------------ \ \ Do the following calculation, for the coordinate given by X (so this is what \ it does for the x-coordinate): \ \ (A P+2 P+1) = (x_sign x_hi x_lo) + (A P+2 P+1) \ \ A is a sign bit and is not included in the calculation, but bits 0-6 of A are \ preserved. Bit 7 is set to the sign of the result. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The sign of P(2 1) in bit 7 \ \ P(2 1) The 16-bit value we want to add the coordinate to \ \ X The coordinate to add, as follows: \ \ * If X = 0, add to (x_sign x_hi x_lo) \ \ * If X = 3, add to (y_sign y_hi y_lo) \ \ * If X = 6, add to (z_sign z_hi z_lo) \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A The sign of the result (in bit 7) \ \ ****************************************************************************** .MVT6 TAY \ Store argument A into Y, for later use EOR INWK+2,X \ Set A = A EOR x_sign BMI MV50 \ If the sign is negative, i.e. A and x_sign have \ different signs, jump to MV50 \ The signs are the same, so we can add the two \ arguments and keep the sign to get the result LDA P+1 \ First we add the low bytes: CLC \ ADC INWK,X \ P+1 = P+1 + x_lo STA P+1 LDA P+2 \ And then the high bytes: ADC INWK+1,X \ STA P+2 \ P+2 = P+2 + x_hi TYA \ Restore the original A argument that we stored earlier \ so that we keep the original sign RTS \ Return from the subroutine .MV50 LDA INWK,X \ First we subtract the low bytes: SEC \ SBC P+1 \ P+1 = x_lo - P+1 STA P+1 LDA INWK+1,X \ And then the high bytes: SBC P+2 \ STA P+2 \ P+2 = x_hi - P+2 BCC MV51 \ If the last subtraction underflowed, then the C flag \ will be clear and x_hi < P+2, so jump to MV51 to \ negate the result TYA \ Restore the original A argument that we stored earlier EOR #%10000000 \ but flip bit 7, which flips the sign. We do this \ because x_hi >= P+2 so we want the result to have the \ same sign as x_hi (as it's the dominant side in this \ calculation). The sign of x_hi is x_sign, and x_sign \ has the opposite sign to A, so we flip the sign in A \ to return the correct result RTS \ Return from the subroutine .MV51 LDA #1 \ Our subtraction underflowed, so we negate the result SBC P+1 \ using two's complement, first with the low byte: STA P+1 \ \ P+1 = 1 - P+1 LDA #0 \ And then the high byte: SBC P+2 \ STA P+2 \ P+2 = 0 - P+2 TYA \ Restore the original A argument that we stored earlier \ as this is the correct sign for the result. This is \ because x_hi < P+2, so we want to return the same sign \ as P+2, the dominant side RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: MV40 \ Type: Subroutine \ Category: Moving \ Summary: Rotate the planet or sun's location in space by the amount of \ pitch and roll of our ship \ Deep dive: Rotating the universe \ \ ------------------------------------------------------------------------------ \ \ We implement this using the same equations as in part 5 of MVEIT, where we \ rotated the current ship's location by our pitch and roll. Specifically, the \ calculation is as follows: \ \ 1. K2 = y - alpha * x \ 2. z = z + beta * K2 \ 3. y = K2 - beta * z \ 4. x = x + alpha * y \ \ ****************************************************************************** .MV40 LDA ALPHA \ Set Q = -ALPHA, so Q contains the angle we want to EOR #%10000000 \ roll the planet through (i.e. in the opposite STA Q \ direction to our ship's roll angle alpha) LDA INWK \ Set P(1 0) = (x_hi x_lo) STA P LDA INWK+1 STA P+1 LDA INWK+2 \ Set A = x_sign JSR MULT3 \ Set K(3 2 1 0) = (A P+1 P) * Q \ \ which also means: \ \ K(3 2 1) = (A P+1 P) * Q / 256 \ = x * -alpha / 256 \ = - alpha * x / 256 LDX #3 \ Set K(3 2 1) = (y_sign y_hi y_lo) + K(3 2 1) JSR MVT3 \ = y - alpha * x / 256 LDA K+1 \ Set K2(2 1) = P(1 0) = K(2 1) STA K2+1 STA P LDA K+2 \ Set K2+2 = K+2 STA K2+2 STA P+1 \ Set P+1 = K+2 LDA BETA \ Set Q = beta, the pitch angle of our ship STA Q LDA K+3 \ Set K+3 to K2+3, so now we have result 1 above: STA K2+3 \ \ K2(3 2 1) = K(3 2 1) \ = y - alpha * x / 256 \ We also have: \ \ A = K+3 \ \ P(1 0) = K(2 1) \ \ so combined, these mean: \ \ (A P+1 P) = K(3 2 1) \ = K2(3 2 1) JSR MULT3 \ Set K(3 2 1 0) = (A P+1 P) * Q \ \ which also means: \ \ K(3 2 1) = (A P+1 P) * Q / 256 \ = K2(3 2 1) * beta / 256 \ = beta * K2 / 256 LDX #6 \ K(3 2 1) = (z_sign z_hi z_lo) + K(3 2 1) JSR MVT3 \ = z + beta * K2 / 256 LDA K+1 \ Set P = K+1 STA P STA INWK+6 \ Set z_lo = K+1 LDA K+2 \ Set P+1 = K+2 STA P+1 STA INWK+7 \ Set z_hi = K+2 LDA K+3 \ Set A = z_sign = K+3, so now we have: STA INWK+8 \ \ (z_sign z_hi z_lo) = K(3 2 1) \ = z + beta * K2 / 256 \ So we now have result 2 above: \ \ z = z + beta * K2 EOR #%10000000 \ Flip the sign bit of A to give A = -z_sign JSR MULT3 \ Set K(3 2 1 0) = (A P+1 P) * Q \ = (-z_sign z_hi z_lo) * beta \ = -z * beta LDA K+3 \ Set T to the sign bit of K(3 2 1 0), i.e. to the sign AND #%10000000 \ bit of -z * beta STA T EOR K2+3 \ If K2(3 2 1 0) has a different sign to K(3 2 1 0), BMI MV1 \ then EOR'ing them will produce a 1 in bit 7, so jump \ to MV1 to take this into account \ If we get here, K and K2 have the same sign, so we can \ add them together to get the result we're after, and \ then set the sign afterwards \ --- Mod: Code removed for flicker-free ships: -------> \LDA K \ We now do the following sum: \CLC \ \ADC K2 \ (A y_hi y_lo -) = K(3 2 1 0) + K2(3 2 1 0) \ \ \ \ starting with the low bytes (which we don't keep) \ \ \ \ The CLC has no effect because MULT3 clears the C \ \ flag, so this instruction could be removed (as it is \ \ in the cassette version, for example) \ --- And replaced by: --------------------------------> LDA K \ We now do the following sum: ADC K2 \ \ (A y_hi y_lo -) = K(3 2 1 0) + K2(3 2 1 0) \ \ starting with the low bytes (which we don't keep) \ --- End of replacement ------------------------------> LDA K+1 \ We then do the middle bytes, which go into y_lo ADC K2+1 STA INWK+3 LDA K+2 \ And then the high bytes, which go into y_hi ADC K2+2 STA INWK+4 LDA K+3 \ And then the sign bytes into A, so overall we have the ADC K2+3 \ following, if we drop the low bytes from the result: \ \ (A y_hi y_lo) = (K + K2) / 256 JMP MV2 \ Jump to MV2 to skip the calculation for when K and K2 \ have different signs .MV1 LDA K \ If we get here then K2 and K have different signs, so SEC \ instead of adding, we need to subtract to get the SBC K2 \ result we want, like this: \ \ (A y_hi y_lo -) = K(3 2 1 0) - K2(3 2 1 0) \ \ starting with the low bytes (which we don't keep) LDA K+1 \ We then do the middle bytes, which go into y_lo SBC K2+1 STA INWK+3 LDA K+2 \ And then the high bytes, which go into y_hi SBC K2+2 STA INWK+4 LDA K2+3 \ Now for the sign bytes, so first we extract the sign AND #%01111111 \ byte from K2 without the sign bit, so P = |K2+3| STA P LDA K+3 \ And then we extract the sign byte from K without the AND #%01111111 \ sign bit, so A = |K+3| SBC P \ And finally we subtract the sign bytes, so P = A - P STA P \ By now we have the following, if we drop the low bytes \ from the result: \ \ (A y_hi y_lo) = (K - K2) / 256 \ \ so now we just need to make sure the sign of the \ result is correct BCS MV2 \ If the C flag is set, then the last subtraction above \ didn't underflow and the result is correct, so jump to \ MV2 as we are done with this particular stage LDA #1 \ Otherwise the subtraction above underflowed, as K2 is SBC INWK+3 \ the dominant part of the subtraction, so we need to STA INWK+3 \ negate the result using two's complement, starting \ with the low bytes: \ \ y_lo = 1 - y_lo LDA #0 \ And then the high bytes: SBC INWK+4 \ STA INWK+4 \ y_hi = 0 - y_hi LDA #0 \ And finally the sign bytes: SBC P \ \ A = 0 - P ORA #%10000000 \ We now force the sign bit to be negative, so that the \ final result below gets the opposite sign to K, which \ we want as K2 is the dominant part of the sum .MV2 EOR T \ T contains the sign bit of K, so if K is negative, \ this flips the sign of A STA INWK+5 \ Store A in y_sign \ So we now have result 3 above: \ \ y = K2 + K \ = K2 - beta * z LDA ALPHA \ Set A = alpha STA Q LDA INWK+3 \ Set P(1 0) = (y_hi y_lo) STA P LDA INWK+4 STA P+1 LDA INWK+5 \ Set A = y_sign JSR MULT3 \ Set K(3 2 1 0) = (A P+1 P) * Q \ = (y_sign y_hi y_lo) * alpha \ = y * alpha LDX #0 \ Set K(3 2 1) = (x_sign x_hi x_lo) + K(3 2 1) JSR MVT3 \ = x + y * alpha / 256 LDA K+1 \ Set (x_sign x_hi x_lo) = K(3 2 1) STA INWK \ = x + y * alpha / 256 LDA K+2 STA INWK+1 LDA K+3 STA INWK+2 \ So we now have result 4 above: \ \ x = x + y * alpha JMP MV45 \ We have now finished rotating the planet or sun by \ our pitch and roll, so jump back into the MVEIT \ routine at MV45 to apply all the other movements \ ****************************************************************************** \ \ Name: Checksum \ Type: Subroutine \ Category: Copy protection \ Summary: Checksum the code from &1000 to &9FFF and check against S%-1 \ \ ------------------------------------------------------------------------------ \ \ In the original source, the checksum byte at S%-1 is set by the first call to \ ZP in the Big Code File, though in the BeebAsm version this is populated by \ elite-checksum.py. \ \ The original 6502 assembly language version of the ZP routine can be found in \ the elite-checksum.asm file. \ \ ****************************************************************************** .Checksum SEC \ Set the C flag, so it gets included in the checksum LDY #0 \ Set Y = 0, to act as a byte counter STY V \ Set V = 0 LDX #&10 \ Set X = &10, so we start with (X Y) = &1000 LDA (SC) \ This has no effect, as A is overwritten by the next \ instruction TXA \ Set A = &10 .CHKLoop STX V+1 \ Set V(1 0) = (X 0) STY T \ Set T = Y ADC (V),Y \ Set A = A + C + contents of (V(1 0) + Y) \ = A + C + contents of ((X 0) + Y) \ = A + C + contents of (X Y) EOR T \ Set A = A EOR Y SBC V+1 \ Set A = A - (1 - C) - X DEY \ Decrement the loop counter to process the next byte BNE CHKLoop \ Loop back until we have done the whole page INX \ Increment the page counter to point to the next page CPX #&A0 \ Loop back to do the next page until X = &A0, when BCC CHKLoop \ (X Y) = &A000 CMP S%-1 \ Compare the calculated checksum in A with the checksum \ stored in S%-1 IF _REMOVE_CHECKSUMS NOP \ If we have disabled checksums, then ignore the result NOP \ of the comparison and return from the subroutine ELSE BNE Checksum \ If the checksum we just calculated does not match \ the value in location S%-1, jump to Checksum to enter \ an infinite loop, which crashes the game ENDIF RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PLUT \ Type: Subroutine \ Category: Flight \ Summary: Flip the coordinate axes for the four different views \ Deep dive: Flipping axes between space views \ \ ------------------------------------------------------------------------------ \ \ This routine flips the relevant geometric axes in INWK depending on which \ view we are looking through (front, rear, left, right). \ \ ****************************************************************************** .PLUT LDX VIEW \ Load the current view into X: \ \ 0 = front \ 1 = rear \ 2 = left \ 3 = right BEQ PU2-1 \ If the current view is the front view, return from the \ subroutine (PU2-1 contains an RTS), as the geometry in \ INWK is already correct .PU1 DEX \ Decrement the view, so now: \ \ 0 = rear \ 1 = left \ 2 = right BNE PU2 \ If the current view is left or right, jump to PU2, \ otherwise this is the rear view, so continue on LDA INWK+2 \ Flip the sign of x_sign EOR #%10000000 STA INWK+2 LDA INWK+8 \ Flip the sign of z_sign EOR #%10000000 STA INWK+8 LDA INWK+10 \ Flip the sign of nosev_x_hi EOR #%10000000 STA INWK+10 LDA INWK+14 \ Flip the sign of nosev_z_hi EOR #%10000000 STA INWK+14 LDA INWK+16 \ Flip the sign of roofv_x_hi EOR #%10000000 STA INWK+16 LDA INWK+20 \ Flip the sign of roofv_z_hi EOR #%10000000 STA INWK+20 LDA INWK+22 \ Flip the sign of sidev_x_hi EOR #%10000000 STA INWK+22 LDA INWK+26 \ Flip the sign of roofv_z_hi EOR #%10000000 STA INWK+26 RTS \ Return from the subroutine .PU2 \ We enter this with X set to the view, as follows: \ \ 1 = left \ 2 = right LDA #0 \ Set RAT2 = 0 (left view) or -1 (right view) CPX #2 ROR A STA RAT2 EOR #%10000000 \ Set RAT = -1 (left view) or 0 (right view) STA RAT LDA INWK \ Swap x_lo and z_lo LDX INWK+6 STA INWK+6 STX INWK LDA INWK+1 \ Swap x_hi and z_hi LDX INWK+7 STA INWK+7 STX INWK+1 LDA INWK+2 \ Swap x_sign and z_sign EOR RAT \ If left view, flip sign of new z_sign TAX \ If right view, flip sign of new x_sign LDA INWK+8 EOR RAT2 STA INWK+2 STX INWK+8 LDY #9 \ Swap nosev_x_lo and nosev_z_lo JSR PUS1 \ Swap nosev_x_hi and nosev_z_hi \ If left view, flip sign of new nosev_z_hi \ If right view, flip sign of new nosev_x_hi LDY #15 \ Swap roofv_x_lo and roofv_z_lo JSR PUS1 \ Swap roofv_x_hi and roofv_z_hi \ If left view, flip sign of new roofv_z_hi \ If right view, flip sign of new roofv_x_hi LDY #21 \ Swap sidev_x_lo and sidev_z_lo \ Swap sidev_x_hi and sidev_z_hi \ If left view, flip sign of new sidev_z_hi \ If right view, flip sign of new sidev_x_hi .PUS1 LDA INWK,Y \ Swap the low x and z bytes for the vector in Y: LDX INWK+4,Y \ STA INWK+4,Y \ * For Y = 9 swap nosev_x_lo and nosev_z_lo STX INWK,Y \ * For Y = 15 swap roofv_x_lo and roofv_z_lo \ * For Y = 21 swap sidev_x_lo and sidev_z_lo LDA INWK+1,Y \ Swap the high x and z bytes for the offset in Y: EOR RAT \ TAX \ * If left view, flip sign of new z-coordinate LDA INWK+5,Y \ * If right view, flip sign of new x-coordinate EOR RAT2 STA INWK+1,Y STX INWK+5,Y \ Fall through into LOOK1 to return from the subroutine \ ****************************************************************************** \ \ Name: LOOK1 \ Type: Subroutine \ Category: Flight \ Summary: Initialise the space view \ \ ------------------------------------------------------------------------------ \ \ Initialise the space view, with the direction of view given in X. This clears \ the upper screen and draws the laser crosshairs, if the view in X has lasers \ fitted. It also wipes all the ships from the scanner, so we can recalculate \ ship positions for the new view (they get put back in the main flight loop). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The space view to set: \ \ * 0 = front \ * 1 = rear \ * 2 = left \ * 3 = right \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ LO2 Contains an RTS \ \ ****************************************************************************** .LO2 RTS \ Return from the subroutine .LQ \ --- Mod: Code added for two-player Elite: -----------> BIT drawPlayerView \ If we are setting up player 1's view, jump to view1 to BPL view1 \ store the space view number in VIEW STX player2VIEW \ We are setting up player 2's view, so store the space \ view number for player 2 in player2VIEW JMP view2 \ Skip the following instruction .view1 \ --- End of added code -------------------------------> STX VIEW \ Set the current space view to X \ --- Mod: Code added for two-player Elite: -----------> .view2 STZ drawPlayerView \ Draw both views as we are setting up a new space view JSR view6 SEC ROR drawPlayerView .view6 \ --- End of added code -------------------------------> JSR TT66 \ Clear the top part of the screen, draw a border box, \ and set the current view type in QQ11 to 0 (space \ view) JSR SIGHT \ Draw the laser crosshairs JMP NWSTARS \ Set up a new stardust field and return from the \ subroutine using a tail call .LOOK1 LDA #0 \ Set A = 0, the type number of a space view JSR DOVDU19 \ Send a #SETVDU19 0 command to the I/O processor to \ switch to the mode 1 palette for the space view, \ which is yellow (colour 1), red (colour 2) and cyan \ (colour 3) LDY QQ11 \ If the current view is not a space view, jump up to LQ BNE LQ \ to set up a new space view \ --- Mod: Code added for two-player Elite: -----------> BIT drawPlayerView \ If we are setting up player 1's view, jump to view3 to BPL view3 \ check the new view number against VIEW \ We are setting up player 2's view, so we need to check \ the new view number against player2VIEW CPX player2VIEW \ If the current view is already of type X, jump to LO2 BEQ LO2 \ to return from the subroutine (as LO2 contains an RTS) STX player2VIEW \ Set the current space view for player 2 to X JMP view4 \ Skip the following instruction .view3 \ --- End of added code -------------------------------> CPX VIEW \ If the current view is already of type X, jump to LO2 BEQ LO2 \ to return from the subroutine (as LO2 contains an RTS) STX VIEW \ Change the current space view to X \ --- Mod: Code added for two-player Elite: -----------> .view4 \ --- End of added code -------------------------------> JSR TT66 \ Clear the top part of the screen, draw a border box, \ and set the current view type in QQ11 to 0 (space \ view) JSR FLIP \ Swap the x- and y-coordinates of all the stardust \ particles and redraw the stardust field JSR WPSHPS \ Wipe all the ships from the scanner and mark them all \ as not being shown on-screen \ And fall through into SIGHT to draw the laser \ crosshairs \ ****************************************************************************** \ \ Name: SIGHT \ Type: Subroutine \ Category: Flight \ Summary: Draw the laser crosshairs \ \ ****************************************************************************** .SIGHT \ --- Mod: Code removed for two-player Elite: ---------> \LDY VIEW \ Fetch the laser power for our new view \LDA LASER,Y \ \BEQ LO2 \ If it is zero (i.e. there is no laser fitted to this \ \ view), jump to LO2 to return from the subroutine (as \ \ LO2 contains an RTS) \ --- And replaced by: --------------------------------> BIT drawPlayerView \ If we are setting up player 1's view, jump to view1 to BPL sigh1 \ store the space view number in VIEW LDY player2VIEW \ We are setting up player 2's view, so fetch the space \ view number for player 2 in player2VIEW LDA player2LASER,Y \ Fetch the front/rear laser power for player 2 BEQ LO2 \ If it is zero (i.e. there is no laser fitted to this \ view), jump to LO2 to return from the subroutine (as \ LO2 contains an RTS) BNE sigh2 \ Skip the following instruction .sigh1 LDY VIEW \ Fetch the laser power for player 1's view LDA LASER,Y BEQ LO2 \ If it is zero (i.e. there is no laser fitted to this \ view), jump to LO2 to return from the subroutine (as \ LO2 contains an RTS) .sigh2 \ --- End of replacement ------------------------------> LDA #YELLOW \ Send a #SETCOL YELLOW command to the I/O processor to JSR DOCOL \ switch to colour 1, which is yellow in the space view LDA #128 \ Set QQ19 to the x-coordinate of the centre of the STA QQ19 \ screen \ --- Mod: Code removed for two-player Elite: ---------> \LDA #Y-24 \ Set QQ19+1 to the y-coordinate of the centre of the \STA QQ19+1 \ screen, minus 24 (because TT15 will add 24 to the \ \ coordinate when it draws the crosshairs) \ --- And replaced by: --------------------------------> LDA #(Y/2)-24 \ Set A to the y-coordinate of the centre of the screen, \ minus 24 (because TT15 will add 24 to the coordinate \ when it draws the crosshairs) BIT drawPlayerView \ If we are drawing player 1's view, so draw the sights BPL site1 \ in the bottom half of the screen CLC \ We are drawing player 2's, so move down ADC #Y .site1 STA QQ19+1 \ Set QQ19+1 to the y-coordinate \ --- End of replacement ------------------------------> LDA #20 \ Set QQ19+2 to size 20 for the crosshairs size STA QQ19+2 JSR TT15b \ Call TT15b to draw crosshairs of size 20 just to the \ left of the middle of the screen, in the current \ colour (yellow) LDA #10 \ Set QQ19+2 to size 10 for the crosshairs size STA QQ19+2 JMP TT15b \ Call TT15b to draw crosshairs of size 10 at the same \ location, which will remove the centre part from the \ laser crosshairs, leaving a gap in the middle, and \ return from the subroutine using a tail call \ ****************************************************************************** \ \ Name: TT66 \ Type: Subroutine \ Category: Drawing the screen \ Summary: Clear the screen and set the current view type \ \ ------------------------------------------------------------------------------ \ \ Clear the top part of the screen, draw a border box, and set the current \ view type in QQ11 to A. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The type of the new current view (see QQ11 for a list of \ view types) \ \ ****************************************************************************** .TT66 STA QQ11 \ Set the current view type in QQ11 to A \ Fall through into TTX66 to clear the screen and draw a \ border box \ ****************************************************************************** \ \ Name: TTX66 \ Type: Subroutine \ Category: Drawing the screen \ Summary: Send control code 11 to the I/O processor to clear the top part \ of the screen, draw a border box and configure the specified view \ \ ------------------------------------------------------------------------------ \ \ Clear the top part of the screen (the space view) and draw a border box \ along the top and sides. \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ BOX Just draw the border and (if this is a space view) the \ view name. This can be used to remove the border and \ view name, as it is drawn using EOR logic \ \ ****************************************************************************** .TTX66 JSR MT2 \ Switch to Sentence Case when printing extended tokens JSR PBZE \ Reset the pixel buffer size in PBUP JSR HBZE \ Reset the horizontal line buffer size in HBUP STZ LBUP \ Reset the line buffer size at LBUP \ --- Mod: Code removed for two-player Elite: ---------> \STZ LSP \ Reset the ball line heap pointer at LSP \ --- And replaced by: --------------------------------> JSR ResetBallLine \ Reset the ball line heap for the view we are currently \ drawing \ --- End of replacement ------------------------------> LDA #%10000000 \ Set bit 7 of QQ17 to switch to Sentence Case STA QQ17 STA DTW2 \ Set bit 7 of DTW2 to indicate we are not currently \ printing a word JSR FLFLLS \ Call FLFLLS to reset the LSO block LDA #YELLOW \ Send a #SETCOL YELLOW command to the I/O processor to JSR DOCOL \ switch to colour 2, which is yellow BIT drawPlayerView \ If we are drawing player 2's view, jump to clsc1 to BMI clsc1 \ set player 2's values STZ LAS2 \ Set LAS2 = 0 to stop any laser pulsing STZ DLY \ Set the delay in DLY to 0, to indicate that we are \ no longer showing an in-flight message, so any new \ in-flight messages will be shown instantly BRA clsc2 \ Jump to clsc2 to skip the following \ --- Mod: Code added for two-player Elite: -----------> .clsc1 STZ player2LAS2 \ Set LAS2 = 0 to stop any laser pulsing STZ player2DLY \ Set the delay in DLY to 0, to indicate that we are \ no longer showing an in-flight message, so any new \ in-flight messages will be shown instantly .clsc2 \ --- End of added code -------------------------------> STZ de \ Clear de, the flag that appends " DESTROYED" to the \ end of the next text token, so that it doesn't \ --- Mod: Code added for two-player Elite: -----------> LDA QQ11 \ If this is a space view, enable split-screen so that BNE clsc3 \ circles are clipped correctly SEC ROR splitScreen LDA #14 \ Set A to clear the correct part of the screen: BIT drawPlayerView \ BPL P%+4 \ * 14 = player 1 LDA #15 \ \ * 15 = player 2 JSR OSWRCH \ Send control code 14 or 15 to OSWRCH, to instruct the \ I/O processor to clear the top part of the screen JMP OLDBOX \ Skip the following .clsc3 STZ splitScreen \ Disable split-screen if this isn't a space view \ --- End of added code -------------------------------> LDA #11 \ Send control code 11 to OSWRCH, to instruct the I/O JSR OSWRCH \ processor to clear the top part of the screen \ --- Mod: Code removed for two-player Elite: ---------> \LDX QQ22+1 \ Fetch into X the number that's shown on-screen during \ \ the hyperspace countdown \ \BEQ OLDBOX \ If the counter is zero then we are not counting down \ \ to hyperspace, so jump to OLDBOX to skip the next \ \ instruction \ \JSR ee3 \ Print the 8-bit number in X at text location (0, 1), \ \ i.e. print the hyperspace countdown in the top-left \ \ corner \ --- End of removed code -----------------------------> .OLDBOX LDA #1 \ Move the text cursor to row 1 JSR DOYC LDA QQ11 \ If this is not a space view, jump to tt66 to skip BNE tt66 \ displaying the view name LDA #11 \ Move the text cursor to column 11 JSR DOXC LDA #CYAN \ Send a #SETCOL CYAN command to the I/O processor to JSR DOCOL \ switch to colour 3, which is cyan in the space view \ --- Mod: Code added for two-player Elite: -----------> BIT drawPlayerView \ If we are drawing player 1's view, jump to clsc3 BPL clsc4 \ We are drawing player 2's view, so move the text \ cursor to the top of the bottom view LDA #13 \ Move the text cursor to column 11, row 13 JSR DOYC LDA player2VIEW \ Fetch player 2's view JMP clsc5 \ Skip the following instruction .clsc4 \ --- End of added code -------------------------------> LDA VIEW \ Load the current view into A: \ \ 0 = front \ 1 = rear \ 2 = left \ 3 = right \ --- Mod: Code added for two-player Elite: -----------> .clsc5 \ --- End of added code -------------------------------> ORA #&60 \ OR with &60 so we get a value of &60 to &63 (96 to 99) JSR TT27 \ Print recursive token 96 to 99, which will be in the \ range "FRONT" to "RIGHT" JSR TT162 \ Print a space LDA #175 \ Print recursive token 15 ("VIEW ") JSR TT27 \ --- Mod: Code added for two-player Elite: -----------> BIT drawPlayerView \ If we are drawing player 1's view, jump to clsc6 BPL clsc6 JSR Player2ee3 \ Print player 2's score JMP tt66 \ Skip the following .clsc6 JSR ee3 \ Print player 1's score \ --- End of added code -------------------------------> .tt66 LDA #1 \ Move the text cursor to column 1, row 1 JSR DOXC JSR DOYC LDX #0 \ Set QQ17 = 0 to switch to ALL CAPS STX QQ17 RTS \ Return from the subroutine .BOX LDA #YELLOW \ Send a #SETCOL YELLOW command to the I/O processor to JSR DOCOL \ switch to colour 2, which is yellow LDX #0 \ Set QQ17 = 0 to switch to ALL CAPS STX QQ17 STX X1 \ Set (X1, Y1) to (0, 0) STX Y1 STX Y2 \ Set Y2 = 0 DEX \ Set X2 = 255 STX X2 JSR LL30 \ Draw a line from (X1, Y1) to (X2, Y2), so that's \ (0, 0) to (255, 0), along the very top of the screen LDA #2 \ Set X1 = X2 = 2 STA X1 STA X2 JSR BOS2 \ Call BOS2 below, which will call BOS1 twice, and then \ fall through into BOS2 again, so we effectively do \ BOS1 four times, decrementing X1 and X2 each time \ before calling LOIN, so this whole loop-within-a-loop \ mind-bender ends up drawing these four lines: \ \ (1, 0) to (1, 191) \ (0, 0) to (0, 191) \ (255, 0) to (255, 191) \ (254, 0) to (254, 191) \ \ So that's a two-pixel wide vertical border along the \ left edge of the upper part of the screen, and a \ two-pixel wide vertical border along the right edge .BOS2 JSR BOS1 \ Call BOS1 below and then fall through into it, which \ ends up running BOS1 twice. This is all part of the \ loop-the-loop border-drawing mind-bender explained \ above .BOS1 LDA #0 \ Set Y1 = 0 STA Y1 LDA #2*Y-1 \ Set Y2 = 2 * #Y - 1. The constant #Y is 96, the STA Y2 \ y-coordinate of the mid-point of the space view, so \ this sets Y2 to 191, the y-coordinate of the bottom \ pixel row of the space view DEC X1 \ Decrement X1 and X2 DEC X2 JMP LL30 \ Draw a line from (X1, Y1) to (X2, Y2), and return from \ the subroutine using a tail call \ ****************************************************************************** \ \ Name: DELAY \ Type: Subroutine \ Category: Utility routines \ Summary: Wait for a specified time, in 1/50s of a second \ \ ------------------------------------------------------------------------------ \ \ Wait for the number of vertical syncs given in Y, so this effectively waits \ for Y/50 of a second (as the vertical sync occurs 50 times a second). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ Y The number of vertical sync events to wait for \ \ ****************************************************************************** .DELAY JSR WSCAN \ Call WSCAN to wait for the vertical sync, so the whole \ screen gets drawn DEY \ Decrement the counter in Y BNE DELAY \ If Y isn't yet at zero, jump back to DELAY to wait \ for another vertical sync RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: CLYNS \ Type: Subroutine \ Category: Drawing the screen \ Summary: Clear the bottom three text rows of the mode 1 screen by sending a \ #clyns command to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ A A is set to 0 \ \ Y Y is set to 0 \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ SC5 Contains an RTS \ \ ****************************************************************************** .CLYNS LDA #%11111111 \ Set DTW2 = %11111111 to denote that we are not STA DTW2 \ currently printing a word LDA #%10000000 \ Set bit 7 of QQ17 to switch standard tokens to STA QQ17 \ Sentence Case LDA #21 \ Move the text cursor to column 1, row 21 STA YC LDA #1 STA XC LDA #clyns \ Send a #clyns command to the I/O processor to clear JSR OSWRCH \ the bottom three text rows of the top part of the JSR OSWRCH \ screen LDA #0 \ Set A = 0 TAY \ Set Y = 0 .SC5 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: SCANpars \ Type: Variable \ Category: Dashboard \ Summary: The scanner buffer to send with the #onescan command \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ SCANflg The sign of the stick height (in bit 7) \ \ SCANlen The stick height for the ship on the scanner \ \ SCANcol The colour of the ship on the scanner \ \ SCANx1 The screen x-coordinate of the dot on the scanner \ \ SCANy1 The screen y-coordinate of the dot on the scanner \ \ ****************************************************************************** .SCANpars \ --- Mod: Code removed for two-player Elite: ---------> \EQUB 7 \ The number of bytes to transmit with this command \ --- And replaced by: --------------------------------> EQUB 8 \ The number of bytes to transmit with this command \ --- End of replacement ------------------------------> EQUB 0 \ The number of bytes to receive with this command .SCANflg EQUB 0 \ The sign of the stick height (in bit 7) .SCANlen EQUB 0 \ The stick height for this ship on the scanner .SCANcol EQUB 0 \ The colour of the ship on the scanner .SCANx1 EQUB 0 \ The screen x-coordinate of the dot on the scanner .SCANy1 EQUB 0 \ The screen y-coordinate of the dot on the scanner \ --- Mod: Code added for two-player Elite: -----------> .SCANtype EQUB 0 \ The ship type (so we can draw a single line dot for \ missiles) \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: SCAN \ Type: Subroutine \ Category: Dashboard \ Summary: Display the current ship on the scanner \ Deep dive: The 3D scanner \ \ ------------------------------------------------------------------------------ \ \ This is used both to display a ship on the scanner, and to erase it again. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ INWK The ship's data block \ \ ****************************************************************************** .SCAN \ --- Mod: Code added for two-player Elite: -----------> BIT titleScreen \ If bit 7 of titleScreen is set then the scanner is BMI SC5 \ disabled for the title screen, so return from the \ subroutine CMP #YELLOW2 \ If we are now drawing player 2's scanner in yellow BNE scan1 \ then jump to scan1 to keep going \ If we get here then we are drawing player 2's scanner \ in yellow BIT gameOver \ If this is also the game over screen, then return from BMI SC5 \ the subroutine without drawing anything as we have \ already cleared the scanner of yellow sticks and don't \ want to draw any more .scan1 STA SCANcol \ Store the scanner colour in SCANcol so it can be sent \ to the I/O processor with the #onescan command \ --- End of added code -------------------------------> LDA INWK+31 \ Fetch the ship's scanner flag from byte #31 AND #%00010000 \ If bit 4 is clear then the ship should not be shown BEQ SC5 \ on the scanner, so return from the subroutine (as SC5 \ contains an RTS) LDX TYPE \ Fetch the ship's type from TYPE into X BMI SC5 \ If this is the planet or the sun, then the type will \ have bit 7 set and we don't want to display it on the \ scanner, so return from the subroutine (as SC5 \ contains an RTS) \ --- Mod: Code removed for two-player Elite: ---------> \LDA scacol,X \ Set A to the scanner colour for this ship type from \ \ the X-th entry in the scacol table \ \STA SCANcol \ Store the scanner colour in SCANcol so it can be sent \ \ to the I/O processor with the #onescan command \ \LDA INWK+1 \ If any of x_hi, y_hi and z_hi have a 1 in bit 6 or 7, \ORA INWK+4 \ then the ship is too far away to be shown on the \ORA INWK+7 \ scanner, so return from the subroutine (as SC5 \AND #%11000000 \ contains an RTS) \BNE SC5 \ --- And replaced by: --------------------------------> LDA INWK+1 \ If any of x_hi, y_hi and z_hi have a 1 in bit 5, 6, or ORA INWK+4 \ 7, then the ship is too far away to be shown on the ORA INWK+7 \ scanner, so return from the subroutine (as SC5 AND #%11100000 \ contains an RTS) BNE SC5 \ --- End of replacement ------------------------------> \ If we get here, we know x_hi, y_hi and z_hi are all \ 63 (%00111111) or less \ Now, we convert the x_hi coordinate of the ship into \ the screen x-coordinate of the dot on the scanner, \ using the following: \ \ X1 = 123 + (x_sign x_hi) \ --- Mod: Code removed for two-player Elite: ---------> \LDA INWK+1 \ Set A = x_hi \ --- And replaced by: --------------------------------> LDA INWK+2 \ Set A = x_hi * 2 ASL A LDA INWK+1 ROL A \ --- End of replacement ------------------------------> LDA INWK+1 \ Set A = x_hi CLC \ Clear the C flag so we can do addition below LDX INWK+2 \ Set X = x_sign BPL SC2 \ If x_sign is positive, skip the following EOR #%11111111 \ x_sign is negative, so flip the bits in A and add 1 ADC #1 \ to make it a negative number (bit 7 will now be set \ as we confirmed above that bits 6 and 7 are clear). So \ this gives A the sign of x_sign and gives it a value \ range of -63 (%11000001) to 0 .SC2 \ --- Mod: Code removed for two-player Elite: ---------> \ADC #123 \ Set A = 123 + (x_sign x_hi) \ --- And replaced by: --------------------------------> ADC #127 \ Set A = 127 + (x_sign x_hi) \ --- End of replacement ------------------------------> STA SCANx1 \ Store the x-coordinate in SCANx1 so it can be sent \ to the I/O processor with the #onescan command \ Next, we convert the z_hi coordinate of the ship into \ the y-coordinate of the base of the ship's stick, \ like this: \ \ SC = 220 - (z_sign z_hi) / 4 \ \ though the following code actually does it like this: \ \ SC = 255 - (35 + z_hi / 4) \ --- Mod: Code removed for two-player Elite: ---------> \LDA INWK+7 \ Set A = z_hi / 4 \LSR A \ \LSR A \ So A is in the range 0-15 \ --- And replaced by: --------------------------------> LDA INWK+7 \ Set A = z_hi / 2 LSR A \ \ So A is in the range 0-15 \ --- End of replacement ------------------------------> CLC \ Clear the C flag for the addition below LDX INWK+8 \ Set X = z_sign BPL SC3 \ If z_sign is positive, skip the following EOR #%11111111 \ z_sign is negative, so flip the bits in A and set the SEC \ C flag. As above, this makes A negative, this time \ with a range of -16 (%11110000) to -1 (%11111111). And \ as we are about to do an ADC, the SEC effectively adds \ another 1 to that value, giving a range of -15 to 0 .SC3 ADC #35 \ Set A = 35 + A to give a number in the range 20 to 50 EOR #%11111111 \ Flip all the bits and store in SC, so SC is in the STA SC \ range 205 to 235, with a higher z_hi giving a lower SC \ Now for the stick height, which we calculate using the \ following: \ \ A = - (y_sign y_hi) / 2 \ --- Mod: Code removed for two-player Elite: ---------> \LDA INWK+4 \ Set A = y_hi / 2 \LSR A \ --- And replaced by: --------------------------------> LDA INWK+4 \ Set A = y_hi \ --- End of replacement ------------------------------> CLC \ Clear the C flag LDX INWK+5 \ Set X = y_sign BMI SCD6 \ If y_sign is negative, skip the following, as we \ already have a positive value in A EOR #%11111111 \ y_sign is positive, so flip the bits in A and set the SEC \ C flag. This makes A negative, and as we are about to \ do an ADC below, the SEC effectively adds another 1 to \ that value to implement two's complement negation, so \ we don't need to add another 1 here .SCD6 \ We now have all the information we need to draw this \ ship on the scanner, namely: \ \ X1 = the screen x-coordinate of the ship's dot \ \ SC = the screen y-coordinate of the base of the \ stick \ \ A = the screen height of the ship's stick, with the \ correct sign for adding to the base of the stick \ to get the dot's y-coordinate \ \ First, though, we have to make sure the dot is inside \ the dashboard, by moving it if necessary ADC SC \ Set A = SC + A, so A now contains the y-coordinate of \ the end of the stick, plus the length of the stick, to \ give us the screen y-coordinate of the dot BPL FIXIT \ If the result has bit 0 clear, then the result has \ overflowed and is bigger than 256, so jump to FIXIT to \ set A to the maximum allowed value of 246 (this \ instruction isn't required as we test both the maximum \ and minimum below, but it might save a few cycles) CMP #194 \ If A >= 194, skip the following instruction, as 194 is BCS P%+4 \ the minimum allowed value of A LDA #194 \ A < 194, so set A to 194, the minimum allowed value \ for the y-coordinate of our ship's dot CMP #247 \ If A < 247, skip the following instruction, as 246 is BCC P%+4 \ the maximum allowed value of A .FIXIT LDA #246 \ A >= 247, so set A to 246, the maximum allowed value \ for the y-coordinate of our ship's dot STA SCANy1 \ Store the y-coordinate in SCANy1 so it can be sent \ to the I/O processor with the #onescan command SEC \ Set A = A - SC to get the stick length, by reversing SBC SC \ the ADC SC we did above. This clears the C flag if the \ result is negative (i.e. the stick length is negative) \ and sets it if the result is positive (i.e. the stick \ length is negative) \ So now we have the following: \ \ X1 = the screen x-coordinate of the ship's dot, \ clipped to fit into the dashboard \ \ Y1 = the screen y-coordinate of the ship's dot, \ clipped to fit into the dashboard \ \ SC = the screen y-coordinate of the base of the \ stick \ \ A = the screen height of the ship's stick, with the \ correct sign for adding to the base of the stick \ to get the dot's y-coordinate \ \ C = 0 if A is negative, 1 if A is positive \ \ and we can get on with drawing the dot and stick STA SCANlen \ Store the stick height in SCANlen so it can be sent \ to the I/O processor with the #onescan command ROR SCANflg \ Rotate the C flag into bit 7 of SCANflg, so bit 7 is \ the sign bit of the stick length .SC48 \ --- Mod: Code added for two-player Elite: -----------> LDX TYPE \ Set SCANtype to the ship type so we can draw a single STX SCANtype \ line dot for missiles) \ --- End of added code -------------------------------> LDX #LO(SCANpars) \ Set (Y X) to point to the SCANpars parameter block LDY #HI(SCANpars) LDA #onescan \ Send a #onescan command to the I/O processor to draw JMP OSWORD \ the ship on the scanner, returning from the subroutine \ using a tail call \ ****************************************************************************** \ \ Name: WSCAN \ Type: Subroutine \ Category: Drawing the screen \ Summary: Ask the I/O processor to wait for the vertical sync by sending a \ #wscn command to the I/O processor \ \ ------------------------------------------------------------------------------ \ \ This routine sends a #wscn command to the I/O processor to ask it to wait for \ the vertical sync. \ \ ****************************************************************************** .WSCpars EQUB 2 \ Transmit 2 bytes as part of this command EQUB 2 \ Receive 2 bytes as part of this command EQUW 0 \ This is unused as no parameters are transmitted along \ with this command .WSCAN PHX \ Store X and Y on the stack so we can restore them PHY \ later LDA #wscn \ Set A in preparation for sending a #wscn command LDX #LO(WSCpars) \ Set (Y X) to point to the parameter block above LDY #HI(WSCpars) JSR OSWORD \ Send a #wscn command to the I/O processor to wait for \ the vertical sync PLY \ Restore X and Y from the stack PLX RTS \ Return from the subroutine \ ****************************************************************************** \ \ Save ELTH.bin \ \ ****************************************************************************** PRINT "ELITE H" PRINT "Assembled at ", ~CODE_H% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_H%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_H% PRINT "S.ELTH ", ~CODE_H%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_H% SAVE "3-assembled-output/ELTH.bin", CODE_H%, P%, LOAD% \ ****************************************************************************** \ \ ELITE I FILE \ \ Produces the binary file ELTI.bin that gets loaded by elite-bcfs.asm. \ \ ****************************************************************************** CODE_I% = P% LOAD_I% = LOAD% + P% - CODE% \ ****************************************************************************** \ \ Name: HIMCNT \ Type: Variable \ Category: Demo \ Summary: Used as a loop counter for the Cobra's slow approach in the demo \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.HIMCNT \ \EQUB 0 \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: ZINF2 \ Type: Subroutine \ Category: Universe \ Summary: Reset the INWK workspace and orientation vectors \ Deep dive: Orientation vectors \ \ ------------------------------------------------------------------------------ \ \ Zero-fill the INWK ship workspace and reset the orientation vectors, with \ nosev pointing into the screen. \ \ ****************************************************************************** .ZINF2 LDA #0 \ Set A to 0 so we can zero-fill the workspace LDX #NI%-1 \ There are NI% bytes in the INWK workspace, so set a \ counter in X so we can loop through them .DML1 STA INWK,X \ Zero the X-th byte of the INWK workspace DEX \ Decrement the loop counter BPL DML1 \ Loop back for the next byte, until we have zero-filled \ the last byte at INWK \ Finally, we reset the orientation vectors as follows: \ \ sidev = (1, 0, 0) \ roofv = (0, 1, 0) \ nosev = (0, 0, 1) \ \ 96 * 256 (&6000) represents 1 in the orientation \ vectors, and we already set the vectors to zero above, \ so we just need to set up the high bytes of the \ diagonal values and we're done LDA #96 \ Set A to represent a 1 (in normalised vector terms) STA INWK+18 \ Set byte #18 = roofv_y_hi = 96 = 1 STA INWK+22 \ Set byte #22 = sidev_x_hi = 96 = 1 STA INWK+14 \ Set byte #14 = nosev_z_hi = 96 = 1 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: TWIST \ Type: Subroutine \ Category: Demo \ Summary: Pitch the current ship by a small angle in a positive direction \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ TWIST2 Pitch in the direction given in A \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.TWIST2 \ \EQUB &2C \ Skip the next instruction by turning it into \ \ &2C &A9 &00, or BIT &00A9, which does nothing apart \ \ from affect the flags \ \.TWIST \ \LDA #0 \ Set A = 0 \ \STA RAT2 \ Set the pitch direction in RAT2 to A \ \LDX #15 \ Rotate (roofv_x, nosev_x) by a small angle (pitch) \LDY #9 \ in the direction given in RAT2 \JSR MVS5 \ \LDX #17 \ Rotate (roofv_y, nosev_y) by a small angle (pitch) \LDY #11 \ in the direction given in RAT2 \JSR MVS5 \ \LDX #19 \ Rotate (roofv_z, nosev_z) by a small angle (pitch) \LDY #13 \ in the direction given in RAT2 and return from the \JMP MVS5 \ subroutine using a tail call \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: STORE \ Type: Subroutine \ Category: Universe \ Summary: Copy the ship data block at INWK back to the K% workspace \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ INF The ship data block in the K% workspace to copy INWK to \ \ ****************************************************************************** .STORE LDY #NI%-1 \ Set a counter in Y so we can loop through the NI% \ bytes in the ship data block .DML2 LDA INWK,Y \ Load the Y-th byte of INWK and store it in the Y-th STA (INF),Y \ byte of INF DEY \ Decrement the loop counter BPL DML2 \ Loop back for the next byte, until we have copied the \ last byte from INWK back to INF RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: DEMON \ Type: Subroutine \ Category: Demo \ Summary: Show the demo \ Deep dive: The 6502 Second Processor demo mode \ Secrets of the Executive version \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.DEMON \ \LDA #1 \ Clear the top part of the screen, draw a border box, \JSR TT66 \ and set the current view type in QQ11 to 1 \ \JSR RESET \ Call RESET to initialise most of the game variables \ \LDA #0 \ Set ALPHA and ALP1 to 0, so our roll angle (i.e. that \STA ALPHA \ of the camera) is 0 \STA ALP1 \ \STA DELTA \ Set DELTA to 0, so our current speed (i.e. that of the \ \ camera) is 0 \ \STA scacol+CYL \ Set the scanner colour for the Cobra Mk III to colour \ \ 0 (black), so it doesn't appear on the scanner during \ \ the demo \ \JSR DOVDU19 \ Send a #SETVDU19 0 command to the I/O processor to \ \ switch to the mode 1 palette for the space view, \ \ which is yellow (colour 1), red (colour 2) and cyan \ \ (colour 3) \ \JSR nWq \ Call nWq to create a random cloud of stardust \ \LDX #LO(acorn) \ Set (Y X) to the address of acorn, which contains the \LDY #HI(acorn) \ text: "ACORNSOFT PRESENTS" (or, in the Executive \ \ version: "PIZZASOFT PRESENTS") \ \JSR SLIDE \ Call SLIDE to display the Star Wars scroll text \ \JSR ZINF2 \ Call ZINF2 to reset INWK and the orientation vectors, \ \ with nosev pointing into the screen. We are about to \ \ add the Elite logo to the universe, and the logo's \ \ nosev points out of the top of the logo, so this will \ \ spawn the logo with it tilted back so it appears \ \ on-edge, with the bottom of the logo pointing towards \ \ the viewer \ \LDA #%10000000 \ Set y_sign to be negative \STA INWK+5 \ \LDA #100 \ Set y_lo = 100 \STA INWK+3 \ \LDA #LGO \ Set the ship type to the Elite logo \STA TYPE \ \JSR NWSHP \ Add a new Elite logo to the local bubble (in this \ \ case, the demo screen), pointing INF to the new ship's \ \ data block in K% \ \LDA #150 \ Set a loop counter in MCNT to 150 for the grand \STA MCNT \ entrance of the logo, which takes it from z = 0 (right \ \ up against the camera) to z = 450 (a fair distance in \ \ front of us), moving the logo a distance of 3 away \ \ from us with each iteration \ \.FLYL1 \ \LDA INWK+6 \ Set (z_hi z_lo) = (z_hi z_lo) + 3 \CLC \ \ADC #3 \ starting with the low bytes \STA INWK+6 \ \LDA INWK+7 \ And then adding the high bytes \ADC #0 \STA INWK+7 \ \JSR LL9 \ Call LL9 to draw the logo on-screen \ \DEC MCNT \ Decrement the main loop counter \ \BNE FLYL1 \ Loop back to FLYL1 until we have done 150 iterations, \ \ after which (z_hi z_lo) = 150 * 3 = 450 \ \.FLYL2 \ \LDA INWK+6 \ Set (z_hi z_lo) = (z_hi z_lo) + 2 \CLC \ \ADC #2 \ starting with the low bytes \STA INWK+6 \ \LDA INWK+7 \ And then adding the high bytes \ADC #0 \STA INWK+7 \ \LDA #%10000000 \ Call TWIST2 with a negative A to pitch the logo by a \JSR TWIST2 \ small angle in a negative direction \ \\DEC INWK+3 \ This instruction is commented out in the original \ \ source, but it would decrement y_lo, moving the logo \ \ down the screen \ \JSR LL9 \ Call LL9 to draw the logo on-screen \ \LDA INWK+14 \ Loop back to FLYL2 to keep pitching, until nosev_z_hi \BPL FLYL2 \ is negative (i.e. the logo has pitched forward through \ \ 90 degrees, as nosev starts out by pointing into the \ \ screen towards the camera, in a positive direction, \ \ and turns negative when it's reached the vertical). \ \ In other words, we loop until the logo has tilted \ \ towards the camera and is fully vertical in front of \ \ the viewer \ \JSR STORE \ Call STORE to copy the ship data block at INWK back to \ \ the K% workspace at INF, so the logo becomes the first \ \ ship in the K% block \ \JSR ZINF2 \ Call ZINF2 to reset INWK and the orientation vectors, \ \ with nosev pointing into the screen \ \LDA #108 \ Set y_lo = 108, so the Cobra we are about to spawn \STA INWK+3 \ appears at the top of the screen \ \LDA #40 \ Set z_lo = -40, so it appears a small distance behind \STA INWK+6 \ the camera (the negative part is achieved by setting \LDA #%10000000 \ z_sign in INWK+8 to be negative, so the ship is behind \STA INWK+8 \ the camera) \ \LDA #CYL \ Set the ship type to a Cobra Mk III \STA TYPE \ \JSR NWSHP \ Add a new Cobra Mk III to the local bubble (in this \ \ case, the demo screen), pointing INF to the new ship's \ \ data block in K% \ \LDA #1 \ Set the Cobra's speed to 1 \STA INWK+27 \ \STA HIMCNT \ Set HIMCNT = 1 to act as an outer counter for the \ \ following loop \ \LDA #90 \ Set MCNT = 90 to act as an inner counter for the \STA MCNT \ following loop \ \JSR TWIST \ Call TWIST three times to pitch by a smallish angle in \JSR TWIST \ a positive direction (i.e. 3 x 3.6 degree, or 10.8 \JSR TWIST \ degrees) \ \ \ The following loop iterates 90 times while HIMCNT is 1 \ \ and 256 times while HIMCNT is 0, to give a total of \ \ 346 iterations \ \.FLYL4 \ \JSR LL9 \ Call LL9 to draw the Cobra on-screen \ \JSR MVEIT \ Call MVEIT to move the Cobra slowly forward \ \DEC MCNT \ Decrement the inner counter in MCNT \ \BNE FLYL4 \ Loop back to FLYL4 until the inner counter is 0 \ \DEC HIMCNT \ Decrement the outer counter in HIMCNT \ \BPL FLYL4 \ Loop back to FLYL4 until the outer counter is negative \ \JSR ZZAAP \ Call ZZAAP to draw a vertical laser line from the \ \ Cobra \ \LDA #0 \ Call the NOISE routine with A = 0 to make the sound \JSR NOISE \ of a laser firing \ \LDY #10 \ Wait for 10/50 of a second (0.2 seconds) \JSR DELAY \ \LDA #44 \ Set the Cobra's roll counter to 44 to make it roll in \STA INWK+29 \ a positive direction (clockwise), for just under a \ \ half roll (44 * 1/16 radians = 2.75 radians = 158 \ \ degrees) \ \LDA #8 \ Set the Cobra's speed to 8 \STA INWK+27 \ \LDA #&87 \ Set the Cobra's pitch counter to -7 to make it pitch \STA INWK+30 \ slightly in a negative direction (pull up), so it \ \ starts flying gently towards the top of the screen \ \JSR STORE \ Call STORE to copy the ship data block at INWK back to \ \ the K% workspace at INF, so the Cobra becomes the \ \ second ship in the K% block \ \LDA #%10000000 \ Set bit 7 of byte #31 of the first ship in the K% \TSB K%+31 \ block, which is the Elite logo, so this flags the logo \ \ as having been killed (the TSB instruction applies the \ \ accumulator to the memory location using an OR) \ \JSR EXNO3 \ Make an explosion sound \ \JSR ZZAAP \ Call ZZAAP to redraw the vertical laser line from the \ \ Cobra, which removes it from the screen \ \.FLYL5 \ \ \ We now want to draw the logo exploding, so we first \ \ need to copy the logo's ship data block from K% to \ \ INWK \ \LDX #NI%-1 \ Set a counter in X so we can loop through the NI% \ \ bytes in the ship data block \ \.DML3 \ \LDA K%,X \ Copy the X-th byte of the first ship data block in K% \STA INWK,X \ to the X-th byte of INWK \ \DEX \ Decrement the loop counter \ \BPL DML3 \ Loop back for the next byte, until we have copied the \ \ last byte from K% to INWK \ \INX \ Increment X back to 0 \ \JSR GINF \ Call GINF to fetch the address of the ship data block \ \ for the ship in slot 0 (the logo) and store it in INF \ \LDA XX21-2+2*LGO \ Set XX0(1 0) to point to the ship blueprint for the \STA XX0 \ Elite logo \LDA XX21-1+2*LGO \STA XX0+1 \ \LDA #LGO \ Set the ship type to the Elite logo \STA TYPE \ \INC INWK \ Increment x_lo to move the logo a little to the right \ \JSR LL9 \ Call LL9 to draw the now-exploding logo on-screen \ \JSR STORE \ Call STORE to copy the ship data block at INWK back to \ \ the K% workspace at INF \ \JSR PBFL \ Call PBFL to send the contents of the pixel buffer to \ \ the I/O processor for plotting on-screen \ \LDA INWK+31 \ Test whether bits 5 and 7 of the logo's byte #31 are \AND #%10100000 \ both set ("ship is exploding" and "ship has been \CMP #%10100000 \ killed") and store the resulting flags on the stack \PHP \ \ \ We now want to animate the Cobra flying into the \ \ distance while rolling clockwise, so we first need to \ \ copy the logo's ship data block from K% to INWK \ \LDX #NI%-1 \ Set a counter in X so we can loop through the NI% \ \ bytes in the ship data block \ \.DML4 \ \LDA K%+NI%,X \ Copy the X-th byte of the second ship data block in K% \STA INWK,X \ to the X-th byte of INWK \ \DEX \ Decrement the loop counter \ \BPL DML4 \ Loop back for the next byte, until we have copied the \ \ last byte from K% to INWK \ \LDX #1 \ Call GINF to fetch the address of the ship data block \JSR GINF \ for the ship in slot 1 (the Cobra) and store it in INF \ \LDA XX21-2+2*CYL \ Set XX0(1 0) to point to the ship blueprint for a \STA XX0 \ Cobra Mk III \LDA XX21-1+2*CYL \STA XX0+1 \ \LDA #CYL \ Set the ship type to a Cobra Mk III \STA TYPE \ \JSR MVEIT \ Call MVEIT to move the Cobra so it pitches, rolls and \ \ flies on \ \JSR LL9 \ Call LL9 to draw the Cobra on-screen \ \JSR STORE \ Call STORE to copy the ship data block at INWK back to \ \ the K% workspace \ \PLP \ Restore the flags that we stored on the stack above \ \BNE FLYL5 \ If it is not the case that bits 5 and 7 of the logo's \ \ byte #31 are both set, jump back to FLYL5 to keep \ \ drawing the exploding logo and the Cobra \ \ \ The logo has now finished exploding, as bits 5 and 7 \ \ of the logo's byte #31 are now both set ("ship is \ \ exploding" and "ship has been killed"). By this time \ \ the Cobra will have reached the upper part of the \ \ screen, where we leave it while we do the next bit of \ \ scrolling text \ \LDA #14 \ Set DELTA to 14, so our current speed (i.e. that of \STA DELTA \ the camera) is 14 \ \STZ DELT4 \ Set DELT4 = 0 \ \LSR A \ Set DELT4+1 = 14 / 4 \ROR DELT4 \ \LSR A \ so DELT4(1 0) therefore contains 14 * 64 \ROR DELT4 \STA DELT4+1 \ \IF _EXECUTIVE \ \LDX #LO(executive) \ Set (Y X) to the address of executive, which contains \LDY #HI(executive) \ the text: "THE EXECUTIVE VERSION" \ \JSR SLIDE \ Call SLIDE to display the Star Wars scroll text \ \ENDIF \ \LDX #LO(byian) \ Set (Y X) to the address of byian, which contains the \LDY #HI(byian) \ text: "BY IAN BELL AND DAVID BRABEN" \ \JSR SLIDE \ Call SLIDE to display the Star Wars scroll text \ \ \ We now want the Cobra to start flying again, so we \ \ first need to copy the logo's ship data block from K% \ \ to INWK \ \LDX #NI%-1 \ Set a counter in X so we can loop through the NI% \ \ bytes in the ship data block \ \.DML5 \ \LDA K%+NI%,X \ Copy the X-th byte of the second ship data block in K% \STA INWK,X \ to the X-th byte of INWK \ \DEX \ Decrement the loop counter \ \BPL DML5 \ Loop back for the next byte, until we have copied the \ \ last byte from K% to INWK \ \.FLYL6 \ \JSR STARS1 \ Call STARS1 to process the stardust for the front view \ \JSR MVEIT \ Call MVEIT to move the Cobra so it pitches, rolls and \ \ flies onwards \ \JSR LL9 \ Call LL9 to draw the Cobra on-screen \ \LDA INWK+8 \ Keep looping back to FLYL6 until z_sign is negative, \BPL FLYL6 \ i.e. the Cobra has been overtaken by the camera as the \ \ camera moves forward, and it's disappeared off the top \ \ of the screen \ \LDX #LO(true3) \ Set (Y X) to the address of true3, which contains the \LDY #HI(true3) \ text: "THE GALAXY IS IN TURMOIL, THE NAVY FAR AWAY AS \ \ THE EMPIRE CRUMBLES" (or, in the Executive version: \ \ "CONGRATULATIONS ON OBTAINING A COPY OF THIS ELUSIVE \ \ PRODUCT") \ \JSR SLIDE \ Call SLIDE to display the Star Wars scroll text \ \JSR RES2 \ Reset a number of flight variables and workspaces \ \LDA #&E0 \ Set nosev_z_hi = -1 (as &E0 is a negative unit vector \STA INWK+14 \ length), so the ship points out of the screen, towards \ \ us \ \STZ DELTA \ Set DELTA to 0, so our current speed (i.e. that of the \ \ camera) is 0 \ \STZ ALPHA \ Set ALPHA and ALP1 to 0, so our roll angle (i.e. that \STZ ALP1 \ of the camera) is 0 \ \LDX #15 \ Set the ship's speed to 15 \STX INWK+27 \ \LDX #5 \ Set the ship's z_hi to 5, so it's in the distance \STX INWK+7 \ \LDA #ADA \ Set the ship type to an Adder \STA TYPE \ \JSR NWSHP \ Add a new Adder to the local bubble (in this case, the \ \ demo screen) \ \.FLYL7 \ \JSR MVEIT \ Call MVEIT to move the Adder so it flies towards the \ \ camera \ \JSR LL9 \ Call LL9 to draw the Adder on-screen \ \LDA INWK+7 \ Keep looping back to FLYL7 until z_hi is zero \BNE FLYL7 \ \LDA #3 \ Set the Adder's roll counter to 3 to make it roll in \STA INWK+29 \ a positive direction (clockwise) \ \STA INWK+30 \ Set the Adder's pitch counter to 3 to make it pitch in \ \ a positive direction (dive) \ \LDA INWK+8 \ Keep looping back to FLYL7 until z_sign is negative \BPL FLYL7 \ i.e. the Adder has flown past the camera \ \JSR SCAN \ Call SCAN to remove the adder from the scanner (by \ \ redrawing it) \ \JSR RES2 \ Reset a number of flight variables and workspaces \ \LDA #CYAN2 \ Set the scanner colour for the Cobra Mk III to its \STA scacol+CYL \ default colour of cyan, so Cobras will appear on the \ \ scanner once again \ \JMP DEATH2 \ Jump to DEATH2 to reset most of the game and restart \ \ from the title screen \ \RTS \ Return from the subroutine \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: SLIDE \ Type: Subroutine \ Category: Demo \ Summary: Display a Star Wars scroll text \ Deep dive: The 6502 Second Processor demo mode \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ (Y X) The contents of the scroll text to display \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.SLIDE \ \JSR GRIDSET \ Call GRIDSET to populate the line coordinate tables at \ \ X1TB, Y1TB, X2TB and Y2TB (the TB tables) with the \ \ lines for the scroll text in (Y X) \ \ \ The following section does the following: \ \ \ \ * Clear the VB tables (X1VB, Y1VB, X2VB and Y2VB) \ \ \ \ * Call GRID with values of BALI dropping by 2 each \ \ time, from 254 to 252 to 250 ... to 6 to 4 to 2, \ \ to display the scroll text moving up the screen \ \ and into the distance \ \ \ \ * Clear the VB tables \ \ \ \ * Call GRID with BALI = 2 to erase the final set of \ \ lines from the screen \ \JSR ZEVB \ Call ZEVB to zero-fill the Y1VB variable, which \ \ effectively clears all the VB tables as we only check \ \ the Y1VB table for zero values \ \LDA #YELLOW \ Send a #SETCOL YELLOW command to the I/O processor to \JSR DOCOL \ switch to colour 2, which is yellow \ \LDA #254 \ Set BALI = 254 to act as a counter from 254 to 2, \STA BALI \ decreasing by 2 each iteration, which represents the \ \ scrolling of the Star Wars scroll text up the screen \ \ and into the distance \ \.SLL2 \ \JSR GRID \ Call GRID to draw the Star Wars scroll text at the \ \ scroll position in BALI \ \DEC BALI \ Set BALI = BALI - 2 to move the scroll text up the \DEC BALI \ screen and into the distance \ \BNE SLL2 \ Loop back to SLL2 until the loop counter is 0 (so GRID \ \ was last called with BALI = 2) \ \.SL1 \ \JSR ZEVB \ Call ZEVB to zero-fill the Y1VB variable, which \ \ effectively clears all the VB tables as we only check \ \ the Y1VB table for zero values \ \LDA #2 \ Set BALI = 2 and fall into GRID below to redraw the \STA BALI \ last set of scroll text, which erases it from the \ \ screen \ \.GRID \ \ \ The GRID routine draws the Star Wars scroll text, with \ \ the value in BALI determining the scroll position of \ \ the perspective view, starting from 254 (not yet \ \ on-screen) and going down to 2 (the scroll has almost \ \ faded into the distance) \ \ \ \ The routine loops through the lines we just put in the \ \ TB tables, projects them into a Star Wars-like \ \ perspective scroll view in 3D space, then projects \ \ them onto the 2D screen, saving the resulting screen \ \ coordinates into the VB table. It then calls the \ \ GRIDEX routine to erase the lines from the previous \ \ call to GRID, and draw the new ones \ \LDY #0 \ Set Y = 0, to act as an index into the TB tables, \ \ where we put the line coordinates above \ \STY UPO \ Set UPO = 0, to act as an index into the UB tables \ \STY INWK+8 \ Set z_sign = 0 \ \STY INWK+1 \ Set x_hi = 0 \ \STY INWK+4 \ Set y_hi = 0 \ \DEY \ Decrement Y to 255, so the following loop starts with \ \ Y pointing to the first byte from the TB tables \ \.GRIDL \ \INY \ Increment Y to point to the next pair of line \ \ coordinates in the TB tables \ \ \ We now fetch the line's start point as 3D space \ \ coordinates, project it onto the Star Wars perspective \ \ scroll text, and project it again onto the 2D screen \ \STZ INWK+7 \ Set z_hi = 0 \ \LDA Y1TB,Y \ Set A to the y-coordinate of the line's start point, \ \ let's call it Y1 \ \BNE P%+5 \ If A = 0, jump to GREX to draw the projected lines \JMP GREX \ as we have now processed all of them \ \SEC \ Set A = A - BALI \SBC BALI \ = Y1 - BALI \ \BCC GRIDL \ If Y1 < BALI, jump back to GRIDL to process the next \ \ line, as this one is not yet on-screen \ \STA R \ Set R = Y1 - BALI \ \ASL A \ Shift bits 6-7 of A into bits 0-1 of z_hi, so the C \ROL INWK+7 \ flag is clear (as we set z_hi to 0 above) and z_hi is \ASL A \ the high byte if A * 4 = (Y1 - BALI) * 4 is expressed \ROL INWK+7 \ as a 16-bit value, i.e. HI((Y1 - BALI) * 4) \ \ADC #D \ Set (z_hi z_lo) = (z_hi z_lo) + #D \STA INWK+6 \ \ \ first adding the low bytes \ \LDA INWK+7 \ And then adding the high bytes, so we now have: \ADC #0 \ \STA INWK+7 \ (z_hi z_lo) = HI((Y1 - BALI) * 4) + #D \ \ \ \ so because we set z_sign to 0 above, we have: \ \ \ \ (z_sign z_hi z_lo) = HI((Y1 - BALI) * 4) + #D \ \STZ S \ Set S = 0 \ \LDA #%10000000 \ Set A to a negative sign byte \ \STA P \ Set P = 128 \ \JSR ADD \ Set (A X) = (A P) + (S R) \ \ = -128 + (0 R) \ \ = -128 + R \ \ = -128 + (Y1 - BALI) \ \ = Y1 - BALI - 128 \ \STA INWK+5 \ Set (y_sign y_lo) = (A X) \STX INWK+3 \ = Y1 - BALI - 128 \ \ \ \ so because we set y_hi to 0 above, we have: \ \ \ \ (y_sign y_hi y_lo) = Y1 - BALI - 128 \ \LDA X1TB,Y \ Set A to the x-coordinate of the line's start point, \ \ let's call it X1. A is in the range 0 to 255, and we \ \ now need to move the coordinate to the left so it's in \ \ the range -128 to +128, but we need to put the result \ \ into (x_sign x_hi x_lo) which is a sign-magnitude \ \ number, so we can't just subtract 128, as that would \ \ give us a two's complement number \ \EOR #%10000000 \ Flip the sign bit of A \ \BPL GR2 \ If bit 7 is now clear, meaning it was previously set, \ \ then jump to GR2 as the original A was in the range \ \ 128 to 255, and we now have the correct result for \ \ A = A - 128, which is also |A - 128| as A was positive \ \ \ Otherwise bit 7 was previously clear, so A was in the \ \ range 0 to 127 and the EOR has shifted that up to 128 \ \ to 255, so we need to negate the number so that 128 \ \ becomes 0, 129 becomes 1 and so on \ \EOR #%11111111 \ Negate the result in A by flipping all the bits and \INA \ adding 1, i.e. using two's complement to negate it to \ \ set A to the magnitude part of the sign-magnitude \ \ number A - 128, i.e. |A - 128| \ \.GR2 \ \STA INWK \ Set x_lo = |A - 128| \ \ = |X1 - 128| \ \LDA X1TB,Y \ Set x_sign to the opposite of bit 7 in X1, so it will \EOR #%10000000 \ be positive if X1 > 127 and negative if X1 <= 127, so \AND #%10000000 \ x_sign has the correct sign for X1 - 128: \STA INWK+2 \ \ \ (x_sign x_lo) = X1 - 128 \ \ \ \ and because we set x_hi to 0 above, we have: \ \ \ \ (x_sign x_hi x_lo) = X1 - 128 \ \STY YS \ Store Y, the index into the TB tables, in YS \ \JSR PROJ \ Project the line's start coordinate onto the screen, \ \ returning: \ \ \ \ * K3(1 0) = the screen x-coordinate \ \ * K4(1 0) = the screen y-coordinate \ \LDY YS \ Retrieve the value of Y from YS, so it once again \ \ contains the index into the TB tables \ \LDA K3 \ Set XX15(1 0) = K3(1 0) \STA XX15 \LDA K3+1 \STA XX15+1 \ \LDA K4 \ Set XX15(3 2) = K4(1 0) \STA XX15+2 \LDA K4+1 \STA XX15+3 \ \ \ We now fetch the line's end point as 3D space \ \ coordinates, project it onto the Star Wars perspective \ \ scroll text, and project it again onto the 2D screen \ \STZ INWK+7 \ Set x_hi = 0 \ \LDA Y2TB,Y \ Set A to Y2, the end point's y-coordinate from Y2TB \ \SEC \ Set A = A - BALI \SBC BALI \ = Y2 - BALI \ \BCC GR6 \ If Y2 < BALI, jump down to GR6 to process the next \ \ line, as this one is not yet on-screen \ \STA R \ Set R = Y2 - BALI \ \ASL A \ Shift bits 6-7 of A into bits 0-1 of z_hi, so the C \ROL INWK+7 \ flag is clear (as we set z_hi to 0 above) and z_hi is \ASL A \ the high byte if A * 4 = (Y2 - BALI) * 4 is expressed \ROL INWK+7 \ as a 16-bit value, i.e. HI((Y2 - BALI) * 4) \ \ADC #D \ Set (z_hi z_lo) = (z_hi z_lo) + #D \STA INWK+6 \ \ \ first adding the low bytes \ \LDA INWK+7 \ And then adding the high bytes, so we now have: \ADC #0 \ \STA INWK+7 \ (z_hi z_lo) = HI((Y2 - BALI) * 4) + #D \ \ \ \ so because we set z_sign to 0 above, we have: \ \ \ \ (z_sign z_hi z_lo) = HI((Y2 - BALI) * 4) + #D \ \STZ S \ Set S = 0 \ \LDA #%10000000 \ Set A to a negative sign byte \ \STA P \ Set P = 128 \ \JSR ADD \ Set (A X) = (A P) + (S R) \ \ = -128 + (0 R) \ \ = -128 + R \ \ = -128 + (Y2 - BALI) \ \ = Y2 - BALI - 128 \ \STA INWK+5 \ Set (y_sign y_lo) = (A X) \STX INWK+3 \ = Y2 - BALI - 128 \ \ \ \ so because we set y_hi to 0 above, we have: \ \ \ \ (y_sign y_hi y_lo) = Y2 - BALI - 128 \ \LDA X2TB,Y \ Set A to the x-coordinate of the line's start point, \ \ let's call it X2. A is in the range 0 to 255, and we \ \ now need to move the coordinate to the left so it's in \ \ the range -128 to +128, but we need to put the result \ \ into (x_sign x_hi x_lo) which is a sign-magnitude \ \ number, so we can't just subtract 128, as that would \ \ give us a two's complement number \ \EOR #%10000000 \ Flip the sign bit of A \ \BPL GR3 \ If bit 7 is now clear, meaning it was previously set, \ \ then jump to GR3 as the original A was in the range \ \ 128 to 255, and we now have the correct result for \ \ A = A - 128, which is also |A - 128| as A was positive \ \ \ Otherwise bit 7 was previously clear, so A was in the \ \ range 0 to 127 and the EOR has shifted that up to 128 \ \ to 255, so we need to negate the number so that 128 \ \ becomes 0, 129 becomes 1 and so on \ \EOR #%11111111 \ Negate the result in A by flipping all the bits and \INA \ adding 1, i.e. using two's complement to negate it to \ \ set A to the magnitude part of the sign-magnitude \ \ number A - 128, i.e. |A - 128| \ \.GR3 \ \STA INWK \ Set x_lo = |A - 128| \ \ = |X2 - 128| \ \LDA X2TB,Y \ Set x_sign to the opposite of bit 7 in X2, so it will \EOR #%10000000 \ be positive if X2 > 127 and negative if X2 <= 127, so \AND #%10000000 \ x_sign has the correct sign for X2 - 128: \STA INWK+2 \ \ \ (x_sign x_lo) = X2 - 128 \ \ \ \ and because we set x_hi to 0 above, we have: \ \ \ \ (x_sign x_hi x_lo) = X2 - 128 \ \JSR PROJ \ Project the line's end coordinate onto the screen, \ \ returning: \ \ \ \ * K3(1 0) = the screen x-coordinate \ \ * K4(1 0) = the screen y-coordinate \ \LDA K3 \ Set XX15(5 4) = K3(1 0) \STA XX15+4 \LDA K3+1 \STA XX15+5 \ \LDA K4 \ Set XX12(1 0) = K4(1 0) \STA XX12 \LDA K4+1 \STA XX12+1 \ \ \ We now have our line, projected onto the Star Wars \ \ perspective scroll text and then onto the screen, so \ \ we can clip it and store it in the UB tables for \ \ drawing later, once we have processed all the lines in \ \ the scroll text in the same way \ \JSR LL145 \ Call LL145 to see if the new line segment needs to be \ \ clipped to fit on-screen, returning the clipped line's \ \ end-points in (X1, Y1) and (X2, Y2) \ \LDY YS \ Retrieve the value of Y from YS, so it once again \ \ contains the index into the TB tables \ \BCS GR6 \ If the C flag is set then the line is not visible on \ \ screen, so loop back to GRIDL via GR6 to process the \ \ next line to draw in the scroll text \ \INC UPO \ Increment the table pointer in UPO to point to the \ \ next free slot in the UB tables \ \LDX UPO \ Load the UPO table pointer into X \ \LDA X1 \ Store the line coordinates (X1, Y1) and (X2, Y2) in \STA X1UB,X \ the next free slot in the UB tables \LDA Y1 \STA Y1UB,X \LDA X2 \STA X2UB,X \LDA Y2 \STA Y2UB,X \ \.GR6 \ \JMP GRIDL \ Loop back to GRIDL to process the next line to draw \ \ in the scroll text \ \.GREX \ \ \ If we get here then it's time to draw the lines we \ \ just projected, and remove any lines that are already \ \ on-screen \ \ \ \ The VB table holds the new lines to draw, while UB \ \ holds all their previous coordinates, i.e. the current \ \ lines on-screen, so drawing the VB lines draws the \ \ lines in their new positions, while drawing the UB \ \ lines erases the old lines from the screen \ \LDY UPO \ Set Y to the UPO table pointer, which contains the \ \ number of coordinates in the X1UB, Y1UB, X2UB and Y2UB \ \ tables (i.e. the number of lines we projected) \ \BEQ GREX2 \ If UPO = 0 then there are no projected lines to draw, \ \ so jump to GREX2 to return from the subroutine \ \ \ We now loop through the projected lines, using Y as a \ \ loop counter that doubles as an index into the line \ \ coordinate tables \ \ \ \ First we draw the Y-th line from the VB table, if \ \ there is one, and then we draw the Y-th line from the \ \ UB table, before copying the Y-th VB line coordinates \ \ into the UB table (so the UB table contains the lines \ \ that are now on-screen) \ \.GRL2 \ \LDA Y1VB,Y \ If there is no Y-th line in the VB table, jump to GR4 \BEQ GR4 \ \STA Y1 \ Otherwise copy the Y-th line's coordinates from the VB \LDA X1VB,Y \ table into (X1, Y1) and (X2, Y2) \STA X1 \LDA X2VB,Y \STA X2 \LDA Y2VB,Y \STA Y2 \ \JSR LOIN \ Draw the line from (X1, Y1) to (X2, Y2) \ \.GR4 \ \LDA X1UB,Y \ Copy the Y-th line's coordinates from the UB table \STA X1 \ into both the VB table and into (X1, Y1) and (X2, Y2) \STA X1VB,Y \LDA Y1UB,Y \STA Y1 \STA Y1VB,Y \LDA X2UB,Y \STA X2 \STA X2VB,Y \LDA Y2UB,Y \STA Y2 \STA Y2VB,Y \ \JSR LOIN \ Draw a line from (X1, Y1) to (X2, Y2) \ \DEY \ Decrement the number of coordinates in Y \ \BNE GRL2 \ Loop back to GRL2 to draw the next set of coordinates \ \ until we have done them all \ \JSR LBFL \ Call LBFL to draw the line in the line buffer \ \.GREX2 \ \RTS \ Return from the subroutine \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: ZEVB \ Type: Subroutine \ Category: Utility routines \ Summary: Zero-fill the Y1VB variable \ \ ****************************************************************************** .ZEVB LDA #0 \ Set A = 0 so we can use it to zero-fill the variable TAY \ Set Y = 0 to use as a loop counter, starting at 0 and \ working from 255 down to 1 .SLL1 STA Y1VB,Y \ Zero the Y-th byte from Y1VB DEY \ Decrement the loop counter BNE SLL1 \ Jump back to zero the next byte until the whole page \ is done RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: GRIDSET \ Type: Subroutine \ Category: Demo \ Summary: Populate the line coordinate tables with the lines for the scroll \ text \ Deep dive: The 6502 Second Processor demo mode \ \ ------------------------------------------------------------------------------ \ \ This routine populates the X1TB, Y1TB, X2TB and Y2TB tables (the TB tables) \ with the line coordinates that make up each character in the scroll text that \ we want to display. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ (Y X) The contents of the scroll text to display \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.GRIDSET \ \STX GSL1+1 \ Modify the LDA instruction at GSL1 below to point to \STY GSL1+2 \ (Y X) instead of P%, i.e. to the Y-th character of the \ \ text we want to display \ \LDA #254 \ Set YP = 254 \STA YP \ \LDY #0 \ Set Y = 0, to act as an index into the text we want \ \ to display, pointing to the character we are currently \ \ processing \ \LDX #0 \ Set X = 0, to act as a pointer when populating the TB \ \ tables with one byte per character \ \STX XP \ Set XP = 0, so we now have (XP, YP) = (0, 254) \ \ \ \ (XP, YP) is the coordinate in space where we start \ \ drawing the lines that make up the scroll text, so \ \ this effectively moves the scroll text cursor to the \ \ top-left corner (as these are space coordinates where \ \ higher y-coordinates are further up the screen) \ \.GSL1 \ \LDA P%,Y \ This instruction was modified above to load the Y-th \ \ character from the text we want to display into A, so \ \ A now contains the ASCII code of the character we want \ \ to process \ \BEQ GRSEX \ If A = 0 then we have reached the end of the text to \ \ display, so jump down to GRSEX \ \STY T \ Store the character index in T so we can retrieve it \ \ later \ \SEC \ Set S = A - ASCII ",", as the table at LTDEF starts \SBC #',' \ with the lines needed for a comma, so A now contains \STA S \ the number of the entry in LTDEF for this character \ \ASL A \ Set Y = S + 4 * A \ASL A \ = A + 4 * A \ADC S \ = 5 * A \TAY \ \ \ so Y now points to the offset of the definition in the \ \ LTDEF table for the character in A, where the first \ \ character in the table is a comma and each definition \ \ in LTDEF consists of five bytes \ \LDA LTDEF,Y \ Call GRS1 to put the coordinates of the character's \JSR GRS1 \ first line into the TB tables \ \LDA LTDEF+1,Y \ Call GRS1 to put the coordinates of the character's \JSR GRS1 \ second line into the TB tables \ \LDA LTDEF+2,Y \ Call GRS1 to put the coordinates of the character's \JSR GRS1 \ third line into the TB tables \ \LDA LTDEF+3,Y \ Call GRS1 to put the coordinates of the character's \JSR GRS1 \ fourth line into the TB tables \ \LDA LTDEF+4,Y \ Call GRS1 to put the coordinates of the character's \JSR GRS1 \ fifth line into the TB tables \ \LDY T \ Retrieve the original character index from T into Y \ \INY \ Increment the character index to point to the next \ \ character in the text we want to display \ \LDA XP \ Set XP = XP + #W2 \CLC \ \ADC #W2 \ to move the x-coordinate along by #W2 (the horizontal \STA XP \ character spacing for the scroll text) \ \BCC GSL1 \ If the addition didn't overflow (i.e. XP < 256) loop \ \ back to GSL1 \ \LDA #0 \ Otherwise we just reached the end of a line in the \STA XP \ scroll text, so set XP = 0 to move the scroll text \ \ cursor to the start of the line \ \LDA YP \ And set YP = YP - #W2Y to move the scroll text cursor \SBC #W2Y \ down by one line (as #W2Y is the scroll text's \STA YP \ vertical line spacing) \ \JMP GSL1 \ Loop back to GSL1 to process the next character in the \ \ scroll text \ \.GRSEX \ \LDA #0 \ Set the X-th byte of Y1TB to 0 to indicate that we \STA Y1TB,X \ have reached the end of the scroll text \ \RTS \ Return from the subroutine \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: GRS1 \ Type: Subroutine \ Category: Demo \ Summary: Populate the line coordinate tables with the lines for a single \ scroll text character \ Deep dive: The 6502 Second Processor demo mode \ \ ------------------------------------------------------------------------------ \ \ This routine populates the X-th byte in the X1TB, Y1TB, X2TB and Y2TB tables \ (the TB tables) with the coordinates for the lines that make up the character \ whose definition is given in A. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ A The value from the LTDEF table for the character \ \ (XP, YP) The coordinate where we should draw this character \ \ X The index of the character within the scroll text \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ X X gets incremented to point to the next character \ \ Y Y is preserved \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.GRS1 \ \BEQ GRR1 \ If A = 0, jump to GRR1 to return from the subroutine \ \ as 0 denotes no line segment \ \STA R \ Store the value from the LTDEF table in R \ \AND #%00001111 \ Set A to bits 0-3 of the LTDEF table value, i.e. the \ \ low nibble \ \STY P \ Store the offset in P, so we can preserve it through \ \ calls to GRS1 \ \TAY \ Set Y = A \ \LDA NOFX,Y \ Set X1TB+X = XP + NOFX+Y \CLC \ \ADC XP \ so the X1 coordinate is XP + the NOFX entry given by \STA X1TB,X \ the low nibble of the LTDEF table value \ \LDA YP \ Set Y1TB+X = YP - NOFY+Y \SEC \ \SBC NOFY,Y \ so the Y1 coordinate is YP - the NOFY entry given by \STA Y1TB,X \ the low nibble of the LTDEF table value \ \LDA R \ Set Y to bits 4-7 of the LTDEF table value, i.e. the \LSR A \ high nibble \LSR A \LSR A \LSR A \TAY \ \LDA NOFX,Y \ Set X2TB+X = XP + NOFX+Y \CLC \ \ADC XP \ so the X2 coordinate is XP + the NOFX entry given by \STA X2TB,X \ the high nibble of the LTDEF table value \ \LDA YP \ Set Y2TB+X = YP - NOFY+Y \SEC \ \SBC NOFY,Y \ so the Y2 coordinate is YP - the NOFY entry given by \STA Y2TB,X \ the high nibble of the LTDEF table value \ \INX \ Increment the byte pointer in X \ \LDY P \ Restore Y from P so it gets preserved through calls to \ \ GRS1 \ \.GRR1 \ \RTS \ Return from the subroutine \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: ZZAAP \ Type: Subroutine \ Category: Demo \ Summary: Draw a vertical red laser line from (128, 67) to (128, 160) \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.ZZAAP \ \LDA #RED \ Send a #SETCOL RED command to the I/O processor to \JSR DOCOL \ switch to colour 2, which is red in the space view \ \LDA #128 \ Set X1 = 128 \STA X1 \ \STA X2 \ Set X2 = 128 \ \LDA #67 \ Set Y1 = 67 \STA Y1 \ \LDA #160 \ Set Y2 = 160 \STA Y2 \ \JMP LL30 \ Call LL30 to draw a line from (X1, Y1) to (X2, Y2), \ \ returning from the subroutine using a tail \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: LTDEF \ Type: Variable \ Category: Demo \ Summary: Line definitions for characters in the Star Wars scroll text \ Deep dive: The 6502 Second Processor demo mode \ \ ------------------------------------------------------------------------------ \ \ Characters in the scroll text are drawn using lines on a 3x6 numbered grid \ like this: \ \ 0 1 2 \ . . . \ 3 4 5 \ . . . \ 6 7 8 \ 9 A B \ \ The low nibble of each byte is the starting point for that line segment, and \ the high nibble is the end point, so a value of &28, for example, means \ "draw a line from point 8 to point 2". This table contains definitions for all \ the characters we can use in the scroll text, as lines on the above grid. \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.LTDEF \ \EQUB &63, &34, &47, &76, &97 \ Letter definition for "," \EQUB &35, &00, &00, &00, &00 \ Letter definition for "-" \EQUB &63, &34, &47, &76, &00 \ Letter definition for "." \EQUB &61, &00, &00, &00, &00 \ Letter definition for "/" \EQUB &73, &31, &15, &57, &00 \ Letter definition for "0" \EQUB &31, &17, &00, &00, &00 \ Letter definition for "1" \EQUB &02, &25, &53, &36, &68 \ Letter definition for "2" \EQUB &02, &28, &86, &35, &00 \ Letter definition for "3" \EQUB &82, &23, &35, &00, &00 \ Letter definition for "4" \EQUB &20, &03, &35, &58, &86 \ Letter definition for "5" \EQUB &20, &06, &68, &85, &53 \ Letter definition for "6" \EQUB &02, &28, &00, &00, &00 \ Letter definition for "7" \EQUB &60, &02, &28, &86, &35 \ Letter definition for "8" \EQUB &82, &20, &03, &35, &00 \ Letter definition for "9" \EQUB &00, &00, &00, &00, &00 \ Letter definition for ":" (blank) \EQUB &00, &00, &00, &00, &00 \ Letter definition for ";" (blank) \EQUB &00, &00, &00, &00, &00 \ Letter definition for "<" (blank) \EQUB &00, &00, &00, &00, &00 \ Letter definition for "=" (blank) \EQUB &00, &00, &00, &00, &00 \ Letter definition for ">" (blank) \EQUB &00, &00, &00, &00, &00 \ Letter definition for "?" (blank) \EQUB &00, &00, &00, &00, &00 \ Letter definition for "@" (blank) \EQUB &60, &02, &28, &35, &00 \ Letter definition for "A" \EQUB &60, &02, &28, &86, &35 \ Letter definition for "B" \EQUB &86, &60, &02, &00, &00 \ Letter definition for "C" \EQUB &60, &05, &56, &00, &00 \ Letter definition for "D" \EQUB &86, &60, &02, &35, &00 \ Letter definition for "E" \EQUB &60, &02, &35, &00, &00 \ Letter definition for "F" \EQUB &45, &58, &86, &60, &02 \ Letter definition for "G" \EQUB &60, &28, &35, &00, &00 \ Letter definition for "H" \EQUB &17, &00, &00, &00, &00 \ Letter definition for "I" \EQUB &28, &86, &63, &00, &00 \ Letter definition for "J" \EQUB &60, &23, &83, &00, &00 \ Letter definition for "K" \EQUB &86, &60, &00, &00, &00 \ Letter definition for "L" \EQUB &60, &04, &42, &28, &00 \ Letter definition for "M" \EQUB &60, &08, &82, &00, &00 \ Letter definition for "N" \EQUB &60, &02, &28, &86, &00 \ Letter definition for "O" \EQUB &60, &02, &25, &53, &00 \ Letter definition for "P" \EQUB &60, &02, &28, &86, &48 \ Letter definition for "Q" \EQUB &60, &02, &25, &53, &48 \ Letter definition for "R" \EQUB &20, &03, &35, &58, &86 \ Letter definition for "S" \EQUB &02, &17, &00, &00, &00 \ Letter definition for "T" \EQUB &28, &86, &60, &00, &00 \ Letter definition for "U" \EQUB &27, &70, &00, &00, &00 \ Letter definition for "V" \EQUB &28, &84, &46, &60, &00 \ Letter definition for "W" \EQUB &26, &08, &00, &00, &00 \ Letter definition for "X" \EQUB &74, &04, &24, &00, &00 \ Letter definition for "Y" \EQUB &02, &26, &68, &00, &00 \ Letter definition for "Z" \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: NOFX \ Type: Variable \ Category: Demo \ Summary: The x-coordinates of the scroll text letter grid \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.NOFX \ \EQUB 4 \ Grid points 0-2 \EQUB 8 \EQUB 12 \ \EQUB 4 \ Grid points 3-5 \EQUB 8 \EQUB 12 \ \EQUB 4 \ Grid points 6-8 \EQUB 8 \EQUB 12 \ \EQUB 4 \ Grid points 9-B \EQUB 8 \EQUB 12 \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: NOFY \ Type: Variable \ Category: Demo \ Summary: The y-coordinates of the scroll text letter grid \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.NOFY \ \EQUB 0 \ Grid points 0-2 \EQUB 0 \EQUB 0 \ \EQUB WY \ Grid points 3-5 \EQUB WY \EQUB WY \ \EQUB 2*WY \ Grid points 6-8 \EQUB 2*WY \EQUB 2*WY \ \EQUB 2.5*WY \ Grid points 9-B \EQUB 2.5*WY \EQUB 2.5*WY \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: acorn \ Type: Variable \ Category: Demo \ Summary: The text for the demo's opening scroll text \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.acorn \ \IF _SNG45 OR _SOURCE_DISC \ \EQUS ":::ACORNSOFT::::" \EQUS ";;;;;;;;;;;;;;;;" \EQUS "::::PRESENTS" \EQUB 0 \ \ELIF _EXECUTIVE \ \EQUS ":::PIZZASOFT::::" \EQUS ";;;;;;;;;;;;;;;;" \EQUS "::::PRESENTS" \EQUB 0 \ \ENDIF \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: byian \ Type: Variable \ Category: Demo \ Summary: The text for the demo's middle scroll text \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.byian \ \EQUS "::::::BY:;::::::" \EQUS ";;;;IAN;BELL;;;;" \EQUS "::::::AND:::::::" \EQUS ";;DAVID;BRABEN" \EQUB 0 \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: executive \ Type: Variable \ Category: Demo \ Summary: Extra text for the demo in the Executive version \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.executive \ \IF _EXECUTIVE \ \EQUS "::::::THE;::::::" \EQUS ";;;EXECUTIVE;;;;" \EQUS "::::VERSION" \EQUB 0 \ \ENDIF \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: true3 \ Type: Variable \ Category: Demo \ Summary: The text for the demo's final scroll text \ \ ****************************************************************************** \ --- Mod: Code removed for two-player Elite: ---------> \.true3 \ \IF _SNG45 OR _SOURCE_DISC \ \EQUS "THE:GALAXY:IS:IN" \EQUS "TURMOIL,THE:NAVY" \EQUS "FAR:AWAY:AS::THE" \EQUS "EMPIRE:CRUMBLES." \EQUB 0 \ \ELIF _EXECUTIVE \ \EQUS "CONGRATULATIONS:" \EQUS ";ON;OBTAINING;A;" \EQUS "::COPY:OF:THIS::" \EQUS "ELUSIVE;PRODUCT." \EQUB 0 \ \ENDIF \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: TALK \ Type: Subroutine \ Category: Sound \ Summary: Speak using the Watford Electronics Beeb Speech Synthesiser \ Deep dive: Secrets of the Executive version \ \ ------------------------------------------------------------------------------ \ \ In the Executive version, if you have a Watford Electronics Beeb Speech \ Synthesiser fitted and enable speech by pressing ":" while the game is paused, \ then the game will speak to you at various points. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The number of the phrase to speak: \ \ 1 = "Incoming missile" \ \ 2 = "Energy low" \ \ 3 = "Elite" \ \ 4 = "Oh shit, it's a mis-jump" \ \ ****************************************************************************** .TALK IF _EXECUTIVE TYA \ Store Y on the stack to we can retrieve it later PHA BIT SPEAK \ If SPEAK is 0, then speech is not enabled, so jump BPL TALK4 \ to TALK4 to restore Y from the stack and return \ from the subroutine LDA #0 \ Set SC = 0 STA SC LDY #LO(SPEECH) \ Set Y to point to the low byte of SPEECH LDA #HI(SPEECH) \ Set SC(1 0) so it points to the start of the page STA SC+1 \ containing SPEECH, so by this point, so SC+Y points \ to the first byte of SPEECH .TALKL LDA (SC),Y \ Fetch the next byte from the SPEECH block CMP #13 \ If it not 13 (which is the terminator for each speech BNE TALK1 \ command), jump to TALK1 to skip to the next byte DEX \ We just reached a terminator, so decrement the phrase \ number in X BEQ TALK2 \ If X is now 0, we have now found the terminator just \ before the phrase we want (the X-th phrase), so jump \ to TALK2 to speak it .TALK1 INY \ Increment the byte counter in Y to point to the next \ byte in the SPEECH table BNE TALKL \ If Y is non-zero, loop back to TALKL to process the \ next byte INC SC+1 \ Y is 0, which means SC+Y has just crossed a page \ boundary, so increment the high byte in SC+1 so that \ SC+Y points to the next page BNE TALKL \ Loop back to TALKL to process the next byte (this BNE \ is effectively a JMP as SC+1 is never zero) .TALK2 INY \ If we get here then SC+Y points to the terminator \ before the phrase we want, so increment Y to point to \ the start of the phrase we want BNE TALK3 \ If Y is non-zero, then SC+Y points to the phrase we \ want to speak, so jump to TALK3 to do the speaking INC SC+1 \ Y is 0, which means SC+Y has just crossed a page \ boundary, so increment the high byte in SC+1 so that \ SC+Y points to the next page .TALK3 TYA \ Set (Y X) to point to (SC+1 Y), i.e. SC+Y TAX LDY SC+1 JSR OSCLI \ Call OSCLI to run the OS command in SC+Y, which will \ be of the form "*TALK xx", a command for making the \ Watford Electronics Beeb Speech Synthesiser talk .TALK4 PLA \ Restore the value of Y we stored on the stack, so it TAY \ gets preserved across the call to the subroutine RTS \ Return from the subroutine ENDIF \ ****************************************************************************** \ \ Name: SPEECH \ Type: Variable \ Category: Sound \ Summary: Phrases for the Watford Electronics Beeb Speech Synthesiser \ Deep dive: Secrets of the Executive version \ \ ****************************************************************************** .SPEECH IF _EXECUTIVE EQUB 13 EQUS "TALK " \ 1: "Incoming missile" EQUS "A NN1 " EQUS "PA2 " EQUS "KK3 AA MM IH NG " EQUS "PA4 " EQUS "MM IH SS I LL" EQUB 13 EQUS "TALK " \ 2: "Energy low" EQUS "N ER1 G " EQUS "PA4 " EQUS "LOW" EQUB 13 EQUS "TALK " \ 3: "Elite" EQUS "EH LL EY TT1" EQUB 13 EQUS "TALK " \ 4: "Oh shit, it's a mis-jump" EQUS "O " EQUS "PA2 " EQUS "SH IH TT1 " EQUS "PA5 " EQUS "IH TT1 SS " EQUS "PA4 " EQUS "A " EQUS "PA4 " EQUS "MM IS " EQUS "PA2 " EQUS "JH UW1 MM PP" EQUB 13 ENDIF \ ****************************************************************************** \ \ Save ELTI.bin \ \ ****************************************************************************** PRINT "ELITE I" PRINT "Assembled at ", ~CODE_I% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_I%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_I% PRINT "S.ELTI ", ~CODE_I%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_H% SAVE "3-assembled-output/ELTI.bin", CODE_I%, P%, LOAD% \ ****************************************************************************** \ \ ELITE J FILE \ \ Produces the binary file ELTJ.bin that gets loaded by elite-bcfs.asm. \ \ ****************************************************************************** CODE_J% = P% LOAD_J% = LOAD% + P% - CODE% \ ****************************************************************************** \ \ Name: EJMP \ Type: Macro \ Category: Text \ Summary: Macro definition for jump tokens in the extended token table \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ The following macro is used when building the extended token table: \ \ EJMP n Insert a jump to address n in the JMTB table \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ n The jump number to insert into the table \ \ ****************************************************************************** MACRO EJMP n EQUB n EOR VE ENDMACRO \ ****************************************************************************** \ \ Name: ECHR \ Type: Macro \ Category: Text \ Summary: Macro definition for characters in the extended token table \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ The following macro is used when building the extended token table: \ \ ECHR 'x' Insert ASCII character "x" \ \ To include an apostrophe, use a backtick character, as in ECHR '`'. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ 'x' The character to insert into the table \ \ ****************************************************************************** MACRO ECHR x IF x = '`' EQUB 39 EOR VE ELSE EQUB x EOR VE ENDIF ENDMACRO \ ****************************************************************************** \ \ Name: ETOK \ Type: Macro \ Category: Text \ Summary: Macro definition for recursive tokens in the extended token table \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ The following macro is used when building the extended token table: \ \ ETOK n Insert extended recursive token [n] \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ n The number of the recursive token to insert into the \ table, in the range 129 to 214 \ \ ****************************************************************************** MACRO ETOK n EQUB n EOR VE ENDMACRO \ ****************************************************************************** \ \ Name: ETWO \ Type: Macro \ Category: Text \ Summary: Macro definition for two-letter tokens in the extended token table \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ The following macro is used when building the extended token table: \ \ ETWO 'x', 'y' Insert two-letter token "xy" \ \ The newline token can be entered using ETWO '-', '-'. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ 'x' The first letter of the two-letter token to insert into \ the table \ \ 'y' The second letter of the two-letter token to insert into \ the table \ \ ****************************************************************************** MACRO ETWO t, k IF t = '-' AND k = '-' EQUB 215 EOR VE ENDIF IF t = 'A' AND k = 'B' EQUB 216 EOR VE ENDIF IF t = 'O' AND k = 'U' EQUB 217 EOR VE ENDIF IF t = 'S' AND k = 'E' EQUB 218 EOR VE ENDIF IF t = 'I' AND k = 'T' EQUB 219 EOR VE ENDIF IF t = 'I' AND k = 'L' EQUB 220 EOR VE ENDIF IF t = 'E' AND k = 'T' EQUB 221 EOR VE ENDIF IF t = 'S' AND k = 'T' EQUB 222 EOR VE ENDIF IF t = 'O' AND k = 'N' EQUB 223 EOR VE ENDIF IF t = 'L' AND k = 'O' EQUB 224 EOR VE ENDIF IF t = 'N' AND k = 'U' EQUB 225 EOR VE ENDIF IF t = 'T' AND k = 'H' EQUB 226 EOR VE ENDIF IF t = 'N' AND k = 'O' EQUB 227 EOR VE ENDIF IF t = 'A' AND k = 'L' EQUB 228 EOR VE ENDIF IF t = 'L' AND k = 'E' EQUB 229 EOR VE ENDIF IF t = 'X' AND k = 'E' EQUB 230 EOR VE ENDIF IF t = 'G' AND k = 'E' EQUB 231 EOR VE ENDIF IF t = 'Z' AND k = 'A' EQUB 232 EOR VE ENDIF IF t = 'C' AND k = 'E' EQUB 233 EOR VE ENDIF IF t = 'B' AND k = 'I' EQUB 234 EOR VE ENDIF IF t = 'S' AND k = 'O' EQUB 235 EOR VE ENDIF IF t = 'U' AND k = 'S' EQUB 236 EOR VE ENDIF IF t = 'E' AND k = 'S' EQUB 237 EOR VE ENDIF IF t = 'A' AND k = 'R' EQUB 238 EOR VE ENDIF IF t = 'M' AND k = 'A' EQUB 239 EOR VE ENDIF IF t = 'I' AND k = 'N' EQUB 240 EOR VE ENDIF IF t = 'D' AND k = 'I' EQUB 241 EOR VE ENDIF IF t = 'R' AND k = 'E' EQUB 242 EOR VE ENDIF IF t = 'A' AND k = '?' EQUB 243 EOR VE ENDIF IF t = 'E' AND k = 'R' EQUB 244 EOR VE ENDIF IF t = 'A' AND k = 'T' EQUB 245 EOR VE ENDIF IF t = 'E' AND k = 'N' EQUB 246 EOR VE ENDIF IF t = 'B' AND k = 'E' EQUB 247 EOR VE ENDIF IF t = 'R' AND k = 'A' EQUB 248 EOR VE ENDIF IF t = 'L' AND k = 'A' EQUB 249 EOR VE ENDIF IF t = 'V' AND k = 'E' EQUB 250 EOR VE ENDIF IF t = 'T' AND k = 'I' EQUB 251 EOR VE ENDIF IF t = 'E' AND k = 'D' EQUB 252 EOR VE ENDIF IF t = 'O' AND k = 'R' EQUB 253 EOR VE ENDIF IF t = 'Q' AND k = 'U' EQUB 254 EOR VE ENDIF IF t = 'A' AND k = 'N' EQUB 255 EOR VE ENDIF ENDMACRO \ ****************************************************************************** \ \ Name: ERND \ Type: Macro \ Category: Text \ Summary: Macro definition for random tokens in the extended token table \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ The following macro is used when building the extended token table: \ \ ERND n Insert recursive token [n] \ \ * Tokens 0-123 get stored as n + 91 \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ n The number of the random token to insert into the \ table, in the range 0 to 37 \ \ ****************************************************************************** MACRO ERND n EQUB (n + 91) EOR VE ENDMACRO \ ****************************************************************************** \ \ Name: TOKN \ Type: Macro \ Category: Text \ Summary: Macro definition for standard tokens in the extended token table \ Deep dive: Printing text tokens \ \ ------------------------------------------------------------------------------ \ \ The following macro is used when building the recursive token table: \ \ TOKN n Insert recursive token [n] \ \ * Tokens 0-95 get stored as n + 160 \ \ * Tokens 128-145 get stored as n - 114 \ \ * Tokens 96-127 get stored as n \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ n The number of the recursive token to insert into the \ table, in the range 0 to 145 \ \ ****************************************************************************** MACRO TOKN n IF n >= 0 AND n <= 95 t = n + 160 ELIF n >= 128 t = n - 114 ELSE t = n ENDIF EQUB t EOR VE ENDMACRO \ ****************************************************************************** \ \ Name: TKN1 \ Type: Variable \ Category: Text \ Summary: The first extended token table for recursive tokens 0-255 (DETOK) \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ The encodings shown for each extended text token use the following notation: \ \ {n} Jump token n = 1 to 31 \ [n?] Random token n = 91 to 128 \ [n] Recursive token n = 129 to 215 \ Two-letter token n = 215 to 255 \ \ ****************************************************************************** .TKN1 EQUB VE \ Token 0: "" \ \ Encoded as: "" \ --- Mod: Code removed for two-player Elite: ---------> \EJMP 9 \ Token 1: "{clear screen} \EJMP 11 \ {draw box around title} \EJMP 30 \ {white} \EJMP 1 \ {all caps} \EJMP 8 \ {tab 6} DISK ACCESS MENU{crlf} \ECHR ' ' \ {lf} \ETWO 'D', 'I' \ {sentence case} \ECHR 'S' \ 1. LOAD NEW {single cap}COMMANDER{crlf} \ECHR 'K' \ 2. SAVE {single cap}COMMANDER \ECHR ' ' \ {commander name}{crlf} \ECHR 'A' \ 3. CATALOGUE{crlf} \ECHR 'C' \ 4. DELETE A FILE{crlf} \ETWO 'C', 'E' \ 5. EXIT{crlf} \ECHR 'S' \ " \ECHR 'S' \ \ECHR ' ' \ Encoded as: "{9}{11}{30}{1}{8} <241>SK AC<233>SS ME \ECHR 'M' \ <225><215>{10}{2}1. [149]<215>2. SA \ECHR 'E' \ <250> [154] {4}<215>3. C<245>A<224>GUE \ETWO 'N', 'U' \ <215>4. DEL<221>E[208]FI<229><215>5. EX \ETWO '-', '-' \ <219><215>" \EJMP 10 \EJMP 2 \ECHR '1' \ECHR '.' \ECHR ' ' \ETOK 149 \ETWO '-', '-' \ECHR '2' \ECHR '.' \ECHR ' ' \ECHR 'S' \ECHR 'A' \ETWO 'V', 'E' \ECHR ' ' \ETOK 154 \ECHR ' ' \EJMP 4 \ETWO '-', '-' \ECHR '3' \ECHR '.' \ECHR ' ' \ECHR 'C' \ETWO 'A', 'T' \ECHR 'A' \ETWO 'L', 'O' \ECHR 'G' \ECHR 'U' \ECHR 'E' \ETWO '-', '-' \ECHR '4' \ECHR '.' \ECHR ' ' \ECHR 'D' \ECHR 'E' \ECHR 'L' \ETWO 'E', 'T' \ECHR 'E' \ETOK 208 \ECHR 'F' \ECHR 'I' \ETWO 'L', 'E' \ETWO '-', '-' \ECHR '5' \ECHR '.' \ECHR ' ' \ECHR 'E' \ECHR 'X' \ETWO 'I', 'T' \ETWO '-', '-' \EQUB VE \ \EJMP 12 \ Token 2: "{cr} \ECHR 'W' \ WHICH DRIVE?" \ECHR 'H' \ \ECHR 'I' \ Encoded as: "{12}WHICH [151]?" \ECHR 'C' \ECHR 'H' \ECHR ' ' \ETOK 151 \ECHR '?' \EQUB VE \ \ECHR 'C' \ Token 3: "COMPETITION NUMBER:" \ECHR 'O' \ \ECHR 'M' \ Encoded as: "COMPE<251><251><223> <225>MB<244>:" \ECHR 'P' \ECHR 'E' \ETWO 'T', 'I' \ETWO 'T', 'I' \ETWO 'O', 'N' \ECHR ' ' \ETWO 'N', 'U' \ECHR 'M' \ECHR 'B' \ETWO 'E', 'R' \ECHR ':' \EQUB VE \ \ETOK 150 \ Token 4: "{clear screen} \ETOK 151 \ {draw box around title} \ECHR ' ' \ {all caps} \EJMP 16 \ {tab 6}DRIVE {drive number} CATALOGUE \ETOK 152 \ {crlf} \ETWO '-', '-' \ " \EQUB VE \ \ \ Encoded as: "[150][151] {16}[152]<215>" \ \ETOK 176 \ Token 5: "{lower case} \ERND 18 \ {justify} \ETOK 202 \ {single cap}[86-90] IS [140-144].{cr} \ERND 19 \ {left align}" \ETOK 177 \ \EQUB VE \ Encoded as: "[176][18?][202][19?][177]" \ \ECHR ' ' \ Token 6: " LOAD NEW {single cap}COMMANDER {all \ECHR ' ' \ caps}(Y/N)?{sentence case}{cr}{cr}" \ETOK 149 \ \ECHR ' ' \ Encoded as: " [149] {1}(Y/N)?{2}{12}{12}" \EJMP 1 \ECHR '(' \ECHR 'Y' \ECHR '/' \ECHR 'N' \ECHR ')' \ECHR '?' \EJMP 2 \EJMP 12 \EJMP 12 \EQUB VE \ \ECHR 'P' \ Token 7: "PRESS SPACE OR FIRE,{single cap} \ETWO 'R', 'E' \ COMMANDER.{cr}{cr}" \ECHR 'S' \ \ECHR 'S' \ Encoded as: "P<242>SS SPA<233> <253> FI<242>,[154]. \ECHR ' ' \ {12}{12}" \ECHR 'S' \ECHR 'P' \ECHR 'A' \ETWO 'C', 'E' \ECHR ' ' \ETWO 'O', 'R' \ECHR ' ' \ECHR 'F' \ECHR 'I' \ETWO 'R', 'E' \ECHR ',' \ETOK 154 \ECHR '.' \EJMP 12 \EJMP 12 \EQUB VE \ \ETOK 154 \ Token 8: "{single cap}COMMANDER'S NAME? " \ECHR '`' \ \ECHR 'S' \ Encoded as: "[154]'S[200]" \ETOK 200 \EQUB VE \ \EJMP 21 \ Token 9: "{clear bottom of screen} \ECHR 'F' \ FILE TO DELETE?" \ECHR 'I' \ \ETWO 'L', 'E' \ Encoded as: "{21}FI<229>[201]DEL<221>E?" \ETOK 201 \ECHR 'D' \ECHR 'E' \ECHR 'L' \ETWO 'E', 'T' \ECHR 'E' \ECHR '?' \EQUB VE \ \EJMP 23 \ Token 10: "{move to row 10, white, lower case} \EJMP 14 \ {justify} \EJMP 2 \ {sentence case} \ECHR 'G' \ GREETINGS {single cap}COMMANDER \ETWO 'R', 'E' \ {commander name}, I {lower case}AM \ETWO 'E', 'T' \ {sentence case} CAPTAIN {mission \ETWO 'I', 'N' \ captain's name} {lower case}OF{sentence \ECHR 'G' \ case} HER MAJESTY'S SPACE NAVY{lower \ECHR 'S' \ case} AND {single cap}I BEG A MOMENT OF \ETOK 213 \ YOUR VALUABLE TIME.{cr} \ETOK 178 \ {single cap}WE WOULD LIKE YOU TO DO A \EJMP 19 \ LITTLE JOB FOR US.{cr} \ECHR 'I' \ {single cap}THE SHIP YOU SEE HERE IS A \ECHR ' ' \ NEW MODEL, THE {single cap}CONSTRICTOR, \ETWO 'B', 'E' \ EQUIPED WITH A TOP SECRET NEW SHIELD \ECHR 'G' \ GENERATOR.{cr} \ETOK 208 \ {single cap}UNFORTUNATELY IT'S BEEN \ECHR 'M' \ STOLEN.{cr} \ECHR 'O' \ {single cap}{display ship, wait for \ECHR 'M' \ key press}IT WENT MISSING FROM OUR SHIP \ETWO 'E', 'N' \ YARD ON {single cap}XEER FIVE MONTHS \ECHR 'T' \ AGO AND {mission 1 location hint}.{cr} \ECHR ' ' \ {single cap}YOUR MISSION, SHOULD YOU \ECHR 'O' \ DECIDE TO ACCEPT IT, IS TO SEEK AND \ECHR 'F' \ DESTROY THIS SHIP.{cr} \ECHR ' ' \ {single cap}YOU ARE CAUTIONED THAT \ETOK 179 \ ONLY {standard tokens, sentence case} \ECHR 'R' \ MILITARY LASERS{extended tokens} WILL \ECHR ' ' \ PENETRATE THE NEW SHIELDS AND THAT THE \ECHR 'V' \ {single cap}CONSTRICTOR IS FITTED WITH \ETWO 'A', 'L' \ AN {standard tokens, sentence case} \ECHR 'U' \ E.C.M.SYSTEM{extended tokens}.{cr} \ETWO 'A', 'B' \ {left align}{sentence case}{tab 6}GOOD \ETWO 'L', 'E' \ LUCK, {single cap}COMMANDER.{cr} \ECHR ' ' \ {left align}{tab 6}{all caps} MESSAGE \ETWO 'T', 'I' \ ENDS{display ship, wait for key press}" \ECHR 'M' \ \ECHR 'E' \ Encoded as: "{23}{14}{2}G<242><221><240>GS[213][178] \ETOK 204 \ {19}I <247>G[208]MOM<246>T OF [179]R V \ECHR 'W' \ <228>U<216><229> <251>ME[204]WE W<217> \ECHR 'E' \ LD LIKE [179][201]DO[208]L<219>T<229> \ECHR ' ' \ JOB F<253> <236>[204][147][207] [179] \ECHR 'W' \ <218>E HE<242>[202]A[210]MODEL, [147] \ETWO 'O', 'U' \ {19}C<223><222>RICT<253>, E<254>IP[196] \ECHR 'L' \ WI<226>[208]TOP <218>CR<221>[210]SHIELD \ECHR 'D' \ G<246><244><245><253>[204]UNF<253>TUN \ECHR ' ' \ <245>ELY <219>'S <247><246> <222>OL \ECHR 'L' \ <246>[204]{22}<219> W<246>T MISS[195] \ECHR 'I' \ FROM <217>R [207] Y<238>D <223> {19} \ECHR 'K' \ <230><244> FI<250> M<223><226>S AGO \ECHR 'E' \ [178]{28}[204][179]R MISSI<223>, SH \ECHR ' ' \ <217>LD [179] DECIDE[201]AC<233>PT \ETOK 179 \ <219>, IS[201]<218>EK[178]D<237>TROY \ETOK 201 \ [148][207][204][179] A<242> CAU<251> \ECHR 'D' \ <223>[196]<226><245> <223>LY {6}[116] \ECHR 'O' \ {5}S W<220>L P<246><221><248>TE [147] \ETOK 208 \ NEW SHIELDS[178]<226><245> [147]{19} \ECHR 'L' \ C<223><222>RICT<253>[202]F<219>T[196]WI \ETWO 'I', 'T' \ <226> <255> {6}[108]{5}[177]{2}{8}GOOD \ECHR 'T' \ LUCK, [154][212]{22}" \ETWO 'L', 'E' \ECHR ' ' \ECHR 'J' \ECHR 'O' \ECHR 'B' \ECHR ' ' \ECHR 'F' \ETWO 'O', 'R' \ECHR ' ' \ETWO 'U', 'S' \ETOK 204 \ETOK 147 \ETOK 207 \ECHR ' ' \ETOK 179 \ECHR ' ' \ETWO 'S', 'E' \ECHR 'E' \ECHR ' ' \ECHR 'H' \ECHR 'E' \ETWO 'R', 'E' \ETOK 202 \ECHR 'A' \ETOK 210 \ECHR 'M' \ECHR 'O' \ECHR 'D' \ECHR 'E' \ECHR 'L' \ECHR ',' \ECHR ' ' \ETOK 147 \EJMP 19 \ECHR 'C' \ETWO 'O', 'N' \ETWO 'S', 'T' \ECHR 'R' \ECHR 'I' \ECHR 'C' \ECHR 'T' \ETWO 'O', 'R' \ECHR ',' \ECHR ' ' \ECHR 'E' \ETWO 'Q', 'U' \ECHR 'I' \ECHR 'P' \ETOK 196 \ECHR 'W' \ECHR 'I' \ETWO 'T', 'H' \ETOK 208 \ECHR 'T' \ECHR 'O' \ECHR 'P' \ECHR ' ' \ETWO 'S', 'E' \ECHR 'C' \ECHR 'R' \ETWO 'E', 'T' \ETOK 210 \ECHR 'S' \ECHR 'H' \ECHR 'I' \ECHR 'E' \ECHR 'L' \ECHR 'D' \ECHR ' ' \ECHR 'G' \ETWO 'E', 'N' \ETWO 'E', 'R' \ETWO 'A', 'T' \ETWO 'O', 'R' \ETOK 204 \ECHR 'U' \ECHR 'N' \ECHR 'F' \ETWO 'O', 'R' \ECHR 'T' \ECHR 'U' \ECHR 'N' \ETWO 'A', 'T' \ECHR 'E' \ECHR 'L' \ECHR 'Y' \ECHR ' ' \ETWO 'I', 'T' \ECHR '`' \ECHR 'S' \ECHR ' ' \ETWO 'B', 'E' \ETWO 'E', 'N' \ECHR ' ' \ETWO 'S', 'T' \ECHR 'O' \ECHR 'L' \ETWO 'E', 'N' \ETOK 204 \EJMP 22 \ETWO 'I', 'T' \ECHR ' ' \ECHR 'W' \ETWO 'E', 'N' \ECHR 'T' \ECHR ' ' \ECHR 'M' \ECHR 'I' \ECHR 'S' \ECHR 'S' \ETOK 195 \ECHR 'F' \ECHR 'R' \ECHR 'O' \ECHR 'M' \ECHR ' ' \ETWO 'O', 'U' \ECHR 'R' \ECHR ' ' \ETOK 207 \ECHR ' ' \ECHR 'Y' \ETWO 'A', 'R' \ECHR 'D' \ECHR ' ' \ETWO 'O', 'N' \ECHR ' ' \EJMP 19 \ETWO 'X', 'E' \ETWO 'E', 'R' \ECHR ' ' \ECHR 'F' \ECHR 'I' \ETWO 'V', 'E' \ECHR ' ' \ECHR 'M' \ETWO 'O', 'N' \ETWO 'T', 'H' \ECHR 'S' \ECHR ' ' \ECHR 'A' \ECHR 'G' \ECHR 'O' \ETOK 178 \EJMP 28 \ETOK 204 \ETOK 179 \ECHR 'R' \ECHR ' ' \ECHR 'M' \ECHR 'I' \ECHR 'S' \ECHR 'S' \ECHR 'I' \ETWO 'O', 'N' \ECHR ',' \ECHR ' ' \ECHR 'S' \ECHR 'H' \ETWO 'O', 'U' \ECHR 'L' \ECHR 'D' \ECHR ' ' \ETOK 179 \ECHR ' ' \ECHR 'D' \ECHR 'E' \ECHR 'C' \ECHR 'I' \ECHR 'D' \ECHR 'E' \ETOK 201 \ECHR 'A' \ECHR 'C' \ETWO 'C', 'E' \ECHR 'P' \ECHR 'T' \ECHR ' ' \ETWO 'I', 'T' \ECHR ',' \ECHR ' ' \ECHR 'I' \ECHR 'S' \ETOK 201 \ETWO 'S', 'E' \ECHR 'E' \ECHR 'K' \ETOK 178 \ECHR 'D' \ETWO 'E', 'S' \ECHR 'T' \ECHR 'R' \ECHR 'O' \ECHR 'Y' \ECHR ' ' \ETOK 148 \ETOK 207 \ETOK 204 \ETOK 179 \ECHR ' ' \ECHR 'A' \ETWO 'R', 'E' \ECHR ' ' \ECHR 'C' \ECHR 'A' \ECHR 'U' \ETWO 'T', 'I' \ETWO 'O', 'N' \ETOK 196 \ETWO 'T', 'H' \ETWO 'A', 'T' \ECHR ' ' \ETWO 'O', 'N' \ECHR 'L' \ECHR 'Y' \ECHR ' ' \EJMP 6 \TOKN 117 \EJMP 5 \ECHR 'S' \ECHR ' ' \ECHR 'W' \ETWO 'I', 'L' \ECHR 'L' \ECHR ' ' \ECHR 'P' \ETWO 'E', 'N' \ETWO 'E', 'T' \ETWO 'R', 'A' \ECHR 'T' \ECHR 'E' \ECHR ' ' \ETOK 147 \ECHR 'N' \ECHR 'E' \ECHR 'W' \ECHR ' ' \ECHR 'S' \ECHR 'H' \ECHR 'I' \ECHR 'E' \ECHR 'L' \ECHR 'D' \ECHR 'S' \ETOK 178 \ETWO 'T', 'H' \ETWO 'A', 'T' \ECHR ' ' \ETOK 147 \EJMP 19 \ECHR 'C' \ETWO 'O', 'N' \ETWO 'S', 'T' \ECHR 'R' \ECHR 'I' \ECHR 'C' \ECHR 'T' \ETWO 'O', 'R' \ETOK 202 \ECHR 'F' \ETWO 'I', 'T' \ECHR 'T' \ETOK 196 \ECHR 'W' \ECHR 'I' \ETWO 'T', 'H' \ECHR ' ' \ETWO 'A', 'N' \ECHR ' ' \EJMP 6 \TOKN 108 \EJMP 5 \ETOK 177 \EJMP 2 \EJMP 8 \ECHR 'G' \ECHR 'O' \ECHR 'O' \ECHR 'D' \ECHR ' ' \ECHR 'L' \ECHR 'U' \ECHR 'C' \ECHR 'K' \ECHR ',' \ECHR ' ' \ETOK 154 \ETOK 212 \EJMP 22 \EQUB VE \ \EJMP 25 \ Token 11: "{incoming message screen, wait 2s} \EJMP 9 \ {clear screen} \EJMP 30 \ {white} \EJMP 23 \ {move to row 10, white, lower case} \EJMP 14 \ {justify} \EJMP 2 \ {sentence case} \ECHR ' ' \ ATTENTION {single cap}COMMANDER \ECHR ' ' \ {commander name}, I {lower case}AM \ETWO 'A', 'T' \ {sentence case} CAPTAIN {mission \ECHR 'T' \ captain's name} {lower case}OF{sentence \ETWO 'E', 'N' \ case} HER MAJESTY'S SPACE NAVY{lower \ETWO 'T', 'I' \ case}. {single cap}WE HAVE NEED OF YOUR \ETWO 'O', 'N' \ SERVICES AGAIN.{cr} \ETOK 213 \ {single cap}IF YOU WOULD BE SO GOOD AS \ECHR '.' \ TO GO TO {single cap}CEERDI YOU WILL BE \ECHR ' ' \ BRIEFED.{cr} \EJMP 19 \ {single cap}IF SUCCESSFUL, YOU WILL BE \ECHR 'W' \ WELL REWARDED.{cr} \ECHR 'E' \ {left align}{tab 6}{all caps} MESSAGE \ECHR ' ' \ ENDS{wait for key press}" \ECHR 'H' \ \ECHR 'A' \ Encoded as: "{25}{9}{30}{23}{14}{2} <245>T<246> \ETWO 'V', 'E' \ <251><223>[213]. {19}WE HA<250> NE[196] \ECHR ' ' \ OF [179]R <218>RVIC<237> AGA<240>[204] \ECHR 'N' \ IF [179] W<217>LD <247> <235> GOOD AS \ECHR 'E' \ [201]GO[201]{19}<233><244><241> [179] W \ETOK 196 \ <220>L <247> BRIEF<252>[204]IF SUC<233> \ECHR 'O' \ SSFUL, [179] W<220>L <247> WELL <242>W \ECHR 'F' \ <238>D<252>[212]{24}" \ECHR ' ' \ETOK 179 \ECHR 'R' \ECHR ' ' \ETWO 'S', 'E' \ECHR 'R' \ECHR 'V' \ECHR 'I' \ECHR 'C' \ETWO 'E', 'S' \ECHR ' ' \ECHR 'A' \ECHR 'G' \ECHR 'A' \ETWO 'I', 'N' \ETOK 204 \ECHR 'I' \ECHR 'F' \ECHR ' ' \ETOK 179 \ECHR ' ' \ECHR 'W' \ETWO 'O', 'U' \ECHR 'L' \ECHR 'D' \ECHR ' ' \ETWO 'B', 'E' \ECHR ' ' \ETWO 'S', 'O' \ECHR ' ' \ECHR 'G' \ECHR 'O' \ECHR 'O' \ECHR 'D' \ECHR ' ' \ECHR 'A' \ECHR 'S' \ETOK 201 \ECHR 'G' \ECHR 'O' \ETOK 201 \EJMP 19 \ETWO 'C', 'E' \ETWO 'E', 'R' \ETWO 'D', 'I' \ECHR ' ' \ETOK 179 \ECHR ' ' \ECHR 'W' \ETWO 'I', 'L' \ECHR 'L' \ECHR ' ' \ETWO 'B', 'E' \ECHR ' ' \ECHR 'B' \ECHR 'R' \ECHR 'I' \ECHR 'E' \ECHR 'F' \ETWO 'E', 'D' \ETOK 204 \ECHR 'I' \ECHR 'F' \ECHR ' ' \ECHR 'S' \ECHR 'U' \ECHR 'C' \ETWO 'C', 'E' \ECHR 'S' \ECHR 'S' \ECHR 'F' \ECHR 'U' \ECHR 'L' \ECHR ',' \ECHR ' ' \ETOK 179 \ECHR ' ' \ECHR 'W' \ETWO 'I', 'L' \ECHR 'L' \ECHR ' ' \ETWO 'B', 'E' \ECHR ' ' \ECHR 'W' \ECHR 'E' \ECHR 'L' \ECHR 'L' \ECHR ' ' \ETWO 'R', 'E' \ECHR 'W' \ETWO 'A', 'R' \ECHR 'D' \ETWO 'E', 'D' \ETOK 212 \EJMP 24 \EQUB VE \ --- And replaced by: --------------------------------> EQUB VE \ Token 1 EQUB VE \ Token 2 EQUB VE \ Token 3 EQUB VE \ Token 4 EQUB VE \ Token 5 EQUB VE \ Token 6 EQUB VE \ Token 7 EQUB VE \ Token 8 EQUB VE \ Token 9 EQUB VE \ Token 10 EQUB VE \ Token 11 \ --- End of replacement ------------------------------> ECHR '(' \ Token 12: "({single cap}C) ACORNSOFT 1984" EJMP 19 \ ECHR 'C' \ Encoded as: "({19}C) AC<253>N<235>FT 1984" ECHR ')' ECHR ' ' ECHR 'A' ECHR 'C' ETWO 'O', 'R' ECHR 'N' ETWO 'S', 'O' ECHR 'F' ECHR 'T' ECHR ' ' ECHR '1' ECHR '9' ECHR '8' ECHR '4' EQUB VE ECHR 'B' \ Token 13: "BY D.BRABEN & I.BELL" ECHR 'Y' \ ECHR ' ' \ Encoded as: "BY D.B<248><247>N & I.<247>LL" ECHR 'D' ECHR '.' ECHR 'B' ETWO 'R', 'A' ETWO 'B', 'E' ECHR 'N' ECHR ' ' ECHR '&' ECHR ' ' ECHR 'I' ECHR '.' ETWO 'B', 'E' ECHR 'L' ECHR 'L' EQUB VE EJMP 21 \ Token 14: "{clear bottom of screen} ETOK 145 \ PLANET NAME? ETOK 200 \ {fetch line input from keyboard}" EJMP 26 \ EQUB VE \ Encoded as: "{21}[145][200]{26}" \ --- Mod: Code removed for two-player Elite: ---------> \EJMP 25 \ Token 15: "{incoming message screen, wait 2s} \EJMP 9 \ {clear screen} \EJMP 30 \ {white} \EJMP 23 \ {move to row 10, white, lower case} \EJMP 14 \ {justify} \EJMP 2 \ {sentence case} \ECHR ' ' \ CONGRATULATIONS {single cap} \ECHR ' ' \ COMMANDER!{cr} \ECHR 'C' \ {cr} \ETWO 'O', 'N' \ THERE{lower case} WILL ALWAYS BE A \ECHR 'G' \ PLACE FOR YOU IN{sentence case} HER \ETWO 'R', 'A' \ MAJESTY'S SPACE NAVY{lower case}.{cr} \ECHR 'T' \ {single cap}AND MAYBE SOONER THAN YOU \ECHR 'U' \ THINK...{cr} \ETWO 'L', 'A' \ {left align}{tab 6}{all caps} MESSAGE \ETWO 'T', 'I' \ ENDS{wait for key press}" \ETWO 'O', 'N' \ \ECHR 'S' \ Encoded as: "{25}{9}{30}{23}{14}{2} C<223>G<248>TU \ECHR ' ' \ <249><251><223>S [154]!{12}{12}<226> \ETOK 154 \ <244>E{13} W<220>L <228>WAYS <247>[208] \ECHR '!' \ P<249><233> F<253> [179] <240>[211] \EJMP 12 \ [204]<255>D <239>Y<247> <235><223><244> \EJMP 12 \ <226><255> [179] <226><240>K..[212] \ETWO 'T', 'H' \ {24}" \ETWO 'E', 'R' \ECHR 'E' \EJMP 13 \ECHR ' ' \ECHR 'W' \ETWO 'I', 'L' \ECHR 'L' \ECHR ' ' \ETWO 'A', 'L' \ECHR 'W' \ECHR 'A' \ECHR 'Y' \ECHR 'S' \ECHR ' ' \ETWO 'B', 'E' \ETOK 208 \ECHR 'P' \ETWO 'L', 'A' \ETWO 'C', 'E' \ECHR ' ' \ECHR 'F' \ETWO 'O', 'R' \ECHR ' ' \ETOK 179 \ECHR ' ' \ETWO 'I', 'N' \ETOK 211 \ETOK 204 \ETWO 'A', 'N' \ECHR 'D' \ECHR ' ' \ETWO 'M', 'A' \ECHR 'Y' \ETWO 'B', 'E' \ECHR ' ' \ETWO 'S', 'O' \ETWO 'O', 'N' \ETWO 'E', 'R' \ECHR ' ' \ETWO 'T', 'H' \ETWO 'A', 'N' \ECHR ' ' \ETOK 179 \ECHR ' ' \ETWO 'T', 'H' \ETWO 'I', 'N' \ECHR 'K' \ECHR '.' \ECHR '.' \ETOK 212 \EJMP 24 \EQUB VE \ --- And replaced by: --------------------------------> EQUB VE \ --- End of replacement ------------------------------> ECHR 'F' \ Token 16: "FABLED" ETWO 'A', 'B' \ ETWO 'L', 'E' \ Encoded as: "F<216><229>D" ECHR 'D' EQUB VE ETWO 'N', 'O' \ Token 17: "NOTABLE" ECHR 'T' \ ETWO 'A', 'B' \ Encoded as: "<227>T<216><229>" ETWO 'L', 'E' EQUB VE ECHR 'W' \ Token 18: "WELL KNOWN" ECHR 'E' \ ECHR 'L' \ Encoded as: "WELL K<227>WN" ECHR 'L' ECHR ' ' ECHR 'K' ETWO 'N', 'O' ECHR 'W' ECHR 'N' EQUB VE ECHR 'F' \ Token 19: "FAMOUS" ECHR 'A' \ ECHR 'M' \ Encoded as: "FAMO<236>" ECHR 'O' ETWO 'U', 'S' EQUB VE ETWO 'N', 'O' \ Token 20: "NOTED" ECHR 'T' \ ETWO 'E', 'D' \ Encoded as: "<227>T<252>" EQUB VE ETWO 'V', 'E' \ Token 21: "VERY" ECHR 'R' \ ECHR 'Y' \ Encoded as: "<250>RY" EQUB VE ECHR 'M' \ Token 22: "MILDLY" ETWO 'I', 'L' \ ECHR 'D' \ Encoded as: "M<220>DLY" ECHR 'L' ECHR 'Y' EQUB VE ECHR 'M' \ Token 23: "MOST" ECHR 'O' \ ETWO 'S', 'T' \ Encoded as: "MO<222>" EQUB VE ETWO 'R', 'E' \ Token 24: "REASONABLY" ECHR 'A' \ ECHR 'S' \ Encoded as: "<242>AS<223><216>LY" ETWO 'O', 'N' ETWO 'A', 'B' ECHR 'L' ECHR 'Y' EQUB VE EQUB VE \ Token 25: "" \ \ Encoded as: "" ETOK 165 \ Token 26: "ANCIENT" EQUB VE \ \ Encoded as: "[165]" ERND 23 \ Token 27: "[130-134]" EQUB VE \ \ Encoded as: "[23?]" ECHR 'G' \ Token 28: "GREAT" ETWO 'R', 'E' \ ETWO 'A', 'T' \ Encoded as: "G<242><245>" EQUB VE ECHR 'V' \ Token 29: "VAST" ECHR 'A' \ ETWO 'S', 'T' \ Encoded as: "VA<222>" EQUB VE ECHR 'P' \ Token 30: "PINK" ETWO 'I', 'N' \ ECHR 'K' \ Encoded as: "P<240>K" EQUB VE EJMP 2 \ Token 31: "{sentence case}[190-194] [185-189] ERND 28 \ {lower case} PLANTATIONS" ECHR ' ' \ ERND 27 \ Encoded as: "{2}[28?] [27?]{13} [185]A<251><223>S" EJMP 13 ECHR ' ' ETOK 185 ECHR 'A' ETWO 'T', 'I' ETWO 'O', 'N' ECHR 'S' EQUB VE ETOK 156 \ Token 32: "MOUNTAINS" ECHR 'S' \ EQUB VE \ Encoded as: "[156]S" ERND 26 \ Token 33: "[180-184]" EQUB VE \ \ Encoded as: "[26?]" ERND 37 \ Token 34: "[125-129] FORESTS" ECHR ' ' \ ECHR 'F' \ Encoded as: "[37?] F<253><237>TS" ETWO 'O', 'R' ETWO 'E', 'S' ECHR 'T' ECHR 'S' EQUB VE ECHR 'O' \ Token 35: "OCEANS" ETWO 'C', 'E' \ ETWO 'A', 'N' \ Encoded as: "O<233><255>S" ECHR 'S' EQUB VE ECHR 'S' \ Token 36: "SHYNESS" ECHR 'H' \ ECHR 'Y' \ Encoded as: "SHYN<237>S" ECHR 'N' ETWO 'E', 'S' ECHR 'S' EQUB VE ECHR 'S' \ Token 37: "SILLINESS" ETWO 'I', 'L' \ ECHR 'L' \ Encoded as: "S<220>L<240><237>S" ETWO 'I', 'N' ETWO 'E', 'S' ECHR 'S' EQUB VE ETWO 'M', 'A' \ Token 38: "MATING TRADITIONS" ECHR 'T' \ ETOK 195 \ Encoded as: "<239>T[195]T<248><241><251><223>S" ECHR 'T' ETWO 'R', 'A' ETWO 'D', 'I' ETWO 'T', 'I' ETWO 'O', 'N' ECHR 'S' EQUB VE ETWO 'L', 'O' \ Token 39: "LOATHING OF [41-45]" ETWO 'A', 'T' \ ECHR 'H' \ Encoded as: "<224><245>H[195]OF [9?]" ETOK 195 ECHR 'O' ECHR 'F' ECHR ' ' ERND 9 EQUB VE ETWO 'L', 'O' \ Token 40: "LOVE FOR [41-45]" ETWO 'V', 'E' \ ECHR ' ' \ Encoded as: "<224><250> F<253> [9?]" ECHR 'F' ETWO 'O', 'R' ECHR ' ' ERND 9 EQUB VE ECHR 'F' \ Token 41: "FOOD BLENDERS" ECHR 'O' \ ECHR 'O' \ Encoded as: "FOOD B<229>ND<244>S" ECHR 'D' ECHR ' ' ECHR 'B' ETWO 'L', 'E' ECHR 'N' ECHR 'D' ETWO 'E', 'R' ECHR 'S' EQUB VE ECHR 'T' \ Token 42: "TOURISTS" ETWO 'O', 'U' \ ECHR 'R' \ Encoded as: "T<217>RI<222>S" ECHR 'I' ETWO 'S', 'T' ECHR 'S' EQUB VE ECHR 'P' \ Token 43: "POETRY" ECHR 'O' \ ETWO 'E', 'T' \ Encoded as: "PO<221>RY" ECHR 'R' ECHR 'Y' EQUB VE ETWO 'D', 'I' \ Token 44: "DISCOS" ECHR 'S' \ ECHR 'C' \ Encoded as: "<241>SCOS" ECHR 'O' ECHR 'S' EQUB VE ERND 17 \ Token 45: "[81-85]" EQUB VE \ \ Encoded as: "[17?]" ECHR 'W' \ Token 46: "WALKING TREE" ETWO 'A', 'L' \ ECHR 'K' \ Encoded as: "W<228>K[195][158]" ETOK 195 ETOK 158 EQUB VE ECHR 'C' \ Token 47: "CRAB" ETWO 'R', 'A' \ ECHR 'B' \ Encoded as: "C<248>B" EQUB VE ECHR 'B' \ Token 48: "BAT" ETWO 'A', 'T' \ EQUB VE \ Encoded as: "B<245>" ETWO 'L', 'O' \ Token 49: "LOBST" ECHR 'B' \ ETWO 'S', 'T' \ Encoded as: "<224>B<222>" EQUB VE EJMP 18 \ Token 50: "{random 1-8 letter word}" EQUB VE \ \ Encoded as: "{18}" ETWO 'B', 'E' \ Token 51: "BESET" ECHR 'S' \ ETWO 'E', 'T' \ Encoded as: "<247>S<221>" EQUB VE ECHR 'P' \ Token 52: "PLAGUED" ETWO 'L', 'A' \ ECHR 'G' \ Encoded as: "P<249>GU<252>" ECHR 'U' ETWO 'E', 'D' EQUB VE ETWO 'R', 'A' \ Token 53: "RAVAGED" ECHR 'V' \ ECHR 'A' \ Encoded as: "<248>VAG<252>" ECHR 'G' ETWO 'E', 'D' EQUB VE ECHR 'C' \ Token 54: "CURSED" ECHR 'U' \ ECHR 'R' \ Encoded as: "CURS<252>" ECHR 'S' ETWO 'E', 'D' EQUB VE ECHR 'S' \ Token 55: "SCOURGED" ECHR 'C' \ ETWO 'O', 'U' \ Encoded as: "SC<217>RG<252>" ECHR 'R' ECHR 'G' ETWO 'E', 'D' EQUB VE ERND 22 \ Token 56: "[135-139] CIVIL WAR" ECHR ' ' \ ECHR 'C' \ Encoded as: "[22?] CIV<220> W<238>" ECHR 'I' ECHR 'V' ETWO 'I', 'L' ECHR ' ' ECHR 'W' ETWO 'A', 'R' EQUB VE ERND 13 \ Token 57: "[170-174] [155-159] [160-164]S" ECHR ' ' \ ERND 4 \ Encoded as: "[13?] [4?] [5?]S" ECHR ' ' ERND 5 ECHR 'S' EQUB VE ECHR 'A' \ Token 58: "A [170-174] DISEASE" ECHR ' ' \ ERND 13 \ Encoded as: "A [13?] <241><218>A<218>" ECHR ' ' ETWO 'D', 'I' ETWO 'S', 'E' ECHR 'A' ETWO 'S', 'E' EQUB VE ERND 22 \ Token 59: "[135-139] EARTHQUAKES" ECHR ' ' \ ECHR 'E' \ Encoded as: "[22?] E<238><226><254>AK<237>" ETWO 'A', 'R' ETWO 'T', 'H' ETWO 'Q', 'U' ECHR 'A' ECHR 'K' ETWO 'E', 'S' EQUB VE ERND 22 \ Token 60: "[135-139] SOLAR ACTIVITY" ECHR ' ' \ ETWO 'S', 'O' \ Encoded as: "[22?] <235><249>R AC<251>V<219>Y" ETWO 'L', 'A' ECHR 'R' ECHR ' ' ECHR 'A' ECHR 'C' ETWO 'T', 'I' ECHR 'V' ETWO 'I', 'T' ECHR 'Y' EQUB VE ETOK 175 \ Token 61: "ITS [26-30] [31-35]" ERND 2 \ ECHR ' ' \ Encoded as: "[175][2?] [3?]" ERND 3 EQUB VE ETOK 147 \ Token 62: "THE {system name adjective} [155-159] EJMP 17 \ [160-164]" ECHR ' ' \ ERND 4 \ Encoded as: "[147]{17} [4?] [5?]" ECHR ' ' ERND 5 EQUB VE ETOK 175 \ Token 63: "ITS INHABITANTS' [165-169] [36-40]" ETOK 193 \ ECHR 'S' \ Encoded as: "[175][193]S' [7?] [8?]" ECHR '`' ECHR ' ' ERND 7 ECHR ' ' ERND 8 EQUB VE EJMP 2 \ Token 64: "{sentence case}[235-239]{lower case}" ERND 31 \ EJMP 13 \ Encoded as: "{2}[31?]{13}" EQUB VE ETOK 175 \ Token 65: "ITS [76-80] [81-85]" ERND 16 \ ECHR ' ' \ Encoded as: "[175][16?] [17?]" ERND 17 EQUB VE ECHR 'J' \ Token 66: "JUICE" ECHR 'U' \ ECHR 'I' \ Encoded as: "JUI<233>" ETWO 'C', 'E' EQUB VE ECHR 'B' \ Token 67: "BRANDY" ETWO 'R', 'A' \ ECHR 'N' \ Encoded as: "B<248>NDY" ECHR 'D' ECHR 'Y' EQUB VE ECHR 'W' \ Token 68: "WATER" ETWO 'A', 'T' \ ETWO 'E', 'R' \ Encoded as: "W<245><244>" EQUB VE ECHR 'B' \ Token 69: "BREW" ETWO 'R', 'E' \ ECHR 'W' \ Encoded as: "B<242>W" EQUB VE ECHR 'G' \ Token 70: "GARGLE BLASTERS" ETWO 'A', 'R' \ ECHR 'G' \ Encoded as: "G<238>G<229> B<249><222><244>S" ETWO 'L', 'E' ECHR ' ' ECHR 'B' ETWO 'L', 'A' ETWO 'S', 'T' ETWO 'E', 'R' ECHR 'S' EQUB VE EJMP 18 \ Token 71: "{random 1-8 letter word}" EQUB VE \ \ Encoded as: "{18}" EJMP 17 \ Token 72: "{system name adjective} [160-164]" ECHR ' ' \ ERND 5 \ Encoded as: "{17} [5?]" EQUB VE EJMP 17 \ Token 73: "{system name adjective} {random 1-8 ECHR ' ' \ letter word}" EJMP 18 \ EQUB VE \ Encoded as: "{17} {18}" EJMP 17 \ Token 74: "{system name adjective} [170-174]" ECHR ' ' \ ERND 13 \ Encoded as: "{17} [13?]" EQUB VE ERND 13 \ Token 75: "[170-174] {random 1-8 letter word}" ECHR ' ' \ EJMP 18 \ Encoded as: "[13?] {18}" EQUB VE ECHR 'F' \ Token 76: "FABULOUS" ETWO 'A', 'B' \ ECHR 'U' \ Encoded as: "F<216>U<224><236>" ETWO 'L', 'O' ETWO 'U', 'S' EQUB VE ECHR 'E' \ Token 77: "EXOTIC" ECHR 'X' \ ECHR 'O' \ Encoded as: "EXO<251>C" ETWO 'T', 'I' ECHR 'C' EQUB VE ECHR 'H' \ Token 78: "HOOPY" ECHR 'O' \ ECHR 'O' \ Encoded as: "HOOPY" ECHR 'P' ECHR 'Y' EQUB VE ECHR 'U' \ Token 79: "UNUSUAL" ETWO 'N', 'U' \ ECHR 'S' \ Encoded as: "U<225>SU<228>" ECHR 'U' ETWO 'A', 'L' EQUB VE ECHR 'E' \ Token 80: "EXCITING" ECHR 'X' \ ECHR 'C' \ Encoded as: "EXC<219><240>G" ETWO 'I', 'T' ETWO 'I', 'N' ECHR 'G' EQUB VE ECHR 'C' \ Token 81: "CUISINE" ECHR 'U' \ ECHR 'I' \ Encoded as: "CUIS<240>E" ECHR 'S' ETWO 'I', 'N' ECHR 'E' EQUB VE ECHR 'N' \ Token 82: "NIGHT LIFE" ECHR 'I' \ ECHR 'G' \ Encoded as: "NIGHT LIFE" ECHR 'H' ECHR 'T' ECHR ' ' ECHR 'L' ECHR 'I' ECHR 'F' ECHR 'E' EQUB VE ECHR 'C' \ Token 83: "CASINOS" ECHR 'A' \ ECHR 'S' \ Encoded as: "CASI<227>S" ECHR 'I' ETWO 'N', 'O' ECHR 'S' EQUB VE ECHR 'S' \ Token 84: "SIT COMS" ETWO 'I', 'T' \ ECHR ' ' \ Encoded as: "S<219> COMS" ECHR 'C' ECHR 'O' ECHR 'M' ECHR 'S' EQUB VE EJMP 2 \ Token 85: "{sentence case}[235-239]{lower case}" ERND 31 \ EJMP 13 \ Encoded as: "{2}[31?]{13}" EQUB VE EJMP 3 \ Token 86: "{selected system name}" EQUB VE \ \ Encoded as: "{3}" ETOK 147 \ Token 87: "THE PLANET {selected system name}" ETOK 145 \ ECHR ' ' \ Encoded as: "[147][145] {3}" EJMP 3 EQUB VE ETOK 147 \ Token 88: "THE WORLD {selected system name}" ETOK 146 \ ECHR ' ' \ Encoded as: "[147][146] {3}" EJMP 3 EQUB VE ETOK 148 \ Token 89: "THIS PLANET" ETOK 145 \ EQUB VE \ Encoded as: "[148][145]" ETOK 148 \ Token 90: "THIS WORLD" ETOK 146 \ EQUB VE \ Encoded as: "[148][146]" ECHR 'S' \ Token 91: "SON OF A BITCH" ETWO 'O', 'N' \ ECHR ' ' \ Encoded as: "S<223> OF[208]B<219>CH" ECHR 'O' ECHR 'F' ETOK 208 ECHR 'B' ETWO 'I', 'T' ECHR 'C' ECHR 'H' EQUB VE ECHR 'S' \ Token 92: "SCOUNDREL" ECHR 'C' \ ETWO 'O', 'U' \ Encoded as: "SC<217>ND<242>L" ECHR 'N' ECHR 'D' ETWO 'R', 'E' ECHR 'L' EQUB VE ECHR 'B' \ Token 93: "BLACKGUARD" ETWO 'L', 'A' \ ECHR 'C' \ Encoded as: "B<249>CKGU<238>D" ECHR 'K' ECHR 'G' ECHR 'U' ETWO 'A', 'R' ECHR 'D' EQUB VE ECHR 'R' \ Token 94: "ROGUE" ECHR 'O' \ ECHR 'G' \ Encoded as: "ROGUE" ECHR 'U' ECHR 'E' EQUB VE ECHR 'W' \ Token 95: "WHORESON BEETLE HEADED FLAP EAR'D ECHR 'H' \ KNAVE" ETWO 'O', 'R' \ ETWO 'E', 'S' \ Encoded as: "WH<253><237><223> <247><221><229> HEAD ETWO 'O', 'N' \ [196]F<249>P E<238>'D KNA<250>" ECHR ' ' ETWO 'B', 'E' ETWO 'E', 'T' ETWO 'L', 'E' ECHR ' ' ECHR 'H' ECHR 'E' ECHR 'A' ECHR 'D' ETOK 196 ECHR 'F' ETWO 'L', 'A' ECHR 'P' ECHR ' ' ECHR 'E' ETWO 'A', 'R' ECHR '`' ECHR 'D' ECHR ' ' ECHR 'K' ECHR 'N' ECHR 'A' ETWO 'V', 'E' EQUB VE ECHR 'N' \ Token 96: "N UNREMARKABLE" ECHR ' ' \ ECHR 'U' \ Encoded as: "N UN<242><239>RK<216><229>" ECHR 'N' ETWO 'R', 'E' ETWO 'M', 'A' ECHR 'R' ECHR 'K' ETWO 'A', 'B' ETWO 'L', 'E' EQUB VE ECHR ' ' \ Token 97: " BORING" ECHR 'B' \ ETWO 'O', 'R' \ Encoded as: " B<253><240>G" ETWO 'I', 'N' ECHR 'G' EQUB VE ECHR ' ' \ Token 98: " DULL" ECHR 'D' \ ECHR 'U' \ Encoded as: " DULL" ECHR 'L' ECHR 'L' EQUB VE ECHR ' ' \ Token 99: " TEDIOUS" ECHR 'T' \ ECHR 'E' \ Encoded as: " TE<241>O<236>" ETWO 'D', 'I' ECHR 'O' ETWO 'U', 'S' EQUB VE ECHR ' ' \ Token 100: " REVOLTING" ETWO 'R', 'E' \ ECHR 'V' \ Encoded as: " <242>VOLT<240>G" ECHR 'O' ECHR 'L' ECHR 'T' ETWO 'I', 'N' ECHR 'G' EQUB VE ETOK 145 \ Token 101: "PLANET" EQUB VE \ \ Encoded as: "[145]" ETOK 146 \ Token 102: "WORLD" EQUB VE \ \ Encoded as: "[146]" ECHR 'P' \ Token 103: "PLACE" ETWO 'L', 'A' \ ETWO 'C', 'E' \ Encoded as: "P<249><233>" EQUB VE ECHR 'L' \ Token 104: "LITTLE PLANET" ETWO 'I', 'T' \ ECHR 'T' \ Encoded as: "L<219>T<229> [145]" ETWO 'L', 'E' ECHR ' ' ETOK 145 EQUB VE ECHR 'D' \ Token 105: "DUMP" ECHR 'U' \ ECHR 'M' \ Encoded as: "DUMP" ECHR 'P' EQUB VE ECHR 'I' \ Token 106: "I HEAR A [130-134] LOOKING SHIP ECHR ' ' \ APPEARED AT ERRIUS" ECHR 'H' \ ECHR 'E' \ Encoded as: "I HE<238>[208][23?] <224>OK[195][207] ETWO 'A', 'R' \ APPE<238>[196]<245>[209]" ETOK 208 ERND 23 ECHR ' ' ETWO 'L', 'O' ECHR 'O' ECHR 'K' ETOK 195 ETOK 207 ECHR ' ' ECHR 'A' ECHR 'P' ECHR 'P' ECHR 'E' ETWO 'A', 'R' ETOK 196 ETWO 'A', 'T' ETOK 209 EQUB VE ECHR 'Y' \ Token 107: "YEAH, I HEAR A [130-134] SHIP LEFT ECHR 'E' \ ERRIUS A WHILE BACK" ECHR 'A' \ ECHR 'H' \ Encoded as: "YEAH, I HE<238>[208][23?] [207] ECHR ',' \ <229>FT[209][208] WHI<229> BACK" ECHR ' ' ECHR 'I' ECHR ' ' ECHR 'H' ECHR 'E' ETWO 'A', 'R' ETOK 208 ERND 23 ECHR ' ' ETOK 207 ECHR ' ' ETWO 'L', 'E' ECHR 'F' ECHR 'T' ETOK 209 ETOK 208 ECHR ' ' ECHR 'W' ECHR 'H' ECHR 'I' ETWO 'L', 'E' ECHR ' ' ECHR 'B' ECHR 'A' ECHR 'C' ECHR 'K' EQUB VE ECHR 'G' \ Token 108: "GET YOUR IRON ASS OVER TO ERRIUS" ETWO 'E', 'T' \ ECHR ' ' \ Encoded as: "G<221> [179]R IR<223> ASS OV<244> TO ETOK 179 \ [209]" ECHR 'R' ECHR ' ' ECHR 'I' ECHR 'R' ETWO 'O', 'N' ECHR ' ' ECHR 'A' ECHR 'S' ECHR 'S' ECHR ' ' ECHR 'O' ECHR 'V' ETWO 'E', 'R' ECHR ' ' ECHR 'T' ECHR 'O' ETOK 209 EQUB VE ETWO 'S', 'O' \ Token 109: "SOME [91-95] NEW SHIP WAS SEEN AT ECHR 'M' \ ERRIUS" ECHR 'E' \ ECHR ' ' \ Encoded as: "<235>ME [24?][210][207] WAS <218><246> ERND 24 \ <245>[209]" ETOK 210 ETOK 207 ECHR ' ' ECHR 'W' ECHR 'A' ECHR 'S' ECHR ' ' ETWO 'S', 'E' ETWO 'E', 'N' ECHR ' ' ETWO 'A', 'T' ETOK 209 EQUB VE ECHR 'T' \ Token 110: "TRY ERRIUS" ECHR 'R' \ ECHR 'Y' \ Encoded as: "TRY[209]" ETOK 209 EQUB VE EQUB VE \ Token 111: "" \ \ Encoded as: "" EQUB VE \ Token 112: "" \ \ Encoded as: "" EQUB VE \ Token 113: "" \ \ Encoded as: "" EQUB VE \ Token 114: "" \ \ Encoded as: "" ECHR 'W' \ Token 115: "WASP" ECHR 'A' \ ECHR 'S' \ Encoded as: "WASP" ECHR 'P' EQUB VE ECHR 'M' \ Token 116: "MOTH" ECHR 'O' \ ETWO 'T', 'H' \ Encoded as: "MO<226>" EQUB VE ECHR 'G' \ Token 117: "GRUB" ECHR 'R' \ ECHR 'U' \ Encoded as: "GRUB" ECHR 'B' EQUB VE ETWO 'A', 'N' \ Token 118: "ANT" ECHR 'T' \ EQUB VE \ Encoded as: "<255>T" EJMP 18 \ Token 119: "{random 1-8 letter word}" EQUB VE \ \ Encoded as: "{18}" ECHR 'P' \ Token 120: "POET" ECHR 'O' \ ETWO 'E', 'T' \ Encoded as: "PO<221>" EQUB VE ETWO 'A', 'R' \ Token 121: "ARTS GRADUATE" ECHR 'T' \ ECHR 'S' \ Encoded as: "<238>TS G<248>DU<245>E" ECHR ' ' ECHR 'G' ETWO 'R', 'A' ECHR 'D' ECHR 'U' ETWO 'A', 'T' ECHR 'E' EQUB VE ECHR 'Y' \ Token 122: "YAK" ECHR 'A' \ ECHR 'K' \ Encoded as: "YAK" EQUB VE ECHR 'S' \ Token 123: "SNAIL" ECHR 'N' \ ECHR 'A' \ Encoded as: "SNA<220>" ETWO 'I', 'L' EQUB VE ECHR 'S' \ Token 124: "SLUG" ECHR 'L' \ ECHR 'U' \ Encoded as: "SLUG" ECHR 'G' EQUB VE ECHR 'T' \ Token 125: "TROPICAL" ECHR 'R' \ ECHR 'O' \ Encoded as: "TROPIC<228>" ECHR 'P' ECHR 'I' ECHR 'C' ETWO 'A', 'L' EQUB VE ECHR 'D' \ Token 126: "DENSE" ETWO 'E', 'N' \ ETWO 'S', 'E' \ Encoded as: "D<246><218>" EQUB VE ETWO 'R', 'A' \ Token 127: "RAIN" ETWO 'I', 'N' \ EQUB VE \ Encoded as: "<248><240>" ECHR 'I' \ Token 128: "IMPENETRABLE" ECHR 'M' \ ECHR 'P' \ Encoded as: "IMP<246><221><248>B<229>" ETWO 'E', 'N' ETWO 'E', 'T' ETWO 'R', 'A' ECHR 'B' ETWO 'L', 'E' EQUB VE ECHR 'E' \ Token 129: "EXUBERANT" ECHR 'X' \ ECHR 'U' \ Encoded as: "EXU<247><248>NT" ETWO 'B', 'E' ETWO 'R', 'A' ECHR 'N' ECHR 'T' EQUB VE ECHR 'F' \ Token 130: "FUNNY" ECHR 'U' \ ECHR 'N' \ Encoded as: "FUNNY" ECHR 'N' ECHR 'Y' EQUB VE ECHR 'W' \ Token 131: "WIERD" ECHR 'I' \ ETWO 'E', 'R' \ Encoded as: "WI<244>D" ECHR 'D' EQUB VE ECHR 'U' \ Token 132: "UNUSUAL" ETWO 'N', 'U' \ ECHR 'S' \ Encoded as: "U<225>SU<228>" ECHR 'U' ETWO 'A', 'L' EQUB VE ETWO 'S', 'T' \ Token 133: "STRANGE" ETWO 'R', 'A' \ ECHR 'N' \ Encoded as: "<222><248>N<231>" ETWO 'G', 'E' EQUB VE ECHR 'P' \ Token 134: "PECULIAR" ECHR 'E' \ ECHR 'C' \ Encoded as: "PECULI<238>" ECHR 'U' ECHR 'L' ECHR 'I' ETWO 'A', 'R' EQUB VE ECHR 'F' \ Token 135: "FREQUENT" ETWO 'R', 'E' \ ETWO 'Q', 'U' \ Encoded as: "F<242><254><246>T" ETWO 'E', 'N' ECHR 'T' EQUB VE ECHR 'O' \ Token 136: "OCCASIONAL" ECHR 'C' \ ECHR 'C' \ Encoded as: "OCCASI<223><228>" ECHR 'A' ECHR 'S' ECHR 'I' ETWO 'O', 'N' ETWO 'A', 'L' EQUB VE ECHR 'U' \ Token 137: "UNPREDICTABLE" ECHR 'N' \ ECHR 'P' \ Encoded as: "UNP<242><241>CT<216><229>" ETWO 'R', 'E' ETWO 'D', 'I' ECHR 'C' ECHR 'T' ETWO 'A', 'B' ETWO 'L', 'E' EQUB VE ECHR 'D' \ Token 138: "DREADFUL" ETWO 'R', 'E' \ ECHR 'A' \ Encoded as: "D<242>ADFUL" ECHR 'D' ECHR 'F' ECHR 'U' ECHR 'L' EQUB VE ETOK 171 \ Token 139: "DEADLY" EQUB VE \ \ Encoded as: "[171]" ERND 1 \ Token 140: "[21-25] [16-20] FOR [61-65]" ECHR ' ' \ ERND 0 \ Encoded as: "[1?] [0?] F<253> [10?]" ECHR ' ' ECHR 'F' ETWO 'O', 'R' ECHR ' ' ERND 10 EQUB VE ETOK 140 \ Token 141: "[21-25] [16-20] FOR [61-65] AND ETOK 178 \ [61-65]" ERND 10 \ EQUB VE \ Encoded as: "[140][178][10?]" ERND 11 \ Token 142: "[51-55] BY [56-60]" ECHR ' ' \ ECHR 'B' \ Encoded as: "[11?] BY [12?]" ECHR 'Y' ECHR ' ' ERND 12 EQUB VE ETOK 140 \ Token 143: "[21-25] [16-20] FOR [61-65] BUT [51-55] ECHR ' ' \ BY [56-60]" ECHR 'B' \ ECHR 'U' \ Encoded as: "[140] BUT [142]" ECHR 'T' ECHR ' ' ETOK 142 EQUB VE ECHR ' ' \ Token 144: " A[96-100] [101-105]" ECHR 'A' \ ERND 20 \ Encoded as: " A[20?] [21?]" ECHR ' ' ERND 21 EQUB VE ECHR 'P' \ Token 145: "PLANET" ECHR 'L' \ ETWO 'A', 'N' \ Encoded as: "PL<255><221>" ETWO 'E', 'T' EQUB VE ECHR 'W' \ Token 146: "WORLD" ETWO 'O', 'R' \ ECHR 'L' \ Encoded as: "W<253>LD" ECHR 'D' EQUB VE ETWO 'T', 'H' \ Token 147: "THE " ECHR 'E' \ ECHR ' ' \ Encoded as: "<226>E " EQUB VE ETWO 'T', 'H' \ Token 148: "THIS " ECHR 'I' \ ECHR 'S' \ Encoded as: "<226>IS " ECHR ' ' EQUB VE ETWO 'L', 'O' \ Token 149: "LOAD NEW {single cap}COMMANDER" ECHR 'A' \ ECHR 'D' \ Encoded as: "<224>AD[210][154]" ETOK 210 ETOK 154 EQUB VE EJMP 9 \ Token 150: "{clear screen} EJMP 11 \ {draw box around title} EJMP 1 \ {all caps} EJMP 8 \ {tab 6}" EQUB VE \ \ Encoded as: "{9}{11}{1}{8}" ECHR 'D' \ Token 151: "DRIVE" ECHR 'R' \ ECHR 'I' \ Encoded as: "DRI<250>" ETWO 'V', 'E' EQUB VE ECHR ' ' \ Token 152: " CATALOGUE" ECHR 'C' \ ETWO 'A', 'T' \ Encoded as: " C<245>A<224>GUE" ECHR 'A' ETWO 'L', 'O' ECHR 'G' ECHR 'U' ECHR 'E' EQUB VE ECHR 'I' \ Token 153: "IAN" ETWO 'A', 'N' \ EQUB VE \ Encoded as: "I<255>" EJMP 19 \ Token 154: "{single cap}COMMANDER" ECHR 'C' \ ECHR 'O' \ Encoded as: "{19}COMM<255>D<244>" ECHR 'M' ECHR 'M' ETWO 'A', 'N' ECHR 'D' ETWO 'E', 'R' EQUB VE ERND 13 \ Token 155: "[170-174]" EQUB VE \ \ Encoded as: "[13?]" ECHR 'M' \ Token 156: "MOUNTAIN" ETWO 'O', 'U' \ ECHR 'N' \ Encoded as: "M<217>NTA<240>" ECHR 'T' ECHR 'A' ETWO 'I', 'N' EQUB VE ETWO 'E', 'D' \ Token 157: "EDIBLE" ECHR 'I' \ ECHR 'B' \ Encoded as: "<252>IB<229>" ETWO 'L', 'E' EQUB VE ECHR 'T' \ Token 158: "TREE" ETWO 'R', 'E' \ ECHR 'E' \ Encoded as: "T<242>E" EQUB VE ECHR 'S' \ Token 159: "SPOTTED" ECHR 'P' \ ECHR 'O' \ Encoded as: "SPOTT<252>" ECHR 'T' ECHR 'T' ETWO 'E', 'D' EQUB VE ERND 29 \ Token 160: "[225-229]" EQUB VE \ \ Encoded as: "[29?]" ERND 30 \ Token 161: "[230-234]" EQUB VE \ \ Encoded as: "[30?]" ERND 6 \ Token 162: "[46-50]OID" ECHR 'O' \ ECHR 'I' \ Encoded as: "[6?]OID" ECHR 'D' EQUB VE ERND 36 \ Token 163: "[120-124]" EQUB VE \ \ Encoded as: "[36?]" ERND 35 \ Token 164: "[115-119]" EQUB VE \ \ Encoded as: "[35?]" ETWO 'A', 'N' \ Token 165: "ANCIENT" ECHR 'C' \ ECHR 'I' \ Encoded as: "<255>CI<246>T" ETWO 'E', 'N' ECHR 'T' EQUB VE ECHR 'E' \ Token 166: "EXCEPTIONAL" ECHR 'X' \ ETWO 'C', 'E' \ Encoded as: "EX<233>P<251><223><228>" ECHR 'P' ETWO 'T', 'I' ETWO 'O', 'N' ETWO 'A', 'L' EQUB VE ECHR 'E' \ Token 167: "ECCENTRIC" ECHR 'C' \ ETWO 'C', 'E' \ Encoded as: "EC<233>NTRIC" ECHR 'N' ECHR 'T' ECHR 'R' ECHR 'I' ECHR 'C' EQUB VE ETWO 'I', 'N' \ Token 168: "INGRAINED" ECHR 'G' \ ETWO 'R', 'A' \ Encoded as: "<240>G<248><240><252>" ETWO 'I', 'N' ETWO 'E', 'D' EQUB VE ERND 23 \ Token 169: "[130-134]" EQUB VE \ \ Encoded as: "[23?]" ECHR 'K' \ Token 170: "KILLER" ETWO 'I', 'L' \ ECHR 'L' \ Encoded as: "K<220>L<244>" ETWO 'E', 'R' EQUB VE ECHR 'D' \ Token 171: "DEADLY" ECHR 'E' \ ECHR 'A' \ Encoded as: "DEADLY" ECHR 'D' ECHR 'L' ECHR 'Y' EQUB VE ECHR 'E' \ Token 172: "EVIL" ECHR 'V' \ ETWO 'I', 'L' \ Encoded as: "EV<220>" EQUB VE ETWO 'L', 'E' \ Token 173: "LETHAL" ETWO 'T', 'H' \ ETWO 'A', 'L' \ Encoded as: "<229><226><228>" EQUB VE ECHR 'V' \ Token 174: "VICIOUS" ECHR 'I' \ ECHR 'C' \ Encoded as: "VICIO<236>" ECHR 'I' ECHR 'O' ETWO 'U', 'S' EQUB VE ETWO 'I', 'T' \ Token 175: "ITS " ECHR 'S' \ ECHR ' ' \ Encoded as: "<219>S " EQUB VE EJMP 13 \ Token 176: "{lower case} EJMP 14 \ {justify} EJMP 19 \ {single cap}" EQUB VE \ \ Encoded as: "{13}{14}{19}" ECHR '.' \ Token 177: ".{cr} EJMP 12 \ {left align}" EJMP 15 \ EQUB VE \ Encoded as: ".{12}{15}" ECHR ' ' \ Token 178: " AND " ETWO 'A', 'N' \ ECHR 'D' \ Encoded as: " <255>D " ECHR ' ' EQUB VE ECHR 'Y' \ Token 179: "YOU" ETWO 'O', 'U' \ EQUB VE \ Encoded as: "Y<217>" ECHR 'P' \ Token 180: "PARKING METERS" ETWO 'A', 'R' \ ECHR 'K' \ Encoded as: "P<238>K[195]M<221><244>S" ETOK 195 ECHR 'M' ETWO 'E', 'T' ETWO 'E', 'R' ECHR 'S' EQUB VE ECHR 'D' \ Token 181: "DUST CLOUDS" ETWO 'U', 'S' \ ECHR 'T' \ Encoded as: "D<236>T C<224>UDS" ECHR ' ' ECHR 'C' ETWO 'L', 'O' ECHR 'U' ECHR 'D' ECHR 'S' EQUB VE ECHR 'I' \ Token 182: "ICE BERGS" ETWO 'C', 'E' \ ECHR ' ' \ Encoded as: "I<233> <247>RGS" ETWO 'B', 'E' ECHR 'R' ECHR 'G' ECHR 'S' EQUB VE ECHR 'R' \ Token 183: "ROCK FORMATIONS" ECHR 'O' \ ECHR 'C' \ Encoded as: "ROCK F<253><239><251><223>S" ECHR 'K' ECHR ' ' ECHR 'F' ETWO 'O', 'R' ETWO 'M', 'A' ETWO 'T', 'I' ETWO 'O', 'N' ECHR 'S' EQUB VE ECHR 'V' \ Token 184: "VOLCANOES" ECHR 'O' \ ECHR 'L' \ Encoded as: "VOLCA<227><237>" ECHR 'C' ECHR 'A' ETWO 'N', 'O' ETWO 'E', 'S' EQUB VE ECHR 'P' \ Token 185: "PLANT" ECHR 'L' \ ETWO 'A', 'N' \ Encoded as: "PL<255>T" ECHR 'T' EQUB VE ECHR 'T' \ Token 186: "TULIP" ECHR 'U' \ ECHR 'L' \ Encoded as: "TULIP" ECHR 'I' ECHR 'P' EQUB VE ECHR 'B' \ Token 187: "BANANA" ETWO 'A', 'N' \ ETWO 'A', 'N' \ Encoded as: "B<255><255>A" ECHR 'A' EQUB VE ECHR 'C' \ Token 188: "CORN" ETWO 'O', 'R' \ ECHR 'N' \ Encoded as: "C<253>N" EQUB VE EJMP 18 \ Token 189: "{random 1-8 letter word}WEED" ECHR 'W' \ ECHR 'E' \ Encoded as: "{18}WE<252>" ETWO 'E', 'D' EQUB VE EJMP 18 \ Token 190: "{random 1-8 letter word}" EQUB VE \ \ Encoded as: "{18}" EJMP 17 \ Token 191: "{system name adjective} {random 1-8 ECHR ' ' \ letter word}" EJMP 18 \ EQUB VE \ Encoded as: "{17} {18}" EJMP 17 \ Token 192: "{system name adjective} [170-174]" ECHR ' ' \ ERND 13 \ Encoded as: "{17} [13?]" EQUB VE ETWO 'I', 'N' \ Token 193: "INHABITANT" ECHR 'H' \ ECHR 'A' \ Encoded as: "<240>HA<234>T<255>T" ETWO 'B', 'I' ECHR 'T' ETWO 'A', 'N' ECHR 'T' EQUB VE ETOK 191 \ Token 194: "{system name adjective} {random 1-8 EQUB VE \ letter word}" \ \ Encoded as: "[191]" ETWO 'I', 'N' \ Token 195: "ING " ECHR 'G' \ ECHR ' ' \ Encoded as: "<240>G " EQUB VE ETWO 'E', 'D' \ Token 196: "ED " ECHR ' ' \ EQUB VE \ Encoded as: "<252> " EQUB VE \ Token 197: "" \ \ Encoded as: "" EQUB VE \ Token 198: "" \ \ Encoded as: "" EQUB VE \ Token 199: "" \ \ Encoded as: "" ECHR ' ' \ Token 200: " NAME? " ECHR 'N' \ ECHR 'A' \ Encoded as: " NAME? " ECHR 'M' ECHR 'E' ECHR '?' ECHR ' ' EQUB VE ECHR ' ' \ Token 201: " TO " ECHR 'T' \ ECHR 'O' \ Encoded as: " TO " ECHR ' ' EQUB VE ECHR ' ' \ Token 202: " IS " ECHR 'I' \ ECHR 'S' \ Encoded as: " IS " ECHR ' ' EQUB VE ECHR 'W' \ Token 203: "WAS LAST SEEN AT {single cap}" ECHR 'A' \ ECHR 'S' \ Encoded as: "WAS <249><222> <218><246> <245> {19}" ECHR ' ' ETWO 'L', 'A' ETWO 'S', 'T' ECHR ' ' ETWO 'S', 'E' ETWO 'E', 'N' ECHR ' ' ETWO 'A', 'T' ECHR ' ' EJMP 19 EQUB VE ECHR '.' \ Token 204: ".{cr} EJMP 12 \ {single cap}" ECHR ' ' \ EJMP 19 \ Encoded as: ".{12} {19}" EQUB VE ECHR 'D' \ Token 205: "DOCKED" ECHR 'O' \ ECHR 'C' \ Encoded as: "DOCK<252>" ECHR 'K' ETWO 'E', 'D' EQUB VE EJMP 1 \ Token 206: "{all caps}(Y/N)?" ECHR '(' \ ECHR 'Y' \ Encoded as: "{1}(Y/N)?" ECHR '/' ECHR 'N' ECHR ')' ECHR '?' EQUB VE ECHR 'S' \ Token 207: "SHIP" ECHR 'H' \ ECHR 'I' \ Encoded as: "SHIP" ECHR 'P' EQUB VE ECHR ' ' \ Token 208: " A " ECHR 'A' \ ECHR ' ' \ Encoded as: " A " EQUB VE ECHR ' ' \ Token 209: " ERRIUS" ETWO 'E', 'R' \ ECHR 'R' \ Encoded as: " <244>RI<236>" ECHR 'I' ETWO 'U', 'S' EQUB VE ECHR ' ' \ Token 210: " NEW " ECHR 'N' \ ECHR 'E' \ Encoded as: " NEW " ECHR 'W' ECHR ' ' EQUB VE EJMP 2 \ Token 211: "{sentence case} HER MAJESTY'S SPACE ECHR ' ' \ NAVY{lower case}" ECHR 'H' \ ETWO 'E', 'R' \ Encoded as: "{2} H<244> <239>J<237>TY'S SPA<233> NAV ECHR ' ' \ Y{13}" ETWO 'M', 'A' ECHR 'J' ETWO 'E', 'S' ECHR 'T' ECHR 'Y' ECHR '`' ECHR 'S' ECHR ' ' ECHR 'S' ECHR 'P' ECHR 'A' ETWO 'C', 'E' ECHR ' ' ECHR 'N' ECHR 'A' ECHR 'V' ECHR 'Y' EJMP 13 EQUB VE ETOK 177 \ Token 212: ".{cr} EJMP 8 \ {left align} EJMP 1 \ {tab 6}{all caps} MESSAGE ENDS" ECHR ' ' \ ECHR ' ' \ Encoded as: "[177]{8}{1} M<237>SA<231> <246>DS" ECHR 'M' ETWO 'E', 'S' ECHR 'S' ECHR 'A' ETWO 'G', 'E' ECHR ' ' ETWO 'E', 'N' ECHR 'D' ECHR 'S' EQUB VE ECHR ' ' \ Token 213: " {single cap}COMMANDER {commander ETOK 154 \ name}, I {lower case}AM{sentence case} ECHR ' ' \ CAPTAIN {mission captain's name} EJMP 4 \ {lower case}OF{sentence case} HER ECHR ',' \ MAJESTY'S SPACE NAVY{lower case}" ECHR ' ' \ ECHR 'I' \ Encoded as: " [154] {4}, I {13}AM{2} CAPTA<240> {27} ECHR ' ' \ {13}OF[211]" EJMP 13 ECHR 'A' ECHR 'M' EJMP 2 ECHR ' ' ECHR 'C' ECHR 'A' ECHR 'P' ECHR 'T' ECHR 'A' ETWO 'I', 'N' ECHR ' ' EJMP 27 ECHR ' ' EJMP 13 ECHR 'O' ECHR 'F' ETOK 211 EQUB VE EQUB VE \ Token 214: "" \ \ Encoded as: "" EJMP 15 \ Token 215: "{left align} UNKNOWN PLANET" ECHR ' ' \ ECHR 'U' \ Encoded as: "{15} UNK<227>WN [145]" ECHR 'N' ECHR 'K' ETWO 'N', 'O' ECHR 'W' ECHR 'N' ECHR ' ' ETOK 145 EQUB VE EJMP 9 \ Token 216: "{clear screen} EJMP 8 \ {tab 6} EJMP 23 \ {move to row 10, white, lower case} EJMP 30 \ {white} EJMP 1 \ {all caps} ETWO 'I', 'N' \ INCOMING MESSAGE" ECHR 'C' \ ECHR 'O' \ Encoded as: "{9}{8}{23}{30}{1}<240>COM[195]M<237>SA ECHR 'M' \ <231>" ETOK 195 ECHR 'M' ETWO 'E', 'S' ECHR 'S' ECHR 'A' ETWO 'G', 'E' EQUB VE ECHR 'C' \ Token 217: "CURRUTHERS" ECHR 'U' \ ECHR 'R' \ Encoded as: "CURRU<226><244>S" ECHR 'R' ECHR 'U' ETWO 'T', 'H' ETWO 'E', 'R' ECHR 'S' EQUB VE ECHR 'F' \ Token 218: "FOSDYKE SMYTHE" ECHR 'O' \ ECHR 'S' \ Encoded as: "FOSDYKE SMY<226>E" ECHR 'D' ECHR 'Y' ECHR 'K' ECHR 'E' ECHR ' ' ECHR 'S' ECHR 'M' ECHR 'Y' ETWO 'T', 'H' ECHR 'E' EQUB VE ECHR 'F' \ Token 219: "FORTESQUE" ETWO 'O', 'R' \ ECHR 'T' \ Encoded as: "F<253>T<237><254>E" ETWO 'E', 'S' ETWO 'Q', 'U' ECHR 'E' EQUB VE ETOK 203 \ Token 220: "WAS LAST SEEN AT {single cap}REESDICE" ETWO 'R', 'E' \ ETWO 'E', 'S' \ Encoded as: "[203]<242><237><241><233>" ETWO 'D', 'I' ETWO 'C', 'E' EQUB VE ECHR 'I' \ Token 221: "IS BELIEVED TO HAVE JUMPED TO THIS ECHR 'S' \ GALAXY" ECHR ' ' \ ETWO 'B', 'E' \ Encoded as: "IS <247>LIEV<252>[201]HA<250> JUMP<252> ECHR 'L' \ [201][148]G<228>AXY" ECHR 'I' ECHR 'E' ECHR 'V' ETWO 'E', 'D' ETOK 201 ECHR 'H' ECHR 'A' ETWO 'V', 'E' ECHR ' ' ECHR 'J' ECHR 'U' ECHR 'M' ECHR 'P' ETWO 'E', 'D' ETOK 201 ETOK 148 ECHR 'G' ETWO 'A', 'L' ECHR 'A' ECHR 'X' ECHR 'Y' EQUB VE \ --- Mod: Code removed for two-player Elite: ---------> \EJMP 25 \ Token 222: "{incoming message screen, wait 2s} \EJMP 9 \ {clear screen} \EJMP 30 \ {white} \EJMP 29 \ {tab 6, white, lower case in words} \EJMP 14 \ {justify} \EJMP 2 \ {sentence case} \ECHR 'G' \ GOOD DAY {single cap}COMMANDER \ECHR 'O' \ {commander name}.{cr} \ECHR 'O' \ {single cap}I{lower case} AM {single \ECHR 'D' \ cap}AGENT{single cap}BLAKE OF {single \ECHR ' ' \ cap}NAVAL {single cap}INTELLEGENCE.{cr} \ECHR 'D' \ {single cap}AS YOU KNOW, THE {single \ECHR 'A' \ cap}NAVY HAVE BEEN KEEPING THE {single \ECHR 'Y' \ cap}THARGOIDS OFF YOUR ASS OUT IN DEEP \ECHR ' ' \ SPACE FOR MANY YEARS NOW. {single cap} \ETOK 154 \ WELL THE SITUATION HAS CHANGED.{cr} \ECHR ' ' \ {single cap}OUR BOYS ARE READY FOR A \EJMP 4 \ PUSH RIGHT TO THE HOME SYSTEM OF THOSE \ETOK 204 \ MOTHERS.{cr} \ECHR 'I' \ {single cap} \EJMP 13 \ {wait for key press} \ECHR ' ' \ {clear screen} \ECHR 'A' \ {white} \ECHR 'M' \ {tab 6, white, lower case in words} \ECHR ' ' \ I{lower case} HAVE OBTAINED THE DEFENCE \EJMP 19 \ PLANS FOR THEIR {single cap}HIVE \ECHR 'A' \ {single cap}WORLDS.{cr} {single cap}THE \ECHR 'G' \ BEETLES KNOW WE'VE GOT SOMETHING BUT \ETWO 'E', 'N' \ NOT WHAT.{cr} {single cap}IF {single \ECHR 'T' \ cap}I TRANSMIT THE PLANS TO OUR BASE ON \ECHR ' ' \ {single cap}BIRERA THEY'LL INTERCEPT \EJMP 19 \ THE TRANSMISSION. {single cap}I NEED A \ECHR 'B' \ SHIP TO MAKE THE RUN.{cr} \ETWO 'L', 'A' \ {single cap}YOU'RE ELECTED.{cr} \ECHR 'K' \ {single cap}THE PLANS ARE UNIPULSE \ECHR 'E' \ CODED WITHIN THIS TRANSMISSION.{cr} \ECHR ' ' \ {single cap}{tab 6}YOU WILL BE \ECHR 'O' \ PAID.{cr} \ECHR 'F' \ {single cap} {single cap}GOOD LUCK \ECHR ' ' \ {single cap}COMMANDER.{cr} \EJMP 19 \ {left align} \ECHR 'N' \ {tab 6}{all caps} MESSAGE ENDS \ECHR 'A' \ {wait for key press}" \ECHR 'V' \ \ECHR 'A' \ Encoded as: "{25}{9}{30}{29}{14}{2}GOOD DAY [154] \ECHR 'L' \ {4}[204]I{13} AM {19}AG<246>T {19}B \ECHR ' ' \ <249>KE OF {19}NAVAL {19}<240>TEL<229> \EJMP 19 \ G<246><233>[204]AS [179] K<227>W, [147] \ETWO 'I', 'N' \ {19}NAVY HA<250> <247><246> KEEP[195] \ECHR 'T' \ [147]{19}<226><238>GOIDS OFF [179]R ASS \ECHR 'E' \ <217>T <240> DEEP SPA<233> F<253> \ECHR 'L' \ <239>NY YE<238>S <227>W. {19}WELL \ETWO 'L', 'E' \ [147]S<219>UA<251><223> HAS CH<255>G \ECHR 'G' \ <252>[204]<217>R BOYS <238>E <242>ADY F \ETWO 'E', 'N' \ <253>[208]PUSH RIGHT[201][147]HOME \ETWO 'C', 'E' \ SYSTEM OF <226>O<218> MO<226><244>S \ETOK 204 \ [204]{24}{9}{30}{29}I{13} HA<250> OBTA \ECHR 'A' \ <240>[196][147]DEF<246><233> P<249>NS F \ECHR 'S' \ <253> <226>EIR {19}HI<250> {19}W<253>LD \ECHR ' ' \ S[204][147]<247><221><229>S K<227>W WE' \ETOK 179 \ <250> GOT <235>ME<226>[195]BUT <227>T W \ECHR ' ' \ H<245>[204]IF {19}I T<248>NSM<219> \ECHR 'K' \ [147]P<249>NS[201]<217>R BA<218> <223> \ETWO 'N', 'O' \ {19}<234><242><248> <226>EY'LL <240>T \ECHR 'W' \ <244><233>PT [147]TR<255>SMISSI<223>. \ECHR ',' \ {19}I NE<252>[208][207][201]<239>KE \ECHR ' ' \ [147]RUN[204][179]'<242> E<229>CT<252> \ETOK 147 \ [204][147]P<249>NS A<242> UNIPUL<218> C \EJMP 19 \ OD[196]WI<226><240> [148]TR<255>SMISSI \ECHR 'N' \ <223>[204]{8}[179] W<220>L <247> PAID \ECHR 'A' \ [204] {19}GOOD LUCK [154][212]{24}" \ECHR 'V' \ECHR 'Y' \ECHR ' ' \ECHR 'H' \ECHR 'A' \ETWO 'V', 'E' \ECHR ' ' \ETWO 'B', 'E' \ETWO 'E', 'N' \ECHR ' ' \ECHR 'K' \ECHR 'E' \ECHR 'E' \ECHR 'P' \ETOK 195 \ETOK 147 \EJMP 19 \ETWO 'T', 'H' \ETWO 'A', 'R' \ECHR 'G' \ECHR 'O' \ECHR 'I' \ECHR 'D' \ECHR 'S' \ECHR ' ' \ECHR 'O' \ECHR 'F' \ECHR 'F' \ECHR ' ' \ETOK 179 \ECHR 'R' \ECHR ' ' \ECHR 'A' \ECHR 'S' \ECHR 'S' \ECHR ' ' \ETWO 'O', 'U' \ECHR 'T' \ECHR ' ' \ETWO 'I', 'N' \ECHR ' ' \ECHR 'D' \ECHR 'E' \ECHR 'E' \ECHR 'P' \ECHR ' ' \ECHR 'S' \ECHR 'P' \ECHR 'A' \ETWO 'C', 'E' \ECHR ' ' \ECHR 'F' \ETWO 'O', 'R' \ECHR ' ' \ETWO 'M', 'A' \ECHR 'N' \ECHR 'Y' \ECHR ' ' \ECHR 'Y' \ECHR 'E' \ETWO 'A', 'R' \ECHR 'S' \ECHR ' ' \ETWO 'N', 'O' \ECHR 'W' \ECHR '.' \ECHR ' ' \EJMP 19 \ECHR 'W' \ECHR 'E' \ECHR 'L' \ECHR 'L' \ECHR ' ' \ETOK 147 \ECHR 'S' \ETWO 'I', 'T' \ECHR 'U' \ECHR 'A' \ETWO 'T', 'I' \ETWO 'O', 'N' \ECHR ' ' \ECHR 'H' \ECHR 'A' \ECHR 'S' \ECHR ' ' \ECHR 'C' \ECHR 'H' \ETWO 'A', 'N' \ECHR 'G' \ETWO 'E', 'D' \ETOK 204 \ETWO 'O', 'U' \ECHR 'R' \ECHR ' ' \ECHR 'B' \ECHR 'O' \ECHR 'Y' \ECHR 'S' \ECHR ' ' \ETWO 'A', 'R' \ECHR 'E' \ECHR ' ' \ETWO 'R', 'E' \ECHR 'A' \ECHR 'D' \ECHR 'Y' \ECHR ' ' \ECHR 'F' \ETWO 'O', 'R' \ETOK 208 \ECHR 'P' \ECHR 'U' \ECHR 'S' \ECHR 'H' \ECHR ' ' \ECHR 'R' \ECHR 'I' \ECHR 'G' \ECHR 'H' \ECHR 'T' \ETOK 201 \ETOK 147 \ECHR 'H' \ECHR 'O' \ECHR 'M' \ECHR 'E' \ECHR ' ' \ECHR 'S' \ECHR 'Y' \ECHR 'S' \ECHR 'T' \ECHR 'E' \ECHR 'M' \ECHR ' ' \ECHR 'O' \ECHR 'F' \ECHR ' ' \ETWO 'T', 'H' \ECHR 'O' \ETWO 'S', 'E' \ECHR ' ' \ECHR 'M' \ECHR 'O' \ETWO 'T', 'H' \ETWO 'E', 'R' \ECHR 'S' \ETOK 204 \EJMP 24 \EJMP 9 \EJMP 30 \EJMP 29 \ECHR 'I' \EJMP 13 \ECHR ' ' \ECHR 'H' \ECHR 'A' \ETWO 'V', 'E' \ECHR ' ' \ECHR 'O' \ECHR 'B' \ECHR 'T' \ECHR 'A' \ETWO 'I', 'N' \ETOK 196 \ETOK 147 \ECHR 'D' \ECHR 'E' \ECHR 'F' \ETWO 'E', 'N' \ETWO 'C', 'E' \ECHR ' ' \ECHR 'P' \ETWO 'L', 'A' \ECHR 'N' \ECHR 'S' \ECHR ' ' \ECHR 'F' \ETWO 'O', 'R' \ECHR ' ' \ETWO 'T', 'H' \ECHR 'E' \ECHR 'I' \ECHR 'R' \ECHR ' ' \EJMP 19 \ECHR 'H' \ECHR 'I' \ETWO 'V', 'E' \ECHR ' ' \EJMP 19 \ECHR 'W' \ETWO 'O', 'R' \ECHR 'L' \ECHR 'D' \ECHR 'S' \ETOK 204 \ETOK 147 \ETWO 'B', 'E' \ETWO 'E', 'T' \ETWO 'L', 'E' \ECHR 'S' \ECHR ' ' \ECHR 'K' \ETWO 'N', 'O' \ECHR 'W' \ECHR ' ' \ECHR 'W' \ECHR 'E' \ECHR '`' \ETWO 'V', 'E' \ECHR ' ' \ECHR 'G' \ECHR 'O' \ECHR 'T' \ECHR ' ' \ETWO 'S', 'O' \ECHR 'M' \ECHR 'E' \ETWO 'T', 'H' \ETOK 195 \ECHR 'B' \ECHR 'U' \ECHR 'T' \ECHR ' ' \ETWO 'N', 'O' \ECHR 'T' \ECHR ' ' \ECHR 'W' \ECHR 'H' \ETWO 'A', 'T' \ETOK 204 \ECHR 'I' \ECHR 'F' \ECHR ' ' \EJMP 19 \ECHR 'I' \ECHR ' ' \ECHR 'T' \ETWO 'R', 'A' \ECHR 'N' \ECHR 'S' \ECHR 'M' \ETWO 'I', 'T' \ECHR ' ' \ETOK 147 \ECHR 'P' \ETWO 'L', 'A' \ECHR 'N' \ECHR 'S' \ETOK 201 \ETWO 'O', 'U' \ECHR 'R' \ECHR ' ' \ECHR 'B' \ECHR 'A' \ETWO 'S', 'E' \ECHR ' ' \ETWO 'O', 'N' \ECHR ' ' \EJMP 19 \ETWO 'B', 'I' \ETWO 'R', 'E' \ETWO 'R', 'A' \ECHR ' ' \ETWO 'T', 'H' \ECHR 'E' \ECHR 'Y' \ECHR '`' \ECHR 'L' \ECHR 'L' \ECHR ' ' \ETWO 'I', 'N' \ECHR 'T' \ETWO 'E', 'R' \ETWO 'C', 'E' \ECHR 'P' \ECHR 'T' \ECHR ' ' \ETOK 147 \ECHR 'T' \ECHR 'R' \ETWO 'A', 'N' \ECHR 'S' \ECHR 'M' \ECHR 'I' \ECHR 'S' \ECHR 'S' \ECHR 'I' \ETWO 'O', 'N' \ECHR '.' \ECHR ' ' \EJMP 19 \ECHR 'I' \ECHR ' ' \ECHR 'N' \ECHR 'E' \ETWO 'E', 'D' \ETOK 208 \ETOK 207 \ETOK 201 \ETWO 'M', 'A' \ECHR 'K' \ECHR 'E' \ECHR ' ' \ETOK 147 \ECHR 'R' \ECHR 'U' \ECHR 'N' \ETOK 204 \ETOK 179 \ECHR '`' \ETWO 'R', 'E' \ECHR ' ' \ECHR 'E' \ETWO 'L', 'E' \ECHR 'C' \ECHR 'T' \ETWO 'E', 'D' \ETOK 204 \ETOK 147 \ECHR 'P' \ETWO 'L', 'A' \ECHR 'N' \ECHR 'S' \ECHR ' ' \ECHR 'A' \ETWO 'R', 'E' \ECHR ' ' \ECHR 'U' \ECHR 'N' \ECHR 'I' \ECHR 'P' \ECHR 'U' \ECHR 'L' \ETWO 'S', 'E' \ECHR ' ' \ECHR 'C' \ECHR 'O' \ECHR 'D' \ETOK 196 \ECHR 'W' \ECHR 'I' \ETWO 'T', 'H' \ETWO 'I', 'N' \ECHR ' ' \ETOK 148 \ECHR 'T' \ECHR 'R' \ETWO 'A', 'N' \ECHR 'S' \ECHR 'M' \ECHR 'I' \ECHR 'S' \ECHR 'S' \ECHR 'I' \ETWO 'O', 'N' \ETOK 204 \EJMP 8 \ETOK 179 \ECHR ' ' \ECHR 'W' \ETWO 'I', 'L' \ECHR 'L' \ECHR ' ' \ETWO 'B', 'E' \ECHR ' ' \ECHR 'P' \ECHR 'A' \ECHR 'I' \ECHR 'D' \ETOK 204 \ECHR ' ' \ECHR ' ' \ECHR ' ' \ECHR ' ' \EJMP 19 \ECHR 'G' \ECHR 'O' \ECHR 'O' \ECHR 'D' \ECHR ' ' \ECHR 'L' \ECHR 'U' \ECHR 'C' \ECHR 'K' \ECHR ' ' \ETOK 154 \ETOK 212 \EJMP 24 \EQUB VE \ \EJMP 25 \ Token 223: "{incoming message screen, wait 2s} \EJMP 9 \ {clear screen} \EJMP 29 \ {tab 6, white, lower case in words} \EJMP 30 \ {white} \EJMP 8 \ {tab 6} \EJMP 14 \ {justify} \EJMP 13 \ {lower case} \EJMP 19 \ {single cap}WELL DONE {single cap} \ECHR 'W' \ COMMANDER.{cr} \ECHR 'E' \ {single cap}YOU HAVE SERVED US WELL \ECHR 'L' \ AND WE SHALL REMEMBER.{cr} \ECHR 'L' \ {single cap}WE DID NOT EXPECT THE \ECHR ' ' \ {single cap}THARGOIDS TO FIND OUT \ECHR 'D' \ ABOUT YOU.{cr} \ETWO 'O', 'N' \ {single cap}FOR THE MOMENT PLEASE \ECHR 'E' \ ACCEPT THIS {single cap}NAVY {standard \ECHR ' ' \ tokens, sentence case}EXTRA ENERGY \ETOK 154 \ UNIT{extended tokens} AS PAYMENT.{cr} \ETOK 204 \ {left align} \ETOK 179 \ {tab 6}{all caps} MESSAGE ENDS \ECHR ' ' \ {wait for key press}" \ECHR 'H' \ \ECHR 'A' \ Encoded as: "{25}{9}{29}{30}{8}{14}{13}{19}WELL D \ETWO 'V', 'E' \ <223>E [154][204][179] HA<250> <218>RV \ECHR ' ' \ [196]US WELL[178]WE SH<228>L <242>MEMB \ETWO 'S', 'E' \ <244>[204]WE DID <227>T EXPECT [147] \ECHR 'R' \ {19}<226><238>GOIDS[201]F<240>D <217>T \ECHR 'V' \ AB<217>T [179][204]F<253> [147]MOM \ETOK 196 \ <246>T P<229>A<218> AC<233>PT [148]{19} \ECHR 'U' \ NAVY {6}[114]{5} AS PAYM<246>T[212] \ECHR 'S' \ {24}" \ECHR ' ' \ECHR 'W' \ECHR 'E' \ECHR 'L' \ECHR 'L' \ETOK 178 \ECHR 'W' \ECHR 'E' \ECHR ' ' \ECHR 'S' \ECHR 'H' \ETWO 'A', 'L' \ECHR 'L' \ECHR ' ' \ETWO 'R', 'E' \ECHR 'M' \ECHR 'E' \ECHR 'M' \ECHR 'B' \ETWO 'E', 'R' \ETOK 204 \ECHR 'W' \ECHR 'E' \ECHR ' ' \ECHR 'D' \ECHR 'I' \ECHR 'D' \ECHR ' ' \ETWO 'N', 'O' \ECHR 'T' \ECHR ' ' \ECHR 'E' \ECHR 'X' \ECHR 'P' \ECHR 'E' \ECHR 'C' \ECHR 'T' \ECHR ' ' \ETOK 147 \EJMP 19 \ETWO 'T', 'H' \ETWO 'A', 'R' \ECHR 'G' \ECHR 'O' \ECHR 'I' \ECHR 'D' \ECHR 'S' \ETOK 201 \ECHR 'F' \ETWO 'I', 'N' \ECHR 'D' \ECHR ' ' \ETWO 'O', 'U' \ECHR 'T' \ECHR ' ' \ECHR 'A' \ECHR 'B' \ETWO 'O', 'U' \ECHR 'T' \ECHR ' ' \ETOK 179 \ETOK 204 \ECHR 'F' \ETWO 'O', 'R' \ECHR ' ' \ETOK 147 \ECHR 'M' \ECHR 'O' \ECHR 'M' \ETWO 'E', 'N' \ECHR 'T' \ECHR ' ' \ECHR 'P' \ETWO 'L', 'E' \ECHR 'A' \ETWO 'S', 'E' \ECHR ' ' \ECHR 'A' \ECHR 'C' \ETWO 'C', 'E' \ECHR 'P' \ECHR 'T' \ECHR ' ' \ETOK 148 \EJMP 19 \ECHR 'N' \ECHR 'A' \ECHR 'V' \ECHR 'Y' \ECHR ' ' \EJMP 6 \TOKN 114 \EJMP 5 \ECHR ' ' \ECHR 'A' \ECHR 'S' \ECHR ' ' \ECHR 'P' \ECHR 'A' \ECHR 'Y' \ECHR 'M' \ETWO 'E', 'N' \ECHR 'T' \ETOK 212 \EJMP 24 \EQUB VE \ --- And replaced by: --------------------------------> EQUB VE EQUB VE \ --- End of replacement ------------------------------> EQUB VE \ Token 224: "" \ \ Encoded as: "" ECHR 'S' \ Token 225: "SHREW" ECHR 'H' \ ETWO 'R', 'E' \ Encoded as: "SH<242>W" ECHR 'W' EQUB VE ETWO 'B', 'E' \ Token 226: "BEAST" ECHR 'A' \ ETWO 'S', 'T' \ Encoded as: "<247>A<222>" EQUB VE ECHR 'B' \ Token 227: "BISON" ECHR 'I' \ ECHR 'S' \ Encoded as: "BIS<223>" ETWO 'O', 'N' EQUB VE ECHR 'S' \ Token 228: "SNAKE" ECHR 'N' \ ECHR 'A' \ Encoded as: "SNAKE" ECHR 'K' ECHR 'E' EQUB VE ECHR 'W' \ Token 229: "WOLF" ECHR 'O' \ ECHR 'L' \ Encoded as: "WOLF" ECHR 'F' EQUB VE ETWO 'L', 'E' \ Token 230: "LEOPARD" ECHR 'O' \ ECHR 'P' \ Encoded as: "<229>OP<238>D" ETWO 'A', 'R' ECHR 'D' EQUB VE ECHR 'C' \ Token 231: "CAT" ETWO 'A', 'T' \ EQUB VE \ Encoded as: "C<245>" ECHR 'M' \ Token 232: "MONKEY" ETWO 'O', 'N' \ ECHR 'K' \ Encoded as: "M<223>KEY" ECHR 'E' ECHR 'Y' EQUB VE ECHR 'G' \ Token 233: "GOAT" ECHR 'O' \ ETWO 'A', 'T' \ Encoded as: "GO<245>" EQUB VE ECHR 'F' \ Token 234: "FISH" ECHR 'I' \ ECHR 'S' \ Encoded as: "FISH" ECHR 'H' EQUB VE ERND 15 \ Token 235: "[71-75] [66-70]" ECHR ' ' \ ERND 14 \ Encoded as: "[15?] [14?]" EQUB VE EJMP 17 \ Token 236: "{system name adjective} [225-229] ECHR ' ' \ [240-244]" ERND 29 \ ECHR ' ' \ Encoded as: "{17} [29?] [32?]" ERND 32 EQUB VE ETOK 175 \ Token 237: "ITS [76-80] [230-234] [240-244]" ERND 16 \ ECHR ' ' \ Encoded as: "[175][16?] [30?] [32?]" ERND 30 ECHR ' ' ERND 32 EQUB VE ERND 33 \ Token 238: "[245-249] [250-254]" ECHR ' ' \ ERND 34 \ Encoded as: "[33?] [34?]" EQUB VE ERND 15 \ Token 239: "[71-75] [66-70]" ECHR ' ' \ ERND 14 \ Encoded as: "[15?] [14?]" EQUB VE ECHR 'M' \ Token 240: "MEAT" ECHR 'E' \ ETWO 'A', 'T' \ Encoded as: "ME<245>" EQUB VE ECHR 'C' \ Token 241: "CUTLET" ECHR 'U' \ ECHR 'T' \ Encoded as: "CUTL<221>" ECHR 'L' ETWO 'E', 'T' EQUB VE ETWO 'S', 'T' \ Token 242: "STEAK" ECHR 'E' \ ECHR 'A' \ Encoded as: "<222>EAK" ECHR 'K' EQUB VE ECHR 'B' \ Token 243: "BURGERS" ECHR 'U' \ ECHR 'R' \ Encoded as: "BURG<244>S" ECHR 'G' ETWO 'E', 'R' ECHR 'S' EQUB VE ETWO 'S', 'O' \ Token 244: "SOUP" ECHR 'U' \ ECHR 'P' \ Encoded as: "<235>UP" EQUB VE ECHR 'I' \ Token 245: "ICE" ETWO 'C', 'E' \ EQUB VE \ Encoded as: "I<233>" ECHR 'M' \ Token 246: "MUD" ECHR 'U' \ ECHR 'D' \ Encoded as: "MUD" EQUB VE ECHR 'Z' \ Token 247: "ZERO-{single cap}G" ETWO 'E', 'R' \ ECHR 'O' \ Encoded as: "Z<244>O-{19}G" ECHR '-' EJMP 19 ECHR 'G' EQUB VE ECHR 'V' \ Token 248: "VACUUM" ECHR 'A' \ ECHR 'C' \ Encoded as: "VACUUM" ECHR 'U' ECHR 'U' ECHR 'M' EQUB VE EJMP 17 \ Token 249: "{system name adjective} ULTRA" ECHR ' ' \ ECHR 'U' \ Encoded as: "{17} ULT<248>" ECHR 'L' ECHR 'T' ETWO 'R', 'A' EQUB VE ECHR 'H' \ Token 250: "HOCKEY" ECHR 'O' \ ECHR 'C' \ Encoded as: "HOCKEY" ECHR 'K' ECHR 'E' ECHR 'Y' EQUB VE ECHR 'C' \ Token 251: "CRICKET" ECHR 'R' \ ECHR 'I' \ Encoded as: "CRICK<221>" ECHR 'C' ECHR 'K' ETWO 'E', 'T' EQUB VE ECHR 'K' \ Token 252: "KARATE" ETWO 'A', 'R' \ ETWO 'A', 'T' \ Encoded as: "K<238><245>E" ECHR 'E' EQUB VE ECHR 'P' \ Token 253: "POLO" ECHR 'O' \ ETWO 'L', 'O' \ Encoded as: "PO<224>" EQUB VE ECHR 'T' \ Token 254: "TENNIS" ETWO 'E', 'N' \ ECHR 'N' \ Encoded as: "T<246>NIS" ECHR 'I' ECHR 'S' EQUB VE EQUB VE \ Token 255: "" \ \ Encoded as: "" \ ****************************************************************************** \ \ Name: RUPLA \ Type: Variable \ Category: Text \ Summary: System numbers that have extended description overrides \ Deep dive: Extended system descriptions \ Extended text tokens \ The Constrictor mission \ \ ------------------------------------------------------------------------------ \ \ This table contains the extended token numbers to show as the specified \ system's extended description, if the criteria in the RUGAL table are met. \ \ The three variables work as follows: \ \ * The RUPLA table contains the system numbers \ \ * The RUGAL table contains the galaxy numbers and mission criteria \ \ * The RUTOK table contains the extended token to display instead of the \ normal extended description if the criteria in RUPLA and RUGAL are met \ \ See the PDESC routine for details of how extended system descriptions work. \ \ ****************************************************************************** .RUPLA EQUB 211 \ System 211, Galaxy 0 Teorge = Token 1 EQUB 150 \ System 150, Galaxy 0, Mission 1 Xeer = Token 2 EQUB 36 \ System 36, Galaxy 0, Mission 1 Reesdice = Token 3 EQUB 28 \ System 28, Galaxy 0, Mission 1 Arexe = Token 4 EQUB 253 \ System 253, Galaxy 1, Mission 1 Errius = Token 5 EQUB 79 \ System 79, Galaxy 1, Mission 1 Inbibe = Token 6 EQUB 53 \ System 53, Galaxy 1, Mission 1 Ausar = Token 7 EQUB 118 \ System 118, Galaxy 1, Mission 1 Usleri = Token 8 EQUB 100 \ System 100, Galaxy 2 Arredi = Token 9 EQUB 32 \ System 32, Galaxy 1, Mission 1 Bebege = Token 10 EQUB 68 \ System 68, Galaxy 1, Mission 1 Cearso = Token 11 EQUB 164 \ System 164, Galaxy 1, Mission 1 Dicela = Token 12 EQUB 220 \ System 220, Galaxy 1, Mission 1 Eringe = Token 13 EQUB 106 \ System 106, Galaxy 1, Mission 1 Gexein = Token 14 EQUB 16 \ System 16, Galaxy 1, Mission 1 Isarin = Token 15 EQUB 162 \ System 162, Galaxy 1, Mission 1 Letibema = Token 16 EQUB 3 \ System 3, Galaxy 1, Mission 1 Maisso = Token 17 EQUB 107 \ System 107, Galaxy 1, Mission 1 Onen = Token 18 EQUB 26 \ System 26, Galaxy 1, Mission 1 Ramaza = Token 19 EQUB 192 \ System 192, Galaxy 1, Mission 1 Sosole = Token 20 EQUB 184 \ System 184, Galaxy 1, Mission 1 Tivere = Token 21 EQUB 5 \ System 5, Galaxy 1, Mission 1 Veriar = Token 22 EQUB 101 \ System 101, Galaxy 2, Mission 1 Xeveon = Token 23 EQUB 193 \ System 193, Galaxy 1, Mission 1 Orarra = Token 24 EQUB 41 \ System 41, Galaxy 2 Anreer = Token 25 IF _SOURCE_DISC EQUB 7 \ System 7, Galaxy 0 Lave = Token 26 ELIF _EXECUTIVE EQUB 7 \ System 7, Galaxy 0 Lave = Token 26 EQUB 46 \ System 46, Galaxy 0 Riedquat = Token 27 ENDIF \ ****************************************************************************** \ \ Name: RUGAL \ Type: Variable \ Category: Text \ Summary: The criteria for systems with extended description overrides \ Deep dive: Extended system descriptions \ Extended text tokens \ The Constrictor mission \ \ ------------------------------------------------------------------------------ \ \ This table contains the criteria for printing an extended description override \ for a system. The galaxy number is in bits 0-6, while bit 7 determines whether \ to show this token during mission 1 only (bit 7 is clear, i.e. a value of &0x \ in the table below), or all of the time (bit 7 is set, i.e. a value of &8x in \ the table below). \ \ In other words, Teorge, Arredi, Anreer and Lave have extended description \ overrides that are always shown, while the rest only appear when mission 1 is \ in progress. \ \ The three variables work as follows: \ \ * The RUPLA table contains the system numbers \ \ * The RUGAL table contains the galaxy numbers and mission criteria \ \ * The RUTOK table contains the extended token to display instead of the \ normal extended description if the criteria in RUPLA and RUGAL are met \ \ See the PDESC routine for details of how extended system descriptions work. \ \ ****************************************************************************** .RUGAL EQUB &80 \ System 211, Galaxy 0 Teorge = Token 1 EQUB &00 \ System 150, Galaxy 0, Mission 1 Xeer = Token 2 EQUB &00 \ System 36, Galaxy 0, Mission 1 Reesdice = Token 3 EQUB &00 \ System 28, Galaxy 0, Mission 1 Arexe = Token 4 EQUB &01 \ System 253, Galaxy 1, Mission 1 Errius = Token 5 EQUB &01 \ System 79, Galaxy 1, Mission 1 Inbibe = Token 6 EQUB &01 \ System 53, Galaxy 1, Mission 1 Ausar = Token 7 EQUB &01 \ System 118, Galaxy 1, Mission 1 Usleri = Token 8 EQUB &82 \ System 100, Galaxy 2 Arredi = Token 9 EQUB &01 \ System 32, Galaxy 1, Mission 1 Bebege = Token 10 EQUB &01 \ System 68, Galaxy 1, Mission 1 Cearso = Token 11 EQUB &01 \ System 164, Galaxy 1, Mission 1 Dicela = Token 12 EQUB &01 \ System 220, Galaxy 1, Mission 1 Eringe = Token 13 EQUB &01 \ System 106, Galaxy 1, Mission 1 Gexein = Token 14 EQUB &01 \ System 16, Galaxy 1, Mission 1 Isarin = Token 15 EQUB &01 \ System 162, Galaxy 1, Mission 1 Letibema = Token 16 EQUB &01 \ System 3, Galaxy 1, Mission 1 Maisso = Token 17 EQUB &01 \ System 107, Galaxy 1, Mission 1 Onen = Token 18 EQUB &01 \ System 26, Galaxy 1, Mission 1 Ramaza = Token 19 EQUB &01 \ System 192, Galaxy 1, Mission 1 Sosole = Token 20 EQUB &01 \ System 184, Galaxy 1, Mission 1 Tivere = Token 21 EQUB &01 \ System 5, Galaxy 1, Mission 1 Veriar = Token 22 EQUB &02 \ System 101, Galaxy 2, Mission 1 Xeveon = Token 23 EQUB &01 \ System 193, Galaxy 1, Mission 1 Orarra = Token 24 EQUB &82 \ System 41, Galaxy 2 Anreer = Token 25 IF _SOURCE_DISC EQUB &80 \ System 7, Galaxy 0 Lave = Token 26 ELIF _EXECUTIVE EQUB &80 \ System 7, Galaxy 0 Lave = Token 26 EQUB &80 \ System 7, Galaxy 0 Riedquat = Token 27 ENDIF \ ****************************************************************************** \ \ Name: RUTOK \ Type: Variable \ Category: Text \ Summary: The second extended token table for recursive tokens 0-26 (DETOK3) \ Deep dive: Extended system descriptions \ Extended text tokens \ The Constrictor mission \ \ ------------------------------------------------------------------------------ \ \ Contains the tokens for extended description overrides of systems that match \ the system number in RUPLA and the conditions in RUGAL. \ \ The three variables work as follows: \ \ * The RUPLA table contains the system numbers \ \ * The RUGAL table contains the galaxy numbers and mission criteria \ \ * The RUTOK table contains the extended token to display instead of the \ normal extended description if the criteria in RUPLA and RUGAL are met \ \ See the PDESC routine for details of how extended system descriptions work. \ \ The encodings shown for each extended text token use the following notation: \ \ {n} Jump token n = 1 to 31 \ [n?] Random token n = 91 to 128 \ [n] Recursive token n = 129 to 215 \ Two-letter token n = 215 to 255 \ \ ****************************************************************************** .RUTOK EQUB VE \ Token 0: "" \ \ Encoded as: "" \ --- Mod: Code removed for two-player Elite: ---------> \ETOK 147 \ Token 1: "THE COLONISTS HERE HAVE VIOLATED \ECHR 'C' \ {sentence case} INTERGALACTIC CLONING \ECHR 'O' \ PROTOCOL{lower case} AND SHOULD BE \ETWO 'L', 'O' \ AVOIDED" \ECHR 'N' \ \ECHR 'I' \ Encoded as: "[147]CO<224>NI<222>S HE<242> HA<250> \ETWO 'S', 'T' \ VIOL<245><252>{2} <240>T<244>G<228>AC \ECHR 'S' \ <251>C C<224>N[195]PROTOCOL{13}[178]SH \ECHR ' ' \ <217>LD <247> AVOID<252>" \ECHR 'H' \ECHR 'E' \ETWO 'R', 'E' \ECHR ' ' \ECHR 'H' \ECHR 'A' \ETWO 'V', 'E' \ECHR ' ' \ECHR 'V' \ECHR 'I' \ECHR 'O' \ECHR 'L' \ETWO 'A', 'T' \ETWO 'E', 'D' \EJMP 2 \ECHR ' ' \ETWO 'I', 'N' \ECHR 'T' \ETWO 'E', 'R' \ECHR 'G' \ETWO 'A', 'L' \ECHR 'A' \ECHR 'C' \ETWO 'T', 'I' \ECHR 'C' \ECHR ' ' \ECHR 'C' \ETWO 'L', 'O' \ECHR 'N' \ETOK 195 \ECHR 'P' \ECHR 'R' \ECHR 'O' \ECHR 'T' \ECHR 'O' \ECHR 'C' \ECHR 'O' \ECHR 'L' \EJMP 13 \ETOK 178 \ECHR 'S' \ECHR 'H' \ETWO 'O', 'U' \ECHR 'L' \ECHR 'D' \ECHR ' ' \ETWO 'B', 'E' \ECHR ' ' \ECHR 'A' \ECHR 'V' \ECHR 'O' \ECHR 'I' \ECHR 'D' \ETWO 'E', 'D' \EQUB VE \ \ETOK 147 \ Token 2: "THE CONSTRICTOR WAS LAST SEEN AT \ECHR 'C' \ {single cap}REESDICE, {single cap} \ETWO 'O', 'N' \ COMMANDER" \ETWO 'S', 'T' \ \ECHR 'R' \ Encoded as: "[147]C<223><222>RICT<253> [203]<242> \ECHR 'I' \ <237><241><233>, [154]" \ECHR 'C' \ECHR 'T' \ETWO 'O', 'R' \ECHR ' ' \ETOK 203 \ETWO 'R', 'E' \ETWO 'E', 'S' \ETWO 'D', 'I' \ETWO 'C', 'E' \ECHR ',' \ECHR ' ' \ETOK 154 \EQUB VE \ \ECHR 'A' \ Token 3: "A [130-134] LOOKING SHIP LEFT HERE A \ECHR ' ' \ WHILE BACK. LOOKED BOUND FOR AREXE" \ERND 23 \ \ECHR ' ' \ Encoded as: "A [23?] <224>OK[195][207] <229>FT HE \ETWO 'L', 'O' \ <242>[208]WHI<229> BACK. LOOK[196]B \ECHR 'O' \ <217>ND F<253> <238>E<230>" \ECHR 'K' \ETOK 195 \ETOK 207 \ECHR ' ' \ETWO 'L', 'E' \ECHR 'F' \ECHR 'T' \ECHR ' ' \ECHR 'H' \ECHR 'E' \ETWO 'R', 'E' \ETOK 208 \ECHR 'W' \ECHR 'H' \ECHR 'I' \ETWO 'L', 'E' \ECHR ' ' \ECHR 'B' \ECHR 'A' \ECHR 'C' \ECHR 'K' \ECHR '.' \ECHR ' ' \ECHR 'L' \ECHR 'O' \ECHR 'O' \ECHR 'K' \ETOK 196 \ECHR 'B' \ETWO 'O', 'U' \ECHR 'N' \ECHR 'D' \ECHR ' ' \ECHR 'F' \ETWO 'O', 'R' \ECHR ' ' \ETWO 'A', 'R' \ECHR 'E' \ETWO 'X', 'E' \EQUB VE \ \ECHR 'Y' \ Token 4: "YEP, A [130-134] NEW SHIP HAD A \ECHR 'E' \ GALACTIC HYPERDRIVE FITTED HERE. USED \ECHR 'P' \ IT TOO" \ECHR ',' \ \ETOK 208 \ Encoded as: "YEP,[208][23?][210][207] HAD[208]G \ERND 23 \ <228>AC<251>C HYP<244>DRI<250> F<219> \ETOK 210 \ T[196]HE<242>. <236>[196]<219> TOO" \ETOK 207 \ECHR ' ' \ECHR 'H' \ECHR 'A' \ECHR 'D' \ETOK 208 \ECHR 'G' \ETWO 'A', 'L' \ECHR 'A' \ECHR 'C' \ETWO 'T', 'I' \ECHR 'C' \ECHR ' ' \ECHR 'H' \ECHR 'Y' \ECHR 'P' \ETWO 'E', 'R' \ECHR 'D' \ECHR 'R' \ECHR 'I' \ETWO 'V', 'E' \ECHR ' ' \ECHR 'F' \ETWO 'I', 'T' \ECHR 'T' \ETOK 196 \ECHR 'H' \ECHR 'E' \ETWO 'R', 'E' \ECHR '.' \ECHR ' ' \ETWO 'U', 'S' \ETOK 196 \ETWO 'I', 'T' \ECHR ' ' \ECHR 'T' \ECHR 'O' \ECHR 'O' \EQUB VE \ \ETOK 148 \ Token 5: "THIS [130-134] SHIP DEHYPED HERE FROM \ECHR ' ' \ NOWHERE, SUN SKIMMED AND JUMPED. I HEAR \ERND 23 \ IT WENT TO INBIBE" \ECHR ' ' \ \ETOK 207 \ Encoded as: "[148] [23?] [207] DEHYP[196]HE<242> FRO \ECHR ' ' \ M <227>WHE<242>, SUN SKIMM<252>[178]JUM \ECHR 'D' \ P<252>. I HE<238> <219> W<246>T[201] \ECHR 'E' \ <240><234><247>" \ECHR 'H' \ECHR 'Y' \ECHR 'P' \ETOK 196 \ECHR 'H' \ECHR 'E' \ETWO 'R', 'E' \ECHR ' ' \ECHR 'F' \ECHR 'R' \ECHR 'O' \ECHR 'M' \ECHR ' ' \ETWO 'N', 'O' \ECHR 'W' \ECHR 'H' \ECHR 'E' \ETWO 'R', 'E' \ECHR ',' \ECHR ' ' \ECHR 'S' \ECHR 'U' \ECHR 'N' \ECHR ' ' \ECHR 'S' \ECHR 'K' \ECHR 'I' \ECHR 'M' \ECHR 'M' \ETWO 'E', 'D' \ETOK 178 \ECHR 'J' \ECHR 'U' \ECHR 'M' \ECHR 'P' \ETWO 'E', 'D' \ECHR '.' \ECHR ' ' \ECHR 'I' \ECHR ' ' \ECHR 'H' \ECHR 'E' \ETWO 'A', 'R' \ECHR ' ' \ETWO 'I', 'T' \ECHR ' ' \ECHR 'W' \ETWO 'E', 'N' \ECHR 'T' \ETOK 201 \ETWO 'I', 'N' \ETWO 'B', 'I' \ETWO 'B', 'E' \EQUB VE \ \ERND 24 \ Token 6: "[91-95] SHIP WENT FOR ME AT AUSAR. MY \ECHR ' ' \ LASERS DIDN'T EVEN SCRATCH THE [91-95]" \ETOK 207 \ \ECHR ' ' \ Encoded as: "[24?] [207] W<246>T F<253> ME <245> \ECHR 'W' \ A<236><238>. MY <249>S<244>S DIDN'T EV \ETWO 'E', 'N' \ <246> SC<248>TCH [147][24?]" \ECHR 'T' \ECHR ' ' \ECHR 'F' \ETWO 'O', 'R' \ECHR ' ' \ECHR 'M' \ECHR 'E' \ECHR ' ' \ETWO 'A', 'T' \ECHR ' ' \ECHR 'A' \ETWO 'U', 'S' \ETWO 'A', 'R' \ECHR '.' \ECHR ' ' \ECHR 'M' \ECHR 'Y' \ECHR ' ' \ETWO 'L', 'A' \ECHR 'S' \ETWO 'E', 'R' \ECHR 'S' \ECHR ' ' \ECHR 'D' \ECHR 'I' \ECHR 'D' \ECHR 'N' \ECHR '`' \ECHR 'T' \ECHR ' ' \ECHR 'E' \ECHR 'V' \ETWO 'E', 'N' \ECHR ' ' \ECHR 'S' \ECHR 'C' \ETWO 'R', 'A' \ECHR 'T' \ECHR 'C' \ECHR 'H' \ECHR ' ' \ETOK 147 \ERND 24 \EQUB VE \ \ECHR 'O' \ Token 7: "OH DEAR ME YES. A FRIGHTFUL ROGUE WITH \ECHR 'H' \ WHAT I BELIEVE YOU PEOPLE CALL A LEAD \ECHR ' ' \ POSTERIOR SHOT UP LOTS OF THOSE BEASTLY \ECHR 'D' \ PIRATES AND WENT TO USLERI" \ECHR 'E' \ \ETWO 'A', 'R' \ Encoded as: "OH DE<238> ME Y<237>.[208]FRIGHTFUL ROG \ECHR ' ' \ UE WI<226> WH<245> I <247>LIE<250> \ECHR 'M' \ [179] PEOP<229> C<228>L[208]<229>AD PO \ECHR 'E' \ <222><244>I<253> SHOT UP <224>TS OF \ECHR ' ' \ <226>O<218> <247>A<222>LY PI<248>T \ECHR 'Y' \ <237>[178]W<246>T[201]<236><229>RI" \ETWO 'E', 'S' \ECHR '.' \ETOK 208 \ECHR 'F' \ECHR 'R' \ECHR 'I' \ECHR 'G' \ECHR 'H' \ECHR 'T' \ECHR 'F' \ECHR 'U' \ECHR 'L' \ECHR ' ' \ECHR 'R' \ECHR 'O' \ECHR 'G' \ECHR 'U' \ECHR 'E' \ECHR ' ' \ECHR 'W' \ECHR 'I' \ETWO 'T', 'H' \ECHR ' ' \ECHR 'W' \ECHR 'H' \ETWO 'A', 'T' \ECHR ' ' \ECHR 'I' \ECHR ' ' \ETWO 'B', 'E' \ECHR 'L' \ECHR 'I' \ECHR 'E' \ETWO 'V', 'E' \ECHR ' ' \ETOK 179 \ECHR ' ' \ECHR 'P' \ECHR 'E' \ECHR 'O' \ECHR 'P' \ETWO 'L', 'E' \ECHR ' ' \ECHR 'C' \ETWO 'A', 'L' \ECHR 'L' \ETOK 208 \ETWO 'L', 'E' \ECHR 'A' \ECHR 'D' \ECHR ' ' \ECHR 'P' \ECHR 'O' \ETWO 'S', 'T' \ETWO 'E', 'R' \ECHR 'I' \ETWO 'O', 'R' \ECHR ' ' \ECHR 'S' \ECHR 'H' \ECHR 'O' \ECHR 'T' \ECHR ' ' \ECHR 'U' \ECHR 'P' \ECHR ' ' \ETWO 'L', 'O' \ECHR 'T' \ECHR 'S' \ECHR ' ' \ECHR 'O' \ECHR 'F' \ECHR ' ' \ETWO 'T', 'H' \ECHR 'O' \ETWO 'S', 'E' \ECHR ' ' \ETWO 'B', 'E' \ECHR 'A' \ETWO 'S', 'T' \ECHR 'L' \ECHR 'Y' \ECHR ' ' \ECHR 'P' \ECHR 'I' \ETWO 'R', 'A' \ECHR 'T' \ETWO 'E', 'S' \ETOK 178 \ECHR 'W' \ETWO 'E', 'N' \ECHR 'T' \ETOK 201 \ETWO 'U', 'S' \ETWO 'L', 'E' \ECHR 'R' \ECHR 'I' \EQUB VE \ \ETOK 179 \ Token 8: "YOU CAN TACKLE THE [170-174] [91-95] \ECHR ' ' \ IF YOU LIKE. HE'S AT ORARRA" \ECHR 'C' \ \ETWO 'A', 'N' \ Encoded as: "[179] C<255> TACK<229> [147][13?] [24?] \ECHR ' ' \ IF [179] LIKE. HE'S <245> <253><238> \ECHR 'T' \ <248>" \ECHR 'A' \ECHR 'C' \ECHR 'K' \ETWO 'L', 'E' \ECHR ' ' \ETOK 147 \ERND 13 \ECHR ' ' \ERND 24 \ECHR ' ' \ECHR 'I' \ECHR 'F' \ECHR ' ' \ETOK 179 \ECHR ' ' \ECHR 'L' \ECHR 'I' \ECHR 'K' \ECHR 'E' \ECHR '.' \ECHR ' ' \ECHR 'H' \ECHR 'E' \ECHR '`' \ECHR 'S' \ECHR ' ' \ETWO 'A', 'T' \ECHR ' ' \ETWO 'O', 'R' \ETWO 'A', 'R' \ETWO 'R', 'A' \EQUB VE \ \EJMP 1 \ Token 9: "{all caps}COMING SOON: ELITE II" \ECHR 'C' \ \ECHR 'O' \ Encoded as: "{1}COM[195]<235><223>: EL<219>E II" \ECHR 'M' \ETOK 195 \ETWO 'S', 'O' \ETWO 'O', 'N' \ECHR ':' \ECHR ' ' \ECHR 'E' \ECHR 'L' \ETWO 'I', 'T' \ECHR 'E' \ECHR ' ' \ECHR 'I' \ECHR 'I' \EQUB VE \ --- And replaced by: --------------------------------> EQUB VE EQUB VE EQUB VE EQUB VE EQUB VE EQUB VE EQUB VE EQUB VE EQUB VE \ --- End of replacement ------------------------------> ERND 25 \ Token 10: "[106-110]" EQUB VE \ \ Encoded as: "[25?]" ERND 25 \ Token 11: "[106-110]" EQUB VE \ \ Encoded as: "[25?]" ERND 25 \ Token 12: "[106-110]" EQUB VE \ \ Encoded as: "[25?]" ERND 25 \ Token 13: "[106-110]" EQUB VE \ \ Encoded as: "[25?]" ERND 25 \ Token 14: "[106-110]" EQUB VE \ \ Encoded as: "[25?]" ERND 25 \ Token 15: "[106-110]" EQUB VE \ \ Encoded as: "[25?]" ERND 25 \ Token 16: "[106-110]" EQUB VE \ \ Encoded as: "[25?]" ERND 25 \ Token 17: "[106-110]" EQUB VE \ \ Encoded as: "[25?]" ERND 25 \ Token 18: "[106-110]" EQUB VE \ \ Encoded as: "[25?]" ERND 25 \ Token 19: "[106-110]" EQUB VE \ \ Encoded as: "[25?]" ERND 25 \ Token 20: "[106-110]" EQUB VE \ \ Encoded as: "[25?]" ERND 25 \ Token 21: "[106-110]" EQUB VE \ \ Encoded as: "[25?]" ERND 25 \ Token 22: "[106-110]" EQUB VE \ \ Encoded as: "[25?]" \ --- Mod: Code removed for two-player Elite: ---------> \ECHR 'B' \ Token 23: "BOY ARE YOU IN THE WRONG GALAXY!" \ECHR 'O' \ \ECHR 'Y' \ Encoded as: "BOY A<242> [179] <240> [147]WR<223>G G \ECHR ' ' \ <228>AXY!" \ECHR 'A' \ETWO 'R', 'E' \ECHR ' ' \ETOK 179 \ECHR ' ' \ETWO 'I', 'N' \ECHR ' ' \ETOK 147 \ECHR 'W' \ECHR 'R' \ETWO 'O', 'N' \ECHR 'G' \ECHR ' ' \ECHR 'G' \ETWO 'A', 'L' \ECHR 'A' \ECHR 'X' \ECHR 'Y' \ECHR '!' \EQUB VE \ \ETWO 'T', 'H' \ Token 24: "THERE'S A REAL [91-95] PIRATE OUT \ETWO 'E', 'R' \ THERE" \ECHR 'E' \ \ECHR '`' \ Encoded as: "<226><244>E'S[208]<242><228> [24?] PI \ECHR 'S' \ <248>TE <217>T <226><244>E" \ETOK 208 \ETWO 'R', 'E' \ETWO 'A', 'L' \ECHR ' ' \ERND 24 \ECHR ' ' \ECHR 'P' \ECHR 'I' \ETWO 'R', 'A' \ECHR 'T' \ECHR 'E' \ECHR ' ' \ETWO 'O', 'U' \ECHR 'T' \ECHR ' ' \ETWO 'T', 'H' \ETWO 'E', 'R' \ECHR 'E' \EQUB VE \ \ETOK 147 \ Token 25: "THE INHABITANTS OF [86-90] ARE SO \ETOK 193 \ AMAZINGLY PRIMITIVE THAT THEY STILL \ECHR 'S' \ THINK {single cap}***** ****** IS 3D" \ECHR ' ' \ \ECHR 'O' \ Encoded as: "[147][193]S OF [18?] A<242> <235> A \ECHR 'F' \ <239>Z<240>GLY PRIMI<251><250> <226> \ECHR ' ' \ <245> <226>EY <222><220>L <226><240>K \ERND 18 \ {19}***** ******[202] 3D" \ECHR ' ' \ECHR 'A' \ETWO 'R', 'E' \ECHR ' ' \ETWO 'S', 'O' \ECHR ' ' \ECHR 'A' \ETWO 'M', 'A' \ECHR 'Z' \ETWO 'I', 'N' \ECHR 'G' \ECHR 'L' \ECHR 'Y' \ECHR ' ' \ECHR 'P' \ECHR 'R' \ECHR 'I' \ECHR 'M' \ECHR 'I' \ETWO 'T', 'I' \ETWO 'V', 'E' \ECHR ' ' \ETWO 'T', 'H' \ETWO 'A', 'T' \ECHR ' ' \ETWO 'T', 'H' \ECHR 'E' \ECHR 'Y' \ECHR ' ' \ETWO 'S', 'T' \ETWO 'I', 'L' \ECHR 'L' \ECHR ' ' \ETWO 'T', 'H' \ETWO 'I', 'N' \ECHR 'K' \ECHR ' ' \EJMP 19 \ECHR '*' \ECHR '*' \ECHR '*' \ECHR '*' \ECHR '*' \ECHR ' ' \ECHR '*' \ECHR '*' \ECHR '*' \ECHR '*' \ECHR '*' \ECHR '*' \ETOK 202 \ECHR ' ' \ECHR '3' \ECHR 'D' \EQUB VE \ --- And replaced by: --------------------------------> EQUB VE EQUB VE EQUB VE \ --- End of replacement ------------------------------> IF _SOURCE_DISC EJMP 2 \ Token 26: "{sentence case}BITS'N PIECES - END OF ECHR 'B' \ PART 1" ECHR 'I' \ ECHR 'T' \ Encoded as: "{2}BITS'N PIECES - END OF PART 1" ECHR 'S' ECHR '`' ECHR 'N' ECHR ' ' ECHR 'P' ECHR 'I' ECHR 'E' ECHR 'C' ECHR 'E' ECHR 'S' ECHR ' ' ECHR '-' ECHR ' ' ECHR 'E' ECHR 'N' ECHR 'D' ECHR ' ' ECHR 'O' ECHR 'F' ECHR ' ' ECHR 'P' ECHR 'A' ECHR 'R' ECHR 'T' ECHR ' ' ECHR '1' EQUB VE ELIF _EXECUTIVE ETWO 'T', 'H' \ Token 26: "THIS MESSAGE IS AVAILABLE ONLY ON THE ECHR 'I' \ EXECUTIVE VERSION OF THIS PROGRAM" ECHR 'S' \ ECHR ' ' \ Encoded as: "<226>IS M<237>SA<231>[202]AVAI<249>B ECHR 'M' \ <229> <223>LY <223> [147]E<230>CU<251> ETWO 'E', 'S' \ <250> <250>RSI<223> OF <226>IS PROGRAM" ECHR 'S' ECHR 'A' ETWO 'G', 'E' ETOK 202 ECHR 'A' ECHR 'V' ECHR 'A' ECHR 'I' ETWO 'L', 'A' ECHR 'B' ETWO 'L', 'E' ECHR ' ' ETWO 'O', 'N' ECHR 'L' ECHR 'Y' ECHR ' ' ETWO 'O', 'N' ECHR ' ' ETOK 147 ECHR 'E' ETWO 'X', 'E' ECHR 'C' ECHR 'U' ETWO 'T', 'I' ETWO 'V', 'E' ECHR ' ' ETWO 'V', 'E' ECHR 'R' ECHR 'S' ECHR 'I' ETWO 'O', 'N' ECHR ' ' ECHR 'O' ECHR 'F' ECHR ' ' ETWO 'T', 'H' ECHR 'I' ECHR 'S' ECHR ' ' ECHR 'P' ECHR 'R' ECHR 'O' ECHR 'G' ECHR 'R' ECHR 'A' ECHR 'M' EQUB VE ENDIF IF _EXECUTIVE ETWO 'O', 'N' \ Token 27: "ONLY THIS EXECUTIVE VERSION HAS THE @ ECHR 'L' \ TOGGLE" ECHR 'Y' \ ECHR ' ' \ Encoded as: "<223>LY [148]E<230>CU<251><250> <250>RS ETOK 148 \ I<223> HAS [147]@ TOGG<229>" ECHR 'E' ETWO 'X', 'E' ECHR 'C' ECHR 'U' ETWO 'T', 'I' ETWO 'V', 'E' ECHR ' ' ETWO 'V', 'E' ECHR 'R' ECHR 'S' ECHR 'I' ETWO 'O', 'N' ECHR ' ' ECHR 'H' ECHR 'A' ECHR 'S' ECHR ' ' ETOK 147 ECHR '@' ECHR ' ' ECHR 'T' ECHR 'O' ECHR 'G' ECHR 'G' ETWO 'L', 'E' EQUB VE ENDIF \ ****************************************************************************** \ \ Name: MTIN \ Type: Variable \ Category: Text \ Summary: Lookup table for random tokens in the extended token table (0-37) \ Deep dive: Extended text tokens \ \ ------------------------------------------------------------------------------ \ \ The ERND token type, which is part of the extended token system, takes an \ argument between 0 and 37, and returns a randomly chosen token in the range \ specified in this table. This is used to generate the extended description of \ each system. \ \ For example, the entry at position 13 in this table (counting from 0) is 66, \ so ERND 14 will expand into a random token in the range 66-70, i.e. one of \ "JUICE", "BRANDY", "WATER", "BREW" and "GARGLE BLASTERS". \ \ ****************************************************************************** .MTIN EQUB 16 \ Token 0: a random extended token between 16 and 20 EQUB 21 \ Token 1: a random extended token between 21 and 25 EQUB 26 \ Token 2: a random extended token between 26 and 30 EQUB 31 \ Token 3: a random extended token between 31 and 35 EQUB 155 \ Token 4: a random extended token between 155 and 159 EQUB 160 \ Token 5: a random extended token between 160 and 164 EQUB 46 \ Token 6: a random extended token between 46 and 50 EQUB 165 \ Token 7: a random extended token between 165 and 169 EQUB 36 \ Token 8: a random extended token between 36 and 40 EQUB 41 \ Token 9: a random extended token between 41 and 45 EQUB 61 \ Token 10: a random extended token between 61 and 65 EQUB 51 \ Token 11: a random extended token between 51 and 55 EQUB 56 \ Token 12: a random extended token between 56 and 60 EQUB 170 \ Token 13: a random extended token between 170 and 174 EQUB 66 \ Token 14: a random extended token between 66 and 70 EQUB 71 \ Token 15: a random extended token between 71 and 75 EQUB 76 \ Token 16: a random extended token between 76 and 80 EQUB 81 \ Token 17: a random extended token between 81 and 85 EQUB 86 \ Token 18: a random extended token between 86 and 90 EQUB 140 \ Token 19: a random extended token between 140 and 144 EQUB 96 \ Token 20: a random extended token between 96 and 100 EQUB 101 \ Token 21: a random extended token between 101 and 105 EQUB 135 \ Token 22: a random extended token between 135 and 139 EQUB 130 \ Token 23: a random extended token between 130 and 134 EQUB 91 \ Token 24: a random extended token between 91 and 95 EQUB 106 \ Token 25: a random extended token between 106 and 110 EQUB 180 \ Token 26: a random extended token between 180 and 184 EQUB 185 \ Token 27: a random extended token between 185 and 189 EQUB 190 \ Token 28: a random extended token between 190 and 194 EQUB 225 \ Token 29: a random extended token between 225 and 229 EQUB 230 \ Token 30: a random extended token between 230 and 234 EQUB 235 \ Token 31: a random extended token between 235 and 239 EQUB 240 \ Token 32: a random extended token between 240 and 244 EQUB 245 \ Token 33: a random extended token between 245 and 249 EQUB 250 \ Token 34: a random extended token between 250 and 254 EQUB 115 \ Token 35: a random extended token between 115 and 119 EQUB 120 \ Token 36: a random extended token between 120 and 124 EQUB 125 \ Token 37: a random extended token between 125 and 129 \ ****************************************************************************** \ \ Name: COLD \ Type: Subroutine \ Category: Loader \ Summary: Copy the recursive tokens and ship blueprints to their correct \ locations \ \ ****************************************************************************** .COLD \ First we copy the 4 pages of recursive tokens from F% \ to QQ18 LDA #LO(F%) \ Set V(1 0) = F% STA V LDA #HI(F%) STA V+1 LDA #LO(QQ18) \ Set SC(1 0) = QQ18 STA SC LDA #HI(QQ18) STA SC+1 LDX #4 \ Set X = 4 to act as a counter for copying 4 pages JSR mvblock \ Call mvblock to copy the recursive tokens \ And then we copy the &22 pages of ship blueprints from \ F% + &0400 to D% \ --- Mod: Code removed for two-player Elite: ---------> \ And then we copy the &22 pages of ship blueprints from \ F% + &0400 to D% \ --- And replaced by: --------------------------------> \ And then we copy the &28 pages of ship blueprints from \ F% + &0400 to D% \ --- End of replacement ------------------------------> LDA #LO(F%) \ Set V(1 0) = F% + &0400 STA V LDA #HI(F%)+4 STA V+1 LDA #LO(D%) \ Set SC(1 0) = D% STA SC LDA #HI(D%) STA SC+1 \ --- Mod: Code removed for two-player Elite: ---------> \LDX #&22 \ Set X = &22 to act as a counter for copying &22 pages \ --- And replaced by: --------------------------------> LDX #&28 \ Set X = &28 to act as a counter for copying &28 pages \ --- End of replacement ------------------------------> \ Fall through into mvblock to copy the ship blueprints .mvblock LDY #0 \ Set Y = 0 to count through the bytes in each page .mvbllop LDA (V),Y \ Copy the Y-th byte of V(1 0) to the Y-th byte of STA (SC),Y \ SC(1 0) INY \ Increment the byte counter to point to the next byte BNE mvbllop \ Loop back to mvbllop until we have copied a whole page INC V+1 \ Increment the high byte of V(1 0) to point to the next \ page to copy from INC SC+1 \ Increment the high byte of SC(1 0) to point to the \ next page to copy into DEX \ Decrement the page counter in X BNE mvbllop \ Loop back to copy the next page until we have copied \ all of them RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: PrintOptionA \ Type: Subroutine \ Category: Two-player Elite \ Summary: Print configuration option A \ \ ****************************************************************************** .PrintOptionA ASL A \ Set Y = A * 2 TAY LDA optionJumpTable,Y \ Set the address of the JSR at opta1 to the Y-th STA opta1+1 \ entry, incrementing Y to point to the next entry INY LDA optionJumpTable,Y STA opta1+2 .opta1 JMP &FFFF \ Call the routine to print this option and return from \ the subroutine using a tail call \ ****************************************************************************** \ \ Name: optionJumpTable \ Type: Variable \ Category: Two-player Elite \ Summary: A jump table for printing configuration options \ \ ****************************************************************************** .optionJumpTable EQUW PrintOption00 \ Jump table for printing configuration options EQUW PrintOption01 EQUW PrintOption02 EQUW PrintOption03 EQUW PrintOption04 EQUW PrintOption05 EQUW PrintOption06 EQUW PrintOption07 EQUW PrintOption08 EQUW PrintOption09 EQUW PrintOption10 EQUW PrintOption11 EQUW PrintOption12 EQUW PrintOption13 \ ****************************************************************************** \ \ Name: PrintOption routines \ Type: Variable \ Category: Two-player Elite \ Summary: Routines for printing configuration options \ \ ****************************************************************************** .PrintOption00 LDX #0 \ Print the text for option #0 (player 1 ship) LDA player1ShipType JMP PrintOption0To95 .PrintOption01 LDX #1 \ Print the text for option #1 (player 2 ship) LDA player2ShipType JMP PrintOption0To95 .PrintOption02 LDX #2 \ Print the text for option #2 (player 1 lasers) LDY LASER \ Set Y = the laser power for the front view .lasr1 LDA #62 \ Set A to token 62 ("PULSE") CPY #128+POW \ If the laser power for view X is not #POW+128 (beam BNE P%+4 \ laser), skip the next LDA instruction LDA #63 \ This sets A = 63 if the laser in view Y is a beam \ laser (token 63 is "BEAM") CPY #Armlas \ If the laser power for view X is not #Armlas (military BNE P%+4 \ laser), skip the next LDA instruction LDA #65 \ This sets A = 65 if the laser in view Y is a military \ laser (token 65 is "MILITARY") CPY #Mlas \ If the laser power for view X is not #Mlas (mining BNE P%+4 \ laser), skip the next LDA instruction LDA #64 \ This sets A = 64 if the laser in view Y is a mining \ laser (token 64 is "MINING") JMP PrintOption0To95 \ Print the laser type .PrintOption03 LDX #3 \ Print the text for option #3 (player 2 lasers) LDY player2LASER \ Set Y = the laser power for the front view BRA lasr1 \ Jump to lasr1 to print the laser type .PrintOption04 LDX #4 \ Print the text for option #4 (player 1 energy) LDA ENGY \ Print the energy type CLC ADC #48 JMP PrintOption0To95 .PrintOption05 LDX #5 \ Print the text for option #5 (player 2 energy) LDA player2ENGY \ Print the energy type CLC ADC #48 JMP PrintOption0To95 .PrintOption06 LDA #1 \ Move to (1, 17) JSR DOXC LDA #17 JSR DOYC LDX #6 \ Set the highlight colour for this option (i.e. red if JSR SetHighlightColour \ this is the highlighted value, yellow otherwise) LDX player1Missiles \ Print the number of missiles for player 1 LDY #0 LDA #1 CLC JMP TT11 .PrintOption07 LDA #30 \ Move to (30, 17) JSR DOXC LDA #17 JSR DOYC LDX #7 \ Set the highlight colour for this option (i.e. red if JSR SetHighlightColour \ this is the highlighted value, yellow otherwise) LDX player2Missiles \ Print the number of missiles for player 2 LDY #0 LDA #1 CLC JMP TT11 .PrintOption08 LDX #8 \ Print the text for option #8 (player 1 E.C.M.) LDA #66 \ Set A to 66 ("NO") or 67 ("YES") depending on whether LDY ECM \ ECM is zero BEQ P%+4 LDA #67 JMP PrintOption0To95 \ Print the E.C.M. type .PrintOption09 LDX #9 \ Print the text for option #9 (player 2 E.C.M.) LDA #66 \ Set A to 66 ("NO") or 67 ("YES") depending on whether LDY player2ECM \ player2ECM is zero BEQ P%+4 LDA #67 JMP PrintOption0To95 \ Print the E.C.M. type .PrintOption10 LDX #10 \ Print the text for option #10 (player 1 controls) LDA #44 \ Set A to 44 ("KEYBOARD") LDY JSTK \ If JSTK is zero then joysticks are not configured, so BEQ joys1 \ jump to joys1 to print the configuration in A LDA #45 \ Set A to 45 ("JOYSTICK") BIT JSTK \ If bit 7 of JSTK is clear then Delta 14B is not BPL joys1 \ configured, jump to joys1 to print the configuration \ in A LDA #47 \ Set A to 47 ("DELTA 14B") .joys1 JMP PrintOption0To95 \ Print the control type .PrintOption11 LDX #11 \ Print the text for option #11 (player 2 controls) LDA #46 \ Set A to 46 ("AI PILOT") LDY player2JSTK \ If player2JSTK is zero then AI Pilot is configured, so BEQ joys2 \ jump to joys2 to print the configuration in A LDA #45 \ Set A to 45 ("JOYSTICK") BIT player2JSTK \ If bit 7 of player2JSTK is clear then Delta 14B is not BPL joys2 \ configured, jump to joys2 to print the configuration \ in A LDA #47 \ Set A to 47 ("DELTA 14B") .joys2 JMP PrintOption0To95 \ Print the control type .PrintOption12 LDY player1GameType \ If the game type is non-zero, jump to gtyp1 to print BNE gtyp1 \ the number of hits LDX #12 \ Print token 52 ("SURVIVOR") for the game type in LDA #52 \ option #12 JMP PrintOption0To95 .gtyp1 \ If we get here then the game type is the first to a \ specific number of hits, with the number of hits \ divided by 10 is in Y LDA #1 \ Move to (1, 20) JSR DOXC LDA #20 JSR DOYC LDX #12 \ Set the highlight colour for this option (i.e. red if JSR SetHighlightColour \ this is the highlighted value, yellow otherwise) LDX player1GameType \ Set X to the number of hits divided by 10 .gtyp2 LDY #1 \ Set Y to the number of digits in X (1 to 3) CPX #10 BCC gtyp3 INY CPX #100 BCC gtyp3 INY .gtyp3 TYA \ Set A to the number of digits LDY #0 \ Print the number of hits for player 1 CLC JSR TT11 LDA #'0' \ Print "0" JSR TT27 LDA #1 \ Set QQ17 = 1 to switch standard tokens to lower STA QQ17 \ case LDA #160+53 \ Print " HITS" JMP TT27 .PrintOption13 LDY player2GameType \ If the game type is non-zero, jump to gtyp4 to print BNE gtyp4 \ the number of hits LDX #13 \ Print token 52 ("SURVIVOR") for the game type in LDA #52 \ option #12 JMP PrintOption0To95 .gtyp4 \ If we get here then the game type is the first to a \ specific number of hits, with the number of hits \ divided by 10 is in Y LDA #20 \ Move to row 20 JSR DOYC LDX #13 \ Set the highlight colour for this option (i.e. red if JSR SetHighlightColour \ this is the highlighted value, yellow otherwise) LDX player2GameType \ Set X to the number of hits divided by 10 LDY #24 \ Set Y to the correct column number to right-align the CPX #10 \ "0 HITS" string BCC gtyp5 DEY CPX #100 BCC gtyp5 DEY .gtyp5 TYA \ Move to the correct column number JSR DOXC JMP gtyp2 \ Jump to gtyp2 to print the number of hits and " HITS" \ ****************************************************************************** \ \ Name: PrintOption0To95 \ Type: Subroutine \ Category: Two-player Elite \ Summary: Print a configuration option using a token in the range 0 to 95 \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X Option number (0-1, 2-3, 4-5 etc.) \ \ A Token to print (in the range 0 to 95) \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .PrintOption0To95 CLC \ Convert the token number into an argument for TT27 ADC #160 \ Fall through into PrintOption to print the token \ --- Mod: Code added for two-player Elite: -----------> \ ****************************************************************************** \ \ Name: PrintOption \ Type: Subroutine \ Category: Two-player Elite \ Summary: Print a configuration option \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X Option number (0-1, 2-3, 4-5 etc.) \ \ A Token to print \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .PrintOption STX optionNumber \ Store the arguments STA tokenNumber TXA \ Set X to half the option number to get the row, and LSR A \ set the C flag when the option number is odd TAX LDA #1 \ If the option number is even, set A = 1, otherwise BCC popt1 \ set A = 20, to use as the column for the option text LDA #20 .popt1 PHA \ Store the column number on the stack JSR DOXC \ Move to the column in A TXA \ Move to the row for this option, with option #0 on CLC \ row 14 ADC #14 JSR DOYC \ Move to the row for this option LDX optionNumber \ Set the highlight colour for this option (i.e. red if JSR SetHighlightColour \ this is the highlighted value, yellow otherwise) PLA \ Set A to the column for this option CMP #1 \ If we are printing in the left column, jump to popt2 BEQ popt2 \ to skip the right-alignment \ Right-align option token PHA \ Store the column on the stack once again LDA #%11000000 \ Set the DTW4 flag to %11000000 (justify text, buffer STA DTW4 \ entire token including carriage returns) STZ DTW5 \ Zero DTW5, which holds the size of the buffer LDA tokenNumber \ Print the token into the buffer JSR TT27 STZ DTW4 \ Reset the buffer-printing flag so we print to the \ screen PLA \ Fetch the column from the stack CLC \ Set A = A + 11 - DTW5 ADC #11 \ SEC \ to give us the column number to print the token to SBC DTW5 \ right-align it .popt2 JSR DOXC \ Move to the correct column for the token LDA #%10000000 \ Set bit 7 of QQ17 to switch standard tokens to STA QQ17 \ Sentence Case LDA tokenNumber \ Print the token and return from the subroutine using JMP TT27 \ a tail call \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: ToggleHighlight \ Type: Subroutine \ Category: Two-player Elite \ Summary: Toggle the currently highlighted configuration option \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ ToggleHighlight+3 Toggle configuration option A \ \ ****************************************************************************** .ToggleHighlight LDA configHighlight \ Set Y = A * 2 ASL A TAY LDA toggleJumpTable,Y \ Set the address of the JSR at togl1 to the Y-th STA togl1+1 \ entry, incrementing Y to point to the next entry INY LDA toggleJumpTable,Y STA togl1+2 .togl1 JMP &FFFF \ Call the routine to print this option and return from \ the subroutine using a tail call \ ****************************************************************************** \ \ Name: toggleJumpTable \ Type: Variable \ Category: Two-player Elite \ Summary: A jump table for toggling configuration options \ \ ****************************************************************************** .toggleJumpTable EQUW ToggleOption00 \ Jump table for printing configuration options EQUW ToggleOption01 EQUW ToggleOption02 EQUW ToggleOption03 EQUW ToggleOption04 EQUW ToggleOption05 EQUW ToggleOption06 EQUW ToggleOption07 EQUW ToggleOption08 EQUW ToggleOption09 EQUW ToggleOption10 EQUW ToggleOption11 EQUW ToggleOption12 EQUW ToggleOption13 \ ****************************************************************************** \ \ Name: ToggleOption routines \ Type: Variable \ Category: Two-player Elite \ Summary: Routines for printing configuration options \ \ ****************************************************************************** .ToggleOption00 LDX player1ShipType \ Set X = the current ship type for player 1 JSR ToggleShipType \ Toggle the ship type LDA shipDistance,X \ Set the correct distance for the new choice STA K%+7 STX player1ShipType \ Set the ship type for player 1 to the new value RTS \ Return from the subroutine .ToggleOption01 LDX player2ShipType \ Set X = the current ship type for player 2 JSR ToggleShipType \ Toggle the ship type LDA shipDistance,X \ Set the correct distance for the new choice STA K%+NI%+7 STX player2ShipType \ Set the ship type for player 2 to the new value RTS \ Return from the subroutine .ToggleShipType INX \ Move ship on to next type CPX #13 \ Fix if new type is Anaconda BNE styp1 LDX #14 RTS .styp1 CPX #15 \ Fix if new type is Viper BNE styp2 LDX #16 RTS .styp2 CPX #20 \ Fix if new type is Moray BNE styp3 LDX #28 RTS .styp3 CPX #30 \ Fix if new type is Cobra BNE styp4 LDX #11 .styp4 RTS \ Return from the subroutine .ToggleOption02 LDA LASER \ Set A = the laser power for player 1 JSR ToggleLaser \ Toggle the laser STA LASER \ Set the lasers for player 1 to the new value STA LASER+1 RTS \ Return from the subroutine .ToggleOption03 LDA player2LASER \ Set A = the laser power for player 2 JSR ToggleLaser \ Toggle the laser STA player2LASER \ Set the lasers for player 2 to the new value STA player2LASER+1 RTS \ Return from the subroutine .ToggleLaser CMP #POW \ Go from Pulse to Beam BNE last1 LDA #POW+128 BNE last4 .last1 CMP #POW+128 \ Go from Beam to Military BNE last2 LDA #Armlas BNE last4 .last2 CMP #Armlas \ Go from Military to Mining BNE last3 LDA #Mlas BNE last4 .last3 LDA #POW \ Go from Mining to Pulse .last4 RTS \ Return from the subroutine .ToggleOption04 LDX ENGY \ Set X = the energy unit for player 1 INX CPX #3 BNE P%+4 LDX #0 STX ENGY \ Set the energy unit for player 1 to the new value RTS \ Return from the subroutine .ToggleOption05 LDX player2ENGY \ Set X = the energy unit for player 2 INX CPX #3 BNE P%+4 LDX #0 STX player2ENGY \ Set the energy unit for player 2 to the new value RTS \ Return from the subroutine .ToggleOption06 LDX player1Missiles \ Set X = the number of missiles for player 1 INX CPX #5 BNE P%+4 LDX #0 STX player1Missiles \ Set the number of missiles for player 1 to the new \ value STX NOMSL \ Set the in-game number of missiles to the new value \ (this setting gets changed during gameplay) JSR msblob \ Update the missiles in the dashboard RTS \ Return from the subroutine .ToggleOption07 LDX player2Missiles \ Set X = the number of missiles for player 2 INX CPX #5 BNE P%+4 LDX #0 STX player2Missiles \ Set the number of missiles for player 2 to the new \ value STX player2NOMSL \ Set the in-game number of missiles to the new value \ (this setting gets changed during gameplay) JSR msblob \ Update the missiles in the dashboard RTS \ Return from the subroutine .ToggleOption08 LDA ECM \ Flip the ECM value for player 1 EOR #&FF STA ECM RTS \ Return from the subroutine .ToggleOption09 LDA player2ECM \ Flip the ECM value for player 2 EOR #&FF STA player2ECM RTS \ Return from the subroutine .ToggleOption10 LDA JSTK \ If the JSTK value for player 1 is zero, jump to tjoy1 BNE tjoy1 \ to process the joystick and Delta 14B values \ If we get here then the current setting is keyboard LDA #1 \ Set JSTK to non-zero with bit 7 clear, for joystick STA JSTK RTS \ Return from the subroutine .tjoy1 CMP #1 \ If the JSTK value for player 1 is not 1, jump to tjoy2 BNE tjoy2 \ to move on to keyboard \ If we get here then the current setting is joystick LDA #%10000001 \ Set JSTK to non-zero with bit 7 set, for Delta 14B STA JSTK RTS \ Return from the subroutine .tjoy2 \ If we get here then the current setting is Delta 14B STZ JSTK \ Set JSTK to zero for keyboard RTS \ Return from the subroutine .ToggleOption11 LDA player2JSTK \ If the player2JSTK value for player 2 is zero, jump to BNE tjoy3 \ tjoy3 to process the joystick and Delta 14B values \ If we get here then the current setting is AI Pilot LDA #1 \ Set player2JSTK to non-zero with bit 7 clear, for STA player2JSTK \ joystick STZ player2INWK32 \ Disable ship AI for player 2 LDA player2LASER \ Enable rear lasers for non-AI player 2 STA player2LASER+1 RTS \ Return from the subroutine .tjoy3 CMP #1 \ If the player2JSTK value for player 1 is not 1, jump BNE tjoy4 \ to tjoy4 to move on to AI Pilot \ If we get here then the current setting is joystick LDA #%10000001 \ Set player2JSTK to non-zero with bit 7 set, for Delta STA player2JSTK \ 14B RTS \ Return from the subroutine .tjoy4 \ If we get here then the current setting is Delta 14B STZ player2JSTK \ Set player2JSTK to zero for AI Pilot LDA #%11111110 \ Enable ship AI for player 2 by setting the AI flags STA player2INWK32 \ for NPC player 2 as follows: \ \ * Bit 0 = no E.C.M. \ \ * Bits 1-6 = aggression level of 63 \ \ * Bit 7 = AI enabled (apply TACTICS to ship) \ \ We set bit 0 when spawning the ship if E.C.M. has been \ configured STZ player2LASER+1 \ Remove rear lasers for the AI Pilot RTS \ Return from the subroutine .ToggleOption12 LDA player1GameType \ Set A = the game type for player 1 JSR ToggleGameType \ Toggle the game type STA player1GameType \ Set the game type for player 1 to the new value STZ P+1 \ Set P(1 0) = game type STA P ASL P \ Set P(1 0) = game type * 2 ROL P+1 LDA P \ Set (S R) = P(1 0) STA R LDA P+1 STA S ASL R \ Set (S R) = (S R) * 4 ROL S \ = P(1 0) * 8 ASL R \ = game type * 8 ROL S CLC \ Set player1Target(1 0) = (S R) + P(1 0) LDA P \ = game type * 8 + game type * 2 ADC R \ = game type * 10 STA player1Target LDA P+1 ADC S STA player1Target+1 RTS \ Return from the subroutine .ToggleOption13 LDA player2GameType \ Set A = the game type for player 2 JSR ToggleGameType \ Toggle the game type STA player2GameType \ Set the game type for player 2 to the new value STZ P+1 \ Set P(1 0) = game type STA P ASL P \ Set P(1 0) = game type * 2 ROL P+1 LDA P \ Set (S R) = P(1 0) STA R LDA P+1 STA S ASL R \ Set (S R) = (S R) * 4 ROL S \ = P(1 0) * 8 ASL R \ = game type * 8 ROL S CLC \ Set player1Target(1 0) = (S R) + P(1 0) LDA P \ = game type * 8 + game type * 2 ADC R \ = game type * 10 STA player2Target LDA P+1 ADC S STA player2Target+1 RTS \ Return from the subroutine .ToggleGameType CMP #0 \ Go from Survivor to 10 hits BNE gaty1 LDA #1 BNE gaty8 .gaty1 CMP #1 \ Go from 10 to 20 hits BNE gaty2 LDA #2 BNE gaty8 .gaty2 CMP #2 \ Go from 20 to 50 hits BNE gaty3 LDA #5 BNE gaty8 .gaty3 CMP #5 \ Go from 50 to 100 hits BNE gaty4 LDA #10 BNE gaty8 .gaty4 CMP #10 \ Go from 100 to 200 hits BNE gaty5 LDA #20 BNE gaty8 .gaty5 CMP #20 \ Go from 200 to 500 hits BNE gaty6 LDA #50 BNE gaty8 .gaty6 CMP #50 \ Go from 500 to 1000 hits BNE gaty7 LDA #100 BNE gaty8 .gaty7 LDA #0 \ Go from 1000 hits to Survivor .gaty8 RTS \ Return from the subroutine \ ****************************************************************************** \ \ Name: SetHighlightColour \ Type: Subroutine \ Category: Two-player Elite \ Summary: Set the highlight colour for a configuration option \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X Option number (0-1, 2-3, 4-5 etc.) \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .SetHighlightColour CPX configHighlight \ If X matches the currently highlighted field, jump BEQ high1 \ to high1 to print the field in red LDA #YELLOW \ Send a #SETCOL YELLOW command to the I/O processor to JMP DOCOL \ switch to yellow .high1 LDA #RED \ Send a #SETCOL RED command to the I/O processor to JMP DOCOL \ switch to red \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: ROTATE_VECTORS_16 \ Type: Macro \ Category: Two-player Elite \ Summary: Rotate an orientation vector by an orientation vector (16 bits) \ \ ------------------------------------------------------------------------------ \ \ Rotate an orientation vector [ v1 v2 v3 ] by another orientation vector \ [ w1 w2 w3 ] and store the result in the coordinate c. The calculation is: \ \ c = [ v1 v2 v3 ] . [ w1 w2 w3 ] \ = v1 * w1 + v2 * w2 + v3 * w3 \ \ where v1, v2, v3 are INWK offsets, w1, w2, w3 are XX3 offsets and c is an \ offset into the newVectors block. \ \ We repeat this calculation nine times to multiply two orientation matrices. \ \ So values of v1, v2, v3 and w1, w2, w3 mean: \ \ * 9 = (nosev_x_hi nosev_x_lo) \ * 11 = (nosev_y_hi nosev_y_lo) \ * 13 = (nosev_z_hi nosev_z_lo) \ \ * 15 = (roofv_x_hi roofv_x_lo) \ * 17 = (roofv_y_hi roofv_y_lo) \ * 19 = (roofv_z_hi roofv_z_lo) \ \ * 21 = (sidev_x_hi sidev_x_lo) \ * 23 = (sidev_y_hi sidev_y_lo) \ * 25 = (sidev_z_hi sidev_z_lo) \ \ while values of c mean: \ \ * 0 = store 16-bit nosev_x in newVectors+0 to newVectors+1 \ * 2 = store 16-bit nosev_y in newVectors+2 to newVectors+3 \ * 4 = store 16-bit nosev_z in newVectors+4 to newVectors+5 \ \ * 6 = store 16-bit roofv_x in newVectors+6 to newVectors+7 \ * 8 = store 16-bit roofv_y in newVectors+8 to newVectors+9 \ * 10 = store 16-bit roofv_z in newVectors+10 to newVectors+11 \ \ * 12 = store 16-bit sidev_x in newVectors+12 to newVectors+13 \ * 14 = store 16-bit sidev_y in newVectors+14 to newVectors+15 \ * 16 = store 16-bit sidev_z in newVectors+16 to newVectors+17 \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> MACRO ROTATE_VECTORS_16 c, v1, v2, v3, w1, w2, w3 \ c = [ v1 v2 v3 ] . [ w1 w2 w3 ] \ v1 * w1 + v2 * w2 + v3 * w3 \ \ Commentary has c = z-coordinate \ v1 = nosev_x \ v2 = nosev_y \ v3 = nosev_z \ w1 = sidev_x \ w2 = sidev_y \ w3 = sidev_z LDY #v1 \ First do nosev_x * sidev_x, so calculate: LDX #w1 \ JSR Multiply16x16 \ K(3 2 1 0) = nosev_x * sidev_x \ \ i.e. (nosev_x_hi nosev_x_lo) * (sidev_x_hi sidev_x_lo) LDA K+1 \ Copy result from K(3 2 1) to XX12(2 1 0), ignoring low STA XX12 \ byte, so: LDA K+2 \ STA XX12+1 \ XX12(2 1 0) = nosev_x * sidev_x LDA K+3 STA XX12+2 BIT K \ Round up byte #1 of result if bit 7 of byte #0 is set BPL rotv1 INC XX12 BNE rotv1 INC XX12+1 BNE rotv1 INC XX12+2 .rotv1 LDY #v2 \ Next do nosev_y * sidev_y, so calculate: LDX #w2 \ JSR Multiply16x16 \ K(3 2 1 0) = nosev_y * sidev_y \ \ i.e. (nosev_y_hi nosev_y_lo) * (sidev_y_hi sidev_y_lo) LDA K+1 \ Copy result from K(3 2 1) to XX12(5 4 3), ignoring low STA XX12+3 \ byte, so: LDA K+2 \ STA XX12+4 \ XX12(5 4 3) = nosev_y * sidev_y LDA K+3 STA XX12+5 BIT K \ Round up byte #1 of result if bit 7 of byte #0 is set BPL rotv2 INC XX12+3 BNE rotv2 INC XX12+4 BNE rotv2 INC XX12+5 .rotv2 LDY #v3 \ Then do nosev_z * sidev_z, so calculate LDX #w3 \ JSR Multiply16x16 \ K(3 2 1 0) = nosev_z * sidev_z \ \ i.e. (nosev_z_hi nosev_z_lo) * (sidev_z_hi sidev_z_lo) BIT K \ Round up byte #1 of result if bit 7 of byte #0 is set BPL rotv3 INC K+1 BNE rotv3 INC K+2 BNE rotv3 INC K+3 .rotv3 \ By this point we have: \ \ XX12(2 1 0) = nosev_x * sidev_x \ XX12(5 4 3) = nosev_y * sidev_y \ K(3 2 1) = nosev_z * sidev_z \ \ So now we need to add them all together LDA XX12 \ Set XX15(3 2 1) = XX12(2 1 0) STA XX15+1 \ = nosev_x * sidev_x LDA XX12+1 STA XX15+2 LDA XX12+2 STA XX15+3 JSR Add24 \ Calculate: \ \ P(2 1 0) = K(3 2 1) + XX15(3 2 1) \ = (nosev_z * sidev_z) + (nosev_x * sidev_x) LDA XX12+3 \ Set XX15(3 2 1) = XX12(5 4 3) STA XX15+1 \ = nosev_y * sidev_y LDA XX12+4 STA XX15+2 LDA XX12+5 STA XX15+3 LDA P \ Set: STA K+1 \ LDA P+1 \ K(3 2 1) = P(2 1 0) STA K+2 \ = (nosev_z * sidev_z) + (nosev_x * sidev_x) LDA P+2 STA K+3 JSR Add24 \ Calculate: \ \ P(2 1 0) = K(3 2 1) + XX15(3 2 1) \ = (nosev_z * sidev_z) + (nosev_x * sidev_x) \ + (nosev_y * sidev_y) JSR DivideBy96 \ Calculate: \ \ K(3 2 1 0) = P(2 1 0) / 96 LDA K+3 \ Extract the sign from K+3 to put into K+1 AND #%10000000 STA T LDA K \ Set result vector to the result in K(1 0) STA newVectors+c LDA K+1 AND #%01111111 ORA T STA newVectors+c+1 ENDMACRO \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: OrientateMissile \ Type: Subroutine \ Category: Two-player Elite \ Summary: Calculate the orientation of a missile in player 2s's view \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .OrientateMissile \ If we get here then we are drawing a missile in player \ 2's view, so we need to calculate the missile's \ orientation vectors for player 2's view, as follows: \ \ missile's orientation vector for player 2's view \ = player 2's orientation matrix \ . missile's orientation vector for player 1's view \ \ The missile's orientation vectors for player 1's view \ are already in INWK at this point, so the first step \ is to copy player 2's orientation vectors into XX3 \ \ We will store the orientation vectors in XX3 using the \ same offsets as the orientation vectors in INWK, so \ both INWK+9 and XX3+9 will represent nosev_x_lo, for \ example LDX #17 \ We start by copying player 2's orientation vectors \ into XX3, so set an index in X for copying 18 bytes .orms1 LDA K%+NI%*2+9,X \ Copy the X-th vector byte from player 2 into XX3 STA XX3+9,X DEX \ Decrement the loop counter BPL orms1 \ Loop back until we have copied all 18 bytes \ So by this point we have: \ \ * The missile's orientation vectors in INWK \ \ * Player 2's orientation matrix in XX3 \ \ We now do the following multiplication for each of the \ missile's orientation vectors in turn: \ \ new vector = player 2 matrix . missile vector \ \ where player 2's orientation matrix is the 3x3 matrix \ made up of player 2's orientation vectors, in this \ form: \ \ [ sidev_x sidev_y sidev_z ] \ [ roofv_x roofv_y roofv_z ] \ [ nosev_x nosev_y nosev_z ] \ \ and each of the missile's orientation vectors is a \ standard orientation vector in the form: \ \ [ sidev_x ] \ [ sidev_y ] \ [ sidev_z ] \ \ So we need to do three calculations to get the new \ orientation vectors for the missile in player 2's \ view, like this: \ \ [ sidev_x sidev_y sidev_z ] [ sidev_x ] \ sidev' = [ roofv_x roofv_y roofv_z ] . [ sidev_y ] \ [ nosev_x nosev_y nosev_z ] [ sidev_z ] \ \ [ sidev_x sidev_y sidev_z ] [ roofv_x ] \ roofv' = [ roofv_x roofv_y roofv_z ] . [ roofv_y ] \ [ nosev_x nosev_y nosev_z ] [ roofv_z ] \ \ [ sidev_x sidev_y sidev_z ] [ nosev_x ] \ nosev' = [ roofv_x roofv_y roofv_z ] . [ nosev_y ] \ [ nosev_x nosev_y nosev_z ] [ nosev_z ] \ \ We store the result in newVectors, ready to be copied \ back into INWK when we are done \ \ Note that orientation vectors are stored in reverse \ order in memory, like this: nosev, roofv and sidev \ \ We store the results in newVectors in the same order \ as they are stored in INWK and XX3, to enable easy \ copying into INWK when we are done \ First we calculate: \ \ sidev' = [ player 2 matrix ] . [ missile sidev ] \ \ [ sidev_x sidev_y sidev_z ] [ sidev_x ] \ = [ roofv_x roofv_y roofv_z ] . [ sidev_y ] \ [ nosev_x nosev_y nosev_z ] [ sidev_z ] \ \ as: \ \ sidev_x' = [ player 2 sidev ] . [ missile sidev ] \ \ sidev_y' = [ player 2 roofv ] . [ missile sidev ] \ \ sidev_z' = [ player 2 nosev ] . [ missile sidev ] \ \ As a reminder, the vectors are stored like this: \ \ * The missile's orientation vectors in INWK \ \ * Player 2's orientation matrix in XX3 \ \ and the arguments to ROTATE_VECTORS_16 are like this: \ \ * v1, v2, v3 are XX3 offsets (i.e. player 2 matrix) \ \ * w1, w2, w3 are INWK offsets (i.e. missile vectors) \ c, v1, v2, v3, w1, w2, w3 ROTATE_VECTORS_16 12, 21, 23, 25, 21, 23, 25 ROTATE_VECTORS_16 14, 15, 17, 19, 21, 23, 25 ROTATE_VECTORS_16 16, 9, 11, 13, 21, 23, 25 \ Next we calculate: \ \ roofv' = [ player 2 matrix ] . [ missile roofv ] \ \ [ sidev_x sidev_y sidev_z ] [ roofv_x ] \ = [ roofv_x roofv_y roofv_z ] . [ roofv_y ] \ [ nosev_x nosev_y nosev_z ] [ roofv_z ] \ \ as: \ \ roofv_x' = [ player 2 sidev ] . [ missile roofv ] \ \ roofv_y' = [ player 2 roofv ] . [ missile roofv ] \ \ roofv_z' = [ player 2 nosev ] . [ missile roofv ] \ c, v1, v2, v3, w1, w2, w3 ROTATE_VECTORS_16 6, 21, 23, 25, 15, 17, 19 ROTATE_VECTORS_16 8, 15, 17, 19, 15, 17, 19 ROTATE_VECTORS_16 10, 9, 11, 13, 15, 17, 19 \ And finally we calculate: \ \ nosev' = [ player 2 matrix ] . [ missile nosev ] \ \ [ sidev_x sidev_y sidev_z ] [ nosev_x ] \ = [ roofv_x roofv_y roofv_z ] . [ nosev_y ] \ [ nosev_x nosev_y nosev_z ] [ nosev_z ] \ \ as: \ \ nosev_x' = [ player 2 sidev ] . [ missile nosev ] \ \ nosev_y' = [ player 2 roofv ] . [ missile nosev ] \ \ nosev_z' = [ player 2 nosev ] . [ missile nosev ] \ c, v1, v2, v3, w1, w2, w3 ROTATE_VECTORS_16 0, 21, 23, 25, 9, 11, 13 ROTATE_VECTORS_16 2, 15, 17, 19, 9, 11, 13 ROTATE_VECTORS_16 4, 9, 11, 13, 9, 11, 13 LDX #17 \ We now copy the new orientation vectors into INWK, so \ set an index in X for copying 18 bytes .orms2 LDA newVectors,X \ Copy the X-th new vector byte into INWK+9, which is STA INWK+9,X \ where the orientation vectors are in the data block DEX \ Decrement the loop counter BPL orms2 \ Loop back until we have copied all 18 bytes JSR TIDY \ Tidy the missile's new orientation vectors JMP dshp18 \ Return from the subroutine, rejoining DrawPlayer2View \ just after the call to this routine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: F% \ Type: Variable \ Category: Utility routines \ Summary: Denotes the end of the main game code, from ELITE A to ELITE J \ \ ****************************************************************************** .F% SKIP 0 \ ****************************************************************************** \ \ Save ELTJ.bin \ \ ****************************************************************************** PRINT "ELITE J" PRINT "Assembled at ", ~CODE_J% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_J%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_J% PRINT "S.ELTJ ", ~CODE_J%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_J% SAVE "3-assembled-output/ELTJ.bin", CODE_J%, P%, LOAD% \ ****************************************************************************** \ \ ELITE SHIP BLUEPRINTS FILE \ \ Produces the binary file SHIPS.bin that gets loaded by elite-bcfs.asm. \ \ ****************************************************************************** CODE_SHIPS% = &D000 LOAD_SHIPS% = &D000 ORG CODE_SHIPS% \ Set the assembly address to CODE_SHIPS% \ ****************************************************************************** \ \ Name: XX21 \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprints lookup table \ Deep dive: Ship blueprints \ \ ****************************************************************************** .XX21 EQUW SHIP_MISSILE \ MSL = 1 = Missile EQUW SHIP_CORIOLIS \ SST = 2 = Coriolis space station EQUW SHIP_ESCAPE_POD \ ESC = 3 = Escape pod EQUW SHIP_PLATE \ PLT = 4 = Alloy plate EQUW SHIP_CANISTER \ OIL = 5 = Cargo canister EQUW SHIP_BOULDER \ 6 = Boulder EQUW SHIP_ASTEROID \ AST = 7 = Asteroid EQUW SHIP_SPLINTER \ SPL = 8 = Splinter EQUW SHIP_SHUTTLE \ SHU = 9 = Shuttle EQUW SHIP_TRANSPORTER \ 10 = Transporter EQUW SHIP_COBRA_MK_3 \ CYL = 11 = Cobra Mk III EQUW SHIP_PYTHON \ 12 = Python EQUW SHIP_BOA \ 13 = Boa EQUW SHIP_ANACONDA \ ANA = 14 = Anaconda EQUW SHIP_ROCK_HERMIT \ HER = 15 = Rock hermit (asteroid) EQUW SHIP_VIPER \ COPS = 16 = Viper EQUW SHIP_SIDEWINDER \ SH3 = 17 = Sidewinder EQUW SHIP_MAMBA \ 18 = Mamba EQUW SHIP_KRAIT \ KRA = 19 = Krait EQUW SHIP_ADDER \ ADA = 20 = Adder EQUW SHIP_GECKO \ 21 = Gecko EQUW SHIP_COBRA_MK_1 \ 22 = Cobra Mk I EQUW SHIP_WORM \ WRM = 23 = Worm EQUW SHIP_COBRA_MK_3_P \ CYL2 = 24 = Cobra Mk III (pirate) EQUW SHIP_ASP_MK_2 \ ASP = 25 = Asp Mk II EQUW SHIP_PYTHON_P \ 26 = Python (pirate) EQUW SHIP_FER_DE_LANCE \ 27 = Fer-de-lance EQUW SHIP_MORAY \ 28 = Moray EQUW SHIP_THARGOID \ THG = 29 = Thargoid EQUW SHIP_THARGON \ TGL = 30 = Thargon EQUW SHIP_CONSTRICTOR \ CON = 31 = Constrictor EQUW SHIP_LOGO \ LGO = 32 = The Elite logo EQUW SHIP_COUGAR \ COU = 33 = Cougar EQUW SHIP_DODO \ DOD = 34 = Dodecahedron ("Dodo") space station \ ****************************************************************************** \ \ Name: E% \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprints default NEWB flags \ Deep dive: Ship blueprints \ Advanced tactics with the NEWB flags \ \ ------------------------------------------------------------------------------ \ \ When spawning a new ship, the bits from this table are applied to the new \ ship's NEWB flags in byte #36 (i.e. a set bit in this table will set that bit \ in the NEWB flags). In other words, if a ship blueprint is set to one of the \ following, then all spawned ships of that type will be too: trader, bounty \ hunter, hostile, pirate, innocent, cop. \ \ The NEWB flags are as follows: \ \ * Bit 0: Trader flag (0 = not a trader, 1 = trader) \ * Bit 1: Bounty hunter flag (0 = not a bounty hunter, 1 = bounty hunter) \ * Bit 2: Hostile flag (0 = not hostile, 1 = hostile) \ * Bit 3: Pirate flag (0 = not a pirate, 1 = pirate) \ * Bit 4: Docking flag (0 = not docking, 1 = docking) \ * Bit 5: Innocent bystander (0 = normal, 1 = innocent bystander) \ * Bit 6: Cop flag (0 = not a cop, 1 = cop) \ * Bit 7: For spawned ships: ship been scooped or has docked \ For blueprints: this ship type has an escape pod fitted \ \ ****************************************************************************** .E% EQUB %00000000 \ Missile EQUB %00000000 \ Coriolis space station EQUB %00000001 \ Escape pod Trader EQUB %00000000 \ Alloy plate EQUB %00000000 \ Cargo canister EQUB %00000000 \ Boulder EQUB %00000000 \ Asteroid EQUB %00000000 \ Splinter EQUB %00100001 \ Shuttle Trader, innocent EQUB %01100001 \ Transporter Trader, innocent, cop EQUB %10100000 \ Cobra Mk III Innocent, escape pod EQUB %10100000 \ Python Innocent, escape pod EQUB %10100000 \ Boa Innocent, escape pod EQUB %10100001 \ Anaconda Trader, innocent, escape pod EQUB %10100001 \ Rock hermit (asteroid) Trader, innocent, escape pod EQUB %11000010 \ Viper Bounty hunter, cop, escape pod EQUB %00001100 \ Sidewinder Hostile, pirate EQUB %10001100 \ Mamba Hostile, pirate, escape pod EQUB %10001100 \ Krait Hostile, pirate, escape pod EQUB %10001100 \ Adder Hostile, pirate, escape pod EQUB %00001100 \ Gecko Hostile, pirate EQUB %10001100 \ Cobra Mk I Hostile, pirate, escape pod EQUB %00000101 \ Worm Hostile, trader EQUB %10001100 \ Cobra Mk III (pirate) Hostile, pirate, escape pod EQUB %10001100 \ Asp Mk II Hostile, pirate, escape pod EQUB %10001100 \ Python (pirate) Hostile, pirate, escape pod EQUB %10000010 \ Fer-de-lance Bounty hunter, escape pod EQUB %00001100 \ Moray Hostile, pirate EQUB %00001100 \ Thargoid Hostile, pirate EQUB %00000100 \ Thargon Hostile EQUB %00000100 \ Constrictor Hostile EQUB %00000000 \ The Elite logo EQUB %00100000 \ Cougar Innocent EQUB 0 \ This byte appears to be unused \ ****************************************************************************** \ \ Name: VERTEX \ Type: Macro \ Category: Drawing ships \ Summary: Macro definition for adding vertices to ship blueprints \ Deep dive: Ship blueprints \ Drawing ships \ \ ------------------------------------------------------------------------------ \ \ The following macro is used to build the ship blueprints: \ \ VERTEX x, y, z, face1, face2, face3, face4, visibility \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ x The vertex's x-coordinate \ \ y The vertex's y-coordinate \ \ z The vertex's z-coordinate \ \ face1 The number of face 1 associated with this vertex \ \ face2 The number of face 2 associated with this vertex \ \ face3 The number of face 3 associated with this vertex \ \ face4 The number of face 4 associated with this vertex \ \ visibility The visibility distance, beyond which the vertex is not \ shown \ \ ****************************************************************************** MACRO VERTEX x, y, z, face1, face2, face3, face4, visibility IF x < 0 s_x = 1 << 7 ELSE s_x = 0 ENDIF IF y < 0 s_y = 1 << 6 ELSE s_y = 0 ENDIF IF z < 0 s_z = 1 << 5 ELSE s_z = 0 ENDIF s = s_x + s_y + s_z + visibility f1 = face1 + (face2 << 4) f2 = face3 + (face4 << 4) ax = ABS(x) ay = ABS(y) az = ABS(z) EQUB ax, ay, az, s, f1, f2 ENDMACRO \ ****************************************************************************** \ \ Name: EDGE \ Type: Macro \ Category: Drawing ships \ Summary: Macro definition for adding edges to ship blueprints \ Deep dive: Ship blueprints \ Drawing ships \ \ ------------------------------------------------------------------------------ \ \ The following macro is used to build the ship blueprints: \ \ EDGE vertex1, vertex2, face1, face2, visibility \ \ When stored in memory, bytes #2 and #3 contain the vertex numbers multiplied \ by 4, so we can use them as indices into the heap at XX3 to fetch the screen \ coordinates for each vertex, as they are stored as four bytes containing two \ 16-bit numbers (see part 10 of the LL9 routine for details). \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ vertex1 The number of the vertex at the start of the edge \ \ vertex1 The number of the vertex at the end of the edge \ \ face1 The number of face 1 associated with this edge \ \ face2 The number of face 2 associated with this edge \ \ visibility The visibility distance, beyond which the edge is not \ shown \ \ ****************************************************************************** MACRO EDGE vertex1, vertex2, face1, face2, visibility f = face1 + (face2 << 4) EQUB visibility, f, vertex1 << 2, vertex2 << 2 ENDMACRO \ ****************************************************************************** \ \ Name: FACE \ Type: Macro \ Category: Drawing ships \ Summary: Macro definition for adding faces to ship blueprints \ Deep dive: Ship blueprints \ Drawing ships \ \ ------------------------------------------------------------------------------ \ \ The following macro is used to build the ship blueprints: \ \ FACE normal_x, normal_y, normal_z, visibility \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ normal_x The face normal's x-coordinate \ \ normal_y The face normal's y-coordinate \ \ normal_z The face normal's z-coordinate \ \ visibility The visibility distance, beyond which the edge is always \ shown \ \ ****************************************************************************** MACRO FACE normal_x, normal_y, normal_z, visibility IF normal_x < 0 s_x = 1 << 7 ELSE s_x = 0 ENDIF IF normal_y < 0 s_y = 1 << 6 ELSE s_y = 0 ENDIF IF normal_z < 0 s_z = 1 << 5 ELSE s_z = 0 ENDIF s = s_x + s_y + s_z + visibility ax = ABS(normal_x) ay = ABS(normal_y) az = ABS(normal_z) EQUB s, ax, ay, az ENDMACRO \ ****************************************************************************** \ \ Name: SHIP_MISSILE \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a missile \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_MISSILE EQUB 0 \ Max. canisters on demise = 0 EQUW 40 * 40 \ Targetable area = 40 * 40 EQUB LO(SHIP_MISSILE_EDGES - SHIP_MISSILE) \ Edges data offset (low) EQUB LO(SHIP_MISSILE_FACES - SHIP_MISSILE) \ Faces data offset (low) EQUB 85 \ Max. edge count = (85 - 1) / 4 = 21 EQUB 0 \ Gun vertex = 0 EQUB 10 \ Explosion count = 1, as (4 * n) + 6 = 10 EQUB 102 \ Number of vertices = 102 / 6 = 17 EQUB 24 \ Number of edges = 24 EQUW 0 \ Bounty = 0 EQUB 36 \ Number of faces = 36 / 4 = 9 EQUB 14 \ Visibility distance = 14 EQUB 2 \ Max. energy = 2 EQUB 44 \ Max. speed = 44 EQUB HI(SHIP_MISSILE_EDGES - SHIP_MISSILE) \ Edges data offset (high) EQUB HI(SHIP_MISSILE_FACES - SHIP_MISSILE) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00000000 \ Laser power = 0 \ Missiles = 0 .SHIP_MISSILE_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, 0, 68, 0, 1, 2, 3, 31 \ Vertex 0 VERTEX 8, -8, 36, 1, 2, 4, 5, 31 \ Vertex 1 VERTEX 8, 8, 36, 2, 3, 4, 7, 31 \ Vertex 2 VERTEX -8, 8, 36, 0, 3, 6, 7, 31 \ Vertex 3 VERTEX -8, -8, 36, 0, 1, 5, 6, 31 \ Vertex 4 VERTEX 8, 8, -44, 4, 7, 8, 8, 31 \ Vertex 5 VERTEX 8, -8, -44, 4, 5, 8, 8, 31 \ Vertex 6 VERTEX -8, -8, -44, 5, 6, 8, 8, 31 \ Vertex 7 VERTEX -8, 8, -44, 6, 7, 8, 8, 31 \ Vertex 8 VERTEX 12, 12, -44, 4, 7, 8, 8, 8 \ Vertex 9 VERTEX 12, -12, -44, 4, 5, 8, 8, 8 \ Vertex 10 VERTEX -12, -12, -44, 5, 6, 8, 8, 8 \ Vertex 11 VERTEX -12, 12, -44, 6, 7, 8, 8, 8 \ Vertex 12 VERTEX -8, 8, -12, 6, 7, 7, 7, 8 \ Vertex 13 VERTEX -8, -8, -12, 5, 6, 6, 6, 8 \ Vertex 14 VERTEX 8, 8, -12, 4, 7, 7, 7, 8 \ Vertex 15 VERTEX 8, -8, -12, 4, 5, 5, 5, 8 \ Vertex 16 .SHIP_MISSILE_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 1, 2, 31 \ Edge 0 EDGE 0, 2, 2, 3, 31 \ Edge 1 EDGE 0, 3, 0, 3, 31 \ Edge 2 EDGE 0, 4, 0, 1, 31 \ Edge 3 EDGE 1, 2, 4, 2, 31 \ Edge 4 EDGE 1, 4, 1, 5, 31 \ Edge 5 EDGE 3, 4, 0, 6, 31 \ Edge 6 EDGE 2, 3, 3, 7, 31 \ Edge 7 EDGE 2, 5, 4, 7, 31 \ Edge 8 EDGE 1, 6, 4, 5, 31 \ Edge 9 EDGE 4, 7, 5, 6, 31 \ Edge 10 EDGE 3, 8, 6, 7, 31 \ Edge 11 EDGE 7, 8, 6, 8, 31 \ Edge 12 EDGE 5, 8, 7, 8, 31 \ Edge 13 EDGE 5, 6, 4, 8, 31 \ Edge 14 EDGE 6, 7, 5, 8, 31 \ Edge 15 EDGE 6, 10, 5, 8, 8 \ Edge 16 EDGE 5, 9, 7, 8, 8 \ Edge 17 EDGE 8, 12, 7, 8, 8 \ Edge 18 EDGE 7, 11, 5, 8, 8 \ Edge 19 EDGE 9, 15, 4, 7, 8 \ Edge 20 EDGE 10, 16, 4, 5, 8 \ Edge 21 EDGE 12, 13, 6, 7, 8 \ Edge 22 EDGE 11, 14, 5, 6, 8 \ Edge 23 .SHIP_MISSILE_FACES \ normal_x, normal_y, normal_z, visibility FACE -64, 0, 16, 31 \ Face 0 FACE 0, -64, 16, 31 \ Face 1 FACE 64, 0, 16, 31 \ Face 2 FACE 0, 64, 16, 31 \ Face 3 FACE 32, 0, 0, 31 \ Face 4 FACE 0, -32, 0, 31 \ Face 5 FACE -32, 0, 0, 31 \ Face 6 FACE 0, 32, 0, 31 \ Face 7 FACE 0, 0, -176, 31 \ Face 8 \ ****************************************************************************** \ \ Name: SHIP_CORIOLIS \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Coriolis space station \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_CORIOLIS EQUB 0 \ Max. canisters on demise = 0 EQUW 160 * 160 \ Targetable area = 160 * 160 EQUB LO(SHIP_CORIOLIS_EDGES - SHIP_CORIOLIS) \ Edges data offset (low) EQUB LO(SHIP_CORIOLIS_FACES - SHIP_CORIOLIS) \ Faces data offset (low) EQUB 89 \ Max. edge count = (89 - 1) / 4 = 22 EQUB 0 \ Gun vertex = 0 EQUB 54 \ Explosion count = 12, as (4 * n) + 6 = 54 EQUB 96 \ Number of vertices = 96 / 6 = 16 EQUB 28 \ Number of edges = 28 EQUW 0 \ Bounty = 0 EQUB 56 \ Number of faces = 56 / 4 = 14 EQUB 120 \ Visibility distance = 120 EQUB 240 \ Max. energy = 240 EQUB 0 \ Max. speed = 0 EQUB HI(SHIP_CORIOLIS_EDGES - SHIP_CORIOLIS) \ Edges data offset (high) EQUB HI(SHIP_CORIOLIS_FACES - SHIP_CORIOLIS) \ Faces data offset (high) EQUB 0 \ Normals are scaled by = 2^0 = 1 EQUB %00000110 \ Laser power = 0 \ Missiles = 6 .SHIP_CORIOLIS_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 160, 0, 160, 0, 1, 2, 6, 31 \ Vertex 0 VERTEX 0, 160, 160, 0, 2, 3, 8, 31 \ Vertex 1 VERTEX -160, 0, 160, 0, 3, 4, 7, 31 \ Vertex 2 VERTEX 0, -160, 160, 0, 1, 4, 5, 31 \ Vertex 3 VERTEX 160, -160, 0, 1, 5, 6, 10, 31 \ Vertex 4 VERTEX 160, 160, 0, 2, 6, 8, 11, 31 \ Vertex 5 VERTEX -160, 160, 0, 3, 7, 8, 12, 31 \ Vertex 6 VERTEX -160, -160, 0, 4, 5, 7, 9, 31 \ Vertex 7 VERTEX 160, 0, -160, 6, 10, 11, 13, 31 \ Vertex 8 VERTEX 0, 160, -160, 8, 11, 12, 13, 31 \ Vertex 9 VERTEX -160, 0, -160, 7, 9, 12, 13, 31 \ Vertex 10 VERTEX 0, -160, -160, 5, 9, 10, 13, 31 \ Vertex 11 VERTEX 10, -30, 160, 0, 0, 0, 0, 30 \ Vertex 12 VERTEX 10, 30, 160, 0, 0, 0, 0, 30 \ Vertex 13 VERTEX -10, 30, 160, 0, 0, 0, 0, 30 \ Vertex 14 VERTEX -10, -30, 160, 0, 0, 0, 0, 30 \ Vertex 15 .SHIP_CORIOLIS_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 3, 0, 1, 31 \ Edge 0 EDGE 0, 1, 0, 2, 31 \ Edge 1 EDGE 1, 2, 0, 3, 31 \ Edge 2 EDGE 2, 3, 0, 4, 31 \ Edge 3 EDGE 3, 4, 1, 5, 31 \ Edge 4 EDGE 0, 4, 1, 6, 31 \ Edge 5 EDGE 0, 5, 2, 6, 31 \ Edge 6 EDGE 5, 1, 2, 8, 31 \ Edge 7 EDGE 1, 6, 3, 8, 31 \ Edge 8 EDGE 2, 6, 3, 7, 31 \ Edge 9 EDGE 2, 7, 4, 7, 31 \ Edge 10 EDGE 3, 7, 4, 5, 31 \ Edge 11 EDGE 8, 11, 10, 13, 31 \ Edge 12 EDGE 8, 9, 11, 13, 31 \ Edge 13 EDGE 9, 10, 12, 13, 31 \ Edge 14 EDGE 10, 11, 9, 13, 31 \ Edge 15 EDGE 4, 11, 5, 10, 31 \ Edge 16 EDGE 4, 8, 6, 10, 31 \ Edge 17 EDGE 5, 8, 6, 11, 31 \ Edge 18 EDGE 5, 9, 8, 11, 31 \ Edge 19 EDGE 6, 9, 8, 12, 31 \ Edge 20 EDGE 6, 10, 7, 12, 31 \ Edge 21 EDGE 7, 10, 7, 9, 31 \ Edge 22 EDGE 7, 11, 5, 9, 31 \ Edge 23 EDGE 12, 13, 0, 0, 30 \ Edge 24 EDGE 13, 14, 0, 0, 30 \ Edge 25 EDGE 14, 15, 0, 0, 30 \ Edge 26 EDGE 15, 12, 0, 0, 30 \ Edge 27 .SHIP_CORIOLIS_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 0, 160, 31 \ Face 0 FACE 107, -107, 107, 31 \ Face 1 FACE 107, 107, 107, 31 \ Face 2 FACE -107, 107, 107, 31 \ Face 3 FACE -107, -107, 107, 31 \ Face 4 FACE 0, -160, 0, 31 \ Face 5 FACE 160, 0, 0, 31 \ Face 6 FACE -160, 0, 0, 31 \ Face 7 FACE 0, 160, 0, 31 \ Face 8 FACE -107, -107, -107, 31 \ Face 9 FACE 107, -107, -107, 31 \ Face 10 FACE 107, 107, -107, 31 \ Face 11 FACE -107, 107, -107, 31 \ Face 12 FACE 0, 0, -160, 31 \ Face 13 \ ****************************************************************************** \ \ Name: SHIP_ESCAPE_POD \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for an escape pod \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_ESCAPE_POD EQUB 0 + (2 << 4) \ Max. canisters on demise = 0 \ Market item when scooped = 2 + 1 = 3 (slaves) EQUW 16 * 16 \ Targetable area = 16 * 16 EQUB LO(SHIP_ESCAPE_POD_EDGES - SHIP_ESCAPE_POD) \ Edges data offset (low) EQUB LO(SHIP_ESCAPE_POD_FACES - SHIP_ESCAPE_POD) \ Faces data offset (low) EQUB 29 \ Max. edge count = (29 - 1) / 4 = 7 EQUB 0 \ Gun vertex = 0 EQUB 22 \ Explosion count = 4, as (4 * n) + 6 = 22 EQUB 24 \ Number of vertices = 24 / 6 = 4 EQUB 6 \ Number of edges = 6 EQUW 0 \ Bounty = 0 EQUB 16 \ Number of faces = 16 / 4 = 4 EQUB 8 \ Visibility distance = 8 EQUB 17 \ Max. energy = 17 EQUB 8 \ Max. speed = 8 EQUB HI(SHIP_ESCAPE_POD_EDGES - SHIP_ESCAPE_POD) \ Edges data offset (high) EQUB HI(SHIP_ESCAPE_POD_FACES - SHIP_ESCAPE_POD) \ Faces data offset (high) EQUB 4 \ Normals are scaled by = 2^4 = 16 EQUB %00000000 \ Laser power = 0 \ Missiles = 0 .SHIP_ESCAPE_POD_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX -7, 0, 36, 2, 1, 3, 3, 31 \ Vertex 0 VERTEX -7, -14, -12, 2, 0, 3, 3, 31 \ Vertex 1 VERTEX -7, 14, -12, 1, 0, 3, 3, 31 \ Vertex 2 VERTEX 21, 0, 0, 1, 0, 2, 2, 31 \ Vertex 3 .SHIP_ESCAPE_POD_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 3, 2, 31 \ Edge 0 EDGE 1, 2, 3, 0, 31 \ Edge 1 EDGE 2, 3, 1, 0, 31 \ Edge 2 EDGE 3, 0, 2, 1, 31 \ Edge 3 EDGE 0, 2, 3, 1, 31 \ Edge 4 EDGE 3, 1, 2, 0, 31 \ Edge 5 .SHIP_ESCAPE_POD_FACES \ normal_x, normal_y, normal_z, visibility FACE 52, 0, -122, 31 \ Face 0 FACE 39, 103, 30, 31 \ Face 1 FACE 39, -103, 30, 31 \ Face 2 FACE -112, 0, 0, 31 \ Face 3 \ ****************************************************************************** \ \ Name: SHIP_PLATE \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for an alloy plate \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_PLATE EQUB 0 + (8 << 4) \ Max. canisters on demise = 0 \ Market item when scooped = 8 + 1 = 9 (alloys) EQUW 10 * 10 \ Targetable area = 10 * 10 EQUB LO(SHIP_PLATE_EDGES - SHIP_PLATE) \ Edges data offset (low) EQUB LO(SHIP_PLATE_FACES - SHIP_PLATE) \ Faces data offset (low) EQUB 21 \ Max. edge count = (21 - 1) / 4 = 5 EQUB 0 \ Gun vertex = 0 EQUB 10 \ Explosion count = 1, as (4 * n) + 6 = 10 EQUB 24 \ Number of vertices = 24 / 6 = 4 EQUB 4 \ Number of edges = 4 EQUW 0 \ Bounty = 0 EQUB 4 \ Number of faces = 4 / 4 = 1 EQUB 5 \ Visibility distance = 5 EQUB 16 \ Max. energy = 16 EQUB 16 \ Max. speed = 16 EQUB HI(SHIP_PLATE_EDGES - SHIP_PLATE) \ Edges data offset (high) EQUB HI(SHIP_PLATE_FACES - SHIP_PLATE) \ Faces data offset (high) EQUB 3 \ Normals are scaled by = 2^3 = 8 EQUB %00000000 \ Laser power = 0 \ Missiles = 0 .SHIP_PLATE_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX -15, -22, -9, 15, 15, 15, 15, 31 \ Vertex 0 VERTEX -15, 38, -9, 15, 15, 15, 15, 31 \ Vertex 1 VERTEX 19, 32, 11, 15, 15, 15, 15, 20 \ Vertex 2 VERTEX 10, -46, 6, 15, 15, 15, 15, 20 \ Vertex 3 .SHIP_PLATE_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 15, 15, 31 \ Edge 0 EDGE 1, 2, 15, 15, 16 \ Edge 1 EDGE 2, 3, 15, 15, 20 \ Edge 2 EDGE 3, 0, 15, 15, 16 \ Edge 3 .SHIP_PLATE_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 0, 0, 0 \ Face 0 \ ****************************************************************************** \ \ Name: SHIP_CANISTER \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a cargo canister \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_CANISTER EQUB 0 \ Max. canisters on demise = 0 EQUW 20 * 20 \ Targetable area = 20 * 20 EQUB LO(SHIP_CANISTER_EDGES - SHIP_CANISTER) \ Edges data offset (low) EQUB LO(SHIP_CANISTER_FACES - SHIP_CANISTER) \ Faces data offset (low) EQUB 53 \ Max. edge count = (53 - 1) / 4 = 13 EQUB 0 \ Gun vertex = 0 EQUB 18 \ Explosion count = 3, as (4 * n) + 6 = 18 EQUB 60 \ Number of vertices = 60 / 6 = 10 EQUB 15 \ Number of edges = 15 EQUW 0 \ Bounty = 0 EQUB 28 \ Number of faces = 28 / 4 = 7 EQUB 12 \ Visibility distance = 12 EQUB 17 \ Max. energy = 17 EQUB 15 \ Max. speed = 15 EQUB HI(SHIP_CANISTER_EDGES - SHIP_CANISTER) \ Edges data offset (high) EQUB HI(SHIP_CANISTER_FACES - SHIP_CANISTER) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00000000 \ Laser power = 0 \ Missiles = 0 .SHIP_CANISTER_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 24, 16, 0, 0, 1, 5, 5, 31 \ Vertex 0 VERTEX 24, 5, 15, 0, 1, 2, 2, 31 \ Vertex 1 VERTEX 24, -13, 9, 0, 2, 3, 3, 31 \ Vertex 2 VERTEX 24, -13, -9, 0, 3, 4, 4, 31 \ Vertex 3 VERTEX 24, 5, -15, 0, 4, 5, 5, 31 \ Vertex 4 VERTEX -24, 16, 0, 1, 5, 6, 6, 31 \ Vertex 5 VERTEX -24, 5, 15, 1, 2, 6, 6, 31 \ Vertex 6 VERTEX -24, -13, 9, 2, 3, 6, 6, 31 \ Vertex 7 VERTEX -24, -13, -9, 3, 4, 6, 6, 31 \ Vertex 8 VERTEX -24, 5, -15, 4, 5, 6, 6, 31 \ Vertex 9 .SHIP_CANISTER_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 0, 1, 31 \ Edge 0 EDGE 1, 2, 0, 2, 31 \ Edge 1 EDGE 2, 3, 0, 3, 31 \ Edge 2 EDGE 3, 4, 0, 4, 31 \ Edge 3 EDGE 0, 4, 0, 5, 31 \ Edge 4 EDGE 0, 5, 1, 5, 31 \ Edge 5 EDGE 1, 6, 1, 2, 31 \ Edge 6 EDGE 2, 7, 2, 3, 31 \ Edge 7 EDGE 3, 8, 3, 4, 31 \ Edge 8 EDGE 4, 9, 4, 5, 31 \ Edge 9 EDGE 5, 6, 1, 6, 31 \ Edge 10 EDGE 6, 7, 2, 6, 31 \ Edge 11 EDGE 7, 8, 3, 6, 31 \ Edge 12 EDGE 8, 9, 4, 6, 31 \ Edge 13 EDGE 9, 5, 5, 6, 31 \ Edge 14 .SHIP_CANISTER_FACES \ normal_x, normal_y, normal_z, visibility FACE 96, 0, 0, 31 \ Face 0 FACE 0, 41, 30, 31 \ Face 1 FACE 0, -18, 48, 31 \ Face 2 FACE 0, -51, 0, 31 \ Face 3 FACE 0, -18, -48, 31 \ Face 4 FACE 0, 41, -30, 31 \ Face 5 FACE -96, 0, 0, 31 \ Face 6 \ ****************************************************************************** \ \ Name: SHIP_BOULDER \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a boulder \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_BOULDER EQUB 0 \ Max. canisters on demise = 0 EQUW 30 * 30 \ Targetable area = 30 * 30 EQUB LO(SHIP_BOULDER_EDGES - SHIP_BOULDER) \ Edges data offset (low) EQUB LO(SHIP_BOULDER_FACES - SHIP_BOULDER) \ Faces data offset (low) EQUB 49 \ Max. edge count = (49 - 1) / 4 = 12 EQUB 0 \ Gun vertex = 0 EQUB 14 \ Explosion count = 2, as (4 * n) + 6 = 14 EQUB 42 \ Number of vertices = 42 / 6 = 7 EQUB 15 \ Number of edges = 15 EQUW 1 \ Bounty = 1 EQUB 40 \ Number of faces = 40 / 4 = 10 EQUB 20 \ Visibility distance = 20 EQUB 20 \ Max. energy = 20 EQUB 30 \ Max. speed = 30 EQUB HI(SHIP_BOULDER_EDGES - SHIP_BOULDER) \ Edges data offset (high) EQUB HI(SHIP_BOULDER_FACES - SHIP_BOULDER) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00000000 \ Laser power = 0 \ Missiles = 0 .SHIP_BOULDER_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX -18, 37, -11, 1, 0, 9, 5, 31 \ Vertex 0 VERTEX 30, 7, 12, 2, 1, 6, 5, 31 \ Vertex 1 VERTEX 28, -7, -12, 3, 2, 7, 6, 31 \ Vertex 2 VERTEX 2, 0, -39, 4, 3, 8, 7, 31 \ Vertex 3 VERTEX -28, 34, -30, 4, 0, 9, 8, 31 \ Vertex 4 VERTEX 5, -10, 13, 15, 15, 15, 15, 31 \ Vertex 5 VERTEX 20, 17, -30, 15, 15, 15, 15, 31 \ Vertex 6 .SHIP_BOULDER_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 5, 1, 31 \ Edge 0 EDGE 1, 2, 6, 2, 31 \ Edge 1 EDGE 2, 3, 7, 3, 31 \ Edge 2 EDGE 3, 4, 8, 4, 31 \ Edge 3 EDGE 4, 0, 9, 0, 31 \ Edge 4 EDGE 0, 5, 1, 0, 31 \ Edge 5 EDGE 1, 5, 2, 1, 31 \ Edge 6 EDGE 2, 5, 3, 2, 31 \ Edge 7 EDGE 3, 5, 4, 3, 31 \ Edge 8 EDGE 4, 5, 4, 0, 31 \ Edge 9 EDGE 0, 6, 9, 5, 31 \ Edge 10 EDGE 1, 6, 6, 5, 31 \ Edge 11 EDGE 2, 6, 7, 6, 31 \ Edge 12 EDGE 3, 6, 8, 7, 31 \ Edge 13 EDGE 4, 6, 9, 8, 31 \ Edge 14 .SHIP_BOULDER_FACES \ normal_x, normal_y, normal_z, visibility FACE -15, -3, 8, 31 \ Face 0 FACE -7, 12, 30, 31 \ Face 1 FACE 32, -47, 24, 31 \ Face 2 FACE -3, -39, -7, 31 \ Face 3 FACE -5, -4, -1, 31 \ Face 4 FACE 49, 84, 8, 31 \ Face 5 FACE 112, 21, -21, 31 \ Face 6 FACE 76, -35, -82, 31 \ Face 7 FACE 22, 56, -137, 31 \ Face 8 FACE 40, 110, -38, 31 \ Face 9 \ ****************************************************************************** \ \ Name: SHIP_ASTEROID \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for an asteroid \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_ASTEROID EQUB 0 \ Max. canisters on demise = 0 EQUW 80 * 80 \ Targetable area = 80 * 80 EQUB LO(SHIP_ASTEROID_EDGES - SHIP_ASTEROID) \ Edges data offset (low) EQUB LO(SHIP_ASTEROID_FACES - SHIP_ASTEROID) \ Faces data offset (low) EQUB 69 \ Max. edge count = (69 - 1) / 4 = 17 EQUB 0 \ Gun vertex = 0 EQUB 34 \ Explosion count = 7, as (4 * n) + 6 = 34 EQUB 54 \ Number of vertices = 54 / 6 = 9 EQUB 21 \ Number of edges = 21 EQUW 5 \ Bounty = 5 EQUB 56 \ Number of faces = 56 / 4 = 14 EQUB 50 \ Visibility distance = 50 EQUB 60 \ Max. energy = 60 EQUB 30 \ Max. speed = 30 EQUB HI(SHIP_ASTEROID_EDGES - SHIP_ASTEROID) \ Edges data offset (high) EQUB HI(SHIP_ASTEROID_FACES - SHIP_ASTEROID) \ Faces data offset (high) EQUB 1 \ Normals are scaled by = 2^1 = 2 EQUB %00000000 \ Laser power = 0 \ Missiles = 0 .SHIP_ASTEROID_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, 80, 0, 15, 15, 15, 15, 31 \ Vertex 0 VERTEX -80, -10, 0, 15, 15, 15, 15, 31 \ Vertex 1 VERTEX 0, -80, 0, 15, 15, 15, 15, 31 \ Vertex 2 VERTEX 70, -40, 0, 15, 15, 15, 15, 31 \ Vertex 3 VERTEX 60, 50, 0, 5, 6, 12, 13, 31 \ Vertex 4 VERTEX 50, 0, 60, 15, 15, 15, 15, 31 \ Vertex 5 VERTEX -40, 0, 70, 0, 1, 2, 3, 31 \ Vertex 6 VERTEX 0, 30, -75, 15, 15, 15, 15, 31 \ Vertex 7 VERTEX 0, -50, -60, 8, 9, 10, 11, 31 \ Vertex 8 .SHIP_ASTEROID_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 2, 7, 31 \ Edge 0 EDGE 0, 4, 6, 13, 31 \ Edge 1 EDGE 3, 4, 5, 12, 31 \ Edge 2 EDGE 2, 3, 4, 11, 31 \ Edge 3 EDGE 1, 2, 3, 10, 31 \ Edge 4 EDGE 1, 6, 2, 3, 31 \ Edge 5 EDGE 2, 6, 1, 3, 31 \ Edge 6 EDGE 2, 5, 1, 4, 31 \ Edge 7 EDGE 5, 6, 0, 1, 31 \ Edge 8 EDGE 0, 5, 0, 6, 31 \ Edge 9 EDGE 3, 5, 4, 5, 31 \ Edge 10 EDGE 0, 6, 0, 2, 31 \ Edge 11 EDGE 4, 5, 5, 6, 31 \ Edge 12 EDGE 1, 8, 8, 10, 31 \ Edge 13 EDGE 1, 7, 7, 8, 31 \ Edge 14 EDGE 0, 7, 7, 13, 31 \ Edge 15 EDGE 4, 7, 12, 13, 31 \ Edge 16 EDGE 3, 7, 9, 12, 31 \ Edge 17 EDGE 3, 8, 9, 11, 31 \ Edge 18 EDGE 2, 8, 10, 11, 31 \ Edge 19 EDGE 7, 8, 8, 9, 31 \ Edge 20 .SHIP_ASTEROID_FACES \ normal_x, normal_y, normal_z, visibility FACE 9, 66, 81, 31 \ Face 0 FACE 9, -66, 81, 31 \ Face 1 FACE -72, 64, 31, 31 \ Face 2 FACE -64, -73, 47, 31 \ Face 3 FACE 45, -79, 65, 31 \ Face 4 FACE 135, 15, 35, 31 \ Face 5 FACE 38, 76, 70, 31 \ Face 6 FACE -66, 59, -39, 31 \ Face 7 FACE -67, -15, -80, 31 \ Face 8 FACE 66, -14, -75, 31 \ Face 9 FACE -70, -80, -40, 31 \ Face 10 FACE 58, -102, -51, 31 \ Face 11 FACE 81, 9, -67, 31 \ Face 12 FACE 47, 94, -63, 31 \ Face 13 \ ****************************************************************************** \ \ Name: SHIP_SPLINTER \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a splinter \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ------------------------------------------------------------------------------ \ \ The ship blueprint for the splinter reuses the edges data from the escape pod, \ so the edges data offset is negative. \ \ ****************************************************************************** .SHIP_SPLINTER EQUB 0 + (11 << 4) \ Max. canisters on demise = 0 \ Market item when scooped = 11 + 1 = 12 (minerals) EQUW 16 * 16 \ Targetable area = 16 * 16 EQUB LO(SHIP_ESCAPE_POD_EDGES - SHIP_SPLINTER) \ Edges from escape pod EQUB LO(SHIP_SPLINTER_FACES - SHIP_SPLINTER) + 24 \ Faces data offset (low) EQUB 29 \ Max. edge count = (29 - 1) / 4 = 7 EQUB 0 \ Gun vertex = 0 EQUB 22 \ Explosion count = 4, as (4 * n) + 6 = 22 EQUB 24 \ Number of vertices = 24 / 6 = 4 EQUB 6 \ Number of edges = 6 EQUW 0 \ Bounty = 0 EQUB 16 \ Number of faces = 16 / 4 = 4 EQUB 8 \ Visibility distance = 8 EQUB 20 \ Max. energy = 20 EQUB 10 \ Max. speed = 10 EQUB HI(SHIP_ESCAPE_POD_EDGES - SHIP_SPLINTER) \ Edges from escape pod EQUB HI(SHIP_SPLINTER_FACES - SHIP_SPLINTER) \ Faces data offset (low) EQUB 5 \ Normals are scaled by = 2^5 = 32 EQUB %00000000 \ Laser power = 0 \ Missiles = 0 .SHIP_SPLINTER_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX -24, -25, 16, 2, 1, 3, 3, 31 \ Vertex 0 VERTEX 0, 12, -10, 2, 0, 3, 3, 31 \ Vertex 1 VERTEX 11, -6, 2, 1, 0, 3, 3, 31 \ Vertex 2 VERTEX 12, 42, 7, 1, 0, 2, 2, 31 \ Vertex 3 .SHIP_SPLINTER_FACES \ normal_x, normal_y, normal_z, visibility FACE 35, 0, 4, 31 \ Face 0 FACE 3, 4, 8, 31 \ Face 1 FACE 1, 8, 12, 31 \ Face 2 FACE 18, 12, 0, 31 \ Face 3 \ ****************************************************************************** \ \ Name: SHIP_SHUTTLE \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Shuttle \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_SHUTTLE EQUB 15 \ Max. canisters on demise = 15 EQUW 50 * 50 \ Targetable area = 50 * 50 EQUB LO(SHIP_SHUTTLE_EDGES - SHIP_SHUTTLE) \ Edges data offset (low) EQUB LO(SHIP_SHUTTLE_FACES - SHIP_SHUTTLE) \ Faces data offset (low) EQUB 113 \ Max. edge count = (113 - 1) / 4 = 28 EQUB 0 \ Gun vertex = 0 EQUB 38 \ Explosion count = 8, as (4 * n) + 6 = 38 EQUB 114 \ Number of vertices = 114 / 6 = 19 EQUB 30 \ Number of edges = 30 EQUW 0 \ Bounty = 0 EQUB 52 \ Number of faces = 52 / 4 = 13 EQUB 22 \ Visibility distance = 22 EQUB 32 \ Max. energy = 32 EQUB 8 \ Max. speed = 8 EQUB HI(SHIP_SHUTTLE_EDGES - SHIP_SHUTTLE) \ Edges data offset (high) EQUB HI(SHIP_SHUTTLE_FACES - SHIP_SHUTTLE) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00000000 \ Laser power = 0 \ Missiles = 0 .SHIP_SHUTTLE_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, -17, 23, 15, 15, 15, 15, 31 \ Vertex 0 VERTEX -17, 0, 23, 15, 15, 15, 15, 31 \ Vertex 1 VERTEX 0, 18, 23, 15, 15, 15, 15, 31 \ Vertex 2 VERTEX 18, 0, 23, 15, 15, 15, 15, 31 \ Vertex 3 VERTEX -20, -20, -27, 2, 1, 9, 3, 31 \ Vertex 4 VERTEX -20, 20, -27, 4, 3, 9, 5, 31 \ Vertex 5 VERTEX 20, 20, -27, 6, 5, 9, 7, 31 \ Vertex 6 VERTEX 20, -20, -27, 7, 1, 9, 8, 31 \ Vertex 7 VERTEX 5, 0, -27, 9, 9, 9, 9, 16 \ Vertex 8 VERTEX 0, -2, -27, 9, 9, 9, 9, 16 \ Vertex 9 VERTEX -5, 0, -27, 9, 9, 9, 9, 9 \ Vertex 10 VERTEX 0, 3, -27, 9, 9, 9, 9, 9 \ Vertex 11 VERTEX 0, -9, 35, 10, 0, 12, 11, 16 \ Vertex 12 VERTEX 3, -1, 31, 15, 15, 2, 0, 7 \ Vertex 13 VERTEX 4, 11, 25, 1, 0, 4, 15, 8 \ Vertex 14 VERTEX 11, 4, 25, 1, 10, 15, 3, 8 \ Vertex 15 VERTEX -3, -1, 31, 11, 6, 3, 2, 7 \ Vertex 16 VERTEX -3, 11, 25, 8, 15, 0, 12, 8 \ Vertex 17 VERTEX -10, 4, 25, 15, 4, 8, 1, 8 \ Vertex 18 .SHIP_SHUTTLE_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 2, 0, 31 \ Edge 0 EDGE 1, 2, 10, 4, 31 \ Edge 1 EDGE 2, 3, 11, 6, 31 \ Edge 2 EDGE 0, 3, 12, 8, 31 \ Edge 3 EDGE 0, 7, 8, 1, 31 \ Edge 4 EDGE 0, 4, 2, 1, 24 \ Edge 5 EDGE 1, 4, 3, 2, 31 \ Edge 6 EDGE 1, 5, 4, 3, 24 \ Edge 7 EDGE 2, 5, 5, 4, 31 \ Edge 8 EDGE 2, 6, 6, 5, 12 \ Edge 9 EDGE 3, 6, 7, 6, 31 \ Edge 10 EDGE 3, 7, 8, 7, 24 \ Edge 11 EDGE 4, 5, 9, 3, 31 \ Edge 12 EDGE 5, 6, 9, 5, 31 \ Edge 13 EDGE 6, 7, 9, 7, 31 \ Edge 14 EDGE 4, 7, 9, 1, 31 \ Edge 15 EDGE 0, 12, 12, 0, 16 \ Edge 16 EDGE 1, 12, 10, 0, 16 \ Edge 17 EDGE 2, 12, 11, 10, 16 \ Edge 18 EDGE 3, 12, 12, 11, 16 \ Edge 19 EDGE 8, 9, 9, 9, 16 \ Edge 20 EDGE 9, 10, 9, 9, 7 \ Edge 21 EDGE 10, 11, 9, 9, 9 \ Edge 22 EDGE 8, 11, 9, 9, 7 \ Edge 23 EDGE 13, 14, 11, 11, 5 \ Edge 24 EDGE 14, 15, 11, 11, 8 \ Edge 25 EDGE 13, 15, 11, 11, 7 \ Edge 26 EDGE 16, 17, 10, 10, 5 \ Edge 27 EDGE 17, 18, 10, 10, 8 \ Edge 28 EDGE 16, 18, 10, 10, 7 \ Edge 29 .SHIP_SHUTTLE_FACES \ normal_x, normal_y, normal_z, visibility FACE -55, -55, 40, 31 \ Face 0 FACE 0, -74, 4, 31 \ Face 1 FACE -51, -51, 23, 31 \ Face 2 FACE -74, 0, 4, 31 \ Face 3 FACE -51, 51, 23, 31 \ Face 4 FACE 0, 74, 4, 31 \ Face 5 FACE 51, 51, 23, 31 \ Face 6 FACE 74, 0, 4, 31 \ Face 7 FACE 51, -51, 23, 31 \ Face 8 FACE 0, 0, -107, 31 \ Face 9 FACE -41, 41, 90, 31 \ Face 10 FACE 41, 41, 90, 31 \ Face 11 FACE 55, -55, 40, 31 \ Face 12 \ ****************************************************************************** \ \ Name: SHIP_TRANSPORTER \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Transporter \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_TRANSPORTER EQUB 0 \ Max. canisters on demise = 0 EQUW 50 * 50 \ Targetable area = 50 * 50 EQUB LO(SHIP_TRANSPORTER_EDGES - SHIP_TRANSPORTER) \ Edges data offset (low) EQUB LO(SHIP_TRANSPORTER_FACES - SHIP_TRANSPORTER) \ Faces data offset (low) EQUB 149 \ Max. edge count = (149 - 1) / 4 = 37 EQUB 48 \ Gun vertex = 48 / 4 = 12 EQUB 26 \ Explosion count = 5, as (4 * n) + 6 = 26 EQUB 222 \ Number of vertices = 222 / 6 = 37 EQUB 46 \ Number of edges = 46 EQUW 0 \ Bounty = 0 EQUB 56 \ Number of faces = 56 / 4 = 14 EQUB 16 \ Visibility distance = 16 EQUB 32 \ Max. energy = 32 EQUB 10 \ Max. speed = 10 EQUB HI(SHIP_TRANSPORTER_EDGES - SHIP_TRANSPORTER) \ Edges data offset (high) EQUB HI(SHIP_TRANSPORTER_FACES - SHIP_TRANSPORTER) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00000000 \ Laser power = 0 \ Missiles = 0 .SHIP_TRANSPORTER_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, 10, -26, 6, 0, 7, 7, 31 \ Vertex 0 VERTEX -25, 4, -26, 1, 0, 7, 7, 31 \ Vertex 1 VERTEX -28, -3, -26, 1, 0, 2, 2, 31 \ Vertex 2 VERTEX -25, -8, -26, 2, 0, 3, 3, 31 \ Vertex 3 VERTEX 26, -8, -26, 3, 0, 4, 4, 31 \ Vertex 4 VERTEX 29, -3, -26, 4, 0, 5, 5, 31 \ Vertex 5 VERTEX 26, 4, -26, 5, 0, 6, 6, 31 \ Vertex 6 VERTEX 0, 6, 12, 15, 15, 15, 15, 19 \ Vertex 7 VERTEX -30, -1, 12, 7, 1, 9, 8, 31 \ Vertex 8 VERTEX -33, -8, 12, 2, 1, 9, 3, 31 \ Vertex 9 VERTEX 33, -8, 12, 4, 3, 10, 5, 31 \ Vertex 10 VERTEX 30, -1, 12, 6, 5, 11, 10, 31 \ Vertex 11 VERTEX -11, -2, 30, 9, 8, 13, 12, 31 \ Vertex 12 VERTEX -13, -8, 30, 9, 3, 13, 13, 31 \ Vertex 13 VERTEX 14, -8, 30, 10, 3, 13, 13, 31 \ Vertex 14 VERTEX 11, -2, 30, 11, 10, 13, 12, 31 \ Vertex 15 VERTEX -5, 6, 2, 7, 7, 7, 7, 7 \ Vertex 16 VERTEX -18, 3, 2, 7, 7, 7, 7, 7 \ Vertex 17 VERTEX -5, 7, -7, 7, 7, 7, 7, 7 \ Vertex 18 VERTEX -18, 4, -7, 7, 7, 7, 7, 7 \ Vertex 19 VERTEX -11, 6, -14, 7, 7, 7, 7, 7 \ Vertex 20 VERTEX -11, 5, -7, 7, 7, 7, 7, 7 \ Vertex 21 VERTEX 5, 7, -14, 6, 6, 6, 6, 7 \ Vertex 22 VERTEX 18, 4, -14, 6, 6, 6, 6, 7 \ Vertex 23 VERTEX 11, 5, -7, 6, 6, 6, 6, 7 \ Vertex 24 VERTEX 5, 6, -3, 6, 6, 6, 6, 7 \ Vertex 25 VERTEX 18, 3, -3, 6, 6, 6, 6, 7 \ Vertex 26 VERTEX 11, 4, 8, 6, 6, 6, 6, 7 \ Vertex 27 VERTEX 11, 5, -3, 6, 6, 6, 6, 7 \ Vertex 28 VERTEX -16, -8, -13, 3, 3, 3, 3, 6 \ Vertex 29 VERTEX -16, -8, 16, 3, 3, 3, 3, 6 \ Vertex 30 VERTEX 17, -8, -13, 3, 3, 3, 3, 6 \ Vertex 31 VERTEX 17, -8, 16, 3, 3, 3, 3, 6 \ Vertex 32 VERTEX -13, -3, -26, 0, 0, 0, 0, 8 \ Vertex 33 VERTEX 13, -3, -26, 0, 0, 0, 0, 8 \ Vertex 34 VERTEX 9, 3, -26, 0, 0, 0, 0, 5 \ Vertex 35 VERTEX -8, 3, -26, 0, 0, 0, 0, 5 \ Vertex 36 .SHIP_TRANSPORTER_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 7, 0, 31 \ Edge 0 EDGE 1, 2, 1, 0, 31 \ Edge 1 EDGE 2, 3, 2, 0, 31 \ Edge 2 EDGE 3, 4, 3, 0, 31 \ Edge 3 EDGE 4, 5, 4, 0, 31 \ Edge 4 EDGE 5, 6, 5, 0, 31 \ Edge 5 EDGE 0, 6, 6, 0, 31 \ Edge 6 EDGE 0, 7, 7, 6, 16 \ Edge 7 EDGE 1, 8, 7, 1, 31 \ Edge 8 EDGE 2, 9, 2, 1, 11 \ Edge 9 EDGE 3, 9, 3, 2, 31 \ Edge 10 EDGE 4, 10, 4, 3, 31 \ Edge 11 EDGE 5, 10, 5, 4, 11 \ Edge 12 EDGE 6, 11, 6, 5, 31 \ Edge 13 EDGE 7, 8, 8, 7, 17 \ Edge 14 EDGE 8, 9, 9, 1, 17 \ Edge 15 EDGE 10, 11, 10, 5, 17 \ Edge 16 EDGE 7, 11, 11, 6, 17 \ Edge 17 EDGE 7, 15, 12, 11, 19 \ Edge 18 EDGE 7, 12, 12, 8, 19 \ Edge 19 EDGE 8, 12, 9, 8, 16 \ Edge 20 EDGE 9, 13, 9, 3, 31 \ Edge 21 EDGE 10, 14, 10, 3, 31 \ Edge 22 EDGE 11, 15, 11, 10, 16 \ Edge 23 EDGE 12, 13, 13, 9, 31 \ Edge 24 EDGE 13, 14, 13, 3, 31 \ Edge 25 EDGE 14, 15, 13, 10, 31 \ Edge 26 EDGE 12, 15, 13, 12, 31 \ Edge 27 EDGE 16, 17, 7, 7, 7 \ Edge 28 EDGE 18, 19, 7, 7, 7 \ Edge 29 EDGE 19, 20, 7, 7, 7 \ Edge 30 EDGE 18, 20, 7, 7, 7 \ Edge 31 EDGE 20, 21, 7, 7, 7 \ Edge 32 EDGE 22, 23, 6, 6, 7 \ Edge 33 EDGE 23, 24, 6, 6, 7 \ Edge 34 EDGE 24, 22, 6, 6, 7 \ Edge 35 EDGE 25, 26, 6, 6, 7 \ Edge 36 EDGE 26, 27, 6, 6, 7 \ Edge 37 EDGE 25, 27, 6, 6, 7 \ Edge 38 EDGE 27, 28, 6, 6, 7 \ Edge 39 EDGE 29, 30, 3, 3, 6 \ Edge 40 EDGE 31, 32, 3, 3, 6 \ Edge 41 EDGE 33, 34, 0, 0, 8 \ Edge 42 EDGE 34, 35, 0, 0, 5 \ Edge 43 EDGE 35, 36, 0, 0, 5 \ Edge 44 EDGE 36, 33, 0, 0, 5 \ Edge 45 .SHIP_TRANSPORTER_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 0, -103, 31 \ Face 0 FACE -111, 48, -7, 31 \ Face 1 FACE -105, -63, -21, 31 \ Face 2 FACE 0, -34, 0, 31 \ Face 3 FACE 105, -63, -21, 31 \ Face 4 FACE 111, 48, -7, 31 \ Face 5 FACE 8, 32, 3, 31 \ Face 6 FACE -8, 32, 3, 31 \ Face 7 FACE -8, 34, 11, 19 \ Face 8 FACE -75, 32, 79, 31 \ Face 9 FACE 75, 32, 79, 31 \ Face 10 FACE 8, 34, 11, 19 \ Face 11 FACE 0, 38, 17, 31 \ Face 12 FACE 0, 0, 121, 31 \ Face 13 \ ****************************************************************************** \ \ Name: SHIP_COBRA_MK_3 \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Cobra Mk III \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_COBRA_MK_3 EQUB 3 \ Max. canisters on demise = 3 EQUW 95 * 95 \ Targetable area = 95 * 95 EQUB LO(SHIP_COBRA_MK_3_EDGES - SHIP_COBRA_MK_3) \ Edges data offset (low) EQUB LO(SHIP_COBRA_MK_3_FACES - SHIP_COBRA_MK_3) \ Faces data offset (low) EQUB 157 \ Max. edge count = (157 - 1) / 4 = 39 EQUB 84 \ Gun vertex = 84 / 4 = 21 EQUB 42 \ Explosion count = 9, as (4 * n) + 6 = 42 EQUB 168 \ Number of vertices = 168 / 6 = 28 EQUB 38 \ Number of edges = 38 EQUW 0 \ Bounty = 0 EQUB 52 \ Number of faces = 52 / 4 = 13 EQUB 50 \ Visibility distance = 50 EQUB 150 \ Max. energy = 150 EQUB 28 \ Max. speed = 28 EQUB HI(SHIP_COBRA_MK_3_EDGES - SHIP_COBRA_MK_3) \ Edges data offset (low) EQUB HI(SHIP_COBRA_MK_3_FACES - SHIP_COBRA_MK_3) \ Faces data offset (low) EQUB 1 \ Normals are scaled by = 2^1 = 2 EQUB %00010011 \ Laser power = 2 \ Missiles = 3 .SHIP_COBRA_MK_3_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 32, 0, 76, 15, 15, 15, 15, 31 \ Vertex 0 VERTEX -32, 0, 76, 15, 15, 15, 15, 31 \ Vertex 1 VERTEX 0, 26, 24, 15, 15, 15, 15, 31 \ Vertex 2 VERTEX -120, -3, -8, 3, 7, 10, 10, 31 \ Vertex 3 VERTEX 120, -3, -8, 4, 8, 12, 12, 31 \ Vertex 4 VERTEX -88, 16, -40, 15, 15, 15, 15, 31 \ Vertex 5 VERTEX 88, 16, -40, 15, 15, 15, 15, 31 \ Vertex 6 VERTEX 128, -8, -40, 8, 9, 12, 12, 31 \ Vertex 7 VERTEX -128, -8, -40, 7, 9, 10, 10, 31 \ Vertex 8 VERTEX 0, 26, -40, 5, 6, 9, 9, 31 \ Vertex 9 VERTEX -32, -24, -40, 9, 10, 11, 11, 31 \ Vertex 10 VERTEX 32, -24, -40, 9, 11, 12, 12, 31 \ Vertex 11 VERTEX -36, 8, -40, 9, 9, 9, 9, 20 \ Vertex 12 VERTEX -8, 12, -40, 9, 9, 9, 9, 20 \ Vertex 13 VERTEX 8, 12, -40, 9, 9, 9, 9, 20 \ Vertex 14 VERTEX 36, 8, -40, 9, 9, 9, 9, 20 \ Vertex 15 VERTEX 36, -12, -40, 9, 9, 9, 9, 20 \ Vertex 16 VERTEX 8, -16, -40, 9, 9, 9, 9, 20 \ Vertex 17 VERTEX -8, -16, -40, 9, 9, 9, 9, 20 \ Vertex 18 VERTEX -36, -12, -40, 9, 9, 9, 9, 20 \ Vertex 19 VERTEX 0, 0, 76, 0, 11, 11, 11, 6 \ Vertex 20 VERTEX 0, 0, 90, 0, 11, 11, 11, 31 \ Vertex 21 VERTEX -80, -6, -40, 9, 9, 9, 9, 8 \ Vertex 22 VERTEX -80, 6, -40, 9, 9, 9, 9, 8 \ Vertex 23 VERTEX -88, 0, -40, 9, 9, 9, 9, 6 \ Vertex 24 VERTEX 80, 6, -40, 9, 9, 9, 9, 8 \ Vertex 25 VERTEX 88, 0, -40, 9, 9, 9, 9, 6 \ Vertex 26 VERTEX 80, -6, -40, 9, 9, 9, 9, 8 \ Vertex 27 .SHIP_COBRA_MK_3_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 0, 11, 31 \ Edge 0 EDGE 0, 4, 4, 12, 31 \ Edge 1 EDGE 1, 3, 3, 10, 31 \ Edge 2 EDGE 3, 8, 7, 10, 31 \ Edge 3 EDGE 4, 7, 8, 12, 31 \ Edge 4 EDGE 6, 7, 8, 9, 31 \ Edge 5 EDGE 6, 9, 6, 9, 31 \ Edge 6 EDGE 5, 9, 5, 9, 31 \ Edge 7 EDGE 5, 8, 7, 9, 31 \ Edge 8 EDGE 2, 5, 1, 5, 31 \ Edge 9 EDGE 2, 6, 2, 6, 31 \ Edge 10 EDGE 3, 5, 3, 7, 31 \ Edge 11 EDGE 4, 6, 4, 8, 31 \ Edge 12 EDGE 1, 2, 0, 1, 31 \ Edge 13 EDGE 0, 2, 0, 2, 31 \ Edge 14 EDGE 8, 10, 9, 10, 31 \ Edge 15 EDGE 10, 11, 9, 11, 31 \ Edge 16 EDGE 7, 11, 9, 12, 31 \ Edge 17 EDGE 1, 10, 10, 11, 31 \ Edge 18 EDGE 0, 11, 11, 12, 31 \ Edge 19 EDGE 1, 5, 1, 3, 29 \ Edge 20 EDGE 0, 6, 2, 4, 29 \ Edge 21 EDGE 20, 21, 0, 11, 6 \ Edge 22 EDGE 12, 13, 9, 9, 20 \ Edge 23 EDGE 18, 19, 9, 9, 20 \ Edge 24 EDGE 14, 15, 9, 9, 20 \ Edge 25 EDGE 16, 17, 9, 9, 20 \ Edge 26 EDGE 15, 16, 9, 9, 19 \ Edge 27 EDGE 14, 17, 9, 9, 17 \ Edge 28 EDGE 13, 18, 9, 9, 19 \ Edge 29 EDGE 12, 19, 9, 9, 19 \ Edge 30 EDGE 2, 9, 5, 6, 30 \ Edge 31 EDGE 22, 24, 9, 9, 6 \ Edge 32 EDGE 23, 24, 9, 9, 6 \ Edge 33 EDGE 22, 23, 9, 9, 8 \ Edge 34 EDGE 25, 26, 9, 9, 6 \ Edge 35 EDGE 26, 27, 9, 9, 6 \ Edge 36 EDGE 25, 27, 9, 9, 8 \ Edge 37 .SHIP_COBRA_MK_3_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 62, 31, 31 \ Face 0 FACE -18, 55, 16, 31 \ Face 1 FACE 18, 55, 16, 31 \ Face 2 FACE -16, 52, 14, 31 \ Face 3 FACE 16, 52, 14, 31 \ Face 4 FACE -14, 47, 0, 31 \ Face 5 FACE 14, 47, 0, 31 \ Face 6 FACE -61, 102, 0, 31 \ Face 7 FACE 61, 102, 0, 31 \ Face 8 FACE 0, 0, -80, 31 \ Face 9 FACE -7, -42, 9, 31 \ Face 10 FACE 0, -30, 6, 31 \ Face 11 FACE 7, -42, 9, 31 \ Face 12 \ ****************************************************************************** \ \ Name: SHIP_PYTHON \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Python \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_PYTHON EQUB 5 \ Max. canisters on demise = 5 EQUW 80 * 80 \ Targetable area = 80 * 80 EQUB LO(SHIP_PYTHON_EDGES - SHIP_PYTHON) \ Edges data offset (low) EQUB LO(SHIP_PYTHON_FACES - SHIP_PYTHON) \ Faces data offset (low) EQUB 89 \ Max. edge count = (89 - 1) / 4 = 22 EQUB 0 \ Gun vertex = 0 EQUB 42 \ Explosion count = 9, as (4 * n) + 6 = 42 EQUB 66 \ Number of vertices = 66 / 6 = 11 EQUB 26 \ Number of edges = 26 EQUW 0 \ Bounty = 0 EQUB 52 \ Number of faces = 52 / 4 = 13 EQUB 40 \ Visibility distance = 40 EQUB 250 \ Max. energy = 250 EQUB 20 \ Max. speed = 20 EQUB HI(SHIP_PYTHON_EDGES - SHIP_PYTHON) \ Edges data offset (high) EQUB HI(SHIP_PYTHON_FACES - SHIP_PYTHON) \ Faces data offset (high) EQUB 0 \ Normals are scaled by = 2^0 = 1 EQUB %00011011 \ Laser power = 3 \ Missiles = 3 .SHIP_PYTHON_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, 0, 224, 0, 1, 2, 3, 31 \ Vertex 0 VERTEX 0, 48, 48, 0, 1, 4, 5, 31 \ Vertex 1 VERTEX 96, 0, -16, 15, 15, 15, 15, 31 \ Vertex 2 VERTEX -96, 0, -16, 15, 15, 15, 15, 31 \ Vertex 3 VERTEX 0, 48, -32, 4, 5, 8, 9, 31 \ Vertex 4 VERTEX 0, 24, -112, 9, 8, 12, 12, 31 \ Vertex 5 VERTEX -48, 0, -112, 8, 11, 12, 12, 31 \ Vertex 6 VERTEX 48, 0, -112, 9, 10, 12, 12, 31 \ Vertex 7 VERTEX 0, -48, 48, 2, 3, 6, 7, 31 \ Vertex 8 VERTEX 0, -48, -32, 6, 7, 10, 11, 31 \ Vertex 9 VERTEX 0, -24, -112, 10, 11, 12, 12, 31 \ Vertex 10 .SHIP_PYTHON_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 8, 2, 3, 31 \ Edge 0 EDGE 0, 3, 0, 2, 31 \ Edge 1 EDGE 0, 2, 1, 3, 31 \ Edge 2 EDGE 0, 1, 0, 1, 31 \ Edge 3 EDGE 2, 4, 9, 5, 31 \ Edge 4 EDGE 1, 2, 1, 5, 31 \ Edge 5 EDGE 2, 8, 7, 3, 31 \ Edge 6 EDGE 1, 3, 0, 4, 31 \ Edge 7 EDGE 3, 8, 2, 6, 31 \ Edge 8 EDGE 2, 9, 7, 10, 31 \ Edge 9 EDGE 3, 4, 4, 8, 31 \ Edge 10 EDGE 3, 9, 6, 11, 31 \ Edge 11 EDGE 3, 5, 8, 8, 7 \ Edge 12 EDGE 3, 10, 11, 11, 7 \ Edge 13 EDGE 2, 5, 9, 9, 7 \ Edge 14 EDGE 2, 10, 10, 10, 7 \ Edge 15 EDGE 2, 7, 9, 10, 31 \ Edge 16 EDGE 3, 6, 8, 11, 31 \ Edge 17 EDGE 5, 6, 8, 12, 31 \ Edge 18 EDGE 5, 7, 9, 12, 31 \ Edge 19 EDGE 7, 10, 12, 10, 31 \ Edge 20 EDGE 6, 10, 11, 12, 31 \ Edge 21 EDGE 4, 5, 8, 9, 31 \ Edge 22 EDGE 9, 10, 10, 11, 31 \ Edge 23 EDGE 1, 4, 4, 5, 31 \ Edge 24 EDGE 8, 9, 6, 7, 31 \ Edge 25 .SHIP_PYTHON_FACES \ normal_x, normal_y, normal_z, visibility FACE -27, 40, 11, 31 \ Face 0 FACE 27, 40, 11, 31 \ Face 1 FACE -27, -40, 11, 31 \ Face 2 FACE 27, -40, 11, 31 \ Face 3 FACE -19, 38, 0, 31 \ Face 4 FACE 19, 38, 0, 31 \ Face 5 FACE -19, -38, 0, 31 \ Face 6 FACE 19, -38, 0, 31 \ Face 7 FACE -25, 37, -11, 31 \ Face 8 FACE 25, 37, -11, 31 \ Face 9 FACE 25, -37, -11, 31 \ Face 10 FACE -25, -37, -11, 31 \ Face 11 FACE 0, 0, -112, 31 \ Face 12 \ ****************************************************************************** \ \ Name: SHIP_BOA \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Boa \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_BOA EQUB 5 \ Max. canisters on demise = 5 EQUW 70 * 70 \ Targetable area = 70 * 70 EQUB LO(SHIP_BOA_EDGES - SHIP_BOA) \ Edges data offset (low) EQUB LO(SHIP_BOA_FACES - SHIP_BOA) \ Faces data offset (low) EQUB 93 \ Max. edge count = (93 - 1) / 4 = 23 EQUB 0 \ Gun vertex = 0 EQUB 38 \ Explosion count = 8, as (4 * n) + 6 = 38 EQUB 78 \ Number of vertices = 78 / 6 = 13 EQUB 24 \ Number of edges = 24 EQUW 0 \ Bounty = 0 EQUB 52 \ Number of faces = 52 / 4 = 13 EQUB 40 \ Visibility distance = 40 EQUB 250 \ Max. energy = 250 EQUB 24 \ Max. speed = 24 EQUB HI(SHIP_BOA_EDGES - SHIP_BOA) \ Edges data offset (high) EQUB HI(SHIP_BOA_FACES - SHIP_BOA) \ Faces data offset (high) EQUB 0 \ Normals are scaled by = 2^0 = 1 EQUB %00011100 \ Laser power = 3 \ Missiles = 4 .SHIP_BOA_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, 0, 93, 15, 15, 15, 15, 31 \ Vertex 0 VERTEX 0, 40, -87, 2, 0, 3, 3, 24 \ Vertex 1 VERTEX 38, -25, -99, 1, 0, 4, 4, 24 \ Vertex 2 VERTEX -38, -25, -99, 2, 1, 5, 5, 24 \ Vertex 3 VERTEX -38, 40, -59, 3, 2, 9, 6, 31 \ Vertex 4 VERTEX 38, 40, -59, 3, 0, 11, 6, 31 \ Vertex 5 VERTEX 62, 0, -67, 4, 0, 11, 8, 31 \ Vertex 6 VERTEX 24, -65, -79, 4, 1, 10, 8, 31 \ Vertex 7 VERTEX -24, -65, -79, 5, 1, 10, 7, 31 \ Vertex 8 VERTEX -62, 0, -67, 5, 2, 9, 7, 31 \ Vertex 9 VERTEX 0, 7, -107, 2, 0, 10, 10, 22 \ Vertex 10 VERTEX 13, -9, -107, 1, 0, 10, 10, 22 \ Vertex 11 VERTEX -13, -9, -107, 2, 1, 12, 12, 22 \ Vertex 12 .SHIP_BOA_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 5, 11, 6, 31 \ Edge 0 EDGE 0, 7, 10, 8, 31 \ Edge 1 EDGE 0, 9, 9, 7, 31 \ Edge 2 EDGE 0, 4, 9, 6, 29 \ Edge 3 EDGE 0, 6, 11, 8, 29 \ Edge 4 EDGE 0, 8, 10, 7, 29 \ Edge 5 EDGE 4, 5, 6, 3, 31 \ Edge 6 EDGE 5, 6, 11, 0, 31 \ Edge 7 EDGE 6, 7, 8, 4, 31 \ Edge 8 EDGE 7, 8, 10, 1, 31 \ Edge 9 EDGE 8, 9, 7, 5, 31 \ Edge 10 EDGE 4, 9, 9, 2, 31 \ Edge 11 EDGE 1, 4, 3, 2, 24 \ Edge 12 EDGE 1, 5, 3, 0, 24 \ Edge 13 EDGE 3, 9, 5, 2, 24 \ Edge 14 EDGE 3, 8, 5, 1, 24 \ Edge 15 EDGE 2, 6, 4, 0, 24 \ Edge 16 EDGE 2, 7, 4, 1, 24 \ Edge 17 EDGE 1, 10, 2, 0, 22 \ Edge 18 EDGE 2, 11, 1, 0, 22 \ Edge 19 EDGE 3, 12, 2, 1, 22 \ Edge 20 EDGE 10, 11, 12, 0, 14 \ Edge 21 EDGE 11, 12, 12, 1, 14 \ Edge 22 EDGE 12, 10, 12, 2, 14 \ Edge 23 .SHIP_BOA_FACES \ normal_x, normal_y, normal_z, visibility FACE 43, 37, -60, 31 \ Face 0 FACE 0, -45, -89, 31 \ Face 1 FACE -43, 37, -60, 31 \ Face 2 FACE 0, 40, 0, 31 \ Face 3 FACE 62, -32, -20, 31 \ Face 4 FACE -62, -32, -20, 31 \ Face 5 FACE 0, 23, 6, 31 \ Face 6 FACE -23, -15, 9, 31 \ Face 7 FACE 23, -15, 9, 31 \ Face 8 FACE -26, 13, 10, 31 \ Face 9 FACE 0, -31, 12, 31 \ Face 10 FACE 26, 13, 10, 31 \ Face 11 FACE 0, 0, -107, 14 \ Face 12 \ ****************************************************************************** \ \ Name: SHIP_ANACONDA \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for an Anaconda \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_ANACONDA EQUB 7 \ Max. canisters on demise = 7 EQUW 100 * 100 \ Targetable area = 100 * 100 EQUB LO(SHIP_ANACONDA_EDGES - SHIP_ANACONDA) \ Edges data offset (low) EQUB LO(SHIP_ANACONDA_FACES - SHIP_ANACONDA) \ Faces data offset (low) EQUB 93 \ Max. edge count = (93 - 1) / 4 = 23 EQUB 48 \ Gun vertex = 48 / 4 = 12 EQUB 46 \ Explosion count = 10, as (4 * n) + 6 = 46 EQUB 90 \ Number of vertices = 90 / 6 = 15 EQUB 25 \ Number of edges = 25 EQUW 0 \ Bounty = 0 EQUB 48 \ Number of faces = 48 / 4 = 12 EQUB 36 \ Visibility distance = 36 EQUB 252 \ Max. energy = 252 EQUB 14 \ Max. speed = 14 EQUB HI(SHIP_ANACONDA_EDGES - SHIP_ANACONDA) \ Edges data offset (high) EQUB HI(SHIP_ANACONDA_FACES - SHIP_ANACONDA) \ Faces data offset (high) EQUB 1 \ Normals are scaled by = 2^1 = 2 EQUB %00111111 \ Laser power = 7 \ Missiles = 7 .SHIP_ANACONDA_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, 7, -58, 1, 0, 5, 5, 30 \ Vertex 0 VERTEX -43, -13, -37, 1, 0, 2, 2, 30 \ Vertex 1 VERTEX -26, -47, -3, 2, 0, 3, 3, 30 \ Vertex 2 VERTEX 26, -47, -3, 3, 0, 4, 4, 30 \ Vertex 3 VERTEX 43, -13, -37, 4, 0, 5, 5, 30 \ Vertex 4 VERTEX 0, 48, -49, 5, 1, 6, 6, 30 \ Vertex 5 VERTEX -69, 15, -15, 2, 1, 7, 7, 30 \ Vertex 6 VERTEX -43, -39, 40, 3, 2, 8, 8, 31 \ Vertex 7 VERTEX 43, -39, 40, 4, 3, 9, 9, 31 \ Vertex 8 VERTEX 69, 15, -15, 5, 4, 10, 10, 30 \ Vertex 9 VERTEX -43, 53, -23, 15, 15, 15, 15, 31 \ Vertex 10 VERTEX -69, -1, 32, 7, 2, 8, 8, 31 \ Vertex 11 VERTEX 0, 0, 254, 15, 15, 15, 15, 31 \ Vertex 12 VERTEX 69, -1, 32, 9, 4, 10, 10, 31 \ Vertex 13 VERTEX 43, 53, -23, 15, 15, 15, 15, 31 \ Vertex 14 .SHIP_ANACONDA_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 1, 0, 30 \ Edge 0 EDGE 1, 2, 2, 0, 30 \ Edge 1 EDGE 2, 3, 3, 0, 30 \ Edge 2 EDGE 3, 4, 4, 0, 30 \ Edge 3 EDGE 0, 4, 5, 0, 30 \ Edge 4 EDGE 0, 5, 5, 1, 29 \ Edge 5 EDGE 1, 6, 2, 1, 29 \ Edge 6 EDGE 2, 7, 3, 2, 29 \ Edge 7 EDGE 3, 8, 4, 3, 29 \ Edge 8 EDGE 4, 9, 5, 4, 29 \ Edge 9 EDGE 5, 10, 6, 1, 30 \ Edge 10 EDGE 6, 10, 7, 1, 30 \ Edge 11 EDGE 6, 11, 7, 2, 30 \ Edge 12 EDGE 7, 11, 8, 2, 30 \ Edge 13 EDGE 7, 12, 8, 3, 31 \ Edge 14 EDGE 8, 12, 9, 3, 31 \ Edge 15 EDGE 8, 13, 9, 4, 30 \ Edge 16 EDGE 9, 13, 10, 4, 30 \ Edge 17 EDGE 9, 14, 10, 5, 30 \ Edge 18 EDGE 5, 14, 6, 5, 30 \ Edge 19 EDGE 10, 14, 11, 6, 30 \ Edge 20 EDGE 10, 12, 11, 7, 31 \ Edge 21 EDGE 11, 12, 8, 7, 31 \ Edge 22 EDGE 12, 13, 10, 9, 31 \ Edge 23 EDGE 12, 14, 11, 10, 31 \ Edge 24 .SHIP_ANACONDA_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, -51, -49, 30 \ Face 0 FACE -51, 18, -87, 30 \ Face 1 FACE -77, -57, -19, 30 \ Face 2 FACE 0, -90, 16, 31 \ Face 3 FACE 77, -57, -19, 30 \ Face 4 FACE 51, 18, -87, 30 \ Face 5 FACE 0, 111, -20, 30 \ Face 6 FACE -97, 72, 24, 31 \ Face 7 FACE -108, -68, 34, 31 \ Face 8 FACE 108, -68, 34, 31 \ Face 9 FACE 97, 72, 24, 31 \ Face 10 FACE 0, 94, 18, 31 \ Face 11 \ ****************************************************************************** \ \ Name: SHIP_ROCK_HERMIT \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a rock hermit (asteroid) \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_ROCK_HERMIT EQUB 7 \ Max. canisters on demise = 7 EQUW 80 * 80 \ Targetable area = 80 * 80 EQUB LO(SHIP_ROCK_HERMIT_EDGES - SHIP_ROCK_HERMIT) \ Edges data offset (low) EQUB LO(SHIP_ROCK_HERMIT_FACES - SHIP_ROCK_HERMIT) \ Faces data offset (low) EQUB 69 \ Max. edge count = (69 - 1) / 4 = 17 EQUB 0 \ Gun vertex = 0 EQUB 50 \ Explosion count = 11, as (4 * n) + 6 = 50 EQUB 54 \ Number of vertices = 54 / 6 = 9 EQUB 21 \ Number of edges = 21 EQUW 0 \ Bounty = 0 EQUB 56 \ Number of faces = 56 / 4 = 14 EQUB 50 \ Visibility distance = 50 EQUB 180 \ Max. energy = 180 EQUB 30 \ Max. speed = 30 EQUB HI(SHIP_ROCK_HERMIT_EDGES - SHIP_ROCK_HERMIT) \ Edges data offset (high) EQUB HI(SHIP_ROCK_HERMIT_FACES - SHIP_ROCK_HERMIT) \ Faces data offset (high) EQUB 1 \ Normals are scaled by = 2^1 = 2 EQUB %00000010 \ Laser power = 0 \ Missiles = 2 .SHIP_ROCK_HERMIT_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, 80, 0, 15, 15, 15, 15, 31 \ Vertex 0 VERTEX -80, -10, 0, 15, 15, 15, 15, 31 \ Vertex 1 VERTEX 0, -80, 0, 15, 15, 15, 15, 31 \ Vertex 2 VERTEX 70, -40, 0, 15, 15, 15, 15, 31 \ Vertex 3 VERTEX 60, 50, 0, 5, 6, 12, 13, 31 \ Vertex 4 VERTEX 50, 0, 60, 15, 15, 15, 15, 31 \ Vertex 5 VERTEX -40, 0, 70, 0, 1, 2, 3, 31 \ Vertex 6 VERTEX 0, 30, -75, 15, 15, 15, 15, 31 \ Vertex 7 VERTEX 0, -50, -60, 8, 9, 10, 11, 31 \ Vertex 8 .SHIP_ROCK_HERMIT_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 2, 7, 31 \ Edge 0 EDGE 0, 4, 6, 13, 31 \ Edge 1 EDGE 3, 4, 5, 12, 31 \ Edge 2 EDGE 2, 3, 4, 11, 31 \ Edge 3 EDGE 1, 2, 3, 10, 31 \ Edge 4 EDGE 1, 6, 2, 3, 31 \ Edge 5 EDGE 2, 6, 1, 3, 31 \ Edge 6 EDGE 2, 5, 1, 4, 31 \ Edge 7 EDGE 5, 6, 0, 1, 31 \ Edge 8 EDGE 0, 5, 0, 6, 31 \ Edge 9 EDGE 3, 5, 4, 5, 31 \ Edge 10 EDGE 0, 6, 0, 2, 31 \ Edge 11 EDGE 4, 5, 5, 6, 31 \ Edge 12 EDGE 1, 8, 8, 10, 31 \ Edge 13 EDGE 1, 7, 7, 8, 31 \ Edge 14 EDGE 0, 7, 7, 13, 31 \ Edge 15 EDGE 4, 7, 12, 13, 31 \ Edge 16 EDGE 3, 7, 9, 12, 31 \ Edge 17 EDGE 3, 8, 9, 11, 31 \ Edge 18 EDGE 2, 8, 10, 11, 31 \ Edge 19 EDGE 7, 8, 8, 9, 31 \ Edge 20 .SHIP_ROCK_HERMIT_FACES \ normal_x, normal_y, normal_z, visibility FACE 9, 66, 81, 31 \ Face 0 FACE 9, -66, 81, 31 \ Face 1 FACE -72, 64, 31, 31 \ Face 2 FACE -64, -73, 47, 31 \ Face 3 FACE 45, -79, 65, 31 \ Face 4 FACE 135, 15, 35, 31 \ Face 5 FACE 38, 76, 70, 31 \ Face 6 FACE -66, 59, -39, 31 \ Face 7 FACE -67, -15, -80, 31 \ Face 8 FACE 66, -14, -75, 31 \ Face 9 FACE -70, -80, -40, 31 \ Face 10 FACE 58, -102, -51, 31 \ Face 11 FACE 81, 9, -67, 31 \ Face 12 FACE 47, 94, -63, 31 \ Face 13 \ ****************************************************************************** \ \ Name: SHIP_VIPER \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Viper \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_VIPER EQUB 0 \ Max. canisters on demise = 0 EQUW 75 * 75 \ Targetable area = 75 * 75 EQUB LO(SHIP_VIPER_EDGES - SHIP_VIPER) \ Edges data offset (low) EQUB LO(SHIP_VIPER_FACES - SHIP_VIPER) \ Faces data offset (low) EQUB 81 \ Max. edge count = (81 - 1) / 4 = 20 EQUB 0 \ Gun vertex = 0 EQUB 42 \ Explosion count = 9, as (4 * n) + 6 = 42 EQUB 90 \ Number of vertices = 90 / 6 = 15 EQUB 20 \ Number of edges = 20 EQUW 0 \ Bounty = 0 EQUB 28 \ Number of faces = 28 / 4 = 7 EQUB 23 \ Visibility distance = 23 EQUB 140 \ Max. energy = 140 EQUB 32 \ Max. speed = 32 EQUB HI(SHIP_VIPER_EDGES - SHIP_VIPER) \ Edges data offset (high) EQUB HI(SHIP_VIPER_FACES - SHIP_VIPER) \ Faces data offset (high) EQUB 1 \ Normals are scaled by = 2^1 = 2 EQUB %00010001 \ Laser power = 2 \ Missiles = 1 .SHIP_VIPER_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, 0, 72, 1, 2, 3, 4, 31 \ Vertex 0 VERTEX 0, 16, 24, 0, 1, 2, 2, 30 \ Vertex 1 VERTEX 0, -16, 24, 3, 4, 5, 5, 30 \ Vertex 2 VERTEX 48, 0, -24, 2, 4, 6, 6, 31 \ Vertex 3 VERTEX -48, 0, -24, 1, 3, 6, 6, 31 \ Vertex 4 VERTEX 24, -16, -24, 4, 5, 6, 6, 30 \ Vertex 5 VERTEX -24, -16, -24, 5, 3, 6, 6, 30 \ Vertex 6 VERTEX 24, 16, -24, 0, 2, 6, 6, 31 \ Vertex 7 VERTEX -24, 16, -24, 0, 1, 6, 6, 31 \ Vertex 8 VERTEX -32, 0, -24, 6, 6, 6, 6, 19 \ Vertex 9 VERTEX 32, 0, -24, 6, 6, 6, 6, 19 \ Vertex 10 VERTEX 8, 8, -24, 6, 6, 6, 6, 19 \ Vertex 11 VERTEX -8, 8, -24, 6, 6, 6, 6, 19 \ Vertex 12 VERTEX -8, -8, -24, 6, 6, 6, 6, 18 \ Vertex 13 VERTEX 8, -8, -24, 6, 6, 6, 6, 18 \ Vertex 14 .SHIP_VIPER_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 3, 2, 4, 31 \ Edge 0 EDGE 0, 1, 1, 2, 30 \ Edge 1 EDGE 0, 2, 3, 4, 30 \ Edge 2 EDGE 0, 4, 1, 3, 31 \ Edge 3 EDGE 1, 7, 0, 2, 30 \ Edge 4 EDGE 1, 8, 0, 1, 30 \ Edge 5 EDGE 2, 5, 4, 5, 30 \ Edge 6 EDGE 2, 6, 3, 5, 30 \ Edge 7 EDGE 7, 8, 0, 6, 31 \ Edge 8 EDGE 5, 6, 5, 6, 30 \ Edge 9 EDGE 4, 8, 1, 6, 31 \ Edge 10 EDGE 4, 6, 3, 6, 30 \ Edge 11 EDGE 3, 7, 2, 6, 31 \ Edge 12 EDGE 3, 5, 6, 4, 30 \ Edge 13 EDGE 9, 12, 6, 6, 19 \ Edge 14 EDGE 9, 13, 6, 6, 18 \ Edge 15 EDGE 10, 11, 6, 6, 19 \ Edge 16 EDGE 10, 14, 6, 6, 18 \ Edge 17 EDGE 11, 14, 6, 6, 16 \ Edge 18 EDGE 12, 13, 6, 6, 16 \ Edge 19 .SHIP_VIPER_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 32, 0, 31 \ Face 0 FACE -22, 33, 11, 31 \ Face 1 FACE 22, 33, 11, 31 \ Face 2 FACE -22, -33, 11, 31 \ Face 3 FACE 22, -33, 11, 31 \ Face 4 FACE 0, -32, 0, 31 \ Face 5 FACE 0, 0, -48, 31 \ Face 6 \ ****************************************************************************** \ \ Name: SHIP_SIDEWINDER \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Sidewinder \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_SIDEWINDER EQUB 0 \ Max. canisters on demise = 0 EQUW 65 * 65 \ Targetable area = 65 * 65 EQUB LO(SHIP_SIDEWINDER_EDGES - SHIP_SIDEWINDER) \ Edges data offset (low) EQUB LO(SHIP_SIDEWINDER_FACES - SHIP_SIDEWINDER) \ Faces data offset (low) EQUB 65 \ Max. edge count = (65 - 1) / 4 = 16 EQUB 0 \ Gun vertex = 0 EQUB 30 \ Explosion count = 6, as (4 * n) + 6 = 30 EQUB 60 \ Number of vertices = 60 / 6 = 10 EQUB 15 \ Number of edges = 15 EQUW 50 \ Bounty = 50 EQUB 28 \ Number of faces = 28 / 4 = 7 EQUB 20 \ Visibility distance = 20 EQUB 70 \ Max. energy = 70 EQUB 37 \ Max. speed = 37 EQUB HI(SHIP_SIDEWINDER_EDGES - SHIP_SIDEWINDER) \ Edges data offset (high) EQUB HI(SHIP_SIDEWINDER_FACES - SHIP_SIDEWINDER) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00010000 \ Laser power = 2 \ Missiles = 0 .SHIP_SIDEWINDER_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX -32, 0, 36, 0, 1, 4, 5, 31 \ Vertex 0 VERTEX 32, 0, 36, 0, 2, 5, 6, 31 \ Vertex 1 VERTEX 64, 0, -28, 2, 3, 6, 6, 31 \ Vertex 2 VERTEX -64, 0, -28, 1, 3, 4, 4, 31 \ Vertex 3 VERTEX 0, 16, -28, 0, 1, 2, 3, 31 \ Vertex 4 VERTEX 0, -16, -28, 3, 4, 5, 6, 31 \ Vertex 5 VERTEX -12, 6, -28, 3, 3, 3, 3, 15 \ Vertex 6 VERTEX 12, 6, -28, 3, 3, 3, 3, 15 \ Vertex 7 VERTEX 12, -6, -28, 3, 3, 3, 3, 12 \ Vertex 8 VERTEX -12, -6, -28, 3, 3, 3, 3, 12 \ Vertex 9 .SHIP_SIDEWINDER_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 0, 5, 31 \ Edge 0 EDGE 1, 2, 2, 6, 31 \ Edge 1 EDGE 1, 4, 0, 2, 31 \ Edge 2 EDGE 0, 4, 0, 1, 31 \ Edge 3 EDGE 0, 3, 1, 4, 31 \ Edge 4 EDGE 3, 4, 1, 3, 31 \ Edge 5 EDGE 2, 4, 2, 3, 31 \ Edge 6 EDGE 3, 5, 3, 4, 31 \ Edge 7 EDGE 2, 5, 3, 6, 31 \ Edge 8 EDGE 1, 5, 5, 6, 31 \ Edge 9 EDGE 0, 5, 4, 5, 31 \ Edge 10 EDGE 6, 7, 3, 3, 15 \ Edge 11 EDGE 7, 8, 3, 3, 12 \ Edge 12 EDGE 6, 9, 3, 3, 12 \ Edge 13 EDGE 8, 9, 3, 3, 12 \ Edge 14 .SHIP_SIDEWINDER_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 32, 8, 31 \ Face 0 FACE -12, 47, 6, 31 \ Face 1 FACE 12, 47, 6, 31 \ Face 2 FACE 0, 0, -112, 31 \ Face 3 FACE -12, -47, 6, 31 \ Face 4 FACE 0, -32, 8, 31 \ Face 5 FACE 12, -47, 6, 31 \ Face 6 \ ****************************************************************************** \ \ Name: SHIP_MAMBA \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Mamba \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_MAMBA EQUB 1 \ Max. canisters on demise = 1 EQUW 70 * 70 \ Targetable area = 70 * 70 EQUB LO(SHIP_MAMBA_EDGES - SHIP_MAMBA) \ Edges data offset (low) EQUB LO(SHIP_MAMBA_FACES - SHIP_MAMBA) \ Faces data offset (low) EQUB 97 \ Max. edge count = (97 - 1) / 4 = 24 EQUB 0 \ Gun vertex = 0 EQUB 34 \ Explosion count = 7, as (4 * n) + 6 = 34 EQUB 150 \ Number of vertices = 150 / 6 = 25 EQUB 28 \ Number of edges = 28 EQUW 150 \ Bounty = 150 EQUB 20 \ Number of faces = 20 / 4 = 5 EQUB 25 \ Visibility distance = 25 EQUB 90 \ Max. energy = 90 EQUB 30 \ Max. speed = 30 EQUB HI(SHIP_MAMBA_EDGES - SHIP_MAMBA) \ Edges data offset (high) EQUB HI(SHIP_MAMBA_FACES - SHIP_MAMBA) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00010010 \ Laser power = 2 \ Missiles = 2 .SHIP_MAMBA_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, 0, 64, 0, 1, 2, 3, 31 \ Vertex 0 VERTEX -64, -8, -32, 0, 2, 4, 4, 31 \ Vertex 1 VERTEX -32, 8, -32, 1, 2, 4, 4, 30 \ Vertex 2 VERTEX 32, 8, -32, 1, 3, 4, 4, 30 \ Vertex 3 VERTEX 64, -8, -32, 0, 3, 4, 4, 31 \ Vertex 4 VERTEX -4, 4, 16, 1, 1, 1, 1, 14 \ Vertex 5 VERTEX 4, 4, 16, 1, 1, 1, 1, 14 \ Vertex 6 VERTEX 8, 3, 28, 1, 1, 1, 1, 13 \ Vertex 7 VERTEX -8, 3, 28, 1, 1, 1, 1, 13 \ Vertex 8 VERTEX -20, -4, 16, 0, 0, 0, 0, 20 \ Vertex 9 VERTEX 20, -4, 16, 0, 0, 0, 0, 20 \ Vertex 10 VERTEX -24, -7, -20, 0, 0, 0, 0, 20 \ Vertex 11 VERTEX -16, -7, -20, 0, 0, 0, 0, 16 \ Vertex 12 VERTEX 16, -7, -20, 0, 0, 0, 0, 16 \ Vertex 13 VERTEX 24, -7, -20, 0, 0, 0, 0, 20 \ Vertex 14 VERTEX -8, 4, -32, 4, 4, 4, 4, 13 \ Vertex 15 VERTEX 8, 4, -32, 4, 4, 4, 4, 13 \ Vertex 16 VERTEX 8, -4, -32, 4, 4, 4, 4, 14 \ Vertex 17 VERTEX -8, -4, -32, 4, 4, 4, 4, 14 \ Vertex 18 VERTEX -32, 4, -32, 4, 4, 4, 4, 7 \ Vertex 19 VERTEX 32, 4, -32, 4, 4, 4, 4, 7 \ Vertex 20 VERTEX 36, -4, -32, 4, 4, 4, 4, 7 \ Vertex 21 VERTEX -36, -4, -32, 4, 4, 4, 4, 7 \ Vertex 22 VERTEX -38, 0, -32, 4, 4, 4, 4, 5 \ Vertex 23 VERTEX 38, 0, -32, 4, 4, 4, 4, 5 \ Vertex 24 .SHIP_MAMBA_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 0, 2, 31 \ Edge 0 EDGE 0, 4, 0, 3, 31 \ Edge 1 EDGE 1, 4, 0, 4, 31 \ Edge 2 EDGE 1, 2, 2, 4, 30 \ Edge 3 EDGE 2, 3, 1, 4, 30 \ Edge 4 EDGE 3, 4, 3, 4, 30 \ Edge 5 EDGE 5, 6, 1, 1, 14 \ Edge 6 EDGE 6, 7, 1, 1, 12 \ Edge 7 EDGE 7, 8, 1, 1, 13 \ Edge 8 EDGE 5, 8, 1, 1, 12 \ Edge 9 EDGE 9, 11, 0, 0, 20 \ Edge 10 EDGE 9, 12, 0, 0, 16 \ Edge 11 EDGE 10, 13, 0, 0, 16 \ Edge 12 EDGE 10, 14, 0, 0, 20 \ Edge 13 EDGE 13, 14, 0, 0, 14 \ Edge 14 EDGE 11, 12, 0, 0, 14 \ Edge 15 EDGE 15, 16, 4, 4, 13 \ Edge 16 EDGE 17, 18, 4, 4, 14 \ Edge 17 EDGE 15, 18, 4, 4, 12 \ Edge 18 EDGE 16, 17, 4, 4, 12 \ Edge 19 EDGE 20, 21, 4, 4, 7 \ Edge 20 EDGE 20, 24, 4, 4, 5 \ Edge 21 EDGE 21, 24, 4, 4, 5 \ Edge 22 EDGE 19, 22, 4, 4, 7 \ Edge 23 EDGE 19, 23, 4, 4, 5 \ Edge 24 EDGE 22, 23, 4, 4, 5 \ Edge 25 EDGE 0, 2, 1, 2, 30 \ Edge 26 EDGE 0, 3, 1, 3, 30 \ Edge 27 .SHIP_MAMBA_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, -24, 2, 30 \ Face 0 FACE 0, 24, 2, 30 \ Face 1 FACE -32, 64, 16, 30 \ Face 2 FACE 32, 64, 16, 30 \ Face 3 FACE 0, 0, -127, 30 \ Face 4 \ ****************************************************************************** \ \ Name: SHIP_KRAIT \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Krait \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_KRAIT EQUB 1 \ Max. canisters on demise = 1 EQUW 60 * 60 \ Targetable area = 60 * 60 EQUB LO(SHIP_KRAIT_EDGES - SHIP_KRAIT) \ Edges data offset (low) EQUB LO(SHIP_KRAIT_FACES - SHIP_KRAIT) \ Faces data offset (low) EQUB 89 \ Max. edge count = (89 - 1) / 4 = 22 EQUB 0 \ Gun vertex = 0 EQUB 18 \ Explosion count = 3, as (4 * n) + 6 = 18 EQUB 102 \ Number of vertices = 102 / 6 = 17 EQUB 21 \ Number of edges = 21 EQUW 100 \ Bounty = 100 EQUB 24 \ Number of faces = 24 / 4 = 6 EQUB 20 \ Visibility distance = 20 EQUB 80 \ Max. energy = 80 EQUB 30 \ Max. speed = 30 EQUB HI(SHIP_KRAIT_EDGES - SHIP_KRAIT) \ Edges data offset (high) EQUB HI(SHIP_KRAIT_FACES - SHIP_KRAIT) \ Faces data offset (high) EQUB 1 \ Normals are scaled by = 2^1 = 2 EQUB %00010000 \ Laser power = 2 \ Missiles = 0 .SHIP_KRAIT_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, 0, 96, 1, 0, 3, 2, 31 \ Vertex 0 VERTEX 0, 18, -48, 3, 0, 5, 4, 31 \ Vertex 1 VERTEX 0, -18, -48, 2, 1, 5, 4, 31 \ Vertex 2 VERTEX 90, 0, -3, 1, 0, 4, 4, 31 \ Vertex 3 VERTEX -90, 0, -3, 3, 2, 5, 5, 31 \ Vertex 4 VERTEX 90, 0, 87, 1, 0, 1, 1, 30 \ Vertex 5 VERTEX -90, 0, 87, 3, 2, 3, 3, 30 \ Vertex 6 VERTEX 0, 5, 53, 0, 0, 3, 3, 9 \ Vertex 7 VERTEX 0, 7, 38, 0, 0, 3, 3, 6 \ Vertex 8 VERTEX -18, 7, 19, 3, 3, 3, 3, 9 \ Vertex 9 VERTEX 18, 7, 19, 0, 0, 0, 0, 9 \ Vertex 10 VERTEX 18, 11, -39, 4, 4, 4, 4, 8 \ Vertex 11 VERTEX 18, -11, -39, 4, 4, 4, 4, 8 \ Vertex 12 VERTEX 36, 0, -30, 4, 4, 4, 4, 8 \ Vertex 13 VERTEX -18, 11, -39, 5, 5, 5, 5, 8 \ Vertex 14 VERTEX -18, -11, -39, 5, 5, 5, 5, 8 \ Vertex 15 VERTEX -36, 0, -30, 5, 5, 5, 5, 8 \ Vertex 16 .SHIP_KRAIT_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 3, 0, 31 \ Edge 0 EDGE 0, 2, 2, 1, 31 \ Edge 1 EDGE 0, 3, 1, 0, 31 \ Edge 2 EDGE 0, 4, 3, 2, 31 \ Edge 3 EDGE 1, 4, 5, 3, 31 \ Edge 4 EDGE 4, 2, 5, 2, 31 \ Edge 5 EDGE 2, 3, 4, 1, 31 \ Edge 6 EDGE 3, 1, 4, 0, 31 \ Edge 7 EDGE 3, 5, 1, 0, 30 \ Edge 8 EDGE 4, 6, 3, 2, 30 \ Edge 9 EDGE 1, 2, 5, 4, 8 \ Edge 10 EDGE 7, 10, 0, 0, 9 \ Edge 11 EDGE 8, 10, 0, 0, 6 \ Edge 12 EDGE 7, 9, 3, 3, 9 \ Edge 13 EDGE 8, 9, 3, 3, 6 \ Edge 14 EDGE 11, 13, 4, 4, 8 \ Edge 15 EDGE 13, 12, 4, 4, 8 \ Edge 16 EDGE 12, 11, 4, 4, 7 \ Edge 17 EDGE 14, 15, 5, 5, 7 \ Edge 18 EDGE 15, 16, 5, 5, 8 \ Edge 19 EDGE 16, 14, 5, 5, 8 \ Edge 20 .SHIP_KRAIT_FACES \ normal_x, normal_y, normal_z, visibility FACE 3, 24, 3, 31 \ Face 0 FACE 3, -24, 3, 31 \ Face 1 FACE -3, -24, 3, 31 \ Face 2 FACE -3, 24, 3, 31 \ Face 3 FACE 38, 0, -77, 31 \ Face 4 FACE -38, 0, -77, 31 \ Face 5 \ ****************************************************************************** \ \ Name: SHIP_ADDER \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for an Adder \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_ADDER EQUB 0 \ Max. canisters on demise = 0 EQUW 50 * 50 \ Targetable area = 50 * 50 EQUB LO(SHIP_ADDER_EDGES - SHIP_ADDER) \ Edges data offset (low) EQUB LO(SHIP_ADDER_FACES - SHIP_ADDER) \ Faces data offset (low) EQUB 101 \ Max. edge count = (101 - 1) / 4 = 25 EQUB 0 \ Gun vertex = 0 EQUB 22 \ Explosion count = 4, as (4 * n) + 6 = 22 EQUB 108 \ Number of vertices = 108 / 6 = 18 EQUB 29 \ Number of edges = 29 EQUW 40 \ Bounty = 40 EQUB 60 \ Number of faces = 60 / 4 = 15 EQUB 20 \ Visibility distance = 20 EQUB 85 \ Max. energy = 85 EQUB 24 \ Max. speed = 24 EQUB HI(SHIP_ADDER_EDGES - SHIP_ADDER) \ Edges data offset (high) EQUB HI(SHIP_ADDER_FACES - SHIP_ADDER) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00010000 \ Laser power = 2 \ Missiles = 0 .SHIP_ADDER_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX -18, 0, 40, 1, 0, 12, 11, 31 \ Vertex 0 VERTEX 18, 0, 40, 1, 0, 3, 2, 31 \ Vertex 1 VERTEX 30, 0, -24, 3, 2, 5, 4, 31 \ Vertex 2 VERTEX 30, 0, -40, 5, 4, 6, 6, 31 \ Vertex 3 VERTEX 18, -7, -40, 6, 5, 14, 7, 31 \ Vertex 4 VERTEX -18, -7, -40, 8, 7, 14, 10, 31 \ Vertex 5 VERTEX -30, 0, -40, 9, 8, 10, 10, 31 \ Vertex 6 VERTEX -30, 0, -24, 10, 9, 12, 11, 31 \ Vertex 7 VERTEX -18, 7, -40, 8, 7, 13, 9, 31 \ Vertex 8 VERTEX 18, 7, -40, 6, 4, 13, 7, 31 \ Vertex 9 VERTEX -18, 7, 13, 9, 0, 13, 11, 31 \ Vertex 10 VERTEX 18, 7, 13, 2, 0, 13, 4, 31 \ Vertex 11 VERTEX -18, -7, 13, 10, 1, 14, 12, 31 \ Vertex 12 VERTEX 18, -7, 13, 3, 1, 14, 5, 31 \ Vertex 13 VERTEX -11, 3, 29, 0, 0, 0, 0, 5 \ Vertex 14 VERTEX 11, 3, 29, 0, 0, 0, 0, 5 \ Vertex 15 VERTEX 11, 4, 24, 0, 0, 0, 0, 4 \ Vertex 16 VERTEX -11, 4, 24, 0, 0, 0, 0, 4 \ Vertex 17 .SHIP_ADDER_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 1, 0, 31 \ Edge 0 EDGE 1, 2, 3, 2, 7 \ Edge 1 EDGE 2, 3, 5, 4, 31 \ Edge 2 EDGE 3, 4, 6, 5, 31 \ Edge 3 EDGE 4, 5, 14, 7, 31 \ Edge 4 EDGE 5, 6, 10, 8, 31 \ Edge 5 EDGE 6, 7, 10, 9, 31 \ Edge 6 EDGE 7, 0, 12, 11, 7 \ Edge 7 EDGE 3, 9, 6, 4, 31 \ Edge 8 EDGE 9, 8, 13, 7, 31 \ Edge 9 EDGE 8, 6, 9, 8, 31 \ Edge 10 EDGE 0, 10, 11, 0, 31 \ Edge 11 EDGE 7, 10, 11, 9, 31 \ Edge 12 EDGE 1, 11, 2, 0, 31 \ Edge 13 EDGE 2, 11, 4, 2, 31 \ Edge 14 EDGE 0, 12, 12, 1, 31 \ Edge 15 EDGE 7, 12, 12, 10, 31 \ Edge 16 EDGE 1, 13, 3, 1, 31 \ Edge 17 EDGE 2, 13, 5, 3, 31 \ Edge 18 EDGE 10, 11, 13, 0, 31 \ Edge 19 EDGE 12, 13, 14, 1, 31 \ Edge 20 EDGE 8, 10, 13, 9, 31 \ Edge 21 EDGE 9, 11, 13, 4, 31 \ Edge 22 EDGE 5, 12, 14, 10, 31 \ Edge 23 EDGE 4, 13, 14, 5, 31 \ Edge 24 EDGE 14, 15, 0, 0, 5 \ Edge 25 EDGE 15, 16, 0, 0, 3 \ Edge 26 EDGE 16, 17, 0, 0, 4 \ Edge 27 EDGE 17, 14, 0, 0, 3 \ Edge 28 .SHIP_ADDER_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 39, 10, 31 \ Face 0 FACE 0, -39, 10, 31 \ Face 1 FACE 69, 50, 13, 31 \ Face 2 FACE 69, -50, 13, 31 \ Face 3 FACE 30, 52, 0, 31 \ Face 4 FACE 30, -52, 0, 31 \ Face 5 FACE 0, 0, -160, 31 \ Face 6 FACE 0, 0, -160, 31 \ Face 7 FACE 0, 0, -160, 31 \ Face 8 FACE -30, 52, 0, 31 \ Face 9 FACE -30, -52, 0, 31 \ Face 10 FACE -69, 50, 13, 31 \ Face 11 FACE -69, -50, 13, 31 \ Face 12 FACE 0, 28, 0, 31 \ Face 13 FACE 0, -28, 0, 31 \ Face 14 \ ****************************************************************************** \ \ Name: SHIP_GECKO \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Gecko \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_GECKO EQUB 0 \ Max. canisters on demise = 0 EQUW 99 * 99 \ Targetable area = 99 * 99 EQUB LO(SHIP_GECKO_EDGES - SHIP_GECKO) \ Edges data offset (low) EQUB LO(SHIP_GECKO_FACES - SHIP_GECKO) \ Faces data offset (low) EQUB 69 \ Max. edge count = (69 - 1) / 4 = 17 EQUB 0 \ Gun vertex = 0 EQUB 26 \ Explosion count = 5, as (4 * n) + 6 = 26 EQUB 72 \ Number of vertices = 72 / 6 = 12 EQUB 17 \ Number of edges = 17 EQUW 55 \ Bounty = 55 EQUB 36 \ Number of faces = 36 / 4 = 9 EQUB 18 \ Visibility distance = 18 EQUB 70 \ Max. energy = 70 EQUB 30 \ Max. speed = 30 EQUB HI(SHIP_GECKO_EDGES - SHIP_GECKO) \ Edges data offset (high) EQUB HI(SHIP_GECKO_FACES - SHIP_GECKO) \ Faces data offset (high) EQUB 3 \ Normals are scaled by = 2^3 = 8 EQUB %00010000 \ Laser power = 2 \ Missiles = 0 .SHIP_GECKO_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX -10, -4, 47, 3, 0, 5, 4, 31 \ Vertex 0 VERTEX 10, -4, 47, 1, 0, 3, 2, 31 \ Vertex 1 VERTEX -16, 8, -23, 5, 0, 7, 6, 31 \ Vertex 2 VERTEX 16, 8, -23, 1, 0, 8, 7, 31 \ Vertex 3 VERTEX -66, 0, -3, 5, 4, 6, 6, 31 \ Vertex 4 VERTEX 66, 0, -3, 2, 1, 8, 8, 31 \ Vertex 5 VERTEX -20, -14, -23, 4, 3, 7, 6, 31 \ Vertex 6 VERTEX 20, -14, -23, 3, 2, 8, 7, 31 \ Vertex 7 VERTEX -8, -6, 33, 3, 3, 3, 3, 16 \ Vertex 8 VERTEX 8, -6, 33, 3, 3, 3, 3, 17 \ Vertex 9 VERTEX -8, -13, -16, 3, 3, 3, 3, 16 \ Vertex 10 VERTEX 8, -13, -16, 3, 3, 3, 3, 17 \ Vertex 11 .SHIP_GECKO_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 3, 0, 31 \ Edge 0 EDGE 1, 5, 2, 1, 31 \ Edge 1 EDGE 5, 3, 8, 1, 31 \ Edge 2 EDGE 3, 2, 7, 0, 31 \ Edge 3 EDGE 2, 4, 6, 5, 31 \ Edge 4 EDGE 4, 0, 5, 4, 31 \ Edge 5 EDGE 5, 7, 8, 2, 31 \ Edge 6 EDGE 7, 6, 7, 3, 31 \ Edge 7 EDGE 6, 4, 6, 4, 31 \ Edge 8 EDGE 0, 2, 5, 0, 29 \ Edge 9 EDGE 1, 3, 1, 0, 30 \ Edge 10 EDGE 0, 6, 4, 3, 29 \ Edge 11 EDGE 1, 7, 3, 2, 30 \ Edge 12 EDGE 2, 6, 7, 6, 20 \ Edge 13 EDGE 3, 7, 8, 7, 20 \ Edge 14 EDGE 8, 10, 3, 3, 16 \ Edge 15 EDGE 9, 11, 3, 3, 17 \ Edge 16 .SHIP_GECKO_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 31, 5, 31 \ Face 0 FACE 4, 45, 8, 31 \ Face 1 FACE 25, -108, 19, 31 \ Face 2 FACE 0, -84, 12, 31 \ Face 3 FACE -25, -108, 19, 31 \ Face 4 FACE -4, 45, 8, 31 \ Face 5 FACE -88, 16, -214, 31 \ Face 6 FACE 0, 0, -187, 31 \ Face 7 FACE 88, 16, -214, 31 \ Face 8 \ ****************************************************************************** \ \ Name: SHIP_COBRA_MK_1 \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Cobra Mk I \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_COBRA_MK_1 EQUB 3 \ Max. canisters on demise = 3 EQUW 99 * 99 \ Targetable area = 99 * 99 EQUB LO(SHIP_COBRA_MK_1_EDGES - SHIP_COBRA_MK_1) \ Edges data offset (low) EQUB LO(SHIP_COBRA_MK_1_FACES - SHIP_COBRA_MK_1) \ Faces data offset (low) EQUB 73 \ Max. edge count = (73 - 1) / 4 = 18 EQUB 40 \ Gun vertex = 40 / 4 = 10 EQUB 26 \ Explosion count = 5, as (4 * n) + 6 = 26 EQUB 66 \ Number of vertices = 66 / 6 = 11 EQUB 18 \ Number of edges = 18 EQUW 75 \ Bounty = 75 EQUB 40 \ Number of faces = 40 / 4 = 10 EQUB 19 \ Visibility distance = 19 EQUB 90 \ Max. energy = 90 EQUB 26 \ Max. speed = 26 EQUB HI(SHIP_COBRA_MK_1_EDGES - SHIP_COBRA_MK_1) \ Edges data offset (high) EQUB HI(SHIP_COBRA_MK_1_FACES - SHIP_COBRA_MK_1) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00010010 \ Laser power = 2 \ Missiles = 2 .SHIP_COBRA_MK_1_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX -18, -1, 50, 1, 0, 3, 2, 31 \ Vertex 0 VERTEX 18, -1, 50, 1, 0, 5, 4, 31 \ Vertex 1 VERTEX -66, 0, 7, 3, 2, 8, 8, 31 \ Vertex 2 VERTEX 66, 0, 7, 5, 4, 9, 9, 31 \ Vertex 3 VERTEX -32, 12, -38, 6, 2, 8, 7, 31 \ Vertex 4 VERTEX 32, 12, -38, 6, 4, 9, 7, 31 \ Vertex 5 VERTEX -54, -12, -38, 3, 1, 8, 7, 31 \ Vertex 6 VERTEX 54, -12, -38, 5, 1, 9, 7, 31 \ Vertex 7 VERTEX 0, 12, -6, 2, 0, 6, 4, 20 \ Vertex 8 VERTEX 0, -1, 50, 1, 0, 1, 1, 2 \ Vertex 9 VERTEX 0, -1, 60, 1, 0, 1, 1, 31 \ Vertex 10 .SHIP_COBRA_MK_1_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 1, 0, 1, 0, 31 \ Edge 0 EDGE 0, 2, 3, 2, 31 \ Edge 1 EDGE 2, 6, 8, 3, 31 \ Edge 2 EDGE 6, 7, 7, 1, 31 \ Edge 3 EDGE 7, 3, 9, 5, 31 \ Edge 4 EDGE 3, 1, 5, 4, 31 \ Edge 5 EDGE 2, 4, 8, 2, 31 \ Edge 6 EDGE 4, 5, 7, 6, 31 \ Edge 7 EDGE 5, 3, 9, 4, 31 \ Edge 8 EDGE 0, 8, 2, 0, 20 \ Edge 9 EDGE 8, 1, 4, 0, 20 \ Edge 10 EDGE 4, 8, 6, 2, 16 \ Edge 11 EDGE 8, 5, 6, 4, 16 \ Edge 12 EDGE 4, 6, 8, 7, 31 \ Edge 13 EDGE 5, 7, 9, 7, 31 \ Edge 14 EDGE 0, 6, 3, 1, 20 \ Edge 15 EDGE 1, 7, 5, 1, 20 \ Edge 16 EDGE 10, 9, 1, 0, 2 \ Edge 17 .SHIP_COBRA_MK_1_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 41, 10, 31 \ Face 0 FACE 0, -27, 3, 31 \ Face 1 FACE -8, 46, 8, 31 \ Face 2 FACE -12, -57, 12, 31 \ Face 3 FACE 8, 46, 8, 31 \ Face 4 FACE 12, -57, 12, 31 \ Face 5 FACE 0, 49, 0, 31 \ Face 6 FACE 0, 0, -154, 31 \ Face 7 FACE -121, 111, -62, 31 \ Face 8 FACE 121, 111, -62, 31 \ Face 9 \ ****************************************************************************** \ \ Name: SHIP_WORM \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Worm \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_WORM EQUB 0 \ Max. canisters on demise = 0 EQUW 99 * 99 \ Targetable area = 99 * 99 EQUB LO(SHIP_WORM_EDGES - SHIP_WORM) \ Edges data offset (low) EQUB LO(SHIP_WORM_FACES - SHIP_WORM) \ Faces data offset (low) EQUB 77 \ Max. edge count = (77 - 1) / 4 = 19 EQUB 0 \ Gun vertex = 0 EQUB 18 \ Explosion count = 3, as (4 * n) + 6 = 18 EQUB 60 \ Number of vertices = 60 / 6 = 10 EQUB 16 \ Number of edges = 16 EQUW 0 \ Bounty = 0 EQUB 32 \ Number of faces = 32 / 4 = 8 EQUB 19 \ Visibility distance = 19 EQUB 30 \ Max. energy = 30 EQUB 23 \ Max. speed = 23 EQUB HI(SHIP_WORM_EDGES - SHIP_WORM) \ Edges data offset (high) EQUB HI(SHIP_WORM_FACES - SHIP_WORM) \ Faces data offset (high) EQUB 3 \ Normals are scaled by = 2^3 = 8 EQUB %00001000 \ Laser power = 1 \ Missiles = 0 .SHIP_WORM_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 10, -10, 35, 2, 0, 7, 7, 31 \ Vertex 0 VERTEX -10, -10, 35, 3, 0, 7, 7, 31 \ Vertex 1 VERTEX 5, 6, 15, 1, 0, 4, 2, 31 \ Vertex 2 VERTEX -5, 6, 15, 1, 0, 5, 3, 31 \ Vertex 3 VERTEX 15, -10, 25, 4, 2, 7, 7, 31 \ Vertex 4 VERTEX -15, -10, 25, 5, 3, 7, 7, 31 \ Vertex 5 VERTEX 26, -10, -25, 6, 4, 7, 7, 31 \ Vertex 6 VERTEX -26, -10, -25, 6, 5, 7, 7, 31 \ Vertex 7 VERTEX 8, 14, -25, 4, 1, 6, 6, 31 \ Vertex 8 VERTEX -8, 14, -25, 5, 1, 6, 6, 31 \ Vertex 9 .SHIP_WORM_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 7, 0, 31 \ Edge 0 EDGE 1, 5, 7, 3, 31 \ Edge 1 EDGE 5, 7, 7, 5, 31 \ Edge 2 EDGE 7, 6, 7, 6, 31 \ Edge 3 EDGE 6, 4, 7, 4, 31 \ Edge 4 EDGE 4, 0, 7, 2, 31 \ Edge 5 EDGE 0, 2, 2, 0, 31 \ Edge 6 EDGE 1, 3, 3, 0, 31 \ Edge 7 EDGE 4, 2, 4, 2, 31 \ Edge 8 EDGE 5, 3, 5, 3, 31 \ Edge 9 EDGE 2, 8, 4, 1, 31 \ Edge 10 EDGE 8, 6, 6, 4, 31 \ Edge 11 EDGE 3, 9, 5, 1, 31 \ Edge 12 EDGE 9, 7, 6, 5, 31 \ Edge 13 EDGE 2, 3, 1, 0, 31 \ Edge 14 EDGE 8, 9, 6, 1, 31 \ Edge 15 .SHIP_WORM_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 88, 70, 31 \ Face 0 FACE 0, 69, 14, 31 \ Face 1 FACE 70, 66, 35, 31 \ Face 2 FACE -70, 66, 35, 31 \ Face 3 FACE 64, 49, 14, 31 \ Face 4 FACE -64, 49, 14, 31 \ Face 5 FACE 0, 0, -200, 31 \ Face 6 FACE 0, -80, 0, 31 \ Face 7 \ ****************************************************************************** \ \ Name: SHIP_COBRA_MK_3_P \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Cobra Mk III (pirate) \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_COBRA_MK_3_P EQUB 1 \ Max. canisters on demise = 1 EQUW 95 * 95 \ Targetable area = 95 * 95 EQUB LO(SHIP_COBRA_MK_3_P_EDGES - SHIP_COBRA_MK_3_P) \ Edges data offset (low) EQUB LO(SHIP_COBRA_MK_3_P_FACES - SHIP_COBRA_MK_3_P) \ Faces data offset (low) EQUB 157 \ Max. edge count = (157 - 1) / 4 = 39 EQUB 84 \ Gun vertex = 84 / 4 = 21 EQUB 42 \ Explosion count = 9, as (4 * n) + 6 = 42 EQUB 168 \ Number of vertices = 168 / 6 = 28 EQUB 38 \ Number of edges = 38 EQUW 175 \ Bounty = 175 EQUB 52 \ Number of faces = 52 / 4 = 13 EQUB 50 \ Visibility distance = 50 EQUB 150 \ Max. energy = 150 EQUB 28 \ Max. speed = 28 EQUB HI(SHIP_COBRA_MK_3_P_EDGES - SHIP_COBRA_MK_3_P) \ Edges data offset (high) EQUB HI(SHIP_COBRA_MK_3_P_FACES - SHIP_COBRA_MK_3_P) \ Faces data offset (high) EQUB 1 \ Normals are scaled by = 2^1 = 2 EQUB %00010010 \ Laser power = 2 \ Missiles = 2 .SHIP_COBRA_MK_3_P_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 32, 0, 76, 15, 15, 15, 15, 31 \ Vertex 0 VERTEX -32, 0, 76, 15, 15, 15, 15, 31 \ Vertex 1 VERTEX 0, 26, 24, 15, 15, 15, 15, 31 \ Vertex 2 VERTEX -120, -3, -8, 3, 7, 10, 10, 31 \ Vertex 3 VERTEX 120, -3, -8, 4, 8, 12, 12, 31 \ Vertex 4 VERTEX -88, 16, -40, 15, 15, 15, 15, 31 \ Vertex 5 VERTEX 88, 16, -40, 15, 15, 15, 15, 31 \ Vertex 6 VERTEX 128, -8, -40, 8, 9, 12, 12, 31 \ Vertex 7 VERTEX -128, -8, -40, 7, 9, 10, 10, 31 \ Vertex 8 VERTEX 0, 26, -40, 5, 6, 9, 9, 31 \ Vertex 9 VERTEX -32, -24, -40, 9, 10, 11, 11, 31 \ Vertex 10 VERTEX 32, -24, -40, 9, 11, 12, 12, 31 \ Vertex 11 VERTEX -36, 8, -40, 9, 9, 9, 9, 20 \ Vertex 12 VERTEX -8, 12, -40, 9, 9, 9, 9, 20 \ Vertex 13 VERTEX 8, 12, -40, 9, 9, 9, 9, 20 \ Vertex 14 VERTEX 36, 8, -40, 9, 9, 9, 9, 20 \ Vertex 15 VERTEX 36, -12, -40, 9, 9, 9, 9, 20 \ Vertex 16 VERTEX 8, -16, -40, 9, 9, 9, 9, 20 \ Vertex 17 VERTEX -8, -16, -40, 9, 9, 9, 9, 20 \ Vertex 18 VERTEX -36, -12, -40, 9, 9, 9, 9, 20 \ Vertex 19 VERTEX 0, 0, 76, 0, 11, 11, 11, 6 \ Vertex 20 VERTEX 0, 0, 90, 0, 11, 11, 11, 31 \ Vertex 21 VERTEX -80, -6, -40, 9, 9, 9, 9, 8 \ Vertex 22 VERTEX -80, 6, -40, 9, 9, 9, 9, 8 \ Vertex 23 VERTEX -88, 0, -40, 9, 9, 9, 9, 6 \ Vertex 24 VERTEX 80, 6, -40, 9, 9, 9, 9, 8 \ Vertex 25 VERTEX 88, 0, -40, 9, 9, 9, 9, 6 \ Vertex 26 VERTEX 80, -6, -40, 9, 9, 9, 9, 8 \ Vertex 27 .SHIP_COBRA_MK_3_P_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 0, 11, 31 \ Edge 0 EDGE 0, 4, 4, 12, 31 \ Edge 1 EDGE 1, 3, 3, 10, 31 \ Edge 2 EDGE 3, 8, 7, 10, 31 \ Edge 3 EDGE 4, 7, 8, 12, 31 \ Edge 4 EDGE 6, 7, 8, 9, 31 \ Edge 5 EDGE 6, 9, 6, 9, 31 \ Edge 6 EDGE 5, 9, 5, 9, 31 \ Edge 7 EDGE 5, 8, 7, 9, 31 \ Edge 8 EDGE 2, 5, 1, 5, 31 \ Edge 9 EDGE 2, 6, 2, 6, 31 \ Edge 10 EDGE 3, 5, 3, 7, 31 \ Edge 11 EDGE 4, 6, 4, 8, 31 \ Edge 12 EDGE 1, 2, 0, 1, 31 \ Edge 13 EDGE 0, 2, 0, 2, 31 \ Edge 14 EDGE 8, 10, 9, 10, 31 \ Edge 15 EDGE 10, 11, 9, 11, 31 \ Edge 16 EDGE 7, 11, 9, 12, 31 \ Edge 17 EDGE 1, 10, 10, 11, 31 \ Edge 18 EDGE 0, 11, 11, 12, 31 \ Edge 19 EDGE 1, 5, 1, 3, 29 \ Edge 20 EDGE 0, 6, 2, 4, 29 \ Edge 21 EDGE 20, 21, 0, 11, 6 \ Edge 22 EDGE 12, 13, 9, 9, 20 \ Edge 23 EDGE 18, 19, 9, 9, 20 \ Edge 24 EDGE 14, 15, 9, 9, 20 \ Edge 25 EDGE 16, 17, 9, 9, 20 \ Edge 26 EDGE 15, 16, 9, 9, 19 \ Edge 27 EDGE 14, 17, 9, 9, 17 \ Edge 28 EDGE 13, 18, 9, 9, 19 \ Edge 29 EDGE 12, 19, 9, 9, 19 \ Edge 30 EDGE 2, 9, 5, 6, 30 \ Edge 31 EDGE 22, 24, 9, 9, 6 \ Edge 32 EDGE 23, 24, 9, 9, 6 \ Edge 33 EDGE 22, 23, 9, 9, 8 \ Edge 34 EDGE 25, 26, 9, 9, 6 \ Edge 35 EDGE 26, 27, 9, 9, 6 \ Edge 36 EDGE 25, 27, 9, 9, 8 \ Edge 37 .SHIP_COBRA_MK_3_P_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 62, 31, 31 \ Face 0 FACE -18, 55, 16, 31 \ Face 1 FACE 18, 55, 16, 31 \ Face 2 FACE -16, 52, 14, 31 \ Face 3 FACE 16, 52, 14, 31 \ Face 4 FACE -14, 47, 0, 31 \ Face 5 FACE 14, 47, 0, 31 \ Face 6 FACE -61, 102, 0, 31 \ Face 7 FACE 61, 102, 0, 31 \ Face 8 FACE 0, 0, -80, 31 \ Face 9 FACE -7, -42, 9, 31 \ Face 10 FACE 0, -30, 6, 31 \ Face 11 FACE 7, -42, 9, 31 \ Face 12 \ ****************************************************************************** \ \ Name: SHIP_ASP_MK_2 \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for an Asp Mk II \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_ASP_MK_2 EQUB 0 \ Max. canisters on demise = 0 EQUW 60 * 60 \ Targetable area = 60 * 60 EQUB LO(SHIP_ASP_MK_2_EDGES - SHIP_ASP_MK_2) \ Edges data offset (low) EQUB LO(SHIP_ASP_MK_2_FACES - SHIP_ASP_MK_2) \ Faces data offset (low) EQUB 105 \ Max. edge count = (105 - 1) / 4 = 26 EQUB 32 \ Gun vertex = 32 / 4 = 8 EQUB 26 \ Explosion count = 5, as (4 * n) + 6 = 26 EQUB 114 \ Number of vertices = 114 / 6 = 19 EQUB 28 \ Number of edges = 28 EQUW 200 \ Bounty = 200 EQUB 48 \ Number of faces = 48 / 4 = 12 EQUB 40 \ Visibility distance = 40 EQUB 150 \ Max. energy = 150 EQUB 40 \ Max. speed = 40 EQUB HI(SHIP_ASP_MK_2_EDGES - SHIP_ASP_MK_2) \ Edges data offset (high) EQUB HI(SHIP_ASP_MK_2_FACES - SHIP_ASP_MK_2) \ Faces data offset (high) EQUB 1 \ Normals are scaled by = 2^1 = 2 EQUB %00101001 \ Laser power = 5 \ Missiles = 1 .SHIP_ASP_MK_2_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, -18, 0, 1, 0, 2, 2, 22 \ Vertex 0 VERTEX 0, -9, -45, 2, 1, 11, 11, 31 \ Vertex 1 VERTEX 43, 0, -45, 6, 1, 11, 11, 31 \ Vertex 2 VERTEX 69, -3, 0, 6, 1, 9, 7, 31 \ Vertex 3 VERTEX 43, -14, 28, 1, 0, 7, 7, 31 \ Vertex 4 VERTEX -43, 0, -45, 5, 2, 11, 11, 31 \ Vertex 5 VERTEX -69, -3, 0, 5, 2, 10, 8, 31 \ Vertex 6 VERTEX -43, -14, 28, 2, 0, 8, 8, 31 \ Vertex 7 VERTEX 26, -7, 73, 4, 0, 9, 7, 31 \ Vertex 8 VERTEX -26, -7, 73, 4, 0, 10, 8, 31 \ Vertex 9 VERTEX 43, 14, 28, 4, 3, 9, 6, 31 \ Vertex 10 VERTEX -43, 14, 28, 4, 3, 10, 5, 31 \ Vertex 11 VERTEX 0, 9, -45, 5, 3, 11, 6, 31 \ Vertex 12 VERTEX -17, 0, -45, 11, 11, 11, 11, 10 \ Vertex 13 VERTEX 17, 0, -45, 11, 11, 11, 11, 9 \ Vertex 14 VERTEX 0, -4, -45, 11, 11, 11, 11, 10 \ Vertex 15 VERTEX 0, 4, -45, 11, 11, 11, 11, 8 \ Vertex 16 VERTEX 0, -7, 73, 4, 0, 4, 0, 10 \ Vertex 17 VERTEX 0, -7, 83, 4, 0, 4, 0, 10 \ Vertex 18 .SHIP_ASP_MK_2_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 2, 1, 22 \ Edge 0 EDGE 0, 4, 1, 0, 22 \ Edge 1 EDGE 0, 7, 2, 0, 22 \ Edge 2 EDGE 1, 2, 11, 1, 31 \ Edge 3 EDGE 2, 3, 6, 1, 31 \ Edge 4 EDGE 3, 8, 9, 7, 16 \ Edge 5 EDGE 8, 9, 4, 0, 31 \ Edge 6 EDGE 6, 9, 10, 8, 16 \ Edge 7 EDGE 5, 6, 5, 2, 31 \ Edge 8 EDGE 1, 5, 11, 2, 31 \ Edge 9 EDGE 3, 4, 7, 1, 31 \ Edge 10 EDGE 4, 8, 7, 0, 31 \ Edge 11 EDGE 6, 7, 8, 2, 31 \ Edge 12 EDGE 7, 9, 8, 0, 31 \ Edge 13 EDGE 2, 12, 11, 6, 31 \ Edge 14 EDGE 5, 12, 11, 5, 31 \ Edge 15 EDGE 10, 12, 6, 3, 22 \ Edge 16 EDGE 11, 12, 5, 3, 22 \ Edge 17 EDGE 10, 11, 4, 3, 22 \ Edge 18 EDGE 6, 11, 10, 5, 31 \ Edge 19 EDGE 9, 11, 10, 4, 31 \ Edge 20 EDGE 3, 10, 9, 6, 31 \ Edge 21 EDGE 8, 10, 9, 4, 31 \ Edge 22 EDGE 13, 15, 11, 11, 10 \ Edge 23 EDGE 15, 14, 11, 11, 9 \ Edge 24 EDGE 14, 16, 11, 11, 8 \ Edge 25 EDGE 16, 13, 11, 11, 8 \ Edge 26 EDGE 18, 17, 4, 0, 10 \ Edge 27 .SHIP_ASP_MK_2_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, -35, 5, 31 \ Face 0 FACE 8, -38, -7, 31 \ Face 1 FACE -8, -38, -7, 31 \ Face 2 FACE 0, 24, -1, 22 \ Face 3 FACE 0, 43, 19, 31 \ Face 4 FACE -6, 28, -2, 31 \ Face 5 FACE 6, 28, -2, 31 \ Face 6 FACE 59, -64, 31, 31 \ Face 7 FACE -59, -64, 31, 31 \ Face 8 FACE 80, 46, 50, 31 \ Face 9 FACE -80, 46, 50, 31 \ Face 10 FACE 0, 0, -90, 31 \ Face 11 EQUB &38, &E5 \ These bytes appear to be unused EQUB &2C, &C5 \ ****************************************************************************** \ \ Name: SHIP_PYTHON_P \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Python (pirate) \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_PYTHON_P EQUB 2 \ Max. canisters on demise = 2 EQUW 80 * 80 \ Targetable area = 80 * 80 EQUB LO(SHIP_PYTHON_P_EDGES - SHIP_PYTHON_P) \ Edges data offset (low) EQUB LO(SHIP_PYTHON_P_FACES - SHIP_PYTHON_P) \ Faces data offset (low) EQUB 89 \ Max. edge count = (89 - 1) / 4 = 22 EQUB 0 \ Gun vertex = 0 EQUB 42 \ Explosion count = 9, as (4 * n) + 6 = 42 EQUB 66 \ Number of vertices = 66 / 6 = 11 EQUB 26 \ Number of edges = 26 EQUW 200 \ Bounty = 200 EQUB 52 \ Number of faces = 52 / 4 = 13 EQUB 40 \ Visibility distance = 40 EQUB 250 \ Max. energy = 250 EQUB 20 \ Max. speed = 20 EQUB HI(SHIP_PYTHON_P_EDGES - SHIP_PYTHON_P) \ Edges data offset (high) EQUB HI(SHIP_PYTHON_P_FACES - SHIP_PYTHON_P) \ Faces data offset (high) EQUB 0 \ Normals are scaled by = 2^0 = 1 EQUB %00011011 \ Laser power = 3 \ Missiles = 3 .SHIP_PYTHON_P_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, 0, 224, 0, 1, 2, 3, 31 \ Vertex 0 VERTEX 0, 48, 48, 0, 1, 4, 5, 31 \ Vertex 1 VERTEX 96, 0, -16, 15, 15, 15, 15, 31 \ Vertex 2 VERTEX -96, 0, -16, 15, 15, 15, 15, 31 \ Vertex 3 VERTEX 0, 48, -32, 4, 5, 8, 9, 31 \ Vertex 4 VERTEX 0, 24, -112, 9, 8, 12, 12, 31 \ Vertex 5 VERTEX -48, 0, -112, 8, 11, 12, 12, 31 \ Vertex 6 VERTEX 48, 0, -112, 9, 10, 12, 12, 31 \ Vertex 7 VERTEX 0, -48, 48, 2, 3, 6, 7, 31 \ Vertex 8 VERTEX 0, -48, -32, 6, 7, 10, 11, 31 \ Vertex 9 VERTEX 0, -24, -112, 10, 11, 12, 12, 31 \ Vertex 10 .SHIP_PYTHON_P_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 8, 2, 3, 31 \ Edge 0 EDGE 0, 3, 0, 2, 31 \ Edge 1 EDGE 0, 2, 1, 3, 31 \ Edge 2 EDGE 0, 1, 0, 1, 31 \ Edge 3 EDGE 2, 4, 9, 5, 31 \ Edge 4 EDGE 1, 2, 1, 5, 31 \ Edge 5 EDGE 2, 8, 7, 3, 31 \ Edge 6 EDGE 1, 3, 0, 4, 31 \ Edge 7 EDGE 3, 8, 2, 6, 31 \ Edge 8 EDGE 2, 9, 7, 10, 31 \ Edge 9 EDGE 3, 4, 4, 8, 31 \ Edge 10 EDGE 3, 9, 6, 11, 31 \ Edge 11 EDGE 3, 5, 8, 8, 7 \ Edge 12 EDGE 3, 10, 11, 11, 7 \ Edge 13 EDGE 2, 5, 9, 9, 7 \ Edge 14 EDGE 2, 10, 10, 10, 7 \ Edge 15 EDGE 2, 7, 9, 10, 31 \ Edge 16 EDGE 3, 6, 8, 11, 31 \ Edge 17 EDGE 5, 6, 8, 12, 31 \ Edge 18 EDGE 5, 7, 9, 12, 31 \ Edge 19 EDGE 7, 10, 12, 10, 31 \ Edge 20 EDGE 6, 10, 11, 12, 31 \ Edge 21 EDGE 4, 5, 8, 9, 31 \ Edge 22 EDGE 9, 10, 10, 11, 31 \ Edge 23 EDGE 1, 4, 4, 5, 31 \ Edge 24 EDGE 8, 9, 6, 7, 31 \ Edge 25 .SHIP_PYTHON_P_FACES \ normal_x, normal_y, normal_z, visibility FACE -27, 40, 11, 31 \ Face 0 FACE 27, 40, 11, 31 \ Face 1 FACE -27, -40, 11, 31 \ Face 2 FACE 27, -40, 11, 31 \ Face 3 FACE -19, 38, 0, 31 \ Face 4 FACE 19, 38, 0, 31 \ Face 5 FACE -19, -38, 0, 31 \ Face 6 FACE 19, -38, 0, 31 \ Face 7 FACE -25, 37, -11, 31 \ Face 8 FACE 25, 37, -11, 31 \ Face 9 FACE 25, -37, -11, 31 \ Face 10 FACE -25, -37, -11, 31 \ Face 11 FACE 0, 0, -112, 31 \ Face 12 \ ****************************************************************************** \ \ Name: SHIP_FER_DE_LANCE \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Fer-de-Lance \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_FER_DE_LANCE EQUB 0 \ Max. canisters on demise = 0 EQUW 40 * 40 \ Targetable area = 40 * 40 EQUB LO(SHIP_FER_DE_LANCE_EDGES - SHIP_FER_DE_LANCE) \ Edges data offset (low) EQUB LO(SHIP_FER_DE_LANCE_FACES - SHIP_FER_DE_LANCE) \ Faces data offset (low) EQUB 109 \ Max. edge count = (109 - 1) / 4 = 27 EQUB 0 \ Gun vertex = 0 EQUB 26 \ Explosion count = 5, as (4 * n) + 6 = 26 EQUB 114 \ Number of vertices = 114 / 6 = 19 EQUB 27 \ Number of edges = 27 EQUW 0 \ Bounty = 0 EQUB 40 \ Number of faces = 40 / 4 = 10 EQUB 40 \ Visibility distance = 40 EQUB 160 \ Max. energy = 160 EQUB 30 \ Max. speed = 30 EQUB HI(SHIP_FER_DE_LANCE_EDGES - SHIP_FER_DE_LANCE) \ Edges data offset (high) EQUB HI(SHIP_FER_DE_LANCE_FACES - SHIP_FER_DE_LANCE) \ Faces data offset (high) EQUB 1 \ Normals are scaled by = 2^1 = 2 EQUB %00010010 \ Laser power = 2 \ Missiles = 2 .SHIP_FER_DE_LANCE_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, -14, 108, 1, 0, 9, 5, 31 \ Vertex 0 VERTEX -40, -14, -4, 2, 1, 9, 9, 31 \ Vertex 1 VERTEX -12, -14, -52, 3, 2, 9, 9, 31 \ Vertex 2 VERTEX 12, -14, -52, 4, 3, 9, 9, 31 \ Vertex 3 VERTEX 40, -14, -4, 5, 4, 9, 9, 31 \ Vertex 4 VERTEX -40, 14, -4, 1, 0, 6, 2, 28 \ Vertex 5 VERTEX -12, 2, -52, 3, 2, 7, 6, 28 \ Vertex 6 VERTEX 12, 2, -52, 4, 3, 8, 7, 28 \ Vertex 7 VERTEX 40, 14, -4, 4, 0, 8, 5, 28 \ Vertex 8 VERTEX 0, 18, -20, 6, 0, 8, 7, 15 \ Vertex 9 VERTEX -3, -11, 97, 0, 0, 0, 0, 11 \ Vertex 10 VERTEX -26, 8, 18, 0, 0, 0, 0, 9 \ Vertex 11 VERTEX -16, 14, -4, 0, 0, 0, 0, 11 \ Vertex 12 VERTEX 3, -11, 97, 0, 0, 0, 0, 11 \ Vertex 13 VERTEX 26, 8, 18, 0, 0, 0, 0, 9 \ Vertex 14 VERTEX 16, 14, -4, 0, 0, 0, 0, 11 \ Vertex 15 VERTEX 0, -14, -20, 9, 9, 9, 9, 12 \ Vertex 16 VERTEX -14, -14, 44, 9, 9, 9, 9, 12 \ Vertex 17 VERTEX 14, -14, 44, 9, 9, 9, 9, 12 \ Vertex 18 .SHIP_FER_DE_LANCE_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 9, 1, 31 \ Edge 0 EDGE 1, 2, 9, 2, 31 \ Edge 1 EDGE 2, 3, 9, 3, 31 \ Edge 2 EDGE 3, 4, 9, 4, 31 \ Edge 3 EDGE 0, 4, 9, 5, 31 \ Edge 4 EDGE 0, 5, 1, 0, 28 \ Edge 5 EDGE 5, 6, 6, 2, 28 \ Edge 6 EDGE 6, 7, 7, 3, 28 \ Edge 7 EDGE 7, 8, 8, 4, 28 \ Edge 8 EDGE 0, 8, 5, 0, 28 \ Edge 9 EDGE 5, 9, 6, 0, 15 \ Edge 10 EDGE 6, 9, 7, 6, 11 \ Edge 11 EDGE 7, 9, 8, 7, 11 \ Edge 12 EDGE 8, 9, 8, 0, 15 \ Edge 13 EDGE 1, 5, 2, 1, 14 \ Edge 14 EDGE 2, 6, 3, 2, 14 \ Edge 15 EDGE 3, 7, 4, 3, 14 \ Edge 16 EDGE 4, 8, 5, 4, 14 \ Edge 17 EDGE 10, 11, 0, 0, 8 \ Edge 18 EDGE 11, 12, 0, 0, 9 \ Edge 19 EDGE 10, 12, 0, 0, 11 \ Edge 20 EDGE 13, 14, 0, 0, 8 \ Edge 21 EDGE 14, 15, 0, 0, 9 \ Edge 22 EDGE 13, 15, 0, 0, 11 \ Edge 23 EDGE 16, 17, 9, 9, 12 \ Edge 24 EDGE 16, 18, 9, 9, 12 \ Edge 25 EDGE 17, 18, 9, 9, 8 \ Edge 26 .SHIP_FER_DE_LANCE_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 24, 6, 28 \ Face 0 FACE -68, 0, 24, 31 \ Face 1 FACE -63, 0, -37, 31 \ Face 2 FACE 0, 0, -104, 31 \ Face 3 FACE 63, 0, -37, 31 \ Face 4 FACE 68, 0, 24, 31 \ Face 5 FACE -12, 46, -19, 28 \ Face 6 FACE 0, 45, -22, 28 \ Face 7 FACE 12, 46, -19, 28 \ Face 8 FACE 0, -28, 0, 31 \ Face 9 \ ****************************************************************************** \ \ Name: SHIP_MORAY \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Moray \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_MORAY EQUB 1 \ Max. canisters on demise = 1 EQUW 30 * 30 \ Targetable area = 30 * 30 EQUB LO(SHIP_MORAY_EDGES - SHIP_MORAY) \ Edges data offset (low) EQUB LO(SHIP_MORAY_FACES - SHIP_MORAY) \ Faces data offset (low) EQUB 73 \ Max. edge count = (73 - 1) / 4 = 18 EQUB 0 \ Gun vertex = 0 EQUB 26 \ Explosion count = 5, as (4 * n) + 6 = 26 EQUB 84 \ Number of vertices = 84 / 6 = 14 EQUB 19 \ Number of edges = 19 EQUW 50 \ Bounty = 50 EQUB 36 \ Number of faces = 36 / 4 = 9 EQUB 40 \ Visibility distance = 40 EQUB 100 \ Max. energy = 100 EQUB 25 \ Max. speed = 25 EQUB HI(SHIP_MORAY_EDGES - SHIP_MORAY) \ Edges data offset (high) EQUB HI(SHIP_MORAY_FACES - SHIP_MORAY) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00010000 \ Laser power = 2 \ Missiles = 0 .SHIP_MORAY_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 15, 0, 65, 2, 0, 8, 7, 31 \ Vertex 0 VERTEX -15, 0, 65, 1, 0, 7, 6, 31 \ Vertex 1 VERTEX 0, 18, -40, 15, 15, 15, 15, 17 \ Vertex 2 VERTEX -60, 0, 0, 3, 1, 6, 6, 31 \ Vertex 3 VERTEX 60, 0, 0, 5, 2, 8, 8, 31 \ Vertex 4 VERTEX 30, -27, -10, 5, 4, 8, 7, 24 \ Vertex 5 VERTEX -30, -27, -10, 4, 3, 7, 6, 24 \ Vertex 6 VERTEX -9, -4, -25, 4, 4, 4, 4, 7 \ Vertex 7 VERTEX 9, -4, -25, 4, 4, 4, 4, 7 \ Vertex 8 VERTEX 0, -18, -16, 4, 4, 4, 4, 7 \ Vertex 9 VERTEX 13, 3, 49, 0, 0, 0, 0, 5 \ Vertex 10 VERTEX 6, 0, 65, 0, 0, 0, 0, 5 \ Vertex 11 VERTEX -13, 3, 49, 0, 0, 0, 0, 5 \ Vertex 12 VERTEX -6, 0, 65, 0, 0, 0, 0, 5 \ Vertex 13 .SHIP_MORAY_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 7, 0, 31 \ Edge 0 EDGE 1, 3, 6, 1, 31 \ Edge 1 EDGE 3, 6, 6, 3, 24 \ Edge 2 EDGE 5, 6, 7, 4, 24 \ Edge 3 EDGE 4, 5, 8, 5, 24 \ Edge 4 EDGE 0, 4, 8, 2, 31 \ Edge 5 EDGE 1, 6, 7, 6, 15 \ Edge 6 EDGE 0, 5, 8, 7, 15 \ Edge 7 EDGE 0, 2, 2, 0, 15 \ Edge 8 EDGE 1, 2, 1, 0, 15 \ Edge 9 EDGE 2, 3, 3, 1, 17 \ Edge 10 EDGE 2, 4, 5, 2, 17 \ Edge 11 EDGE 2, 5, 5, 4, 13 \ Edge 12 EDGE 2, 6, 4, 3, 13 \ Edge 13 EDGE 7, 8, 4, 4, 5 \ Edge 14 EDGE 7, 9, 4, 4, 7 \ Edge 15 EDGE 8, 9, 4, 4, 7 \ Edge 16 EDGE 10, 11, 0, 0, 5 \ Edge 17 EDGE 12, 13, 0, 0, 5 \ Edge 18 .SHIP_MORAY_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 43, 7, 31 \ Face 0 FACE -10, 49, 7, 31 \ Face 1 FACE 10, 49, 7, 31 \ Face 2 FACE -59, -28, -101, 24 \ Face 3 FACE 0, -52, -78, 24 \ Face 4 FACE 59, -28, -101, 24 \ Face 5 FACE -72, -99, 50, 31 \ Face 6 FACE 0, -83, 30, 31 \ Face 7 FACE 72, -99, 50, 31 \ Face 8 \ ****************************************************************************** \ \ Name: SHIP_THARGOID \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Thargoid mothership \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_THARGOID EQUB 0 \ Max. canisters on demise = 0 EQUW 99 * 99 \ Targetable area = 99 * 99 EQUB LO(SHIP_THARGOID_EDGES - SHIP_THARGOID) \ Edges data offset (low) EQUB LO(SHIP_THARGOID_FACES - SHIP_THARGOID) \ Faces data offset (low) EQUB 105 \ Max. edge count = (105 - 1) / 4 = 26 EQUB 60 \ Gun vertex = 60 / 4 = 15 EQUB 38 \ Explosion count = 8, as (4 * n) + 6 = 38 EQUB 120 \ Number of vertices = 120 / 6 = 20 EQUB 26 \ Number of edges = 26 EQUW 500 \ Bounty = 500 EQUB 40 \ Number of faces = 40 / 4 = 10 EQUB 55 \ Visibility distance = 55 EQUB 240 \ Max. energy = 240 EQUB 39 \ Max. speed = 39 EQUB HI(SHIP_THARGOID_EDGES - SHIP_THARGOID) \ Edges data offset (high) EQUB HI(SHIP_THARGOID_FACES - SHIP_THARGOID) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00010110 \ Laser power = 2 \ Missiles = 6 .SHIP_THARGOID_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 32, -48, 48, 0, 4, 8, 8, 31 \ Vertex 0 VERTEX 32, -68, 0, 0, 1, 4, 4, 31 \ Vertex 1 VERTEX 32, -48, -48, 1, 2, 4, 4, 31 \ Vertex 2 VERTEX 32, 0, -68, 2, 3, 4, 4, 31 \ Vertex 3 VERTEX 32, 48, -48, 3, 4, 5, 5, 31 \ Vertex 4 VERTEX 32, 68, 0, 4, 5, 6, 6, 31 \ Vertex 5 VERTEX 32, 48, 48, 4, 6, 7, 7, 31 \ Vertex 6 VERTEX 32, 0, 68, 4, 7, 8, 8, 31 \ Vertex 7 VERTEX -24, -116, 116, 0, 8, 9, 9, 31 \ Vertex 8 VERTEX -24, -164, 0, 0, 1, 9, 9, 31 \ Vertex 9 VERTEX -24, -116, -116, 1, 2, 9, 9, 31 \ Vertex 10 VERTEX -24, 0, -164, 2, 3, 9, 9, 31 \ Vertex 11 VERTEX -24, 116, -116, 3, 5, 9, 9, 31 \ Vertex 12 VERTEX -24, 164, 0, 5, 6, 9, 9, 31 \ Vertex 13 VERTEX -24, 116, 116, 6, 7, 9, 9, 31 \ Vertex 14 VERTEX -24, 0, 164, 7, 8, 9, 9, 31 \ Vertex 15 VERTEX -24, 64, 80, 9, 9, 9, 9, 30 \ Vertex 16 VERTEX -24, 64, -80, 9, 9, 9, 9, 30 \ Vertex 17 VERTEX -24, -64, -80, 9, 9, 9, 9, 30 \ Vertex 18 VERTEX -24, -64, 80, 9, 9, 9, 9, 30 \ Vertex 19 .SHIP_THARGOID_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 7, 4, 8, 31 \ Edge 0 EDGE 0, 1, 0, 4, 31 \ Edge 1 EDGE 1, 2, 1, 4, 31 \ Edge 2 EDGE 2, 3, 2, 4, 31 \ Edge 3 EDGE 3, 4, 3, 4, 31 \ Edge 4 EDGE 4, 5, 4, 5, 31 \ Edge 5 EDGE 5, 6, 4, 6, 31 \ Edge 6 EDGE 6, 7, 4, 7, 31 \ Edge 7 EDGE 0, 8, 0, 8, 31 \ Edge 8 EDGE 1, 9, 0, 1, 31 \ Edge 9 EDGE 2, 10, 1, 2, 31 \ Edge 10 EDGE 3, 11, 2, 3, 31 \ Edge 11 EDGE 4, 12, 3, 5, 31 \ Edge 12 EDGE 5, 13, 5, 6, 31 \ Edge 13 EDGE 6, 14, 6, 7, 31 \ Edge 14 EDGE 7, 15, 7, 8, 31 \ Edge 15 EDGE 8, 15, 8, 9, 31 \ Edge 16 EDGE 8, 9, 0, 9, 31 \ Edge 17 EDGE 9, 10, 1, 9, 31 \ Edge 18 EDGE 10, 11, 2, 9, 31 \ Edge 19 EDGE 11, 12, 3, 9, 31 \ Edge 20 EDGE 12, 13, 5, 9, 31 \ Edge 21 EDGE 13, 14, 6, 9, 31 \ Edge 22 EDGE 14, 15, 7, 9, 31 \ Edge 23 EDGE 16, 17, 9, 9, 30 \ Edge 24 EDGE 18, 19, 9, 9, 30 \ Edge 25 .SHIP_THARGOID_FACES \ normal_x, normal_y, normal_z, visibility FACE 103, -60, 25, 31 \ Face 0 FACE 103, -60, -25, 31 \ Face 1 FACE 103, -25, -60, 31 \ Face 2 FACE 103, 25, -60, 31 \ Face 3 FACE 64, 0, 0, 31 \ Face 4 FACE 103, 60, -25, 31 \ Face 5 FACE 103, 60, 25, 31 \ Face 6 FACE 103, 25, 60, 31 \ Face 7 FACE 103, -25, 60, 31 \ Face 8 FACE -48, 0, 0, 31 \ Face 9 \ ****************************************************************************** \ \ Name: SHIP_THARGON \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Thargon \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ------------------------------------------------------------------------------ \ \ The ship blueprint for the Thargon reuses the edges data from the cargo \ canister, so the edges data offset is negative. \ \ ****************************************************************************** .SHIP_THARGON EQUB 0 + (15 << 4) \ Max. canisters on demise = 0 \ Market item when scooped = 15 + 1 = 16 (alien items) EQUW 40 * 40 \ Targetable area = 40 * 40 EQUB LO(SHIP_CANISTER_EDGES - SHIP_THARGON) \ Edges from canister EQUB LO(SHIP_THARGON_FACES - SHIP_THARGON) \ Faces data offset (low) EQUB 69 \ Max. edge count = (69 - 1) / 4 = 17 EQUB 0 \ Gun vertex = 0 EQUB 18 \ Explosion count = 3, as (4 * n) + 6 = 18 EQUB 60 \ Number of vertices = 60 / 6 = 10 EQUB 15 \ Number of edges = 15 EQUW 50 \ Bounty = 50 EQUB 28 \ Number of faces = 28 / 4 = 7 EQUB 20 \ Visibility distance = 20 EQUB 20 \ Max. energy = 20 EQUB 30 \ Max. speed = 30 EQUB HI(SHIP_CANISTER_EDGES - SHIP_THARGON) \ Edges from canister EQUB HI(SHIP_THARGON_FACES - SHIP_THARGON) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00010000 \ Laser power = 2 \ Missiles = 0 .SHIP_THARGON_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX -9, 0, 40, 1, 0, 5, 5, 31 \ Vertex 0 VERTEX -9, -38, 12, 1, 0, 2, 2, 31 \ Vertex 1 VERTEX -9, -24, -32, 2, 0, 3, 3, 31 \ Vertex 2 VERTEX -9, 24, -32, 3, 0, 4, 4, 31 \ Vertex 3 VERTEX -9, 38, 12, 4, 0, 5, 5, 31 \ Vertex 4 VERTEX 9, 0, -8, 5, 1, 6, 6, 31 \ Vertex 5 VERTEX 9, -10, -15, 2, 1, 6, 6, 31 \ Vertex 6 VERTEX 9, -6, -26, 3, 2, 6, 6, 31 \ Vertex 7 VERTEX 9, 6, -26, 4, 3, 6, 6, 31 \ Vertex 8 VERTEX 9, 10, -15, 5, 4, 6, 6, 31 \ Vertex 9 .SHIP_THARGON_FACES \ normal_x, normal_y, normal_z, visibility FACE -36, 0, 0, 31 \ Face 0 FACE 20, -5, 7, 31 \ Face 1 FACE 46, -42, -14, 31 \ Face 2 FACE 36, 0, -104, 31 \ Face 3 FACE 46, 42, -14, 31 \ Face 4 FACE 20, 5, 7, 31 \ Face 5 FACE 36, 0, 0, 31 \ Face 6 \ ****************************************************************************** \ \ Name: SHIP_CONSTRICTOR \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Constrictor \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_CONSTRICTOR EQUB 3 \ Max. canisters on demise = 3 EQUW 65 * 65 \ Targetable area = 65 * 65 EQUB LO(SHIP_CONSTRICTOR_EDGES - SHIP_CONSTRICTOR) \ Edges data offset (low) EQUB LO(SHIP_CONSTRICTOR_FACES - SHIP_CONSTRICTOR) \ Faces data offset (low) EQUB 81 \ Max. edge count = (81 - 1) / 4 = 20 EQUB 0 \ Gun vertex = 0 EQUB 46 \ Explosion count = 10, as (4 * n) + 6 = 46 EQUB 102 \ Number of vertices = 102 / 6 = 17 EQUB 24 \ Number of edges = 24 EQUW 0 \ Bounty = 0 EQUB 40 \ Number of faces = 40 / 4 = 10 EQUB 45 \ Visibility distance = 45 EQUB 252 \ Max. energy = 252 EQUB 36 \ Max. speed = 36 EQUB HI(SHIP_CONSTRICTOR_EDGES - SHIP_CONSTRICTOR) \ Edges data offset (high) EQUB HI(SHIP_CONSTRICTOR_FACES - SHIP_CONSTRICTOR) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00110100 \ Laser power = 6 \ Missiles = 4 .SHIP_CONSTRICTOR_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 20, -7, 80, 2, 0, 9, 9, 31 \ Vertex 0 VERTEX -20, -7, 80, 1, 0, 9, 9, 31 \ Vertex 1 VERTEX -54, -7, 40, 4, 1, 9, 9, 31 \ Vertex 2 VERTEX -54, -7, -40, 5, 4, 9, 8, 31 \ Vertex 3 VERTEX -20, 13, -40, 6, 5, 8, 8, 31 \ Vertex 4 VERTEX 20, 13, -40, 7, 6, 8, 8, 31 \ Vertex 5 VERTEX 54, -7, -40, 7, 3, 9, 8, 31 \ Vertex 6 VERTEX 54, -7, 40, 3, 2, 9, 9, 31 \ Vertex 7 VERTEX 20, 13, 5, 15, 15, 15, 15, 31 \ Vertex 8 VERTEX -20, 13, 5, 15, 15, 15, 15, 31 \ Vertex 9 VERTEX 20, -7, 62, 9, 9, 9, 9, 18 \ Vertex 10 VERTEX -20, -7, 62, 9, 9, 9, 9, 18 \ Vertex 11 VERTEX 25, -7, -25, 9, 9, 9, 9, 18 \ Vertex 12 VERTEX -25, -7, -25, 9, 9, 9, 9, 18 \ Vertex 13 VERTEX 15, -7, -15, 9, 9, 9, 9, 10 \ Vertex 14 VERTEX -15, -7, -15, 9, 9, 9, 9, 10 \ Vertex 15 VERTEX 0, -7, 0, 15, 9, 1, 0, 0 \ Vertex 16 .SHIP_CONSTRICTOR_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 9, 0, 31 \ Edge 0 EDGE 1, 2, 9, 1, 31 \ Edge 1 EDGE 1, 9, 1, 0, 31 \ Edge 2 EDGE 0, 8, 2, 0, 31 \ Edge 3 EDGE 0, 7, 9, 2, 31 \ Edge 4 EDGE 7, 8, 3, 2, 31 \ Edge 5 EDGE 2, 9, 4, 1, 31 \ Edge 6 EDGE 2, 3, 9, 4, 31 \ Edge 7 EDGE 6, 7, 9, 3, 31 \ Edge 8 EDGE 6, 8, 7, 3, 31 \ Edge 9 EDGE 5, 8, 7, 6, 31 \ Edge 10 EDGE 4, 9, 6, 5, 31 \ Edge 11 EDGE 3, 9, 5, 4, 31 \ Edge 12 EDGE 3, 4, 8, 5, 31 \ Edge 13 EDGE 4, 5, 8, 6, 31 \ Edge 14 EDGE 5, 6, 8, 7, 31 \ Edge 15 EDGE 3, 6, 9, 8, 31 \ Edge 16 EDGE 8, 9, 6, 0, 31 \ Edge 17 EDGE 10, 12, 9, 9, 18 \ Edge 18 EDGE 12, 14, 9, 9, 5 \ Edge 19 EDGE 14, 10, 9, 9, 10 \ Edge 20 EDGE 11, 15, 9, 9, 10 \ Edge 21 EDGE 13, 15, 9, 9, 5 \ Edge 22 EDGE 11, 13, 9, 9, 18 \ Edge 23 .SHIP_CONSTRICTOR_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 55, 15, 31 \ Face 0 FACE -24, 75, 20, 31 \ Face 1 FACE 24, 75, 20, 31 \ Face 2 FACE 44, 75, 0, 31 \ Face 3 FACE -44, 75, 0, 31 \ Face 4 FACE -44, 75, 0, 31 \ Face 5 FACE 0, 53, 0, 31 \ Face 6 FACE 44, 75, 0, 31 \ Face 7 FACE 0, 0, -160, 31 \ Face 8 FACE 0, -27, 0, 31 \ Face 9 \ ****************************************************************************** \ \ Name: SHIP_LOGO \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for the Elite logo \ Deep dive: Ship blueprints \ \ ****************************************************************************** .SHIP_LOGO \ --- Mod: Code removed for two-player Elite: ---------> \EQUB 0 \ Max. canisters on demise = 0 \EQUW 99 * 99 \ Targetable area = 99 * 99 \ \EQUB LO(SHIP_LOGO_EDGES - SHIP_LOGO) \ Edges data offset (low) \EQUB LO(SHIP_LOGO_FACES - SHIP_LOGO) \ Faces data offset (low) \ \EQUB 153 \ Max. edge count = (153 - 1) / 4 = 38 \EQUB 0 \ Gun vertex = 0 \EQUB 54 \ Explosion count = 12, as (4 * n) + 6 = 54 \EQUB 252 \ Number of vertices = 252 / 6 = 42 \EQUB 37 \ Number of edges = 37 \EQUW 0 \ Bounty = 0 \EQUB 20 \ Number of faces = 20 / 4 = 5 \EQUB 99 \ Visibility distance = 99 \EQUB 252 \ Max. energy = 252 \EQUB 36 \ Max. speed = 36 \ \EQUB HI(SHIP_LOGO_EDGES - SHIP_LOGO) \ Edges data offset (high) \EQUB HI(SHIP_LOGO_FACES - SHIP_LOGO) \ Faces data offset (high) \ \EQUB 1 \ Normals are scaled by = 2^1 = 2 \EQUB %00000000 \ Laser power = 0 \ \ Missiles = 0 \ \.SHIP_LOGO_VERTICES \ \ \ x, y, z, face1, face2, face3, face4, visibility \VERTEX 0, -9, 55, 0, 0, 0, 0, 31 \ Vertex 0 \VERTEX -10, -9, 30, 0, 0, 0, 0, 31 \ Vertex 1 \VERTEX -25, -9, 93, 0, 0, 0, 0, 31 \ Vertex 2 \VERTEX -150, -9, 180, 0, 0, 0, 0, 31 \ Vertex 3 \VERTEX -90, -9, 10, 0, 0, 0, 0, 31 \ Vertex 4 \VERTEX -140, -9, 10, 0, 0, 0, 0, 31 \ Vertex 5 \VERTEX 0, -9, -95, 0, 0, 0, 0, 31 \ Vertex 6 \VERTEX 140, -9, 10, 0, 0, 0, 0, 31 \ Vertex 7 \VERTEX 90, -9, 10, 0, 0, 0, 0, 31 \ Vertex 8 \VERTEX 150, -9, 180, 0, 0, 0, 0, 31 \ Vertex 9 \VERTEX 25, -9, 93, 0, 0, 0, 0, 31 \ Vertex 10 \VERTEX 10, -9, 30, 0, 0, 0, 0, 31 \ Vertex 11 \VERTEX -85, -9, -30, 2, 0, 3, 3, 31 \ Vertex 12 \VERTEX 85, -9, -30, 2, 0, 4, 4, 31 \ Vertex 13 \VERTEX -70, 11, 5, 1, 0, 3, 3, 31 \ Vertex 14 \VERTEX -70, 11, -25, 2, 0, 3, 3, 31 \ Vertex 15 \VERTEX 70, 11, -25, 2, 0, 4, 4, 31 \ Vertex 16 \VERTEX 70, 11, 5, 1, 0, 4, 4, 31 \ Vertex 17 \VERTEX 0, -9, 5, 0, 0, 0, 0, 31 \ Vertex 18 \VERTEX 0, -9, 5, 0, 0, 0, 0, 31 \ Vertex 19 \VERTEX 0, -9, 5, 0, 0, 0, 0, 31 \ Vertex 20 \VERTEX -28, 11, -2, 0, 0, 0, 0, 31 \ Vertex 21 \VERTEX -49, 11, -2, 0, 0, 0, 0, 31 \ Vertex 22 \VERTEX -49, 11, -10, 0, 0, 0, 0, 31 \ Vertex 23 \VERTEX -49, 11, -17, 0, 0, 0, 0, 31 \ Vertex 24 \VERTEX -28, 11, -17, 0, 0, 0, 0, 31 \ Vertex 25 \VERTEX -28, 11, -10, 0, 0, 0, 0, 31 \ Vertex 26 \VERTEX -24, 11, -2, 0, 0, 0, 0, 31 \ Vertex 27 \VERTEX -24, 11, -17, 0, 0, 0, 0, 31 \ Vertex 28 \VERTEX -3, 11, -17, 0, 0, 0, 0, 31 \ Vertex 29 \VERTEX 0, 11, -2, 0, 0, 0, 0, 31 \ Vertex 30 \VERTEX 0, 11, -17, 0, 0, 0, 0, 31 \ Vertex 31 \VERTEX 4, 11, -2, 0, 0, 0, 0, 31 \ Vertex 32 \VERTEX 25, 11, -2, 0, 0, 0, 0, 31 \ Vertex 33 \VERTEX 14, 11, -2, 0, 0, 0, 0, 31 \ Vertex 34 \VERTEX 14, 11, -17, 0, 0, 0, 0, 31 \ Vertex 35 \VERTEX 49, 11, -2, 0, 0, 0, 0, 31 \ Vertex 36 \VERTEX 28, 11, -2, 0, 0, 0, 0, 31 \ Vertex 37 \VERTEX 28, 11, -10, 0, 0, 0, 0, 31 \ Vertex 38 \VERTEX 28, 11, -17, 0, 0, 0, 0, 31 \ Vertex 39 \VERTEX 49, 11, -17, 0, 0, 0, 0, 31 \ Vertex 40 \VERTEX 49, 11, -10, 0, 0, 0, 0, 31 \ Vertex 41 \ \.SHIP_LOGO_EDGES \ \ \ vertex1, vertex2, face1, face2, visibility \EDGE 0, 1, 0, 0, 31 \ Edge 0 \EDGE 1, 2, 0, 0, 31 \ Edge 1 \EDGE 2, 3, 0, 0, 31 \ Edge 2 \EDGE 3, 4, 0, 0, 31 \ Edge 3 \EDGE 4, 5, 0, 0, 31 \ Edge 4 \EDGE 5, 6, 0, 0, 31 \ Edge 5 \EDGE 6, 7, 0, 0, 31 \ Edge 6 \EDGE 7, 8, 0, 0, 31 \ Edge 7 \EDGE 8, 9, 0, 0, 31 \ Edge 8 \EDGE 9, 10, 0, 0, 31 \ Edge 9 \EDGE 10, 11, 0, 0, 31 \ Edge 10 \EDGE 11, 0, 0, 0, 31 \ Edge 11 \EDGE 14, 15, 3, 0, 30 \ Edge 12 \EDGE 15, 16, 1, 0, 30 \ Edge 13 \EDGE 16, 17, 4, 0, 30 \ Edge 14 \EDGE 17, 14, 1, 0, 30 \ Edge 15 \EDGE 4, 12, 3, 0, 30 \ Edge 16 \EDGE 12, 13, 2, 2, 30 \ Edge 17 \EDGE 13, 8, 4, 0, 30 \ Edge 18 \EDGE 8, 4, 1, 1, 30 \ Edge 19 \EDGE 4, 14, 3, 1, 30 \ Edge 20 \EDGE 12, 15, 3, 1, 30 \ Edge 21 \EDGE 13, 16, 4, 2, 30 \ Edge 22 \EDGE 8, 17, 4, 1, 30 \ Edge 23 \EDGE 21, 22, 0, 0, 30 \ Edge 24 \EDGE 22, 24, 0, 0, 30 \ Edge 25 \EDGE 24, 25, 0, 0, 30 \ Edge 26 \EDGE 23, 26, 0, 0, 30 \ Edge 27 \EDGE 27, 28, 0, 0, 30 \ Edge 28 \EDGE 28, 29, 0, 0, 30 \ Edge 29 \EDGE 30, 31, 0, 0, 30 \ Edge 30 \EDGE 32, 33, 0, 0, 30 \ Edge 31 \EDGE 34, 35, 0, 0, 30 \ Edge 32 \EDGE 36, 37, 0, 0, 30 \ Edge 33 \EDGE 37, 39, 0, 0, 30 \ Edge 34 \EDGE 39, 40, 0, 0, 30 \ Edge 35 \EDGE 41, 38, 0, 0, 30 \ Edge 36 \ \.SHIP_LOGO_FACES \ \ \ normal_x, normal_y, normal_z, visibility \FACE 0, 23, 0, 31 \ Face 0 \FACE 0, 4, 15, 31 \ Face 1 \FACE 0, 13, -52, 31 \ Face 2 \FACE -81, 81, 0, 31 \ Face 3 \FACE 81, 81, 0, 31 \ Face 4 \ --- End of removed code -----------------------------> \ ****************************************************************************** \ \ Name: SHIP_COUGAR \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Cougar \ Deep dive: Ship blueprints \ Comparing ship specifications \ The elusive Cougar \ \ ****************************************************************************** .SHIP_COUGAR EQUB 3 \ Max. canisters on demise = 3 EQUW 70 * 70 \ Targetable area = 70 * 70 EQUB LO(SHIP_COUGAR_EDGES - SHIP_COUGAR) \ Edges data offset (low) EQUB LO(SHIP_COUGAR_FACES - SHIP_COUGAR) \ Faces data offset (low) EQUB 105 \ Max. edge count = (105 - 1) / 4 = 26 EQUB 0 \ Gun vertex = 0 EQUB 42 \ Explosion count = 9, as (4 * n) + 6 = 42 EQUB 114 \ Number of vertices = 114 / 6 = 19 EQUB 25 \ Number of edges = 25 EQUW 0 \ Bounty = 0 EQUB 24 \ Number of faces = 24 / 4 = 6 EQUB 34 \ Visibility distance = 34 EQUB 252 \ Max. energy = 252 EQUB 40 \ Max. speed = 40 EQUB HI(SHIP_COUGAR_EDGES - SHIP_COUGAR) \ Edges data offset (high) EQUB HI(SHIP_COUGAR_FACES - SHIP_COUGAR) \ Faces data offset (high) EQUB 2 \ Normals are scaled by = 2^2 = 4 EQUB %00110100 \ Laser power = 6 \ Missiles = 4 .SHIP_COUGAR_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, 5, 67, 2, 0, 4, 4, 31 \ Vertex 0 VERTEX -20, 0, 40, 1, 0, 2, 2, 31 \ Vertex 1 VERTEX -40, 0, -40, 1, 0, 5, 5, 31 \ Vertex 2 VERTEX 0, 14, -40, 4, 0, 5, 5, 30 \ Vertex 3 VERTEX 0, -14, -40, 2, 1, 5, 3, 30 \ Vertex 4 VERTEX 20, 0, 40, 3, 2, 4, 4, 31 \ Vertex 5 VERTEX 40, 0, -40, 4, 3, 5, 5, 31 \ Vertex 6 VERTEX -36, 0, 56, 1, 0, 1, 1, 31 \ Vertex 7 VERTEX -60, 0, -20, 1, 0, 1, 1, 31 \ Vertex 8 VERTEX 36, 0, 56, 4, 3, 4, 4, 31 \ Vertex 9 VERTEX 60, 0, -20, 4, 3, 4, 4, 31 \ Vertex 10 VERTEX 0, 7, 35, 0, 0, 4, 4, 18 \ Vertex 11 VERTEX 0, 8, 25, 0, 0, 4, 4, 20 \ Vertex 12 VERTEX -12, 2, 45, 0, 0, 0, 0, 20 \ Vertex 13 VERTEX 12, 2, 45, 4, 4, 4, 4, 20 \ Vertex 14 VERTEX -10, 6, -40, 5, 5, 5, 5, 20 \ Vertex 15 VERTEX -10, -6, -40, 5, 5, 5, 5, 20 \ Vertex 16 VERTEX 10, -6, -40, 5, 5, 5, 5, 20 \ Vertex 17 VERTEX 10, 6, -40, 5, 5, 5, 5, 20 \ Vertex 18 .SHIP_COUGAR_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 2, 0, 31 \ Edge 0 EDGE 1, 7, 1, 0, 31 \ Edge 1 EDGE 7, 8, 1, 0, 31 \ Edge 2 EDGE 8, 2, 1, 0, 31 \ Edge 3 EDGE 2, 3, 5, 0, 30 \ Edge 4 EDGE 3, 6, 5, 4, 30 \ Edge 5 EDGE 2, 4, 5, 1, 30 \ Edge 6 EDGE 4, 6, 5, 3, 30 \ Edge 7 EDGE 6, 10, 4, 3, 31 \ Edge 8 EDGE 10, 9, 4, 3, 31 \ Edge 9 EDGE 9, 5, 4, 3, 31 \ Edge 10 EDGE 5, 0, 4, 2, 31 \ Edge 11 EDGE 0, 3, 4, 0, 27 \ Edge 12 EDGE 1, 4, 2, 1, 27 \ Edge 13 EDGE 5, 4, 3, 2, 27 \ Edge 14 EDGE 1, 2, 1, 0, 26 \ Edge 15 EDGE 5, 6, 4, 3, 26 \ Edge 16 EDGE 12, 13, 0, 0, 20 \ Edge 17 EDGE 13, 11, 0, 0, 18 \ Edge 18 EDGE 11, 14, 4, 4, 18 \ Edge 19 EDGE 14, 12, 4, 4, 20 \ Edge 20 EDGE 15, 16, 5, 5, 18 \ Edge 21 EDGE 16, 18, 5, 5, 20 \ Edge 22 EDGE 18, 17, 5, 5, 18 \ Edge 23 EDGE 17, 15, 5, 5, 20 \ Edge 24 .SHIP_COUGAR_FACES \ normal_x, normal_y, normal_z, visibility FACE -16, 46, 4, 31 \ Face 0 FACE -16, -46, 4, 31 \ Face 1 FACE 0, -27, 5, 31 \ Face 2 FACE 16, -46, 4, 31 \ Face 3 FACE 16, 46, 4, 31 \ Face 4 FACE 0, 0, -160, 30 \ Face 5 \ ****************************************************************************** \ \ Name: SHIP_DODO \ Type: Variable \ Category: Drawing ships \ Summary: Ship blueprint for a Dodecahedron ("Dodo") space station \ Deep dive: Ship blueprints \ Comparing ship specifications \ \ ****************************************************************************** .SHIP_DODO EQUB 0 \ Max. canisters on demise = 0 EQUW 180 * 180 \ Targetable area = 180 * 180 EQUB LO(SHIP_DODO_EDGES - SHIP_DODO) \ Edges data offset (low) EQUB LO(SHIP_DODO_FACES - SHIP_DODO) \ Faces data offset (low) EQUB 101 \ Max. edge count = (101 - 1) / 4 = 25 EQUB 0 \ Gun vertex = 0 EQUB 54 \ Explosion count = 12, as (4 * n) + 6 = 54 EQUB 144 \ Number of vertices = 144 / 6 = 24 EQUB 34 \ Number of edges = 34 EQUW 0 \ Bounty = 0 EQUB 48 \ Number of faces = 48 / 4 = 12 EQUB 125 \ Visibility distance = 125 EQUB 240 \ Max. energy = 240 EQUB 0 \ Max. speed = 0 EQUB HI(SHIP_DODO_EDGES - SHIP_DODO) \ Edges data offset (high) EQUB HI(SHIP_DODO_FACES - SHIP_DODO) \ Faces data offset (high) EQUB 0 \ Normals are scaled by = 2^0 = 1 EQUB %00000000 \ Laser power = 0 \ Missiles = 0 .SHIP_DODO_VERTICES \ x, y, z, face1, face2, face3, face4, visibility VERTEX 0, 150, 196, 1, 0, 5, 5, 31 \ Vertex 0 VERTEX 143, 46, 196, 1, 0, 2, 2, 31 \ Vertex 1 VERTEX 88, -121, 196, 2, 0, 3, 3, 31 \ Vertex 2 VERTEX -88, -121, 196, 3, 0, 4, 4, 31 \ Vertex 3 VERTEX -143, 46, 196, 4, 0, 5, 5, 31 \ Vertex 4 VERTEX 0, 243, 46, 5, 1, 6, 6, 31 \ Vertex 5 VERTEX 231, 75, 46, 2, 1, 7, 7, 31 \ Vertex 6 VERTEX 143, -196, 46, 3, 2, 8, 8, 31 \ Vertex 7 VERTEX -143, -196, 46, 4, 3, 9, 9, 31 \ Vertex 8 VERTEX -231, 75, 46, 5, 4, 10, 10, 31 \ Vertex 9 VERTEX 143, 196, -46, 6, 1, 7, 7, 31 \ Vertex 10 VERTEX 231, -75, -46, 7, 2, 8, 8, 31 \ Vertex 11 VERTEX 0, -243, -46, 8, 3, 9, 9, 31 \ Vertex 12 VERTEX -231, -75, -46, 9, 4, 10, 10, 31 \ Vertex 13 VERTEX -143, 196, -46, 6, 5, 10, 10, 31 \ Vertex 14 VERTEX 88, 121, -196, 7, 6, 11, 11, 31 \ Vertex 15 VERTEX 143, -46, -196, 8, 7, 11, 11, 31 \ Vertex 16 VERTEX 0, -150, -196, 9, 8, 11, 11, 31 \ Vertex 17 VERTEX -143, -46, -196, 10, 9, 11, 11, 31 \ Vertex 18 VERTEX -88, 121, -196, 10, 6, 11, 11, 31 \ Vertex 19 VERTEX -16, 32, 196, 0, 0, 0, 0, 30 \ Vertex 20 VERTEX -16, -32, 196, 0, 0, 0, 0, 30 \ Vertex 21 VERTEX 16, 32, 196, 0, 0, 0, 0, 23 \ Vertex 22 VERTEX 16, -32, 196, 0, 0, 0, 0, 23 \ Vertex 23 .SHIP_DODO_EDGES \ vertex1, vertex2, face1, face2, visibility EDGE 0, 1, 1, 0, 31 \ Edge 0 EDGE 1, 2, 2, 0, 31 \ Edge 1 EDGE 2, 3, 3, 0, 31 \ Edge 2 EDGE 3, 4, 4, 0, 31 \ Edge 3 EDGE 4, 0, 5, 0, 31 \ Edge 4 EDGE 5, 10, 6, 1, 31 \ Edge 5 EDGE 10, 6, 7, 1, 31 \ Edge 6 EDGE 6, 11, 7, 2, 31 \ Edge 7 EDGE 11, 7, 8, 2, 31 \ Edge 8 EDGE 7, 12, 8, 3, 31 \ Edge 9 EDGE 12, 8, 9, 3, 31 \ Edge 10 EDGE 8, 13, 9, 4, 31 \ Edge 11 EDGE 13, 9, 10, 4, 31 \ Edge 12 EDGE 9, 14, 10, 5, 31 \ Edge 13 EDGE 14, 5, 6, 5, 31 \ Edge 14 EDGE 15, 16, 11, 7, 31 \ Edge 15 EDGE 16, 17, 11, 8, 31 \ Edge 16 EDGE 17, 18, 11, 9, 31 \ Edge 17 EDGE 18, 19, 11, 10, 31 \ Edge 18 EDGE 19, 15, 11, 6, 31 \ Edge 19 EDGE 0, 5, 5, 1, 31 \ Edge 20 EDGE 1, 6, 2, 1, 31 \ Edge 21 EDGE 2, 7, 3, 2, 31 \ Edge 22 EDGE 3, 8, 4, 3, 31 \ Edge 23 EDGE 4, 9, 5, 4, 31 \ Edge 24 EDGE 10, 15, 7, 6, 31 \ Edge 25 EDGE 11, 16, 8, 7, 31 \ Edge 26 EDGE 12, 17, 9, 8, 31 \ Edge 27 EDGE 13, 18, 10, 9, 31 \ Edge 28 EDGE 14, 19, 10, 6, 31 \ Edge 29 EDGE 20, 21, 0, 0, 30 \ Edge 30 EDGE 21, 23, 0, 0, 20 \ Edge 31 EDGE 23, 22, 0, 0, 23 \ Edge 32 EDGE 22, 20, 0, 0, 20 \ Edge 33 .SHIP_DODO_FACES \ normal_x, normal_y, normal_z, visibility FACE 0, 0, 196, 31 \ Face 0 FACE 103, 142, 88, 31 \ Face 1 FACE 169, -55, 89, 31 \ Face 2 FACE 0, -176, 88, 31 \ Face 3 FACE -169, -55, 89, 31 \ Face 4 FACE -103, 142, 88, 31 \ Face 5 FACE 0, 176, -88, 31 \ Face 6 FACE 169, 55, -89, 31 \ Face 7 FACE 103, -142, -88, 31 \ Face 8 FACE -103, -142, -88, 31 \ Face 9 FACE -169, 55, -89, 31 \ Face 10 FACE 0, 0, -196, 31 \ Face 11 EQUB &A9, &80 \ These bytes appear to be unused EQUB &14, &2B EQUB &20, &FD EQUB &B8, &90 EQUB &01, &60 \ ****************************************************************************** \ \ Name: EraseRestOfPlanet \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw all remaining lines in the ball line heap to erase the rest \ of the old planet \ \ ****************************************************************************** \ --- Mod: Code added for flicker-free planets: -------> .EraseRestOfPlanet LDY LSNUM \ Set Y to the offset in LSNUM, which points to the part \ of the heap that we are overwriting with new points CPY LSNUM2 \ If LSNUM >= LSNUM2, then we have already redrawn all BCS eras1 \ of the lines from the old circle's ball line heap, so \ skip the following JSR DrawPlanetLine \ Erase the next planet line from the ball line heap JMP EraseRestOfPlanet \ Loop back for the next line in the ball line heap .eras1 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: DrawPlanetLine \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw a segment of the old planet from the ball line heap \ \ ------------------------------------------------------------------------------ \ \ Other entry points: \ \ DrawPlanetLine+2 If bit 7 of K3+8 is set, store the line coordinates in \ K3+4 to K3+7 (X1, Y1, X2, Y2) and do not draw the line \ \ ****************************************************************************** \ --- Mod: Code added for flicker-free planets: -------> .DrawPlanetLine STZ K3+8 \ Clear bit 7 of K3+8 so we draw the current line below STZ K3+9 \ Clear bit 7 of K3+9 to indicate that there is no line \ to draw (we may change this below) LDA LSNUM \ If LSNUM = 1, then this is the first point from the CMP #2 \ heap, so jump to plin3 to set the previous coordinate BCC plin3 \ and return from the subroutine LDA X1 \ Save X1, X2, Y1, Y2 and Y on the stack PHA LDA Y1 PHA LDA X2 PHA LDA Y2 PHA TYA PHA LDY LSNUM \ Set Y to the offset in LSNUM, which points to the part \ of the heap that we are overwriting with new points CPY LSNUM2 \ If LSNUM >= LSNUM2, then we have already redrawn all BCS plin1 \ of the lines from the old circle's ball line heap, so \ jump to plin1 to return from the subroutine \ Otherwise we need to draw the line from the heap, to \ erase it from the screen LDA K3+2 \ Set X1 = K3+2 = screen x-coordinate of previous point STA X1 \ from the old heap LDA K3+3 \ Set Y1 = K3+3 = screen y-coordinate of previous point STA Y1 \ from the old heap \ --- Mod: Code removed for two-player Elite: ---------> \LDA LSX2,Y \ Set X2 to the y-coordinate from the LSNUM-th point in \STA X2 \ the heap \ --- And replaced by: --------------------------------> LDA (LSX2S),Y \ Set X2 to the y-coordinate from the LSNUM-th point in STA X2 \ the heap \ --- End of replacement ------------------------------> STA K3+2 \ Store the x-coordinate of the point we are overwriting \ in K3+2, so we can use it on the next iteration \ --- Mod: Code removed for two-player Elite: ---------> \LDA LSY2,Y \ Set Y2 to the y-coordinate from the LSNUM-th point in \STA Y2 \ the heap \ --- And replaced by: --------------------------------> LDA (LSY2S),Y \ Set Y2 to the y-coordinate from the LSNUM-th point in STA Y2 \ the heap \ --- End of replacement ------------------------------> STA K3+3 \ Store the y-coordinate of the point we are overwriting \ in K3+3, so we can use it on the next iteration INC LSNUM \ Increment LSNUM to point to the next coordinate, so we \ work our way through the current heap LDA Y1 \ If Y1 or Y2 = &FF then this indicates a break in the CMP #&FF \ circle, so jump to plin1 to skip the following and BEQ plin1 \ return from the subroutine, asthere is no line to LDA Y2 \ erase CMP #&FF BEQ plin1 DEC K3+9 \ Decrement K3+9 to &FF to indicate that there is a line \ to draw BIT K3+8 \ If bit 7 of K3+8 is set, jump to plin2 to store the BMI plin2 \ line coordinates rather than drawing the line JSR LL30 \ The coordinates in (X1, Y1) and (X2, Y2) that we just \ pulled from the ball line heap point to a line that is \ still on-screen, so call LL30 to draw this line and \ erase it from the screen .plin1 PLA \ Restore Y, X1, X2, Y1 and Y2 from the stack TAY PLA STA Y2 PLA STA X2 PLA STA Y1 PLA STA X1 RTS \ Return from the subroutine .plin2 LDA X1 \ Store X1, Y1, X2, Y2 in K3+4 to K3+7 STA K3+4 LDA Y1 STA K3+5 LDA X2 STA K3+6 LDA Y2 STA K3+7 JMP plin1 \ Jump to plin1 to return from the subroutine .plin3 \ --- Mod: Code removed for two-player Elite: ---------> \LDA LSX2+1 \ Store the heap's first coordinate in K3+2 and K3+3 \STA K3+2 \LDA LSY2+1 \STA K3+3 \ --- And replaced by: --------------------------------> PHY \ Store the value of Y on the stack so we can preserve \ it LDY #1 \ Store the heap's first coordinate in K3+2 and K3+3 LDA (LSX2S),Y STA K3+2 LDA (LSY2S),Y STA K3+3 PLY \ Retrieve Y from the stack \ --- End of replacement ------------------------------> INC LSNUM \ Increment LSNUM to point to the next coordinate, so we \ work our way through the current heap RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: DrawNewPlanetLine \ Type: Subroutine \ Category: Drawing lines \ Summary: Draw a ball line, but only if it is different to the old line \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ K3+4 to K3+7 The (X1, Y1) and (X2, Y2) coordinates of the old line \ \ ****************************************************************************** \ --- Mod: Code added for flicker-free planets: -------> .DrawNewPlanetLine BIT K3+9 \ If bit 7 of K3+9 is clear, then there is no old line BPL nlin2 \ to draw, so jump to nlin2 to draw the new line only LDA K3+4 \ If the old line equals the new line, jump to nlin3 CMP X1 \ to skip drawing both lines BNE nlin1 LDA K3+5 CMP Y1 BNE nlin1 LDA K3+6 CMP X2 BNE nlin1 LDA K3+7 CMP Y2 BEQ nlin3 .nlin1 \ If we get here then the old line is different to the \ new line, so we draw them both JSR LL30 \ Draw the new line from (X1, Y1) to (X2, Y2) LDA K3+4 \ Set up the old line's coordinates STA X1 LDA K3+5 STA Y1 LDA K3+6 STA X2 LDA K3+7 STA Y2 .nlin2 JSR LL30 \ Draw the old line to erase it .nlin3 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: ROTATE_COORDINATE_24 \ Type: Macro \ Category: Two-player Elite \ Summary: Rotate a coordinate by an orientation vector (24-bits) \ \ ------------------------------------------------------------------------------ \ \ Rotate a coordinate (c1, c2, c3) by an orientation vector [ v1 v2 v3 ] and \ store the result in the coordinate c. The calculation is: \ \ c = [ v1 v2 v3 ] . [ c1 c2 c3 ] \ = v1 * c1 + v2 * c2 + v3 * c3 \ \ where c1, c2, c3 and v1, v2, v3 are INWK offsets, and c is an offset into the \ newCoords block. \ \ So values of c1, c2, c3 mean: \ \ * 0 = INWK+0 to INWK+2 (x_sign x_hi x_lo) \ * 3 = INWK+3 to INWK+5 (y_sign y_hi y_lo) \ * 6 = INWK+6 to INWK+8 (z_sign z_hi z_lo) \ \ and values of v1, v2, v3 mean: \ \ * 9 = (nosev_x_hi nosev_x_lo) \ * 11 = (nosev_y_hi nosev_y_lo) \ * 13 = (nosev_z_hi nosev_z_lo) \ \ * 15 = (roofv_x_hi roofv_x_lo) \ * 17 = (roofv_y_hi roofv_y_lo) \ * 19 = (roofv_z_hi roofv_z_lo) \ \ * 21 = (sidev_x_hi sidev_x_lo) \ * 23 = (sidev_y_hi sidev_y_lo) \ * 25 = (sidev_z_hi sidev_z_lo) \ \ while values of c mean: \ \ * 0 = store 24-bit x-coordinate in newCoords+0 to newCoords+2 \ * 3 = store 24-bit y-coordinate in newCoords+3 to newCoords+5 \ * 6 = store 24-bit z-coordinate in newCoords+6 to newCoords+8 \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> MACRO ROTATE_COORDINATE_24 c, v1, v2, v3, c1, c2, c3 \ c = [ v1 v2 v3 ] . [ c1 c2 c3 ] \ v1 * c1 + v2 * c2 + v3 * c3 \ \ Commentary has c = z-coordinate \ v1 = nosev_x \ v2 = nosev_y \ v3 = nosev_z \ c1 = x \ c2 = y \ c3 = z LDY #v1 \ First do nosev_x * x, so calculate: LDX #c1 \ JSR Multiply16x24 \ K(3 2 1 0) = nosev_x * x \ \ i.e. (nosev_x_hi nosev_x_lo) * (x_sign x_hi x_lo) LDA K+1 \ Copy result from K(3 2 1) to XX12(2 1 0), ignoring low STA XX12 \ byte, so: LDA K+2 \ STA XX12+1 \ XX12(2 1 0) = nosev_x * x LDA K+3 STA XX12+2 BIT K \ Round up byte #1 of result if bit 7 of byte #0 is set BPL rotc1 INC XX12 BNE rotc1 INC XX12+1 BNE rotc1 INC XX12+2 .rotc1 LDY #v2 \ Next do nosev_y * y, so calculate: LDX #c2 \ JSR Multiply16x24 \ K(3 2 1 0) = nosev_y * y \ \ i.e. (nosev_y_hi nosev_y_lo) * (y_sign y_hi y_lo) LDA K+1 \ Copy result from K(3 2 1) to XX12(5 4 3), ignoring low STA XX12+3 \ byte, so: LDA K+2 \ STA XX12+4 \ XX12(5 4 3) = nosev_y * y LDA K+3 STA XX12+5 BIT K \ Round up byte #1 of result if bit 7 of byte #0 is set BPL rotc2 INC XX12+3 BNE rotc2 INC XX12+4 BNE rotc2 INC XX12+5 .rotc2 LDY #v3 \ Then do nosev_z * z, so calculate LDX #c3 \ JSR Multiply16x24 \ K(3 2 1 0) = nosev_z * z \ \ i.e. (nosev_z_hi nosev_z_lo) * (z_sign z_hi z_lo) BIT K \ Round up byte #1 of result if bit 7 of byte #0 is set BPL rotc3 INC K+1 BNE rotc3 INC K+2 BNE rotc3 INC K+3 .rotc3 \ By this point we have: \ \ XX12(2 1 0) = nosev_x * x \ XX12(5 4 3) = nosev_y * y \ K(3 2 1) = nosev_z * z \ \ So now we need to add them all together LDA XX12 \ Set XX15(3 2 1) = XX12(2 1 0) STA XX15+1 \ = nosev_x * x LDA XX12+1 STA XX15+2 LDA XX12+2 STA XX15+3 JSR Add24 \ Calculate: \ \ P(2 1 0) = K(3 2 1) + XX15(3 2 1) \ = (nosev_z * z) + (nosev_x * x) LDA XX12+3 \ Set XX15(3 2 1) = XX12(5 4 3) STA XX15+1 \ = nosev_y * y LDA XX12+4 STA XX15+2 LDA XX12+5 STA XX15+3 LDA P \ Set K(3 2 1) = P(2 1 0) STA K+1 \ = (nosev_z * z) + (nosev_x * x) LDA P+1 STA K+2 LDA P+2 STA K+3 JSR Add24 \ Calculate: \ \ P(2 1 0) = K(3 2 1) + XX15(3 2 1) \ = (nosev_z * z) + (nosev_x * x) \ + (nosev_y * y) JSR DivideBy96 \ Calculate: \ \ K(3 2 1 0) = P(2 1 0) / 96 LDA K+3 \ Extract the sign from K+3 to put into K+2 AND #%10000000 STA T LDA K \ Set player 2's z-coordinate to the result in K(2 1 0) STA newCoords+c LDA K+1 STA newCoords+c+1 LDA K+2 AND #%01111111 ORA T STA newCoords+c+2 ENDMACRO \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: DrawPlayer2View (Part 1 of 6) \ Type: Subroutine \ Category: Two-player Elite \ Summary: Draw the current ship in player 2's view \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .DrawPlayer2View JSR SaveShipData \ Save current INWK state so we can restore it later LDA XSAV \ Set player2ViewSlot to slot #10 + XSAV, which is the CLC \ slot number of the ship we are processing but for ADC #10 \ player 2's view STA player2ViewSlot LDX XSAV \ If we are drawing the planet or sun, jump to part 2 to CPX #2 \ check whether it is on-screen and if so, only move it BCS P%+5 \ by the rotations of player 2's controls JMP dshp9 CPX #5 \ If this is slot #5 or above then return without BCC P%+3 \ drawing anything, as we only process slots #0 to #4 RTS \ If we get here we are drawing player 1's ship or a \ missile DEX \ Set player1X to the original ship slot number from the DEX \ stack, so we get 0 for player 1's ship, or 1 and 2 for STX player1X \ the missiles, which we can use as an index into the \ player1INWK31 table LDA player1INWK31,X \ If the ship's INWK+31 byte is non-zero then this is BNE dshp1 \ not the first time we've called this routine for this \ ship, so jump to dshp1 to keep processing this ship \ If we get here then this is the first time we've \ called this routine for this ship, so we need to do \ some initiaisation LDA #%00010000 \ Set bit #4 to indicate that the new ship should be STA player1INWK31,X \ drawn on the scanner (and to make player1INWK31 \ non-zero so we don't repeat these initialisation \ steps) LDA INWK+34 \ Set the high heap byte to set the heap address for the PHA \ ship in player 2's view to &2000 below the heap for SEC \ the ship in player 1's view, storing the original high SBC #&20 \ byte on the stack STA INWK+34 LDY #2 \ Set the Y2 coordinate of the laser line in the ship LDA #255 \ line heap to 255 so there is no laser line in the heap STA (INWK+33),Y \ for player 2's view PLA \ Restore the original heap address STA INWK+34 JMP dshp12 \ Jump to part 3 to draw the ship, as we don't need to \ do explosion and scanner checks on a new ship .dshp1 CPX #0 \ If this is not the player's ship, jump to dshp5 to BNE dshp5 \ skip the explosion checks BIT player2Exploding \ If bit 7 of player2Exploding is clear then player 2 BPL dshp2 \ has not just exploded, so jump to dshp2 to check bit 6 \ If we get here then player 2 has just exploded LDA #%01000000 \ Clear bit 7 and set bit 6 of player2Exploding to move STA player2Exploding \ the explosion on to the second stage LDX #12 \ Fetch the ship's coordinates for player 2's ship from JSR GetShipDataToINWK \ slot #12 so that if the ship is already on the scanner \ we can remove it LDA player1INWK31 \ Set the scanner visibility flag from player1INWK31 STA INWK+31 LDA #YELLOW2 \ Remove the ship from the scanner, if it's there JSR SCAN LDA player1INWK31 \ Prevent player 1's ship in player 2's view from AND #%01001111 \ exploding or appearing on the scanner STA player1INWK31 JMP dshp12 \ Jump to part 3 to skip all the other explosion checks .dshp2 BIT player2Exploding \ If bit 6 of player2Exploding is clear then player 1 is BVC dshp3 \ not currently exploding, so jump to dshp5 to keep \ going LDA player1INWK31 \ Prevent player 1's ship in player 2's view from AND #%01001111 \ exploding or appearing on the scanner STA player1INWK31 JMP dshp12 \ Jump to part 3 to skip all the other explosion checks .dshp3 BIT player1Exploding \ If bit 7 of player1Exploding is clear then player 1 BPL dshp4 \ has not just exploded, so jump to dshp4 to check bit 6 \ If we get here then player 1 has just exploded LDA #%01000000 \ Clear bit 7 and set bit 6 of player1Exploding to move STA player1Exploding \ the explosion on to the second stage BRA dshp5 \ Jump to dshp5 to keep going .dshp4 BIT player1Exploding \ If bit 6 of player1Exploding is clear then player 1 is BVC dshp5 \ not currently exploding, so jump to dshp5 to keep \ going \ If we get here then player 1 is currently exploding LDA player1INWK31 \ Set bits 5, 6 and 7 to indicate that the ship is ORA #%11100000 \ exploding and that the explosion is on-screen STA player1INWK31 BRA dshp6 \ Jump to dshp6 to skip the INWK+31 check, as we are no \ longer taking any notice of slot #2's INWK+31 (as that \ is for player 2's ship in player 1's view) .dshp5 LDA INWK+31 \ If the ship is not exploding, jump to dshp8 AND #%10100000 BEQ dshp8 .dshp6 LDA player1INWK31,X \ If we have already noted the explosion in the copy of AND #%00100000 \ INWK+31 in player1INWK31, jump to part 3 to skip the BNE dshp7 \ following, as we have already removed the ship from \ the scanner \ If we get here then bit 7 is set but bit 5 is not, so \ the ship has just started to explode LDA player1INWK31,X \ If we get here then the ship has just exploded but we AND #%11001111 \ haven't removed the yellow copy from the scanner, so ORA #%10000000 \ first of all copy the new explosion and scaner states STA player1INWK31,X \ to player1INWK31 \ Now we need to remove the yellow copy from the scanner LDX player2ViewSlot \ Fetch the ship's coordinates from slot #10 + ID (i.e. JSR GetShipDataToINWK \ #12 for player 2's ship, or #13 or #14 for a missile) \ so that if the ship is already on the scanner, we can \ remove it (if the ship hasn't yet been processed in \ this new slot, nothing will happen in the following) LDA #%00010000 \ Set bit #4 so the call to SCAN will draw the ship on STA INWK+31 \ the scanner, thus removing it LDA #YELLOW2 \ Remove the ship from the scanner, if it's there JSR SCAN .dshp7 JMP dshp12 \ Jump to part 3 .dshp8 LDX player2ViewSlot \ Fetch the ship's coordinates from slot #10 + ID (i.e. JSR GetShipDataToINWK \ #12 for player 2's ship, or #13 or #14 for a missile) \ so that if the ship is already on the scanner, we can \ remove it (if the ship hasn't yet been processed in \ this new slot, nothing will happen in the following) LDX player1X \ Set the scanner visiblilty flag from player1INWK31 LDA player1INWK31,X STA INWK+31 LDA #YELLOW2 \ Remove the ship from the scanner, if it's there JSR SCAN JMP dshp12 \ Jump to part 3 to skip the planet/sun code in part 2 \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: DrawPlayer2View (Part 2 of 6) \ Type: Subroutine \ Category: Two-player Elite \ Summary: If the planet/sun is on-screen, only move it by the rotations of \ player 2's controls, so it doesn't jump around on-screen \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .dshp9 LDA INWK+5 \ If the planet/sun is within |y_sign| < 2 then it is AND #%01111111 \ close to the player's view, so apply player 2's CMP #2 \ movement to the current position of the planet by BCC dshp11 \ jumping to dshp11 (to prevent it jumping back onto the \ screen when we start using the correct position again) CPX #1 \ If we are drawing the sun, jump to dshp10 BEQ dshp10 LDA LSX2a \ If LSX2a is non-zero then the ball line heap for BNE dshp12 \ player 2's view is empty, so the planet is not \ currently visible to player 2, so jump to dshp12 to \ move the planet to the correct position in space BEQ dshp11 \ If we get here then the planet is visible in player \ 2's view, so instead of calculating its correct \ position in space (which will make it jump around \ due to the approximate maths used in rotations), we \ instead apply player 2's movement to the current \ position of the planet by jumping to dshp11 .dshp10 LDA LSXa \ If LSXa < 0 then the sun line heap for player 2's view BMI dshp12 \ is empty, so the sun is not currently visible to \ player 2, so jump to dshp12 to move the sun to the \ correct position in space .dshp11 \ If we get here then the planet/sun is visible in \ player 2's view, so instead of calculating its correct \ position in space (which will make it jump around \ due to the approximate maths used in rotations), we \ instead apply player 2's movement to the current \ position of the planet/sun LDX player2ViewSlot \ Fetch the ship data for the planet/sun from slot #10 JSR GetShipDataToINWK \ or #11, which contains the coordinates for player 2's \ view JSR SaveShipMovement \ Switch to player 2's movement data JSR GetPlayer2Movement LDA #&60 \ Change the first instruction in part 9 of MVEIT to an STA MV5 \ RTS to the following call to MV40 will run MV40 and \ parts 6 to 8 of MVEIT before returning here (so we \ apply player 2's movement to the planet/sun as well as \ rotating the planet's orientation vectors to make it \ spin) JSR MV40 \ Rotate the planet/sun by player 2's alpha and beta and \ make the planet spin LDA #&A5 \ Change the first instruction in part 9 of MVEIT back STA MV5 \ to an LDA instruction JSR LoadShipMovement \ Switch back to the previous movement data JMP dshp20 \ Jump to part 4 to draw the planet/sun \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: DrawPlayer2View (Part 3 of 6) \ Type: Subroutine \ Category: Two-player Elite \ Summary: Calculate the ship's coordinates and orientation in player 2's \ frame of reference \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .dshp12 LDX XSAV \ Refetch the current ship data so that it's the correct JSR GetShipDataToINWK \ way around for the following calculation, even if \ player 1 has changed view (so this reverts any PLUT \ calls in the main loop) \ We now calculate the 3D coordinates and orientation \ vectors for player 1's ship within player 2's frame of \ reference, so we can draw player 1's ship in player \ 2's space view \ \ If: \ \ * [x y z] are player 2's coordinates, which is the \ same as the vector from player 1 to player 2 \ \ * sidev, roofv and nosev are player 2's orientation \ vectors \ \ Then do the following: \ \ 1. Negate [x y z] so it turns into the vector from \ player 2 to player 1 (do this for player 2's ship \ coordinates only) \ \ 2. Multiply the result by player 2's orientation \ vectors to move the vector into player 2's frame of \ reference, as follows: \ \ x = [sidev_x sidev_y sidev_z] . [x y z] \ y = [roofv_x roofv_y roofv_z] . [x y z] \ z = [nosev_x nosev_y nosev_z] . [x y z] \ \ We can do the calculation like this: \ \ x = (sidev_x_hi sidev_x_lo) * (x_sign x_hi x_lo) \ + (sidev_y_hi sidev_y_lo) * (y_sign y_hi y_lo) \ + (sidev_z_hi sidev_z_lo) * (z_sign z_hi z_lo) \ \ y = (roofv_x_hi roofv_x_lo) * (x_sign x_hi x_lo) \ + (roofv_y_hi roofv_y_lo) * (y_sign y_hi y_lo) \ + (roofv_z_hi roofv_z_lo) * (z_sign z_hi z_lo) \ \ z = (nosev_x_hi nosev_x_lo) * (x_sign x_hi x_lo) \ + (nosev_y_hi nosev_y_lo) * (y_sign y_hi y_lo) \ + (nosev_z_hi nosev_z_lo) * (z_sign z_hi z_lo) \ \ And divide each coordinate by (0 96 0) as that's how \ the unit vector is represented \ \ Step 3: Transpose the orientation matrix for player 2 \ in player 1's view, so that's: \ \ sidev_x sidev_y sidev_z sidev_x roofv_x nosev_x \ roofv_x roofv_y roofv_z -> sidev_y roofv_y nosev_y \ nosev_x nosev_y nosev_z sidev_z roofv_z nosev_z \ \ The result is the orientation matrix (and therefore \ orientation vectors) for player 1's ship in player 2's \ frame of reference \ Step 1: negate [x y z] (if this is player 2's ship) LDA XSAV \ If this is the planet or sun, jump to dshp15 to skip CMP #2 \ the following, so we don't change the coordinates for BCC dshp15 \ the sun and planet BEQ dshp14 \ If this is player 2's ship, jump to dshp14 so we only \ do the following for missiles \ If we get here then this is a missile and INWK is set \ to the missile's data block LDA UNIV+4 \ Set (A V) to the address of player 2's ship data block STA V \ in slot #2 LDA UNIV+4+1 JSR VCSUB \ Calculate vector K3 as follows: \ \ K3(2 1 0) = (x_sign x_hi x_lo) - x-coordinate of \ player 2's ship \ \ K3(5 4 3) = (y_sign y_hi z_lo) - y-coordinate of \ player 2's ship \ \ K3(8 7 6) = (z_sign z_hi z_lo) - z-coordinate of \ player 2's ship \ So K3 now contains the vector from player 2 to the \ missile LDX #8 \ We now want to copy the result from K3 to INWK, so set \ up a counter for 9 bytes .dshp13 LDA K3,X \ Copy the X-th byte from K3 to the X-th byte of INWK STA INWK,X DEX \ Decrement the counter BPL dshp13 \ Loop back until we have copied all 9 bytes \ INWK now contains: \ \ Missile (x, y, z) - player 2 (x, y, z) \ \ which is the result we want BMI dshp15 \ We have the result we want, so skip the negation and \ move on to the coordinate rotation (this BMI is \ effectively a JMP as we just passed through a BPL) .dshp14 LDA INWK+2 \ Negate x_sign EOR #%10000000 STA INWK+2 LDA INWK+5 \ Negate y_sign EOR #%10000000 STA INWK+5 LDA INWK+8 \ Negate z_sign EOR #%10000000 STA INWK+8 .dshp15 \ Step 2: Rotate x-coordinate \ c = [ v1 v2 v3 ] . [ c1 c2 c3 ] \ x = [ sidev_x sidev_y sidev_z ] . [ x y z ] \ c, v1, v2, v3, c1, c2, c3 ROTATE_COORDINATE_24 0, 21, 23, 25, 0, 3, 6 \ Step 2: Rotate y-coordinate \ c = [ v1 v2 v3 ] . [ c1 c2 c3 ] \ y = [ roofv_x roofv_y roofv_z ] . [ x y z ] \ c, v1, v2, v3, c1, c2, c3 ROTATE_COORDINATE_24 3, 15, 17, 19, 0, 3, 6 \ Step 2: Rotate z-coordinate \ c = [ v1 v2 v3 ] . [ c1 c2 c3 ] \ z = [ nosev_x nosev_y nosev_z ] . [ x y z ] \ c, v1, v2, v3, c1, c2, c3 ROTATE_COORDINATE_24 6, 9, 11, 13, 0, 3, 6 LDA newCoords \ Copy new coords back into INWK STA INWK LDA newCoords+1 STA INWK+1 LDA newCoords+2 STA INWK+2 LDA newCoords+3 STA INWK+3 LDA newCoords+4 STA INWK+4 LDA newCoords+5 STA INWK+5 LDA newCoords+6 STA INWK+6 LDA newCoords+7 STA INWK+7 LDA newCoords+8 STA INWK+8 LDA XSAV \ If this is the planet or sun, jump to part 4 to skip CMP #2 \ the transpose, as we don't bother to rotate the planet BCC dshp20 \ or sun or need to override its type LDX player1X \ Set the scan visiblilty flag from player1INWK31 LDA player1INWK31,X STA INWK+31 AND #%10100000 \ If the ship is exploding, skip the following so we do BNE dshp16 \ not draw the ship on the scanner LDA #YELLOW2 \ Draw the ship on the scanner JSR SCAN .dshp16 LDA XSAV \ If this is player 2's ship, jump to dshp17 to skip CMP #2 \ the following instruction BEQ dshp17 JMP OrientateMissile \ Step 3: Calculate the missile's orientation matrix, \ jumping back to dshp18 when we're done .dshp17 JSR TransposeMatrix \ Step 3: Transpose the player 2 orientation matrix .dshp18 \ Set heap for player 2's view to be &2000 below player \ 1's view LDA INWK+34 \ Set the high heap byte to set the heap address for the SEC \ ship in player 2's view to &2000 below the heap for SBC #&20 \ the ship in player 1's view STA INWK+34 LDA #1 \ Set A to the correct ship type for a missile LDX XSAV \ If we're drawing a missile, jump to dshp19 to skip the CPX #3 \ next instruction BCS dshp19 LDA player1ShipType \ We're drawing player 1, so switch to the correct ship \ type .dshp19 STA TYPE \ Store the correct type for the ship we are drawing ASL A \ Set Y = ship type * 2 TAY LDA XX21-2,Y \ The ship blueprints at XX21 start with a lookup STA XX0 \ table that points to the individual ship blueprints, \ so this fetches the low byte of this particular ship \ type's blueprint and stores it in XX0 LDA XX21-1,Y \ Fetch the high byte of this particular ship type's STA XX0+1 \ blueprint and store it in XX0+1 LDX player1X \ Copy "on-screen" state from player1INWK31 to INWK+31 LDA player1INWK31,X STA INWK+31 \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: DrawPlayer2View (Part 4 of 6) \ Type: Subroutine \ Category: Two-player Elite \ Summary: Draw the ship in player 2's view \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .dshp20 SEC \ Configure drawing for player 2 ROR drawPlayerView LDX player2ViewSlot \ Save the new coordinates into slot #10 + XSAV JSR SaveShipDataInSlot LDX player2VIEW \ Rotate everything into the correct view JSR PLUT+3 JSR LL9 \ Call LL9 to draw the ship from player 2's perspective \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: DrawPlayer2View (Part 5 of 6) \ Type: Subroutine \ Category: Two-player Elite \ Summary: Target and laser checks for player 1's ship in player 2's view \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> LDA XSAV \ If we are drawing the planet or sun, jump to part 6 to CMP #2 \ skip the following ship-related checks BCC dshp23 LDX player1X \ Copy "on-screen" state from INWK+31 to player1INWK31 LDA INWK+31 \ for this ship STA player1INWK31,X JSR HITCH \ Call HITCH to see if this ship is in the crosshairs, BCC dshp23 \ in which case the C flag will be set (so if there is \ no missile or laser lock, we jump to dshp23 to skip \ the following) LDA player2MSAR \ We have missile lock, so check whether the leftmost BEQ dshp21 \ missile is currently armed, and if not, jump to dshp21 \ to process laser fire, as we can't lock an unarmed \ missile JSR BEEP \ We have missile lock and an armed missile, so call \ the BEEP subroutine to make a short, high beep LDX player2ViewSlot \ Call ABORT2 to store the details of this missile LDY #RED2 \ lock, with the targeted ship's slot number in JSR Player2ABORT2 \ player2ViewSlot and set the colour of the missile \ indicator to the colour in Y .dshp21 LDA player2LAS \ If player 2 is firing a laser then LAS will contain BEQ dshp23 \ the laser power, so if this is zero, jump to part 6 \ to skip the following LDX #15 \ Player 2 is firing a laser and the ship in INWK is in JSR EXNO \ the crosshairs, so call EXNO to make the sound of \ us making a laser strike on another ship LDA XSAV \ If this ship in the crosshairs is player 1, jump to CMP #2 \ dshp22 to process a hit on player 1 BEQ dshp22 \ This must be a missile, so kill it instantly (as the \ missile's maximum energy is only 2, which is way less \ than any individual laser hit) ASL storeData+31 \ Set bit 7 of the missile's byte #31 flag to mark it as SEC \ having been killed, so it explodes ROR storeData+31 \ \ We need to update the flag in storeData as we are \ about to throw away INWK for the missile and restore \ it from storeData with a call to LoadShipData, so this \ ensures the new setting is retained and processed back \ in the main loop JMP dshp23 \ Jump to part 6 to finish up .dshp22 \ Player 1 has been hit, so process player 1's shields LDX player2VIEW \ Fetch the power of the current laser and clear the LDA player2LASER,X \ continuous bit to fetch the power per pulse, and then AND #%01111111 \ halve the laser power to get the damage level LSR A JSR OOPS \ Remove the relevant energy from player 1's shields and \ update the scores \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: DrawPlayer2View (Part 6 of 6) \ Type: Subroutine \ Category: Two-player Elite \ Summary: Reload the previous ship data configuration and finish up \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .dshp23 STZ drawPlayerView \ Back to drawing the view for player 1 JSR LoadShipData \ Reload INWK state RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: TransposeMatrix \ Type: Subroutine \ Category: Two-player Elite \ Summary: Transpose the current ship's rotation matrix (i.e. its orientation \ vectors) \ \ ------------------------------------------------------------------------------ \ \ Transpose matrix to rotate player 1 correctly for player 2 view \ \ Matrix is in INWK+9 through +26 \ \ Stored as lo/hi \ \ sidev_x sidev_y sidev_z 21/22 23/24 25/26 \ roofv_x roofv_y roofv_z = 15/16 17/18 19/20 \ nosev_x nosev_y nosev_z 9/10 11/12 13/14 \ \ sidev_x sidev_y sidev_z sidev_x roofv_x nosev_x \ roofv_x roofv_y roofv_z -> sidev_y roofv_y nosev_y \ nosev_x nosev_y nosev_z sidev_z roofv_z nosev_z \ \ Swap nosev_x and sidev_z -> 9/10 and 25/26 \ Swap nosev_y and roofv_z -> 11/12 and 19/20 \ Swap roofv_x and sidev_y -> 15/16 and 23/24 \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .TransposeMatrix LDA INWK+9 \ nosev_x_lo LDX INWK+25 \ sidev_z_lo STA INWK+25 STX INWK+9 LDA INWK+10 \ nosev_x_hi LDX INWK+26 \ sidev_z_hi STA INWK+26 STX INWK+10 LDA INWK+11 \ nosev_y_lo LDX INWK+19 \ roofv_z_lo STA INWK+19 STX INWK+11 LDA INWK+12 \ nosev_y_hi LDX INWK+20 \ roofv_z_hi STA INWK+20 STX INWK+12 LDA INWK+15 \ roofv_x_lo LDX INWK+23 \ sidev_y_lo STA INWK+23 STX INWK+15 LDA INWK+16 \ roofv_x_hi LDX INWK+24 \ sidev_y_hi STA INWK+24 STX INWK+16 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: Multiply16x16 \ Type: Subroutine \ Category: Two-player Elite \ Summary: Multiply two 16-bit orientation vectors \ \ ------------------------------------------------------------------------------ \ \ Calculate: \ \ K(3 2 1 0) = orientation vector * orientation vector \ \ So if Y = 21 and X = 9, for example, then we set K(3 2 1 0) to: \ \ (sidev_x_hi sidev_x_lo) * (nosev_x_hi nosev_x_lo) \ \ We are multiplying two sign-magnitude numbers, so we multiply the absolute \ values of the operands, and only apply the sign at the end. \ \ Specifically, we do this calculation: \ \ |sidev_x_hi sidev_x_lo| * |nosev_x_hi nosev_x_lo| \ \ = |sidev_x_hi| * |nosev_x_hi nosev_x_lo| \ + |sidev_x_lo| * |nosev_x_hi nosev_x_lo| >> 8 \ \ and then we apply the sign bit of sidev_x_hi EOR nosev_x_hi to the result. \ \ We do each multiplication using MULT3, which calculates the following: \ \ K(3 2 1 0) = (A P+1 P) * Q \ \ where both (A P+1 P) and Q are sign-magnitude numbers. \ \ We therefore do the calculation in steps, like this: \ \ 1. XX15(3 2 1 0) = |nosev_x_hi nosev_x_lo| * |sidev_x_lo >> 1| << 1 \ \ 2. K(3 2 1 0) = |nosev_x_hi nosev_x_lo| * |sidev_x_hi| \ \ 3. Round up XX15+1 if bit 7 of XX15 is set \ \ 4. Calculate K(3 2 1 0) = K(3 2 1 0) + XX15(3 2 1) \ \ 5. If bit 0 of sidev_x_lo is set, add another |nosev_x_hi nosev_x_lo| \ \ 6. Apply the sign bit of sidev_x_hi EOR nosev_x_hi to the result \ \ Step 1 includes right and left shifts because MULT3 expects sign-magnitude \ arguments, but sidev_x_lo doesn't have a sign bit as it is the low byte of the \ 16-bit sign-magnitude number (sidev_x_hi sidev_x_lo). So we shift right to \ insert a sign bit of zero into bit 7 (thus keeping it positive), do the call \ to MULT3, and then shift the result back. If bit 0 of sidev_x_lo is set, then \ we avoid a loss of accuracy by adding one more |nosev_x_hi nosev_x_lo| to the \ result. \ \ So in essence, step 1 does this: \ \ XX15(3 2 1 0) = |nosev_x_hi nosev_x_lo| * |sidev_x_lo| \ \ which means step 4 does this: \ \ K(3 2 1 0) = K(3 2 1 0) + XX15(3 2 1) \ = |nosev_x_hi nosev_x_lo| * |sidev_x_hi| \ + |nosev_x_hi nosev_x_lo| * |sidev_x_lo| >> 8 \ \ which is what we want to calculate. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The orientation vector to multiply, from INWK: \ \ * If Y = 9, multiply nosev_x \ 11, multiply nosev_y \ 13, multiply nosev_z \ \ * If Y = 15, multiply roofv_x \ 17, multiply roofv_y \ 19, multiply roofv_z \ \ * If Y = 21, multiply sidev_x \ 23, multiply sidev_y \ 25, multiply sidev_z \ \ Y The orientation vector to multiply, from XX3: \ \ * If Y = 9, multiply nosev_x \ 11, multiply nosev_y \ 13, multiply nosev_z \ \ * If Y = 15, multiply roofv_x \ 17, multiply roofv_y \ 19, multiply roofv_z \ \ * If Y = 21, multiply sidev_x \ 23, multiply sidev_y \ 25, multiply sidev_z \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ K(3 2 1 0) The result \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Multiply16x16 \ The following commentary assumes we are calculating: \ \ (sidev_x_hi sidev_x_lo) * (nosev_x_hi nosev_x_lo) \ \ where sidev is in XX3 (as determined by Y) and nosev \ is in INWK (as determined by X) STX coordinateIndex \ Store the INWK vector index in coordinateIndex so we \ can retrieve it later LDA XX3,Y \ Q = |sidev_x_lo >> 1| LSR A \ STA Q \ We shift right to create a sign bit PHP \ Put the bit that we just shifted out of sidev_x_lo \ onto the stack, so we can incorporate it into the \ result below LDA INWK+0,X \ (A P+1 P) = |nosev_x_hi nosev_x_lo| STA P LDA INWK+1,X AND #%01111111 STA P+1 LDA #0 JSR MULT3 \ K(3 2 1 0) = (A P+1 P) * Q \ = |nosev_x_hi nosev_x_lo| \ * |sidev_x_lo >> 1| \ \ Y, P and P+1 are unchanged LDA K \ Set XX15(3 2 1 0) = K(3 2 1 0) << 1 ASL A \ STA XX15 \ So: LDA K+1 \ ROL A \ XX15(3 2 1 0) = |nosev_x_hi nosev_x_lo| * |sidev_x_lo| STA XX15+1 \ LDA K+2 \ We know the sign bit is positive so we can ignore it ROL A STA XX15+2 LDA K+3 ROL A STA XX15+3 BIT XX15 \ Round up the second-lowest byte in XX15+1 as we are BPL molt1 \ going to drop the bottom byte in XX15 INC XX15+1 BNE molt1 INC XX15+2 BNE molt1 INC XX15+3 .molt1 LDA XX3+1,Y \ Q = |sidev_x_hi| for ship #2 AND #%01111111 STA Q LDX coordinateIndex \ Retrieve the the INWK vector index LDA INWK+0,X \ (A P+1 P) = |nosev_x_hi nosev_x_lo| STA P LDA INWK+1,X AND #%01111111 STA P+1 LDA #0 JSR MULT3 \ K(3 2 1 0) = (A P+1 P) * Q \ = |nosev_x_hi nosev_x_lo| * |sidev_x_hi| CLC \ Calculate: LDA K \ ADC XX15+1 \ K(3 2 1 0) = K(3 2 1 0) + XX15(3 2 1) STA K LDA K+1 ADC XX15+2 STA K+1 LDA K+2 ADC XX15+3 STA K+2 LDA K+3 ADC #0 STA K+3 PLP \ Retrieve bit 0 of sidev_x_lo, which we pushed onto the \ stack above when shifting sidev_x_lo right to create a \ sign bit BCC molt2 \ If bit 0 is clear then the result is already accurate, \ so jump to molt2 to skip the following \ If we get here then bit 0 of sidev_x_lo is 1, so we \ need to add one more |nosev_x_hi nosev_x_lo| to the \ result to make it accurate LDX coordinateIndex \ Retrieve the the INWK vector index CLC \ Calculate: LDA INWK+0,X \ ADC K \ K(3 2 1 0) = K(3 2 1 0) + |nosev_x_hi nosev_x_lo| STA K LDA INWK+1,X AND #%01111111 ADC K+1 STA K+1 LDA K+2 ADC #0 STA K+2 .molt2 LDX coordinateIndex \ Retrieve the the INWK vector index LDA INWK+1,X \ Calculate the sign of the result: EOR XX3+1,Y \ AND #%10000000 \ nosev_x_hi EOR sidev_x_hi ORA K+3 \ Apply the sign to the result STA K+3 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: Multiply16x24 \ Type: Subroutine \ Category: Two-player Elite \ Summary: Multiply a 24-bit coordinate by a 16-bit orientation vector from \ ship #2 \ \ ------------------------------------------------------------------------------ \ \ Calculate: \ \ K(3 2 1 0) = orientation vector * coordinate \ \ So if Y = 21 and X = 0, for example, then we set K(3 2 1 0) to: \ \ (sidev_x_hi sidev_x_lo) * (x_sign x_hi x_lo) \ \ We are multiplying two sign-magnitude numbers, so we multiply the absolute \ values of the operands, and only apply the sign at the end. \ \ Specifically, we do this calculation: \ \ |sidev_x_hi sidev_x_lo| * |x_sign x_hi x_lo| \ \ = |sidev_x_hi| * |x_sign x_hi x_lo| \ + |sidev_x_lo| * |x_sign x_hi x_lo| >> 8 \ \ and then we apply the sign bit of x_sign EOR sidev_x_hi to the result. \ \ We do each multiplication using MULT3, which calculates the following: \ \ K(3 2 1 0) = (A P+1 P) * Q \ \ where both (A P+1 P) and Q are sign-magnitude numbers. \ \ We therefore do the calculation in steps, like this: \ \ 1. XX15(3 2 1 0) = |x_sign x_hi x_lo| * |sidev_x_lo >> 1| << 1 \ \ 2. K(3 2 1 0) = |x_sign x_hi x_lo| * |sidev_x_hi| \ \ 3. Round up XX15+1 if bit 7 of XX15 is set \ \ 4. Calculate K(3 2 1 0) = K(3 2 1 0) + XX15(3 2 1) \ \ 5. If bit 0 of sidev_x_lo is set, add another |x_sign x_hi x_lo| \ \ 6. Apply the sign bit of sidev_x_hi EOR x_sign to the result \ \ Step 1 includes right and left shifts because MULT3 expects sign-magnitude \ arguments, but sidev_x_lo doesn't have a sign bit as it is the low byte of the \ 16-bit sign-magnitude number (sidev_x_hi sidev_x_lo). So we shift right to \ insert a sign bit of zero into bit 7 (thus keeping it positive), do the call \ to MULT3, and then shift the result back. If bit 0 of sidev_x_lo is set, then \ we avoid a loss of accuracy by adding one more |x_sign x_hi x_lo| to the \ result. \ \ So in essence, step 1 does this: \ \ XX15(3 2 1 0) = |x_sign x_hi x_lo| * |sidev_x_lo| \ \ which means step 4 does this: \ \ K(3 2 1 0) = K(3 2 1 0) + XX15(3 2 1) \ = |x_sign x_hi x_lo| * |sidev_x_hi| \ + |x_sign x_hi x_lo| * |sidev_x_lo| >> 8 \ \ which is what we want to calculate. \ \ ------------------------------------------------------------------------------ \ \ Arguments: \ \ X The coordinate to multiply in INWK: \ \ * If X = 0, multiply coordinate (x_sign x_hi x_lo) \ \ * If X = 3, multiply coordinate (y_sign y_hi y_lo) \ \ * If X = 6, multiply coordinate (z_sign z_hi z_lo) \ \ Y The orientation vector to multiply, from ship #2: \ \ * If Y = 9, multiply nosev_x \ 11, multiply nosev_y \ 13, multiply nosev_z \ \ * If Y = 15, multiply roofv_x \ 17, multiply roofv_y \ 19, multiply roofv_z \ \ * If Y = 21, multiply sidev_x \ 23, multiply sidev_y \ 25, multiply sidev_z \ \ ------------------------------------------------------------------------------ \ \ Returns: \ \ K(3 2 1 0) The result \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Multiply16x24 \ The following commentary assumes we are calculating: \ \ (sidev_x_hi sidev_x_lo) * (x_sign x_hi x_lo) \ \ where sidev is the orientation vector from ship #2 \ (as determined by Y) and the coordinate is in INWK \ (as determined by X) STX coordinateIndex \ Store the coordinate index in coordinateIndex so we \ can retrieve it later LDA K%+NI%*2,Y \ Q = |sidev_x_lo >> 1| for ship #2 LSR A \ STA Q \ We shift right to create a sign bit PHP \ Put the bit that we just shifted out of sidev_x_lo \ onto the stack, so we can incorporate it into the \ result below LDA INWK+0,X \ (A P+1 P) = |x_sign x_hi x_lo| STA P LDA INWK+1,X STA P+1 LDA INWK+2,X AND #%01111111 JSR MULT3 \ K(3 2 1 0) = (A P+1 P) * Q \ = |x_sign x_hi x_lo| * |sidev_x_lo >> 1| \ \ Y, P and P+1 are unchanged LDA K \ Set XX15(3 2 1 0) = K(3 2 1 0) << 1 ASL A \ STA XX15 \ So XX15(3 2 1 0) = |x_sign x_hi x_lo| * |sidev_x_lo| LDA K+1 \ ROL A \ We know the sign bit is positive so we can ignore it STA XX15+1 LDA K+2 ROL A STA XX15+2 LDA K+3 ROL A STA XX15+3 BIT XX15 \ Round up the second-lowest byte in XX15+1 as we are BPL mult1 \ going to drop the bottom byte in XX15 INC XX15+1 BNE mult1 INC XX15+2 BNE mult1 INC XX15+3 .mult1 LDA K%+NI%*2+1,Y \ Q = |sidev_x_hi| for ship #2 AND #%01111111 STA Q LDX coordinateIndex \ Retrieve the coordinate index LDA INWK+0,X \ (A P+1 P) = |x_sign x_hi x_lo| STA P LDA INWK+1,X STA P+1 LDA INWK+2,X AND #%01111111 JSR MULT3 \ K(3 2 1 0) = (A P+1 P) * Q \ = |x_sign x_hi x_lo| * |sidev_x_hi| CLC \ Calculate: LDA K \ ADC XX15+1 \ K(3 2 1 0) = K(3 2 1 0) + XX15(3 2 1) STA K LDA K+1 ADC XX15+2 STA K+1 LDA K+2 ADC XX15+3 STA K+2 LDA K+3 ADC #0 STA K+3 PLP \ Retrieve bit 0 of sidev_x_lo, which we pushed onto the \ stack above when shifting sidev_x_lo right to create a \ sign bit BCC mult2 \ If bit 0 is clear then the result is already accurate, \ so jump to mult2 to skip the following \ If we get here then bit 0 of sidev_x_lo is 1, so we \ need to add one more |nosev_x_hi nosev_x_lo| to the \ result to make it accurate LDX coordinateIndex \ Retrieve the the INWK vector index CLC \ Calculate: LDA INWK+0,X \ ADC K \ K(3 2 1 0) = K(3 2 1 0) + |x_sign x_hi x_lo| STA K LDA INWK+1,X ADC K+1 STA K+1 LDA INWK+2,X AND #%01111111 ADC K+2 STA K+2 LDA K+3 ADC #0 STA K+3 .mult2 LDX coordinateIndex \ Retrieve the coordinate index LDA INWK+2,X \ Calculate the sign of the result: EOR K%+NI%*2+1,Y \ AND #%10000000 \ x_sign EOR sidev_x_hi ORA K+3 \ Apply the sign to the result STA K+3 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: Add24 \ Type: Subroutine \ Category: Two-player Elite \ Summary: Calculate P(2 1 0) = K(3 2 1) + XX15(3 2 1) \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .Add24 \ Calculate result: \ \ P(2 1 0) = K(3 2 1) + XX15(3 2 1) LDA XX15+3 \ If operands have different signs, jump to adds2 EOR K+3 BMI adds2 LDA XX15+3 \ Set S to the sign bit of XX15(3 2 1) AND #%10000000 STA S \ Set P(2 1 0) = K(3 2 1) + XX15(3 2 1) LDA XX15+1 \ Add low bytes CLC ADC K+1 STA P LDA XX15+2 \ Add high bytes ADC K+2 STA P+1 LDA XX15+3 \ Add sign bytes AND #%01111111 STA T LDA K+3 AND #%01111111 ADC T BCC adds1 \ If addition is OK, skip to adds1 BPL adds1 \ Addition has overflowed or affected the sign bit LDA #%11111111 \ Set P(1 0) to all set bits STA P STA P+1 LDA #%01111111 \ Set A to all set bits bar the sign bit, for P+2 .adds1 ORA S \ Apply correct sign to result STA P+2 \ Store sign byte of addition, so result is in P(2 1 0) RTS \ Return from the subroutine .adds2 \ Set P(2 1 0) = K(3 2 1) + XX15(3 2 1) \ \ which will be either: \ \ |K(3 2 1)| - |XX15(3 2 1)| \ \ or: \ \ |XX15(3 2 1)| - |K(3 2 1)| \ \ so work out the correct order for the subtraction LDA XX15+3 \ If |K+3| < |XX15+3|, jump to adds4 to calculate: AND #%01111111 \ STA T \ |XX15(3 2 1)| - |K(3 2 1)| LDA K+3 AND #%01111111 CMP T BCC adds4 BNE adds3 \ If |K+3| > |XX15+3|, jump to adds3 to calculate: \ \ |K(3 2 1)| - |XX15(3 2 1)| \ If we get here then the sign bytes match, so move on \ to the high bytes (which don't have signs) LDA K+2 \ If K+2 < XX15+2, jump to adds4 to calculate: CMP XX15+2 \ BCC adds4 \ |XX15(3 2 1)| - |K(3 2 1)| BNE adds3 \ If K+2 > XX15+2, jump to adds3 to calculate: \ \ |K(3 2 1)| - |XX15(3 2 1)| \ If we get here then the sign and high bytes match, so \ move on to the low bytes (which don't have signs) LDA K+1 \ If K+1 < XX15+1, jump to adds4 to calculate: CMP XX15+1 \ BCC adds4 \ |XX15(3 2 1)| - |K(3 2 1)| BNE adds3 \ If K+1 > XX15+1, jump to adds3 to calculate: \ \ |K(3 2 1)| - |XX15(3 2 1)| \ If we get here then the magnitudes are equal but the \ signs are different, so the result of the addition \ will be zero and we return that value without having \ to do the calculation STZ P \ Set P(2 1 0) = 0 STZ P+1 STZ P+2 RTS \ Return from the subroutine .adds3 \ If we get here then |K(3 2 1)| > |XX15(3 2 1)|, so we \ calculate: \ \ P(2 1 0) = |K(3 2 1)| - |XX15(3 2 1)| \ \ and apply the sign of K(3 2 1) to the result LDA K+3 \ Set S to the sign bit of K(3 2 1) AND #%10000000 STA S LDA K+1 \ Subtract low bytes SEC SBC XX15+1 STA P LDA K+2 \ Subtract high bytes SBC XX15+2 STA P+1 LDA XX15+3 \ Subtract sign bytes (magnitudes only) AND #%01111111 STA T LDA K+3 AND #%01111111 SBC T \ There's no need to check for an underflow as we know \ the subtraction will work ORA S \ Apply correct sign to result STA P+2 \ Store sign byte of addition, so result is in P(2 1 0) RTS \ Return from the subroutine .adds4 \ If we get here then |K(3 2 1)| < |XX15(3 2 1)|, so we \ calculate: \ \ P(2 1 0) = |XX15(3 2 1)| - |K(3 2 1)| \ \ and apply the sign of XX15(3 2 1) to the result LDA XX15+3 \ Set S to the sign bit of XX15(3 2 1) AND #%10000000 STA S LDA XX15+1 \ Subtract low bytes SEC SBC K+1 STA P LDA XX15+2 \ Subtract high bytes SBC K+2 STA P+1 LDA K+3 \ Subtract sign bytes (magnitudes only) AND #%01111111 STA T LDA XX15+3 AND #%01111111 SBC T \ There's no need to check for an underflow as we know \ the subtraction will work ORA S \ Apply correct sign to result STA P+2 \ Store sign byte of addition, so result is in P(2 1 0) RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: DivideBy96 \ Type: Subroutine \ Category: Two-player Elite \ Summary: Calculate K(3 2 1 0) = P(2 1 0) / 96 \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .DivideBy96 \ Calculate: \ \ K(3 2 1 0) = P(2 1 0) / 96 STZ S \ Set (S R Q) = 96 STZ R LDA #96 STA Q JMP DVID3B \ Set K(3 2 1 0) = P(2 1 0) / (S R Q) \ \ and return from the subroutine using a tail call \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: SaveShipData \ Type: Subroutine \ Category: Two-player Elite \ Summary: Stash the current ship's data in storeData \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .SaveShipData LDY #NI%-1 \ There are NI% bytes in each ship data block (and in \ the INWK workspace, so we set a counter in Y so we can \ loop through them .savs1 LDA INWK,Y \ Load the Y-th byte of INWK and store it in the Y-th STA storeData,Y \ byte of storeData DEY \ Decrement the loop counter BPL savs1 \ Loop back for the next byte until we have copied the \ last byte from INF to INWK LDA INF \ Store INF(1 0) STA storeINF LDA INF+1 STA storeINF+1 LDA XX0 \ Store XX0(1 0) STA storeXX0 LDA XX0+1 STA storeXX0+1 LDA TYPE \ Store TYPE STA storeTYPE RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: LoadShipData \ Type: Subroutine \ Category: Two-player Elite \ Summary: Retrieve the current ship's data from storeData \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .LoadShipData LDY #NI%-1 \ There are NI% bytes in each ship data block (and in \ the INWK workspace, so we set a counter in Y so we can \ loop through them .lods1 LDA storeData,Y \ Load the Y-th byte of storeData and store it in the STA INWK,Y \ Y-th byte of INWK DEY \ Decrement the loop counter BPL lods1 \ Loop back for the next byte until we have copied the \ last byte from INF to INWK LDA storeINF \ Restore INF(1 0) STA INF LDA storeINF+1 STA INF+1 LDA storeXX0 \ Restore XX0(1 0) STA XX0 LDA storeXX0+1 STA XX0+1 LDA storeTYPE \ Restore TYPE STA TYPE RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: GetShipDataToINWK \ Type: Subroutine \ Category: Two-player Elite \ Summary: Fetch ship data for ship X into INWK \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .GetShipDataToINWK JSR GINF \ Call GINF to fetch the address of the ship data block \ for slot X LDY #NI%-1 \ There are NI% bytes in each ship data block (and in \ the INWK workspace, so we set a counter in Y so we can \ loop through them .gets1 LDA (INF),Y \ Load the Y-th byte of INF and store it in the Y-th STA INWK,Y \ byte of INWK DEY \ Decrement the loop counter BPL gets1 \ Loop back for the next byte until we have copied the \ last byte from INF to INWK RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: SaveShipDataInSlot \ Type: Subroutine \ Category: Two-player Elite \ Summary: Save the ship data in INWK into slot #X \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .SaveShipDataInSlot JSR GINF \ Call GINF to fetch the address of the ship data block \ for slot X JMP STORE \ Copy the data to INF(1 0) and return from the \ subroutine using a tail call \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: SaveShipMovement \ Type: Subroutine \ Category: Two-player Elite \ Summary: Save current ship movement data \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .SaveShipMovement LDA ALPHA \ Save current ship movement data STA storeData LDA ALP1 STA storeData+1 LDA ALP2 STA storeData+2 LDA ALP2+1 STA storeData+3 LDA BETA STA storeData+4 LDA BET1 STA storeData+5 LDA BET2 STA storeData+6 LDA BET2+1 STA storeData+7 LDA DELTA STA storeData+8 LDA DELT4 STA storeData+9 LDA DELT4+1 STA storeData+10 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: LoadShipMovement \ Type: Subroutine \ Category: Two-player Elite \ Summary: Restore current ship movement data \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .LoadShipMovement LDA storeData \ Restore current ship movement data STA ALPHA LDA storeData+1 STA ALP1 LDA storeData+2 STA ALP2 LDA storeData+3 STA ALP2+1 LDA storeData+4 STA BETA LDA storeData+5 STA BET1 LDA storeData+6 STA BET2 LDA storeData+7 STA BET2+1 LDA storeData+8 STA DELTA LDA storeData+9 STA DELT4 LDA storeData+10 STA DELT4+1 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: GetPlayer2Movement \ Type: Subroutine \ Category: Two-player Elite \ Summary: Set the current ship to player 2's movement data \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .GetPlayer2Movement LDA player2ALPHA \ Restore current ship movement data STA ALPHA LDA player2ALP1 STA ALP1 LDA player2ALP2 STA ALP2 LDA player2ALP2+1 STA ALP2+1 LDA player2BETA STA BETA LDA player2BET1 STA BET1 LDA player2BET2 STA BET2 LDA player2BET2+1 STA BET2+1 LDA player2DELTA STA DELTA LDA player2DELT4 STA DELT4 LDA player2DELT4+1 STA DELT4+1 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: SavePlayer2Movement \ Type: Subroutine \ Category: Two-player Elite \ Summary: Store the current ship's movement data for player 2 \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .SavePlayer2Movement LDA ALPHA \ Save current ship movement data STA player2ALPHA LDA ALP1 STA player2ALP1 LDA ALP2 STA player2ALP2 LDA ALP2+1 STA player2ALP2+1 LDA BETA STA player2BETA LDA BET1 STA player2BET1 LDA BET2 STA player2BET2 LDA BET2+1 STA player2BET2+1 LDA DELTA STA player2DELTA LDA DELT4 STA player2DELT4 LDA DELT4+1 STA player2DELT4+1 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: SetPlayerBallLine \ Type: Subroutine \ Category: Drawing circles \ Summary: Set the pointers to use the correct view (player 1 or 2) \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .SetPlayerBallLine BIT drawPlayerView \ If we are drawing player 1's view, jump to ball1 to BPL ball1 \ set up the pointers for player 1's ball line heap \ Set the pointers for player 2's ball line heap LDA #LO(LSX2a) \ Set LSX2S to the address of LSX2a, so CIRCLE and BLINE STA LSX2S \ store the player 2 line y-coordinates in LSX2a LDA #HI(LSX2a) STA LSX2S+1 LDA #LO(LSY2a) \ Set LSY2S to the address of LSY2a, so CIRCLE and BLINE STA LSY2S \ store the player 2 line y-coordinates in LSY2a LDA #HI(LSY2a) STA LSY2S+1 LDA #LO(LSPa) \ Set LSPS to the address of LSPa, so CIRCLE and BLINE STA LSPS \ store the player 2 ball line heap pointer in LSPa LDA #HI(LSPa) STA LSPS+1 RTS \ Return from the subroutine .ball1 \ Set the pointers for player 1's ball line heap LDA #LO(LSX2) \ Set LSX2S to the address of LSX2, so CIRCLE and BLINE STA LSX2S \ store the player 1 line y-coordinates in LSX2 LDA #HI(LSX2) STA LSX2S+1 LDA #LO(LSY2) \ Set LSY2S to the address of LSY2, so CIRCLE and BLINE STA LSY2S \ store the player 1 line y-coordinates in LSY2 LDA #HI(LSY2) STA LSY2S+1 LDA #LO(LSP) \ Set LSPS to the address of LSP, so CIRCLE and BLINE STA LSPS \ store the player 1 ball line heap pointer in LSP LDA #HI(LSP) STA LSPS+1 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: ResetBallLine \ Type: Subroutine \ Category: Drawing circles \ Summary: Reset the pointers for the correct view (player 1 or 2) \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .ResetBallLine BIT drawPlayerView \ If we are drawing player 1's view, skip the following BPL rest1 STZ LSPa \ Reset the ball line heap by setting the ball line heap \ pointer to 0 for player 2's view RTS \ Return from the subroutine .rest1 STZ LSP \ Reset the ball line heap by setting the ball line heap \ pointer to 0 for player 1's view RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Name: SetPlayerBallLine \ Type: Subroutine \ Category: Drawing circles \ Summary: Set the pointers to use the correct view (player 1 or 2) \ \ ****************************************************************************** \ --- Mod: Code added for two-player Elite: -----------> .SetPlayerSunHeap BIT drawPlayerView \ If we are drawing player 1's view, jump to sunh1 to BPL sunh1 \ set up the pointers for player 1's sun line heap \ Set the pointers for player 2's sun line heap LDA #LO(LSOa) \ Set LSOS to the address of LSOa, so SUN stores the STA LSOS \ sun line heap in LSOa LDA #HI(LSOa) STA LSOS+1 LDA #LO(SUNXa) \ Set SUNXS to the address of SUNXa, so SUN stores the STA SUNXS \ sun centre x-coordinate in SUNXa LDA #HI(SUNXa) STA SUNXS+1 RTS \ Return from the subroutine .sunh1 \ Set the pointers for player 1's sun line heap LDA #LO(LSO) \ Set LSOS to the address of LSO, so SUN stores the STA LSOS \ sun line heap in LSO LDA #HI(LSO) STA LSOS+1 LDA #LO(SUNX) \ Set SUNXS to the address of SUNX, so SUN stores the STA SUNXS \ sun centre x-coordinate in SUNX LDA #HI(SUNX) STA SUNXS+1 RTS \ Return from the subroutine \ --- End of added code -------------------------------> \ ****************************************************************************** \ \ Save SHIPS.bin \ \ ****************************************************************************** PRINT "SHIPS" PRINT "Assembled at ", ~CODE_SHIPS% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_SHIPS%) PRINT "Execute at ", ~LOAD_SHIPS% PRINT "Reload at ", ~LOAD_SHIPS% PRINT "S.SHIPS ", ~CODE_SHIPS%, " ", ~P%, " ", ~LOAD_SHIPS%, " ", ~LOAD_SHIPS% SAVE "3-assembled-output/SHIPS.bin", CODE_SHIPS%, P%, LOAD_SHIPS% \ ****************************************************************************** \ \ Show free space \ \ ****************************************************************************** PRINT "ELITE game code ", ~(K%-F%), " bytes free" PRINT "F% = ", ~F% PRINT "Ends at ", ~P% PRINT "Addresses for the scramble routines in elite-checksum.py" PRINT "S% = ", ~S% PRINT "G% = ", ~G% PRINT "F% = ", ~F%