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

标签 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,?

?<- 问题是:我不知道如何从命令行获取参数并将其作为 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"

最佳答案

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

首先,请养成在代码中使用注释的习惯。我知道英语不是你的第一语言,所以我可能看不懂你的评论,但你应该拥有它们。

二、停止使用;终止您的 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 来读取下一个字节。

和其他 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]。
  • 关于c - 从内联汇编中的命令行获取参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41727300/

    相关文章:

    程序集:数组写入问题

    c - 使用 openmp 并行化此 C 代码

    c - 如何对链表进行排序

    使用## 和 __LINE__ 创建 C 宏(标记连接与定位宏)

    assembly - 如何通过BIOS中断在实模式下处理键盘?

    performance - 移位操作码真的比移动快 3 倍吗?英特尔 x86

    c - 需求中的 for 循环错误 (C)

    c++ - 避免使用内联 asm 优化 away 变量

    assembly - 如何在保护模式下更新数据段选择器

    assembly - 指令表中缺少延迟