我是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
我的问题是这是否是复制数组的正确方法,以及如何在之后显示数组的内容? 谢谢!
答案 0 :(得分:1)
循环看起来不错,但很笨重。如果您的程序崩溃,请使用调试器。请参阅x86以获取链接,并快速了解asm的gdb。
我认为你正确地加载了argv[1]
。 (请注意,这是第一个命令行arg。argv[0]
是命令名。)https://en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames说ebp+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方法是将X
和ebx
之间的偏移量转换为另一个寄存器,因此其中一个有效地址可以使用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
。