第三章 ARM指令集
3.1 ARM指令集简介
- ARM微处理器的ARM指令集 ,所有的指令长度都是32位 ,并且大多数指令都在一个单独指令周期内执行。
- 主要特点:
- 指令是条件执行的
- ARM微处理器的指令集是加载/存储型的
- 在多寄存器操作指令中一次最多可以完成 16个寄存器的数据传送
- 主要特点:
3.1.1 ARM指令格式
一、 ARM指令格式
<opcode>{<Condition>}{S} {<Rd>}{,<Rn>}{,<Operand2>}
-
格式中
< >
的内容必不可少,{ }
中的内容可省略。<opcode>
表示操作码,如ADD表示算术加法{<cond>}
表示指令执行的条件域,如EQ、NE等{S}
决定指令的执行结果是否影响CPSR的值,使用该后缀则指令执行的结将果影响CPSR的值,否则不影响。<Rd>
表示目标或源寄存器<Rn>
表示第一个操作数,为寄存器<Operand2>
表示第二个操作数,可以是立即数、寄存器和寄存器移位操作数
-
除了
<opcode>
其余域都可以选择使用。 -
举例
例如: 指令ADDEQS R0,R1,#8;操作码为ADD,条件域cond为EQ,S表示该指令的执行影响CPSR寄存器的值,目的寄存器Rd为R0,第一个操作数寄存器Rn为R1,第二个操作数OP2为立即数#8。执行结果:R0 = R1+ 8
二、 指令的机器码
- 从形式上看,ARM指令在机器中的表示格式是用32位的二进制数表示。
- 如ARM中有一条指令为:
ADDEQS R0, R1, #8
- 其二进制代码形式为
- 如ARM中有一条指令为:
31~28 | 27~25 | 24~21 | 20 | 19~16 | 15~12 | 11~0 |
---|---|---|---|---|---|---|
0000 | 001 | 0100 | 1 | 0001 | 0000 | 000000001000 |
cond | opcode | s | Rn | Rd | Op2 |
-
ARM指令一般可以分为5个域
- 第1个域–4位
- 是4位[31:28]的条件码域,4位条件码共有16种组合
- 第2个域–8位
- 是指令代码域[27:20],除了指令编码外,还包含几个很重要的指令特征和可选后缀的编码
- 第3个域–4位
- 是第1个操作数寄存器Rn,是4位[19:16],为R0~R15共16个寄存器编码
- 第4个域–4位
- 是目标或源寄存器Rd,是4位[15:12],为R0~R15共16个寄存器编码
- 第5个域–12位
- 是第二个操作数[11:0]。
- 第1个域–4位
三、指令的可选后缀
-
S后缀
- 指令中使用S后缀时,指令执行后,CPSR的条件标志位将被刷新
- 不使用S后缀时,指令执行后,CPSR的条件标志将不会发生变化。
例:假设R0=0x1,R3=0x3,指令执行之前CPSR部分标志位为 nZcv,分别执行如下指令CPSR的值有何变化?SUB R1,R0,R3SUBS R1,R0,R3
- 分析:
- 执行第1条指令对于标志寄存器的值没有任何影响,因此CPSR的值不变。
- 执行第2条指令后CPSR=Nzcv。
-
!后缀
-
如果指令地址表达式中不含!后缀,则基址寄存器中的地址值不会发生变化。
-
指令中的地址表达式中含有!后缀时,指令执行后,基址寄存器中的地址值将发生变化,变化的结果如下:
- 基址寄存器中的值(指令执行后)=指令执行前的值+地址偏移量
例:分别执行下面两条指令有何区别?LDR R3,[R0,#4] LDR R3,[R0,#4]!
- 分析:
- 第1条指令没有!后缀,指令的结果是把R0加4作为地址指针,把这个指针所指向的地址单元所存储的数据读入R3,R0的值不变
- 第2条指令除了实现以上操作外,还把R0+4的结果送到R0中
-
使用!后缀需要注意如下事项:
- !后缀必须紧跟在地址表达式后面,而地址表达式要有明确的地址偏移量
- !后缀不能用于R15(PC)的后面
- 当用在单个地址寄存器后面时,必须确信这个寄存器有隐性的偏移量
- 例如“STMIA R7!, {R0 – R3}”
- 此时地址基址寄存器R7的隐性偏移量是16字节(因为连续压栈了4个寄存器的值,每一个寄存器的值为4字节长度,即一个字)
- 如果R7的初始值为 0X40000000,则该语句结束后为0X40000010
- 例如“STMIA R7!, {R0 – R3}”
-
-
B后缀
- 指令所涉及的数据是一个字节,不是一个字或半字
LDR R4,[R0]R4=[R0],指令传送一个字 LDRB R4,[R0]R4=[R0],指令传送一个字节 LDREQB R4,[R0]如果相等则执行,R4=[R0],指令传送一个字节
3.1.2 ARM指令的条件码
-
当处理器工作在 ARM状态时,几乎所有的指令均能根据CPSR中的条件标志位的状态和指令的条件域有条件地执行。
- 当指令的执行条件满足时,指令被执行,否则指令被忽略
- 条件后缀只影响指令是否执行,不影响指令的内容
-
ARM指令的条件码和助记符如下表所示:(不用记)
条件码 条件码助记符 CPSR中条件标志位值 含义 0000 EQ Z=1 相等 0001 NE Z=0 不相等 0010 CS/HS C=1 无符号数大于或等于 0011 CC/LO C=0 无符号数小于 0100 MI N=1 负数 0101 PL N=0 正数或零 0110 VS V=1 溢出 0111 VC V=0 未溢出 1000 HI C=1且Z=0 无符号数大于 1001 LS C=0或Z=1 无符号数小于或等于 1010 GE N=V 带符号数大于或等于 1011 LT N!=V 带符号数小于 1100 GT Z=0且N=V 带符号数大于 1101 LE Z=1或N!=V 带符号数小于或等于 1110 AL 无条件执行 1111 NV ARMV3之前 从不执行(不要使用) - 条件后缀和S后缀的关系
- 如果既有条件后缀又有S后缀,则书写时S排在后面
- 如:
ADDEQS R1,R0,R2
- 该指令在Z=1时执行,将R0+R2的值放入R1,同时刷新条件标志位
- 如:
- 条件后缀是要测试条件标志位,而S后缀是要刷新条件标志位
- 条件后缀要测试的是执行前的标志位,而S后缀是依据指令的结果改变条件标志
- 如果既有条件后缀又有S后缀,则书写时S排在后面
- 条件后缀和S后缀的关系
3.1.3 ARM指令分类
- ARM 指令可以分为:分支指令、数据处理指令、存储访问指令、协处理器指令、杂项指令五类。
- 分支指令:分支指令用于控制程序的执行流程、实现ARM代码与Thumb代码之间进行切换。
- 数据处理指令:数据处理指令在通用寄存器上执行计算
- 主要分为3种:算术/逻辑指令、比较指令和乘法指令。
- 存储访问指令:用于 加载/存储 存放于MCU片外存储系统中的数据。
- 加载指令用于从内存中读取数据放入寄存器中
- 存储指令用于将寄存器中的数据保存到内存中
- 协处理器指令:ARM协处理器指令用于控制外部的协处理器。包括:
- 数据处理指令:启动一个协处理器专用的内部操作。
- 数据转移指令:使数据在协处理器和存储器之间进行转移。
- 寄存器转移指令:协处理器值转移到ARM寄存器或ARM寄存器的值转移到协处理器。
- 杂项指令:包括状态寄存器转移指令和异常中断产生指令。
- 状态寄存器转移指令
- 将CPSR或SPSR的内容转移到一个通用寄存器,或者反过来将通用寄存器的内容写入CPSR或SPSR寄存器
- ARM有两条异常中断产生指令,分别为 软中断指令SWI 和 断点中断指令BKPT
- 状态寄存器转移指令
3.2 指令寻址方式
3.2.1 立即数寻址
-
立即数寻址也叫立即寻址,操作数本身就在指令中给出,取出指令也就取到了操作数。这个操作数被称为立即数,对应的寻址方式也就叫做立即数寻址。
-
立即数要求以“#”为前缀
- 对于以十六进制表示的立即数,还要求在“#”后加上**“0x”或“&”**
- 对于以二进制表示的立即数,要求在“#”后加上**“0b”**
- 对于以十进制表示的立即数,要求在“#”后加上**“0d”或缺省**
-
举例
-
MOV R0, #15 ; R0←#15
-
此指令是将立即数15传送到R0中,该指令的机器编码为E3A0000FH(H表示16进制)
-
由指令编码中0x00F即可得到一个32位的立即0x0000000FH,这个数再送入R0
-
-
Q:如何在指令编码中表达32位立即数?
-
A:
-
-
3.2.2 寄存器寻址
- 寄存器寻址就是利用寄存器中的数值作为操作数,这种寻址方式是各类微处理器经常采用的一种方式,也是一种执行效率较高的寻址方式
- 例:
- MOV R1,R2 ;R1←R2
- ADD R0,R1,R2 ;R0←R1+R2
- 例:
3.2.3 寄存器移位寻址
- 当第二操作数为寄存器型时,在执行寄存器寻址操作时,也可以对第二操作数寄存器进行移位,此时第二操作数形式为:
- ADD Rd, Rn, Rm,{<shift>}
- 其中:
- Rm:称为第二操作数寄存器
- <shift>:用来指定移位类型和移位位数,有两种形式:
- 5位立即数(其值小于32)
- 寄存器(用Rs表示)(其值小于32)
- 其中:
- ADD Rd, Rn, Rm,{<shift>}
- 在指令执行时,将寄存器移位后的内容,作为第二操作数参与运算。
- 例如指令:
- ADD R3,R2,R1,LSR #2 ;R3←R2+(R1右移2位)
- ADD R3,R2,R1,LSR R0 ;R3←R2+(R1右移R0位)
- 例如指令:
- 第二操作数的移位方式
- LSL 逻辑左移 :
- 逻辑左移,空出的最低有效位用0填充。
- SUB R3,R2,R1,LSL #2 ;R3←R2-(R1左移2位)
- LSR 逻辑右移 :
- 逻辑右移,空出的最高有效位用0填充。
- SUB R3,R2,R1,LSR R0 ;R3←R2-(R1右移R0位)
- ASL 算术左移 :
- 算术左移,由于左移空出的有效位用0填充,因此它与LSL同义。
- ASR 算术右移 :
- 算术右移 (Arithmetic Shift Right) 。算术移位的对象是带符号数,移位过程中必须保持操作数的符号不变。如果源操作数是正数,空出的最高有效位用0填充,如果是负数用1填充。
- ROR 循环右移
- 循环右移(Rotate Right),移出的字的最低有效位依次填入空出的最高有效位
- RRX 带扩展的循环右移
- 带进位位的循环右移(Rotate Right Extended) 。将寄存器的内容循环右移1位,空位用原来C标志位填充
- SUB R3,R2,R1,RRX ;R3←R2-(R1带进位位循环右移1位)
- LSL 逻辑左移 :
- 第二操作数的移位位数
- 移位位数可以用立即数方式或者寄存器方式给出,其值均小于32,应为0—31。
- 如下所示:
- ADD R3,R2,R1,LSR #2 ;R3=R2+(R1右移2位)
- ADD R3,R2,R1,LSR R4 ;R3=R2+(R1右移R4位)
- 寄存器R1的内容分别逻辑右移2位、R4位,再与寄存器R2的内容相加,结果放入R3中。
3.2.4 寄存器间接寻址
- 寄存器间接寻址就是以寄存器中的值作为操作数的地址,而操作数本身存放在存储器中。
- 例:LDR R0,[R4] ;R0←[R4]
3.2.5 基址变址寻址
-
变址寻址
-
也叫基址变址寻址:将基址寄存器的内容与指令中给出的地址偏移量相加,得到操作数所在的存储器的有效地址。
-
变址寻址方式常用于访问某基地址附近的地址单元。(4K范围的偏移)
- 例如: LDR R0,[R1,#4] ;R0←mem32[R1+4]
-
-
加偏移地址的方式
-
前变址模式(不修改基址寄存器):
-
先基址+偏址,生成操作数地址,再做指令指定的操作。也叫前索引偏移。
-
-
自动变址模式(修改基址寄存器):
- 先基址+偏址,生成操作数地址,做指令指定的操作
- 然后自动修改基址寄存器。
- 例如:LDR R0,[R1,#4]!
- 先 R0←mem32 [R1+4]
- 然后 R1←R1+4
- ! 表示更新基址寄存器。
- 例如:LDR R0,[R1,#4]!
-
后变址模式(修改基址寄存器):
-
基址寄存器不加偏移作为操作数地址。
-
完成指令操作后,用(基址+偏移)的值修改基址寄存器
-
即先用基地址传数,然后修改基地址(基址+偏移),也叫后索引偏移
-
-
-
偏移地址形式
-
可以是一个立即数,也可以是另一个寄存器,并且还可以是寄存器移位操作。
- 常用的是立即数偏移的形式。
-
如下所示:
-
LDR R2,[R3,#0X0C] ;R2< —[R3+0X0C]
-
STR R1,[R0,#-0x4]!
- ;R1 —>mem32[R0-4],
- ;R0=R0-4
-
LDR r0,[r1,r2] ;r0<—mem32[r1+r2]
-
LDR r0,[r1,r2,LSL #2] ;r0<—mem32[r1+r2*4]]
-
-
3.2.6 多寄存器寻址
- 采用多寄存器寻址方式,一条指令可以完成多个寄存器值的传送。
- 这种寻址方式是多寄存器传送指令 LDM/STM 的寻址方式,这种寻址方式中用一条指令最多可传送16个通用寄存器的值。
- 连续的寄存器间用
-
连接,否则用,
分隔。- LDMIA R0!,{R1-R4} ;R1←**[R0]、R2←[R0+4]、R3←[R0+8]、R4←[R0+12]**
- 注意:
- 对于所有LDM/STM指令而言,寄存器序号低的,在低地址单元,序号大的在高地址单元!
- 与书写顺序无关!
- 4种寻址操作
- LDMIA/STMIA Increment After(先传送,后地址加4)
- LDMIB/STMIB Increment Before(先地址加4 ,后传送)
- LDMDA/STMDA Decrement After(先传送,后地址减4)
- LDMDB/STMDB Decrement Before (先地址减4,后传送)
3.2.7 堆栈寻址
-
堆栈是一种数据结构,按后进先出(Last In First Out, LIFO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
-
堆栈可分为两种增长方式:
- 向上生长:向高地址方向生长,称为递增堆栈
- 向下生长:向低地址方向生长,称为递减堆栈
-
根据堆栈指针指向的数据位置的不同,可分为:
- 满堆栈:堆栈指针指向最后压入堆栈的有效数据项,称为满堆栈;
- 空堆栈:堆栈指针指向下一个待压入数据的空位置,称为空堆栈。
-
这样就有4种类型的堆栈表示递增和递减的满和空堆栈的各种组合。
- 四种类型的堆栈工作方式
- 满递增堆栈FA(Full Ascending):堆栈指针指向最后压入的数据,且由低地址向高地址生长。
- 满递减堆栈FD(Full Descending) :堆栈指针指向最后压入的数据,且由高地址向低地址生长。
- 空递增堆栈EA(Empty Ascending):堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生长。
- 空递减堆栈ED(Empty Descending):堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生长。
- 也就是说上面的递增递减都指的是堆栈生长方向,即入栈时递增还是递减
- 四种类型的堆栈工作方式
3.2.8 相对寻址
- 与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。
3.3 ARM指令集
3.3.1 分支指令
-
在ARM中有两种方式可以实现程序的跳转:
- 一种是使用分支转移指令直接跳转;
- 另一种则是直接向PC寄存器赋值来实现跳转。
-
ARM的分支转移指令,可以从当前指令向前或向后的32MB的地址空间跳转,根据完成的功能它可以分为以下4种
一、B & BL
-
B 指令与 BL 指令的编码格式如下:
- 从编码中看到L控制了PC与LR寄存器之间的开关。
- 当L=0 时,该开关断开,指令为B指令;
- 当L=1时,该开关接通,指令为BL指令。
- Signed_immed_24表示24位有符号的立即数(偏移量)
- 从编码中看到L控制了PC与LR寄存器之间的开关。
-
B 和 BL 指令的助记符格式为:
- B{<cond>} <target>和BL{<cond>} <target>
- cond表示指令执行条件
- target表示跳转地址
- 功能:跳转到指定地址执行,地址范围限制在当前PC寄存器所指向的指令地址的±32MB范围
- 跳转指令也叫程序转移指令。
- 写汇编程序时,可以跳转到一个绝对地址,如:
- B 0x1234 (注: B #0x1234是错误的 )
- 编译器会把该绝对地址转换为相对地址放入指令中
- B{<cond>} <target>和BL{<cond>} <target>
-
signed_immed_24 间接提供目标地址,真正的目标地址是由处理器根据这个有符号数和当前的PC值计算出来的。
- 具体计算为:
- 先将 signed_immed_24 左移两位(即具有26位的偏移量),并扩展为32位有符号数
- 然后再将这32位有符号数与 PC 的当前值相加,得到实际的跳转地址。
- 因此B 和 BL 指令转移的偏移量为 26 位,即转移的跨度为前后 32MB 地址空间
- 具体计算为:
-
B和BL的区别:
- BL在跳转之前会把BL指令的下一条指令地址(断点地址)保存到连接寄存器 LR(R14),因此程序在必要的时候可以通过将 LR 的内容加载到 PC 中,使程序返回到跳转点。
- BL 指令经常被用来调用一个子程序。
二、BX
-
BX 指令的格式:
- BX{<cond>} Rm
- cond表示指令执行条件
- Rm寄存器,值是绝对地址值,不是偏移量,在指令执行后,Rm中的地址值与#0XFFFF FFFE 进行 “与” 运算,再被复制到程序计数器PC。
- Rm[0]为1时,强制程序从ARM指令状态跳转到Thumb指令状态。
- Rm[0]为0时,强制程序从Thumb 指令状态跳转到ARM指令状态。
- 功能: 跳转到指令中所指定的目标地址,并实现状态切换。(T<-Rm[0])
-
指令编码格式
三、BLX
- BLX 指令的格式有两种:
- BLX <target> 和 BLX{<cond>} Rm
- 以target方式提供目标地址的 BLX 指令的功能是:
- 把程序跳转到指令中所指定的目标地址继续执行,并同时将处理器的工作状态从ARM状态切换到Thumb状态,并将下一条的地址保存到寄存器 LR 中。
- 以 Rm 方式提供目标地址的 BLX 指令,除了跳转和下一条的地址保存到LR之外,也可进行状态切换,但其切换的依据是Rm最低位的值。
- 如果值为0 ,则目标地址处应为 ARM指令
- 如果值为1 ,则目标地址处应为 Thumb 指令
- 以target方式提供目标地址的 BLX 指令的功能是:
- BLX <target> 和 BLX{<cond>} Rm
四、长跳转
- 另一种实现指令跳转的方式是通过直接向 PC 寄存器中写入目标地址值,实现在4GB 地址空间中任意跳转,这种跳转又称为长跳转。
- 如果在长跳转指令之前使用“MOV LR,PC” 等指令,可以保存将来返回的地址值,也就实现了在 4GB 的地址空间中的子程序调用。
3.3.2 数据处理指令
一、数据处理指令概述
-
ARM数据处理指令的功能
- 主要完成寄存器中数据的算术和逻辑运算操作。
-
ARM数据处理指令的特点
- 操作数来源:所有的操作数要么来自寄存器,要么来自立即数,不会来自存储器。
- 操作结果:如果有结果,则结果一定是为32位宽、或64位宽(长乘法指令),并且放在一个或两个寄存器中,不会写入存储器。
- 有第二个操作数(除了乘法指令)Operand2 :切记其三种形式:立即数、寄存器、寄存器移位。
- 乘法指令的操作数:全部是寄存器。
-
ARM数据处理指令分类
- 22条可分为5类:
- 算术运算指令:ADD ADC SUB SBC RSB RSC MUL MLA UMULL UMLAL SMULL SMLAL
- 逻辑运算指令:AND ORR EOR BIC
- 数据传送指令:MOV MVN
- 比较指令:CMP CMN
- 测试指令:TST TEQ
- 上述指令只能对寄存器操作,不能针对存储器
- 22条可分为5类:
-
数据处理指令对程序状态寄存器CPSR的影响
-
指令中可以选择s后缀,以影响状态标志。
- 但是比较指令(CMP和CMN)和测试指令(TST和TEQ)不需要后缀S,它们总会直接影响CPSR中的状态标志
-
关于恢复CPSR原值问题:
-
如果指令带有S后缀(除了比较指令以外),同时又以PC为目标寄存器进行操作:
-
在异常模式下:则操作的同时从SPSR恢复CPSR
-
比如:
movs pc, #0xff /* cpsr = spsr; pc = 0xff */ adds pc, r1, #0xff00/* cpsr = spsr; pc = r1 + 0xff00 */ ands pc, r1, r2 /* cpsr = spsr; pc = r1 & r2; */
- 在user或者system模式:会产生不可预料的结果,因为在这两种模式下没有SPSR
-
-
-
-
数据处理指令的详细列表(未含6条乘法指令)
-
指令编码格式
- opcode为数据处理指令操作码
- I用于区别立即数(I为1)或寄存器移位(I为0)
- S用于设置标志位
- Rn为第一操作数寄存器,Rd为目标寄存器
- operand2为第二个操作数,若指令不需要全部的可用操作数时(如MOV指令的Rn),不用的寄存器域应设置为0(由编译器自动完成)
- 对于比较指令,b20位固定为1。
-
二、算术运算指令
-
加减运算指令
-
ADD——加法运算指令
-
指令格式:ADD{cond}{S} Rd,Rn,operand2
-
ADD指令把第1源操作数寄存器Rn和第2源操作数operand2相加后,将结果存放到目的寄存器 Rd
-
执行流程(后面的指令,流程类似便不再列举了)
- 举例:
- ADD R0,R1,R2 ;R0←(R1)+(R2)
- ADD R0,R1,#255 ;R0 ←(R1)+ 255
- ADD R0,R2,R3,LSL#1 ;R0 ←(R2) +(R3<<1)
- 举例:
-
受影响的CPSR标志位 取值 N 寄存器Rd[31]被复制到N Z 如果Rd为0,则Z=1,否则Z=0 C 运算结果有进位C=1,否则C=0 V 运算结果有溢出V=1,否则V=0
-
-
ADC——带进位加法指令
- 指令格式:ADC{cond}{S} Rd,Rn,operand2
- ADC指令将operand2的数据与Rn的值相加,再加上CPSR中的C条件标志位,结果保存到Rd寄存器。
- 标志位只修改 N、Z、C、V这4个标志位
- ADC通常用来实现字长大于32位的加法运算。
-
SUB——减法运算指令
-
指令格式:SUB{cond}{S} Rd,Rn,operand2
-
SUB指令用寄存器Rn减去operand2,结果保存到Rd中
-
受影响的CPSR标志位 取值 N 寄存器Rd[31]被复制到N Z 如果Rd为0则Z=1,否则Z=0 C 运算结果有借位则C=0,否则C=1 V 运算结果有溢出则V=1,否则V=0
-
-
SBC——带进位减法指令
- 指令格式:SBC{cond}{S} Rd,Rn,operand2
- SBC指令用寄存器Rn减去operand2,再减去CPSR中的C条件标志位的反码,结果保存到Rd中。
- 标志位的修改同 SUB。
- 该指令主要用于字长大于 32 位的数据的减法运算。
-
RSB——逆(反)向减法指令
- 指令格式:RSB{cond}{S} Rd,Rn,operand2
- RSB指令用operand2减去寄存器Rn,结果保存到Rd中。
-
RSC——带进位反向减法指令
- 指令格式:RSC{cond}{S} Rd,Rn,operand2
- RSC 指令用寄存器operand2减去Rn,再减去CPSR中的C条件标志位的反码,结果保存到Rd中。
-
-
乘法指令
-
ARM有两类乘法指令:
-
32位的乘法指令,即乘法操作的结果为32位
-
64位的乘法指令,即乘法操作的结果为64位
-
-
MUL——32位乘法指令
-
指令格式:MUL{cond}{S} Rd,Rm,Rs
-
MUL指令将Rm和Rs中的值相乘,结果的低32位保存到Rd中(Rd ≠ Rm)
-
受影响的CPSR标志位 取值 N 寄存器Rd[31]被复制到N Z 如果Rd为0则Z=1,否则Z=0
-
-
MLA——32位乘加指令
- 格式:MLA{cond}{S} Rd,Rm,Rs,Rn
- 指令将Rm和Rs中的值相乘,再将乘积加上第3个操作数,结果的低32位保存到Rd中( Rd ≠ Rm )
- 标志位的修改同 MUL
-
UMULL——64位无符号乘法指令
-
指令格式:UMULL{cond}{S} RdLo,RdHi,Rm,Rs ; RdHi, RdLo← Rm*Rs
-
UMULL指令将Rm和Rs中的值作无符号数相乘,结果的低32位保存到RdLo中,高32位保存到RdHi中
-
受影响的CPSR标志位 取值 N 寄存器**RdHi[31]**被复制到N Z 如果RdHi且Rdlo为0,则Z=1,否则Z=0
-
-
**UMLAL——64位无符号乘加指令 **
- 指令格式:UMLAL{cond}{S} RdLo,RdHi,Rm,Rs ;RdHi, RdLo← Rm*Rs+ RdHi, RdLo
- UMLAL指令将Rm和Rs中的值作无符号数相乘,64位乘积与RdHi、RdLo相加,结果的低32位保存到RdLo中,而高32位保存到RdHi中
- 标志的修改同 UMULL
-
SMLAL—64位有符号乘加指令
- 指令格式:SMLAL{cond}{S} RdLo,RdHi,Rm,Rs ; RdHi, RdLo← Rm*Rs+ RdHi, RdLo
- SMLAL指令将Rm和Rs中的值作有符号数相乘,64位乘积与RdHi、RdLo相加,结果的低32位保存到RdLo中,高32位保存到RdHi中
- 标志的修改同 SMULL。
-
乘法指令的特点
- 不支持第2操作数为立即数
- 结果寄存器不能与第一源寄存器相同。
- Rd、RdHi、RdLo不能与Rm为同一寄存器。
- RdHi和RdLo不能为同一寄存器。
- 避免将R15定义为任一操作数或结果寄存器。
-
乘法指令对标志位的影响
- 对N标志位:
- 若结果是32位指令形式,Rd的第31位是标志位N
- 对于产生长结果的指令形式,RdHi的第31位是标志位N
- 对Z标志位:如果Rd或RdHi、RdLo为0,则标志位Z置位
- 对V标志位:乘法指令不影响V标志位
- 对C标志位: ARM v5及以上的版本不影响C标志位; ARM v5以前的版本,C标志位数值不确定
- 对N标志位:
-
指令编码格式
- opcode为乘法指令操作码
- S为设置标志位
- Rm为被乘数寄存器,Rs为乘数的寄存器
- Rn/RdLo用于MLA指令相加的寄存器或64位乘法指令的目标寄存器(低32位)。
- Rd/RdHi用于目标寄存器或64位乘法指令的目标寄存器(高32位)。
- 若指令不需要全部的可用操作数时(如MUL指令的Rn),不用的寄存器域应设置为0(由编译器自动完成)。
-
三、逻辑运算指令(按位逻辑操作指令)
-
AND——逻辑“与”操作指令
-
指令格式:AND{cond}{S} Rd,Rn,operand2
-
AND指令将operand2的值与寄存器Rn的值**按位逻辑“与”**操作,结果保存到Rd中。
-
执行流程
-
受影响的CPSR标志位 取值 N 寄存器Rd[31]被复制到N Z 如果Rd为0则Z=1,否则Z=0 C 不影响C标志位 AND指令可用于提取寄存器中某些位的值,也可以用于把指定位清0
-
-
ORR——逻辑“或”操作指令
- 指令格式: ORR{cond}{S} Rd,Rn,operand2
- ORR指令将**operand2的值与寄存器Rn的值按位逻辑“或”**操作,结果保存到Rd中。
- ORR指令用于将寄存器中某些位的值设置成1
- 标志位的影响同AND。
-
EOR——逻辑“异或”操作指令
- 指令格式:EOR{cond}{S} Rd,Rn,operand2
- EOR指令将**operand2的值与寄存器Rn的值按位逻辑“异或”**操作,结果保存到Rd中。
- EOR指令可用于将寄存器中某些位的值取反
- 与0异或,该位值不变
- 与1异或,该位值被求反
- 标志位的影响同AND
-
BIC——位清除指令
- 指令格式:BIC{cond}{S} Rd,Rn,operand2
- BIC指令将寄存器Rn的值与operand2的值的反码按位逻辑**“与”**操作,结果保存到Rd中。
- BIC指令可用于将寄存器中某些位的值清除为0
- 将某一位 与1 做BIC操作,该位值被清除为0 ;
- 将某一位 与0 做BIC操作,该位值不变。
四、数据传送指令
-
MOV——数据传送指令
-
指令格式:MOV{cond}{S} Rd,operand2
-
MOV指令将operand2传送到目标寄存器Rd中。
-
受影响的CPSR标志位 取值 N 寄存器Rd[31]被复制到N Z 如果Rd为0,则Z=1,否则Z=0 C C=0 -
功能总结
- 寄存器之间传送。
- 立即数传送到寄存器中。(8位立即数位图)
- 实现单纯的移位操作。MOV Rd,Rd,LSL,#3
- 实现子程序调用、从子程序中返回。当PC寄存器作为目标寄存器时可以实现程序跳转。
- 实现异常模式的返回,并把当前处理器模式的SPSR寄存器内容复制到CPSR中
- 例:MOVS PC,LR ;PC←LR,异常模式下返回,且CPSR←SPSR
-
-
MVN——数据求反传送指令
- 指令格式:MVN{cond}{S} Rd,operand2
- MVN指令将operand2按位取反后传送到目标寄存器Rd中。
五、比较指令
-
CMP——比较指令
-
指令格式:CMP{cond} Rn,operand2
-
CMP指令将寄存器Rn的值减去operand2的值,但不存储运算结果,只根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。
-
受影响的CPSR标志位 取值 N 运算结果的第31位被复制到N Z 运算结果为0则Z=1,否则Z=0 C 运算结果有借位则C=0,否则C=1 V 运算结果有溢出则V=1,否则V=0 -
比较类指令本身带有更新 CPSR的功能,故在该指令中不能使用后缀 S
-
-
CMN——负数比较指令
- 指令格式:CMN{cond} Rn,operand2
- CMN指令将寄存器Rn的值减去operand2的负数(即加上operand2的值),但不存储运算结果,只根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。
六、测试指令
- TST——位测试指令
- 指令格式:TST{cond} Rn,operand2
- TST指令将寄存器Rn的值与operand2的值按位逻辑“与”操作,但不存储运算结果,只根据操作的结果更新CPSR中的相应条件标志位。
- 该指令一般用来检测是否设置了特定的位。
- TEQ——测试相等指令
- 指令格式:TEQ{cond} Rn,operand2
- TEQ指令将寄存器Rn的值与operand2的值**按位逻辑“异或”**操作,但不存储运算结果,只根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。
- 例子:
- TEQ R0,R1
- ;比较R0与R1是否相等
- ;(不影响V位和C位)
- 例子:
3.3.3 存储器访问指令
- ARM微处理器用加载/存储指令访问存储器,实现在寄存器和存储器之间传送数据。
- 由于ARM处理器对外设寄存器、I/O映射空间与存储器统一编址,因此,对外围设备的I/O操作也用此类指令。
- 基本的加载/存储指令仅有5条,分为3种:
- LDR和STR,单寄存器加载/存储指令
- LDM和STM,多寄存器加载/存储指令
- SWP,寄存器和存储器数据交换指令
一、 单寄存器的存取指令
- 单寄存器加载/存储指令是ARM在寄存器和存储器间传送单个字节和字的最灵活方式。
- 根据传送数据的类型不同,单个寄存器存取指令又可以分为以下两类:
- 单字和无符号字节的加载/存储指令
- 半字和有符号字节的加载/存储指令
-
单字和无符号字节的加载/存储指令
-
LDR:指令从内存中取32位字或8位无符号字节数据放入寄存器
- 内存到寄存器
-
STR:指令将寄存器中的32位字或8位无符号字节数据保存到存储器中。
- 寄存器到内存
-
注意:无符号字节加载时,用0将8位的操作数扩展到32位
-
指令格式
LDR{cond}{T} Rd,<地址>
;加载指定地址上的字数据,放入Rd中。STR{cond}{T} Rd,<地址>
;存储Rd中字数据,到指定地址的存储单元。LDR{cond}B{T} Rd,<地址>
;加载字节数据到Rd低8位数据位中,高24位为0。STR{cond}B{T} Rd,<地址>
;存储Rd中字节数据, Rd中最低字节为传送数据。- T后缀
- T为可选后缀,若指令有T,存储系统将访问看成是处理器在用户模式下。
- 用于存储器保护。
- 不能与后变址模式、自动变址模式一起使用(即不能改变基址寄存器值)。
- T在用户模式下无效。
-
操作数寻址方式
-
LDR/STR指令为基址变址寻址(或寄存器间接寻址),由两部分组成:
-
基地址部分:为一个基址寄存器,可以为任一个通用寄存器
-
偏移地址部分:这一部分非常灵活,实际就类似第二个操作数,可以有以下3种格式:
-
立即数:12位立即数是一个无符号的数值。这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值。
-
指令举例如下:
LDR R1,[R0,#0x10] ;将R0+0x10地址处的数据读出, ;保存到R1中(R0的值不变) LDR R1,[R0,# -0x10] ;将R0-0x10地址处的数据读出, ;保存到R1中(R0的值不变)
-
-
寄存器:寄存器中的数值(无符号数)可以加到基址寄存器,也可以从基址寄存器中减去这个数值。
-
指令举例如下:
LDR R1,[R0,R2] ;将R0+R2地址处的数据读出,保存到R1中 LDR R1,[R0,-R2] ;将R0-R2地址处的数据读出,保存到R1中
-
-
寄存器及移位常数:寄存器移位后的值(无符号数)可以加到基址寄存器,也可以从基址寄存器中减去这个数值。
-
指令举例如下:
LDR R1,[R0,R2,LSL #2];将R0+R2×4地址处的数据读出,;保存到R1中(R0、R2的值不变) LDR R1,[R0,-R2,LSL #2];将R0-R2×4地址处的数据读出,;保存到R1中(R0、R2的值不变)
- 注意:移位位数只能是5位的立即数,不能使用寄存器指定移位位数
-
-
-
-
-
指令编码格式
-
-
半字和有符号字节的加载/存储指令
-
这类LDR/STR指令可实现半字(有符号和无符号)、有符号字节数据的传送。
-
特点:
- 偏移量格式、寻址方式与加载/存储字和无符号字节指令基本相同
- 立即数偏移量限定在8位
- 寄存器偏移量不可经过移位得到。
-
指令格式
LDR {cond}SB Rd,<地址>
;加载指定地址上有符号字节到Rd中,高24位用符号位扩展LDR {cond}SH Rd,<地址>
;加载指定地址上的有符号半字到Rd中,高16位用符号位扩展LDR {cond}H Rd,<地址>
;加载无符号半字数据到Rd的低16位,高16位清零。STR{cond}H Rd,<地址>
;存储Rd中的低16位半字数据。
-
两点说明:
- 符号位
- 有符号字节或有符号半字的加载,用**“符号位”扩展到32位**
- 无符号半字传送是用0扩展到32位
- 地址对齐
- 对半字传送的地址必须为偶数。非半字对齐的半字加载将使Rd内容不可靠
- 非半字对齐的半字存储将使指定地址的2字节存储内容不可靠。
- 符号位
-
指令编码格式
-
- 看PPT的例子P127-130
二、多寄存器的存取指令
-
LDM和STM指令可以实现在一组寄存器和一块连续的内存单元之间存/取数据。
- LDM为加载多个寄存器(内存到寄存器)
- STM为存储多个寄存器(**寄存器到内存)
-
这两条指令,允许传送16个寄存器R0—R15的任何子集或所有寄存器。
-
指令格式
LDM{cond}<模式> Rn{!},<reglist>{^}
STM{cond} <模式> Rn{!},<reglist>{^}
- 指令格式说明
- Rn:表示基址寄存器,装有传送数据的初始地址,Rn不允许为R15(即PC)
- Rn后缀 **“!” **:表示最后的地址写回到Rn中
- reglist:表示寄存器列表,其中包含一个或多个寄存器。当寄存器不连续时,中间使用“,”隔开。
- 格式例子:{R1,R2,R6-R9}
- 列表寄存器和存储器地址的关系规则:编号低的寄存器对应于存储器中低地址单元,编号高的寄存器对应于存储器中高地址单元
- 后缀 “^” 说明
- 寄存器列表不包含PC:
- 使用后缀“^”进行数据传送时,加载/存储的是用户模式的寄存器,而不是当前模式的寄存器
- 寄存器列表包含有PC:
- 除了正常的多寄存器传送外,还要将SPSR拷贝到CPSR中。该用法可用于异常处理返回。
- 禁用情况:后缀“^”不允许在用户模式或系统模式下使用。 因为它们没有SPSR
- 寄存器列表不包含PC:
- 当Rn在寄存器列表中且使用后缀“!”
- 对于STM指令,若Rn为寄存器列表中的最低序号的寄存器,则会将Rn的初值保存;
- 其它情况下Rn的编译无法通过。
- 地址字对齐:这些指令寻址是字对齐的,即忽略地址位[1:0]。
-
指令编码格式
-
模式项
-
LDM/STM的主要用途是现场保护、数据复制和参数传送等。
-
其模式有如下8种(前面4种用于数据块的传输(为存储操作), 后面4种是堆栈操作):
类型 每次基址寄存器的操作 传送起始地址 Rn序号的变化 IA 先传送数据,后基地址加4 (Rn) 增加 IB 先基地址加4,后传送数据 (Rn)+4 增加 DA 先传送数据,后基地址减4 (Rn) 减少 DB 先基地址减4,后传送数据 (Rn)-4 减少 类型 堆栈类型 弹出(pop)指令 压入(push)指令 FA 递增满堆栈 LDMFA STMFA FD 递减满堆栈 LDMFD STMFD EA 递增空堆栈 LDMEA STMEA ED 递减空堆栈 LDMED STMED -
堆栈操作与批量传输对应
寻址方式 说明 pop =LDM push =STM FA 递增满 LDMFA LDMDA STMFA STMIB FD 递减满 LDMFD LDMIA STMFD STMDB EA 递增空 LDMEA LDMDB STMEA STMIA ED 递减空 LDMED LDMIB STMED STMDA
-
-
-
示例
- 这里能看出来序号高的寄存器一定会存/取地址高的地址
三、单寄存器交换指令(SWP)
-
SWP指令用于将一个存储单元(该单元地址放在寄存器Rn中)的内容读取到一个寄存器Rd中,同时将另一个寄存器Rm的内容写入到该存储单元中。
-
交换指令是一个原子操作,也就是说,在连续的总线操作中读/写一个存储单元,在操作期间阻止其它任何指令对该存储单元的读写
-
指令格式
SWP{cond}{B} Rd,Rm,[Rn]
- B为可选后缀,若有B,则交换无符号字节,否则交换32位字
- Rd为被加载的寄存器
- Rm的数据用于存储到Rn所指的地址中,若Rm与Rd相同,则为寄存器与存储器内容进行交换
- Rn为要进行数据交换的存储器地址,Rn不能与Rd和Rm相同。
- 功能:将一个内存单元[Rn]的内容读取到一个寄存器Rd中,同时将另一个寄存器Rm的内容写入到该内存单元中
-
指令编码格式
-
指令举例
- SWP R1,R1,[R0];将R1的内容与R0指向的存储单元的内容进行交换。
- SWPB R1,R2,[R0];将R0指向的存储单元的内容读取1字节数据到R1中(高24位清零),并将R2的内容写入到该内存单元中(最低字节有效)
3.3.5 杂项指令
- 主要由两种类型指令组成,程序状态寄存器操作指令、中断操作指令,一共有5条指令。
- 状态寄存器操作指令:
- MRS:读程序状态寄存器指令
- MSR:写程序状态寄存器指令
- 异常中断操作指令:
- SWI: 软件中断指令
- BKPT:断点指令(v5T体系)
- CLZ: 前导0计数(v5T体系)
- 状态寄存器操作指令:
一、程序状态寄存器处理指令
- ARM指令中有两条指令,用于在状态寄存器和通用寄存器之间传送数据。修改状态寄存器一般是通过“读取-修改-写回”三个步骤的操作来实现的。
-
MRS–读状态寄存器指令
-
指令格式:
MRS{cond} Rd,psr
; Rd <- psr -
把**状态寄存器psr(CPSR或SPSR)**的内容传送到目标寄存器中。
- Rd —— 目标寄存器。Rd不允许为R15。
- psr —— CPSR或SPSR。
-
指令编码格式
- 注意:在ARM处理器中,只有MRS指令可以将状态寄存器CPSR或SPSR读出到通用寄存器中。
-
-
MSR–写状态寄存器指令
-
在ARM处理器中,只有MSR指令可以直接设置状态寄存器CPSR或SPSR。
-
指令格式如下:
-
MSR{cond} psr_fields,#immed
-
MSR{cond} psr_fields,Rm
-
其中:
-
psr:CPSR或SPSR
-
immed:要传送到状态寄存器指定域的8位立即数
-
Rm:要传送到状态寄存器指定域的数据的源寄存器
-
fields:指定传送的区域。fields可以是以下的一种或多种:
-
c 控制域 (psr[7…0]);
-
x 扩展域(psr[15…8]);(暂未用)
-
s 状态域 (psr[23…16]);(暂未用)
-
f 标志位域 (psr[31…24])。
-
-
-
-
指令举例
- MSR CPSR_cxsf,R0 ;传送R0的内容到CPSR
- MSR SPSR_cxsf,R0 ;传送R0的内容到SPSR
- MSR CPSR_c,R0 ;传送R0的内容到CPSR,但仅仅修改CPSR中的控制位域
- MSR CPSR_cfxs,R0 ;传送R0的内容到CPSR,修改所有域
-
注 意:
- 控制域的修改问题:只有在特权模式下才能修改状态寄存器的控制域[7:0],以实现处理器模式转换,或设置开/关异常中断 。
- T控制位的修改问题:程序中不能通过MSR指令,直接修改CPSR中的T控制位来实现ARM状态/Thumb状态的切换,必须使用BX指令完成处理器状态的切换。
- 用户模式下能够修改的位:在用户模式只能修改“标志位域”,不能对CPSR[23:0]做修改。
- S后缀的使用问题:在MRS/MSR指令中不可以使用S后缀
-
二、异常中断产生指令
- 异常中断指令可以分为以下几种:
- SWI: 软件中断指令
- BKPT:断点指令(v5T及以上体系)
- CLZ: 前导0计数(v5T及以上体系)
-
SWI——软件中断指令
-
软件中断指令SWI产生软件异常中断,用来实现用户模式到特权模式的切换。
-
用于在用户模式下对操作系统中特权模式的程序的调用;
-
它将处理器置于管理(svc)模式,中断矢量地址为0x08。
-
指令格式如下:
SWI {<cond>} <24位立即数>
- 24位立即数,指定用户程序调用系统例程的类型,相关参数通过寄存器传递,当指令中24位立即数被忽略时(立即数为0),用户程序调用系统例程的类型由通用寄存器R0决定,同时参数通过其它寄存器传递
-
指令编码格式
-
说明:
- 主要用于用户程序调用操作系统的API。
- 参数传递通常有两种方法:
- 指令中的24bit立即数指定API号,其它参数通过寄存器传递。
- 忽略指令中的24bit立即数,r0指定API号,其它参数通过其它寄存器传递。
-
举例
- 软中断号在指令中,不传递其它参数:
- SWI 10 ;中断类型号为10(注:没有#号)
- SWI 0x123456 ;中断类型号为0x123456
- 软中断号在指令中,其它参数在寄存器中传递:
- MOV R0,#34 ;准备参数
- SWI 12 ;调用12号软中断
- 不用指令中的立即数,软中断类型号和其它参数都在寄存器中传递:
- MOV R0,#12 ;准备中断类型号
- MOV R1,#34 ;准备参数
- SWI 0 ;进入软中断。
- 软中断号在指令中,不传递其它参数:
-
-
BKPT——断点指令(了解)
- 断点中断指令BKPT用于产生软件断点,供调试程序用。
- v5T及以上体系使用。
- 指令格式如下:
BKPT { immed_16}
- immed_16:16位的立即数。该立即数被调试软件用来保存额外的断点信息。
- 断点指令用于软件调试;它使处理器停止执行正常指令而进入相应的调试程序。
-
CLZ——前导0计数指令(了解)
-
前导0计数指令CLZ 对Rm中的前导0的个数进行计数,结果放到Rd中。
-
v5T及以上体系使用。
-
指令格式:
CLZ{<cond>} Rd, Rm
-
举例如下:
MOV R2, #0X17C00 ;R2=0b0000 0000 0000 0001 0111 1100 0000 0000 CLZ R3, R2 ;R3=15
-