; Frequency Calculation ; Filename: FREQUENC.ASM ; Copyright (c) Intel Corporation 2001-2003 ; ; This program has been developed by Intel Corporation. Intel ; has various intellectual property rights which it may assert ; under certain circumstances, such as if another ; manufacturer's processor mis-identifies itself as being ; "GenuineIntel" when the CPUID instruction is executed. ; ; Intel specifically disclaims all warranties, express or ; implied, and all liability, including consequential and other ; indirect damages, for the use of this program, including ; liability for infringement of any proprietary rights, ; and including the warranties of merchantability and fitness ; for a particular purpose. Intel does not assume any ; responsibility for any errors which may appear in this program ; nor any responsibility to update it. ; ; This example assumes the system has booted DOS. ; This program runs in Real mode. ; ;************************************************************************ ; ; This program was assembled using MASM 6.14.8444 and tested on a ; system with a Pentium(r) II processor, a system with a ; Pentium(r) III processor, a system with a Pentium(r) 4 processor, ; B2 stepping, and a system with a Pentium(r) 4 processor, ; C1 stepping. ; ; This program performs the following 8 steps to determine the ; actual processor frequency. ; ; Step 1. Execute the CPUID instruction with an input value of EAX=0 ; and ensure the vendor-ID string returned is "GenuineIntel". ; Step 2. Execute the CPUID instruction with EAX=1 to load the EDX ; register with the feature flags. ; Step 3. Ensure that the TSC feature flag (EDX bit 4) is set. This ; indicates the processor supports the Time Stamp Counter ; and RDTSC instruction. ; Step 4. Read the TSC at the beginning of the reference period ; Step 5. Read the TSC at the end of the reference period. ; Step 6. Compute the TSC delta from the beginning and ending of the ; reference period. ; Step 7. Compute the actual frequency by dividing the TSC delta by ; the reference period. ; ;************************************************************************ .DOSSEG .MODEL small, pascal .STACK ;4096 wordToDec PROTO NEAR PASCAL decAddr:WORD, hexData:WORD ;---------------------------------------------------------------------- ; Macro printst ; This macro is used to print a string passed as an input ; parameter and a word value immediately after the string. ; The string is delared in the data segment routine during ; assembly time. The word is converted to dec ascii and ; printed after the string. ; ; Input: stringData = string to be printed. ; wordData = word to be converted to dec ascii and printed ; ; Destroys: None ; ; Output: None ; ; Assumes: Stack is available ; ;---------------------------------------------------------------------- printst MACRO stringdata, hexWord local stringlabel, decData .data stringlabel DB stringdata decData DB 5 dup (0) DB 0dh, 0ah, '$' .code pushf pusha ; Convert the word ino hex ascii and store in the string invoke wordToDec, offset decData, hexWord mov dx, offset stringlabel ; Setup string to be printed mov ah, 09h ; Execute DOS print function int 21h popa popf ENDM SEG_BIOS_DATA_AREA EQU 40h OFFSET_TICK_COUNT EQU 6ch INTERVAL_IN_TICKS EQU 91 ; Data segment .DATA ; Code segment .CODE .686p cpufreq PROC NEAR local tscLoDword:DWORD, / tscHiDword:DWORD, / mhz:WORD,/ Nearest66Mhz:WORD,/ Nearest50Mhz:WORD,/ delta66Mhz:WORD .startup ; Allow assembler to create code that ; initializes stack and data segment ; registers ; Step 1. ; Verify Genuine Intel processor by checking CPUID generated vendor ID mov eax, 0 cpuid cmp ebx, 'uneG' ; Check VendorID = GenuineIntel jne exit ; Jump if not Genuine Intel processor cmp edx, 'Ieni' jne exit cmp ecx, 'letn' jne exit ; Step 2 and 3 ; Get CPU feature flags ; Verify TSC is supported mov eax, 1 cpuid bt edx, 4t ; Flags Bit 4 is TSC support jnc exit ; jump if TSC not supported push SEG_BIOS_DATA_AREA pop es mov si, OFFSET_TICK_COUNT ; The BIOS tick count updateds mov ebx, DWORD PTR es:[si] ; ~ 18.2 times per second. wait_for_new_tick: cmp ebx, DWORD PTR es:[si] ; Wait for tick count change je wait_for_new_tick ; Step 4 ; **Timed interval starts** ; Read CPU time stamp rdtsc ; Read and save TSC immediately mov tscLoDword, eax ; after a tick mov tscHiDword, edx add ebx, INTERVAL_IN_TICKS + 1 ; Set time delay value ticks. wait_for_elapsed_ticks: cmp ebx, DWORD PTR es:[si] ; Have we hit the delay? jne wait_for_elapsed_ticks ; Step 5 ; **Time interval ends** ; Read CPU time stamp immediatly after tick delay reached. rdtsc ; Step 6 sub eax, tscLoDword ; Calculate TSC delta from sbb edx, tscHiDword ; beginning to end of interval ; Step 7 ; ; 54945 = (1 / 18.2) * 1,000,000 This adjusts for MHz. ; 54945*INTERVAL_IN_TICKS adjusts for number of ticks in interval ; mov ebx, 54945*INTERVAL_IN_TICKS div ebx ; ax contains measured speed in MHz mov mhz, ax ; Find nearest full/half multiple of 66/133 MHz xor dx, dx mov ax, mhz mov bx, 3t mul bx add ax, 100t mov bx, 200t div bx mul bx xor dx, dx mov bx, 3 div bx ; ax contains nearest full/half multiple of 66/100 MHz mov Nearest66Mhz, ax sub ax, mhz jge delta66 neg ax ; ax = abs(ax) delta66: ; ax contains delta between actual and nearest 66/133 multiple mov Delta66Mhz, ax ; Find nearest full/half multiple of 100 MHz xor dx, dx mov ax, mhz add ax, 25t mov bx, 50t div bx mul bx ; ax contains nearest full/half multiple of 100 MHz mov Nearest50Mhz, ax sub ax, mhz jge delta50 neg ax ; ax = abs(ax) delta50: ; ax contains delta between actual and nearest 50/100 MHz multiple mov bx, Nearest50Mhz cmp ax, Delta66Mhz jb useNearest50Mhz mov bx, Nearest66Mhz ; Correction for 666 MHz (should be reported as 667 MHZ) cmp bx, 666 jne correct666 inc bx correct666: useNearest50MHz: ; bx contains nearest full/half multiple of 66/100/133 MHz printst "Reported MHz = ~", bx printst "Measured MHz = ", mhz ; print decimal value exit: .exit ; returns control to DOS ret cpufreq ENDP ;---------------------------------------------------------------------- ; Procedure wordToDec ; This routine will convert a word value into a 5 byte decimal ; ascii string. ; ; Input: decAddr = address to 5 byte location for converted string ; (near address assumes DS as segment) ; hexData = word value to be converted to hex ascii ; ; Destroys: ax, bx, cx ; ; Output: 5 byte converted hex string ; ; Assumes: Stack is available ; ;---------------------------------------------------------------------- wordToDec PROC NEAR PUBLIC uses es, decAddr:WORD, hexData:WORD pusha mov di, decAddr push @data pop es ; ES:DI -> 5-byte converted string mov ax, hexData xor dx, dx mov bx, 10000t div bx add ax, 30h stosb mov ax, dx xor dx, dx mov bx, 1000t div bx add ax, 30h stosb mov ax, dx xor dx, dx mov bx, 100t div bx add ax, 30h stosb mov ax, dx xor dx, dx mov bx, 10t div bx add ax, 30h stosb mov ax, dx add ax, 30h stosb popa ret wordToDec ENDP END