arm汇编中的跳转指令

 
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:
     .equ    STACK_BASE0x0c002000
.equ    STACK_SIZE0x00001000

.text
    ldr    sp, = STACK_BASE
    ldr    sl, = STACK_BASE - STACK_SIZE
    ldr    pc, =
 entry

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

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

我们如果把上面代码写成:
    .text
    mov    sp, #0x0c002000
    mov    sl, #0x0c001000
    mov    pc, #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:     file format elf32 - littlearm

Disassembly of section .text:

00000000   < .text > :
0 :   e59fd004    ldr     sp, [pc, # 4 ]    ; c  < .text + 0xc >
4 :   e59fa004    ldr     sl, [pc, # 4 ]    ;  10   < .text + 0x10 >
8 :   e59ff004    ldr     pc, [pc, # 4 ]    ;  14   < .text + 0x14 >
c:   0c002000    stceq   
0 , cr2, [r0]
10 :  0c001000    stceq    0 , cr1, [r0]
14 00000000     andeq   r0, r0, r0
Disassembly of section .data:

    0、4、8三行相当于是代码段,C,10,14相当于是数据段,伪指令的变量定义存储在这里。由于entry还没连上目标地 址,objdump反汇编会认为是0,我们先不管它。另外两条LDR伪指令变成了实际的LDR指令!但目标很奇怪,都是[pc, #4]。那好我们看看[pc, #4]是什么。
    我们知道pc中存放的是当前指令的下下条指令的位置,也就是.+8。那么上面的第一条指令ldr
sp, [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常用汇编指令,汇编,编译器,flash,c,存储,linux)