This blog post details an 8086 assembly program that adds two 32-bit numbers. This example demonstrates handling multi-word arithmetic, showcasing the use of carry flags and conditional jumps for accurate results. Let’s get started!
data segment abc dd 12345678h def dd 9ABCDEF0h ghi dw ? data ends code segment assume cs:code, ds:data start: mov ax,data mov ds,ax 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 int 3 code ends end start
Understanding the Code
This program is more complex than adding 8-bit or 16-bit numbers because it requires handling potential carries between the lower and upper 16-bit words.
Data Segment:
- data segment: Defines the variables.
- abc dd 12345678h: Declares a 32-bit doubleword variable abc, initialized to 12345678h.
- def dd 9ABCDEF0h: Declares a 32-bit doubleword variable def, initialized to 9ABCDEF0h.
- ghi dd ?: Declares a 32-bit doubleword variable ghi to store the 32-bit sum (with potential overflow).
Flowchart:

Code Segment:
- assume cs:code, ds:data: This directive informs the assembler that the cs register will contain the address of the code segment, and the ds register will contain the address of the data segment. This is essential for the program to access both code and data correctly.
- start:: This label marks the beginning of the program’s execution.
- mov ax, data: This instruction moves the segment address of the data segment into the ax register. The data segment is where the variables abc, def, and ghi are declared.
- mov ds, ax: This instruction copies the value from the ax register (the data segment address) into the ds (data segment) register. This makes the data segment accessible to the program. Without this, the program wouldn’t be able to access the variables.
- mov dl, 00h: This instruction initializes the dl register to 0. dl will act as a temporary register to store the carry flag after adding the higher 16 bits. It essentially becomes a carry register to handle any overflow.
- ; Add lower 16 bits: This is a comment indicating the next section’s purpose.
- mov ax, word ptr abc: This loads the lower 16 bits of the 32-bit variable abc into the ax register. The word ptr directive is crucial; it explicitly tells the assembler that we are dealing with a 16-bit word and not a byte or a doubleword.
- mov bx, word ptr def: This loads the lower 16 bits of the 32-bit variable def into the bx register.
- add ax, bx: This adds the contents of bx to ax. The result (the sum of the lower 16 bits) is stored in ax.
- mov word ptr ghi, ax: This stores the result (from ax) into the lower 16 bits of the 32-bit variable ghi.
- ; Add higher 16 bits with carry: Another comment.
- mov ax, word ptr abc + 2: This loads the upper 16 bits of abc into ax. The + 2 offset is because each word (16 bits) takes up 2 bytes, so the upper word starts 2 bytes after the lower word.
- mov bx, word ptr def + 2: This loads the upper 16 bits of def into bx.
- adc ax, bx: This instruction is crucial. adc stands for “add with carry.” It adds the contents of bx to ax and also adds the carry flag. The carry flag is set if the previous add operation resulted in an overflow (the sum exceeded FFFFh). This ensures correct addition across the 32-bit boundary.
- mov word ptr ghi + 2, ax: This stores the sum of the upper 16 bits (including any carry) into the upper 16 bits of ghi.
- ; Check for carry after the second addition: Another comment.
- jnc move: This is a conditional jump instruction. jnc stands for “jump if no carry.” If the carry flag is not set after the adc instruction (meaning there was no overflow from adding the upper 16 bits), the program jumps to the move label.
- inc dl: If the carry flag was set after the adc (overflow from the higher 16 bits), this instruction increments the dl register.
- move:: This label marks the target of the jnc jump instruction.
- mov byte ptr ghi + 4, dl: This instruction stores the value of dl (0 or 1, indicating whether there was an overflow) into the highest byte of ghi. This is the most significant byte, and we are essentially storing any carry that occurred during the 32-bit addition.
- int 3: This instruction generates a software interrupt 3, which typically halts program execution and allows for debugging.
On High-Level
- Initialization: Variables are declared and initialized; a carry register (dl) is set to 0.
- Segment Setup: The data segment is made accessible.
- Lower 16-bit Addition: The lower 16 bits of the numbers are added.
- Higher 16-bit Addition with Carry: The upper 16 bits are added using adc to account for potential carries.
- Overflow Check: The carry flag is checked. If set, the carry register is incremented.
- Result Storage: The 32-bit sum (including the potential overflow byte) is stored in ghi.
- Program Termination: Execution is halted.
Output
C:\TASM>masm an32add.asm Microsoft (R) Macro Assembler Version 5.00 Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved. Object filename [an32add.OBJ]: Source listing [NUL.LST]: Cross-reference [NUL.CRF]: 50288 + 450368 Bytes symbol space free 0 Warning Errors 0 Severe Errors C:\TASM>link an32add.obj Microsoft (R) Overlay Linker Version 3.60 Copyright (C) Microsoft Corp 1983-1987. All rights reserved. Run File [AN32ADD.EXE]: List File [NUL.MAP]: Libraries [.LIB]: 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...... 0B97:0010 B8 97 0B 8E D8 B2 00 A1-00 00 8B 1E 04 00 03 C3 ................ 0B97:0020 A3 08 00 A1 02 00 8B 1E-06 00 13 C3 A3 0A 00 73 ...............s 0B97:0030 02 FE C2 88 16 0C 00 CC-63 83 C4 06 FF 36 24 21 ........c....6$! 0B97:0040 B8 0A 00 50 E8 47 5E 83-C4 04 5E 8B E5 5D C3 90 ...P.G^...^..].. 0B97:0050 55 8B EC 81 EC 84 00 C4-5E 04 26 80 7F 0A 00 74 U.......^.&....t 0B97:0060 3E 8B 46 08 8B 56 0A 89-46 FC 89 56 FE C4 5E FC >.F..V..F..V..^. 0B97:0070 26 8A 47 0C 2A E4 40 50-8B C3 05 0C 00 52 50 E8 &.G.*[email protected]. -q
Understanding the Memory Dump
This is the memory dump starting from address 0B97:0000
, showing the contents of memory. Here is the breakdown:
0B97:0000 78 56 34 12 F0 DE BC 9A-68 35 F1 AC 00 00 00 00 xV4.....h5......
- The value
78 56 34 12
representsabc = 12345678h
. - The value
F0 DE BC 9A
representsdef = 9ABCDEF0h
.
After the program runs:
ghi
would hold:ghi[0]
(lower word):68 4C
(result of the lower 16-bit addition5678 + DEF0
).ghi[2]
(upper word):F1 AC
(result of the upper 16-bit addition1234 + 9ABC + carry
).ghi[4]
would be the carry flag, stored indl
, either0
or1
based on whether there was a carry.
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