您的位置:首页 > 其它

ARM汇编总结 MOV PC,LR

2015-09-11 17:37 381 查看
目录(?)[-]

终于明白这个LR寄存器了

汇编学习总结记录


终于明白这个LR寄存器了

看下面这个ARM汇编吧

BL NEXT ;跳转到子程序

......... ;NEXT处执行

NEXT

..........

MOV PC,LR ;从子程序返回

这里的BL是跳转的意思,LR(R14)保存了返回地址

PC(R15)是当前地址,把LR给PC就是从子程序返回

这里有一下总结

首先

1.SP(R13) LR(R14)PC(R15)

2.lr(r14)的作用问题,这个lr一般来说有两个作用:

1》.当使用bl或者blx跳转到子过程的时候,r14保存了返回地址,可以在调用过程结尾恢复。

2》.异常中断发生时,这个异常模式特定的物理R14被设置成该异常模式将要返回的地址。

另外注意pc,在调试的时候显示的是当前指令地址,而用mov lr,pc的时候lr保存的是此指令向后数两条指令的地址,大家可以试一下用mov pc,pc,结果得到的是跳转两条指令,这个原因是由于arm的流水线造成的,预取两条指令的结果.

3.》我以前看书不懂的地方

子程序返回的三种方法

现在总结如下

1.MOV PC,LR

2.BL LR

3.在子程序入口处使用以下指令将R14存入堆栈

STMFD SP!,{<Regs>,LR}

对应的,使用以下指令可以完成子程序的返回

LDMFD SP!, {<Regs>,LR}

转载自:http://blog.csdn.net/xgx198831/article/details/8333446

汇编学习总结记录

1.1. 汇编学习总结记录

对于我们之前分析的start.S中,涉及到很多的汇编的语句,其中,可以看出,很多包含了很多种不同的语法,使用惯例等,下面,就对此进行一些总结,借 以实现一定的举一反三或者说触类旁通,这样,可以起到一定的借鉴功能,方便以后看其他类似汇编代码, 容易看懂汇编代码所要表达的含义。

1.1.1. 汇编中的标号=C中的标号

像前面汇编代码中,有很多的,以点开头,加上一个名字的形式的标号,比如:

reset:

/*

* set the cpu to SVC32 mode

*/

mrs r0,cpsr

复制代码
中的reset,就是汇编中的标号,相对来说,比较容易理解,就相当于C语言的标号。

比如,C语言中定义一个标号ERR_NODEV:

ERR_NODEV: /* no device error */

... /* c code here */

复制代码
然后对应在别处,使用goto去跳转到这个标号ERR_NODEV:

if (something)

goto ERR_NODEV ;

复制代码
【总结】

汇编中的标号 = C语言中的标号Label

1.1.2. 汇编中的跳转指令=C中的goto

对应地,和上面的例子中的C语言中的编号和掉转到标号的goto类似,汇编中,对于定义了标号,那么也会有对应的指令,去跳转到对应的汇编中的标号。

这些跳转的指令,就是b指令,b是branch的缩写。

b指令的格式是:

b{cond} label

简单说就是跳转到label处。

用和上面的例子相关的代码来举例:

.globl _start

_start: b reset

复制代码
就是用b指令跳转到上面那个reset的标号。

【总结】

汇编中的b跳转指令 = C语言中的goto

1.1.3. 汇编中的.globl=C语言中的extern

对于上面例子中:

.globl _start

中的.global,就是声明_start为全局变量/标号,可以供其他源文件所访问。

即汇编器,在编译此汇编代码的时候,会将此变量记下来,知道其是个全局变量,遇到其他文件是用到此变量的的时候,知道是访问这个全局变量的。

因此,从功能上来说,就相当于C语言用extern去生命一个变量,以实现本文件外部访问此变量。

【总结】

汇编中的.globl或.global = C语言中的extern

1.1.4. 汇编中用bl指令和mov pc,lr来实现子函数调用和返回

和b指令类似的,另外还有一个bl指令,语法是:

BL{cond} label

