2.7.1. Direct loading with ADR and ADRL

The ADR and ADRL pseudo-instructions enable you to generate an address, within a certain range, without performing a data load. ADR and ADRL accept either of the following:

The assembler converts an ADR rn,label pseudo-instruction by generating:

The offset range is ±255 bytes for an offset to a non word-aligned address, and ±1020 bytes (255 words) for an offset to a word-aligned address. (For Thumb, the address must be word aligned, and the offset must be positive.)

The assembler converts an ADRL rn,label pseudo-instruction by generating:

The range of an ADRL pseudo-instruction is ±64KB for a non word-aligned address and ±256KB for a word-aligned address. (There is no ADRL pseudo-instruction for Thumb.)

ADRL assembles to two instructions, if successful. The assembler generates two instructions even if the address could be loaded in a single instruction.

Refer to Loading addresses with LDR Rd, = label for information on loading addresses that are outside the range of the ADRL pseudo-instruction.

Note

The label used with ADR or ADRL must be within the same code section. The assembler faults references to labels that are out of range in the same section. The linker faults references to labels that are out of range in other code sections.

In Thumb state, ADR can generate word-aligned addresses only.

ADRL is not available in Thumb code. Use it only in ARM code.

Example 2.6 shows the type of code generated by the assembler when assembling ADR and ADRL pseudo-instructions. It is supplied as adrlabel.s in the examples\asm subdirectory of the ADS. Refer to Code examples for instructions on how to assemble, link, and execute the example.

The instructions listed in the comments are the ARM instructions generated by the assembler.

Example 2.6. 

            AREA    adrlabel, CODE,READONLY
            ENTRY                          ; Mark first instruction to execute
Start
            BL      func                   ; Branch to subroutine
stop        MOV     r0, #0x18              ; angel_SWIreason_ReportException
            LDR     r1, =0x20026           ; ADP_Stopped_ApplicationExit
            SWI     0x123456               ; ARM semihosting SWI
            LTORG                          ; Create a literal pool
func        ADR     r0, Start              ; => SUB r0, PC, #offset to Start
            ADR     r1, DataArea           ; => ADD r1, PC, #offset to DataArea
            ; ADR   r2, DataArea+4300      ; This would fail because the offset
                                           ; cannot be expressed by operand2
                                           ; of an ADD
            ADRL    r2, DataArea+4300      ; => ADD r2, PC, #offset1
                                           ;    ADD r2, r2, #offset2
            MOV     pc, lr                 ; Return
DataArea    SPACE   8000                   ; Starting at the current location,
                                           ; clears a 8000 byte area of memory
                                           ; to zero
            END

Implementing a jump table with ADR

Example 2.7 shows ARM code that implements a jump table. It is supplied as jump.s in the examples\asm subdirectory of ADS. Refer to Code examples for instructions on how to assemble, link, and execute the example.

The ADR pseudo-instruction loads the address of the jump table.

In the example, the function arithfunc takes three arguments and returns a result in r0. The first argument determines which operation is carried out on the second and third arguments:

argument1=0

Result = argument2 + argument3.

argument1=1

Result = argument2 – argument3.

The jump table is implemented with the following instructions and assembler directives:

EQU

Is an assembler directive. It is used to give a value to a symbol. In this example it assigns the value 2 to num. When num is used elsewhere in the code, the value 2 is substituted. Using EQU in this way is similar to using #define to define a constant in C.

DCD

Declares one or more words of store. In this example each DCD stores the address of a routine that handles a particular clause of the jump table.

LDR

The LDR pc,[r3,r0,LSL#2] instruction loads the address of the required clause of the jump table into the pc. It:

  • multiplies the clause number in r0 by 4 to give a word offset

  • adds the result to the address of the jump table

  • loads the contents of the combined address into the program counter.

Example 2.7.  ARM code jump table

        AREA    Jump, CODE, READONLY     ; Name this block of code
        CODE32                           ; Following code is ARM code
num     EQU     2                        ; Number of entries in jump table
        ENTRY                            ; Mark first instruction to execute
start                                    ; First instruction to call
        MOV     r0, #0                   ; Set up the three parameters
        MOV     r1, #3
        MOV     r2, #2
        BL      arithfunc                ; Call the function
stop    MOV     r0, #0x18                ; angel_SWIreason_ReportException
        LDR     r1, =0x20026             ; ADP_Stopped_ApplicationExit
        SWI     0x123456                 ; ARM semihosting SWI
arithfunc                                ; Label the function
        CMP     r0, #num                 ; Treat function code as unsigned integer
        MOVHS   pc, lr                   ; If code is >= num then simply return
        ADR     r3, JumpTable            ; Load address of jump table
        LDR     pc, [r3,r0,LSL#2]        ; Jump to the appropriate routine
JumpTable
        DCD     DoAdd
        DCD     DoSub
DoAdd   ADD     r0, r1, r2               ; Operation 0
        MOV     pc, lr                   ; Return
DoSub   SUB     r0, r1, r2               ; Operation 1
        MOV     pc, lr                   ; Return
        END                              ; Mark the end of this file

Converting to Thumb

Example 2.8 shows the implementation of the jump table converted to Thumb code.

Most of the Thumb version is the same as the ARM code. The differences are commented in the Thumb version.

In Thumb state, you cannot:

  • increment the base register of LDR and STR instructions

  • load a value into the pc using an LDR instruction

  • do an inline shift of a value held in a register.

Example 2.8.  Thumb code jump table

        AREA    Jump, CODE, READONLY
        CODE16                           ; Following code is Thumb code
num     EQU     2
        ENTRY
start
        MOV     r0, #0
        MOV     r1, #3
        MOV     r2, #2
        BL      arithfunc
stop    MOV     r0, #0x18
        LDR     r1, =0x20026
        SWI     0xAB                     ; Thumb semihosting SWI
arithfunc
        CMP     r0, #num
        BHS     exit                     ; MOV pc, lr cannot be conditional
        ADR     r3, JumpTable
        LSL     r0, r0, #2               ; 3 instructions needed to replace
        LDR     r0, [r3,r0]              ; LDR pc, [r3,r0,LSL#2]
        MOV     pc, r0
        ALIGN                            ; Ensure that the table is aligned on a
                                         ; 4-byte boundary
JumpTable
        DCD     DoAdd
        DCD     DoSub
DoAdd   ADD     r0, r1, r2
exit    MOV     pc, lr
DoSub   SUB     r0, r1, r2
        MOV     pc, lr
        END
Copyright © 2000, 2001 ARM Limited. All rights reserved.ARM DUI 0068B
Non-Confidential