第五章基于ARM的嵌入式程序设计_第1页
第五章基于ARM的嵌入式程序设计_第2页
第五章基于ARM的嵌入式程序设计_第3页
第五章基于ARM的嵌入式程序设计_第4页
第五章基于ARM的嵌入式程序设计_第5页
已阅读5页,还剩75页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1第五章基于ARM的嵌入式程序设计5.1ARM汇编语言的伪操作、宏指令与伪指令5.2ARM汇编语言程序设计5.3

嵌入式C语言程序设计基础5.4

嵌入式C语言程序设计实例5.5嵌入式C语言程序设计技巧5.6C与汇编语言混合编程25.1ARM汇编语言的伪操作、宏指令与伪指令5.1.1两种常见的ARM编译开发环境5.1.2ADS编译环境下的伪操作和宏指令5.1.3GNU编译环境下的伪操作和宏指令5.1.4ARM汇编语言的伪指令

35.1.1两种常见的ARM编译开发环境ADS/SDTIDE开发环境:它由ARM公司开发,使用了CodeWarrior公司的编译器;集成了GNU开发工具的IDE开发环境::它由GNU的汇编器as、交叉编译器gcc、和链接器ld等组成。4GNU计划:译为“革奴计划”,是由理查德·斯托曼在1983年9月27日公开发起的。它的目标是创建一套完全自由的操作系统。理查德·斯托曼最早是在net.unix新闻组上公布该消息,并附带一份《GNU宣言》解释为何发起该计划的文章,其中一个理由就是要“重现当年软件界合作互助的团结精神”。GNU:“GNU'sNotUnix”的缩写,发音为“Guh-NOO”(/ˈgnuː/

)。宗旨:自由软件计划5伪操作:ARM汇编语言程序里的特殊指令助记符,主要作用是完成汇编程序各种准备工作,在源程序进行编译时由汇编程序处理,而不是在计算机运行期间由机器执行。宏指令:是一段独立的代码,可插在源程序中,它通过伪操作来定义。通过实际指令替代宏体实现相关的一段代码。伪指令:ARM汇编语言里的特殊指令助记符,不在处理器运行期间由机器执行。它们在编译时将被合适的机器指令替代。65.1.2ADS编译环境下的伪操作和宏指令

ADS编译环境下的伪操作可分为以下几类:符号定义(SymbolDefinition)伪操作数据定义(DataDefinition)伪操作汇编控制(AssemblyControl)伪操作信息报告(Reporting)伪操作其他(Miscellaneous)伪操作

7符号定义伪操作伪操作语法格式作用GBLAGBLAVariable声明一个全局的算术变量,并将其初始化成0。GBLLGBLLVariable声明一个全局的逻辑变量,并将其初始化成{FALSE}。GBLSGBLSVariable声明一个全局的字符串变量,并将其初始化成空串“”。LCLALCLAVariable声明一个局部的算术变量,并将其初始化成0。LCLLLCLLVariable声明一个局部的逻辑变量,并将其初始化成{FALSE}。LCLSLCLSVariable声明一个局部的串变量,并将其初始化成空串“”。SETAVariableSETAexpr给一个全局或局部算术变量赋值。SETLVariableSETLexpr给一个全局或局部逻辑变量赋值。SETSVariableSETSexpr给一个全局或局部字符串变量赋值。RLISTnameRLIST{listofregisters}为一个通用寄存器列表定义名称。CNnameCNexpr为一个协处理器的寄存器定义名称。CPnameCPexpr为一个协处理器定义名称。DN/SNnameDN/SNexprDN/SN为一个双精度/单精度的VFP寄存器定义名称。FNnameFNexpr为一个FPA浮点寄存器定义名称。8数据定义伪操作

