Skip to navigation

Revs on the BBC Micro

# Dashboard: DrawDashboardLine

```       Name: DrawDashboardLine                                       [Show more]
Type: Subroutine
Category: Dashboard
Summary: Draw a hand on the rev counter or a line on the steering wheel
Context: See this subroutine in context in the source code
References: This subroutine is called as follows:
* UpdateDashboard calls DrawDashboardLine

This routine is a mode 5 Bresenham line-drawing routine, which modifies itself
to cater for lines of different slopes.

Arguments:

V                    The slope of the line to draw, which is expressed as a
quadrant number, where quadrant 0 is from 12 o'clock to
3 o'clock, with quadrants ordered 0 to 3 in a clockwise
order:

* 0 = %000 = Quadrant 0, first half (12:00 to 1:30)
Steep slope, right and up
Step up along y-axis (stepAxis = DEY)
Move right along x-axis (shortAxis = INX)

* 1 = %001 = Quadrant 0, second half (1:30 to 3:00)
Shallow slope, right and up
Step right along x-axis (stepAxis = INX)
Move up along y-axis (shortAxis = DEY)

* 2 = %010 = Quadrant 1, first half (3:00 to 4:30)
Shallow slope, right and down
Step right along x-axis (stepAxis = INX)
Move down along y-axis (shortAxis = INY)

* 3 = %011 = Quadrant 1, second half (4:30 to 6:00)
Steep slope, right and down
Step down along y-axis (stepAxis = INY)
Move right along x-axis (shortAxis = INX)

* 4 = %100 = Quadrant 2, first half (6:00 to 7:30)
Steep slope, left and down
Step down along y-axis (stepAxis = INY)
Move left along x-axis (shortAxis = DEX)

* 5 = %101 = Quadrant 2, second half (7:30 to 9:00)
Shallow slope, left and down
Step left along x-axis (stepAxis = DEX)
Move down along y-axis (shortAxis = INY)

* 6 = %110 = Quadrant 3, first half (9:00 to 10:30)
Shallow slope, left and up
Step left along x-axis (stepAxis = DEX)
Move up along y-axis (shortAxis = DEY)

* 7 = %111 = Quadrant 3, second half (10:30 to 12:00)
Steep slope, left and up
Step up along y-axis (stepAxis = DEY)
Move left along x-axis (shortAxis = DEX)

(Q P)                The screen address of the character block containing
the line's starting point

Y                    The pixel row within the character block containing the
line's starting point (0 to 7)

U                    The number of pixels to step along the step axis:

* The number of pixels along the longer (step) axis
when drawing the rev counter

* 6 when drawing the steering wheel line

T                    The slope error for each step along the step axis is
T/SS, and T is set to:

* The distance between the hand and quadrant when
drawing the rev counter hand

* When drawing the steering wheel line:

0-37 when the line is close to the centre (in the
top two quadrants either side of the centre)

37-16 when line is further from the centre (in the
left and right quadrants)

SS                   The slope error for each step along the step axis is
T/SS, and SS is set to:

* The same as U when drawing the rev counter hand
i.e. the number of pixels along the longer (step)
axis

* A value from wheelPixels when drawing the steering
wheel line (38 to 53)

H                    The starting index to use in the pixelByte and
yLookupLo+8 lookup tables:

* %000 when drawing the rev counter hand, so the line
gets drawn in white

* %100 when drawing the steering wheel line, so the
line gets drawn in black

W                    The pixel number (0-7) of the first pixel to draw
along the x-axis

.DrawDashboardLine

LDX V                  \ Modify the instruction at dlin2 to the V-th shortAxis
LDA shortAxis,X        \ instruction
STA dlin2

LDA stepAxis,X         \ Modify the instruction at dlin8 to the V-th stepAxis
STA dlin8              \ instruction

\ The following code has the instructions for V = %010,
\ which has INY at dlin2 for the short axis, and INX at
\ dlin8 for the step axis, so that's this kind of line:
\
\   * Quadrant 1, first half (3:00 to 4:30)
\   * Shallow slope, right and down
\   * Step right along x-axis (stepAxis = INX)
\   * Move down along y-axis (shortAxis = INY)

LDX W                  \ Set X = W, so X contains the position of the current
\ pixel within the pixel row, if there were eight pixels
\ per row

LDA #0                 \ Set A = -SS
SEC                    \
SBC SS                 \ So this is the starting point for our slope error
\ calculation

CLC                    \ Clear the C flag for the following addition

.dlin1

\ We use A to keep track of the slope error, adding the
\ step along the smaller axis (in T) until it reaches 0,
\ at which point it is a multiple of SS and we need
\ to move one pixel along the smaller axis

ADC T                  \ Set A = A + T
\
\ So A is updated with the slope error

BCC dlin3              \ If the addition didn't overflow, then the result in A
\ is still negative, so skip the following instruction

\ The slope error just overflowed (in other words, the
\ cumulative slope error in A just reached a multiple of
\ SS), so we need to adjust the slope error to make
\ it negative again, and we need to step along the
\ shorter axis

SBC SS                 \ Subtract SS from the cumulative slope error to
\ bring it back to being negative, so we can detect when
\ it reaches next multiple of SS

.dlin2

INY                    \ Increment Y to move down along the y-axis (i.e. along
\ the shorter axis)
\
\ This instruction is modified at the start of this
\ routine, depending on the slope of the line in V

.dlin3

STA II                 \ Store the updated slope error in II, so we can
\ retrieve it below, ready for the next iteration of the
\ drawing loop

TXA                    \ X contains the position of the current pixel within
LSR A                  \ the pixel row, in the range 0 to 7, so set A to half
AND #%00000011         \ this value to get the mode 5 pixel number (as there
\ are only four pixels per pixel byte on mode 5)

ORA H                  \ Set bit 2 of A if this is the steering wheel, which
\ is the same as adding 4

STA V                  \ Store the result in V, so V contains the pixel number
\ (0 to 3) of the pixel to draw, plus 4 if this is the
\ steering wheel (4 to 7)

TXA                    \ X contains the position of the current pixel within
\ the pixel line, so put this in A

BPL dlin4              \ If bit 7 of A is clear, jump to dlin4

\ Otherwise we need to move (Q P) to the previous
\ character block to the left, by subtracting 8 (as
\ there are 8 bytes per character block)

LDX #7                 \ Set X = 7 to set as the new value of W below

LDA P                  \ Set (Q P) = (Q P) - 8
SEC                    \
SBC #8                 \ starting with the low bytes
STA P

BCS dlin5              \ And then the high bytes
DEC Q

BCS dlin5              \ This instruction has no effect, as we already passed
\ through the BCS above, which is presumably a bug (this
\ should perhaps be a BCC?)

.dlin4

CMP #8                 \ If A < 8, jump to dlin5
BCC dlin5

\ Otherwise we need to move (Q P) to the next character
\ block to the right, by adding 8 (as there are 8 bytes
\ per character block)

LDX #0                 \ Set X = 0 to set as the new value of W below

LDA P                  \ Set (Q P) = (Q P) + 8
CLC                    \
ADC #8                 \ starting with the low bytes
STA P

BCC dlin5              \ And then the high bytes
INC Q

.dlin5

STX W                  \ Store X in W, so W moves along one pixel to the right

LDX lineBufferSize     \ Set X to the size of the line buffer, which gives us
\ the index of the next empty space in the buffer

TYA                    \ Y contains the number of the pixel row within the
\ current character block, so put this in A

BPL dlin6              \ If A >=0, jump to dlin6

\ Otherwise we need to move (Q P) to the next character
\ row above, by subtracting &140 (as there are &140
\ bytes per character row)

LDA P                  \ Set (Q P) = (Q P) - &140
SEC                    \
SBC #&40               \ starting with the low bytes
STA P

LDA Q                  \ And then the high bytes
SBC #&01
STA Q

LDY #7                 \ Set Y = A = 7 as the new value of Y
TYA

BNE dlin7              \ Jump to dlin7 (this BNE is effectively a JMP as A is
\ never zero)

.dlin6

CMP #8                 \ If A < 8, jump to dlin7
BCC dlin7

\ Otherwise we need to move (Q P) to the next character
\ row below, by adding &140 (as there are &140 bytes per
\ character row)

LDA P                  \ Set (Q P) = (Q P) + &140
CLC                    \
ADC #&40               \ starting with the low bytes
STA P

LDA Q                  \ And then the high bytes
ADC #&01
STA Q

LDY #0                 \ Set Y = A = 0 as the new value of Y
TYA

.dlin7

\ We now store the details of the pixel we are about
\ to overwrite in the line buffer, which stores a screen
\ address plus the original contents of that address
\
\ We get the screen address by adding the address of the
\ character block in P to the number of the pixel row
\ within the character in A, which we can do with an ORA
\ as P only occupies bits 3 to 7, while A only occupies
\ bits 0 to 2

ORA P                  \ Store the address we are about to overwrite in the
STA lineBufferAddrLo,X \ next empty space at the end of the line buffer, i.e.
\ the X-th byte of (lineBufferAddrHi lineBufferAddrLo),
\ starting with the low byte of the address

LDA Q                  \ And then the high byte of the address
STA lineBufferAddrHi,X

LDA (P),Y              \ Store the current pixel contents into the pixel
STA lineBufferPixel,X  \ contents buffer at lineBufferPixel

INC lineBufferSize     \ Increment the size of the pixel buffers, as we just
\ added an entry

LDX V                  \ Set X = V, so X now contains the pixel number
\ (0 to 3) of the pixel to draw, plus 4 if this is the
\ steering wheel (4 to 7)

AND yLookupLo+8,X      \ Apply the X-th pixel mask from yLookupLo+8, so this
\ clears the X-th pixel in the pixel row (the table
\ contains the same bytes in 0 to 3 as in 0 to 7)

ORA pixelByte,X        \ OR with a pixel byte with pixel X set, so this sets
\ the X-th pixel to colour 2 (white) if X is 0 to 3, or
\ colour 0 (black) if X is 4 to 7 - so the rev counter
\ hand is white, while the steering wheel line is black

STA (P),Y              \ Draw the pixel byte to the screen

\ We now set up all the variables so we can loop back
\ to dlin1 for the next pixel

LDX W                  \ Set X = W

LDA II                 \ Set A to the current slope error, which we stored in
\ II above

CLC                    \ Clear the C flag for the addition at the start of the
\ loop

.dlin8

INX                    \ Increment X to step right along the x-axis (i.e. along
\ the longer axis)
\
\ This instruction is modified at the start of this
\ routine, depending on the slope of the line in V

DEC U                  \ Decrement the pixel counter

BMI dlin9              \ If we have drawn the correct number of pixels along
\ the longer axis, jump to dlin9 to return from the
\ subroutine as we have finished drawing the line

JMP dlin1              \ Otherwise loop back to draw the next pixel

.dlin9

RTS                    \ Return from the subroutine
```