Ассемблер и программирование для IBM PC

Арифметические операции I: Обработка двоичных данных


------------------------------------------------------------

Арифметические операции I: Обработка двоичных данных

Цель: Дать сведения об операциях сложения, вычитания, умножения и деления двоичных данных.

ВВЕДЕНИЕ ------------------------------------------------------------

Несмотря на то, что мы привыкли к десятичной арифметике (база 10), компьютер работает только с двоичной арифметикой (база 2). Кроме того, ввиду ограничения, накладываемого 16-битовыми регистрами, большие величины требуют специальной обработки. Данная глава дает сведения об операциях сложения, вычитания, умножения и деления для беззнаковых и знаковых данных. В главе приводятся много примеров и предупреждений о различных ловушках для опрометчивых исследователей мира микропроцессора. В следующей главе будут раскрыты операции преобразования между двоичными данными и ASCII кодами.

СЛОЖЕНИЕ И ВЫЧИТАНИЕ ------------------------------------------------------------

Команды ADD и SUB выполняют сложение и вычитание байтов или слов, содержащих двоичные данные. Вычитание выполняется в компьютере по методу сложения с двоичным дополнением: для второго операнда устанавливаются обратные значения бит и прибавляется 1, а затем проиCXодит сложение с первым операндом. Во всем, кроме первого шага, операции сложения и вычитания идентичны. На рис. 12.1 представленны примеры команд ADD и SUB, обрабатывающие байты или слова. В процедуре B10ADD используется команда ADD для сложения байтов, а в процедуре C10SUB команда SUB вычитает слова. Примеры показывают все пять возможных ситуаций: сложение/вычитание регистр-регистр; сложение/вычитание память-регистр; сложение/вычитание регистр-память; сложение/вычитание регистр-непоср.значение; сложение/вычитание память-непоср.значение.

------------------------------------------------------------ ------------------------------------------------------------ Рис. 12.1 Примеры команд ADD и SUB.

Поскольку прямой операции память-память не существует, данная oперация выполняется через регистр. В следующем примере к содержимому слова WORDB прибавляется содержимое слова WORDA, описанных как DW:


Ассемблер для IBM PC. Глава 12 263
MOV AX,WORDA ADD AX,WORDB MOV WORDB,AX
Переполнения


Опасайтесь переполнений в арифметических операциях. Один байт содержит знаковый бит и семь бит данных, т.е. значения от -128 до +127. Результат арифметической операции может легко превзойти емкость однобайтового регистра. Например, результат сложения в регистре AL, превышающий его емкость, автоматически не переходит в регистр AH. Предположим, что регистр AL содержит шест.60, тогда результат команды
ADD AL,20H
генерирует в AL суумму - шест.80. Но операция также устанавливает флаг переполнения и знаковый флаг в состояние "отрицательно". Причина заключается в том, что шест.80 или двоичное 1000 0000 является отрицательным числом. Т.е. в результате, вместо +128, мы получим -128. Так как регистр AL слишком мал для такой операции и следует воспользоваться регистром AX. В следующем примере команда CBW (Convert Byte to Word - преобразовать байт в слово) преобразует шест.60 в регистре AL в шест.0060 в регистре AX, передавая при этом знаковый бит (0) через регистр AH. Команда ADD генерирует теперь в регистре AX правильный результат: шест.0080, или +128:
CBW ;Расширение AL до AX ADD AX,20H ;Прибавить к AX
Но полное слово имеет также ограничение: один знаковый бит и 15 бит данных, что соответствует значениям от -32768 до +32767. Рассмотрим далее как можно обрабатывать числа, превышающие эти пределы.
Многословное сложение
Максимальное возможное значение в регистре +32767 ограни чивает возможность компьютера для выполнения арифметических операций. Рассмотрим два способа выполнения арифметических операций. Первый способ - более прост, но специфичен, второй - сложнее, но имеет общий характер.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 12.2. Сложение двойных слов.
Ассемблер для IBM PC. Глава 12 264
На рис.12.2 процедура D10DWD демонстрирует простой способ сложения содержимого одной пары слов (WORD1A и WORD1B) с содержимым второй пары слов (WORD2A и WORD2B) и сохранения суммы в третьей паре слов (WORD3A и WORD3B). Сначала выполняется сложение правых слов:


