I am trying to write some conditional jumps in AVR assembly using AVR-gcc. According to AVR instruction set manual, the brxx
instructions take in an operand k
, and jumps to PC+k+1
. Also, according to the tutorial PDF from http://www.avrbeginners.net/new/tutorials/jumps-calls-and-the-stack/, I should be able to use the PC
operand to jump like this:
brne PC+2
However, when I write such test code:
#include <avr/io.h>
.section .text
.global main ; Note [5]
main:
sbi _SFR_IO_ADDR(DDRA), PA0
sbi _SFR_IO_ADDR(PORTA), PA0
ldi 16, 0xFF
cpi 16, 0xFF
breq PC + 2
cbi _SFR_IO_ADDR(PORTA), PA0
rjmp end
end:
rjmp end
I get this error:
avr-gcc -mmcu="atmega16" -DF_CPU="16000000UL" -O0 main.S -o main.o
/tmp/ccAa2ySf.o: In function `main':
(.text+0x8): undefined reference to `PC'
collect2: ld returned 1 exit status
make: *** [main.o] Error 1
Apparently PC is not defined in AVR-libc. Then how am I going to do such condition branch? Thanks!
Update 1
I found this question How can I jump relative to the PC using the gnu assembler for AVR? and found that the syntax for gnu as is breq .+2
. However, I get the same error as that question have. When I disassemble using avr-objdump -d main.o
, I do get
74: 01 f0 breq .+0 ; 0x76
Which is the same symptom as that question. I will try using linker script, but I have no experience in that.
Update 2
Actually I found that if I use even numbers in the breq instruction, like breq .+2
or breq .+4
, the objdump shows correct result. However, if I use odd numbers, it will become breq .+0
. Can someone explain why?
OK, the answer is totally rewritten now. This is what I understand from the objdump
of compiled C codes. Firstly, binutils
uses byte addressing, not word addressing, for the program counter, and starts at the instruction right after the current one. This is explained in the following code:
#include <avr/io.h>
.section .text
.global main
main:
sbi _SFR_IO_ADDR(DDRA), PA0
sbi _SFR_IO_ADDR(PORTA), PA0
ldi 16, 0xFF
cpi 16, 0xFF
breq .+4 ;; If we are executing here
cbi _SFR_IO_ADDR(PORTA), PA0 ;; This is .+0, will be skipped
cbi _SFR_IO_ADDR(PORTA), PA0 ;; This is .+2, will be skipped
cbi _SFR_IO_ADDR(PORTA), PA0 ;; This is .+4, which will be executed
rjmp end
end:
rjmp end
Apparently, the PC width has nothing to do with relative address. It only affects the maximum PC value, either 0xFF or 0xFFF, so no matter what AVR platform I am compiling for, binutils
uses two bytes for an instruction.
P.S. I think, if the only way I can know how a compiler works is to observe how it works, probably that means poor documentation? Or maybe I just don't know when to start. If someone see this, could you help pointing some useful books about 'this kind of things'? (I don't even know how to describe it) Thanks!