Skip to navigation


Version analysis of LOIN (Part 4 of 7) / LOINQ (Part 4 of 7)

This code appears in the following versions (click to see it in the source code):

Code variations between these versions are shown below.

Code variation 1 of 10A variation in the comments only

Tap on a block to expand it, and tap it again to revert.

Name: LOIN (Part 4 of 7)
Name: LOINQ (Part 4 of 7)
Type: Subroutine Category: Drawing lines Summary: Draw a shallow line going right and down or left and up Deep dive: Bresenham's line algorithm
This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * The line is going right and down (no swap) or left and up (swap)

Code variation 2 of 10A variation in the comments only

Tap on a block to expand it, and tap it again to revert.

* X1 < X2 and Y1-1 <= Y2
* X1 < X2 and Y1 <= Y2
* Draw from (X1, Y1) at top left to (X2, Y2) at bottom right, omitting the first pixel

Code variation 3 of 10A variation in the comments only

This variation is blank in the Cassette, Disc (flight), Disc (docked) and Electron versions.

This routine looks complex, but that's because the loop that's used in the BBC Micro cassette and disc versions has been unrolled to speed it up. The algorithm is unchanged, it's just a lot longer.
.DOWN

Code variation 4 of 10Related to the screen mode

This variation is blank in the Cassette, Disc (flight), Disc (docked) and Electron versions.

LDA #%10001000 \ Modify the value in the LDA instruction at LI200 below AND COL \ to contain a pixel mask for the first pixel in the STA LI200+1 \ 4-pixel byte, in the colour COL, so that it draws in \ the correct colour LDA #%01000100 \ Modify the value in the LDA instruction at LI210 below AND COL \ to contain a pixel mask for the second pixel in the STA LI210+1 \ 4-pixel byte, in the colour COL, so that it draws in \ the correct colour LDA #%00100010 \ Modify the value in the LDA instruction at LI220 below AND COL \ to contain a pixel mask for the third pixel in the STA LI220+1 \ 4-pixel byte, in the colour COL, so that it draws in \ the correct colour LDA #%00010001 \ Modify the value in the LDA instruction at LI230 below AND COL \ to contain a pixel mask for the fourth pixel in the STA LI230+1 \ 4-pixel byte, in the colour COL, so that it draws in \ the correct colour LDA SC \ Set SC(1 0) = SC(1 0) - 248 SBC #248 STA SC LDA SC+1 SBC #0 STA SC+1 TYA \ Set bits 3-7 of Y, which contains the pixel row within EOR #%11111000 \ the character, and is therefore in the range 0-7, so TAY \ this does Y = 248 + Y \ \ We therefore have the following: \ \ SC(1 0) + Y = SC(1 0) - 248 + 248 + Y \ = SC(1 0) + Y \ \ so the screen location we poke hasn't changed, but Y \ is now a larger number and SC is smaller. This means \ we can increment Y to move down a line, as per usual, \ but we can test for when it reaches the bottom of the \ character block with a simple BEQ rather than checking \ whether it's reached 8, so this appears to be a code \ optimisation \ \ If it helps, you can think of Y as being a negative \ number that we are incrementing towards zero as we \ move along the line - we just need to alter the value \ of SC so that SC(1 0) + Y points to the right address

Code variation 5 of 10Related to the screen mode

This variation is blank in the 6502 Second Processor and Master versions.

LDA SWAP \ If SWAP = 0 then we didn't swap the coordinates above, BEQ LI9 \ so jump down to LI9 to skip plotting the first pixel DEX \ Decrement the counter in X because we're about to plot \ the first pixel .LIL3 \ We now loop along the line from left to right, using X \ as a decreasing counter, and at each count we plot a \ single pixel using the pixel mask in R LDA R \ Fetch the pixel byte from R EOR (SC),Y \ Store R into screen memory at SC(1 0), using EOR STA (SC),Y \ logic so it merges with whatever is already on-screen .LI9 LSR R \ Shift the single pixel in R to the right to step along \ the x-axis, so the next pixel we plot will be at the \ next x-coordinate along BCC LI10 \ If the pixel didn't fall out of the right end of R \ into the C flag, then jump to LI10 ROR R \ Otherwise we need to move over to the next character \ block, so first rotate R right so the set C flag goes \ back into the left end, giving %10000000 LDA SC \ Add 8 to SC, so SC(1 0) now points to the next ADC #8 \ character along to the right STA SC

Code variation 6 of 10Related to the screen mode

This variation is blank in the Cassette, Disc (flight), Disc (docked), 6502 Second Processor and Master versions.

BCC LI10 \ If the addition of the low bytes overflowed, increment INC SC+1 \ the high byte of SC(1 0)

Code variation 7 of 10Related to the screen mode