WORD1B BC62 WORD2B 553A Сумма: 1119C
Сумма - шест.1119C превышает емкость регистра AX. Переполнение вызывает установку флага переноса в 1. Затем выполняется сложение левых слов, но в данном случае, вместо команды ADD используется команда сложения с переносом ADC (ADd with Carry). Эта команда складывает два значения, и если флаг CF уже установлен, то к сумме прибавляется 1:
WORD1A 0123 WORD2A 0012 Плюс перенос 1 Сумма: 0136
При использовании отладчика DEBUG для трассировки арифметических команд можно увидеть эту сумму 0136 в регистре AX, и обpатные значения 3601 в поле WORD3A и 9C11 в поле WORD3B. На рис.12.2 процедура E10DWD демонстрирует подход к сложению значений любой длины. Действие начинается со сложения самых правых слов складываемых полей. В первом цикле складываются правые cлова, во втором - слова, расположенные левее. При этом адреса в регистрах SI, DI и BX уменьшаются на 2. По две команда DEC выполняют эту операцию для каждого регистра. Применять команду
SUB reg,02
в данном случае нельзя, т.к. при этом будет очищен флаг переноса, что приведет к искажению результата сложения. Ввиду наличия цикла, используется только одна команда сложения ADC. Перед циклом команда CLC (CLear Carry - очистить флаг переноса) устанавливает нулевое значение флага переноса. Для работы данного метода необходимо: 1) обеспе чить смежность слов, 2) выполнять обработку справа налево и 3) загрузить в регистр CX число складываемых слов. Для многословного вычитания используется команда SBB (SuBtract with Borrow - вычитание с заемом) эквивалентная команде ADC. Заменив в процедуре E10DWD (рис.12.2) команду ADC на SBB, получим процедуру для вычитания.
БЕЗЗНАКОВЫЕ И ЗНАКОВЫЕ ДАННЫЕ ------------------------------------------------------------
Ассемблер для IBM PC. Глава 12 265
Многие числовые поля не имеют знака, например, номер абонента, aдрес памяти. Некоторые числовые поля предлагаются всегда положительные, например, норма выплаты, день недели, значение числа ПИ. Другие числовые поля являются знаковые, так как их содержимое может быть положительным или отрицательным. Например, долговой баланс покупателя, который может быть отрицательным при переплатах, или алгебраическое число. Для беззнаковых величин все биты являются битами данных и вместо ограничения +32767 регистр может содержать числа до +65535. Для знаковых величин левый байт является знаковым битом. Команды ADD и SUB не делают разницы между знаковыми и беззнаковыми величинами, они просто складывают и вычитают биты. В следующем примере сложения двух двоичных чисел, первое число содержит единичный левый бит. Для беззнакового числа биты представляют положительное число 249, для знакового - отрицательное число -7:


Беззнаковое Знаковое 11111001 249 -7 00000010 2 +2 11111011 251 -5
Двоичное представление результата сложения одинаково для беззнакового и знакового числа. Однако, биты представляют +251 для беззнакового числа и -5 для знакового. Таким одразом, числовое содержимое поля может интерпретироваться по разному. Состояние "перенос" возникает в том случае, когда имеется пеpенос в знаковый разряд. Состояние "переполнение" возника ет в том случае, когда перенос в знаковый разряд не создает переноса из разрядной сетки или перенос из разрядной сетки проиCXодит без переноса в знаковый разряд. При возникновении переноса при сложении беззнаковых чисел, результат получает ся неправильный:
Беззнаковое Знаковое CF OF 11111100 252 -4 00000101 5 +5 00000001 1 1 1 0 (неправильно)
При возникновении переполнения при сложении знаковых чисел, результат получается неправильный:
Беззнаковое Знаковое CF OF 01111001 121 +121 00001011 11 +11 10000100 132 -124 0 1 (неправильно)
При операциях сложения и вычитания может одновременно возникнуть и переполнение, и перенос:
Ассемблер для IBM PC. Глава 12 266
Беззнаковое Знаковое CF OF 11110110 246 -10 10001001 137 -119 01111111 127 +127 1 1 (неправильно) (неправильно)
УМНОЖЕНИЕ ------------------------------------------------------------
Операция умножения для беззнаковых данных выполняется командой MUL, а для знаковых - IMUL (Integer MULtiplication - умножение целых чисел). Ответственность за контроль над форматом обрабатываемых чисел и за выбор подходящей команды умножения лежит на самом программисте. Существуют две основные операции умножения:
"Байт на байт". Множимое находится в регистре AL, а множи тель в байте памяти или в однобайтовом регистре. После умножения произведение находится в регистре AX. Операция игнорирует и стиpает любые данные, которые находились в регистре AH.
| AH | AL | | AX | До умножения:| |Множимое| После:|Произведение|
"Слово на слово". Множимое находится в регистре AX, а мно житель - в слове памяти или в регистре. После умножения произведение находится в двойном слове, для которого требуется два регистра: старшая (левая) часть произведения находится в регистре DX, а младшая (правая) часть в регистре AX. Операция игнорирует и стирает любые данные, которые находились в регистре DX.


| AX | | DX || AX | До умножения:|Множимое| После: |Ст.часть||Мл.часть| | Произведение |
В единственном операнде команд MUL и IMUL указывается множитель. Рассмотрим следующую команду:
MUL MULTR
Если поле MULTR определено как байт (DB), то операция предполагает умножение содержимого AL на значение байта из поля MULTR. Если поле MULTR определено как слово (DW), то опереция предполагает умножение содержимого AX на значение слова из поля MULTR. Если множитель находится в регистре, то длина регистра определяет тип операции, как это показанно ниже:
MUL CL ;Байт-множитель: множимое в AL, произвед. в AX MUL BX ;Слово-множитель:множимое в AX, произв.в DX:AX
Ассемблер для IBM PC. Глава 12 267
Беззнаковое умножение: Команда MUL
Команда MUL (MULtiplication - умножение) умножает беззна ковые числа. На рис. 12.3 в процедуре C10MUL дано три примера умножения: байт на байт, слово на слово и слово на байт. Первый пример команды MUL умножает шест.80 (128) на шест.47 (64). Произведение -ш ест.2000 (8192) получается в регистре AX.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 12.3. Беззнаковое и знаковое умножение.
Второй пример команды MUL генерирует шест. 10000000 в регистpах DX:AX. Третий пример команды MUL выполняет умножение слова на байт и требует расширение байта BYTE1 до размеров слова. Так как предполагаются беззнаковые величины, то в примере левый бит регистра AH равен нулю. (При использовании команды CBW значение левого бита регистpа AL может быть 0 или 1). Произведение - шест. 00400000 получается в регистрах DX:AX.
Знаковое умножение: Команда IMUL
Команда IMUL (Integer MULtiplication - умножение целых чисел) умножает знаковые числа. На рис. 12.3 в процедуре D10IMUL используются те же три примера умножения, что и в процедуре C10MUL, но вместо команд MUL записаны команды IMUL. Первый пример команды IMUL умножает шест.80 (отрицатель ное число) на шест.40 (положительное число). Произведение - шест.E000 получается в регистре AX. Используя те же данные, команда MUL дает в результате шест.2000, так что можно видеть разницу в использовании команд MUL и IMUL. Команда MUL рассматривает шест.80 как +128, а команда IMUL - как -128. В результате умножения -128 на +64 получается -8192 или шест.E000. (Попробуйте преобразовать шест.Е000 в десятичный формат). Второй пример команды IMUL умножает шест.8000 (отрицатель ное значение) на шест.2000 (положительное значение). Произведение - шест.F0000000 получается в регистрах DX:AX и представляет собой oтрицательное значение. Третий пример команды IMUL перед умножением выполняет расширение байта BYTE1 до размеров слова в регистре AX. Так как значения предполагаются знаковые, то в примере используется команда CBW для перевода левого знакового бита в регистр AH: шест.80 в pегистре AL превращается в шест.FF80 в регистре AX. Поскольку множитель в слове WORD1 имеет также отрицательное значение, то произведение должно получится положительное. В самом деле: шест.00400000 в


