ARM指令集

来源:互联网 发布:linux ansi 颜色设置 编辑:程序博客网 时间:2024/06/15 09:08

算术和逻辑指令

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 : 位清除

(BiClear)

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 : 传送取反的值

(Move Negative)

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 之间的一个值的寄存器指定移位数量。

 

逻辑或算术左移

(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 位的数量。

乘法指令

 

指令格式

这两个指令与普通算术指令在对操作数的限制上有所不同:

  1. 给出的所有操作数、和目的寄存器必须为简单的寄存器。
  2. 你不能对操作数 2 使用立即值或被移位的寄存器。
  3. 目的寄存器和操作数 1 必须是不同的寄存器。
  4. 最后,你不能指定 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 位整数乘法。如果操作数是有符号的,可以假定结果也是有符号的。

比较指令

 

指令格式

译注:CMP 和 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 : 分支

(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_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 子例程。

条件执行

 

 

 

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 Zero80386 添加了:  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, R1  ADDS    R0, R0, R1  ADDEQS  R0, R0, R1
第一个例子是一个基本的加法(把 R1 的值增加到 R0),它不影响状态寄存器。

第二个例子是同一个加法,只不过它导致更改状态寄存器。

最后一个例子是同一个加法,更改状态寄存器。不同在于它是一个有条件的指令。只有前一个操作的结果是 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}       ;         返回到调用者
注解:
  • 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,&0000032800000008 : .óŸå : E59FF31C : LDR     PC,&0000032C0000000C : .óŸå : E59FF31C : LDR     PC,&0000033000000010 : .óŸå : E59FF31C : LDR     PC,&0000033400000014 : .óŸå : E59FF31C : LDR     PC,&0000033800000018 : .óŸå : E59FF31C : LDR     PC,&0000033C0000001C :  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 0ReadC  EQU 256WriteI 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指令的基本格式

ARM指令的基本格式为:

<Opcode>   {<Cond>}  {S}  <Rd> ,  <Rn>  { , <Opcode2> }

其中,<  >内的项是必需的,{  }内的项是可选的

(1)Opcode项

Opcode是指令助记符,即操作码,说明指令需要执行的操作,在指令中是必需的。

(2)Cond项(command)

Cond项表明了指令的执行的条件,每一条ARM指令都可以在规定的条件下执行,每条ARM指令包含4位的条件码,位于指令的最高4位[31:28]。条件码共有16种,每种条件码用2个字符表示,这两个字符可以添加至指令助记符的后面,与指令同时使用。当指令的执行条件满足时,指令才被执行,否则指令被忽略。如果在指令后不写条件码,则使用默认条件AL(无条件执行)

指令的条件码

条 件 码

助记符后缀

标    志

含    义

0000

EQ

Z置位

相等equal

0001

NE

Z清零

不相等not equal

0010

CS

C置位

无符号数大于或等于Carry Set

0011

CC

C清零

无符号数小于

0100

MI

N置位

负数minus

0101

PL

N清零

正数或零plus

0110

VS

V置位

溢出

0111

VC

V清零

没有溢出

1000

HI

C置位Z清零

无符号数大于high

1001

LS

Z置位C清零

无符号数小于或等于less

1010

GE

N等于V

带符号数大于或等于

1011

LT

N不等于V

带符号数小于least

1100

GT

Z清零且(N等于V)

带符号数大于great

1101

LE

Z清零或(N不等于V)

带符号数小于或等于

1110

AL

忽略

无条件执行all

1111

 

 

 

条件码应用举例:

例:比较两个值大小,并进行相应加1处理,C语言代码为:

if  ( a > b )  a++;

else  b++;

对应的ARM指令如下(其中R0中保存a 的值,R1中保存b的值):

CMP  R0, R1  ; R0与R1比较,做R0-R1的操作

ADDHI  R0, R0, #1  ;若R0 > R1, 则R0 = R0 + 1

ADDLS  R1, R1, #1  ; 若R0 <= R1, 则R1 = R1 + 1

 

CMP比较指令,用于把一个寄存器的内容和另一个寄存器的内容或一个立即数进行比较,同时更新CPSR中条件标志位的值。指令将第一操作数减去第二操作数,但不存储结果,只更改条件标志位。

CMP  R1, R0  ;做R1-R0的操作。

CMP  R1,#10  ;做R1-10的操作。

(3) S项(sign)

S项是条件码设置项,它决定本次指令执行的结果是否影响至CPSR寄存器的相应状态位的值。该项是可选的,使用时影响CPSR,否则不影响CPSR。

(4)Rd项(destination)

Rd是指令中的目标寄存器,它是必需的。根据指令的不同,有些指令中要求Rd必须有R0~R7之间,有些要求Rd必须在R0~R14之间,有些则没有特殊要求。

(5)Rn项

Rn是第一个操作数的寄存器,和Rd一样,不同的指令对其的使用有不同的要求。

(6)Opcode2项

Opcode2项是第二个操作数,在ARM指令中,该操作数有三种形式:立即数形式、寄存器Rm形式和寄存器加移位形式(Rm, shift)。

例如:

SUB  R3,  R1,  #10

SUB  R3,  R1,  R2

SUB  R3,  R1,  R2,  LSL  #2

SUB  R3,  R1,  R2,  LSL  R0

ARM指令详解

ARM指令集可分为以下6类:

l  跳转指令

l  数据处理指令

l  程序状态寄存器(PSR)处理指令

l  加载/存储指令

l  协处理器指令

l  异常产生指令

 

ARM指令及功能描述

助 记 符

指令功能描述

ADC

带进位加法指令

ADD

加法指令

AND

逻辑与指令

B

跳转指令

BIC

位清零指令

BKPT

软件断点

BL

带返回的跳转指令

BLX

带返回和状态切换的跳转指令

BX

带状态切换的跳转指令

CDP

协处理器数据操作指令

CMN

取反比较指令

CMP

比较指令

EOR

逻辑异或指令

LDC

存储器到协处理器的数据传输指令

LDM

加载多个寄存器的指令

LDR

存储器到寄存器的数据传输指令

MCR

从ARM寄存器到协处理器寄存器的数据传输指令

MLA

乘加运算指令

MOV

数据传输指令

MRC

从协处理器寄存器到ARM寄存器的数据传输指令

MRS

传送CPSR或SPSR的值到通用寄存器的指令

MSR

传送通用寄存器的值到CPSR或SPSR的指令

MUL

32位乘法指令

MVN

数据取反传送指令

ORR

逻辑或指令

RSB

反向减法指令

RSC

带借位的反向减法指令

SBC

带借位的减法指令

STC

协处理器寄存器写入存储器指令

STM

存储多个寄存器的值到存储器指令

STR

存储寄存器的值到存储器的指令

SUB

减法指令

SWI

软件中断指令

SWP

寄存器与存储器 或 寄存器与寄存器之间的数据交换指令

TEQ

相等测试指令

TST

位测试指令

 

1. 跳转指令

用于实现程序流程的跳转,在ARM程序中有两种方法可以实现程序流程的跳转:一是使用专门的跳转指令,二是直接向程序计数器PC写入跳转地址值。第二种方法可以实现在4GB的地址空间中的任意跳转,在跳转之前结合使用“MOV LR , PC”等类似指令,可以保存将来的返回地址值,从而实现在4GB连续的线性地址空间的子程序调用。

1)ARM指令集中的跳转指令可以实现从当前指令向前或向后的32MB的地址空间的跳转

