ARM-汇编的基础知识
指令集版本:ARMv7-A
1.汇编指令
mov 指令:加载12位立即数到寄存器或转移一个寄存器的值到另一个寄存器
例如:
mov r0, #8 MOV{S}<C> <Rd>, #<const>
mov r1, r0 MOV{S}<C> <Rd>, <Rn>
Rd是目标寄器,Rn是第一操作寄存器
add指令:
add r0, r1, #12 ADD{S}<C> <Rd>, <Rn>, #<const>
add r0, r0, r1 ADD{S}<C> <Rd>, <Rn>, <Rm>
类似指令:sub指令(减法)
SUB{S}<c> <Rd>, <Rn>, #<const>
SUB{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}
判断一个数是不是立即数?
1.如果某个数的数值范围是0~0xFF之间,那么这个数一定是立即数
2.把某个数展开成2进制,这个数的最高位1至最低位1之间的二进制数序列的位数不能超过8位
3.这个数的二进制序列凑够8位之后的的右边必须为偶数个连续的 0
例如:0x234 = 0000 0000 0000 0000 0000 0010 0011 0100
最高位1至最低位1之间的二进制数序列:1000 1101没有超过8位末尾1的右边有2个0,所以0x234是立即数
0x132 = 0000 0000 0000 0000 0000 0001 0011 0010
最高位1至最低位1之间的二进制数序列:1001 1001 从第一个1开始到最后一个1之间没有超过8位
末尾1的右边有1个0,不满足第二条,所以0x132不是立即数
判断一个数是否为有效的 ARM 立即数,需要看它能否通过8 位基值(Byte)和 4 位循环右移量(Shift) 组合生成。
1. ldr
寄存器加载指令(Load Register)
- 功能:从内存读取数据到寄存器。
- 格式:
ldr <目标寄存器>, <内存地址>
- 说明:支持直接地址、寄存器间接寻址、带偏移量的寻址等。
ldr r0, =0x12345678 ; 将立即数0x12345678加载到r0(伪指令,实际可能转为mov或ldr)
ldr r1, [r2] ; 读取r2指向的内存地址的数据到r1
ldr r3, [r4, #4] ; 读取r4+4地址处的数据到r3(前索引寻址)
ldr r5, [r6], #8 ; 读取r6指向的地址数据到r5,之后r6 +=8(后索引寻址)
2. bic
指令(Bit Clear)
- 功能:清除寄存器中的特定位(按位与非操作)。
- 格式:
bic <目标寄存器>, <源寄存器1>, <源寄存器2/立即数>
- 运算逻辑:
目标寄存器 = 源寄存器1 & (~源寄存器2/立即数)
bic r0, r1, #0x0F ; 清除r1的低4位(0x0F对应二进制低4位为1),结果存到r0
bic r2, r3, r4 ; 清除r3中与r4对应为1的位,结果存到r2
3. orr
指令(Bitwise OR)
- 功能:对寄存器进行按位或操作,可用于设置特定位。
- 格式:
orr <目标寄存器>, <源寄存器1>, <源寄存器2/立即数>
- 运算逻辑:
目标寄存器 = 源寄存器1 | 源寄存器2/立即数
orr r0, r1, #0x01 ; 置位r1的第0位(最低位),结果存到r0
orr r2, r3, r4 ; r3与r4按位或,结果存到r2
4. str
指令(Store Register)
- 功能:将寄存器中的数据写入内存。
- 格式:
str <源寄存器>, <内存地址>
- 说明:寻址方式与
ldr
对应,方向相反(寄存器→内存)。
str r0, [r1] ; 将r0中的数据写入r1指向的内存地址
str r2, [r3, #4] ; 将r2中的数据写入r3+4地址处(前索引)
str r4, [r5], #8 ; 将r4中的数据写入r5指向的地址,之后r5 +=8(后索引)
汇编指令的s后缀:s后缀是指指令在执行过程中更新CPSR寄存器的N、V、C、Z 位
N: 在结果时有符号的二进制补码情况下,结果为负数,则N= 1;非负数, N=0.
Z:如果结果为0,则Z=1;如果结果为非零,否则Z=0。
C: 是针对无符号数最高有效位向更高位进位时C=1;减法中运算结果的最高有效位从更高位借位时C=0。
V :该位针对有符号位操作,两个最高有效位均为0的数相加,得到的最高有效位为1 或者
两个最高有效位均为1的数相加,得到的结果最高有效位为0;除了这两种情况以外V位为0
几乎所有的arm指令都可以在指令之后可选地增加执行条件
cmp r0, r1 ;比较movge r3, r0 ;如果大于,将r0的值赋给r3movls r3, r1 ;如果小于,将r1的值赋给r3
CMP比较指令用于比较两个寄存器的值或者比较一个寄存器和立即数的值,其原理是对待比较的两个数求差,看结果是否为0,这个指令会无条件修改N,V,C,Z位
5.跳转指令
无条件跳转指令 b
(Branch)
- 功能:直接跳转到目标地址执行,不返回原地址。
start:mov r0, #1b loop ; 无条件跳转到loop标签处
loop:add r0, r0, #1b loop ; 循环跳转(死循环)
带链接的跳转指令 bl
(Branch with Link)
- 功能:跳转前将下一条指令地址存入
lr
(r14,链接寄存器),用于函数调用后返回。
main:bl add_func ; 调用add_func,lr = 下一条指令地址(mov r1, r0)mov r1, r0 ; 函数返回后执行此句...add_func:add r0, r1, r2 ; 函数逻辑mov pc, lr ; 从lr恢复返回地址,跳转回main
远跳转指令 ldr pc, =<目标地址>
通过加载 32 位地址到程序计数器(pc)实现大范围跳转(不受 ±32MB 限制
入方式栈有:满增、满减、空增、空减
arm体系采用的方案是满减
入栈保护指令:stmfd
STMFD<c> <Rn>{!}, <registers>
出栈恢复指令:ldmfd
LDMFD<c> <Rn>{!}, <registers>
向c函数传参
向c函数传参的方法很简单,如果参数个数小于等于4个,就直接用r0~r3传参,c函数返回值通过r0寄存器返回:
; 设置栈指针(SP)的值为0x40001000
; 栈是程序运行时用于临时存储数据和保存上下文的内存区域
ldr sp, =0x40001000 ; 将寄存器r0到r12以及lr(链接寄存器)的值压入栈中保存
; stmfd: 满递减栈(Stack Memory Full Descending),!表示更新sp的值
; 这一步是为了保护现场,防止后续操作修改这些寄存器的值
stmfd sp!, {r0-r12, lr}; 导入外部的C语言函数c_max(声明该函数在其他文件中定义)
import c_max; 给寄存器r0赋值5,r0将作为c_max函数的第一个参数
mov r0, #5
; 给寄存器r1赋值7,r1将作为c_max函数的第二个参数
mov r1, #7; 将r0和r1的值压入栈中保存(临时保护这两个参数寄存器)
; 因为调用函数时可能会修改r0-r3,这里保存是为了后续可以恢复
stmfd sp!, {r0-r1}; 调用C语言函数c_max,会自动将返回地址存入lr寄存器
; 函数执行完后会返回到下一条指令(ldmfd)
bl c_max; 从栈中恢复r0和r1的值(出栈),!表示更新sp的值
; 恢复之前保存的参数寄存器
ldmfd sp!, {r0-r1}; 从栈中恢复r0到r12以及lr寄存器的值(出栈)
; 恢复现场,还原调用前的寄存器状态
ldmfd sp!, {r0-r12, lr}
如果参数个数大于4个,从第五个参数开始就需要通过栈来传参
在c语言中调用汇编编写的函数类似,不过在汇编中用export声明函数,同时需要在c语言中用extern声明函数,按照标准,调用者负责保护现场和恢复现场
MRS 和 MSR的用法
在 ARM 汇编中,mrs
和 msr
是用于访问特殊寄存器(如程序状态寄存器 CPSR/SPSR 等)的指令
mrs <通用寄存器>, <特殊寄存器>
mrs r0, cpsr ; 将当前程序状态寄存器(CPSR)的值读取到r0
mrs r1, spsr ; 将保存的程序状态寄存器(SPSR)的值读取到r1
读取 CPSR 中的标志位(如 Z、N、C、V 等)进行判断
msr <特殊寄存器><字段>, <通用寄存器/立即数>
msr cpsr_c, r0 ; 将r0的值写入CPSR的控制位字段(修改处理器模式、中断使能等)
msr cpsr, #0xD3 ; 直接写入立即数到CPSR(0xD3对应特权模式,关闭IRQ和FIQ中断)
**字段说明:cpsr
可分为 4 个 8 位字段(c
:控制位,x
:扩展位,s
:状态位,f
:标志位),可指定修改特定字段(如 cpsr_c
仅修改控制位)