Cortex-A 系列芯片上电后,SP 指针未初始化,C 语言运行环境尚未建立,无法直接执行 C 代码。必须先用汇编完成以下工作:
当汇编把 C 环境设置好后,才跳转到 C 语言的 main 函数。这与 STM32 的启动文件 startup_stm32f10x_hd.s 原理相同,只是 STM32 的启动文件由 ST 写好,开发者无需修改。
参考文档:
MDK 和 IAR 的汇编语法不同,不能直接移植。本教程使用 GCC 交叉编译器,汇编代码必须符合 GNU 语法。
基本格式:
label:instruction @comment
label:标号,表示地址位置,以 : 结尾instruction:汇编指令或伪操作@:注释符号(也可用 /* */)大小写规则:指令、伪操作、寄存器名可全大写或全小写,但不能混用。
| 伪操作 | 含义 |
|---|---|
.text |
代码段 |
.data |
已初始化数据段 |
.bss |
未初始化数据段 |
.rodata |
只读数据段 |
.section |
自定义段 |
.section .testsection @ 定义自定义段
| 伪操作 | 用法示例 | 含义 |
|---|---|---|
.byte |
.byte 0x12 |
定义单字节数据 |
.short |
.short 0x1234 |
定义双字节数据 |
.long |
.long 0x12345678 |
定义 4 字节数据 |
.equ |
.equ num, 0x12 |
赋值(num = 0x12) |
.align |
.align 4 |
4 字节对齐 |
.end |
.end |
源文件结束 |
.global |
.global _start |
定义全局符号 |
函数名:
函数体
返回语句 @ 非必须
示例——Cortex-A7 中断服务函数:
/* 未定义中断 */
Undefined_Handler:
ldr r0, =Undefined_Handler
bx r0
/* SVC 中断 */
SVC_Handler:
ldr r0, =SVC_Handler
bx r0
/* 预取终止中断 */
PrefAbort_Handler:
ldr r0, =PrefAbort_Handler
bx r0
处理器内部数据传输的三种场景:
| 场景 | 指令 |
|---|---|
| 寄存器 → 寄存器 | MOV |
| 特殊寄存器(CPSR/SPSR)→ 通用寄存器 | MRS |
| 通用寄存器 → 特殊寄存器 | MSR |
将数据从一个寄存器拷贝到另一个寄存器,或加载立即数到寄存器。
MOV R0, R1 @ R0 = R1
MOV R0, #0x12 @ R0 = 0x12
读取特殊寄存器(CPSR / SPSR)的值到通用寄存器。读特殊寄存器只能用 MRS。
MRS R0, CPSR @ R0 = CPSR
将通用寄存器的值写入特殊寄存器。写特殊寄存器只能用 MSR。
MSR CPSR, R0 @ CPSR = R0
ARM 不能直接访问存储器(包括寄存器映射的 RAM),需要通过 LDR/STR 指令间接访问。
| 指令 | 描述 |
|---|---|
LDR Rd, [Rn, #offset] |
从存储器 Rn+offset 读取数据到 Rd |
STR Rd, [Rn, #offset] |
将 Rd 的值写入存储器 Rn+offset |
LDRB Rd, [Rn, #offset] |
按字节读取 |
STRB Rd, [Rn, #offset] |
按字节写入 |
LDRH Rd, [Rn, #offset] |
按半字(16 位)读取 |
STRH Rd, [Rn, #offset] |
按半字写入 |
LDR 有两种用法:
@ 用法1:从存储器加载数据到寄存器
LDR R0, =0x0209C004 @ 将地址 0x0209C004 加载到 R0(立即数用 =)
LDR R1, [R0] @ 从地址 0x0209C004 读取数据到 R1
@ 用法2:加载立即数到寄存器
LDR R0, =0x12345678 @ R0 = 0x12345678
关键区别:加载立即数用 =,加载地址值也用 =;加载寄存器中存储的地址指向的数据用 []。
@ 配置 GPIO1_GDIR 寄存器(地址 0x0209C004)的值为 0x20000002
LDR R0, =0x0209C004 @ R0 = 寄存器地址
LDR R1, =0x20000002 @ R1 = 要写入的值
STR R1, [R0] @ 将 R1 写入 R0 指向的地址
函数调用时需要保存/恢复寄存器状态(现场保护/恢复现场),使用 PUSH 和 POP 指令。
| 指令 | 描述 |
|---|---|
PUSH <reg list> |
将寄存器列表压入栈中 |
POP <reg list> |
从栈中恢复寄存器列表 |
STMFD SP!, <reg list> |
PUSH 的另一种写法 |
LDMFD SP!, <reg list> |
POP 的另一种写法 |
@ 将 R0~R3 和 R12 压栈
PUSH {R0~R3, R12}
@ 将 LR 压栈
PUSH {LR}
堆栈变化示意(SP 初始值 0x80000000,向下增长):
高地址
0x80000000 ┌─────────┐
│ (空) │ ← 压栈前 SP
0x7FFFFFEC ├─────────┤
│ R12 │
│ R3 │
│ R2 │
│ R1 │
│ R0 │ ← PUSH {R0~R3,R12} 后 SP
0x7FFFFFE8 ├─────────┤
│ LR │ ← PUSH {LR} 后 SP
低地址
出栈顺序必须与压栈相反:
POP {LR} @ 先恢复 LR
POP {R0~R3, R12} @ 再恢复 R0~R3, R12
PUSH 和 POP 是 STMFD / LDMFD 的简化写法:
STMFD SP!, {R0~R3, R12} @ 等价于 PUSH {R0~R3, R12}
STMFD SP!, {LR}
LDMFD SP!, {LR} @ 等价于 POP {LR}
LDMFD SP!, {R0~R3, R12}
FD = Full Descending(满递减):ARM 使用 FD 类型堆栈,SP 指向最后一个入栈值,堆栈由高地址向低地址增长。STM / LDM 是多存储/多加载指令,可一次操作多个连续数据。
| 指令 | 描述 |
|---|---|
B <label> |
跳转到 label(范围 ±2KB,用 B.W 可扩大到 32 位) |
BX <Rm> |
间接跳转到 Rm 中的地址,并切换指令集 |
BL <label> |
跳转到 label,并将返回地址保存到 LR |
BLX <Rm> |
跳转到 Rm 指定地址,保存返回地址到 LR,并切换指令集 |
用于不需要返回的跳转,例如汇编初始化完成后跳转到 C 的 main 函数:
_start:
ldr sp, =0x80200000 @ 设置栈指针
b main @ 跳转到 main,不再返回
用于函数调用,跳转前自动将当前 PC 值保存到 LR(R14):
push {r0, r1} @ 保存 r0, r1
cps #0x13 @ 进入 SVC 模式
bl system_irqhandler @ 调用 C 语言中断处理函数
cps #0x12 @ 进入 IRQ 模式
pop {r0, r1}
str r0, [r1, #0x10] @ 写 EOIR,中断处理完成
选择原则:需要返回原处用 BL,跳转后不返回用 B。
| 指令 | 公式 | 说明 |
|---|---|---|
ADD Rd, Rn, Rm |
Rd = Rn + Rm |
加法 |
ADD Rd, Rn, #immed |
Rd = Rn + #immed |
立即数加法 |
ADC Rd, Rn, Rm |
Rd = Rn + Rm + C |
带进位加法 |
SUB Rd, Rn, Rm |
Rd = Rn - Rm |
减法 |
SUB Rd, Rn, #immed |
Rd = Rn - #immed |
立即数减法 |
SBC Rd, Rn, Rm |
Rd = Rn - Rm - !C |
带借位减法 |
MUL Rd, Rn, Rm |
Rd = Rn * Rm |
乘法(32位) |
UDIV Rd, Rn, Rm |
Rd = Rn / Rm |
无符号除法 |
SDIV Rd, Rn, Rm |
Rd = Rn / Rm |
有符号除法 |
嵌入式开发中最常用的是加减指令,乘除基本用不到。
| 指令 | 公式 | 说明 |
|---|---|---|
AND Rd, Rn, Rm |
Rd = Rn & Rm |
按位与 |
AND Rd, Rn, #immed |
Rd = Rn & #immed |
立即数与 |
ORR Rd, Rn, Rm |
Rd = Rn \| Rm |
按位或 |
ORR Rd, Rn, #immed |
Rd = Rn \| #immed |
立即数或 |
BIC Rd, Rn, Rm |
Rd = Rn & (~Rm) |
位清除 |
BIC Rd, Rn, #immed |
Rd = Rn & (~#immed) |
立即数位清除 |
ORN Rd, Rn, #immed |
Rd = Rn \| (~#immed) |
按位或非 |
EOR Rd, Rn, Rm |
Rd = Rn ^ Rm |
按位异或 |
EOR Rd, Rn, #immed |
Rd = Rn ^ #immed |
立即数异或 |
逻辑运算指令常用于配置 I.MX6UL 外设寄存器时的位操作。
Cortex-A7 有 16 个 32 位通用寄存器(R0-R15)和 2 个特殊状态寄存器:
| 寄存器 | 别名 | 用途 |
|---|---|---|
| R0-R3 | 参数传递 / 返回值(AAPCS 调用约定) | |
| R4-R10 | 通用变量,被调用者保存 | |
| R11 | FP | 帧指针(可选) |
| R12 | IP | 过程间调用暂存 |
| R13 | SP | 栈指针,指向当前栈顶 |
| R14 | LR | 链接寄存器,保存子程序返回地址 |
| R15 | PC | 程序计数器,当前正在执行的指令地址 |
| CPSR | 当前程序状态寄存器(标志位、中断使能、处理器模式) | |
| SPSR | 保存的程序状态寄存器(异常返回时恢复 CPSR) |
31 30 29 28 ... 7 6 5 4 3 2 1 0
N Z C V I F T M4 M3 M2 M1 M0
└──处理器模式──┘
| 位 | 名称 | 含义 |
|---|---|---|
| N | 负数 | 运算结果为负置 1 |
| Z | 零 | 运算结果为零置 1 |
| C | 进位 | 加法进位 / 减法借位 |
| V | 溢出 | 有符号运算溢出置 1 |
| I | IRQ | 1 = 禁止 IRQ 中断 |
| F | FIQ | 1 = 禁止 FIQ 中断 |
| T | Thumb | 0 = ARM 状态,1 = Thumb 状态 |
| M[4:0] | 模式 | 处理器模式(SVC=10011, IRQ=10010 等) |
| 类别 | 指令 | 语法 |
|---|---|---|
| 数据传输 | MOV / MRS / MSR | MOV Rd, Operand2 |
| 存储加载 | LDR / STR / LDRB / STRB | LDR Rd, [Rn, #offset] |
| 压栈出栈 | PUSH / POP / STMFD / LDMFD | PUSH {reg list} |
| 跳转 | B / BL / BX / BLX | BL label |
| 算术 | ADD / ADC / SUB / SBC | ADD Rd, Rn, Rm |
| 乘除 | MUL / UDIV / SDIV | MUL Rd, Rn, Rm |
| 逻辑 | AND / ORR / BIC / EOR | AND Rd, Rn, Rm |
在线运行:以下所有示例均可直接粘贴到 CPUlator ARM 中运行。 操作:打开网址 → 语言选 ARM → 粘贴代码 → 按 F5 编译加载 → F2 单步执行 → 观察寄存器变化。
@ 示例1:数据传输指令
@ 在 CPUlator 中运行,观察 R0-R3 和 CPSR 的变化
.global _start
_start:
@ MOV:寄存器之间拷贝
mov r0, #0x10 @ R0 = 0x10
mov r1, r0 @ R1 = R0 = 0x10
mov r2, #0xff @ R2 = 0xFF
@ MOV:加载小立即数
mov r3, #0x12 @ R3 = 0x12
@ MRS:读取 CPSR 到通用寄存器
mrs r0, cpsr @ R0 = CPSR 的值
@ MSR:修改 CPSR 的中断禁止位
orr r0, r0, #0xc0 @ 设置 I=1, F=1(禁止 IRQ 和 FIQ)
msr cpsr, r0 @ 写回 CPSR
@ 死循环,程序停在这里
hang:
b hang
观察要点:单步执行后,R0-R3 的值逐步变化,CPSR 在 MSR 前后发生变化。
@ 示例2:存储加载指令
@ 在 CPUlator 中运行,观察寄存器和内存的变化
.global _start
_start:
@ 用 LDR 加载地址到寄存器
ldr r0, =0x1000 @ R0 = 0x1000(目标内存地址)
@ 用 STR 写入数据到内存
ldr r1, =0x12345678 @ R1 = 0x12345678
str r1, [r0] @ 将 R1 写入地址 0x1000
@ 用 LDR 从内存读取数据
ldr r2, [r0] @ R2 = 内存[0x1000] = 0x12345678
@ 按字节操作(LDRB / STRB)
ldr r3, =0x2000 @ R3 = 0x2000
mov r4, #0xAA @ R4 = 0xAA
strb r4, [r3] @ 将 0xAA 写入地址 0x2000(只写低 8 位)
ldrb r5, [r3] @ R5 = 0xAA(只读低 8 位)
@ 按半字操作(LDRH / STRH)
ldr r6, =0x3000 @ R6 = 0x3000
ldr r7, =0xBBCC @ R7 = 0xBBCC
strh r7, [r6] @ 将 0xBBCC 写入地址 0x3000(只写低 16 位)
ldrh r8, [r6] @ R8 = 0xBBCC
@ 死循环
hang:
b hang
观察要点:切换到 Memory 窗口查看地址 0x1000、0x2000、0x3000 的内容变化。
@ 示例3:算术运算指令
@ 在 CPUlator 中运行,观察标志位(N, Z, C, V)的变化
.global _start
_start:
@ 加法
mov r0, #100 @ R0 = 100
mov r1, #200 @ R1 = 200
add r2, r0, r1 @ R2 = 100 + 200 = 300
@ 立即数加法
add r3, r2, #50 @ R3 = 300 + 50 = 350
@ 减法
sub r4, r1, r0 @ R4 = 200 - 100 = 100
@ 带借位减法(SBC)
mov r5, #0
subs r6, r5, #1 @ R6 = 0 - 1 = 0xFFFFFFFF,借位 C=0
sbc r7, r5, #0 @ R7 = 0 - 0 - !C = 0(因为借位了)
@ 乘法
mov r0, #7
mov r1, #6
mul r2, r0, r1 @ R2 = 7 * 6 = 42
@ 无符号除法
mov r0, #100
mov r1, #7
udiv r2, r0, r1 @ R2 = 100 / 7 = 14(商)
@ 有符号除法
mov r0, #-100
mov r1, #7
sdiv r2, r0, r1 @ R2 = -100 / 7 = -14
@ 死循环
hang:
b hang
观察要点:执行 subs 后,CPSR 的 C 位(进位/借位)会变化。
@ 示例4:逻辑运算指令
@ 在 CPUlator 中运行,常用于寄存器位操作
.global _start
_start:
@ AND:按位与(清除高位)
ldr r0, =0x1234FFFF @ R0 = 0x1234FFFF
and r1, r0, #0x0000FF00 @ R1 = R0 & 0x0000FF00 = 0x0000FF00(只保留 bit8-15)
@ ORR:按位或(设置某些位)
mov r2, #0 @ R2 = 0
orr r2, r2, #0x80 @ R2 = 0x80(设置 bit7)
orr r2, r2, #0x01 @ R2 = 0x81(设置 bit0)
@ BIC:位清除(清除指定位)
ldr r3, =0xFFFFFFFF @ R3 = 0xFFFFFFFF
bic r3, r3, #0x01 @ R3 = 0xFFFFFFFE(清除 bit0)
bic r3, r3, #0x02 @ R3 = 0xFFFFFFFC(清除 bit1)
@ EOR:按位异或(翻转某些位)
mov r4, #0xFF @ R4 = 0xFF
eor r4, r4, #0x0F @ R4 = 0xF0(翻转低 4 位)
@ 实际应用:读取-修改-写入 GPIO 寄存器
ldr r5, =0x1000 @ 假设 GPIO 寄存器地址
ldr r6, [r5] @ 读取当前值
orr r6, r6, #0x01 @ 设置 bit0(点亮 LED)
str r6, [r5] @ 写回
@ 死循环
hang:
b hang
观察要点:观察每次逻辑运算后寄存器的二进制位变化。
@ 示例5:压栈出栈指令
@ 在 CPUlator 中运行,观察 SP 和栈内存的变化
.global _start
_start:
@ 设置栈指针(向下增长的满递减栈)
ldr sp, =0x4000 @ SP = 0x4000(栈顶)
@ 初始化寄存器
mov r0, #0x11 @ R0 = 0x11
mov r1, #0x22 @ R1 = 0x22
mov r2, #0x33 @ R2 = 0x33
mov r3, #0x44 @ R3 = 0x44
@ 压栈:保存 R0-R3
push {r0, r1, r2, r3} @ SP 从 0x4000 变为 0x3FF0
@ 修改寄存器(模拟函数调用)
mov r0, #0xAA @ R0 被修改
mov r1, #0xBB @ R1 被修改
@ 出栈:恢复 R0-R3
pop {r0, r1, r2, r3} @ SP 从 0x3FF0 变回 0x4000
@ R0-R3 应恢复为原始值 0x11, 0x22, 0x33, 0x44
@ STMFD / LDMFD 写法(等价)
stmfd sp!, {r0-r3} @ 压栈
ldmfd sp!, {r0-r3} @ 出栈
@ 死循环
hang:
b hang
观察要点:
push 后 SP 减小(0x4000 → 0x3FF0)pop 后 SP 恢复,R0-R3 被还原@ 示例6:跳转与比较指令
@ 在 CPUlator 中运行,观察 PC 和条件标志的变化
.global _start
_start:
@ 计算 1+2+3+...+10 的累加和
mov r0, #0 @ R0 = sum = 0
mov r1, #1 @ R1 = i = 1
loop:
add r0, r0, r1 @ sum = sum + i
add r1, r1, #1 @ i = i + 1
cmp r1, #11 @ 比较 i 和 11
blt loop @ 如果 i < 11,跳转到 loop
@ 结束后 R0 = 55(1+2+3+...+10)
@ BL 跳转示例:调用子程序
mov r0, #10 @ 参数
bl double_value @ 调用,返回后 R0 = 20
@ R0 = 20
@ 死循环
hang:
b hang
@ 子程序:将 R0 的值翻倍
double_value:
lsl r0, r0, #1 @ R0 = R0 << 1 = R0 * 2
bx lr @ 返回
观察要点:
cmp 后 CPSR 的 Z 位会变化blt 条件跳转依赖 CPSR 的 N 位@ 示例7:位移操作
@ 在 CPUlator 中运行,位移是乘除 2 的高效替代
.global _start
_start:
@ 逻辑左移(LSL):相当于乘以 2^n
mov r0, #1 @ R0 = 1
lsl r1, r0, #3 @ R1 = 1 << 3 = 8
lsl r2, r0, #4 @ R2 = 1 << 4 = 16
@ 逻辑右移(LSR):相当于无符号除以 2^n
ldr r3, =0x80 @ R3 = 128
lsr r4, r3, #3 @ R4 = 128 >> 3 = 16
@ 算术右移(ASR):有符号除以 2^n(保留符号位)
mov r5, #-128 @ R5 = -128(0xFFFFFF80)
asr r6, r5, #3 @ R6 = -128 >> 3 = -16(0xFFFFFFF0)
@ 循环左移(LSL)实现乘法
mov r0, #5
lsl r0, r0, #2 @ R0 = 5 * 4 = 20(左移 2 位 = 乘 4)
@ 循环右移(LSR)实现除法
mov r1, #100
lsr r1, r1, #2 @ R1 = 100 / 4 = 25(右移 2 位 = 除 4)
@ 死循环
hang:
b hang
以下代码是实际工程中的启动代码片段,需要交叉编译器和链接脚本,无法在 CPUlator 中直接运行。用于理解完整启动流程。
@ Cortex-A7 启动汇编示例(适用于 I.MX6ULL)
@ 文件:start.S
.global _start
_start:
@ ============================================
@ 第一阶段:汇编初始化
@ ============================================
@ 1. 关闭 IRQ 和 FIQ
mrs r0, cpsr
orr r0, r0, #0xc0 @ 设置 I=1, F=1,禁止中断
msr cpsr, r0
@ 2. 设置 SVC 模式
mrs r0, cpsr
bic r0, r0, #0x1f @ 清除 M[4:0] 位
orr r0, r0, #0x13 @ 设置为 SVC 模式(10011)
msr cpsr, r0
@ 3. 设置栈指针(SVC 模式栈)
ldr sp, =0x80200000 @ DDR 起始地址 + 2MB 作为栈顶
@ 4. 清零 .bss 段
ldr r0, =__bss_start @ .bss 起始地址(链接脚本定义)
ldr r1, =__bss_end @ .bss 结束地址
mov r2, #0
bss_loop:
cmp r0, r1 @ 比较当前地址与结束地址
bge bss_done @ 如果 >= 结束地址,跳转完成
str r2, [r0] @ 写零
add r0, r0, #4 @ 地址 +4
b bss_loop @ 继续循环
bss_done:
@ 5. 跳转到 C 语言 main 函数
bl main @ 调用 main,保存返回地址到 LR
@ main 返回后(理论上不会到这里)
hang:
b hang @ 死循环
@ 中断向量表(放在代码段最前面)
.section .text.startup, "ax"
.global _vector_table
_vector_table:
ldr pc, =_start @ 复位
ldr pc, =Undefined_Handler
ldr pc, =SWI_Handler
ldr pc, =PrefAbort_Handler
ldr pc, =DataAbort_Handler
nop @ 保留
ldr pc, =IRQ_Handler
ldr pc, =FIQ_Handler
@ IRQ 中断服务函数示例
IRQ_Handler:
push {r0, r1} @ 保存 r0, r1
cps #0x13 @ 切换到 SVC 模式
bl system_irqhandler @ 调用 C 语言中断处理函数
cps #0x12 @ 切换回 IRQ 模式
pop {r0, r1}
str r0, [r1, #0x10] @ 写 EOIR 清中断
subs pc, lr, #4 @ 返回到中断前的下一条指令
@ 汇编函数示例:调用 C 函数并保存寄存器
my_function:
push {r4-r11, lr} @ 保存 callee-saved 寄存器和返回地址
@ 调用 C 函数
mov r0, #10 @ 第一个参数
mov r1, #20 @ 第二个参数
bl c_add_func @ 调用 C 函数,返回值在 r0
pop {r4-r11, pc} @ 恢复寄存器并返回
LDR R0, =0x12345678 @ 加载立即数到 R0(伪指令,编译器生成文字池)
LDR R0, [R1] @ 从 R1 指向的地址读取数据
MOV R0, #0x12 @ 加载小立即数(8 位以内)
= 用于加载任意 32 位立即数(编译器自动处理),# 用于直接立即数(有限范围)。
@ 错误!出栈顺序与压栈相反
PUSH {r0, r1, r2}
POP {r2, r1, r0} @ 正确但不推荐
@ 推荐写法:POP 按原顺序写,硬件自动按正确顺序恢复
PUSH {r0, r1, r2}
POP {r0, r1, r2} @ 推荐!
| 场景 | 使用 | 原因 |
|---|---|---|
| 初始化完成后跳 main | B |
不需要返回汇编 |
| 调用 C 函数 | BL |
需要返回继续执行 |
| 中断处理调用 C 函数 | BL |
必须返回完成中断恢复 |
bx lr:返回到 LR 保存的地址pop {pc}:从栈中恢复 PC(等价于返回)mov pc, lr:简单返回(不切换指令集)@ 以下两种写法等价
MOV R0, R1
mov r0, r1
@ 但不能混用
Mov r0, R1 @ 错误!
Q:为什么 Cortex-A 芯片上电后必须先执行汇编?
A:上电时 SP 指针未初始化,C 语言的函数调用依赖栈(出栈入栈),没有栈 C 语言无法运行。汇编不依赖栈,可以先初始化 DDR、设置 SP,建立 C 运行环境后再跳转到 C 代码。
Q:MOV 和 LDR 加载立即数有什么区别?
A:MOV 只能加载 8 位立即数(通过循环右移偶数位得到的值),LDR 伪指令可以加载任意 32 位立即数(编译器会将值放在代码段附近的文字池中,用 PC 相对寻址加载)。
Q:B 和 BL 指令的区别?
A:B 只做跳转,不保存返回地址;BL 跳转前自动将当前 PC+4(下一条指令地址)保存到 LR 寄存器,支持子程序调用后返回。
Q:PUSH/POP 与 STMFD/LDMFD 的关系?
A:PUSH 等价于 STMFD SP!,POP 等价于 LDMFD SP!。FD 表示 Full Descending(满递减堆栈),ARM 处理器默认使用这种堆栈模型。
Q:什么是现场保护?为什么需要它?
A:函数调用或中断发生时,需要先保存当前寄存器值(R0-R15),称为现场保护;函数返回或中断处理完成后,再恢复寄存器值,称为恢复现场。这是保证程序正确执行的基础。
Q:CPSR 寄存器的作用?
A:CPSR(Current Program Status Register)保存当前处理器的状态信息,包括:条件标志位(N/Z/C/V)、中断禁止位(I/F)、处理器模式位(M[4:0])、Thumb 状态位(T)等。通过 MRS/MSR 指令读写。