汇编(实现C语言程序的调用)
一、ARM汇编指令
-
mov: 将一个值或寄存器的内容移动到另一个寄存器中。这个指令可以用来赋值。
示例:
mov r0, #5
// 将立即数 5 移动到寄存器 r0 -
add: 执行加法操作,将两个操作数相加,并将结果存储在目标寄存器中。
示例:
add r0, r1, r2
// 将 r1 和 r2 的值相加,并将结果存储在 r0 中 -
sub: 执行减法操作,从一个寄存器的值中减去另一个寄存器的值,并将结果存储在目标寄存器中。
示例:
sub r0, r1, r2
// 计算 r1 - r2 的值,并将结果存储在 r0 中 -
ldr: 从内存中加载数据到寄存器中。
示例:
ldr r0, [r1]
// 从 r1 指向的地址加载数据到 r0 中 -
bic: 对两个操作数执行按位清除运算,保留第一个操作数中的位,而将第二个操作数中的位清零。
示例:
bic r0, r1, r2
// 清除 r1 中与 r2 中相同的位,将结果存储在 r0 中 -
orr: 对两个操作数执行按位或运算。
示例:
orr r0, r1, r2
// 将 r1 和 r2 的位进行按位或运算,结果存储在 r0 中 -
cmp: 比较两个寄存器中的值,设置相应的标志位(如零标志、负标志等),但不存储结果。
示例:
cmp r0, r1
// 比较 r0 和 r1 的值 -
stmfd: 将多个寄存器的内容存入内存,按全降序存储,通常用于保存寄存器状态。
示例:
stmfd sp!, {r0, r1, r2}
// 将 r0, r1, r2 存储到栈中 -
ldmfd: 从内存中加载多个寄存器的内容,按全升序恢复寄存器状态。
示例:
ldmfd sp!, {r0, r1, r2}
// 从栈中加载 r0, r1, r2 的值
二、跳转语句
-
b: 无条件跳转指令,跳转到指定的标签或地址。这是ARM汇编中最常用的跳转指令。
示例:
b label
// 跳转到名为 label 的位置 -
bl: 带链接的跳转,除了跳转到指定地址外,还将返回地址(下一条指令的地址)保存到链接寄存器(LR 或 r14)中。常用于子程序调用。
示例:
bl function
// 跳转到 function,并将返回地址存储在 LR 中 -
bx: 通过指定的寄存器地址跳转,并且可以根据寄存器的最低位设置处理器的状态(如进入 ARM 或 Thumb 状态)。它通常用于从子程序返回。
示例:
bx r0
// 跳转到 r0 寄存器中存储的地址
栈指针寄存器
- SP(栈指针寄存器): SP 寄存器用于指向当前栈的顶部。栈通常用于存储局部变量、保存返回地址等。当使用
stmfd
和ldmfd
指令时,stack pointer (SP) 寄存器可用于管理函数调用和返回。
CPSR 寄存器相关指令
-
mrs: 从 CPSR(当前程序状态寄存器)读取数据。当需要获取当前状态或标志位时,使用此指令。
示例:
mrs r0, cpsr
// 将 CPSR 的值加载到 r0 中 -
msr: 设置 CPSR 的某些位。这可以用于改变处理器的状态或控制特权级别。
示例:
msr cpsr_c, r0
// 将 r0 的内容写入 CPSR 中的控制位
三、汇编
preserve8 ; 保留8字节对齐
area reset, code, readonly ; 定义一个名为“reset”的只读代码区域
code32 ; 设定代码为32位
entry ; 定义入口点
b start ; 跳转到 start 标签,开始程序执行
ldr pc, =do_undifined ; 将 do_undifined 的地址加载到程序计数器(PC),设置未定义指令处理
ldr pc, =do_swi ; 将 do_swi 的地址加载到 PC,设置软件中断处理
ldr pc, =do_p_abort ; 将 do_p_abort 的地址加载到 PC,设置预取中止处理
ldr pc, =do_d_abort ; 将 do_d_abort 的地址加载到 PC,设置数据中止处理
nop ; 空操作,通常用于填充或延迟
ldr pc, =do_irq ; 将 do_irq 的地址加载到 PC,设置外部中断处理
ldr pc, =do_fiq ; 将 do_fiq 的地址加载到 PC,设置快速中断处理
do_fiq ; 快速中断处理程序
b do_fiq ; 无限循环,等待快速中断
do_irq ; 外部中断处理程序
import irq_handler ; 导入 irq_handler 函数
sub lr, lr, #4 ; 将链接寄存器(LR)减去4,以调整返回地址
stmfd sp!, {r0-r12, lr} ; 将 r0-r12 和 LR 保存到栈中,进行现场保护
bl irq_handler ; 调用中断处理程序
ldmfd sp!, {r0-r12, pc}^ ; 恢复寄存器并返回
do_d_abort ; 数据中止处理程序
b do_d_abort ; 无限循环,等待数据中止
do_p_abort ; 预取中止处理程序
b do_p_abort ; 无限循环,等待预取中止
do_swi ; 软件中断处理程序
import swi_handler ; 导入 swi_handler 函数
stmfd sp!, {r0-r12, lr} ; 保存 r0-r12 和 LR 到栈中
bl swi_handler ; 调用软件中断处理程序
ldmfd sp!, {r0-r12, pc}^ ; 恢复寄存器并返回
do_undifined ; 未定义指令处理程序
b do_undifined ; 无限循环,等待未定义指令
start ; 程序启动点
ldr sp, =0x40001000 ; 将栈指针(SP)初始化为指定地址
mrs r0, cpsr ; 读取当前程序状态寄存器(CPSR)
bic r0, r0, #0x1F ; 清除 CPSR 中的模式位
orr r0, r0, #0x12 ; 设置 CPSR 进入 SVC 模式
bic r0, #(1 << 7) ; 清除 CPSR 中的 F 标志位
msr cpsr_c, r0 ; 更新 CPSR
ldr r0, =0x40001000 ; 将地址 0x40001000 加载到 r0
sub r0, r0, #1024 ; 计算栈顶地址
mov sp, r0 ; 设置栈指针 SP
mrs r0, cpsr ; 读取当前 CPSR
bic r0, r0, #0x1F ; 清除模式位
orr r0, r0, #0x10 ; 设置 CPSR 进入 IRQ 模式
msr cpsr_c, r0 ; 更新 CPSR
ldr r0, =0x40001000 ; 将地址 0x40001000 加载到 r0
sub r0, r0, #2048 ; 计算新的栈顶地址
mov sp, r0 ; 设置栈指针 SP
import main ; 导入 main 函数
b main ; 跳转到 main 函数
asm_fn ; 汇编函数的标签
export asm_fn ; 导出 asm_fn 函数
swi #7 ; 触发软件中断,调用系统服务
bx lr ; 返回到调用者
finished ; 完成标签
b finished ; 无限循环,等待程序结束
end ; 汇编代码的结束标志