(Addition with Carry)html
ADC{條件}{S} <dest>, <op 1>, <op 2> dest = op_1 + op_2 + carry
ADC
將把兩個操做數加起來,並把結果放置到目的寄存器中。它使用一個進位標誌位,這樣就能夠作比 32 位大的加法。下列例子將加兩個 128 位的數。
128 位結果: 寄存器 0、一、二、和 3
第一個 128 位數: 寄存器 四、五、六、和 7
第二個 128 位數: 寄存器 八、九、十、和 11。ide
ADDS R0, R4, R8 ; 加低端的字 ADCS R1, R5, R9 ; 加下一個字,帶進位 ADCS R2, R6, R10 ; 加第三個字,帶進位 ADCS R3, R7, R11 ; 加高端的字,帶進位
若是若是要作這樣的加法,不要忘記設置 S 後綴來更改進位標誌。oop
(Addition)測試
ADD{條件}{S} <dest>, <op 1>, <op 2> dest = op_1 + op_2
ADD
將把兩個操做數加起來,把結果放置到目的寄存器中。操做數 1 是一個寄存器,操做數 2 能夠是一個寄存器,被移位的寄存器,或一個當即值:ui
ADD R0, R1, R2 ; R0 = R1 + R2 ADD R0, R1, #256 ; R0 = R1 + 256 ADD R0, R2, R3,LSL#1 ; R0 = R2 + (R3 << 1)
加法能夠在有符號和無符號數上進行。url
(logical AND)spa
AND{條件}{S} <dest>, <op 1>, <op 2> dest = op_1 AND op_2
AND
將在兩個操做數上進行邏輯與,把結果放置到目的寄存器中;對屏蔽你要在上面工做的位頗有用。 操做數 1 是一個寄存器,操做數 2 能夠是一個寄存器,被移位的寄存器,或一個當即值:操作系統
AND R0, R0, #3 ; R0 = 保持 R0 的位 0 和 1,丟棄其他的位。
AND 的真值表(兩者都是 1 則結果爲 1):3d
Op_1 Op_2 結果 0 0 0 0 1 0 1 0 0 1 1 1
(Bit Clear)code
BIC{條件}{S} <dest>, <op 1>, <op 2> dest = op_1 AND (!op_2)
BIC
是在一個字中清除位的一種方法,與 OR 位設置是相反的操做。操做數 2 是一個 32 位位掩碼(mask)。若是若是在掩碼中設置了某一位,則清除這一位。未設置的掩碼位指示此位保持不變。
BIC R0, R0, #%1011 ; 清除 R0 中的位 0、一、和 3。保持其他的不變。
BIC 真值表 :
Op_1 Op_2 結果 0 0 0 0 1 0 1 0 1 1 1 0
譯註:邏輯表達式爲 Op_1 AND NOT Op_2
(logical Exclusive OR)
EOR{條件}{S} <dest>, <op 1>, <op 2> dest = op_1 EOR op_2
EOR
將在兩個操做數上進行邏輯異或,把結果放置到目的寄存器中;對反轉特定的位有用。操做數 1 是一個寄存器,操做數 2 能夠是一個寄存器,被移位的寄存器,或一個當即值:
EOR R0, R0, #3 ; 反轉 R0 中的位 0 和 1
EOR 真值表(兩者不一樣則結果爲 1):
Op_1 Op_2 結果 0 0 0 0 1 1 1 0 1 1 1 0
(Move)
MOV{條件}{S} <dest>, <op 1> dest = op_1
MOV
從另外一個寄存器、被移位的寄存器、或一個當即值裝載一個值到目的寄存器。你能夠指定相同的寄存器來實現 NOP 指令的效果,你還能夠專門移位一個寄存器:
MOV R0, R0 ; R0 = R0... NOP 指令 MOV R0, R0, LSL#3 ; R0 = R0 * 8
若是 R15 是目的寄存器,將修改程序計數器或標誌。這用於返回到調用代碼,方法是把鏈接寄存器的內容傳送到 R15:
MOV PC, R14 ; 退出到調用者
MOVS PC, R14 ; 退出到調用者並恢復標誌位
(不聽從 32-bit 體系)
(MoveNegative)
MVN{條件}{S} <dest>, <op 1> dest = !op_1
MVN
從另外一個寄存器、被移位的寄存器、或一個當即值裝載一個值到目的寄存器。不一樣之處是在傳送以前位被反轉了,因此把一個被取反的值傳送到一個寄存器中。這是邏輯非操做而不是算術操做,這個取反的值加 1 纔是它的取負的值:
MVN R0, #4 ; R0 = -5 MVN R0, #0 ; R0 = -1
(logical OR)
ORR{條件}{S} <dest>, <op 1>, <op 2> dest = op_1 OR op_2
OR
將在兩個操做數上進行邏輯或,把結果放置到目的寄存器中;對設置特定的位有用。操做數 1 是一個寄存器,操做數 2 能夠是一個寄存器,被移位的寄存器,或一個當即值:
ORR R0, R0, #3 ; 設置 R0 中位 0 和 1
OR 真值表(兩者中存在 1 則結果爲 1):
Op_1 Op_2 結果 0 0 0 0 1 1 1 0 1 1 1 1
(Reverse Subtraction)
RSB{條件}{S} <dest>, <op 1>, <op 2> dest = op_2 - op_1
SUB
用操做數 two 減去操做數 one,把結果放置到目的寄存器中。操做數 1 是一個寄存器,操做數 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
反向減法能夠在有符號或無符號數上進行。
(Reverse Subtraction with Carry)
RSC{條件}{S} <dest>, <op 1>, <op 2> dest = op_2 - op_1 - !carry
同於 SBC
,但倒換了兩個操做數的先後位置。
(Subtraction with Carry)
SBC{條件}{S} <dest>, <op 1>, <op 2> dest = op_1 - op_2 - !carry
SBC
作兩個操做數的減法,把結果放置到目的寄存器中。它使用進位標誌來表示借位,這樣就能夠作大於 32 位的減法。SUB
和 SBC
生成進位標誌的方式不一樣於常規,若是須要借位則清除進位標誌。因此,指令要對進位標誌進行一個非操做 - 在指令執行期間自動的反轉此位。
(Subtraction)
SUB{條件}{S} <dest>, <op 1>, <op 2> dest = op_1 - op_2
SUB
用操做數 one 減去操做數 two,把結果放置到目的寄存器中。操做數 1 是一個寄存器,操做數 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)
減法能夠在有符號和無符號數上進行。
ARM 處理器組建了能夠與數據處理指令(ADC、ADD、AND、BIC、CMN、CMP、EOR、MOV、MVN、ORR、RSB、SBC、SUB、TEQ、TST)一塊兒使用的桶式移位器(barrel shifter)。你還可使用桶式移位器影響在 LDR/STR 操做中的變址值。
譯註:移位操做在 ARM 指令集中不做爲單獨的指令使用,它是指令格式中是一個字段,在彙編語言中表示爲指令中的選項。若是數據處理指令的第二個操做數或者單一數據傳送指令中的變址是寄存器,則能夠對它進行各類移位操做。若是數據處理指令的第二個操做數是當即值,在指令中用 8 位當即值和 4 位循環移位來表示它,因此對大於 255 的當即值,彙編器嘗試經過在指令中設置循環移位數量來表示它,若是不能表示則生成一個錯誤。在邏輯類指令中,邏輯運算指令由指令中 S 位的設置或清除來肯定是否影響進位標誌,而比較指令的 S 位老是設置的。在單一數據傳送指令中指定移位的數量只能用當即值而不能用寄存器。
下面是給不一樣的移位類型的六個助記符:
LSL 邏輯左移 ASL 算術左移 LSR 邏輯右移 ASR 算術右移 ROR 循環右移 RRX 帶擴展的循環右移
ASL
和 LSL
是等同的,能夠自由互換。
你能夠用一個當即值(從 0 到 31)指定移位數量,或用包含在 0 和 31 之間的一個值的寄存器指定移位數量。
(Logical or Arithmetic Shift Left)
Rx, LSL #n or Rx, ASL #n or Rx, LSL Rn or Rx, ASL Rn
接受 Rx 的內容並按用‘n’或在寄存器 Rn 中指定的數量向高有效位方向移位。最低有效位用零來填充。除了概念上的第 33 位(就是被移出的最小的那位)以外丟棄移出最左端的高位,若是邏輯類指令中 S 位被設置了,則此位將成爲從桶式移位器退出時進位標誌的值。
考慮下列:
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 位被設置了,則把最後被移出最右端的那位放置到進位標誌中。它同於 BASIC 的 register = value >>> shift
。
(Arithmetic Shift Right)
Rx, ASR #n or Rx, ASR Rn
相似於 LSR,但使用要被移位的寄存器(Rx)的第 31 位的值來填充高位,用來保護補碼錶示中的符號。若是邏輯類指令中 S 位被設置了,則把最後被移出最右端的那位放置到進位標誌中。它同於 BASIC 的 register = value >> shift
。
(Rotate Right)
Rx, ROR #n or Rx, ROR Rn
循環右移相似於邏輯右移,可是把從右側移出去的位放置到左側,若是邏輯類指令中 S 位被設置了,則同時放置到進位標誌中,這就是位的‘循環’。一個移位量爲 32 的操做將致使輸出與輸入徹底一致,由於全部位都被移位了 32 個位置,又回到了開始時的位置!
(Rotate Right with extend)
Rx, RRX
這是一個 ROR#0 操做,它向右移動一個位置 - 不一樣之處是,它使用處理器的進位標誌來提供一個要被移位的 33 位的數量。
乘法指令 |
這兩個指令與普通算術指令在對操做數的限制上有所不一樣:
(Multiplication with Accumulate)
MLA{條件}{S} <dest>, <op 1>, <op 2>, <op 3> dest = (op_1 * op_2) + op_3
MLA
的行爲同於 MUL
,但它把操做數 3 的值加到結果上。這在求總和時有用。
(Multiplication)
MUL{條件}{S} <dest>, <op 1>, <op 2> dest = op_1 * op_2
MUL
提供 32 位整數乘法。若是操做數是有符號的,能夠假定結果也是有符號的。
比較指令 |
譯註:CMP 和 CMP 是算術指令,TEQ 和 TST 是邏輯指令。把它們納入一類的緣由是它們的 S 位老是設置的,就是說,它們老是影響標誌位。
(Compare Negative)
CMN{條件}{P} <op 1>, <op 2> status = op_1 - (- op_2)
CMN
同於 CMP
,但它容許你與小負值(操做數 2 的取負的值)進行比較,好比難於用其餘方法實現的用於結束列表的 -1。這樣與 -1 比較將使用:
CMN R0, #1 ; 把 R0 與 -1 進行比較
詳情參照 CMP
指令。
(Compare)
CMP{條件}{P} <op 1>, <op 2> status = op_1 - op_2
CMP
容許把一個寄存器的內容如另外一個寄存器的內容或當即值進行比較,更改狀態標誌來容許進行條件執行。它進行一次減法,但不存儲結果,而是正確的更改標誌。標誌表示的是操做數 1 比操做數 2 如何(大小等)。若是操做數 1 大於操做操做數 2,則此後的有 GT 後綴的指令將能夠執行。
明顯的,你不須要顯式的指定 S
後綴來更改狀態標誌... 若是你指定了它則被忽略。
(Test Equivalence)
TEQ{條件}{P} <op 1>, <op 2> Status = op_1 EOR op_2
TEQ
相似於 TST
。區別是這裏的概念上的計算是 EOR 而不是 AND。這提供了一種查看兩個操做數是否相同而又不影響進位標誌(不象 CMP
那樣)的方法。加上 P
後綴的 TEQ
還可用於改變 R15 中的標誌(在 26-bit 模式中)。詳情請參照 psr.html,在 32-bit 模式下如何作請參見這裏。
(Test bits)
TST{條件}{P} <op 1>, <op 2> Status = op_1 AND op_2
TST
相似於 CMP
,不產生放置到目的寄存器中的結果。而是在給出的兩個操做數上進行操做並把結果反映到狀態標誌上。使用 TST
來檢查是否設置了特定的位。操做數 1 是要測試的數據字而操做數 2 是一個位掩碼。通過測試後,若是匹配則設置 Zero 標誌,不然清除它。象 CMP
那樣,你不須要指定 S
後綴。
TST R0, #%1 ; 測試在 R0 中是否設置了位 0。
(Branch)
B{條件} <地址>
是最簡單的分支。一旦遇到一個 指令,ARM 處理器將當即跳轉到給定的地址,從那裏繼續執行。BB
注意存儲在分支指令中的實際的值是相對當前的 R15 的值的一個偏移量;而不是一個絕對地址。
它的值由彙編器來計算,它是 24 位有符號數,左移兩位後有符號擴展爲 32 位,表示的有效偏移爲 26 位(+/- 32 M)。
在其餘處理器上,你可能常常見到這樣的指令:
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
這不是一個很好的例子,但你能夠構想如何更好的去條件執行而不是分支。另外一方面,若是你有大段的代碼或者你的代碼使用狀態標誌,那麼你可使用條件執行來實現各種分支: 這樣一個單一的簡單條件執行指令能夠替代在其餘處理器中存在的全部這些分支和跳轉指令。
OPT 1 ADR R1, #&70 LDR R0, [R1] CMP R0, #0 STRNE R0, [R1, #2] MOV PC, R14
(Branch with Link)
BL{條件} <地址>
是另外一個分支指令。就在分支以前,在寄存器 14 中裝載上 R15 的內容。你能夠從新裝載 R14 到 R15 中來返回到在這個分支以後的那個指令,BL
它是子例程的一個基本但強力的實現。它的做用在屏幕裝載器 2 (例子 4)中得以很好的展示...
.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]!
...在這裏咱們見到在裝載器循環以前調用了三個子例程。接着,一旦知足了條件執行就在循環中調用了 read_byte 子例程。
條件執行 |
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
B 分支 BL 帶鏈接的分支
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,不是很是有用。你不管如何不要使用這個代碼...接着你會想,若是你能夠在一個分支指令上加上全部這些條件,那麼對一個寄存器裝載指令可否加上它們? 答案是能夠。
下面是可得到的條件代碼的列表:
S,
它以相反的方式工做。當用於一個指令的時候,致使更改狀態標誌。這不是自動發生的 - 除非這些指令的目的是設置狀態。例如:ADD R0, R0, R1 ADDS R0, R0, R1 ADDEQS R0, R0, R1
第二個例子是同一個加法,只不過它致使更改狀態寄存器。
最後一個例子是同一個加法,更改狀態寄存器。不一樣在於它是一個有條件的指令。只有前一個操做的結果是 EQ (若是設置了 Z 標誌)的時候它才執行。
下面是條件執行的一個工做中的例子。你把寄存器 0 與存儲在寄存器 10 中內容相比較。
若是不等於 R10,則調用一個軟件中斷,增長它並分支回來再次作這些。不然清除 R10 並返回到調用它的那部分代碼(它的地址存儲在 R14)。
\ 條件執行的一個例子 .loop ; 標記循環開始位置 CMP R0, R10 ; 把 R0 與 R10 相比較 SWINE &40017 ; 不等於: 調用 SWI &40017 ADDNE R0, R0, #1 ; 向 R0 加 1 BNE loop ; 分支到 'loop' MOV R10, #0 ; 等於 : 設置 R10 爲零 LDMFD R13!, {R0-R12,PC} ; 返回到調用者
LDMFD,
它從棧中裝載多個寄存器。在這個例子中,咱們從一個徹底正式的棧中裝載 R0 至 R12 和 R14。關於寄存器裝載和存儲的更多信息請參閱 str.html。LDMFD R13!, {R0-R12,R14}
MOV PC, R14
MOV
語句。 SWI 指令 |
SWI : 軟件中斷
(Software Interrupt)
SWI{條件} <24 位編號>
這是一個簡單的設施,但多是最經常使用的。多數操做系統設施是用 SWI 提供的。沒有 SWI 的 RISC OS 是不可想象的。
Nava Whiteford 解釋了 SWI 是如何工做的(最初在 Frobnicate issue 12?)...
SWI 表示 Software Interrupt。在 RISC OS 中使用 SWI 來訪問操做系統例程或第三方生產的模塊。許多應用使用模塊來給其餘應用提供低層外部訪問。
SWI 的例子有:
在以這種方式使用的時候,SWI 容許操做系統擁有一個模塊結構,這意味着用來創建完整的操做系統的所需的代碼能夠被分割成許多小的部分(模塊)和一個模塊處理程序(handler)。
當 SWI 處理程序獲得對特定的例程編號的一個請求的時候,它找到這個例程的位置並執行它,並傳遞(有關的)任何數據。
首先查看一下如何使用它。一個 SWI 指令(彙編語言)看起來以下:
SWI &02
或
SWI "OS_Write0"
這些指令其實是相同的,將被彙編成相同的指令。惟一的不一樣是第二個指令使用一個字符串來表示 SWI 編號 &02。
在使用採用了字符串編號的程序的時候,在執行以前首先查找這個字符串。
在這裏咱們不想處理字符串,由於它不能給出它要進行什麼的一個真實表示。它們一般用於增進一個程序的清晰程度,但不是實際執行的指令。
讓咱們再次看一下第一個指令:
SWI &02
這是什麼意思? 字面的意思是進入 SWI 處理程序並傳遞值 &02。在 RISC OS 中這意味着執行編號是 &02 的例程。
它是如何這麼做的? 它如何傳遞 SWI 編號和進入 SWI 處理程序?
若是你查看內存的開始 32 字節(位於 0-&1C)並反彙編它們(查開實際的 ARM 指令)你將見到以下:
地址 內容 反彙編 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
讓咱們仔細看一下。
除了第一個和最後一個指令以外(它們是特殊狀況)你見到的都是把一個新值裝載到 PC (程序計數器)的指令,它們告訴計算機到哪裏去執行下一個指令。
還展現了這個值是從內存中的一個地址接受來的。(你能夠在 !Zap 主菜單上使用「Read Memory」選項去本身查看一下。)
這看起來好象與 SWI 沒多少關係,下面作進一步的說明。
一個 SWI 所作的一切就是把模式改變成超級用戶並設置 PC 來執行在地址 &08 處的下一個指令!
把處理器轉換到超級用戶模式會切換掉兩個寄存器 r13 和 r14 並用 r13_svc 和 r14_svc 替換它們。
在進入超級用戶模式的時候,還把 r14_svc 設置爲在這個 SWI 指令以後的地址。
這個實際上就象一個鏈接到地址 &08 的分支指令(BL &08),但帶有用於一些數據(SWI 編號)的空間。
象我說過的那樣,地址 &08 包含跳轉到另外一個地址的一個指令,就是實際的 SWI 程序的地址!
此時你可能會想「稍等一會! 還有 SWI 編號呢?」。實際上處理器忽略這個值自己。SWI 處理程序使用傳遞來的 r14_svc 的值來獲取它。
下面是完成它的步驟(在存儲寄存器 r0-r12 以後):
容易吧! ;)
下面是一個例子,來自 ARM610 datasheet:
0x08 B Supervisor EntryTable DCD ZeroRtn DCD ReadCRtn DCD WriteIRtn ... Zero EQU 0 ReadC EQU 256 WriteI EQU 512 ; SWI 包含須要的例程在位 8-23 中和數據(若是有的話)在位 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}^ ; 恢復工做空間,並返回、恢復處理器模式和標誌。
這就是 SWI 指令的基本處理步驟。