常⽤ARM指令集及汇编
ARM7TDMI(-S)指令集及汇编
ARM 处理器是基于精简指令集计算机(RISC)原理设计的,指令集和相关译码机制较为简单,ARM7TDMI(-S)具有 32 位 ARM 指令集和16 位 Thumb 指令集,ARM 指令集效率⾼,但是代码密度低,⽽ Thumb 指令集具有更好的代码密度,却仍然保持 ARM 的⼤多数性能上的优势,它是 ARM 指令集的⼦集。所有 ARM 指令都是可以有条件执⾏的,⽽ Thumb 指令仅有⼀条指令具备条件执⾏功能。ARM 程序和Thumb 程序可相互调⽤,相互之间的状态切换开销⼏乎为零。
ARM 处理器寻址⽅式
寻址⽅式是根据指令中给出的地址码字段来实现寻找真实操作数地址的⽅式,ARM 处理器有 9 种基本寻址⽅式。
寄存器寻址
操作数的值在寄存器中,指令中的地址码字段指出的是寄存器编号,指令执⾏时直接取出寄存器值操作。
寄存器寻址指令举例如下:
MOV R1,R2;R2 -> R1
SUB R0,R1,R2;R1 - R2 -> R0
⽴即寻址
⽴即寻址指令中的操作码字段后⾯的地址码部分就是操作数本⾝,也就是说,数据就包含在指令当中,取出指令也就取出了可以⽴即使⽤的操作数(⽴即数)。
⽴即寻址指令举例如下:
SUBS R0,R0,#1 ;R0 – 1 -> R0
MOV R0,#0xff00 ;0xff00 -> R0 ⽴即数要以“#”为前缀,表⽰ 16 进制数值时以“0x”表⽰。
寄存器偏移寻址
寄存器偏移寻址是ARM指令集特有的寻址⽅式,当第2操作数是寄存器偏移⽅式时,第 2 个寄存器操作数在与第 1 个操作数结合之前,选择进⾏移位操作。
寄存器偏移寻址⽅式指令举例如下:
MOV R0,R2,LSL #3 ;R2 的值左移 3 位,结果放⼊ R0,即 R0 = R2 * 8
ANDS R1,R1,R2,LSL R3 ;R2 的值左移 R3 位,然后和 R1 相与操作,结果放⼊ R1 可采⽤的移位操作如下:
LSL:逻辑左移(Logical Shift Left),寄存器中字的低端空出的位补 0
LSR:逻辑右移(Logical Shift Right),寄存器中字的⾼端空出的位补 0
ASR:算术右移(Arithmetic Shift Right),移位过程中保持符号位不变,即如果源操作数为正数,则字的⾼端空出的位补 0,否则补 1 ROR:循环右移(Rotate Right),由字的低端移出的位填⼊字的⾼端空出的位 RRX:带扩展的循环右移(Rotate Right eXtended by
1place),操作数右移⼀位,
⾼端空出的位⽤原 C 标志值填充。
各移位操作如下图所⽰。
0 LSL 移位操作
碳捕获
0 LSR 移位操作
ASR 移位操作
ROR 移位操作
C RRX 移位操作
小环境
寄存器间接寻址
寄存器间接寻址指令中的地址码给出的是⼀个通⽤寄存器编号,所需要的操作数保存在寄存器指定地址的存储单元中,即寄存器为操作数的地址指针。
寄存器间接寻址指令举例如下:
LDR R1,[R2] ;将 R2 中的数值作为地址,取出此地址中的数据保存在 R1 中 SWP R1,R1,[R2];将如中的数值作为地址,取出此地址中的数值与 R1 中的值交换
基址寻址
基址寻址是将基址寄存器的内容与指令中给出的偏移量相加,形成操作数的有效地
址,基址寻址⽤于访问基址附近的存储单元,常⽤于查表,数组操作,功能部件寄存器
访问等。基址寻址指令举例如下:
LDR R2,[R3,#0x0F] ;将 R3 中的数值加 0x0F 作为地址,取出此地址的数值保存在 R2 中
STR R1,[R0,#-2] ;将 R0 中的数值减 2 作为地址,把 R1 中的内容保存到此地址位置
多寄存器寻址
多寄存器寻址就是⼀次可以传送⼏个寄存器值,允许⼀条指令传送 16 个寄存器的任何⼦集或所有寄存器。
多寄存器寻址指令举例如下:
LDMIA R1!,{R2-R7,R12} ;将 R1 单元中的数据读出到 R2-R7,R12,R1 ⾃动加 1
STMIA R0!,{R3-R6,R10};将 R3-R6,R10 中的数据保存到 R0 指向的地址,R0 ⾃动加 1
使⽤多寄存器寻址指令时,寄存器⼦集的顺序时由⼩到⼤的顺序排列,连续的寄存器可⽤“-”连接,否则,⽤“,”分隔书写。
堆栈寻址
堆栈是特定顺序进⾏存取的存储区,操作顺序分为“后进先出”和“先进后出”,堆栈寻址时隐含的,它使⽤⼀个专门的寄存器(堆栈指针)指向⼀块存储区域(堆栈),指针所指向的存储单元就是堆栈的栈顶。存储器堆栈可分为两种:
才会相思向上⽣长:向⾼地址⽅向⽣长,称为递增堆栈向下⽣长:向低地址⽅向⽣长,称为递减堆栈堆栈指针指向最后压⼊的堆栈的有效数据项,称为满堆栈;堆栈指针指向下⼀个要
放⼊的空位置,称为空堆栈。这样就有 4 中类型的堆栈表⽰递增和递减的满堆栈和空堆栈的各种组合。
满递增:堆栈通过增⼤存储器的地址向上增长,堆栈指针指向内含有效数据项的最⾼地址。指令如 LDMFA,STMFA 等。
空递增:堆栈通过增⼤存储器的地址向上增长,堆栈指针指向堆栈上的第⼀个空位置。指令如 LDMEA,STMEA 等。
满递减:堆栈通过减⼩存储器的地址向下增长,堆栈指针指向内含有效数据项的最
低地址。指令如 LDMFD,STMFD 等。空递减:堆栈通过减⼩存储器的地址向下增长,堆栈指针指向堆栈下的第⼀个空
位置。指令如 LDMED,STMED 等。堆栈寻址指令举例如下:
STMFD SP!,{R1-R7,LR} ; 将 R1~R7,LR ⼊栈。满递减堆栈。
LDMFD SP!,{R1-R7,LR} ;数据出栈,放⼊ R1~R7,LR 寄存器。满递减堆栈。
块拷贝寻址
多寄存器传送指令⽤于⼀块数据从存储器的某⼀位置拷贝到另⼀位置。块拷贝寻址指令举例如下:
STMIA R0!,{R1-R7};将 R1~R7 的数据保存到存储器中,存储器指针在保存第
⼀
;个值之后增加,增长⽅向为向上增长。
STMIB R0!,{R1-R7};将 R1~R7 的数据保存到存储器中,存储器指针在保存第
⼀
;个值之前增加,增长⽅向为向上增长。
STMDAR0!,{R1-R7};将 R1~R7 的数据保存到存储器中,存储器指针在保存第
⼀
;个值之后增加,增长⽅向为向下增长。
STMDBR0!,{R1-R7};将 R1~R7 的数据保存到存储器中,存储器指针在保存第
⼀
;个值之前增加,增长⽅向为向下增长。
相对寻址
相对寻址是基址寻址的⼀种变通,由程序计数器 PC 提供基准地址,指令中的地址码字段作为偏移量,两者相加后得到的地址即为操作数的有效地址。
相对寻址指令举例如下:
BL ROUTE1 ;调⽤到 ROUTE1 ⼦程序 BEQ LOOP ;条件跳转到 LOOP 标号处
…
LOOP MOV R2,#2
…
ROUTE1
…
指令集介绍
ARM 指令集
指令格式
基本格式
<opcode>{<cond>}{S} <Rd>,<Rn>{,<opcode2>} 其中,<>内的项是必须的,{}内的项是可选的,如<opcode>是指令助记符,是必须
的,⽽{<cond>}为指令执⾏条件,是可选的,如果不写则使⽤默认条件 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 个操作数
在 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
MOVPC,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 指令。
条件码
使⽤指令条件码,可实现⾼效的逻辑操作,提⾼代码效率。条件码表
条件码助记符标志含义
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 指令具有条件码执⾏功能,此指令条件码同表 2.1,但
如果为⽆条件执⾏时,条件码助记符“AL”不能在指令中书写。条件码应⽤举例如下:
⽐较两个值⼤⼩,并进⾏相应加 1 处理,C 代码为
if(a>b)a++;
el 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
ARM 存储器访问指令
ARM 处理是加载/存储体系结构的典型的 RISC 处理器,对存储器的访问只能使⽤加载和存储指令实现。ARM 的加载/存储指令是可以实现字、半字、,⽆符/有符字节操作;批量加载/存储指令可实现⼀条指令加载/存储多个寄存器的内容,⼤⼤提⾼效率;SWP 指令是⼀条寄存器和存储器内容交换的指令,可⽤于信号量操作等。ARM 处理器是冯. 诺依曼存储结构,程序空间、RAM 空间及 IO 映射空间统⼀编址,除对对 RAM 操作以外,对外围 IO、程序数据的访问均要通过加载/存储指令进⾏。
ARM 存储访问指令表
助记符说明操作条件码位置LDR Rd,addressing加载字数据Rd←[addressing],addressing
索引
LDR{cond}
LDRB
Rd,addressing 加载⽆符字节数据Rd←[addressing],addressing
索引
LDR{cond}B
LDRT以⽤户模式加载字数Rd←[addressing],addressing LDR{cond}T
LDRT
Rd,addressing 以⽤户模式加载字数
据
Rd←[addressing],addressing
索引
LDR{cond}T
LDRBT
Rd,addressing 以⽤户模式加载⽆符
号字数据
Rd←[addressing],addressing
索引
LDR{cond}BT
LDRH
Rd,addressing 加载⽆符半字数据Rd←[addressing],addressing
索引
LDR{cond}H
LDRSB
Rd,addressing 加载有符字节数据Rd←[addressing],addressing
索引
LDR{cond}SB
LDRSH
Rd,addressing 加载有符半字数据Rd←[addressing],addressing
索引
LDR{cond}SH
STR
Rd,addressing 存储字数据[addressing]←Rd,addressing
索引
STR{cond}
STRB
Rd,addressing 存储字节数据[addressing]←Rd,addressing
索引
STR{cond}B
STRT
Rd,addressing 以⽤户模式存储字数
据
[addressing]←Rd,addressing
索引
STR{cond}T
SRTBT
Rd,addressing 以⽤户模式存储字节
数据
[addressing]←Rd,addressing
索引
STR{cond}BT
STRH
Rd,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}
SWP Rd,Rm,Rn寄存器和存储器字数
据交换Rd←[Rd],[Rn]←[Rm](Rn≠Rd
或 Rm)
SWP{cond}
SWPB Rd,Rm,Rn寄存器和存储器字节
数据交换Rd←[Rd],[Rn]←[Rm](Rn≠Rd
或 Rm)
SWP{cond}B
X 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