ARM匯編概述:Cortex-M3/M4實戰(zhan)指(zhi)南(nan)
Cortex-M3/M4是嵌入式(shi)開發中最主流的ARM內核(he),廣泛應(ying)用于STM32等微(wei)控制器(qi)(qi)。其匯(hui)編語(yu)言(yan)遵循RISC架(jia)構"精簡(jian)高(gao)效"的設計(ji)理念。本文從(cong)核(he)心(xin)寄存器(qi)(qi)到常用指(zhi)令,再到實戰示(shi)例,層層遞進講解(jie)ARM匯(hui)編的核(he)心(xin)邏(luo)輯。
一、核心寄存器(匯編的"操作對象")
Cortex-M3/M4的寄存器是匯編指令的直接操作對象,無需記憶所有寄存器,僅需掌握以下高頻核心寄存器,即可覆蓋(gai)絕(jue)大多數嵌(qian)入式開發場景(jing)。
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個(ge)標志位(wei)是"條件指令"的核心判斷依據:
- 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(將寄存器(qi)數據存儲到內(nei)存)指令完成。
1. 數據處理指令(CPU內部寄存器運算)
僅操(cao)作通用寄存器,不直接訪問內存,是實現"加(jia)減、比較(jiao)、位操(cao)作"等(deng)邏輯的核心。
(1)MOV:數據傳送指令
核心作用:實現數(shu)據在寄(ji)存器間的傳遞(di),或立(li)即(ji)數(shu)到寄(ji)存器的加載。
語法: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:加減指令
核心作用:實現寄存器或立即數的加減運算(suan)。
語法:
- 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:比較指令
核心作用:隱(yin)性(xing)計算"Rn - Op2",不保存結(jie)果(guo),僅(jin)更(geng)新APSR標志,為(wei)后續"條件跳轉"做準備。
語法: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:讀內存指令
核心作用:將內存(cun)中的數據讀取(qu)到寄存(cun)器。
語法: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:寫內存指令
核心作用:將寄存(cun)器中的(de)數(shu)據寫入內存(cun)。
語法:與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!的別名,專(zhuan)用于棧(zhan)操(cao)作,更簡潔直觀(guan)。
語法:
- 入棧(保存寄存器):
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)接(jie)修(xiu)改PC值,跳轉到指定標號,適用于"循環、分支判斷(duan)"。
語法:B{cond} Label
示例:
B MainLoop ; 無條件跳轉到MainLoop
CMP R0, #0
BNE ErrorHandler ; 若R0≠0,跳轉到ErrorHandler
(2)BL:函數調用指令
核心作用:跳轉前自動將返回地(di)址(下(xia)一(yi)條指(zhi)令地(di)址)存(cun)入LR,用(yong)(yong)于函數調用(yong)(yong)。
語法: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:加載任意(yi)32位數值到寄(ji)存器。
LDR R0, =0x12345678 ; 加載非立即數
LDR R1, =0x10 ; 編譯器可能優化為 MOV R1, #0x10
ADR:獲取標(biao)號的(de)相對地址(zhi)(短距(ju)離)。
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匯編的核心邏輯可提煉為三(san)句話:
- 操作對象是寄存器:核心寄存器僅需掌握R0-R7(數據)、SP/LR/PC(控制)和APSR(條件標志)。
- 內存訪問靠Load/Store:遵循RISC原則,僅
LDR/STR指令與內存交互。函數上下文保護使用推薦的PUSH/POP指令。 - 程序流靠PC控制:跳轉用
B,函數調用用BL(依賴LR),返回推薦用BX LR或POP {PC}。
掌握以上內容,即可為理解和應對嵌入式開發中啟動代碼/硬件初始化、中斷服務例程(ISR)編寫、性能關鍵代碼段優化等場景打下堅實基礎。
