当前位置: 首页 > news >正文

ARM 架构核心知识笔记(整理与补充版)

ARM 架构核心知识笔记(整理与补充版)

一、ARM CPU 与内存、外设的交互逻辑

核心原理

ARM CPU 通过地址(addr) 与外部设备交互,所有交互需经过内存控制器(memory controller) ;内存控制器根据 CPU 发出的地址范围,选择访问目标设备(内存、UART、USB 控制器等)。关键特性:内存与 I/O(外设)访问方式统一—— 在 CPU 视角,操作内存和操作外设无本质区别,均通过 “发送地址→内存控制器译码→访问目标设备” 的流程实现,所有设备共享同一地址空间。

补充说明

  • 地址范围由芯片厂商预先划分,例如:0x00000000~0x0FFFFFFF 分配给 Flash 内存,0x20000000~0x2000FFFF 分配给 SRAM,0x40000000~0x4000FFFF 分配给 UART 外设。
  • 内存控制器的核心作用是 “地址译码”,确保不同地址对应唯一设备,避免访问冲突。

二、ARM 的 RISC 架构特点

ARM 芯片属于精简指令集计算机(RISC) ,指令设计简洁,核心特点与运算流程如下:

1. RISC 核心特点

  • 对内存仅支持 “读(Load)” 和 “写(Store)” 两类指令,无直接对内存的运算指令。
  • 数据运算(加减乘除、逻辑运算)均在 CPU 内部的ALU(算术逻辑单元) 中完成,需先将内存数据读入 CPU 寄存器。
  • CPU 硬件复杂度低,易于设计和优化,指令执行周期固定,适合流水线操作。

2. 乘法运算(a = a * b)指令流程(用户代码补充注释)

以 “a 和 b 存储在内存,结果写回 a” 为例,需 4 条汇编指令,流程如下:

arm

; 1. 从内存地址a处,读取数据到CPU寄存器(暂存,为运算做准备)
LDR R0, [a]  ; R0 = 内存[a](即a的原始值)
; 2. 从内存地址b处,读取数据到另一寄存器
LDR R1, [b]  ; R1 = 内存[b](即b的值)
; 3. CPU内部ALU执行乘法运算,结果存入R0(覆盖原a的值)
MUL R0, R0, R1  ; R0 = R0 * R1(a*b的结果)
; 4. 将运算结果从寄存器写回内存地址a,更新a的值
STR R0, [a]  ; 内存[a] = R0(完成a = a*b的更新)

三、ARM CPU 内部寄存器结构

ARM CPU(如 Cortex-M3/M4/A7)内部包含R0~R15 共 16 个通用寄存器(32 位宽),部分寄存器具有特殊功能,需按 “普通寄存器” 和 “特殊功能寄存器” 分类理解。

1. 通用寄存器(R0~R15)

(1)普通数据寄存器
  • 低寄存器(Low Registers):R0~R7,所有指令均支持访问,是最常用的临时数据存储寄存器(如传递函数参数、暂存运算中间结果)。
  • 高寄存器(High Registers):R8~R12,仅部分指令支持访问(如 32 位 Thumb2 指令),通常用于存储局部变量或不常用数据。补充:R12 又称IP(Internal Procedure Register,内部过程寄存器),在函数调用时临时保存中间结果,用户代码未提及,需注意其辅助作用。
(2)特殊功能寄存器(R13~R15)
寄存器别名核心功能补充说明
R13SP栈指针,管理栈空间的当前地址分主栈指针(SP_main)和进程栈指针(SP_process),适配操作系统多任务场景
R14LR链接寄存器,保存函数返回地址函数调用(如 BL 指令)时,自动将 “下一条指令地址” 存入 LR;返回时需通过 “MOV PC, LR” 或 “BX LR” 恢复执行流程
R15PC程序计数器,指示下一条要执行的指令地址PC 的值始终比当前执行指令地址大 4(ARM 指令)或 2(Thumb 指令),修改 PC 即可实现程序跳转

2. 加法运算(a = a + b)指令示例(用户代码补充注释)

arm

