【问题标题】:Incrementing from 0 to 100 in assembly language汇编语言中从 0 递增到 100
【发布时间】:2010-09-06 08:56:34
【问题描述】:

这有点奇怪,但我今天正在研究 GNU 汇编器(我希望至少能够阅读语法),并试图让我这个人为的小例子工作。也就是说,我只想从 0 到 100,一直打印数字。所以几分钟后我想出了这个:

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

_main:
    movl    $0, %eax # The starting point/current value.
    movl    $100,   %ebx # The ending point.

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

    # Check against the ending value.
    cmpl    %eax, %ebx
    je    _end

    # Increment the current value.
    incl    %eax
    jmp _loop   

_end:

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

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

【问题讨论】:

  • xorl %eax, %eax 完全等价于movl $0, %eax,并且少了 3 个字节。只是说。 :)

标签: gnu-assembler assembly


【解决方案1】:

你不能相信任何被调用的过程对任何寄存器所做的事情。 要么将寄存器压入堆栈并在调用 printf 后将它们弹出,要么将增量和终点值保存在内存中并根据需要读取/写入寄存器。

我希望以下工作。我假设 pushl 有一个等效的 popl 并且您可以将额外的几个数字推入堆栈。

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

_main:
    movl    $0, %eax # The starting point/current value.
    movl    $100,       %ebx # The ending point.

_loop:
    # Remember your registers.
    pushl   %eax
    pushl   %ebx

    # Display the current value.
    pushl   %eax
    pushl   $string
    call     _printf
    addl     $8, %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:

【讨论】:

  • btw - pusha 和 popa 将推送所有寄存器,并将它们全部弹出。我发现过去很方便
  • 来自以下注释。重要的是要注意......“@seanyboy,你的解决方案是多余的。所需要的只是用其他一些寄存器替换 eax,比如 ecx。”
  • @warren - pushapopa 在 64 位模式下不受支持。
  • @Daniel Kozar - 从未在 64 位模式下工作过,但很高兴知道 :)
【解决方案2】:

我对_printf不太熟悉,但是会不会是它修改了eax? Printf 应该返回打印的字符数,在这种情况下是两个:'0' 和 '\n'。我认为它在 eax 中返回这个,当你增加它时,你会得到 3,这就是你继续打印的内容。 您最好为计数器使用不同的寄存器。

【讨论】:

    【解决方案3】:

    您可以安全地使用“被调用者保存”的寄存器,而无需自己保存它们。在 x86 上,它们是 edi、esi 和 ebx;其他架构有更多。

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

    【讨论】:

      【解决方案4】:

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

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

      事实上,据我所知,编译器通常以这种方式编译函数来准确处理这个问题。

      @seanyboy,您的解决方案太过分了。只需将 eax 替换为 ecx 等其他寄存器即可。

      【讨论】:

        【解决方案5】:

        内森走在正确的轨道上。您不能假设在调用子程序后寄存器值不会被修改。事实上,最好假设它们会被修改,否则子程序将无法完成它的工作(至少对于像 x86 这样的低寄存器计数架构)。如果你想保留一个值,你应该将它存储在内存中(例如,将它压入堆栈并跟踪它的位置)。

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

        【讨论】:

          【解决方案6】:

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

          # count.s: print the numbers from 0 to 100. 
              .text
          string: .asciz "%d\n"
              .globl _main
          
          _main:
              push    %ecx
              push    %ebp
              movl    $0, %ecx # The starting point/current value.
              movl    $100,       %ebp # The ending point.
          
          _loop:
              # Display the current value.
              pushl   %ecx
              pushl   $string
              call     _printf
              addl     $8, %esp
          
              # Check against the ending value.
              cmpl    %ecx, %ebp
              je    _end
          
              # Increment the current value.
              incl    %ecx
              jmp _loop   
          
          _end:
              pop     %ebp
              pop     %ecx
          

          【讨论】:

            猜你喜欢
            • 2022-01-08
            • 2018-07-31
            • 1970-01-01
            • 1970-01-01
            • 2010-12-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多