8086 Assembly Program for Subtraction of Two 32-bit Numbers

Subtracting two 32-bit numbers on the 16-bit 8086 uses the same two-pass approach as 32-bit addition, but with SBB (Subtract with Borrow) instead of ADC. The first pass subtracts the lower 16 bits with SUB; if that produces a borrow (CF=1), the second pass uses SBB to subtract the upper 16 bits and the borrow together. This post walks through a working implementation in three environments: MASM/TASM, emu8086, and NASM.

Prerequisites: Familiarity with 16-bit subtraction and carry/borrow flags. Read 8086 Assembly Program to Subtract Two 16-bit Numbers first.


The Problem: Subtracting 12345678h from 9ABCDEF0h

We want to compute 9ABCDEF0h − 12345678h. Manual check: lower words DEF0h − 5678h = 8878h, no borrow (CF=0). Upper words with SBB: 9ABCh − 1234h − 0 = 8888h. Full result: 88888878h. Memory dump confirms 78 88 88 88 at ghi ✓.


Version 1 — MASM / TASM (Classic DOS Toolchain)

data segment
    abc dd 9ABCDEF0h    ; 32-bit minuend
    def dd 12345678h    ; 32-bit subtrahend
    ghi db 5 dup(0)     ; 4 bytes result + 1 byte borrow flag
data ends

code segment
assume cs:code, ds:data
start:
    mov ax, data
    mov ds, ax
    mov dl, 00h             ; DL = borrow flag (0 = no borrow)

    ; Subtract lower 16 bits
    mov ax, word ptr abc
    mov bx, word ptr def
    sub ax, bx
    mov word ptr ghi, ax    ; Store lower word of result

    ; Subtract upper 16 bits with borrow from lower subtraction
    mov ax, word ptr abc+2
    mov bx, word ptr def+2
    sbb ax, bx              ; AX = AX - BX - CF
    mov word ptr ghi+2, ax  ; Store upper word of result

    jnc move                ; If no borrow out of upper subtraction, skip
    inc dl                  ; Record the borrow in DL
move:
    mov byte ptr ghi+4, dl  ; Store borrow flag
    int 3
code ends
end start
💡 Side note — SBB is to subtraction what ADC is to addition: SBB AX, BX computes AX = AX − BX − CF. The CF it reads is the borrow flag set by the previous SUB instruction. If the lower-word subtraction produced a borrow (CF=1), SBB automatically deducts that extra 1 from the upper-word subtraction. Using plain SUB for both words ignores the borrow and gives the wrong upper-word result whenever the lower words produce a borrow.


Step-by-Step Explanation

1. Data Segment

  • abc: 32-bit minuend (9ABCDEF0h).
  • def: 32-bit subtrahend (12345678h).
  • ghi: 5-byte result buffer — 4 bytes for the 32-bit difference, 1 byte for the borrow flag.

2. Code Segment

  • mov dl, 00h: Initialize borrow register.
  • mov ax, word ptr abc / mov bx, word ptr def: Load lower 16-bit words.
  • sub ax, bx: Subtract lower words. Sets CF if borrow.
  • sbb ax, bx: Subtract upper words including borrow from lower subtraction.
  • jnc move / inc dl: Record final borrow if the upper subtraction also borrows.

Flowchart


Output

C:TASM>masm an32sub.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 an32sub.obj
LINK : warning L4021: no stack segment

C:TASM>debug an32sub.exe
-g

AX=8888  BX=1234  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 NA PE NC
0B98:0027 CC            INT     3
-d 0B97:0000
0B97:0000  F0 DE BC 9A 78 56 34 12-78 88 88 88 00 00 00 00   ....xV4.x.......
-q

Confirm AX=8888 (upper word of result) and DX=0000 (DL=0, no final borrow). Result 88888878h stored at ghi as 78 88 88 88 ✓.


Understanding the Memory Dump

0B97:0000  F0 DE BC 9A 78 56 34 12-78 88 88 88 00 00 00 00
AddressHexDescriptionValue
0B97:0000F0 DE BC 9AMinuend abc9ABCDEF0h
0B97:000478 56 34 12Subtrahend def12345678h
0B97:000878 88 88 88Result ghi88888878h
0B97:000C00Borrow flag — 0 means no final borrow
⚠️ Common mistake — swapping the order of abc and def: SBB (like SUB) is not commutative. The code computes abc − def by loading abc into AX and def into BX, then subtracting. If you accidentally load them in reverse, you compute def − abc, get the two’s complement result, and the borrow flag fires when you did not expect it. Always double-check which variable is minuend (goes into AX) and which is subtrahend (goes into BX).

