8086 Assembly Program to Multiply Two 32-bit Numbers

Multiplying two 32-bit numbers on the 16-bit 8086 is the most involved of the basic arithmetic operations. Because the processor only has a 16×16 → 32 multiplier, a 32×32 multiplication must be broken into four 16×16 partial products and their results accumulated with careful carry propagation. The final 64-bit result spans eight bytes across four 16-bit words. This post walks through a working implementation in three environments: MASM/TASM, emu8086, and NASM.

Prerequisites: Familiarity with 16-bit multiplication (MUL) and carry propagation. Read 8086 Assembly Program to Multiply Two 16-bit Numbers first.


The Problem: Multiplying 12345678h by 12345678h

We want to compute 12345678h × 12345678h (305,419,896 squared). The 64-bit result is 014B66DC_1DF4D840h. The debugger memory dump confirms ghi = 40 D8 F4 1D DC 66 4B 01 — reading as little-endian 64-bit: lower 32 bits 1DF4D840h, upper 32 bits 014B66DCh ✓.

💡 Side note — why four partial products? A 32-bit number has a low word (bits 0–15) and a high word (bits 16–31). To multiply two 32-bit numbers A×B, expand as (A_hi×2¹⁶ + A_lo) × (B_hi×2¹⁶ + B_lo) = A_lo×B_lo + (A_hi×B_lo + A_lo×B_hi)×2¹⁶ + A_hi×B_hi×2³². Each term is a 16×16 product that the 8086 can handle. The four partial products are then accumulated into the correct bit positions of the 64-bit result.

Version 1 — MASM / TASM (Classic DOS Toolchain)

data segment
abc dd 12345678H    ; 32-bit multiplicand
def dd 12345678H    ; 32-bit multiplier
ghi dq ?            ; 64-bit result (dq = define quadword)
data ends

code segment
assume cs:code, ds:data
start:
mov ax, data
mov ds, ax

; Partial product 1: abc_lo * def_lo -> ghi[0] and carry into CX
mov ax, word ptr abc
mul word ptr def
mov word ptr ghi, ax    ; Store bits 0-15 of result
mov cx, dx              ; CX holds carry (bits 16-31, partial)

; Partial product 2: abc_hi * def_lo -> accumulate into CX and BX
mov ax, word ptr abc+2
mul word ptr def
add cx, ax              ; Accumulate into CX
mov bx, dx             ; BX = carry from this partial product
jnc move
add bx, 0001H           ; Propagate carry into BX

move:
; Partial product 3: abc_lo * def_hi -> accumulate into CX and BX
mov ax, word ptr abc
mul word ptr def+2
add cx, ax
mov word ptr ghi+2, cx  ; Store bits 16-31
mov cx, dx
jnc ma
add bx, 0001H

ma:
; Partial product 4: abc_hi * def_hi -> accumulate CX and DX
mov ax, word ptr abc+2
mul word ptr def+2
add cx, ax
jnc mb
add dx, 0001H

mb:
add cx, bx
mov word ptr ghi+4, cx  ; Store bits 32-47
jnc mc
add dx, 0001H

mc:
mov word ptr ghi+6, dx  ; Store bits 48-63
int 3
code ends
end start
⚠️ Common mistake — using dw instead of dq for the result: The maximum 32×32 product is FFFFFFFF² = FFFFFFFE_00000001h, which needs 64 bits (8 bytes). Declaring ghi dw ? reserves only 2 bytes. The program writes 8 bytes across ghi through ghi+7, silently corrupting memory beyond the variable. Always declare the result with dq (define quadword, 8 bytes) or db 8 dup(0).


Step-by-Step Explanation

1. Data Segment:

  • abc dd 12345678H — 32-bit multiplicand.
  • def dd 12345678H — 32-bit multiplier (same value).
  • ghi dq ? — 64-bit result buffer (8 bytes).

2. Code Segment — Four Partial Products:

  • abc_lo × def_lo: Lowest 16×16 product. Low 16 bits stored directly to ghi[0]; high 16 bits held in CX.
  • abc_hi × def_lo: Middle-low partial. Result accumulated into CX; carry propagated into BX.
  • abc_lo × def_hi: Middle-high partial. CX finalized and stored to ghi[2]; carry continues into BX and DX.
  • abc_hi × def_hi: Highest partial. Results stored to ghi[4] and ghi[6].

3. Overall Process:

  • Four 16×16 partial products are computed and accumulated with careful carry tracking.
  • The 64-bit result is assembled across ghi, ghi+2, ghi+4, and ghi+6.

Output

C:TASM>masm an_32mul.asm
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987.  All rights reserved.

      0 Warning Errors
      0 Severe  Errors

C:TASM>link an_32mul.obj
LINK : warning L4021: no stack segment

C:TASM>debug an_32mul.exe
-g

