how does the SUB instruction work in AVR assembly? does it take the twos compliment and add it or does it function differently? If I subtract two 8- bit numbers, say 1 - 2 and the answer is -1, should I expect the twos complement of 1 or some other value?
Edit: I'm not sure how to think about the question when thinking about numbers higher than 128, my understanding of twos compliment is that you half the magnitude of numbers you can count to, but if I give the SUB instruction to do:
2 - 255
what do the bit operations look like? My understanding is that the twos compliment of 255 requires nine bits (100000001), does it just overflow in an 8 bit system? Would the math look like:
00000010 00000001 = 00000011
CodePudding user response:
The assembly language does not do subtract nor does the machine code, the logic in the processor does. And yes it does use addition to do subtraction.
2 - 255
from grade school
2 - 255 = 2 (-255)
and we know from first semester programming class, invert and add one
2 - 255 = 2 (-255) = 2 ~255 1
And now it is easy to do with an adder
1
00000010
00000000
===========
000000001
00000010
00000000
===========
00000011
now that looks strange, but if you are thinking 8 bit with a number like 255 that is -1 signed. so 2 - (-1) = 2 1 = 3. And that makes sense. The beauty of twos complement is that we can use the same logic and not care about unsigned or signed, you get the same answer for the same bit patterns.
as pointed out in comments 255 for an 8 bit number can only be unsigned. but the same bits are a -1.
you example is a tough one to visiualize. Since it all works the same 4 bits is easier to describe here than 8. so 0 to 15 or -8 to 7 for unsigned or signed.
so if we did 12 5 we would get 17 but that is an unsigned overflow, lets try
0
1100
0101
======
11000
1100
0101
======
0101
carry out is set so we had an unsigned overflow.
what about -7 - 4 = -11
10011
1001
1100
=======
0110
which is 6, but. look at the carry in and carry out of the msbit. They are not the same, so this is a signed overflow. Another way to say it is look at the msbits of the operands and the result, if the operand msbits are the same but the result is not, then it is a signed overflow.
if we had another bit 10110 is 1001 1 = 1011 so -11.
it works for 8 or 32 or 64 or 128 bits. multiplication and division can have signed vs unsigned, but addition and subtraction due to the nature of twos complement, the bit patterns use an adder and nothing special for signed or unsigned. It is the programmer, not the logic, that cares.
Now when it comes to cascading, that does matter, in particular with subtraction. Not shown above, you can try it yourself, but when doing unsigned a carry out of the msbit is an unsigned overflow of course, but also if the processor uses flags, that becomes the carry flag. But for subtraction a 1 (and not signed overflow) means not borrow. The math did not need to do a borrow (if you did it longhand and not the invert and add one thing). Which is good but some processors invert that and make the carry flag a borrow flag for subtract. Others do not invert and make it a not borrow flag for subtraction. If there is a subtract with borrow then it matters if they inverted or not as to how the lsbit carry in is handled. Now this is buried in the logic and "just works" but since you asked how these things work.
Also better processor documentation (Read: ARM) shows you a table of conditionals and what flags are what. Which will not be the same from processor to processor if the carry out is a borrow vs not borrow for subtract, since subtract is use for compare instructions...
CodePudding user response:
The subtraction operation works as long as you subtract two equal types of data. It's mean you can subtract signed
- signed
or unsigned
- unsigned
. You can't mix signed and unsigned type.
First case:
; xl, yl is considered as signed
ldi xl, 2 ;first op = 2
ldi yl, 255 ;second op = -1
sub xl, yl ;result = 3 because (2-(-1))
brvs overflow ;test if overflow occurred (no it's false now)
Second case:
; xl, yl is considered as unsigned
ldi xl, 2 ;first op = 2
ldi yl, 255 ;second op = 255
sub xl, yl ;result = invalid because 2-255 is negative
brlo overflow ;test if first operand is lower as second (yes it's true now)
If you really want subtract 2 - 255 you must use two byte for both numbers. Because result is -253 and valid range for one byte is only -128 to 127
; xh:xl, yh:yl is considered as signed
ldi xh, 0
ldi xl, 2 ;first op = 2
ldi yh, 0
ldi yl, 255 ;second op = 255
sub xl, yl
sbc xh, yh ;result is -235 (0xFF03)
brvs overflow ;test if overflow occurred (no it's false now)