10

The MC6809 is unusual for an 8-bit processor of its era in having a hardware multiply instruction. MUL multiples the 8-bit unsigned integer in the A register with the 8-bit unsigned integer in the B register, leaving the product in the 16-bit D register:

         lda   #12             A=12
         ldb   #34             B=34
         mul                   D=408

Interestingly, the mul instruction sets the C flag to bit 7 of the D register. Bit 7 is the the most significant bit of the least significant byte. According to the Motorola 6809 and Hitachi 6309 Programmer's Reference, p. 100, that is to support rounding:

The Carry flag is set equal to bit 7 of the least-significant byte so that rounding of the most-significant byte can be accomplished by executing:

ADCA #0

But what does rounding mean in the context of integer multiplication? How is this rounding capability used in practice?

10

The rounding capability is useful when doing fixed point math with exactly eight fractional digits. For example, supposing that the number in A is a fixed-point number with 2 fractional digits, and the number in B is a fixed-point number with 6 fractional digits:

         lda   #%00100010      A=%001000.10=8.5
         ldb   #%11110000      B=%11.110000=3.75
         mul                   D=%00011111.11100000=31.875

If we only want the integer result, we could take the most significant byte of the result, which throws away the fractional part:

         sta   result         Stores truncated result, 31

However, since the C flag has been set to the most significant fractional digit:

00011111.11100000
         ^
         C flag set to 1 to match this digit

We can round the integer result easily:

         adca  #0              A=32 (rounded result)

That gives us a more accurate result for this weird corner case where our fixed point multiplicands have exactly eight fractional digits between them.

Approximating division more accurately

If doing fixed point math with exactly eight fractional digits seems like a weird thing to build in support for, consider that it let lets you improve accuracy when approximating division of one eight-bit integer by an integer constant. The 6809 doesn't have hardware division, but if you can accept the result being sometimes off by one, then the approximation is quick and easy, multiplying by the reciprocal of the dividend, converted to a fixed-point binary fraction. You can do this without rounding and get results that are close, but often off by one. With rounding, you improve accuracy and get results that are more often correct.

Let's divide 2 by 3. This is done by multiplying 2 by 1/3. We can't represent 1/3 exactly in binary, but we can get close:

         lda   #2
         ldb   #%01010101      B=%.01010101=0.33203125
         mul                   D=%00000000.10101010=0.6640625

The integer part of the result is 0, which is only one off from the actual integer result, but we can do better:

         adca  #0              A=%00000001=1

For this case, where we are approximating dividing by three, then trying all 256 possible divisors without rounding gives us a mean absolute error of 0.66. Not bad. But with rounding, the mean absolute error goes down to 0.28. Now the majority of the integer results are exactly correct.

| improve this answer | |
6

Like it says, it is useful for rounding the most significant byte. If you are only interested getting a 8-bit answer, and thus want to discard the low 8 bits of the multiplication. If the answer would be 234.9 for example, taking the high 8 bits directly would truncate to 234. Rounding to 235 would be closer to result. It could be accomplished by a 16-bit addition of 0.5 (128) and then using the truncated high 8 bits. But as the carry flag is already set if least significant byte is 128 or more, it will be enough to add 0 with carry to most significant byte. That is actually a neat trick for rounding the result to 8 bits and is assisted by CPU in hardware.

| improve this answer | |
5

TL;DR:

It allows the use of MUL as part of a Multiply-Accumulate operation, by providing the rounding factor from the multiplication for a follow up addition.


The Long Read:

It's not about (mathematical) rounding of the result as 16 bit value, as a multiplication will never have any remainder. It's about supporting rounding toward an 8 bit value as it is needed for Multiply-Add (Multiply-Accumulate) operations, for example when scaling (*1).

By moving the relevant bit position into carry, it can be used for rounding 'for free' since ADCx dies adding and rounding in one step (Acc-old plus new-value plus Carry)

A multiply-add operation multiplies two factors and adds the result in an accumulator. That's basically an Acc = Acc + (B * C) operation. When using the 6809 MUL with two 8 bit number for B and C, the result in D can as well be seen as having an 8 bit result in A with the carry set if the high bit in B is set. Now a single ADC can be used to summ up this with the value of Acc:

      LDA   Bval    ; Load B Value (usually some input list)
      LDB   Cval    ; Load C Value (usually a scaling value)
      MUL           ; 8x8 Multiply to scale
      ADCA  Acc     ; Adding the Accumularoe (so far) plus new value plus Carry for rounding
      STA   Acc     ; Update Accumulator

It's a most nifty use of what is already present to provide a simple 8x8 bit multiplication as well as a cheap way to synthesize a MAC operation as needed for signal processing, graphics and many more.

MAC instructions are part of many modern micro controller and an essential part of any DSP, GPU and modern CPU's from ARM to x86. For example do x86 SIMD instruction sets include FMA instructions.


*1 - Scaling is just the most easy to understand example. There are many other uses.

| improve this answer | |
  • That is nifty. It looks to me like the scaling value needs to be a fixed-point fraction for this to work. Am I right? – Wayne Conrad Feb 17 at 22:38
  • 1
    Basically es. Then again, it depends all on what it is used for. MAC is essential for a huge number of problems. That's why MAC has been added in many variation to modern CPUs. GPU's have dedicated units for (F)MAC. FPGAs have premade MAC circuitry. And there is no DSP without. In fact, it's the most basic DSP instruction. – Raffzahn Feb 17 at 22:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.