Skip to navigation


Aviator A source

AVIATOR MAIN GAME CODE Produces the binary file AVIA.bin that contains the main game code.
CLEAR &0B00, &0D00 \ Clear the guard on the &0B00-&0CFF workspace so we can \ assemble the loader code into this address range ORG CODE%
Name: SetupScreen [Show more] Type: Subroutine Category: Setup Summary: Set up the screen mode and load the dashboard image
Context: See this subroutine on its own page References: This subroutine is called as follows: * Entry calls SetupScreen
.SetupScreen LDA #22 \ Switch to screen mode 5 with the following VDU JSR OSWRCH \ command: LDA #5 \ JSR OSWRCH \ VDU 22, 5 LDY #0 \ We now want to write the VDU command to disable the \ cursor, whose bytes are in the variable at \ disableCursor, so set up a counter in Y .sscr1 LDA disableCursor,Y \ Write the Y-th value from disableCursor JSR OSWRCH INY \ Increment the loop counter CPY #10 \ Loop back to write the next character until we have BNE sscr1 \ written all 10, in other words: \ \ VDU 23, 0, 10, 23, 0, 0, 0, 0, 0, 0 LDA #31 \ Move the text cursor to column 4, row 10 with the JSR OSWRCH \ following VDU command: LDA #4 \ JSR OSWRCH \ VDU 31, 4, 10 LDA #10 JSR OSWRCH LDY #0 \ We now want to print the "Please wait" message in \ variable pleaseWaitText, so set up a counter in Y .sscr2 LDA pleaseWaitText,Y \ Print the Y-th character from pleaseWaitText JSR OSWRCH INY \ Increment the loop counter CPY #11 \ Loop back to print the next character until we have BNE sscr2 \ printed all 11 ("Please wait") LDX #LO(loadDashboard) \ Set (Y X) to point to loadDashboard ("L.DASHBD 7100") LDY #HI(loadDashboard) JSR OSCLI \ Call OSCLI to run the OS command in loadDashboard, \ which loads the dashboard image into the screen LDA #129 \ Call OSBYTE with A = 129, X = &FF and Y = 0 to scan LDX #&FF \ the keyboard for &FF centiseconds (2.56 seconds) LDY #0 JSR OSBYTE JMP DrawCanopy \ Jump down to DrawCanopy to continue the setup process
Name: loadDashboard [Show more] Type: Variable Category: Setup Summary: The OS command string for loading the dashboard image
Context: See this variable on its own page References: This variable is used as follows: * SetupScreen uses loadDashboard
.loadDashboard EQUS "L.DASHBD 7100" \ This is short for "*LOAD DASHBD 7100" EQUB 13
Name: disableCursor [Show more] Type: Variable Category: Setup Summary: The VDU command for disabling the cursor
Context: See this variable on its own page References: This variable is used as follows: * SetupScreen uses disableCursor
.disableCursor EQUB 23, 0, 10, 23 \ Set 6845 register R10 = 23 EQUB 0, 0, 0 \ EQUB 0, 0, 0 \ This is the "cursor start" register, so this sets the \ cursor start line at 23, effectively disabling the \ cursor
Name: pleaseWaitText [Show more] Type: Variable Category: Setup Summary: The "Please wait" message shown when the game loads
Context: See this variable on its own page References: This variable is used as follows: * SetupScreen uses pleaseWaitText
.pleaseWaitText EQUS "Please wait" EQUB 13
Name: DrawCanopy [Show more] Type: Subroutine Category: Setup Summary: Move code around, clear the edges of the canopy view, draw the canopy edges and rivets, and jump to the main code
Context: See this subroutine on its own page References: This subroutine is called as follows: * SetupScreen calls DrawCanopy
.DrawCanopy LDA #140 \ Call OSBYTE with A = 140 to select the tape filing JSR OSBYTE \ system (i.e. do a *TAPE command) LDY #0 \ We now copy the following block in memory: \ \ * &0400-&07FF is copied to &0D00-&10FF \ \ so we set up a byte counter in Y \ \ Note that this is the same block that was copied from \ &5800-&5BFF by the Entry routine, so in all we end up \ moving code as follows: \ \ * &5800-&5BFF is copied to &0400-&07FF \ then to &0D00-&10FF \ * &5C00-&5DFF is copied to &0B00-&0CFF \ \ As the rest of the main code file runs from &1100 to \ &57FF, this gives us a continuous block of code from \ &0B00 to &57FF, followed by screen memory at &5800 to \ &7FFF .dcan1 LDA &0400,Y \ Copy the Y-th byte of &0400 to the Y-th byte of &0D00 STA &0D00,Y LDA &0500,Y \ Copy the Y-th byte of &0500 to the Y-th byte of &0E00 STA &0E00,Y LDA &0600,Y \ Copy the Y-th byte of &0600 to the Y-th byte of &0F00 STA &0F00,Y LDA &0700,Y \ Copy the Y-th byte of &0700 to the Y-th byte of &1000 STA &1000,Y DEY \ Decrement the loop counter BNE dcan1 \ Loop back until we have copied a whole page of bytes NOP \ Presumably this contained some kind of copy protection NOP \ or decryption code that has been replaced by NOPs in NOP \ this unprotected version of the game NOP \ The following two calls to ClearRows clear the first \ two character rows on-screen LDA #&58 \ Set (Q P) = &5800, so the call to ClearRows starts STA Q \ clearing from the start of the first character row LDA #0 \ (i.e. row 0) STA P LDA #2 \ Set R = 2, so we clear 2 character rows STA R LDY #0 \ Set Y = 0, so we clear 256 bytes per row, or 32 of the \ 40 character blocks in each screen row (128 pixels) JSR ClearRows \ Call ClearRows to clear the first 256 bytes of the \ first two character rows on-screen, which blanks out \ the first 32 character blocks (blocks 0 to 31) of the \ top two character rows (rows 0 and 1) LDA #&58 \ Set (Q P) = &58FF, so the call to ClearRows starts STA Q \ clearing from character block 32 in the first LDA #&FF \ character row on-screen STA P LDA #2 \ Set R = 2, so we clear 2 character rows STA R LDY #64 \ Set Y = 64, so we clear 64 bytes per row, or 8 of the \ 40 character blocks in each screen row (32 pixels) JSR ClearRows \ Call ClearRows to clear the last 64 bytes of the first \ two character rows on-screen, which blanks out the \ last 8 character blocks (blocks 32 to 39) of the top \ two character rows (rows 0 and 1) \ The following two calls to ClearRows clear a \ 4-pixel-wide column on the left and right edges of the \ screen LDA #&5A \ Set (Q P) = &5A7F, so the call to ClearRows starts STA Q \ clearing from the start of the third character row LDA #&7F \ (i.e. row 2) STA P LDA #18 \ Set R = 18, so we clear 18 character rows STA R LDY #8 \ Set Y = 8, so we clear 8 bytes per row, or 1 of the \ 40 character blocks in each screen row (4 pixels) JSR ClearRows \ Call ClearRows to clear the first byte of character \ rows 2 to 20, which blanks out the first character \ block (block 0) on all 18 rows, i.e. the first four \ pixels LDA #&5B \ Set (Q P) = &5BB7, so the call to ClearRows starts STA Q \ clearing from the start of the last character block LDA #&B7 \ on the third character row (i.e. row 2) STA P LDA #18 \ Set R = 18, so we clear 18 character rows STA R LDY #8 \ Set Y = 8, so we clear 8 bytes per row, or 1 of the \ 40 character blocks in each screen row (4 pixels) JSR ClearRows \ Call ClearRows to clear the last byte of character \ rows 2 to 20, which blanks out the last character \ block (block 39) on all 18 rows, i.e. the last four \ pixels \ We now draw the edges of the canopy view, starting \ with the left edge, then the right edge, and then the \ horizontal line along the top (so this does not \ include the bottom edge, which has already been \ displayed as part of the dashboard image, and it also \ doesn't include the diagonal top corners, which are \ drawn by the main game code) LDX #3 \ Move the graphics cursor to (3, 96) LDY #96 JSR VduMove LDX #3 \ Draw a line to (3, 239) LDY #239 JSR VduDraw LDX #156 \ Move the graphics cursor to (156, 96) LDY #96 JSR VduMove LDX #156 \ Draw a line to (156, 239) LDY #239 JSR VduDraw LDX #8 \ Move the graphics cursor to (8, 248) LDY #248 JSR VduMove LDX #151 \ Draw a line to (151, 248) LDY #248 JSR VduDraw \ We now draw the square rivets around the edge of the \ canopy view, starting with the three rivets up each \ side of the screen LDY #121 \ Set Y = 121 so the first rivets are drawn at height \ 121, i.e. at (0, 121) and (158, 121) .dcan2 LDX #0 \ Draw a square rivet at (0, Y) JSR DrawRivet LDX #158 \ Draw a square rivet at (158, Y) JSR DrawRivet TYA \ Set Y = Y + 48 CLC \ ADC #48 \ so the next rivet we draw will be 48 pixels higher TAY CPY #9 \ Loop back to draw the next rivet until Y = 9, at which BNE dcan2 \ point Y has wrapped round off the top of the screen \ back to the bottom and we will have drawn three rivets \ up each side of the canopy view \ Finally, we draw the six square rivets along the top \ of the canopy view LDY #255 \ Set X and Y so the first rivet is at (19, 255) LDX #19 .dcan3 JSR DrawRivet \ Draw a square rivet at (X, Y) TXA \ Set X = X + 24 CLC \ ADC #24 \ so the next rivet we draw will be 24 pixels to the TAX \ right CPX #163 \ Loop back to draw the next rivet until X = 163, at BNE dcan3 \ which point we will have drawn six rivets along the \ top of the canopy view JMP StartGame \ Jump to StartGame to start the game
Name: VduPoint [Show more] Type: Subroutine Category: Graphics Summary: Draw a point on-screen using the standard VDU routines
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawRivet calls VduPoint

Arguments: X The x-coordinate of the point (0-159) Y The y-coordinate of the point (0-255)
.VduPoint LDA #69 \ Set A = 69 so the following VDU 25 command plots a \ point at an absolute position on-screen BNE VduPlot \ Jump to VduPlot to do the move (this BNE is \ effectively a JMP as A is never zero
Name: VduMove [Show more] Type: Subroutine Category: Graphics Summary: Move the graphics cursor using the standard VDU routines
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawCanopy calls VduMove

Arguments: X The x-coordinate to move the graphics cursor to (0-159) Y The y-coordinate to move the graphics cursor to (0-255)
.VduMove LDA #4 \ Set A = 4 so the following VDU 25 command moves the \ graphics cursor an absolute position on-screen BNE VduPlot \ Jump to VduPlot to do the move (this BNE is \ effectively a JMP as A is never zero
Name: VduDraw [Show more] Type: Subroutine Category: Graphics Summary: Draw a line using the standard VDU routines
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawCanopy calls VduDraw

Arguments: X The x-coordinate of the end of the line (0-159) Y The y-coordinate of the end of the line (0-255)
.VduDraw LDA #5 \ Set A = 5 to denote "draw line absolute in the current \ graphics foreground colour"
Name: VduPlot [Show more] Type: Subroutine Category: Graphics Summary: Perform a plot command using the standard VDU routines
Context: See this subroutine on its own page References: This subroutine is called as follows: * VduMove calls VduPlot * VduPoint calls VduPlot

Arguments: A The type of plot command: * 4 = Move cursor to coordinate * 5 = Draw line from cursor to coordinate * 69 = Draw point at coordinate X The x-coordinate for the plot command (0-159) Y The y-coordinate for the plot command (0-255)
.VduPlot PHA \ Store the value of A on the stack, so we can retrieve \ it after the call to OSWRCH LDA #25 \ Start a VDU 25 command, which is the equivalent of a JSR OSWRCH \ PLOT command in BBC BASIC, and which has the following \ format: \ \ VDU 25, K, X; Y; \ \ where K is the plotting mode (i.e. move or draw) and \ X and Y are the relevant coordinates as 16-bit numbers PLA \ Retrieve the value of A that we stored on the stack JSR OSWRCH \ Write the K parameter of the VDU command, i.e. the \ type of plot command LDA #0 \ Set P = 0 STA P TXA \ Set A = X, so now (P A) contains the x-coordinate as a \ 16-bit number ASL A \ Set (P A) = (P A) * 8 ROL P \ = x-coordinate * 8 ASL A \ ROL P \ This gives the screen location of the x-coordinate in ASL A \ terms of VDU coordinates, where the screen is always ROL P \ 1280 pixels wide, which is 8 times the number of \ pixels in 160-pixel-wide mode 5 (i.e. 8 * 160 = 1280) JSR OSWRCH \ Write (P A), writing the low byte in A first LDA P \ And then the high byte in P JSR OSWRCH LDA #0 \ Set P = 0 STA P TYA \ Set A = Y, so now (P A) contains the y-coordinate as a \ 16-bit number ASL A \ Set (P A) = (P A) * 4 ROL P \ = y-coordinate * 4 ASL A \ ROL P \ This gives the screen location of the y-coordinate in \ terms of VDU coordinates, where the screen is always \ 1024 pixels high, which is 4 times the number of \ pixels in 256-pixel-high mode 5 (i.e. 4 * 256 = 1024) JSR OSWRCH \ Write (P A), writing the low byte in A first LDA P \ And then the high byte in P JSR OSWRCH RTS \ Return from the subroutine
Name: DrawRivet [Show more] Type: Subroutine Category: Graphics Summary: Draw a square rivet (2 pixels across, 4 pixels high)
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawCanopy calls DrawRivet

Arguments: X The x-coordinate of the top-left corner of the rivet Y The y-coordinate of the top-left corner of the rivet
.DrawRivet JSR VduPoint \ Draw a point at (X, Y) DEY \ Draw a point at (X, Y - 1) JSR VduPoint DEY \ Draw a point at (X, Y - 2) JSR VduPoint DEY \ Draw a point at (X, Y - 3) JSR VduPoint INX \ Draw a point at (X + 1, Y - 3) JSR VduPoint INY \ Draw a point at (X + 1, Y - 2) JSR VduPoint INY \ Draw a point at (X + 1, Y - 1) JSR VduPoint INY \ Draw a point at (X + 1, Y) JSR VduPoint DEX \ Restore X to its original value RTS \ Return from the subroutine
Name: ClearRows [Show more] Type: Subroutine Category: Graphics Summary: Clear the specified number of rows on-screen
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawCanopy calls ClearRows

This routine zeroes a block of Y bytes on R screen rows, starting from screen address (Q P) on the first row. A value of Y = 0 will zero 256 bytes. In other words, (Q P) represents the top-left pixel to blank, Y represents the width of the area to blank (with a value of 8 per character block), and R contains the number of rows to blank.
Arguments: Y The width of each character row to zero (in bytes), 0 indicates 256 bytes (Q P) The screen address to start zeroing from R The number of character rows to zero
.ClearRows STY S \ Store the width of each character row in S .crow1 LDA #0 \ We are about to zero a block of memory, so set A = 0 \ so we can use it as our overwrite value LDY S \ Fetch the width of each character row, which we stored \ in S above .crow2 STA (P),Y \ Zero the Y-th byte of the page at (Q P), which sets 4 \ pixels to black DEY \ Decrement the byte pointer BNE crow2 \ Loop back until we have zeroed (Q P) to (Q P) + Y LDA P \ Set (Q P) = (Q P) + 320 CLC \ ADC #LO(320) \ starting with the low bytes STA P LDA Q \ And then the high bytes ADC #HI(320) STA Q DEC R \ Decrement the row counter in R BNE crow1 \ Loop back until we have zeroed R rows RTS \ Return from the subroutine SKIP 30 \ These bytes appear to be unused EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF EQUB &FF, &FF SKIP 32 EQUB &40
Name: ProjectPoint (Part 1 of 3) [Show more] Type: Subroutine Category: 3D geometry Summary: Project a point onto the screen (i.e. convert from 3D coordinates to screen coordinates)
Context: See this subroutine on its own page References: This subroutine is called as follows: * ProcessLinesToShow calls ProjectPoint

