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
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
- Flags Register Layout — The 8086 flags register is 16 bits wide but only 9 bits are defined. Bits 0,2,4,6,7,11 are status flags (CF,PF,AF,ZF,SF,OF) set by arithmetic/logic operations. Bits 8,9,10 are control flags (TF,IF,DF) set by specific instructions.
- CF — Carry Flag — CF is set when an arithmetic operation produces a carry out of the MSB (addition) or a borrow into the MSB (subtraction) for unsigned interpretation. It indicates unsigned overflow. Also set by shift/rotate instructions to hold the last bit shifted out.
- ZF — Zero Flag — ZF is set to 1 when the result of an arithmetic or logical operation is exactly zero. ZF=0 when the result is nonzero. The most commonly tested flag — used for equality checks (CMP A,B / JE target) and loop termination (DEC CX / JNZ loop).
- SF — Sign Flag — SF is a copy of the MSB (most significant bit) of the result. For signed interpretation: SF=0 means positive or zero, SF=1 means negative. SF reflects bit 7 for byte operations and bit 15 for word operations.
- OF — Overflow Flag — OF is set when the signed result is too large (positive overflow) or too small (negative overflow) to fit in the destination. OF=1 means the sign of the result is wrong — adding two positives got a negative, or adding two negatives got a positive.
- PF — Parity Flag — PF is set to 1 if the low 8 bits of the result have an even number of 1-bits (even parity). PF=0 for odd parity. Originally used for serial communication error detection. Only checks the LOW byte, even for 16-bit operations.
- AF — Auxiliary Carry Flag — AF is set when there is a carry from bit 3 to bit 4 (out of the lower nibble). Used exclusively by BCD instructions (DAA, DAS, AAA, AAS) to detect when a nibble exceeds 9. Not directly testable with conditional jumps.
- IF — Interrupt Flag — IF controls whether maskable hardware interrupts (INTR pin) are recognized. IF=1 enables interrupts (STI), IF=0 disables them (CLI). NMI is not affected by IF. IF is automatically cleared when the CPU enters an interrupt handler.
- DF — Direction Flag — DF controls the direction of string operations (MOVSB, CMPSB, STOSB, etc.). DF=0 (CLD) means auto-increment SI/DI (forward). DF=1 (STD) means auto-decrement SI/DI (backward). Always set DF explicitly before string operations.
- TF — Trap Flag — TF enables single-step mode. When TF=1, the CPU generates an INT 1 (single-step interrupt) after every instruction. This is how debuggers implement single-stepping. TF is normally 0; debuggers set it temporarily via the saved flags on the stack.
- Conditional Jumps and Flag Combinations — Conditional jumps test specific flag conditions. JE/JZ tests ZF=1. JA (unsigned above) tests CF=0 AND ZF=0. JG (signed greater) tests ZF=0 AND SF=OF. Understanding which flags drive which jumps is key to writing correct branch logic.
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 backwardLine-by-line walkthrough
- 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. 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. 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. 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. 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. 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. CLI/STI control the IF flag. CLI is used before critical sections where interrupts could cause race conditions. STI re-enables interrupts afterward.
- 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?
Show answer
Explain like I'm 5
Fun fact
Hands-on challenge
More resources
- 8086 Flags Register (GeeksforGeeks)
- Flags and Conditional Jumps Explained (YouTube)
- Conditional Jump Instructions in 8086 (TutorialsPoint)
- x86 Flags Reference (Wikipedia)