; 场景:a(内存地址0x12)、b(内存地址0x34),结果写回a
LDR R0, [a]    ; 1. 读取内存a的值(0x12处数据)到R0
LDR R1, [b]    ; 2. 读取内存b的值(0x34处数据)到R1
ADD R0, R0, R1 ; 3. CPU内部ALU执行加法:R0 = R0 + R1(a+b结果)
STR R0, [a]    ; 4. 结果写回内存a:0x12处数据更新为R0的值

四、Cortex-M3/M4 的程序状态寄存器(xPSR)

xPSR(Program Status Register)是记录 CPU 运行状态的核心寄存器,并非单个寄存器,而是由APSR、IPSR、EPSR 三个子寄存器组合而成,支持单独或整体访问。

1. xPSR 的结构拆分

子寄存器全称核心功能
APSRApplication PSR(应用状态寄存器)记录应用层运算状态,如结果正负(N)、是否为零(Z)、进位(C)、溢出(V)等标志
IPSRInterrupt PSR(中断状态寄存器)记录当前正在处理的中断 / 异常编号(0~255),CPU 通过该编号找到中断服务函数入口
EPSRExecution PSR(执行状态寄存器)记录指令执行相关状态,如 Thumb 模式标志(T 位,Cortex-M 系列始终为 1,清除会触发异常)、条件执行标志(ICI/IT 位)

2. xPSR 的访问方式(MRS/MSR 指令)

xPSR 需通过MRS(读特殊寄存器) 和MSR(写特殊寄存器) 指令访问,支持 “单独访问子寄存器” 和 “整体访问组合寄存器” 两种方式:

(1)单独访问子寄存器

arm

; 读取APSR到通用寄存器R0(获取运算标志)
MRS R0, APSR  
; 读取IPSR到R0(获取当前中断编号)
MRS R0, IPSR  
; 将R0的值写入APSR(修改运算标志,如清除溢出标志)
MSR APSR, R0  
(2)整体访问组合寄存器(PSR)

arm

; 一次性读取APSR+IPSR+EPSR的组合状态到R0
MRS R0, PSR  
; 一次性将R0的值写入组合PSR(整体配置CPU状态,需谨慎使用)
MSR PSR, R0  

3. xPSR 关键位域含义(补充用户表格细节)