This routine projects a point in 3D space onto the screen. If the point in 3D space is (xPoint, yPoint, zPoint), then the resulting projection is at pixel coordinate (x, y) on-screen, where: x = 80 + 256 * (xPoint / zPoint) y = 96 + 256 * (2 * yPoint / zPoint) The result is stored in (xPoint, yPoint), while zPoint is left alone so we can check its sign. The coordinates are all signed 16-bit values. Also, various bits are set in the point's status byte. These describe the relationship between the x- and y-coordinates of the point, which we can use to quickly determine whether the line containing this point is on-screen. We also set a bit that records that we have projected this point, so we don't repeat the process during this iteration of the main loop. The routine breaks down as follows: * Part 1 sets the various bits in the point's status byte * Part 2 calculates the divisions by zPoint * Part 3 calculates the addition of (80, 96)
Arguments: GG The point ID to process xPoint+GG The point's x-coordinate in 3D space yPoint+GG The point's y-coordinate in 3D space zPoint+GG The point's z-coordinate in 3D space
Returns: pointStatus+GG The following bits are set as follows, so they can be used in ProcessLinesToShow to determine the visibility of the line containing this point: * Bit 0: * 0 = we have not projected this point before * 1 = we have already projected this point * Bit 2: * 0 = yPoint is positive * 1 = yPoint is negative * Bit 3: * 0 = xPoint is positive * 1 = xPoint is negative * Bit 4: * 0 = |yPoint| * 2 < |zPoint| * 1 = |yPoint| * 2 >= |zPoint| * Bit 5: * 0 = |xPoint| < |zPoint| * 1 = |xPoint| >= |zPoint| xPoint+GG The pixel x-coordinate of the point, projected onto the screen yPoint+GG The pixel y-coordinate of the point, projected onto the screen
.ProjectPoint LDX GG \ Set X to the point ID to process LDA #0 \ Set N = 0, which we use for collecting set bits to STA N \ apply to the point's status byte LDA #%00010000 \ Set bit 4 of R, for use in ScaleUp STA R LDA pointStatus,X \ Set Y = A = the status byte for the point to process TAY AND #1 \ If bit 0 of the status byte is clear, then we have not BEQ proj1 \ yet projected this point onto the screen, so jump to \ proj1 to skip the following instruction and move on \ to the projection process RTS \ Bit 0 of the point's status byte is set, which means \ we have already projected this point, so return from \ the subroutine .proj1 TYA \ Set bit 0 of the point's status byte in pointStatus ORA #1 \ to indicate that we have now called ProjectPoint for STA pointStatus,X \ this point, i.e. the point has been projected (or soon \ will be, anyway) \ The first step is to set the four bits in the point's \ status byte, which we will return from the subroutine \ to be used in ProcessLinesToShow for determining the \ visibility of the line containing this point \ We first set (Q P) = |zPoint| \ \ bumping (Q P) up to 1 if zPoint = 0, so (Q P) has a \ minimum value of 1 LDA zPointHi,X \ Set A = the high byte of the point's z-coordinate BMI proj4 \ If the z-coordinate is negative, jump to proj4 to \ change its sign STA Q \ Set Q = the high byte of the point's z-coordinate BEQ proj2 \ If the high byte of the z-coordinate is 0, jump to \ proj2 LDA zPointLo,X \ Set P = the low byte of the point's z-coordinate, so STA P \ (Q P) now contains the point's z-coordinate, as \ required JMP proj5 \ Jump to proj5 to move on to the point's x-coordinate .proj2 \ If we get here then the high byte of the z-coordinate \ is 0 LDA zPointLo,X \ Set A = the low byte of the point's z-coordinate BNE proj3 \ If the low byte of the point's z-coordinate is \ non-zero, skip the following instructions \ If we get here then both the high and low bytes of the \ z-coordinate are 0, so zPoint = 0 LDA #1 \ Set A = 1 to use as the low byte of (Q P), so we will \ end up with (Q P) = 1, as required .proj3 STA P \ Set P = the low byte of the point's z-coordinate, or \ 1 if the low byte was zero, so (Q P) now contains \ |zPoint|, with a minimum value of 1, as required JMP proj5 \ Jump to proj5 to move on to the point's x-coordinate .proj4 \ If we get here then the high byte of the z-coordinate \ is negative, so now we negate it to make it positive LDA #0 \ Negate zPoint and store it in (Q P), starting with the SEC \ low bytes SBC zPointLo,X STA P LDA #0 \ And then the high bytes, so (Q P) now contains the SBC zPointHi,X \ point's z-coordinate made positive, i.e. |zPoint|, STA Q \ as required .proj5 \ We now set (QQ PP) = |xPoint| LDA xPointHi,X \ Set A = the high byte of the point's x-coordinate BMI proj6 \ If the x-coordinate is negative, jump to proj6 to \ change its sign STA QQ \ Set QQ = the high byte of the point's x-coordinate LDA xPointLo,X \ Set PP = the low byte of the point's x-coordinate, so STA PP \ (QQ PP) now contains |xPoint|, as required JMP proj7 \ Jump to proj7 to move on to the point's y-coordinate .proj6 \ If we get here then the high byte of the x-coordinate \ is negative, so now we negate it to make it positive LDA #0 \ Negate xPoint and store it in (QQ PP), starting with SEC \ the low bytes SBC xPointLo,X STA PP LDA #0 \ And then the high bytes, so (QQ PP) now contains the SBC xPointHi,X \ point's x-coordinate made positive, i.e. |xPoint|, STA QQ \ as required LDA N \ Set bit 3 of N (so we set bit 3 of the point's status ORA #%00001000 \ byte when we're done) to indicate that xPoint is STA N \ negative .proj7 \ We now set (SS RR) = |yPoint| * 2 LDA yPointHi,X \ Set A = the high byte of the point's y-coordinate BMI proj8 \ If the y-coordinate is negative, jump to proj8 to \ change its sign STA SS \ Set SS = the high byte of the point's y-coordinate LDA yPointLo,X \ Set A = the high byte of the point's y-coordinate, so \ we now have: \ \ (SS A) = |yPoint| ASL A \ Set (SS RR) = (SS A) * 2 ROL SS \ = |yPoint| * 2 STA RR JMP proj9 \ Jump to proj9 to move on to the next stage .proj8 \ If we get here then the high byte of the y-coordinate \ is negative, so now we negate it to make it positive \ before multiplying by 2 LDA #0 \ Negate yPoint and store it in (A RR), starting with SEC \ the low bytes SBC yPointLo,X STA RR LDA #0 \ And then the high bytes, so (A RR) now contains the SBC yPointHi,X \ point's y-coordinate made positive, so \ we now have: \ \ (A RR) = |yPoint| ASL RR \ Set (SS RR) = (A RR) * 2 ROL A \ = |yPoint| * 2 STA SS LDA N \ Set bit 2 of N (so we set bit 2 of the point's status ORA #%00000100 \ byte when we're done) to indicate that yPoint is STA N \ negative .proj9 \ We now set bit 5 of the status byte in N by comparing \ the |xPoint| and |zPoint| values we just calculated LDA QQ \ If QQ < Q, jump to proj11 to leave bit 5 clear CMP Q BCC proj11 BNE proj10 \ If QQ > Q, jump to proj10 to set bit 5 \ If we get here then QQ = Q LDA PP \ If PP < P, jump to proj11 to leave bit 5 clear CMP P BCC proj11 .proj10 \ If we get here then QQ = Q and PP >= P, which means \ (QQ PP) >= (Q P), i.e. |xPoint| >= |zPoint| LDA #%00100000 \ Set bit 5 of N (so we set bit 5 of the point's status ORA N \ byte when we're done) to indicate that STA N \ |xPoint| >= |zPoint| .proj11 \ We now set bit 4 of the status byte in N by comparing \ the |yPoint| and |zPoint| values we just calculated LDA SS \ If SS < Q, jump to proj13 to leave bit 4 clear CMP Q BCC proj13 BNE proj12 \ If SS > Q, jump to proj12 to set bit 4 \ If we get here then SS = Q LDA RR \ If RR < P, jump to proj13 to leave bit 4 clear CMP P BCC proj13 .proj12 \ If we get here then SS = Q and RR >= P, which means \ (SS RR) >= (Q P), i.e. |yPoint| * 2 >= |zPoint| LDA N \ Set bit 4 of N (so we set bit 4 of the point's status ORA #%00010000 \ byte when we're done) to indicate that STA N \ |yPoint| * 2 >= |zPoint|
Name: ProjectPoint (Part 2 of 3) [Show more] Type: Subroutine Category: 3D geometry Summary: Calculate the screen coordinates of the projected point
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.proj13 \ By this point, we have the following: \ \ (QQ PP) = |xPoint| \ (SS RR) = |yPoint| * 2 \ (Q P) = |zPoint| \ \ with (Q P) set to a minimum value of 1 LDY P \ Set (X Y) = (Q P) LDX Q \ = |zPoint| \ \ so we call ScaleUp with a non-zero (X Y), as we know \ (Q P) is at least 1 JSR ScaleUp \ Set (A Y) = (X Y), scaled up until it doesn't fit into \ 16 bits any more, and set WW to the minimum number of \ bits in the original number TAX \ Set (TT S) = the A-th entry from the division table LDA divisionHi,X \ with bits 0 to 2 cleared (as they contain unrelated STA TT \ data) LDA divisionLo,X AND #%11111000 STA S STY K \ Set K = Y, the low byte of the result from ScaleUp LDA WW \ Set UU = WW STA UU LDY PP \ Set (X Y) = (QQ PP) LDX QQ \ = |xPoint| JSR DivideScaled \ Set (Q P) = (X Y) divided by (TT S) \ = |xPoint| / |zPoint| \ \ And set WW to the scale factor of the result LDA Q \ Set (QQ PP) = (Q P) STA QQ LDA P STA PP LDA WW \ Set VV = WW STA VV LDY RR \ Set (X Y) = (SS RR) LDX SS \ = |yPoint| * 2 JSR DivideScaled \ Set (Q P) = (X Y) divided by (TT S) \ = |yPoint| * 2 / |zPoint| \ \ And set WW to the scale factor of the result JSR ScaleDown \ Scale (Q P) and (QQ PP) down by the correct amounts in \ UU, VC and WW to give screen coordinates in (SS QQ) \ and (RR Q)
Name: ProjectPoint (Part 3 of 3) [Show more] Type: Subroutine Category: 3D geometry Summary: Move the projected coordinates to the centre of the screen and update the point's status byte
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
\ By this point we have the following projected \ coordinates for the point whose ID is in GG: \ \ (SS QQ) = screen x-coordinate of 3D point projected \ onto the screen \ \ (RR Q) = screen y-coordinate of 3D point projected \ onto the screen \ \ We now move the projected coordinate to the correct \ place on screen, as the projected coordinates have the \ origin straight ahead of us, i.e. in the centre of the \ screen, while the screen has the origin in the \ bottom-left corner \ \ In other words, we add (80, 96) to the projected \ coordinates so that a projected coordinate of (0, 0), \ which is straight ahead of us, will appear in the \ centre of the canopy view, which is 160 x 192 pixels \ in size \ \ We also set the correct sign for the projected \ coordinate LDX GG \ Set X to the point ID to process LDA zPointHi,X \ If the point's z-coordinate is negative, jump to BMI proj14 \ proj14 LDA xPointHi,X \ If the point's x-coordinate is positive, jump to BPL proj16 \ proj16 as the coordinates have the same sign JMP proj15 \ Otherwise jump to proj15 as the coordinates have \ opposite signs .proj14 \ If we get here then the point's z-coordinate is \ negative LDA xPointHi,X \ If the point's x-coordinate is negative, jump to BMI proj16 \ proj16 as the coordinates have the same sign .proj15 \ If we get here then either the point's z-coordinate is \ positive and the point's x-coordinate is negative, or \ the point's z-coordinate is negative and the point's \ x-coordinate is positive - in other words, the point's \ x- and z-coordinates have opposite signs LDA #80 \ Set (xPointHi xPointLo) = 80 - (SS QQ) SEC \ SBC QQ \ starting with the low bytes STA xPointLo,X LDA #0 \ And then the high bytes SBC SS STA xPointHi,X JMP proj17 \ Jump to proj17 to move on to the y-coordinate .proj16 \ If we get here then either the point's z-coordinate is \ positive and the point's x-coordinate is positive, or \ the point's z-coordinate is negative and the point's \ x-coordinate is negative - in other words, the point's \ x- and z-coordinates have the same sign LDA #80 \ Set (xPointHi xPointLo) = 80 + (SS QQ) CLC \ ADC QQ \ starting with the low bytes STA xPointLo,X LDA #0 \ And then the high bytes ADC SS STA xPointHi,X .proj17 \ We now move the y-coordinate in the projected result \ to the correct place on screen, by adding 96 to the \ result and setting the correct sign in the process LDX GG \ Set X to the point ID to process LDA zPointHi,X \ If the point's z-coordinate is negative, jump to BMI proj18 \ proj18 LDA yPointHi,X \ If the point's y-coordinate is positive, jump to BPL proj20 \ proj20 as the coordinates have the same sign JMP proj19 \ Otherwise jump to proj19 as the coordinates have \ opposite signs .proj18 \ If we get here then the point's z-coordinate is \ negative LDA yPointHi,X \ If the point's y-coordinate is negative, jump to BMI proj20 \ proj20 as the coordinates have the same sign .proj19 \ If we get here then either the point's z-coordinate is \ positive and the point's y-coordinate is negative, or \ the point's z-coordinate is negative and the point's \ y-coordinate is positive - in other words, the point's \ y- and z-coordinates have opposite signs LDA #96 \ Set (yPointHi yPointLo) = 96 - (RR Q) SEC \ SBC Q \ starting with the low bytes STA yPointLo,X LDA #0 \ And then the high bytes SBC RR STA yPointHi,X JMP proj21 \ Jump to proj17 to move on to the point's status byte .proj20 LDA #96 \ Set (yPointHi yPointLo) = 96 + (RR Q) CLC \ ADC Q \ starting with the low bytes STA yPointLo,X LDA #0 \ And then the high bytes ADC RR STA yPointHi,X .proj21 LDA pointStatus,X \ Apply any set bits from N to the point's status byte ORA N STA pointStatus,X RTS \ Return from the subroutine
Name: DivideScaled [Show more] Type: Subroutine Category: Maths Summary: Divide a 16-bit number by a scaled 16-bit number
Context: See this subroutine on its own page References: This subroutine is called as follows: * ProjectPoint (Part 2 of 3) calls DivideScaled

The commentary in this routine is a work in progress. This routine calculates: (Q P) = (X Y) divided by (TT S) where (X Y) is a positive 16-bit number and (TT S) is the entry in the (divisionHi divisionLo) table for the denominator after being scaled by the ScaleUp routine.
Arguments: (X Y) A positive 16-bit number containing a number to be divided (the dividend) Z flag Set according to the high byte in X (i.e. the routine is called after setting register X) R Contains %00010000 (which is set at the start of ProjectPoint) (TT S) The lookup from (divisionHi divisionLo) for the high byte of the scaled up denominator (i.e. the number we are dividing by) K The low byte of the scaled up denominator
Returns: (Q P) (X Y) divided by the denominator used to look up the value of (TT S)
JSR ScaleUp \ This instruction appears to be an unused duplicate of \ the first instruction of DivideScaled, so perhaps this \ is a remnant of an unused entry point DivideScaled-3, \ which would run ScaleUp twice? .DivideScaled JSR ScaleUp \ Set (A Y) = (X Y), scaled up until it doesn't fit into \ 16 bits any more, and set WW to the minimum number of \ bits in the original number STA J \ Set (J I) = (A Y) STY I LDX TT \ Set X = TT \ = %TTTTtttt LDY J \ Set Y = J \ = %JJJJjjjj LDA highNibble,X \ Set T = (X AND %11110000) OR (Y >> 4) ORA shift4Right,Y \ = %TTTTJJJJ STA T AND #%11110000 \ Set U = (T AND %11110000) OR (Y AND %00001111) ORA lowNibble,Y \ = %TTTTjjjj STA U AND #%00001111 \ Set V = (U AND %00001111) OR (X << 4) ORA shift4Left,X \ = %ttttjjjj STA V AND #%11110000 \ Set Y = (V AND %11110000) OR (Y >> 4) ORA shift4Right,Y \ = %ttttJJJJ TAY LDX S \ Set X = S \ = %SSSSssss AND #%00001111 \ Set X = (Y AND %00001111) OR (X AND %11110000) ORA highNibble,X \ = %SSSSJJJJ TAX \ So by this point we have: \ \ T = %TTTTJJJJ \ U = %TTTTjjjj \ V = %ttttjjjj \ Y = %ttttJJJJ \ X = %SSSSJJJJ \ \ where the following are arguments that were passed to \ the routine: \ \ (TT S) = (%TTTTtttt %SSSSssss) \ \ (J I) = (%JJJJjjjj %IIIIiiii) \ \ The first one is the denominator's lookup from the \ division table, while the second is the dividend, \ scaled up \ We now calculate (Q P) = (TT S) * (1 J I) \ \ (I am unsure about the following) LDA timesTable,X \ Set A = %SSSS * %JJJJ CLC \ Set P = A + (%tttt * %jjjj) LDX V \ = (%SSSS * %JJJJ) + (%tttt * %jjjj) ADC timesTable,X STA P LDX T \ Set Q = (%TTTT * %JJJJ) + 1 LDA timesTable,X ADC #1 STA Q \ So (Q P) = (%SSSS * %JJJJ) + (%tttt * %jjjj) \ + ((%TTTT * %JJJJ) + 1) << 8 LDX U \ Set X = (%TTTT * %jjjj) + (%tttt * %JJJJ) LDA timesTable,X ADC timesTable,Y TAX LDY #0 \ Set Y = 0, to use as the high byte of (Y A) BCC divs1 \ If the above addition didn't overflow, skip the \ following instruction LDY #16 \ The above addition overflowed, so set Y = 16 to use as \ the high byte of (Y A), where Y = %00010000 .divs1 LDA shift4Left,X \ Set A = (X << 4) ADC P \ Set (Y A) = (Y A) + P \ \ starting with the low bytes BCC divs2 \ And then the high bytes INY .divs2 ADC J \ Set (Y A) = (Y A) + J \ \ starting with the low bytes BCC divs3 \ And then the high bytes INY .divs3 ADC S \ Set (Y A) = (Y A) + S \ \ just doing the low bytes STA P \ Set (A P) = (Y A) TYA ADC shift4Right,X \ Set A = A + X << 4 + Q ADC Q BCC divs4 \ If the addition didn't overflow, jump to divs4 CLC \ Set A = A + TT ADC TT SEC \ Set the C flag to feed into bit 7 of (A P) when we \ shift it right JMP divs5 \ Skip the following instruction .divs4 ADC TT \ Set A = A + TT \ \ and set the C flag to the carry .divs5 ROR A \ Set (A P) = (A P) >> 1 ROR P STA Q \ Set (Q P) = (A P) LDA I \ Set A = I = %IIIIiiii BEQ divs6 \ If I = 0, jump to divs6 AND #%11110000 \ Set A = %IIII0000 LDX TT \ Set X = %TTTTtttt ORA shift4Right,X \ Set A = %IIII0000 OR %0000TTTT \ = %IIIITTTT TAY \ Set Y = %IIIITTTT AND #%11110000 \ Set X = %IIII0000 OR %0000tttt ORA lowNibble,X \ = %IIIItttt TAX LDA timesTable,X \ Set X = %IIII * %tttt TAX \ = %XXXXxxxx CLC \ Set the C flag from adding %xxxx0000 + %IIIIiiii LDA shift4Left,X ADC I LDA timesTable,Y \ Set A = %IIII * %TTTT + %XXXX + C ADC shift4Right,X \ = %IIII * %TTTT + (%IIII * %tttt) >> 4 + C ROR A \ Set A = A >> 1 CLC \ Set (Q P) = (Q P) + A ADC P \ STA P \ starting with the low bytes BCC divs6 INC Q \ And then the high bytes BNE divs6 LDA #&FF \ Set (Q P) = &FFFF STA Q STA P .divs6 LDA K \ If bits 6 and 7 of K are clear, jump to divs9 to AND #%11000000 \ return from the subroutine BEQ divs9 STA K \ Clear bits 0 to 5 of K CLC \ Set W = TT >> 1 + 1 LDA TT ADC #1 ROR A STA W LSR A \ Set A = W >> 1 BIT K \ If bit 6 of K is set, jump to divs7 BVS divs7 LDA #0 \ Bit 6 of K is clear, so set A = 0 BIT K \ Set the flags for K again .divs7 BPL divs8 \ If bit 7 of K is clear, jump to divs8 CLC \ Bit 7 of K is set, so set A = A + W ADC W .divs8 TAY \ Set Y = A LDX Q \ Set X = Q JSR Multiply8x8 \ Set (A V) = X * Y \ = A * Q STA G \ Set (Q P) = (Q P) - A LDA P \ SEC \ starting with the high bytes SBC G STA P BCS divs9 \ If the subtraction didn't underflow, skip the next \ instruction DEC Q \ Decrement the high byte .divs9 RTS \ Return from the subroutine
Name: ScaleUp [Show more] Type: Subroutine Category: Maths Summary: Scale up a 16-bit number until it doesn't fit into 16 bits any more
Context: See this subroutine on its own page References: This subroutine is called as follows: * DivideScaled calls ScaleUp * ProjectPoint (Part 2 of 3) calls ScaleUp