伪操作语法格式作用LTORGLTORG声明一个数据缓冲池(也称为文字池)的开始。MAPMAPexpr{,base-register}定义一个结构化的内存表(StorageMap)的首地址。FIELD{label}FIELDexpr定义一个结构化内存表中的数据域。SPACE{label}SPACEexpr分配一块连续内存单元,并用0初始化。DCB{label}DCBexpr{,expr}分配一段字节内存单元,并用expr初始化。DCD/DCDU{label}DCDexpr{,expr}…分配一段字内存单元。DCDO{label}DCDOexpr{,expr}…分配一段字对齐的字内存单元。DCFD/DCFDU{label}DCFD{U}fpliteral{,fpliteral}…为双精度的浮点数分配字对齐的内存单元。DCFS/DCFSU{label}DCFS{U}fpliteral{,fpliteral}…为单精度的浮点数分配字对齐的内存单元。DCI{label}DCIexpr{,expr}…在ARM代码中分配一段字对齐的内存单元;在Thumb代码中,分配一段半字对齐的半字内存单元。DCQ/DCQU{label}DCQ{U}{﹣}literal{,{﹣}literal}…分配一段以双字(8个字节)为单位的内存DCW/DCWU{label}DCW{U}expr{,expr}…DCW用于分配一段半字对齐的半字内存单元。9汇编控制伪操作伪操作语法格式作用IF,ELSE及ENDIFIFlogicalexpression…{ELSE…}ENDIF能够根据条件把一段源代码包括在汇编语言程序内或者将其排除在程序之外。WHILE及WENDWHILElogicalexpression…WEND能够根据条件重复汇编相同的一段源代码。MACRO、MEND及MEXITMACRO{$label}macroname{$parameter{,$parameter}…}… ;宏代码MENDMACRO标识宏定义的开始,MEND标识宏定义的结束。MERIT用于从宏中跳转出去。用MACRO和MEND定义的一段代码,称为宏定义体。通过宏名称来调用宏。10信息报告伪操作

伪操作语法格式作用ASSERTASSERTlogicalexpression对汇编程序的第二遍扫描中,如果其中ASSERT中条件不成立,ASSERT伪操作将报告该错误信息。INFOINFOnumeric-expression,string-expression在汇编处理过程的第一遍扫描或者第二遍扫描时INFO伪操作报告诊断信息。OPTOPTn通过OPT伪操作可以在源程序中设置列表选项。TTLTTLtitle在列表文件的每一页的开头插入一个标题。SUBTSUBTsubtitle在列表文件的每一页的开头插入一个子标题。11其他伪操作伪操作语法格式作用CODE16CODE16告诉汇编编译器后面的指令序列为16位的Thumb指令CODE32CODE32告诉汇编编译器后面的指令序列为32位的ARM指令。EQUnameEQUexpr{,type}为数字常量、基于寄存器的值和程序中的标号(基于PC的值)定义一个字符名称。AREAAREAsectionname{,attr}{,attr}…定义一个代码段或者数据段。ENTRYENTRY指定程序的入口点。ENDEND告诉编译器已经到了源程序结尾。ALIGNALIGN{expr{,offset}}通过添加补丁字节使当前位置满足一定的对齐方式。EXPORT/GLOBALEXPORTsymbol{[WEAK]}声明一个符号可以被其他文件引用,相当于声明了一个全局变量。IMPORTIMPORTsymbol{[WEAK]}告诉编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号。EXTERNEXTERNsymbol{〔WEAK〕}告诉编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号。GET/INCLUDEGETfilename

将一个源文件包含到当前源文件中,并将被包含的文件在其当前位置进行汇编处理。INCBININCBINfilename将一个文件包含到当前源文件中,被包含的文件不进行汇编处理。KEEPKEEP{symbol}告诉编译器将局部符号包含在目标文件的符号表中。NOFPNOFP禁止源程序中包含浮点运算指令。REQUIREREQUIRElable指定段之间的相互依赖关系。RNnameRNexpr为一个特定的寄存器定义名称。ROUT{name}ROUT定义局部变量的有效范围。125.1.3GNU编译环境下的伪操作和宏指令

