Урок 7

Расширенные арифметические операции с целыми числами

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

Сложение и вычитание

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

сложение/вычитание регистр-регистр;
сложение/вычитание память-регистр;
сложение/вычитание регистр-память;
сложение/вычитание регистр-непоср.значение;
сложение/вычитание память-непоср.значение.

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

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 ограни чивает возможность компьютера для выполнения арифметических операций. Рассмотрим два способа выполнения арифметических операций. Первый способ - более прост, но специфичен, второй - сложнее, но имеет общий характер. Сумма - шест.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, получим процедуру для вычитания.

Беззнаковые данные

Многие числовые поля не имеют знака, например, номер абонента, 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
(неправильно)

При операциях сложения и вычитания может одновременно возникнуть и переполнение, и перенос:

Беззнаковое Знаковое 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

Беззнаковое умножение: Команда MUL

Команда MUL (MULtiplication - умножение) умножает беззна ковые числа. На рис. 12.3 в процедуре C10MUL дано три примера умножения: байт на байт, слово на слово и слово на байт. Первый пример команды MUL умножает шест.80 (128) на шест.47 (64). Произведение -ш ест.2000 (8192) получается в регистре AX. Второй пример команды 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 в регистрах 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
+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. Умножение «двойного слова на двойное слово». Умножение двух двойных слов включает следующие четыре операции умножения:

Множимое Множитель
слово 2 х слово 2
слово 2 х слово 1
слово 1 х слово 2
слово 1 х слово 1

Каждое произведение в регистрах DX и AX складывается с соответствующим словом в окончательном результате. Пример такого умножения приведен в процедуре F10XMUL на рис. 12.4. Множимое 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 бита
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 дано четыре примера деления: слово на байт, байт на байт, двойное слово на слово и слово на слово. Первый пример команды 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.

Знаковое деление: Команда 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, то необходимо сделать два дополнения:

В метке C20 сравнивать AX и BX только при нулевом DX.

После команды 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). Сопроцессор 8087 действует совместно с 8088, а сопро цессор 80287 действует совместно с 80286. Каждый сопроцессор имеет собственный набор команд и средства для операций с плавающей запятой для выполнения экспоненциальных, логарифмических и тригонометрических функций. Сопроцессор содержит восемь 80-битовых регистров с плавающей запятой, которые могут представить числовые значения до 10 в 400 сте пени. Математические вычисления в сопроцессоре выполняются примерно в 100 раз быстрее, чем в основном процессоре. Основной процессор выполняет специальные операции и передает числовые данные в сопроцессор, который выполняет необходимые вычисления и возвращает результат. Для ассембли рования с помощью транслятора MASM, необходимо добавлять параметр /E или /R, например, MASM /R.