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

Obsah / Utility / TEXTFORM / FormDateTimeN

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

Související:

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

FormDateTimeN - Délka formátovaného textu data a času

Funkce FormDateTimeN zjistí délku zformátovaného textu data a času v bufferu.


; -----------------------------------------------------------------------------
;              Format date/time into text buffer - get text length
; -----------------------------------------------------------------------------
; 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)
; OUTPUT:	ESI = text length
; -----------------------------------------------------------------------------

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. Na výstupu obsahuje registr ESI délku textu v bajtech.


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

FormDateTimeN:	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

; ------------- Clear text length

		xor	esi,esi		; ESI <- 0, text length

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. Tato proměnná je shodná s funkcí FormDateTime a proto musí být registry do zásobníku uschovány stejně. Registr ESI je vynulován a bude představovat střadač délky textu.


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

FormDateTimeN2:	dec	ebx		; check next source character
		jl	short FormDateTimeN9 ; 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	FormDateTimeN4	; no other characters
FormDateTimeN3:	cmp	al,[edx]	; identical character?
		jne	FormDateTimeN4	; character is not identical
		inc	edx		; EDX <- increase text pointer
		inc	ecx		; ECX <- increase repeat counter
		dec	ebx		; EBX <- decrease source counter
		jnz	FormDateTimeN3	; 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.


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

FormDateTimeN4:	cmp	al,39		; string with '?
		je	short FormDateTimeNQ ; yes, string
		cmp	al,34		; string with "?
		je	short FormDateTimeNQ ; yes, string

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

		cmp	al,"A"		; minimal character
		jb	FormDateTimeN8	; invalid character
		cmp	al,"z"		; maximal character
		ja	FormDateTimeN8	; invalid character
		movzx	eax,al		; EAX <- character
		jmp	[FormDateTimNJmp+eax*4-"A"*4] ; jump

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

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

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

FormDateTimeN8:	add	esi,ecx		; ESI <- add characters
		jmp	short FormDateTimeN2 ; next character

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

FormDateTimeN9:	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 FormDateTimNJmp, pouze znaky uvozovek se obslouží samostatně. Neplatné znaky zvýší střadač délky textu ESI o ECX znaků.


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

FormDateTimeNQ:	mov	ah,al		; AH <- quoted character
		shr	ecx,1		; ECX <- number of real characters
		jnc	short FormDateTimeN8 ; store characters
		jnz	short FormDateTimeN7 ; we must store characters first
FormDateTimeNQ2:or	ebx,ebx		; end of source text?
		jz	short FormDateTimeN9 ; 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	FormDateTimeNQ4	; end of string
FormDateTimeNQ3:inc	esi		; increase text length
		jmp	short FormDateTimeNQ2 ; next character

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

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 přičtou ke střadači délky textu. Pokud zůstal lichý znak, ale přitom byly zadány i platné párové znaky, lichý znak se navrátí (zpracování textu se nechá na později) a přičtou se nejdříve pouze párové znaky uvozovek.

V cyklu pro zpracová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), inkrementuje se čítač délky textu.

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 přičte ke střadači textu. Je-li to jiný znak, obsluha textu v uvozovkách je ukončena.


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

FormDateTimeNHS: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 FormDateTimNAS2

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 zjištění délky čísla.


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

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

Znak "H" označuje hodiny ve 24-hodinovém cyklu. Do registru EAX se připraví údaj hodin a přejde se na obsluhu zjištění délky čísla.


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

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

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

		mov	al,NAT_ShortWeek ; EAX <- table of short names
		je	FormDateTimNDS6	; use short names
		mov	al,NAT_LongWeek	; EAX <- long names
FormDateTimNDS6:movzx	ecx,byte [ebp+DATETIME_WDay] ; day of week
FormDateTimNDS7:add	eax,[FDTNat]	; EAX <- + nationality
		mov	ecx,[eax+ecx*4-4] ; ECX <- text data
		movzx	eax,byte [ecx+TEXT_Length] ; EAX <- text length
		add	esi,eax		; ESI <- increase text length
		jmp	short FormDateTimeN2A ; end of text, get next character

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 zjištění délky čísla.

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 EAX se připraví délka textu. Z důvodu shody s obsluhou FormDateTime se délka načte jako bajt. Délka textu se přičte ke střadači délky.


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

FormDateTimeNAS:movzx	eax,word [ebp+DATETIME_YDay] ; EAX <- day in year
		inc	eax		; correction
FormDateTimNAS2:call	UDWToTextBufN	; get text length
		add	esi,eax		; increase text length
FormDateTimeN2A:jmp	FormDateTimeN2	; 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 UDWToTextBufN se zjstí délka textu čísla a přičte se ke střadači délky ESI.


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

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

Znak "m" označuje minuty. Do registru EAX se připraví údaj minut a přejde se na obsluhu zjištění délky čísla.


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

FormDateTimeNMC:movzx	eax,byte [ebp+DATETIME_Month] ; EAX <- month
		cmp	ecx,byte 3	; 3 characters?
		jb	short FormDateTimNAS2 ; 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	FormDateTimNMC2	; 3 characters, use short names
		mov	al,NAT_LongMonth ; EAX <- long names
		je	FormDateTimNMC2	; 4 characters, use long names
		mov	al,NAT_LongMonth1 ; EAX <- long names, 1st case