l  B指令

格式:

B{条件}  目标地址

注意,存储在跳转指令中的实际值是相对当前PC值的一个偏移量,而不是一个绝对地址,它的值由汇编器来计算(相对寻址)。这个偏移量是一个24位的有符号数,左移两位后表示的有效偏移为26位(前后32MB的地址空间)。{}表示可以省略。

如:B Label     程序无条件跳转到标号Label处执行。

CMP R1,#0

BEQ Label

当CPSR寄存器中的Z条件码置位时,程序跳转到Label处执行。

当前PC:是指跳转指令本身的起始地址。

l  BL指令

格式:

BL{条件}  目标地址

这条指令在跳转之前,会在寄存器R14中保存当前的下一条指令的地址,因此,可以通过将R14重新加载到PC中,来返回到跳转指令之后的那条指令处执行。该指令是实现子程序调用的一种常用手段。

l  BX 指令

格式:

BX {条件}  目标地址

BX指令中所指定的目标地址,只能使用寄存器的寻址方式,即跳转的目标地址应先保存在一个寄存器中。指令在实现跳转的同时,完成处理器的工作状态的切换(ARM状态与Thumb状态间的切换)

BX指令中,用寄存器的最低位来指示切换到哪一个工作状态。如寄存器最低位为1,则把目标地址处的代码解释为Thumb代码,进入Thumb工作状态,并自动将CPSR中的控制位T置1。若寄存器最低位为0,则把目标地址处的代码解释为ARM代码,进入ARM工作状态,并自动将CPSR中的控制位T置0。