Version 2 — emu8086 (Windows Emulator)

Tested with: emu8086 v4.08 on Windows 10.

The emu8086 version allows you to easily track the borrow flag (CF) as it moves between the lower and upper word subtractions. This implementation uses the COM format, keeping the code and data in a single segment for simplicity.

; emu8086 version -- 8086 Assembly Program for Subtraction of Two 32-bit Numbers
#make_COM#

org 100h

; --- Code ---
start:
    mov dl, 00h

    mov ax, word ptr abc
    mov bx, word ptr def
    sub ax, bx
    mov word ptr ghi, ax

    mov ax, word ptr abc+2
    mov bx, word ptr def+2
    sbb 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 9ABCDEF0h
def dd 12345678h
ghi db 5 dup(0)

Version 3 — NASM (Modern Open-Source Assembler)

Tested with: NASM 2.16.01, DOSBox 0.74-3.

The NASM implementation is designed for those who prefer a more traditional command-line assembler. It uses standard x86 syntax and is ready to be compiled into a binary file for execution in any 16-bit DOS environment.

; NASM version -- 8086 Assembly Program for Subtraction of Two 32-bit Numbers
; nasm -f bin an32sub.asm -o an32sub.com

bits 16
org  100h

start:
    mov dl, 0

    mov ax, [abc]
    mov bx, [def]
    sub ax, bx
    mov [ghi], ax

    mov ax, [abc+2]
    mov bx, [def+2]
    sbb ax, bx
    mov [ghi+2], ax

    jnc move
    inc dl
move:
    mov [ghi+4], dl

    mov ax, 4c00h
    int 21h

; --- Data ---
abc dd 9ABCDEF0h
def dd 12345678h
ghi times 5 db 0

Frequently Asked Questions

Why use SBB for the upper word instead of a second SUB?

SBB AX, BX computes AX − BX − CF. The CF it reads is the borrow flag from the previous SUB. If the lower-word subtraction borrowed (CF=1), SBB automatically deducts that 1 from the upper-word result, keeping the multi-word arithmetic correct. Using SUB for both words ignores the borrow and produces an incorrect upper result in all cases where the lower words borrow.

What does DL=1 in ghi+4 mean?

It means the minuend was smaller than the subtrahend — the 32-bit subtraction produced a borrow out of the most significant bit. The stored result in ghi is the two’s complement wrap-around value, not a meaningful positive difference. Programs that need to distinguish between “result is valid” and “underflow occurred” check this byte after the operation.

Can I use more than two passes for numbers wider than 32 bits?

Yes. The pattern scales to any width: SUB the lowest word, then SBB for every subsequent word in order from least significant to most significant. A 64-bit subtraction would use SUB for the lowest 16 bits, then three SBB instructions for the remaining three words, each inheriting the borrow from the word before it.


Conclusion

32-bit subtraction on the 8086 mirrors the 32-bit addition pattern: SUB the lower words first, then SBB the upper words so the borrow propagates correctly. The key is always SBB for every word beyond the first — and making sure enough bytes are reserved for the result variable to hold both the 32-bit difference and the borrow flag.


See Also

2 thoughts on “8086 Assembly Program for Subtraction of Two 32-bit Numbers”

  1. Doesn’t work in the case of smaller number – larger number:

    ; 32 bit subtraction
    ; _______________

    assume cs:code, ds:data

    data segment
    opr1 dd 0ffffffffh
    opr2 dd 00000001h
    res dd 00000000h
    car db 00h
    data ends

    code segment
    org 0100h

    start: mov ax, data
    mov ds, ax
    mov ch, 00h
    mov ax, word ptr opr1
    mov bx, word ptr opr2
    sub ax, bx
    jnc here1
    neg ax

    here1: mov word ptr res, ax
    mov ax, word ptr opr1 + 2
    mov bx, word ptr opr2 + 2
    sbb ax, bx
    jnc here2
    neg ax
    inc ch

    here2: mov word ptr res+2, ax
    mov byte ptr res+4, ch
    mov ah, 4ch
    int 21h

    code ends

    end start

Leave a Reply

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