ARM指令集详解超详细带实例.docx
《ARM指令集详解超详细带实例.docx》由会员分享,可在线阅读,更多相关《ARM指令集详解超详细带实例.docx(21页珍藏版)》请在冰豆网上搜索。
ARM指令集详解超详细带实例
算术和逻辑指令
ADC:
带进位的加法
(Additionwith 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_1ANDop_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_1AND(!
op_2)
BIC 是在一个字中清除位的一种方法,与OR位设置是相反的操作。
操作数2是一个32位位掩码(mask)。
如果如果在掩码中设置了某一位,则清除这一位。
未设置的掩码位指示此位保持不变。
BICR0,R0,#%1011;清除R0中的位0、1、和3。
保持其余的不变。
BIC真值表:
Op_1Op_2结果
000
010
101
110
译注:
逻辑表达式为Op_1ANDNOTOp_2
EOR:
逻辑异或
(logical Exclusive OR)
EOR{条件}{S},,
dest=op_1EORop_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:
传送取反的值
(Move Negative)
MVN{条件}{S},
dest=!
op_1
MVN 从另一个寄存器、被移位的寄存器、或一个立即值装载一个值到目的寄存器。
不同之处是在传送之前位被反转了,所以把一个被取反的值传送到一个寄存器中。
这是逻辑非操作而不是算术操作,这个取反的值加1才是它的取负的值:
MVNR0,#4;R0=-5
MVNR0,#0;R0=-1
ORR:
逻辑或
(logical OR)
ORR{条件}{S},,
dest=op_1ORop_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 Subtractionwith Carry)
RSC{条件}{S},,
dest=op_2-op_1-!
carry
同于 SBC,但倒换了两个操作数的前后位置。
SBC:
带借位的减法
(Subtractionwith 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)一起使用的桶式移位器(barrelshifter)。
你还可以使用桶式移位器影响在LDR/STR操作中的变址值。
译注:
移位操作在ARM指令集中不作为单独的指令使用,它是指令格式中是一个字段,在汇编语言中表示为指令中的选项。
如果数据处理指令的第二个操作数或者单一数据传送指令中的变址是寄存器,则可以对它进行各种移位操作。
如果数据处理指令的第二个操作数是立即值,在指令中用8位立即值和4位循环移位来表示它,所以对大于255的立即值,汇编器尝试通过在指令中设置循环移位数量来表示它,如果不能表示则生成一个错误。
在逻辑类指令中,逻辑运算指令由指令中S位的设置或清除来确定是否影响进位标志,而比较指令的S位总是设置的。
在单一数据传送指令中指定移位的数量只能用立即值而不能用寄存器。
下面是给不同的移位类型的六个助记符:
LSL逻辑左移
ASL算术左移
LSR逻辑右移
ASR算术右移
ROR循环右移
RRX带扩展的循环右移
ASL 和 LSL 是等同的,可以自由互换。
你可以用一个立即值(从0到31)指定移位数量,或用包含在0和31之间的一个值的寄存器指定移位数量。
逻辑或算术左移
(Logicalor Arithmetic Shift Left)
Rx,LSL#nor
Rx,ASL#nor
Rx,LSLRnor
Rx,ASLRn
接受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,LSRRn
它在概念上与左移相对。
把所有位向更低有效位方向移动。
如果逻辑类指令中S位被设置了,则把最后被移出最右端的那位放置到进位标志中。
它同于BASIC的 register=value>>>shift。
算术右移
(Arithmetic Shift Right)
Rx,ASR#nor
Rx,ASRRn
类似于LSR,但使用要被移位的寄存器(Rx)的第31位的值来填充高位,用来保护补码表示中的符号。
如果逻辑类指令中S位被设置了,则把最后被移出最右端的那位放置到进位标志中。
它同于BASIC的 register=value>>shift。
循环右移
(Rotate Right)
Rx,ROR#nor
Rx,RORRn
循环右移类似于逻辑右移,但是把从右侧移出去的位放置到左侧,如果逻辑类指令中S位被设置了,则同时放置到进位标志中,这就是位的‘循环’。
一个移位量为32的操作将导致输出与输入完全一致,因为所有位都被移位了32个位置,又回到了开始时的位置!
带扩展的循环右移
(Rotate Rightwithextend)
Rx,RRX
这是一个ROR#0操作,它向右移动一个位置-不同之处是,它使用处理器的进位标志来提供一个要被移位的33位的数量。
乘法指令
指令格式
这两个指令与普通算术指令在对操作数的限制上有所不同:
1.给出的所有操作数、和目的寄存器必须为简单的寄存器。
2.你不能对操作数2使用立即值或被移位的寄存器。
3.目的寄存器和操作数1必须是不同的寄存器。
4.最后,你不能指定R15为目的寄存器。
MLA:
带累加的乘法
(Multiplicationwith 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_1EORop_2
TEQ 类似于 TST。
区别是这里的概念上的计算是EOR而不是AND。
这提供了一种查看两个操作数是否相同而又不影响进位标志(不象 CMP 那样)的方法。
加上 P 后缀的 TEQ 还可用于改变R15中的标志(在26-bit模式中)。
详情请参照 ,在32-bit模式下如何做请参见这里。
TST:
测试位
(Test bits)
TST{条件}{P},
Status=op_1ANDop_2
TST 类似于 CMP,不产生放置到目的寄存器中的结果。
而是在给出的两个操作数上进行操作并把结果反映到状态标志上。
使用 TST 来检查是否设置了特定的位。
操作数1是要测试的数据字而操作数2是一个位掩码。
经过测试后,如果匹配则设置Zero标志,否则清除它。
象 CMP 那样,你不需要指定 S 后缀。
TSTR0,#%1;测试在R0中是否设置了位0。
分支指令
B:
分支
(Branch)
B{条件}<地址>
B是最简单的分支。
一旦遇到一个B指令,ARM处理器将立即跳转到给定的地址,从那里继续执行。
注意存储在分支指令中的实际的值是相对当前的R15的值的一个偏移量;而不是一个绝对地址。
它的值由汇编器来计算,它是24位有符号数,左移两位后有符号扩展为32位,表示的有效偏移为26位(+/-32M)。
在其他处理器上,你可能经常见到这样的指令:
OPT1
LDA&70
CMP#0
BEQZero
STA&72
.ZeroRTS
(取自AcornElectronUserGuideissue1page213)
在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:
带连接的分支
(BranchwithLink)
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是任何东西。
为了举例,下面是Intel8086处理器分支指令的一个列表:
JAJumpifAbove
JAEJumpifAboveorEqual
JBJumpifBelow
JBEJumpifBeloworEqual
JCJumpifCarry
JCXZJumpifCXZero(CXisaregisterthatcanbeusedforloopcounts)
JEJumpifEqual
JGJumpifGreaterthan
JGEJumpifGreaterthanorEqual
JLJumpifLessthan
JLEJumpifLessThanorEqual
JMPJuMP
JNAJumpifNotAbove
JNAEJumpifNotAboveorEqual
JNBJumpifNotBelow
JNBEJumpifNotBeloworEqual
JNCJumpifNoCarry
JNEJumpifNotEqual
JNGJumpifNotGreaterthan
JNGEJumpifNotGreaterthanorEqual
JNLJumpifNotLessthan
JNLEJumpifNotLessthanorEqual
JNOJumpifNotOverflow
JNPJumpifNotParity
JNSJumpifNotSign
JNZJumpifNotZero
JOJumpifOverflow
JPJumpifParity
JPEJumpifParityEven
JPOJumpifParityOdd
JSJumpifSign
JZJumpifZero
80386添加了:
JECXZJumpifECXZero
作为对比,ARM处理器只提供了:
B分支
BL带连接的分支
但ARM提供了条件执行,你可以不受这个表面上不灵活的方式的限制:
BEQBranchifEQual
BNEBranchifNotEqual
BVSBranchifoVerflowSet
BVCBranchifoVerflowClear
BHIBranchifHIgher
BLSBranchifLowerortheSame
BPLBranchifPLus
BMIBranchifMInus
BCSBranchifCarrySet
BCCBranchifCarryClear
BGEBranchifGreaterthanorEqual
BGTBranchifGreaterThan
BLEBranchifLessthanorEqual
BLTBranchifLessThan
BLEQBranchwithLinkifEQual
....
BLLTBranchwithLinkifLessThan
还有两个代码,
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:
大于(有符号)
如果一次比较之后...
设置了