Arithmetic Instructions
ADD, SUB, MUL, DIV, and their variants — the 8086 math toolkit
Open interactive version (quiz + challenge)Real-world analogy
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
- ADD — Addition — ADD adds the source to the destination and stores the result in the destination. Affects CF, ZF, SF, OF, PF, AF. Works with 8-bit or 16-bit operands. One operand must be a register (no memory-to-memory ADD).
- ADC — Add with Carry — ADC adds source + destination + CF (Carry Flag). Essential for multi-byte/multi-word addition where the carry from the lower part must propagate to the upper part. First ADD the low parts, then ADC the high parts.
- SUB and SBB — Subtraction — SUB subtracts source from destination. SBB subtracts source and borrow (CF) from destination. Like ADC, SBB is used for multi-word subtraction where the borrow propagates from the lower part to the upper.
- INC and DEC — Increment/Decrement — INC adds 1 to the operand. DEC subtracts 1. They affect ZF, SF, OF, PF, AF but NOT the Carry Flag (CF). This is important: INC/DEC preserve CF, which lets you use them in multi-precision loops without disturbing the carry chain.
- NEG — Negate (Two's Complement) — NEG computes the two's complement of the operand: result = 0 - operand. Effectively flips the sign of a signed number. Sets CF=1 unless the operand was 0. Affects all arithmetic flags.
- MUL — Unsigned Multiplication — MUL multiplies AL (8-bit) or AX (16-bit) by the source operand. For 8-bit: result in AX = AL × src. For 16-bit: result in DX:AX = AX × src. The source is always a register or memory, never immediate. Sets CF/OF if upper half is nonzero.
- IMUL — Signed Multiplication — IMUL is the signed version of MUL. It treats operands as signed 2's complement values. The result destination is the same: AX for 8-bit, DX:AX for 16-bit. CF/OF are set if the result cannot be represented in the lower half alone.
- DIV — Unsigned Division — DIV divides AX (8-bit divisor) or DX:AX (16-bit divisor) by the source. For 8-bit: AL=quotient, AH=remainder. For 16-bit: AX=quotient, DX=remainder. Division by zero or quotient overflow triggers INT 0 (divide error).
- IDIV — Signed Division — IDIV performs signed division. For 8-bit: AX / src -> AL=quotient, AH=remainder. For 16-bit: DX:AX / src -> AX=quotient, DX=remainder. The remainder has the same sign as the dividend. Use CBW or CWD to sign-extend before dividing.
- CBW and CWD — Sign Extension — CBW (Convert Byte to Word) sign-extends AL into AX. CWD (Convert Word to Doubleword) sign-extends AX into DX:AX. These are essential before IDIV — without sign extension, the upper half contains garbage and the division gives wrong results.
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 = -6Line-by-line walkthrough
- 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. 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. 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. 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. IMUL BX with AX=-500, BX=30: signed multiply gives -15000. DX:AX holds the 32-bit signed result FFFFC568h.
- 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. 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. 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?
Show answer
Explain like I'm 5
Fun fact
Hands-on challenge
More resources
- 8086 Arithmetic Instructions (GeeksforGeeks)
- ADD, SUB, MUL, DIV in 8086 (YouTube)
- 8086 Multiply and Divide (TutorialsPoint)
- x86 Instruction Reference (x86 Reference)