Lesson 22 of 48 intermediate

Arithmetic Instructions

ADD, SUB, MUL, DIV, and their variants — the 8086 math toolkit

Open interactive version (quiz + challenge)

Real-world analogy

Arithmetic instructions are like a calculator with some quirks. ADD/SUB are your basic +/- buttons. ADC/SBB are like adding/subtracting with a carry digit from the previous calculation — essential for multi-byte math. MUL/DIV are special: they always use fixed registers (AX, DX) like a calculator that only shows results on a specific screen.

What is it?

The 8086 arithmetic instructions perform addition (ADD/ADC/INC), subtraction (SUB/SBB/DEC/NEG), multiplication (MUL/IMUL), and division (DIV/IDIV) on 8-bit or 16-bit operands. Multi-precision arithmetic uses ADC/SBB to propagate carry/borrow. Multiplication results are double-width (8-bit×8-bit=16-bit in AX, 16-bit×16-bit=32-bit in DX:AX). Division requires proper setup of the dividend (CWD/CBW for signed). All arithmetic instructions affect the flags register.

Real-world relevance

These exact instructions form the backbone of all computation on x86 processors. When a C compiler generates code for 'a + b', it emits ADD. When it compiles 'x / y' for signed integers, it emits CWD followed by IDIV. Multi-precision libraries (like OpenSSL's bignum) still use ADC chains for arbitrary-precision arithmetic. The divide-by-zero exception (INT 0) from DIV is the origin of the 'Divide Error' crash message in DOS and early Windows.

Key points

Code example

; Arithmetic instruction showcase

; === Addition ===
MOV AX, 7FFFh      ; 32767 (max positive signed word)
ADD AX, 0001h      ; AX = 8000h
; CF=0 (no unsigned overflow: 32767+1=32768 OK)
; OF=1 (signed overflow: 32767+1 = -32768 WRONG!)

; === 32-bit Addition using ADC ===
; Add 0001FFFFh + 00000002h
MOV AX, 0FFFFh     ; Low word of first number
MOV DX, 0001h      ; High word of first number
ADD AX, 0002h      ; AX = 0001h, CF = 1 (carry!)
ADC DX, 0000h      ; DX = 0001h + 0 + 1 = 0002h
; DX:AX = 0002:0001h = 00020001h (correct!)

; === Subtraction ===
MOV AX, 0000h
SUB AX, 0001h      ; AX = FFFFh, CF=1 (borrow)
; Unsigned: 0-1 = 65535 (wrapped)
; Signed: 0-1 = -1 (correct)

; === Unsigned multiply ===
MOV AL, 200         ; AL = C8h (200)
MOV BL, 100         ; BL = 64h (100)
MUL BL              ; AX = 200 × 100 = 20000
                    ; AX = 4E20h, CF=OF=1 (result > 255)

; === Signed multiply ===
MOV AX, -500        ; AX = FE0Ch
MOV BX, 30          ; BX = 001Eh
IMUL BX             ; DX:AX = -500 × 30 = -15000
                    ; DX:AX = FFFFC568h

; === Unsigned divide ===
MOV DX, 0           ; Clear high word
MOV AX, 1000        ; Dividend = 1000
MOV BX, 7           ; Divisor = 7
DIV BX              ; AX = 142 (quotient)
                    ; DX = 6 (remainder)

; === Signed divide (with sign extension!) ===
MOV AX, -1000       ; AX = FC18h
CWD                 ; DX = FFFFh (sign extend)
MOV BX, 7
IDIV BX             ; AX = -142, DX = -6

Line-by-line walkthrough

  1. 1. ADD AX, 0001h with AX=7FFFh — unsigned result 8000h is fine (CF=0), but signed result wraps from +32767 to -32768 (OF=1). The flags tell you which interpretation overflowed.
  2. 2. ADD AX, 0002h with AX=FFFFh gives 0001h with CF=1 — this carry is crucial because ADC DX,0000h picks it up, correctly incrementing DX from 0001h to 0002h.
  3. 3. SUB AX, 0001h with AX=0000h gives FFFFh with CF=1 (borrow). As unsigned, 0-1 wrapped to 65535. As signed, this is simply -1.
  4. 4. MUL BL with AL=200, BL=100: unsigned multiply 200×100=20000, stored in AX=4E20h. CF and OF are set because the result does not fit in AL alone (AH is nonzero).
  5. 5. IMUL BX with AX=-500, BX=30: signed multiply gives -15000. DX:AX holds the 32-bit signed result FFFFC568h.
  6. 6. DIV BX — before dividing, we clear DX to 0 because DIV uses DX:AX as the 32-bit dividend. Forgetting to clear DX is a common bug that causes divide overflow.
  7. 7. CWD before IDIV — sign-extends AX=-1000 (FC18h) into DX=FFFFh. Without this, DX would be 0 from the earlier DIV, making the dividend wrong for signed division.
  8. 8. IDIV gives AX=-142 (quotient) and DX=-6 (remainder). The remainder has the same sign as the dividend, which is the 8086's convention.

Spot the bug

; Goal: Divide signed value -200 by 7
MOV AX, -200       ; AX = FF38h (-200)
MOV BX, 7          ; BX = 7
DIV BX             ; Divide!
; Expected: quotient = -28, remainder = -4
; But the program crashes with a divide error!
Need a hint?
DIV is for unsigned division. And what value is in DX?
Show answer
Two bugs: (1) DIV is unsigned division, not signed — it treats DX:AX as a large unsigned number. For signed division, use IDIV. (2) DX is not set up properly. DIV uses DX:AX as a 32-bit unsigned dividend, but DX contains whatever garbage was left from previous operations. Even if we fix to IDIV, we need CWD first to sign-extend AX into DX. Fix: MOV AX, -200 / CWD / MOV BX, 7 / IDIV BX. Now AX=-28 (quotient) and DX=-4 (remainder).

Explain like I'm 5

ADD is like putting two groups of marbles together and counting the total. SUB is like taking some marbles away. If you have too many marbles to fit in one jar (overflow), you need a second jar — that is why MUL puts big results in TWO registers. DIV is like splitting marbles into equal groups: the groups go in one register (quotient), and the leftover marbles go in another (remainder). ADC is like saying 'oh, there was one extra marble from last time — add that too!'

Fun fact

The 8086 MUL instruction takes between 70-118 clock cycles for a 16-bit multiply — that is almost 25 microseconds at 5 MHz! By comparison, a modern x86 processor does a 64-bit multiply in 3-4 clock cycles (about 1 nanosecond at 4 GHz). That is roughly a 25,000x improvement in multiply speed over 45 years of processor evolution.

Hands-on challenge

Write an 8086 routine that: (1) Adds two 32-bit unsigned numbers stored in DX:AX and CX:BX using ADD/ADC, (2) Multiplies a 16-bit unsigned number in AX by 10 without using MUL (hint: use shifts and ADD), (3) Divides a signed 32-bit number in DX:AX by a signed 16-bit number in BX, storing quotient and remainder separately, (4) Computes the absolute value of a signed number in AX (handle the edge case of -32768).

More resources

Open interactive version (quiz + challenge) ← Back to course: Microprocessor A–Z