1 指令格式
(1)基本格式
{}{S} ,{,}
其中,<>内的项是必须的,{}内的项是可选的,如是指令助记符,是必须的,而{}为 指令执行条件,是可选的,如果不写则使用默认条件AL(无条件执行)。
opcode 指令助记符,如LDR,STR 等
cond 执行条件,如EQ,NE 等
S 是否影响CPSR 寄存器的值,书写时影响CPSR,否则不影响
Rd 目标寄存器
Rn 第一个操作数的寄存器
operand2 第二个操作数
指令格式举例如下:
LDR R0,[R1] ;读取R1 地址上的存储器单元内容,执行条件AL
BEQ DATAEVEN ;跳转指令,执行条件EQ,即相等跳转到DATAEVEN
ADDS R1,R1,#1 ;加法指令,R1+1=R1 影响CPSR 寄存器,带有S
SUBNES R1,R1,#0xD;条件执行减法运算(NE),R1-0xD=>R1,影响CPSR 寄存器,带有S
(2)第2个操作数
在ARM 指令中,灵活的使用第2个操作数能提高代码效率,第2个操作数的形式如下:
#immed_8r
常数表达式,该常数必须对应8 位位图,即常数是由一个8 位的常数循环移位偶数位得到。
合法常量
0x3FC、0、0xF0000000、200、0xF0000001等都是合法常量。
非法常量
0x1FE、511、0xFFFF、0x1010、0xF0000010等都是非法常量。
常数表达式应用举例如下:
MOV R0,#1 ;R0=1
AND R1,R2,#0x0F ;R2 与0x0F,结果保存在R1
LDR R0,[R1],#-4 ;读取R1 地址上的存储器单元内容,且R1=R1-4
Rm
寄存器方式,在寄存器方式下操作数即为寄存器的数值。
寄存器方式应用举例:
SUB R1,R1,R2 ;R1-R2=>R1
MOV PC,R0 ;PC=R0,程序跳转到指定地址
LDR R0,[R1],-R2 ;读取R1 地址上的存储器单元内容并存入R0,且R1=R1-R2
Rm, shift
寄存器移位方式。将寄存器的移位结果作为操作数,但RM 值保存不变,移位方法如下:
ASR #n 算术右移n 位(1≤n≤32)
LSL #n 逻辑左移n 位(1≤n≤31)
LSR #n 逻辑左移n 位(1≤n≤32)
ROR #n 循环右移n 位(1≤n≤31)
RRX 带扩展的循环右移1位
type Rs 其中,type 为ASR,LSL,和ROR 中的一种;Rs 偏移量寄存器,低8位有效,若其值大于或等于32,则第2 个操作数的结果为0(ASR、ROR例外)。
寄存器偏移方式应用举例:
ADD R1,R1,R1,LSL #3 ;R1=R1*9
SUB R1,R1,R2,LSR#2 ;R1=R1-R2*4
R15 为处理器的程序计数器PC,一般不要对其进行操作,而且有些指令是不允许使用R15,如UMULL 指令。
(3)条件码
使用指令条件码,可实现高效的逻辑操作,提高代码效率。表A-1给出条件码表。
对于Thumb指令集,只有B 指令具有条件码执行功能,此指令条件码同表A-?,但如果为无条件执行时,条件码助记符“AL”不能在指令中书写。
条件码应用举例如下:
比较两个值大小,并进行相应加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
若两个条件均成立,则将这两个数值相加,C代码为:
If((a!=10)&&(b!=20)) a=a+b;
对应的ARM 指令如下,其中R0 为a,R1 为b。
CMP R0,#10 ; 比较R0 是否为10
CMPNE R1,#20 ; 若R0 不为10,则比较R1 是否20
ADDNE R0,R0,R1 ; 若R0 不为10 且R1 不为20,指令执行,R0=R0+R1
2 ARM 存储器访问指令
ARM 处理是加载/存储体系结构的典型的RISC 处理器,对存储器的访问只能使用加载和存储指令实现。ARM 的加载/存储指令是可以实现字、半字、无符/有符字节操作;批量加载/存储指令可实现一条指令加载/存储多个寄存器的内容,大大提高效率;SWP指令是一 条寄存器和存储器内容交换的指令,可用于信号量操作等。ARM 处理器是冯?诺依曼存储结构,程序空间、RAM 空间及IO 映射空间统一编址,除对对RAM 操作以外,对外围IO、程序数据的访问均要通过加载/存储指令进行。表A-2给出ARM 存储访问指令表。
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
…
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 实现堆栈操作。
多寄存器传送指令示意图如图A-1所示,其中R1为指令执行前的基址寄存器,R1’则为指令执行完后的基址寄存器。
![](http://image14.360doc.com/DownloadImg/2010/08/1710/4670178_3.jpg)
(a)指令STMIA R1!,{R5-R7} (b)指令STMIB R1!,{R5-R7}
![](http://image14.360doc.com/DownloadImg/2010/08/1710/4670178_4.jpg)
(c)指令STMDA R1!, {R5-R7} (d)指令STMDB R1!,{R5-R7}
图A-1 多寄存器传送指令示意图
使用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} ;恢复寄存器,并返回
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 ;若没有信号,则等待
3 ARM 数据处理指令
数据处理指令大致可分为3 类;数据传送指令(如MOV、MVN),算术逻辑运算指令(如ADD,SUM,AND),比较指令(如CMP、TST)。数据处理指令只能对寄存器的内容 进行操作。
所有ARM 数据处理指令均可选择使用S 后缀,以影响状态标志。比较指令CMP、CMN、TST和TEQ不需要后缀S,它们会直接影响状态标志。ARM 数据处理指令列于表A-4中。
(1)数据传送指令
MOV
数据传送指令。将8 位图立即数或寄存器(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 ,子程序返回
MVN
数据非传送指令。将8 位图立即数或寄存器(operand2)按位取反后传送到目标寄存器(Rd),因为其具有取反功能,所以可以装载范围更广的立即数。指令格式如下:
MVN{cond}{S} Rd,operand2
MVN 指令举例如下:
MVN R1,#0xFF ;R1=0xFFFFFF00
MVN R1,R2 ;将R2 取反,结果存到R1
(2)算术逻辑运算指令
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
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
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
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)
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)
RSC
带进位逆向减法指令。用寄存器operand2 减去Rn,再减去CPSR 中的C 条件标志位,结果保存到Rd 中。指令格式如下:
RSC{cond}{S} Rd,Rn,operand2
RSC 指令举例如下:
RSBS R2,R0,#0
RSC R3,R1,#0 ;使用RSC 指令实现求64 位数值的负数
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
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 位中
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,并影响标志位
BIC
位清除指令。将寄存器Rn 的值与operand2 的值的反码按位作逻辑与操作,结果保存到Rd 中。指令格式如下:
BIC{cond}{S}Rd,Rn,operand2
BIC 指令举例如下:
BIC R1,R1,#0x0F ;将R1 的低4 位清零,其它位不变
BIC R1,R2,R3 ;将拭的反码和R2 相逻辑与,结果保存到R1
(3)比较指令
CMP
比较指令。指令使用寄存器Rn 的值减去operand2 的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:
CMP{cond} Rn,operand2
CMP 指令举例如下:
CMP R1,#10 ;R1 与10 比较,设置相关标志位
CMP R1,R2 ;R1 与R2 比较,设置相关标志位
CMP 指令与SUBS 指令的区别在于CMP 指令不保存运算结果。在进行两个数据大小判断时,常用CMP 指令及相应的条件码来操作。
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复位。
TST
位测试指令。指令将寄存器Rn 的值与operand2 的值按位作逻辑与操作,根据操作的结果更新CPSR 中相应的条件标志位,以便后面指令根据相应的条件标志来判断是否执行。指令格式如下:
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 有效。
TEQ
相等测试指令。指令寄存器Rn 的值与operand2 的值按位作逻辑异或操作,根据操作的结果更新CPSR 中相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:
TEQ{cond} Rn,operand2
TEQ 指令举例如下:
TEQ R0,R1 ;比较R0 与R1 是否相等(不影响V 位和C 位)
TST 指令与EORS 指令的区别在于TST 指令不保存运算结果。使用TEQ 进行相等测试,常与EQNE 条件码配合使用,当两个数据相等时,EQ 有效,否则NE 有效。
(4)乘法指令
ARM7TDMI(-S)具有32×32 乘法指令、32×32 乘加指令、32×32 结果为64 位的乘法指令。表A-5给出全部的ARM 乘法指令。
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 位
MLA
32 位乘加指令。指令将Rm 和Rs 中的值相乘,再将乘积加上第3 个操作数,结果的低32 位保存到Rd 中。指令格式如下:
MLA{cond}{S} Rd,Rm,Rs,Rn
MLA 指令举例如下:
MLA R1,R2,R3,R0 ;R1=R2×R3+10
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
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)
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
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)
4 ARM 跳转指令
在ARM 中有两种方式可以实现程序的跳转,一种是使用跳转指令直接跳转,另一种则是直接向PC 寄存器赋值实现跳转。跳转指令有跳转指令B,带链接的跳转指令BL 带状态切换的跳转指令BX。表A-6给出全部的ARM跳转指令。
B
跳转指令。跳转到指定的地址执行程序。指令格式如下:
B{cond} label
跳转指令B 举例如下:
B WAITA ;跳转到WAITA 标号处
B 0x1234 ;跳转到绝对地址0x1234 处
跳转到指令B 限制在当前指令的±32Mb 的范围内。
BL
带链接的跳转指令。指令将下一条指令的地址拷贝到R14(即LR)链接寄存器中,然后跳转到指定地址运行程序。指令格式如下:
BL{cond} label
带链接的跳转指令BL 举例如下:
BL DELAY
跳转指令B 限制在当前指令的±32MB 的范围内。BL 指令用于子程序调用。
BX
带状态切换的跳转指令。跳转到Rm 指定的地址执行程序,若Rm 的位[0]为1,则跳转时自动将CPSR 中的标志T 置位,即把目标地址的代码解释为Thumb 代码;若Rm 的位[0]为0,则跳转时自动将CPSR 中的标志T 复位,即把目标地址的代码解释为ARM 代码。指令格式如下:
BX{cond} Rm
带状态切换的跳转指令BX 举例如下:
ADRL R0,ThumbFun+1
BX R0 ;跳转到R0 指定的地址,并根据R0 的最低位来切换处理器状态
ARM 支持协处理器操作,协处理器的控制要通过协处理器命令实现。表A-7给出全部的ARM协处理器指令。
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
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 寄存器中
STC
协处理器数据写入指令。STC 指令将协处理器的寄存器数据写入到某一连续的内存单元中。进行协处理器数据的数据传送,由协处理器来控制传送的字数。若协处理器不能成功地执行该操作,将 产生未定义指令异常中断。指令格式如下:
STC{cond}{L} coproc,CRd,<地址>
其中: L 可选后缀,指明是长整数传送。
coproc 指令操作的协处理器名。标准名为pn,n 为0~15
CRd 作为目标寄存的协处理器寄存器。
<地址> 指定的内存地址
STC 指令举例如下:
STC p5,c1,[R0]
STC p5,c1,[Ro,#-0x04]
MCR
ARM 寄存器到协处理器寄存器的数据传送指令。MCR 指令将ARM 处理器的寄存器中的数据传送到协处理器的寄存器中。若协处理器不能成功地执行该操作,将产生未定义指令异常中断。指令格式如下:
MCR{cond} coproc,opcodel,Rd,CRn,CRm{,opcode2}
其中:coproc 指令操作的协处理器名。标准名为pn,n 为0~15。
cpcodel 协处理器的特定操作码。
CRD 作为目标寄存器的协处理器寄存器。
CRn 存放第1 个操作数的协处理器寄存器
CRm 存放第2 个操作数的协处理器寄存器。
Opcode2 可选的协处理器特定操作码。
MCR 指令举例如下:
MCR p6,2,R7,c1,c2,
MCR P7,0,R1,c3,c2,1,
MRC
协处理器寄存器到ARM 寄存器到的数据传送指令。MRC 指令将协处理器寄存器中的数据传送到ARM 处理器的寄存器中。若协处理器不能成功地执行该操作。将产生未定义异常中断。指令格式如下:
MRC {cond} coproc,opcodel,Rd,CRn,CRm{,opcode2}
其中:coproc 指令操作的协处理器名。标准名为pn,n为0~15。
opcodel 协处理器的特定操作码。
CRd 作为目标寄存器的协处理器寄存器。
CRn 存放第1 个操作数的协处理器寄存器。
CRm 存放第2 个操作数的协处理器寄存器。
opcode2 可选的协处理器特定操作码。
MRC 指令举例如下:
MRC p5,2,R2,c3,c2
MRC p7,0,R0,c1,c2,1
6 ARM 杂项指令
表A-8给出全部的ARM协处理器指令。
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 异常中断返回
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
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
…
7 ARM 伪指令
ARM 伪指令不是ARM 指令集中的指令,只是为了编程方便编译器定义了伪指令,使用时可以像其它ARM 指令一样使用,但在编译时这些指令将被等效的ARM 指令代替。ARM 伪指令有四条,分别为ADR 伪指令、ADRL 伪指令、LDR 伪指令和NOP 伪指令。
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
DCB 0Xc0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90
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 R1 Thumb_Sub+1 ;取得了Thumb 子程序入口地址,且R1 的0 位置1
BX R1 ;调用Thumb 子程序,并切换处理器状态
RETURNI
…
CODE16
Thumb_Sub
MOV R1,#10
…
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 的参数有“=”号
NOP
空操作伪指令。NOP 伪指令在汇编时将会被代替成ARM 中的空操作,比如可能为“MOV R0, R0”指令等,NOP 伪指令格式如下:
NOP
NOP
NOP
NOP
SUBS R1, R1, #1
BNE DELAY1
…