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

Obsah / Utility / TEXT / TextAddIntSig

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


TextAddIntSig - Přidání neformátovaného celého čísla se znaménkem na konec textového řetězce

Funkce TextAddIntSig přidá neformátované celé číslo se znaménkem na konec textového řetězce.


; -----------------------------------------------------------------------------
;              Add unformated signed integer number to end of text
; -----------------------------------------------------------------------------
; INPUT:	EAX = signed number
;		EBX = pointer to TEXT variable
; OUTPUT:	CY = memory error or invalid number (text not changed)
; -----------------------------------------------------------------------------

Na vstupu funkce obsahuje registr EAX celé číslo se znaménkem. Registr EBX obsahuje ukazatel na textovou proměnnou, ke které se má číslo přidat. V případě chyby paměti je navrácen chybový příznak CY a obsah textové proměnné se nezmění.


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

TextAddIntSig:	push	eax		; push EAX
		push	ecx		; push ECX
		push	edx		; push EDX
		push	esi		; push ESI
		push	edi		; push EDI
		xchg	eax,esi		; ESI <- number

; ------------- Copy text on write

		call	TextCopyWrite	; copy text on write
		jc	TextAddIntSig8	; memory error

Konvertované číslo se uchová do registru ESI. Pomocí funkce TextCopyWrite se provede kopie textu před zápisem. Pokud nastane chyba paměti, funkce se ukončí s chybou a text zůstane nezměněn.


; ------------- Absolute value

		xor	edx,edx		; EDX <- 0, number is positive
		or	esi,esi		; is number negative?
		jns	TextAddIntSig2	; number is not negative
		inc	edx		; EDX <- 1, number is negative
		neg	esi		; ESI <- absolute value

Číslo v ESI se převede na absolutní hodnotu. Do registru EDX se přednastaví délka textu 0 nebo 1 podle toho, zda bude číslo obsahovat znaménko mínus "-".


; ------------- Get number of bits in number (-> AL, minimal 1)

TextAddIntSig2:	xor	ecx,ecx		; ECX <- 0
		inc	ecx		; ECX <- 1, minimal 1 digit if zero
		bsr	eax,esi		; AL <- highest bit in number
		jz	TextAddIntSig4	; number is zero, use 1 digit
		inc	eax		; AL <- number of bits in number

Pro konverzi čísla je potřeba znát délku textu, protože dekódování čísla se provádí odzadu. Do registru ECX se přednastaví 1 jako minimální délka čísla pro případ nuly. Instrukcí BSR se vyhledá pozice nejvyššího bitu "1" v čísle. Pokud bylo číslo nula, použije se délka čísla 1. Inkrementací pozice bitu v registru EAX se obdrží počet bitů čísla.


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

		mov	ah,78		; AH <- 100h / 3.3219 round up
		mul	ah		; AH <- aprox. number of digits
		movzx	ecx,ah		; ECX <- aprox. number of digits
		cmp	[IntMul10+ecx*4],esi ; check number
		adc	cl,0		; ECX <- number of digits

Z počtu bitů se zjistí počet číslic čísla tak, že počet bitů (což je dvojkový logaritmus čísla) se vydělí konstantou ln 10 / ln 2 = 3.3219, tím se převede na dekadický logaritmus, což znamená počet číslic. Vlivem zaokrouhlení na celá čísla ale tento údaj není ještě přesný, proto se upřesní tak, že se konvertované číslo porovná s hranicemi podle tabulky IntMul10 (obsahující mocniny 10) a počet číslic se opraví na správnou hodnotu.


; ------------- Resize data buffer

TextAddIntSig4:	mov	eax,[ebx]	; EAX <- data buffer
		mov	eax,[eax+TEXT_Length] ; EAX <- length of data buffer
		add	eax,ecx		; EAX <- add text length
		add	eax,edx		; EAX <- add sign
		call	TextResize	; resize data buffer
		jc	TextAddIntSig8	; memory error
		add	eax,[ebx]	; EAX <- end of data buffer

Voláním funkce TextResize se nastaví nová velikost datového bufferu, tj. stará velikost zvýšená o zjištěný počet číslic ECX a zjištěnou délku znaménka EDX. Pokud nastane chyba paměti, funkce se ukončí s chybou a text zůstane nezměněn.


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

		or	edx,edx		; is number negative?
		jz	TextAddIntSig5	; number is not negative
		mov	edx,eax		; EDX <- end of data buffer
		sub	edx,ecx		; EDX <- start of data in buffer
		mov	byte [edx+TEXT_Text-1],"-" ; store negative sign
TextAddIntSig5:	add	eax,TEXT_Text-1	; EAX <- last character in buffer
		xchg	eax,edi		; EDI <- end of data buffer

Pokud je číslo negativní (obsah registru EDX je 1), uloží se znaménko mínus "-" za konec starého textu. Do registru EDI se připraví adresa konce datového bufferu.


; ------------- Convert number

		std			; set direction down
TextAddIntSig6:	mov	eax,3435973837	; EAX <- 800000000h/10, rounded up
		mul	esi		; 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,esi		; EAX <- number, ESI <- number low byte
		sub	eax,esi		; AL <- number % 10
		add	al,"0"		; AL <- convert to ASCII character
		stosb			; store one character
		mov	esi,edx		; ESI <- number / 10
		loop	TextAddIntSig6	; next character
		cld			; set direction up

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

		clc			; clear error flag
TextAddIntSig8:	pop	edi		; pop EDI
		pop	esi		; pop ESI
		pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	eax		; pop EAX
		ret

Nyní je vše již připraveno a může být zahájena konverze čísla na číslice. Konverze se bude provádět od konce bufferu, proto se nastaví příznak směru ukládání dolů.

K získání další (nejnižší) číslice bude číslo vyděleno 10. Pro zrychlení konverze se namísto dělení použije rychlejší operace násobení převrácenou hodnotou. Podíl zůstane v registru EDX. Opětovným vynásobením číslem 10 a odečtením od původního čísla se získá zbytek po dělení. Vzhledem k rozsahu výsledku není třeba dělat plné násobení, stačí jen násobit nejnižší bajt podílu (v registru DL). Zbytek po dělení se přičtením "0" převede na ASCII znak a uloží do bufferu EDI. Poté se pokračuje další číslicí čítáním registru ECX.


Obsah / Utility / TEXT / TextAddIntSig