assemblyx86comparisoncpu-architectureeflags

Why does the Zero Flag exist?


I understand the use of the carry and overflow flags exist for error checking in arithmetic, but why is the zero flag useful to the system/programmer?


Solution

  • An extremely common task for any computer is to compare two integers (or pointers) for being greater / less / equal, and conditionally jump according to the result. This is how you implement higher-level constructs like if (x < y), if (x == y), and so on.

    On x86, as in many other architectures, this task is broken into two instructions: CMP or some similar instruction to perform the arithmetic of the comparison, and the conditional Jcc instructions to perform the jump. There has to be some kind of state in the CPU for CMP to communicate its result for Jcc to use, and that's what the FLAGS register is for.

    A test for equality is the most basic of these, so it makes sense that there should be a flag which is set according to whether the two operands of CMP were equal. Since CMP is really a subtraction operation under the hood, this is equivalent to asking whether the result of the subtraction was zero, which is easily done in hardware by OR'ing together all the bits and inverting the one-bit result. And that's the zero flag. Since it costs basically nothing, the 8086 designers decided to set the zero flag in this way after almost every arithmetic or logical instruction, not only CMP.

    Then "jump if equal" is equivalent to "jump if zero flag set". Indeed, JE and JZ are just different assembly mnemonics for the same machine-code instruction.

    The other standard arithmetic flags (carry, sign, overflow) are also used in this way. This is by far a more common use for them than any kind of error checking. (Indeed, many compiled languages do no such error checking and simply ignore overflow if it occurs.) For instance, if you want to jump if AX < BX, where the values in the registers are to be treated as unsigned, you would subtract BX from AX and jump if a borrow occurred, which on x86 sets the carry flag. Then your code simply looks like CMP AX, BX / JC label. The assembler lets you use JB instead of JC so that you can think of it as "jump if below". You can also combine it with the zero flag; if you want to jump if AX <= BX, then you write CMP AX, BX ; JBE label, where JBE will jump if either of the carry or zero flags is set.

    The sign and overflow flags are used similarly for signed compares, though the rules are a little more complicated.