1.跳壓棧出棧指令:
我們通常會在 A 函數(shù)中調(diào)用 B 函數(shù),當 B 函數(shù)執(zhí)行完以后再回到 A 函數(shù)繼續(xù)執(zhí)行。要想 再跳回 A 函數(shù)以后代碼能夠接著正常運行,那就必須在跳到 B 函數(shù)之前將當前處理器狀態(tài)保存 起來(就是保存 R0~R15 這些寄存器值),當 B 函數(shù)執(zhí)行完成以后再用前面保存的寄存器值恢復
R0~R15 即可。保存 R0~R15 寄存器的操作就叫做現(xiàn)場保護,恢復 R0~R15 寄存器的操作就叫做 恢復現(xiàn)場。在進行現(xiàn)場保護的時候需要進行壓棧(入棧)操作,恢復現(xiàn)場就要進行出棧操作。壓棧 的指令為 PUSH,出棧的指令為 POP,PUSH 和 POP 是一種多存儲和多加載指令,即可以一次 操作多個寄存器數(shù)據(jù),他們利用當前的棧指針 SP 來生成地址,PUSH 和 POP 的用法如表所示:
假如我們現(xiàn)在要將 R0~R3 和 R12 這 5 個寄存器壓棧,當前的 SP 指針指向 0X80000000,處理器的堆棧是向下增長的,使用的匯編代碼如下:
PUSH {R0~R3, R12} @將 R0~R3 和 R12 壓棧
?壓棧完成以后的堆棧如圖所示:
上圖就是對R0~R3,R12進行壓棧以后的堆棧示意圖,此時的SP指向了0X7FFFFFEC, 假如我們現(xiàn)在要再將 LR 進行壓棧,匯編代碼如下
PUSH {LR} @將 LR 進行壓棧
對 LR 進行壓棧完成以后的堆棧模型如圖所示:
?上圖就是分兩步對 R0~R3,R12 和 LR 進行壓棧以后的堆棧模型,如果我們要出棧的話 就是使用如下代碼:
POP {LR} @先恢復 LR
POP {R0~R3,R12} @在恢復 R0~R3,R12
出棧的就是從棧頂,也就是 SP 當前執(zhí)行的位置開始,地址依次減小來提取堆棧中的數(shù)據(jù) 到要恢復的寄存器列表中。PUSH 和 POP 的另外一種寫法是“STMFD SP!”和“LDMFD SP!”,
因此上面的匯編代碼可以改為:
STMFD SP!,{R0~R3, R12} @R0~R3,R12 入棧
STMFD SP!,{LR} @LR 入棧
LDMFD SP!, {LR} @先恢復 LR
LDMFD SP!, {R0~R3, R12} @再恢復 R0~R3, R12
????????STMFD 可以分為兩部分:STM 和 FD,同理,LDMFD 也可以分為 LDM 和 FD??吹?STM和 LDM 有沒有覺得似曾相識(不是 STM32 啊啊啊啊),前面我們講了 LDR 和 STR,這兩個是 數(shù)據(jù)加載和存儲指令,但是每次只能讀寫存儲器中的一個數(shù)據(jù)。STM 和 LDM 就是多存儲和多 加載,可以連續(xù)的讀寫存儲器中的多個連續(xù)數(shù)據(jù)。
????????FD 是 Full Descending 的縮寫,即滿遞減的意思。根據(jù) ATPCS 規(guī)則,ARM 使用的 FD 類型 的堆棧,SP 指向最后一個入棧的數(shù)值,堆棧是由高地址向下增長的,也就是前面說的向下增長 的堆棧,因此最常用的指令就是 STMFD 和 LDMFD。STM 和 LDM 的指令寄存器列表中編號 小的對應(yīng)低地址,編號高的對應(yīng)高地址。
2.跳轉(zhuǎn)指令
我們重點來看一下 B 和 BL 指令,因為這兩個是我們用的最多的,如果要在匯編中進行函 數(shù)調(diào)用使用的就是 B 和 BL 指令:
B 指令
這是最簡單的跳轉(zhuǎn)指令,B 指令會將 PC 寄存器的值設(shè)置為跳轉(zhuǎn)目標地址, 一旦執(zhí)行 B 指 令,ARM 處理器就會立即跳轉(zhuǎn)到指定的目標地址。如果要調(diào)用的函數(shù)不會再返回到原來的執(zhí)行 處,那就可以用 B 指令,如下示例:
_start:
ldr sp,=0X80200000 @設(shè)置棧指針
b main @跳轉(zhuǎn)到 main 函數(shù)
上述代碼就是典型的在匯編中初始化 C 運行環(huán)境,然后跳轉(zhuǎn)到 C 文件的 main 函數(shù)中運行,上述代碼只是初始化了 SP 指針,有些處理器還需要做其他的初始化,比如初始化 DDR 等等。 因為跳轉(zhuǎn)到 C 文件以后再也不會回到匯編了,所以在第 4 行使用了 B 指令來完成跳轉(zhuǎn)。
BL 指令
BL 指令相比 B 指令,在跳轉(zhuǎn)之前會在寄存器 LR(R14)中保存當前 PC 寄存器值,所以可以 通過將 LR 寄存器中的值重新加載到 PC 中來繼續(xù)從跳轉(zhuǎn)之前的代碼處運行,這是子程序調(diào)用 一個基本但常用的手段。比如 Cortex-A 處理器的 irq 中斷服務(wù)函數(shù)都是匯編寫的,主要用匯編 來實現(xiàn)現(xiàn)場的保護和恢復、獲取中斷號等。但是具體的中斷處理過程都是 C 函數(shù),所以就會存 在匯編中調(diào)用 C 函數(shù)的問題。而且當 C 語言版本的中斷處理函數(shù)執(zhí)行完成以后是需要返回到匯編中斷服務(wù)函數(shù),因為還要處理其他的工作,一般是恢復現(xiàn)場。這個時候就不能直接使用B 指令了,因為 B 指令一旦跳轉(zhuǎn)就再也不會回來了,這個時候要使用 BL 指令,示例代碼如下:文章來源:http://www.zghlxwxcb.cn/news/detail-409169.html
push {r0, r1} @保存 r0,r1
cps #0x13 @進入 SVC 模式,允許其他中斷再次進去
bl system_irqhandler @加載 C 語言中斷處理函數(shù)到 r2 寄存器中
cps #0x12 @進入 IRQ 模式
pop {r0, r1}
str r0, [r1, #0X10] @中斷執(zhí)行完成,寫 EOIR
上述代碼中第 5 行就是執(zhí)行 C 語言版的中斷處理函數(shù),當處理完成以后是需要返回來繼續(xù) 執(zhí)行下面的程序,所以使用了 BL 指令。文章來源地址http://www.zghlxwxcb.cn/news/detail-409169.html
到了這里,關(guān)于Linux當中的壓棧和出棧指令以及跳轉(zhuǎn)指令詳細教程的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!