ARM匯編概(gai)述:Cortex-M3/M4實戰指南
Cortex-M3/M4是嵌入(ru)式開發中最主(zhu)流的ARM內核,廣泛應用于STM32等微控制器。其匯編語言遵循RISC架(jia)構"精簡高(gao)效(xiao)"的設計理念。本文從核心寄存器到(dao)常用指令(ling),再到(dao)實戰示例,層(ceng)層(ceng)遞進(jin)講(jiang)解ARM匯編的核心邏輯(ji)。
一、核心寄存器(匯編的"操作對象")
Cortex-M3/M4的寄存器是匯編指令的直接操作對象,無需記憶所有寄存器,僅需掌握以下高頻核心寄存器,即(ji)可覆蓋絕大多數嵌(qian)入式開發場景。
1. 通用寄存器
| 寄存器 | 別名 / 功能 | 核心用途 |
|---|---|---|
| R0-R7 | 低寄存器 | 暫存運算數據、傳遞函數前4個參數、存儲內存地址(配合LDR/STR訪問RAM/ROM) |
| R13 | SP(棧指針) | 指向棧頂,管理函數調用時的上下文保存與恢復。Cortex-M默認采用滿遞減棧模型 |
| R14 | LR(鏈接寄存器) | 保存函數調用后的返回地址(BL指令自動存入)。 |
| R15 | PC(程序計數器) | 指向當前執行的指令地址,修改PC可實現跳轉。Cortex-M始終處于Thumb狀態,故PC的BIT0必須為1。 |
2. 程序狀態寄存器
程序狀態寄存器(xPSR)由APSR(應用狀態寄存器)、IPSR(中斷程序狀態寄存器)和EPSR(執行程序狀態寄存器)三個部分組合而成,其中APSR需重點關注,其4個標志位是"條(tiao)件指令"的核心判(pan)斷依據:
- N(負標志):運算結果為負(最高位為1)時N=1,否則為0;用于判斷有符號數的正負。
- Z(零標志):運算結果為0時Z=1,否則為0;常用于循環結束判斷(如計數到0)、相等比較(如
CMP R0, R1后用BEQ跳轉)。 - C(進位/借位標志):加法有進位或減法無借位時C=1,否則為0;用于多字節運算(如64位數據加法)。
- V(溢出標志):有符號數運算超出32位范圍時V=1,否則為0;用于檢測有符號數運算錯誤(如
0x7FFFFFFF + 1會溢出)。
二、ARM常用匯編指令
Cortex-M3/M4架構遵循Load-Store原則,即數據處理指令只操作寄存器,與內存的數據交換必須通過LDR(從內存加載數據到寄存器)和STR(將寄存(cun)器(qi)數據存(cun)儲到內存(cun))指令完成。
1. 數據處理指令(CPU內部寄存器運算)
僅操(cao)作通用寄存器(qi),不直接訪問內存,是實現"加減、比(bi)較、位操(cao)作"等邏輯的核心。
(1)MOV:數據傳送指令
核心作用:實現數據在寄(ji)存器間(jian)的(de)傳遞,或立即數到寄(ji)存器的(de)加載。
語法:MOV{S}{cond} Rd, Op2
{S}:可選,指令執行后更新APSR標志(如MOVS R0, #0會設置Z=1)。{cond}:可選,條件執行后綴(如MOVNE R0, #0xFF表示"若Z=0(前序運算結果非0),則執行")。Op2:可以是立即數或另一個寄存器,并可包含移位操作(如R3, LSL #2)。
示例:
MOV R0, #0x20000000 ; R0 = 0x20000000(加載RAM基地址)
MOVS R1, #0 ; R1 = 0,同時更新APSR的Z標志(Z=1)
MOV R2, R3, LSL #2 ; R2 = R3 << 2(將R3的值左移2位后存入R2)
(2)ADD/SUB:加減指令
核心作用:實現寄存器或立(li)即數的加減運算。
語法:
- ADD(加法):
ADD{S}{cond} Rd, Rn, Op2(Rd = Rn + Op2) - SUB(減法):
SUB{S}{cond} Rd, Rn, Op2(Rd = Rn - Op2)
示例:
; ADD示例
ADD R0, R1, #5 ; R0 = R1 + 5
ADDS R2, R3, R4 ; R2 = R3 + R4,同時更新APSR
; SUB示例
SUB R0, R1, #10 ; R0 = R1 - 10
SUBS R5, R5, #1 ; R5自減1(循環計數器),并更新標志位
(3)CMP:比較指令
核心作用:隱性(xing)計算"Rn - Op2",不保存結果(guo),僅更新APSR標志,為后(hou)續"條件跳轉"做準備。
語法:CMP{cond} Rn, Op2
示例:
CMP R0, #100 ; 比較R0與100
BEQ LoopEnd ; 若Z=1(R0=100),跳轉到LoopEnd
BNE LoopContinue ; 若Z=0(R0≠100),跳轉到LoopContinue
2. 內存訪問指令
(1)LDR:讀內存指令
核心作用:將內(nei)存中的數據讀取到寄存器(qi)。
語法:LDR{type}{cond} Rd, [Rn {, #offset}]
{type}:可選,指定數據類型(B=無符號字節、H=無符號半字、默認=字)。- 尋址方式:偏移尋址(
[Rn, #4]):地址 = Rn + 4,Rn不變。前索引([Rn, #4]!):地址 = Rn + 4,然后更新Rn = Rn + 4。后索引([Rn], #4):地址 = Rn,然后更新Rn = Rn + 4。
示例:
LDR R0, [R1] ; 讀R1指向的4字節數據到R0
LDRB R2, [R1, #1] ; 讀R1+1地址的1字節到R2
LDRH R3, [R1], #2 ; 讀R1指向的2字節到R3,然后R1 = R1 + 2
(2)STR:寫內存指令
核心作用:將寄(ji)存器中的數據寫(xie)入內存。
語法:與LDR一致(Rd為源寄存器)。
示例:
STR R0, [R1] ; 將R0的4字節數據寫入R1指向的地址
STRH R2, [R1, #4]! ; 將R2的2字節數據寫入R1+4,然后R1 = R1 + 4
(3)PUSH/POP:棧操作指令
核心作用:批量保存/恢復寄存器到棧,是函數調用時保護上下文的標準且推薦的方式。它們是STMFD SP!和LDMFD SP!的別名,專用(yong)于棧操作,更簡潔直觀。
語法:
- 入棧(保存寄存器):
PUSH {reglist} - 出棧(恢復寄存器):
POP {reglist}
示例:
; 函數入口:保存R4-R6(需保護的寄存器)和LR(返回地址)
PUSH {R4-R6, LR} ; 入棧,SP相應遞減
; 函數體 ... (可安全使用R4-R6)
; 函數出口:恢復寄存器并返回
POP {R4-R6, PC} ; 出棧,恢復R4-R6,并將LR的值直接彈出到PC(實現返回)
3. 跳轉與函數調用指令(程序流控制)
(1)B:無條件/條件跳轉
核心作用:直(zhi)接修改PC值,跳(tiao)轉到指(zhi)定標號,適用于"循環、分支判斷"。
語法:B{cond} Label
示例:
B MainLoop ; 無條件跳轉到MainLoop
CMP R0, #0
BNE ErrorHandler ; 若R0≠0,跳轉到ErrorHandler
(2)BL:函數調用指令
核心作用:跳(tiao)轉(zhuan)前自(zi)動將(jiang)返回地(di)址(下一(yi)條指令(ling)地(di)址)存入LR,用于函(han)數(shu)調用。
語法:BL{cond} Label
示例:
BL Delay ; 調用Delay函數,LR = 返回地址
MOV R1, #1 ; Delay返回后,從此處繼續執行
Delay:
MOV R0, #100000
DelayLoop:
SUBS R0, R0, #1
BNE DelayLoop
BX LR ; 使用 BX LR 返回調用處
(3)偽指令
LDR =val:加載任意32位數值到寄(ji)存器(qi)。
LDR R0, =0x12345678 ; 加載非立即數
LDR R1, =0x10 ; 編譯器可能優化為 MOV R1, #0x10
ADR:獲取標號的(de)相對(dui)地址(短(duan)距離(li))。
ADR R0, DataBuf ; 將DataBuf的地址加載到R0
DataBuf DCD 0x00, 0x01, 0x02
三、完整示例程序
以下示例覆蓋"棧操作、函數調用、內存讀寫、數據校驗"四大核心場景,并使用推薦的PUSH/POP指令。
; 程序說明:Cortex-M3/M4匯編實戰示例
; 核心功能:1.棧保存寄存器 2.調用延時函數 3.讀寫RAM數據 4.校驗數據一致性 5.循環執行
AREA ARM_Demo, CODE, READONLY
ENTRY
THUMB ; 明確指定使用Thumb指令集
ALIGN 4
; --------------------------
; 主函數:程序核心邏輯入口
; --------------------------
Main
; 1.棧操作:在函數入口保存可能被使用的寄存器及返回地址,遵守調用規范
PUSH {R0-R2, LR} ; 使用PUSH保存寄存器
; 2.內存讀寫:向RAM地址(0x20000000)寫入數據,再讀取校驗
MOV R0, #0x20000000 ; R0 = RAM基地址
LDR R1, =0x12345678 ; R1 = 待寫入數據
STR R1, [R0] ; 寫操作:將數據寫入內存
LDR R2, [R0] ; 讀操作:從內存讀取數據
; 3.數據校驗:比較"寫入值(R1)"與"讀取值(R2)"
CMP R1, R2
BEQ Data_OK ; 若數據一致,跳轉
MOV R3, #0x00 ; 數據不一致:R3 = 0x00(錯誤標志)
B Call_Delay
Data_OK
MOV R3, #0xFF ; 數據一致:R3 = 0xFF(成功標志)
; 4.調用延時函數
Call_Delay
BL Delay_Func ; 調用延時函數
; 5.恢復寄存器并返回:從棧中恢復R0-R2,并通過將LR彈出至PC來返回到調用者,實現循環
POP {R0-R2, PC} ; 使用POP恢復寄存器并返回
; --------------------------
; 延時函數:簡單遞減延時
; --------------------------
Delay_Func
PUSH {R0, LR} ; 延時函數也保護它用到的寄存器和LR
LDR R0, =500000
Delay_Loop
SUBS R0, R0, #1
BNE Delay_Loop
POP {R0, PC} ; 恢復R0,并通過彈出LR到PC來返回
ALIGN 4
END
執行效果:
- 入棧后:SP相應遞減。
- 寫內存后:查看
0x20000000地址,值為0x12345678。 - 校驗后:APSR的Z標志為1,R3被設為
0xFF。 - 全速運行:程序在
Main和Delay_Func間循環,R3始終保持0xFF。
四、總結
Cortex-M3/M4匯編的(de)核心邏輯可提煉為三句話:
- 操作對象是寄存器:核心寄存器僅需掌握R0-R7(數據)、SP/LR/PC(控制)和APSR(條件標志)。
- 內存訪問靠Load/Store:遵循RISC原則,僅
LDR/STR指令與內存交互。函數上下文保護使用推薦的PUSH/POP指令。 - 程序流靠PC控制:跳轉用
B,函數調用用BL(依賴LR),返回推薦用BX LR或POP {PC}。
掌握以上內容,即可為理解和應對嵌入式開發中啟動代碼/硬件初始化、中斷服務例程(ISR)編寫、性能關鍵代碼段優化等場景打(da)下堅(jian)實基(ji)礎。