AX=5A90  BX=0626  CX=66DC  DX=014B  SP=0000  BP=0000  SI=0000  DI=0000
DS=0B97  ES=0B87  SS=0B97  CS=0B98  IP=0052   NV UP EI PL NZ NA PO NC
0B98:0052 CC            INT     3
-d 0B97:0000
0B97:0000  78 56 34 12 78 56 34 12-40 D8 F4 1D DC 66 4B 01  [email protected].
-q


Understanding the Memory Dump

0B97:0000  78 56 34 12  78 56 34 12  40 D8 F4 1D  DC 66 4B 01
Address RangeContentDescription
0B97:0000–000778 56 34 12 78 56 34 12abc and def, both 12345678h in little-endian
0B97:0008–000B40 D8 F4 1DLower 32 bits of result: 1DF4D840h
0B97:000C–000FDC 66 4B 01Upper 32 bits of result: 014B66DCh

Full 64-bit result: 014B66DC_1DF4D840h — which equals 12345678h² = 305,419,896² = 93,281,182,073,217,024 decimal.


Version 2 — emu8086 (Windows Emulator)

Tested with: emu8086 v4.08 on Windows 10.

The emu8086 version translates the complex 32-bit multiplication logic into a format ready for the built-in debugger. This version uses the standard COM template, making it easy to single-step through each partial product and carry propagation.

; emu8086 version -- 8086 Assembly Program to Multiply Two 32-bit Numbers
#make_COM#

org 100h

; --- Code ---
start:
mov ax, word ptr abc
mul word ptr def
mov word ptr ghi, ax
mov cx, dx

mov ax, word ptr abc+2
mul word ptr def
add cx, ax
mov bx, dx
jnc move
add bx, 0001H

move:
mov ax, word ptr abc
mul word ptr def+2
add cx, ax
mov word ptr ghi+2, cx
mov cx, dx
jnc ma
add bx, 0001H

ma:
mov ax, word ptr abc+2
mul word ptr def+2
add cx, ax
jnc mb
add dx, 0001H

mb:
add cx, bx
mov word ptr ghi+4, cx
jnc mc
add dx, 0001H

mc:
mov word ptr ghi+6, dx

    mov ax, 4c00h
    int 21h

; --- Data (after code to avoid execution as instructions) ---
abc dd 12345678H
def dd 12345678H
ghi dq 0

Version 3 — NASM (Modern Open-Source Assembler)

Tested with: NASM 2.16.01, DOSBox 0.74-3.

For modern environments, the NASM version provides a clean implementation with explicit memory references. This version is ideal for users who prefer working with the command line or integrating assembly into a modern development pipeline.

; NASM version -- 8086 Assembly Program to Multiply Two 32-bit Numbers
; nasm -f bin an32mul.asm -o an32mul.com

bits 16
org  100h

start:
mov ax, [abc]
mul word [def]
mov [ghi], ax
mov cx, dx

mov ax, [abc+2]
mul word [def]
add cx, ax
mov bx, dx
jnc move
add bx, 1

move:
mov ax, [abc]
mul word [def+2]
add cx, ax
mov [ghi+2], cx
mov cx, dx
jnc ma
add bx, 1

ma:
mov ax, [abc+2]
mul word [def+2]
add cx, ax
jnc mb
add dx, 1

mb:
add cx, bx
mov [ghi+4], cx
jnc mc
add dx, 1

mc:
mov [ghi+6], dx

    mov ax, 4c00h
    int 21h

; --- Data ---
abc dd 12345678H
def dd 12345678H
ghi dq 0

Frequently Asked Questions

Why does 32×32-bit multiplication need a 64-bit result?

The maximum 32-bit value is FFFFFFFFh. Squaring it gives FFFFFFFE_00000001h, which requires 64 bits. Just as 16×16 overflows into DX:AX (32 bits), 32×32 overflows into an 8-byte quadword. The 8086 has no native 32×32 instruction, so the software must assemble the 64-bit result from four 16×16 partial products.

Why are there multiple JNC / carry-increment sequences?

Each time two partial products are added together with ADD CX, AX, the sum can overflow 16 bits. Each such overflow must be counted and propagated into the next higher word of the result. The pattern JNC skip / ADD BX, 1 is the manual carry-propagation mechanism — BX and DX accumulate carries that are folded in when their corresponding result words are written.

What is dq and why is it needed for the result?

dq stands for “define quadword” and reserves 8 bytes — enough to hold a 64-bit value. Using dw (2 bytes) or dd (4 bytes) for ghi means the program writes beyond the declared variable into adjacent memory, silently corrupting other data. Always match the result variable size to the widest possible product.


Conclusion

32-bit multiplication on the 8086 decomposes into four 16×16 partial products that are accumulated into a 64-bit result. The algorithm is more involved than simpler arithmetic operations, but every step follows directly from the schoolbook long-multiplication method applied at the word level. The critical correctness requirements are: use dq for the result, propagate every carry between partial products, and store all four 16-bit words of the result.


See Also

4 thoughts on “8086 Assembly Program to Multiply Two 32-bit Numbers”

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.