Given a positive 16-bit argument, this routine scales the number up until it doesn't fit into 16 bits any more. It does this by doubling it (i.e. shifting it left) until a set bit pops out of the left end). The number of shifts is returned (in the form of the minimum number of binary digits in the original number) so we know how much the value was scaled up by.
Arguments: (X Y) A positive 16-bit number containing a coordinate magnitude Z flag Set according to the high byte in X (i.e. the routine is called after setting register X) R Contains %00010000 (which is set at the start of ProjectPoint)
Returns: (A Y) The original argument in (X Y), left-shifted until a set bit pops out of the left end, so the leftmost set bit is not in (A Y) WW WW + 1 is the minimum number of binary digits that the original value of (X Y) fitted into (from which we can calculate the number of shifts we had to do to scale the number up)
.ScaleUp BEQ scup4 \ If the high byte in X = 0, jump down to scup4 LDA divisionLo,X \ Set A = int(log2(X)) AND #%00000111 \ \ so we know X fits into a minimum of A + 1 binary \ digits CLC \ Set WW = A + 8 ADC #8 \ STA WW \ so (X Y) fits into WW + 1 binary digits CMP #13 \ Set the flags for the WW < 13 comparison below TXA \ Set (A T) = (X Y) STY T BCC scup3 \ If WW < 13, jump to scup3 .scup1 \ If we get here, then (X Y) fits into 13 + 1 digits or \ more, i.e. it fits into 14 digits or more \ \ Also, (A T) = (X Y) ASL T \ Left-shift (A T) until we shift a 1 out of bit 7 of ROL A \ the high byte in A BCC scup1 LDY T \ Set Y = T \ So now we have: \ \ (A Y) = (A T) \ \ which is the result that we want RTS \ Return from the subroutine .scup2 ASL T \ Left-shift (A T) by one place ROL A .scup3 \ If we get here, then (X Y) fits into fewer than 13 + 1 \ binary digits, i.e. it fits into 13 digits or fewer \ \ Also, (A T) = (X Y) BIT R \ If bit 4 of A is clear, loop back to scup2 to keep BEQ scup2 \ shifting (A T) left until bit 4 of A is set (we check \ bit 4 because R = %00010000) TAY \ Set (Y X) = (A T) LDX T \ \ so (Y X) contains the original value of (X Y), shifted \ left until bit 4 of X is set (note that the X and Y \ have swapped round here) \ \ Let's say (Y X) = (%AAAAaaaa %TTTTtttt), which is the \ original (X Y) shifted left until bit 4 is set LDA shift4Right,X \ Set A = (X >> 4) OR (Y << 4) ORA shift4Left,Y \ = (%TTTTtttt >> 4) OR (%AAAAaaaa << 4) \ = %aaaaTTTT LDY shift4Left,X \ Set Y = X << 4 \ = %TTTTtttt << 4 \ = %tttt0000 \ So we now have: \ \ (A Y) = (%aaaaTTTT %tttt0000) \ = (%AAAAaaaa %TTTTtttt) << 4 \ = (A T) << 4 \ = (X Y) << 4 \ \ which is the result that we want RTS \ Return from the subroutine .scup4 \ If we get here, then the high byte in X = 0 CPY #0 \ If the low byte in Y = 0, jump down to scup8 BEQ scup8 LDA divisionLo,Y \ Set WW = int(log2(Y)) AND #%00000111 \ STA WW \ so we know Y fits into a minimum of WW + 1 binary \ digits CMP #4 \ Set the flags for the WW < 4 comparison below TYA \ Set (A Y) = (Y 0) LDY #0 BCC scup7 \ If WW < 4, jump to scup7 .scup5 \ If we get here, then (X Y) fits into 4 + 1 digits or \ more, i.e. it fits into 5 digits or more, so \ (X Y) = (0 Y) \ \ Also, (A Y) = (Y 0), which effectively does the first \ 8 left-shifts for us, as we know none of those shifts \ will shift a 1 out of bit 7 of the high byte ASL A \ Left-shift (A Y) until we shift a 1 out of bit 7 of BCC scup5 \ the high byte in A (we don't need to shift the low \ byte as we know it's 0) \ So now we have: \ \ (A Y) = (X Y) shifted left until we shift a 1 out of \ bit 7 \ \ which is the result that we want RTS \ Return from the subroutine .scup6 \ If we get here, then (X Y) fits into fewer than 4 + 1 \ binary digits, i.e. it fits into 4 digits or fewer, so \ (X Y) = (0 Y) = (0 %0000yyyy) \ \ Also, (A Y) = (Y 0), which effectively does the first \ 8 left-shifts for us, as we know none of those shifts \ will shift a 1 out of bit 7 of the high byte ASL A \ Left-shift (A Y) by one place (we don't need to shift \ the low byte as we know it's 0) .scup7 BIT R \ If bit 4 of A is clear, loop back to scup6 to keep BEQ scup6 \ shifting (A Y) left until bit 4 of A is set (we check \ bit 4 because R = %00010000), so now we have something \ like this (depending on how many of %yyyy are set): \ \ (A Y) = (%0001yyy0 %00000000) TAX \ Set A = A << 4 LDA shift4Left,X \ \ which moves the result into the top byte of (A Y) like \ this: \ \ (A Y) = (%yyy00000 %00000000) \ \ which is the result that we want RTS \ Return from the subroutine .scup8 \ If we get here, then (X Y) = 0 TSX \ We can only get here if we called this routine from INX \ the DivideScaled routine, as the only other call of INX \ this routine is from ProjectPoint, when we know we TXS \ are calling it with a value of at least 1 in (X Y) \ \ These instructions remove two bytes from the top of \ the stack so the RTS below returns an extra level up \ the call chain, and as DivideScaled itself must have \ been called from ProjectPoint, this returns us to \ ProjectPoint with the following results LDA #0 \ Set (Q P) = 0 STA Q STA P LDX UU \ Set WW = UU - 1 DEX STX WW RTS \ Return from the subroutine
Name: ScaleDown (Part 1 of 4) [Show more] Type: Subroutine Category: Maths Summary: Scale down the results of divisions done using the ScaleUp and DivideScaled routines
Context: See this subroutine on its own page References: This subroutine is called as follows: * ProjectPoint (Part 2 of 3) calls ScaleDown

The commentary in this routine is a work in progress.
Arguments: UU Scale factor for the z-coordinate from ScaleUp routine VV Scale factor for the x-coordinate from ScaleUp routine WW Scale factor for the y-coordinate from ScaleUp routine (QQ PP) Result from DivideScaled routine for x-coordinate (Q P) Result from DivideScaled routine for y-coordinate
Returns: (SS QQ) The correctly scaled x-coordinate (RR Q) The correctly scaled y-coordinate
.ScaleDown LDA #0 \ Set RR = 0, to use as the high byte in the scaled STA RR \ y-coordinate (RR Q P), where (RR Q) in the final \ result is the integer, and P is the fractional part \ (which we discard) STA SS \ Set SS = 0, to use as the high byte in the scaled \ x-coordinate (SS QQ PP), where (SS QQ) in the final \ result is the integer, and PP is the fractional part \ (which we discard) LDA #7 \ Set T = 0 STA T LDA VV \ Set X = VV - UU + 1 SEC \ SBC UU \ so X is the imbalance between the x- and z-coordinates TAX \ in terms of scale factors, and because the division INX \ was x / z, this is the scale factor we need to apply \ to the x-coordinate, as 2^VV / 2^UU = 2^(VV - UU) LDA WW \ Set Y = WW - UU + 1 SEC \ SBC UU \ so Y is the imbalance between the y- and z-coordinates TAY \ in terms of scale factors, and because the division INY \ was y / z, this is the scale factor we need to apply \ to the y-coordinate, as 2^WW / 2^UU = 2^(WW - UU) CPY #7 \ If Y < 7, jump to down1 BCC down1 JMP down2 \ Y >= 7, so jump to down2 .down1 CPX #7 \ If X < 7, jump to down6 to do the scaling with T = 0 BCC down6 .down2 \ If we get here then at least one of X and Y is >= 7 LDA VV \ Set A = VV - WW SEC \ SBC WW \ so A is the imbalance between the x- and y-coordinates \ in terms of scale factors BEQ down3 \ If A = 0, i.e. VV = WW, jump to down3 BPL down4 \ If A > 0, i.e. VV > WW, jump to down4 \ At this point, VV < WW LDA Q \ If (Q P) < 0, jump to down6 to do the scaling with BMI down6 \ T = 0 JMP down5 \ (Q P) >= 0, so jump to down5 .down3 LDA Q \ If (Q P) < 0, jump to down6 to do the scaling with BMI down6 \ T = 0 .down4 LDA QQ \ If (QQ PP) < 0, jump to down6 to do the scaling with BMI down6 \ T = 0 .down5 \ We get here if at least one of X and Y is >= 7, and \ any of the following are true: \ \ * VV = WW and (Q P) >= 0 and (QQ PP) >= 0 \ i.e. x-scale = y-scale and x-coord >= 0 and \ y-coord >= 0 \ \ * VV > WW and (QQ PP) >= 0 \ i.e. x-scale > y-scale and x-coord >= 0 \ \ * VV < WW and (Q P) >= 0 \ i.e. x-scale < y-scale and y-coord >= 0 INC T \ Increment T to 1, so we decrease X and Y by one less \ in the next part
Name: ScaleDown (Part 2 of 4) [Show more] Type: Subroutine Category: Maths Summary: Balance the scale factors for the x- and y-coordinates
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.down6 \ If Y >= 0, we decrease X and Y by Y - T TYA \ If Y < 0, jump to down9 to skip the decrement loop BMI down9 JMP down8 \ Y >= 0, so jump to down8 to decrement X and Y .down7 DEX \ Decrement X and Y DEY .down8 CPY T \ While Y >= T, loop back to decrement X and Y BCS down7 .down9 \ If X >= 0, we decrease X and Y by X - T TXA \ If X < 0, jump to down12 to skip the decrement loop BMI down12 JMP down11 \ X >= 0, so jump to down11 to decrement X and Y .down10 DEX \ Decrement X and Y DEY .down11 CPX T \ While X >= T, loop back to decrement X and Y BCS down10
Name: ScaleDown (Part 3 of 4) [Show more] Type: Subroutine Category: Maths Summary: Scale the x-coordinate
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.down12 \ We now shift the x-coordinate in (SS QQ PP) by X \ places in the correct direction, discarding the \ fractional part in PP when we are done, but only after \ rounding (SS QQ) to the nearest integer TXA \ If X < 0, jump to down15 to shift the x-coordinate BMI down15 \ right by X places, with A set to X BNE down13 \ If X > 0, jump to down13 to shift the x-coordinate \ left by X places ASL PP \ X = 0, so shift bit 7 of PP into the C flag and jump JMP down17 \ to down17 to round the result to the nearest integer, \ without shifting the x-coordinate first .down13 \ We now shift (SS QQ PP) left by X places LDA QQ \ Set (SS A PP) = (SS QQ PP) .down14 ASL PP \ Set (SS A PP) = (SS A PP) << 1 ROL A ROL SS DEX \ Decrement the shift counter BNE down14 \ Loop back until we have shifted left by X places STA QQ \ Set (SS QQ PP) = (SS A PP) ASL PP \ Shift bit 7 of PP into the C flag, so it contains bit \ 7 of the fractional part JMP down17 \ Jump to down17 to round the result to the nearest \ integer .down15 \ If we get here then we want to shift the result right \ by the number of places in A, where A is negative EOR #&FF \ Set X = -A so we can use it as a shift counter for the CLC \ number of places to shift ADC #1 TAX \ We now shift (SS QQ PP) right by X places, and because \ SS = 0 and we are going to discard the fractional part \ in PP, we only actually need to shift QQ LDA QQ \ Set A = QQ .down16 LSR A \ Set A = A >> 1 DEX \ Decrement the shift counter BNE down16 \ Loop back until we have shifted right by X places STA QQ \ Set QQ = A, so now we have shifted (SS QQ) right by \ X places, and the C flag is set to the last bit that \ we shifted out of QQ, which would be bit 7 of the \ fractional part .down17 \ We now round up the result, if bit 7 of the fractional \ part is set \ The C flag contains the next bit that would have been \ left-shifted out of PP into the result in (SS QQ), or \ right-shifted out of the result in (SS QQ), so if that \ bit is set, we need to round up the result BCC down18 \ If the next bit is clear, then jump to down18 to move \ on to scaling the y-coordinate, as we do not need to \ round up the result INC QQ \ Otherwise increment the low byte of the result in BNE down18 \ (SS QQ) to round it up, and if we did that without \ overflowing, jump to down18 to move on to scaling the \ y-coordinate INC SS \ If the increment overflowed the low byte, increment \ the high byte of the result in (SS QQ) to round it up LDA SS \ If the high byte is < &40, jump to down18 to move on CMP #&40 \ to scaling the y-coordinate BCC down18 LDA #&3F \ Otherwise set (SS QQ) = &3FFF as the highest value we STA SS \ can return as the x-coordinate LDA #&FF STA QQ
Name: ScaleDown (Part 4 of 4) [Show more] Type: Subroutine Category: Maths Summary: Scale the y-coordinate
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.down18 \ We now shift the y-coordinate in (RR Q P) by Y \ places in the correct direction, discarding the \ fractional part in P when we are done, but only after \ rounding (RR Q) to the nearest integer TYA \ If Y < 0, jump to down21 to shift the y-coordinate BMI down21 \ right by Y places, with A set to Y BNE down19 \ If Y > 0, jump to down19 to shift the y-coordinate \ right by Y places ASL P \ Y = 0, so shift bit 7 of P into the C flag and jump JMP down23 \ to down23 to round the result to the nearest integer, \ without shifting the y-coordinate first .down19 \ We now shift (RR Q P) left by X places LDA Q \ Set (RR A P) = (RR Q P) .down20 ASL P \ Set (RR A P) = (RR A P) << 1 ROL A ROL RR DEY \ Decrement the shift counter BNE down20 \ Loop back until we have shifted left by Y places STA Q \ Set (RR Q P) = (RR A P) ASL P \ Shift bit 7 of P into the C flag, so it contains bit \ 7 of the fractional part JMP down23 \ Jump to down23 to round the result to the nearest \ integer .down21 \ If we get here then we want to shift the result right \ by the number of places in A, where A is negative EOR #&FF \ Set Y = -A so we can use it as a shift counter for the CLC \ number of places to shift ADC #1 TAY \ We now shift (RR Q P) right by Y places, and because \ RR = 0 and we are going to discard the fractional part \ in P, we only actually need to shift Q LDA Q \ Set A = Q .down22 LSR A \ Set A = A >> 1 DEY \ Decrement the shift counter BNE down22 \ Loop back until we have shifted right by Y places STA Q \ Set Q = A, so now we have shifted (RR Q) right by \ Y places, and the C flag is set to the last bit that \ we shifted out of Q, which would be bit 7 of the \ fractional part .down23 \ We now round up the result, if bit 7 of the fractional \ part is set \ The C flag contains the next bit that would have been \ left-shifted out of PP into the result in (RR Q), or \ right-shifted out of the result in (RR Q), so if that \ bit is set, we need to round up the result BCC down24 \ If the next bit is clear, then jump to return from the \ subroutine, as we do not need to round up the result INC Q \ Otherwise increment the low byte of the result in BNE down24 \ (RR Q) to round it up, and if we did that without \ overflowing, jump to down24 to return from the \ subroutine as we now have our result INC RR \ If the increment overflowed the low byte, increment \ the high byte of the result in (RR Q) to round it up LDA RR \ If the high byte is < &40, jump to down24 to return CMP #&40 \ from the subroutine as we now have our result BCC down24 LDA #&3F \ Otherwise set (RR Q) = &3FFF as the highest value we STA RR \ can return as the y-coordinate LDA #&FF STA Q .down24 RTS \ Return from the subroutine
Name: DrawClippedLine (Part 1 of 6) [Show more] Type: Subroutine Category: Drawing lines Summary: Clip a line to fit on-screen, starting with the line deltas Deep dive: Line buffers
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawCanopyView calls DrawClippedLine * DrawHalfHorizon calls via DrawClippedHorizon

