日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ARM指令集详解(超详细!带实例!)

發布時間:2023/12/20 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ARM指令集详解(超详细!带实例!) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文地址

算術和邏輯指令

ADC : 帶進位的加法

(Addition with Carry)

ADC{條件}{S} <dest>, <op 1>, <op 2>dest = op_1 + op_2 + carry

ADC 將把兩個操作數加起來,并把結果放置到目的寄存器中。它使用一個進位標志位,這樣就可以做比 32 位大的加法。下列例子將加兩個 128 位的數。
128 位結果: 寄存器 0、1、2、和 3
第一個 128 位數: 寄存器 4、5、6、和 7
第二個 128 位數: 寄存器 8、9、10、和 11。

ADDS R0, R4, R8 ; 加低端的字 ADCS R1, R5, R9 ; 加下一個字,帶進位 ADCS R2, R6, R10 ; 加第三個字,帶進位 ADCS R3, R7, R11 ; 加高端的字,帶進位

如果如果要做這樣的加法,不要忘記設置 S 后綴來更改進位標志。

ADD : 加法

(Addition)

ADD{條件}{S} <dest>, <op 1>, <op 2>dest = op_1 + op_2

ADD 將把兩個操作數加起來,把結果放置到目的寄存器中。操作數 1 是一個寄存器,操作數 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)

加法可以在有符號和無符號數上進行。

AND : 邏輯與

(logical AND)

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):

Op_1Op_2結果
000
010
100
111

BIC : 位清除

(Bit Clear)

BIC{條件}{S} <dest>, <op 1>, <op 2>dest = op_1 AND (!op_2)

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

BIC R0, R0, #%1011 ; 清除 R0 中的位 0、1、和 3。保持其余的不變。

BIC 真值表 :

Op_1Op_2結果
000
010
101
110

譯注:邏輯表達式為 Op_1 AND NOT Op_2
 

EOR : 邏輯異或

