ARM 汇编指令是 ARM 处理器的机器语言,直接控制硬件执行操作。理解 ARM 汇编对于底层开发、性能优化和调试非常重要。
学习arm汇编的主要目的是为了编写arm启动代码,启动代码启动以后,引导程序到c语言环境下运行。换句话说启动代码的目的是为了在处理器复位以后搭建c语言最基本的需求。因此启动代码的主要任务有:
- 初始化异常向量表;
- 初始化各工作模式的栈指针寄存器;
- 开启arm内核中断允许;
- 将工作模式设置为user模式;
- 完成上述工作后,引导程序进入c语言主函数执行;
mov
加载12位立即数到寄存器或转移一个寄存器的值到另外一个寄存器
mov r0, #2 ;加载立即数2到寄存器r0,MOV{S}<c> <Rd>, #<const>
mov r1, r0 ;将r0寄存器的值加载到r1,MOV{S}<c> <Rd>, <Rm>
大多数指令的格式为opcode rd, rn ,rm,其中,rd是目标寄存器,rn是第一操作数寄存器
; mov r0, #1 ; mov r1, #2 ; mov r2,r1 ; mov r3,r0,lsl #2 ; mov r4,r3,lsl r1add
ADD{S}<c> <Rd>, <Rn>, #<const>
ADDS}<c> <Rd>, <Rn>, <Rm>{, <shift>}
ADD R0, R1, R2 ; R0 = R1 + R2 ADD R0, R1, #10 ; R0 = R1 + 10 ADD R0, R1, R2, LSL #2 ; R0 = R1 + (R2 <<sub
SUB{S}<c> <Rd>, <Rn>, #<const>
SUB{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
SUB R0, R1, R2 ; R0 = R1 - R2 SUB R0, R1, #10 ; R0 = R1 - 10立即数
立即数(Immediate)是 ARM 汇编中直接写在指令里的常数,不需要从内存或寄存器读取。
判断条件
立即数是直接写在指令中的常数。ARM 指令只有 12 位给立即数,所以不是所有数字都能直接加载。LDR 伪指令可以加载任意 32 位数,编译器会自动处理。记住:MOV 有立即数限制,LDR 没有限制。
LDR,SDR
ldr r0 ,=0x40000000 ldr r1 ,[r0] ldr r0, =0x40000004 ldr r2, [r0] add r3,r1,r2 ldr r0,=0x40000008 str r3,[r0]STR R0, [R1] ; *R1 = R0(将 R0 的值存到 R1 指向的地址) STR R0, [R1, #4] ; *(R1+4) = R0 STR R0, [R1, R2] ; *(R1+R2) = R0cmp比较指令
CMP比较指令用于比较两个寄存器的值或者比较一个寄存器和立即数的值,其原理是对待比较的两个数求差,看结果是否为0,这个指令会无条件修改N,V,C,Z位。
如:
mov r0, #100
cmp r0, #100
会导致Z置位,从条件码表可知,只要Z置位就是两数相等。
esp:
mov r0,#1 mov r1,#3 mov r3,#5 cmp r0,r1 movlt r2,r1 movgt r2,r0 cmp r2,r3 movlt r2,r3用汇编来写dowhile循环
mov r0,#1 mov r1,#0 lable add r1,r0,r1 add r0,r0,#1 cmp r0,#100 ble table finished b finishB、BL、BX
| 指令 | 全称 | 核心作用 | 是否保存返回地址 | 是否切换状态 |
|---|---|---|---|---|
| B | Branch | 无条件跳转 | ❌ 不保存 | ❌ 不切换 |
| BL | Branch with Link | 函数调用 | ✅ 保存到 LR | ❌ 不切换 |
| BX | Branch and eXchange | 跳转并切换状态 | ❌ 不保存 | ✅ 可切换 |
| 场景 | 用什么指令 | 原因 |
|---|---|---|
| 函数调用(要回来) | BL | 需要保存返回地址到 LR |
| 无条件跳转(不用回来) | B | 不需要返回地址,节省代码 |
| 无限循环 | B自己 | b .或b label |
| 函数返回 | BX LR | 根据 LR 最低位自动切换状态 |
| 状态切换 | BX Rm | 切换 ARM/Thumb 模式 |
| 调用 Thumb 函数 | BLX | 带返回的状态切换调用 |
栈区的保护
arm栈的四种类型
| 类型 | 名称 | 增长方向 | SP指向 |
|---|---|---|---|
| FA | 满增栈 | 向高地址 | 最后一个数据 |
| EA | 空增栈 | 向高地址 | 下一个空位 |
| FD | 满减栈 | 向低地址 | 最后一个数据 |
| ED | 空减栈 | 向低地址 | 下一个空位 |
STMFD= Store Multiple Full Descending 存储多个寄存器到满递减栈LDMFD= Load Multiple Full Descending 从满递减栈加载多个寄存器
_asm_max mov r0,#20 mov r1,#10 stmfd sp!,{r0,r1,lr}//入栈 bl _asm_min ldmfd sp!,{r0,r1,lr}//出栈 cmp r0,r1 movgt r2,r0 movlt r2,r1 mov pc,lr bx lr关于c函数的调用
首先框架开头加上PRESERVE8
PRESERVE8是 ARM 汇编中的一个伪指令,用于告诉链接器代码的栈对齐要求,确保栈在函数调用边界是8 字节对齐的
作用:
import
import(在 C 语言中用#include)是调用 C 函数的前提条件,它告诉编译器函数的声明(函数原型),让编译器知道如何正确调用这个函数。
ldr sp,=0x40001000 import c_add mov r0,#3 mov r1,#4 mov r2,#5 mov r3,#6 stmfd sp!,{r0-r12,lr} stmfd sp!,{r4} bl c_add ldmfd sp!,{r0-r12,lr} ldmfd sp!,{r4} stmfd sp!,{r0-r12,lr} bl _asm_max ldmfd sp!,{r0-r12,lr}其中的参数传递
将c语言传递的参数保存到通用寄存器,返回值保存到r0
export
export在 ARM 汇编中用于将符号(函数名、变量名)导出,让其他文件(C 或汇编)可以调用。它是实现模块化编程和跨文件调用的关键。
_asm_fn export _asm_fn svc #7 bx lr