                              Zrozumiec Koprocesor  
		          Written by CoxoC/CookieCrK


Ten tekst pisze z potrzeby , gdyz potrzebuje procedurek konwertujacych liczby 
zmiennoprzecinkowe REAL4 i REAL8 na lancuchy kodow ASCII (hehe po prostu stringi ;)
Hmmmm zerknijcie na chwilke na punkt->Zrodla znajdujacy sie na koncu tego tekstu otoz 
chcialbym wam opisac wycinek mojego zyciorysu,ktory jest zwiazany z tymze tematem ;) 
Ksiazka [1] zostala kupiona przeze mnie za jeden polski zloty z wyprzedazy z ksiegarni 
PWN za czasow liceum - oraz przydala sie pozniej na studiach na kursie "arytmetyka 
komputerow" prowadzonym przez samego autora prof. nadzw. dr. hab. inz. Janusza 
Biernata sami ocenicie czy zaliczylem owy kurs ;) chcialbym wiec przekazac wam moja 
wiedze w tej materii przechodzac od dywagacji czysto teoretycznych i naukowych do 
praktyki ....

Ok zacznijmy ... jak juz stwiardzilem od teorii do praktyki wiec ...
standardy zmiennoprzecinkowe zostaly sprecyzowane normach IEEE 754 i 854.

Norma IEEE 754 jest standardem dwojkowym, w ktorym wymaga sie, aby baza wszystkich 
reprezentacji bylo =2. Standard ten precyzuje formaty zapisu liczb oraz dzialania
na tych liczbach.

Norma IEEE 854 jest standardem uniwersalnym, niezaleznym od bazy, obejmujacym zarowno
arytmetyke dwojkowa, jak i arytmetyke dziesietna (=10).Standard ten nie precyzuje
dokladnie przyporzadkowania poszczegolnych bitow ani sposobu kodowania liczb 
zmiennoprzecinkowych - olewamy ta norme ....

W standardzie IEEE 754 okreslono trzy formaty staloprzecinkowe dwojkowe,cztery
formaty zmiennoprzecinkowe i jeden staloprzecinkowy format BCD.

Dwojkowymi formatami staloprzecinkowymi sa :
- 16bitowy format calkowity, tzw. SHORT INTEGER (czyli po assemblerowemu WORD)
- 32bitowy format calkowity, tzw. INTEGER (czyli DWORD)
- 64bitowy format calkowity, tzw. EXTENDED INTEGER (na maszynach 32bitowych intela
					jest to para rejestrow EDX:EAX uzywana do
					dzielenia i mnozenia)

Format dziesietny BCD jest 80-bitowy i przewiduje zakodowanie 18-cyfrowej liczby 
calkowitej dziesietnej oraz znaku na najbardziej znaczacej pozycji (bit 79).
Bity 72-78 nie maja okreslonej wartosci.

Dla reprezentacji zmiennoprzecinkowych przewidziano cztery formaty :
- zwykly o pojedynczej precyzji - SHORT REAL (SINGLE PRECISION)
- rozszerzony o pojedynczej precyzji - REAL (SINGLE EXTENDED PRECISION)
- zwykly o podwojnej precyzji - LONG REAL (DOUBLE PRECISION)
- rozszerzony o podwojnej precyzji - DOUBLE EXTENDED PRECISION

Zajmijmy sie reprezentacja DOUBLE EXTENDED PRECISION 

Jest to podstawowa reprezentacja danych, uzywana przez wszystkie koprocesory 80x87,
definiujaca liczbe na 80-bitach - jest to po naszemu liczba koprocesora FLOAT.
Ten typ danych moze bezposrednio reprezentowac liczby z przedzialu 3.36*10^-4932
do 1.19^4932 - dla liczb znormalizowanych oraz z przedzialu 3.65*10^-4951 do
1.19*10^4932 - dla liczb zdenormalizowanych (operator ^ oznacza operacje potegowania)
Format ten posiada 64bity mantysy, 15bitow wykladnika oraz 1 bit znaku.Dzieki niemu
mozemy zakodowac 19-cyfrowa liczbe dziesietna.

