740 likes | 905 Views
第六章 基于 ARM 的嵌入式程序设计. 6.1 ARM 汇编语言的伪操作、宏指令与伪指令. 6.1.1 两种常见的 ARM 编译开发环境 6.1.2 ADS 编译环境下的伪操作和宏指令 6.1.3 GNU 编译环境下的伪操作和宏指令 6.1.4 ARM 汇编语言的伪指令. ARM 汇编语言的伪操作、宏指令与伪指令. 伪操作 —— 为汇编程序所用,在源程序进行汇编时由汇编程序处理,只在汇编过程起作用,不参与程序运行。 宏指令 —— 通过伪操作定义的一段独立的代码。在调用它时将宏体插入到源程序中。
E N D
6.1 ARM汇编语言的伪操作、宏指令与伪指令 • 6.1.1 两种常见的ARM编译开发环境 • 6.1.2 ADS编译环境下的伪操作和宏指令 • 6.1.3 GNU编译环境下的伪操作和宏指令 • 6.1.4 ARM汇编语言的伪指令
ARM汇编语言的伪操作、宏指令与伪指令 • 伪操作——为汇编程序所用,在源程序进行汇编时由汇编程序处理,只在汇编过程起作用,不参与程序运行。 • 宏指令——通过伪操作定义的一段独立的代码。在调用它时将宏体插入到源程序中。 • 伪指令——是汇编语言程序里的特殊指令助记符,在汇编时被合适的机器指令替代。
6.1.1两种常见的ARM编译开发环境 • ADS/SDT IDE开发环境:它由ARM公司开发,使用了CodeWarrior公司的编译器; • EmbestIDE开发环境:集成了GNU开发工具的IDE开发环境,由GNU的汇编器as、交叉编译器gcc、和链接器ld等组成。 • 这两种开发环境的编译器都完全支持ARM指令集,但伪指令不同,代码风格不同。
6.1.2ADS编译环境下的伪操作和宏指令 • ADS编译环境下的伪操作可分为以下几类: • 符号定义(Symbol Definition)伪操作 • 数据定义(Data Definition)伪操作 • 汇编控制(Assembly Control)伪操作 • 信息报告(Reporting)伪操作 • 其他(Miscellaneous)伪操作
符号定义伪操作 • GBLA,GBLL,GBLS:声明全局变量。 • LCLA,LCLL,LCLS:声明局部变量。 • SETA,SETL,SETS:给变量赋值。 • RLIST:为通用寄存器列表定义名称。
1、GBLA,GBLL,GBLS • 用于声明一个ARM程序中的全局变量,并将其初始化。 • GBLA:声明一个全局算术变量,并初始化成0。 • GBLL:声明一个全局逻辑变量,并初始化成{FALSE}。 • GBLS:声明一个全局串变量,并初始化成空串“”。 • 语法格式: <gblx> variable 全局变量的作用范围为:包含该变量的源程序,全局变量名称在作用范围内必须唯一。
2、LCLA,LCLL,LCLS • 用于声明一个ARM程序中的局部变量,并将其初始化。 • LCLA:声明一个局部算术变量,并初始化成0。 • LCLL:声明一个局部逻辑变量,并初始化成{FALSE}。 • LCLS:声明一个局部串变量,并初始化成空串“”。 • 语法格式: <lclx> variable 局部变量的作用范围为:包含该局部变量的宏代码的一个实例
3、 SETA,SETL,SETS • 用于给ARM程序中的变量赋值。 • SETA:给一个算术变量赋值。 • SETL:给一个逻辑变量赋值。 • SETS:给一个串变量赋值。 • 语法格式: variable <setx> expression expression:赋给变量的值。 在向变量赋值前,必须先声明该变量。
;声明一个全局串变量s1 ;声明一个全局串变量s2 • 例: • GBLS s1 • GBLS s2 • LCLS s3 • S1 SETS “strings1” • S2 SETS “strings2” • S3 SETS s1:CC:S2 • GBLL logic • logic SETL {TRUE} • LCLA arithmetic • arithmetic SETA 0xff ;声明一个局部串变量s3 ; 串变量s1赋值为“strings1” ; 串变量s2赋值为” strings2” ; 串变量s3赋值 ;声明一个全局逻辑变量logic ; 变量logic赋初值为{TRUE} ;声明一个局部算术变量 ; 变量arithmetic赋初值为oxff
4、RLIST • 为一个通用寄存器列表定义名称。定义的名称可以在LDM/STM指令中使用。 • 语法格式: name RLIST {list-of-registers} name:寄存器列表名称。 {list-of-registers}:通用寄存器列表。 例: context RLIST {R0-R6};将寄存器列表名称定义为context LDMDF SP! ,context ;加载寄存器列表
数据定义伪操作 • LTORG:声明一个数据缓冲池的开始。 • SPACE:分配一块内存单元,并用0初始化。 • MAP:定义一个结构化的内存表的首地址。 • FIELD:定义结构化内存表中的一个数据域。 • DCB:分配一段字节内存单元,并初始化。 • DCD、DCDU:分配一段字内存单元,并初始化。
1、 LTORG • 用于声明一个数据缓冲池(文字池)的开始。 • 语法格式: • LTORG • 例:start BL func …… func LDR R1,=0x8000 ;子程序 …… MOV PC,LR ;子程序返回 LTORG ;定义数据缓冲池&0x8000 Data SPACE 4200 ;从当前位置开始分配4200字节的内 存单元,并初始化为0。 END 默认数据缓冲池为空
注意: • LTORG伪操作通常放在无条件跳转指令之后,或者子程序返回指令之后,这样处理器不会错误地将数据缓冲池中的数据当作指令来执行。 • 通常ARM汇编编译器把数据缓冲池放在代码段的最后面,即下一个代码段开始之前,或者END伪操作之前。
2、SPACE——也可以用符号“%”表示 • 用于分配一块连续的内存单元供程序使用,并且将这个内存单元的内容初始化为0。 • 语法格式: • {label} SPACE expr • 或 {label} % expr • 其中: • {label} :是一个标号,可选项。 • expr:数值表达式,用来规定内存单元的字节数。 • 例: • AREA DataSpace,Data,READWRITE • data1 SPACE 256 ;定义大小为256字节的数据区
3、MAP、FIELD • 使用MAP和FIELD伪操作描述数据结构。 • MAP用于定义数据结构的起始地址; • FIELD用于定义结构中的字段(各数据域的字节长度),并可为每一个数据域指定一个标号,其它指令可引用此标号。 • 注: • MAP和FIELD伪操作只是定义数据结构并不实际分配内存单元。
(1)MAP ——也可以用符号”^”表示 • 语法格式: • MAP expr {,base-register} • 或 ^ expr {,base-register} • 其中: • expr:数据表达式或程序中的标号(使用MAP之 前已定义)。 • base-register:基址寄存器 • 若指令中没有此项时,expr为结构的首地址。(基于绝对地址的数据结构) • 若指令中包含此项时,expr+base-register的和为结构首地址。(基于相对地址的数据结构)
例: • MAP 0xC0008 ;数据结构存放的起始地址是0xC0008 • ^ datalabel+64 ;数据结构存放的起始地址是datalabel+64 ;(datalabel已定义过) • MAP 0x10,R9 ;数据结构存放的起始地址是R9中存放的数值加上0x10
(2)FIELD ——也可以用符号”#”表示 • 用户定义数据结构中的字段。 • 语法格式: {label} FIELD expr 或 {label} # expr • 其中: • {label}:字段的名称(地址标号)。 • expr: 此字段在数据结构中所占的字节数。
由MAP和FIELD伪操作配合定义的数据结构有3种: • 基于绝对地址的数据结构 • 基于寄存器相对偏移的数据结构 • 基于程序相对偏移的数据结构(基于PC)。 • 例1:基于绝对地址—首地址固定 MAP 0x200 ;内存的首地址为0x200 a FIELD 4 ;字段a长度为4,相对位置为0 b FIELD 4 ;字段b长度为4,相对位置为4 x FIELD 8 ;字段x长度为8,相对位置为8 y FIELD 8 ;字段y长度为8,相对位置为16 s FIELD 16 ;字段s长度为16,相对位置为24 引用结构中的数据: LDR R0,b
例2:基于寄存器相对偏移—首地址基于基址寄存器例2:基于寄存器相对偏移—首地址基于基址寄存器 MAP 0,R9 ;内存的首地址为R9寄存器的值 a FIELD 4 ;字段a长度为4,相对位置为0 b FIELD 4 ;字段b长度为4,相对位置为4 x FIELD 8 ;字段x长度为8,相对位置为8 y FIELD 8 ;字段y长度为8,相对位置为16 s FIELD 16 ;字段s长度为16,相对位置为24 • 可通过下面的指令访问数据结构中的信息: ADR R9,datastart ; datastart是数据起始地址 LDR R0, b ;LDR R0,[R9,#4] 通过在ADR指令中指定不同的基址寄存器的值,定义的数据结构可在程序中有多个实例。
例3:基于程序相对偏移—首地址基于PC Datastruc SPACE 280 ;分配280字节的内存单元 MAP Datastruc ;内存的首地址为Datastruc内存单元 a FIELD 4 ;字段a长度为4,相对位置为0 b FIELD 4 ;字段b长度为4,相对位置为4 x FIELD 8 ;字段x长度为8,相对位置为8 y FIELD 8 ;字段y长度为8,相对位置为16 s FIELD 256 ;字段s长度为16,相对位置为24 • 可通过下面的 指令访问数据结构中的信息: LDR R0, b ;LDR R0, Datastruc+4
例:判断当前内存的使用情况是否超过程序分配的可用内存的方法。例:判断当前内存的使用情况是否超过程序分配的可用内存的方法。 startofmem EQU 1000 ;分配内存首地址 endofmem EQU 2000 ;分配内存末地址 MAP startofmem ;内部首地址为startofmem a FIELD 4 ;字段a长度为4,相对位置为0 b FIELD 4 ;字段b长度为4,相对位置为4 x FIELD 8 ;字段x长度为8,相对位置为8 y FIELD 8 ;字段y长度为8,相对位置为16 s FIELD max ;字段s长度为max,相对位置为24 endofstru FIELD 0 ;endofstru用于检查内存是否越界 ASSERT endofstru<=endofmem
4、DCB——也可以用符号”=”表示 • 用于定义并且初始化一个或者多个字节的内存区域。 • 语法格式: {label} DCB expr{,expr}…… 或 {label} = expr{,expr} • 其中: • expr表示: • -128到255之间的一个数值常量或者表达式。 • 一个字符串。 当DCB后面紧跟一个指令时,可能需要使用ALIGN确保指令是字对齐的。
例: • short DCB 1 ;为short分配了一个字节,并初始 化为1。 • string DCB “string”,0 ;构造一个以0结尾的字 符串
5、DCD、DCDU • (1)DCD ——也可以用符号”&”表示 • 用于分配一段字内存单元(分配的内存单元都是字对齐的)并初始化。 • 语法格式: {label} DCD expr{,expr}…… 或 {label} & expr{,expr}…… • 其中: • expr:数字表达式或程序中的标号。 DCD伪操作可能在分配的第一个内存单元前插入填补字节以保证分配的内存是字对齐的。
例: • data1 DCD 2,4,6 ;为data1分配三个字,内容初始化为2,4,6 • data2 DCD label+4 ;初始化data2为label+4对应的地址 • (2)DCDU • DCDU与DCD的不同之处在于DCDU分配的内存单元并不严格字对齐。
汇编控制伪操作 • IF,ELSE及ENDIF • WHILE及WEND • MACRO,MEND及MEXIT
1、IF,ELSE及ENDIF • IF,ELSE及ENDIF伪操作能够根据条件把一段源代码包括在汇编语言程序内或者将其排除在程序之外。 • “[“是IF伪操作的同义词 • “|“是ELSE伪操作的同义词 • “]”是ENDIF伪操作的同义词 • 语法格式: • IF logic expression; logic expression控制选择逻辑表达式 • …… ;指令或伪指令代码段1 • {ELSE ;可选 • …… ;指令或伪指令代码段2 • } • ENDIF IF、ELSE及ENDIF伪操作可以嵌套使用
例: • IF Variable=16 ;如果Variable=16成立,则编译下面代码 • BNE SUB1 • LDR R0,=SUB0 • BX R0 • ELSE ;否则编译下面代码 • BNE SUB0 • LDR R0,=SUB0 • BX R1 • ENDIF
2、WHILE及WEND • WHILE及WEND伪操作能够根据条件重复汇编相同的一段源代码。 • 语法格式: WHILE logic expression instructions or derectives WEND WHILE和WEND伪操作可以嵌套使用
例: count SETA 1;设置循环计数变量count初始值为1 WHILE count<=4 ;由count控制循环执行的次数 count SETA count+1 ;将循环计数变量加1 …… ;代码 WEND ;结束
3、MACRO、MEND及MEXIT • MACRO:标识宏定义的开始。 • MEND:标识宏定义的结束。 • MACRO和MEND之间的代码称为宏定义体。 • MEXIT:用于从宏中跳转出去。 • 宏体代表了一个预定义的汇编语言指令序列。在汇编程序中通过宏的名称调用宏,编译时,宏语句被展开。
语法格式: MACRO {$label} macroname {$parameter{, $parameter}..} ;code …… ;code MEND 其中: {$label}:在宏被展开时,label可被替换成相应的符号。 macroname:多定义的宏的名称。 $parameter:宏指令的参数。
在程序中调用此宏: exam jump sub,det …… • 例:(示例26) MACRO $label jump $a1,$a2 …… $label.loop1 …… BGE $label.loop1 $label.loop2 BL $a1 BGT $label.loop2 …… ADR $a2 MEND examloop1 …… BGE examloop1 examloop2 BL sub BGT examloop2 …… ADR det
例: MACRO $abc macroabc $param1,$param2 ;code IF condition1 ;code MEXIT ;从宏中跳出 ELSE ;code ENDIF ;code MEND 子程序比较短,而需要传递的参数比较多是可以使用宏汇编技术。
其他伪操作 • 1、CODE16和CODE32 • CODE16告诉汇编编译器后面的指令序列为16位的Thumb指令。 • CODE32告诉汇编编译器后面的指令序列为32位的ARM指令。 • 语法格式: CODE16 CODE32 CODE16和CODE32只是告诉编译器后面指令的类型,该伪操作本身不进行程序状态的切换。
例: • AREA ChangeState, CODE, READONLY ENTRY CODE32 ;下面为32位ARM指令 LDR R0,=start+1 BX R0 …… CODE16 ;下面为16位Thumb指令 start MOV R1,#10 ……. END ;切换到Thumb状态,并跳转到start处执行
2、EQU ——也可以用符号”*”表示 • EQU伪操作为数字常量、基于寄存器的值和程序中的标号定义一个字符名称。 • 语法格式: name EQU expr{,type} • 其中: • expr:基于寄存器的地址值、程序中的标号、32位的地址常量或者32位的常量。 • name:为expr定义的字符名称。 • type:当expr为32位常量时,可以使用type指示expr的数据的类型。取值为: • CODE32 • CODE16 • DATA
例: abcd EQU 2 ;定义abcd符号的值为2 abcd EQU label+16 ;定义abcd符号的值为(label+16) abcd EQU 0x1c,CODE32 ;定义abcd符号的值为绝对地址 ;值0x1c,而且此处为ARM指令
3、AREA • 用于定义一个代码段或是数据段。 • 语法格式: AREA sectionname{,attr} {,attr}… • 其中: • sectionname:为所定义的段的名称。 • attr:该段的属性。具有的属性为: • CODE:定义代码段。 • DATA:定义数据段。 • READONLY:指定本段为只读,代码段的默认属性。 • READWRITE:指定本段为可读可写,数据段的默认属性。
ALIGN=expression:指定段的对齐方式为2expression。expression的取值为0~31。ALIGN=expression:指定段的对齐方式为2expression。expression的取值为0~31。 • ASSOC=section:指定与本段相关的ELF段。 • COMDEF:定义一个通用的段。该段可以包含代码和数据。在某个源文件中,同名的COMDEF段必须相同。 • COMMON:定一个通用段。该段不包含任何用户代码和数据。 • NOINIT:指定此数据段仅仅保留了内存单元,而没有将各初始值写入内存单元,或者将各个内存单元值初始化为0。 一个大的程序可包含多个代码段和数据段。一个汇编程序至少包含一个代码段。
4、ENTRY • 指定程序的入口点。 • 语法格式: • ENTRY • 注意: • 一个程序(可包含多个源文件)中至少要有一个ENTRY(可以有多个ENTRY),但一个源文件中最多只能有一个ENTRY(可以没有ENTRY)
5、END • END伪操作告诉编译器已经到了源程序结尾。 • 语法格式: • END • 注意: • 每一个汇编源程序都必须包含END伪操作,以表明本源程序的结束。
6、ALIGN • ALIGN伪操作通过填充0将当前的位置以某种形式对齐。 • 语法格式: • ALIGN {expr{,offset}} • 其中: • expr:一个数字,表示对齐的单位。这个数字是2的整数次幂,范围在20~231之间。(如果没有指定expr,则当前位置对齐到下一个字边界处。) • offset:偏移量,可以为常数或数值表达式。 不指定offset表示将当前位置对齐到以expr为单位的起始位置。
例1: • short DCB 1 ;本位操作使字对齐被破坏 • ALIGN ;重新使其为字对齐 • MOV R0,1 • 例2: • ALIGN 8 ;当前位置以2个字的方式对齐
7、EXPORT及GLOBAL • 声明一个源文件中的符号,使得此符号可以被其他源文件引用。 • 语法格式: • EXPORT/GLOBAL symbol {[weak]} • 其中: • symbol:声明的符号的名称。(区分大小写) • [weak]:声明其他同名符号优先于本符号被引用。 • 例: AREA example,CODE,READONLY • EXPORT DoAdd • DoAdd ADD R0,R0,R1
8、IMPORT及EXTERN • 声明一个符号是在其他源文件中定义的。 • 语法格式: • IMPORT symbol{[weak]} • EXTERN symbol{[weak]} • 其中: • symbol:声明的符号的名称。
[weak]: • 当没有指定此项时,如果symbol在所有的源文件中都没有被定义,则连接器会报告错误。 • 当指定此项时,如果symbol在所有的源文件中都没有被定义,则连接器不会报告错误,而是进行下面的操作。 • 1、如果该符号被B或者BL指令引用,则该符号被设置成下一条指令的地址,该B或BL指令相当于一条NOP指令。 • 2、其他情况下此符号被设置成0。
9、GET及INCLUDE • 将一个源文件包含到当前源文件中,并将被包含的文件在其当前位置进行汇编处理。 • 指令格式: • GET filename • INCLUDE filename • 其中: • filename:包含的源文件名,可以使用路径信息(可包含空格)。 • 例:GET d:\arm\file.s