shark38j
shark38j

Reputation: 124

Atmel Studio AVR operand out of range

I am working on a small program to learn how to mix C and Assembly in Atmel Studio GCC. Basically I am writing a C program to initiate the stack pointer in assembly. When I build it I keep getting an error "Operand out of range". I've initiated stack pointers many times in programs but can't get it to work in the ".s" file of this program. I've gotten the program to work with different register in the ".s" file so I don't think it's the connection between the two files. Any help would be appreciated.

Main.c:

    #define F_CPU 16000000

    #include <avr/io.h>
    #include <util/delay.h>

    extern void setStackPointer(void);

    int main(void)
    {
        DDRB |= 0x20;
        setStackPointer();
        while (1) 
        {
            PORTB = 0x20;
            _delay_ms(500);
            PORTB = 0x00;
            _delay_ms(500);
        }
    }

Assembler1.s:

    #define _SFR_ASM_COMPAT 1
    #define _SFR_OFFSET 0
    #include <avr/io.h>

    .global setStackPointer

    setStackPointer:
                ldi r18, lo8(RAMEND-0x20)
                out SPL, R18
                ldi R18, hi8(RAMEND-0x20)
                out SPH, R18
                ret

Upvotes: 2

Views: 3134

Answers (1)

Edgar Bonet
Edgar Bonet

Reputation: 3566

There are several issues here.

First, the comment by Sir Jo Black is right: there is no reason to subtract 0x20 from RAMEND. I mean, unless you want to set apart 32 bytes at the end of the RAM...

Second, there is no point in setting the stack pointer yourself. On most recent AVRs, including the ATmega328P, SP is automatically initialized by the hardware with RAMEND. C.f. the datasheet. If that weren't enough, it is initialized again by the C runtime, which gets normally linked into your program (even a 100% assembly program) if you compile it with gcc.

Third, from the avr-libc documentation:

For more backwards compatibility, insert the following at the start of your old assembler source file:

#define __SFR_OFFSET 0

This automatically subtracts 0x20 from I/O space addresses, but it's a hack, so it is recommended to change your source: wrap such addresses in macros defined here, as shown below. After this is done, the __SFR_OFFSET definition is no longer necessary and can be removed.

The recommended way to write that code is then:

setStackPointer:
    ldi r18, lo8(RAMEND)
    out _SFR_IO_ADDR(SPL), r18
    ldi r18, hi8(RAMEND)
    out _SFR_IO_ADDR(SPH), r18
    ret

If you really want to use the old hack, write

#define __SFR_OFFSET 0

at the beginning of your program. And pay attention to the double underscore at the beginning of the macro name.

Upvotes: 2

Related Questions