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

4

6

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???]
Allsopp answered 21/7, 2013 at 19:22 Comment(9)
Because the spec says it does. (What you're doing is subtracting 1 from zero. This causes a "borrow" into the high-order bit. Borrow and carry are the same thing only different.)Surfacetosurface
Carry flag is set when the result of the operation is too big to fit into the registers you are using.Fransis
I don't see why it is too big in this case.Allsopp
"you're doing is subtracting 1 from zero." I'm adding 1's to zero to get eight 1's in the end ;)Allsopp
For subtraction, the carry flag acts as the borrow flag. Since 1 - 2 is negative, it results in a borrow - thus, the carry flag is set.Chalutz
what instruction set is this? depending on the processor the carry flag being set either indicates that it DID borrow or DID NOT borrow. If there is no modification then a non-borrow subtraction will result in a 1 on the last carry out. some processors invert that bit to make it a zero in the processor status register or wherever that processor stores its flagsPalmira
the carry flag is simply the carry out from the msbit, some processors invert it if the operation is something subtract based (neg, sub, sbc, etc). Some dont. there is no real magic to it at all, I suspect what you are seeing is that they inverted the carry out, indicating there was a borrow.Palmira
Possible duplicate of https://mcmap.net/q/1776812/-carry-flag-in-substractionBucket
Note that x86 sub doesn't set flags according to adding the inverse, it sets flags according to an actual borrow. Arithmetic identities and EFLAGS. CF is the borrow output from the binary subtraction.Heth
S
5

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).

Sokil answered 21/7, 2013 at 19:26 Comment(7)
Why does that make sense?Allsopp
@user2453180: Are you familiar with the purpose of the carry flag? Are you familiar with when it gets set in a subtraction?Sokil
I don't see why '11111111' (8 numbers) would not fit inside a 8 bit register.Allsopp
Is the carry flag inverted by definition. when using substractions on x86? How about arm and mips?Allsopp
@user2453180: In your example of 1-2, the assumption that this can be modeled as 1+(-2) is valid, so long as you adapt how the carry flag is calculated. Remember that if you had done long subtraction (i.e. 0001 - 0010), you would have generated a borrow. So in that sense, yes, it's inverted.Sokil
@Allsopp It sounds like you should get an x86 programming book and work through it. These are all questions the book should answer. It is unreasonable to expect SO to teach you x86 assembly from scratch.Cannery
@RaymondChen This is actually from a x86 programming book.Allsopp
M
1
  • 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.

Morello answered 29/3, 2015 at 13:30 Comment(1)
Note that x86 sub flag-setting is not exactly equivalent to add with -src. For example, emulating sub a,b as neg b / add a,b / cmc doesn't work for b=0. sub a,0 always clears CF, but so does add a,0 so inverting the CF result of that would be wrong. (i.e. ~0 + 1 wrapping back to 0 is the special case, you'd have to handle that carry-out.) See Arithmetic identities and EFLAGSHeth
D
1

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

Divergence answered 18/5, 2017 at 12:19 Comment(1)
on x86, CF is a borrow output from CMP/SUB. On ARM, it's inverted: it's set on no-borrow, cleared on borrow propagation out of the top bit. So ARM is most definitely not the same as x86 in the way they set their carry flag for subtraction.Heth
P
0

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.

Prowl answered 29/3, 2015 at 13:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.