匯編語言程序的結(jié)構(gòu)1
匯編語言程序的結(jié)構(gòu)2
匯編語言程序的結(jié)構(gòu)3
匯編語言程序的結(jié)構(gòu)4
ARM的匯編語言程序一般由幾個段組成,每個段均由AREA偽操作定義。
段可以分為多種,如代碼段、數(shù)據(jù)段、通用段,每個段又有不同的屬性,如代碼段的默認屬性為READONLY,數(shù)據(jù)段的默認屬性為READWRITE。
本程序定義了兩個段,第一個段為代碼段codesec,它在存儲器中存放用于程序執(zhí)行的代碼以及main函數(shù)的本地字符串;第二個段為數(shù)據(jù)段constdatasec,存放了全局的字符串,由于本程序沒有對數(shù)據(jù)進行寫操作,該數(shù)據(jù)段定義屬性為READONLY。
匯編語言的行構(gòu)成1
格式:
[標簽] 指令/偽操作/偽指令 操作數(shù) [;語句的注釋]
所有的標簽必須在一行的開頭頂格寫,前面不能留空格,后面也不能跟C語言中的標簽一樣加上“:”;
ARM匯編器對標識符的大小寫敏感,書寫標號及指令時字母的大小寫要一致;
注釋使用“;”符號,注釋的內(nèi)容從“;”開始到該行的結(jié)尾結(jié)束
匯編語言的行構(gòu)成2
標簽
標簽是一個符號,可以代表指令的地址、變量、數(shù)據(jù)的地址和常量。
一般以字母開頭,由字母、數(shù)字、下劃線組成。
當符號代表地址時又稱標號,可以以數(shù)字開頭,其作用范圍為當前段或者在下一個ROUT偽操作之前。
指令/偽操作
指令/偽操作是指令的助記符或者定義符,它告訴ARM的處理器應(yīng)該執(zhí)行什么樣的操作或者告訴匯編程序偽指令語句的偽操作功能。
匯編語言的標號1
標號代表地址。
標號分為段內(nèi)標號和段外標號。段內(nèi)標號的地址值在匯編時確定,段外編號的地址值在鏈接時確定 。
在程序段中,標號代表其所在位置與段首地址的偏移量。根據(jù)程序計數(shù)器(PC)和偏移量計算地址即程序相對尋址。
在映像中定義的標號代表標號到映像首地址的偏移量。映像的首地址通常被賦予一個寄存器,根據(jù)該寄存器值與偏移量計算地址即寄存器相對尋址。
例如:
loop SUBS r0,r0,#1 ;每次循環(huán)使r0=r0-1
BNE loop ;跳轉(zhuǎn)到loop 標號去執(zhí)行
匯編語言的標號2
在宏中也可以使用局部符號。
局部標號是0~99的十進位數(shù)開始,可以重復(fù)定義。
局部標號引用格式:
%{F|B}{A|T} N{routname}
% :局部標號引用操作。
F :編譯器只向前搜索。
B :編譯器只向后搜索。
A :編譯器搜索宏的所有嵌套層次。
T :編譯器搜索宏的當前層。
例如:
01 SUBS r0,r0,#1 ;每次循環(huán)使r0=r0-1
BNE %B01 ;跳轉(zhuǎn)到01標號去執(zhí)行
匯編語言的常量
常量:其值在程序運行過程中不能被改變的量。
(1)數(shù)字常量:數(shù)字常量有3種表示方式:
十進制數(shù),如1、2、123
十六進制數(shù),如 0x123,0xabc
n進制數(shù),形式為n_XXX,n的范圍是2到9,XXX是具體數(shù)字
(2)字符常量:由單引號及中間的字符組成,包括C語言中的轉(zhuǎn)義字符,如’a’,’\n’
(3)字符串常量:由一對雙引號及中間的字符串表示,中間也可以使用C語言中的轉(zhuǎn)義字符,比如:“abcdef\0xa\r\n”
(4)邏輯常量:{TRUE},{FALSE},注意帶大括號
匯編程序的變量代換1
這里所說的變量,是相對于匯編程序的“變量”,是用于匯編程序進行處理的,但一旦編譯到程序中,則不會改變,成為常量。
在字符串變量的前面有一個$字符,在匯編時編譯器將用該字符串變量的內(nèi)容代替該串變量。
在數(shù)字變量前面有一個代換操作符“$”,編譯器會將該數(shù)字變量的值轉(zhuǎn)換為十六進制的字符串,并用該十六進制的字符串代換“$”后的數(shù)字變量。
需要將“$”字符加入到字符串中,可以用“$$”代替,此時編譯器將不再進行變量代換,而是把“$$”看作一個“$”。
在兩個“|”之間的“$”并不進行變量的代換,但如果“|”在雙引號內(nèi),則將進行變量代換。
使用“.”來表示字符串中變量名的結(jié)束。
匯編程序的變量代換2
字符串“aaa str1$str1. l1$l1,a1$num1.ccc”中的3個變量將在編譯時被替換。
程序運行后看到下面結(jié)果:
aaa str1:bbb l1:T,a1:0000004Fccc
偽指令
在ARM匯編語言源程序中有些特殊助記符,它們沒有相對應(yīng)的操作碼或者機器碼,通常稱為偽指令,它們所完成的操作稱為偽操作。
偽指令在源程序中的作用是為完成匯編程序作各種準備工作的,由匯編程序在源程序的匯編期間進行處理,僅在匯編過程中起作用。
在ARM的匯編程序中,有如下幾種偽指令:
符號定義偽指令
數(shù)據(jù)定義偽指令
匯編控制偽指令
信息報告?zhèn)沃噶?
宏指令以及其他偽指令
符號定義偽指令
作用:用于定義ARM匯編程序中的變量、對變量賦值以及定義寄存器的別名等。
符號定義有如下幾種偽指令:
用于定義局部變量的LCLA、LCLL和LCLS。
用于定義全局變量的GBLA、GBLL和GBLS。
用于對變量賦值的SETA、SETL和SETS。
為通用寄存器列表定義名稱的RLIST。
符號定義偽指令1-1
(1)LCLA、LCLL和LCLS
格式:
LCLA/LCLL/LCLS 局部變量名
說明:LCLA、LCLL和LCLS偽指令用于定義一個匯編程序中的局部變量并初始化。
其中:
LCLA定義一個局部的數(shù)字變量,初始化為0。
LCLL定義一個局部的邏輯變量,初始化為F。
LCLS定義一個局部的字符串變量,初始化為空串。
這3條偽指令用于聲明局部變量,在其局部作用范圍內(nèi)變量名必須惟一,例如在宏內(nèi)。
符號定義偽指令1-2
符號定義偽指令2-1
2)GBLA、GBLL和GBLS
格式:
GBLA/GBLL/GBLS 變量名
說明:GBLA、GBLL和GBLS偽操作定義一個匯編程序中的全局變量并初始化。
其中:
GBLA定義一個全局數(shù)字變量,并初始化為0。
GBLL定義一個全局邏輯變量,并初始化為F。
GBLS定義一個全局字符串變量,并初始化為空串。
這3條偽指令用于定義全局變量,因此在整個程序范圍內(nèi)變量名必須惟一。
符號定義偽指令2-2
例如:
GBLA num1 ;定義一個全局的數(shù)字變
;量,變量名為num1
num1 SETA 0xabcd;將該變量賦值為0xabcd
GBLL l2 ;定義一個全局的邏輯變
;量,變量名為l2
l2 SETL {FALSE} ;將該變量賦值為假
GBLS str3 ;定義一個全局的字符串變
;量,變量名為str3
str3 SETS "Hello!" ;將該變量賦值為“Hello!”
符號定義偽指令3-1
(3)SETA、SETL和SETS
格式:
變量名 SETA/SETL/SETS 表達式
說明:
SETA:給一個數(shù)字變量賦值。
SETL:給一個邏輯變量賦值。
SETS:給一個字符串變量賦值。
格式中的變量名必須為已經(jīng)定義過的全局或局部變量,表達式為將要賦給變量的值。
符號定義偽指令3-2
例如:
LCLA num1 ;定義一個局部的數(shù)字
;變量,變量名為num1
num1 SETA 0x1234 ;將該變量賦值
;為0x1234
LCLS str3 ;定義一個局部的字符串變
;量,變量名為str3
str3 SETS “Hello!” ;將該變量賦值為
;“Hello!”
符號定義偽指令4
4)RLIST
格式:
名稱 RLIST {寄存器列表}
說明:RLIST可用于對一個通用寄存器列表定義名稱,該名稱可在ARM指令LDM/ STM中使用。在LDM/STM指令中,列表中的寄存器為根據(jù)寄存器的編號由低到高訪問次序,與列表中的寄存器排列次序無關(guān)。
例如:
pblock RLIST {R0-R3,R7,R5,R9}
;將寄存器列表名稱定義為pblock,可在ARM指令
;LDM/STM中通過該名稱訪問寄存器列表
數(shù)據(jù)定義偽指令
作用:為數(shù)據(jù)分配存儲單元,同時初始化。
有如下幾種:
DCB 字節(jié)分配
DCW/DCWU 半字(2字節(jié))分配
DCD/DCDU 字(4字節(jié))分配
DCQ/DCQU 8個字節(jié)分配
DCFS/DCFSU 單精度浮點數(shù)分配
DCFD/DCFDU 雙精度浮點數(shù)分配
SPACE 分配一塊連續(xù)的存儲單元
FIELD 定義一個結(jié)構(gòu)化的內(nèi)存表的數(shù)據(jù)域
MAP 定義一個結(jié)構(gòu)化的內(nèi)存表首地址
數(shù)據(jù)定義偽指令1
(1)DCB
格式:
標號 DCB 表達式
說明:分配一塊字節(jié)單元并用偽指令中指定的表達式進行初始化。
表達式可以為使用雙引號的字符串或0~255的數(shù)字。
DCB可用“=”代替。
例如:
Array1 DCB 1,2,3,4,5 ;數(shù)組
str1 DCB "Your are welcome!"
;構(gòu)造字符串并分配空間
數(shù)據(jù)定義偽指令2
(2)DCW/DCWU
格式:
標號 DCW/DCWU 表達式
說明: DCW分配一段半字存儲單元并用表達式值初始化,它定義的存儲空間是半字對齊的。
DCWU功能與DCW類似,只是分配的字存儲單元不嚴格半字對齊。
例如:
Arrayw1 DCW 0xa,-0xb,0xc,-0xd
;構(gòu)造固定數(shù)組并分配半字存儲單元
數(shù)據(jù)定義偽指令3
(3)DCD/DCDU
格式:
標號 DCD/DCDU 表達式
說明:DCD偽指令用于分配一塊字存儲單元并用偽指令中指定的表達式初始化,它定義的存儲空間是字對齊的。
DCD也可用“&”代替。
DCDU功能與DCD類似,只是分配的存儲單元不嚴格字對齊。
例如:
Arrayd1 DCD 1334,234,345435
;構(gòu)造固定數(shù)組并分配字為單元的存儲單元
Label DCD str1;該字單元存放str1的地址
數(shù)據(jù)定義偽指令4
(4)DCQ/DCQU
格式:
標號 DCQ/DCQU 表達式
說明:DCQ用于分配一塊以8個字節(jié)為單位的存儲區(qū)域并用偽指令中指定的表達式初始化,它定義的存儲空間是字對齊的。
DCQU功能與DCQ類似,只是分配的存儲單元不嚴格字對齊。
例如:
Arrayd1 DCQ 234234,98765541
;構(gòu)造固定數(shù)組并分配字為單元的存儲空間。
;注意:DCQ不能給字符串分配空間
數(shù)據(jù)定義偽指令5
(5)DCFD/DCFDU
格式:
標號 DCFD/DCFDU 表達式
說明:DCFD用于為雙精度的浮點數(shù)分配一片連續(xù)的字存儲單元并用偽指令中指定的表達式初始化,它定義的存儲空間是字對齊的。
每個雙精度的浮點數(shù)占據(jù)兩個字單元。
DCFDU功能與DCFD類似,只是分配的存儲單元不嚴格字對齊。
例如:
Arrayf1 DCFD 6E2
Arrayf2 DCFD 1.23,1.45
數(shù)據(jù)定義偽指令6
(6)DCFS/DCFSU
格式:
標號 DCFS/DCFSU 表達式
說明:DCFS用于為單精度的浮點數(shù)分配一片連續(xù)的字存儲單元并用表達式初始化,它定義的存儲空間是字對齊的。
每個單精度浮點數(shù)使用一個字單元。
DCFSU功能與DCFS類似,只是分配的存儲單元不嚴格字對齊。
例如:
Arrayf1 DCFS 6E2 ,-9E-2,-.3
Arrayf2 DCFSU 1.23,6.8E9
數(shù)據(jù)定義偽指令7
7)SPACE
格式:
標號 SPACE 表達式
說明:SPACE用于分配一片連續(xù)的存儲區(qū)域并初始化為0,表達式為要分配的字節(jié)數(shù)。
SPACE也可用“%”代替。
例如:
freespace SPACE 1000
;分配1000字節(jié)的存儲空間
數(shù)據(jù)定義偽指令8
(8)MAP
格式:
MAP 表達式 [,基址寄存器]
說明:MAP定義一個結(jié)構(gòu)化的內(nèi)存表的首地址。此時,內(nèi)存表的位置計數(shù)器{VAR}(匯編器的內(nèi)置變量)設(shè)置成該地址值。
“^”可以用來代替MAP。
表達式可以為程序中的標號或數(shù)學(xué)表達式,基址寄存器為可選項,當基址寄存器選項不存在時,表達式的值即為內(nèi)存表的首地址,當該選項存在時,內(nèi)存表的首地址為表達式的值與基址寄存器的和。
MAP可以與FIELD偽操作配合使用來定義結(jié)構(gòu)化的內(nèi)存表。
例如:
MAP 0x130,R2 ;內(nèi)存表首地址為0x130+R2
數(shù)據(jù)定義偽指令9
(9)FIELD
格式: 標號 FIELD 字節(jié)數(shù)
說明:FIELD用于定義一個結(jié)構(gòu)化內(nèi)存表中的數(shù)據(jù)域。
“#”可用來代替FIELD。
FIELD常與MAP配合使用來定義結(jié)構(gòu)化的內(nèi)存表:FIELD偽指令定義內(nèi)存表中的各個數(shù)據(jù)域,MAP則定義內(nèi)存表的首地址,并為每個數(shù)據(jù)域指定一個標號以供其他的指令引用。
需要注意的是MAP和FIELD偽指令僅用于定義數(shù)據(jù)結(jié)構(gòu),并不分配存儲單元。
例如:
MAP 0xF10000
;定義結(jié)構(gòu)化內(nèi)存表首地址為0xF10000
count FIELD 4
;定義count的長度為4字節(jié),位置為0xF1000+0
x FIELD 4
;定義x的長度為4字節(jié),位置為0xF1004
y FIELD 4
;定義y的長度為4字節(jié),位置為0xF1008
匯編控制偽指令
作用:指引匯編程序的執(zhí)行流程。
常用的偽操作包括:
(1)MACRO和MEND:宏定義的開始與結(jié)束。
(2)IF、ELSE和ENDIF:根據(jù)邏輯表達式的成立與否決定是否在編譯時加入某個指令序列。
(3)WHILE和WEND:根據(jù)邏輯表達式的成立與否決定是否循環(huán)執(zhí)行這個代碼段。
(4)MEXIT:從宏中退出。
MACRO和MEND
格式
MACRO
[$標號] 宏名 [$參數(shù)1,$參數(shù)2,……]
指令序列
MEND
其中,$標號在宏指令被展開時,標號可被替換成相應(yīng)的符號(在一個符號前使用$,表示程序在匯編時將使用相應(yīng)的值來替代$后的符號), $參數(shù)1為宏指令的參數(shù),當宏指令被展開時將被替換成相應(yīng)的值,類似于函數(shù)中的形式參數(shù)。
宏指令可以重復(fù)使用,與子程序有些類似,子程序可以節(jié)省存儲空間,提供模塊化的程序設(shè)計。但是使用子程序結(jié)構(gòu)時需要保存/恢復(fù)現(xiàn)場,從而增加了系統(tǒng)的開銷。
使用說明:在子程序比較短而需要傳遞的參數(shù)比較多的情況下,可使用宏匯編技術(shù)。
宏定義偽指令
例子
MACRO ;宏定義開始
$label jump $a1,$a2 ;宏的名稱為jump,有2個參數(shù)a1和a2
$label.loop1 ; $label.loop1 為宏體的內(nèi)部標號
…
BGE $label.loop1
$label.loop2
BL $a1 ;參數(shù)$a1為一個子程序的名稱
BGT $label.loop2
…
ADR $a2
…
MEND ;宏定義結(jié)束
宏定義偽指令
在程序中調(diào)用該宏
exam jump sub,det ;調(diào)用宏jump,宏的標號為exam, 參數(shù)1為sub,參數(shù)2為det
程序被匯編后,宏的展開結(jié)果:
…
examloop1
…
BGE examloop1
examloop2
BL sub
BGT examloop2
ADR det
IF、ELSE和ENDIF
格式:
IF 邏輯表達式
代碼段1
ELSE
代碼段2
ENDIF
說明:能根據(jù)邏輯表達式的成立與否決定是否在編譯時加入某個指令序列。 IF、ELSE和ENDIF分別可以用“[”,“|”,“]”代替。如果IF后面的邏輯表達式為真,則編譯代碼段1,否則編譯代碼段2。ELSE及代碼段2也可以沒有,這時,當IF后面的邏輯表達式為真時,則代碼段1,否則繼續(xù)編譯后面的指令。
WHILE和WEND
格式:
WHILE 邏輯表達式
代碼段
WEND
說明: WHILE和WEND偽指令能根據(jù)邏輯表達式的成立與否決定是否循環(huán)執(zhí)行這個代碼段。當WHILE后面的邏輯表達式為真時,則執(zhí)行代碼段,該代碼段執(zhí)行完畢后,再判斷邏輯表達式的值,若為真則繼續(xù)執(zhí)行,一直到邏輯表達式的值為假。
例如:
GBLA num ;聲明全局的數(shù)字變量num
num SETA 9 ;由num控制循環(huán)次數(shù)
…
WHILE num>0
sub r0,r0,1
add r1,r1,1
WEND
其他偽指令
在匯編程序中經(jīng)常會使用一些其他的偽指令,包括以下18條:
ASSERT AREA
ALIGN CODE16/CODE32
ENTRY END
EQU IMPORT
EXPORT/GLOBAL EXTERN
INCBIN GET/INCLUDE
RN ROUT
ADR ADRL
LDR NOP
其他偽指令1
(1)ASSERT
格式:
ASSERT 邏輯表達式
說明:ASSERT用來表示程序的編譯必須滿足一定的條件,如果邏輯表達式不滿足,則編譯器會報錯,并終止匯編。
例如:
ASSERT ver>7 ;保證ver>7
其他偽指令2
2)AREA
格式: AREA 段名 屬性,……
說明:AREA用于定義一個代碼段、數(shù)據(jù)段或者特定屬性的段。如果段名以數(shù)字開頭,那么該段名需用“|”字符括起來,如|7wolf|,用C的編譯器產(chǎn)生的代碼一般也用“|”括 起來。
屬性部分表示該代碼段/數(shù)據(jù)段的相關(guān)屬性,多個屬性可以用“,”分隔。
常見屬性如下:
① DATA:定義數(shù)據(jù)段,默認屬性是READWRITE。
② CODE:定義代碼段,默認屬性是READONLY 。
③ READONLY:表示本段為只讀。
④ READWRITE:表示本段可讀寫。
⑤ ALIGN=表達式,表示段的對齊方式為2的表達式次方,例如:表達式=3,則對齊方式為8字節(jié)對齊。表達式的取值范圍為0~31。
⑥ COMMON屬性:定義一個通用段,這個段不包含用戶代碼和數(shù)據(jù)。
其他偽指令3
(3)ALIGN
格式:
ALIGN [表達式[,偏移量]]
說明:ALIGN偽操作可以通過填充字節(jié)使當前的位置滿足一定的對齊方式。
表達式的值為2的冪,如1、2、4、8、16等,用于指定對齊方式。
如果偽操作中沒有指定表達式,則編譯器會將當前位置對齊到下一個字的位置。偏移量也是個數(shù)字表達式,如果存在偏移量,則當前位置自動對齊到2的表達式值次方+偏移量。
例如:
AREA ||.data||,DATA,READWRITE,ALIGN=2
其他偽指令4
(4)CODE16/CODE32
格式: CODE16/CODE32
說明:CODE16偽操作指示編譯器后面的代碼為16位的Thumb指令。CODE32偽操作指示編譯器后面的代碼為32位的ARM指令。
如果在匯編源代碼中同時包含Thumb和ARM指令時,可以用“CODE32”通知編譯器后的指令序列為32位的ARM指令,用“CODE16”偽指令通知編譯器后的指令序列為16位的Thumb指令。
CODE16/CODE32不能對處理器進行狀態(tài)的切換。
例如:
CODE32 ; 32位的ARM指令
AREA ||.text||,CODE,READONLY
…
LDR R0,=0x8500;
BX R0 ;程序跳轉(zhuǎn),并將處理器切換到Thumb狀態(tài)
…
CODE16 ;16位的Thumb指令
ADD R3,R3,1
END ;源文件結(jié)束
其他偽指令5-1
(5)ENTRY
格式:
ENTRY
說明:ENTRY用于指定匯編程序的入口。
在一個完整的匯編程序中至少要有一個ENTRY,程序中也可以有多個,此時,程序的真正入口點可在鏈接時指定,但在一個源文件里最多只能有一個ENTRY或者沒有ENTRY。
其他偽指令5-2
其他偽指令6
(6)END
格式:
END
說明:END告訴編譯器已經(jīng)到了源程序的結(jié)尾。
例如:
AREA constdata,DATA,READONLY
…
END ;結(jié)尾
其他偽指令7
(7)EQU
格式: 名稱 EQU 表達式 [,類型]
說明:EQU用于將程序中的數(shù)字常量、標號、基于寄存器的值賦予一個等效的名稱,這一點類似于C語言中的#define.
可用“*”代替EQU。
如果表達式為32位的常量,我們可以指定表達式的數(shù)據(jù)類型,類型域可以有以下3種:CODE16/CODE32/DATA
例如:
num1 EQU 1234 ;定義num1為1234
addr5 EQU str1+0x50
d1 EQU 0x2400,CODE32 ;定義d1的為0x2400,且該處為32位的ARM指令
其他偽指令8
(8)EXPORT/GLOBAL
格式:
EXPORT/GLOBAL 標號 [,WEAK]
說明:EXPORT在程序中聲明一個全局標號,其他文件中的代碼可以被該標號引用。用戶也可以用GLOBAL代替EXPORT。
[,WEAK]可選項聲明其他文件有同名的標號,則該同名標號優(yōu)先于該標號被引用。
例如:
AREA ||.text||,CODE,READONLY
main PROC
…
ENDP
EXPORT main ;聲明一個可全局引用的函數(shù)main
…
END
其他偽指令9
(9)IMPORT
格式:
IMPORT 標號 [,WEAK]
說明:告訴編譯器,這個標號要在當前源文件中使用,但標號是在其他的源文件中定義的。
[,WEAK]:如果所有的源文件都沒有找到這個標號的定義,編譯器也不會提示錯誤信息,同時編譯器也不會到當前沒有被INCLUDE進來的庫中去查找該符號。
例如:
AREA mycode,CODE,READONLY
IMPORT _ printf
;通知編譯器當前文件要引用函數(shù)_ printf
…
END
IMPORT
使用說明
使用IMPORT為操作聲明一個符號是在其他源文件中定義的。如果鏈接器在鏈接處理時不能解析該符號,而且IMPORT為操作中沒有指定[WEAK]選項,則鏈接器將會報告錯誤。如果鏈接器在鏈接處理時不能解析該符號,而IMPORT偽操作中指定了[WEAK]選項,則鏈接器不會報告錯誤,而是進行下面的操作:
如果該符號被B或BL指令引用,則該符號被設(shè)置成下一條指令的地址,該B或者BL指令相當于一條NOP指令。例如“B sign ”,“sign”不能被解析,則該指令被忽略為NOP指令,繼續(xù)執(zhí)行下面的指令,也就是將sign理解為下一條指令的地址。
其他情況下該符號被設(shè)置為0。
其他偽指令10
10)EXTERN
格式:
EXTERN 標號 [,WEAK]
說明:告訴編譯器,標號要在當前源文件中引用,但是該標號是在其他的源文件中定義的。
與IMPORT不同的是,如果當前源文件實際上沒有引用該標號,該標號就不會被加入到當前文件的符號表中。
[,WEAK]:即使所有的源文件都沒有找到這個標號的定義,編譯器也不給出錯誤信息。
例如:
AREA ||.text||,CODE,READONLY
…
EXTERN _ printf,WEAK ;告訴編譯器當前文件要引用標號,如果找不到,則不提示錯誤
…
END
其他偽指令11
(11)GET/INCLUDE
格式: GET 文件名
說明:GET將一個源文件包含到當前的源文件中,并將被包含的源文件在當前位置展開進行匯編處理。
INCLUDE和GET的作用是等效的。
使用方法:在某源文件中定義一些宏指令,用MAP和FIELD定義結(jié)構(gòu)化的數(shù)據(jù)類型,用EQU定義常量的符號名稱,然后用GET/INCLUDE將這個源文件包含到其他的源文件中。
使用方法與C語言中的“#include”相似。
GET/INCLUDE只能用于包含源文件,包含其他文件則需要使用INCBIN偽指令。
例如:
AREA mycode,DATA,READONLY
GET E:\code\prog1.s ;通知編譯器在當前源文件包含源文件E:\code\ prog1.s
GET prog2.s ;通知編譯器當前源文件包含可搜索目錄下的prog2.s
…
END
其他偽指令12
(12)INCBIN
格式:
INCBIN 文件名
說明:INCBIN將一個數(shù)據(jù)文件或者目標文件包含到當前的源文件中,編譯時被包含的文件不作任何變動地存放在當前文件中,編譯器從后面開始繼續(xù)處理。
例如:
AREA constdata,DATA,READONLY
INCBIN data1.dat ;源文件包含文件data1.dat
INCBIN E:\DATA\data2.bin
;源文件包含文件E:\DATA\data2.bin
…
END
其他偽指令13
(13)RN
格式:
名稱 RN 表達式
說明:RN用于給一個寄存器定義一個別名,以便程序員記憶該寄存器的功能。
名稱為給寄存器定義的別名,表達式為寄存器的編碼。
例如:
count RN R1 ;給R1定義一個別名count
其他偽指令14
(14)ROUT
格式:
[名稱] ROUT
說明:ROUT可以給一個局部變量定義作用范圍。
在程序中未使用該偽指令時,局部變量的作用范圍為所在的AREA,而使用ROUT后,局部變量的作用范圍為當前ROUT和下一個ROUT之間。
例如:
routine ROUT ;定義局部標號的有效范圍
…
1 routine ;routine內(nèi)的局部標號1
…
BEQ %1 routine ;若條件成立,則跳轉(zhuǎn)到routine范圍內(nèi)的局部標號1
…
Otherroutine ROUT ;定義新的局部標號的有效范圍
其他偽指令14
(15)LTORG
說明:LTORG用于聲明一個數(shù)據(jù)緩沖池(literal pool)的開始。通常放在無條件跳轉(zhuǎn)指令之后,或者子程序返回指令之后,以免處理器錯誤地將數(shù)據(jù)緩沖池中地數(shù)據(jù)作為指令來執(zhí)行。
例如:
Func1
……
MOV PC, LR
LTORG
DATA SPACE 26;從data標號開始預(yù)留256字節(jié)地內(nèi)存單元
END
其他偽指令15
(16)ADR 小范圍地址讀取
格式:
ADR{<cond>} <Rd>,< expr>;
說明:將基于PC相對偏移的地址值或基于寄存器相對偏移的地址值(expr 地址表達式)讀取到目標寄存器Rd中。
當?shù)刂分凳欠亲謱R時,取值范圍在-255~255字節(jié)之間;
當?shù)刂分凳亲謱R時,取值范圍在-1 020~1 020字節(jié)之間。
在匯編編譯源程序時,ADR偽指令被編譯器替換成一條合適的指令。
通常,編譯器用一條ADD指令或SUB指令來實現(xiàn)該ADR偽指令的功能。若不能用一條指令實現(xiàn),則產(chǎn)生錯誤,編譯失敗。
對于基于PC相對偏移的地址值時,給定范圍是相對當前指令地址后兩個字處(因為ARM7TDMI為三級流水線)。
可以用ADR加載地址實現(xiàn)查表。
例如:
LOOP MOV R1,#0xF0
ADR R2,LOOP ;將LOOP的地址放入R2,因為PC值為當前指令地址值加8字節(jié),所以本 ADR偽指令將被編譯器換成“SUB R2,PC,0XC”
其他偽指令16
(17)ADRL 中等范圍地址讀取
格式:
ADRL{<cond>} <Rd>,< expr>;
說明:類似于ADR, 但比ADR讀取更大范圍的地址。
當?shù)刂分凳欠亲謱R時,取值范圍在-64KB~64 KB之間;
地址值是字對齊時,取值范圍在-256KB~256 KB之間。
在匯編編譯源程序時,ADRL偽指令被編譯器替換成兩條合適的指令。
若不能用兩條指令實現(xiàn)ADRL偽指令功能,則產(chǎn)生錯誤,編譯失敗。
可以用ADRL加載地址,實現(xiàn)程序跳轉(zhuǎn)。
例如:
ADRL R0,DATA_BUF
ADRL R1,DATA_BUF+80
DATA_BUF
SPACE 100 ;定義100字節(jié)緩沖區(qū)
其他偽指令17
(18)LDR 大范圍地址讀取
格式:
LDR{<cond>} <Rd>,< =expr/label-expr >;
說明:加載32位的立即數(shù)或一個地址值到目標寄存器Rd。
在匯編編譯源程序時,LDR偽指令被編譯器替換成一條合適的指令。
若加載的常數(shù)未超出MOV或MVN的范圍,則使用MOV或MVN指令代替該LDR偽指令;否則匯編器將常量放入文字池,并使用一條程序相對偏移的LDR指令從文字池讀出常量。
LDR用于加載芯片外圍功能部件的寄存器地址(32位立即數(shù)),以實現(xiàn)各種控制操作。從PC到文字池的偏移量必須小于4 KB。
與ARM指令的LDR相比,偽指令的LDR的參數(shù)有“=”符號。
例如:
LDR R0,=0x12345678 ;加載32位立即數(shù)0x12345678
LDR R0,=DATA_BUF+60 ;加載DATA_BUF地址+60
…
LTORG ;聲明文字池
其他偽指令18
(19)NOP 空操作
格式:
NOP ;
說明:不產(chǎn)生任何有意義的操作,只是占用一個機器時間。
NOP偽指令在匯編時將會被替代成ARM中的空操作,比如可能為“MOV R0,R0”指令等。
簡單的ARM匯編程序設(shè)計(一)
查表和散轉(zhuǎn)程序設(shè)計
當涉及到數(shù)據(jù)串或者跳轉(zhuǎn)表格時,常常需要通過地址對他們進行訪問,通常有兩種方法裝載地址:
通過ADR和ADRL偽指令直接裝載地址;
通過偽指令LDR Rd,=label從數(shù)據(jù)池中裝載地址。
下面以程序jump.s為例,介紹通過ADR偽指令裝載地址地散轉(zhuǎn)程序地設(shè)計。
查表和散轉(zhuǎn)程序設(shè)計
主程序中設(shè)置了3個參數(shù),arithfunc根據(jù)3個參數(shù)返回一個R0值。
當R0=0時,R0:=R1+R2;
當R0=1時,R0:=R1-R2;
簡單的ARM匯編程序設(shè)計(一) 查表和散轉(zhuǎn)程序設(shè)計
AREA Jump, CODE, READONLY ; name this block of code
CODE32 ; Following code is ARM code
num EQU 2 ; 跳轉(zhuǎn)表的入口數(shù)目
ENTRY ; 程序入口
start
MOV r0, #0 ; 設(shè)置3個參數(shù)
MOV r1, #3
MOV r2, #2
BL arithfunc ; 調(diào)用子程序
Stop ; 執(zhí)行中止
MOV r0, #0x18 ;軟中斷參數(shù)設(shè)置
LDR r1, =0x20026 ;軟中斷參數(shù)設(shè)置
SWI 0x123456 ; 將CPU的控制權(quán)交給調(diào)試器ARM ;semihosting SWI
查表和散轉(zhuǎn)程序設(shè)計——續(xù)
arithfunc ;
CMP r0, #num ; 比較參數(shù)
MOVHS pc, lr ; 若超出范圍則程序返回
ADR r3, JumpTable ; 裝載跳轉(zhuǎn)表格標號地址
LDR pc, [r3,r0,LSL#2] ; 跳轉(zhuǎn)到相應(yīng)子程序入口地址處
JumpTable
DCD DoAdd
DCD DoSub
DoAdd
ADD r0, r1, r2 ; =0時的操作
MOV pc, lr ; 返回
DoSub
SUB r0, r1, r2 ; =1時的操作
MOV pc,lr ; 返回
END ; 程序結(jié)尾
軟中斷指令 SWI
Semihosting 在ADS的C語言函數(shù)庫中,某些ANSIC的功能是由主機的調(diào)試環(huán)境來提供的,這套機制有一個專門術(shù)語叫Semihosting。
Semihosting通過一組軟件中斷(SWI)指令來實現(xiàn)。
當一個Semihosting軟中斷被執(zhí)行時,調(diào)試系統(tǒng)先識別這個SWI請求,然后掛起正在運行的程序,調(diào)用Semihosting的服務(wù),完成后再恢復(fù)原來的程序執(zhí)行。
因此,主機執(zhí)行的任務(wù)對于程序來說是透明的。
SWI傳遞的功能號
(例如: semi-hosting, 使用0x123456 (ARM) or 0xAB (Thumb)
續(xù)
在此例中,表格jumptable中存放地是子程序地入口地址,我們把這種表格稱為跳轉(zhuǎn)表格。
注意指令LDR PC,[R3,R0,LSL #2],執(zhí)行地操作為PC=R3+R0×4,因為表格中存放地地址為4字節(jié)地址,所以要將R0乘以4得出偏移量,再加上表格首地址,得出子程序地入口地址賦值給PC。
字符串拷貝程序設(shè)計
下面的例子為用ARM指令編寫的串拷貝的例子。
兩個數(shù)據(jù)串都放在數(shù)據(jù)段中,且用DCB偽指令定義,DCB為定義1字節(jié)或多字節(jié)內(nèi)存空間,雙引號中的字符串在內(nèi)存中是順序存放的,因此取數(shù)/存數(shù)時需要使用LDRB和STRB指令;
若數(shù)據(jù)串是用DCD存放的,則應(yīng)使用LDR和STR指令。
另外,例子中采用的LDRB/STRB 指令是后索引尋址方式,即尋址完成后更新地址。
字符串拷貝程序設(shè)計(用LDR和STR實現(xiàn))
AREA StrCopy, CODE, READONLY
ENTRY ; 程序入口
start
LDR r1, =srcstr ; 初始串的指針
LDR r0, =dststr ; 結(jié)果串的指針
BL strcopy ; 調(diào)用子程序執(zhí)行復(fù)制
stop
MOV r0, #0x18 ; 執(zhí)行中止
LDR r1, =0x20026 ;
SWI 0x123456 ;
字符串拷貝程序設(shè)計(用LDR和STR實現(xiàn))——續(xù)
strcopy
LDRB r2, [r1],#1 ; 加載并且更新源串指針
STRB r2, [r0],#1 ; 存儲且更新目的串指針;
CMP r2, #0 ; 是否為0
BNE strcopy ;
MOV pc,lr ;
AREA Strings, DATA, READWRITE
srcstr DCB "First string - source",0
dststr DCB "Second string - destination",0
END
字符串拷貝程序設(shè)計
數(shù)據(jù)串拷貝時,若使用LDM和STM則可增加程序的效率??紤]到ARM的寄存器,一次采用8個寄存器進行傳輸比較合適,
通過指令: MOVS r3,r2, LSR #3 來計算需要幾輪8位數(shù)據(jù)傳送,剩余的數(shù)據(jù)個數(shù)可以通過指令A(yù)NDS r2, r2, #7 獲得,再對其進行按字傳輸即可。
字符串拷貝程序設(shè)計(用LDM和STM實現(xiàn))
AREA Block, CODE, READONLY ; 命名
num EQU 20 ; 設(shè)置被拷貝的字數(shù)
ENTRY ; 程序入口
start
LDR r0, =src ; r0 = 源串指針
LDR r1, =dst ; r1 = 目的串指針
MOV r2, #num ; r2 = 拷貝字數(shù)
MOV sp, #0x400 ; 設(shè)置堆棧指針 (r13)
blockcopy
MOVS r3,r2, LSR #3 ; 字數(shù)/8
BEQ copywords ; 少于8個字
STMFD sp!, {r4-r11} ; save some working registers
octcopy
LDMIA r0!, {r4-r11} ; 從源串加載8個字
STMIA r1!, {r4-r11} ; 放入目的串
SUBS r3, r3, #1 ; 控制變量減少
BNE octcopy ; ... 繼續(xù)
字符串拷貝程序設(shè)計(用LDM和STM實現(xiàn))——續(xù)
LDMFD sp!, {r4-r11} ;
copywords
ANDS r2, r2, #7 ; 奇數(shù)字被拷貝
BEQ stop ; No words left to copy ?
wordcopy
LDR r3, [r0], #4 ; 從源串加載一個字且指針自增
STR r3, [r1], #4 ; 存儲到目的串
SUBS r2, r2, #1 ; 字控制變量減少
BNE wordcopy ; 繼續(xù)
stop
MOV r0, #0x18 ; 執(zhí)行中止
LDR r1, =0x20026 ;
SWI 0x123456 ;
AREA BlockData, DATA, READWRITE
src DCD 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4
dst DCD 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
END