我必须计算您从命令行作为参数获得的字符串中的字数。首先,我制作了这个程序:
#include <stdio.h>
#include <string.h>
int main( int argc, char *argv[] ){
char* s;
if(argc==1)
{
s="";
} else {
s = argv[1];
}
//char* s = " aqr b qabxx xryc pqr"; example
int x;
asm volatile(
".intel_syntax noprefix;"
"mov eax,%1;"
"xor edx,edx;"
"jmp petla;"
"petla0:"
"inc eax;"
"petla:"
"cmp [eax],byte ptr 0;"
"jz wyjscie;"
"cmp [eax],byte ptr 32;"
"jz petla0;"
"inc edx;"
"petla1:"
"inc eax;"
"cmp [eax],byte ptr 0;"
"jz wyjscie;"
"cmp [eax],byte ptr 32;"
"jz petla;"
"jmp petla1;"
"wyjscie:"
"mov %0,edx;"
".att_syntax prefix;"
: "=r" (x)
: "r" (s)
: "eax","edx"
);
printf("%hd\n",x);
return 0;
}
它工作得很好;我得到5
作为“aqr b qabxx xryc pqr”的答案。但是我需要我的程序只使用汇编代码编写。像这样:
.intel_syntax noprefix
.globl main
.text
main:
mov ecx,?
?
xor edx,edx
jmp petla
petla0:
inc ecx
petla:
cmp byte ptr [ecx],0
jz wyjscie
cmp byte ptr [ecx],32
jz petla0
inc edx
petla1:
inc ecx
cmp byte ptr [ecx],0
jz wyjscie
cmp byte ptr [ecx], 32
jz petla
jmp petla1
wyjscie:
push edx
push offset msg
call printf
add esp, 8
mov edx,0
ret
.data
msg: .ascii "number of words=%d\n"
所以首先,让我们看看你的“工作”代码。虽然它有效,但这里有一些“可教”的项目。
首先,请养成在代码中使用注释的习惯。我意识到英语不是你的第一语言,所以我可能看不懂你的注释,但你仍然应该有它们。
其次,停止使用;
来终止您的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,在那里您再次检查它是否是空格而不是petla0以读取下一个字节。
还有另外两个缺点:当比较一些东西和零时,我使用test
而不是cmp
(生成稍微好一点的代码)。虽然它做了完全相同的事情,但当我比较两个值(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"
);
我还删除了易失性
。由于您使用的是输出(通过打印x
),因此这不是必需的。
我会让你自己把你想保留的任何东西卷成你纯粹的阿斯帕斯。
至于为什么你的纯asm不起作用,我不在linux上,所以我不能运行这个。然而,我看不出你的计数代码有什么问题。你可以查看这个来访问命令行参数,但是你正在做的事情不应该给你1。
您如何指定您的命令行?我怀疑您没有在字符串周围使用"
标记:a. out"aqr b qabxx xryc pqr"
。这会导致每个单词都被视为单独的(以空结尾)参数。
编辑1:经过更多的阅读,看起来指向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])打印出值。这应该是程序名称。