News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

freq. counter revisited

Started by SteveAsm, March 09, 2012, 02:30:02 AM

Previous topic - Next topic

SteveAsm

Okay guys,
I hate to do this to you, but, I'm stumped (or maybe just tired) on this one.
I've searched the forum archives, for both LARGE_INTEGER and QueryPerformanceFrequency, for an example and I've got information overload.
I'm trying to translate this C code to ASM for use with JWasm.


double get_HiRez_Timer()
{
    double value, dFreq, dCount;
    LARGE_INTEGER lpFreq, lpCount;


    QueryPerformanceFrequency(&lpFreq);
    dFreq = (double) (lpFreq.HighPart * 2 ^ 32) + lpFreq.LowPart;

    QueryPerformanceCounter(&lpCount);
    dCount = (double) (lpCount.HighPart * 2 ^ 32) + lpCount.LowPart;
    value = dCount / dFreq;

    return value;
}


The problem I'm having is with LARGE_INTEGER and the HighPart/LowPart stuff.
The archives have several examples for using the QPF and QPC functions, but, not the way it's illustrated in the above.
I looked in the .inc files, but, I didn't see LARGE_INTEGER defined in this way.

Just as in the above example, my proc will be called and the result will either be on ST(0) or in QWORD:value.
I deal with the result elsewhere.
Any ideas on how I would translate this ?

Thanks guys.
Steve

dedndave

#1
a large integer is a qword
seeing as we can't directly access a qword with a single register, we usually use 2 dwords
it doesn't matter how you type it, really - you are passing a pointer to the function
if you intend to load it into the FPU - and that's all you want to do with it - you can type it as a qword
quite often, i just make room on the stack   :P
        push    edx
        push    eax
        INVOKE  QueryPerformanceCounter,esp
        pop     eax
        pop     edx
;EDX:EAX = perf counter


if you want to access 2 dwords as a qword, you can use a size override
LoDword dd ?
HiDword dd ?

        fld qword ptr LoDword


or visa-versa
qwValue dq ?

        mov     eax,dword ptr qwValue
        mov     edx,dword ptr qwValue+4

MichaelW

Steve,

The C code that you're trying to translate is a mess. In BASIC ^ is the exponentiation operator, but in C it's the bitwise XOR operator. And there is no need to combine the low and high parts into a QWORD because the LARGE_INTEGER union is laid out with the low-order DWORD at the lower address and the high-order DWORD at the highest address, specifically so it can be accessed as two DWORD components or as a single QWORD component.
eschew obfuscation

dedndave

at any rate - you wanted to translate to asm
i think this will work....

get_HiRez_Timer PROTO :LPVOID,:LPVOID

;
;
;

get_HiRez_Timer PROC lpdFreq:LPVOID,lpdCount:LPVOID

;Call With: lpdFreq  = pointer to dFreq (real8)
;           lpdCount = pointer to dCount (real8)

        LOCAL   qwTemp:QWORD

        INVOKE  QueryPerformanceFrequency,addr qwTemp
        fild    qwTemp
        mov     edx,lpdFreq
        fstp real8 ptr [edx]

        INVOKE  QueryPerformanceCounter,addr qwTemp
        fild    qwTemp
        mov     edx,lpdCount
        fstp real8 ptr [edx]

        ret

get_HiRez_Timer ENDP


that assumes you do not want the qword integer values Freq and Count
it also assumes that the FPU has an empty register available   :P

SteveAsm

Thanks Dave,
I think I've got both versions working.
The stack version is certainly easy enough.

SteveAsm

Quote from: MichaelW on March 09, 2012, 09:24:55 AM
The C code that you're trying to translate is a mess.
Well Mike, that may be your opinion, but the code does seem to work.
I don't recall the link where I got it, (it's been months ago), but, I think it was on MSDN.

Quote
In BASIC ^ is the exponentiation operator, but in C it's the bitwise XOR operator.
Yeh, I can't explain why the XOR.

Quote
And there is no need to combine the low and high parts into a QWORD because the LARGE_INTEGER union is laid out with the low-order DWORD at the lower address and the high-order DWORD at the highest address, specifically so it can be accessed as two DWORD components or as a single QWORD component.
Hey, I just copied the code, I didn't author it.
Try it.

MichaelW

I did try it.

//=============================================================================
#include <windows.h>
#include <conio.h>
#include <stdio.h>
//=============================================================================

double get_HiRez_Timer()
{
    double value, dFreq, dCount;
    LARGE_INTEGER lpFreq, lpCount;

    QueryPerformanceFrequency(&lpFreq);
    dFreq = (double) (lpFreq.HighPart * 2 ^ 32) + lpFreq.LowPart;

    QueryPerformanceCounter(&lpCount);
    dCount = (double) (lpCount.HighPart * 2 ^ 32) + lpCount.LowPart;
    value = dCount / dFreq;

    return value;
}

double _get_HiRez_Timer()
{
    LARGE_INTEGER freq, count;
    QueryPerformanceFrequency( &freq );
    QueryPerformanceCounter( &count );
    return (double) count.QuadPart / (double) freq.QuadPart;
}

void main( void )
{
    printf( "%f\n", get_HiRez_Timer() );
    printf( "%f\n", _get_HiRez_Timer() );
    printf( "%d\n", GetTickCount() / 1000 );
    getch();
}


580.670504
61773.741514
61773

eschew obfuscation

SteveAsm


double _get_HiRez_Timer()
{
    LARGE_INTEGER freq, count;
    QueryPerformanceFrequency( &freq );
    QueryPerformanceCounter( &count );
    return (double) count.QuadPart / (double) freq.QuadPart;
}


That certainly does make more sense.
It escapes me the reason stated for all that other fluff.
Thanks.

baltoro

STEVE ASM,
Here is an article from MSDN Magazine (2004) that has alot of information about Windows timer functions and their accuracy:
Implement a Continuously Updating, High-Resolution Time Provider for Windows
Baltoro

SteveAsm

Thanks baltoro, interesting article.