GNU编译环境下的伪操作可分为以下几类:常量编译控制伪操作汇编程序代码控制伪操作宏及条件编译控制伪操作其他伪操作13常量编译控制伪操作

伪操作语法格式作用.byte.byteexpr{,expr}…分配一段字节内存单元,并用expr初始化。.hword/.short.hwordexpr{,expr}…分配一段半字内存单元,并用expr初始化。.ascii.asciiexpr{,expr}…定义字符串expr(非零结束符)。.asciz/.string.ascizexpr{,expr}…定义字符串expr(以/0为结束符)。.float/.single.floatexpr{,expr}…定义一个32bitIEEE浮点数expr。.double.doubleexpr{,expr}…定义64bitIEEE浮点数expr。word/.long/.int.wordexpr{,expr}…分配一段字内存单元,并用expr初始化。.fill.fillrepeat{,size}{,value}分配一段字节内存单元,用size长度value填充repeat次。.zero.zerosize分配一段字节内存单元,并用0填充内存。.space/.skip.spacesize{,value}分配一段内存单元,用value将内存单元初始化。14汇编程序代码控制伪操作伪操作语法格式作用.section.sectionexpr定义域中包含的段。.text.text{subsection}将操作符开始的代码编译到代码段或代码段子段。.data.data{subsection}将操作符开始的数据编译到数据段或数据段子段。.bss.bss{subsection}将变量存放到.bss段或.bss段的子段。.code16/.thumb.code16.thumb表明当前汇编指令的指令集选择Thumb指令集。.code32/.arm.code32.arm表明当前汇编指令的指令集选择ARM指令集。.end.end标记汇编文件的结束行,即标号后的代码不作处理。.include.include“filename”将一个源文件包含到当前源文件中。.align/.balign.align{alignment}{,fill}{,max}通过添加填充字节使当前位置满足一定的对齐方式。15宏及条件编译控制伪操作

伪操作语法格式作用.macro、.exitm及.endm.macroacroname{parameter{,

parameter}…}….endm.macro伪操作标识宏定义的开始,.endm标识宏定义的结束。用.macro及.endm定义一段代码,称为宏定义体。.exitm伪操作用于提前退出宏。.ifdef,.else及.endif.ifdefcondition….else….endif当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。其中else可以缺省。16其他伪操作伪操作语法格式作用.eject.eject在汇编符号列表文件中插入一分页符。.list.list产生汇编列表(从.list到.nolist)。.nolist.nolist表示汇编列表结束处。.title.title“heading”使用“heading”作为标题。.sbttl.sbttl“heading”使用“heading”作为子标题。.ltorg.ltorg在当前段的当前地址(字对齐)产生一个文字池。.req.reqname,expr为一个特定的寄存器定义名称。.err.err使编译时产生错误报告。.print.printstring打印信息到标准输出。.fail.failexpr编译汇编文件时产生警告。175.1.4ARM汇编语言的伪指令

伪指令语法格式作用ADRADR{cond}

register,expr将基于PC或基于寄存器的地址值读取到寄存器中。小范围的地址读取。ADRLADRL{cond}register,expr将基于PC或基于寄存器的地址值读取到寄存器中。中等范围的地址读取。LDRLDR{cond}register,=[expr|label-expr]将一个32位的立即数或者一个地址值读取到寄存器中。大范围的地址读取。NOPNOP在汇编时将被替换成ARM中的空操作。185.2ARM汇编语言程序设计5.2.1ARM汇编中的文件格式5.2.2ARM汇编语言语句格式5.2.3ARM汇编语言编程的重点5.2.4ARM汇编程序实例195.2.1ARM汇编中的文件格式

ARM源程序文件(可简称为源文件)可以由任意一种文本编辑器来编写程序代码,它一般为文本格式。在ARM程序设计中,常用的源文件可简单分为以下几种:源程序文件文件名说