其作用是,除了b指令跳转到label之外,在跳转之前,先把下一条指令地址存到lr寄存器中,以方便跳转到那边执行完毕后,将lr再赋值给pc,以实现函数返回,继续执行下面的指令的效果。

用下面这个start.S中的例子来说明:

bl cpu_init_crit

。。。

cpu_init_crit:

。。。

mov pc, lr

复制代码
其中,就是先调用bl掉转到对应的标号cpu_init_crit,其实就是相当于一个函数了,

然后在cpu_init_crit部分,执行完毕后,最后调用 mov pc, lr,将lr中的值,赋给pc,即实现函数的返回原先 bl cpu_init_crit下面那条代码,继续执行函数。

上面的整个过程,用C语言表示的话,就相当于

。。。

cpu_init_crit();

。。。

void cpu_init_crit(void)

{

。。。

}

复制代码

而关于C语言中,函数的跳转前后所要做的事情,都是C语言编译器帮我们实现好了,会将此C语言中的函数调用,转化为对应的汇编代码的。

其中,此处所说的,函数掉转前后所要做的事情,就是:

函数跳转前:要将当前指令的下一条指令的地址,保存到lr寄存器中。

函数调用完毕后:将之前保存的lr的值给pc,实现函数跳转回来。继续执行下一条指令。

而如果你本身自己写汇编语言的话,那么这些函数跳转前后要做的事情,都是你程序员自己要关心,要实现的事情。

【总结】

汇编中bl + mov pc,lr = C语言中的子函数调用和返回

1.1.5. 汇编中的对应位置有存储值的标号 = C语言中的指针变量

像前文所解析的代码中类似于这样的:

LABEL1:.word Value2

复制代码
比如:

_TEXT_BASE:

.word TEXT_BASE

复制代码
所对应的含义是,有一个标号_TEXT_BASE

而该标号中对应的位置,所存放的是一个word的值,具体的数值是TEXT_BASE,此处的TEXT_BASE是在别处定义的一个宏,值是0x33D00000。

所以,即为:

有一个标号_TEXT_BASE,其对应的位置中,所存放的是一个word的值,值为TEXT_BASE=0x33D00000。

总的来说,此种用法的含义,如果用C语言来表示,其实更加容易理解:

int *_TEXT_BASE = TEXT_BASE = 0x33D00000

即:

int *_TEXT_BASE = 0x33D00000

【C语言中如何引用汇编中的标号】

不过,对于这样的类似于C语言中的指针的汇编中的标号,在C语言中调用到的话,却是这样引用的:

/* for the following variables, see start.S */

extern ulong _armboot_start; /* code start */

extern ulong _bss_start; /* code + data end == BSS start */

。。。

IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;

。。。

复制代码
而不是我原以为的,直接当做指针来引用该变量的方式:

*IRQ_STACK_START = *_armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;

复制代码

其中,对应的汇编中的代码为:

.globl _armboot_start

_armboot_start:

.word _start

复制代码
所以,针对这点,还是需要注意一下的。至少以后如果自己写代码的时候,在C语言中引用汇编中的global的标号的时候,知道是如何引用该变量的。

【总结】

汇编中类似这样的代码:

label1: .word value2

就相当于C语言中的:

int *label1 = value2

但是在C语言中引用该标号/变量的时候,却是直接拿来用的,就像这样:

label1 = other_value

其中label1就是个int型的变量。

1.1.6. 汇编中的ldr+标号,来实现C中的函数调用

接着上面的内容,继续解释,对于汇编中这样的代码:

第一种:

ldr pc, 标号1

。。。

标号1:.word 标号2

。。。

标号2:

。。。(具体要执行的代码)

或者是,

第二种:

ldr pc, 标号1

。。。

标号1:.word XXX(C语言中某个函数的函数名)

的意思就是,将地址为标号1中内容载入到pc中。

而地址为标号1中的内容,就是标号2。

所以上面第一种的意思:

