; *********************************************************************** ; * * ; * Pseudo-instructions for the ARM * ; * * ; *********************************************************************** ; Author: John Zaitseff ; Date: 25th February, 2003 ; Version: 1.4 ; This program shows the pseudo-instructions "nop", "ldr =", "adr" and ; "adrl" for the ARM microcontroller in action. It also illustrates using ; multiple ".ltorg" directives. Notice that the symbol "->" is used to ; mean "translates to". .text .global _start _start: nop ; Do nothing: actually "mov r0,r0" bl sub1 ; Call the first subroutine: "ldr =" bl sub2 ; Second subroutine: "ldr =" bl sub3 ; Third subroutine: "ldr" and "adr" bl sub4 ; Fourth subroutine: "ldr =" with labels bl sub5 ; Fifth subroutine: more "adr" and "adrl" exit: swi 0x11 ; Terminate the program ; Subroutine 1: Demonstrate the "ldr =" pseudo-instruction sub1: ldr r0,=42 ; -> mov r0,#42 ldr r1,=-42 ; -> mvn r1,#41 ldr r2,=0x01234567 ; -> ldr r2,[pc,#4] (offset to 1st constant) ldr r3,=0x89ABCDEF ; -> ldr r2,[pc,#4] (offset to 2nd constant) mov pc,lr ; Return from the subroutine .ltorg ; Literal pool #1 ; The literal pool created above contains two constants. The equivalent ; assembler language code would have been: ; .word 0x01234567 ; .word 0x89ABCDEF ; You can check this by disassembling the executable. To do this, type: ; make pseudo.elf ; arm-elf-objdump -d pseudo.elf ; Subroutine 2: Demonstrate a second literal pool sub2: ldr r0,=0x01234567 ; -> ldr r0,[pc,#4] ldr r1,=0x5A5A5A5A ; -> ldr r1,[pc,#4] mov pc,lr .ltorg ; Literal pool #2 ; The second literal pool contains 0x01234567 and 0x5A5A5A5A. Whenever ; the GNU Assembler sees a ".ltorg" directive, it dumps the constants it ; knows about and then promptly forgets that these have been used; hence ; 0x01234567 appears in both the first and second literal pool. ; Subroutine 3: Demonstrate "ldr" and "adr" with labels sub3: ldr r0,var1 ; Load value stored in var1 (0xAAAA5555) ; -> ldr r0,[pc,#32] (offset to var1) ; The above form of "ldr" (with no "=") can be used to load values stored ; in the ".text" section. Technically, this should only be used for ; constants, as ".text" is only meant for code and read-only data adr r1,var1 ; Load address of var1 into R1 ; -> add r1,pc,#28 ldr r2,[r1] ; then load the value at that address ; The "adr" pseudo-instruction (and its cousin "adrl") can be used to load ; the ADDRESS instead of the value of a location. Again, it can only be ; used for those "variables" stored in the ".text" section. ldr r3,=var1 ; Load value of address of var1 into R3 ; -> ldr r3,[pc,#24] (literal pool #3) ldr r4,[r3] ; then load the value at that address ; The above form of "ldr =" can be used to load values stored in either ; the ".text" OR the ".data" (or any other) section. The GNU Assembler ; places the address in a literal pool, which gets loaded into R3. adr r5,tbl1 ; R5 := address of tbl1 ; -> add r5,pc,#20 adr r6,tbl1+200 ; R6 := address of 200'th byte in tbl1 ; -> add r6,pc,#216 ; adr r7,tbl1+2000 ; Offset is too large for "adr" adrl r7,tbl1+2000 ; R7 := address of 2000'th byte in tbl1 ; -> add r7,pc,#220 ; -> add r7,r7,#1792 mov pc,lr var1: .word 0xAAAA5555 ; Value stored in the ".text" section .ltorg ; Literal pool 3 ; Literal pool 3 contains the address of "var1". You can confirm this by ; disassembling the executable. tbl1: .skip 8192 ; A table of 8192 bytes; a real table would ; be composed of ".word" directives... ; Subroutine 4: "ldr =" with labels sub4: ; ldr r0,var2 ; "var2" is not in the ".text" section ; adr r1,var2 ; "var2" is not in the ".text" section ; If you uncomment the previous two lines and recompile the program (by ; typing "make"), you will get two rather cryptic error messages: ; Error: internal_relocation (type 234) not fixed up (OFFSET_IMM) ; Error: internal relocation (type 232) not fixed up (IMMEDIATE) ldr r2,=var2 ; R2 := address of "var2" ; -> ldr r2,[pc,#4] ldr r0,[r2] ; Load the value (0x5555AAAA) from address mov pc,lr ; The GNU Assembler places the fourth literal pool here (just before the ; switch to the ".data" section). The pool contains the address of ; "var2". .data ; Make "var2" appear in the ".data" section .align ; Align on a word boundary var2: .word 0x5555AAAA ; Value of "var2", in read/write memory .text ; Switch back to the ".text" section... ; Subroutine 5: More "adr" and "adrl" sub5: ; adr r0,_start ; The offset is too large to be assembled adrl r0,_start ; R0 := address of instruction at "_start" ; -> sub r0,pc,#148 and ; sub r0,r0,#8192 ; If you remove the comment character from the first of the above lines ; and recompile, you will get the following error message: ; Error: invalid constant (ffffdf6c) after fixup adrl r1,sub5 ; R1 := address of this subroutine ; -> sub r1,pc,#16 and ; nop ; The "adr" pseudo-instruction is always replaced by ONE real instruction. ; The "adrl" pseudo-instruction is always replaced by TWO real instructions. this: adr r2,this ; R2 := address of this instruction ; -> sub r2,pc,#8 adr r3, . ; R3 := address of THIS instruction ; -> sub r3,pc,#8 ; "." represents "the current address" mov pc,lr .end