/* Copyright (C) 2022 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * CPU-specific routines common to 32 and 64-bit x86 */ #ifndef INCLUDED_X86_X64 #define INCLUDED_X86_X64 #if !ARCH_X86_X64 #error "including x86_x64.h without ARCH_X86_X64=1" #endif #if MSC_VERSION #include // __rdtsc #endif namespace x86_x64 { /** * registers used/returned by cpuid **/ #pragma pack(push, 1) // (allows casting to int*) struct CpuidRegs { u32 eax; u32 ebx; u32 ecx; u32 edx; }; #pragma pack(pop) /** * invoke CPUID instruction. * @param regs input/output registers. * regs->eax must be set to the desired function. * some functions (e.g. 4) require regs->ecx to be set as well. * rationale: this interface (input/output structure vs. function parameters) * avoids unnecessary copying/initialization if some inputs aren't needed * and allows graceful expansion to functions that require further inputs. * @return true on success or false if the sub-function isn't supported. **/ bool cpuid(CpuidRegs* regs); /** * CPU vendor. * (this is exposed because some CPUID functions are vendor-specific.) * (an enum is easier to compare than the original string values.) **/ enum Vendors { VENDOR_UNKNOWN, VENDOR_INTEL, VENDOR_AMD }; Vendors Vendor(); enum Models { MODEL_NEHALEM_EP = 0x1A, // Bloomfield (X35xx), Gainestown (X55xx) MODEL_NEHALEM_EP_2 = 0x1E, // Clarksfield, Lynnfield (X34xx), Jasper Forest (C35xx, C55xx) MODEL_I7_I5 = 0x1F, // similar to 1E; mentioned in 253665-041US, no codename known MODEL_CLARKDALE = 0x25, // Arrandale, Clarkdale (L34xx) MODEL_WESTMERE_EP = 0x2C, // Gulftown (X36xx, X56xx) MODEL_NEHALEM_EX = 0x2E, // Beckton (X75xx) MODEL_WESTMERE_EX = 0x2F, // Gulftown uarch, Beckton package (E7-48xx) MODEL_SANDY_BRIDGE = 0x2A, // (E3-12xx, E5-26xx) MODEL_SANDY_BRIDGE_2 = 0x2D, // (E5-26xx, E5-46xx) }; size_t Model(); size_t Family(); /** * @return the colloquial processor generation * (5 = Pentium, 6 = Pentium Pro/II/III / K6, 7 = Pentium4 / Athlon, 8 = Core / Opteron) **/ size_t Generation(); /** * bit indices of CPU capability flags (128 bits). * values are defined by IA-32 CPUID feature flags - do not change! **/ enum Caps { // standard (ecx) - currently only defined by Intel CAP_SSE3 = 0+0, // Streaming SIMD Extensions 3 CAP_EST = 0+7, // Enhanced Speedstep Technology CAP_SSSE3 = 0+9, // Supplemental Streaming SIMD Extensions 3 CAP_SSE41 = 0+19, // Streaming SIMD Extensions 4.1 CAP_SSE42 = 0+20, // Streaming SIMD Extensions 4.2 // standard (edx) CAP_FPU = 32+0, // Floating Point Unit CAP_TSC = 32+4, // TimeStamp Counter CAP_MSR = 32+5, // Model Specific Registers CAP_CMOV = 32+15, // Conditional MOVe CAP_TM_SCC = 32+22, // Thermal Monitoring and Software Controlled Clock CAP_MMX = 32+23, // MultiMedia eXtensions CAP_SSE = 32+25, // Streaming SIMD Extensions CAP_SSE2 = 32+26, // Streaming SIMD Extensions 2 CAP_HT = 32+28, // HyperThreading // extended (ecx) CAP_AMD_CMP_LEGACY = 64+1, // N-core and CAP_HT is falsely set // extended (edx) CAP_AMD_MP = 96+19, // MultiProcessing capable; reserved on AMD64 CAP_AMD_MMX_EXT = 96+22, CAP_AMD_3DNOW_PRO = 96+30, CAP_AMD_3DNOW = 96+31 }; /** * @return whether the CPU supports the indicated Cap / feature flag. **/ bool Cap(Caps cap); void GetCapBits(u32* d0, u32* d1, u32* d2, u32* d3); //----------------------------------------------------------------------------- // stateless /** * @return the current value of the TimeStampCounter (a counter of * CPU cycles since power-on, which is useful for high-resolution timing * but potentially differs between multiple CPUs) * * notes: * - a macro avoids call overhead, which is important for TIMER_ACCRUE. * - x64 RDTSC writes to edx:eax and clears the upper halves of rdx and rax. **/ #if MSC_VERSION static inline u64 rdtsc() { return __rdtsc(); } #else u64 rdtsc(); #endif /** * trigger a breakpoint inside this function when it is called. **/ void DebugBreak(); /** * measure the CPU clock frequency via rdtsc and timer_Time. * (it follows that this must not be called from WHRT init.) * this takes several milliseconds (i.e. much longer than * os_cpu_ClockFrequency) but delivers accurate measurements. **/ double ClockFrequency(); } // namespace x86_x64 #endif // #ifndef INCLUDED_X86_X64