按照编译器不同,汇编分为两大量:一类是ADS的汇编程序,一类是GNU汇编格式任。
以冒号结尾的标识符都被认为是一个标号,而不一定非要在一行的开始。
GNU汇编:http://web.mit.edu/gnu/doc/html/as_toc.html#SEC127
一、ARM指令 (带点的一般都是ARM GNU伪汇编指令)
1. ".title xxx":指定汇编列表的标题。
".list":用来输出列表文件。
".type xxx":指定符号的类型。
2. ".text":代码段。已编译程序的可执行机器码。
".rodata":只读数据段。const全局变量。比如printf语句中的格式串和开关(switch)语句的跳转表。
".data":数据段。已初始化的非零全局变量和静态变量。它会在main函数之前被处理。
".bss":未初始化(或初值为0)的全局变量。它在目标文件中不占实际的空间(不保存在bin文件中),是一个占位符。目标文件格式区分初始化和未初始化变量是为了提高空间效率。启动代码完成两方面:①未初始化变量的清0。②设置已初始化变量的初值。保存了BSS段和COMMON段(存放注释)的内容。
".symtab":符号表。存放被定义和引用的函数及全局变量。每个可重定位目标文件在.symtab中都有一张符号表。与编译器中的符号表不同,.symtab不包含局部变量的表目。不一定要通过-g编译程序,得到符号表信息。
注释:为了让启动代码简单,编译链接器会把已初始化的变量放.data段,这个段的映像(包含了各个变量的初值)保存在“只读数据段”。启动代码复制这个映像到 .data 段,初始化所有变量。初始化为0的变量保存在bss段,未初始化变量保存在common段,链接时再将其放入bss段。启动代码调用 memset 把所有未初始化变量清0。
".rel.text":保存着一系列在.text中的位置的列表。这些位置在链接时被修改。这些位置通常保存着引用全局变量,或外部函数的指令。在运行时并不需要它以及下面的.rel.data。生成可执行文件ELF object时会去掉。除非使用者去显式指示链接器包含这些信息。
".rel.data":保存全局变量的重定位信息。如果一个全局变量的初始化值,是另一个全局变量的地址,或者是外部函数的地址时,它就需要被重定位。
".debug":调试符号表。其可以是程序中定义的:局部变量、类型定义、全局变量的定义和引用、C源文件。只有" -g "选项才会生成这张表。
".line":源程序中的行号和.text中机器指令间的映射。只有以-g选项调用编译驱动程序时才会生成。
".strtab":字符串表。包含.symtab和.debug中的符号表,和各个section的名字。字符串表是以null结尾的字符串序列。.debug和.symtab中,保存name的域,等于保存了一个偏移值。可以通过这个偏移值在字符串表里面找到相应字符串。(https://www.veryarm.com/23019.html)
3. ".section":自定义一个段。.section section_name [, "flags"[, %type[,flag_specific_arguments]]]。每一个段以段名为开始, 以下一个段名或者文件结尾为结束。这些段都有缺省的标志(flags),连接器可以识别这些标志。(与armasm中的AREA相同)。
4. 注释:代码行中的注释符号: ‘@’。整行注释符号: ‘#’。语句分离符号: ‘ ; ’。直接操作数前缀: ‘#’ 或 ‘$’。
5. "_start":汇编程序的缺省入口。如果想更改,到相应的链接脚本中去用ENTRY指明其他入口标志。标号可以直接认为是地址。
6. ".globl/.global":定义一个全局符号,通常为ld使用。例如 .global _start(定义 _start 为外部程序可以访问的标签)。
7. ".abort":停止汇编。
8. ".string/.asciz/.ascii"、".byte"、".short"、".int"、".long"、".float"、".word"、".quad":定义一个类型并分配空间(字符串、字节(1 byte)、短整型(2 byte)、整型(4 byte)、长整型(4 byte)、浮点数、字(与系统位数有关。16位系统中是2字节)、quard word(4字))。用".string/.asciz"定义时要加双引号,用".ascii"定义时还要在末尾手动添加"\0"。
9. ".align xxx, yyy":对齐方式。xxx表示对齐方式,4, 8,16或32。 yyy 表示填充的值。一般用于定义完字符串等类型之后的代码对齐。
10. ".if" ".else" ".endif":条件预编译。if的变种如下:
11. ".include "file" ":包含指定文件。可以把汇编常量定义放在头文件中。
12. ".incbin "file"[,skip[,count]]":将二进制文件编译到当前文件中。skip是以字节为单位,从文件头部读取的偏移量。count是读取的字数。
13. ".macro .endm":.macro定义的宏代码的开始, .endm宏代码的结束,.exitm跳出宏。若宏使用参数,则宏体中使用该参数时添加前缀“\”。宏定义时的参数还可以使用默认值。
14. ".comm symbol, length":在bss段申请一段叫symbol的命名空间,长度为length。Ld连接器连接时会为它留出空间。
15. ".rept x":重复定义x次其内的定义。用 .endr结束。
16. "(.equ/.set) xxx, yyy":把符号定义成值。它不分配空间,相当于#define。或者用“ xxx equ yyy ”。
17. ".req":为寄存器定义别名。
".unreq":取消寄存器别名。
18. ".code":.code [16|32]: 指定指令代码产生的长度, 16表示Thumb指令, 32表示ARM指令。
19. ".ltorg/.pool":当前往下的定义在归于当前段,并为之分配空间。即声明数据缓冲池。
20. ".space <x> {,<aaa>}":分配x字节空间,并用aaa填充。缺省填充0。
二、进制的表示
1. 二进制数以0b开头,其中字母也可以为大写
2. 八进制数以0开始,如:0456,0123
3. 十进制数以非0数字开头,如:123和9876
4. 十六进制数以0x开头,如:0xabcd,0X123f
5. 字符串常量用引号括起来,中间也可以使用转义字符。如: “You are welcome!\n”
6. 当前地址以" . "表示
7. 表达式:汇编程序中表达式可使用常数或数值。" - "取负数, " ~ "取补," < > "不相等," +、-、*、 /、%、<、<<、>、>>、|、&、^、!、==、>=、<=、&&、|| "跟C语言用法相似。
三、操作寄存器
"ldr r0, [ r1 ]":读取地址 [ r1 ] 上的数据,并保存到 R0 寄存器中,长度为4字节。(方括号内的表示地址)
"ldr r0, [ r1, #4 ]":将地址为 r1+4 的内存单元数据读取到 r1 中。
"ldr r0, [ r1 ], #4:将地址为 [ r1 ] 的内存单元数据读取到 r0 中,然后 r1 = r1+4。
★.在32位ARM指令中会用某些位表示当前执行的指令(LDR等)及寄存器(R0等)。剩下的位数不足以表示任意值,所以引入伪指令。伪指令会被拆分成ARM指令。
ldr 伪指令的三种用法:
①. ldr r0, = 0x10:给 r0 赋值为0x10。
②. ldr pc, =Label:将 Label 符号放入pc寄存器中。编译时会替换成一条 ldr 指令和一条 dcd 伪指令。符号地址由编译器指定。
③. ldr pc, Label_addr:读取存储器中 Label_addr 符号所表示的地址中的值放入 pc 。多读一次存储器。
"str r0, [ r1 ]":把 r0 寄存器上的值,保存到地址 [ r1 ] 的地方,长度为4字节。
"str r0, [ r1, #4 ]":将 r0 的数据保存到地址为 r1+4 的内存单元中。
"str r0, [ r1 ], #4":将 r0 的数据保存到地址为 r1 的内存单元中,然后 r1=r1+4。
"ldmia r0, { r1, r2, r3, r4 } / stmdb r0!, { r1, r2, r3, r4 }":一次设置多个寄存器。后增,即先操作后增加。32位芯片的话,增加/减少的单位是4。按寄存器编号依次操作寄存器,高编号寄存器存放在高地址。(ld/st) + m + (i/d/f/e) + (a/b) = (读取/设置)+ 多个寄存器 +(增加/减少/满/空)+(之后/之前)。" ! "表示此寄存器的值等于最终被修改后的值。否则最终值等于初始值。ldm命令末尾的" ^ "表示将spsr写回到cpsr,用于异常返回后工作状态的恢复,不允许在用户模式和系统模式下运行。
当堆栈指针指向最后压入的数据时为满堆栈,当指针指向下一个将要放入数据的空位置时为空堆栈。当堆栈由低地址向高地址生成时称递增堆栈,当堆栈由高地址向低地址生成时称递减堆栈。下列是入栈和出栈的指令对:
Full descending : stmdb--ldmia 或 stmfd--ldmfd
Full ascending : stmib--ldmda 或 stmfa--ldmfa
Empty descending :stmda--ldmib 或 stmed--ldmed
Empty ascending : stmia--ldmdb 或 stmea--ldmea
注意:虽然ARM处理器核对于两种生长方式的堆栈均支持,但ADS的C语言编译器仅支持一种方式,即从上往下长,并且必须是满递减堆栈。所以STMFD等指令用的最多。
"adr r0, xxx":将基于PC相对偏移的地址值读取到 r0。编译时ADR伪指令会被替换。通常是用ADD或SUB指令。若不能用一条指令实现,则会产生错误,编译失败。
"mrs r1, cpsr":读出 cpsr 中的值到 r1 中。
"msr cpsr, r1":将 r1 寄存器中的值恢复到 r1 中。
"swi xxx":执行软中断。xxx 为24位的二进制数,表示处理不同软中断。
"mov R0, R1":把 R1 的值赋给 R0 。
"mov R0, [ R1 ]":把 R1 地址的值赋给 R0 。
"mov R0, #0x10":把立即数 0x10 赋给 R0 。立即数是小于0xff(65535)的数,如果大于65535,则用ldr指令赋值。
"mvn r0, #0":把0取反(即-1)传给 r0。
注意:ARM是RISC结构,数据在内存和CPU间的移动只能通过ldr/str指令。mov只是在寄存器间移动数据,或把立即数移动到寄存器中。而X86中没有ldr这种指令。因为X86的mov指令可以将数据从内存中移到寄存器中。
"nop":空操作指令。可以用作延时。
"add r0, r1, r2":r0 = r1 + r2。
"adds r0, r1, r2":r0 = r1 + r2。s表示把进位结果写入cpsr。sub同理。
"sub r0, r1, r2":r0 = r1 - r2。
"mul r0, r1, r2": r0 = r1 * r2。
<opcode>{<cond>}{S} <Rd>,<Rn>{,<shifter_operand>}
对shifter_operand参数的位移。
左右移:http://www.eeworld.com.cn/mcu/2015/0930/article_22693.html
"tst r0, r1":位比较。先按位与并把结果写入CPSR,供下一句使用。结果为1,则设置标志位zero=0,会执行bne语句。否则不执行bne。beq则相反。
"cmp xxx, yyy":两值相减,只改标志位。标志位存入CPSR供下一句语句使用。
(分支指令)
"b xxx":简单的跳转,即调用子程序。跳转到到目标标号处执行。跳转范围是当前指令的前后32M。
"bl xxx":带链接的程序跳转,即要带返回地址。跳转前将当前PC-4(返回地址)保存至R14(LR)。子程序返回时会执行 MOV PC, LR。跳转范围是当前指令的前后32M。
"bx{<condition>} <rx>":<condition>为指令执行的条件码,缺省时无条件执行。<rx>寄存器中为跳转的目标地址。当<rx>寄存器bit[0]为0时,目标地址处的指令为ARM指令;当bit[0]为1时,则为Thumb指令。(ARM汇编)
注意:反汇编文件里,用 B或BL 等跳某个值,只是方便查看,并不是真的跳转。
(条件执行指令)
"bne xxx":数据跳转指令,标志寄存器中Z标志位不等于零时,跳转到BNE后标签处。
"beq xxx":数据跳转指令,标志寄存器中Z标志位等于零时,跳转到BEQ后标签处。
注意:bne 1f,或beq 1b。1f:程序之后的"1"标号(forward)。1b:程序之前的"1"标号(before)。
bvs: Branch if overflow(溢出) Set
bvc: Branch if overflow(溢出) Clear
bhi: Branch if HIgher
bls: Branch if Lower or the Same
bpl: Branch if Plus
bmi: Branch if MInus
bcs: Branch if Carry Set(进位设置)
bcc: Branch if Carry Clear(进位清除)
bge: Branch if Greater than or Equal
bgt: Branch if Greater Than
ble: Branch if Less than or Equal
blt: Branch if Less Than
bleq: Branch with Link if Equal(带返回链接的判断型跳转)
bllt: Branch with Link if Less Than(带返回链接的判断型跳转)
"moveq r1, #0": 如果上一句条件执行码<condition>判断为相等,则执行mov指令。ARM汇编指令。
"movgt r1, #0":如果上一句条件执行码<condition>判断左边大,则执行mov指令。ARM汇编指令。
"and r0, r1":r0 &= r1。按位与。
"and r0, r1, r2":r0 = r1 & r2。
"orr r0, r1":r0 |= r1。按位或。
"orr r0, r1, r2":r0 = r1 | r2。
"not r0":r0 = ! r0。按位取反。指令的执行不影响任何标志位。
"bic r0, r1":r0 &= ~r1。按位清零。
"bic r0, r1, #%1011":r0 = r1 & 4。%表示二进制,0x表示十六进制。
"orn r0, r1":r0 = r0 | ~r1。按位或反。
"eor r0, r1":r0 ^= r1。按位异或(不同为1,相同为0)。
1、注释行以“@”代替“;”
2、伪操作符替换:INCLUDE 替换成 .INCLUDE
TCLK2 EQU PB25 替换成 .equ TCLK2, PB25
EXPORT 替换成 .global
IMPORT 替换成 .extern
DCD 替换成 .long
IF :DEF: 替换成 .IFDEF
ELSE 替换成 .ELSE
ENDIF 替换成 .ENDIF
:OR: 替换成 |
:SHL: 替换成 <<
END 替换成 .end
符号定义后要加":"号
AREA Word, CODE, READONLY --> .text
AREA Block, DATA, READWRITE --> .data
CODE32 --> .arm
CODE16 --> .thumb
LTORG --> .ltorg
网友评论