Reputation: 124
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
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