ARM的九种寻址方式

寻址方式就是CPU根据指令中的地址信息,找出物理地址也就是内存地址的方式,通俗理解就是ARM指出内存地址的方式。

寻址的目的就是找出操作数,比如ARM要做一个除法运算,就需要除数和被除数,除数和被除数都是除法指令的操作数,要找到这些操作数,可以有多种方法,寻找操作数的过程就叫做寻址。(我个人理解)

ARM支持九种寻址方式:

  • 立即数寻址
  • 寄存器寻址
  • 寄存器偏移寻址
  • 寄存器间接寻址
  • 寄存器基址变址寻址
  • 多寄存器寻址
  • 相对寻址
  • 堆栈寻址
  • 块拷贝寻址

1.立即数寻址

立即数寻址就是直接将内存中的数据发给CPU作为操作数。注意,由于ARM是32位指令集,所以立即数的范围不可以超出0255,也就是说立即数的范围只能是0255。

格式:就是在立即数前面加上 # 来作为操作数

典型的例子就是直接对寄存器进行写值:

ldr r0, #254   ;将254写入r0寄存器
add r1, r2, #3 ;将r2寄存器中的值与3相加后,在写入r1寄存器

2.寄存器寻址

寄存器寻址就是直接将寄存器中的数值作为操作数:

ldr r1, r0      ;将r0寄存器中的值写到r0
add r3, r2, r1  ;将r1、r2寄存器的值相加,结果写入r3寄存器

3.寄存器间接寻址

还是利用了寄存器,只不过操作数不是寄存器中的值了,操作数在内存中,那怎么办?没事,操作数的地址就在寄存器中。所以寄存器间接寻址相当于以寄存器中的值作为内存地址,去内存中寻找操作数。

格式:在提供操作数地址的寄存器上加上[],比如[r0]

mov r0, #0X54000032
ldr r1, [r0]           ;将地址为0X54000032的数据写入r1寄存器中

4.寄存器偏移寻址

以寄存器寻址为本,将寄存器中的数移位后作为操作数。

一共有6中移位操作:

LSL:逻辑左移(Logical Shift Left),寄存器中字的低端空出的位补0。

LSR:逻辑右移(Logical Shift Right),寄存器中字的高端空出的位补0。

ASL:算术左移(Arithmetic Shift Left),和逻辑左移LSL相同。

ASR:算术右移(Arithmetic Shift Right),移位过程中符号位不变,即如果源操作数是正数,则字的高端空出的位补0,否则补1。

ROR:循环右移(Rotate Right),由字的低端移出的位填入字的高端空出的位。

RRX:带扩展的循环右移(Rotate Right eXtended),操作数右移一位,高端空出的位用进位标志C的值来填充,低端移出的位填入进位标志位。

格式:rx, 移位命令 移位操作数

ldr r0, r1, lsl #3   ;将r1的值逻辑左移3位后写入r0
ldr r0, r1, ror r2   ;将r1的值循环右移r2中的值对应位后,写入r0

5.寄存器基址变址寻址

基址变址寻址是基于寄存器间接寻址的,只不过地址不再是寄存器中的值了,而是偏移后的值,这里的偏移值可以理解为地址相加值。

加上感叹号应该有优先执行的意思吧(个人理解)

格式:[rx, n],表示在rx寄存器所指向的地址上,再偏移(相加)n字节

ldr r0, [r1, #3]     ;地址为:r1值+3字节,指令执行完r1不变
ldr r0, [r1, #3]!    ;地址为:r1值+3字节,指令执行完r1+3
ldr r0, [r1, #-1]    ;地址为:r1值-1字节,指令执行完r1不变
ldr r0, [r1, r2]     ;地址为:r1值+r2值
ldr r0, [r1], #4     ;地址为:r1值,但指令执行完后,r1值+4字节

6.批量寄存器寻址

批量寄存器寻址就是使用一个大括号{}包含多个寄存器

ldmia r0, {r1, r2, r3, r4}     ;将r1,r2,r3,r4中的数据依次放入R0指向的内存地址,r0+4指向的内存地址...
ldmia r0, {r1-r4}             ;同上。

7.相对寻址

通过标号进行寻址,经常与跳转指令相配合使用

	bl heihei 

heihei:        ;跳转到heihei执行

8.堆栈寻址

堆栈即Stack,因为CPU的寄存器总是及其有限的,很多时候我们不得不使用内存来存储数据,比如进行多级跳转的时候,这时候堆栈就是一个很好的工具,每次跳转就将当前函数的返回地址存储到内存,最底层被调用的子函数会最先返回,就先将压入栈的现场返回,以此类推…,ARM使用SP(R13)作为栈指针,ARM设计的内存栈模型有2×2=4种

按照栈在内存增长的方向分为递增栈递减栈

递增(Increase) 堆栈:向堆栈写入数据时,堆栈由低地址向高地址生长。

递减(Descend) 堆栈:向堆栈写入数据时,堆栈由高地址向低地址生长。

根据堆栈指针SP指向的位置,又可以把堆栈分为满堆栈空堆栈两种。

满堆栈(Full Stack):SP始终指向栈顶元素,压栈的时候先移动SP,再将数据放入SP指向的地址。

空堆栈(Empty Stack):SP始终指向下一个将要放入元素的位置,压栈时先将数据放入SP指向的地址,再移动SP

最后,可以得到4种基本的堆栈类型:

满增栈(FA):堆栈指针指向最后压入的数据,且由低地址向高地址生长。

满减栈(FD):堆栈指针指向最后压入的数据,且由高地址向低地址生长。常用这种

空增栈(EA):堆栈指针指向下一个将要压入数据的地址,且由低地址向高地址生长。

空减栈(ED):堆栈指针指向下一个将要压入数据的地址,且由高地址向低地址生长。

stmfd sp!, {r1-r7, lr}  ;将r1到r7和lr的数据压入fd栈

9.块拷贝寻址

块拷贝寻址提供了一块内存和一组寄存器之间的拷贝,按照内存使用方式的不同,可以分为2×2=4种。地址增方向/地址减方向×先偏移/后偏移。堆栈寻址就可以看作是块拷贝寻址的的一个实例。

即:

IB:Increment Before Operating

IA:Increment After Operating

DB:Decrement Before Operating

DA:Decrement After Operating

STMIA  R0!,{R1—R7}  ;将R1-R7的寄存器中的值放入R0指向的地址,R0自动更新,指向操作后的地址

参考

posted @ 2020-03-23 20:40  Aspirant-GQ  阅读(636)  评论(0编辑  收藏  举报