ARM64 汇编小记
carbofish

记录一下几个指令用法

LDRB 指令

在ARM汇编指令中,LDRB(Load Register Byte)是一种用于加载单个字节数据到寄存器的指令。与LDR指令相比,LDRB会从内存中加载一个字节(8位)而不是一个字(32位)数据,同时支持零扩展(zero-extension)到32位寄存器中。

指令格式

1
LDRB <Rd>, [<Rn>, #<offset>]

参数说明

  • Rd 目标寄存器,用于存储加载的字节值。
  • Rn 基址寄存器,提供内存地址。
  • offset 可选的偏移量(立即数或寄存器),用于计算内存地址。

工作原理

计算地址:根据基址寄存器 Rn 和偏移量 offset 计算出目标内存地址。
读取内存:从该地址读取一个字节数据。
零扩展:将读取到的8位数据扩展为32位(高位填充为0),并存入目标寄存器 Rd

示例

读取一个字节

1
LDRB R0, [R1]
  • R1 寄存器指向的内存地址读取一个字节,存入 R0 寄存器。
  • 加载的字节内容会被零扩展到32位。

带偏移的加载

1
LDRB R2, [R3, #4]
  • R3寄存器的地址加上偏移量 4 的内存地址读取一个字节,存入 R2

使用寄存器偏移

1
LDRB R4, [R5, R6]
  • 使用 R5 作为基址,R6的值作为偏移量,读取目标地址的一个字节数据到 R4

使用场景

  • 读取存储在内存中的单字节数据,例如:
    • 处理字符串(ASCII字符)
    • 操作8位寄存器或标志位数据
    • 处理设备寄存器中的单字节数据

注意事项

零扩展:LDRB 会将读取的8位数据零扩展为32位。如果需要符号扩展,可以使用 LDRSB 指令。
对齐要求:尽管加载一个字节不需要严格的对齐,但应确保基址和偏移的计算结果是有效的内存地址。
内存访问异常:如果目标地址无效或不可访问,会引发数据访问异常。

SXTB 指令

在 ARM 汇编指令集中,SXTB(Sign Extend Byte) 是一种用于符号扩展的指令,它将一个 8 位的有符号值扩展为 32 位的有符号值。

指令功能

SXTB 会从指定寄存器的低 8 位中提取数据,并根据最高有效位(符号位)进行符号扩展:

  • 如果最高有效位(第 7 位)是 0,扩展为正数。
  • 如果最高有效位是 1,扩展为负数。

结果会写入目标寄存器。

指令格式

1
SXTB <Rd>, <Rm> {, <rotation>}

参数说明

  • Rd 目标寄存器,用于存储符号扩展后的 32 位值。
  • Rm 源寄存器,包含要扩展的 8 位数据。
  • rotation(可选):旋转操作,可以是 ROR #8, ROR #16ROR #24,将源数据按指定位数旋转后再取低 8 位。

工作原理

提取源寄存器 Rm 的低 8 位数据。
根据第 7 位(符号位)进行符号扩展:

  • 若符号位为 1,高位用 1 填充。
  • 若符号位为 0,高位用 0 填充。
    将扩展后的 32 位值存入目标寄存器 Rd

示例

基本符号扩展

1
SXTB R1, R0
  • R0 的低 8 位提取值。
  • 根据符号位扩展为 32 位,并存入 R1

如果 R0 = 0xFFFFFF85(低 8 位是 0x85,符号位为 1),则:

  • 符号扩展后:R1 = 0xFFFFFF85(扩展为负数)。

如果 R0 = 0x0000007F(低 8 位是 0x7F,符号位为 0),则:

  • 符号扩展后:R1 = 0x0000007F(扩展为正数)。

带旋转的符号扩展

1
SXTB R2, R3, ROR #8
  • 先将 R3 的值按右旋 8 位。
  • 然后从结果的低 8 位提取值并进行符号扩展,结果存入 R2

使用场景

  • 处理有符号的 8 位数据:如读取字符(char 类型)或 8 位寄存器数据。
  • 数据对齐和扩展:将单字节数据扩展为 32 位,方便与其他数据操作。
  • 符号正确性:确保符号位在扩展后保持一致,用于数学运算或逻辑操作。

与其他指令的比较

  • SXTB 符号扩展 8 位。
  • UXTB 无符号扩展 8 位(高位填充 0)。
  • LDRSB 从内存加载一个字节并符号扩展。
  • MOV 不进行扩展,直接移动数据。

SXTB 是 ARM 汇编中操作符号位的重要工具,适合处理 8 位有符号数据并将其扩展为 32 位值进行进一步的运算。

在 ARM 汇编语言中,LDP(Load Pair of Registers) 是一种用于同时加载两个寄存器的指令。它是 ARM 架构(尤其是 ARMv8-A)中为优化性能而引入的一种指令,可以一次性从内存中加载两个连续存储的数据到寄存器对。

LDP 指令

指令功能

  • LDP 指令从内存地址中加载两个连续的数据,分别存入两个目标寄存器。
  • 与单个加载指令(如 LDR)相比,LDP 提供了更高的内存访问效率,因为它在一次指令中完成了两次加载操作。

指令格式

1
LDP <Rt>, <Rt2>, [<Rn>{, #<imm>}]

参数说明

  • Rt 第一个目标寄存器,用于存储第一个加载的数据。
  • Rt2 第二个目标寄存器,用于存储第二个加载的数据。
  • Rn 基址寄存器,提供内存的起始地址。
  • imm(可选):偏移量(字节数),用于从基址 Rn 计算内存地址,默认为 0。

工作原理

计算地址:从基址寄存器 Rn 计算出目标内存地址。如果指定了偏移量 imm,则将其加到 Rn 的值上。
加载数据:

  • 从计算地址加载第一个值存入寄存器 Rt
  • 从计算地址加上寄存器宽度(通常为 8 字节)的位置加载第二个值存入寄存器 Rt2
    更新基址(可选):在某些模式下(如 LDP 加更新地址),基址 Rn 的值会被更新。

示例

基本使用(无偏移)

1
LDP X0, X1, [X2]
  • X2 指向的内存地址加载两个 64 位数据:
    • 第一个数据存入 X0
    • 第二个数据存入 X1

带偏移量

1
LDP X3, X4, [X5, #16]
  • X5 + 16 地址开始加载两个数据:
    • 第一个数据存入 X3
    • 第二个数据存入 X4

加载后自动更新基址

1
LDP X6, X7, [X8], #16
  • X8 指向的地址加载两个数据:
    • 第一个数据存入 X6
    • 第二个数据存入 X7
  • 然后将 X8 的值加上偏移量 16,更新 X8

使用场景

高效加载连续数据:LDP 可以一次性加载两个连续的内存数据,减少内存访问的次数。
栈操作:

  • 从栈中加载两个寄存器数据:
    1
    LDP X29, X30, [SP], #16
    (用于从栈恢复链寄存器和返回地址)
    数据处理:
  • 用于矩阵计算、加密解密等需要快速加载连续数据的场景。

与其他指令的比较

  • LDR 一次加载一个寄存器的数据。
  • LDP 一次加载两个寄存器的数据,效率更高。
  • STP 存储寄存器对到内存的指令,与 LDP 相对。

通过使用 LDP,可以减少指令数量和内存访问延迟,从而提升代码性能,是 ARM 汇编中非常常用的一种优化指令。

CSEL 指令

CSEL(Conditional Select)是 ARM 架构中用于实现条件选择的一条指令。它根据条件标志(由上一次指令的结果设置)来选择两个操作数之一,并将选择的结果存入目标寄存器。

指令功能

  • CSEL 是一种无分支条件执行指令,可以避免传统分支指令(如 B)可能引入的分支预测失误问题。
  • 根据指定的条件(Condition Code, CC),选择一个值存入目标寄存器。

指令格式

1
CSEL <Rd>, <Rn>, <Rm>, <cond>

参数说明

  • Rd:目标寄存器,用于存储最终选择的值。
  • Rn:第一个源寄存器(条件满足时选择该值)。
  • Rm:第二个源寄存器(条件不满足时选择该值)。
  • cond:条件码,指定选择 RnRm 的条件。

工作原理

根据指定的条件码 cond,检查条件标志(由之前指令更新)。
如果条件满足(cond 为真),将 Rn 的值存入 Rd
如果条件不满足(cond 为假),将 Rm 的值存入 Rd

条件码(cond

条件码是 4 位标志,常见的条件如下:

条件码含义描述
EQEqual等于,零标志位(Z)为 1
NENot Equal不等于,零标志位(Z)为 0
CS/HSCarry Set/Unsigned Higher or Same进位标志位(C)为 1
CC/LOCarry Clear/Unsigned Lower进位标志位(C)为 0
MIMinus负数,负标志位(N)为 1
PLPlus正数或零,负标志位(N)为 0
VSOverflow Set溢出标志位(V)为 1
VCOverflow Clear溢出标志位(V)为 0
HIUnsigned HigherC=1 且 Z=0
LSUnsigned Lower or SameC=0 或 Z=1
GEGreater or EqualN=V
LTLess ThanN≠V
GTGreater ThanZ=0 且 N=V
LELess or EqualZ=1 或 N≠V
ALAlways总是执行

示例

条件选择

1
CSEL X0, X1, X2, EQ
  • 如果条件标志 Z=1(EQ 条件成立),则将 X1 的值存入 X0
  • 如果条件标志 Z=0(EQ 条件不成立),则将 X2 的值存入 X0

用于最大值计算

1
2
CMP X3, X4          // 比较 X3 和 X4
CSEL X5, X3, X4, GT // 如果 X3 > X4,则选择 X3,否则选择 X4
  • 通过比较指令 CMP 更新条件标志。
  • CSEL 根据比较结果选择较大的值存入 X5

条件选择替代分支

传统的分支代码:

1
2
3
4
5
6
7
CMP X6, X7
B.GT greater
MOV X8, X7
B done
greater:
MOV X8, X6
done:

可以被简化为:

1
2
CMP X6, X7
CSEL X8, X6, X7, GT

优势

无分支延迟:CSEL 避免了传统分支指令可能引起的分支预测失败问题,从而提升性能。
代码简洁:减少多条分支和跳转指令,优化指令数量。
高效性:非常适合需要快速选择结果而不需要复杂逻辑的场景。

相关指令

  • CSINC 条件选择 + 增量操作。
  • CSINV 条件选择 + 取反操作。
  • CSNEG 条件选择 + 取负操作。

CSEL 是 ARM 汇编中一个强大的条件操作指令,在高效实现条件逻辑和优化分支跳转时非常有用。

UDIV 指令

在 ARM 汇编中,UDIV 是用于无符号整数除法的指令。

指令功能

UDIV 指令计算两个寄存器中值的无符号整数商,并将结果存储到目标寄存器中。

指令格式

1
UDIV <Rd>, <Rn>, <Rm>

参数说明

  • Rd 目标寄存器,用于存储结果(商)。
  • Rn 被除数(Dividend)。
  • Rm 除数(Divisor)。

工作原理

读取操作数:

  • 从寄存器 Rn 中读取被除数(无符号整数)。
  • 从寄存器 Rm 中读取除数(无符号整数)。
    执行无符号除法:
  • 计算 Rn ÷ Rm 的无符号商。
    存储结果:
  • 将计算的商存储到目标寄存器 Rd 中。

示例

基本用法

1
UDIV X0, X1, X2
  • 将寄存器 X1(被除数)的值除以 X2(除数)的值。
  • 将结果(无符号商)存储到寄存器 X0

示例操作

假设:

  • X1 = 20
  • X2 = 3

执行:

1
UDIV X0, X1, X2

结果:

  • X0 = 6 (整数商,忽略余数)。

使用场景

无符号整数运算:

  • 对非负整数进行除法操作。
    数组索引计算:
  • 在循环中计算步长或分区操作。
    处理无符号数据类型:
  • 常见于嵌入式系统和加密解密算法。

注意事项

除数为 0:

  • 如果 Rm = 0(除数为 0),行为未定义(UNPREDICTABLE)。
  • 在编写代码时应确保除数不为 0。

无符号数据:

  • UDIV 仅适用于无符号数据。如果需要对有符号数据进行除法,请使用 SDIV 指令。

与其他指令的比较

  • UDIV 无符号除法。
  • SDIV 有符号除法。
  • MUL 乘法(无符号/有符号)。
  • SMULLUMULL 64 位乘法扩展

MSUB 指令

在 ARM 汇编中,MSUB 是一种乘法并减法的指令,它的功能是先进行乘法运算,然后将结果从另一个值中减去。

指令功能

MSUB 计算以下数学公式:

1
Rd = Ra - (Rn * Rm)
  • 它会将两个操作数(RnRm)相乘,然后从一个累加值(Ra)中减去乘积,并将结果存储在目标寄存器 Rd 中。

指令格式

1
MSUB <Rd>, <Rn>, <Rm>, <Ra>

参数说明

  • Rd 目标寄存器,用于存储最终的结果。
  • Rn 第一个乘数。
  • Rm 第二个乘数。
  • Ra 被减数(累加值)。

工作原理

计算乘积:

  • RnRm 相乘,得到乘积 P = Rn * Rm
    减法操作:
  • 用累加值 Ra 减去乘积 P,即 Rd = Ra - P
    存储结果:
  • 将最终结果存入目标寄存器 Rd

示例

基本使用

1
MSUB X0, X1, X2, X3
  • 计算 X0 = X3 - (X1 * X2)

示例操作

假设:

  • X1 = 4(第一个乘数)
  • X2 = 3(第二个乘数)
  • X3 = 20(累加值)

执行:

1
MSUB X0, X1, X2, X3

结果:

  • X0 = 20 - (4 * 3) = 20 - 12 = 8

使用场景

优化计算公式:

  • 避免单独执行乘法和减法指令,提高运算效率。
  • 可用于公式简化,如 c - (a * b)

数字信号处理(DSP):

  • 常用于滤波、矩阵运算等需要大量乘法和减法操作的领域。

图形计算:

  • 在图形处理的几何计算或物理仿真中,常需要计算某种形式的乘法和减法组合。

与其他指令的比较

  • MADD:执行乘法并加法(Rd = Ra + (Rn * Rm))。
  • MSUB:执行乘法并减法(Rd = Ra - (Rn * Rm))。
  • MUL:仅执行乘法。
  • SMULL/UMULL:进行乘法并扩展结果为 64 位。
  • SDIV/UDIV:执行有符号/无符号除法。

注意事项

数据类型:

  • MSUB 适用于无符号和有符号整数运算。操作数的符号取决于数据的上下文。

性能优化:

  • MSUB 指令在 ARMv8-A 架构中是硬件加速的,避免了单独使用乘法和减法指令的额外指令开销。

MSUB 是 ARM 指令集中重要的数学运算指令之一,适合处理多步骤运算,帮助开发者简化代码逻辑并提高执行效率。

LDRSB 指令

LDRSB 是 ARM 汇编中的一条指令,用于从内存加载一个字节,并将其符号扩展为 32 位或 64 位,然后存储到目标寄存器中。

指令功能

  • LDRSB(Load Register Signed Byte)会从指定的内存地址加载一个字节(8 位)数据。
  • 将加载的 8 位数据按照其符号位(第 7 位)扩展为:
    • 32 位(用于目标寄存器为 Wn)。
    • 64 位(用于目标寄存器为 Xn)。
  • 符号扩展表示:
    • 如果第 7 位为 0,高位填充为 0
    • 如果第 7 位为 1,高位填充为 1

指令格式

1
LDRSB <Rt>, [<Rn>{, #<offset>}]

参数说明

  • Rt 目标寄存器,可以是 32 位(Wn)或 64 位(Xn)。
  • Rn 基址寄存器,提供内存地址。
  • offset(可选):偏移量,可以是立即数或寄存器值,用于计算内存地址。

工作原理

计算目标地址:

  • 从基址寄存器 Rn 读取地址。
  • 加上偏移量 offset(如果有)得到最终地址。
    从计算出的内存地址加载 8 位数据。
    符号扩展:
  • 根据字节的最高有效位(符号位,第 7 位)扩展到 32 位或 64 位。
  1. 将结果存入目标寄存器 Rt

示例

基本用法

1
LDRSB W0, [X1]
  • X1 指向的内存地址加载一个字节。
  • 符号扩展为 32 位。
  • 存入 W0

带偏移量

1
LDRSB X2, [X3, #4]
  • X3 + 4 的地址加载一个字节。
  • 符号扩展为 64 位。
  • 存入 X2

示例操作

假设内存地址 0x1000 存储值 0xFF(8 位,表示 -1 的补码形式),并执行以下指令:

1
LDRSB W0, [X1]
  • 假设 X1 = 0x1000
  • 从内存 0x1000 加载字节值 0xFF
  • 符号扩展:
    • 0xFF(8 位) -> 0xFFFFFFFF(32 位,有符号 -1)。
  • 存入 W0,结果是 0xFFFFFFFF

如果目标寄存器为 Xn

1
LDRSB X0, [X1]
  • 符号扩展:
    • 0xFF(8 位)-> 0xFFFFFFFFFFFFFFFF(64 位,有符号 -1)。
  • 存入 X0

使用场景

处理有符号 8 位数据:

  • 如读取字符(char 类型)或设备寄存器的有符号数据。
    加载小范围整数:
  • 符号扩展后,可以直接参与算术运算而无需额外处理。
    低位处理:
  • 从内存中加载压缩格式的数据(如图像像素或音频样本)。

注意事项

符号扩展:

  • 目标寄存器的大小(WnXn)决定符号扩展的结果位宽(32 位或 64 位)。
    对齐问题:
  • 由于加载的是一个字节,通常不需要严格的内存对齐,但基址和偏移量必须指向有效地址。
    内存访问异常:
  • 如果访问的地址无效或不可用,可能会触发异常。

与其他加载指令的比较

  • LDRB:无符号扩展加载字节(高位填充 0)。

  • LDRSB:符号扩展加载字节(高位填充符号位)。

  • LDR:加载 32 位或 64 位数据(不扩展)。

  • LDRSH:符号扩展加载半字(16 位)。

  • LDRSW:符号扩展加载字(32 位)。

STRB 指令

STRB 是 ARM 汇编语言中用于将一个字节(8位)数据从寄存器存储到内存的指令。

指令功能

STRB 的全称是 Store Register Byte,它会将寄存器的低 8 位数据存储到内存中。

指令格式

1
STRB <Rt>, [<Rn>{, #<offset>}]

参数说明

  • Rt 源寄存器,包含需要存储的数据(仅低 8 位会被存储)。
  • Rn 基址寄存器,指向内存地址的起始位置。
  • offset(可选):偏移量,指定从基址寄存器开始的内存偏移,单位为字节。

工作原理

从源寄存器 Rt 的低 8 位提取字节数据。
计算目标内存地址:

  • 如果没有偏移量,直接使用基址寄存器 Rn
  • 如果有偏移量,将 Rn 加上 offset 计算目标地址。
    将提取的 8 位数据存储到目标内存地址中。

示例

基本存储

1
STRB W0, [X1]
  • W0 中提取低 8 位数据(例如 0x34)。
  • 将其存储到 X1 指向的内存地址。

带偏移的存储

1
STRB W0, [X1, #4]
  • W0 中提取低 8 位数据。
  • 将其存储到 X1 + 4 的内存地址。

使用寄存器作为偏移

1
STRB W0, [X1, X2]
  • W0 中提取低 8 位数据。
  • 将其存储到 X1 + X2 的内存地址。

示例操作

假设:

  • W0 = 0x12345678
  • X1 = 0x1000
  • 偏移量为 #4

执行:

1
STRB W0, [X1, #4]

结果:

  • 提取 W0 的低 8 位:0x78
  • 存储到地址 0x1000 + 4 = 0x1004
  • 内存中:Mem[0x1004] = 0x78

使用场景

存储单字节数据:

  • char 或其他 8 位数据类型存储到内存。
    低位数据处理:
  • 操作某个寄存器的低 8 位,将其写入设备寄存器或内存。
    性能优化:
  • 比完整的 32 位或 64 位存储更节省内存和带宽。

注意事项

存储的数据仅为低 8 位:

  • 只存储寄存器的低 8 位,忽略高 24 位(如果是 32 位寄存器)或高 56 位(如果是 64 位寄存器)。
    对齐和有效地址:
  • ARM 允许字节存储的非对齐访问,但目标地址必须是有效的内存地址。
    数据覆盖:
  • 确保存储的字节不会意外覆盖关键内存。

相关指令

  • LDRB 从内存加载一个字节数据到寄存器。
  • STR 存储一个完整的 32 位或 64 位值到内存。
  • LDR 从内存加载一个完整的 32 位或 64 位值。
 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
总字数 54k 访客数 访问量