ADRL  R0,  ThumbFun + 1     ;生成分支地址并置最低位为 1

BX  R0    ;跳转到R0所指定的地址,并切换处理器到Thumb工作状态

ThumbFun  

…         ;Thumb汇编指令

BLX指令

以上两条指令的综合。

2. 数据处理指令

数据处理指令可分数据传送指令、算术逻辑运算指令和比较指令等。数据传送指令用于在寄存器和存储器之间进行数据的双向的传输。所有ARM数据处理指令均可选择使用S后缀,以影响状态标志CPSR。比较指令(CMP、CMN、TST、TEQ)不保存运算结果,这些指令也不使用S后缀,但会直接影响CPSR中的相应的状态标志位。

(1)数据传送指令MOV 和MVN

格式:

MOV {条件} {S} 目的寄存器, 源操作数

MOV指令可以完成从另一个寄存器、被移位的寄存器、或将一个立即数加载到目的寄存器。与MVN指令不同的是在传送之前,将被传送的对象先按位取反,再传送到目的寄存器。

例:MVN  R1 ,  #0XFF    ;R1 ← 0X FFFF FF00

  MVN  R1 ,  R2

  MOV PC,R14;将寄存器R14的值传送给PC,用于子程序返回。

(2) 数据比较指令CMP , CMN , TST , TEQ

例:

CMP  R1,  R2  ; 做R1 – R2 的操作,结果不保存,但影响标志位。

CMP R1 , #10  ;做R1 - 10的操作,结果不保存,但影响标志位。

CMN 指令用于把一个寄存器的内容和另一个寄存器的内容或立即数取反后进行比较操作,根据运算结果影响CPSR中的标志位。该指令实际完成操作数1和操作数2相加,并根据结果更改条件标志位。

TST位测试指令,用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的与运算,并根据运算结果更新CPSR中条件标志位的值。操作数1是要测试的数,而操作数2 是一个位掩码,该指令一般用来检测是否设置了特定的位。

TST {条件} 操作数1, 操作数2

例:TST  R0,  #0X0000 0040 ; 指令用来测试R0的位3是否为1。

TST指令通常和EQ、NE条件码配合使用,当所有测试位为0时,EQ有效,而只要有一个测试位不为0,则NE有效。

TEQ相等测试指令,用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的异或运算,并根据运算结果更新CPSR中的条件标志位。指令用于比较两个操作数是否相等。如果相等,则 Z = 1,否则Z = 0。指令通常和EQ、NE条件码配合使用

例:TEQ  R1, R2

TST R1,#%1;测试R1中是否设置了最低位(%表示二进制数)

(3)逻辑运算类指令:AND、ORR、EOR、BIC

格式:逻辑类指令 {条件} {S} 目的寄存器,操作数1, 操作数2

S选项,说明运算结果影响CPSR的条件标志位,没有S选项,则不影响CPSR的条件标志位。

操作数1应该是一个寄存器,操作数2可以是一个寄存器、被移位的寄存器或一个立即数。

AND指令常用于将操作数1的某个位置0;ORR指令常用于将操作数1的某个位置1;EOR(异或)指令常用于将操作数1的某个位取反。与0相异或,保持不变,与1相异或,则取反。BIC指令用于清除操作数1的某些位,并把结果放置到目的寄存器中,如果在掩码中设置了某一位,则清除这一位。未设置的掩码位保持不变。

例:BIC R1, R1, #0X0F    ;将R1的低四位清零,其他位不变

(4)算术运算类指令:ADD、ADC、SUB、SBC、RSB、RSC

格式:算术运算类指令 {条件} {S} 目的寄存器,操作数1,操作数2

目的寄存器,操作数1和操作数2使用的寄存器必须在R0~R7之间。

操作数1应该是一个寄存器,操作数2可以是一个寄存器、被移位的寄存器或一个立即数。

例:ADDS  R1, R1,#10   ;结果影响标志位

    ADD   R1, R1, R2   ;结果不影响标志位

    ADD   R3, R1, R2, LSL #2   ; R3 = R1 + ( R2 << 2 )

ADD指令完成的功能是将操作数1加上操作数2,结果送到目的寄存器。

ADC指令完成的功能是将操作数1加上操作数2,再加上标志位C的值,结果送到目的寄存器。

SUB指令完成的功能是将操作数1减去操作数2,结果送到目的寄存器。

SBC指令完成的功能是将操作数1减去操作数2,再减去标志位C的取反值,结果送到目的寄存器。

RSB逆向减法指令完成的功能是将操作数2减去操作数1,结果送到目的寄存器。

RSC带借位的逆向减法指令完成的功能是将操作数2减去操作数1,再减去标志位C的取反值,结果送到目的寄存器。

例:

SUB  R0, R1, #256  ;R0 = R1 - 256 , 结果不影响标志位

SUBS  R0, R2,R3,LSL #1 ;R0 = R2 - ( R3 <<1 ),结果影响标志位

SUB  SP , #380   ;SP = SP - 380

SBC  R0, R1, R2 ;R0 = R1 - R2 - !C

RSC  R0, R1, R2 ;R0 = R2 - R1 - !C

(5)乘法指令与乘加指令

ARM微处理器支持的乘法指令与乘加指令共有6条,可分为运算结果为32位和结果为64位两类,与前面的数据处理指令不同,指令中的所有操作数、目的寄存器必须为通用寄存器,不能对操作数使用立即数或被移位的寄存器,同时,目的寄存器和操作数1必须是不同的寄存器。

l  MUL指令

格式:

MUL {条件} {S} 目的寄存器,操作数1, 操作数2

功能:

目的寄存器 = 操作数1 × 操作数2,同时可以根据运算结果设置CPSR中相应的条件标志位N和Z。操作数1和操作数2均为32位的有符号数或无符号数。

l  MLA指令

格式:

MLA {条件} {S} 目的寄存器,操作数1, 操作数2, 操作数3

功能:

目的寄存器 = 操作数1 × 操作数2 + 操作数3,同时可以根据运算结果设置CPSR中相应的条件标志位N和Z。操作数1和操作数2均为32位的有符号数或无符号数。

l  SMULL指令(S:Signed, 有符号)

格式:

SMULL {条件} {S} 目的寄存器Low,目的寄存器High,操作数1, 操作数2

功能:

目的寄存器Low = (操作数1 × 操作数2 )的低32位,

