汇编语言中从 0 递增到 100

发布于 2024-07-04 09:13:26 字数 709 浏览 11 评论 0原文

这有点奇怪,但我今天正在研究 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。 就像我说的,这只是一个有点做作的例子,所以不要太担心,这不是生死攸关的问题。

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

This is kinda oddball, but I was poking around with the GNU assembler today (I want to be able to at least read the syntax), and was trying to get this little contrived example of mine to work. Namely I just want to go from 0 to 100, printing out numbers all the while. So a few minutes later I come up with this:

# 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:

All I get from this is 3 printed over and over again. Like I said, just a little contrived example, so don't worry too much about it, it's not a life or death problem.

(The formatting's a little messed up, but nothing major).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

猛虎独行 2024-07-11 09:13:26

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

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

Nathan is on the right track. You can't assume that register values will be unmodified after calling a subroutine. In fact, it's best to assume they will be modified, else the subroutine wouldn't be able to do it's work (at least for low register count architectures like x86). If you want to preserve a value you should store it in memory (e.g. push it onto the stack and keep track of it's location).

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.)

像你 2024-07-11 09:13:26

您可以重写它,以便使用不会更改的寄存器,例如 %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

You could rewrite it so that you use registers that aren't suppose to change, for example %ebp. Just make sure you push them onto the stack at the beginning, and pop them off at the end of your routine.

# 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
半暖夏伤 2024-07-11 09:13:26

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

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

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

@seanyboy,你的解决方案太过分了。 所需要做的就是用其他寄存器(如 ecx)替换 eax。

Well written functions will usually push all the registers onto the stack and then pop them when they're done so that they remain unchanged during the function. The exception would be eax that contains the return value. Library functions like printf are most likely written this way, so I wouldn't do as Wedge suggests:

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.)

In fact, from what I know, compilers usually compile functions that way to deal exactly with this issue.

@seanyboy, your solution is overkill. All that's needed is to replace eax with some other register like ecx.

我也只是我 2024-07-11 09:13:26

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

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

You can safely use registers that are "callee-saved" without having to save them yourself. On x86 these are edi, esi, and ebx; other architectures have more.

These are documented in the ABI references: http://math-atlas.sourceforge.net/devel/assembly/

明天过后 2024-07-11 09:13:26

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

I'm not too familiar with _printf, but could it be that it modifies eax? Printf should return the number of chars printed, which in this case is two: '0' and '\n'. I think it returns this in eax, and when you increment it, you get 3, which is what you proceed to print.
You might be better off using a different register for the counter.

内心荒芜 2024-07-11 09:13:26

您不能相信任何被调用的过程对任何寄存器所做的事情。
要么将寄存器压入堆栈,然后在调用 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:

You can't trust what any called procedure does to any of the registers.
Either push the registers onto the stack and pop them back off after calling printf or have the increment and end point values held in memory and read/written into registers as you need them.

I hope the following works. I'm assuming that pushl has an equivalant popl and you can push an extra couple of numbers onto the stack.

# 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:
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文