<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path> </svg> <p>首先说明一下 ARM是RISC(“reduced instruction set computer”,即“精简指令集计算机”)结构<br /> x86是CISC(“Complex Instruction Set Computer”,即“复杂指令系统计算机”)结构<br /> 想表达个什么意思呢?也就是它俩不一样,至于怎么不一样呢,并非这篇博客讨论的要点。因此mov指令使用起来也不一样。</p>
讯享网
接下来介绍一下ARM里面的mov指令
mov只能在寄存器之间移动数据,或者把立即数移动到寄存器中,这个和x86这种CISC架构的芯片区别最大的地方。
x86中没有ldr这种指令,因为x86的mov指令可以将数据从内存中移动到寄存器中。
而且ARM里面的mov指令有一些限制,有的数是不能表示出来的哦,待我道来:
MOV对于立即数是有要求的,就是下面的“8位图”数据。
只能由一个8bit连续有效位通过偶数次移(为什么是偶数次呢,因为必须乘以2(规定就是如此))位得到的数。

讯享网
它为什么会有这样的限制呢?
原因是,MOV本身就是一个32bit指令,除了指令码本身,它不可能再带一个可以表示32bit的数字,所以用了其中的12bit来表示
立即数,其中4bit表示移位的尾数(循环右移,且数值*2),8bit用来表示要移位的一个基数。
如果立即数超过这个范围,就没有办法用一条MOV指令给寄存器赋值
举例解释:

Operand2占了12位,其中bit11-bit8是移位数(rotate),bit7-0是一个8位的立即数(imm),MOV Rn, op2,(这里描述得不太准确,主要看下面例子)执行之后,Rn=op2 >> (rotate * 2),这里的移位是循环右移
(而且循环右移的空间范围是32位)
当我以前看到这里的时候,我总是在想,它会不会把高位数据覆盖了?如果高位数据搞了半天没用的话,全部置零,等着来循环移动覆盖,那不是多此一举吗?反而浪费了好多空间,为什么不能直接填入,还要覆盖,后来我请教了一位佬,他跟我解释是:
机器在底层是有个芯片算法的,来识别这个立即数,可以理解为机器码和芯片之间的约定(所以并不是在原始地方进行移位,这样一解释的话,那的确节省了好多空间呀)
举个例子:
以下是几个例子:
1、mov r3, #0x
虽然0x是一个32位的数,但是可以找到这么一个8位立即数,通过右移得到,看下机器码e3a03456,展开成二进制,对照下格式
1110 0011 1010 0000 0011 0100 0101 0110
cond[31:28]=1110
[27:26]=00
L[25]=1,代表op2是一个立即数
OpCode[24:21]=1101
S[20]=0
Rn[19:16]=0000

Rd[15:12]=0011,R3
Op2[11:8]=0100,右移4 * 2位
Op2[7:0]=0101 0110,8位立即数,0x56
首先要将0x56扩展成32位的无符号数,0x00000056,然后循环右移8位,就得到了0x
2、mov r3, #0x
0x是无法通过移位来得到的,这时编译器会报错,C语言编写的程序,编译器会这样来处理:
mov r3, #0x
add r3, r3, #0x14
代替mov的另外一条指令就是ldr,或许会更方便点。
MOV指令可以操作的数字举例:
0x12
0x120
0x1200
0x
MOV指令不能操作的数字举例:
0x123
0x1230
0x
mov指令到此就讲解完成。接下来是LDR和LDR伪指令:
LDR指令(有等号的ldr指令是伪汇编指令)
ldr指令既可以是大范围的地址读取伪指令,也可以内存访问指令。
当它的第二个参数前面有“=”时,表示伪指令,否则表示内存访问指令。
LDR指令:就是个单寄存器存储的ARM存储器访问指令。
(LDR补充了MOV指令不能访问内存的缺陷。)
数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令。比如想把数据从内存中某处读取到寄存器中,只能使用ldr。mov不能干这个活,mov只能在寄存器之间移动数据,或者把立即数移动到寄存器中,再次强调。
ldr r0, 0x
就是把0x这个地址中的值存放到r0中。
ldr r0, =0x
这样,就把0x这个地址写到r0中了。所以,ldr伪指令和mov是比较相似的。只不过mov指令限制了立即数的长度为8位,也就是不能超过512。而ldr伪指令没有这个限制。如果使用ldr伪指令时,后面跟的立即数没有超过8位,那么在实际汇编的时候该ldr伪指令是被转换为mov指令的。
ldr伪指令和ldr指令不是一个同东西
LDR指令的格式:
LDR{条件} 目的寄存器 <存储器地址>

作用:将 存储器地址 所指地址处连续的4个字节(1个字)的数据传送到目的寄存器中。
LDR R0,[R1,R2] ;将存储器地址为R1+R2的字数据读入寄存器R0。
LDR R0,[R1,#8] ;将存储器地址为R1+8的字数据读入寄存器R0。
LDR R0,[R1],R2 ;将存储器地址为R1的字数据读入寄存器R0,并将R1+R2的值存入R1。
LDR R0,[R1],#8 ;将存储器地址为R1的字数据读入寄存器R0,并将R1+8的值存入R1。
LDR R0,[R1,R2]! ;将存储器地址为R1+R2的字数据读入寄存器R0,并将R1+R2的值存入R1。
LDR R0,[R1,LSL #3] ;将存储器地址为R1*8的字数据读入寄存器R0。
LDR R0,[R1,R2,LSL #2] ;将存储器地址为R1+R2*4的字数据读入寄存器R0。
LDR R0,[R1,R2,LSL #2]! ;将存储器地址为R1+R24的字数据读入寄存器R0,并将R1+R24的值存入R1。
LDR R0,[R1],R2,LSL #2 ;将存储器地址为R1的字数据读入寄存器R0,并将R1+R2*4的值存入R1。
LDR R0,Label ;Label为程序标号,Label必须是当前指令的-4~4KB范围内。
要注意的是
LDR Rd,[Rn],#0x04 ;这里Rd不允许是R15。
另外LDRB 的指令格式与LDR相似,只不过它是将存储器地址中的8位(1个字节)读到目的寄存器中。
LDRH的指令格式也与LDR相似,它是将内存中的16位(半字)读到目的寄存器中。
LDR R0,=0xff
这里的LDR不是arm指令,而是伪指令。这个时候与MOVE很相似,只不过MOV指令后的立即数是有限制的。这个立即数必须是0X00-OXFF范围内的数经过偶数次右移得到的数,所以MOV用起来比较麻烦,因为有些数不那么容易看出来是否合法。
综上所述:ldr伪指令用于加载32位的立即数或一个地址值到指定寄存器。
在汇编编译源程序时,ldr伪指令被编译器替换成一条合适的指令。
若加载的常数未超出mov或mvn的范围,则使用mov或mvn指令代替该ldr伪指令,
否则汇编器将常量放入文字池,并使用一条程序相对偏移的ldr指令从文字池读出常量。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/172225.html