请教:
(1)LDR R1,= 0x12345678 ;加载 32 位立即数
LDR R1,0x12345678
有什么不相同啊?
一般在什么情况下用 LDR R1,= 0x12345678 ?
在什么情况下用 LDR R1,0x12345678 ?
(2)LDR R0,=LED_TAB ;加载标号地址
LDR R0,LED_TAB
有什么不相同啊?
一般在什么情况下用 LDR R0,=LED_TAB ?
在什么情况下用 LDR R0,LED_TAB ?
====================================================
答:
1. 没有“LDR R1,0x12345678 ”。
2. 以下两条指令是一样的,都被称为“伪指令”,就是说编译器会把这条指令替换成其他合适的指令。
LDR R1, =0x12345678
LDR R0, =LED_TAB
如果这些数值不太复杂,那么就会用mov指令代替,比如:
ldr r1, =0x00
在编译时就会变成:
mov r1, #0x00
如果这些数值很复杂,那么编译时,这个数值会被保存在某个地方,然后使用读内存的指令进行读取,比如:
LDR R1, =0x12345678
在编译时变成:
ldr r1, [pc, xxxxx] // 这个xxx与pc相加,刚好就是some_locate的地址──编译器会帮你做好这一切
some_locate: .word 0x12345678
3. 你说“LDR R0,=LED_TAB ;加载标号地址 ”,没错,
LED_TAB是个地址标号,就是一个数值而已,编译器连接程序时会确定它的值,如果它很简单,就会使用mov指令赋值;如果很复杂,就存在某个地址,然后用读内存的指令读出。
4. LDR R0,LED_TAB
没有“=”号,它表示“读内存”.
比如:
LDR R0,LED_TAB
LDR R1, =LED_TAB
LED_TAB: .work 0x12345678
R0的值是0x12345678,R1的值是LED_TAB标号值,就是0x12345678在内存中存放的地址
adr是将基于PC相对偏移的地址值或基于寄存器相对地址值读取的为指令,而ldr用于加载32为立即数或一个地址到指定的寄存器中。到这儿就会看到其中的区别了。如果在程序中想加载某个函数或者某个在联接时候指定的地址时请使用adr,例如在lds中需要重新定位的地址。当加载32为的立即数或外部地址时请用ldr。
下面是
ldr r0,_start
ldr r1,_TEXT_BASE
ldr r2,_armboot_start
ldr r3,_bss_start
sub r2,r3,r2
add r2,r2,r0
和
adr r0,_start
ldr r1,_TEXT_BASE
adr r2,_armboot_start
adr r3,_bss_start
sub r2,r3,r2
add r2,r2,r0
两个片段的反汇编代码
80000068: e51f0070 ldr r0, [pc, #ffffff90] ; 80000000 <_start>
8000006c: e51f1054 ldr r1, [pc, #ffffffac] ; 80000020 <_TEXT_BASE>
80000070: e51f2054 ldr r2, [pc, #ffffffac] ; 80000024 <_armboot_start>
80000074: e51f3054 ldr r3, [pc, #ffffffac] ; 80000028 <_bss_start>
80000078: e0432002 sub r2, r3, r2
8000007c: e0822000 add r2, r2, r0
--------------------------------------------------------------------------
80000068: e24f0070 sub r0, pc, #112 ; 0x70
8000006c: e24f1054 sub r1, pc, #84 ; 0x54
80000070: e24f2054 sub r2, pc, #84 ; 0x54
80000074: e24f3054 sub r3, pc, #84 ; 0x54
80000078: e0432002 sub r2, r3, r2
8000007c: e0822000 add r2, r2, r0
由于ldr是读取的32为地址,因此会自动用fffff来填充,可是用PC相减的时候就不知道减到哪儿去了。