以下内容源于网络资源的学习与整理,如有侵权请告知删除。

参考内容

(1)文档《ARM汇编指令集汇总

(2)文档《ARM汇编手册》

(3)文档《ARM Software Development Toolkit User Guide

内容总结

1、本文详细说明了ARM官方汇编指令的用法,包括以下几类:

指令类型指令备注
数据处理指令数据传送指令
算术运算指令
比较指令
逻辑运算指令
测试整理
乘法指令
跳转指令
程序状态寄存器访问指令
加载与存储指令
异常产生指令
协处理器指令
伪指令

2、关于ARM官方汇编中的伪操作,见博文ARM官方汇编与ARM GNU汇编中的伪操作

一、数据处理指令

1、数据传送指令

【MOV指令】

(1)指令格式:MOV{条件}{S} 目的寄存器,源操作数

(2)指令作用:把一个寄存器的值赋给另一个寄存器,或者将一个常量赋给寄存器,将后边的量赋给前边的量。

(3)特别说明:MOV指令中,条件缺省时表示指令无条件执行;S选项决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。

(4)代码示例:

MOV R1,R0   ;将寄存器R0的值传送到寄存器R1
MOV PC,R14   ;将寄存器R14的值传送到PC,常用于子程序返回
MOV R1,R0,LSL#3 ;将寄存器R0的值左移3位后传送到R1(即乘8)
MOVS PC,R14      ;将寄存器R14的值传送到PC中,返回到调用代码并恢复标志位

【MVN指令】

(1)指令格式:MVN{条件}{S} 目的寄存器,源操作数

(2)指令作用:将源操作数的位取反后,加载到目的寄存器。

(3)特别说明:与MOV指令不同,该指令把源操作数按位取反后再传给目的寄存器。其中S选项决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。

(4)代码示例:

MVN R0,#0   ;将立即数0取反传送到寄存器R0中,完成后R0=-1(有符号位取反)

2、算术运算指令

【加法指令:ADD】

(1)指令格式:ADD{条件}{S} 目的寄存器,操作数1,操作数2

(2)指令作用:把两个操作数相加,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器、被移位的寄存器,或者一个立即数。

(3)代码示例:

ADD  R0,R1,R2           ; R0 = R1 + R2
ADD  R0,R1,#256         ; R0 = R1 + 256
ADD  R0,R2,R3,LSL#1     ; R0 = R2 + (R3 << 1)

【带进位的加法指令:ADC】

(1)指令格式:ADC{条件}{S} 目的寄存器,操作数1,操作数2

(2)指令作用:把两个操作数相加,再加上CPSR中的C条件标志位的值,并将结果存放到目的寄存器中。

(3)特别说明:它使用一个进位标志位,这样就可以做比32位大的数的加法,注意要设置S后缀来更改进位标志。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。

(4)代码示例:以下指令序列完成两个128位数(此处应为两个四字数相加)的加法,第一个数由高到低存放在寄存器R7~R4,第二个数由高到低存放在寄存器R11~R8,运算结果由高到低存放在寄存器R3~R0。

ADDS  R0,R4,R8     ;加低端的字,R0=R4+R8
ADCS  R1,R5,R9     ;加第二个字,带进位,R1=R5+R9
ADCS  R2,R6,R10    ;加第三个字,带进位,R2=R6+R10
ADC  R3,R7,R11     ;加第四个字,带进位,R3=R7+R11

【减法指令:SUB】

(1)指令格式:SUB{条件}{S} 目的寄存器,操作数1,操作数2

(2)指令作用:把操作数1减去操作数2,并将结果存放到目的寄存器中。

(3)特别说明:操作数1应是一个寄存器,操作数2可以是一个寄存器、被移位的寄存器,或者一个立即数。该指令可用于有符号数或无符号数的减法运算。

(4)代码示例:

SUB  R0,R1,R2         ;R0 = R1 - R2
SUB  R0,R1,#256       ;R0 = R1 - 256
SUB  R0,R2,R3,LSL#1   ;R0 = R2 - (R3 << 1)

