Увеличение от 0 до 100 на ассемблере - PullRequest
7 голосов
/ 21 августа 2008

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

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

Ответы [ 6 ]

12 голосов
/ 21 августа 2008

Вы не можете доверять тому, что любая вызванная процедура делает с любым из регистров. Либо поместите регистры в стек и вытащите их обратно после вызова 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:
6 голосов
/ 21 августа 2008

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

5 голосов
/ 04 сентября 2008

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

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

3 голосов
/ 21 августа 2008

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

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

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

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

1 голос
/ 21 августа 2008

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

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

0 голосов
/ 22 августа 2008

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