Z80 compare instruction

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Post Reply
dfzx
Manic Miner
Posts: 683
Joined: Mon Nov 13, 2017 6:55 pm
Location: New Forest, UK
Contact:

Z80 compare instruction

Post by dfzx »

I'm having trouble understanding what happens to the Z80 carry flag when the CP instruction is used in various scenarios. The Z80 manual says "C is set if borrow; reset otherwise" but I'm not really sure what that means. For example:

Code: Select all

CP 177
JP NC,_somewhere
We compare A with 177 and jump if ... what? In English, please? If A is less than or equal to 177? Or greater than 177?

What about negative numbers?

Code: Select all

LD A,-10
CP -5
JP C,_somewhere
Would that make the jump? I have no idea and I can't unravel in my head what the relationship between the compared numbers and the C flag is.

Explanations gratefully received! :)
Derek Fountain, author of the ZX Spectrum C Programmer's Getting Started Guide and various open source games, hardware and other projects, including an IF1 and ZX Microdrive emulator.
User avatar
Stefan
Manic Miner
Posts: 819
Joined: Mon Nov 13, 2017 9:51 pm
Location: Belgium
Contact:

Re: Z80 compare instruction

Post by Stefan »

Compare is like a subtraction, but without saving the result.

So CP 177 will set the carry flag (C) if A is smaller than 177. If A is equal or greater than 177 the carry flag will not be set (NC).

While CP is not sign-aware, I /think/ that the cleverness of two's complement will just work.
User avatar
Stefan
Manic Miner
Posts: 819
Joined: Mon Nov 13, 2017 9:51 pm
Location: Belgium
Contact:

Re: Z80 compare instruction

Post by Stefan »

Just found https://www.exploringbinary.com/twos-co ... converter/

-10 = 11110110 = 246
-5 -> 11111011 = 251

246 - 251 -> C

So it will not work intuitively for "negative" numbers.
User avatar
MonkZy
Manic Miner
Posts: 279
Joined: Thu Feb 08, 2018 1:01 pm

Re: Z80 compare instruction

Post by MonkZy »

Z80 Heaven states :

CP N

Unsigned

If A == N, then Z flag is set.
If A != N, then Z flag is reset.
If A < N, then C flag is set.
If A >= N, then C flag is reset.

Signed

If A == N, then Z flag is set.
If A != N, then Z flag is reset.
If A < N, then S and P/V are different.
A >= N, then S and P/V are the same.

http://z80-heaven.wikidot.com/instructions-set:cp
User avatar
ketmar
Manic Miner
Posts: 719
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Z80 compare instruction

Post by ketmar »

"cp" is just a "sub" without storing the result. and carry flag is set when subtraction "wraps" over zero.

in plain words, carry flag is set when "CP" argument is greater than A value. so, "JP C" means "jump if cp argument is greater than A", and "JP NC" means "jump if cp argument is lesser or equal to A".

about negatives.
technically, there are no negative numbers per se, it is just a convention. irl, `-2` is `256+(-2)` (so-called two-complement integer).

so, let's convert our negative numbers to positive:

Code: Select all

ld  a,246  ; this is -10
cp  251    ; this is -5
now we can use the rule above… and it still means the same: "carry set if cp argument is greater than A".

hope this helps.

p.s.: note that comparing positive and negative numbers may give unexpected results, so it's better to not mix them.
Ralf
Rick Dangerous
Posts: 2292
Joined: Mon Nov 13, 2017 11:59 am
Location: Poland

Re: Z80 compare instruction

Post by Ralf »

From practice, in 99% of cases you won't need negative numbers. And if you are learning now, they can confuse you a lot so you may leave them
at the moment.

In assembler, negative numbers aren't as "real" as in maths. There are only 0s and 1s and no such separate entity as "sign".
So negative numbers are some kind of convention - instead of interpreting combinations of 8 bits as numbers 0-255 we interpret
the same combinations as numbers from -128 to 127.

The funny thing is that interpretion doesn't change the result, If you add 10110110 to 11011110 the result will be same in binary
and correct in decimal no matter how you intepret these numbers, positive or negative.

By the way, one thing you must learn quickly when dealing with assembler is register overflow. Look:
LD A,254
LD B.3
ADD A,B
What A will be after that? it can be 257 because A can contain values only from 0-255.
It will be 1. Which is 257-256. The biggest bit got lost. But not completely lost, it went into carry which is another important thing to get quickly.

And with subtraction there may be carry as well.
Hope it helps you a bit.
dfzx
Manic Miner
Posts: 683
Joined: Mon Nov 13, 2017 6:55 pm
Location: New Forest, UK
Contact:

Re: Z80 compare instruction

Post by dfzx »

Many thanks for the responses, I get it now. Or at least I get it better. :)

I've got this bit of disassembly which I'm trying to learn from:

Code: Select all

LD A,(IX+1)
SUB B
NEG
CP 6
JR NC,_something
I know that (IX+1) and B both hold screen ypos positions (in pixels) and that this code is doing a compare as part of the collision detection. The bullet graphic is 6 pixels long and travels down the screen.

I think what's happening here is the bullet ypos is the screen location of the top if it (which the draw routine uses), but the leading tip, where the collision detection needs to check, is the bottom of it. So an adjustment is made with that SUB, NEG, CP and then a check of the carry flag. It works, I've single stepped it and written down the values as the instructions run. But I can't quite see it!

How, in English, is it working?
Derek Fountain, author of the ZX Spectrum C Programmer's Getting Started Guide and various open source games, hardware and other projects, including an IF1 and ZX Microdrive emulator.
catmeows
Manic Miner
Posts: 718
Joined: Tue May 28, 2019 12:02 pm
Location: Prague

Re: Z80 compare instruction

Post by catmeows »

It is very hard to judge without seeing whole picture. But I think works like this:
The Y-axis Is counted from top to bottom.
Load bullet ypos into A .
B already holds avatar ypos.
Since avatar is always at the bottom of screen, B >= A.
The result of A-B is always negative or zero.
Therefore NEG instruction is used. -1*(A-B)=(B-A)
The CP 6, JR NC,XX is the final test:
If (B-A)<6 then jump
Or you could say if (Yavatar-Ybullet)<HeightOfBullet then jump
Does it make any sense?
Proud owner of Didaktik M
User avatar
ketmar
Manic Miner
Posts: 719
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Z80 compare instruction

Post by ketmar »

such code usually used to check things like: `(A-B) >= -6`. with a simple transformation we get: `-(A-B) >= 6`, and that's what the code is doing. we need such transformation due to how negative numbers are stored: our `-6` is actually `250`, so we can't use "CP -6", it won't work.

the trick is this: NEG will convert negative numbers to positive, and positive numbers to even bigger positives. always remember that "negative integer" is just a convention. what NEG does is simply 8-bit `256-A`. so, for example:

if A is `-3`, then NEG will convert it to `3`: 256-(-3) = 259 (0x103), and we're only using the low 8 bits of the result.
if A is `3`, then NEG will convert it to `-3`: 256-3 = 253 (0xfd). (see about 2-complement integer representation above).

now, after "NEG", the code treats `A` as 8-bit unsigned number, and "CP 6" Just Works.


simple "CP -6" will not work here, because it is actually "CP 250". remember what i wrote about comparisons of mixed positive and negative numbers above? it won't work as expected. so the code had to jump over some hoops to make it work.

so, the trick here is: "NEG will convert negatives to small positives, and positives to even bigger (always >= 128) positives (if we will interpret NEG result as unsigned integer)".
User avatar
1024MAK
Bugaboo
Posts: 3125
Joined: Wed Nov 15, 2017 2:52 pm
Location: Sunny Somerset in the U.K. in Europe

Re: Z80 compare instruction

Post by 1024MAK »

Another crude way of getting how the carry flag works, is to think of the hours of a 12 hour digital clock.
The most significant digit is equivalent to a carry flag.
When the clock is showing the hour as 9, if you add one hour, the hour overflows and we add one to the ‘10s’ digit. Except that in a 12 hour clock, the ‘10s’ digit can only show 0 or 1. So the clock will now show 10. That’s equivalent to the carry bit in binary where the 8 bit register has overflowed:

Code: Select all

C 76543210
0 11111111 = 255
Add 1 and you get
1 00000000 = 0 but with carry set, so the overall value is equivalent to 256
If a digital clock is showing 12 and you deduct 3 hours because you travelled across a bunch of time zones in an aircraft, you get 9. The most significant digit now being reset, equivalent to a carry flag being reset (here reset means zero or clear).

An example in binary…

Code: Select all

C 76543210
0 00000001 = 1
Subtract 4 and you get
1 11111101 = 253 but with carry set, so the overall value is equivalent to 509 or -3 depending on how you interpret the binary number (where bit 7 is treated as a sign bit)
Mark
:!: Standby alert :!:
“There are four lights!”
Step up to red alert. Sir, are you absolutely sure? It does mean changing the bulb :dance
Looking forward to summer later in the year.
Post Reply