在汇编语言中从0增加到100


7

这有点古怪,但我今天正在与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打印一遍又一遍。就像我刚才说的,只是一个有点人为的例子,所以不要太担心它,这不是一个生死攸关的问题。

(格式有点搞砸了,但没什么大不了)。

+2

'xorl%eax中,%eax'是完全等效于'MOVL $ 0,%eax',并且需要3个字节以下。只是说。 :) 10 3月. 122012-03-10 00:38:24

12

你不能相信任何被调用的过程对任何寄存器的作用。 将寄存器压入堆栈并在调用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: 
+1

btw - pusha和popa会推送所有的寄存器,并将它们全部弹出。我发现过去非常方便 18 11月. 082008-11-18 17:08:06

  0

来自以下注释。需要注意的是......“@seanyboy,你的解决方案是过度杀伤性的,所需要的只是用ecx等其他寄存器代替eax。” 13 8月. 092009-08-13 07:40:39

  0

@warren - 在64位模式下不支持'pusha'和'popa'。 10 3月. 122012-03-10 00:37:57

  0

@Daniel Kozar - 从未在64位模式下工作,但这很好理解:) 12 3月. 122012-03-12 13:52:36


6

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


1

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

您需要对其他任何变量进行相同操作。使用寄存器来存储局部变量对于具有足够寄存器以支持它的体系结构来说非常有用(例如,EPIC,amd64等)


3

写得很好的函数通常会将所有寄存器压入堆栈,重新完成,以便在功能期间保持不变。该例外将是包含返回值的eax。像printf这样的库函数很可能是用这种方式编写的,所以我不会像Wedge所说的那样做:

您需要对其他任何变量执行相同的操作。利用寄存器存储本地变量是相当多保留架构具有足够的寄存器来支持它(如EPIC,AMD64等)

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

@seanyboy,你的解决方案是矫枉过正。所有需要的是用ecx等其他寄存器替换eax。


-1

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

5

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

这些记录在ABI出处:http://math-atlas.sourceforge.net/devel/assembly/