REAL - SINGLE EXTENDED PRECISION

Liczba 48bitowa (6 bajtow) - 39 bitow kodujacych mantyse, 8 bitow kodujacych 
ceche(wykladnik), oraz 1bit kodujacy znak

SHORT REAL - SINGLE PRECISION

Ten typ danych posiada 32-bity i jest podzielony na 23bity mantysy, 8bitow wykladnika
oraz 1bit znaku.Jest to typ REAL4 - ta czworka oznacza , ze ten typ zapisany jest na 
czterech bajtach czyli na 4*8=32bitach.Ten typ zapewnia precyzje do szesciu-siedmiu
miejsc dziesietnych i reprezentuje liczby z zakresu 1.17*10^-38 i 3.40*10^38 - dla 
liczb znormalizowanych oraz 1.40*10^-45 do 3.40*10^38 - dla liczb zdenormalizowanych.

LONG REAL - DOUBLE PRECISION

Ten typ danych ma 64-bity i sklada sie z 52bitow mantysy, 11bitow wykladnika, oraz
1bit znaku.Zapewnia precyzje do 15-16 liczb dziesietnych i moze przechowac liczby z 
zakresu od 2.22*10^-308 do 1.79*10^308 - dla liczb znormalizowanych oraz 4.94*10^-324
do 1.79*10^308 wlaczajac liczby zdenormalizowane.Jest to nasz typ REAL8 (8*8=64)

Ok czas na pare slow wyjasnienia .... otoz liczba zmiennoprzecinkowa jest reprezentowana
przez trojke (M,,E), gdzie M jest znacznikiem (significand), zwanym mantysa (mantissa),
 jest baza reprezentacji (radix), zas E wykladnikiem (exponent), zwanym cecha 
(characteristic). Wiec wartoscia liczby zmiennoprzecinkowej jest F=M*^E . W naszym 
przypadku =2 (gdyz mamy do czynienia z systemami dwojkowymi)

Jezeli wielkosc znacznika nie jest ograniczona, to kazda liczbe zmiennoprzecinkowa mozna
przedstawic w bardzo wielu roznych reprezentacjach
		F=M*^E=(M*^-i)*^(E+i)
	przy czym wyrazenie M*^-i jest mantysa (Mi) liczby Mi*^(E+i).

Hmm dla przykladu wezmy liczbe 48 i zakodujmy ja jako liczbe zmiennoprzecinkowa o bazie 2
	F=48=....=1.5*2^5=3*2^4=6*2^3=12*2^2=24*2^1=48*2^0=96*2^-1=....  
						chyba wiecie juz o co biega ;)

Jak zauwazyliscie uprawnione sa wszystkie reprezentacje jednej liczby. Hmm teraz pomyslcie
w jaki sposob koprocesor porownuje dwie liczby dla przykladu liczby 1*2^5 oraz 4*2^3 ??
co do wartosci sa one rowne lecz gdyby porownywac mantyse i wykladnik z osobna wyszloby, ze
sa rozne ... otoz przed porownaniem koprocesor normalizuje obie liczby, czyli przeksztalca
tak obie te liczby aby mantysa byla w zakresie od 1 <= M < 2 (mowimy o =2) i dopiero pozniej
porownuje mantyse i wykladnik z osobna :). Liczba 1*2^5 jest juz liczba znormalizowana , 
natomiast ta druga - nie.W porzadku, wrocmy na chwilke do zakresow liczb. Jak zauwazyliscie 
przy omawianiu typow danych podalem dwa zakresy jeden dla liczb znormalizowanych i drugi dla 
liczb zdenormalizowanych. Otoz koprocesor uzywa tak zwanej techniki "ukrytego bitu" 
(hidden bit technique) - wykorzystuje fakt,ze dla liczb znormalizowanych zawsze mantysa 
zaczyna sie od 1. (dla przykladu 1 <= 1.789324 < 2 ) czyli z tego wynika, ze nie ma potrzeby
kodowania calosci (1) w mantysie - dzieki temu efektywna dlugosc mantysy wzrasta o 1bit.
Dla liczb zdenormalizowanych pierwszy bit mantysy koduje liczbe calkowita stojaca przed 
przecinkiem.

Struktura kodow formatow zmiennoprzecinkowych IEEE 754:

   |s|.............|...............|
                    \\\\\\\\\\\\\\\--> zakodowana w kodzie dwojkowym mantysa (bez bitu ukrytego)
      \\\\\\\\\\\\\------------------> wykladnik E - w kodzie dwojkowym z obciazeniem +B
    \--------------------------------> bit kodujacy znak

Coz to jest ten kod z obciazeniem ??? Otoz aby nie przynudzac ;) chodzi o to aby zakodowac
na tych bitach rowniez liczby ujemne. Zasade dzialania kodu z obciazeniem przedyskutuje
na przykladzie. Mamy wykladnik E zakodowany na czterech bitach i rowny 0011 , wiemy , ze
jest on zakodowany w kodzie z obciazeniem 4 teraz 
0011(bin)=3(dec)      teraz od 3 odejmujemy obciazenie i w ten sposob
                      otrzymamy prawdziwa wartosc liczby czyli -1
dla liczby 1000 mamy 
1000(bin)=8(dec) - 4 = 4    

Ok teraz * dla typu SINGLE PRECISION          obciazenie B=127   (typ Single w Pascalu)
         * dla typu SINGLE EXTENDED PRECISION obciazenie B=129   (typ Real w Pascalu)
         * dla typu DOUBLE PRECISION          obciazenie B=1023  (typ Double w Pascalu)
         * dla typu DOUBLE EXTENDED PRECISION obciazenie B=16383 (typ Extended w Pascalu)

Kodowanie liczb zmiennoprzecinkowych zgodnie z podanymi, przeze mnie, powyzej zasadami nie
rozwiazuje problemow reprezentacji liczb bardzo malych , zera oraz reprezentacji nieskonczonosci.
Sposob kodowania takich wielkosci powinien umozliwiac ich latwa identyfikacje, przyjelo sie 
zatem, ze 
 - dla liczb bardzo malych i zera rezerwujemy przestrzen kodowa odpowiadajaca najmniejszemu
   kodowi wykladnika (czyli zawierajacego same zera) - mantysa przyjmuje wartosc 0.
 - dla nieskonczonosci (Inf) wykladnik przyjmuje najwieksza wartosc (czyli zawiera same jedynki) 
   natomiast mantysa jest rowna zero 
 - definiuje sie rowniez wartosc NaN (Not a Number) - wtedy wykladnik przyjmuje najwieksza 
   wartosc natomiast mantysa jest rozna od zera 


Ubocznym skutkiem takiego rozwiazania jest znakowane zero , czyli podwojne kodowanie zera -0,+0.

Ok koniec z ta teoria :)) tak przy okazji jak sie tego kiedys uczylem to wydawalo sie to pomocne
a zarazem czysto teoretyczne - natomiast teraz ;) po przeszlo pol roku stwierdzam, ze przydalo 
sie rowniez w prakryce :))))

Przejdzmy wiec do praktyki - przyjmijmy najpierw , ze bedziemy zapisywac liczbe 
w konwencji F=M*2^E . Procedurka konwersji REAL4 przyjmie wiec nastepujaca postac :

;-----------------------------
;Wejscie : EAX=REAL4
;Wyjscie : ECX=bit znaku
;          EAX=mantysa
;          EDX=wykladnik
;-----------------------------
Konwersja_REAL4_do_F	PROC
			PUSH EAX
			XOR EDX,EDX
			SHLD EDX,EAX,1		;EDX=znak
			MOV ECX,EDX
			AND EAX,7FFFFFh		;maska 23bitow - mantysa
			POP EBX
			XOR EDX,EDX
			SHLD EDX,EBX,9		;bit znku + bity wykladnika
			AND EDX,0FFh		;maska 8 bitow - wykladnik
			SUB EDX,127		;B=127
			RET
Konwersja_REAL4_do_F	ENDP

