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

Obsah / Utility / TEXTFORM / HexSToTextBuf, HexCToTextBuf

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

Související:

HexToTextBufN   Délka textu formátovaného čísla HEX
FormToTextBuf   Zformátování textu do bufferu

HexSToTextBuf, HexCToTextBuf - Zformátování čísla HEX do bufferu

Funkce HexSToTextBuf a HexCToTextBuf zkonvertují 64-bitové celé číslo bez znaménka na text ve formátovaném hexadecimálním (šestnáctkovém) tvaru. HexSToTextBuf použije malá písmena abcdef, HexCToTextBuf velká písmena ABCDEF.


; -----------------------------------------------------------------------------
;             Convert HEX number into text buffer - small letters
; -----------------------------------------------------------------------------
; INPUT:	EDX:EAX = number
;		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:	On AMD 1.6 GHz it takes approx. "8*digits+50" nanoseconds.
; -----------------------------------------------------------------------------
; Local variables (ebp+N are read-only variables):
%define		HEXNumL    ebp+16	; (4) number LOW
%define		HEXNat     ebp+8	; (4) pointer to nationality NATIONAL
%define		HEXNumH    ebp+4	; (4) number HIGH
					; (3)
%define		HEXCap	   ebp-4	; (1) capital (7) or small (27h) flag
%define		HEXForm    ebp-8	; (4) formatting parameters FORMPAR
%define		HEXLen	   ebp-12	; (4) text length

%define		HEXStack  12		; stack size

Na vstupu každé z funkcí obsahuje registrový pár EDX:EAX číslo bez znaménka 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žívají lokální proměnné s bázovým registrem EBP. Proměnné s kladným offsetem (HEXNumL, HEXNat a HEXNumH) jsou použity jen pro čtení, jejich obsah není modifikován. HEXNumL je nižší dvojslovo vstupního čísla (vstupní registr EAX). HEXNat je ukazatel na národnostní informace NATIONAL (vstupní registr ECX). HEXNumH je vyšší dvojslovo vstupního čísla (vstupní registr EDX). HEXCap je příznak použití velkého (=7) nebo malého (=27h) písmene ve výstupní hodnotě. HEXForm jsou formátovací parametry FORMPAR (vstupní registr EBX). HEXLen je délka ukládaného textu. HEXStack je velikost lokálních proměnných v zásobníku.


; ------------- Macro - store one HEX digit
; Uses: EDX:EBX=number, ESI=free space-1, EDI=end of destination, [HEXCap]
; Destroys: AL

%macro		HEXTOTEXTBUFDIG 0

		inc	esi		; shift free space counter
		mov	al,bl		; EAX <- number
		jle	%%L2		; position is behind end of buffer
		and	al,0fh		; mask low nibble
		cmp	al,9		; is it HEX character?
		jbe	%%L1		; it is digit
		add	al,[HEXCap]	; add letter flag
%%L1:		add	al,"0"		; convert to ASCII character
		mov	[edi],al	; store character
%%L2:		shrd	ebx,edx,4	; shift number LOW 4 bits right
		dec	edi		; shift destination pointer
		shr	edx,4		; shift number HIGH 4 bits right
%endmacro

Pomocné makro HEXTOTEXTBUFDIG je používáno uvnitř funkce k uložení jedné HEX číslice. Makro modifikuje vstupní číslo v registrovém páru EDX:EBX. V registru ESI je čítač volného místa v cílovém bufferu snížený o 1. EDI je ukazatel konce dat v cílovém bufferu (číslo se ukládá odzadu). Obsah registru AL se funkcí zničí.

Inkrementací čítače volného místa v registru ESI se provede test, zda je ukazatel do cílového bufferu platný. Není-li čítač kladný, ukazuje ukazatel za konec cílového bufferu, v tom případě se přeskočí část ukládající další znak.

Z registru BL se obdrží nejnižší bajt dekódovaného čísla a zamaskováním 0Fh se připraví nejnižší tetráda čísla, která bude další číslicí dekódovaného čísla. Pro hodnoty vyšší než 9 bude znakem písmeno A až F, proto se k číslu přičte korekce HEXCap, která já hodnotu 7 pro velká písmena nebo 27h pro malá písmena. Přičtením ASCII znaku "0" se číslo zkonvertuje na ASCII číslici, která se uloží do cílového bufferu.

Ukazatel cílového bufferu se sníží a rotací dvojregistru EDX:EBX o 4 bity doprava (instrukcemi SHRD a SHR) se číslo posune o jednu číslici k vyšším řádům.


; ------------- Macro - store thousand separator
; Uses: ESI=free space-1, EDI=end of destination, EAX.byte2=thousand separator

%macro		TOTEXTBUFSEP 0

		inc	esi		; shift free space counter
		jle	%%L1		; position is behind end of buffer
		ror	eax,16		; AL <- thousand separator
		mov	[edi],al	; store character
		rol	eax,16		; return EAX
%%L1:		dec	edi		; shift destination pointer

%endmacro

Pomocné makro TOTEXTBUFSEP je používáno uvnitř funkce k uložení znaku oddělovače řádů. V registru ESI je čítač volného místa v cílovém bufferu snížený o 1. EDI je ukazatel konce dat v cílovém bufferu (číslo se ukládá odzadu). 3. bajt v registru EAX (tj. bity 16 až 23) obsahuje znak oddělovače tisíců.

Inkrementací čítače volného místa v registru ESI se provede test, zda je ukazatel do cílového bufferu platný. Není-li čítač kladný, ukazuje ukazatel za konec cílového bufferu, v tom případě se znak neuloží, pouze se posune ukazatel cílových dat.

Rotací registru EAX o 16 bitů doprava se v registru AL objeví znak oddělovače řádů, který se uloží do cílového bufferu. Rotací o 16 bitů doleva se obsah registru EAX vrátí do původního stavu. Ukazatel clových dat EDI se dekrementuje na novou pozici v textu.


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

HexSToTextBuf:	or	esi,esi		; check remaining space
		jle	short HexToTextBufN9 ; not enough free space

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

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

; ------------- Prepare "capital" flag

		mov	byte [HEXCap],27h ; prepare "small" flag
		jmp	short HexToTextBuf1

Na začátku funkce HexSToTextBuf 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é. Do proměnné HEXCap se připraví konstanta pro malá písmena.


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

HexCToTextBuf:	or	esi,esi		; check remaining space
		jle	short HexToTextBufN9 ; not enough free space

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

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

; ------------- Prepare "capital" flag

		mov	byte [HEXCap],7	; prepare "capital" flag

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

HexToTextBuf1:	mov	[HEXForm],ebx	; formatting parameters FORMPAR

Začátek funkce HexCToTextBuf je shodný se začátkem funkce HexSToTextBuf s tím rozdílem, že do proměnné HEXCap se připraví konstanta pro velká písmena.

Dále již pokračují obě funkce společnou větví programu. Do proměnné HEXForm se uschovají formátovací parametry z registru EBX.


; ------------- Get text length without spaces (-> EDX)

		call	Hex0ToTextBufN	; get text length without spaces
		mov	[HEXLen],eax	; store text length
		xchg	eax,edx		; EDX <- push text length

Ke zformátování čísla v bufferu je potřeba nejdříve znát délku čísla. Délka čísla se zjistí funkcí Hex0ToTextBufN, která na rozdíl od funkce HexToTextBufN ignoruje parametr minimální šířky pole s údajem. Délka čísla se uchová do proměnné HEXLen a do registru EDX.


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

		bt	ebx,FORMFLAG_Left_b ; left-justify?
		jc	HexToTextBuf22	; left-justify, no leading spaces
		movzx	ecx,bh		; ECX <- minimal width of field
		sub	ecx,edx		; ECX <- remaining spaces
		jle	HexToTextBuf22	; no spaces left
		bt	ebx,FORMFLAG_Cent_b ; center?
		jnc	HexToTextBuf2	; no, right-justify
		shr	ecx,1		; ECX <- spaces / 2, round down
		jz	HexToTextBuf22	; no spaces left
HexToTextBuf2:	mov	al," "		; AL <- space
HexToTextBuf21:	dec	esi		; decrease space counter
		stosb			; store one space
		jz	short HexToTextBuf24 ; buffer is full
		loop	HexToTextBuf21	; next character

Není-li číslo zarovnáno doleva (příznakový bit FORMFLAG_Left_b), je nutno před číslo doplnit úvodní mezery. Do registru ECX se připraví z registru BH (což je parametr FORMPAR_Width z popisovače formátovacích parametrů) požadovaná šířka pole. Odečtením délky textu z registru EDX se obdrží zbývající šířka pro okraje. Je-li to kladné číslo, bude se pokračovat dále. 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 prefix

HexToTextBuf22:	mov	ecx,edx		; ECX <- text length
		bt	ebx,FORMFLAG_Alt_b ; use prefix?
		jnc	HexToTextBuf3	; not using prefix
		mov	al,"0"		; AL <- character "0"
		dec	esi		; decrease counter of free space
		stosb			; store first character
		jz	short HexToTextBuf24 ; buffer is full
		dec	ecx		; ECX <- text length - 1
		mov	al,"x"		; AL <- character "x"
		dec	esi		; decrease counter of free space
		stosb			; store second character
HexToTextBuf24:	jz	HexToTextBuf9	; buffer is full
		dec	ecx		; ECX <- text length - 1

; ------------- Suffix correction

HexToTextBuf3:	bt	ebx,FORMFLAG_Alt2_b ; use suffix?
		sbb	ecx,byte 0	; ECX <- decrease if suffix

Volitelně se může před začátek čísla uložit prefix "0x" (je-li nastaven příznakový bit FORMFLAG_Alt_b). Postupně se ukládají znaky "0" a "x", přičemž je dekrementací čítače v registru ESI sledováno volné místo k uložení dalšího znaku. Dosáhne-li čítač nuly, funkce se ukončí. Současně s ukládáním prefixu se v registru ECX dekrementuje údaj zbývající délky textu o 2.

Po uložení prefixu se testem příznakového bitu FORMFLAG_Alt2_b zjistí, zda se bude ukládat sufix "h". Pokud ano, sníží se údaj zbývající délky textu o 1.


; ------------- Prepare registers to convert number

		mov	ebx,[HEXNumL]	; EBX <- number LOW
		add	edi,ecx		; EDI <- last character + 1
		mov	edx,[HEXNumH]	; EDX <- number HIGH
		sub	esi,ecx		; ESI <- subtract length
		or	ecx,ecx		; check text length
		jle	HexToTextBuf81	; no characters

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

		push	esi		; push ESI
		push	edi		; push EDI
		dec	edi		; EDI <- set pointer to last character

Při přípravě registrů ke konverzi se do dvojregistru EDX:EBX načte konvertované číslo. Přičtením zbývající délky textu čísla k EDI a odečtením od ESI se ukládací ukazatel posune za konec ukládaného textu. Není-li délka textu v registru ECX platné kladné číslo, přeskočí se část pro ukládání čísla.

Po úschově zápisových ukazatelů ESI a EDI se dekrementací EDI nastaví ukládací ukazatel na poslední znak čísla.


; ------------- Convert number without thousand separator
; EDX:EBX=number, ESI=free space-1, EDI=end of destination, ECX=num. of chars

		bt	dword [HEXForm],FORMFLAG_Thsn_b; thousand separator?
		jc	HexToTextBuf5	; use thousand separator
HexToTextBuf42:	HEXTOTEXTBUFDIG		; store one digit into buffer
		loop	HexToTextBuf42	; next digit
		jmp	HexToTextBuf8

Není-li nastaven příznakový bit FORMFLAG_Thsn_b, bude se číslo dekódovat bez oddělovačů řádů. Během konverze obsahuje registrový pár EDX:EBX konvertované číslo, registr ESI čítač volného místa - 1, registr EDI ukazatel na konec ukládaného textu a registr ECX čítač znaků k uložení. K dekódování čísla se používá makro HEXTOTEXTBUFDIG, které je opakovaně voláno ve smyčce s čítačem ECX.


; ------------- Convert number with thousand separator
; EDX:EBX=number, ESI=free space-1, EDI=end of destination, ECX=num. of chars,
; AH=order counter, EAX.byte2=thousand separator

HexToTextBuf5:	mov	eax,[HEXNat]	; EAX <- pointer to nationality
		mov	al,[eax+NAT_ThsndSep] ; AL <- thousand separator
		shl	eax,16		; shift character
		mov	ah,5		; AH <- 5, init order counter
HexToTextBuf52:	dec	ah		; count order counter
		jnz	HexToTextBuf55	; no thousand separator
		TOTEXTBUFSEP		; store thousand separator
		dec	ecx		; decrease character counter
		mov	ah,4		; AH <- 4, init order counter
		jz	HexToTextBuf8	; no next character
HexToTextBuf55:	HEXTOTEXTBUFDIG		; store one digit into buffer
		loop	HexToTextBuf52	; next digit

Při konverzi čísla s oddělovači řádů obsahuje registrový pár EDX:EBX konvertované číslo, registr ESI čítač volného místa - 1, registr EDI ukazatel na konec ukládaného textu, registr ECX čítač znaků k uložení, registr AH čítač řádů a třetí bajt registru EAX (tj. bity 16 až 23) znak oddělovače řádů.

Do třetího bajtu registru EAX se připraví znak oddělovače řádů - do registru EAX se načte z HEXNat ukazatel na popisovač národnostních informací, z něj z NAT_ThsndSep do registru AL znak oddělovače řádů, který se rotací o 16 bitů posune na pozici 3. bajtu. Do registru AH se připraví čítač řádů.

K dekódování čísla se používá makro HEXTOTEXTBUFDIG, které je opakovaně voláno ve smyčce s čítačem ECX. Na začátku smyčky je dekrementován čítač řádů v registru AH. Pokud dosáhne nuly, uloží se znak oddělovače pomocí makra TOTEXTBUFSEP, sníží se čítač zbývajících znaků a do registru AH se připravý nový čítač řádů.


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

HexToTextBuf8:	pop	edi		; pop EDI
		pop	esi		; pop ESI

; ------------- Limit offset

HexToTextBuf81:	or	esi,esi		; buffer overflow?
		jg	HexToTextBuf82	; buffer is OK
		add	edi,esi		; EDI <- correct address
		xor	esi,esi		; limit free space to 0
		jmp	short HexToTextBuf9

Po dekódování textu čísla se navrátí ukládací registry ESI a EDI, které ukazovaly za konec ukládaného čísla. Ukazatele se upraví tak, aby nepřesahovaly konec bufferu - tj. přesahující ukládací ukazatel EDI se přičtením čítače ESI posune na konec a poté se čítač zbylého místa vynuluje.


; ------------- Store suffix

HexToTextBuf82:	test	byte [HEXForm+FORMPAR_Flags2],FORMFLAG2_Alt2 ; suffix?
		jz	HexToTextBuf83	; not using suffix
		mov	al,"h"		; AL <- suffix
		stosb			; store suffix
		dec	esi		; decrease space counter
		jz	HexToTextBuf9	; no free space

Je-li nastaven v registru formátovacích parametrů příznakový bit FORMFLAG2_Alt2, uloží se za konec čísla sufix "h". Po uložení sufixu se dekrementací čítače volného místa ESI zkontroluje zbývající volné místo, při dosažení konce bufferu se funkce ukončí.


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

HexToTextBuf83:	mov	eax,[HEXLen]	; EAX <- text length
		movzx	ecx,byte [HEXForm+FORMPAR_Width] ; ECX <- minimal width
		sub	ecx,eax		; ECX <- remaining spaces
		jle	HexToTextBuf9	; no spaces remain
		test	byte [HEXForm+FORMPAR_Flags1],FORMFLAG1_Left ; left?
		jnz	HexToTextBuf84	; left-justify
		test	byte [HEXForm+FORMPAR_Flags2],FORMFLAG2_Cent ; center?
		jz	HexToTextBuf9	; no center
		shr	ecx,1		; ECX <- spaces / 2
		adc	cl,ch		; ECX <- round up
		jz	HexToTextBuf9	; no spaces
HexToTextBuf84:	mov	al," "		; AL <- space
HexToTextBuf85:	stosb			; store one space
		dec	esi		; decrease space counter
		loopnz	HexToTextBuf85	; next character

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

HexToTextBuf9:	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. Do registru EAX je načtena délka textu (bez mezer), 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 / HexSToTextBuf, HexCToTextBuf