汇编源程序一般用于系统最基本的初始化:初始化堆栈指针、设置页表、操作 ARM的协处理器等。这些初始化工作完成后就可以跳转到C代码main函数中执行。
1、 GNU汇编语言语句格式
任何Linux汇编行都是如下结构:
[
下面定义一个"add"的函数,最终返回两个参数的和:
.section .text, “x”
.global add @ give the symbol “add” external linkage
add:
ADD r0, r0, r1 @ add input arguments
MOV pc, lr @ return from subroutine
@ end of program
注意:
2、 GNU汇编程序中的标号symbol(或label)
标号只能由a~z,A~Z,0~9,“.”,_等(由点、字母、数字、下划线等组成,除局部标号外,不能以数字开头)字符组成。
Symbol的本质:代表它所在的地址,因此也可以当作变量或者函数来使用。
Symbol的分类:3类(依据标号的生成方式)。
特别说明:局部标号Symbol
局部标号主要在局部范围内使用,而且局部标号可以重复出现。它由两部组成:开头是一个0-99直接的数字,后面紧接一个通常表示该局部变量作用范围的符号。局部变量的作用范围通常为当前段,也可以用ROUT来定义局部变量的作用范围。
局部变量定义的语法格式:N{routname}
局部变量引用的语法格式:%{F|B}{A|T}N{routname}
例:使用局部符号的例子,一段循环程序
subs r0, r0, #1 @每次循环使r0=r0-1
bne 1F @跳转到1标号去执行
...
1:
mov pc,lr
注意:
3、 GNU汇编程序中的常数
4、 函数的定义伪操作
函数名:
函数体
返回语句
一般的,函数如果需要在其他文件中调用, 需要用到.global伪操作将函数声明为全局函数。为了不至于在其他程序在调用某个C函数时发生混乱,对寄存器的使用我们需要遵循APCS准则。函数编译器将处理函数代码为一段.global的汇编码。
例如:
asm_putc:
ldr r1, =ELFIN_UART_BASE
1: ldr r2, [r1, #UTRSTAT_OFFSET]
tst r2, #2
beq 1b
str r0, [r1, #UTXH_OFFSET]
mov pc, lr
5、 GNU ARM汇编特殊字符和语法
1、.section
语法格式
.section section_name[,"flags"[,%type[,flag_specific_arguments]]]
定义一个段,每一个段以段名为开始,以下一个段名或者文件结尾为结束。
ELF格式允许的段标志: a:可分配
w:可写段
x:执行段
举例
.section .mysection @自定义数据段,段名为“.mysection”
2、预定义段 .text、.data、.bss
语法格式
.text{subsection} @代码段
.data{subsection} @初始化数据段 .data Read-write initialized long data
.bss{subsection} @未初始化数据段
.text、.data和.bss将汇编系统预定义的段名编译到相应的代码段、数据段和bss段。
注意:源程序中.bss段应该在.text之前。
bss段通常是指用来存放程序中未初始化的全局变量的一块内存区域 数据段通常是指用来存放程序中已初始化的全局变量的一块内存区域
举例
.section .data
.section .bss
.section .text .global _start
_start:
3、.code16、code32、.thumb、.arm
语法格式
.code16、code32
.thumb
.arm
用来选择ARM或者Thumb指令集,.thumb伪操作等同于.code16,表明使用Thumb指令类似的.arm等同于.code32
.code伪操作用来选择ARM或者Thumb指令集,格式如下:
.code 表达式
如果表达式的值为16则表明下面的指令为Thumb指令,如果表达式的值为32则表明下面的指令为ARM指令.
4、.end
语法格式
.end
表明源文件的结束,如果该标号之后还有代码,不会被编译到执行文件中
5、.include
语法格式
.include "filename"
可以将指定的文件在使用位置处展开,一般是头文件
6、.incbin
语法格式
.incbin "file"[,skip[,count]]
可以将原封不动的一个二进制文件编译到当前文件中。其中,skip表明是从文件开始跳过skip个字节开始读取文件,count是读取的字数
7、.if、.else/.endif
语法格式
.if 条件表达式
代码段1
.else
代码段2
.endif
根据一个表达式的值来决定是否要编译下面的代码, 用.endif伪操作来表示条件判断的结束, 中间可以使用.else来决定.if的条件不满足的情况下应该编译哪一部分代码。
.if有多个变种:
.ifdef symbol @判断symbol是否定义
.ifndef symbol @判断是否没有定义symbol, 跟.ifdef恰好相反
.ifnotdef symbol @同.ifndef
.ifc string1,string2 @字符串string1和string2是否相等,字符串可以用单引号括起来
.ifeq expression @判断expression的值是否为0
.ifeqs string1,string2 @判断string1和string2是否相等,字符 串必须用双引号括起来
.ifge expression @判断expression的值是否大于等于0
.ifgt absolute expression@判断expression的值是否大于0
.ifle expression @判断expression的值是否小于等于0
.iflt absolute expression @判断expression的值是否小于0
.ifnc string1,string2 @判断string1和string2是否不相等, 其用法跟.ifc恰好相反。
.ifne expression @如果expression的值不是0, 那么编译器将编译下面的代码
.ifnes string1,string2 @如果字符串string1和string2不相 等, 那么编译器将编译下面的代码.
8、.align
语法格式
.align [absexpr1, absexpr2]
用来指定数据的对齐方式。以某种对齐方式,在未使用的存储区域填充值. 第一个值表示对齐方式,4, 8,16或 32. 第二个表达式值表示填充的值。
9、.macro、.exitm和.endm
语法格式
.macro 宏名 参数名列表 @伪操作.macro定义一个宏
宏体
.endm @.endm表示宏结束
如果宏使用参数,那么在宏体中使用该参数时添加前缀“\”。宏定义时的参数还可以使用默认值,可以使用.exitm伪指令来退出宏
举例
.macro SHIFTLEFT a,b
.if \b<0
MOV \a,\a,ASR #-\b
.exitm
.endif
MOV \a,\a,LSL #\b
.endm
10、.byte
语法格式
.byte 表达式{,表达式}...
定义单字节
举例
.byte 1,2,'s',0x34
11、.short、.hword
语法格式
.short/.hword 表达式{,表达式}...
定义双字节数据
举例
.short 0x1234
12、.long、.word、.int
语法格式
.long/.word/.int 表达式{,表达式}...
.long、.word和.int定义4字节数据
举例
.long 0x12345678,23876565
定义两个4字节的数值
valueOfStart:
.word Start
这样程序的开头Start便被存入了内存变量valueOfStart中。
hwlist:
.hword {,} …
插入一个16-bit的数据队列。(与armasm中的DCW相同)
13、.quad、.float
语法格式
.quad 表达式{,表达式}...
.quad定义8字节数据;.float定义浮点数
举例
.quad 0x1234567890abcdef
.float 0f-31415926535897932384626433832795028841971.693993751E-40 @ - pi
14、.string/.asciz/.ascii
语法格式 .string/.asciz/.ascii 表达式{,表达式}...
.string/.asciz/.ascii定义多个字符串。注意:ascii伪操作定义的字符串需要自动添加结尾字符'\0'
举例
.string "abcd", "efgh", "hello!"
.asciz "qwer", "sun", "world!"
.ascii "welcome\0"
15、.zero、.space、.skip
语法格式
.zero
.space/.skip {,}
分配number_of_bytes字节的数据空间,.zero伪操作用0填充内存,.space/.skip用值为fill_byte填充内存,若未指定该值,缺省填充0
16、.rept、.endr
语法格式
.rept 重复次数
数据定义
.endr @结束重复定义
.rept及.endr用于重复定义伪操作
举例
.rept 3
.byte 0x23
.endr
17、.equ、.set
语法格式
.equ(.set)常量名,表达式
.equ和.set用于为程序中标号定义名称
举例
.equ abc, 3 @让abc=3
18、.global/.globl
语法格式
.global/.globl symbol
.global和.globl用来定义一个全局的符号
举例
汇编程序的缺省入口是_start标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点。
.globl _start
_start:
19、.extern
语法格式
.extern label
.extern用于声明一个外部标号
20、.ltorg、.pool
语法格式
.ltorg/.pool
.ltorg和.pool用于声明一个数据缓冲池的开始,它可以分配很大的空间
21、ARM特有的伪操作
21.1、语法格式
.reg: 用来给寄存器赋予别名,格式如下:
别名 .req 寄存器名
.unreq: 用来取消一个寄存器的别名,格式如下:
.unreq 寄存器别名
注意被取消的别名必须事先定义过,否则编译器就会报错,这个伪操作也可以用来取消系统预制的别名, 例如r0, 但如果没有必要的话不推荐那样做。
22、其他伪命令
.err 使编译结果产生错误报告
.eject 在汇编符号列表文件中插入一分页符
.list .产生汇编列表(从.list到.nolist)
.nolist 汇编列表结束处。再次使用.list产生汇编列表
.title "title_name" 使用heading作为标题(位于汇编列表文件中文件名下一行)
.sbttl "title_name" 使用heading作为子标题(位于.title标题下一行)
.print string 打印输出信息到标准输出