Вопрос

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

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

Это было полезно?

Решение

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

Другие советы

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

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

Они документированы в ссылках ABI: http://math-atlas.sourceforge.net/devel/assembly/

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

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

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

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

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

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

Вы можете переписать его так, чтобы использовать регистры, которые не должны меняться, например %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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top