在计算机的世界里,CPU只能识别二进制的机器码,对于一个32位ARMv7架构来说,每一个指令由32个0或1的组合组成,CPU的设计者会指定一组固定的组合格式,告诉CPU完成什么动作,这组固定格式的0或1的组合就是指令集。
对于程序员来说,阅读二进制的机器码是一个相当大的挑战,为了便于编写程序,最早的汇编语言就诞生了,比如
ADD R0,R1,R2
这句话的的作用是寄存器R1、R2里的值求和后,存储到R0里面。
汇编语言通过编译器的编译,转换成CPU能够识别的二进制机器码。
计算机完成一定的操作通过指令来实现,不同的指令方式被称为计算机架构,目前计算机主要有三大架构阵营,一是CISC(Complex Instruction Set Computers)复杂指令集架构,另一种是RISC(Reduced Instruction Set Computers)精简指令集架构,此外还有一个国内比较火的MIPS指令集。采用CISC架构的代表是Intel和AMD的X86指令集,采用RISC架构的代表是ARM指令集和PowerPC指令集。ARM架构从诞生到今天从V1版本已经升级到了V8版本,各版本代表的分支型号如下表所示。
架构 | 处理器家族 | 历史 |
ARMv1 | ARM1 | 最早由Acorn 计算机公司设计,1985年开发出样本。 |
ARMv2 | ARM2、ARM3 | 1986年开发出ARM2,32位数据,26位地址空间。 |
ARMv3 | ARM6、ARM7 | 1990年设计团队组成新公司Advanced RISC Machines Ltd. (安某国际) |
ARMv4 | StrongARM、ARM7TDMI、ARM9TDMI | |
ARMv5 | ARM7EJ、ARM9E、ARM10E、XScale | |
ARMv6 | ARM11、ARM Cortex-M | |
ARMv7 | ARM Cortex-A、ARM Cortex-M、ARM Cortex-R | |
ARMv8 | Cortex-A35、Cortex-A50系列[14]、Cortex-A72、Cortex-A73 |
从图中可以看出,Cortex-A9 架构的CPU采用的是ARMv7的指令集,目前高性能的64位视频处理,比如Cortex-A72,采用的是ARMv8指令集。
高版本的指令集会向下兼容低版本,在低版本的基础上增加了更多特殊用途的指令集,比如浮点运算,CPU加速、数据安全等特殊用途的指令集。
ARM指令格式形式如下:
指令 {条件} {S} {目的Register},{OP1},{OP2}
括号中的操作都是可选项,对应的32机器码,哪里代表指令,哪里代表条件,哪里代表操作数呢? 请看下表。
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Cond | Opcode | S | Rn | Rd | Shift_operand |
不同域的具体意义如下表所示:
[31:28]Cond | 条件域,用来定义指令执行的条件 |
---|---|
[27:25] | 指令类别,跳转指令、数据处理指令、移位指令等等 |
[24:21]Opcode | 指令域,代表具体的指令,比如ADD,MOV等。 |
[20]S | 指令的后缀,可加可不加,指令后缀带S表示该指令的结果会影响CPSR寄存器。 |
[19:16]Rn | 第一个操作数(OP1)的寄存器编码 |
[15:12]Rd | 目标寄存器编码 |
[11:0]Shift_oprand | 第二个操作数(OP2) |
每种架构的CPU都有一组特殊用途的寄存器,用来存放从存储器读来数据或者指令编码,在ARM中,有一个特殊的寄存器叫CPSR(current program status register),用来存放当前指令执行的状态。比如存放数学运算结果的正负、借位、溢出等情况,详细信息如下表所示。
CPSR field | F (Flags field mask byte) | S (Stats field mask byte) | X (Extension field mask byte) | C (control field mask byte) | |||||||||||||||||||||||||||||
CPSR | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
意义 | N | Z | C | V | Q | DNZ (RAZ) 系统扩展用 | I | F | T | M4 | M3 | M2 | M1 | M0 | |||||||||||||||||||
CPSR各bit详细意义 | N | 当前指令运算结果为负时,N = 1; 结果为非负时,N = 0 | |||||||||||||||||||||||||||||||
Z | 运算结果为0,Z=1;否则Z=0 | ||||||||||||||||||||||||||||||||
C | 上溢出、进位C=1;下溢出、借位C=0 | ||||||||||||||||||||||||||||||||
V | 加减法V=1表示符号位溢出 | ||||||||||||||||||||||||||||||||
I | I=1时,禁止IRQ中断 | ||||||||||||||||||||||||||||||||
F | F=1时,禁止FIQ中断 | ||||||||||||||||||||||||||||||||
T | T=0,ARM指令;T=1,Thumb指令 | ||||||||||||||||||||||||||||||||
M[4:0] | 0b10000 | User | |||||||||||||||||||||||||||||||
0b10001 | FIQ | ||||||||||||||||||||||||||||||||
0b10010 | IRQ | ||||||||||||||||||||||||||||||||
0b10011 | Supervisor | ||||||||||||||||||||||||||||||||
0b10111 | Abort | ||||||||||||||||||||||||||||||||
0b11011 | Undefined | ||||||||||||||||||||||||||||||||
0b11111 | System |
.data /* the .data section is dynamically created and its addresses cannot be easily predicted */
var1: .word 3 /* variable 1 in memory */
var2: .word 4 /* variable 2 in memory */
.text /* start of the text (code) section */
.global _start
_start:
ldr r0, adr_var1 @ 加载adr_var1的内存地址到寄存器R0中。load the memory address of var1 via label adr_var1 into R0
ldr r1, adr_var2 @ 加载adr_var2的内存地址到寄存器R1中。load the memory address of var2 via label adr_var2 into R1
ldr r2, [r0] @ 加载地址为adr_var1的内存中的数据到R2中。load the value (0x03) at memory address found in R0 to register R2
str r2, [r1] @ R2中的数据保存到加载地址为adr_var2的内存中。store the value found in R2 (0x03) to the memory address found in R1
bkpt
adr_var1: .word var1 /* address to var1 stored here */
adr_var2: .word var2 /* address to var2 stored here */
31 | 30 | 29 | 28 | Cond域描述 | |||
0000 | EQ | Z=1 | Equal | ||||
0001 | NE | Z=0 | Not equal, or unordered | ||||
0010 | CS/HS | C=1 | Carry set / Unsigned higher or same | Great than or equal, or unordered | |||
0011 | CC/LO | C=0 | Carry clear / Unsigned lower | Less than | |||
0100 | MI | N=1 | Negative | Less than | |||
0101 | PL | N=0 | Positive or zero | Greater than or equal , or unordered | |||
0110 | VS | V=1 | Overflow | Unordered | |||
0111 | VC | V=0 | No overflow | Not unordered | |||
1000 | HI | C=1且Z=0 | Unsigned higher | Greater than, or unordered | |||
1001 | LS | C=0或Z=1 | Unsigned lower or same | Less than or equal | |||
1010 | GE | N=1且V=1 或N=0且V=0 | Signed greater than or equal | Greater than or equal | |||
1011 | LT | N=1且V=0 或N=0且V=1 | Signed less than | Less than , or unordered | |||
1100 | GT | Z=0或N=V | Signed greater than | Great than | |||
1101 | LE | Z=1或N!=V | Signed less than or equal | Less than or equal , or unordered | |||
1110 | AL | Always (unconditional) |
其它类指令请参阅 ARM Articheture Reference Manual
24 | 23 | 22 | 21 | Opcode域描述 | |
0000 | AND | Logical AND | |||
0001 | EOR | Logical Exclusive OR | |||
0010 | SUB | Subtract | |||
0011 | RSB | Reverse Subtract | |||
0100 | ADD | Add | |||
0101 | ADC | Add with Carry | |||
0110 | SBC | Subtract with Carry | |||
0111 | RSC | Reverse Subtract with Carry | |||
1000 | TST | Test | |||
1001 | TEQ | Test Equivalence | |||
1010 | CMP | Compare | |||
1011 | CMN | Compare Negated | |||
1100 | ORR | Logical OR | |||
1101 | MOV | Move | |||
1110 | BIC | Bit Clear | |||
1111 | MVN | Move Not |
指令格式: 指令{条件}{S} {目的Register},{OP1},{OP2} | "{ }"中的内容可选。即,可以不带条件只有目的寄存器,或 | ||||||
只有目的寄存器和操作数1,也可以同时包含所有选项。“S” 决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值 | |||||||
助记符 | 英文全称 | 示例、功能 | |||||
跳转指令 | B | Branch | B Label ;程序无条件跳转到标号Label处执行 | ||||
BL | Branch with Link | BL Label ;当程序无条件跳转到标号Label处执行时,同时将当前的PC值保存到R14中 | |||||
BLX | Branch with Link and exchange | BLX Label ;从ARM指令集跳转到指令中所指定的目标地址,并将处理器的工作状态有ARM状态切换到Thumb状态,该指令同时将PC的当前内容保存到寄存器R14中 | |||||
BX | Branch and exchange | BX Label ;跳转到指令中所指定的目标地址,目标地址处的指令既可以是ARM指令,也可以是Thumb指令 | |||||
数据处理 | MOV | Move | MOV R1,R0,LSL#3 ;将寄存器R0的值左移3位后传送到R1 | ||||
MVN | Move NOT | MVN R0,#0 ;将立即数0取反传送到寄存器R0中,完成后R0=-1 | |||||
CMP | Compare | CMP R1,R0 ;将寄存器R1的值与寄存器R0的值相减,并根据结果设置CPSR的标志位 | |||||
CMN | Compare negative | CMN R1,R0 ;将寄存器R1的值与寄存器R0的值相加,并根据结果设置CPSR的标志位 | |||||
TST | Test | TST R1,#0xffe ;将寄存器R1的值与立即数0xffe按位与,并根据结果设置CPSR的标志位 | |||||
TEQ | Test equivalence | TEQ R1,R2 ;将寄存器R1的值与寄存器R2的值按位异或,并根据结果设置CPSR的标志位 | |||||
ADD | Add | ADD R0,R2,R3,LSL#1 ; R0 = R2 + (R3 << 1) | |||||
ADC | Add with carry | ADCS R2,R6,R10 ; R2 = R6+R10+!C,且更新CPSR的进位标志位 | |||||
SUB | Subtract | SUB R0,R1,#256 ; R0 = R1 – 256 | |||||
SBC | Subtract with carry | SUBS R0,R1,R2 ; R0 = R1 - R2 - !C,并根据结果设置CPSR的进位标志位 | |||||
RSB | Reverse subtract | RSB R0,R1,R2 ; R0 = R2 – R1 | |||||
RSC | Reverse subtract with carry | RSC R0,R1,R2 ; R0 = R2 – R1 - !C | |||||
AND | And | AND R0,R0,#3 ; 该指令保持R0的0、1位,其余位清零。 | |||||
ORR | OR | ORR R0,R0,#3 ; 该指令设置R0的0、1位,其余位保持不变。 | |||||
EOR | Exclusive OR | EOR R0,R0,#3 ; 该指令反转R0的0、1位,其余位保持不变。 | |||||
BIC | Bit clear | BIC R0,R0,#0b1011 ; 该指令清除 R0 中的位 0、1、和 3,其余的位保持不变。 | |||||
CLZ | Count left zero | 计算操作数最高端0的个数 | |||||
乘加指令 | MUL | Multiply | MUL R0,R1,R2 ;R0 = R1 × R2 | ||||
MLA | Multiply and accumulate | MLAS R0,R1,R2,R3 ;R0 = R1 × R2 + R3,同时设置CPSR中的相关条件标志位 | |||||
SMULL | Signed multiply long | SMULL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位 R1 = (R2 × R3)的高32位 | |||||
SMLAL | Signed mul l and accumulate l | SMLAL R0,R1,R2,R3 ;R0 =(R2 × R3)的低32位+R0; R1 =(R2 × R3)的高32位+ R1 | |||||
UMULL | Unsigned multiply long | UMULL R0,R1,R2,R3 ;R0 = (R2 × R3)的低32位;R1 =(R2 × R3)的高32位 | |||||
UMLAL | Unsigned mul&accumulate lon | UMLAL R0,R1,R2,R3 ;R0 =(R2 × R3)的低位+R0;R1 =(R2 × R3)的高32位+R1 | |||||
PSR访问 | MRS | Move PSR to register | MRS R0,CPSR ;传送CPSR的内容到R0 | ||||
MSR | Move register to PSR | MSR CPSR_c ,R0 ;传送R0的内容到CPSR,但仅仅修改CPSR中的控制位域 | |||||
加载/存储指令 | LDR | Load word | LDR R0,[R1,R2]! ;将存储器地址为R1+R2的字数据读入R0,并将新地址R1+R2写入R1。 | ||||
LDRB | Load byte | LDRB R0,[R1,#8] ;将存储器地址为R1+8的字节数据读入R0,并将R0的高24位清零 | |||||
LDRH | Load half word | LDRH R0,[R1] ;将存储器地址为R1的半字数据读入寄存器R0,并将R0的高16位清零 | |||||
LDM | Load multiple | LDMFD R13!,{R0,R4-R12,PC} ;将堆栈内容恢复到寄存器(R0,R4到R12,LR) | |||||
STR | Store | STR R0,[R1],#8 ;将R0中的字数据写入R1为地址的存储器中,并将新地址R1+8写入R1 | |||||
STRB | Store byte | STRB R0,[R1,#8] ;将寄存器R0中的字节数据写入以R1+8为地址的存储器中 | |||||
STRH | Store half word | STRH R0,[R1,#8] ;将寄存器R0中的半字数据写入以R1+8为地址的存储器中 | |||||
STM | Store multiple | STMFD R13!,{R0,R4-R12,LR} ;将寄存器列表中的寄存器(R0,R4到R12,LR)存入堆栈 | |||||
数据交换 | SWP | Swap word | SWP R0,R1,[R2] ;R2所指的字数据传送到R0,同时R1的数据传送到R2所指的单元 | ||||
SWPB | Swap byte | SWPB R0,R1,[R2] ;R2所指的字节数据传送到R0,R0高24位清零,同时R1低8位送R2所指单元。 | |||||
移位指令 | LSL | Logic shift left | MOV R0, R1, LSL#2(ASL#2) ;将R1中的内容左移两位后传送到R0中,低位用0填充 | ||||
ASL | Arithmetic shift left | ||||||
LSR | Logic shift right | MOV R0, R1, LSR#2 ;将R1中的内容右移两位后传送到R0中,左端用零来填充 | |||||
ASR | Arithmetic shift right | MOV R0, R1, ASR#2 ;将R1中的内容右移两位后传送到R0中,左端用第31位的值来填充 | |||||
ROR | Rotate right | MOV R0, R1, ROR#2 ;将R1中的内容循环右移两位后传送到R0中 | |||||
RRX | Rotate right extended | 左端用进位标志位C来填充 | |||||
协处理器 | CDP | Data operations | |||||
LDC | Load | ||||||
STC | Store | ||||||
MCR | Move to coproc fr ARM reg | ||||||
MRC | M to ARM reg fr coprocessor | ||||||
并行指令前缀 | S | Signed arithmetic modulo 28 or 216 ,sets CPSR GE bit | |||||
Q | Signed saturating arithmetic | ||||||
SH | Signed arithmetic, halving results | ||||||
U | Unsigned arithmetic modulo 28 or 216 ,sets CPSR GE bit | ||||||
UQ | Unsigned saturating arithmetic | ||||||
UH | Unsigned arithmetic ,halving results | ||||||
批量传输地址模式 | Block load / store | Stack pop / push | |||||
IA | Increment after | FD | Full descending | ||||
IB | Increment before | ED | Empty descending | ||||
DA | Decrement after | FA | Full ascending | ||||
DB | Decrement before | EA | Empty ascending | ||||
ARM指令寻址方式 | 立即寻址 | ADD R0,R0,#0x3f | R0←R0+0x3f | ||||
寄存器寻址 | ADD R0,R1,R2 | R0←R1+R2 | |||||
间接寻址 | ADD R0,R1,[R2] | R0←R1+[R2] | |||||
变址寻址 | LDR R0,[R1,#4] | R0←[R1+4] | |||||
LDR R0,[R1,#4]! | R0←[R1+4]、R1←R1+4 | ||||||
LDR R0,[R1] ,#4 | R0←[R1]、R1←R1+4 | ||||||
LDR R0,[R1,R2] | R0←[R1+R2] | ||||||
多寄存器寻址 | LDMIA R0,{R1,R2,R3,R4} | R1←[R0];R2←[R0+4];R3←[R0+8];R4←[R0+12] | |||||
伪指令及伪操作 | |||||||
符号定义 | GBLA / LCLA | 定义一个全局 / 局部的数字变量,并初始化为0 | |||||
GBLL / LCLL | 定义一个全局 / 局部的逻辑变量,并初始化为F(假) | ||||||
GBLS / LCLS | 定义一个全局 / 局部的字符串变量,并初始化为空 | ||||||
SETA / SETL / SETS | 给一个数学 / 逻辑 / 字符串变量赋值 | ||||||
RLIST | 对一个通用寄存器列表定义名称,访问次序为根据寄存器的编号由低到高,与排列次序无关 | ||||||
数据定义 | DCB(=)/ DCW(DCWU) | 分配一片连续的字节 / 半字存储单元并用指定的数据初始化 | 后缀U表示不要求对齐 | ||||
DCFS(DCFSU)/DCFD(DCFDU) | 分配一片连续的(单 / 双精度的浮点数)字存储单元并用指定的数据初始化 | ||||||
DCQ(DCQU)/ DCD(DCDU) | 用于分配一片以双字 / 字为单位的连续的存储单元并用指定的数据初始化 | ||||||
DCDO | 分配字内存但愿,初始化为标号基于静态基址寄存器R9的偏移量 | ||||||
DCI | 和DCD类似,不同处在于DCI内存中的数据被标识为指令 | ||||||
SPACE(%) | DataSpace SPACE 100 ;分配连续100字节的存储单元并初始化为0 | ||||||
MAP(^) | MAP 0x100,R0 ;定义结构化内存表首地址的值为0x100+R0 | ||||||
FIELD(#) | A FIELD 16 ;定义A的长度为16字节 | ||||||
控制伪指令 | IF、ELSE、ENDIF | IF 逻辑表达式 | IF、ELSE、ENDIF伪指令能根据条件的成立与否决定是否执行某个指令序列。 | ||||
指令序列1 | 当IF后面的逻辑表达式为真,则执行指令序列1,否则执行指令序列2。其中, | ||||||
ELSE | ELSE及指令序列2可以没有,此时,当IF后面的逻辑表达式为真,则执行 | ||||||
指令序列2 | 指令序列1,否则继续执行后面的指令。 | ||||||
ENDIF | |||||||
WHILE、WEND | WHILE 逻辑表达式 | WHILE、WEND伪指令能根据条件的成立与否决定是否循环执行某个指令序列。当WHILE后面的逻辑表达式为真,则执行指令序列,该指令序列执行完毕后,再判断逻辑表达式的值,若为真则继续执行,一直到逻辑表达式的值为假。 | |||||
指令序列 | |||||||
WEND | |||||||
MACRO、MEND | MACRO | $标号在宏指令被展开时,标号会被替换为用户定义的符号, | |||||
MEXIT | $标号 宏名 $参数1,$参数2,……指令序列 | 宏指令可以使用一个或多个参数,当宏指令被展开时,这些参数被相应的值替换。 | |||||
MEND | MEXIT用于从宏定义中跳转出去 | ||||||
AREA | AREA 段名 属性1,属性2,…… | 用于定义一个代码段或数据段。其中,段名若以数字开头,则该段名需用“|”括起来,如|1_test|。 | |||||
ALIGN | AREA Init,CODE,ALIEN=3 | 指定后面的指令为8字节对齐 | |||||
CODE | CODE16、CODE32 | 指定指令序列为16位的Thumb指令或32位的ARM指令 | |||||
ENTRY | 在一个完整的汇编程序中至少要有一个ENTRY(也可以有多个,当有多个ENTRY时,程序的真正入口点由链接器指定),但在一个源文件里最多只能有一个ENTRY(可以没有)。 | ||||||
EQU(*) | 名称 EQU 表达式 {,类型} | 为程序中的常量、标号等定义一个等效的字符名称 | |||||
EXPORT | EXPORT 标号 | 用于在声明一个全局的标号,该标号可在其他的文件中引用。EXPORT可用GLOBAL代替。 | |||||
IMPORT | IMPORT 标号 | 用于通知编译器要使用的标号在其他的源文件中定义,无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的符号表中 | |||||
EXTERN | EXTERN 标号 | 用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用,如果当前源文件实际并未引用该标号,该标号就不会被加入到当前源文件的符号表中 | |||||
GET | GET 文件名 | 将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置进行汇编处理 | |||||
INCBIN | INCBIN 文件名 | INCBIN伪指令用于将一个目标文件或数据文件包含到当前的源文件中,被包含的文件不作任何变动的存放在当前文件中,编译器从其后开始继续处理 | |||||
RN | 名称 RN 表达式 | RN伪指令用于给一个寄存器定义一个别名 | |||||
ROUT | {名称} ROUT | ROUT伪指令用于给一个局部变量定义作用范围。在程序中未使用该伪指令时,局部变量的作用范围为所在的AREA,而使用ROUT后,局部变量的作为范围为当前ROUT和下一个ROUT之间。 |