News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Twos complement and the CF (carry flag)

Started by lutherbaker, January 29, 2007, 05:07:32 PM

Previous topic - Next topic

lutherbaker

I am a newbie ... self-studying Kip Irvine's 4th edition assembly book using Visual Studio 2005 and MASM 8.0. I may have mistyped an example program into my compiler - but assuming not ...

I've got a basic handle on binary numbers and twos complement notation - but I have a question about the CARRY FLAG.

Since the CF is really only relevant for unsigned operations ??? I assume it looks at each transaction as though it were being carried out on unsigned values.

So, since "sub" is implemented as adding the twos complement, why does the CF=1 (carry flag get set to 1) when subtracting 90h from 80h and not when adding 70h to 80h?

Aren't they the same from an unsigned perspective? Nothing gets carried ... what is the CPU doing when subtracting 90h from 80h that it knows to mark the CF=1?



80h + 70h = F0h, CF=0
that is
1000 0000 + 0111 0000 = 1111 0000

but

80h - 90h = F0h, CF=1
that is
90h => 1001 0000 => 0110 1111 + 0000 0001 => 0111 0000
so
80h - 90h = 80h + 70h = F0h.

Why does the CARRY FLAG = 1?


Thanks in advance,

-Luher

TNick

Welcome aboard!!

Intel® 64 and IA-32 Architectures Software Developer's Manuals will be your friends. You have a comprehensive explanation for each instruction in there.


Quote from: Intel® 64 and IA-32 Architectures Software Developer's ManualsSubtracts the second operand (source operand) from the first operand (destination
operand) and stores the result in the destination operand. The destination operand
can be a register or a memory location; the source operand can be an immediate,
register, or memory location. (However, two memory operands cannot be used in one
instruction.) When an immediate value is used as an operand, it is sign-extended to
the length of the destination operand format.
The SUB instruction performs integer subtraction. It evaluates the result for both
signed and unsigned integer operands and sets the OF and CF flags to indicate an
overflow in the signed or unsigned result, respectively. The SF flag indicates the sign
of the signed result

Regards,
Nick

lutherbaker


Wistrik

Flags will always be set by certain instructions and events regardless of whether you need them or not. They're there in case you do need them.

The Carry flag works whether you're doing signed or unsigned math (the Sign and Overflow flags are used in making signed decisions). It will always be set by addition and subtraction instructions when there is a carry or a borrow. This is useful when you're working with smaller chunks of a larger value, such as bytes of a word, words of a dword (common in 16-bit programs), dwords of a qword (32-bit software), qwords of a oword (64-bit software), etc. Since you're subtracting a byte value from a byte value, you may not care what the Carry flag says afterward. In that case, just ignore it. On the other hand, if you're working with smaller chunks of a larger value, you must take the Carry flag into consideration if you wish to receive correct results.

For example, say your values are word-sized instead of byte-sized, but you still wish to add or subtract them a byte at a time. The Carry flag must be taken into consideration in this case.

Word values for subtraction: 0080h, 0090h

(SUB) 80h - 90h = F0h, CF = 1 (the result went negative, so set CF to signal a borrow)
(SBB) 00h - 00h - CF = FFh, CF = 0 (the CF borrow is taken from the second byte, and the CF is cleared to signal borrow applied)
Result = FFF0h

Without the Carry flag and the SBB (subtract with borrow) instruction, you'd have an incorrect result of 00F0h. (SUB sets or clears the Carry flag but doesn't use its value in the subtraction.)


Word values for addition: 0080h, 0070h

