在嵌入式开发中,汇编程序往往应用在非常关键的地方,比如系统启动时的初始化,进出中断时的环境保存、恢复,对性能要求非常苛刻的函数等。
1. 相对跳转指令:b、bl
这两条指令的不同之处在于bl指令除了跳转之外,还将返回地址(bl的下一条指令的地址)保存在lr寄存器中。
这两条指令的可跳转范围是当前指令的前后32MB。它们是与位置无关的指令。
示例:
b fun1
...
fun1:
-------------------------------
bl fun2
...
fun2:
...
mov pc, lr @返回
2. 数据传送指令mov,地址读取指令ldr
mov指令可以把一个寄存器的值赋值给另一个寄存器,或者是把一个常数赋值给另一个寄存器。例子如下:
mov r1, r2 /*r1=r2*/
mov r1, #4096
mov指令传送的常数必须能用“立即数”来表示。
当不知道一个数能否用“立即数”来表示时,可以使用ldr命令来赋值。ldr是伪指令,它不是真实存在的指令,编译器会把它扩展成为真正的指令:如果该常数能用“立即数”来表示,则使用mov指令;否则编译时将该常数保存在某个位置,使用内存读取指令把它读出来。
示例:
ldr r1,=4097 /*r1=4097*/
ldr本意为“大范围的地址读取伪指令”,下面的例子是使用它来将常数赋给寄存器r1。下例是获得代码的绝对地址:
ldr r1, =label
label:
...
3. 内存访问指令:ldr、str、ldm、stm
注意:ldr指令既可能是前面所述的大范围的地址读取伪指令,也可能是内存访问指令。当它的第二个参数前面有“=”时,表示伪指令,否则表示内存访问指令。
ldr指令是从内存读存指令到寄存器中,str指令是把寄存器中的值存储到内存中。它们操作的数都是32位的数。
示例:
ldr r1, [r2, #4] /*将地址为r2+4的内存单元数据读取到r1中*/
ldr r1, [r2] /*将地址为r2的内存单元数据读取到r1中*/
ldr r1, [r2], #4 /*将地址为r2的内存单元数据读取到r1中,然后,r2=r2+4*/
str r1, [r2, #4] /*将r1的数据保存到地址为r2+4的内存单元中*/
str r1, [r2] /*将r1的数据保存到地址为r2的内存单元中*/
str r1, [r2], #4 /*将r1的数据保存到地址为r2的内存单元中,然后,r2=r2+4*/
ldm和stm属于批量内存访问指令,只用一条指令就可以读写多个数据。它们的格式如下:
ldm{cond}<addressing_mode> <rn>{!} <register list>{^}
stm{cond}<addressing_mode> <rn>{!} <register list>{^}
其中,
{cond}表示指令的执行条件,参见下面的指令条件码。
<addressing_mode>表示地址变化模式,有以下4种方式:
1)ia(increment after):事后递增方式;
2)ib(increment before):事先递增方式;
3)da(decrement after):事后递减方式;
4)db(decrement before):事先递减方式。
<rn>中保存内存的地址,如果后面加上了感叹号,指令执行后,rm的值会更新,等于下一个内存单元的地址。
<register list>表示寄存器列表,对于ldm指令,从<rn>所对应的内存块中取出数据,写入这些寄存器;对于stm指令,把这些寄存器的值写入<rn>所对应的内存块中。
{^}有两种含义:如果<register list>中有PC寄存器,它表示指令执行后,spsr寄存器的值将自动复制到cpsr寄存器中——这常用于从中断处理函数中返回;如果<register list>中没有PC寄存器,{^}表示操作的是用户模式下的寄存器,而不是当前特权模式的寄存器。
指令中寄存器列表和内存单元的对应关系为:编号低的寄存器对应于内存中的低地址单元,编号高的寄存器对应于内存中高地址的单元。
示例如下:
HandleIRQ: @中断入口函数
sub lr,lr,#4 @计算返回地址
stmdb sp!,{r0-r12,lr} @保存使用到的寄存器
@r0~r12,lr被保存在sp表示的内存中
@"!"使得指令执行后sp=sp-14*4
ldr lr,=int_return @设置调用IRQ_Handle函数后的返回地址
ldr pc,=IRQ_Handle @调用中断分发函数
int_return:
ldmia sp!,{r0-r12,pc}^ @中断返回,"^"表示将spsr的值复制到cpsr
@于是从irq模式返回被中断的工作模式
@"!"使得指令执行后sp=sp+14*4
4. 加减指令:add、sub
示例:
add r1, r2, #1 /*表示r1=r2+1,即寄存器r1的值等于寄存器r2的值加上1*/
sub r1, r2, #1 /*表示r1=r2-1*/
5. 程序状态寄存器的访问指令:msr、mrs
ARM处理器有一个程序状态寄存器(cpsr),它用来控制处理器的工作模式、设置中断的总开关。
示例:
msr cpsr, r0 /*复制r0到cpsr中*/
mrs r0, cpsr /*复制cpsr到r0中*/
6. 其它伪指令
.extern main
.text
.global _start
_start:
含义:
".extern"定义一个外部符号(可以是变量也可以是函数),上面的代码表示本文件中引用的main是一个外部函数。
".text"表示下面的语句都属于代码段。
".global"将本文件中的某个程序标号定义为全局的,比如上面的代码表示_start是个全局函数。
7. 汇编语言的执行条件
大多数的ARM指令都可以条件执行,即根据cpsr寄存器中的条件标志决定是否执行该指令:如果条件不满足,该指令相当于一条nop指令。
每条ARM指令包含4位的条件码域,这表明可以定义16个执行条件。可以将这些执行条件的助记符附加在指令后面,比如moveq、movgt等。这16个条件码和它们的助记符含义如下表所示:
源文档 <http://weimenlove.blog.163.com/blog/static/177754732011115115350287/>