Tvůrce webu je i pro tebe! Postav třeba web. Bez grafika. Bez kodéra. Hned.
wz

Obsah / Utility / TEXTFORM / FltToTextBuf

Zdrojový kód: INCLUDE\UTIL\TEXTFORM.INC, UTIL\TEXTFORM.ASM

Související:

FltToTextBufN   Délka textu formátovaného desetinného čísla
MixSToTextBuf   Zformátování smíšeného desetinného čísla do bufferu, malé "e"
MixCToTextBuf   Zformátování smíšeného desetinného čísla do bufferu, velké "E"
FormToTextBuf   Zformátování textu do bufferu

FltToTextBuf - Délka formátovaného textu data a času

Funkce FltToTextBuf zkonvertuje desetinné číslo na text ve formátovaném tvaru bez exponentu.


; -----------------------------------------------------------------------------
;          Convert float number into text buffer in float point form
; -----------------------------------------------------------------------------
; INPUT:	ST0 = float number (it does not pop it from the FPU stack)
;		EBX = formatting parameters FORMPAR
;		ECX = pointer to nationality descriptor NATIONAL
;		ESI = remaining free space in buffer
;		EDI = destination buffer
; OUTPUT:	ESI = next remaining free space in buffer
;		EDI = next destination buffer
; NOTES:	It uses 2 more registers from FPU stack.
;		FPU should be in default state - 64 bits, round to nearest.
;		To destroy FPU register you can use "ffreep st0" instruction.
;		On AMD 1.6 GHz it takes approx. "10*decimal places+200" nsec.
; -----------------------------------------------------------------------------
; Local variables (ebp+N are read-only variables):
%define		FLTNat     ebp+8	; (4) pointer to nationality NATIONAL
%define		FLTForm    ebp-4	; (4) formatting parameters FORMPAR
%define		FLTLen     ebp-8	; (4) total length of text
%define		FLTThsnd   ebp-12	; (4) thousand separator counter
%define		FLTDec     ebp-16	; (4) decimal separator counter
%define		FLTIntDig  ebp-20	; (4) number of digits of integer part
%define		FLTDigits  ebp-24	; (4) total number of digits
%define		FLTDigNum  ebp-25	; (1) digit counter
%define		FLTSign    ebp-26	; (1) stored flags, bit 7 = sign
%define		FLTExp     ebp-28	; (2) float number, exponent
					;       (bit 0..14) and sign (bit 15)
%define		FLTMantH   ebp-32	; (4) float number, mantissa HIGH
%define		FLTMantL   ebp-36	; (4) float number, mantissa LOW
%define		FLTFloat   FLTMantL	; ...(10) float number

%define		FLTStack   36		; stack size

Na vstupu funkce obsahuje registr ST0 desetinné číslo ke konverzi, registr EBX obsahuje formátovací parametry FORMPAR (ne ukazatel), registr ECX ukazatel na popisovač národnostních informací NATIONAL, registr ESI čítač zbylého místa v cílovém bufferu a registr EDI ukazatel do cílového bufferu. Na výstupu z funkce jsou registry ESI a EDI posunuty na novou ukládací pozici. Funkce používá další 2 registry koprocesoru. Vstupní číslo v registru ST0 zůstává zachováno, neuvolňuje se ze zásobníku koprocesoru. K uvolnění čísla z registru ST0 lze použít instrukci "ffreep st0" volanou po ukončení funkce. Funkce předpokládá, že koprocesor je v implicitním nastavení - přesnost 64 bitů, zaokrouhlení k nejbližšímu.

Funkce používá lokální proměnné s bázovým registrem EBP. Proměnné s kladným offsetem (FLTNat) jsou použity jen pro čtení, jejich obsah není modifikován. FLTNat je ukazatel na národnostní informace NATIONAL (vstupní registr ECX). FLTForm jsou formátovací parametry FORMPAR (vstupní registr EBX). FLTLen je délka ukládaného textu. FLTThsnd je čítač k zobrazení oddělovače řádů. FLTDec je čítač k zobrazení oddělovače desetinných míst. FLTIntDig je počet číslic celočíselné části čísla. FLTDigits je celkový počet číslic. FLTDigNum je čítač číslic. FLTSign jsou příznaky - bit 7 označuje záporné znaménko čísla. FLTFloat je desetinné číslo v zásobníku a skládá se z částí FLTMantL - nižší dvojslovo mantisy, FLTMantH - vyšší dvojslovo mantisy a FLTExp - exponent. FLTStack je velikost lokálních proměnných v zásobníku.


; ------------- Macro - store decimal separator (destroys EAX)
; %1 = jump label if buffer is full

%macro		FLTDECIM 1

		dec	dword [FLTDec]	; decrement decimal separator
		jnz	%%L2		; not decimal separator
		test	byte [FLTForm+FORMPAR_Flags2],FORMFLAG2_Alt ; point?
		jz	%%L1		; not using decimal separator
		mov	eax,[FLTNat]	; EAX <- pointer to nationality
		mov	al,[eax+NAT_DecimalSep] ; AL <- decimal separator
		dec	esi		; decrease space counter
		stosb			; store decimal separator
		jz	short %1	; buffer is full
