原文地址

算术和逻辑指令

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_1 Op_2 结果
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 位设置是相反的操作。操作数 2 是一个 32 位位掩码(mask)。如果如果在掩码中设置了某一位,则清除这一位。未设置的掩码位指示此位保持不变。

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

BIC 真值表 :

Op_1 Op_2 结果
0 0 0
0 1 0
1 0 1
1 1 0

译注:逻辑表达式为 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_1 Op_2 结果
0 0 0
0 1 1
1 0 1
1 1 0

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_1 Op_2 结果
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 用操作数 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 之后):

  1. 它从 r14 中减去 4 来获得 SWI 指令的地址。

  2. 把这个指令装载到一个寄存器。

  3. 清除这个指令的高端 8 位,去掉了 OpCode 而只剩下的 SWI 编号。

  4. 使用这个值来找到要被执行的代码的例程的地址(使用查找表等)。

  5. 恢复寄存器 r0-r12。

  6. 使处理器离开超级用户模式。

  7. 跳转到这个例程的地址。

容易吧! ;)
下面是一个例子,来自 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指令集详解(超详细!带实例!)相关推荐

  1. Java 泛型详解(超详细的java泛型方法解析)

    Java 泛型详解(超详细的java泛型方法解析) 1. 什么是泛型 泛型:是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型.也就是说在泛型使用过程中,操作的数据类型被指定为 ...

  2. 【bind()函数】JavaScript手写bind()及详解-超详细~~~

    这两天学习了手写call.apply.bind,手写bind思考了很久才实现了MDN的示例的结果,所以记录下来~ 因为是第一篇文章,所以可能存在一些错误,希望各位大佬批评指正,不吝赐教. 也欢迎不懂的 ...

  3. mysql 联表比对,MySQL联表查询详解/超详细mysql left join,right join,inner join用法分析比较...

    超详细mysql left join,right join,inner join用法分析 下面是例子分析 表A记录如下: aID aNum 1 a20050111 2 a20050112 3 a200 ...

  4. log4j 配置详解(超详细)

    一.Log4j简介 Log4j有三个主要的组件:Loggers(记录器),Appenders (输出源)和Layouts(布局).这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出.综合使 ...

  5. @Autowired注解详解——超详细易懂

    @Autowired详解 要搞明白@Autowired注解就是要了解它是什么?有什么作用?怎么用?为什么? 首先了解一下IOC操作Bean管理,bean管理是指(1)spring创建对象 (2)spr ...

  6. 斜率优化详解(超详细, 有图有代码有注释)

    文章目录 斜率优化引入 从例题开始 斜率优化Part 1: 推为斜率式 斜率优化Part 2: 合法点集的斜率单调性 Part 3: 找到最优决策点 Part 4: 斜率优化大流程 Part 5: 斜 ...

  7. web服务器常见配置搭建详解(超详细)

    前言: 本博客借鉴一些写的比较好的博客,进行归纳总结,整理了一篇比较详细的服务器常见配置搭建教程 一来是和大家一起分享,二来也是作为自己的学习笔记记录一下. 温馨提示: 篇幅较长,请分阶段选择性查看. ...

  8. 【Linux】saltstack的使用详解 超详细

    一.salt常用命令 salt 该命令执行salt的执行模块,通常在master端运行,也是我们最常用到的命令 salt [options] '<target>' <function ...

  9. 一、初识Metasploit(MSF使用详解超详细)

    前言:Metasploit是The Metasploit Framework的简称,也可以叫做MSF! MSF高度模块化即框架由多个module组成,是全球最受欢迎的工具. Metasploit Fr ...

  10. 计算机网络 - IPv4 常考知识点详解(超详细!)

    目录 一.IPv4分组 1.IPv4分组的格式 2.IP数据报分片 3.网络层转发分组的流程 二.IPv4地址与NAT 1.IPv4地址 2.NAT 三.子网划分与子网掩码.CIDR 1.子网划分 2 ...

最新文章

  1. 访问一个HTTPS的网站的大致流程
  2. 《Drupal实战》——2.5 使用Node clone快速添加测试数据
  3. pytorch argmax_一起无聊地用PyTorch刷爆sklearn的内置数据集吧(`?ω?′)
  4. C语言实现DES加解密算法
  5. CentOS+Nginx+Supervisor部署ASP.NET Core项目
  6. 吴恩达深度学习4.1练习_Convolutional Neural Networks_Convolution_model_StepByStep_1
  7. centos mysql-dev_CentOS7 安装 mysql
  8. Windows电脑上最好的3个azw3阅读器
  9. 图像特征提取(二)——SIFT算法详解
  10. linux多线程_免费Linux下载工具,你还不知道?
  11. [统计学笔记] (八)分类数据分析
  12. 导弹的坐标系、角度和力
  13. python输入名字配对情侣网名_输入名字制作情侣网名-网名搜索
  14. 论文翻译—3D NDT算法论文(节选6.1-6.2)
  15. 云计算助力中国产业数字化升级
  16. python使用Neo4j图数据库——py2neo详解(1)
  17. 项目外包网Elance承包商使用手册中文版
  18. mysql scn_ORACLE SCN的概念
  19. 视频聊天开发参考资料
  20. Android之调用第三方API导航(高德,百度,google)

热门文章

  1. 中行网银安全控件 v1.0 官方版
  2. 飞行堡垒FN+F5风扇调节失灵怎么办? 一文教你完美解决此问题
  3. 编译原理第四章--自上而下的语法分析
  4. 服务器系统启用flash,如何在Windows Server 2016中启用Adobe Flash Player
  5. 【word论文排版教程4】样式的应用
  6. 在线购物系统后台登录界面html代码,电子商城(购物网站)html模板源码
  7. 8086系列(18):查找电话号码
  8. 小米笔记本 镜像_小米笔记本Air 13.3原装WIN10出厂系统ISO镜像1607原版镜像下载
  9. 谷歌地图的离线地图下载
  10. html5 语音直播,一种基于HTML5浏览器的音视频直播方法与流程