嵌入式Linux ARM匯編(三)——ARM匯編指令(四)
十二、ARM偽指令
ARM偽指令不是ARM指令集中的指令,只是為了編程方便編譯器定義了偽指令,使用時可以像其他ARM指令一樣使用,但在編譯時這些偽指令將被等效的ARM指令代替。
1、符號定義偽指令
A、全局變量定義GBLA(GBLL/GBLS) 全局變量名 GBLA 、GBLL 和GBLS 偽指令用於定義一個ARM 程序中的全局變量,並將其初始化。其中:
GBLA 偽指令用於定義一個全局的數字變量,並初始化為0 ;
GBLL 偽指令用於定義一個全局的邏輯變量,並初始化為F(假);
GBLS 偽指令用於定義一個全局的字符串變量,並初始化為空; 由於以上三條偽指令用於定義全局變量,因此在整個程序范圍內變量名必須唯一。
B、局部變量定義 LCLA (LCLL 或 LCLS )局部變量名
LCLA 、LCLL 和LCLS 偽指令用於定義一個ARM 程序中的局部變量,並將其初始化。其中:
LCLA偽指令用於定義一個局部的數字變量,並初始化為0 ;
LCLL偽指令用於定義一個局部的邏輯變量,並初始化為F(假);
LCLS偽指令用於定義一個局部的字符串變量,並初始化為空;
以上三條偽指令用於聲明局部變量,在其作用范圍內變量名必須唯一。
C、變量的賦值變量名 SETA (SETL 或 SETS )表達式
偽指令 SETA 、SETL 、SETS 用於給一個已經定義的全局變量或局部變量賦值。
SETA偽指令用於給一個數學變量賦值;
SETL偽指令用於給一個邏輯變量賦值;
SETS偽指令用於給一個字符串變量賦值;
其中,變量名為已經定義過的全局變量或局部變量,表達式為將要賦給變量的值。
D、RLIST名稱 RLIST { 寄存器列表 }
RLIST偽指令可用於對一個通用寄存器列表定義名稱,使用該偽指令定義的名稱可在ARM 指令 LDM/STM中使用。在LDM/STM指令中,列表中的寄存器訪問次序為根據寄存器的編號由低到高,而與列表中的寄存器排列次序無關。RegList RLIST {R0-R5,R8,R10} 將寄存器列表名稱定義為 RegList ,可在ARM指令LDM/STM中通過該名稱訪問寄存器列表。
2、數據定義偽指令
數據定義偽指令一般用於為特定的數據分配存儲單元,同時可完成已分配存儲單元的初始化。
常見的數據定義偽指令有如下幾種:
DCB 用於分配一片連續的字節存儲單元並用指定的數據初始化。
DCW(DCWU)用於分配一片連續的半字存儲單元並用指定的數據初始化。
DCD (DCDU)用於分配一片連續的字存儲單元並用指定的數據初始化。
DCFD(DCFDU)用於為雙精度的浮點數分配一片連續的字存儲單元並用指定的數據初始化。
DCFS(DCFSU)用於為單精度的浮點數分配一片連續的字存儲單元並用指定的數據初始化。
DCQ(DCQU)用於分配一片以8字節為單位的連續的存儲單元並用指定的數據初始化。
SPACE 用於分配一片連續的存儲單元。
MAP 用於定義一個結構化的內存表首地址。
FIELD 用於定義一個結構化的內存表的數據域。
A、DCB 語法格式:
標號 DCB 表達式
DCB偽指令用於分配一片連續的字節存儲單元並用偽指令中指定的表達式初始化。其中,表達式可以為0~255的數字或字符串。DCB 也可用“=”代替。
使用示例:
Str DCB “This is a test” ;分配一片連續的字節存儲單元並初始化。
B、DCW(或DCWU) 語法格式:
標號 DCW (或DCWU) 表達式
DCW(或DCWU)偽指令用於分配一片連續的半字存儲單元並用偽指令中指定的表達式初始化。
其中,表達式可以為程序標號或數字表達式。
用DCW分配的字存儲單元是半字對齊的,而用DCWU分配的字存儲單元並不嚴格半字對齊。
使用示例:
DataTest DCW 1 ,2 ,3 ;分配一片連續的半字存儲單元並初始化。
C、DCD(或DCDU) 語法格式:
標號 DCD(或DCDU) 表達式
DCD(或DCDU)偽指令用於分配一片連續的字存儲單元並用偽指令中指定的表達式初始化。其中,表達式可以為程序標號或數字表達式。DCD也可 用"&” 代替。
用DCD分配的字存儲單元是字對齊的,而用DCDU分配的字存儲單元並不嚴格字對齊。
使用示例:
DataTest DCD 4 ,5 ,6 ;分配一片連續的字存儲單元並初始化。
D、DCFD(或DCFDU) 語法格式:
標號 DCFD(或DCFDU) 表達式
DCFD(或DCFDU)偽指令用於為雙精度的浮點數分配一片連續的字存儲單元並用偽指令中指定的表達式初始化。每個雙精度的浮點數占據兩個字單元。用 DCFD分配的字存儲單元是字對齊的,而用DCFDU分配的字存儲單元並不嚴格字對齊。
使用示例: FDataTest DCFD 2E115 ,-5E7 ;分配一片連續的字存儲單元並初始化 為指定的雙精度數。
E、DCFS(或DCFSU) 語法格式:
標號 DCFS(或DCFSU) 表達式
DCFS(或DCFSU)偽指令用於為單精度的浮點數分配一片連續的字存儲單元並用偽指令中指定的表達式初始化。每個單精度的浮點數占據一個字單元。用 DCFS分配的字存儲單元是字對齊的,而用DCFSU分配的字存儲單元並不嚴格字對齊。
使用示例:
FDataTest DCFS 2E5 ,-5E -7 ;分配一片連續的字存儲單元並初始化為 指定的單精度數。
F、DCQ(或DCQU) 語法格式:
標號 DCQ(或DCQU) 表達式
DCQ(或DCQU)偽指令用於分配一片以8個字節(雙字)為單位的連續存儲區域並用偽指令中指定的表達式 初始化。 用DCQ分配的存儲單元是字對齊的,而用DCQU 分配的存儲單元並不嚴格字對齊。
使用示例:
DataTest DCQ 100 ;分配一片連續的存儲單元並初始化為指定的值。
G、SPACE 語法格式:
標號 SPACE 表達式
SPACE偽指令用於分配一片連續的存儲區域並初始化為0 。其中,表達式為要分配的字節數。
SPACE也可用“ % ”代替。
使用示例:
DataSpace SPACE 100 ;分配連續100字節的存儲單元並初始化為0 。
H、MAP 語法格式:
MAP 表達式 { ,基址寄存器 }
MAP偽指令用於定義一個結構化的內存表的首地址。MAP也可用“^” 代替。
表達式可以為程序中的標號或數學表達式,基址寄存器為可選項,當基址寄存器選項不存在時,表達式的值即為內存表的首地址,當該選項存在時,內存表的首地址 為表達式的值與基址寄存器的和。
MAP偽指令通常與FIELD偽指令配合使用來定義結構化的內存表。
使用示例:
MAP 0x100 ,R0 ;定義結構化內存表首地址的值為0x100+R0 。
I、FILED 語法格式:
標號 FIELD 表達式
FIELD偽指令用於定義一個結構化內存表中的數據域。FILED 也可用“#” 代替。
表達式的值為當前數據域在內存表中所占的字節數。
FIELD偽指令常與MAP偽指令配合使用來定義結構化的內存表。MAP偽指令定義內存表的首地址,FIELD偽指令定義內存表中的各個數據域,並可以為 每個數據域指定一個標號供其他的指令引用。
注意MAP和FIELD偽指令僅用於定義數據結構,並不實際分配存儲單元。
使用示例:
MAP 0x100 ; 定義結構化內存表首地址的值為0x100。
A FIELD 16 ; 定義A的長度為16字節,位置為0x100。
B FIELD 32 ; 定義B的長度為32字節,位置為0x110。
S FIELD 256 ;定義S的長度為256字節,位置為0x130。
3、匯編控制偽指令
匯編控制偽指令用於控制匯編程序的執行流程,常用的匯編控制偽指令包括以下幾條:
IF 、ELSE 、ENDIF
WHILE 、WEND
MACRO 、MEND、MEXIT
A、IF、ELSE、ENDIF 語法格式:
IF 邏輯表達式
指令序列 1
ELSE
指令序列 2
ENDIF
IF 、ELSE 、ENDIF偽指令能根據條件的成立與否決定是否執行某個指令序列。當IF後面的邏輯表達式為真,則執行指令序列1 ,否則執行指令序 列2 。其中,ELSE及指令序列2可以沒有,此時,當IF後面的邏輯表達式為真,則執行指令序列1 ,否則繼續執行後面的指令。
IF 、ELSE 、ENDIF偽指令可以嵌套使用。
使用示例:
GBLL Test ;聲明一個全局的邏輯變量,變量名為Test
IF Test = TRUE
指令序列 1
ELSE
指令序列 2
ENDIF
B、WHILE、WEND 語法格式:
WHILE 邏輯表達式
指令序列
WEND
WHILE 、WEND偽指令能根據條件的成立與否決定是否循環執行某個指令序列。當WHILE後面的邏輯表達式為真,則執行指令序列,該指令序列執行完畢後,再判斷 邏輯表達式的值,若為真則繼續執行,一直到邏輯表達式的值為假。
WHILE 、WEND偽指令可以嵌套使用。
使用示例:
GBLA Counter ; 聲明一個全局的數學變量,變量名為Counter
Counter SETA 3 ;由變量Counter 控制循環次數
……
WHILE Counter < 10
指令序列
WEND
C、MACRO、MEND、MEXIT 語法格式:
MACRO
[$ 標號]宏名[$ 參數 1 ,$ 參數 2 ,……]
語句段MEXIT語句段
MEND
MACRO 、MEND偽指令可以將一段代碼定義為一個整體,稱為宏指令,然後就可以在程序中通過宏指令多次調用該段代碼。其中,$標號在宏指令被展開 時,標號會被替 換為用戶定義的符號,宏指令可以使用一個或多個參數,當宏指令被展開時,這些參數被相應的值替換。包含在MACRO和MEND之間的指令序列稱為宏定義體,在宏定義體的第一行應聲明宏的原型(包含宏名、所需的參數),然後就可以在匯編程序中通過宏名 來 調用該指令序列。在源程序被編譯時,匯編器將宏調用展開,用宏定義中的指令序列代替程序中的宏調用,並將實際參數的值傳遞給宏定義中的形式參數。
宏是一段功能完整的程序,能夠實現一個特定的功能,在使用中可以把它視為一個子程序。在其他程序中 可以調用宏來完成某個功能。調用宏時通過調用宏的名稱來實現的。在源程序被編譯時,匯編器將宏調用展開,用宏定義中的指令序列替換程序中宏名的調用,並將實際參數的值傳遞給宏定義中的參數。宏與子程序的區別在於調用宏時編譯器會在調用處插入宏的程序段,有多少次調用就會插入多少宏的程序段,調用宏的好處是不占用傳遞參數的寄存器,不要保護現場,但如果多次調用宏,則無形中增加了代碼量。
MACRO、MEND偽指令可以嵌套使用。
D、MEXIT 語法格式:
MEXIT
MEXIT用於從宏定義中跳轉出去。
4、其他偽指令
A、AREA 語法格式:
AREA 段名 屬性1 ,屬性2 ,……
AREA偽指令用於定義一個代碼段或數據段。其中,段名若以數字開頭,則該段名需用“|”括起來,如:|1_test| 。
屬性字段表示該代碼段(或數據段)的相關屬性,多個屬性用逗號分隔。常用的屬性如下:
— CODE 屬性:用於定義代碼段,默認為READONLY 。
— DATA 屬性:用於定義數據段,默認為READWRITE 。
— READONLY 屬性:指定本段為只讀,代碼段默認為READONLY 。
— READWRITE 屬性:指定本段為可讀可寫,數據段的默認屬性為READWRITE 。
— ALIGN 屬性:使用方式為ALIGN表達式。在默認時,ELF(可執行連接文件)的代碼段和數據段是按字對齊的,表達式的取值范圍為0~31,相應的對齊方式為2 表達式次方。
— COMMON 屬性:該屬性定義一個通用的段,不包含任何的用戶代碼和數據。各源文件中同名的COMMON段共享同一段存儲單元。
一個匯編語言程序至少要包含一個段,當程序太長時,也可以將程序分為多個代碼段和數據段。
使用示例: AREA Init ,CODE ,READONLY ; 該偽指令定義了一個代碼段,段 名為Init ,屬性為只讀。
B、ALIGN 語法格式:
ALIGN { 表達式 { ,偏移量 }}
ALIGN偽指令可通過添加填充字節的方式,使當前位置滿足一定的對齊方式。其中,表達式的值用於指定對齊方式,可能的取值為2的冪,如1 、2 、 4 、8 、16 等。若未指定表達式,則將當前位置對齊到下一個字的位置。偏移量也為一個數字表達式,若使用該字段,則當前位置的對齊方式為:2的表達 式次冪+偏移 量。
使用示例:
AREA Init ,CODE ,READONLY ,ALIEN=3 ;指定後面的指令為8 字節對齊。
指令序列
END
C、CODE16、CODE32 語法格式:
CODE16(或CODE32)
CODE16偽指令通知編譯器,其後的指令序列為16位的Thumb指令。
CODE32偽指令通知編譯器,其後的指令序列為32位的ARM指令。
若 在匯編源程序中同時包含ARM指令和Thumb指令時,可用CODE16偽指令通知編譯器其後的指令序列為16位的Thumb指令,CODE32偽指 令 通知編譯器其後的指令序列為32位的ARM指令。因此,在使用ARM指令和Thumb指令混合編程的代碼裡,可用這兩條偽指令進行切換,但注意他們只 通知 編譯器其後指令的類型,並不能對處理器進行狀態的切換。
使用示例:
AREA Init ,CODE ,READONLY ……
CODE32 ; 通知編譯器其後的指令為32位的 ARM指令
LDR R0 ,=NEXT+1 ;將跳轉地址放入寄存器R0
BX R0 ; 程序跳轉到新的位置執行, 並將處理器切換到Thumb工作狀態
……
CODE16 ; 通知編譯器其後的指令為16位的 Thumb指令
NEXT LDR R3,=0x3FF
……
END ;
D、ENTRY 語法格式:
ENTRY
ENTRY偽指令用於指定匯編程序的入口點。在一個完整的匯編程序中至少要有一個ENTRY(也可以有多個,當有多個ENTRY時,程序的真正入口點由鏈 接器指定),但在一個源文件裡最多只能有一個ENTRY(可以沒有)。
使用示例:
AREA Init ,CODE ,READONLY
ENTRY ; 指定應用程序的入口點
……
E、END 語法格式:
END
END偽指令用於通知編譯器已經到了源程序的結尾。
使用示例:
AREA Init ,CODE ,READONLY
……
END ;指定應用程序的結尾
F、EQU 語法格式:
名稱 EQU 表達式 { ,類型 }
EQU偽指令用於為程序中的常量、標號等定義一個等效的字符名稱,類似於C語言中的#define 。其中EQU可用“*”代替。名稱為EQU偽指令定義的字符名稱,當表達式為32位的常量時,可以指定 表達式的數據類型,可以有以下三種類型:
CODE16 、CODE32 和DATA
使用示例:
Test EQU 50 ; 定義標號Test 的值為50。
Addr EQU 0x55 ,CODE32 ; 定義Addr的值為0x55 ,且該處為32位的ARM指令。
G、EXPORT(或GLOBAL) 語法格式:
EXPORT 標號 {[WEAK]}
EXPORT偽指令用於在程序中聲明一個全局的標號,該標號可在其他的文件中引用。EXPORT 可用GLOBAL代替。標號在程序中區分大小寫,[WEAK] 選項聲明其他的同名標號優先於該標號被引用。
使用示例:
AREA Init ,CODE ,READONLY
EXPORT Stest ;聲明一個可全局引用的標號Stest
END
H、IMPORT 語法格式:
IMPORT 標號 {[WEAK]}
IMPORT偽指令用於通知編譯器要使用的標號在其他的源文件中定義,但要在當前源文件中引用,而且無論當前源文件是否引用該標號,該標號均會被加入到當 前源文件的符號表中。標 號在程序中區分大小寫,[WEAK] 選項表示當所有的源文件都沒有定義這樣一個標號時,編譯器也不給出錯誤信息,在多數情況下 將該標號置為0 ,若該標號為B或BL指令引用,則將B或BL指令置為NOP操作。
使用示例:
AREA Init ,CODE ,READONLY
IMPORT Main ;通知編譯器當前文件要引用標號Main,但Main在其他源文件中定 義。
END
I、EXTERN 語法格式:
EXTERN 標號 {[WEAK]}
EXTERN偽指令用於通知編譯器要使用的標號在其他的源文件中定義,但要在當前源文件中引用,如果當前源文件實際並未引用該標號,該 標號就不會被加入 到當前源文件的符號表中。標號在程序中區分大小寫, [WEAK] 選項表示當所有的源文件都沒有定義這樣一個標號時,編譯器也不給出錯誤信息,在多數情 況下將該標號置為0 ,若該標號為B或BL指令引用,則將B或BL指令置為NOP操作。
使用示例:
AREA Init ,CODE ,READONLY
EXTERN Main ;通知編譯器當前文件要引用標號Main,但Main在其他源文件中定 義。
END
J、GET(或INCLUDE) 語法格式:
GET 文件名
GET偽指令用於將一個源文件包含到當前的源文件中,並將被包含的源文件在當前位置進行匯編處理。可 以使用INCLUDE代替GET。
匯編程序中常用的方法是在某源文件中定義一些宏指令,用EQU定義常量的符號名稱,用MAP和FIELD定義結構化的數據類型,然後用GET偽指令將這個 源文件包含到其他的源文件中。使用方法與C 語言中的"include” 相似。
GET偽指令只能用於包含源文件,包含目標文件需要使用INCBIN偽指令
使用示例:
AREA Init ,CODE ,READONLY
GET a1.s ; 通知編譯器當前源文件包含源文件a1.s
GET C:\a2.s ; 通知編譯器當前源文件包含源文件C:\a2.s
END
K、INCBIN 語法格式:
INCBIN 文件名
INCBIN偽指令用於將一個目標文件或數據文件包含到當前的源文件中,被包含的文件不作任何變動的存放在當前文件中,編譯器從其後開始繼續處理。
使用示例:
AREA Init ,CODE ,READONLY
INCBIN a1.dat ; 通知編譯器當前源文件包含文件a1.dat
INCBIN C:\a2.txt ;通知編譯器當前源文件包含文件C:\a2.txt
END
L、RN 語法格式:
名稱 RN 表達式
RN偽指令用於給一個寄存器定義一個別名。采用這種方式可以方便程序員記憶該寄存器的功能。其中,名稱為給寄存器定義的別名,表達式為寄存器的編碼。
使用示例:
Temp RN R0 ;將R0定義一個別名Temp
M、ROUT 語法格式:
{名稱} ROUT
ROUT偽指令用於給一個局部變量定義作用范圍。在程序中未使用該偽指令時,局部 變量的作用范圍為所在的AREA,而使用ROUT後,局部變量的作為范圍為當前ROUT和下一個ROUT之間。 參考博文:
ARM 匯編之尋址方式 (CSDN bianhonglei)
ARM匯編--尋址方式 (CSDN sfrysh)
ARM匯編指令集(china unix hdu_song)
本文出自 “生命不息,奮斗不止” 博客,請務必保留此出處http://9291927.blog.51cto.com/9281927/1785934