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

Obsah / Utility / TEXTFORM / FormDateTime

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

Související:

Formátovací řetězec funkce FormDateTime
FormDateTimeN   Délka formátovaného textu data a času
FormAbsDateTime   Zformátování absolutního času do bufferu

FormDateTime - Zformátování data a času do bufferu

Funkce FormDateTime zformátuje do bufferu text data a času podle formátovacího řetězce.


; -----------------------------------------------------------------------------
;                      Format date/time into text buffer
; -----------------------------------------------------------------------------
; INPUT:	EAX = pointer to date-time structure DATETIME
;		EBX = size of source text (with date-time format descriptor)
;		ECX = pointer to nationality descriptor NATIONAL
;		EDX = source text (with date-time format descriptor)
;		ESI = remaining free space in destination buffer
;		EDI = destination buffer
; OUTPUT:	ESI = next remaining free space in buffer
;		EDI = next destination buffer
; NOTES:	To get user default nationality use DEFAULT_NAT macro.
; -----------------------------------------------------------------------------

Na vstupu funkce obsahuje registr EAX ukazatel na strukturu DATETIME s připravenými položkami údaje data a času. Nepoužité položky nemusí být platné. Registr EBX obsahuje délku zdrojového textu v bajtech. Registr ECX obsahuje ukazatel na popisovač národnostních informací NATIONAL. Ke zjištění implicitního popisovače národnostních informací lze použít makro DEFAULT_NAT. Registr EDX obsahuje ukazatel na zdrojový text ke zformátování. Zdrojový text je v kódu UTF-8. Registr ESI obsahuje čítač zbylého místa v cílovém bufferu a registr EDI ukazatel do cílového bufferu. Na výstupu ukazují registry ESI a EDI na novou ukládací pozici.


%define		FDTNat	esp+8		; (4) pointer to nationality

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

FormDateTime:	push	eax		; push EAX
		push	ebx		; push EBX
		push	ecx		; push ECX (pointer to nationality)
		push	edx		; push EDX
		push	ebp		; push EBP 
		xchg	eax,ebp		; EBP <- pointer to DATETIME

Na začátku funkce jsou uschovány registry do zásobníku a ukazatel na strukturu DATETIME se připraví do registru EBP. Původní obsah registru ECX (tj. ukazatel na národnostní informace NATIONAL) zůstává přístupný přes zásobník jako lokální proměnná FDTNat.


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

FormDateTime1:	or	esi,esi		; check remaining space
		jle	short FormDateTime9 ; not enough free space

Testem registru ESI je ověřeno, zda je v cílovém bufferu volné místo pro další znak. Pokud je cílový buffer plný, funkce se ihned ukončí.


; ------------- Read one character (-> AL)

FormDateTime2:	dec	ebx		; check next source character
		jl	short FormDateTime9 ; end of source text
		mov	al,[edx]	; AL <- character from source text
		inc	edx		; increase source pointer

Dekrementací registru EBX se ověří, zda je ve zdrojovém bufferu připraven další znak. Pokud ne, funkce se ukončí. Jinak se z ukazatele EDX načte další zdrojový znak a ukazatel se inkrementuje.


; ------------- Read repeated characters (-> ECX)

		xor	ecx,ecx		; ECX <- 0
		inc	ecx		; ECX <- 1, character counter
		or	ebx,ebx		; end of source text?
		jz	FormDateTime4	; no other characters
FormDateTime3:	cmp	al,[edx]	; identical character?
		jne	FormDateTime4	; character is not identical
		inc	edx		; EDX <- increase text pointer
		inc	ecx		; ECX <- increase repeat counter
		dec	ebx		; EBX <- decrease source counter
		jnz	FormDateTime3	; next character

Interpretace formátovacích znaků data a času závisí na tom, kolik znaků je za sebou uvedeno, proto je nutno spočítat za sebou následující stejné znaky. Čítač ECX se připraví na hodnotu 1 (první znak který byl načten). V cyklu se opakovaně testuje, zda je následující znak stejný jako dříve načtený znak a pokud ano, posunou se ukazatele a zvýší se čítač shodných znaků ECX. Cyklus se ukončí nalezením rozdílného znaku nebo nalezením konce zdrojového textu.


