鸿 网 互 联 www.68idc.cn

常用ARM汇编指令及ATPCS规则

来源:互联网 作者:佚名 时间:2015-10-09 05:41
原文:http://blog.sina.com.cn/s/blog_87c063060101ber5.html 在 嵌入 式开发中, 汇编 程序常常用于非常关键的地方,比如系统启动时的初始化,进出中断时的环境保存、恢复,对性能要求非常苛刻的函数等。 1、相对跳转 指令 : b 、 bl 不同之处在于: bl

原文:http://blog.sina.com.cn/s/blog_87c063060101ber5.html

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

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}  { ! }  { ^ }
stm  {cond}  { ! }  { ^ }
其中,{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。

表示地址变化模式,有4种方式:
ia (Increment After)        :事后递增方式。
ib (Increment Before)     :事先递增方式。
da (Decrement After)     :事后递减方式。
db (Decrement Before)  :事先递减方式。
中保存内存的地址,如果后面加上感叹号,指令执行后,rn 的值会更新,等于下一个内存单元的地址。
表示寄存器列表,对于 ldm 指令,从 所对应的内存块中取出数据,写入这些寄存器;对于 stm 指令,把这些寄存器的值写入 所对应的内存块中。
{^} 有两种含义:
如果 中有 pc寄存器 ,它表示指令执行后,spsr寄存器的值将自动复制到cpsr寄存器中——这常用于从中断处理函数中返回
如果 中没有 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、fp

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个寄存器来传递参数;如果参数个数超过 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                               @判断函数返回值









****************************************************************************************************************************************************************************************************

ATPCS概述


  为了使单独编译的C语言程序和汇编程序之间能够相互调用,必须为子程序之间

的调用规定一定的规则.ATPCS就是ARM程序和THUMB程序中子程序调用的基本规

则...

一.ATPCS概述...

ATPCS规定了一些子程序之间调用的基本规则.这些基本规则包括子程序调用过

程中寄存器的使用规则,数据栈的使用规则,参数的传递规则.为适应一些特定的需要,

对这些基本的调用规则进行一些修改得到几种不同的子程序调用规则,这些特定的调

规则包括:支持数据栈限制检查的ATPCS. 支持只读段位置无关的ATPCS. 支持可

读写段位置无关的ATPCS. 支持ARM程序和THUMB程序混合使用的ATPCS. 处理浮

点运算的ATPCS...

有调用关系的所有子程序必须遵守同一种ATPCS. 编译器或者汇编器在ELF格

式的目标文件中设置相应的属性,标识用户选定的ATPCS类型.对应不同类型的

ATPCS规则,有相应的C语言库,连接器根据用户指定的ATPCS类型连接相应的C语

言库...

使用ADS的C语言编译器编译的C语言子程序满足用户指定的ATPCS类型. 而对

汇编语言程序来说,完全要依赖用户来保证各子程序满足选定的ATPCS类型. 具

体来说,汇编语言子程序必须满足下面三个条件: 在子程序编写时必须遵守相应的

ATPCS规则; 数据栈的使用要遵守ATPCS规则; 在汇编编译器中使用-apcs选项...

二. 基本ATPCS...

    基本ATPCS规定了在子程序调用时的一些基本规则,包括以下三个方面的内容: 

    各寄存器的使用规则及其相应的名字; 

     数据栈的使用规则

     参数传递的规则

     相对于其他类型的ATPCS,满足基本ATPCS的程序的执行速度更快,所占用的内存更少. 但是它不能提供以下的支持: 

     ARM程序和THUMB程序相互调用;

     数据以及代码的位置无关的支持; 

     子程序的可重入性; 

      数据栈检查的支持. 

      而派生的其他几种特定的ATPCS就是在基本ATPCS的基础上再添加其他的规则而形成的.其目的就是提供上述的功能...

寄存器的使用规则:

   1. 子程序通过寄存器R0~R3来传递参数. 这时寄存器可以记作: A0~A3 , 调用的

子程序在返回前无需恢复寄存器R0~R3的内容.

   2. 在子程序中,使用R4~R11来保存局部变量,这时寄存器R4~R11可以记作:

V1~V8 .如果 在子程序中 使用到V1~V8的某些寄存器, 在进入子程序时 必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值,对于子程序中没有用到的寄存器则不必

执行这些操作.在THUMB程序中,通常只能使用寄存器R4~R7来保存局部变量.

    3.寄存器R12用作 子程序间scratch(暂存)寄存器,记作ip; 在子程序的链接代码段中经常会有这种使用规则.

4. 寄存器R13用作数据栈指针,记做SP, 在子程序中 寄存器R13不能用做其他用途.

寄存器SP在进入子程序时的值和退出子程序时的值必须相等.

5. 寄存器R14用作连接寄存器,记作lr ; 它用于保存子程序的返回地址,如果在子程

序中保存了返回地址,则R14可用作其它的用途.

6. 寄存器R15是程序计数器,记作PC ; 它不能用作其他用途.

7. ATPCS中的各寄存器在ARM编译器和汇编器中都是预定义的.(这句话很关键)

数据栈的使用规则

   栈指针通常可以指向不同的位置.当栈指针指向栈顶元素(即最后一个入栈的数

据元素)时,称为FULL栈.当栈指针指向与栈顶元素相邻的一个元素时,称为Empty栈.

数据栈的增长方向也可以不同. 当数据栈向内存减小地址方向增长时,称为

Descending栈; 当数据栈向着内存地址增加的方向增长时,称为Ascending栈.

  综合这两种特点可以由以下4种数据栈: FD ED FA EA . ATPCS规定数据栈为FD类型,并对数据栈的操作是8字节对齐的,下面是一个数据栈的示例及相关的名词.

1.数据栈栈指针.stack pointer 指向最后一个写入栈的数据的内存地址.

2.数据栈的基地址.stack base 是指数据栈的最高地址.由于ATPCS中的数据栈是

FD类型的,实际上数据栈中最早入栈数据占据的内存单元基地址的下一个内存单

元.

3.数据栈界限.stack limit 是指数据栈中可以使用的最低的内存单元地址.

4.已占用的数据栈.used stack 是指数据栈的基地址数据栈栈指针之间的区域.

其中包括数据栈栈指针对应的内存单元.

5.数据栈中的数据帧(stack frames) 是指在数据栈中,为子程序分配的用来保存存器局部变量的区域.

  异常中断的处理程序可以使用 被中断程序的数据栈,这时用户要保证中断的程序

数据栈足够大. 使用ADS编译器产生的目标代码中包含了DRFAT2格式的数据帧.在

调试过程中,调试器可以使用这些数据帧来查看数据栈中的相关信息.而对于汇编

言来说,用户必须使用FRAME伪操作来描述数据栈中的数据帧.ARM汇编器根据这

些伪操作在目标文件中产生相应的DRFAT2格式的数据帧.

在ARMv5TE中,批量传送指令LDRD/STRD要求数据栈是8字节对齐,以提高数

据的传送速度.用ADS编译器产生的目标文件中,外部接口的数据栈都是8字节对齐的,

并且编译器将告诉连接器: 本目标文件中的数据栈是8字节对齐的. 而对于汇编程序

来说,如果目标文件中包含了外部调用,则必须满足以下条件: 

      外部接口的数据栈一定是8位对齐的,也就是要保证在进入该汇编代码后,直到该汇编程序调用外部代码之间,数据栈的栈指针变化为偶数个字; 

      在汇编程序中使用PRESERVE8伪操作告诉连接器,本汇编程序是8字节对齐的.


参数的传递规则.

根据参数个数是否固定,可以将子程序分为参数个数固定子程序参数个数可

子程序.这两种子程序的参数传递规则是不同的.

1.参数个数可变的子程序参数传递规则

   对于参数个数可变的子程序,当参数不超过4个时,可以使用寄存器R0~R3来进行

参数传递,当参数超过4个时,还可以使用数据栈来传递参数. 

   在参数传递时,将所有参看做是存放在连续的内存单元中的字数据。然后,依次将各名字数据传送到寄存器R0,R1,R2,R3; 

   如果参数多于4个,将剩余的字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后一个字数据先入栈. 按照上面的规则,一个浮点数参数可以通过寄存器传递,也可以通过数据栈传递,也可能一半通过寄存器传递,另一半通过数据栈传递.

2.参数个数固定的子程序参数传递规则

对于参数个数固定的子程序,参数传递与参数个数可变的子程序参数传递规则

同,如果系统包含浮点运算的硬件部件,浮点参数将按照下面的规则传递: 各个浮点参

数按顺序处理;为每个浮点参数分配FP寄存器;分配的方法是,满足该浮点参数需要的

且编号最小的一组连续的FP寄存器.第一个整数参数通过寄存器R0~R3来传递,其他

参数通过数据栈传递.

子程序结果返回规则

1.结果为一个32位的整数时,可以通过寄存器R0返回.

2.结果为一个64位整数时,可以通过R0和R1返回,依此类推.

3.结果为一个浮点数时,可以通过浮点运算部件的寄存器f0,d0或者s0来返回.

4.结果为一个复合的浮点数时,可以通过寄存器f0-fN或者d0~dN来返回.

5.对于位数更多的结果,需要通过调用内存来传递.

三.几种特定的ATPCS...

A.支持数据栈限制检查的ATPCS.

如果在程序设计期间能够准确地计算出程序所需的内存总量,就不需要进行数据

栈的检查,但是在通常情况下这是很难做到的,这时需要进行数据栈的检查. 在进行数

据栈的检查时,使用寄存器R10作为数据栈限制指针,这时寄存器R10又记作sl.用户在

程序中不能控制该寄存器.具体来说,支持数据栈限制的ATPCS要满足下面的规则: 在

已经占有的栈的最低地址和sl之间必须有256字节的空间,也就是说,sl所指的内存地

址必须比已经占用的栈的最低地址低256个字节.当中断处理程序可以使用用户的数

据栈时,在已经占用的栈的最低地址和sl之间除了必须保留的256个字节的内存单元

外,还必须为中断处理预留足够的内存空间; 用户在程序中不能修改sl的值;数据栈栈

指针sp的值必须不小于sl的值.

与支持数据栈限制检查的ATPCS相关的编译/汇编选项有下面几种: 选项/SWST

指示编译器生成的代码遵守支持数据栈限制检查的ATPCS,用户在程序设计期间不

能够准确计算程序所需的数据栈大小时,需要指定该选项;选项/noswst指示编译器生

成的代码不支持数据栈限制检查的功能,用户在程序设计期间能够准确计算出程序所

需的数据栈大小,可以指定该选项,这个选项是默认的;选项/SWSTNA 如果汇编程序

对于是否进行数据栈检查无所谓,而与该汇编程序连接的其他程序指定了选项

swst/noswst,这时使用该选项.

B.编写遵守支持数据栈限制检查的ATPCS的汇编语言程序.

对于C程序和C++程序来说,如果在编译时指定了选项SWST,生成的目标代码将

遵守支持数据栈限制检查的ATPCS. 对于汇编语言程序来说,如果要遵守支持数据

栈限制检查的ATPCS,用户在编写程序时必须满足支持数据栈限制检查的ATPCS所

要求的规则,然后指定选项SWST,下面介绍用户编写汇编语言程序时的一些要求.

叶子子程序是指不调用别的程序的子程序.

数据栈小于256字节的叶子子程序不许要进行数据栈检查,如果几个子程序组合起

来构成的叶子子程序数据栈也小于256字节,这个规则同样适用; 数据栈小于256字节

的非叶子子程序可以使用下面的代码段来进行数据栈检查.

ARM程序使用: SUB sp,sp,#size ;#size 为sp和sl之间必须保留的空间大小

CMP sp,sl;

BLLO _ARM_stack_overflow

THUMB程序使用: ADD sp,#-size ; #size为sp和sl之间必须保留的空间大小

CMP sp,sl;

BLLO _THUMB_stack_overflow

数据栈大于256字节的子程序,为了保证sp的值不小于数据栈可用的内存单元最小

的地址值,需要引入相应的寄存器.

ARM程序使用下列代码: SUB ip,sp,#size;

CMP ip,sl;

BLLO _ARM_stack_overflow

THUMB程序使用下列代码: LDR wr,#-size;

ADD wr,sp;

CMP wr,sl;

BLLO _THUMB_stack_overflow

支持只读段位置无关的ATPCS...

支持可读写段位置无关的ATPCS...

支持ARM程序和THUMB程序混合使用的ATPCS...

在编译或汇编时,使用/intework告诉编译器或汇编器生成的目标代码遵守支持

ARM程序和THUMB程序混合使用的ATPCS,它用在以下场合: 程序中存在ARM程序

调用THUMB程序的情况;程序中存在THUMB程序调用ARM程序的情况;需要连接器

来进行ARM状态和THUMB状态切换的情况;.在下述情况下使用选项nointerwork:程

序中不包含THUMB程序;用户自己进行ARM程序和THUMB程序切换.需要注意的是:

在同一个C/C++程序中不能同时有ARM指令和THUMB指令.


网友评论
<