位域符号含义应用场景
31N负标志:运算结果为负时置 1判断减法结果是否为负(如比较两个数大小)
30Z零标志:运算结果为 0 时置 1循环判断(如 SUBS R0,R0,#1 后,BNE Loop)
29C进位 / 借位标志:加法有进位或减法无借位时置 1多字节加法(如 64 位数据运算)、无符号数比较
28V溢出标志:运算结果超出数据表示范围时置 1有符号数运算(如 int8_t 类型计算 127+1 时置 1)
8TThumb 模式标志:Cortex-M 系列始终为 1清除 T 位会触发 “未定义指令异常”,不可修改
4~0-异常编号:IPSR 中的位,指示当前中断编号中断服务函数中判断中断来源(如 UART 中断编号为 28)

补充说明

  • MSR 指令仅能修改 APSR 和 EPSR 的部分位,IPSR(中断编号)由硬件自动更新,软件无法手动修改。
  • xPSR 的状态会影响条件指令的执行(如 BEQ、BNE 等),是程序分支控制的核心依据。

五、ARM CPU 核心操作类型

ARM CPU 的所有功能可归纳为 4 类核心操作,覆盖数据处理、存储交互、流程控制的全场景:

操作类型核心功能常用指令 / 模块补充说明
内存 R/W与内存进行数据读写LDR(读)、STR(写)、LDM(批量读)、STM(批量写)所有内存操作均需通过寄存器中转,无直接内存运算指令(RISC 特性)
运算数据的数学 / 逻辑运算数学运算:ADD、SUB、MUL;逻辑运算:AND、ORR、EOR运算均在 CPU 内部 ALU 完成,运算对象必须是寄存器(或立即数)
跳转 / 分支改变程序执行顺序无条件跳转:B;带返回跳转:BL;指令集切换跳转:BX、BLX跳转地址可通过标签(如 B Loop)或寄存器(如 BX R0)指定
比较数据比较,更新 xPSR 标志CMP(比较)、CMN(负数比较)、TST(位测试)比较指令本质是 “执行减法但不保存结果,仅更新 xPSR 标志”(如 CMP R0,R1 等价于 SUBS R2,R0,R1 但不保存 R2)

六、ARM 指令集演进(ARM/Thumb/Thumb2)

ARM 最初支持两类指令集,后续通过 Thumb2 优化切换复杂度,不同指令集的特性与切换逻辑如下:

1. 早期两类指令集对比

指令集指令长度核心优势核心劣势适用场景
ARM32 位运行效率高,单条指令功能强占用存储空间大(相同功能下比 Thumb 多占 50% 空间)对性能要求高、存储空间充足的场景(如早期高端处理器)
Thumb16 位存储空间占用小,代码密度高部分复杂操作需多条指令实现,效率略低存储空间有限的场景(如嵌入式微控制器)

2. 指令集识别与切换(T 位控制)

CPU 通过xPSR 中的 T 位识别当前指令集状态:

  • T=0:ARM 状态,执行 32 位 ARM 指令;
  • T=1:Thumb 状态,执行 16/32 位 Thumb/Thumb2 指令。
手动切换指令集(用户代码逻辑补充)

调用不同指令集编写的函数时,需修改 PC 寄存器最低位(BIT0)实现切换:

arm

; 场景1:调用ARM指令集函数B(地址0x1000)
MOV PC, #0x1000  ; PC的BIT0=0,切换为ARM状态,跳转到0x1000执行
; 场景2:调用Thumb指令集函数A(地址0x2000)
MOV PC, #0x2001  ; PC的BIT0=1,切换为Thumb状态,跳转到0x2000执行(BIT0仅用于状态识别,地址实际为0x2000)

3. Thumb2 指令集的优化(关键补充)

为解决 “手动切换麻烦” 的问题,ARM 推出Thumb2 指令集,核心优势:

  • 兼容 16 位 Thumb 指令和 32 位 “Thumb 风格” 指令,无需手动切换状态;
  • 代码密度与 Thumb 相当,性能接近 ARM 指令集,兼顾空间与效率;
  • 现代 ARM 芯片(如 Cortex-M3/M4/M7)仅支持 Thumb/Thumb2 指令集,不再支持 ARM 指令集,用户代码未提及此点,需重点注意。

七、ARM 汇编指令分类与格式

1. 汇编指令五大类(参考 ARM 官方文档)

指令类别核心功能代表指令补充说明
数据处理寄存器间数据运算、立即数操作ADD(加法)、SUB(减法)、AND(与)、MOV(数据移动)运算对象可为 “寄存器 + 寄存器”“寄存器 + 立即数”,部分指令带 S 后缀(如 SUBS)可更新 xPSR
内存访问寄存器与内存的数据传输LDR(读内存到寄存器)、STR(写寄存器到内存)、LDM(批量读)、STM(批量写)支持多种寻址方式(如立即数偏移、寄存器偏移、堆栈寻址)
跳转改变程序执行流程B(基础跳转)、BL(带返回跳转)、BX(跳转 + 指令集切换)、BLX(带返回 + 切换)跳转范围受指令长度限制(如 Thumb 指令的 B 指令跳转范围为 ±2KB)
饱和运算处理数值溢出,避免结果错乱QADD(饱和加法)、QSUB(饱和减法)、QDADD(双倍饱和加法)适用于信号处理、传感器数据采集等场景(如 8 位数据 127+1 时,饱和结果为 127 而非 - 128)
其他指令系统控制、中断管理等杂项操作MRS(读特殊寄存器)、MSR(写特殊寄存器)、SWI(软件中断)、NOP(空操作)多为特权指令,仅能在系统模式或特权级下执行

2. 数据处理指令格式(UAL 统一汇编语言)

数据处理指令的标准格式为:Operation{cond}{S} Rd, Rn, Operand2,各字段含义如下:

  • Operation:指令操作码,指定指令功能(如 ADD、MOV、AND);
  • {cond}(可选):条件后缀,指定指令执行的条件(如 EQ = 相等、NE = 不相等、GT = 大于),省略则无条件执行;补充:常见条件后缀表(用户未提及,需补充)
    条件后缀含义xPSR 标志依据
    EQ相等Z=1
    NE不相等Z=0
    GT大于(有符号)N=V
    LT小于(有符号)N≠V
    CS/HS有进位(无符号)C=1
    CC/LO无进位(无符号)C=0
  • {S}(可选):状态更新标志,带 S 则指令执行后更新 APSR 的 N/Z/C/V 标志(如 SUBS 会更新标志,SUB 不更新);
  • Rd:目的寄存器,运算结果存储的目标寄存器;
  • Rn:第一个源操作数寄存器,存放参与运算的第一个数据;
  • Operand2:第二个源操作数,可分为 “立即数”(如 #0x12)、“寄存器”(如 R1)、“寄存器移位”(如 R1,LSL#2,即 R1 左移 2 位)。
指令格式示例(补充注释)

arm

; 1. ADD{cond}{S} Rd, Rn, Operand2:无条件加法,不更新APSR(无S)
ADD R0, R1, #0x05  ; R0 = R1 + 5,不更新N/Z/C/V标志
; 2. SUBS{cond} Rd, Rn, Operand2:相等时执行减法,更新APSR(带S)
SUBS EQ R0, R1, R2  ; 若Z=1(前次运算结果相等),则R0 = R1 - R2,同时更新N/Z/C/V标志
; 3. AND{S} Rd, Rn, Operand2:逻辑与,更新APSR
AND S R0, R1, R2    ; R0 = R1 & R2,更新N/Z/C/V标志(Z=1表示结果为0)

八、寄存器赋值:MOV 指令限制与 LDR 伪指令

1. MOV 指令的 “立即数限制”(核心问题)

MOV Rd, #VAL 是寄存器赋值的基础指令,但VAL 必须是 “合法立即数”,原因:

  • MOV 指令长度为 16 位(Thumb)或 32 位(ARM),其中用于存储立即数的位宽有限(如 Thumb 的 MOV 指令仅支持 8 位立即数 + 4 位移位);
  • 合法立即数需满足 “二进制可表示为 8 位数据循环右移偶数位”(如 0x12、0x1200、0x12000000,而 0x12345678 不合法)。
示例(合法与非法 MOV 指令)

arm

MOV R0, #0x12      ; 合法:0x12是8位立即数,无需移位
MOV R0, #0x1200    ; 合法:0x12循环右移16位(偶数位)
; MOV R0, #0x12345678  ; 非法:无法通过“8位数据+偶数移位”表示

2. LDR 伪指令:解决 “任意值赋值” 问题

若需给寄存器赋 “非合法立即数”,需使用LDR 伪指令(格式:LDR Rd, =VAL),核心特点:

  • “伪指令” 并非 CPU 原生指令,由编译器在编译阶段自动替换为 “合法原生指令”;
  • 必须带 “=”,否则为 “原生 LDR 指令”(用于读内存),二者需严格区分。

3. LDR 伪指令的编译器替换逻辑(用户代码补充)

编译器根据 VAL 的类型自动适配,分两种情况:

(1)VAL 是合法立即数:替换为 MOV 指令

arm

; 原伪指令:LDR R0, =0x12(0x12是合法立即数)
; 编译器替换为原生MOV指令:
MOV R0, #0x12  
(2)VAL 是非合法立即数:替换为 “DCD 定义 + 原生 LDR 读内存”

arm

; 原伪指令:LDR R0, =0x12345678(非合法立即数)
; 编译器处理步骤:
; 1. 在程序数据段用DCD指令定义该数值(存入内存)
Data_Sec: DCD 0x12345678  ; 内存地址假设为0x20000000
; 2. 替换为原生LDR指令(从内存读取该数值到R0)
LDR R0, [PC, #offset]    ; offset = 0x20000000 - PC当前值(由链接器计算)

补充说明

  • LDR 伪指令的偏移范围由 PC 的寻址范围决定(通常为 ±4KB),若 VAL 的地址超出范围,链接器会报错;
  • 对于常量地址(如外设寄存器地址),推荐使用 LDR 伪指令(如 LDR R0, =0x40001000),避免手动计算立即数合法性。

九、ADR 伪指令:基于 PC 的地址加载

1. ADR 伪指令的本质

ADR 是 “地址加载伪指令”,非 CPU 原生指令,由编译器在编译阶段替换为 “基于 PC 的加法指令”(如 ADD Rd, PC, #offset),核心作用是 “通过标签快速获取内存地址”。

2. 代码示例与替换逻辑(用户代码补充注释)

arm

; 场景:获取Loop标签的地址,存入R0
ADR R0, Loop  ; 原伪指令:加载Loop标签地址到R0
; 编译器替换逻辑:
; 1. 链接阶段计算Loop标签与当前PC的偏移量offset(假设PC当前值为0x1000,Loop地址为0x1010,则offset=0x10)
; 2. 替换为原生ADD指令:
ADD R0, PC, #0x10  ; R0 = PC + 0x10 = 0x1000 + 0x10 = 0x1010(即Loop地址)
; 后续可通过R0访问Loop标签处的代码或数据
Loop:
SUBS R1, R1, #1  ; Loop标签处的指令
BNE Loop

3. ADR 与 LDR = label 的区别(补充用户疏漏)

对比维度ADR 伪指令LDR = label 伪指令
地址来源基于 PC 的偏移(小范围,通常 ±4KB)直接加载标签的绝对地址(大范围,无偏移限制)
替换结果替换为 ADD/ SUB 指令(无内存访问)若地址是立即数,替换为 MOV;否则替换为 LDR 读内存
适用场景位置无关代码(如 Bootloader)位置相关代码(如固定地址的外设寄存器访问)

十、ARM 汇编的内存操作示例

1. 基础内存写操作(用户代码补充注释)

场景:将数据 0x1234 写入内存地址 0x20000,需 3 条指令:

arm

; 1. 给R0赋值为目标内存地址(0x20000),R0作为“地址寄存器”
MOV R0, #0X20000  
; 2. 用LDR伪指令给R1赋值为要存储的数据(0x1234),R1作为“数据寄存器”
LDR R1, =0X1234  
; 3. 用STR指令将R1的数据写入R0指向的内存地址(0x20000)
STR R1, [R0]      

2. 内存查看窗口验证(用户图示补充)

内存窗口(地址 0x20000~0x21000)显示结果:

  • Word Value(32 位数据):0x1234,与 R1 的数据一致,说明写入成功;
  • 字节分布(Byte 3~Byte 0):0x00、0x00、0x12、0x34,符合 ARM小端模式(低字节存低地址)——0x1234 分解为低字节 0x34(存 Byte 0,地址 0x20000)、高字节 0x12(存 Byte 1,地址 0x20001),高位补 0。

3. 多种 STR 寻址方式(用户代码补充注释)

STR 指令支持多种寻址方式,适配不同内存访问场景:

arm

MOV R0, #0x20000  ; 基地址:0x20000
MOV R1, #0x10     ; 偏移寄存器:R1=0x10
MOV R2, #0x12     ; 待存储数据:R2=0x12; 1. 立即数偏移:存储到R0+4地址(地址=0x20004),R0不变
STR R2, [R0, #4]  
; 2. 立即数偏移+R0更新:存储到R0+8地址(0x20008),之后R0=R0+8(0x20008)
STR R2, [R0, #8]! 
; 3. 寄存器偏移:存储到R0+R1地址(0x20008 + 0x10 = 0x20018),R0不变
STR R2, [R0, R1]  
; 4. 寄存器移位偏移:存储到R0+(R1<<4)地址(0x20008 + (0x10<<4) = 0x20108),R0不变
STR R2, [R0, R1, LSL #4]  
; 5. 后递增偏移:先存储到R0地址(0x20008),之后R0=R0+0x20(0x20028)
STR R2, [R0], #0X20  

补充说明

  • 所有内存访问指令的地址必须4 字节对齐(Cortex-M 系列默认要求),否则会触发 “对齐错误异常”;
  • 小端模式是 ARM 芯片的默认存储模式,部分芯片支持大端模式(需通过寄存器配置),但嵌入式场景极少使用。

十一、LDM/STM 指令:多寄存器批量内存操作

LDM(Load Multiple)和 STM(Store Multiple)是 “批量读写多寄存器与内存” 的指令,核心用于函数上下文保存、栈操作等场景。

1. 指令语法格式

指令类型语法格式核心作用
LDM(批量读)LDM{addr_mode}{cond} Rn{!}, reglist{^}从 Rn 指向的内存地址,批量读取数据到 reglist 中的寄存器
STM(批量写)STM{addr_mode}{cond} Rn{!}, reglist{^}将 reglist 中的寄存器数据,批量写入 Rn 指向的内存地址

2. 关键参数解释

(1)addr_mode:地址模式(控制 Rn 增减时机)
地址模式全称核心逻辑适用场景
IAIncrement After(后递增,默认)先传输数据,再将 Rn += 4(4 字节对齐)栈出栈(LDMIA)、数组连续读取
IBIncrement Before(前递增)先将 Rn += 4,再传输数据仅 ARM 指令支持,嵌入式场景极少用
DADecrement After(后递减)先传输数据,再将 Rn -= 4仅 ARM 指令支持,用于特殊内存布局
DBDecrement Before(前递减)先将 Rn -= 4,再传输数据栈入栈(STMDB)、批量数据保存
(2)其他参数
  • {cond}(可选):条件后缀,如 EQ(相等时执行)、NE(不相等时执行);
  • Rn(基址寄存器):内存访问的基准地址,通常为 SP(栈指针)或外设基地址;
  • {!}(可选):Rn 更新标志,带!则指令执行后将 “增减后的 Rn” 写回 Rn(如 STMDB SP!, {R0-R3} 会更新 SP);
  • reglist(寄存器列表):批量操作的寄存器集合,格式为 {R0-R3, R5}(注意:低编号寄存器对应低内存地址);
  • {^}(可选):状态寄存器影响标志,带 ^ 则操作时更新 CPSR(仅用于异常处理,如中断返回)。

3. Rn 按 4 字节增减的原因(补充细节)

  • ARM 寄存器为 32 位(4 字节),批量传输时 “一个寄存器对应 4 字节内存”,需保证地址对齐;
  • 若 Rn 按非 4 字节增减(如 2 字节),会导致内存地址未对齐,触发 “对齐错误异常”。
示例:STMDB 批量保存寄存器(栈入栈)

arm

MOV SP, #0x20000  ; 初始化栈指针SP=0x20000(满减栈)
; 批量保存R0~R3到栈:先SP -= 4×4=16(4个寄存器,每个4字节),再传输数据
STMDB SP!, {R0-R3}  
; 执行后:SP=0x20000 - 16=0x1FFFC,R3→0x1FFFC、R2→0x1FFF8、R1→0x1FFF4、R0→0x1FFF0(低寄存器存低地址)
示例:LDMIA 批量恢复寄存器(栈出栈)

arm

; 批量恢复R0~R3从栈:先传输数据,再SP += 16
LDMIA SP!, {R0-R3}  
; 执行后:SP=0x1FFFC + 16=0x20000,R0=0x1FFF0数据、R1=0x1FFF4数据、R2=0x1FFF8数据、R3=0x1FFFC数据

十二、ARM 栈操作(满减栈为核心)

栈是 “LIFO(后进先出)” 的临时存储区域,由 SP(栈指针)管理,ARM 支持 4 种栈类型,其中满减栈(Full Descending) 是嵌入式场景的默认选择。

1. 栈的分类规则

栈的类型由 “SP 指向” 和 “SP 增长方向” 共同决定:

分类维度类型核心定义
SP 指向满栈(Full)SP 指向 “最后一个入栈的数据”
空栈(Empty)SP 指向 “下一个待入栈的空地址”
SP 增长方向增栈(Ascending)入栈时 SP 增大(向高地址扩展)
减栈(Descending)入栈时 SP 减小(向低地址扩展)
4 种栈类型对比
栈类型入栈逻辑出栈逻辑常用指令
满增栈先存数据,再 SP += 4先 SP -= 4,再读数据STMIA、LDMDB
满减栈先 SP -= 4,再存数据先读数据,再 SP += 4STMFD(=STMDB)、LDMFD(=LDMIA)
空增栈先存数据,再 SP += 4先读数据,再 SP -= 4STMIB、LDMDA
空减栈先 SP -= 4,再存数据先 SP += 4,再读数据STMDD、LDMEA

2. 常用满减栈操作(用户代码补充注释)

场景:保存 R1~R3 到栈,修改后恢复,SP 初始值 0x20000:

arm

; 1. 准备数据:R1=1、R2=2、R3=3
MOV R1, #1  
MOV R2, #2  
MOV R3, #3  
; 2. 初始化栈指针SP=0x20000(满减栈基地址)
MOV SP, #0x20000  ; 3. 入栈(STMFD = STMDB):批量保存R1~R3到栈
STMFD SP!, {R1-R3}  
; 入栈逻辑:SP先减12(3个寄存器×4字节)→0x1FFF4,再存R3→0x1FFF4、R2→0x1FFF8、R1→0x1FFFC; 4. 模拟中间操作:修改R1~R3的值
MOV R1, #0  
MOV R2, #0  
MOV R3, #0  ; 5. 出栈(LDMFD = LDMIA):从栈恢复R1~R3的值
LDMFD SP!, {R1-R3}  
; 出栈逻辑:先读R1→0x1FFFC数据(1)、R2→0x1FFF8数据(2)、R3→0x1FFF4数据(3),再SP加12→0x20000; 执行后:R1=1、R2=2、R3=3(恢复原始值),SP=0x20000(恢复初始值)

补充说明

  • 栈的大小由编译器或程序员配置(如在链接脚本中指定栈空间为 0x1000),超出栈大小会导致 “栈溢出”,破坏其他数据;
  • 函数调用时,栈用于保存函数参数、返回地址(LR)和局部变量,是函数上下文切换的核心载体。

十三、ARM 跳转指令(4 类核心指令)

跳转指令用于控制程序执行流程,支持 “基础跳转”“带返回跳转”“指令集切换跳转”,是函数调用、循环控制的核心。

1. 基础跳转指令:B(Branch)

  • 作用:无条件或条件跳转至标签,不保存返回地址(跳转后无法直接返回);
  • 语法B{cond}{.W} label
    • {cond}:条件后缀(如 EQ、NE),省略则无条件跳转;
    • {.W}:指定为 Thumb2 的 32 位指令,扩大跳转范围(±16MB);
  • 示例(用户场景补充)

arm

; 无条件跳转至Loop标签(16位Thumb指令,跳转范围±2KB)
B Loop  
; 相等时跳转至Exit标签(32位Thumb2指令,跳转范围±16MB)
B.W EQ Exit  
Loop:
SUBS R0, R0, #1  
BNE Loop  ; 不相等则跳回Loop(B指令的条件变体)
Exit:
NOP  ; 跳转目标

2. 带返回跳转指令:BL(Branch with Link)

  • 作用:跳转前将 “下一条指令地址” 存入 LR 寄存器,支持跳转后返回(核心用于函数调用);
  • 语法BL{cond} label
  • 示例(函数调用与返回)

arm

; 调用func函数:跳转前将“MOV R1, #0”的地址存入LR
BL func  
MOV R1, #0  ; 函数返回后执行此指令
func:
ADD R0, R0, #1  ; 函数逻辑
BX LR  ; 函数返回:将LR的值赋给PC,回到调用处

3. 跳转 + 指令集切换:BX(Branch and eXchange)

  • 作用:根据寄存器 Rm 的 BIT0 切换指令集,再跳转到 Rm 指向的地址;
  • 语法BX{cond} Rm
    • Rm 的 BIT0=0:切换为 ARM 状态(执行 32 位 ARM 指令);
    • Rm 的 BIT0=1:切换为 Thumb 状态(执行 16/32 位 Thumb 指令);
  • 示例(指令集切换)

arm

; R0=0x1001(BIT0=1):切换为Thumb状态,跳转到0x1000(BIT0仅用于状态识别)
BX R0  
; R0=0x2000(BIT0=0):切换为ARM状态,跳转到0x2000
BX R0  

4. 带返回 + 指令集切换:BLX(Branch with Link and eXchange)

  • 作用:结合 BL 和 BX 的功能 —— 保存返回地址到 LR,同时根据目标地址 BIT0 切换指令集;
  • 语法:两种形式:
    • BLX{cond} label:跳转到标签,自动根据标签地址 BIT0 切换指令集;
    • BLX{cond} Rm:根据 Rm 的 BIT0 切换指令集,跳转到 Rm 指向的地址;
  • 示例(跨指令集函数调用)

arm

; 调用Thumb指令集的func函数:保存返回地址到LR,切换为Thumb状态
BLX func  
; 调用ARM指令集的func2函数:通过R0指定地址,保存返回地址到LR,切换为ARM状态
MOV R0, #0x3000  ; R0=0x3000(BIT0=0,ARM状态)
BLX R0  

补充说明

  • Cortex-M 系列仅支持 Thumb/Thumb2 指令集,BX/BLX 的 “ARM 状态切换” 在该系列中无效(会触发异常),仅在 Cortex-A 系列(如 A7/A9)中有用;
  • 函数返回优先使用 “BX LR”,若需支持协处理器,可使用 “BLX LR”(但嵌入式场景极少)。

十四、延迟循环代码示例

代码实现 “延迟循环 + 函数式返回”,核心利用 ADR 伪指令保存返回地址、LR 寄存器存储返回点,整理并补充注释如下:

代码分段与注释

arm

; 1. 保存返回地址:用ADR伪指令将Ret标签地址加载到LR(链接寄存器)
; 作用:记住“延迟逻辑执行完后,要回到Ret处继续执行”
ADR LR, Ret  ; 2. 跳转到延迟逻辑入口:用ADR伪指令将Delay标签地址加载到PC(程序计数器)
; 作用:修改PC,让程序跳转到Delay处执行延迟代码
ADR PC, Delay  ; 3. 延迟逻辑入口:Delay标签
Delay:
; 初始化R1=1(R1暂未参与核心延迟,可作为临时变量)
MOV R1, #1  
; 初始化延迟循环计数器R0=5(循环将执行5次)
MOV R0, #5  ; 4. 延迟循环体:Loop标签
Loop:
; R0 = R0 - 1,带S后缀(更新xPSR的Z标志)
SUBS R0, R0, #1  
; 若Z=0(R0≠0),则跳回Loop继续循环;Z=1(R0=0)则退出循环
BNE Loop  ; 5. 延迟结束后返回:将LR中保存的Ret地址赋给PC,回到初始调用处
MOV PC, LR  ; 6. 返回目标地址:Ret标签(延迟结束后执行此指令)
Ret:
MOV R1, #1  ; 示例后续逻辑,可根据需求修改

补充说明

  • 延迟时间计算:延迟总时间 = 循环次数 × 单循环指令周期数 × 指令周期(如晶振 16MHz,指令周期 = 62.5ns,5 次循环总延迟≈5×2×62.5ns=625ns);
  • ADR 伪指令的偏移范围有限(通常 ±4KB),若 Delay 标签地址超出范围,需改用 LDR 伪指令(如 LDR PC, =Delay)。
http://www.dtcms.com/a/503467.html

相关文章:

  • 《i.MX6ULL LED 裸机开发实战:从寄存器到点亮》
  • 迈向零信任存储:基于RustFS构建内生安全的数据架构
  • 网站开发公司找哪家帮卖货平台
  • C++ Vector:动态数组的高效使用指南
  • html5微网站漂亮网站
  • C++ 分配内存 new/malloc 区别
  • Respective英文单词学习
  • 网络排错全流程:从DNS解析到防火墙,逐层拆解常见问题
  • 移动端开发工具集锦
  • 使用Nvidia Video Codec(三) NvDecoder
  • 周口规划建设局网站wordpress模板中添加短代码
  • Linux小课堂: 命令手册系统深度解析之掌握 man 与 apropos 的核心技术机制
  • 阿里云做网站官网网站改版的seo注意事项
  • 每日算法刷题Day76:10.19:leetcode 二叉树12道题,用时3h
  • 【OS笔记11】:进程和线程9-死锁及其概念
  • 贪心算法1
  • 服务器搭建vllm框架并部署模型+cursor使用经验
  • Arduino采集温湿度、光照数据
  • 32HAL——外部中断
  • 网站建设会议议程新闻营销发稿平台
  • 【图像处理】CMKY色彩空间
  • 南宁建行 网站南通网站的优化
  • 构建AI智能体:六十八、集成学习:从三个臭皮匠到AI集体智慧的深度解析
  • 从入门到精通【Redis】Redis 典型应⽤ --- 分布式锁
  • 6.5 万维网(答案见原书P294)
  • CycloneDDS:跨主机多进程通信全解析
  • Java基础语法—类型转换、表达式的自动类型提升
  • CentOS8无法使用sudo提权
  • 软件工程师招聘信息网站数据库对网站开发的作用
  • Python核心数据结构与函数编程