сборка 8086 умножить 41 без использования MUL - PullRequest
0 голосов
/ 25 октября 2018

Я хотел бы знать, есть ли способ выполнить любое умножение или деление без использования инструкций MUL или DIV, потому что они требуют много циклов ЦП.Могу ли я использовать инструкции SHL или SHR для этой цели?Как я могу реализовать ассемблерный код?

Мне нужна помощь с конкретным числом - как я могу умножить bx на 41 только с 5 командами ???

всякий раз, когда я пытаюсь решить проблему, я получаю минимум 6 команд ...

мой код:

    mov ax,bx
    mov cx,bx
    shl bx,5    ;  *32
    shl ax,3    ;  *8
    add bx,ax   ; *40 
    add bx,cx   ; *41

Ответы [ 2 ]

0 голосов
/ 25 октября 2018

Какие процессоры вы настраиваете?Вы действительно имеете в виду 8086?Они по-прежнему существуют в виде микроконтроллеров, но подавляющее большинство кода x86 в наши дни работает на современных x86.

Современные процессоры x86 имеют очень быстрые множители, поэтому обычно стоит только использовать shift / add или LEA, когда вы можетеполучить работу за 2 моп или меньше.div / idiv все еще медленны, но умножение не в современных процессорах, которые бросают достаточное количество транзисторов в проблему.

imul eax, ebx, 41 имеет задержку 3 цикла, 1 на тактовую пропускную способность, вклсовременные процессоры Intel и Ryzen (https://agner.org/optimize/), и поддерживается в 286 и более поздних версиях. (16-разрядная форма imul ax, bx, 41 составляет 2 моп вместо 1, с задержкой в ​​4 цикла на ЦП семейства Sandybridge.)


Если вы можете использовать 32-битные режимы адресации (386 и более поздние), вы можете сделать это в 2 инструкциях LEA (таким образом, всего 2 мопа, задержка 2 цикла на современных процессорах).

Посмотрите, как gcc / clang компилирует эту функцию ( в проводнике компилятора Godbolt ):

int times41(int x) { return x*41; }

# compiled for 32-bit with gcc -O3 -m32 -mregparm=1
times41(int):  # first arg in EAX
    lea     edx, [eax+eax*4]      # edx = eax*5
    lea     eax, [eax+edx*8]      # eax = eax + edx*8 =  x + x*40
    ret

Это лучший выбор для старых процессоров, где imul или mul требуют больше мопов, и если задержка важнее, чем количество мопов на современных процессорах.

В вашем 16-битном коде вы можете использовать

    lea     eax, [ebx+ebx*4]     # ax = bx*5
    lea     ax, [ebx+eax*8]      # ax = bx + ax*8 =  x + x*40

Использование 32-битный размер операнда для первого LEA позволяет избежать ложной зависимости от старого значения EAX и избежать остановки частичного регистрав Nehalem и более ранних версиях (из 2-го LEA, считывающего EAX после записи AX).

Это стоит всего 1 дополнительный байт размера кода для префикса размера операнда (а также префикса размера адреса), ине имеет значения для правильности.(Младшие 16 битов результатов сдвига влево и сложения не зависят от старших битов ввода.)

Или, возможно, вы захотите xor eax,eax перед записью AX, позволяя процессорам Intel избежать частичнойзарегистрировать слияние для будущего использования AX.( Почему GCC не использует частичные регистры? ).

0 голосов
/ 25 октября 2018
; ax = x
mov bx, ax     ; bx = x
shl bx, 3      ; bx = 8 * x
add ax, bx     ; ax = 9 * x
shl bx, 2      ; bx = 32 * x
add ax, bx     ; ax = 41 * x
...