Aviator on the BBC Micro

# Dashboard: GetHandVector

```       Name: GetHandVector                                           [Show more]
Type: Subroutine
Category: Dashboard
Summary: Vector line calculation for a hand on indicators 0 to 6
Deep dive: Clock hands and dial indicators
Context: See this subroutine in context in the source code
References: This subroutine is called as follows:
* DrawIndicatorHand calls GetHandVector

Arguments:

A                    The value to show as a hand on the dial indicator

Returns:

W                    Magnitude of x-coordinate of line's vector |x-delta|

G                    Magnitude of y-coordinate of line's vector |y-delta|

R                    Direction of vector (T, U):

* Bit 7 is the direction of the x-delta

* Bit 6 is the direction of the y-delta

Direction is like a clock, so positive (clear) is up and
right, so this means the following:

* x-delta +ve, y-delta +ve: 12 to 3 o'clock

* x-delta +ve, y-delta -ve: 3 to 6 o'clock

* x-delta -ve, y-delta -ve: 6 to 9 o'clock

* x-delta -ve, y-delta +ve: 9 to 12 o'clock

.GetHandVector

LDY #0                 \ Set R = 0, to use as the first guess for the direction
STY R                  \ of the hand on the dial (we will change it below if
\ required)

LDY dialQuadrant,X     \ Set Y = the dialQuadrant value for this indicator,
\ which is the size of a quarter of the dial in terms of
\ the value in A

INY                    \ Set K = Y + 1
STY K                  \       = dialQuadrant + 1
\
\ Doing this enables us to subtract this value below,
\ leaving the result in a suitable state for negating
\ using two's complement (see dhvc2)

SEC                    \ Set the C flag for the subtraction we are about to do

.dhvc1

SBC K                  \ Set A = A - K
\       = A - (dialQuadrant + 1)
\
\ so we have subtracted a quadrant's worth of value from
\ the value we want to show in A

BCS dhvc2              \ If the subtraction didn't underflow, jump to dhvc2 to
\ skip the following two instructions

\ The subtraction underflowed, so we know that the hand
\ is in the first quadrant, i.e. 3 to 6 o'clock

ADC K                  \ Reverse the subtraction by adding K to A, so A is now
\ back to its original value

JMP dhvc6              \ Jump down to dhvc5 to calculate the hand's vector,
\ with R set to 0 to indicate that both the x-delta and
\ y-delta for the line are positive

.dhvc2

\
\     = A - 2 * dialQuadrant - 1
\
\ If we want to negate this value below, then we can do
\ this using two's complement by simply inverting all
\ the bits, as we have already subtracted 1, and we can
\ negate by either inverting-and-adding-1, or by
\ subtracting-1-and-inverting (as they are equivalent)

BCS dhvc3              \ If the subtraction didn't underflow, jump to dhvc3 to
\ skip the following three instructions

\ The subtraction underflowed, so we know that the hand
\ is in the second quadrant, i.e. 6 to 9 o'clock

LDY #%01000000         \ Set bit 6 of R to indicate that the x-delta for the
STY R                  \ line is positive and the y-delta is negative

BNE dhvc5              \ Jump down to dhvc5 to negate A before calculating the
\ hand's vector (this BNE is effectively a JMP as A is
\ never zero)

.dhvc3

SBC K                  \ Subtract a third quadrant's worth

BCS dhvc4              \ If the subtraction didn't underflow, jump to dhvc4 to
\ skip the following four instructions

\ The subtraction underflowed, so we know that the hand
\ is in the third quadrant on the dial

ADC K                  \ Reverse the subtraction by adding K to A, so A is now
\ back to its original value

LDY #%11000000         \ Set bits 7 and 6 of R to indicate that both the
STY R                  \ x-delta and y-delta for the line are negative

BNE dhvc6              \ Jump down to dhvc5 to calculate the hand's vector
\ (this BNE is effectively a JMP as A is never zero)

.dhvc4

BCS dhvc1              \ If the subtraction didn't underflow, jump to dhvc1 to
\ start the subtraction process again, as we have now
\ subtracted a whole dial's worth and need to keep going

\ The subtraction underflowed, so we know that the hand
\ is in the fourth quadrant on the dial

LDY #%10000000         \ Set bit 7 of R to indicate that the x-delta for the
STY R                  \ line is negative and the y-delta is positive

.dhvc5

\ If we get here then the hand is either in the second
\ or fourth quadrant on the dial

EOR #&FF               \ Invert the value of A (i.e. negate it) using two's
\ complement, which works because we can negate a number
\ by subtracting 1 and then inverting, and we
\ effectively subtracted 1 in the above by using K

.dhvc6

\ By the time we get here, the direction of the new
\ dial hand is in R, the value of A has been reduced
\ into negative territory by repeated subtraction, and
\ it has been switched to be positive if the quadrant is
\ bottom-right or top-left, i.e. 3 to 6 o'clock or
\ 9 to 12 o'clock)
\
\ We now use this value of A as our value of x in the
\ above calculation

STA S                  \ Store the reduced value of A in S

\ We now calculate y-delta, where y = w - x or y = x - w
\ x correctly with the above EOR, so if we calculate
\ y = w - x, it will be correct

LDA dialQuadrant,X     \ Set A = the dialQuadrant value for this indicator,
\ which is the size of a quarter of the dial in terms of
\ the value in A. This is what we use for w in the above

SEC                    \ Set A = A - S
SBC S                  \       = w - x
\
\ so A contains our y-delta

\ We now cap the y-delta, which has the effect of
\ blunting the points of our diamond

CMP yDeltaMax,X        \ If A < yDeltaMax for this indicator, jump to dhvc7 to
BCC dhvc7              \ skip the following instruction

LDA yDeltaMax,X        \ Set A = yDeltaMax, so A is never greater than the
\ yDeltaMax value for this indicator

.dhvc7

CLC                    \ Set G = A + 1
ADC #1                 \       = y-delta + 1
STA G                  \
\ so G contains the y-delta, plus 1 to ensure it is
\ non-zero

LDA S                  \ Fetch the reduced value of A that we stored in S we
\ above, which we know is our x-delta

CLC                    \ Set A = (A + 1) / 2
LSR A                  \ because mode 5 pixels are twice as wide as they are
\ high, so this scales the x-delta to be a pixel value

CMP xDeltaMax,X        \ If A < xDeltaMax for this indicator, jump to dhvc8 to
BCC dhvc8              \ skip the following instruction

LDA xDeltaMax,X        \ Set A = xDeltaMax, so A is never greater than the
\ xDeltaMax value for this indicator

.dhvc8

CLC                    \ Set W = A + 1
ADC #1                 \       = x-delta + 1
STA W                  \
\ so W contains the y-delta, plus 1 to ensure it is
\ non-zero

RTS                    \ Return from the subroutine
```