This variation is blank in the 6502 Second Processor and Master versions.

.LI10 LDA S \ Set S = S + Q to update the slope error ADC Q STA S BCC LIC3 \ If the addition didn't overflow, jump to LIC3 INY \ Otherwise we just overflowed, so increment Y to move \ to the pixel line below CPY #8 \ If Y < 8 we are still within the same character block, BNE LIC3 \ so skip to LIC3

Code variation 8 of 10Related to the screen mode

This variation is blank in the 6502 Second Processor and Master versions.

Tap on a block to expand it, and tap it again to revert.

INC SCH \ Otherwise we need to move down into the character LDY #0 \ block below, so increment the high byte of the screen \ address and set the pixel line to the first line in \ that character block
\ We now need to move down into the character block \ below, and each character row in screen memory takes \ up &140 bytes (&100 for the visible part and &20 for \ each of the blank borders on the side of the screen), \ so that's what we need to add to SC(1 0) \ \ We also know the C flag is set, as we didn't take the \ BCC above, so we can add &13F in order to get the \ correct result LDA SC \ Set SC(1 0) = SC(1 0) + &140 ADC #&3F \ STA SC \ Starting with the low bytes LDA SC+1 \ And then adding the high bytes ADC #&01 STA SC+1 LDY #0 \ Set the pixel line to the first line in that character \ block

Code variation 9 of 10Related to the screen mode

This variation is blank in the 6502 Second Processor and Master versions.

.LIC3 DEX \ Decrement the counter in X BNE LIL3 \ If we haven't yet reached the right end of the line, \ loop back to LIL3 to plot the next pixel along LDY YSAV \ Restore Y from YSAV, so that it's preserved

Code variation 10 of 10Related to the screen mode

This variation is blank in the Cassette, Disc (flight), Disc (docked) and Electron versions.

