Detecting CPU speed !

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
inflater
Member
Member
Posts: 1309
Joined: Thu Sep 28, 2006 10:32 am
Location: Slovakia
Contact:

Detecting CPU speed !

Post 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
My web site: http://inflater.wz.cz (Slovak)
Derrick operating system: http://derrick.xf.cz (Slovak and English :P)
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Detecting CPU speed !

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
inflater
Member
Member
Posts: 1309
Joined: Thu Sep 28, 2006 10:32 am
Location: Slovakia
Contact:

Post by inflater »

Fixed (now returns +/- 1 MHz to the actual speed of processor), thanks for your hints, Brendan :)

Regards,
inflater
My web site: http://inflater.wz.cz (Slovak)
Derrick operating system: http://derrick.xf.cz (Slovak and English :P)
Post Reply