1
votes

I am trying to understand how to do signed addition between 2 16-bit numbers in avr assembly.

Given this C code:

#include <stdbool.h>

int16_t my_fun(const int16_t x, bool is_positive){
    const int16_t y = 10000 * (is_positive? 1 : -1);
    return x + y;
}

avr-gcc outputs (simplified to be more readable):

my_fun:
    cpse r22,r1
    rjmp positive
    ldi r18,-16
    ldi r19,-40
    rjmp add_them
positive:
    ldi r18,16
    ldi r19,39
add_them:
    add r24,r18
    adc r25,r19
    ret

I understand that x's lower byte is in r24 and higher byte in r25. Also, the return value is in the same registers. r18 and r19 store y's lower and higher byte.

My questions are: in the positive case r18 and r19 get 16 and 39 (39*256+16==10000) but why do they get -40 instead of -39 in the negative case? How do signed multi-byte numbers are represented in general and how do we add them?

1
-40 is 216 unsigned, -16 is 240 unsigned which gives 55536 unsigned which is -10000 signed. Or in other words -10000 is 0xD8F0 hex, which has the given low and high bytes. See two's complement. Addition itself works the same.Jester
addition and subtraction have no notion of signed or unsigned. Multiply and divide, sure, but add and subtract not. Part of the beauty of twos complement.old_timer

1 Answers

0
votes

Extended precision integers are stored in chunks (usually of the machine word size, which is 1B for AVR, but 4B for x86-32.). GMP (the gnu multi-precision library) calls these chunks "limbs".

In your case, you have all the chunks of both numbers in registers (after the ldi instructions). They can be stored in memory like an array or struct would be.

Since carry/borrow propagates from LSB to MSB in addition and subtraction, the sequence of machine instructions for adding them begins with a normal add on the LSB which sets the carry flag (or not), followed by adc (add-with-carry) instructions for all the rest of the chunks. adc reads and writes the carry flag.

add / adc is the same sequence you'd see on x86 for adding a 64bit number in 32bit mode, or for a 128b number in 64b mode. (x86 uses the same mnemonics as AVR for those insns.)