一 匯編補充:
area reset, code, readonlycode32entry;mov r0, #4 ; r0 = 4;mov r1, r0 ; r1 = r0;mov r2, r1, lsl #1 ;r2 = r1 << 1 乘2;mov r3, r1, lsr #1 ;r3 = r1 >> 1 除2;mov r4, r1, ror #2;mov r0, #100 ;100是十進制 轉為16進制賦值給十進制;mov r1, #8;add r2, r0, #100;add r2, r0, r1;add r2, r0, #(100 << 2);;mov r0, #0xF0;mvn r0, r0;ldr r0, =0xfac0;把任意 32 位常數送進寄存器;指定位置置0;mov r0, #0xFFFFFFFF;mov r1, #1;bic r0, r0, #4 ;指定位 4:(0100)r0里4對應的1所在位置置0;bic r0, r0, r1, lsl #2 ;先把r1 左移2位 然后對應的1的位置對應于r0中 給置0;bic r0, r0, #(1 << 2);清理掉第2位;指定位置1;mov r0, #0x80000000;orr r0, r0, #(1<<31);mov r0, #0x0 ;1011;orr r0, r0, #(1 << 10);三個數之間找最大值;mov r0, #100;mov r1, #200;mov r2, #300;cmp r0, r1;movge r3, r0;movlt r3, r1;cmp r3, r2;movge r3, r3;movlt r3, r2;if分支;mov r0, #0x100;mov r1, #0x200;cmp r0, r1;bge greatr;blt less;greatr;mov r2, r0;b finish ;跳過另一個分支 跳轉到finish;less;mov r2, r1;finish;b finish;while 循環;mov r0, #0;mov r1, #0
;loop;cmp r0, #100;bge finish;add r1, r1, r0;add r0, r0, #1;b loop;finish;b finish ;死循環;do_while循環;mov r0, #0;mov r1, #0
;loop;add r1, r1, r0;add r0, r0, #1;cmp r0, #100;bge finish;b loop
;finish;b finish ;死循環;b main
;asm_maxTwoNum
; mov r0, #100
; mov r1, #200
; cmp r0, r1
; movge r3, r0
; movlt r3, r1
; mov pc, lr ;把返回地址賦給 PC → 立即跳回調用點繼續執行
; bx lr ;bx 跳轉到調用點的下一行
;main
; mov r0, #10;mov r1, #20
; bl asm_maxTwoNum ;bl 跳轉之前保留當前地址
; mov r2, #10
; mov r3, #20;========= 子函數:返回 r0、r1 中的較大值 =========
asm_maxTwoNummov r0, #100 ;把參數寫成100、200(覆蓋main傳入的10/20)mov r1, #200cmp r0, r1stmfd sp!, {r0-r12, lr} ; 先壓棧保護現場bl asm_fun0 ; 調用asm_fun0(無意義,返回后r0/r1仍保持100/200)ldmfd sp!, {r0-r12, lr} ; 彈棧恢復現場movge r3, r0 ; 若 r0>=r1 大數→r3 (100<200,不執行)movlt r3, r1 ; 若 r0< r1 大數→r3 (執行,r3=200)mov r0, r3 ; 把結果200寫回r0作為返回值bx lr ; 返回;========= 另一個函數,僅演示跳轉 =========
asm_fun0mov r0, #100mov r1, #200bx lr ; 直接返回,無實質操作;========= 主函數:入口 =========
mainldr sp, =0x40001000 ; 設棧頂(裸機常用)mov r0, #10 ; 準備參數 1mov r1, #20 ; 準備參數 2stmfd sp!, {r0-r12, lr} ; 保存現場bl asm_maxTwoNum ; 調最大值函數ldmfd sp!, {r0-r12, lr} ; 恢復現場mov r2, #10 mov r3, #20ldr r0, =0x40000000 ;地址指針ldr r1, =0x12345678 ;數據str r1,[r0],#4 ;*p = 0x12345678; p++ldr r2,[r0] ; r2 = *(p+1)(未初始化,值未知);ARM 是 32 位架構,一個“字(word)” = 4 字節。;地址按字節編號,因此索引寫 #4 才能指到下一個字。finishb finish ; 死循環,程序結束end
匯編asm和c文件之間的傳參?
?preserve8
且魔術棒配置ROM地址區域
mainldr sp, =0x40001000 ; 設棧頂(裸機常用)import c_add;mov r0, #10 ; 準備參數 1;mov r1, #20 ; 準備參數 2stmfd sp!, {r0-r12, lr} ; 保存現場;bl asm_maxTwoNum ; 調最大值函數mov r0, #100 mov r1, #200bl c_add;r0默認接收函數返回值ldmfd sp!, {r0-r12, lr} ; 恢復現場;mov r2, #10 ;mov r3, #20ldr r0, =0x40000000 ;地址指針ldr r1, =0x12345678 ;數據str r1,[r0],#4 ;*p = 0x12345678; p++ldr r2,[r0] ; r2 = *(p+1)(未初始化,值未知);ARM 是 32 位架構,一個“字(word)” = 4 字節。;地址按字節編號,因此索引寫 #4 才能指到下一個字。finishb finish ; 死循環,程序結束extern c_add(int a, int b);int c_add(int a, int b)
{return a + b;
}
當傳參數大于4個——通過壓棧傳參
b main
asm_maxTwoNummov r0, #100 ;把參數寫成100、200(覆蓋main傳入的10/20)mov r1, #200cmp r0, r1stmfd sp!, {r0-r12, lr} ; 先壓棧保護現場bl asm_fun0 ; 調用asm_fun0(無意義,返回后r0/r1仍保持100/200)ldmfd sp!, {r0-r12, lr} ; 彈棧恢復現場movge r3, r0 ; 若 r0>=r1 大數→r3 (100<200,不執行)movlt r3, r1 ; 若 r0< r1 大數→r3 (執行,r3=200)mov r0, r3 ; 把結果200寫回r0作為返回值bx lr ; 返回;========= 另一個函數,僅演示跳轉 =========
asm_fun0mov r0, #100mov r1, #200bx lr ; 直接返回,無實質操作;========= 主函數:入口 =========
mainldr sp, =0x40001000 ; 設棧頂(裸機常用)import c_addmov r0, #10 ; 準備參數 1mov r1, #20 ; 準備參數 2stmfd sp!, {r0-r12, lr} ; 保存現場;bl asm_maxTwoNum ; 調最大值函數mov r0, #10 mov r1, #20mov r2, #30 mov r3, #40mov r4, #50; 傳參大于4個需要通過壓棧進行傳參 stmfd sp!, {r4} ; 壓棧r4傳參 bl c_add;r0默認接收函數返回值ldmfd sp!, {r4} ; 彈棧ldmfd sp!, {r0-r12, lr} ; 恢復現場;mov r2, #10 ;mov r3, #20ldr r0, =0x40000000 ;地址指針ldr r1, =0x12345678 ;數據str r1,[r0],#4 ;*p = 0x12345678; p++ldr r2,[r0] ; r2 = *(p+1)(未初始化,值未知);ARM 是 32 位架構,一個“字(word)” = 4 字節。;地址按字節編號,因此索引寫 #4 才能指到下一個字。finishb finish ; 死循環,程序結束
C語言 調用匯編
extern int c_add(int a, int b, int c, int d, int e);extern int asm_maxTwoNum(int a, int b);int c_add(int a, int b, int c, int d, int e)
{int ret;//ret = a + b + c + d + e;ret = asm_maxTwoNum(5, 10);return ret;
}b main
asm_fun0mov r0, #100mov r1, #200bx lrexport asm_maxTwoNum
asm_maxTwoNumcmp r0, r1movge r3, r0movlt r3, r1 mov r0, r3bx lr
啟動代碼:最小可運行的 ARM 裸機啟動/中斷向量表/軟中斷(SWI) 示例
上電后建立向量表 → 切到用戶模式并開 IRQ → 給 User 棧留空間 → 跳進 C 的 main()
;同時提供 asm_swi_fun()
讓 C 主動觸發 7 號軟中斷,異常處理完整保存/恢復現場并自動返回用戶模式
preserve8area reset, code, readonlycode32entry;中斷向量表ldr pc, =start_hander ; 0x00 復位ldr pc, =undefine_hander ; 0x04 未定義指令ldr pc, =software_hander ; 0x08 SWI(軟中斷)ldr pc, =prefetch_hander ; 0x0C 預取指中止ldr pc, =data_hander ; 0x10 數據中止nop ; 0x14 保留ldr pc, =irq_hander ; 0x18 IRQldr pc, =fiq_hander ; 0x1C FIQundefine_handerb undefine_handerprefetch_handerb prefetch_handerdata_handerb data_handerirq_handerb irq_handerfiq_handerb fiq_handerimport software_vector;告訴匯編器 C 里實現該函數
software_handerstmfd sp!, {r0-r12, lr} ; 保存用戶現場(lr = SWI 返回地址)bl software_vector ; 調到 C 處理函數ldmfd sp!, {r0-r12, pc}^ ; 恢復寄存器 且 把保存的 lr 彈進 pc; ^ 表示 同時把 SPSR_svc → CPSR(自動返回用戶模式)export asm_swi_fun ; 供c調用
asm_swi_funswi #7 ; 觸發 7 號軟中斷(編號任意)bx lr ; 從 SWI 返回后再回到 Cstart_handerldr sp, =0x40001000 ; 設棧頂(裸機常用)import main ;引入c語言中的main函數;--- 下面 5 行 = 切到用戶模式并打開 IRQ ----------mrs r0, cpsr ;r0 ← CPSRbic r0, r0, #(0x1F << 0) ;CPSR的M域是后五位 先清零(清模式位)bic r0, r0, #(1 << 7) ;清CPSR 的 I 位(允許 IRQ)orr r0, r0, #(0x10 << 0) ;設 M[4:0]=0b10000(User 模式)msr cpsr_c, r0 ;**只把模式/中斷位寫回**;給 User 模式建棧ldr sp, =0x40001000sub sp, sp, #1024 ;; 留 1 KB 給 User 棧b main ; 跳轉c語言的函數end
15、arm匯編調用c語言函數以及c語言函數調用匯編編寫的函數,函數參數和返回值如何處理?
核心原則:
參數和返回值主要通過 寄存器 傳遞,當寄存器不夠用時,使用 棧。
ARM匯編調用C語言函數 過程如下:
參數傳遞:
前4個參數 (對于32位ARM): 如果參數是32位整型或指針類型,依次使用寄存器
R0
,R1
,R2
,R3
來傳遞。第5個及以后的參數: 從第5個參數開始,將其壓入棧 (Stack) 中。調用者負責在調用前分配棧空間并壓入這些參數。
執行調用 :
使用
BL
(Branch with Link) 指令跳轉到C函數地址。BL
指令會將下一條指令的地址(返回地址)保存到鏈接寄存器LR
(R14) 中。返回值獲取:
32位及以下的返回值: C函數執行完畢后,返回值通過寄存器
R0
返回給匯編調用者。C語言函數調用ARM匯編函數 過程如下:
編寫匯編函數:
匯編函數需要將自己視為一個被C代碼調用的普通函數
它可以從
R0-R3
中獲取前4個傳入的參數。如果需要更多的參數,它需要從棧上的特定位置去獲取。SP寄存器指向的棧頂之后的位置就是第5、6...個參數。
保存上下文 :
匯編函數如果會修改一些被調用者保存寄存器 (Callee-saved registers),如
R4-R11
,SP
,LR
,則必須在函數開頭將它們壓棧保存 (PUSH),并在函數結束返回前出棧恢復 (POP)。對于調用者保存寄存器 (Caller-saved registers),如
R0-R3
,R12
,則可以自由使用,無需保存(因為調用者C代碼已經假定它們可能被破壞)。返回值設置 (Setting the Return Value):
在函數返回前,將需要返回的值放入
R0
(或R0
和R1
)。返回 (Returning):
通常使用
BX LR
指令返回到調用者(C代碼)。這條指令會跳轉到LR
寄存器中保存的返回地址。16. ARM內核異常、類型及工作模式切換
核心概念:
異常是CPU對內部或外部緊急事件的一種響應機制。當異常發生時,ARM內核會暫停當前執行的程序,跳轉到預先設置好的異常向量表中的特定地址去執行對應的異常處理程序,同時CPU的工作模式會自動切換到對應的特權模式,以便操作系統內核有足夠的權限來處理異常。
ARM經典架構(如ARMv7-A)中的7種異常:
異常類型 觸發條件 使內核進入的工作模式 優先級(通常) 1. 復位 (Reset) 處理器上電或按下復位鍵 Supervisor (SVC) 最高 (1) 2. 未定義指令 (Undefined Instruction) CPU遇到無法識別的指令 Undefined 6 3. 軟件中斷 (SWI) / SVC 程序執行 SVC
或SWI
指令Supervisor (SVC) 6 4. 指令預取中止 (Prefetch Abort) 預取指令時發生內存訪問錯誤 Abort 5 5. 數據中止 (Data Abort) 讀寫數據時發生內存訪問錯誤 Abort 2 6. 中斷請求 (IRQ) 外部設備發出普通中斷請求 IRQ 4 7. 快速中斷請求 (FIQ) 外部設備發出高優先級中斷請求 FIQ 3 工作模式詳解:
異常會導致內核從User等非特權模式切換到下表中的特權模式:
工作模式 簡稱 用途 用戶模式 User 正常程序執行(非異常模式) 快速中斷模式 FIQ 處理高速數據傳輸、DMA等 (異常模式) 中斷模式 IRQ 處理普通中斷 (異常模式) 管理模式 SVC 操作系統內核、軟件中斷、復位 (異常模式) 中止模式 Abort 處理內存訪問失敗 (異常模式) 未定義模式 Undefined 處理未定義指令異常 (異常模式) 系統模式 System 運行特權級操作系統任務 (ARMv4及以上,非異常模式) 重點說明:
復位 (Reset) 是最高優先級異常,它讓系統從一個已知的初始狀態開始運行,直接進入SVC模式。
軟件中斷 (SVC) 是用戶程序主動切換到內核態(SVC模式)的方式,用于請求系統服務,例如打開文件、創建線程等(類似Linux中的系統調用)。
IRQ和FIQ 是異步異常,由外部硬件觸發。FIQ的向量地址在向量表末尾,允許處理程序直接開始執行而無需跳轉,并且有更多的專用寄存器,從而可以實現更快的處理速度。
中止異常 通常與內存管理單元 (MMU) 相關,用于實現虛擬內存和內存保護。預取中止發生在取指階段,數據中止發生在數據訪問階段。
每種異常模式都有自己獨立的棧指針 (SP) 和鏈接寄存器 (LR) 副本,這避免了在處理異常時破壞用戶模式下的寄存器狀態,確保了異常處理的安全和可靠。