您的位置 首页 ADAS

ARM指令详解

算术和逻辑指令ADC:带进位的加法(AdditionwithCarry)ADC{条件}{S},,dest=op_1+op_2+carryADC将把两个操作数加起来…

算术和逻辑指令

ADC : 带进位的加法
(Addition with Carry)

ADC{条件}{S}, ,

dest = op_1 + op_2 + carry
ADC 将把两个操作数加起来,并把成果放置到意图寄存器中。它运用一个进位标志位,这样就能够做比 32 位大的加法。下列比方将加两个 128 位的数。
128 位成果: 寄存器 0、1、2、和 3
第一个 128 位数: 寄存器 4、5、6、和 7
第二个 128 位数: 寄存器 8、9、10、和 11。

ADDSR0, R4, R8; 加低端的字
ADCSR1, R5, R9; 加下一个字,带进位
ADCSR2, R6, R10; 加第三个字,带进位
ADCSR3, R7, R11; 加高端的字,带进位
假如假如要做这样的加法,不要忘掉设置 S 后缀来更改进位标志。

 

ADD : 加法
(Addition)

ADD{条件}{S}, ,

dest = op_1 + op_2
ADD 将把两个操作数加起来,把成果放置到意图寄存器中。操作数 1 是一个寄存器,操作数 2 可所以一个寄存器,被移位的寄存器,或一个当即值:

ADDR0, R1, R2; R0 = R1 + R2
ADDR0, R1, #256; R0 = R1 + 256
ADDR0, R2, R3,LSL#1; R0 = R2 + (R3 << 1)
加法能够在有符号和无符号数上进行。

 

AND : 逻辑与
(logical AND)

AND{条件}{S}, ,

dest = op_1 AND op_2
AND 将在两个操作数上进行逻辑与,把成果放置到意图寄存器中;对屏蔽你要在上面作业的位很有用。 操作数 1 是一个寄存器,操作数 2 可所以一个寄存器,被移位的寄存器,或一个当即值:

ANDR0, R0, #3; R0 = 坚持 R0 的位 0 和 1,丢掉其他的位。
AND 的真值表(二者都是 1 则成果为 1):

Op_1Op_2成果

000
010
100
111
 
BIC : 位铲除
(Bit Clear)

BIC{条件}{S}, ,

dest = op_1 AND (!op_2)
BIC 是在一个字中铲除位的一种办法,与 OR 位设置是相反的操作。操作数 2 是一个 32 位位掩码(mask)。假如假如在掩码中设置了某一位,则铲除这一位。未设置的掩码位指示此位坚持不变。

BICR0, R0, #11; 铲除 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 EOR op_2
EOR 将在两个操作数上进行逻辑异或,把成果放置到意图寄存器中;对回转特定的位有用。操作数 1 是一个寄存器,操作数 2 可所以一个寄存器,被移位的寄存器,或一个当即值:

EORR0, R0, #3; 回转 R0 中的位 0 和 1
EOR 真值表(二者不同则成果为 1):

Op_1Op_2成果

000
011
101
110
 
MOV : 传送
(Move)

MOV{条件}{S},

dest = op_1
MOV 从另一个寄存器、被移位的寄存器、或一个当即值装载一个值到意图寄存器。你能够指定相同的寄存器来完结 NOP 指令的作用,你还能够专门移位一个寄存器:

MOVR0, R0; R0 = R0… NOP 指令

MOVR0, R0, LSL#3; R0 = R0 * 8
假如 R15 是意图寄存器,将修正程序计数器或标志。这用于回来到调用代码,办法是把衔接寄存器的内容传送到 R15:

MOVPC, R14; 退出到调用者

MOVSPC, R14; 退出到调用者并康复标志位
(不遵照 32-bit 系统)
 
MVN : 传送取反的值
(MoveNegative)

MVN{条件}{S},

dest = !op_1
MVN 从另一个寄存器、被移位的寄存器、或一个当即值装载一个值到意图寄存器。不同之处是在传送之前位被回转了,所以把一个被取反的值传送到一个寄存器中。这是逻辑非操作而不是算术操作,这个取反的值加 1 才是它的取负的值:

MVNR0, #4; R0 = -5

MVNR0, #0; R0 = -1
 
ORR : 逻辑或
(logical OR)

ORR{条件}{S}, ,

