Как я могу найти последний 0 в массиве в nasm?

#arrays #assembly #x86 #nasm

#массивы #сборка #x86 #nasm

Вопрос:

Я пишу код для поиска последнего 0 в массиве.

В принципе, мне нужно переместить новое значение в «начало» каждого массива, если в нем есть только нули, оно помещается в конец, и если оно находит другое значение, оно помещается в последний 0 (я рассматриваю свои массивы как стопки).

Пока что моя подпрограмма по большей части работает нормально, но иногда она переписывает значение, которое мне не нужно (вместо получения первого значения, отличного от 0, она принимает следующее). Вот код, который я использовал для получения «вершины» массива.

 TOP:
    xor ecx,ecx
    xor ebx,ebx
TOP_FOR:                    
    mov bx,word[eax ecx*2]   ;eax has the pointer of the array
    cmp ecx,n                ;n is the array's length
    je END_TOP
    inc ecx
    cmp bx,0
    je TOP_FOR
                    ;here i get the direction of the first value different
END_TOP:            ;from 0 but in my code i need the last 0, so
    dec ecx         ;i decrease ecx (result of this subrutine)
    ret
  

Например,
Если я помещу массив с 0,2, я ожидаю ecx = 0 , но с этим вводом фактически получу 1.

  • С массивом 1,2 я получаю 0 (это то, что я хочу)
  • с массивом 0,0 я получаю 1 (то, что я хочу, опять же)

Редактировать: попробовал запустить цикл на n-1, и это дало мне еще более странные результаты.

 TOP:
    xor ecx,ecx
    ;xor ebx,ebx
    mov ecx,n-1
TOP_FOR:
    ;mov bx,word[eax ecx*2]
    cmp word[eax ecx*2],0
    je FIN_TOPE
    dec ecx
    cmp ecx,0
    jne TOP_FOR

END_TOP:

    ret
  

Комментарии:

1. Ваш обновленный код не запускает тело цикла для i=0 , поэтому он никогда не просматривает первый элемент. Вот почему я использовал dec ecx / jge в своем ответе; он выходит из цикла только после того, как ECX становится -1 . Также обратите внимание, что dec устанавливает флаги, вам не нужен отдельный cmp с 0.

Ответ №1:

Ваша логика полностью обратная. Ваше cmp / je цикла завершает цикл, когда вы находите первое ненулевое значение. (И вы уже увеличили ECX после загрузки, но перед его проверкой).

Итак, после вашего цикла ECX = индекс элемента, следующего за первым ненулевым элементом.

У вас есть как минимум 2 варианта:

  • запомните последний видимый 0 в другом регистре и используйте его в конце цикла
  • выполните цикл в обратном порядке, начиная с ECX = n-1, и завершите цикл на первом нуле. (Или при dec ecx создании 0.)

Один из них, очевидно, более эффективен и проще, чем другой. 😛

Я оставляю на ваше усмотрение решение проблем с отклонением на 1, но, вероятно, вы захотите установить флажок ecx < n или ecx >= 0 в нижней части цикла, например, dec ecx / jge TOP_FOR . т.е. do{}while(--i) цикл.


Кроме того, обычно EBX является регистром, сохраняемым при вызове. Однако вам вообще не нужно его использовать. cmp word [eax ecx*2], 0 работает нормально.

Также в вашем текущем коде вы считываете 2 байта после конца массива. потенциально ошибка, если это было в конце страницы. (Однако вы его не используете, так что это не проблема корректности, кроме этого.) Вы используете ECX в качестве индекса, прежде чем проверять, не слишком ли он велик! Эта проблема исчезает, если вы просто используете память, немедленно cmp .

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