ARM 指令集是针对ARM体系架构设计的指令。在BootLoader引导的第一阶段以及内核的第一阶段都会有一个使用汇编语言编写的文件,在不跑操作系统的裸板中也有一段用来初始化开发板环境的汇编代码。所以无论是开发带操作系统的板子,还是裸板开发,汇编语言都很有必要学习一番,最少要了解一些常用的汇编指令。要想设计出性能超强的系统,ARM的工作原理是必须掌握的。
ARM指令集可以分为以下六种
典型的arm指令语法是:< opcode > {< cond >} {s} < Rd >, < Rn >,< shifter_operand >
ARM指令寻址指的是寻找操作数的地址。寻址方式可以分为:
这些指令的操作数有3种格式:立即数2、寄存器方式(操作数即为寄存器的数值)、寄存器移位方式(操作数为寄存器中的值做相应的移位)。寄存器移位方式3中有:ASR(算术右移)、LSL(逻辑左移)、LSR(逻辑右移)、ROR(循环右移)、RRX(带扩展的循环右移),移位的位数可以使用立即数也可以使用寄存器方式表示。 所以数据处理指令寻址方式有11种。
load/store指令的寻址方式由两部分组成。一部分为一个基址寄存器,另一部分为一个地址偏移量。地址偏移量有3种格式:立即数、寄存器、寄存器移位。同样寻址方式的地址计算方法有3种:普通的偏移量、事先更新方法、事后更新方法。
在需要加载或者是存储大量数据的时候,可以使用批量load/Store指令操作。一条批量指令可以实现在一组寄存器和一块连续的内存单元之间传输数据。一次最多传输16个内存单元数据。指令中寄存器和内存单元的对应关系是,编号低的寄存器对应于内存中低地址单元。
格式为:LDM|STM{< cond >} < addressing_mode> < Rn >{!}, < registers >{^} < addressing_code>表示地址的变化方式,有以下四种:
对于通常的数据传输来说,由于load指令和store指令可以采用相同的寻址方式。但是对于数据栈的操作,数据写入内存和从内存读出的顺序不同,所以使用的寻址方式也不一样。栈寻址方式有:FD、ED、FA、EA4。
杂类指令包括操作数为半字、带符号字节、双字节的load/store指令。语法格式为:
LDR|STR{< cond >}H |SH|SB|D < Rd>,< addressing_mode>
ARM中有两种方式可以实现程序的跳转:使用跳转指令、直接向PC中写入目标地址值。两种的区别是直接操作PC寄存器,可以实现在4GB的地址空间任意跳转(长跳转)。ARM的跳转指令可以从当前指令向前或向后32MB的地址空间跳转。指令有B、BL、BLX、BX L将PC的值保存到LR寄存器中,X带状态切换的跳转(可以切换到thumb指令集,目标地址处的指令类型有目标地址的bit[0]决定)。
数据处理指令可以分为3类:数据传送指令、算术逻辑运算指令、比较指令。操作数的计算方法参考上述数据处理指令寻址方式部分。其中算术逻辑指令会将结果存入目标寄存器,同时更新CPSR中的调价标志位。而比较指令不保存运算结果,只更新CPSR中相应的条件标志位。
数据传输指令有:MOV MVN
MOV|MVN {< cond >} {s} < Rd >, < shifter-operand >
MOV(MVN)指令是将< shifter_operand >表示的数据(的反码)传送到目标寄存器< Rd>中。
note:MOVS PC,LR指令可以实现从某些异常中断中返回。PC为目标寄存器并且S位被设置,指令执行时会将当前处理器模式对应的SPSR的值复制到CPSR中。
比较指令有:CMP CMN TST TEQ
< opcode > { < cond >} < Rn > ,< shifter_operand >
CMP指令是将< Rn >中的数值-< shifet_operand >的值,根据操作结果更新CPSR中相应的条件标志位。
CMN指令是将< Rn >中的数值+< shifet_operand >的值,根据操作结果更新CPSR中相应的条件标志位。
TST指令是将< Rn >中的数值与< shifet_operand >的值做按位与操作,根据操作结果更新CPSR中相应的条件标志位。
TEQ指令是将< Rn >中的数值与< shifet_operand >的值按位做异或操作,根据操作结果更新CPSR中相应的条件标志位。
算术逻辑指令有ADD SUB RSB ADC SBC RSC AND BIC EOR ORR
< opcode > {< cond >} {s} < Rd >, < Rn >, < shifter_operand >
ADD将操作数与寄存器< Rn >中的值相加,并把结果保存到目标寄存器。
ADC带位加法指令,在ADD的基础上再加上CPSR中的C条件标志位的值。
eg:64位操作数相加。R0和R1中放置一个64位的源操作数,R0中放置低33位,R2和R3中也是一个64位的源操作数,R2中存放低32位
ADDS R4,R0,R2
ADC R5 , R1,R3 ==>R5R4就是计算出来的结果
SUB、SUC减法指令和带位减法指令。SUC在SUB的基础上再减去CPSR中C条件标志位的反码。这两个指令联合使用同样也可以进行64位数的减法操作。
SUBS R4,R0,R2
SUC R5,R1,R3
RSB、RSC逆向减法指令以及带位逆向减法指令。 < Rd> = < shifter_operand> - < Rn >
乘法指令:(这个使用的不多)
CLZ {< cond >} < Rd >, < Rm >。指令用于计算寄存器中操作数最高端0的个数。
ARM指令集中的除法运算是通过协处理器来实现的,所以没有除法算术的指令。
AND、ORR、EOR、BIC分别是按位逻辑与、或、异或、清除操作。
ARM中有两条指令用于在状态寄存器和通用寄存器之间传送数据。程序不能通过直接修改CPSR中的T控制位直接将程序状态切换到thumb状态,必须通过BX等指令完成程序状态的切换。通常程序状态寄存器修改是通过“读->改->写”的方式来实现的。
load指令用于从内存中读取数据放入到寄存器中,store指令用于将寄存器中的数据保存到内存中。
对于大量搬移数据,ARM还提供了批量Load/Store内存访问指令。批量load指令可以一次性从连续的内存单元中读取数据,传送到寄存器中。而批量store指令则是异性性将寄存器列表中的多个寄存器值写入到内存中。
LDM|STM {< cond >} < addressing_mode > Rn{!} , < registers >{^}
命令中的**!**指令执行操作后会将操作数的内存地址写入基址寄存器< Rn >中,即更新基地址。
^ 表示指令执行时将当前处理器模式下的SPSR值复制到CPSR中。寄存器中不包含PC时,作用指示指令中所用的寄存器为用户模式下的寄存器。
在数据传送指令中有一条比较特殊的指令。对于信号量的操作要求是一个原子操作(不能被中断,即只有一条指令)。SWP指令完成读取和修改寄存器的时,所以用于信号量的操作。
swp {< cond >} < Rd > ,< Rm >, [< Rn >] 执行的结果是把Rn的内容读取到Rd中,同时将Rm寄存器的内容写入到Rn中。
ARM协处理器指令包括以下3类:
CDP协处理器操作指令。CDP指令让ARM处理器能够通知ARM协处理器执行特定的操作,该操作由协处理器完成。
LDC指令从连续的内存单元将数据读取到协处理器的寄存器中。而STC指令将协处理器的寄存器中的数据写入到一系列的内存单元中。
格式:
LDC { < cond >} {L} < coproc > , < CRd > ,< addressing_mode >
LDC2 { L } < coproc >, < CRd >, < addressing_mode >
STC {< cond >} {L} < coproc > , < CRd > ,< addressing_mode >
STC2 { L } < coproc >, < CRd >, < addressing_mode >
MCR指令将ARM处理器的寄存器中的数据传送到协处理器的寄存器中,而MRC则是将协处理器寄存器中的数值传送到ARM处理器的寄存器中。
MCR {< cond >} < coproc > ,< opcode_1>, < Rd >, < CRn > , < CRm > {,< opcode2 >}
MCR2 < coproc > ,< opcode_1>, < Rd >, < CRn > , < CRm > {,< opcode2 >}
MRC {< cond >} < coproc > ,< opcode_1>, < Rd >, < CRn > , < CRm > {,< opcode2 >}
MRC2 < coproc > ,< opcode_1>, < Rd >, < CRn > , < CRm > {,< opcode2 >}
< Rd >:为ARM寄存器,其值将被传送到协助利器的寄存器中或者是从协助处理寄存器中读取数值。
< CRn >:为协助处理器寄存器
< CRm >:为附加的目标寄存器或者源操作数寄存器。
在ARM概述中,咱们知道ARM有7种模式,可以分为用户模式和特权模式。在特权模式中可以访问系统的所有资源以及任意切换到别的模式。但是在用户模式中,权限就没有这么大,那么用户模式下要访问系统的资源怎么办呢?ARM提供了异常中断指令SWI,通过软中断实现在用户模式中对操作系统中特权模式的程序的调用。
SWI {< cond >} < immed_24 > 操作系统通过24位的立即数来区分用户程序请求的服务类型。
立即数是32bit,但是并不是所有的操作数都是立即数。立即数=8位数据>>偶数陪(最多是移位30位,有些地方说是32位,右移32位其实就是没有移位),立即数的构成是使用移位最少的那个方式,所以数据宽度超多了8bit肯定就不是立即数了。在ARM指令集中立即数使用#标记。 ↩︎
算术右移是带有符号位,即最高位的值保留;逻辑右移是用0填充左边被移走的位;循环右移是用右边移出的位填充左边的位;带拓展的循环右移是使用CPSR中的C位填充最高位。 ↩︎
F(full)、E(empty):栈指针指向栈顶元素(最后一个入栈的数据)时称为full栈,栈指针指向与栈顶元素相邻的一个可用数据单元时称为empty栈。D(descending):数据栈向内存地址减少的方向增长,A(ascending)数据栈向内存地址增加的方向增长。 ↩︎