本专栏总结王利涛《C语言嵌入式Linux高级编程》第二期课程
一、汇编语言的程序格式
- 以段(section)为单位组织源文件
- 分为代码段、数据段,段之间互相独立;
- 一个ARM汇编程序至少需要一个代码段,使用AREA伪操作标识一个段的开始、段的名字和属性;
- 使用伪操作ENTRY标识程序执行的第一行;
- 使用伪操作END标识汇编程序结束;
- 代码段中,常见的还有标号和注释。
二、汇编语言实例:数据块复制
AREA COPY, CODE, READONLY ; CODE, READONLY表示代码段的属性为CODE、只读
ENTRY ; 表明入口在START地址,指向START地址,而START等于第一条指令
START ; 符号
LDR R0, =SRC ; 伪指令
LDR R1, =DST
MOV R2, #0
LOOP ; 符号
LDR R3, [R0], #4
STR R3, [R1], #4
SUBS R2, R2, #1
BNE LOOP ; 这里的LOOP是标号
AREA COPYDATA, DATA, READWRITE ; 属性为 数据,可读写
SRC DCD 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ; 申请两个存储单元。 SRC 和 DST可以看作是符号
DST DCD 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
END
三、符号与标号
1)符号的定义
- 在ARM汇编中,使用符号来表示地址、变量和数字常量;
- 当用符号来标识地址时,又称为 标号。
2)符号的命名
- 由字母、数字和下划线组成,开头不能用数字,局部标号除外;
- 符号在其作用域内必须唯一;
- 不能与系统内部或系统预定义的符号同名;
- 不能与指令助记符、伪指令同名。
四、局部标号
特点
- 通过数字[0, 99] 而不是使用名称(字符)进行地址引用;
- 作用域为当前段;
- 引用格式:%{F | B | A | T} N {routname}
- F:指示编译器只向前搜索;
- B:指示编译器只向后搜索;
- A:指示编译器搜索宏的所有嵌套层次;
- T:指示编译器搜索宏的当前层;
- routname:局部标号作用范围名称,使用ROUT定义;
- 若B、F没指定,编译器将先向后搜索,再向前搜索。
0 ;定义一个局部标号
LDR R3, [R0], #4
STR R3, [R1], #4
SUBS R2, R2, #1
BEN %B0 ; 只向后搜索,往上即为后,往下即为前
五、伪操作
1)什么是伪操作
- ARM汇编语言中一些特殊的指令助记符。
- 主要目的是对汇编源程序指令作各种处理,组装成一个完整的汇编程序。
- 伪操作是为汇编器服务的,类似于C语言中的宏,为编译器服务。预处理之后,宏小时;汇编过后,伪操作消失。
- 不同的环境,伪操作格式可能不一样。
2)常用伪操作
- 定义符号的伪操作:GBLA、LCLA…
- 数据定义伪操作:DCD、DCB、SPACE
- 汇编控制伪操作:AREA、ENTRY、MACRO、MEND
六、定义符号常用的伪操作
伪操作 | 使用实例 | 说明 |
---|
GBLA | GBLA a | 定义一个全局算术变量a,并初始化为0 |
GBLL | GBLL a | 定义一个全局逻辑变量a,并初始化为{FALSE} |
GBLS | GBLS str | 定义一个全局字符串变量str,并初始化为“ ” |
LCLA/L/S | LCLA/L/S a | 定义一个局部的算术/逻辑/字符串变量 |
SETA | num SETA 20 | 给算术变量赋值为20 |
SETL | bool SETL {TRUE} | 给逻辑变量bool 赋值为{TRUE} |
SETS | str SETS “hello” | 给字符串变量str赋值为 “hello” |
RLIST | ArReg RLIST {R0-R3} | 给寄存器列表定义名称为ArReg,可以通过LDM/STM访问 |
CN | Cache CN7 | 给协处理器的寄存器7命名为Cache |
CP | memory CP 15 | 为一个协处理器P15定义名称(ARM支持16个协处理器) |
DN | name DN rgname | 为一个双精度的VFP寄存器命名 |
SN | name SN rgname | 为一个单精度的VFP寄存器命名 |
FN | name FN rgname | 为一个浮点寄存器命名 |
七、定义数据的伪操作
伪操作 | 使用实例 | 说明 |
---|
DCD或 & | num DCD 0x11223344 | 指定编译器分配一个或多个字,4字节对齐 |
DCDU | DCDU 0xFFEE11FF | 不需要边界对齐 |
DCB或= | DCB ‘A’ | 分配一个或多个字节空间 |
SPACE 或 % | Buf SPACE 100 | 分配一块连续空间,并初始化为0 |
MAP或^ | MAP 0x30009000 | 定义一个结构化内存表的首地址 |
FIELD或# | a FILED 4/ str FIELD 8 | 定义一个结构化内存表的数据域 |
其它:
伪操作 | 说明 |
---|
DCFD/DCFDU | 为双精度浮点数分配存储空间 |
DCFS/DCFSU | 为单精度浮点数分配存储空间 |
DCQ/DCQU | 分配双字(8个字节) 存储空间 |
DATA | 指示代码段数据的标号,一般后跟DCD或DCB |
LTORG | 声明一个文字池(数据缓冲池) |
八、汇编控制伪操作
伪操作 | 说明 |
---|
IF,ELSE,ENDIF | 条件汇编 |
WHILE,WHEN | 重复汇编相同的一段源代码 |
MACRO、MEND、MEXIT | 宏定义 |
九、其它常用伪操作
伪操作 | 说明 |
---|
ALIGN | 使当前位置满足一定字节的对齐 |
AREA | 指示汇编器代码段或数据段的开始 |
CODE16 | 指示编译器后面的指令为thumb指令 |
CODE32 | 指示编译器后面的指令为arm指令 |
ENTRY | 指定程序的入口点 |
EQU | 给常量定义一个符号名,类似于宏 |
END | 汇编源程序结束 |
EXPORT/GLOBAL | 声明一个符号可以被其它文件引用 |
IMPORT/EXTERN | 引用其它文件的符号,要先IMPORT |
GET/INCLUDE | 包含一个文件 |
代码示例:
IMPORT sum ; 声明其它文件的符号
AREA SUM_ASM, CODE, READONLY
EXPORT SUM_ASM ; 声明SUM_ASM 符号可以被其它文件调用
SUM_ASM
STR LR, [sp, #-4]
LDR R0, =0x3
LDR R1, =0x4
BL sum ; 调用其它文件的函数
LDR PC, [sp],#4
END
--------------------------------------------------------------
--------------------------------------------------------------
int sum(int a, int b)
{
int result=0;
printf("result = %d\n",result);
return result;
}
int main(void)
{
SUM_ASM();
return 0;
}