Умножение сборки 16-бит x 32-бит => 48-бит

Предположим, я хочу умножить большое число на другое (возможно, маленькое) число в ассемблере. Большое число (множимое) сохраняется в DX:AX, а множитель сохраняется в BX. Инструкция MUL работает только с AX. Так что же делать с DX?

Например, число 0001:0000H (65536), и я хочу умножить его на 2.

number     dw   0000h, 0001h
...
mov    ax, [number]
mov    dx, [number+2]
mov    bx, 2
mul    bx   ; it is ax*2 or 0000*2

Поэтому результат нулевой! Есть идеи по этому поводу?


person mahmood    schedule 28.10.2016    source источник
comment
ax содержит адрес 0000, а не значение. Вероятно, вы хотели сделать mov ax,[number] и mov dx,[number+2] (+2, потому что dw — это 2 байта).   -  person Ped7g    schedule 28.10.2016
comment
Переместите свой номер из DX:AX в EAX :)   -  person Jose Manuel Abarca Rodríguez    schedule 28.10.2016
comment
да, это тоже только 16bit x86? И вы хотите числа со знаком или без знака? (mul против imul)   -  person Ped7g    schedule 28.10.2016
comment
@Ped7g: Спасибо. Я исправил... Да, это 16-битный код. Так что EAX здесь недействителен   -  person mahmood    schedule 28.10.2016
comment
ax=0000h, поэтому 2*0 = 0. В чем проблема?   -  person Jose Manuel Abarca Rodríguez    schedule 28.10.2016
comment
Чтобы умножить на 2, вы используете сдвиги :) Чтобы выполнить общее умножение 32x16, вам понадобятся два 16-битных умножения, чтобы отдельно умножить младшее и старшее слово, а затем сложить соответствующие части вместе.   -  person Jester    schedule 28.10.2016
comment
Загрузка регистра dx не имеет никакого эффекта. 16-битное умножение использует только dx в качестве выходного регистра: старшие биты. Здесь результатом в dx:ax являются все нули.   -  person Gene    schedule 28.10.2016
comment
Ребята.. Это простой фрагмент для демонстрации проблемы. В 16-битной сборке как посчитать 71345*13? Множимое находится за пределами 16-битного регистра. Вы используете смену?   -  person mahmood    schedule 28.10.2016
comment
Вы не можете умножать 32-битные числа в 16-битной сборке 8086 с помощью одной инструкции. Вот что делает его 16-битным набором инструкций. Вам нужно более одного 16-битного умножения, а также некоторые сдвиги и сложения: (a+b*2^16) * (c + d*2^16) = ac + (bc + a d)*2^16 + (bd*2^32). Вы можете пропустить последний член, так как его значение больше 32 бит.   -  person Gene    schedule 28.10.2016
comment
@Gene: вы можете сделать это в одной инструкции с модулем x87 с плавающей запятой: P fild dword [a], fimul dword [b] / fistp qword [result] должны помочь, по крайней мере, если ваши входные данные представляют собой целые числа со знаком. 80-битный внутренний формат x87 может точно представлять каждое 64-битное целое число со знаком, поэтому этот результат должен быть точным. Я не проверял, что 8087 поддерживает 64-битные целочисленные хранилища, но определенно не требует x86-64. (gcc использует FILD/FISTP для 64-битной атомарной загрузки/сохранения в 32-битном режиме.)   -  person Peter Cordes    schedule 29.10.2016
comment
Ну ладно. Конечно. Инструкции x87 были необязательными (реализованы в отдельном чипе, который обычно не устанавливался) на исходных 16-битных машинах. ОП не удосужился описать окружающую среду.   -  person Gene    schedule 29.10.2016


Ответы (1)


Давайте представим, что это 286, так что у вас нет eax.

number dd 0x12345678      ; = dw 0x5678, 0x1234
result dw 0, 0, 0         ; 32b * 16b = 48b needed
    ...
    mov    ax,[number]    ; 0x5678
    mov    cx,[number+2]  ; 0x1234 ; cx, dx will be used later
    mov    bx,0x9ABC
    ; now you want unsigned 0x12345678 * 0x9ABC (= 0xB00DA73B020)
    mul    bx             ; dx:ax = 0x5678 * 0x9ABC
    ; ^ check instruction reference guide why "dx:ax"!
    xchg   cx,ax
    mov    di,dx          ; di:cx = intermediate result
    mul    bx             ; dx:ax = 0x1234 * 0x9ABC
    ; put the intermediate multiplication results together
    ; into one 48b number dx:di:cx
    add    di,ax
    adc    dx,0
    ; notice how I added the new result as *65536 to old result
    ; by using different 16bit registers

    ; store the result
    mov    [result],cx
    mov    [result+2],di
    mov    [result+4],dx

Это так же, как когда вы умножаете числа на бумаге, только вы не перемещаетесь на *10 компонентов, а используете природу размера регистра 16b для перемещения на *65536 (0x10000) компонентов, чтобы сделать это за меньшее количество шагов.

I.e.

  13
* 37
----
  91 (13 * 7)
 39_ (13 * 3, shifted left by *base (=10))
---- (summing the intermediate results, the 39 "shifted")
 481 (13 * 37)
person Ped7g    schedule 28.10.2016
comment
Иметь это значение 48b в виде трех слов в памяти, вероятно, окажется крайне непрактичным, добавление еще одного слова с нулевым значением, чтобы сделать его по крайней мере допустимым значением 64b, вероятно, было бы лучше, опять же, это зависит от того, что вы хотите сделать с этим значением в любом случае. - person Ped7g; 28.10.2016