; ------------- Jump to service

FormDateTime4:	cmp	al,34		; minimal character
		jb	FormDateTime8	; invalid character
		cmp	al,"z"		; maximal character
		ja	FormDateTime8	; invalid character
		movzx	eax,al		; EAX <- character
		jmp	[FormDateTimeJmp+eax*4-34*4] ; jump

; ------------- Return last character (quoted string)

FormDateTime7:	dec	edx		; EDX <- decrease text pointer
		inc	ebx		; EBX <- increase source counter

; ------------- Store one character into destination buffer (repeated)

FormDateTime8:	dec	esi		; decrease free space
		stosb			; store character into buffer
		loopnz	FormDateTime8	; next character
		jnz	short FormDateTime2 ; next character

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

FormDateTime9:	pop	ebp		; pop EBP
		pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	ebx		; pop EBX
		pop	eax		; pop EAX
		ret

Po zjištění počtu znaků se provede skok na obsluhu znaku pomocí skokové tabulky FormDateTimeJmp. Neplatné znaky se uloží opakovaně do bufferu v původním nezměněném tvaru. Během ukládání se dekrementací registru ESI zjišťuje volné místo v cílovém bufferu a pokud dojde k zaplnění cílového bufferu, funkce se ukončí. Registr ECX je čítač znaků k uložení.


; ------------- Time separator ":"

FormDateTimeT:	mov	eax,[FDTNat]	; EAX <- nationality
		mov	al,[eax+NAT_TimeSep] ; AL <- time separator
		jmp	short FormDateTime8 ; store character

Znak ":" se nahradí znakem oddělovače času z tabulky národnostních informací a uloží se opakovaně do cílového bufferu.


; ------------- Date separator "/"

FormDateTimeD:	mov	eax,[FDTNat]	; EAX <- nationality
		mov	al,[eax+NAT_DateSep] ; AL <- date separator
		jmp	short FormDateTime8 ; store character

Znak "/" se nahradí znakem oddělovače data z tabulky národnostních informací a uloží se opakovaně do cílového bufferu.


; ------------- Decimal separator "."

FormDateTimeC:	mov	eax,[FDTNat]	; EAX <- nationality
		mov	al,[eax+NAT_DecimalSep] ; AL <- decimal separator
		jmp	short FormDateTime8 ; store character

Znak "." se nahradí znakem oddělovače desetinných míst z tabulky národnostních informací a uloží se opakovaně do cílového bufferu.


; ------------- Thousand separator ","

FormDateTimeH:	mov	eax,[FDTNat]	; EAX <- nationality
		mov	al,[eax+NAT_ThsndSep] ; AL <- thousand separator
		jmp	short FormDateTime8 ; store character

Znak "," se nahradí znakem oddělovače řádů tisíců z tabulky národnostních informací a uloží se opakovaně do cílového bufferu.


; ------------- Data entry separator ";"

FormDateTimeE:	mov	eax,[FDTNat]	; EAX <- nationality
		mov	al,[eax+NAT_EntrySep] ; AL <- entry separator
		jmp	short FormDateTime8 ; store character

Znak ";" se nahradí znakem oddělovače datových položek z tabulky národnostních informací a uloží se opakovaně do cílového bufferu.


; ------------- Quoted string ' "

FormDateTimeQ:	mov	ah,al		; AH <- quoted character
		shr	ecx,1		; ECX <- number of real characters
		jnc	short FormDateTime8 ; store characters
		jnz	short FormDateTime7 ; we must store characters first
FormDateTimeQ2:	or	ebx,ebx		; end of source text?
		jz	short FormDateTime9 ; no other characters
		mov	al,[edx]	; AL <- read character
		dec	ebx		; EBX <- decrease source counter
		inc	edx		; EDX <- increase text pointer
		cmp	al,ah		; end of string?
		je	FormDateTimeQ4	; end of string
FormDateTimeQ3:	dec	esi		; decrease free space
		stosb			; store character into buffer
		jnz	FormDateTimeQ2	; next character
FormDateTime9A:	jmp	short FormDateTime9 ; end of text

FormDateTimeQ4:	or	ebx,ebx		; end of source text?
		jz	short FormDateTime9 ; end of text
		cmp	al,[edx]	; duplicated character?
		jne	short FormDateTime1A ; it is valid end of string
		dec	ebx		; EBX <- decrease source counter
		inc	edx		; EDX <- increase text pointer
		jmp	short FormDateTimeQ3

Znaky jednoduchých a dvojitých uvozovek ' a " uvozují text, který se do cílového bufferu uloží v nezměněném tvaru. Jsou-li tyto znaky uvedeny opakovaně za sebou, znamenají platný znak. Proto je počet znaků v registru ECX snížen na polovinu. Pokud nezůstal žádný lichý znak uvozovek, šlo pouze o platné znaky a ty se uloží do výstupního bufferu. Pokud zůstal lichý znak, ale přitom byly zadány i platné párové znaky, lichý znak se navrátí (dekódování textu se nechá na později) a uloží se nejdříve pouze párové znaky uvozovek.

V cyklu pro dekódování textu uvnitř uvozovek se testem registru EBX ověří, zda je připraven další znak ve zdrojovém bufferu. Pokud není, funkce se ukončí. Je-li připraven znak, načte se do registru AL a ukazatel EDX a čítač EBX se posunou.

Není-li nový znak shodný se znakem uvozovek (který je uchován v registru AH), uloží se znak do výstupního bufferu. Současně je dekrementací registru ESI ověřeno volné místo v cílovém bufferu. Pokud dojde k zaplnění cílového bufferu, funkce se ukončí.

Pokud byl nalezen znak uvozovek, otestuje se zda je připraven další zdrojový znak. Pokud ne, byl konec textu platný a funkce se ukončí. Je-li připraven další znak, zkontroluje se zda je to opět znak uvozovek. Pokud ano, je to platný znak uvnitř textu a tento znak uvozovek se uloží do cílového bufferu a pokračuje se ve zpracování textu v uvozovkách. Je-li to jiný znak, obsluha textu v uvozovkách je ukončena.


; ------------- Hour 12 hours "h"

FormDateTimeHS:	mov	al,[ebp+DATETIME_Hour] ; AL <- hour
		add	al,11		; correction, hour - 1
		aam	12		; AL <- hour in 12h cycle
		movzx	eax,al		; EAX <- hour
		inc	eax		; EAX <- hour
		jmp	short FormDateTimeAS2

Znak "h" označuje hodiny ve 12-hodinovém cyklu. Do registru AL se připraví údaj hodin. Přičtením 11 se údaj dekrementuje (aby mohl být výsledek v rozsahu 1 až 12), instrukcí AAM se převede do intervalu 0 až 11 a inkrementací se převede do intervalu 1 až 12 a přejde se na obsluhu dekódování čísla do bufferu.


; ------------- Hour 24 hours "H"

FormDateTimeHC:	movzx	eax,byte [ebp+DATETIME_Hour] ; EAX <- hour
		jmp	short FormDateTimeAS2

Znak "H" označuje hodiny ve 24-hodinovém cyklu. Do registru EAX se připraví údaj hodin a přejde se na obsluhu dekódování čísla do bufferu.


; ------------- Day in month "d", "dd"

FormDateTimeDS:	movzx	eax,byte [ebp+DATETIME_Day] ; EAX <- day in month
		cmp	ecx,byte 3	; 3 characters?
		jb	short FormDateTimeAS2 ; 1 or 2 characters

; ------------- Day of week "ddd", "dddd"

		mov	al,NAT_ShortWeek ; EAX <- table of short names
		je	FormDateTimeDS6	; use short names
		mov	al,NAT_LongWeek	; EAX <- long names
FormDateTimeDS6:movzx	ecx,byte [ebp+DATETIME_WDay] ; day of week
FormDateTimeDS7:add	eax,[FDTNat]	; EAX <- + nationality
		mov	ecx,[eax+ecx*4-4] ; ECX <- text data
		mov	ah,[ecx+TEXT_Length] ; AH <- text length
		add	ecx,TEXT_Text	; EBX <- start of text
		inc	ah		; prepare character counter
