Увеличение от 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, напечатанные снова и снова.Как я уже сказал, это всего лишь немного надуманный пример, так что не беспокойтесь об этом слишком сильно, это не вопрос жизни и смерти.
(Форматирование немного испорчено, но ничего страшного).
Решение
Вы не можете доверять тому, что любая вызванная процедура делает с любым из регистров.Либо поместите регистры в стек и извлеките их обратно после вызова 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