The AVR instruction set supports signed arithmetic like adding,subtracting and comparing 8 bit (byte) signed numbers but has no support for 16 bit (word) signed numbers.
Since the scope of Acorn kernel is assembler and assembler only, i have summarized the basic macro definitions that will help programmers do complex signed and unsigned numbers based algorithms in AVR assembler.
Signed number representation - two's complement.
We all know that in mathematics, the negative signed number is represented by prefixing them with a ‘−’ sign. However the AVR assembler numbers are represented by a sequence of ‘1’ and ‘0’ without extra symbols and any notion of a sign.
Here the 2-complement method of sign number representation is discussed.The negative number(“-” sign) is represented by setting the most significant bit to 1 in byte/word number representation.Thus the byte 1011 0000 which is 176 in the unsigned arithmetic, becomes -80 in signed arithmetic.Converting unsigned number to its signed representation and vice versa goes through 2 basic steps:
Initial value:
01010000(2)=0x50(16)=80(10) 80
Flipping all bits yields
10101111(2)=0xAF(16)=175(10)
10101111
+
00000001
-------------
10110000(2)=0xB0(16)=176(10) - 80
The two's complement of zero is zero: inverting gives all ones, and adding one changes the ones back to zeros (since the overflow is ignored). Furthermore, the two's complement of the most negative number representable (e.g. a one as the most-significant bit and all other bits zero) is itself. Hence, there appears to be an 'extra' negative number.
It should be noted that although the two's-complement representation of a non-negative number is represented by its ordinary binary representation, the range of numbers represented is not the same as with unsigned binary numbers. For example, an 8-bit unsigned number can represent the values 0 to 255(11111111). However a two's complement 8-bit number can only represent positive bytes from 0 to 127(01111111), because the rest of the bit combinations with the most significant bit as '1' represent the negative bytes-1 to -128. The following rule summerizes the signed number range that could be represented by N bits.
Using N bits, all integers from −(2N−1) to 2N−1 − 1 can be represented.
Arithmetic Overflow.
What happens however if we try to calculate numbers and the result falls outside the designated N bits range [(2N−1) ; 2N−1 − 1]?Such condition is called arithmetic overflow and the AVR CPU architecture has got something to help programmers with this condition:
V - Two’s Complement Overflow Flag in SREG status register.
Let us observe its behaviour with an example.
11110111{2} =0xF7{16}=247{10}= -9
+
10001000{2} =0x88{16} =136{10}=-120
It is obvious that the result -129 falls outside the 8 bits signed range [-128;127] so lets see how AVR CPU will react to this condition.
In AVR assembler code this will be
ldi r16,-9
ldi r17,-120
add r16,r17
The SREG status register right after the addition will be
where V - two's complement overflow flag is set.
Same example with positive numbers
00001001{2} =0x09=9{10}= 9
+
01111000{2} =0x78{16} =120=120
The result 129 falls outside the 8 bits signed range [-128;127].
In AVR assembler code this will be
ldi r16,9
ldi r17,120
add r16,r17
The SREG status register right after the addition will be
where V - two's complement overflow flag is set.
It is up to the programmer to investigate such conditions and properly handle arithmetic overflows.
Signed 1 byte/8 bits Arithmetic.
As already mentioned AVR instruction set supports signed arithmetic on a single byte length number.Let us see some basic examples.
ldi axl,-127
ldi axh,1
loop:
add axl,axh
brvs overflow ;overflow -> handle it!
rjmp loop
overflow :
ldi axl,-127
ldi axh,1
rjmp loop
ldi axl,-127
ldi axh,-1
loop:
sub axl,axh
brvs overflow ;overflow -> handle it!
rjmp loop
overflow :
ldi axl,-127
ldi axh,-1
rjmp loop
Signed 2 bytes/16 bits Arithmetic.
Unfortunately, there is no AVR instruction set support for 2 bytes/16 bit signed arithmetic that will work right out of the box but there is an instruction infrastructure that will help us in this regard.
Calculating the allowed number range using the formula above, we get the upper and lower boundary for 2 bytes signed numbers [-32 768;32 767].
There is no avr instruction which adds and subtracts 2 byte signed numbers out of the box so we will have to define these operations ourselves
.MACRO ADD16
add @0,@2 ;Add low bytes
adc @1,@3 ;Add high bytes with carry
.ENDMACRO
.MACRO ADDI16
subi @0,low(-@2)
sbci @1,high(-@2)
.ENDMACRO
;Example
ldi axl,low(-32 768)
ldi axh,high(-32 768)
ldi bxl,low(1)
ldi bxh,high(1)
loop:
ADD16 axl,axh,bxl,bxh
brvs overflow ;overflow -> handle it!
rjmp loop
overflow :
ldi axl,low(-32 768)
ldi axh,high(-32 768)
ldi bxl,low(1)
ldi bxh,high(1)
rjmp loop
.MACRO SUB16
sub @0,@2 ;Subtract low bytes
sbc @1,@3 ;Add high byte with carry
.ENDMACRO
.MACRO SUBI16
subi @0,low(@2)
sbci @1,high(@2)
.ENDMACRO
;Example
ldi axl,low(3)
ldi axh,high(3)
ldi bxl,low(1)
ldi bxh,high(1)
loop:
SUB16 axl,axh,bxl,bxh
brvs overflow ;overflow -> handle it!
rjmp loop
overflow :
ldi axl,low(3)
ldi axh,high(3)
ldi bxl,low(1)
ldi bxh,high(1)
rjmp loop
There is an interesting point in the macro definition in regard to two's complement overflow flag in STATUS register. Since the addition/subtraction involves 2 consecutive instructions (add,adc/sub,sbc) that modify the two's complement overflow flag the question is what will happen when the 8 bit signed
boundary is met (-128 or 127). It is clear that the first instruction will set the flag but the second one will clear it up, thus preserving the integrity of the 16 bit signed addition/subtraction.
Signed 1 byte/8 bits comparison.
Going through the instruction set one could see that there are some instructions to facilitate 8-bit signed arithmetic.
BRGE - If the instruction is executed immediately after any of the instructions
CP Rd,Rr
CPI Rd,K
SUB Rd,Rr
SUBI Rd,K
the branch will occur if and only if the signed binary number represented in Rd was greater than or equal to the signed binary number represented in Rr(K).
BRLT - If the instruction is executed immediately after any of the instructions
CP Rd,Rr
CPI Rd,K
SUB Rd,Rr
SUBI Rd,K
the branch will occur if and only if the signed binary number represented in Rd was less than the signed binary number represented in Rr(K).
So the following code snippets are quite appropriate for 8 bit signed comparison
Signed 2 bytes/16 bits comparison.
As already mentioned AVR CPU has no support for 2 bytes signed number comparison therefore the following macros were devised.