If you understand how decimal multiplication "on paper" is performed, then it will be easy to replace decimal digits by 8-bit numbers.
Let's say you have two decimal numbers: AB and XY (where A, B, X, Y - are decimal digits).
You can express it as a sum of two multiplications of two-digit number by one-digit number:
AB * XY = (AB * Y) + ((AB * X) << 1d) (where << 1d means shifted 1 digit position left and equals to a multiplication by 10 in decimal)
The multiplication of two digits by one digit can be expressed in the same terms as
AB * Y = (B * Y) + ((A * Y) << 1d)
or whole experssion may be written as:
AB * XY = (B * Y) + ((A * Y) << 1d) + ((B * X) << 1d) + ((A * X) << 2d)
now you can just jump from the decimal system into the 256-based system, assuming every digit in the example above is a single byte.
So, to find multiplication of AB * XY you need:
- calculate B * Y, store it into 4-byte result (two upper bytes will be zeroes)
- calculate B * X, shift it left by 1 byte, add to result
- calculate A * Y, shift it left by 1 byte, add to result
- calculate A * X, shift it left by 2 bytes, add to result
In assmbler it may look as follows:
Let's say we have:
- r25:r24 - the first multiplier,
- r23:r22 - the second
- r21:r20:r19:r18 - result
The code would be like:
clr r16 // a zero register, we'll need it in the future
mul r24, r22 // r1:r0 = r24 * r22
movw r0, r18 // move result to r19:r18
clr r20 // clear r21 and r22
clr r21
mul r24, r23 // r1:r0 = r24 * r23
add r19, r0 // add to the result starting from the second from the right byte (r19)
adc r20, r1 // add next byte with carry
adc r21, r16 // add zero with carry
mul r25, r22 // r1:r0 = r25 * r22
add r19, r0 // add to the result starting from the second from the right byte (r19)
adc r20, r1 // add next byte with carry
adc r21, r16 // add zero with carry
mul r25, r23 // r1:r0 = r25 * r23
add r20, r0 // add to the result starting from the third from the right byte (r20)
adc r21, r1 // add next byte with carry
Well done! Now you have the four-byte result in r21:r20:r19:r18
To divide by 2 you can just move the result 1 binary position to the right. You need two instructions:
- lsr - logical shift right. It moves bits one position to the right. Leftmost position filled by zero, while the pushed out rightmost position stored in the carry flag.
- ror - rotate right through carry. It does the same: moves bits one position to the right and stores the pushed out rightmost position in the carry, but the leftmost position filled by the initial value of the carry flag
The code:
lsr r21
ror r20
ror r19
ror r18
now the four-byte value is divided by two (rounding downwards)