目的寄存器High = (操作数1 × 操作数2 )的高32位,同时可以根据运算结果设置CPSR中相应的条件标志位。操作数1和操作数2均为32位的有符号数

例:

SMULL  R0, R1, R2, R3

;R0 = (R2 ×R3)的低32位, R1 = (R2 ×R3)的高32位。

l  SMLAL指令(S:Signed, 有符号)

格式:

SMLAL {条件} {S} 目的寄存器Low,目的寄存器High,操作数1, 操作数2

功能:

目的寄存器Low = (操作数1 × 操作数2 )的低32位 + 目的寄存器Low,

目的寄存器High = (操作数1 × 操作数2 )的高32位 + 目的寄存器High,同时可以根据运算结果设置CPSR中相应的条件标志位。操作数1和操作数2均为32位的有符号数

例:

SMLAL  R0, R1, R2, R3

;R0 = (R2 ×R3)的低32位 + R0,

;R1 = (R2 ×R3)的高32位 + R1。

l  UMULL指令(U:UnSigned, 无符号)

格式:

UMULL {条件} {S} 目的寄存器Low,目的寄存器High,操作数1, 操作数2

功能:

目的寄存器Low = (操作数1 × 操作数2 )的低32位,

目的寄存器High = (操作数1 × 操作数2 )的高32位,同时可以根据运算结果设置CPSR中相应的条件标志位。操作数1和操作数2均为32位的无符号数

例:

UMULL  R0, R1, R2, R3

;R0 = (R2 ×R3)的低32位, R1 = (R2 ×R3)的高32位。

l  UMLAL指令(U:UnSigned, 无符号)

格式:

UMLAL {条件} {S} 目的寄存器Low,目的寄存器High,操作数1, 操作数2

功能:

目的寄存器Low = (操作数1 × 操作数2 )的低32位 + 目的寄存器Low,

目的寄存器High = (操作数1 × 操作数2 )的高32位 + 目的寄存器High,同时可以根据运算结果设置CPSR中相应的条件标志位。操作数1和操作数2均为32位的无符号数

例:

UMLAL  R0, R1, R2, R3

;R0 = (R2 ×R3)的低32位 + R0,

;R1 = (R2 ×R3)的高32位 + R1。

 

3. 程序状态寄存器访问指令

功能:用于在程序状态寄存器和通用寄存器之间传送数据。

l  MRS指令

格式:

MRS {条件}  通用寄存器, 程序状态寄存器(CPSR、SPSR)

功能:

将状态寄存器的内容传送到通用寄存器。

使用环境:

(1)当需要改变程序状态寄存器的内容时,可用MRS将状态寄存器的内容读入到通用寄存器,修改后再写回到程序状态寄存器。

(2)当在异常处理或进程切换时,需要保存程序状态寄存器的值,可先用该指令读出程序状态寄存器的值,然后保存。

l  MSR指令

格式:

MSR {条件}  程序状态寄存器(CPSR、SPSR)_<域>,操作数

功能:

将操作数的内容传送到程序状态寄存器的特定域中。其中,操作数可以为通用寄存器或立即数。<域>用于设置程序状态寄存器中需要操作的位,32位的程序状态寄存器分为4个域:

F域:位31~位24为条件标志位域;

S域:位23~位16为状态位域;

X域:位15~位8为扩展位域;

C域:位7~位0为控制位域;

使用环境:

(1)当需要改变程序状态寄存器的内容时,可用MRS将状态寄存器的内容读入到通用寄存器,修改后再写回到程序状态寄存器。

(2)当在异常处理或进程切换时,需要保存程序状态寄存器的值,可先用该指令读出程序状态寄存器的值,然后保存。

例:

MSR  CPSR , R0   ;CPSR←R0

MSR  SPSR_c , R0  ;传送R0到SPSR,但仅修改SPSR中的控制位域

