Практичне заняття № 3
з дисципліни «Системне програмування»
Команди порівняння чисел.
У центральному процесорі команди умовних переходів виконуються відповідно до значень окремих бітів регістра прапорців процесора. У арифметичному співпроцесорі існують спеціальні команди порівнянь, за наслідками виконання яких, встановлюються біти кодів умов в регістрі стану:
FCOM - Порівняння
FICOM - Цілочисельне порівняння
FCOMP - Порівняння і вилучення зі стеку ST(0)
FICOMP - Цілочисельне порівняння і вилучення зі стеку
FCOMPP - Порівняння і подвійне вилучення зі стеку (ST(0), ST(1))
FTST - Порівняння операнда з нулем
FXAM - Аналіз операнда на тип числа (скінчене число, денормалізоване число, нуль, безкінечність,... )
Команда FCOM віднімає вміст операнда, розміщеного в оперативній пам’яті, від значення у вершині стеку ST(0). Результат віднімання нікуди не записується і покажчик вершини стеку ST не змінюється.
Позначимо операнд команди порівняння як "x". У наступній таблиці приведемо значення бітів кодів умови після виконання команди "FCOM x":
C3 = 0, C0 = 0
ST(0)> x

C3 = 0, C0 = 1
ST(0)< x

C3 = 1, C0 = 0
ST(0)= x

C3 = 1, C0 = 1
ST(0) і x непорівнювані

Остання комбінація виникає при спробі порівняння нечисел, невизначеностей або нескінченості, а також в деяких інших випадках.
Команда FICOM в якості операндів передбачає 16- або 32-розрядні числа, а в решті - аналогічна команді FCOM.
Команди FCOMP і FICOMP аналогічні, відповідно, командам FCOM і FICOM, за винятком того, що після виконання операнд вилучається зі стеку.
Команда FCOMPP виконує ті ж дії, що і FCOM, але вона після виконання вилучає зі стеку обидва операнди, що брали участь в порівнянні.
Команда FTST призначена для порівняння операнду з нулем. Після її виконання коди умов встановлюються згідно з таблицею:
C3 = 0, C0 = 0
ST(0)> 0

C3 = 0, C0 = 1
ST(0)< 0

C3 = 1, C0 = 0
ST(0)= 0

C3 = 1, C0 = 1
ST(0) і 0 непорівнювані

Команда FXAM аналізує вміст ST(0). Після її виконання встановлюються коди умов, згідно яких можна визначити знак числа, його скінченність або нескінченність, нормалізованість і т.д.
Біт C1 містить знак аналізованого числа: 0 -додатний, 1 – від’ємний.
За допомогою біта C0 можна визначити, є число скінченим або нескінченим: 0 - скінчене число, 1 - нескінчене.
Для скінчених чисел подальша класифікація може проводитися за вмістом кодів умов C2 і C3:
C3 = 0, C0 = 0
Ненормалізоване число

C3 = 0, C0 = 1
Нормалізоване число

C3 = 1, C0 = 0
Нульове число

C3 = 1, C0 = 1
Число денормалізоване


Аналогічно, для нескінчених чисел коди умов C2 і C3 мають наступні значення:
C3 = 0, C0 = 0
Нечисло

C3 = 0, C0 = 1
Нескінчене число

C3 = 1, C0 = 0
Порожнє число

C3 = 1, C0 = 1
Порожнє число


