10

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:

  100
+(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.

3

3 Answers 3

18

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
No_XOR:
     JP  M,Less      -> V was less than what A was
     JP  PE,Greater  -> V was greater than what A was

Nifty, isn't it :)) Now 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 legaly) available as PDF on Gaby's Z80-Info site)

13
  • 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, 2019 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, 2019 at 22:22
  • 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, 2019 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, 2019 at 5:31
  • 1
    @JetBlue The polarity of the C flag after compare (or subtract) depends on processor architecture. On Intel 8086 (and later), C is clear if there is no unsigned overflow. SBC does behave like SUB if C is clear. On the 6502, C is clear if ther is unsigned overflow. SBC does not apply borrow if the C is set. Apr 15, 2020 at 19:32
5

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.

1

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.

2
  • 4
    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, 2019 at 4:15
  • UncleBod is correct, and looking just at subtraction can even be misleading. For example, the 6502 CMP instruction is not just an SBC with the result of the subtraction discarded. It differs in two ways, both related to flags: 1) SBC uses the current value of the carry flag and CMP does not; 2) SBC affects the V (overflow) flag and CMP does not. So the standard SBC technique for detecting overflow in signed numbers does not work with CMP.
    – cjs
    Apr 15, 2020 at 3:59

You must log in to answer this question.

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