明汇编程序文件*.S用ARM汇编语言编写的ARM程序或Thumb程序。C程序文件*.C用C语言编写的程序代码。头文件*.H为了简化源程序,把程序中常用到的常量命名、宏定义、数据结构定义等等单独放在一个文件中,一般称为头文件。205.2.2ARM汇编语言语句格式

ARM汇编语言语句格式如下所示:{symbol}{instruction|directive|pseudo-instruction}{;comment}

其中:instruction为指令。directive为伪操作。pseudo-instruction为伪指令。symbol为符号。comment为语句的注释。

21ARM汇编语言中的符号符号:可以代表地址、变量和数字常量。符号的命名规则:符号由大小写字母、数字以及下划线组成局部标号以数字开头,其他符号都不能以数字开头符号是区分大小写的符号在其作用范围内必须唯一程序中的符号不能与系统内部变量或者系统预定义的符号同名程序中的符号通常不要与指令助记符或者伪操作同名22变量: 3种变量:数字变量、逻辑变量、串变量数字常量:

3种表示方式:

十进制数,如:43、6、112等

十六进制数,如:0x3425、0xEF、0x1等

n进制数,用n_XXX表示

n为2~9,XXX为具体数 如:2_01001101、8_432623

标号:

表示程序中的指令或地址的符号,3种:基于PC的标号基于寄存器的标号绝对地址局部标号:主要用于局部范围代码。由一个0~99数字和符号组成定义的格式:N{routname}

其中:N为0~99数字,routname为符号引用的格式:%{F|B}{A|T}N{routname}

其中:%表示引用操作、F指示编译器只先向前搜索、B指示编译器只向后搜索、A指示编译器搜索宏的所有嵌套层次、T指示编译器搜索宏的当前层次24ARM汇编语言中的表达式优先级:括号内的表达式优先级最高各种操作符有一定的优先级相邻的单目操作符的执行顺序为由右到左,单目操作符优先级高于其他操作符优先级相同的双目操作符执行顺序为由左到右25字符串表达式相关操作符LEN:返回字符串的长度

:LEN:A A为字符串变量 例: GBLSSTR STRSETS“AAA” :LEN:STR ;LEN=3CHR:将0~255之间的整数作为含一个ASCII字符的串

:CHR: A A为某一字符的ASCII值26字符串表达式相关操作符STR:将一个数字量或者逻辑表达式转换成串。对于32为的数字量,STR将其转换成8个十六进制数组成的串;对于逻辑表达式,STR将其转换成字符串T或F。

:STR: A A为数字量或逻辑表达式 例:

GLBA A1 SETA A115 :STR: A1 ;将A1转换为”0000000F”27字符串表达式相关操作符LEFT:返回一个字符串最左端一定长度的子串。

A :LEFT: B A为源字符串;B为数字量,表示返回字符个数 例:

GBLS STR1 GBLS STR2 SETS STR1 “AAAABBBB” SETS STR2 STR1 :LEFT: 3

结果:STR2为”AAA”28字符串表达式相关操作符RIGHT:返回一个字符串最右端一定长度的子串

A :RIGHT: B A为源字符串;B为数字量,表示返回的字符个数 例:

GBLS STR1 GBLS STR2 SETS STR1 “AAABBB” SETS STR2 STR1 :RIGHT: 3

结果:STR2为”BBB”29字符串表达式相关操作符CC:连接2个字符串

A :CC: B A为第一个源字符串;B为第二个源字符串 例:

GBLS STR1 GBLS STR2 STR1 SETS “AAACCC” STR2 SETS “BBB” :CC: (STR1 :LEFT: 3)

结果:STR2为”BBBAAA”30数字表达式相关操作符NOT:按位取反

:NOT: A

A为32位的数字量+、-、×、/及MOD:算术操作符

