2015-04-29 133 views
4

我在彙編中編寫了一個while循環以便在帶有nasm和gcc的Linux終端中編譯。該程序比較x和y,直到y> = x並報告最後的循環數。下面的代碼:NASM程序集while循環計數器

segment .data 

out1 db "It took ", 10, 0 
out2 db "iterations to complete loop. That seems like a lot.", 10, 0 
x  db 10 
y  db 2 
count db 0 

segment .bss 

segment .text 

global main 
extern printf 

main: 
    mov eax, x 
    mov ebx, y 
    mov ecx, count 
    jmp lp   ;jump to loop lp 

lp: 
    cmp ebx, eax ;compare x and y 
    jge end  ;jump to end if y >= x 
    inc eax  ;add 1 to x 
    inc ebx  ;add 2 to y 
    inc ebx 
    inc ecx  ;add 1 to count 
    jp  lp   ;repeat loop 

end: 

    push out1  ;print message part 1 
    call printf 

    push count  ;print count 
    call printf 

    push out2  ;print message part 2 
    call printf 

    ;mov edx, out1    ; 
    ;call print_string   ; 
            ; 
    ;mov edx, ecx    ;these were other attempts to print 
    ;call print_int    ;using an included file 
            ; 
    ;mov edx, out2    ; 
    ;call print_string   ; 

這是編譯和與終端運行:

nasm -f elf test.asm 
gcc -o test test.o 
./test 

端子輸出散發出來的:

It took 
iterations to complete loop. That seems like a lot. 
Segmentation fault (core dumped) 

我看不出什麼毛病邏輯。我認爲它是語法的,但我們剛剛開始學習彙編,並且嘗試了各種不同的語法,如變量中的括號以及在段的末尾使用ret,但似乎沒有任何效果。我也搜索了分段錯誤,但我還沒有發現任何真正有用的東西。任何幫助將不勝感激,因爲我是一個絕對的初學者。

回答

6

它崩潰的原因可能是您的main函數沒有ret指令。此外,一定要設置eax 0信號成功:

xor  eax, eax ; or `mov eax, 0` if you're more comfortable with that 
ret 

此外,全局變量指定的指針,而不是價值。 mov eax, xeax設置爲x的地址。如果你想要發生任何事情(或者不使用全局變量),你需要回寫它。

最後,你調用printf與單一非字符串參數:

push count  ;print count 
call printf 

的第一個參數必須是一個格式字符串,如"%i"。這裏,count是一個指向空字節的指針,所以你不會得到任何東西。把我的頭,你應該試試這個:

out3 db "%i ", 0 

; snip 

push ecx 
push out3 
call printf 
+0

非常棒的幫助,謝謝,輸出現在'花了134520932次迭代......很多。你能解釋一下在這種情況下寫回eax嗎?使用'mov eax,[x]'沒有像我想的那樣幫忙。另外,我在main的末尾添加了'mov eax,0'和'ret',但仍然出現分段錯誤,有什麼想法?另外,你會介意解釋'mov eax,0'和'ret'的用途嗎? – vroom

+0

就這樣,沒有。考慮使用'gdb'來找出崩潰的位置。 (另外,我剛剛注意到你正在用'jp'跳回去,你確定你不是指'jmp'嗎?) – zneak

+0

對於'mov eax,0'和ret',這是因爲否則,沒有指示告訴你的程序結束。執行將繼續執行零字節(意思就像'add al,0'或者其他),直到它到達分配內存的末尾,然後崩潰。當一個函數返回時,它的結果應該在'eax'中,所以我們基本上在這裏說「return 0」,如果你有任何C的概念。 – zneak

0

我認爲你的問題可能只是您引用您的常量的地址,而不是其內在價值。人們必須將nasm中的標籤看作指針而不是價值。要訪問它,你只需要使用[label]

segment .data 
    x  dw 42 
segment .text 
    global main 
    extern printf 
main: 
    mov eax, x 
    push eax 
    call printf ; will print address of x (like doing cout<<&x in C++) 
    mov eax, [x] 
    push eax 
    call printf ; will print 42 
    sub esp, 8 
    xor eax, eax 
    ret 

PS:我不認爲任何人提到它,但是調用外部代碼,因爲在編譯的(C或C++或其他)時,易失寄存器的修改非常頻繁你使用的函數被「翻譯」爲程序集,然後與你的asm文件鏈接。 PC不是人類,因此它不區分高級或低級寫入的內容,處理器只是讀取存儲在寄存器和內存中的操作碼和操作數,因此爲什麼使用低級語言時的外部功能( call printf)將要修改(或不是!總是取決於編譯器和體系結構)寄存器,你也在使用它。 爲了解決這個問題有不同的解決方案:

  1. 您檢查寄存器沒有被使用gcc your_c_file.c -S然後將該文件your_c_file.s將是你的編譯器已經從C文件產生的預先準備的彙編代碼修改的內容。 (它很難弄清楚什麼是什麼,如果你打算用這個方法檢查Name Mangling,看看func的名字將如何改變。)

  2. 將你想要保存的所有寄存器推入堆棧,然後在調用後將它們彈回到它們的寄存器中,記住LIFO方法。

  3. 使用指令PUSHAPOPA分別推送或彈出所有寄存器。

這是NASM手冊3這也解釋了語言的基礎使用方法:http://www.csie.ntu.edu.tw/~comp03/nasm/nasmdoc3.html

希望你設法解決這個問題。