За допомогою команди "FSTSW AX" програма може переписати вміст регістра стану співпроцесора в регістр AX центрального процесора. Далі вміст регістра AH можна переписати в регістр прапорів центрального процесора за допомогою команди SAHF. Біти кодів умов співпроцесора відображаються (проектуються) на регістр прапорів центрального процесора так, щоб без додаткових дій, використовувати команди умовних переходів.
Наприклад, в наступному фрагменті програми виконується перехід до мітки error, якщо операнди непорівнювані:
.286
. . .
fcom
fstsw ах
sahf
je error
Трансцендентні команди.
Трансцендентні команди призначені для обчислення наступних функцій:
тригонометричні (sin, cos, tg...)
зворотні тригонометричні (arcsin, arccos...)
показникові (xy , 2x , 10x , ex )
гіперболічні (sh, ch, th...)
зворотні гіперболічні (arsh, arch, arcth...)
Ось список всіх трансцендентних команд математичного співпроцесора:
FPTAN Обчислення часткового тангенса
FPATAN Обчислення часткового арктангенса
FYL2X Обчислення y*log2(x)
FYL2XP1 Обчислення y*log2(x+1)
F2XM1 Обчислення 2x-1
FCOS Обчислення cos(x)
FSIN Обчислення sin(x)
FSINCOS Обчислення sin(x) і cos(x) одночасно
Команда FPTAN обчислює частковий тангенс ST(0), розміщуючи в стеку такі два числа x та у, що y/x = tg(ST(0)).
Після виконання команди число у розташовується в ST(0), а число x заноситься у вершину стеку (тобто записується в ST(1)). Аргумент команди FPTAN винен знаходиться в межах:
0 <= ST(0) <= pi/4
Користуючись знайденим значенням часткового тангенса, можна обчислити інші тригонометричні функції за наступними формулами:
sin(z)= 2*(y/x)/ (1 + (y/x)2)
cos(z)= (1 - (y/x) 2) / (1 + (y/x)2)
tg(z/2)= y/x;
ctg(z/2)= x/y;
cosec(z)= (1 + (y/x)2) / 2*(y/x)
sec(z)= (1 + (y/x)2) / (1 - (y/x) 2)
Де z - значення, що знаходилося в ST(0) до виконання команди FPTAN, x і у - значення в регістрах ST(0) і ST(1), відповідно.
Команда FPATAN обчислює частковий арктангенс:
z=arctg(ST(0) /ST(1))=arctg(x/y).
Перед виконанням команди числа x і у розташовуються в ST(0) і ST(1), відповідно. Аргументи команди FPATAN винен знаходиться в межах: 0 < у < x . Результат записується в ST(0).
Команда FYL2X обчислює вираз y*log2 (x), операнди x і у розміщуються, відповідно, в ST(0) і ST(1). Операнди вилучаються зі стеку, а результат записується в стек. Параметр x повинен бути додатнім числом.
Користуючись результатом виконання цієї команди, можна обчислити таким чином логарифмічні функції:
Логарифм за основою два: log2 (x)= FYL2(x)
Натуральний логарифм:
logе (x)= logе (2)* log2 (x)= FYL2X(logе (2), x)= FYL2X(FLDLN2, x)
Десятковий логарифм:
log10 (x)= log10 (2)* log2 (x)= FYL2X (log10 (2), x) = FYL2X(FLDLG2, x)
Функція FYL2XP1 обчислює вираз y*log2 (x+1), де x відповідає ST(0), а у - ST(1). Результат записується в ST(0), обидва операнди вилучаються зі стеку і втрачаються. На операнд x накладається обмеження: 0 < x < 1 - 1/sqrt(2)
Команда F2XM1 обчислює вираз 2x-1, де x - ST(0). Результат записується в ST(0), параметр повинен знаходиться в наступних межах: 0 <= x <= 0,5
Команда FCOS обчислює cos(x). Параметр x винен знаходиться в ST(0), туди ж записується результат виконання команди.
Команда FSIN аналогічна команді FCOS, але обчислює значення синуса ST(0).
Команда FSINCOS обчислює одночасно значення синуса і косинуса параметра ST(0). Значення синуса записується в ST(1), косинуса - в ST(0).
Команди керування.
Команди, керування, призначені для роботи з нечисловими регістрами співпроцесора. Деякі команди мають альтернативні варіанти. Мнемоніки цих команд можуть починатися з FN або F. Перший варіант відповідає командам "Без очікування". Для таких команд процесор не перевіряє, чи зайнятий співпроцесор виконанням команди, тобто біт зайнятості B не перевіряється. Особливі випадки також ігноруються.
Варіанти команд "З очікуванням" діють так само, як і звичайні команди співпроцесора.
Ось список команд керування для співпроцесора:
FNSTCW (FSTCW) Записати управляюче слово (записує вміст регістра управління в оперативну пам’ять).
FLDCW Завантажити управляюче слово (завантажує регістр управління з оперативної пам’яті і, як правило, використовується для зміни режиму роботи співпроцесора).
FNSTSW (FSTSW) Записати слово стану (записує вміст регістра стану в оперативну пам’ять).
FNSTSW AX (FSTSW AX) Записати слово стану в AX (записує вміст регістра стану в регістр AX центрального процесора, де можливий аналіз вмісту за допомогою команд умовних переходів).
FNCLEX (FCLEX) Скинути особливі випадки (скидає прапорці особливих випадків в регістрі стану співпроцесора, також скидаються біти ES і B).
FNINIT (FINIT) Ініціалізувати співпроцесор (ініціалізує регістр стану, регістр управління, і регістр тегів таким чином:
Регістр управління - Проектна нескінченість, округлення до найближчого, розширена точність, всі особливі випадки замасковані
Регістр стану - B=0 (біт зайнятості скинутий), код умови не визначений, ST=ES=0, прапорці особливих випадків встановлені в нуль
Регістр тегів - Всі поля регістра тегів містять значення 11 (порожній регістр)).
FNSTENV (FSTENV) Записати оточення (записує в пам’ять вміст всіх регістрів, окрім числових, у визначеному форматі. Команда корисна при обробці особливих випадків).
FLDENV Завантажити оточення (завантажує регістри, збережені командою FNSTENV).
FNSAVE (FSAVE) Записати повний стан (діє аналогічно команді FNSTENV, але додатково зберігає вміст числових регістрів).
FRSTOR Відновити повний стан (діє аналогічно команді FLDENV, але додатково відновлює вміст числових регістрів).
FINCSTP Збільшити покажчик стека SP на 1
FDECSTP Зменшити покажчик стека SP на 1
FFREE Звільнити регістр (визначає числовий регістр ST, вказаний як операнд, як порожній, записуючи у відповідне поле регістра тегів значення 11).
FNOP Пуста команда, немає операції (не робить жодних дій).
FSETPM Встановлює захищений режим роботи (переводить співпроцесор в захищений режим роботи).
Програмування співпроцесора.
При роботі в різних середовищах програмування, заснованих на мовах високого рівня (наприклад, Сі або Паскаль), під час створення проекту, як правило, надається можливість вибору одного з трьох варіантів стандартної бібліотеки:
бібліотека емулятора (втратила актуальність для процесорів сімейства Pentium, але частково стала знов актуальною після появи MMX);
бібліотека, що використовує арифметичний співпроцесор;
бібліотека альтернативної математики.
Перший варіант (бібліотека емулятора) використовується за замовчанням. Програми, які використовують бібліотеку емуляції, працюватимуть як за наявності в системі співпроцесора, так і за його відсутності. У останньому випадку обчислення з плаваючою крапкою виконуються спеціальними підпрограмами, які приєднуються до основної програми на етапі лінкування (що, зрозуміло, приводить до збільшення розмірів програми). Крім того, в код програми включається функція, що визначає наявність співпроцесора, яка при запуску програми визначить факт наявності (або відсутності) співпроцесора і вибере відповідний спосіб виконання обчислень - або з використанням співпроцесора, або з використанням підпрограм емуляції співпроцесора.
Другий варіант бібліотеки розрахований на наявність співпроцесора. Якщо співпроцесора немає, програма працювати не буде. Але якщо відомо, що співпроцесор є (наприклад, процесор Pentium завжди містить блок арифметики), то є сенс використовувати саме цей варіант як найбільш швидкодіючий (іноді, наприклад, при одночасному використанні інструкцій MMX і співпроцесора, щоб не уповільнювати роботу програми, для математичних обчислень буває вигідніше використовувати бібліотеку альтернативної математики).
Третій варіант не використовує співпроцесор зовсім (навіть якщо він присутній в системі). Всі обчислення виконуються спеціальними підпрограмами, що входять до складу бібліотеки альтернативної математики і підключаються до користувацької програми автоматично на етапі лінкування.
На жаль, є програми, в яких використання бібліотеки емуляції неможливе або вкрай ускладнене:
резидентні програми;
драйвери;
програми, що мають жорсткі вимоги до точності і швидкості обчислень
У випадку з резидентними програмами неможливість використання бібліотеки емулятора викликана тим, що після залишення програми резидентною в пам’яті, наприклад, функцією _dos_keep, вона втрачає доступ до модулів емуляції. Механізм виклику програм емуляції заснований на використанні переривань з номерами 34h - 3Eh. Перш ніж залишити програму резидентною, функція _dos_keep відновлює вміст вказаних векторів переривань, роблячи неможливим доступ резидентній програмі до модулів емулятора. Та і самих цих модулів вже немає в пам’яті - на їх місце може бути завантажена нова програма. Тому в документації по програмуванні на мові Сі рекомендується для резидентних програм застосовувати бібліотеку альтернативної математики. Але ця бібліотека, не використовує співпроцесор.
Ситуація з драйверами аналогічна - драйвери, як правило, пишуться на мові асемблер, тому засоби емуляції бібліотек Сі недоступні. Виходом може бути безпосереднє програмування співпроцесора на мові асемблера. При цьому можна повністю використовувати всі можливості співпроцесора і досягти найбільшої ефективності обчислень.
Обробка особливих випадків.У арифметичному співпроцесорі є два механізми обробки помилок, що виникають при виконанні різних команд.
Перший механізм заснований на виклику переривання особливого випадку (INT 10h). Це переривання виробляється у тому випадку, коли відбувається яка-небудь помилка (наприклад, ділення на нуль) за умови, що відповідні біти масок особливих випадків в регістрі управління не встановлені.
При другому способі обробки помилок всі особливі випадки маскуються (відповідні біти регістра управління встановлюються в одиницю) і у разі помилки співпроцесор повертає деяке наперед відоме особливе значення (нечисло, невизначеність або нескінченність).
Програміст може вибирати між цими способами обробки помилок, маскуючи або дозволяючи переривання по особливому випадку.
Якщо переривання особливого випадку замасковане, можна використовувати такий спосіб виявлення помилки:
скинути прапорці особливих випадків в регістрі стану;
виконати одну або декілька команд співпроцесора;
перевірити значення прапорців особливих випадків в регістрі стану, зокрема, біт сумарної помилки ES;
якщо який-небудь прапорець встановлений, викликати програму обробки помилкової ситуації;
у програмі обробки помилкової ситуації можна скинути прапорці особливих випадків, записавши відповідне значення в регістр стану
Особливі значення результатів роботи співпроцесора
Після виконання команди співпроцесора, що проводить які-небудь обчислення, буває корисно перевірити результат, що вийшов, на приналежність до особливих значень.
Неточний результат
В результаті виконання деяких операцій може виникнути така ситуація, коли неможливо точно представити результат. Наприклад, при діленні числа 1.0 на 3.0 результатом є нескінченний періодичний двійковий дріб 0.010101.. (або десятковий нескінченний дріб 0.333...). Таке число не може бути представлене точно ні в одному форматі дійсних чисел. Зазвичай неточний результат є результатом округлення і може не розглядатися як помилка.
Переповнення
Якщо результат виконання операції дуже великий і не може бути представлений у форматі приймача результату, фіксується особливий випадок переповнення. Цей особливий випадок обов’язково відбудеться, наприклад, при додаванні максимального числа розширеної точності до самого себе, або при перетворенні цього числа у формат з подвійною або одинарною точністю. Оскільки для зберігання проміжних результатів використовується 80-бітове представлення, при виконанні операцій над числами з одинарною або подвійною точністю переповнення, як правило, не відбувається. Величезний діапазон чисел з розширеною точністю гарантує правильність представлення великих по абсолютному значенню результатів операцій з числами одинарної і подвійної точності.
Антипереповнення
Антипереповнення виникає тоді, коли результат дуже малий для його представлення у форматі приймача результату операції, але все таки відмінний від нуля. Наприклад, якщо робиться спроба перетворити найменше додатне число з розширеною точністю у формат числа з подвійною або одинарною точністю.
Якщо використовуються числа тільки з подвійною або одинарною точністю, а для зберігання проміжних результатів використовується формат з розширеною точністю, особливий випадок антипереповнювання, як правило, не виникає.
Ділення на нуль
Цей особливий випадок виникає при спробі виконати ділення скінченого ненульового числа на нуль.
У афінному режимі при діленні скінчених (додатніх або від’ємних) чисел на нуль (додатній або від’ємний) як результат повертається нескінченість. Знак цієї нескінченості залежить від знаку діленого і від знаку нуля. Наприклад, при діленні додатного ненульового числа на додатній уль виходить плюс нескінченість (+?) , при діленні додатного ненульового числа на від’ємний нуль - мінус нескінченність (-?).
У проектному режимі, а також при спробі ділення нуля на нуль виникає особливий випадок недійсної операції.
Недійсна операція
Цей особливий випадок виникає при спробі виконання таких заборонених команд, як ділення нуля на нуль, знаходження кореня квадратного з від’ємного числа, звертання до неіснуючого регістра співпроцесора або при спробі використання в якості операндів команд нечисел, невизначеностей або нескінченості (для трансцендентних функцій).
Денормалізований операнд
Відомо, що співпроцесор використовує операнди в нормалізованій формі. Проте при виконанні операції може виявитися, що результат дуже малий по модулю для представлення його в нормалізованій формі. Якщо такий результат вважати нульовим, це приведе до втрати точності обчислень або, навіть, до значних помилок. Наприклад, обчислюється наступний вираз: (y-x)+x; Якщо різниця (y-x) викликає антипереповнення і як результат береться нульове значення, то після обчислення всього виразу вийде x. Якщо ж піти на розширення діапазону представлення чисел за рахунок зниження точності і сформувати результат обчислення різниці (y-x) як денормалізоване число, вираз буде обчислений правильно і в результаті вийде у.
Таким чином, іноді доцільно замаскувати особливий випадок денормалізованого операнда і використовувати денормалізовані числа. Проте при спробі ділення на ненормалізоване число або знаходження його квадратного кореня фіксується особливий випадок недійсної операції.
Приклади
Лістинг 1
; Знайти результат обчислення виразу: z=y*sqrt(x), де x та y – цілі числа
; зі знаком.
includelib kernel32.lib
extrn __imp__ExitProcess@4:dword
ExitProcess equ __imp__ExitProcess@4
.586
.model flat
.stack 100h
.data
x dd 15 ;0000000F
y dd -1 ; FFFFFFFF
z dd 00
RC dw 0
.code
start:
finit ; ініціалізація співпроцесора
fstcw RC ; запис регістру управління у змінну RC
or RC,0c00h ; примусове округлення в напрямку до нуля
fldcw RC ; запис змінної RC до регістру управління
fild y ; ціле число заноситься в регістр співпроцесора
fild x
fsqrt
fmulp ; ST(1)*ST->ST(0), p означає, що потім з ST число
; вилучається із співпроцесора, а добуток з ST(1)
; заноситься в ST
fistp z ; у z заносимо ціле число після округлення
; (z=FFFFFFFDh).
call ExitProcess(0) ; завершити виконання програми
end start
Лістинг 2
; / cos(3*pi), x>3
; Знайти результат обчислення виразу: tmp=|
; \ sin(3*pi), x<=3
includelib kernel32.lib
extrn __imp__ExitProcess@4:dword
ExitProcess equ __imp__ExitProcess@4
.586
.model flat
.stack 100h
.data
x dd 15.0
tmp dd 00
.code
start:
finit ; ініціалізація співпроцесора
fldpi ; завантажити константу pi
mov tmp,3 ; tmp <- 3
fild tmp ; завантажити ціле число tmp у ST(0)
fmul ; ST(0)*ST(1), заносить результат в ST(1) і
; вилучає ST(0)
fld x ; завантажити дійсне число х у ST(0)

ficomp tmp ; порівняти ST(0) з tmp (15.0>3)
fstsw ax ; завантажити результат порівняння в ах
sahf ; завантажити ah в FLAGS

ja ___cos
fsin
jmp ___next
___cos:
fcos ; ST(0) <- (-1)
___next:
fstp tmp ; у змінну tmp заноситься результат
; обчислення (tmp= -1.0).
call ExitProcess(0) ; завершити виконання
end start
Лістинг 3
; Знайти результат обчислення виразу: tmp=ctg(pi/3)
.data
tmp dd 00
.code
start:
finit ; ініціалізація співпроцесора
fldpi ; завантажити константу pi
mov tmp,3 ; tmp <- 3
fild tmp ; st(0) <- tmp
fdiv ; st(1) = ST(1)/ST(0), pop ST(0) => ST(0) = result
fptan ; tg(pi/3), ST(1) = result, ST(0) = 1
fdivr ; ctg(pi/3)
fstp tmp
end start