(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_1Op_2結果
000
011
101
110

MOV : 傳送

(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 體系)
 

MVN : 傳送取反的值

(MoveNegative)

MVN{條件}{S} <dest>, <op 1>dest = !op_1

MVN 從另一個寄存器、被移位的寄存器、或一個立即值裝載一個值到目的寄存器。不同之處是在傳送之前位被反轉了,所以把一個被取反的值傳送到一個寄存器中。這是邏輯非操作而不是算術操作,這個取反的值加 1 才是它的取負的值:

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 將在兩個操作數上進行邏輯或,把結果放置到目的寄存器中;對設置特定的位有用。操作數 1 是一個寄存器,操作數 2 可以是一個寄存器,被移位的寄存器,或一個立即值:

ORR R0, R0, #3 ; 設置 R0 中位 0 和 1

OR 真值表(二者中存在 1 則結果為 1):

Op_1Op_2結果
000
011
101
111

RSB : 反向減法

(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

反向減法可以在有符號或無符號數上進行。

RSC : 帶借位的反向減法

(Reverse Subtraction with Carry)

RSC{條件}{S} <dest>, <op 1>, <op 2>dest = op_2 - op_1 - !carry

同于 SBC,但倒換了兩個操作數的前后位置。

SBC : 帶借位的減法

(Subtraction with Carry)

SBC{條件}{S} <dest>, <op 1>, <op 2>dest = op_1 - op_2 - !carry

SBC 做兩個操作數的減法,把結果放置到目的寄存器中。它使用進位標志來表示借位,這樣就可以做大于 32 位的減法。SUB 和 SBC 生成進位標志的方式不同于常規,如果需要借位則清除進位標志。所以,指令要對進位標志進行一個非操作 - 在指令執行期間自動的反轉此位。

SUB : 減法

(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 之間的一個值的寄存器指定移位數量。

LSL/ASL : 邏輯或算術左移

(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

LSR : 邏輯右移

(Logical Shift Right)

Rx, LSR #n or Rx, LSR Rn

它在概念上與左移相對。把所有位向更低有效位方向移動。如果邏輯類指令中 S 位被設置了,則把最后被移出最右端的那位放置到進位標志中。它同于 BASIC 的 register = value >>> shift。

ASR : 算術右移

(Arithmetic Shift Right)

Rx, ASR #n or Rx, ASR Rn

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

ROR : 循環右移

(Rotate Right)

Rx, ROR #n or Rx, ROR Rn

循環右移類似于邏輯右移,但是把從右側移出去的位放置到左側,如果邏輯類指令中 S 位被設置了,則同時放置到進位標志中,這就是位的‘循環’。一個移位量為 32 的操作將導致輸出與輸入完全一致,因為所有位都被移位了 32 個位置,又回到了開始時的位置!

RRX : 帶擴展的循環右移

(Rotate Right with extend)

Rx, RRX

這是一個 ROR#0 操作,它向右移動一個位置 - 不同之處是,它使用處理器的進位標志來提供一個要被移位的 33 位的數量。
轉者注:將寄存器內容循環右移一位,空位用原來的C來 填充,移出的最低有效位填入C。

乘法指令

指令格式

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

  • 給出的所有操作數、和目的寄存器必須為簡單的寄存器。
  • 你不能對操作數 2 使用立即值或被移位的寄存器。
  • 目的寄存器和操作數 1
  • 最后,你不能指定 R15 為目的寄存器。

MLA : 帶累加的乘法

(Multiplication with Accumulate)

MLA{條件}{S} <dest>, <op 1>, <op 2>, <op 3>dest = (op_1 * op_2) + op_3

MLA 的行為同于 MUL,但它把操作數 3 的值加到結果上。這在求總和時有用。

MUL : 乘法

(Multiplication)

MUL{條件}{S} <dest>, <op 1>, <op 2>dest = op_1 * op_2

MUL 提供 32 位整數乘法。如果操作數是有符號的,可以假定結果也是有符號的。

比較指令

指令格式

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

CMN : 比較取負的值

(Compare Negative)

CMN{條件}{P} <op 1>, <op 2>status = op_1 - (- op_2)

CMN 同于 CMP,但它允許你與小負值(操作數 2 的取負的值)進行比較,比如難于用其他方法實現的用于結束列表的 -1。這樣與 -1 比較將使用:

CMN R0, #1 ; 把 R0 與 -1 進行比較

詳情參照 CMP 指令。

CMP : 比較

(Compare)

CMP{條件}{P} <op 1>, <op 2>status = op_1 - op_2

CMP 允許把一個寄存器的內容如另一個寄存器的內容或立即值進行比較,更改狀態標志來允許進行條件執行。它進行一次減法,但不存儲結果,而是正確的更改標志。標志表示的是操作數 1 比操作數 2 如何(大小等)。如果操作數 1 大于操作操作數 2,則此后的有 GT 后綴的指令將可以執行。
明顯的,你不需要顯式的指定 S 后綴來更改狀態標志… 如果你指定了它則被忽略。

TEQ : 測試等價

(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 模式下如何做請參見這里。

TST : 測試位

(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。

分支指令

轉者注:B和BL其實就是函數入口點跳轉之類的東西了,但是這個說明真心看不懂。。。

B : 分支

(Branch)

B{條件} <地址>

B 是最簡單的分支。一旦遇到一個 B 指令,ARM 處理器將立即跳轉到給定的地址,從那里繼續執行。
注意存儲在分支指令中的實際的值是相對當前的 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

轉者注:這段看不懂

BL : 帶連接的分支

(Branch with Link)

BL{條件} <地址>

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

它是子例程的一個基本但強力的實現。它的作用在屏幕裝載器 2 (例子 4)中得以很好的展現…

.load_new_formatBL switch_screen_modeBL get_screen_infoBL load_palette.new_loopMOV R1, R5BL read_byteCMP R0, #255BLEQ read_loopSTRB R0, [R2, #1]!

…在這里我們見到在裝載器循環之前調用了三個子例程。接著,一旦滿足了條件執行就在循環中調用了 read_byte 子例程。

條件執行

ARM 處理器的一個非常特殊的特征是它的條件執行。我們指的不是基本的如果進位則分支,ARM 使這個邏輯階段進一步深化為如果進位則 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 提供了條件執行,你可以不受這個表面上不靈活的方式的限制:

  • 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,不是非常有用。你無論如何不要使用這個代碼…

當你發現所有 Bxx 指令實際上是同一個指令的時候,緊要關頭就到了。
接著你會想,如果你可以在一個分支指令上加上所有這些條件,那么對一個寄存器裝載指令能否加上它們? 答案是可以。
下面是可獲得的條件代碼的列表:
EQ : 等于
如果一次比較之后設置了 Z 標志。
 
NE : 不等于
如果一次比較之后清除了 Z 標志。
 
VS : 溢出設置
如果在一次算術操作之后設置了 V 標志,計算的結果不適合放入一個 32bit 目標寄存器中。
 
VC : 溢出清除
如果清除了 V 標志,與 VS 相反。
 
HI : 高于(無符號)
如果一次比較之后設置了 C 標志并清除了 Z 標志。
 
LS : 低于或同于(無符號)
如果一次比較操作之后清除了 C 標志或設置了 Z 標志。
 
PL : 正號
如果一次算術操作之后清除了 N。出于定義‘正號’的目的,零是正數的原因是它不是負數…
 
MI : 負號
如果一次算術操作之后設置了 N 標志。
 
CS : 進位設置
如果一次算術操作或移位操作之后設置了 C 標志,操作的結果不能表示為 32bit。你可以把 C 標志當作結果的第 33 位。
 
CC : 進位清除
與 CS 相反。
 
GE : 大于或等于(有符號)
如果一次比較之后…
設置了 N 標志并設置了 V 標志
或者…
清除了 N 標志并清除了 V 標志。
 
GT : 大于(有符號)
如果一次比較之后…
設置了 N 標志并設置了 V 標志
或者…
清除了 N 標志并清除了 V 標志
并且…
清除了 Z 標志。
 
LE : 小于或等于(有符號)
如果一次比較之后…
設置了 N 標志并清除了 V 標志
或者…
清除了 N 標志并設置了 V 標志
并且…
設置了 Z 標志。
 
LT : 小于(有符號)
如果一次比較之后…
設置了 N 標志并清除了 V 標志。
或者…
清除了 N 標志并設置了 V 標志。
 
AL : 總是
缺省條件,所以不用明顯聲明。
 
NV : 從不
不是特別有用,它表示應當永遠不執行這個指令。是窮人的 NOP。
包含 NV 是為了完整性(與 AL 相對),你不應該在你的代碼中使用它。
有一個在最后的條件代碼 S,它以相反的方式工作。當用于一個指令的時候,導致更改狀態標志。這不是自動發生的 - 除非這些指令的目的是設置狀態。例如:

ADD R0, R0, R1ADDS R0, R0, R1ADDEQS R0, R0, R1

第一個例子是一個基本的加法(把 R1 的值增加到 R0),它不影響狀態寄存器。
第二個例子是同一個加法,只不過它導致更改狀態寄存器。
最后一個例子是同一個加法,更改狀態寄存器。不同在于它是一個有條件的指令。只有前一個操作的結果是 EQ (如果設置了 Z 標志)的時候它才執行。

下面是條件執行的一個工作中的例子。你把寄存器 0 與存儲在寄存器 10 中內容相比較。

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

\ 條件執行的一個例子

.loop ; 標記循環開始位置CMP R0, R10 ; 把 R0 與 R10 相比較SWINE &40017 ; 不等于: 調用 SWI &40017ADDNE R0, R0, #1 ; 向 R0 加 1BNE loop ; 分支到 'loop'MOV R10, #0 ; 等于 : 設置 R10 為零LDMFD R13!, {R0-R12,PC} ; 返回到調用者

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

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

我說要裝載 R14。那么為什么要把它放入 PC 中? 原因是此時 R14 存儲的值包含返回地址。我們也可以采用:

LDMFD R13!, {R0-R12,R14} MOV PC, R14

但是直接恢復到 PC 中可以省略這個 MOV 語句。

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

SWI 指令

SWI : 軟件中斷

(Software Interrupt)

SWI{條件} <24 位編號>

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

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

SWI 是什么?

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

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

打印機驅動器 SWI,用來輔助使用打印并行端口。

FreeNet/Acorn TCP/IP 協議棧 SWI,用 TCP/IP 協議在 Internet 上發送和接收數據。

在以這種方式使用的時候,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 之后):

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

  • 把這個指令裝載到一個寄存器。

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

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

  • 恢復寄存器 r0-r12。

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

  • 跳轉到這個例程的地址。

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

    0x08 B SupervisorEntryTable 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 指令的基本處理步驟。

    總結

    以上是生活随笔為你收集整理的ARM指令集详解(超详细!带实例!)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。