\ We now work our way along the line from left to right, \ using X as a decreasing counter, and at each count we \ plot a single pixel using the pixel mask in R LDA SWAP \ If SWAP = 0 then we didn't swap the coordinates above, BEQ LI191 \ so jump down to LI191 to plot the first pixel \ If we get here then we want to omit the first pixel LDA R \ Fetch the pixel byte from R, which we set in part 2 to \ the horizontal pixel number within the character block \ where the line starts (so it's 0, 1, 2 or 3) BEQ LI200+6 \ If R = 0, jump to LI200+6 to start plotting from the \ second pixel in this byte (LI200+6 points to the DEX \ instruction after the EOR/STA instructions, so the \ pixel doesn't get plotted but we join at the right \ point to decrement X correctly to plot the next three) CMP #2 \ If R < 2 (i.e. R = 1), jump to LI210+6 to skip the BCC LI210+6 \ first two pixels but plot the next two CLC \ Clear the C flag so it doesn't affect the additions \ below BEQ LI220+6 \ If R = 2, jump to LI220+6 to skip the first three \ pixels but plot the last one BNE LI230+6 \ If we get here then R must be 3, so jump to LI230+6 to \ skip plotting any of the pixels, but making sure we \ join the routine just after the plotting instructions .LI191 DEX \ Decrement the counter in X because we're about to plot \ the first pixel LDA R \ Fetch the pixel byte from R, which we set in part 2 to \ the horizontal pixel number within the character block \ where the line starts (so it's 0, 1, 2 or 3) BEQ LI200 \ If R = 0, jump to LI200 to start plotting from the \ first pixel in this byte CMP #2 \ If R < 2 (i.e. R = 1), jump to LI210 to start plotting BCC LI210 \ from the second pixel in this byte CLC \ Clear the C flag so it doesn't affect the additions \ below BEQ LI220 \ If R = 2, jump to LI220 to start plotting from the \ third pixel in this byte BNE LI230 \ If we get here then R must be 3, so jump to LI130 to \ start plotting from the fourth pixel in this byte \ (this BNE is effectively a JMP as by now R is never \ zero) .LI200 LDA #%10001000 \ Set a mask in A to the first pixel in the 4-pixel byte \ (note that this value is modified by the code at the \ start of this section to be a bit mask for the colour \ in COL) EOR (SC),Y \ Store A into screen memory at SC(1 0), using EOR STA (SC),Y \ logic so it merges with whatever is already on-screen DEX \ Decrement the counter in X BEQ LIEX \ If we have just reached the right end of the line, \ jump to LIEX to return from the subroutine LDA S \ Set S = S + Q to update the slope error ADC Q STA S BCC LI210 \ If the addition didn't overflow, jump to LI210 CLC \ Otherwise we just overflowed, so clear the C flag and INY \ increment Y to move to the pixel line below BEQ LI201 \ If Y is zero we need to move down into the character \ block below, so jump to LI201 to increment the screen \ address accordingly (jumping back to LI210 afterwards) .LI210 LDA #%01000100 \ Set a mask in A to the second pixel in the 4-pixel \ byte (note that this value is modified by the code at \ the start of this section to be a bit mask for the \ colour in COL) EOR (SC),Y \ Store A into screen memory at SC(1 0), using EOR STA (SC),Y \ logic so it merges with whatever is already on-screen DEX \ Decrement the counter in X BEQ LIEX \ If we have just reached the right end of the line, \ jump to LIEX to return from the subroutine LDA S \ Set S = S + Q to update the slope error ADC Q STA S BCC LI220 \ If the addition didn't overflow, jump to LI220 CLC \ Otherwise we just overflowed, so clear the C flag and INY \ increment Y to move to the pixel line below BEQ LI211 \ If Y is zero we need to move down into the character \ block below, so jump to LI211 to increment the screen \ address accordingly (jumping back to LI220 afterwards) .LI220 LDA #%00100010 \ Set a mask in A to the third pixel in the 4-pixel byte \ (note that this value is modified by the code at the \ start of this section to be a bit mask for the colour \ in COL) EOR (SC),Y \ Store A into screen memory at SC(1 0), using EOR STA (SC),Y \ logic so it merges with whatever is already on-screen DEX \ Decrement the counter in X BEQ LIEX2 \ If we have just reached the right end of the line, \ jump to LIEX2 to return from the subroutine LDA S \ Set S = S + Q to update the slope error ADC Q STA S BCC LI230 \ If the addition didn't overflow, jump to LI230 CLC \ Otherwise we just overflowed, so clear the C flag and INY \ increment Y to move to the pixel line below BEQ LI221 \ If Y is zero we need to move down into the character \ block below, so jump to LI221 to increment the screen \ address accordingly (jumping back to LI230 afterwards) .LI230 LDA #%00010001 \ Set a mask in A to the fourth pixel in the 4-pixel \ byte (note that this value is modified by the code at \ the start of this section to be a bit mask for the \ colour in COL) EOR (SC),Y \ Store A into screen memory at SC(1 0), using EOR STA (SC),Y \ logic so it merges with whatever is already on-screen LDA S \ Set S = S + Q to update the slope error ADC Q STA S BCC LI240 \ If the addition didn't overflow, jump to LI240 CLC \ Otherwise we just overflowed, so clear the C flag and INY \ increment Y to move to the pixel line below BEQ LI231 \ If Y is zero we need to move down into the character \ block below, so jump to LI231 to increment the screen \ address accordingly (jumping back to LI240 afterwards) .LI240 DEX \ Decrement the counter in X BEQ LIEX2 \ If we have just reached the right end of the line, \ jump to LIEX2 to return from the subroutine LDA SC \ Add 8 to SC, so SC(1 0) now points to the next ADC #8 \ character along to the right STA SC BCC LI200 \ If the addition didn't overflow, jump back to LI200 \ to plot the next pixel INC SC+1 \ Otherwise the low byte of SC(1 0) just overflowed, so \ increment the high byte SC+1 as we just crossed over \ into the right half of the screen CLC \ Clear the C flag to avoid breaking any arithmetic BCC LI200 \ Jump back to LI200 to plot the next pixel .LI201 INC SC+1 \ If we get here then we need to move down into the INC SC+1 \ character block below, so we increment the high byte LDY #248 \ of the screen twice (as there are two pages per screen \ row) and set the pixel line to the first line in that \ character block (as we subtracted 248 from SC above) BNE LI210 \ Jump back to the instruction after the BMI that called \ this routine .LI211 INC SC+1 \ If we get here then we need to move down into the INC SC+1 \ character block below, so we increment the high byte LDY #248 \ of the screen twice (as there are two pages per screen \ row) and set the pixel line to the first line in that \ character block (as we subtracted 248 from SC above) BNE LI220 \ Jump back to the instruction after the BMI that called \ this routine .LI221 INC SC+1 \ If we get here then we need to move down into the INC SC+1 \ character block below, so we increment the high byte LDY #248 \ of the screen twice (as there are two pages per screen \ row) and set the pixel line to the first line in that \ character block (as we subtracted 248 from SC above) BNE LI230 \ Jump back to the instruction after the BMI that called \ this routine .LI231 INC SC+1 \ If we get here then we need to move down into the INC SC+1 \ character block below, so we increment the high byte LDY #248 \ of the screen twice (as there are two pages per screen \ row) and set the pixel line to the first line in that \ character block (as we subtracted 248 from SC above) BNE LI240 \ Jump back to the instruction after the BMI that called \ this routine .LIEX2
 RTS                    \ Return from the subroutine