Arguments: L The point ID for the line's start point M The point ID for the line's end point
Other entry points: DrawClippedHorizon Set bit 1 of the line direction in V (for the horizon)
.DrawClippedHorizon LDA #%00000010 \ For the horizon, set bit 1 of A to use as the starting BNE draw1 \ value for the line direction in V (so bit 1 of V gets \ set) .DrawClippedLine LDA #0 \ Set A = 0 to use as the starting value for the line \ direction in V .draw1 STA V \ Set V = A to set the starting value for the line \ direction in V (we will set bits 6 and 7 later) LDA #0 \ Set TT = 0, which we will use to store information STA TT \ about whether the line's start point fits on screen STA UU \ Set UU = 0, which we will use to store information \ about whether the line's end point fits on screen \ The first step is to calculate the line's |x-delta| \ and |y-delta| values, updating the line direction \ information in V as we do so LDX L \ Set X to the point ID for the line's start point LDY M \ Set Y to the point ID for the line's end point LDA xPointLo,X \ Set (RR R) = the X-th entry from (xPointHi xPointLo) STA R \ LDA xPointHi,X \ i.e. the x-coordinate of the line's start point STA RR LDA yPointLo,X \ Set (SS S) = the X-th entry from (yPointHi yPointLo) STA S \ LDA yPointHi,X \ i.e. the y-coordinate of the line's start point STA SS LDA xPointLo,Y \ Set (QQ W) = the Y-th entry from (xPointHi xPointLo) STA W \ \ starting with the low byte in W SEC \ Set T = W - R SBC R \ STA T \ which is the low byte of the calculation: \ \ (I T) = (QQ W) - (RR R) LDA xPointHi,Y \ Set (QQ W) = the Y-th entry from (xPointHi xPointLo) STA QQ \ \ i.e. the x-coordinate of the line's end point SBC RR \ Set I = QQ - RR STA I \ \ so we now have: \ \ (I T) = (QQ W) - (RR R) \ = x-coordinate of the line's end point \ - x-coordinate of the line's start point \ \ so (I T) is the line's x-delta BPL draw2 \ If the high byte in I is positive, then so is (I T), \ so jump to draw2 to skip the following, as the x-delta \ is positive \ Otherwise the x-delta is negative, so we need to set \ the x-direction in V and negate x-delta so it is \ positive (as we want to calculate |x-delta|) LDA #%10000000 \ Set bit 7 of V to indicate a negative x-delta in the ORA V \ line direction in V STA V LDA #0 \ Negate (I T), starting with the low bytes SEC SBC T STA T LDA #0 \ And then negating the high bytes SBC I \ STA I \ so now (I T) is positive, and contains |x-delta| .draw2 LDA yPointLo,Y \ Set (H G) = the Y-th entry from (yPointHi yPointLo) STA G \ \ starting with the low byte in G SEC \ Set U = G - S SBC S \ STA U \ which is the low byte of the calculation: \ \ (J U) = (H G) - (SS S) LDA yPointHi,Y \ Set (H G) = the Y-th entry from (yPointHi yPointLo) STA H \ \ i.e. the y-coordinate of the line's end point SBC SS \ Set J = H - SS STA J \ \ so we now have: \ \ (J U) = (H G) - (SS S) \ = y-coordinate of the line's end point \ - y-coordinate of the line's start point \ \ so (J U) is the line's y-delta BPL draw3 \ If the high byte in J is positive, then so is (J U), \ so jump to draw3 to skip the following, as the y-delta \ is positive \ Otherwise the y-delta is negative, so we need to set \ the y-direction in V and negate y-delta so it is \ positive (as we want to calculate |y-delta|) LDA #%01000000 \ Set bit 6 of V to indicate a negative y-delta in the ORA V \ line direction in V STA V LDA #0 \ Negate (J U), starting with the low bytes SEC SBC U STA U LDA #0 \ And then negating the high bytes SBC J \ STA J \ so now (J U) is positive, and contains |y-delta|
Name: DrawClippedLine (Part 2 of 6) [Show more] Type: Subroutine Category: Drawing lines Summary: Work out whether the line's start point is on-screen
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.draw3 LDA #0 \ The next step is to work out where the line's \ coordinates lie in relation to the visible screen, \ i.e. are the coordinates to the right or left of the \ screen bounds, or above or below \ \ We will capture this information for a single \ coordinate in the top 4 bits of A, so we start by \ setting A = 0 so we can populate it with information \ of how the line's coordinates relate to the screen \ edges \ We start by looking at the y-coordinate for the start \ point, and will store the result in bits 4 and 5 of \ the final result in A LDX S \ Set (Y X) = (SS S) LDY SS \ = the y-coordinate of the line's start point BEQ draw4 \ If the high byte in Y is zero, jump to draw4 PHP \ Set X = 0, but without affecting the processor flags, LDX #0 \ which will still be set according to the value of the PLP \ high byte in Y CLC \ Clear the C flag BMI draw5 \ If the high byte in Y is negative, jump to draw5 DEX \ Decrement X from 0 to 255 .draw4 SEC \ Set the C flag .draw5 \ By this point we have the following, depending on the \ value of the high byte of the start point's \ y-coordinate: \ \ * If SS < 0, C = 0 and X = 0 \ * If SS = 0, C = 1 and X = S \ * If SS > 0, C = 1 and X = 255 ROR A \ Put the C flag into bit 7 of A, which will end up \ being bit 4 in the final result (as we are going to \ shift three more bits into A). Note that we flip this \ bit in the EOR below CPX #152 \ The result of this comparison depends on the value \ that we gave to X above: \ \ * If SS < 0, X = 0 and 0 < 152, so C = 0 \ * If SS = 0, X = S, so if S < 152, C = 0 \ if S >= 152, C = 1 \ * If SS > 0, X = 255 and 255 >= 152, so C = 1 ROR A \ Rotate the C flag into bit 7 of A, which will end up \ being bit 5 in the final result (as we are going to \ shift two more bits into A) EOR #%01000000 \ Flip bit 6 \ We now have the following in bits 6 and 7, which will \ be shifted down to bits 4 and 5 in the final result: \ \ * Bit 6 (4) is set if SS < 0 \ clear if SS >= 0 \ \ * Bit 7 (5) is clear if SS < 0 \ or SS = 0 and S < 152 \ set if SS = 0 and S >= 152 \ or SS > 0 \ \ The visible part of the canopy screen has its origin \ (0, 0) at the bottom-left corner of the canopy, just \ above the dashboard, and it is 19 character blocks \ high, which is 8 * 19 = 152 pixels high, so this sets \ bits 0 and 1 in the final result depending on whether \ the start y-coordinate is within this range, and if \ not, whether it is above or below the range - in other \ words, it determines whether this coordinate is \ on-screen or off-screen, as follows: \ \ Bit 6 (4) Bit 7 (5) \ Off top of screen 0 1 \ On-screen 0 0 \ Off bottom of screen 1 0 \ \ We never have both bits set \ We now repeat the above process for the start point's \ x-coordinate in (RR R), putting the result into bits 6 \ and 7 of A while shifting the above result into bits 4 \ and 5 \ \ The process is slightly different as this time the \ on-screen x-coordinate range is 4 to 155 LDX R \ Set (Y X) = (RR R) LDY RR \ = the x-coordinate of the line's start point BEQ draw6 \ If the high byte in Y is zero, jump to draw6 PHP \ Set X = 0, but without affecting the processor flags, LDX #0 \ which will still be set according to the value of the PLP \ high byte in Y BMI draw6 \ If the high byte in Y is negative, jump to draw6 DEX \ Decrement X from 0 to 255 .draw6 \ By this point we have the following, depending on the \ value of the high byte of the start point's \ x-coordinate: \ \ * If RR < 0, X = 0 \ * If RR = 0, X = R \ * If RR > 0, X = 255 CPX #4 \ The result of this comparison depends on the value \ that we gave X above: \ \ * If RR < 0, X = 0 and 0 < 4, so C = 0 \ * If RR = 0, X = R, so if R < 4, C = 0 \ if R >= 4, C = 1 \ * If RR > 0, X = 255 and 255 >= 4, so C = 1 ROR A \ Rotate the C flag into bit 7 of A, which will end up \ being bit 6 in the final result (as we are going to \ shift one more bit into A). Note that we flip this \ bit in the EOR below CPX #156 \ The result of this comparison depends on the value \ that we gave X above: \ \ * If RR < 0, X = 0 and 0 < 156, so C = 0 \ * If RR = 0, X = R, so C = 0 if R < 156, \ C = 1 if R >= 156 \ * If RR > 0, X = 255 and 255 >= 156, so C = 1 ROR A \ Rotate the C flag into bit 7 of A, which is where it \ will stay EOR #%01000000 \ Flip bit 6 \ We now have the following in bits 6 and 7: \ \ * Bit 6 is clear if RR > 0 \ or RR = 0 and R >= 4 \ i.e. if (RR R) >= 4 \ set if RR < 0 \ or RR = 0 and R < 4 \ i.e. if (RR R) < 4 \ \ * Bit 7 is clear if RR < 0 \ or RR = 0 and R < 156 \ i.e. if (RR R) < 156 \ set if RR = 0 and R >= 156 \ or RR > 0 \ i.e. if (RR R) >= 156 \ \ The visible part of the canopy screen has its origin \ (0, 0) at the bottom-left corner of the canopy, just \ above the dashboard, but the rivets and the left edge \ of the canopy take up the first four pixels, so the \ leftmost x-coordinate inside the canopy is 4 \ \ Similarly, the right edge of the canopy is four pixels \ wide, and the whole screen is 160 pixels wide, so the \ rightmost x-coordinate inside the canopy is 155 \ \ So the above sets bits 6 and 7 in the final result \ depending on whether the start x-coordinate is within \ this range, and if not, whether it is to the left or \ right of the range - in other words, it determines \ whether this coordinate is on-screen or off-screen, \ as follows: \ \ Bit 6 Bit 7 \ Off right of screen 0 1 \ On-screen 0 0 \ Off left of screen 1 0 \ \ We never have both bits set STA TT \ Store A in TT \ \ So TT contains the clipping requirements for the start \ point, in bits 4 to 7, as follows: \ \ Bit 4 Bit 5 \ Off top of screen 0 1 \ On-screen 0 0 \ Off bottom of screen 1 0 \ \ Bit 6 Bit 7 \ Off right of screen 0 1 \ On-screen 0 0 \ Off left of screen 1 0
Name: DrawClippedLine (Part 3 of 6) [Show more] Type: Subroutine Category: Drawing lines Summary: Work out whether the line's end point is on-screen
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
\ We now repeat the above process, but for the line's \ end point, whose y-coordinate is in (H G) and \ x-coordinate is in (QQ W). Refer to the previous part \ for a detailed description LDA #0 \ Set A = 0 so we can capture bits 4 to 7 for the end \ point LDX G \ Set (Y X) to the end point's y-coordinate in (H G) LDY H BEQ draw7 \ This section sets the following: PHP \ LDX #0 \ * If H < 0, C = 0 and X = 0 PLP \ * If H = 0, C = 1 and X = G CLC \ * If H > 0, C = 1 and X = 255 BMI draw8 DEX .draw7 SEC \ See above .draw8 ROR A \ This section sets bits 6 and 7, which will become CPX #152 \ bits 4 and 5 of the final result: ROR A \ EOR #%01000000 \ Bit 6 (4) Bit 7 (5) \ Off top of screen 0 1 \ On-screen 0 0 \ Off bottom of screen 1 0 LDX W \ Set (Y X) to the end point's x-coordinate in (QQ W) LDY QQ BEQ draw9 \ This section sets the following: PHP \ LDX #0 \ * If QQ < 0, X = 0 PLP \ * If QQ = 0, X = W BMI draw9 \ * If QQ > 0, X = 255 DEX .draw9 CPX #4 \ This section sets bits 6 and 7: ROR A \ CPX #156 \ Bit 6 Bit 7 ROR A \ Off right of screen 0 1 EOR #%01000000 \ On-screen 0 0 \ Off left of screen 1 0 STA UU \ Store A in UU \ \ So UU contains the clipping requirements for the end \ point, in bits 4 to 7, as follows: \ \ Bit 4 Bit 5 \ Off top of screen 0 1 \ On-screen 0 0 \ Off bottom of screen 1 0 \ \ Bit 6 Bit 7 \ Off right of screen 0 1 \ On-screen 0 0 \ Off left of screen 1 0
Name: DrawClippedLine (Part 4 of 6) [Show more] Type: Subroutine Category: Drawing lines Summary: Calculate the starting point and direction for our clipped vector line
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
\ In this part, the end-goal is to calculate the start \ point and direction for an on-screen vector line, \ which we can pass them to the DrawCanopyLine routine \ later on \ \ This means we want to calculate a pixel coordinate in \ (R, S) and a direction in V, by clipping the current \ start and end points to fit on-screen, if necessary LDX L \ Set X to the point ID for the line's start point LDY M \ Set Y to the point ID for the line's end point LDA zPointHi,Y \ If the z-coordinate for the line's end point is BPL draw10 \ positive, then it's in front of us, so jump to draw10 LDA V \ The end point is behind us, so flip bits 6 and 7 in V EOR #%11000000 \ to reverse the line direction in both axes STA V \ The end point is behind us, so we can't use the end \ point as our vector line's starting point, so now we \ check whether we can use the start point LDA TT \ If TT is zero then the start point is on-screen, so BEQ draw15 \ jump to draw15 to use the current values of (R, S) as \ our pixel coordinate, which works because RR and SS \ have to be zero for the start point to be on-screen, \ so (RR R) = (0 R) = R and (SS S) = (0 S) = S, so we \ can just use the low bytes as the two coordinates, \ i.e. (R, S) BNE draw12 \ TT is non-zero, so the start point is off-screen, so \ jump to draw12 to clip the start of the line so it \ fits on-screen (this BNE is effectively a JMP as A is \ never zero) .draw10 \ If we get here then the end point is in front of us LDA zPointHi,X \ If the z-coordinate for the line's start point is BPL draw11 \ positive, which is in front of us, jump to draw11 JSR SwapLinePoints \ The start point is behind us and the end point is in \ front of us, so copy the end point's coordinates and \ clipping information into the start point, so the \ start point is now in front of us LDA TT \ If TT is zero then the start point is on-screen, so BEQ draw15 \ jump to draw15 BNE draw12 \ TT is non-zero, so the start point is off-screen, so \ jump to draw12 to clip the start of the line (this BNE \ is effectively a JMP as A is never zero) .draw11 \ If we get here then both the start and end points are \ in front of us LDA TT \ If TT is non-zero then the start point is off-screen, BNE draw14 \ so jump to draw14 to potentially clip from the end of \ the line (i.e. if the end point is off-screen) LDA UU \ If UU is non-zero then the end point is off-screen, so BNE draw15 \ jump to draw13 via draw15 to clip from the end of the \ line BEQ draw21 \ Both TT and UU are zero, so both points are on-screen \ and we don't need to do any clipping, so jump to \ draw21 .draw12 \ If we get here then the one point is off-screen but \ in front of us, and the other point is behind us and \ can't be used as our vector line starting point, and \ we've set the start point to be the point that is in \ front of us JSR ClipStartOfLine \ Clip the line at the start to move the start point \ on-screen, so we can use it as our vector line's \ starting point JMP draw15 \ We now have an on-screen pixel coordinate in (R, S), \ so jump to draw15 to move on to the next stage .draw13 \ If we get here then both the start and end points are \ off-screen, but they are both in front of us, so we \ need to clip both ends of the line JSR ClipBestEndOfLine \ Clip the line at either the start or end point, \ whichever is best, so that it fits on-screen JMP draw15 \ We now have an on-screen pixel coordinate in (R, S), \ so jump to draw15 to move on to the next stage .draw14 \ If we get here then both the start and end points are \ in front of us and the start point is off-screen LDA UU \ If UU is non-zero then the end point is also BNE draw13 \ off-screen, so jump to draw13 to clip both ends of the \ line \ If we get here then the start point is off-screen but \ the end point is on-screen, so we now use the end \ point for our vector line's starting point LDA V \ Flip bits 6 and 7 in V to reverse the line direction EOR #%11000000 STA V LDA W \ Set (R, S) to the end point's coordinate in (W, G) STA R LDA G STA S
Name: DrawClippedLine (Part 5 of 6) [Show more] Type: Subroutine Category: Drawing lines Summary: Calculate the deltas for our clipped vector line
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.draw15 LDA #4 \ If bit 7 of V is set, so the direction of the x-delta BIT V \ is to the left, set A = 4 BMI draw16 LDA #155 \ Otherwise the direction of the x-delta is to the \ right, so set A = 155 .draw16 STA W \ Set W = 4 or 155, depending on the direction of the \ x-delta, so W is the value of the x-coordinate at the \ edge of the screen in the direction given in V LDA #0 \ If bit 6 of V is set, so the direction of the y-delta BVS draw17 \ is down, set A = 0 LDA #151 \ Otherwise the direction of the y-delta is up, so set \ A = 151 .draw17 STA G \ Set G = 0 or 151, depending on the direction of the \ y-delta, so W is the value of the y-coordinate at the \ edge of the screen in the direction given in V JMP draw19 \ Jump to draw19 .draw18 LSR I \ Right-shift (I T) ROR T LSR J \ Right-shift (J U) ROR U .draw19 \ Back in part 1 we set the following values: \ \ * (I T) is the line's |x-delta| \ * (J U) is the line's |y-delta| \ \ We now reduce these 16-bit values to 8-bit values by \ shifting them until they fit into one byte each LDA I \ If the high bytes of either of the line delta's is ORA J \ non-zero, loop up to draw18 to right-shift both deltas BNE draw18 \ We now have the following: \ \ * T = |x-delta| \ * U = |y-delta| LDA #255 \ If T <> 255, jump to draw20 CMP T BNE draw20 LSR T \ T = 255, which is too big to use as our line delta, so LSR U \ divide both T and U by 2 .draw20 CMP U \ If U <> 255, jump to draw21 BNE draw21 LSR T \ U = 255, which is too big to use as our line delta, so LSR U \ divide both T and U by 2 .draw21 INC T \ Increment T to give us our final |x-delta|, so it is \ at least 1 INC U \ Increment U to give us our final |y-delta|, so it is \ at least 1
Name: DrawClippedLine (Part 6 of 6) [Show more] Type: Subroutine Category: Drawing lines Summary: Add the clipped line to the line buffer and draw it
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
LDA colourCycle \ If bit 7 of colourCycle is set, i.e. %11110000, jump BMI draw23 \ down to draw23 to add a line to buffer 1 LDX lineBuffer2Count \ If lineBuffer2Count <> 95, line buffer 2 is not full, CPX #95 \ so jump down to draw22 to add a new line to the buffer BNE draw22 RTS \ Return from the subroutine .draw22 INX \ Increment the value in lineBuffer2Count as we are STX lineBuffer2Count \ about to add a new line to line buffer 2 JMP draw25 \ Jump down to draw25 to buffer the line and draw it .draw23 LDX lineBuffer1Count \ If lineBuffer1Count <> 47, line buffer 1 is not full, CPX #47 \ so jump down to draw24 to add a new line to the buffer BNE draw24 RTS \ Return from the subroutine .draw24 INX \ Increment the value in lineBuffer1Count as we are STX lineBuffer1Count \ about to add a new line to line buffer 1 .draw25 LDA R \ Save the start x-coordinate in lineBufferR STA lineBufferR,X LDA W \ Save the max/min x-coordinate in lineBufferW STA lineBufferW,X LDA S \ Save the start y-coordinate in lineBufferS STA lineBufferS,X LDA G \ Save the max/min y-coordinate in lineBufferG STA lineBufferG,X LDA T \ Save the |x-delta| in lineBufferT STA lineBufferT,X LDA U \ Save the |y-delta| in lineBufferU STA lineBufferU,X LDA V \ Save the direction in lineBufferV STA lineBufferV,X \ Fall through into DrawCanopyLine to draw the line
Name: DrawCanopyLine (Part 1 of 9) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a line in the canopy view Deep dive: Line buffers
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawCanopyLine (Part 9 of 9) calls DrawCanopyLine * EraseCanopyLines calls DrawCanopyLine

