2015-11-23 2 views
0

Я пытаюсь построить цикл, но это дает мне бесконечный цикл:NASM сборочного цикла

SECTION .data 

i: dd 0 
message: db "The number is %d",10,0 
SECTION .text 

extern printf 
global main 

main: 
    push ebp 
    mov ebp, esp 

    mov eax, DWORD [i] 
    mov ecx, DWORD 10 

    L1: 
     add eax, DWORD 1 
     push eax 
     push message 
     call printf 
     add esp, 8 
     loop L1 
    mov esp, ebp 
    pop ebp 

NASM дает мне выход как The number is 18 бесконечно. Но если я положил printf в конце кода. Он дает мне правильный результат

L1: 
    add eax, DWORD 1 
    loop L1 

push eax 
push message 
call printf 
add esp, 8 

mov esp, ebp 
pop ebp 

Кто-нибудь знает, где я ошиблась?

+1

_ECX_ (переменная цикла) необходимо сохранить (сохранить) перед вызовом _printf_. Соглашение о вызове C для 32-битного кода в Linux заключается в том, что _EAX_, _ECX_ и _EDX_ могут быть уничтожены вызовом функции. Если вам нужен какой-либо из этих регистров, чтобы быть тем, кем они были до вызова, вам нужно будет его сохранить, а затем восстановить. У вас такая же проблема с _EAX_ –

ответ

2

ecx - это переменная цикла. Обычно это номер вызывающего абонента - то есть функция, такая как printf, может перезаписать ее, а не восстанавливать старое значение. Поэтому при возврате с printf, ecx будет мусором.

Чтобы исправить это, вы можете добавить push ecx перед нажатием аргументов, а затем pop ecx после того, как add esp удаляет аргументы функции.

+0

_EAX_ также уничтожается. Кажется, они хотят печатать подсчет от 1 до 10. _EAX_ сбивается. –

+0

@MichaelPetch Для 32-битного [sans «быстрого вызова», который помещает первые два аргумента в регистры], ABI должен быть одним и тем же linux/BSD/winX, потому что printf перестает работать, если порядок push был отменен, что является только отклонением языка (например, IIRC, старый fortran _may_ сделали это или любой язык, у которого нет varargs _could_) –

+0

@SerenaQi: Или, лучше, используйте регистр с сохранением вызова, например 'ebx' для вашего счетчика циклов (с' dec/jnz' вместо медленного 'loop' insn). сохранить/восстановить ebx в начале/конце 'main', вместо сохранения/восстановления ecx внутри вашего цикла. 'printf' должен сохранять/восстанавливать' ebx', используете ли вы его или нет (если он хочет его использовать, что, вероятно, делает, поскольку это сложная функция). Обратитесь к http://www.stackoverflow.com/tags/x86/info x86 ABI для получения подробной информации о том, какие регистры являются (с сохранением или сохранением звонящего). –

Смежные вопросы