Po takiej konwersji liczby w rejestrach mozna bez problemu przekonwertowac na stringa.
Dla REAL8 mozna podobnie zrobic taka konwersje - z uwagi na 8 bajtow lepiej liczbe REAL8 
umiescic na stosie lub w jakiejs 8 bajtowej komorce pamieci.Hmm przegladajac Bajtki 
(btw jest to moim zdaniem najlepsze pismo komputerowe lat 80-tych) natknalem sie na inny
sposob konwersji do postaci F a oto on (Bajtek grudzien 1988 i styczen 1989 'Zapis liczb 
w pamieci RAM ZX SPECTRUM' Piotr Sumara) :
                 wiec    R=M*2^E      (R - liczba rzeczywista)
                         R/M = 2^E    / nakladamy logarytm przy podstawie 2
                        log(2)(R/M)=E  / przyjmijmy na moment ze M=1/2 wiec

                        INT{log(2)(R*2)}=E  /i niech E bedzie liczba calkowita 
					  INT{x} oznacza czesc calkowita z liczby x
               w tym momencie mamy E-calkowite , wyliczamy wyrazenie K
                         K=2^E
		i obliczamy mantyse M wedlug rownania
                         M=R/K
co ciekawe mantysa M jest zawsze wieksza lub co najmniej rowna 0,5 dziesietnie to 0.1 
binarnie (i mniejsza od 1). Jezeli zatem kazda mantysa posiada skladnik 0.5 (dec) czyli 
0.1 (bin), to znaczy, ze w zapisie binarnym mantysy pierwszym znakiem po przecinku jest 
zawsze "1" z tego wynika, ze nie trzeba kodowac owej jedynki - oszczedzamy wiec 1 bit !
co ciekawe w ZXSpectrumie liczba zmiennoprzecinkowa jest zapisywana na pieciu bajtach
4 bajty na mantyse (ten jeden bit zaoszczedzony na znak) i jeden bajt na wykladnik E,
ktory jest zapisywany w kodzie z obciazeniem - czyli podobnie jak na dzisiejszych
koprocesorach. W porzadku zanim przystapimy do zakodowania tego problemu zaznajomie was
z operacja skalowania, otoz operacja skalowania polega na przesuwaniu przecinka w liczbie.
Normalnie operujemy systemem dziesietnym (=10) wiec operacja skalowania na przyklad o jedno 
mniejsce w prawo odpowiada operacji pomnozenia owej liczby przez 10, jesli skalujemy o dwa
w prawo to tak naprawde mnozymy liczbe przez 100 czyli 10^2, jesli skalujemy na przyklad 
o 6 w lewo to dzielimy przez 10^6. Podobnie sprawa wyglada z operacjami skalowania na 
koprocesorze, gdzie przewodnim systemem jest system dwojkowy (=2) , czyli podobnie jesli
skalujemy liczbe o 2 w lewo to tak naprawde dzielimy ja przez 2^2 , jesli o 4 w prawo to 
mnozymy przez 2^4.Wrocmy do problemu, w logarytmie mamy wyrazenie R*2 czyli z tego wynika,
ze liczbe R znajdujaca sie na stosie mozemy przeskalowac o jedno niejsce w prawo podobnie
przy wyliczaniu wartosci K mozemy sie posluzyc skalowaniem, otoz jesli mielibysmy na stosie
wartosc 1 to jesli przeskalowalibysmy ja o E miejsc w prawo (E-liczba calkowita) otrzymalibysmy
wartosc 2^E :)) instrukcja koprocesora FSCALE wykonuje wlasnie operacje skalowania

			FLD1	- wprowadz wartosc 1 na stos koprocesora
			FLD [R] - wprowadz liczbe rzeczywista na stos koprocesora
			FSCALE  - R*2
			FYL2X   - oblicz wartosc st(1)*log(2)(st(0))
					st(1) = 1 , st(0) = R*2
			FIST [E]
			FILD [E]
			FLD1
			FSCALE  - 2^E=K
			FLD [R]
			FDIV    - M=R/K
Po przeprowadzeniu powyzszych operacji na stosie znajdzie sie liczba M

