常用ARM汇编指令及ATPCS规则

在嵌入式开发中,汇编程序常常用于非常关键的地方,比如系统启动时的初始化,进出中断时的环境保存、恢复,对性能要求非常苛刻的函数等。

1、相对跳转指令: b 、 bl 
不同之处在于: bl 指令除了跳转之外,还将返回地址( bl 的下一条指令的地址)保存在lr 寄存器中。
跳转范围:当前指令的前后32M
它们是与位置无关的指令。
示例:
        b    fun1
......
fun1:
        bl    fun2
......
fun2:
......


2、数据传送指令: mov ,地址读取伪指令: ldr
mov 指令可以把一个寄存器的值赋给另一个寄存器,或者把一个常数赋给寄存器。
例:
mov    r1,  r2
mov    r1,  #4096
mov 指令传送的常数必须能用立即数来表示。
当不知道一个数能否用立即数来表示时,可以使用 ldr 命令来赋值。 ldr 是伪指令,它不是真实存在的指令,编译器会把它扩展成真正的指令:如果该常数能用立即数来表示,则使用 mov 指令;否则编译时将该常数保存在某个位置,使用内存读取指令把它读出来。
例:
ldr    r1,  =4097
ldr 本意为“大范围的地址读取伪指令”,以下是获得代码的绝对地址:
例:
        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} 表示指令的执行条件有:

条件码(cond

助记符

含义

cpsr中条件标志位

0000

eq

相等

Z = 1

0001

ne

不相等

Z = 0

0010

cs/hs

无符号数大于/等于

C = 1

0011

cc/lo

无符号数小于

C = 0

0100

mi

负数

N = 1

0101

pl

非负数

N = 0

0110

vs

上溢出

V = 1

0111

vc

没有上溢出

V = 0

1000

hi

无符号数大于

C = 1 或 Z = 0

1001

ls

无符号数小于等于

C = 0 或 Z = 1

1010

ge

带符号数大于等于

N = 1, V = 1 或 N = 0, V = 0

1011

lt

带符号数小于

N = 1, V = 0 或 N = 0, V = 1

1100

gt

带符号数大于

Z = 0 且 N = V

1101

le

带符号数小于/等于

Z = 1 或 N! = V

1110

al

无条件执行

-

1111

nv

从不执行

-

大多数ARM指令都可以条件执行,即根据cpsr寄存器中的条件标志位决定是否执行该指令:如果条件不满足,该指令相当于一条nop指令。
每条ARM指令包含4位的条件码域,这表明可以定义16个执行条件。
cpsr条件标志位N、Z、C、V分别表示Negative、Zero、Carry、oVerflow。

<addressing_mode>
 表示地址变化模式,有4种方式:
ia (Increment After)        :事后递增方式。
ib (Increment Before)     :事先递增方式。
da (Decrement After)     :事后递减方式。
db (Decrement Before)  :事先递减方式。
<rn> 中保存内存的地址,如果后面加上感叹号,指令执行后,rn 的值会更新,等于下一个内存单元的地址。
<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
sub    r1,  r2,  #1       // r1 = r2  - 1


5、程序状态寄存器的访问指令: msr 、 mrs
ARM处理器有一个程序状态寄存器(cpsr),它用来控制处理器的工作模式、设置中断的总开关。
例:
msr    cpsr,  r0              // 复制r0到cpsr中
mrs    r0,  cpsr              // 复制cpsr到r0中

6、其他伪指令
.extern    :    定义一个外部符号(可以是变量也可以是函数)
.text        :    表示现在的语句都属于代码段
.global    :    将本文件中的某个程序标号定义为全局的


ARM-THUMB子程序调用规则:ATPCS
为了使C语言程序和汇编程序之间能够互相调用,必须为子程序间的调用制定规则,在ARM处理器中,这个规则被称为 ATPCS :ARM程序和THUMB程序中子程序调用的规则。基本的ATPCS规则包括寄存器使用规则数据栈使用规则参数传递规则

1、寄存器使用规则
子程序间通过寄存器 r0 ~ r3 来传递参数,这时可以使用它们的别名 a1 ~ a4 。被调用的子程序返回前无需恢复 r0 ~ r3 的内容。 
在子程序中,使用 r4 ~ r11 来保存局部变量,这时可以使用它们的别名 v1 ~ v8 。如果在子程序中使用了它们的某些寄存器,子程序进入时要保存这些寄存器的值,在返回前恢复它们;对于子程序中没有使用到的寄存器,则不必进行这些操作。在THUMB程序中,通常只能使用寄存器 r4 ~ r7 来保存局部变量。
寄存器 r12 用作子程序间scratch寄存器,别名为ip。
寄存器 r13 用作数据栈指针,别名为 sp 。在子程序中寄存器 r13 不能用作其他用途。它的值在进入、退出子程序时必须相等。
寄存器 r14 称为连接寄存器,别名为 lr 。它用于保存子程序的返回地址。如果在子程序中保存了返回地址(比如将 lr 值保存到数据栈中), r14 可以用作其他用途。
寄存器 r15 是程序计数器,别名为 pc 。它不能用作其他用途。

寄存器

别名

使用规则

r15

pc

程序计数器

r14

lr

连接寄存器

r13

sp

数据栈指针

r12

ip

子 程序内部调用的scratch寄存器

r11

v8

ARM状态局部变量寄存器8

r10

v7s1

ARM状态局部变量寄存器7、在支持数据栈检查的ATPCS中为数据栈限制指针

r9

v6sb

ARM状态局部变量寄存器6、在支持RWPIATPCS中为静态基址寄存器

r8

v5

ARM状态局部变量寄存器5

r7

v4wr

ARM状态局部变量寄存器4THUMB状态工作寄存器

r6

v3

ARM状态局部变量寄存器3

r5

v2

ARM状态局部变量寄存器2

r4

v1

ARM状态局部变量寄存器1

r3

a4

参数/结果/scratch寄存器4

r2

a3

参数/结果/scratch寄存器3

r1

a2

参数/结果/scratch寄存器2

r0

a1

参数/结果/scratch寄存器1


2、数据栈使用规则
数据栈有两个增长方向:向内存地址减小的方向增长时,称为 DESCENDING栈 ;向内存地址增加的方向增长时,称为 ASCENDING栈 。
所谓数据栈的增长就是移动栈指针。当栈指针指向栈顶元素(最后一个入栈的数据)时,称为 FULL栈 ;当栈指针指向栈顶元素(最后一个入栈的数据)相邻的一个空的数据单元时,称为 EMPTY栈 。
则数据栈可以分为4种:
FD:Full  Descending         满递减
ED:Empty  Descending    空递减
FA :Full  Ascending            满递增
EA:Empty  Ascending       空递增
ATPCS规定数据栈为FD类型,并且对数据栈的操作是8字节对齐的。使用 stmdb / ldmia 批量内存访问指令来操作FD数据栈。
使用stmdb命令往数据栈中保存内容时,先递减sp指针,再保存数据,使用ldmia命令从数据栈中恢复数据时,先获得数据,再递增sp指针,sp指针总是指向栈顶元素,这刚好是FD栈的定义。

3、参数传递规则
一般地,当参数个数不超过 4 个时,使用 r0 ~ r3 这4个寄存器来传递参数;如果参数个数超过 个,剩余的参数通过数据栈来传递。
对于一般的返回结果,通常使用 r0 ~ r3 来传递。
例:
假设CopyCode2SDRAM函数是用C语言实现的,它的数据原型如下:
int    CopyCode2SDRAM( unsigned  char  *buf,  unsigned  long  start_addr,  int  size )
在汇编代码中,使用下面的代码调用它,并判断返回值:
ldr    r0,  =0x30000000                 @1. 目标地址 = 0x30000000,这是SDRAM的起始地址
mov    r1,  #0                                 @2. 源地址 = 0
mov    r2,  #16*1024                     @3. 复制长度 = 16K
bl    CopyCode2SDRAM               @调用C函数CopyCode2SDRAM
cmp    a0,  #0                               @判断函数返回值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值