/****************************************************************************
  Description: CPA Timer.lib. Window specific part.
 
  Author: F. Jentey.
  Last Update : 03/03/97
 ***************************************************************************/
    
 
#include "TimerPrv.h"
#include "Detect.h"
 
#include <ddraw.h>
 
 
extern tdstTimer   a_stTimerTab[];
short  g_wTimerPeriodOk = 0;
u_long  g_ulPeriodMin;
 
 
#ifndef WATCOM
/********  VISUAL 5 ************/
 
#define RDTSC   __asm _emit 0x0F  __asm _emit 0x31
 
void fn_vTimerPentiumCounter(stTimerCount *p_stTimerCount)
{
    __asm
    {
        RDTSC
        mov  ebx,dword ptr p_stTimerCount
        mov  [ebx],eax
        mov  [ebx+4],edx
    }
}
 
void fn_vTimerWaitVBL(void)
{
    __asm
    {
        push    eax
        push    edx
        mov     dx,03dah
Wait_VBLStart:
        in      al,dx
        and     ax,08h
        jz      Wait_VBLStart
 
Wait_VBLEnd:
        in      al,dx
        and     ax,08h
        jnz     Wait_VBLEnd
 
        pop     edx
        pop     eax
    }
}
        
#else
/********  WATCOM  ************/
 
void fn_vTimerPentiumCounter(stTimerCount *p_stTimerCount);
#pragma aux fn_vTimerPentiumCounter = \
         "db   0x0F,0x31" \
         "mov  [ebx],eax"  \
         "mov  4[ebx],edx"  \
         parm [ebx]      \
         modify [eax edx]
         
void fn_vTimerWaitVBL(void);
#pragma aux fn_vWaitVBL = \
        "mov dx,03dah" \
        "Wait_VBLStart:" \
        "in al,dx" \
        "and al,08h" \
        "jz Wait_VBLStart" \
        "Wait_VBLEnd:" \
        "in al,dx" \
        "and al,08h" \
        "jnz Wait_VBLEnd" \
        modify [eax edx]        
 
#endif
 
 
 
