CODE_K% = P% LOAD_K% = LOAD% + P% - CODE%ELITE K FILE Produces the binary file ELTK.bin that gets loaded by elite-bcfs.asm..TWOS EQUB %00000001 ; x000000 EQUB %00000010 ; 0x00000 EQUB %00000100 ; 00x0000 EQUB %00001000 ; 000x000 EQUB %00010000 ; 0000x00 EQUB %00100000 ; 00000x0 EQUB %01000000 ; 000000xName: TWOS [Show more] Type: Variable Category: Drawing pixels Summary: Ready-made bytes for drawing one-pixel dots in the space viewContext: See this variable on its own page References: This variable is used as follows: * LOIN (Part 2 of 7) uses TWOS * LOIN (Part 5 of 7) uses TWOS
This table contains ready-made pixel bytes for drawing a one-pixel dot in the high-resolution screen mode on the Apple II. The pixels in bits 0 to 6 appear in that order on-screen (so bit 0 is on the left). The comments below show how the bits map into the screen, with seven pixels per byte. See the LOIN routine for details..TWOS2 EQUB %10000011 ; xx00000 EQUB %10000110 ; 0xx0000 EQUB %10001100 ; 00xx000 EQUB %10011000 ; 000xx00 EQUB %10110000 ; 0000xx0 EQUB %11100000 ; 00000xx EQUB %11000000 ; 000000xName: TWOS2 [Show more] Type: Variable Category: Drawing pixels Summary: Ready-made two-bit pixel bytes for plotting colour pixelsContext: See this variable on its own page References: This variable is used as follows: * CPIX uses TWOS2
This table contains ready-made pixel bytes for drawing a one-pixel colour or two-pixel white dot in the high-resolution screen mode on the Apple II. Bit 7 in each byte is set, so when this is used as a mask byte via AND, it retains bit 7 (which sets the colour palette). The pixels in bits 0 to 6 appear in that order on-screen, so bit 0 is on the left. The comments below show how the bits map into the screen, with seven pixels per byte. See the CPIX routine for details..TWFL EQUB %10000011 ; xx00000 EQUB %10000111 ; xxx0000 EQUB %10001111 ; xxxx000 EQUB %10011111 ; xxxxx00 EQUB %10111111 ; xxxxxx0 EQUB %11111111 ; xxxxxxx EQUB %11111111 ; xxxxxxxName: TWFL [Show more] Type: Variable Category: Drawing lines Summary: Ready-made pixel bytes for the left end of a horizontal lineContext: See this variable on its own page References: This variable is used as follows: * HLOIN uses TWFL
Ready-made bytes for plotting horizontal line end caps. This table provides a byte with pixels at the left end, which is used for the right end of the line. The minimum cap size is two pixels, so the first entry in the table contains a two-pixel pattern. Bit 7 in each byte is used to define the colour palette in that byte, so the pixels themselves are defined in bits 0 to 6. The pixels in bits 0 to 6 appear in that order on-screen, so bit 0 is on the left. The comments below show how the two bytes map into the screen, with seven pixels per byte. See the HLOIN routine for details..TWFR EQUB %11111111 ; xxxxxxx EQUB %11111110 ; 0xxxxxx EQUB %11111100 ; 00xxxxx EQUB %11111000 ; 000xxxx EQUB %11110000 ; 0000xxx EQUB %11100000 ; 00000xx EQUB %11000000 ; 000000xName: TWFR [Show more] Type: Variable Category: Drawing lines Summary: Ready-made character rows for the right end of a horizontal line in the space viewContext: See this variable on its own page References: This variable is used as follows: * HLOIN uses TWFR
Ready-made bytes for plotting horizontal line end caps. This table provides a byte with pixels at the right end, which is used for the left end of the line. Bit 7 in each byte is used to define the colour palette in that byte, so the pixels themselves are defined in bits 0 to 6. The pixels in bits 0 to 6 appear in that order on-screen, so bit 0 is on the left. The comments below show how the two bytes map into the screen, with seven pixels per byte. See the HLOIN routine for details..cellocl EQUB LO($0400 + 2) EQUB LO($0480 + 2) EQUB LO($0500 + 2) EQUB LO($0580 + 2) EQUB LO($0600 + 2) EQUB LO($0680 + 2) EQUB LO($0700 + 2) EQUB LO($0780 + 2) EQUB LO($0428 + 2) EQUB LO($04A8 + 2) EQUB LO($0528 + 2) EQUB LO($05A8 + 2) EQUB LO($0628 + 2) EQUB LO($06A8 + 2) EQUB LO($0728 + 2) EQUB LO($07A8 + 2) EQUB LO($0450 + 2) EQUB LO($04D0 + 2) EQUB LO($0550 + 2) EQUB LO($05D0 + 2) EQUB LO($0650 + 2) EQUB LO($06D0 + 2) EQUB LO($0750 + 2) EQUB LO($07D0 + 2)Name: cellocl [Show more] Type: Variable Category: Drawing the screen Summary: Lookup table for converting a text row number to the address of that row in text screen memory (low byte)Context: See this variable on its own page References: This variable is used as follows: * RR5 uses cellocl
The text screen has the same kind of interleaved row layout in memory as the Apple II high-res screen, except screen memory is at $400 rather than $2000. We add 2 to indent the text by two characters..SCTBL EQUB LO($2000) EQUB LO($2080) EQUB LO($2100) EQUB LO($2180) EQUB LO($2200) EQUB LO($2280) EQUB LO($2300) EQUB LO($2380) EQUB LO($2028) EQUB LO($20A8) EQUB LO($2128) EQUB LO($21A8) EQUB LO($2228) EQUB LO($22A8) EQUB LO($2328) EQUB LO($23A8) EQUB LO($2050) EQUB LO($20D0) EQUB LO($2150) EQUB LO($21D0) EQUB LO($2250) EQUB LO($22D0) EQUB LO($2350) EQUB LO($23D0)Name: SCTBL [Show more] Type: Variable Category: Drawing the screen Summary: Lookup table for converting a character row number to the address of the top or bottom pixel line in that character row (low byte)Context: See this variable on its own page References: This variable is used as follows: * clearrow uses SCTBL * CPIX uses SCTBL * HLOIN uses SCTBL * letter2 uses SCTBL * LOIN (Part 2 of 7) uses SCTBL * LOIN (Part 3 of 7) uses SCTBL * LOIN (Part 4 of 7) uses SCTBL * LOIN (Part 5 of 7) uses SCTBL * LOIN (Part 6 of 7) uses SCTBL * LOIN (Part 7 of 7) uses SCTBL * PIXEL uses SCTBL * VLOIN uses SCTBL
The character rows in screen memory for the Apple II high-res screen are not stored in the order in which they appear. The SCTBL, SCTBH and SCTBH2 tables provide a lookup for the address of the start of each character row. Also, the pixel rows within each character row are interleaved, so each pixel row appears $400 bytes after the previous pixel row. The address of pixel row n within character row Y is stored at the address given in the Y-th entry of (SCTBH SCTBL), plus n * $400, so the addresses are as follows: * Pixel row 0 is at the Y-th entry from (SCTBH SCTBL) * Pixel row 1 is at the Y-th entry from (SCTBH SCTBL) + $400 * Pixel row 2 is at the Y-th entry from (SCTBH SCTBL) + $800 * Pixel row 3 is at the Y-th entry from (SCTBH SCTBL) + $C00 * Pixel row 4 is at the Y-th entry from (SCTBH SCTBL) + $1000 * Pixel row 5 is at the Y-th entry from (SCTBH SCTBL) + $1400 * Pixel row 6 is at the Y-th entry from (SCTBH SCTBL) + $1800 * Pixel row 7 is at the Y-th entry from (SCTBH SCTBL) + $1C00 To make life easier, the table at SCTBH2 contains the high byte for the final row, where the high byte has $1C00 added to the address..SCTBH EQUB HI($2000) EQUB HI($2080) EQUB HI($2100) EQUB HI($2180) EQUB HI($2200) EQUB HI($2280) EQUB HI($2300) EQUB HI($2380) EQUB HI($2028) EQUB HI($20A8) EQUB HI($2128) EQUB HI($21A8) EQUB HI($2228) EQUB HI($22A8) EQUB HI($2328) EQUB HI($23A8) EQUB HI($2050) EQUB HI($20D0) EQUB HI($2150) EQUB HI($21D0) EQUB HI($2250) EQUB HI($22D0) EQUB HI($2350) EQUB HI($23D0) EQUD $20202020 ; These bytes appear to be unused EQUD $20202020Name: SCTBH [Show more] Type: Variable Category: Drawing the screen Summary: Lookup table for converting a character row number to the address of the top pixel line in that character row (high byte)Context: See this variable on its own page References: This variable is used as follows: * clearrow uses SCTBH * CPIX uses SCTBH * HLOIN uses SCTBH * letter2 uses SCTBH * LOIN (Part 2 of 7) uses SCTBH * LOIN (Part 4 of 7) uses SCTBH * LOIN (Part 5 of 7) uses SCTBH * PIXEL uses SCTBH
The character rows in screen memory for the Apple II high-res screen are not stored in the order in which they appear. The SCTBL, SCTBH and SCTBH2 tables provide a lookup for the address of the start of each character row. Also, the pixel rows within each character row are interleaved, so each pixel row appears $400 bytes after the previous pixel row. The address of pixel row n within character row Y is stored at the address given in the Y-th entry of (SCTBH SCTBL), plus n * $400, so the addresses are as follows: * Pixel row 0 is at the Y-th entry from (SCTBH SCTBL) * Pixel row 1 is at the Y-th entry from (SCTBH SCTBL) + $400 * Pixel row 2 is at the Y-th entry from (SCTBH SCTBL) + $800 * Pixel row 3 is at the Y-th entry from (SCTBH SCTBL) + $C00 * Pixel row 4 is at the Y-th entry from (SCTBH SCTBL) + $1000 * Pixel row 5 is at the Y-th entry from (SCTBH SCTBL) + $1400 * Pixel row 6 is at the Y-th entry from (SCTBH SCTBL) + $1800 * Pixel row 7 is at the Y-th entry from (SCTBH SCTBL) + $1C00 To make life easier, the table at SCTBH2 contains the high byte for the final row, where the high byte has $1C00 added to the address..SCTBH2 EQUB HI($2000 + $1C00) EQUB HI($2080 + $1C00) EQUB HI($2100 + $1C00) EQUB HI($2180 + $1C00) EQUB HI($2200 + $1C00) EQUB HI($2280 + $1C00) EQUB HI($2300 + $1C00) EQUB HI($2380 + $1C00) EQUB HI($2028 + $1C00) EQUB HI($20A8 + $1C00) EQUB HI($2128 + $1C00) EQUB HI($21A8 + $1C00) EQUB HI($2228 + $1C00) EQUB HI($22A8 + $1C00) EQUB HI($2328 + $1C00) EQUB HI($23A8 + $1C00) EQUB HI($2050 + $1C00) EQUB HI($20D0 + $1C00) EQUB HI($2150 + $1C00) EQUB HI($21D0 + $1C00) EQUB HI($2250 + $1C00) EQUB HI($22D0 + $1C00) EQUB HI($2350 + $1C00) EQUB HI($23D0 + $1C00)Name: SCTBH2 [Show more] Type: Variable Category: Drawing the screen Summary: Lookup table for converting a character row number to the address of the bottom pixel line in that character row (high byte)Context: See this variable on its own page References: This variable is used as follows: * LOIN (Part 3 of 7) uses SCTBH2 * LOIN (Part 6 of 7) uses SCTBH2 * LOIN (Part 7 of 7) uses SCTBH2 * PIXEL uses SCTBH2 * VLOIN uses SCTBH2
The character rows in screen memory for the Apple II high-res screen are not stored in the order in which they appear. The SCTBL, SCTBH and SCTBH2 tables provide a lookup for the address of the start of each character row. Also, the pixel rows within each character row are interleaved, so each pixel row appears $400 bytes after the previous pixel row. The address of pixel row n within character row Y is stored at the address given in the Y-th entry of (SCTBH SCTBL), plus n * $400, so the addresses are as follows: * Pixel row 0 is at the Y-th entry from (SCTBH SCTBL) * Pixel row 1 is at the Y-th entry from (SCTBH SCTBL) + $400 * Pixel row 2 is at the Y-th entry from (SCTBH SCTBL) + $800 * Pixel row 3 is at the Y-th entry from (SCTBH SCTBL) + $C00 * Pixel row 4 is at the Y-th entry from (SCTBH SCTBL) + $1000 * Pixel row 5 is at the Y-th entry from (SCTBH SCTBL) + $1400 * Pixel row 6 is at the Y-th entry from (SCTBH SCTBL) + $1800 * Pixel row 7 is at the Y-th entry from (SCTBH SCTBL) + $1C00 To make life easier, the table at SCTBH2 contains the high byte for the final row, where the high byte has $1C00 added to the address.;.grubbyline ; These instructions are commented out in the original ; ; source ;RTS .LL30 SKIP 0 ; LL30 is a synonym for LOIN ; ; In the BBC Micro cassette and disc versions of Elite, ; LL30 and LOIN are synonyms for the same routine, ; presumably because the two developers each had their ; own line routines to start with, and then chose one of ; them for the final game .LOIN STY YSAV ; Store Y into YSAV, so we can preserve it across the ; call to this subroutine ;LDA Y1 ; These instructions are commented out in the original ;CMP #Y*2 ; source ;BCS grubbyline ; ;LDA Y2 ;CMP #Y*2 ;BCS grubbyline LDA #128 ; Set S = 128, which is the starting point for the STA S ; slope error (representing half a pixel) ASL A ; Set SWAP = 0, as %10000000 << 1 = 0 STA SWAP LDA X2 ; Set A = X2 - X1 SBC X1 ; = delta_x ; ; This subtraction works as the ASL A above sets the C ; flag BCS LI1 ; If X2 > X1 then A is already positive and we can skip ; the next three instructions EOR #%11111111 ; Negate the result in A by flipping all the bits and ADC #1 ; adding 1, i.e. using two's complement to make it ; positive SEC ; Set the C flag, ready for the subtraction below .LI1 STA P ; Store A in P, so P = |X2 - X1|, or |delta_x| LDA Y2 ; Set A = Y2 - Y1 SBC Y1 ; = delta_y ; ; This subtraction works as we either set the C flag ; above, or we skipped that SEC instruction with a BCS BCS LI2 ; If Y2 > Y1 then A is already positive and we can skip ; the next two instructions EOR #%11111111 ; Negate the result in A by flipping all the bits and ADC #1 ; adding 1, i.e. using two's complement to make it ; positive .LI2 STA Q ; Store A in Q, so Q = |Y2 - Y1|, or |delta_y| CMP P ; If Q < P, jump to STPX to step along the x-axis, as BCC STPX ; the line is closer to being horizontal than vertical JMP STPY ; Otherwise Q >= P so jump to STPY to step along the ; y-axis, as the line is closer to being vertical than ; horizontalName: LOIN (Part 1 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a line: Calculate the line gradient in the form of deltas Deep dive: Bresenham's line algorithmContext: See this subroutine on its own page References: This subroutine is called as follows: * BLINE calls LOIN * BOMBOFF calls LOIN * LL9 (Part 12 of 12) calls LOIN * LSPUT calls LOIN * WPLS2 calls LOIN * LASLI calls via LL30
This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. This stage calculates the line deltas.
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
Returns: Y Y is preserved
Other entry points: LL30 LL30 is a synonym for LOIN and draws a line from (X1, Y1) to (X2, Y2).STPX LDX X1 ; Set X = X1 CPX X2 ; If X1 < X2, jump down to LI3, as the coordinates are BCC LI3 ; already in the order that we want DEC SWAP ; Otherwise decrement SWAP from 0 to $FF, to denote that ; we are swapping the coordinates around LDA X2 ; Swap the values of X1 and X2 STA X1 STX X2 TAX ; Set X = X1 LDA Y2 ; Swap the values of Y1 and Y2 LDY Y1 STA Y1 STY Y2 .LI3 ; By this point we know the line is horizontal-ish and ; X1 < X2, so we're going from left to right as we go ; from X1 to X2 LDA Y1 ; Set A to the y-coordinate in Y1 LSR A ; Set T1 = A >> 3 LSR A ; = y div 8 LSR A ; STA T1 ; So T1 now contains the number of the character row ; that will contain the pixel we want to draw ; ; We will refer to T1 throughout the rest of the routine TAY ; Set the low byte of SC(1 0) to the Y-th entry from LDA SCTBL,Y ; SCTBL, which contains the low byte of the address of STA SC ; the start of character row Y in screen memory LDA Y1 ; Set T2 = Y1 mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw our pixel (as STA T2 ; each character block has 8 rows) ; ; We will refer to T2 throughout the rest of the routine ASL A ; Set the high byte of SC(1 0) as follows: ASL A ; ADC SCTBH,Y ; SC+1 = SCBTH for row Y + pixel row * 4 STA SC+1 ; ; Because this is the high byte, and because we already ; set the low byte in SC to the Y-th entry from SCTBL, ; this is the same as the following: ; ; SC(1 0) = (SCBTH SCTBL) for row Y + pixel row * $400 ; ; So SC(1 0) contains the address in screen memory of ; the pixel row containing the pixel we want to draw, as ; (SCBTH SCTBL) gives us the address of the start of the ; character row, and each pixel row within the character ; row is offset by $400 bytes LDY SCTBX1,X ; Using the lookup table at SCTBX1, set Y to the bit ; number within the pixel byte that corresponds to the ; pixel at the x-coordinate in X, i.e. the start of the ; line (so Y is in the range 0 to 6, as bit 7 in the ; pixel byte is used to set the pixel byte's colour ; palette) LDA TWOS,Y ; Fetch a one-pixel byte from TWOS where pixel Y is set, STA R ; and store it in R LDY SCTBX2,X ; Using the lookup table at SCTBX2, set Y to the byte ; number within the pixel row that contains the start of ; the line LDX Q ; Set X = |delta_y| BNE LIlog7 ; If |delta_y| is non-zero, jump to LIlog7 to skip the ; following TXA ; If we get here then |delta_y| = 0, so set A = 0 and BEQ LIlog6 ; jump to LIlog6 to return 0 as the result of the ; division .LIlog7 LDA logL,X ; Set A = log(Q) - log(P) LDX P ; = log(|delta_y|) - log(|delta_x|) SEC ; SBC logL,X ; by first subtracting the low bytes of log(Q) - log(P) LDX Q ; And then subtracting the high bytes of log(Q) - log(P) LDA log,X ; so now A contains the high byte of log(Q) - log(P) LDX P SBC log,X BCC P%+6 ; If the subtraction underflowed then skip the next two ; instructions as log(P) - log(Q) >= 256 ; Otherwise the subtraction fitted into one byte and ; didn't underflow, so log(P) - log(Q) < 256, and we ; now return a result of 255 LDA #255 ; The division is very close to 1, so set A to the BNE LIlog6 ; closest possible answer to 256, i.e. 255, and jump to ; LIlog6 to return the result (this BNE is effectively a ; JMP as A is never zero) TAX ; Otherwise we set A to the A-th entry from the antilog LDA alogh,X ; table so the result of the division is now in A .LIlog6 STA Q ; Store the result of the division in Q, so we have: ; ; Q = |delta_y| / |delta_x| SEC ; Set the C flag for the subtraction below LDX P ; Set X = P + 1 INX ; = |delta_x| + 1 ; ; We will use P as the x-axis counter, and we add 1 to ; ensure we include the pixel at each end LDA Y2 ; If Y1 <= Y2, jump to DOWN, as we need to draw the line SBC Y1 ; to the right and down BCS DOWNName: LOIN (Part 2 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a line: Line has a shallow gradient, step right along x-axis Deep dive: Bresenham's line algorithmContext: See this subroutine on its own page References: No direct references to this subroutine in this source file
This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * |delta_y| < |delta_x| * The line is closer to being horizontal than vertical * We are going to step right along the x-axis * We potentially swap coordinates to make sure X1 < X2LDA SWAP ; If SWAP > 0 then we swapped the coordinates above, so BNE LI6 ; jump down to LI6 to skip plotting the first pixel ; ; This appears to be a bug that omits the last pixel ; of this type of shallow line, rather than the first ; pixel, which makes the treatment of this kind of line ; different to the other kinds of slope (they all have a ; BEQ instruction at this point, rather than a BNE) ; ; The result is a rather messy line join when a shallow ; line that goes right and up or left and down joins a ; line with any of the other three types of slope ; ; This bug was fixed in the advanced versions of Elite, ; where the BNE is replaced by a BEQ to bring it in line ; with the other three slopes DEX ; Decrement the counter in X because we're about to plot ; the first pixel .LIL2 ; We now loop along the line from left to right, using X ; as a decreasing counter, and at each count we plot a ; single pixel using the pixel mask in R LDA R ; Fetch the pixel byte from R EOR (SC),Y ; Store R into screen memory at SC(1 0), using EOR STA (SC),Y ; logic so it merges with whatever is already on-screen .LI6 ASL R ; Shift the single pixel in R to the left to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along (we shift left because the ; pixels in the high-resolution screen are the opposite ; way around than the bits in the pixel byte) BPL LI7 ; If the pixel didn't fall out of the left end of the ; pixel bits in R into the palette bit in bit 7, then ; jump to LI7 LDA #%00000001 ; Otherwise we need to move over to the next character STA R ; block, so set R = %00000001 to move the pixel to the ; left end of the next pixel byte INY ; And increment Y to move on to the next character block ; along to the right .LI7 LDA S ; Set S = S + Q to update the slope error ADC Q STA S BCC LIC2 ; If the addition didn't overflow, jump to LIC2 DEC T2 ; Otherwise we just overflowed, so decrement the pixel ; row counter within the character block, which is in ; T2, as we are moving to a new pixel line BMI LI20 ; If T2 is negative then the counter just ran down and ; we are no longer within the same character block, so ; jump to LI20 to move to the bottom pixel row in the ; character row above ; We now need to move up into the pixel row above LDA SC+1 ; Subtract 4 from the high byte of SC(1 0), so this does SBC #4 ; the following: STA SC+1 ; ; SC(1 0) = SC(1 0) - $400 ; ; The SBC works because the C flag is set, as we passed ; through the BCC above ; ; So this sets SC(1 0) to the address of the pixel row ; above the one we just drew in, as each pixel row ; within the character row is spaced out by $400 bytes ; in screen memory .LIC2 DEX ; Decrement the counter in X BNE LIL2 ; If we haven't yet reached the right end of the line, ; loop back to LIL2 to plot the next pixel along LDY YSAV ; Restore Y from YSAV, so that it's preserved RTS ; Return from the subroutine .LI20 ; If we get here then we need to move up into the bottom ; pixel row in the character block above LDA #7 ; Set the pixel line number within the character row STA T2 ; (which we store in T2) to 7, which is the bottom pixel ; row of the character block above STX T ; Store the current character row number in T, so we can ; restore it below LDX T1 ; Decrement the number of the character row in T1, as we DEX ; are moving up a row STX T1 LDA SCTBL,X ; Set SC(1 0) to the X-th entry from (SCTBH2 SCTBL), so STA SC ; it contains the address of the start of the bottom LDA SCTBH2,X ; pixel row in character row X in screen memory (so ; that's the bottom pixel row in the character row we ; just moved up into) ; ; We set the high byte below (though there's no reason ; why it isn't done here) LDX T ; Restore the value of X that we stored, so X contains ; the previous character row number, from before we ; moved up a row (we need to do this as the following ; jump returns us to a point where the previous row ; number is still in X) STA SC+1 ; Set the high byte of SC(1 0) as above JMP LIC2 ; Jump back to keep drawing the lineName: LOIN (Part 3 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a shallow line going right and up or left and down Deep dive: Bresenham's line algorithmContext: See this subroutine on its own page References: No direct references to this subroutine in this source file
This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * The line is going right and up (no swap) or left and down (swap) * X1 < X2 and Y1 > Y2 * Draw from (X1, Y1) at bottom left to (X2, Y2) at top right, omitting the first pixel.DOWN LDA T2 ; Set T2 = 7 - T2 EOR #7 ; STA T2 ; T2 contains the number of the pixel row within the ; character block, from 0 at the top to 7 at the bottom ; ; We're going to be drawing a line that goes downwards, ; so this calculation enables us to use T2 as a pixel ; row counter, stepping down one pixel line at a time LDA SWAP ; If SWAP = 0 then we didn't swap the coordinates above, BEQ LI9 ; so jump down to LI9 to skip plotting the first pixel DEX ; Decrement the counter in X because we're about to plot ; the first pixel .LIL3 ; We now loop along the line from left to right, using X ; as a decreasing counter, and at each count we plot a ; single pixel using the pixel mask in R LDA R ; Fetch the pixel byte from R EOR (SC),Y ; Store R into screen memory at SC(1 0), using EOR STA (SC),Y ; logic so it merges with whatever is already on-screen .LI9 ASL R ; Shift the single pixel in R to the left to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along (we shift left because the ; pixels in the high-resolution screen are the opposite ; way around than the bits in the pixel byte) BPL LI10 ; If the pixel didn't fall out of the left end of the ; pixel bits in R into the palette bit in bit 7, then ; jump to LI10 LDA #%00000001 ; Otherwise we need to move over to the next character STA R ; block, so set R = %00000001 to move the pixel to the ; left end of the next pixel byte INY ; And increment Y to move on to the next character block ; along to the right .LI10 LDA S ; Set S = S + Q to update the slope error ADC Q STA S BCC LIC3 ; If the addition didn't overflow, jump to LIC3 DEC T2 ; Otherwise we just overflowed, so decrement the pixel ; row counter within the character block, which is in ; T2, as we are moving to a new pixel line BMI LI21 ; If T2 is negative then the counter just ran down and ; we are no longer within the same character block, so ; jump to LI21 to move to the top pixel row in the ; character row below ; We now need to move down into the pixel row below LDA SC+1 ; Add 4 to the high byte of SC(1 0), so this does the ADC #3 ; following: STA SC+1 ; ; SC(1 0) = SC(1 0) + $400 ; ; The ADC adds 4 rather than 3 because the C flag is ; set, as we passed through the BCC above ; ; So this sets SC(1 0) to the address of the pixel row ; below the one we just drew in, as each pixel row ; within the character row is spaced out by $400 bytes ; in screen memory .LIC3 DEX ; Decrement the counter in X BNE LIL3 ; If we haven't yet reached the right end of the line, ; loop back to LIL3 to plot the next pixel along LDY YSAV ; Restore Y from YSAV, so that it's preserved RTS ; Return from the subroutine .LI21 ; If we get here then we need to move down into the top ; pixel row in the character block below LDA #7 ; Set the pixel line number within the character row STA T2 ; (which we store in T2) to 7, which is the bottom pixel ; row of the character block above STX T ; Store the current character row number in T, so we can ; restore it below LDX T1 ; Increment the number of the character row in T1, as we INX ; are moving down a row STX T1 LDA SCTBL,X ; Set SC(1 0) to the X-th entry from (SCTBH SCTBL), so STA SC ; it contains the address of the start of the top pixel LDA SCTBH,X ; row in character row X in screen memory (so that's the STA SC+1 ; top pixel row in the character row we just moved down ; into) LDX T ; Restore the value of X that we stored, so X contains ; the previous character row number, from before we ; moved down a row (we need to do this as the following ; jump returns us to a point where the previous row ; number is still in X) JMP LIC3 ; Jump back to keep drawing the lineName: LOIN (Part 4 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a shallow line going right and down or left and up Deep dive: Bresenham's line algorithmContext: See this subroutine on its own page References: No direct references to this subroutine in this source file
This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * The line is going right and down (no swap) or left and up (swap) * X1 < X2 and Y1 <= Y2 * Draw from (X1, Y1) at top left to (X2, Y2) at bottom right, omitting the first pixel.STPY LDY Y1 ; Set A = Y = Y1 TYA LDX X1 ; Set X = X1 CPY Y2 ; If Y1 >= Y2, jump down to LI15, as the coordinates are BCS LI15 ; already in the order that we want DEC SWAP ; Otherwise decrement SWAP from 0 to $FF, to denote that ; we are swapping the coordinates around LDA X2 ; Swap the values of X1 and X2 STA X1 STX X2 TAX ; Set X = X1 LDA Y2 ; Swap the values of Y1 and Y2 STA Y1 STY Y2 TAY ; Set Y = A = Y1 .LI15 ; By this point we know the line is vertical-ish and ; Y1 >= Y2, so we're going from top to bottom as we go ; from Y1 to Y2 LSR A ; Set T1 = A >> 3 LSR A ; = y div 8 LSR A ; STA T1 ; So T1 now contains the number of the character row ; that will contain the pixel we want to draw TAY ; Set the low byte of SC(1 0) to the Y-th entry from LDA SCTBL,Y ; SCTBL, which contains the low byte of the address of STA SC ; the start of character row Y in screen memory LDA Y1 ; Set T2 = Y1 mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw our pixel (as STA T2 ; each character block has 8 rows) ASL A ; Set the high byte of SC(1 0) as follows: ASL A ; ADC SCTBH,Y ; SC+1 = SCBTH for row Y + pixel row * 4 STA SC+1 ; ; Because this is the high byte, and because we already ; set the low byte in SC to the Y-th entry from SCTBL, ; this is the same as the following: ; ; SC(1 0) = (SCBTH SCTBL) for row Y + pixel row * $400 ; ; So SC(1 0) contains the address in screen memory of ; the pixel row containing the pixel we want to draw, as ; (SCBTH SCTBL) gives us the address of the start of the ; character row, and each pixel row within the character ; row is offset by $400 bytes LDY SCTBX1,X ; Using the lookup table at SCTBX1, set Y to the bit ; number within the pixel byte that corresponds to the ; pixel at the x-coordinate in X, i.e. the start of the ; line (so Y is in the range 0 to 6, as bit 7 in the ; pixel byte is used to set the pixel byte's colour ; palette) LDA TWOS,Y ; Fetch a one-pixel byte from TWOS where pixel Y is set, STA R ; and store it in R LDY SCTBX2,X ; Using the lookup table at SCTBX2, set Y to the byte ; number within the pixel row that contains the start of ; the line ; The following section calculates: ; ; P = P / Q ; = |delta_x| / |delta_y| ; ; using the log tables at logL and log to calculate: ; ; A = log(P) - log(Q) ; = log(|delta_x|) - log(|delta_y|) ; ; by first subtracting the low bytes of the logarithms ; from the table at LogL, and then subtracting the high ; bytes from the table at log, before applying the ; antilog to get the result of the division and putting ; it in P LDX P ; Set X = |delta_x| BEQ LIfudge ; If |delta_x| = 0, jump to LIfudge to return 0 as the ; result of the division LDA logL,X ; Set A = log(P) - log(Q) LDX Q ; = log(|delta_x|) - log(|delta_y|) SEC ; SBC logL,X ; by first subtracting the low bytes of log(P) - log(Q) LDX P ; And then subtracting the high bytes of log(P) - log(Q) LDA log,X ; so now A contains the high byte of log(P) - log(Q) LDX Q SBC log,X BCC P%+6 ; If the subtraction underflowed then skip the next two ; instructions as log(P) - log(Q) >= 256 ; Otherwise the subtraction fitted into one byte and ; didn't underflow, so log(P) - log(Q) < 256, and we ; now return a result of 255 LDA #255 ; The division is very close to 1, so set A to the BNE LIlog2 ; closest possible answer to 256, i.e. 255, and jump to ; LIlog2 to return the result (this BNE is effectively a ; JMP as A is never zero) TAX ; Otherwise we set A to the A-th entry from the LDA alogh,X ; alogh so the result of the division is now in A .LIlog2 STA P ; Store the result of the division in P, so we have: ; ; P = |delta_x| / |delta_y| .LIfudge SEC ; Set the C flag for the subtraction below LDX Q ; Set X = Q + 1 INX ; = |delta_y| + 1 ; ; We add 1 so we can skip the first pixel plot if the ; line is being drawn with swapped coordinates LDA X2 ; Set A = X2 - X1 SBC X1 BCC LFT ; If X2 < X1 then jump to LFT, as we need to draw the ; line to the left and downName: LOIN (Part 5 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a line: Line has a steep gradient, step up along y-axis Deep dive: Bresenham's line algorithmContext: See this subroutine on its own page References: No direct references to this subroutine in this source file
This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * |delta_y| >= |delta_x| * The line is closer to being vertical than horizontal * We are going to step up along the y-axis * We potentially swap coordinates to make sure Y1 >= Y2CLC ; Clear the C flag LDA SWAP ; If SWAP = 0 then we didn't swap the coordinates above, BEQ LI17 ; so jump down to LI17 to skip plotting the first pixel DEX ; Decrement the counter in X because we're about to plot ; the first pixel .LIL5 ; We now loop along the line from left to right, using X ; as a decreasing counter, and at each count we plot a ; single pixel using the pixel mask in R LDA R ; Fetch the pixel byte from R EOR (SC),Y ; Store R into screen memory at SC(1 0), using EOR STA (SC),Y ; logic so it merges with whatever is already on-screen .LI17 DEC T2 ; Decrement the pixel row counter within the character ; block, which is in T2 BMI LI22 ; If T2 is negative then the counter just ran down and ; we are no longer within the same character block, so ; jump to LI22 to move to the bottom pixel row in the ; character row above ; We now need to move up into the pixel row above LDA SC+1 ; Subtract 4 from the high byte of SC(1 0), so this does SBC #3 ; the following: STA SC+1 ; ; SC(1 0) = SC(1 0) - $400 ; ; The SBC works because we cleared the C flag above ; ; So this sets SC(1 0) to the address of the pixel row ; above the one we just drew in, as each pixel row ; within the character row is spaced out by $400 bytes ; in screen memory CLC ; Clear the C flag again, as it will have been set by ; the subtraction .LI16 LDA S ; Set S = S + P to update the slope error ADC P STA S BCC LIC5 ; If the addition didn't overflow, jump to LIC5 ASL R ; Shift the single pixel in R to the left to step along ; the x-axis, so the next pixel we plot will be at the ; next x-coordinate along (we shift left because the ; pixels in the high-resolution screen are the opposite ; way around than the bits in the pixel byte) BPL LIC5 ; If the pixel didn't fall out of the left end of the ; pixel bits in R into the palette bit in bit 7, then ; jump to LIC5 LDA #%00000001 ; Otherwise we need to move over to the next character STA R ; block, so set R = %00000001 to move the pixel to the ; left end of the next pixel byte INY ; And increment Y to move on to the next character block ; along to the right .LIC5 DEX ; Decrement the counter in X BNE LIL5 ; If we haven't yet reached the right end of the line, ; loop back to LIL5 to plot the next pixel along LDY YSAV ; Restore Y from YSAV, so that it's preserved RTS ; Return from the subroutine .LI22 ; If we get here then we need to move up into the bottom ; pixel row in the character block above LDA #7 ; Set the pixel line number within the character row STA T2 ; (which we store in T2) to 7, which is the bottom pixel ; row of the character block above STX T ; Store the current character row number in T, so we can ; restore it below LDX T1 ; Decrement the number of the character row in T1, as we DEX ; are moving up a row STX T1 LDA SCTBL,X ; Set SC(1 0) to the X-th entry from (SCTBH2 SCTBL), so STA SC ; it contains the address of the start of the bottom LDA SCTBH2,X ; pixel row in character row X in screen memory (so ; that's the bottom pixel row in the character row we ; just moved up into) ; ; We set the high byte below (though there's no reason ; why it isn't done here) LDX T ; Restore the value of X that we stored, so X contains ; the previous character row number, from before we ; moved up a row (we need to do this as the following ; jump returns us to a point where the previous row ; number is still in X) STA SC+1 ; Set the high byte of SC(1 0) as above JMP LI16 ; Jump back to keep drawing the lineName: LOIN (Part 6 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a steep line going up and left or down and right Deep dive: Bresenham's line algorithmContext: See this subroutine on its own page References: No direct references to this subroutine in this source file
This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * The line is going up and left (no swap) or down and right (swap) * X1 < X2 and Y1 >= Y2 * Draw from (X1, Y1) at top left to (X2, Y2) at bottom right, omitting the first pixel.LFT LDA SWAP ; If SWAP = 0 then we didn't swap the coordinates above, BEQ LI18 ; so jump down to LI18 to skip plotting the first pixel DEX ; Decrement the counter in X because we're about to plot ; the first pixel .LIL6 LDA R ; Fetch the pixel byte from R EOR (SC),Y ; Store R into screen memory at SC(1 0), using EOR STA (SC),Y ; logic so it merges with whatever is already on-screen .LI18 DEC T2 ; Decrement the pixel row counter within the character ; block, which is in T2 BMI LI23 ; If T2 is negative then the counter just ran down and ; we are no longer within the same character block, so ; jump to LI23 to move to the bottom pixel row in the ; character row above ; We now need to move up into the pixel row above LDA SC+1 ; Subtract 4 from the high byte of SC(1 0), so this does SBC #3 ; the following: STA SC+1 ; ; SC(1 0) = SC(1 0) - $400 ; ; The SBC works because we cleared the C flag above ; ; So this sets SC(1 0) to the address of the pixel row ; above the one we just drew in, as each pixel row ; within the character row is spaced out by $400 bytes ; in screen memory CLC ; Clear the C flag again, as it will have been set by ; the subtraction .LI19 LDA S ; Set S = S + P to update the slope error ADC P STA S BCC LIC6 ; If the addition didn't overflow, jump to LIC6 LSR R ; Shift the single pixel in R to the right to step along ; the x-axis, so the next pixel we plot will be at the ; previous x-coordinate to the left (we shift right ; because the pixels in the high-resolution screen are ; the opposite way around than the bits in the pixel ; byte) BCC LIC6 ; If the pixel didn't fall out of the right end of the ; pixel bits in R into the C flag, then jump to LIC6 LDA #%01000000 ; Otherwise we need to move left to the next character STA R ; block, so set R = %01000000 to move the pixel to the ; right end of the next pixel byte, skipping bit 7 as ; that's reserved for the colour palette bit DEY ; And decrement Y to move on to the next character block ; along to the left CLC ; Clear the C flag so it doesn't affect the additions ; if we loop back .LIC6 DEX ; Decrement the counter in X BNE LIL6 ; If we haven't yet reached the left end of the line, ; loop back to LIL6 to plot the next pixel along LDY YSAV ; Restore Y from YSAV, so that it's preserved .HL6 RTS ; Return from the subroutine .LI23 ; If we get here then we need to move up into the bottom ; pixel row in the character block above LDA #7 ; Set the pixel line number within the character row STA T2 ; (which we store in T2) to 7, which is the bottom pixel ; row of the character block above STX T ; Store the current character row number in T, so we can ; restore it below LDX T1 ; Decrement the number of the character row in T1, as we DEX ; are moving up a row STX T1 LDA SCTBL,X ; Set SC(1 0) to the X-th entry from (SCTBH2 SCTBL), so STA SC ; it contains the address of the start of the bottom LDA SCTBH2,X ; pixel row in character row X in screen memory (so ; that's the bottom pixel row in the character row we ; just moved up into) ; ; We set the high byte below (though there's no reason ; why it isn't done here) LDX T ; Restore the value of X that we stored, so X contains ; the previous character row number, from before we ; moved up a row (we need to do this as the following ; jump returns us to a point where the previous row ; number is still in X) STA SC+1 ; Set the high byte of SC(1 0) as above JMP LI19 ; Jump back to keep drawing the lineName: LOIN (Part 7 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a steep line going up and right or down and left Deep dive: Bresenham's line algorithmContext: See this subroutine on its own page References: This subroutine is called as follows: * HLOIN calls via HL6
This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * The line is going up and right (no swap) or down and left (swap) * X1 >= X2 and Y1 >= Y2 * Draw from (X1, Y1) at bottom left to (X2, Y2) at top right, omitting the first pixel
Other entry points: HL6 Contains an RTS.MSBARS JSR P%+3 ; Call the following instruction as a subroutine, to ; draw a line from (X1, Y1+1) to (X2, Y1+1), and then ; fall through to draw another line from (X1, Y1+2) to ; (X2, Y1+2), as Y1 is incremented each time INC Y1 ; Increment Y1 and fall through into HLOIN to draw a ; horizontal line from (X1, Y1) to (X2, Y1)Name: MSBARS [Show more] Type: Subroutine Category: Dashboard Summary: Draw two horizontal lines as part of an indicator bar, from (X1, Y1+1) to (X2, Y1+1) and (X1, Y1+2) to (X2, Y1+2)Context: See this subroutine on its own page References: This subroutine is called as follows: * DIS1 calls MSBARS * MSBAR calls MSBARS
Returns: Y1 Y1 is incremented by 2.HLOIN STY YSAV ; Store Y into YSAV, so we can preserve it across the ; call to this subroutine ; We are going to draw the line like this: ; ; * Draw the byte containing the start of the line ; (and if it also happens to contain the end of the ; line, draw both ends in one byte and terminate) ; ; * Draw any full bytes in the middle of the line ; ; * Draw the byte containing the end of the line, plus ; one more pixel (which may spill over into the next ; pixel byte) ; ; We draw the end cap with an extra pixel to ensure that ; there is room for a full two-bit colour number in the ; last byte (i.e. %00 for two black pixels, %11 for two ; white pixels, %01 or %10 for two coloured pixels) ; ; To facilitate this approach, we need to make sure the ; start and end x-coordinates are both even, so the ; two-bit colour numbers start on even pixel numbers LDA X1 ; Round the x-coordinate in X1 down to the nearest even AND #%11111110 ; coordinate, so we can draw the line in two-pixel steps STA X1 TAX ; Set X to the rounded x-coordinate in X1 LDA X2 ; Round the x-coordinate in X2 down to the nearest even AND #%11111110 ; coordinate, setting A to the rounded coordinate in X2 STA X2 ; in the process, so we can draw the line in two-pixel ; steps CMP X1 ; If X1 = X2 then the start and end points are the same, BEQ HL6 ; so return from the subroutine (as HL6 contains an RTS) BCS HL5 ; If X1 < X2, jump to HL5 to skip the following code, as ; (X1, Y1) is already the left point STX X2 ; Swap the values of X1 and X2 (in X and X2), so we know TAX ; that (X1, Y1) is on the left and (X2, Y1) is on the ; right ; ; This does not update X1, but we don't use it in the ; following (we use X instead) .HL5 LDA Y1 ; Set A to the y-coordinate in Y1 LSR A ; Set A = A >> 3 LSR A ; = y div 8 LSR A ; ; So A now contains the number of the character row ; that will contain our horizontal line TAY ; Set the low byte of SC(1 0) to the Y-th entry from LDA SCTBL,Y ; SCTBL, which contains the low byte of the address of STA SC ; the start of character row Y in screen memory LDA Y1 ; Set A = Y1 mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw our line (as ; each character block has 8 rows) ASL A ; Set the high byte of SC(1 0) as follows: ASL A ; ADC SCTBH,Y ; SC+1 = SCBTH for row Y + pixel row * 4 STA SC+1 ; ; Because this is the high byte, and because we already ; set the low byte in SC to the Y-th entry from SCTBL, ; this is the same as the following: ; ; SC(1 0) = (SCBTH SCTBL) for row Y + pixel row * $400 ; ; So SC(1 0) contains the address in screen memory of ; the pixel row containing the pixel we want to draw, as ; (SCBTH SCTBL) gives us the address of the start of the ; character row, and each pixel row within the character ; row is offset by $400 bytes LDA SCTBX2,X ; Using the lookup table at SCTBX2, set A to the byte ; number within the pixel row that contains the pixel we ; want to draw (as X contains the x-coordinate of the ; start of the line) AND #1 ; Set Y to the colour in COL, plus 1 if the byte number ORA COL ; within the pixel row is odd TAY ; ; We can use this to fetch the correct pixel bytes from ; MASKT that we can poke into screen memory to draw a ; continuous line of the relevant colour ; ; Bytes #0 and #1 of the relevant entry in MASKT contain ; the bit pattern for when the first byte is placed in ; an even-numbered pixel byte (counting along the pixel ; row), while bytes #1 and #2 contain the bit pattern ; for when the first byte is placed in an odd-numbered ; pixel byte ; ; So Y now points to the correct MASKT entry for the ; start of the line, because it points to byte #0 in ; offset COL if the byte number within the pixel row is ; even, or byte #1 in offset COL if the byte number ; within the pixel row is odd LDA MASKT,Y ; Set T1 and T2 to the correct pixel bytes for drawing a STA T1 ; continuous line in colour COL into the pixel byte that LDA MASKT+1,Y ; contains the pixel we want to draw STA T2 ; So T1 contains the pattern for the byte at the start ; of the line, and T2 contains the pattern for the next ; byte along, and T1 contains the pattern for the byte ; after that, and so on ; ; In other words, we alternate between the patterns in ; T1 and T2 as we work our way along the line, one byte ; at a time .HL1 LDY X2 ; Using the lookup table at SCTBX2, set A to the byte LDA SCTBX2-2,Y ; number within the pixel row that contains the pixel ; at X2 - 2, so we omit the last pixel (we subtract 2 ; as we draw the end of the line with an extra pixel) LDY SCTBX1,X ; Using the lookup table at SCTBX1, set Y to the bit ; number within the pixel byte that corresponds to the ; pixel at the start of the line (as X contains the ; x-coordinate of the start of the line) SEC ; SCTBX2,X is the byte number within the pixel row that SBC SCTBX2,X ; contains the pixel at X, which is at the start of the STA R ; line, so this calculation: ; ; R = A - SCTBX2,X ; ; sets R to the number of pixel bytes between the start ; and the end of the line LDA TWFR,Y ; Fetch a ready-made byte with Y pixels filled in at the ; right end of the byte (so the filled pixels start at ; point Y and go all the way to the end of the byte), ; which is the shape we want for the left end of the ; line AND T1 ; Apply the pixel mask in A to the continuous block of ; coloured pixels in T1, so we now know which bits to ; set in screen memory to paint the relevant pixels in ; the required colour for the pixel byte at the start ; of the line LDY SCTBX2,X ; Set Y to the byte number within the pixel row that ; contains the pixel at X, which is at the start of the ; line LDX R ; Set X = R, so X contains the number of pixel bytes ; between the start and end of the line ; ; We use X as a counter in the following to ensure we ; draw the correct number of bytes for the line BEQ HL3 ; If X = 0 then there are no pixel bytes between the ; start and end, which means the line starts and ends ; within the same pixel byte, so jump to HL3 to draw ; this single-byte line ; Otherwise we need to draw the pixel byte containing ; the left end of the line STA T4 ; Store the pixel pattern for the left end of the line ; in T4 LDA (SC),Y ; Draw the pixel pattern in T4 into the Y-th pixel byte AND #%01111111 ; on the line's pixel row, using EOR logic to merge the EOR T4 ; pattern with whatever is already on-screen, and using STA (SC),Y ; AND to set the colour palette in bit 7 to that in T4 INY ; Increment Y to move to the next pixel byte to the ; right DEX ; Decrement the byte counter in X as we just drew the ; first pixel byte, so X now contains the number of ; bytes left before we reach the pixel byte containing ; the end of the line BEQ HL4 ; If X = 0 then there are no more pixel bytes before we ; reach the end of the line, so jump to HL4 to skip ; drawing any pixel bytes between the start and end ; bytes (as there aren't any) ; Otherwise we now loop through all the pixel bytes in ; the line between the start byte and the end byte, ; using the counter in X to draw the correct number of ; bytes ; ; We draw the bytes in the middle of the line two at a ; time, using pattern T2 for the first byte, then T1 ; for the next, and then T2 again, and so on ; ; If we reach the end of the middle section having just ; drawn a T2 byte, we jump to HL8 to make sure we draw ; the end of the line using pattern T1, otherwise we ; fall through into HL4 to draw it using pattern T2 .HLL1 LDA (SC),Y ; Draw the pixel pattern in T2 into the Y-th pixel byte AND #%01111111 ; on the line's pixel row, using EOR logic to merge the EOR T2 ; pattern with whatever is already on-screen, and using STA (SC),Y ; AND to set the colour palette in bit 7 to that in T2 INY ; Increment Y to move to the next pixel byte to the ; right DEX ; Decrement the byte counter in X as we just drew the ; first pixel byte BEQ HL8 ; If X = 0 then we have drawn all the bytes between the ; start and end bytes, so jump to HL8 to draw the byte ; at the end of line using pattern T1 LDA (SC),Y ; Draw the pixel pattern in T1 into the Y-th pixel byte AND #%01111111 ; on the line's pixel row, using EOR logic to merge the EOR T1 ; pattern with whatever is already on-screen, and using STA (SC),Y ; AND to set the colour palette in bit 7 to that in T1 INY ; Increment Y to move to the next pixel byte to the ; right DEX ; Decrement the byte counter in X as we just drew the ; first pixel byte BNE HLL1 ; Loop back until we have drawn all X bytes ; We have finished drawing the middle of the line, so ; fall through into HL4 to draw the end of the line ; using the pattern in T2 .HL4 ; If we reach here then we only have one more pixel byte ; to draw, the one for the end of the line LDA T2 ; Set A to the pattern in T2, as we only get here if we ; have just drawn a pixel byte with the pattern in T1 .HL2 LDX X2 ; Set X to the x-coordinate of the end of the line LDY SCTBX1-2,X ; Using the lookup table at SCTBX1, set Y to the bit ; number within the pixel byte that corresponds to the ; pixel at x-coordinate X2 - 2, so we omit the last ; pixel (we subtract 2 as we draw the end of the line ; with an extra pixel) CPY #6 ; If Y < 6 then clear the C flag, so we can use this to ; check whether we need to spill into the next pixel ; byte to draw the end of the line properly AND TWFL,Y ; Apply the pixel pattern in A to a ready-made byte with ; Y + 1 pixels filled in at the left end of the byte (so ; the filled pixels start at the left edge and go up to ; point Y + 1), which is the shape we want for the right ; end of the line ; ; Note that unlike TWFR, the minimum cap size is two ; pixels, so it can take a full two-bit colour number ; even if we only really need one pixel in the end cap LDY SCTBX2-2,X ; Using the lookup table at SCTBX2, set Y to the byte ; number within the pixel row that contains the pixel ; at X2 - 2, so we omit the last pixel (we subtract 2 ; as we draw the end of the line with an extra pixel) STA T4 ; Store the pixel pattern for the right end of the line ; in T4 LDA (SC),Y ; Draw the pixel pattern in T4 into the Y-th pixel byte AND #%01111111 ; on the line's pixel row, using EOR logic to merge the EOR T4 ; pattern with whatever is already on-screen, and using STA (SC),Y ; AND to set the colour palette in bit 7 to that in T4 BCC HL7 ; If the C flag is clear then the bit number for the ; pixel at the end of the line is less than 6, so jump ; to HL7 as the end of the line is not spilling into the ; next pixel byte ; If we get here then the last pixel in the line is at ; bit number 6, so we need to spill over by one bit into ; the next pixel byte, as the colour of a pixel is ; defined by a two-bit sequence LDA #%10000001 ; We only want to draw one bit into the next pixel byte, AND T1 ; and the bit we need to set is bit 0, as this is the ; leftmost pixel in the pixel byte, so we take the pixel ; pattern from T1 and extract just bits 0 (for the ; overspill) and bit 7 (for the colour palette) to leave ; the correct pixel byte for the overspill in A INY ; Increment Y to move to the next pixel byte to the ; right, which is where we need to poke the overspill STA T4 ; Store the pixel pattern for the overspill in T4 LDA (SC),Y ; Draw the pixel pattern in T4 into the Y-th pixel byte AND #%01111111 ; on the line's pixel row, using EOR logic to merge the EOR T4 ; pattern with whatever is already on-screen, and using STA (SC),Y ; AND to set the colour palette in bit 7 to that in T4 .HL7 LDY YSAV ; Restore Y from YSAV, so that it's preserved RTS ; Return from the subroutine .HL8 ; If we get here then we just finished drawing the ; middle section of the line using pattern T2, so we ; need to draw the end of the line using pattern T1 LDA T1 ; Set A to the colour pattern in T1, to use for the ; last pixel byte at the end of the line .HL3 ; If we jump directly here from above, then the line ; starts and ends within the same pixel byte, and A ; contains the pixel pattern for the left end of the ; line, so we keep that pattern in A before jumping to ; HL2 ; ; This means that when we apply the pattern in A to the ; end of the line, we end up with a single-byte pixel ; pattern that contains both ends of the line LDX T2 ; Set T1 = T2 STX T1 ; ; We do this because the routine at HL2 draws the pixel ; byte for the end of the line using the pattern in A, ; which will either be T1 (if we got here via HL8 above) ; or the single-byte pixel pattern that's based on T1 ; (if we got here by jumping to HL3) ; ; In both cases the pattern of the next byte along after ; the end of the line should therefore be in pattern T2, ; and the code at HL2 uses the pattern in T1 to draw any ; overspill after the byte containing the end of the ; line, so this ensures that any overspill uses the ; pattern in T2, rather than T1 JMP HL2 ; Jump to HL2 to draw the last pixel byte of the line ; using the pixel pattern in AName: HLOIN [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a horizontal line from (X1, Y1) to (X2, Y1)Context: See this subroutine on its own page References: This subroutine is called as follows: * HLOIN2 calls HLOIN * NLIN2 calls HLOIN * SUN (Part 3 of 4) calls HLOIN * TT15 calls HLOIN * TT22 calls HLOIN * TTX66K calls HLOIN
We do not draw a pixel at the right end of the line.
Arguments: COL The line colour, as an offset into the MASKT table
Returns: Y Y is preserved.MASKT ; Byte #2 Byte #1 Byte #0 Byte #0 Byte #1 Byte #2 ; 6543210 6543210 6543210 0123456 0123456 0123456 EQUD %000000000000000000000000 ; Black (00) in colour palette 0 ; 0000000 0000000 0000000 EQUD %010101010010101001010101 ; Violet (x0) in colour palette 0 ; x0x0x0x 0x0x0x0 x0x0x0x EQUD %001010100101010100101010 ; Green (0x) in colour palette 0 ; 0x0x0x0 x0x0x0x 0x0x0x0 EQUD %011111110111111101111111 ; White (xx) in colour palette 0 ; xxxxxxx xxxxxxx xxxxxxx EQUD %110101011010101011010101 ; Blue (x0) in colour palette 1 ; x0x0x0x 0x0x0x0 x0x0x0x EQUD %101010101101010110101010 ; Red (0x) in colour palette 1 ; 0x0x0x0 x0x0x0x 0x0x0x0 EQUD %101010101010101010101010 ; Fuzzy (red/black/blue) in colour palette 1 ; 0x0x0x0 0x0x0x0 0x0x0x0Name: MASKT [Show more] Type: Variable Category: Drawing lines Summary: High-resolution pixel bytes for drawing continuous lines of solid colourContext: See this variable on its own page References: This variable is used as follows: * CPIX uses MASKT * HLOIN uses MASKT
This table contains four bytes for each colour (the colour variables such as VIOLET and RED are indexes into this table). The first three bytes contain the values we need to store in screen memory to draw a continuous line in the relevant colour; the fourth byte is ignored and is zero. Bytes #0 and #1 contain the bit pattern for when the first byte is placed in an even-numbered pixel byte (counting along the pixel row), while bytes #1 and #2 contain the bit pattern for when the first byte is placed in an odd-numbered pixel byte. The comments show the on-off patterns that the high-resolution mode converts into colours, with bit 7 removed for clarity..VLOIN STY YSAV ; Store Y into YSAV, so we can preserve it across the ; call to this subroutine LDA Y1 ; Set A to the y-coordinate of the start of the line CMP Y2 ; If Y1 >= Y2 then jump to VLO1 as the coordinates are BCS VLO1 ; already the correct way around LDY Y2 ; Otherwise swap Y1 and Y2 around so that Y1 >= Y2 STA Y2 ; (with Y1 in A) TYA .VLO1 ; We now want to draw a line from (X1, A) to (X1, Y2), ; which goes up the screen from bottom to top LDX X1 ; Draw a single pixel at screen coordinate (X1, A), at JSR CPIX ; the start of the line ; ; This also sets the following: ; ; * T2 = the number of the pixel row within the ; character block that contains the pixel, ; which we use in the loop below to draw the ; line ; ; * R = the pixel byte for drawing the pixel ; ; * Y = the byte offset within the pixel row of the ; byte that contains the pixel LDA Y1 ; Set A = Y1 - Y2 SEC ; SBC Y2 ; So A contains the height of the vertical line in ; pixels BEQ VLO5 ; If the start and end points are at the same height, ; jump to VLO5 to return from the subroutine, as we ; already drew a one-pixel vertical line TAX ; Set X to the height of the line, plus 1, so we can use INX ; this as a pixel counter in the loop below (the extra 1 ; is to take account of the pixel we just drew) JMP VLO4 ; Jump into the following loop at VLO4 to draw the rest ; of the line .VLOL1 LDA R ; Set A to the pixel byte that was returned by the CPIX ; routine when we drew the first pixel in the vertical ; line, which is the same pixel byte that we need for ; every pixel in the line (as it is a vertical line) EOR (SC),Y ; Draw the pixel pattern in A into the Y-th pixel byte STA (SC),Y ; on the correct pixel row, using EOR logic to merge the ; pattern with whatever is already on-screen LDA T3 ; Set A to the pattern for the next byte along, which ; was returned by the CPIX BEQ VLO4 ; If T3 is zero then there is no need to write to the ; next byte along, so jump to VLO4 to move on to drawing ; the rest of the line INY ; Increment Y to move to the next pixel byte to the ; right EOR (SC),Y ; Draw the pixel pattern in A into the Y-th pixel byte STA (SC),Y ; on the correct pixel row, using EOR logic to merge the ; pattern with whatever is already on-screen DEY ; Decrement Y to move back to the previous pixel byte, ; so we keep drawing our line in the correct position .VLO4 ; This is where we join the loop from above, at which ; point we have the following variables set: ; ; * T2 = the pixel row of the start of the line ; ; * X = the height of the line we want to draw + 1 ; ; * Y = the byte offset within the pixel row of the ; line ; ; The height in X has an extra one added to it because ; we are about to decrement it (so that extra one is ; effectively counting the single pixel we already drew ; before jumping here) DEC T2 ; Decrement the pixel row number in T2 to move to the ; pixel row above BMI VLO2 ; If T2 is negative then the we are no longer within the ; same character block, so jump to VLO2 to move to the ; bottom pixel row in the character row above ; We now need to move up into the pixel row above LDA SC+1 ; Subtract 4 from the high byte of SC(1 0), so this does SEC ; the following: SBC #4 ; STA SC+1 ; SC(1 0) = SC(1 0) - $400 ; ; So this sets SC(1 0) to the address of the pixel row ; above the one we just drew in, as each pixel row ; within the character row is spaced out by $400 bytes ; in screen memory .VLO3 DEX ; Decrement the pixel counter in X BNE VLOL1 ; Loop back until we have drawn X - 1 pixels .VLO5 LDY YSAV ; Restore Y from YSAV, so that it's preserved RTS ; Return from the subroutine .VLO2 ; If we get here then we need to move up into the bottom ; pixel row in the character block above LDA #7 ; Set the pixel line number within the character row STA T2 ; (which we store in T2) to 7, which is the bottom pixel ; row of the character block above STX T ; Store the current character row number in T, so we can ; restore it below LDX T1 ; Decrement the number of the character row in T1, as we DEX ; are moving up a row STX T1 LDA SCTBL,X ; Set SC(1 0) to the X-th entry from (SCTBH2 SCTBL), so STA SC ; it contains the address of the start of the bottom LDA SCTBH2,X ; pixel row in character row X in screen memory (so ; that's the bottom pixel row in the character row we ; just moved up into) ; ; We set the high byte below (though there's no reason ; why it isn't done here) LDX T ; Restore the value of X that we stored, so X contains ; the previous character row number, from before we ; moved up a row (we need to do this as the following ; jump returns us to a point where the previous row ; number is still in X) STA SC+1 ; Set the high byte of SC(1 0) as above JMP VLO3 ; Jump back to keep drawing the lineName: VLOIN [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a vertical line from (X1, Y1) to (X1, Y2)Context: See this subroutine on its own page References: This subroutine is called as follows: * DIS5 calls VLOIN * DVLOIN calls VLOIN * SCAN calls VLOIN * TT15 calls VLOIN
Arguments: R The line colour, as an offset into the MASKT table
Returns: Y Y is preserved.CPIX STA Y1 ; Store the y-coordinate in Y1 LSR A ; Set T1 = A >> 3 LSR A ; = y div 8 LSR A ; STA T1 ; So T1 now contains the number of the character row ; that will contain the pixel we want to draw TAY ; Set the low byte of SC(1 0) to the Y-th entry from LDA SCTBL,Y ; SCTBL, which contains the low byte of the address of STA SC ; the start of character row Y in screen memory LDA Y1 ; Set A = Y1 mod 8, which is the pixel row within the AND #7 ; character block at which we want to draw our pixel (as ; each character block has 8 rows) STA T2 ; Store the pixel row number in T2, so we can return it ; from the subroutine ASL A ; Set the high byte of SC(1 0) as follows: ASL A ; ADC SCTBH,Y ; SC+1 = SCBTH for row Y + pixel row * 4 STA SC+1 ; ; Because this is the high byte, and because we already ; set the low byte in SC to the Y-th entry from SCTBL, ; this is the same as the following: ; ; SC(1 0) = (SCBTH SCTBL) for row Y + pixel row * $400 ; ; So SC(1 0) contains the address in screen memory of ; the pixel row containing the pixel we want to draw, as ; (SCBTH SCTBL) gives us the address of the start of the ; character row, and each pixel row within the character ; row is offset by $400 bytes LDY SCTBX1,X ; Using the lookup table at SCTBX1, set Y to the bit ; number within the pixel byte that corresponds to the ; pixel we want to draw (as X contains the x-coordinate ; of the pixel) LDA #0 ; Set A = 0 to use as the pixel mask for the next pixel ; byte along, so by default we don't change anything in ; the next pixel byte CPY #6 ; If Y = 6 then then the bit number for the pixel is bit BNE P%+4 ; 6 and we will need to spill into the next pixel byte, LDA #%10000001 ; so set A = %10000001 to use as the pixel mask for the ; next pixel byte along STA T3 ; Store the pixel mask for the next pixel byte in T3 LDA TWOS2,Y ; Fetch a two-bit pixel byte with the pixels set at STA R ; position Y and store it in R so we can use it as a ; mask for the bits we want to change in the pixel byte LDA SCTBX2,X ; Using the lookup table at SCTBX2, set A to the byte ; number within the pixel row that contains the pixel we ; want to draw (as X contains the x-coordinate of the ; start of the line) AND #1 ; Set Y to the colour in COL, plus 1 if the byte number ORA COL ; within the pixel row is odd TAY ; ; We can use this to fetch the correct pixel bytes from ; MASKT that we can poke into screen memory to draw a ; continuous line of the relevant colour ; ; Bytes #0 and #1 of the relevant entry in MASKT contain ; the bit pattern for when the first byte is placed in ; an even-numbered pixel byte (counting along the pixel ; row), while bytes #1 and #2 contain the bit pattern ; for when the first byte is placed in an odd-numbered ; pixel byte ; ; So Y now points to the correct MASKT entry for the ; start of the line, because it points to byte #0 in ; offset COL if the byte number within the pixel row is ; even, or byte #1 in offset COL if the byte number ; within the pixel row is odd LDA MASKT+1,Y ; Set T3 to the correct pixel byte for drawing our pixel AND T3 ; in the next pixel byte along, by combining the colour STA T3 ; mask from MASKT+1 with the pixel mask in T3 LDA MASKT,Y ; Set A to the correct pixel byte for drawing our pixel AND R ; in the first pixel byte, by combining the colour mask ; from MASKT with the pixel mask in A STA R ; Store the pixel byte for drawing our pixel in R, so it ; can be returned by the subroutine ; So A contains the pattern for the byte at the pixel ; coordinates, and T3 contains the pattern for the next ; byte along LDY SCTBX2,X ; Using the lookup table at SCTBX2, set Y to the byte ; number within the pixel row that contains the pixel ; at X EOR (SC),Y ; Draw the pixel pattern in A into the Y-th pixel byte STA (SC),Y ; on the correct pixel row, using EOR logic to merge the ; pattern with whatever is already on-screen LDA T3 ; Set A to the pattern for the next byte along BEQ CPR1 ; If T3 is zero then there is no need to write to the ; next byte along, so jump to CPR1 to return from the ; subroutine INY ; Increment Y to move to the next pixel byte to the ; right EOR (SC),Y ; Draw the pixel pattern in A into the Y-th pixel byte STA (SC),Y ; on the correct pixel row, using EOR logic to merge the ; pattern with whatever is already on-screen DEY ; Decrement Y to move back to the previous pixel byte, ; so we can return this value from the subroutine .CPR1 RTS ; Return from the subroutineName: CPIX [Show more] Type: Subroutine Category: Drawing pixels Summary: Draw a colour pixelContext: See this subroutine on its own page References: This subroutine is called as follows: * DOEXP calls CPIX * SCAN calls CPIX * VLOIN calls CPIX
Arguments: X The screen x-coordinate of the pixel A The screen y-coordinate of the pixel COL The pixel colour, as an offset into the MASKT table
Returns: Y The byte offset within the pixel row of the byte that contains the pixel T2 The number of the pixel row within the character block that contains the pixel R The pixel byte for drawing the pixel.ECBLB2 LDA #32 ; Set the E.C.M. countdown timer in ECMA to 32 STA ECMA ;LDY #sfxecm ; These instructions are commented out in the original ;JSR NOISE ; source ; Fall through into ECBLB to light up the E.C.M. bulbName: ECBLB2 [Show more] Type: Subroutine Category: Dashboard Summary: Start up the E.C.M. (light up the indicator, start the countdown and make the E.C.M. sound)Context: See this subroutine on its own page References: This subroutine is called as follows: * Main flight loop (Part 3 of 16) calls ECBLB2 * TACTICS (Part 1 of 7) calls ECBLB2.ECBLB LDA #LO(ECBT) ; Set A to the low byte of the character definition in ; ECBT, to pass to BULB LDX #7*8 ; The E.C.M. bulb is in character block number 7 and ; each character is eight pixels wide, so this sets ; X to the pixel x-coordinate of the bulb we want to ; draw BNE BULB ; Jump down to BULB (this BNE is effectively a JMP as ; A will never be zero)Name: ECBLB [Show more] Type: Subroutine Category: Dashboard Summary: Light up the E.C.M. indicator bulb ("E") on the dashboardContext: See this subroutine on its own page References: This subroutine is called as follows: * ECMOF calls ECBLB.SPBLB LDA #LO(SPBT) ; Set A to the low byte of the address of the character ; definition in SPBT LDX #24*8 ; The space station bulb is in character block number 24 ; and each character is eight pixels wide, so this sets ; X to the pixel x-coordinate of the bulb we want to ; draw.BULB STA P ; Store the low byte of the screen address in P LDA #HI(SPBT) ; Set the high byte of P(1 0) to the high byte of SPBT, STA P+1 ; so P(1 0) now contains the address of the character ; definition of the bulb to be drawn (this assumes that ; ECBT and SPBT are in the same page and have the same ; high byte) LDA #22 ; Set YC = 22, so we draw the bulb on that character row STA YC JMP letter2 ; Call letter2 to print the character definition pointed ; to by P(1 0) in text column X on character row 22, ; returning from the subroutine using a tail callName: BULB [Show more] Type: Subroutine Category: Dashboard Summary: Draw an indicator bulb on the dashboardContext: See this subroutine on its own page References: This subroutine is called as follows: * ECBLB calls BULB
Arguments: A The low byte of the address of the character definition of the bulb to be drawn, i.e. #LO(ECBT) for the E.C.M. bulb, or #LO(SPBT) for the space station bulb X The pixel x-coordinate of the bulb we want to draw.ECBT EQUB %01111111 ; x x x x x x x EQUB %01111111 ; x x x x x x x EQUB %00000111 ; x x x . . . .Name: ECBT [Show more] Type: Variable Category: Dashboard Summary: The character bitmap for the E.C.M. indicator bulbContext: See this variable on its own page References: This variable is used as follows: * ECBLB uses ECBT
The character bitmap for the E.C.M. indicator's "E" bulb that gets displayed on the dashboard. The E.C.M. indicator uses the first 5 rows of the space station's "S" bulb below, as the bottom 5 rows of the "E" match the top 5 rows of the "S". The bulb is seven pixels wide, so it fits into one character block, along with the colour palette in bit 7..SPBT EQUB %01111111 ; x x x x x x x EQUB %01111111 ; x x x x x x x EQUB %00000111 ; x x x . . . . EQUB %01111111 ; x x x x x x x EQUB %01111111 ; x x x x x x x EQUB %01110000 ; . . . . x x x EQUB %01111111 ; x x x x x x x EQUB %01111111 ; x x x x x x xName: SPBT [Show more] Type: Variable Category: Dashboard Summary: The bitmap definition for the space station indicator bulbContext: See this variable on its own page References: This variable is used as follows: * BULB uses SPBT * SPBLB uses SPBT
The bitmap definition for the space station indicator's "S" bulb that gets displayed on the dashboard. The bulb is seven pixels wide, so it fits into one character block, along with the colour palette in bit 7..MSBAR TYA ; Store the new indicator colour in Y on the stack so we PHA ; can retrieve it after the call to MSBAR2 JSR MSBAR2 ; Call MSBAR2 below to draw this indicator using its ; previous value and colour, which will remove it from ; the screen as we draw indicators using EOR logic PLA ; Retrieve the new indicator colour from the stack and STA mscol-1,X ; store it in the mscol table, for use the next time ; the indicator is drawn ; ; We subtract 1 as the indicator number is in the range ; 1 to NOMSL (i.e. 1 to 4 if we have four missiles ; fitted) .MSBAR2 LDA mscol-1,X ; Set A to the previous colour of this indicator from ; the mscol table BEQ coolkey ; If the previous colour is the same as the new colour, ; jump to coolkey to clear the C flag and return from ; the subroutine, as we do not need to redraw the ; indicator STA COL ; Set the drawing colour to A LDA msloc-1,X ; Set X1 to the x-coordinate for indicator X, which we STA X1 ; fetch from the msloc table CLC ; Set X1 = X2 + 6 ADC #6 ; STA X2 ; So the indicator is six pixels across TXA ; Store the indicator number in X on the stack so we can PHA ; retrieve it after the calls to MSBARS LDA #184 ; Set Y1 = 184, the y-coordinate of the top line of the STA Y1 ; indicators JSR MSBARS ; Call MSBARS twice to draw four pixel lines to form the JSR MSBARS ; missile indicator, drawing from top to bottom PLA ; Restore the indicator number from the stack into X so TAX ; it is preserved TYA ; Set A to the value of Y when we called this routine, ; so we can return it from the subroutine LDY #0 ; Set Y = 0, so we can return it from the subroutine RTS ; Return from the subroutineName: MSBAR [Show more] Type: Subroutine Category: Dashboard Summary: Draw a specific indicator in the dashboard's missile barContext: See this subroutine on its own page References: This subroutine is called as follows: * ABORT2 calls MSBAR * Main flight loop (Part 3 of 16) calls MSBAR * msblob calls MSBAR
Arguments: X The number of the missile indicator to update (counting from right to left, so indicator NOMSL is the leftmost indicator) Y The new colour of the missile indicator: * #BLACK = black (no missile) * #RED = red (armed and locked) * #WHITE = white (armed) * #GREEN = green (disarmed)
Returns: X X is preserved Y Y is set to 0 A The value of Y when the subroutine was called.msloc EQUB 40 ; Indicator 1 (right) EQUB 32 ; Indicator 2 EQUB 24 ; Indicator 3 EQUB 16 ; Indicator 4 (left)Name: msloc [Show more] Type: Variable Category: Dashboard Summary: The screen x-coordinates of the four missile indicators on the dashboardContext: See this variable on its own page References: This variable is used as follows: * MSBAR uses msloc.newosrdch JSR $FFFF ; This address is overwritten by the STARTUP routine to ; contain the original value of RDCHV, so this call acts ; just like a standard JSR OSRDCH call, and reads a ; character from the current input stream and stores it ; in A CMP #128 ; If A < 128 then skip the following three instructions, BCC P%+6 ; otherwise the character is invalid, so fall through ; into badkey to deal with it .badkey ; If we get here then the character we read is invalid, ; so we return a beep character LDA #7 ; Set A to the beep character CLC ; Clear the C flag RTS ; Return from the subroutine ; If we get here then A < 128 CMP #' ' ; If A >= ASCII " " then this is a valid alphanumerical BCS coolkey ; key press (as A is in the range 32 to 127), so jump ; down to coolkey to return this key press CMP #13 ; If A = 13 then this is the return character, so jump BEQ coolkey ; down to coolkey to return this key press CMP #21 ; If A <> 21 jump up to badkey BNE badkey .coolkey ; If we get here then the character we read is valid, so ; return it CLC ; Clear the C flag RTS ; Return from the subroutineName: newosrdch [Show more] Type: Subroutine Category: Tube Summary: The custom OSRDCH routine for reading characters Deep dive: 6502 Second Processor Tube communicationContext: See this subroutine on its own page References: This subroutine is called as follows: * MSBAR calls via coolkey
This routine is not used in this version of Elite. It is left over from the 650s Second Processor version. The entry point at coolkey is used, however.
Other entry points: coolkey Clear the C flag and return from the subroutine.WSCAN IF _IB_DISK OR _4AM_CRACK PHA ; Store the A, X and Y registers on the stack TXA PHA TYA PHA LDY #15 ; Set an outer loop counter in Y, so we do a total of 15 ; outer loops to give a delay of 15 * 256 iterations LDX #0 ; Set an inner loop counter in X to do 256 iterations of ; each inner loop .WSCL1 DEX ; Decrement the inner loop counter BNE WSCL1 ; Loop back until we have done 256 iterations around the ; inner loop DEY ; Decrement the outer loop counter BNE WSCL1 ; Loop back until we have done Y iterations around the ; outer loop, to give Y * 256 iterations in all PLA ; Retrieve the A, X and Y registers from the stack TAY PLA TAX PLA ELIF _SOURCE_DISK BIT $C019 ; Wait until bit 7 of the VERTBLANK soft switch is set, BPL WSCAN ; which occurs when the vertical retrace is on .WSCL1 BIT $C019 ; Wait until bit 7 of the VERTBLANK soft switch is BMI WSCL1 ; clear, which occurs when the vertical retrace is off ENDIF RTS ; Return from the subroutineName: WSCAN [Show more] Type: Subroutine Category: Drawing the screen Summary: Wait for 15 * 256 loop iterationsContext: See this subroutine on its own page References: This subroutine is called as follows: * DELAY calls WSCAN * DK4 calls WSCAN * TT16 calls WSCAN
In the released version of Apple II Elite, this routine implements a fixed-length pause of 15 * 256 iterations of an empty loop. In the version on the source disk on Ian Bell's site, this routine waits for the vertical sync by checking the state of the VERTBLANK soft switch, so this behaviour was presumably changed at some stage during development..CHPR2 CMP #123 ; If the character to print in A is outside of the range BCS whosentthisshit ; 13 to 122, jump to whosentthisshit to print nothing CMP #13 BCC whosentthisshit BNE CHPR ; If A is not 13, jump to CHPR to print the character, ; returning from the subroutine using a tail call LDA #12 ; If we get here then A is 13, so call CHPR with A = 12, JSR CHPR ; which will print a carriage return LDA #13 ; Set A = 13 so it is unchanged .whosentthisshit CLC ; Clear the C flag, as the CHPR routine does this and ; we need CHPR2 to act in the same way RTS ; Return from the subroutineName: CHPR2 [Show more] Type: Subroutine Category: Text Summary: Character print vector handlerContext: See this subroutine on its own page References: This subroutine is called as follows: * COLD calls CHPR2
This routine is set as the handler in CHRV, so it replaces the Kernal's character-printing routine.
Arguments: A The character to print
Returns: C flag The C flag is cleared.R5 JSR BEEP ; Call the BEEP subroutine to make a short, high beep JMP RR4 ; Jump to RR4 to restore the registers and return from ; the subroutine using a tail callName: R5 [Show more] Type: Subroutine Category: Text Summary: Make a beep and jump back into the character-printing routine at CHPRContext: See this subroutine on its own page References: This subroutine is called as follows: * CHPR calls R5.clss BIT text ; If bit 7 of text is clear then the current screen mode BPL clss1 ; is the high-resolution graphics mode, so jump to clss1 ; to clear the graphics screen JSR cleartext ; This is a text view, so clear screen memory for the ; text screen mode and move the text cursor to the ; top-left corner LDA K3 ; We called this routine from CHPR, which put the ; character we are printing into K3, so set A to the ; character number so we can jump back to CHPR to print ; it on the newly cleared screen JMP RRafter ; Jump back into the CHPR routine to print the character ; in A .clss1 JSR cleargrap ; This is a high-resolution graphics screen, so clear ; screen memory for the top part of the graphics screen ; mode (the space view), drawing blue borders along the ; sides as we do so, and moving the text cursor to the ; top-left corner LDA K3 ; We called this routine from CHPR, which put the ; character we are printing into K3, so set A to the ; character number so we can jump back to CHPR to print ; it on the newly cleared screen JMP RRafter ; Jump back into the CHPR routine to print the character ; in AName: clss [Show more] Type: Subroutine Category: Drawing the screen Summary: Clear the screen, move the text cursor to the top-left corner and jump back into the CHPR routine to print the next characterContext: See this subroutine on its own page References: This subroutine is called as follows: * CHPR calls clss.RR5 IF _IB_DISK OR _4AM_CRACK BIT UPTOG ; If bit 7 of UPTOG is set, jump to RR7 to skip the BMI RR7 ; following, so we print both upper and lower case ; letters ELIF _SOURCE_DISK BIT UPTOG ; If bit 7 of UPTOG is clear, jump to RR7 to skip the BPL RR7 ; following, so we print both upper and lower case ; letters (so in the source disk variants, the default ; setting is to display upper case letters only) ENDIF CMP #'[' ; If the character in A is less than ASCII '[' then it BCC RR7 ; is already an upper case letter, so jump to RR7 to ; skip the following SBC #$20 ; This is a lower case letter, so subtract $20 to ; convert it to the upper case letter equivalent .RR7 ORA #128 ; Set bit 7 of the character number so that we print it ; in normal video (i.e. white characters on a black ; background) PHA ; Store the character to print on the stack so we can ; retrieve it below LDA cellocl,Y ; Use the cellocl lookup table to fetch the low byte of STA SC ; the address of text row Y in text screen memory and ; store it in the low byte of SC(1 0) TYA ; Set A = 4 + (Y mod 8) / 2 AND #7 ; LSR A ; This calculation converts the text row number into the CLC ; high byte of the address of character row Y in text ADC #4 ; screen memory, so it's a way of calculating the HI() STA SC+1 ; equivalent of the cellocl table TXA ; Copy X into Y, so Y contains the text column where we TAY ; want to print the character PLA ; Set A to the character to print on the stack, which we ; put on the stack above STA (SC),Y ; Print the character in A into column X on the text row ; at SC(1 0) JMP RR6 ; Jump back into the CHPR routine to move the text ; cursor along and return from the subroutineName: RR5 [Show more] Type: Subroutine Category: Text Summary: Print a character in the text screen modeContext: See this subroutine on its own page References: This subroutine is called as follows: * CHPR calls RR5
Arguments: A The character to print X The text column to print at (the x-coordinate) Y The line number to print on (the y-coordinate).TT67K ; This does the same as the existing TT67 routine, which ; is also present in this source, so it isn't clear why ; this duplicate exists ; ; In the original source this label is TT67, but ; because BeebAsm doesn't allow us to redefine labels, ; I have renamed it to TT67K LDA #12 ; Set A to a carriage return character ; Fall through into CHPR to print the newlineName: TT67K [Show more] Type: Subroutine Category: Text Summary: Print a newlineContext: See this subroutine on its own page References: This subroutine is called as follows: * STATUS calls TT67K.CHPR STA K3 ; Store the A, X and Y registers, so we can restore STY YSAV2 ; them at the end (so they don't get changed by this STX XSAV2 ; routine) LDY QQ17 ; Load the QQ17 flag, which contains the text printing ; flags CPY #255 ; If QQ17 = 255 then printing is disabled, so jump to BEQ RR4 ; RR4, which doesn't print anything, it just restores ; the registers and returns from the subroutine .RRafter CMP #7 ; If this is a beep character (A = 7), jump to R5, BEQ R5 ; which will emit the beep, restore the registers and ; return from the subroutine CMP #32 ; If this is an ASCII character (A >= 32), jump to RR1 BCS RR1 ; below, which will print the character, restore the ; registers and return from the subroutine CMP #10 ; If this is control code 10 (line feed) then jump to BEQ RRX1 ; RRX1, which will move down a line, restore the ; registers and return from the subroutine .RRX2 LDX #1 ; If we get here, then this is control code 12 or 13, STX XC ; both of which are used. This code prints a newline, ; which we can achieve by moving the text cursor ; to the start of the line (carriage return) and down ; one line (line feed). These two lines do the first ; bit by setting XC = 1, and we then fall through into ; the line feed routine that's used by control code 10 .RRX1 CMP #13 ; If this is control code 13 (carriage return) then jump BEQ RR4 ; RR4 to restore the registers and return from the ; subroutine INC YC ; Print a line feed, simply by incrementing the row ; number (y-coordinate) of the text cursor, which is ; stored in YC BNE RR4 ; Jump to RR4 to restore the registers and return from ; the subroutine (this BNE is effectively a JMP as Y ; will never be zero) .RR1 LDX XC ; Set X to the text column in XC where we want to print ; the character in A CPX #31 ; If X < 31 then we are printing in a text column BCC RRa ; that's on-screen, so jump to RRa to skip the following JSR RRX2 ; Otherwise call RRX2 above as a subroutine to move the ; text cursor to column 1 and print a carriage return, ; so we move onto the next text row LDX XC ; Set X to the new value of XC, which is column 1 .RRa LDY YC ; Set Y to the text row in YC where we want to print ; the character in A CPY #24 ; If Y >= 24 then the row is off the bottom of the BCS clss ; screen, so call clss to clear the screen, move the ; text cursor to the top-left corner and jump back to ; RRafter above to print the character in A BIT text ; If bit 7 of text is set then the current screen mode BMI RR5 ; is the text mode, so jump to RR5 to print the ; character in the text screen mode PHA ; Store the character to print on the stack LDA XC ; Set X = 8 * XC + 13 - XC - 1 ASL A ; = 7 * XC + 12 ASL A ; ASL A ; So X is the pixel number of the text column, plus 12, ADC #13 ; as each byte contains seven pixels SBC XC TAX PLA ; Restore the character to print from the stack into A JSR letter ; Draw the character in A in the high-resolution screen ; mode at pixel x-coordinate X and text row YC .RR6 INC XC ; Move the text cursor to the right by one column .RR4 LDY YSAV2 ; We're done printing, so restore the values of the LDX XSAV2 ; A, X and Y registers that we saved above and clear LDA K3 ; the C flag, so everything is back to how it was CLC RTS ; Return from the subroutineName: CHPR [Show more] Type: Subroutine Category: Text Summary: Print a character at the text cursor by poking into screen memory Deep dive: Drawing textContext: See this subroutine on its own page References: This subroutine is called as follows: * BELL calls CHPR * BRBR calls CHPR * CHPR2 calls CHPR * CLYNS calls CHPR * MT26 calls CHPR * TITLE calls CHPR * TT26 calls CHPR * R5 calls via RR4 * RR5 calls via RR6 * clss calls via RRafter
Print a character at the text cursor (XC, YC), do a beep, print a newline, or delete left (backspace). The CHPR2 sends characters here for printing if they are in the range 13-122.
Arguments: A The character to be printed. Can be one of the following: * 7 (beep) * 10-13 (line feeds and carriage returns) * 32-95 (ASCII capital letters, numbers and punctuation) * 127 (delete the character to the left of the text cursor and move the cursor to the left) XC Contains the text column to print at (the x-coordinate) YC Contains the line number to print on (the y-coordinate)
Returns: A A is preserved X X is preserved Y Y is preserved C flag The C flag is cleared
Other entry points: RR4 Restore the registers and return from the subroutine RR6 A re-entry point from the RR5 routine after we print the character in A in the text screen mode RRafter A re-entry point from the clss routine to print the character in A.letter ; If we get here, then the character to print is an ; ASCII character in the range 32-95. The quickest way ; to display text on-screen is to poke the character ; pixel by pixel, directly into screen memory, so ; that's what the rest of this routine does ; ; The first step, then, is to get hold of the bitmap ; definition for the character we want to draw on the ; screen (i.e. we need the pixel shape of this ; character) ; ; The Apple II version of Elite uses its own unique font ; which is embedded into this source code at page FONT, ; so page 0 of the font is at FONT, page 1 is at ; FONT+$100, and page 2 at FONT+$200 ; ; The following code reads the relevant character ; bitmap from the copied font bitmaps at FONT and pokes ; those values into the correct position in screen ; memory, thus printing the character on-screen ; ; It's a long way from 10 PRINT "Hello world!":GOTO 10 ; The following logic is easier to follow if we look ; at the three character number ranges in binary: ; ; Bit # 76543210 ; ; 32 = %00100000 Page 0 of bitmap definitions ; 63 = %00111111 ; ; 64 = %01000000 Page 1 of bitmap definitions ; 95 = %01011111 ; ; 96 = %01100000 Page 2 of bitmap definitions ; 125 = %01111101 ; ; We'll refer to this below LDY #HI(FONT)-1 ; Set Y to point to the page before the first font page, ; which is HI(FONT) - 1 ASL A ; If bit 6 of the character is clear (A is 32-63) ASL A ; then skip the following instruction BCC P%+4 LDY #HI(FONT)+1 ; A is 64-126, so set Y to point to the after the first ; font page, which is HI(FONT) + 1 ASL A ; If bit 5 of the character is clear (A is 64-95) BCC RR9 ; then skip the following instruction INY ; Increment Y ; By this point, we started with Y = FONT-1, and then ; we did the following: ; ; If A = 32-63: skip then INX so Y = FONT ; If A = 64-95: Y = FONT+1 then skip so Y = FONT+1 ; If A = 96-126: Y = FONT+1 then INX so Y = FONT+2 ; ; In other words, Y points to the relevant page. But ; what about the value of A? That gets shifted to the ; left three times during the above code, which ; multiplies the number by 8 but also drops bits 7, 6 ; and 5 in the process. Look at the above binary ; figures and you can see that if we cleared bits 5-7, ; then that would change 32-53 to 0-31... but it would ; do exactly the same to 64-95 and 96-125. And because ; we also multiply this figure by 8, A now points to ; the start of the character's definition within its ; page (because there are 8 bytes per character ; definition) ; ; Or, to put it another way, Y contains the high byte ; (the page) of the address of the definition that we ; want, while A contains the low byte (the offset into ; the page) of the address .RR9 ;CLC ; These instructions are commented out in the original ;ADC #LO(FONT) ; source STA P ; Set the low byte of P(1 0) to A ;BCC P%+3 ; These instructions are commented out in the original ;INY ; source STY P+1 ; Set the high byte of P(1 0) to Y, so P(1 0) = (Y A) ; Fall through into letter2 to draw the character at ; address P(1 0) in the high-resolution screen modeName: letter [Show more] Type: Subroutine Category: Text Summary: Draw a character in the high-resolution screen mode using the game fontContext: See this subroutine on its own page References: This subroutine is called as follows: * CHPR calls letter
Arguments: X The pixel x-coordinate of the letter we want to draw YC The y-coordinate of the character as a text row number.letter2 LDY YC ; Set the low byte of SC(1 0) to the YC-th entry from LDA SCTBL,Y ; SCTBL, which contains the low byte of the address of STA SC ; the start of character row YC in screen memory LDA SCTBH,Y ; Set the high byte of SC(1 0) to the Y-th entry from STA SC+1 ; SCTBH, which contains the high byte of the address of ; the start of character row YC in screen memory LDY SCTBX1,X ; Using the lookup table at SCTBX1, set P+2 to the bit STY P+2 ; number within the pixel byte that corresponds to the ; pixel at the start of the character (as X contains the ; x-coordinate of the character we want to draw) LDY SCTBX2,X ; Using the lookup table at SCTBX2, set T1 to the byte STY T1 ; number within the pixel row that contains the start of ; the character (as X contains the x-coordinate of the ; character we want to draw) LDY #0 ; We want to print the 8 bytes of the character bitmap ; to the screen (one byte per pixel row), so set up a ; counter in Y to count through the character bitmap ; pixel rows, starting from the top and working ; downwards .RRL1 LDA #0 ; Set T3 = 0, to use as the pixel pattern for the second STA T3 ; pixel byte to the right (as the 8-bit wide character ; will span two seven-pixel character blocks) LDA (P),Y ; The character definition is at P(1 0), so load the ; Y-th byte from P(1 0), which will contain the bitmap ; for the Y-th row of the character ; We now take this 8-bit pixel pattern and position it ; correctly within two seven-bit high-resolution screen ; bytes, so we can poke it into screen memory ; ; The first byte (on the left) will be in A, and the ; second byte (on the right) will be in T3 ; ; To understand the following, consider two seven-pixel ; bytes side-by-side in the high-resolution screen, like ; this (omitting bit 7 for clarity): ; ; xxxxxxx 0000000 ; ; In this example, the pixel byte on the left contains ; all set pixels, while the pixel byte on the right ; contains all clear pixels ; ; If we want to shift the pixels along to the right ; on-screen, then we need to consider how this is laid ; out in terms of bit numbers: ; ; 0 6 0 6 ; xxxxxxx 0000000 ; ; So to shift the pattern along, we need to do this: ; ; 0 6 0 6 ; 0xxxxxx x000000 ; ; In other words, we shift bits out of the high end of ; the first byte (i.e. out of bit 6), and into the low ; end of the second byte (i.e. into bit 0) ; ; This is a bit counterintuitive, but it's what we need ; to do in order to shift our character bitmap byte in A ; so that it's split correctly across the two-byte ; sequence of A on the left and T3 on the right (and the ; byte on the left in A contains eight full bits of data ; rather than seven) LDX P+2 ; Set X to the bit number within the pixel byte that ; corresponds to the pixel at the start of the character ; (which we stored in P+2 above) ; ; This is the number of shifts we need to apply to the ; two-byte sequence A T3, shifting the bits as above, ; so we use this as a shift counter in the following .RRL2 CMP #128 ; If the bitmap byte in A is >= 128, set the C flag, ; otherwise clear it, so this sets the C flag to bit 7 ; of the 8-bit character bitmap in A ROL T3 ; Shift the result left into bit 0 of T3, which is the ; same shifting a pixel right into the leftmost pixel of ; the right pixel byte (as described above) DEX ; Decrement the shift counter in X BMI RR8 ; If we have performed all X shifts then jump to RR8 to ; stop shifting (but note that we have not shifted A to ; the left by this point) ASL A ; Shift the bitmap byte in A to the left, so we have now ; shifted the whole two-byte sequence JMP RRL2 ; Loop back for the next left shift .RR8 AND #%01111111 ; Clear bit 7 of the pixel byte to set the colour ; palette of the left pixel byte to 0 ; ; This works because we didn't perform the last shift in ; the above process, so bit 7 has already been moved ; into T3 and can be overwritten with the palette number ; We now have two bytes, in A and T3, that are suitable ; for poking into screen memory to draw this pixel row ; of the character on-screen CLC ; Clear the C flag for the addition below STY T2 ; Store the character bitmap pixel row in T2, so we can ; retrieve it below LDY T1 ; Set Y to the byte number within the pixel row that ; contains the start of the character, which we stored ; in T1 above EOR (SC),Y ; Draw the pixel pattern in A into the Y-th pixel byte STA (SC),Y ; on the correct pixel row, using EOR logic to merge the ; pattern with whatever is already on-screen ; ; So this draws the left pixel byte for this pixel row ; of the character INY ; Increment Y to point to the next pixel byte along to ; the right LDA T3 ; Set A to the pixel pattern for the second pixel byte EOR (SC),Y ; Draw the pixel pattern in A into the Y-th pixel byte STA (SC),Y ; on the correct pixel row, using EOR logic to merge the ; pattern with whatever is already on-screen ; ; And this draws the right pixel byte for this pixel row ; of the character LDY T2 ; Set Y to the number of the character bitmap pixel row, ; which we stored in T2 above ; We now need to move down into the pixel row below LDA SC+1 ; Add 4 to the high byte of SC(1 0), so this does the ADC #4 ; following: STA SC+1 ; ; SC(1 0) = SC(1 0) + $400 ; ; The addition works because we cleared the C flag above ; ; So this sets SC(1 0) to the address of the pixel row ; below the one we just drew in, as each pixel row ; within the character row is spaced out by $400 bytes ; in screen memory INY ; Increment Y to point to the next character bitmap ; pixel row, working down the character bitmap CPY #8 ; Loop back until we have drawn all eight pixel rows in BNE RRL1 ; the character RTS ; Return from the subroutineName: letter2 [Show more] Type: Subroutine Category: Text Summary: Draw a character or indicator bulb bitmap in the high-resolution screen modeContext: See this subroutine on its own page References: This subroutine is called as follows: * BULB calls letter2
Arguments: X The pixel x-coordinate of the character we want to draw YC The y-coordinate of the character as a text row number P(1 0) The address of the character definition to be drawn.TTX66K LDA QQ11 ; If this is the space view, jump to wantgrap to set up BEQ wantgrap ; the high-resolution graphics screen CMP #13 ; If QQ11 = 13 then this is either the title screen or BEQ wantgrap ; the rotating ship screen in the mission 1 briefing, so ; jump to wantgrap to set up the high-resolution ; graphics screen AND #%11000000 ; If either bit 6 or 7 of the view type is set - so BNE wantgrap ; this is either the Short-range or Long-range Chart - ; then jump to wantgrap to set up the high-resolution ; graphics screen JSR cleartext ; This is a text view, so clear screen memory for the ; text screen mode JMP TEXT ; Switch to the text screen mode, returning from the ; subroutine using a tail call .cleartext LDY #0 ; Set Y = 0 to use as a byte counter when clearing ; screen memory for the text mode LDX #4 ; Set X = 4 to use as a page counter when clearing the ; four pages of screen memory from $0400 to $0800 STY SC ; Set SC(1 0) = $0400, which is the address of screen STX SC+1 ; memory for bank 1 of the text screen mode LDA #160 ; Set A to 160, which is the ASCII for a space character ; with bit 7 set, which is a space character in normal ; video ; ; We set bit 7 so it will show as a black block on ; screen when we fill the text mode's screen memory with ; this value, as opposed to a white block if it were in ; inverse video .cleartextl STA (SC),Y ; Blank the Y-th byte of screen memory at SC(1 0) INY ; Increment the byte counter BNE cleartextl ; Loop back until we have cleared a whole page of text ; mode screen memory INC SC+1 ; Increment the high byte of SC(1 0) so it points to ; the next page in screen memory DEX ; Decrement the page counter BNE cleartextl ; Loop back until we have cleared four pages of memory RTS ; Return from the subroutine .wantgrap JSR cleargrap ; This is a high-resolution graphics view, so clear ; screen memory for the top part of the graphics screen ; mode (the space view) JSR BOX ; Call BOX to draw a border box along the top edge of ; the space view (the sides are retained from the ; loading screen, along with the dashboard) JSR HGR ; Switch to the high-resolution graphics screen mode RTS ; Return from the subroutine .BOX LDX #0 ; Set X1 = 0 STX X1 STX Y1 ; Set Y1 = 0 DEX ; Set X2 = 255 STX X2 LDA #BLUE ; Switch to colour blue STA COL JSR HLOIN ; Draw a horizontal line from (X1, Y1) to (X2, Y1) in ; blue, which will draw a line along the top edge of the ; screen from (0, 0) to (255, 0) LDA #%10101010 ; Draw the top-left corner of the box as a continuous STA SCBASE+1 ; line of blue LDA #%10101010 ; Draw the top-right corner of the box as a continuous STA SCBASE+37 ; line of blue RTS ; Return from the subroutineName: TTX66K [Show more] Type: Subroutine Category: Drawing the screen Summary: Clear the whole screen or just the space view (as appropriate), and draw a border box if requiredContext: See this subroutine on its own page References: This subroutine is called as follows: * TTX66 calls TTX66K * clss calls via cleartext
If this is a high-resolution graphics view, clear the top part of the screen and draw a border box. If this is a text view, clear the screen.
Other entry points: BOX Just draw the border box along the top of the screen (the sides are retained from the loading screen, along with the dashboard) cleartext Clear screen memory for the text screen mode.cleargrap LDY #16 ; Set a counter in Y to clear the 17 character rows of ; the space view .cleargl JSR clearrow ; Clear character row Y in screen memory, drawing blue ; borders along the left and right edges as we do so DEY ; Decrement the row counter BPL cleargl ; Loop back until we have cleared all 17 character rows INY ; Y is decremented to 255 by the loop, so this sets Y to ; zero STY XC ; Move the text cursor to column 0 on row 0 STY YC RTS ; Return from the subroutineName: cleargrap [Show more] Type: Subroutine Category: Drawing the screen Summary: Clear screen memory for the top part of the graphics screen mode (the space view), drawing blue borders along the sides as we do soContext: See this subroutine on its own page References: This subroutine is called as follows: * clss calls cleargrap * TTX66K calls cleargrap.ZES1k LDY #0 ; If we set Y = SC = 0 and fall through into ZESNEW STY SC ; below, then we will zero-fill 255 bytes starting from ; SC - in other words, we will zero-fill the whole of ; page XName: ZES1k [Show more] Type: Subroutine Category: Utility routines Summary: Zero-fill the page whose number is in XContext: See this subroutine on its own page References: No direct references to this subroutine in this source file
Arguments: X The page we want to zero-fill.ZES2k LDA #0 ; Load A with the byte we want to fill the memory block ; with - i.e. zero STX SC+1 ; We want to zero-fill page X, so store this in the ; high byte of SC, so the 16-bit address in SC and ; SC+1 is now pointing to the SC-th byte of page X .ZEL1k STA (SC),Y ; Zero the Y-th byte of the block pointed to by SC, ; so that's effectively the Y-th byte before SC DEY ; Decrement the loop counter BNE ZEL1k ; Loop back to zero the next byte RTS ; Return from the subroutineName: ZES2k [Show more] Type: Subroutine Category: Utility routines Summary: Zero-fill a specific pageContext: See this subroutine on its own page References: No direct references to this subroutine in this source file
Zero-fill from address (X SC) + Y to (X SC) + 1.
Arguments: Y The offset from (X SC) where we start zeroing, counting down to 1; if Y = 0, then the whole page is reset
Returns: Z flag Z flag is set A A is 0 Y Y is 0.ZESNEW LDA #0 ; Load A with the byte we want to fill the memory block ; with - i.e. zero .ZESNEWL STA (SC),Y ; Zero the Y-th byte of the block pointed to by SC INY ; Increment the loop counter BNE ZESNEWL ; Loop back to zero the next byte RTS ; Return from the subroutineName: ZESNEW [Show more] Type: Subroutine Category: Utility routines Summary: Zero-fill memory from SC(1 0) to the end of the pageContext: See this subroutine on its own page References: No direct references to this subroutine in this source file
Zero-fill from address SC(1 0) + Y to SC(1 0) + $FF.
Arguments: Y The offset from SC(1 0) where we start zeroing, counting up to $FF SC(1 0) The starting address of the zero-fill
Returns: Z flag Z flag is setIF _SOURCE_DISK_BUILD OR _SOURCE_DISK_ELT_FILES ;.SETXC ; These instructions are commented out in the original ; ; source ;STA XC ;JMP PUTBACK RTS ; Return from the subroutine ELIF _IB_DISK OR _4AM_CRACK OR _SOURCE_DISK_CODE_FILES .SETXC STA XC ; Store the new text column in XC ENDIF ;JMP PUTBACK ; This instruction is commented out in the original ; source RTS ; Return from the subroutineName: SETXC [Show more] Type: Subroutine Category: Text Summary: Move the text cursor to a specific columnContext: See this subroutine on its own page References: No direct references to this subroutine in this source file
Arguments: A The text columnIF _SOURCE_DISK_BUILD OR _SOURCE_DISK_ELT_FILES ;.SETYC ; These instructions are commented out in the original ; ; source ;STA YC ELIF _IB_DISK OR _4AM_CRACK OR _SOURCE_DISK_CODE_FILES .SETYC STA YC ; Store the new text row in YC ;JMP PUTBACK ; This instruction is commented out in the original ; source RTS ; Return from the subroutine ENDIFName: SETYC [Show more] Type: Subroutine Category: Text Summary: Move the text cursor to a specific rowContext: See this subroutine on its own page References: No direct references to this subroutine in this source file
Arguments: A The text row.mvblockK LDY #0 ; Set an index counter in Y .mvbllop LDA (V),Y ; Copy the Y-th byte from V(1 0) to SC(1 0) STA (SC),Y DEY ; Decrement the index counter BNE mvbllop ; Loop back until we have copied a whole page of bytes INC V+1 ; Increment the high bytes of V(1 0) and SC(1 0) to INC SC+1 ; point to the next page in memory DEX ; Decrement the page counter BNE mvbllop ; Loop back until we have copied X pages of memory RTS ; Return from the subroutineName: mvblockK [Show more] Type: Subroutine Category: Utility routines Summary: Copy a specific number of pages in memoryContext: See this subroutine on its own page References: No direct references to this subroutine in this source file
Arguments: V(1 0) Source address SC(1 0) Destination address X Number of pages of memory to copy
Other entry points: mvbllop Only copy Y bytes, rather than a whole page.CLYNS LDA #0 ; Set the delay in DLY to 0, to indicate that we are STA DLY ; no longer showing an in-flight message, so any new ; in-flight messages will be shown instantly STA de ; Clear de, the flag that appends " DESTROYED" to the ; end of the next text token, so that it doesn't .CLYNS2 JSR CLYS1 ; Call CLYS1 to move the text cursor to column 21 on ; row 1 LDA #%11111111 ; Set DTW2 = %11111111 to denote that we are not STA DTW2 ; currently printing a word LDA #%10000000 ; Set bit 7 of QQ17 to switch standard tokens to STA QQ17 ; Sentence Case LDA text ; If bit 7 of text is clear then the current screen mode BPL CLY1 ; is the high-resolution graphics mode, so jump to CLY1 ; clear two character rows on the graphics screen ; Otherwise this is the text screen, so we clear two ; character rows by printing 64 spaces (32 spaces per ; row LDA #' ' ; Set A to the space character, so we can pass it to ; CHPR to print a space LDX #64 ; Set a character counter in X so we print 64 spaces .CLYL1 JSR CHPR ; Print a space character DEX ; Decrement the character counter BNE CLYL1 ; Loop back until we have printed 64 spaces to blank ; out two character rows .CLYS1 LDA #21 ; Move the text cursor to column 21 on row 1 STA YC LDA #1 STA XC RTS ; Return from the subroutine .CLY1 LDY #15 ; Move the text cursor to column 15 on row 1 STY YC LDA #1 STA XC JSR clearrow ; Clear character row Y in screen memory, drawing blue ; borders along the left and right edges as we do so INY ; Increment Y to the next character row ; Fall through into clearrow to clear a second character ; row in screen memory, drawing blue borders along the ; left and right edges as we do soName: CLYNS [Show more] Type: Subroutine Category: Drawing the screen Summary: Clear two character rows near the bottom of the screenContext: See this subroutine on its own page References: This subroutine is called as follows: * dockEd calls CLYNS * EQSHP calls CLYNS * hm calls CLYNS * JMTB calls CLYNS * me2 calls CLYNS * MESS calls CLYNS * qv calls CLYNS * TT219 calls CLYNS
This routine clears some space at the bottom of the screen and moves the text cursor to column 1 on row 21 (for the space view) or row 15 (for the text views)..clearrow LDA #8 ; Set T2 = 8 to act as a pixel row counter STA T2 LDX SCTBL,Y ; Set the low byte of SC(1 0) to the Y-th entry from STX SC ; SCTBL, which contains the low byte of the address of ; the start of character row Y in screen memory LDX SCTBH,Y ; Set X to the Y-th entry from SCTBH, which contains ; the high byte of the address of the start of character ; row Y in screen memory TYA ; Store the number of the character row we are clearing PHA ; on the stack, so we can retrieve it below .cleargl2 STX SC+1 ; Set the high byte of SC(1 0) to X, so it contains the ; address of the start of the pixel row we want to clear LDA #%10100000 ; Set A to the pixel byte for the right end of the pixel ; row, containing a blue border in the second-to-last ; pixel (bit 7 is set to choose colour palette 1) LDY #37 ; We now clear the pixel row, starting from the right ; end of the line, so set a pixel byte counter in Y to ; count down from byte #37 to byte #1 .cleargl3 STA (SC),Y ; Set the Y-th pixel byte of the row to the pixel byte ; in A LDA #0 ; Set A = 0 so the last byte contains the border, but ; the rest of them are blank DEY ; Decrement the byte counter BNE cleargl3 ; Loop back until we have drawn bytes #37 to #1, leaving ; Y = 0 LDA #%11000000 ; Set A to the pixel byte for the left end of the pixel ; row, containing a blue border in the seventh pixel ; (bit 7 is set to choose colour palette 1) STA (SC),Y ; Draw the pixel byte for the left end of the pixel row ; in the first byte of the row at SC(1 0) INY ; Increment Y to point to the second pixel byte in the ; row ASL A ; Set A = %10000000, which is a pixel byte in colour ; palette 1 with no pixels set STA (SC),Y ; Draw this as the second pixel byte in the row to set ; the colour palette for the second pixel to palette 1, ; so the left edge is drawn correctly in blue INX ; Set X = X + 4, so the high byte of SC(1 0) gets INX ; increased by 4 when we loop back, which means we do INX ; the following: INX ; ; SC(1 0) = SC(1 0) + $400 ; ; So this sets SC(1 0) to the address of the pixel row ; below the one we just drew in, as each pixel row ; within the character row is spaced out by $400 bytes ; in screen memory DEC T2 ; Decrement the pixel row counter in T2 BNE cleargl2 ; Loop back until we have cleared all eight pixel rows ; in the character row PLA ; Restore the number of the character row into Y, so we TAY ; can return it unchanged ; Fall through into SCAN to return from the subroutine, ; as it starts with an RTSName: clearrow [Show more] Type: Subroutine Category: Drawing the screen Summary: Clear a character row of screen memory, drawing blue borders along the left and right edges as we do soContext: See this subroutine on its own page References: This subroutine is called as follows: * cleargrap calls clearrow * CLYNS calls clearrow
Arguments: Y The character row number
Returns: Y Y is preserved.SCR1 RTS ; Return from the subroutine .SCAN ;LDA QQ11 ; These instructions are commented out in the original ;BNE SCR1 ; source LDA INWK+31 ; Fetch the ship's scanner flag from byte #31 AND #%00010000 ; If bit 4 is clear then the ship should not be shown BEQ SCR1 ; on the scanner, so return from the subroutine (as SCR1 ; contains an RTS) LDX TYPE ; Fetch the ship's type from TYPE into X BMI SCR1 ; If this is the planet or the sun, then the type will ; have bit 7 set and we don't want to display it on the ; scanner, so return from the subroutine (as SCR1 ; contains an RTS) LDA scacol,X ; Set A to the scanner colour for this ship type from ; the X-th entry in the scacol table STA COL ; Store the scanner colour in COL so it can be used to ; draw this ship in the correct colour LDA INWK+1 ; If any of x_hi, y_hi and z_hi have a 1 in bit 6 or 7, ORA INWK+4 ; then the ship is too far away to be shown on the ORA INWK+7 ; scanner, so return from the subroutine (as SCR1 AND #%11000000 ; contains an RTS) BNE SCR1 ; If we get here, we know x_hi, y_hi and z_hi are all ; 63 (%00111111) or less ; Now, we convert the x_hi coordinate of the ship into ; the screen x-coordinate of the dot on the scanner, ; using the following: ; ; X1 = 123 + (x_sign x_hi) LDA INWK+1 ; Set A = x_hi CLC ; Clear the C flag so we can do addition below LDX INWK+2 ; Set X = x_sign BPL SC2 ; If x_sign is positive, skip the following EOR #%11111111 ; x_sign is negative, so flip the bits in A and add 1 ADC #1 ; to make it a negative number (bit 7 will now be set ; as we confirmed above that bits 6 and 7 are clear). So ; this gives A the sign of x_sign and gives it a value ; range of -63 (%11000001) to 0 CLC ; Clear the C flag so we can do addition below .SC2 ADC #125 ; Set X1 = 125 + (x_sign x_hi) AND #%11111110 ; STA X1 ; and if the result is odd, subtract 1 to make it even TAX ; Set X = X1 - 2 DEX ; DEX ; So X contains the x-coordinate that's two pixels to ; left of the top of the stick, so we can draw the dot ; at the end of the stick by simply drawing a dot at ; x-coordinate X at the correct end of the stick ; Next, we convert the z_hi coordinate of the ship into ; the y-coordinate of the base of the ship's stick, ; like this: ; ; SC = 220 - (z_sign z_hi) / 4 ; ; though the following code actually does it like this: ; ; SC = 255 - (35 + z_hi / 4) LDA INWK+7 ; Set A = z_hi / 4 LSR A ; LSR A ; So A is in the range 0-15 CLC ; Clear the C flag for the addition below LDY INWK+8 ; Set Y = z_sign BPL SC3 ; If z_sign is positive, skip the following EOR #%11111111 ; z_sign is negative, so flip the bits in A and set the SEC ; C flag. As above, this makes A negative, this time ; with a range of -16 (%11110000) to -1 (%11111111). And ; as we are about to do an ADC, the SEC effectively adds ; another 1 to that value, giving a range of -15 to 0 .SC3 ADC #91 ; Set A = 91 + A to give a number in the range 76 to 106 EOR #%11111111 ; Flip all the bits and store in Y2, so Y2 is in the STA Y2 ; range 149 to 179, with a higher z_hi giving a lower Y2 ; Now for the stick height, which we calculate using the ; following: ; ; A = - (y_sign y_hi) / 2 LDA INWK+4 ; Set A = y_hi / 2 LSR A CLC ; Clear the C flag LDY INWK+5 ; Set Y = y_sign BMI SCD6 ; If y_sign is negative, skip the following, as we ; already have a positive value in A EOR #%11111111 ; y_sign is positive, so flip the bits in A and set the SEC ; C flag. This makes A negative, and as we are about to ; do an ADC below, the SEC effectively adds another 1 to ; that value to implement two's complement negation, so ; we don't need to add another 1 here .SCD6 ; We now have all the information we need to draw this ; ship on the scanner, namely: ; ; X1 = the screen x-coordinate of the ship's dot ; ; A = the screen height of the ship's stick, with the ; correct sign for adding to the base of the stick ; to get the dot's y-coordinate ; ; First, though, we have to make sure the dot is inside ; the dashboard, by moving it if necessary ADC Y2 ; Set A = Y2 + A, so A now contains the y-coordinate of ; the end of the stick, plus the length of the stick, to ; give us the screen y-coordinate of the dot ;BPL ld246 ; This instruction is commented out in the original ; source CMP #146 ; If A >= 146, skip the following instruction, as 146 is BCS P%+4 ; the minimum allowed value of A LDA #146 ; A < 146, so set A to 146, the minimum allowed value ; for the y-coordinate of our ship's dot CMP #191 ; If A < 191, skip the following instruction, as 190 is BCC P%+4 ; the maximum allowed value of A .ld246 LDA #190 ; A >= 191, so set A to 190, the maximum allowed value ; for the y-coordinate of our ship's dot JSR CPIX ; Draw a single pixel at screen coordinate (X, A), at ; the start of the line, to draw the ship dot ; ; Because this is a colour pixel that is two pixels to ; the left of the top of the stick, this means the pixel ; byte will contains pixels of the form %101 (with the ; dot on the left and the stick on the right), so this ; will actually draw a two-pixel dash to the left of the ; top of the stick, rather than a lone colour pixel JMP VLOIN ; Draw a vertical line from (X1, Y1) to (X1, Y2) for the ; ship stick and return from the subroutine using a tail ; callName: SCAN [Show more] Type: Subroutine Category: Dashboard Summary: Display the current ship on the scanner Deep dive: The 3D scannerContext: See this subroutine on its own page References: This subroutine is called as follows: * ESCAPE calls SCAN * Main flight loop (Part 11 of 16) calls SCAN * MVEIT (Part 2 of 9) calls SCAN * MVEIT (Part 9 of 9) calls SCAN * WPSHPS calls SCAN
This is used both to display a ship on the scanner, and to erase it again.
Arguments: INWK The ship's data block.HGR LDA $C054 ; Select page 1 display (i.e. main screen memory) by ; reading the PAGE20FF soft switch LDA $C052 ; Configure graphics on the whole screen by reading the ; MIXEDOFF soft switch LDA $C057 ; Select high-resolution graphics by reading the HIRESON ; soft switch LDA $C050 ; Select the graphics mode by reading the TEXTOFF soft ; switch LSR text ; Clear bit 7 of text to indicate that we are now in the ; high-resolution graphics screen mode RTS ; Return from the subroutine.TEXT LDA $C054 ; Select page 1 display (i.e. main screen memory) by ; reading the PAGE20FF soft switch LDA $C051 ; Select the text mode by reading the TEXTON soft switch SEC ; Set bit 7 of text to indicate that we are now in the ROR text ; text screen mode RTS ; Return from the subroutineName: TEXT [Show more] Type: Subroutine Category: Drawing the screen Summary: Switch to the text screen modeContext: See this subroutine on its own page References: This subroutine is called as follows: * TTX66K calls TEXT.F% SKIP 0 IF _IB_DISK OR _4AM_CRACK EQUB $83, $6F ; These bytes appear to be unused EQUB $63, $6F EQUB $75 ENDIFName: F% [Show more] Type: Variable Category: Utility routines Summary: Denotes the end of the main game code, from ELITE A to ELITE KContext: See this variable on its own page References: No direct references to this variable in this source filePRINT "ELITE K" PRINT "Assembled at ", ~CODE_K% PRINT "Ends at ", ~P% PRINT "Code size is ", ~(P% - CODE_K%) PRINT "Execute at ", ~LOAD% PRINT "Reload at ", ~LOAD_K% PRINT "S.ELTK ", ~CODE_K%, " ", ~P%, " ", ~LOAD%, " ", ~LOAD_J% SAVE "3-assembled-output/ELTK.bin", CODE_K%, P%, LOAD% PRINT "Addresses for the scramble routines in elite-checksum.py" PRINT "B% = ", ~CODE% PRINT "G% = ", ~G% PRINT "NA2% = ", ~NA2%Save ELTK.bin
[X]
Subroutine BEEP (category: Sound)
Make a short, high beep
[X]
Configuration variable BLUE
Offset into the MASKT table for blue
[X]
Subroutine BULB (category: Dashboard)
Draw an indicator bulb on the dashboard
[X]
Subroutine CHPR (category: Text)
Print a character at the text cursor by poking into screen memory
[X]
Subroutine CPIX (category: Drawing pixels)
Draw a colour pixel
[X]
Label DOWN in subroutine LOIN (Part 4 of 7)
[X]
Variable DTW2 (category: Text)
A flag that indicates whether we are currently printing a word
[X]
Variable ECBT (category: Dashboard)
The character bitmap for the E.C.M. indicator bulb
[X]
Configuration variable FONT
The address of the game's text font
[X]
Subroutine HGR (category: Drawing the screen)
Switch to the high-resolution graphics screen mode
[X]
Entry point HL6 in subroutine LOIN (Part 7 of 7) (category: Drawing lines)
Contains an RTS
[X]
Subroutine HLOIN (category: Drawing lines)
Draw a horizontal line from (X1, Y1) to (X2, Y1)
[X]
Label LFT in subroutine LOIN (Part 7 of 7)
[X]
Label LI1 in subroutine LOIN (Part 1 of 7)
[X]
Label LI10 in subroutine LOIN (Part 4 of 7)
[X]
Label LI15 in subroutine LOIN (Part 5 of 7)
[X]
Label LI16 in subroutine LOIN (Part 6 of 7)
[X]
Label LI17 in subroutine LOIN (Part 6 of 7)
[X]
Label LI18 in subroutine LOIN (Part 7 of 7)
[X]
Label LI19 in subroutine LOIN (Part 7 of 7)
[X]
Label LI2 in subroutine LOIN (Part 1 of 7)
[X]
Label LI20 in subroutine LOIN (Part 3 of 7)
[X]
Label LI21 in subroutine LOIN (Part 4 of 7)
[X]
Label LI22 in subroutine LOIN (Part 6 of 7)
[X]
Label LI23 in subroutine LOIN (Part 7 of 7)
[X]
Label LI3 in subroutine LOIN (Part 2 of 7)
[X]
Label LI6 in subroutine LOIN (Part 3 of 7)
[X]
Label LI7 in subroutine LOIN (Part 3 of 7)
[X]
Label LI9 in subroutine LOIN (Part 4 of 7)
[X]
Label LIC2 in subroutine LOIN (Part 3 of 7)
[X]
Label LIC3 in subroutine LOIN (Part 4 of 7)
[X]
Label LIC5 in subroutine LOIN (Part 6 of 7)
[X]
Label LIC6 in subroutine LOIN (Part 7 of 7)
[X]
Label LIL2 in subroutine LOIN (Part 3 of 7)
[X]
Label LIL3 in subroutine LOIN (Part 4 of 7)
[X]
Label LIL5 in subroutine LOIN (Part 6 of 7)
[X]
Label LIL6 in subroutine LOIN (Part 7 of 7)
[X]
Label LIfudge in subroutine LOIN (Part 5 of 7)
[X]
Label LIlog2 in subroutine LOIN (Part 5 of 7)
[X]
Label LIlog6 in subroutine LOIN (Part 2 of 7)
[X]
Label LIlog7 in subroutine LOIN (Part 2 of 7)
[X]
Variable MASKT (category: Drawing lines)
High-resolution pixel bytes for drawing continuous lines of solid colour
[X]
Subroutine MSBARS (category: Dashboard)
Draw two horizontal lines as part of an indicator bar, from (X1, Y1+1) to (X2, Y1+1) and (X1, Y1+2) to (X2, Y1+2)
[X]
Subroutine R5 (category: Text)
Make a beep and jump back into the character-printing routine at CHPR
[X]
Subroutine RR5 (category: Text)
Print a character in the text screen mode
[X]
Configuration variable SCBASE
The address of screen memory
[X]
Variable SCTBH (category: Drawing the screen)
Lookup table for converting a character row number to the address of the top pixel line in that character row (high byte)
[X]
Variable SCTBH2 (category: Drawing the screen)
Lookup table for converting a character row number to the address of the bottom pixel line in that character row (high byte)
[X]
Variable SCTBL (category: Drawing the screen)
Lookup table for converting a character row number to the address of the top or bottom pixel line in that character row (low byte)
[X]
Variable SCTBX1 (category: Drawing the screen)
Lookup table for converting a pixel x-coordinate to the bit number within the pixel row byte that corresponds to this pixel
[X]
Variable SCTBX2 (category: Drawing the screen)
Lookup table for converting a pixel x-coordinate to the byte number in the pixel row that corresponds to this pixel
[X]
Variable SPBT (category: Dashboard)
The bitmap definition for the space station indicator bulb
[X]
Label STPX in subroutine LOIN (Part 2 of 7)
[X]
Label STPY in subroutine LOIN (Part 5 of 7)
[X]
Subroutine TEXT (category: Drawing the screen)
Switch to the text screen mode
[X]
Variable TWFL (category: Drawing lines)
Ready-made pixel bytes for the left end of a horizontal line
[X]
Variable TWFR (category: Drawing lines)
Ready-made character rows for the right end of a horizontal line in the space view
[X]
Variable TWOS (category: Drawing pixels)
Ready-made bytes for drawing one-pixel dots in the space view
[X]
Variable TWOS2 (category: Drawing pixels)
Ready-made two-bit pixel bytes for plotting colour pixels
[X]
Variable UPTOG in workspace Option variables
Upper case configuration setting
[X]
Subroutine VLOIN (category: Drawing lines)
Draw a vertical line from (X1, Y1) to (X1, Y2)
[X]
Subroutine WSCAN (category: Drawing the screen)
Wait for 15 * 256 loop iterations
[X]
Variable alogh (category: Maths (Arithmetic))
Binary antilogarithm table
[X]
Variable cellocl (category: Drawing the screen)
Lookup table for converting a text row number to the address of that row in text screen memory (low byte)
[X]
Subroutine cleargrap (category: Drawing the screen)
Clear screen memory for the top part of the graphics screen mode (the space view), drawing blue borders along the sides as we do so
[X]
Subroutine clearrow (category: Drawing the screen)
Clear a character row of screen memory, drawing blue borders along the left and right edges as we do so
[X]
Label cleartextl in subroutine TTX66K
[X]
Subroutine clss (category: Drawing the screen)
Clear the screen, move the text cursor to the top-left corner and jump back into the CHPR routine to print the next character
[X]
Subroutine letter (category: Text)
Draw a character in the high-resolution screen mode using the game font
[X]
Subroutine letter2 (category: Text)
Draw a character or indicator bulb bitmap in the high-resolution screen mode
[X]
Variable log (category: Maths (Arithmetic))
Binary logarithm table (high byte)
[X]
Variable logL (category: Maths (Arithmetic))
Binary logarithm table (low byte)
[X]
Variable mscol in workspace Option variables
Current missile indicator colours
[X]
Variable msloc (category: Dashboard)
The screen x-coordinates of the four missile indicators on the dashboard
[X]
Variable scacol (category: Drawing ships)
Ship colours on the scanner
[X]
Label whosentthisshit in subroutine CHPR2