首先,让我们看看您的“工作”代码。虽然它有效,但这里有一些“可教”的项目。
首先,请养成在代码中使用 cmets 的习惯。我知道英语不是你的第一语言,所以我可能无法阅读你的 cmets,但你应该拥有它们。
其次,停止使用; 来终止您的asm 指令。是的,使用\n\t 看起来有点笨拙,但是当您使用 gcc 的 -S 输出汇编程序时(查看实际情况的好方法),如果没有 \n\t,您的代码将一团糟。
到目前为止,这让我们明白了:
asm volatile(
".intel_syntax noprefix\n\t"
// %1 is read-only, so use eax as temp
"mov eax,%1\n\t"
// # of words found
"xor edx,edx\n\t"
"jmp petla\n"
// Skip over spaces
"petla0:\n\t"
"inc eax\n"
"petla:\n\t"
"cmp [eax],byte ptr 0\n\t"
"jz wyjscie\n\t" // End of string
"cmp [eax],byte ptr 32\n\t"
"jz petla0\n\t" // Another space
// Starting new word
"inc edx\n"
// Walk the rest of the current word
"petla1:\n\t"
"inc eax\n\t"
"cmp [eax],byte ptr 0\n\t"
"jz wyjscie\n\t" // End of string
"cmp [eax],byte ptr 32\n\t"
"jz petla\n\t" // End of word
"jmp petla1\n" // Not end of word
"wyjscie:\n\t"
"mov %0,edx\n\t"
".att_syntax prefix"
: "=r" (x)
: "r" (s)
: "eax","edx"
);
第三,您需要了解,在使用扩展 asm 时,%0 只是将传入的任何内容作为第一个参数引用的一种方式。在这种情况下,您指定它必须是一个寄存器 ("=r")。所以这个值已经是一个寄存器。您可以将计数直接存储在 %0 中,而不是同时使用 edx 和 %0。
第四,byte ptr 的目的是让汇编器知道[eax] 是否意味着:[eax] 处的字节、[eax] 处的单词、[eax] 处的 dword 等。这样的话,它更常见于cmp 指令的另一侧:
asm volatile(
".intel_syntax noprefix\n\t"
// %1 is read-only, so use eax as temp
"mov eax,%1\n\t"
// # of words found
"xor %0,%0\n\t"
"jmp petla\n"
// Skip over spaces
"petla0:\n\t"
"inc eax\n"
"petla:\n\t"
"cmp byte ptr [eax], 0\n\t"
"jz wyjscie\n\t" // End of string
"cmp byte ptr [eax], ' '\n\t"
"jz petla0\n\t" // Another space
// Starting new word
"inc %0\n"
// Walk the rest of the current word
"petla1:\n\t"
"inc eax\n\t"
"cmp byte ptr [eax], 0\n\t"
"jz wyjscie\n\t" // End of string
"cmp byte ptr [eax], ' '\n\t"
"jz petla\n\t" // End of word
"jmp petla1\n" // Not end of word
"wyjscie:\n\t"
".att_syntax prefix"
: "=r" (x)
: "r" (s)
: "eax","edx"
);
接下来是什么?哦耶。当您使用 jz 或 jnz 时,如果它不跳转,则代码将直通到下一条指令。这意味着:
"cmp byte ptr [eax], 0\n\t"
"jz wyjscie\n\t" // End of string
"cmp byte ptr [eax], ' '\n\t"
"jz petla\n\t" // End of word
"jmp petla1\n" // Not end of word
"wyjscie:\n\t"
可以这样做:
"cmp byte ptr [eax], 0\n\t"
"jz petla\n\t" // End of word
"cmp byte ptr [eax], ' '\n\t"
"jnz petla1\n\t" // Not end of string
"wyjscie:\n\t"
一般来说,我会避免多次读取内存。那么你在哪里做:
"cmp byte ptr [eax], 0\n\t"
"cmp byte ptr [eax], ' '\n\t"
我愿意:
"mov dl, [eax]\n\t"
"cmp dl, 0\n\t"
"cmp dl, ' '\n\t"
这也让我们摆脱了byte ptr。 dl 只能保存一个字节,所以这一定是我们正在阅读的内容。
另一个微妙的点:在你的原始代码中,当你遍历字母时,如果遇到空格,你会跳回到 petla,在那里你 再次检查 看看它是否是一个空格of 到 petla0 读取下一个字节。
还有另外 2 个 nits:当与零进行比较时,我使用 test 而不是 cmp(生成稍微更好的代码)。虽然它完全做同样的事情,但当我比较 2 个值 (cmp edx, ' ') 时,我发现用“这些东西是否‘相等’”而不是“有区别”来思考更容易他们之间为零?”因此,我会使用je 而不是jz。
把所有这些放在一起给了我:
asm (
".intel_syntax noprefix\n\t"
// %1 is read-only, so use eax as temp
"mov eax, %1\n\t"
// # of words found
"xor %0,%0\n"
// Skip over spaces
"petla0:\n\t"
"mov dl, [eax]\n\t"
"inc eax\n\t"
"test dl, dl\n\t"
"jz wyjscie\n\t" // End of string
"cmp dl, ' '\n\t"
"je petla0\n\t" // Another space
// Starting new word
"inc %0\n"
// Walk the rest of the current word
"petla1:\n\t"
"mov dl, [eax]\n\t"
"inc eax\n\t"
"cmp dl, ' '\n\t"
"je petla0\n\t" // end of word
"test dl, dl\n\t"
"jnz petla1\n" // not end of string
"wyjscie:\n"
".att_syntax prefix;"
: "=r" (x)
: "r" (s)
: "eax", "edx", "cc", "memory"
);
我还删除了volatile。由于您使用的是输出(通过打印 x),因此不需要这样做。
我会让你自己把任何你想保留的东西卷进你的纯汇编中。
至于为什么你的纯asm不起作用,我不在linux上,所以我不能运行这个。但是,我认为您的计数代码没有任何实际问题。您可能会查看 this 以访问命令行参数,但您所做的不应该给您 1。
你是如何指定你的命令行的?我怀疑您没有在字符串周围使用" 标记:a.out " aqr b qabxx xryc pqr"。这将导致每个单词都被视为一个单独的(以空结尾的)参数。
编辑 1:在更多 reading 之后,看起来指向 argv[1] 的指针确实应该位于 [esp + 8]。至少在linux上。你不在Windows上,对吧?很确定它使用了不同的方案。
您可以尝试这样做以确保您的 asm 工作正常,但我很确定这不是您的问题。
lea ecx, str
// Down by .data add:
str: .ascii " asdf adfs asd f adsf "
- 您可以尝试使用
msg 格式字符串来打印argc。如果您正确传递参数,则应该是 2。
- 将您的
msg 更改为使用 %s,并从 argv[0](又名 [esp+4])打印出值。这应该是程序名称。
- 使用该 %s,您可以打印出 argv[1]。