Assembler doesn't recognize correct value for PORTx in e.g. 'sbi PORTD, PD7'

I’m writing an assembly ‘library’ for a new application (I’ve written them before), but I guess this is the first time I’ve used sbi, sbic, sbis, etc in one. If I write

sbi PORTD, PD7

the assembler complains with

Error: number must be positive and less than 32

If I replace PORTD with its I/O address 0x0b, everything assembles OK. So it recognizes the bit numbers (PD7), but gets the value for PORTD wrong. I would suspect that it may be using its memory-space address (0x2b) instead of the I/O address.

I’m using Arduino v1.0.3 and an EtherMega board (ATmega2560).

In my library .S file I have

#include <avr/io.h>

I would expect this to be a common complaint, but I can’t seem to get a handle on it with google and forum searches.

Try this...

    sbi _SFR_IO_ADDR(PORTD), PD7

Further information:

If I insert a (junk) line in my assembly file

ldi r16, PORTD

and then compile the sketch (and 'library') it assembles with a value of 0x2b for PORTD

144: 0b e2 ldi r16, 0x2B ; 43

Now port and bit values are #defined in iomxx0_1.h, which is #included by iom2560.h e.g.

#define PORTD _SFR_IO8(0x0B)
#define PD7 7
#define PD6 6
#define PD5 5

To confirm that this is where the values are really coming from, I altered the #define of PD7 to have a value of 3. I then recompiled my sketch, and the line

sbi 0x0b, PD7 then compiled as

142: 5b 9a sbi 0x0b, 3 ; 11

From this it would appear that the assembler thinks that PORTD has a value of 0x2b, despite the values contained in iomxx0_1.h

Where is the problem??

Ah,... thanks Coding Badly. I didn't see your post until after my second post.

So, does this need to be applied to all I/O register addresses (up to 0x3f) when I need them to be treated as I/O addresses?

If I want to access one of these registers via its memory-space address (e.g. lds r16, xxx) then I simply use

lds r16, PORTD

Correct??

Can you point me to where I find all this documented, please?

Regards,

roger99:
So, does this need to be applied to all I/O register addresses (up to 0x3f) when I need them to be treated as I/O addresses?

http://www.nongnu.org/avr-libc/user-manual/group__avr__sfr__notes.html

Apparently, ASSEMBLER (appears to actually be ASSEMBLER in the header file) needs to be defined before including io.h.

If I want to access one of these registers via its memory-space address (e.g. lds r16, xxx) then I simply use "lds r16, PORTD" Correct??

I have no idea. I have only done inline assembly.

Can you point me to where I find all this documented, please?

Other than Google, no.

https://www.google.com/search?q=gcc+avr+assembly
http://www.nongnu.org/avr-libc/user-manual/assembler.html

Thanks mate, that looks good.

I'm having this same problem, but I'm using PORTL.
It complains in these two places:

sbi _SFR_IO_ADDR(DDRL), 0
sbi _SFR_IO_ADDR(PORTL), 0

Inside iomxx0_1.h, I see these:

# define DDRL   _SFR_MEM8(0x10A)
# define PORTL  _SFR_MEM8(0x10B)

Which is more than 32.
How can I make this work?

I believe you have to use memory access instructions (lds and sts).

Yep. SBI and CBI can only access the ports whose address is lower than 0x1f - on a 2560 that includes portA through portG. The “in” and “out” instruction can access address below 0x3F (there are no GPIO ports between 1F and 3F)
The “high” ports like portL can only be access with load/store instructions, which means that the closest you can get to “sbi” is a sequence that uses 5 clock cycles and a temporary register:

  ldd reg, PORTL
  ori reg, (1<<bit)
  std PORTL, reg

A good reason to program in C, instead of assembler :slight_smile:

Thanks!!!
Yeah, I was just trying to reuse some source code from someone else that had a .S code, but I was able to move the relevant part to the C side and it worked the same.