delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/1999/10/21/12:30:53

Message-ID: <001701bf1be2$71889ac0$0201a8c0@center1.mivlgu.ru>
From: "Sergey Vlasov" <vsu AT au DOT ru>
To: "Eli Zaretskii" <eliz AT is DOT elta DOT co DOT il>
Cc: <djgpp-workers AT delorie DOT com>
Subject: new uclock.c (with Windows VTD.VXD support)
Date: Thu, 21 Oct 1999 19:31:00 +0300
MIME-Version: 1.0
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 4.72.3110.5
X-MimeOLE: Produced By Microsoft MimeOLE V4.72.3110.3
Reply-To: djgpp-workers AT delorie DOT com

Here is my uclock.c with both VTD.VXD support and original timer access.
It works both under DOS and under Windows 95 (tested under 4.00.950).

To get the VxD entry point, I use INT 2F AX=1684 DX=0005 (VTD device ID).
If ES:EDI is zero after this call, I use direct hardware access; otherwise
this entry point is used. Before the INT 2F call I set ES and EDI to zero
to provide the default value when this function is not supported (when
Windows is not running).

I have done some measurements to determine the effect this change has on
performance. I used the following test program, which prints the average
time taken by the uclock() function (in uclock units):

====== Cut: uctime.c ======
#include <stdio.h>
#include <time.h>

int
main (void)
{
        unsigned long long t1, t2, sum;
        int i;

        for ( i = 0; i < 10; i++ )
                uclock();
        sum = 0;
        for ( i = 0; i < 1000000; i++ ) {
                t1 = uclock();
                t2 = uclock();
                sum += t2 - t1;
        }
        printf ("%f\n", ((double)sum) / 1000000.0);
        return 0;
}
====== Cut: End of uctime.c ======

Compilation commands:

1) gcc -Wall -O2 -march=i386 -mcpu=pentium -o uctime_0.exe uctime.c uclock0.c
2) gcc -Wall -O2 -march=i386 -mcpu=pentium -o uctime.exe uctime.c uclock.c

1 -- new uclock() from the current version, 2 -- my modified uclock().

On the Pentium-90, I have got the following results:

Under DOS:
  - current version: 3.72;
  - my modified version: 3.79.

Under Windows 95 (4.00.950):
  - current version: 19.56;
  - my modified version: 8.30.

As you can see, the modified version is only about 2% slower under DOS, but
more than 2 times faster under Windows 95.

I have commented out the code which waits for the next timer tick (I think
it is no longer needed, because Windows 95 is now handled by the VTD code).

There is also a minor optimization in the code which accesses timer ports
directly: _farpeekl was replaced by _farsetsel and _farnspeekl.

====== Cut: uclock.c ======
/* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
#include <time.h>
#include <errno.h>
#include <pc.h>
#include <libc/farptrgs.h>
#include <go32.h>
#include <dpmi.h>
#include <libc/bss.h>

static int uclock_bss = -1;
static uclock_t base = 0;
static unsigned long last_tics = 0;


/* tics = about 18.2 * 65536 (1,192,755)

   actually, it's   0x1800b0 tics/day (FAQ)
                  / 24*60*60 sec/day
                  * 65536 utics/tic
                  = 1,193,180 utics/sec */

/* Direct hardware access */
static inline uclock_t
uclock_timer(void)
{
  unsigned char lsb, msb;
  unsigned long tics, otics;
  uclock_t rv;

  /* Make sure the numbers we get are consistent */
  _farsetsel(_dos_ds);
  do {
    otics = _farnspeekl(0x46c);
    outportb(0x43, 0x00);
    lsb = inportb(0x40);
    msb = inportb(0x40);
    tics = _farnspeekl(0x46c);
  } while (otics != tics);

  /* calculate absolute time */
  msb ^= 0xff;
  lsb ^= 0xff;
  {
    unsigned long utics = ((msb << 8) | lsb);
    rv = ((unsigned long long)tics << 16) | utics;
  }

  if (base == 0L)
    base = rv;

  if (last_tics > tics) /* midnight happened */
    base -= 0x1800b00000LL;

  last_tics = tics;

  /* return relative time */
  return rv - base;
}

/* VTD.{386,VXD} support */

static __dpmi_paddr vtd_entry;  /* VTD entry point address */
static int use_vtd = 0;         /* Set to nonzero value when using VTD */

/* Get VTD entry point address */
static inline void
get_vtd_entry(void)
{
  asm ( "pushl %%es\n\t"
        "movl %%di,%%es\n\t"
        "int $0x2F\n\t"
        "movl %%edi,%0\n\t"
        "movw %%es,%1\n\t"
        "popl %%es"
  : "=m" (vtd_entry.offset32), "=m" (vtd_entry.selector)
  : "a" (0x1684), "b" (0x0005), "D" (0)
  : "ax", "bx", "cx", "dx", "si", "di" );
}

/* Get current time from VTD (using function 0x0100) */
static inline uclock_t
uclock_vtd(void)
{
  register unsigned long long rv;
  asm ( "lcall %1" : "=A" (rv) : "m" (vtd_entry), "a" (0x0100) );
  if ( base == 0L )
    base = rv;
  return rv - base;
}

static void
uclock_init(void)
{
  get_vtd_entry();
  if ( vtd_entry.offset32 == 0 && vtd_entry.selector == 0 ) {
    use_vtd = 0;

    /* switch the timer to mode 2 (rate generator) */
    /* rather than mode 3 (square wave), */
    /* which doesn't count linearly. */

    outportb(0x43, 0x34);
    outportb(0x40, 0xff);
    outportb(0x40, 0xff);

    last_tics = 0;
    base = 0;
#if 0 /* No longer needed, Windows 9X handled by the VTD support */
    /* It seems like Windows 9X virtualization of the timer device
       delays the actual execution of the above command until the
       next timer tick.  Or maybe it only consults the actual device
       once per tick.  In any case, the values returned during the
       first 55 msec after the timer was reprogrammed still look as
       if the timer worked in mode 3.  So we simply wait for one clock
       tick when we run on Windows.  */
    {
      unsigned long otics;
      int e = errno;
      _farsetsel(_dos_ds);
      otics = _farnspeekl(0x46c);
      do {
        errno = 0;
        __dpmi_yield();   /* will set errno to ENOSYS on plain DOS */
      } while (errno == 0 && _farnspeekl(0x46c) == otics);
      errno = e;
    }
#endif
  } else {
    /* VTD entry point found */
    use_vtd = 1;
    base = 0;
  }
}

uclock_t
uclock(void)
{
  if (uclock_bss != __bss_count) {
    uclock_bss = __bss_count;
    uclock_init();
  }
  if ( !use_vtd )
    return uclock_timer();
  else
    return uclock_vtd();
}
====== Cut: end of uclock.c ======


--- Sergey Vlasov <vsu AT au DOT ru>



- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019