Elite on the BBC Micro and NES

# Version analysis of LOIN (Part 3 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.

```       Name: LOIN (Part 3 of 7)
Type: Subroutine
Category: Drawing lines
Summary: Draw a shallow line going right and up or left and down
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 up (no swap) or left and down (swap)

```

Code variation 1 of 12A variation in the comments only

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

```Cassette, Flight, Docked, Electron6502SP, Master   * X1 < X2 and Y1-1 > Y2
* X1 < X2 and Y1 > Y2
```
```
* Draw from (X1, Y1) at bottom left to (X2, Y2) at top right, omitting the
first pixel

```

Code variation 2 of 12A variation in the comments only

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

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

Code variation 3 of 12Related to the screen mode

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

```6502SP, Master LDA #%10001000         \ Modify the value in the LDA instruction at LI100 below
AND COL                \ to contain a pixel mask for the first pixel in the
STA LI100+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 LI110 below
AND COL                \ to contain a pixel mask for the second pixel in the
STA LI110+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 LI120 below
AND COL                \ to contain a pixel mask for the third pixel in the
STA LI120+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 LI130 below
AND COL                \ to contain a pixel mask for the fourth pixel in the
STA LI130+1            \ 4-pixel byte, in the colour COL, so that it draws in
\ the correct colour
```

Code variation 4 of 12Other (e.g. bug fix, optimisation)

The original versions contain a bug where the last pixel is skipped instead of the first pixel, but only when drawing lines that go right and up or left and down. This leads to a messy line join between this kind of line and lines with different slopes. This bug was fixed in the advanced versions.

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

```Cassette, Flight, Docked, Electron6502SP, Master LDA SWAP               \ If SWAP > 0 then we swapped the coordinates above, so
BNE LI6                \ jump down to LI6 to skip plotting the first pixel
\
\ This appears to be a bug that omits the last pixel
\ of this type of shallow line, rather than the first
\ pixel, which makes the treatment of this kind of line
\ different to the other kinds of slope (they all have a
\ BEQ instruction at this point, rather than a BNE)
\
\ The result is a rather messy line join when a shallow
\ line that goes right and up or left and down joins a
\ line with any of the other three types of slope
\
\ This bug was fixed in the advanced versions of Elite,
\ where the BNE is replaced by a BEQ to bring it in line
\ with the other three slopes
\ 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 LI190              \ so jump down to LI190 to plot the first pixel
```

Code variation 5 of 12Related to the screen mode

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

```6502SP, Master                        \ 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 LI100+6            \ If R = 0, jump to LI100+6 to start plotting from the
\ second pixel in this byte (LI100+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 LI110+6 to skip the
BCC LI110+6            \ first two pixels but plot the next two

CLC                    \ Clear the C flag so it doesn't affect the additions
\ below

BEQ LI120+6            \ If R = 2, jump to LI120+6 to skip the first three
\ pixels but plot the last one

BNE LI130+6            \ If we get here then R must be 3, so jump to LI130+6 to
\ skip plotting any of the pixels, but making sure we
\ join the routine just after the plotting instructions

.LI190
```

Code variation 6 of 12Related to the screen mode

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

```Cassette, Flight, Docked, Electron6502SP, Master DEX                    \ Decrement the counter in X because we're about to plot
\ the first pixel

.LIL2

\ 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
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)
```

Code variation 7 of 12Related to the screen mode

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

```Cassette, Flight, Docked, Electron 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

.LI6

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 LI7                \ If the pixel didn't fall out of the right end of R

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 8 of 12Related to the screen mode

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

```Electron BCC LI7                \ If the addition of the low bytes of SC overflowed,
INC SC+1               \ increment the high byte
```

Code variation 9 of 12Related to the screen mode

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

```Cassette, Flight, Docked, Electron.LI7

LDA S                  \ Set S = S + Q to update the slope error
STA S

DEY                    \ Otherwise we just overflowed, so decrement Y to move
\ to the pixel line above

BPL LIC2               \ If Y is positive we are still within the same
```

Code variation 10 of 12Related 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.

```Cassette, Flight, DockedElectron DEC SCH                \ Otherwise we need to move up into the character block
LDY #7                 \ above, so decrement the high byte of the screen
\ address and set the pixel line to the last line in
\ that character block
\ We now need to move up into the character block above,
\ 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 subtract from SC(1 0)

