复制并显示数组

时间:2015-12-01 18:07:24

标签: arrays x86 nasm 32-bit display

大家好!

我是NASM的新手,我刚刚开始。我目前有一个程序保留一个数组,并且应该从命令行参数复制并显示字符串的内容到该数组中。

现在,我不确定我是否正确地复制字符串,因为每次尝试显示时,我都会收到分段错误! 这是我复制数组的代码:

例如:

%include "asm_io.inc"

section .bss
X: resb 50 ;;This is our array

〜一些代码〜

mov eax, dword [ebp+12]   ; eax holds address of 1st arg
add eax, 4                ; eax holds address of 2nd arg
mov ebx, dword [eax]      ; ebx holds 2nd arg, which is pointer to string
mov ecx, dword 0
;Where our 2nd argument is a string eg "abcdefg" i.e ebx = "abcdefg"
copyarray:
      mov al, [ebx]         ;; get a character
      inc ebx
      mov [X + ecx], al
      inc ecx
      cmp al, 0
      jz done
      jmp copyarray

我的问题是这是否是复制数组的正确方法,以及如何在之后显示数组的内容? 谢谢!

1 个答案:

答案 0 :(得分:1)

循环看起来不错,但很笨重。如果您的程序崩溃,请使用调试器。请参阅以获取链接,并快速了解asm的gdb。

我认为你正确地加载了argv[1]。 (请注意,这是第一个命令行arg。argv[0]是命令名。)https://en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Framesebp+12是第二个arg到32位函数的常用点设置堆栈帧。

Michael Petch评论了Simon删除的答案,即asm_io库中包含print_int,print_string,print_char和print_nl例程。所以大概是你指向缓冲区的指针到其中一个函数,并称它为一天。或者您可以使用sys_write(2)指令直接调用int 0x80,因为您不需要进行任何字符串格式化,并且您已经拥有了长度。

不是为两个数组分别递增,而是可以为两者使用相同的索引,并为加载使用索引寻址模式。

;; main (int argc ([esp+4]), char **argv ([esp+8]))
... some code you didn't show that I guess pushes some more stuff on the stack
mov eax, dword [ebp+12]   ; load argv
    ;; eax + 4 is &argv[1], the address of the 1st cmdline arg (not counting the command name)
mov esi, dword [eax + 4]  ; esi holds 2nd arg, which is pointer to string
xor ecx, ecx

copystring:
    mov   al, [esi + ecx]
    mov   [X + ecx], al
    inc   ecx

    test  al, al
    jnz copystring

我将评论更改为“cmdline arg”,以区分这些和“函数参数”。

如果没有花费任何额外指令,请使用esi作为源指针,edi表示dest指针,以提高可读性。

检查ABI您可以使用哪些寄存器而不保存/恢复(至少eax,ecx和edx。这可能是32位x86的全部。)。如果要使用其他寄存器,则必须保存/恢复。至少,如果您正在制作符合通常ABI的功能。在asm中你可以做你喜欢的事情,只要你不告诉C编译器调用非标准函数。

还要注意循环结束时的改进。单个jnz循环比jz break / jmp更有效。

这应该在Intel上每个字节一个周期运行,因为test/jnz宏融合成一个uop。负载是一个uop,商店微熔合成一个uop。 inc也是一个uop。 Core2以后的Intel CPU为4宽:每个时钟发出4个uop。

您的原始循环以该速度的一半运行。由于它是6 uop,因此需要2个时钟周期才能发出迭代。

另一种执行此操作的hacky方法是将Xebx之间的偏移量转换为另一个寄存器,因此其中一个有效地址可以使用one-register addressing mode,即使是dest不是静态数组。 mov [X + ebx + ecx], al。 (其中ecx = X - start_of_src_buf)。但理想情况下,您将使存储器使用单寄存器寻址模式,除非加载是ALU指令的内存操作数,可以微融合它。如果dest是一个静态缓冲区,那么这个地址不同的hack根本没用。

您不能使用rep字符串指令(如rep movsb)来为隐式长度字符串实现strcpy(C以null结尾,而不是使用单独存储的长度)。嗯,你可以,但只扫描源两次:一次查找长度,再次发送到memcpy。

要比一个字节时钟快,你必须使用向量指令在并行的16个位置中的任何一个位置测试空字节。谷歌以优化的strcpy实现为例。可能对全0的向量寄存器使用pcmpeqb