В четвертом примере команды DIV сначала выполняется расширение слова
WORD1 до двойного слова в регистре DX. После деления остаток шест.0000
получится в регистре DX, а частное шест.0002 - в регистре AX.
__________________________________________________________________________
page 60,132
TITLE EXDIV (COM) Пример операций DIV и IDIV
CODESG SEGMENT PARA 'Code'
ORG 100H
BEGIN: JMP SHORT MAIN
; ---------------------------------------------
BYTE1 DB 80H ;Data items
BYTE2 DB 16H
WORD1 DW 2000H
WORD2 DW 0010H
WORD3 DW 1000H
; ---------------------------------------------
MAIN PROC NEAR ;Основная процедура
CALL D10DIV ;Вызов подпрограммы DIV
CALL E10IDIV ;Вызов подпрограммы IDIV
MAIN ENDP
; Примеры с командой DIV:
; ---------------------------------------------
D10DIV PROC
MOV AX,WORD1 ;Слово / байт
DIV BYTE1 ; остаток:частное в AH:AL
MOV AL,BYTE1 ;Байт / байт
SUB AH,AH ; расширить делимое в AH
DIV BYTE3 ; остаток:частное в AH:AL
MOV DX,WORD2 ;Двойное слово / слово
MOV AX,WORD3 ; делимое в DX:AX
DIV WORD1 ; остаток:частное в DX:AX
MOV AX,WORD1 ;Слово / слово
SUB DX,DX ; расширить делимое в DX
DIV WORD3 ; остаток:частное в DX:AX
RET
D10DIV ENDP
; Примеры с командой IDIV:
; ---------------------------------------------
E10IDIV PROC
MOV AX,WORD1 ;Слово / байт
IDIV BYTE1 ; остаток:частное в AH:AL
MOV AL,BYTE1 ;Байт / байт
CBW ; расширить делимое в AH
IDIV BYTE3 ; остаток:частное в AH:AL
MOV DX,WORD2 ;Двойное слово / слово
MOV AX,WORD3 ; делимое в DX:AX
IDIV WORD1 ; остаток:частное в DX:AX
MOV AX,WORD1 ;Слово / слово
CWD ; расширить делимое в DX
IDIV WORD3 ; остаток:частное в DX:AX
RET
E10DIV ENDP
CODESG ENDS
END BEGIN
__________________________________________________________________________
Рис.12.5. Беззнаковое и знаковое деление.
Знаковое деление: Команда IDIV
--------------------------------
Команда IDIV (Integer DIVide) выполняет деление знаковых чисел. На
рис.12.5 в процедуре E10IDIV используются те же четыре примера деления,
что и в процедуре D10DIV, но вместо команд DIV записаны команды IDIV.
Первый пример команды IDIV делит шест.2000 (положительное число) на
шест.80 (отрицательное число). Остаток от деления - шест. 00 получается в
регистре AH , а частное - шест. C0 (-64) - в регистре AL. Команда DIV,
используя те же числа, генерирует частное +64.
Шестнадцатиричные результаты трех остальных примеров деления
приведены ниже:
Пример команды IDIV Остаток Частное
2 EE (-18) FB (-5)
3 1000 (4096) 0080 (128)
4 0000 0002
Только в примере 4 вырабатывается такой же результат, что и для команды
DIV. Таким образом, если делимое и делитель имеют одинаковый знаковый бит,
то команды DIV и IDIV генерируют одинаковый pезультат. Но, если делимое и
делитель имеют разные знаковые биты, то команда DIV генерирует
положительное частное, а команда IDIV - отрицательное частное. Можно
обнаружить это, используя отладчик DEBUG для трассировки этих примеров.
Повышение производительности. При делении на степень числа 2 (2, 4, и
т.д.) более эффективным является сдвиг вправо на требуемое число битов. В
следующих примерах предположим, что делимое находится в регистре AX:
Деление на 2: SHR AX,1
Деление на 8: MOV CL,3
SHR AX,CL
Переполнения и прерывания
---------------------------
Используя команды DIV и особенно IDIV, очень просто вызвать
пеpеполнение. Прерывания приводят (по крайней мара в системе, используемой
при тестировании этих программ) к непредсказуемым результатам. В операциях
деления предполагается, что частное значительно меньше, чем делимое.
Деление на ноль всегда вызывает прерывание. Но деление на 1 генерирует
частное, которое равно делимому, что может также легко вызвать прерывание.
Рекомендуется использовать следующее правило: если делитель - байт,
то его значение должно быть меньше, чем левый байт (AH) делителя: если
делитель - слово, то его значение должно быть меньше, чем левое слово (DX)
делителя. Проиллюстрируем данное правило для делителя, равного 1:
Операция деления: Делимое Делитель Частное
Слово на байт: 0123 01 (1)23
Двойное слово на слово: 0001 4026 0001 (1)4026
В обоих случаях частное превышает возможный размер. Для того чтобы
избежать подобных ситуаций, полезно вставлять перед командами DIV и IDIV
соответствующую проверку. В первом из следующих примеpов предположим, что
DIVBYTE - однобайтовый делитель, а делимое находится уже в регистре AX. Во
втором примере предположим, что DIVWORD - двухбайтовый делитель, а делимое
находится в регистровой паре DX:AX.
Слово на байт Двойное слово на байт
CMP AH,DIVBYTE CMP DX,DIVWORD
JNB переполнение JNB переполнение
DIV DIVBYTE DIV DIVWORD
Для команды IDIV данная логика должна учитывать тот факт, что либо
делимое, либо делитель могут быть отрицательными, а так как сравниваются
абсолютные значения, то необходимо использовать команду NEG для временного
перевода отрицательного значения в положительное.
Деление вычитанием
--------------------
Если частное слишком велико, то деление можно выполнить с помощью
циклического вычитания. Метод заключается в том, что делитель вычитается
из делимого и в этом же цикле частное увеличивается на 1. Вычитание
продолжается, пока делимое остается больше делителя. В cледующем примере,
делитель находится в регистре AX, а делимое - в BX, частное вырабатывается
в CX:
SUB CX,CX ;Очистка частного
C20: CMP AX,BX ;Если делимое < делителя,
JB C30 ; то выйти
SUB AX,BX ;Вычитание делителя из делимого
INC CX ;Инкремент частного
JMP C20 ;Повторить цикл
С30: RET ;Частное в CX, остаток в AX
В конце подпрограммы регистр CX будет содержать частное, а AX -
oстаток. Пример умышленно примитивен для демонстрации данной техники
деления. Если частное получается в регистровой паре DX:AX, то необходимо
сделать два дополнения:
1. В метке C20 сравнивать AX и BX только при нулевом DX.
2. После команды SUB вставить команду SBB DX,00.
П р и м е ч а н и е: очень большое частное и малый делитель могут
вызвать тысячи циклов.
ПРЕОБРАЗОВАНИЕ ЗНАКА
________________________________________________________________
Команда NEG обеспечивает преобразование знака двоичных чисел из
положительного в отрицательное и наоборот. Практически команда NEG
устанавливает противоположные значения битов и прибавляет 1. Примеры:
NEG AX
NEG BL
NEG BINAMT ;(байт или слово в памяти)
Преобразование знака для 35-битового (или большего) числа включает
больше шагов. Предположим, что регистровая пара DX:AX содержит 32-битовое
двоичное число. Так как команда NEG не может обрабатывать два регистра
одновременно, то ее использование приведет к неправильному результату. В
следующем примере показано использование команды NOT:
NOT DX ;Инвертирование битов
NOT AX ;Инвертирование битов
ADD AX,1 ;Прибавление 1 к AX
ADC DX,0 ;Прибавление переноса к DX
Остается одна незначительная проблема: над числами, представленными в
двоичном формате, удобно выполнять арифметические операции, если сами
числа определены в программе. Данные, вводимые в программу с дискового
файла, могут также иметь двоичный формат. Но данные, вводимые с
клавиатуры, представлены в ASCII-формате. Хотя ASCII-коды удобны для
отображения и печати, они требуют специальных преобразований в двоичный
формат для арифметических вычислений. Но это уже тема следующей главы.
ПРОЦЕССОРЫ INTEL 8087 И 80287 ДЛЯ ОБРАБОТКИ ЧИСЛОВЫХ ДАННЫХ
________________________________________________________________
Системная плата компьютера содержит пустое гнездо, зарезервированное
для числового процессора Intel 8087 (или 80287). Сопроцессор 8087
действует совместно с 8088, а сопроцессор 80287 действует совместно с
80286. Каждый сопроцессор имеет собственный набор команд и средства для
операций с плавающей запятой для выполнения экспоненциальных,
логарифмических и тригонометрических функций. Сопроцессор содержит восемь
80-битовых регистров с плавающей запятой, которые могут представить
числовые значения до 10 в 400 степени. Математические вычисления в
сопроцессоре выполняются примерно в 100 раз быстрее, чем в основном
процессоре.
Основной процессор выполняет специальные операции и передает числовые
данные в сопроцессор, который выполняет необходимые вычисления и
возвращает результат. Для ассемблирования с помощью транслятора MASM,
необходимо добавлять параметр /E или /R, например, MASM /R.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ
________________________________________________________________
- Будьте особенно внимательны при использовании однобайтовых
pегистров. Знаковые значения здесь могут быть от -128 до +127.
- Для многословного сложения используйте команду ADC для учета
переносов от предыдущих сложений. Если операция выполняется в цикле, то
используя команду CLC, установите флаг переноса в 0.
- Используйте команды MUL или DIV для беззнаковых данных и команды
IMUL или IDIV для знаковых.
- При делении будьте осторожны с переполнениями. Если нулевой
делитель возможен, то обеспечьте проверку этой операции. Кроме того,
делитель должен быть больше содержимого регистра AH (для байта) или DX
(для слова).
- Для умножения или деления на степень двойки используйте cдвиг.
Сдвиг вправо выполняется командой SHR для беззнаковых полей и командой SAR
для знаковых полей. Для сдвига влево используются идентичные команды SHL и
SAL.
- Будьте внимательны при ассемблировании по умолчанию. Например, если
поле FACTOR определено как байт (DB), то команда MUL FACTOR полагает
множимое в регистре AL, а команда DIV FACTOR полагает делимое в регистре
AX. Если FACTOR определен как слово (DW), то команда MUL FACTOR полагает
множимое в регистре AX, а команда DIV FACTOR полагает делимое в
регистровой паре DX:AX.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ
________________________________________________________________
Все вопросы имеют отношение к следующим данным:
DATAX DW 0148H
DW 2316H
DATAY DW 0237H
DW 4052H