Dword timestamp and optional compressed SYSTEMTIME structure

Started by Tight_Coder_Ex, May 06, 2011, 08:16:07 PM

Previous topic - Next topic

Tight_Coder_Ex

For a lot of my applications I needed a 32 bit timestamp that would show the relative time period between records and where the actual date is required a compressed version of SYSTEMTIME to convserve space.
Primarily EAX is to return the number of seconds since Jan 1, 2000 and returning a packed SYSTEMTIME or passing a static SYSTEMTIME to procedure are optional.

NOTE: I haven't incorporated any error checking so values in last centry or greater than 2137 will
       yield erroneous results.

Sorry for the messy code, but for some reason tab show properly in edit window, but not when it's posted  
   

; ==========================================================================================================
;        Compressed SYSTEMTIME buffer converts all values to 8 bit except wMilliseconds and omits wDayOfWeek
;        in same order as source.

;        ENTRY:        ECX = Pointer to compressed buffer, otherwise NULL if not required.
;                EDX = Pointer to SYSTEMTIME struct populated with predefined values or NULL if procedure
;                      is to calculate values based on call to GetSystemTime.

;        LEAVE:        EAX = 32 bit value representing number seconds elapsed since turn of century.
;                ECX & EDX Unchanged
;                EDX = Undefined

;        FLAGS:        Undefined.
; ----------------------------------------------------------------------------------------------------------


     NASM Prologue


TimeComp        enter        16, 0                                ; Create a SYSTEMTIME buffer by default
                push        esi
                push        edi                                ; Preserve essential as per M$ specs

        ; As this procedure conforms to _fastcall, preserve ECX & EDX as they could be part of a loop
        ; or an offset to next set of values.

                push        ecx
                push        edx


     MASM Prologue  and replace [ebp-16] with StkSysTime and .TC?? with @@: & @F - @B where applicable
     

TimeComp        proc        uses esi edi ecx edx

    LOCAL        StkSysTime : SYSTEMTIME


     Body of procedure written for NASM


        ; Evaluate EDX and if not defined needs to read current date/time into SYSTEMTIME

                mov        esi, edx                        ; Assume EDX is defined
                or        edx, edx
                jnz        .TC01

                lea        esi, [ebp-16]                        ; Point to local buffer
                push        ecx                                ; Need to save pointer to compresed buffer
                push        esi                                ; Arg1: lpSystemtime
                call        _GetSystemTime@4
                pop        ecx                                ; Incase ECX is undefined.

        ; Evaluate ECX

        .TC01        mov        edi, ecx                        ; Assume ECX is defined
                or        ecx, ecx
                jnz        .TC02

        ; There is no conflict here as if neither ECX or EDX have been defined SYSTEMTIME also becomes
        ; the compressed buffer as niether need be passed back to caller

                lea        edi, [ebp-16]                        ; Point to local buffer

        ; Now we can begin calculating seconds since 2000-01-01

        .TC02        lodsw                                        ; wYear
                cwde                                        ; Sign extend to 32 bits
                sub         ax, 2000                        ; Skew from begining of century
                stosb                                        ; Save Year
                mov        ecx, eax
                imul        eax, 365                        ; Number of days to beginning of current year
                mov         dl,  cl
                shr        ecx, 2                                ; Determine number of leap years.
                add        ecx, eax
                lodsd                                        ; Get month we are in and waste wDayOfWeek
                cwde
                stosb                                        ; Save month
                dec        eax                                ; Zero index month so Offset will be read correctly

        ; At this point EAX represents the actual number of day (zero indexed), however, if we are in
        ; a leap year and before Mar 1, EAX need be decremented once more.

                and         dl, 11B                        ; Strip bits 2 & 3 of DL
                jnz        .TC03

                cmp         al, 1                                ; Is month Jan or Feb
                ja        .TC03

                dec        cx                                ; Bump as we are before Feb 29

        .TC03        add        cx, word [Offsets + eax * 2]        ; This works becuase max days is C22E (16 bit)
                lodsw                                        ; Get day number
                stosb                                        ; Save it
                add        ecx, eax                        ; Actual number of days to midnight current day
                imul        ecx, 24                                ; ECX = Total hours to midnight today
                mov         dl, 0

        ; Loop through TC04 twice in evaluating Hour & Minute of structure
       
        .TC04        lodsw                                        ; Get next value
                stosb                                        ; Save in compressed buffer
                add        ecx, eax                        ; Update total value
                imul        ecx, 60
                btc        edx, 1
                jnc        .TC04

        ; Finish process by adding seconds to EAX so total is returned in EAX
       
                lodsw                                        ; Load number of seconds
                stosb
                add        eax, ecx                        ; Return number of seconds in EAX

                movsw                                        ; Post milliseconds unchanged (16 bit value)


    This epilog not required by MASM users
   
               
        ; Restore values passed by caller

                pop        edx
                pop        ecx

                pop        edi
                pop        esi                                ; Restore essential registers
                leave
                ret

                align        8
               
; -=*=-  -=*=-  -=*=-  -=*=-  -=*=-  -=*=-  -=*=-  -=*=-  -=*=-  -=*=-  -=*=-  -=*=-  -=*=-  -=*=-  -=*=-

Offsets        dw        0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
                  align        16
                 


MASM users will probably put Offsets into .const

dedndave

seems to me that the high dword of FILETIME would work
i know the OS and misc programs use a similar 32-bit date/time stamp all over the place in the system registry

LocalFileTimeToFileTime
SystemTimeToFileTime

also - there is "time_t" (tea time ?)
http://msdn.microsoft.com/en-us/library/ms724228%28v=VS.85%29.aspx

you can probably grab that from a function in msvcrt

Tight_Coder_Ex

