原文转自:http://blog.csdn.net/feimor/article/details/5704257
原文作者:
寄存器的使用规则:
寄存器 R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15
ATPCS名称 a1 a2 a3 a4 v1 v2 v3 v4 WR v5 v6 SB v7 SL v8 FP
IP SP LR PC 1.子程序间通过寄存器R0~R3来传递参数。被调用的子程序在返回前无须恢复寄存器R0~R3的内容。
2.在子程序中,使用寄存器R4~R11来保存局部变量。这时,寄存器R4~R11可以记为v1~v8。如果在子程序中使用了寄存器v1~v8中的某些寄存器,则子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值。在Thumb程序中,通常只能使用寄存器R4~R7来保存局部变量。另外R9,R10和R11还有一个特殊作用,分别记为:静态基址寄存器SB,数据栈限制指针SL和桢指针FP。
3.寄存器R12用做过程调用中间临时寄存器IP。寄存器R13用做堆栈指针SP。在子程序中寄存器R13不能用做其它用途。寄存器SP在进入子程序时的值和退出子程序的值必须相等。寄存器R14称为链接寄存器LR,它用于保存子程序的返回地址。如果在子程序中保存了返回地址,寄存器R14则可以用做其他用途。寄存器R15为程序计数器PC,不能用做其他用途。
4.只有寄存器R0~R7,SP,LR和PC可以在Thumb状态下使用,其中R7常常作为Thumb状态的工作寄存器,记为WR。
数据栈使用规则:
1.满降序栈(FD),且8字节对齐。
关于PCS与ATPCS的一点介绍
2008-09-01 22:21:50
本文来自网络,我稍作了一点编辑。
如果读者使用的是ADS1.2编译器,那么ATPCS.pdf文档就在X:/Program Files/ARM/ADSv1_2/PDF/specs目录里面。(X:/指的是ADS1.2编译器所在的安装盘)什么是PCS,什么是ATPCS?
PCS即Procedure Call Standard(过程调用规范),ATPCS即ARM-THUMB procedure call standard。
PCS规定了应用程序的函数可以如何分开地写,分开地编译,最后将它们连接在一起,所以它实际上定义了一套有关过程(函数)调用者与被调用者之间的协议。PCS强制实现如下约定:调用函数如何传递参数(即压栈方法,以何种方式存放参数),被调用函数如何获取参数,以何种方式传递函数返回值。PCS的制订是一系列指标的“tradeoff(折衷)”(因为很大程度上涉及系统的一些性能),如会涉及生成代码的大小,调试功能的支持,函数调用上下文处理速度以及内存消耗。当然,通过编译器的支持可以让生成的代码有不同的特性,如gcc编译选项可以支持或不支持framepointer来支持深入调试功能或提高程序运行性能。PCS是体系结构密切相关的,直接涉及编译器如何使用处理器提供的应用寄存器,如编译器使用什么寄存器作为栈指针,利用哪些寄存器作直接传参等。值得注意的是,没有谁规定说PCS是必须这样而不是那样的。它是应用相关的。任何一个和应用可以处于它自身的考虑定义自己的PCS。当然,如果那样,也必须有自己的编译器。而实际上,在一个处理器设计时,都会有某种假设,所以PCS某种程度上应该是一样的。
ATPCS是基于ARM指令集和THUMB指令集过程调用的规范。
寄存器的使用规则:
寄存器的使用必须满足下面的规则:
1. 子程序间通过寄存器R0 R3来传递参数,这时,寄存器R0R3可以记作A1-A4。被调用的子程序在返回前无需恢复寄存器R0-R3的内容。
2. 在子程序中,使用寄存器R4 R11来保存局部变量.这时,寄存器R4-R11可以记作V1-V8。如果在子程序中使用到了寄存器V1-V8中的某些寄存器,子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值;对于子程序中没有用到的寄存器则不必进行这些操作。在Thumb程序中,通常只能使用寄存器R4-R7来保存局部变量。
3. 寄存器R12用作子程序间scratch寄存器(用于保存SP,在函数返回时使用该寄存器出栈),记作ip。在子程序间的连接代码段中常有这种使用规则。
4. 寄存器R13用作数据栈指针,记作sp。在子程序中寄存器R13不能用作其他用途。寄存器sp在进入子程序时的值和退出子程序时的值必须相等。
5. 寄存器R14称为连接寄存器,记作lr。它用于保存子程序的返回地址。如果在子程序中保存了返回地址,寄存器R14则可以用作其他用途。
6. 寄存器R15是程序计数器,记作pc。它不能用作其他用途。
ATPCS规则 (2008-10-21 16:04:14) 标签:atpcs arm 杂谈 分类:嵌入式ARM
基本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,这时使用该选项.
编写遵守支持数据栈限制检查的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指令.
处理浮点运算的ATPCS...
ATPCS规则1 (2008-10-21 15:56:12) 标签:atpcs规则杂谈 分类:嵌入式ARM
参数传递规则
参数不超过4个时,可以使用寄存器R0~R3来传递参数,当参数超过4个时,还可以使用数据栈来传递参数。
结果为一个32位整数时,可以通过寄存器R0返回
结果为一个64位整数时,可以通过寄存器R0和R1返回,依次类推。
汇编程序、C程序及C++程序相互调用
C 程序调用汇编程序:
o 汇编程序的设置要遵循ATPCS 规则,保证程序调用时参数的正确传递。
o 在汇编程序中使用EXPORT 伪指令声明本子程序,使其它程序可以调用此子程序。
o 在C 语言程序中使用extern 关键字声明外部函数(声明要调用的汇编子程序),即可调用此汇编子程序。
o 调用汇编的C 函数:
o 示例
#include
extern void strcopy(char *d,const char *s) //声明外部函数,即要调用的汇编
//子程序
int main(void)
{
const char *srcstr=“First string-source”; //定义字符串常量
char dstsrt[] =“Second string-destination”;//定义字符串变量
printf(“Before copying:/n”);
printf(“’%s’/n ‘%s/n,”srcstr,dststr); //显示源字符串和目标字符串的内容
strcopy(dststr,srcstr); //调用汇编子程序,R0=dststr,R1=srcstr
printf(“After copying:/n”)
printf(“’%s’/n ‘%s/n,”srcstr,dststr); //显示strcopy 复制字符串结果
return(0);
}
o 被调用汇编子程序:
AREA SCopy,CODE,READONLY
EXPORT strcopy ;声明汇编程序strcopy,以便外部程序引用
strcopy ;R0 为目标字符串的地址
;R1 为源字符串的地址 ;
LDRB R2,[R1],#1 ;读取字节数据,源地址加1
STRB R2,[R0],#1 ;保存读取的1 字节数据,目标地址加1
CMP r2,#0 ;判断字符串是否复制完毕
BNE strcopy ;没有复制完毕,继续循环
MOV pc,lr ;返回
END
汇编程序调用C程序
o 汇编程序的设置要遵循ATPCS 规则,保证程序调用时参数的正确传递.
o 在汇编程序中使用IMPORT 伪指令声明将要调用的C 程序函数.
o 在调用C 程序时,要正确设置入口参数,然后使用BL 调用.
o 汇编调用C 程序的C 函数:
int sum5(int a,lit b, int c,int d,int e)
{
return(a+b+c+d+e); //返回5 个变量的和
}
o 汇编调用C 程序的汇编程序
AREA sample, CODE,READONLY
IMPORT sum5 ;声明外部标号sum5,即C 函数sum5()
CALLSUM
STMFD SP! {LR} ;LR 寄存器放栈
ADD R1,R0,R0 ;设置sum5 函数入口参数,R0 为参数a
ADD R2,R1,R0 ;R1 为参数b,R2 为参数c
ADD R3,R1,R2,
STR R3,[SP,# -4]! ;参数e 要通过堆栈传递
ADD R3,R1,R1 ;R3 为参数d
BL sum5 ;调用sum5(),结果保存在R0
ADD SP,SP#4 ;修正SP 指针
LDMFD SP,PC ;子程序返回
END
嵌入式C编程
概述:
的优点是运行速度快、编译效率高、移
植性好和可读性强。C语言支持模块化程序设计,支持自顶
向下的结构化程序设计方法。因此在嵌入式程序设计中经
常会用到C语言程序设计。
嵌入式C语言程序设计是利用基本的C语言知识,面向
嵌入式工程实际应用进行程序设计。也就是说它首先是C语
言程序设计,因此必须符合C语言基本语法,只是它是面向
嵌入式的应用而设计的程序。
o C语言的“预处理伪指令”在嵌入式程序设计中的应用。
1、文件包含伪指令
格式:
#include <头文件名.h> ;标准头文件
#include “头文件名.h” ;自定义头文件
2、宏定义伪指令
格式:
# define 宏标识符 宏体
例:
n #define U32 unsigned int
n #define U16 unsigned short
n #define S32 int
n #define S16 short int
n #define U8 unsigned char
n #define S8 char
3、条件宏:先测试是否定义过某宏标识符,然后决定如何处理。这样做是为了避免重复定义。
格式:
#ifdef 宏标识符
#undef 宏标识符
#define 宏标识符 宏体
#else
#define 宏标识符 宏体
#endif
例:
#ifdef INCLUDE_SERIAL
#undef NUM_TTY
#define NUM_TTY N_UART_CHANNELS
#undef CONSOLE_TTY
#define CONSOLE_TTY 0
#undef CONSOLE_BAUD_RATE
#define CONSOLE_BAUD_RATE 115200
#endif
4、条件编译伪指令
格式
#if(条件表达式1)
…
#elif(条件表达式2)
…
#elif(条件表达式n)
…
#else
…
#endif
这样,编译时,编译器仅对#if()…#endif之间满足某一条
件表达式的源文件部分进行编译。
使用寄存器变量
当对一个变量频繁被读写时,需要反复访问内存,从而花费大量的存取时间。
为此,C语言提供了一种变量,即寄存器变量。这种变量存放在CPU的寄存器中,
使用时,不需要访问内存,而直接从寄存器中读写,从而提高效率。寄存器变量的
说明符是register。对于循环次数较多的循环控制变量及循环体内反复使用的变量
均可定义为寄存器变量,而循环计数是应用寄存器变量的最好候选者。
例:
WORD Addition(BYTE n)
{
register i,s=0; for(i=1;i<=n;i++)
{
s=s+i;
}
return s;
}
活用位操作 (熟练掌握)
使用C语言的位操作可以减少除法和取模的运算。在计算机程序中数
据的位是可以操作的最小数据单位,理论上可以用“位运算”来完成所有的
运算和操作,因而,灵活的位操作可以有效地提高程序运行的效率 。
例:
int i,j;
i = 879 / 16;
j = 562 % 32;
int i,j;
i = 879 >> 4;
j = 562 - (562 >> 5 << 5);
例 int Ra ;//Ra[15:16]=11
Ra &= ~(3<<15);
C语言位运算除了可以提高运算效率外,在嵌入式
系统的编程中,它的另一个最典型的应用,而且十分广
泛地正在被使用着的是位间的(&)、(|)、非(~)
操作,这跟嵌入式系统的编程特点有很大关系。
例:
rGPCDAT=(rGPCDAT&0xFFFFFFF0)|0x0E
rINTMSK&=~(BIT_TIMER1)
数据指针
在嵌入式系统的编程中,常常要求在特定的内存单元读写内容,汇编有对应的
MOV指令,而除C/C++以外的其它编程语言基本没有直接访问绝对地址的能力。在
嵌入式系统的实际调试中,多借助C语言指针所具有的对绝对地址单元内容的读写能
力。以指针直接操作内存多发生在如下几种情况:
n 某I/O芯片被定位在CPU的存储空间而非I/O空间,而且寄存器对应于
某特定地址;
n 两个CPU之间以双端口RAM通信,CPU需要在双端口RAM的特定单
元(称为mail box)书写内容以在对方CPU产生中断;
n 读取在ROM或FLASH的特定单元所烧录的汉字和英文字模。
例:
int *p = (int *)0xF000FF00 ;
*p=0xABCD;
#define rGPACON (*(volatile unsigned *)0x56000000);
rGPACON=0x1234;
关键字volatile
一般这个修饰符用来告知编译器,被修饰的变量是个“易变的”变
量(volatile的本意是“易变的”),防止编译器进行优化。将变量加上
volatile修饰,则编译器保证对此变量的读写操作都不会被优化。
用法
1、中断服务程序中修改的供其它程序检测的变量需要加volatile。
2、多任务环境下各任务间共享的标志应该加volatile。
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义。
阅读(5368) | 评论(0) | 转发(0) |