This code appears in the following versions (click to see it in the source code):
Code variations between these versions are shown below.
Type: Subroutine Category: Drawing lines Summary: Draw a steep line going up and right or down and left 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 up and right (no swap) or down and left (swap) * X1 >= X2 and Y1 >= Y2 * Draw from (X1, Y1) at bottom left to (X2, Y2) at top right, omitting the first pixel
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.
Other entry points: HL6 Contains an RTS
.LFT
LDA SWAP \ If SWAP = 0 then we didn't swap the coordinates above, BEQ LI18 \ so jump down to LI18 to skip plotting the first pixel DEX \ Decrement the counter in X because we're about to plot \ the first pixel .LIL6 LDA R \ Fetch the pixel byte from R EOR (SC),Y \ Store R into screen memory at SC(1 0), using EOR STA (SC),Y \ logic so it merges with whatever is already on-screen .LI18 DEY \ Decrement Y to step up along the y-axis BPL LI19 \ If Y is positive we are still within the same \ character block, so skip to LI19
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.
.LI19 LDA S \ Set S = S + P to update the slope error ADC P STA S BCC LIC6 \ If the addition didn't overflow, jump to LIC6 ASL R \ Otherwise we just overflowed, so shift the single \ pixel in R to the left, so the next pixel we plot \ will be at the previous x-coordinate BCC LIC6 \ If the pixel didn't fall out of the left end of R \ into the C flag, then jump to LIC6 ROL R \ Otherwise we need to move over to the next character \ block, so first rotate R left so the set C flag goes \ back into the right end, giving %0000001 LDA SC \ Subtract 7 from SC, so SC(1 0) now points to the SBC #7 \ previous character along to the left STA SC
This variation is blank in the Cassette, Disc (flight), Disc (docked), 6502 Second Processor and Master versions.
BCS P%+4 \ If the subtraction of the low bytes of SC underflowed, DEC SCH \ decrement the high byte
CLC \ Clear the C flag so it doesn't affect the additions \ below .LIC6 DEX \ Decrement the counter in X BNE LIL6 \ If we haven't yet reached the left end of the line, \ loop back to LIL6 to plot the next pixel along LDY YSAV \ Restore Y from YSAV, so that it's preserved .HL6 RTS \ Return from the subroutine
This variation is blank in the Cassette, Disc (flight), Disc (docked) and Electron versions.
LDA SWAP \ If SWAP = 0 then we didn't swap the coordinates above, BEQ LI291 \ so jump down to LI291 to plot the first pixel TYA \ Fetch bits 0-2 of the y-coordinate, so Y contains the AND #7 \ y-coordinate mod 8 TAY \ \ So Y is the pixel row within the character block where \ we want to start drawing BNE P%+5 \ If Y = 0, jump to LI407+8 to start plotting from the JMP LI407+8 \ pixel above the top row of this character block \ (LI407+8 points to the DEX instruction after the \ EOR/STA instructions, so the pixel at row 0 doesn't \ get plotted but we join at the right point to \ decrement X and Y correctly to continue plotting from \ the character row above) CPY #2 \ If Y < 2 (i.e. Y = 1), jump to LI406+8 to start BCS P%+5 \ plotting from row 0 of this character block, missing JMP LI406+8 \ out row 1 CLC \ Clear the C flag so it doesn't affect the arithmetic \ below BNE P%+5 \ If Y = 2, jump to LI405+8 to start plotting from row JMP LI405+8 \ 1 of this character block, missing out row 2 CPY #4 \ If Y < 4 (i.e. Y = 3), jump to LI404+8 to start BCS P%+5 \ plotting from row 2 of this character block, missing JMP LI404+8 \ out row 3 CLC \ Clear the C flag so it doesn't affect the arithmetic \ below BNE P%+5 \ If Y = 4, jump to LI403+8 to start plotting from row JMP LI403+8 \ 3 of this character block, missing out row 4 CPY #6 \ If Y < 6 (i.e. Y = 5), jump to LI402+8 to start BCS P%+5 \ plotting from row 4 of this character block, missing JMP LI402+8 \ out row 5 CLC \ Clear the C flag so it doesn't affect the arithmetic \ below BEQ P%+5 \ If Y <> 6 (i.e. Y = 7), jump to LI400+8 to start JMP LI400+8 \ plotting from row 6 of this character block, missing \ out row 7 JMP LI401+8 \ Otherwise Y = 6, so jump to LI401+8 to start plotting \ from row 5 of this character block, missing out row 6 .LI291 DEX \ Decrement the counter in X because we're about to plot \ the first pixel TYA \ Fetch bits 0-2 of the y-coordinate, so Y contains the AND #7 \ y-coordinate mod 8 TAY \ \ So Y is the pixel row within the character block where \ we want to start drawing BNE P%+5 \ If Y = 0, jump to LI407 to start plotting from row 0 JMP LI407 \ of this character block CPY #2 \ If Y < 2 (i.e. Y = 1), jump to LI406 to start plotting BCS P%+5 \ from row 1 of this character block JMP LI406 CLC \ Clear the C flag so it doesn't affect the arithmetic \ below BNE P%+5 \ If Y = 2, jump to LI405 to start plotting from row 2 JMP LI405 \ of this character block CPY #4 \ If Y < 4 (i.e. Y = 3), jump to LI404 (via LI404S) to BCC LI404S \ start plotting from row 3 of this character block CLC \ Clear the C flag so it doesn't affect the arithmetic \ below BEQ LI403S \ If Y = 4, jump to LI403 (via LI403S) to start plotting \ from row 4 of this character block CPY #6 \ If Y < 6 (i.e. Y = 5), jump to LI402 (via LI402S) to BCC LI402S \ start plotting from row 5 of this character block CLC \ Clear the C flag so it doesn't affect the arithmetic \ below BEQ LI401S \ If Y = 6, jump to LI401 (via LI401S) to start plotting \ from row 6 of this character block JMP LI400 \ Otherwise Y = 7, so jump to LI400 to start plotting \ from row 7 of this character block .LI410 ASL R \ If we get here then the slope error just overflowed \ after plotting the pixel in LI400, so shift the single \ pixel in R to the left, so the next pixel we plot will \ be at the previous x-coordinate BCC LI401 \ If the pixel didn't fall out of the left end of R \ into the C flag, then jump to LI401 to plot the pixel \ on the next character row up LDA #%00010001 \ Otherwise we need to move over to the next character STA R \ block to the left, so set a mask in R to the fourth \ pixel in the 4-pixel byte LDA SC \ Subtract 8 from SC, so SC(1 0) now points to the SBC #8 \ previous character along to the left STA SC BCS P%+4 \ If the subtraction underflowed, decrement the high DEC SC+1 \ byte in SC(1 0) to move to the previous page in \ screen memory CLC \ Clear the C flag so it doesn't affect the arithmetic \ below .LI401S BCC LI401 \ Jump to LI401 to rejoin the pixel plotting routine \ (this BCC is effectively a JMP as the C flag is clear) .LI411 ASL R \ If we get here then the slope error just overflowed \ after plotting the pixel in LI410, so shift the single \ pixel in R to the left, so the next pixel we plot will \ be at the previous x-coordinate BCC LI402 \ If the pixel didn't fall out of the left end of R \ into the C flag, then jump to LI402 to plot the pixel \ on the next character row up LDA #%00010001 \ Otherwise we need to move over to the next character STA R \ block to the left, so set a mask in R to the fourth \ pixel in the 4-pixel byte LDA SC \ Subtract 8 from SC, so SC(1 0) now points to the SBC #8 \ previous character along to the left STA SC BCS P%+4 \ If the subtraction underflowed, decrement the high DEC SC+1 \ byte in SC(1 0) to move to the previous page in \ screen memory CLC \ Clear the C flag so it doesn't affect the arithmetic \ below .LI402S BCC LI402 \ Jump to LI402 to rejoin the pixel plotting routine \ (this BCC is effectively a JMP as the C flag is clear) .LI412 ASL R \ If we get here then the slope error just overflowed \ after plotting the pixel in LI420, so shift the single \ pixel in R to the left, so the next pixel we plot will \ be at the previous x-coordinate BCC LI403 \ If the pixel didn't fall out of the left end of R \ into the C flag, then jump to LI403 to plot the pixel \ on the next character row up LDA #%00010001 \ Otherwise we need to move over to the next character STA R \ block to the left, so set a mask in R to the fourth \ pixel in the 4-pixel byte LDA SC \ Subtract 8 from SC, so SC(1 0) now points to the SBC #8 \ previous character along to the left STA SC BCS P%+4 \ If the subtraction underflowed, decrement the high DEC SC+1 \ byte in SC(1 0) to move to the previous page in \ screen memory CLC \ Clear the C flag so it doesn't affect the arithmetic \ below .LI403S BCC LI403 \ Jump to LI403 to rejoin the pixel plotting routine \ (this BCC is effectively a JMP as the C flag is clear) .LI413 ASL R \ If we get here then the slope error just overflowed \ after plotting the pixel in LI430, so shift the single \ pixel in R to the left, so the next pixel we plot will \ be at the previous x-coordinate BCC LI404 \ If the pixel didn't fall out of the left end of R \ into the C flag, then jump to LI404 to plot the pixel \ on the next character row up LDA #%00010001 \ Otherwise we need to move over to the next character STA R \ block to the left, so set a mask in R to the fourth \ pixel in the 4-pixel byte LDA SC \ Subtract 8 from SC, so SC(1 0) now points to the SBC #8 \ previous character along to the left STA SC BCS P%+4 \ If the subtraction underflowed, decrement the high DEC SC+1 \ byte in SC(1 0) to move to the previous page in \ screen memory CLC \ Clear the C flag so it doesn't affect the arithmetic \ below .LI404S BCC LI404 \ Jump to LI404 to rejoin the pixel plotting routine \ (this BCC is effectively a JMP as the C flag is clear) .LIEX5 RTS \ Return from the subroutine .LI400 \ Plot a pixel on row 7 of this character block LDA R \ Fetch the pixel byte from R and apply the colour in AND COL \ COL to it 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 LIEX5 \ If we have just reached the right end of the line, \ jump to LIEX5 to return from the subroutine DEY \ Decrement Y to step up along the y-axis LDA S \ Set S = S + P to update the slope error ADC P STA S BCS LI410 \ If the addition overflowed, jump to LI410 to move to \ the pixel in the row above, which returns us to LI401 \ below .LI401 \ Plot a pixel on row 6 of this character block LDA R \ Fetch the pixel byte from R and apply the colour in AND COL \ COL to it 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 LIEX5 \ If we have just reached the right end of the line, \ jump to LIEX5 to return from the subroutine DEY \ Decrement Y to step up along the y-axis LDA S \ Set S = S + P to update the slope error ADC P STA S BCS LI411 \ If the addition overflowed, jump to LI411 to move to \ the pixel in the row above, which returns us to LI402 \ below .LI402 \ Plot a pixel on row 5 of this character block LDA R \ Fetch the pixel byte from R and apply the colour in AND COL \ COL to it 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 LIEX5 \ If we have just reached the right end of the line, \ jump to LIEX5 to return from the subroutine DEY \ Decrement Y to step up along the y-axis LDA S \ Set S = S + P to update the slope error ADC P STA S BCS LI412 \ If the addition overflowed, jump to LI412 to move to \ the pixel in the row above, which returns us to LI403 \ below .LI403 \ Plot a pixel on row 4 of this character block LDA R \ Fetch the pixel byte from R and apply the colour in AND COL \ COL to it 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 LIEX5 \ If we have just reached the right end of the line, \ jump to LIEX5 to return from the subroutine DEY \ Decrement Y to step up along the y-axis LDA S \ Set S = S + P to update the slope error ADC P STA S BCS LI413 \ If the addition overflowed, jump to LI413 to move to \ the pixel in the row above, which returns us to LI404 \ below .LI404 \ Plot a pixel on row 3 of this character block LDA R \ Fetch the pixel byte from R and apply the colour in AND COL \ COL to it 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 LIEX6 \ If we have just reached the right end of the line, \ jump to LIEX6 to return from the subroutine DEY \ Decrement Y to step up along the y-axis LDA S \ Set S = S + P to update the slope error ADC P STA S BCS LI414 \ If the addition overflowed, jump to LI414 to move to \ the pixel in the row above, which returns us to LI405 \ below .LI405 \ Plot a pixel on row 2 of this character block LDA R \ Fetch the pixel byte from R and apply the colour in AND COL \ COL to it 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 LIEX6 \ If we have just reached the right end of the line, \ jump to LIEX6 to return from the subroutine DEY \ Decrement Y to step up along the y-axis LDA S \ Set S = S + P to update the slope error ADC P STA S BCS LI415 \ If the addition overflowed, jump to LI415 to move to \ the pixel in the row above, which returns us to LI406 \ below .LI406 \ Plot a pixel on row 1 of this character block LDA R \ Fetch the pixel byte from R and apply the colour in AND COL \ COL to it 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 LIEX6 \ If we have just reached the right end of the line, \ jump to LIEX6 to return from the subroutine DEY \ Decrement Y to step up along the y-axis LDA S \ Set S = S + P to update the slope error ADC P STA S BCS LI416 \ If the addition overflowed, jump to LI416 to move to \ the pixel in the row above, which returns us to LI407 \ below .LI407 \ Plot a pixel on row 0 of this character block LDA R \ Fetch the pixel byte from R and apply the colour in AND COL \ COL to it 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 LIEX6 \ If we have just reached the right end of the line, \ jump to LIEX6 to return from the subroutine DEC SC+1 \ We just reached the top of the character block, so DEC SC+1 \ decrement the high byte in SC(1 0) twice to point to LDY #7 \ the screen row above (as there are two pages per \ screen row) and set Y to point to the last row in the \ new character block LDA S \ Set S = S + P to update the slope error ADC P STA S BCS P%+5 \ If the addition didn't overflow, jump to LI400 to JMP LI400 \ continue plotting from row 7 of the new character \ block ASL R \ If we get here then the slope error just overflowed \ after plotting the pixel in LI407 above, so shift the \ single pixel in R to the left, so the next pixel we \ plot will be at the previous x-coordinate BCS P%+5 \ If the pixel didn't fall out of the left end of R JMP LI400 \ into the C flag, then jump to LI400 to continue \ plotting from row 7 of the new character block LDA #%00010001 \ Otherwise we need to move over to the next character STA R \ block to the left, so set a mask in R to the fourth \ pixel in the 4-pixel byte LDA SC \ Subtract 8 from SC, so SC(1 0) now points to the SBC #8 \ previous character along to the left STA SC BCS P%+4 \ If the subtraction underflowed, decrement the high DEC SC+1 \ byte in SC(1 0) to move to the previous page in \ screen memory CLC \ Clear the C flag so it doesn't affect the arithmetic \ below JMP LI400 \ Jump to LI400 to continue plotting from row 7 of the \ new character block .LIEX6 RTS \ Return from the subroutine .LI414 ASL R \ If we get here then the slope error just overflowed \ after plotting the pixel in LI440, so shift the single \ pixel in R to the left, so the next pixel we plot will \ be at the previous x-coordinate BCC LI405 \ If the pixel didn't fall out of the left end of R \ into the C flag, then jump to LI405 to plot the pixel \ on the next character row up LDA #%00010001 \ Otherwise we need to move over to the next character STA R \ block to the left, so set a mask in R to the fourth \ pixel in the 4-pixel byte LDA SC \ Subtract 8 from SC, so SC(1 0) now points to the SBC #8 \ previous character along to the left STA SC BCS P%+4 \ If the subtraction underflowed, decrement the high DEC SC+1 \ byte in SC(1 0) to move to the previous page in \ screen memory CLC \ Clear the C flag so it doesn't affect the arithmetic \ below BCC LI405 \ Jump to LI405 to rejoin the pixel plotting routine \ (this BCC is effectively a JMP as the C flag is clear) .LI415 ASL R \ If we get here then the slope error just overflowed \ after plotting the pixel in LI450, so shift the single \ pixel in R to the left, so the next pixel we plot will \ be at the previous x-coordinate BCC LI406 \ If the pixel didn't fall out of the left end of R \ into the C flag, then jump to LI406 to plot the pixel \ on the next character row up LDA #%00010001 \ Otherwise we need to move over to the next character STA R \ block to the left, so set a mask in R to the fourth \ pixel in the 4-pixel byte LDA SC \ Subtract 8 from SC, so SC(1 0) now points to the SBC #8 \ previous character along to the left STA SC BCS P%+4 \ If the subtraction underflowed, decrement the high DEC SC+1 \ byte in SC(1 0) to move to the previous page in \ screen memory CLC \ Clear the C flag so it doesn't affect the arithmetic \ below BCC LI406 \ Jump to LI406 to rejoin the pixel plotting routine \ (this BCC is effectively a JMP as the C flag is clear) .LI416 ASL R \ If we get here then the slope error just overflowed \ after plotting the pixel in LI460, so shift the single \ pixel in R to the left, so the next pixel we plot will \ be at the previous x-coordinate BCC LI407 \ If the pixel didn't fall out of the left end of R \ into the C flag, then jump to LI407 to plot the pixel \ on the next character row up LDA #%00010001 \ Otherwise we need to move over to the next character STA R \ block to the left, so set a mask in R to the fourth \ pixel in the 4-pixel byte LDA SC \ Subtract 8 from SC, so SC(1 0) now points to the SBC #8 \ previous character along to the left STA SC BCS P%+4 \ If the subtraction underflowed, decrement the high DEC SC+1 \ byte in SC(1 0) to move to the previous page in \ screen memory CLC \ Clear the C flag so it doesn't affect the arithmetic \ below JMP LI407 \ Jump to LI407 to rejoin the pixel plotting routine