Somewhere behind every jz, jnz, bcc, or beq instruction you’ve ever written, there’s a small, unglamorous register quietly doing the real work: the Program Status Word (PSW). It doesn’t hold your data, and it doesn’t hold an address. It holds the state of the CPU itself — the flags, the mode, the interrupt-enable bits. I used to skim right past it in reference manuals, until I hit a bug that only made sense once I understood exactly what the PSW was tracking. This post is the explanation I wish I’d read first.
What Exactly Is a Program Status Word?
The Program Status Word (sometimes called the flags register, status register, or on ARM, the Current Program Status Register — CPSR/xPSR) is a special-purpose register that packs together multiple single-bit and multi-bit fields describing the current state of the processor. It’s not one “thing” so much as a bitfield of many small things:
- Condition flags — results of the last arithmetic/logic operation (zero, carry, sign, overflow, parity).
- Control flags — interrupt-enable bits, direction flags, trap flags.
- Mode/privilege bits — what privilege level or execution mode the CPU is currently in.
Different architectures name and organize this differently, but the underlying purpose is identical everywhere: give the CPU (and the programmer) a compact snapshot of “what just happened” and “what mode am I in” at any instant.
The x86/x86-64 View: EFLAGS/RFLAGS
On x86, the Program Status Word is the FLAGS register (16-bit), extended to EFLAGS (32-bit) and RFLAGS (64-bit). Key bits include:
| Flag | Bit | Meaning |
|---|---|---|
| CF (Carry) | 0 | Set on unsigned overflow/borrow |
| PF (Parity) | 2 | Set if low byte of result has even parity |
| ZF (Zero) | 6 | Set if result is zero |
| SF (Sign) | 7 | Set if result is negative (MSB = 1) |
| TF (Trap) | 8 | Enables single-step debugging |
| IF (Interrupt Enable) | 9 | Enables maskable hardware interrupts |
| DF (Direction) | 10 | Controls direction of string instructions |
| OF (Overflow) | 11 | Set on signed overflow |
Example: Flags in Action
mov eax, 5
sub eax, 5 ; result is 0
jz is_zero ; jumps because ZF was set by SUB
mov eax, 0x7FFFFFFF
add eax, 1 ; signed overflow — OF gets set
jo overflow_detected
Every conditional jump (JZ, JNZ, JC, JO, JL, JG, and dozens more) is really just a test of one or more bits in EFLAGS/RFLAGS. That’s the entire mechanism behind conditional control flow in x86 assembly.
Reading and Writing EFLAGS Directly
pushfq ; push RFLAGS onto the stack
pop rax ; RAX now holds the flags
or rax, 0x40 ; manually set ZF bit (illustrative)
push rax
popfq ; RFLAGS updated from stack
The ARM View: CPSR / APSR / xPSR
ARM splits this concept slightly differently depending on the profile:
- ARM classic/Cortex-A/R: the Current Program Status Register (CPSR) holds condition flags (N, Z, C, V), interrupt masks (I, F), the Thumb state bit (T), and the current processor mode (User, FIQ, IRQ, Supervisor, etc.) — all in one register.
- ARM Cortex-M: splits this into the APSR (Application PSR — just the condition flags), IPSR (Interrupt PSR — current exception number), and EPSR (Execution PSR — Thumb state, IT-block state), collectively addressable as xPSR.
| Flag | Meaning |
|---|---|
| N | Negative result |
| Z | Zero result |
| C | Carry/borrow occurred |
| V | Signed overflow occurred |
| Q | Saturation occurred (DSP-oriented) |
| I / F | IRQ / FIQ mask bits |
| T | Thumb execution state |
| Mode bits | Current privilege/execution mode |
ARM Assembly Example
CMP R0, R1 ; compare, updates N, Z, C, V in APSR
BEQ equal_case ; branch if Z is set
MRS R2, APSR ; read APSR into R2
MSR APSR_nzcvq, R2 ; write flag bits back from R2
ARM’s conditional execution goes further than most architectures — many ARM instructions themselves can be conditionally executed based on PSW flags (e.g., ADDEQ, MOVNE), not just branches, which is a distinctive feature worth knowing if you’re comparing architectures.
Internal Working Process
sequenceDiagram
participant ALU as ALU/Execute Stage
participant PSW as Program Status Word
participant CU as Control Unit
ALU->>PSW: Write result flags (Z, C, N, V...)
CU->>PSW: Read flags for conditional branch/instr
PSW-->>CU: Flag values
CU->>CU: Decide branch taken / instruction executes
Note over PSW: PSW also holds mode & interrupt-enable bits,<br/>checked continuously by hardware
Why the PSW Matters Beyond Just Branching
- Context switching — when an OS switches between processes or threads, it must save and restore the PSW along with the general-purpose registers and program counter. Get this wrong, and a resumed process could behave as though a previous comparison or interrupt state still applied.
- Exception/interrupt handling — entering an ISR typically pushes the current PSW onto the stack automatically (x86’s
INT/hardware interrupt entry does this; ARM pushes xPSR as part of exception entry), so the interrupted context can be perfectly restored afterward. - Privilege enforcement — the mode bits inside the PSW (ARM’s mode field, x86’s
CPLderived from segment selectors plus system flags) determine what the currently executing code is allowed to do, which is foundational to OS/user separation. - String and loop instruction behavior — on x86, the Direction Flag (DF) in EFLAGS controls whether
MOVS,CMPS,SCAS, etc., process memory forward or backward.
Comparison Table: PSW Across Architectures
| Aspect | x86/x86-64 (EFLAGS/RFLAGS) | ARM (CPSR/xPSR) |
|---|---|---|
| Register name | EFLAGS / RFLAGS | CPSR (A/R profile), xPSR (M profile) |
| Condition flags | CF, ZF, SF, OF, PF, AF | N, Z, C, V (+ Q) |
| Interrupt control | IF flag | I, F mask bits |
| Mode/privilege info | Derived from segment/CPL, separate from EFLAGS | Encoded directly in CPSR mode field |
| Conditional execution scope | Branches only | Branches + many data-processing instructions |
| Direct read/write | PUSHF/POPF, LAHF/SAHF | MRS/MSR |
Advantage of x86’s approach: a single, well-known flat flags register that’s simple to save/restore. Advantage of ARM’s approach: built-in conditional execution of ordinary instructions reduces branch count, which — as covered in pipeline discussions — reduces misprediction risk.
Practical Use Cases
- Implementing multi-word arithmetic (
ADC/SBBon x86,ADCS/SBCSon ARM) relies entirely on the carry flag propagating correctly between instructions. - Writing bootloaders and OS kernels requires manual PSW manipulation to switch privilege levels or enable/disable interrupts during critical setup phases.
- Debuggers rely on the Trap Flag (x86) or debug-related exception mechanisms to implement single-step execution.
Debugging and Optimization Considerations
- Common mistake: assuming flags persist across instructions that don’t actually preserve them. Many instructions (like
MOV) don’t touch the flags at all, but plenty of others silently clobber them — always check the ISA reference for “flags affected.” - Debugging tip: most debuggers display the flags register directly; when a conditional jump doesn’t do what you expect, check the actual flag bits rather than assuming the comparison “should” have worked.
- Optimization tip: ARM’s conditional instruction execution can eliminate short branches entirely, which is faster on pipelines where misprediction is costly — but overusing it on cores where predicated instructions still cost a cycle each way can be a wash. Profile before assuming it’s a win.
Best Practices
- Always check an instruction’s documented “flags affected” list before relying on it for a subsequent conditional operation.
- When writing interrupt handlers or context-switch code, never assume the PSW is preserved automatically unless the architecture explicitly documents that it pushes/restores it.
- Use
PUSHF/POPF(x86) orMRS/MSR(ARM) sparingly and deliberately — manual flag manipulation is powerful but easy to misuse. - When comparing signed vs. unsigned values, make sure you’re branching on the correct flag combination (
JG/JLvsJA/JBon x86 test entirely different flag sets).
FAQs
Is the Program Status Word the same as the “flags register”? Functionally, yes, in most literature — “PSW” is the more general/textbook term, while specific architectures use their own names (EFLAGS, CPSR, xPSR).
Can user-mode code freely modify the entire PSW? No — mode/privilege bits and interrupt-enable bits are typically protected and can only be modified by privileged (kernel-mode) code, even though condition flags are freely modifiable by any code.
Why do signed and unsigned comparisons use different flags? Because “greater than” means something different depending on interpretation — unsigned comparisons rely on the Carry flag, signed comparisons rely on the relationship between the Sign and Overflow flags.
What happens to the PSW during a context switch? The OS scheduler saves the outgoing thread’s PSW (usually as part of its saved register/context block) and restores the incoming thread’s PSW, so each thread sees its own consistent flag/mode state.
Summary and Key Takeaways
The Program Status Word is the CPU’s compact record of “what just happened” and “what mode am I in” — condition flags from the last operation, control bits like interrupt-enable and direction, and privilege/mode information. Every conditional branch, every context switch, and every interrupt entry/exit depends on it being tracked and preserved correctly.
Key takeaways:
- On x86/x86-64 it’s EFLAGS/RFLAGS; on ARM it’s CPSR (or the split APSR/IPSR/EPSR on Cortex-M).
- Condition flags (zero, carry, sign, overflow) drive conditional branching and arithmetic chaining.
- Control and mode bits govern interrupt masking and privilege enforcement.
- Proper save/restore of the PSW is essential for correct interrupts, exceptions, and context switching.
References
- Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1 — Basic Architecture, EFLAGS chapter.
- AMD64 Architecture Programmer’s Manual, Volume 1 — RFLAGS register description.
- ARM Architecture Reference Manual — Program Status Registers (CPSR, APSR, xPSR) chapters.
- GNU Assembler (GAS) documentation for
pushf/popf/lahf/sahfand ARMmrs/msrsyntax.
