常用的ARM彙編指令總結

原文地址:https://blog.csdn.net/zb861359/article/details/81027021

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,並影響標誌位

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章