Adding two 32-bit numbers on the 16-bit 8086 requires two passes: first add the lower 16-bit words, then add the upper 16-bit words using ADC (Add with Carry) to fold in any carry from the first pass. The carry propagation between the two halves is what makes multi-word arithmetic correct — skip it and the upper half of your result will be wrong whenever the lower addition overflows. This post walks through a working implementation in three environments: MASM/TASM, emu8086, and NASM.
Prerequisites: Familiarity with 8086 registers, the segment setup pattern, and 16-bit addition. Read 8086 Assembly Program to Add Two 16-bit Numbers first if you are new to this.
The Problem: Adding 12345678h and 9ABCDEF0h
We want to add 12345678h and 9ABCDEF0h and store the 32-bit result. Manual check: lower words 5678h + DEF0h = 13568h → low result 3568h, carry 1. Upper words 1234h + 9ABCh + 1 = ACF1h, no further carry. Full result: ACF13568h. The debugger confirms memory bytes 68 35 F1 AC at ghi ✓.
ADC vs ADD for the upper word: ADD AX, BX adds two values and ignores any previous carry. ADC AX, BX adds two values plus the current Carry Flag. For multi-word addition you must use ADD for the lowest word (there is no incoming carry yet) and ADC for every subsequent word. Using ADD for both words is a silent bug — it assembles cleanly and the result looks plausible, but will be off by 1 whenever the lower word addition produces a carry.
Version 1 — MASM / TASM (Classic DOS Toolchain)
data segment
abc dd 12345678h ; 32-bit addend 1
def dd 9ABCDEF0h ; 32-bit addend 2
ghi db 5 dup(0) ; 32-bit result (+ 1 overflow byte at ghi+4)
data ends
code segment
assume cs:code, ds:data
start:
mov ax, data
mov ds, ax
mov dl, 00h ; DL = carry flag register (0 = no carry yet)
; Add lower 16 bits
mov ax, word ptr abc
mov bx, word ptr def
add ax, bx
mov word ptr ghi, ax ; Store lower word of result
; Add upper 16 bits with carry from lower addition
mov ax, word ptr abc+2
mov bx, word ptr def+2
adc ax, bx ; AX = AX + BX + CF
mov word ptr ghi+2, ax ; Store upper word of result
jnc move ; If no carry out of upper addition, skip
inc dl ; Else record the overflow in DL
move:
mov byte ptr ghi+4, dl ; Store overflow byte
int 3
code ends
end start
ghi as dw instead of dd: The code writes to ghi, ghi+2, and ghi+4 — a total of 5 bytes. Declaring ghi dw ? reserves only 2 bytes; the writes to offsets 2 and 4 silently overwrite whatever memory follows in the data segment. Declare it as ghi dd ? (4 bytes for the 32-bit sum) and add a separate db ? for the overflow byte, or use db 5 dup(0) to reserve all 5 bytes explicitly.
Related Links:
Understanding the Code
Data Segment
abc dd 12345678h: 32-bit first addend.def dd 9ABCDEF0h: 32-bit second addend.ghi dd ?: 32-bit result variable (plus one extra byte atghi+4for the overflow flag).
Flowchart

