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与汇编混合编程、模式切换与异常,是嵌入式底层、启动代码开发、面试复习的核心干货。

相关推荐
DaTou大头3 小时前
STM32第三篇
单片机·嵌入式·电路基础
纯音乐sjc7 小时前
SPI 通信协议详解:从四线制到 ADXL345
面试·嵌入式
纯音乐sjc8 小时前
嵌入式面试复习准备
面试·嵌入式
潘潘的嵌入式日记11 小时前
位操作怎么写才不出错?置位、清位、翻转、取位一次讲清
c语言·嵌入式·寄存器·位操作
金士顿11 小时前
ASP.NET Core BackgroundService 深度解析:设备后台任务到底应该怎么设计
linux·嵌入式·asp.net core·arm64
嵌入式阿蔡12 小时前
CRA合规的工程化落地:SBOM、24小时响应与攻击面分析
嵌入式
嵌入式阿蔡12 小时前
工业物理AI 2026:从巡检机器人到AI原生工厂
嵌入式
嵌入式阿蔡12 小时前
边缘AI芯片2026:SoC与协处理器的路线分化
嵌入式
嵌入式阿蔡13 小时前
工业控制的国产化路径:从PLC替代到鸿蒙生态
嵌入式