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

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

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后缀的指令影响状态寄存器的标志位,再通过各类条件码后缀执行相应判断

条件码助记符条件码标志含义
EQ0000Z=1相等
NE0001Z=0不相等
CS/HS0010C=1无符号数大于或等于
CC/LO0011C=0无符号数小于
MI0100N=1负数
PL0101N=0正数
VS0110V=1溢出
VC0111V=0没有溢出
HI1000C=1,Z=0无符号数大于
LS1001C=0或Z=1无符号数小于或等于
GE1010N=V带符号数大于或等于
LT1011N!=V带符号数小于
GT1100Z=0,N=V带符号数大于
LE1101Z=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表示条件码

数据传送指令

处理器内数据传送

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

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

存储器访问指令

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

数据类型读存储器指令写存储器指令语法
32位LDRSTRLDR Rx,ADDR;将地址ADDR上的值赋给Rx
STR Rx,ADDR;将Rx的值赋给地址为ADDR的存储空间
16位有符号LDRSH
16位无符号LDRHSTRH
8位有符号LDRSB
8位无符号LDRBSTRB
多个32位LDMSTMLDM、STM
双字(64位)LDRDSTRDLDRD/STRD R1,R2,ADDR;从地址ADDR上读出两个字并分别赋给两个寄存器
栈操作(32位)POPPUSHPUSH、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以返回子程序

算术运算

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

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

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

逻辑运算

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

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

移位

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

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

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

图解:

在这里插入图片描述

数据转换

展开

指令名称语法指令作用
SXTBSXTB 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

反转

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

图解:
在这里插入图片描述

       举例:

  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

位域处理

指令名称语法指令作用
BFCBFC Rx,#num_1,#num_2将寄存器Rx中num_1到num_1+num_2-1对应的位清零,即将寄存器对应位清零,num_1为清零的最低位,num_2为清零宽度
BFIBFI Rx,Ry,#num_1,#num_2寄存器Ry中的0到num_2-1位赋给R1的num_1到num_1+num_2-1对应的位
CLZCLZ Rx,Ry将源操作数中从31位开始连续为0的个数赋给目的操作数,即计算前导零的个数
RBITRBIT Rx,Ry将源操作数按位反转,即0和31位互换,1和30位互换…
SBFXSBFX 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立即数限制

比较和测试

指令名称语法指令作用
CMPCMP 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等)

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

函数调用

指令名称语法指令作用
BLBL label跳转到标号位置并将返回地址保存到链接寄存器R14(LR)中
BLXBLX 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语句之类的分支选项

比较并跳转

指令名称语法指令作用
CBZCBZ 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

表格跳转

指令名称语法指令作用
TBBTBB语法及示例进行字节跳转(偏移),偏移范围为0 ~ 512(2*2^8)字节
TBHTBH语法及示例进行字节跳转(偏移),偏移范围为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语句

饱和运算

指令名称语法指令作用
SSATSSAT Rx,#num,Ry将Ry局限到-2 ^(num-1)-1 ~ 2 ^(num-1)并将该值赋给Rx
USATUSAT 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

异常相关

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

休眠模式相关

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

存储器屏障

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

       应用场景:

在这里插入图片描述
在这里插入图片描述

其它指令

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

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

  • 12
    点赞
  • 104
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值