一、跳轉指令
跳轉指令用於實現程序流程的跳轉,在ARM 程序中有兩種方法可以實現程序流程的跳轉:
Ⅰ.使用專門的跳轉指令。
Ⅱ.直接向程序計數器PC 寫入跳轉地址值。
通過向程序計數器PC 寫入跳轉地址值,可以實現在4GB 的地址空間中的任意跳轉,在跳轉之前結合使用 MOV LR,PC 等類似指令,可以保存將來的返回地址值,從而實現在4GB 連續的線性地址空間的子程序調用。
ARM指令集中的跳轉指令可以完成從當前指令向前或向后的32MB的地址空間的跳轉,包括以下4 條指令:
1、 B 指令
B 指令的格式為:
B{條件} 目標地址
B 指令是最簡單的跳轉指令。一旦遇到一個 B 指令,ARM 處理器將立即跳轉到給定的目標地址,從那里繼續執行。注意存儲在跳轉指令中的實際值是相對當前PC 值的一個偏移量,而不是一個絕對地址,它的值由匯編器來計算(參考尋址方式中的相對尋址)。它是 24 位有符號數,左移兩位后有符號擴展為 32 位,表示的有效偏移為 26 位(前后32MB 的地址空間)。以下指令:
B Label ;程序無條件跳轉到標號Label 處執行
CMP R1,#0 ;當CPSR 寄存器中的Z 條件碼置位時,程序跳轉到標號Label 處執行
BEQ Label
2、 BL 指令
BL 指令的格式為:
BL{條件} 目標地址
BL 是另一個跳轉指令,但跳轉之前,會在寄存器R14 中保存PC 的當前內容,因此,可以通過將R14 的內容重新加載到PC 中,來返回到跳轉指令之后的那個指令處執行。該指令是實現子程序調用的一個基本但常用的手段。以下指令:
BL Label ;當程序無條件跳轉到標號Label 處執行時,同時將當前的PC 值保存到R14 中
3、 BLX 指令
BLX 指令的格式為:
BLX 目標地址
BLX 指令從ARM 指令集跳轉到指令中所指定的目標地址,並將處理器的工作狀態有ARM 狀態切換到Thumb 狀態,該指令同時將PC 的當前內容保存到寄存器R14 中。因此,當子程序使用Thumb 指令集,而調用者使用ARM 指令集時,可以通過BLX 指令實現子程序的調用和處理器工作狀態的切換。同時,子程序的返回可以通過將寄存器R14 值復制到PC 中來完成。
4、 BX 指令
BX 指令的格式為:
BX{條件} 目標地址
BX 指令跳轉到指令中所指定的目標地址,目標地址處的指令既可以是ARM 指令,也可以是Thumb 指令。
ARM7TDMI(-S)具有32位ARM指令集和16位Thumb指令集。
ARM指令集效率高,但是代碼密度高
Thumb指令集具有較高的代碼密度,卻仍然保持着ARM的大多數性能上的優勢,它是ARM的子集。
所有的ARM置零都是可以條件執行的,而Thumb置零僅有一條指令具備條件執行的功能。
ARM和Thumb程序可以相互調用,相互之間狀態切換開銷幾乎為零。
首先我們來看一下分類:
一、數據處理指令操作數尋址方式
1、立即數尋址方式
2、寄存器尋址方式
3、寄存器移位尋址方式
二、存儲器訪問指令操作數尋址方式
1、寄存器間接尋址
2、基址變址尋址
3、相對尋址
4、多寄存器尋址(塊拷貝尋址)
5、堆棧尋址
下面來一一介紹:
ARM有9種尋址方式
尋址方式:
1、立即尋址:
立即尋址指令中的操作碼字段后面的部分就是操作數本省,也就是說,數據就包含再指
令,取指令也就取出了可以立即使用的操作數。舉例:
SUBS R0,R0,#1 ;R0減1,結果放入R0中,並且影響標志位
MOV R0,#0xFF000 ;將立即數0xFF000裝入R0寄存器
ARM規定:這個立即數必須符合8位圖格式,負責必須使用“文字池”方式,通過存儲器訪問指令加載,所謂的8位圖格式就是指,這個數據能通過一個8bit的數循環右移偶數位得到。
2、寄存器尋址:
操作數的值在寄存器中,指令中的地址字段指出的是寄存器的編號,指令執行的時候直
出寄存器值來操作。舉例:
MOV R1,R2 ;將R2的值存入R1
MOV R0,R1,R2 ;將R1的值減去R2的值,結構存到R0
3、寄存器移位尋址
寄存器移位尋址是ARM處理器特有的尋址方式。當第二個操作數是寄存器移位方式時,
第二個操作數在與第一個操作數結合之前,選擇進行移位操作。舉例:
MOV R0,R2,LSL #3 ;R2的值左移3位,結果放入R0,即R0=R2*8
ANDS R1,R1,R2,LSL R3 ;R2的值左移R3位,然后和R1相"與"操作,結構放入R1
可采用移位操作的指令如下:
LSL左移、LSR右移、ASR算術右移、ROR循環右移、RRX帶擴展的循環右移
4、寄存器間接尋址:
寄存器間接尋址指令中的地址碼給出的是一個通用寄存器的編號,所需的操作數保存
再寄存器指定地址的存儲單元中,即寄存器為操作數的地址指針,舉例:
LDR R1,[R2] ;將R2指向的單元中的數據保存再R1中
SWP R1,R1,[R2] ;將寄存器R1的值和R2指向的單元中進行內容交換
5、基址變址尋址:
基址尋址就是將基址寄存器的內容與指令中給出的偏移量進行相加,形成操作數的有效
地址。
基址尋址用於訪問基址附近的存儲單元,常用於查表、數組操作以及功能不見寄存器訪
問等 舉例:
LDR R2,[R3,#0x0c] ; 讀取R3 0x0c地址指向的存儲單元的內容,放入R2
STR R1,[R0,#-4]! ;先R0=R0-4,然后把R1的值寄存到R0所指向的單元中
LDR R1,[R0,R3,LSL #1] ;將R0 R3*2地址上的單元的內容讀出,並存入R1中
6、相對尋址:
相對尋址是基址尋址的一種變通。由程序計數器PC提供基址地址,指令中的地址碼字段為偏移量。兩者相加后得到的地址即為操作數的有效地址。
7、多寄存器尋址:
多寄存器尋址即一次可傳送幾個寄存器的值,允許一條指令傳送16個寄存器的任何子集或
所有的存儲器,舉例:
LDMIA R1!,{R2-R7,R12};將R1指向的單元中的數據讀出到R2---R7、R12中(R1自自動 )
STMIA R0!,{R2-R7,R12};將R2---R7、R12中的數據一次讀入到R0指向的單元中(R0自動 )
8、堆棧尋址(塊拷貝尋址):
堆棧是一種按特定順序進行存取的存儲區,操作順序分為“先進后出”和“后進先出”,堆棧尋址是隱含的,它使用一個專門的寄存器(堆棧指針)指向的存儲區域(堆棧),指針所指向的存儲單元即是堆棧的棧頂。
存儲器堆棧可以分為兩種:
向上生長:遞增堆棧
向下生長:遞減堆棧
還有從當前堆棧指針指向的內容是否有效可以分為:滿遞增、空遞增、滿遞減、空遞減
舉例:
STMFD SP!,{R1-R7,LR} ;將R1---R7、LR入棧。滿遞減堆棧
LDMFD SP!,{R1-R7,LR} ;數據出棧,放入R1---R7、LR寄存器,滿遞減堆棧
ARM指令的基本格式
ARM指令的基本格式為:
<Opcode> {<Cond>} {S} <Rd> , <Rn> { , <Opcode2> }
其中,< >內的項是必需的,{ }內的項是可選的。
(1)Opcode項
Opcode是指令助記符,即操作碼,說明指令需要執行的操作,在指令中是必需的。
(2)Cond項(command)
Cond項表明了指令的執行的條件,每一條ARM指令都可以在規定的條件下執行,每條ARM指令包含4位的條件碼,位於指令的最高4位[31:28]。條件碼共有16種,每種條件碼用2個字符表示,這兩個字符可以添加至指令助記符的后面,與指令同時使用。當指令的執行條件滿足時,指令才被執行,否則指令被忽略。如果在指令后不寫條件碼,則使用默認條件AL(無條件執行)。
指令的條件碼
條 件 碼 |
助記符后綴 |
標 志 |
含 義 |
0000 |
EQ |
Z置位 |
相等equal |
0001 |
NE |
Z清零 |
不相等not equal |
0010 |
CS |
C置位 |
無符號數大於或等於Carry Set |
0011 |
CC |
C清零 |
無符號數小於 |
0100 |
MI |
N置位 |
負數minus |
0101 |
PL |
N清零 |
正數或零plus |
0110 |
VS |
V置位 |
溢出 |
0111 |
VC |
V清零 |
沒有溢出 |
1000 |
HI |
C置位Z清零 |
無符號數大於high |
1001 |
LS |
Z置位C清零 |
無符號數小於或等於less |
1010 |
GE |
N等於V |
帶符號數大於或等於 |
1011 |
LT |
N不等於V |
帶符號數小於least |
1100 |
GT |
Z清零且(N等於V) |
帶符號數大於great |
1101 |
LE |
Z清零或(N不等於V) |
帶符號數小於或等於 |
1110 |
AL |
忽略 |
無條件執行all |
1111 |
|
|
|
條件碼應用舉例:
例:比較兩個值大小,並進行相應加1處理,C語言代碼為:
if ( a > b ) a++;
else b++;
對應的ARM指令如下(其中R0中保存a 的值,R1中保存b的值):
CMP R0, R1 ; R0與R1比較,做R0-R1的操作
ADDHI R0, R0, #1 ;若R0 > R1, 則R0 = R0 + 1
ADDLS R1, R1, #1 ; 若R0 <= R1, 則R1 = R1 + 1
CMP比較指令,用於把一個寄存器的內容和另一個寄存器的內容或一個立即數進行比較,同時更新CPSR中條件標志位的值。指令將第一操作數減去第二操作數,但不存儲結果,只更改條件標志位。
CMP R1, R0 ;做R1-R0的操作。
CMP R1,#10 ;做R1-10的操作。
(3) S項(sign)
S項是條件碼設置項,它決定本次指令執行的結果是否影響至CPSR寄存器的相應狀態位的值。該項是可選的,使用時影響CPSR,否則不影響CPSR。
(4)Rd項(destination)
Rd是指令中的目標寄存器,它是必需的。根據指令的不同,有些指令中要求Rd必須有R0~R7之間,有些要求Rd必須在R0~R14之間,有些則沒有特殊要求。
(5)Rn項
Rn是第一個操作數的寄存器,和Rd一樣,不同的指令對其的使用有不同的要求。
(6)Opcode2項
Opcode2項是第二個操作數,在ARM指令中,該操作數有三種形式:立即數形式、寄存器Rm形式和寄存器加移位形式(Rm, shift)。
例如:
SUB R3, R1, #10
SUB R3, R1, R2
SUB R3, R1, R2, LSL #2
SUB R3, R1, R2, LSL R0
ARM指令詳解
ARM指令集可分為以下6類:
l 跳轉指令
l 數據處理指令
l 程序狀態寄存器(PSR)處理指令
l 加載/存儲指令
l 協處理器指令
l 異常產生指令
ARM指令及功能描述
助 記 符 |
指令功能描述 |
ADC |
帶進位加法指令 |
ADD |
加法指令 |
AND |
邏輯與指令 |
B |
跳轉指令 |
BIC |
位清零指令 |
BKPT |
軟件斷點 |
BL |
帶返回的跳轉指令 |
BLX |
帶返回和狀態切換的跳轉指令 |
BX |
帶狀態切換的跳轉指令 |
CDP |
協處理器數據操作指令 |
CMN |
取反比較指令 |
CMP |
比較指令 |
EOR |
邏輯異或指令 |
LDC |
存儲器到協處理器的數據傳輸指令 |
LDM |
加載多個寄存器的指令 |
LDR |
存儲器到寄存器的數據傳輸指令 |
MCR |
從ARM寄存器到協處理器寄存器的數據傳輸指令 |
MLA |
乘加運算指令 |
MOV |
數據傳輸指令 |
MRC |
從協處理器寄存器到ARM寄存器的數據傳輸指令 |
MRS |
傳送CPSR或SPSR的值到通用寄存器的指令 |
MSR |
傳送通用寄存器的值到CPSR或SPSR的指令 |
MUL |
32位乘法指令 |
MVN |
數據取反傳送指令 |
ORR |
邏輯或指令 |
RSB |
反向減法指令 |
RSC |
帶借位的反向減法指令 |
SBC |
帶借位的減法指令 |
STC |
協處理器寄存器寫入存儲器指令 |
STM |
存儲多個寄存器的值到存儲器指令 |
STR |
存儲寄存器的值到存儲器的指令 |
SUB |
減法指令 |
SWI |
軟件中斷指令 |
SWP |
寄存器與存儲器 或 寄存器與寄存器之間的數據交換指令 |
TEQ |
相等測試指令 |
TST |
位測試指令 |
1. 跳轉指令
用於實現程序流程的跳轉,在ARM程序中有兩種方法可以實現程序流程的跳轉:一是使用專門的跳轉指令,二是直接向程序計數器PC寫入跳轉地址值。第二種方法可以實現在4GB的地址空間中的任意跳轉,在跳轉之前結合使用“MOV LR , PC”等類似指令,可以保存將來的返回地址值,從而實現在4GB連續的線性地址空間的子程序調用。
1)ARM指令集中的跳轉指令可以實現從當前指令向前或向后的32MB的地址空間的跳轉。
l B指令
格式:
B{條件} 目標地址
注意,存儲在跳轉指令中的實際值是相對當前PC值的一個偏移量,而不是一個絕對地址,它的值由匯編器來計算(相對尋址)。這個偏移量是一個24位的有符號數,左移兩位后表示的有效偏移為26位(前后32MB的地址空間)。{}表示可以省略。
如:B Label 程序無條件跳轉到標號Label處執行。
CMP R1,#0
BEQ Label
當CPSR寄存器中的Z條件碼置位時,程序跳轉到Label處執行。
當前PC:是指跳轉指令本身的起始地址。
l BL指令
格式:
BL{條件} 目標地址
這條指令在跳轉之前,會在寄存器R14中保存當前的下一條指令的地址,因此,可以通過將R14重新加載到PC中,來返回到跳轉指令之后的那條指令處執行。該指令是實現子程序調用的一種常用手段。
l BX 指令
格式:
BX {條件} 目標地址
BX指令中所指定的目標地址,只能使用寄存器的尋址方式,即跳轉的目標地址應先保存在一個寄存器中。指令在實現跳轉的同時,完成處理器的工作狀態的切換(ARM狀態與Thumb狀態間的切換)。
BX指令中,用寄存器的最低位來指示切換到哪一個工作狀態。如寄存器最低位為1,則把目標地址處的代碼解釋為Thumb代碼,進入Thumb工作狀態,並自動將CPSR中的控制位T置1。若寄存器最低位為0,則把目標地址處的代碼解釋為ARM代碼,進入ARM工作狀態,並自動將CPSR中的控制位T置0。
…
ADRL R0, ThumbFun + 1 ;生成分支地址並置最低位為 1
BX R0 ;跳轉到R0所指定的地址,並切換處理器到Thumb工作狀態
…
ThumbFun
… ;Thumb匯編指令
…
| BLX指令
以上兩條指令的綜合。
2. 數據處理指令
數據處理指令可分數據傳送指令、算術邏輯運算指令和比較指令等。數據傳送指令用於在寄存器和存儲器之間進行數據的雙向的傳輸。所有ARM數據處理指令均可選擇使用S后綴,以影響狀態標志CPSR。比較指令(CMP、CMN、TST、TEQ)不保存運算結果,這些指令也不使用S后綴,但會直接影響CPSR中的相應的狀態標志位。
(1)數據傳送指令MOV 和MVN
格式:
MOV {條件} {S} 目的寄存器, 源操作數
MOV指令可以完成從另一個寄存器、被移位的寄存器、或將一個立即數加載到目的寄存器。與MVN指令不同的是在傳送之前,將被傳送的對象先按位取反,再傳送到目的寄存器。
例:MVN R1 , #0XFF ;R1 ← 0X FFFF FF00
MVN R1 , R2
MOV PC,R14;將寄存器R14的值傳送給PC,用於子程序返回。
(2) 數據比較指令CMP , CMN , TST , TEQ
例:
CMP R1, R2 ; 做R1 – R2 的操作,結果不保存,但影響標志位。
CMP R1 , #10 ;做R1 - 10的操作,結果不保存,但影響標志位。
CMN 指令用於把一個寄存器的內容和另一個寄存器的內容或立即數取反后進行比較操作,根據運算結果影響CPSR中的標志位。該指令實際完成操作數1和操作數2相加,並根據結果更改條件標志位。
TST位測試指令,用於把一個寄存器的內容和另一個寄存器的內容或立即數進行按位的與運算,並根據運算結果更新CPSR中條件標志位的值。操作數1是要測試的數,而操作數2 是一個位掩碼,該指令一般用來檢測是否設置了特定的位。
TST {條件} 操作數1, 操作數2
例:TST R0, #0X0000 0040 ; 指令用來測試R0的位3是否為1。
TST指令通常和EQ、NE條件碼配合使用,當所有測試位為0時,EQ有效,而只要有一個測試位不為0,則NE有效。
TEQ相等測試指令,用於把一個寄存器的內容和另一個寄存器的內容或立即數進行按位的異或運算,並根據運算結果更新CPSR中的條件標志位。指令用於比較兩個操作數是否相等。如果相等,則 Z = 1,否則Z = 0。指令通常和EQ、NE條件碼配合使用
例:TEQ R1, R2
TST R1,#%1;測試R1中是否設置了最低位(%表示二進制數)
(3)邏輯運算類指令:AND、ORR、EOR、BIC
格式:邏輯類指令 {條件} {S} 目的寄存器,操作數1, 操作數2
S選項,說明運算結果影響CPSR的條件標志位,沒有S選項,則不影響CPSR的條件標志位。
操作數1應該是一個寄存器,操作數2可以是一個寄存器、被移位的寄存器或一個立即數。
AND指令常用於將操作數1的某個位置0;ORR指令常用於將操作數1的某個位置1;EOR(異或)指令常用於將操作數1的某個位取反。與0相異或,保持不變,與1相異或,則取反。BIC指令用於清除操作數1的某些位,並把結果放置到目的寄存器中,如果在掩碼中設置了某一位,則清除這一位。未設置的掩碼位保持不變。
例:BIC R1, R1, #0X0F ;將R1的低四位清零,其他位不變。
(4)算術運算類指令:ADD、ADC、SUB、SBC、RSB、RSC
格式:算術運算類指令 {條件} {S} 目的寄存器,操作數1,操作數2
目的寄存器,操作數1和操作數2使用的寄存器必須在R0~R7之間。
操作數1應該是一個寄存器,操作數2可以是一個寄存器、被移位的寄存器或一個立即數。
例:ADDS R1, R1,#10 ;結果影響標志位
ADD R1, R1, R2 ;結果不影響標志位
ADD R3, R1, R2, LSL #2 ; R3 = R1 + ( R2 << 2 )
ADD指令完成的功能是將操作數1加上操作數2,結果送到目的寄存器。
ADC指令完成的功能是將操作數1加上操作數2,再加上標志位C的值,結果送到目的寄存器。
SUB指令完成的功能是將操作數1減去操作數2,結果送到目的寄存器。
SBC指令完成的功能是將操作數1減去操作數2,再減去標志位C的取反值,結果送到目的寄存器。
RSB逆向減法指令完成的功能是將操作數2減去操作數1,結果送到目的寄存器。
RSC帶借位的逆向減法指令完成的功能是將操作數2減去操作數1,再減去標志位C的取反值,結果送到目的寄存器。
例:
SUB R0, R1, #256 ;R0 = R1 - 256 , 結果不影響標志位
SUBS R0, R2,R3,LSL #1 ;R0 = R2 - ( R3 <<1 ),結果影響標志位
SUB SP , #380 ;SP = SP - 380
SBC R0, R1, R2 ;R0 = R1 - R2 - !C
RSC R0, R1, R2 ;R0 = R2 - R1 - !C
(5)乘法指令與乘加指令
ARM微處理器支持的乘法指令與乘加指令共有6條,可分為運算結果為32位和結果為64位兩類,與前面的數據處理指令不同,指令中的所有操作數、目的寄存器必須為通用寄存器,不能對操作數使用立即數或被移位的寄存器,同時,目的寄存器和操作數1必須是不同的寄存器。
l MUL指令
格式:
MUL {條件} {S} 目的寄存器,操作數1, 操作數2
功能:
目的寄存器 = 操作數1 × 操作數2,同時可以根據運算結果設置CPSR中相應的條件標志位N和Z。操作數1和操作數2均為32位的有符號數或無符號數。
l MLA指令
格式:
MLA {條件} {S} 目的寄存器,操作數1, 操作數2, 操作數3
功能:
目的寄存器 = 操作數1 × 操作數2 + 操作數3,同時可以根據運算結果設置CPSR中相應的條件標志位N和Z。操作數1和操作數2均為32位的有符號數或無符號數。
l SMULL指令(S:Signed, 有符號)
格式:
SMULL {條件} {S} 目的寄存器Low,目的寄存器High,操作數1, 操作數2
功能:
目的寄存器Low = (操作數1 × 操作數2 )的低32位,
目的寄存器High = (操作數1 × 操作數2 )的高32位,同時可以根據運算結果設置CPSR中相應的條件標志位。操作數1和操作數2均為32位的有符號數。
例:
SMULL R0, R1, R2, R3
;R0 = (R2 ×R3)的低32位, R1 = (R2 ×R3)的高32位。
l SMLAL指令(S:Signed, 有符號)
格式:
SMLAL {條件} {S} 目的寄存器Low,目的寄存器High,操作數1, 操作數2
功能:
目的寄存器Low = (操作數1 × 操作數2 )的低32位 + 目的寄存器Low,
目的寄存器High = (操作數1 × 操作數2 )的高32位 + 目的寄存器High,同時可以根據運算結果設置CPSR中相應的條件標志位。操作數1和操作數2均為32位的有符號數。
例:
SMLAL R0, R1, R2, R3
;R0 = (R2 ×R3)的低32位 + R0,
;R1 = (R2 ×R3)的高32位 + R1。
l UMULL指令(U:UnSigned, 無符號)
格式:
UMULL {條件} {S} 目的寄存器Low,目的寄存器High,操作數1, 操作數2
功能:
目的寄存器Low = (操作數1 × 操作數2 )的低32位,
目的寄存器High = (操作數1 × 操作數2 )的高32位,同時可以根據運算結果設置CPSR中相應的條件標志位。操作數1和操作數2均為32位的無符號數。
例:
UMULL R0, R1, R2, R3
;R0 = (R2 ×R3)的低32位, R1 = (R2 ×R3)的高32位。
l UMLAL指令(U:UnSigned, 無符號)
格式:
UMLAL {條件} {S} 目的寄存器Low,目的寄存器High,操作數1, 操作數2
功能:
目的寄存器Low = (操作數1 × 操作數2 )的低32位 + 目的寄存器Low,
目的寄存器High = (操作數1 × 操作數2 )的高32位 + 目的寄存器High,同時可以根據運算結果設置CPSR中相應的條件標志位。操作數1和操作數2均為32位的無符號數。
例:
UMLAL R0, R1, R2, R3
;R0 = (R2 ×R3)的低32位 + R0,
;R1 = (R2 ×R3)的高32位 + R1。
3. 程序狀態寄存器訪問指令
功能:用於在程序狀態寄存器和通用寄存器之間傳送數據。
l MRS指令
格式:
MRS {條件} 通用寄存器, 程序狀態寄存器(CPSR、SPSR)
功能:
將狀態寄存器的內容傳送到通用寄存器。
使用環境:
(1)當需要改變程序狀態寄存器的內容時,可用MRS將狀態寄存器的內容讀入到通用寄存器,修改后再寫回到程序狀態寄存器。
(2)當在異常處理或進程切換時,需要保存程序狀態寄存器的值,可先用該指令讀出程序狀態寄存器的值,然后保存。
l MSR指令
格式:
MSR {條件} 程序狀態寄存器(CPSR、SPSR)_<域>,操作數
功能:
將操作數的內容傳送到程序狀態寄存器的特定域中。其中,操作數可以為通用寄存器或立即數。<域>用於設置程序狀態寄存器中需要操作的位,32位的程序狀態寄存器分為4個域:
F域:位31~位24為條件標志位域;
S域:位23~位16為狀態位域;
X域:位15~位8為擴展位域;
C域:位7~位0為控制位域;
使用環境:
(1)當需要改變程序狀態寄存器的內容時,可用MRS將狀態寄存器的內容讀入到通用寄存器,修改后再寫回到程序狀態寄存器。
(2)當在異常處理或進程切換時,需要保存程序狀態寄存器的值,可先用該指令讀出程序狀態寄存器的值,然后保存。
例:
MSR CPSR , R0 ;CPSR←R0
MSR SPSR_c , R0 ;傳送R0到SPSR,但僅修改SPSR中的控制位域
MSR CPSR_c , #0XD3 ; CPSR[7..0] = 0XD3 , 即切換到管理模式
MSR CPSR_cxsf , R3 ; CPSR ← R3
注意:只有在特權模式下,才能修改狀態寄存器。
程序中不能通過MSR指令直接修改CPSR中的T控制位來實現ARM/Thumb狀態的切換,必須使用BX指令來完成處理器狀態的切換。
MRS與MSR配合使用,可以實現CPSR或SPSR寄存器的讀/修改/寫操作,進行處理器模式 切換,進行允許/禁止IRQ/FIQ中斷等的設置。
例:使能IRQ中斷
MRS R0 , CPSR
BIC R0 , R0 , #0X80
MSR CPSR_c , R0
MOV PC , LR
例:禁止IRQ中斷
MRS R0 , CPSR
ORR R0 , R0 , #0X80
MSR CPSR_c , R0
MOV PC , LR
4. 存儲器加載/存儲指令
功能:用於在寄存器和存儲器之間傳送數據,加載指令用於將存儲器中的數據傳送到寄存器,存儲指令則將寄存器中的數據傳送到存儲器。
存儲器加載/存儲指令分為單個存儲器加載/存儲指令和多個存儲器加載/存儲指令。
(1)單個存儲器加載/存儲指令
LDR字數據加載指令;
LDRH(Half)半字數據加載指令;
LDRB字節數據加載指令;
STR字數據存儲指令;
STRH半字數據存儲指令;
STRB字節數據存儲指令。
l 加載指令
格式:
加載指令 {條件} 目的寄存器, <存儲器地址>
例:
LDR R0 , [R1] ;將地址為R1的字數據讀入R0。
LDR R0 , [R1,R2] ;將地址為R1+R2的字數據讀入R0。
LDR R0 , [R1, #4] ;將地址為R1+4的字數據讀入R0。
LDR R0 , [R1, R2]! ;將地址為R1+R2的字數據讀入R0,並將新地址R1+R2寫入R1。
LDR R0 , [R1, R2, LSL #2 ] !
;將地址為R1 + R2 × 4的字數據讀入R0,並將新地址R1 + R2 × 4寫入R1。
LDRH指令用於從存儲器中將一個16位的半字數據傳送到目的寄存器中,同時將寄存器的高16位清零。
LDRH R0 , [R1] ;將地址為R1的半字數據讀入R0,並將R0的高16位清零。
LDRB指令用於從存儲器中將一個8位的字節數據傳送到目的寄存器中,同時將寄存器的高24位清零。
LDRB R0 , [R1] ;將地址為R1的字節數據讀入R0,並將R0的高24位清零。
注意:當是字操作時,操作數的地址必須是字對齊的,如果是半字操作,操作數的地址必須是半字對齊。否則,讀出的數據是無效,隨機的。
例:
LDR R0 , [R1 , # 2 ]
LDRH R0 , [R1 , # 1 ]
l 存儲指令
格式:
存儲指令 {條件} 源寄存器, <存儲器地址>
例:
STR R0 , [R1] , #8
;將R0中的字數據寫入以R1為地址的存儲器中,並將新地址R1+8寫入R1。
STR R0 , [R1 , #8] ;將R0中的字數據寫入以R1+8為地址的存儲器中。
STRH指令用於從源寄存器中將一個16位的半字數據傳送到存儲器中。該半字數據為源寄存器中的低16位。
STRH R0 , [R1, #8] ;將寄存器R0中的低16位寫入以R1 + 8為地址的存儲器中。
STRB指令用於從源寄存器中將一個8位的字節數據傳送到存儲器中。該字節數據為源寄存器中的低8位。
注意:當是字操作時,操作的地址必須是字對齊的,如果是半字操作,操作的地址必須是半字對齊。否則,讀出的數據是無效,隨機的。
(2)批量數據加載/存儲指令
功能:可以一次在一片連續的存儲器單元和多個寄存器之間傳送數據,批量加載指令用於將一片連續的存儲器中的數據傳送到多個寄存器,批量數據存儲指令完成相反的操作。
LDM(或STM)指令(Load Data To Multiple Register)
格式:
LDM(或STM) {條件} {類型} 基址寄存器{!} ,寄存器列表{^}
{類型}為以下幾種情況:
類 型 |
含 義 |
IA |
每次操作后,地址加4 |
IB |
每次操作前,地址加4 |
DA |
每次操作后,地址減4 |
DB |
每次操作前,地址減4 |
FD |
滿遞減堆棧 |
ED |
空遞減堆棧 |
FA |
滿遞增堆棧 |
EA |
空遞增堆棧 |
{!}為可選后綴,若選用,則當數據傳送完畢之后,將最后的地址寫入基址寄存器,否則基址寄存器的內容不改變。
基址寄存器不允許為R15,寄存器列表可以為R0 ~ R15的任意組合。
{^}為可選后綴,當指令為LDM且寄存器列表中包含有R15,選用該后綴表示:除了正常的數據傳送之外,還將SPSR復制到CPSR。同時,該后綴還表示傳入或付傳出的是用戶模式下的寄存器,而不是當前模式下的寄存器。
例:
STMFD SP!, {R0 - R7 , LR} ;現場保存,將R0 - R7 , LR入棧,滿遞減堆棧。
LDMFD SP!, {R0 - R7 , PC}^ ;恢復現場,異常處理返回,滿遞減堆棧
在進行數據復制時,先設置好源數據指針,然后使用塊拷貝尋址指令進行讀取和存儲。而在堆棧操作中,則要先設置堆棧指針SP,然后使用堆棧尋址指令實現堆棧操作。
5. 數據交換指令
功能:支持在存儲器和寄存器之間交換數據。
SWP ( Swap ) 字數據交換指令;
SWPB 字節數據交換指令。
格式:
交換指令 {條件} 目的寄存器, 源寄存器1, [源寄存器2]
例:
SWP R0, R1, [R2] ;將R2所指的存儲器中的字數據傳送到R0,同時將R1中的字數據傳送到R2所指的存儲器單元。
顯然,當源寄存器1與目的寄存器是同一個寄存器時,就完成了寄存器與存儲器間的交換操作。
SWPB指令用於將源寄存器2所指向的存儲器中的字節數據到目的寄存器中,目的寄存器的高24位清零,同時將源寄存器1中的低8位數據(低位字節)傳送到源寄存器2所指向的存儲器中。
6. 異常產生指令
異常指令有兩條:SWI軟件中斷指令和BKPT斷點中斷指令。
l SWI中斷指令
格式:
SWI {條件} 24位的立即數
功能:
產生軟件中斷,方便用戶程序調用操作系統的系統例程。
操作:
切換運行模式到管理模式,設置PC來執行在地址0X08處的下一條指令,設置相應的R13_svc和R14_svc。該指令的操作與執行BL 0X08這條指令的效果是相同的。不同的地方在於,SWI還帶有指明系統例程的類型的“24位的立即數”。在具體應用中,為便於記憶,可以使用字符串代替“24位的立即數”,例如:SWI “OS_Write0”和 SWI 0X02是一樣的。當指令中24位的立即數被忽略時,系統例程的類型由通用寄存器R0的內容決定。傳送給系統例程的參數通過通用寄存器來傳遞。
l BKPT指令
格式:
BKPT 16位的立即數
功能:
用於產生軟件斷點中斷,執行時中斷正常指令,進入相應的調試子程序。
7. 協處理器指令
ARM處理器可支持多達16個協處理器,每個協處理器只執行針對其自身的協處理指令。ARM的協處理器指令主要用於ARM處理器初始化、協處理器的數據處理操作、在ARM處理器與協處理器的寄存器之間傳送數據、在協處理器和存儲器之間傳送數據。ARM協處理器指令有以下5條:
l CDP協處理器數據操作指令;
l LDC協處理器數據加載指令;
l STC協處理器數據存儲指令;
l MCR ARM處理器寄存器到協處理器寄存器的數據傳送指令;
l MRC 協處理器寄存器到ARM處理器寄存器的數據傳送指令。
(1)CDP指令
格式:
CDP {條件}協處理器編碼,協處理器操作碼1,目的寄存器,源寄存器1,源寄存器2,協處理器操作碼2
功能:用於ARM處理器通知協處理器執行特定的操作,若協處理器不能執行指定的操作,則產生未定義指令異常。
注意:指令中涉及到的寄存器都是協處理器的寄存器,不涉及ARM處理器的寄存器和存儲器。操作碼1、操作碼2是協處理器要執行的操作。
例: CDP p5 , 1 , c3 , c4 , c5 , 2
;指示協處理器P5,執行操作1,可選操作為2;C3, C4, C5是相應的協處理器寄存器。
(2) LDC指令
格式:
LDC {條件}{L}協處理器編碼,目的寄存器,[源寄存器]
功能:
用於將源寄存器所指向的存儲器中的字數據傳送到目的寄存器中。若協處理器不能成功執行,則產生未定義指令異常。選項{L}表示指令為長讀取操作,可用於雙精度數據的傳輸。
注意:指令中涉及到的源寄存器是ARM處理器的寄存器。
例:LDC P3, C4, [R2, #4]
(3) STC指令
格式:
STC {條件}{L}協處理器編碼,源寄存器,[目的寄存器]
功能:
用於將源寄存器中的字數據傳送到目的寄存器所指向的存儲器中。若協處理器不能成功執行,則產生未定義指令異常。選項{L}表示指令為長讀取操作,可用於雙精度數據的傳輸。
注意:指令中涉及到的目的寄存器是ARM處理器的寄存器。
例:STC P3, C4, [R0]
;將協處理器P3的寄存器C4中的數據傳送到ARM處理器的寄存器R0所指向的存儲器.
(4) MCR指令
格式:
MCR {條件}協處理器編碼,協處理器操作碼1,源寄存器,目的寄存器1,目的寄存器2,協處理器操作碼2
功能:
MCR指令用於將ARM處理器寄存器中的數據傳送到協處理器的寄存器中。若協處理器不能完成這個操作,將引發未定義指令異常。源寄存器為ARM處理器的寄存器。
(5) MRC指令
格式:
MRC {條件}協處理器編碼,協處理器操作碼1,目的寄存器,源寄存器1,源寄存器2,協處理器操作碼2
功能:
MRC指令用於將協處理器寄存器中的數送到ARM處理器的寄存器中。若協處理器不能完成這個操作,將引發未定義指令異常。源寄存器為ARM處理器的寄存器。
例:MRC P3 , 3 , R0 , C4 , C5 , 6
;將協處理器P3的寄存器C4與C5中的數據傳送到ARM的寄存器中,並執行編號為3和6的操作。
Thumb指令集合
Thumb指令集是ARM指令集的一個子集,允許指令編碼為16位的長度,Thumb指令集在保留32位代碼優勢的同時,大大節省了系統的存儲空間。
當處理器在執行ARM程序段時,稱ARM處理器處於ARM工作狀態,當處理器在執行Thumb程序段時,稱ARM處理器處於Thumb工作狀態。
在編寫Thumb指令時,先要用偽指令CODE16聲明以下為Thumb指令代碼,在ARM指令代碼中可以使用BX指令跳轉到Thumb指令代碼片。同樣編寫ARM代碼時,則使用偽指令CODE32進行聲明,在Thumb指令代碼中使用BX指令可以跳轉到ARM指令代碼處。
大多數Thumb指令是無條件執行的,而幾乎所有的ARM指令都是有條件執行的。由於Thumb數據處理指令中的目的寄存器與其中的一個源寄存器相同,Thumb指令在指令編碼時由三個操作數改為兩個操作數。
通常實現同樣的程序功能時,所需的Thumb指令的條數比ARM指令多。但使用Thumb指令集合的代碼有以下特點:
l 比ARM代碼更節略存儲空間。
l 使用的指令條數比ARM代碼多。
l 若使用32位的存儲器,ARM代碼比Thumb代碼快約40%。
l 若使用16位的存儲器,Thumb代碼比ARM代碼快約40%~50%。
l 與ARM代碼相比較,使用Thumb代碼,存儲器的功耗會降低約30%。
<其它尋址模式與其它指令>
現在我們已經掌握了所有知識,可以編寫簡單的ARM匯編程序,但如果要編寫較為復雜的ARM程序,就必須掌握更多的尋址模式和指令,這就是本文的重點所在。
我們在“基本尋址模式與基本指令”一文中學習了最常用的3種尋址方式。下面介紹其它尋址方式。
1、基址尋址
基址尋址就是將基址寄存器的內容與指令中給出的偏移量相加,形成操作數的有效地址。基址尋址用於訪問基址附近的存儲單元,常用於查表、數組操作、功能部件寄存器訪問等。基址尋址指令舉例如下:
LDR R1,[R2,#0x0C]
R2的值+0x0C形成內存地址,讀取內存中該地址上的內容,放入R1
其它額外需要了解的內容:
§零偏移。 如:LDR R0,[R1]
§前索引偏移。 如:LDR R0,[R1,#0x04]!,表示將R1的值加上4后作為內存地址,並且指令執行結束時,R1本身的值也要加4。這里!表示要回寫
§程序相對偏移。 如:LDR R0,labe1,表示將標號label所代表的地址處存放的內容放入R0,相當於LDR R0, [PC, #某個常數]
§后索引偏移。 如:LDR R0,[R1],#0x04,表示將R1的值作為內存地址,並且指令執行結束時,R1本身的值要加4
2、多寄存器尋址
多寄存器尋址一次可傳送幾個寄存器值,允許一條指令傳送16個寄存器的任何子集或所有寄存器。多寄存器尋址指令舉例如下:
LDMIA R1!,{R2-R4,R6} ,它是ldr的多寄存版本,將內存中的4個字放入寄存器R2,R3,R4,R6中
指令執行前
指令執行后
兩點說明:
1)、R1!中的!號表示在指令執行完成后,要改變(回寫)基址寄存器(R1)的值
2)、寄存器列表{R2-R4, R6}中的順序並不要緊。最終寄存器與內存地址的對應關系是:編號小的寄存器與內存的低地址相對應
兩點問題:
1)、為什么內存起地址是0x40000000,而不是0x40000004
2)、為什么內存地址是從0x40000000 ---- 0x4000000C,而不是從0x3FFFFFF4 ---- 0x40000000
要解釋上面2個問題,其實也很簡單。其實多寄存加載指令ldm總共有4個:ldmia、 ldmib、 ldmda、 ldmdb。ia的意思是increase after,ib的意思是increase before,da的意思是decrease after,db的意思是decrease before。以LDMIA R1!, {R2-R4, R6}為例子,這里的ia是指辦事(將內存中的數加載到寄存器)之后增加基址寄存器(R1)的值。這條指令的執行過程從邏輯上看,如下:
1)、先辦事:將R1的值(0x40000000)作為內存地址,到該地址處取得數(0x01),加載到寄存器R2中
2)、后增加:將R1的值從0x40000000增加為0x40000004
再重復上面的操作3次,分別將內存中的數0x02、0x03、0x04放到寄存器中R3、R4、R6中,最后R1的值變為0x40000010。
這個例子中,如果將ldmia改為ldmib,則R2、R3、R4、R6中存放的是0x02、0x03、0x04、內存0x40000010處的內容,最后R1的值為0x40000010。
除了4條多寄存器加載指令外,還有4條類似的多寄存器存儲指令,分別是stria、 strib、 strda、 strdb
3、堆棧尋址
由於ARM指令集沒有專門的出棧和入棧指令,所以ARM匯編程序是采用SP作為棧指針,以stm指令完成入棧操作,以ldm指令完成出棧操作。
以入棧后SP的值是增加還是減少為依據,可將堆棧類型划分為遞增堆棧(向上生長)和遞減堆棧(向下生長);
以SP所指向的內存處存放的是棧頂元素還是下一次要入棧的元素,可將堆棧類型划分為滿堆棧和空堆棧
那么當堆棧類型為空遞減堆棧時候,入棧操作應該使用什么指令?出棧操作應該使用什么指令?進一步,如果堆棧類型為空遞增、滿遞增、滿遞減堆棧,又將如何呢?如果你不看下面的答案,我相信你一定會讓這幾個問題折磨得做很多的腦力體操,然后感嘆ARM指令集的設計者太不為你這樣的程序員考慮了,給了你本不應該由你承擔的負荷。但事實上正相反,ARM指令集的設計者充分理解了你作為程序員的苦惱,請看下面的答案。
數據塊傳送 | 堆棧操作 | 說明 |
存儲 | 壓棧 | |
STMDA | STMED | 空遞減 |
STMIA | STMEA | 空遞增 |
STMDB | STMFD | 滿遞減 |
STMIB | STMFA | 滿遞增 |
數據塊傳送 | 堆棧操作 | 說明 |
加載 | 出棧 | |
LDMDA | LDMFA | 滿遞增 |
LDMIA | LDMFD | 滿遞減 |
LDMDB | LDMEA | 空遞增 |
LDMIB | LDMED | 空遞減 |
這2張表的第一、三列回答了前面你絞盡腦汁回答的問題。而第二列則體現了ARM指令集的設計者對作為程序員的你的充分體貼。第二列中的ED、EA、FD、FA分別表示empty descend(空遞減)、 empty ascend(空遞增)、 full descend(滿遞減)、 full ascend(滿遞增),其含義是說,如果你采用的是空遞減(空遞增、滿遞減、滿遞增)堆棧的話,入棧操作則使用指令STMED(STMEA、STMFD、STMFA),出棧操作則使用指令LDMED(LDMEA、LDMFD、LDMFA)。從此你再也不會為你應該使用ia、ib、da還是db來實現出、入棧操作而苦惱了。
STMED、STMEA、STMFD、STMFA和LDMED、LDMEA、LDMFD、LDMFA就是所謂的堆棧尋址指令。由此可見:為了對程序員體貼入微,ARM指令集的設計者設計了堆棧尋址指令,其實質就是多寄存尋址指令的快捷方式。
4、寄存器移位尋址
寄存器移位尋址是ARM指令集特有的尋址方式。當第2個操作數是寄存器移位方式時,第2個寄存器操作數在與第1個操作數結合之前,選擇進行移位操作。例如:
MOV R0,R2,LSL #3 表示將R2的值邏輯左移3位,結果放入R0,即是R0=R2×8。
移位的方式有以下幾種:
LSL(logic shift left):邏輯左移
LSR(logic shift right):邏輯右移
ASR(arithmetic shift right):算術右移
ROR(rotate shift right):循環右移
RRX(rotate shift right with extend):帶擴展的循環右移。其中的C指的是CPSR的C位
5、相對尋址
相對尋址是基址尋址的一種變通。由程序計數器PC提供基准地址,指令中的地址碼字段作為偏移量,兩者相加后得到的地址即為操作數的有效地址。例如:
B LOOP
...
LOOP MOV R6,#1
該條B指令的意思是要跳轉到標號LOOP所代表的指令處,其含義相當明顯,但你要明白CPU根本不明白標號是個什么東西(事實上在指令的機器碼中根本就沒有標號這種東西),那么b loop這條指令的機器碼會是什么呢?答案是:高8bit是操作碼相關內容,低24bit是一個常數,表示從b指令到mov指令之間的內存地址的差值(如果不考慮流水線的影響的話)。由此可見,b loop這條指令相當於add pc, pc, #偏移量常數,典型的相對於PC(當前指令地址)的相對尋址。由於是相對於當前指令地址進行相對尋址,所以無論程序最終運行在內存的何處(即使運行的地址不是它預期的位置),這條B指令都能正確運行。關於相對尋址、程序期望的運行地址等等,我將在“ARM匯編偽指令”一文中詳細描述。
隨便說一下,前面學到b指令的跳轉范圍是當前指令的先后32M,為什么是這個范圍呢?因為24bit常數用1個比特區別正負,還剩23bit,同時由於ARM指令在內存中的地址其最低2bit一定是0(為什么?請自行思考一下),因此23bit中可以不必表示這2個0,所以23bit可以表示的范圍是0 ---- 2^25,即:0 ---- 32M。
我們在“基本尋址模式與基本指令”一文中學習了最常用的指令。下面介紹其它較為常用的指令。
1、訪存指令
LDRH(半字加載);LDRSH (有符號半字加載);STRH(半字存儲)
交換指令
助記符 |
說明 |
操作 |
SWP Rd,Rm,[Rn] |
寄存器和存儲器字數據交換 |
Rd←[Rn],[Rn]←Rm (Rn≠Rd或Rm) |
SWPB Rd,Rm,[Rn] |
寄存器和存儲器字節數據交換 |
Rd←[Rn],[Rn]←Rm (Rn≠Rd或Rm) |
2、數據處理指令
助記符 |
說明 |
操作 |
MVN Rd,operand2 |
數據非傳送 |
Rd←(~operand2) |
助記符 |
說明 |
操作 |
RSB Rd, Rn, operand2 |
逆向減法指令 |
Rd←operand2-Rn |
ADC Rd, Rn, operand2 |
帶進位加法 |
Rd←Rn+operand2+Carry |
SBC Rd, Rn, operand2 |
帶進位減法指令 |
Rd←Rn-operand2-(NOT)Carry |
RSC Rd, Rn, operand2 |
帶進位逆向減法指令 |
Rd←operand2-Rn-(NOT)Carry |
這里要特別提到,ADC指令結合CPSR,可以實現64位整數加法,詳情參見“雜項解釋”一文
助記符 |
說明 |
操作 |
BIC Rd, Rn, operand2 |
按位清除指令 |
Rd←Rn & (~operand2) |
其實現功能是:將Rn中對應於operand2中為1的bit位全部清0,然后將結果保存到Rd中
助記符 |
說明 |
操作 |
CMN Rn, operand2 |
負數比較指令 |
標志N、Z、C、V←Rn+operand2 |
TST Rn, operand2 |
位測試指令 |
標志N、Z、C←Rn & operand2 |
TEQ Rn, operand2 |
相等測試指令 |
標志N、Z、C←Rn ^ operand2 |
TST指令測試的是:Rn中所有指定bit位是否全為0(指定的bit位是operand2中為1的所有位);
TEQ指令測試的是:Rn和operand2是否相等。這點上與CMP指令一樣,區別在於CMP指令除了可以比較2個數是否相等外,也可以比較2個數誰大誰小,但TEQ不行。
3、乘法指令
助記符 |
說明 |
操作 |
MUL Rd,Rm,Rs |
32位乘法指令 |
Rd←Rm*Rs (Rd≠Rm) |
MLA Rd,Rm,Rs,Rn |
32位乘加指令 |
Rd←Rm*Rs+Rn (Rd≠Rm) |
UMULL RdLo,RdHi,Rm,Rs |
64位無符號乘法指令 |
(RdLo,RdHi) ←Rm*Rs |
UMLAL RdLo,RdHi,Rm,Rs |
64位無符號乘加指令 |
(RdLo,RdHi) ←Rm*Rs+(RdLo,RdHi) |
SMULL RdLo,RdHi,Rm,Rs |
64位有符號乘法指令 |
(RdLo,RdHi) ←Rm*Rs |
SMLAL RdLo,RdHi,Rm,Rs |
64位有符號乘加指令 |
(RdLo,RdHi) ←Rm*Rs+(RdLo,RdHi) |
4、協處理器指令
參見“MMU與內存保護的實現”一文
5、雜項指令
SWI:軟中斷指令,參見“swi與system call的實現”一文
MRS、MSR:程序狀態寄存器操作指令,參見“ARM異常處理”一文
以上摘自互聯網
另外附條bcc(轉移)公式,自己捉摸的, 可能有不對的地方.
計算opcode:
(dst - src) / 4 - 2 = opcode
計算dst:
src + (opcode + 2) * 4 = dst
不明看以下示例:)
1 ROM:9D039D3C 108 1C 00 00 1A BNE loc_9D039DB4 ; Branch 2 (9D039DB4 - 9D039D3C) / 4 - 2 = 1c 3 9D039D3C + (1c + 2) * 4 4 5 6 7 ROM:9D039D90 108 07 00 00 EA B loc_9D039DB4 ; Branch 8 (9D039DB4 - 9D039D90) / 4 - 2 = 7 9 9D039D90 + (7 + 2) * 4