从内联汇编中的命令行获取参数

时间:2017-01-18 18:56:46

标签: c assembly x86

我必须计算从命令行获得的字符串中的单词数。 首先我做了这个程序:

#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,?

?&lt; - 这是问题:我不知道如何从命令行获取参数并将其作为char *访问。

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"

1 个答案:

答案 0 :(得分:3)

首先,让我们看看你的“工作”代码。虽然它有效,但这里有一些“可教”的物品。

首先,请养成在代码中使用注释的习惯。我意识到英语不是你的第一语言,所以我可能无法阅读你的评论,但你仍然应该拥有它们。

其次,停止使用;终止您的asm指令。是的,使用\n\t看起来有点笨拙,但是当你使用gcc的-S来输出汇编程序时(一种很好的方式来查看真正发生的事情),你的代码将会很乱\吨。

到目前为止,这让我们:

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,在那里再次检查 以查看它是否是空格而不是to petla0读取下一个字节。

另外两个尼特:当比较零时,我使用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中。

至于为什么你的纯粹的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 "
  • 您可以尝试使用打印argc的msg格式字符串。如果您正确传递参数,则应为2。
  • msg更改为使用%s,并打印出argv [0](又名[esp + 4])中的值。这应该是程序名称。
  • 使用该%s,您可以打印出argv [1]。