ATmega128: Adding and subtracting 16-bit numbers (assembly)

16,766

Solution 1

Back to gradeschool with pencil and paper. If I want to add 1234 and 5678

  1234
+ 5678
======

4+8 is 2 carry the 1

    1
  1234
+ 5678
======
     2    

and so on

 00110 <-- carry bits
  1234 <-- first operand
+ 5678 <-- second operand
======
  6912

the carry bit above the ones column is significant, it is called the carry in, and the carry bit that leaves the leftmost column is carry out.

What if I only had paper wide enough to add two columns at a time?

 110 
  34 
+ 78 
======
  12

I start with the two lower sets of digits, and I require a zero as a carry in. I get a result 12 with a carry out.

Now I take that carry out, use it as a carry in for the next two digits. This adder I must be able to take a carry out from a prior add and use it as the carry in for this add.

 001
  12
+ 56
====
  69

When all is said and done I get 69 and 12, put those together I get 6912 but didnt need a full 4 digit adder to get there. You can repeat this forever or until you run out of memory, registers or clock cycles.

The avr may have other ways to solve the problem, but most processors at least have two forms of add and two forms of subtract so that you can cascade the adder to be as wide as you need. Examine the instruction set for the avr and what is going on above should jump out at you.

EDIT:

A C example might help...(switching to hex)

unsigned int a,b,c,d,cin,cout,x,y;

a=0x12; b=0x34;
c=0x56; d=0x78;

x=b+d; //dont want a carry in or assume it is zero
cout=x&0x100; 
if(cout) cin=1; else cin=0;
y=a+c+cin; //need the carry out on the prior add as the carry in here

x&=0xFF;
y&=0xFF;

printf("0x%02X%02X\n",y,x);

EDIT2:

I hope this is not a homework assignment...

ldi r20,0x12
ldi r21,0x34
ldi r22,0x56
ldi r23,0x78
add r21,r23
adc r20,r22

result is in r20 high byte, and r21 low byte

if you need to read from ram there are many ways, this assumes the 16 bit numbers are little endian

lds r0,0x100
lds r1,0x101
lds r2,0x102
lds r3,0x103
add r0,r2
adc r1,r3

r0 low half of result, r1 upper half.

or use one of the x,y,or z pointer registers

;put 0x0100 in Z
ldi r30,0x00
ldi r31,0x01
ld r0,z+
ld r1,z+
ld r2,z+
ld r3,z+
add r0,r2
adc r1,r3

Solution 2

Your datasheet has add and adc from this link. As I guessed above, perhaps you need to use program memory loads, ldm to get at your numbers.

Basically:



ldi r0, number1 ; get low address of number 1 in a register.
ldm r16, r0+    ; low-byte of number 1 - inc pointer after each read with r0+
ldm r17, r0+    ; high-byte of number 1
ldm r18, r0+    ; low-byte of number 2
ldm r19, r0+    ; high-byte of number 2

add r16, r18    ; add low bytes
adc r17, r19    ; add hi-bytes with carry

; r16/r17 now holds the sum of the number1 and number2 as a 16-bit number.  
; Store to RAM or whatever you want with them.
;  Note, you may have to push/pop registers depending on your system requirements...

Above code won't work and there is a working example...

If I'm understanding the context correctly with the post-increment ldm command and all registers are 8-bit.

Solution 3

Well, you're not really issuing any addition instruction. I'm not an AVR programmer in any way, but after a quick glance at the instruction set of the ATmega128, something like this seems much more correct. I'm assuming your assembler uses the Intel syntax and that the numbers are stored as Little Endian.

lds r16, number1 ; low byte of number1
lds r17, number2 ; low byte of number2
add r16, r17 ; number1 += number2

lds r17, number1+1 ; high byte of number1
lds r18, number2+1 ; high byte of number2
adc r17, r18 ; add the high bytes including the carry flag generated by the "add" instruction above

The result is therefore stored in r17:r16, e.g. the high byte in r17, and the low byte in r16.

Share:
16,766
Lasse A Karlsen
Author by

Lasse A Karlsen

Part-time programmer.

Updated on June 04, 2022

Comments

  • Lasse A Karlsen
    Lasse A Karlsen about 2 years

    I'm working with a ATmega128 microcontroller and supposedly need to add two 16-bit numbers. I'm using AVR Studio and this is what I got so far:

    .include "m128def.inc";
    
    .equ    ramstart = 0x100
    .def    temp = r16
    
    .dseg
    .org ramstart
    number1: .byte 2
    number2: .byte 2
    
    .cseg
    .org 0
    
    rjmp start
    
    start:
        ; number1 := 0x7856
        ldi temp, low(number1)
        sts number1, temp
        ldi temp, high(number1)
        sts number1+1, temp
    
        ; number2 := 0x34B2
        lds temp, number1
        sts number2, temp
        lds temp, number1+1
        sts number2+1, temp
    
    slutt:
        rjmp slutt
    

    This is not far from the first time I'm using any type of assembly, I know I'm doing something wrong, but can't seem to figure out what. Am I missing the carry flag?

    • old_timer
      old_timer over 12 years
      is this a homework assignment? If so please tag it as such. yes, first you need to load some registers, then you need to add them all up or subtract. You can always fall back to add/adc or sub/sbc to perform large number of bit/byte adds or subtracts.
    • Lasse A Karlsen
      Lasse A Karlsen over 12 years
      If I use add or adc, how do I store the result the correct way?
  • Lasse A Karlsen
    Lasse A Karlsen over 12 years
    Hm, didn't work.. I only end up with 0xFF in the three registers. Like it's maxed out or something?
  • Daniel Kamil Kozar
    Daniel Kamil Kozar over 12 years
    The specification of the LDS instruction says that it works properly only on registers from 16 to 31. I edited my post accordingly.
  • Michael Dorgan
    Michael Dorgan over 12 years
    Instead of using lds, what about ldm? Not sure where in RAM ramstart is allocated, but it could be in "program memory" considering you are placing your numbers and code in the same area. My guess is this is why you are getting 0xFF - SRAM is not initialized or doesn't exist.
  • Lasse A Karlsen
    Lasse A Karlsen over 12 years
    Thanks for this post. Wrapping my head around it just now.
  • Lasse A Karlsen
    Lasse A Karlsen over 12 years
    Hmm.. where are you loading number2? And by ldm I assume you mean ldi?
  • Michael Dorgan
    Michael Dorgan over 12 years
    Load address immediate into r0. Load Program Mode from r0 into r16,r17,r18,r19. So I meant the ldm instruction. I'm confused how to get values from RAM though via registers as they are only 8-bits in length.
  • Michael Dorgan
    Michael Dorgan over 12 years
    Ah, X/Y/Z registers ala Z80 days. My bad.