%%L1:		and	dword [FLTThsnd],byte 0 ; no thousand separator
%%L2:

%endmacro

Makro FLTDECIM uloží oddělovač desetinných míst do výstupního bufferu. Parametrem makra je návěští na které má program přejít, je-li výstupní buffer plný. Makro ničí registr EAX.

Na začátku makro čítá čítač FLTDec. Dosáhne-li nuly, má být oddělovač desetinných míst uložen. Testem příznaku FORMFLAG2_Alt se zjistí, zda má být oddělovač uložen. Pokud ano, načte se znak oddělovače z popisovače národnostních informací a uloží se do výstupního bufferu. Čítáním čítače volného místa ESI se ověří, zda nedošlo k zaplnění výstupního bufferu, pokud ano, funkce se ihned ukončí. Jinak se pokračuje vynulováním čítače k zobrazení oddělovače řádů, čímž se zajistí, že pro následující číslice se již oddělovač řádů nebude používat.


; ------------- Macro - store thousand separator (destroys EAX)
; %1 = jump label if buffer is full

%macro		FLTTHSND 1

		dec	dword [FLTThsnd] ; decrement thousand separator counter
		jnz	%%L1		; not thousand separator
		mov	eax,[FLTNat]	; EAX <- pointer to nationality
		mov	al,[eax+NAT_ThsndSep] ; AL <- thousand separator
		dec	esi		; decrease space counter
		stosb			; store one digit
		jz	short %1	; buffer is full
		mov	byte [FLTThsnd],3 ; new thousand separator
%%L1:

%endmacro

Makro FLTTHSND uloží oddělovač řádů do výstupního bufferu. Parametrem makra je návěští na které má program přejít, je-li výstupní buffer plný. Makro ničí registr EAX.

Na začátku makro čítá čítač FLTThsnd. Dosáhne-li nuly, má být oddělovač řádů uložen. Znak oddělovače se načte z popisovače národnostních informací a uloží se do výstupního bufferu. Čítáním čítače volného místa ESI se ověří, zda nedošlo k zaplnění výstupního bufferu, pokud ano, funkce se ihned ukončí. Jinak se pokračuje nastavením čítače na novou hodnotu 3.


; ------------- Check remaining free space in buffer

FltToTextBuf:	or	esi,esi		; check remaining space
		jle	short FltToTextBufN9 ; not enough free space

; ------------- Push registers

		push	eax		; push EAX
		push	ebx		; push EBX
		push	ecx		; push ECX pointer to nationality
		push	edx		; push EDX
		push	ebp		; push EBP
		mov	ebp,esp		; EBP <- push ESP
		sub	esp,FLTStack	; ESP <- create local variables

; ------------- Prepare local variables

		mov	[FLTForm],ebx	; formatting parameters FORMPAR

Na začátku funkce FltToTextBuf je nejdříve proveden test volného místa v cílovém bufferu. Není-li v cílovém bufferu volné místo, funkce se ihned ukončí.

Po úschově registrů se vytvoří v zásobníku místo pro lokální proměnné. Formátovací parametry se uloží z registru EBX do lokální proměnné FLTForm.


; ------------- Store number into stack

		fld	st0		; duplicate number
		fstp	tword [FLTFloat] ; store number into stack

; ------------- Load exponent (-> EDX)

		fwait			; synchronize FPU
		movzx	edx,word [FLTExp] ; EDX <- exponent
		mov	ch,dh		; CH <- sign
		and	ch,B7		; left only sign flag
		mov	[FLTSign],ch	; push sign flag (bit 7)

Nyní se připraví exponent konvertovaného čísla. Číslo se uloží do bufferu v zásobníku FLTFloat (nejdříve se musí zduplikovat, protože není k dispozici instrukce pro uložení čísla v rozšířené přesnosti bez uvolnění čísla ze zásobníku koprocesoru).

Exponent čísla FLTExp se načte do registru EDX. Do proměnné FLTSign se uchová znaménko čísla, současně se vynulují ostatní bity FLTSign.


; ------------- Absolute value (it uses copy of register)

		fld	st0		; duplicate number
		fabs			; absolute value
		and	dx,7fffh	; mask exponent (15 bits)
		jnz	FltToTextBuf2	; neither zero nor denormalized number

Konvertované číslo se zduplikuje (aby mohlo být modifikováno a přitom zůstalo vstupní číslo uchováno) a převede se na absolutní hodnotu. Vynulováním nejvyššího bitu v registru DX se vynuluje znaménkový bit. Je-li exponent v registru DX nulový, jedná se o speciální případ nuly nebo nenormalizovaného čísla.


; ============= Special cases

; ------------- Zero or denormalized number

		mov	eax,[FLTMantL]	; EAX <- mantissa LOW
		or	eax,[FLTMantH]	; check if mantissa is zero
		jnz	FltToTextBuf2	; not zero, it is denormalized number

