Skip to navigation

Elite on the BBC Micro and NES

Elite B source

[6502 Second Processor version]

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 [Show more] Type: Variable Category: Universe Summary: Table of pointers to the local universe's ship data blocks Deep dive: The local bubble of universe
Context: See this variable on its own page References: This variable is used as follows: * GINF uses UNIV * KILLSHP uses UNIV * KS2 uses UNIV * TACTICS (Part 1 of 7) uses UNIV

See the deep dive on "Ship data blocks" for details on ship data blocks, and the deep dive on "The local bubble of universe" for details of how Elite stores the local universe in K%, FRIN and UNIV.
.UNIV FOR I%, 0, NOSH EQUW K% + I% * NI% \ Address of block no. I%, of size NI%, in workspace K% NEXT
Name: TWOS [Show more] Type: Variable Category: Drawing pixels Summary: Ready-made single-pixel character row bytes for mode 4 Deep dive: Drawing monochrome pixels in mode 4
Context: See this variable on its own page Variations: See code variations for this variable in the different versions References: No direct references to this variable in this source file

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 [Show more] Type: Variable Category: Drawing pixels Summary: Ready-made double-pixel character row bytes for mode 4 Deep dive: Drawing monochrome pixels in mode 4
Context: See this variable on its own page Variations: See code variations for this variable in the different versions References: No direct references to this variable in this source file

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 [Show more] Type: Variable Category: Drawing pixels Summary: Ready-made single-pixel character row bytes for mode 5 Deep dive: Drawing colour pixels in mode 5
Context: See this variable on its own page Variations: See code variations for this variable in the different versions References: No direct references to this variable in this source file

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 [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a one-segment line by sending an OSWRCH 129 command to the I/O processor
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * LASLI calls LL30 * NLIN2 calls LL30 * TT15 calls LL30 * TTX66 calls LL30 * ZZAAP calls LL30

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 [Show more] Type: Subroutine Category: Drawing lines Summary: Add a line segment to the multi-segment line buffer
Context: See this subroutine on its own page References: This subroutine is called as follows: * SLIDE calls LOIN

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 [Show more] 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
Context: See this subroutine on its own page References: This subroutine is called as follows: * LOIN calls LBFL * SLIDE calls LBFL
.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 [Show more] Type: Variable Category: Drawing lines Summary: The multi-segment line buffer used by LOIN
Context: See this variable on its own page References: This variable is used as follows: * LBFL uses LBUF * LOIN uses LBUF

This buffer contains a hidden message containing the authors' names, which is overwritten when the buffer is used.
.LBUF SKIP 256 \ The line buffer to send with this command
Name: FLKB [Show more] Type: Subroutine Category: Keyboard Summary: Flush the keyboard buffer
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * MT26 calls FLKB * TRADEMODE calls FLKB
.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 [Show more] Type: Subroutine Category: Drawing lines Summary: Print a title and draw a horizontal line at row 19 to box it in
Context: See this subroutine on its own page References: This subroutine is called as follows: * EQSHP calls NLIN3 * STATUS calls NLIN3 * TT167 calls NLIN3 * TT208 calls NLIN3 * TT23 calls NLIN3 * TT25 calls NLIN3

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 [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a horizontal line at pixel row 19 to box in a title
Context: See this subroutine on its own page References: This subroutine is called as follows: * JMTB calls NLIN4 * TT213 calls NLIN4

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 [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a horizontal line at pixel row 23 to box in a title
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * TT22 calls NLIN

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 [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a screen-wide horizontal line at the pixel row in A
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * NLIN4 calls NLIN2 * TT22 calls NLIN2

This draws a line from (2, A) to (254, A), which is almost screen-wide and fits in nicely between the white borders 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 [Show more] Type: Subroutine Category: Drawing lines Summary: Remove a line from the sun line heap and draw it on-screen
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * SUN (Part 2 of 4) calls HLOIN2 * SUN (Part 4 of 4) calls HLOIN2 * WPLS calls HLOIN2

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 LDA #0 \ Set the Y-th byte of the LSO block to 0 STA LSO,Y \ Fall through into HLOIN to draw a horizontal line from \ (X1, Y) to (X2, Y)
Name: HLOIN [Show more] Type: Subroutine Category: Drawing lines Summary: Add a sun line to the horizontal line buffer
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * SUN (Part 3 of 4) calls HLOIN

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 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 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 [Show more] 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
Context: See this subroutine on its own page References: This subroutine is called as follows: * HLOIN calls HBFL * SUN (Part 4 of 4) calls HBFL * TT23 calls HBFL * WPLS calls HBFL
.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 [Show more] Type: Subroutine Category: Drawing lines Summary: Reset the horizontal line buffer
Context: See this subroutine on its own page References: This subroutine is called as follows: * TTX66 calls HBZE
.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 [Show more] Type: Variable Category: Drawing lines Summary: The horizontal line buffer to send with the OSWORD 247 command
Context: See this variable on its own page References: This variable is used as follows: * HBFL uses HBUF * HLOIN uses HBUF
.HBUF EQUB 0 \ The number of bytes to transmit with this command EQUB 0 \ The number of bytes to receive with this command SKIP 256 \ The horizontal line buffer to send with this command
Name: TWFL [Show more] Type: Variable Category: Drawing lines Summary: Ready-made character rows for the left end of a horizontal line in mode 4
Context: See this variable on its own page Variations: See code variations for this variable in the different versions References: No direct references to this variable in this source file

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 [Show more] Type: Variable Category: Drawing lines Summary: Ready-made character rows for the right end of a horizontal line in mode 4
Context: See this variable on its own page Variations: See code variations for this variable in the different versions References: No direct references to this variable in this source file

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 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate (YY+1 SYL+Y) = (A P) + (S R) and draw stardust particle
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * STARS1 calls PIX1 * STARS2 calls PIX1 * STARS6 calls PIX1

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 (further away = smaller point)
.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 PIX1 to draw the stardust particle \ at (X1,Y1)
Name: PIXEL2 [Show more] Type: Subroutine Category: Drawing pixels Summary: Draw a stardust particle relative to the screen centre
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * FLIP calls PIXEL2 * nWq calls PIXEL2 * STARS1 calls PIXEL2 * STARS2 calls PIXEL2 * STARS6 calls PIXEL2

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 (further away = smaller point)
.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| 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) 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 negate \ it to a positive number, i.e. A is now |Y1| .PX2 STA T \ Set A = 97 - Y1 LDA #97 \ SBC T \ So if Y is positive we display the point up from the \ centre at y-coordinate 97, while a negative Y means \ down from the centre \ Fall through into PIXEL to draw the stardust at the \ screen coordinates in (X, A)
Name: PIXEL [Show more] Type: Subroutine Category: Drawing pixels Summary: Add a white dot at a specific distance to the pixel buffer (2-pixel dash or 4-pixel square)
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * TT22 calls PIXEL * PIXEL2 calls via PX4

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 (further away = smaller point)
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 2-pixel dash or 4-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 [Show more] Type: Subroutine Category: Drawing pixels Summary: Draw the pixel in the pixel buffer by sending an OSWORD 241 command to the I/O processor
Context: See this subroutine on its own page References: This subroutine is called as follows: * DEMON calls PBFL * Main flight loop (Part 16 of 16) calls PBFL * nWq calls PBFL * PIXEL calls PBFL * PIXEL3 calls PBFL * TT22 calls PBFL
.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 [Show more] Type: Subroutine Category: Drawing pixels Summary: Reset the pixel buffer
Context: See this subroutine on its own page References: This subroutine is called as follows: * TTX66 calls PBZE
.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 [Show more] Type: Subroutine Category: Drawing pixels Summary: Add a coloured dot at a specific distance to the pixel buffer (1-pixel dot, 2-pixel dash or 4-pixel square)
Context: See this subroutine on its own page References: This subroutine is called as follows: * DOEXP calls PIXEL3

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 (further away = smaller point)
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 [Show more] Type: Variable Category: Drawing pixels Summary: The pixel buffer to send with the OSWORD 241 command
Context: See this variable on its own page References: This variable is used as follows: * PIXEL uses PBUF * PIXEL3 uses PBUF * PBFL calls via pixbl

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 SKIP 256 \ The pixel buffer to send with this command
Name: BLINE [Show more] 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
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * CIRCLE2 calls BLINE * PLS22 calls BLINE

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) 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 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) SWAP If non-zero, we swap (X1, Y1) and (X2, Y2)
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 y-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 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) .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 LDY LSP \ Set Y = LSP 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 INY \ Increment Y to point to the next byte in LSX2/LSY2 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 INY \ Increment Y to point to the next byte in LSX2/LSY2 STY LSP \ Update LSP to point to the same as Y .BL5 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 [Show more] Type: Subroutine Category: Stardust Summary: Reflect the stardust particles in the screen diagonal and redraw the stardust field
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * LOOK1 calls FLIP

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 .FLL1 LDX SY,Y \ Copy the Y-th particle's y-coordinate from SY+Y into X 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 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 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 RTS \ Return from the subroutine
Name: STARS [Show more] Type: Subroutine Category: Stardust Summary: The main routine for processing the stardust
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * Main flight loop (Part 16 of 16) calls STARS

