Практичне заняття № 2
з дисципліни «Системне програмування»
ВИКОРИСТАННЯ МАТЕМАТИЧНОГО СПІВПРОЦЕСОРА
Математичний або арифметичний співпроцесор (FPU) призначений для виконання операцій над числами в форматі з плаваючою комою і довгими цілими числами. Він значно прискорює обрахунки, які пов’язані з дійсними числами. Співпроцесор може виконати як прості арифметичні операції (множення, додавання, віднімання, ділення),так і обраховувати значення різних функцій(косинус, синус, тангенс, котангенс).
Основна область застосування математичного співпроцесора – наукові обрахунки, машинна графіка, і звичайно ж ігри, які моделюють реальний світ, оскільки такі ігри, як правило, включають в себе і машинну графіку, та інженерні обрахунки.
Безпосереднє програмування арифметичного співпроцесора позволяє більш оптимально побудувати код програми і пришвидшити обрахунки. Багато засобів програмування дозволяють робити асемблерні вставки, які дають можливість напряму звертатись до співпроцесора. Це в свою чергу, дозволяє відмовитися від використання додаткових бібліотек, необхідних для роботи з дійсними числами. При цьому, відповідальність за коректність операндів і перевірку результатів у випадку безпосереднього програмування арифметичного співпроцесора, повністю покладається на програміста.
Зараз переважна більшість персональних комп’ютерів в якості центрального процесора використовують різні покоління процесора Pentium фірми Intel. Цей процесор має вбудований математичний співпроцесор.
Принципи роботи співпроцесора.
Математичний співпроцесор запускається центральним процесором. Після запуску він виконує всі обрахунки самостійно і паралельно з роботою центрального процесора. Якщо центральний процесор направляє наступну команду співпроцесору в той момент, коли співпроцесор ще не закінчив виконання попередньої команди, центральний процесор переводиться в стан очікування. Якщо ж співпроцесор не зайнятий, центральний процесор, направивши команду співпроцесору, продовжує свою роботу, не очікуючи завершення обрахунку. Послідовне розташування команд співпроцесора і центрального процесора в коді програми створюють ілюзію послідовного їх виконання. Простіше кажучи можлива така ситуація, що коли центральний процесор звернеться до комірки пам’яті, куди арифметичний співпроцесор повинен був записати результат своїх обчислень і якщо співпроцесор ще не закінчив обрахунки то результату там не буде. Однак є спеціальні засоби синхронізації (команда FWAIT).
Префікс команд та адресація операндів
Команди які призначенні для виконання співпроцесором, записуються в програмі як звичайні машинні команди центрального процесора, але всі вони починаються з байта, який відповідає команді центрального процесора ESC. Зустрівши таку команду, процесор передає її співпроцесору, а сам продовжує виконання програми з наступної команди.
Асемблерна мнемоніка всіх команд співпроцесора починається з букви F, наприклад: FADD, FDIV, FSUB и так далі. Команди співпроцесора можуть адресувати операнди, аналогічно звичайним командам центрального процесора. Операндами можуть бути або дані, які розміщуються в основній пам’яті комп’ютера, або внутрішні регістри співпроцесора.
Для команд арифметичного співпроцесора можливі всі види адресації даних, які використовуються центральним процесором.
Формати даних
Математичний співпроцесор здатний працювати з 7-ми різними типами даних:
3 типи з рухомою комою, за допомогою яких представляють дійсні числа (стандарт IEEE 754);
3 цілі двійкові типи;
1 цілий десятковий (двійково-десятковий упакований) тип.
Дійсні числа.
В загальному вигляді дійсні числа в загальних обрахунках можна записати наступним чином:
(знак)(мантиса)*10(знак)(порядок)
Наприклад: -1.35*105 (мінус одна ціла тридцять п’ять сотих, помножені на десять в п’ятому степені).
Тут знак: мінус, мантиса: 1.35, порядок: 5. Порядок теж може мати знак.
Важливим є таке поняття, як нормалізоване представлення чисел: якщо ціла частина мантиси числа складається з однієї цифри, не рівної нулю, то число з плаваючою крапкою називається нормалізованим. Перевага використання нормалізованих чисел полягає в тому, що для фіксованої розрядної сітки числа (тобто для фіксованої кількості цифр в числі) нормалізовані числа мають найбільшу точність. Крім того, нормалізоване представлення виключає неоднозначність, яка може виникнути через те, що кожне число з плаваючою крапкою може бути представлене різними (ненормалізованими) способами (наприклад: 123.5678*10^5 = 12.35678*10^6 = 1.235678*10^7 = 0.1235678*10^8 ).
При програмуванні на мовах високого рівня зустрічається наступне представлення чисел з рухомою (плаваючою) комою (крапкою):
(знак)(мантиса)E(знак)(порядок)
Наприклад, -5.35E-2 означає число -5.35*10-2 (мінус п’ять цілих тридцять п’ять сотих, помножені на десять в мінус другому ступені).
Арифметичний співпроцесор може працювати з дійсними числами в трьох форматах:
- одинарної точності (4 байти)
- подвійної точності (8 байт)
- розширеної точності (10 байт)
При будь-якому представленні. старший біт визначає знак дійсного числа: 0 - додатне число, 1 – від’ємне число. Всі числа, рівні по модулю, відрізняються лише цим бітом, оскільки для представлення від’ємних чисел доповняльний код не використовується, на відміну від центрального процесора.
Арифметичний співпроцесор працює з нормалізованими двійковими числами. Двійкове число з плаваючою крапкою називається нормалізованим, якщо ціла частина мантиси рівна 1. З метою розширення розрядної сітки, ця одиниця не зберігається у форматах одинарної і подвійної точності. У форматі з розширеною точністю зберігається і "зайвий" біт цілої частини нормалізованого числа. Основна причина використання для обчислень розширеної точності - оберігання програми від можливої втрати точності обчислень, пов’язаної з великими відмінностями в порядках чисел, що беруть участь в арифметичних операціях.
Поле порядку - це степінь числа 2, на який множиться мантиса, плюс зсув, рівний 127 для одинарної точності, 1023 - для подвійної точності і 16383 - для розширеної точності.
Для того, щоб визначити абсолютне значення числа з плаваючою крапкою, можна скористатися наступними формулами:
- Одинарна точність: 1.(цифри мантиси)*2Р-127
- Подвійна точність: 1.(цифри мантиси)* 2Р-1023
- Розширена точність: 1.(цифри мантиси)* 2Р-16383
Знак числа визначається старшим бітом.
Наприклад:
Маємо таке число представлено з одинарною точністю:

Для цього числа знаковий біт рівний 1 (від’ємне число), порядок рівний 126, мантиса - 11 (у двійковій системі числення).
Значення цього числа рівне:
1.11 * 2(126-127)= -1.75 * 2^-1 = -0,875
Особливі випадки представлення дійсних чисел.
- Нуль - це таке число, у якого порядок і мантиса рівні нулю. Нуль може мати знак плюс, або мінус, які ігноруються в операціях порівняння. Таким чином, є два нулі – додатній та від’ємний.
- Найменше додатне число - це число, яке має нульовий знаковий біт, значення порядку, рівне 1, і значення мантиси, рівне нулю. Залежно від представлення, найменше додатне число має наступні значення:
1,17*10-38 (одинарна точність);
2.23*10-308 (подвійна точність);
3.37*10-4932 (розширена точність).
- Найбільше від’ємне число - повністю співпадає з найменшим додатнім числом, але має біт знаку, встановлений в 1.
- Найбільше додатне число - це число, яке має нульовий знаковий біт, поле порядку, в якому всі біти окрім наймолодшого, рівні 1, і містить одиниці у всіх розрядах мантиси. Залежно від представлення, найбільше додатне число має наступні значення:
3.37*1038 (одинарна точність);
1.67*10308 (подвійна точність);
1.2*104932 (розширена точність).
- Найменше від’ємне число - повністю співпадає з найбільшим додтнім числом, але має біт знаку, встановлений в 1;
- Плюс і мінус нескінченність - це число містить всі одиниці в полі порядку і всі нулі в полі мантиси. Залежно від стану знакового біта може бути додатна і від’ємна нескінченність. Нескінченність може вийти, наприклад, як результат ділення дійсного числа на нуль.
-Нечисло - містить всі одиниці в полі порядку і будь-яке значення в полі мантиси. Нечисло може виникнути в результаті помилки виконання операції на співпроцесорі.
-Невизначеність - містить в полі порядку всі одиниці, а в полі мантиси - число 1000..0 (для одинарної і подвійної точності) або 11000..0 (для розширеної точності, оскільки в цьому форматі зберігається старший біт мантиси).
Цілі числа.
Арифметичний співпроцесор разом з дійсними числами здатний обробляти і цілі числа. Він має команди, що виконують перетворення цілих чисел в дійсні і назад.
Можливий чотири формати цілих чисел:
- ціле число (2 байти);
- коротке ціле число (4 байти);
- довге ціле число (8 байти);
- упаковане двійково-десяткове число (10 байт).
Ціле число займає два байти. Його формат повністю відповідає формату, що використовується центральним процесором. Для представлення від’ємних чисел використовується доповняльний код.
Коротке ціле і довге ціле мають аналогічні формати, але займають, відповідно, 4 і 8 байт.
Упаковане двійково-десяткове число займає 10 байт. Це число містить 18 десяткових цифр, розташованих по дві в кожному байті. Знак упакованого BCD числа знаходиться в старшому біті найлівішого байта. Решта біт старшого байта повинна бути рівні 0.
Існують команди співпроцесора, які перетворюють числа у формат упакованих двійково-десяткових чисел з внутрішнього представлення в розширеному дійсному форматі. Якщо програма робить спробу перетворення в упакований формат ненормалізованих чисел, нечисел, нескінченності і т.ін., в результаті виходить невизначеність. Невизначеність в упакованому BCD форматі є числом, в якому два старші байти містять одиниці у всіх розрядах. Вміст решти восьми байтів довільний. При спробі використовувати таке упаковане число в операціях - фіксується помилка.
Регістри співпроцесора.
Арифметичний співпроцесор має наступну систему регістрів:
8 регістрів загального призначення для роботи з даними (R0–R7, кожний розміром 10 байт), до яких можна звертатися тільки як до елементів стеку (де ST(0) – його вершина);
регістр стану SR (два байта) – якісно характеризує результати обчислень (помилки, переповнення тощо);
регістр управління CR (два байта) – управління точністю, заокругленням, маски особливих ситуацій;
регістр тегів TW (два байта) – описує вміст регістру даних (пусто, містить число, нуль, не-число);
регістр вказівника команди FIP (шість байтів);
регістр вказівника операнду команди FDIP (шість байтів).
Останні 2 регістри (FIP, FDIP) використовуються сумісно при опрацюванні виключних ситуацій і містять адресу останньої виконаної команди і адресу операнду цієї команди.
Числові регістри.
Числові регістри, як правило, в технічній літературі позначаються ST0 - ST7.
Числові регістри використовуються за принципом стеку.
Регістр стану в полі ST містить номер числового регістра, що є вершиною стеку. При виконанні команд як операнди можуть виступати числові регістри. В цьому випадку номер вказаного в команді регістра додається до вмісту поля ST регістра стану і таким чином визначається регістр, що використовується. Більшість команд після виконання збільшують поле ST регістра стану, записуючи результати своєї роботи в стек числових регістрів.
Регістр тегів.( TW)
Цей регістр розділений на вісім двобітових полів, які позначаються TAG0...TAG7. Кожне поле відноситься до свого числового регістра.
Поля регістра тегів класифікують вміст "свого" числового регістра:
00 - Регістр містить дійсне ненульове число
01 - В регістрі знаходиться нуль
10 - Регістр містить недійсне число - нечисло, нескінченність, невизначеність
11 - Порожній неініціалізований регістр
Наприклад, якщо всі регістри співпроцесора були порожні, а потім в стек числових регістрів було занесено одне дійсне ненульове значення, вміст регістра тегів в шістнадцятковому представленні буде 3FFFh.
Регістр управління. (CR)
Формат регістра управління (позначення біта і його номер):
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0




