16_ARM_SYSTEM
文章目录
- 16_ARM_SYSTEM
- 1.计算机硬件基础
- 2.ARM处理器概述
- 3.ARM寄存器组织
- 4.ARM异常处理
- 5.ARM汇编指令集
1.计算机硬件基础
地址空间
地址总线的定义:地址总线是用于传输内存地址的总线。地址总线的宽度(位数)决定了处理器能够直接寻址的最大内存空间。
地址空间的计算:
- 32位地址总线意味着处理器可以生成的地址有32位长。
- 每个位可以有两个状态(0或1),因此32位地址总线可以生成的不同地址数量是 2 32 2^{32} 232。
- 2 32 = 4 , 294 , 967 , 296 2^{32} = 4,294,967,296 232=4,294,967,296,这意味着处理器可以直接寻址4,294,967,296个不同的内存位置。
字节寻址:
- 大多数现代处理器采用字节寻址方式,即每个地址对应一个字节(8位)的内存。
- 因此,32位地址总线可以寻址的总内存大小为4,294,967,296字节,转换为GB(千兆字节)就是4GB。
CPU执行指令的过程
-
取指令(Fetch):
- 从内存中取出当前需要执行的指令,存入指令寄存器(IR)。
- 程序计数器(PC)保存了指令的内存地址。
-
译码(Decode):
- 将取来的指令送到指令译码器(Instruction Decoder)。
- 译码器分析指令,确定操作类型和操作数的位置。
-
取操作数(Fetch Operands):
- 如果需要,从寄存器或内存中获取操作数。
-
执行(Execute):
- 根据指令类型,执行相应的操作,如算术运算、数据传输等。
-
写回(Write Back):
- 将执行结果写回到目标寄存器或内存位置。
-
更新程序计数器(Update PC):
- 更新程序计数器(PC),指向下一条指令。
这个过程不断循环,完成程序的执行。
2.ARM处理器概述
ARM处理器
处理器分为CISC处理器与RISC处理器
ARM处理器属于RISC处理器
特性 | CISC(复杂指令集计算机) | RISC(精简指令集计算机) |
---|---|---|
指令集复杂性 | 复杂,指令长度不固定 | 简洁,指令长度固定 |
指令执行周期 | 多个时钟周期 | 大多数指令一个时钟周期 |
寻址模式 | 多种复杂寻址模式 | 少量简单寻址方式 |
微代码与硬件实现 | 微代码控制单元 | 硬连线控制单元 |
寄存器与内存访问 | 频繁访问内存 | 加载/存储架构,只能访问寄存器 |
编译器设计 | 编译器设计简单 | 编译器设计复杂 |
指令集扩展性 | 易于扩展 | 难于扩展 |
功耗 | 相对较高 | 低功耗 |
性能 | 高性能,但复杂性较高 | 高效能,适合多种应用 |
应用领域 | 台式机、服务器、大型计算机等 | 移动设备、嵌入式系统、物联网、服务器等 |
指令集
指令集类型
类型 | 描述 | 示例 |
---|---|---|
CISC(复杂指令集计算机) | 指令集复杂,包含多种复杂指令,每条指令可以执行多个低级操作 | x86指令集 |
RISC(精简指令集计算机) | 指令集简洁,每条指令通常只执行一个简单操作,指令长度固定 | ARM指令集、MIPS指令集 |
指令集关键方面
关键方面 | 描述 |
---|---|
数据处理指令 | 执行算术和逻辑操作,如加法、减法、乘法、除法、与、或、移位等 |
数据传输指令 | 在寄存器和内存之间传输数据,如加载、存储、移动等 |
控制流指令 | 改变程序的执行顺序,如跳转、调用、返回、条件分支等 |
输入/输出指令 | 与外部设备进行数据交换,如读写I/O端口 |
特殊指令 | 应用于特定功能的指令,如多媒体处理指令、加密指令等 |
指令集的重要性
重要性 | 描述 |
---|---|
性能优化 | 指令集设计直接影响处理器的性能和效率,简洁高效的指令集可以提高处理器的执行速度 |
编程模型 | 指令集定义了编程模型,影响编译器的设计和高级编程语言的支持 |
兼容性 | 不同的指令集通常意味着不同的硬件架构,程序在不同指令集的处理器上运行时需要重新编译或移植 |
应用领域 | 指令集的设计可以针对特定应用领域进行优化,例如嵌入式系统、移动设备、高性能计算等 |
ARM工作模式
ARM处理器有多种工作模式,以满足不同的操作需求和处理不同的任务。这些模式可以分为以下几类:
-
用户模式(User Mode, usr):
- 用户模式是执行应用程序的正常模式,拥有最低的权限,无法访问某些受保护的系统资源。
-
系统模式(System Mode, sys):
- 系统模式与用户模式相似,但具有更高的权限,可以访问受保护的系统资源。
- 通常用于操作系统的内核执行特权级操作。
-
异常模式(Exception Modes):
这些模式用于处理异常(例如中断、错误)和特殊事件。每种异常模式都有特定的用途和一组专用寄存器。
- 快速中断请求模式(Fast Interrupt Request Mode, fiq) :
- 用于处理快速中断请求,有优先级最高的中断处理能力。
- 中断请求模式(Interrupt Request Mode, irq) :
- 用于处理普通中断请求。
- 管理模式(Supervisor Mode, svc) :
- 当系统调用或软件中断(SWI)发生时进入此模式,通常用于操作系统执行管理任务。
- 中止模式(Abort Mode, abt) :
- 用于处理内存访问异常,如预取中止和数据中止。
- 未定义模式(Undefined Mode, und) :
- 当执行未定义指令时进入此模式。
- 监控模式(Monitor Mode, mon) (仅适用于ARMv6K及以上):
- 用于安全扩展,如TrustZone。
- 快速中断请求模式(Fast Interrupt Request Mode, fiq) :
-
调试模式(Debug Mode):
- 用于调试目的,允许开发人员检查和控制处理器的状态。
ARM处理器通过不同的工作模式,可以实现多任务操作、异常处理、系统管理和调试等功能。这些模式之间的切换通常由硬件自动完成,以确保系统的稳定和高效运行。
3.ARM寄存器组织
概念:寄存器是处理器内部的存储器,没有地址
作用:一般用于暂时存放参与运算的数据和运算结果
分类:包括通用寄存器、专用寄存器、控制寄存器
通用寄存器
R0 - R7
:这是在所有处理器模式下都可以访问和使用的通用寄存器,常用于数据操作和传递参数。R8 - R12
:在快速中断模式下有独立的一组,其他模式下共用。R13
:通常作为堆栈指针寄存器,不同模式下有各自独立的版本,用于管理堆栈。R14
:链接寄存器,保存子程序的返回地址。R15
:程序计数器,指示当前正在执行的指令地址。
R13(堆栈指针寄存器) :
R13 通常被用作堆栈指针,用于管理函数调用时的堆栈操作。不同的运行模式下有各自独立的 R13,这有助于实现不同模式下堆栈的隔离和保护。例如,在系统模式和用户模式下,R13 分别指向不同的堆栈空间,当发生模式切换时,不会相互干扰。
R14(链接寄存器) :
R14 用于保存子程序返回地址。当执行子程序调用时,R14 会被自动设置为子程序返回后的下一条指令地址。比如说,当一个函数调用另一个函数时,R14 就记录了返回的位置,以便函数执行完毕后能正确回到原来的位置继续执行。
R15(程序计数器) :
R15 存放当前正在执行的指令地址。处理器通过读取 R15 来获取下一条要执行的指令。如果发生跳转等操作,R15 的值会相应改变以指向新的指令地址。
专用寄存器
CPSR
(当前程序状态寄存器):包含条件标志位(、控制位(如中断使能控制位)和模式位,反映当前处理器的工作模式和状态。SPSR
(备份程序状态寄存器):每种异常模式(如快速中断模式、中断模式等)都有对应的 SPSR,用于在进入异常时保存 CPSR 的值,以便在异常返回时恢复原来的状态。
CPSR(当前程序状态寄存器) :
CPSR 包含了条件标志位(N 标志表示结果为负,Z 标志表示结果为零,C 标志表示进位或借位,V 标志表示溢出)、控制位(如中断禁止位)和模式位等。它反映了当前处理器的运行状态和模式。例如,根据 N 位可以判断结果是负数还是正数,通过中断禁止位可以控制是否响应中断。
控制寄存器
- 在一些特定的 ARM 架构扩展中,可能存在用于控制特定功能的寄存器,如控制缓存、内存管理单元(MMU)等的相关寄存器。
4.ARM异常处理
常见异常源
异常源名称 | 触发条件 | 作用 |
---|---|---|
复位(Reset) | 系统上电或外部硬件手动触发复位等 | 将 PC 设置为复位向量地址、关闭中断、初始化处理器寄存器到默认状态 |
未定义指令(Undefined Instruction) | 遇到无法识别的指令,如不存在或未实现的指令、未定义的协处理器指令等 | 软件模拟未定义的指令、链接到相关错误处理程序 |
软件中断(Software Interrupt,SWI) | 执行 SWI 指令,如程序需要请求操作系统服务等 | 实现系统调用、提供用户模式和特权模式切换机制 |
预取指中止(Prefetch Abort) | 处理器试图从内存中预取指令时因内存访问错误(如无效内存地址、内存系统故障等)无法成功获取指令 | 纠正内存访问错误、恢复程序正常执行 |
数据中止(Data Abort) | 处理器对数据访问时出现错误,如访问不存在的内存地址、内存保护机制阻止数据访问等 | 处理数据访问错误、保证数据完整性和系统稳定性 |
IRQ异常
IRQ(Interrupt Request)即中断请求,是一种硬件机制,用于让设备向处理器发出信号,表明它需要处理器的关注或服务。
- ARM处理器遇到IRQ异常时的自动动作
- 当 ARM 处理器遇到 IRQ 异常时,它会自动完成以下一系列动作:
- 保存当前程序状态寄存器 CPSR 的内容到备份程序状态寄存器 SPSR_irq 中。这样做是为了在异常处理完成后能够恢复到原来的程序状态。
- 将 CPSR 模式位 M[4:0]设置为 0b10010,即进入 IRQ 模式。这使得处理器处于一种特殊的运行模式,以便处理中断相关的操作。
- 禁止 IRQ 中断,即将 CPSR 的 I 位设置为 1。这样可以防止在处理当前中断时被新的 IRQ 中断打断,保证中断处理的完整性。
- 将程序计数器 PC 的值自动设置为异常向量表中 IRQ 异常对应的地址 0x00000018,从而开始执行 IRQ 异常处理程序。处理器会从这个地址开始取指令执行相应的中断处理代码。
- 当 ARM 处理器遇到 IRQ 异常时,它会自动完成以下一系列动作:
- 处理完 IRQ 异常后返回正常程序的过程
- 当 IRQ 异常处理程序完成后,需要返回到原来被中断的正常程序继续执行,其返回过程如下:
- 从 SPSR_irq 恢复 CPSR。即将备份的程序状态寄存器 SPSR_irq 的内容恢复到当前程序状态寄存器 CPSR 中,使得处理器恢复到原来的程序状态,包括处理器模式、中断允许状态等。
- 将返回地址从 LR_irq(链接寄存器,在进入异常时自动保存了返回地址)复制到程序计数器 PC 中。这样处理器就会跳转到被中断程序的下一条指令继续执行,从而回到正常程序的执行流程。
- 当 IRQ 异常处理程序完成后,需要返回到原来被中断的正常程序继续执行,其返回过程如下:
总之,ARM 处理器在遇到 IRQ 异常时会通过一系列自动动作进入异常处理模式,处理完异常后又通过特定的步骤返回到正常程序,以保证系统的稳定运行和对外部中断的正确响应。
5.ARM汇编指令集
ARM 汇编指令集中的数据处理指令主要用于对寄存器中的数据进行操作
数据传送指令
- MOV(数据传送指令)
- 指令格式:
MOV{条件}{S} 目的寄存器,操作数
- 功能:将操作数传送到目的寄存器中。如果设置 S 后缀,会根据传送的值更新 CPSR 中的标志位(但有一些限制和特殊情况)。
- 例如:
MOV R22, #10
,将立即数 10 传送到 R22 中。
- 指令格式:
指令组成
指令组成部分 | 作用 |
---|---|
MOV | 操作码,基本功能是进行数据传送,将操作数的值传送到目的寄存器中 |
{条件} | 条件码,用于决定指令是否执行,不同条件码根据程序具体逻辑需求选择,以实现更精确控制,如 EQ(相等)条件,满足条件才执行指令 |
{S} | 后缀,当包含"S"时,会指示处理器根据指令执行结果更新 CPSR 中的标志位,如 Z、N、C、V 标志位;不包含"S"时,处理器不更新 CPSR 中的标志位 |
目的寄存器 | 指令操作的目标对象,数据将被传送到这个寄存器中,如 R5 等通用寄存器,用于存储数据或在程序执行中起不同作用 |
操作数 | 指令要传送的数据来源,可以是立即数(如#20),直接将值传送到目的寄存器;也可以是寄存器(如 R8),将该寄存器中的值传送到目的寄存器 |
立即数
在 ARM 汇编中,立即数是指令中的一个常量数值。
特点
- 直接编码在指令中
- 立即数是在指令编码时就确定的固定数值,它直接跟随在操作码和其他指令字段之后。例如在指令
MOV R0, #10
中,#10 就是立即数,它会被直接编码到指令的二进制格式中。
- 立即数是在指令编码时就确定的固定数值,它直接跟随在操作码和其他指令字段之后。例如在指令
在 ARM 汇编中,关于立即数的这种编码规则是有其特定原因和实现方式的。
规则原理
- 8 位常数基础
- ARM 指令集规定以一个 8 位的常数作为基础来构造可以使用的立即数。这 8 位常数可以表示 0 到 255 之间的数值(二进制的 00000000 到 11111111)。
- 循环右移偶数位
- 这个 8 位常数需要能够通过循环右移偶数位的方式得到最终要使用的 32 位立即数。循环右移是一种移位操作,与普通的右移不同,循环右移是将移出的位重新插入到另一端。例如,对于一个二进制数 10110110,循环右移 2 位后变为 10101101(最右边移出的 2 位 10 又重新插入到左端)。
- 并且要求只能循环右移偶数位,也就是 0 位(不移位)、2 位、4 位、6 位、8 位......28 位、30 位等情况。
实际编码过程示例
- 比如我们要得到立即数 0x3FC(十进制的 1020):
- 首先找到一个 8 位常数,这里可以选择 0xFF(十进制的 255)。
- 然后对 0xFF 进行循环右移操作。因为 0x3FC 可以通过 0xFF 循环右移 2 位得到。
- 0xFF 二进制形式为 11111111,循环右移 2 位后变为 111111111100(前面自动补 0 扩展到 32 位),即 0x3FC。所以在指令中就可以将 0x3FC 作为合法的立即数来使用。
- 再比如要得到立即数 0x10(十进制的 16):
- 选择 8 位常数 0x10(本身在 8 位范围内)。
- 它相当于 0x10 循环右移 0 位(不移位)得到的,所以 0x10 是合法的立即数。在指令中可以直接使用,例如
MOV R6, #0x10
。
三、规则的目的和好处
- 节省指令空间
- 通过这种方式,可以用相对较少的指令编码位来表示更多的立即数。如果不采用这种规则,要直接表示 32 位的任意立即数,可能需要更多的指令位来编码,这会增加指令的长度和程序的存储空间占用。而采用 8 位常数循环右移偶数位的方式,可以在一定程度上减少表示立即数所需的指令位数。
- 提高指令执行效率
- 对于 ARM 处理器来说,这种编码规则使得处理器在解析和执行包含立即数的指令时更加高效。处理器可以通过相对简单的电路和算法来处理这种符合规则的立即数,快速地将其应用到相应的运算中,而不需要处理过于复杂的任意 32 位立即数的解码和使用,从而提高了指令的执行速度和整个程序的运行效率。
算术运算指令
- ADD(加法指令)
- 指令格式:
ADD{条件}{S} 目的寄存器,操作数 1,操作数 2
- 功能:将操作数 1 和操作数 2 相加,并把结果保存到目的寄存器中。如果设置了 S 后缀,会根据结果更新 CPSR(当前程序状态寄存器)中的标志位 N、Z、C、V。
ADD R0, R1, R2
,R0 = R1 + R2ADD R0, R1, #3
,R0 = R1 + 3
- 指令格式:
- SUB(减法指令)
- 指令格式:
SUB{条件}{S} 目的寄存器,操作数 1,操作数 2
- 功能:用操作数 1 减去操作数 2,并将结果保存到目的寄存器中。设置 S 后缀时同样会更新 CPSR 中的标志位。
SUB R3, R4, R5
,R3 = R4 - R5SUB R3, R4, #3
,R3 = R4 - 3
- 指令格式:
- RSB(逆向减法指令)
- 指令格式:
RSB{条件}{S} 目的寄存器, 操作数1, 操作数2
- 功能:用操作数 2 减去操作数 1,并将结果保存到目的寄存器中。这与普通减法指令
SUB
不同的是操作数的顺序颠倒了,即原本是操作数1 - 操作数2,而RSB
是操作数2 - 操作数1。 RSB R0, R1, R2
,R0 = R2 - R1RSB R0, R1, #3
,R0 = R2 - 3
- 指令格式:
- MUL(乘法指令)
- 指令格式:
MUL{条件}{S} 目的寄存器,操作数 1,操作数 2
- 功能:将操作数 1 和操作数 2 相乘,并把结果的低 32 位保存到目的寄存器中。设置 S 后缀时会根据结果更新 CPSR 中的标志位,但乘法操作对标志位的影响相对复杂。
- 例如:
MUL R6, R7, R8
,将 R7 和 R8 相乘,结果的低 32 位保存到 R6 中。 - 注意:乘法指令只能用寄存器,不能用立即数
- 指令格式:
逻辑运算指令
- AND(逻辑与指令)
- 指令格式:
AND{条件}{S} 目的寄存器,操作数 1,操作数 2
- 功能:对操作数 1 和操作数 2 按位进行逻辑与操作,并将结果保存到目的寄存器中。设置 S 后缀可更新 CPSR 中的标志位。
- 例如:
AND R9, R10, R11
,将 R10 和 R11 按位进行与操作,结果保存到 R9 中。
- 指令格式:
- ORR(逻辑或指令)
- 指令格式:
ORR{条件}{S} 目的寄存器,操作数 1,操作数 2
- 功能:对操作数 1 和操作数 2 按位进行逻辑或操作,并将结果保存到目的寄存器中。S 后缀功能同 AND 指令中的 S 后缀。
- 例如:
ORR R12, R13, R14
,将 R13 和 R14 按位进行或操作,结果保存到 R12 中。
- 指令格式:
- EOR(异或指令)
- 指令格式:
EOR{条件}{S} 目的寄存器,操作数 1,操作数 2
- 功能:对操作数 1 和操作数 2 按位进行异或操作,并将结果保存到目的寄存器中。S 后缀功能类似。
- 例如:
EOR R15, R16, R17
,将 R16 和 R17 按位进行异或操作,结果保存到 R15 中。
- 指令格式:
比较指令
- CMP(比较指令)
- 指令格式:
CMP{条件} 操作数 1,操作数 2
- 功能:比较操作数 1 和操作数 2 的大小,实际是用操作数 1 减去操作数 2,但不保存结果,只是根据结果更新 CPSR 中的标志位。
- 例如:
CMP R18, R19
,比较 R18 和 R19 中的值大小,更新 CPSR 标志位。
- 指令格式:
- CMN(反值比较指令)
- 指令格式:
CMN{条件} 操作数 1,操作数 2
- 功能:将操作数 1 和操作数 2 相加,并根据结果更新 CPSR 中的标志位,用于比较两个数相加后的情况。
- 例如:
CMN R20, R21
,将 R20 和 R21 相加,根据结果更新 CPSR 标志位。
- 指令格式:
位移指令
逻辑左移指令(LSL)
- 指令格式
LSL{条件}{S} 目的寄存器, 操作数
- 功能
- 将目的寄存器中的值逻辑左移指定的位数(由操作数指定)。每左移一位,相当于将原数值乘以 2。
- 例如,
LSL R0, R1, #2
,将 R1 中的值逻辑左移 2 位,然后把结果保存到 R0 中。如果 R1 初始值为二进制的 00000101(十进制的 5),左移 2 位后变为 00010100(十进制的 20)。 - 当指令中包含"S"后缀时,会根据移位结果更新 CPSR 中的标志位。如果移位结果为 0,Z 标志位会被设置为 1;如果最高位(符号位)因移位而改变,N 标志位和 V 标志位也会相应更新等。
二、逻辑右移指令(LSR)
- 指令格式
LSR{条件}{S} 目的寄存器, 操作数
- 功能
- 将目的寄存器中的值逻辑右移指定的位数(由操作数指定)。每右移一位,相当于将原数值除以 2(忽略余数)。
- 例如,
LSR R2, R3, #3
,将 R3 中的值逻辑右移 3 位,结果保存到 R2 中。若 R3 初始值为二进制的 11001000(十进制的 200),右移 3 位后变为 00011001(十进制的 25)。 - 和 LSL 类似,带有"S"后缀时会更新 CPSR 中的标志位。
三、算术右移指令(ASR)
- 指令格式
ASR{条件}{S} 目的寄存器, 操作数
- 功能
- 与 LSR 不同的是,ASR 进行的是算术右移。对于有符号数,算术右移保持符号位不变。
- 例如,
ASR R4, R5, #2
,如果 R5 中的值为二进制的 10011010(假设是有符号数,十进制的 -102),算术右移 2 位后变为 11100110(十进制的 -26,符号位 1 保持不变),结果保存到 R4 中。 - 同样,"S"后缀可用于更新 CPSR 标志位。
四、循环右移指令(ROR)
- 指令格式
ROR{条件}{S} 目的寄存器, 操作数
- 功能
- 循环右移操作,将目的寄存器中的值循环右移指定的位数(由操作数指定)。
- 例如,
ROR R6, R7, #3
,假设 R7 初始值为二进制的 10110101,循环右移 3 位后变为 01011011,结果保存到 R6 中。 - 带有"S"后缀时会更新 CPSR 标志位。
位清除指令
- 指令格式
BIC{条件}{S} 目的寄存器, 操作数1, 操作数2
- 功能
- 基本运算
- 它将操作数1中的位与操作数2的反码按位进行与操作,结果保存到目的寄存器中。简单来说,就是将操作数1中与操作数2中对应位为1的位清零,而操作数1中与操作数2中对应位为0的位保持不变。
- 例如
BIC R0, R1, R2
,假设 R1 的二进制值为 10101100,R2 的二进制值为 11001010,那么 R2 的反码就是 00110101。将 R1 与 R2 的反码按位进行与操作,得到的结果是 10100100,这个结果会保存到 R0 中。
- 带条件执行和标志位更新
- 可以根据条件码有条件地执行,和其他数据处理指令类似。例如
BICNE R3, R4, R5
,只有当之前的某些运算结果使得条件码 NE(不相等)对应的条件满足时(也就是 Z 标志位为 0),才会执行上述的位清除操作并将结果保存到 R3 中。 - 当指令中包含"S"后缀时,会根据指令执行的结果更新 CPSR(当前程序状态寄存器)中的标志位 N、Z、C、V。如果指令中不包含"S"后缀,则不会更新 CPSR 中的标志位。
- 可以根据条件码有条件地执行,和其他数据处理指令类似。例如
- 基本运算
CPSR寄存器
在 ARM 体系结构中,CPSR(Current Program Status Register,当前程序状态寄存器)是一个非常重要的寄存器。
作用
- 反映处理器状态
- CPSR 包含了条件标志位、中断禁止位、当前处理器模式位等多种状态信息。它实时反映了处理器当前的运行状态,程序可以根据 CPSR 中的这些状态信息来进行不同的操作和决策。
- 控制处理器行为
- 通过设置 CPSR 中的某些位,可以控制处理器的行为。例如,设置中断禁止位可以控制是否响应外部中断,设置处理器模式位可以切换处理器的运行模式(如用户模式、系统模式、管理模式等)。
组成部分
- 条件标志位
- N(Negative)标志位 :当进行有符号数运算结果为负数时,N 标志位被设置为 1;结果为正数或 0 时,N 标志位为 0。例如,在执行
SUB R0, R1, R2
减法指令后,如果结果是一个负数,N 标志位就会被置 1。 - Z(Zero)标志位 :当运算结果为 0 时,Z 标志位被设置为 1;结果不为 0 时,Z 标志位为 0。比如在执行
CMP R3, R4
比较指令后,如果 R3 和 R4 的值相等,那么相关的运算结果为 0,Z 标志位就会置 1。 - C(Carry)标志位 :在无符号数运算中,它用于表示进位或借位状态。例如在执行加法
ADD R5, R6, R7
时,如果产生了进位,C 标志位就会被置 1;在执行减法SUB R8, R9, R10
时,如果产生了借位(不够减),C 标志位也会有相应的变化(具体取决于处理器实现,可能会被清 0 或设置为其他值来表示借位情况)。 - V(Overflow)标志位:在有符号数运算中,它用于表示溢出情况。当有符号数运算结果发生溢出时,V 标志位被设置为 1;没有溢出时,V 标志位为 0。例如,在两个有符号数相加时,如果结果超出了能够表示的有符号数范围,就会发生溢出,V 标志位就会相应设置。
- N(Negative)标志位 :当进行有符号数运算结果为负数时,N 标志位被设置为 1;结果为正数或 0 时,N 标志位为 0。例如,在执行
- 中断禁止位
- 包括 I(IRQ 中断禁止)位和 F(FIQ 中断禁止)位。
- 当 I 位被设置为 1 时,处理器将不响应外部 IRQ(普通中断)请求;当 I 位为 0 时,处理器可以正常响应 IRQ 中断。类似地,F 位控制处理器对 FIQ(快速中断)的响应。例如,在某些关键代码段执行时,为了防止被中断干扰,可以设置 I 位和 F 位为 1,禁止中断,执行完关键代码后再将它们设置为 0 以恢复中断响应。
- 处理器模式位
- ARM 处理器有多种运行模式,如用户模式(User Mode)、系统模式(System Mode)、管理模式(Supervisor Mode)、中止模式(Abort Mode)、未定义模式(Undefined Mode)、中断模式(IRQ Mode)和快速中断模式(FIQ Mode)等。CPSR 中的模式位用于指示当前处理器所处的运行模式。不同的模式具有不同的权限和功能,例如用户模式权限最低,主要用于运行普通的应用程序代码;而管理模式权限较高,用于操作系统内核等关键代码的执行。处理器在不同模式下可以访问不同的资源和执行不同的操作。
在程序中的应用
- 条件判断和分支
- 程序可以根据 CPSR 中的条件标志位来进行条件判断和分支。例如,使用条件码结合条件标志位来决定是否执行某条指令。如前面提到的
ADDEQ R0, R1, R2
,就是根据 Z 标志位是否为 1(即相等条件)来决定是否执行加法操作。
- 程序可以根据 CPSR 中的条件标志位来进行条件判断和分支。例如,使用条件码结合条件标志位来决定是否执行某条指令。如前面提到的
- 中断处理
- 通过设置和读取 CPSR 中的中断禁止位,程序可以控制中断的响应和禁止。在进入一些关键代码段时禁止中断,确保代码执行的原子性和稳定性,在合适的时候再恢复中断响应以处理外部事件。
- 处理器模式切换
- 在操作系统等复杂程序中,可能需要在不同的处理器模式之间切换。例如,从用户模式切换到管理模式来执行一些特权操作,然后再切换回用户模式。程序可以通过修改 CPSR 中的模式位来实现这种模式切换,但这种切换需要遵循一定的规则和流程以确保系统的稳定性和安全性。
进位加法和借位减法
在 ARM 汇编中,有涉及进位和借位的相关指令。
带进位的加法指令(ADC)
- 指令格式
ADC{条件}{S} 目的寄存器, 操作数1, 操作数2
- 功能
- 它将操作数1、操作数2以及进位标志位 C 的值相加,并将结果保存到目的寄存器中。
- 例如,假设初始时 R0 的值为 5(二进制 00000101),R1 的值为 3(二进制 00000011),进位标志位 C 初始为 0。执行
ADC R2, R0, R1
后,先计算 5+3+0 = 8(二进制 00001000),结果保存到 R2 中。如果之后又有一次计算,R3 的值为 7(二进制 00000111),R4 的值为 9(二进制 00001001),进位标志位 C 此时为 1(假设是上一次计算产生了进位)。执行ADC R5, R3, R4
,则计算 7+9+1 = 17(二进制 00010001),结果保存到 R5 中。 - 当指令中包含"S"后缀时,会根据计算结果更新 CPSR 中的标志位 N、Z、C、V。例如,如果计算结果为 0,Z 标志位会被设置为 1;如果结果是负数,N 标志位会被设置为 1 等。
带借位的减法指令(SBC)
- 指令格式
SBC{条件}{S} 目的寄存器, 操作数1, 操作数2
- 功能
- 它用操作数1减去操作数2再减去借位标志位 C 的反码(若 C 为 0,则减去 1 的反码即 0xFFFFFFFF;若 C 为 1,则减去 0 的反码即 0x00000000),并将结果保存到目的寄存器中。
- 例如,假设初始 R6 的值为 10(二进制 00001010),R7 的值为 6(二进制 00000110),借位标志位 C 初始为 0。执行
SBC R8, R6, R7
时,计算 10 - 6 - 1(因为 C 初始为 0,减去 1 的反码即减去 1)= 3(二进制 00000011),结果保存到 R8 中。如果之后又有一次计算,R9 的值为 15(二进制 00001111),R10 的值为 8(二进制 00001000),借位标志位 C 此时为 1(假设上一次计算结果没有产生借位,这里设置 C 为 1)。执行SBC R11, R9, R10
,则计算 15 - 8 - 0(因为 C 为 1,减去 0 的反码即减去 0)= 7(二进制 00000111),结果保存到 R11 中。 - 当指令中包含"S"后缀时,同样会根据计算结果更新 CPSR 中的标志位。
用途
- 多精度算术运算
- 在进行较大数值的加法或减法运算时,当数值超出了单个寄存器所能表示的范围时,带进位和借位的指令就非常有用。可以通过多个寄存器联合使用,利用进位和借位标志位来实现多精度的算术运算,例如对大整数的计算等。
- 循环和迭代计算
- 在一些循环或迭代的计算中,每次计算的结果可能会影响到下一次计算的进位或借位情况。带进位和借位的指令可以准确地处理这种连续计算中的进位和借位关系,确保计算结果的正确性。例如在一些算法中,需要对一系列数据进行连续的加法或减法操作,并且要考虑进位和借位的影响。
- 位操作和数据处理
- 在某些特定的位操作和数据处理任务中,可能需要精确控制进位和借位来实现特定的功能。例如在一些数据编码、校验等操作中,利用带进位和借位的指令可以更灵活地处理数据的位模式和计算关系。
跳转指令
B指令(Branch)
- 指令格式
B{条件} 目标地址
- 功能
- 它是简单的跳转指令,使程序无条件或有条件地跳转到指定的目标地址执行代码。目标地址通常是一个相对当前指令地址的偏移量。
- 例如,
BEQ label
,如果 Z 标志位为 1(即相等条件满足),程序就会跳转到名为label
的代码位置执行。B label
则是无条件跳转到label
处。
- 特点
- B 指令的跳转范围相对有限,它的目标地址计算是基于当前 PC(程序计数器)值和一个相对偏移量。偏移量是一个有符号的 24 位数值,通过对其进行符号扩展并左移 2 位后与当前 PC 值相加得到目标地址。这意味着 B 指令能跳转的范围大约是前后 32MB。
BL指令(Branch with Link)
- 指令格式
BL{条件} 目标地址
- 功能
- 它不仅实现程序的跳转,还会在跳转之前将下一条指令的地址保存到链接寄存器(R14)中。这使得程序在执行完跳转目标处的代码后,可以通过将链接寄存器的值赋回 PC 来返回到跳转指令的下一条指令继续执行。
- 例如,
BL function
,程序会跳转到名为function
的代码段执行,同时将当前跳转指令的下一条指令地址保存到 R14 中。当function
中的代码执行完毕后,可以通过MOV PC, R14
这样的指令返回到原来的代码位置继续执行后面的代码。
- 用途
- 常用于函数调用。在一个程序中,当需要调用另一个函数(代码段)来执行一些特定任务时,使用 BL 指令可以方便地实现函数调用和返回。例如在一个操作系统内核中,可能会使用 BL 指令来调用不同的设备驱动程序函数来处理硬件设备的操作。
BX指令(Branch and Exchange)
- 指令格式
BX{条件} 目标寄存器
- 功能
- 它根据目标寄存器的最低位来决定程序的跳转地址,并且可以实现处理器运行模式的切换(如果目标地址在不同的地址空间)。
- 如果目标寄存器的最低位为 0,程序将跳转到目标寄存器保存的地址执行,并且处理器保持在 ARM 状态。如果目标寄存器的最低位为 1,程序将跳转到目标寄存器保存的地址执行,并且处理器切换到 Thumb 状态(如果支持 Thumb 指令集)。
- 例如,
BX R10
,如果 R10 的最低位为 0,程序跳转到 R10 中存储的地址(假设该地址处的代码为 ARM 指令集代码)执行;如果 R10 的最低位为 1,程序跳转到 R10 中存储的地址(假设该地址处的代码为 Thumb 指令集代码)执行,并且处理器切换到 Thumb 状态。
- 应用场景
- 在一些混合使用 ARM 和 Thumb 指令集的程序中,BX 指令可以方便地在不同指令集代码之间进行切换。例如,当程序需要在性能敏感的部分使用 ARM 指令集,而在代码尺寸敏感的部分使用 Thumb 指令集时,可以通过 BX 指令根据需要在两种指令集之间切换执行代码。同时,在一些需要动态切换处理器运行模式的场景中,BX 指令也能发挥作用。
条件码
常见条件码及其含义
- EQ(Equal,相等)
- 当 Z 标志位(零标志位)被设置为 1 时,即上一次算术或逻辑运算结果为 0 时,指令执行。
- 例如,
ADDEQ R0, R1, R2
只有在满足相等条件(如之前的相关运算结果为 0)时才会执行加法操作并将结果存入 R0。
- NE(Not Equal,不相等)
- 当 Z 标志位为 0 时,即上一次算术或逻辑运算结果不为 0 时,指令执行。
- 例如,
SUBNE R3, R4, R5
在前面比较的结果是两个数不相等时,就会执行减法操作。
- GT(Greater Than,大于)
- 当 N 标志位(负数标志位)和 V 标志位(溢出标志位)相同,且 Z 标志位为 0 时,指令执行。这通常用于有符号数的比较。
- 例如,
MOVGT R6, #100
在比较两个有符号数的大小后,如果结果是一个数大于另一个数,就会将立即数 100 传送到 R6 中。
- LT(Less Than,小于)
- 当 N 标志位和 V 标志位不相同,且 Z 标志位为 0 时,指令执行。常用于有符号数比较判断小于的情况。
- 比如,
MUL LT R7, R8, R9
在对两个有符号数进行比较操作后,如果结果是一个数小于另一个数,就会执行乘法操作并将结果的低 32 位保存到 R7 中(前提是满足小于条件)。
- GE(Greater Than or Equal,大于或等于)
- 当 N 标志位和 V 标志位相同时,指令执行。用于有符号数的大于或等于判断。
- 例如,
ORRGE R10, R11, R12
在满足大于或等于条件时,会对 R11 和 R12 进行按位或操作并将结果保存到 R10 中。
- LE(Less Than or Equal,小于或等于)
- 当 N 标志位和 V 标志位不同,或者 Z 标志位为 1 时,指令执行。用于有符号数的小于或等于判断。
- 例如,
AN DLE R13, R14, R15
在满足小于或等于条件时,会对 R14 和 R15 进行按位与操作并将结果保存到 R13 中。
- CS(Carry Set,无符号数大于或等于,即进位标志位 C 被设置为 1)
- 例如,
ADCCS R26, R27, R28
当无符号数比较结果是大于或等于时(进位标志位 C 被设置为 1),该指令会执行加法操作并将结果存到 R26 中。
- 例如,
- CC(Carry Clear,无符号数小于,即进位标志位 C 被清除为 0)
- 例如,
SUBCC R34, R35, R36
当无符号数比较结果是小于时(进位标志位 C 被清除为 0),该指令会执行减法操作并将结果存到 R34 中。
- 例如,
内存访问指令
加载指令(LDR)
- 指令格式
LDR{条件}{类型} 目的寄存器, [地址表达式]
- 类型可以是 B(字节)、SB(有符号字节)、H(半字,2 字节)、SH(有符号半字)、W(字,4 字节,这是最常用的默认类型)等。
- 功能
- 从指定的内存地址读取数据,并将其加载到目的寄存器中。
- 例如,
LDR R0, [R1]
,将以 R1 寄存器中的值作为内存地址,从该内存地址读取一个 32 位字(如果没有指定类型,默认是字)的数据,并将其保存到 R0 寄存器中。 LDRB R2, [R3]
则是从以 R3 中的值为地址的内存中读取一个字节的数据,并将其保存到 R2 寄存器中,同时将该字节数据符号扩展到 32 位(因为 R2 是 32 位寄存器)。
- 加载立即偏移的变体(LDR with immediate offset)
LDR{条件}{类型} 目的寄存器, [基址寄存器, 立即偏移]
- 例如,
LDR R4, [R5, #4]
,将以 R5 中的值加上立即数 4 得到的地址作为内存地址,从该地址读取一个 32 位字的数据并保存到 R4 寄存器中。
存储指令(STR)
- 指令格式
STR{条件}{类型} 源寄存器, [地址表达式]
- 类型与 LDR 指令类似。
- 功能
- 将源寄存器中的数据存储到指定的内存地址中。
- 例如,
STR R6, [R7]
,将 R6 寄存器中的 32 位字数据存储到以 R7 寄存器中的值为地址的内存中。 STRH R8, [R9, #2]
,将 R8 寄存器中的低 16 位数据(半字)存储到以 R9 中的值加上立即数 2 得到的内存地址中。
批量加载指令(LDM)和批量存储指令(STM)
- 批量加载指令(LDM)格式
LDM{条件}{模式} 基址寄存器!, {寄存器列表}
- 模式可以是 IA(Increment After,每次传送后基址寄存器值增加)、IB(Increment Before,每次传送前基址寄存器值增加)、DA(Decrement After,每次传送后基址寄存器值减小)、DB(Decrement Before,每次传送前基址寄存器值减小)等。
- 例如,
LDMIA R10!, {R0-R3}
,以 R10 中的值为基址,从内存中顺序读取 4 个 32 位字数据,分别存储到 R0、R1、R2、R3 寄存器中,并且在每次读取后将 R10 的值增加 4(因为每个字是 4 字节)。
- 批量存储指令(STM)格式
STM{条件}{模式} 基址寄存器!, {寄存器列表}
- 例如,
STMDB R11!, {R4-R7}
,以 R11 中的值为基址,将 R4 到 R7 这 4 个寄存器中的 32 位字数据顺序存储到内存中,并且在每次存储前将 R11 的值减小 4。
内存访问指令的用途
- 数据读写操作
- 在程序中,经常需要从内存中读取数据进行处理,或者将处理后的数据写回内存。例如,在一个图像编辑程序中,需要从内存中读取图像数据到寄存器中进行处理(如颜色调整、滤波等操作),然后再将处理后的图像数据写回内存。
- 数据结构的操作
- 对于数组、结构体等数据结构,内存访问指令可以方便地实现对其元素的读写操作。例如,对于一个结构体数组,可以使用内存访问指令逐个读取结构体成员的数据进行处理。
- 栈操作
- 在函数调用和返回过程中,需要使用内存访问指令来实现栈的操作。例如,将函数的参数、局部变量等数据压入栈中(使用 STM 指令),在函数返回时从栈中弹出这些数据(使用 LDM 指令)。
- 与外部设备交互
- 内存映射的外部设备可以通过内存访问指令来进行读写操作。例如,读取某个传感器的数据到寄存器中进行分析,或者将控制命令写入到与设备对应的内存地址来控制设备的运行。
ARM指令寻址方式
寻址方式 | 说明 | 示例 |
---|---|---|
立即寻址 | 操作数本身就在指令中,即立即数 | MOV R0, #10,#10 是立即数,将 10 传送到 R0 中 |
寄存器寻址 | 操作数存放在寄存器中,指令给出寄存器编号 | ADD R0, R1, R2 将 R1 和 R2 的内容相加结果存到 R0 中 |
寄存器间接寻址 | 寄存器中存放的是操作数的地址,操作数存放在内存中 | LDR R0, [R1],R1 中存放内存地址,将该地址指向的内存单元数据加载到 R0 中 |
基址变址寻址 | 将基址寄存器的内容与偏移量相加,得到操作数的有效地址 | LDR R0, [R1, #4],R1 是基址寄存器,#4 是偏移量,将 R1 内容加 4 得到的地址指向的内存单元数据加载到 R0 中 |
相对寻址 | 以程序计数器 PC 的当前值为基地址,指令中的地址码字段作为偏移量,两者相加得到操作数的有效地址 | BL 指令(带链接的跳转)等 |
堆栈寻址 | ARM 支持堆栈操作,堆栈寻址是特殊寻址方式,堆栈有满递减堆栈(FD)、空递减堆栈(ED)等类型 | PUSH 和 POP 指令用于在堆栈中进行数据的入栈和出栈操作,操作数地址通过堆栈指针 SP 与相应操作确定 |
基址变址寻址索引方式
类型 | 定义和操作方式 | 应用场景 |
---|---|---|
前索引 | 先将偏移量与基址寄存器的值相加得到有效地址,从该有效地址读取数据到指定寄存器中,并且在读取操作完成后,将基址寄存器的值更新为相加后的结果(如指令LDR R0, [R1, #4]! ,这里! 表示前索引) |
当需要顺序访问一块连续的内存区域,并且每次访问后自动更新基址寄存器以便指向下一个地址时,如遍历数组等 |
后索引 | 先从基址寄存器所指向的地址读取数据到指定寄存器中,然后将基址寄存器的值更新为基址寄存器值加上偏移量(如指令LDR R0, [R1], #4 ,这里没有! ,表示后索引) |
当需要先获取当前地址的数据,然后再移动到下一个地址的情况,如处理链表结构等 |
自动索引 | 强调在基址变址寻址过程中,基址寄存器能够根据一定的规则自动更新,前索引和后索引都属于自动索引范畴 | 在一些需要重复访问相邻内存区域的算法和数据结构操作中,如在循环中对一段连续内存区域进行操作时,可提高代码效率和简洁性 |
栈的应用举例
assembly
MOV SP, #0x40000020
@ 将栈指针(SP)设置为 0x40000020,定义了栈的起始地址
MIAN:
@ 定义一个名为 MIAN 的代码标签
MOV R1, #3
@ 将立即数 3 赋值给 R1 寄存器
MOV R2, #5
@ 将立即数 5 赋值给 R2 寄存器
@调用FUNC子程序
BL FUNC
@ 使用 BL(Branch with Link)指令调用 FUNC 子程序,
@ 同时将下一条指令(ADD R3, R1, R2)的地址保存到链接寄存器 LR 中
ADD R3, R1, R2
@ 将 R1 和 R2 的值相加,并将结果保存到 R3 寄存器
B STOP
@ 跳转到 STOP 标签处(无限循环,停止程序进一步执行)
FUNC:
@ 定义一个名为 FUNC 的子程序标签
@压栈保护现场
STMFD SP!, {R4-R11, LR}
@ 将 R4 到 R11 寄存器以及链接寄存器 LR 的值压入堆栈,
@ 先递减栈指针 SP,然后存储这些寄存器的值。
@ 这是为了保护在 FUNC 子程序中可能被修改的这些寄存器的值
MOV R1, #10
@ 将立即数 10 赋值给 R1 寄存器(在 FUNC 子函数内重新赋值)
MOV R2, #20
@ 将立即数 20 赋值给 R2 寄存器(在 FUNC 子函数内重新赋值)
SUB R3, R2, R1
@ 用 R2 减去 R1 的值,并将结果保存到 R3 寄存器(在 FUNC 子函数内进行计算)
@出栈恢复现场
LDMFD SP!, {R4-R11, PC}
@ 从堆栈中弹出之前压入的 R4 到 R11 寄存器以及程序计数器 PC 的值,
@ 先递增栈指针 SP,然后将这些值加载回相应的寄存器。
@ 其中将弹出的返回地址加载到 PC 中,实现返回到调用 FUNC 的地方继续执行
STOP:
@ 定义一个名为 STOP 的代码标签
B STOP
@ 无限循环,停止程序进一步执行(可以理解为程序在这里停止运行,等待其他事件)
伪指令
比较项目 | 指令 | 伪指令 |
---|---|---|
定义与功能 | 处理器能够直接执行的操作命令,进行实际的数据运算、存储器读写、控制程序流程等操作 | 本身不是真正处理器指令,用于告诉汇编器如何进行汇编操作,如定义数据、分配存储单元、指定程序起始地址等 |
执行方式 | 程序运行时被处理器逐个取指、译码并执行 | 在汇编阶段由汇编器进行处理 |
对程序运行的影响 | 直接决定程序运行时处理器的具体操作和数据处理流程,通过改变处理器内部状态实现程序功能 | 主要影响程序组织结构和汇编过程,帮助组织代码、定义数据和符号,使目标代码更符合预期结构和功能 |
1. NOP
表示空操作,不执行任何实际功能,通常用于短时间的延迟或填充代码。
2. IF
、ELSE
、ENDIF
用于条件编译。例如:
assembly
IF {CONDITION}
@ 条件为真时执行的代码
ELSE
@ 条件为假时执行的代码
ENDIF
3. WHILE
、WEND
用于实现循环结构。例如:
assembly
COUNT EQU 5
REG EQU 0
WHILE COUNT > 0
@ 循环体中的代码
SUB COUNT, COUNT, #1
WEND
4. MACRO
、MEND
用于定义宏。例如:
assembly
MACRO
MY_MACRO $ARG1, $ARG2
@ 宏的主体
MEND
@ 使用宏
MY_MACRO 10, 20
5. LTORG
用于声明一个文字池,存放之前使用 LDR
伪指令加载的立即数。
6. AREA
用于定义一个代码段或数据段。例如:
assembly
AREA MyData, DATA, READWRITE
这定义了一个名为 MyData
的数据段,具有读写属性。
7. DCB
用于分配一段字节类型的存储单元,并初始化其值。例如:
assembly
DCB 0x10, 0x20, 0x30
8. DCD
分配一段 32 位字类型的存储单元,并初始化其值。例如:
assembly
DCD 0x12345678, 0x87654321
9. SPACE
分配一段未初始化的存储空间。例如:
assembly
SPACE 100
这将分配 100 个字节的未初始化空间。
10. ENTRY
指定程序的入口点。例如:
assembly
ENTRY
11. EXPORT
和 IMPORT
EXPORT
用于将符号导出,使其能被其他模块使用;IMPORT
用于导入其他模块中的符号。例如:
assembly
EXPORT MyFunction
IMPORT AnotherModuleFunction
ATPCS协议
ATPCS(ARM-Thumb Procedure Call Standard,ARM-Thumb 过程调用标准)是一套用于 ARM 体系结构中函数调用的规则和约定。
它主要包括以下几个方面的内容:
- 寄存器的使用规则 :规定了哪些寄存器用于传递参数,哪些用于保存返回值等。例如,前 4 个参数通过
r0 - r3
传递,多余的参数通过栈来传递。 - 栈的使用方式:明确了栈的增长方向(可以是递增或递减)以及栈指针的操作。
- 参数和返回值的类型和大小处理:例如,对于 32 位的整数参数和返回值的处理方式。
ATPCS 有助于确保不同的函数在相互调用时能够正确地传递参数和返回值,保证程序的正确性和可移植性。
例如,在一个函数调用中,如果参数较多,前 4 个参数通过 r0 - r3
传递,第 5 个参数开始就会被压入栈中。在函数返回时,如果返回值是 32 位整数,通常会存放在 r0
中。
所以在定义函数时尽量将参数控制在四个以内,确保程序快速运行。
C 和汇编混合编程
C 语言和汇编语言的混合编程在许多场景中都非常有用。
在实现混合编程时,通常有以下几种方式:
1. 在 C 语言中嵌入汇编语句
通过特定的编译器指令,在 C 代码中直接插入汇编语句。例如,在 GCC 编译器中,可以使用 asm
关键字。
c
void myFunction() {
int a = 10;
asm("mov r0, #%0" : : "r"(a)); // 这是嵌入的汇编语句,将变量 a 的值赋给寄存器 r0
}
2. 从 C 语言调用汇编函数
先编写汇编函数,然后在 C 语言中声明并调用。
汇编函数示例:
armasm
.global asmFunction
asmFunction:
mov r0, #5
bx lr
C 语言调用部分:
c
extern int asmFunction(); // 声明外部的汇编函数
void callFunction() {
int result = asmFunction(); // 调用汇编函数
}
3. 在汇编中调用 C 函数
汇编代码需要按照 ATPCS 规则来处理参数传递和返回值。
例如,如果 C 函数的定义如下:
在汇编中调用可能类似于:
armasm
mov r0, #10
mov r1, #20
bl add // 调用 C 函数
混合编程常用于以下情况:
- 对性能要求极高的关键部分,使用汇编进行优化。
- 直接操作硬件寄存器或实现特定的底层操作。