1

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?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
mahmood
  • 23,197
  • 49
  • 147
  • 242
  • 1
    `ax` contains address of `0000`, not value. You probably wanted to do `mov ax,[number]` and `mov dx,[number+2]` (+2, because `dw` is 2 bytes). – Ped7g Oct 28 '16 at 17:59
  • Move you number from DX:AX into EAX :) – Jose Manuel Abarca Rodríguez Oct 28 '16 at 18:02
  • yeah, also is this 16bit x86 only? And do you want signed or unsigned numbers? (`mul` vs `imul`) – Ped7g Oct 28 '16 at 18:03
  • @Ped7g: Thanks. I fixed it... Yes it is a 16-bit code. So EAX is not valid here – mahmood Oct 28 '16 at 18:06
  • ax=0000h, so 2*0 = 0. What is the problem? – Jose Manuel Abarca Rodríguez Oct 28 '16 at 18:09
  • To multiply by 2, you use shifts :) To perform a generic 32x16 multiplication, you will need two 16 bit multiplications to multiply the low and the high word separately, then add the appropriate parts together. – Jester Oct 28 '16 at 18:09
  • 1
    Loading the dx register has no effect. The 16-bit multiply only uses dx as an output register: the high order bits. Here the result in dx:ax is all zeros. – Gene Oct 28 '16 at 18:11
  • Guys.. This is a simple snippet to demonstrate the problem. In 16-bit assembly how to you calculate 71345*13? The multiplicand is beyond a 16-bit register. Do you use shift? – mahmood Oct 28 '16 at 18:11
  • 2
    You can't multiply 32-bit numbers in 16-bit 8086 assembly with one instruction. That's what makes it a 16-bit instruction set. You need more than one 16-bit multiply and some shifts and additions: (a+b*2^16) * (c + d*2^16) = a*c + (b*c + a*d)*2^16 + (b*d*2^32). You might skip the last term since its value is larger than 32 bits. – Gene Oct 28 '16 at 18:16
  • @Gene: you can do it in one instruction with the x87 floating-point unit :P `fild dword [a]`, `fimul dword [b]` / `fistp qword [result]` should do the trick, at least if your inputs are signed integers. 80-bit x87 internal format can exactly represent every 64-bit signed integer, so this result should be exact. I haven't checked that 8087 supported 64-bit integer stores, but it definitely doesn't require x86-64. (gcc uses FILD/FISTP for 64-bit atomic loads/stores in 32-bit mode.) – Peter Cordes Oct 29 '16 at 07:12
  • Well, okay. Sure. The x87 instructions were optional (implemented in a separate chip that usually wasn't installed) in the original 16-bit machines. The OP didn't bother to describe the environment. – Gene Oct 29 '16 at 16:00

1 Answers1

5

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)
Ped7g
  • 16,236
  • 3
  • 26
  • 63
  • Having that 48b value as three words in memory will probably turn out to be highly impractical, adding one more word with zero value to make it at least valid 64b value would be probably better, then again it depends what you want to do with that value anyway. – Ped7g Oct 28 '16 at 18:21