ARM匯編在格式上有少數硬性要求,在排版上幾乎沒什么硬性要求,都不多,以下分別說明。
格式要求?
ARM 匯編有一些格式上的硬性要求,這些規則由匯編器(如 GNU 的
gas
、ARM 官方的armasm
)強制執行,違反會導致編譯錯誤。以下是核心硬性要求:1.?指令格式:操作碼與操作數的順序
- 必須遵循 “操作碼 目標操作數,源操作數”?的順序(與 x86 匯編相反)。
例如:ADD R0, R1, R2 ; 正確:R0 = R1 + R2(目標在前,源在后) ; 錯誤寫法:ADD R1, R2, R0(順序顛倒)
- 立即數前必須加?
#
,寄存器前必須加?R
(或?r
,大小寫不敏感):MOV R0, #10 ; 正確:R0 = 10 ; 錯誤寫法:MOV 0, 10(缺少R和#)
2.?標簽定義:必須以冒號?
:
?結尾
- 標簽(用于跳轉或數據引用)必須以?
:
?結尾,且單獨占一行(或行首):loop: ; 正確:定義loop標簽B loop ; 跳轉至loop ; 錯誤寫法:loop B loop(標簽未加:)
3.?段定義:必須通過偽指令聲明
- 代碼、數據必須放在明確的段中,通過?
.text
(代碼段)、.data
(數據段)等偽指令聲明:.text ; 正確:代碼段開始 main:MOV R0, #0.data ; 正確:數據段開始 var: .word 100 ; 錯誤:無段聲明直接寫指令或數據
4.?注釋格式:必須用特定符號開頭
- GNU 匯編器(
gas
)中,注釋以?@
?開頭;ARM 匯編器(armasm
)支持?;
?或?@
:MOV R0, #0 @ 正確:GNU風格注釋(推薦) MOV R1, #1 ; 正確:ARM匯編器兼容的注釋
5.?指令集模式匹配
- ARM 指令集(32 位)和 Thumb 指令集(16/32 位混合)不能混用,需通過偽指令聲明模式:
.arm ; 聲明ARM模式(32位指令) ADD R0, R1, R2 ; 32位ARM指令.thumb ; 聲明Thumb模式(16/32位指令) ADD R0, R1 ; 16位Thumb指令(操作數限制更嚴格) ; 錯誤:在Thumb模式下使用ARM特有的32位指令
6.?對齊要求:數據和指令的地址對齊
- 數據類型需按大小對齊(如?
.word
?占 4 字節,需 4 字節對齊):.align 2 ; 強制4字節對齊(2^2=4) var: .word 0x12345678 ; 正確:地址為4的倍數 ; 錯誤:未對齊時,某些架構可能無法訪問數據
- 指令對齊:Thumb 指令需 2 字節對齊,ARM 指令需 4 字節對齊(匯編器通常自動處理,但復雜場景需手動用?
.align
?控制)。7.?偽指令格式:必須以?
.
?開頭
- 匯編器偽指令(如段定義、數據定義)必須以?
.
?開頭:.global main ; 正確:聲明全局符號 .word 100 ; 正確:定義4字節數據 ; 錯誤:global main(缺少.)
8.?寄存器使用規范
- 特殊寄存器(如?
PC
、LR
、SP
)有固定用途,不能隨意當作普通寄存器使用:MOV PC, LR ; 正確:用LR恢復PC實現返回 ; 錯誤:MOV LR, #10(隨意修改LR會破壞返回地址)
總結
ARM 匯編的硬性要求主要體現在:指令格式、標簽定義、段劃分、注釋符號、指令集模式、對齊規則?等方面。這些規則確保匯編器能正確解析代碼,生成符合目標架構的機器碼。不同匯編器(如
gas
和armasm
)可能有細微差異,但上述核心規則是通用的。
排版建議
ARM 匯編對排版(格式布局)沒有像高級語言那樣嚴格的語法約束,但良好的排版習慣能提高代碼可讀性,且部分匯編器對特定元素的位置有隱性要求。以下是需要注意的排版規則和建議:
一、硬性排版要求(違反會報錯)
標簽必須位于行首
標簽(帶:
的符號)必須放在一行的開頭,不能縮進,否則匯編器會將其識別為指令而非標簽:loop: ; 正確:標簽在行首B loop ; 指令縮進(推薦); 錯誤示例:標簽前有空格error_label: ; 匯編器會報錯(無法識別為標簽)
指令和偽指令不能與標簽同行
標簽必須單獨占一行,不能跟在指令或偽指令后面data: .word 100 ; 錯誤:標簽與偽指令同行; 正確寫法: data:.word 100
操作數之間的逗號必須緊跟前一個操作數
指令的操作數之間用逗號分隔,逗號后可加空格,但逗號不能單獨換行:ADD R0, R1, R2 ; 正確 ADD R0,R1,R2 ; 正確(無空格也可); 錯誤示例:逗號后換行 ADD R0, R1, R2 ; 匯編器會將第二行識別為獨立指令,導致錯誤
二、推薦排版規范(提高可讀性)
指令和偽指令縮進
標簽不縮進,指令、偽指令和注釋縮進(通常用 4 個空格或 1 個 Tab),使代碼結構清晰:main:push {lr} ; 縮進指令mov r0, #0 ; 縮進指令.data ; 偽指令也建議縮進 var:.word 100
注釋與代碼分隔
注釋盡量放在指令右側,或單獨占一行,與代碼保持一定距離(如用多個空格分隔):; 推薦:注釋與代碼用空格分隔 MOV R0, #10 @ 設置返回值為10; 不推薦:注釋緊貼代碼,可讀性差 MOV R0,#10@返回值
同類指令對齊
操作碼、操作數、注釋分別對齊,形成列結構,尤其適合批量處理的指令:MOV R0, #0 @ 初始化參數1 MOV R1, #100 @ 初始化參數2 MOV R2, #255 @ 初始化參數3
空行分隔邏輯塊
用空行分隔不同功能的代碼塊(如初始化、循環、函數調用等),增強層次感:main:push {lr}; 初始化參數mov r0, #5mov r1, #3; 調用加法函數bl addpop {pc}
大小寫統一
寄存器(R0
/r0
)、指令(ADD
/add
)、偽指令(.text
/.TEXT
)的大小寫不影響匯編器解析,但建議統一風格(如大寫指令、小寫寄存器):; 推薦:風格統一 add r0, r1, r2 @ 小寫指令和寄存器; 不推薦:大小寫混亂 Add R0, r1, R2
三、匯編器的靈活性
- 多數 ARM 匯編器(如
gas
)對空格、換行不敏感,只要語法正確,即使排版混亂也能編譯通過(但可讀性極差)。- 唯一的例外是標簽必須在行首,這是幾乎所有匯編器都嚴格要求的排版規則。
總結
ARM 匯編的硬性排版要求極少,僅需遵守 “標簽行首”“操作數逗號不換行” 等規則;但推薦的排版規范(縮進、對齊、空行等)能極大提升代碼的可維護性,尤其在復雜程序中更為重要。良好的排版習慣是匯編開發的基本素養。
AAPCS(Procedure Call Standard for the ARM Architecture)是ARM 架構的過程調用標準,定義了 ARM 程序中函數調用時必須遵循的一套規則,確保不同語言(如 C 和匯編)、不同編譯單元之間的函數調用能夠正確交互。
AAPCS調用規范
簡單說,AAPCS 的核心作用是:規定函數調用時參數如何傳遞、返回值如何存儲、寄存器如何使用、棧如何維護,避免因調用規則不統一導致程序崩潰。
AAPCS 的核心內容
1.?參數傳遞規則
- 前 4 個參數:通過寄存器?
r0-r3
?傳遞(稱為 “參數寄存器”)。
- 例如:
func(a, b, c, d)
?中,a→r0
,b→r1
,c→r2
,d→r3
。- 第 5 個及以后的參數:從右向左依次壓入棧(棧增長方向為低地址)。
- 例如:
func(a, b, c, d, e)
?中,e
?先壓棧,再壓入其他超出 4 個的參數。- 參數類型適配:
- 32 位數據(int、指針等)直接放入寄存器或棧。
- 64 位數據(long long、double)占用兩個連續寄存器(如?
r0-r1
)或棧空間。- 結構體等大型數據:通常通過指針傳遞(放入?
r0
),或按規則拆分到寄存器 / 棧。2.?返回值存儲規則
- 32 位返回值(int、float、指針等):存入?
r0
。- 64 位返回值(long long、double):存入?
r0-r1
(低 32 位在?r0
,高 32 位在?r1
)。- 大型返回值(如結構體):調用者預先在棧上分配空間,將空間地址通過?
r0
?傳遞給被調函數,被調函數將結果寫入該地址。3.?寄存器使用規范
寄存器分為調用者保存寄存器和被調用者保存寄存器,職責明確:
- 調用者保存寄存器(
r0-r3
、r12
):
- 用途:傳遞參數、臨時計算。
- 規則:被調函數可以隨意修改,調用者若需保留這些寄存器的值,需在調用前手動壓棧保存。
- 被調用者保存寄存器(
r4-r11
、r14
=LR):
- 用途:保存局部變量、函數上下文。
- 規則:被調函數若使用這些寄存器,必須在函數入口處壓棧保存,退出前恢復(確保調用者的上下文不被破壞)。
- 特殊寄存器:
r13
(SP):棧指針,必須保持 4 字節或 8 字節對齊(依架構而定)。r15
(PC):程序計數器,不可手動修改(通過分支指令間接更新)。4.?棧的使用規則
- 棧對齊:函數調用前后,棧指針(SP)必須保持 8 字節對齊(確保 64 位數據訪問正確)。
- 棧幀結構:通常包含以下部分(從高地址到低地址):
高地址 → 調用者的棧幀被調用者保存的寄存器(r4-r11等)局部變量臨時數據/參數(第5個及以后的參數) 低地址 → SP(棧指針)
- 棧增長方向:向低地址增長(壓棧時 SP 減小,出棧時 SP 增大)。
5.?其他重要規則
- Thumb 與 ARM 指令集兼容:混合使用時需確保調用跳轉符合指令集切換規范(如用?
BX LR
?而非?MOV PC, LR
)。- 位置無關代碼(PIC):函數調用需支持動態地址重定位(通過相對尋址實現)。
- 浮點參數:通過 VFP(向量浮點單元)寄存器?
s0-s15
?傳遞,遵循類似整數寄存器的規則。為什么需要 AAPCS?
如果沒有統一的調用標準,會導致:
- C 函數調用匯編函數時,參數傳遞方式不匹配(如 C 存
r0
,匯編讀r1
)。- 寄存器被意外修改(如匯編函數修改了
r4
但未恢復,導致 C 函數的局部變量錯亂)。- 棧對齊錯誤(導致硬件無法訪問 64 位數據,觸發異常)。
AAPCS 通過統一這些規則,確保不同語言、不同模塊的代碼能夠無縫協作,是 ARM 架構下混合編程(如 C 與匯編互調)的基礎。
實際開發中,編譯器(如 GCC)會自動遵循 AAPCS 生成代碼,手動編寫匯編時則需要嚴格遵守這些規則,才能確保函數調用正確。