【带借位减法指令:SBC】

(1)指令格式:SBC{条件}{S} 目的寄存器,操作数1,操作数2

(2)指令作用:把操作数1减去操作数2,再减去CPSR中的C条件标志位的反码,并将结果存放到目的寄存器中。

(3)特别说明:操作数1应是一个寄存器,操作数2可以是一个寄存器、被移位的寄存器,或者一个立即数。该指令使用进位标志来表示借位,这样就可以做大于32位的减法,注意要设置S后缀来更改进位标志。该指令可用于有符号数或无符号数的减法运算。

(4)代码示例:

SUBS  R0,R1,R2  ;R0 = R1-R2-!C,并根据结果设置CPSR的进位标志位

3、比较指令

比较指令,用来比较两个寄存器中的数,纵使不加S后缀也会影响CPSR中的标志位。

【直接比较指令:CMP】

(1)指令格式:CMP{条件} 操作数1,操作数2

(2)指令作用:将一个寄存器的内容和另一个寄存器的内容或立即数进行比较,同时更新CPSR中条件标志位的值。

(3)特别说明:该指令进行一次减法运算,但不存储结果,只更改条件标志位。标志位表示的是操作数1与操作数2的大小关系。例如,当操作数1大于操作操作数2,则此后带有GT后缀的指令将得以执行。

(4)代码示例:

CMP R1,R0     ;将寄存器R1的值与寄存器R0的值相减,并根据结果设置CPSR的标志位
CMP R1,#100  ;将寄存器R1的值与立即数100相减,并根据结果设置CPSR的标志位

【负数比较指令:CMN】

(1)指令格式:CMN{条件} 操作数1,操作数2

(2)指令作用:用于把一个寄存器的内容和另一个寄存器的内容或立即数取反后进行比较,同时更新CPSR中条件标志位的值。

(3)特别说明:该指令实际完成操作数1和操作数2相加,并根据结果更改条件标志位。

(4)代码示例:

CMN R1,R0     ;将寄存器R1的值与寄存器R0的值相加,并根据结果设置CPSR的标志位
CMN R1,#100  ;将寄存器R1的值与立即数100相加,并根据结果设置CPSR的标志位

4、逻辑运算指令

【逻辑与指令:AND】

(1)指令格式:AND{条件}{S} 目的寄存器,操作数1,操作数2

(2)指令作用:将两个操作数进行逻辑与运算,并把结果放置到目的寄存器中。

(3)特别说明:操作数1应是一个寄存器,操作数2可以是一个寄存器、被移位的寄存器,或者一个立即数。该指令常用于屏蔽操作数1的某些位。

(4)代码示例:

AND  R0,R0,#3   ;该指令保持R0的0、1位,其余位清零。

【逻辑或指令:ORR】

(1)指令格式:ORR{条件}{S} 目的寄存器,操作数1,操作数2

(2)指令作用:将两个操作数进行逻辑或运算,并把结果放置到目的寄存器中。

(3)特别说明:操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于设置操作数1的某些位。

(4)代码示例:

ORR  R0,R0,#3     ;该指令设置R0的0、1位,其余位保持不变。

【逻辑异或指令:EOR】

(1)指令格式:EOR{条件}{S} 目的寄存器,操作数1,操作数2

(2)指令作用:将两个操作数进行逻辑异或运算,并把结果放置到目的寄存器中。

(3)特别说明:操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于反转操作数1的某些位。

(4)代码示例:

EOR  R0,R0,#3  ;该指令反转R0的0、1位,其余位保持不变。

【位清零指令:BIC】

(1)指令格式:BIC{条件}{S} 目的寄存器,操作数1,操作数2

(2)指令作用:用于清除操作数1的某些位,并把结果放置到目的寄存器中。

(3)特别说明:操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。操作数2是32位的掩码,如果在掩码中设置了某一位,则清除这一位。未设置的掩码位保持不变。

(4)代码示例:

