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

Obsah / Utility / TEXTFORM / IntToTextBufN

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

Související:

IntToTextBuf   Zformátování čísla INT se znaménkem do bufferu
FormToTextBufN   Délka formátovaného textu

IntToTextBufN - Délka textu formátovaného dekadického čísla se znaménkem

Funkce IntToTextBufN zjistí délku formátovaného dekadického čísla se znaménkem v bufferu.


; -----------------------------------------------------------------------------
;          Convert signed INT number into text buffer - get text length
; -----------------------------------------------------------------------------
; INPUT:	EDX:EAX = number
;		EBX = formatting parameters FORMPAR
; OUTPUT:	EAX = text length
; -----------------------------------------------------------------------------

Na vstupu funkce obsahuje registrový pár EDX:EAX číslo se znaménkem ke konverzi a registr EBX formátovací parametry FORMPAR (ne ukazatel). Na výstupu je navrácena v registru EAX délka textu.


IntToTextBufN:	call	Int0ToTextBufN	; get text length without spaces
		push	ebx		; push EBX
		movzx	ebx,bh		; EBX <- minimal width of field
		cmp	ebx,eax		; check text length
		jb	IntToTextBufN8	; text length is OK
		xchg	eax,ebx		; EAX <- new text length
IntToTextBufN8:	pop	ebx		; pop EBX
IntToTextBufN9:	ret

Funkce IntToTextBufN volá interní funkci Int0ToTextBufN, která nezapočítává požadovanou šířku pole. Porovnáním s požadovanou minimální šířkou se ověří, zda je délka textu čísla větší než požadovaná minimální šířka. Pokud je menší, navrátí funkce požadovanou minimální šířku namísto délky textu bez mezer.


; -----------------------------------------------------------------------------
;  Convert signed INT number into text buffer - get text length without spaces
; -----------------------------------------------------------------------------
; INPUT:	EDX:EAX = number
;		EBX = formatting parameters FORMPAR
; OUTPUT:	EAX = text length
; -----------------------------------------------------------------------------

Funkce Int0ToTextBufN zjistí délku textu bez uvažování minimální šířky textu. Na vstupu funkce obsahuje registrový pár EDX:EAX číslo se znaménkem ke konverzi a registr EBX formátovací parametry FORMPAR (ne ukazatel). Na výstupu je navrácena v registru EAX délka textu.


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

Int0ToTextBufN:	push	edx		; push EDX
		push	edi		; push EDI
		push	ebp		; push EBP

; ------------- Always use sign

		xor	ebp,ebp		; EBP <- 0, no sign characters
		bt	ebx,FORMFLAG_Spc_b ; add space? (CY = yes)
		jc	Int0ToTextBfN1	; add space
		bt	ebx,FORMFLAG_Sign_b ; always use sign? (CY = yes)
Int0ToTextBfN1:	adc	ebp,ebp		; EBP <- 1 if always use sign

; ------------- Negative number

		or	edx,edx		; negative number?
		jns	Int0ToTextBfN2	; not negative number
		neg	eax		; EAX <- negative value LOW
		adc	edx,byte 0	; carry
		or	ebp,1		; length = 1
		neg	edx		; EDX <- negative number HIGH

Po úschově obsahu registrů EDX, EDI a EBP se připraví délka úvodní části před číslem. Obsah registru EBP bude nastaven na 1, pokud má být vždy zobrazeno znaménko nebo má být namísto znaménka zobrazena mezera, v ostatních případech se vynuluje.

Je-li požadované číslo ke konverzi negativní, převede se na absolutní hodnotu a délka úvodní části v registru EBP se nastaví na hodnotu 1 (1 znak pro záporné znaménko).


; ------------- Check if number is QWORD (here is ZY if EDX = 0)

Int0ToTextBfN2:	xchg	eax,edi		; EDI <- number LOW
		jnz	Int0ToTextBfN3	; number is QWORD

; ------------- Get number of bits in number LOW (-> AL)

		bsr	eax,edi		; AL <- highest bit in number LOW
		setz	ah		; AH <- 1 if EAX == 0, 0 if EAX != 0
		add	al,1		; AL <- number of bits in number LOW
		dec	ah		; AH <- 0 if EAX == 0, 0ffh if EAX != 0
		and	al,ah		; clear AL if number LOW is zero

; ------------- Get number of digits (-> EDX) (ln 10/ln 2=3.3219)

		mov	ah,78		; AH <- 100h / 3.3219 round up
		mul	ah		; AH <- aprox. number of digits
		movzx	edx,ah		; EDX <- aprox. number of digits
		cmp	[IntMul10+edx*4],edi ; check number LOW
		adc	dl,dh		; EDX <- number of digits
		jmp	Int0ToTextBfN4

Číslo LOW e uschová do registru EDI a testem registru EDX se rozliší, zda má číslo velikost DWORD (1 dvojslovo) nebo QWORD (2 dvojslova).

Má-li číslo rozměr DWORD, vyhledá se v registru EDI (nižší část čísla) nejvyšší pozice jedničkového bitu. Pokud byl obsah registru EDI nulový, nastaví se příznak ZF. To využije následující instrukce SETZ, která uloží do registru AH hodnotu 1 v případě nulového obsahu čísla, jinak do AH uloží 0. Přičtením 1 k AL se obdrží v registru AL počet bitů původního čísla LOW. Avšak pokud byl obsah čísla LOW nulový, je obsah registru AL nedefinovaný. Proto je AL zamaskován maskou vygenerovanou z příznaku ZF v registru AH. Výsledkem je, že registr AL obsahuje buď počet bitů v původní nižší části čísla nebo nulu pokud byla nižší část čísla nulová.

Počet bitů se přepočte na počet číslic a to tak, že se počet bitů nejdříve vynásobí poměrem dvojkového a dekadického logaritmu. Vznikne přibližný počet dekadických číslic, zaokrouhleno dolů, který se uloží do registru EDX. Stejně jako u funkce UDWToTextBufN se počet číslic upřesní pomocí tabulky IntMul10.


; ------------- Get number of bits in number HIGH (-> AL)

Int0ToTextBfN3:	bsr	eax,edx		; EAX <- highest bit in number HIGH
		add	al,32+1		; AL <- number of bits

; ------------- Get number of digits (-> EDX) (ln 10/ln 2=3.3219)

		mov	ah,78		; AH <- 100h / 3.3219 round up
		mul	ah		; AH <- aprox. number of digits
		movzx	eax,ah		; EAX <- aprox. number of digits
		sub	edi,[Int2Mul10+eax*8-10*8] ; check number LOW
		sbb	edx,[Int2Mul10+eax*8+4-10*8] ; check number HIGH
		cmc			; CY = number is not less
		adc	al,ah		; EAX <- number of digits
		xchg	eax,edx		; EDX <- number of digits

Má-li číslo rozměr QWORD (2 dvojslova), vyhledá se v registru EDX (vyšší část čísla) nejvyšší pozice jedničkového bitu. Obsah registru EDX nebyl nulový, proto není třeba obsluhovat případ nuly. Zvýšením pozice bitu o 32+1 bude registr AL obsahovat počet bitů čísla.

Zjištěný binární logaritmus bude převeden na dekadický logaritmus vydělením číslem ln 10 / ln 2 = 3,3219 (přibližně). Namísto dělení se použije rychlejší operace násobení převrácenou hodnotou, tj. číslem 100h / 3.3219 (zaokrouhleno nahoru). Po převzetí výsledku z registru AH se v registru EAX obdrží výsledný dekadický logaritmus (přesněji jeho celočíselná část).


; ------------- Table of multiple of 10 (1e10 to 1e19)

		align	8, db 0
Int2Mul10:	dd	540BE400h,2		; 1e10
		dd	4876E800h,17h		; 1e11
		dd	0D4A51000h,0E8h		; 1e12
		dd	4E72A000h,918h		; 1e13
		dd	107A4000h,5AF3h		; 1e14
		dd	0A4C68000h,38D7Eh	; 1e15
		dd	6FC10000h,2386F2h	; 1e16
		dd	5D8A0000h,1634578h	; 1e17
		dd	0A7640000h,0DE0B6B3h	; 1e18
		dd	89E80000h,8AC72304h	; 1e19

