串操作指令(String Instructions)
串指令对内存块进行批量操作,结合 rep 前缀自动循环,使用隐式寄存器(i386 约定):
- 源地址:
%rsi(32 位%esi) - 目的地址:
%rdi(32 位%edi) - 计数:
%rcx(32 位%ecx) - 方向标志 DF:
cld(清 0,地址递增)/std(置 1,地址递减)
每条串指令处理一个"元素",元素大小由后缀决定:b(1) w(2) l(4) q(8)。执行后 rsi/rdi 按方向 ± 元素大小移动,rcx 减 1。
基本串指令
| 指令 | 作用 | 等价语义 |
|---|---|---|
movs |
复制 | ES:[rdi] = DS:[rsi] |
cmps |
比较 | cmp [rsi],[rdi] |
scas |
扫描 | cmp al/ax/eax/rax, [rdi] |
stos |
存储 | [rdi] = acc |
lods |
加载 | acc = [rsi] |
ins / outs |
端口 I/O | 需特权(IOPL),用户态一般不可用 |
rep 前缀家族
| 前缀 | 语义 |
|---|---|
rep |
rcx!=0 时重复(用于 movs/stos) |
repne / repnz |
rcx!=0 且 ZF=0 时重复(用于 cmps/scas) |
repe / repz |
rcx!=0 且 ZF=1 时重复 |
注意:rep 是"先判断后执行",rcx=0 时一次也不执行——不需要在循环前单独判断 0 长度。
示例:内存复制(memcpy)
# memcpy(rdi=dst, rsi=src, rdx=n) —— n 为字节数
memcpy:
movq %rdx, %rcx
cld # 地址递增
rep movsb # 逐字节复制 rcx 次
ret
若按 8 字节复制更快(需 n 为 8 的倍数且对齐):
memcpy8:
movq %rdx, %rcx
shrq $3, %rcx # 字节数 -> 八字节数
cld
rep movsq # 主块按 8 字节复制
movq %rdx, %rcx
andq $7, %rcx # 余数部分
rep movsb # 按字节补齐
ret
示例:内存填充(memset)
# memset(rdi=dst, rsi=value, rdx=n)
memset:
movq %rdx, %rcx
movb %sil, %al
cld
rep stosb
ret
示例:查找字节(memchr / 扫描)
# 在缓冲区中查找目标字节,返回地址或 NULL
# rdi=buf, rcx=len, al=target
find_byte:
cld
repne scasb # 逐字节比较,找到则 ZF=1
je found
xorq %rax, %rax # 未找到返回 0
ret
found:
leaq -1(%rdi), %rax # rdi 已越过匹配处,回退 1
ret
注意事项
rep movs/rep stos由 CPU 内部做微码优化,对大块通常比手写循环快;超大块还可配合erms(Enhanced REP MOVS)特性。小块数据用 SIMD 或普通循环可能更优。- 不要对重叠区域用
rep movsb正向复制(应用反向std+rep movsb或memmove逻辑)。 lods与rep配合较少见,因其不断覆盖累加器、只留下最后一个元素。- ABI 要求:System V 规定函数入口/出口处 DF 必须为 0——若函数内用了
std,返回前务必cld,否则会破坏调用者(如 C 代码)的行为。