一、概述
在 PowerPC 的汇编代码中,我们需要实现调用 C 函数(例如回调函数),并传递参数。本文将详细介绍如何通过一系列步骤完成这一目标,包括代码示例和详细的注释。
二、调用 C 函数的基本步骤及代码
1. 保存工作寄存器
在调用 C 函数之前,需要先保存部分工作寄存器的内容以防止数据丢失。以下是保存 r0
寄存器值的代码片段:
Assembly
asm
; 保存 r0 工作寄存器到栈上偏移为 32 字节的位置
e_stw r0, 32(r1)
2. 加载 C 函数地址
接下来,我们将 C 回调函数 EXCEP_Err_handler
的地址加载到 r0
寄存器中,以便后续跳转执行。代码如下:
asm
; 使用 e_lis 和 e_or2i 将 EXCEP_Err_handler 地址分两次加载至 r0
e_lis r0, EXCEP_Err_handler@h ; 高位地址加载到 r0
e_or2i r0, EXCEP_Err_handler@l ; 低位地址与高位地址组合
3. 保存关键寄存器状态
为了保证函数调用完成后可以恢复原始状态,需提前保存一些重要的寄存器内容,如链接寄存器 (LR)、条件寄存器 (XER)、计数寄存器 (CTR) 和条件码寄存器 (CR)。代码如下:
asm
; 保存 LR(链接寄存器)
se_mflr r3 ; 把 LR 值移动到 r3
e_stw r3, 8(r1) ; 将 r3 内容存储到堆栈指定位置
; 保存 XER(条件寄存器)
mfspr r3, 1 ; 从 SPR(特殊用途寄存器) 中读取 XER 值放到 r3
e_stw r3, 20(r1) ; 存储到堆栈对应位置
; 保存 CTR(计数寄存器)
se_mfctr r3 ; 从控制寄存器获取 CTR 值放入 r3
e_stw r3, 24(r1) ; 存储到堆栈对应位置
; 保存 CR(条件码寄存器)
mfcr r3 ; 从处理器内部读取 CR 值到 r3
e_stw r3, 28(r1) ; 存储到堆栈对应位置
4. 保存通用寄存器 R4-R12
同样地,为了避免通用寄存器 (R4-R12
) 数据被覆盖,在调用前也需要将其保存到栈中。代码如下:
asm
; 分别保存 R4 至 R12 到堆栈相应位置
e_stw r4, 40(r1)
e_stw r5, 44(r1)
e_stw r6, 48(r1)
e_stw r7, 52(r1)
e_stw r8, 56(r1)
e_stw r9, 60(r1)
e_stw r10, 64(r1)
e_stw r11, 68(r1)
e_stw r12, 72(r1)
5. 确定中断源并向 C 函数传参
此阶段的主要任务是确定中断来源并将相关参数准备好供 C 函数使用。其中,CSRR0
的值以及当前指令会被作为参数传递给 C 函数。代码如下:
asm
; 设置中断向量所在的 r0 值到链接寄存器 LR 中
se_mtlr r0
; 获取 CSRR0 并准备第一个参数 (无符号长整型返址)
mfspr r3, 570
; 当前指令值提取出来准备第二个参数 (无符号短整型指针)
e_lhz r4, 0(r3)
6. 跳转到 C 函数
最后一步,我们利用 se_blrl
指令跳转到已经加载好的 C 函数地址处运行,并等待其返回结果继续往下走。
asm
; 执行间接分支操作并保持链接关系不变
se_blrl
三、C 函数原型声明
下面是上述汇编代码所调用的 C 函数定义形式:
unsigned long EXCEP_Err_handler(unsigned long return_address, unsigned short instruction);
该函数接收两个参数:一个是无符号长整形表示的返回地址;另一个则是无符号短整形代表的具体指令值。最终它会给出一个无符号长整形的结果值反馈回来。
总结
借助以上六个步骤的操作流程图解分析可知,在 PowerPC 架构下的汇编程序里成功嵌套了针对外部 C 类语言所提供的功能支持模块——即所谓“回调机制”。