MSR  CPSR_c ,  #0XD3   ; CPSR[7..0] = 0XD3 ,  即切换到管理模式

MSR  CPSR_cxsf ,  R3  ; CPSR ← R3

注意:只有在特权模式下,才能修改状态寄存器。

程序中不能通过MSR指令直接修改CPSR中的T控制位来实现ARM/Thumb状态的切换,必须使用BX指令来完成处理器状态的切换。

MRS与MSR配合使用,可以实现CPSR或SPSR寄存器的读/修改/写操作,进行处理器模式 切换,进行允许/禁止IRQ/FIQ中断等的设置。

例:使能IRQ中断

MRS  R0 ,  CPSR

BIC   R0 ,  R0 ,  #0X80

MSR  CPSR_c  ,  R0

MOV  PC ,  LR

例:禁止IRQ中断

MRS  R0 ,  CPSR

ORR   R0 ,  R0 ,  #0X80

MSR  CPSR_c  ,  R0

MOV  PC ,  LR

 

4. 存储器加载/存储指令

功能:用于在寄存器和存储器之间传送数据,加载指令用于将存储器中的数据传送到寄存器,存储指令则将寄存器中的数据传送到存储器。

存储器加载/存储指令分为单个存储器加载/存储指令和多个存储器加载/存储指令。

(1)单个存储器加载/存储指令

LDR字数据加载指令;

LDRH(Half)半字数据加载指令;

LDRB字节数据加载指令;

STR字数据存储指令;

STRH半字数据存储指令;

STRB字节数据存储指令。

 

l  加载指令

格式:

加载指令 {条件} 目的寄存器, <存储器地址>

例:

LDR  R0 , [R1]   ;将地址为R1的字数据读入R0。

LDR  R0 , [R1,R2]  ;将地址为R1+R2的字数据读入R0。

