轉自:https://blog.csdn.net/zb861359/article/details/81027021?utm_source=app
1、 IMPORT和EXPORT
IMPORT ,定義表示這是一個外部變量的標號,不是在本程序定義的
EXPORT ,表示本程序里面用到的變量提供給其他模塊調用的。
以上兩個在匯編和C語言混合編程的時候用到。
2、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 ,屬性為只讀。
3、LDR、LDRB、LDRH
ARM微處理器支持加載/存儲指令用於在寄存器和存儲器之間傳送數據,加載指令用於將存儲器中的數據傳送到寄存器,存儲指令則完成相反的操作。常用的加載存儲指令如下:
— LDR 字數據加載指令
— LDRB 字節數據加載指令
— LDRH 半字數據加載指令
1) LDR指令有兩種用法:
a、ldr加載指令
LDR指令的格式為:
LDR{條件} 目的寄存器,<存儲器地址>
LDR指令用亍從存儲器中將一個32位的字數據傳送到目的寄存器中。該指令通常用於從存儲器中讀取32位的字數據到通用寄存器,然后對數據迕行處理。當程序計數器PC作為目的寄存器時,指令從存儲器中讀取的字數據被當作目的地址,從而可以實現程序流程的跳轉。該指令在程序設計中比較常用,丏尋址方式靈活多樣,請讀者認真掌握。
指令示例:
LDR R0,[R1] ;將存儲器地址為R1的字數據讀入寄存器R0。
LDR R0,[R1,R2] ;將存儲器地址為R1+R2的字數據讀入寄存器R0。
LDR R0,[R1,#8] ;將存儲器地址為R1+8的字數據讀入寄存器R0。
LDR R0,[R1,R2]!;將存儲器地址為R1+R2的字數據讀入寄存器R0,幵將新地址R1+R2寫入R1。
LDR R0,[R1,#8]! ;將存儲器地址為R1+8的字數據讀入寄存器R0,幵將新地址R1+8寫入R1。
LDR R0,[R1],R2 ;將存儲器地址為R1的字數據讀入寄存器R0,幵將新地址R1+R2寫入R1。
LDR R0,[R1,R2,LSL#2]! ;將存儲器地址為R1+R2×4的字數據讀入寄存器R0,並將新地址R1+R2×4寫入R1。
LDR R0,[R1],R2,LSL#2 ;將存儲器地址為R1的字數據讀入寄存器R0,幵將新地址R1+R2×4寫入R1。”
ARM是RISC結構,數據從內存到CPU間的移勱只能通過L/S指令來完成,也就是ldr/str指令。
比如想把數據從內存中某處讀取到寄存器中,只能使用ldr
比如:
ldr r0, 0x12345678
就是把0x12345678返個地址中的值存放到r0中。
b、ldr偽指令
ARM指令集中,LDR通常都是作加載指令的,但是它也可以作偽指令。
LDR偽指令的形式是“LDRRn,=expr”。
例子:
COUNT EQU 0x40003100
……
LDR R1,=COUNT
MOV R0,#0
STR R0,[R1]
COUNT是我們定義的一個變量,地址為0x40003100。這種定義方法在匯編語言中是很常見的,如果使用過單片機的話,應該都熟悉這種用法。
LDR R1,=COUNT是將COUNT這個變量的地址,也就是0x40003100放到R1中。
MOV R0,#0是將立即數0放到R0中。最后一句STR R0,[R1]是一個典型的存儲指令,將R0中的值放到以R1中的值為地址的存儲單元去。實際就是將0放到地址為0x40003100的存儲單元中去。
下面還有一個例子
;將COUNT的值賦給R0
LDR R1,=COUNT
LDR R0,[R1]
LDR R1,=COUNT這條偽指令,是怎樣完成將COUNT的地址賦給R1,有興趣的可以看它編譯后的結果。這條指令實際上會編譯成一條LDR指令和一條DCD偽指令。
2) LDRB指令
LDRB指令的格式為:
LDR{條件}B 目的寄存器,<存儲器地址>
LDRB指令用於將存儲器中低8位的字節數據傳送到目的寄存器中,同時將寄存器的高24位清零,然后對數據進行處理。當程序計數器PC作為目的寄存器時,指令從存儲器中讀取的數據被當作目的地址,從而可以實現程序流程的跳轉。
指令示例:
LDRB R0,[R1] ;將存儲器地址為R1的字節數據讀入寄存器R0,並將R0的高24位清零。
LDRB R0,[R1,#8] ;將存儲器地址為R1+8的字節數據讀入寄存器R0,並將R0的高24位清零。
3) LDRH指令
LDRH指令的格式為:
LDR{條件}H 目的寄存器,<存儲器地址>
LDRH指令用於將存儲器中低16位的半字數據傳送到目的寄存器中,同時將寄存器的高16位清零,然后對數據進行處理。當程序計數器PC作為目的寄存器時,指令從存儲器中讀取的字數據被當作目的地址,從而可以實現程序流程的跳轉。
指令示例:
LDRH R0,[R1] ;將存儲器地址為R1的半字數據讀入寄存器R0,並將R0的高16位清零。
LDRH R0,[R1,#8] ;將存儲器地址為R1+8的半字數據讀入寄存器R0,並將R0的高16位清零。
LDRH R0,[R1,R2] ;將存儲器地址為R1+R2的半字數據讀入寄存器R0,並將R0的高16位清零。
4、SUB
(Subtraction)
SUB{條件}{S} , , dest = op_1 - op_2
SUB 用操作數 one 減去操作數 two,把結果放置到目的寄存器中。操作數 1 是一個寄存器,操作數 2 可以是一個寄存器,被移位的寄存器,或一個立即值:
SUB R0, R1, R2 ;R0 = R1 - R2
SUB R0, R1, #256; R0 = R1 - 256
SUB R0, R2,R3,LSL#1 ; R0 = R2 - (R3 << 1)
減法可以在有符號和無符號數上進行。
ps:帶進位的減法SBC
5、CMP 、TST、BNE、BEQ
BNE和BEQ經常與CMP 或TST搭配使用。
在講解這幾個指令前,先介紹下CPSR這個寄存器,因為CMP和TST指令會對CPSR中的某些位產生影響,而BNE和BEQ指令的動作正是與CPSR中被影響的這些位有關。
CMP:
假設現在AX寄存器中的數是0002H,BX寄存器中的數是0003H。執行的指令是:CMP AX, BX
執行這條指令時,先做用AX中的數減去BX中的數的減法運算。列出二進制運算式子:
0000 0000 0000 0010
-0000 0000 0000 0011
_________________________________
(借位1) 1111 11111111 1111
所以,運算結果是 0FFFFH
根據這個結果,各標志位將會被分別設置成以下值:
CF=1,因為有借位 // CF即為上述CPSR中的C
OF=0,未溢出 // OF即為上述CPSR中的V
SF=1,結果是負數 // SF即為上述CPSR中的N
ZF=0,結果不全是零 // ZF即為上述CPSR中的Z
還有AF, PF等也會相應地被設置。
CMP 比較指令做了減法運算以后,根據運算結果設置了各個標志位。
標志位設置過以后,0FFFFH這個減法運算的結果就沒用了,它被丟棄,不保存。
執行過了CMP指令以后,除了CF,ZF,OF,SF,等各個標志位變化外,其它的數據不變。
對照普通的減法指令 SUB AX, BX,它們的區別就在於:
SUB指令執行過以后,原來AX中的被減數丟了,被換成了減法的結果。
CMP指令執行過以后,被減數、減數都保持原樣不變。
TST:
邏輯處理指令,用於把一個寄存器的內容和另一個寄存器的內容或立即數進行按位與運算,並根據運算結果更新CPSR中條件標志位的值。當前運算結果為非0,則Z=0;當前運算結果為0,則Z=1
BNE:數據跳轉指令,標志寄存器中Z標志位等於零時,跳轉到BNE后標簽處。特別要注意這里,通常我們說BNE是“不相等”或“不為零”時跳轉,此處的“不相等”或“不為零”是指比較結果不相等或做減法不為零,不是指Z標志位。比較結果不相等或做減法不為零時,Z標志位是等於零的。
BEQ:數據跳轉指令,標志寄存器中Z標志位不等於零時, 跳轉到BEQ后標簽處
6、STR、STRB、STRH指令
— STR 字數據存儲指令
— STRB 字節數據存儲指令
— STRH 半字數據存儲指令
A、STR指令的格式為:
STR{條件} 源寄存器,<存儲器地址>
STR指令用於從源寄存器中將一個32位的字數據傳送到存儲器中。該指令在程序設計中比較常用,且尋址方式靈活多樣,使用方式可參考指令LDR。
指令示例:
STR R0,[R1],#8 ;將R0中的字數據寫入以R1為地址的存儲器中,並將新地址R1+8寫入R1。
STR R0,[R1,#8] ;將R0中的字數據寫入以R1+8為地址的存儲器中。
B、STRB指令的格式為:
STR{條件}B 源寄存器,<存儲器地址>
STRB指令用於從源寄存器中將一個8位的字節數據傳送到存儲器中。該字節數據為源寄存器中的低8位。
指令示例:
STRB R0,[R1] ;將寄存器R0中的字節數據寫入以R1為地址的存儲器中。
STRB R0,[R1,#8] ;將寄存器R0中的字節數據寫入以R1+8為地址的存儲器中。
C、STRH指令的格式為:
STR{條件}H 源寄存器,<存儲器地址>
STRH指令用於從源寄存器中將一個16位的半字數據傳送到存儲器中。該半字數據為源寄存器中的低16位。
指令示例:
STRH R0,[R1] ;將寄存器R0中的半字數據寫入以R1為地址的存儲器中。
STRH R0,[R1,#8] ;將寄存器R0中的半字數據寫入以R1+8為地址的存儲器中。
7、跳轉指令B、BL、BX、BLX 和 BXJ的區別
跳轉指令用於實現程序流程的跳轉,在 ARM 程序中有兩種方法可以實現程序流程的跳轉:
(1) 使用專門的跳轉指令。
(2) 直接向程序計數器 PC 寫入跳轉地址值。
通過向程序計數器 PC 寫入跳轉地址值,可以實現在4GB 的地址空間中的任意跳轉,在跳轉之前結合使用
MOV LR , PC
等類似指令,可以保存下一條指令地址作為將來的返回地址值,從而實現在 4GB 連續的線性地址空間的子程序調用。
專門的跳轉指令:
B、BL、BX、BLX 和 BXJ
跳轉、帶鏈接跳轉(帶返回的跳轉)、跳轉並切換指令集、帶鏈接跳轉並切換指令集(帶返回的跳轉並切換指令集)、跳轉並轉換到 Jazelle 狀態。
a) 、 B 指令
B 指令的格式為:
B{條件} 目標地址
B 指令是最簡單的跳轉指令。一旦遇到一個 B 指令,ARM 處理器將立即跳轉到給定的目標地址,從那里繼續執行。注意存儲在跳轉指令中的實際值是相對當前PC 值的一個偏移量,而不是一個絕對地址,它的值由匯編器來計算(參考尋址方式中的相對尋址)。它是 24 位有符號數,左移兩位后有符號擴展為 32 位,表示的有效偏移為 26 位(前后32MB 的地址空間)。以下指令:
B Label ;程序無條件跳轉到標號 Label 處執行
CMP R1 ,# 0 ;當 CPSR 寄存器中的 Z 條件碼置位時,程序跳轉到標號Label 處執行
BEQ Label
b) 、 BL 指令
BL 指令的格式為:
BL{條件} 目標地址
BL 是另一個跳轉指令,但跳轉之前,會在寄存器R14 中保存PC 的當前內容,因此,可以通過將R14 的內容重新加載到PC 中,來返回到跳轉指令之后的那個指令處執行。該指令是實現子程序調用的一個基本但常用的手段。以下指令:
BL Label ;當程序無條件跳轉到標號 Label 處執行時,同時將當前的 PC 值保存到 R14 中
c) 、 BLX 指令
BLX 指令的格式為:
BLX 目標地址
BLX 指令從ARM 指令集跳轉到指令中所指定的目標地址,並將處理器的工作狀態有ARM 狀態切換到Thumb 狀態,該指令同時將PC 的當前內容保存到寄存器R14 中。因此,當子程序使用Thumb 指令集,而調用者使用ARM 指令集時,可以通過BLX 指令實現子程序的調用和處理器工作狀態的切換。
同時,子程序的返回可以通過將寄存器R14 值復制到PC 中來完成。
d) 、 BX 指令
BX 指令的格式為:
BX{條件} 目標地址
BX 指令跳轉到指令中所指定的目標地址,目標地址處的指令既可以是ARM 指令,也可以是Thumb指令。
語法
op1{cond}{.W}<wbr />label
op2{cond} <wbr />Rm
其中:
op1
是下列項之一:
B:跳轉;BL:帶鏈接跳轉;BLX:帶鏈接跳轉並切換指令集。
op2
是下列項之一:
BX:跳轉並切換指令集;BLX:帶鏈接跳轉並切換指令集;
BXJ:跳轉並轉換為Jazelle執行。
cond:是一個可選的條件代碼。 cond 不能用於此指令的所有形式。
.W:是一個可選的指令寬度說明符,用於強制要求在 Thumb-2 中使用 32 位 B 指令。
label:是一個程序相對的表達式。
Rm:是一個寄存器,包含要跳轉到的目標地址。
所有這些指令均會引發跳轉,或跳轉到 label,或跳轉到包含在Rm中的地址處。此外:
BL 和 BLSTMCSIAX 指令可將下一個指令的地址復制到 lr(r14,鏈接寄存器)中。
BX 和 BLX 指令可將處理器的狀態從 ARM 更改為 Thumb,或從 Thumb 更改為 ARM。
BLX label 無論何種情況,始終會更改處理器的狀態。
BX Rm 和 BLX Rm 可從 Rm 的位 [0] 推算出目標狀態:
如果 Rm 的位 [0] 為 0,則處理器的狀態會更改為(或保持在)ARM 狀態
如果 Rm 的位 [0] 為 1,則處理器的狀態會更改為(或保持在)Thumb 狀態。
BXJ 指令會將處理器的狀態更改為 Jazelle
8、STMFD和LDMFD指令
這兩條指令分別是入棧和出棧的意思,因此先來講一下堆棧的相關概念:
a) 滿堆棧:即入棧后堆棧指針sp指向最后一個入棧的元素。也就是sp先減一(加一)再入棧。
b) 空堆棧:即入棧后堆棧指針指向最后一個入棧元素的下一個元素。也就是先入棧sp再減一(或加一)。
c) 遞增堆棧:即堆棧一開始的地址是低地址,向高地址開始遞增。就如同一個水杯(假設上面地址大)開口的是大地址,從杯底開始裝水。
d) 遞減堆棧:即堆棧一開始的地址是高地址,向低地址開始遞增。就如同剛才說的那個水杯,現在開口的是小地址,從大地址開始用。
有這些類型就可以構成4種不同的堆棧方式,arm的棧一般我們用滿堆棧、遞減堆棧。
一開始,看到 STMFD sp!{R0-R5,LR} 這條命令時真是有點疑惑。STMFD的意思是:ST(store 存儲) M(multiple 多次)F(full 滿堆棧)D(decrease 遞減堆棧),合起來就是按滿的遞減的方式把后面的寄存器里的值都存到sp中。
STMFD sp!{R0-R5,LR}就是把lr r5-r0 依次存到sp中,並且sp會在存數據之前自動減一個數據的空間(因為arm棧是遞減的)。至於最后一個問題,就是sp后為什么有一個“!”。如果有!號,表示在存入數據后sp會指向最后一個存入的數據的地址,否則sp會把自己的值加到一開始的地址。(就是sp在執行完這條指令之后sp指向的地址不變)。
例子:STMFD sp!,{r0} ;將r0中的值壓入堆棧,壓入過程是,由於r0中的值為32位的,首先將sp減去4(因為arm棧是遞減的),將r0中的低八位放入sp這個位置,第九位到第十六位放入sp+1的地址,將第十七位到第二十四位放入sp+2的位置,將第二十五位到第三十二位放入sp+3的位置。
LDMFD sp!,{r2,r3};將堆棧中的內容出棧,出棧過程是,將sp這個位置的值放入r2中的低八位,將sp+1這個位置的值放入r2中的第九位到第十六位,將sp+2這個位置的值放入r2中的第十七位到第二十四位,將sp+3這個位置的值放入r2中的第二十五位到第三十二位;將sp+4這個位置的值放入r3中的低八位,將sp+5這個位置的值放入r3中的第九位到第十六位,將sp+6這個位置的值放入r3中的第十七位到第二十四位,將sp+4這個位置的值放入r3中的第二十五位到第三十二位。最后sp=sp+8。
此外,STR指令也可以用來入棧:strr1, [sp,#4] ,將r1中的值壓入堆棧,壓入過程是,由於r1中的值為32位的,將r0中的低八位放入sp+4這個位置,第九位到第十六位放入sp+5的地址,將第十七位到第二十四位放入sp+6的位置,將第二十五位到第三十二位放入sp+7的位置。
9、移位指令(操作)
a) LSL(或ASL)操作
LSL(或ASL)操作的格式為:
通用寄存器,LSL(或ASL) 操作數
LSL(或ASL)可完成對通用寄存器中的內容進行邏輯(或算術)的左移操作,按操作數所指定的數量向左移位,低位用零來填充。其中,操作數可以是通用寄存器,也可以是立即數(0~31)。
操作示例
MOV R0, R1,LSL#2 ;將R1中的內容左移兩位后傳送到R0中。
b) LSR操作
LSR操作的格式為:
通用寄存器,LSR 操作數
LSR可完成對通用寄存器中的內容進行右移的操作,按操作數所指定的數量向右移位,左端用零來填充。其中,操作數可以是通用寄存器,也可以是立即數(0~31)。
操作示例:
MOV R0, R1, LSR#2 ;將R1中的內容右移兩位后傳送到R0中,左端用零來填充。
c) ASR操作
ASR操作的格式為:
通用寄存器,ASR 操作數
ASR可完成對通用寄存器中的內容進行右移的操作,按操作數所指定的數量向右移位,左端用第31位的值來填充。其中,操作數可以是通用寄存器,也可以是立即數(0~31)。
操作示例:
MOV R0, R1, ASR#2 ;將R1中的內容右移兩位后傳送到R0中,左端用第31位的值來填充。
d) ROR操作
ROR操作的格式為:
通用寄存器,ROR 操作數
ROR可完成對通用寄存器中的內容進行循環右移的操作,按操作數所指定的數量向右循環移位,左端用右端移出的位來填充。其中,操作數可以是通用寄存器,也可以是立即數(0~31)。顯然,當進行32位的循環右移操作時,通用寄存器中的值不改變。
操作示例:
MOV R0, R1, ROR#2 ;將R1中的內容循環右移兩位后傳送到R0中。
e) RRX操作
RRX操作的格式為:
通用寄存器,RRX 操作數
RRX可完成對通用寄存器中的內容進行帶擴展的循環右移的操作,按操作數所指定的數量向右循環移位,左端用進位標志位C來填充。其中,操作數可以是通用寄存器,也可以是立即數(0~31)。
操作示例:
MOV R0, R1,RRX#2 ;將R1中的內容進行帶擴展的循環右移兩位后傳送到R0中。
f) SHL和SHR:邏輯移位指令。
SHL是邏輯左移指令,它的功能為:
(1)將一個寄存器或內存單元中的數據向左移位;
(2)將最后移出的一位寫入CF中;
(3)最低位用0補充。
指令:
MOV AL,01001000b
SHL AL,1 ;將AL中數據左移一位
執行后(AL)=10010000b,CF=0。
注意:
如果移動位數大於1時,必須將移動位數放在CL中。
比如,指令:
MOV AL,01010001b
MOV CL,3
SHL AL,CL
執行后(AL)=10001000b,因為最后移出的一位是0,所以CF=0。LDRB
SHR是邏輯右移指令,它和SHL所進行的操作剛好相反。
10、邏輯指令
a) AND
邏輯與操作指令。將operand2 值與寄存器Rn 的值按位作邏輯與操作,結果保存到Rd 中。指令格式如下:
AND{cond}{S} Rd,Rn,operand2
AND 指令舉例如下:
ANDS R0,R0,#x01 ;R0=R0&0x01,取出最低位數據
AND R2,R1,R3 ;R2=R1&R3
b) ORR
邏輯或操作指令。將operand2 的值與寄存器Rn 的值按位作邏輯或操作,結果保存到Rd 中。指令格式如下:
ORR{cond}{S} Rd,Rn,operand2
ORR 指令舉例如下:
ORR R0,R0,#x0F ;將R0 的低4 位置1
MOV R1,R2,LSR #4
ORR R3,R1,R3,LSL #8 ;使用ORR 指令將近R2 的高8 位數據移入到R3 低8 位中
c) EOR
邏輯異或操作指令。將operand2 的值與寄存器Rn 的值按位作邏輯異或操作,結果保存到Rd 中。指令格式如下:
EOR{cond}{S}Rd,Rn,operand2
EOR 指令舉例如下:
EOR R1,R1,#0x0F ;將R1 的低4 位取反
EOR R2,R1,R0 ;R2=R1^R0
EORS R0,R5,#0x01 ;將R5 和0x01 進行邏輯異或,結果保存到R0,並影響標志位
11、條件助記符
ARM匯編指令的基本指令格式如下:
<opcode>[<cond>][s]<Rd>,<Rn>,[<op2>],其中,[<參數>]可選,指令長度32bit。第一項為操作碼,第二項為條件助記符。常用的條件助記符及其含義如下表所示。由指令格式可知,操作碼可與條件助記符結合起來一起使用,如MOVEQ,MOVNE,BLS等等,根據下表中條件助記符的含義和操作碼的含義,對於上述組合命令也就不難理解了。
操作碼 |
條件助記符 |
標志 |
含義 |
0000 |
EQ |
Z=1 |
相等 |
0001 |
NE |
Z=0 |
不相等 |
0010 |
CS/HS |
C=1 |
無符號數大於或等於 |
0011 |
CC/LO |
C=0 |
無符號數小於 |
0100 |
MI |
N=1 |
負數 |
0101 |
PL |
N=0 |
正數或零 |
0110 |
VS |
V=1 |
溢出 |
0111 |
VC |
V=0 |
沒有溢出 |
1000 |
HI |
C=1,Z=0 |
無符號數大於 |
1001 |
LS |
C=0,Z=1 |
無符號數小於或等於 |
1010 |
GE |
N=V |
有符號數大於或等於 |
1011 |
LT |
N!=V |
有符號數小於 |
1100 |
GT |
Z=0,N=V |
有符號數大於 |
1101 |
LE |
Z=1,N!=V |
有符號數小於或等於 |
1110 |
AL |
任何 |
無條件執行 (指令默認條件) |
1111 |
NV |
任何 |
從不執行(不要使用)
|