ARM匯編程序分析過程中,比較難理解的是他的偽操作、宏指令和偽指令。在讀vivi時遇到很多不懂的,所以在此對引導程序中出現偽操作、宏指令和偽指令進行總結,
*****************************************************
一、GET option.s
// GET和INCLUDE功能相同
功能:引進一個被編譯過的文件。
格式:GET filename
其中:fiename 匯編時引入的文件名,可以有路徑名。
GET符號在匯編時對宏定義,EQU符號以及存儲映射時是很有用的,在引入文件匯編完以后,匯編將從GET符號后開始。在被引入的文件中可能有GET符號再引入其他的文件。GET符號不能用來引入目標文件。
*****************************************************
二、INTPND EQU 0x01e00004
//EQU可以用“*”代替,在閱讀源程序時注意。
功能:對一個數字常量賦予一個符號名。
格式:name EQU expression
其中:name 符號名。Expression 寄存器相關或者程序相關的固定值。
使用EQU定義常量,與C語言中用#define定義一個常量相同。
例:num EQU 2 ; 數字2賦予符號num
*****************************************************
三、GBLL THUMBCODE
[ {CONFIG} = 16
THUMBCODE SETL {TRUE}
CODE32
|
THUMBCODE SETL {FALSE}
]
[ THUMBCODE
CODE32 ;for start-up code for Thumb mode
]
//其中[=IF ,|=ELSE ,]= ENDIF, CODE32 表明一下操作都在ARM狀態。這些都是偽操作這段理解為設定THUMCODE的值,然后確定,用戶的程序是在ARM狀態還是THUM狀態。
*****************************************************
四、MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
sub sp,sp,#4 ;decrement sp(to store jump address)
stmfd sp!,{r0} ;PUSH the work register to stack
ldr r0,=$HandleLabel;load the address of HandleXXX to r0
ldr r0,[r0] ;load the contents(service routine start address) of HandleXXX
str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack
ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
MEND
//MACRO……MEND
功能:標志一下宏的定義。
格式:MACRO
Macro_prototype
MEND
宏表達式的格式如下:
{$label} macroname {$ parameter{,parameter2}…}
其中:
$ label 參數,在宏使用時,被給定的符號替代。
Macroname 宏的名稱,并不一定以一條指令或者符號名開始。
$parameter 在宏使用時,被替代的參數,格式為:$parameter=“default value”
在宏體中,參數如:$parameter和變量一樣使用,在被宏引用時,被賦于新值,參數必須用“$”符號加于區別。$label在宏定義內部符號 時很有用,可以看作宏的參數。使用“|”符號作為使用一個參數缺省值的變量,如果使用的是一個空格符串,將省去該變量。在使用內部標志的宏定義中,將內部 標志定義為帶后綴的標志,將會很有用。如果在擴展中空間不夠,可以作為參數和后繼文字之間或者參數之間使用圓點隔開,但在文本和后繼參數之間不能使用圓點。宏可以定義局部變量的范圍。宏還可以嵌套使用。
例:
MACRO
$label xmac $p1,$p2
LCLS err
$labell,loopl
BGE $pl
$labell,loop2
BL $p1
BEG $p1
BEG $labell,loop2
MEND
*****************************************************
五、$和$$
//$臨時變量替換,若程序中需要用字符$則用$$來表示,通常情況下,包含在兩個||之間的$并不表示進行變量替換,但是如果|線是在雙引號內,則將進行變量替換。用“.”來分割出變量名的用法,
GBLS STR1
GBLS STR2
STR1 SETS "AAA"
STR2 SETS "BBB$$STR1.CCC" //匯編后STR2的值為bbAAACCC
*****************************************************
六、 IMPORT Main ; The main entry of mon program
//該偽操作告訴編譯器當前的符號不是在本文件中定義的,在本源文件中可能引用該符號,而不論該源文件是否使用該符號,該符號都將被加入到本源文件中。
格式:
IMPORT symbol {[WEAK]}
symbol 引用的符號的名稱,他是區分大小寫的,[WEAK]指定這個選項后,如果symbol所在的源文件中沒有被定義,編譯器也不會報錯。他和EXTERN作用相同,不同之處在于,如果本源文件沒有實際引用該符號,該符號將不會被加入到本源文件的符號表中。
*****************************************************
七、AREA Init,CODE,READONLY
ENTRY
//功能:指示匯編器匯編一段新的代碼或新的數據區。
格式:
name 給出的特定段名。以數字開頭,必須加豎線,否則,將報錯,例如:|1_Data-Area|。某些名字已保留,如:|C$$code|已經被C編譯器用作代碼,或者用作與C庫相連的代碼段。
Attr 段名屬性,下列屬性是有效的:
ALIGN=expression
缺省狀態下,AOF段將按4個字節對準,expression可以是2~31之間的整數,該段將按2(上標為expression)字節對準。例如,espression等于
10,該段將按1KB對準。
CODE 特定機器指令,缺省為READONLY。
COMDEF 通用段定義。該AOF段可能包括代碼和數據,但必須與其他段名相區別。
COMMON 通用數據段,無須再注釋定義任何代碼和數據,通常由鏈接器初始化為零。
DATA 包含數據,但是不包含指令,缺省為READWRITE
INTERWORK 表明代碼段可以適用ARM/Thumb interworking功能。
NOINIT 表明數據段可以初始化為零,只包含指示符。
PIC 表明定位獨立段,可以不修改情況下,在任意地址執行。
READONLY 表明該段可讀可寫。
匯編時,必須至少有一個AREA指示符。使用AREA符號可以將源程序區分,但是必須不重名。通常需要獨立的AOF段做為代碼或者數據段,較大程序 可以分為多個代碼段。AOF段可以定義局部標簽的范圍,可以使用ROUT符號。如果沒有任何的AREA指示符定義,匯編器將會產生名為|$$$$$$$| 的AOF段和一條診斷信息,將限制由于缺少指示符而產生的錯誤信息,但是并不一定會成功匯編。
*****************************************************
八、LTORG
//LTORG是在此指令出現的地方放一個文本池(literal pool). 在ARM匯編中常用到
ldr r0, =instruction 將地址instruction載入r0
此時編譯器將ldr盡可能的轉變成mov或mvn指令。 如果轉變不成, 將產生一個ldr指令,通過pc相對地址從一塊保存常數的內存區讀出instruction的值。此內存區既是文本池。一般的, 文本池放在END指令之后的地方。但是, 如果偏移地址大于4k空間, ldr指令會出錯(因為ldr的相對偏移地址為12-bit的值). 此時使用LTORG放到會出錯的ldr指令附近,以解決此問題。編譯器會收集沒有分配的ldr的值放到此文本池中
。所以必須在LDR指令前后4KB的范圍內用LTORG顯式地在代碼段中添加一個文字池。
*****************************************************
九、LDR r0,=WTCON ;watch dog disable
LDR r1,=0x0
功能:將一個32位常量或地址讀取至寄存器。
格式:
LDR{condition} register,=[expression|Label-expression]
其中:
condition 可選的條件代碼。
register 讀取的寄存器。
expression 數字常量:
如果該數字常量在MOV或MVN指令的范圍中,匯編器會產生合適的指令;
如果該數字量不在MOV或MVN指令的范圍中,匯編器把該常量于程序后,用程序相關的LDR偽指令讀取,PC與該常量的偏移量不得超過4KB。
Label-expression 程序相關的或外部的表達式。匯編器將其存放在程序后的常量庫(稱為文字池(literal pool))中,用程序相關的LDR偽指令讀取,PC與與該常量的偏移量不得超過4KB。
LDR偽指令的使用有兩個目的:
對于不能被MOV和MVN指令所讀取的立即數,將其變成常量,進行讀取。
將一個程序相關的或外部的表達式讀取進寄存器中。
例:
LDR R1, =0xfff
LDR R2, =place
*****************************************************
十、DCD 0x11110090
;Bank0=OM[1:0], Bank1~Bank7=16bit, bank2=8bit;
//DCD或“&”
功能: 分配一個或多個字,從4個字節邊界開始。
格式:
{label}DCD expression{,expression}…
其中:
expression 可以是:
一個數學表達式;
一個程序相關的表達式。
如果在Thumb代碼中,使用DCD符號定義帶標志的數據時則必須使用DATA符號。
按4個字節對準時,DCD符號會在第一個字節之前插入3個字節的空字符,如果無須對準的話,可以使用DCDU符號。
例:
datal DCD 1,5,20
data2 DCD mem06
data3 DCD glb+4
*****************************************************
十一、ALIGN
//功能:從1個字邊界開始。
格式:
ALIGN {expression {,offset-expression} }
其中:
expression 2(上標為0)到2(上標為31)之間的任意數冪,當前按2(上標為n)字節對準,如果該參數沒有指定,ALIGN將按字對準。
Offset-expression 定義expression指定的對準方式的字節偏移量。
使用ALIGN符號,保證程序正確對準。對于Thumb地址,使用ALIGN符號保證其按字對準,例如:ADR Thuub偽指令只能讀取字對準的地址。
在代碼段出現數據定義符時,使用ALIGE符號。當在代碼段使用數據定義符(DCB,DCW,DCWU,DCDU和%),程序計數器PC并不一定按字對準。
匯編器會在下一條指令時插入3個字節,保證:
ARM狀態下按字對準;
Thumb狀態下按半字對準。
在Thumb狀態下,可以使用ALIGN2對Thumb代碼按半字對準。
使用ALIGN狀態下,還可以充分利用一些ARM處理器的Cache,例如,ARM940T有一個每行4字的Cache,使用ALIGN16按16字節對準,從而最大限度使用Cache。
*****************************************************
十二、^ _ISR_STARTADDRESS
//MAP與"^"
MAP用于定義一個結構化的內存表(StorageMAP)的首地址。此時,內存表的位置計數器{VAR}(匯編器的內置變量)設置成該地址值。MAP可以用”^”代替。
語法:MAP expr {,base-register}
其中,expr為數字表達式或者是程序中已經定義過的標號。Base-register為一個寄存器。當指令中沒有Base-register時, expr為結構化內存表的首地址。此時,內存表的位置計數器{VAR}設置成該地址值。當指令中包含這一項時,結構化內存表的首地址為expr和Base -register寄存器內容的和。
使用說明:MAP偽操作和FIELD偽操作配合使用來定義結構化的內存表結構。
舉例:MAP偽操作
MAP fun ;fun就是內存表的首地址
MAP 0x100,R9 ;內存表的首地址為 R9+0x100
*****************************************************
十三、HandleReset # 4
HandleUndef # 4
HandleSWI # 4
//FIELD和"#"
FIELD 用于定義一個結構化的內存表中的數據域。FIELD 可用“#”代替。
語法:{label} FIELD expr
其中:{label}為可選的。當指令中包含這一項時,label的值為當前內存表的位置計數器{VAR}的值。匯編編譯器處理了這條FIELD偽操作后。
內存表計數器的值將加上expr.expr表示本數據域在內存中所占的字節數。
使用說明:MAP偽操作和FIELD偽操作配合使用來定義結構化的內存表結構。MAP偽操作定義內存表的首地址。FIELD偽操作定義內存表的數據域的字節長度,并可以為每一格數據域指定一個標號,其他指令可以引用該標號。
MAP偽操作中的Base-registe寄存器值隊以其后所有FIELD偽操作定義的數據域是默認使用的,直到遇到新的包含Base-registe項的MAP偽操作需要特別注意的是,MAP偽操作和FIELD偽操作僅僅是定義數據結構,他們并不實際分配內存單元。由MAP偽操作和FIELD偽操作配合 定義的內存表有3種:基于絕對地址的內存表,基于相對地址的內存表和基于PC的內存表。
舉例:基于絕對地址的內存表
用偽操作序列定義一個內存表,其首地址為固定的地址8192(0X2000),該內存表中包括5個數據域。
Consta長度為4個字節;constb長為4個字節,x長為8字節;y長為8字節;string長為16字節。這種內存表成為基于絕對地址的內存表。
MAP 8192 ; //內存表的首地址8192(0x2000)
Consta FIELD 4 ; //consta 長為4字節,相對位置為0
Constb FIELD 4; //constb長為4字節,相對位置為4
X FIELD 8; // X長為8字節,相對位置為8
Y FIELD 8; // y長為8字節,相對位置為16
String FIELD 16 ;// String為16字節,相對位置為24
在指令中,可以這樣引用內存表中的數據域;
LDR R0,consta; //將consta地址處對應內存加載到R0上面的指令僅僅可以訪問LDR指令前后4KB地址范圍的數據域。
舉例:相對絕對地址的內存表
下面的偽操作序列定義一個內存表,其首地址為0與R9寄存器值得和,該內存表中包含5個數據域。這種表稱為相對地址的內存表。
MAP 0,R9;//內存表的首地址寄存器R9的值
Consta FIELD 4 ; //consta 長為4字節,相對位置為0
Constb FIELD 4; //constb長為4字節,相對位置為4
X FIELD 8; // X長為8字節,相對位置為8
Y FIELD 8; // y長為8字節,相對位置為16
String FIELD 16;// String為16字節,相對位置為24
可以通過下面的指令訪問地址范圍超過4KB的數據;
ADR R9, Field ; //偽指令
LDR R5,Constb;//相當于LDR R5,[R9,#4]
在這里,內存表中的數據都是相對于R9寄存器的內容,而不是相對于一個固定的地址。通過在LDR中指定不同的基址寄存器的值,定義的內存表結構可以在程序中有多個實例。可多次使用LDR指令,用以實現不同的程序實例。
舉例:基于PC的內存表
Data SPACE 100 ; //分配100字節的內存單元,并初始化為0
MAP Data;//內存表的首地址為Datastruc內存單元
Consta FIELD 4 ; //consta 長為4字節,相對位置為0
Constb FIELD 4; //constb長為4字節,相對位置為4
X FIELD 8; // X長為8字節,相對位置為8
Y FIELD 8; // y長為8字節,相對位置為16
String FIELD 16;// String為16字節,相對位置為24
可以通過下面的指令訪問范圍不超過4kb的數據;
LDR R5,constb ;相當于 LDR R5,[PC,offset]