Arithmetic and the flags

Arithmetic instructions put their result in the first operand: ADD AX, BX means AX = AX + BX.

InstructionDoes
ADD d, sd = d + s
SUB d, sd = d − s
INC d / DEC dd = d + 1 / d = d − 1
NEG dd = 0 − d (changes the sign)
ADC d, s / SBB d, sthe same as ADD / SUB, plus or minus the carry flag

The flags

Every result also sets single bits in the FLAGS register. Later instructions read them: the jumps of lesson 6 decide by them.

FlagSet to 1 when the result...
ZF zerois 0
SF signhas its top bit 1 (negative, as a signed number)
CF carrydid not fit as an unsigned number (a carry out of the top bit, or a borrow)
OF overflowdid not fit as a signed number
PF parityhas an even number of 1 bits in its low byte
AF auxiliarycarried out of the low 4 bits (used for decimal arithmetic)
; What ADD and SUB do to the flags.
.MODEL SMALL
.STACK 100H
.CODE
MAIN PROC
    MOV AL, 200
    ADD AL, 100       ; 300 does not fit in a byte: AL = 44, CF = 1
    MOV AL, 127
    ADD AL, 1         ; 128 is too big for a signed byte: OF = 1, SF = 1
    MOV AL, 5
    SUB AL, 5         ; 0: ZF = 1
    MOV AL, 3
    SUB AL, 5         ; below 0: AL = FEh (-2), CF = 1, SF = 1
    INC AL            ; FFh (-1); INC leaves CF alone
    NEG AL            ; 0 - (-1) = 1
    MOV AH, 4CH
    INT 21H
MAIN ENDP
END MAIN
Open in compiler

Step through it and watch the flags panel. 200 + 100 = 300 does not fit in a byte (0 to 255), so AL keeps 300 − 256 = 44 and CF = 1. 127 + 1 = 128 fits unsigned, but as a signed byte (−128 to 127) it overflows, so OF = 1 and SF = 1: the bits now read as −128.

One pattern, two meanings

The bits 11111111b are 255 as an unsigned number and −1 as a signed number (two's complement: to negate, flip every bit and add 1). The CPU does not know which you mean; it sets CF for the unsigned reading and OF for the signed one, and you choose which flag to look at. NEG AX with AX = 5 gives FFFBh, which is −5.

INC and DEC leave CF unchanged, which loops use to keep a carry across steps.

Numbers bigger than 16 bits

ADD a pair of registers at a time: add the low words with ADD, then the high words with ADC, which also adds the carry from the low words:

; Add two 32-bit numbers: 0001FFFFh + 00000001h = 00020000h.
.MODEL SMALL
.STACK 100H
.CODE
MAIN PROC
    MOV DX, 0001H     ; first number, high word
    MOV AX, 0FFFFH    ; first number, low word
    MOV CX, 0000H     ; second number, high word
    MOV BX, 0001H     ; second number, low word
    ADD AX, BX        ; low words: FFFFh + 1 = 0000h, CF = 1
    ADC DX, CX        ; high words plus the carry: 1 + 0 + 1 = 2, so DX:AX = 0002:0000h
    MOV AH, 4CH
    INT 21H
MAIN ENDP
END MAIN
Open in compiler

Common mistakes

  • Expecting a byte to hold 300: it wraps round, and CF tells you so.
  • Reading CF as a signed overflow. CF is for unsigned numbers; OF is for signed ones.
  • Expecting INC to set CF when a register wraps from FFFFh to 0: it sets ZF instead.
  • Writing ADD 5, AX: the result needs a place, so the destination comes first.

Quick check

1. AL = 250. After ADD AL, 10, what are AL and CF?
2. Which flag says a signed result did not fit?
3. AX = 5. What is AX after NEG AX?
4. Why does ADC follow ADD when adding 32-bit numbers?
My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.