16-bit multiplication is the multiplication of two 16-bit value from another. Such a
multiplication results in a 32-bit value.

Programming Tip: In fact, any multiplication results in an answer which is the
sum of the bits in the two multiplicands. For example, multiplying an 8-bit value by
a 16-bit value results in a 24-bit value (8 + 16). A 16-bit value multiplied by another
16-bit value results in a 32-bit value (16 + 16), etc.

For the sake of example, let's multiply 25,136 by 17,198. The answer is
432,288,928. As with both addition and subtraction, let's first convert the expression
into hexadecimal: 6230h x 432Eh.

Once again, let's arrange the numbers in columns as we did in primary school to multiply
numbers, although now the grid becomes more complicated. The green section represents
the original two values. The yellow section represents the intermediate calculations
obtained by multipying each byte of the original values. The red section of the grid
indicates our final answer, obtained by summing the columns in the yellow area.

.

Byte 4

Byte 3

Byte 2

Byte 1

.

.

.

62

30

*

.

.

43

2E

=

.

.

08

A0

.

.

11

9C

.

.

.

0C

90

.

.

19

A6

.

.

=

19

C4

34

A0

Remember how we did this in elementary school? First we multiply 2Eh by 30h (byte 1 of both
numbers), and place the result directly below. Then we multiply 2Eh by 62h (byte 1 of the
bottom number by byte 2 of the upper number). This result is lined up such that the
right-most column ends up in byte 2. Next we multiply 43h by 30h (byte 2 of the bottom
number by byte 1 of the top number), again lining up the result so that the right-most column
ends up in byte 2. Finally, we multiply 43h by 62h (byte 2 of both numbers) and position the
answer such that the right-most column ends up in byte 3. Once we've done the above, we add
each column, with appropriate carries, to arrive at the final answer.

Our process in assembly language will be identical. Let's use our now-familiar grid to help
us get an idea of what we're doing:

.

Byte 4

Byte 3

Byte 2

Byte 1

*

R6

R7

*

R4

R5

=

R0

R1

R2

R3

Thus our first number will be contained in R6 and R7 while our second number will be held
in R4 and R5. The result of our multiplication will end up in R0, R1, R2 and R3. At
8-bits per register, these four registers give us the 32 bits we need to handle the largest
possible multiplication. Our process will be the following:

Multiply R5 by R7, leaving the 16-bit result in R2 and R3.

Multiply R5 by R6, adding the 16-bit result to R1 and R2.

Multiply R4 by R7, adding the 16-bit result to R1 and R2.

Multiply R4 by R6, adding the 16-bit result to R0 and R1.

We'll now convert the above process to assembly language, step by step.

Step 1. Multiply R5 by R7, leaving the 16-bit result in R2 and R3.

MOV A,R5 ;Move the R5 into the Accumulator
MOV B,R7 ;Move R7 into B
MUL AB ;Multiply the two values
MOV R2,B ;Move B (the high-byte) into R2
MOV R3,A ;Move A (the low-byte) into R3

MOV A,R4 ;Move R4 into the Accumulator
MOV B,R7 ;Move R7 into B
MUL AB ;Multiply the two values
ADD A,R2 ;Add the low-byte into the value already in R2
MOV R2,A ;Move the resulting value back into R2
MOV A,B ;Move the high-byte into the accumulator
ADDC A,R1 ;Add the current value of R1 (plus any carry)
MOV R1,A ;Move the resulting answer into R1.
MOV A,#00h ;Load the accumulator with zero
ADDC A,R0 ;Add the current value of R0 (plus any carry)
MOV R0,A ;Move the resulting answer to R1.

Step 4. Multiply R4 by R6, adding the 16-bit result to R0 and R1.

MOV A,R4 ;Move R4 back into the Accumulator
MOV B,R6 ;Move R6 into B
MUL AB ;Multiply the two values
ADD A,R1 ;Add the low-byte into the value already in R1
MOV R1,A ;Move the resulting value back into R1
MOV A,B ;Move the high-byte into the accumulator
ADDC A,R0 ;Add it to the value already in R0 (plus any carry)
MOV R0,A ;Move the resulting answer back to R0

Combining the code from the two steps above, we come up with the following subroutine:

MUL16_16:
;Multiply R5 by R7
MOV A,R5 ;Move the R5 into the Accumulator
MOV B,R7 ;Move R7 into B
MUL AB ;Multiply the two values
MOV R2,B ;Move B (the high-byte) into R2
MOV R3,A ;Move A (the low-byte) into R3
;Multiply R5 by R6
MOV A,R5 ;Move R5 back into the Accumulator
MOV B,R6 ;Move R6 into B
MUL AB ;Multiply the two values
ADD A,R2 ;Add the low-byte into the value already in R2
MOV R2,A ;Move the resulting value back into R2
MOV A,B ;Move the high-byte into the accumulator
ADDC A,#00h ;Add zero (plus the carry, if any)
MOV R1,A ;Move the resulting answer into R1
MOV A,#00h ;Load the accumulator with zero
ADDC A,#00h ;Add zero (plus the carry, if any)
MOV R0,A ;Move the resulting answer to R0.
;Multiply R4 by R7
MOV A,R4 ;Move R4 into the Accumulator
MOV B,R7 ;Move R7 into B
MUL AB ;Multiply the two values
ADD A,R2 ;Add the low-byte into the value already in R2
MOV R2,A ;Move the resulting value back into R2
MOV A,B ;Move the high-byte into the accumulator
ADDC A,R1 ;Add the current value of R1 (plus any carry)
MOV R1,A ;Move the resulting answer into R1.
MOV A,#00h ;Load the accumulator with zero
ADDC A,R0 ;Add the current value of R0 (plus any carry)
MOV R0,A ;Move the resulting answer to R1.
;Multiply R4 by R6
MOV A,R4 ;Move R4 back into the Accumulator
MOV B,R6 ;Move R6 into B
MUL AB ;Multiply the two values
ADD A,R1 ;Add the low-byte into the value already in R1
MOV R1,A ;Move the resulting value back into R1
MOV A,B ;Move the high-byte into the accumulator
ADDC A,R0 ;Add it to the value already in R0 (plus any carry)
MOV R0,A ;Move the resulting answer back to R0
;Return - answer is now in R0, R1, R2, and R3
RET

And to call our routine to multiply the two values we used in the example above, we'd use
the code: