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指令跳转了。

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

相关推荐
松涛和鸣18 小时前
60、嵌入式定时器深度解析:EPIT与GPT
c语言·arm开发·单片机·嵌入式硬件·gpt·fpga开发
CQ_YM20 小时前
ARM中断
arm开发·嵌入式硬件·arm
猫猫的小茶馆20 小时前
【Linux 驱动开发】一. 搭建开发环境
linux·汇编·arm开发·驱动开发·stm32·嵌入式硬件·mcu
猫猫的小茶馆20 小时前
【Linux 驱动开发】嵌入式 Linux 开发概念
linux·服务器·arm开发·stm32·单片机·嵌入式硬件·mcu
猫猫的小茶馆21 小时前
【Linux 驱动开发】二. linux内核模块
linux·汇编·arm开发·驱动开发·stm32·嵌入式硬件·架构
算力魔方AIPC21 小时前
DeepX OCR:以 DeepX NPU 加速 PaddleOCR 推理,在 ARM 与 x86 平台交付可规模化的高性能 OCR 能力
arm开发·人工智能·ocr
小鹿软件办公1 天前
首批搭载 Windows 系统的 ARM PC 及英伟达处理器今年面世
arm开发·armpc
切糕师学AI2 天前
ARM 中的 SVC 监管调用(Supervisor Call)
linux·c语言·汇编·arm开发
陌上花开缓缓归以2 天前
linux jiffies 初始化不为0问题分析
linux·arm开发
天骄t2 天前
裸机开发:ARMv7-A中断驱动LED/蜂鸣器实战
arm开发