LDA SC                 \ Set SC(1 0) = SC(1 0) - &140
SBC #&40               \
STA SC                 \ Starting with the low bytes

LDA SC+1               \ And then subtracting the high bytes
SBC #&01
STA SC+1

LDY #7                 \ Set the pixel line to the last line in that character
\ block
```

Code variation 11 of 12Related to the screen mode

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

```Cassette, Flight, Docked, Electron.LIC2

DEX                    \ Decrement the counter in X

BNE LIL2               \ If we haven't yet reached the right end of the line,
\ loop back to LIL2 to plot the next pixel along

LDY YSAV               \ Restore Y from YSAV, so that it's preserved
```

Code variation 12 of 12Related to the screen mode

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

```6502SP, Master BEQ LI100              \ If R = 0, jump to LI100 to start plotting from the
\ first pixel in this byte

CMP #2                 \ If R < 2 (i.e. R = 1), jump to LI110 to start plotting
BCC LI110              \ from the second pixel in this byte

CLC                    \ Clear the C flag so it doesn't affect the additions
\ below

BEQ LI120              \ If R = 2, jump to LI120 to start plotting from the
\ third pixel in this byte

JMP LI130              \ If we get here then R must be 3, so jump to LI130 to
\ start plotting from the fourth pixel in this byte

.LI100

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

.LIEXS

BEQ LIEX               \ If we have just reached the right end of the line,

LDA S                  \ Set S = S + Q to update the slope error
STA S

CLC                    \ Otherwise we just overflowed, so clear the C flag and
DEY                    \ decrement Y to move to the pixel line above

BMI LI101              \ If Y is negative we need to move up into the character
\ address accordingly (jumping back to LI110 afterwards)

.LI110

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,

LDA S                  \ Set S = S + Q to update the slope error
STA S

CLC                    \ Otherwise we just overflowed, so clear the C flag and
DEY                    \ decrement Y to move to the pixel line above

BMI LI111              \ If Y is negative we need to move up into the character
\ address accordingly (jumping back to LI120 afterwards)

.LI120

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 LIEX               \ If we have just reached the right end of the line,

LDA S                  \ Set S = S + Q to update the slope error
STA S

CLC                    \ Otherwise we just overflowed, so clear the C flag and
DEY                    \ decrement Y to move to the pixel line above

BMI LI121              \ If Y is negative we need to move up into the character
\ address accordingly (jumping back to LI130 afterwards)

.LI130

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
STA S

CLC                    \ Otherwise we just overflowed, so clear the C flag and
DEY                    \ decrement Y to move to the pixel line above

BMI LI131              \ If Y is negative we need to move up into the character
\ address accordingly (jumping back to LI140 afterwards)

.LI140

DEX                    \ Decrement the counter in X

BEQ LIEX               \ If we have just reached the right end of the line,

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 LI100              \ If the addition didn't overflow, jump back to LI100
\ 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 LI100              \ Jump back to LI100 to plot the next pixel

.LI101

DEC SC+1               \ If we get here then we need to move up into the
DEC SC+1               \ character block above, so we decrement the high byte
LDY #7                 \ of the screen twice (as there are two pages per screen
\ row) and set the pixel line to the last line in
\ that character block

BPL LI110              \ Jump back to the instruction after the BMI that called
\ this routine

.LI111

DEC SC+1               \ If we get here then we need to move up into the
DEC SC+1               \ character block above, so we decrement the high byte
LDY #7                 \ of the screen twice (as there are two pages per screen
\ row) and set the pixel line to the last line in
\ that character block

BPL LI120              \ Jump back to the instruction after the BMI that called
\ this routine

.LI121

DEC SC+1               \ If we get here then we need to move up into the
DEC SC+1               \ character block above, so we decrement the high byte
LDY #7                 \ of the screen twice (as there are two pages per screen
\ row) and set the pixel line to the last line in
\ that character block

BPL LI130              \ Jump back to the instruction after the BMI that called
\ this routine

.LI131

DEC SC+1               \ If we get here then we need to move up into the
DEC SC+1               \ character block above, so we decrement the high byte
LDY #7                 \ of the screen twice (as there are two pages per screen
\ row) and set the pixel line to the last line in
\ that character block

BPL LI140              \ Jump back to the instruction after the BMI that called
\ this routine

.LIEX
```
``` RTS                    \ Return from the subroutine

```