Loops x86汇编:循环!
好的,长话短说,我正在学习汇编,我正在尝试做一个循环,打印出ascii字符“0”-“9”。 所以,我做了我在示例中看到的所有基础工作,比如用pushad和popad保存寄存器状态,分配堆栈空间,确保事情按照开始的方式进行。 所以我管理了这个小例子:Loops x86汇编:循环!,loops,assembly,x86,nasm,Loops,Assembly,X86,Nasm,好的,长话短说,我正在学习汇编,我正在尝试做一个循环,打印出ascii字符“0”-“9”。 所以,我做了我在示例中看到的所有基础工作,比如用pushad和popad保存寄存器状态,分配堆栈空间,确保事情按照开始的方式进行。 所以我管理了这个小例子: ; ; Hello_World.asm ; (NASM Syntax, Windows) extern _printf section .text _main: pushad ; save re
;
; Hello_World.asm
; (NASM Syntax, Windows)
extern _printf
section .text
_main:
pushad ; save register states
push ebp ; save old stack
mov ebp, esp ; prepare new stack
sub esp, 1*4 ; allocate 4 bytes
mov byte [esp + 0], 48 ; add ascii '0' to stack
mov byte [esp + 1], 0 ; add ascii NULL terminator to stack
push esp; ; push the string in the stacks refrence
call _printf ; call printf()
add esp, 4 ; pop string refrence
add esp, 1*4 ; deallocate 4 bytes
mov esp, ebp ; close this stack
pop ebp ; restore old stack
popad ; restore register states
ret ; leave this function
这是可行的,它打印出“0”,但这有点安全。我试着给它加上一个循环,但是事情就这样破裂了。我读到“循环”操作码应该减少ECX寄存器,如果ECX>0,则返回标签参数,但是,我认为我还没有完全理解它
因此,我添加了几行,并得出以下结论:
;
; Hello_World.asm
;
extern _printf
global _main
section .text
_main:
pushad ; save register states
push ebp ; save old stack
mov ebp, esp ; prepare new stack
sub esp, 1*4 ; allocate 4 bytes
mov byte [esp + 0], 48 ; add ascii '0' to stack
mov byte [esp + 1], 0 ; add ascii NULL terminator to stack
mov ecx, 9 ; set loop counter to 9
aLoop:
inc byte [esp + 0] ; increment ascii character
push esp; ; push the string in the stacks refrence
call _printf ; call printf()
add esp, 4 ; pop string refrence
loop aLoop ; loop back to aLoop if ecx > 0
add esp, 1*4 ; deallocate 4 bytes
mov esp, ebp ; close this stack
pop ebp ; restore old stack
popad ; restore register states
ret ; leave this function
好吧,现在事情变得疯狂了。我在命令提示符下运行它,我通过耳机听到嘟嘟声,它在每一个ascii字符中循环,将它们全部打印出来。所以在飞行角色5秒钟后,我假设有东西溢出,然后它就崩溃了
我对汇编相当陌生(今天是我真正编程的第一天),我不知道出了什么问题。有人能解释一下我如何更好地实现循环吗
谢谢你!
-Jason好吧,你要描述的是,循环没有因为某种原因而终止。这意味着问题必须在这里:
add esp, 4 ; pop string refrence
loop aLoop ; loop back to aLoop if ecx > 0
这听起来像是调试器的工作:ecx到底发生了什么
我注意到您正在将ecx
设置为9
。然后将4
添加到esp
。您什么时候更改ecx?(是的,我知道它应该发生在循环
指令中,但如果它起作用,你就不会问了。ecx
到底发生了什么?)
顺便说一句,哔哔声很容易:当你在所有ASCII字符中循环时,你碰到的是ASCII 0x07,即BEL字符。子例程是否保留ECX的内容?如果不是,那可能是你的问题。试着在通话中保存它
aLoop:
inc byte [esp + 0] ; increment ascii character
push ecx; ; save ecx
push esp; ; push the string in the stacks refrence
call printf ; call printf()
add esp, 4 ; pop string refrence
pop ecx
loop aLoop ; loop back to aLoop if ecx > 0
调用方保存的寄存器为eax、ecx、edx。允许被调用的子例程修改这些寄存器。使用搜索引擎查找调用者保存的寄存器与被调用者保存的寄存器。应该会给您提供更多详细信息。是的,接下来我可能会找到一个好的调试器(有什么建议吗?)。但我的理解是“循环”指令应该减少ECX寄存器,然后进行比较,但这似乎没有发生。也许我最好切换到正常的比较和跳转…您已经在那里调用了_printf(顺便说一句,您确定调用顺序正确吗?),因此您可以尝试打印ecx的值。至于调试器,我不了解您的环境。你有nasm,所以我想可能是Windows;你有VisualStudio吗?经过快速检查,这就解决了它!现在,只要将ecx快速推/弹出到堆栈,并进行一些操作来维护我的字符串引用,它就可以完美地工作!非常感谢,这是一个我以后必须注意的问题。@Jason请将此答案标记为您的问题的解决方案。
loop
很慢。此外,在这种情况下,您应该避免使用push/pop。只需使用被调用方保存的ebx作为计数器。循环条件为:DEC EBX;JNZ aLoop
Ahh,但是您可以看到,通过在字符串引用之前按ecx,esp现在指向其他东西!虽然只需稍加操纵,但一切都能正常工作。谢谢你!我上一次使用x86汇编是在20年前,正是这样的事情提醒了我,我没有错过它。啊,谢谢你告诉我要保存哪些寄存器,这将在将来是一个很大的帮助。迈克·德西莫内,我觉得这样的小事很有趣,我这样做是为了迎接挑战,但是是的,这很乏味。哦,别误会,这很有趣。我上了一个学期的课,我们只做了8086(没有E_uuu_uuu寄存器),386刚出来,所以是的,近距离和远距离指针。但与此同时,为了好玩,我在Mac电脑上写了68000个汇编,我更喜欢寄存器文件,而不是过于专业化的寄存器,这需要大量数据移动才能实现更复杂的算法。