Elite on the BBC Micro

# Drawing lines: LOIN (Part 5 of 7) [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
```