A+B A、B的和

A-B A、B的差

A×B A、B的积

A/B A除以B的商

A:MOD:B A除以B的余数31数字表达式相关操作符ROL、ROR、SHL、SHR:循环移位操作

A:ROL:B 将整数A循环左移B位

A:ROR:B 将整数A循环右移B位

A:SHL:B 将整数A左移B位,空位补0 A:SHR:B 将整数A右移B位,空位补0AND、OR、EOR:按位逻辑操作符

A:AND:B 将数字表达式A和B按位作逻辑“与”操作

A:OR:B 将数字表达式A和B按位作逻辑“或”操作

A:EOR:B 将数字表达式A和B按位作逻辑“异或”操作32逻辑表达式关系操作符:

A=B A等于B A>B A大于B A>=B A大于或等于B A<B A小于B A/=BA<>B A不等于B逻辑操作符:

:LNOT:A A的值取反

A:LAND:B A和B的逻辑“与”

A:LOR:B A和B的逻辑“或”

A:LEOR:B A和B的逻辑“异或”33ARM汇编语言程序格式ARM汇编语言是以段(section)为单位来组织源文件的。段是相对独立的、具有特定名称的、不可分割的指令或者数据序列。段又可以分为代码段和数据段,代码段存放执行代码,数据段存放代码运行时需要用到的数据。一个ARM源程序至少需要一个代码段,大的程序可以包含多个代码段和数据段。

34举例说明ARM汇编语言源程序的基本结构

AREAEXAMPLE,CODE,READONLYENTRYstartMOVR0,#10MOVR1,#3ADDR0,R0,R1END本程序的程序体部分实现了一个简单的加法运算。

355.2.3ARM汇编语言编程的重点ARM数据处理操作设置条件码汇编语言子程序调用及返回跳转表思想ARM与Thumb之间的状态转换及函数的互相调用36ARM数据处理操作ARM中数据的处理有以下三种形式:简单的寄存器操作立即数操作寄存器移位操作其中32位立即数在32位指令中的编码以及ARM特有的寄存器移位操作是数据处理方面的难点。37设置条件码ARM的任何数据处理指令都能通过增加“S”操作码来设置条件码(N,Z,C和V)。

条件执行

ARM指令集不同寻常的特征是每条指令(除了某些v5T指令)都可以是条件执行的。

条件转移

在程序中可以通过条件码的使用让微处理器决定是否进行转移,还可用来控制循环的退出。

38汇编语言子程序调用及返回

子程序的调用在ARM汇编语言中,子程序调用是通过BL指令来完成的。BL指令的语法格式如下:BLsubname其中,subname是被调用的子程序的名称。子程序的返回在返回调用子程序时,转移链接指令保存到LR寄存器(R14)中的值需要拷贝回程序寄存器PC(R15)。

39跳转表思想在程序设计中,有时为使程序完成一定的功能,需要调用一系列子程序中的一个,而决定究竟调用哪一个由程序的计算值确定。跳转表是解决该问题的有效方案。跳转表是利用程序计数器PC在通用寄存器文件中的可见性来实现的,如下例所示:

40ARM与Thumb间的状态转换

状态切换的实现

ARM/Thumb之间的状态切换是通过一条专用的转移交换指令BX来实现的。BX利用Rn寄存器中目的地址值的最后一位来判断跳转后的状态。当最后一位为0时,表示转移到ARM状态;当最后一位为1时,表示转移到Thumb状态,如下图所示。

41ARM与Thumb间的函数的相互调用ARM/Thumb之间的函数调用

在同一状态下的子程序调用,通常只需要一条指令实现调用:

BLfunction实现返回也只需要从LR恢复PC即可:

