目录
一、ARM 汇编语法与核心概念
1.1 伪操作
1.2 立即数与移位操作
1.2.1 立即数
1.2.2 移位操作
二、数据操作与运算
2.1 数据传送与逻辑指令
2.2 算术运算指令
2.3 条件判断指令
2.3.1 CPSR 标志位
2.3.2 CMP 指令
三、流程控制
3.1 do-while 循环
3.2 while/for 循环
四、函数调用
4.1 跳转指令
4.2 ARM 栈类型与初始化
4.3 现场保护与恢复
五、汇编与 C 语言互调
5.1 汇编调用 C 函数
5.2 C 语言调用汇编函数
六、工作模式与异常处理
6.1 工作模式切换
6.2 异常向量表
七、常见问题 FAQ
在嵌入式开发中,ARM 架构占据核心地位,而汇编语言是解锁 ARM 内核底层能力的关键 —— 它的核心使命是编写启动代码,为 C 语言运行搭建基础环境(初始化异常向量表、配置栈指针、开启中断、切换工作模式、引导 C 主函数)。
一、ARM 汇编语法与核心概念
1.1 伪操作
伪操作并非 CPU 执行指令,而是告诉汇编器如何组织代码 / 数据,核心伪操作如下:
| area reset, code, readonly | 定义段:reset 为段名(通常用于复位向量),code 表示代码段,readonly 只读属性 |
| code32 | 后续指令使用 32 位 ARM 指令集(对应 thumb 为 16 位指令集) |
| entry | 标记程序入口(复位后首个执行的指令位置) |
| end | 标记汇编文件结束 |
| preserve8 | 栈 8 字节对齐(解决 C / 汇编互调时的编译报错) |
基础框架示例:
area reset, code, readonly ; 定义复位段
code32 ; 32位ARM指令集
entry ; 程序入口
; 核心代码(启动流程/业务逻辑)
end ; 文件结束
1.2 立即数与移位操作
1.2.1 立即数
汇编中 "立即数" 特指 12 位立即数(imm12),判断标准:
- 将数值转为二进制后,存在偶数位循环右移,使得移位后高 24 位全 0,低 8 位为有效数据(imm8)。
- 合法示例:0x80000000(循环右移 30 位后,低 8 位为0x01)、0xF0(无需移位,低 8 位直接有效)
- 非法示例:0x101(无偶数位循环右移能让高 24 位全 0)
1.2.2 移位操作
ARM 指令支持寄存器移位后参与运算,无需单独指令,常见类型:
| LSL | 逻辑左移(高位丢弃) | mov r0, r1, lsl #3(左移 3 位) |
| LSR | 逻辑右移(低位丢弃) | add r0, r1, lsr r2(按 r2 值右移) |
| ASR | 算术右移(符号位扩展) | mov r0, r1, asr #2 |
| ROR | 循环右移(低位补高位) | sub r0, r1, ror #4 |
| RRX | 扩展右移(带进位标志) | mov r0, r1, rrx(无需移位量) |
二、数据操作与运算
ARM 汇编指令围绕 "数据传送、运算、逻辑操作" 展开,以下是高频指令详解:
2.1 数据传送与逻辑指令
| MOV | 数据传送(寄存器 / 立即数→寄存器) |
mov r0, #0x8(立即数传 r0)、 mov r1, r0(寄存器传寄存器) |
| MVN | 按位取反后传送 | mvn r0, #0xFF(r0=0xFFFFFF00) |
| BIC | 指定位清 0 | bic r0, r0, #(1<<31)(清 r0 第 31 位) |
| ORR | 指定位置 1 | orr r0, r0, #0x80000000(置 r0 第 31 位) |
2.2 算术运算指令
核心支持加减运算,支持立即数 / 寄存器 / 移位寄存器作为操作数:
; ADD指令示例
mov r0, #0x0F ; r0=15
mov r1, #0xF0 ; r1=240
mov r2, #1 ; r2=1
add r3, r0, #0xF0 ; 立即数运算:r3=15+240=255
add r4, r0, r1 ; 寄存器运算:r4=255
add r5, r0, r1, lsl #1 ; 移位寄存器运算:r1左移1位(480),r5=15+480=495
add r6, r0, r1, lsl r2 ; 按寄存器移位:r2=1,效果同上
; SUB指令示例(格式与ADD一致)
sub r7, r0, #0x05 ; r7=15-5=10
2.3 条件判断指令
2.3.1 CPSR 标志位
ARM 通过 CPSR 寄存器(当前程序状态寄存器)的条件标志位实现分支判断,核心标志位:
| N(符号) | 结果为有符号负数 | 运算结果最高位(bit31)=1 |
| Z(零值) | 结果为 0 | 运算结果全 0 |
| C(进位) | 无符号运算进位 / 无借位 | 加法进位、减法无借位 |
| V(溢出) | 有符号运算溢出 | 两正数相加得负数、两负数相加得正数 |
分支判断
2.3.2 CMP 指令
CMP <Rn>, <操作数> 等价于 SUBS <Rn>, <操作数>(减法不保存结果,仅更新 CPSR 标志位),配合条件码实现分支:
mov r0, #10
mov r1, #20
cmp r0, r1 ; 比较r0和r1(10-20),Z=0、N=1、C=0、V=0
movge r2, r0 ; GE(大于等于):N==V,此处不执行
movlt r2, r1 ; LT(小于):N!=V,执行r2=20
常见条件码:eq(等于)、ne(不等于)、gt(大于)、lt(小于)、al(无条件执行,默认)。
三、流程控制
ARM 汇编通过 "标签 + 跳转指令" 实现循环,需满足循环三要素:结束条件、趋向终结的语句、循环体。
3.1 do-while 循环
C 语言对照:
int i=0, sum=0;
do{
sum += i;
i++;
}while(i <= 100);
汇编实现:
mov r0, #0 ; i=0(r0存储循环变量)
mov r1, #0 ; sum=0(r1存储累加结果)
loop ; 循环标签
add r1, r1, r0 ; 循环体:sum += i
add r0, r0, #1 ; 趋向终结:i++
cmp r0, #100 ; 结束条件判断:i<=100?
ble loop ; 满足则跳转循环(ble=小于等于)
3.2 while/for 循环
C 语言对照:
int i=0, sum=0;
while(i <= 100)
{
sum += i;
i++;
}
汇编实现:
mov r0, #0 ; i=0
mov r1, #0 ; sum=0
loop
cmp r0, #100 ; 先判断:i>100?
bgt finish ; 满足则退出循环(bgt=大于)
add r1, r1, r0 ; 循环体:sum += i
add r0, r0, #1 ; i++
b loop ; 跳转循环
finish
b finish ; 程序暂停
四、函数调用
ARM 函数调用的核心是栈管理和返回地址保存,需理解跳转指令差异、栈类型及现场保护逻辑。
4.1 跳转指令
| b | 无条件跳转,不保存返回地址 | 同一函数内分支跳转 |
| bl | 跳转前将返回地址(PC+4)存入 LR 寄存器 | 函数调用(需返回主调函数) |
| bx | 跳转并切换指令集(ARM↔Thumb),通过寄存器传递目标地址 | 函数返回(bx lr = mov pc, lr) |
基础函数调用示例:
b main ; 跳转到main函数(不返回)
func ; 被调函数
mov r0, #1
mov r1, #2
add r3, r0, r1 ; r3=3
bx lr ; 返回主调函数(lr存储返回地址)
main ; 主调函数
mov r0, #100
mov r1, #200
bl func ; 调用func,lr=当前PC+4
mov r3, #300 ; 函数返回后执行
4.2 ARM 栈类型与初始化
ARM 内核(如 S3C2440)采用满减栈(Full Descending),栈操作规则:
- 入栈:先 sp -= 4(栈指针下移),再存储数据
- 出栈:先读取数据,再 sp += 4(栈指针上移)
栈指针初始化:
栈需指定连续内存区域,通过 ldr 指令加载合法地址(直接 mov sp, #0x40001000 报错,因非 12 位立即数):
ldr sp, =0x40001000 ; 栈基地址=0x40001000(需在IRAM范围内)
; Keil配置:Target→IRAM1 Start=0x40000000,Size=0x1000
4.3 现场保护与恢复
函数调用时,被调函数可能修改主调函数的寄存器,或嵌套调用覆盖 LR,需通过压栈 / 弹栈保护现场:
- 保护现场:stmfd sp!, {r0-r12, lr}(将寄存器组和 LR 入栈)
- 恢复现场:ldmfd sp!, {r0-r12, pc}(弹栈恢复寄存器,PC=LR 实现返回)
嵌套函数示例:
b main
func1 ; 二级被调函数
mov r0, #10
mov r1, #20
cmp r0, r1
movge r2, r0 ; r2=20
movlt r2, r1
bx lr
func0 ; 一级被调函数
stmfd sp!, {r0-r12, lr} ; 保护现场
bl func1 ; 嵌套调用func1(LR被覆盖)
ldmfd sp!, {r0-r12, pc} ; 恢复现场并返回
main
ldr sp, =0x40001000 ; 初始化栈指针
stmfd sp!, {r0-r12, lr} ; 主调函数保护现场
bl func0 ; 调用func0
ldmfd sp!, {r0-r12, lr} ; 恢复现场
finish
b finish
end
五、汇编与 C 语言互调
嵌入式开发中,汇编负责底层初始化,C 语言负责业务逻辑,两者互调是核心场景。
5.1 汇编调用 C 函数
步骤与规则:
完整示例:
- 汇编文件(start.s):
area reset, code, readonly
code32
preserve8 ; 栈8字节对齐,避免编译报错
entry
import c_add ; 导入C函数
main
ldr sp, =0x40001000 ; 初始化栈
stmfd sp!, {r0-r12, lr} ; 保护现场
; 传参:r0=1, r1=2, r2=3, r3=4, r4=5(第5个参数入栈)
mov r0, #1
mov r1, #2
mov r2, #3
mov r3, #4
mov r4, #5
stmfd sp!, {r4} ; 第5个参数入栈
bl c_add ; 调用C函数
ldmfd sp!, {r4} ; 弹出第5个参数
ldmfd sp!, {r0-r12, lr} ; 恢复现场
finish
b finish
end
- C 文件(main.c):
#include <stdio.h>
// C函数实现
void c_add(int a, int b, int c, int d, int e)
{
int sum = a + b + c + d + e;
// sum=15(可通过调试查看)
}
编译配置(Keil):
5.2 C 语言调用汇编函数
步骤与规则:
完整示例:
- 汇编文件(func.s):
area func, code, readonly
code32
export func1 ; 导出函数,供C调用
func1 ; 汇编函数:返回a和b的最大值
cmp r0, r1 ; r0=a, r1=b
movge r0, r0 ; 若a>=b,返回a(r0不变)
movlt r0, r1 ; 若a<b,返回b(r0=r1)
bx lr ; 返回C函数
end
- C 文件(main.c):
#include <stdio.h>
extern int func1(int a, int b); // 声明汇编函数
int main()
{
int max = func1(10, 20); // 调用汇编函数,max=20
return 0;
}
六、工作模式与异常处理
6.1 工作模式切换
ARM 内核支持 7 种工作模式,通过 CPSR 寄存器的 M 域(bit0-bit4)控制,切换步骤:
- 读取 CPSR:mrs r0, cpsr
- 清除 M 域:bic r0, r0, #0x1F(0x1F=5 位全 1)
- 设置目标模式:orr r0, r0, #0x10(0x10 = 用户模式)
- 写入 CPSR:msr cpsr, r0
切换到用户模式示例:
mrs r0, cpsr ; 读取当前CPSR
bic r0, r0, #0x1F ; 清除M域
orr r0, r0, #0x10 ; 设置为用户模式(USR模式)
msr cpsr, r0 ; 写入生效
6.2 异常向量表
异常向量表是 ARM 处理中断 / 异常的核心,复位后 CPU 首先读取 0x0 地址的向量表,常见异常如软中断(SWI):
; 简化异常向量表
area reset, code, readonly
code32
entry
b reset_handler ; 复位异常(0x0)
b . ; 未定义指令异常(0x4)
b . ; 软中断异常(0x8):swi #7触发
; … 其他异常向量
reset_handler
; 启动流程(初始化栈、异常向量表等)
七、常见问题 FAQ
1. 什么是立即数?如何判断某数是非法是12位立即数?
- 12 位立即数是 ARM 指令支持的立即数格式。
- 判断标准:二进制存在偶数位循环右移,使高 24 位全 0、低 8 位有效;反之则为非法(如 0x101)。
2. b、bl、bx指令的区别是什么?
- b:无条件跳转,不保存返回地址;
- bl:跳转前保存返回地址到 LR,用于函数调用;
- bx:跳转并切换指令集(ARM↔Thumb),常用于函数返回(bx lr)。
3. ARM内核采用的栈是哪种栈?
- 采用满减栈(Full Descending):入栈先 sp-4 再存数据,出栈先读数据再 sp+4。
4. CPSR中条件标志位,分别在什么情况下被置位
- N:结果最高位为 1(有符号负数);
- Z:结果全 0;
- C:无符号加法进位 / 减法无借位;
- V:有符号运算溢出(两正相加得负、两负相加得正)。
5. ARM汇编调用C语言函数以及C语言函数调用汇编编写的函数,函数参数和返回值如何处理?
- 参数:前 4 个通过 r0-r3 传递,超过 4 个入栈;
- 返回值:通过 r0 寄存器返回。