Called at the very end of the main flight loop.
.STARS LDX VIEW \ Load the current view into X: \ \ 0 = front \ 1 = rear \ 2 = left \ 3 = right BEQ STARS1 \ If this 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 [Show more] Type: Subroutine Category: Stardust Summary: Process the stardust for the front view Deep dive: Stardust in the front view
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * DEMON calls STARS1 * STARS calls STARS1

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 deep dive on "Stardust in the front view".
.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 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 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 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 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 [Show more] Type: Subroutine Category: Stardust Summary: Process the stardust for the rear view
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * STARS calls STARS6

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 deep dive on "Stardust in the front view".
.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 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 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 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 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Add an orientation vector coordinate to an INWK coordinate Deep dive: The space station safe zone
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 14 of 16) calls MAS1

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 [Show more] Type: Subroutine Category: Maths (Geometry) Summary: Calculate a cap on the maximum distance to the planet or sun
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * Main flight loop (Part 14 of 16) calls MAS2 * Main flight loop (Part 15 of 16) calls MAS2 * WARP calls MAS2 * Main flight loop (Part 15 of 16) calls via m * WARP calls via m

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 [Show more] Type: Subroutine Category: Maths (Arithmetic) Summary: Calculate A = x_hi^2 + y_hi^2 + z_hi^2 in the K% block
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 15 of 16) calls MAS3

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 A = x_hi^2 + y_hi^2 + z_hi^2 A = &FF if the calculation overflows a one-byte result
.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 sum 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 [Show more] Type: Subroutine Category: Status Summary: Show the Status Mode screen (red key f8) Deep dive: Combat rank
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * TT102 calls STATUS
.wearedocked \ We call this from STATUS below if we are docked LDA #205 \ Print extended token 205 ("DOCKED") and return from JSR DETOK \ 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 to 2 \ Dangerous = 2 to 9 \ Deadly = 10 to 24 \ Elite = 25 and up LDX #9 \ Set X to 9 for an Elite rating CMP #25 \ If A >= 25, jump to st3 to print out our rating, as we BCS st3 \ are Elite DEX \ Decrement X to 8 for a Deadly rating CMP #10 \ If A >= 10, jump to st3 to print out our rating, as we BCS st3 \ are Deadly DEX \ Decrement X to 7 for a Dangerous rating CMP #2 \ If A >= 2, jump to st3 to print out our rating, as we BCS st3 \ are Dangerous DEX \ Decrement X to 6 for a Competent rating BNE st3 \ Jump to st3 to print out our rating, as we are \ Competent (this BNE is effectively a JMP as A will \ never be zero) .STATUS LDA #8 \ Clear the top part of the screen, draw a white border, 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 \ Set A to token 129 ("{sentence case}DOCKED") 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 kills, so jump \ to st4 to work out whether we are Competent, \ Dangerous, Deadly or Elite \ Otherwise we have fewer than 256 kills, so we are one \ of Harmless, Mostly Harmless, Poor, Average or Above \ Average TAX \ Set X to 0 (as A is 0) LDA TALLY \ Set A = lower byte of tally / 4 LSR A LSR A \ 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 for 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 %00000011 \ Mostly Harmless = %00000100 to %00000111 \ Poor = %00001000 to %00001111 \ Average = %00010000 to %00011111 \ Above Average = %00100000 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 INX \ Increment X for each shift 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 [Show more] Type: Subroutine Category: Text Summary: Print text followed by a newline and indent of 6 characters
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * STATUS calls plf2

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 [Show more] Type: Subroutine Category: Moving Summary: Calculate K(3 2 1) = (x_sign x_hi x_lo) + K(3 2 1)
Context: See this subroutine on its own page References: This subroutine is called as follows: * MAS1 calls MVT3 * MV40 calls MVT3 * TAS1 calls MVT3

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

