汇编语言笔记-ARM架构指令集

目录

  • 指令的执行
  • 指令后缀
  • 指令集
    • 数据传送指令
      • 处理器内数据传送
    • 存储器访问指令
      • 不同数据大小的存储器访问
      • 存储器访问方式(地址表达式)(部分省略)
        • 立即数偏移(前序)
        • 寄存器偏移
        • 多加载和多存储
        • 压栈和出栈
    • 算术运算
    • 逻辑运算
    • 移位
    • 数据转换
      • 展开
      • 反转
    • 位域处理
    • 比较和测试
    • 程序流控制
      • 跳转
      • 函数调用
      • 条件跳转
      • 比较并跳转
      • 条件执行(IT指令)
      • 表格跳转
    • 饱和运算
    • 异常相关
    • 休眠模式相关
    • 存储器屏障
    • 其它指令

       伪指令和指令的区别:只存在于汇编语言中,而不存在于机器语言中。帮助编译器将汇编语言(或者其它高级语言)转换成机器语言(伪指令被编译时被等效的指令替换)。

ARM架构的操作状态有Thumb状态和ARM状态之分(当然还有调试状态):

  Thumb状态 ARM状态
指令集 Thumb指令集 ARM指令集
指令长度 16位(半字指令) 32位
指令执行条件 大多数指令无条件执行 大多数指令有条件执行
优点 低功耗,存储空间要求低 代码需要的指令数少,性能高

Cortex-M系列不支持ARM状态,因此本文内容基于Thumb状态进行撰写。

Thumb指令集的操作数和指令地址仍为32位

指令的执行

       程序要执行的指令都保存在存储器中(指令转化为机器码,也称操作码)。当计算机需要执行一条指令时,首先产生这条指令的地址,并根据地址号打开相应的存储单元并取出指令代码,最后CPU根据指令代码的要求以及指令中的操作数去执行相应的操作。

指令后缀

后缀 描述
S 更新APSR(应用程序状态寄存器,如进位、溢出、零和负标志),例如:ADDS R0,R1;该ADD操作会更新APSR
EQ, NE, CS, CC, MI,PL,VS,VC,HI,LS,GE, LT, GT, LE 条件执行后缀,若满足相应条件则执行后面的语句,例如:BEQ label;若之前的操作得到相等的状态(状态寄存器Z置位),则跳转至 label,各个条件码的介绍如条件码介绍表所示
.N,.W 指定使用的是16位指令( narrow)或32位指令(wide)
.32,.F32 指定32位单精度运算,对于多数工具链,32后缀是可选的
.64,.F64 指定64位单精度运算,对于多数工具链,64后缀是可选的

可以通过S后缀的指令影响状态寄存器的标志位,再通过各类条件码后缀执行相应判断

条件码助记符 条件码 标志 含义
EQ 0000 Z=1 相等
NE 0001 Z=0 不相等
CS/HS 0010 C=1 无符号数大于或等于
CC/LO 0011 C=0 无符号数小于
MI 0100 N=1 负数
PL 0101 N=0 正数
VS 0110 V=1 溢出
VC 0111 V=0 没有溢出
HI 1000 C=1,Z=0 无符号数大于
LS 1001 C=0或Z=1 无符号数小于或等于
GE 1010 N=V 带符号数大于或等于
LT 1011 N!=V 带符号数小于
GT 1100 Z=0,N=V 带符号数大于
LE 1101 Z=1或N!=V 带符号数小于或等于

       条件码应用举例:
       1、比较两个值大小,C语言代码如下:

if(a > b) a++; else b++;

       对应的ARM指令代码如下:(设R0为a,R1为b)

CMP R0, R1         ;R0与R1比较 
ADDHI R0,R0,#1     ;若R0 > R1,则R0 = R0 + 1 
ADDLS R1,R1,#1     ;若R0 <= R1,则R1 = R1 + 1

       2、若两个条件均成立,则将这两个数值相加,C语言代码如下:

if((a != 10)&&(b != 20)) a = a + b;

       对应的ARM指令代码为:

CMP R0,#10     ;比较R0是否为10 
CMPNE R1,#20   ;若R0不为10,则比较R1是否为20 
ADDNE R0,R0,R1 ;若R0不为10且R1不为20,则执行 R0 = R0+R1

       3、若两个条件有一个成立,则将这两个数值相加,C语言代码如下:

if((a!=10)||(b!=20)) a=a+b; 

       对应的ARM指令代码为:

CMP R0,#10      
CMPEQ R1,#20   
ADDNE R0,R0,R1

指令集

Rx、Ry、Rz为寄存器,#num32为小于0xFFFF FFFF(32位)的立即数(即未溢出),ADDR为[地址表达式],Fun_lab表示函数标号,num_lab表示变量或常量标号,cond表示条件码

数据传送指令

处理器内数据传送

指令名称 语法 指令作用 注意
MOV MOV Rx,Ry/#num32 将源操作数的值赋给目的操作数
MRS MRS Rx,Rs 同MOV 源操作数应为特殊寄存器
MSR MSR Rs,Rx 同MOV 目的操作数应为特殊寄存器
MOVW MOVW Rx,#num16 将源操作数赋给目的操作数的低16位 高位清零
MOVT MOVT Rx,#num16 将源操作数赋给目的操作数的高16位 低位不变

MOV指令传递的立即数应在0~0xFFFF范围内或可以通过8bit连续有效位通过移位或复制全部奇数或偶数字节能得到

存储器访问指令

不同数据大小的存储器访问

数据类型 读存储器指令 写存储器指令 语法
32位 LDR STR LDR Rx,ADDR;将地址ADDR上的值赋给Rx
STR Rx,ADDR;将Rx的值赋给地址为ADDR的存储空间
16位有符号 LDRSH
16位无符号 LDRH STRH
8位有符号 LDRSB
8位无符号 LDRB STRB
多个32位 LDM STM LDM、STM
双字(64位) LDRD STRD LDRD/STRD R1,R2,ADDR;从地址ADDR上读出两个字并分别赋给两个寄存器
栈操作(32位) POP PUSH PUSH、POP
  LDR R0, =X            ;将变量X的地址赋给R0
  MOV R1,#0xFFFFBFFF    ;R1=0xFFFFBFFF
  STRH  R1,[R0]         ;将R1的低16位值赋给X,X=0xBFFF
  LDR   R2,[R0]         ;R2=0xBFFF
  LDRSH R3,[R0]         ;R3=0xFFFFBFFF
  LDRH  R4,[R0]         ;R4=0xBFFF

  STR   R1,[R0]         ;将R1的值赋给X(相当于C语言通过指针为变量赋值),X=0xFFFFBFFF
  LDR   R2,[R0]         ;R2=0xFFFFBFFF
  LDRSH R3,[R0]         ;R3=0xFFFFBFFF
  LDRH  R4,[R0]         ;R4=0xBFFF

有符号读和无符号读的区别在于,同样的数据0x83,通过LDRB读取是0x0000 0083,通过LDRSB指令读取是0xFFFF FF83,即无符号读将对应8位数据进行符号位扩展

LDR等指令的操作数为立即数时,范围为±4095

LDR不一定是指令,当使用格式为LDR 寄存器,=立即数时为伪操作,作用是将该立即数赋给寄存器,与MOV指令的区别在于,MOV指令赋的立即数有限制

存储器访问方式(地址表达式)(部分省略)

立即数偏移(前序)

       数据传输使用的存储器地址为:寄存器中的数值+立即数常量(偏移地址)

LDRB R0,[R1,#0x3];从地址R1+0x3中读取一个字节并将其存入R0

加入感叹号(!)可更新存放地址的寄存器的值(写回):
LDRB R0,[R1,#0x3]!;从地址R1+0x3中读取一个字节并将其存入R0后令R1=R1+0x3

可以使用寄存器R15(PC)作为寄存器,PC寄存器的值为当前指令地址

寄存器偏移

       类似立即数偏移,但这里的寄存器可以通过移位指令进行移位:

LDR R3,[R0, R2, LSL #2];将存储器[R0+(R2<<2)]读入R3
LDR R3,[R0, R2];将存储器[R0+R2]读入R3

       注意:这里进行的是前序偏移,也就是以地址偏移后的值为地址进行取值,下面介绍一下后序寻址:

       后序寻址是取地址上的值,后进行地址偏移:

LDR R0, [R1], #offset;读取存储器[R1],然后R1被赋值为R1+偏移

后序寻址不能使用R14(SP)或R15(PC)。

多加载和多存储

       介绍:连续取出一个地址上连续的多个数据

LDM和STM可添加相应后缀以控制地址增长方向及地址变化方式,可添加后缀如下表所示。

可添加后缀 作用 等效后缀
IB 地址增加后完成操作 FA
IA 完成操作后地址增加 EA
DB 地址减少后完成操作 FD
DA 完成操作后地址减少 ED

注意,在Cortex-M4上似乎只能使用IA和DB后缀,另外两个可能需要ARM状态。

指令 作用 注意事项
LDMIA(/LDMEA) 以目的操作数的值为地址,读取多个值(数量和源操作数的数量一致) “IA/EA”后缀表示操作后地址增加
LDMDB 同“LMDIA” “DB/FD”后缀代表操作后地址减小
STMIA 以目的操作数的值为地址,写入多个值(数量和源操作数的数量一致) “IA”后缀表示操作后地址增加
STMDB 同“STMIA” “DB”后缀代表操作后地址减小
  MOV   R1,#0xFFFFAFFF    
  STR   R1,[R0]        
  MOV   R1,#0xFFFFBFFF    
  STR   R1,[R0,#4]
  MOV   R1,#0xFFFFCFFF    
  STR   R1,[R0,#8]
  MOV   R1,#0xFFFFDFFF    
  STR   R1,[R0,#12]

  LDMIA R0,{R5-R8}      ;R5~R8分别是0xFFFFAFFF、B、C、D
  ADD R0,#16
  LDMDB R0,{R1-R4}      ;R1~R4分别是0xFFFFAFFF、B、C、D

       注意:地址增加/减少最终并不会影响目的操作数的值,即不会“写回”。

源操作数应为多个寄存器,格式如下:
1.以“{”为开始,“}”为结束。
2.可以使用“-”表示范围:R2-R5表示R2、R3、R4、R5四个寄存器。
3.通过“,”将各个寄存器隔开。
注意:{R1-R4}、{R1,R2,R3,R4}、{R4,R3,R2,R1}三者等效,顺序都是R1 ~ R4

加入感叹号(!)可更新存放地址的寄存器的值(写回):
LDMIA R0!,[R1,R3-R5];从地址R0中读取4个字并将其分别赋给R1、R3、R4、R5四个寄存器后令R0=R0+4

压栈和出栈

       压栈指令PUSH和出栈指令POP的操作数可以为多个寄存器,格式如:多寄存器格式

PUSH {R4-R6, LR}  ;在子程序开始处将R4-R6和LR(链接寄存器)中的值入栈
POP {R4-R6, PC}   ;从栈中恢复R4-R6和返回地址,返回地址存入PC以返回子程序

算术运算

指令名称 语法 指令作用 注意
ADD ADD Rx,Ry/#num32
ADD Rx,Ry,Rz/#num32
Rx=Rx+Ry
Rx=Ry+Rz加法运算
源操作数可以为立即数,立即数限制与MOV同:限制
ADDW ADD Rx,#num12
ADD Rx,Ry,#num12
同ADD 源操作数至少有一个是立即数,且不超过12位。
ADC 同ADD Rx=Rx+Ry+CF
Rx=Ry+Rz+CF,带进位的加法运算
在ADD的基础上加上进位标志位的值(0/1)
SUB 同ADD 减法运算 可添加W后缀(SUBW)
SBC 同ADD 带借位的减法运算 在SUB的基础上减去借位标志位的值(0/1)
RSB 同ADD Rx=Ry-Rx
Rx=Rz-Ry减法反转,结果为-SUB
MUL 同ADD 32位乘法
UDIV 同ADD 无符号除法
SDIV 同ADD 有符号除法

注意:许多数据处理指令会有多种形式,形式差别包括操作数数量(2 ~ 3)以及操作数种类(寄存器、立即数、存储器),以ADD为例:
操作数数目为2:ADD Rx,Ry;Rx=Rx+Ry
操作数数目为3:ADD Rx,Ry,Rz;Rx=Ry+Rz

如果具有两个源操作数,这不可都为立即数,且立即数不可为第一源操作数。

逻辑运算

指令名称 语法 指令作用
AND AND Rx,Ry/#num
AND Rx,Ry,Rz/#num
按位进行与运算
ORR 同AND 按位进行或运算
BIC 同AND Rx=Rx&(~Ry)
Rx=Ry&(~Rz)进行非与操作
ORN 同AND 进行或非操作
EOR 同AND 进行异或操作
MVN AND Rx,Ry/#num 进行取反操作(位非)

逻辑运算的立即数应是可以通过8bit连续有效位通过移位或复制全部奇数或偶数字节能得到的立即数。

移位

指令名称 语法 指令作用 注意
ASR ASR Rx,Ry/#num
ASR Rx,Ry,Rz/#num
Rx=Rx<
Rx=Ry<
算术右移
右移指定位数,并往左侧补原符号位
LSL 同ASR 逻辑左移 左移指定位数,并往右侧补0
LSR 同ASR 逻辑右移 右移指定位数,并往左侧补0
ROR 同ASR 循环右移 右移x位=左移32-x位
RRX RRX Rx,Ry 将进位标志并在最右侧后进行循环移动一位

移位指令使用的立即数范围为1 ~ 32

对于上述移位指令,更加常用的用法是,在进行数据操作时进行移位:
指令名 Rx,Ry,移位指令名 #num/Rz,如
MOV R0,Ry,LSL #2
注意:RRX作为移位指令名时,不需要立即数或寄存器作为移位位数

图解:

汇编语言笔记-ARM架构指令集_第1张图片

数据转换

展开

指令名称 语法 指令作用
SXTB SXTB Rx,Ry 将源操作数[7,0]有符号地展开
SXTH 同SXTB 将源操作数[15,0]有符号地展开
UXTB 同SXTB 将源操作数[7,0]无符号地展开
UXTH 同SXTB 将源操作数[15,0]无符号地展开

举例:

  MOV R0,#0xBB    ;R0=0x0000 00BB
  SXTB R1,R0      ;R1=0xFFFF FFBB
  SXTH R1,R0      ;R1=0x0000 00BB
  UXTB R1,R0      ;R1=0x0000 00BB
  UXTH R1,R0      ;R1=0x0000 00BB

可在源操作数后加入{,ROR #n}进行先移位后展开,n的取值为0、8、16、24,且仅能ROR

反转

指令名称 语法 指令作用
REV REV Rx,Ry 反转字中的字节,如图解所示
REV16 同REV 反转每个半字中的字节
REVSH 同REV 反转低半字中的字节并将结果有符号展开

图解:
汇编语言笔记-ARM架构指令集_第2张图片

       举例:

  MOV R1,#0x01
  MOV R0,R1,LSL #24
  ADD R1,#1
  ADD R0,R1,LSL #16
  ADD R1,#1
  ADD R0,R1,LSL #8
  ADD R1,#1
  ADD R0,R1        ;R0=0x0102 0304
	
  REV R2,R0        ;R2=0x0403 0201
  REV16 R3,R0      ;R3=0x0201 0403
  REVSH R4,R0      ;R4=0x0000 0403

位域处理

指令名称 语法 指令作用
BFC BFC Rx,#num_1,#num_2 将寄存器Rx中num_1到num_1+num_2-1对应的位清零,即将寄存器对应位清零,num_1为清零的最低位,num_2为清零宽度
BFI BFI Rx,Ry,#num_1,#num_2 寄存器Ry中的0到num_2-1位赋给R1的num_1到num_1+num_2-1对应的位
CLZ CLZ Rx,Ry 将源操作数中从31位开始连续为0的个数赋给目的操作数,即计算前导零的个数
RBIT RBIT Rx,Ry 将源操作数按位反转,即0和31位互换,1和30位互换…
SBFX SBFX Rx,Ry,#num_1,#num_2 将寄存器Ry中的num_1到num_1+num_2-1对应的位有符号的展开并赋给Rx:举例
UBFX 同SBFX 同SBFX,但将对应的位无符号展开

LDR R0, =0x5678ABCD
UBFX R1,R0,#4,#8;将R0的4~11位无符号展开后赋给R1,即R1=0x0000 00BC
SBFX R1,R0,#4,#8;将R0的4~11位有符号展开后赋给R1,即R1=0xFFFF FFBC

注意,这里的LDR是LDR伪指令,与LDR指令不同,=表示将后面的值赋给寄存器R0
这里LDR伪指令和MOV指令的区别是,MOV指令并不能传递所有的32位立即数,具体限制:MOV立即数限制。

比较和测试

指令名称 语法 指令作用
CMP CMP Rx,Ry/#num 计算Rx-Ry/#num,并更新APSR寄存器
CMN 同CMP 计算Rx+Ry/#num,并更新APSR寄存器
TST 同CMP 计算Rx&Ry/#num(按位与),并更新APSR寄存器的N和Z位
TEQ 同CMP 计算Rx⊕Ry/#num(按位异或),并更新APSR寄存器的N和Z位

这4个指令使用的立即数同样有限制,同MOV:具体限制:MOV立即数限制。

上述计算的结果不会保存,如CMP指令,不会把Rx-Ry的差值赋给Rx

程序流控制

跳转

       引起跳转操作的指令:
       - 跳转指令
       - 更新R15(PC)的数据处理指令(MOV、ADD等)
       - 写入PC的读存储器指令(LDR、LDM、POP等)

指令名称 语法 指令作用
B B label 跳转到标号对应的地址,属于相对跳转(会计算标号和当前PC的差),跳转范围为±2KB(可添加.W后缀使用32位版本的指令)
BX BX Rx 跳转到存放于寄存器Rx中的地址值,并基于Rx第0位设置处理器执行状态(Cortex-M只支持Thumb状态,因此第0位必须为1)

函数调用

指令名称 语法 指令作用
BL BL label 跳转到标号位置并将返回地址保存到链接寄存器R14(LR)中
BLX BLX Rx 跳转到存放于寄存器Rx中的地址值并将返回地址保存到LR中,以及更新EPSR中的T位为Rx的最低位

程序计数器R15(PC)为跳转目标地址(即将标号/地址赋给PC)
返回地址即BL/BLX指令后的指令的地址
由于Cortex-M只支持Thumb状态,因此使用BLX指令时,Rx的第0位必须为1

函数调用和标号跳转的区别在于,函数调用需要将返回地址保存,这也是BL和BLX与B和BX的区别

条件跳转

       通过指令改变标志寄存器后根据条件码运行程序。

       指令格式:B(cond) label/B(cond).W label

B(cond)表示指令B和条件码cond相连,中间无空格

       各个条件码如条件码助记符所示。

       举例:

CMP R0,#2   ;比较R0和1的值,并根据结果修改应用程序状态寄存器APSR
BEQ LOOP    ;注意,BEQ是B和EQ结合,但APSR的Z位置位,即符合条件码EQ,则跳转到LOOP对应的位置

可用于if语句之类的分支选项

比较并跳转

指令名称 语法 指令作用
CBZ CBZ Rx label 当Rx为0时跳转到标号对应的位置
CBNZ 同CBZ 当Rx不为0时跳转到标号对应的位置

标号的相对偏移量应该在0x00 ~ 0x7E内

可用于循环中的条件判断

指令不影响APSR的值

条件执行(IT指令)

       指定下面语句执行的条件

       语法:

IT(T/E)(T/E)(T/E) cond;(T/E)数量为0~3,决定下面指令的数量
ins_1(cond)           ;若数量为0,则下面不需要写,ins_1表示指令,这里的cond和上一行的cond相同
ins_2(cond/~cond)     ;若数量为1,则需要写到这一句(当然下面的不用写),当第一个(T/E)为T时,这里的cond和第一行的cond相同,为E时相反
ins_3(cond/~cond)     ;
ins_4(cond/~cond)     ;

       举例:

ITETT NE
ADDNE R0, R0, R1
ADDEQ R0, R0, R3
ADDNE R2, R4, #1
MOVNE R5, R3

表格跳转

指令名称 语法 指令作用
TBB TBB语法及示例 进行字节跳转(偏移),偏移范围为0 ~ 512(2*2^8)字节
TBH TBH语法及示例 进行字节跳转(偏移),偏移范围为0 ~ 128k(2*2^16)字节

  LDR     R0, =1      ;偏移量
  TBB    [PC, R0]     ;TBB语法,其中PC可以替换为其它寄存器,一般用PC实现跳转,R0为偏移量,该语句执行后,(由于处理器特性),PC=PC+4,即指向下面的语句(这里是标号Table_start)
Table_start   ;跳转表起始,指向该标号后,PC=PC+R0(偏移量),即跳转到下面的DCB语句进行赋值
  DCB   ((Dest0-Table_start)/2);数据为8位因此使用DCB,作用:为PC赋值,进而跳转到对应的标号。
  DCB   ((Dest1-Table_start)/2);由于R0=1,PC会执行到此语句,之后PC=Table_start+2*((Dest1-Table_start)/2)=Dest1,进而跳转到对应的编号
  DCB   ((Dest2-Table_start)/2)
  DCB   ((Dest3-Table_start)/2)
Dest0
  LDR     R1, =0
  B       Table_end
Dest1                          ;即跳转到这里执行
  LDR     R1, =1
  B       Table_end            ;执行后跳出
Dest2
  LDR     R1, =2
  B       Table_end
Dest3
  LDR     R1, =3
Table_end

  LDR     R0, =3            ;偏移量
  TBH    [PC, R0, LSL #1]   ;同TBB,“LSL #1”不可更换
Table_start
  DCI   ((Dest0-Table_start)/2);数据为16位因此使用DCI
  DCI   ((Dest1-Table_start)/2)
  DCI   ((Dest2-Table_start)/2)
  DCI   ((Dest3-Table_start)/2)
Dest0
 LDR     R1, =0;
  B       Table_end
Dest1
  LDR     R1, =1
  B       Table_end
Dest2 
  LDR     R1, =2
  B       Table_end
Dest3
  LDR     R1, =3
Table_end

上述的TBB和TBH实际上实现的是表格跳转,使用PC时可以实现分支选择,即c语言中的switch语句

饱和运算

指令名称 语法 指令作用
SSAT SSAT Rx,#num,Ry 将Ry局限到-2 ^(num-1)-1 ~ 2 ^(num-1)并将该值赋给Rx
USAT USAT Rx,#num,Ry 将Ry局限到0 ~ 2 ^(num-1)并将该值赋给Rx

注意,num为位数限制,因此1 <= num <= 32

Ry后可加位移运算,但仅局限于ASR与LSL

       示例:

  LDR R0,=0x00020000
  SSAT R1,#16,R0      ;R1=0x00007FFF
  SSAT R1,#17,R0      ;R1=0x00020000
  USAT R1,#16,R0      ;R1=0x00008000
  USAT R1,#17,R0      ;R1=0x00020000
  LDR R0,=0xFFF20000
  SSAT R1,#16,R0      ;R1=0xFFFF8000
  USAT R1,#16,R0      ;R1=0x00000000

异常相关

指令名称 语法 指令作用
SVC SVC num 触发SVC中断,可用于执行状态的切换。num为优先级,范围为0x00 ~ 0xFF,并且该立即数前面可不加"#"
CPS CPSIE/CPSID I/F "IE"后缀用于使能中断,"ID"后缀用于禁止中断

休眠模式相关

指令名称 语法 指令作用
WFI(Wait for interrupt) WFI 进入休眠(中断可唤醒)
WFE(Wait for event) WFE 进入休眠,等待事件(中断、复位、外部输入等)发生后唤醒
SEV SEV 发送事件

存储器屏障

指令名称 语法 指令作用
DMB DMB 数据存储器屏障。确保在执行新的存储器访问前所有的存储器访问都已经完成
DSB DSB 数据同步屏障。确保在下一条指令执行前所有的存储器访问都已经完成
ISB ISB 指令同步屏障。清空流水线,确保在执行新的指令前,之前所有的指令都已完成

       应用场景:

汇编语言笔记-ARM架构指令集_第3张图片
汇编语言笔记-ARM架构指令集_第4张图片

其它指令

指令名称 语法 指令作用
NOP NOP 空语句,无任何功能,可用于指令对齐或延时
BKPT BKPT num num立即数,且此时其前面可不加"#",大小为0x00 ~ 0xFF。该指令的作用是实现软件断点,特殊的num值会使处理器执行某些动作

本文基于keil5,芯片STM32F429IGx撰写,参考书籍:《ARM Cortex-M3与Cortex-M4权威指南》

你可能感兴趣的:(汇编笔记,arm,架构,汇编)