Powyzsze sposoby konwersji do postaci F mozna oczywiscie zastapic jedna instrukcja koprocesora
FXTRACT - wyciagniecie mantysy i wykladnika z wierzcholka stosu :)) ale przeciez nie o to 
w tym wszystkim chodzi :)))

Ok wiemy juz jak to dziala, lecz zapis F liczby nie jest dla nas przyjemny, gdyz jestesmy
przyzwyczajeni do systemu dziesietnego ... hmm system dziesietny , zobatrzmy wiec , czy
dalobysie zapisac liczbe w takiej oto postaci D=A*10^B  . Przyrownajmy wiec te dwie 
reprezentacje liczby
		F=D  ->   M*2^E = A*10^B
przyjmijmy, ze M=A wtedy otrzymujemy   2^E=10^B  logarytmujac obie strony rownania log o 
podstawie 10 otrzymamy
			log10(2^E)=log10(10^B)=B  
               czyli  B=log10(2^E)=E*log10(2)
Z tego wynika, ze majac liczbe E wyliczona z powyzszej procedurki i mnozac ja przez log10(2)
otrzymamy liczbe B - to da sie zrobic - instrukcja FLDL2T umieszcza ten logarytm w wierzcholku
stosu , wiec jesli wykonamy :

			FILD  [E]	wprowadzenie E na wierzcholek stosu
			FLDLGT		wprowadzenie logarytmu na stos
			FMULP		pomnozenie logarytmu i E

to na stosie koprocesora bedzie sie znajdowala liczba B - zostaje tylko (i az) sciagniecie
tej liczby ze stosu i zmiana jej na string w postaci liczby dziesietnej - czyli utknelismy
w miejscu , gdyz zadaniem naszym jest wlasnie sciagniecie liczby rzeczywistej ze stosu 
i przerobienie jej na taki string - zauwazmy, ze liczba B nie jest liczba calkowita ...
moznaby sciagnac ja ze stosu jako calkowita (FIST [B]) lecz wtedy popelniamy duzy blad
z uwagi na obciecie czesci ulamkowej ... no nic zaczalem wiec swoje poszukiwania od 
zagladniecia do dokumentacji intela - znalazlem tam gotowa a zarazem bardzo pogmatwana 
procedurke do konwersji liczb typu float na ASCII string - zaczalem ja przerabiac aby 
skompilowala sie w kodzie 32bitowym pod MASMem ... lecz znudzilo mi sie to poprawianie 
i zrezygnowalem... od poczatku mi sie to nie podobalo... Pomyslalem - przeciez mamy 
rowniez do dyspozycji 80-bitowa liczbe BCD, ktora jest prosta do obrobki ... na jakiej 
zasadzie dziala system BCD ?? otoz kazda cyfra w kodzie dziesietnym jest zakodowana na 
czterech bitach, maksymalna wartoscia zakodowana na czterech bitach jest 1111(bin)=15(dec) 
czyli mozemy zakodowac w ten sposob 16 znakow (razem z zerem) - w kodzie BCD wykorzystywanych 
jest dziesiec z szesnastu tych wartosci (spowodowane jest to tym , ze w kodzie dziesietnym 
mamy 10 cyfr :) czyli jesli mamy ciag bitow zakodowanych w kodzie BCD 

               1000 0011 0001 0010 to koduja one 
                 8    3    1    2  czyli liczbe 8312(dec)

Wracajac do tematu mamy 80bitowa liczbe BCD - wiemy , ze na bicie 79 zakodowany jest znak,
bity od 72 do 78 sa nieuzywane - co daje nam 72bity przeznaczone na liczbe BCD - czyli 
maksymalna iloscia cyfr, zakodowanych na 72bitach w kodzie BCD jest 72/4=18 (gdyz 4 bity
przeznaczone sa na zakodowanie jednej cyfry). Komenda FBSTP sciaga liczbe rzeczywista
ze stosu i zapisuje ja do pamieci w postaci upakowanej liczby dziesietnej (kod BCD).
Oto kawalek kodu wyciagniety z bartpack'a #5 (hi bart^cp :) i troszeczke przerobiony
przeze mnie ;)

