7

In the ARM documentation here, it says that:

A carry occurs:

... if the result of a subtraction is positive or zero ...

I know from this answer on SO that the carry flag is set in subtraction when there is unsigned underflow (ie the number being subtracted (subtrahend) is greater than the number that is being subtracted from (minuend)).

So consider the example:

r1 = 5
r2 = 10
CMP r1, r2

Compare (CMP) does a subtraction as mentioned here and then sets the appropriate flags. In this case, r1-r2 = 5-10 = -5. Since we have unsigned underflow here (5 < 10), we expect the carry flag to be set. But according to the ARM documentation, the result (-5) is not positive or zero, so the carry flag should not be set. (Assuming we look at the result signed; otherwise a carry would never occur according to the documentation).

Is the ARM documentation wrong? What's my misunderstanding?

Community
  • 1
  • 1
takanuva15
  • 1,286
  • 1
  • 15
  • 27
  • unsigned overflow is the carry out of the msbit of the addition. signed overflow is set if the carry in and carry out of the msbit of the addition differ. logic uses addition to do subtraction. twos complement negation (-a) = -(a) invert and add one the second operand is inverted (ones complement) and the carry in is inverted (a one). some architectures invert the carry out to indicate an unsigned borrow rather than an unsigned overflow. – old_timer Oct 30 '18 at 17:47
  • The Q&A you linked is about x86, where the carry flag works as a borrow flag for subtraction. ARM is the opposite. – Peter Cordes Oct 31 '18 at 03:31
  • @PeterCordes Oops lol; thanks for the clarification. (That was causing my confusion, as the answer by fuz also clarifies) – takanuva15 Oct 31 '18 at 17:47

3 Answers3

9

ARM uses an inverted carry flag for borrow (i.e. subtraction). That's why the carry is set whenever there is no borrow and clear whenever there is. This design decision makes building an ALU slightly simpler which is why some CPUs do it.

