Aviator on the BBC Micro

# Maths: Multiply8x16

```       Name: Multiply8x16                                            [Show more]
Type: Subroutine
Category: Maths
Summary: Multiply an 8-bit and a 16-bit number
Deep dive: Times tables and nibble arithmetic
Context: See this subroutine in context in the source code
References: This subroutine is called as follows:
* ScaleByAltitude calls Multiply8x16
* ApplyFlightControl calls entry point Multiply8x16-6
* ProcessLanding (Part 7 of 7) calls entry point Multiply8x16-6
* ScaleFlightForces calls entry point Multiply8x16-6
* GetMoments calls entry point Multiply8x16-2

This routine multiplies an unsigned 8-bit and a signed 16-bit number, as
follows:

(G W V) = (Q P) * R

It uses the following algorithm:

(Q P) * R
= (Q << 8 + P) * R
= (Q << 8) * R + (P * R)
= (Q * R) << 8 + (P * R)

Arguments:

(Q P)                A signed 16-bit number

R                    An unsigned 8-bit number

Returns:

(G W V)              The result, (Q P) * R

A                    The high byte of the result (G)

Other entry points:

Multiply8x16-2       Store X in VV before doing the calculation

Multiply8x16-6       Store X in VV and calculate (G W V) = (A Y) * R

STY P                  \ Set (Q P) = (A Y)
STA Q

STX VV                 \ Store X in VV

.Multiply8x16

LDX Q                  \ Set X to the high byte of the argument in (Q P)

BPL muly1              \ If X is positive, jump to muly1 to skip the following

LDA #0                 \ Set (X P) = 0 - (Q P)
SEC                    \
SBC P                  \ Starting with the low bytes
STA P

LDA #0                 \ And then the high bytes
SBC Q
TAX

.muly1

\ By this point, (X P) is positive, and we have:
\
\   (X P) = |Q P|

LDY R                  \ Set (A V) = X * Y
JSR Multiply8x8        \           = |Q| * R

STA G                  \ Set (G W) = (A V)
LDA V                  \           = |Q| * R
STA W

LDY R                  \ Set (A V) = X * Y
LDX P                  \           = P * R
JSR Multiply8x8

\ We now calculate the following:
\
\   (G W V) = (G W 0) + (0 A V)
\           = (G W) << 8 + (P * R)
\           = (|Q| * R) << 8 + (P * R)
\
\ which is the result we want
\
\ We don't need to calculate the lowest bytes, as we
\ already know that V + 0 = V, so we just do the middle
\ and high bytes

CLC                    \ Set (G W V) = (G W 0) + (0 A V)
STA W                  \ starting with the middle bytes

LDA #0                 \ And then the top bytes
STA G

LDX Q                  \ If the original argument (Q P) was positive, the
BPL GetMoments-1       \ result already has the correct sign, so return from
\ the subroutine (as GetMoments-1 contains an RTS)

LDA #0                 \ Otherwise we want to negate (G W V), so start with by
SEC                    \ negating V
SBC V
STA V

\ And fall through into Negate16Bit to negate (G W) and
\ return that as our result
```