Search code examples
assemblyx86-16bigintextended-precision

Assembly multiplication of 16-bit x 32-bit => 48-bit


Assume I want to multiply a large number by another (maybe small) number in assembly. The big number (multiplicand) is saved in DX:AX and the multiplier is saved in BX. The MUL instruction only operates on AX. So what to do with DX?

For example, the number is 0001:0000H (65536) and I want to multiply it by 2.

number     dw   0000h, 0001h
...
mov    ax, [number]
mov    dx, [number+2]
mov    bx, 2
mul    bx   ; it is ax*2 or 0000*2

Therefore the result is zero! Any idea on that?


Solution

  • Let's pretend this is 286, so you don't have eax.

    number dd 0x12345678      ; = dw 0x5678, 0x1234
    result dw 0, 0, 0         ; 32b * 16b = 48b needed
        ...
        mov    ax,[number]    ; 0x5678
        mov    cx,[number+2]  ; 0x1234 ; cx, dx will be used later
        mov    bx,0x9ABC
        ; now you want unsigned 0x12345678 * 0x9ABC (= 0xB00DA73B020)
        mul    bx             ; dx:ax = 0x5678 * 0x9ABC
        ; ^ check instruction reference guide why "dx:ax"!
        xchg   cx,ax
        mov    di,dx          ; di:cx = intermediate result
        mul    bx             ; dx:ax = 0x1234 * 0x9ABC
        ; put the intermediate multiplication results together
        ; into one 48b number dx:di:cx
        add    di,ax
        adc    dx,0
        ; notice how I added the new result as *65536 to old result
        ; by using different 16bit registers
    
        ; store the result
        mov    [result],cx
        mov    [result+2],di
        mov    [result+4],dx
    

    It's the same way as when you multiply numbers on paper, just you don't move by *10 components, but exploit the 16b register size nature to move by *65536 (0x10000) components to make it in less steps.

    I.e.

      13
    * 37
    ----
      91 (13 * 7)
     39_ (13 * 3, shifted left by *base (=10))
    ---- (summing the intermediate results, the 39 "shifted")
     481 (13 * 37)