MOVPC,LR在不同状态下的子程序调用中,就需要进行状态之间的切换,需要考虑到以下几点:需要由BX来切换状态,因为BL不能完成状态切换。需要在BX之前先保存好LR,BX不能自动保存返回地址到LR。需要用“BXLR”来返回,不能使用“MOVPC,LR”,返回时要仔细考虑保存在LR中最低位的内容是否正确。425.2.4ARM汇编程序实例

简单的ARM指令程序数据块复制

利用跳转表实现程序跳转

435.3嵌入式C语言程序设计基础5.3.1C语言“预处理伪指令”在嵌入式程序 设计中的应用5.3.2嵌入式程序设计中的函数及函数库5.3.3嵌入式程序设计中常用的C语言语句5.3.4嵌入式程序设计中C语言的变量、数 组、结构、联合

445.3.1C语言“预处理伪指令”在嵌入式程序设计中的应用

“预处理命令”可以改进程序设计的环境,提高编程效率,一般以#号打头,可分为以下三种:文件包含宏定义条件编译45文件包含文件包含伪指令可将头文件包含到程序中,头文件中定义的内容包括符号常量、复合变量原型、用户定义的变量类型原型和函数的原型说明等。编译器编译预处理时用文件包含的正文内容替换到实际程序中。文件包含伪指令的格式#include<头文件名.h>;标准头文件#include“头文件名.h”

;自定义头文件#include宏标识符46宏定义宏定义伪指令分为:简单宏、参数宏、条件宏、预定义宏及宏释放。

简单宏:#define宏标识符宏体参数宏:#define宏标识符(形式参数表)

宏体条件宏定义:

#ifdef 宏标识符 #ifndef宏标识符

#undef 宏标识符 #define 宏标识符宏体

#define 宏标识符宏体 #else #else #undef 宏标识符

#define 宏标识符宏体 #define 宏标识符宏体

#endif #endif47条件编译条件编译伪指令是写给编译器的,指示编译器在满足某一条件时仅编译源文件中与之相应的部分。其格式如右框中所示:

#if(条件表达式1)…#elif(条件表达式2)…#elif(条件表达式n)…#else…#endif485.3.2嵌入式程序设计中的函数及函数库

函数是C语言程序设计的核心。一个较大的C语言程序一般是由一个主函数和若干个子函数组成,每个函数完成一个特定的功能。函数之间也可以相互调用。函数的格式:

定义性说明格式:[存储类说明符]类型说明符[修饰符]标识符(参数表){函数体}

原型说明格式:extern类型说明符[修饰符]标识符(参数表){函数体}

49嵌入式程序设计中的函数及函数库函数库是为了减少编程工作量,将一些常用的功能的函数放在函数库中供公共使用.它包括C的标准库函数,也包括一些用户自己编写非标准库。例如,

44blib.h

是根据基于S3C44B0X处理器的开发板及其功能模块编写的一个C语言函数库。它不属于C语言的标准库。505.3.3嵌入式程序设计中常用的C语言语句

C语言语句格式为:

[标号:]语句[;]

C语言语句很多,常用到的有以下几种:条件语句

swith语句循环语句

515.3.4嵌入式程序设计中C语言的变量、数组、结构、联合

变量

[存储类型]类型说明符[修饰符]标识符[=初值][,标识符[=初值]]…;数组一维数组:类型说明符标识符[常量表达式][={初值,初值,…}];char标识符[]=“字符串”;二维数组:类型说明符标识符[m][n][={{初值表},{初值表}…}];指针数组和数组指针类型说明符*标识符[常量表达式][={地址,地址,…}];类型说明符(*标识符)[][=数组标识符];

52嵌入式程序设计中C语言的变量、数组、结构、联合结构说明