FloatToBCD	PROC
	PUSH	esi
	PUSH	edi
	sub	esp, 10		;Zarezerwowanie miejsca na 80bitowa liczbe BCD
	fbstp	[esp]		;Sciagniecie liczby rzeczywistej jako BCD
	lea	edi, [InternalBufor+1]
	TEST    BYTE PTR [ESP+9],10000000b		;okresl znak (bit 79)
	JZ      @dodatnia
	MOV     BYTE PTR [EDI-1],'-'
	JMP     @wartosc
@dodatnia:
	MOV     BYTE PTR [EDI-1],'+'		
@wartosc:
	lea	esi, [esp+8]
	mov	ecx, 9
@petelka:
	mov al, [esi]		; xxxx xxxx AAAA BBBB
	dec esi
	rol ax, 12		; BBBB xxxx xxxx AAAA
	rol ah, 4		; xxxx BBBB xxxx AAAA
	and ax, 0f0fh		; 0000 BBBB 0000 AAAA
	add ax, 3030h		; 0003 BBBB 0003 AAAA = 3B3Ah
	mov [edi], ax
	add edi, 2
	dec ecx
	JNZ @petelka
	add	esp, 10
	pop	edi
	pop	esi
	ret
FloatToBCD	ENDP

Powyzsza procedurka sciaga czesc calkowita liczby rzeczywistej ze stosu natomiast nie
sciaga czesci ulamkowej :(( zastanowmy sie przez chwile na jakiej zasadzie moze dzialac
algorytm sciagajacy czesc ulamkowa danej liczby, otoz jesli mamy na stosie koprocesora
liczbe mniejsza od calosci (jesli mamy liczbe wieksza od calosci to pobieramy czesc calkowita
a nastepnie odejmujemy ta pobrana czesc calkowita od liczby) to mozemy przeprowadzic
operacje skalowania tej liczby przesuwajac przecinek w prawo w bazie =10 (nie mozemy 
wykorzystac instrukcji FSCALE gdyz przesuwa ona przecinek w systemie o bazie =2 !)
a nastepnie pobrac otrzymana liczbe powyzszym algorytmem zapisujac wynik jej dzialania
po przecinku w stringu - przesuwanie przecinka w prawo o P pozycji w systemie o bazie 10 
to nic innego jak mnozenie liczby przez 10 do do potegi P. Czyli jesli bedziemy chcieli
uzyskac dokladnosc do trzeciego miejsca po przecinku pomnozymi liczbe ulamkowa przez 1000
a nastepnie pobierzemy ja jako liczbe BCD. Mozna rowniez do tematu podejsc z innej strony
otoz w petli :
1) mnozyc przez 10
2) pobierac liczbe C jako calkowita (FIST)
3) odejmowac liczbe calkowita C od liczby rzeczywistej bedacej na stosie
4) skok do punktu 1

Tak skonstruowany algorytm (punkty od 1 do 4) daje nam mozliwosc konwersji liczby rzeczywistej
nie tylko na system dziesietny ale rowniez na system o dowolnej bazie (osemkowy, czworkowy,
piatkowy itd...) - w powyzszym alogrytmie =10 (punkt 1) zmieniajac  dla przykladu na 4 
(bedziemy mnozyc liczbe przez 4) bedziemy otrzymywali postac liczby w systemie czworkowym itd..


A oto przyklad procedurki przerabiajacej liczbe rzeczywista na string ASCII dzialajacy
na powyzszej zasadzie :

radix		dw 10
dokladnosc	EQU 4

FloatToString	PROC
	PUSH	esi
	PUSH	edi
	sub	esp, 10
	fld  	st(0)
	FNSTCW WORD PTR [ESP]
	OR BYTE PTR [ESP+1],1000b
	AND BYTE PTR [ESP+1],11111011b
	FLDCW  WORD PTR [ESP]			;ustaw Round Up - hmm przy sciaganiu przez
	fbstp	[esp]				;fdstp liczbe BCD zaokragla w dol :)
	fbld	[esp]
	FSUBP	st(1),st(0)
	lea	esi, [esp+8]
	lea	edi, [InternalBufor]
	TEST    BYTE PTR [ESI+1],10000000b		;okresl znak (bit 79)
	JZ      @dodatnia
	MOV     BYTE PTR [EDI],'-'
	JMP     @wartosc
