1

I try to compare the next expressions:

1) 
    mov al, 4
    mov bl, 12
    sub al, bl ; CF == 1

    0000 0100 (4)
   +
    1111 0100 (-12)
   = 
    1111 1000 (-8 == 248) 

2) 
    mov al, 4
    mov bl, -12
    add al, bl ; CF == 0

    0000 0100 (4)
   +
    1111 0100 (-12)
   = 
    1111 1000 (-8 == 248) 

The results are identical, but carry flags are not. Why? The subtraction realized by addition to twos complement value.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Danny
  • 126
  • 6
  • Is this hand written assembly, generated by a compiler, or just an exploration? – 1201ProgramAlarm Nov 20 '20 at 22:08
  • CF is for unsigned arithmetic. As such first case goes negative hence you have unsigned overflow. Second case in unsigned is just 4+244 which does not overflow. – Jester Nov 20 '20 at 22:09
  • But the CPU does not know what kinds of values he process signed or unsigned. – Danny Nov 20 '20 at 22:17
  • I use netwide assembler (nasm) – Danny Nov 20 '20 at 22:17
  • Right. In the second case you loaded `1111 0100` which the cpu interprets as 244 for the purposes of CF. It's your job to consult CF or OF depending on whether you want unsigned or signed. – Jester Nov 20 '20 at 22:19
  • Yes, but this two operations are equals, but CFs are not... – Danny Nov 20 '20 at 22:26
  • 2
    @Danny They are not equal. The numeric result is equal, but the carry is not. Do the subtraction as a long subtraction and you'll see that it does generate a carry. Subtraction is not implemented as addition to the two's complement in x86. – fuz Nov 20 '20 at 22:48
  • 2
    When considered unsigned the first one is 4-12 which overflows. The second one is 4+244 which doesn't, and it's not the same. In signed, yes, they are equivalent, but then you need to consult the OF and you will find it is zero in both cases. – Jester Nov 20 '20 at 22:51
  • That is, the subtraction is not implemented as addition to the two's complement? How is it implemented? Thanks – Danny Nov 20 '20 at 22:57
  • 1
    This is a duplicate of your prior question. The carry flag is inverted into a borrow flag on subtraction on an x86. It is not inverted on an addition. – old_timer Nov 21 '20 at 01:23
  • 4 - 12 will borrow so the carry out flag is a 1 (on an x86 and some number of other architectures). Some architectures do not invert the carry flag on the way out of a subtraction and on those you would see the same carry flag output. – old_timer Nov 21 '20 at 01:25

1 Answers1

4

Subtraction is not equal to addition of the two's complement on x86. To understand what value the carry flag will assume you must perform a long subtraction instead:

    0000 0100
-   0000 1100
-------------
  1 1111 1000

See how there's a borrow left over at the end? This borrow is what sets the carry flag (i.e. carry equals borrow).

Some other architectures like ARM do indeed implement subtraction as addition, but they don't do so as addition of the two's complement but rather as addition of the one's complement and an extra carry in. This is significant when subtracting 0.

For example, your method would yield for 12 − 0:

    0000 1100
+   0000 0000 (- 0000 0000 => + 0000 0000)
-------------
  0 0000 1100

with a clear carry. But what actually happens is

    0000 1100
+   1111 1111 (- 0000 0000 => +1111 1111 + 1)
+           1
-------------
  1 0000 1100

with a carry. This detail is important as otherwise comparisons with 0 would not work correctly. In this scheme, carry is indicated whenever there is no borrow (i.e. carry is complemented borrow).

The way Intel does it and the way ARM does it actually always yield the same result, except the carry flag is just the other way round. So whenever ARM would set the carry, Intel would clear it and vice versa.

Both approaches to subtraction semantics are fairly common. The ARM approach is slightly easier to implement as it allows direct use of the adder for subtraction without having to touch the carry at all. With the Intel approach, carry in and out have to be complemented when a subtraction is performed, but the extra gates to do so really don't matter in the grand scheme of things. On the other hand, Intel's approach is more intuitive to programmers as thinking of the carry flag as also indicating borrow makes more sense if you visualise the operation being performed as being a long subtraction.

fuz
  • 88,405
  • 25
  • 200
  • 352