The commentary in this routine is a work in progress. The code in this routine is modified by the ModifyDrawRoutine routine, and by the DrawCanopyLine routine itself. The default code (i.e. the unmodified version in the source) is run when U < T (shallow horizontal slope), bit 6 of V is clear (y-axis up) and bit 7 is set (x-axis left).
Arguments: R Start point x-coordinate S Start point y-coordinate T Relative magnitude of line's vector |x-delta| U Relative magnitude of line's vector |y-delta| V Direction of vector (T, U): * Bit 7 is the direction of the x-delta * Bit 6 is the direction of the y-delta * Bit 1 is set if this is the horizon line * Bit 0 is set if the line has been clipped Direction is like a clock, so positive (clear) is up and right W Max/min x-coordinate for the end of the line G Max/min y-coordinate for the end of the line xTemp1Lo The x-coordinate of the start of the line, if clipped yTemp1Lo The y-coordinate of the start of the line, if clipped
.DrawCanopyLine LDA R \ Set X = R / 4 LSR A \ LSR A \ so X is the number of the character block containing TAX \ pixel (R, S), as each character block is 4 pixels wide LDA S \ Set Y = S / 8 LSR A \ LSR A \ so Y is the number of the character row containing LSR A \ pixel (R, S), as each character row is 8 pixels high TAY CLC \ Set P = X-th byte of xLookupLo LDA xLookupLo,X \ + Y-th byte of yLookupLo ADC yLookupLo,Y \ = LO(X * 8) + LO(screen address) STA P LDA xLookupHi,X \ Set Q = X-th byte of xLookupHi ADC yLookupHi,Y \ + Y-th byte of yLookupHi STA Q \ = HI(X * 8) + HI(screen address) \ So (Q P) is the screen address of the pixel row \ containing pixel (R, S), out by 8 bytes for each row \ above or below the top of the dashboard LDA U \ If U < T, the line is a shallow horizontal slope, so CMP T \ jump down to dlin1 to draw the line BCC dlin1 JMP dlin41 \ Otherwise U >= T and the line is a steep vertical \ slope, so jump down to part 4 to draw the line
Name: DrawCanopyLine (Part 2 of 9) [Show more] Type: Subroutine Category: Drawing lines Summary: Modify the line drawing routine for a shallow horizontal slope
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The code in this routine is modified by the ModifyDrawRoutine routine, and by the DrawCanopyLine routine itself. The default code (i.e. the unmodified version in the source) is run when: * Bit 7 of V is clear, so we step along the x-axis in a positive direction, i.e. to the right * Bit 6 of V is set, so we step along the y-axis in a negative direction, i.e. down the screen
.dlin1 \ If we get here then the line is a shallow horizontal \ slope BIT V \ If bit 7 of V is set, jump to dlin3 to step along the BMI dlin3 \ x-axis in a negative direction, i.e. to the left \ If we get here then bit 7 of V is clear, so we step \ along the x-axis in a positive direction, i.e. to the \ right, which we implement by modifying the routine's \ code, setting it back to the default LDA #5 \ Modify the following instruction at dlin20: STA dlin20+1 \ \ BCC dlin22 -> BCC dlin22 \ \ i.e. set it back to the default LDA #9 \ Modify the following instruction at dlin21: STA dlin21+1 \ \ BNE dlin25 -> BNE dlin25 \ \ i.e. set it back to the default .dlin2 LDA #LO(colour1L2R) \ Modify the following instruction at dlin33: STA dlin33+1 \ \ LDA colour1L2R,X -> LDA colour1L2R,X \ \ Note that this is a two-layer modification, as the \ LDA #LO(colour1L2R) instruction gets modified by the \ ModifyDrawRoutine routine as follows: \ \ * LO(colour1L2R) when colourLogic = %01000000 \ so the bit pattern lookup table uses colour 1 \ i.e. LDA colour1L2R,X \ \ * LO(colour2L2R) when colourLogic = %01000000 \ so the bit pattern lookup table uses colour 2 \ i.e. LDA colour2L2R,X \ \ * LO(colour1Row) when colourLogic = %00000000 \ and colourCycle = %00001111 \ so the bit pattern lookup is always %00001111 \ i.e. LDA colour1Row,X \ \ * LO(colour2Row) when colourLogic = %00000000 \ and colourCycle = %11110000 \ so the bit pattern lookup is always %11110000 \ i.e. LDA colour2Row,X \ \ In other words, this instruction has already been \ modified to implement the current colour cycle LDA #39 \ Set I = 39, the number of the last character block STA I \ on the row BNE dlin5 \ Jump to dlin5 to skip the code modifications for the \ other value of bit 7 (this BNE is effectively a JMP as \ A is never zero) .dlin3 \ If we get here then bit 7 of V is set, so we step \ along the x-axis in a negative direction, i.e. to the \ left, which we implement by modifying the routine's \ code LDA #&24 \ Modify the following instruction at dlin20: STA dlin20+1 \ \ BCC dlin22 -> BCC dlin27 LDA #&28 \ Modify the following instruction at dlin21: STA dlin21+1 \ \ BNE dlin25 -> BNE dlin30 .dlin4 \ Modify the following instruction at dlin33: LDA #LO(colour1R2L) \ STA dlin33+1 \ LDA colour1L2R,X -> LDA colour1R2L,X \ \ Note that this is a two-layer modification, as the \ LDA #LO(colour1R2L) instruction gets modified by the \ ModifyDrawRoutine routine as follows: \ * LO(colour1R2L) when colourLogic = %01000000 \ so the bit pattern lookup table uses colour 1 \ i.e. LDA colour1R2L,X \ \ * LO(colour2R2L) when colourLogic = %01000000 \ so the bit pattern lookup table uses colour 2 \ i.e. LDA colour2R2L,X \ \ * LO(colour1Row) when colourLogic = %00000000 \ and colourCycle = %00001111 \ so the bit pattern lookup is always %00001111 \ i.e. LDA colour1Row,X \ \ * LO(colour2Row) when colourLogic = %00000000 \ and colourCycle = %11110000 \ so the bit pattern lookup is always %11110000 \ i.e. LDA colour2Row,X \ \ In other words, this instruction has already been \ modified to implement the current colour cycle LDA #0 \ Set I = 0, the number of the first character block STA I \ on the row .dlin5 BIT V \ If bit 6 of V is set, jump to dlin6 to step along the BVS dlin6 \ y-axis in a negative direction, i.e. down the screen \ If we get here then bit 6 of V is clear, so we step \ along the y-axis in a positive direction, i.e. up the \ screen, which we implement by modifying the routine's \ code LDA #&98 \ Modify the following instruction at dlin35: STA dlin35 \ \ INY -> TYA LDA #&88 \ Modify the following instruction at dlin36: STA dlin36 \ \ TYA -> DEY LDA #&C8 \ Modify the following instruction at dlin37: STA dlin37+1 \ \ ADC #&38 -> ADC #&C8 LDA #&FE \ Modify the following instruction at dlin38: STA dlin38+1 \ \ ADC #1 -> ADC #&FE LDA #158 \ Set J = 158 - G SEC \ SBC G \ So J contains the y-coordinate of the end of the line, STA J \ flipped to match screen memory (so higher values of \ the y-coordinate are lower down the screen) JMP dlin7 \ Jump to dlin7 to skip the code modifications for the \ other value of bit 6 .dlin6 \ If we get here then bit 6 of V is set, so we step \ along the y-axis in a negative direction, i.e. down \ the screen, which we implement by modifying the \ routine's code, setting it back to the default LDA #&C8 \ Modify the following instruction at dlin35: STA dlin35 \ \ INY -> INY \ \ i.e. set it back to the default LDA #&98 \ Modify the following instruction at dlin36: STA dlin36 \ \ TYA -> TYA \ \ i.e. set it back to the default LDA #&38 \ Modify the following instruction at dlin37: STA dlin37+1 \ \ ADC #&38 -> ADC #&38 \ \ i.e. set it back to the default LDA #1 \ Modify the following instruction at dlin38: STA dlin38+1 \ \ ADC #1 -> ADC #1 \ \ i.e. set it back to the default LDA #160 \ Set J = 160 - G SEC \ SBC G \ So J contains the y-coordinate of the end of the line, STA J \ flipped to match screen memory (so higher values of \ the y-coordinate are lower down the screen)
Name: DrawCanopyLine (Part 3 of 9) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a line as a shallow horizontal slope
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.dlin7 \ By the time we get here, the code has been modified to \ work with the step directions given in bits 6 and 7 of \ V, as well as the current colour cycle \ \ To keep things simple, we will only document the \ default code, which is for a shallow horizontal slope \ with the following: \ \ * Bit 7 of V is clear, so we step along the x-axis \ in a positive direction, i.e. to the right \ \ * Bit 6 of V is set, so we step along the y-axis in \ a negative direction, i.e. down the screen \ \ * The current colour cycle is drawing in colour 1, \ using the pixel bitmaps at colour1L2R \ \ By this point, we also have the following variables \ set: \ \ * (Q P) is the screen address of the pixel row \ containing pixel (R, S), out by 8 bytes for each \ row above or below the top of the dashboard \ \ * I = 39, the number of the last character block on \ a screen row, heading left to right \ \ * J = 160 - G, the y-coordinate of the end of the \ line, flipped around so it increases as we go down \ the screen \ \ The last two have different values with different line \ directions, but these are the values for the default \ case that we're considering here LDA #159 \ Set Y = 159 - S SEC \ SBC S \ This gets added to the screen address in (Q P) that we TAY \ set above, to give the screen address of the starting \ point at coordinate (R, S) LDA #255 \ Set RR = 255 - T SEC \ = 255 - |x-delta| SBC T STA RR CLC \ Set SS = RR ADC #1 \ = 255 - T + 1 STA SS \ \ This is the starting value for the slope error LDA V \ If bits 0 and 1 of V are both clear, then this is not AND #%00000011 \ the horizon line or a clipped line, so jump to dlin8 BEQ dlin8 \ to skip the following LDA U \ If U < 2, jump to dlin8 to skip the following CMP #2 BCC dlin8 \ If we get here then U >= 2, and this is either the \ horizon line or the line has been clipped LDA #255 \ Set SS = 255 STA SS .dlin8 LDA R \ Set X = bits 0 and 1 of R, so X is the pixel number AND #%00000011 \ in the character row for pixel (R, S), in the range TAX \ 0 to 3 \ We are going to use QQ to keep track of the current \ character block number as we work our way along the \ line, drawing as we go, so we need to initialise it to \ the character block number of the starting point in \ (R, S) LDA R \ Set QQ = R / 4 LSR A \ LSR A \ so QQ is the number of the character block containing STA QQ \ pixel (R, S), as each character block is 4 pixels wide LDA SS \ Set A = SS, so it contains the current slope error BIT V \ If bit 7 of V is set, then we are stepping along the BMI dlin10 \ x-axis in a negative direction, i.e. to the left, so \ jump to dlin10 CPX #1 \ If X < 1 (i.e. X = 0), jump to dlin13 BCC dlin13 BNE dlin9 \ If X <> 1 (i.e. X = 2 or 3), jump to dlin9 CLC \ If we get here then X = 1, so clear the C flag and BCC dlin14 \ jump to dlin14 (this BCC is effectively a JMP as we \ know the C flag is clear) .dlin9 \ If we get here then X = 2 or 3 CPX #3 \ If X < 3 (i.e. X = 2), jump to dlin16 BCC dlin16 CLC \ If we get here then X = 3, so clear the C flag and BCC dlin18 \ jump to dlin18 (this BCC is effectively a JMP as we \ know the C flag is clear) .dlin10 \ If we get here then bit 7 of V is set, so we are \ stepping along the x-axis in a negative direction, \ i.e. to the left CPX #1 \ If X < 1 (i.e. X = 0), jump to dlin18 BCC dlin18 BNE dlin11 \ If X <> 1 (i.e. X = 2 or 3), jump to dlin11 CLC \ If we get here then X = 1, so clear the C flag and BCC dlin16 \ jump to dlin16 (this BCC is effectively a JMP as we \ know the C flag is clear) .dlin11 CPX #3 \ If X < 3 (i.e. X = 2), jump to dlin14 BCC dlin14 CLC \ If we get here then X = 3, so clear the C flag and BCC dlin13 \ jump to dlin13 (this BCC is effectively a JMP as we \ know the C flag is clear)
Name: DrawCanopyLine (Part 4 of 9) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a part of the line, working down the screen
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The code in this routine is modified by the ModifyDrawRoutine, and by the DrawCanopyLine routine itself. The default code (i.e. the unmodified version in the source) is run when: * The current colour cycle is drawing in colour 1, using the pixel bitmaps at colour1L2R and OR logic for updating the screen
.dlin12 \ This forms the start of a loop that works its way \ along the line. We only jump to this part from the end \ of the loop, which is joined at different points from \ part 3 above \ \ The loop is joined with A already set to SS, which \ keeps track of the slope error as we step along the \ x-axis one pixel at a time \ \ So SS keeps track of the y-axis position by adding \ the y-delta in U, and when adding U to A overflows \ because the slope error has reached a whole number \ (i.e. 256), we move down to the next a pixel row \ \ The following commentary assumes we are moving along \ the x-axis from left to right, and down the screen. \ The code is modified in-place for drawing in the \ other directions, which is note in the commentary \ \ In the following, X is used to store the offset of the \ pixel byte we are going to draw, from the colour1L2R \ table (this is modified to cater for different colour \ cycles, but for simplicity we just consider the case \ of drawing from left to right in colour 1) CLC \ Clear the C flag to reset the carry for the additions \ below LDA SS \ Set A = SS, so it contains the current slope error .dlin13 LDX #0 \ Set X = 0 to point to pixel 0, which is the pixel \ byte 1000 when drawing from left to right ADC U \ Set A = A + U, to add the y-delta to the slope error BCC dlin15 \ If the addition didn't overflow, jump to dlin15 to \ increment X to move to the second pixel along, \ which is the pixel byte 1100 when drawing from left \ to right JSR dlin32 \ Otherwise the slope error just overflowed, so we need \ to move down a pixel row, so call dlin32 to draw the \ pixel byte pointed to by X and move down to the next \ character row .dlin14 \ If we get here then we either just draw the first \ pixel in the pixel byte, i.e. 1000, and moved up or \ down a pixel row, or we just jumped here from part 3 \ \ In both cases, we want to start drawing the next pixel \ byte from the second pixel along, i.e. 0100 LDX #3 \ Set X = 3 so the next instruction increments X to 4, \ which is the pixel byte 0100 when drawing from left \ to right .dlin15 INX \ Increment X to set the next pixel in the pixel byte \ (i.e. step through the colour1L2R table) ADC U \ Set A = A + U, to add the y-delta to the slope error BCC dlin17 \ If the addition didn't overflow, jump to dlin17 to \ increment X to move to the third pixel along, \ which is the pixel byte 1110 when drawing from left \ to right JSR dlin32 \ Otherwise the slope error just overflowed, so we need \ to move down a pixel row, so call dlin32 to draw the \ pixel byte pointed to by X and move down to the next \ character row .dlin16 \ If we get here then we either just draw the second \ pixel in the pixel byte, i.e. 0100 or 1100, and moved \ down a pixel row, or we just jumped here from part 3 \ \ In both cases, we want to start drawing the next pixel \ byte from the second pixel along, i.e. 0010 LDX #6 \ Set X = 6 so the next instruction increments X to 7, \ which is the pixel byte 0010 when drawing from left \ to right .dlin17 INX \ Increment X to set the next pixel in the pixel byte \ (i.e. step through the colour1L2R table) ADC U \ Set A = A + U, to add the y-delta to the slope error BCC dlin19 \ If the addition didn't overflow, jump to dlin19 to \ increment X to move to the fourth pixel along, \ which is the pixel byte 1111 when drawing from left \ to right JSR dlin32 \ Otherwise the slope error just overflowed, so we need \ to move down a pixel row, so call dlin32 to draw the \ pixel byte pointed to by X and move down to the next \ character row .dlin18 \ If we get here then we either just draw the third \ pixel in the pixel byte, i.e. 0010 or 0110 or 1110, \ and moved down a pixel row, or we just jumped here \ from part 3 \ \ In both cases, we want to start drawing the next pixel \ byte from the second pixel along, i.e. 0001 LDX #8 \ Set X = 8 so the next instruction increments X to 9, \ which is the pixel byte 0001 when drawing from left \ to right .dlin19 INX \ Increment X to set the next pixel in the pixel byte \ (i.e. step through the colour1L2R table) ADC U \ Set A = A + U, to add the y-delta to the slope error .dlin20 BCC dlin22 \ If the addition didn't overflow, jump to dlin22 to \ draw the pixel byte but without going down a row, as \ we have reached the end of the pixel byte and need to \ draw it before moving on the next pixel byte to the \ right \ \ Gets modified by the DrawCanopyLine routine: \ \ * BCC dlin22 when bit 7 of V is clear \ \ * BCC dlin27 when bit 7 of V is set \ \ In other words, this instruction has already been \ modified to implement the current drawing direction JSR dlin32 \ Otherwise the slope error just overflowed, so we need \ to move down a pixel row, so call dlin32 to draw the \ pixel byte pointed to by X and move down to the next \ character row .dlin21 BNE dlin25 \ Jump to dlin25 to move on to the next character block, \ as we just moved down a pixel row after drawing the \ fourth pixel in the pixel byte, so we need to move on \ to the next pixel byte along \ \ The dlin32 routine ends with a BEQ just before the RTS \ so this BNE is effectively a JMP, as we had to pass \ through the BEQ in dlin32 to return from the above \ call \ \ Gets modified by the DrawCanopyLine routine: \ \ * BNE dlin25 when bit 7 of V is clear \ \ * BNE dlin30 when bit 7 of V is set \ \ In other words, this instruction has already been \ modified to implement the current drawing direction .dlin22 \ If we get here then we need to draw the current pixel \ byte as pointed to by X, and then move to the next \ character block along, without going down a row STA SS \ Set SS = A to store the updated slope error in SS .dlin23 LDA colour1L2R,X \ Fetch the X-th pixel byte from colour1L2R \ \ Gets modified by the ModifyDrawRoutine routine: \ \ * LDA colour1L2R,X when colourLogic = %10000000 \ \ * LDA colour2L2R,X when colourLogic = %01000000 \ \ * LDA colour1Row,X when colourLogic = %00000000 \ and colourCycle = %00001111 \ \ * LDA colour2Row,X when colourLogic = %00000000 \ and colourCycle = %11110000 \ \ In other words, this instruction has already been \ modified to implement the current colour cycle .dlin24 ORA (P),Y \ OR the pixel byte with the current screen contents \ \ Gets modified by the ModifyDrawRoutine routine: \ \ * ORA (P),Y when colourLogic = %01000000 \ \ * AND (P),Y when colourLogic = %00000000 \ \ In other words, this instruction has already been \ modified to implement the current drawing logic STA (P),Y \ Update the Y-th byte of (Q P) with the result, which \ sets 4 pixels to the pixel pattern in A .dlin25 LDA P \ Set (Q P) = (Q P) + 8 CLC \ ADC #8 \ starting with the low bytes STA P BCC dlin26 \ And then the high bytes, so (Q P) now points to the INC Q \ next character block to the right .dlin26 INC QQ \ Increment QQ, which contains the current character \ block number LDA QQ \ If QQ <> I, then we haven't yet reached the right edge CMP I \ of the screen (whose character block number we set in BNE dlin12 \ I in part 2), so jump back to dlin12 to keep drawing \ the line JMP dlin65 \ Otherwise we have reached the edge of the screen, so \ jump to dlin65 to process the clipped part of the \ line, if applicable
Name: DrawCanopyLine (Part 5 of 9) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a part of the line, working up the screen
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The code in this routine is modified by the ModifyDrawRoutine, and by the DrawCanopyLine routine itself. The default code (i.e. the unmodified version in the source) is run when: * The current colour cycle is drawing in colour 1, using the pixel bitmaps at colour1L2R and OR logic for updating the screen
.dlin27 \ This routine draws a part of a line (one pixel row, \ i.e. a byte) and moves us up to the previous pixel row \ \ We call this subroutine with: \ \ * A = the current slope error \ \ * X = the index of the pixel byte at colour1R2L STA SS \ Set SS = A .dlin28 LDA colour1R2L,X \ Fetch the X-th pixel byte from colour1R2L \ \ Gets modified by the ModifyDrawRoutine routine: \ \ * LDA colour1R2L,X when colourLogic = %10000000 \ \ * LDA colour2R2L,X when colourLogic = %01000000 \ \ * LDA colour1Row,X when colourLogic = %00000000 \ and colourCycle = %00001111 \ \ * LDA colour2Row,X when colourLogic = %00000000 \ and colourCycle = %11110000 \ \ In other words, this instruction has already been \ modified to implement the current colour cycle .dlin29 ORA (P),Y \ OR the pixel byte with the current screen contents \ \ Gets modified by the ModifyDrawRoutine routine: \ \ * ORA (P),Y when colourLogic = %01000000 \ \ * AND (P),Y when colourLogic = %00000000 \ \ In other words, this instruction has already been \ modified to implement the current drawing logic STA (P),Y \ Update the Y-th byte of (Q P) with the result, which \ sets 4 pixels to the pixel pattern in A .dlin30 LDA P \ Set (Q P) = (Q P) - 8 SEC \ SBC #8 \ starting with the low bytes STA P BCS dlin31 \ And then the high bytes, so (Q P) now points to the DEC Q \ previous character block to the left .dlin31 DEC QQ \ Decrement QQ, which contains the current character \ block number LDA QQ \ If QQ <> I, then we haven't yet reached the right edge CMP I \ of the screen (whose character block number we set in BNE dlin12 \ I in part 2), so jump back to dlin12 to keep drawing \ the line JMP dlin65 \ Otherwise we have reached the edge of the screen, so \ jump to dlin65 to process the clipped part of the \ line, if applicable
Name: DrawCanopyLine (Part 6 of 9) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a part of the line, working along the screen
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The code in this routine is modified by the ModifyDrawRoutine, and by the DrawCanopyLine routine itself. The default code (i.e. the unmodified version in the source) is run when: * The current colour cycle is drawing in colour 1, using the pixel bitmaps at colour1L2R and OR logic for updating the screen * Bit 6 of V is set, so we step along the y-axis in a negative direction, i.e. down the screen
.dlin32 \ This routine draws a part of a line (one pixel row, \ i.e. a byte) and moves us down to the next character \ row (though it may be modified to move up a row for \ lines that are being drawn in that direction) \ \ We call this subroutine with: \ \ * A = the current slope error \ \ * X = the index of the pixel byte at colour1L2R \ \ * The C flag is always set ADC RR \ Set SS = A + RR + C STA SS \ = slope error + (255 - |x-delta|) + 1 \ = slope error + 256 - |x-delta| \ = slope error - |x-delta| .dlin33 LDA colour1L2R,X \ Fetch the X-th pixel byte from colour1L2R \ \ Gets modified by the DrawCanopyLine routine, which in \ turn gets modified by the ModifyDrawRoutine routine: \ \ * colour1L2R when colourLogic = %01000000 \ \ * colour2L2R when colourLogic = %01000000 \ \ * colour1Row when colourLogic = %00000000 \ and colourCycle = %00001111 \ \ * colour2Row when colourLogic = %00000000 \ and colourCycle = %11110000 \ \ In other words, this instruction has already been \ modified to implement the current colour cycle .dlin34 ORA (P),Y \ OR the pixel byte with the current screen contents \ \ Gets modified by the ModifyDrawRoutine routine: \ \ * ORA (P),Y when colourLogic = %01000000 \ \ * AND (P),Y when colourLogic = %00000000 \ \ In other words, this instruction has already been \ modified to implement the current drawing logic STA (P),Y \ Update the Y-th byte of (Q P) with the result, which \ sets 4 pixels to the pixel pattern in A .dlin35 INY \ Increment Y to point to the next screen byte, i.e. the \ offset of the next row in the character block \ \ Gets modified by the DrawCanopyLine routine: \ \ * INY when bit 6 of V is set \ \ * TYA when bit 6 of V is clear \ \ In other words, this instruction has already been \ modified to implement the current drawing direction .dlin36 TYA \ Set A to the offset of the next row in the character \ block \ \ Gets modified by the DrawCanopyLine routine: \ \ * TYA when bit 6 of V is set \ \ * DEY when bit 6 of V is clear \ \ In other words, this instruction has already been \ modified to implement the current drawing direction AND #7 \ If A mod 7 <> 0 then we haven't reached the end of the BNE dlin39 \ 8-row character block, so jump to dlin39 to skip the \ following \ Otherwise we have reached the last row of the \ character block, so we now add &138 to (Q P) to move \ to the address of the start of the character block \ in the row below (as each character row in mode 5 \ contains &140 bytes, so this is &140 - 8 to cater for \ the block we just finished) LDA P \ We start by adding &38 to the low byte in P CLC .dlin37 ADC #&38 \ Add &38 to the low byte \ \ Gets modified by the DrawCanopyLine routine: \ \ * ADC #&38 when bit 6 of V is set \ \ * ADC #&E8 when bit 6 of V is clear \ \ In other words, this instruction has already been \ modified to implement the current drawing direction STA P \ Store the updated low byte in P LDA Q \ Then add the high bytes .dlin38 ADC #1 \ Add &1 to the high byte \ \ Gets modified by the DrawCanopyLine routine: \ \ * ADC #1 when bit 6 of V is set \ \ * ADC #&FE when bit 6 of V is clear \ \ In other words, this instruction has already been \ modified to implement the current drawing direction STA Q \ Which we store in Q, so now we have: \ \ (Q P) = (Q P) + &138 \ \ so (Q P) is the address of the start of the character \ block in the row below .dlin39 LDA SS \ Set A = SS, so it contains the current slope error CPY J \ If the current y-coordinate in Y = J, then we have CLC \ reached the y-coordinate of the end of the line (which BEQ dlin40 \ we set in part 2), so jump to dlin40 to stop drawing \ the line and move on to the clipped part of the line RTS \ Return from the subroutine with the C flag clear .dlin40 TSX \ Remove two bytes from the top of the stack, so the INX \ next RTS returns us to caller of the DrawCanopyLine INX \ routine rather than the caller of dlin32 (so this TXS \ effectively breaks out of the current line-drawing \ loop) JMP dlin65 \ Jump to dlin65 to process the clipped part of the \ line, if applicable
Name: DrawCanopyLine (Part 7 of 9) [Show more] Type: Subroutine Category: Drawing lines Summary: Modify the line drawing routine for a steep vertical slope
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

The code in this routine is modified by the ModifyDrawRoutine routine, and by the DrawCanopyLine routine itself. The default code (i.e. the unmodified version in the source) is run when: * Bit 7 of V is clear, so we step along the x-axis in a positive direction, i.e. to the right * Bit 6 of V is clear, so we step along the y-axis in a positive direction, i.e. up the screen
.dlin41 \ If we get here then the line is a steep vertical slope BIT V \ If bit 6 of V is set, jump to dlin42 to step along the BVS dlin42 \ y-axis in a negative direction, i.e. down the screen \ If we get here then bit 6 of V is clear, so we step \ along the y-axis in a positive direction, i.e. up the \ screen, which we implement by modifying the routine's \ code LDA #&98 \ Modify the following instruction at dlin53: STA dlin53 \ \ TYA -> TYA \ \ i.e. set it back to the default LDA #&88 \ Modify the following instruction at dlin54: STA dlin54 \ \ DEY -> DEY \ \ i.e. set it back to the default LDA #&C8 \ Modify the following instruction at dlin37: STA dlin55+1 \ \ ADC #&C8 -> ADC #&C8 \ \ i.e. set it back to the default LDA #&FE \ Modify the following instruction at dlin38: STA dlin56+1 \ \ ADC #&FE -> ADC #&FE \ \ i.e. set it back to the default LDA #7 \ Set J = 7 STA J \ \ So J contains the y-coordinate of the bottom of the \ screen, flipped to match screen memory (so higher \ values of the y-coordinate are lower down the screen) BNE dlin43 \ Jump to dlin43 to skip the code modifications for the \ other value of bit 6 (this BNE is effectively a JMP as \ A is never zero) .dlin42 \ If we get here then bit 6 of V is set, so we step \ along the y-axis in a negative direction, i.e. down \ the screen, which we implement by modifying the \ routine's code, setting it back to the default LDA #&C8 \ Modify the following instruction at dlin53: STA dlin53 \ \ TYA -> INY LDA #&98 \ Modify the following instruction at dlin54: STA dlin54 \ \ DEY -> TYA LDA #&38 \ Modify the following instruction at dlin37: STA dlin55+1 \ \ ADC #&C8 -> ADC #&38 LDA #1 \ Modify the following instruction at dlin38: STA dlin56+1 \ \ ADC #&FE -> ADC #1 LDA #160 \ Set J = 160 STA J \ \ So J contains the y-coordinate of the end of the line, \ flipped to match screen memory (so higher values of \ the y-coordinate are lower down the screen) .dlin43 BIT V \ If bit 7 of V is set, jump to dlin44 to step along the BMI dlin44 \ x-axis in a negative direction, i.e. to the left \ If we get here then bit 7 of V is clear, so we step \ along the x-axis in a positive direction, i.e. to the \ right, which we implement by modifying the routine's \ code, setting it back to the default LDA #&1D \ Modify the following instruction at dlin52: STA dlin52+1 \ \ BCS dlin57 -> BCS dlin57 \ \ i.e. set it back to the default LDA W \ Set I = W + 1 CLC ADC #1 STA I JMP dlin45 \ Jump to dlin45 to skip the code modifications for the \ other value of bit 7 .dlin44 \ If we get here then bit 7 of V is set, so we step \ along the x-axis in a negative direction, i.e. to the \ left, which we implement by modifying the routine's \ code LDA #&3F \ Modify the following instruction at dlin52: STA dlin52+1 \ \ BCS dlin57 -> BCS dlin61 LDA W \ Set I = W - 1 SEC SBC #1 STA I
Name: DrawCanopyLine (Part 8 of 9) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a line as a steep vertical slope
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file
.dlin45 \ By the time we get here, the code has been modified to \ work with the step directions given in bits 6 and 7 of \ V \ \ To keep things simple, we will only document the \ default code, which is for a steep vertical slope with \ the following: \ \ * Bit 7 of V is clear, so we step along the x-axis \ in a positive direction, i.e. to the right \ \ * Bit 6 of V is clear, so we step along the y-axis \ in a positive direction, i.e. up the screen \ \ By this point, we also have the following variables \ set: \ \ * (Q P) is the screen address of the pixel row \ containing pixel (R, S), out by 8 bytes for each \ row above or below the top of the dashboard \ \ * I = W + 1, the x-coordinate for the end of the \ line + 1 \ \ * J = 7, the y-coordinate of the end of the line \ \ The last two have different values with different line \ directions, but these are the values for the default \ case that we're considering here LDA #159 \ Set Y = 159 - S SEC \ SBC S \ This gets added to the screen address in (Q P) that we TAY \ set above, to give the screen address of the starting \ point at coordinate (R, S) LDA #255 \ Set RR = 255 - U SEC \ = 255 - |y-delta| SBC U STA RR CLC \ Set SS = RR + 1 ADC #1 \ = 255 - U + 1 STA SS \ \ This is the starting value for the slope error LDA V \ If bits 0 and 1 of V are both clear, jump to dlin46 AND #%00000011 BEQ dlin46 LDA T \ If T < 2, jump to dlin46 CMP #2 BCC dlin46 LDA #255 \ Set SS = 255 for the starting slope error STA SS .dlin46 LDA R \ Set X = bits 0 and 1 of R, so X is the pixel number AND #%00000011 \ in the character row for pixel (R, S) TAX .dlin47 LDA #%00001000 \ Set A to a pixel byte with pixel 0 set to colour 1 \ \ Gets modified by the ModifyDrawRoutine routine: \ \ * LDA #8 when colourLogic = %10000000 \ \ * LDA #&80 when colourLogic = %01000000 \ \ In other words, this instruction has already been \ modified to implement the current colour logic CPX #0 \ If X = 0, then the pixel number for point (R, S) is 0, BEQ dlin49 \ so skip the following loop as the pixel is already in \ the right place \ Otherwise we right-shift A by X places to move the \ pixel to the right place .dlin48 LSR A \ Shift A to the right by one place DEX \ Decrement the shift counter BNE dlin48 \ Loop back until we have shifted A right by X places, \ so the pixel is now in the right place for point \ (R, S) within this pixel row .dlin49 STA H \ Store the pixel byte in H, which contains a single \ pixel in the correct colour in position X CLC \ Clear the C flag for the addition below LDX R \ Set X = R, so it contains the x-coordinate of the \ start of the line .dlin50 LDA H \ Set A to the pixel byte for point (R, S) within this \ pixel row \ \ Gets modified by the ModifyDrawRoutine routine: \ \ * LDA H when colourLogic = %01000000 \ or %10000000 \ \ * LDA #%00001111 when colourLogic = %00000000 \ and colourCycle = %00001111 \ \ * LDA #%11110000 when colourLogic = %00000000 \ and colourCycle = %11110000 \ \ In other words, this instruction has already been \ modified to implement the current colour cycle .dlin51 ORA (P),Y \ OR the pixel byte with the current screen contents \ \ Gets modified by the ModifyDrawRoutine routine: \ \ * ORA (P),Y when colourLogic = %01000000 \ \ * AND (P),Y when colourLogic = %00000000 \ \ In other words, this instruction has already been \ modified to implement the current drawing logic STA (P),Y \ Update the Y-th byte of (Q P) with the result, which \ sets 4 pixels to the pixel pattern in A, so this sets \ the pixel at screen coordinate (R, S) to the current \ colour LDA SS \ Set A = SS + T ADC T \ = slope error + |x-delta| \ \ so this updates the slope error as we move up one \ the screen one pixel at a time .dlin52 BCS dlin57 \ If the above addition overflowed, then the slope error \ just overflowed, so jump to dlin57 to move along the \ x-axis \ \ Gets modified by the DrawCanopyLine routine: \ \ * BCC dlin57 when bit 7 of V is clear \ \ * BCC dlin61 when bit 7 of V is set \ \ In other words, this instruction has already been \ modified to implement the current drawing direction STA SS \ Set SS = A to store the updated slope error in SS .dlin53 TYA \ Store the current y-coordinate in A \ \ Gets modified by the DrawCanopyLine routine: \ \ * TYA when bit 6 of V is clear \ \ * INY when bit 6 of V is set \ \ In other words, this instruction has already been \ modified to implement the current drawing direction .dlin54 DEY \ Decrement Y to move up a pixel row \ \ Gets modified by the DrawCanopyLine routine: \ \ * DEY when bit 6 of V is clear \ \ * TYA when bit 6 of V is set \ \ In other words, this instruction has already been \ modified to implement the current drawing direction AND #7 \ If A mod 7 <> 0 then we haven't reached the top of the BNE dlin50 \ 8-row character block, so loop back to dlin50 to draw \ the next row \ Otherwise we have reached the top row of the character \ block, so we now subtract &138 from (Q P) to move to \ the address of the bottom of the character block in \ the row above (as each character row in mode 5 \ contains &140 bytes, so this is &140 - 8 to cater for \ the block we just finished) \ \ Subtracting &138 is the same as adding &FEC8, so \ that's what we do now LDA P \ We start by adding &C8 to the low byte in P CLC .dlin55 ADC #&C8 \ Add &C8 to the low byte \ \ Gets modified by the DrawCanopyLine routine: \ \ * ADC #&C8 when bit 6 of V is clear \ \ * ADC #&38 when bit 6 of V is set \ \ In other words, this instruction has already been \ modified to implement the current drawing direction STA P \ Store the updated low byte in P LDA Q \ Then add the high bytes .dlin56 ADC #&FE \ Add &FE to the high byte \ \ Gets modified by the DrawCanopyLine routine: \ \ * ADC #&FE when bit 6 of V is clear \ \ * ADC #1 when bit 6 of V is set \ \ In other words, this instruction has already been \ modified to implement the current drawing direction STA Q \ Which we store in Q, so now we have: \ \ (Q P) = (Q P) + &FEC8 \ = (Q P) - &138 \ \ so (Q P) is the address of the end of the character \ block in the row above CPY J \ If the current y-coordinate in Y <> J, then we have CLC \ not yet reached the y-coordinate of the end of the BNE dlin50 \ line (which we set in part 7), so loop back to dlin50 \ to keep drawing the line JMP dlin65 \ Otherwise we have reached the y-coordinate of the end \ of the line, so jump to dlin65 to process the clipped \ part of the line, if applicable .dlin57 \ If we get here then the slope error just overflowed, \ and we are drawing up the screen (bit 7 of V is clear) \ \ We reached with a BCS, so we know the C flag is set \ \ We now need to move the line along the x-axis to the \ right by 1 pixel, which we do by shifting the single \ pixel in H to the right, and if it falls off the right \ end, adjusting the screen address in (Q P) to point to \ the next character block along ADC RR \ Set SS = A + RR + C STA SS \ = slope error + 255 - |y-delta| + 1 \ = slope error + 256 - |y-delta| \ = slope error - |y-delta| INX \ Increment X to move along the x-axis to the right LDA H \ Set A to the single pixel byte in H and shift it right LSR A \ to move the pixel along by one place .dlin58 CMP #%00000000 \ If the pixel byte is non-zero, then the pixel hasn't BNE dlin60 \ fallen off the end, so jump to dlin60 to skip the \ following \ \ Gets modified by the ModifyDrawRoutine routine: \ \ * CMP #%00000000 when colourLogic = %10000000 \ \ * CMP #%00001000 when colourLogic = %01000000 \ \ In other words, this instruction has already been \ modified to implement the current drawing logic LDA P \ Set (Q P) = (Q P) + 8 CLC \ ADC #8 \ starting with the low bytes STA P .dlin59 LDA #%00001000 \ Set A to a pixel byte with the leftmost pixel set, so \ we can use this as our new single pixel byte in H \ below (so shifting across by one character block only \ moves the single pixel right by one pixel) \ \ Gets modified by the ModifyDrawRoutine routine: \ \ * LDA #8 when colourLogic = %10000000 \ \ * LDA #&80 when colourLogic = %01000000 \ \ In other words, this instruction has already been \ modified to implement the current drawing logic BCC dlin60 \ And now add the high bytes, so (Q P) now points to the INC Q \ next character block to the right .dlin60 STA H \ Store the updated one-pixel byte in H CPX I \ If the current x-coordinate in X <> I, then we have CLC \ not yet reached the x-coordinate of the end of the BNE dlin53 \ line (which we set in part 7), so loop back to dlin53 \ to keep drawing the line BEQ dlin65 \ Otherwise we have reached the x-coordinate of the end \ of the line, so jump to dlin65 to process the clipped \ part of the line, if applicable (this BEQ is \ effectively a JMP as we just passed through a BNE) .dlin61 \ If we get here then the slope error just overflowed, \ and we are drawing down the screen (bit 7 of V is set) \ \ We reached with a BCS, so we know the C flag is set \ \ We now need to move the line along the x-axis to the \ left by 1 pixel, which we do by shifting the single \ pixel in H to the left, and if it falls off the left \ end, adjusting the screen address in (Q P) to point to \ the previous character block ADC RR \ Set SS = A + RR + C STA SS \ = slope error + 255 - |y-delta| + 1 \ = slope error + 256 - |y-delta| \ = slope error - |y-delta| DEX \ Decrement X to move along the x-axis to the left LDA H \ Set A to the single pixel byte in H and shift it left ASL A \ to move the pixel along by one place .dlin62 CMP #%00010000 \ If the pixel byte is not %00010000, then the pixel BNE dlin64 \ hasn't fallen off the end, so jump to dlin64 to skip \ the following \ \ Gets modified by the ModifyDrawRoutine routine: \ \ * CMP #%00010000 when colourLogic = %10000000 \ \ * CMP #%00000000 when colourLogic = %01000000 \ \ In other words, this instruction has already been \ modified to implement the current drawing logic LDA P \ Set (Q P) = (Q P) - 8 SEC \ SBC #8 \ starting with the low bytes STA P .dlin63 LDA #%00000001 \ Set A to a pixel byte with the rightmost pixel set, so \ we can use this as our new single pixel byte in H \ below (so shifting across by one character block only \ moves the single pixel left by one pixel) \ \ Gets modified by the ModifyDrawRoutine routine: \ \ * LDA #1 when colourLogic = %10000000 \ \ * LDA #16 when colourLogic = %01000000 \ \ In other words, this instruction has already been \ modified to implement the current drawing logic BCS dlin64 \ And now subtract the high bytes, so (Q P) now points DEC Q \ to the previous character block to the left .dlin64 STA H \ Store the updated one-pixel byte in H CPX I \ If the current x-coordinate in X <> I, then we have CLC \ not yet reached the x-coordinate of the end of the BNE dlin53 \ line (which we set in part 7), so loop back to dlin53 \ to keep drawing the line \ Otherwise we have reached the x-coordinate of the end \ of the line, so fall through into dlin65 to process \ the clipped part of the line, if applicable
Name: DrawCanopyLine (Part 9 of 9) [Show more] Type: Subroutine Category: Drawing lines Summary: If the line was clipped, draw a line from the clipped coordinates to the edge of the screen
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

If the line has been clipped, this part reverses the line's direction in V and switches the start point at (R, S) to the other end of the line, before jumping to the start of the line-drawing routine to draw the reversed line.
.dlin65 LDA V \ Set A = V >> 1, setting the C flag to bit 0 of V LSR A BCS dlin66 \ If bit 0 if V is set, which means the line has been \ clipped by the ClipStartOfLine routine, jump to dlin66 RTS \ Otherwise bit 0 of V is clear and the line has not \ been clipped, so we return from the subroutine .dlin66 ASL A \ Shift A left so it contains V again EOR #%11000000 \ Flip bits 6 and 7 of V to reverse the direction of the STA V \ line LDA xTemp1Lo \ Set (R, S) = (xTemp1Lo, yTemp1Lo), which we set to the STA R \ start coordinate of the clipped line in the LDA yTemp1Lo \ ClipStartOfLine routine STA S LDA #4 \ Set A = 4, to use as the value of W if bit 7 of V is \ set (i.e. when we step along the x-axis in a negative \ direction, to the left) BIT V \ If bit 7 of V is set, jump to dlin67 to skip the next BMI dlin67 \ instruction LDA #155 \ Set A = 155, to use as the value of W if bit 7 of V is \ clear (i.e. when we step along the x-axis in a \ positive direction, to the right) .dlin67 STA W \ Set W to the value in A (4 or 155), so W contains the \ x-coordinate for the edge of the screen in the step \ direction (i.e. 4 if we are stepping left, 155 if we \ are stepping right) LDA #0 \ Set A = 0, to use as the value of W if bit 6 of V is \ set (i.e. when we step along the y-axis in a negative \ direction, down the screen) BVS dlin68 \ If bit 6 of V is set, jump to dlin68 to skip the next \ instruction LDA #151 \ Set A = 151, to use as the value of W if bit 6 of V is \ clear (i.e. when we step along the y-axis in a \ positive direction, up the screen) .dlin68 STA G \ Set G to the value in A (0 or 151), so G contains the \ y-coordinate for the edge of the screen in the step \ direction (i.e. 0 if we are stepping down, 151 if we \ are stepping up) JMP DrawCanopyLine \ Jump up to DrawCanopyLine to draw the reversed line \ from (xTemp1Lo, yTemp1Lo) to the edge of the screen
Name: ModifyDrawRoutine [Show more] Type: Subroutine Category: Drawing lines Summary: Modify the drawing routines to draw in the correct colour for the current colour cycle
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawCanopyView calls ModifyDrawRoutine * EraseCanopyLines calls ModifyDrawRoutine * ResetLineLists calls ModifyDrawRoutine

This routine modifies the DrawCanopyLine routine, depending on the value of the colourLogic and colourCycle variables. See the DrawCanopyView routine for details of the colour-cycling system that this forms part of. This is what the ModifyDrawRoutine routine does: If colourLogic = %00000000 (erase lines): * Modify the drawing logic in DrawCanopyLine to AND * Modify DrawCanopyLine so it fetches bit patterns from: * colour1Row if colourCycle = %00001111 * colour2Row if colourCycle = %11110000 In other words, the bit pattern it fetches is always the same as the value of colourCycle, as colour1Row contains %00001111 and colour2Row contains %11110000 * Modify DrawCanopyLine (part 3) so it pokes the value of colourCycle as a bit pattern in the screen-updating routine at dlin50 If colourLogic = %01000000 (draw lines in colour 2): * Modify the drawing logic in DrawCanopyLine to OR (the default) * Modify DrawCanopyLine so it fetches bit patterns from colour2L2R and colour2R2L (colour 2) instead of colour1L2R and colour1R2L (colour 1) If colourLogic = %10000000 (draw lines in colour 1): * Modify the drawing logic in DrawCanopyLine to OR (the default) * Restore the DrawCanopyLine routine back to its default code, so we draw in colour 1
.ModifyDrawRoutine LDA colourLogic \ If colourLogic is non-zero, jump to modd3 BNE modd3 \ If we get here then colourLogic is %00000000 LDA #&31 \ Set A to the opcode for the AND (P),Y instruction STA dlin24 \ Modify the following instruction in DrawCanopyLine \ (part 4): \ \ ORA (P),Y -> AND (P),Y STA dlin29 \ Modify the following instruction in DrawCanopyLine \ (part 5): \ \ ORA (P),Y -> AND (P),Y STA dlin34 \ Modify the following instruction in DrawCanopyLine \ (part 6): \ \ ORA (P),Y -> AND (P),Y STA dlin51 \ Modify the following instruction in DrawCanopyLine \ (part 8): \ \ ORA (P),Y -> AND (P),Y LDA colourCycle \ If bit 7 of colourCycle is set, i.e. %11110000, jump BMI modd1 \ down to modd1 LDA #LO(colour1Row) \ Bit 7 of colourCycle is clear, i.e. %00001111, so set \ A to LO(colour1Row) so the instructions below are \ modified to the following: \ \ LDA #LO(colour1L2R) -> LDA #LO(colour1Row) \ LDA colour1L2R,X -> LDA colour1Row,X \ LDA #LO(colour1R2L) -> LDA #LO(colour1Row) \ LDA colour1R2L,X -> LDA colour1Row,X BNE modd2 \ Jump down to modd2 (this BNE is effectively a JMP as \ A is never zero) .modd1 LDA #LO(colour2Row) \ Bit 7 of colourCycle is set, i.e. %11110000, so set \ A to LO(colour2Row) so the instructions below are \ modified to the following: \ \ LDA #LO(colour1L2R) -> LDA #LO(colour2Row) \ LDA colour1L2R,X -> LDA colour2Row,X \ LDA #LO(colour1R2L) -> LDA #LO(colour2Row) \ LDA colour1R2L,X -> LDA colour2Row,X .modd2 \ Modify the following instruction in DrawCanopyLine \ (part 2) where LO(colourA) is the value of A: \ STA dlin2+1 \ LDA #LO(colour1L2R) -> LDA #LO(colourA) \ Modify the following instruction in DrawCanopyLine \ (part 4) where colourA is the value of A: \ STA dlin23+1 \ LDA colour1L2R,X -> LDA colourA,X \ Modify the following instruction in DrawCanopyLine \ (part 2) where LO(colourA) is the value of A: \ STA dlin4+1 \ LDA #LO(colour1R2L) -> LDA #LO(colourA) \ Modify the following instruction in DrawCanopyLine \ (part 5) where colourA is the value of A: \ STA dlin28+1 \ LDA colour1R2L,X -> LDA colourA,X LDA colourCycle \ Modify the following instruction in DrawCanopyLine STA dlin50+1 \ (part 2): LDA #&A9 \ STA dlin50 \ LDA H -> LDA #colourCycle \ \ as the opcode for the LDA #n instruction is &A9 RTS \ Return from the subroutine .modd3 \ If we get here then colourLogic is non-zero LDA #&11 \ Set A to the opcode for the ORA (P),Y instruction STA dlin24 \ Modify the following instruction in DrawCanopyLine \ (part 4): \ \ ORA (P),Y -> ORA (P),Y \ \ i.e. set them back to the default STA dlin29 \ Modify the following instruction in DrawCanopyLine \ (part 5): \ \ ORA (P),Y -> ORA (P),Y \ \ i.e. set them back to the default STA dlin34 \ Modify the following instruction in DrawCanopyLine \ (part 6): \ \ ORA (P),Y -> ORA (P),Y \ \ i.e. set them back to the default STA dlin51 \ Modify the following instruction in DrawCanopyLine \ (part 8): \ \ ORA (P),Y -> ORA (P),Y \ \ i.e. set them back to the default LDA colourLogic \ If bit 7 of colourLogic is set, i.e. %10000000, jump BMI modd4 \ to modd4 \ If we get here then colourLogic is %01000000 \ Modify the following instructions in DrawCanopyLine \ (parts 2 and 4): LDA #LO(colour2L2R) \ STA dlin2+1 \ LDA #LO(colour1L2R) -> LDA #LO(colour2L2R) STA dlin23+1 \ LDA colour1L2R,X -> LDA colour2L2R,X \ Modify the following instructions in DrawCanopyLine \ (parts 2 and 5): LDA #LO(colour2R2L) \ STA dlin4+1 \ LDA #LO(colour1R2L) -> LDA #LO(colour2R2L) STA dlin28+1 \ LDA colour1R2L,X -> LDA colour2R2L,X \ Modify the following instructions in DrawCanopyLine \ (part 8): LDA #%10000000 \ STA dlin47+1 \ LDA #%00001000 -> LDA #%10000000 LDA #%00001000 \ STA dlin58+1 \ CMP #%00000000 -> CMP #%00001000 LDA #%10000000 \ STA dlin59+1 \ LDA #%00001000 -> LDA #%10000000 LDA #00000000 \ STA dlin62+1 \ CMP #%00010000 -> CMP #%00000000 LDA #%00010000 \ STA dlin63+1 \ LDA #%00000001 -> LDA #%00010000 BNE modd5 \ Jump down to modd5 (this BNE is effectively a JMP as \ A is never zero) .modd4 \ If we get here then colourLogic is %10000000 \ Modify the following instructions in DrawCanopyLine \ (parts 2 and 4): LDA #LO(colour1L2R) \ STA dlin2+1 \ LDA #LO(colour1L2R) -> LDA #LO(colour1L2R) STA dlin23+1 \ LDA colour1L2R,X -> LDA colour1L2R,X \ \ i.e. set them back to the default \ Modify the following instructions in DrawCanopyLine \ (parts 2 and 5): LDA #LO(colour1R2L) \ STA dlin4+1 \ LDA #LO(colour1R2L) -> LDA #LO(colour1R2L) STA dlin28+1 \ LDA colour1R2L,X -> LDA colour1R2L,X \ \ i.e. set them back to the default \ Modify the following instructions in DrawCanopyLine \ (part 8): LDA #%00001000 \ STA dlin47+1 \ LDA #%00001000 -> LDA #%00001000 LDA #%00000000 \ STA dlin58+1 \ CMP #%00000000 -> CMP #%00000000 LDA #%00001000 \ STA dlin59+1 \ LDA #%00001000 -> LDA #%00001000 LDA #%00010000 \ STA dlin62+1 \ CMP #%00010000 -> CMP #%00010000 LDA #%00000001 \ STA dlin63+1 \ LDA #%00000001 -> LDA #%00000001 \ \ i.e. set them back to the default .modd5 \ Modify the following instructions in DrawCanopyLine \ (part 2): LDA #&A5 \ STA dlin50 \ LDA H -> LDA H LDA #&79 \ STA dlin50+1 \ as the opcode for the LDA n instruction is &A5 RTS \ Return from the subroutine
Name: ClipBestEndOfLine [Show more] Type: Subroutine Category: Drawing lines Summary: Clip a line at the start or end point, depending on which is best
Context: See this subroutine on its own page References: This subroutine is called as follows: * DrawClippedLine (Part 4 of 6) calls ClipBestEndOfLine * ClipStartOfLine (Part 1 of 5) calls via AbortLine * ClipStartOfLine (Part 5 of 5) calls via AbortLine

Arguments: (RR R) The x-coordinate of the line's start point (SS S) The y-coordinate of the line's start point TT The clipping requirements for the start point (QQ W) The x-coordinate of the line's end point (H G) The y-coordinate of the line's end point UU The clipping requirements for the end point
Returns: (RR R) The x-coordinate of the line's new start point, clipped to fit on-screen (SS S) The y-coordinate of the line's new start point, clipped to fit on-screen T Relative magnitude of line's vector |x-delta| U Relative magnitude of line's vector |y-delta| W Max/min x-coordinate for the new end of the line G Max/min y-coordinate for the new end of the line
Other entry points: AbortLine Abort drawing this line
.ClipBestEndOfLine LDA TT \ If TT and UU have a set bit in common, this means that AND UU \ both points are off-screen in the same direction, so BNE AbortLine \ the line can't possibly cross the visible part of the \ screen at any point, so jump to AbortLine to abort the \ drawing of this line LDA SS \ If the start point's y-coordinate is positive, jump to BPL clen1 \ clen1 to skip the following instruction EOR #&FF \ Flip the start point's y-coordinate so it's positive, \ so: \ \ A = |start_y| .clen1 STA maxCoord \ Store the start point's y-coordinate in maxCoord, so: \ \ maxCoord = |start_y| LDA RR \ If the start point's x-coordinate is positive, jump to BPL clen2 \ clen2 to skip the following instruction EOR #&FF \ Flip the start point's x-coordinate so it's positive, \ so: \ \ A = |start_x| .clen2 CMP maxCoord \ If the start point's x-coordinate is < maxCoord, jump BCC clen3 \ to clen3 to skip the following instruction STA maxCoord \ The start point's x-coordinate is >= maxCoord, so \ update maxCoord so it contains the maximum value: \ \ maxCoord = max(|start_x|, |start_y|) .clen3 LDA QQ \ If the end point's x-coordinate is positive, jump to BPL clen4 \ clen4 to skip the following instruction EOR #&FF \ Flip the end point's x-coordinate so it's positive, \ so: \ \ A = |end_x| .clen4 CMP maxCoord \ If |end_x| >= maxCoord, the start point has a smaller BCS ClipStartOfLine \ x-coordinate magnitude which means it is closer to the \ origin, so jump to ClipStartOfLine to clip the line at \ the start point, as it's a better candidate for \ clipping LDA H \ If the end point's y-coordinate is positive, jump to BPL clen5 \ clen5 to skip the following instruction EOR #&FF \ Flip the end point's y-coordinate so it's positive, \ so: \ \ A = |end_y| .clen5 CMP maxCoord \ If |end_y| >= maxCoord, the start point has a smaller BCS ClipStartOfLine \ y-coordinate magnitude which means it is closer to the \ origin, so jump to ClipStartOfLine to clip the line at \ the start point, as it's a better candidate for \ clipping JSR SwapLinePoints \ Otherwise the end point is the better candidate for \ clipping, so copy the end point's coordinates and \ clipping information into the start point LDA V \ We've just flipped the end point to the start, so flip EOR #%11000000 \ bits 6 and 7 in V to reverse the line direction in STA V \ both axes JMP ClipStartOfLine \ Jump to ClipStartOfLine to clip the line at the new \ start point .AbortLine TSX \ Remove two bytes from the top of the stack, which INX \ removes the return address that was put there by the INX \ last JSR instruction. This means that the RTS below TXS \ jumps two levels up the call stack, rather than one, \ so we return to the subroutine that called the \ subroutine that called ClipBestEndOfLine. The only \ call to ClipBestEndOfLine is in the DrawClippedLine \ routine, and the only call to DrawClippedLine is in \ DrawCanopyView, so this ensures that the RTS below \ returns us to DrawCanopyView without drawing the line \ and without leaving anything on the stack \ \ In short, this makes the RTS abort the drawing of this \ line RTS \ Return to the DrawCanopyView routine
Name: ClipStartOfLine (Part 1 of 5) [Show more] Type: Subroutine Category: Drawing lines Summary: Check whether the line is completely off-screen
Context: See this subroutine on its own page References: This subroutine is called as follows: * ClipBestEndOfLine calls ClipStartOfLine * DrawClippedLine (Part 4 of 6) calls ClipStartOfLine

The commentary in this routine is a work in progress.
Arguments: (RR R) The x-coordinate of the line's start point (SS S) The y-coordinate of the line's start point (I T) The line's |x-delta| (J U) The line's |y-delta| TT The clipping requirements for the start point Bit 4 Bit 5 Off top of screen 0 1 On-screen 0 0 Off bottom of screen 1 0 Bit 6 Bit 7 Off right of screen 0 1 On-screen 0 0 Off left of screen 1 0 V Direction of the line: * Bit 7 is the direction of the x-delta * Bit 6 is the direction of the y-delta Direction is like a clock, so positive (clear) is up and right
Returns: R The x-coordinate of the line's start point, clipped to fit on-screen S The y-coordinate of the line's start point, clipped to fit on-screen V Bit 0 is set to indicate the line has been clipped xTemp1Lo If the line has been clipped, contains a copy of R yTemp1Lo If the line has been clipped, contains a copy of S
.ClipStartOfLine LDA S \ Set (SS S) = (SS S) + 4 CLC \ ADC #4 \ starting with the low bytes STA S BCC clip1 \ If the addition didn't overflow, jump to clip1 to \ skip the following instruction INC SS \ And then we increment the high byte if the addition \ overflowed \ \ So we have now added an extra 4 to the start \ y-coordinate: \ \ (SS S) = (SS S) + 4 \ \ We add this to make the x- and y-coordinate consistent \ in terms of range, as the canopy's x-coordinates start \ at 4, but the y-coordinates start at 0. We remove this \ additional 4 at the end, after clipping the line .clip1 \ We now do some simple checks to weed out lines that \ are entirely off to one side of the screen LDA TT \ Set A = the clipping requirements for the start point BIT V \ If bit 7 of V is clear, jump to clip2 as the x-delta BPL clip2 \ of the line is positive, or to the right \ Bit 7 of V is set, so the x-delta of the line is \ negative, or to the left AND #%01000000 \ If bit 6 of TT is set, then the start point is off the BNE AbortLine \ left of the screen and the line direction is also to \ the left, so jump to AbortLine to stop drawing the \ line as it must be entirely off-screen BEQ clip3 \ Jump to clip3 to move on to the next check (this BEQ \ is effectively a JMP, as A is always zero) .clip2 \ If we get here then the x-delta of the line is \ positive, or to the right AND #%10000000 \ If bit 7 of TT is set, then the start point is off the BNE AbortLine \ right of the screen and the line direction is also to \ the right, so jump to AbortLine to stop drawing the \ line as it must be entirely off-screen BEQ clip3 \ Jump to clip3 to move on to the next check (this BEQ \ is effectively a JMP, as A is always zero) .clip3 LDA TT \ Set A = the clipping requirements for the start point BVC clip4 \ If bit 6 of V is clear, jump to clip4 as the y-delta \ of the line is positive, or up \ Bit 6 of V is set, so the y-delta of the line is \ negative, or down AND #%00010000 \ If bit 4 of TT is set, then the start point is off the BNE AbortLine \ bottom of the screen and the line direction is also \ down, so jump to AbortLine to stop drawing the \ line as it must be entirely off-screen BEQ clip5 \ Jump to clip5 to move on to the next check (this BEQ \ is effectively a JMP, as A is always zero) .clip4 \ If we get here then the y-delta of the line is \ positive, or up AND #%00100000 \ If bit 5 of TT is set, then the start point is off the BNE AbortLine \ top of the screen and the line direction is also up, \ so jump to AbortLine to stop drawing the line as it \ must be entirely off-screen \ If we get here then the line might have an on-screen \ element, so we now move on to the actual clipping
Name: ClipStartOfLine (Part 2 of 5) [Show more] Type: Subroutine Category: Drawing lines Summary: Work out the deltas depending on the direction of slope
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

If slope is / (UU TT) = |start_x + start_y - 159| Clear bit 7 of WW If slope is \ (UU TT) = |start_y - start_x| Set bit 7 of WW (Q P) = |x-delta| + |y-delta| + 2
.clip5 LDA V \ If either bit 7 or bit 6 of V are set, but not both, ASL A \ then if we EOR them we will get a 1, so this jumps to EOR V \ clip6 if this is the case BMI clip6 \ If we get here then bits 6 and 7 of V are the same, so \ the line slope is up-right or down-left, i.e. / LDA #0 \ Set WW = 0, so bit 7 is clear STA WW LDA R \ Set (UU TT) = (RR R) + (SS S) CLC \ ADC S \ starting with the low bytes STA TT LDA RR \ And then the high bytes ADC SS STA UU LDA TT \ Set (UU TT) = (UU TT) - 159 SEC \ SBC #159 \ starting with the low bytes STA TT LDA UU \ And then the high bytes, so we now have: SBC #0 \ STA UU \ (UU TT) = start_x + start_y - 159 JMP clip7 .clip6 \ If we get here then bits 6 and 7 of V are different, \ so the line slope is up-right or down-left, i.e. \ LDA #%10000000 \ Set WW = %10000000, so bit 7 is set STA WW LDA S \ Set (UU TT) = (SS S) - (RR R) SEC \ SBC R \ starting with the low bytes STA TT LDA SS \ And then the high bytes, so now we have: SBC RR \ STA UU \ (UU TT) = start_y - start_x .clip7 BPL clip8 \ If (UU TT) is positive, jump to clip8 LDA #0 \ Set (UU TT) = 0 - (UU TT) SEC \ SBC TT \ starting with the low bytes STA TT LDA #0 \ And then the high bytes, so (UU TT) is now positive, SBC UU \ so: STA UU \ \ (UU TT) = |UU TT| \ = |start_x + start_y - 159| if slope is / \ |start_y - start_x| if slope is \ .clip8 LDA T \ Set (Q P) = (I T) + (J U) CLC \ ADC U \ starting with the low bytes STA P LDA I \ And then the high bytes, so: ADC J \ STA Q \ (Q P) = |x-delta| + |y-delta| LDA P \ Set (Q P) = (Q P) + 2 CLC \ ADC #2 \ starting with the low bytes STA P BCC clip9 \ If the addition didn't overflow, jump to clip9 to \ skip the following instruction INC Q \ Increment the high byte in Q
Name: ClipStartOfLine (Part 3 of 5) [Show more] Type: Subroutine Category: Drawing lines Summary: Work out the deltas depending on the steepness of slope
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

Shallow horizontal: Bit 7 of HH = direction of the y-delta (PP N) = |x-delta| + 1 Clear bit 6 of WW Steep vertical: Bit 7 of HH = direction of the x-delta (PP N) = |y-delta| + 1 Set bit 6 of WW Set (RR R) = (SS S), so start_x is set to start_y
.clip9 LDA #0 \ Set K = 0, though this has no effect as we don't use STA K \ K in the following LDA I \ If I < J, then (I T) < (J U), so jump to clip11 CMP J BCC clip11 BNE clip10 \ If I > J, then (I T) > (J U), so jump to clip10 \ If we get here then I = J LDA T \ If T < U, then (I T) < (J U), so jump to clip11 CMP U BCC clip11 .clip10 \ If we get here, then (I T) >= (J U), which is the same \ as |x-delta| >= |y-delta|, so this is a shallow \ horizontal slope LDA V \ Set HH = V STA HH \ \ so bit 7 of HH is set to bit 7 of V, i.e. the \ direction of the y-delta LDA T \ Set (PP N) = (I T) + 1 CLC \ ADC #1 \ starting with the low bytes STA N LDA I \ And then the high bytes, so: ADC #0 \ STA PP \ (PP N) = |x-delta| + 1 JMP clip12 \ Jump down to clip12 to move on to the next stage .clip11 \ If we get here, then (I T) < (J U), which is the same \ as |x-delta| < |y-delta|, so this is a steep vertical \ slope LDA V \ Set HH = V << 1 ASL A \ STA HH \ so bit 7 of HH is set to bit 6 of V, i.e. the \ direction of the x-delta LDA U \ Set (PP N) = (J U) + 1 CLC \ ADC #1 \ starting with the low bytes STA N LDA J \ And then the high bytes, so: ADC #0 \ STA PP \ (PP N) = |y-delta| + 1 LDA WW \ Set bit 6 of WW ORA #%01000000 STA WW LDA S \ Set (RR R) = (SS S) STA R \ LDA SS \ so the x-coordinate of the line's start point is now STA RR \ the y-coordinate of the start point
Name: ClipStartOfLine (Part 4 of 5) [Show more] Type: Subroutine Category: Drawing lines Summary: Calculate where to clip the line
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

Division algorithm of some kind, I think it's this for a shallow horizontal / (QQ G) = (UU TT) * (PP N) / (Q P) = (start_x + start_y - 159) * (|x-delta| + 1) / (|x-delta| + |y-delta|) This calculates the delta to add to the coordinates in the final part, but I haven't got to the bottom of this 1. Double (Q P) and (PP N) until Q >= UU If Q = UU and P < TT, repeat until P >= TT i.e. repeat until (Q P) >= (UU TT) 2. Halve (Q P K) and (PP N VV) until Q < UU i.e. repeat until (Q P K) < (UU TT H) 3. If (Q P K) = 0, done Otherwise add 0.5 to (PP N) using a third byte (so add 128) (UU TT H) = (UU TT 0) - (Q P K) Jump back to step 2 while (UU TT H) >= 2
.clip12 LDA #128 \ Set W = 128, to use in (QQ G W) STA W LDA #0 \ Set VV = 0, to use as the third byte in (PP N VV) STA VV STA K \ Set K = 0, to use as the third byte in (Q P K) STA H \ Set H = 0, to use as the third byte in (UU TT H) STA QQ \ Set QQ = 0, to use in (QQ G W) STA G \ Set G = 0, so now we have: \ \ (QQ G W) = 128 BEQ clip14 \ Jump to clip14 (this BEQ is effectively a JMP as A is \ always zero) .clip13 ASL P \ Set (Q P) = (Q P) * 2 ROL Q ASL N \ Set (PP N) = (PP N) * 2 ROL PP .clip14 LDA Q \ If Q < UU, jump up to clip13 to double the values of CMP UU \ (Q P) and (PP N) until Q >= UU BCC clip13 BNE clip15 \ If Q <> UU, i.e. Q > U, then jump to clip15 LDA P \ If we get here then Q = UU, so if P < TT, jump back to CMP TT \ clip13 to keep on doubling until P >= TT BCC clip13 .clip15 LSR Q \ Set (Q P K) = (Q P K) / 2 ROR P ROR K LSR PP \ Set (PP N VV) = (PP N VV) / 2 ROR N ROR VV .clip16 LDA Q \ If Q < UU, jump down to clip17 CMP UU BCC clip17 BNE clip15 \ If Q <> UU, i.e. Q > U, jump back to clip15 to keep on \ halving until Q < UU or Q = UU LDA P \ If we get here then Q = UU, so if P < TT, jump down to CMP TT \ clip17 BCC clip17 BNE clip15 \ If P <> TT, i.e. P > TT, jump back to clip15 to keep \ on halving until Q < UU, or Q = UU and P <= TT LDA K \ If K <= H, jump down to clip17 CMP H BCC clip17 BEQ clip17 LDA Q \ If (Q P K) <> 0, jump back to clip15 ORA P ORA K BNE clip15 BEQ clip18 \ (Q P K) = 0 so jump to clip18 (this BEQ is effectively \ a JMP as we just passed through a BNE) .clip17 \ The first time we get here, QQ = G = 0 and W = 128, so \ the following sum is (QQ G W) = 128 + (PP N VV) LDA W \ Set (QQ G W) = (QQ G W) + (PP N VV) CLC \ ADC VV \ starting with the low bytes STA W LDA G \ Then the middle bytes ADC N STA G LDA QQ \ And then the high bytes ADC PP STA QQ \ The first time we get here, H = 0, so the \ following sum is (UU TT H) = (UU TT 0) - (Q P K) LDA H \ Set (UU TT H) = (UU TT H) - (Q P K) SEC \ SBC K \ starting with the low bytes STA H LDA TT \ Then the middle bytes SBC P STA TT LDA UU \ And then the high bytes SBC Q STA UU \ The following comparisons jump back to clip16 if \ (UU TT H) >= 2 BNE clip16 \ If UU <> 0, jump back to clip16 LDA TT \ If TT <> 0, jump back to clip16 BNE clip16 LDA H \ If H >= 2, jump back to clip16 CMP #2 BCS clip16 \ By the time we get here, (UU TT H) is 1 or 0
Name: ClipStartOfLine (Part 5 of 5) [Show more] Type: Subroutine Category: Drawing lines Summary: Move the start point to the clipped position and return it
Context: See this subroutine on its own page References: No direct references to this subroutine in this source file

If bit 7 of HH clear, (RR R) and (SS S) = (RR R) + (QQ G) If bit 7 of HH set, (RR R) and (SS S) = (RR R) - (QQ G) If bits 6 and 7 of WW clear, (SS S) = 159 - (RR R) If bit 6 of WW is set, bit 7 clear, (RR RR) = 159 - (SS S) S = S - 4 to undo the bump in the first part Confirm (R, S) is on-screen and return it as the new start point
.clip18 \ By the time we get here, (QQ G W) contains the delta \ that we need to apply to the start point's x- or \ y-coordinate, multiplied by 256 so the lowest byte is \ the fractional part \ If bit 7 of W is set, that means the fractional part \ is at least 0.5, so we add 1 to the integral part in \ (QQ G) to round it up when going from the fractional \ (QQ G W) to the integer (QQ G) LDA G \ We start with the low byte, rotating bit 7 of W into ROL W \ the C flag which we then add with ADC #0 ADC #0 STA G BCC clip19 \ If the addition didn't overflow, jump to clip19 to \ skip the following instruction INC QQ \ And then we increment the high byte if the addition \ overflowed .clip19 LDA HH \ If bit 7 of HH is set, jump to clip20 to skip the BMI clip20 \ following \ Bit 7 of HH is clear, so we add the delta in (QQ G) \ to both coordinates to move the whole line so the \ start coordinate is on the edge of the screen LDA R \ Set (RR R) and (S SS) = (RR R) + (QQ G) CLC \ ADC G \ starting with the low bytes STA R STA S LDA RR \ And then the high bytes ADC QQ STA RR STA SS JMP clip21 \ Jump to clip21 to skip the following .clip20 \ Bit 7 of HH is set, so we subtract the delta in (QQ G) \ from both coordinates to move the whole line so the \ start coordinate is on the edge of the screen LDA R \ Set (RR R) and (SS S) = (RR R) - (QQ G) SEC \ SBC G \ starting with the low bytes STA R STA S LDA RR \ And then the high bytes SBC QQ STA RR STA SS .clip21 BIT WW \ If bit 7 of WW is set, jump to clip23 to skip all of BMI clip23 \ the following and return the final coordinates BVS clip22 \ If bit 6 of WW is set, jump to clip22 to skip the \ following \ Bits 6 and 7 of WW are both clear, so we update the \ y-coordinate LDA #159 \ Set (SS S) = 159 - (RR R) SEC \ SBC R \ starting with the low bytes STA S LDA #0 \ And then the high bytes SBC RR STA SS JMP clip23 \ Jump to clip23 to skip the following .clip22 \ Bit 6 of WW is set, so we update the x-coordinate LDA #159 \ Set (RR RR) = 159 - (SS S) SEC \ SBC S \ starting with the low bytes STA R LDA #0 \ And then the high bytes SBC SS STA RR .clip23 LDA RR \ If RR is non-zero, jump to clip24 to abort the line as BNE clip24 \ the (RR R) x-coordinate is off-screen LDA R \ If R >= 156, jump to clip24 to abort the line as the CMP #156 \ (RR R) x-coordinate is off the right of the screen BCS clip24 CMP #4 \ If R < 4, jump to clip24 to abort the line as the BCC clip24 \ (RR R) x-coordinate is off the left of the screen LDA S \ Set S = S - 4 SEC \ SBC #4 \ which subtracts the 4 that we added at the start of STA S \ the routine BCC clip24 \ If the subtraction just underflowed, jump to clip24 to \ abort the line as the (SS S) y-coordinate is off the \ bottom of the screen CMP #152 \ If S > 152, jump to clip24 to abort the line as the BCS clip24 \ (SS S) y-coordinate is off the top of the screen \ If we get here then we have successfully clipped the \ start point to the edge of the screen LDA #1 \ Set bit 0 of the line direction in V to indicate that ORA V \ the line has been clipped STA V LDA R \ Copy the clipped (R, S) pixel coordinate into STA xTemp1Lo \ (xTemp1Lo, yTemp1Lo) so we can access it in the LDA S \ DrawCanopyLine routine STA yTemp1Lo RTS \ Return from the subroutine .clip24 JMP AbortLine \ Jump to AbortLine to abort drawing this line and \ return from the subroutine using a tail call
Name: SwapLinePoints [Show more] Type: Subroutine Category: Drawing lines Summary: Copy an end point into a start point
Context: See this subroutine on its own page References: This subroutine is called as follows: * ClipBestEndOfLine calls SwapLinePoints * DrawClippedLine (Part 4 of 6) calls SwapLinePoints

Arguments: (QQ W) The x-coordinate of the line's end point (H G) The y-coordinate of the line's end point UU The clipping requirements for the end point
Returns: (RR R) Gets set to the x-coordinate above (SS S) Gets set to the y-coordinate above TT Gets set to the clipping requirements above
.SwapLinePoints LDA W \ Set (RR R) = (QQ W) STA R LDA QQ STA RR LDA G \ Set (SS S) = (H G) STA S LDA H STA SS LDA UU \ Set TT = UU STA TT RTS \ Return from the subroutine