目录
4.5 MRC 协处理器寄存器到ARM寄存器到的数据传送指令
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
…