Skip to navigation

Elite on the BBC Micro and NES

Drawing lines: LOIN (Part 5 of 7)

[Acorn Electron version]

Name: LOIN (Part 5 of 7) [Show more] Type: Subroutine Category: Drawing lines Summary: Draw a line: Line has a steep gradient, step up along y-axis Deep dive: Bresenham's line algorithm
Context: See this subroutine in context in the source code Variations: See code variations for this subroutine in the different versions References: No direct references to this subroutine in this source file

This routine draws a line from (X1, Y1) to (X2, Y2). It has multiple stages. If we get here, then: * |delta_y| >= |delta_x| * The line is closer to being vertical than horizontal * We are going to step up along the y-axis * We potentially swap coordinates to make sure Y1 >= Y2
.STPY LDY Y1 \ Set A = Y = Y1 TYA LDX X1 \ Set X = X1 CPY Y2 \ If Y1 >= Y2, jump down to LI15, as the coordinates are BCS LI15 \ already in the order that we want DEC SWAP \ Otherwise decrement SWAP from 0 to &FF, to denote that \ we are swapping the coordinates around LDA X2 \ Swap the values of X1 and X2 STA X1 STX X2 TAX \ Set X = X1 LDA Y2 \ Swap the values of Y1 and Y2 STA Y1 STY Y2 TAY \ Set Y = A = Y1 .LI15 \ By this point we know the line is vertical-ish and \ Y1 >= Y2, so we're going from top to bottom as we go \ from Y1 to Y2 \ We now calculate the address of the character block \ containing the pixel (X1, Y1) and put it in SC(1 0), \ as follows: \ \ SC = &5800 + (Y1 div 8 * 256) + (Y1 div 8 * 64) + 32 \ \ See the deep dive on "Drawing pixels in the Electron \ version" for details LSR A \ Set A = Y1 / 8, so A now contains the character row LSR A \ that will contain our horizontal line LSR A STA SC+1 \ Set SC+1 = A, so (SC+1 0) = A * 256 \ = char row * 256 LSR A \ Set (A SC) = (A SC) / 4 ROR SC \ = (4 * ((char row * 64) + 32)) / 4 LSR A \ = char row * 64 + 32 ROR SC ADC SC+1 \ Set SC(1 0) = (A SC) + (SC+1 0) + &5800 ADC #&58 \ = (char row * 64 + 32) STA SC+1 \ + char row * 256 \ + &5800 \ \ which is what we want, so SC(1 0) contains the address \ of the first visible pixel on the character row \ containing the point (X1, Y1) TXA \ Each character block contains 8 pixel rows, so to get AND #%11111000 \ the address of the first byte in the character block \ that we need to draw into, as an offset from the start \ of the row, we clear bits 0-2 ADC SC \ And add the result to SC(1 0) to get the character STA SC \ block on the row we want BCC P%+4 \ If the addition of the low bytes overflowed, increment INC SC+1 \ the high byte \ So SC(1 0) now contains the address of the first pixel \ in the character block containing the (X1, Y1), taking \ the screen borders into consideration LDA Y1 \ Set Y = Y1 mod 8, which is the pixel row within the AND #7 \ character block at which we want to draw the start of TAY \ our line (as each character block has 8 rows) TXA \ Set X = X1 mod 8, which is the pixel column within the AND #7 \ character block at which we want to draw the start of TAX \ our line (as each character block has 8 rows) LDA TWOS,X \ Fetch a mode 4 1-pixel byte with the pixel position STA R \ at X and store it in R to act as a mask \ The following calculates: \ \ P = P / Q \ = |delta_x| / |delta_y| \ \ using the same shift-and-subtract algorithm \ documented in TIS2 LDA P \ Set A = |delta_x| LDX #1 \ Set Q to have bits 1-7 clear, so we can rotate through STX P \ 7 loop iterations, getting a 1 each time, and then \ getting a 1 on the 8th iteration... and we can also \ use P to catch our result bits into bit 0 each time .LIL4 ASL A \ Shift A to the left BCS LI13 \ If bit 7 of A was set, then jump straight to the \ subtraction CMP Q \ If A < Q, skip the following subtraction BCC LI14 .LI13 SBC Q \ A >= Q, so set A = A - Q SEC \ Set the C flag to rotate into the result in Q .LI14 ROL P \ Rotate the counter in P to the left, and catch the \ result bit into bit 0 (which will be a 0 if we didn't \ do the subtraction, or 1 if we did) BCC LIL4 \ If we still have set bits in P, loop back to TIL2 to \ do the next iteration of 7 \ We now have: \ \ P = A / Q \ = |delta_x| / |delta_y| \ \ and the C flag is set LDX Q \ Set X = Q + 1 INX \ = |delta_y| + 1 \ \ We add 1 so we can skip the first pixel plot if the \ line is being drawn with swapped coordinates LDA X2 \ Set A = X2 - X1 (the C flag is set as we didn't take SBC X1 \ the above BCC) BCC LFT \ If X2 < X1 then jump to LFT, as we need to draw the \ line to the left and down