BIC  R0,R0,#%1011  ;该指令清除R0中的位0、1、3,其余的位保持不变。

5、测试指令

【位测试指令:TST】

(1)指令格式:TST{条件} 操作数1,操作数2

(2)指令作用:把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的与运算,并根据运算结果更新CPSR中条件标志位的值。

(3)特别说明:操作数1是要测试的数据,而操作数2是一个位掩码,该指令一般用来检测是否设置了特定的位。

(4)代码示例:

TST R1,#%1    ;用于测试在寄存器R1中是否设置了最低位(%表示二进制数)
TST R1,#0xffe  ;将寄存器R1的值与立即数0xffe按位与,并根据结果设置CPSR的标志位

【位测试指令:TEQ】

(1)指令格式:TEQ{条件} 操作数1,操作数2

(2)指令作用:用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的异或运算,并根据运算结果更新CPSR中条件标志位的值。

(3)特别说明:该指令通常用于比较操作数1和操作数2是否相等。

(4)代码示例:

TEQ R1,R2  ;将寄存器R1的值与寄存器R2的值按位异或,并根据结果设置CPSR的标志位

6、乘法指令

二、跳转指令

在ARM程序中有两种方法可以实现程序流程的跳转:

(1)使用跳转指令实现程序流程的跳转

跳转指令可以完成从当前指令向前或向后的32MB的地址空间的跳转,包括以下4条指令:

  • B:直接跳转(就没打算返回)。
  • BL(branch and link):跳转前把返回地址放入lr中,以便返回,以便用于函数调用。
  • BX:带状态切换的跳转指令,一般用于异常处理的跳转。
  • BLX:带返回和状态切换的跳转指令。

(2)直接向程序计数器PC写入跳转地址值

通过向程序计数器PC写入跳转地址值,可以实现在4GB的地址空间中任意跳转,在跳转之前结合使用MOV LR,PC等类似指令,可以保存将来的返回地址值,从而实现在4GB连续的线性地址空间的子程序调用。

【指令B】

(1)指令格式:B {条件} 目标地址

(2)指令作用:B跳转指令是最简单的跳转指令,ARM处理器跳转到给定的目标地址。

(3)特别说明:注意存储在跳转指令中的实际值是相对当前PC值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(参考寻址方式中的相对寻址)。它是 24 位有符号数,左移两位后有符号扩展为 32 位,表示的有效偏移为 26 位(前后32MB的地址空间)。

(4)代码示例:

B  Label    ;程序无条件跳转到标号Label处执行
CMP R1,#0  ;当CPSR寄存器中的Z条件码置位时,程序跳转到标号Label处执行
BEQ Label  

【指令BL】

(1)指令格式:BL{条件} 目标地址

(2)指令作用:BL指令在跳转之前,会在寄存器R14中保存PC的当前内容,因此可以通过将R14 的内容重新加载到PC中,返回到跳转指令之后的那个指令处执行。

(3)特别说明:该指令是实现子程序调用的一个基本但常用的手段。

(4)代码示例:

BL Label  ;程序无条件跳转到标号Label处,同时将当前的PC值保存到R14中

【指令BX】

(1)指令格式:BX{条件} 目标地址

(2)指令作用:跳转到指定的目标地址,目标地址处的指令既可以是ARM指令,也可以是Thumb指令。

【指令BLX】

(1)指令格式:BLX 目标地址

(2)指令作用:跳转到指定的目标地址,并将处理器的工作状态从ARM状态切换到Thumb状态,该指令同时将PC的当前内容保存到寄存器R14中。

(3)特别说明:当子程序使用Thumb指令集,而调用者使用ARM指令集时,可以通过BLX指令实现子程序的调用和处理器工作状态的切换。同时,子程序的返回可以通过将寄存器R14值复制到PC中来完成。

三、程序状态寄存器访问指令

程序状态寄存器(这里指CPSR、SPSR)比较特殊,需要用专门的指令MRS、MSR来访问。

【MRS指令】

(1)指令格式:MRS{条件} 通用寄存器,程序状态寄存器

