作为汇编代码的初学者,我对movs很困惑。从y开始的九个字节的结果内容将是('a','b','c','a','b','c','a','b','c'),但我不知道为什么。我知道movs是从一个内存复制到另一个内存,但以下代码如何工作?
1 .data
2 x: .string "abcde" # 5 characters plus a null
3 y: .space 9
4
5 .text
6 .globl _start
7 _start:
8 movl $x, %esi #esi point to x as source
9 movl %esi, %edi #edi point to x as destination
10 addl $3, %edi #why we add 3 to edi?
11 movl $6, %ecx #counter
12 rep movsb #what does it exactly do?
13 done:
答案 0 :(得分:3)
指令的工作原理写在指令集参考中。
addl $ 3,%edi #why我们将3添加到edi?
调整目标地址,使其指向第4个字符。
rep movsb#究竟是做什么的?
执行ecx
次迭代,每次将一个字节从[ds:esi]
复制到[es:edi]
并将两者递增(假设方向标志是清除的,通常是这样)。
因此,该代码相当于:
for(i = 0; i < 6; i++) x[i + 3] = x[i];
这当然会复制从字符串开头开始的6个字符,而不是3。但是当i >= 3
它将读取已复制的字节时:
1. abcae
2. abcab
3. abcabc
4. abcabca
5. abcabcab
6. abcabcabc
答案 1 :(得分:2)
英特尔手册与https://stackoverflow.com/tags/x86/info相关联。您将找到movsb
所做的所有事情的准确而详细的描述。指针增量的方向取决于&#34;方向&#34;标志,但是公共ABI要求在函数入口上清除方向标志(因此esi / edi递增,不递减)。除非您正在编写引导加载程序(应该尽可能少地假设初始状态),否则您可以假设DF被清除,除非您自己设置它。
重叠的source和dest会生成这样的东西。读取的第4个字节不是读取d
,而是写为第一个字节的a
。 add $3, %edi
显然将目的地设置为x+3
,我猜是要展示重叠的副本。更好的代码就是用mov/add
insn替换lea 3(%esi), %edi
对。
您确定abcabcabc
是您在y
找到的内容吗?这正是您应该在x
找到的内容,尾随的零字节是y
中最初的那个(不是movs
复制的那个)。
即使rep movs
的微代码实现将使用64位加载/存储以获得高性能,它仍然可以处理紧密重叠的src和dest的特殊情况。 (在这种情况下IDK关于性能。它可能会回落到较慢的版本,或者找出重复的模式和stos
的那种。)
答案 2 :(得分:2)
Intel's manuals中描述了所有x86指令,因此我建议您下载它们。
MOVSB
的说明是:
A4 MOVSB For legacy mode, Move byte from address
DS:(E)SI to ES:(E)DI. For 64-bit mode move
byte from address (R|E)SI to (R|E)DI.
使用以下操作伪代码:
DEST ← SRC;
IF (Byte move)
THEN IF DF = 0
THEN
(E)SI ← (E)SI + 1;
(E)DI ← (E)DI + 1;
ELSE
(E)SI ← (E)SI – 1;
(E)DI ← (E)DI – 1;
FI;
...
REP
前缀仅表示:
在计数寄存器中指定的次数重复字符串指令。
在这种情况下,计数寄存器为ecx
。
答案 3 :(得分:1)
在调试器中运行它,看看!
基本上,esi(source)和edi(dest)都指向.string
变量。然后将edi设置为指向超过字符串开头的3个字符,即它指向保存的内存'D'。
此时,以下执行6次(以rep movsb
)
编辑:正如Michael指出的那样 - ESI和EDI是递增还是递减取决于direction-flag。如果(已)设置,则寄存器递减。 /编辑
因此,目标始终位于源前3个字节。因此,在复制了3个字节之后,源现在指向dest在rep movsb
指令开始之前的位置。