Linux学习-ARM汇编指令
一、移位指令
- 逻辑左移(LSL):
MOV <Rd>, <Rm>, LSL #<n>
,功能是逻辑左移,低位补0。 - 算术右移(ASR):
MOV <Rd>, <Rm>, ASR #<n>
,功能是算术右移,符号位保持不变。
二、 什么是立即数?如何判断某数是不是一个12位立即数?
- 立即数:指在指令中直接包含的常数,无需从内存或寄存器读取。例如
MOV R0, #0x123
中的0x123
就是立即数。 - 12位立即数判断:判断一个数是否为合法的12位立即数,需满足以下三点:
- 数值范围在
0~0xFF
(8位立即数)。 - 将该数展开为二进制后,从最高位1到最低位1之间的二进制数序列的位数不超过8位。
- 该数二进制最低位1后的0的个数为偶数。
三、加载指令
- LDR伪操作(加载非立即数):
Ldr <Rd>, =非立即数
。 - LDR内存加载:
LDR <cc> <Rd>, [<Rn>], #+/- <im12>
,用于从内存中加载数据,支持条件执行(<cc>
)和自动索引。
位操作指令
- ** bic(位清除)**:功能是指定位清零。
- ** orr(位设置)**:功能是指定位置1。
四、b.bl
与 bx
指令的区别
-
b/bl
:- 用于相对跳转(基于当前PC值计算偏移),只能跳转到当前指令前后±32MB范围内。
b
是单纯跳转;bl
跳转前会将返回地址存入lr
(链接寄存器),用于函数调用。- 不改变处理器状态(ARM/Thumb模式不变)。
-
bx
:- 用于绝对跳转(目标地址来自寄存器,如
bx r0
),可跳转到任意地址。 - 跳转时会根据目标地址的最低位(0:ARM模式,1:Thumb模式)切换处理器状态。
- 不自动保存返回地址,需手动处理(如
mov lr, pc; bx r0
)。
- 用于绝对跳转(目标地址来自寄存器,如
五、条件标志位(CPSR寄存器的N、Z、C、V位)
汇编指令加S
后缀时,执行过程会更新CPSR寄存器的这四个标志位:
- N(负标志位):在结果是有符号二进制补码的情况下,结果为负数则
N=1
,非负数则N=0
。 - Z(零标志位):结果为0则
Z=1
,非零则Z=0
。 - C(进位/借位标志位):
- 无符号数最高有效位向更高位进位时,
C=1
; - 减法中运算结果的最高有效位从更高位借位时,
C=0
。
- 无符号数最高有效位向更高位进位时,
- V(溢出标志位):
- 有符号数中,两个最高有效位均为0的数相加,结果最高有效位为1;或两个最高有效位均为1的数相加,结果最高有效位为0时,
V=1
; - 其余情况
V=0
。
- 有符号数中,两个最高有效位均为0的数相加,结果最高有效位为1;或两个最高有效位均为1的数相加,结果最高有效位为0时,
六、跳转与函数调用
- 无条件跳转(b):
b label
,直接跳转到label
处执行。
示例:b label mov r0, #3 ; 跳转会跳过该指令 mov r1, #5 label: mov r2, #4
- 函数调用(bl):
bl 函数名
,跳转的同时将返回地址保存到lr
寄存器,用于函数返回。 - 循环实现(类似while(1)):
finished: b finished
七、栈操作(STMDB/STMFD、LDMIA/LDMFD)
- 入栈(STMDB/STMFD):将寄存器列表中的值压入栈中,
STMFD
是STMDB
的别名(满递减栈)。
示例:stmfd sp!, {r0 - r8}
,将r0
到r8
的寄存器值压入栈,sp
自动递减。 - 出栈(LDMIA/LDMFD):从栈中弹出值到寄存器列表,
LDMFD
是LDMIA
的别名(满递减栈)。
四种栈类型的区别
栈的类型由栈指针(SP)指向的位置和入栈/出栈时SP的变化方向决定:
- 满栈(Full):SP指向最后一个已入栈的数据(非空)。
- 空栈(Empty):SP指向即将入栈的空位置(栈顶无数据)。
- 增栈(Ascending):入栈时SP增大(向高地址生长)。
- 减栈(Descending):入栈时SP减小(向低地址生长)。
具体分类:
- 满减栈(Full Descending, FD):SP指向最后入栈的数据,入栈时SP减小(
STMFD SP!, {R0-R3}
)。 - 满增栈(Full Ascending, FA):SP指向最后入栈的数据,入栈时SP增大。
- 空减栈(Empty Descending, ED):SP指向空位置,入栈时SP减小。
- 空增栈(Empty Ascending, EA):SP指向空位置,入栈时SP增大。
ARM内核使用的栈类型
ARM默认推荐使用满减栈(FD),这是ARM架构中最常用的栈类型。例如:
- 函数调用时通过
STMFD SP!, {R0-R3, LR}
保存寄存器,LDMFD SP!, {R0-R3, PC}
恢复现场。 - 操作系统(如Linux)和多数编译器均默认采用FD栈。
八、ARM汇编与C函数调用的规则(ATPCS标准)
为确保调用兼容性,需遵循ARM-Thumb Procedure Call Standard (ATPCS):
(1)汇编调用C函数
- 参数传递:前4个参数用
R0-R3
传递,超过4个的参数入栈(满减栈,参数从右向左入栈)。 - 返回值:32位返回值存
R0
,64位存R0-R1
。 - 寄存器保护:
- 调用者保存:
R0-R3
、R12
(IP)、LR
(返回地址)。 - 被调用者(C函数)保存:
R4-R11
、SP
(栈指针)。
- 调用者保存:
- 调用步骤:
; 传递参数(前4个用R0-R3) MOV R0, #1 ; 第一个参数 MOV R1, #2 ; 第二个参数 ; 调用C函数(自动保存LR) BL c_function ; 结果在R0中
(2)C调用汇编函数
- 函数声明:C中声明汇编函数为
extern
,如extern int asm_func(int a, int b);
。 - 汇编函数实现:
- 用
EXPORT
导出函数名(如EXPORT asm_func
)。 - 遵循参数传递规则(从
R0-R3
取参数)。 - 保护
R4-R11
(若使用需入栈保存,退出前恢复)。 - 用
BX LR
返回(或MOV PC, LR
,需确保模式正确)。
- 用
- 示例:
EXPORT asm_func asm_func:; R0 = a, R1 = bADD R0, R0, R1 ; 计算a+b,结果存R0BX LR ; 返回
九、特殊寄存器操作(MRS、MSR)
- MRS(读取特殊寄存器):格式为
MRS <cc> <Rd>, <spe_reg>
,用于将特殊寄存器(如CPSR)的值读取到通用寄存器。
示例:MRS r0, cpsr
,将CPSR的值读到r0
。 - MSR(写入特殊寄存器):格式为
MSR <cc> <spe_reg>, <Rd>
,用于将通用寄存器的值写入特殊寄存器。
示例:MSR cpsr, r0
,将r0
的值写入CPSR。 - 模式切换示例(以User模式为例):
mrs r0, cpsr ; 读取CPSR到r0 bic r0, r0, #0x1F ; 清除模式位 orr r0, r0, #0x10 ; 设置为User模式 msr cpsr, r0 ; 写入CPSR完成模式切换 ; 若在Keil中上述指令报错,可改用:msr cpsr_c, r0