algorithmassemblybit-shiftmotorolainteger-arithmetic

Assembly language using signed int multiplication math to perform shifts


This is a bit of a turn around.

Usually one is attempting to use shifts to perform multiplication and not the other way around.

On the Hitachi/Motorola 6309 there is no shift by n bits. There is only shift by 1 bit.

However there is a 16 bit x 16 bit signed multiply (provides a 32 bit signed result).

(EDIT) Using this is no problem for a 16 bit shift (left) however I'm trying to use 2 x 16x16 signed mults to do a 32 bit shift. The high order word of the result for the low order word shift is the problem. (Does that make sence?)

Some pseudo code might help:

result.highword = low word of (val.highword * shiftmulttable[shift])
temp = val.lowword * shiftmulttable[shift]
result.lowword = temp.lowword
result.highword = or (result.highword, temp.highword)
(with some magic on temp.highword to consider signed values)

I have been exercising my logic in an attempt to use this instruction to perform the shifts but so far I have failed.

I can easily achieve any positive value shifts by 0 to 14 but when it comes to shifting by 15 bits (mult by 0x8000) or shifting any negative values certain combinations of values require either:

And I just can't see any pattern to these values.

Any ideas appreciated!


Solution

  • Best I can tell from the problem description, implementing the 32-bit shift would work as desired by using an unsigned 16x16->32 bit multiply. This can easily be synthesized from a signed 16x16->32 multiply instruction by exploiting the two's complement integer representation. If the two factors are a and b, adding b to the high-order 16 bits of the signed product when a is negative, and adding a to the high-order 16 bits of the signed product when b is negative will give us the unsigned multiplication result.

    The following C code implements this approach and tests it exhaustively:

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    
    /* signed 16x16->32 bit multiply. Hardware instruction */
    int32_t mul16_wide (int16_t a, int16_t b)
    {
        return (int32_t)a * (int32_t)b;
    }
    
    /* unsigned 16x16->32 bit multiply (synthetic) */
    int32_t umul16_wide (int16_t a, int16_t b)
    {
        int32_t p = mul16_wide (a, b); // signed 16x16->32 bit multiply
        if (a < 0) p = p + (b << 16);  // add 'b' to upper 16 bits of product
        if (b < 0) p = p + (a << 16);  // add 'a' to upper 16 bits of product
        return p;
    }
    
    /* unsigned 16x16->32 bit multiply (reference) */
    uint32_t umul16_wide_ref (uint16_t a, uint16_t b)
    {
         return (uint32_t)a * (uint32_t)b;
    }
    
    /* test synthetic unsigned multiply exhaustively */
    int main (void)
    {
        int16_t a, b;
        int32_t res, ref;
        uint64_t count = 0;
    
        a = -32768;
        do {
            b = -32768;
            do {
                res = umul16_wide (a, b);
                ref = umul16_wide_ref (a, b);
                count++;
                if (res != ref) {
                    printf ("!!!! a=%d b=%d res=%d ref=%d\n", a, b, res, ref);
                    return EXIT_FAILURE;
                }
                if (b == 32767) break;
                b = b + 1;
            } while (1);
            if (a == 32767) break;
            a = a + 1;
        } while (1);
        printf ("test cases passed: %llx\n", count);
        return EXIT_SUCCESS;
    }
    

    I am not familiar with the Hitachi/Motorola 6309 architecture. I assume it uses a special 32-bit register to hold the result of a wide multiply, from which high and low half can be extracted into 16-bit general-purpose registers, and the conditional corrections can then be applied to the register holding the upper 16 bits.