user2453180
user2453180

Reputation: 131

When are the carry flags set by x86 negation (NEG) / subtraction (SUB)?

What is meant by "Applying the NEG instruction to a nonzero operand always sets the Carry flag."

Why does substracting 2 from 1 set the carry flag?

           00000001 (1)
 +         11111110 (-2) [in 2-complement form]
 ---------------------
CF:1       11111111 (-1) [ why is the carry flag set here???]

Upvotes: 6

Views: 6554

Answers (4)

Artur
Artur

Reputation: 7267

It is unclear till the moment one reads how CF flag is defined. Intel CPU docs manual says:

If the result of an arithmetic operation is treated as an unsigned integer, 
the CF flag indicates an out-of-range condition

Negating anything which is treated as unsigned is obviously invalid since no unsigned value can have its sign flipped and still remain unsigned (unless it is zero). So although using pencil and paper method you get completety opposite results: 'pencil carry = 1' when negating zero and 'pencil carry = 0' when negating any other bit pattern.

Probably this is why we have 'carry FLAG' in flag register and NOT 'carry BIT'. And again - carry flag is out there to indicate that result of an operation on operand(s) (treated as unsigned) becomes out of allowed 'unsigned range' for particular number of bits.

If value which you negate is treated as signed - you should look at Overflow Flag.

The key point here is: 'carry FLAG' is NOT 'carry BIT'. It sometimes bahaves like one but not always.

BTW: It's not that only x86/64 does that. This is the same for example in Atmel's AVRs.

ARM will probably do the same since their manuals say that:

For a subtraction, including the comparison instruction CMP and the negate 
instructions NEGS and NGCS, C is set to 0 if the subtraction produced a 
borrow (that is, an unsigned underflow), and to 1 otherwise.

and in general, ARM's doc call Carry Flag - a Carry/borrow flag so again it should not be treated as Carry BIT

Upvotes: 1

Tony
Tony

Reputation: 6158

  • First, we are know when a < b, a - b can always produce a carry(Borrow).

  • Second, let's understand why x86 inverted the carry flag when subtracting.
    The first point for is so obvious a human being, but not so for a computer. (Suppose computer use a + (~b+1) to replace the a - b to do calculation.) How computer figure out there is a borrow ? We can take 2's complements as a clock.(Clockwise means the original number -- a, anticlockwise means inversion --~b+1)
    enter image description here
    So for a computer, it can tell that (for subtraction) if a > b, a + ~b + 1 will have carry (overlap in the picture); if a < b, a + ~b + 1 will not have carry (gap in the picture).

  • Conclusion:
    So for subtraction, if there is no carry means there is borrow; if there is carry means no borrow, ie invert the carry bit.

By the way, as far as I have tried, there is no overflow (ie, OF is not set when 1 - 2.) I don't think @Oliver is right in this point.

Upvotes: 1

mcleod_ideafix
mcleod_ideafix

Reputation: 11448

The carry flag, in a substraction, is set whenever the result cannot be represented in an unsigned binary number. An unsigned number is treated always as positive.

1 - 2 gives a result that would be -1 but -1 cannot be represented in an unsigned 8 bits format, hence, the carry flag is set.

This happens even if the substraction algorithm comes with a result that fits into 8 bits, and even if that result can be interpreted as a 2-complement binary number with the correct result.

The NEG instruction is meant to be used with numbers that will be interpreted as 2-complement numbers, because what the instruction does is precisely, to 2-complement that number, that is, to change its sign. Sign is a property of 2-complement numbers, not of unsigned numbers.

NEG n is the same as calculating 0-n, and the only result that fits an unsigned 8 bit number here is 00000000. Ay other result will not be a proper unsigned number.

Upvotes: 0

Oliver Charlesworth
Oliver Charlesworth

Reputation: 272772

You could view NEG a as equivalent to SUB 0, a. If a is non-zero, then this will set the carry flag (as this will always result in an unsigned overflow).

Upvotes: 5

Related Questions