Code Segment
mov dl, 00h: DL is used as a manual carry register. It starts at 0 and is incremented if the upper-word addition produces a carry out.mov ax, word ptr abc/mov bx, word ptr def: Load the lower 16 bits of each operand.word ptrtells the assembler to treat the 32-bit variable as a 16-bit read.add ax, bx: Add the lower words. May set CF if the sum exceeds FFFFh.adc ax, bx: Add the upper words plus CF from the lower addition. This is the essential instruction for correct 32-bit addition.jnc move/inc dl: If the upper-word addition also overflowed, record it in DL.
High-Level Overview
- Initialize DS and the carry register DL.
- Add lower 16-bit words with
ADD; store result and retain CF. - Add upper 16-bit words with
ADCto include the carry; store result. - Record any final overflow and halt.
Output
C:TASM>masm an32add.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 an32add.obj
LINK : warning L4021: no stack segment
C:TASM>debug an32add.exe
-g
AX=ACF1 BX=9ABC CX=0038 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B97 ES=0B87 SS=0B97 CS=0B98 IP=0027 NV UP EI NG NZ AC PO NC
0B98:0027 CC INT 3
-d 0B97:0000
0B97:0000 78 56 34 12 F0 DE BC 9A-68 35 F1 AC 00 00 00 00 xV4.....h5......
-q
Understanding the Memory Dump
0B97:0000 78 56 34 12 F0 DE BC 9A-68 35 F1 AC 00 00 00 00
78 56 34 12:abc = 12345678hin little-endian.F0 DE BC 9A:def = 9ABCDEF0hin little-endian.68 35 F1 AC:ghi = ACF13568hin little-endian. Low word3568hstored as68 35; high wordACF1hstored asF1 AC.00: Overflow byte atghi+4= 0 (no carry out of the 32-bit sum).
Version 2 — emu8086 (Windows Emulator)
Tested with: emu8086 v4.08 on Windows 10.
The emu8086 implementation provides a clean, visual way to observe how the carry flag propagates between the two addition passes. It follows the standard COM file structure with data placed at the end of the source.
; emu8086 version -- 8086 Assembly Program to Add Two 32-bit Numbers
#make_COM#
org 100h
; --- Code ---
start:
mov dl, 00h
mov ax, word ptr abc
mov bx, word ptr def
add ax, bx
mov word ptr ghi, ax
mov ax, word ptr abc+2
mov bx, word ptr def+2
adc ax, bx
mov word ptr ghi+2, ax
jnc move
inc dl
move:
mov byte ptr ghi+4, dl
mov ax, 4c00h
int 21h
; --- Data (after code to avoid execution as instructions) ---
abc dd 12345678h
def dd 9ABCDEF0h
ghi db 5 dup(0) ; 4 bytes result + 1 byte overflow flag
Version 3 — NASM (Modern Open-Source Assembler)
Tested with: NASM 2.16.01, DOSBox 0.74-3.
The NASM version is highly portable and follows modern x86 assembly conventions. It explicitly handles memory addressing with square brackets, making the code clear and easy to integrate into larger 16-bit projects.
; NASM version -- 8086 Assembly Program to Add Two 32-bit Numbers
; nasm -f bin an32add.asm -o an32add.com
bits 16
org 100h
start:
mov dl, 0
mov ax, [abc]
mov bx, [def]
add ax, bx
mov [ghi], ax
mov ax, [abc+2]
mov bx, [def+2]
adc ax, bx
mov [ghi+2], ax
jnc move
inc dl
move:
mov [ghi+4], dl
mov ax, 4c00h
int 21h
; --- Data ---
abc dd 12345678h
def dd 9ABCDEF0h
ghi times 5 db 0 ; 4 bytes result + 1 byte overflow
Frequently Asked Questions
Why use ADC for the upper word instead of a second ADD?
ADC (Add with Carry) adds its two operands plus the current CF. When the lower-word ADD overflows, CF is set to 1. The upper-word addition must include this carry or the result will be wrong by 1 for every case where the lower half overflows. ADD ignores CF entirely, so using it for the upper word silently drops the carry.
What does word ptr do when reading from a dd variable?
dd declares a 32-bit (4-byte) variable. When you write mov ax, word ptr abc, the word ptr override tells the assembler to read only 2 bytes from the address of abc — the lower 16 bits. Adding +2 to the address skips the first word and reads the upper 16 bits. Without word ptr, the assembler would see a size mismatch between a dd variable and a 16-bit register and refuse to assemble.
Can the 32-bit sum ever exceed 32 bits?
Yes. The maximum 32-bit sum is FFFFFFFFh + FFFFFFFFh = 1FFFFFFFEh, which needs 33 bits. The program handles this with the DL register — if the upper-word ADC produces a carry, DL is incremented to 1 and stored at ghi+4. In the current example both values sum to ACF13568h, which fits in 32 bits, so DL stays 0.
Conclusion
32-bit addition on the 8086 is the 16-bit pattern applied twice: ADD the lower words, then ADC the upper words. The one rule to never forget is using ADC for every word beyond the first so that carry propagates correctly across the word boundary.
Superb yar
Cool bro.
Can you show how to add 16bit+8bit or 16bit-8bit ?
good
mov ax, word ptr abc + 2
mov bx, word ptr def + 2
adc ax, bx ; makes no sense
mov byte ptr ghi+4, dl ; WTF ghi + 4 actually points outside of the data segment.
here’s how it’s done:
jmp @f
extra dw 0
n1 dd 0FFFFFFFFh
n2 dd 0FFFFFFFFh
@@:
mov ax, word ptr [n1]
mov bx, word ptr [n1+2]
mov cx, word ptr [n2]
mov dx, word ptr [n2+2]
add ax, cx
adc bx, dx
adc [extra], 0
the result is ACF13568 ?
where is 3568 stored?
But 8086 cannot access 32 bit how can u load?
; ADDs two 32-bit integers into a 64-bit integer using 16-bit CPU (8086-80286)
;
; ML ADD.ASM
; DEBUG ADD.EXE
; -g
; -d DS:0 F
; -q
; NOTE: x86 integers are Little Endian and so appear inverted in memory
data segment
a1 dd 0FFFFFFFFh ; 0xFFFFFFFF +
a2 dd 2h ; 0x00000002 =
res dq ? 0x0000000100000001
data ends
code segment
assume cs:code, ds:data
start:
mov ax, data
mov ds, ax
xor dl, dl
mov ax, word ptr a1 ; add 1st word and store result
mov bx, word ptr a2
add ax, bx
mov word ptr res, ax
mov ax, word ptr a1+2 ; add 2nd word *with carry* and store result
mov bx, word ptr a2+2
adc ax, bx
mov word ptr res+2, ax
jnc move
inc dl ; in case of an overflow, result’s 3rd word becomes 1
move:
mov byte ptr res+4, dl
int 3 ; breaks DEBUG
mov ax, 4C00h ; terminates DOS program (if called from prompt)
int 21h
code ends
end start