1、这两条指令经常用标号作为操作数,如adr r0,label和ldr r0,label。adr指令是位置相关码,把基于pc相对偏移的地址值读取到寄存器r0中,因此寄存器r0=标号label与该指令的距离差+该指令当前实际运行的地址,ldr指令中把label当成一个指针,把指针所指向的值读入到r0中。
举例:U-boot中有一段重定位代码,意在将Nor或者Nand中启动的代码拷贝到SDRAM中去执行,
.globl _start
b reset
pc, _undefined_instruction
pc, _software_interrupt
pc, _prefetch_abort
pc, _data_abort
pc, _not_used
pc, _irq
pc, _fiq
_TEXT_BASE: /* *_TEXT_BASE=TEXT_BASE */
.word TEXT_BASE
.globl _armboot_start
_armboot_start: /* *_armboot_start=_start */
.word _start
relocate:/* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code,读取_start标号的地址,即程序实际开始运行的地址到r0,如果u-boot是烧写到nor或者nand中运行的话,那么程序从0地址开始运行,r0=0,如果u-boot是通过下载器下载到内存中运行的话,那么程序从内存TEXT_BASE(代码的链接地址,在board/$(BOARDDIR)/config.mk中指定,smdk2410开发板中TEXT_BASE=0x33f80000)开始运行,r0=0x33f80000。 */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM。读取_TEXT_BASE标号(指针)所指向的值到r1中,即r1=*_TEXT_BASE,由于第10行指定了_TEXT_BASE标号(指针)处存放的值为TEXT_BASE,因此r1=0x33f80000 */
cmp r0, r1 /* don't reloc during debug 。比较r0是否等于r1,如果相等,说明代码是从SDRAM中开始运行的,一般来说是通过下载器下载到内存中运行,那么就不需要进行clear_bas操作。反之则说明是从flash启动。 */
beq clear_bss
ldr r2, _armboot_start /* 读取_armboot_start标号(指针)所指向的值到r2中,即r2=*_armboot_start,而由第13行可知*_armboot_start=_start ,再根据第2行可知_start=(代码开始运行的地址,0x00000000),,因此r2=(代码开始运行的地址,0x00000000).</span>*/
ldr r3, _bss_start /* 读取*_bss_start的值到r3中,而_bss_start在连接脚本(board/$(BOARDDIR)/U-Boot.lds)中指定,它代表着bss段的开始地址,它之前的数据为代码段和数据段,因此r3=(bss段之前的所有段的结束地址). </span>*/
sub r2, r3, r2 /* r2=r3-r2得到bss之前的所有段的长度 */
add r2, r0, r2 /* r2=r2+r0,由于r0=(程序实际开始运行的地址),因此r2=(bss段开始的实际地址。后续代码是将r0开始的数据拷贝到r1开始的地址,直到r0==r2*/
copy_loop:
ldmia r0!, {r3-r10}/* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop、
loop:
ldmia r0!, {r3-r10}/* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmpr0, r2 /* until source end addreee [r2] */
ble copy_loop
2、注意还有ldr伪指令,其一般形式为ldr r0,=label,那么r0=(label应该位于的地址,而非实际地址),这里所谓应该位于的地址是跟链接地址有关的,属于位置无关码,如果链接地址为_TEXT_BASE,那么r0=(label相对于程序开头的偏移+_TEXT_BASE),这个值与程序是在SDRAM中运行还是在flash运行是无关的。
举例:U-boot中有一段初始化SDRAM的代码,lowlevel_init函数,代码如下:
lowlevel_init:
/* memory control configuration */
/* make r0 relative the current location so that it */
/* reads SMRDATA out of FLASH rather than memory ! */
ldr r0, =SMRDATA
ldr r1, _TEXT_BASE
sub r0, r0, r1
ldr r1, =BWSCON/* Bus Width Status Controller */
add r2, r0, #13*4
0:
ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0<span style="white-space:pre"> </span>
bne 0b
/* everything is fine now */
mov pc, lr
.ltorg
/* the literal pools origin */
SMRDATA: //13个寄存器的值
.work ....
上面的代码中ldr r0, =SMRDATA,将SMRDATA标号应该位于的地址赋给r0,r0=(SMRDATA相对于程序开头的偏移量+TEXT_BASE),然后通过sub r0,r0,r1指令使得r0=(SMRDATA相对于程序开头的偏移量),即r0保存了13个寄存器值在flash上的开始地址,最后把SMRDATA处的数据拷贝到BWSCON开始的内存处,进行SDRAM的初始化。这里为什么不用adr r0,SMRDATA,让r0=(SMRDATA标号后指令运行的实际地址)呢?原因在于SMRDATA后面保存的是数据,它们位于专门的数据段,而adr只能支持小范围的跳转,要跳转到数据段,需要ldr这种可大范围跳转的指令。