home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / snip9707.zip / UCLOCK.C < prev    next >
C/C++ Source or Header  |  1997-07-05  |  8KB  |  269 lines

  1. /* +++Date last modified: 05-Jul-1997 */
  2.  
  3. /*
  4. **  UCLOCK.C
  5. **
  6. **  Contains routines to perform microsecond accuracy timing
  7. **  operations.
  8. **
  9. **  Adapted from free sources originally by:
  10. **  Kris Heidenstrom (kheidens@actrix.gen.nz)
  11. **
  12. **  For additional information, ftp:
  13. **  oak.oakland.edu//SimTel/msdos/info/pctim*.zip.
  14. **
  15. **  Modified by Bob Stout (see UCLOCK_.ASM)
  16. */
  17.  
  18. #include <stddef.h>
  19. #include <assert.h>
  20. #include "uclock.h"
  21. #include "cast.h"
  22. #include "snpdosys.h"
  23.  
  24. /*
  25. **  usec_clock()
  26. **
  27. **  An analog of the standard clock() function, usec_clock() returns a
  28. **  uclock_t value (defined in UCLOCK.H) which represents the number
  29. **  of clock ticks past midnight. Analogous to CLK_TCK is UCLK_TCK, the
  30. **  number which a usec_clock() reading must be divided by to yield
  31. **  a number of seconds.
  32. **
  33. **  Parameters: 1 - Pointer to a Uclock_T structure to be filled in. If
  34. **                  NULL, an auto buffer is used.
  35. **
  36. **  Returns: If NULL is passed, returns uclock_t ticks since last midnight.
  37. **           If a Uclock_T pointer was passed, returns -1.0.
  38. **
  39. **  Side effects: Reprograms timer channel 0 to mode 2. This is a benign
  40. **                change with no known harmful effects on any normally
  41. **                PC-compatible hardware. Note that although most early
  42. **                PC's, XT's, and AT's typically run timer channel 0 in
  43. **                mode 3, later model AT compatibles including many 386,
  44. **                486, and Pentium systems already use mode 2.
  45. **
  46. **  Notes: Under Windows or OS/2 (HW_TIMER must be turned on), results are
  47. **         generally unreliable due to the fact that there can be a
  48. **         considerable and unpredictable lag between the time when the
  49. **         virtual machine rolls over the virtual CTC registers the time
  50. **         when the BIOS data area is updated. As a compromise, usec_clock()
  51. **         detects when running in a non-DOS environment and retries until
  52. **         it achieves monotonically increasing readings. This results in
  53. **         an effective resolution much coarser than the .8381 uSec.
  54. **         achieved under DOS. Although the result will still contain
  55. **         20-bit data, the actual resolution will be lower and dependent
  56. **         upon system speed, loading, and other overhead factors.
  57. */
  58.  
  59. uclock_t usec_clock(Uclock_T *now)
  60. {
  61.       static int init = 0;
  62.       Uclock_T buf, *ptr;
  63.  
  64.       if (NULL == now)
  65.             ptr = &buf;
  66.       else  ptr = now;
  67.  
  68.       if (!init)                          /* Only reprogram CTC once!   */
  69.       {
  70.             SetMode2_();
  71.             init = -1;
  72.       }
  73.  
  74.       Uclock_(ptr);
  75.  
  76.       /*
  77.       ** If Win or OS/2, ticks may lag CTC rollover so wait for
  78.       ** monotonically increasing readings.
  79.       */
  80.  
  81.       if (DOS != get_os())
  82.       {
  83.             static Uclock_T last;
  84.  
  85.             while (1)
  86.             {
  87.                   if ((last.ticks > ptr->ticks) ||
  88.                         ((last.ticks == ptr->ticks) &&
  89.                         (last.count > ptr->count)))
  90.                   {
  91.                         ++(ptr->ticks);
  92.                         last.ticks = ptr->ticks;
  93.                         last.count = ptr->count;
  94.                         Uclock_(ptr);
  95.                   }
  96.                   else
  97.                   {
  98.                         last.ticks = ptr->ticks;
  99.                         last.count = ptr->count;
  100.                         break;
  101.                   }
  102.             }
  103.       }
  104.  
  105.       return ((NULL == now) ? uclock_cnvrt(ptr) : -1.0);
  106. }
  107.  
  108. /*
  109. **  uclock_diff()
  110. **
  111. **  Subtracts one Uclock_T value from another.
  112. **
  113. **  Parameters: (All passed as pointers to Uclock_T structures)
  114. **              1 - First time (time to subtract)
  115. **              2 - Second time
  116. **              3 - Difference or NULL
  117. **
  118. **  Returns : Difference converted to a uclock_t if parameter 3 is NULL, else
  119. **            returns -1.0 and difference is returned in structure pointed to
  120. **            by parameter 3.
  121. **
  122. **  Note: uclock_diff() properly handles elapsed times spanning midnight,
  123. **        and is therefore preferable to simply subtracting uclock_t values.
  124. **        If you must subtract uclock_t values, convert negative results to
  125. **        positive by adding (UCLK_RLVR+1).
  126. */
  127.  
  128. uclock_t uclock_diff(Uclock_T *Start, Uclock_T *Stop, Uclock_T *Diff)
  129. {
  130.       Uclock_T buf, *ptr = ((NULL == Diff) ? &buf : Diff);
  131.  
  132.       /*
  133.       ** Add rollover constant if the day has changed
  134.       */
  135.  
  136.       if (Start->ticks <= Stop->ticks)
  137.             ptr->ticks = Stop->ticks - Start->ticks;
  138.       else  ptr->ticks = Stop->ticks + 0x001800B0L - Start->ticks;
  139.  
  140.       ptr->count = Stop->count - Start->count;
  141.       if (Stop->count < Start->count)
  142.             --(ptr->ticks);
  143.  
  144.       return ((NULL == Diff) ? uclock_cnvrt(ptr) : -1.0);
  145. }
  146.  
  147. /*
  148. **  uclock_cnvrt()
  149. **
  150. **  Converts from Uclock_T structure to a uclock_t value.
  151. **
  152. **  Parameters: 1 - Pointer to Uclock_T structure to convert.
  153. **
  154. **  Returns: uclock_t value.
  155. */
  156.  
  157. uclock_t uclock_cnvrt(Uclock_T *ticks)
  158. {
  159.       return (uclock_t)(ticks->count) + (uclock_t)(65536.0 * ticks->ticks);
  160. }
  161.  
  162. /*
  163. **  usec_delay()
  164. **
  165. **  Delays a specified number of microseconds.
  166. **
  167. **  Parameters: 1 - Delay time in microseconds.
  168. **
  169. **  Returns: Nothing
  170. **
  171. **  Note: Uses integer math in a tight loop to maximize resolution.
  172. */
  173.  
  174. void usec_delay(unsigned long usecs)
  175. {
  176.       Uclock_T tm0, tm1, diff;
  177.       unsigned long et;
  178.  
  179.       assert(usecs < 40904449L);          /* Avoid 40.9 sec. overflow */
  180.  
  181.       Uclock_(&tm0);
  182.       do
  183.       {
  184.             Uclock_(&tm1);
  185.             uclock_diff(&tm0, &tm1, &diff);
  186.  
  187.             /*
  188.             ** Convert ticks to uSecs. using integer math
  189.             */
  190.  
  191.             et = CAST(unsigned long, diff);
  192.  
  193.             et *= 88L;
  194.             et /= 105L;
  195.       } while (usecs > et);
  196. }
  197.  
  198. #ifdef TEST
  199.  
  200. /*
  201. **  Test main() adapted from Sample program #15 by K. Heidenstrom
  202. **  as cited above.
  203. */
  204.  
  205. #include <stdio.h>      /* Needed for printf()              */
  206. #include <stdlib.h>     /* Needed for exit()                */
  207. #include <conio.h>      /* Needed for kbhit() & getch()     */
  208.  
  209. main(void)
  210. {
  211.       Uclock_T ts, ts1, ts2;
  212.       unsigned int sched = 0;
  213.       unsigned int ch;
  214.  
  215.       puts("Press any key to get timestamp; "
  216.             "press <Esc> for continuous test\n\n");
  217.  
  218.       do
  219.       {
  220.             ch = getch();                             /* Get a keypress */
  221.             usec_clock(&ts);                          /* Get timestamp  */
  222.  
  223.             printf("Absolute timestamp: 0x%08lX%04X units of 0.8381 us\n",
  224.                   ts.ticks, ts.count);
  225.             printf("...representing %f seconds since last midnight\n\n",
  226.                   uclock_cnvrt(&ts) / UCLK_TCK);
  227.       } while ((ch & 0xff) != '\x1b');
  228.  
  229.       puts("\nProgram is now performing continuous timestamp test\n");
  230.       puts("Press <Esc> to exit\n");
  231.  
  232.       ts1.ticks = ts.ticks;
  233.       ts1.count = ts.count;
  234.  
  235.       while (1)
  236.       {
  237.             ts2.ticks = ts1.ticks;
  238.             ts2.count = ts1.count;
  239.             ts1.ticks = ts.ticks;
  240.             ts1.count = ts.count;
  241.             usec_clock(&ts);
  242.  
  243.             printf("0x%08lX%04X\r",
  244.                   ts.ticks, ts.count);
  245.  
  246.             if ((ts.ticks < ts1.ticks) || ((ts.ticks == ts1.ticks) &&
  247.                   (ts.count < ts1.count)))            /* Went backwards? */
  248.             {
  249.                   printf("Timestamp went backwards: 0x%08lX%04X, "
  250.                         "0x%08lX%04X, then 0x%08lX%04X\n",
  251.                         ts2.ticks, ts2.count,
  252.                         ts1.ticks, ts1.count,
  253.                         ts.ticks, ts.count);
  254.             }
  255.             ++sched;
  256.             if (!(sched & 0xff))
  257.             {
  258.                   if (kbhit())
  259.                   {
  260.                         if ('\x1b' == (getch() & 0xff))
  261.                               break;
  262.                   }
  263.             }
  264.       }
  265.       return EXIT_SUCCESS;
  266. }
  267.  
  268. #endif /* TEST */
  269.