11

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
ADC #$01

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?

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

3 Answers 3

13

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.

3
  • Instructions that modify the Z flag behave in that fashion, but not all instructions modify the Z flag.
    – supercat
    Jul 15, 2019 at 16:39
  • 1
    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, 2019 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, 2020 at 22:06
8

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.

So short answer:

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]


Long Answer:

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)

Doing away with flags can

  • 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.

*2 - But beware Gift vs Gift :))

*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
  • a move long produces CC=0 for all transferred, CC=1 for receive-is-too-short and CC=2 for receive-field-was-padded
  • 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.

3
  • 1
    Great answer that addresses the question behind the question. Jul 15, 2019 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?
    – Lorraine
    Aug 3, 2020 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, 2020 at 12:35
6

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.

9
  • 1
    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, 2019 at 13:30
  • That's not true with the Z80. It's not that simple. It is true that many ALU operations on the A register will affect the Z flag. But some don't. For example, POP AF and any LD A,X operation. There are also instruction not related to the accumulator that will set the zero flag too. For example, ADD HL,DE.
    – Cthutu
    Dec 21, 2021 at 16:51
  • @Cthutu, if A = 255 and you add 1 so that it rolls over to 0. Will the Z flag be set? That's what the OP is asking. So it's option 2 from the question. Do you dispute it?
    – Lorraine
    Dec 21, 2021 at 16:56
  • The same with ADD HL,DE, this operation will set the zero flag if the result has rolled over to zero, I'm pretty sure without looking it up.
    – Lorraine
    Dec 21, 2021 at 16:57
  • @OmarL, please read my comment more closely. I mentioned ADD HL,DE will affect the Z flag as an example that it isn't to do with the accumulator. Also adding 1 to A with INC or ADD opcodes will set the Z flag. But, LD A,0 will not, for example. So your assertion that Z flag will be set whenever the accumulator is 0 is wrong.
    – Cthutu
    Dec 23, 2021 at 7:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .