Intel x86 Assembly Language & Microarchitecture Register Fundamentals Flags register


When the x86 Arithmetic Logic Unit (ALU) performs operations like NOT and ADD, it flags the results of these operations ("became zero", "overflowed", "became negative") in a special 16-bit FLAGS register. 32-bit processors upgraded this to 32 bits and called it EFLAGS, while 64-bit processors upgraded this to 64 bits and called it RFLAGS.

Condition Codes

But no matter the name, the register is not directly accessible (except for a couple of instructions - see below). Instead, individual flags are referenced in certain instructions, such as conditional Jump or conditional Set, known as Jcc and SETcc where cc means "condition code" and references the following table:

Condition CodeNameDefinition
E, ZEqual, ZeroZF == 1
NE, NZNot Equal, Not ZeroZF == 0
OOverflowOF == 1
NONo OverflowOF == 0
SSignedSF == 1
NSNot SignedSF == 0
PParityPF == 1
NPNo ParityPF == 0
C, B, NAECarry, Below, Not Above or EqualCF == 1
NC, NB, AENo Carry, Not Below, Above or EqualCF == 0
A, NBEAbove, Not Below or EqualCF==0 and ZF==0
NA, BENot Above, Below or EqualCF==1 or ZF==1
GE, NLGreater or Equal, Not LessSF==OF
NGE, LNot Greater or Equal, LessSF!=OF
G, NLEGreater, Not Less or EqualZF==0 and SF==OF
NG, LENot Greater, Less or EqualZF==1 or SF!=OF

In 16 bits, subtracting 1 from 0 is either 65,535 or -1 depending on whether unsigned or signed arithmetic is used - but the destination holds 0xFFFF either way. It's only by interpreting the condition codes that the meaning is clear. It's even more telling if 1 is subtracted from 0x8000: in unsigned arithmetic, that merely changes 32,768 into 32,767; while in signed arithmetic it changes -32,768 into 32,767 - a much more noteworthy overflow!

The condition codes are grouped into three blocks in the table: sign-irrelevant, unsigned, and signed. The naming inside the latter two blocks uses "Above" and "Below" for unsigned, and "Greater" or "Less" for signed. So JB would be "Jump if Below" (unsigned), while JL would be "Jump if Less" (signed).

Accessing FLAGS directly

The above condition codes are useful for interpreting predefined concepts, but the actual flag bits are also available directly with the following two instructions:

  • LAHF Load AH register with Flags
  • SAHF Store AH register into Flags

Only certain flags are copied across with these instructions. The whole FLAGS / EFLAGS / RFLAGS register can be saved or restored on the stack:

  • PUSHF / POPF Push/pop 16-bit FLAGS onto/from the stack
  • PUSHFD / POPFD Push/pop 32-bit EFLAGS onto/from the stack
  • PUSHFQ / POPFQ Push/pop 64-bit RFLAGS onto/from the stack

Note that interrupts save and restore the current [R/E]FLAGS register automatically.

Other Flags

As well as the ALU flags described above, the FLAGS register defines other system-state flags:

  • IF The Interrupt Flag.
    This is set with the STI instruction to globally enable interrupts, and cleared with the CLI instruction to globally disable interrupts.
  • DF The Direction Flag.
    Memory-to-memory operations such as CMPS and MOVS (to compare and move between memory locations) automatically increment or decrement the index registers as part of the instruction. The DF flag dictates which one happens: if cleared with the CLD instruction, they're incremented; if set with the STD instruction, they're decremented.
  • TF The Trap Flag. This is a debug flag. Setting it will put the processor into "single-step" mode: after each instruction is executed it will call the "Single Step Interrupt Handler", which is expected to be handled by a debugger. There are no instructions to set or clear this flag: you need to manipulate the bit while it is in memory.

80286 Flags

To support the new multitasking facilities in the 80286, Intel added extra flags to the FLAGS register:

  • IOPL The I/O Privilege Level.
    To protect multitasking code, some tasks needed privileges to access I/O ports, while others had to be stopped from accessing them. Intel introduced a four-level Privilege scale, with 002 being most privileged and 112 being least. If IOPL was less than the current Privilege Level, any attempt to access I/O ports, or enable or disable interrups, would cause a General Protection Fault instead.
  • NT Nested Task flag.
    This flag was set if one Task CALLed another Task, which caused a context switch. The set flag told the processor to do a context switch back when the RET was executed.

80386 Flags

The '386 needed extra flags to support extra features designed into the processor.

  • RF The Resume Flag.
    The `386 added Debug registers, which could invoke the debugger on various hardware accesses like reading, writing or executing a certain memry location. However, when the debug handler returned to execute the instruction the access would immediately re-invoke the debug handler! Or at least it would if it wasn't for the Resume Flag, which is automatically set on entry into the debug handler, and automatically cleared after every instruction. If the Resume Flag is set, the Debug handler is not invoked.
  • VM The Virtual 8086 Flag.
    To support older 16-bit code as well as newer 32-bit code, the 80386 could run 16-bit Tasks in a "Virtual 8086" mode, with the aid of a Virtual 8086 executive. The VM flag indicated that this Task was a Virtual 8086 Task.

80486 Flags

As the Intel architecture improved, it got faster through such technology as caches and super-scalar execution. That had to optimise access to the system by making assumptions. To control those assumptions, more flags were needed:

  • AC Alignment Check flag The x86 architecture could always access multi-byte memory values on any byte boundary, unlike some architectures which required them to be size-aligned (4-byte values needed to be on 4-byte boundaries). However, it was less efficient to do so, since multiple memory accesses were needed to access unaligned data. If the AC flag was set, then an unaligned access would raise an exception rather than execute the code. That way, code could be improved during development with AC set, but turned off for production code.

Pentium Flags

The Pentium added more support for virtualising, plus support for the CPUID instruction:

  • VIF The Virtual Interrupt Flag.
    This is a virtual copy of this Task's IF - whether or not this Task wants to disable interrupts, without actually affecting Global Interrupts.
  • VIP The Virtual Interrupt Pending Flag.
    This indicates that an interrupt was virtually blocked by VIF, so when the Task does an STI a virtual interrupt can be raised for it.
  • ID The CPUID-allowed Flag.
    Whether or not to allow this Task to execute the CPUID instruction. A Virtual monitor could disallow it, and "lie" to the requesting Task if it executes the instruction.