PCTIMER: Millisecond Resolution Timing With DJGPP V2 and DPMI

Published: 1998-03-30

Updated: 1999-10-15

License: Freeware

If You Use Windows SDK

If you use Windows SDK (with 32-bit compilers such as Visual C++ 1.52 and above), check this out:

Test Report: Millisecond Resolution Timing With Visual C++ and Windows 95/98

A Few Words On Win95/98

Although I have only tested PCTIMER with Win95/98 and CWSDPMI, PCTIMER should run on most DPMI servers.

However, theoretically, applications running under Win95/98 should not touch hardware interrupts. The "correct" method of doing millisecond resolution timing under Win95/98 is to call Windows API. The standard Multimedia Timer API can do millisecond resolution timing. (With Microsoft Visual C++ 4, you'll need to include <mmsystem.h> and link with a library called winmm.lib. As for DJGPP, as far as I know, RSXNT does not provide Multimedia API access.)

If you need an example on using Windows API to do millisecond resolution timing, please go to this page (I use Visual C++ 4.0):

Test Report: Millisecond Resolution Timing With Visual C++ and Windows 95/98

Basic Logic

PCTIMER reprograms the 8254 IC (Programmable Interrupt Timer) to get high frequency of System Timer Interrupt (IRQ0) whose default frequency is 18.2 Hz. Since the operating systems rely heavily on the System Timer Interrupt, increasing its frequency could cause instability. PCTIMER hooks both protected and real mode int 8h handlers (default handler for System Timer Interrupt), reprograms 8254 to get higher frequency of interrupts, but calls the original handlers at a fixed frequency of 18.2 Hz.

Relationship Between Protected and Real Mode Int 8h

According to DJGPP V2 FAQ, hardware interrupts are always passed to protected mode first, and only if unhandled are they reflected to real mode. In other words, we must at least hook protected mode int 8h. To avoid potential loss of ticks when the protected mode fails to handle the hardware interrupt, we should also hook real mode int 8h.

In actual implementation of the two handlers, things become much more complex than that.

PCTIMER Protected Mode Int 8h Handler

Here is PCTIMER's protected mode int 8h in pseudocode. The meanings of pm_termination_flag's values are:

TRUE_FAILURE: The handler failed to handle the hardware interrupt.

CHAIN_TERMINATION: The handler terminated with chaining to the old handler. Note that after chaining the old protected mode int 8h handler, the real mode int 8h will get called. We need to take care of this.

OUTPORTB_TERMINATION: The handler terminated with an outportb (0x20, 0x20) instruction. This instruction sends a hardware request to the Interrupt Controller to terminate the interrupt. This works (although intrusive), but will cause the DPMI server to believe that the protected mode handler failed to do its job. Therefore, the real mode handler will get called. We need to take care of this, too.

PCTIMER Protected Mode Int 8h Handler (Pseudocode)
  * pm_termination_flag = TRUE_FAILURE
  * counter++
  * if it is time to chain old handler
      - pm_termination_flag = CHAIN_TERMINATION
      - let the wrapper chains the old handler
  * else
      - pm_termination_flag = OUTPORTB_TERMINATION
      - outportb (0x20, 0x20)

(Read the real code for details.)

PCTIMER Real Mode Int 8h Handler

The real mode handler is considerably more complex than the protected mode one. It depends on pm_termination_flag to determine how it should behave. Always set pm_termination_flag to TRUE_FAILURE before we leave, so in case the protected mode handler should fail, the real mode handler can detect it next time.

PCTIMER Real Mode Int 8h Handler (Pseudocode)
  * if pm_termination_flag = TRUE_FAILURE
      - counter++
  * if it is time to chain the old handler, or if the protected
      mode handler decided to do that (i.e.,
      pm_termination_flag = CHAIN_TERMINATION)
      - call old real mode handler
      - pm_termination_flag = TRUE_FAILURE
  * else
      - outportb (0x20, 0x20)
      - pm_termination_flag = TRUE_FAILURE

(Read the real code for details.)

Example of Usage

#include <gccint8.h>

void main (void)
  unsigned long int start, finish, elapsed_time;

  /* initiate the timer with 1/1000 s resolution */
  /* you can use different resolution, but resolution */
  /* higher than 1000 is not recommended. */

  pctimer_init (1000);

  start = pctimer_get_ticks ();
  /* do some stuff here */
  finish = pctimer_get_ticks ();
  elapsed_time = pctimer_time (start, finish);
  /* always returns elapsed time in the unit of ms. */

  pctimer_sleep (200); /* sleep 200 ms */
  pctimer_sound (800, 200); /* 800 Hz sound for 200 ms */

  /* never forget to exit the timer!!! */
  /* otherwise, the system WILL crash!!! */

  pctimer_exit ();



Note. If you use Microsoft C or Turbo C and want to compile real mode MS-DOS programs, the source code in old versions of PCTIMER is still useable. However, if you use DJGPP, the source code in old versions of PCTIMER is obsolete. Get the latest version!
PCTIMER V1.2 (1995-01-29; DJGPP V1, Turbo C++, and Microsoft C++ source) PCTIMER V1.3 (1995-11-26; DJGPP V1, Turbo C++ source) PCTIMER V1.4 (1998-03-15; DJGPP V2 source)
Simtel N/A N/A pctime14.zip
Local pctime12.zip pctime13.zip pctime14.zip


I am not at all responsible for any damages, consequential or incidental, and by using PCTIMER, you are agreeing not to hold either of the above responsible for any problems whatsoever.