
Использование FPU
.pdfИспользование FPU.
Команды FPU. Основные приемы программирования с использованием FPU.
Мы рассматривали команды и алгоритмы обработки целочисленных данных, то есть чисел с фиксированной точкой. Для обработки числовых данных в формате с плавающей точкой процессоры IA-32 содержат специальное устройство, которое является важной частью их архитектуры.
Устройства для обработки чисел с плавающей точкой появились в компьютерах давно. Начиная, с модели i486 сопроцессор исполняется в одном корпусе с основным процессором и, таким образом, является неотъемлемой частью компьютера.
Для чего нужен сопроцессор, какие возможности добавляет он к тому, что делает основной процессор, кроме обработки еще одного формата данных? Перечислим некоторые из них:
-Полная поддержка стандартов IEEE-754 и 854 на арифметику с плавающей точкой. Эти стандарты описывают как форматы данных, с которыми должен работать сопроцессор, так и набор реализуемых им функций.
-Поддержка численных алгоритмов для вычисления значений тригонометрических функций, логарифмов и т. п. Эта работа сопроцессора выполняется абсолютно прозрачно для программиста, что само по себе очень ценно, так как не требует от него разработки соответствующих подпрограмм.
-Обработка десятичных чисел с точностью до 18 разрядов, что позволяет сопроцессору без округления выполнять арифметические операции над целыми десятичными числами со значениями до 1018.
-Обработка вещественных чисел из диапазона 3,37 • 10-4932...1,18 • 10+4932.
Система команд сопроцессора.
Система команд сопроцессора включает около 80 машинных команд. Рассмотрим основные моменты образования названий команд.
-Все мнемонические обозначения начинаются с символа F (Float).
-Вторая буква мнемонического обозначения определяет тип операнда в памяти, с которым работает команда:
-I — целое двоичное число;
-В — целое десятичное число;
-D отсутствие буквы — вещественное число.
-Последняя буква Р в мнемоническом обозначении команды означает, что последним действием команды обязательно является извлечение операнда из стека.
-Последняя или предпоследняя буква R (reversed) в мнемоническом обозначении команды означает реверсивное следование операндов при выполнении команд вычитания и деления, так как для них важен порядок следования операндов.
Например:
FINIT - инициализация сопроцессора;
FLD - загрузка в стек действительного числа;
FILD - преобразование заданного целочисленного операнда в действительное представление и загрузка его в стек;
FADD - сложение двух действительных чисел;
FIADD - сложение целого числа с действительным;
FSTP - запись действительного числа и выталкивание из стека;
FISTP - преобразование числа из вершины стека в целое число, запись его в поле памяти и выполнение операции выталкивания из стека;
FWAIT - ожидание окончания работы сопроцессора;
Упражнение 1. Фрагмент программного кода на С++ |
Упражнение 1а. Фрагмент программного кода на MASM32 |
|||
демонстрирует нахождение сумммы двух вещественных чисел с |
демонстрирует нахождение сумммы двух вещественных чисел с |
|||
применением команд сложения математического сопроцессора. |
применением команд сложения математического сопроцессора. |
|||
// FADD_ASM.cpp |
; FADD_ASM.asm |
|
||
#include "iostream" |
TITLE Сумма двух действительных чисел |
|
||
//#include <stdio.h> //код для Си |
.686 |
|
|
|
using namespace std; |
;.model flat, stdcall |
|
||
int main() |
option casemap:none |
|
||
|
|
|
||
{float f1, f2, fsum; |
include \masm32\include\masm32rt.inc |
|
||
while (true) |
|
|
|
|
{ |
.data |
|
|
|
cout<<"Enter float 1: "; |
formats db '%f',0 ;формат вводимых и выводимых действительных чисел |
|||
//printf("\nEnter float 1: "); //код для Си |
|
|
|
|
cin>>f1; |
f1 dd ? |
;первое слагаемое |
|
|
//scanf("%f", &f1); //код для Си |
f2 dd ? |
;второе слагаемое |
|
|
cout<<"Enter float 2: "; |
fsum dq 0 ;исходная сумма |
|
||
//printf("Enter float 2: "); //код для Си |
|
|
|
|
cin>>f2; |
.code |
|
|
|
//scanf("%f", &f2); //код для Си |
main: |
|
|
|
_asm { |
print "Enter f1=" |
|
||
finit |
invoke |
crt_scanf,ADDR formats,ADDR f1 |
;ввод первого слагаемого как |
|
fld f1 |
;симв. стр. и преобр. ее в число, с помещ. в яч. f1 |
|||
fadd f2 |
|
|
|
|
fstp fsum |
|
|
|
|
fwait |
print "Enter f2=" |
|
||
}; |
invoke |
crt_scanf,ADDR formats,ADDR f2 |
;ввод второго слагаемого как |
|
//cout.precision( 10 ); //вывод числа с 10 знаками после запятой |
;симв. стр. и преобр. ее в число, с помещ. в яч. f2 |
|||
cout<<"fl + f2 = "<<fsum<<endl<<endl; |
||||
|
|
|
||
//printf("fl + f2 =%6.3f\n", fsum); //код для Си |
|
|
|
|
} |
finit |
;инициализация сопроцессора |
|
|
return 0; |
fld f1 ;загрузка в стек действительного числа |
|||
} |
fadd f2 ;сложение двух действительных чисел |
|||
В блоке _asm {...} первая команда fld загружает вещественное |
fstp fsum ;запись действительного числа и выталкивание из стека |
|||
число f1 из памяти в вершину стека сопроцессора. Команда fadd |
fwait |
;ожидание окончания работы сопроцессора |
||
вычисляет сумму значений вершины стека ST(0) и ячейки памяти, |
|
|
|
|
содержащей значение f2. Результат операции сохраняется в |
print "fsum=f1+f2=" |
|
||
вершине стека сопроцессора. Наконец, команда fstp сохраняет |
invoke |
crt_printf, addr formats, fsum ;вывод fsum на экран с 6 знаками |
||
значение суммы в переменной fsum, при этом вершина стека st(0) |
;после запятой |
|
||
|
print chr$(13,10) |
|

