ARM寻址方式

寻址方式指的是确定操作数位置的方式。

寻址方式:

立即数寻址

直接寻址(绝对寻址),ARM不支持这种寻址方式,但所有CISC处理器都支持

寄存器间接寻址

3种寻址方式总结如下:

助记符 RTL格式 描述

复制代码
ADD	r0,r1,#Q	[r0]<-[r1] + Q		立即数寻址:把整数Q与寄存器r1的内容相加
LDR	r0,Mem	[r0]<-[Mem]  		绝对寻址:存储单元内容加到寄存器r0中
LDR	r0,[r1]     	[r0]<-[[r1]]		间接寻址:把r1所指存储单元的值加载到r0中

立即数寻址

在介绍ARM如何实现立即数寻址之前,先来看一个使用立即数寻址的实例。高级语言

常用立即数寻址来指定一个常量而不是变量,如:

IF I > 25

THEN J = K + 12

常量12和25由立即数寻址指定。

复制代码
假设I在r0中,J在r1中,K在r2中,可表示为:
		CMP	r0,#25		; 把I与25比较
		BLE	Exit		; 如果 I <= 25,则退出
		ADD	r1,r2,#12	; 否则K+12
	Exit							;

使用条件执行表示:
		CMP	r0,#25		; I与25比较
		ADDGT	r1,r2,#12	; 如果I > 25,则J = K + 12

ARM的立即数实现方法

ARM在指令中提供了12位的立即数字段,但不支持值在0~4095之间的12位无符号立即数或

值在-2048~2047之间的12位有符号立即数。实际上ARM提供的是可按2的幂缩放的8位立即

数。

下图,给出了ARM立即数的编码结构,当操作码的第25位为0时,ARM将进行一次移位操

作,当第25位为1时,操作数2字段将编码12位立即数,它被分成两部分:8位立即数和4位

对齐码。

立即数字段中最高4位知道了立即数在32位字中的对齐方式。

如果8位立即数为N,4位对齐码为 n(范围在0~15之间),则立即数的值为N x 22n,因

此ARM提供了一个可按2的幂缩放的8位立即数,这与浮点数的表示和存储方式相似。

下图描述了一个ARM立即数缩放,该图说明了对齐码是如何在32位框架移动立即数的。为

了得到立即数循环右移的位数,必须将对齐码的数字翻倍。

尽管不能直接指定32位立即数,但ARM缩放结构可以表示常量FF00000016,即用8位FF16

左移24位(即右移8位)来表示。

取反传送指令MVN r0,r1,#literal,能够指定一个不必移位且范围在0xFFFFFF00到

0xFFFFFFFF的常量。程序员不用担心如何产生移位常量,这是汇编器的工作。立即数的缩

放不是伪操作。

如,指令MOV r1,#0x0000FF00的二进制代码为E3A01CFF16:

MOV r0,#0xFF

MOV r1,#0x0000FF00

MOV r2,#0xFF000000

以指令MOV r1,#0xFF00为例,立即数编码为CFF16(1100111111112),对齐码为C16或1210。

实际移位位数是这个数的2倍,即2410,通过循环右移来实现。24次循环右移操作等于8次

循环左移操作,即将FF16移位为FF0016,最后来看0xFF000000,将十六进制数左移6次,

相当于将二进制左移24次,不过这里使用循环右移,等价于循环右移8次。要保存的缩放

常数(即对齐码)是移位次数的一半,即4。上图中第6行指令的常量编码为4FF。

寄存器的间接寻址

操作数的地址保存在寄存器中,这种寻址方式叫作寄存器间接寻址,也叫索引寻址或基址

寻址。ARM的立即数偏移量为12位。它确实是12位立即数,不是8位可缩放的值。

什么时候是真正的12位

