SI
= 源索引
DI
= 目标索引
正如其他人所指出的,它们对字符串指令有特殊用途。对于实模式编程,ES
段寄存器必须与 DI
和 DS
与 SI
一起使用,如
movsb es:di, ds:si
SI 和 DI 也可用作通用变址寄存器。例如,C
源代码
srcp [srcidx++] = argv [j];
编译成
8B550C mov edx,[ebp+0C]
8B0C9A mov ecx,[edx+4*ebx]
894CBDAC mov [ebp+4*edi-54],ecx
47 inc edi
其中 ebp+12
包含 argv
,ebx
为 j
,edi
包含 srcidx
。注意第三条指令使用 edi
乘以 4 并添加 ebp
偏移量 0x54(srcp
的位置);地址周围的括号表示间接。
这个
这个
AX
= 累加器
DX
= 双字累加器
CX
= 计数器
BX
= 基址寄存器
它们看起来像通用寄存器,但有许多指令(出乎意料?)使用其中的一个——但哪一个?——隐式使用。
有一些操作只能使用 DI/SI(或它们的扩展对应物,如果您在 1985 年没有学习 ASM)。其中有
REP STOSB
REP MOVSB
REP SCASB
它们分别是重复(=大量)存储、加载和扫描的操作。您所做的是设置 SI 和/或 DI 指向一个或两个操作数,也许在 CX 中计数,然后让'er rip。这些是一次处理一堆字节的操作,它们使 CPU 处于自动状态。因为您没有明确编码循环,所以它们比手动编码循环更有效(通常)。
以防万一您想知道:根据您设置操作的方式,重复存储可能很简单,例如将值 0 插入一个大的连续内存块;我认为,MOVSB 用于将数据从一个缓冲区(嗯,任何一串字节)复制到另一个缓冲区;并且 SCASB 用于查找与某些搜索条件匹配的字节(我不确定它是否仅在相等性上进行搜索,或者什么 - 你可以查一下 :))
这就是这些 regs 的大部分用途。
像 MOVSB 和 MOVSW 这样的操作码可以有效地将数据从 ESI 指向的内存复制到 EDI 指向的内存。因此,
mov esi, source_address
mov edi, destination_address
mov ecx, byte_count
cld
rep movsb ; fast!
除了其他答案中提到的字符串操作(MOVS/INS/STOS/CMPS/SCASB/W/D/Q 等)之外,我想补充一点,还有更多“现代”x86 汇编指令隐式使用最少 EDI/RDI:
SSE2 MASKMOVDQU
(以及即将到来的 AVX VMASKMOVDQU
)指令选择性地将字节从 XMM 寄存器写入 EDI/RDI 指向的内存。
除了用于批量操作的寄存器之外,它们还有助于通过 32 位调用约定中的函数调用(调用保留)来保留它们的属性。 ESI、EDI、EBX、EBP、ESP 是呼叫保留的,而 EAX、ECX 和 EDX 不是呼叫保留的。 C 库函数尊重调用保留寄存器,并且它们的值通过 C 库函数调用保持不变。
Jeff Duntemann 在他的汇编语言书中有一个用于打印命令行参数的示例汇编代码。该代码使用 esi 和 edi 来存储计数器,因为它们不会被 C 库函数 printf 更改。对于 eax、ecx、edx 等其他寄存器,不能保证它们不会被 C 库函数使用。
https://www.amazon.com/Assembly-Language-Step-Step-Programming/dp/0470497025
请参阅第 12.8 节 C 如何查看命令行参数。
请注意,64 位调用约定与 32 位调用约定不同,我不确定这些寄存器是否保留调用。
不定期副业成功案例分享