see also HighFrequencyCounterInC
This is a quickie program I needed to detect a system's CPU speed from a DOS prompt. This code listing is ugly and needs a cleanup but wanted to preserve it before I lost it.
I used the djgpp compiler (from
http://www.delorie.com/djgpp). Along the way I learned a lot more about assembler, AT&T assembler differences and the
8245.
#include <stdio.h>
int warmup ()
{
int subtime;
__asm__(
"cpuid\n"
"rdtsc\n"
"movl %%eax, %0\n"
"cpuid\n"
"rdtsc\n"
"subl %0, %%eax\n"
"movl %%eax, %0\n"
:
: "g" (subtime)
);
__asm__(
"cpuid\n"
"rdtsc\n"
"movl %%eax, %0\n"
"cpuid\n"
"rdtsc\n"
"subl %0, %%eax\n"
"movl %%eax, %0\n"
:
: "g" (subtime)
);
__asm__(
"cpuid\n"
"rdtsc\n"
"movl %%eax, %0\n"
"cpuid\n"
"rdtsc\n"
"subl %0, %%eax\n"
"movl %%eax, %0\n"
:
: "g" (subtime)
);
return subtime;
}
void start_timer()
{
__asm__ __volatile__(
"mov $0x34, %al\n"
"out %al, $0x43\n"
"sub %al, %al\n"
"out %al, $0x40\n"
"out %al, $0x40\n"
);
}
unsigned int read_8254()
{
unsigned int clock = 0;
unsigned int high = 0;
unsigned int low = 0;
__asm__ __volatile__(
"pushf\n"
"cli\n"
"movb $0, %%al\n"
"out %%al, $0x43\n"
"in $0x40, %%al\n"
"movb %%al, %1\n"
"in $0x40, %%al\n"
"movb %%al, %0\n"
"sti\n"
"popf\n"
: "=g" (high), "=g" (low)
);
// printf("%02x - %02x :: ", high, low);
// outb(0x00, 0x43);
// low = inb(0x40);
// high = inb(0x40);
// clock = low;
clock = (high << 8) | low;
// printf("%x\n", clock);
return clock;
}
long long get_rdtsc(void)
{
unsigned int lower_word = 0;
unsigned int upper_word = 0;
unsigned long long total_word = 0;
__asm__ __volatile__(
"cpuid\n"
"rdtsc\n"
: "=a" (lower_word), "=d" (upper_word));
// printf("ticks now = %010x %010x\n", upper_word, lower_word);
total_word = ( (unsigned long long) upper_word) | lower_word;
// printf("ticks now = %010lx\n", total_word);
return lower_word;
}
int get_cpu_speed(void)
{
int warmup_time;
long t1, t2;
unsigned int clock1 = 0;
unsigned int clock2 = 0;
unsigned int cpu_speed;
int done = 0;
warmup_time = warmup();
// while ((inb(0x61) & 0x20) == 0);
// printf("warmup = %d\n", warmup_time);
start_timer();
do
{
t1 = get_rdtsc();
clock1 = read_8254();
} while (clock1 < 0xFE00);
do
{
t2 = get_rdtsc();
clock2 = read_8254();
} while (clock2 > 0x0E00);
t2 = get_rdtsc() - t1 - 2*warmup_time;
clock2 = clock1 - clock2;
// printf("RDTSC ticks == %lu -- ", t2);
// printf(" clock diff == %lu -- ", clock2);
cpu_speed = ( t2 * 1.193180) / clock2;
return cpu_speed;
}
int main(void)
{
unsigned int freq;
int done = 0;
int i, j;
printf("DOS CPU Frequency Detector\n");
// while(!done)
// {
// freq = get_cpu_speed();
printf("CPU speed == %u\n", freq);
// if (freq > 370 || freq < 358)
// {
// done = 1;
// }
}
/* Test routine - should spit out a . once a second
*/
/*
for (j = 0; j < 10; j++)
{
for (i = 0; i < 18; i++)
// wait for clock
{
while(read_8254() < 0xFe00);
while(read_8254() > 0x0e00);
}
printf(".");
}
*/
return freq;
}
--
MattWalsh - 11 Aug 2003