|
|
@@ -340,7 +340,304 @@ Cortex-A7 有 16 个 32 位通用寄存器(R0-R15)和 2 个特殊状态寄
|
|
|
|
|
|
## 9.3 代码示例
|
|
|
|
|
|
-### 完整的 Cortex-A7 启动汇编框架
|
|
|
+> **在线运行**:以下所有示例均可直接粘贴到 [CPUlator ARM](https://cpulator.01xz.net/?sys=arm) 中运行。
|
|
|
+> 操作:打开网址 → 语言选 ARM → 粘贴代码 → 按 F5 编译加载 → F2 单步执行 → 观察寄存器变化。
|
|
|
+
|
|
|
+### 示例 1:数据传输指令(MOV / MRS / MSR)
|
|
|
+
|
|
|
+```asm
|
|
|
+@ 示例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:存储加载指令(LDR / STR)
|
|
|
+
|
|
|
+```asm
|
|
|
+@ 示例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:算术运算指令(ADD / SUB / MUL)
|
|
|
+
|
|
|
+```asm
|
|
|
+@ 示例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:逻辑运算指令(AND / ORR / BIC / EOR)
|
|
|
+
|
|
|
+```asm
|
|
|
+@ 示例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:压栈出栈指令(PUSH / POP)
|
|
|
+
|
|
|
+```asm
|
|
|
+@ 示例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
|
|
|
+```
|
|
|
+
|
|
|
+**观察要点**:
|
|
|
+
|
|
|
+1. 执行 `push` 后 SP 减小(0x4000 → 0x3FF0)
|
|
|
+2. Memory 窗口查看 0x3FF0-0x3FFC 可以看到压入的值
|
|
|
+3. 执行 `pop` 后 SP 恢复,R0-R3 被还原
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 示例 6:跳转与比较指令(B / CMP / 条件跳转)
|
|
|
+
|
|
|
+```asm
|
|
|
+@ 示例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 @ 返回
|
|
|
+```
|
|
|
+
|
|
|
+**观察要点**:
|
|
|
+
|
|
|
+1. 单步执行循环,观察 R0(累加和)和 R1(计数器)的变化
|
|
|
+2. `cmp` 后 CPSR 的 Z 位会变化
|
|
|
+3. `blt` 条件跳转依赖 CPSR 的 N 位
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 示例 7:位移操作(LSL / LSR / ASR)
|
|
|
+
|
|
|
+```asm
|
|
|
+@ 示例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
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 示例 8:综合实战——Cortex-A7 启动汇编框架
|
|
|
+
|
|
|
+> 以下代码是实际工程中的启动代码片段,需要交叉编译器和链接脚本,无法在 CPUlator 中直接运行。用于理解完整启动流程。
|
|
|
|
|
|
```asm
|
|
|
@ Cortex-A7 启动汇编示例(适用于 I.MX6ULL)
|