/****************************************/
/*  Get the clock frequency of the CPU. */
/****************************************/
u_long fn_ulTimerCpuClock()
{
    stTimerCount    count1, count2;
    u_long          t, ulresult;
 
    t = timeGetTime();
    fn_vTimerPentiumCounter(&count1);
    while (timeGetTime()-t < 1000);
    fn_vTimerPentiumCounter(&count2);
    if (count2.m_ulLowPart > count1.m_ulLowPart)
        ulresult =  count2.m_ulLowPart - count1.m_ulLowPart;
    else
        ulresult = count2.m_ulLowPart + 4294967295 - count1.m_ulLowPart;
 
    if ( ulresult < 63000000 )  return 60000000;
    if ( ulresult < 68000000 )  return 66600000;
    if ( ulresult < 80000000 )  return 75000000;
    if ( ulresult < 94000000 )  return 90000000;
    if ( ulresult < 106000000 ) return 100000000;
    if ( ulresult < 125000000 ) return 120000000;
    if ( ulresult < 138000000 ) return 133300000;
    if ( ulresult < 155000000 ) return 150000000;
    if ( ulresult < 170000000 ) return 166600000;
    if ( ulresult < 185000000 ) return 180000000;
    if ( ulresult < 205000000 ) return 200000000;
    if ( ulresult < 240000000 ) return 233300000;
    if ( ulresult < 272000000 ) return 266600000;
    if ( ulresult < 310000000 ) return 300000000;
    if ( ulresult < 340000000 ) return 333300000;
    if ( ulresult < 356000000 ) return 350000000;
    if ( ulresult < 380000000 ) return 366600000;
/* ANNECY AV*/
	if ( ulresult < 425000000 ) return 400000000;
	if ( ulresult < 475000000 ) return 450000000;
	if ( ulresult < 525000000 ) return 500000000;
	if ( ulresult < 575000000 ) return 550000000;
	if ( ulresult < 625000000 ) return 600000000;
    return ulresult;
/* END ANNECY AV*/
}
 
 
/***************************************************************/
/*  This function return the number of CPU cycle of a frame.   */
/***************************************************************/
u_long fn_ulTimerFrameLength()
{
    LPDIRECTDRAW    lpDD;
    HRESULT         ddrval;
    stTimerCount    stBefore, stAfter;
    u_long          ulTime,ulSomme = 0;
    long            i;
 
    if (!fn_lIsRdtscAvailable()) return (0);
 
    ddrval = DirectDrawCreate(NULL, &lpDD, NULL);
    if (ddrval != DD_OK) lpDD = NULL;
 
    ulTime = 0;
    fn_vTimerPentiumCounter(&stBefore);    /* La fonction Direct Draw WaitForVerticalBlank */
    for (i=0; i<64; i++)                   /* ne fonctionne pas correctement sous 95 !?    */
    {
        unsigned long ulNewTime;
        ddrval = IDirectDraw_WaitForVerticalBlank(lpDD,DDWAITVB_BLOCKBEGIN, NULL);
        if (ddrval != DD_OK) return (0);
        ulNewTime = timeGetTime();
        if (ulNewTime-ulTime < 5) return (0);
        ulTime = ulNewTime;
    }
    fn_vTimerPentiumCounter(&stAfter);
    if (stAfter.m_ulLowPart > stBefore.m_ulLowPart )
         ulSomme = stAfter.m_ulLowPart - stBefore.m_ulLowPart;
    else
         ulSomme = stAfter.m_ulLowPart + 4294967295 - stBefore.m_ulLowPart;
    stBefore.m_ulLowPart = stAfter.m_ulLowPart;
 
    if (lpDD) IDirectDraw_Release(lpDD);
 
    return (ulSomme >> 6);
}
 
 
/******************************************************************/
/*  Set the precision (in millisecond) of the timeGetTime and     */
/*  setEvent function                                             */
/******************************************************************/
void fn_vSetTimePeriod()
{
    TIMECAPS  tc;
    MMRESULT  err;
 
    if (!g_wTimerPeriodOk)
    {
        err = timeGetDevCaps(&tc, sizeof(TIMECAPS));
        if (err != TIMERR_NOERROR)
            g_ulPeriodMin = 0;
        else 
        {
            g_ulPeriodMin = min( max( tc.wPeriodMin, 1 ), tc.wPeriodMax); 
            /* TO DO:  Test the result */
            timeBeginPeriod(g_ulPeriodMin);
        }
        g_wTimerPeriodOk = 1;
    }
}
 
 
/**************************************************************************/
/*  Return the frequency of the specified timer type in tick per second   */
/**************************************************************************/
u_long fn_ulTimerTickPerSecond(short wTimerType)
{
    LARGE_INTEGER   perf;
    u_long          ulTicksPerSecond;
    
    switch (wTimerType)
    {
    case C_wTimerFrequencyLow:
        fn_vSetTimePeriod();
        ulTicksPerSecond = 1000;
        break;
 
    case C_wTimerFrequencyMedium:
        if ( QueryPerformanceFrequency(&perf) == TRUE )
            ulTicksPerSecond = perf.LowPart;
        else
            ulTicksPerSecond = 0;
        break;
 
    case C_wTimerFrequencyHigh:
        if (fn_lIsRdtscAvailable())
            ulTicksPerSecond = fn_ulTimerCpuClock();
        else
            ulTicksPerSecond = 0;
        break;
 
    default:
        ulTicksPerSecond = 0;
    }
    
    return (ulTicksPerSecond);
}
 
 
/*************************************************************************/
/*  Return the frequency of the specified event type in tick per second  */
/*************************************************************************/
u_long fn_ulTimerEventTickPerSecond(short wTimerType)
{
/*    TIMECAPS  tc;
    MMRESULT  err;*/
    u_long    ulTicksPerSecond;
    
    switch (wTimerType)
    {
    case C_wTimerFrequencyLow:
        /*err = timeGetDevCaps(&tc, sizeof(TIMECAPS));
        if ( err != TIMERR_NOERROR )
            ulTicksPerSecond = 0;
        else 
        { UINT
            ulTicksPerSecond = min( max( tc.wPeriodMin, 1 ), tc.wPeriodMax); 
              TO DO Test the result*/
        /*    timeBeginPeriod(ulTicksPerSecond);*/
        fn_vSetTimePeriod();
        ulTicksPerSecond = 1000;
        break;
        
    case C_wTimerFrequencyMedium: /* Not Available */
        ulTicksPerSecond = 0;
        break;
        
    case C_wTimerFrequencyHigh:   /* Not Available */
        ulTicksPerSecond = 0;
        break;
        
    default:
        ulTicksPerSecond = 0;
    }
    
    return ulTicksPerSecond;
}
 
 
/*********************************************************/
/*  Some operation to release event                      */
/*********************************************************/
short fn_wTimerReleaseEvents(/*u_long* p_EventFreqTab*/)
{
/*    short wTimerNum;*/
    /* First, kill all events */
/*    for (wTimerNum=0 ; wTimerNum < C_uwTimerMaxCount ; wTimerNum++ )
    {
        if ( (a_stTimerTab[wTimerNum].m_wTimerType >=  C_wTimerEvent)
              && (a_stTimerTab[wTimerNum].m_wTimerState != C_wTimerUnused) )
        {
            timeKillEvent( a_stTimerTab[wTimerNum].m_ulEventId );
        }
    }
*/
    /* TO DO */
    /* Test if timeBeginPeriod succed before */
    timeEndPeriod(g_ulPeriodMin);
    return (0);
}
 
