新聞中心

ARM指令詳解

作者: 時(shí)間:2016-11-20 來源:網(wǎng)絡(luò) 收藏
算術(shù)和邏輯指令

ADC : 帶進(jìn)位的加法
(Addition with Carry)

本文引用地址:http://butianyuan.cn/article/201611/318752.htm

ADC{條件}{S}, ,

dest = op_1 + op_2 + carry
ADC 將把兩個(gè)操作數(shù)加起來,并把結(jié)果放置到目的寄存器中。它使用一個(gè)進(jìn)位標(biāo)志位,這樣就可以做比 32 位大的加法。下列例子將加兩個(gè) 128 位的數(shù)。
128 位結(jié)果: 寄存器 0、1、2、和 3
第一個(gè) 128 位數(shù): 寄存器 4、5、6、和 7
第二個(gè) 128 位數(shù): 寄存器 8、9、10、和 11。

ADDSR0, R4, R8; 加低端的字
ADCSR1, R5, R9; 加下一個(gè)字,帶進(jìn)位
ADCSR2, R6, R10; 加第三個(gè)字,帶進(jìn)位
ADCSR3, R7, R11; 加高端的字,帶進(jìn)位
如果如果要做這樣的加法,不要忘記設(shè)置 S 后綴來更改進(jìn)位標(biāo)志。

 


ADD : 加法
(Addition)

ADD{條件}{S}, ,

dest = op_1 + op_2
ADD 將把兩個(gè)操作數(shù)加起來,把結(jié)果放置到目的寄存器中。操作數(shù) 1 是一個(gè)寄存器,操作數(shù) 2 可以是一個(gè)寄存器,被移位的寄存器,或一個(gè)立即值:

ADDR0, R1, R2; R0 = R1 + R2
ADDR0, R1, #256; R0 = R1 + 256
ADDR0, R2, R3,LSL#1; R0 = R2 + (R3 << 1)
加法可以在有符號和無符號數(shù)上進(jìn)行。

 

AND : 邏輯與
(logical AND)

AND{條件}{S}, ,

dest = op_1 AND op_2
AND 將在兩個(gè)操作數(shù)上進(jìn)行邏輯與,把結(jié)果放置到目的寄存器中;對屏蔽你要在上面工作的位很有用。 操作數(shù) 1 是一個(gè)寄存器,操作數(shù) 2 可以是一個(gè)寄存器,被移位的寄存器,或一個(gè)立即值:

ANDR0, R0, #3; R0 = 保持 R0 的位 0 和 1,丟棄其余的位。
AND 的真值表(二者都是 1 則結(jié)果為 1):

Op_1Op_2結(jié)果

000
010
100
111
 
BIC : 位清除
(Bit Clear)

BIC{條件}{S}, ,

dest = op_1 AND (!op_2)
BIC 是在一個(gè)字中清除位的一種方法,與 OR 位設(shè)置是相反的操作。操作數(shù) 2 是一個(gè) 32 位位掩碼(mask)。如果如果在掩碼中設(shè)置了某一位,則清除這一位。未設(shè)置的掩碼位指示此位保持不變。

BICR0, R0, #11; 清除 R0 中的位 0、1、和 3。保持其余的不變。
BIC 真值表 :

Op_1Op_2結(jié)果

000
010
101
110
譯注:邏輯表達(dá)式為 Op_1 AND NOT Op_2
 

EOR : 邏輯異或
(logical Exclusive OR)

EOR{條件}{S}, ,

dest = op_1 EOR op_2
EOR 將在兩個(gè)操作數(shù)上進(jìn)行邏輯異或,把結(jié)果放置到目的寄存器中;對反轉(zhuǎn)特定的位有用。操作數(shù) 1 是一個(gè)寄存器,操作數(shù) 2 可以是一個(gè)寄存器,被移位的寄存器,或一個(gè)立即值:

EORR0, R0, #3; 反轉(zhuǎn) R0 中的位 0 和 1
EOR 真值表(二者不同則結(jié)果為 1):

Op_1Op_2結(jié)果

000
011
101
110
 
MOV : 傳送
(Move)

MOV{條件}{S},

dest = op_1
MOV 從另一個(gè)寄存器、被移位的寄存器、或一個(gè)立即值裝載一個(gè)值到目的寄存器。你可以指定相同的寄存器來實(shí)現(xiàn) NOP 指令的效果,你還可以專門移位一個(gè)寄存器:

MOVR0, R0; R0 = R0... NOP 指令

MOVR0, R0, LSL#3; R0 = R0 * 8
如果 R15 是目的寄存器,將修改程序計(jì)數(shù)器或標(biāo)志。這用于返回到調(diào)用代碼,方法是把連接寄存器的內(nèi)容傳送到 R15:

MOVPC, R14; 退出到調(diào)用者

MOVSPC, R14; 退出到調(diào)用者并恢復(fù)標(biāo)志位
(不遵從 32-bit 體系)
 
MVN : 傳送取反的值
(MoveNegative)

MVN{條件}{S},

dest = !op_1
MVN 從另一個(gè)寄存器、被移位的寄存器、或一個(gè)立即值裝載一個(gè)值到目的寄存器。不同之處是在傳送之前位被反轉(zhuǎn)了,所以把一個(gè)被取反的值傳送到一個(gè)寄存器中。這是邏輯非操作而不是算術(shù)操作,這個(gè)取反的值加 1 才是它的取負(fù)的值:

MVNR0, #4; R0 = -5

MVNR0, #0; R0 = -1
 
ORR : 邏輯或
(logical OR)

ORR{條件}{S}, ,

dest = op_1 OR op_2
OR 將在兩個(gè)操作數(shù)上進(jìn)行邏輯或,把結(jié)果放置到目的寄存器中;對設(shè)置特定的位有用。操作數(shù) 1 是一個(gè)寄存器,操作數(shù) 2 可以是一個(gè)寄存器,被移位的寄存器,或一個(gè)立即值:

ORRR0, R0, #3; 設(shè)置 R0 中位 0 和 1
OR 真值表(二者中存在 1 則結(jié)果為 1):

Op_1Op_2結(jié)果

000
011
101
111
 
RSB : 反向減法
(Reverse Subtraction)

RSB{條件}{S}, ,

dest = op_2 - op_1
SUB 用操作數(shù) two 減去操作數(shù) one,把結(jié)果放置到目的寄存器中。操作數(shù) 1 是一個(gè)寄存器,操作數(shù) 2 可以是一個(gè)寄存器,被移位的寄存器,或一個(gè)立即值:

RSBR0, R1, R2; R0 = R2 - R1
RSBR0, R1, #256; R0 = 256 - R1
RSBR0, R2, R3,LSL#1; R0 = (R3 << 1) - R2
反向減法可以在有符號或無符號數(shù)上進(jìn)行。

 

RSC : 帶借位的反向減法
(Reverse Subtraction with Carry)

RSC{條件}{S}, ,

dest = op_2 - op_1 - !carry
同于 SBC,但倒換了兩個(gè)操作數(shù)的前后位置。

 

SBC : 帶借位的減法
(Subtraction with Carry)

SBC{條件}{S}, ,

dest = op_1 - op_2 - !carry
SBC 做兩個(gè)操作數(shù)的減法,把結(jié)果放置到目的寄存器中。它使用進(jìn)位標(biāo)志來表示借位,這樣就可以做大于 32 位的減法。SUB 和 SBC 生成進(jìn)位標(biāo)志的方式不同于常規(guī),如果需要借位則清除進(jìn)位標(biāo)志。所以,指令要對進(jìn)位標(biāo)志進(jìn)行一個(gè)非操作 - 在指令執(zhí)行期間自動的反轉(zhuǎn)此位。

 

SUB : 減法
(Subtraction)

SUB{條件}{S}, ,

dest = op_1 - op_2
SUB 用操作數(shù) one 減去操作數(shù) two,把結(jié)果放置到目的寄存器中。操作數(shù) 1 是一個(gè)寄存器,操作數(shù) 2 可以是一個(gè)寄存器,被移位的寄存器,或一個(gè)立即值:

