9.5.2.3 Relocatable Expression Modifiers

The assembler supports several modifiers when using relocatable addresses in AVR instruction operands. The general syntax is the following:

modifier(relocatable-expression)

When the argument of a modifier is not computable at assemble time, then the assembler has to encode the expression in an abstract form using some target-specific reloc. The consequence is that only a very limited number of argument expressions is supported when they are not computable at assemble time.

lo8

Bits 0 through 7 of an expression as an 8-bit relocatable expression.

hi8

Bits 8 through 15 of an expression as an 8-bit relocatable expression. This is useful with instructions that have an immediate operand, for example:

;; my_array is an array of 8-bit values in RAM.
;; Load the N-th element to R24 where the 16-bit
;; value N is provided in the Z register (r30 and r31).
subi r30, lo8(-(my_array))
sbci r31, hi8(-(my_array))
ld   r24, Z
hlo8
hh8

Bits 16 through 23 of an expression as an 8-bit relocatable expression.

hhi8

Bits 24 through 31 of an expression as an 8-bit relocatable expression.

gs

A function address divided by 2 in order to get a word address. Generate a stub (linker trampoline) as needed. This is required to handle function pointers on devices with more than 128 KiB of program memory without the need for function pointers wider than 16 bits. For the rationale, see the GCC documentation.

On devices with less program memory, gs() behaves like pm().

The following example loads a 16-bit address related to ‘func’ and calls it:

;; Invoke func by means of an indirect call.
ldi r30, lo8(gs(func))
ldi r31, hi8(gs(func))
#idef __AVR_HAVE_EIJMP_EICALL__
   eicall
#else
   icall
#endif

This will always be a 16-bit address, even on devices with a 3-byte PC. With a 3-byte PC and when the target address is not reachable by an EICALL, the linker will generate a stub with a single ‘JMP func’ instruction that is reachable by EIND. gs() evaluates to the lower 16 bits of the word address of that stub.

Notice that EIND is set by the startup code and never read or changed by the compiler. The default linker script locates the stubs such that they are in the reach of EIND as set by the startup code, so that the code above works in all situations and without the need to touch EIND.

pm_lo8

Bits 1 through 8 of an expression as an 8-bit relocatable expression. This modifier is useful for accessing code from program memory with a word address.

pm_hi8

Bits 9 through 16 of an expression as an 8-bit relocatable expression. This modifier is useful for accessing code from program memory with a word address.

pm_hh8

Bits 17 through 24 of an expression as an 8-bit relocatable expression. This modifier is useful for accessing code from program memory with a word address.

The code below performs an indirect call by hand to function ‘func’ using the following steps:

  1. Put the word address of the ‘1:’ label on the stack. This is the return address.
  2. Put the word address of ‘func’ on the stack.
  3. Invoke ‘func’ by means of a RET instruction.
  4. The RET instruction at the end of ‘func’ will jump to the ‘1:’ label.
;; Push the word address of the return location.
ldi r24, pm_lo8(1f)  $  push r24
ldi r24, pm_hi8(1f)  $  push r24
#ifdef __AVR_3_BYTE_PC__
ldi r24, pm_hh8(1f)  $  push r24
#endif

;; Push the word address of func using alternative syntax.
ldi r24, lo8(pm(func))  $  push r24
ldi r24, hi8(pm(func))  $  push r24
#ifdef __AVR_3_BYTE_PC__
ldi r24, hh8(pm(func))  $  push r24
#endif

;; Indirect jump to the word address on the stack
ret

;; The location to which func will return.
1: