目录
1. ARM 存储器访问指令
1.1 LDR 和STR 加载/存储字和无符号字节指令
1.2 LDM和STM 批量加载/存储指令
1.3 SWP 寄存器和存储器交换指令
2. ARM 数据处理指令
2.1数据传送指令
2.1.1 MOV 数据传送指令
2.1.2 MVN 数据非传送指令
2.2算术逻辑运算指令
2.2.1 ADD 加法运算指令
2.2.2 SUB 减法运算指令
2.2.3 RSB 逆向减法指令
2.2.4 ADC 带进位加法指令
2.2.5 SBC 带进位减法指令
2.2.6 RSC 带进位逆向减法指令
2.2.7 AND 逻辑与操作指令
2.2.8 ORR 逻辑或操作指令
2.2.9 EOR 逻辑异或操作指令
2.2.10 BIC 位清除指令
2.3 比较指令
2.3.1 CMP 比较指令
2.3.2 CMN 负数比较指令
2.3.3 TST 位测试指令
2.3.4 TEQ 相等测试指令
2.4 乘法指令
2.4.1 MUL 32 位乘法指令
2.4.2 MLA 32 位乘加指令
2.4.3 UMULL 64 位无符号乘法指令
2.4.4 UMLAL 64 位无符号乘加指令
2.4.5 SMULL 64 位有符号乘法指令
2.4.6 SMLAL 64 位有符号乘加指令
3. ARM 跳转指令
3.1 B 跳转指令
3.2 BL 带链接的跳转指令
3.3 BX 带状态切换的跳转指令
3.4 BLX
4. ARM 协处理器指令
4.1 CDP 协处理器数据操作指令
4.2 LDC 协处理器数据读取指令
4.3 STC 协处理器数据写入指令
4.4 MCR ARM寄存器到协处理器寄存器的数据传送指令
4.5 MRC 协处理器寄存器到ARM寄存器到的数据传送指令
5. ARM 杂项指令
5.1 SWI 软中断指令
5.2 MRS 读状态寄存器指令
5.3 MSR 写状态寄存器指令
6. ARM 伪指令
6.1 ADR 小范围的地址读取伪指令
6.2 ADRL 中等范围的地址读取伪指令
6.3 LDR 大范围的地址读取伪指令
6.4 NOP 空操作伪指令
1. ARM 存储器访问指令
ARM 处理器是冯诺依曼存储结构,程序空间、RAM 空间及IO 映射空间统一编址,除对对RAM 操作以外,对外围IO、程序数据的访问均要通过加载/存储指令进行。
ARM 的加载/存储指令是可以实现字、半字、无符/有符字节操作;批量加载/存储指令可实现一条指令加载/存储多个寄存器的内容,大大提高效率。A
1.1 LDR 和STR 加载/存储字和无符号字节指令
使用单一数据传送指令(STR 和LDR)来装载和存储单一字节或字的数据从/到内存。LDR指令用于从内存读取数据放入寄存器中;STR 指令用于将寄存器中的数据保存到内存。指令格式如下:
LDR{cond}{T} Rd,<地址>;加载指定地址上的数据(字),放入Rd中
STR{cond}{T} Rd,<地址>;存储数据(字)到指定地址的存储单元,要存储的数据在Rd中
LDR{cond}B{T} Rd,<地址>;加载字节数据,放入Rd中,即Rd最低字节有效,高24位清零
STR{cond}B{T} Rd,<地址>;存储字节数据,要存储的数据在Rd,最低字节有效
其中,T 为可选后缀,若指令有T,那么即使处理器是在特权模式下,存储系统也将访问看成是处理器是在用户模式下。T在用户模式下无效,不能与前索引偏移一起使用T。
LDR/STR 指令寻址是非常灵活的,由两部分组成,一部分为一个基址寄存器,可以为任一个通用寄存器,另一部分为一个地址偏移量。地址偏移量有以下3种格式:
(1) 立即数。立即数可以是一个无符号数值,这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:
LDR R1,[R0,#0x12] ;将R0+0x12 地址处的数据读出,保存到R1中(R0 的值不变)
LDR R1,[R0,#-0x12];将R0-0x12 地址处的数据读出,保存到R1中(R0 的值不变)
LDR R1,[R0] ;将R0 地址处的数据读出,保存到R1 中(零偏移)
(2)寄存器。寄存器中的数值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例值。指令举例如下:
LDR R1,[R0,R2] ;将R0+R2 地址的数据计读出,保存到R1中(R0 的值不变)
LDR R1,[R0,-R2] ;将R0-R2 地址处的数据计读出,保存到R1中(R0 的值不变)
(3)寄存器及移位常数。寄存器移位后的值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:
LDR R1,[R0,R2,LSL #2] ;将R0+R2*4地址处的数据读出,保存到R1中(R0,R2的值不变)
LDR R1,[R0,-R2,LSL #2];将R0-R2*4地址处的数据计读出,保存到R1中(R0,R2的值不变)
从寻址方式的地址计算方法分,加载/存储指令有以下4 种形式:
(1)零偏移。Rn 的值作为传送数据的地址,即地址偏移量为0。指令举例如下:
LDR Rd,[Rn]
(2)前索引偏移。在数据传送之前,将偏移量加到Rn 中,其结果作为传送数据的存储地址。若使用后缀“!”,则结果写回到Rn中,且Rn 值不允许为R15。指令举例如下:
LDR Rd,[Rn,#0x04]!
LDR Rd,[Rn,#-0x04]
(3)程序相对偏移。程序相对偏移是索引形式的另一个版本。汇编器由PC 寄存器计算偏移量,并将PC寄存器作为Rn 生成前索引指令。不能使用后缀“!”。指令举例如下:
LDR Rd,label ;label 为程序标号,label 必须是在当前指令的±4KB范围内
(4) 后索引偏移。Rn 的值用做传送数据的存储地址。在数据传送后,将偏移量与Rn相加,结果写回到Rn中。Rn 不允许是R15。指令举例如下:
LDR Rd,[Rn],#0x04
地址对准--大多数情况下,必须保证用于32 位传送的地址是32 位对准的。
加载/存储字和无符号字节指令举例如下:
LDR R2,[R5] ;加载R5 指定地址上的数据(字),放入R2 中
STR R1,[R0,#0x04] ;将R1 的数据存储到R0+0x04存储单元,R0 值不变
LDRB R3,[R2],#1 ;读取R2 地址上的一字节数据,并保存到R3中,R2=R3+1
STRB R6,[R7] ;读R6 的数据保存到R7 指定的地址中,只存储一字节数据
加载/存储半字和带符号字节。这类LDR/STR 指令可能加载带符字节\加载带符号半字、加载/存储无符号半字。偏移量格式、寻址方式与加载/存储字和无符号字节指令相同。指令格式如下:
LDR{cond}SB Rd,<地址> ;加载指定地址上的数据(带符号字节),放入Rd中
LDR{cond}SH Rd,<地址> ;加载指定地址上的数据(带符号字节),放入Rd中
LDR{cond}H Rd,<地址> ;加载半字数据,放入Rd中,即Rd最低16位有效,高16位清零
STR{cond}H Rd,<地址> ;存储半字数据,要存储的数据在Rd,最低16位有效
说明:带符号位半字/字节加载是指带符号位加载扩展到32 位;无符号位半字加载是指零扩展到32位。
地址对准--对半字传送的地址必须为偶数。非半字对准的半字加载将使Rd 内容不可靠,非半字对准的半字存储将使指定地址的2字节存储内容不可靠。
加载/存储半字和带符号字节指令举例如下:
LDRSB R1[R0,R3] ;将R0+R3地址上的字节数据读出到R1,高24 位用符号位扩展
LDRSH R1,[R9] ;将R9 地址上的半字数据读出到R1,高16位用符号位扩展
LDRH R6,[R2],#2 ;将R2 地址上的半字数据读出到R6,高16位用零扩展,R2=R2+1
SHRH R1,[R0,#2]!;将R1 的数据保存到R2+2 地址中,只存储低2字节数据,R0=R0+2
LDR/STR 指令用于对内存变量的访问,内存缓冲区数据的访问、查表、外设的控制操作等等,若使用LDR 指令加载数据到PC 寄存器,则实现程序跳转功能,这样也就实现了程序散转。
变量的访问
NumCount EQU 0x40003000 ;定义变量NumCount
…
LDR R0,=NumCount ;使用LDR 伪指令装载NumCount的地址到R0
LDR R1,[R0] ;取出变量值
ADD R1,R1,#1 ;NumCount=NumCount+1
STR R1,[R0] ;保存变量值
…
GPIO 设置
GPIO-BASE EQU 0Xe0028000 ;定义GPIO 寄存器的基地址
…
LDR R0,=GPIO-BASE
LDR R1,=0x00FFFF00 ;装载32 位立即数,即设置值
STR R1,[R0,#0x0C] ;IODIR=0x00FFFF00, IODIR 的地址为0xE002800C
MOV R1,#0x00F00000
STR R1,[R0,#0x04] ;IOSET=0x00F00000,IOSET 的地址为0xE0028004
…
程序散转
…
MOV R2,R2,LSL #2 ;功能号乘上4,以便查表
LDR PC,[PC,R2] ;查表取得对应功能子程序地址,并跳转
NOP
FUN-TAB DCD FUN-SUB0
DCD FUN-SUB1
DCD FUN-SUB2
…
1.2 LDM和STM 批量加载/存储指令
用于在一组寄存器和一块连续的内存单元之间传输数据。LDM为加载多个寄存器,STM 为存储多个寄存器。允许一条指令传送16 个寄存器的任何子集或所有寄存器。指令格式如下:
LDM{cond}<模式> Rn{!},reglist{^}
STM{cond}<模式> Rn{!},reglist{^}
LDM /STM 的主要用途是现场保护、数据复制、参数传送等。其模式有8种,如下所列:(前面4 种用于数据块的传输,后面4 种是堆栈操作)。
(1) IA:每次传送后地址加4
(2) IB:每次传送前地址加4
(3) DA:每次传送后地址减4
(4) DB:每次传送前地址减4
(5) FD:满递减堆栈
(6) ED:空递减堆栈
(7) FA:满递增堆栈
(8) EA:空递增堆栈
其中,寄存器Rn 为基址寄存器,装有传送数据的初始地址,Rn 不允许为R15;后缀“!”表示最后的地址写回到Rn中;寄存器列表reglist 可包含多于一个寄存器或寄存器范围,使用“,”分开,如{R1,R2,R6-R9},寄存器排列由小到大排列;“^”后缀不允许在用户模式呈系统模式下使用,若在LDM 指令用寄存器列表中包含有PC 时使用,那么除了正常的多寄存器传送外,将SPSR 拷贝到CPSR 中,这可用于异常处理返回;使用“^”后缀进行数据传送且寄存器列表不包含PC时,加载/存储的是用户模式的寄存器,而不是当前模式的寄存器。
地址对准――这些指令忽略地址的位[1:0]。
批量加载/存储指令举例如下:
LDMIA R0!,{R3-R9} ;加载R0 指向的地址上的多字数据,保存到R3~R9中,R0 值更新
STMIA R1!,{R3-R9} ;将R3~R9 的数据存储到R1 指向的地址上,R1值更新
STMFD SP!,{R0-R7,LR} ;现场保存,将R0~R7、LR入栈
LDMFD SP!,{R0-R7,PC}^;恢复现场,异常处理返回
在进行数据复制时,先设置好源数据指针,然后使用块拷贝寻址指令LDMIA/STMIA、LDMIB/STMIB、LDMDA/STMDA、LDMDB/STMDB 进行读取和存储。而进行堆栈操作时,则要先设置堆栈指针,一般使用SP 然后使用堆栈寻址指令STMFD/LDMFD、STMED。LDMED、STMFA/LDMFA、STMEA/LDMEA实现堆栈操作。
使用LDM/STM 进行数据复制例程如下:
…
LDR R0,=SrcData ;设置源数据地址
LDR R1,=DstData ;设置目标地址
LDMIA R0,{R2-R9} ;加载8 字数据到寄存器R2~R9
STMIA R1,{R2-R9} ;存储寄存器R2~R9 到目标地址
使用LDM/STM 进行现场寄存器保护,常在子程序中或异常处理使用:
SENDBYTE
STMFD SP!,{R0-R7,LR} ;寄存器入堆
…
BL DELAY ;调用DELAY 子程序
…
LDMFD SP!,{R0-R7,PC} ;恢复寄存器,并返回
1.3 SWP 寄存器和存储器交换指令
SWP指令用于将一个内存单元(该单元地址放在寄存器Rn中)的内容读取到一个寄存器Rd中,同时将另一个寄存器Rm 的内容写入到该内存单元中。使用SWP 可实现信号量操作。
指令格式如下:
SWP{cond}{B} Rd,Rm,[Rn]
其中,B 为可选后缀,若有B,则交换字节,否则交换32 位字:Rd 为数据从存储器加载到的寄存器;Rm的数据用于存储到存储器中,若Rm 与Rn 相同,则为寄存器与存储器内容进行交换;Rn 为要进行数据交换的存储器地址,Rn 不能与Rd 和Rm 相同。
SWP 指令举例如下:
SWP R1,R1,[R0] ; 将R1 的内容与R0 指向的存储单元的内容进行交换
SWP R1,R2,,[R0] ; 将R0 指向的存储单元内容读取一字节数据到R1中(高24 位清零)
; 并将R2 的内容写入到该内存单元中(最低字节有效)
使用SWP 指令可以方便地进行信号量的操作:
12C_SEM EQU 0x40003000
…
12C_SEM_WAIT
MOV R0,#0
LDR R0,=12C_SEM
SWP R1,R1,[R0] ;取出信号量,并设置其为0
CMP R1,#0 ;判断是否有信号
BEQ 12C_SEM_WAIT ;若没有信号,则等待
2. ARM 数据处理指令
数据处理指令大致可分为3 类:
(1)数据传送指令(如MOV、MVN)
(2)算术逻辑运算指令(如ADD,SUM,AND)
(3)比较指令(如CMP、TST)。
数据处理指令只能对寄存器的内容操作。
所有ARM 数据处理指令均可选择使用S 后缀,以影响状态标志。比较指令CMP、CMN、TST和TEQ不需要后缀S,它们会直接影响状态标志。
2.1数据传送指令
2.1.1 MOV 数据传送指令
将立即数或寄存器(operant2)传送到目标寄存器Rd,可用于移位运算等操作。指令格式如下:
MOV{cond}{S} Rd,operand2
MOV 指令举例如下:
MOV R1#0x10 ;R1=0x10
MOV R0,R1 ;R0=R1
MOVS R3,R1,LSL #2 ;R3=R1<<2,并影响标志位
MOV PC,LR ;PC=LR ,子程序返回
2.1.2 MVN 数据非传送指令
将立即数或寄存器(operand2)按位取反后传送到目标寄存器(Rd)。指令格式如下:
MVN{cond}{S} Rd,operand2
MVN 指令举例如下:
MVN R1,#0xFF ;R1=0xFFFFFF00
MVN R1,R2 ;将R2 取反,结果存到R1
2.2算术逻辑运算指令
2.2.1 ADD 加法运算指令
将operand2 数据与Rn 的值相加,结果保存到Rd 寄存器。格式如下:
ADD{cond}{S} Rd,Rn,operand2
ADD 指令举例如下:
ADDS R1,R1,#1 ;R1=R1+1
ADD R1,R1,R2 ;R1=R1+R2
ADDS R3,R1,R2,LSL #2 ;R3=R1+R2<<2
2.2.2 SUB 减法运算指令
用寄存器Rn 减去operand2,结果保存到Rd 中,格式如下:
SUB{cond}{S} Rd,Rn,operand2
SUB 指令举例如下:
SUBS R0,R0,#1 ;R0=R0-1
SUBS R2,R1,R2 ;R2=R1-R2
SUB R6,R7,#0x10 ;R6=R7-0x10
2.2.3 RSB 逆向减法指令
用寄存器operand2 减法Rn,结果保存到Rd 中,格式如下:
RSB{cond}{S} Rd,Rn,operand2
SUB 指令举例如下:
RSB R3,R1,#0xFF00 ;R3=0xFF00-R1
RSBS R1,R2,R2,LSL #2 ;R1=R2<<2-R2=R2×3
RSB R0,R1,#0 ;R0=-R1
2.2.4 ADC 带进位加法指令
将operand2 的数据与Rn 的值相加,再加上CPSR中的C 条件标志位。结果保存到Rd 寄存器。指令格式如下:
ADC{cond}{S} Rd,Rn,operand2
ADC 指令举例如下:
ADDS R0,R0,R2
ADC R1,R1,R3 ;使用ADC 实现64 位加法,(R1、R0)=(R1、R0)+(R3、R2)
2.2.5 SBC 带进位减法指令
用寄存器Rn 减去operand2,再减去CPSR 中的C条件标志位的非(即若C 标志清零,则结果减去1),结果保存到Rd 中。指令格式如下:
SCB{cond}{S}Rd,Rn,operand2
SBC 指令举例如下:
SUBS R0,R0,R2
SBC R1,R1,R3 ;使用SBC 实现64 位减法,(R1,R0)-(R3,R2)
2.2.6 RSC 带进位逆向减法指令
用寄存器operand2 减去Rn,再减去CPSR 中的C条件标志位,结果保存到Rd 中。指令格式如下:
RSC{cond}{S} Rd,Rn,operand2
RSC 指令举例如下:
RSBS R2,R0,#0
RSC R3,R1,#0 ;使用RSC 指令实现求64 位数值的负数
2.2.7 AND 逻辑与操作指令
将operand2 值与寄存器Rn 的值按位作逻辑与操作,结果保存到Rd中。指令格式如下:
AND{cond}{S} Rd,Rn,operand2
AND 指令举例如下:
ANDS R0,R0,#x01 ;R0=R0&0x01,取出最低位数据
AND R2,R1,R3 ;R2=R1&R3
2.2.8 ORR 逻辑或操作指令
将operand2 的值与寄存器Rn的值按位作逻辑或操作,结果保存到Rd 中。指令格式如下:
ORR{cond}{S} Rd,Rn,operand2
ORR 指令举例如下:
ORR R0,R0,#x0F ;将R0 的低4 位置1
MOV R1,R2,LSR #4
ORR R3,R1,R3,LSL #8 ;使用ORR 指令将近R2 的高8位数据移入到R3 低8 位中
2.2.9 EOR 逻辑异或操作指令
将operand2 的值与寄存器Rn 的值按位作逻辑异或操作,结果保存到Rd中。指令格式如下:
EOR{cond}{S}Rd,Rn,operand2
EOR 指令举例如下:
EOR R1,R1,#0x0F ;将R1 的低4 位取反
EOR R2,R1,R0 ;R2=R1^R0
EORS R0,R5,#0x01 ;将R5 和0x01 进行逻辑异或,结果保存到R0,并影响标志位
2.2.10 BIC 位清除指令
将寄存器Rn 的值与operand2 的值的反码按位作逻辑与操作,结果保存到Rd中。指令格式如下:
BIC{cond}{S}Rd,Rn,operand2
BIC 指令举例如下:
BIC R1,R1,#0x0F ;将R1 的低4 位清零,其它位不变
BIC R1,R2,R3 ;将拭的反码和R2 相逻辑与,结果保存到R1
2.3 比较指令
2.3.1 CMP 比较指令
指令使用寄存器Rn 的值减去operand2 的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:
CMP{cond} Rn,operand2
CMP 指令举例如下:
CMP R1,#10 ;R1 与10 比较,设置相关标志位
CMP R1,R2 ;R1 与R2 比较,设置相关标志位
CMP 指令与SUBS 指令的区别在于CMP 指令不保存运算结果。在进行两个数据大小判断时,常用CMP指令及相应的条件码来操作。
2.3.2 CMN 负数比较指令
指令使用寄存器Rn 与值加上operand2 的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行,指令格式如下:
CMN{cond} Rn,operand2
CMN R0,#1 ;R0+1,判断R0 是否为1 的补码,若是Z 置位
CMN 指令与ADDS 指令的区别在于CMN 指令不保存运算结果。CMN指令可用于负数比较,比如CMNR0,#1 指令则表示R0 与-1 比较,若R0 为-(即1 的补码),则Z 置位,否则Z复位。
2.3.3 TST 位测试指令
指令将寄存器Rn 的值与operand2 的值按位作逻辑与操作,根据操作的结果更新CPSR中相应的条件标志位(当结果为0时,EQ位被设置),以便后面指令根据相应的条件标志来判断是否执行。指令格式如下:
TST{cond} Rn,operand2
TST 指令举例如下:
TST R0,#0x01 ;判断R0 的最低位是否为0
TST R1,#0x0F ;判断R1 的低4 位是否为0
TST 指令与ANDS 指令的区别在于TST4 指令不保存运算结果。TST指令通常于EQ、NE条件码配合使用,当所有测试位均为0 时,EQ 有效,而只要有一个测试为不为0,则NE 有效。
2.3.4 TEQ 相等测试指令
指令寄存器Rn 的值与operand2 的值按位作逻辑异或操作,根据操作的结果更新CPSR中相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:
TEQ{cond} Rn,operand2
TEQ 指令举例如下:
TEQ R0,R1 ;比较R0 与R1 是否相等(不影响V 位和C 位)
TST 指令与EORS 指令的区别在于TST 指令不保存运算结果。使用TEQ进行相等测试,常与EQNE 条件码配合使用,当两个数据相等时,EQ 有效,否则NE 有效。
2.4 乘法指令
ARM7TDMI(-S)具有32×32 乘法指令、32×32 乘加指令、32×32结果为64 位的乘法指令。
2.4.1 MUL 32 位乘法指令
指令将Rm 和Rs 中的值相乘,结果的低32 位保存到Rd中。指令格式如下:
MUL{cond}{S} Rd,Rm,Rs
MUL 指令举例如下:
MUL R1,R2,R3 ;R1=R2×R3
MULS R0,R3,R7 ;R0=R3×R7,同时设置CPSR 中的N位和Z 位
2.4.2 MLA 32 位乘加指令
指令将Rm 和Rs 中的值相乘,再将乘积加上第3 个操作数,结果的低32位保存到Rd 中。指令格式如下:
MLA{cond}{S} Rd,Rm,Rs,Rn
MLA 指令举例如下:
MLA R1,R2,R3,R0 ;R1=R2×R3+10
2.4.3 UMULL 64 位无符号乘法指令
指令将Rm 和Rs 中的值作无符号数相乘,结果的低32位保存到RsLo 中,而高32 位保存到RdHi 中。指令格式如下:
UMULL{cond}{S} RdLo,RdHi,Rm,Rs
UMULL 指令举例如下:
UMULL R0,R1,R5,R8 ;(R1、R0)=R5×R8
2.4.4 UMLAL 64 位无符号乘加指令
指令将Rm 和Rs 中的值作无符号数相乘,64 位乘积与RdHi、RdLo相加,结果的低32 位保存到RdLo 中,而高32 位保存到RdHi 中。指令格式如下:
UMLAL{cond}{S} RdLo,RdHi,Rm,Rs
UMLAL 指令举例如下:
UMLAL R0,R1,R5,R8;(R1,R0)=R5×R8+(R1,R0)
2.4.5 SMULL 64 位有符号乘法指令
指令将Rm 和Rs 中的值作有符号数相乘,结果的低32位保存到RdLo 中,而高32 位保存到RdHi 中。指令格式如下:
SMULL{cond}{S} RdLo,RdHi,Rm,Rs
SMULL 指令举例如下:
SMULL R2,R3,R7,R6 ;(R3,R2)=R7×R6
2.4.6 SMLAL 64 位有符号乘加指令
指令将Rm 和Rs 中的值作有符号数相乘,64 位乘积与RdHi、RdLo,相加,结果的低32位保存到RdLo 中,而高32 位保存到RdHi 中。指令格式如下:
SMLAL{cond}{S} RdLo,RdHi,Rm,Rs
SMLAL 指令举例如下:
SMLAL R2,R3,R7,R6;(R3,R2)=R7×R6+(R3,R2)
3. ARM 跳转指令
两种方式可以实现程序的跳转:
(1)使用跳转指令直接跳转,跳转指令有跳转指令B,带链接的跳转指令BL ,带状态切换的跳转指令BX。
(2)直接向PC 寄存器赋值实现跳转。
3.1 B 跳转指令
跳转到指定的地址执行程序。
B{cond} label
举例如下:
B WAITA ;跳转到WAITA 标号处
B 0x1234 ;跳转到绝对地址0x1234 处
跳转到指令B 限制在当前指令的±32Mb 的范围内。
EQ:equal 相等
NE:not equal,不相等
GT:great than,大于
GE greate equal,大于等于
LT:less than,小于
LE:less equal,.小于等于
3.2 BL 带链接的跳转指令
指令将下一条指令的地址拷贝到R14(即LR)链接寄存器中,然后跳转到指定地址运行程序。
BL{cond} label
举例如下:
BL DELAY
跳转指令B 限制在当前指令的±32MB 的范围内。BL 指令用于子程序调用。
3.3 BX 带状态切换的跳转指令
跳转到Rm 指定的地址执行程序,若Rm 的位[0]为1,则跳转时自动将CPSR 中的标志T 置位,即把目标地址的代码解释为Thumb代码;若Rm 的位[0]为0,则跳转时自动将CPSR 中的标志T 复位,即把目标地址的代码解释为ARM代码。指令格式如下:
BX{cond} Rm
举例如下:
ADRL R0,ThumbFun+1
BX R0 ;跳转到R0 指定的地址,并根据R0 的最低位来切换处理器状态
3.4 BLX
BLX目标地址:跳转,改变状态及保存PC值
4. ARM 协处理器指令
ARM 支持协处理器操作,协处理器的控制要通过协处理器命令实现。
4.1 CDP 协处理器数据操作指令
ARM 处理器通过CDP 指令通知ARM 协处理器执行特定的操作。该操作由协处理器完成,即对命令的参数的解释与协处理器有关,指令的使用取决于协处理器。若协处理器不能成功地执行该操作,将产生未定义指令异常中断。指令格式如下:
CDP{cond}coproc,opcodel,CRd,CRn,CRm{,opcode2}
其中: coproc 指令操作的协处理器名。标准名为pn,n 为0~15。
opcodel 协处理器的特定操作码。
CRd 作为目标寄存器的协处理器寄存器。
CRN 存放第1 个操作数的协处理器寄存器。
CRm 存放第2 个操作数的协处理器寄存器。
Opcode2 可选的协处理器特定操作码。
CDP 指令举例如下:
CDP p7,0,c0,c2,c3,0 ;协处理器7 操作,操作码为0,可选操作码为0
CDP p6,1,c3,c4,c5 ;协处理器操作,操作码为1
4.2 LDC 协处理器数据读取指令
LDC指令从某一连续的内存单元将数据读取到协处理器的寄存器中。协处理器数据的数据的传送,由协处理器来控传送的字数。若协处理器不能成功地执行该操作,将产生未定义指令异常中断。指令格式如下:
LDC{cond}{L} coproc,CRd,<地址>
其中: L 可选后缀,指明是长整数传送。
coproc 指令操作的协处理器名。标准名为pn,n 为0~15
CRd 作为目标寄存的协处理器寄存器。
<地址> 指定的内存地址
LDC 指令举例如下:
LDC p5,c2,[R2,#4];读取R2+4指向的内存单元的数据,传送到协处理器p5的c2寄存器中
LDC p6,c2,[R1] ;读取是指向的内存单元的数据,传送到协处理器p6的c2 寄存器中
4.3 STC 协处理器数据写入指令
STC指令将协处理器的寄存器数据写入到某一连续的内存单元中。进行协处理器数据的数据传送,由协处理器来控制传送的字数。若协处理器不能成功地执行该操作,将产生未定义指令异常中断。指令格式如下:
STC{cond}{L} coproc,CRd,<地址>
其中: L 可选后缀,指明是长整数传送。
coproc 指令操作的协处理器名。标准名为pn,n 为0~15
CRd 作为目标寄存的协处理器寄存器。
<地址> 指定的内存地址
STC 指令举例如下:
STC p5,c1,[R0]
STC p5,c1,[Ro,#-0x04]
4.4 MCR ARM寄存器到协处理器寄存器的数据传送指令
MCR 指令将ARM 处理器的寄存器中的数据传送到协处理器的寄存器中。若协处理器不能成功地执行该操作,将产生未定义指令异常中断。指令格式如下:
MCR{cond}coproc,opcodel,Rd,CRn,CRm{,opcode2}
其中:coproc 指令操作的协处理器名。标准名为pn,n 为0~15。
cpcodel 协处理器的特定操作码。
RD 作为目标寄存器。
CRn 存放第1 个操作数的协处理器寄存器
CRm 存放第2 个操作数的协处理器寄存器。
Opcode2 可选的协处理器特定操作码。
MCR 指令举例如下:
MCR p6,2,R7,c1,c2,
MCR P7,0,R1,c3,c2,1,
4.5 MRC 协处理器寄存器到ARM寄存器到的数据传送指令
MRC 指令将协处理器寄存器中的数据传送到ARM 处理器的寄存器中。若协处理器不能成功地执行该操作。将产生未定义异常中断。指令格式如下:
MRC {cond}coproc,opcodel,Rd,CRn,CRm{,opcode2}
其中:coproc 指令操作的协处理器名。标准名为pn,n为0~15。
opcodel 协处理器的特定操作码。
Rd 作为目标寄存器。
CRn 存放第1 个操作数的协处理器寄存器。
CRm 存放第2 个操作数的协处理器寄存器。
opcode2 可选的协处理器特定操作码。
MRC 指令举例如下:
MRC p5,2,R2,c3,c2
MRC p7,0,R0,c1,c2,1
5. ARM 杂项指令
5.1 SWI 软中断指令
SWI 指令用于产生软中断,从而实现在用户模式变换到管理模式,CPSR保存到管理模式的SPSR中,执行转移到SWI 向量,在其它模式下也可使用SWI 指令,处理同样地切换到管理模式。指令格式如下:
SWI{cond} immed_24
其中:immed_24 24 位立即数,值为0~16777215 之间的整数。
SWI 指令举例如下:
SWI 0 ;软中断,中断立即数为0
SWI 0x123456 ;软中断,中断立即数为0x123456
使用SWI 指令时,通常使用以下两种方法进行传递参数,SWI 异常中断处理程序就可以提供相关的服务,这两种方法均是用户软件协定。SWI异常中断处理程序要通过读取引起软中断的SWI 指令,以取得24 位立即数。
(A)指令中的24 位立即数指定了用户请求的服务类型,参数通过用寄存器传递。
MOV R0,#34 ;设置了功能号为34
SWI 12 ;调用12 号软中断
(B)指令中的24 位立即数被忽略,用户请求的服务类型由寄存器R0 的值决定,参数通过其它的通用寄存器传递。
MOV R0,#12 ;调用12 号软中断
MOV R1,#34 ;设置子功能号为34
SWI 0 ;
在SWI 异常中断处理程序中,取出SWI 立即数的步骤为:确定引起软中断的SWI指令是ARM指令还时Thumb 指令,这可以通过对SPSR 访问得到,然后要取得该SWI 指令的地址,这可以通过访问LR 寄存器得到:接着读出指令,分解出立即数。
读出SWI 立即数:
T_bit EQU 0x20
SWI_Hander
STMFD SP!,{R0_R3,R12,LR} ;现场保护
MRS R0,SPSR ;读取SPSR
STMFD SP!,{R0} ;保存SPSR
TST R0,#T_bit ;测试T标志位
LDRNEH R0,[LR,#-2] ;若是Thumb指令,读取指令码(16 位)
BICNE R0,R0,#0xFF00 ;取得Thumb 指令的8 位立即数
LDREQ R0,[LR,#-4] ;若是ARM 指令,读取指令码(32 位)
BICNQ R0,R0,#0xFF00000 ;取得ARM 指令的24 位立即数
…
LDMFD SP!,{R0-R3,R12,PC}^ ;SWI 异常中断返回
5.2 MRS 读状态寄存器指令
在ARM 处理器中,只有MRS 指令可以状态寄存器CPSR或SPSR读出到通用寄存器中。指令格式如下:
MRS{cond} Rd ,psr
其中: Rd 目标寄存器。Rd 不允许为R15。
psr CPSR 或SPSR
MRS指令举例如下:
MRS R1,CPSR ;将CPSR状态寄存器读取,保存到R1 中
MRS R2,SPSR ;将SPSR状态寄存器读取,保存到R2 中
MRS 指令读取CPSR,可用来判断ALU 的状态标志,或IRQ、FIQ中断是否允许等;在异常处理程序中,读SPSR 可知道进行异常前的处理器状态等。MRS 与MSR 配合使用,实现CPSR 或SPSR 寄存器的读—修改---写操作,可用来进行处理器模式切换,允许/禁止IRQ/FIQ中断等设置。另外,进程切换或允许异常中断嵌套时,也需要使用MRS 指令读取SPSR 状态值。保存起来。
使能IRQ 中断例程:
ENABLE_IRQ
MRS R0,CPSR
BIC R0。R0,#0x80
MSR CPSR_c,R0
MOV PC,LR
禁能IRQ 中断例程:
DISABLE_IRQ
MRS R0,CPSR
ORR R0,R0,#0x80
MSR CPSR_c,R0
MOV PC,LR
5.3 MSR 写状态寄存器指令
在ARM 处理器中。只有MSR 指令可以直接设置状态寄存器CPSR或SPSR。指令格式如下:
MSR{cond} psr_fields,#immed_8r
MSR{cond} psr_fields,Rm
其中: psr CPSR 或SPSR
fields 指定传送的区域。Fields 可以是以下的一种或多种(字母必须为小写):
c 控制域屏蔽字节(psr[7…0])
x 扩展域屏蔽字节(psr[15…8])
s 状态域屏蔽字节(psr[23。…16])
f 标志域屏蔽字节(psr[31…24])
immed_8r 要传送到状态寄存器指定域的立即数,8 位。
Rm 要传送到状态寄存器指定域的数据的源寄存器。
MSR 指令举例如下:
MSR CPSR_c,#0xD3 ;CPSR[7…0]=0xD3,即切换到管理模式。
MSR CPSR_cxsf,R3 ;CPSR=R3
只有在特权模式下才能修改状态寄存器。
程序中不能通过MSR 指令直接修改CPSR 中的T 控制位来实现ARM 状态/Thumb状态的切换,必须使用BX 指令完成处理器状态的切换(因为BX 指令属转移指令,它会打断流水线状态,实现处理器状态切换)。MRS 与MSR 配合使用,实现CPSR或SPSR 寄存器的读-修改-写操作,可用来进行处理器模式切换、允许/禁止IRQ/FIQ 中断等设置。
堆栈指令实始化例程:
INITSTACK
MOV R0,LR ;保存返回地址
;设置管理模式堆栈
MSR CPSR_c,#0xD3
LDR SP,StackSvc
;设置中断模式堆栈
MSR CPSR_c,#0xD2
LDR SP,StackIrq
…
6. ARM 伪指令
ARM 伪指令不是ARM 指令集中的指令,只是为了编程方便编译器定义了伪指令,使用时可以像其它ARM 指令一样使用,但在编译时这些指令将被等效的ARM 指令代替。
ARM伪指令有四条,分别为ADR 伪指令、ADRL 伪指令、LDR 伪指令和NOP 伪指令。
6.1 ADR 小范围的地址读取伪指令
ADR 指令将基于PC 相对偏移的地址值读取到寄存器中。在汇编编译源程序时,ADR伪指令被编译器替换成一条合适的指令。通常,编译器用一条ADD 指令或SUB 指令来实现该ADR 伪指令的功能,若不能用一条指令实现,则产生错误,编译失败。
ADR 伪指令格式如下:
ADR{cond} register,exper
其中:register 加载的目标寄存器。
exper 地址表达式。当地址值是非字地齐时,取值范围-255~255 字节之间;当地址是字对齐时,取值范围-1020~1020字节之间。
对于基于PC 相对偏移的地址值时,给定范围是相对当前指令地址后两个字处(因为ARM7TDMI为三级流水线)。
ADR 伪指令举例如下:
LOOP MOV R1, #0xF0
…
ADR R2, LOOP ;将LOOP 的地址放入R2
ADR R3, LOOP+4
可以用ADR 加载地址,实现查表:
…
ADR R0,DISP_TAB ;加载转换表地址
LDRB R1,[R0,R2] ;使用R2作为参数,进行查表
…
DISP_TAB
DCB0Xc0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90
6.2 ADRL 中等范围的地址读取伪指令
ADRL 指令将基于PC 相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中,比ADR伪指令可以读取更大范围的地址。在汇编编译源程序时,ADRL 伪指令被编译器替换成两个条合适的指令。若不能用两条指令实现ADRL 伪指令功能,则产生错误,编译失败。ADRL伪指令格式如下:
ADR{cond} register,exper
其中:register 加载的目标寄存器。
expr 地址表达式。当地址值是非字对齐时,取范围-64K~64K 字节之间;当地址值是字对齐时,取值范围-256K~256K字节之间。
ADRL 伪指令举例如下:
ADRL R0,DATA_BUF
…
ADRL R1 DATA_BUF+80
…
DATA_BUF
SPACE 100 ;定义100 字节缓冲区
可以且用ADRL 加载地址,实现程序跳转,中等范围地址的加载:
…
ADR LR,RETURNI ;设置返回地址
ADRL R1Thumb_Sub+1 ;取得了Thumb 子程序入口地址,且R1 的0 位置1
BX R1 ;调用Thumb子程序,并切换处理器状态
RETURNI
…
CODE16
Thumb_Sub
MOV R1,#10
…
6.3 LDR 大范围的地址读取伪指令
LDR 伪指令用于加载32 位的立即数或一个地址值到指定寄存器。在汇编编译源程序时,LDR伪指令被编译器替换成一条合适的指令。若加载的常数未超出MOV 或MVN 的范围,则使用MOV 或MVN 指令代替该LDR 伪指令,否则汇编器将常量放入字池,并使用一条程序相对偏移的LDR指令从文字池读出常量。LDR 伪指令格式如下:
LDR{cond} register,=expr/label_expr
其中:register 加载的目标寄存器
expr 32 位立即数。
label_expr 基于PC 的地址表达式或外部表达式。
LADR 伪指令举例如下:。
LDR R0,=0x123456 ;加载32 位立即数0x12345678
LDR R0,=DATA_BUF+60 ;加载DATA_BUF 地址+60
…
LTORG ;声明文字池
伪指令LDR 常用于加载芯片外围功能部件的寄存器地址(32 位立即数),以实现各种控制操作加载32位立即数:
…
LDR R0,=IOPIN ;加载GPIO 寄存器IOPIN 的地址
LDR R1,[R0] ;读取IOPIN 寄存器的值
…
LDR R0,=IOSET
LDR R1,=0x00500500
STR R1,[R0] ;IOSET=0x00500500
…
从PC 到文字池的偏移量必须小于4KB。与ARM 指令的LDR 相比,伪指令的LDR的参数有“=”号
6.4 NOP 空操作伪指令
NOP 伪指令在汇编时将会被代替成ARM 中的空操作,比如可能为“MOV R0, R0”指令等,NOP 伪指令格式如下:
NOP
NOP
NOP
NOP
SUBS R1, R1, #1
BNE DELAY1
…
————————————————
版权声明:本文为CSDN博主「闪耀大叔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/freestep96/article/details/126442658