Увеличение от 0 до 100 на ассемблере

Это немного странно, но сегодня я возился с ассемблером 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 печатных снова и снова. Как я уже сказал, просто небольшой надуманный пример, так что не волнуйтесь об этом, это не проблема жизни или смерти.

(Форматирование немного запутано, но ничего особенного).

21.08.2008 06:20:08
xorl %eax, %eaxполностью эквивалентно movl $0, %eaxи занимает на 3 байта меньше. Просто говорю. :)
Daniel Kamil Kozar 10.03.2012 00:38:24
6 ОТВЕТОВ
РЕШЕНИЕ

Вы не можете доверять тому, что любая вызванная процедура делает с любым из регистров. Либо поместите регистры в стек и вытащите их обратно после вызова 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:
12
21.08.2008 07:47:00
Кстати, Пуша и Попа запихнут все регистры и вытолкнут их все ... Я нашел это очень удобным в прошлом
warren 18.11.2008 17:08:06
Из заметок ниже. Важно отметить, что ... "@seanyboy, ваше решение излишне. Все, что нужно, это заменить eax каким-либо другим регистром, например ecx."
seanyboy 13.08.2009 07:40:39
@warren - pushaи popaне поддерживаются в 64-битном режиме.
Daniel Kamil Kozar 10.03.2012 00:37:57
@Daniel Kozar - никогда не работал в 64-битном режиме, но это хорошо знать :)
warren 12.03.2012 13:52:36

Я не слишком знаком с _printf, но может ли быть так, что он модифицирует eax? Printf должен вернуть количество напечатанных символов, которое в данном случае равно двум: '0' и '\ n'. Я думаю, что он возвращает это в eax, и когда вы увеличиваете его, вы получаете 3, то есть то, что вы продолжаете печатать. Возможно, вам лучше использовать другой регистр для счетчика.

6
21.08.2008 06:36:20

Натан на правильном пути. Вы не можете предполагать, что значения регистра не будут изменены после вызова подпрограммы. На самом деле, лучше предположить, что они будут изменены, иначе подпрограмма не сможет выполнить свою работу (по крайней мере, для архитектур с малым количеством регистров, таких как x86). Если вы хотите сохранить значение, вы должны сохранить его в памяти (например, поместить его в стек и отследить его местоположение).

Вам нужно будет сделать то же самое для любой другой переменной, которая у вас есть. Использование регистров для хранения локальных переменных в значительной степени зарезервировано для архитектур с достаточным количеством регистров для их поддержки (например, EPIC, amd64 и т. Д.).

1
21.08.2008 08:00:47

Хорошо написанные функции обычно помещают все регистры в стек, а затем извлекают их, когда они сделаны, чтобы они оставались неизменными во время функции. Исключением будет eax, который содержит возвращаемое значение. Библиотечные функции, такие как printf, скорее всего написаны таким образом, поэтому я бы не стал делать то, что предлагает Клин:

Вам нужно будет сделать то же самое для любой другой переменной, которая у вас есть. Использование регистров для хранения локальных переменных в значительной степени зарезервировано для архитектур с достаточным количеством регистров для их поддержки (например, EPIC, amd64 и т. Д.).

На самом деле, насколько я знаю, компиляторы обычно компилируют функции таким образом, чтобы точно решить эту проблему.

@seanyboy, ваше решение излишне. Все, что нужно, это заменить eax другим регистром, например ecx.

3
21.08.2008 09:46:17

Вы можете переписать его, например, чтобы использовать регистры, которые не должны изменяться %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
-1
12.11.2008 01:16:45

Вы можете безопасно использовать регистры, которые «сохраняются вызываемыми» без необходимости сохранять их самостоятельно. На x86 это edi, esi и ebx; другие архитектуры имеют больше.

Они описаны в ссылках ABI: http://math-atlas.sourceforge.net/devel/assembly/

5
4.09.2008 02:32:11