Pitch or roll a ship by a small, fixed amount (1/16 radians, or 3.6 degrees), in a specified direction, by rotating the orientation vectors. The vectors to rotate are given in X and Y, and the direction of the rotation is given in RAT2. The calculation is as follows: * If the direction is positive: X = X * (1 - 1/512) + Y / 16 Y = Y * (1 - 1/512) - X / 16 * If the direction is negative: X = X * (1 - 1/512) - Y / 16 Y = Y * (1 - 1/512) + X / 16 So if X = 15 (roofv_x), Y = 21 (sidev_x) and RAT2 is positive, it does this: roofv_x = roofv_x * (1 - 1/512) + sidev_x / 16 sidev_x = sidev_x * (1 - 1/512) - roofv_x / 16
Arguments: X The first vector to rotate: * If X = 15, rotate roofv_x * If X = 17, rotate roofv_y * If X = 19, rotate roofv_z * If X = 21, rotate sidev_x * If X = 23, rotate sidev_y * If X = 25, rotate sidev_z Y The second vector to rotate: * If Y = 9, rotate nosev_x * If Y = 11, rotate nosev_y * If Y = 13, rotate nosev_z * If Y = 21, rotate sidev_x * If Y = 23, rotate sidev_y * If Y = 25, rotate sidev_z RAT2 The direction of the pitch or roll to perform, positive or negative (i.e. the sign of the roll or pitch counter in bit 7)
.MVS5 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 [Show more] Type: Variable Category: Text Summary: A constant used when printing large numbers in BPRNT Deep dive: Printing decimal numbers
Context: See this variable on its own page References: This variable is used as follows: * BPRNT uses TENS

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

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

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

