总算了解这个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!,{
对应的,运用以下指令能够完结子程序的回来
LDMFD SP!, {
转载自: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言语中调用到的话,却是这样引证的:
- /* 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
- bldo_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 pWTCON0x53000000
- 。。。
- 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 pWTCON0x53000000
- 。。。
- ldr r0, =pWTCON
被翻译后的真实的汇编代码:
- 33d00068: e3a00453mov r0, #1392508928 ; 0x53000000
(2)假如该操作数不是mov的有用操作数,那么ldr伪指令就会被翻译成ldr指令。
举例阐明:
汇编代码:
- ldr r1, =0x7fff
被翻译后的真实的汇编代码:
- 33d00080: e59f13f8ldr r1, [pc, #1016] ; 33d00480
- 。。。
- 33d00480: 00007fff.word 0x00007fff
即把ldr伪指令翻译成真实的ldr指令,而且别的分配了一个word的地址空间用于寄存该数值,然后用ldr指令将对应地址中的值载入,赋值给r1寄存器。
汇编中,一个常用的,用来给某个地址赋值的办法,相似如下办法:
- #define 宏的姓名寄存器地址
- 。。。
- ldr r1, =要赋的值
- ldr r0, =宏的姓名
- str r1, [r0]