SUBR0, R1, R2; R0 = R1 - R2
SUBR0, R1, #256; R0 = R1 - 256
SUBR0, R2, R3,LSL#1; R0 = R2 - (R3 << 1)
減法可以在有符號和無符號數(shù)上進(jìn)行。

移位指令
ARM 處理器組建了可以與數(shù)據(jù)處理指令(ADC、ADD、AND、BIC、CMN、CMP、EOR、MOV、MVN、ORR、RSB、SBC、SUB、TEQ、TST)一起使用的桶式移位器(barrel shifter)。你還可以使用桶式移位器影響在 LDR/STR 操作中的變址值。

譯注:移位操作在 ARM 指令集中不作為單獨(dú)的指令使用,它是指令格式中是一個(gè)字段,在匯編語言中表示為指令中的選項(xiàng)。如果數(shù)據(jù)處理指令的第二個(gè)操作數(shù)或者單一數(shù)據(jù)傳送指令中的變址是寄存器,則可以對它進(jìn)行各種移位操作。如果數(shù)據(jù)處理指令的第二個(gè)操作數(shù)是立即值,在指令中用 8 位立即值和 4 位循環(huán)移位來表示它,所以對大于 255 的立即值,匯編器嘗試通過在指令中設(shè)置循環(huán)移位數(shù)量來表示它,如果不能表示則生成一個(gè)錯誤。在邏輯類指令中,邏輯運(yùn)算指令由指令中 S 位的設(shè)置或清除來確定是否影響進(jìn)位標(biāo)志,而比較指令的 S 位總是設(shè)置的。在單一數(shù)據(jù)傳送指令中指定移位的數(shù)量只能用立即值而不能用寄存器。

下面是給不同的移位類型的六個(gè)助記符:

LSL邏輯左移
ASL算術(shù)左移
LSR邏輯右移
ASR算術(shù)右移
ROR循環(huán)右移
RRX帶擴(kuò)展的循環(huán)右移
ASL 和 LSL 是等同的,可以自由互換。

你可以用一個(gè)立即值(從 0 到 31)指定移位數(shù)量,或用包含在 0 和 31 之間的一個(gè)值的寄存器指定移位數(shù)量。

邏輯或算術(shù)左移
(Logical or Arithmetic Shift Left)

Rx, LSL #nor
Rx, ASL #nor
Rx, LSL Rnor
Rx, ASL Rn
接受 Rx 的內(nèi)容并按用‘n’或在寄存器 Rn 中指定的數(shù)量向高有效位方向移位。最低有效位用零來填充。除了概念上的第 33 位(就是被移出的最小的那位)之外丟棄移出最左端的高位,如果邏輯類指令中 S 位被設(shè)置了,則此位將成為從桶式移位器退出時(shí)進(jìn)位標(biāo)志的值。

考慮下列:

MOVR1, #12
MOVR0, R1, LSL#2
在退出時(shí),R0 是 48。 這些指令形成的總和是 R0 = #12, LSL#2 等同于 BASIC 的 R0 = 12 << 2

邏輯右移
(Logical Shift Right)

Rx, LSR #nor
Rx, LSR Rn
它在概念上與左移相對。把所有位向更低有效位方向移動。如果邏輯類指令中 S 位被設(shè)置了,則把最后被移出最右端的那位放置到進(jìn)位標(biāo)志中。它同于 BASIC 的 register = value >>> shift。

算術(shù)右移
(Arithmetic Shift Right)

Rx, ASR #nor
Rx, ASR Rn
類似于 LSR,但使用要被移位的寄存器(Rx)的第 31 位的值來填充高位,用來保護(hù)補(bǔ)碼表示中的符號。如果邏輯類指令中 S 位被設(shè)置了,則把最后被移出最右端的那位放置到進(jìn)位標(biāo)志中。它同于 BASIC 的 register = value >> shift。

循環(huán)右移
(Rotate Right)

Rx, ROR #nor
Rx, ROR Rn
循環(huán)右移類似于邏輯右移,但是把從右側(cè)移出去的位放置到左側(cè),如果邏輯類指令中 S 位被設(shè)置了,則同時(shí)放置到進(jìn)位標(biāo)志中,這就是位的‘循環(huán)’。一個(gè)移位量為 32 的操作將導(dǎo)致輸出與輸入完全一致,因?yàn)樗形欢急灰莆涣?32 個(gè)位置,又回到了開始時(shí)的位置!

