4

Jump's based on comparing signed integers use the Zero, Sign, and Overflow flag to determine the relationship between operands. After CMP with two signed operands, there are three possible scenario's:

  1. ZF = 1 - Destination = Source
  2. SF = OF - Destination > Source
  3. SF != OF - Destination < Source

I'm having trouble understanding scenario 2 and 3. I've worked through the possible combinations and see that they do work - but I still can't figure out why they work.

Can anyone explain why a comparison of the Sign and Overflow flags reflects signed integer relationships?

Edit:

There seems to be some understanding regarding what I'm asking. Jumps based on signed comparisons utilize the Zero, Sign, and Carry flag - these include JG, JL, and so on.

For example:

mov   al, 1
cmp   al, -1    
jg    isGreater

isGreater: 

The jump is taken because Overflow flag = Sign Flag (both are 0), indicating in terms of signed comparison, the destination operand is larger than the source.

If the Overflow flag was set to 1 and the Sign flag set to 0, that would indicate the destination is smaller.

My questions is - I just can't seem to wrap my head around WHY this actually works.

Govind Parmar
  • 20,656
  • 7
  • 53
  • 85
cafekaze
  • 377
  • 2
  • 6
  • 18
  • 4
    Think of a compare as a subtract where the result is discarded - the flags and their interpretation make more sense in that light (to me at least). – Paul R Feb 01 '17 at 06:16
  • 1
    From the [tag:x86] tag wiki: http://teaching.idallen.com/dat2343/10f/notes/040_overflow.txt – Cody Gray - on strike Feb 01 '17 at 07:55
  • 1
    I understand CMP, Carry, and Overflow. I'm saying I don't understand how a combination of the Sign and Overflow flag reflects the relationship between two signed operands, used by Jumps based on signed comparisons. – cafekaze Feb 01 '17 at 08:13
  • Try working through some actual examples with pen and paper - there aren't very many different cases to test. This will be much more educational than someone trying to explain it (especially given that there are already plenty of good written explanations available). – Paul R Feb 01 '17 at 08:47

2 Answers2

7

Performing the signed subtraction R = Destination - Source yields a signed result.

Suppose there is no overflow - the usual arithmetic laws holds: if R = Destination - Source > 0 then Destination > Source.
Having no overflow means OF = 0 and R > 0 means SF = 0.

Now suppose there is an overflow - let's call O the most significant, non-sign, bit and S the sign bit.
An overflow condition means that either a) Computing the result's O needed a borrow and result's S didn't or b) result's O didn't need a borrow and S did.

In case a) since result's S didn't need a borrow, the two S bits of the operands were either (1, 0) (1, 1) or (0, 0).
Since result's O needed a borrow, and thus flipping the first source S bit, we must exclude the second and third option.
So the operands sign bits were 1 and 0 (thus Destination < Source), the result's sign bit SF = 0 and OF = 1 by hypothesis.

In case b) since result's S did need a borrow, the two S bits of the operands were (0, 1).
Since O didn't need a borrow, the first operand S bit has been not changed and we don't need to consider any further case.
So the operands sign bits were 0 and 1 (thus Destination > Source), the result's sign bit SF = 1 and OF = 1 by hypothesis.

To recap:

  • If OF = 0 then Destination > Source => SF = 0.
  • If OF = 1 then Destination > Source => SF = 1.

In short OF = SF.

Margaret Bloom
  • 41,768
  • 5
  • 78
  • 124
4

The OF flags tracks signed overflow, i.e. a change in the sign.
The sign flag obviously just tracks whether a number is negative or not.
Both flags monitor the sign or most significant bit (MSB) of the destination operand.

The compare CMP instructions perform a subtract.
If A != B and both operands have the same sign then obviously the following will happen (assume dword operands).

 100 -  200 = -100 (sign change OF=1 + SF=1, ergo A(100) < B(200)).
-100 - -200 =  300 (sign change OF=1 + SF=0, ergo A(-100) > B(-200)).

If A and B have different signs than the following will happen.

-100 - 100 = -200 (no sign change, SF=1, OF=0, A < B)
100 - -100 = 200  (no sign change, SF=0, OF=0, A > B)

