访存指令
访存指令用于数据在存储器和处理器寄存器之间传递。有三种访存指令:单寄存器传输指令、多寄存器传输指令,交换指令。
1. 单寄存器传输指令
- 概念:这些指令用于将单数据移入和移出寄存器。数据类型支持有符号和无符号字长(32-bits),半字长(16-bits)和字节。
-
常用的单寄存器传输指令 及其 语法
其中:
addressing1(数据类型是word, unsigned byte)可用桶形移位器-比例因子是2的倍数;
addressing2(数据类型是半字,有符号半字,有符号字节,以及双字)。
- LDR,STR 指令
LDR只在存储器加载32-bit字长地址,4字节的倍数-0,4,8...。
示例1.1
以下示例为从寄存器r1中包含的内存地址进行加载,然后将存储返回到存储器中的相同地址。
寄存器r1:
第一条指令:加载寄存器r1指向的内存地址的内容到寄存器r0。
目标寄存器r0:
第二条指令:存储寄存器r0的内容到寄存器r1指向的内存地址。
r1 的偏移为0,寄存器r1为基地址寄存器。
单寄存器寻址模式
ARM指令集提供了不同的寻址模式。这些模式结合了索引方法:带写回的预索引,预索引和后索引。
索引方法如下表所示:
示例1.2:
-
带写回的预索引
通过基寄存器地址加上地址偏移的计算,然后更新基寄存器地址为新地址。该模式用于遍历数组。
解析:
基寄存器r1:
r1地址和偏移量相加,新地址为:mem32[0x00009000+0x00000004] -- mem32[0x00009004],基寄存器r1地址更新为这个新地址:即 r1 = 0x00009004;
目标寄存器r0:
加载更新后的寄存器r1指向的内存地址的内容到r1。
mem32[0x00009004] = 0x02020202, r1现在地址存储的内容是 0x02020202,即r0 = 0x02020202
-
预索引
与上面相反,索引偏移和带写回的预索引一样但是不更新基寄存器地址。该模式用于在访问数据结构中的元素时间。
解析:
基寄存器r1:
r1地址和偏移量相加,新地址为:mem32[0x00009000+0x00000004] -- mem32[0x00009004],基寄存器r1地址地址不更新,不变,即 r1 = 0x00009000目标寄存器r0:
r1的地址不变,但是由于偏移后的地址为0x00009004,mem32[0x00009004] = 0x02020202, 即r0 = 0x02020202
-
后索引
当地址被用了以后只是更新基寄存器地址。该模式用于遍历数组。
解析:
基寄存器r1:
r1地址和偏移量相加,新地址为:mem32[0x00009000+0x00000004] -- mem32[0x00009004],因为地址被用了,所以只更新基寄存器r1地址更新为这个新地址:即 r1 = 0x00009004;目标寄存器r0:
在这个模式下,目标寄存器加载源地址指向的内容。mem32[0x00009000] = 0x01010101, 即 r0 = 0x01010101。
小结:
示例1.2 使用了preindex方法,展现了每一个索引方法是如何影响 寄存器 r1 地址的,以及加载到 寄存器 r0 中的数据内容。每一个指令展现了在同一个先决条件的索引方法结果。-
总结1:
下表为LDR 指令的不同变化:
总结2:
下表为使用16-bits半字长或有符号字节数据访存时可用的寻址方式:
2. 多寄存器传输指令
- 概念:多寄存器传输指令在一个指令下,可以在内存和寄存器中传递多个寄存器。传输从指向存储器的基地址寄存器Rn开始;多寄存器传输指令可提高效率,例如:数据块的操作,上下文切换,堆栈操作;
多寄存器传输指令会增加中断延时(不会打断正在执行的指令)。ARM不支持经常中断当执行的时候。
编译器比如armcc提供了switch指令可以控制一条load-store指令最大寄存器数目,限制了最大中断延迟。 - 常用的多寄存器传输指令 及其 语法
下表为多寄存器传输指令的不同寻址模式,N 为寄存器个数。
基地址寄存器Rn决定了多寄存器传输指令的源地址和目标地址。通过下面的转换,寄存器可以自动更新。
IA 执行后增加
IB 执行前增加
DA 执行后减少
DB 执行前减少
更新基地址的load-store指令对,用于类似成对的push pop操作,
STMIA - LDMDB
STMIB - LDMDA
STMDA - LDMIB
STMDB - LDMIALDMIA 指令示例:
下例中,寄存器r0是基寄存器Rn阶乘,表示指令执行后更新寄存器。可以注意到,在多寄存器传输指令中寄存器没有单独列出,而是用符号 " - " 代表一段寄存器范围。在这个例子中,寄存器范围是从寄存器r1 到 r3。
每一个寄存器可以列出,用圆括号 { 和 } 来分开每一个寄存器。
在先决条件下,基址寄存器r0指向的内存地址为 0x80010 ,内存地址0x80010,0x80014和0x80018上相应存储的值是0x01,0x02 和 0x03.
下图为图形表示:
多寄存器传输指令LDMIA执行后,寄存器r1, r2 和 r3 包含的值如下图所示:
基寄存器r0 在上个字长加载以后,现在指向内存地址 0x8001c。
- LDMIB指令示例
使用和上面相同的LDMIA 指令 中的Per-condition,寄存器r0指向的第一个字长忽略,寄存器r1从下一个内存地址加载,如下图所示:
多寄存器传输LDMIB指令执行后,寄存器r0 现在指向最后一次被加载后的内存地址 0x8001c,和LDMIA指向下一个内存地址相反
LDMDA和LDMDB 指令从起始地址减少并存储到上升的内存地址。
使用增加和减少的多寄存器传输指令,可以前向和后向访问数组,允许堆栈的push和pull操作。
堆栈操作
ARM结构用多存储器传输指令执行堆栈操作。
pop : 从堆栈中移除数据
push: 数据放入堆栈
在使用堆栈时要决定是 ascending(A)还是 descending(D): 升序是堆栈朝着高地址增长,降序是朝着低地址增长。
堆栈3个属性,堆栈基址、堆栈指针(sp)、堆栈限制;
当使用full stack(F),sp指向最后一个地址或满地址;
当使用empty stack(E),sp指向第一个或空的地址。
ARM-Thumb过程调用标准(ATPCS)定义了例程如何被调用和寄存器如何分配;
ATPCS定义堆栈为递减式满堆栈(Descending-Full stack);
堆栈检查enable时,ATPCS定义r10为堆栈限制或sl(stack limit); 备注:push堆栈溢出错误,pop堆栈下溢(stack underflow)错误。
下表为支持堆栈操作的可用寻址方式:
3. 交换指令
- 概念:交换指令是一个特殊的访存指令。它交换内存个寄存器的内容。是原子操作,会占据总线,直至交换完成。
-
常用的交换指令 及其 语法:
交换不能被其他指令或总线访问中断,这是系统占用总线直到完成。
-
示例:Swap指令从内存加载一个字长到寄存器r0,然后用寄存器r1覆写内存。
这个指令在一个操作系统中当生成信号量semaphore和互斥时特别有用。