帶擴(kuò)展的循環(huán)右移
(Rotate Right with extend)

Rx, RRX
這是一個(gè) ROR#0 操作,它向右移動一個(gè)位置 - 不同之處是,它使用處理器的進(jìn)位標(biāo)志來提供一個(gè)要被移位的 33 位的數(shù)量。

乘法指令
指令格式

這兩個(gè)指令與普通算術(shù)指令在對操作數(shù)的限制上有所不同:

給出的所有操作數(shù)、和目的寄存器必須為簡單的寄存器。
你不能對操作數(shù) 2 使用立即值或被移位的寄存器。
目的寄存器和操作數(shù) 1 必須是不同的寄存器。
最后,你不能指定 R15 為目的寄存器。
MLA : 帶累加的乘法
(Multiplication with Accumulate)

MLA{條件}{S}, , ,

dest = (op_1 * op_2) + op_3
MLA 的行為同于 MUL,但它把操作數(shù) 3 的值加到結(jié)果上。這在求總和時(shí)有用。

 

MUL : 乘法
(Multiplication)

MUL{條件}{S}, ,

dest = op_1 * op_2
MUL 提供 32 位整數(shù)乘法。如果操作數(shù)是有符號的,可以假定結(jié)果也是有符號的。

比較指令
指令格式

譯注:CMP 和 CMP 是算術(shù)指令,TEQ 和 TST 是邏輯指令。把它們歸入一類的原因是它們的 S 位總是設(shè)置的,就是說,它們總是影響標(biāo)志位。

CMN : 比較取負(fù)的值
(Compare Negative)

CMN{條件}{P},

status = op_1 - (- op_2)
CMN 同于 CMP,但它允許你與小負(fù)值(操作數(shù) 2 的取負(fù)的值)進(jìn)行比較,比如難于用其他方法實(shí)現(xiàn)的用于結(jié)束列表的 -1。這樣與 -1 比較將使用:

CMNR0, #1; 把 R0 與 -1 進(jìn)行比較
詳情參照 CMP 指令。

 

CMP : 比較
(Compare)

CMP{條件}{P},

status = op_1 - op_2
CMP 允許把一個(gè)寄存器的內(nèi)容如另一個(gè)寄存器的內(nèi)容或立即值進(jìn)行比較,更改狀態(tài)標(biāo)志來允許進(jìn)行條件執(zhí)行。它進(jìn)行一次減法,但不存儲結(jié)果,而是正確的更改標(biāo)志。標(biāo)志表示的是操作數(shù) 1 比操作數(shù) 2 如何(大小等)。如果操作數(shù) 1 大于操作操作數(shù) 2,則此后的有 GT 后綴的指令將可以執(zhí)行。
明顯的,你不需要顯式的指定 S 后綴來更改狀態(tài)標(biāo)志... 如果你指定了它則被忽略。

 

TEQ : 測試等價(jià)
(Test Equivalence)

TEQ{條件}{P},

Status = op_1 EOR op_2
TEQ 類似于 TST。區(qū)別是這里的概念上的計(jì)算是 EOR 而不是 AND。這提供了一種查看兩個(gè)操作數(shù)是否相同而又不影響進(jìn)位標(biāo)志(不象 CMP那樣)的方法。加上 P 后綴的 TEQ 還可用于改變 R15 中的標(biāo)志(在 26-bit 模式中)。詳情請參照 psr.html,在 32-bit 模式下如何做請參見這里。

 

TST : 測試位
(Test bits)

TST{條件}{P},

Status = op_1 AND op_2
TST 類似于 CMP,不產(chǎn)生放置到目的寄存器中的結(jié)果。而是在給出的兩個(gè)操作數(shù)上進(jìn)行操作并把結(jié)果反映到狀態(tài)標(biāo)志上。使用 TST 來檢查是否設(shè)置了特定的位。操作數(shù) 1 是要測試的數(shù)據(jù)字而操作數(shù) 2 是一個(gè)位掩碼。經(jīng)過測試后,如果匹配則設(shè)置 Zero 標(biāo)志,否則清除它。象 CMP 那樣,你不需要指定 S 后綴。

