Operacje zmiennoprzecinkowe

Wprowadzenie

Koprocesor jest wyposażony w 8 rejestrów do przechowywania liczb zmiennoprzecinkowych (każdy rejestr przechowuje 80 bitów danych). Rejestry noszą nazwy ST0, ST1, ST2, . . . ST7. Rejestry są zorganizowane w postaci stosu (LIFO) z rejestrem ST0 na wierzchu stosu. Lista rozkazów koprocesora jest długa - rozkazy te operują bezpośrednio na rejestrach koprocesora. Dobre wprowadzenie do zagadnień operacji zmiennoprzecinkowych i przykłady programów asemblerowych wykorzystujących koprocesor znajdują się tutaj. Bardzo dobre rozwinięcie tematyki operacji zmiennoprzecinkowych znajduje się w rozdziale 6 książki Paula Cartera “PC Assembly Language”. Na Listingu 1 przedstawiono kod C, wywołujący asemblerową procedurę liczącą pierwiastki równania kwadratowego i pokazaną na Listingu 2 (przykład zaczerpnięty z książki Cartera).

Listing 1

// kod C w main.c, kod asemblerowy w quad.asm
// KOMPILACJA:
// gcc -m32 -o main.o -c main.c
// nasm -felf32 -o quad.o quad.asm
// gcc -m32 -o quad quad.o main.o

#include <stdio.h>

extern int quadratic(double, double, double, double *, double *);

int main()
{
    double a, b, c, root1, root2;

    printf("Enter a , b, c : ");
    scanf("%lf %lf %lf", &a, &b, &c);

    if (quadratic(a,b,c,&root1,&root2))
        printf ("roots: %.10g %.10g\n", root1, root2 );
    else
        printf ("No real roots\n");
    return 0;
}

Listing 2

; funkcja quadratic
; znajduje pierwiastki równania kwadratowego:
; a*x^2 + b*x + c = 0
; prototyp C:
; int quadratic( double a, double b, double c,
;                double * root1, double *root2 )
; Parametry:
; a, b, c - współczynniki równania
; root1 - wskaźnik do zmiennej typu double, przechowującej rozwiązanie
; root2 - j.w.
; Wartość zwracana:
; 1 jeśli istnieją pierwiastki rzeczywiste, w przeciwnym wypadku 0

 %define a qword [ebp+8]
 %define b qword [ebp+16]
 %define c qword [ebp+24]
 %define root1 dword [ebp+32]
 %define root2 dword [ebp+36]
 %define disc qword [ebp-8]
 %define one_over_2a qword [ebp-16]

 segment .data
 MinusFour dw -4

 segment .text
 global quadratic
 quadratic:

 push ebp
 mov ebp, esp
 sub esp, 16              ; alokacja pamięci na dane lokalne procedury (disc i one_over_2a)
 push ebx                 ; KONWENCJA C - musimy zachować oryginalne dane z rejestru EBX

 fild word [MinusFour]    ; stack -4
 fld a                    ; stack: a, -4
 fld c                    ; stack: c, a, -4
 fmulp st1                ; stack: a*c, -4
 fmulp st1                ; stack: -4*a*c
 fld b
 fld b                    ; stack: b, b, -4*a*c
 fmulp st1                ; stack: b*b, -4*a*c
 faddp st1                ; stack: b*b - 4*a*c
 ftst                     ; porównaj ST0 z 0
 fstsw ax                 ; przenosimy flagi C koprocesora do rejestru AX
 sahf                     ; przenosimy dane z AX do rejestrów flagowych procesora
                          ; powyższe dwie instrukcje są konieczne do stworzenia instrukcji warunkowej!!!

 jb no_real_solutions     ; if disc < 0, nie ma rozwiązań rzeczywistych
 fsqrt                    ; stack: sqrt(b*b - 4*a*c)
 fstp disc                ; store and pop stack
 fld1                     ; stack: 1.0
 fld a                    ; stack: a, 1.0
 fscale                   ; stack: a * 2^(1.0) = 2*a, 1
 fdivp st1                ; stack: 1/(2*a)
 fst one_over_2a          ; stack: 1/(2*a)
 fld b                    ; stack: b, 1/(2*a)
 fld disc                 ; stack: disc, b, 1/(2*a)
 fsubrp st1               ; stack: disc - b, 1/(2*a)
 fmulp st1                ; stack: (-b + disc)/(2*a)

 mov ebx, root1           ; ładuję adres root1 do rejestru
 fstp qword [ebx]         ; ściągam ze stosu do *root1
 fld b                    ; stack: b
 fld disc                 ; stack: disc, b
 fchs                     ; stack: -disc, b
 fsubrp st1               ; stack: -disc - b
 fmul one_over_2a         ; stack: (-b - disc)/(2*a)

 mov ebx, root2
 fstp qword [ebx]
 mov eax, 1               ; return 1
 jmp short quit

 no_real_solutions:
 mov eax, 0               ; return 0

 quit:
 pop ebx
 mov esp, ebp
 pop ebp
 ret

Przesyłanie rozwiązań

Wszystkie zadania powinny być rozwiązane w assemblerze 32 bitowym i przesłane na BaCę

[http://pn.baca.ii.uj.edu.pl]

Wysłać należy tylko plik assemblerowy.

Zadania

Zadanie 1

Proszę napisać w assemblerze funkcję o nagłówku

extern "C" double wartosc(double a, double b, double  c, double d, double x);

wyliczającą wartość wyrażenia y=ax^3+bx^2+cx+d.

Funkcja ma pobierać dane wejściowe od procedury wołającej napisanej w C, która wyświetla wyniki obliczeń.

Zadanie 2

Proszę napisać funkcję asemblerową o nagłówku

extern "C" void prostopadloscian( float a, float b, float c, float * objetosc, float * pole);

wyliczającą objętość i pole powierzchni prostopadłościanu a.

Funkcja ma pobierać dane wejściowe od procedury wołającej napisanej w C, która wyświetla wyniki obliczeń.

Zadanie 3

Proszę napisać funkcję asemblerową o nagłówku

extern "C" long double iloczyn_skalarny(int n, long double * x, long double * y);

mnożącą skalarnie dwa n-wymiarowe wektory liczb rzeczywistych o współrzędnych w tablicach x i y.

Funkcja ma pobierać dane wejściowe od procedury wołającej napisanej w C, która wyświetla wyniki obliczeń.

Zadanie 4

Proszę napisać funkcję asemblerową o nagłówku

extern "C" void tablicuj(double a, double b, double P, double Q, double xmin, double xmax, int k,  double * wartosci);

Funkcja ma tablicować wartości funkcji:

y=a*(sin(P*2*pi*x))^2 + b*(sin(Q*2*pi*x))^2

dla k>=2 równoodległych punktów w przedziale od xmin do xmax.(tj. x1 = xmin … xk = xmax )

Wynik ma być zapisany w tablicy wartosci (zakladamy, że jest odpowiednio duza);

Funkcja ma pobierać dane wejściowe od procedury wołającej napisanej w C, która wyświetla wyniki obliczeń.