目录
2.1 程序员模型
2.2 Processor modes
2.3 寄存器
2.4 通用寄存器
2.4.1(Unbanked Registers),R0-R7
2.4.2(Banked Registers),R8-R14
2.4.3 PC,R15
读取程序计数器
写入程序计数器
2.1 程序员模型
数据类型 ARM处理器支持以下数据类型:
- 字节(Byte):8位。
- 半字(Halfword):16位(半字必须对齐到两个字节的边界上)。
- 字(Word):32位(字必须对齐到四个字节的边界上)。
注意:
- 所有这三种类型都支持在ARM架构版本4及以上。在ARM架构版本4之前,只支持字节和字。
- 当这些类型被描述为无符号(unsigned)时,N位数据值表示一个非负整数,范围从0到+2^N-1,使用正常的二进制格式。
- 当这些类型被描述为有符号(signed)时,N位数据值表示一个整数,范围从-2^N-1到+2^N-1-1,使用二进制补码格式。
- 所有数据操作,例如加法(ADD),都是在字量上执行的。
- 加载和存储操作可以传输字节、半字和字到内存,并且在加载时自动零扩展或符号扩展字节或半字。
- ARM指令恰好是一个字(并且对齐在四个字节的边界上)。Thumb指令恰好是一个半字(并且对齐在两个字节的边界上)。
2.2 Processor modes
ARM架构支持表中显示的七种处理器模式。模式切换可以在软件控制下进行,也可以由外部中断或异常处理引起。
大多数应用程序在用户模式(User mode)下执行。当处理器处于用户模式时,正在执行的程序无法访问一些受保护的系统资源或更改模式,除非引发异常。这允许一个适当编写的操作系统控制对系统资源的使用。
除了用户模式之外的其他模式被称为特权模式。它们可以完全访问系统资源,并且可以自由地更改模式。其中五种被称为异常模式:
- 快速中断(FIQ)
- 普通中断(IRQ)
- 超级监视器(Supervisor)
- 中止(Abort)
- 未定义(Undefined)。
当特定的异常发生时,会进入这些模式。它们各自有一些额外的寄存器,以避免在异常发生时破坏用户模式状态(见A2-4页上的“寄存器”了解详细信息)。
剩下的模式是系统模式(System mode),它只在ARM架构版本4及以上中出现。它不会被任何异常进入,并且可用的寄存器与用户模式完全相同。然而,它是一个特权模式,因此不受用户模式限制。它适用于需要访问系统资源的操作系统任务,但希望避免使用与异常模式相关的额外寄存器。避免这种使用确保了任务状态不会被任何异常的发生所破坏。
2.3 寄存器
ARM处理器共有37个寄存器:
- 31个通用寄存器,包括一个程序计数器。这些寄存器是32位宽,
- 6个状态寄存器。这些寄存器也是32位宽,但只有32位中的12位被分配或需要实现。
寄存器被安排在部分重叠的bank中,每个处理器模式都有一个不同的寄存器bank,如图所示。在任何时候,15个通用寄存器(R0到R14)、一个或两个状态寄存器和程序计数器是可见的。图的每一列显示了在指示的处理器模式中哪些通用寄存器和状态寄存器是可见的。
这种部分重叠的寄存器bank设计意味着,当处理器切换模式时,某些寄存器(如R0到R14和PC)在所有模式中都是共享的,而其他寄存器(如R13和R14)则在不同模式下有特定的用途,并且可能有不同的名称和行为。例如,R13通常用作堆栈指针(SP),而R14用作链接寄存器(LR)。状态寄存器(如CPSR和SPSRs)保存了处理器的状态信息,包括条件码标志、中断使能位和处理器模式等。
2.4 通用寄存器
通用寄存器R0-R15可以分为三组。这些组在它们的银行化方式以及特殊用途上有所不同
2.4.1(Unbanked Registers),R0-R7
- 寄存器R0到R7是未银行化的寄存器。这意味着它们中的每一个都指向所有处理器模式中相同的32位物理寄存器。它们是完全通用的寄存器,架构没有暗示任何特殊用途,并且可以在任何指令允许指定通用寄存器的地方使用。
2.4.2(Banked Registers),R8-R14
- 寄存器R8到R14是bank的寄存器。它们所指向的物理寄存器取决于当前的处理器模式。如果需要指定一个特定的物理寄存器,而不依赖于当前的处理器模式,就会使用更具体的名称(如下所述)。几乎所有指令都允许在允许使用通用寄存器的地方使用bank化寄存器。
- 注意:在个别指令描述中有少数例外。如果对bank化寄存器的使用存在限制,它总是适用于所有R8到R14的寄存器。例如,即使在从未使用FIQ模式的系统中,R8到R12也受到此类限制,因此只有一个物理版本的寄存器在使用。
- 寄存器R8到R12每个都有两个bank化的物理寄存器。一个用于除FIQ模式之外的所有处理器模式,另一个用于FIQ模式。如果需要具体说明正在引用哪个版本,第一组物理寄存器被称为R8_usr到R12_usr,第二组被称为R8_fiq到R12_fiq。
- 寄存器R8到R12在架构中没有任何专用的特殊用途。然而,对于足够简单,可以使用寄存器R8到R14进行处理的中断,这些寄存器的FIQ模式版本的存在允许非常快速的中断处理。这种用法的例子可以在单通道DMA传输和双通道DMA传输中找到。
- 寄存器R13和R14每个都有六个bank化的物理寄存器。一个用于用户和系统模式,而剩下的五个中的每一个都用于五种异常模式中的一个。如果需要具体说明正在引用哪个版本,可以使用以下名称:
- R13_<mode>
- R14_<mode>
其中<mode>是usr、svc(用于超级监视器模式)、abt、und、irq和fiq中的适当一个。
寄存器R13通常用作堆栈指针,也被称为SP。在ARM指令集中,这只是出于惯例,因为没有定义的指令或其他功能以特殊方式使用R13。然而,在Thumb指令集中有这样的指令。
每个异常模式都有自己的bank化版本R13,通常应该初始化为指向该异常模式专用的堆栈。在进入异常处理程序时,异常处理程序通常将其他寄存器的值存储到这个堆栈中。通过在返回时将这些值重新加载到寄存器中,异常处理程序可以确保它不会破坏在异常发生时正在执行的程序的状态。
寄存器R14(也称为链接寄存器或LR)在架构中有两个特殊功能:
- 在每个模式中,该模式自己的R14版本用于保存子程序返回地址。当通过BL或BLX指令执行子程序调用时,R14被设置为子程序返回地址。子程序返回是通过将R14复制回程序计数器来执行的。这通常可以通过以下两种方式之一完成:
- 执行以下指令之一:
MOV PC,LR
BX LR
- 在子程序入口处,使用以下形式的指令将R14存储到堆栈中:
STMFD SP!,{<registers>,LR}
- 并使用匹配的指令返回:
LDMFD SP!,{<registers>,PC}
- 执行以下指令之一:
- 当异常发生时,适当的异常模式版本R14被设置为异常返回地址(对于某些异常,偏移量会稍微调整)。异常返回与子程序返回类似,但使用稍微不同的指令以确保完全恢复在异常发生时正在执行的程序的状态。
在其他所有时候,寄存器R14可以被视为通用寄存器。
注意: 当可能发生嵌套异常时,这两种特殊用途可能会发生冲突。例如,如果在一个程序在用户模式下执行时发生IRQ中断,用户模式寄存器不一定会被破坏。但是,如果一个在IRQ模式下运行的中断处理程序重新启用IRQ中断,并且发生了嵌套的IRQ中断,那么外部中断处理程序当时在R14_irq中保存的任何值都会被嵌套中断的返回地址覆盖。
系统程序员需要小心处理这种交互。处理这些问题的通常方法是确保在可能发生嵌套异常的时候,R14的适当版本不保存任何重要内容。当直接方式难以实现时,通常最好在重新启用中断或以其他方式允许嵌套异常发生之前,在异常处理程序的入口处切换到另一个处理器模式。(在ARM架构版本4及以上中,系统模式通常是最好的模式,用于此目的。)
2.4.3 PC,R15
寄存器R15持有程序计数器(PC)。它经常可以替代通用寄存器R0到R14中的一个使用,因此被认为是通用寄存器之一。然而,关于它的使用也有许多指令特定的限制或特殊情况。这些在各个指令描述中都有注明。通常,如果R15的使用方式违反了这些限制,那么指令的行为是不可预测的(UNPREDICTABLE)。
程序计数器总是用于一个特殊的目的,如下所述:
- 读取程序计数器
- 写入程序计数器
读取程序计数器
当一个指令读取R15而不违反其使用限制时,读取到的值是指令地址加8字节。由于ARM指令总是字对齐的,结果值的位[1:0]总是零。(在架构的T变体中,当执行Thumb状态时,这种行为会改变 )
这种读取PC的方式主要用于快速、位置无关的附近指令和数据的寻址,包括程序内的位置无关分支。
上述规则的例外情况发生在STR或STM指令存储R15时。这些指令可以像其他读取R15的指令一样存储指令地址加8字节,或者存储指令自己的地址加12字节。使用8字节偏移量还是12字节偏移量是实现定义的(IMPLEMENTATION DEFINED)。一个实现必须对所有存储R15的STR和STM指令使用相同的偏移量。它不能对其中一些使用8,对另一些使用12。
由于这个例外,通常最好避免使用存储R15的STR和STM指令。如果这很困难,可以使用适当的指令序列在程序中确定实现使用的偏移量。例如,如果R0指向一个可用的字存储器,那么以下指令将实现的偏移量放入R0:
SUB R1, PC, #4 ; R1 = 下一条STR指令的地址
STR PC, [R0] ; 存储STR指令的地址 + 偏移量,
LDR R0, [R0] ; 然后重新加载它
SUB R0, R0, R1 ; 计算偏移量作为差异
注意: 关于如何读取R15的规则只适用于指令的读取。特别是,它们不一定描述在指令提取期间硬件地址总线上放置的值。像所有其他硬件接口的细节一样,这些值是实现定义的(IMPLEMENTATION DEFINED)。
写入程序计数器
当一个指令写入R15而不违反其使用限制时,通常的结果是写入R15的值被视为指令地址,并且会跳转到该地址执行指令。由于ARM指令需要字对齐,通常期望写入R15的值位[1:0] == 0b00。对此的具体规则取决于架构版本:
- 在ARM架构版本3及以下,写入R15的值的位[1:0]被忽略,因此指令的实际目的地地址是(写入R15的值)AND 0xFFFFFFFC。
- 在ARM架构版本4及以上,写入ARM状态下R15的值的位[1:0]必须是0b00。如果不是,结果将是不可预测的(UNPREDICTABLE)。
- 类似地,在ARM架构版本4及以上的T变体中,Thumb指令需要半字对齐。在Thumb状态下写入R15的值的位[0]被忽略,因此指令的实际目的地地址是(写入R15的值)AND 0xFFFFFFFE。
一些指令对写入R15的值有它们自己的解释规则。例如,BX和其他设计用于在ARM和Thumb状态之间传输的指令使用值的位[0]来选择是否在ARM状态或Thumb状态下执行目标地址的代码。这类特殊规则在各个指令页面上描述,并覆盖本节中的一般规则。
挖坑待补