ARM处理器指定12位常量作为立即操作数(如ADD r0,r1,#123)。常量为8位数并通过4位

对齐码进行放大。

然而,当ARM处理器指定某个立即数偏移量作为索引地址的一部分时,它就是真正的12位

数,如LDR r0,[r1,#123]。

寄存器间接寻址

寄存器间接寻址通过3个读操作来访问一个操作数:

(1)读指令得到指针寄存器

(2)读指针寄存器得到操作数地址

(3)读操作数地址所指的存储器单元得到操作数

寄存器间接寻址可以在运行时修改寄存器的内容,而寄存器中含有指向实际操作数的指针

(地址),因此地址是变量,允许访问如数组、列表、矩阵、向量、表格等数据结构。

下图,通过ARM的加载指令LDR r1,[r0]说明了寄存器间接寻址,指针寄存器r0的值为n,则

指向或引用存储单元n:

LDR r1,[r0]和ADD r0,r0,#4这两个操作的RTL定义:

r1\] \<- \[\[r0\]\] ; 读取r0所指存储单元的值,\[\[r0\]\]就是r0所指存储单元的值 \[r0\] \<- \[r0\] + 4 ; 指针递增指向下一个存储单元 考虑下面的例子,表格中的7个项分别代表一个星期的每一天。D1代表星期一,D2代表星 期二,等等。如果Di是第i天,则Di+1就表示下一天,从一天移到下一天,只需将索引i加1, 这就是需要变址的原因: ADR r0,Week ; r0指向数组Week ADD r0,r0,r1, LSL #2 ; r0现在指向r1所含那一天 LDR r2,[r0] ; 读取这一天的数据到r2 Week DCD ; 第1天的数据 DCD ; 第2天的数据 . DCD ; 第7天的数据 假定天数的索引为0\~6,天数的索引必须乘以4,因为这里的数组是字的数组(每个字4个 字节),连续两个元素的地址之差为4。 字符串是一个很好的使用基于指针寻址的例子。假设要找到某个特定字符在字符串中的位 置,可以写出下面代码。这不是ARM代码,因为还没有介绍字节操作。使用下标表明操作 数的大小: LDR32 r0,#String ; r0指向String Loop LDR8 r1,[r0] ; REPEAT 读取字符 ADD32 r0,r0,#1 ; 更新字符指针 CMP8 r1,#Terminator ; UNTIL 发现终止符(终止符为行结束字符) BNE Loop ; 有些计算机将寄存器间接寻址与指针更新结合在一起,这样指针在被使用之后就可以自动 地指向下一个存储单元。 考虑下面的C代码段: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/5940bbabcecc449eaffa6de7452d6373.png) 程序中数0、21、10是汇编期间有立即数寻址方式指定的常量。转为下面的ARM汇编语言 #### 带偏移量的寄存器间接寻址 操作数有效地址是寄存器的内容加上编码在load/store指令的立即数偏移量,这种寻址方式 也叫基址加位移寻址。 下图,用指令LDR r0,\[r1,#4\]说明,图中有效地址是指针寄存器r1的内容加上偏移量4的和, 即操作数距离指针所指的地址4个字节: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/27b9130e8a2b428b86654bc7eadb5d24.png) 下述代码段说明了如何使用偏移量实现数组访问,偏移量是常量,在运行时不能改变: Sun EQU 0 ; 一星期中每一天的偏移量 Mon EQU 4 Tue EQU 8 . Sat EQU 24 ADR r0,Week ; r0指向数组Week LDR r2,[r0,#Tue] ; 读取星期二的数据到r2 LDR r3,[r0,#Wed] ; 读取星期三的数据到r3 ADD r4,r2,r3 ; 星期二的数据与星期三的数据相加 STR r4,[r0,#Mon] ; 把结果存放到星期一 Week DCD ; 第1天的数据(星期天) DCD ; 第2天的数据(星期一) DCD ; 第3天的数据(星期二) DCD ; 第4天的数据(星期三) DCD ; 第5天的数据(星期四) DCD ; 第6天的数据(星期五) DCD ; 第7天的数据(星期六) ARM允许指定第二个寄存器作为偏移量,这样就可以使用运行时可以修改的动态偏移量, 如下图: LDR r2,\[r0,r1\] ; \[r2\]\<-\[\[r0\] + \[r1\]\]把r0加r1所指存储单元的值加载到r2中 LDR r2,\[r0,r1,LSL #2\] ; \[r2\]\<-\[\[r0\] + 4 x \[r1\]\]将r1乘4 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/93d2fd0586f74dc89552766809112fac.png) 寄存器r1扩大了4倍。当处理数组时,允许使用一个被缩放的偏移量。如果r0指向数组X且 r1包括索引i,则元素i的地址为X+4i。这样就可以使用指令LDR r2,\[r0,r1,LSL #2\]访问该元素。 可以通过向基址寄存器增加立即数偏移量或寄存器偏移量来扩展寄存器间接寻址方式。在 ARM术语中,基址寄存器加偏移量的寻址方式叫作前索引,因为是在访问操作数之前把偏 移量加到指针上。指令LDR r0,\[r1,#8\]指定了前索引寻址方式,操作数的有效地址由\[r1\]+8给 出。这里,前索引表示偏移量#8在load操作的读阶段访问寄存器之前就被加到基址寄存器 r1上。 前索引寻址方式可以来访问数组X的元素i,如: ADR r0,X ; 寄存器r0指向数组X LDR r1,\[r0,i\] ; 读出元素i #### ARM的自动前索引寻址方式 通过将偏移量加到基址寄存器(指针寄存器)上ARM实现了两种自动索引方式。这两种方 式的差别在于基址寄存器递增的时机------要么在访问寄存器之前,要么在之后。 ARM的自动前索引寻址方式是在有效地址后面添加后缀!来表示。如: LDR r0,\[r1,#8\]! ; 将寄存器r1+8所指存储单元中的字加载到r0中 ; 然后将r1加8以更新指针 RTL定义如下: \[r0\] \<- \[\[r1\] + 8\] 访问地址为基址寄存器r1+8的存储单元 \[r1\] \<- \[r1\] + 8 加上偏移量更新指针(基址寄存器) 考虑下面两个数组相加的例子: Len BQU 8 ; 数组长度为8个字 ADR r0,A-4 ; 寄存器r0指向数组A ADR r1,B-4 ; 寄存器r1指向数组B ADR r2,C-4 ; 寄存器r2指向数组C MOV r5,#Len ; 寄存器r5用作循环计数器 Loop LDR r3,[r0,#4]! ; 取出A的元素 LDR r4,[r1,#4]! ; 取出B的元素 ADD r3,r3,r4 ; 两元素相加 STR r3,[r2,#4]! ; 和保存到C中 SUBS r5,r5,#1 ; 测试循环是否结束 BNE Loop ; 重复直到全部完成 #### ARM自动后索引寻址方式 自动后索引寻址方式首先访问基址寄存器所指的存储单元中的操作数,然后将基址寄存器 递增。如: LDR r0,\[r1\],#8 ; 将r1所指的字加载到r0中,然后完成后索引,即r1加8 后索引把偏移量放在方括号的外面(如\[r1\],#8),RTL定义为: \[r0\] \<- \[\[r1\]\] 访问基址寄存器r1所指存储单元 \[r1\] \<- \[r1\] + 8 加上偏移量更新指针(基址寄存器) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3c1ff8a8710d4c049a3e3cf446eda530.png) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/17acd8a174904b019187982875c8780b.png) #### 程序计数器相对寻址 寄存器r15是一个程序计数器,把r15用作访问操作数的指针寄存器,这种寻址方式叫作程 序计数器(PC)相对寻址。操作数地址由其与当前代码的相对位置确定。这意味着可以将 代码及与之相关的数据移动到存储器中的不同地方,而无需重新计算操作数地址。 假设执行指令LDR r0,\[r15,#100\],操作数地址距离寄存器r15的内容的相对偏移为100字 节(25个字),因此,操作数位于当前位置偏移100字节处。 #### ARM的load与store指令编码 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/da4a32ff22054883bc315a707894d966.png) 访存操作有一条条件执行字段,即操作码的第28\~31位,它们可以像其他ARM指令一样条 件执行: ; if(a==b) then x = p else x = q CMP r1,r2 ; if(a==b) LDREQ r3,[r4] ;then x = p LDRNE r3,[r5] ;else x = q 操作码第20位选择数据传送方向,即指令是load还是store 第25位(#位)决定了偏移量是带可选移位的寄存器内容还是12位常量 第22位选择操作数大小,并确定ARM是传送32位字还是8位字节,当字节被加载到32位寄存器中时, 寄存器的第8\~31位被置0。 基址寄存器r基址是存储器指针 U位定义了有效地址的计算机是加上还是减去偏移量 W位决定了当前指令结束时基址寄存器是否会被更新,W=1,则会更新基址寄存器 P位控制偏移量是计算机有效地址之前还是在之后被加到基址寄存器上 因为U位决定了对偏移量进行加法还是减法,ARM能使用以下寻址方式: LDR r0,\[r1,+r2\] ; 有效地址是\[r1\] + \[r2

LDR r0,[r1,-r2] ; 有效地址是[r1] -- [r2]

下表总结了ARM基于寄存器的寻址方式:

考虑二进制字符串01010111001000100100000100000110表示一条ARM指令,把这条指令分

解,得到下表中的编码,得到的指令就是STRPL r4,[r2,-r6,LSL #2]!

相关推荐
suyong_yq8 小时前
调试Cortex-M85 MCU启动汇编和链接命令文件 - 解题一则
汇编·arm开发·嵌入式系统
流水灯LCG10 小时前
windows 创建arm虚拟机
arm开发·windows
无敌的神龙战士1 天前
ARM 算数指令
arm开发
IT阳晨。2 天前
【嵌入式Linux】基于ARM-Linux的zero2平台的智慧楼宇管理系统项目
linux·arm开发
_清风来叙2 天前
【Linux】Linux内核模块开发
linux·arm开发
没有余地 EliasJie2 天前
Ubuntu平台使用aarch64-Linux交叉编译opencv库并移植RK3588S边缘端
linux·arm开发·opencv·ubuntu
楚灵魈2 天前
[Linux]从零开始的STM32MP157 Buildroot根文件系统构建
linux·arm开发·stm32
云中飞鸿3 天前
加载ko驱动模块:显示Arm版本问题解决!
linux·arm开发
无敌的神龙战士3 天前
ARM ASM
arm开发