[存储类说明符]struct[结构原型名]{类型说明标识符[,标识符…];类型说明标识符[,标识符…];

…}标识符[={初值表}[,标识符[={初值表}]…];

53嵌入式程序设计中C语言的变量、数组、结构、联合联合说明

[存储类说明符]union[联合原型名]{类型说明符标识符[,标识符…];类型说明符标识符[,标识符…];

}标识符

={初值表}[,标识符[={初值表}]…];

545.4嵌入式C语言程序设计实例*5.4.1S3VCE40开发板测试程序实例5.4.2嵌入式C语言程序编写的简单构架*5.4.3Flash测试代码介绍555.4.2嵌入式C语言程序编写的简单构架#include预编译指令

一个C语言代码,一般要用#include编译指令将所需要的头文件加到该程序中,这是很有必要的,尤其是对编写较大的程序代码时。随后是定义一些外部变量,并对程序中的函数进行声明。主函数main()的编写;在每一个C语言代码中,一定要有一个main()函数,在该函数中完成该程序文件所要完成的各个功能,一般是通过调用各个子函数来完成。当然,它也可以调用其他文件中的函数。完成相应功能的各个功能函数的编写。各个函数之间可以相互调用。

565.5嵌入式C语言程序设计技巧5.5.1变量定义5.5.2参数传递5.5.3循环条件575.5.1变量定义

在变量声明的时候,最好把所有相同类型的变量放在一起定义,这样可以优化存储器布局。由下例可以看出:对于局部变量类型的定义,使用short或char来定义变量并不是总能节省存储空间。有时使用32位int或unsingedint局部变量更有效率一些,如下图所示:变量定义中,为了精简程序,程序员总是竭力避免使用冗余变量。但有时使用冗余变量可以减少存储器访问的次数这可以提高系统性能。

585.5.2参数传递

为了使单独编译的C语言程序和汇编程序能够互相调用,定义了统一的函数过程调用标准ATPCS。ATPCS定义了寄存器组中的{R0~R3}作为参数传递和结果返回寄存器,如果参数数目超过四个,则使用堆栈进行传递。内部寄存器的访问速度是远远大于存储器的,所以要尽量使参数传递在寄存器里面进行,即应尽量把函数的参数控制在四个以下。595.5.3循环条件

计数循环是程序中十分常用的流程控制结构,一般有以下两种形式:for(loop=1;loop<=limit;loop++)

for(loop=limit;loop!=0;loop--)这两种循环形式在逻辑上并没有效率差异,但是映射到具体的体系结构中时,就产生了很大的不同,如下图所示。

605.6C与汇编语言混合编程5.6.1ATPCS介绍

寄存器的使用 数据栈的使用 参数的传递5.6.2内嵌汇编5.6.3C和ARM汇编程序间相互调用615.6.1ATPCS介绍

ATPCS(ARM-ThumbProduceCallStandard)是ARM程序和Thumb程序中子程序调用的基本规则,目的是为了使单独编译的C语言程序和汇编程序之间能够相互调用。这些基本规则包括子程序调用过程中寄存器的使用规则、数据栈的使用规则和参数的传递规则。

62寄存器的使用规则子程序间通过寄存器R0~R3来传递参数,这时,寄存器R0~R3可记作A1~A4。被调用的子程序在返回前无需恢复寄存器R0~R3的内容在子程序中,使用寄存器R4~R11来保存局部变量,这时,寄存器R4~R11可记作V1~V8。如果在子程序中使用到了寄存器V1~V8中的某些寄存器,则子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值;在Thumb中,只使用R4~R7。寄存器R12用作程序间的scratch寄存器(用于保存SP,在函数返回时使用该寄存器出栈),记作IP。63寄存器的使用规则寄存器R13用作数据栈指针,记作SP。在子程序中R13不能用作其他用途。寄存器SP在进入子程序时的值和退出子程序的值必须相等寄存器R14称为链接寄存器,记作LR。它用于保存程序的返回地址。如果在子程序中保存了返回地址,则寄存器R14可用作其他用途寄存器R15是程序计数器,记作PC。它不能用作其他用途。64寄存器别名特殊名使用规则R0a1

参数/结果/scratch寄存器1R1a2

参数/结果/scratch寄存器2R2a3

参数/结果/scratch寄存器3R3a4

参数/结果/scratch寄存器4R4v1

ARM状态局部变量寄存器1R5v2

ARM状态局部变量寄存器2R6v3

ARM状态局部变量寄存器3R7v4wrARM状态局部变量寄存器4Thumb状态工作寄存器R8v5

ARM状态局部变量寄存器5R9v6sbARM状态局部变量寄存器6,在支持RWPI的ATPCS中为静态基址寄存器R10v7slARM状态局部变量寄存器7,在支持数据栈检查的ATPCS中为数据栈限制指针R11v8fpARM状态局部变量寄存器8/帧指针R12

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

sp数据栈指针R14

lr连接寄存器R15

pc程序计数器65数据栈的使用规则根据堆栈指针指向位置的不同和增长方向的不同可以分为以下4种数据栈:

FD(FullDescending)

满递减

ED(EmptyDescending)空递减

FA(FullAscending)

满递增

EA(EmptyAscending)

空递增ATPCS规定数据栈为FD(满递减)类型,并且对数据栈的操作是8字节对齐的。

66参数的传递规则参数个数固定的子程序参数传递规则:第一个整数参数,通过寄存器R0~R3来传递。其他参数通过数据栈传递。

参数个数可变的子程序参数传递规则:当参数不超过4个时,可以使用寄存器R0~R3来传递参数;当参数超过4个时,还可以使用数据栈来传递参数子程序结果返回规则

结果为一个32位的整数时,可以通过寄存器R0返回;结果为一个64位整数时,可以通过寄存器R0和R1返回,依次类推。675.6.2内嵌汇编

在C程序中嵌入汇编程序可以实现一些高级语言没有的功能,并可以提高执行效率。armcc和armcpp内嵌汇编器支持完整的ARM指令集;tcc和tcpp用于Thumb指集。内嵌的汇编指令包括大部分的ARM指令和Thumb指令,但是不能直接引用C的变量定义,数据交换必须通过ATPCS进行。嵌入式汇编在形式上表现为独立定义的函数体。68内嵌汇编指令的语法格式

__asm(“指令[;指令]”);

ARMC汇编器使用关键字“__asm”。如果有多条汇编指令需要嵌入,可以用“{}”将它们归为一条语句。如:__asm{指令[;指令]…[指令]}需要特别注意的是__asm是两个下划线。

69内嵌的汇编指令的特点

操作数可以是寄存器、常量或C表达式。它们可以是char、short或者int类型,而且是作为无符号数进行操作。内嵌的汇编指令中使用物理寄存器有一些限制。常量前的符号“#”可以省略只有指令B可以使用C程序中的标号,指令BL不能使用C程序中的标号。

不支持汇编语言中用于内存分配的伪操作。指令中如果包含常量操作数。则该指令可能会被汇编器展开成几条指令

70内嵌汇编器与armasm汇编器的区别

内嵌汇编器不支持通过“·”指示符或PC获取当前指令地址;

不支持LDRRn,=expression伪指令,而使用MOVRn,

expression指令向寄存器赋值;

不支持标号表达式;不支持ADR和ADRL伪指令;不支持BX和BLX指令;不可以向PC赋值;使用0x前缀替代“&”表示十六进制数。

71内嵌汇编注意事项

必须小心使用物理寄存器,如R0~R3,LR和PC。

__arm __arm{ {MOVR0,x MOVvar,xADDy,R0,x/y ADDy,var,x/y} }72不要使用寄存器寻址变量。

intbac_f(intx) { __arm { ADDR0,R0,#1 } returnx } intbac_f(intx) { __arm { ADDx,x,#1 } returnx }intf(intx){ __asm { STMFDSP!,{R0} ADDR0,x,#1 EORx,R0,x LDMFDSP!,{R0} } returnx;}73编译器自动保存和恢复用到的

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论