Hi,
I am using the following code for calculating the operating frequency of my processor in my OS. I found it at :
http://www.mega-tokyo.com/osfaq2/index.php/How%20can%20I%20tell%20CPU%20speed%20%3F?
They say it expects the timer (PIT) to be programed to 100Hz. The variable [Ticks] is incremented every time the Timer Interrupt is called. Can someone tell me how I can program the timer. I tried googling but none of the results were very clear. Some said it ran at The timer is running at the default frequency as I have not tampered with it yet. I read somewhere the default frequency is 18.2 Hz or something. Now it gives me an outrageous value like 5927667295 MHz :dazzled: . Well I wish I had one like that. ;) But unfortunately I only have 850 MHz.
CPUSpeed:
push ebx
mov eax,1
cpuid
test edx,10h ; check whether RDTSC is supported
jnz @cpus_bad
mov ebx,[Ticks]
@cpus_waittimer: ; Wait for TIMER IRQ to fire
cmp ebx,[Ticks]
jz @cpus_waittimer
rdtsc ; returns ticks in edx:eax QWORD
mov [ticksLow],eax ; low DWORD in [ebp-4]
mov [ticksHigh],edx ; high DWORD in [ebp-8]
add ebx,2 ; Now ebx is [ticks]-1. So next tick is ebx+2
@cpus_waittimer2:
cmp ebx,[Ticks]
jnz @cpus_waittimer2
rdtsc
sub eax,[ticksLow]
sbb edx,[ticksHigh]
mov ebx,10000 ; divide to get speed in MHz
div ebx
and eax,0FFh
jmp @cpus_exit
@cpus_bad:
mov eax,-1
@cpus_exit:
pop ebx
ret
CPUSpeed:
push ebx
mov al,36h
out 43h,al
mov ax,100
out 40h,al
mov al,ah
out 40h,al
; mov dword[Ticks],0
mov eax,1
cpuid
test edx,10h ; check whether RDTSC is supported
jnz @cpus_bad
mov ebx,[Ticks]
@cpus_waittimer: ; Wait for TIMER IRQ to fire
cmp ebx,[Ticks]
jz @cpus_waittimer
rdtsc ; returns ticks in edx:eax QWORD
mov [ticksLow],eax ; low DWORD in [ebp-4]
mov [ticksHigh],edx ; high DWORD in [ebp-8]
add ebx,2 ; Now ebx is [ticks]-1. So next tick is ebx+2
@cpus_waittimer2:
cmp ebx,[Ticks]
jnz @cpus_waittimer2
rdtsc
sub eax,[ticksLow]
sbb edx,[ticksHigh]
mov ebx,10000 ; divide to get speed in MHz
div ebx
and eax,0FFh
jmp @cpus_exit
@cpus_bad:
mov eax,-1
@cpus_exit:
pop ebx
ret
ticksLow dd 0
ticksHigh dd 0
Plz help
Thomas
The 100Hz frequency would make the math a little easier, but I doubt that it would be enough easier to justify reprogramming the timer. I normally use the BIOS Event Wait function for this purpose. The BIOS Event Wait function will always fail for a DOS program running under Windows 2000/XP, it depends on the RTC so it is not supported on most pre-AT systems (~1983), and it will fail if some other process is using it.
.data
ts dq 0
.code
rdtsc
mov dword ptr ts,eax
mov dword ptr ts+4,edx
; Set up and call the BIOS RTC Delay function
; for a 1 second delay.
mov ah,86h
mov cx,0fh
mov dx,4240h
int 15h
jc RTC_Delay_function_failed
rdtsc
sub eax,dword ptr ts
sbb edx,dword ptr ts+4
mov ecx,1000000
div ecx
I did find some code that implements a 10-second delay by reprogramming system timer 2 (present on all systems, normally used for speaker sounds).
;--------------------------------------------------------------
; If the system timer clock is somewhere close to 1,193,182Hz,
; This program should delay 10 seconds between prompts.
;--------------------------------------------------------------
.model small
.386
.stack
.data
tobegin db "Press any key to begin",13,10,"$"
finished db "Finished, press any key to exit",13,10,"$"
.code
.startup
; Set the gate for timer 2 (bit 0 at I/O port 61h) to off.
; To avoid changing other bits in the register, read the
; current value, set bit 0, and write the altered value
; back.
mov dx,61h
in al,dx
and al,NOT 1
out dx,al
; Program timer 2 for LSB then MSB, mode 0, binary.
; The system timer control word register is at I/O
; port 43h. The system timer control word is set as
; follows:
; bit 7-6: 10 = timer 2
; bit 5-4: 11 = R/W LSB then MSB
; bit 3-1: 000 = single timeout
; bit 0: 0 = binary
mov dx,43h
mov al,0b0h
out dx,al
; The system timer normally has a 1,192,182Hz clock.
; For a full timer cycle you load an initial count
; of zero, which, because the count is decremented
; before it is checked for zero, causes the timer
; to count 65,536 clock cycles before it times out.
; Each cycle with take 65536/1192183 ~ 55ms, so
; 182 cycles should take ~10 seconds.
mov ah,9
mov dx,OFFSET tobegin
int 21h
mov ah,0
int 16h
mov cx,182
looper:
; Load the starting value, LSB then MSB.
mov dx,42h
mov al,0
out dx,al
out dx,al
; Set the gate for timer 2 (bit 0 at I/O port 61h) to on.
mov dx,61h
in al,dx
or al,1
out dx,al
; Wait until the output bit (bit 5 at I/O port 61h) is set.
@@:
in al,dx
and al,20h
jz @B
loop looper
mov ah,9
mov dx,OFFSET finished
int 21h
mov ah,0
int 16h
.exit
end
See Ralf Brown's Interrupt List for details.
Hi Micheal,
Thanx I will check it out. I can't use all that interrupts tho. But the working part doesn't use any interrupts right. Where is the final clock speed located in?
Thomas :U
Hi Thomas,
The first block of code leaves the clock speed in EAX (or at least that is what it is supposed to do :bg). For a programmable delay without interrupts, the best I can do in the limited amount of time I have right now is provide some QuickBASIC code. You could create an ASM version of this, programmed for a 1-second delay, and substitute it for the BIOS function call in the first block of code. For better accuracy you could increase the delay period and the divider for the cycle count (both in the same ratio) and/or move the timer setup to before the first RDTSC.
SUB Delay (milliseconds%) STATIC
' Delays for <milliseconds%> milliseconds before returning.
'
' This procedure uses system timer 2 to produce a short programmable
' delay. This method was chosen because it produces better accuracy
' than would be possible with the TIMER function and because it does
' not depend on a hardware interrupt (so it will continue to function
' with the system timer tick interrupt disabled).
'
' IMPORTANT:
' Because system timer 2 is normally used to produce speaker
' sounds, this procedure will conflict with the QB SOUND and
' PLAY statements.
' Exit now if <milliseconds%> is 0. The timer decrements the count
' value before testing it for 0 so a starting value of 0 would cause
' the timer to cycle through the full range.
IF milliseconds = 0 THEN EXIT SUB
' Set the gate for timer 2 (bit 0 at I/O port 61h) to off. To avoid
' changing other bits in the register, this statement reads the
' current value, sets bit 0, and writes the altered value back.
OUT &H61, INP(&H61) AND NOT 1
' Program timer 2 for LSB then MSB, mode 0, binary. The system timer
' control word register is at I/O port 43h. The system timer control
' word is set as follows:
' bit 7-6: 10 = timer 2
' bit 5-4: 11 = R/W LSB then MSB
' bit 3-1: 000 = single timeout
' bit 0: 0 = binary
OUT &H43, &HB0
' Calculate the number of full range cycles and the count for the
' last cycle. Each full range cycle has a duration of ~54.9 ms.
totalCount& = milliseconds * 1193&
cycleCount = totalCount& \ 65536
lastCount& = totalCount& MOD 65536
' Do the full range cycles.
FOR i = 1 TO cycleCount
' Load the starting value, LSB then MSB.
OUT &H42, 0
OUT &H42, 0
' Set the gate for timer 2 (bit 0 at I/O port 61h) to on.
OUT &H61, INP(&H61) OR 1
' Wait until the output bit (bit 5 at I/O port 61h) is set.
WAIT &H61, &H20
NEXT
' Do the last cycle.
IF lastCount& THEN
' Set the gate for timer 2 (bit 0 at I/O port 61h) to off.
OUT &H61, INP(&H61) AND NOT 1
' Load the starting value, LSB then MSB.
OUT &H42, LowByte(LowWord(lastCount&))
OUT &H42, HighByte(LowWord(lastCount&))
' Set the gate for timer 2 to on.
OUT &H61, INP(&H61) OR 1
' Wait until the output bit (bit 5 at I/O port 61h) is set.
WAIT &H61, &H20
END IF
' Set the gate for timer 2 (bit 0 at I/O port 61h) to off.
OUT &H61, INP(&H61) AND NOT 1
END SUB
I had more time than I thought. This version appears to work OK from Windows 98 MS-DOS mode, but it returns zero under Windows 2000 (expected) and GPF's under Windows 98 SE (?). And it returns the clock speed as a truncated integer.
[attachment deleted by admin]
Hi,
I tried the one after converting it to NASM. In Bochs, it displays the speed as 0 MHz :dazzled: while on my real machine it is displayed as 151 MHz but is actually 851 MHz. Your prog gives the correct value in windows. I gues it is some problem with the constants. I can't understand how you found the LASTCOUNT value. If it is according to the formula
totalCount& = milliseconds * 1193&
cycleCount = totalCount& \ 65536
lastCount& = totalCount& MOD 65536
then it is not the value you have given. I have used these formulas with a smaller delay (125 ms ) and also with 2 seconds. No result. I will try your value and see the results
Thomas :U
I calculated the constants as:
INT( 1193182 * 2 / 65536 ) = 36
1193182 * 2 - 65536 * 36 = 27068
I'll try the code on another system.
I have now tested on both of the systems I have here, and for both the program returns the same value that the DirectX Diagnostic tool returns. I tested on my Windows 98 SE system by switching to MS-DOS mode, and on my Windows 2000 system by booting from a Windows 98 SE startup disk.
On Windows XP, it returns 0
On Windows ME, it crashes.
Paul