roadmap
week 2 · day 8

Arithmetic & the FLAGS Register

add, sub, and the 4 bits that decide every branch

Easy 32 min 180 xp
After this you can
  • Do arithmetic in assembly: add, sub, inc, dec, neg, imul
  • Explain the four condition flags ZF, SF, CF, OF and what each records
  • Derive which flags an operation sets, by hand, from the result and the two's-complement rules
  • Say why the same add serves both signed and unsigned math, and where each overflows

Every operation leaves a receipt

Arithmetic in assembly is what you'd guess: add rax, rbx does rax = rax + rbx; sub, inc, dec, neg, imul do the obvious. That part is easy. The part that *matters* — the part that makes decisions possible — is that every arithmetic instruction quietly writes a receipt into a special register called RFLAGS. Four bits of that receipt drive every branch you'll ever write.

The CPU has no "if." An if is: do some arithmetic, read the receipt, jump based on it. So today is really the foundation of *all* control flow.

The four flags that matter

RFLAGS has many bits; four decide branches. Each records one fact about the *result* of the last operation:

FlagNameSet when…Reads the question
ZFZerothe result is exactly 0were they equal? (after cmp)
SFSignthe result's top bit is 1is the (signed) result negative?
CFCarryan unsigned add carried out / sub borroweddid UNSIGNED math overflow?
OFOverflowthe signed result won't fitdid SIGNED math overflow?

Notice the split at the bottom: CF is the unsigned overflow flag; OF is the signed overflow flag. The CPU can't know whether *you* meant a byte to be 0–255 or −128–127 — the bits are the same (day 1). So it computes *both* verdicts every time and hands you both flags; your choice of which branch instruction to use later decides which one you're asking about.

Watch the flags fall out of one subtraction

Take an 8-bit sub al, bl with al = 0x05, bl = 0x05:

sub 0x05 - 0x05
   0000 0101   (5)
 - 0000 0101   (5)
 -----------
   0000 0000   (0)   → ZF=1 (zero!)  SF=0 (top bit 0)  CF=0 (no borrow)  OF=0

Result 0 → ZF=1. That single flag is how if (a == b) works: subtract, and if ZF is set they were equal. Now the interesting cases — overflow. Recall day 2: an 8-bit *signed* value maxes at 0x7F = +127.

add 0x7F + 1, read two ways
   0111 1111   (unsigned 127 / signed +127)
 + 0000 0001   (1)
 -----------
   1000 0000   → SF=1 (top bit set)  ZF=0  CF=0 (no unsigned carry: 127+1=128 fits in 8 bits unsigned)
                OF=1  (SIGNED overflow: +127 + 1 should be +128, but that can't fit; bits say -128)
Key idea
Same add, two truths: as unsigned, 127 + 1 = 128, which fits in a byte — CF=0, no overflow. As signed, +127 + 1 = +128, which does *not* fit (max is +127) — OF=1, and the bits now read as −128. This is exactly day 2's wrap, now with the flags that detect it. CF watches the unsigned edge; OF watches the signed edge; they fire independently.

A rule you can apply, not memorize

  • ZF = "is the result all zeros?" — set it by looking at the result.
  • SF = "is the top bit of the result 1?" — copy the sign bit.
  • CF (for add) = "did a 1 carry out past the top bit?" i.e. did the true unsigned sum need one
  • more bit than fits. For sub, "did you have to borrow?"
  • OF = "do the two inputs have the same sign but the result has the opposite sign?" That's the
  • signature of signed overflow — two positives making a negative, or two negatives making a positive.

You never memorize a table; you read the result and apply these four questions.

Predict first
8-bit add al, bl with al = 0x80 (signed −128) and bl = 0x80 (signed −128). Predict the result byte and all four flags (ZF, SF, CF, OF).
arithmetic that sets flags (Intel/NASM)asm
1 mov al, 0x7f
2 add al, 1 ; al = 0x80 ; OF=1 (signed overflow), SF=1
3 mov bl, 0xff
4 add bl, 1 ; bl = 0x00 ; ZF=1, CF=1 (unsigned wrap)
5 sub bl, 1 ; bl = 0xff ; CF=1 (borrow), SF=1
Note
One instruction sets flags *without* changing a register: `cmp a, b` computes a - b, sets the flags, and throws the result away. And `test a, b` does the same for a bitwise AND. These are how you ask a question without disturbing your data — the star of tomorrow's lesson.
finished reading?
Your task, you write the code

Make the flags visible

In flags.asm, perform three 8-bit operations you predict the flags for: (a) add that causes an unsigned wrap, (b) add that causes a signed overflow, (c) a sub that results in zero. For EACH, before running, write on paper your predicted ZF/SF/CF/OF using the four questions from this lesson. Then step through in gdb and read `info registers eflags` after each, confirming your predictions. You only need add/sub/mov and the flag rules taught here.

deliverable: flags.asm (+ your written predictions)
build & run
$ nasm -f elf64 flags.asm -o flags.o && ld flags.o -o flags
$ gdb ./flags # break _start, stepi, then: info registers eflags
self-review before running
  • You predicted all four flags for each op BEFORE running
  • Your unsigned-wrap case shows CF=1 and your signed-overflow case shows OF=1
  • Your zero result shows ZF=1
  • You can state the difference between CF and OF in one sentence
stretchFind an operation where CF is set but OF is not, and another where OF is set but CF is not. Explaining why each is possible proves you understand that the two flags answer different questions.

Self-check

01Which flag tells you two values were equal after a cmp?
028-bit unsigned 255 + 1. Which flag fires, and what's the result?
03Why does the CPU compute both CF and OF for every add?
04OF (signed overflow) is set when…
0/4 correct · 0/4 checked