[知识点] ARM指令集详解
点个小赞关注一波,持续更新……
[专栏]嵌入式软件校招笔记(点击跳转)
ARM指令集可以分为以下六种
- 数据处理指令
- 跳转指令
- 程序状态寄存器传输指令
- Load/Store指令
- 协处理器指令
- 异常中断指令
1. ARM 数据处理指令
数据处理指令大致可分为3 类:
(1)数据传送指令(如MOV、MVN)
(2)算术逻辑运算指令(如ADD,SUM,AND)
(3)比较指令(如CMP、TST)。
数据运算指令格式
<操作码><目标寄存器><第一操作寄存器><第二操作数>
操作码:表示执行哪种操作
目标寄存器:用于存储运算的结果
第一操作寄存器:存储第一个参与运算的数据(只能是寄存器)
第二操作数:第二个参与运算的数据(可以是寄存器,也可以是立即数)
数据处理指令只能对寄存器的内容操作。
所有ARM 数据处理指令均可选择使用S 后缀,以影响状态标志。比较指令CMP、CMN、TST和TEQ不需要后缀S,它们会直接影响状态标志。
默认情况下数据运算不会对条件位产生影响,当在指令后加后缀"s"后可以影响
2.1数据传送指令
2.1.1 MOV 数据传送指令
把一个数移动到目标寄存器
格式:
MOV 条件 s
条件: 就表示mov指令是否要执行,如果满足条件就执行mov
s: 是否影响cpsr的值
注:寄存器存储得数据,可以是一个常数,也可以是一个数经过左移右移得到的数据。
将立即数或寄存器(operant2)传送到目标寄存器Rd,可用于移位运算等操作。指令格式如下:
MOV{cond}{S} Rd,operand2 MOV 指令举例如下: MOV R1#0x10 @ R1=0x10 MOV R0,R1 @ R0=R1 MOVS R3,R1,LSL #2 @ R3=R1<<2,并影响标志位 MOV PC,LR @ PC=LR ,子程序返回
立即数:
由0-255之间的任意数据通过循环右移偶数位得到的数据
立即数的本质就是包含在指令当中的数,属于指令的一部分
立即数的优点:
取址的时候就可以将其读到CPU,不用单独去内存读取,速度快
立即数的缺点
不能是任意的32位的数字,有局限性
2.1.2 MVN 数据非传送指令
将立即数或寄存器(operand2)按位取反后传送到目标寄存器(Rd)。指令格式如下:
MVN{cond}{S} Rd,operand2 MVN 指令举例如下: MVN R1,#0xFF ;R1=0xFFFFFF00 MVN R1,R2 ;将R2 取反,结果存到R1
2.2算术逻辑运算指令
2.2.1 ADD 加法运算指令
将operand2 数据与Rn 的值相加,结果保存到Rd 寄存器。格式如下:
ADD{cond}{S} Rd,Rn,operand2 ADD 指令举例如下: ADDS R1,R1,#1 ;R1=R1+1 ADD R1,R1,R2 ;R1=R1+R2 ADDS R3,R1,R2,LSL #2 ;R3=R1+R2<<2
不可以两个数相加 ADD R1, #2, #2是错误的
2.2.2 SUB 减法运算指令
用寄存器Rn 减去operand2,结果保存到Rd 中,格式如下:
SUB{cond}{S} Rd,Rn,operand2 SUB 指令举例如下: SUBS R0,R0,#1 ;R0=R0-1 SUBS R2,R1,R2 ;R2=R1-R2 SUB R6,R7,#0x10 ;R6=R7-0x10
2.2.3 RSB 逆向减法指令
用寄存器operand2 减法Rn,结果保存到Rd 中,格式如下:
RSB{cond}{S} Rd,Rn,operand2
SUB 指令举例如下:
RSB R3,R1,#0xFF00 ;R3=0xFF00-R1 RSBS R1,R2,R2,LSL #2 ;R1=R2<<2-R2=R2×3 RSB R0,R1,#0 ;R0=-R1
2.2.4 ADC 带进位加法指令
将operand2 的数据与Rn 的值相加,再加上CPSR中的C 条件标志位。结果保存到Rd 寄存器。指令格式如下:
ADC{cond}{S} Rd,Rn,operand2 ADC 指令举例如下: ADDS R0,R0,R2 ADC R1,R1,R3 ;使用ADC 实现64 位加法,(R1、R0)=(R1、R0)+(R3、R2)
2.2.5 SBC 带进位减法指令
用寄存器Rn 减去operand2,再减去CPSR 中的C条件标志位的非(即若C 标志清零,则结果减去1),结果保存到Rd 中。指令格式如下:
SCB{cond}{S}Rd,Rn,operand2 SBC 指令举例如下: SUBS R0,R0,R2 SBC R1,R1,R3 ;使用SBC 实现64 位减法,(R1,R0)-(R3,R2)
2.2.6 RSC 带进位逆向减法指令
用寄存器operand2 减去Rn,再减去CPSR 中的C条件标志位,结果保存到Rd 中。指令格式如下:
RSC{cond}{S} Rd,Rn,operand2 RSC 指令举例如下: RSBS R2,R0,#0 RSC R3,R1,#0 ;使用RSC 指令实现求64 位数值的负数
2.2.7 AND 逻辑与操作指令
将operand2 值与寄存器Rn 的值按位作逻辑与操作,结果保存到Rd中。指令格式如下:
AND{cond}{S} Rd,Rn,operand2 AND 指令举例如下: ANDS R0,R0,#x01 ;R0=R0&0x01,取出最低位数据 AND R2,R1,R3 ;R2=R1&R3
2.2.8 ORR 逻辑或操作指令
将operand2 的值与寄存器Rn的值按位作逻辑或操作,结果保存到Rd 中。指令格式如下:
ORR{cond}{S} Rd,Rn,operand2 ORR 指令举例如下: ORR R0,R0,#x0F ;将R0 的低4 位置1 MOV R1,R2,LSR #4 ORR R3,R1,R3,LSL #8 ;使用ORR 指令将近R2 的高8位数据移入到R3 低8 位中
2.2.9 EOR 逻辑异或操作指令
将operand2 的值与寄存器Rn 的值按位作逻辑异或操作,结果保存到Rd中。指令格式如下:
EOR{cond}{S}Rd,Rn,operand2 EOR 指令举例如下: EOR R1,R1,#0x0F ;将R1 的低4 位取反 EOR R2,R1,R0 ;R2=R1^R0 EORS R0,R5,#0x01 ;将R5 和0x01 进行逻辑异或,结果保存到R0,并影响标志位
2.2.10 BIC 位清除指令
将寄存器Rn 的值与operand2 的值的反码按位作逻辑与操作,结果保存到Rd中。指令格式如下:
BIC{cond}{S}Rd,Rn,operand2 BIC 指令举例如下: BIC R2,R1,#0x0F ;将R1 的低4 位清零,其它位不变(将#0x0F中值为1的位对应R1中的位清零存到R2,R1并没有变)
LSL(或ASL)左移操作
LSL(或ASL)可完成对通用寄存器中的内容进行逻辑(或算术)的左移操作,按操作数所指定的数量向左移位,低位用零来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
MOV R0, R1, LSL #2 @ 将R1中的内容左移两位后传送到R0中。
MOV R1, #1 MOV R2, #2 MOV R3, #3 LSL R1,R2,R3 @ R1=(R2 <<R3)
LSR右移操作
LSR可完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用零来填充。其中,操作数可以是通用寄存器,也可以是立即数(0~31)。
MOV R0, R1, LSL #2 @ 将R1中的内容左移两位后传送到R0中。 MOV R3, #1 MOV R4, #2 MOV R5, #3 LSR R3,R4,R5 @ R3=(R4 >> R5)
2.3 比较指令
2.3.1 CMP 比较指令
指令使用寄存器Rn 的值减去operand2 的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:
本质就是一条减法指令(SUBS),只是没有将运算结果存入寄存器
CMP R1, R2:
=》CPSR寄存器如果标志位Z=1 -> 两者相等,如果Z=0 -> 两者不相等
=》标志位C=0 -> R1<R2,如果C=0或Z=1 -> R1<=R2
=》标志位C=1且Z=0 -> R1>R2
=》标志位C=1 -> R1>=R2
CMP{cond} Rn,operand2 CMP 指令举例如下: CMP R1,#10 ;R1 与10 比较,设置相关标志位 CMP R1,R2 ;R1 与R2 比较,设置相关标志位 CMP 指令与SUBS 指令的区别在于CMP 指令不保存运算结果。在进行两个数据大小判断时,常用CMP指令及相应的条件码来操作。
2.3.2 CMN 负数比较指令
指令使用寄存器Rn 与值加上operand2 的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行,指令格式如下:
CMN{cond} Rn,operand2 CMN R0,#1 ;R0+1,判断R0 是否为1 的补码,若是Z 置位
CMN 指令与ADDS 指令的区别在于CMN 指令不保存运算结果。CMN指令可用于负数比较,比如CMNR0,#1 指令则表示R0 与-1 比较,若R0 为-(即1 的补码),则Z 置位,否则Z复位。
2.3.3 TST 位测试指令
指令将寄存器Rn 的值与operand2 的值按位作逻辑与操作,根据操作的结果更新CPSR中相应的条件标志位(当结果为0时,EQ位被设置),以便后面指令根据相应的条件标志来判断是否执行。指令格式如下:
TST{cond} Rn,operand2 TST 指令举例如下: TST R0,#0x01 ;判断R0 的最低位是否为0 TST R1,#0x0F ;判断R1 的低4 位是否为0 TST 指令与ANDS 指令的区别在于TST4 指令不保存运算结果。TST指令通常于EQ、NE条件码配合使用,当所有测试位均为0 时,EQ 有效,而只要有一个测试为不为0,则NE 有效。
2.3.4 TEQ 相等测试指令
指令寄存器Rn 的值与operand2 的值按位作逻辑异或操作,根据操作的结果更新CPSR中相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:
TEQ{cond} Rn,operand2 TEQ 指令举例如下: TEQ R0,R1 ;比较R0 与R1 是否相等(不影响V 位和C 位) TST 指令与EORS 指令的区别在于TST 指令不保存运算结果。使用TEQ进行相等测试,常与EQNE 条件码配合使用,当两个数据相等时,EQ 有效,否则NE 有效。
补充:条件码
操作码 条件码助记符 标志 含义 0000 EQ Z=1 相等 0001 NE(Not Equal) Z=0 不相等 0010 CS/HS(Carry Set/High or Same) C=1 无符号数大于或等于 0011 CC/LO(Carry Clear/LOwer) C=0 无符号数小于 0100 MI(MInus) N=1 负数 0101 PL(PLus) N=0 正数或零 0110 VS(oVerflow set) V=1 溢出 0111 VC(oVerflow clear) V=0 没有溢出 1000 HI(HIgh) C=1,Z=0 无符号数大于 1001 LS(Lower or Same) C=0,Z=1 无符号数小于或等于 1010 GE(Greater or Equal) N=V 有符号数大于或等于 1011 LT(Less Than) N!=V 有符号数小于 1100 GT(Greater Than) Z=0,N=V 有符号数大于 1101 LE(Less or Equal) Z=1,N!=V 有符号数小于或等于 1110 AL 任何 无条件执行(默认) 1111 NV 任何 从不执行
MOV R1, #1 MOV R2, #2 BEQ FUNC @ if(EQ){B FUNC} 本质:if(Z==1){B FUNC} MOV R4, #3 MOV R3, #3 FUNC: MOV R5, #5 MOV R6, #7
2.4 乘法指令
ARM7TDMI(-S)具有32×32 乘法指令、32×32 乘加指令、32×32结果为64 位的乘法指令。
2.4.1 MUL 32 位乘法指令
指令将Rm 和Rs 中的值相乘,结果的低32 位保存到Rd中。(只能是两个寄存器相乘)
指令格式如下:
MUL{cond}{S} Rd,Rm,Rs MUL 指令举例如下: MUL R1,R2,R3 ;R1=R2×R3 MULS R0,R3,R7 ;R0=R3×R7,同时设置CPSR 中的N位和Z 位
2.4.2 MLA 32 位乘加指令
指令将Rm 和Rs 中的值相乘,再将乘积加上第3 个操作数,结果的低32位保存到Rd 中。指令格式如下:
MLA{cond}{S} Rd,Rm,Rs,Rn MLA 指令举例如下: MLA R1,R2,R3,R0 ;R1=R2×R3+10
2.4.3 UMULL 64 位无符号乘法指令
指令将Rm 和Rs 中的值作无符号数相乘,结果的低32位保存到RsLo 中,而高32 位保存到RdHi 中。指令格式如下:
UMULL{cond}{S} RdLo,RdHi,Rm,Rs UMULL 指令举例如下: UMULL R0,R1,R5,R8 ;(R1、R0)=R5×R8
2.4.4 UMLAL 64 位无符号乘加指令
指令将Rm 和Rs 中的值作无符号数相乘,64 位乘积与RdHi、RdLo相加,结果的低32 位保存到RdLo 中,而高32 位保存到RdHi 中。指令格式如下:
UMLAL{cond}{S} RdLo,RdHi,Rm,Rs UMLAL 指令举例如下: UMLAL R0,R1,R5,R8;(R1,R0)=R5×R8+(R1,R0)
2.4.5 SMULL 64 位有符号乘法指令
指令将Rm 和Rs 中的值作有符号数相乘,结果的低32位保存到RdLo 中,而高32 位保存到RdHi 中。指令格式如下:
SMULL{cond}{S} RdLo,RdHi,Rm,Rs SMULL 指令举例如下: SMULL R2,R3,R7,R6 ;(R3,R2)=R7×R6
2.4.6 SMLAL 64 位有符号乘加指令
指令将Rm 和Rs 中的值作有符号数相乘,64 位乘积与RdHi、RdLo,相加,结果的低32位保存到RdLo 中,而高32 位保存到RdHi 中。指令格式如下:
SMLAL{cond}{S} RdLo,RdHi,Rm,Rs SMLAL 指令举例如下: SMLAL R2,R3,R7,R6;(R3,R2)=R7×R6+(R3,R2)
2. ARM 跳转指令
跳转指令用于实现程序流程的跳转,在ARM程序中有两种方法可以实现程序流程的跳转:
- 使用专门的跳转指令;
- 直接向程序计数器PC写入跳转地址值。
通过向程序计数器PC写入跳转地址值,可以实现在4GB的地址空间中的任意跳转,在跳转之前结合使用MOV LR,PC等类似指令,可以保存将来的返回地址值,从而实现在4GB连续的线性地址空间的子程序调用。
ARM指令集中的跳转指令可以完成从当前指令向前或向后的32MB的地址空间的跳转,包括以下4条指令。
- B:跳转指令。
- BL:带返回的跳转指令。
- BLX:带返回和状态切换的跳转指令。
- BX:带状态切换的跳转指令。
3.1 B 跳转指令
跳转到指定的地址执行程序。
B{cond} label
举例如下:
B WAITA ;跳转到WAITA 标号处 B 0x1234 ;跳转到绝对地址0x1234 处
B指令是最简单的跳转指令。一旦遇到一个B指令,ARM处理器将立即跳转到给定的目标地址,从那里继续执行。注意存储在跳转指令中的实际值是相对当前PC值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。它是24位有符号数,左移两位后有符号扩展为32位,表示的有效偏移为26位(前后32MB的地址空间)。(跳转到指令B 限制在当前指令的±32Mb 的范围内。)
EQ:equal 相等 NE:not equal,不相等 GT:great than,大于 GE greate equal,大于等于 LT:less than,小于 LE:less equal,.小于等于
3.2 BL 带链接的跳转指令
BL是另一个跳转指令,但在跳转之前,会在寄存器R14中保存PC的当前内容,因此,可以通过将R14的内容重新加载到PC中,来返回到跳转指令之后的那个指令处执行。该指令是实现子程序调用的一个基本但常用的手段。
指令将下一条指令的地址拷贝到R14(即LR)链接寄存器中,然后跳转到指定地址运行程序。
BL{cond} label
举例如下:
BL Label ;当程序无条件跳转到标号Label处执行时,同时将当前的PC 值保存到R14中
跳转指令B 限制在当前指令的±32MB 的范围内。BL 指令用于子程序调用。
3.3 BX 带状态切换的跳转指令
跳转到Rm 指定的地址执行程序,若Rm 的位[0]为1,则跳转时自动将CPSR 中的标志T 置位,即把目标地址的代码解释为Thumb代码;若Rm 的位[0]为0,则跳转时自动将CPSR 中的标志T 复位,即把目标地址的代码解释为ARM代码。指令格式如下:
BX{cond} Rm 举例如下: ADRL R0,ThumbFun+1 BX R0 ;跳转到R0 指定的地址,并根据R0 的最低位来切换处理器状态
3.4 BLX
BLX目标地址:跳转,改变状态及保存PC值
汇编指令实例
area RESET, code,readonly code32 entry start mov r0, #9 mov r1, #15 loop cmp r0,r1 ;判断两个寄存器值是否相等 beq stop ;如果相等,结束程序 cmp r0,r1 ;判断两个寄存器值 subgt r0,r0,r1 ;如果大于,则执行r0=r0-r1 sublt r1,r1,r0 ;如果小于,则执行r1=r1-r0 bloop ;跳转到loop标号继续执行 stop end
伪代码: @ if(R1== R2) @ STOP() @ else if(R1 >R2) @ { @ R1 = R1- R2; @ goto START; @ } @ else{ @ R2 = R2 - R1; @ goto START; @ } 汇编: MOV R1, #1 MOV R2, #4 START: CMP R1, R2 BEQ STOP SUBGT R1, R2, R3 SUBLT R2, R2, R1 B START STOP: @死循环防止程序跑飞 B STOP .end @汇编的结束
3. Load/Store指令
ARM 处理器是冯诺依曼存储结构,程序空间、RAM 空间及IO 映射空间统一编址,除对对RAM 操作以外,对外围IO、程序数据的访问均要通过加载/存储指令进行。
ARM 的加载/存储指令是可以实现字、半字、无符/有符字节操作;批量加载/存储指令可实现一条指令加载/存储多个寄存器的内容,大大提高效率。
Load/Store 内存访问指令在 ARM 寄存器和存储器之间传送数据。(访问(读写)内存)ARM 指令中有 3 种基本的数据传送指令。
(1)单寄存器 Load/Store 指令(Single Register),这些指令在 ARM 寄存器和存储器之间提供更灵活的单数据项传送方式。数据项可以是字节、16 位半字或 32 位字。
(2)多寄存器 Load/Store 内存访问指令。这些指令的灵活性比单寄存器传送指令差,但可以使大量的数据更有效地传送。它们用于进程的进入和退出、保存和恢复工作寄存器及复制存储器中的一块数据。
(3)单寄存器交换指令(Single Register Swap)。这些指令允许寄存器和存储器中的数值进行交换,在一条指令中有效地完成 Load/Store 操作。它们在用户级编程中很少用到。它的主要用途是在多处理器系统中实现信号量(Semaphores)的操作,以保证不会同时访问公用的数据结构。
(4)单寄存器的 Load/Store 指令,这种指令用于把单一的数据传入或者传出一个寄存器。支持的数据类型有字节(8 位)、半字(16 位)和字(32 位)。
3.1. 单寄存器的Load/Store指令
如表 3-8 所示列出了所有单寄存器的 Load/Store 指令。
使用单一数据传送指令(STR 和LDR)来装载和存储单一字节或字的数据从/到内存。LDR指令用于从内存读取数据放入寄存器中;STR 指令用于将寄存器中的数据保存到内存。指令格式如下:
LDR{cond}{T} Rd,<地址>;加载指定地址上的数据(字),放入Rd中 STR{cond}{T} Rd,<地址>;存储数据(字)到指定地址的存储单元,要存储的数据在Rd中 LDR{cond}B{T} Rd,<地址>;加载字节数据,放入Rd中,即Rd最低字节有效,高24位清零 STR{cond}B{T} Rd,<地址>;存储字节数据,要存储的数据在Rd,最低字节有效
其中,T 为可选后缀,若指令有T,那么即使处理器是在特权模式下,存储系统也将访问看成是处理器是在用户模式下。T在用户模式下无效,不能与前索引偏移一起使用T。
LDR/STR 指令寻址是非常灵活的,由两部分组成,一部分为一个基址寄存器,可以为任一个通用寄存器,另一部分为一个地址偏移量。地址偏移量有以下3种格式:
(1) 立即数。立即数可以是一个无符号数值,这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:
LDR R1,[R0,#0x12] ;将R0+0x12 地址处的数据读出,保存到R1中(R0 的值不变) LDR R1,[R0,#-0x12];将R0-0x12 地址处的数据读出,保存到R1中(R0 的值不变) LDR R1,[R0] ;将R0 地址处的数据读出,保存到R1 中(零偏移)
(2)寄存器。寄存器中的数值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例值。指令举例如下:
LDR R1,[R0,R2] ;将R0+R2 地址的数据计读出,保存到R1中(R0 的值不变) LDR R1,[R0,-R2] ;将R0-R2 地址处的数据计读出,保存到R1中(R0 的值不变)
(3)寄存器及移位常数。寄存器移位后的值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:
LDR R1,[R0,R2,LSL #2] ;将R0+R2*4地址处的数据读出,保存到R1中(R0,R2的值不变) LDR R1,[R0,-R2,LSL #2];将R0-R2*4地址处的数据计读出,保存到R1中(R0,R2的值不变)
从寻址方式的地址计算方法分,加载/存储指令有以下4 种形式:
(1)零偏移。Rn 的值作为传送数据的地址,即地址偏移量为0。指令举例如下:
LDR Rd,[Rn]
(2)前索引偏移。在数据传送之前,将偏移量加到Rn 中,其结果作为传送数据的存储地址。若使用后缀“!”,则结果写回到Rn中,且Rn 值不允许为R15。指令举例如下:
LDR Rd,[Rn,#0x04]! LDR Rd,[Rn,#-0x04]
(3)程序相对偏移。程序相对偏移是索引形式的另一个版本。汇编器由PC 寄存器计算偏移量,并将PC寄存器作为Rn 生成前索引指令。不能使用后缀“!”。指令举例如下:
LDR Rd,label ;label 为程序标号,label 必须是在当前指令的±4KB范围内
(4) 后索引偏移。Rn 的值用做传送数据的存储地址。在数据传送后,将偏移量与Rn相加,结果写回到Rn中。Rn 不允许是R15。指令举例如下:
LDR Rd,[Rn],#0x04
地址对准--大多数情况下,必须保证用于32 位传送的地址是32 位对准的。
加载/存储字和无符号字节指令举例如下:
LDR R2,[R5] ;加载R5 指定地址上的数据(字),放入R2 中 STR R1,[R0,#0x04] ;将R1 的数据存储到R0+0x04存储单元,R0 值不变 LDRB R3,[R2],#1 ;读取R2 地址上的一字节数据,并保存到R3中,R2=R3+1 STRB R6,[R7] ;读R6 的数据保存到R7 指定的地址中,只存储一字节数据
加载/存储半字和带符号字节。这类LDR/STR 指令可能加载带符字节\加载带符号半字、加载/存储无符号半字。偏移量格式、寻址方式与加载/存储字和无符号字节指令相同。指令格式如下:
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
记录本人校招过程中遇到的问题及笔记整理!后续会持续更新