dest = op_1 OR op_2
OR 将在两个操作数上进行逻辑或,把成果放置到意图寄存器中;对设置特定的位有用。操作数 1 是一个寄存器,操作数 2 可所以一个寄存器,被移位的寄存器,或一个当即值:

ORRR0, R0, #3; 设置 R0 中位 0 和 1
OR 真值表(二者中存在 1 则成果为 1):

Op_1Op_2成果

000
011
101
111
 
RSB : 反向减法
(Reverse Subtraction)

RSB{条件}{S}, ,

dest = op_2 – op_1
SUB 用操作数 two 减去操作数 one,把成果放置到意图寄存器中。操作数 1 是一个寄存器,操作数 2 可所以一个寄存器,被移位的寄存器,或一个当即值:

RSBR0, R1, R2; R0 = R2 – R1
RSBR0, R1, #256; R0 = 256 – R1
RSBR0, R2, R3,LSL#1; R0 = (R3 << 1) - R2
反向减法能够在有符号或无符号数上进行。

 

RSC : 带借位的反向减法
(Reverse Subtraction with Carry)

RSC{条件}{S}, ,

dest = op_2 – op_1 – !carry
同于 SBC,但倒换了两个操作数的前后方位。

 

SBC : 带借位的减法
(Subtraction with Carry)

SBC{条件}{S}, ,

dest = op_1 – op_2 – !carry
SBC 做两个操作数的减法,把成果放置到意图寄存器中。它运用进位标志来一共借位,这样就能够做大于 32 位的减法。SUB 和 SBC 生成进位标志的办法不同于惯例,假如需求借位则铲除进位标志。所以,指令要对进位标志进行一个非操作 – 在指令碑文期间主动的回转此位。

 

SUB : 减法
(Subtraction)

SUB{条件}{S}, ,

dest = op_1 – op_2
SUB 用操作数 one 减去操作数 two,把成果放置到意图寄存器中。操作数 1 是一个寄存器,操作数 2 可所以一个寄存器,被移位的寄存器,或一个当即值:

SUBR0, R1, R2; R0 = R1 – R2
SUBR0, R1, #256; R0 = R1 – 256
SUBR0, 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 #nor
Rx, ASL #nor
Rx, LSL Rnor
Rx, ASL Rn
承受 Rx 的内容并按用‘n’或在寄存器 Rn 中指定的数量向高有用位方向移位。最低有用位用零来填充。除了概念上的第 33 位(便是被移出的最小的那位)之外丢掉移出最左端的高位,假如逻辑类指令中 S 位被设置了,则此位将成为从桶式移位器退出时进位标志的值。

考虑下列:

MOVR1, #12
MOVR0, R1, LSL#2
在退出时,R0 是 48。 这些指令构成的总和是 R0 = #12, LSL#2 同等于 BASIC 的 R0 = 12 << 2

逻辑右移
(Logical Shift Right)

Rx, LSR #nor
Rx, LSR Rn
它在概念上与左移相对。把一切位向更低有用位方向移动。假如逻辑类指令中 S 位被设置了,则把最终被移出最右端的那位放置到进位标志中。它同于 BASIC 的 register = value >>> shift。

算术右移
(Arithmetic Shift Right)

Rx, ASR #nor
Rx, ASR Rn
类似于 LSR,但运用要被移位的寄存器(Rx)的第 31 位的值来填充高位,用来维护补码一共中的符号。假如逻辑类指令中 S 位被设置了,则把最终被移出最右端的那位放置到进位标志中。它同于 BASIC 的 register = value >> shift。

循环右移
(Rotate Right)

Rx, ROR #nor
Rx, ROR Rn
循环右移类似于逻辑右移,可是把从右侧移出去的位放置到左边,假如逻辑类指令中 S 位被设置了,则一起放置到进位标志中,这便是位的‘循环’。一个移位量为 32 的操作将导致输出与输入彻底一致,由于一切位都被移位了 32 个方位,又回到了开端时的方位!

带扩展的循环右移
(Rotate Right with extend)

Rx, RRX
这是一个 ROR#0 操作,它向右移动一个方位 – 不同之处是,它运用处理器的进位标志来供给一个要被移位的 33 位的数量。

乘法指令
指令格局

这两个指令与一般算术指令在对操作数的约束上有所不同:

给出的一切操作数、和意图寄存器有必要为简略的寄存器。
你不能对操作数 2 运用当即值或被移位的寄存器。
意图寄存器和操作数 1 有必要是不同的寄存器。
最终,你不能指定 R15 为意图寄存器。
MLA : 带累加的乘法
(Multiplication with Accumulate)

MLA{条件}{S}, , ,

dest = (op_1 * op_2) + op_3
MLA 的行为同于 MUL,但它把操作数 3 的值加到成果上。这在求总和时有用。

 

MUL : 乘法
(Multiplication)

MUL{条件}{S}, ,

dest = op_1 * op_2
MUL 供给 32 位整数乘法。假如操作数是有符号的,能够假定成果也是有符号的。

比较指令
指令格局

译注:CMP 和 CMP 是算术指令,TEQ 和 TST 是逻辑指令。把它们归入一类的原因是它们的 S 位总是设置的,便是说,它们总是影响标志位。

CMN : 比较取负的值
(Compare Negative)

CMN{条件}{P},

status = op_1 – (- op_2)
CMN 同于 CMP,但它答应你与小负值(操作数 2 的取负的值)进行比较,比方难于用其他办法完结的用于完毕列表的 -1。这样与 -1 比较将运用:

CMNR0, #1; 把 R0 与 -1 进行比较
概况参照 CMP 指令。

 

CMP : 比较
(Compare)

CMP{条件}{P},

status = op_1 – op_2
CMP 答应把一个寄存器的内容如另一个寄存器的内容或当即值进行比较,更改状况标志来答应进行条件碑文。它进行一次减法,但不存储成果,而是正确的更改标志。标志一共的是操作数 1 比操作数 2 怎么(巨细等)。假如操作数 1 大于操作操作数 2,则尔后的有 GT 后缀的指令将能够碑文。
显着的,你不需求显式的指定 S 后缀来更改状况标志… 假如你指定了它则被疏忽。

 

TEQ : 测验等价
(Test Equivalence)

TEQ{条件}{P},

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},

Status = op_1 AND op_2
TST 类似于 CMP,不产生放置到意图寄存器中的成果。而是在给出的两个操作数上进行操作并把成果反映到状况标志上。运用 TST 来检查是否设置了特定的位。操作数 1 是要测验的数据字而操作数 2 是一个位掩码。通过测验后,假如匹配则设置 Zero 标志,不然铲除它。象 CMP 那样,你不需求指定 S 后缀。

