arm寻址方式

ARM属于RISC体系结构,一个AMR汇编程序中的大部分汇编指令,基本上都和数据传输有关,在内存-寄存器,内存-内存,寄存器-寄存器之间来回传输数据。不同的ARM指令又有不同的寻址方式,比较常见的寻址方式有寄存器殉职,立即寻址,寄存器偏移寻址,寄存器间接寻址,多寄存器寻址,相对寻址。

3.3.1 寄存器寻址

寄存器寻址比较简单,操作数保存在寄存器中,通过寄存器名就可以直接对寄存器中的数据进行读写。

mov r1,r2

add r1,r2,r3

3.3.2 立即数寻址

在立即数寻址中,ARM指令中的操作数为一个常数,立即数以#为前缀,0x前缀表示该立即数为十六进制,不加前缀默认就是十进制。

add r1,r1, #1 //r1 = r1 + 1

mov r1, #0xff 将十六进制0xff, 写入寄存器r1

mov r1, #12 将十进制12u存入寄存器r1

add r1, r1, #16, 20 r1 = r1 + 16 立即数16右移20位

3.3.3 寄存器偏移寻址

寄存器偏移寻址可以看作寄存器寻址的一种特例,通过第二个操作数operand2的灵活配置,我们可以将第二个操作数各种左移和右移操作,作为新的操作数使用。

mov r2, r1, LSL, #3 r2 = r1 << 3

add r3, r2, r1, LSL #3 r3 = r2 + r1 << 3

add r3, r2, r1, LSL r0 r3 = r2 + r1<<r0

常见的移位操作有逻辑移位和算数移位,两者的区别是,逻辑移位无论是左移还是右移,空缺位一律补0,而算术移位则不同,左移时空缺位补0, 右移时空缺位使用符号位填充。

3.3.4 寄存器间接寻址

寄存器间接寻址主要用来在内存和寄存器之间传输数据。寄存器中保存的数据在内存中的存储地址,我们通过这个地址就可以在寄存器和内存之间传输数据。C语言中的指针操作,在汇编层次其实就是使用寄存器间接寻址实现的,寄存器间接寻址的使用示例以及使用说明如下所示。

LDR r1, [r2]; 将r2中的值作为地址,取该内存地址上的数据,保存到r1

str r1, [r2] 将r2中的值作为地址,将r1寄存器的值写入该内存地址。

3.3.5 基址寻址

基址寻址其实也属于寄存器间接寻址,两者的不同之处在于,基址寻址将寄存器中的地址与一个偏移量相加,生成一个新的地址,然后基于这个新地址去访问内存。