fuz
  • 88,405
  • 25
  • 200
  • 352
  • The same documentation mentions that the CC condition code (unsigned lower) is true when the carry flag is clear. In the `CMP 5,10` example in the question, the carry flag would not be set in ARM. Thus, in ARM, I should look at CC as "is the 2nd arg lower than the 1st unsigned"? – takanuva15 Oct 30 '18 at 16:29
  • @takanuva15 I think that's correct. Another name for the `CC` condition code is `LO` for “unsigned lower.” – fuz Oct 30 '18 at 16:31
  • Interesting...so for programs in other assembly languages where the carry flag is not inverted, I would need to read `CMP 5,10` as "is the 1st arg lower than the 2nd unsigned"? (That's probably been confusing me this whole time) – takanuva15 Oct 30 '18 at 16:34
  • Wait, no. From my first comment, since `CMP 5,10` doesn't set the carry flag, the CC/LO flag would be true. Which means that I should read `CMP 5,10` (with a following CC check) as "is the 1st arg lower than the 2nd unsigned"? – takanuva15 Oct 30 '18 at 16:39
  • 1
    Oh sorry, I misread your first comment. Yes `CMP a, b` followed by a `BCC` branches if `a < b` unsigned. – fuz Oct 30 '18 at 16:41
  • some instruction sets invert the carry out into a borrow, some dont, I would not in anyway assume that arm is the only one that does it the way it does it. You have to take each new instruction set one at a time and examine them. The nice thing about arm vs say x86 is that the flag and conditional behavior is well documented you can tell from the if V set and not N and such and such exactly what the flags represent... – old_timer Oct 30 '18 at 17:48
  • do a very simple test have one operand fixed, say 5 have another operand go 3,4,5,6,7 and examine the flags, you can see what they do when one is greater than equal to and then less than the other – old_timer Oct 30 '18 at 17:49
  • @fuz Ok cool. Now consider `CMP -1,0` (obviously -1 is 0xFFFFFFFF). When doing the subtraction, the result is -1, which would not set the carry flag according to the ARM documentation referenced in the question. However, the CC condition code would be true since the carry bit is not set. But -1 is not less than 0 when interpreting them unsigned. Isn't that a contradiction? – takanuva15 Oct 30 '18 at 18:07
  • @takanuva15 It does set the carry flag because there is no borrow. Remember, with subtractions the carry flag is set if no borrow occured and clear if borrow occured; as I said in my answer, you can think of this as the carry flag being inverted for subtractions. No borrow occurs in `0xffffffff - 0x00000000`. So you perhaps misunderstood the documentation. – fuz Oct 31 '18 at 01:12
  • @fuz Your logic makes sense, but the [ARM documentation](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/BABEHFEF.html) mentioned in the question says "a carry occurs...if the result of a subtraction is positive or zero". `CMP -1,0` is going to do `-1 - 0` in terms of subtraction. The result of that is `-1`, which is not "positive or zero". Thus, according to documentation's words, carry should not be set. What am I misunderstanding when the documentation says "if the result of a subtraction is positive or zero"? – takanuva15 Oct 31 '18 at 17:44
  • @fuz uhhh...your prior comment said "No borrow occurs in `0xffffffff - 0x00000000` ". Now you said "Yes, carry (precisely: borrow) occurs". In both cases we are talking about the `CMP -1,0` example I mentioned. (I understand that the carry flag is set in ARM when there is no borrow). But that doesn't seem to address what the documentation itself is literally saying: " **C - Set to 1 when the operation resulted in a carry** ...A carry occurs if the result of a subtraction is positive or zero". The sub that happens in `CMP -1,0` doesn't result in a positive or 0. Thus a carry doesn't occur... – takanuva15 Nov 01 '18 at 02:59
  • @fuz ...according to the documentation. Thus the C flag is not set according to the documentation. Can we agree on that so far from what _the documentation directly says_ ? – takanuva15 Nov 01 '18 at 03:04
2

we dont need all 32 bits to show what is going on with a subtraction

5 - 3

from gradeschool we know that 5 - 3 = 5 +(-3) which is how it is done in logic, a feature of twos complement is to get to -3 you invert and add one so

5 - 3

      1
   0101
 + 1100
=========

finish it

  11011
   0101
 + 1100
=========
   0010

so generically (not processor specific) 5 - 3 = 2. the carry in and carry out of the misbit are both one so the signed overflow would be not set the carry out is a 1. But some architectures as well as inverting the second operand and the carry in (to the lsbit) also invert the carry out calling it a borrow. Some dont.

Some document this when you look at things like greater than and less than as to what the definition of the carry bit is

5 - 4

  11111
   0101
 + 1011
=========
   0001

5 - 5

  11111
   0101
 + 1010
=========
   0000

5 - 6

  00011
   0101
 + 1001
=========
   1111

5 - 7

  00011
   0101
 + 1000
=========
   1110

What this is showing is that if you look at the raw carry out it switches when operand b equals operand a, when b is less than a it is set when b is equal to or greater than a it is clear. So then if the architecture leaves it unmodified you can use the carry bit for unsigned greater than or less than (but not equal) in one of the cases greater than or equal is defined by that flag and which direction depends on the architecture (if it inverts carry out into a borrow). the other direction you either flip the operands and use the one bit or you use N and C to determine something than or equal.

The arm docs unlike some others show you for each of the conditionals what the flags are and knowing the above or being able to repeat it in a simple test you can figure out if they invert or dont. the instruction sbb is subtract with borrow rather than sbc subtract with carry which sorta, but not by itself implies how that instruction set interprets the bit.

old_timer
  • 69,149
  • 8
  • 89
  • 168
1

I have same confusion about:

  • The ALU status flags
    • A carry occurs if the result of an addition is greater than or equal to 232, if the result of a subtraction is positive, or as the result of an inline barrel shifter operation in a move or logical instruction

thought: 5 - 3 = 2 -> positive result -> should C=1 ?

finally found ARM official useful doc: Carry flag

In A32/T32 code, C is set in one of the following ways:

  • For an addition, including the comparison instruction CMN, C is set to 1 if the addition produced a carry (that is, an unsigned overflow), and to 0 otherwise.
  • For a subtraction, including the comparison instruction CMP, C is set to 0 if the subtraction produced a borrow (that is, an unsigned underflow), and to 1 otherwise.

to know:

  • for subtraction: 5 - 10 = -5 -> produced a borrow = unsigned underflow = negative result -> should C=0
    • Note: NOT C=1

so, conform to:

  • for subtraction: 5 - 3 = 2 -> positive result -> should C=1
crifan
  • 12,947
  • 1
  • 71
  • 56