了解用于編程ARM內(nèi)核的ARM指令集中使用的一些基本指令。
本文旨在幫助您了解有關ARM核心編程的基本匯編指令。
我們將從上一篇有關ARM寄存器文件的文章中摘錄-請在繼續(xù)之前考慮一下該信息,因為我們將在下面的說明中引用寄存器符號和寄存器標志。
下一篇文章將使用此信息來編程使用32位ARM內(nèi)核的Raspberry Pi。在本文中,我們將重點放在32位ARMv7指令和32位寄存器上。
注意:運行Raspbian的Raspberry Pi的更高版本使用64位ARMv8處理器,但與舊版v7一樣,以32位模式運行。我們可能會在以后的文章中介紹ARMv8。
機器碼
指令由處理器使用-讓我們看一下指令所代表的機器代碼。由于我們要講的大部分指令都是關于數(shù)據(jù)操作的,因此我從ARMV7手冊中獲取了數(shù)據(jù)處理指令。
圖1.ARM數(shù)據(jù)處理指令
圖1顯示了ARM數(shù)據(jù)處理指令中的32位;每個位都有特定的用途,可以單獨使用,也可以作為一組使用。
條件字段為4位寬,因為大約有十五個條件代碼。操作碼為4位寬,位于立即信號和條件設置標志之間,該信號指示操作數(shù)2保持一個立即值,而條件設置標志可用于在操作期間更新狀態(tài)寄存器(稍后會詳細介紹)。注意,由操作碼確定處理器將執(zhí)行的操作(例如加法,減法或異或)。
當您瀏覽下面的指令時,我們將參考圖1并嘗試查看匯編指令如何被編碼為二進制。并且不要害怕深入閱讀ARM手冊以獲取更多信息。
如何閱讀匯編說明:助記符和操作數(shù)
每條指令均以代表操作的助記符開頭。助記符后面是將要操作的操作數(shù)。這些通常是目標操作數(shù)和源操作數(shù),如下所示。
MNEMONIC DEST, SRC1, SRC2
ADD指令(在下面的部分中介紹)將R2加到R1并將結果放入寄存器R0中(有關這些符號的說明,請參見上一篇文章)。這是讀取匯編指令的典型方法。將R2加到R1并將其(結果)放入R0。將在ADD指令旁邊顯示將在處理器上執(zhí)行的等效機器代碼。
“條件”字段包含始終執(zhí)行的“ 1110”。這些位在使用附加到ADD操作的條件后綴時起作用。下一個字段未使用且設置為零?!?I”字段為零,因為“ Op2”是一個寄存器,而不是立即數(shù)?!?S”字段為零,因為我們沒有將S附加到ADD操作上,也就是說,我們不希望該指令更新狀態(tài)寄存器標志(如上所述的N,Z,C和V)。
如果您再次參考圖1,請注意要添加的操作碼。0100b。這告訴處理器設置ADD操作的數(shù)據(jù)路徑。最后三個字段是R1(0001b),R0(0000b)和R2(….0010b)。
Cond I OpCd SRn Rd Op2
ADD R0,R1, R2 @ 1110|00|0|0100|0|0001|0000|000000000010
指令中的操作數(shù)通常是寄存器,但它們也可以是內(nèi)存地址或立即數(shù)。立即值是要使用的確切數(shù)字。這些前綴有#符號。例如,我們可以使用立即數(shù)42代替上面的R2。該指令如下所示:
Cond I OpCd SRn Rd Op2
ADDS R4, R6, #42 @ 1110|00|1|0100|1|0110|0100|000000101010
該指令將42加到R6并將結果放入R4。這次圍繞“ I”設置為1的原因是,我們將立即數(shù)用于操作數(shù)2。操作碼保持不變,因為我們?nèi)栽谶M行加法運算。請注意,“ S”字段為1;因此,我們希望此ADD操作在執(zhí)行期間更新狀態(tài)寄存器標志。
下一條指令可以使用“條件”字段檢查狀態(tài)標志,并根據(jù)結果有條件地執(zhí)行。'Rn'是0110b,代表R6,而'Rd'是0100b對于R4?!?Op2”中的立即數(shù)是數(shù)字42的12位二進制表示形式。本節(jié)的其余部分列出了最基本的ARM指令的子集,并提供了簡短說明和示例。
數(shù)據(jù)處理說明以下說明處理數(shù)據(jù)。這可以是執(zhí)行數(shù)學功能的算術運算,比較運算或數(shù)據(jù)移動。
加法(ADD)加法(ADD)將R2加到R1并將結果放入R0。帶進位加法(ADC)將R2與進位標志一起加到R1。在處理大于單個32位字的數(shù)字時使用此功能。
ADD R0, R1, R2
ADC R0, R1, R2
減法(SUB)減法(SUB)從R1減去R2并將結果放入R0。進位減法(SBC)從R1中減去R2,如果清除了進位標志,則從結果中減去1。這等效于算術借用,并確保多字減法正確工作。
SUB R0,R1,R2
SBC R0,R1,R2
比較(CMP)和比較負數(shù)(CMN)比較(CMP)和比較負數(shù)(CMN)比較兩個操作數(shù)。CMP從R0中減去R1,CMN將R2與R1相加,然后根據(jù)加法或減法的結果更新狀態(tài)標志。
CMP R0,R1
CMN R1,R2
移動(MOV)“移動(MOV)”操作確實像聽起來那樣。它將數(shù)據(jù)從一個地方移到另一個地方。下面,將R1復制到R0。第二行將立即數(shù)8放入R0。
MOV R0,R1
MOV R0,#8
移負(MVN)負向移動(MVN)執(zhí)行類似的操作,但先對數(shù)據(jù)進行補充(取反)。當執(zhí)行帶有負數(shù)的操作時,尤其是使用二進制補碼時,這很有用。以下指令將NOT 8(通常稱為–9)放入R0。將一個加到該結果中,您便完成了兩個的補數(shù)并獲得了-8。
MVN R0,#8
AND執(zhí)行R2和R1的按位與運算,并將結果放入R0??梢允褂昧⒓磾?shù)代替R2。
AND R0,R1,R2
ORR和EORORR和EOR分別執(zhí)行R2和R1的按位OR和XOR。
ORR R0,R1,R2
EOR R0,R1,R2
位清除(BIC)位清除(BIC)對R2和R1執(zhí)行按位與,但首先對R2中的位進行補充。此操作通常與立即數(shù)一起使用,如第二行所示,立即數(shù)0xFF取反,然后與R1進行“與”運算。將八個零與R1的第一個字節(jié)進行“與”運算將清除這些位,即,將它們設置為零,然后將結果放入R0。
BIC R0,R1,R2
BIC R0,R1,#0xFF
測試位(TST)和測試當量(TEQ)存在TeST位(TST)和測試等效項(TEQ)以測試位于寄存器中的位。這些指令不使用目標寄存器,而只是根據(jù)結果更新狀態(tài)寄存器。TST本質(zhì)上執(zhí)行兩個操作數(shù)的按位與。通過為操作數(shù)2使用掩碼,我們可以測試R0中是否設置了單個位。
在這種情況下,我們檢查第3位(位掩碼= 1000b = 8),然后根據(jù)結果設置Z標志。TEQ與排他或執(zhí)行類似的功能,非常適合檢查兩個寄存器是否相等。這將更新N和Z標志,因此它也適用于帶符號的數(shù)字。如果它們的符號不同,則將N設置為1。
TST R0, #8
TEQ R1, R2
乘法(MUL)乘法(MUL)將R1乘以R2并將結果存入R0。乘法不能與立即數(shù)一起使用。
MUL R0,R1,R2
移位和旋轉說明邏輯左移(LSL)邏輯左移(LSL)將R1中的位移位一個移位值。在這種情況下,立即數(shù)為3,并丟棄最高有效位。移出的最后一位放入進位標志,最低有效位用零填充。在下面,R1向左移動立即數(shù)3,即R2中的0到31之間的一個值,然后放入R0。邏輯左移一個值乘以2。這是進行簡單乘法的廉價方法。
LSL R0,R1,#3
LSL R0,R1,R2
邏輯右移(LSR)邏輯右移(LSR)與LSL以相反的方式工作,并且有效地將一個值除以2。最高有效位用零填充,最后最低有效位放入進位標志。
LSR R0,R1,#2
算術右移(ASR)算術右移(ASR)執(zhí)行與LSR相同的工作,但設計用于帶符號的數(shù)字。它將符號位復制回左側的最后一個位置。
ASR R0,R1,#4
向右旋轉(ROR)向右旋轉(ROR)將一個單詞中的所有位旋轉一個值。無需將左側的位填充零,只需將移出的位放回另一端即可。
ROR R0,R1,#5
分支操作說明處理器的一項重要功能是能夠基于一組輸入在兩個代碼路徑之間進行選擇的能力。這正是分支操作所要做的。處理器通常通過將程序計數(shù)器(PC)R15遞增四個字節(jié)(即,一條指令的長度)來執(zhí)行一條指令。分支將PC更改為另一個標簽,該標簽表示代表匯編代碼的那部分。
支(B)分支(B)將PC移至標簽指定的地址。標簽(下面的示例中的“循環(huán)”)代表您希望處理器接下來執(zhí)行的一段代碼。標簽只是文本,通常是一個有意義的詞。
B loop
分支鏈接(BL)分支鏈接(BL)執(zhí)行類似的操作,但是它將下一條指令的地址復制到鏈接寄存器(LR)R14中。這在執(zhí)行子例程/過程調(diào)用時非常有用,因為一旦標簽上的代碼部分完成,我們就可以使用LR返回分支。在下面,我們跳轉到標簽“子例程”,然后使用鏈接寄存器返回下一條指令。
BL subroutine
…
subroutine:
…
…
MOV PC, LR
我們使用mov指令將鏈接寄存器放回程序計數(shù)器。這將使程序返回到我們子程序調(diào)用之后的位置,此處標記為。注意上面的LR和PC的使用。ARM匯編器分別將它們識別為R14和R15。這樣可以方便地提醒程序員有關正在執(zhí)行的操作。
加載和存儲說明計算機的內(nèi)存存儲處理器所需的數(shù)據(jù)。通過使用地址訪問此數(shù)據(jù)。首先將地址放入寄存器,然后我們可以訪問該地址的數(shù)據(jù)。這就是為什么我們使用加載和存儲操作。
加載寄存器(LDR)加載寄存器(LDR)將位于某個地址的數(shù)據(jù)加載到目標寄存器中。R1周圍的括號表示該寄存器包含一個地址。通過使用括號,我們將位于該地址的數(shù)據(jù)(而不是地址本身)放入R0。我們還可以使用這種表示法來定位與某個地址的數(shù)據(jù)偏移量,如第二行所示。R0將包含與地址R1所包含的內(nèi)容相距兩個字的數(shù)據(jù)。
LDR R0,[R1]
LDR R0,[R1,#8]
我們還可以使用標簽表示地址,然后可以將相應的數(shù)據(jù)加載到寄存器中。下面的第一行將標簽“ info”的地址加載到R0中。然后訪問存儲在該地址的值,并將其放入第二行的R1中。
LDR R0, =info
LDR R1, [R0]
商店(STR)存儲(STR)執(zhí)行補充操作以加載。STR將寄存器的內(nèi)容放入存儲位置。下面的代碼將數(shù)據(jù)存儲在R1中R0的地址處。同樣,方括號表示R0擁有一個地址,我們希望修改該地址上的數(shù)據(jù)。
STR R1,[R0]
加載和存儲類型:字節(jié)(B),半字(H),字(略),有符號(SB),無符號(B)加載和存儲都可以使用附加的類型編寫。此類型表示指令將操作字節(jié)(B),半字(H)還是字(忽略),以及數(shù)據(jù)是帶符號的(SB)還是無符號的(B)。
一個方便的地方是用于字符串操作,因為ASCII字符的長度為一個字節(jié)。這些操作還允許在加載或存儲時使用偏移量,如最后一行所示。
LDR R0, =text @ load a 32 bit address into R0
STRB R1, [R0] @ store byte at address in memory
STRB R1, [R0, + R2] @ store byte at address + offset R2
如前所述,指令中使用的助記符可以附加可選的條件代碼。這允許條件執(zhí)行。
請記住,這些標志(如前面的文章中布局)是Z(?ERO),C(?ARRY),N(?egative)和V(OveRFlow)。
為了強制指令更新狀態(tài)寄存器,可以將一個可選的S附加到迄今為止提到的大多數(shù)助記符中。更新狀態(tài)寄存器后,可以使用以下條件后綴(如下所示)來控制指令是否執(zhí)行。這些后綴的二進制代碼對應于上面顯示的數(shù)據(jù)處理指令的前四位(請參見圖1)。
圖2.條件后綴
在編寫匯編時,這些后綴將附加到助記符。下面的清單顯示了前面提到的說明中使用的一些條件后綴。
由于我們將在下一篇文章中使用GNU匯編器進行匯編,因此我們需要使用@符號表示注釋。
.global _start
start:
MOV R0, #3 @ Put the value 3 into R0
MOV R1, #0 @ Put the value 0 into R1
loop:
CMP R0, R1 @ Compare R1 to R0 (effectively R0 minus R1)
BEQ done @ If they are equal (Z=1) branch to done label
ADDGT R1, #1 @ If R0 is greater than R1 add 1 to R1
SUBLT R1, #1 @ If R0 is less than R1 subtract 1 from R1
B loop @ Branch back and re-run loop
done:
@ do other stuff
希望本文能使你對用于編程ARM內(nèi)核的基本指令有基本的了解。在下一篇文章中,我們將在一個使用Raspberry Pi編程內(nèi)核的簡單示例中使用此知識。
-
ARM
+關注
關注
134文章
9027瀏覽量
366435 -
匯編指令
+關注
關注
0文章
38瀏覽量
11431
發(fā)布評論請先 登錄
相關推薦
評論