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

Obsah / Utility / TEXT/ UDWToTextBuf

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

Související:

UDWToTextBufN   Délka textu neformátovaného čísla DWORD bez znaménka
FormToTextBuf   Zformátování textu do bufferu

UDWToTextBuf - Konverze neformátovaného čísla DWORD bez znaménka na text

Nejčastější operací při převodu čísel na text je konverze 32-bitového celého čísla na text bez formátování. Proto je na tuto operaci k dispozici samostatná funkce UDWToTextBuf, která zajistí rychlou konverzi čísla na text.


; -----------------------------------------------------------------------------
;             Convert unformatted unsigned DWORD into text buffer
; -----------------------------------------------------------------------------
; INPUT:	EAX = unsigned number
;		ECX = minimal number of digits
;		ESI = remaining free space in buffer
;		EDI = destination buffer
; OUTPUT:	ESI = next remaining free space in buffer
;		EDI = next destination buffer
; -----------------------------------------------------------------------------

Na vstupu funkce obsahuje registr EAX číslo bez znaménka ke konverzi, registr ECX požadovaný minimální počet číslic, registr ESI zbývající volné místo v cílovém bufferu a registr EDI ukazatel na cílový buffer. Na výstupu z funkce jsou registry ESI a EDI posunuty na novou zápisovou pozici.


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

UDWToTextBuf:	push	eax		; push EAX
		push	ebx		; push EBX
		push	ecx		; push ECX
		push	edx		; push EDX

; ------------- Prepare registers

		mov	ebx,eax		; EBX <- number to convert

; ------------- Get text length (-> ECX)

		call	UDWToTextBufN	; get text length (-> EAX)
		xchg	eax,ecx		; ECX <- text length
		jecxz	UDWToTextBuf3	; no digit

; ------------- Shift buffer pointer

		add	edi,ecx		; EDI <- behind end of text
		sub	esi,ecx		; ESI <- decrease free space

; ------------- Push new end of text

		push	esi		; push ESI
		push	edi		; push EDI
		js	UDWToTextBuf4	; buffer is full

Běžný postup při dekódování čísla bývá dělením získávat číslice "odzadu" čísla, ukládat je do zásobníku a poté v opačném pořadí navracet a ukládat do výstupního bufferu. V jádru systému je nutné šetřit se zásobníkem, proto se použije ukládání čísla přímo do výstupního bufferu, což je ve výsledku i rychlejší operace.

Pomocí funkce UDWToTextBufN se zjistí délka konvertovaného čísla a posune se ukládací adresa bufferu i čítač zbylých dat. Pokud nastalo podtečení čítače zbylých dat, proběhne konverze pomalejší opatrnou metodou.


; ------------- Buffer is OK, use fast conversion

		std			; set direction down
		dec	edi		; shift to last character
UDWToTextBuf2:	mov	eax,3435973837	; EAX <- 800000000h/10, rounded up
		mul	ebx		; EDX <- (number*8) / 10
		shr	edx,3		; EDX <- number / 10
		mov	al,10		; AL <- 10
		mul	dl		; AL <- number / 10 * 10 low byte
		xchg	eax,ebx		; EAX <- number, EBX <- number low byte
		sub	al,bl		; AL <- number % 10
		add	al,"0"		; AL <- convert to ASCII character
		stosb			; store one character
		mov	ebx,edx		; ESI <- number / 10
		loop	UDWToTextBuf2	; next character
		cld			; set direction up

; ------------- Pop new end of text

		pop	edi		; pop EDI
		pop	esi		; pop ESI

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

UDWToTextBuf3:	pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	ebx		; pop EBX
		pop	eax		; pop EAX
		ret

Nedojde-li k přeplnění výstupního bufferu, může se použít rychlá konverze bez kontroly konce bufferu. Při konverzi se číslo postupně dělí deseti a zbytek po dělení se ukládá jako číslice od konce bufferu k začátku.

Pro zrychlení konverze se namísto pomalé operace dělení použije rychlejší operace násobení převrácenou hodnotou čísla 10. Vynásobením číslem 800000000h/10 (zaokrouhleno nahoru) a posunem registru EDX o 3 bity doprava se obdrží v registru EDX číslo vydělené 10.

Pro získání zbytku po dělení je nutno výsledek dělení vynásobit zpět číslem 10 a od původního čísla odečíst. Vzhledem k rozsahu výsledku postačí namísto celého dvojslova vynásobit pouze nejnižší bajt (což je poměrně rychlá operace) a výsledek poté odečíst od nižšího bajtu původního čísla. Přičtením ASCII znaku "0" se výsledný zbytek po dělení zkonvertuje na číslici, která se uloží do bufferu. V konverzní smyčce se dále pokračuje pro požadovaný počet číslic v registru ECX.


; ------------- Buffer is full, convert carefully

UDWToTextBuf4:	mov	eax,3435973837	; EAX <- 800000000h/10, rounded up
		mul	ebx		; EDX <- (number*8) / 10
		shr	edx,3		; EDX <- number / 10
		mov	al,10		; AL <- 10
		mul	dl		; AL <- number / 10 * 10 low byte
		xchg	eax,ebx		; EAX <- number, EBX <- number low byte
		sub	al,bl		; AL <- number % 10
		dec	edi		; shift to last character
		add	al,"0"		; AL <- convert to ASCII character
		inc	esi		; increase remaining space
		jle	UDWToTextBuf6	; pointer is behind the text
		mov	[edi],al	; store character into buffer
UDWToTextBuf6:	mov	ebx,edx		; ESI <- number / 10
		loop	UDWToTextBuf4	; next character

; ------------- Pop new end of text

		pop	edi		; pop EDI
		pop	esi		; pop ESI

; ------------- Correct end of text

		add	edi,esi		; EDI <- correct text pointer
		xor	esi,esi		; ESI <- correct remaining free space

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

		pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	ebx		; pop EBX
		pop	eax		; pop EAX
		ret

Dojde-li k přeplnění výstupního bufferu, je nutné provést pomalou konverzi s kontrolou platnosti ukládací adresy. Při konverzi se číslo postupně dělí deseti a zbytek po dělení se ukládá jako číslice od konce bufferu k začátku.

Pro zrychlení konverze se namísto pomalé operace dělení použije rychlejší operace násobení převrácenou hodnotou čísla 10. Vynásobením číslem 800000000h/10 (zaokrouhleno nahoru) a posunem registru EDX o 3 bity doprava se obdrží v registru EDX číslo vydělené 10.

Pro získání zbytku po dělení je nutno výsledek dělení vynásobit zpět číslem 10 a od původního čísla odečíst. Vzhledem k rozsahu výsledku postačí namísto celého dvojslova vynásobit pouze nejnižší bajt (což je poměrně rychlá operace) a výsledek poté odečíst od nižšího bajtu původního čísla. Přičtením ASCII znaku "0" se výsledný zbytek po dělení zkonvertuje na číslici.

Před uložením do výstupního bufferu je nedříve posunut čítač zbylého místa v bufferu. Číslice se uloží do bufferu pouze v případě, že čítač volného místa v bufferu je větší než 0, tj. ukazatel není za koncem bufferu. V konverzní smyčce se dále pokračuje pro požadovaný počet číslic v registru ECX.

Po ukončení konverze je nutné provést ošetření ukazatele bufferu tak, aby ukazoval maximálně na konec bufferu, ale ne dále (tj. aby čítač zbylých dat byl 0). To se zajistí přičtením čítače zbylých dat (který címe že je záporný) k cílovému ukazateli a vynulováním čítače dat.


Obsah / Utility / TEXTFORM / UDWToTextBuf