算術(shù)和邏輯指令
ADC : 帶進(jìn)位的加法
(Addition with Carry)
ADC{條件}{S} <dest>, <op 1>, <op 2>
dest = op_1 + op_2 + carry
ADC將把兩個操作數(shù)加起來,并把結(jié)果放置到目的寄存器中。它使用一個進(jìn)位標(biāo)志位历葛,這樣就可以做比 32 位大的加法秃踩。下列例子將加兩個 128 位的數(shù)镐依。128 位結(jié)果: 寄存器 0钦听、1、2茅糜、和 3第一個 128 位數(shù): 寄存器 4橄杨、5秘症、6、和 7第二個 128 位數(shù): 寄存器 8式矫、9乡摹、10、和 11采转。
ADDS R0, R4, R8 ; 加低端的字
ADCS R1, R5, R9 ; 加下一個字聪廉,帶進(jìn)位
ADCS R2, R6, R10 ; 加第三個字,帶進(jìn)位
ADCS R3, R7, R11 ; 加高端的字故慈,帶進(jìn)位
如果如果要做這樣的加法板熊,不要忘記設(shè)置 S 后綴來更改進(jìn)位標(biāo)志。
ADD : ****加法
(Addition)
ADD{條件}{S} <dest>, <op 1>, <op 2>
dest = op_1 + op_2
ADD將把兩個操作數(shù)加起來察绷,把結(jié)果放置到目的寄存器中干签。操作數(shù) 1 是一個寄存器,操作數(shù) 2 可以是一個寄存器拆撼,被移位的寄存器容劳,或一個立即值:
ADD R0, R1, R2 ; R0 = R1 + R2
ADD R0, R1, #256 ; R0 = R1 + 256
ADD R0, R2, R3,LSL#1 ; R0 = R2 + (R3 << 1)
加法可以在有符號和無符號數(shù)上進(jìn)行喘沿。
AND : 邏輯與
(logical AND)
AND{條件}{S} <dest>, <op 1>, <op 2>
dest = op_1 AND op_2
AND將在兩個操作數(shù)上進(jìn)行邏輯與,把結(jié)果放置到目的寄存器中竭贩;對屏蔽你要在上面工作的位很有用蚜印。操作數(shù) 1 是一個寄存器,操作數(shù) 2 可以是一個寄存器娶视,被移位的寄存器,或一個立即值:
AND R0, R0, #3 ; R0 = 保持 R0 的位 0 和 1睁宰,丟棄其余的位肪获。
AND 的真值表(二者都是 1 則結(jié)果為 1):
Op_1 Op_2 結(jié)果
0 0 0
0 1 0
1 0 0
1 1 1
BIC : 位清除
(Bit Clear)
BIC{條件}{S} <dest>, <op 1>, <op 2>
dest = op_1 AND (!op_2)
BIC是在一個字中清除位的一種方法,與 OR 位設(shè)置是相反的操作柒傻。操作數(shù) 2 是一個 32 位位掩碼(mask)孝赫。如果如果在掩碼中設(shè)置了某一位,則清除這一位红符。未設(shè)置的掩碼位指示此位保持不變青柄。
BIC R0, R0, #%1011 ; 清除 R0 中的位 0、1预侯、和 3致开。保持其余的不變。
BIC 真值表 :
Op_1 Op_2 結(jié)果
0 0 0
0 1 0
1 0 1
1 1 0
譯注:邏輯表達(dá)式為 Op_1 AND NOT Op_2
EOR : 邏輯異或
(logical Exclusive OR)
EOR{條件}{S} <dest>, <op 1>, <op 2>
dest = op_1 EOR op_2
EOR將在兩個操作數(shù)上進(jìn)行邏輯異或萎馅,把結(jié)果放置到目的寄存器中双戳;對反轉(zhuǎn)特定的位有用。操作數(shù) 1 是一個寄存器糜芳,操作數(shù) 2 可以是一個寄存器飒货,被移位的寄存器,或一個立即值:
EOR R0, R0, #3 ; 反轉(zhuǎn) R0 中的位 0 和 1
EOR 真值表(二者不同則結(jié)果為 1):
Op_1 Op_2 結(jié)果
0 0 0
0 1 1
1 0 1
1 1 0
MOV : 傳送
(Move)
MOV{條件}{S} <dest>, <op 1>
dest = op_1
MOV從另一個寄存器峭竣、被移位的寄存器塘辅、或一個立即值裝載一個值到目的寄存器。你可以指定相同的寄存器來實(shí)現(xiàn)NOP 指令的效果皆撩,你還可以專門移位一個寄存器:
MOV R0, R0 ; R0 = R0... NOP 指令
MOV R0, R0, LSL#3 ; R0 = R0 * 8
如果 R15 是目的寄存器扣墩,將修改程序計(jì)數(shù)器或標(biāo)志。這用于返回到調(diào)用代碼扛吞,方法是把連接寄存器的內(nèi)容傳送到 R15:
MOV PC, R14 ; 退出到調(diào)用者
MOVS PC, R14 ; 退出到調(diào)用者并恢復(fù)標(biāo)志位
(不遵從 32-bit 體系)
MVN : 傳送取反的值
(MoveNegative)
MVN{條件}{S} <dest>, <op 1>
dest = !op_1
MVN從另一個寄存器沮榜、被移位的寄存器、或一個立即值裝載一個值到目的寄存器喻粹。不同之處是在傳送之前位被反轉(zhuǎn)了蟆融,所以把一個被取反的值傳送到一個寄存器中。這是邏輯非操作而不是算術(shù)操作守呜,這個取反的值加 1 才是它的取負(fù)的值:
MVN R0, #4 ; R0 = -5
MVN R0, #0 ; R0 = -1
ORR : 邏輯或
(logical OR)
ORR{條件}{S} <dest>, <op 1>, <op 2>
dest = op_1 OR op_2
OR將在兩個操作數(shù)上進(jìn)行邏輯或型酥,把結(jié)果放置到目的寄存器中山憨;對設(shè)置特定的位有用。操作數(shù) 1 是一個寄存器弥喉,操作數(shù) 2 可以是一個寄存器郁竟,被移位的寄存器,或一個立即值:
ORR R0, R0, #3 ; 設(shè)置 R0 中位 0 和 1
OR 真值表(二者中存在 1 則結(jié)果為 1):
Op_1 Op_2 結(jié)果
0 0 0
0 1 1
1 0 1
1 1 1
RSB : 反向減法
(Reverse Subtraction)
RSB{條件}{S} <dest>, <op 1>, <op 2>
dest = op_2 - op_1
SUB用操作數(shù)two 減去操作數(shù)one由境,把結(jié)果放置到目的寄存器中棚亩。操作數(shù) 1 是一個寄存器,操作數(shù) 2 可以是一個寄存器虏杰,被移位的寄存器讥蟆,或一個立即值:
RSB R0, R1, R2 ; R0 = R2 - R1
RSB R0, R1, #256 ; R0 = 256 - R1
RSB R0, R2, R3,LSL#1 ; R0 = (R3 << 1) - R2
反向減法可以在有符號或無符號數(shù)上進(jìn)行。
RSC : 帶借位的反向減法
(Reverse Subtraction with Carry)
RSC{條件}{S} <dest>, <op 1>, <op 2>
dest = op_2 - op_1 - !carry
同于SBC纺阔,但倒換了兩個操作數(shù)的前后位置瘸彤。
SBC : 帶借位的減法
(Subtraction with Carry)
SBC{條件}{S} <dest>, <op 1>, <op 2>
dest = op_1 - op_2 - !carry
SBC做兩個操作數(shù)的減法,把結(jié)果放置到目的寄存器中笛钝。它使用進(jìn)位標(biāo)志來表示借位质况,這樣就可以做大于 32 位的減法。SUB和SBC生成進(jìn)位標(biāo)志的方式不同于常規(guī)玻靡,如果需要借位則清除進(jìn)位標(biāo)志结榄。所以,指令要對進(jìn)位標(biāo)志進(jìn)行一個非操作 - 在指令執(zhí)行期間自動的反轉(zhuǎn)此位囤捻。
SUB : 減法
(Subtraction)
SUB{條件}{S} <dest>, <op 1>, <op 2>
dest = op_1 - op_2
SUB用操作數(shù)one 減去操作數(shù) two潭陪,把結(jié)果放置到目的寄存器中。操作數(shù) 1 是一個寄存器最蕾,操作數(shù) 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)
減法可以在有符號和無符號數(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ú)的指令使用特姐,它是指令格式中是一個字段,在匯編語言中表示為指令中的選項(xiàng)黍氮。如果數(shù)據(jù)處理指令的第二個操作數(shù)或者單一數(shù)據(jù)傳送指令中的變址是寄存器唐含,則可以對它進(jìn)行各種移位操作。如果數(shù)據(jù)處理指令的第二個操作數(shù)是立即值沫浆,在指令中用 8 位立即值和 4 位循環(huán)移位來表示它捷枯,所以對大于255 的立即值,匯編器嘗試通過在指令中設(shè)置循環(huán)移位數(shù)量來表示它件缸,如果不能表示則生成一個錯誤铜靶。在邏輯類指令中叔遂,邏輯運(yùn)算指令由指令中 S 位的設(shè)置或清除來確定是否影響進(jìn)位標(biāo)志他炊,而比較指令的 S 位總是設(shè)置的。在單一數(shù)據(jù)傳送指令中指定移位的數(shù)量只能用立即值而不能用寄存器已艰。
下面是給不同的移位類型的六個助記符:
LSL 邏輯左移
ASL 算術(shù)左移
LSR 邏輯右移
ASR 算術(shù)右移
ROR 循環(huán)右移
RRX 帶擴(kuò)展的循環(huán)右移
ASL和LSL是等同的痊末,可以自由互換。
你可以用一個立即值(從 0 到 31)指定移位數(shù)量哩掺,或用包含在 0 和 31 之間的一個值的寄存器指定移位數(shù)量凿叠。
邏輯或算術(shù)左移
(Logical or Arithmetic Shift Left)
Rx, LSL #n or
Rx, ASL #n or
Rx, LSL Rn or
Rx, ASL Rn
接受 Rx 的內(nèi)容并按用‘n’或在寄存器 Rn 中指定的數(shù)量向高有效位方向移位。最低有效位用零來填充嚼吞。除了概念上的第 33 位(就是被移出的最小的那位)之外丟棄移出最左端的高位盒件,如果邏輯類指令中 S 位被設(shè)置了,則此位將成為從桶式移位器退出時進(jìn)位標(biāo)志的值舱禽。
考慮下列:
MOV R1, #12
MOV R0, R1, LSL#2
在退出時炒刁,R0 是 48。這些指令形成的總和是R0 = #12, LSL#2等同于 BASIC 的R0 = 12 << 2
邏輯右移
(Logical Shift Right)
Rx, LSR #n or
Rx, LSR Rn
它在概念上與左移相對誊稚。把所有位向更低有效位方向移動翔始。如果邏輯類指令中 S 位被設(shè)置了,則把最后被移出最右端的那位放置到進(jìn)位標(biāo)志中里伯。它同于 BASIC 的register = value >>> shift城瞎。
算術(shù)右移
(Arithmetic Shift Right)
Rx, ASR #n or
Rx, ASR Rn
類似于 LSR,但使用要被移位的寄存器(Rx)的第 31 位的值來填充高位疾瓮,用來保護(hù)補(bǔ)碼表示中的符號脖镀。如果邏輯類指令中 S 位被設(shè)置了,則把最后被移出最右端的那位放置到進(jìn)位標(biāo)志中狼电。它同于 BASIC 的register = value >> shift认然。
循環(huán)右移
(Rotate Right)
Rx, ROR #n or
Rx, ROR Rn
循環(huán)右移類似于邏輯右移补憾,但是把從右側(cè)移出去的位放置到左側(cè),如果邏輯類指令中 S 位被設(shè)置了卷员,則同時放置到進(jìn)位標(biāo)志中盈匾,這就是位的‘循環(huán)’。一個移位量為 32 的操作將導(dǎo)致輸出與輸入完全一致毕骡,因?yàn)樗形欢急灰莆涣?32個位置削饵,又回到了開始時的位置!
帶擴(kuò)展的循環(huán)右移
(Rotate Right with extend)
Rx, RRX
這是一個 ROR#0 操作,它向右移動一個位置 - 不同之處是未巫,它使用處理器的進(jìn)位標(biāo)志來提供一個要被移位的 33 位的數(shù)量窿撬。
乘法指令
指令格式
這兩個指令與普通算術(shù)指令在對操作數(shù)的限制上有所不同:
給出的所有操作數(shù)、和目的寄存器必須為簡單的寄存器叙凡。
你不能對操作數(shù) 2 使用立即值或被移位的寄存器劈伴。
目的寄存器和操作數(shù) 1 必須是不同的寄存器。
-
最后握爷,你不能指定 R15 為目的寄存器跛璧。
MLA : ****帶累加的乘法
(Multiplication with Accumulate)
MLA{條件}{S} <dest>, <op 1>, <op 2>, <op 3>dest = (op_1 * op_2) + op_3
MLA的行為同于MUL,但它把操作數(shù) 3 的值加到結(jié)果上新啼。這在求總和時有用追城。
MUL : 乘法
(Multiplication)
MUL{條件}{S} <dest>, <op 1>, <op 2>
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} <op 1>, <op 2>
status = op_1 - (- op_2)
CMN同于CMP火诸,但它允許你與小負(fù)值(操作數(shù) 2 的取負(fù)的值)進(jìn)行比較,比如難于用其他方法實(shí)現(xiàn)的用于結(jié)束列表的 -1涵叮。這樣與 -1 比較將使用:
CMN R0, #1 ; 把 R0 與 -1 進(jìn)行比較
詳情參照CMP指令惭蹂。
CMP : 比較
(Compare)
CMP{條件}{P} <op 1>, <op 2>
status = op_1 - op_2
CMP允許把一個寄存器的內(nèi)容如另一個寄存器的內(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 : 測試等價
(Test Equivalence)
TEQ{條件}{P} <op 1>, <op 2>
Status = op_1 EOR op_2
TEQ類似于TST。區(qū)別是這里的概念上的計(jì)算是 EOR 而不是 AND芥玉。這提供了一種查看兩個操作數(shù)是否相同而又不影響進(jìn)位標(biāo)志(不象CMP那樣)的方法蛇摸。加上P后綴的TEQ還可用于改變 R15 中的標(biāo)志(在 26-bit 模式中)。詳情請參照 psr.html灿巧,在 32-bit 模式下如何做請參見這里赶袄。
TST : 測試位
(Test bits)
TST{條件}{P} <op 1>, <op 2>
Status = op_1 AND op_2
TST類似于CMP,不產(chǎn)生放置到目的寄存器中的結(jié)果抠藕。而是在給出的兩個操作數(shù)上進(jìn)行操作并把結(jié)果反映到狀態(tài)標(biāo)志上饿肺。使用TST來檢查是否設(shè)置了特定的位。操作數(shù) 1 是要測試的數(shù)據(jù)字而操作數(shù) 2 是一個位掩碼盾似。經(jīng)過測試后敬辣,如果匹配則設(shè)置 Zero 標(biāo)志,否則清除它零院。象CMP 那樣溉跃,你不需要指定S后綴。
TST R0, #%1 ; 測試在 R0 中是否設(shè)置了位 0门粪。
分支指令
B : 分支
(Branch)
B{條件} <地址>
B是最簡單的分支喊积。一旦遇到一個 B指令烹困,ARM 處理器將立即跳轉(zhuǎn)到給定的地址玄妈,從那里繼續(xù)執(zhí)行。
注意存儲在分支指令中的實(shí)際的值是相對當(dāng)前的 R15 的值的一個偏移量髓梅;而不是一個絕對地址拟蜻。
它的值由匯編器來計(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 處理器上搔扁,它們將變成下面這些東西:
OPT 1
ADR R1, #&70
LDR R0, [R1]
CMP #0
BEQ Zero
STR R0, [R1, #2]
.Zero
MOV PC, R14
這不是一個很好的例子,但你可以構(gòu)想如何更好的去條件執(zhí)行而不是分支蟋字。另一方面稿蹲,如果你有大段的代碼或者你的代碼使用狀態(tài)標(biāo)志,那么你可以使用條件執(zhí)行來實(shí)現(xiàn)各類分支: 這樣一個單一的簡單條件執(zhí)行指令可以替代在其他處理器中存在的所有這些分支和跳轉(zhuǎn)指令鹊奖。
OPT 1
ADR R1, #&70
LDR R0, [R1]
CMP R0, #0
STRNE R0, [R1, #2]
MOV PC, R14
BL : 帶連接的分支
(Branch with Link)
BL{條件} <地址>
BL是另一個分支指令苛聘。就在分支之前,在寄存器 14 中裝載上 R15 的內(nèi)容。你可以重新裝載 R14 到 R15 中來返回到在這個分支之后的那個指令设哗,
它是子例程的一個基本但強(qiáng)力的實(shí)現(xiàn)唱捣。它的作用在屏幕裝載器 2 (例子 4)中得以很好的展現(xiàn)...
.load_new_format
BL switch_screen_mode
BL get_screen_info
BL load_palette
.new_loop
MOV R1, R5
BL read_byte
CMP R0, #255
BLEQ read_loop
STRB R0, [R2, #1]!
...在這里我們見到在裝載器循環(huán)之前調(diào)用了三個子例程。接著网梢,一旦滿足了條件執(zhí)行就在循環(huán)中調(diào)用了 read_byte 子例程震缭。
條件執(zhí)行
ARM 處理器的一個非常特殊的特征是它的條件執(zhí)行。我們指的不是基本的如果進(jìn)位則分支战虏,ARM 使這個邏輯階段進(jìn)一步深化為如果進(jìn)位則 XXX- 這里的 XXX 是任何東西蛀序。
為了舉例,下面是 Intel 8086 處理器分支指令的一個列表:
JA Jump if Above
** JAE Jump if Above or Equal**
** JB Jump if Below**
** JBE Jump if Below or Equal**
** JC Jump if Carry**
** JCXZ Jump if CX Zero (CX is a register that can be used for loop counts)**
** JE Jump if Equal**
** JG Jump if Greater than**
** JGE Jump if Greater than or Equal**
** JL Jump if Less than**
** JLE Jump if Less Than or Equal**
** JMP JuMP**
** JNA Jump if Not Above**
** JNAE Jump if Not Above or Equal**
** JNB Jump if Not Below**
** JNBE Jump if Not Below or Equal**
** JNC Jump if No Carry**
** JNE Jump if Not Equal**
** JNG Jump if Not Greater than**
** JNGE Jump if Not Greater than or Equal**
** JNL Jump if Not Less than**
** JNLE Jump if Not Less than or Equal**
** JNO Jump if Not Overflow**
** JNP Jump if Not Parity**
** JNS Jump if Not Sign**
** JNZ Jump if Not Zero**
** JO Jump if Overflow**
** JP Jump if Parity**
** JPE Jump if Parity Even**
** JPO Jump if Parity Odd**
** JS Jump if Sign**
** JZ Jump if Zero**
80386 添加了:
** JECXZ Jump if ECX Zero**
作為對比活烙,ARM 處理器只提供了:
B 分支
** BL 帶連接的分支**
但 ARM 提供了條件執(zhí)行徐裸,你可以不受這個表面上不靈活的方式的限制:
BEQ Branch if EQual
** BNE Branch if Not Equal**
** BVS Branch if oVerflow Set**
** BVC Branch if oVerflow Clear**
** BHI Branch if HIgher**
** BLS Branch if Lower or the Same**
** BPL Branch if PLus**
** BMI Branch if MInus**
** BCS Branch if Carry Set**
** BCC Branch if Carry Clear**
** BGE Branch if Greater than or Equal**
** BGT Branch if Greater Than**
** BLE Branch if Less than or Equal**
** BLT Branch if Less Than**
** BLEQ Branch with Link if EQual**
** ....**
** BLLT Branch with Link if Less Than**
還有兩個代碼,
· AL - ALways啸盏,缺省條件所以不須指定
· NV - NeVer重贺,不是非常有用。你無論如何不要使用這個代碼...
當(dāng)你發(fā)現(xiàn)所有 Bxx 指令實(shí)際上是同一個指令的時候回懦,緊要關(guān)頭就到了气笙。
接著你會想,如果你可以在一個分支指令上加上所有這些條件怯晕,那么對一個寄存器裝載指令能否加上它們? 答案是可以潜圃。
下面是可獲得的條件代碼的列表:
EQ : 等于
如果一次比較之后設(shè)置了 Z 標(biāo)志。
NE : 不等于
如果一次比較之后清除了 Z 標(biāo)志舟茶。
VS : 溢出設(shè)置
如果在一次算術(shù)操作之后設(shè)置了 V 標(biāo)志谭期,計(jì)算的結(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ù)的原因是它不是負(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í)行這個指令酪术。是窮人的 NOP。包含 NV 是為了完整性(與 AL 相對)翠储,你不應(yīng)該在你的代碼中使用它绘雁。
有一個在最后的條件代碼 S,它以相反的方式工作援所。當(dāng)用于一個指令的時候庐舟,導(dǎo)致更改狀態(tài)標(biāo)志。這不是自動發(fā)生的 - 除非這些指令的目的是設(shè)置狀態(tài)住拭。例如:
ADD R0, R0, R1
** ADDS R0, R0, R1**
** ADDEQS R0, R0, R1**
第一個例子是一個基本的加法(把 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í)行的一個例子
** .loop ; 標(biāo)記循環(huán)開始位置**
** CMP R0, R10 ; 把 R0 與 R10 相比較**
** SWINE &40017 ; 不等于: 調(diào)用 SWI &40017**
** ADDNE R0, R0, #1 ; 向 R0 加 1**
** BNE loop ; 分支到 'loop'**
** MOV R10, #0 ; 等于 : 設(shè)置 R10 為零**
** LDMFD R13!, {R0-R12,PC} ; 返回到調(diào)用者**
注解:
· SWI 編號就象我寫的這樣疲憋。在 RISC OS 下,它是給 Econet_DoImmediate的編號梁只。不要字面的接受它缚柳,這只是一個例子!
· 你可能以前沒見過 LDMFD,它從棧中裝載多個寄存器搪锣。在這個例子中秋忙,我們從一個完全正式的棧中裝載 R0至 R12 和 R14。關(guān)于寄存器裝載和存儲的更多信息請參閱 str.html构舟。
· 我說要裝載 R14灰追。那么為什么要把它放入 PC 中? 原因是此時 R14 存儲的值包含返回地址。我們也可以采用:LDMFD R13!, {R0-R12,R14}
· ** MOV PC, R14但是直接恢復(fù)到 PC 中可以省略這個 MOV語句。**
· 最后弹澎,這些寄存器很有可能被一個 SWI 調(diào)用所占用(依賴于在調(diào)用期間執(zhí)行的代碼)朴下,所以你最好把你的重要的寄存器壓入棧中,以后在恢復(fù)它們苦蒿。
SWI 指令
SWI : 軟件中斷
(Software Interrupt)
SWI{條件} <24 位編號>
指令格式
這是一個簡單的設(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ù)。
在以這種方式使用的時候苍蔬,SWI 允許操作系統(tǒng)擁有一個模塊結(jié)構(gòu)诱建,這意味著用來建立完整的操作系統(tǒng)的所需的代碼可以被分割成許多小的部分(模塊)和一個模塊處理程序(handler)。
當(dāng) SWI 處理程序得到對特定的例程編號的一個請求的時候碟绑,它找到這個例程的位置并執(zhí)行它俺猿,并傳遞(有關(guān)的)任何數(shù)據(jù)。
它是如何工作的?
首先查看一下如何使用它格仲。一個 SWI 指令(匯編語言)看起來如下:
SWI &02
或
SWI "OS_Write0"
這些指令實(shí)際上是相同的押袍,將被匯編成相同的指令。唯一的不同是第二個指令使用一個字符串來表示 SWI 編號 &02凯肋。
在使用采用了字符串編號的程序的時候谊惭,在執(zhí)行之前首先查找這個字符串。
在這里我們不想處理字符串侮东,因?yàn)樗荒芙o出它要進(jìn)行什么的一個真實(shí)表示圈盔。它們通常用于增進(jìn)一個程序的清晰程度,但不是實(shí)際執(zhí)行的指令悄雅。
讓我們再次看一下第一個指令:
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 : STR R0,[R0,#-48]
00000004 : .ó?? : E59FF31C : LDR PC,&00000328
00000008 : .ó?? : E59FF31C : LDR PC,&0000032C
0000000C : .ó?? : E59FF31C : LDR PC,&00000330
00000010 : .ó?? : E59FF31C : LDR PC,&00000334
00000014 : .ó?? : E59FF31C : LDR PC,&00000338
00000018 : .ó?? : E59FF31C : LDR PC,&0000033C
0000001C : 2?? : E3A0A632 : MOV R10,#&3200000
讓我們仔細(xì)看一下宽闲。
除了第一個和最后一個指令之外(它們是特殊情況)你見到的都是把一個新值裝載到 PC (程序計(jì)數(shù)器)的指令众眨,它們告訴計(jì)算機(jī)到哪里去執(zhí)行下一個指令握牧。
還展示了這個值是從內(nèi)存中的一個地址接受來的。(你可以在 !Zap 主菜單上使用“Read Memory”選項(xiàng)去自己查看一下娩梨。)
這看起來好象與 SWI 沒多少關(guān)系我碟,下面做進(jìn)一步的說明。
一個 SWI 所做的一切就是把模式改變成超級用戶并設(shè)置 PC 來執(zhí)行在地址 &08 處的下一個指令!
把處理器轉(zhuǎn)換到超級用戶模式會切換掉兩個寄存器 r13 和 r14 并用 r13_svc 和 r14_svc 替換它們姚建。
在進(jìn)入超級用戶模式的時候矫俺,還把 r14_svc 設(shè)置為在這個 SWI 指令之后的地址。
這個實(shí)際上就象一個連接到地址 &08 的分支指令(BL &08)掸冤,但帶有用于一些數(shù)據(jù)(SWI 編號)的空間厘托。
象我說過的那樣,地址 &08 包含跳轉(zhuǎn)到另一個地址的一個指令稿湿,就是實(shí)際的 SWI 程序的地址!
此時你可能會想“稍等一會! 還有 SWI 編號呢?”铅匹。實(shí)際上處理器忽略這個值本身。SWI 處理程序使用傳遞來的 r14_svc 的值來獲取它饺藤。
下面是完成它的步驟(在存儲寄存器 r0-r12 之后):
- 它從 r14 中減去 4 來獲得 SWI 指令的地址包斑。
- 把這個指令裝載到一個寄存器。
- 清除這個指令的高端 8 位涕俗,去掉了 OpCode 而只剩下的 SWI 編號罗丰。
- 使用這個值來找到要被執(zhí)行的代碼的例程的地址(使用查找表等)。
- 恢復(fù)寄存器 r0-r12再姑。
- 使處理器離開超級用戶模式萌抵。
- 跳轉(zhuǎn)到這個例程的地址。
容易吧! ;)
下面是一個例子元镀,來自 ARM610 datasheet:
0x08 B Supervisor
EntryTable
DCD ZeroRtn
DCD ReadCRtn
DCD WriteIRtn
...
Zero EQU 0
ReadC EQU 256
WriteI EQU 512
; SWI 包含需要的例程在位 8-23 中和數(shù)據(jù)(如果有的話)在位 0-7 中绍填。
; 假定 R13_svc 指向了一個合適的棧
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 指令的基本處理步驟。