ARM指令詳解
ADC : 帶進位的加法
(Addition with Carry)
ADC{條件}{S}
ADC 將把兩個操作數(shù)加起來,并把結(jié)果放置到目的寄存器中。它使用一個進位標(biāo)志位,這樣就可以做比 32 位大的加法。下列例子將加兩個 128 位的數(shù)。
128 位結(jié)果: 寄存器 0、1、2、和 3
第一個 128 位數(shù): 寄存器 4、5、6、和 7
第二個 128 位數(shù): 寄存器 8、9、10、和 11。
ADDS
如果如果要做這樣的加法,不要忘記設(shè)置 S 后綴來更改進位標(biāo)志。
ADD : 加法
(Addition)
ADD{條件}{S}
ADD 將把兩個操作數(shù)加起來,把結(jié)果放置到目的寄存器中。操作數(shù) 1 是一個寄存器,操作數(shù) 2 可以是一個寄存器,被移位的寄存器,或一個立即值:
ADD
加法可以在有符號和無符號數(shù)上進行。
AND : 邏輯與
(logical AND)
AND{條件}{S}
AND 將在兩個操作數(shù)上進行邏輯與,把結(jié)果放置到目的寄存器中;對屏蔽你要在上面工作的位很有用。 操作數(shù) 1 是一個寄存器,操作數(shù) 2 可以是一個寄存器,被移位的寄存器,或一個立即值:
AND
AND 的真值表(二者都是 1 則結(jié)果為 1):
Op_1
BIC : 位清除
(Bit Clear)
BIC{條件}{S}
BIC 是在一個字中清除位的一種方法,與 OR 位設(shè)置是相反的操作。操作數(shù) 2 是一個 32 位位掩碼(mask)。如果如果在掩碼中設(shè)置了某一位,則清除這一位。未設(shè)置的掩碼位指示此位保持不變。
BIC
BIC 真值表 :
Op_1
譯注:邏輯表達式為 Op_1 AND NOT Op_2
EOR : 邏輯異或
(logical Exclusive OR)
EOR{條件}{S}
EOR 將在兩個操作數(shù)上進行邏輯異或,把結(jié)果放置到目的寄存器中;對反轉(zhuǎn)特定的位有用。操作數(shù) 1 是一個寄存器,操作數(shù) 2 可以是一個寄存器,被移位的寄存器,或一個立即值:
EOR
EOR 真值表(二者不同則結(jié)果為 1):
Op_1
MOV : 傳送
(Move)
MOV{條件}{S}
MOV 從另一個寄存器、被移位的寄存器、或一個立即值裝載一個值到目的寄存器。你可以指定相同的寄存器來實現(xiàn) NOP 指令的效果,你還可以專門移位一個寄存器:
MOV
如果 R15 是目的寄存器,將修改程序計數(shù)器或標(biāo)志。這用于返回到調(diào)用代碼,方法是把連接寄存器的內(nèi)容傳送到 R15:
MOV
MVN : 傳送取反的值
(MoveNegative)
MVN{條件}{S}
MVN 從另一個寄存器、被移位的寄存器、或一個立即值裝載一個值到目的寄存器。不同之處是在傳送之前位被反轉(zhuǎn)了,所以把一個被取反的值傳送到一個寄存器中。這是邏輯非操作而不是算術(shù)操作,這個取反的值加 1 才是它的取負的值:
MVN
ORR : 邏輯或
(logical OR)
ORR{條件}{S}
OR 將在兩個操作數(shù)上進行邏輯或,把結(jié)果放置到目的寄存器中;對設(shè)置特定的位有用。操作數(shù) 1 是一個寄存器,操作數(shù) 2 可以是一個寄存器,被移位的寄存器,或一個立即值:
ORR
OR 真值表(二者中存在 1 則結(jié)果為 1):
Op_1
RSB : 反向減法
(Reverse Subtraction)
RSB{條件}{S}
SUB 用操作數(shù) two 減去操作數(shù) one,把結(jié)果放置到目的寄存器中。操作數(shù) 1 是一個寄存器,操作數(shù) 2 可以是一個寄存器,被移位的寄存器,或一個立即值:
RSB
反向減法可以在有符號或無符號數(shù)上進行。
RSC : 帶借位的反向減法
(Reverse Subtraction with Carry)
RSC{條件}{S}
同于 SBC,但倒換了兩個操作數(shù)的前后位置。
SBC : 帶借位的減法
(Subtraction with Carry)
SBC{條件}{S}
SBC 做兩個操作數(shù)的減法,把結(jié)果放置到目的寄存器中。它使用進位標(biāo)志來表示借位,這樣就可以做大于 32 位的減法。SUB 和 SBC 生成進位標(biāo)志的方式不同于常規(guī),如果需要借位則清除進位標(biāo)志。所以,指令要對進位標(biāo)志進行一個非操作 - 在指令執(zhí)行期間自動的反轉(zhuǎn)此位。
SUB : 減法
(Subtraction)
SUB{條件}{S}
SUB 用操作數(shù) one 減去操作數(shù) two,把結(jié)果放置到目的寄存器中。操作數(shù) 1 是一個寄存器,操作數(shù) 2 可以是一個寄存器,被移位的寄存器,或一個立即值:
SUB
減法可以在有符號和無符號數(shù)上進行。
移位指令
譯注:移位操作在 ARM 指令集中不作為單獨的指令使用,它是指令格式中是一個字段,在匯編語言中表示為指令中的選項。如果數(shù)據(jù)處理指令的第二個操作數(shù)或者單一數(shù)據(jù)傳送指令中的變址是寄存器,則可以對它進行各種移位操作。如果數(shù)據(jù)處理指令的第二個操作數(shù)是立即值,在指令中用 8 位立即值和 4 位循環(huán)移位來表示它,所以對大于 255 的立即值,匯編器嘗試通過在指令中設(shè)置循環(huán)移位數(shù)量來表示它,如果不能表示則生成一個錯誤。在邏輯類指令中,邏輯運算指令由指令中 S 位的設(shè)置或清除來確定是否影響進位標(biāo)志,而比較指令的 S 位總是設(shè)置的。在單一數(shù)據(jù)傳送指令中指定移位的數(shù)量只能用立即值而不能用寄存器。
下面是給不同的移位類型的六個助記符:
LSL
ASL 和 LSL 是等同的,可以自由互換。
你可以用一個立即值(從 0 到 31)指定移位數(shù)量,或用包含在 0 和 31 之間的一個值的寄存器指定移位數(shù)量。
邏輯或算術(shù)左移
(Logical or Arithmetic Shift Left)
Rx, LSL #n
接受 Rx 的內(nèi)容并按用‘n’或在寄存器 Rn 中指定的數(shù)量向高有效位方向移位。最低有效位用零來填充。除了概念上的第 33 位(就是被移出的最小的那位)之外丟棄移出最左端的高位,如果邏輯類指令中 S 位被設(shè)置了,則此位將成為從桶式移位器退出時進位標(biāo)志的值。
考慮下列:
MOV
在退出時,R0 是 48。 這些指令形成的總和是 R0 = #12, LSL#2 等同于 BASIC 的 R0 = 12 << 2
邏輯右移
(Logical Shift Right)
Rx, LSR #n
它在概念上與左移相對。把所有位向更低有效位方向移動。如果邏輯類指令中 S 位被設(shè)置了,則把最后被移出最右端的那位放置到進位標(biāo)志中。它同于 BASIC 的 register = value >>> shift。
算術(shù)右移
(Arithmetic Shift Right)
Rx, ASR #n
類似于 LSR,但使用要被移位的寄存器(Rx)的第 31 位的值來填充高位,用來保護補碼表示中的符號。如果邏輯類指令中 S 位被設(shè)置了,則把最后被移出最右端的那位放置到進位標(biāo)志中。它同于 BASIC 的 register = value >> shift。
循環(huán)右移
(Rotate Right)
Rx, ROR #n
循環(huán)右移類似于邏輯右移,但是把從右側(cè)移出去的位放置到左側(cè),如果邏輯類指令中 S 位被設(shè)置了,則同時放置到進位標(biāo)志中,這就是位的‘循環(huán)’。一個移位量為 32 的操作將導(dǎo)致輸出與輸入完全一致,因為所有位都被移位了 32 個位置,又回到了開始時的位置!
帶擴展的循環(huán)右移
(Rotate Right with extend)
Rx, RRX
這是一個 ROR#0 操作,它向右移動一個位置 - 不同之處是,它使用處理器的進位標(biāo)志來提供一個要被移位的 33 位的數(shù)量。
乘法指令
指令格式
這兩個指令與普通算術(shù)指令在對操作數(shù)的限制上有所不同:
給出的所有操作數(shù)、和目的寄存器必須為簡單的寄存器。
你不能對操作數(shù) 2 使用立即值或被移位的寄存器。
目的寄存器和操作數(shù) 1 必須是不同的寄存器。
最后,你不能指定 R15 為目的寄存器。
MLA : 帶累加的乘法
(Multiplication with Accumulate)
MLA{條件}{S}
MLA 的行為同于 MUL,但它把操作數(shù) 3 的值加到結(jié)果上。這在求總和時有用。
MUL : 乘法
(Multiplication)
MUL{條件}{S}
MUL 提供 32 位整數(shù)乘法。如果操作數(shù)是有符號的,可以假定結(jié)果也是有符號的。
比較指令
指令格式
譯注:CMP 和 CMP 是算術(shù)指令,TEQ 和 TST 是邏輯指令。把它們歸入一類的原因是它們的 S 位總是設(shè)置的,就是說,它們總是影響標(biāo)志位。
CMN : 比較取負的值
(Compare Negative)
CMN{條件}{P}
CMN 同于 CMP,但它允許你與小負值(操作數(shù) 2 的取負的值)進行比較,比如難于用其他方法實現(xiàn)的用于結(jié)束列表的 -1。這樣與 -1 比較將使用:
CMN
詳情參照 CMP 指令。
CMP : 比較
(Compare)
CMP{條件}{P}
CMP 允許把一個寄存器的內(nèi)容如另一個寄存器的內(nèi)容或立即值進行比較,更改狀態(tài)標(biāo)志來允許進行條件執(zhí)行。它進行一次減法,但不存儲結(jié)果,而是正確的更改標(biāo)志。標(biāo)志表示的是操作數(shù) 1 比操作數(shù) 2 如何(大小等)。如果操作數(shù) 1 大于操作操作數(shù) 2,則此后的有 GT 后綴的指令將可以執(zhí)行。
明顯的,你不需要顯式的指定 S 后綴來更改狀態(tài)標(biāo)志... 如果你指定了它則被忽略。
TEQ : 測試等價
(Test Equivalence)
TEQ{條件}{P}
TEQ 類似于 TST。區(qū)別是這里的概念上的計算是 EOR 而不是 AND。這提供了一種查看兩個操作數(shù)是否相同而又不影響進位標(biāo)志(不象 CMP那樣)的方法。加上 P 后綴的 TEQ 還可用于改變 R15 中的標(biāo)志(在 26-bit 模式中)。詳情請參照 psr.html,在 32-bit 模式下如何做請參見這里。
TST : 測試位
(Test bits)
TST{條件}{P}
TST 類似于 CMP,不產(chǎn)生放置到目的寄存器中的結(jié)果。而是在給出的兩個操作數(shù)上進行操作并把結(jié)果反映到狀態(tài)標(biāo)志上。使用 TST 來檢查是否設(shè)置了特定的位。操作數(shù) 1 是要測試的數(shù)據(jù)字而操作數(shù) 2 是一個位掩碼。經(jīng)過測試后,如果匹配則設(shè)置 Zero 標(biāo)志,否則清除它。象 CMP 那樣,你不需要指定 S 后綴。
TST
分支指令
B : 分支
(Branch)
B{條件}
B 是最簡單的分支。一旦遇到一個 B 指令,ARM 處理器將立即跳轉(zhuǎn)到給定的地址,從那里繼續(xù)執(zhí)行。
注意存儲在分支指令中的實際的值是相對當(dāng)前的 R15 的值的一個偏移量;而不是一個絕對地址。
它的值由匯編器來計算,它是 24 位有符號數(shù),左移兩位后有符號擴展為 32 位,表示的有效偏移為 26 位(+/- 32 M)。
在其他處理器上,你可能經(jīng)常見到這樣的指令:
OPT 1
(取自 Acorn Electron User Guide issue 1 page 213)
在 ARM 處理器上,它們將變成下面這些東西:
OPT
這不是一個很好的例子,但你可以構(gòu)想如何更好的去條件執(zhí)行而不是分支。另一方面,如果你有大段的代碼或者你的代碼使用狀態(tài)標(biāo)志,那么你可以使用條件執(zhí)行來實現(xiàn)各類分支: 這樣一個單一的簡單條件執(zhí)行指令可以替代在其他處理器中存在的所有這些分支和跳轉(zhuǎn)指令。
OPT
BL : 帶連接的分支
(Branch with Link)
BL{條件}
BL 是另一個分支指令。就在分支之前,在寄存器 14 中裝載上 R15 的內(nèi)容。你可以重新裝載 R14 到 R15 中來返回到在這個分支之后的那個指令,
它是子例程的一個基本但強力的實現(xiàn)。它的作用在屏幕裝載器 2 (例子 4)中得以很好的展現(xiàn)...
.load_new_format
...在這里我們見到在裝載器循環(huán)之前調(diào)用了三個子例程。接著,一旦滿足了條件執(zhí)行就在循環(huán)中調(diào)用了 read_byte 子例程。
條件執(zhí)行
ARM 處理器的一個非常特殊的特征是它的條件執(zhí)行。我們指的不是基本的如果進位則分支,ARM 使這個邏輯階段進一步深化為如果進位則 XXX- 這里的 XXX 是任何東西。
為了舉例,下面是 Intel 8086 處理器分支指令的一個列表:
JA
80386 添加了:
作為對比,ARM 處理器只提供了:
B
但 ARM 提供了條件執(zhí)行,你可以不受這個表面上不靈活的方式的限制:
BEQ
還有兩個代碼,
AL - ALways,缺省條件所以不須指定
NV - NeVer,不是非常有用。你無論如何不要使用這個代碼...
當(dāng)你發(fā)現(xiàn)所有 Bxx 指令實際上是同一個指令的時候,緊要關(guān)頭就到了。
接著你會想,如果你可以在一個分支指令上加上所有這些條件,那么對一個寄存器裝載指令能否加上它們? 答案是可以。
下面是可獲得的條件代碼的列表:
EQ : 等于
如果一次比較之后設(shè)置了 Z 標(biāo)志。
NE : 不等于
如果一次比較之后清除了 Z 標(biāo)志。
VS : 溢出設(shè)置
如果在一次算術(shù)操作之后設(shè)置了 V 標(biāo)志,計算的結(jié)果不適合放入一個 32bit 目標(biāo)寄存器中。
VC : 溢出清除
如果清除了 V 標(biāo)志,與 VS 相反。
HI : 高于(無符號)
如果一次比較之后設(shè)置了 C 標(biāo)志并清除了 Z 標(biāo)志。
LS : 低于或同于(無符號)
如果一次比較操作之后清除了 C 標(biāo)志或設(shè)置了 Z 標(biāo)志。
PL : 正號
如果一次算術(shù)操作之后清除了 N。出于定義‘正號’的目的,零是正數(shù)的原因是它不是負數(shù)...
MI : 負號
如果一次算術(shù)操作之后設(shè)置了 N 標(biāo)志。
CS : 進位設(shè)置
如果一次算術(shù)操作或移位操作之后設(shè)置了 C 標(biāo)志,操作的結(jié)果不能表示為 32bit。你可以把 C 標(biāo)志當(dāng)作結(jié)果的第 33 位。
CC : 進位清除
與 CS 相反。
GE : 大于或等于(有符號)
如果一次比較之后...
設(shè)置了 N 標(biāo)志并設(shè)置了 V 標(biāo)志
或者...
清除了 N 標(biāo)志并清除了 V 標(biāo)志。
GT : 大于(有符號)
如果一次比較之后...
設(shè)置了 N 標(biāo)志并設(shè)置了 V 標(biāo)志
或者...
清除了 N 標(biāo)志并清除了 V 標(biāo)志
并且...
清除了 Z 標(biāo)志。
LE : 小于或等于(有符號)
如果一次比較之后...
設(shè)置了 N 標(biāo)志并清除了 V 標(biāo)志
或者...
清除了 N 標(biāo)志并設(shè)置了 V 標(biāo)志
并且...
設(shè)置了 Z 標(biāo)志。
LT : 小于(有符號)
如果一次比較之后...
設(shè)置了 N 標(biāo)志并清除了 V 標(biāo)志。
或者...
清除了 N 標(biāo)志并設(shè)置了 V 標(biāo)志。
AL : 總是
缺省條件,所以不用明顯聲明。
NV : 從不
不是特別有用,它表示應(yīng)當(dāng)永遠不執(zhí)行這個指令。是窮人的 NOP。
包含 NV 是為了完整性(與 AL 相對),你不應(yīng)該在你的代碼中使用它。
有一個在最后的條件代碼 S,它以相反的方式工作。當(dāng)用于一個指令的時候,導(dǎo)致更改狀態(tài)標(biāo)志。這不是自動發(fā)生的 - 除非這些指令的目的是設(shè)置狀態(tài)。例如:
ADD
第一個例子是一個基本的加法(把 R1 的值增加到 R0),它不影響狀態(tài)寄存器。
第二個例子是同一個加法,只不過它導(dǎo)致更改狀態(tài)寄存器。
最后一個例子是同一個加法,更改狀態(tài)寄存器。不同在于它是一個有條件的指令。只有前一個操作的結(jié)果是 EQ (如果設(shè)置了 Z 標(biāo)志)的時候它才執(zhí)行。
下面是條件執(zhí)行的一個工作中的例子。你把寄存器 0 與存儲在寄存器 10 中內(nèi)容相比較。
如果不等于 R10,則調(diào)用一個軟件中斷,增加它并分支回來再次做這些。否則清除 R10 并返回到調(diào)用它的那部分代碼(它的地址存儲在 R14)。
條件執(zhí)行的一個例子
注解:
SWI 編號就象我寫的這樣。在 RISC OS 下,它是給 Econet_DoImmediate 的編號。不要字面的接受它,這只是一個例子!
你可能以前沒見過 LDMFD,它從棧中裝載多個寄存器。在這個例子中,我們從一個完全正式的棧中裝載 R0 至 R12 和 R14。關(guān)于寄存器裝載和存儲的更多信息請參閱 str.html。
我說要裝載 R14。那么為什么要把它放入 PC 中? 原因是此時 R14 存儲的值包含返回地址。我們也可以采用:
LDMFD
但是直接恢復(fù)到 PC 中可以省略這個 MOV 語句。
最后,這些寄存器很有可能被一個 SWI 調(diào)用所占用(依賴于在調(diào)用期間執(zhí)行的代碼),所以你最好把你的重要的寄存器壓入棧中,以后在恢復(fù)它們。
SWI 指令
SWI : 軟件中斷
(Software Interrupt)
SWI{條件}
指令格式
這是一個簡單的設(shè)施,但可能是最常用的。多數(shù)操作系統(tǒng)設(shè)施是用 SWI 提供的。沒有 SWI 的 RISC OS 是不可想象的。
Nava Whiteford 解釋了 SWI 是如何工作的(最初在 Frobnicate issue 12?)...
SWI 是什么?
SWI 表示 Software Interrupt。在 RISC OS
SWI 的例子有:
文件器 SWI,它輔助讀寫磁盤、設(shè)置屬性等。
打印機驅(qū)動器 SWI,用來輔助使用打印并行端口。
FreeNet/Acorn TCP/IP 協(xié)議棧 SWI,用 TCP/IP 協(xié)議在 Internet 上發(fā)送和接收數(shù)據(jù)。
在以這種方式使用的時候,SWI 允許操作系統(tǒng)擁有一個模塊結(jié)構(gòu),這意味著用來建立完整的操作系統(tǒng)的所需的代碼可以被分割成許多小的部分(模塊)和一個模塊處理程序(handler)。
當(dāng) SWI 處理程序得到對特定的例程編號的一個請求的時候,它找到這個例程的位置并執(zhí)行它,并傳遞(有關(guān)的)任何數(shù)據(jù)。
它是如何工作的?
首先查看一下如何使用它。一個 SWI 指令(匯編語言)看起來如下:
SWI &02
或
SWI "OS_Write0"
這些指令實際上是相同的,將被匯編成相同的指令。唯一的不同是第二個指令使用一個字符串來表示 SWI 編號 &02。
在使用采用了字符串編號的程序的時候,在執(zhí)行之前首先查找這個字符串。
在這里我們不想處理字符串,因為它不能給出它要進行什么的一個真實表示。它們通常用于增進一個程序的清晰程度,但不是實際執(zhí)行的指令。
讓我們再次看一下第一個指令:
SWI &02
這是什么意思? 字面的意思是進入 SWI 處理程序并傳遞值 &02。在 RISC OS 中這意味著執(zhí)行編號是 &02 的例程。
它是如何這么作的? 它如何傳遞 SWI 編號和進入 SWI 處理程序?
如果你查看內(nèi)存的開始 32 字節(jié)(位于 0-&1C)并反匯編它們(查開實際的 ARM 指令)你將見到如下:
地址
00000000 : 0..? : E5000030 : STR
00000004 : .ó?? : E59FF31C : LDR
00000008 : .ó?? : E59FF31C : LDR
0000000C : .ó?? : E59FF31C : LDR
00000010 : .ó?? : E59FF31C : LDR
00000014 : .ó?? : E59FF31C : LDR
00000018 : .ó?? : E59FF31C : LDR
0000001C :
讓我們仔細看一下。
除了第一個和最后一個指令之外(它們是特殊情況)你見到的都是把一個新值裝載到 PC (程序計數(shù)器)的指令,它們告訴計算機到哪里去執(zhí)行下一個指令。
還展示了這個值是從內(nèi)存中的一個地址接受來的。(你可以在 !Zap 主菜單上使用“Read Memory”選項去自己查看一下。)
這看起來好象與 SWI 沒多少關(guān)系,下面做進一步的說明。
一個 SWI 所做的一切就是把模式改變成超級用戶并設(shè)置 PC 來執(zhí)行在地址 &08 處的下一個指令!
把處理器轉(zhuǎn)換到超級用戶模式會切換掉兩個寄存器 r13 和 r14 并用 r13_svc 和 r14_svc 替換它們。
在進入超級用戶模式的時候,還把 r14_svc 設(shè)置為在這個 SWI 指令之后的地址。
這個實際上就象一個連接到地址 &08 的分支指令(BL &08),但帶有用于一些數(shù)據(jù)(SWI 編號)的空間。
象我說過的那樣,地址 &08 包含跳轉(zhuǎn)到另一個地址的一個指令,就是實際的 SWI 程序的地址!
此時你可能會想“稍等一會! 還有 SWI 編號呢?”。實際上處理器忽略這個值本身。SWI 處理程序使用傳遞來的 r14_svc 的值來獲取它。
下面是完成它的步驟(在存儲寄存器 r0-r12 之后):
它從 r14 中減去 4 來獲得 SWI 指令的地址。
把這個指令裝載到一個寄存器。
清除這個指令的高端 8 位,去掉了 OpCode 而只剩下的 SWI 編號。
使用這個值來找到要被執(zhí)行的代碼的例程的地址(使用查找表等)。
恢復(fù)寄存器 r0-r12。
使處理器離開超級用戶模式。
跳轉(zhuǎn)到這個例程的地址。
容易吧! ;)
下面是一個例子,來自 ARM610 datasheet:
0x08 B Supervisor
EntryTable
Zero
ReadC
WriteI EQU 512
; SWI 包含需要的例程在位 8-23 中和數(shù)據(jù)(如果有的話)在位 0-7 中。
; 假定 R13_svc 指向了一個合適的棧
STMFD R13, {r0-r2 , R14}
LDR R0,[R14,#-4]
BIC R0,R0, #0xFF000000
MOV R1, R0, LSR #8
ADR R2, EntryTable
LDR R15,[R2,R1,LSL #2]
WriteIRtn
.............
這就是 SWI 指令的基本處理步驟。
評論