Учебный курс. Часть 11. Умножение и деление

Автор: xrnd | Рубрика: Учебный курс | 01-04-2010 | Распечатать запись Распечатать запись

Умножение и деление выполняются по-разному для чисел со знаком и без, поэтому в системе команд процессора x86 есть отдельные команды умножения и деления для чисел со знаком и для чисел без знака.

Умножение чисел без знака

Для умножения чисел без знака предназначена команда MUL. У этой команды только один операнд — второй множитель, который должен находиться в регистре или в памяти. Местоположение первого множителя и результата задаётся неявно и зависит от размера операнда:

Размер операнда Множитель Результат
Байт AL AX
Слово AX DX:AX

Отличие умножения от сложения и вычитания в том, что разрядность результата получается в 2 раза больше, чем разрядность сомножителей. Также и в десятичной системе — например, умножая двухзначное число на двухзначное, мы можем получить в результате максимум четырёхзначное. Запись «DX:AX» означает, что старшее слово результата будет находиться в DX, а младшее — в AX. Примеры:

    mul bl    ;AX = AL * BL
    mul ax    ;DX:AX = AX * AX

Если старшая часть результата равна нулю, то флаги CF и ОF будут иметь нулевое значение. В этом случае старшую часть результата можно отбросить. Это свойство можно использовать в программе, если результат должен быть такого же размера, как множители.

Умножение чисел со знаком

Для умножения чисел со знаком предназначена команда IMUL. Эта команда имеет три формы, различающиеся количеством операндов:

  • С одним операндом — форма, аналогичная команде MUL. В качестве операнда указывается множитель. Местоположение другого множителя и результата определяется по таблице.
  • С двумя операндами — указываются два множителя. Результат записывается на место первого множителя. Старшая часть результата в этом случае игнорируется. Кстати, эта форма команды не работает с операндами размером 1 байт.
  • С тремя операндами — указывается положение результата, первого и второго множителя. Второй множитель должен быть непосредственным значением. Результат имеет такой же размер, как первый множитель, старшая часть результата игнорируется. Это форма тоже не работает с однобайтными множителями.

Примеры:

    imul cl           ;AX = AL * CL
    imul si           ;DX:AX = AX * SI
    imul bx,ax        ;BX = BX * AX
    imul cx,-5        ;CX = CX * (-5)
    imul dx,bx,134h   ;DX = BX * 134h

CF = OF = 0, если произведение помещается в младшей половине результата, иначе CF = OF = 1. Для второй и третьей формы команды CF = OF = 1 означает, что произошло переполнение.

Деление чисел без знака

Деление целых двоичных чисел — это всегда деление с остатком! По аналогии с умножением, размер делителя, частного и остатка должен быть в 2 раза меньше размера делимого. Деление чисел без знака осуществляется с помощью команды DIV. У этой команды один операнд — делитель, который должен находиться в регистре или в памяти. Местоположение делимого, частного и остатка задаётся неявно и зависит от размера операнда:

Размер операнда
(делителя)
Делимое Частное Остаток
Байт AX AL AH
Слово DX:AX AX DX

При выполнении команды DIV может возникнуть прерывание (о прерываниях я подробно расскажу потом, пока старайтесь избегать таких случаев):

  • если делитель равен нулю;
  • если частное не помещается в отведённую под него разрядную сетку (например, если при делении слова на байт частное больше 255).

Примеры:

    div cl   ;AL = AX / CL, остаток в AH
    div di   ;AX = DX:AX / DI, остаток в DX

Деление чисел со знаком

Для деления чисел со знаком предназначена команда IDIV. Единственным операндом является делитель. Местоположение делимого и частного определяется также, как для команды DIV. Эта команда тоже генерирует прерывание при делении на ноль или слишком большом частном.

Пример программы

Допустим, в программе требуется вычислять координату какого-то движущегося объекта по формуле:

x = x0 + v0t + at2/2

Все числа в правой части — 8-битные целые без знака, а x — 16-битное целое и тоже без знака. Здесь нужно внимательно следить за размерами операндов.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use16               ;Генерировать 16-битный код
org 100h            ;Программа начинается с адреса 100h
 
    mov al,[v0]     ;AL = v0
    mov cl,[t]      ;CL = t
    mul cl          ;AX = AL*CL = v0*t
    mov bx,ax       ;BX = AX = v0*t
 
    mov al,[a]      ;AL = a
    mul cl          ;AX = AL*CL = a*t
    mov ch,0        ;Преобразуем t в слово в регистре CX
    mul cx          ;DX:AX = AX*CX = a*(t^2)
    mov cl,2        ;CL = 2 = CX, так как CH = 0
    div cx          ;AX = DX:AX/2 = a*(t^2)/2
 
    add ax,bx       ;AX = AX+BX = v0*t + a*(t^2)/2
    add al,[x0]     ;\
    adc ah,ch       ;/ AX = AX+x0 = x0 + v0*t + a*(t^2)/2
 
    mov [x],ax      ;Сохраняем результат в x
 
    mov ax,4C00h    ;\
    int 21h         ;/ Завершение программы
;-------------------------------------------------------
x0  db 188
v0  db 7
a   db 3
t   db 25
x   dw ?

В 7-й строке промежуточный результат сохраняется в bx. В 11-й строке происходит преобразование байта в слово, путём добавления нулевой старшей части. Такой метод подходит для чисел без знака, но приведёт к ошибке для чисел со знаком (в случае отрицательного числа). Прибавление x0 происходит в два этапа (строки 17 и 18) с учётом переноса, так как мы складываем слово и байт.

Упражнение

Напишите программу для вычисления формулы z = (x·y) / (x + y). Все числа 16-битные целые со знаком.

Сложное упражнение

Напишите программу для умножения двух 32-битных целых без знака. В результате должно получиться 64-битное целое без знака. Подсказка: используйте комбинацию умножения по частям и сложения, как в способе умножения столбиком. (Мой вариант решения вы можете посмотреть здесь).

Следующая часть »

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