原文:http://blog.sina.com.cn/s/blog_87c063060101ber5.html
在嵌入式开发中,汇编程序常常用于非常关键的地方,比如系统启动时的初始化,进出中断时的环境保存、恢复,对性能要求非常苛刻的函数等。
1、相对跳转指令: b 、 bl
不同之处在于: bl 指令除了跳转之外,还将返回地址( bl 的下一条指令的地址)保存在 lr 寄存器中。
跳转范围:当前指令的前后32M。
它们是与位置无关的指令。
示例:
......
fun1:
......
fun2:
......
2、数据传送指令: mov ,地址读取伪指令: ldr
mov 指令可以把一个寄存器的值赋给另一个寄存器,或者把一个常数赋给寄存器。
例:
mov
mov
mov 指令传送的常数必须能用立即数来表示。
当不知道一个数能否用立即数来表示时,可以使用 ldr 命令来赋值。 ldr 是伪指令,它不是真实存在的指令,编译器会把它扩展成真正的指令:如果该常数能用立即数来表示,则使用 mov 指令;否则编译时将该常数保存在某个位置,使用内存读取指令把它读出来。
例:
ldr
ldr 本意为“大范围的地址读取伪指令”,以下是获得代码的绝对地址:
例:
label:
......
3、内存访问指令: ldr 、 str 、 ldm 、 stm
ldr 指令既可能是大范围的地址读取伪指令,也可能是内存访问指令。当它的第二个参数前面有 “ = ”时,表示伪指令,否则表示内存访问指令。
ldr 指令是从内存中读取数据到寄存器,str 指令把寄存器的值存储到内存中,它们操作的数据都是32位的。
例:
ldr
ldr
ldr
str
str
str
ldm 和 stm 属于批量内存访问指令,只用一条指令就可以读写多个数据。格式为:
ldm
stm
其中,{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指令包含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:
int_return:
4、加减指令: add 、 sub
例:
add
sub
5、程序状态寄存器的访问指令: msr 、 mrs
ARM处理器有一个程序状态寄存器( cpsr ),它用来控制处理器的工作模式、设置中断的总开关。
例:
msr
mrs
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 | v7、s1 | ARM状态局部变量寄存器7、在支持数据栈检查的ATPCS中为数据栈限制指针 |
r9 | v6、sb | ARM状态局部变量寄存器6、在支持RWPI的ATPCS中为静态基址寄存器 |
r8 | v5 | ARM状态局部变量寄存器5 |
r7 | v4、wr | ARM状态局部变量寄存器4、THUMB状态工作寄存器 |
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
ED:Empty
FA :Full
EA:Empty
ATPCS规定数据栈为FD类型,并且对数据栈的操作是8字节对齐的 。使用 stmdb / ldmia 批量内存访问指令来操作FD数据栈。
使用stmdb命令往数据栈中保存内容时,先递减sp指针,再保存数据,使用ldmia命令从数据栈中恢复数据时,先获得数据,再递增sp指针,sp指针总是指向栈顶元素,这刚好是FD栈的定义。
3、参数传递规则
一般地,当参数个数不超过 4 个时,使用 r0 ~ r3 这4个寄存器来传递参数;如果参数个数超过 4 个,剩余的参数通过数据栈来传递。
对于一般的返回结果,通常使用 r0 ~ r3 来传递。
例:
假设CopyCode2SDRAM函数是用C语言实现的,它的数据原型如下:
int
在汇编代码中,使用下面的代码调用它,并判断返回值:
ldr
mov
mov
bl
cmp
的调用规定一定的规则.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...
寄存器的使用规则:
子程序在返回前无需恢复寄存器R0~R3的内容.
V1~V8 .如果 在子程序中 使用到V1~V8的某些寄存器, 在进入子程序时 必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值,对于子程序中没有用到的寄存器则不必
执行这些操作.在THUMB程序中,通常只能使用寄存器R4~R7来保存局部变量.
4. 寄存器R13用作数据栈指针,记做SP,
寄存器SP在进入子程序时的值和退出子程序时的值必须相等.
5. 寄存器R14用作连接寄存器,记作lr ; 它用于保存子程序的返回地址,如果在子程
序中保存了返回地址,则R14可用作其它的用途.
6. 寄存器R15是程序计数器,记作PC ; 它不能用作其他用途.
7. ATPCS中的各寄存器在ARM编译器和汇编器中都是预定义的.(这句话很关键)
数据栈的使用规则
据元素)时,称为FULL栈.当栈指针指向与栈顶元素相邻的一个元素时,称为Empty栈.
数据栈的增长方向也可以不同. 当数据栈向内存减小的地址方向增长时,称为
Descending栈; 当数据栈向着内存地址增加的方向增长时,称为Ascending栈.
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字节对齐的. 而对于汇编程序
来说,如果目标文件中包含了外部调用,则必须满足以下条件:
参数的传递规则.
根据参数个数是否固定,可以将子程序分为参数个数固定的子程序和参数个数可
变的子程序.这两种子程序的参数传递规则是不同的.
1.参数个数可变的子程序参数传递规则
参数传递,当参数超过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指令.