FormDateTimNMC2:jmp	short FormDateTimNDS7

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 zjištění délky čísla.

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 zjištění délky textu, registr ECX obsahuje index textu.


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

FormDateTimeNGS:mov	al,NAT_ShortBCECE ; EAX <- table of short names
		dec	ecx		; short form?
		jz	FormDateTimNGS2	; use short names
		mov	al,NAT_LongBCECE ; EAX <- long names
FormDateTimNGS2: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 FormDateTimNDS7 ; 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 obsluhu zjištění délky textu.


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

FormDateTimeNTS:mov	al,NAT_ShortAMPM ; EAX <- table of short names
		dec	ecx		; short form?
		jz	FormDateTimNTS2	; use short names
		mov	al,NAT_LongAMPM	; EAX <- long names
FormDateTimNTS2: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 FormDateTimNDS7 ; 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 obsluhu zjištění délky textu.


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

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

Znak "s" označuje sekundy. Do registru EAX se připraví údaj sekund a přejde se na obsluhu zjištění délky čísla.


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

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

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 zjištění délky čísla.


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

FormDateTimeNYS:movsx	eax,word [ebp+DATETIME_Year] ; EAX <- year
		or	eax,eax		; negative?
		jns	FormDateTimNYS2	; not negative
		neg	eax		; EAX <- absolute value
		inc	eax		; correction
FormDateTimNYS2:cmp	ecx,3		; limit number of digits?
		ja	FormDateTimNYC2	; don't limit number of digits
		add	esi,ecx		; increase text length
		jmp	short FormDateTimeN2A ; next character

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 obsluhu čísla s tím, že počet znaků udává minimální počet číslic. Pro menší počet znaků odpovídá skutečná délka čísla počtu znaků, takže může být střadač délky textu o tuto hodnotu přímo zvýšen.


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

FormDateTimeNYC:movsx	eax,word [ebp+DATETIME_Year] ; EAX <- year
FormDateTimNYC2:call	SDWToTextBufN	; get text length
		add	esi,eax		; increase text length
		jmp	short FormDateTimeN2A ; 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 pomocí funkce SDWToTextBufN se zjistí délka čísla.


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

FormDateTimeNFS:add	esi,ecx		; increase text length
FormDateTimeN1A:jmp	FormDateTimeN2	; next character

Znak "f" označuje zlomkovou část sekund bez odstranění koncových nul. Počet znaků přitom odpovídá počtu číslic, takže může být střadač délky textu o tuto hodnotu přímo zvýšen.


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

FormDateTimeNFC:call	FormDateZFracN	; decode seconds fraction
		jmp	short FormDateTimeN1A ; next character

Znak "F" označuje zlomkovou část sekund s odstraněním koncových nul. Pomocí funkce FormDateZFracN se zjistí délka čísla a ta se přičte ke střadači ESI..


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

FormDateTimeNJS: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
FormDateTimNJS2:dec	ecx		; ECX <- without one character
		mov	bl,cl		; BL <- precision
		call	AbsToJulian	; convert to Julian date -> ST0
		call	FltToTextBufN	; get text length
		ffreep	st0		; free number from FPU stack
		pop	edx		; pop EDX
		pop	ebx		; pop EBX
		add	esi,eax		; icrease text length
		jmp	short FormDateTimeN1A ; 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 a pomocí funkce FltToTextBufN se zjistí délka textu čísla, která se přičte ke střadači ESI. 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"

FormDateTimeNJC: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 FormDateTimNJS2

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 - get text length
; -----------------------------------------------------------------------------
; INPUT: ECX=number of digits, ESI=text length
; OUTPUT: ESI=new text length
; DESTROYS: EAX, ECX

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

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

Funkce FormDateZFracN zjistí délku zlomkové části 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 ESI obsahuje střadač délky textu. 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	FormDateZFracN2	; number of digits is OK
		xor	ecx,ecx		; ECX <- 0
		mov	cl,9		; ECX <- 9, limit number of digits
FormDateZFracN2: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

FormDateZFracN3: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	FormDateZFracN7	; skip zero
		sub	ebp,ecx		; EBP <- remaining zeros
		add	esi,ebp		; ESI <- increase text length
		mov	ebp,ecx		; EBP <- synchronize digit counter
		dec	ebp		; EBP <- remaining zeros
		inc	esi		; increase text length
FormDateZFracN7:loop	FormDateZFracN3	; next character

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

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

Číslice se postupně dekódují a to tak, že vynásobením desetinného čísla číslem 10 přeteče přes rozsah QWORD nová číslice.

Ořezání koncových nul je zajištěno tak, že vyskytne-li se v čísle nula, číslice se nezapočítává a pouze se dekrementuje čítač číslic ECX.

Vyskytne-li se v čísle nenulová číslice, je rozdílem registrů EBP a ECX zjištěn počet chybějících číslic a ty jsou přičteny ke střadači ESI.


Obsah / Utility / TEXTFORM / FormDateTimeN