对于高级语言,数据来源可能是文件或者是直接用户输入,这些数据会被存储在变量当中然后进行相应的数据计算和处理。对于汇编设计的硬件,数据资源来源于CPU外设,或者其他外部设备硬件寄存器中,其数据获取的方式设计数据寻址指令。
1. 处理器寻址
数据资源获取是汇编编写代码中核心重要的一项数据,寻址方式是根据指令中给定的地址码字段来实现寻找真实操作数地址的方式。Mov指令涉及源与目的是多样的,可以在CPU内或CPU和寄存器之间传送字节,它传送的信息可以从寄存器到寄存器,立即数到寄存器,立即数到储存单元,从储存单元到寄存器,从寄存器到储存单元,从寄存器或储存单元到除CS外的段寄存器(注意立即数不能直接送段寄存器),从段寄存器到寄存器或储存单元。ARM处理器共包含9中寻址方式。
1.什么是寻址?数据都存在存储器中,寻址简单地说就是找到存储数据或指令的地址。存储器有很多存储单元,用于存储数据。或者说,寻址就是读取数据所在储存装置中对应地址编号中存储的内容。以存储单位寻址分为位,字,节,字,双字寻址。以功能寻址分为直接寻址,间接寻址,寄存器寻址。寻址就是为运算和执行命令。
2.什么是寻址方式?通常是指某一个CPU指令系统中规定的寻找操作数所在地址的方式,或者说通过什么的方式找到操作数。寻址方式的方便与快捷是衡量CPU性能的一个重要方面.
1.1. 立即数寻址:
操作数在指令中,如: ADD R0,R0,#10 ---->R0 = R0 + 10
特点:快,单周期;但受合法立即数的限制,目前测试ARM9立即数限制是256;
1.2. 寄存器寻址:
利用寄存器中的值作为操作数,如:ADD R0,R1,R2 ---->R0 = R 1 + R 2
特点:也是单周期
1.3. 寄存器移位寻址:
寄存器中的值移位后得到操作数,用到桶形移位器
介绍一下桶形移位器:
LSL:(逻辑左移),相当于无符号数x2;
ASR: (算术右移),相当于带符号的数除2;
LSR: (逻辑右移),相当于无符号数除2;
ROR:(循环右移),相当于位轮换;
RRX:(带扩展的循环右移),位轮换,从CF到MSB都参与(这一项谁比较懂的帮忙解释一下)
如:ADD R0,R1,R2,LSL #2 -------->R0 = R1 + R2<<2;
1.4. 寄存器间接寻址:
寄存器中的值作为操作数的地址,操作数本身放在存储器中;
如:LDR R0,[R1] ---->R0 = [R1],取出R1存的地址中的值,赋给R0;
1.5. 基址变址寻址:
基址寄存器的内容与指令中的偏移量相加,得到有效操作数的地址,然后访问该地址空间;分三种:
1)、前索引:
如:LDR R0,[R1,#4] --->R1存的地址+4,访问新地址里面的值,放到R0;
2)、自动索引:
如:LDR R0, [R1,#4]! --->在前索引的基础上,新地址回写进R1;注:!表示回写地址
3)、后索引:
如:LDR R0 [R1],#4 --->R1存的地址的内容写进R0,R1存的地址+4再写进R1;
1.6. 多寄存器寻址:
一条指令完成多个寄存器的传送,最多16个寄存器;
如:STMxx R0!,{R1-R5} 注:xx是IDAB的任意组合:I-增;D-减;A-后;B-先;
执行这类指令要考虑如下几个问题:
1)、基址寄存器指向原始地址有没有放一个有效值?
2)、寄存器列表哪个寄存器被最先传送?
3)、存储器地址增长方向?
4)、指令执行完成后,基址寄存器有没有指向一个有效值?
如:STMia R0!,{R1-R5} 的答案分别是:有;R1;低-高;没有。
为什么要考虑这么多,因为涉及到数据还原的问题;
如:STMib r0!,[r1-r5]
LDMda r0! , [r1-r5] ------还原
1.7. 相对寻址:
pc当前值位基址,指令中值为偏移量,相加作为操作数的地址;如 B/BL 不过有范围限制 pc+-32Mbytes;再如beq flag。
1.8. 堆栈寻址:
先进先出的原则;
如:STMxx SP! (r0 - r12) --- xx是FEAD的任意组合,不过规定只用fd;
4中堆栈方式:F-满;E-空;A-后;B-先;
如:入栈:STMfd SP!,(r0-r12)
出栈:LDMfd SP! ,(r0-r12)
1.9. 伪指令
LDR伪指令的形式是“LDR Rn, =expr”
2. arm指令中mov和ldr有什么区别?
ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令。
比如想把数据从内存中某处读取到寄存器中,只能使用ldr
比如:
ldr r0, 0x12345678
就是把0x12345678这个地址中的值存放到r0中。
而mov不能干这个活,mov只能在寄存器之间移动数据,或者把立即数移动到寄存器中,这个和x86这种CISC架构的芯片区别最大的地方。
x86中没有ldr这种指令,因为x86的mov指令可以将数据从内存中移动到寄存器中。
另外还有一个就是ldr伪指令,虽然ldr伪指令和ARM的ldr指令很像,但是作用不太一样。ldr伪指令可以在立即数前加上=,以表示把一个地址写到某寄存器中,比如:
ldr r0, =0x12345678
这样,就把0x12345678这个地址写到r0中了。所以,ldr伪指令和mov是比较相似的。只不过mov指令限制了立即数的长度为8位,也就是不能超过512。而ldr伪指令没有这个限制。如果使用ldr伪指令时,后面跟的立即数没有超过8位,那么在实际汇编的时候该ldr伪指令是被转换为mov指令的。
ldr伪指令和ldr指令不是一个同东西。
ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令。 比如想把数据从内存中某处读取到寄存器中,只能使用ldr 比如: ldr r0, 0x12345678 就是把0x12345678这个地址中的值存放到r0中。 而mov不能干这个活,mov只能在寄存器之间移动数据,或者把立即数移动到寄存器中,这个和x86这种CISC架构的芯片区别最大的地方。 x86中没有ldr这种指令,因为x86的mov指令可以将数据从内存中移动到寄存器中。 另外还有一个就是ldr伪指令,虽然ldr伪指令和ARM的ldr指令很像,但是作用不太一样。ldr伪指令可以在立即数前加上=,以表示把一个地址写到某寄存器中,比如: ldr r0, =0x12345678 这样,就把0x12345678这个地址写到r0中了。所以,ldr伪指令和mov是比较相似的。只不过mov指令限制了立即数的长度为8位,也就是不能超过512。而ldr伪指令没有这个限制。如果使用ldr伪指令时,后面跟的立即数没有超过8位,那么在实际汇编的时候该ldr伪指令是被转换为mov指令的。ldr伪指令和ldr指令不是一个同东西。
/*
* 2018/12/20 11:43 qing
*/
/*
* ARM中的MOV指令格式
*/
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
| Cond | 0 0 | L| OpCode | S | Rn | Rd | Operand2 |
op2是占了12位,其中 bit 11 - bit 8 是移位数(rotate), bit 7 - 0 是一个 8 位的立即数(imm),
mov Rn, op2 @ 执行之后 Rn = op2 >> (rotate * 2),这里的移位是循环右移,这就决定了mov指令不是所有的立即数都能表示的
1. mov r3, #0x56000000
虽然0x56000000是一个32位的数,但是可以找到这么一个8位立即数,通过右移得到,看下机器码 e3a03456 ,展开成二进制,对照下格式
31 27 23 19 15 11 7 3
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 位,就得到了 0x56000000
2. mov r3, #0x56000014
0x56000014是无法通过移位来得到的,这时编译器会报错,C语言编写的程序,编译器会这样来处理:
mov r3, #0x56000000
add r3, r3, #0x14
代替mov的另外一条指令就是ldr,或许会更方便点。
/*
* 在ARM中使用立即数的规律
*/
在ARM中不能像X86那样直接将立即数加载到寄存器中。因为你使用的立即数是受限的。
可以通过LDR绕过这些限制,有以下的的技巧:
每条ARM指令的宽度是32位,所有的指令都是可以条件执行的。
有16中条件可以使用而且每个条件在机器码中的占位都是4位。之后我们需要2位来做为目的寄存器。2位作为第一操作寄存器,
1位用作设置状态的标记位,再加上比如操作码(opcode)这些的占位。最后每条指令留给我们存放立即数的空间只有12位宽。也就是4096个不同的值。
这也就意味着ARM在使用MOV指令时所能操作的立即数值范围是有限的。那如果很大的话,只能拆分成多个部分外加移位操作拼接了。
所以这剩下的12位可以再次划分,8位用作加载0-255中的任意值,4位用作对这个值做0~30位的循环右移。
这也就意味着这个立即数可以通过这个公式得到:v = n ror 2 * r。换句话说,有效的立即数都可以通过循环右移来得到。
这里有一个例子
有效值:
#256 // 1 循环右移 24位 --> 256
#384 // 6 循环右移 26位 --> 384
#484 // 121 循环右移 30位 --> 484
#16384 // 1 循环右移 18位 --> 16384
#2030043136 // 121 循环右移 8位 --> 2030043136
#0x06000000 // 6 循环右移 8位 --> 100663296 (十六进制值0x06000000)
Invalid values:
#370 // 185 循环右移 31位 --> 31不在范围内 (0 – 30)
#511 // 1 1111 1111 --> 比特模型不符合
#0x06010000 // 1 1000 0001.. --> 比特模型不符合
LDR R0,=0x56000010 @R0 is set to be register GPBCON and is used to select pin function for Port B 以上三条汇编语句的功能是将数值0x00004000存储到以0x56000010为地址的存储单元中。 其反汇编代码如下 0: e59f0044 ldr r0, [pc, #68] ; 0x4c ...... 4c: 56000010 undefined 可见LDR R0,=0x56000010 被转换成ldr指令来执行 再来看 LDR R0,=0x56000000 MOV R1,#0x00004000 其反汇编代码如下 0: e3a00456 mov r0, #1442840576 ; 0x56000000 这里LDR R0,=0x56000000 被转换成mov r0, #1442840576 也就是说LDR伪指令是根据后面的数据值来决定转换为ldr指令或MOV指令执行。 那么同样是给R0赋值,LDR R0,=0x56000010 能否用mov r0, #0x56000010来代替呢, mov指令后面的立即数是有限制的,这个立即数必须由一个8位的二进制数经过偶数次右移后得到才合法数据 LDR R0,=0x56000000 被转换成mov r0, #0x56000000,其中立即数0x56000000是可以由0x56经过循环右移得到的,而0x56000010无法通过一个8位的二进制数经过偶数次右移后得到,所以无法转换成mov指令来实现。 再举例如下: mov R0,#0x101 mov R0,#0xFF1 以上两条指令都不正确,因为立即数不合法。 这样的话用MOV指令是比较麻烦的,因为有些简单的数据比较容易看出来,有些数据即不容易看出来是否是合法数据。 为了解决这个问题,我们可以用LDR伪指令来实现,根据后面的立即数来决定转换为ldr指令或MOV指令执行,符合MOV指令的立即数合法性要求就转换为MOV指令,不符合的话就转换为LDR加载指令来实现。 |
1.2.15.ARM汇编指令集1
两个概念:指令与伪指令
(汇编)指令是CPU机器指令的助记符,经过编译后会得到一串10组成的机器码,可以由CPU读取执行。
(汇编)伪指令本质上不是指令(只是和指令一起写在代码中),它是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码。
两种不同风格的ARM指令
ARM官方的ARM汇编风格:指令一般用大写、Windows中IDE开发环境(如ADS、MDK等)常用。如: LDR R0, [R1]
GNU风格的ARM汇编:指令一般用小写字母、linux中常用。如:ldr r0, [r1]
寻址方式是根据指令中给定的地址码字段来实现寻找真实操作数地址的方式,ARM处理器共包含9中寻址方式。
arm指令中mov和ldr有什么区别?
ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令。
比如想把数据从内存中某处读取到寄存器中,只能使用ldr
比如:
ldr r0, 0x12345678
就是把0x12345678这个地址中的值存放到r0中。
而mov不能干这个活,mov只能在寄存器之间移动数据,或者把立即数移动到寄存器中,这个和x86这种CISC架构的芯片区别最大的地方。
x86中没有ldr这种指令,因为x86的mov指令可以将数据从内存中移动到寄存器中。
另外还有一个就是ldr伪指令,虽然ldr伪指令和ARM的ldr指令很像,但是作用不太一样。ldr伪指令可以在立即数前加上=,以表示把一个地址写到某寄存器中,比如:
ldr r0, =0x12345678
这样,就把0x12345678这个地址写到r0中了。所以,ldr伪指令和mov是比较相似的。只不过mov指令限制了立即数的长度为8位,也就是不能超过512。而ldr伪指令没有这个限制。如果使用ldr伪指令时,后面跟的立即数没有超过8位,那么在实际汇编的时候该ldr伪指令是被转换为mov指令的。
ldr伪指令和ldr指令不是一个同东西。
ARM汇编特点1:LDR/STR架构
ARM采用RISC架构,CPU本身不能直接读取内存,而需要先将内存中内容加载入CPU中通用寄存器中才能被CPU处理。 RISC (Reduced Instruction Set Computer)
ldr(load register)指令将内存内容加载入通用寄存器。长跳转,加载的是链接地址,在重定位中此命令有大量用到,理解。
str(store register)指令将寄存器内容存入内存空间中。
ldr/str组合用来实现 ARM CPU和内存数据交换
LDR R1,=COUNT //伪指令,把CONUT中的值加载到R1寄存器中
STR R0,[R1] //把R0寄存器中的值,存储到以以R1中的值为地址的储存单元中去。