就很容易看出来,就是把标号2这个地址值,给pc,即实现了跳转到标号2的位置执行代码,就相当于调用一个函数,该函数名为标号2.

第二种的意思,和上面类似,是将C语言中某个函数的函数名,即某个地址值,给pc,实现调用C中对应的那个函数。

两种做法,其含义用C语言表达,其实很简单:

PC = *(标号1) = 标号2

举个例子就是:

第一种:

。。。

ldr pc, _software_interrupt

。。。

_software_interrupt: .word software_interrupt

。。。

software_interrupt:

get_bad_stack

bad_save_user_regs

bl do_software_interrupt

复制代码

就是实现了将标号1,_software_interrupt,对应的位置中的值,标号2,software_interrupt,给pc,即实现了将pc掉转到software_interrupt的位置,即实现了调用函数software_interrupt的效果。

第二种:

ldr pc, _start_armboot

_start_armboot: .word start_armboot

复制代码
含义就是,将标号1,_start_armboot,所对应的位置中的值,start_armboot给pc,即实现了调用函数start_armboot的目的。

其中,start_armboot是C语言文件中某个C语言的函数。

【总结】

汇编中,实现函数调用的效果,有如下两种方法:

方法1:

ldr pc, 标号1

。。。

标号1:.word 标号2

。。。

标号2:

。。。(具体要执行的代码)

方法2:

ldr pc, 标号1

。。。

标号1:.word XXX(C语言中某个函数的函数名)

1.1.7. 汇编中设置某个寄存器的值或给某个地址赋值

在汇编代码start.S中,看到不止一处, 类似于这样的代码:

形式1:

# define pWTCON 0x53000000

。。。

ldr r0, =pWTCON

mov r1, #0x0

str r1, [r0]

复制代码
或者:

形式2:

# define INTSUBMSK 0x4A00001C

。。。

ldr r1, =0x7fff

ldr r0, =INTSUBMSK

str r1, [r0]

复制代码
其含义,都是将某个值,赋给某个地址,此处的地址,是用宏定义来定义的,对应着某个寄存器的地址。

其中,形式1是直接通过mov指令来将0这个值赋给r1寄存器,和形式2中的通过ldr伪指令来将0x3ff赋给r1寄存器,两者区别是,前者是因为已经确定所要赋的值0x0是mov的有效操作数,而后者对于0x3ff不确定是否是mov的有效操作数

(如果不是,则该指令无效,编译的时候,也无法通过编译,会出现类似于这样的错误:

start.S: Assembler messages:

start.S:149: Error: invalid constant -- `mov r1,#0xFFEFDFFF'

make[1]: *** [start.o] 错误 1

make: *** [cpu/arm920t/start.o] 错误 2

复制代码


所以才用ldr伪指令,让编译器来帮你自动判断:

(1)如果该操作数是mov的有效操作数,那么ldr伪指令就会被翻译成对应的mov指令。

举例说明:

汇编代码:

# define pWTCON 0x53000000

。。。

ldr r0, =pWTCON

复制代码
被翻译后的真正的汇编代码:

33d00068: e3a00453 mov r0, #1392508928 ; 0x53000000

复制代码
(2)如果该操作数不是mov的有效操作数,那么ldr伪指令就会被翻译成ldr指令。

举例说明:

汇编代码:

ldr r1, =0x7fff

复制代码
被翻译后的真正的汇编代码:

33d00080: e59f13f8 ldr r1, [pc, #1016] ; 33d00480 <fiq+0x60>

。。。

33d00480: 00007fff .word 0x00007fff

复制代码
即把ldr伪指令翻译成真正的ldr指令,并且另外分配了一个word的地址空间用于存放该数值,然后用ldr指令将对应地址中的值载入,赋值给r1寄存器。

【总结】

汇编中,一个常用的,用来给某个地址赋值的方法,类似如下形式:

#define 宏的名字 寄存器地址

。。。

ldr r1, =要赋的值

ldr r0, =宏的名字

str r1, [r0]

复制代码

转载自:http://bbs.chinaunix.net/thread-2312780-1-1.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: