2

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
shark38j
  • 114
  • 13
  • What's the MCU you are using? – Sir Jo Black Dec 04 '17 at 11:03
  • Using the assembler as: `avr-as -mmcu=atmega328 Assembler1.s`, the compiler doesn't generate errors. – Sir Jo Black Dec 04 '17 at 11:15
  • ... But the code is generated with `ldi r18,0` both for low and high byte! – Sir Jo Black Dec 04 '17 at 11:57
  • I’m using an Arduino. I’ve always initiated the stack from a stk500 board. I wonder if the Arduino boot loader hinders you from initiating the stack. – shark38j Dec 05 '17 at 23:54
  • Yes, but STK500 supports several MCUs, which's the MCU you are using? – Sir Jo Black Dec 06 '17 at 09:56
  • I remember that some register are to be used as memory pointers. Compiling this assembly code also the register results to have a value of 0, this could be not correct! I think this macro are not correct to be used with avr-as. – Sir Jo Black Dec 06 '17 at 10:41
  • I tried to use your code. But I see that the define you are using are for C use, not for assembler. If you create a simple C function like this: `void setStackPointer() { SPL = (RAMEND & 0xFF); SPH = (RAMEND >> 8); }` you'll obtain what you want! – Sir Jo Black Dec 06 '17 at 11:33
  • I subtracted nothing from RAMEND value because the stack (I think) should be set at the maximum value of the memory to use! – Sir Jo Black Dec 06 '17 at 11:34
  • I think that is better you study your MCU manual. It's since a lot of time I don't use AVRs. But, despite, I wait for you to tell me which's the MCU you are using! – Sir Jo Black Dec 06 '17 at 11:43
  • The SPL macro is something like `#define SPL _SFR_IO8(0x3D)` then it's not good to be compiled with avr-as; it's written for a C compiler. If you compile the C lines I've indicated above using `avr-gcc -c -mmcu=whatsyourmcu prgname` you obtain prgname.o. You may disassemble it using avr-objdump -S prgname.o and so you can see the assembler code for your MCU. – Sir Jo Black Dec 06 '17 at 15:47

1 Answers1

2

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.

Edgar Bonet
  • 3,416
  • 15
  • 18
  • I don't know if the MCU @shark38j is using is atmega328p! But I think your explanation should be good for a lot of AVR. – Sir Jo Black Dec 07 '17 at 13:41