3.7 ARM指令集
ARM微处理器的指令集是加载/存储型的,即指令集仅能处理寄存器中的数据,而且处理结果都要放回寄存器中,而对系统存储器的访问则需要通过专门的加载/存储指令来完成。
ARM指令集可以是以下任意一种:
■ 32bit长(ARM状态);
■ 16bit长(Thumb状态)。
所有ARM指令都是32位长度,指令以字对准方式保存,这样ARM状态指令地址的最低2位总是零。实际上,一些指令通常使用最低有效位来判定代码是转向Thumb代码还是ARM代码。
所有Thumb指令都是16位长度,这些指令可在存储器中以半字对准方式保存。因此,指令的最低有效位在Thumb状态下总是零。实际上,Thumb指令集是32位ARM指令集的功能子集。
3.7.1 ARM指令的格式
下面首先讲述ARM指令的基本格式,然后具体介绍条件码的一些含义。
(1)基本格式
<opcode>{<cond>}{S} <Rd>,<Rn>{,<opcode2>}
其中,<>内的项是必须的,{}内的项是可选的。如<opcode>是指令助记符,是必须的,而{<cond>}为指令执行条件,是可选的,如果不写则使用默认条件AL。
■ opcode指令助记符:如LDR,STR等。
■ cond执行条件:如EQ,NE等。
■ S:是否影响CPSR 寄存器的值,书写时影响CPSR,否则不影响。
■ Rd:目标寄存器。
■ Rn:第一个操作数的寄存器。
■ operand2:第二个操作数。在ARM指令中,灵活的使用第2个操作数能提高代码效率。
(2)条件码
几乎所有的ARM指令都包含一个可选择的条件码,即{<cond>}。使用指令条件码,可实现高效的逻辑操作,提高代码效率。ARM条件码参见表3-3。
表3-3 ARM条件码
3.7.2 ARM指令分类
ARM指令主要包括ARM存储器访问指令、ARM数据处理指令、ARM跳转指令、ARM协处理器指令、ARM伪指令和ARM杂项指令。
(1)ARM存储器访问指令
ARM微处理器支持加载/存储指令用于在寄存器和存储器之间传送数据,加载指令用于将存储器中的数据传送到寄存器,存储指令则完成相反的操作。ARM的加载/存储指令是可以实现字、半字、无符/有符字节操作;批量加载/存储指令可实现一条指令加载/存储多个寄存器的内容;SWP指令是一条寄存器和存储器内容交换的指令,可用于信号量操作等。
ARM处理器是冯处诺依曼存储结构,程序空间、RAM空间及I/O映射空间统一编址,除对RAM操作以外,对外围I/O、程序数据的访问均要通过加载/存储指令进行。
ARM存储访问指令参见表3-4。
表3-4 ARM存储访问指令
指令示例:
1)LDR R0,[R1];将存储器地址为R1的字数据读入寄存器R0。
2)LDR R0,[R1,R2];将存储器地址为R1+R2的字数据读入寄存器R0。
3)LDR R0,[R1,#8];将存储器地址为R1+8的字数据读入寄存器R0。
4)LDR R0,[R1,R2]!;将存储器地址为R1+R2的字数据读入寄存器R0,并将新地址R1+R2写入R1。
5)LDR R0,[R1,#8]!;将存储器地址为R1+8的字数据读入寄存器R0,并将新地址R1+8写入R1。
6)LDR R0,[R1],R2;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2写入R1。
7)LDR R0,[R1,R2,LSL#2]!;将存储器地址为R1+R2×4的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。
8)LDRB R0,[R1,#8];将存储器地址为R1+8的字节数据读入寄存器R0,并将R0的高24位清零。
9)LDRH R0,[R1,R2];将存储器地址为R1+R2的半字数据读入寄存器R0,并将R0的高16位清零。
10)STR R0,[R1],#8;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。
11)STRB R0,[R1,#8];将寄存器R0中的字节数据写入以R1+8为地址的存储器中。
12)STRH R0,[R1];将寄存器R0中的半字数据写入以R1为地址的存储器中。
(2)ARM数据处理指令
数据处理指令可分为数据传送指令、算术逻辑运算指令和比较指令等。数据传送指令用于在寄存器和存储器之间进行数据的双向传输。所有ARM数据处理指令均可选择使用S后缀,以影响状态标志。比较指令不需要后缀S,它们会直接影响状态标志。算术逻辑运算指令完成常用的算术与逻辑的运算,该类指令不但将运算结果保存在目的寄存器中,同时更新CPSR中的相应条件标识位。比较指令不保存运算结果,只更新CPSR中相应的条件标识位。
ARM数据处理指令参见表3-5。
表3-5 ARM数据处理指令
指令示例:
1)MOV R1,R0;将寄存器R0的值传送到寄存器R1。
2)MOV PC,R14;将寄存器R14的值传送到PC,常用于子程序返回。
3)MOV R1,R0,LSL #3;将寄存器R0的值左移3位后传送到R1。
4)MVN R0,#0;将立即数零取反传送到寄存器R0中,完成后R0= -1。
5)CMP R1,R0;将寄存器R1的值与寄存器R0的值相减,并根据结果设置CPSR的标识位。
6)CMN R1,R0;将寄存器R1的值与寄存器R0的值相加,并根据结果设置CPSR的标识位。
7)TST R1,#0xffe;将寄存器R1的值与立即数0xffe按位与,并根据结果设置CPSR的标识位。
8)TEQ R1,R2;将寄存器R1的值与寄存器R2的值按位异或,并根据结果设置CPSR的标识位。
9)ADD R0,R1,R2;R0 = R1 + R2。
10)ADDS R0,R4,R8;加低端的字。
11)ADCS R1,R5,R9;加第二个字,带进位。
12)SUB R0,R1,#256;R0 = R1-256。
13)RSB R0,R1,R2;R0 = R2-R1。
14)AND R0,R0,#3;该指令保持R0的0、1位,其余位清零。
15)ORR R0,R0,#3;该指令设置R0的0、1位,其余位保持不变。
16)EOR R0,R0,#3;该指令反转R0的0、1位,其余位保持不变。
(3)ARM跳转指令
跳转指令用于实现程序流程的跳转,在ARM中有两种方式可以实现程序的跳转,一种是使用跳转指令直接跳转,另一种则是直接向PC寄存器赋值实现跳转。
通过向程序计数器PC写入跳转地址值,可以实现在4GB的地址空间中的任意跳转,在跳转之前结合使用MOV LR,PC等类似指令,可以保存将来的返回地址值,从而实现在4GB连续的线性地址空间的子程序调用。
ARM指令集中的跳转指令可以完成从当前指令向前或向后的32MB的地址空间的跳转。
包括以下4条指令:
■ B(跳转指令)
B指令的格式为
B{条件} 目标地址
B指令是最简单的跳转指令。一旦遇到一个B指令,ARM处理器将立即跳转到给定的目标地址,在给定的目标地址中继续执行。注意存储在跳转指令中的实际值是相对当前PC值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。它是24位有符号数,左移两位后有符号扩展为32位,表示的有效偏移为26位(前后32MB的地址空间)。例如,指令:
B Label;程序无条件跳转到标号Label处执行 CMP R1,#0;当CPSR寄存器中的Z条件码置位时,程序跳转到标号Label处执行 BEQ Label
■ BL(带返回的跳转指令)
BL指令的格式为
BL{条件} 目标地址
BL是另一个跳转指令,但跳转之前,会在寄存器R14中保存PC的当前内容,因此,可以通过将R14的内容重新加载到PC中,再返回到跳转指令后的那个指令处执行。该指令是实现子程序调用的一个基本但常用的手段。如指令:
BL Label;当程序无条件跳转到标号Label处执行时,同时将当前的PC值保存到R14中。
■ BLX(带返回和状态切换的跳转指令)
BLX指令的格式为
BLX 目标地址
BLX指令有两种格式。第1种格式记作BLX(1)。BLX(1)从ARM指令集跳转到指令中所指定的目标地址,并将处理器的工作状态由ARM状态切换到Thumb状态,该指令同时将PC的当前内容保存到寄存器R14中。因此,当子程序使用Thumb指令集,而调用者使用ARM指令集时,可以通过BLX指令实现子程序的调用和处理器工作状态的切换。同时,子程序的返回可以通过将寄存器R14值复制到PC中来完成。第2种格式记作BLX(2)。BLX(2)从ARM指令集跳转到指令中所指定的目标地址,目标地址的指令可以是ARM指令,也可以是Thumb指令。该指令同时将PC的当前内容保存到寄存器R14中。
■ BX(带状态切换的跳转指令)
BX指令的格式为
BX{条件} 目标地址
BX指令跳转到指令中所指定的目标地址,目标地址处的指令既可以是ARM指令,也可以是Thumb指令。
(4)ARM协处理器指令
ARM微处理器支持协处理器操作,协处理器的控制要通过协处理器命令实现。在程序执行的过程中,每个协处理器只执行针对自身的协处理指令,忽略ARM处理器和其他协处理器的指令。
ARM的协处理器指令主要用于ARM处理器初始化、ARM协处理器的数据处理操作,以及在ARM处理器的寄存器和协处理器的寄存器之间传送数据和在ARM协处理器的寄存器和存储器之间传送数据。
ARM协处理器指令参见表3-6。
例如,指令:
1)CDP P3,2,C12,C10,C3,4;该指令完成协处理器P3的初始化。
表3-6 ARM协处理器指令
2)LDC P3,C4,[R0];将ARM处理器的寄存器R0所指向的存储器中的字数据传送到协处理器P3的寄存器C4中。
3)STC P3,C4,[R0];将协处理器P3的寄存器C4中的字数据传送到ARM处理器的寄存器R0所指向的存储器中。
4)MCR P3,3,R0,C4,C5,6;该指令将ARM处理器寄存器R0中的数据传送到协处理器P3的寄存器C4和C5中。
5)MRC P3,3,R0,C4,C5,6;该指令将协处理器P3的寄存器中的数据传送到ARM处理器寄存器中。
(5)ARM伪指令
1)ADR,将程序相对偏移或寄存器相对偏移地址加载到寄存器中。
指令格式为
ADR{cond} register,expr
其中,register 加载的寄存器。
expr 程序相对偏移或寄存器相对偏移表达式。
2)ADRL,将程序相对偏移或寄存器相对偏移地址加载到寄存器中。
指令格式为
ADR {cond} register,expr
3)LDR,用32位常量或一个地址加载寄存器。
指令格式为
LDR {cond} register,=[expr | label-expr]
其中,register 加载的寄存器。
expr 赋值成数字常量。
label-expr 程序相对偏移或外部表达式。
4)NOP,NOP产生所需的ARM无操作代码。
NOP不能有条件使用。执行和不执行无操作指令是一样的,因而不需要有条件执行。ALU状态标志不受NOP影响。
(6)ARM杂项指令
1)SWI,引起软件中断。这意味着处理器模式变换为管理模式,CPSR保存到管理模式的SPSR中,执行转移到SWI矢量。
指令格式为
SWI {cond} immed_24
其中,immed_24为表达式,其值为0~224-1范围内的整数。
2)MRS,将CPSR或SPSR的内容传送到通用寄存器。
指令格式为
MRS {cond} Rd,psr
其中,Rd 目标寄存器。Rd不允许为R15。
psr CPSR或SPSR。
3)MSR,用立即数或通用寄存器的内容加载CPSR或SPSR的指定区域。
指令格式为
MSR {cond} <psr>_<fields>,#immed_8r MSR {cond} <psr>_<fields>,Rm
其中,<psr> CPSR或SPSR。
<fields> 指定传送的区域。
immed_8r 值为数字常量的表达式。
4)BKPT,引起处理器进入调试模式。
指令格式为
BKPT immed_16
其中,immed_16为表达式,其值为在0~65536内的整数。
3.7.3 Thumb指令介绍
16位Thumb指令集是从32位ARM指令集提取指令格式的,每条Thumb指令有针对相同处理器模型对应的32位ARM指令。
ARM开发工具完全支持Thumb指令,应用程序可以灵活的将ARM和Thumb子程序混合编程,以便在编程的基础上提高性能或代码密度。
(1)Thumb指令的特点
■ 16位的指令子集,代码密度小。
■ 在指令集名中,含有T的均可执行Thumb指令。
■ CPSR中的T标识位决定是执行Thumb指令还是ARM指令,如置位,执行Thumb指令,否则执行ARM指令。
■ Thumb状态下没有协处理器指令。
■ 所有Thumb指令均有对应的ARM指令。
■ Thumb是一个不完整的体系结构,不能使处理器只执行Thumb代码而不支持ARM指令集。
(2)Thumb模式的进入和退出
■ 进入Thumb模式
进入Thumb指令模式有两种方法:一种是执行一条交换转移指令BX,另一种是利用异常返回,也可以把微处理器从ARM模式转换为Thumb模式。
■ 退出Thumb模式
退出Thumb指令模式也有两种方法:一种是执行Thumb指令中的交换转移BX指令,可以显式的返回到ARM指令流;另一种是利用异常进入ARM指令流。
3.7.4 Thumb指令分类
Thumb指令主要包括Thumb存储器访问指令、Thumb数据处理指令、Thumb跳转指令、Thumb杂项指令和Thumb伪指令。
(1)Thumb存储器访问指令
Thumb存储器访问指令参见表3-7。
表3-7 Thumb存储器访问指令
(2)Thumb数据处理指令
Thumb数据处理指令参见表3-8。
表3-8 Thumb数据处理指令
续表
(3)Thumb跳转指令
分支指令用于向后转移形成循环、条件结构向前转移、转向子程序和处理器从Thumb状态切换到ARM状态。
程序相对转移,特别是条件转移与在ARM状态下相比,在范围上有更多的限制,转向子程序只能是无条件转移。
Thumb跳转指令有B、BL、BX4和BLX条指令。
1)B指令
格式1:B <条件码> <Label>
编码结构如图3-16所示。
格式2:B <Label>
编码结构如图3-17所示。
图3-16 B指令编码结构1
图3-17 B指令编码结构2
2)BL指令
格式:BL{X} <Label>
编码结构如图3-18所示。
3)BX、BLX指令
格式: BX Rm
或BLX Rm
其中,Rm装有目的地址的ARM寄存器,m=0~15。Rm的位[0]不用于地址部分。若Rm的位[0]清零,则位[1]也必须清零;指令清零CPSR中的标志T,目的地址的代码被解释为ARM代码。
编码结构如图3-19所示。
图3-18 BL指令编码结构
图3-19 BX、BLX指令编码结构
(4)Thumb杂项指令
Thumb杂项指令有软件中断指令SWI和断点中断指令BKPT。
(5)Thumb伪指令
Thumb伪指令有ADR,LDR和NOP。
3.7.5 ARM指令集和Thumb指令集的区别
ARM体系结构除了支持执行效率很高的32位ARM指令集外,同时支持16位的Thumb指令集。Thumb指令集允许指令编码为16位的长度。Thumb指令集在保留32位代码优势的同时,大大的节省了系统的存储空间。
所有的Thumb指令都有对应的ARM指令,而且Thumb的编程模型也对应于ARM的编程模型,在应用程序的编写过程中,只要遵循一定调用的规则,Thumb子程序和ARM子程序就可以互相调用。与ARM指令集相比较,Thumb指令集中的数据处理指令的操作数仍然是32位,指令地址也为32位,但Thumb指令集为实现16位的指令长度。大多数的Thumb数据处理指令的目的寄存器与其中一个源寄存器相同。
Thumb指令可以看作是ARM指令压缩形式的子集,是针对代码密度的问题而提出的,它具有16位的代码密度,但是它不如ARM指令的效率高。Thumb指令集不是一个完整的体系结构,没有协处理器指令、信号量指令及访问CPSR或SPSR的指令,没有乘加指令及64位乘法指令等。除了跳转指令B有条件执行功能外,其他指令均为无条件执行。因此,Thumb指令只需要支持通用功能,必要时可以借助于完善的ARM指令集。
由于Thumb指令的长度为16位,即只用ARM指令一半的位数来实现同样的功能,所以,要实现特定的程序功能,所需的Thumb指令的条数较ARM指令多。一般情况下,Thumb指令与ARM指令的时间效率和空间效率关系为Thumb代码所需的存储空间为ARM代码的60%~70%,Thumb代码使用的指令数约比ARM代码多30%~40%。
Thumb指令集与ARM指令的区别一般有如下4点。
(1)单寄存器加载和存储指令
在Thumb状态下,单寄存器加载和存储指令只能访问寄存器R0~R7。
(2)批量寄存器加载和存储指令
LDM和STM指令可以将任何范围为R0~R7的寄存器子集加载或存储。PUSH和POP指令使用堆栈指令R13作为基址实现满递减堆栈。除R0~R7外,PUSH指令还可以存储链接寄存器R14,并且POP指令可以加载程序指令PC。
(3)跳转指令
程序相对转移,特别是条件跳转与ARM代码下的跳转相比,在范围上有更多的限制,转向子程序是无条件的转移。
(4)数据处理指令
数据处理指令是对通用寄存器进行操作,在大多数情况下,操作的结果须放入其中一个操作数寄存器中,而不是第3个寄存器中。数据处理操作比ARM状态的更少,访问寄存器R8~R15受到一定限制。除MOV和ADD访问寄存器R8~R15外,其他数据处理指令总是更新CPSR中的ALU状态标志。访问寄存器R8~R15的Thumb数据处理指令不能更新CPSR中的ALU状态标志。
显然,ARM指令集和Thumb指令集各有其优点,若对系统的性能有较高要求,应使用32位的存储系统和ARM指令集;若对系统的成本及功耗有较高要求,则应使用16位的存储系统和Thumb指令集。当然,若两者结合使用,充分发挥其各自的优点,会取得更好的效果。