1.arm体系结构的版本(了解)
ARM指令集体系结构,从最初开发至今已有了重大改进,而且将会不断完善和发展。
为了精确表达每个ARM实现中所使用的指令集,到目前ARM体系结构共定义了8个版本,以版本号v1~v8表示,各版本特点如下。
(1)版本1(v1)
该版本包括:
- 基本数据处理指令(不包括乘法)。
- 字节、字以及半字加载/存储指令。
- 分支(branch)指令,包括用于子程序调用的分支与链接(branch-and-link)指令。
- 软件中断指令,用于进行操作系统调用。
- 26位地址总线。
(2)版本2(v2)
与版本1相比,版本2增加了下列指令:
- 乘法和乘加指令(multiply & multiply-accumulate)。
- 支持协处理器。
- 原子性(atomic)加载/存储指令SWP和SWPB(稍后的版本称v2a)。
- FIQ中的两个以上的分组寄存器。
(3)版本3(v3)
版本3较以前的版本发生了大的变化,具体改进如下:
- 推出32位寻址能力。
- 分开的CPSR(current program status register,当前程序状态寄存器)和SPSR(saved program status register,备份的程序状态寄存器),当异常发生时,SPSR用于保存CPSR的当前值,从异常退出时则可由SPSR来恢复CPSR。
- 增加了两种异常模式,使操作系统代码可方便地使用数据访问中止异常、指令预取中止异常和未定义指令异常。
- 增加了MRS指令和MSR指令,用于完成对CPSR和SPSR寄存器的读/写;修改了原来的从异常中返回的指令。
(4)版本4(v4)
版本4在版本3的基础上增加了如下内容:
- 有符号、无符号的半字和有符号字节的load和store指令。
- 增加了T变种,处理器可工作于Thumb状态,在该状态下,指令集是16位压缩指令集(Thumb指令集)。
- 增加了处理器的特权模式。在该模式下,使用的是用户模式下的寄存器。
另外,在版本4中还清楚地指明了哪些指令会引起未定义指令异常。版本4不再强制要求与以前的26位地址空间兼容。
(5)版本5(v5)
与版本4相比,版本5增加或修改了下列指令:
- 提高了T变种中ARM/Thumb指令混合使用的效率。
- 增加了前导零计数(CLZ)指令。
- 增加了BKPT(软件断点)指令。
- 为支持协处理器设计提供了更多的可选择的指令。
- 更加严格地定义了乘法指令对条件标志位的影响。
(6)版本6(v6)
ARM体系版本6是2001年发布的。该版本在降低耗电的同时,还强化了图形处理性能。
通过追加有效多媒体处理的SIMD(single instruction multiple datastream,单指令流,多数据流)功能,将语音及图像的处理功能提高到了原机型的4倍。
ARM体系版本6首先在2002年春季发布的ARM11处理器中使用。
除此之外,v6还支持多微处理器内核。
(7)版本7 (v7)
ARMv7架构是在ARMv6架构的基础上诞生的。该架构采用了Thumb-2技术,Thumb-2技术是在ARM的Thumb代码压缩技术的基础上发展起来的,并且保持了对现存ARM解决方案的完整的代码兼容性。
Thumb-2技术比纯32位代码少使用 31%的内存,减小了系统开销。
同时能够提供比已有的基于Thumb技术的解决方案高出38%的性能。
ARMv7架构还采用了NEON技术,将DSP和媒体处理能力提高了近4倍,并支持改良的浮点运算,满足下一代3D图形、游戏物理应用以及传统嵌入式控制应用的需求。
此外,ARMv7还支持改良的运行环境,以迎合不断增加的JIT(Just In Time)和DAC(DynamicAdaptive Compilation)技术的使用。
另外,ARMv7架构对于早期的ARM处理器软件也提供很好的兼容性。
ARMv7架构定义了三大分工明确的系列:“A”系列面向尖端的基于虚拟内存的操作系统和用户应用;“R”系列针对实时系统;“M”系列对微控制器和低成本应用提供优化。
(8)版本8(v8)
ARMv8-A 将 64 位架构支持引入 ARM 架构中,其中包括:
- 64 位通用寄存器、SP(堆栈指针)和 PC(程序计数器)
- 64 位数据处理和扩展的虚拟寻址
两种主要执行状态:
- AArch64 - 64 位执行状态,包括该状态的异常模型、内存模型、程序员模型和指令集支持
- AArch32 — 32 位执行状态,包括该状态的异常模型、内存模型、程序员模型和指令集支持
这些执行状态支持三个主要指令集:
- A32(或 ARM):32 位固定长度指令集,通过不同架构变体增强部分 32 位架构执行环境现在称为 AArch32。
- T32 (Thumb) 是以 16 位固定长度指令集的形式引入的,随后在引入 Thumb-2 技术时增强为 16 位和 32 位混合长度指令集。
部分 32 位架构执行环境现在称为 AArch32。 - A64:提供与 ARM 和 Thumb 指令集类似功能的 32 位固定长度指令集。随 ARMv8-A 一起引入,它是一种 AArch64 指令集。
2.ARM内核版本命名规则
ARM内核命名时以数字表示内核的版本号,以字母表示内核所支持的额外功能。
规则如下:
ARM{x}{y}{z}{T}{D}{M}{I}{E}{J}{F}{-S}
大括号内的字母是可选的,各个字母的含义如下。
x — 系列号,如ARM7中的“7”、ARM9中的“9”。
y — 内部存储管理/保护单元,如ARM72中的“2”、ARM94中的“4”。
z — 内含有高速缓存(Cache)。
T — 支持16位的Thumb指令集。
D — 支持JTAG片上调试。
M — 支持用于长乘法操作(64位结果)的ARM指令,包含快速乘法器。
I — 带有嵌入式追踪宏单元ETM(Embedded Trace Macro),用来设置断点和观察点的调试硬件。
E — 增强型DSP指令(基于TDMI)。
J — 含有Java加速器Jazelle,与Java虚拟机相比,Jazelle使Java代码运行速度提高了8倍,功耗降低到原来的80%。
F — 向量浮点单元。
S — 可综合版本,意味着处理器内核是以源代码形式提供的。这种源代码形式又可以被编译成一种易于EDA工具使用的形式。
3.ARM体系架构分类
(1)复杂指令集和精简指令集
CISC架构采用庞大的指令集,可以减少编程所需要的代码行数,减轻程式师的负担,RISC采用精简指令集,包含了简单、基本的指令,透过这些简单、基本的指令,就可以组合成复杂指令,
(2)普林斯顿结构和哈佛结构
普林斯顿结构也称冯·诺伊曼结构,是一种将程序指令存储器和数据存储器合并在一起的存储器结构。哈佛结构是一种将程序指令存储和数据存储分开的存储器结构。
4.流水线技术
(1)三级流水线
取指→译码→执行
- 取指:将指令从存储器中取出,放入指令Cache中。
- 译码:由译码逻辑单元完成,是将在上一步指令Cache中的指令进行解释,告诉CPU将如何操作。
- 执行:这阶段包括移位操作、读通用寄存器内容、输出结果、写通用寄存器等。
(2)五级流水线技术
取指→译码→执行→存储器访问→寄存器回写
- 取指:从指令Cache中读取指令。
- 译码:对指令进行译码,识别出是对哪个寄存器进行操作并从通用寄存器中读取操作数。
- 执行:进行ALU运算和移位操作,如果是对存储器操作的指令,则在ALU中计算出要访问的存储器地址。
- 存储器访问:如果是对存储器访问的指令,用来实现数据缓冲功能(通过数据Cache)。
- 寄存器回写:将指令运算或操作结果写回到目标寄存器中。
注:ARM处理的工作状态
- ARM状态:执行字对齐的32位ARM指令
- Thumb状态:执行半字对齐的16位Thumb指令。在Thumb状态下,程序计数器PC使用位1选择另一个半字。
5.ARM处理器模式
ARM微处理器支持7种运行模式。
① 用户模式(USR):ARM处理器正常的程序执行状态。
② 快速中断模式(FIQ):用于高速数据传输或通道处理。
③ 外部中断模式(IRQ):用于通用的中断处理。
④ 管理模式(SVC):操作系统使用的保护模式。
⑤ 数据访问终止模式(ABT):当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护。
⑥ 系统模式(SYS):运行具有特权的操作系统任务。
⑦ 未定义指令中止模式(UND):当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真。
除了用户模式之外的其他6种处理器模式称为特权模式。
特权模式下,程序可以访问所有的系统资源,也可以任意地进行处理器模式的切换。
特权模式中,除系统模式外,其他5种模式又称为异常模式。
大多数的用户程序运行在用户模式下,此时,应用程序不能够访问一些受操作系统保护的系统资源,应用程序也不能直接进行处理器模式的切换。
用户模式下,当需要进行处理器模式切换时,应用程序可以产生异常处理,在异常处理中进行处理器模式的切换。
6.寄存器
ARM微处理器共有37个32位寄存器,其中31个为通用寄存器,6个为状态寄存器。但是这些寄存器不能被同时访问,具体哪些寄存器是可以访问的,取决ARM处理器的工作状态及具体的运行模式。但在任何时候,通用寄存器R14~R0、程序计数器PC、一个状态寄存器都是可访问的。
各处理器模式下可见的寄存器情况,
(1)通用寄存器
R0~R15
R13_svc、R14_svc
R13_abt、R14_abt
R13_und、R14_und
R13_irq、R14_irq
R8_fiq~R14_fiq
通用寄存器可以分为下面3类:未分组寄存器(The unbanked registers),包括R0~R7。分组寄存器(The banked registers),包括R8~R14。程序计数器PC,即R15。
①未分组寄存器
R0-R7是不分组寄存器。这意味着在所有处理器模式下,访问的都是同一个物理寄存器。不分组寄存器没有被系统用于特别的用途,任何可采用通用寄存器的应用场合都可以使用未分组寄存器。
②分组寄存器
对于分组寄存器R8~R12来说,每个寄存器对应两个不同的物理寄存器。
例如,当使用fiq模式时,访问寄存器R8_fiq~R12_fiq;当使用除fiq模式以外的其他模式时,访问寄存器R8_usr~R12_usr。
对于R13、R14来说,每个寄存器对应6个不同的物理寄存器,其中的一个是用户模式与系统模式共用,另外5个物理寄存器对应于其他5种不同的运行模式。
采用以下的记号来区分不同的物理寄存器:
R13_、R14_,可以是下面几种模式之一:usr、svc、abt、und、irq及fiq。
寄存器R13在ARM中常用作栈指针。用户也可以使用其他的寄存器作为栈指针;而在Thumb指令集中,有一些指令强制性地使用R13作为栈指针。
每一种异常模式拥有自己的物理的R13。应用程序初始化该R13,使其指向该异常模式专用的栈地址。当进入异常模式时,可以将需要使用的寄存器保存在R13所指的栈中;当退出异常处理程序时,将保存在R13所指的栈中的寄存器值弹出。这样就使异常处理程序不会破坏被其中断程序的运行现场。
寄存器R14又被称为连接寄存器(Link Register,LR),在ARM体系中具有下面两种特殊的作用:每一种处理器模式自己的物理R14中存放在当前子程序的返回地址。
当通过BL或BLX指令调用子程序时,R14被设置成该子程序的返回地址。在子程序中,当把R14的值复制到程序计数器PC中时,子程序即返回。
当异常中断发生时,该异常模式特定的物理R14被设置成该异常模式将要返回的地址,对于有些异常模式,R14的值可能与将返回的地址有一个常数的偏移量。
具体的返回方式与上面的子程序返回方式基本相同。
R14寄存器也可以作为通用寄存器使用。
注: R13用于保存堆栈指针,它始终指向栈的顶部;R14用于保存程序的返回地址,保存的是要返回程序的下一条指令。
③程序计数器PC
寄存器R15被用作程序计数器,也称为PC。其值等于当前正在执行的指令的地址+8(因为在取地址和执行之间多了一个译码的阶段)。
在正常操作过程中,在执行一条指令的同时对下一条指令进行译码,并将第三条指令从存储器中取出。
处理器处于ARM状态时,每条指令为4个字节,所以PC值为正在执行的指令地址加8字节,即是:PC值 = 当前程序执行位置 + 8字节
处理器处于Thumb状态时,每条指令为2字节,所以PC值为正在执行的指令地址加4字节,即是:PC值 = 当前程序执行位置 + 4字节
(2)状态寄存器
CPSR、SPSR_svc、SPSR_abt、SPSR_und、SPSR_irq、SPSR_fiq
ARM所有工作模式下都可以访问程序状态寄存器CPSR。CPSR包含条件码标志、中断禁止位、当前处理器模式以及其他状态和控制信息。
CPSR在每个异常模式下都有一个对应的物理寄存器——程序状态保存寄存器SPSR。当异常出现时,SPSR用于保存CPSR的值,以便异常返回后恢复异常发生时的工作状态。
①条件码标志
N、Z、C、V,最高4位称为条件码标志。ARM的大多数指令可以条件执行的,即通过检测这些条件码标志来决定程序指令如何执行。
各个条件码的含义如下:
- N:在结果是有符号的二进制补码情况下,如果结果为负数,则N=1;如果结果为非负数,则N=0。
- Z:如果结果为0,则Z=1;如果结果为非零,则Z=0。
- C:其设置分一下几种情况:
- 对于加法指令(包含比较指令CMN),如果产生进位(无符号数溢出),则C=1;否则C=0。
- 对于减法指令(包括比较指令CMP),如果产生借位(无符号数溢出),则C=0;否则C=1。
- 对于有移位操作的非加/减法运算指令,C为移位操作中最后移出位的值。
- 对于其他非加/减法运算指令,C通常不变。
- V:对于加减法指令,在操作数和结果是有符号的整数时,如果发生溢出,则V=1;如果无溢出发生,则V=0;对于其他指令,V通常不发生变化。
补:Q:在ARM V5及以上版本的E系列处理器中,用Q标志位指示增强的DSP运算指令是否发生了溢出。在其它版本的处理器中,Q标志位无定义
②控制位的作用在上图中可以看出。
③保留位
SPSR中的其余位为保留位,当改变SPSR中的条件码标志位或者控制位时,保留位不要被改变,在程序中也不要使用保留位来存储数据。
保留位将用于ARM版本的扩展。
SPSR_c、SPSR_f分别指CPSR低8位(控制位)和高4位(标志位)
7.异常中断
当异常中断发生时,系统执行完当前指令后,将跳转到相应的异常中断处理程序处执行。当异常中断处理程序执行完成后,程序返回到发生中断指令的下条指令处执行。
在进入异常中断处理程序时,要保存被中断程序的执行现场,从异常中断处理程序退出时,要恢复被中断程序的执行现场。
(1)引起异常的原因
指令执行引起的异常:
软件中断、未定义指令(包括所要求的协处理器不存在对于协处理器指令)、预取址中止(存储器故障)、数据中止。
外部产生的中断:
复位、FIQ、IRQ。
(2)异常类型
补充:
异常 地址 优先级 I位(7位) F位(6位) 进入模式
复位 0x0000,0000 1 1 1 管理模式
未定义 0x0000,0004 6 1 - 未定义模式
软件中断 0x0000,0008 6 1 - 管理模式
中止(预取指令) 0x0000,000C 5 1 - 中止模式
中止(数据) 0x0000,0010 2 1 - 中止模式
保留 0x0000,0014 保留模式
IRQ 0x0000,0018 4 1 - IRQ
FIQ 0x0000,001C 3 1 1 FIQ
从上表还可以看出,cpsr的I位和F位可以屏蔽或使能某些异常。
复位程序具有最高的优先级,是系统启动(或复位)时调用的程序。
- 它应该对异常处理程序的系统进行初始化(包括配置存储器和cache)。
- 保证在IRQ和FIQ中断允许之前初始化外部的中断源,避免在还没有设置好相应的处理程序前产生中断。
- ③设置好各种处理器模式的堆栈指针。
数据中止异常指示访问了无效的存储器地址,或者当前代码没有正确的数据访问权限。它不屏蔽FIQ,所以在处理过程中可以处理IRQ。
FIQ中断的优先级比IRQ中断的优先级要高,且内核进入FIQ处理程序时,把FIQ和IRQ都禁止了,因此,任何外部中断源都不能被处理,如果要实现嵌套或者优先级时就要对上下文进行适当处理然后用软件使能中断。同样,要实现IRQ的嵌套,也要相应的上下文操作和软件开中断,但IRQ不能屏蔽FIQ请求。当IRQ中断请求出现时,只有当FIQ异常和数据中止异常都没有发生时,IRQ处理程序才被调用,并屏蔽IRQ中断,直到中断源被清除。
预取指中断会屏蔽IRQ异常,但不影响FIQ。
当上述的所有异常都没有发生,此时系统可以处理SWI异常,cpsr会被置成管理模式。如果要实现SWI的嵌套,也要保存r14(lr)和spsr。
SWI和未定义指令异常共享同一优先级,所以两者不能同时出现。
(3)异常响应过程
除了复位异常外,当异常发生时,ARM处理器尽可能完成当前指令(除了复位异常)后,再去处理异常。并执行如下动作:
- 将引起异常指令的下一条指令的地址保存到新模式的R14中,若异常是从ARM状态进入,LR寄存器中保存的是下一条指令的地址
(当前PC+4或PC+8,与异常的类型有关);
若异常是从Thumb状态进入,则在LR寄存器中保存当前PC的偏移量,这样,异常处理程序就不需要确定异常是从何种状态进入的。
例如:在软件中断异常SWI,指令MOV PC,R14_svc总是返回到下一条指令,不管SWI是在ARM状态执行,还是在Thumb状态执行。 - 将CPSR的内容保存到要执行异常中断模式的SPSR中。(注意:如果通过程序修改CPSR进入异常模式,硬件将不会将CPSR保存到SPSR中)
- 设置CPSR相应的位进入相应的中断模式。
- 通过设置CPSR的第7位来禁止IRQ。如果异常为快速中断和复位。则还要设置CPSR的第6位来禁止快速中断。
- 给PC强制赋向量地址值。
ARM处理器内核会自动执行以上几步,程序计数器PC总是跳转到相应的固定地址。
如果异常发生时,处理器处于Thumb状态,则当异常向量地址加载入PC时,处理器自动切换到ARM状态,则异常处理返回时,自动切换到Thumb状态,即中断处理只在ARM状态下处理。
(4)异常中断处理返回
异常处理完毕之后,ARM微处理器会执行以下几步操作从异常返回:
- 将所有修改过的用户寄存器从处理程序的保护栈中恢复。
- 将SPSR复制回CPSR中,将连接寄存器LR的值减去相应的偏移量后送到PC中。
- 若在进入异常处理时设置了中断禁止位,要在此清除。
复位异常处理程序不需要返回。
8.ARM体系的存储系统
(1)地址空间
ARM体系结构将存储器看做是从零地址开始的字节的线性组合。从0字节到3字节放置第1个存储的字数据,从第4个字节到第7个字节放置第2个存储的字数据,依次排列。
作为32位的微处理器,ARM体系结构所支持的最大寻址空间为4GB(232字节)。当程序正常执行时,每执行一条ARM指令,当前指令计数器加4个字节;每执行一条Thumb指令,当前指令计数器加2个字节。
(2)存储器格式
ARM体系结构可以用两种方法存储字数据,称之为大端格式和小端格式。大端格式中字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。
小端格式与大端存储格式相反,在小端存储格式中,低地址中存放的是字数据的低字节,高地址存放的是字数据的高字节。
举一个例子,比如十六进制数字0x12345678在内存中的表示形式为:
①大端模式:
低地址 -----------------> 高地址
0x12 | 0x34 | 0x56 | 0x78
②小端模式:
低地址 -----------------> 高地址
0x78 | 0x56 | 0x34 | 0x12
(3)存储器访问对准
- 对齐的存储访问:字单元的地址是字对齐的(地址的低两位为0b00),半字单元的地址是半字对齐的(地址的最低为0b0)。
- 非对齐的存储器访问:
- 非对齐的指令预取操作:
当处理器处于ARM状态器件,如果写入到寄存器PC中的值是非字对齐的(低两位不为0b00),要么指令执行的结果不可预知,要么地址值中最低两位被忽略;
当处理器处于Thumb状态器件,如果写入到寄存器PC中的值是非半字对齐的(最低位不为0b0),要么指令执行的结果不可预知,要么的重地值中最低位被忽略. - 非对齐地址内存的访问操作
对于LOAD/STORE操作,系统定义了下面3种可能的结果。
- 执行结果不可预知。
- 忽略字单元地址低两位的值,即访问地址为字单元;忽略半字单元最低位的值,即访问地址为半字单元。这种忽略是由存储系统自动实现的。
- 在LDR和SWP指令中,对存储器访问忽略造成地址不对齐的低地址位,然后使用这些低地址位控制装载数据的循环。
9.ARM指令系统
ARM内核属于RISC结构,所以其指令集有着一些独特的特点:指令长度固定,指令格式的种类少,寻址方式简单。
需要特别指出的是,ARM处理器的指令集是加载/存储型的,也即指令集仅能处理寄存器中的数据,而且处理结果都要放回寄存器中,而对系统存储器的访问则需要通过专门的加载/存储指令来完成。
操作数可能在的位置:指令中;寄存器中,R0-R15,CPSR,SPSR;内存中。
(1)指令格式
①编码格式
②语法格式
<opcode> {<cond>} {S} <Rd>, <Rn>, <shift_op2>
③解释
●<>内的项是必须的,{}内的项是可选的
●<opcode>:操作码域,指令编码的助记符。
●{<cond>}:条件码域,指令允许执行的条件编码。
●{S}:条件码设置域。这是一个可选项,当在指令中设置{S}域时,指令执行的结果将会影响程序状态寄存器CPSR中相应的状态标志。
●<Rd>:目的操作数。ARM指令中的目的操作数总是一个寄存器。如果<Rd>与第1操作数寄存器<Rn>相同,也必须要指明,不能默认。
●<Rn>:第1操作数。ARM指令中的第1操作数也必须是个寄存器。
●<shift_op2>:第2操作数。在第2操作数中可以是寄存器、内存存储单元或者立即数。
由于第2操作数只有12个bit,用第2操作数表示立即数时,其取值范围为0~2^{12}−1,要表示超出这个范围的立即数,通常要依靠伪指令实现。
ARM指令的一个重要特点是可以条件执行,每条ARM指令的条件码域包含4位条件码,共16种。几乎所有指令均根据CPSR中条件码的状态和指令条件码域的设置有条件地执行。
当指令执行条件满足时,指令被执行,否则被忽略。指令条件码及其助记符后缀表示
每种条件码可用两个字符表示,这两个字符可以作为后缀添加在指令助记符的后面和指令同时使用。
(2)寻址方式
①立即寻址
立即寻址也叫立即数寻址,操作数本身就在指令中给出,只要取出指令也就取到了操作数。这个操作数被称为立即数,对应的寻址方式也就叫做立即寻址。
如:ADD R0,R0,#1 ;R3←R0+1
MOV R0,#255 ;R0 <- #255,#0~#255都是立即数,且立即数是8位
立即数的表示以“#”为前缀,十六进制的立即数在“#”后面加“&”符号,以二进制表示的立即数,要求在“#”后加上“%”。
注: 如果立即数大于255,那么它必须能由0与255(包含0和255)之间的某个数经过循环右移偶数次得到
②寄存器寻址
指令地址码给出寄存器的编号,寄存器中的内容为操作数,这种寻址方式是各类微处理器经常采用的一种方式,也是一种执行效率较高的寻址方式。
如:ADD R0,R1,R2 ;R0←R1+R2
写操作数的顺序为:第1个寄存器R0为结果寄存器,第2个寄存器R1为第1操作数寄存器,第3个寄存器R2为第2操作数寄存器。
③寄存器间接寻址
以寄存器中的值作为操作数的地址,而操作数本身存放在存储器中。
如:LDR R0,[R1] ;R0←[R1] 将以R1的值为地址的存储器中的数据传送到R0中。
STR R0,[R1] ;[R1]←R0 将R0的值传送到以R1的值为地址的存储器中。
注:LDR R0, 0x12345678 ;把0x12345678这个地址中的值存放到R0中。
LDR R0, =0x12345678 ;把0x12345678这个地址写到R0中了。
④基址变址寻址
将寄存器(该寄存器一般称作基址寄存器)的内容与指令中给出的地址偏移量相加,从而得到一个操作数的有效地址。
可以将寄存器间接寻址看做是位移量为0的基址加偏移量寻址。
如:LDR R0,[R1,#4] ;取R1的内容当作主存的地址,在此基础上+4byte,从该地址处取操作数。
LDR R0,[R1,#4]! ;同上,操作完毕后,!表示指令执行完毕把最后的数据地址写到R1,即R1原来的地址+4
LDR R0,[R1,R2] ;将寄存器R1的内容加上寄存器R2的内容形成操作数的地址,取得的操作数存入寄存器R0中。
STR R0,[R1,#-4] ;将R1中的数值减4作为地址,把R0中的数据存放到这个地址中。
LDR R0,[R1],#4 ;把R1指向的数据放到R0中,操作完成后[R1]自增4byte
STR R0,[R1],#8 ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。
⑤多寄存器寻址
多寄存器寻址是指一次可以传送多个寄存器的值,允许一条指令可以传送16个寄存器的任何子集。
- 操作数存放到内存单元中;
- 指令地址码字段给出{寄存器编号(名)列表};
- 编号高的寄存器总是对应内存中的高地址单元;
- 可完成存储块和16个寄存器或其子集之间的数据传送。
如:LDMIA R0,{R1,R2,R3,R4} ;R1←[R0]
;R2←[R0+4]
;R3←[R0+8]
;R4←[R0+12]
多寄存器指令的后缀含义如下:
I:Increment
D:Decrement
A:After
B:Before
该指令的后缀IA表示在每次执行完加载/存储操作后,R0按字长度增加,因此,指令可将连续存储单元的值传送到R1~R4。
多个连续的寄存器可以用“-”符号连接;不连续的寄存器用“,”分隔书写,如上例可写成:
LDMIA R0,{R1-R4}
LDMIA R0,{R1-R3,R4}
⑥寄存器移位寻址
将寄存器的值进行移位,再将移位后的数据当作操作数
如:MOV R0,R2,LSL #1 ;R2的值左移1位,结果赋给R0。
MOV R0,R2,LSL R1 ;R2的值左移R1位,结果放入R0。
有6种移位操作:
- LSL:逻辑左移(Logical Shift Left),寄存器中字的低端空出的位补0。
- LSR:逻辑右移(Logical Shift Right),寄存器中字的高端空出的位补0。
- ASL:算术左移(Arithmetic Shift Left),和逻辑左移LSL相同。
- ASR:算术右移(Arithmetic Shift Right),移位过程中符号位不变,即如果源操作数是正数,则字的高端空出的位补0,否则补1。
- ROR:循环右移(Rotate Right),由字的低端移出的位填入字的高端空出的位。
- RRX:带扩展的循环右移(Rotate Right eXtended),操作数右移一位,高端空出的位用进位标志C的值来填充,低端移出的位填入进位标志位。
⑦相对寻址
以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。
如:
BL LOOP ;跳转到子程序LOOP处执行
……
LOOP
……
MOV PC,LR ;从子程序返回
⑧堆栈寻址(以下介绍是以存取一个字节的数为例)
堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用一个称为堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
当堆栈指针指向最后压入堆栈的数据时,称为满堆栈(Full Stack),而当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈(Empty Stack)。
同时,当堆栈由低地址向高地址生成时,称为递增堆栈(Ascending Stack),当堆栈由高地址向低地址生成时,称为递减堆栈(Decending Stack)。
这样就有4种类型的堆栈工作方式,ARM微处理器支持这4种类型的堆栈工作方式。
- 满递增堆栈(FA):堆栈指针指向最后压入的数据,且由低地址向高地址生成。
- 满递减堆栈(FD):堆栈指针指向最后压入的数据,且由高地址向低地址生成。
- 空递增堆栈(EA):堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生成。
- 空递减堆栈(ED):堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生成。
注: 满、空栈区别:根据当前指针所在位置是否有东西。
满栈(full stack):栈指针指向最后压入栈的数据,数据入栈时,SP先减一(或加一)再入栈。
空栈(empty stack):栈指针指向下一个将要放入数据的位置,数据入栈时,先入栈SP再减一(或加一)。
(若存取数据为32位,则SP减或加4,且存数据时的地址总是从低向高变化)
增、减栈区别:根据堆栈的生成方向不同。
递增堆栈(ascending stack):堆栈由低地址向高地址生长。
递减堆栈(secending stack):堆栈由高地址向低地址生长。
总结:
- 满栈进栈是先移动指针再存;
- 满栈出栈是先出数据再移动指针;
- 空栈进栈先存再移动指针;
- 空栈出栈先移动指针再取数据。
(3)ARM指令集
ARM处理器的指令按功能可分为7大类:加载/存储指令(包括批量加载/存储指令)、分支指令、数据处理指令、乘法指令、状态寄存器访问指令、异常中断指令和协处理器指令。
1)加载/存储指令
①LDR指令
格式:LDR目的寄存器,<存储器地址>
功能:LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。该指令通常用于从存储器中读取32位的字数据到通用寄存器,然后对数据进行处理。
当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。
例如:
LDR R0, [R1] ; 将存储器地址为R1 的字数据读入寄存器R0
LDR R0, [R1, R2] ; 将存储器地址为R1+R2 的字数据读入寄存器R0
LDR R0, [R1, #8] ; 将存储器地址为R1+8 的字数据读入寄存器R0
LDR R0, [R1, R2, LSL#2]! ;将存储器地址为R1+R2×4的数据读入寄存器R0,并将新地址R1+R2×4写入R1
LDR R0, [R1], R2, LSL#2 ; 将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2×4写入R1
②LDRB指令
格式:LDRB目的寄存器,<存储器地址>
功能:LDRB指令用于从存储器中将一个8位的字节数据传送到目的寄存器中,同时将寄存器的高24位清零。该指令通常用于从存储器中读取8位的字节数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。
例如:
LDRB R0, [R1] ; 将存储器地址为R1的字节数据读入寄存器R0, 并将R0的高24位清零
③LDRH指令
格式:LDRH 目的寄存器,<存储器地址>
功能:LDRH指令用于从存储器中将一个16位的半字数据传送到目的寄存器中,同时将寄存器的高16位清零。该指令通常用于从存储器中读取16位的半字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。
④STR指令
格式:STR源寄存器,<存储器地址>
功能:STR指令用于从源寄存器中将一个32位的字数据传送到存储器中。该指令在程序设计中比较常用,且寻址方式灵活多样,使用方式可参考指令LDR。
例如:
STR R0, [R1], #8 ; 将R0 中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1
⑤STRB指令
格式:STRB源寄存器,<存储器地址>
功能:STRB指令用于从源寄存器中将一个8位的字节数据传送到存储器中。该字节数据为源寄存器中的低8位。
⑥STRH指令
格式:STRH 源寄存器,<存储器地址>
功能:STRH指令用于从源寄存器中将一个16位的半字数据传送到存储器中。该半字数据为源寄存器中的低16位。
2)批量加载/存储指令
LDM:(load much)多数据加载,将地址上的值加载到寄存器上
STM:(store much)多数据存储,将寄存器的值存到地址上
主要用途:现场保护、数据复制、参数传送等,共有8种模式(前面4种用于数据块的传输,后面4种是堆栈操作)如下:
- IA:(Increase After) 每次传送后地址加4,其中的寄存器从左到右执行,例如:STMIA R0,{R1,LR} 先存R1,再存LR
- IB:(Increase Before)每次传送前地址加4,同上 [见图15(图15.png)]
- DA:(Decrease After)每次传送后地址减4,其中的寄存器从右到左执行,例如:STMDA R0,{R1,LR} 先存LR,再存R1
- DB:(Decrease Before)每次传送前地址减4,同上
- FA: 满递增堆栈 (每次传送后地址减4)
- FD: 满递减堆栈 (每次传送前地址减4)
- EA: 空递增堆栈 (每次传送后地址加4)
- ED: 空递减堆栈 (每次传送前地址加4)
- STMFD:多次入栈满栈递减
- STMFA:多次入栈满栈递增
- STMED:多次入栈空栈递减
- STMEA:多次入栈空栈递增
- LDMFD:多次出栈满栈递减
- LDMFA:多次出栈满栈递增
- LDMED:多次出栈空栈递减
- LDMEA:多次出栈空栈递增
注:其中在数据块的传输中是STMDB和LDMIA对应,STMDA和LDMIB对应;而在堆栈操作是STMFD和LDMFD对应,STMFA和LDMFA对应。
格式:
LDM{cond} mode Rn{!}, reglist{^}
STM{cond} mode Rn{!}, reglist{^}
其中:
Rn:基址寄存器,装有传送数据的起始地址,Rn不允许为R15;
!:表示最后的地址写回到Rn中;
reglist:可包含多于一个寄存器范围,用“,”隔开,如{R1,R2,R6-R9},寄存器由小到大顺序排列;
^:不允许在用户模式和系统模式下运行
3)分支指令
ARM分支指令也称跳转指令,用于实现程序流程的跳转,在ARM程序中有如下两种方法可以实现程序流程的跳转:使用专门的跳转指令和直接向程序计数器PC写入跳转地址值。
ARM指令集中的跳转指令可以完成从当前指令向前或向后的32MB的地址空间的跳转,包括以下4条指令。
①跳转指令B
作用:跳转指令B使程序跳转到指定的地址执行程序(跳转范围是PC-32MB到PC+32MB)
指令格式(注:B后面如果有条件,条件就是紧跟在B后面,没有空格):
B{ < condition >} <target_address>
参数说明:< condition >:它指示指令在什么条件下执行,可省略
< target_address >:指令跳转的目标地址,指令通过下面的方法计算目标地址:
●将24位带符号的补码立即数符号扩展到32位
●将扩展后的32位立即数左移两位
●将得到的值加到PC寄存器中,即得到跳转的目标地址
②带连接的跳转指令BL
作用:带连接的跳转指令BL将下一条指令的地址拷贝到r14(即返回地址连接寄存器LR)寄存器中,然后跳转到指定地址运行程序。
语法格式:BL{} <target_address>
参数说明同B指令
③带状态切换的跳转指令BX
作用:使程序跳转到指令中指定的参数Rm指定的地址执行程序
语法格式:BX{}
参数说明: 包含跳转指令的目标地址,该地址处的指令既可以是ARM指令,也可以是Thumb指令。
④带状态切换的连接跳转指令BLX
作用:用于使程序跳转到Thumb状态或从Thumb状态返回,该指令为无条件执行指令,并用分支寄存器的最低位来更新CPSR中的T位,将返回地址写入到连接寄存器LR中。
语法格式:BLX{} <target_address>
参数说明:<target_address>为指令的跳转目标地址,该地址根据以下规则计算
●将指令指定的24位偏移量进行符号扩展,形成32位立即数
●将结果左移两位
●位H(bit[24])加到结果地址的第一位(bit[1])
●将结果累加到程序计数器PC中
4)数据处理指令
①MOV指令
格式:MOV{S} 目的寄存器,源操作数
功能:MOV指令可完成从另一个寄存器、被移位的寄存器或将一个立即数加载到目的寄存器。其中,S选项决定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。
②MVN指令
格式:MVN{S} 目的寄存器,源操作数
功能:MVN指令可完成从另一个寄存器、被移位的寄存器或将一个立即数加载到目的寄存器。与MOV指令不同之处是在传送之前按位被取反了, 即把一个被取反的值传送到目的寄存器中。其中S 定指令的操作是否影响CPSR中条件标志位的值,当没有S时指令不更新CPSR中条件标志位的值。
③CMP指令
格式:CMP 操作数1,操作数2
功能:CMP指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行比较,同时更新CPSR中条件标志位的值。该指令进行一次减法运算,但不存储结果,只更改条件标志位。影响flag的CF,ZF,OF,AF,PF
我们怎么判断大小呢?若执行指令后
- ZF=1 则说明两个数相等
- 当无符号时:
- CF=1 则说明了有进位或借位,cmp是进行的减操作,故可以看出为借位,所以,此时操作数1<操作数2
- CF=0 则说明了无借位,但此时要注意ZF是否为0,若为0,则说明结果不为0,故此时操作数1>操作数2
- 当有符号时:
- 若SF=0,OF=0 则说明了此时的值为正数,没有溢出,可以直观的看出,操作数1>操作数2
- 若SF=1,OF=0 则说明了此时的值为负数,没有溢出,则为操作数1<操作数2
- 若SF=0,OF=1 则说明了此时的值为正数,有溢出,可以看出操作数1<操作数2
- 若SF=1,OF=1则说明了此时的值为负数,有溢出,可以看出操作数1>操作数2
- 最后两个可以作出这种判断的原因是,溢出的本质问题:
- 两数同为正,相加,值为负,则说明溢出
- 两数同为负,相加,值为正,则说明溢出
- 故有,正正得负则溢出,负负得正则溢出
补充:两数相减,同号,则不溢出;两数为异号,结果与减数符号相同,则溢出。
④CMN指令
格式:CMN 操作数1,操作数2
功能:CMN指令用于把一个寄存器的内容和另一个寄存器的内容或立即数取反后进行比较,同时更新CPSR中条件标志位的值。该指令实际完成操作数1和操作数2相加,并根据结果更改条件标志位。
⑤TST指令
格式:TST 操作数1,操作数2
功能:TST指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的与运算,并根据运算结果更新CPSR中条件标志位的值。操作数1是要测试的数据,而操作数2是一个位掩码,该指令一般用来检测是否设置了特定的位。
⑥TEQ指令
格式:TEQ 操作数1,操作数2
功能:TEQ指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的异或运算,并根据运算结果更新CPSR中条件标志位的值。
该指令通常用于比较操作数1和操作数2是否相等。
⑦ADD指令
格式:ADD{S} 目的寄存器,操作数1,操作数2
功能:ADD指令用于把两个操作数相加,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
⑧ADC指令
格式:ADC{S} 目的寄存器,操作数1,操作数2
功能:ADC指令用于把两个操作数相加,再加上CPSR中的C条件标志位的值,并将结果存放到目的寄存器中。它使用一个进位标志位,这样就可以做比32位大的数的加法,注意不要忘记设置S后缀来更改进位标志。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
⑨SUB指令
格式:SUB{S} 目的寄存器,操作数1,操作数2
功能:SUB指令用于把操作数1减去操作数2,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
该指令可用于有符号数或无符号数的减法运算。
⑩SBC指令
格式:SBC{S} 目的寄存器,操作数1,操作数2
功能:SBC指令用于把操作数1减去操作数2,再减去CPSR中的C条件标志位的反码,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令使用进位标志来表示借位,这样就可以做大于32位的减法,注意不要忘记设置S后缀来更改进位标志。
该指令可用于有符号数或无符号数的减法运算。
⑪RSB指令
格式:RSB{S} 目的寄存器,操作数1,操作数2
功能:RSB指令称为逆向减法指令,用于把操作数2减去操作数1,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令可用于有符号数或无符号数的减法运算。
⑫RSC指令
格式:RSC{S}目的寄存器,操作数1,操作数2
功能:RSC指令用于把操作数2减去操作数1,再减去CPSR中的C条件标志位的反码,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令使用进位标志来表示借位,这样就可以做大于32位的减法,注意不要忘记设置S后缀来更改进位标志。
该指令可用于有符号数或无符号数的减法运算。
⑬AND指令
格式:AND{S} 目的寄存器,操作数1,操作数2
功能:AND指令用于在两个操作数上进行逻辑与运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
该指令常用于屏蔽操作数1的某些位。
⑭ORR指令
格式:ORR{S}目的寄存器,操作数1,操作数2
功能:ORR指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
该指令常用于设置操作数1的某些位。
⑮EOR指令
格式:EOR{S} 目的寄存器,操作数1,操作数2
功能:EOR指令用于在两个操作数上进行逻辑异或运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。
该指令常用于反转操作数1的某些位。
⑯BIC指令
格式:BIC{S} 目的寄存器,操作数1,操作数2
功能:BIC指令用于清除操作数1的某些位,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。操作数2为32位的掩码,
如果在掩码中设置了某一位,则清除这一位。未设置的掩码位保持不变。
5)乘法指令与乘加指令
ARM微处理器支持的乘法指令与乘加指令共有6条,可分为运算结果为32位和运算结果为64位两类,与前面的数据处理指令不同,指令中的所有操作数、目的寄存器必须为通用寄存器,不能对操作数使用立即数或被移位的寄存器,同时,目的寄存器和操作数1必须是不同的寄存器。
①MUL指令
格式:MUL{S} 目的寄存器,操作数1,操作数2
功能:32位乘法指令,完成将操作数1与操作数2的乘法运算,并把结果放置到目的寄存器中,同时可以根据运算结果设置CPSR中相应的条件标志位。
其中,操作数1和操作数2均为32位的有符号数或无符号数。
②MLA指令
格式:MLA{S} 目的寄存器,操作数1,操作数2,操作数3
功能:32位乘法指令,完成将操作数1与操作数2的乘法运算,再将乘积加上操作数3,并把结果放置到目的寄存器中,同时可以根据运算结果设置CPSR中相应的条件标志位。
其中,操作数1和操作数2均为32位的有符号数或无符号数。
③SMULL指令
格式:SMULL{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
功能:64位有符号数乘法指令,完成将操作数1与操作数2的乘法运算,并把结果的低32位放置到目的寄存器Low中,结果的高32位放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。
其中,操作数1和操作数2均为32位的有符号数。
④SMLAL指令
格式:SMLAL{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
功能:64位有符号数乘加指令,完成将操作数1与操作数2的乘法运算,并把结果的低32位同目的寄存器Low中的值相加后又放置到目的寄存器Low中,结果的高32位同目的寄存器High中的值相加后又放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。
其中,操作数1和操作数2均为32位的有符号数。对于目的寄存器Low,在指令执行前存放64位加数的低32位,指令执行后存放结果的低32位。
对于目的寄存器High,在指令执行前存放64位加数的高32 位,指令执行后存放结果的高32位。
⑤UMULL指令
格式:UMULL{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
功能:64位无符号数乘法指令,完成将操作数1与操作数2的乘法运算,并把结果的低32位放置到目的寄存器Low中,结果的高32位放置到目的寄存器High 中,同时可以根据运算结果设置CPSR中相应的条件标志位。其中,操作数1和操作数2均为32位的无符号数。
⑥UMLAL指令
格式:UMLAL{S} 目的寄存器Low,目的寄存器低High,操作数1,操作数2
功能:64位无符号数乘加指令,完成将操作数1与操作数2的乘法运算,并把结果的低32位同目的寄存器Low中的值相加后又放置到目的寄存器Low中,结果的高32位同目的寄存器 High中的值相加后又放置到目的寄存器High中,同时可以根据运算结果设置CPSR中相应的条件标志位。
其中,操作数1和操作数2均为32位的无符号数。对于目的寄存器Low,在指令执行前存放64位加数的低32位,指令执行后存放结果的低32位。
对于目的寄存器High,在指令执行前存放64位加数的高32位,指令执行后存放结果的高32位。
6)状态寄存器访问指令
①MRS指令
格式:MRS{条件} 通用寄存器,程序状态寄存器(CPSR或SPSR)
功能:MRS指令用于将程序状态寄存器的内容传送到通用寄存器中。该指令一般用在以下两种情况。
- 当需要改变程序状态寄存器的内容时,可用MRS将程序状态寄存器的内容读入通用寄存器,修改后再写回程序状态寄存器。
- 当在异常处理或进程切换时,需要保存程序状态寄存器的值,可先用该指令读出程序状态寄存器的值,然后保存。
②MSR指令
格式:MSR程序状态寄存器(CPSR或SPSR)_<域>,操作数
功能:MSR指令用于将操作数的内容传送到程序状态寄存器的特定域中。其中,操作数可以为通用寄存器或立即数。<域>用于设置程序状态寄存器中需要操作的位,32位的程序状态寄存器可分为4个域:
- 位[31:24]为条件标志位域,用f表示;
- 位[23:16]为状态位域,用s表示;
- 位[15:8]为扩展位域,用x表示;
- 位[7:0]为控制位域,用c表示;
该指令通常用于恢复或改变程序状态寄存器的内容,在使用时,一般要在MSR指令中指明将要操作的域。
7)异常中断指令
①SWI指令
格式:SWI{条件} 24位的立即数
功能:SWI指令用于产生软件中断,以便用户程序能调用操作系统的系统例程。操作系统在SWI的异常处理程序中提供相应的系统服务,指令中24位的立即数指定用户程序调用系统例程的类型,相关参数通过通用寄存器传递,当指令中24位的立即数被忽略时,用户程序调用系统例程的类型由通用寄存器R0的内容决定,同时,参数通过其他通用寄存器传递。
②BKPT指令
格式:BKPT 16位的立即数
功能:BKPT指令产生软件断点中断,可用于程序的调试。
8)协处理器指令
①CDP指令
格式:CDP{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2
功能:CDP指令用于ARM处理器通知ARM协处理器执行特定的操作,若协处理器不能成功完成特定的操作,则产生未定义指令异常。
其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,目的寄存器和源寄存器均为协处理器的寄存器,指令不涉及ARM处理器的寄存器和存储器。
②LDC指令
格式:LDC{条件}{L} 协处理器编码,目的寄存器,[源寄存器]
功能:LDC指令用于将源寄存器所指向的存储器中的字数据传送到目的寄存器中,若协处理器不能成功完成传送操作,则产生未定义指令异常。
其中,{L}选项表示指令为长读取操作,如用于双精度数据的传输。
③STC指令
格式:STC{条件}{L} 协处理器编码,源寄存器,[目的寄存器]
功能:STC指令用于将源寄存器中的字数据传送到目的寄存器所指向的存储器中,若协处理器不能成功完成传送操作,则产生未定义指令异常。
其中,{L}选项表示指令为长读取操作,如用于双精度数据的传输。
④MCR指令
格式:MCR{条件} 协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2
功能:MCR指令用于将ARM处理器寄存器中的数据传送到协处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。
其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,源寄存器为ARM处理器的寄存器,目的寄存器1和目的寄存器2均为协处理器的寄存器。
⑤MRC指令
格式:MRC{条件} 协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2
功能:MRC指令用于将协处理器寄存器中的数据传送到ARM处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。
其中协处理器操作码1和协处理器操作码2为协处理器将要执行的操作,目的寄存器为ARM处理器的寄存器,源寄存器1和源寄存器2均为协处理器的寄存器。
关于协处理器见附件1(附件1.docx)
8)总结
①影响CPSR状态寄存器的ARM汇编指令
- 带S的指令有:
- MOVS–>数据传送指令(只能用在寄存器和寄存器之间)
MOVS总是会影响CPSR, 包括N,Z,C标志位,执行MOVS pc, lr时,CPSR会被SPSR覆盖(内核态,USER和SYSTEM模式下没有SPSR) - MVNS–>数据传送指令(和mov区别是,不仅进行数据传输,还进行数据取反)
- ADDS–>加法指令
- SUBS–>减法指令
- MULS–>乘法指令
- ANDS–>逻辑与指令(相同是1,不同是0,区别“按位与TST"指令)
- ORRS–>逻辑或指令(不同是0,相同是1,区别“按位与TST"指令)
- BICS–>设置某一位为1
- 另外四个指令:
- CMP–>比较指令
- CMN–>取反后比较指令
- TST–>按位与指令
- TEQ–>按位异或指令
②几个特殊的寄存器
R13–>SP 堆栈控制寄存器
R14–>LR 连接寄存器
R15–>PC 程序计数器
补充:
ARM LDR/STR, LDM/STM 指令
这里比较下容易混淆的四条指令,已经在这4条指令的混淆上花费了很多精力,现在做个小结,LDR,STR,LDM,STM这四条指令,关于LDM和STM的说明,见另外一个说明文件,说明了这两个文件用于栈操作时的注意事项。
- (1)DR:L表示LOAD,LOAD的含义应该理解为:Load from memory into register。
下面这条语句就说明的很清楚:
LDR R1, [R2] ; R1<——[R2]
就是把R2所指向的存储单元的内容的值(一个memory地址内的值),读取到R1中(一个register) - (2)STR:S表示STORE,STORE的含义应该理解为:Store from a register into memory。
下面这条语句表示的很清楚:
STR R1, [R2] ; R1——>[R2]
就是把寄存器R1中的内容“保存”到R2所指向的存储的单元中(一个memory地址)。
显然,这两条语句都有个特点,就是寄存器写在前面(左边)而内存地址写在后面(右边),数据传送的方向则是恰好相反的。
下面对LDM和STM介绍,使用sp来介绍,因为实际使用中,和sp一起使用更多。
- (3)LDM:L的含义仍然是LOAD,即是Load from memory into register。
虽然貌似是LDR的升级,但是,千万要注意,这个指令运行的方向和LDR是不一样的,是从左到右运行的。
该指令是将内存中堆栈内的数据,批量的赋值给寄存器,即是出栈操作;
其中堆栈指针一般对应于SP,注意SP是寄存器R13,实际用到的却是R13中的内存地址,只是该指令没有写为[R13],
同时,LDM指令中寄存器和内存地址的位置相对于前面两条指令改变了,下面的例子:
LDMFD SP! , {R0, R1, R2} ; 实际上可以理解为: LDMFD [SP]!, {R0, R1, R2}
意思为:把sp指向的3个连续地址段(应该是3*4=12字节(因为为r0,r1,r2都是32位))中的数据拷贝到r0,r1,r2这3个寄存器中去 - (4)STM:S的含义仍然是STORE,与LDM是配对使用的,其指令格式上也相似,即区别于STR,是将堆栈指针写在左边,而把寄存器组写在右边。
STMFD SP!, {R0} ; 同样的,该指令也可理解为: STMFD [SP]!, {R0}
意思是:把R0保存到堆栈(sp指向的地址)中。
显然,这两个堆栈操作指令也有个特点,就是寄存器组写在后面(右边)而堆栈指针写在前面(左边),而且实际上使用的是堆栈指针中的内存地址,这一点与前面两条指令是有区别的。
(补充:sp后面的!,作用是指命令执行完后,对应的地址值赋给sp,对于例程的SDM,是说最后sp的值应该是sp+3*4=sp+12)
这四条指令中,前面两条和后面两条其实联系不多,反而是差别很大,因此,可以直接把这两组指令区分开来,认为它们之间没有联系,这样避免误解。
STM和LDM的主要用途是现场保护、数据复制、参数传递等,其模式有8种,如下:
注:前面4种用于数据块的传输,后面4种用于堆栈操作
●IA 每次传送后地址加4 -- Inc After
●IB 每次传送前地址加4 -- Inc Before
●DA 每次传送后地址减4 -- Dec After
●DB 每次传送前地址减4 -- Dec Before
●FD 满递减堆栈
●FA 满递增堆栈
●ED 空递减堆栈
●EA 空递增堆栈
下面的讲述对于空递减堆栈和空递增堆栈同样适用.
在堆栈操作时,经常错误以为使用STMFD满递减将寄存器压入堆栈后,在弹出数据的时候应该使用LDMFA。
但是FD和FA仅用于指示目前操作的堆栈是何种模式(堆栈共有四种模式),FD指明目前的堆栈是满递减堆栈,则数据入栈时的指令为STMFD,那么数据出栈时的指令对应的为LDMFD,而不是LDMFA。
我们可以这样认为STMFD等价于STMDB,LDMFD等价于STMIA
那么,数据传输的顺序和数据入栈的顺序又是如何呢
先来看STMFD SP!,{R1-R3} 执行的结果图(操作之后SP指向SP’)
SP------->
|R3|
|R2|
SP'------>|R1|
那么STMFD SP!,{R3,R2,R1}执行后的堆栈顺序是不是刚好和上面的堆栈顺序相反,实际情况时这两个指令执行后的堆栈数据顺序一样,因为ARM编译器会自动将STMFD SP!,{R3,R2,R1}转换为STMFD SP!,{R0-R3}指令,也就是说,ARM编译器默认高寄存器优先存入堆栈。
即便你在指令STMFD SP!,{R3,R2,R1}中刻意“安排”了寄存器入栈顺序,而在编译时编译器又重新做了处理,打乱了你期望的数据入栈顺序。
同理STMDB R0!,{R1-R3}和STMDB R0!,{R3,R2,R1}指令执行后数据在堆栈中的顺序完全一致。
STMFD SP!,{R1-R3}指令对应的出栈指令是LDMFD SP!,{R1-R3}(R1,R2,R3的顺序任意)
10.Thumb体系结构与指令集
(1)Thumb指令系统
为兼容数据总线宽度为16位的应用系统,ARM体系结构除了支持执行效率很高的32位ARM指令集以外,同时支持16位的Thumb指令集。
在一般的情况下,Thumb指令与ARM指令的时间效率和空间效率关系如下。
- Thumb代码所需的存储空间为ARM代码的60%~70%。
- Thumb代码使用的指令数比ARM代码多30%~40%。
- 若使用32位的存储器,ARM代码比Thumb代码快40%。
- 若使用16位的存储器,Thumb代码比ARM代码快40%~50%。
- 与ARM代码相比较,使用Thumb代码,存储器的功耗会降低30%。
Thumb指令集与ARM指令集在以下几个方面有区别:
- 跳转指令:条件跳转在范围上有更多的限制,转向子程序只具有无条件转移。
- 数据处理指令:对通用寄存器进行操作,操作结果需放入其中一个操作数寄存器,而不是第3个寄存器。
- 单寄存器加载和存储指令:Thumb状态下,单寄存器加载和存储指令只能访问寄存器R0~R7。
- 批量寄存器加载和存储指令:LDM和STM指令可以将任何范围为R0~R7的寄存器子集加载或存储,PUSH和POP指令使用堆栈指针R13作为基址实现满递减堆栈,除R0~R7外,PUSH指令还可以存储链接寄存器R14,并且POP指令可以加载程序指令PC。
(2)Thumb状态寄存器组织
Thumb状态下的寄存器集是ARM状态下寄存器集的一个子集,程序可以直接访问8个通用寄存器(R7~R0)、程序计数器(PC)、堆栈指针(SP)、连接寄存器(LR)和CPSR。
同时,在每一种特权模式下都有一组SP、LR和SPSR。
Thumb状态下的寄存器组织与ARM状态下的寄存器组织的关系如下:
- Thumb状态下和ARM状态下的R0~R7是相同的。
- Thumb状态下和ARM状态下的CPSR和所有的SPSR是相同的。
- Thumb状态下的SP对应于ARM状态下的R13。
- Thumb状态下的LR对应于ARM状态下的R14。
- Thumb状态下的程序计数器对应于ARM状态下的R15。
以上的对应关系
(3)Thumb指令集
1)存储器访问指令
①LDR和STR—立即数偏移
加载寄存器和存储寄存器。存储器的地址以一个寄存器的立即数偏移(immediate offset)指明。
格式:
op Rd, [Rn,#immed_5×4]
opH Rd, [Rn,#immed_5×2]
opB Rd, [Rn,#immed_5×1]
其中:
op:为LDR或STR。
H:指明无符号半字传送的参数。
B:指明无符号字节传送的参数。
Rd:加载和存储寄存器。Rd 必须在R0~R7范围内。
Rn:基址寄存器。Rn 必须在R0~R7范围内。
immed_5×N:偏移量。它是一个表达式,其取值(在汇编时)是N的倍数,在(0~31)*N范围内,N=4、2、1。
STR:用于存储一个字、半字或字节到存储器中。
LDR:用于从存储器加载一个字、半字或字节。
Rn:Rn中的基址加上偏移形成操作数的地址。
立即数偏移的半字和字节加载是无符号的。数据加载到Rd的最低有效字或字节,Rd的其余位补0。字传送的地址必须可被4整除,半字传送的地址必须可被2整除。
②LDR和STR—寄存器偏移
加载寄存器和存储寄存器。用一个寄存器的基于寄存器偏移指明存储器地址。
格式:op Rd,[Rn,Rm]
其中,op是下列情况之一:
LDR:加载寄存器,4字节字;
STR:存储寄存器,4字节字;
LDRH:加载寄存器,2字节无符号半字;
LDRSH:加载寄存器,2字节带符号半字;
STRH:存储寄存器,2字节半字;
LDRB:加载寄存器,无符号字节;
LDRSB:加载寄存器,带符号字节;
STRB:存储寄存器,字节。
Rm:内含偏移量的寄存器,Rm必须在R0~R7范围内。
带符号和无符号存储指令没有区别。
STR指令将Rd中的一个字、半字或字节存储到存储器。
LDR指令从存储器中将一个字、半字或字节加载到Rd。
Rn中的基址加上偏移量形成存储器的地址。
寄存器偏移的半字和字节加载可以是带符号或无符号的。数据加载到Rd的最低有效字或字节。对于无符号加载,Rd的其余位补0;或对于带符号加载,Rd的其余位复制符号位。字传送地址必须可被4整除,半字传送地址必须可被2整除。
③LDR和STR—PC或SP相对偏移
加载寄存器和存储寄存器。用PC或SP中值的立即数偏移指明存储器中的地址。没有PC相对偏移的STR指令。
格式:
LDR Rd,[PC,#immed_8×4]
LDR Rd,[label]
LDR Rd,[[SP,#immed_8×4]
STR Rd, [SP,#immed_8×4]
④PUSH和POP
低寄存器和可选的LR进栈以及低寄存器和可选的PC出栈。
格式:
PUSH {reglist}
POP {reglist}
PUSH {reglist,LR}
POP {reglist,PC}
⑤LDMIA和STMIA
加载和存储多个寄存器。
格式:op Rn!,{reglist}
其中,op为LDMIA或STMIA。
reglist为低寄存器或低寄存器范围的、用逗号隔开的列表。括号是指令格式的一部分,它们不代表指令列表可选,列表中至少应有一个寄存器。
寄存器以数字顺序加载或存储,最低数字的寄存器在Rn的初始地址中。
Rn的值以reglist中寄存器个数的4 倍增加。
2)数据处理指令
①ADD和SUB—低寄存器
加法和减法。对于低寄存器操作,这2条指令各有如下3种形式:
- 两个寄存器的内容相加或相减,结果放到第3个寄存器中。
- 寄存器中的值加上或减去一个小整数,结果放到另一个不同的寄存器中。
- 寄存器中的值加上或减去一个大整数,结果放回同一个寄存器中。
格式:
op Rd,Rn,Rm
op Rd,Rn,#expr3
op Rd,#expr8
②ADD—高或低寄存器
将寄存器中值相加,结果送回到第1操作数寄存器。
格式:ADD Rd,Rm
其中:
Rd:目的寄存器,也是第1操作数寄存器。
Rm:第2操作数寄存器。
这条指令将Rd和Rm中的值相加,结果放在Rd中。当Rd和Rm都是低寄存器时,指令“ADD Rd,Rm”汇编成指令“ADD Rd,Rd,Rm”。若Rd和Rm是低寄存器,则更新条件码标志N、Z、C和V;其他情况下这些标志不受影响。
③ADD和SUB—SP
=SP加上或减去立即数常量。
格式:
ADD SP,#expr
SUB SP,#expr
其中,expr为表达式,取值(在汇编时)为在-508~+508范围内的4的整倍数。
该指令把expr的值加到SP 的值上或用SP的值减去expr的值,结果放到SP中。
expr为负值的ADD指令汇编成相对应的带正数常量的SUB指令。expr为负值的SUB指令汇编成相对应的带正数常量的ADD指令。
④ADD—PC或SP相对偏移
SP或PC值加一立即数常量,结果放入低寄存器。
格式:ADD Rd,Rp,#expr
其中,
Rd:目的寄存器。Rd必须在R0~R7范围内。
Rp:SP或PC。
#expr:表达式,取值(汇编时)为0~1 020范围内的4的整倍数。
这条指令把expr加到Rp的值中,结果放入Rd。若Rp是PC,则使用值是(当前指令地址+4)AND &FFFFFFC,即忽略地址的低2位。
⑤ADC、SBC和MUL
带进位的加法、带进位的减法和乘法。
格式:op Rd,Rm
其中,
op为ADC、SBC或MUL。
Rd:目的寄存器,也是第1操作数寄存器。
Rm:第2操作数寄存器,Rd、Rm必须是低寄存器。
ADC将带进位标志的Rd和Rm的值相加,结果放在Rd中,用这条指令可组合成多字加法。
SBC考虑进位标志,从Rd值中减去Rm的值,结果放入Rd中,用这条指令可组合成多字减法。
MUL进行Rd和Rm值的乘法,结果放入Rd 中。
Rd和Rm必须是低寄存器(R0~R7)。
ADC和SBC更新标志N、Z、C和V。MUL更新标志N和Z。在ARMv4及以前版本中,MUL会使标志C和V不可靠。在ARMv5及以后版本中,MUL不影响标志C和V。
⑥按位逻辑操作AND、ORR、EOR和BIC
格式:op Rd,Rm
其中,
op为AND、ORR、EOR或BIC。
Rd:目的寄存器,它也包含第1操作数,Rd必须在R0~R7范围内。
Rm:第2操作数寄存器,Rm必须在R0~R7范围内。
这些指令用于对Rd和Rm中的值进行按位逻辑操作,结果放在Rd中,操作如下:
- AND:进行逻辑“与”操作;
- ORR:进行逻辑“或”操作;
- EOR:进行逻辑“异或”操作;
- BIC:进行“Rd AND NOT Rm”操作。
这些指令根据结果更新标志N和Z。
⑦移位和循环移位操作ASR、LSL、LSR和ROR
Thumb指令集中,移位和循环移位操作作为独立的指令使用,这些指令可使用寄存器中的值或立即数移位量。
格式:
op Rd,Rs
op Rd,Rm,#expr
其中,op是下列其中之一:
ASR:算术右移,将寄存器中的内容看做补码形式的带符号整数,将符号位复制到空出位;
LSL:逻辑左移,空出位填零;
LSR:逻辑右移,空出位填零;
ROR:循环右移,将寄存器右端移出的位循环移回到左端。ROR仅能与寄存器控制的移位一起使用。
Rd:目的寄存器,它也是寄存器控制移位的源寄存器。Rd必须在R0~R7范围内。
Rs:包含移位量的寄存器,Rs必须在R0~R7范围内。
Rm:立即数移位的源寄存器,Rm必须在R0~R7范围内。
expr:立即数移位量,它是一个取值(在汇编时)为整数的表达式。整数的范围为:若op是LSL,则为0~31;其他情况则为1~32。
⑧比较指令CMP 和CMN
格式:
CMP Rn,#expr
CMP Rn,Rm
CMN Rn,Rm
其中:
Rn:第1操作数寄存器。
expr:表达式,其值(在汇编时)为在0~255范围内的整数。
Rm:第2操作数寄存器。
CMP指令从Rn的值中减去expr或Rm的值,CMN指令将Rm和Rn的值相加,这些指令根据结果更新标志N、Z、C和V,但不往寄存器中存放结果。
对于“CMP Rn,#expr”和CMN指令,Rn和Rm必须在R0~R7范围内。
对于“CMP Rn,Rm”指令,Rn和Rm可以是R0~R15中的任何寄存器。
⑨传送、传送非和取负(MOV、MVN和NEG)
格式:
MOV Rd,#expr
MOV Rd,Rm
MVN Rd,Rm
NEG Rd,Rm
其中,
Rd:目的寄存器。
expr:表达式,其取值为在0~255范围内的整数。
Rm:源寄存器。
MOV指令将#expr或Rm的值放入Rd。MVN指令从Rm中取值,然后对该值进行按位逻辑“非”操作,结果放入Rd。NEG指令取Rm的值再乘以−1,结果放入Rd。
对于“MOV Rd,#expr”、MVN和NEG指令,Rd和Rm必须在R0~R7范围内。
对于“MOV Rd,Rm”指令,Rd和Rm可以是寄存器R0~R15中的任意一个。
“MOV Rd,#expr”和MVN 指令更新标志N和Z,对标志C或V无影响。NEG指令更新标志N、Z、C 和V。“MOV Rd,Rm”指令中,若Rd或Rm是高寄存器(R8~R18),则标志不受影响;若Rd 和Rm 都是低寄存器(R0~R7),则更新标志N和Z,且清除标志C和V。
⑩测试位TST
格式:TST Rn,Rm
其中,
Rn:第1操作数寄存器。
Rm:第2操作数寄存器。
TST对Rm和Rn中的值进行按位“与”操作。但不把结果放入寄存器。该指令根据结果更新标志N和Z,标志C和V不受影响。Rn和Rm必须在R0~R7范围内。
3)分支指令
①分支B指令
这是Thumb指令集中唯一的有条件指令。
格式:B{cond} label
其中,label是程序相对偏移表达式,通常是在同一代码块内的标号。若使用cond,则label必须在当前指令的−256~+256字节范围内。若指令是无条件的,则label必须在±2KB范围内。若cond满足或不使用cond,则B指令引起处理器转移到label。label必须在指定限制内。ARM链接器不能增加代码来产生更长的转移。
②带链接的长分支BL指令
格式:BL label
其中,1abel为程序相对转移表达式。BL指令将下一条指令的地址复制到R14(链接寄存器),并引起处理器转移到1abel。BL指令不能转移到当前指令±4MB以外的地址。
必要时,ARM链接器插入代码以允许更长的转移。
③分支,并可选地切换指令集BX
格式:BX Rm
其中,Rm装有分支目的地址的ARM寄存器。Rm的位[0]不用于地址部分。若Rm 的位[0]清零,则位[1]也必须清零,指令清除CPSR中的标志T,目的地址的代码被解释为ARM代码,BX指令引起处理器转移到Rm存储的地址。若Rm的位[0]置位,则指令集切换到Thumb状态。
④带链接分支,并可选地交换指令集BLX
格式:
BLX Rm
BLX label
其中,Rm 装有分支目的地址的ARM寄存器。Rm的位[0]不用于地址部分。若Rm 的位[0]清零,则位[1]必须也清零,指令清除CPSR中的标志T,目的地址的代码被解释为ARM代码。label为程序相对偏移表达式,“BLX label”始终引起处理器切换到ARM状态。
BLX指令可用于:
- 复制下一条指令的地址到R14;
- 引起处理器转移到label或Rm存储的地址。
如果Rm的位[0]清零,或使用“BLX label”形式,则指令集切换到ARM状态。指令不能转移到当前指令±4MB范围以外的地址。
必要时,ARM链接器插入代码以允许更长的转移。
4)中断和断点指令
①软件中断SWI指令
格式:SWI immed_8
其中,immed_8为数字表达式,其取值为0~255范围内的整数。
SWI指令引起SWI异常。这意味着处理器状态切换到ARM状态;处理器模式切换到管理模式,CPSR保存到管理模式的SPSR中,执行转移到SWI向量地址。
处理器忽略immed_8,但immed_8出现在指令操作码的位[7:0]中,而异常处理程序用它来确定正在请求何种服务,这条指令不影响条件码标志。
②断点BKPT指令
格式:BKPT immed_8
其中,immed_8为数字表达式,取值为0~255范围内的整数。
BKPT指令引起处理器进入调试模式。调试工具利用这一点来调查到达特定地址的指令时的系统状态。尽管immed_8出现在指令操作码的位[7:0]中,处理器忽略immed_8。调试器用它来保存有关断点的附加信息。