Lesson 24 of 48 intermediate

Flags Register and Condition Codes

CF, ZF, SF, OF, PF, AF, DF, IF, TF — the 9 flags that drive every decision the 8086 makes

Open interactive version (quiz + challenge)

Real-world analogy

The flags register is like a dashboard in a car. CF (Carry) is the fuel warning light — it means something overflowed in unsigned math. ZF (Zero) is the odometer hitting exactly 000000. SF (Sign) indicates whether you are going forward or backward. OF (Overflow) is the speedometer needle going past the maximum into the red zone. The driver (conditional jump instructions) checks these lights to decide what to do next.

What is it?

The 8086 flags register contains 9 flags that record the results of operations and control processor behavior. Six status flags (CF, ZF, SF, OF, PF, AF) are set by arithmetic and logical instructions. Three control flags (IF, DF, TF) are set by specific instructions to control interrupts, string direction, and single-step debugging. Conditional jumps test flag combinations to implement branches, loops, and comparisons.

Real-world relevance

Every if-statement, while-loop, and comparison in every program compiled for x86 ultimately comes down to flags and conditional jumps. When you write 'if (x > 0)' in C, the compiler emits CMP followed by JLE (to skip the if-body). The flags register is the single most important mechanism for program control flow. Modern x86 processors have a FLAGS register expanded to 64 bits (RFLAGS) but the original 9 flags from the 8086 are all still there and work exactly the same way.

Key points

Code example

; Flags register deep dive with examples

; === CF: Carry Flag ===
MOV AL, 200         ; AL = C8h (unsigned 200)
ADD AL, 100         ; AL = 2Ch, CF = 1
                    ; 200+100=300 > 255 -> carry!
JC unsigned_overflow ; Jump if CF=1

; === ZF: Zero Flag ===
MOV AX, 10
SUB AX, 10          ; AX = 0, ZF = 1
JZ result_is_zero   ; Jump if ZF=1

; === SF: Sign Flag ===
MOV AX, 1
SUB AX, 5           ; AX = FFFCh (-4), SF = 1
JS result_negative   ; Jump if SF=1 (result < 0)

; === OF: Overflow Flag ===
MOV AL, 100          ; Signed: +100
ADD AL, 50           ; AL = 96h (150 unsigned, -106 signed)
                     ; OF = 1! +100+50=150 > 127
JO signed_overflow   ; Jump if OF=1

; === Combined: signed comparison ===
; Is AX > BX (signed)?
MOV AX, -5
MOV BX, -10
CMP AX, BX           ; -5 - (-10) = +5
                      ; ZF=0, SF=0, OF=0
                      ; SF=OF and ZF=0 -> AX > BX (signed)
JG ax_is_greater      ; True! -5 > -10

; === Combined: unsigned comparison ===
MOV AX, 0FFF0h       ; 65520 unsigned
MOV BX, 0010h        ; 16 unsigned
CMP AX, BX           ; FFF0h - 0010h = FFE0h
                      ; CF=0, ZF=0 -> AX above BX
JA ax_is_above        ; True! 65520 > 16

; === Control flags ===
CLI                   ; IF=0 — disable interrupts
STI                   ; IF=1 — enable interrupts
CLD                   ; DF=0 — string ops go forward
STD                   ; DF=1 — string ops go backward

Line-by-line walkthrough

  1. 1. ADD AL, 100 with AL=200: unsigned result is 300, which overflows 8 bits. AL wraps to 44 (2Ch) and CF=1 signals the carry.
  2. 2. SUB AX, 10 with AX=10: result is exactly 0, so ZF=1. This is the most common way to check for equality — CMP sets ZF=1 when operands are equal.
  3. 3. SUB AX, 5 with AX=1: result is FFFCh (-4). The MSB is 1, so SF=1 indicating a negative result in signed interpretation.
  4. 4. ADD AL, 50 with AL=100: unsigned result 150 fits in a byte (CF=0), but signed result 150 exceeds +127 (OF=1). The same operation can overflow in signed but not unsigned.
  5. 5. CMP AX, BX with AX=-5 and BX=-10: the subtraction -5-(-10) = +5. Result is positive (SF=0) and no overflow (OF=0). Since SF=OF and ZF=0, JG is taken — correctly indicating -5 > -10.
  6. 6. CMP AX, BX with AX=FFF0h and BX=0010h: unsigned subtraction FFF0h-0010h = FFE0h with no borrow (CF=0). Since CF=0 and ZF=0, JA is taken — correctly indicating 65520 > 16 unsigned.
  7. 7. CLI/STI control the IF flag. CLI is used before critical sections where interrupts could cause race conditions. STI re-enables interrupts afterward.
  8. 8. CLD/STD control the DF flag for string operations. Most code uses CLD (forward direction). The ABI convention requires DF=0 at function entry.

Spot the bug

; Goal: Compare two signed numbers and jump to the
; correct label
MOV AX, 0FFF0h      ; AX = -16 (signed)
MOV BX, 0010h       ; BX = +16 (signed)
CMP AX, BX
JA ax_bigger         ; Jump if AX > BX
JMP ax_smaller
;
; Programmer expects: -16 < 16, should go to ax_smaller
; But program jumps to ax_bigger!
Need a hint?
JA is for unsigned comparisons. What unsigned value is FFF0h?
Show answer
Bug: JA (Jump if Above) uses UNSIGNED comparison — it tests CF=0 AND ZF=0. In unsigned terms, FFF0h = 65520 and 0010h = 16, so 65520 > 16 and JA is taken. But the programmer intended a SIGNED comparison where FFF0h = -16 and -16 < +16. Fix: Use JG (Jump if Greater) for signed comparison instead of JA. JG tests ZF=0 AND SF=OF. After CMP -16, +16: the result is negative (SF=1) and no overflow (OF=0), so SF != OF, meaning JG is NOT taken — correctly indicating -16 is not greater than +16.

Explain like I'm 5

Flags are like little sticky notes the CPU leaves for itself after doing math. 'Hey, the answer was zero!' (ZF). 'Hey, there was too much to fit!' (CF). 'Hey, the answer is negative!' (SF). Then when the CPU reaches a fork in the road (a conditional jump), it checks its sticky notes to decide which way to go. IF is like a 'Do Not Disturb' sign — when IF=0, no one is allowed to interrupt the CPU.

Fun fact

The Parity Flag (PF) was included in the 8086 primarily for backward compatibility with the 8080/8085, where it was used for serial communication error checking. In the decades since, PF found a surprising second life: modern compilers and CPUs use it for floating-point comparisons! The x87 FPU comparison instructions (FCOM, FUCOM) set flags in the FPU status word, which are then loaded into the CPU flags — PF indicates an 'unordered' (NaN) comparison result.

Hands-on challenge

Write a complete 8086 routine that implements a 'clamp' function: given a signed value in AX, a minimum in BX, and a maximum in CX, clamp AX to the range [BX, CX]. Use CMP and the correct signed conditional jumps (JL, JG). Then write a routine that takes an unsigned value in AX and prints whether it is zero, positive, or has the high bit set — using only TEST, JZ, and JS. Finally, explain why JA and JG give different results when comparing FFFFh with 0001h.

More resources

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