Приращение от 0 до 100 в языке ассемблера


7

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

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

+2

'xorl% EAX,% eax' полностью эквивалентно' MOVL $ 0,% eax' и занимает 3 байта меньше. Просто говорю. :) 10 мар. 122012-03-10 00:38:24

12

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

btw - pusha и popa будут вытолкнуть все регистры и вытащить их все. Я нашел это довольно удобным в прошлом 18 ноя. 082008-11-18 17:08:06

  0

Из примечаний ниже. Важно отметить, что ... «@seanyboy, ваше решение слишком велико. Все, что нужно, - это заменить eax другим регистром, например ecx». 13 авг. 092009-08-13 07:40:39

  0

@warren - 'pusha' и' popa' не поддерживаются в 64-битном режиме. 10 мар. 122012-03-10 00:37:57

  0

@ Даниэль Козар - никогда не работал в 64-битном режиме, но это хорошо знать :) 12 мар. 122012-03-12 13:52:36


6

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


1

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

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


3

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

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

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

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


-1

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

5

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

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