TSTR0, #%1; 測試在 R0 中是否設(shè)置了位 0。

分支指令
B : 分支
(Branch)
B{條件}<地址>
B 是最簡單的分支。一旦遇到一個(gè) B 指令,ARM 處理器將立即跳轉(zhuǎn)到給定的地址,從那里繼續(xù)執(zhí)行。
注意存儲在分支指令中的實(shí)際的值是相對當(dāng)前的 R15 的值的一個(gè)偏移量;而不是一個(gè)絕對地址。
它的值由匯編器來計(jì)算,它是 24 位有符號數(shù),左移兩位后有符號擴(kuò)展為 32 位,表示的有效偏移為 26 位(+/- 32 M)。
在其他處理器上,你可能經(jīng)常見到這樣的指令:
OPT 1
LDA &70
CMP #0
BEQ Zero
STA &72
.Zero RTS
(取自 Acorn Electron User Guide issue 1 page 213)
在 ARM 處理器上,它們將變成下面這些東西:
OPT1
ADRR1, #&70
LDRR0, [R1]
CMP#0
BEQZero
STRR0, [R1, #2]
.Zero
MOVPC, R14
這不是一個(gè)很好的例子,但你可以構(gòu)想如何更好的去條件執(zhí)行而不是分支。另一方面,如果你有大段的代碼或者你的代碼使用狀態(tài)標(biāo)志,那么你可以使用條件執(zhí)行來實(shí)現(xiàn)各類分支: 這樣一個(gè)單一的簡單條件執(zhí)行指令可以替代在其他處理器中存在的所有這些分支和跳轉(zhuǎn)指令。
OPT1
ADRR1, #&70
LDRR0, [R1]
CMPR0, #0
STRNER0, [R1, #2]
MOVPC, R14
BL : 帶連接的分支

(Branch with Link)

BL{條件}<地址>

BL 是另一個(gè)分支指令。就在分支之前,在寄存器 14 中裝載上 R15 的內(nèi)容。你可以重新裝載 R14 到 R15 中來返回到在這個(gè)分支之后的那個(gè)指令,

它是子例程的一個(gè)基本但強(qiáng)力的實(shí)現(xiàn)。它的作用在屏幕裝載器 2 (例子 4)中得以很好的展現(xiàn)...

.load_new_format
BLswitch_screen_mode
BLget_screen_info
BLload_palette

.new_loop
MOVR1, R5
BLread_byte
CMPR0, #255
BLEQread_loop
STRBR0, [R2, #1]!

...在這里我們見到在裝載器循環(huán)之前調(diào)用了三個(gè)子例程。接著,一旦滿足了條件執(zhí)行就在循環(huán)中調(diào)用了 read_byte 子例程。

條件執(zhí)行

 


ARM 處理器的一個(gè)非常特殊的特征是它的條件執(zhí)行。我們指的不是基本的如果進(jìn)位則分支,ARM 使這個(gè)邏輯階段進(jìn)一步深化為如果進(jìn)位則 XXX- 這里的 XXX 是任何東西。
為了舉例,下面是 Intel 8086 處理器分支指令的一個(gè)列表:
JAJump if Above
JAEJump if Above or Equal
JBJump if Below
JBEJump if Below or Equal
JCJump if Carry
JCXZJump if CX Zero (CX is a register that can be used for loop counts)
JEJump if Equal
JGJump if Greater than
JGEJump if Greater than or Equal
JLJump if Less than
JLEJump if Less Than or Equal
JMPJuMP
JNAJump if Not Above
JNAEJump if Not Above or Equal
JNBJump if Not Below
JNBEJump if Not Below or Equal
JNCJump if No Carry
JNEJump if Not Equal
JNGJump if Not Greater than
JNGEJump if Not Greater than or Equal
JNLJump if Not Less than
JNLEJump if Not Less than or Equal
JNOJump if Not Overflow
JNPJump if Not Parity
JNSJump if Not Sign
JNZJump if Not Zero
JOJump if Overflow
JPJump if Parity
JPEJump if Parity Even
JPOJump if Parity Odd
JSJump if Sign
JZJump if Zero

80386 添加了:
JECXZ Jump if ECX Zero

作為對比,ARM 處理器只提供了:
B分支
BL帶連接的分支

但 ARM 提供了條件執(zhí)行,你可以不受這個(gè)表面上不靈活的方式的限制:
BEQBranch if EQual
BNEBranch if Not Equal
BVSBranch if oVerflow Set
BVCBranch if oVerflow Clear
BHIBranch if HIgher
BLSBranch if Lower or the Same
BPLBranch if PLus
BMIBranch if MInus
BCSBranch if Carry Set
BCCBranch if Carry Clear
BGEBranch if Greater than or Equal
BGTBranch if Greater Than
BLEBranch if Less than or Equal
BLTBranch if Less Than

BLEQBranch with Link if EQual
....
BLLTBranch with Link if Less Than

還有兩個(gè)代碼,
AL - ALways,缺省條件所以不須指定


NV - NeVer,不是非常有用。你無論如何不要使用這個(gè)代碼...


當(dāng)你發(fā)現(xiàn)所有 Bxx 指令實(shí)際上是同一個(gè)指令的時(shí)候,緊要關(guān)頭就到了。
接著你會想,如果你可以在一個(gè)分支指令上加上所有這些條件,那么對一個(gè)寄存器裝載指令能否加上它們? 答案是可以。
下面是可獲得的條件代碼的列表:
EQ : 等于
如果一次比較之后設(shè)置了 Z 標(biāo)志。
 
NE : 不等于
如果一次比較之后清除了 Z 標(biāo)志。
 
VS : 溢出設(shè)置
如果在一次算術(shù)操作之后設(shè)置了 V 標(biāo)志,計(jì)算的結(jié)果不適合放入一個(gè) 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ù)的原因是它不是負(fù)數(shù)...
 
MI : 負(fù)號
如果一次算術(shù)操作之后設(shè)置了 N 標(biāo)志。
 
CS : 進(jìn)位設(shè)置
如果一次算術(shù)操作或移位操作之后設(shè)置了 C 標(biāo)志,操作的結(jié)果不能表示為 32bit。你可以把 C 標(biāo)志當(dāng)作結(jié)果的第 33 位。
 
CC : 進(jìn)位清除
與 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)永遠(yuǎn)不執(zhí)行這個(gè)指令。是窮人的 NOP。
包含 NV 是為了完整性(與 AL 相對),你不應(yīng)該在你的代碼中使用它。
有一個(gè)在最后的條件代碼 S,它以相反的方式工作。當(dāng)用于一個(gè)指令的時(shí)候,導(dǎo)致更改狀態(tài)標(biāo)志。這不是自動發(fā)生的 - 除非這些指令的目的是設(shè)置狀態(tài)。例如:
ADDR0, R0, R1

ADDSR0, R0, R1

ADDEQSR0, R0, R1

第一個(gè)例子是一個(gè)基本的加法(把 R1 的值增加到 R0),它不影響狀態(tài)寄存器。
第二個(gè)例子是同一個(gè)加法,只不過它導(dǎo)致更改狀態(tài)寄存器。

最后一個(gè)例子是同一個(gè)加法,更改狀態(tài)寄存器。不同在于它是一個(gè)有條件的指令。只有前一個(gè)操作的結(jié)果是 EQ (如果設(shè)置了 Z 標(biāo)志)的時(shí)候它才執(zhí)行。

下面是條件執(zhí)行的一個(gè)工作中的例子。你把寄存器 0 與存儲在寄存器 10 中內(nèi)容相比較。

如果不等于 R10,則調(diào)用一個(gè)軟件中斷,增加它并分支回來再次做這些。否則清除 R10 并返回到調(diào)用它的那部分代碼(它的地址存儲在 R14)。

條件執(zhí)行的一個(gè)例子

.loop; 標(biāo)記循環(huán)開始位置
CMPR0, R10; 把 R0 與 R10 相比較
SWINE&40017; 不等于: 調(diào)用 SWI &40017
ADDNER0, R0, #1;向 R0 加 1
BNEloop;分支到 loop
MOVR10, #0; 等于: 設(shè)置 R10 為零
LDMFDR13!, {R0-R12,PC};返回到調(diào)用者

注解:
SWI 編號就象我寫的這樣。在 RISC OS 下,它是給 Econet_DoImmediate 的編號。不要字面的接受它,這只是一個(gè)例子!


你可能以前沒見過 LDMFD,它從棧中裝載多個(gè)寄存器。在這個(gè)例子中,我們從一個(gè)完全正式的棧中裝載 R0 至 R12 和 R14。關(guān)于寄存器裝載和存儲的更多信息請參閱 str.html。


我說要裝載 R14。那么為什么要把它放入 PC 中? 原因是此時(shí) R14 存儲的值包含返回地址。我們也可以采用:
LDMFDR13!, {R0-R12,R14}

MOVPC, R14
但是直接恢復(fù)到 PC 中可以省略這個(gè) MOV 語句。


最后,這些寄存器很有可能被一個(gè) SWI 調(diào)用所占用(依賴于在調(diào)用期間執(zhí)行的代碼),所以你最好把你的重要的寄存器壓入棧中,以后在恢復(fù)它們。



SWI 指令


SWI : 軟件中斷

(Software Interrupt)

SWI{條件}<24 位編號>

指令格式
這是一個(gè)簡單的設(shè)施,但可能是最常用的。多數(shù)操作系統(tǒng)設(shè)施是用 SWI 提供的。沒有 SWI 的 RISC OS 是不可想象的。

Nava Whiteford 解釋了 SWI 是如何工作的(最初在 Frobnicate issue 12?)...

 

SWI 是什么?

SWI 表示 Software Interrupt。在 RISC OS中使用 SWI 來訪問操作系統(tǒng)例程或第三方生產(chǎn)的模塊。許多應(yīng)用使用模塊來給其他應(yīng)用提供低層外部訪問。
SWI 的例子有:


文件器 SWI,它輔助讀寫磁盤、設(shè)置屬性等。


打印機(jī)驅(qū)動器 SWI,用來輔助使用打印并行端口。


FreeNet/Acorn TCP/IP 協(xié)議棧 SWI,用 TCP/IP 協(xié)議在 Internet 上發(fā)送和接收數(shù)據(jù)。

在以這種方式使用的時(shí)候,SWI 允許操作系統(tǒng)擁有一個(gè)模塊結(jié)構(gòu),這意味著用來建立完整的操作系統(tǒng)的所需的代碼可以被分割成許多小的部分(模塊)和一個(gè)模塊處理程序(handler)。


當(dāng) SWI 處理程序得到對特定的例程編號的一個(gè)請求的時(shí)候,它找到這個(gè)例程的位置并執(zhí)行它,并傳遞(有關(guān)的)任何數(shù)據(jù)。


它是如何工作的?

首先查看一下如何使用它。一個(gè) SWI 指令(匯編語言)看起來如下:
SWI &02


SWI "OS_Write0"

這些指令實(shí)際上是相同的,將被匯編成相同的指令。唯一的不同是第二個(gè)指令使用一個(gè)字符串來表示 SWI 編號 &02。
在使用采用了字符串編號的程序的時(shí)候,在執(zhí)行之前首先查找這個(gè)字符串。
在這里我們不想處理字符串,因?yàn)樗荒芙o出它要進(jìn)行什么的一個(gè)真實(shí)表示。它們通常用于增進(jìn)一個(gè)程序的清晰程度,但不是實(shí)際執(zhí)行的指令。

