/**************************************************************************** Description: CPA Timer.lib. Window specific part. Author: F. Jentey. Last Update : 03/03/97 ***************************************************************************/ #include "TimerPrv.h" #include "Detect.h" #include 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); }