arm的v8r中汇编指令STRD的详细用法
经过查询相关资料,ARMv8-R 架构中的 STRD 指令用于将两个 32 位字(Word)或一个 64 位双字(Double-Word) 从两个寄存器存储到内存中。这在进行 64 位数据操作或者需要高效地存储一对寄存器时非常有用。由于搜索结果中相关信息较少,我会结合自己的知识为你详细解释。
📝 STRD 指令格式
STRD 指令的基本语法格式如下:
STRD <Rt1>, <Rt2>, [<Rn>, #<offset>]! ; 预先带写回的变址寻址
STRD <Rt1>, <Rt2>, [<Rn>], #<offset> ; 事后带写回的变址寻址
STRD <Rt1>, <Rt2>, [<Rn>, #<offset>] ; 不带写回的变址寻址(偏移模式)
或者更一般的形式:
STRD <Rt1>, <Rt2>, [<Xn|SP>{, #<imm>}]
<Rt1>和<Rt2>:这是你要存储到内存中的两个源寄存器。Rt1中的值会被存储到较低的内存地址,Rt2中的值会被存储到较高的内存地址。[<Rn>]:这是基址寄存器,包含了存储操作的目标内存地址。#<offset>:这是一个可选的立即数偏移量(通常有特定的范围限制,例如在 ARMv8 中常见的是 -1020 到 +1020 且是 4 的倍数)。它会被加到基址寄存器Rn上来形成有效地址。!:如果存在!,表示预先变址寻址(Pre-index) 并且将计算后的新地址写回基址寄存器Rn。
格式:[Rn, #offset]!-> 先计算Rn + offset作为地址,然后将Rn更新为Rn + offset。- 如果没有
!,但有偏移量且写在括号内(如[Rn, #offset]),这通常是偏移寻址(Offset),使用Rn + offset作为地址,但不写回Rn。 - 如果偏移量写在括号外(如
[Rn], #offset),表示事后变址寻址(Post-index):先使用Rn的值作为地址,然后将Rn更新为Rn + offset。
🔍 STRD 指令的操作
STRD 指令的核心操作是将两个寄存器(Rt1 和 Rt2)的值存储到连续的内存单元中:
- 计算地址:根据使用的寻址模式(偏移、预先变址、事后变址)计算有效内存地址。
- 设有效地址为
address。
- 设有效地址为
- 存储数据:
- 将寄存器
Rt1的值存储到计算得到的有效地址address处(一个 word,4 字节)。 - 将寄存器
Rt2的值存储到地址address + 4处(下一个 word,4 字节)。 - 这样就连续存储了 8 个字节(64 位)的数据。
- 将寄存器
⚠️ 重要注意事项
- 地址对齐:STRD 指令要求目标内存地址是 64 位(8 字节)对齐的。这意味着地址值最好是 8 的倍数(即最低 3 位为 0)。如果地址非 8 字节对齐,执行 STRD 指令可能会引发对齐错误异常(Alignment Fault),具体行为取决于系统配置。
- 寄存器对:
Rt1和Rt2通常是两个不同的通用寄存器。虽然有些架构可能允许Rt1和Rt2是同一个寄存器,但这通常没有实际意义,并且可能在某些情况下产生不可预知的行为,应避免这样使用。 - 偏移量范围:立即数偏移量
#<imm>的范围和缩放因子由架构定义。在 ARMv8 中,STRD的偏移量通常是一个有符号的立即数,范围是 -1020 到 +1020,并且必须是 8 的倍数(因为要访问 8 字节)。编码时偏移量会被除以 8。 - 写回(Write-back):使用
!(预先变址)或事后变址格式时,基址寄存器Rn会被更新。确保Rn不是 PC(程序计数器),并且注意不要意外覆盖仍在使用的基址值。 - 访问权限:执行存储操作时,当前执行级别(EL)必须有权限写入目标内存地址,否则会产生权限错误异常。
🖥️ STRD 指令示例
假设寄存器 R1 的当前值是 0x8000,R2 中的值是 0xAABBCCDD,R3 中的值是 0x11223344。
1. 偏移模式(Offset)
STRD R2, R3, [R1, #16] ; 将 R2 的值存储到 [R1+16] = 0x8010
; 将 R3 的值存储到 [R1+16+4] = 0x8014
; R1 的值保持不变 (0x8000)
2. 预先变址模式(Pre-index with write-back)
STRD R2, R3, [R1, #24]! ; 先将 R1 更新为 R1 + 24 = 0x8018
; 然后将 R2 的值存储到 [0x8018]
; 将 R3 的值存储到 [0x8018+4] = 0x801C
; 执行后 R1 = 0x8018
3. 事后变址模式(Post-index with write-back)
STRD R2, R3, [R1], #-8 ; 先将 R2 的值存储到 [R1] = [0x8000]
; 将 R3 的值存储到 [0x8000+4] = 0x8004
; 然后将 R1 更新为 R1 + (-8) = 0x7FF8
为了更直观地理解上述示例中内存和寄存器的变化,可以参考下面的表格:
| 示例模式 | 指令执行前 | 操作说明 | 指令执行后 |
|---|---|---|---|
| 偏移模式 | |||
| R1 = 0x8000 | 基址寄存器 R1 初始值。 | R1 = 0x8000 (不变) | |
STRD R2, R3, [R1, #16] |
R2 = 0xAABBCCDD | 要存储的第一个源寄存器。 | 内存地址 0x8010 = 0xAABBCCDD (R2) |
| R3 = 0x11223344 | 要存储的第二个源寄存器。 | 内存地址 0x8014 = 0x11223344 (R3) | |
| 预先变址模式 | |||
| R1 = 0x8000 | 基址寄存器 R1 初始值。 | R1 = 0x8018 (更新) | |
STRD R2, R3, [R1, #24]! |
R2 = 0xAABBCCDD | 要存储的第一个源寄存器。 | 内存地址 0x8018 = 0xAABBCCDD (R2) |
| R3 = 0x11223344 | 要存储的第二个源寄存器。 | 内存地址 0x801C = 0x11223344 (R3) | |
| 事后变址模式 | |||
| R1 = 0x8000 | 基址寄存器 R1 初始值。 | R1 = 0x7FF8 (更新) | |
STRD R2, R3, [R1], #-8 |
R2 = 0xAABBCCDD | 要存储的第一个源寄存器。 | 内存地址 0x8000 = 0xAABBCCDD (R2) |
| R3 = 0x11223344 | 要存储的第二个源寄存器。 | 内存地址 0x8004 = 0x11223344 (R3) |
🔄 相关指令
- LDRD:与
STRD相对应的加载指令,用于从内存中连续读取 64 位数据到两个寄存器中。 - STR / LDR:用于存储/加载单一寄存器的指令。
- STP / LDP:用于存储/加载一对寄存器的指令,功能与
STRD/LDRD类似,是 ARMv8 中更现代和通用的写法,通常推荐使用。例如STP R2, R3, [R1, #16]。
💎 总结
ARMv8-R 架构中的 STRD 指令主要用于将两个通用寄存器(共 64 位数据)存储到连续的内存地址中。它支持偏移、预先变址(带写回)和事后变址(带写回) 三种寻址模式,提供了灵活的内存访问方式。使用时需特别注意内存地址必须 8 字节对齐,并合理运用偏移量和写回功能来优化代码效率。
希望这些信息能帮助你全面理解 ARMv8-R 中 STRD 指令的用法。
浙公网安备 33010602011771号