(ADD) 80h + 70h = F0h, CF = 0 (the result doesn't exceed byte limits so clear CF to signal no carry)
(ADC) 00h + 00h + CF = 00h
Result = 00F0h

Nothing special here. If you were working with a variable, however, it might at some point grow to where it would no longer fit into a byte. In that case the Carry flag would be set, and the ADC (add with carry) instruction would make sure it was figured into the addition. For example, adding 0080h and 0090h:

(ADD) 80h + 90h = 10h, CF = 1 (the result overflowed byte limits so set CF to signal carry)
(ADC) 00h + 00h + CF = 01h
Result = 0110h

Without the Carry flag and the ADC instruction, you'd have an incorrect result of 0010h.


This is like basic math taught in school, if you think about it for a moment. When working with decimal numbers you'd write a '1' over the next column as a reminder that your addition in the current column exceeded the value '9', or you'd subtract '1' from the next column and add it to the current column if your subtraction would result in a negative value. (Well, that's how we did it before calculators were allowed in class.)

As suggested by TNick, download and read the manuals from Intel (or AMD if you wish; I have both sets of manuals for comparison). They'll tell you everything you want to know, and much more, about why the processor does what it does. It is very helpful to understand how the flags work with arithmetic and other instructions, as this will make finding bugs and logic errors in your code much, much easier. The software manuals are helpful, but don't forget to grab the instruction lists too. (Intel has them split into two documents: A-M and N-Z. AMD has them in one document.)

Edit: finished an unfinished sentence.

Ratch

lutherbaker,

QuoteI've got a basic handle on binary numbers and twos complement notation

     The carry flag (CF) and overflow flag (OF) work the same for either 1's or 2's complement arithmetic.

     You should do a search of this forum and come up with these two links, where the subject gets the hell beat out of it.  Ratch

http://www.masm32.com/board/index.php?topic=5852.0
http://www.masm32.com/board/index.php?topic=2923.0


lutherbaker

I really appreciate all the comments.

So here's what was confusing me ... I believe the cpu implements a SUB by adding the 2s complement.

That threw me. For example, here is a standard ADD operation.

80h + 70h = F0h

   1000 0000 ...
+ 0111 0000 ...
------------------------
   1111 0000 ...

CF=0

No borrow, no carry. Just plain old addition.


Now consider: 80h - 90h. How does this get implemented I thought?

Well,

1000 0000 - 1001 0000 = 1000 000 + (0110 1111 + 1) = 1000 0000 + 0111 000 = F0h.

or

  1000 0000 ...      1000 0000          1000 0000
- 1001 0000 ... => 0110 1111 + 1 = 0111 0000
-------------------------------------------------------------------
                                                    1111 0000


Again, no borrow, no carry - at least not literally.

So that confused me. My underlying question was ... since there is no literal CARRY in this implementation of SUB, what causes the CPU to be smart enough to set the CF.

Technically then, to go down the path I am thinking, I guess the the SUB operation also inverts the CF value ... so, on a sub operation, if CF=0, then CF=1.

The theory works quite well but I've no documentation stating such. I think this is related to the way I'm looking at the implementation. The "borrow- a-bit" terminology you used gave me another perspective to think about - as opposed to the literal operations the Processor is doing. Since one never 'borrows' to add, I was just considering CARRYing... but its obvious we are BORROWing a bit as well in some of these operations so I'm expanding how I model this in my head.

Any risky assumptions here?

Thanks for all the input. Thanks,

-Luther

Ratch

lutherbaker,

QuoteTechnically then, to go down the path I am thinking, I guess the the SUB operation also inverts the CF value ... so, on a sub operation, if CF=0, then CF=1.

The theory works quite well but I've no documentation stating such.

From: Digital Design by M. Morris Mano, Second Edition, pp. 12,13

Subtraction with Complements

The direct method of subtraction taught in elementary schools uses the borrow concept.  In this method, we borrow a 1 from a higher significant position when the minuend digit is smaller than the subtrahend digit.  This seems to be easiest when people perform subtraction with paper and pencil.  When subtraction is implemented with digital hardware, this method is found to be less efficient than the method that uses complements.

    The subraction of two n-digit unsigned numbers M - N is base r can be done as follows:
1)  Add the minuend M to the r's complement of the subtrahend N.  This performs M + (r^n - N) = M - N + r^n .

2)  If M >= N, the sum will produce an end carry, r^n, which is discarded; what is left is the result M - N .

3)  If M < N, the sub does not produce an end carry and is equal to r^n - (n-M) .  To obtain the answer in a familiar form, take the r's complement of the sum and place a negative sign in front.

So there is the formal procedure you wanted.  Ratch

Mark_Larson


  You might want to look up a copy of Masm 6.15.  6.15 supports MMX/SSE/SSE2.  I hate the 8.0 series.  They are buggier.  you can also use the 6.14 version from MASM32, but it only supports MMX/SSE.  Their are macros for both SSE2 and SSE3 that you can use, that you can't tell the difference from the normal instructions.

BIOS programmers do it fastest, hehe.  ;)

My Optimization webpage
htttp://www.website.masmforum.com/mark/index.htm