LDR  R0 , [R1, #4] ;将地址为R1+4的字数据读入R0。

LDR  R0 , [R1, R2]! ;将地址为R1+R2的字数据读入R0,并将新地址R1+R2写入R1。

LDR  R0 , [R1, R2, LSL #2 ] !

;将地址为R1 + R2 × 4的字数据读入R0,并将新地址R1 + R2 × 4写入R1。

 

LDRH指令用于从存储器中将一个16位的半字数据传送到目的寄存器中,同时将寄存器的高16位清零。

LDRH  R0 , [R1]   ;将地址为R1的半字数据读入R0,并将R0的高16位清零。

LDRB指令用于从存储器中将一个8位的字节数据传送到目的寄存器中,同时将寄存器的高24位清零。

LDRB  R0 , [R1]   ;将地址为R1的字节数据读入R0,并将R0的高24位清零。

注意:当是字操作时,操作数的地址必须是字对齐的,如果是半字操作,操作数的地址必须是半字对齐。否则,读出的数据是无效,随机的。

例:

LDR  R0 ,   [R1 ,  # 2 ]

LDRH  R0 ,   [R1 ,  # 1 ]

l  存储指令

格式:

存储指令 {条件} 源寄存器, <存储器地址>

例:

STR  R0 , [R1] , #8  

;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。

STR  R0 , [R1 , #8]  ;将R0中的字数据写入以R1+8为地址的存储器中。

STRH指令用于从源寄存器中将一个16位的半字数据传送到存储器中。该半字数据为源寄存器中的低16位。

STRH  R0 , [R1, #8]  ;将寄存器R0中的低16位写入以R1 + 8为地址的存储器中。

STRB指令用于从源寄存器中将一个8位的字节数据传送到存储器中。该字节数据为源寄存器中的低8位。

注意:当是字操作时,操作的地址必须是字对齐的,如果是半字操作,操作的地址必须是半字对齐。否则,读出的数据是无效,随机的。

(2)批量数据加载/存储指令

功能:可以一次在一片连续的存储器单元和多个寄存器之间传送数据,批量加载指令用于将一片连续的存储器中的数据传送到多个寄存器,批量数据存储指令完成相反的操作。

LDM(或STM)指令(Load Data To Multiple Register)

格式:

LDM(或STM) {条件} {类型} 基址寄存器{!}  ,寄存器列表{^}

{类型}为以下几种情况:

类 型

含     义

IA

每次操作后,地址加4

IB

每次操作前,地址加4

DA

每次操作后,地址减4

DB

每次操作前,地址减4

FD

满递减堆栈

ED

空递减堆栈

FA

满递增堆栈

EA

空递增堆栈

{!}为可选后缀,若选用,则当数据传送完毕之后,将最后的地址写入基址寄存器,否则基址寄存器的内容不改变。

基址寄存器不允许为R15,寄存器列表可以为R0 ~ R15的任意组合。

{^}为可选后缀,当指令为LDM且寄存器列表中包含有R15,选用该后缀表示:除了正常的数据传送之外,还将SPSR复制到CPSR。同时,该后缀还表示传入或付传出的是用户模式下的寄存器,而不是当前模式下的寄存器。

例:

STMFD  SP!, {R0 - R7 , LR}   ;现场保存,将R0 - R7 , LR入栈,满递减堆栈。

LDMFD  SP!, {R0 - R7 , PC}^  ;恢复现场,异常处理返回,满递减堆栈

在进行数据复制时,先设置好源数据指针,然后使用块拷贝寻址指令进行读取和存储。而在堆栈操作中,则要先设置堆栈指针SP,然后使用堆栈寻址指令实现堆栈操作。

 

5. 数据交换指令

功能:支持在存储器和寄存器之间交换数据。

SWP ( Swap )  字数据交换指令;

SWPB  字节数据交换指令。

格式:

交换指令  {条件} 目的寄存器, 源寄存器1, [源寄存器2]

例:

SWP  R0, R1, [R2] ;将R2所指的存储器中的字数据传送到R0,同时将R1中的字数据传送到R2所指的存储器单元。

显然,当源寄存器1与目的寄存器是同一个寄存器时,就完成了寄存器与存储器间的交换操作。

SWPB指令用于将源寄存器2所指向的存储器中的字节数据到目的寄存器中,目的寄存器的高24位清零,同时将源寄存器1中的低8位数据(低位字节)传送到源寄存器2所指向的存储器中。

 

6. 异常产生指令

异常指令有两条:SWI软件中断指令和BKPT断点中断指令。

l  SWI中断指令

格式:

SWI  {条件}  24位的立即数

功能:

产生软件中断,方便用户程序调用操作系统的系统例程。

操作:

切换运行模式到管理模式,设置PC来执行在地址0X08处的下一条指令,设置相应的R13_svc和R14_svc。该指令的操作与执行BL 0X08这条指令的效果是相同的。不同的地方在于,SWI还带有指明系统例程的类型的“24位的立即数”。在具体应用中,为便于记忆,可以使用字符串代替“24位的立即数”,例如:SWI  “OS_Write0”和 SWI  0X02是一样的。当指令中24位的立即数被忽略时,系统例程的类型由通用寄存器R0的内容决定。传送给系统例程的参数通过通用寄存器来传递。

l  BKPT指令

格式:

BKPT  16位的立即数

功能:

用于产生软件断点中断,执行时中断正常指令,进入相应的调试子程序。

 

7. 协处理器指令

ARM处理器可支持多达16个协处理器,每个协处理器只执行针对其自身的协处理指令。ARM的协处理器指令主要用于ARM处理器初始化、协处理器的数据处理操作、在ARM处理器与协处理器的寄存器之间传送数据、在协处理器和存储器之间传送数据。ARM协处理器指令有以下5条:

l  CDP协处理器数据操作指令;

l  LDC协处理器数据加载指令;

l  STC协处理器数据存储指令;

l  MCR  ARM处理器寄存器到协处理器寄存器的数据传送指令;

l  MRC  协处理器寄存器到ARM处理器寄存器的数据传送指令。

(1)CDP指令

格式:

CDP {条件}协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2

功能:用于ARM处理器通知协处理器执行特定的操作,若协处理器不能执行指定的操作,则产生未定义指令异常。

注意:指令中涉及到的寄存器都是协处理器的寄存器,不涉及ARM处理器的寄存器和存储器。操作码1、操作码2是协处理器要执行的操作。

例: CDP  p5 ,  1 ,  c3 ,  c4 ,  c5 , 2

;指示协处理器P5,执行操作1,可选操作为2;C3, C4, C5是相应的协处理器寄存器。

(2) LDC指令

格式:

LDC {条件}{L}协处理器编码,目的寄存器,[源寄存器]

功能:

用于将源寄存器所指向的存储器中的字数据传送到目的寄存器中。若协处理器不能成功执行,则产生未定义指令异常。选项{L}表示指令为长读取操作,可用于双精度数据的传输。

注意:指令中涉及到的源寄存器是ARM处理器的寄存器。

例:LDC  P3, C4, [R2, #4]

(3) STC指令

格式:

STC {条件}{L}协处理器编码,源寄存器,[目的寄存器]

功能:

用于将源寄存器中的字数据传送到目的寄存器所指向的存储器中。若协处理器不能成功执行,则产生未定义指令异常。选项{L}表示指令为长读取操作,可用于双精度数据的传输。

注意:指令中涉及到的目的寄存器是ARM处理器的寄存器。

例:STC  P3, C4, [R0]

;将协处理器P3的寄存器C4中的数据传送到ARM处理器的寄存器R0所指向的存储器.

(4)  MCR指令

格式:

MCR {条件}协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,协处理器操作码2

功能:

MCR指令用于将ARM处理器寄存器中的数据传送到协处理器的寄存器中。若协处理器不能完成这个操作,将引发未定义指令异常。源寄存器为ARM处理器的寄存器。

(5)  MRC指令

格式:

MRC {条件}协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2

功能:

MRC指令用于将协处理器寄存器中的数送到ARM处理器的寄存器中。若协处理器不能完成这个操作,将引发未定义指令异常。源寄存器为ARM处理器的寄存器。

例:MRC  P3  , 3 , R0 , C4 , C5 , 6

;将协处理器P3的寄存器C4与C5中的数据传送到ARM的寄存器中,并执行编号为3和6的操作。

Thumb指令集合

Thumb指令集是ARM指令集的一个子集,允许指令编码为16位的长度,Thumb指令集在保留32位代码优势的同时,大大节省了系统的存储空间。

当处理器在执行ARM程序段时,称ARM处理器处于ARM工作状态,当处理器在执行Thumb程序段时,称ARM处理器处于Thumb工作状态。

在编写Thumb指令时,先要用伪指令CODE16声明以下为Thumb指令代码,在ARM指令代码中可以使用BX指令跳转到Thumb指令代码片。同样编写ARM代码时,则使用伪指令CODE32进行声明,在Thumb指令代码中使用BX指令可以跳转到ARM指令代码处。

大多数Thumb指令是无条件执行的,而几乎所有的ARM指令都是有条件执行的。由于Thumb数据处理指令中的目的寄存器与其中的一个源寄存器相同,Thumb指令在指令编码时由三个操作数改为两个操作数。

通常实现同样的程序功能时,所需的Thumb指令的条数比ARM指令多。但使用Thumb指令集合的代码有以下特点:

l  比ARM代码更节略存储空间。

l  使用的指令条数比ARM代码多。

l  若使用32位的存储器,ARM代码比Thumb代码快约40%。

l  若使用16位的存储器,Thumb代码比ARM代码快约40%~50%。

l  与ARM代码相比较,使用Thumb代码,存储器的功耗会降低约30%。