前言
在平常调试芯片代码或者看数字仿真波形的过程中,常常会需要对软件代码的反汇编文件进行查看,因此有必要掌握arm常见的一些汇编指令,下面是以armv8-m手册中的指令作为参考并加上一些自己的总结。
一、跳转指令
- B (Branch)
常见用法:B<c>{<q>} <label>
此指令有四种变体,不同的变体可以跳转不同的偏移量,比如
跳转逻辑:
imm32 = SignExtend(imm8:'0', 32)
BranchTo(PC + imm32);
其中PC+imm32就是label的位置。注意imm32是由imm8通过符号扩展的方式生成的,也就是imm8的符号位也对应着imm32的符号位,可正可负,即可以往当前PC的前或者后进行相对跳转。B指令总共存在四种变体,主要是指令长度不同(2字节或4字节),由此对应出跳转的偏移量不同,四种变体对应的跳转偏移量:-256~254, -2048~2046, -1048576~1048574, -16777216~16777214。
-
BL (Branch with Link)
常见用法:BL{<c>}{<q>} <label>
与指令B类似,只不过只有一种四字节指令形式,跳转范围是-16777216~16777214,唯有一点不同,就是在跳转到label之前,会将当前指令的下一个地址保存至LR寄存器中,这样可以使得跳出去之后还能跳回来,此命令一般出现在函数跳转的时候。 -
BX (Branch and eXchange)
常见用法:BX{<c>}{<q>} <Rm>
address = R[m],PC = address
与前面的B指令不同,BX指令不再是跳转至PC的相对地址,而是以寄存器Rm中存的值作为绝对地址进行跳转,另外BX的exchange指的是:根据address[0]判断cpu的切换状态,1代表thumb state、0代表arm state。由于armv8-m仅支持thumb指令,因此Rm寄存器中值的最低位必须为1,否则将会产生不可预测的错误。 -
BLX (Branch with Link and eXchange)
常见用法:BLX{<c>}{<q>} <Rm>
这条指令是BL和BX指令的结合体,当然功能也是二者的一个结合:跳转至Rm寄存器中保存的绝对地址,并且在跳转之前会保存返回地址到LR寄存器中(这里不讨论中断情况)
简单总结下:跳转指令中,带有字母L说明需要保存返回值到LR寄存器中,否则不需要保存;带有字母X说明以寄存器中的值作为绝对地址进行跳转,否则,汇编器会计算出label与PC的偏移量然后进行相对跳转(具有跳转范围限制)。
二、比较指令
- CBNZ, CBZ
例: CBZ{<q>} <Rn>, <label>
CBNZ{<q>} <Rn>, <label>
将寄存器Rn中的值与0比较,满足条件便跳转至label:BranchWritePC(PC + imm32)
其中imm32 = ZeroExtend(i:imm5:‘0’, 32),由此可知,label只能在当前PC的前方位置,且距离不能超 过 2^7= 128bytes
-
CMP (immediate or register)
- T1: CMP{<c>}{<q>} <Rn>, #<imm8>
- T2: CMP{<c>}{<q>} <Rn>, #<const>
- CMP{<c>}{<q>} <Rn>, <Rm>
- CMP{<c>}{<q>} <Rn>, <Rm>, <shift_type> #<amount>
将立即数或者Rm或者经过移位后的Rm中的值的补码与Rn相加(相当于做减法),并根据结果更新CPSR寄存器里的N,Z,C,V,由此可知两个比较数的大小关系
-
APSR中N,Z,C,V的含义
三、位移指令
常见的移位指令有以下几种,LSL(逻辑左移),LSR(逻辑右移),ASR(算数右移),ROR(循环右移),ROR(循环右移),RRX。
-
LSL
由伪代码可知,逻辑左移shift位之后,会在右边补shift位的0,并且会把最后左移出的位作为进位值 -
LSR
由伪代码可知,逻辑右移shift位之后,会在左边补shift位0,并将最后右移出的位作为进位值 -
ASR
由伪代码可知,算数右移shift位之后,会根据原本的符号位在左边补上shift个符号位,并将最后右移出的位作为进位值 -
ROR
由伪代码可知,循环右移shift位后,会将移除的数据放进左边,如果循环移位32次,那么这个数不会改变 -
RRX
四、位运算
-
按位与
AND (immediate) or (register)
常见用法如下:
T1 : AND{<c>}{<q>} {<Rd>, } <Rn>, #<const>
T2 : AND{<c>}{<q>} {<Rdn>, } <Rdn>, <Rm>
T3 : AND{<c>}{<q>} {<Rd>, } <Rn>, <Rm> {, <shift> #<amount>}
将立即数或者(移位后)的寄存器(Rm)的值与寄存器Rn中的值按位相与的结果写入Rd中。并可以选择是否更新APSR中的位 -
按位或
ORR (immediate) or (register)
常见用法如下
T1 : ORR{<c>}{<q>} {<Rd>,} <Rn>, #<const>
T2 : ORR{<c>}{<q>} {<Rdn>,} <Rdn>, <Rm>
T3 : ORR{<c>}{<q>} {<Rd>,} <Rn>, <Rm> {, <shift> #<amount>}
将立即数或者(移位后)的寄存器(Rm)的值与寄存器Rn中的值按位相或的结果写入Rd中。并可以选择是否更新APSR中的位 -
按位取反
MVN (immediate) or (register)
常见用法如下
T1 : MVN{<c>}{<q>} <Rd>, #<const>
T2 : MVN{<c>}{<q>} <Rd>, <Rm>
T3 : MVN{<c>}{<q>} <Rd>, <Rm> {, <shift> #<mount>}
将立即数或者(移位后)的寄存器(Rm)的值按位取反的结果写入Rd中。并可以选择是否更新APSR中的位 -
异或
EOR (immediate) or (register)
常见用法如下:
T1 : EOR{<c>}{<q>} {<Rd>,} <Rn>, #<const>
T2 : EOR{<c>}{<q>} {<Rdn>,} <Rdn>, <Rm>
T3 : EOR{<c>}{<q>} {<Rd>,} <Rn>, <Rm> {, <shift> #<amount>}
将立即数或者(移位后)的寄存器(Rm)的值与寄存器Rn中的值按位异或的结果写入Rd中。并可以选择是否更新APSR中的位
五、待更新
总结
以上总结了一部分arm汇编中常出现的一些指令,以后有时间会接着补充。
熟悉汇编指令后除了能方便平常调试以外,更有意思的是将汇编代码直接嵌入在自己的程序当中,C内嵌汇编能让开发者在C的基础上更加接近底层,并能最大限度的优化程序运行效率,后续将会介绍C语言中如何内嵌汇编。