8086 Assembly Program to Add Two 32 bit Numbers

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

  1. Initialization: Variables are declared and initialized; a carry register (dl) is set to 0.
  2. Segment Setup: The data segment is made accessible.
  3. Lower 16-bit Addition: The lower 16 bits of the numbers are added.
  4. Higher 16-bit Addition with Carry: The upper 16 bits are added using adc to account for potential carries.
  5. Overflow Check: The carry flag is checked. If set, the carry register is incremented.
  6. Result Storage: The 32-bit sum (including the potential overflow byte) is stored in ghi.
  7. 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 represents abc = 12345678h.
  • The value F0 DE BC 9A represents def = 9ABCDEF0h.

After the program runs:

  • ghi would hold:
    • ghi[0] (lower word): 68 4C (result of the lower 16-bit addition 5678 + DEF0).
    • ghi[2] (upper word): F1 AC (result of the upper 16-bit addition 1234 + 9ABC + carry).
    • ghi[4] would be the carry flag, stored in dl, either 0 or 1 based on whether there was a carry.

7 thoughts on “8086 Assembly Program to Add Two 32 bit Numbers”

  1. 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

  2. ; 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

Leave a Reply

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