汇编指令-位置无关码(BL)与绝对位置码(LDR)(2)
位置无关码
即该段代码无论放在内存的哪个地址,都能正确运行。究其原因,是因为代码里没有使用绝对地址,都是相对地址。
位置相关码
即它的地址与代码处于的位置相关,是绝对地址
BL :带链接分支跳转指令,也是位置无关码(相对位置),用于调用函数用的。
B:分支跳转指令,指目标不能太远,一般用于同一个文件下的目标地址跳转。
LDR:通常都是作加载指令的,但是它也可以作伪指令,通常有两种不同的表示:
1)LDR pc, =MyHandleIRQ 表示将MyHandleIRQ地址放入pc寄存器中,相当于PC=MyHandleIRQ 。
1. LDR r0,=label //用于加载立即数或一个地址值到指定寄存器中
//如果label是立即数: LDR r0,=0X123 ;将0X123存入r0中
//如果name是个标识符: LDR r0,=label_1 ;将label_1所指向的地址值存入r0中
例如:
- LDR r0,[r1] //将R1中的值存到r0中
- LDR r1,[r2,#16] //将(r2+16)地址中的内容存到r1中
- LDR r1,[r2],#4 //将r2地址中的内容存到r1中,同时r2=r2+4
1 Reset:
2 ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈
3 bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
4 // bl是位置无关码,相当于:PCnew = PC + 偏移
5 // PCnew = (4+8) + 0x28 = 0x34
6
7 //ldr pc, =disable_watch_dog /* 这样写将出错 */
8
9 bl clock_init @ 设置MPLL,改变FCLK、HCLK、PCLK
10 bl memsetup @ 设置存储控制器以使用SDRAM
11 bl copy_steppingstone_to_sdram @ 复制代码到SDRAM中
12 ldr pc, =on_sdram @ 跳到SDRAM中继续执行
13 on_sdram:
14 ldr sp, =0x34000000 @ 设置栈指针
15 ldr lr, =halt_loop @ 设置返回地址
16 ldr pc, =main @ 调用main函数
17 halt_loop:
18 b halt_loop
链接脚本如下,链接地址在0X30000000:
1 SECTIONS {
2 . = 0x30000000;
3 .text : { *(.text) }
4 .rodata ALIGN(4) : {*(.rodata)}
5 .data ALIGN(4) : { *(.data) }
6 .bss ALIGN(4) : { *(.bss) *(COMMON) }
7 }
反汇编如下:
1 30000000 <_start>: 2 30000000: e3a0da01 mov sp, #4096 ; 0x1000 3 30000004: eb00000a bl 30000034 <disable_watch_dog> 4 30000008: eb00000d bl 30000044 <clock_init> 5 3000000c: eb000026 bl 300000ac <memsetup> 6 30000010: eb000040 bl 30000118 <copy_steppingstone_to_sdram> 7 30000014: e59ff00c ldr pc, [pc, #12] ; 30000028 <.text+0x28> 8 9 30000018 <on_sdram>: 10 30000018: e3a0d30d mov sp, #872415232 ; 0x34000000 11 3000001c: e59fe008 ldr lr, [pc, #8] ; 3000002c <.text+0x2c> 12 30000020: e59ff008 ldr pc, [pc, #8] ; 30000030 <.text+0x30> 13 14 30000024 <halt_loop>: 15 30000024: eafffffe b 30000024 <halt_loop> 16 30000028: 30000018 andcc r0, r0, r8, lsl r0 17 3000002c: 30000024 andcc r0, r0, r4, lsr #32 18 30000030: 30000200 andcc r0, r0, r0, lsl #4 19 20 30000034 <disable_watch_dog>:
... ...
从反汇编中可以看出当执行ldr pc, =on_sdram 时的反汇编是 ldr pc, [pc, #12] ; 相当于pc=*(pc+12)=30000018,此时的*(pc+12)是指的pc+12地址所指的地址,所以无论pc怎么变都是指的30000018这个常量来执行on_sdram,属于绝对转移。
执行 bl disable_watch_dog 时,地址0X30000004跳转到0X30000034.这里的0X30000034是通过机器码算出来的,机器码格式如下图所示:
其中[31:28]位是条件码;[27:24]位为“1010”(0xeaffffff)时,表示B跳转指令,为“1011”时,表示BL跳转指令;[23:0]表示偏移地址。
从反汇编中可以看到 bl disable_watch_dog 的机器码是eb00000a ,二进制为1110 1011 000000000000000000001010。
其中1110表示无条件执行,接下的1011就是BL指令,如L==0则就表示B指令,剩下的Offset就是链接位。
BL指令的跳转地址计算:
1.如上图所示,先将24位Offset补码左移两位,得到000000000000000000001010 00 =0X28
2.由于ARM流水线,当前PC永远等于PC+8,所以PC=PC+0X28+8=0X30000004+0X28+8=0X30000034。
若这里的PC值为其它值,算出来的转移地址也会随之改变,所以BL指令为地址无关码,跳转地址与位置无关。
注:ARM9是3级流水线,也就是PC处理时正在执行第1条指令的同时对第2条指令进行译码,并将第3条指令从存储器中取出,如下图所示,PC总是指向第3条指令取值的地方。
人间有真情,人间有真爱。