常用ARMv4指令集及汇编.docx
- 文档编号:23313266
- 上传时间:2023-05-16
- 格式:DOCX
- 页数:124
- 大小:148.18KB
常用ARMv4指令集及汇编.docx
《常用ARMv4指令集及汇编.docx》由会员分享,可在线阅读,更多相关《常用ARMv4指令集及汇编.docx(124页珍藏版)》请在冰豆网上搜索。
常用ARMv4指令集及汇编
常用ARMv4指令集及汇编
前言
零零散散用了大概一周的时间,在《常用ARM指令集及汇编》(2003年12月1日)的基础上,大致学习了一遍ARM指令集和汇编,看的过程当中更正了一些错误的地方,并结合其它资料适当添加了一些内容,也做了一些删减,现分享出来,希望能帮助到需要的人。
文中必然还有一些错误还有待改进,有些地方还需要解释的更加详细,本人精力有限,希望有心的读者,订正并增加注释,最好也分享出来,以方便大家对ARM指令的深入理解。
作为刚入行的新手,不谦虚的推荐几本书:
《ARM体系结构与编程-杜春雷》,《嵌入式系统体系结构、编程与设计-RajKamal著,贾建斌译》,《嵌入式系统:
采用公开源代码和StrongARM_XScale处理器-毛德操》。
这几本书到底好不好,看个人需求和喜好吧,仁者见仁智者见智。
最后,感谢《常用ARM指令集及汇编》的原作者和其它资料的作者让我有机会学习我不熟悉的东西,谢谢。
learllp
2015-11-09
ARMv4指令集及汇编
ARM处理器是基于精简指令集计算机(RISC)原理设计的,指令集和相关译码机制较为简单,具有32位ARM指令集和16位Thumb指令集。
ARM指令集效率高,但是代码密度低,而Thumb指令集具有更好的代码密度,却仍然保持ARM的大多数性能上的优势,它是ARM指令集的子集,主要用于编译器设计。
所有ARM指令都是可以有条件执行的,而Thumb指令仅有一条指令具备条件执行功能。
ARM程序和Thumb程序可相互调用,相互之间的状态切换开销几乎为零。
一、ARM处理器寻址方式
寻址方式是根据指令中给出的地址码字段来实现寻找真实操作数地址的方式,ARM处理器有9种基本寻址方式。
寄存器寻址
操作数的值在寄存器中,指令中的地址码字段指出的是寄存器编号,指令执行时直接取出寄存器值操作。
寄存器寻址指令举例如下:
MOVR1,R2;R2->R1
SUBR0,R1,R2;R1-R2->R0
立即寻址
立即寻址指令中的操作码字段后面的地址码部分就是操作数本身,也就是说,数据就包含在指令当中,取出指令也就取出了可以立即使用的操作数(立即数)。
立即寻址指令举例如下:
SUBSR0,R0,#1;R0–1->R0
MOVR0,#0xff00;0xff00->R0
立即数要以“#”为前缀,表示16进制数值时以“0x”表示。
寄存器偏移寻址
寄存器偏移寻址是ARM指令集特有的寻址方式,当第2操作数是寄存器偏移方式时,第2个寄存器操作数在与第1个操作数结合之前,选择进行移位操作。
寄存器偏移寻址方式指令举例如下:
MOVR0,R2,LSL#3;R2的值左移3位,结果放入R0,即R0=R2*8
ANDSR1,R1,R2,LSLR3;R2的值左移R3位,然后和R1相与操作,结果放入R1
可采用的移位操作如下:
LSL:
逻辑左移(LogicalShiftLeft),寄存器中字的低端空出的位补0。
LSR:
逻辑右移(LogicalShiftRight),寄存器中字的高端空出的位补0。
ASR:
算术右移(ArithmeticShiftRight),移位过程中保持符号位不变,即如果源操作数为正数,则字的高端空出的位补0,否则补1。
ROR:
循环右移(RotateRight),由字的低端移出的位填入字的高端空出的位。
RRX:
带扩展的循环右移(RotateRightextendedby1place),操作数右移一位,高端空出的位用原C标志值填充。
各移位操作如下图所示:
寄存器间接寻址
寄存器间接寻址指令中的地址码给出的是一个通用寄存器编号,所需要的操作数保存在寄存器指定地址的存储单元中,即寄存器为操作数的地址指针。
寄存器间接寻址指令举例如下:
LDRR1,[R2];将R2中的数值作为地址,取出此地址中的数据保存在R1中
SWPR1,R1,[R2];将如中的数值作为地址,取出此地址中的数值与R1中的值交换
基址寻址
基址寻址是将基址寄存器的内容与指令中给出的偏移量相加,形成操作数的有效地址,基址寻址用于访问基址附近的存储单元,常用于查表,数组操作,功能部件寄存器访问等。
基址寻址指令举例如下:
LDRR2,[R3,#0x0F];将R3中的数值加0x0F作为地址,将该地址保存在R2中
STRR1,[R0,#-2];将R0中的数值减2作为地址,把R1中的内容保存到此地址
多寄存器寻址
多寄存器寻址就是一次可以传送几个寄存器值,允许一条指令传送16个寄存器的任何子集或所有寄存器。
多寄存器寻址指令举例如下:
LDMIAR1!
,{R2-R7,R12};将[R1]单元中的数据读出到R2-R7,R12,R1自动加1
STMIAR0!
,{R3-R6,R10};将R3-R6,R10中的数据保存到[R0],R0自动加1
使用多寄存器寻址指令时,寄存器子集的顺序由小到大的顺序排列,连续的寄存器可用“-”连接,否则用“,”分隔书写。
堆栈寻址
堆栈是特定顺序进行存取的存储区,操作顺序分为“后进先出”和“先进后出”,堆栈寻址是隐含的,它使用一个专门的寄存器(堆栈指针)指向一块存储区域(堆栈),指针所指向的存储单元就是堆栈的栈顶。
存储器堆栈可分为两种:
向上生长:
向高地址方向生长,称为递增堆栈。
向下生长:
向低地址方向生长,称为递减堆栈。
堆栈指针指向最后压入堆栈的有效数据项,称为满堆栈;堆栈指针指向下一个要放入的空位置,称为空堆栈。
这样就有4种类型的堆栈表示。
满递增:
堆栈通过递增存储器的地址向上增长,堆栈指针指向堆栈内含有效数据项的最高地址。
指令如LDMFA,STMFA等。
空递增:
堆栈通过递增存储器的地址向上增长,堆栈指针指向堆栈内的第一个空位置。
指令如LDMEA,STMEA等。
满递减:
堆栈通过递减存储器的地址向下增长,堆栈指针指向堆栈内含有效数据项的最低地址。
指令如LDMFD,STMFD等。
空递减:
堆栈通过递减存储器的地址向下增长,堆栈指针指向堆栈下的第一个空位置。
指令如LDMED,STMED等。
堆栈寻址指令举例如下:
STMFDSP!
,{R1-R7,LR};将R1~R7,LR入栈
LDMFDSP!
,{R1-R7,LR};数据出栈,放入R1~R7,LR寄存器
块拷贝寻址
多寄存器传送指令用于一块数据从存储器的某一位置拷贝到另一位置。
块拷贝寻址指令举例如下:
STMIAR0!
,{R1-R7};将R1~R7的数据保存到存储器中,存储器指针在保存第一
;个值之后增加,增长方向为向上增长
STMIBR0!
,{R1-R7};将R1~R7的数据保存到存储器中,存储器指针在保存第一
;个值之前增加,增长方向为向上增长
STMDAR0!
,{R1-R7};将R1~R7的数据保存到存储器中,存储器指针在保存第一
;个值之后增加,增长方向为向下增长
STMDBR0!
,{R1-R7};将R1~R7的数据保存到存储器中,存储器指针在保存第一
;个值之前增加,增长方向为向下增长
相对寻址
相对寻址是基址寻址的一种变通,由程序计数器PC提供基准地址,指令中的地址码字段作为偏移量,两者相加后得到的地址即为操作数的有效地址。
相对寻址指令举例如下:
BLROUTE1;调用ROUTE1子程序
BEQLOOP;条件跳转到LOOP标号处
…
LOOPMOVR2,#2
…
ROUTE1
…
二、指令集介绍
ARM指令集
指令格式
基本格式
opcode指令助记符,如LDR,STR等
cond执行条件,如EQ,NE等
S是否影响CPSR寄存器的值,带S则影响CPSR,否则不影响
Rd目标寄存器
Rn第一个操作数的寄存器
operand2第二个操作数
指令格式举例如下:
SUBNESR1,R1,#0xD;条件执行减法运算(NE),R1-0xD=>R1,影响CPSR寄存器,带有S
第2个操作数
在ARM指令中,灵活的使用第2个操作数能提高代码效率,第2个操作数形式如有3种形式:
1.#immed_8r常数表达式
该常数必须对应8位位图,即常数是由一个8位的常数循环移位偶数位得到。
此处immed_8r可以是一个32位的立即数,但这不是一个任意的32位立即数,它必须是一个8位立即数在32位范围内进行偶数次循环右移能够获得的数,否则编译时会出错。
在12位的operand2中:
后8位存数据,前4位存移位的次数。
8位存数据:
解释了“该常数必须对应8位位图”。
4位存移位的次数:
解释了为什么只能移偶数位。
4位只有16种可能值,而32位数可以循环移位32次(32种可能),那就只好限制:
只能移偶数位,一次移动两位,好像一个16位数在移位,16种移位可能。
合法常量:
0x3FC、0、0xF0000000、200,0xF0000001
非法常量:
0x1FE、511、0xFFFF、0x1010、0xF0000010
解释:
0x3FC,可以通过对0xFF循环右移30次获得。
0xF0000001,可以通过对0x1F循环右移4次获得。
0x1010,不能通过对某个8位数循环右移偶数次得到,所以非法。
常数表达式应用举例如下:
MOVR0,#1;R0=1
ANDR1,R2,#0x0F;R2与0x0F,结果保存在R1
LDRR0,[R1],#-4;读取R1地址上的存储器单元内容,且R1=R1-4
2.Rm寄存器方式
在寄存器方式下操作数即为寄存器的数值。
寄存器方式应用举例:
SUBR1,R1,R2;R1=R1-R2
MOVPC,R0;PC=R0,程序跳转到指定地址
LDRR0,[R1],-R2;先读取[R1]的内容并存入R0,后R1=R1-R2
3.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位
寄存器偏移方式应用举例:
ADDR1,R1,R1,LSL#3;R1=R1*9,此处Rs位置为一个立即数
SUBR1,R1,R2,LSR#2;R1=R1-R2*4
R15为处理器的程序计数器PC,一般不要对其进行操作,而且有些指令是不允许使用R15,如UMULL指令。
条件码
使用指令条件码,可实现高效的逻辑操作,提高代码效率。
条件码表
条件码助记符
CPRS标志
含义
EQ
Z=1
相等
NE
Z=0
不相等
CS/HS
C=1
无符号数大于或等于
CC/LO
C=0
无符号数小于
MI
N=1
负数
PL
N=0
正数或零
VS
V=1
溢出
VC
V=0
没有溢出
HI
C=1,Z=0
无符号数大于
LS
C=0,Z=1
无符号数小于或等于
GE
N=V
带符号数大于或等于
LT
N!
=V
带符号数小于
GT
Z=0,N=V
带符号数大于
LE
Z=1,N!
=V
带符号数小于或等于
AL
任何
无条件执行(指令默认条件)
对于Thumb指令集,只有B指令具有条件码执行功能,此指令条件码同上表,但如果为无条件执行时,条件码助记符“AL”不能在指令中书写。
条件码应用举例如下:
比较两个值大小,并进行相应加1处理,C代码为
if(a>b)
a++;
elseb++;
对应的ARM指令如下。
其中R0为a,R1为b。
CMPR0,R1;R0与R1比较
ADDHIR0,R0,#1;若R0>R1,则R0=R0+1
ADDLSR1,R1,#1;若R0<=R1,则R1=R1+1
若两个条件均成立,则将这两个数值相加,C代码为
If((a!
=10)&&(b!
=20))
a=a+b;
对应的ARM指令如下,其中R0为a,R1为b。
CMPR0,#10;比较R0是否为10
CMPNER1,#20;若R0不为10,则比较R1是否20
ADDNER0,R0,R1;若R0不为10且R1不为20,指令执行,R0=R0+R1
ARM存储器访问指令
ARM处理是加载/存储体系结构的典型的RISC处理器,对存储器的访问只能使用加载和存储指令实现。
ARM的加载/存储指令是可以实现字、半字、无符/有符字节操作;批量加载/存储指令可实现一条指令加载/存储多个寄存器的内容,大大提高效率;SWP指令是一条寄存器和存储器内容交换的指令,可用于信号量操作等。
ARM处理器是冯.诺依曼或者哈佛存储结构,程序空间、RAM空间及IO映射空间统一编址,除对RAM操作以外,对外围IO、程序数据的访问也均要通过加载/存储指令进行。
ARM存储访问指令表
助记符
说明
操作
条件码位置
LDRRd,addressing
加载字数据
Rd←[addressing],addressing索引
LDR{cond}
LDRBRd,addressing
加载无符字节数据
Rd←[addressing],addressing索引
LDR{cond}B
LDRTRd,addressing
以用户模式加载字数据
Rd←[addressing],addressing索引
LDR{cond}T
LDRBTRd,addressing
以用户模式加载无符号字数据
Rd←[addressing],addressing索引
LDR{cond}BT
LDRHRd,addressing
加载无符半字数据
Rd←[addressing],addressing索引
LDR{cond}H
LDRSBRd,addressing
加载有符字节数据
Rd←[addressing],addressing索引
LDR{cond}SB
LDRSHRd,addressing
加载有符半字数据
Rd←[addressing],addressing索引
LDR{cond}SH
STRRd,addressing
存储字数据
[addressing]←Rd,addressing索引
STR{cond}
STRBRd,addressing
存储字节数据
[addressing]←Rd,addressing索引
STR{cond}B
STRTRd,addressing
以用户模式存储字数据
[addressing]←Rd,addressing索引
STR{cond}T
SRTBTRd,addressing
以用户模式存储字节数据
[addressing]←Rd,addressing索引
STR{cond}BT
STRHRd,addressing
存储半字数据
[addressing]←Rd,addressing索引
STR{cond}H
LDM{mode}Rn{!
},reglist
批量(寄存器)加载
reglist←[Rn…],Rn回存等
LDM{cond}{more}
STM{mode}Rn{!
},rtglist
批量(寄存器)存储
[Rn…]←reglist,Rn回存等
STM{cond}{more}
SWPRd,Rm,Rn
寄存器和存储器字数据交换
Rd←[Rd],[Rn]←[Rm](Rn≠Rd或Rm)
SWP{cond}
SWPBRd,Rm,Rn
寄存器和存储器字节数据交换
Rd←[Rd],[Rn]←[Rm](Rn≠Rd或Rm)
SWP{cond}B
◆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)立即数。
立即数可以是一个无符号数值,这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值。
指令举例如下:
LDRR1,[R0,#0x12];将R0+0x12地址处的数据读出,保存到R1中(R0的值不变)
LDRR1,[R0,#-0x12];将R0-0x12地址处的数据读出,保存到R1中(R0的值不变)
LDRR1,[R0];将R0地址处的数据读出,保存到R1中(零偏移)
(2)寄存器。
寄存器中的数值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。
指令举例如下:
LDRR1,[R0,R2];将R0+R2地址的数据计读出,保存到R1中(R0的值不变)
LDRR1,[R0,-R2];将R0-R2地址处的数据计读出,保存到R1中(R0的值不变)
(3)寄存器及移位常数。
寄存器移位后的值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。
指令举例如下:
LDRR1,[R0,R2,LSL#2];将R0+R2*4地址处的数据读出,保存到R1中(R0,R2的值不变)
LDRR1,[R0,-R2,LSL#2];将R0-R2*4地址处的数据计读出,保存到R1中(R0,R2的值不变)
从寻址方式的地址计算方法划分,加载/存储指令有以下4种形式:
(1)零偏移。
Rn的值作为传送数据的地址,即地址偏移量为0。
指令举例如下:
LDRRd,[Rn]
(2)前索引偏移。
在数据传送之前,将偏移量加到Rn中,其结果作为传送数据的存储地址。
若使用后缀“!
”,则结果写回到Rn中,且Rn值不允许为R15。
指令举例如下:
LDRRd,[Rn,#0x04]!
LDRRd,[Rn,#-0x04]
(3)程序相对偏移。
程序相对偏移是索引形式的另一个版本。
汇编器根据PC寄存器计算偏移量,并将PC寄存器作为Rn生成前索引指令。
不能使用后缀“!
”。
指令举例如下:
LDRRd,label;label为程序标号,label必须是在当前指令的±4KB范围内
(4)后索引偏移。
Rn的值用做传送数据的存储地址。
在数据传送后,将偏移量与Rn相加,结果写回到Rn中。
Rn不允许是R15。
指令举例如下:
LDRRd,[Rn],#0x04
地址对齐
大多数情况下,必须保证用于32位传送的地址是32位对齐的。
(所谓对齐,通常理解就是变量存储的首地址要能够整除变量的字节数,这样有助于CPU高效率的读取数据。
对于C语言,结构体本身按照结构体成员中最大的变量类型所占的字节数对齐,比如,变量中有8位的longlong型成员,最终,结构体就按照8位对齐。
例,
//AMD32位CPU,DEV-C++5.4.0编译器,XP2系统
structtest
{
charx1[9];//占10个字节
shortx2;//占2个字节
charx3[13];//占20个字节
doublex4;//占8个字节
charx5[5];//占8个字节,为了结构体对齐
};//结构体共占48个字节
各变量起始地址分别为:
0x22fea8,0x22feb2,0x22feb4,0x22fec8,0x22fed0
)加载/存储字和无符号字节指令举例如下:
LDRR2,[R5];加载R5指定地址上的数据(字),放入R2中
STRR1,[R0,#0x04];将R1的数据存储到R0+0x04存储单元,R0值不变
LDRBR3,[R2],#1;读取R2地址上的一个字节的数据,并保存到R3中,R2=R3+1
STRBR6,[R7];读R6的数据保存到R7指定的地址中,只存储一个字节
加载/存储半字和带符号字节。
这类LDR/STR指令可能加载带符字节\加载带符号半字、加载/存储无符号半字。
偏移量格式、寻址方式与加载/存储字和无符号字节指令相同。
指令格式如下:
LDR{cond}SBRd,<地址>;加载指定地址上的数据(带符号字节),放入Rd中
LDR{cond}SHRd,<地址>;加载指定地址上的数据(带符号半字),放入Rd中
LDR{cond}HRd,<地址>;加载半字数据,放入Rd中,低16位有效,高16位清零
STR{cond}HRd,<地址>;存储半字数据,要存储的数据在Rd,低16位有效
说明:
带符号位半字/字节加载是指带符号位加载扩展到32位;无符号位半字加载是指零扩展到32位。
地址对齐,对半字传送的地址必须为偶数。
非半字对齐的半字加载将使Rd内容不可靠,非半字对齐的半字存储将使指定地址的2字节存储内容不可靠。
加载/存储半字和带符号字节指令举例如下:
LDRSBR1[R0,R3];将[R0+R3]上的字节数据读出到R1,高24位用符号位扩展
LDRSHR1,[R9];将R9地址上的半字数据读出到R1,高16位用符号位扩展
LDRHR6,[R2],#2;将R2地址上的半字数据读出到R6,高16位用零扩展,R2=R2+1
SHRHR1,[R0,#2]!
;将R1的数据保存到[R0+2],只存储低2字节数据,R0=R0+2
LDR/STR指令用于对内存变量的访问,内存缓冲区数据的访问、查表、外围部件的控制操作等等,若使用LDR指令加载数据到PC寄存器,
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 常用 ARMv4 指令 汇编
![提示](https://static.bdocx.com/images/bang_tan.gif)