汇编语言从0递增到100

时间:2020-03-05 18:41:24  来源:igfitidea点击:

这有点奇怪,但是我今天正在玩GNU汇编程序(我希望至少能够读懂语法),并试图让我的这个人为的小例子起作用。即我只想从0到100,一直打印数字。所以几分钟后,我想到了这个:

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    movl    
# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    movl    
You'll need to do the same for any other variable you have. Using registers to store local variables is pretty much reserved to architectures with enough registers to support it (e.g. EPIC, amd64, etc.)
, %eax # The starting point/current value. movl 0, %ebx # The ending point. _loop: # Remember your registers. pushl %eax pushl %ebx # Display the current value. pushl %eax pushl $string call _printf addl , %esp # reinstate registers. popl %ebx popl %eax # Check against the ending value. cmpl %eax, %ebx je _end # Increment the current value. incl %eax jmp _loop _end:
, %eax # The starting point/current value. movl 0, %ebx # The ending point. _loop: # Display the current value. pushl %eax pushl $string call _printf addl , %esp # Check against the ending value. cmpl %eax, %ebx je _end # Increment the current value. incl %eax jmp _loop _end:

我从中得到的就是一遍又一遍地打印3遍。就像我说的,只是一个人为的例子,所以不必太担心,这不是生死攸关的问题。

(格式有点混乱,但没什么大不了的)。

解决方案

回答

我对_printf不太熟悉,但是它可能会修改eax吗? Printf应该返回打印的字符数,在这种情况下为两个:'0'和'\ n'。我认为它以eax形式返回此值,当我们递增它时,得到3,这是我们继续打印的内容。
我们最好使用其他寄存器作为计数器。

回答

我们不能相信任何调用过程对任何寄存器的作用。
或者在调用printf之后将寄存器压入堆栈,然后弹出它们,或者将增量和端点值保存在内存中,然后根据需要将它们读/写到寄存器中。

希望以下作品。我假设pushl有一个等价的popl,并且我们可以将另外两个数字压入堆栈。

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    push    %ecx
    push    %ebp
    movl    ##代码##, %ecx # The starting point/current value.
    movl    0,       %ebp # The ending point.

_loop:
    # Display the current value.
    pushl   %ecx
    pushl   $string
    call     _printf
    addl     , %esp

    # Check against the ending value.
    cmpl    %ecx, %ebp
    je    _end

    # Increment the current value.
    incl    %ecx
    jmp _loop   

_end:
    pop     %ebp
    pop     %ecx

回答

内森(Nathan)走在正确的轨道上。我们不能假定调用子例程后寄存器的值将保持不变。实际上,最好假设它们将被修改,否则子例程将无法完成它的工作(至少对于x86等低寄存器数的体系结构而言)。如果要保留值,则应将其存储在内存中(例如,将其压入堆栈并跟踪其位置)。

我们需要对所有其他变量执行相同的操作。使用寄存器存储局部变量几乎是保留给具有足够寄存器来支持它的架构的(例如EPIC,amd64等)

回答

编写良好的函数通常会将所有寄存器压入堆栈,完成后将其弹出,以使它们在函数执行期间保持不变。例外是包含返回值的eax。像printf这样的库函数很可能是用这种方式编写的,所以我不会像Wedge所建议的那样做:

##代码##

实际上,据我所知,编译器通常以这种方式编译函数以准确解决此问题。

@seanyboy,解决方案过于矫kill过正。需要做的就是用其他一些寄存器(例如ecx)替换eax。

回答

我们可以重写它,以便使用不应该更改的寄存器,例如"%ebp"。只需确保在开始时将它们压入堆栈,然后在例程结束时将其弹出即可。

##代码##

回答

我们可以安全地使用"已保存被调用者"的寄存器,而不必自己保存。在x86上,这些是edi,esi和ebx。其他架构有更多。

这些都记录在ABI参考中:http://math-atlas.sourceforge.net/devel/assembly/