Message-ID: <001701bf1be2$71889ac0$0201a8c0@center1.mivlgu.ru> From: "Sergey Vlasov" To: "Eli Zaretskii" Cc: Subject: new uclock.c (with Windows VTD.VXD support) Date: Thu, 21 Oct 1999 19:31:00 +0300 MIME-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit 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 #include 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 #include #include #include #include #include #include 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