在嵌入式的學習中,編程時,總會遇到一些彙編代碼。。。
不過一想也是,嵌入式本身就偏底層,和硬件接觸,彙編代碼效率更高,總會接觸的。
-----------------------------
彙編語言是什麼?
彙編語言是程序設計語言的基礎語言,是唯一可以直接與計算機硬件打交道的語言。
彙編語言根據CPU的不同,也分爲:ARM彙編語言、x86彙編語言
ARM彙編指令:
EQU 給數字常量名取一個符號名,相當於C語言中的define
AREA 彙編一個新的代碼段或者數據段
SPACE 分配內存空間
PRESERVE8 當前文件棧需按照 8 字節對齊
EXPORT 聲明一個標號具有全局屬性,可被外部的文件使用
DCD 以字爲單位分配內存,要求 4 字節對齊,並要求初始化這些內存
PROC 定義子程序,與 ENDP 成對使用,表示子程序結束
IMPORT 聲明標號來自外部文件,跟 C 語言中的 EXTERN 關鍵字類似
B 跳轉到一個標號
END 到達文件末尾 ,文件結束
IF,ELSE,
ENDIF 彙編條件分支語句,跟 C 語言的 if else 類似
MRS 加在特殊功能寄存器的值到通用寄存器
MSR 存儲通用寄存器的值到特殊功能寄存器
CBZ 比較,比較結果爲0就轉移
CBNZ 比較,比較結果非0就轉移
LDR 從存儲器中加載字到寄存器中l 是
LDR[僞指令] 加載一個立即數或者一個地址值到一個寄存器。例如:ldr rd, = lable 如果 label 是立即數,那 Rd 等於立即數,如果 labe一個標識符,比如指針,那存到 Rd 的就是 label 這個標識符的地址
LDRH 從存儲器中加半字到一個寄存器中
LDRB 從存儲器中加載字節到寄存器中
STR 把一個寄存器按字存儲到存儲器中
STRH 把一個寄存器存的低【半】字存儲到存儲器中
STRB 把一個寄存器的低字節存儲到存儲器中
LDMIA 將多個字從存儲器加載到 CPU 寄存器,先操作,指針在遞增。
STMDB 將多個字從 CPU 寄存器存儲到存儲器,指針先遞減,再操作
ORR 按位或
BX 直接跳轉到由寄存器給定的地址
BL 跳轉到 標號對應的地址,並且把跳轉前的下條指令地址保存到 LR
BLX 跳轉到由寄存器 REG 給出的的地址,並根據 REG 的 LSB 切換處理器狀
態 , 還 要 把 轉 移 前 的 下 條 指 令 地 址 保 存 到 LR 。 ARM(LSB=0) ,
Thumb(LSB=1)。CM3 只在 Thumb 中運行,就必須保證 reg 的 LSB=1 否則一個fault 打過來
編譯器指令:
WEAK 弱定義,如果外部文件聲明瞭一個標號,則優先使用外部文件定義的標號,如果外部文件沒有定義也不出錯。
ALIGN 編譯器對指令或者數據的存放地址進行對齊,一般需要跟一個立即數,缺省表示 4 字節對齊
------再看彙編代碼並註釋
__asm void xPortPendSVHandler( void ) //這個函數完成:上下文切換:上文保存、切換下文
{
extern pxCurrentTCB; (1)//
extern vTaskSwitchContext; (2)//
PRESERVE8 (3)//當前文件棧需按照 8 字節對齊
mrs r0, psp (4)//加在特殊功能寄存器psp的值到通用寄存器r0
isb //指令同步隔離。最嚴格:它會清洗流水線,以保證所有它前面的指令都執行完畢之後,才執行它後面的指令
ldr r3, =pxCurrentTCB (5)//加載 pxCurrentTCB 的地址到 r3
ldr r2, [r3] (6)//加載r3指向的內容到r2,即 r2 等於 pxCurrentTCB
stmdb r0!, {r4-r11} (7)//db:decrease before. 以 r0 作爲基址(指針先遞減,再操作)將 CPU 寄存器 r4~r11 的值存儲到任務棧,同時更新 r0 的值
str r0, [r2] (8)//將 r0 的值存儲到 r2 指向的內容,r2 等於 pxCurrentTCB。
//到這步結束,上下文切換的上文保存,完成。
stmdb sp!, {r3, r14} (9)//將 R3 和 R14 臨時壓入堆棧
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY (10)//將 configMAX_SYSCALL_INTERRUPT_PRIORITY 的值存儲到r0
msr basepri, r0 (11)//關中斷(???)將r0的值到特殊功能寄存器basepri
dsb //數據同步隔離。比 DMB 嚴格: 僅當所有在它前面的存儲器訪問操作都執行完畢後,才執行在它後面的指令
isb //指令同步隔離
bl vTaskSwitchContext (12)//跳轉到vTaskSwitchContext的地址,即執行該函數,
mov r0, #0 (13)//退出臨界段,開中斷,直接往 r0 寫 0
msr basepri, r0 //再將r0中的0寫入到basepri中
ldmia sp!, {r3, r14} (14)//將多個字從存儲器加載到 CPU 寄存器,先操作,指針在遞增:從主堆棧中恢復寄存器 r3 和 r14 的值,此時的 sp 使用的是 MSP
ldr r1, [r3] (15)//加載 r3 指向的內容到 r1。r3 存放的是 pxCurrentTCB 的地址,即 讓 r1 等於 pxCurrentTCB。pxCurrentTCB 在上面的 vTaskSwitchContext 函數中被更新,指向了下一個將要運行的任務的 TCB。
ldr r0, [r1] (16)//加載 r1 指向的內容到 r0,即下一個要運行的任務的棧頂指針
ldmia r0!, {r4-r11} (17)//以 r0 作爲基地址(先取值,再遞增指針,LDMIA 的 IA 表示Increase After),將下一個要運行的任務的任務棧的內容加載到 CPU 寄存器 r4~r11。
msr psp, r0 (18)//將r0的值寫到psp中:更新 psp 的值,等下異常退出時,會以 psp 作爲基地址,將任務棧中剩下的內容自動加載到 CPU 寄存器
isb //指令同步隔離
bx r14 (19)//直接跳轉到由寄存器r14給定的地址
nop //NOP空操作僞指令
}