Přibližný počet číslic je již známý, ale údaj je potřeba ještě upřesnit, protože binární logaritmy mohou ležet přes hranici dekadických logaritmů. K tomu slouží tabulka Int2Mul10, která obsahuje dekadické mocniny, tedy čísla 1e10, 1e11 atd. Porovnáním původního čísla s hranicí podle vypočteného dekadického logaritmu čísla se upřesní, zda číslo leží nad hranicí nebo pod hranicí a pokud leží nad hranicí, počet číslic se zvýší o 1.


; ------------- Limit minimal number of digits (-> EDX)

Int0ToTextBfN4:	cmp	dl,bl		; check minimal number of digits
		ja	Int0ToTextBfN5	; number of digits is OK
		mov	dl,bl		; EDX <- minimal numer of digits

Vypočtená délka čísla se zvýší podle požadovaného minimálního počtu číslic (zadaného položkou FORMPAR_Prec formátovacích parametrů).


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

Int0ToTextBfN5:	bt	ebx,FORMFLAG_Zero_b ; add zeros instead of spaces?
		jnc	Int0ToTextBfN7	; no zeros

; ------------- Prepare width of field (-> EAX)

		movzx	eax,bh		; EAX <- minimal width of field
		sub	eax,ebp		; subtract sign
		jbe	Int0ToTextBfN7	; width is low

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

		bt	ebx,FORMFLAG_Thsn_b ; use thousand separator?
		jnc	Int0ToTextBfN6	; not using thousand separator

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

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

; ------------- New minimal number of digits

Int0ToTextBfN6:	cmp	eax,edx		; check number of digits
		jb	Int0ToTextBfN7	; number of digits is not greater
		xchg	eax,edx		; EDX <- new text size

Další výpočet musí zajistit doplnění nul k číslu, je-li aktivní příznakový bit FORMFLAG_Zero_b.

Do registru EAX se připraví požadovaná minimální šířka pole (z registru BH, tj. parametr FORMPAR_Width formátovacích parametrů) a odečte se délka úvodní části čísla (znaménko) v registru EBP.

Je-li požadován oddělovač řádů (přepínač FORMFLAG_Thsn_b), musí se od počtu znaků odečíst počet oddělovačů. Na 3 číslice připadá 1 znak oddělovače, přičemž před oddělovačem musí být vždy minimálně 1 číslice, proto počet číslic pro danou šířku pole EAX se vypočte inkrementací šířky pole o 1 a vynásobením číslem 3/4.

Po výpočtu počtu číslic potřebných k doplnění čísla nulami se výsledek porovná se skutečnou šířkou čísla a případně se počet číslic zvýší.


; ------------- Add thousand separator

Int0ToTextBfN7:	bt	ebx,FORMFLAG_Thsn_b ; use thousand separator?
		jnc	Int0ToTextBfN9	; not using thousand separator
		mov	eax,edx		; EAX <- number of digits
		dec	eax		; without 1 digit
		jle	Int0ToTextBfN9	; no digit
		imul	eax,eax,21846	; EAX <- digits*10000h/3
		shr	eax,16		; EAX <- number of separators
		add	edx,eax		; EDX <- add separators in characters

; ------------- Add sign

Int0ToTextBfN9:	add	edx,ebp		; EDX <- new number of bytes

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

		xchg	eax,edx		; EAX <- text size in characters
		pop	ebp		; pop EBP
		pop	edi		; pop EDI
		pop	edx		; pop EDX
		ret

Je-li požadován oddělovač řádů (je nastaven přepínač FORMFLAG_Thsn_b), je potřeba k délce čísla přičíst oddělovače řádů. Na 3 číslice čísla připadá 1 oddělovač řádů, přičemž před oddělovačem musí být minimálně jedna číslice. Proto se počet oddělovačů získá snížením počtu číslic o 1 a vydělením údaje třemi. Vypočtený počet oddělovačů se přičte ke střadači délky textu v registru EDX, přičte se délka úvodní části a střadač je uložen do výstupního registru EAX.


Obsah / Utility / TEXTFORM / IntToTextBufN