The Intel 80386, part 3: Flags and condition codes


The flags register contains a bunch of stuff, but here are the flags easily accessible in the debugger:

Flag Clear/Set Meaning Notes
OF nv/ov Overflow
DF up/dn Direction Must be up at function boundaries
SF pl/ng Sign
IF ei/di Interrupts Set if interrupts are enabled
ZF nz/zr Zero
AF na/ac Auxiliary carry Not used by C code
PF pe/po Parity Not used by C code
CF nc/cy Carry

We'll learn about the direction flag when we get to string operations. The important detail for now is that the direction flag must be clear (up) at function boundaries.

Instructions for manipulating the interrupt flag are privileged, so you won't see user-mode code messing with it. I wouldn't normally have mentioned it, but the Windows disassembler displays the state of the interrupt flags in the register output, so I included it here just so you can see what it means (and then promptly forget about it).

The auxiliary carry is used to indicate whether a carry occurred between bits 3 and 4. It is used by the binary coded decimal instructions.

The parity is used to indicate whether the number of set bits in the least significant 8 bits of the result is odd or even.

The Clear/Set column denotes how the Windows disassembler represents flags in the register output:

eax=00000000 ebx=00000000 ecx=9f490000 edx=00000000 esi=7f19e000 edi=00000000
eip=77a93dad esp=0048f844 ebp=0048f870 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

The efl represents the value of the 32-bit flags register, and selected bits are parsed out and rendered as mnemonics on the line above.

Various combinations of conditions can be expressed with condition codes. Note that many conditions have multiple mnemonics. The first one listed is the one the disassembler uses.

Code Meaning Condition Notes
E Equal ZF
Z Zero
NE Not equal !ZF
NZ Not zero
A Above !CF && !ZF Unsigned greater than
NBE Not below or equal
AE Above or equal !CF Unsigned greater than or equal
NB Not below
NC No carry No unsigned overflow
B Below CF Unsigned less than
NAE Not above or equal
C Carry set Unsigned overflow
BE Below or equal CF || ZF Unsigned less than or equal
NA Not above
G Greater !(SF ^ OF) && !ZF Signed greater than
NLE Not less than or equal
GE Greater than or equal !(SF ^ OF) Signed greater than or equal
NL Not less than
L Less than (SF ^ OF) Signed less than
NGE Not greater than or equal
LE Less than or equal (SF ^ OF) || ZF Signed less than or equal
NG Not greater than
S Sign SF Negative
NS No sign !SF Positive or zero
O Overflow OF Signed overflow
NO No overflow !OF No signed overflow
P Parity PF Even number of bits set
PE Parity even
NP No parity !PF Odd number of bits set
PO Parity odd

The overflow and parity conditions are not normally used by C code. Note also that many flags are not testable via condition codes. (Poor auxiliary carry flag. Nobody loves you.)

There are a few instructions for directly manipulating selected flags:

    STC         ; set carry
    CLC         ; clear carry
    CMC         ; complement (toggle) carry

    STD         ; set direction (go down)
    CLD         ; clear direction (go up)

Controlling the interrupt flag is a privileged instruction, so you won't see it in user-mode code. There are no instructions for directly manipulating the other flags, but you can manipulate them indirectly by performing an arithmetic operation with a known effect on flags. For example, you can force ZF to be set by performing a calculation whose result is known to be zero, such as XOR EAX, EAX.

Okay, that was extremely boring, but it had to be done. Next time, we'll start doing arithmetic.

Comments (12)
  1. Why is the mnemonic for SF clear ‘pl’? There’s no L in ‘positive’. Why not ‘ps’ or ‘pv’?

    > There are no instructions for directly manipulating the other flags, but you can manipulate them indirectly by performing an arithmetic operation with a known effect on flags.
    I take it you’ll be covering PUSHF and POPF in a later article in this series?

    1. pl stands for “plus”.

    2. Darran Rowe says:

      While it is true that POPF can write to some of the flags and PUSHF can read from most of the flags, it is not as if they can work in solitude.
      PUSHF allows you to read the flags by first pushing to the stack and either reading from the stack or popping this to a register. POPF allows you to write to the flags by first pushing a value to the stack and then popping this value to the flags. Because it uses the stack, this is the very definition of indirect.

      1. Darran Rowe says:

        Also should mention, PUSHF and POPF does not toggle a single flag. If you want to set the zero flag then xor eax, eax does this. If you want to toggle this flag using PUSHF and POPF then first you will have to pushf, or with the correct value to set the zero flag, then popf. It is the same with the remainder of the flag toggles, if there is no instruction to toggle it then it takes 3 regular instructions to do it. So it is more efficient to use an operation which sets the flag.

      2. I’m well aware. The text “but you can manipulate them indirectly by performing an arithmetic operation with a known effect on flags” is referencing the fact that you can manipulate some of them by (say) performing an ADD instruction; PUSHF/POPF are not the arithmetic instructions being alluded to here.

  2. hyperman_ says:

    Wouldn’t the LAHF/SAHF instructions count as directly manipluating the flags register? E.g. mov AH,0 SAHF wil clear all flags even without any calculation

    1. FrostCat says:

      LAHF/SAHF and PUSHF/POPF are not really exactly equivalent. The set/clear flag instructions are designed to directly update a single flag. There is no equivalent for, say, the overflow flag (e.g., STO/CLO).

      Interestingly (?), LAHF doesn’t update DF, and POPF, in certain modes, doesn’t update all the flags.

    2. I meant “directly manipulating individual flags registers”. There is no “set overflow” instruction, for example.

    3. Darran Rowe says:

      I would honestly say no, as how I would not class PUSHF and POPF as directly manipulating them.
      The reason being simple, you are reading from or writing to part of EFLAGS using the AH register. So because they work via a register, they are indirect.

  3. Anonymous says:
    (The content was deleted per user request)
  4. Sjnaficy says:

    Why do you need a flag if you cannot branch on it? I’m talking about aux. Is there arithmetic instructions using it?

    1. As Raymond said, the aux flag is used by the carry mechanism of the BCD instructions. Which he may or may not ever discuss further. See here for more: https://en.wikipedia.org/wiki/Intel_BCD_opcode

      As indirectly discussed in the comments, PUSHF can be used to get access to all the flags for further bit twiddling that can then drive a branch if you really needed to code a branch on the aux flag value.

Comments are closed.

Skip to main content