ldr r1, [fp, #2] 将fp中的值加2作为新地址,取该地址上的值保存到r1

ldr r1, [fp, #2] fp = fp + 2 将fp + r0 作为新地址,取该地址上的值保存到r1。

ldr r1, [fp, r0] 将fp + r0 作为新地址,取该地址上的值保存到r1

ldr r1, [fp, r0, LSL #2] 将fp + r0 << 2作为新地址,读取该内存地址上的值保存到r1

ldr r1, [fp], #2 将fp中国呢的值作为地址,读取该地址上的值保存到r1, 然后fp中的值加2,

str r1, [fp, #-2] 将fp中的值减2,作为新地址,将r1中的值写入该地址

str r1, [fp], #-2 将fp中的值作为地址,将r1中的值写入此地址,然后fp中的值减2.

基址寻址一般用在查表,数组访问,函数的栈帧管理等场合。根据偏移量的正负,基址寻址又可以分为向前索引寻址和向后索引寻址,如上面的第1条和第3条指令,就是向后索引寻址,而第6条指令则为向前索引寻址。

3.3.6 多寄存器寻址

STM/LDM 指令就属于多寄存器寻址,一次可以传输多个寄存器的值。

LDMIA sp!, {r0-R2, r14} 将内存栈中的数据依次弹出到r14, r2,r1, r0

STMDB sp!, {r0-r2, r14} 将r0,r1,r2,r14依次压入栈

LDMFD, sp!, {r0-r2, r14} 将内存栈中的数据依次弹出到r14,r2,r1,r0

在多寄存器寻址中,用大括号{}括起来的是寄存器列表,寄存器之间用逗号隔开,如果是连续的寄存器,还可以使用连接符-连接,如r0-r3,就表示r0,r1,r2,r3这4个寄存器。LDM/STM指令一般和IA IB,DA, DB组合使用,分别表示Increase After Increase before

decrease after decrease before

LDM /STM指令也可以和FD,ED,FA,EA组合使用,用于堆栈操作。

栈是程序运行过程中非常重要的一段内存空间,栈是C语言运行的基础,函数内的局部变量,函数调用过程中要传递的参数,函数的返回值一般是保存在栈中的。可以这么说,没有栈,C语言就无法运行。在嵌入式运行的一些启动代码中,你会看到,在运行C语言程序之前,必须要先运行一段汇编代码初始化内存和栈指针SP,然后才能跳到C语言程序中运行。

ARM没有专门的入栈和出栈指令,ARM中的栈操作其实就是通过上面所讲的STM/LDM指令和栈指针SP配合操作完成的,栈一般可以分为以下四类。

递增栈A:入栈时,SP栈指针从低地址往高地址方向增长。

递减栈D,入栈时,SP栈指针从高地址往低地址方向增长。

满栈F,SP栈指针总是指向栈顶元素

空栈E,SP栈指针总是指向栈顶

ARM默认使用满递减堆栈,通过STMFD/LDMFD指令配对使用,完成堆栈的入栈和出栈操作。ARM中的PUSH和POP指令其实就是LDM/STM的同义词,是LDMFD和STMFD组合指令的助记符。PUSH指令和POP指令的使用示例如下。

STMFD sp!, {r0-r2, r14} 将r0,r1,r2,r14入栈

LDMFD sp!, {r0-r2,r14} 将栈中的数据依次弹出到r14, r2,r1,r0}

PUSH {r0-r2,r14}

POP {r0-r2,r14}

3.3.7 相对寻址

相对寻址其实也属于基址寻址,只不过它是基址殉职的一种特殊情况,特殊在什么地方呢?是以PC指针作为基址寻址的,以指令中的地址差作为偏移。两者相加后得到的就是一个新地址,然后可以对这个地址进行读写操作。ARM中的B,BL,ADR指令其实都是采用相对寻址的,

B LOOP

LOOP MOV r0, #1

MOV r1,r0

在上面的示例代码中,B LOOP 指令其实就等价于

ADD PC,PC #OFFSET

其中OFFSET未B LOOP 这条当前正在执行的指令地址与地址符号LOOP之间的地址偏移,B指令的前后跳转范围[0, 32MB],如果你编写的程序生成的二进制文件小雨32MB。基本上就可以随意的使用B指令跳转了。

除此之外,很多位置无关的代码,如动态链接库,在汇编代码层次实现其实也是采用相对寻址的。程序中的使用相对寻址访问的好处是不需要重定位,将代码加载到内存中的任何地址都可以直接运行。

相关推荐
烬奇小云1 天前
使用 unicorn 和 capstone 库来模拟 ARM Thumb 指令的执行,并实现多个钩子(hook)来监控代码执行、系统调用和内存读写操作(二)
java·arm开发·python
CYRUS STUDIO1 天前
Android 下内联汇编,Android Studio 汇编开发
android·汇编·arm开发·android studio·arm
艾格北峰2 天前
STM32 BootLoader 刷新项目 (九) 跳转指定地址-命令0x55
arm开发·stm32·单片机·嵌入式硬件
橘色的喵3 天前
嵌入式ARM平台Linux网络实时性能优化
linux·网络·arm开发·性能优化·实时·内核优化
荣世蓥4 天前
2.ARM_ARM是什么
arm开发
憧憬一下4 天前
Linux 内核中断描述符 (irq_desc) 的初始化与动态分配机制详解
arm开发·嵌入式硬件·嵌入式·c/c++·linux驱动开发
亿道电子4 天前
【ARM】MDK-E203 Undefined identifier
arm开发·stm32·单片机·arm
ShuQiHere4 天前
【ShuQiHere】️计算机架构:x86 与 ARM 指令集架构的对比与发展
arm开发·架构
CYRUS STUDIO4 天前
Android下的系统调用 (syscall),内联汇编syscall
android·linux·汇编·arm开发·arm·内联汇编
橘色的喵5 天前
C++编程:嵌入式Linux-ARM与外设中断交互的程序设计
linux·arm开发·select·interrupt·中断·低延迟·设备交互