Сведения о вопросе

rjevskii

19:24, 1st August, 2020

Теги

gas   assembly    

Увеличение от 0 до 100 в языке assembly

Просмотров: 435   Ответов: 6

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

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



  Сведения об ответе

DO__IT

09:43, 26th August, 2020

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


  Сведения об ответе

VERSUION

16:28, 7th August, 2020

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


  Сведения об ответе

+-*/

17:48, 15th August, 2020

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

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


  Сведения об ответе

ASER

20:17, 17th August, 2020

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

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

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

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


  Сведения об ответе

ЯЯ__4

13:39, 16th August, 2020

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

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


  Сведения об ответе

lesha

18:23, 22nd August, 2020

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться