ARM汇编代码新手入门
为什么要学 ARM 汇编?
汇编是直接操作硬件的 “机器语言翻译”,在嵌入式开发中(如芯片驱动、实时系统)不可替代; 例:STM32 芯片的启动代码必须用汇编编写,因为 C 语言无法直接初始化 CPU 寄存器。
ARM 架构与寄存器(重点)
ARM 是什么
一种主流嵌入式处理器架构(手机、汽车芯片、物联网设备常用),我们以 32 位 ARMv7 架构为例(Cortex-M/R 系列通用)。
寄存器:CPU 内部的 “临时存储器”(比内存快 1000 倍),ARM 有 16 个通用寄存器(r0-r15),重点记住这几个特殊寄存器:
r0-r3:传递函数参数 / 返回值(类似快递盒);
sp(r13):栈指针(指向当前栈顶,类似 “临时工作台”);
lr(r14):链接寄存器(存函数返回地址,类似 “回家的路牌”);
pc(r15):程序计数器(存下一条要执行的指令地址,类似 “当前页码”)。
类比:寄存器就像厨房的 “调料罐”,常用的调料(数据)放在罐子里,炒菜(运算)时直接拿,比从仓库(内存)取快得多。
汇编程序基本结构
.text ; 代码段(存放指令).global main ; 声明程序入口
main: ; 程序入口(类似C的main)mov r0, #5 ; r0 = 5(#表示常数)mov r1, #3 ; r1 = 3add r2, r0, r1 ; r2 = r0 + r1(结果8)bx lr ; 程序结束(返回)
练习
- 写出
mov r3, #10
的作用。 - 补全程序:用
sub
指令实现r4 = r0 - r1
(已知r0=8,r1=3
)。
解析
1.
mov r3, #10
:将常数 10 存入寄存器r3
(r3 = 10
)。
2.
mov r0, #8 ; r0 = 8
mov r1, #3 ; r1 = 3
sub r4, r0, r1 ; r4 = 8 - 3 = 5
基础指令与运算
1. 数据传送指令
mov rd, rn
:rd = rn
(寄存器间传值);mov rd, #imm
:rd = 常数
(传常数);ldr rd, [rn]
:从rn
指向的内存读数据到rd
(内存→寄存器);str rd, [rn]
:把rd
的值写到rn
指向的内存(寄存器→内存)。
2. 算术运算指令
add rd, rn, rm
:rd = rn + rm
;sub rd, rn, rm
:rd = rn - rm
;mul rd, rn, rm
:rd = rn × rm
。
练习
- 编写程序:计算
10 - (2 + 3)
,结果存r5
。 - 用
ldr/str
指令实现:将内存地址0x20000000
中的值加 10 后,写回原地址。
解析
1.
mov r0, #2 ; r0 = 2
mov r1, #3 ; r1 = 3
add r2, r0, r1 ; r2 = 2 + 3 = 5(先算括号内)
mov r3, #10 ; r3 = 10
sub r5, r3, r2 ; r5 = 10 - 5 = 5(最终结果)
2.
ldr r0, =0x20000000 ; r0 = 内存地址(=表示取地址)
ldr r1, [r0] ; r1 = 从0x20000000读的值(假设原 value=5)
add r1, r1, #10 ; r1 = 5 + 10 = 15
str r1, [r0] ; 把15写回0x20000000
ldr r0, =0x20000000
:将内存地址存入r0
(注意=
的用法);ldr/str
配合[rn]
实现内存访问。
程序流程控制(分支与循环)
1. 无条件跳转
b label
:跳转到label
标签(类似goto
)。
2. 条件跳转(重点)
ARM 通过CPSR 寄存器(程序状态寄存器)的 “条件码” 判断跳转,常用条件:
eq
(等于)、ne
(不等于)、gt
(大于)、lt
(小于)。
指令格式:b<条件> label
(如beq label
:等于则跳转)。
例:if-else 逻辑
cmp r0, r1 ; 比较r0和r1(结果存CPSR)beq equal ; 若相等,跳equalmov r2, #0 ; 不相等:r2=0b end ; 跳end
equal:mov r2, #1 ; 相等:r2=1
end:
3. 循环结构
用 “条件跳转 + 自增” 实现循环。
练习
- 编写程序:判断
r0
是否为偶数(提示:偶数的二进制末位为 0,用and
指令)。 - 计算 1~5 的乘积(1×2×3×4×5),结果存
r2
。
解析
1.判断偶数代码:
mov r0, #6 ; 假设r0=6(偶数)
and r1, r0, #1 ; r1 = r0 & 1(取末位,偶数末位0,奇数末位1)
cmp r1, #0 ; 比较r1是否为0
beq even ; 若等于0,跳even
mov r2, #0 ; 奇数:r2=0
b endeven:
mov r2, #1 ; 偶数:r2=1
end:
2.
2. 计算乘积代码:
```asm
mov r0, #1 ; r0=1(乘积初始值)
mov r1, #1 ; r1=1(当前数,从1开始)
loop:
mul r0, r0, r1 ; 乘积 = 乘积 × 当前数(1×1→1×2→2×3→...)
add r1, r1, #1 ; 当前数+1(1→2→3→4→5→6)
cmp r1, #6 ; 判断当前数是否>5(因为要乘到5)
blt loop ; 若<6,继续循环
mov r2, r0 ; 结果存r2(r2=120)
函数调用与栈操作
1. 函数调用流程
- 调用:
bl 函数名
(跳转 + 保存返回地址到lr
); - 返回:
bx lr
(跳回lr
存的地址)。
2. 栈的作用:保存现场
函数内部可能覆盖寄存器,需用栈保存状态:
push {r0-r3, lr}
:压栈保存寄存器;pop {r0-r3, pc}
:出栈恢复(pc=lr
实现返回)。
练习
- 编写函数
sub_func
,接收r0
(a)和r1
(b),返回a - b
,在main
中调用。 - 编写函数
max
,接收r0
和r1
,返回较大的数。
解析
.global main
main:
mov r0, #10 ; a=10
mov r1, #3 ; b=3
bl sub_func ; 调用函数,返回值存 r0(10-3=7)
bx lrsub_func: ; 函数:返回 r0 - r1
sub r0, r0, r1 ; 计算差
bx lr ; 返回
2. 求最大值函数代码:
.global main
main:
mov r0, #5 ; 参数1=5
mov r1, #8 ; 参数2=8
bl max ; 调用max,返回值存r0(8)
bx lrmax: ; 函数:返回r0和r1中的最大值
cmp r0, r1 ; 比较
bge greater ; 若r0 >= r1,跳greater
mov r0, r1 ; 否则r0 = r1(让r0存较大值)
b end
greater:
; 若r0更大,无需操作
end:
bx lr ; 返回r0