Print the 32-bit number stored in K(0 1 2 3) to a specific number of digits, left-padding with spaces for numbers with fewer digits (so lower numbers are right-aligned). Optionally include a decimal point. See the deep dive on "Printing decimal numbers" for details of the algorithm used in this routine.
Arguments: K(0 1 2 3) The number to print, stored with the most significant byte in K and the least significant in K+3 (i.e. as a big-endian number, which is the opposite way to how the 6502 assembler stores addresses, for example) U The maximum number of digits to print, including the decimal point (spaces will be used on the left to pad out the result to this width, so the number is right- aligned to this width). U must be 11 or less C flag If set, include a decimal point followed by one fractional digit (i.e. show the number to 1 decimal place). In this case, the number in K(0 1 2 3) contains 10 * the number we end up printing, so to print 123.4, we would pass 1234 in K(0 1 2 3) and would set the C flag to include the decimal point
.BPRNT LDX #11 \ Set T to the maximum number of digits allowed (11 STX T \ characters, which is the number of digits in 10 \ billion). We will use this as a flag when printing \ characters in TT37 below PHP \ Make a copy of the status register (in particular \ the C flag) so we can retrieve it later BCC TT30 \ If the C flag is clear, we do not want to print a \ decimal point, so skip the next two instructions DEC T \ As we are going to show a decimal point, decrement DEC U \ both the number of characters and the number of \ digits (as one of them is now a decimal point) .TT30 LDA #11 \ Set A to 11, the maximum number of digits allowed SEC \ Set the C flag so we can do subtraction without the \ C flag affecting the result STA XX17 \ Store the maximum number of digits allowed (11) in \ XX17 SBC U \ Set U = 11 - U + 1, so U now contains the maximum STA U \ number of digits minus the number of digits we want INC U \ to display, plus 1 (so this is the number of digits \ we should skip before starting to print the number \ itself, and the plus 1 is there to ensure we print at \ least one digit) LDY #0 \ In the main loop below, we use Y to count the number \ of times we subtract 10 billion to get the leftmost \ digit, so set this to zero STY S \ In the main loop below, we use location S as an \ 8-bit overflow for the 32-bit calculations, so \ we need to set this to 0 before joining the loop JMP TT36 \ Jump to TT36 to start the process of printing this \ number's digits .TT35 \ This subroutine multiplies K(S 0 1 2 3) by 10 and \ stores the result back in K(S 0 1 2 3), using the fact \ that K * 10 = (K * 2) + (K * 2 * 2 * 2) ASL K+3 \ Set K(S 0 1 2 3) = K(S 0 1 2 3) * 2 by rotating left ROL K+2 ROL K+1 ROL K ROL S LDX #3 \ Now we want to make a copy of the newly doubled K in \ XX15, so we can use it for the first (K * 2) in the \ equation above, so set up a counter in X for copying \ four bytes, starting with the last byte in memory \ (i.e. the least significant) .tt35 LDA K,X \ Copy the X-th byte of K(0 1 2 3) to the X-th byte of STA XX15,X \ XX15(0 1 2 3), so that XX15 will contain a copy of \ K(0 1 2 3) once we've copied all four bytes DEX \ Decrement the loop counter BPL tt35 \ Loop back to copy the next byte until we have copied \ all four LDA S \ Store the value of location S, our overflow byte, in STA XX15+4 \ XX15+4, so now XX15(4 0 1 2 3) contains a copy of \ K(S 0 1 2 3), which is the value of (K * 2) that we \ want to use in our calculation ASL K+3 \ Now to calculate the (K * 2 * 2 * 2) part. We still ROL K+2 \ have (K * 2) in K(S 0 1 2 3), so we just need to shift ROL K+1 \ it twice. This is the first one, so we do this: ROL K \ ROL S \ K(S 0 1 2 3) = K(S 0 1 2 3) * 2 = K * 4 ASL K+3 \ And then we do it again, so that means: ROL K+2 \ ROL K+1 \ K(S 0 1 2 3) = K(S 0 1 2 3) * 2 = K * 8 ROL K ROL S CLC \ Clear the C flag so we can do addition without the \ C flag affecting the result LDX #3 \ By now we've got (K * 2) in XX15(4 0 1 2 3) and \ (K * 8) in K(S 0 1 2 3), so the final step is to add \ these two 32-bit numbers together to get K * 10. \ So we set a counter in X for four bytes, starting \ with the last byte in memory (i.e. the least \ significant) .tt36 LDA K,X \ Fetch the X-th byte of K into A ADC XX15,X \ Add the X-th byte of XX15 to A, with carry STA K,X \ Store the result in the X-th byte of K DEX \ Decrement the loop counter BPL tt36 \ Loop back to add the next byte, moving from the least \ significant byte to the most significant, until we \ have added all four LDA XX15+4 \ Finally, fetch the overflow byte from XX15(4 0 1 2 3) ADC S \ And add it to the overflow byte from K(S 0 1 2 3), \ with carry STA S \ And store the result in the overflow byte from \ K(S 0 1 2 3), so now we have our desired result, i.e. \ \ K(S 0 1 2 3) = K(S 0 1 2 3) * 10 LDY #0 \ In the main loop below, we use Y to count the number \ of times we subtract 10 billion to get the leftmost \ digit, so set this to zero so we can rejoin the main \ loop for another subtraction process .TT36 \ This is the main loop of our digit-printing routine. \ In the following loop, we are going to count the \ number of times that we can subtract 10 million and \ store that count in Y, which we have already set to 0 LDX #3 \ Our first calculation concerns 32-bit numbers, so \ set up a counter for a four-byte loop SEC \ Set the C flag so we can do subtraction without the \ C flag affecting the result .tt37 \ 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 [Show more] 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
Context: See this variable on its own page References: This variable is used as follows: * DETOK2 uses DTW1 * MT13 uses DTW1 * MT2 uses DTW1

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 [Show more] Type: Variable Category: Text Summary: A flag that indicates whether we are currently printing a word Deep dive: Extended text tokens
Context: See this variable on its own page References: This variable is used as follows: * CLYNS uses DTW2 * DETOK2 uses DTW2 * MT8 uses DTW2 * TT26 uses DTW2 * TTX66 uses DTW2

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 [Show more] Type: Variable Category: Text Summary: A flag for switching between standard and extended text tokens Deep dive: Extended text tokens
Context: See this variable on its own page References: This variable is used as follows: * DETOK2 uses DTW3 * MT5 uses DTW3

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 [Show more] Type: Variable Category: Text Summary: Flags that govern how justified extended text tokens are printed Deep dive: Extended text tokens
Context: See this variable on its own page References: This variable is used as follows: * MESS uses DTW4 * MT15 uses DTW4 * TT26 uses DTW4

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 [Show more] Type: Variable Category: Text Summary: The size of the justified text buffer at BUF Deep dive: Extended text tokens
Context: See this variable on its own page References: This variable is used as follows: * HME2 uses DTW5 * MESS uses DTW5 * MT15 uses DTW5 * MT17 uses DTW5 * TT26 uses DTW5

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 [Show more] 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
Context: See this variable on its own page References: This variable is used as follows: * DETOK2 uses DTW6 * MT13 uses DTW6 * MT2 uses DTW6

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 [Show more] Type: Variable Category: Text Summary: A mask for capitalising the next letter in an extended text token Deep dive: Extended text tokens
Context: See this variable on its own page References: This variable is used as follows: * DETOK2 uses DTW8 * MT19 uses DTW8 * TT26 uses DTW8

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 [Show more] Type: Subroutine Category: Text Summary: Print a newline
Context: See this subroutine on its own page References: This subroutine is called as follows: * GTDRV calls FEED * MT26 calls FEED
.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 [Show more] Type: Subroutine Category: Text Summary: Print the character in variable DTW7 Deep dive: Extended text tokens
Context: See this subroutine on its own page References: This subroutine is called as follows: * JMTB calls MT16
.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 [Show more] 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
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * BPRNT calls TT26 * cmn calls TT26 * DOXC calls TT26 * gnum calls TT26 * TITLE calls TT26 * TT102 calls TT26 * TT160 calls TT26 * TT161 calls TT26 * TT16a calls TT26 * TT214 calls TT26 * TT25 calls TT26 * TT42 calls TT26 * TT74 calls TT26 * DETOK2 calls via DASC * JMTB calls via DASC * CHPR calls via rT9

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 [Show more] Type: Subroutine Category: Sound Summary: Make a standard system beep
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * DKS3 calls BELL

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 [Show more] Type: Subroutine Category: Text Summary: Send a character to the I/O processor for printing or processing
Context: See this subroutine on its own page References: This subroutine is called as follows: * GTDRV calls CHPR * TT26 calls CHPR * BELL calls via CHPRD

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 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 [Show more] Type: Variable Category: Text Summary: A flag that determines whether to send text output to the printer as well as the screen
Context: See this variable on its own page References: This variable is used as follows: * CHPR uses printflag * DOXC uses printflag * Main game loop (Part 5 of 6) uses printflag * TRADEMODE uses printflag

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 [Show more] Type: Subroutine Category: Dashboard Summary: Update the dashboard indicators and flight variables by sending a #RDPARAMS command to the I/O processor
Context: See this subroutine on its own page References: This subroutine is called as follows: * Main game loop (Part 5 of 6) calls DIALS * RES2 calls DIALS

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 LDA ALTIT \ Send the current altitude to the I/O processor JSR OSWRCH 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 LDA QQ14 \ Send the current fuel level to the I/O processor JSR OSWRCH LDA GNTMP \ Send the laser temperature to the I/O processor JSR OSWRCH LDA CABTMP \ Send the cabin temperature to the I/O processor JSR OSWRCH LDA FLH \ Send the flashing console bars configuration setting JSR OSWRCH \ to the I/O processor LDA ESCP \ Send the escape pod status to the I/O processor JSR OSWRCH 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 [Show more] Type: Subroutine Category: Flight Summary: Launch our escape pod
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * Main flight loop (Part 3 of 16) calls ESCAPE

This routine displays our doomed Cobra Mk III disappearing off into the ether before arranging our replacement ship. Called when we press ESCAPE during flight and have an escape pod fitted.
.ESCAPE JSR RES2 \ Reset a number of flight variables and workspaces 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
Name: HME2 [Show more] Type: Subroutine Category: Charts Summary: Search the galaxy for a system
Context: See this subroutine on its own page Variations: See code variations for this subroutine in the different versions References: This subroutine is called as follows: * TT102 calls HME2
.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
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%