That's all possible scenario's with OF+SF covered.
As you can see A > B only when SF <> OF and A < B only when SF = OF.

The only exception is when unsigned overflow occurs.
Let's assume we're comparing byte operands (-128..127).

126 - -126 = -4 (sign change OF=1 + SF=1, ergo A(126) < B(-126)) ***Oops.

However this will trigger the carry flag (CF) to be set, which the non-overflowing operations will not.
These incorrect results only occur when the result of the calculation does not fit inside the operand size, the solution is to keep a close eye on the carry flag and don't assume that OF and SF handle all possible cases.

Johan
  • 74,508
  • 24
  • 191
  • 319
  • This isn't correct - `OF` is set by XORing the carry out and in of the MSB, and only occurs when two positive numbers' sum is negative or two negative numbers' sum is positive (or the equivalent subtraction). In 32 bits 100 - 200 will not cause overflow. – cafekaze Feb 01 '17 at 10:05
  • @cafekaze, please work on your reading skills. I've listed the assumptions for overflow explicitly. AS with regards to SF != MSB that is semantic nitpicking. If you read carefully you can see I said OF tracks sign changes. – Johan Feb 01 '17 at 10:21
  • @cafekaze, BTW why are you talking about `SUM`'s? `CMP` does a SUBTRACT. – Johan Feb 01 '17 at 10:31
  • Please be civil, especially when there is so much false information in your post. You stated overflow tracks sign change - this is flat out untrue. Go test 100 - 200 with 32-bit operands - no overflow will occur even though the sign will change. OF tracks if the result cannot fit in the destination operand. The reason I mention sum is because subtraction is in actuality an addition of the 2's complement of the source operand - which is why carries in and out of the MSB occur and set the overflow flag during `cmp`. – cafekaze Feb 01 '17 at 10:56
  • The OF flag tracks sign change. 100% correct. It does this by tracking MSB (aka the **sign** bit). There is another flag which tracks unsigned overflow. This is called the carry flag CF. I've never considered the term Overflow flag to be very helpful. You should consider it the Sign change flag, because it **only** tracks signed overflow (aka change in MSB/SF). It does not and cannot track unsigned overflow, which is where I think you are confused. – Johan Feb 01 '17 at 11:01
  • "The OF flag tracks sign change." and "it does this by tracking MSB (aka the sign bit). " are both **incorrect**. Please go test 100 - 200 in 32-bits as I mentioned - no Overflow will occur. – cafekaze Feb 01 '17 at 22:38
  • 1
    @Johan subtraction takes advantage of twos complement, it uses adder logic and inverts the second operand and the carry in. Signed overflow is when the carry in of the msbit and the carry out of the msbit dont match. If you look at the truth table you can determine this without examining the carry out, you can do it by looking at the sign bits, I dont have it memorized, if the operands match and the result is different, something like that. – old_timer Feb 02 '17 at 02:32
  • 1
    Unsigned overflow is obviously just the carry out bit. Also note that some logic returns the inverted carry out on subtract and some doesnt, the case of compare it may not matter, depends on the architecture. Some define it is a simply the carry out and not set is a borrow, some invert on the way out and carry when set is a borrow. I dont memorize all the architectures so dont know which way x86 does it or if they have a subtract with carry which is also affected by the architecture specific definition. – old_timer Feb 02 '17 at 02:36
  • @old_timer This is what I've been trying to figure out regarding `sub` - why the Carry flag is set during a "borrow." I believe it's as you said - the carry out is inverted. As `sub` is adding 2's complement of the source: whenever a > b then a + (-b) should result in carry, but in reality Carry flag is cleared. Whereas whenever a < b then a + (-b) shouldn't result in a carry, but in reality Carry flag is set. – cafekaze Feb 02 '17 at 03:32
  • @old_timer Do you know where I might find info on how it's implemented in x86? Can't seem to find this anywhere. – cafekaze Feb 02 '17 at 03:33
  • Look for things like branch or in x86, jump if greater than, less than, with and without the or equal options. The flag combinations that include carry (is that OF in x86) should tell you. the signed conditional jumps. – old_timer Feb 02 '17 at 03:52