讓我們再次看一下第一個(gè)指令:

SWI &02

這是什么意思? 字面的意思是進(jìn)入 SWI 處理程序并傳遞值 &02。在 RISC OS 中這意味著執(zhí)行編號是 &02 的例程。
它是如何這么作的? 它如何傳遞 SWI 編號和進(jìn)入 SWI 處理程序?


如果你查看內(nèi)存的開始 32 字節(jié)(位于 0-&1C)并反匯編它們(查開實(shí)際的 ARM 指令)你將見到如下:


 


地址內(nèi)容反匯編
00000000 : 0..? : E5000030 : STRR0,[R0,#-48]
00000004 : .ó?? : E59FF31C : LDRPC,&00000328
00000008 : .ó?? : E59FF31C : LDRPC,&0000032C
0000000C : .ó?? : E59FF31C : LDRPC,&00000330
00000010 : .ó?? : E59FF31C : LDRPC,&00000334
00000014 : .ó?? : E59FF31C : LDRPC,&00000338
00000018 : .ó?? : E59FF31C : LDRPC,&0000033C
0000001C :2?? : E3A0A632 : MOVR10,#&3200000

讓我們仔細(xì)看一下。
除了第一個(gè)和最后一個(gè)指令之外(它們是特殊情況)你見到的都是把一個(gè)新值裝載到 PC (程序計(jì)數(shù)器)的指令,它們告訴計(jì)算機(jī)到哪里去執(zhí)行下一個(gè)指令。


還展示了這個(gè)值是從內(nèi)存中的一個(gè)地址接受來的。(你可以在 !Zap 主菜單上使用“Read Memory”選項(xiàng)去自己查看一下。)


這看起來好象與 SWI 沒多少關(guān)系,下面做進(jìn)一步的說明。


一個(gè) SWI 所做的一切就是把模式改變成超級用戶并設(shè)置 PC 來執(zhí)行在地址 &08 處的下一個(gè)指令!


把處理器轉(zhuǎn)換到超級用戶模式會切換掉兩個(gè)寄存器 r13 和 r14 并用 r13_svc 和 r14_svc 替換它們。


在進(jìn)入超級用戶模式的時(shí)候,還把 r14_svc 設(shè)置為在這個(gè) SWI 指令之后的地址。


這個(gè)實(shí)際上就象一個(gè)連接到地址 &08 的分支指令(BL &08),但帶有用于一些數(shù)據(jù)(SWI 編號)的空間。


象我說過的那樣,地址 &08 包含跳轉(zhuǎn)到另一個(gè)地址的一個(gè)指令,就是實(shí)際的 SWI 程序的地址!


此時(shí)你可能會想“稍等一會! 還有 SWI 編號呢?”。實(shí)際上處理器忽略這個(gè)值本身。SWI 處理程序使用傳遞來的 r14_svc 的值來獲取它。


下面是完成它的步驟(在存儲寄存器 r0-r12 之后):


它從 r14 中減去 4 來獲得 SWI 指令的地址。


把這個(gè)指令裝載到一個(gè)寄存器。


清除這個(gè)指令的高端 8 位,去掉了 OpCode 而只剩下的 SWI 編號。


使用這個(gè)值來找到要被執(zhí)行的代碼的例程的地址(使用查找表等)。


恢復(fù)寄存器 r0-r12。


使處理器離開超級用戶模式。


跳轉(zhuǎn)到這個(gè)例程的地址。


容易吧! ;)
下面是一個(gè)例子,來自 ARM610 datasheet:


0x08 B Supervisor

EntryTable
DCD ZeroRtn
DCD ReadCRtn
DCD WriteIRtn

...

ZeroEQU 0
ReadCEQU 256
WriteI EQU 512

; SWI 包含需要的例程在位 8-23 中和數(shù)據(jù)(如果有的話)在位 0-7 中。
; 假定 R13_svc 指向了一個(gè)合適的棧

STMFD R13, {r0-r2 , R14}
; 保存工作寄存器和返回地址。
LDR R0,[R14,#-4]
; 得到 SWI 指令。
BIC R0,R0, #0xFF000000
; 清除高端的 8 位。
MOV R1, R0, LSR #8
; 得到例程偏移量。
ADR R2, EntryTable
; 得到入口表(EntryTable)的開始地址。
LDR R15,[R2,R1,LSL #2]
; 分支到正確的例程

WriteIRtn
; 寫 R0 中的位 0 - 7 中的字符。

.............
LDMFD R13, {r0-r2 , R15}^
; 恢復(fù)工作空間,并返回、恢復(fù)處理器模式和標(biāo)志。

這就是 SWI 指令的基本處理步驟。



關(guān)鍵詞: ARM指令詳

評論


技術(shù)專區(qū)

關(guān)閉