Ассемблер для IBM PC. Глава 12 268
регистрах DX:AX - такой же результат, как и в случае умножения командой MUL, которая предполагала положительные сомножители. Таким образом, если множимое и множитель имеет одинаковый знаковый бит, то команды MUL и IMUL генерируют одинаковый результат. Но, если сомножители имеют разные знаковые биты, то команда MUL вырабатывает положительный результат умножения, а команда IMUL - отрицательный. Можно обнаружить это, используя отладчик DEBUG для трассировки примеров.
Повышение эффективности умножения: При умножении на степень числа 2 (2,4,8 и т.д.) более эффективным является сдвиг влево на требуемое число битов. Сдвиг более чем на 1 требует загрузки величины сдвига в регистр CL. В следующих примерах предположим, что множимое находится в регистре AL или AX:
Умножение на 2: SHL AL,1 Умножение на 8: MOV CL,3 SHL AX,CL
Многословное умножение
Обычно умножение имеет два типа: "байт на байт" и "слово на слово". Как уже было показано, максимальное знаковое значение в слове ограничено величиной +32767. Умножение больших чисел требует выполнения некоторых дополнительных действий. Рассматриваемый подход предполагает умножение каждого слова отдельно и сложение полученных результатов. Рассмотрим следующее умножение в десятичном формате:
1365 х12 2730 1365 16380
Представим, что десятичная арифметика может умножать только двухзначные числа. Тогда можно умножить 13 и 65 на 12 раздельно, cледующим образом:
13 65 х12 х12 26 130 13 65 156 780
Следующим шагом сложим полученные произведения, но поскольку число 13 представляло сотни, то первое произведение в действительности будет 15600:
15600
Ассемблер для IBM PC. Глава 12 269
+780 16380
Ассемблерная программа использует аналогичную технику за исключением того, что данные имеют размерность слов (четыре цифры) в шестнадцатеричном формате.
Умножение двойного слова на слово. Процедура E10XMUL на рис.12.4 умножает двойное слово на слово. Множимое, MULTCND, состоит из двух слов, содержащих соответственно шест. 3206 и шест. 2521. Определение данных в виде двух слов (DW) вместо двойного слова (DD) обусловлено необходимостью правильной адресации для команд MOV, пересылающих слова в регистр AX. Множитель MULTPLR содержит шест. 6400. Область для записи произведения, PRODUCT, состоит из трех слов. Первая команда MUL перемножает MULTPLR и правое cлово поля MULTCND; произведение - шест. 0E80 E400 записывается в PRODUCT+2 и PRODUCT+4. Вторая команда MUL перемножает MULTPLR и левое слово поля MULTCND, получая в результате шест. 138A 5800. Далее выполняется сложение двух произведений следующим образом:


Произведение1: 0000 0E80 E400 Произведение 2: 138A 5800 Результат: 138A 6680 E400
Так как первая команда ADD может выработать перенос, то второе cложение выполняется командой сложения с переносом ADC (ADd with Carry). В силу обратного представления байтов в словах в процессоpах 8086/8088, область PRODUCT в действи тельности будет содержать значение 8A13 8066 00E4. Программа предполагает, что первое слово в области PRODUCT имеет начальное значение 0000.
------------------------------------------------------------ ------------------------------------------------------------ Рис.12.4. Многословное умножение.
Умножение "двойного слова на двойное слово". Умножение двух двойных слов включает следующие четыре операции умножения:
Множимое Множитель
слово 2 х слово 2 слово 2 х слово 1 слово 1 х слово 2 слово 1 х слово 1
Каждое произведение в регистрах DX и AX складывается с соответствующим словом в окончательном результате. Пример такого умножения приведен в процедуре F10XMUL на рис. 12.4.
Ассемблер для IBM PC. Глава 12 270
Множимое MULTCND содержит шест. 3206 2521, множитель MULTPLR - шест. 6400 0A26. Результат заносится в область PRODUCT, состоящую из четырех слов. Хотя логика умножения двойных слов аналогична умножению двойного слова на слово, имеется одна особенность, после пары команд сложения ADD/ADC используется еще одна команда ADC, которая прибавляет 0 к значению в поле PRODUCT. Это необходимо потому, что первая команда ADC сама может вызвыть перенос, который последующие команды могут стереть. Поэтому вторая команда ADC прибавит 0, если переноса нет, и прибавит 1, если перенос есть. Финальная пара команд ADD/ADC не тредует дополнительной команды ADC, так как область PRODUCT достаточно велика для генерации окончательного результата и переноса на последнем этапе не будет. Окончательный результат 138A 687C 8E5C CCE6 получится в поле PRODUCT в обратной записи байт в словах. Выполните трассировку этого примера с помощью отладчика DEBUG.
СДВИГ РЕГИСТРОВОЙ ПАРЫ DX:AX ------------------------------------------------------------


Следующая подпрограмма может быть полезна для сдвига содержимого pегистровой пары DX:AX вправо или влево. Можно придумать более эффективный метод, но данный пример представляет общий подход для любого числа циклов (и, соответственно, сдвигов) в регистре CX. Заметьте, что сдвиг единичного бита за разрядную сетку устанавливает флаг переноса.
Сдвиг влево на 4 бита MOV CX,04 ;Инициализация на 4 цикла C20: SHL DX,1 ;Сдвинуть DX на 1 бит влево SHL AX,1 ;Сдвинуть AX на 1 бит влево ADC DX,00 ;Прибавить значение переноса LOOP C20 ;Повторить Сдвиг вправо на 4 бита MOV CX,04 ;Инициализация на 4 цикла D20: SHR AX,1 ;Сдвинуть AX на 1 бит вправо SHR DX,1 ;Сдвинуть DX на 1 бит вправо JNC D30 ;Если есть перенос, OR AH,10000000B ; то вставить 1 в AH D30: LOOP D20 ;Повторить
Ниже приведен более эффективный способ для сдвига влево, не требующий организации цикла. В этом примере фактор сдвига записывается в регистр CL. Пример написан для сдвига на 4 бита, но может быть адаптирован для других величин сдвигов:
MOV CL,04 ;Установить фактор сдвига SHL DX,CL ;Сдвинуть DX влево на 4 бита MOV BL,AH ;Сохранить AH в BL SHL AX,CL ;Сдвинуть AX влево на 4 бита SHL BL,CL ;Сдвинуть BL вправо на 4 бита
Ассемблер для IBM PC. Глава 12 271
OR DL,BL ;Записать 4 бита из BL в DL
ДЕЛЕНИЕ ------------------------------------------------------------
Операция деления для беззнаковых данных выполняется командой DIV, a для знаковых - IDIV. Ответственность за подбор подходящей команды лежит на программисте. Существуют две основные операции деления:
Деление "слова на байт". Делимое находится в регистре AX, а делитель - в байте памяти или а однобайтовом регистре. После деления остаток получается в регистре AH, а частное - в AL. Так как однобайтовое частное очень мало (максимально +255 (шест.FF) для беззнакового деления и +127 (шест.7F) для знакового), то данная операция имеет ограниченное использование.
| AX | | AH | AL | До деления: | Делимое| После: |Остаток|Частное|
Деление "двойного слова на слово". Делимое находится в регистровой паре DX:AX, а делитель - в слове памяти или а регистре. После деления остаток получается в регистре DX, а частное в регистре AX. Частное в одном слове допускает максимальное значение +32767 (шест.FFFF) для беззнакового деления и +16383 (шест.7FFF) для знакового.


| DX || AX | | AH || AL | До деления:|Ст.часть||Мл.часть| После:|Остаток||Частное| | Делимое |
В единственном операнде команд DIV и IDIV указывается делитель. Рассмотрим следующую команду:
DIV DIVISOR
Если поле DIVISOR определено как байт (DB), то операция предполагает деление слова на байт. Если поле DIVISOR определено как слово (DW), то операция предполагает деление двойного слова на слово. При делении, например, 13 на 3, получается разельтат 4 1/3. Частное есть 4, а остаток - 1. Заметим, что ручной калькулятор (или программа на языке BASIC) выдает в этом случае результат 4,333.... Значение содержит целую часть (4) и дробную часть (,333). Значение 1/3 и 333... есть дробные части, в то время как 1 есть остаток от деления.
Беззнаковое деление: Команда DIV
Команда DIV делит беззнаковые числа. На рис.12.5 в процедуре D10DIV дано четыре примера деления: слово на байт, байт на байт, двойное слово на слово и слово на слово.
Ассемблер для IBM PC. Глава 12 272
Первый пример команды DIV делит шест.2000 (8092) на шест.80 (128). В результате остаток 00 получается в регистре AH, а частное шест.40 (64) - в регистре AL. Второй пример команды DIV выполняет прежде расширение байта BYTE1 до размеров слова. Так как здесь предполагается беззнаковая величина, то в примере левый бит регистра AH равен нулю. В результате деления остаток - шест. 12 получает ся в регистре AH, а частное шест.05 - в регистре AL. Третий пример команды DIV генерирует остаток шест. 1000 в регистре DX и частное шест. 0080 в регистре AX. В четвертом примере команды DIV сначала выполняется расширение слова WORD1 до двойного слова в регистре DX. После деления остаток шест.0000 получится в регистре DX, а частное шест. 0002 - в регистре AX.
------------------------------------------------------------ ------------------------------------------------------------ Рис.15.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
Ассемблер для IBM PC. Глава 12 273
Деление на 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:
Ассемблер для IBM PC. Глава 12 274
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-коды удобны для отображения и печати, они требуют специальных преобразований в двоичный формат для арифметических вычислений. Но это уже тема следующей главы.
Ассемблер для IBM PC. Глава 12 275
ПРОЦЕССОРЫ 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.
Ассемблер для IBM PC. Глава 12 276
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------
Все вопросы имеют отношение к следующим данным:
DATAX DW 0148H DW 2316H DATAY DW 0237H DW 4052H
12.1. Закодируйте команды для сложения а) слова DATAX со словом DATAY; б) двойного слова, начинающегося по адресу DATAX, с двойным словом в DATAY.
12.2. Объясните действие следующих команд:
STC MOV BX,DATAX ADC BX,DATAY
12.3. Закодируйте команды для умножения (MUL): а) слова DATAX на слово DATAY; б) двойного слова , начинающего ся по адресу DATAX, на слово DATAY.
12.4. Какой делитель, кроме нуля, вызывает ошибку переполнения?
12.5. Закодируйте команды для деления (DIV): а) слова DATAX на 23; б) двойного слова, начинающегося по адресу DATAX, на слово DATAY.
12.6. Последний пример в разделе "Сдвиг регистроврй пары DX:AX" является более эффективным по сравнению с предыдущими примерами для сдвига влево на четыре бита. Измените пример для сдвига вправо на четыре бита.
Ассемблер для IBM PC. Глава 13 1


    Содержание раздела