/*******************************/
/*       Destroy an event      */
/*******************************/
short fn_wTimerDestroyEvent(u_long ulEventId)
{
    /* TO DO */
    /* Test if timeBeginPeriod succed before */
    if (timeKillEvent(ulEventId) !=  TIMERR_NOERROR)
        return (0);
    else
        return (1);
}
 
/***************************************************/
/*  Get the current value of the specified counter */
/***************************************************/
void fn_vTimerGetCounter(short wTimerType, stTimerCount* p_stValue)
{
    u_long         ulTemp;
 
    switch ( wTimerType )
    {
    case C_wTimerFrequencyLow:
        ulTemp = timeGetTime();
        p_stValue->m_ulLowPart = ulTemp;
        p_stValue->m_ulHighPart = 0;
        break;
 
    case C_wTimerFrequencyMedium:
        QueryPerformanceCounter((LARGE_INTEGER*)p_stValue);
        break;
 
    case C_wTimerFrequencyHigh:
        fn_vTimerPentiumCounter(p_stValue);
        break;
    }
}
 
 
/********************************/
/*  Wait during ulTicksToWait   */
/********************************/
void fn_vTimerWait(u_long ulTicksToWait)
{
    u_long ulTick;
 
    ulTick = timeGetTime();
    while ( timeGetTime() - ulTick < ulTicksToWait );
}
 
 
/***************************************************************************/
/*  Return the number of ticks per second for the fn_wTimerDelay function  */
/***************************************************************************/
u_long fn_ulTimerWaitTicksPerSecond()
{
    return (1000);
}
 
 
/***************************************************************************/
/*  This is the event server                                               */
/*  This function call the callback function associated with the event     */
/*  corresponding to the parameter uTimerID                                */
/***************************************************************************/
void PASCAL fn_vTimerEventsCallback(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
    short wTimerNum;
 
    /* First, finf the timer corrsponding to uTimerID */
    for (wTimerNum=0 ; wTimerNum < C_uwTimerMaxCount ; wTimerNum++ )
    {
        if ( a_stTimerTab[wTimerNum].m_wTimerType >=  C_wTimerEvent )
        {
            if ( (a_stTimerTab[wTimerNum].m_ulEventId == uTimerID) &&
                 (a_stTimerTab[wTimerNum].m_p_fn_vEventCallback) )
            {
                a_stTimerTab[wTimerNum].m_p_fn_vEventCallback();
                break;
            }
        }
    }
}
 
 
/***********************************************************************************/
/*  Create an event                                                                */
/*  ulPeriod is the period of the event in ticks                                   */
/*  p_fn_vEventCallback is a pointer to the callback function of the event         */
/*  ulEventType must be set with C_ulTimerEventOneTime or C_ulTimerEventPeriodic   */
/***********************************************************************************/
short fn_wTimerNewEvent(
                        u_long  ulPeriod,
                        u_long  ulResolution,
                        td_p_fn_vTimerEventCallback p_fn_vEventCallback,
                        u_long  ulTimerEventType,
                        u_long* p_ulEventId
                       )
{
    MMRESULT Id;
    Id = timeSetEvent(
                      ulPeriod,
                      ulResolution,
                      (LPTIMECALLBACK)fn_vTimerEventsCallback,
                      0,
                      ulTimerEventType
                     );
    if (Id == 0L)  return (C_wTimerError);
 
    *p_ulEventId = Id;
    return (0);
}
 
 
 
/*******************************************************************************/
/*  This function return vertical refresh rate. The result is multiple by 100. */
/*  Actualy return 0 if not Intel Pentium                                      */
/*******************************************************************************/
u_long fn_ulTimerVerticalRefreshRate(u_long ulCpuClock)
{
    LPDIRECTDRAW   lpDD = NULL;
    HRESULT        ddrval;
    u_long         ulFrequency;
    float          fTempFreq;
 
    ddrval = DirectDrawCreate(NULL, &lpDD, NULL);
    if(ddrval == DD_OK)
    {
        ddrval = IDirectDraw_GetMonitorFrequency(lpDD,&ulFrequency);
        IDirectDraw_Release(lpDD);
        if (ddrval == DD_OK)
        {   /* DDraw do not return in Hz*100 */
            if (ulFrequency < 1000)  ulFrequency *= 100;
            return (ulFrequency);
        }
    }
 
    ulFrequency = fn_ulTimerFrameLength();
    if (ulFrequency==0) return (0);
 
    /* Use a float to avoid overflow */
    fTempFreq = (float)ulFrequency;
    fTempFreq = (float)ulCpuClock * (float)100.0 / fTempFreq;
    ulFrequency = (u_long)fTempFreq;
 
    return (ulFrequency);
}
 
