Page 1 of 1

Detecting CPU speed !

Posted: Wed Jul 04, 2007 10:11 am
by inflater
Hello (again),
I've found a code that works in Windows, that will detect the CPU speed in megahertz. So I converted it a little :)

Code: Select all

	
        dw    310Fh                        ;dw 310Fh = rdtsc
	mov   dword [TLow], eax
	mov   dword [THigh], edx
	mov   ax,9                         ;This will set up about a 
	call  SetTimer                     ;500 ms delay (that will halt the
	dw    310Fh                        ;execution until its completed)
	sub   eax, dword [TLow]
	sub   edx, dword [THigh]
	mov   dword [TLow], eax
	mov   dword [THigh], edx
	mov   eax,dword [TLow / 500000d]
	mov   dword[CPUSpeed],eax
(CPUSpeed, TLow, THigh are doubleword data types)

It's expected to work (this worked in Windows) and I've executed this at the protected-mode part of my OS, where the timer is reprogrammed. But my conversion was false, on the real machine it throwd something about 459XXX, wow, 459000 MHz CPU? :D

So, do you have any idea to fix this code [again]? You know, I would be *very* grateful :)

Regards,
inflater

Re: Detecting CPU speed !

Posted: Wed Jul 04, 2007 12:19 pm
by Brendan
Hi,

Ok - 2 mistakes.

The first one is your 64-bit subtraction:

Code: Select all

	sub   eax, dword [TLow]
	sub   edx, dword [THigh]
This doesn't work, and should be:

Code: Select all

	sub   eax, dword [TLow]
	sbb   edx, dword [THigh]
This is a little like doing subtraction on paper and forgetting to "carry the one".

The other problem is this:

Code: Select all

	mov   eax,dword [TLow / 500000d]
This divides the address of your TLow variable by 500000 at compile time, and then gets the value at that address - you could skip everything before this instruction and still get the same dodgy answer.

To do unsigned division (at run time) you should probably consider the DIV instruction....

For example:

Code: Select all

   rdtsc                                      ;Get a decent assembler!
   mov   dword [TLow], eax
   mov   dword [THigh], edx
   mov   ax,9                         ;This will set up about a
   call  SetTimer                     ;500 ms delay (that will halt the
   rdtsc                                      ;Get a decent assembler!
   sub   eax, dword [TLow]
   sbb   edx, dword [THigh]
   mov ebx,5000000
   div ebx                                ;divide the 64-bit value in EDX:EAX by the value in EBX
   mov   dword[CPUSpeed],eax
It's also probably a good idea to round it to the nearest:

Code: Select all

   rdtsc                                      ;Get a decent assembler!
   mov   dword [TLow], eax
   mov   dword [THigh], edx
   mov   ax,9                         ;This will set up about a
   call  SetTimer                     ;500 ms delay (that will halt the
   rdtsc                                      ;Get a decent assembler!
   sub   eax, dword [TLow]
   sbb   edx, dword [THigh]
   mov ebx,5000000
   div ebx                                ;divide the 64-bit value in EDX:EAX by the value in EBX
   shr ebx,1                             ;ebx = half the divisor
   sub edx,ebx                         ;set the carry flag if the remainder is greater than half the divisor
   adc eax,0                            ;Round up if carry is set
   mov   dword[CPUSpeed],eax
I'm thinking the answer you get will change each time you boot, and could be (roughly) 10% wrong. The reason for this is you don't know how long it will be before the first IRQ occurs, and your delay will be between 439 ms and 494 ms, or between 494 ms and 549 ms.

It'll also take (roughly) half a second to measure something that will typically happen around 1 billion times per second (a 5 ms delay would be more than enough).

It'd be possible to fix both of these problems by reading a PIT timer's count, rather than waiting for an IRQ...


Cheers,

Brendan

Posted: Fri Jul 06, 2007 11:23 am
by inflater
Fixed (now returns +/- 1 MHz to the actual speed of processor), thanks for your hints, Brendan :)

Regards,
inflater