ARM彙編僞指令介紹
在ARM彙編語言程式裡,有一些特殊的助記符,這些助記符與指令系統的助記符不同,沒有相對應的操作碼,這些特殊指令助記符被稱為僞指令,他們所完成的操作稱為僞操作。僞指令在源程式中的作用是為完成彙程式設計式作各種準備工作的,這些僞指令僅在彙編過程中起作用,一旦彙編結束,僞指令的使命就完成。
在ARM的彙程式設計式中,有如下幾種僞指令:符号定義僞指令、資料定義僞指令、彙編控制僞指令、宏指令以及其他僞指令。
1)其他常用的僞指令
還有一些其他的僞指令,在彙程式設計式中經常會被使用,包括以下幾條:
— AREA
— ALIGN
— CODE16、CODE32
— ENTRY
— END
— EQU
— EXPORT(或GLOBAL)
— IMPORT
— EXTERN
— GET(或INCLUDE)
— INCBIN
— RN
— ROUT
1.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,屬性為隻讀
2.ALIGN
文法格式:
ALIGN {表達式{,偏移量}}
ALIGN僞指令可通過添加填充位元組的方式,使目前位置滿足一定的對其方式|。其中,表達式的值用于指定對齊方式,可能的取值為2的幂,如1、2、4、8、16等。若未指定表達式,則将目前位置對齊到下一個字的位置。偏移量也為一個數字表達式,若使用該字段,則目前位置的對齊方式為:2的表達式次幂+偏移量。
使用示例:
AREA Init,CODE,READONLY,ALIEN=3;指定後面的指令為8位元組對齊。
指令序列
END
3.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;程式結束
4.ENTRY
文法格式:
ENTRY
ENTRY僞指令用于指定彙程式設計式的入口點。在一個完整的彙程式設計式中至少要有一個ENTRY(也可以有多個,當有多個ENTRY時,程式的真正入口點由連結器指定),但在一個源檔案裡最多隻能有一個ENTRY(可以沒有)。
使用示例:
AREA Init,CODE,READONLY
ENTRY;指定應用程式的入口點
……
5.END
文法格式:
END
END僞指令用于通知編譯器已經到了源程式的結尾。
使用示例:
AREA Init,CODE,READONLY
……
END;指定應用程式的結尾
6.EQU
文法格式:
名稱EQU表達式{,類型}
EQU僞指令用于為程式中的常量、标号等定義一個等效的字元名稱,類似于C語言中的#define。
其中EQU可用“ * ”代替。
名稱為EQU僞指令定義的字元名稱,當表達式為32位的常量時,可以指定表達式的資料類型,可以有以下三種類型:
CODE16、CODE32和DATA
使用示例:
Test EQU 50;定義标号Test的值為50
Addr EQU 0x55,CODE32;定義Addr的值為0x55,且該處為32位的ARM令。
7.EXPORT(或GLOBAL)
文法格式:
EXPORT标号{[WEAK]}
EXPORT僞指令用于在程式中聲明一個全局的标号,該标号可在其他的檔案中引用。EXPORT可用GLOBAL代替。标号在程式中區分大小寫,[WEAK]選項聲明其他的同名标号優先于該标号被引用。
使用示例:
AREA Init,CODE,READONLY
EXPORT Stest;聲明一個可全局引用的标号Stest……
END
8.IMPORT
文法格式:
IMPORT标号{[WEAK]}
IMPORT僞指令用于通知編譯器要使用的标号在其他的源檔案中定義,但要在目前源檔案中引用,而且無論目前源檔案是否引用該标号,該标号均會被加入到目前源檔案的符号表中。
标号在程式中區分大小寫,[WEAK]選項表示當所有的源檔案都沒有定義這樣一個标号時,編譯器也不給出錯誤資訊,在多數情況下将該标号置為0,若該标号為B或BL指令引用,則将B或BL指令置為NOP操作。
使用示例:
AREA Init,CODE,READONLY
IMPORT Main;通知編譯器目前檔案要引用标号Main,但Main在其他源檔案中定義……
END
9.EXTERN
文法格式:
EXTERN标号{[WEAK]}
EXTERN僞指令用于通知編譯器要使用的标号在其他的源檔案中定義,但要在目前源檔案中引用,如果目前源檔案實際并未引用該标号,該标号就不會被加入到目前源檔案的符号表中。标号在程式中區分大小寫,[WEAK]選項表示當所有的源檔案都沒有定義這樣一個标号時,編譯器也不給出錯誤資訊,在多數情況下将該标号置為0,若該标号為B或BL指令引用,則将B或BL指令置為NOP操作。
使用示例:
AREA Init,CODE,READONLY
EXTERN Main;通知編譯器目前檔案要引用标号Main,但Main在其他源檔案中定義……
END
10.GET(或INCLUDE)
文法格式:
GET檔案名
GET僞指令用于将一個源檔案包含到目前的源檔案中,并将被包含的源檔案在目前位置進行彙編處理。可以使用INCLUDE代替GET。
彙程式設計式中常用的方法是在某源檔案中定義一些宏指令,用EQU定義常量的符号名稱,用MAP和FIELD定義結構化的資料類型,然後用GET僞指令将這個源檔案包含到其他的源檔案中。使用方法與C語言中的“ include ”相似。
GET僞指令隻能用于包含源檔案,包含目标檔案需要使用INCBIN僞指令
使用示例:
AREA Init,CODE,READONLY
GET a1.s;通知編譯器目前源檔案包含源檔案a1.s
GE T C:\a2.s;通知編譯器目前源檔案包含源檔案C:\ a2.s ……
END
11.INCBIN
文法格式:
INCBIN檔案名
INCBIN僞指令用于将一個目标檔案或資料檔案包含到目前的源檔案中,被包含的檔案不作任何變動的存放在目前檔案中,編譯器從其後開始繼續處理。
使用示例:
AREA Init,CODE,READONLY
INCBIN a1.dat;通知編譯器目前源檔案包含檔案a1.dat
INCBIN C:\a2.txt;通知編譯器目前源檔案包含檔案C:\a2.txt……
END
12.RN
文法格式:
名稱RN表達式
RN僞指令用于給一個寄存器定義一個别名。采用這種方式可以友善程式員記憶該寄存器的功能。其中,名稱為給寄存器定義的别名,表達式為寄存器的編碼。
使用示例:
Temp RN R0;将R0定義一個别名Temp
13.ROUT
文法格式:
{名稱} ROUT
ROUT僞指令用于給一個局部變量定義作用範圍。在程式中未使用該僞指令時,局部變量的作用範圍為所在的AREA,而使用ROUT後,局部變量的作為範圍為目前ROUT和下一個ROUT之間。
2)ARM雜項僞指令
1.ADR僞指令:小範圍的位址讀取僞指令。
ADR指令将基于PC相對偏移的位址值讀取到寄存器中。在彙編編譯源程式時,ADR僞指令被編譯器替換成一條合适的指令。通常編譯器用一條ADD指令或SUB指令來實作該ADR僞指令的功能。
指令格式:ADR{cond} register ,expr
Register加載的寄存器
Expr程式相對偏移或寄存器相對偏移的表達式
非字對齊位址在-255~255位元組範圍内;
字對齊位址在-1020~1020位元組範圍内。
舉例:
Start MOV R1,#10
ADR R4,start ;相當于PC-10後指派給R4
2.ADRL指令:中等範圍的位址讀取僞指令。
ADRL指令将基于PC相對偏移的位址值或基于相對偏移的位址值讀取到寄存器中,比ADR僞指令可讀取更大範圍的位址。在彙編編譯源程式時,ADRL僞指令被編譯器替換成兩條合适的指令。若不能用兩條指令實作ADRL僞指令功能,則産生錯誤,編譯失敗。
指令格式與ADR相同
非字對齊位址在64K位元組範圍内;
字對齊位址在256K位元組範圍内。
舉例:
Start MOV R1,#10
ADR R4,start+6000 ;=>ADD R4,PC,#0xe800 ADD R4,R4,#0x254
3.LDR指令 大範圍的位址讀取僞指令
LDR僞指令用于加載32位的立即數或一個位址值到指定寄存器。
在彙編編譯源程式時,LDR指令被編譯器替換成一條合适的指令,若加載的常數未超出MOV或MVN的範圍,則使用MOV或MVN指令代替該LDR僞指令,否則彙編器将常量放入字池(記憶體),并使用一條程式相對偏移的LDR指令從文字池讀出常量。
指令格式:LDR {cond} register , = expr/label_expr
Expr32位立即數
Label_expr基于PC的位址表達式或外部表達式
舉例
LDR R0,=0x123987;加載32位立即數
LDR R0,=DATA_BUF+60;加載DATA_BUF位址+60
4.NOP指令
NOP指令産生所需的ARM無操作代碼。可以使用指令MOV R0,R0。NOP不能有條件使用。執行和不執行無操作指令是一樣的,因而不需要有條件執行。ALU狀态不受NOP影響。
3)符号定義( Symbol Definit年ion)僞指令
符号定義僞指令用于定義ARM彙程式設計式中的變量、對變量指派以及定義寄存器的别名等操作。
常見的符号定義僞指令有如下幾種:
Ø用于定義全局變量的GBLA、GBLL和GBLS
Ø用于定義局部變量的LCLA、LCLL和LCLS
Ø用于對變量指派的SETA、SETL、SETS
Ø為通用寄存器清單定義名稱的RLIST
1.GBLA、GBLL和GBLS
文法格式:
GBLA(GBLL或GBLS)全局變量名
GBLA、GBLL和GBLS僞指令用于定義一個ARM程式中的全局變量,并将其初始化。其中:
GBLA僞指令用于定義一個全局的數字變量,并初始化為0;
GBLL僞指令用于定義一個全局的邏輯變量,并初始化為F(假);
GBLS僞指令用于定義一個全局的字元串變量,并初始化為空;
由于以上三條僞指令用于定義全局變量,是以在整個程式範圍内變量名必須唯一。
使用示例:
GBLA Test1;定義一個全局的數字變量,變量名為Test1
Test1 SETA 0xaa;将該變量指派為0xaa
GBLL Test2;定義一個全局的邏輯變量,變量名為Test2
Test2 SETL {TRUE};将該變量指派為真
GBLS Test3;定義一個全局的字元串變量,變量名為Test3
Test3 SETS “ Testing ”;将該變量指派為“ Testing ”
2.LCLA、LCLL和LCLS
文法格式:
LCLA(LCLL或LCLS)局部變量名
LCLA、LCLL和LCLS僞指令用于定義一個ARM程式中的局部變量,并将其初始化。其中:
LCLA僞指令用于定義一個局部的數字變量,并初始化為0;
LCLL僞指令用于定義一個局部的邏輯變量,并初始化為F(假);
LCLS僞指令用于定義一個局部的字元串變量,并初始化為空;
以上三條僞指令用于聲明局部變量,在其作用範圍内變量名必須唯一。
使用示例:
LCLA Test4;聲明一個局部的數字變量,變量名為Test4
Test3 SETA 0xaa;将該變量指派為0xaa
LCLL Test5;聲明一個局部的邏輯變量,變量名為Test5
Test4 SETL {TRUE};将該變量指派為真
LCLS Test6;定義一個局部的字元串變量,變量名為Test6
Test6 SETS “ Testing ”;将該變量指派為“ Testing ”
3.SETA、SETL和SETS
文法格式:
變量名SETA(SETL或SETS)表達式
僞指令SETA、SETL、SETS用于給一個已經定義的全局變量或局部變量指派。
SETA僞指令用于給一個數學變量指派;
SETL僞指令用于給一個邏輯變量指派;
SETS僞指令用于給一個字元串變量指派;
其中,變量名為已經定義過的全局變量或局部變量,表達式為将要賦給變量的值。
使用示例:
LCLA Test3;聲明一個局部的數字變量,變量名為Test3
Test3 SETA 0xaa;将該變量指派為0xaa
LCLL Test4;聲明一個局部的邏輯變量,變量名為Test4
Test4 SETL {TRUE};将該變量指派為真
4.RLIST
文法格式:
名稱RLIST {寄存器清單}
RLIST僞指令可用于對一個通用寄存器清單定義名稱,使用該僞指令定義的名稱可在ARM指令LDM/STM中使用。在LDM/STM指令中,清單中的寄存器通路次序為根據寄存器的編号由低到高,而與清單中的寄存器排列次序無關。
使用示例:
RegList RLIST {R0-R5,R8,R10};将寄存器清單名稱定義為RegList,可在ARM指令LDM/STM中通過該名稱通路寄存器清單。