@dodatnia:
	MOV     BYTE PTR [EDI],'+'		
@wartosc:
	INC 	EDI
	mov	ecx, 9
.REPEAT							;przelicz wartosc BCD na string
	mov al, [esi]
	dec esi
	rol ax, 12
	rol ah, 4
	and ax, 0f0fh
	add ax, 3030h
	mov [edi], ax
	add edi, 2
	dec ecx
.UNTIL ZERO?
	FNSTCW WORD PTR [EDI]				;Ustaw znacznik RC tak aby
	OR BYTE PTR [EDI+1],100b			;koprocesor zaokraglal liczby
	AND BYTE PTR [EDI+1],11110111b			;w dol
	FLDCW  WORD PTR [EDI]
	MOV BYTE PTR [EDI],','
	INC EDI
	MOV ECX,dokladnosc
	FABS						;zmien znak na dodatni
.REPEAT	
	FILD 	radix
	FMUL
	FIST WORD PTR [EDI]
	FILD WORD PTR [EDI]				;i pokolei zdejmuj wartosci ze stosu
	FSUBP	st(1),st(0)				;odejmujac je zarazem od liczby
	ADD BYTE PTR [EDI],30h				;zmien na kod ASCII ;)
	INC EDI
	DEC ECX
.UNTIL ZERO?
	MOV BYTE PTR [EDI],0		;Na koniec stringa wpisz 0
	fbstp	[esp]			;zdejmij liczbe z wirzcholka stosu (0) 
					;i zapeln normalny stos zerami ;)
	add	esp, 10
	pop	edi
	pop	esi
	ret
FloatToString	ENDP


Wywolanie tej procedurki z programu moze odbyc sie nastepujaco : 

.DATA
liczba	      dq -999.1231234
InternalBufor db 30 dup (0)
.CODE
FLD QWORD PTR [liczba]
Call FloatToString

Do tej pory milczaco zakladalem, ze koprocesor zawsze zaokragla liczby w dol - nie jest 
to prawda - mamy mozliwosc ustawienia koprocesora tak, aby zaokraglal liczby
	* w dol
        * w gore
        * do najblizszej calosci
Ustawia sie to wlasnie przez instrukcje FNSTCW i FLDCW

Powoli zblizamy sie do konca tego mojego wywodziku na temat arytmetyki i koprocesora.
Mysle, ze teraz juz wiecie co piszczy w koprocesorze, na koniec chcialbym zaproponowac
wam zabawe koprocesorem ale nie taka byle jaka :)) otoz przyjrzyjcie sie jeszcze raz 
procedurce Konwersja_REAL4_do_F na wejsciu w rejestrze EAX jest wartosc REAL4 sciagnieta
ze stosu koprocesora przy uzyciu nastepujacej sekwencji instrukcji :

			FIST REAL4 [Bufor]
			MOV EAX,[Bufor]

czy domyslacie sie juz do czego zmierzam ??? otoz przy wymyslaniu nastepnych procedurek
do crackme czy tez do programow uzytkowych sprobujcie polaczyc arytmetyke zmiennoprzecinkowa
z arytmetyka staloprzecinkowa troszke niekonwencjonalnie otoz potraktujcie dana bedaca 
w Buforze jako ciag bitow, na ktorym przeciez mozna wykonywac stare operacje oczywiscie 
przed zdjeciem liczby z koprocesora mozna wykonywac wszelkiego rodzaju operacje nie 
dostepne w arytmetyce staloprzecinkowej (log,sqrt) pomyslcie o tym .... 

						CoxoC/CookieCrK
					homepage : www.cookiecrk.org
Zrodla 
[0]	moj mozg !
[1]	"Arytmetyka Komputerow" Janusz Biernat (PWN 1996) 
[2]	"Everything You Always Wanted To Know About Math Coprocessors" (artykul	
	zaciagniety z netu)
[3]	"Borland Pascal 7.0" czesc 1c Andrzej Marciniak

