一、偽指令
ARM偽指令有四個,分別是LDR、ADR、ADRL和NOP,下邊對其分別介紹。
1.1 LDR
LDR 偽指令用於加載 32 位的立即數或一個地址值到指定寄存器 。形式如 LDR{cond} register,=[expr | label_expr],與 ARM 指令的 LDR 相比 , 偽指令的 LDR 的參數有“ =” 號 。LDR有三方面的應用:
(1)用於加載常量,如 LDR R2, =0xFF0 其等同於MOV R2, #0xFF0 但需要注意的是LDR指令加載常量可以是合法的立即數也可以不是,但是MOV加載數時必須為合法的立即數。
(2)設置GPIO
GPIO-BASE EQU 0xe0028000
...
LDR R0, =GPIO-BASE
LDR R1,=0x00ffff00
STR R1,[R0,#0x0c]
(3)加載地址
...
LDR R1,=InitStack
...
InitStack
MOV R0, LR
...
1.2 ADR與ADRL
兩者都是將基於 PC 相對偏移的地址值或基於寄存器相對偏移的地址值讀取到寄存器中 。形如 ADR{cond} register,expr 和LDR不同的是,這里沒有等號。兩者作用類似,區別在於兩者可以加載的數的范圍不同,當地址值是字節對齊時 , ADRL取值范圍為- - 64K ~ 64K,ADR為- 255 ~ 255 。
1.3 NOP
空操作指令,就是什么也不干,靜等時間流逝。
1.4 ARM指令與偽指令的區別
偽指令經過匯編編譯后就不存在了,而指令依舊存在。偽操作只是匯編過程中起作用, , 一旦匯編結束, ,偽操作也就隨之消失。另外,ARM 偽指令不屬於 ARM 指令集中的指令,是為了編程方便而定義的。
總結:其實LDR、ADRL、ADR的作用和用法基本類似,主要區別是加載數的范圍大小不同,LDR加載范圍最大,ADRL次之,ADR最小。另外,使用LDR偽指令時,操作數要帶“=”號,ADRL和ADR則不需要。
二、偽操作
偽操作主要有符號定義、數據定義、指令集類型標識和其他類型四種,下邊將進行一一介紹。
2.1 符號定義偽操作
符號定義偽操作用於定義 ARM 匯編程序中的變量、對變量賦值及定義寄存器的別名等操作。常見的符號定義偽操作有如下幾種:
(1)局部變量定義 LCLA 、 LCLL 及 LCLS
(2)全局變量定義 GBLA 、 GBLL 及 GBLS
(3)變量賦值偽操作 SETA 、 SETL 及 SETS
(4)給通用寄存器列表定義名稱RLIST
A為Arithmetic的首字母,意為定義一個數字變量。L為Logic的首字母,意為定義一個邏輯變量(true或false)。S為String的首字母,意為定義一個字符串變量。舉個例子:
GBLA Test1 ;定義全局數字變量Test1
Test1 SETA 0xaa ;Test1=0xaa
GBLL Test2 ;定義全局邏輯變量Test2
Test2 SETL {TRUE} ;Test2=True
GBLS Test3 ;定義全局字符串變量Test3
Test3 SETS “Testing” ;Test3=“Testing”
RLIST 形式如 name RLIST {registers_list} ,該偽操作用於給一個通用寄存器列表定義名稱,使用該偽操作定義的名稱可以在 LDM/STM 中使用。再舉個例子:
list RLIST {R0-R2,R6,R8} ;當你定義一個列表后,以后再用就直接寫列表名稱就好
LDMIA R3!,{R0-R2,R6,R8} ;好處就是當需要多次加載同一寄存器列表時,極大地簡化了書寫
LDMIA R3!,list ;和上邊的語句效果一樣
2.2 數據定義偽操作
數據定義偽操作一般用於為特定的數據分配存儲單元,同時可完成已分配存儲單元的初始化。常見的數據定義偽操作有如下幾種:
(1)DCB 用於分配一片連續的字節存儲單元並用指定的數據初始化。
(2)DCD 用於分配一片連續的字存儲單元並用指定的數據初始化。
(3)LTORG 用於聲明一個數據緩沖池。
(4)SPACE 用於分配一片連續的存儲區域並將其初始化為0 。
(5)MAP 用於定義一個結構化的內存表首地址。
(6)FIELD 用於定義一個結構化的內存表的數據域。
這六個偽操作中的DCB和DCD會在文末給出一個綜合的示例來講解其用法,這里先從LTORG開始講。LTORG主要配合LDR偽指令一起使用,我們都知道用MOV指令時,由於ARM分配了12位用來存儲立即數,所以並不是所有的立即數數都能被存儲,這才有了合法與不合法的立即數之說,而LDR偽指令之所以能加載不合法的立即數的原因就是LTORG存在,使用LDR偽指令時,立即數有多大,你就可以聲明一個相應大小的數據緩沖池來存放該立即數。當然你完全不用顯式聲明,當你不寫時,ARM系統會在編譯階段自動為其補上,所以只需了解這個偽操作即可。這里給了一個例子,幫助你了解LTORG 的使用,看不懂可跳過,反正也不太重要。
LDR R0, =0xAABBCCDD
EOR R1 ,R1,R0
B SUB _pro
LTORG ;聲明一個數據緩沖池用來存儲0xAABBCCDD
SPACE的作用就如上邊所說,具體使用例子如下:
AREA Data,DATA,READWRITE
DataBuf SPACE 1000
…
MAP與FIELD也是一起使用的,它倆有點像C語言中的結構體,具體例子如下,自行理解。
MAP 0x300 ;定義一個結構化的內存表,首地址固定為0x300,包含4個域
Fdata1 FIELD 4 ;Fdata1 長度為4字節
Fdata2 FIELD 8 ;Fdata2 長度為8字節
Fdata3 FIELD 100 ;Fdata3 長度為 100 字節
Fdata4 FIELD 200 ;Fdata4 長度為 200 字節
三、指令集類型標識
指令集類型標識偽操作用來告訴編譯器所處理的是32 位的ARM 指令還是16 位的Thumb 指令,實現這一操作的操作符有ARM 、CODE32 、THUMB、CODE16。其中ARM CODE32指示編譯器將要處理的是 32 位的 ARM 指令,而THUMB和CODE16指示編譯器將要處理的是 16 位的 Thumb 指令。具體例子如下:
AREA ToThumb,CODE,READONLY
ENTRY
ARM ;注意這里
start
ADR R0,into_thumb+1
BX R0
THUMB ;注意這里
into_thumb
MOVS R0,#10
…
四、其他類型偽操作
下邊主要講四種相對常用又簡單的偽操作,由於內容相對簡單,具體示例統一在文末的綜合示例中給出。
(1)段屬性定義偽操作AREA
(2)源程序結尾標識END
(3)聲明程序的入口點ENTRY
(4)定義常量或標號名稱EQU
AREA 用於定義一個代碼段或數據段, AREA 偽操作指示匯編器匯編新的代碼段或數據段。
END 偽操作用於通知編譯器已經到了源程序的結尾。相當於漢語中的句號吧。
ENTRY 偽操作用於指定匯編程序的入口點。在一個完整的匯編程序中(一個程序可以包含多個源文件)至少要有一個ENTRY, 但在一個源文件里不能使用多個ENTRY。
EQU 偽操作用於為程序中的常量、標號等定義一個等效的字符名稱。
五、綜合示例
該例子綜了以上學習的偽指令和偽操作,主要作用是將src中內容復制到dst中。