算术和逻辑指令
ADC : 带进位的加法
(Addition with Carry)
ADC{条件}{S}
ADC 将把两个操作数加起来,并把成果放置到意图寄存器中。它运用一个进位标志位,这样就能够做比 32 位大的加法。下列比方将加两个 128 位的数。
128 位成果: 寄存器 0、1、2、和 3
第一个 128 位数: 寄存器 4、5、6、和 7
第二个 128 位数: 寄存器 8、9、10、和 11。
ADDS
假如假如要做这样的加法,不要忘掉设置 S 后缀来更改进位标志。
ADD : 加法
(Addition)
ADD{条件}{S}
ADD 将把两个操作数加起来,把成果放置到意图寄存器中。操作数 1 是一个寄存器,操作数 2 可所以一个寄存器,被移位的寄存器,或一个当即值:
ADD
加法能够在有符号和无符号数上进行。
AND : 逻辑与
(logical AND)
AND{条件}{S}
AND 将在两个操作数上进行逻辑与,把成果放置到意图寄存器中;对屏蔽你要在上面作业的位很有用。 操作数 1 是一个寄存器,操作数 2 可所以一个寄存器,被移位的寄存器,或一个当即值:
AND
AND 的真值表(二者都是 1 则成果为 1):
Op_1
BIC : 位铲除
(Bit Clear)
BIC{条件}{S}
BIC 是在一个字中铲除位的一种办法,与 OR 位设置是相反的操作。操作数 2 是一个 32 位位掩码(mask)。假如假如在掩码中设置了某一位,则铲除这一位。未设置的掩码位指示此位坚持不变。
BIC
BIC 真值表 :
Op_1
译注:逻辑表达式为 Op_1 AND NOT Op_2
EOR : 逻辑异或
(logical Exclusive OR)
EOR{条件}{S}
EOR 将在两个操作数上进行逻辑异或,把成果放置到意图寄存器中;对回转特定的位有用。操作数 1 是一个寄存器,操作数 2 可所以一个寄存器,被移位的寄存器,或一个当即值:
EOR
EOR 真值表(二者不同则成果为 1):
Op_1
MOV : 传送
(Move)
MOV{条件}{S}
MOV 从另一个寄存器、被移位的寄存器、或一个当即值装载一个值到意图寄存器。你能够指定相同的寄存器来完结 NOP 指令的作用,你还能够专门移位一个寄存器:
MOV
假如 R15 是意图寄存器,将修正程序计数器或标志。这用于回来到调用代码,办法是把衔接寄存器的内容传送到 R15:
MOV
MVN : 传送取反的值
(MoveNegative)
MVN{条件}{S}
MVN 从另一个寄存器、被移位的寄存器、或一个当即值装载一个值到意图寄存器。不同之处是在传送之前位被回转了,所以把一个被取反的值传送到一个寄存器中。这是逻辑非操作而不是算术操作,这个取反的值加 1 才是它的取负的值:
MVN
ORR : 逻辑或
(logical OR)
ORR{条件}{S}
OR 将在两个操作数上进行逻辑或,把成果放置到意图寄存器中;对设置特定的位有用。操作数 1 是一个寄存器,操作数 2 可所以一个寄存器,被移位的寄存器,或一个当即值:
ORR
OR 真值表(二者中存在 1 则成果为 1):
Op_1
RSB : 反向减法
(Reverse Subtraction)
RSB{条件}{S}
SUB 用操作数 two 减去操作数 one,把成果放置到意图寄存器中。操作数 1 是一个寄存器,操作数 2 可所以一个寄存器,被移位的寄存器,或一个当即值:
RSB
反向减法能够在有符号或无符号数上进行。
RSC : 带借位的反向减法
(Reverse Subtraction with Carry)
RSC{条件}{S}
同于 SBC,但倒换了两个操作数的前后方位。
SBC : 带借位的减法
(Subtraction with Carry)
SBC{条件}{S}
SBC 做两个操作数的减法,把成果放置到意图寄存器中。它运用进位标志来一共借位,这样就能够做大于 32 位的减法。SUB 和 SBC 生成进位标志的办法不同于惯例,假如需求借位则铲除进位标志。所以,指令要对进位标志进行一个非操作 – 在指令碑文期间主动的回转此位。
SUB : 减法
(Subtraction)
SUB{条件}{S}
SUB 用操作数 one 减去操作数 two,把成果放置到意图寄存器中。操作数 1 是一个寄存器,操作数 2 可所以一个寄存器,被移位的寄存器,或一个当即值:
SUB
减法能够在有符号和无符号数上进行。
移位指令
译注:移位操作在 ARM 指令拘押不作为独自的指令运用,它是指令格局中是一个字段,在汇编语言中一共为指令中的选项。假如数据处理指令的第二个操作数或许单一数据传送指令中的变址是寄存器,则能够对它进行各种移位操作。假如数据处理指令的第二个操作数是当即值,在指令顶用 8 位当即值和 4 位循环移位来一共它,所以对大于 255 的当即值,汇编器测验通过在指令中设置循环移位数量来一共它,假如不能一共则生成一个过错。在逻辑类指令中,逻辑运算指令由指令中 S 位的设置或铲除来确认是否影响进位标志,而比较指令的 S 位总是设置的。在单一数据传送指令中指定移位的数量只能用当即值而不能用寄存器。
下面是给不同的移位类型的六个助记符:
LSL
ASL 和 LSL 是同等的,能够自在交换。
你能够用一个当即值(从 0 到 31)指定移位数量,或用包括在 0 和 31 之间的一个值的寄存器指定移位数量。
逻辑或算术左移
(Logical or Arithmetic Shift Left)
Rx, LSL #n
承受 Rx 的内容并按用‘n’或在寄存器 Rn 中指定的数量向高有用位方向移位。最低有用位用零来填充。除了概念上的第 33 位(便是被移出的最小的那位)之外丢掉移出最左端的高位,假如逻辑类指令中 S 位被设置了,则此位将成为从桶式移位器退出时进位标志的值。
考虑下列:
MOV
在退出时,R0 是 48。 这些指令构成的总和是 R0 = #12, LSL#2 同等于 BASIC 的 R0 = 12 << 2
逻辑右移
(Logical Shift Right)
Rx, LSR #n
它在概念上与左移相对。把一切位向更低有用位方向移动。假如逻辑类指令中 S 位被设置了,则把最终被移出最右端的那位放置到进位标志中。它同于 BASIC 的 register = value >>> shift。
算术右移
(Arithmetic Shift Right)
Rx, ASR #n
类似于 LSR,但运用要被移位的寄存器(Rx)的第 31 位的值来填充高位,用来维护补码一共中的符号。假如逻辑类指令中 S 位被设置了,则把最终被移出最右端的那位放置到进位标志中。它同于 BASIC 的 register = value >> shift。
循环右移
(Rotate Right)
Rx, ROR #n
循环右移类似于逻辑右移,可是把从右侧移出去的位放置到左边,假如逻辑类指令中 S 位被设置了,则一起放置到进位标志中,这便是位的‘循环’。一个移位量为 32 的操作将导致输出与输入彻底一致,由于一切位都被移位了 32 个方位,又回到了开端时的方位!
带扩展的循环右移
(Rotate Right with extend)
Rx, RRX
这是一个 ROR#0 操作,它向右移动一个方位 – 不同之处是,它运用处理器的进位标志来供给一个要被移位的 33 位的数量。
乘法指令
指令格局
这两个指令与一般算术指令在对操作数的约束上有所不同:
给出的一切操作数、和意图寄存器有必要为简略的寄存器。
你不能对操作数 2 运用当即值或被移位的寄存器。
意图寄存器和操作数 1 有必要是不同的寄存器。
最终,你不能指定 R15 为意图寄存器。
MLA : 带累加的乘法
(Multiplication with Accumulate)
MLA{条件}{S}
MLA 的行为同于 MUL,但它把操作数 3 的值加到成果上。这在求总和时有用。
MUL : 乘法
(Multiplication)
MUL{条件}{S}
MUL 供给 32 位整数乘法。假如操作数是有符号的,能够假定成果也是有符号的。
比较指令
指令格局
译注:CMP 和 CMP 是算术指令,TEQ 和 TST 是逻辑指令。把它们归入一类的原因是它们的 S 位总是设置的,便是说,它们总是影响标志位。
CMN : 比较取负的值
(Compare Negative)
CMN{条件}{P}
CMN 同于 CMP,但它答应你与小负值(操作数 2 的取负的值)进行比较,比方难于用其他办法完结的用于完毕列表的 -1。这样与 -1 比较将运用:
CMN
概况参照 CMP 指令。
CMP : 比较
(Compare)
CMP{条件}{P}
CMP 答应把一个寄存器的内容如另一个寄存器的内容或当即值进行比较,更改状况标志来答应进行条件碑文。它进行一次减法,但不存储成果,而是正确的更改标志。标志一共的是操作数 1 比操作数 2 怎么(巨细等)。假如操作数 1 大于操作操作数 2,则尔后的有 GT 后缀的指令将能够碑文。
显着的,你不需求显式的指定 S 后缀来更改状况标志… 假如你指定了它则被疏忽。
TEQ : 测验等价
(Test Equivalence)
TEQ{条件}{P}
TEQ 类似于 TST。区别是这儿的概念上的核算是 EOR 而不是 AND。这供给了一种检查两个操作数是否相同而又不影响进位标志(不象 CMP那样)的办法。加上 P 后缀的 TEQ 还可用于改动 R15 中的标志(在 26-bit 形式中)。概况请参照 psr.html,在 32-bit 形式下怎么做请拜见这儿。
TST : 测验位
(Test bits)
TST{条件}{P}
TST 类似于 CMP,不产生放置到意图寄存器中的成果。而是在给出的两个操作数上进行操作并把成果反映到状况标志上。运用 TST 来检查是否设置了特定的位。操作数 1 是要测验的数据字而操作数 2 是一个位掩码。通过测验后,假如匹配则设置 Zero 标志,不然铲除它。象 CMP 那样,你不需求指定 S 后缀。
TST
分支指令
B : 分支
(Branch)
B{条件}
B 是最简略的分支。一旦遇到一个 B 指令,ARM 处理器将当即跳转到给定的地址,从那里持续碑文。
留意存储在分支指令中的实践的值是相对当时的 R15 的值的一个偏移量;而不是一个肯定地址。
它的值由汇编器来核算,它是 24 位有符号数,左移两位后有符号扩展为 32 位,一共的有用偏移为 26 位(+/- 32 M)。
在其他处理器上,你或许常常见到这样的指令:
OPT 1
(取自 Acorn Electron User Guide issue 1 page 213)
在 ARM 处理器上,它们将变成下面这些东西:
OPT
这不是一个很好的比方,但你能够设想怎么更好的去条件碑文而不是分支。另一方面,假如你有大段的代码或许你的代码运用状况标志,那么你能够运用条件碑文来完结各类分支: 这样一个单一的简略条件碑文指令能够代替在其他处理器中存在的一切这些分支和跳转指令。
OPT
BL : 带衔接的分支
(Branch with Link)
BL{条件}
BL 是另一个分支指令。就在分支之前,在寄存器 14 中装载上 R15 的内容。你能够从头装载 R14 到 R15 中来回来到在这个分支之后的那个指令,
它是子例程的一个根本但强力的完结。它的作用在屏幕装载器 2 (比方 4)中得以很好的展现…
.load_new_format
…在这儿咱们见到在装载器循环之前调用了三个子例程。接着,一旦分量了条件碑文就在循环中调用了 read_byte 子例程。
条件碑文
ARM 处理器的一个十分特别的特征是它的条件碑文。咱们指的不是根本的假如进位则分支,ARM 使这个逻辑阶段进一步深化为假如进位则 XXX- 这儿的 XXX 是任何东西。
为了举例,下面是 Intel 8086 处理器分支指令的一个列表:
JA
80386 添加了:
作为比照,ARM 处理器只供给了:
B
但 ARM 供给了条件碑文,你能够不受这个表面上不灵敏的办法的约束:
BEQ
还有两个代码,
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
第一个比方是一个根本的加法(把 R1 的值添加到 R0),它不影响状况寄存器。
第二个比方是同一个加法,只不过它导致更改状况寄存器。
最终一个比方是同一个加法,更改状况寄存器。不同在于它是一个有条件的指令。只要前一个操作的成果是 EQ (假如设置了 Z 标志)的时分它才碑文。
下面是条件碑文的一个作业中的比方。你把寄存器 0 与存储在寄存器 10 中内容相比较。
假如不等于 R10,则调用一个软件中止,添加它并分支回来再次做这些。不然铲除 R10 并回来到调用它的那部分代码(它的地址存储在 R14)。
\ 条件碑文的一个比方
注解:
SWI 编号就象我写的这样。在 RISC OS 下,它是给 Econet_DoImmediate 的编号。不要字面的承受它,这仅仅一个比方!
你或许曾经没见过 LDMFD,它从栈中装载多个寄存器。在这个比方中,咱们从一个彻底正式的栈中装载 R0 至 R12 和 R14。关于寄存器装载和存储的更多信息请参阅 str.html。
我说要装载 R14。那么为什么要把它放入 PC 中? 原因是此刻 R14 存储的值包括回来地址。咱们也能够选用:
LDMFD
可是直接康复到 PC 中能够省掉这个 MOV 查办。
最终,这些寄存器很有或许被一个 SWI 调用所占用(依赖于在调用期间碑文的代码),所以你最好把你的重要的寄存器压入栈中,今后在康复它们。
SWI 指令
SWI : 软件中止
(Software Interrupt)
SWI{条件}
指令格局
这是一个简略的设备,但或许是最常用的。大都操作系统设备是用 SWI 供给的。没有 SWI 的 RISC OS 是不行幻想的。
Nava Whiteford 解说了 SWI 是怎么作业的(开端在 Frobnicate issue 12?)…
SWI 是什么?
SWI 一共 Software Interrupt。在 RISC OS
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
00000004 : .ó?? : E59FF31C : LDR
00000008 : .ó?? : E59FF31C : LDR
0000000C : .ó?? : E59FF31C : LDR
00000010 : .ó?? : E59FF31C : LDR
00000014 : .ó?? : E59FF31C : LDR
00000018 : .ó?? : E59FF31C : LDR
0000001C :
让咱们细心看一下。
除了第一个和最终一个指令之外(它们是特别情况)你见到的都是把一个新值装载到 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
Zero
ReadC
WriteI EQU 512
; SWI 包括需求的例程在位 8-23 中和数据(假如有的话)在位 0-7 中。
; 假定 R13_svc 指向了一个适宜的栈
STMFD R13, {r0-r2 , R14}
LDR R0,[R14,#-4]
BIC R0,R0, #0xFF000000
MOV R1, R0, LSR #8
ADR R2, EntryTable
LDR R15,[R2,R1,LSL #2]
WriteIRtn
………….
这便是 SWI 指令的根本处理过程。