очищается. |
inkey |
|
|
|
FINIT - инициализация сопроцессора; |
exit |
|
|
|
FLD - загрузка в стек действительного числа |
end main |
|
||
FADD - cложение двух действительных чисел |
|
|||
FSTP - запись действительного числа и выталкивание из стека |
|
|
|
|
FWAIT - ожидание окончания работы сопроцессора; |
|
|
|
|
Задание 1. Проверьте работу программы с использованием Си кода и |
|
|
|
|
различными значениями переменных f1 и f2. |
|
|
|
|
|
|
|||
Упражнение 2. Применение команд сопроцессора ассемблера: |
Упражнение 2a. Применение команд сопроцессора ассемблера: |
|||
загрузки, сложения и сохранения при использовании ASM вставки |
загрузки, сложения и сохранения в MASM32 для нахождения |
|||
в С++ для нахождения суммы элементов целочисленного массива |
суммы элементов целочисленного массива из семи элементов. |
|||
из семи элементов. |
TITLE Сумма целочисленных элементов массива |
|||
//FSUM_ARRAY_ASM.cpp |
||||
#include "iostream" |
.686 |
|
|
|
using namespace std; |
;.model flat, stdcall |
|||
int |
main() |
option casemap:none |
||
{int iarray[6] = {-3, -7, 0, 5, 3, 9}; |
|
|
|
|
int *piarray = iarray; |
include \masm32\include\masm32rt.inc |
|||
int isum; |
||||
int sf = sizeof(iarray)/4; |
|
|
|
|
_asm |
{ |
.data |
|
|
mov |
ECX, sf |
|
|
|
dec |
ECX |
formats db '%d',0 ;формат вводимых и выводимых целых чисел |
||
mov |
ESI, piarray |
|
|
|
finit |
|
iarray dd -3, -7, 0, 5, 3, 9 |
||
fild [ESI] |
||||
next: |
ESI, 4 |
isum dq 0 |
; |
|
add |
.code |
|
|
|
fiadd |
[ESI] |
main: |
|
|
loop |
next |
|
|
|
fistp |
isum |
mov ECX,SIZEOF iarray/4 |
||
fwait |
|
mov ESI,0 |
|
|
} |
|
|
||
|
finit |
;инициализация сопроцессора |
||
printf("Summa of integers = %d\n", isum); |
||||
getchar(); |
fild isum ;загрузка в стек целого числа хранящегося в isum |
|||
return 0; |
next: |
|
|
|
} |
|
fiadd |
iarray[ESI*4] ;сложение двух целых чисел (одно в стеке, |
|
Для нахождения суммы элементов массива воспользуемся следующим |
другое в памяти) |
|||
простым алгоритмом: первоначально загрузим в вершину стека первый |
fistp |
isum |
;запись целого числа в переменную isum нах. впамяти |
|
элемент массива с помощью команды fild dword ptr [esi], после чего будем |
и выталкивание из стека |
|||
прибавлять к нему в каждой итерации следующий элемент. Адрес первого |
||||
элемента массива поместим в регистр esi, а количество итераций, на 1 |
fild isum ;загрузка в стек целого числа хранящегося в isum |
|||
меньшее размера массива, поместим в регистр есх. После того как сумма |
inc ESI |
|
||
найдена, сохраняем ее в переменной isum командой fistp. |
loop |
next |
|
|
DECдекремент (уменьшение регистра или ячейки на 1); |
fistp |
isum |
;запись целого числа в переменную isum нах. впамяти и |
|
FILD - преобразование заданного целочисленного операнда в |
выталкивание из стека |
|||
действительное представление и загрузка его в стек; |
fwait |
;ожидание окончания работы сопроцессора |
||
FIADD Сложение целого числа с действительным; |
|
|
|
|
FISTP - преобразование числа из вершины стека в целое число, запись его в |
print "isum=iarray[0]+...+iarray[5]=" |
|||
поле памяти и выполнение операции выталкивания из стека; |
invoke crt_printf, addr formats, isum ;вывод isum на экран |
|||
|
|
|||
Задание 2. Проверьте работу программы с использованием различной длины |
print chr$(13,10) |
|||
массива. |
inkey |
|
|
|
Задача 1. Найдите сумму четных элементов массива. |
exit |
|
|
|
|
|
|
||
|
|
end main |
|
|
|
|
|||
Упражнение 3. Вычисление квадратного корня из вещественных чисел |
Упражнение 3б. Вычисление квадратного корня из вещественных чисел |
|||
по итерационной формуле Герона xn+1=0,5(xn+a/xn); x0=a; |
по итерационной формуле Герона xn+1=0,5(xn+a/xn); x0=a; |
|||
limxn = a на С++ с использованием ассемблерной вставки |
lim xn = |
a на MASM32 |
||
n→∞ |
|
n→∞ |
|
|
#include <iostream> |
.686 |
|
|
|
using namespace std; |
option casemap :none |
|||
void main() |
include \masm32\include\masm32rt.inc |
|||
.data |
|
|
||
{//для double точность 15 знаков после запятой |
|
|
||
|
double xn = 0; |
xn dq ? |
|
|
|
double x0 = 0; |
xn1 dd ? |
|
|
|
double k = 0.5; |
x0 dd ? |
|
|
|
double xn1 = 0; |
k dd 0.5 |
|
|
|
double a = 0; |
a dd ? |
|
|
|
int n = 100; |
n dd 100 |
|
|
cout << "Enter a="; |
formats db '%f',0 ;формат вводимых и выводимых вещественных чисел |
|||
cin >> a; |
.code |
|
|
|
_asm { |
|
|
|
|
fld a |
;x0 = a |
start: |
|
|
|
print "Enter a=" |
|||
fstp x0 |
|
|||
m: |
|
invoke crt_scanf, ADDR formats, ADDR a |
||
|
|
|
|
|

;xn = x0
fld x0 fstp xn
;xn1 = k*(xn + (a / xn))
fld a fdiv xn fadd xn fmul k fstp xn1
|
;x0 = xn1 |
fld xn1 |
|
fstp |
x0 |
|
;n = n - 1 |
mov |
eax,n |
sub |
eax,1 ;или можно заменить эту строку на: dec eax |
mov |
n,eax |
|
;if (n >=0) goto m |
js m1 jmp m
}
m1:
cout << endl; cout.precision( 30 );
cout << xn1 << endl;
}
Упражнение 3а. Вычисление квадратного корня из вещественных чисел
по итерационной формуле Герона xn+1=0,5(xn+a/xn); x0=a;
lim xn = a на С++
n→∞
#include <iostream> using namespace std;
void main()
{//для double точность 15 знаков после запятой double xn = 0;
double x0 = 0; double k = 0.5; double xn1 = 0; double a = 0; int n = 100;
cout << "Enter a="; cin >> a;
x0 = a;
m:
xn = x0;
xn1 = k*(xn + (a / xn));
x0 = xn1; //n = n - 1;
if ((n = n - 1) >=0) goto m; cout << endl;
cout.precision( 30 );
cout << xn1 << endl;
}
;x0 = a fld a
fstp x0 m:
;xn = x0
fld x0 fstp xn
;xn1 = k*(xn + (a / xn))
fld a fdiv xn fadd xn fmul k fstp xn1
|
;x0 = xn1 |
fld xn1 |
|
fstp |
x0 |
|
;n = n - 1 |
mov |
eax,n |
sub |
eax,1 ;или можно заменить эту строку на: dec eax |
mov |
n,eax |
|
;if (n >=0) goto m |
js m1 jmp m
m1:
invoke crt_printf, SADD("SQRT: %.8Lf",13,10), xn ;печатаем вычисленный корень
;invoke crt_printf, ADDR formats, xn ;печатаем вычисленный корень
;print chr$(13,10) exit
end start
inkey
Задача 2. Написать программу нахождения действительных корней квадратного уравнения: ax2+bx+c=0
x |
= |
−b ± b2 |
−4ac |
|
|
||
1,2 |
|
2a |
|
|
|
|