我一直在从我的书中研究这个汇编程序,我有一个关于它的问题。该程序的目的是将 string1 复制到 string2。我的问题涉及以下两条说明:
mov AX,DS
mov ES,AX
我发现如果没有它们,程序将无法正常工作,但我会想通过将 ESI
指向 string1 并将 EDI
指向 string2,这就是你的全部需要做。然后只需增加 ESI
和 EDI
并逐个字符地移动它。 DS
到底包含什么以及为什么我们需要将其移至 ES
?
.DATA
string1 db 'The original string',0
strLen EQU $ - string1
.UDATA
string2 resb 80
.CODE
.STARTUP
mov AX,DS ; set up ES
mov ES,AX ; to the data segment
mov ECX,strLen ; strLen includes NULL
mov ESI,string1
mov EDI,string2
cld ; forward direction
rep movsb
最佳答案
所有使用EDI
的字符串指令都使用ES:EDI
。 (或di
或rdi
)
使用EDI
(如[edi]
)的显式寻址模式默认为DS
,但movs/stos/scas/cmps
(with/without rep
/repz/nz
) all use es:edi
。 lods
仅使用ds:esi
。 (rep lods
“有效”,但很少有用。当 cx=0 或 1 时,它可以作为缓慢的条件加载,因为与 loop
不同,rep
在递减之前检查 cx
。)
请注意,尽管 scas
是只读的,但它使用 (r|e)di
。这使得它可以很好地与 lods
配合使用:使用 lods
从一个数组加载,scas
可以与不同的数组进行比较。 (可选地在比较之前对 (r|e)ax
进行某种处理)。
通常,当您可以使用 32 位地址时,您将拥有一个平面内存模型,其中所有段都具有相同的基址和限制。或者,如果您使用 NASM 制作 .COM
平面二进制文件,则您将拥有微小的实模式内存模型,其中所有段都具有相同的值。请参阅@MichaelPetch 的评论 on this answer和 on the question 。 如果您的程序在不设置ES
的情况下无法运行,那么您正在做一些奇怪的事情。 (就像可能在某个地方破坏es
?)
请注意,在没有地址大小前缀的 16 位模式下,rep movsb
使用 CX
、DS:SI
和 ES:DI
,无论您是否使用操作数大小前缀来编写 edi
而不是 di
。
另请注意,代表字符串指令(尤其是非代表版本)**通常不是最快的方法。它们适合代码大小,但通常比 SSE/AVX 循环慢。
rep stos
和 rep movs
具有快速微编码实现,可以以 16 或 32 字节(或 Skylake-AVX512 上的 64 字节?)为单位存储或复制。请参阅Enhanced REP MOVSB for memcpy 。凭借 32 字节对齐的指针和中等到大的缓冲区大小,它们可以与优化的 AVX 循环一样快。当现代 CPU 上的大小低于 128 或 256 字节或未对齐的指针时,AVX 复制循环通常会获胜。英特尔的优化手册中有一节对此进行了介绍。
但是 repne cmpsb
绝对不是实现 memcmp
的最快方法:使用 SSE2 或 AVX2 SIMD 比较 (pcmpeqb
),因为微码一次仍然只比较一个字节。 (小心读取超过缓冲区末尾的内容,尤其是避免跨越页面(或最好是缓存行)边界。)无论如何,repne
/repe
没有“快速”不幸的是,Intel 或 AMD CPU 中的字符串”优化。
关于汇编字符串指令在实模式下寄存器 DS 和 ES,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47566416/