IC
RC
PC
IEM

PM
UM
OM
ZM
DM
IM


Шостий, тринадцятий, чотирнадцятий і п’ятнадцятий біти не використовуються.
У регістрі управління біти 0...5 - маски особливих випадків. Особливі випадки іноді виникають при виконанні команд співпроцесора, наприклад, при діленні на нуль, переповнюванні і так далі.
Якщо всі біти масок особливих випадків рівні нулю, особливий випадок викликає переривання центрального процесора INT10h (це переривання використовується BIOS для роботи з дисплейним адаптером). Якщо ж особливі випадки замасковані встановленням відповідних бітів в одиничний стан, переривання не викликається, а повертається особливе значення - нескінченність, нечисло і так далі.
Нижче приведена таблиця масок особливих випадків:
IM Недійсна операція
DM Ненормалізований результат
ZM Ділення на нуль
OM Переповнювання
UM Антипереповнення
PM Особливий випадок при неточному результаті
IEM Маскування одночасне всіх особливих випадків незалежно від значення битів0-5 регістра управління. Цей біт дійсний тільки для співпроцесора 8087.
Двобітове поле PC регістра управління управляє точністю обчислень в співпроцесорі:
00 - Використання розширеної точності. Цей режим встановлюється при ініціалізації співпроцесора
10 - Округлення результату до подвійної точності
11 - Округлення результату до одинарної точності
Штучне погіршення точності обчислень не приводить до прискорення роботи програми. Режими із зниженою точністю призначені для емуляції процесорів, що використовують подвійну і одинарну точність, відповідно.
Двобітове поле RC задає режим округлення при виконанні операцій з дійсними числами:
00 - округлення до найближчого числа, цей режим встановлюється при ініціалізації співпроцесора
01 - округлення у напрямку до плюс нескінченності
10 - округлення у напрямку до мінус нескінченності
11 - округлення у напрямку до нуля
Для зменшення помилок обчислень доцільно використовувати режим округлення у напрямі до найближчого числа. Режим округлення у напрямі до нуля використовується при моделюванні цілочисельної арифметики. Інші два режими округлення використовуються в інтервальній арифметиці. Для отримання найбільш точного результату кожна команда (операція) виконується двічі - перший раз з округленням у напрямку до міну нескінченості, другий раз - у напрямку до плюс нескінченності. Точний результат лежить між отриманими значеннями. Це стосується виконання лише окремих операцій, а не всієї програми обчислень.
Поле IC регістра управління призначене для управління нескінченістю і може мати два значення: 0 - проектний режим і 1 - афінний режим.
У проектному режимі існує тільки одна нескінченість, вона не має знаку. У афінному режимі є дві нескінченості – плюс і мінус нескінченість.
Афінний режим допускає виконання багатьох операцій з нескінченістю – додавання, множення і так далі.
Регістр стану.( SR)
Позначення бітів і їх порядок в регістрі стану:
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0

B
C3
ST
C2
C1
C0
ES

PE
UE
OE
ZE
DE
IE


Шостий біт не використовується.
У регістрі стану біти 0-5 - прапорці особливих випадків. Вони встановлюються завжди при виникненні особливих випадків, навіть замаскованих установкою в 1 відповідних бітів регістру управління.
Нижче приведена таблиця прапорців особливих випадків:
IE Недійсна операція
DE Ненормалізований результат
ZE Ділення на нуль
OE Переповнення
UE Антипереповнення
PE Неточний результат
Для того, щоб скинути сталий прапорець, програма повинна явним чином встановити його в нуль, виконавши команду запису в регістр стану.
Співпроцесори 80287/80387 і вбудовані співпроцесори процесорів 486 і вище використовують біт 7 як прапорець загальної помилки, який встановлюється в 1 при виникненні незамаскованого особливого випадку.
Біти C0, C1, C2, C3 - це коди умов. Вони визначаються за результатом виконання команд порівняння і команд знаходження остачі. Їх призначення яких аналогічне деяким прапорцям регістру FLAGS: C0 ( CF; C2 ( PF; C3 ( ZF.
Поле ST займає три біти 11, 12, 13 і містить номер числового регістра, стеку числових регістрів, що є вершиною.
Біт B - біт зайнятості. Він встановлюється в 1, коли співпроцесор виконує команду або коли відбувається переривання від співпроцесора. Якщо співпроцесор вільний, біт зайнятості встановлений в 0.
Регістри покажчика команди і покажчика операнда (FIP FDIP)
Регістри покажчика команди і покажчика операнда призначені для обробки особливих випадків, що виникають при роботі співпроцесора.
У співпроцесорі 8087 покажчик команди містить 20-розрядну адресу команди, що викликала особливий випадок і код виконуваної у цей момент операції. Адреса команди тут вказується без врахування передуючих команді префіксів
Співпроцесори 80287/80387 і вбудовані співпроцесори процесорів 486 і вище в реальному режимі роботи мають такий же формат регістра покажчика команд, проте цей покажчик показує на перший префікс команди, що викликала особливий випадок.
У захищеному режимі адреса складається з селектора і зсуву. Код операції тут відсутній, але його легко отримати, користуючись адресою команди.
Якщо при виникненні особливого випадку використовувався операнд, що знаходиться в оперативній пам’яті, його адреса записується в регістр покажчика операнда.
Система команд математичного співпроцесора
Можливі три формати команд співпроцесора, аналогічні форматам команд центральних процесорів фірми Intel. Це команди зі звертанням до оперативної пам’яті, команди зі звертанням до одного з числових регістрів і команди без операндів, заданих явним чином.
Команди зі звертанням до пам’яті можуть займати від двох до чотирьох байт, залежно від способу адресації.
Всі асемблерні мнемоніки команд співпроцесора починаються з букви F, тому їх легко відрізнити від команд центрального процесора.
Команди співпроцесора можна розділити на декілька груп:
команди пересилки даних
арифметичні команди
команди порівнянь чисел
трансцендентні команди
команди керування
Команди пересилки даних призначені для завантаження чисел з оперативної пам’яті в числові регістри, записи даних з числових регістрів в оперативну пам’ять, копіювання даних з одного числового регістра в іншій.
Арифметичні команди виконують такі операції, як додавання, віднімання, множення, ділення, знаходження квадратного кореня, знаходження часткового залишку, округлення і т.п.
Команди порівняння порівнюють дійсні і цілі числа, виконують аналіз чисел.
Трансцендентні команди призначені для обчислення різних тригонометричних, логарифмічних, показникових і гіперболічних функцій - sin, cos, tg і т.ін.
Команди керування забезпечують установку режиму роботи арифметичного співпроцесора, його скидання і ініціалізацію, перехід співпроцесора в захищений режим роботи і т.д.
Команди пересилки даних.
Команди завантаження в стек (Fpu LOAD):
FLD - завантажує з пам’яті у вершину стека ST(0) дійсне число
FILD - завантажує з пам’яті у вершину стека ST(0) ціле число
FBLD - завантажує з пам’яті у вершину стека ST(0) двійково-десяткове число
При виконанні цих команд операнд зчитується з оперативної пам’яті, перетворюється у формат з розширеною точністю. Потім поле ST регістра стану зменшується на одиницю і виконується запис операнду в числовий регістр, номер якого визначається новим значенням поля ST. Тобто операнд записується в стек числових регістрів, а покажчик стека (поле ST) зменшується на одиницю. По своїй дії ці команди нагадують команду PUSH центрального процесора.
Безпосередньо перед завантаженням числового регістра перевіряється вміст поля TAG0. Якщо це поле не рівний 11 (порожній регістр), в регістрі стану встановлюється прапорець IE (недійсна операція) і викликається переривання (якщо в регістрі управління не встановлена маска IM - маска недійсної операції).
Команди роботи зі стеком (Fpu STore and Pop).
FSTP ST(0) -> пам’ять, ST(0) позначається як вільний і ТОР=+1, дійсний формат
FISTP ST(0) -> пам’ять, ST(0) позначається як вільний і ТОР=+1, цілий формат
FBSTP ST(0) -> пам’ять, ST(0) позначається як вільний і ТОР=+1, двійково-десятковий формат
Ці команди спочатку зберігають вершину стеку в пам’яті, а потім виштовхують (видаляють) дані з вершини стека. Вміст числового регістра, номер якого визначається полем ST регістра стану, перетворюється в необхідний формат і записується в операнд, який задається в команді і знаходиться в пам’яті. Після запису вміст поля ST збільшується на одиницю. Ці дії аналогічні до тих, що виконуються командою POP центрального процесора.
Залежно від команди (FSTP, FISTP або FBSTP) проводиться перетворення формату (з розширеного в дійсний, цілий або BCD, відповідно). В процесі перетворення для команд FSTP і FISTP виконується округлення відповідно до вмісту поля RC регістра управління. Для команди FBSTP округлення завжди виконується таким чином - додається число 0.5, потім дробова частина результату відкидається.
Команди копіювання даних (Fpu STore).
FST ST(0) -> пам’ять, дійсний формат
FIST ST(0) -> пам’ять, цілий формат
FBST ST(0) -> пам’ять, десятковий формат
Ці команди пересилають дані з вершини стеку в область пам’яті, вказану операндом команди. При цьому вміст покажчика стека (поля ST) не змінюється.
Команда FST в якості операнду може використовувати посилання на числовий регістр ST(i), тому цю команду можна використовувати для копіювання вершини стеку в будь-який інший числовий регістр.
При записі даних в оперативну пам’ять виконується перетворення формату (у дійсний для FST, в цілий для FIST і в десятковий для FBST.
Команда обміну (Fpu eXCHange).
FXCH - обмін вмістом верхшини стеку ST(0) і числового регістра, вказаного як операнд команди
Команди завантаження констант.
FLDZ 0 -> ST(0) - Завантажити нуль
FLD1 1 -> ST(0) - Завантажити одиницю
FLDPI -> ST(0) - Завантажити число ( ("пі")
FLDL2T loge10 -> ST(0) - Завантажити loge10
FLDL2E log2e -> ST(0) - Завантажити log2 e
FLDLG2 log102 -> ST(0) - Завантажити log10 2
FLDLN2 loge2 -> ST(0) - Завантажити loge 2
Завантаження констант виконується набагато швидше спеціальними командами, ніж командами завантаження даних з оперативної пам’яті.
Арифметичні команди.
Співпроцесор використовує шість основних типів арифметичних команд:
Fxxx
Перший операнд береться з вершини стека (джерело), другий - наступний елемент стека. Результат виконання команди записується в стек

Fxxx пам’ять
Джерело береться з пам’яті, приймачем є вершина стека ST(0). Покажчик стека ST не змінюється, команда дійсна тільки для операндів з одинарною і подвійною точністю

Fixxx пам’ять
Аналогічно попередньому типу команди, але операндами можуть бути 16- або 32-розрядні цілі числа

Fxxx ST, ST(i)
Для цього типу регістр ST(i) є джерелом, а ST(0) - верхівка стека - приймачем. Покажчик стека не змінюється

Fxxx ST(i), ST
Для цього типу регістр ST(0) є джерелом, а ST(i) - приймачем. Покажчик стека не змінюється

FXXXP ST(i), ST
Регістр ST(i) - приймач, регістр ST(0) - джерело. Після виконання команди джерело ST(0) витягується із стека


Рядок "xxx" може приймати наступні значення:
ADD - Додавання
SUB - Віднімання
SUBR - Зворотне віднімання, тобто операнди міняються місцями
MUL - Множення
DIV - Ділення
DIVR - Зворотне ділення, ділене і дільник міняються місцями
Окрім основних арифметичних команд є додаткові арифметичні команди:
FSQRT - Знаходження квадратного кореня
FSCALE – Масштабування на ступінь числа 2
FPREM - Обчислення часткової остачі
FRNDINT - Округлення до цілого
FXTRACT - Виділення порядку числа і мантиси
FABS – Знаходження абсолютного значення (модуля) числа
FCHS - Зміна знаку числа
Після команди FSQRT обчислене значення квадратного кореня записується у вершину стека ST(0).
Команда FSCALE змінює порядок числа, що знаходиться в ST(0). Після цієї команди значення порядку числа ST(0) додається до масштабного коефіцієнту, який повинен бути заздалегідь записаний в ST(1). Дію цієї команди можна представити наступною формулою:
ST(0)= ST(0)* 2n, де -215 <= n <= +215
У цій формулі n містить у ST(1).
Команда FPREM обчислює остачу від ділення діленого ST(0) на дільника ST(1). Знак результату рівний знаку ST(0), а сам результат поміщається у вершину стеку ST(0).
Дія команди полягає в зсувах і відніманнях, аналогічно діленню "в стовпчик". Після виконання команди прапорець C2 регістру стану може приймати наступні значення:
0 - Остача від ділення, отримана в ST(0), менша за дільника ST(1), команда завершилася повністю
1 - ST(0) містить часткову остачу, програма повинна ще раз виконати команду для отримання точного значення остачі.
Команда RNDINT округляє ST(0) відповідно до вмісту поля RC регістру управління.
Команда FABS знаходить абсолютне значення ST(0).
Команда FCHS змінює знак ST(0) на протилежний.
Приклади
1. Знайти результат обчислення виразу: z = x + у, де x та y – дійсні числа.
; підключаємо бібліотеку kernel32.lib,
; яка містить реалізацію функції виходу __imp__ExitProcess
includelib kernel32.lib
; оголошення __imp__ExitProcess
extrn __imp__ExitProcess@4:dword
; для спрощення роботи оголосимо, що __imp__ExitProcess це ExitProcess
ExitProcess equ __imp__ExitProcess@4
; компіляція під 586 процесор і вище
.586
.model flat
.stack 100h
.data
x dd 1.5 ; 3FC00000
y dd -2.5 ; C0200000
z dd 00
.code
start:
finit ; ініціалізувати співпроцесор
fld x ; х завантажується в ST
fld y ; x переміщається в ST(1), а у - в ST
fadd ; сума отримується в ST, ST(1) звільняється
fwait ; зчекти поки обчислення не закінчаться
fstp z ; перемістити суму в z, ST звільняється (z=0xBF800000h)
call ExitProcess(0) ; завершити виконання
end start
2. Те саме з взаємодією С-асемблер
#include <stdio.h>
extern "C" void calc(void);

extern "C"
{
float X=0;
float Y=0;
float Z=0;
};
int main()
{
printf("Please, enter your numbers:\n");
printf("X = ");
scanf("%f",&X);
printf("Y = ");
scanf("%f",&Y);
calc();
printf("Z = %.4f\n",Z);
return 0;
}
.386
.model flat,c
EXTRN X:SDWORD, Y:SDWORD, Z:SDWORD
.data
Xfl dd 0
Yfl dd 0
.code
calc PROC
mov eax,X
mov Xfl,eax
mov eax,Y
mov Yfl,eax
fld Yfl
fld Xfl
fadd
fstp Z
ret
calc ENDP
END
3. Знайти результат обчислення виразу: z = x % у, де x та y – дійсні числа.
; підключаємо бібліотеку kernel32.lib,
; яка містить реалізацію функції виходу __imp__ExitProcess
includelib kernel32.lib
; оголошення __imp__ExitProcess
extrn __imp__ExitProcess@4:dword
; для спрощення роботи оголосимо, що __imp__ExitProcess це ExitProcess
ExitProcess equ __imp__ExitProcess@4
; компіляція під 586 процесор і вище
.586
.model flat
.stack 100h
.data
x dd 1000.0 ; 0x447a0000
y dd 25.5 ; 0x41cc0000
z dd 00
.code
start:
finit ; ініціалізувати співпроцесор

fld y ; y завантажується в ST
fld x ; y переміщається в ST(1), а x в ST(0)
PartialLp:
fprem1
fstsw ax ; завантажити регістр статусу SR в AX. ах = 7300h
test ah, 100b ; перевірити чи біт C2 встановлений (рівний 1)
; якщо отримано точний результат, то С2=0, якщо частковий, то С2=1
jnz PartialLp ; повторювати поки C2 встановлений (рівний 1)

fstp z ; Зберегти модуль (5.5).
fstp st(0) ; вилучити значення у
call ExitProcess(0) ; завершити виконання
end start