目录
[🔹 Rd ------ 目标寄存器 (Destination Register)](#🔹 Rd —— 目标寄存器 (Destination Register))
[🔹 Rn ------ 第一操作数寄存器 (First Operand Register)](#🔹 Rn —— 第一操作数寄存器 (First Operand Register))
[🔹 Operand2 ------ 第二操作数 (Second Operand)](#🔹 Operand2 —— 第二操作数 (Second Operand))
[1. ARM 的堆栈类型](#1. ARM 的堆栈类型)
[2. STMFD 与 LDMFD 指令](#2. STMFD 与 LDMFD 指令)
[2.1 STMFD ------ 入栈(Push)](#2.1 STMFD —— 入栈(Push))
[2.2 LDMFD ------ 出栈(Pop)](#2.2 LDMFD —— 出栈(Pop))
[3. 感叹号(!)的作用](#3. 感叹号(!)的作用)
[4. 初始化栈sp寄存器(满递减栈):](#4. 初始化栈sp寄存器(满递减栈):)
[1.使用swi 软中断 ,会进入异常向量表](#1.使用swi 软中断 ,会进入异常向量表)
[2.1 计算 SWI 指令的地址](#2.1 计算 SWI 指令的地址)
[2.2 读取 SWI 指令](#2.2 读取 SWI 指令)
[2.3 提取 24 位软件中断号](#2.3 提取 24 位软件中断号)
[2.4 恢复现场并返回(^ )的作用](#2.4 恢复现场并返回(^ )的作用)
一、C语言基础
讲解汇编原理之前,用一副图回顾一下C语言基础
1.数据类型

2.变量类型

要记住指针和基类型这两个的定义
这里列举一些例子,判断是什么变量类型只需要看最后的两个字是什么。

补充知识点:
定义一个变量,如果是全局的变量则不需要初始化为0,默认就是0
如果是局部变量则需要初始化为0,不然就是随机值
二、汇编数据处理指令

语法解释:
🔹
Rd------ 目标寄存器 (Destination Register)
含义 :存放指令运算结果的寄存器。
特点:通常是第一个参数。
例子 :
ADD R0, R1, R2中的 R0 就是 Rd。意思是"把运算结果保存在 R0 里"。🔹
Rn------ 第一操作数寄存器 (First Operand Register)
含义:存放第一个参加运算的数值的寄存器。
特点 :它是源操作数,是运算的基础数据。
例子 :
ADD R0, R1, R2中的 R1 就是 Rn。意思是"用 R1 的值加上别的数"。🔹
Operand2------ 第二操作数 (Second Operand)
含义 :第二个参与运算的数据。它非常灵活,这也就是图片里提到的"桶型移位器"的威力所在。
特点 :它不一定是 一个固定的数字,也不一定只是一个寄存器,它可以是一个经过移位处理的寄存器值。
三种形式:
立即数 :一个常数。例如
ADD R0, R1, #5中的 #5 。(**指令中自带的常量,**读取速度最快,因为CPU执行到这条指令时,数据已经"在手边"了)寄存器 :直接使用另一个寄存器的值。例如
ADD R0, R1, R2中的 R2。移位寄存器 :先把一个寄存器的值进行移位(左移、右移等),然后再参与运算。这是 ARM 的特色。例如
ADD R0, R1, R2, LSL #2,意思是先把 R2 的值左移2位(相当于乘以4),再加上 R1 的值,最后存到 R0。
1.ADD/ADC:
ADD(不更新标志位),以及ADDS(更新标志位但不进位)
add r0, r0, r1 ;汇编中的注释用;号表示,
;把r1和r0的值加起来然后存到r0寄存器中
ADC(进位,CPSR的进位C会更新)
如果是32位的寄存器要计算0x1 ffff ffff 和0x 04的加法运算:

bash
mov r0 #0x1
mov r1,#0xffffffff
mov r2,#0
mov r3,#4
adds r5, r1, r3
adc r4, r0, r2
这里ADDS指令执行后,CPSR中的进位C更新了,然后ADC指令会读取CPSR中的进位C加到当前的寄存器中
2.SUB
和add一样,减法运算,也有subs和sbc
3.AND/ORR/EOR
and按位与:
对 Rn 和 Operand2 的每一位进行与运算,结果存到rd中
and rd,rn,operand2
orr按位或
对 Rn 和 Operand2 的每一位进行或运算,结果存到rd中
orr rd,rn,operand2
eor按位异或
对 Rn 和 Operand2 的每一位进行异或运算,结果存到rd中
eor rd,rn,operand2
5.BIC
效果:按位清除,用 Operand2 中为1的位去清除 Rn 中对应的位(将那些位强制变为0)
bic rd, rn, #0x0F → 将 rn 的低4位清零(无论原来是0还是1),其余位保持不变,结果存入 rd。
6.MOV/MVN/LDR
| 指令 | 核心作用 | 示例 |
|---|---|---|
MOV |
将数据原样复制到目标寄存器 | MOV R0, #0x0F → R0的值变为 0x0F |
MVN |
将数据按位取反(Bitwise NOT) 后再传送到目标寄存器 | MVN R0, #0x0F → R0的值变为 0xFFFFFFF0 (即~0x0F) |
| LDR | 加载任意 32 位立即数 | ldr sp, =0x40001000 |
arm中MOV可以加载0x0-0xFF范围的立即数,但超过这个范围就需要一些规则来存,比如,MOV指令不能直接加载像0x40001000这样的大数值,这是因为ARM的MOV指令对立即数有严格的编码限制。一旦你尝试使用超出范围的立即数,汇编器就会报错(报警)。而LDR伪指令则专门用来解决这个问题。
7.CMP
cmp比较(结果放在CPSR,所以c语言中if的条件判断结果都是放在CPSR中)
通过cmp加上条件码,我们就可以实现c语言的if语句和switch语句

比如我想判断r0和r1的大小然后把数据填入r2中,把条件码写到mov后即可:

如果是多个条件怎么实现呢,结果是再cmp中嵌套cmp,举个例子(这个例子的代码逻辑有问题,但能看懂if的多条件在汇编中怎么实现的就行)

8.B
类似于c语言中的goto
用这个可以实现函数的调用和c语言中的while语句

b后面也可以跟条件码,这是一个1-100的累加,加到第100个数跳出循环
三、函数的调用原理
调用函数,就必须了解arm中的SP寄存器
在 ARM 汇编中,入栈 和出栈 通常使用 STM (Store Multiple)和 LDM (Load Multiple)指令,配合 FD (Full Descending)后缀,再加上一个 感叹号(!) 来控制堆栈指针(SP)的更新。下面我们详细拆解。
1. ARM 的堆栈类型
ARM 支持多种堆栈模式,但嵌入式开发中最常用的是 满递减堆栈(FD, Full Descending):
-
满 :SP 指向最后压入的数据(即栈顶)。
-
递减 :压栈时 SP 向低地址移动(先减后存),出栈时向高地址移动(先取后加)。
因此,ARM 专门设计了 STMFD 和 LDMFD 指令来完美匹配这种堆栈操作。
2. STMFD 与 LDMFD 指令
2.1 STMFD ------ 入栈(Push)
-
语法 :
STMFD SP!, {寄存器列表} -
功能 :将寄存器列表中的内容按顺序压入堆栈,并更新 SP。
-
实际动作(满递减栈):
-
从高地址到低地址依次将寄存器存入 SP 指向的地址。
-
每次存一个寄存器前,SP 先减 4(因为 ARM 是 32 位,一个寄存器占 4 字节)。
-
存入后 SP 指向新的栈顶。
-
感叹号表示将最终 SP 的值写回 SP 寄存器。
-
2.2 LDMFD ------ 出栈(Pop)
-
语法 :
LDMFD SP!, {寄存器列表} -
功能 :将堆栈中的数据弹出到寄存器列表,并更新 SP。
-
实际动作(满递减栈):
-
从当前 SP 指向的地址读取数据到寄存器。
-
每读一个寄存器,SP 加 4。
-
全部读完,SP 指向新的栈顶(即原来栈顶之上的位置)。
-
感叹号同样表示更新 SP。
-
3. 感叹号(!)的作用
感叹号 称为 "写回" (Write-back)标志,表示在完成数据传输后,将计算出的最终地址写回基址寄存器(这里就是 SP)。
如果没有 !,则 SP 在入栈或出栈后不会自动改变 ,这在需要重复使用同一块栈空间时有用,但堆栈操作通常必须更新 SP,所以 ! 是必不可少的。
例如:
-
STMFD SP, {R0-R3}→ 将寄存器压栈,但 SP 不变(危险,下次压栈会覆盖)。 -
STMFD SP!, {R0-R3}→ 压栈后 SP 自动向下移动 4×4 = 16 字节,指向新的栈顶。
4. 初始化栈sp寄存器(满递减栈):
ldr sp, =0x40001000(SP通常要求8字节对齐(AAPCS标准))

栈底为 0x40000000,那么栈空间大小就是 0x1000 字节
5.栈使用(函数调用原理)

(1)调用原理(栈SP寄存器):
进入函数后保存当前状态:入栈r4-r12的值以及lr
函数执行完毕后恢复状态:出栈r4-r12的值,以及把lr的值出栈到pc上
(2)参数传递:
ARM 调用约定(ATPCS) 规定:
前 4 个参数通过 R0 ~ R3 传递。
第 5 个及之后的参数通过栈传递,并且由调用者负责在调用前将参数压栈,调用后负责恢复栈指针(一般由调用者平衡栈)。
如:
总结超过四个参数后,调用函数会自动往栈上去找值
(3)跨文件汇编和c语言于、汇编混合编译:
汇编文件需要用到C语言的函数:
要在汇编文件里面导入:
import c_add(函数名)
c语言文件要用汇编的函数:
首先在汇编文件导出
export asm_add(函数名)
然后在c文件中声明这个函数再调用:
int asm_add(int x, int y);
这个函数声明你随便任意类型都行,甚至多个参数都可以,但是实现的功能只能是汇编中写 的
C跟汇编混合编程时需要字节对齐,头部要加preserve8
四、更改工作模式
使用MRS和MSR指令

_fields是域:即图中的四块区域f,s,x,c
注意不同的工作模式下SP是独立的,所以进入USR模式后要把sp寄存器的值重新给一下
start ;主程序
ldr sp, =0x40001000 ;初始化栈指针
mrs r0, cpsr ;将当前程序状态寄存器(CPSR)的值读取到通用寄存器 r0
bic r0, r0, #0x1f ;将 r0 的低 5 位清零(0x1F = 31)。低 5 位是 CPSR 的模式位(M[4:0]),这一步是清除当前模式
orr r0, r0, #0x10 ;将 r0 的低 5 位设置为 10000(二进制),即 用户模式(User Mode)的编码。
msr cpsr_c, r0 ;将 r0 的值写入 CPSR 的控制域(c 表示控制域,包含模式位、中断使能位等)
ldr sp, =0x40000c00 ;为用户模式设置一个新的栈指针 sp
示例代码中工作模式为USER

五、中断原理(异常处理)
1.使用swi 软中断 ,会进入异常向量表
swi可以带入参数如:swi #3、swi #7
这里要说明一下,进入向量表后,PC会跳转到相应的地址:

所以在b start的后面不能有任何其他无关指令,修改示例为(nop为任意函数或指令):
bash
preserve8
area reset, code, readonly
code32
entry
b start ;reset这里是0x00
nop ;undef这里是0x04,依次往下4个字节
b deal_swi ;swi
nop ;pre abort
nop ;data abort
nop ;reserved
nop ;irq
nop ;fiq
start
ldr sp, =0x40001000
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0x10
msr cpsr_c, r0
ldr sp, =0x40000c00
2.中断函数:
这里的swi我传入了参数7,进入函数后会处理这个SWI

swi的中断向量表位置我们使用函数来表达,在里面处理这个数据把参数提取出来(因为数据和指令混合在一起的)
bash
deal_swi
stmfd sp!, {r4-r12, lr} ; 保存现场
sub r0, lr, #4 ; 计算 SWI 指令的地址
ldr r1, [r0] ; 读取 SWI 指令本身
bic r0, r1, #(0xff << 24) ; 提取低 24 位(SWI 号)
import c_deal_swi ; 声明外部 C 函数
bl c_deal_swi ; 调用 C 函数,参数在 r0 中
ldmfd sp!, {r4-r12, pc}^ ; 恢复现场并返回,同时切换模式
我们详细讲解这个函数是如何提取swi的参数的:
2.1 计算 SWI 指令的地址
sub r0, lr, #4
-
当 SWI 异常发生时,处理器会自动将 返回地址 存入当前模式的
lr寄存器中。对于 SWI 异常,lr中存放的是 SWI 指令的下一条指令地址。 -
为了获得 SWI 指令本身,只需要将
lr减去 4 即可,因为 ARM 指令长度固定为 4 字节。 -
执行后,
r0中保存了 SWI 指令的内存地址。
2.2 读取 SWI 指令
ldr r1, [r0]
- 从
r0指向的内存地址(即 SWI 指令所在位置)读取一个 32 位的指令字,存入r1。
2.3 提取 24 位软件中断号
bic r0, r1, #(0xff << 24)
最后r0寄存器就存入了我们的中断号7,这时候再设计一个switch函数来判断中断号并执行相对于的语句就是我们熟悉的c语言中的中断服务函数了
2.4 恢复现场并返回(^ )的作用
ldmfd sp!, {r4-r12, pc}^
-
^后缀 的作用:-
当
pc在寄存器列表中且使用^时,除了从堆栈中恢复pc(即返回地址)外,还会将当前模式的 SPSR 自动恢复到 CPSR(上一篇文章的异常处理过程) -
这实现了从异常模式(如 SVC 模式)返回到中断发生前的模式(如用户模式或系统模式),同时恢复处理器状态标志、中断使能位等。
-
-
如果没有
^,则仅仅将pc弹出,但不恢复 CPSR,这会导致返回后仍处于异常模式,且状态标志可能错误。
因此,这一条指令完成了:
-
恢复
r4~r12的原始值。 -
从堆栈中弹出返回地址给
pc,跳回原程序。 -
同时将 SPSR 恢复到 CPSR,完成模式切换和状态恢复。
六、补充
注意:代码段area reset中的reset不要和其他的汇编文件重名了,会报错
本文没有讲解全部的数据指令,这里简单列出:

