Какова цель ДОБАВЛЕНИЯ BX, 2 в этот код?

#arrays #recursion #assembly #x86-16

#массивы #рекурсия #сборка #x86-16

Вопрос:

Я пытаюсь понять этот код рекурсии, но я запутался в том, какова цель add bx, 2 . Я поставил пометку в соответствующей строке. Как я понимаю, мы должны увеличить указатель, указывающий на массив со значениями, но почему мы добавляем 2, а не 1?

     .model small
    .stack 100
    .data
     arr dw 38, 39, 90, 94, 13, 24, 53, 59, 63
     size dw 9
     result dw ?
    .code
    func proc
     push bp
     mov bp, sp
     push ax
     push bx
     push cx
     push dx
     mov cx, [bp 4]
     mov bx, [bp 6]
     mov ax, [bx]
     cmp cx, 1
     ja more
     mov [bp 6], ax
     jmp done
    more:

     **add bx, 2**

     push bx
     dec cx
     push cx
     call func
     pop dx
     cmp dx, ax
     jg greater
     mov [bp 6], ax
     jmp done
    greater:
     mov [bp 6], dx
    done:
     pop dx
     pop cx
     pop bx
     pop ax
     pop bp
     ret 2
    func endp
  

Ответ №1:

Как я понимаю, мы должны увеличить указатель, указывающий на массив со значениями, но почему мы добавляем 2, а не 1?

Если я правильно понимаю, BX указывает на адрес элемента в массиве, содержащем 16-разрядные значения.

Инструкция mov ax, [bx] показывает вам, что элементы представляют собой 16-разрядные значения, а не 8- или 32-разрядные значения. 16-разрядное значение имеет длину 2 байта.

На большинстве процессоров (есть исключения, такие как TMS 320 или TMS 9900) разница между адресами двух элементов в массиве составляет, n если элемент имеет длину n в байтах.

Итак, если x это адрес элемента в массиве, содержащем 16-разрядные значения, а y это адрес следующего элемента, то y-x=2 .

Следовательно, два должны быть добавлены к BX , чтобы получить адрес следующего элемента.

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

1. Спасибо! теперь я понимаю, поскольку каждый элемент массива — это слово, а не байт!

Ответ №2:

Обратите внимание на push bx / … / call func впоследствии: это рекурсивная функция, и она переходит bx 2 к следующему вызову.

Я думаю, что BX используется как указатель, и есть arr , который представляет собой массив «слов» (2 байта), так что это почти наверняка увеличение указателя.

Это выглядит очень неэффективно; это только однократно рекурсивно, поэтому его можно очень легко записать в виде цикла. например do { something with *p ; } while(--cx); на C, то есть цикл с dec cx / jnz внизу.

Также ветвление довольно глупо: оно могло бы проверять условие завершения рекурсии намного раньше, прежде чем сохранять столько регистров. И это могло бы вывести jmp из обычного пути через функцию с помощью jna done . Возможно, потребуется перейти к специальному блоку в конце функции, поместив 2 перехода в путь особого случая, но это все равно лучше, чем иметь 2 перехода по основному пути.

Также сохранение / перезагрузка материала в [bp 6] является странным. Возвращает ли эта функция результат путем изменения одного из своих аргументов в стеке? Это выглядит так на основе pop dx сразу после вызова. Я надеюсь, что это намеренно запутано или написано в качестве отправной точки для оптимизации, потому что это выглядит очень сложным.

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