ARM指令集(Instruction Set)細節
本文旨在深入探討 ARM 指令集(Instruction Set)的細節。這是一個非常廣泛的主題,我會將其分解為關鍵概念、不同版本的區別以及核心特性,并提供一些示例。
ARM 指令集的核心在于 RISC(精簡指令集計算機) 設計哲學,這意味著:
- 指令數量少且規整:大多數指令長度固定(通常是32位或16位),格式一致,解碼簡單。
- 加載-存儲架構:只有專門的加載(LDR)和存儲(STR)指令可以訪問內存。所有算術和邏輯運算都是在寄存器之間進行的。
- 大量的通用寄存器:擁有16個(在AArch32下)或31個(在AArch64下)通用寄存器,減少了訪問內存的次數,提高了效率。
1. 兩種主要指令集狀態:AArch32 與 AArch64
AArch32/64的全稱是 ARM Architecture 32/64-bit
這是理解現代 ARM 指令集的首要概念。ARMv8 架構引入了 64 位執行狀態,并向后兼容 32 位。
特性 | AArch32 (ARM 32-bit) | AArch64 (ARM 64-bit) |
---|---|---|
架構版本 | ARMv4T 到 ARMv8-A (兼容模式) | ARMv8-A 及更高 |
指令集 | ARM、Thumb、Thumb-2 | A64 |
指令長度 | ARM: 32-bit; Thumb: 16-bit; Thumb-2: 16/32-bit | 固定 32-bit |
通用寄存器 | 16個 (R0-R15),包括: - R13: SP (堆棧指針) - R14: LR (鏈接寄存器) - R15: PC (程序計數器) | 31個 (X0-X30),加上: - XZR: 零寄存器 (恒為0) - SP: 堆棧指針 (獨立) |
程序計數器 | 是通用寄存器 R15 | 不是通用寄存器,無法直接操作 |
條件執行 | 大多數指令都可以條件執行(通過條件碼) | 只有分支等少數指令可以條件執行 |
操作數 | 第二個操作數非常靈活(立即數 + 移位/循環) | 尋址模式更嚴格,但仍有靈活性 |
2. AArch32 下的指令集變體
在 32 位世界中,處理器可以在兩種主要狀態之間切換:
a. ARM 指令集 (32-bit)
- 特點:高性能、全功能。所有指令都是 32 位寬。
- 條件執行:這是 ARM 模式的一個標志性特性。幾乎每條指令都可以根據 APSR(程序狀態寄存器)中的條件標志(N, Z, C, V)來條件地執行。
- 示例:
ADDEQ R0, R1, R2
; 如果相等(Z=1),則執行 R0 = R1 + R2
- 示例:
- 靈活的第二個操作數:
- 示例 1:
ADD R0, R1, #42
; 立即數 - 示例 2:
ADD R0, R1, R2
; 寄存器 - 示例 3:
ADD R0, R1, R2, LSL #3
; 寄存器 R2 邏輯左移 3 位后的值 - 示例 4:
ADD R0, R1, R2, ROR R3
; 寄存器 R2 循環右移 R3 位后的值
- 示例 1:
b. Thumb / Thumb-2 指令集 (16/32-bit)
- 初衷 (Thumb):提供更高的代碼密度。指令是 16 位的,因此占用的內存空間更小。性能通常低于 ARM 模式,因為需要更多指令來完成相同任務。
- 進化 (Thumb-2):ARMv6T2 及以后版本引入。它混合了 16 位和 32 位指令,在保持高代碼密度的同時,提供了接近 ARM 模式的性能。Thumb-2 是現代 Cortex-M 和 Cortex-R 系列處理器唯一支持的指令集狀態(它們無法執行傳統的 32 位 ARM 指令)。
- 特點:指令長度可變(2 字節或 4 字節),條件執行能力有限(主要用于分支指令)。
3. AArch64 下的 A64 指令集
這是純粹的 64 位指令集,設計上吸取了 AArch32 的經驗教訓。
- 固定長度:所有指令都是 32 位寬,解碼簡單。
- 取消大規模條件執行:只有分支、比較和少數其他指令支持條件執行。這釋放了寶貴的指令編碼空間,用于其他功能。
- 新的指令編碼:擁有 31 個通用寄存器(X0-X30),64位(X)和32位(W)視圖。
ADD X0, X1, X2
; 64位加法ADD W0, W1, W2
; 32位加法,結果高32位清零
- 改進的立即數和尋址模式:雖然不如 AArch32 靈活,但仍然功能強大。
- 零寄存器:
XZR
/WZR
寄存器始終返回 0,簡化了許多操作(例如,比較、清零)。- 示例:
MOV X0, XZR
; 將 X0 清零 (實際上是一條ORR
指令的別名)
- 示例:
4. 關鍵指令類別(通用)
a. 數據處理指令
- 算術運算:
ADD
,ADC
(帶進位加),SUB
,SBC
(帶借位減),MUL
,MLA
(乘加) - 邏輯運算:
AND
,ORR
(或),EOR
(異或),BIC
(位清除,A AND NOT B
) - 移位操作:
LSL
(邏輯左移),LSR
(邏輯右移),ASR
(算術右移),ROR
(循環右移) - 比較指令:
CMP
(比較,本質上是SUBS
),CMN
(負數比較),TST
(位測試,本質上是ANDS
),TEQ
(相等測試)
b. 加載-存儲指令
這是 ARM 架構的基石。
- 單寄存器傳輸:
LDR R0, [R1]
; 從 R1 指向的地址加載一個字到 R0STR R0, [R1]
; 將 R0 中的字存儲到 R1 指向的地址- 支持前變址、后變址等多種尋址模式:
LDR R0, [R1, #4]!
; 前變址:地址 = R1+4,然后 R1 = R1+4LDR R0, [R1], #4
; 后變址:地址 = R1,然后 R1 = R1+4
- 多寄存器傳輸:
LDM
(加載多個),STM
(存儲多個),用于高效地操作堆棧和內存塊。- 示例:
STMDB SP!, {R4-R11, LR}
; 壓棧:將寄存器 R4-R11 和 LR 壓入堆棧 (在函數開頭) - 示例:
LDMIA SP!, {R4-R11, PC}
; 出棧:從堆棧恢復 R4-R11,并將返回地址直接裝入 PC (函數返回)
- 示例:
c. 分支與控制流指令
B label
; 無條件跳轉到標簽label
BL label
; 分支并鏈接:跳轉到標簽,同時將返回地址 (PC+4) 存入 LR (R14)。用于函數調用。BX R0
; 分支并交換指令集(例如,從 ARM 切換到 Thumb,反之亦然)RET X30
; (A64) 從函數返回,相當于MOV PC, LR
d. 協處理器和系統指令
MRS R0, CPSR
; 將特殊寄存器(如 CPSR)的值移動到通用寄存器 R0MSR CPSR, R0
; 將 R0 的值移動到特殊寄存器SVC #0x80
; (AArch32) 發起一個系統調用(軟件中斷)HVC
,SMC
; 用于虛擬化和安全監控調用
5. 條件執行(AArch32 的精華)
條件碼附加在指令助記符的后面。條件基于 APSR
中的標志位:
條件碼 | 含義 | 標志位測試 |
---|---|---|
EQ | 相等 | Z == 1 |
NE | 不相等 | Z == 0 |
CS/HS | 進位置位/無符號高于或相同 | C == 1 |
CC/LO | 進位清零/無符號低于 | C == 0 |
MI | 負數 | N == 1 |
PL | 正數或零 | N == 0 |
VS | 溢出 | V == 1 |
VC | 無溢出 | V == 0 |
HI | 無符號高于 | (C == 1) && (Z == 0) |
LS | 無符號低于或相同 | (C == 0) || (Z == 1) |
GE | 有符號大于或等于 | N == V |
LT | 有符號小于 | N != V |
GT | 有符號大于 | (Z == 0) && (N == V) |
LE | 有符號小于或等于 | (Z == 1) || (N != V) |
AL | 總是執行 | 無條件 |
示例:高效的除法余數計算
; C 代碼: if (a > b) { a = a - b; }
; R0 = a, R1 = b
CMP R0, R1 ; 比較 a 和 b
SUBHI R0, R0, R1 ; 如果 a > b (無符號), 則執行 a = a - b
; 這避免了分支指令,提高了效率(無流水線沖刷)。
總結
- ARM (AArch32):功能強大,靈活性高,以條件執行和靈活的桶式移位器為特點。
- Thumb-2:代碼密度高,性能好,是 Cortex-M 系列的默認和唯一選擇。
- A64 (AArch64):現代 64 位設計,規則化,擁有更多寄存器,專注于性能和能效,用于高端應用處理器。
理解 ARM 指令集的關鍵在于實踐。使用 QEMU 模擬器或一塊簡單的開發板(如 Raspberry Pi 或 STM32 Nucleo),通過反匯編編譯器生成的代碼,是學習細節的最佳方式。