Бесконечный цикл в программе сборки MASM X86

В настоящее время я учусь на уроке компьютерной организации и архитектуры в своем университете, и мы много работали с MASM, и я играл с некоторыми программами из собственного интереса. Прямо сейчас я пишу программу, которая просит пользователя ввести 10 чисел в массив, а затем вычисляет наибольшее число в массиве. Однако моя проблема заключается в том, что если самое большое число не окажется самым последним введенным числом, эта программа выйдет из строя и сформируется бесконечный цикл. Я проверил содержимое ecx, и оказалось, что оно идет... 10 9 8 7 6 5 4 3 2 1 -1 -2 -3, так что просто пропускает 0. Я тут как-то потерялся, а не действительно уверен, что я делаю неправильно. Извините за мой стиль кодирования, так как я все еще очень новичок в этом, но просто пытаюсь понять. Спасибо!

INCLUDE Irvine32.inc

.data
    currentNum DWORD ?
    largestNum DWORD ?
    numbers DWORD 10 DUP(0)
    prompt BYTE "Enter a number:  ", 0
    message BYTE "The largest number is:  ", 0

.code
main PROC
    mov ecx, LENGTHOF numbers
    mov esi, OFFSET numbers
    mov eax, esi

    L1 :
        mov edx, OFFSET prompt
        call WriteString
        call ReadInt
        mov[esi], eax
        add esi, TYPE numbers
        loop L1

    mov ecx, LENGTHOF numbers
    mov esi, OFFSET numbers
    mov eax, [esi]
    mov largestNum, eax
    mov currentNum, eax

    L2:
        mov eax, [esi]
        mov currentNum, eax
        add esi, TYPE numbers
        cmp eax, largestNum
        ja setNewMax
        loop L2

    setNewMax:
        mov eax, currentNum
        mov largestNum, eax
        loop L2

    mov eax, largestNum
    mov edx, OFFSET message
    call WriteString
    call WriteDec
    call Crlf
    call Crlf

    exit

main ENDP


END main```

person Stephen Landaas    schedule 21.05.2020    source источник
comment
Итак, ваша первая инструкция loop L2 будет выполняться в ecx раз, верно? Как вы думаете, что произойдет, когда эта инструкция достигнет нуля? Поскольку он не возвращается к L2, он просто переходит к следующему оператору, который называется setNewMax. Вероятно, не то, что вы хотели. К вашему сведению, когда он достигает второй инструкции цикла, он снова уменьшает ecx, уменьшая значение до 0xffffffff. Это означает, что он будет работать в течение длительного времени.   -  person David Wohlferd    schedule 21.05.2020
comment
Нет никакой причины копировать текущий элемент массива в статическую/глобальную переменную currentNum. Просто используйте регистры; вот для чего они. Регистры похожи на локальные переменные в C (вы также можете использовать пространство стека, если у вас закончились регистры).   -  person Peter Cordes    schedule 21.05.2020
comment
Ваше тело цикла должно состоять всего из нескольких инструкций, например cmp eax, [esi] / jna поверх mov eax, [esi] для обновления максимума, затем приращение указателя + цикл.   -  person Peter Cordes    schedule 21.05.2020


Ответы (1)


Проблема заключается в двух инструкциях loop:

       ...
       ja    setNewMax
       loop  L2
setNewMax:
       mov   eax, currentNum
       mov   largestNum, eax
       loop  L2

Первый цикл (непосредственно перед тем, как метка setNewMax здесь уменьшает ecx, обнаруживает, что оно равно нулю, и не переходит. Следовательно, он просто продолжает выполнение следующей инструкции, т. е. mov eax, currentNum. Через три инструкции он встречает другую loop, он уменьшает ecx (которое теперь равно -1, а не нулю) и, следовательно, выполняет цикл.

Вы можете вставить прыжок из этого цикла следующим образом:

       ...
       ja    setNewMax
       loop  L2
       jmp   exitLoop
setNewMax:
       mov   eax, currentNum
       mov   largestNum, eax
       loop  L2
exitLoop:
       ...

В качестве альтернативы вы можете использовать простую инструкцию loop и написать вторую часть цикла следующим образом:

       ...
       jna   endOfloop
setNewMax:             ; <- this label is not necessary anymore
       mov   eax, currentNum
       mov   largestNum, eax
endOfloop:
       loop  L2

Возможно, этот код может быть немного проще для понимания, потому что теперь есть только одна инструкция loop, а инвертируя логику перехода ja, вы даже сохраняете инструкцию :).

person Tobias    schedule 21.05.2020
comment
Это не единственное возможное исправление. Другим очевидным способом исправить это было бы не выполнять оптимизацию хвостового дублирования в первую очередь, вместо этого выполнять jna над инструкциями mov. Но в общем случае это стоило бы дополнительной взятой ветки вместо невыбранной. (Однако OP, очевидно, не заботится о производительности, учитывая, как они продолжают копировать данные в именованные статические переменные вместо использования регистров и они вообще используют инструкцию loop.) - person Peter Cordes; 21.05.2020
comment
@PeterCordes Да, я знаю, что есть и другие решения, но в вопросах, которые (неявно) связаны с пониманием кода, я считаю важным придерживаться исходного кода как можно ближе. В конце концов, этот вопрос касается понимания поведения инструкции loop, а не оптимизации. - person Tobias; 21.05.2020
comment
Мой комментарий начался с придирки к вашему выбору формулировки: Вам придется подразумевает, что других решений нет. Но на самом деле проще IMO исправить это, превратив условное выражение в стандартный идиоматический if с переходом вперед, используя противоположное условие. Хотя не то чтобы в SO не хватает вопросов о том, как сделать if на ассемблере, и вряд ли кто-нибудь их когда-нибудь найдет. - person Peter Cordes; 21.05.2020
comment
@PeterCordes Точка принята. Я обновил свой ответ соответственно. - person Tobias; 21.05.2020