[comp.sys.ibm.pc.misc] Fast Timer Interrupts, anyone?

scole@janus.Berkeley.EDU (Steven Cole) (10/13/90)

I'm wondering if anyone out there can help me...

I'm looking for a code fragment that sets up timer interrupts
approximately every 125 usec on a vanilla PC.  From what I understand,
this is possible by resetting the timer chip that causes INT 08, but
unfortunately I don't know just how to do this.  (I.e., is this
register memory mapped or does it take some obscure I/O operation?)

Thanks for any pointers,

Steve Cole,   scole@janus.berkeley.edu

np4@prism.gatech.EDU (POMPONIO,NICHOLAS A) (10/13/90)

In article <39119@ucbvax.BERKELEY.EDU> scole@janus.Berkeley.EDU (Steven Cole) writes:
>I'm wondering if anyone out there can help me...
>
>I'm looking for a code fragment that sets up timer interrupts
>approximately every 125 usec on a vanilla PC.  From what I understand,
>this is possible by resetting the timer chip that causes INT 08, but
>unfortunately I don't know just how to do this.  (I.e., is this
>register memory mapped or does it take some obscure I/O operation?)
>
>Thanks for any pointers,
>

I used the following snippit of code to speed the clock interrupts. (I hope 
that you are using a very fast PC, because an XT that I used previously
could take up to 125 usec just to respond to the interrupt!) The code is
in Microsoft C 5.1. I gathered bits and pieces from some working code, and
hopefully the details you need are here. If not, let me know by e-mail.

--------------------------------------------------------------------------

static void (interrupt * old_intr8)();
static void interrupt system_timer ();

/*  Timer (8253) values */

#define  TIMER_CONTROL_PORT  0x43
#define  TIMER_0_PORT        0x40
#define  TIMER_LOAD0_CMD     0x36
#define  DIVIDE_BY           2  /* Speed up clock int's by this factor */
#define  TIMER_0_COUNT       0x10000 / DIVIDE_BY
#define  TIMER_0_DEFAULT     0u

static void  install_interrupts ()
{
/*
     This routine installs the interrupt handler routine below and reprograms
     the timer channel 0
*/ 
   auto union {
      unsigned int   word;
      unsigned char  bytes [2];
   } split;

   old_intr8 = _dos_getvect (0x08);
   split.word = TIMER_0_COUNT;

   _disable ();

   outp (TIMER_CONTROL_PORT, TIMER_LOAD0_CMD);
   outp (TIMER_0_PORT, split.bytes [0]);
   outp (TIMER_0_PORT, split.bytes [1]);
   _dos_setvect (0x08, system_timer);

   _enable ();
}

#pragma check_stack(off)
void interrupt system_timer ()
{
/*
   This routine is the interrupt handler. The check_stack pragmas are needed
   only if you plan to call another function from this handler.
*/
   static int  divide_by = DIVIDE_BY;

   if (--divide_by == 0)   {
      divide_by = DIVIDE_BY;
      (* old_intr8)();
   }
   else   {
      outp (0x20, 0x20);   /* non-specific end-of-interrupt reset */
      _enable ();
   }
/*  Do something here */
}
#pragma check_stack(on)
-- 
POMPONIO,NICHOLAS A
Georgia Institute of Technology, Atlanta Georgia, 30332
uucp:	  ...!{decvax,hplabs,ncar,purdue,rutgers}!gatech!prism!np4
Internet: np4@prism.gatech.edu

dixon@sagittarius.crd.ge.com (walt dixon) (10/13/90)

In <39119@ucbvax.BERKELEY.EDU> Steven Cole writes:

>I'm looking for a code fragment that sets up timer interrupts
>approximately every 125 usec on a vanilla PC.  From what I understand,
>this is possible by resetting the timer chip that causes INT 08, but
>unfortunately I don't know just how to do this.  (I.e., is this
>register memory mapped or does it take some obscure I/O operation?)

It is definitely possible to change the rate of the system clock.  There are
side effects of just increasing the clock frequency such as the tod clock
gains time and some bios operations time out prematurely.  The "proper"
approach is to install a new int 8 ISR which calls the previous ISR
at the appropriate time.  The clock chip on the PC (8253) is pretty easy
to use,  and there are a number of books which describe its operation.
Before I would attempt such a speed up,  I would do some kind of timing
analysis to make sure there were enough CPU cycles available.

Check out Chapter 11 of "The MS-DOS Papers" (Howard Sams, 1988).  In that
chapter I present a device driver which speeds up the clock by a factor
of 16 and go through the timing analysis.  The book contains complete
source code for the driver.  BTW I get no revenues from book sales;  I'm
just citing a good reference.

Walt Dixon		{internet:	dixon@crd.ge.com	}
			{us mail:	ge-crd			}
			{		po box 8		}
			{		schenectady, ny 12301	}
			{phone:		518-387-5798 (W)	}
			{		518-875-6203 (H)	}
Walt Dixon dixon@crd.ge.com

KRW1@Lehigh (10/15/90)

It's fairly easy to speed up the timer.  Dealing with the consequences
is another matter.  You work with the system timer by issuing the proper
command to the control register at i/o address 43h, and reading/writing
data at 40h.  The basic timer tick is .838095 usec.  The largest number
of ticks in the countdown cycle is ffffh, which is about 55 msec - the
basic timer interrupt interval.  To change to something else, first
write 34h to port 43h to indicate that you will be sending first the
low and then high order bytes of the countdown cycle.  Write those to
port 40h in succession.  That's it.  Then you can try it again after
the system hangs.

It's generally not considered a safe practice to change the timer rate -
there are many potential eyes watching and depending on it.  If you've
taken care of those possibilities, however, the other thing to consider
is that a vanilla PC probably can't interrupt every 125 usec.  500 usec
would be a better figure to shoot for if the interrupt routine is
expected to have time to do anything.  Fast 286's and 386's can handle
it without any problem, but not 808x and slow 286's.  If you just need
high timing resolution, consider an alternate approach which involves a
non-interrupt driven process that reads the timer directly.  There are
various hi-res timer routines floating around on the net.  -- Kevin

------------------------------------------------------------------
Kevin Weiner   Lehigh University Computing Center   (215) 758-3991