TSTR0, #%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 处理器上,它们将变成下面这些东西:
OPT1
ADRR1, #&70
LDRR0, [R1]
CMP#0
BEQZero
STRR0, [R1, #2]
.Zero
MOVPC, R14
这不是一个很好的比方,但你能够设想怎么更好的去条件碑文而不是分支。另一方面,假如你有大段的代码或许你的代码运用状况标志,那么你能够运用条件碑文来完结各类分支: 这样一个单一的简略条件碑文指令能够代替在其他处理器中存在的一切这些分支和跳转指令。
OPT1
ADRR1, #&70
LDRR0, [R1]
CMPR0, #0
STRNER0, [R1, #2]
MOVPC, R14
BL : 带衔接的分支

(Branch with Link)

BL{条件}<地址>

BL 是另一个分支指令。就在分支之前,在寄存器 14 中装载上 R15 的内容。你能够从头装载 R14 到 R15 中来回来到在这个分支之后的那个指令,

它是子例程的一个根本但强力的完结。它的作用在屏幕装载器 2 (比方 4)中得以很好的展现…

.load_new_format
BLswitch_screen_mode
BLget_screen_info
BLload_palette

.new_loop
MOVR1, R5
BLread_byte
CMPR0, #255
BLEQread_loop
STRBR0, [R2, #1]!

…在这儿咱们见到在装载器循环之前调用了三个子例程。接着,一旦分量了条件碑文就在循环中调用了 read_byte 子例程。

条件碑文

 


ARM 处理器的一个十分特别的特征是它的条件碑文。咱们指的不是根本的假如进位则分支,ARM 使这个逻辑阶段进一步深化为假如进位则 XXX- 这儿的 XXX 是任何东西。
为了举例,下面是 Intel 8086 处理器分支指令的一个列表:
JAJump if Above
JAEJump if Above or Equal
JBJump if Below
JBEJump if Below or Equal
JCJump if Carry
JCXZJump if CX Zero (CX is a register that can be used for loop counts)
JEJump if Equal
JGJump if Greater than
JGEJump if Greater than or Equal
JLJump if Less than
JLEJump if Less Than or Equal
JMPJuMP
JNAJump if Not Above
JNAEJump if Not Above or Equal
JNBJump if Not Below
JNBEJump if Not Below or Equal
JNCJump if No Carry
JNEJump if Not Equal
JNGJump if Not Greater than
JNGEJump if Not Greater than or Equal
JNLJump if Not Less than
JNLEJump if Not Less than or Equal
JNOJump if Not Overflow
JNPJump if Not Parity
JNSJump if Not Sign
JNZJump if Not Zero
JOJump if Overflow
JPJump if Parity
JPEJump if Parity Even
JPOJump if Parity Odd
JSJump if Sign
JZJump if Zero

80386 添加了:
JECXZ Jump if ECX Zero

作为比照,ARM 处理器只供给了:
B分支
BL带衔接的分支

但 ARM 供给了条件碑文,你能够不受这个表面上不灵敏的办法的约束:
BEQBranch if EQual
BNEBranch if Not Equal
BVSBranch if oVerflow Set
BVCBranch if oVerflow Clear
BHIBranch if HIgher
BLSBranch if Lower or the Same
BPLBranch if PLus
BMIBranch if MInus
BCSBranch if Carry Set
BCCBranch if Carry Clear
BGEBranch if Greater than or Equal
BGTBranch if Greater Than
BLEBranch if Less than or Equal
BLTBranch if Less Than

BLEQBranch with Link if EQual
….
BLLTBranch 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,它以相反的办法作业。当用于一个指令的时分,导致更改状况标志。这不是主动产生的 – 除非这些指令的意图是设置状况。例如:
ADDR0, R0, R1

ADDSR0, R0, R1

ADDEQSR0, R0, R1

第一个比方是一个根本的加法(把 R1 的值添加到 R0),它不影响状况寄存器。
第二个比方是同一个加法,只不过它导致更改状况寄存器。

最终一个比方是同一个加法,更改状况寄存器。不同在于它是一个有条件的指令。只要前一个操作的成果是 EQ (假如设置了 Z 标志)的时分它才碑文。

下面是条件碑文的一个作业中的比方。你把寄存器 0 与存储在寄存器 10 中内容相比较。

假如不等于 R10,则调用一个软件中止,添加它并分支回来再次做这些。不然铲除 R10 并回来到调用它的那部分代码(它的地址存储在 R14)。

\ 条件碑文的一个比方

.loop; 符号循环开端方位
CMPR0, R10; 把 R0 与 R10 相比较
SWINE&40017; 不等于: 调用 SWI &40017
ADDNER0, R0, #1;向 R0 加 1
BNEloop;分支到 loop
MOVR10, #0; 等于: 设置 R10 为零
LDMFDR13!, {R0-R12,PC};回来到调用者

注解:
SWI 编号就象我写的这样。在 RISC OS 下,它是给 Econet_DoImmediate 的编号。不要字面的承受它,这仅仅一个比方!


你或许曾经没见过 LDMFD,它从栈中装载多个寄存器。在这个比方中,咱们从一个彻底正式的栈中装载 R0 至 R12 和 R14。关于寄存器装载和存储的更多信息请参阅 str.html。


我说要装载 R14。那么为什么要把它放入 PC 中? 原因是此刻 R14 存储的值包括回来地址。咱们也能够选用:
LDMFDR13!, {R0-R12,R14}

MOVPC, 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 : STRR0,[R0,#-48]
00000004 : .ó?? : E59FF31C : LDRPC,&00000328
00000008 : .ó?? : E59FF31C : LDRPC,&0000032C
0000000C : .ó?? : E59FF31C : LDRPC,&00000330
00000010 : .ó?? : E59FF31C : LDRPC,&00000334
00000014 : .ó?? : E59FF31C : LDRPC,&00000338
00000018 : .ó?? : E59FF31C : LDRPC,&0000033C
0000001C :2?? : E3A0A632 : MOVR10,#&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 Supervisor

EntryTable
DCD ZeroRtn
DCD ReadCRtn
DCD WriteIRtn

ZeroEQU 0
ReadCEQU 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 指令的根本处理过程。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/qiche/adas/264014.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部