Accurate timing in Win32

An extremely important part of a good emulator is maintaining proper timing across a variety of hardware platforms.  When your game requires 60 frames per second, you need to make sure it runs at 60 fps on a 486-66 as well as a K-6 350.  There are basically three kinds of timing methods in Win32: 1) The 'standard' Windows timer functions, 2) The 'multimedia' timer functions, 3) The 'high performance' timer functions.

1) The 'standard' Windows timer functions - This includes the two functions SetTimer() and KillTimer().  Windows allows you to define up to 16 separate interval timers which can either call a procedure or post a message to a window.   One problem with these timers is that they are based on the Windows tick counter which runs at a frequency of 18.2Hz (55ms period).  If your game requires 60 frames per second (16.667ms period), you cannot get accurate timing from these functions.   The other problem is that the WM_TIMER message is a low priority message and like the WM_PAINT message is really just a bit flag.  When multiple timer messages get stacked up because your code is busy doing something, the extra messages are thrown away.   These functions are really useless for real-time or accuracy and only serve for doing 'gross' kinds of time events which need an accuracy in terms or seconds or minutes.   I have included a description of this type of timer for completeness, but it is not something you would use for video frame timing in an emulator.

2) The 'multimedia' timer functions - These functions reside in WINMM.DLL and are considered part of the multimedia extensions to standard Windows.   The functions that we are interested in are timeBeginPeriod(), timeGetTime(), and timeEndPeriod().  These functions are supported on most hardware and allow for an accuracy down to 1ms.  There are other time functions included in this library, but I'm only going to cover these.  Accuracy of 1ms is almost good enough for accurate emulation.  If you don't mind your game running at 58.8fps or 62.5 fps because 1000 is not evenly divisible by 60fps, so either you use 16 or 17ms per frame.

3) The 'high performance' timer functions - These functions reside in KERNEL32.DLL and allow you access to the countdown timer used to trigger the 'low performance' Windows tick counter.  In the standard PC-compatible hardware architecture, there is a 1.19Mhz crystal oscillator connected to an Intel timer chip.  The timer chip has a 16-bit countdown timer which is loaded with 0 at the start of each cycle.   1.19Mhz divided by a countdown value of 65536 gives 18.2 Hz or 55ms.  This is the basis for the PC's tick counter and the timing of the WM_TIMER messages.  The 'high performance' functions allow you to directly read the countdown timer value.   The value returned is a 64bit integer with the upper 48-bits being the system tick counter and the lower 16 bits being the 1.19Mhz countdown timer value.  Microsoft says that this timer is not supposed to exist on all versions of Windows or on all PC's, but every PC I have tried has these functions and they work on Win95, Win98, and Windows NT.

How does it fit into the emulator?
I chose to use method #3 and in case Microsoft was telling the truth, I fall back to using method #2 for machines which don't support method #3.  The way that I use timers may not be ideal, but it does provide accurate timing with minimal wasted cycles.  I basically poll (read in a tight loop) the timer functions until I arrive at the correct time interval for each video frame.  This, at first, seems like a very poorly behaved Windows program since you are supposed to be event driven in Windows and not use polling techniques.  The way I correct for this is to use the Sleep() function.  If I arrive at the end of my frame handling code and there are more than 2ms to spare, I let the sleep function take up the slack which allows other threads to execute.  I don't trust the Sleep() function for intervals of less than 3ms.  The reason I don't trust the Sleep() function for less than 3ms is because thread priority/timing on Win95/98 is not perfect, so by giving up your time slice with Sleep(), you may not get control again for a longer time than you expected.  I don't have hard evidence to support my belief, but I feel it's better to be safe than sorry. Below is pseudocode showing my timing technique in action:

while (!bUserAbort)
   {
   CheckKeysAndJoysticks();
   EmulateCPU(oneframetime);
   ProduceSound(oneframetime);
   if (ScreenNeedsPainting)
      UpdateScreen();
   WasteLeftoverTime();
   }

/* The WasteLeftoverTime() function would look like this: */

void WasteLeftoverTime()
{
   if (timeleft > 2ms)
      Sleep(timeleft - 1ms);
   while (timeleft)
      CheckTimer();
}

Judging by the CPU utilization shown on the NT task manager, my technique uses less CPU time than other Windows game emulators out there.  Obviously there is more that you can do with timers than I've written here, but this is enough information to get your program timing its frames accurately.

Back