我知道CLD会清除方向标志(direction flag),而STD会设置方向标志。但是,设置和清除方向标志有什么意义呢?
REP
前缀来重复操作。(虽然lods
与rep
不太有用)。MOVS
(将内存复制到内存)、STOS
(存储AL/AX/EAX/RAX)、SCAS
(扫描字符串)、CMPS
(比较字符串)和LODS
(加载字符串)。还有ins
/outs
用于在内存和IO端口之间进行复制。每个指令都有字节、字、双字和四字操作数大小可用。dword [es:edi] = dword [ds:esi] // 4-byte copy memory to memory
if (DF == 0)
esi += 4;
edi += 4;
else // DF == 1
esi -= 4;
edi -= 4;
fi
使用REP前缀,它会执行ECX次操作,现代的x86 CPU具有优化的“快速字符串”微码,可以使用16字节或32字节的内部操作进行复制(或stos
存储)。请参见关于内存带宽和ERMSB特性的此问答。(注意,仅rep stos
和rep movs
被以这种方式优化,而不是repne/repe scas
或cmps
。)
CLD指令清除方向标志位,数据正向传送。 STD指令设置方向标志位,数据反向传送。
rep movsd
/ rep stosd
而无需 CLD 指令。(在现代x86上,它们通常只能向上快速移动,DF=0) - Peter CordesCLD: 清除方向标志,使字符串指针在每个字符串操作后自动增加
STD: std 用于将方向标志设置为1,以便在执行任何一个字符串指令时,SI和/或DI将自动减少以指向下一个字符串元素。如果设置了方向标志,则对于字节字符串,SI/DI将减少1,对于字字符串,SI/DI将减少2。
这个答案可能对您有所帮助。
CLD:清除EFLAGS寄存器中的DF标志。当DF标志设置为0时,字符串操作会递增索引寄存器(ESI和/或EDI)。
这里有一个简单的例子:
section .text
global main
main:
mov ecx, len
mov esi, s1
mov edi, s2
cld ; redundant because DF is already guaranteed to be 0 on function entry
; but included for illustration purposes
loop_here:
lodsb ; AL=[esi], ESI+=1 (because DF=0, otherwise ESI-=1)
add al, 02
stosb ; [edi]=AL, EDI+=1 (because DF=0, otherwise EDI-=1)
loop loop_here ; like dec ecx / jnz but without setting flags
; ECX=0, EDI and ESI pointing to the end of their buffers
mov edx, len-1 ;message length, not including the terminating 0 byte
mov ecx,s2 ;message to write
mov ebx,1 ;file descriptor (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax,1 ;system call number (sys_exit)
xor ebx,ebx
int 0x80 ;call kernel: sys_exit(0)
section .data
s1: db 'password', 0 ; source buffer
len equ $-s1
section .bss
s2: resb len ; destination buffer
(使用nasm -felf32 caesar.asm && gcc -no-pie -m32 caesar.o -o caesar
进行汇编和链接。如果您喜欢,也可以将其作为_start
而不是main
链接到静态可执行文件中。)
(此示例尝试实现凯撒密码。)
cld
之前使用 lodsb
/stosb
仍然能够可靠工作的原因。rep movsb
在 ECX=0(来自 loop
)的情况下运行,因此它会将零字节复制到 s1
/s2
的末尾,并且此时设置 DF 的值并不重要。 - Peter Cordesloop
很慢,除非你优化代码大小而不是速度,否则不要使用它。此外,您可能希望将字母表的最后2个字母包装到前2个字母中。因此,如果您要实现包装检查,则有意义加载并在寄存器中操作一个字节。否则,您可以使用add byte [esi], 2
/ inc esi
原地修改字符串。或者,如果不会进位,可以一次处理4个字节,例如add dword [esi], 0x02020202
。 - Peter Cordes