; ------------- Zero

		ffreep	st0		; pop register from the FPU stack
		xor	ebx,ebx		; EBX <- zero decimal exponent
	       test byte [FLTForm+FORMPAR_Flags2],FORMFLAG2_Alt2+FORMFLAG2_Prec
		jz	FltToTextBuf5	; don't truncate
FltToTextBuf1:	mov	byte [FLTForm+FORMPAR_Prec],0 ; limit precision
		jmp	FltToTextBuf5

V případě nulového exponentu se provede test mantisy na nulu. Není-li mantisa nulová, jedná se o nenormalizované číslo (s exponentem menším než přibližně 1e-4931), takové číslo se bude dále zpracovávat běžným způsobem.

Jsou-li exponent i mantisa nulové, jedná se o nulu. Číslo je uvolněno ze zásobníku koprocesoru, do registru EBX je připraven nulový dekadický exponent a přeskočí se část rozdělující číslo na exponent a mantisu. V případě požadavku ořezání koncových nul se omezí přesnost na 0 desetinných číslic.


; ------------- Infinity (+1.#INF, -1.#INF) or non-number (+1.#NAN, -1.#NAN)

FltToTextBuf2:	cmp	dx,7fffh	; infinity or non-number?
		jne	FltToTextBuf3	; neither infinity nor non-number
		ffreep	st0		; pop register from the FPU stack
		xchg	eax,ecx		; AH <- sign flag (bit 7)
		mov	ecx,[FLTNat]	; ECX <- pointer to nationality
		lea	edx,[FLTFloat]	; EDX <- pointer to float number
		call	FloatInfNan	; store text
		jz	FltToTextBuf9	; buffer is full
		jmp	FltToTextBuf86	; store trailing spaces

Má-li exponent v registru DX hodnotu 7FFFh, jedná se o další zvláštní případ - nekonečno nebo nedefinované číslo. Číslo se uvolní ze zásobníku koprocesoru, do registru AH (bit 7) se připraví příznak záporného znaménka, do registru ECX se připraví ukazatel na národnostní informace, do registru EDX se připraví ukazatel na buffer čísla v zásobníku a voláním funkce FloatInfNan se dekóduje text speciálního případu do výstupního bufferu. Je-li navrácen příznak ZY, došlo k přetečení výstupního bufferu a funkce se ihned ukončí. Jinak se pokračuje uložením koncových mezer (v registru EAX je navrácena délka textu).


; ============= Convert number to digits

; ------------- Split number to mantissa and exponent (here is EDX=bin. exp.)

FltToTextBuf3:	lea	ecx,[FLTFloat]	; ECX <- pointer to float in stack
		call	FloatSplit	; split number to mantissa and exponent

Dále bude pokračovat obsluha rozložení čísla na číslice. Pomocí funkce FloatSplit je číslo rozloženo na mantisu a dekadický exponent. Na vstupu funkce je v registru EDX binární exponent čísla, registr ECX ukazuje na buffer čísla v zásobníku a v registru ST0 je absolutní hodnota konvertovaného čísla. Funkce navrací v registru EBX dekadický exponent a v registru ST0 mantisu čísla (normalizovanou do rozsahu 1 včetně až 10 vyjma).


;-------------- Round, denormalize and convert mantissa to BCD (EBX = exponent)

		movzx	eax,byte [FLTForm+FORMPAR_Prec] ; EAX <- precision
		test	byte [FLTForm+FORMPAR_Flags2],FORMFLAG2_Prec ; precis.?
		jz	FltToTextBuf4	; normal precision (=fractional digits)
		dec	eax		; without first digit
		jns	FltToTextBuf42	; digits are OK
		jmp	FltToTextBuf41	; limit to 0
FltToTextBuf4:	add	eax,ebx		; EAX <- position of last digit
		jg	FltToTextBuf42	; digits are OK
FltToTextBuf41:	xor	eax,eax		; EAX <- 0, digits are out of display
FltToTextBuf42:	lea	ecx,[FLTFloat]	; ECX <- pointer to float in stack
		bt	dword [FLTForm],FORMTYPE_Cap_b ; round always down?
		call	FloatMantBCD	; process mantissa

Číslo bude pomocí funkce FloatMantBCD zaokrouhleno na poslední číslici a rozloženo na jednotlivé číslice. Před voláním funkce je do registru EAX připravena požadovaná přesnost.

Není-li nastaven příznak FORMFLAG2_Prec, má přesnost význam počtu číslic desetinné části. Přičtením exponentu se obdrží počet zobrazených platných číslic - 1. Podteče-li počet číslic pod 0, číslo leží celé za koncem zobrazené desetinné části, počet číslic se omezí na 0.

Je-li zvolena alternativní přesnost FORMFLAG2_Prec, má přesnost význam počtu významných číslic. Odečtením 1 se provede korekce pro funkci FloatMantBCD (která očekává počet číslic bez první číslice), došlo-li podtečení pod 0, omezí se počet číslic na 0.

Do registru ECX se připraví ukazatel na buffer čísla v zásobníku. Testem příznaku FORMTYPE_Cap_b je funkci předán příznak, zda se má vypnout zaokrouhlování poslední zobrazené číslice. Voláním FloatMantBCD se mantisa převede na BCD číslice.


; ------------- Use precision digits (here is EBX = decimal exponent)

		lea	edx,[FLTFloat+9] ; EDX <- last digit
		test	byte [FLTForm+FORMPAR_Flags2],FORMFLAG2_Prec ; precis.?
		jz	FltToTextBuf43	; normal precision (=fractional digits)
		call	FloatTrunc	; truncate trailing zeros
		mov	[FLTForm+FORMPAR_Prec],cl ; set new precision
		sub	ecx,ebx		; ECX <- maximal precision
		jg	FltToTextBuf52	; precision is positive
		xor	ecx,ecx		; ECX <- limit precision
		jmp	FltToTextBuf52

; ------------- Truncate trailing zeros (here is EBX = decimal exponent)

FltToTextBuf43:	test	byte [FLTForm+FORMPAR_Flags2],FORMFLAG2_Alt2 ; trunc.?
		jz	FltToTextBuf5	; don't truncate
		movzx	eax,byte [FLTForm+FORMPAR_Prec] ; EAX <- precision
		add	eax,ebx		; check if number is out of display
		jl	FltToTextBuf1	; number is not visible
		call	FloatTrunc	; truncate trailing zeros
		sub	ecx,ebx		; ECX <- maximal precision
		jg	FltToTextBuf44	; precision is positive
		xor	ecx,ecx		; ECX <- limit precision
FltToTextBuf44:	movzx	eax,byte [FLTForm+FORMPAR_Prec] ; EAX <- precision
		cmp	ecx,eax		; check precision
		jae	FltToTextBuf5	; precision is OK
		mov	[FLTForm+FORMPAR_Prec],cl ; limit precision

Bude následovat ořezání nevýznamných koncových nul (s využitím funkce FloatTrunc). Do registru EDX se připraví ukazatel na poslední bajt mantisy v BCD tvaru a rozliší se, jakým způsobem má být interpretována požadovaná přesnost.

Je-li nastaven příznakový bit FORMFLAG2_Prec, má přesnost význam počtu významných číslic. Přesnost navrácená funkcí FloatTrunc (tedy počet významných číslic - 1) se použije jako nová požadovaná přesnost k zápisu čísla, tj. počet dekódovaných významných číslic - 1. Odečtením exponentu v registru EBX se obdrží počet číslic desetinné části. Ponechá se v registru ECX a při podtečení pod nulu (tj. číslo je příliš vlevo) se omezí na 0.

Není-li příznakový bit FORMFLAG2_Prec nastaven, má přesnost význam počtu desetinných míst. Není-li požadováno odstranění nevýznamných koncových nul, následující úsek se přeskočí. Jinak se pokračuje přípravou požadované přesnosti do registru EAX. Přičtením exponentu z registru EBX se zjistí vzdálenost první významné číslice od poslední číslice desetinné části. Podteče-li vzdálenost pod nulu, leží číslo za koncem desetinné části a tedy se nezobrazí. V tom případě se nahradí číslem nula bez desetinných míst.

Zavoláním funkce FloatTrunc se zjistí počet významných číslic - 1 (registr ECX). Odečtením exponentu z registru EBX se obdrží skutečný počet číslic desetinné části. V případě podtečení, tj. leží-li číslo příliš vlevo, se omezí na nulu. Do registru EAX se načte požadovaná přesnost a porovná se se skutečnou přesností. Je-li skutečná přesnost nižší, nahradí se jí požadovaná přesnost v proměnné formátovacích parametrů.


; ============= Get text length (here is EBX = decimal exponent)

; ------------- Add sign (-> EAX)

FltToTextBuf5:	movzx	ecx,byte [FLTForm+FORMPAR_Prec]	; ECX <- num. of digits
FltToTextBuf52:	mov	al,[FLTSign]	; AL <- sign (bit 7)
		mov	ah,[FLTForm+FORMPAR_Flags1] ; AH <- flags 1
		and	ax,B7 + (FORMFLAG1_Sign + FORMFLAG1_Space)*256
		setnz	al		; AL <- 1 if sign, else 0
		movzx	eax,al		; EAX <- 1 if sign, else 0

; ------------- Add decimal point and precision (-> ECX)

		mov	[FLTDigits],ecx	; prepare total number of digits
		jecxz	FltToTextBuf62	; no digits after decimal point
		bts	dword [FLTForm],FORMFLAG_Alt_b ; set dec. point flag
FltToTextBuf62:	bt	dword [FLTForm],FORMFLAG_Alt_b ; use decimal point?
		adc	ecx,eax		; ECX <- add decimal point and sign

V další části bude zjištěna délka textu dekódovaného čísla. Do registru ECX se připraví požadovaná přesnost představující počet číslic za desetinnou tečkou. V případě alternativní přesnosti se tato instrukce přeskočí s registrem ECX již připraveným.

Do registru AL se připraví znaménkový bit, do registru AH formátovací příznaky. Je-li znaménkový bit nastaven (bude znaménko "-") nebo je-li nastaven požadavek vynuceného znaménka (bude znaménko "+") nebo je-li nastaven požadavek vynucené mezery, připraví se do registru EAX číslo 1 jako délka textu znaménka, jinak bude obsah registru EAX nulový.

Obsah registru ECX, představující počet číslic za oddělovačem desetinných míst, se uloží do proměnné FLTDigits, kam bude později přičten počet číslic celočíselné části. Není-li počet číslic desetinné části nulový, nastaví se vynuceně příznak požadavku uložení oddělovače desetinných míst. Je-li příznak FORMFLAG_Alt_b nastaven, je požadováno uložení oddělovače desetinných míst, délka textu ve střadači ECX se zvýší o 1. Současně se přičte délka textu znaménka (která může být 0 nebo 1).


; ------------- Prepare minimal integer part (-> EAX)

		xor	eax,eax		; EAX <- 0
		or	ebx,ebx		; is exponent negative?
		sets	al		; AL <- 1 if < 1.0, 0 if >= 1.0
		dec	eax		; EAX <- 0 if < 1.0, -1 if >= 1.0
		and	eax,ebx		; EAX <- 0 if < 1.0, exponent if >= 1.0
		inc	eax		; EAX <- add first digit

Délka celočíselné části odpovídá kladné hodnotě exponentu zvýšené o 1. V případě záporného exponentu bude hodnota exponentu omezena na 0. Namísto testu se skokovou instrukcí se použije rychlejší registrová operace. Testem exponentu v registru EBX se zjistí, zda je exponent záporný. Pokud ano, nastavi se obsah registru AL na 1. Pokud ne, bude obsah registru AL nulový.

Dekrementací EAX se připraví do EAX maska, která je nulová v případě záporného exponentu, v případě nezáporného exponentu je 0FFFFFFFFh. Zamaskováním s exponentem v registru EBX se v registru EAX obdrží hodnota exponentu omezená na nulu při záporném exponentu. Po inkrementaci údaje bude registr EAX obsahovat počet číslic celočíselné části.


; ------------- Check if use zeros instead of spaces

		test	byte [FLTForm+FORMPAR_Flags2],FORMFLAG2_Zero ; zero?
		jz	FltToTextBuf64	; no zeros

; ------------- Prepare minimal integer part if use zeros (-> EDX)

		movzx	edx,byte [FLTForm+FORMPAR_Width] ; EDX <- minimal width
		sub	edx,ecx		; EDX <- without sign and fractional
		jle	FltToTextBuf64	; not enough space

; ------------- Check if use thousand separator

		test	byte [FLTForm+FORMPAR_Flags2],FORMFLAG2_Thsnd ; thsnd?
		jz	FltToTextBuf63	; not using thousand separator

; ------------- Number of digits without thousand separators (-> EDX)

		inc	edx		; EDX <- width + 1
		imul	edx,edx,3	; multiply with 3
		shr	edx,2		; EDX <- /4, max. number of digits

; ------------- New minimal number of integer digits (-> EAX)

FltToTextBuf63:	cmp	eax,edx		; check minimal number of digits
		ja	FltToTextBuf64	; number of digits is OK
		xchg	eax,edx		; EAX <- new number of digits
FltToTextBuf64:	mov	[FLTIntDig],eax	; store number of digits of integer
		add	[FLTDigits],eax	; set total number of digits
		mov	[FLTDec],eax	; init decimal separator counter

Není-li nastaven příznak FORMFLAG2_Zero, výpočet počtu číslic celočíselné části je hotov a může se přejít k uložení výsledku. Jinak se pokračuje zjištením minimálního počtu číslic vyplývajícího z požadované minimální šířky pole s číslem.

Do registru EDX je načtena požadovaná minimální šířka pole s číslem. Po odečtení délky textu desetinné částí se znaménkem v registru ECX se obdrží zbývající šířka pro celočíselnou část. Dál má smysl pokračovat jen je-li zbývající šířka kladná.

Není-li nastaven příznakový bit FORMFLAG2_Thsnd, nepoužijí se oddělovače řádů a registr EDX již přímo obsahuje počet číslic. Pokud se oddělovače řádů mají použít, je nutno odečíst znaky připadající na oddělovače řádů. Inkrementací šířky pole v registru EDX se provede korekce pro započtení první číslice. Vynásobením číslem 3/4 se v registru EDX obdrží počet číslic bez oddělovačů řádů.

Po výpočtu minimálního počtu číslic celočíselné části, vyžadovaného doplněním nul zleva, se minimální počet číslic porovná se skutečně zjištěným počtem číslic v registru EAX a je-li větší, použije se namísto skutečného počtu číslic.

Zjištěná délka celočíselné části se uloží jednak do proměnné délky celočíselné části FLTIntDig, přičte se k celkovému počtu číslic FLTDigits a nastaví se jí čítač pro zobrazení oddělovače desetinných míst FLTDec.


; ------------- Add thousand separators and prepare thousand counter (-> EDX)

		xor	edx,edx		; EDX <- 0, no thousand counter
		test	byte [FLTForm+FORMPAR_Flags2],FORMFLAG2_Thsnd ; thsnd?
		jz	FltToTextBuf66	; not using thousand separator
		lea	edx,[eax-1]	; EDX <- number of digits - 1
		imul	edx,edx,43691	; EDX <- (digits-1) * 20000h/3
		shr	edx,17		; EDX <- number of separators
		add	eax,edx		; EAX <- add thousand separators
		shl	edx,2		; EDX <- characters in whole groups
		neg	edx		; EDX <- -characters in whole groups
		add	edx,eax		; EDX <- digits in first group
FltToTextBuf66:	add	eax,ecx		; EAX <- add sign and fractional part
		mov	[FLTThsnd],edx	; set thousand separator counter
		mov	[FLTLen],eax	; set length of text
		xchg	eax,edx		; EDX <- length of text

Dále je potřeba připravit čítač k zobrazení oddělovače řádů. Nejsou-li oddělovače řádů požadovány (tj. není nastaven příznak FORMFLAG2_Thsnd), bude čítač k zobrazení oddělovače řádů vynulován a délka textu bude odpovídat součtu délky celočíselné části a desetinné části se znaménkem a oddělovačem destinných míst. Jsou-li oddělovače řádů požadovány, je potřeba zjistit jejich počet z počtu číslic celočíselné části.

Do registru EDX se připraví počet číslic celočíselné části snížený o první číslici, která je vždy vyžadována před prvním oddělovačem. Vydělením počtu číslic třemi se obdrží počet celých skupin číslic po 3 znacích, které budou odpovídat počtu oddělovačů řádů. Namísto operace dělení se použije rychlejší operace násobení převrácenou hodnotou. Výsledek se obdrží v registru EDX a přičtením k registru EAX se opraví údaj délky celočíselné části.

Při přípravě čítače pro první oddělovač řádů se vynásobením počtu oddělovačů čtyřmi obdrží počet znaků v celých skupinách číslic (tj. 1 znak oddělovače a 3 znaky číslic). Odečtením od počtu číslic celočíselné části se obdrží počet číslic před prvním oddělovačem, což je čítač k zobrazení prvního oddělovače.

Zjištěná hodnota čítače k zobrazení prvního oddělovače řádů se uloží do proměnné FLTThsnd. Po sečtení celočíselné a desetinné části se obdrží celková délka textu čísla, která se uloží do proměnné FLTLen a současně do registru EDX.


; ============= Decode spaces, sign, zeros (EDX=text length, EBX=exponent)

; ------------- Store leading spaces

		test	byte [FLTForm+FORMPAR_Flags1],FORMFLAG1_Left ; left?
		jnz	FltToTextBuf72	; left-justify, no spaces
		movzx	ecx,byte [FLTForm+FORMPAR_Width] ; ECX <- minimal width
		sub	ecx,edx		; ECX <- remaining spaces
		jle	FltToTextBuf72	; no spaces left
		test	byte [FLTForm+FORMPAR_Flags2],FORMFLAG2_Cent ; center?
		jz	FltToTextBuf7	; no, right-justify
		shr	ecx,1		; ECX <- spaces / 2, round down
		jz	FltToTextBuf72	; no spaces left
FltToTextBuf7:	mov	al," "		; AL <- space
FltToTextBuf71:	dec	esi		; decrease space counter
		stosb			; store one space
		loopnz	FltToTextBuf71	; next character
		jz	short FltToTextBuf759 ; buffer is full

Všechny potřebné informace jsou nyní již k dispozici a proto může být zahájeno dekódování čísla do výstupního bufferu.

Není-li číslo zarovnáno doleva (příznak FORMFLAG1_Left), je nutno před číslo doplnit úvodní mezery. Do registru ECX se připraví z parametru FORMPAR_Width požadovaná šířka pole. Odečtením délky textu z registru EDX se obdrží zbývající šířka pro okraje. Má-li být text centrován, použije se pouze polovina mezer se zaokrouhlením dolů.

V cyklu se ukládají znaky mezer do cílového bufferu. Počet mezer je v registru ECX. Dekrementací registru ESI se ověřuje, zda bude v bufferu místo pro další znak. Pokud čítač ESI dosáhne nuly, dosáhlo se konce cílového bufferu a funkce se ukončí.


; ------------- Store sign

FltToTextBuf72:	mov	al,"-"		; AL <- negative sign
		test	byte [FLTSign],B7 ; negative number?
		jnz	FltToTextBuf73	; negative number
		test	byte [FLTForm+FORMPAR_Flags1],FORMFLAG1_Sign ; sign?
		mov	al,"+"		; AL <- positive sign
		jnz	FltToTextBuf73	; use positive sign
		test	byte [FLTForm+FORMPAR_Flags1],FORMFLAG1_Space ; space?
		jz	FltToTextBuf74	; no space
		mov	al," "		; AL <- space
FltToTextBuf73:	dec	esi		; decrease space counter
		stosb			; store sign
		jz	short FltToTextBuf759 ; buffer is full

Před číslo se uloží znaménko. Je-li číslo záporné, uloží se znaménko mínus "-". Je-li znaménko vynucené (příznak FORMFLAG1_Sign), uloží se znaménko plus "+". Má-li být mezera místo kladného znaménka (příznak FORMFLAG1_Space), uloží se znak mezery " ". Jinak se žádný znak neuloží. Pokud čítač ESI volného místa v cílovém bufferu dosáhl nuly, funkce se ihned ukončí.


; ------------- Store leading zeros

FltToTextBuf74:	mov	ecx,[FLTIntDig]	; ECX <- digits in integer part
		dec	ecx		; without first digit
		sub	ecx,ebx		; ECX <- rest of digits
		jle	FltToTextBuf76	; no zeros
		sub	[FLTDigits],ecx	; decrease total number of digits
		jns	FltToTextBuf75	; number of digits is OK
		xor	eax,eax		; EAX <- 0
		xchg	eax,[FLTDigits]	; no digits remain
		add	ecx,eax		; EXC <- decrease number of digits
		jle	FltToTextBuf76	; no zeros
FltToTextBuf75:	mov	al,"0"		; AL <- zero
		dec	esi		; decrease space counter
		stosb			; store one digit
FltToTextBuf759:jz	FltToTextBuf9	; buffer is full
		FLTDECIM FltToTextBuf759 ; store decimal separator
		FLTTHSND FltToTextBuf759 ; store thousand separator
		loop	FltToTextBuf75	; next character

Před první platnou číslici budou uloženy nuly vyžádané buď vyšší šířkou pole (s nastaveným požadavkem uložení nul namísto mezer) nebo nulami před první platnou číslicí (je-li exponent záporný).

Do registru ECX se z proměnné FLTIntDig připraví počet číslic celočíselné části čísla. Odečtením dekadického exponentu z registru EBX a hodnoty 1 pro první číslici zůstane v registru ECX počet nul, které mají být umístěny na začátek čísla. Není-li počet nul kladné číslo, nebude nic uloženo.

Počet nul se odečte od celkového počtu číslic FLDDigits. Pokud došlo k podtečení počtu číslic, celkový počet číslic se vynuluje a opraví se počet nul v registru ECX.

Během ukládání úvodních nul se dekrementací čítače v registru ESI kontroluje volné místo v cílovém bufferu. Pokud nastane zaplnění cílového bufferu, funkce se ihned ukončí.

Po uložení znaku nuly je pomocí makra FLTDECIM uložen znak oddělovače desetinných míst, pomocí makra FLTTHSND uložen znak oddělovače řádů a čítáním registru ECX se pokračuje v ukládání dalšího znaku nuly.


; ============= Decode mantissa (here is EBX=decimal exponent)

FltToTextBuf76:	lea	edx,[FLTFloat]	; EDX <- pointer to mantissa

; ------------- Prepare number of digits of mantissa (-> CL)

		movzx	ecx,byte [FLTForm+FORMPAR_Prec] ; ECX <- precision
		inc	ecx		; add first digit
		test	byte [FLTForm+FORMPAR_Flags2],FORMFLAG2_Prec ; precis.?
		jnz	FltToTextBuf766	; use full precision
		add	ecx,ebx		; ECX <- add exponent
		jle	FltToTextBuf79	; no digits
FltToTextBuf766:cmp	ecx,19		; check maximal number of digits
		jb	FltToTextBuf77	; number of digits is OK
		xor	ecx,ecx		; ECX <- 0
		mov	cl,19		; ECX <- limit number of digits
FltToTextBuf77:	sub	[FLTDigits],ecx	; decrease total number of digits
		mov	[FLTDigNum],cl	; prepare number of digits

Bude následovat dekódování významných číslic mantisy. Do registru EDX se připraví ukazatel na začátek mantisy v zásobníku. Je-li nastaven přepínač FORMFLAG2_Prec, bude počet ukládaných číslic odpovídat údaji přesnosti FORMPAR_Prec zvýšenému o 1.

Není-li příznak nastaven, odpovídá údaj přesnosti počtu číslic za desetinnou tečkou, počet číslic mantisy se tedy obdrží přičtením exponentu. Nebude-li získaný počet číslic kladným číslem, přeskočí se obsluha dekódování mantisy. Počet číslic k dekódování mantisy se omezí na 19 a odečte se od celkového počtu číslic.


; ------------- Store mantissa - first digit

FltToTextBuf78:	mov	al,[edx]	; AL <- first digit
		and	al,0fh		; AL <- nask digit
		add	al,"0"		; AL <- convert to character
		dec	esi		; decrease space counter
		stosb			; store one digit
		jz	short FltToTextBuf7B ; buffer is full
		FLTDECIM FltToTextBuf7B ; store decimal separator
		FLTTHSND FltToTextBuf7B ; store thousand separator
		dec	byte [FLTDigNum] ; decrease digit counter
		jz	FltToTextBuf79	; no next character

; ------------- Store mantissa - second digit

		mov	al,[edx]	; AL <- 2 digits
		shr	al,4		; AL <- mask digit
		add	al,"0"		; AL <- convert to character + carry
		dec	esi		; decrease space counter
		stosb			; store one digit
		jz	short FltToTextBuf7B ; buffer is full
		FLTDECIM FltToTextBuf7B	; store decimal separator
		FLTTHSND FltToTextBuf7B	; store thousand separator
		inc	edx		; EDX <- shift pointer
		dec	byte [FLTDigNum] ; decrease digit counter
		jnz	FltToTextBuf78	; next character

Při dekódování mantiry se uloží nejdříve číslice z nižší tetrády bajtu mantisy a poté číslice z vyšší tetrády. Během ukládání se sleduje volné místo v cílovém bufferu čítáním registru ESI a pokud dojde k zaplnění cílového bufferu, funkce se ihned ukončí.

Po uložení číslice do bufferu je pomocí makra FLTDECIM uložen oddělovač desetinných míst a pomocí makra FLTTHSND oddělovač řádů. V proměnné FLTDigNum jsou čítány číslice k uložení.


; ------------- Store trailing zeros

FltToTextBuf79:	mov	ecx,[FLTDigits]	; ECX <- remaining digits
		jecxz	FltToTextBuf8	; no digits left
FltToTextBuf7A:	mov	al,"0"		; AL <- zero
		dec	esi		; decrease space counter
		stosb			; store one digit
FltToTextBuf7B:	jz	short FltToTextBuf9 ; buffer is full
		FLTDECIM FltToTextBuf9	; store decimal separator
		FLTTHSND FltToTextBuf9	; store thousand separator
		loop	FltToTextBuf7A	; next character

Následuje uložení koncových nul. Nuly se uloží v případě, že v proměnné FLTDigits zbývají ještě nějaké neuložené číslice. Pokud během ukládání číslic čítač ESI volného místa v cílovém bufferu dosáhne nuly, funkce se ihned ukončí.

Po uložení nuly do bufferu je pomocí makra FLTDECIM uložen oddělovač desetinných míst a pomocí makra FLTTHSND oddělovač řádů. V registru ECX jsou čítány nuly k uložení.


; ------------- Store trailing spaces

FltToTextBuf8:	test	byte [FLTForm+FORMPAR_Flags2],FORMFLAG2_Zero ; zeros?
		jnz	short FltToTextBuf9 ; zeros, no spaces
		mov	eax,[FLTLen]	; EAX <- text length
FltToTextBuf86:	movzx	ecx,byte [FLTForm+FORMPAR_Width] ; ECX <- min. width
		sub	ecx,eax		; ECX <- remaining spaces
		jle	short FltToTextBuf9 ; no spaces remain
		test	byte [FLTForm+FORMPAR_Flags1],FORMFLAG1_Left ; left?
		jnz	FltToTextBuf87	; left-justify
		test	byte [FLTForm+FORMPAR_Flags2],FORMFLAG2_Cent ; center?
		jz	short FltToTextBuf9 ; no center
		shr	ecx,1		; ECX <- spaces / 2
		adc	cl,ch		; ECX <- round up
		jz	short FltToTextBuf9 ; no spaces
FltToTextBuf87:	mov	al," "		; AL <- space
FltToTextBuf88:	stosb			; store one space
		dec	esi		; decrease space counter
		loopnz	FltToTextBuf88	; next character

; ------------- Pop registers

FltToTextBuf9:	mov	esp,ebp		; pop ESP
		pop	ebp		; pop EBP
		pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	ebx		; pop EBX
		pop	eax		; pop EAX
		ret

Poslední operací je uložení mezer za koncem čísla. Je-li požadováno použití nul namísto mezer, není potřeba již ukládat žádnou mezeru a funkce se ukončí. Jinak se do registru EAX načte délka textu (bez mezer) a do registru ECX požadovaná šířka pole, odečtením zůstane v registru ECX počet zbývajících mezer. Pokud žádné mezery pro okraje nezbyly, funkce se ukončí.

Je-li nastaven příznakový bit FORMFLAG1_Left, je číslo zarovnáno doleva, ihned se přejde k ukládání mezer za číslo. Není-li tento příznak nastaven a není-li nastaven ani příznakový bit FORMFLAG2_Cent, bylo číslo zarovnáno doprava a mezery tedy již byly uloženy dříve, takže se funkce ukončí. Jinak platí, že číslo je centrováno. Do registru ECX se připraví poloviční počet mezer, ale tentokrát se zaokrouhlí nahoru (aby se zachytila případná lichá mezera).

Následuje ukládání mezer do bufferu. Počet mezer je dán čítačem v registru ECX. Současně se sleduje zbývající volné místo v cílovém bufferu čítáním registru ESI. Pokud dosáhne nuly, bylo dosaženo konce bufferu a smyčka se ukončí.


Obsah / Utility / TEXTFORM / FltToTextBuf