记录一下几个指令用法
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 #16
或ROR #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
:条件码,指定选择Rn
或Rm
的条件。
工作原理
根据指定的条件码 cond
,检查条件标志(由之前指令更新)。
如果条件满足(cond
为真),将 Rn
的值存入 Rd
。
如果条件不满足(cond
为假),将 Rm
的值存入 Rd
。
条件码(cond
)
条件码是 4 位标志,常见的条件如下:
条件码 | 含义 | 描述 |
---|---|---|
EQ | Equal | 等于,零标志位(Z)为 1 |
NE | Not Equal | 不等于,零标志位(Z)为 0 |
CS/HS | Carry Set/Unsigned Higher or Same | 进位标志位(C)为 1 |
CC/LO | Carry Clear/Unsigned Lower | 进位标志位(C)为 0 |
MI | Minus | 负数,负标志位(N)为 1 |
PL | Plus | 正数或零,负标志位(N)为 0 |
VS | Overflow Set | 溢出标志位(V)为 1 |
VC | Overflow Clear | 溢出标志位(V)为 0 |
HI | Unsigned Higher | C=1 且 Z=0 |
LS | Unsigned Lower or Same | C=0 或 Z=1 |
GE | Greater or Equal | N=V |
LT | Less Than | N≠V |
GT | Greater Than | Z=0 且 N=V |
LE | Less or Equal | Z=1 或 N≠V |
AL | Always | 总是执行 |
示例
条件选择
1 | CSEL X0, X1, X2, EQ |
- 如果条件标志 Z=1(
EQ
条件成立),则将X1
的值存入X0
。 - 如果条件标志 Z=0(
EQ
条件不成立),则将X2
的值存入X0
。
用于最大值计算
1 | CMP X3, X4 // 比较 X3 和 X4 |
- 通过比较指令
CMP
更新条件标志。 CSEL
根据比较结果选择较大的值存入X5
。
条件选择替代分支
传统的分支代码:
1 | CMP X6, X7 |
可以被简化为:
1 | CMP X6, X7 |
优势
无分支延迟: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
乘法(无符号/有符号)。SMULL
和UMULL
64 位乘法扩展
MSUB 指令
在 ARM 汇编中,MSUB
是一种乘法并减法的指令,它的功能是先进行乘法运算,然后将结果从另一个值中减去。
指令功能
MSUB
计算以下数学公式:
1 | Rd = Ra - (Rn * Rm) |
- 它会将两个操作数(
Rn
和Rm
)相乘,然后从一个累加值(Ra
)中减去乘积,并将结果存储在目标寄存器Rd
中。
指令格式
1 | MSUB <Rd>, <Rn>, <Rm>, <Ra> |
参数说明
Rd
目标寄存器,用于存储最终的结果。Rn
第一个乘数。Rm
第二个乘数。Ra
被减数(累加值)。
工作原理
计算乘积:
- 将
Rn
和Rm
相乘,得到乘积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
)。
- 32 位(用于目标寄存器为
- 符号扩展表示:
- 如果第 7 位为
0
,高位填充为0
。 - 如果第 7 位为
1
,高位填充为1
。
- 如果第 7 位为
指令格式
1 | LDRSB <Rt>, [<Rn>{, #<offset>}] |
参数说明
Rt
目标寄存器,可以是 32 位(Wn
)或 64 位(Xn
)。Rn
基址寄存器,提供内存地址。offset
(可选):偏移量,可以是立即数或寄存器值,用于计算内存地址。
工作原理
计算目标地址:
- 从基址寄存器
Rn
读取地址。 - 加上偏移量
offset
(如果有)得到最终地址。
从计算出的内存地址加载 8 位数据。
符号扩展: - 根据字节的最高有效位(符号位,第 7 位)扩展到 32 位或 64 位。
- 将结果存入目标寄存器
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
类型)或设备寄存器的有符号数据。
加载小范围整数: - 符号扩展后,可以直接参与算术运算而无需额外处理。
低位处理: - 从内存中加载压缩格式的数据(如图像像素或音频样本)。
注意事项
符号扩展:
- 目标寄存器的大小(
Wn
或Xn
)决定符号扩展的结果位宽(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 位值。