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
.
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 Code | Name | Definition |
---|---|---|
E , Z | Equal, Zero | ZF == 1 |
NE , NZ | Not Equal, Not Zero | ZF == 0 |
O | Overflow | OF == 1 |
NO | No Overflow | OF == 0 |
S | Signed | SF == 1 |
NS | Not Signed | SF == 0 |
P | Parity | PF == 1 |
NP | No Parity | PF == 0 |
-------------- | ---- | ---------- |
C , B , NAE | Carry, Below, Not Above or Equal | CF == 1 |
NC , NB , AE | No Carry, Not Below, Above or Equal | CF == 0 |
A , NBE | Above, Not Below or Equal | CF ==0 and ZF ==0 |
NA , BE | Not Above, Below or Equal | CF ==1 or ZF ==1 |
--------------- | ---- | ---------- |
GE , NL | Greater or Equal, Not Less | SF ==OF |
NGE , L | Not Greater or Equal, Less | SF !=OF |
G , NLE | Greater, Not Less or Equal | ZF ==0 and SF ==OF |
NG , LE | Not Greater, Less or Equal | ZF ==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).
FLAGS
directlyThe 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 FlagsSAHF
Store AH
register into FlagsOnly 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 stackPUSHFD
/ POPFD
Push/pop 32-bit EFLAGS
onto/from the stackPUSHFQ
/ POPFQ
Push/pop 64-bit RFLAGS
onto/from the stackNote that interrupts save and restore the current [R/E]FLAGS
register automatically.
As well as the ALU flags described above, the FLAGS
register defines other system-state flags:
IF
The Interrupt Flag.STI
instruction to globally enable interrupts, and cleared with the CLI
instruction to globally disable interrupts.DF
The Direction Flag.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.To support the new multitasking facilities in the 80286, Intel added extra flags to the FLAGS
register:
IOPL
The I/O Privilege Level.00
2 being most privileged and 11
2 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.CALL
ed another Task, which caused a context switch. The set flag told the processor to do a context switch back when the RET
was executed.The '386 needed extra flags to support extra features designed into the processor.
RF
The Resume Flag.VM
The Virtual 8086 Flag.VM
flag indicated that this Task was a Virtual 8086 Task.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.The Pentium added more support for virtualising, plus support for the CPUID
instruction:
VIF
The Virtual Interrupt Flag.IF
- whether or not this Task wants to disable interrupts, without actually affecting Global Interrupts.VIP
The Virtual Interrupt Pending Flag.VIF
, so when the Task does an STI
a virtual interrupt can be raised for it.ID
The CPUID
-allowed Flag.CPUID
instruction. A Virtual monitor could disallow it, and "lie" to the requesting Task if it executes the instruction.