FormDateTimeDS8:dec	ah		; decrease character counter
		jz	short FormDateTime1A ; end of text, get next character
		mov	al,[ecx]	; AL <- get character
		inc	ecx		; increase text pointer
		dec	esi		; decrease free space
		stosb			; store character into buffer
		jnz	FormDateTimeDS8	; next character
		jmp	short FormDateTime9A ; buffer is full

Znak "d" označuje den v měsíci nebo den v týdnu. Je-li počet znaků "d" 1 nebo 2, jedná se o označení dne v měsíci. Do registru EAX se načte číslo dne v měsíci a přejde se na dekódování čísla do bufferu.

Je-li počet znaků 3, jedná se o označení dne v týdnu v krátkém formátu jmen (3 znaky). Pro větší počet znaků se použije dlouhý formát jmen. Do registru EAX se připraví offset textu ve struktuře národnostních informací a s jeho pomocí se do registru ECX načte ukazatel na textová data s příslušným textem.

Do registru AH se připraví délka textu a registr ECX se posune na začátek textu. Postupně se načítají znaky textu jména dne a ukládají se do výstupního bufferu. Přitom je dekrementací registru ESI kontrolováno volné místo v cílovém bufferu. Pokud dojde k zaplnění cílového bufferu, funkce se ihned ukončí.


; ------------- Absolute day in year "a"

FormDateTimeAS:	movzx	eax,word [ebp+DATETIME_YDay] ; EAX <- day in year
		inc	eax		; correction
FormDateTimeAS2:call	UDWToTextBuf	; convert absolute day in year
FormDateTime1A:	jmp	FormDateTime1	; next character

Znak "a" označuje absolutní den v roce. Do registru EAX se připraví číslo dne v roce a inkrementací se posune na bázi 1. Voláním funkce UDWToTextBuf se číslo zkonvertuje do výstupního bufferu, registr ECX přitom udává minimální počet číslic.


; ------------- Minute "m"

FormDateTimeMS:	movzx	eax,byte [ebp+DATETIME_Min] ; EAX <- minute
		jmp	short FormDateTimeAS2

Znak "m" označuje minuty. Do registru EAX se připraví údaj minut a přejde se na obsluhu dekódování čísla do bufferu.


; ------------- Month "M"

FormDateTimeMC:	movzx	eax,byte [ebp+DATETIME_Month] ; EAX <- month
		cmp	ecx,byte 3	; 3 characters?
		jb	short FormDateTimeAS2 ; 1 or 2 characters
		cmp	ecx,byte 4	; 4 characters?
		movzx	ecx,al		; ECX <- month
		mov	al,NAT_ShortMonth ; EAX <- table of short names
		jb	FormDateTimeMC2	; 3 characters, use short names
		mov	al,NAT_LongMonth ; EAX <- long names
		je	FormDateTimeMC2	; 4 characters, use long names
		mov	al,NAT_LongMonth1 ; EAX <- long names, 1st case
FormDateTimeMC2:jmp	short FormDateTimeDS7

Znak "M" označuje měsíc. Do registru EAX se připraví údaj měsíce. Je-li počet znaků "M" 1 nebo 2, přejde se na obsluhu dekódování čísla do bufferu.

Do registru AL se připraví offset tabulky jmen měsíců - pro 3 znaky tabulka krátkých jmen (3 znaky), pro 4 znaky tabulka dlouhých jmen ve skloňovaném tvaru pro použití v datu a pro více znaků tabulka jmen v 1. pádu. Poté se přejde na dekódování textu do bufferu, registr ECX obsahuje index textu.


; ------------- Period or era "g" (BCE=Before Common Era, CE=Common Era)

FormDateTimeGS:	mov	al,NAT_ShortBCECE ; EAX <- table of short names
		dec	ecx		; short form?
		jz	FormDateTimeGS2	; use short names
		mov	al,NAT_LongBCECE ; EAX <- long names
FormDateTimeGS2:cmp	word [ebp+DATETIME_Year],0 ; before common era?
		setns	cl		; CL <- 0 if BCE, 1 if CE
		movzx	ecx,cl		; ECX <- 0 if BCE, 1 if CE
		inc	ecx		; index correction
		jmp	short FormDateTimeDS7 ; write era name

Znak "g" představuje označení letopočtu. Pro 1 znak "g" se do registru AL připraví offset krátkého označení letopočtu a pro více znaků offset dlouhého označení letopočtu. Testem absolutního čísla roku se rozliší, zda se jedná o rok našeho letopočtu nebo před naším letopočtem a podle toho je nastaven obsah registru na hodnotu 0 nebo 1. Poté se přejde na dekódování textu do bufferu.


; ------------- AM/PM "t" (AM=ante meridiem, PM=post meridiem)

FormDateTimeTS:	mov	al,NAT_ShortAMPM ; EAX <- table of short names
		dec	ecx		; short form?
		jz	FormDateTimeTS2	; use short names
		mov	al,NAT_LongAMPM	; EAX <- long names
FormDateTimeTS2:cmp	byte [ebp+DATETIME_Hour],12 ; afternoon?
		setae	cl		; CL <- 0 if AM, 1 if PM
		movzx	ecx,cl		; ECX <- 0 if BCE, 1 if CE
		inc	ecx		; index correction
		jmp	short FormDateTimeDS7 ; write era name

Znak "t" představuje označení dopoledne nebo odpoledne. Pro 1 znak "t" se do registru AL připraví offset krátkého označení a pro více znaků offset dlouhého označení. Testem hodiny se rozliší, zda se jedná o dopoledne nebo odpoledne a podle toho je nastaven obsah registru na hodnotu 0 nebo 1. Poté se přejde na dekódování textu do bufferu.


; ------------- Seconds "s"

FormDateTimeSS:	movzx	eax,byte [ebp+DATETIME_Sec] ; EAX <- seconds
		jmp	short FormDateTimeAS2

Znak "s" označuje sekundy. Do registru EAX se připraví údaj sekund a přejde se na obsluhu dekódování čísla do bufferu.


; ------------- Week number "w"

FormDateTimeWS:	movzx	eax,byte [ebp+DATETIME_Week] ; EAX <- week
		jmp	short FormDateTimeAS2

Znak "w" označuje číslo týdne v roce. Do registru EAX se připraví údaj čísla týdne v roce a přejde se na obsluhu dekódování čísla do bufferu.


; ------------- Year "y"

FormDateTimeYS:	movsx	eax,word [ebp+DATETIME_Year] ; EAX <- year
		or	eax,eax		; negative?
		jns	FormDateTimeYS2	; not negative
		neg	eax		; EAX <- absolute value
		inc	eax		; correction
FormDateTimeYS2:cmp	ecx,3		; limit number of digits?
		ja	FormDateTimeYC2	; don't limit number of digits
		push	edx		; push EDX
		push	ecx		; push ECX
		cmp	cl,2		; 2 digits?
		mov	cl,10		; ECX <- limit number to 1 digit
		jb	FormDateTimeYS3	; 1 digit
		mov	cl,100		; ECX <- limit number to 2 digits
		je	FormDateTimeYS3	; 2 digits
		mov	cx,1000		; ECX <- limit number to 3 digits
FormDateTimeYS3:xor	edx,edx		; EDX <- 0
		div	ecx		; EDX <- limited number
		xchg	eax,edx		; EAX <- year
		pop	ecx		; pop ECX
		pop	edx		; pop EDX
		jmp	short FormDateTimeYC2

Znak "y" označuje číslo roku v absolutním tvaru (bez rozlišení letopočtu). Do registru EAX se připraví rok a upraví se rok před naším letopočtem na kladný údaj s bází 1.

Jsou-li zadány více než 3 znaky, není číslo nijak upravováno a přejde se přímo na dekódování čísla s tím, že počet znaků udává minimální počet číslic. Pro menší počet znaků je číslo nejdříve upraveno tak, že se použijí jen poslední 3, 2 nebo 1 číslice (souhlasně s počtem znaků).


; ------------- Signed year "Y"

FormDateTimeYC:	movsx	eax,word [ebp+DATETIME_Year] ; EAX <- year
FormDateTimeYC2:call	SDWToTextBuf	; convert signed year
FormDateTime1B:	jmp	FormDateTime1	; next character

