2008-08-21 57 views
7

這有點古怪,但我今天正在與GNU彙編人員討論(我希望能夠至少讀取語法),並試圖獲得這個小小的人爲的例子我的工作。即我只想從0到100,一直打印數字。所以幾分鐘後,我想出了這個:在彙編語言中從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個字節以下。只是說。 :) – 2012-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會推送所有的寄存器,並將它們全部彈出。我發現過去非常方便 – warren 2008-11-18 17:08:06

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