ARM汇编入门笔记(二):启动代码、指令详解与C语言混合编程

一、开发环境与学习意义

开发环境:Keil4,参考 ARMv7 架构手册

学习核心 :ARM汇编不用于写业务代码,核心用途是编写启动代码。启动代码在C程序运行前执行,负责初始化硬件与运行环境,最终引导进入 main 函数。

二、ARM启动代码五大核心任务

处理器复位后,启动代码依次完成:

  1. 初始化异常向量表

  2. 初始化各模式栈指针(SP)

  3. 开启内核中断

  4. 切换系统为 User 用户模式

  5. 初始化硬件,跳转执行 C 语言 main 函数

三、汇编伪操作(编译器指令)

伪操作不被CPU执行,仅用于指导汇编器编译规则。

复制代码

AREA reset, CODE, READONLY

CODE32

ENTRY

END

  • AREA:定义代码段/数据段,reset 为复位启动段

  • CODE:当前段为可执行代码段

  • READONLY:代码段只读

  • CODE32:使用32位 ARM 指令集

  • THUMB:使用16位 Thumb 指令集

  • ENTRY:标记程序入口

  • END:汇编文件结束

四、ARM核心常用指令

4.1 MOV 传送指令

用于寄存器赋值、立即数加载,支持移位操作,是最基础的数据搬运指令。

复制代码

MOV{S}<c> <Rd>, #<const>

MOV{S}<c> <Rd>, <Rm>

常用移位:LSL左移、LSR右移、ASR算术右移、ROR循环右移、RRX扩展右移

复制代码

MOV r0, #0x8

MOV r1, r0

MOV r6, r0, LSL #31

4.2 ADD / SUB 加减指令

完成加减运算,带 S 可更新 CPSR 标志位,支持移位运算。

复制代码

ADD Rd, Rn, #const

ADD Rd, Rn, Rm, LSL #n

SUB Rd, Rn, #const

SUB Rd, Rn, Rm

注意:ARM 不支持两个立即数直接运算,常量计算由编译器预处理完成。

4.3 12位合法立即数(必考)

ARM 数据指令仅支持 12 位立即数,并非任意32位数都能直接 MOV。

判定规则 :数值可由8位基数 + 循环右移偶数位(0~30) 得到,即为合法立即数。

使用原则:合法立即数用 MOV,非法立即数必须用 LDR 伪指令加载。

示例:0x80000000(合法)、0x40001000(非法)

4.4 LDR / STR 读写内存指令

MOV 只能操作寄存器,LDR/STR 是唯一内存读写指令。

  • LDR:内存 → 寄存器,可加载超大/非法立即数

  • STR:寄存器 → 内存,保存数据

复制代码

LDR sp, =0x40001000 ; 初始化栈指针(非法立即数,必须LDR)

4.5 位操作指令(MVN / BIC / ORR)

  • MVN:按位取反后赋值

  • BIC:位清零,指定位置0

  • ORR:位置1,用于寄存器位配置

4.6 CMP 比较指令与 NZCV 标志位

CMP Rn, Rm:等价 SUBS,只更新标志位,不保存结果

CPSR 四大标志位:

  • N:结果为负(最高位为1)置1

  • Z:结果为0置1

  • C:加法进位/减法无借位置1

  • V:有符号数运算溢出置1

常用条件码:EQ相等、NE不等、GT大于、LT小于、GE大于等于、LE小于等于

4.7 跳转指令 B / BL / BX

  • B:纯跳转,不保存返回地址,用于循环、分支

  • BL:带链接跳转,保存下一条指令地址到 LR,用于函数调用

  • BX:寄存器跳转,可切换 ARM/Thumb 状态,用于函数返回(BX LR)

五、汇编循环结构

汇编循环依靠 CMP + 条件跳转 实现

do-while 循环示例:

复制代码

MOV r0, #0 ; i

MOV r1, #0 ; sum

loop

ADD r1, r1, r0

ADD r0, r0, #1

CMP r0, #100