(2)指令作用:MRS指令用于将程序状态寄存器的内容传送到通用寄存器中。

(3)特别说明:当需要改变程序状态寄存器的内容时,可以使用MRS指令将程序状态寄存器的内容读入通用寄存器,然后修改,最后再写回程序状态寄存器。另外异常处理或进程切换时需要保存程序状态寄存器的值,可以使用该指令读出程序状态寄存器的值,然后保存。

(4)代码示例:

MRS R0,CPSR   ;传送CPSR的内容到R0
MRS R0,SPSR   ;传送SPSR的内容到R0

【MSR指令】

(1)指令格式:MSR{条件} 程序状态寄存器_<域>,操作数

(2)指令作用:将操作数的内容传送到程序状态寄存器的特定域中。操作数可以是通用寄存器或立即数,程序状态寄存器_<域>是指程序状态寄存器中需要操作的位。该指令通常用于恢复或改变程序状态寄存器的内容,在使用时,一般要在MSR指令中指明将要操作的域。

(3)特别说明:32位的程序状态寄存器可分为4个域。

bit范围域名表示方法
[31:24]条件标志位域用f表示
[23:16]状态位域用s表示
[15:8]扩展位域用x表示
[7:0]控制位域用c表示

(4)代码示例:

MSR CPSR,R0     ;传送R0的内容到CPSR
MSR SPSR,R0     ;传送R0的内容到SPSR
MSR CPSR_c,R0   ;传送R0的内容到SPSR,但仅仅修改CPSR中的控制位域


四、加载/存储指令

ARM是RISC结构,数据在内存到寄存器之间的移动,需要通过加载/存储指令。

【LDR指令】

(1)指令格式:LDR{条件} 目的寄存器,<内存地址>

(2)指令作用:从内存中将一个32位的字数据传送到目的寄存器中。该指令通常用于从内存中读取32位的字数据到通用寄存器,然后对数据进行处理。

(3)特别说明:当程序计数器PC作为目的寄存器时,从内存中读取的字数据将被当作目的地址,从而实现程序流程的跳转。

(4)代码示例:

