一、概述
在 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 類語言所提供的功能支持模塊——即所謂“回調機制”。