diff --git a/ChangeLog b/ChangeLog index 3895e4c5..b11202d9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2010-01-08 Joerg Wunsch + + Submitted by Doug: + patch #7010: Win32 enhanced bitbang_delay + * bitbang.c (bitbang_calibrate_delay, bitbang_delay): On Win32, + use the high-resolution performance counter rather than the + uneducated delay loop guess if it is available on the target + hardware. + 2010-01-08 Joerg Wunsch Submitted by Gerard: diff --git a/bitbang.c b/bitbang.c index b52517eb..c7ba89b1 100644 --- a/bitbang.c +++ b/bitbang.c @@ -42,7 +42,9 @@ static int delay_decrement; -#if !defined(WIN32NATIVE) +#if defined(WIN32NATIVE) +static int has_perfcount; +#else static volatile int done; typedef void (*mysighandler_t)(int); @@ -53,22 +55,46 @@ static void alarmhandler(int signo) done = 1; signal(SIGALRM, saved_alarmhandler); } -#endif /* !WIN32NATIVE */ +#endif /* WIN32NATIVE */ /* * Calibrate the microsecond delay loop below. */ static void bitbang_calibrate_delay(void) { - /* - * Right now, we don't have any Win32 implementation for this, so we - * can only run on a preconfigured delay stepping there. The figure - * below should at least be correct within an order of magnitude, - * judging from the auto-calibration figures seen on various Unix - * systems on comparable hardware. - */ #if defined(WIN32NATIVE) - delay_decrement = 100; + static LARGE_INTEGER freq; + + /* + * If the hardware supports a high-resolution performance counter, + * we ultimately prefer that one, as it gives quite accurate delays + * on modern high-speed CPUs. + */ + if (QueryPerformanceFrequency(&freq)) + { + has_perfcount = 1; + if (verbose >= 2) + fprintf(stderr, + "%s: Using performance counter for bitbang delays\n", + progname); + } + else + { + /* + * If a high-resolution performance counter is not available, we + * don't have any Win32 implementation for setting up the + * per-microsecond delay count, so we can only run on a + * preconfigured delay stepping there. The figure below should at + * least be correct within an order of magnitude, judging from the + * auto-calibration figures seen on various Unix systems on + * comparable hardware. + */ + if (verbose >= 2) + fprintf(stderr, + "%s: Using guessed per-microsecond delay count for bitbang delays\n", + progname); + delay_decrement = 100; + } #else /* !WIN32NATIVE */ struct itimerval itv; volatile int i; @@ -116,10 +142,27 @@ static void bitbang_calibrate_delay(void) */ void bitbang_delay(int us) { +#if defined(WIN32NATIVE) + LARGE_INTEGER countNow, countEnd; + + if (has_perfcount) + { + QueryPerformanceCounter(&countNow); + countEnd.QuadPart = countNow.QuadPart + freq.QuadPart * us / 1000000ll; + + while (countNow.QuadPart < countEnd.QuadPart) + QueryPerformanceCounter(&countNow); + } + else /* no performance counters -- run normal uncalibrated delay */ + { +#endif /* WIN32NATIVE */ volatile int del = us * delay_decrement; while (del > 0) del--; +#if defined(WIN32NATIVE) + } +#endif /* WIN32NATIVE */ } /*