LDR  R0,[R1]       ;将内存地址为R1的字数据读入寄存器R0。
LDR  R0,[R1,R2]    ;将内存地址为R1+R2的字数据读入寄存器R0。
LDR  R0,[R1,#8]   ;将内存地址为R1+8的字数据读入寄存器R0。
LDR  R0,[R1,R2]!   ;将内存地址为R1+R2的字数据读入寄存器R0,并将新地址R1+R2写入R1。
LDR  R0,[R1,#8]!  ;将内存地址为R1+8的字数据读入寄存器R0,并将新地址R1+8写入R1。
LDR  R0,[R1],R2         ;将内存地址为R1的字数据读入寄存器R0,并将新地址R1+R2写入R1。
LDR  R0,[R1,R2,LSL#2]! ;将内存地址为R1+R2×4的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。
LDR  R0,[R1],R2,LSL#2  ;将内存地址为R1的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。

(5)LDR通常用作加载指令,但也可以作伪指令“ LDR Rn,=expr ”。

COUNT EQU 0x40003100 ;COUNT是我们定义的一个变量,地址为0x40003100。
LDR R1,=COUNT        ;将COUNT这个变量的地址,也就是0x40003100放到R1中。
MOV R0,#0            ;将立即数0放到R0中。

STR R0,[R1]          ;将R0中的值放到以R1中的值为地址的存储单元去。
                     ;实际就是将0放到地址为0x40003100的存储单元中去。
//这三条指令是为了完成对变量COUNT赋值。
//用三条指令来完成对一个变量的赋值,跟ARM的采用RISC有关。
//注意,变量、变量的地址、变量的值
    //  COUNT  0x40003100  0

【LDRB指令】

(1)指令格式:LDR{条件}B 目的寄存器,<内存地址>

(2)指令作用:从内存中将一个8位的字节数据传送到目的寄存器中,同时将寄存器的高24位清零。

(3)特别说明:该指令通常用于从内存中读取8位的字节数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,从内存中读取到的字节数据被当作目的地址,从而可以实现程序流程的跳转。

(4)代码示例:

LDRB  R0,[R1]      ;将内存地址为R1的字节数据读入寄存器R0,并将R0的高24位清零。
LDRB  R0,[R1,#8]  ;将内存地址为R1+8的字节数据读入寄存器R0,并将R0的高24位清零。

【LDRH指令】

(1)指令格式:LDR{条件}H 目的寄存器,<内存地址>

(2)指令作用:从内存中将一个16位的半字数据传送到目的寄存器中,同时将寄存器的高16位清零。

(3)特别说明:该指令通常用于从内存中读取16位的半字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,从内存中读取的半字数据被当作目的地址,从而可以实现程序流程的跳转。

(4)代码示例:

LDRH  R0,[R1]      ;将内存地址为R1的半字数据读入寄存器R0,并将R0的高16位清零。
LDRH  R0,[R1,#8]  ;将内存地址为R1+8的半字数据读入寄存器R0,并将R0的高16位清零。
LDRH  R0,[R1,R2]   ;将内存地址为R1+R2的半字数据读入寄存器R0,并将R0的高16位清零。

【STR指令】

(1)指令格式:STR{条件} 源寄存器,<内存地址>

(2)指令作用:从源寄存器中将一个32位的字数据传送到内存中。该指令在程序设计中比较常用,且寻址方式灵活多样,使用方式可参考指令LDR。

(3)代码示例:

STR R0,[R1],#8 ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。
STR R0,[R1,#8] ;将R0中的字数据写入以R1+8为地址的存储器中。
STR R1,[R0]     ;将R1寄存器的值,传送到以r0为地址的存储器中。

【STRB指令】

(1)指令格式:STR {条件}B源寄存器,<内存地址>

(2)指令作用:从源寄存器中将一个8位的字节数据传送到内存中。该字节数据为源寄存器中的低8位。

(3)代码示例:

STRB  R0,[R1]      ;将寄存器R0中的字节数据写入以R1为地址的存储器中。
STRB  R0,[R1,#8]  ;将寄存器R0中的字节数据写入以R1+8为地址的存储器中。

【STRH指令】

(1)指令格式:STR{条件}H 源寄存器,<内存地址>

(2)指令作用:该指令从源寄存器中将一个16位的半字数据传送到存储器中。该半字数据为源寄存器中的低16位。

(3)代码示例:

STRH  R0,[R1]      ;将寄存器R0中的半字数据写入以R1为地址的存储器中。
STRH  R0,[R1,#8]  ;将寄存器R0中的半字数据写入以R1+8为地址的存储器中。

【LDM / STM指令】

LDR/STR指令每周期只能访问4字节内存,对内存的读取与写入速度太慢。于是我们可以使用批量加载/存储指令(即STM与LDM指令),一次性地在连续的内存单元和多个寄存器之间传送数据。

(1)指令格式:LDM(或STM){条件}{类型} 基址寄存器{!},寄存器列表{∧}

(2)指令作用:LDM(或STM)指令用于(由基址寄存器所指示的一片连续)内存与(寄存器列表所指示的多个)寄存器之间的数据传送。该指令的常见用途是将多个寄存器的内容入栈或出栈。

(3)特别说明

  • {类型}有以下几种情况

类型描述
IA(increase after)每次传送后地址加1
IB(increase before)每次传送前地址加1
DA(decrease after)每次传送后地址减1
DB( decrease before)每次传送前地址减1
FD(full decrease)满递减堆栈
ED(empty decrease)空递减堆栈
FA满递增堆栈
EA空递增堆栈
  • {!}为可选后缀,若选用该后缀,则当数据传送完毕之后,将最后的地址写入基址寄存器,否则基址寄存器的内容不改变。

  • 基址寄存器不允许是R15,寄存器列表可以为R0~R15的任意组合。

  • {∧}为可选后缀,当指令为LDM且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据传送之外,还将SPSR复制到CPSR。同时,该后缀还表示传入或传出的是用户模式下的寄存器,而不是当前模式下的寄存器。

ldmfd sp!, {r0 - r6, pc}
ldmfd sp!, {r0 - r6, pc}^ ;目标寄存器中有pc时,同时将spsr写入到cpsr,一般用于从异常模式返回。

(4)代码示例:

stmia sp, {r0--r12}  ;将r0~r12中的内容存入以sp为起始地址的内容空间里
/*stmia sp, {r0--r12} 的解释
将r0中的内容存入sp指向的内存处(假设为0x30001000);
然后地址+4(即指向0x30001004),将r1中的内容存入该地址;
然后地址再+4(指向0x30001008),将r2中的内容存入该地址;
······直到r12内容放入(0x3001030),指令完成。
一个访存周期同时完成13个寄存器的读写。
*/


STMFD  R13!,{R0,R4-R12,LR}  ;将寄存器列表中的寄存器(R0,R4到R12,LR)存入堆栈。
LDMFD  R13!,{R0,R4-R12,PC}  ;将堆栈内容恢复到寄存器(R0,R4到R12,LR)。

五、异常产生指令

【SWI指令】

(1)指令格式:SWI{条件} 24位的立即数

(2)指令作用:产生软件中断,以便用户程序能调用操作系统的系统例程。

(3)特别说明:在SWI的异常处理程序中,操作系统提供相应的系统服务,指令中24位的立即数指定用户程序调用系统例程的类型,相关参数通过通用寄存器传递。当指令中24位的立即数被忽略时,用户程序调用系统例程的类型由通用寄存器R0的内容决定,同时参数通过其他通用寄存器传递。

(4)代码示例:

SWI 0x02  ;该指令调用操作系统编号位02的系统例程。

【BKPT指令】

(1)指令格式:BKPT 16位的立即数

(2)指令作用:BKPT指令产生软件断点中断,可用于程序的调试。

六、协处理器的汇编指令

协处理器(cp,coprocessor)是SoC内部的处理单元,用来协助主CPU实现一些特定的功能,比如MMU、cache、TLB等内容。ARM在设计上支持16个协处理器,但是SoC一般只实现CP15。 ARM协处理器的指令主要是mcr、mrc这两个指令。

(1) 指令格式

mcr {<cond>} p15,<opcode_1>,<Rd>,<Crn>,<Crm>,{<opcode_2>}

(2)指令作用:主要用于操控协处理器中的寄存器(c0~c15)与 CPU中的寄存器之间的数据传输,其中mrc用于读取CP15中的寄存器,mcr用于写入CP15中的寄存器。

(3)特别说明

  • p15,指协处理器cp15
  • opcode_1:对于cp15永远为0
  • Rd:ARM的普通寄存器
  • Crn:cp15的寄存器,可选的合法值是c0~c15
  • Crm:cp15的寄存器,一般设为c0
  • opcode_2:一般省略或为0

(4)代码示例

mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #1
mcr p15, 0, r0, c1, c0, 0

七、伪指令

伪指令包括ADR、ADRL、LDR、NOP等指令。

【ADR】

(1)指令格式:ADR{cond} register, expr

(2)指令作用:将基于PC或基于寄存器的地址值读取到寄存器,用于小范围的地址读取。

(3)特别说明

(4)代码示例

【ADRL】

(1)指令格式:ADRL{cond} register, expr

(2)指令作用:将给予PC或基于寄存器的地址值读取到寄存器,中等范围的地址读取。

(3)特别说明

(4)代码示例

【LDR】

(1)指令格式:LDR {cond} register,=[expr|label]

(2)指令作用:将一个32位的立即数或者一个地址值读取到寄存器,大范围的地址读取。

(3)特别说明

(4)代码示例

【NOP】

(1)指令格式:NOP

(2)指令作用:在汇编时,被替换成空操作。

(3)特别说明

(4)代码示例

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