# Behavior of the zero and negative/sign flags on classic instruction sets

It seems to me that there's effectively two ways that the zero bit could work.

1. `Z` is set iff the result of a computation is mathematically equal to 0.
2. `Z` is set iff a bit pattern consisting entirely of 0 is stored in the destination (whatever that may be)

These may seem identical, but consider the following fragment of 6502 assembly:

``````LDA #\$FF
CLC
``````

So what's `Z` for this? Mathematically, `0xFF` + `0x01` = `0x100`, but 256 doesn't fit in an 8-bit register, so the `C` flag gets set, and the bit pattern `0b00000000` is stored in `A`. Often time, manuals will say something like "The Z flag is set if the result is zero" but that seems hopelessly ambiguous to mean. I'm very curious, cross-architecturally, how this has varied.

Similarly, it's unclear how the `N` (negative/sign) flag "should behave. Is it set according to the "actual" sign of the result, or does it literally just reflect the MSB of the result actually stored in the destination, irrespective of whether signed overflow occurred?

• Mathematically, computers use modular arithmetic so the answer is both. – user722 Jul 14 '19 at 13:12
• I get the feeling that the question is not really ask about the 6502 in particular. – Raffzahn Jul 14 '19 at 14:12

Neither. In the 6502, the Z flag is set if there's an all-zero pattern on the internal data bus in the last instruction cycle and cleared if it isn't. This means specifically instructions as PLA or TAX affect the Z flag. On the 65C02, e.g. PLX does, too.

You can see this from the CMP, CPX, CPY, BIT instructions, which store nothing.

Same for the N flag, it's simply copied from bit 7 of the internal data bus.

• Instructions that modify the Z flag behave in that fashion, but not all instructions modify the Z flag. – supercat Jul 15 '19 at 16:39
• All instructions but that ones storing data into memory, flag manipulation, jumps and TXS behave that way. See oxyron.de/html/opcodes02.html. – Janka Jul 15 '19 at 18:00
• The 6502's BIT instruction can set N, V, and Z to any combination of values if the accumulator holds a value between 1 and 63, so I don't think Z and N use the same data bus at the same time. – supercat Jun 2 '20 at 22:06

While Wilson and Janka already explain the arithmetic and 6502 related (*1) implication, I somehow get the feeling this question is not about the 6502

Behavior of the zero and negative/sign flags on classic instruction sets

but rather some generic, absolute meaning.

I'm very curious, cross-architecturally, how this has varied.

There is no variation, as there is no absolute meaning (*2) what Z should be, or that Z even stands for Zero.

There is no one size fits all. One has to look at every ISA on its own, as similar naming will may mean different things based on different assumptions for each architecture.

[Insert...

If at all, one may think of it as with languages. There can be words with similar meanings in different languages, but they rarely overlap exactly, even for simple things. Even if these words sound (almost) the same and their meaning seems quite similar, they are not - like for Z and flags.

Think of English 'Hound' and German 'Hund'. Both are about a dog, but while the German Hund means dog in general, English Hound is a subset of dog. Much like the Z80 Z flag is a subset of the 6502 Z flag, isn't it? (*2)

...back to CPUs and conditions]

There are CPUs

• without flags at all - like some VLIW
• without flags but relational tests - like DEC ALPHA
• without flags but conditional traps
• with condition codes instead of flags - like IBM /360ish (it can't get more classic :))
• with flags much like 6502 or Z80 but many different compositions

(I guess with some thinking the list may be extended many more ways.)

As well as combinations thereof, where MIPS is a great example (*3) again:

Bottom line: CPU architectures with flags are just one case of many, rather a minority among all designs - that is, unless we count the number of delivered devices (even considering MIPS working hard in many cheap consumer products won't change this).

The reason is rather simple, as using flags is the least effort hardware wise. Basically flags are just cached output signals of the ALU (like on Z80) sometimes enriched by additional test logic(*4). They only make very basic observations available to software. Fine as long as there are only a few and rather basic 'notifications' to be made about the result of an operation. But as soon as we leave the very basic domain and the number of resulting conditions may become quite crowded, hard to be handled in a systematic way - even less in similar ways across architectures.

While a 'simple' Zero or the mentioned Negative introduces only minor differences between architectures, it gets quite messy when the meaning to be transported gets more and more complex. Just take a look at the handling of signed numbers and overflow in 8080 vs. 6502. Here several new flags with only minor overlap are introduced. And this is still only integer arithmetic.

Architectures offering a more diverse set of operations (*5) usually drop the whole flag part and go either for generation of a more abstract operation result, like a condition code, to be used on a case-by-case basis(*6), or move the whole result/condition testing away from the operation completely (like MIPS, Alpha)

• simplify software structure and
• improve performance

*1 - Well, both contain part of the workings within a 6502. The signal for Zero (DBZ) is generated by an 8-input NAND, but that Z not always reflect what's (in the last cycle) on the internal data bus (DB part). After all, this would mean that (for example) in case of a `JMP` instruction Z would be set according to the high byte of the address.

To avoid this the control logic enable flag setting for Z in a case by case situation via distinct control signals. Within the Hanson diagram they are mentioned as `DBI/Z` in case of input operations (like `LDA #`) and `DBZ/Z` for arithmetic. In hardware it's an OR combination driven by several lines from the decoder PLA detecting certain opcode/sequence cases.

*3 - A rare case where a Wiki entry has at least some information to serve as pointer for details.

*4 - Like parity on a 8080 or zero detection for data by the 6800/6500 series.

*5 - By initial design that is, as 'grown' architectures like x86 have to squeeze them in.

*6 - Basically the same number of markers (avoiding the term Flag on purpose) gets used differently on different instructions. For example on a /370

• an arithmetic operation produced CC=0 for result zero, CC=1 for negative and CC=2 for positive and CC=3 for overflow
• A compare operation produces CC=0 for equal, CC=1 for less than and CC=2 for greater than
• a logic operation produces CC=0 for zero and CC=1 for non zero
• a bit test operation produces CC=0 for all-zero, CC=1 for mixed and CC=3 for all-one
• and so on...

As a result only two bits of condition code are needed to store such an abstract result - and only a single set of test instructions are necessary to act, no matter how different the instruction results are.

• Great answer that addresses the question behind the question. – Wayne Conrad Jul 15 '19 at 16:53
• What did you mean by "without flags but conditional traps"? Maybe something like the PDP-4/7/8/9, which has no addressable Zero flag but tests the accumulator when it executes a `sza` instruction? – OmarL Aug 3 '20 at 9:43
• This is a very wide area, my grouping is rather rough. I would put `SZA` to the line above, relational tests. For conditional traps I was thinking about the MIPS II `T*`(like TGE Trap if Greater or Equal) as well as the `*U` (ADDU`` etc.) instructions of MIPS I, where overflow issues a trap. – Raffzahn Aug 3 '20 at 12:35

In the case you describe, the 6502 will set the Zero flag (in other words, the Z flag will be one if the operation left the accumulator equal to 0 mod 256). That's convenient, because usually a programmer is interested either in `Z`, to test for an actual zero condition, or in `C`, to test for a carry.

The Z80 works in the same way.

My understanding is that it's also easier to implement, since that's effectively an 8-input NAND.

• For what it's worth, the 68000 acts the same way, even in intentionally open-ended instructions like `ADDX` — they either clear the zero flag or leave it alone so that the end result is correct for the net total of all handled [bytes/words/long words], but they do it only based on the [8/16/32]-bit result, with no regard for the generated carry/extend. – Tommy Jul 14 '19 at 13:30