Znak "Y" označuje číslo roku ve znaménkovém tvaru (rok před naším letopočtem má číslo 0, -1, -2, ...). Do registru EAX se připraví rok a údaj se dekóduje pomocí funkce SDWToTextBuf do výstupního bufferu.


; ------------- Seconds fraction "f"

FormDateTimeFS:	call	FormDateSFrac	; decode seconds fraction
		jmp	short FormDateTime1B ; next character

Znak "f" označuje zlomkovou část sekund bez odstranění koncových nul. Dekódování se provede pomocí funkce FormDateSFrac.


; ------------- Seconds fraction, truncate trailing zeros "F"

FormDateTimeFC:	call	FormDateZFrac	; decode seconds fraction
		jmp	short FormDateTime1B ; next character

Znak "F" označuje zlomkovou část sekund s odstraněním koncových nul. Dekódování se provede pomocí funkce FormDateZFrac.


; ------------- Julian date and time "j"

FormDateTimeJS:	push	ebx		; push EBX
		push	edx		; push EDX
		mov	ebx,ebp		; EBX <- DATETIME structure
		call	DateTimeToAbs	; convert to absolulte time -> EDX:EAX
		mov	ebx,FORMTYPE_Cap<<FORMPAR_TypeF_b ; don't round
FormDateTimeJS2:dec	ecx		; ECX <- without one character
		mov	bl,cl		; BL <- precision
		call	AbsToJulian	; convert to Julian date -> ST0
		mov	ecx,[FDTNat+8]	; ECX <- nationality
		call	FltToTextBuf	; convert number into buffer
		ffreep	st0		; free number from FPU stack
		pop	edx		; pop EDX
		pop	ebx		; pop EBX
		jmp	short FormDateTime1B ; next character

Znak "j" označuje Juliánské datum a čas bez ořezání koncových nul. Počet znaků "j" představuje počet desetinných míst.

Datum a čas ve struktuře DATETIME se pomocí funkce DateTimeToAbs zkonvertuje na absolutní čas v registrovém páru EDX:EAX. Pomocí funkce AbsToJulian se zkonvertuje na Juliánský čas do registru ST0.

Do registru BL se načte požadovaná přesnost, do registru ECX ukazatel na národnostní popisovač (offset "+8" zajistí přeskočení dalších 2 registrů v zásobníku) a údaj se pomocí funkce FltToTextBuf zkonvertuje na text. V registru EBX je přitom nastaven příznak velkého písmene, který zajistí, že číslo se zaokrouhluje vždy dolů. Nakonec se číslo uvolní instrukcí ffreep ze zásobníku koprocesoru.


; ------------- Julian date and time, truncate trailing zeros "J"

FormDateTimeJC:	push	ebx		; push EBX
		push	edx		; push EDX
		mov	ebx,ebp		; EBX <- DATETIME structure
		call	DateTimeToAbs	; convert to absolulte time -> EDX:EAX
               mov ebx,(((FORMFLAG2_Alt2+FORMFLAG2_Thsnd)<<8)+FORMTYPE_Cap)<<16
		jmp	short FormDateTimeJS2

Znak "J" označuje Juliánské datum a čas s ořezáním koncových nul. Počet znaků "J" představuje počet desetinných míst.

Datum a čas ve struktuře DATETIME se pomocí funkce DateTimeToAbs zkonvertuje na absolutní čas v registrovém páru EDX:EAX. Dále se pokračuje společnou částí s obsluhou znaku "j" s tím rozdílem, že parametry v registru EBX zajistí ořezání koncových nul.


; -----------------------------------------------------------------------------
;            Local function - convert seconds fraction into buffer
; -----------------------------------------------------------------------------
; INPUT: ECX=number of digits, EDI=buffer, ESI=size of buffer
; DESTROYS: EAX, ECX

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

FormDateSFrac:	push	ebx		; push EBX
		push	edx		; push EDX

