新闻中心

EEPW首页>嵌入式系统>设计应用> arm汇编中的跳转指令

arm汇编中的跳转指令

作者: 时间:2016-11-20 来源:网络 收藏
ARM汇编中,常有两种跳转方法:b跳转指令、ldr指令向PC赋值。
我自己经过归纳如下:
(1)b label
该 指令完成的操作是pc<-label,将label处的地址传给pc。b跳转指令是相对跳转,依赖当前PC的值,偏移量是通过该指令本身的 bit[23:0]算出来的,这使得使用b指令的程序不依赖于要跳到的代码的位置,只看指令本身。即该分支指令的二进制码的后24位的实际的值是相对当前 的 R15 的值的一个偏移量;而不是一个绝对地址。它的值由汇编器来计算,它是 24 位有符号数,左移两位后有符号扩展为 32 位,表示的有效偏移为 26 位(+/- 32 M)。
(2)ldr pc, =label
该指令是一条伪指令,将内存中的某个数据的位置(label处)赋给PC,同样依赖当前PC的值,但是偏移量是那个位置(label)的连接地址(运行时的地址),所以可以用它实现从Flash到RAM的程序跳转,说白了,pc是个地址数值。
伪指令LDR 常用于加载芯片外围功能部件的寄存器地址(32 位立即数),以实现各种
控制操作。
如:ldr r0,=5e000000 ;将外围某IO端口寄存器的地址赋给r0,注意该立即数前面没有#。
---------------------------------------------------------------------------
这里讲一下为什么会有ldr 伪指令

范例demo.s:

. equSTACK_BASE,0x0c002000

.equSTACK_SIZE,0x00001000


.text
ldrsp,=STACK_BASE
ldrsl,=STACK_BASE-STACK_SIZE
ldrpc,=entry

这是一个合法的汇编文件,它把堆栈基址设为0x0c002000,栈限设为0x0c001000,然后跳到entry所标识的C程序中执行。

下面我们假设符号“entry”的地址为0x0c000000。

我们如果把上面代码写成:
.text
movsp,#0x0c002000
movsl,#0x0c001000
movpc,#0x0c000000
汇编器会报错:
demo.s: Assembler messages:
demo.s:2: Error: invalid constant -- `mov sp,#0x0c002000
demo.s:3: Error: invalid constant -- `mov sl,#0x0c001000

说起这个错误的原因可就话长了,简而言之是因为RISC有一个重要的概念就是所有指令等长。在ARM指令集中,所有指令长度为4字节(Thumb指令是2 字节)。那问题就来了,4字节是不可能同时存的下指令控制码和32位立即数的,那么我要把一个32位立即数(比如一个32位地址值)传送给寄存器该怎么 办?
RISC CPU提供一个通用的方法就是把地址值作为数据而不是代码,从存储器中相应的位置读入到寄存器中。像在代码一中,将所有读取的32位数据放到label标 注的内存地址中,使用ldr伪指令,从该内存处读出该数据。因此label相当于一个内存地址。如下,给出了代码一的反汇编代码:让我们在Linux环境下执行下面的命令:
arm-elf-as -o demo.o demo.s
arm-elf-objdump -D demo.o

结果:
demo.o:fileformatelf32-littlearm

Disassemblyofsection.text:

00000000<.text>:
0: e59fd004 ldrsp,[pc,#4];c<.text+0xc>
4: e59fa004 ldrsl,[pc,#4];10<.text+0x10>
8: e59ff004 ldrpc,[pc,#4];14<.text+0x14>
c: 0c002000 stceq0,cr2,[r0]
10: 0c001000 stceq0,cr1,[r0]
14:00000000 andeqr0,r0,r0
Disassemblyofsection.data:

0、4、8三行相当于是代码段,C,10,14相当于是数据段,伪指令的 变量定义存储在这里。由于entry还没连上目标地 址,objdump反汇编会认为是0,我们先不管它。另外两条LDR伪指令变成了实际的LDR指令!但目标很奇怪,都是[pc, #4]。那好我们看看[pc, #4]是什么。
我们知道pc中存放的是当前指令的下下条指令的位置,也就是.+8。那么上面的第一条指令ldrsp,[pc,#4]中的pc就是0x8,pc+4就是0xc,而[0xc]的内容正是0x0c002000;同理,第二条ldr指令也是如此。显然这里LDR伪指令采用的是RISC通用的方法。
另外从反汇编的代码可以看出ldr伪指令中存储的是一个相对偏移量,该偏移量是相对当前pc值的一个偏移量。
------------------------------------------------------------------------------
(3)此外,有必要回味一下adr伪指令,U-boot中那段relocate代码就是通过adr实现当前程序是在RAM中还是flash中。
ADR指令为小范围的地址读取伪指令.ADR指令将基于PC相对偏移的地址值读取到寄存器中.在汇编编译源程序时,ADR伪指令被编译器替换成一条合适的 指令.通常,编译器用一条ADD指令或SUB指令来实现该ADR伪指令的功能,若不能用一条指令实现,则产生错误,编译失败.
ADR 伪指令格式如下
ADR{cond} register,exper 其中
register 加载的目标寄存器
exper 地址表达式.当地址值是非字地齐时,取值范围-255~255 字节之间;当地址是字对齐时,取值范围-1020~1020 字节之间.对于基于PC 相对偏移的地址 值时,给定范围是相对当前指令地址后两个字处(因为ARM7TDMI 为三级流水线).
ADR 伪指令举例如下;
LOOP MOV R1,#0xF0

ADR R2,LOOP ;将LOOP 的地址放入R2
ADR R3,LOOP+4
可以用ADR 加载地址,实现查表:

ADR R0,DISP_TAB ;加载转换表地址
LDRB R1,[R0,R2] ;使用R2 作为参数,进行查表

DISP_TAB
DCB 0Xc0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90

4 ldr pc,_label
_label .word label
一般ldr pc,=label就被解释成上面这两条指令,但是从反汇编程序中可以看出由于偏移量仅为4k,_label的定义位置要和ldr指令相距在4k以内。借此可以实现大范围地址的跳转,完成从flash到sdram的跳转。


关键词:arm汇编跳转指

评论


技术专区

关闭