Linux嵌入式系统开发
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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指令集。当然,若两者结合使用,充分发挥其各自的优点,会取得更好的效果。