Funkce FormDateSFrac uloží do výstupního bufferu zlomkovou část sekund bez ořezání koncových nul. Funkce je volána během obsluhy znaku "f". Na vstupu funkce obsahuje registr ECX požadovaný počet číslic, registr EDI cílový buffer a ESI velikost volného místa v cílovém bufferu. Funkce zničí obsah registrů EAX a ECX.


; ------------- Multiply nanoseconds with 1 0000 0000 0000 0000h/1 000 000 000

		xor	eax,eax		; EAX <- 0
		mov	al,4		; EAX <- coefficient HIGH
		mul	dword [ebp+DATETIME_NSec]; multiply nanoseconds
		xchg	eax,ebx		; EBX <- fractions
		mov	eax,4b82fa0ah	; EAX <- coefficient LOW
		mul	dword [ebp+DATETIME_NSec]; multiply nanoseconds
		add	ebx,edx		; EBX <- fractions
		inc	ebx		; rounding correction

Údaj zlomku sekund bude do bufferu ukládán od vyšších číslic, číslic bude maximálně 9. Proto se údaj převede na desetinné číslo a to tak, že se počet nanosekund vydělí číslem 1 000 000 000 (tedy maximálním možným údajem počtu nanosekund, tím se údaj dostane do rozsahu 0 až 0,999999999). Převod se provede vynásobením číslem 1 0000 0000 0000 0000h / 1 000 000 000 (ve 2 oddělených krocích), tím se v registrech EBX:EAX obdrží požadované desetinné číslo.


; ------------- Limit number of digits

		push	ecx		; push ECX
		cmp	ecx,9		; maximal number of digits
		jb	FormDateSFrac2	; number of digits is OK
		xor	ecx,ecx		; ECX <- 0
		mov	cl,9		; ECX <- 9, limit number of digits

Požadovaný počet číslic se omezí na maximální povolenou hodnotu 9.


; ------------- Decode digits

FormDateSFrac2:	mov	eax,10		; EAX <- multiplier
		mul	ebx		; multiple number by 10
		xchg	eax,ebx		; EBX <- new number * 10
		xchg	eax,edx		; EAX <- digit
		add	al,"0"		; AL <- convert to ASCII character
		dec	esi		; decrease free space
		stosb			; store character into buffer
		loopnz	FormDateSFrac2	; next character
		pop	ecx		; pop ECX
		jz	FormDateSFrac8	; buffer is full

Číslice se postupně dekódují do výstupního bufferu a to tak, že vynásobením desetinného čísla číslem 10 přeteče přes rozsah QWORD nová číslice, která se uloží do výstupního bufferu. Přitom je dekrementací registru ESI čítáno volné místo v cílovém bufferu. Pokud dojde k zaplnění cílového bufferu, funkce se ukončí.


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

		sub	ecx,9		; ECX <- rest of number
		jbe	FormDateSFrac8	; no digits left
		mov	al,"0"		; AL <- trailign zero character
FormDateSFrac6:	dec	esi		; decrease free space
		stosb			; store digit into buffer
		loopnz	FormDateSFrac6	; next digit

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

FormDateSFrac8:	pop	edx		; pop EDX
		pop	ebx		; pop EBX
		ret

Pokud bylo požadováno více číslic než 9, je nutno ještě doplnit zbývající nevýznamné nuly,. Během ukládání je dekrementací registru ESI čítáno volné místo v cílovém bufferu. Pokud dojde k zaplnění cílového bufferu, funkce se ukončí.


; -----------------------------------------------------------------------------
;      Local function - convert seconds fraction into buffer, truncate zeros
; -----------------------------------------------------------------------------
; INPUT: ECX=number of digits, EDI=buffer, ESI=size of buffer
; DESTROYS: EAX, ECX

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

FormDateZFrac:	push	ebx		; push EBX
		push	edx		; push EDX
		push	ebp		; push EBP

Funkce FormDateZFrac uloží do výstupního bufferu zlomkovou část sekund s ořezáním koncových nul. Funkce je volána během obsluhy znaku "F". Na vstupu funkce obsahuje registr ECX požadovaný počet číslic, registr EDI cílový buffer a ESI velikost volného místa v cílovém bufferu. Funkce zničí obsah registrů EAX a ECX.


