指令概述
ARM指令
STR与LDR概述(详细在本文的数据加载与存储指令中)
数据操作指令
数据操作指令是指对存放在寄存器中的数据进行操作的指令。主要包括数据传送指令、算术指令、逻辑指令、比较与测试指令及乘法指令。如果在数据处理指令后使用“S”后缀,指令的执行结果将会影响CPSR中的标志位。数据处理指令的基本语法格式如下:
< opcode > {< cond >} {S} < Rd >,< Rn >,< shifter_operand >
指令的可选后缀
S后缀
!后缀
即该指令相当于
指令的条件执行
条件码
两种后缀同时存在时,S排后面
其他后缀
(1)字长后缀
(2)索引后缀和堆栈后缀
T后缀
cortex-M3指令
“统一汇编语言(UAL)”语法机制
解析:
LDR命令,将存储器地址所指地址处连续的4个字节(1个字)的数据传送到目的寄存器中。内存中的值是不会改变的。 故C
ARM寻址方式
所谓寻址,就是要找存放某个东西的位置
数据处理指令寻址方式可以分为以下几种:
- (1)立即数寻址方式。
- (2)寄存器寻址方式。
- (3)寄存器移位寻址方式。
立即寻址:就是在让你做事的时候,同时把你要用的东西也给你,也是不用你忙活着去找。
指令中的立即数是由一个8bit的常数移动4bit偶数位(0,2,4,…,26,28,30)得到的,所以,每一条指令都包含一个8bit的常数X和移位值Y,得到的立即数=X循环右移(2×Y)
下面列举了一些有效的立即数:
0xFF、0x104、0xFF0、0xFF00、0xFF000、0xFF000000、0xF000000F。
下面是一些无效的立即数:
0x101、0x102、0xFF1、0xFF04、0xFF003、0xFFFFFFFF、0xF000001F。
在以下两条指令中,第二个源操作数即为立即数,要求以“#”为前缀,对于以十六进制表示的立即数,还要求在“#”后加上“0x”或“&”。
立即数生成方式(循环左移右移)
直接寻址:就是告诉你储物柜的号码,你自己去该储物柜里把东西拿出来用。
mov 寄存器,[偏移地址]
mov [偏移地址],寄存器
寄存器寻址:就是有几个固定的门房收发室,你找门房问,就能告诉你储物柜的号码,然后就能从储物柜拿到东西。
寄存器的值可以被直接用于数据操作指令,这种寻址方式是各类处理器经常采用的方式,利用寄存器中的数值作为操作数,也是一种执行效率较高的寻址方式。例如以下指令:
寄存器间接寻址:还是去找门房,问到储物柜号码,然后去打开储物柜一看,里面是个纸条,纸条上说东西在另一个储物柜,号码是XXX。
寄存器间接寻址就是以寄存器中的值作为操作数的地址,而操作数本身存放在存储器中。例如以下指令:
在第一条指令中,以寄存器R2的值作为操作数的地址,在存储器中取得一个操作数后与R1相加,结果存入寄存器R0中。
第二条指令将以R1的值为地址的存储器中的数据传送到R0中。
第三条指令将R0的值传送到以R1的值为地址的存储器中。
寄存器移位寻址方式
寄存器的值在被送到ALU之前,可以事先经过桶形移位寄存器的处理。预处理和移位发生在同一周期内,所以有效地使用移位寄存器,可以增加代码的执行效率。下面是一些在指令中使用了移位操作的例子:
Rm,shift—寄存器移位方式
将寄存器的移位结果作为操作数(移位操作不消 耗额外的时间),但Rm值保持不变。
逻辑右移和算术右移的区别:
基址变址寻址
基址变址寻址就是将寄存器(该寄存器一般称为基址寄存器)的内容与指令中给出的地址偏移量相加,从而得到一个操作数的有效地址。变址寻址方式常用于访问某基地址附近的地址单元。采用变址寻址方式的指令常见有以下几种形式,如下指令所示:
重要!!!
在第一条指令中,将寄存器R1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器R0中。
在第二条指令中,将寄存器R1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器R0中,然后,R1的内容自增4个字节。
在第三条指令中,以寄存器R1的内容作为操作数的有效地址,从而取得操作数存入寄存器R0中,然后,R1的内容自增4个字节。
在第四条指令中,将寄存器R1的内容加上寄存器R2的内容形成操作数的有效地址,从而取得操作数存入寄存器R0中。
多寄存器寻址
采用多寄存器寻址方式,一条指令可以完成多个寄存器值的传送。这种寻址方式可以用一条指令完成传送最多16个通用寄存器的值。例如以下指令:
该指令的后缀IA表示在每次执行完加载/存储操作后,R0按字长度增加,因此,指令可将连续存储单元的值传送到R1~R4。
多寄存器寻址一次可传送几个寄存器值,该寻址方 式中一条指令最多可传送16个通用寄存器的值。连 续的寄存器间用“-”连接,否则用“,”分隔
相对寻址
与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。以下程序段完成子程序的调用和返回,跳转指令BL采用了相对寻址方式:
堆栈寻址
Cortex-M3支持的Thumb-2指令集
数据处理指令
数据传送指令
MOV指令
MVN指令
MVN是反相传送(Move Negative)指令,它将操作数的反码传送到目的寄存器,多用于向寄存器传送一个负数或生成位掩码。
移位指令(包括5种)
1.LSL逻辑左移(无符号)
例题(第二题是进位)
分析:
R1 = 0x80000004,而LSL #1就是左移一位,那么把R1由十六进制数转换为二进制数,即R1 = 1000 … 0100,左移一位,则R1 = 0000 … 1000,所以R0 = 0x00000008,R1保持不变
2.LSR逻辑右移(无符号)
例题
3.ASR算术右移
4.ROR循环右移
算术指令
1. ADD 加法指令
例题
2. ADC带进位加法指令
3. SUB减法指令
4. SBC 带借位减法指令
5. RSB 逆向减法指令
逻辑运算指令
1. AND 逻辑与指令
2. ORR 逻辑或指令
3. EOR 逻辑异或指令
4. BIC 位清除指令
BIC(Bit Clear)位清零指令将寄存器Rn的值与第二个源操作数shifter_operand的值的反码按位做逻辑与操作,并将结果保存到目标寄存器Rd中。
比较指令
1. CMP比较指令
2. CMN反值比较指令
3. TST位测试指令
与CMP指令一样,该指令不需要指定S后缀。
4. TEQ相等测试指令
使用TEQ进行相等测试,常与EQ和NE条件码配合使用,当两个数据相等时,条件码EQ有效;否则条件码NE有效。
乘法和乘加指令
注意事项
1. MUL 32位乘法指令
2. MLA 32位乘加指令
3. SMULL 64位有符号数乘法指令
4. SMLAL 64位有符号数乘加指令
5. UMULL 64位无符号数乘法指令
6. UMLAL 64位无符号数乘加指令
乘法指令–注意事项
数据加载与存储指令
ARM处理器对存储器的访问只能通过加载和存储指令。
1. 数据加载与存储的方向
2. 数据加载/存储指令的寻址(LDR和STR)
LDR指令
LDR指令用于从指定地址的内存中读取一个32位的字到目标寄存器。
STR指令
STR指令用于将一个32位的字数据写入到指令中指定的内存单元。
3. 地址索引(前索引、自动索引、后索引)
不同索引方式的区别
单数据访存指令
单寄存器加载与存储指令
例题
【注意事项】
多寄存器加载与存储指令
例题★
堆栈操作
堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用一个称为堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
ARM微处理器所支持的批量数据加载/存储指令可以一次在一片连续的存储器单元和多个寄存器之间传送数据,批量加载指令用于将一片连续的存储器中的数据传送到多个寄存器,批量数据存储指令则完成相反的操作。
LDM(或STM)指令的格式为:
LDM(或STM)指令用于从基址寄存器所指示的一片连续存储器到寄存器列表所指示的多个寄存器之间传送数据,该指令的常见用途是将多个寄存器的内容入栈或出栈。
堆栈的操作包括: 建栈、进栈、出栈三种基本操作。
(1)建栈
(2)进栈
当堆栈指针指向最后压入堆栈的数据时,称为满堆栈(Full Stack),而当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈(Empty Stack)。
同时,根据堆栈的生成方式,又可分为递增堆栈(Ascending Stack)和递减堆栈(Decending Stack),当堆栈由低地址向高地址生成时,称为递增堆栈,当堆栈由高地址向低地址生成时,称为递减堆栈。
这样就有4种类型的堆栈工作方式,ARM微处理器是支持基于堆栈操作的,其Rn寄存器为R13(SP)栈指针寄存器4种类型的堆栈工作方式,即:
堆栈寻址(满递增堆栈、满递减堆栈、空递增堆栈、空递减堆栈)
- 满递增堆栈(FA):堆栈指针指向最后压入的数据,且由低地址向高地址生成。
- 满递减堆栈(FD):堆栈指针指向最后压入的数据,且由高地址向低地址生成。
- 空递增堆栈(EA):堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生成。
- 空递减堆栈(ED):堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生成。
STMFD SP!,{R0-R7,LR}
先是sp = sp - 4,然后LR进栈,然后sp = sp - 8,R7进栈,接下来依次类推
提示
{!}为可选后缀,若选用该后缀,则当数据传送完毕之后,将最后的地址写入基址寄存器,否则基址寄存器的内容不改变。
基址寄存器不允许为R15,寄存器列表可以为R0~R15的任意组合。{∧}为可选后缀,当指令为LDM且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据传送之外,还将SPSR复制到CPSR。同时,该后缀还表示传入或传出的是用户模式下的寄存器,而不是当前模式下的寄存器。
STMFD (Push) 块存储- Full Descending stack [STMDB]
LDMFD (Pop) 块装载- Full Descending stack [LDMIA]
(3)出栈
例题★
小案例:
分支指令(B、BL、BX、BLX)
分支指令B(B、BEQ、NE)
带返回的分支指令BL
BL是另一个跳转指令,但在跳转之前,会在寄存器R14中保存PC的当前内容,因此,可以通过将R14的内容重新加载到PC中,来返回到跳转指令之后的那个指令处执行。该指令是实现子程序调用的一个基本但常用的手段。
例题
顺序为:ACDEGHIFBJ
带返回和状态切换的分支指令BLX
BLX指令从ARM指令集跳转到指令中所指定的目标地址,并将处理器的工作状态由ARM状态切换到Thumb状态,该指令同时将PC的当前内容保存到寄存器R14中。因此,当子程序使用Thumb指令集,而调用者使用ARM指令集时,可以通过BLX指令实现子程序的调用和处理器工作状态的切换。同时,子程序的返回可以通过将寄存器R14值复制到PC中来完成。
程序状态寄存器访问指令
1、状态寄存器到通用寄存器的传送指令(MRS)
2、通用寄存器到状态寄存器的传送指令(MSR)
例题
课后测试一
1、
解析:
由于ADD没有S,所以CPSR的Z不变,Z = 0,
R4 = R3 + 1 = 7
故答案为7;0
2、
因为Z = 1,所以数为0,执行ADDEQ R4,R3, #1
所以R4 = R3 + 1 = 7
故答案为7;1
3、
R4 = R3 + 1 = 0
Z = 1
故答案为0;1
4、
R4 = R0 + R2 = -1 + 4 = 3
Z = 0
故答案为3;0
课后测试二
1、
解析:
(1)LDR R3,[R4,#8]
前索引,寄存器间接寻址,所以R3 = [0x2008] = 0x55667788,R4 = 0x2000
(2)LDR R3,[R4,#8]!
自动索引,因为有!后缀,所以基址寄存器的地址发生变化,R3 = 0x55667788,R4 = 0x2008
(3)LDR R3,[R4],#8
后索引,所以R3 = 0x11223344,R4 = 0x2008
2、
解析:
(1)
BD
(2)
R2左移2位,我们先将R2的十六进制转化为二进制,0001左移后变为0100(就针对1那个值看,不然我前面要写好多个0,给你们看也不够直观),所以R2 = 0x0004
而这个加法指令对应第二种格式,所以R1 = 0x1000 + 0x0004 = 0x1004
3、
答案:BE
4、
解析:
(1)
LDMIA为出栈,所以R2 = [原R1(0x2000)] = 0x11
R3 = [原R1 + 4] = 0x22
R1 = 原R1(0x2000) + 8 = 0x2008
(2)
STMIA为进栈,R2和R3依次进栈,所以R2和R3不变,分别为R2 = 0x3000,R3 = 0x3004,R1 = 0x2000 + 4 * 2 = 0x2008,
乘法指令
1、
解析:
目的寄存器Rd和操作数Rm必须是不同的寄存器
故选CD
2、
解析:
Rs必须是通用寄存器,而不是立即数
故选E
3、
答案F
4、
解析:
两种后缀同时存在时,S排后面
故选B
5、
解析:
因为Rdlo、Rdhi和Rm必须使用不同的寄存器
故选DE
6、
故选F
7、
解析:
因为不能对操作数使用立即数
故选E
指令系统测试
1、
解析:
MOV R8, #0x05
//R8 = 0x05
MOV R5,#0x08
//R5 = 0x08
CMP R8,#10
//R8 - 10 < 0所以执行ADDMI语句
//R6 = R5 + 0x07 = 0x0F
MOVS R3,#0
//R3 = 0,且Z = 1
MOV R2,#0x20
//R2 = 0x20
SUB R3,R2,R8
//R3 = R2 - R8 = 0x20 - 0x05 = 0x1B
//因为Z = 1,所以执行ADDEQ语句
R3 = R2 + R5 = 0x20 + 0x08 = 0x28
故答案为0x05,0x0F,0x1B,0x28
2、
解析:
MOV R1,#0x08
MOV R3,#0x10
//R1 = 0x08,R3 = 0x10
CMP R1,#10
//R1 - 10 < 0所以执行SUBMI指令
R3 = R3 - 0x07 = 0x09
MOVS R0,#0
//R0 = 0,Z = 1
MOV R4,#0x20
//R4 = 0x20
SUB R3,R4,R1
//R3 = R4 - R1 = 0x20 - 0x08 = 0x18
//因为Z = 1,所以ADDNE不执行
故答案为0x08;0x09;0x18;0x0