ARM汇编语言编程详解硅谷芯微嵌入式学院技术贡献网址:汇编语言使用汇编语言编写程序,它的特点是程序执行速度快,程序代码生成量少,但汇编语言是一种不易学习的编程语言,并且可读性较差,这种语言属于低级语言。每一种汇编语言对应每一款芯片,使用这种语言需要对硬件有深刻的了解。在通常情况下,可以使用汇编语言编写驱动程序、需要严格计算执行时间的程序以及需要加速执行的程序。•4.1.1ARM汇编程序的格式(1)先介绍一个例子来说明ARM汇编程序的格式。例1计算20+8,结果放入R0寄存器。AREABuf,DATA,READWRITE;声明数据段BufCountDCB20;定义一个字节单元CountAREAExample,CODE,READONLY;声明代码段ExampleENTRY;标识程序入口CODE32;声明32位ARM指令STARTLDRBR0,Count;R0=Count=20MOVR1,#8;R1=8ADDR0,R0,R1;R0=R0+R1BSTARTEND•4.1.1ARM汇编程序的格式(2)例1中定义了两个段:数据段Buf和代码段Example。数据段中定义了字节单元Count,其中Count用来保存一个被加数;代码段中包含了所有源程序代码,程序中首先读取Count字节单元的内容,然后与立即数8相加,计算结果保存到R0中。由例1可见,ARM汇编语言的源程序是分段的,由若干个段组成一个源程序。源程序的一般格式为:AREAname1,attr;声明特定的段标号语句1;语句...语句nAREAname2,attr标号语句n+1...语句n+mEND;结束符•4.1.1ARM汇编程序的格式(3)每一个段都有一个名字,并且段名是唯一的。每个段以符号AREA作为段的开始,以碰到下一个符号AREA作为该段的结束。段都有自己的属性,如是代码段(CODE)还是数据段(DATA),是只读(READONLY)还是可读写(READWRITE)?这些属性可以在attr栏中设定。注意:符号AREA和END都不能顶格写,只有标号可以而且必须顶格写。•4.1.1.1ARM汇编程序的书写格式(1)ARM汇编源程序是由若干段组成的,而一个段又是由若干个语句行组成。语句就是完成一个动作的说明。源程序中的语句可以分为以下两种类型:■指令性语句:汇编程序会把指令性语句翻译成机器代码,然后利用这些机器代码命令处理器执行某些操作。如由MOV、ADD等指令构造的语句。■指示性语句:汇编程序并不把它们翻译成机器代码,只是用来指示、引导汇编程序在汇编时进行一些操作。如由ENTRY、AREA等指令构造的语句,我们也称这些指令为伪指令。从例1可知,语句行的基本格式如下:[标号]指令操作数[;注释]•4.1.1.1ARM汇编程序的书写格式(2)在一条语句中,[]号中的内容是可选的。在书写ARM汇编程序时,需要注意以下3点:■标号必须在一行的顶格书写,其后面不要加“:”,对于变量的设置、常量的定义,其标识符必须在一行的顶格书写;而所有指令均不能顶格书写。■汇编器对标识符大小写敏感,书写标号及指令时字母大小写要一致。在ARM汇编程序时,一个ARM指令、伪指令、寄存器名可以全部为大写字母,也可以全部为小写字母,但不要大小写混合使用。■注释使用“;”。注释内容由“;”开始到此行结束,注释可以在一行的顶格书写。例2某一段错误的汇编语言程序。•4.1.1.1ARM汇编程序的书写格式(3)例2某一段错误的汇编语言程序。STARTMOVR0,#1;标号START没有顶格写ABC:MOVR1,#2;标号后不能带:MOVR2,#3;指令不允许顶格书写LOOPMovR2,#3;指令中大小写混合Bloop;无法跳转到loop标号,只有LOOP标号•4.1.1.2语句行的符号(1)任何一个汇编源程序都是由符号组成的。符号分为两大类:指令助记符和用户定义符。指令助记符包括ARM指令、伪指令等,这些符号都是预先定义好的,且具备专用的目的和功能;用户定义符是由用户在编写汇编程序时自行定义的,只在本程序中有意义,不具备通用性。本节所讲的符号特指用户定义符,符号的命名需注意以下规则:■符号由大小写字母、数字以及下划线组成。■符号不能以数字开头(局部标号除外)。■符号区分大小写,且所有字符都是有意义的。■符号在其作用域范围内必须是唯一的。■符号不能与系统内部或系统预定义的符号同名。■符号不要以指令助记符、伪指令同名。符号可以代表地址、数值、变量。当符号代表地址时又称为标号,符号代表某个特定数值时又称为符号常量,•4.1.1.2语句行的符号(2)符号代表变量时又称为变量名。所以符号有3个用途:标号、符号常量、变量名。(1)标号:标号代表一个地址,段内标号的地址在汇编时确定,而段外标号的地址值在链接时确定。根据标号的生成方式可以分为以下3种:■基于PC的标号:该标号是位于目标指令前的标号或程序中的数据定义伪指令前的标号。这种标号在汇编时将被处理成PC值加上或减去一个数字常量。它常用于表示跳转指令的目标地址,或者代码段中所嵌入的少量数据。■基于寄存器的标号:该标号通常用MAP和FILED伪指令定义,也可以用于EQU伪指令定义。这种标号在汇编时被处理成寄存器的值加上或减去一个数字常量。它常用于访问位于数据段中的数据。•4.1.1.2语句行的符号(3)■绝对地址:绝对地址是一个32位的数字量,可寻址的范围为0~232-1,可以直接寻址整个内存空间。例3标号举例。...;...表示省略的程序BSTART;程序跳转到START标号处...START;START标号,这是一个基于PC的段内标号...•4.1.1.2语句行的符号(4)(2)符号常量:在程序运行过程中,其值不能被改变的量称为常量。常量区分为3种不同的类型:■数字常量:数字常量表示某个特定的数字,如0、5、-19、0xF8都是数字常量。同一个数字常量可以有十进制数、十六进制数等多种表达方式。■字符常量:字符常量由一对单引号及中间字符串表示,标准C语言中的转义符也是也可使用。如果需要包含双引号或“$”,必须使用“”或“$$”代替。如‘H’是一个字符常量,“HelloWorld”是一个字符串常量。■布尔常量:布尔常量由括号{}和逻辑值(TRUE、FALSE)表示。逻辑真为{TRUE},逻辑假为{FALSE}。为了程序书写的方便,可以用一个标识符来代表一个常量,称这个标识符为符号常量,即标识符形式的常量。•4.1.1.2语句行的符号(5)例4用EQU伪指令定义数字符号常量。例子中定义了两个数字符号常量:T_bit和PLLCON。所以程序中用到这两个符号常量时,在程序链接时就会被相应的值0x20、0xE01FC080所替代。T_bitEQU0x20;定义数字常量T_bit,其值为0x20PLLCONEQU0xE01FC080;定义PLLCON,值为0xE01FC080•4.1.1.2语句行的符号(6)(3)变量名:变量是指存放在存储单元的操作数,并且它的值可以改变。变量名代表了一个变量,当程序中要用到变量时,只需要引用对应的变量名。实际上,变量名是一个符号地址,当程序编译链接时,系统会给每一个变量名分配一个内存地址。在程序中从变量中取值,实际上是通过变量名找到相应的内存地址,从其存储单元中读取数据。按照变量的作用范围可分为全局变量和局部变量;按照变量的数值类型可分为数字变量、字符变量和逻辑变量。根据两种类型的组合,变量共具有6种类型:全局数字变量、全局逻辑变量、全局字符串变量、局部数字变量、局部逻辑变量、局部字符串变量。具体对这些类型的变量如何声明、赋初值,将在4.1.2小节符号定义伪指令中详细介绍。•4.1.2伪指令语句(1)汇编语言程序由机器指令、伪指令和宏指令组成。伪指令不像机器指令那样在处理器运行期间由机器执行,而是在对源程序进行汇编期间由汇编工具处理的操作,它们可以完成如符号定义、数据定义、分配存储区、指示程序开始结束等功能。本小节只说明一些常用的伪指令。另外,还有一些伪指令可查看相关手册。在前面的ARM指令集章节中,已经接触了几条常用的ARM伪指令,如ADR、ADRL、LDR、NOP等。把它们和指令集一起介绍是因为它们在汇编时会被合适的机器指令代替,实现真正机器指令操作。伪指令大概可分为以下6种类型:■ARM伪指令,如ADR、LDR、NOP等,本节不再重复介绍。■符号定义伪指令。■段及段属性定义伪指令。•4.1.2伪指令语句(2)■数据定义伪指令。■汇编控制伪指令。■杂项伪指令。•4.1.2.1符号定义伪指令(1)符号定义伪指令用于定义ARM汇编程序的常量、标号和变量,对变量进行赋值等操作。符号定义伪指令包括EQU伪指令、变量声明伪指令、变量赋值伪指令。变量的声明与赋值伪指令如表4.1所示。表4.1变量声明与赋值伪指令变量类型全局变量声明伪指令局部变量声明伪指令赋值伪指令SETLSETS数字变量字符串变量逻辑变量GBLAGBLLGBLSLCLALCLLSETALCLS•4.1.2.1符号定义伪指令(2)(1)EQU:EQU用于将程序中的数字常量、标号、基于寄存器的值赋予一个等效的名称,这一点类似于C语言中的#define,可用“*”代替EQU。指令格式如下:其中,name为要定义的常量的名称;expr可以为数字常量、程序中的标号、32位地址常量、寄存器的地址值等;type指示expr的数据类型,是可选项。例5EQU伪指令的使用。nameEQUexpr{,type}T_bitEQU0x20;定义常量T_bit=0x20PLLCONEQU0xE01F000C;定义PLLCON=0xE01F000CABCDEQUlabel+8;定义标号ABCD=label+8•4.1.2.1符号定义伪指令(3)(2)变量声明伪指令:变量声明伪指令包括全局变量声明伪指令和局部变量声明伪指令。全局变量声明伪指令包括GBLA、GBLL、GBLS,局部变量声明伪指令包括LCLA、LCLL、LCLS。全局变量多用于程序体中,而局部变量用于宏定义体中。其中:■GBLA、LCLA伪指令用于声明一个数字变量,并将其初始化为0。■GBLL、LCLL伪指令用于声明一个逻辑变量,并将其初始化为{FALSE}。■GBLS、LCLS伪指令用于声明一个字符串变量,并将其初始化为空字符串。伪指令格式如下:[GB/LC]L[A/L/S]variable•4.1.2.1符号定义伪指令(4)其中,[GB/LC]L[A/L/S]为变量声明伪指令,可以为6个变量声明伪指令(GBLA、GBLL、GBLS、LCLA、LCLL、LCLS)中的任一个。variable是定义的变量名,其数据类型和作用范围由变量声明伪指令来确定,但变量名在其作用内必须唯一。例6使用全局变量。例7宏结构中使用局部变量。GBLLcodebg;声明一个全局逻辑变量codebgcodebgSETL{TRUE};设置变量为{TRUE}...MACRO;声明一个宏SENDDAT$dat;宏的原型LCLAbitno;声明一个局部数字变量,在此宏中使用...bitnoSETA8;设置变量bitno值为8...MEND•4.1.2.1符号定义伪指令(5)(3)变量赋值伪指令:变量赋值伪指令用于对已定义的全局变量或局部变量赋值,共有3条变量赋值伪指令:SETA、SETL、SETS。■SETA伪指令用于给一个全局或局部的算术变量赋值。■SETL伪指令用于给一个全局或局部的逻辑变量赋值。■SETS伪指令用于给一个全局或局部的字符变量赋值。指令格式如下:其中Variable_a、Variable_l、Variable_s就是前面全局变量或局部变量所定义的变量名。expr_a为赋值的常数;expr_l为逻辑值,即{TRUE}或{FALSE};expr_s为赋值的字符串。Variable_aSETAexpr_aVariable_lSETLexpr_lVariable_sSETSexpr_s•4.1.2.1符号定义伪指令(6)例8