; ------------- Multiply nanoseconds with 1 0000 0000 0000 0000h/1 000 000 000

		xor	eax,eax		; EAX <- 0
		mov	al,4		; EAX <- coefficient HIGH
		mul	dword [ebp+DATETIME_NSec]; multiply nanoseconds
		xchg	eax,ebx		; EBX <- fractions
		mov	eax,4b82fa0ah	; EAX <- coefficient LOW
		mul	dword [ebp+DATETIME_NSec]; multiply nanoseconds
		add	ebx,edx		; EBX <- fractions
		inc	ebx		; rounding correction

Údaj zlomku sekund bude do bufferu ukládán od vyšších číslic, číslic bude maximálně 9. Proto se údaj převede na desetinné číslo a to tak, že se počet nanosekund vydělí číslem 1 000 000 000 (tedy maximálním možným údajem počtu nanosekund, tím se údaj dostane do rozsahu 0 až 0,999999999). Převod se provede vynásobením číslem 1 0000 0000 0000 0000h / 1 000 000 000 (ve 2 oddělených krocích), tím se v registrech EBX:EAX obdrží požadované desetinné číslo.


; ------------- Limit number of digits

		cmp	ecx,9		; maximal number of digits
		jb	FormDateZFrac2	; number of digits is OK
		xor	ecx,ecx		; ECX <- 0
		mov	cl,9		; ECX <- 9, limit number of digits
FormDateZFrac2:	mov	ebp,ecx		; EBP <- remaining digits

Požadovaný počet číslic se omezí na maximální povolenou hodnotu 9 a uschová se do registru EBP.


; ------------- Decode digits

FormDateZFrac3:	mov	eax,10		; EAX <- multiplier
		mul	ebx		; multiple number by 10
		xchg	eax,ebx		; EBX <- number * 10
		xchg	eax,edx		; EAX <- digit
		cmp	al,1		; zero?
		jb	FormDateZFrac7	; skip zero
FormDateZFrac5:	cmp	ebp,ecx		; any zeros pending?
		je	FormDateZFrac6	; no zeros pending
		mov	byte [edi],"0"	; store pending zero
		inc	edi		; increase pointer
		dec	ebp		; decrease counter of pending zeros
		dec	esi		; decrease free space
		jnz	FormDateZFrac5	; next pending zero
		jmp	short FormDateZFrac8 ; buffer is full
FormDateZFrac6:	dec	ebp		; EBP <- remaining zeros
		add	al,"0"		; AL <- convert to ASCII character
		dec	esi		; decrease free space
		stosb			; store character into buffer
FormDateZFrac7:	loopnz	FormDateZFrac3	; next character

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

FormDateZFrac8:	pop	ebp		; pop EBP
		pop	edx		; pop EDX
		pop	ebx		; pop EBX
		ret

Číslice se postupně dekódují do výstupního bufferu a to tak, že vynásobením desetinného čísla číslem 10 přeteče přes rozsah QWORD nová číslice, která se uloží do výstupního bufferu. Přitom je dekrementací registru ESI čítáno volné místo v cílovém bufferu. Pokud dojde k zaplnění cílového bufferu, funkce se ukončí.

Ořezání koncových nul je zajištěno tak, že vyskytne-li se v čísle nula, ukládání číslice se přeskočí a pouze dochází k dekrementaci čítače číslic ECX.

Vyskytne-li se v čísle nenulová číslice, je porovnáním registru EBP a ECX zjištěno, zda byly nějaké nulové číslice přeskočeny. Pokud ne, číslice se uloží běžným způsobem. Kromě čítače ECX je současně posouván i čítač v EBP a testováno volné místo v cílovém bufferu dekrementací registru ESI. Dojde-li k přetečení cílového bufferu, funkce se ukončí.

Pokud nejsou registry EBP a ECX shodné, byly přeskočeny nějaké nuly a proto budou nejdříve uloženy. Ukládání probíhá tak dlouho, dokud se čítače v registrech ECX a EBP opět nevyrovnají. Současně je dekrementací registru ESI kontrolování volné místo v cílovém bufferu a v případě přetečení je funkce ukončena.


Obsah / Utility / TEXTFORM / FormDateTime