CPU Speed mit Inline Assembler auslesen?
-
Hi,
ich code einen C++ Kernel der auch soweit läuft, nun will ich beim Booten (wenn man das so nennen darf *g*) den CPU Speed ausgeben.
Weis einer wie das geht? Achja, arbeitsspeicher wäre auch sehr hilfreich wenn mir einer sagen könnte wie das geht
Schon mal ein dickes Danke im voraus
-
haste echtzeituhr oder sonsigen definierten timer und rdtsc, dann gehts schon sehr gut.
-
Wegen Speicher: Guck dir mal den Int 11h und den Int 15h Fnkt. 88h an. Ersteres liefert den Speicher unter 1 MB, zweiteres den über 1 MB. Wobei letzteres scheinbar nur maximal 64 MB ermitteln kann, da es die Speichergröße in KB in AX angibt...
-
Diesen Code habe ich mal vor einiger Zeit im Netz gefunden. Man kann den CPU-Speed und CPU-Type ermitteln. Der Code ist für Visual C++ 6, sollte sich aber auch an andere Compiler anpassen lassen. Soweit vorhanden ist die Quelle im Header, dieser Code stammt nicht aus meiner Feder. Da das hier grossenteils Assembler ist und die Frage hierzu passte bitte ich um Entschuldigung für dieses Monster hier - ausgerechnet im Assembler-Forum. Die Assemblerteile sollten sich ja auch wieder verwenden lassen.
Beispiel wie das verwendet wird kommt weiter unten.
// CpuId.h: interface for the CCpuId class. // // Copyright (c) 1998 by Bill Oatman. // All rights reserved. // http://www.netacc.net/~waterbry/cpuid/cpuid.htm // // Basic CPU and speed detection routines // Copyright (c) 1995, Intel Corporation. All rights reserved. ////////////////////////////////////////////////////////////////////// #ifndef _CPUID_H #define _CPUID_H //nicht MFC: Anstelle der CString einfach string der STL oder char[] verwenden. class CCpuId { public: static void GetCpuSpeed(CString *CpuSpeed); static void GetCpuId(CString *CpuIdStr); CCpuId(); virtual ~CCpuId(); }; #endif //_CPUID_H
Die Implementierung:
// CpuId.cpp: implementation of the CCpuId class. // // Copyright (c) 1998 by Bill Oatman. // All rights reserved. // http://www.netacc.net/~waterbry/cpuid/cpuid.htm // // Basic CPU and speed detection routines // Copyright (c) 1995, Intel Corporation. All rights reserved. ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "CpuIdTest.h" #include "CpuId.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif typedef unsigned short ushort; typedef unsigned long ulong; #define CPU_ID __asm _emit 0x0f __asm _emit 0xa2 // CPUID instruction #define RDTSC _asm _emit 0x0f _asm _emit 0x31 // RDTSC instruction // CONSTANT DEFINITIONS //////////////////////////////////////// #define CLONE_MASK 0x8000 // Mask to be 'OR'ed with proc- #define MAXCLOCKS 150 // Maximum number of cycles per // BSF instruction // ACCURACY AFFECTING CONSTANTS //////////////////////////// #define ITERATIONS 4000 // Number of times to repeat BSF // instruction in samplings. // Initially set to 4000. #define MAX_TRIES 20 // Maximum number of samplings // to allow before giving up // and returning current // average. Initially set to // 20. #define TOLERANCE 1 // Number of MHz to allow // samplings to deviate from // average of samplings. // Initially set to 2. #define SAMPLINGS 10 // Number of BSF sequence // samplings to make. // Initially set to 10. #define ROUND_THRESHOLD 6 struct FREQ_INFO { unsigned long in_cycles; // Internal clock cycles during // test unsigned long ex_ticks; // Microseconds elapsed during // test unsigned long raw_freq; // Raw frequency of CPU in MHz unsigned long norm_freq; // Normalized frequency of CPU // in MHz. }; // Number of cycles needed to execute a single BSF instruction. // Note that processors below i386(tm) are not supported. static ulong processor_cycles[] = { 00, 00, 00, 115, 47, 43, 38, 38, 38, 38, 38, 38, }; ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CCpuId::CCpuId() { } CCpuId::~CCpuId() { } /*************************************************************** * check_clone() * * Inputs: none * * Returns: * 1 if processor is clone (limited detection ability) * 0 otherwise ***************************************************************/ static bool check_clone() { short cpu_type=0; _asm { MOV AX,5555h // Check to make sure this XOR DX,DX // is a 32-bit processor MOV CX,2h DIV CX // Perform Division CLC JNZ no_clone JMP clone no_clone: STC clone: PUSHF POP AX // Get the flags AND AL,1 XOR AL,1 // AL=0 is probably Intel, // AL=1 is a Clone MOV cpu_type, ax } cpu_type = cpu_type & 0x0001; if(cpu_type) return TRUE; else return FALSE; } /*************************************************************** * check_IDProc() * * Inputs: none * * Returns: * CPU Family (i.e. 4 if Intel 486, 5 if Pentium(R) Processor) * ***************************************************************/ static WORD check_IDProc() { int i=0; WORD cpu_type=0xffff; BYTE stepping=0; BYTE model=0; BYTE vendor_id[]="------------"; BYTE intel_id[]="GenuineIntel"; __asm { xor eax, eax CPU_ID mov dword ptr vendor_id, ebx mov dword ptr vendor_id[+4], edx mov dword ptr vendor_id[+8], ecx } __asm { cmp eax, 1 // Make sure 1 is valid input // for CPUID jl end_IDProc // If not, jump to end xor eax, eax inc eax CPU_ID // Get family/model/stepping/ // features mov stepping, al and stepping, 0x0f //0fh and al, 0f0h shr al, 4 mov model, al and eax, 0f00h shr eax, 8 // Isolate family and eax, 0fh mov cpu_type, ax // Set _cpu_type with family end_IDProc: mov ax, cpu_type } return cpu_type; } // Check_IDProc() int wincpuidsupport() { int cpuid_support = 1; _asm { pushfd // Get original EFLAGS pop eax mov ecx, eax xor eax, 200000h // Flip ID bit in EFLAGS push eax // Save new EFLAGS value on // stack popfd // Replace current EFLAGS value pushfd // Get new EFLAGS pop eax // Store new EFLAGS in EAX xor eax, ecx // Can not toggle ID bit, jnz support // Processor=80486 mov cpuid_support,0 // Clear support flag support: } return cpuid_support; } /*************************************************************** * wincpuid() * * Inputs: none * * Returns: * 0 = Unknown * 2 = 80286 * 3 = 80386 * 4 = 80486 * 5 = Pentium(R) Processor * 6 = PentiumPro(R) Processor * 7 or higher = Processor beyond the PentiumPro6(R) Processor * ***************************************************************/ int wincpuid() { int cpuid; bool clone_flag = FALSE; if ( wincpuidsupport() ) // Determine whether CPUID // opcode is supported cpuid=check_IDProc(); else { clone_flag=check_clone(); cpuid=0; } if (clone_flag) cpuid = cpuid | CLONE_MASK; // Signify that a clone has been // detected by setting MSB high return cpuid; } void GetCpuString(int type, CString *str) { switch (type) { case 3: *str = "386"; break; case 4: *str = "486"; break; case 5: *str = "Pentium(R) Processor"; break; case 6: *str = "PentiumPro(R) Processor"; break; default: str->Format("Unknown Type (%d)", type); break; } } void CCpuId::GetCpuId(CString * CpuIdStr) { int cpu_type; int cpuid_support; CString tStr; cpu_type = wincpuid(); cpuid_support = wincpuidsupport(); GetCpuString(cpu_type, &tStr); if ( cpu_type & CLONE_MASK ) CpuIdStr->Format("Intel Clone CPU Family : %s", (LPCTSTR)tStr); else if ( cpuid_support ) CpuIdStr->Format("Intel CPU Family : %s", (LPCTSTR)tStr); else CpuIdStr->Format("CPU Family : %s", (LPCTSTR)tStr); } /*************************************************************** * wincpufeatures() * * Inputs: none * * Returns: * 0 = Processor which does not execute the CPUID instruction. * This includes 8086, 8088, 80286, 80386, and some * older 80486 processors. * * Else * Feature Flags (refer to App Note AP-485 for description). * This DWORD was put into EDX by the CPUID instruction. * * Current flag assignment is as follows: * * bit31..10 reserved (=0) * bit9=1 CPU contains a local APIC (iPentium-3V) * bit8=1 CMPXCHG8B instruction supported * bit7=1 machine check exception supported * bit6=0 reserved (36bit-addressing & 2MB-paging) * bit5=1 iPentium-style MSRs supported * bit4=1 time stamp counter TSC supported * bit3=1 page size extensions supported * bit2=1 I/O breakpoints supported * bit1=1 enhanced virtual 8086 mode supported * bit0=1 CPU contains a floating-point unit (FPU) * * Note: New bits will be assigned on future processors... see * processor data books for updated information * ***************************************************************/ DWORD wincpufeatures() { int i=0; DWORD cpuff=0x00000000; BYTE vendor_id[]="------------"; BYTE intel_id[]="GenuineIntel"; if ( wincpuidsupport() ) { _asm { xor eax, eax // Set up for CPUID instruction CPU_ID // Get and save vendor ID mov dword ptr vendor_id, ebx mov dword ptr vendor_id[+4], edx mov dword ptr vendor_id[+8], ecx } _asm { cmp eax, 1 // Make sure 1 is valid input // for CPUID jl end_cpuff // If not, jump to end xor eax, eax inc eax CPU_ID // Get family/model/stepping/ // features mov cpuff, edx end_cpuff: mov eax, cpuff } } return cpuff; } static void GetRDTSCCpuSpeed(struct FREQ_INFO *cpu_speed) { LARGE_INTEGER t0,t1; // Variables for High- // Resolution Performance // Counter reads ulong freq =0; // Most current frequ. calculation ulong freq2 =0; // 2nd most current frequ. calc. ulong freq3 =0; // 3rd most current frequ. calc. ulong total; // Sum of previous three frequency // calculations int tries=0; // Number of times a calculation has // been made on this call to // cpuspeed ulong total_cycles=0, cycles; // Clock cycles elapsed // during test ulong stamp0, stamp1; // Time Stamp Variable // for beginning and end // of test ulong total_ticks=0, ticks; // Microseconds elapsed // during test LARGE_INTEGER count_freq; // High Resolution // Performance Counter // frequency #ifdef WIN32 int iPriority; HANDLE hThread = GetCurrentThread(); #endif // WIN32; memset(cpu_speed, 0x00, sizeof(cpu_speed)); if ( !QueryPerformanceFrequency ( &count_freq ) ) return; // On processors supporting the Read // Time Stamp opcode, compare elapsed // time on the High-Resolution Counter // with elapsed cycles on the Time // Stamp Register. do { // This do loop runs up to 20 times or // until the average of the previous // three calculated frequencies is // within 1 MHz of each of the // individual calculated frequencies. // This resampling increases the // accuracy of the results since // outside factors could affect this // calculation tries++; // Increment number of times sampled // on this call to cpuspeed freq3 = freq2; // Shift frequencies back to make freq2 = freq; // room for new frequency // measurement QueryPerformanceCounter(&t0); // Get high-resolution performance // counter time t1.LowPart = t0.LowPart; // Set Initial time t1.HighPart = t0.HighPart; #ifdef WIN32 iPriority = GetThreadPriority(hThread); if ( iPriority != THREAD_PRIORITY_ERROR_RETURN ) { SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL); } #endif // WIN32 while ( (ulong)t1.LowPart - (ulong)t0.LowPart<50) { // Loop until 50 ticks have // passed since last read of hi- // res counter. This accounts for // overhead later. QueryPerformanceCounter(&t1); RDTSC; // Read Time Stamp _asm { MOV stamp0, EAX } } t0.LowPart = t1.LowPart; // Reset Initial t0.HighPart = t1.HighPart; // Time while ((ulong)t1.LowPart-(ulong)t0.LowPart<1000 ) { // Loop until 1000 ticks have // passed since last read of hi- // res counter. This allows for // elapsed time for sampling. QueryPerformanceCounter(&t1); RDTSC; // Read Time Stamp __asm { MOV stamp1, EAX } } #ifdef WIN32 // Reset priority if ( iPriority != THREAD_PRIORITY_ERROR_RETURN ) { SetThreadPriority(hThread, iPriority); } #endif // WIN32 cycles = stamp1 - stamp0; // Number of internal // clock cycles is // difference between // two time stamp // readings. ticks = (ulong) t1.LowPart - (ulong) t0.LowPart; // Number of external ticks is // difference between two // hi-res counter reads. // Note that some seemingly arbitrary mulitplies and // divides are done below. This is to maintain a // high level of precision without truncating the // most significant data. According to what value // ITERATIIONS is set to, these multiplies and // divides might need to be shifted for optimal // precision. ticks = ticks * 100000; // Convert ticks to hundred // thousandths of a tick ticks = ticks / ( count_freq.LowPart/10 ); // Hundred Thousandths of a // Ticks / ( 10 ticks/second ) // = microseconds (us) total_ticks += ticks; total_cycles += cycles; if ( ticks%count_freq.LowPart > count_freq.LowPart/2 ) ticks++; // Round up if necessary freq = cycles/ticks; // Cycles / us = MHz if ( cycles%ticks > ticks/2 ) freq++; // Round up if necessary total = ( freq + freq2 + freq3 ); // Total last three frequency // calculations } while ( (tries < 3 ) || (tries < 20)&& ((abs(3 * freq -total) > 3*TOLERANCE )|| (abs(3 * freq2-total) > 3*TOLERANCE )|| (abs(3 * freq3-total) > 3*TOLERANCE ))); // Compare last three calculations to // average of last three calculations. // Try one more significant digit. freq3 = ( total_cycles * 10 ) / total_ticks; freq2 = ( total_cycles * 100 ) / total_ticks; if ( freq2 - (freq3 * 10) >= ROUND_THRESHOLD ) freq3++; cpu_speed->raw_freq = total_cycles / total_ticks; cpu_speed->norm_freq = cpu_speed->raw_freq; freq = cpu_speed->raw_freq * 10; if( (freq3 - freq) >= ROUND_THRESHOLD ) cpu_speed->norm_freq++; cpu_speed->ex_ticks = total_ticks; cpu_speed->in_cycles = total_cycles; return; } int GetCmosTick(void) { int tick = 0; // __asm mov ah, 02h // __asm int 1Ah // __asm mov al, dh // __asm and ax, 000Fh __asm xor ax, ax __asm out 070h, al __asm xor ax, ax __asm in al, 071h // _outp( 0x70, offset ); // base = _inp( 0x71 ); // value returned in ax by function __asm mov word ptr tick, ax return tick; } //*************************************************************** // // Function: cpuTimeStamp // // Returns the pentium cpu time stamp in 2 32 bit unsigned longs // // Notes: maintains a flag to make sure the cpu supports the RDTSC instruction. There is // the overhead of checking the cpu the first time afterwhich the time consumed in // checking the flag is very minimal. You could adjust the count but then you would // have to do 64bit math. ugh. // //*************************************************************** unsigned long cpuTimeStamp(unsigned long *hi, unsigned long *low) { unsigned long ulHi = 0L; unsigned long ulLow = 0L; __asm { ;RDTSC _emit 0Fh _emit 31h mov ulLow, eax mov ulHi, edx } *hi = ulHi; *low = ulLow; return ulLow; } //*************************************************************** // // Function: diffTime64 // // Calculates the difference of a 64 bit time as represented by // two 32 bit unsigned longs // //*************************************************************** unsigned long diffTime64(unsigned long t1Hi, unsigned long t1Low, unsigned long t2Hi, unsigned long t2Low, unsigned long *tHi, unsigned long *tLow ) { unsigned long xl, xh; /* *tHi = t2Hi - t1Hi; if( t1Low > t2Low ) { *tLow = t1Low - t2Low; *tLow = ULONG_MAX - *tLow; *tHi -= 1; } else { *tLow = t2Low - t1Low; } */ __asm { mov eax, t2Low mov ebx, t1Low sub eax, ebx mov xl, eax mov eax, t2Hi mov ebx, t1Hi sbb eax, ebx mov xh, eax } *tLow = xl; *tHi = xh; return *tLow; } #define ABS_TICK(a,b) (b<a)?b+10-a:b-a static void GetCmosCpuSpeed(struct FREQ_INFO *cpu_speed) { unsigned long t1Low, t1High, t2Low, t2High, tResLow, tResHigh; int timeStart, timeStop, lapseTime; unsigned long temp; unsigned long temp1; unsigned long cpuSpeed = 0l; #ifdef WIN32 HANDLE hThread = GetCurrentThread(); int iPriority; #endif // WIN32 memset(cpu_speed, 0x00, sizeof(cpu_speed)); // This loop waits for the next tick // so that we begin speed test on a tick edge #ifdef WIN32 iPriority = GetThreadPriority(hThread); if ( iPriority != THREAD_PRIORITY_ERROR_RETURN ) { SetThreadPriority(hThread, iPriority+1); } #endif // WIN32 timeStart = GetCmosTick(); for(;;) { timeStop = GetCmosTick(); if ( ABS_TICK(timeStart,timeStop) > 0 ) { cpuTimeStamp(&t1High, &t1Low); break; } } timeStart = timeStop; for(;;) { timeStop = GetCmosTick(); if ( ABS_TICK(timeStart,timeStop) > 0 ) { cpuTimeStamp(&t2High, &t2Low); break; } } #ifdef WIN32 // Set thread priority back. if ( iPriority != THREAD_PRIORITY_ERROR_RETURN ) { SetThreadPriority(hThread, iPriority); } #endif // WIN32 diffTime64(t1High, t1Low, t2High, t2Low, &tResHigh, &tResLow ); lapseTime = ABS_TICK(timeStart,timeStop); cpuSpeed = tResLow; ///lapseTime; cpu_speed->in_cycles = tResLow; // Cycles count since we in this routine //round to nearest digit temp = cpuSpeed/1000000; temp1 = cpuSpeed/100000; temp = temp * 10; // realign with last digit = zero cpuSpeed = cpuSpeed/1000000; // cpuSpeed/1000000; cpu_speed->raw_freq = cpuSpeed; if( (temp1 - temp) >= ROUND_THRESHOLD ) cpuSpeed++; cpu_speed->norm_freq = cpuSpeed; cpu_speed->ex_ticks = (timeStop - timeStart) * 1000000; return; } static void GetBSFCpuSpeed(ulong cycles, struct FREQ_INFO *cpu_speed) { // If processor does not support time // stamp reading, but is at least a // 386 or above, utilize method of // timing a loop of BSF instructions // which take a known number of cycles // to run on i386(tm), i486(tm), and // Pentium(R) processors. LARGE_INTEGER t0,t1; // Variables for High- // Resolution Performance // Counter reads ulong freq =0; // Most current frequ. calculation ulong ticks; // Microseconds elapsed // during test LARGE_INTEGER count_freq; // High Resolution // Performance Counter // frequency int i; // Temporary Variable ulong current = 0; // Variable to store time // elapsed during loop of // of BSF instructions ulong lowest = ULONG_MAX; // Since algorithm finds // the lowest value out of // a set of samplings, // this variable is set // intially to the max // unsigned long value). // This guarantees that // the initialized value // is not later used as // the least time through // the loop. memset(cpu_speed, 0x00, sizeof(cpu_speed)); if ( !QueryPerformanceFrequency ( &count_freq ) ) return; for ( i = 0; i < SAMPLINGS; i++ ) { // Sample Ten times. Can // be increased or // decreased depending // on accuracy vs. time // requirements QueryPerformanceCounter(&t0); // Get start time _asm { mov eax, 80000000h mov bx, ITERATIONS // Number of consecutive BSF // instructions to execute. // Set identical to // nIterations constant in // speed.h loop1: bsf ecx,eax dec bx jnz loop1 } QueryPerformanceCounter(&t1); // Get end time current = (ulong) t1.LowPart - (ulong) t0.LowPart; // Number of external ticks is // difference between two // hi-res counter reads. if ( current < lowest ) // Take lowest elapsed lowest = current; // time to account } // for some samplings // being interrupted // by other operations ticks = lowest; // Note that some seemingly arbitrary mulitplies and // divides are done below. This is to maintain a // high level of precision without truncating the // most significant data. According to what value // ITERATIIONS is set to, these multiplies and // divides might need to be shifted for optimal // precision. ticks = ticks * 100000; // Convert ticks to hundred // thousandths of a tick ticks = ticks / ( count_freq.LowPart/10 ); // Hundred Thousandths of a // Ticks / ( 10 ticks/second ) // = microseconds (us) if ( ticks%count_freq.LowPart > count_freq.LowPart/2 ) ticks++; // Round up if necessary freq = cycles/ticks; // Cycles / us = MHz cpu_speed->raw_freq = freq; if ( cycles%ticks > ticks/2 ) freq++; // Round up if necessary cpu_speed->in_cycles = cycles; // Return variable structure cpu_speed->ex_ticks = ticks; // determined by one of cpu_speed->norm_freq = freq; return; } /*************************************************************** * CpuSpeed() -- Return the raw clock rate of the host CPU. * * Inputs: * clocks: 0: Use default value for number of cycles * per BSF instruction. * -1: Use CMos timer to get cpu speed. * Positive Integer: Use clocks value for number * of cycles per BSF instruction. * * Returns: * If error then return all zeroes in FREQ_INFO structure * Else return FREQ_INFO structure containing calculated * clock frequency, normalized clock frequency, number of * clock cycles during test sampling, and the number of * microseconds elapsed during the sampling. ***************************************************************/ struct FREQ_INFO wincpuspeed(int clocks) { ulong cycles; // Clock cycles elapsed // during test ushort processor = wincpuid(); // Family of processor DWORD features = wincpufeatures(); // Features of Processor int manual=0; // Specifies whether the user // manually entered the number of // cycles for the BSF instruction. struct FREQ_INFO cpu_speed; // Return structure for // cpuspeed memset(&cpu_speed, 0x00, sizeof(cpu_speed)); if ( processor & CLONE_MASK ) return cpu_speed; // Check for manual BSF instruction clock count if (clocks <= 0) { cycles = ITERATIONS * processor_cycles[processor]; } else if (0 < clocks && clocks <= MAXCLOCKS) { cycles = ITERATIONS * clocks; manual = 1; // Toggle manual control flag. // Note that this mode will not // work properly with processors // which can process multiple // BSF instructions at a time. // For example, manual mode // will not work on a // PentiumPro(R) } if ( ( features&0x00000010 ) && !(manual) ) { // On processors supporting the Read // Time Stamp opcode, compare elapsed // time on the High-Resolution Counter // with elapsed cycles on the Time // Stamp Register. if ( clocks == 0 ) GetRDTSCCpuSpeed(&cpu_speed); else GetCmosCpuSpeed(&cpu_speed); } else if ( processor >= 3 ) { GetBSFCpuSpeed(cycles, &cpu_speed); } return cpu_speed; } void CCpuId::GetCpuSpeed(CString * CpuSpeed) { struct FREQ_INFO cpu_speed; cpu_speed = wincpuspeed(0); CpuSpeed->Format("%luMHz", cpu_speed.norm_freq); }
Beispiel:
//Test.cpp #include "stdafx.h" #include "cpuid.h" #include <iostream> using namespace std; int main(int argc, char **argv() { CString m_cpuId, m_cpuSpeed; //Anstelle von CString geht auch string der STL CCpuId::GetCpuId(&m_cpuId); //oder einfach nur ein char[] Buffer CCpuId::GetCpuSpeed(&m_cpuSpeed); cout << "CPU-Speed: " << m_cpuSpeed << endl; cout << "CPU-Type: " << m_cpuId << endl; }
Gruss René
PS: Hoffentlich werde ich jetzt nicht gesteinigt...