ARM 处理器是典型的 RISC 处理器,因为它们执行的是加载/存储体系结构。只
有加载和存储指令才能访问内存。数据处理指令只操作寄存器的内容。
ARM、Thumb 和 ThumbEE 状态
正执行 ARM 指令的处理器在 ARM 状态 下工作。正执行 Thumb 指令的处理器在
Thumb 状态 下工作。在其中一种状态下工作的处理器不能执行不同指令集内的指令。例如,处于ARM 状态下的处理器不能执行 Thumb 指令,而处于 Thumb 状态下的处理器不能执行 ARM 指令。
更改状态
每种指令集都包含用于更改处理器状态的指令。
若要在 ARM 和 Thumb 状态之间进行转换,必须切换汇编程序模式,以便使用ARM 或 THUMB 指令生成正确的操作代码。若要生成 Thumb-2EE 代码,请使用THUMBX。
处理器模式
ARM 处理器支持不同的处理器模式,具体取决于体系结构的版本
除用户模式之外,其他所有模式统称为特权模式。它们具有对系统资源的完全访问权限,并可随意更改模式。
需要任务保护的应用程序通常在用户模式下执行。有些嵌入式应用程序可能完全运行在超级用户模式或系统模式下。
寄存器
ARM 处理器拥有 37 个寄存器。这些寄存器按部分重叠组方式加以排列。每个处理器模式都有一个不同的寄存器组。编组的寄存器为处理处理器异常和特权操作提供了快速的上下文切换。
通用寄存器
在任一时刻都存在十五个通用寄存器,即 r0、r1… r13、r14,具体取决于当前的处理器模式。
r13
r13 是堆栈指针 (sp)。 C 和 C++ 编译器始终将 r13 用作堆栈指针。在 Thumb-2 中,sp 被严格定义为堆栈指针,因此如果使用 r13,则在堆栈操作中用处不大的许多指令会产生不可预测的结果。
r14
在用户模式下,r14 被用作链接寄存器 (lr),用于存储调用子例程时的返回地址。如果返回地址存储在堆栈上,则也可将 r14 用作通用寄存器。
在异常处理模式下,r14 存放异常的返回地址;如果在一个异常内执行子例程调用,则 r14 存放子例程的返回地址。如果返回地址存储在堆栈上,则可将 r14 用
作通用寄存器。
程序计数器 (pc)
程序计数器被当作 r15(或 pc)来加以访问。它在 ARM 状态下以一个字(四字节)为增量,在 Thumb 状态下则按指令的大小执行。跳转指令将目标地址加载到 pc 中。可以使用数据操作指令来直接加载 PC。
执行期间,当前执行指令的地址
在执行期间,r15 (pc) 不包含当前执行的指令的地址。在 ARM 状态下,当前执行的指令的地址通常是 pc-8,而在 Thumb 状态下通常是 pc-4。
应用程序状态寄存器(APSR)
APSR存放算术逻辑单元 (ALU) 状态标记的副本。这些标记用于确定是否执行条件指令。
在 ARMv5TE 和 ARMv6 及更高版本中,APSR 还存放 Q 标记。
在 ARMv6 及更高版本中,APSR 还存放 GE 标记。
可在所有模式下使用 MSR 和 MRS 指令访问这些标记。
当前程序状态寄存器 (CPSR)
CPSR 存放下列内容:
- APSR 标记
- 当前处理器模式
- 中断禁用标记。
在支持 Thumb 或 Jazelle® 的处理器上,CPSR 还存放当前处理器状态(ARM、Thumb、ThumbEE 或 Jazelle)。
在 ARMv6T2 及更高版本中,Thumb-2 为 CPSR 引入了新的状态位。IT 指令使用这些位来控制 IT 块的条件执行.
在所有模式下均可访问的标记只有 APSR 标记。对于 CPSR 的其余位,只能在特权模式下使用 MSR 和 MRS 指令访问它们。
保存的程序状态寄存器 (SPSR)
当发生异常时,使用 SPSR 来存储 CPSR。在每种异常处理模式下,可访问一个SPSR。用户模式和系统模式没有 SPSR,因为二者不是异常处理模式。
指令集
所有 ARM 指令的长度都是 32 位。这些指令是按字对齐方式存储的,因此在ARM 状态下,指令地址的两个最低有效位始终为零。
Thumb、Thumb-2 和 Thumb-2EE 指令的长度是 16 位或 32 位。这些指令按半字对齐方式存储。其中有些指令使用最低有效位来确定跳转到的目标代码是 Thumb 代码还是 ARM 代码。
在引入 Thumb-2 之前,Thumb 指令集只是 ARM 指令集功能的一个限定的子集。几乎所有 Thumb 指令都是 16 位。 Thumb-2 指令集的功能与 ARM 指令集的功能几乎相同。
ARM 和 Thumb 指令可划分为多个功能组
- 跳转指令
此类指令用于:
- 向后跳转以构成循环
- 在条件结构中向前跳转
- 跳转到子例程
- 在 ARM 状态和 Thumb 状态之间转换处理器状态。
- 数据处理指令
此类指令用于对通用寄存器执行运算。它们可对两个寄存器的内容执行加法、减法或按位逻辑等运算,并将结果存放到第三个寄存器中。此外,它们还可以对单个寄存器中的值执行运算,或者对寄存器中的值与指令中提供的常数(立即值)执行运算。
长乘指令用两个寄存器提供 64 位的结果。
- 寄存器加载和存储指令
此类指令用于从内存加载单个寄存器的值,或者在内存中存储单个寄存器的值。它们可加载或存储 32 位字、16 位半字或 8 位无符号字节。可以用符号或零扩展字节和半字加载以填充 32 位寄存器。
还定义了几个可将 64 位双字值加载或存储到两个 32 位寄存器的指令。
- 多个寄存器加载和存储指令
此类指令可从内存加载通用寄存器的任何子集,或者在内存中存储这样的子集。
状态寄存器访问指令
此类指令向通用寄存器或者从通用寄存器往外移动状态寄存器的内容。协处理器指令
此类指令支持一种用于扩展 ARM 体系结构的通用方式。
指令功能
条件执行
可以根据 APSR 中 ALU 状态标记的值,有条件地执行几乎所有 ARM 指令。虽然不需要使用跳转来跳过条件指令,但当一系列指令依赖于相同的条件时,这样做的效果会更好。在没有 Thumb-2 的处理器上的 Thumb 状态下,条件跳转是提供条件执行的唯一机制。大多数数据处理指令会更新 ALU 标记。通常不能指定指令是否更新 ALU 标记的状态。
Thumb-2 通过使用 IT (If-Then) 指令和同样的 ALU 标记为条件执行提供了另一种机制。IT 是一个 16 位指令,最多可为后面的四个指令提供条件执行。
在 ARM 和 Thumb-2 代码中,可以指定数据处理指令是否更新 ALU 标记。可以使用一个指令所设置的标记来控制其他指令的执行,即使在它们之间有很多非标记设置指令也是如此。
在 ARM 状态下以及具有 Thumb-2 的处理器上的 Thumb 状态下,大多数数据处理指令都具有一个选项,该选项可根据运算结果来更新应用程序状态寄存器(APSR) 中的 ALU 状态标记。有些指令会更新所有标记,而有些指令仅更新部分标记。如果某一标记未得到更新,则会保留其原始值。每个指令的描述详细介绍了它对这些标记所具有的影响。未执行的条件指令对这些标记没有影响。
在 ARM 状态下以及具有 Thumb-2 的处理器上的 Thumb 状态下,可以根据其他指令中设置的 ALU 状态标记有条件地执行指令
执行时间为:
在更新这些标记的指令后立即执行
在尚未更新这些标记的任何数目的插入指令之后执行。
可以根据 APSR 中的 ALU 状态标记的状态有条件地执行几乎所有 ARM 指令。下图为条件代码后缀
在 Thumb 状态下,使用条件跳转是一种条件执行机制。
在具有 Thumb-2 的处理器上的 Thumb 状态下,可以使用特殊的 IT (If-Then) 指令使指令有条件地执行。此外,还可以使用 CBZ(零条件跳转)和 CBNZ 指令将寄存器值与零进行比较。
寄存器访问
在 ARM 状态下,所有指令都可访问 r0 到 r14,并且大多数指令也可访问 r15 (pc)**。MRS 和 MSR 指令可将状态寄存器的内容移到通用寄存器中**,在通用寄存器中可以用普通的数据处理操作来处理这些内容。
Thumb-2 处理器上的 Thumb 状态提供了同样的功能,但会禁止一些对 r13 和 r15 的无用访问。
在没有 Thumb-2 的处理器上的 Thumb 状态下,大多数指令只能访问 r0 到 r7。只有少数指令能够访问 r8 到 r15。**寄存器 r0 到 r7 称为低位寄存器。寄存器 r8 到r15 称为高位寄存器**。
访问内联的滚筒式移位器
ARM 算术逻辑单元具有一个 32 位滚筒式移位器,可执行移位和循环操作。对于所有 ARM 和 Thumb-2 数据处理指令和单寄存器数据传送指令的第二个操作数,可以在执行数据处理或数据传送之前,将其作为指令的一部分执行移位操作。
此操作支持(但不限于):比例寻址
乘以一个常数
构造常数。
Thumb-2 指令与 ARM 指令对滚筒式移位器的访问方式几乎相同。
16 位 Thumb 指令集只允许使用单独的指令来访问滚筒式移位器。
MSR(用于存储)
将 PSR 的内容移到通用寄存器中
语法
MRS{cond} Rd, psr
cond: 是一个可选的条件代码
Rd: 是目标寄存器。Rd 不能为 r15(pc)。
psr: 是下列项之一:
- APSR 任何处理器,任何模式。
- CPSR 推荐使用的 APSR 的同义词,用于调试状态。
- SPSR 除 ARMv7-M 外的任何处理器,仅可用于特权模式。
- MpSR 仅用于 ARMv7-M 处理器。
Mpsr: 是下列项之一:
IPSR、EPSR、IEPSR、IAPSR、EAPSR、PSR、MSP、PSP、DSP、
PRIMASK、BASEPRI、BASEPRI_MAX 或 CONTROL
用法
可将 MRS 与 MSR 结合使用,创建一个更新 PSR 的读- 改- 写序列,例如更改处理器模式或清除 Q 标记。
在进程交换代码中,必须保存程序员的换出进程的模型状态,包括 PSR 的相关内容。同样,也必须恢复换入进程的状态。这些操作使用的是 MRS/存储和加载/MSR 指令序列。
注意:
当处理器处于用户或系统模式时,请不要访问 SPSR。这是您的责任。汇编程序无法就此发出警告,因为它不知道执行过程中的处理器模式。如果在处理器处于用户模式或系统模式时,尝试访问 SPSR,则结果将是无法预料的。
仅当处理器处于调试状态、暂停调试模式时,才能读取 CPSR 执行状态位。否则,CPSR 中的执行状态位的读取结果将会为零。条件标记的读取不受模式和处理器的限制。应使用 APSR,而不是 CPSR。
此指令不更改标记。
此 ARM 指令可用于所有版本的 ARM 体系结构。这些 32 位 Thumb 指令可用于 ARMv6T2 和 ARMv7。此指令无 16 位 Thumb 版本。
MSR(用于加载)
将通用寄存器的立即数或内容加载到程序状态寄存器 (PSR) 的指定位段中。
语法(ARMv7-M 除外)
MSR{cond} APSR_flags, #constant
MSR{cond} APSR_flags, Rm
MSR{cond} psr_fields, #constant
MSR{cond} psr_fields, Rm
其中:
cond: 是一个可选的条件代码
flags: 指定要移动的 APSR 标记。flags 可以是以下的一个或多个指令:
- nzcvq ALU 可标记位段掩码,PSR[31:27](用户模式)
- g SIMD GE 可标记位段掩码,PSR[19:16](用户模式)。
constant: 是取值为常数的一个表达式。该常数必须对应于一个 8 位结构,可通过在 32 位字内循环移动偶数位而得到。在 Thumb 中不可用。
Rm: 是源寄存器。
psr: 是下列项之一:
- CPSR 用于调试状态,请不要将其看做 APSR 的同义词
- SPSR 仅可用于特权模式下的处理器。
fields: 指定要移动的 SPSR 或 CPSR 位段。fields 可以是以下一个或多个值:
- c 控制位段掩码字节,PSR[7:0](特权模式)
- x 扩展位段掩码字节,PSR[15:8](特权模式)
- s 状态位段掩码字节,PSR[23:16](特权模式)
- f 标记位段掩码字节,PSR[31:24](特权模式)。
语法 (ARMv7-M)
MSR{cond} psr, Rm
cond: 是一个可选的条件代码
Rm: 是源寄存器。
psr: 是下列项之一:APSR、IPSR、EPSR、IEPSR、IAPSR、EAPSR、PSR、MSP、PSP、DSP、PRIMASK、BASEPRI、BASEPRI_MAX 或 CONTROL。仅限 ARMv7-M。
用法与MRS相同
在用户模式中:
- 使用 APSR 访问条件标记、Q 或 GE 位。
- 忽略对 CPSR 中的未分配状态位、特权状态位或执行状态位的写入。这可确保用户模式程序不会更改为 特权模式。
如果在用户或系统模式时尝试访问 SPSR,则结果将是无法预料的。
注意:
- 如果指定了 APSR_nzcvq 或 CPSR_f 位段,则此指令将会显式更新标记。
- 此 ARM 指令可用于所有版本的 ARM 体系结构。这些 32 位 Thumb 指令可用于 ARMv6T2 和 ARMv7。此指令无 16 位 Thumb 版本。
汇编语言模块的结构
汇编语言是指 ARM 汇编程序 (armasm) 进行分析并汇编生成对象代码的语言。缺省情况下,汇编程序应使用 ARM 汇编语言编写源代码。
armasm 支持用旧版本的 ARM 汇编语言编写的源代码。在这种情况下,它无需获得相应的通知。
armasm 还可支持用旧版本的 Thumb 汇编语言编写的源代码。在这种情况下,必须在源代码中使用 –16 命令行选项或 CODE16 指令通知 armasm。旧版本的 Thumb 汇编语言不支持 Thumb-2 指令。
语法格式
汇编语言的源代码行的一般格式是{label} {instruction|directive|pseudo-instruction} {;comment}
下图为官方的arm汇编示例
注意:
即使没有标签,指令、伪指令和指令前面也必须使用空格或制表符等留出空白。
源代码行的所有三部分都是可选的。使用空行可使代码更具可读性。
大小写规则
指令助记符、指令和符号寄存器名称可以用大写或小写编写,但不能混合使用大小写。
行长度的最大值为 4095 个字符,包括使用反斜杠的任何扩展在内。
ELF节
ELF 节 是独立的、已命名的、不可分割的代码或数据序列。单个代码节是生成应用程序的最低要求。
汇编或编译的输出内容可包括:
- 一个或多个代码节。它们通常是只读节。
- 一个或多个数据节。它们通常是读写节。它们可以是零初始化的 (ZI)。
链接器依照节位置规则,将每个节放在一个程序映像中。对于在源文件中相邻的节,在应用程序映像中不一定相邻。
AREA
在源文件中,AREA 指令标记一节的开始。该指令对节进行命名并设置其属性。属性放在名称后面,之间用逗号分隔。
可以为节选择任何名称。但是,以任何非字母字符开头的名称必须括在竖线内,否则会生成 AREA name missing 错误。例如,|1_DataArea|。
ENTRY 指令
ENTRY 指令标记的是第一个要执行的指令。在包含 C 代码的应用程序中,在 C 库初始化代码中也包含一个入口点。初始化代码和异常处理程序也包含入口点。
应用程序执行(start)
应用程序代码在标签 start 处开始执行,
官方示例图中
start开始并将
进制值 10 和 3 加载到寄存器 r0 和 r1 中。这些寄存器将一起相加,并且结果将存放到 r0 中。
应用程序终止(stop)
在执行主代码后,应用程序会将控制权返回调试器,以此来终止执行。
END指令
此指令指示汇编程序停止处理此源文件。每个汇编语言源模块必须以仅包括 END指令的一行结束。
调用子例程
若要调用子例程,应使用跳转和链接指令,其语法是:BL destination
其中,destination 通常是位于子例程的第一个指令处的标签。destination 也可以是程序相对表达式。
BL 指令
作用:
- 将返回地址存放到链接寄存器中
- 将 pc 设置为子例程的地址。
在执行子例程代码后,可以使用 BX lr 指令返回。按照约定,寄存器 r0 到 r3 用于将参数传递给子例程,并且 r0 还用于将结果传递回调用方。
Q标记
在 ARMv5TE 和 ARMv6 及更高版本中,当饱和算术指令(在 ARMv5TE 和 ARMv6 及更高版本中,当饱和算术指令)中出现饱和或某些乘法指令(SMULxy 和 SMLAxy 和第4-79 页的SMULWy 和 SMLAWy)中出现溢出时,会记录 Q 标记。
Q 标记是一种粘性 标记。虽然这些指令可以设置该标记,但不能清除它。
要清除 Q 标记,则请使用 MSR 指令
不能直接用条件代码来测试 Q 标记的状态。若要读取 Q 标记的状态,请使用 MRS指令