How do you compare two signed numbers in Z80 assembly? According to this site,

To compare stuff, simply do a CP, and if the zero flag is set [then] the argument[s] were equal, else if the carry is set the argument was greater...

To simplify the explanation, suppose we are working with a three bit signed representation:

binary  unsigned  signed
000     0          0
001     1          1
010     2          2
011     3          3
100     4         -4
101     5         -3
110     6         -2
111     7         -1

Suppose I want to compare -4 and 3. According to the quote above, a less than condition is indicated with the zero flag being clear and the carry flag being clear

The CP instruction subtracts -4 from 3 as follows:

+(101)  // two's complement of 3
 1001   // carry bit is set

As you can see the carry flag is set indicating that -4 is greater than 3. Which is not what I expect =( This makes me suspect that the logic in the quote only works for unsigned comparisons.

  • Am I correct in assuming that CP does signed subtraction? – Jet Blue Feb 11 at 22:46
  • 2
    Subtraction is subtraction in twos complement. Whether you regard the result as signed or unsigned is up to you. – another-dave Feb 12 at 3:05

How do you compare two signed numbers in z80 assembly?

Signed arithmetics - this includes compare - uses the Overflow flag (P/V) to signal any over/underflow. To decode it has to be seen in relation with the sign flag. This is a two step process:

Step 1: Test for equal.

  • If Z flag is set then V (value compared) is equal to A
  • If Z flag is reset then Vr is not equal to A

Step Two: Test for less or greater

  • If S and P/V are the same then V is greater (*) an A
  • If S and P/V are different then V is less than A

S and P/V are as well the same if the number is equal to A, so without the first test comparing S an P/V can be used to catch a number greater or equal to A.

On a Z80 (or any 8080 follow up) 1 to 3 flags need to be checked, depending on the question asked the resulting jumps may be rather complex. Now, drawing this up as a table gives additional insight

Z   S  P/V
0   0   0   V>A
0   0   1   V<A
0   1   0   V<A
0   1   1   V>A
1   0   0   V=A
1   0   1   V=A (doesn't happen anyway)
1   1   0   V=A ( "       "      "    )
1   1   1   V=A

Looking at the first 4 lines looks quite like an XOR operation, doesn't it?

A   B   Q
0   0   0
0   1   1
1   0   1
1   1   0

If we just had a way to XOR flag bits ... to bad that moving them to A not only destroys A, but also requires a lot of code. But help is near, there is an a variation of CP that copies the sign into an accumulator bit: SUB :))

Lets try this:

     SUB -4
     JP  Z,Equal     -> V was equal to what A was
     JP  PO,No_XOR   -> P/V not set, so XOR wouldn't do anything useful
     XOR 80h         -> S := S XOR P/V - as tabled above
     JP  M,Less      -> V was less than what A was
     JP  PE,Greater  -> V was greater than what A was

Nifty, isn't it :)) No just remove the unneeded jumps. Ofc, if you need to preserve A, then the usual jumps are back on track.

In general carry is not meant for comparison, it's sole purpose is to handle carry (borrow) on multi byte/word operations. Now, in the special case of unsigned numbers, testing it (at the end), instead of operating on S vs. P/V, does simplify code. Bottom line: above citation is valid, but only (and as already assumed) in case of unsigned numbers.

(Hint: Instead of wasting time on more or less special web pages, get a copy of Rodney Zacks's Programming the Z80, eventually the standard book for this purpose - and conveniently (and legal) available as PDF on Gaby's Z80-Info site)

  • Thanks! I later came across this site among many that says for unsigned comparisons "If A < N, then C flag is set", which is the opposite of what I quoted above. Searching the web about 50% of results say for a less than comparison the C flag is set, and the other 50% that it's cleared. In the book you share, a line says JR NC, NOSWITCH #jump if less than max. This use implies that the C flag is cleared. (I couldn't find anything more explicit from a quick search of the book). Which is the correct one? – Jet Blue Feb 11 at 22:14
  • Also, how does this bit in your code work JP PO,Less_Than -> V was less than what A was? What about this case where the PO is set but it's greater than 0 1 1 V>A. Thanks for your insight! – Jet Blue Feb 11 at 22:22
  • @JetBlue Sorry, screwed that up then typing from faint memory. Code is now corrected - it helps to shake off dust :) – Raffzahn Feb 11 at 22:45
  • 1
    @jetblue in the example from the book, A is the max value, so C-flag is set when N is greater than max. – UncleBod Feb 12 at 5:18
  • 1
    Does this work on the 8080, since the OP mentions it in the title? As noted on this page: en.wikipedia.org/wiki/Intel_8080#Flags the 8080 has no overflow flag, and it was only in the Z80 that parity got "overloaded" to be overflow for arithmetic operations. Which incidentally explains why XOR A, SUB 3 JPE ... allows you to tell a Z80 from an 808[05] at runtime. e.g. for LDIR optimization. – dgnuff Feb 12 at 5:31

Comparisons in microprocessors is essentially done by subtracting one value from the other, and looking at the flags generated by that process.

The difference being, of course, is that comparison is not destructive whereas subtraction is.

So, simply if you know how to subtract two signed numbers, you know how to compare them. This could give you better insight in to the problem.

  • The key is not how to subtract. The key is to know what flags are affected by the subtraction in what ways. – UncleBod Feb 12 at 4:15

Lots of problems using sign and overflow flags on Z80 for comparing signed numbers could be overcome in the following way:

Just invert sign bits of both numbers prior to comparison, which will turn them from [-128..+127] range to [0..255] range. Then compare them as unsigned, using just carry and zero flags. Et voilà, you're done!

If your numbers are not big, i.e. the subtraction won't overflow signed range, all you need to do to just account for sign bit.

Your Answer