For some reason I've been having problems using msvcrt.dll in NASM and normally if I was contemplating a windows only environment FILETIME would have be a viable alternative.  This algo lends itself to minimum modification to accommodate either int 0x80 Linux or int 0x1A in the BIOS.

As everything I do is hobby driven, this algo was born out of a concept and then playing with different versions to produce what I think is the most efficient procedure.

Thanks for your input Dave as the criteria you depicted is exactly those I had contemplated at inception, so I'm glad I haven't missed anything I hadn't already thought about.


There is no particular reason for highlighting NASM other than, I wonder what these buttons do and come to the conclusion, this is cool

dedndave

when i first started out with 32-bit code, i played a little with nasm and fasm, and a few others
after umpteen years of writing 16-bit code with masm, i just couldn't get the hang of the syntax - lol

Tight_Coder_Ex

One thing about it, they are both very capable tools, as are probably GoASM and the like, but I have only experience with these two.  The only advantage NASM has it works on Linux  :U

donkey

You could try a timedatestamp (#seconds since January 1, 1970), it is the standard 32 bit format used in PE files.

This converts a file time to a timedatestamp.

mov ecx,[pFileTime]
mov eax,[ecx]
mov edx,[ecx+4]
sub eax,0D53E8000h
sbb edx,0019DB1DEh
mov ecx,10000000
div ecx
mov [TimeDateStamp],eax


This converts it the other way

mov eax,[TimeDateStamp]
mov edx,10000000
mul edx
add eax,0D53E8000h
adc edx,0019DB1DEh
mov ecx,[pFileTime]
mov [ecx],eax
mov [ecx+4],edx


From FileTime to system time is a standard API function.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Tight_Coder_Ex

This algo incorporates Edgar's snippet and still allows me to dynamically have process assert SYSTEMTIME or pass it in ECX.

I may be doing a no no, but as SystemTimeToFileTime has already set EDX to the proper value so I'm not re-reading it.


00 C8180000               enter   18h,0
04 56                     push    esi
05 57                     push    edi
06 89CF                   mov     edi,ecx
08 09C9                   or      ecx,ecx
0A 7509                   jnz     15

0C 8D7DE8                 lea     edi,[ebp-18h]
0F 57                     push    edi
10 E8 kernel32            call    GetSystemTime

15 8D75F8                 lea     esi,[ebp-8]
18 56                     push    esi
19 57                     push    edi
1A E8 kernel32            call    SystemTimeToFileTime
1F AD                     lodsd
20 2D00803ED5             sub     eax,0D53E8000h
25 81DADEB19D01           sbb     edx,19DB1DEh
2B B980969800             mov     ecx,989680h
30 F7F1                   div     ecx
32 5F                     pop     edi
33 5E                     pop     esi
34 C9                     leave
35 C3                     ret

dedndave

definately a no-no   :bg
it may work on your system, but not under some other OS

try this...
        push    edx
        push    eax
        INVOKE  SystemTimeToFileTime,edi,esp
        pop     eax
        pop     edx
        sub     eax,0D53E8000h
        sbb     edx,19DB1DEh
        mov     ecx,989680h
        div     ecx

donkey

"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable


Tight_Coder_Ex

Thanks guys, now I can start working on a way to display date string, maybe even sensitive to regional settings



00    57                push    edi                   Preserve outside frame as ESP modified @ 0x0A if ECX is NULL
01    55                push    ebp
02    89E5              mov     ebp, esp

04    89CF              mov     edi, ecx
06    09C9              or      ecx, ecx
08    75 0B             jnz     15                    Branch if a pointer to SYSTEMTIME passed in ECX

0A    83C4 F0           add     esp, -10              SYSTEMTIME structure
0D    54                push    esp
0E    E8 kernel32       call    GetSystemTime
13    89E7              mov     edi, esp

15    55                push    ebp
16    55                push    ebp
17    54                push    esp
18    57                push    edi
19    E8 kernel32       call    SystemTimeToFileTime
1E    58                pop     eax
1F    5A                pop     edx

                        Convert number of nanoseconds since 1601-01-01 to seconds since 1970-01-01
                       
20    2D 00803ED5       sub     eax, D53E8000
25    81DA DEB19D01     sbb     edx, 19DB1DE
2B    B9 80969800       mov     ecx, 989680
30    F7F1              div     ecx                   Result returned in EAX

32    C9                leave
33    5F                pop     edi
34    C3                ret

donkey

Hi Tight_Coder_Ex,

Although I occasionally like to use the push/push/call method with my code, I am generally trying to avoid it these days. It makes changing code to X64 a nightmare and for the code above both API calls could have been changed to an INVOKE call without any loss in performance, they would have encoded exactly the same so there is no advantage in not using INVOKE. However, writing code like that will seriously impact on the portability of your application to 64 bits should you want to do that at some time in the future. This might be nit-picking but I have gone through a lot of my old code and some of it I just gave up and rewrote as the number of stack tricks to track down made it more practical just to do a complete rewrite. Having learned my lesson, I generally only write more easily portable code now (ie use INVOKE and pay attention to register selection).
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Tight_Coder_Ex

Duly noted Edgar, but as application development is nothing more than a hobby for me now, experimentation is primary and I'm sort of contemplating building a turnkey system that has BIOS calls as its foundation.  Hopefully the task will not be to arduous porting my snippets to that type of platform.

Most often INVOKE would work for me whether it be in ML or NASMW, but there are many times, espcially with those API's that have several parameters like CreateWindowEx, that I do calculations while building the stack which can't be done with INVOKE, so it's been more out of habit that I haven't used it.

I do appreciate any and all input as innocuous as some postings may be, I can usually glean something from my peers or more often than not, those much more experienced than myself.

Peter