BLE loop ; 小于等于则继续循环

六、函数调用与栈保护

6.1 PC 与 LR 寄存器

  • PC(R15):程序计数器,指向当前执行指令

  • LR(R14):链接寄存器,保存函数返回地址

函数调用:BL 保存返回地址到 LR;函数返回:BX LR

6.2 栈与现场保护

ARM 默认使用 满减栈(FD)。函数嵌套会覆盖寄存器与 LR,必须压栈保护现场、弹栈恢复现场。

  • STMFD:压栈,保护寄存器与LR

  • LDMFD:出栈,恢复寄存器与PC

复制代码

STMFD sp!, {r0-r12, lr} ; 保护现场

BL func1 ; 调用子函数

LDMFD sp!, {r0-r12, pc} ; 恢复现场并返回

七、汇编与C语言混合编程

7.1 汇编调用C函数

  1. 汇编中 IMPORT 导入C函数

  2. 前4个参数放入 R0~R3,多余参数压栈

  3. 调用前 STMFD 保护现场,调用后恢复

  4. 添加 PRESERVE8 保证8字节栈对齐,避免编译报错

7.2 C语言调用汇编函数

  1. 汇编中 EXPORT 导出函数

  2. C语言中 extern 声明函数

  3. 参数 R0~R3 传递,返回值存放在 R0

八、内核工作模式切换与异常

通过 MRS/MSR 读写 CPSR 寄存器,修改M域可切换内核工作模式。

MRS :读状态寄存器;MSR:写状态寄存器

SWI:软中断指令,用于触发软件中断异常

九、高频面试/作业简答题(汇总)

1、什么是12位立即数?如何判断非法立即数?

ARM数据指令的立即数由8位基数+4位循环移位组成。能通过8位数循环右移偶数位得到为合法立即数,反之是非法立即数,需用LDR加载。

2、B、BL、BX区别?

B普通跳转;BL跳转并保存返回地址到LR,用于函数调用;BX带指令集切换,用于函数返回。

3、ARM默认栈类型?

满递减栈(FD),先减指针、后存数据,搭配 STMFD/LDMFD 使用。

4、NZCV标志位置位条件?

N负、Z零、C进位/无借位、V有符号溢出。

5、汇编与C互调用参数规则?

前4参R0-R3,多参压栈,返回值R0;汇编调C需栈对齐,C调汇编需EXPORT/extern声明。

十、总结

本篇笔记精简覆盖 ARM 汇编核心:启动代码工作流程、伪操作、全部常用指令、立即数规则、栈帧保护、循环与函数调用、C与汇编混合编程、模式切换与异常,是嵌入式底层、启动代码开发、面试复习的核心干货。

相关推荐
嵌入式分享16 小时前
BSP调试#01:RTC(RK3588)
嵌入式·实时音视频
嵌入式分享17 小时前
嵌入式分享#38:踩坑实录!我在 RK3576 HDMI 搭错线
嵌入式
oku620892 天前
单片机底层系列:C 库运行时——从 libspace 到多任务与中断安全
嵌入式
Zwawa2 天前
反馈:让系统知道自己做得对不对
c语言·单片机·嵌入式
蓝天居士2 天前
PY32F系列MCU在OTA时App区概率性跑不起来的根因分析(2)
mcu·嵌入式
剑指offer.3 天前
ARM裸机开发-SPI
arm开发·嵌入式硬件·嵌入式·arm
___波子 Pro Max.3 天前
软件开发里的 prebuilt 是什么
嵌入式
7yewh3 天前
SLAM 三维空间刚体运动(2)
数据结构·人工智能·机器人·嵌入式·slam
LCG元3 天前
树莓派 Pico RP2040 PIO 逻辑分析仪实战:SM 指令周期拆解、DREQ 触发 DMA 环形缓冲与采样率参数实测
嵌入式·树莓派·rp2040
7yewh4 天前
SLAM 从视觉里程计到建图(6)
数据结构·人工智能·机器人·自动驾驶·嵌入式