Signed and Unsigned 8/16 bit arithmetic.

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

  • Flip all bits


Flipping all bits yields
10101111(2)=0xAF(16)=175(10)

  • Add 1 ignoring overflow

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.

  • adding combining positive and negative bytes

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

  • subtracting combining positive and negative bytes

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

  • 16 bits/2 bytes adding combining positive and negative numbers


.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

  • 16 bits/2 bytes subtracting combining positive and negative numbers

.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

ldi axl,-12
ldi axh,1
cp axl,axh
brge loop

ldi axl,-12
cpi axl,1
brge loop


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.


.MACRO CPI16
cpi @0,low(@3)    ;Compare low byte with low immediate number’s byte
ldi @2,high(@3)  ;  Using a temp register!!!!!!!!
cpc @1,@2       ;Compare high byte with high immediate number’s byte
.ENDMACRO

.MACRO CP16
cp @0,@2 ;Compare low byte
cpc @1,@3 ;Compare high byte with carry from previous operation
.ENDMACRO

;Example

ldi axl,low(-30000)
ldi axh,high(-30000)

ldi bxl,low(1)
ldi bxh,high(1)

CP16 axl,axh,bxl,bxh
brge loop


ldi bxl,low(1)
ldi bxh,high(1)

CPI16 axl,axh,temp,-30000
brge loop