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