В процессорах Intel операции с плавающей запятой выполняет специальное устройство – арифметический сопроцессор, который начиная с процессора 80486 встраивается в основной процессор.
Сопроцессор работает параллельно с целочисленным процессором. Параллельная работа уменьшает время обработки, позволяя математическому сопроцессору производить математические вычисления, в то время как процессор продолжает выполнять другие функции.
Для программиста сопроцессор состоит из восьми 80-битных регистров, регистра управления, регистра состояния, регистра признаков и указателей особых случаев.
80-битные регистры используются для хранения констант и промежуточных результатов в процессе вычислений, уменьшая, таким образом, число обращений к памяти и повышая одновременно и скорость, и доступность магистрали. Регистровое пространство может быть использовано как стек, или как фиксированный набор регистров. При использовании в качестве стека в каждый конкретный момент доступны только два верхних элемента стека. При использовании в качестве набора регистров доступны одновременно все регистры.
Данные в регистрах хранятся во временном вещественном формате (см. ниже), который используется при всех вычислениях.
Поле ST в слове состояния определяет текущий верхний регистр стека. Операция загрузки уменьшает ST на единицу и загружает новую величину в верхний регистр стека. Операция извлечения и сохранения переписывает в нужное место величину верхнего регистра стека и увеличивает ST на единицу. Таким образом регистровый стек сопроцессора растет в направлении от старшего регистра к младшему.
Команды могут адресовать регистры прямо и косвенно. Команды, которые работают с вершиной стека, осуществляют косвенную адресацию регистра, на который указывает ST. Например, команда FSQRT замещает число в вершине стека его квадратным корнем; эта команда не получает операндов, так как в качестве операнда используется верхний регистр стека. Прямая адресация регистров зависит от вершины стека. Выражение ST определяет текущую вершину стека, а ST(i) ссылается на i-й регистр от ST. Например, если ST содержит двоичное 011 (регистр 3 находится в вершине стека), то команда FADD ST,ST(2) будет складывать регистры 3 и 5.
|
Слово состояния отражает все условия сопроцессора. Оно может быть сохранено в памяти по команде сопроцессора и затем проверено процессором. Слово состояния делится на несколько полей, как показано на рис. 1.
15 7 0
┌─┬──┬──────┬──┬──┬──┬──┬-─┬──┬──┬──┬──┬──┬──┐ Флаги ошибок
│B│C3│ ST │C2│C1│C0│ES│SF│PE│UE│OE│ZE│DE│IE│ (1 = ошибка)
└─┴──┴─┴──┴─┴──┴──┴──┴──┴─-┴──┴──┴──┴──┴──┴──┘
│ │ ───┬── ────┬─── │ │ │ │ │ │ │ └─ неверная операция
│ │ │ │ │ │ │ │ │ │ └ денормализованный операнд
│ │ │ │ │ │ │ │ │ └──────── деление на ноль
│ │ │ │ │ │ │ │ └─────────── переполнение
|
│ │ │ │ │ │ │ └────────────── потеря значимости
│ │ │ │ │ │ └───────────────── потеря точности
│ │ │ │ │ └──────────────────── стековая ошибка
│ │ │ │ └───────────────────── общий флаг ошибки
│ └────┼───────┴──────────────────────────── коды условий
│ └──────────────────────────── указатель вершины стека
└──────────────────────────────────────────── занято
Рис. 1. Формат слова состояния
Некоторые инструкции, например инструкции сравнения, передают свои результаты в качестве кодов условий (биты 14, 10-8). Основное назначение кодов условий – для условного ветвления. Это может быть реализовано, если сперва обработать команду, устанавливающую код условия, а затем проверить код условия с помощью команды процессора.
Биты с 13 по 11 слова состояния указывают на регистр сопроцессора, который является текущей вершиной стека. Бит 7 – это поле запроса на прерывание, а биты 5-0 служат для индикации того, что устройство обработки чисел сопроцессора обнаружило ошибку при обработке команды.
|
Слово управления сопроцессора (рис. 2) определяет его режим работы: точность вычислений, способ округления, реакцию на ошибки и др. Слово управления загружается из памяти специальной командой, кроме того, содержимое регистра управления устанавливается при инициализации сопроцессора и может быть оставлено по умолчанию.
15 7 0
┌─────┬──┬────┬────┬───┬─┬──┬──┬──┬──┬──┬──┐ Маски ошибок
│ │IC│ RC │ PC │IEM│ │PM│UM│OM│ZM│DM│IM│ (1 = маска установ-
└─┴─┴─┴──┴──┴─┴──┴─┴───┴─┴──┴──┴──┴──┴──┴──┘ лена)
──┬── │ ──┬─ ──┬─ │ │ │ │ │ │ │ └── неверная команда
│ │ │ │ │ │ │ │ │ │ └ денормализованный операнд
│ │ │ │ │ │ │ │ │ └──────── деление на ноль
│ │ │ │ │ │ │ │ └─────────── переполнение
│ │ │ │ │ │ │ └────────────── потеря значимости
│ │ │ │ │ │ └───────────────── точность
│ │ │ │ │ └─────────────────── зарезервировано
│ │ │ │ └───────────── маска разрешения прерываний
│ │ │ └──────────────────────── управление точностью
│ │ └─────────────────────────── управление округлением
│ └──────────────────────────── управление бесконечностью
└──────────────────────────────────────── зарезервировано
Рис. 2. Формат слова управления
Маска разрешения прерываний:
0 – прерывания разрешены,
1 – прерывания запрещены (маскированы).
Управление точностью:
00 – 24 бита,
01 – зарезервировано,
10 – 53 бита,
11 – 64 бита.
Управление округлением:
00 – округлять до ближайшего или четного,
10 – округлять вверх,
01 – округлять вниз,
11 – отбрасывать.
Управление бесконечностью:
0 – проективная,
1 – афинная.
Cлово признаков (тэгов) предназначено для пометки содержимого каждого регистра, как показано на рис. 3. Основная его функция – оптимизация работы сопроцессора под несколькими управляющими программами. Программист обычно не нуждается в обращении к этому слову.
15 7 0
┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
│TAG(7)│TAG(6)│TAG(5)│TAG(4)│TAG(3)│TAG(2)│TAG(1)│TAG(0)│
└──┴───┴──┴───┴──┴───┴──┴───┴───┴──┴──┴───┴──┴───┴──┴───┘
Рис. 3. Формат слова признаков
Величины признаков:
00 - конечное ненулевое число,
01 - истинный нуль,
10 - специальное число (не-число, бесконечность или денор-мализованное),
11 - пусто.
Указатели на ошибку (два 48-битных регистра) необходимы для написанных пользователем программ обработки ошибок. Когда сопроцессор выполняет команду, то устройство управления хранит в регистрах указателя на ошибку адреса команды и операнда. Подпрограмма обработки ошибок может сохранить эти указатели в памяти и определить, какая команда привела к возникновению условия ошибки.
Арифметический сопроцессор работает с несколькими типами числовых данных, разделяя их на три класса: двоичные целые (три типа), десятичные целые (один тип), вещественные числа (три типа). Форматы чисел приведены на рис. 4, их основные характеристики - в табл. 1.
Система команд сопроцессора описана в приложении 1.
Таблица 1.
Тип данных | Биты | Диапазон значений (десятичный) |
word_integer | -32768…+32767 | |
short_integer | -2Е+9…+2Е9 | |
long_integer | -9Е18…+9Е18 | |
bcd | -99..99…+99..99 | |
short_real | -8,43Е-37…+3,37E+38 | |
long_real | -4,19E-307…+1,67E+308 | |
temp_real | -3,4E-4932…+1,2E+4932 |
31 0 63 0
┌────────────────────┐ ┌────────────────────┐
│ дополнительный код │ │ дополнительный код │
└────────────────────┘ └────────────────────┘
а) короткое целое (4 байта) б) длинное целое (8 байтов)
79 78 72 71 0
┌───╥───┬─────┬───╥─────┬─────┬─────┬─────┬────┬────┐
│ S ║ 0 │... │ 0 ║ D17 │ D16 │ D15 │... │ D1 │ D0 │
└───╨───┴─────┴───╨─────┴─────┴─────┴─────┴────┴────┘
в) упакованное десятичное (10 байтов)
31 30 23 22 0 63 62 52 51 0
┌───┬─────────┬──────────┐ ┌───┬─────────┬────────────┐
│ S │ порядок │ мантисса │ │ S │ порядок │ мантисса │
└───┴─────────┴──────────┘ └───┴─────────┴────────────┘
г) одинарная точность (смещение д) двойная точность (смещение
порядка = 127, неявный бит порядка = 1023, неявный бит
мантиссы) мантиссы)
79 78 64 63 0
┌───┬───────────┬──────────────┐
│ S │ порядок │ мантисса │
└───┴───────────┴──────────────┘
е) расширенная точность (смещение порядка = 16383)
Рис. 4. Форматы данных
Короткие, длинные и временные вещественные типы данных соответствуют вещественным данным с одинарной, двойной и расширенной точностью.
Рассмотрим примеры разработки программ.
Задание. Составить программы вычисления функции z =exp(x) для следующих случаев: а) с использованием трансцендентных команд сопроцессора; б) путем разложения функции в ряд
exp(x) = 1 + x /1! + x 2/2! +... + x n/ n! +....
В случае (б) считать, что требуемая точность eps достигнута, если очередное слагаемое по модулю меньше eps.
;ВЫЧИСЛЕНИЕ ЭКСПОНЕНТЫС ПОМОЩЬЮ ФУНКЦИЙ СОПРОЦЕССОРА
cseg segment
assume cs:cseg,ds:cseg
x dd 1.2; аргумент функции
z dd?; результат вычисления функции
beg: mov ax,cs; настроить сегментные
mov ds,ax; регистры
finit; инициализировать
; сопроцессор
fld x; загрузить x
fldl2e; загрузить log2 e
fmulp st(1),st(0); вычислить y=x*log2 e
fld st(0); обpазовать копию y
frndint; окpуглить до целого y1
fsub st(1),st(0); выделить дpобную часть
; y2=y-y1
fxch; обменять y2 и y1
f2xm1; вычислить 2**(y2)-1
fld1; загрузить 1
faddp st(1),st; вычислить 2**(y2)
fscale; домножить на 2^y1
fstp st(1); удалить y1
fstp z; сохранить результат
mov ax,4c00h; вернуться в DOS
int 21h
Cseg ends
end beg
; ВЫЧИСЛЕНИЕ ЭКСПОНЕНТЫС ПОМОЩЬЮ РАЗЛОЖЕНИЯ В РЯД
; Алгоритм вычисления:
; 1. n=0; Delta=1; S=1
; 2. n=n+1; Delta=Delta*x/n; S=S+Delta
; 3. Если ABS(Delta)>eps идти к 2, иначе - закончить
; Распределение регистров:
; ST(0) - рабочий, ST(1) - S, ST(2) - Delta,
; ST(3) - n,; ST(4) - x, ST(5) - 1.0, ST(6) - eps
Cseg segment
assume cs:Cseg,ds:Cseg
x dq 1.0; аргумент функции
sum dq?; результат вычисления функции
eps dq 1.0E-05; точность вычисления
;
start: mov ax,cs; настройка сегмента данных на cs
mov ds,ax
finit; инициализация сопроцессора
; реализация первого шага алгоритма
Fld eps
fld1
fld x
fldz
fld1
fld1
fld1
; шаг 2
calc: fxch st(3);
fadd st(0),st(5); n=n+1
fst st(3)
fdivr st(0),st(2); Delta/n
fmul st(0),st(4); (Delta/n)*x
fst st(2)
fadd st(1),st(0); S=S+Delta
; шаг 3
fabs;, ABS(Delta)
; fcomi st(6); ABS(Delta)>eps
db 0dbh,0x6h; машинный код команды
ja calc; переход к шагу 2
fstp st
fstp sum; запоминание результата
finit
mov ax,4c00h; выход из программы
int 21h
Cseg ends
end start