Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Использование FPU

.pdf
Скачиваний:
36
Добавлен:
03.06.2015
Размер:
203.22 Кб
Скачать

Использование 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