0

大家好!


我是 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

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

4

1 回答 1

1

循环看起来不错,但很笨重。如果您的程序崩溃,请使用调试器。有关链接,请参阅和 asm 的 gdb 快速介绍。

我认为您argv[1]已正确加载。(请注意,这是第一个命令行参数。 argv[0]是命令名称。) https://en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames说这ebp+12是第二个参数到 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目标指针,以提高可读性。

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

还要注意循环结束时的改进。单个jnzto 循环比jz break / jmp.

这应该在 Intel 上以每个字节一个周期运行,因为test/jnz宏融合到一个 uop 中。负载为 1 uop,存储微熔为 1 uop。 inc也是一个uop。自 Core2 以来的英特尔 CPU 为 4 宽:每个时钟发出 4 微指令。

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


另一种解决方法是获取另一个寄存器之间的偏移量Xebx因此其中一个有效地址可以使用单寄存器寻址模式,即使 dest 不是静态数组。 mov [X + ebx + ecx], al. (其中 ecx = X - start_of_src_buf)。但理想情况下,您应该使存储成为使用单寄存器寻址模式的存储,除非加载是 ALU 指令的内存操作数,可以对其进行微融合。在 dest 是静态缓冲区的情况下,这种地址不同的 hack 根本没有用。


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

为了比一个字节时钟快,您必须使用向量指令来并行测试 16 个位置中的任何一个处的空字节。例如,谷歌优化了 strcpy 实现。可能使用pcmpeqb全零向量寄存器。

于 2015-12-03T03:30:36.453 回答