1 /* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2006 Sam Lantinga 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 19 Sam Lantinga 20 slouken (at) libsdl.org 21 */ 22 #include "SDL_config.h" 23 24 /* CPU feature detection for SDL */ 25 26 #include "SDL.h" 27 #include "SDL_cpuinfo.h" 28 29 #if defined(__MACOSX__) && defined(__ppc__) 30 #include <sys/sysctl.h> /* For AltiVec check */ 31 #elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP 32 #include <signal.h> 33 #include <setjmp.h> 34 #endif 35 36 #define CPU_HAS_RDTSC 0x00000001 37 #define CPU_HAS_MMX 0x00000002 38 #define CPU_HAS_MMXEXT 0x00000004 39 #define CPU_HAS_3DNOW 0x00000010 40 #define CPU_HAS_3DNOWEXT 0x00000020 41 #define CPU_HAS_SSE 0x00000040 42 #define CPU_HAS_SSE2 0x00000080 43 #define CPU_HAS_ALTIVEC 0x00000100 44 45 #if SDL_ALTIVEC_BLITTERS && HAVE_SETJMP && !__MACOSX__ 46 /* This is the brute force way of detecting instruction sets... 47 the idea is borrowed from the libmpeg2 library - thanks! 48 */ 49 static jmp_buf jmpbuf; 50 static void illegal_instruction(int sig) 51 { 52 longjmp(jmpbuf, 1); 53 } 54 #endif /* HAVE_SETJMP */ 55 56 static __inline__ int CPU_haveCPUID(void) 57 { 58 int has_CPUID = 0; 59 #if defined(__GNUC__) && defined(i386) 60 __asm__ ( 61 " pushfl # Get original EFLAGS \n" 62 " popl %%eax \n" 63 " movl %%eax,%%ecx \n" 64 " xorl $0x200000,%%eax # Flip ID bit in EFLAGS \n" 65 " pushl %%eax # Save new EFLAGS value on stack \n" 66 " popfl # Replace current EFLAGS value \n" 67 " pushfl # Get new EFLAGS \n" 68 " popl %%eax # Store new EFLAGS in EAX \n" 69 " xorl %%ecx,%%eax # Can not toggle ID bit, \n" 70 " jz 1f # Processor=80486 \n" 71 " movl $1,%0 # We have CPUID support \n" 72 "1: \n" 73 : "=m" (has_CPUID) 74 : 75 : "%eax", "%ecx" 76 ); 77 #elif defined(__GNUC__) && defined(__x86_64__) 78 /* Technically, if this is being compiled under __x86_64__ then it has 79 CPUid by definition. But it's nice to be able to prove it. :) */ 80 __asm__ ( 81 " pushfq # Get original EFLAGS \n" 82 " popq %%rax \n" 83 " movq %%rax,%%rcx \n" 84 " xorl $0x200000,%%eax # Flip ID bit in EFLAGS \n" 85 " pushq %%rax # Save new EFLAGS value on stack \n" 86 " popfq # Replace current EFLAGS value \n" 87 " pushfq # Get new EFLAGS \n" 88 " popq %%rax # Store new EFLAGS in EAX \n" 89 " xorl %%ecx,%%eax # Can not toggle ID bit, \n" 90 " jz 1f # Processor=80486 \n" 91 " movl $1,%0 # We have CPUID support \n" 92 "1: \n" 93 : "=m" (has_CPUID) 94 : 95 : "%rax", "%rcx" 96 ); 97 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) 98 __asm { 99 pushfd ; Get original EFLAGS 100 pop eax 101 mov ecx, eax 102 xor eax, 200000h ; Flip ID bit in EFLAGS 103 push eax ; Save new EFLAGS value on stack 104 popfd ; Replace current EFLAGS value 105 pushfd ; Get new EFLAGS 106 pop eax ; Store new EFLAGS in EAX 107 xor eax, ecx ; Can not toggle ID bit, 108 jz done ; Processor=80486 109 mov has_CPUID,1 ; We have CPUID support 110 done: 111 } 112 #elif defined(__sun) && defined(__i386) 113 __asm ( 114 " pushfl \n" 115 " popl %eax \n" 116 " movl %eax,%ecx \n" 117 " xorl $0x200000,%eax \n" 118 " pushl %eax \n" 119 " popfl \n" 120 " pushfl \n" 121 " popl %eax \n" 122 " xorl %ecx,%eax \n" 123 " jz 1f \n" 124 " movl $1,-8(%ebp) \n" 125 "1: \n" 126 ); 127 #elif defined(__sun) && defined(__amd64) 128 __asm ( 129 " pushfq \n" 130 " popq %rax \n" 131 " movq %rax,%rcx \n" 132 " xorl $0x200000,%eax \n" 133 " pushq %rax \n" 134 " popfq \n" 135 " pushfq \n" 136 " popq %rax \n" 137 " xorl %ecx,%eax \n" 138 " jz 1f \n" 139 " movl $1,-8(%rbp) \n" 140 "1: \n" 141 ); 142 #endif 143 return has_CPUID; 144 } 145 146 static __inline__ int CPU_getCPUIDFeatures(void) 147 { 148 int features = 0; 149 #if defined(__GNUC__) && ( defined(i386) || defined(__x86_64__) ) 150 __asm__ ( 151 " movl %%ebx,%%edi\n" 152 " xorl %%eax,%%eax # Set up for CPUID instruction \n" 153 " cpuid # Get and save vendor ID \n" 154 " cmpl $1,%%eax # Make sure 1 is valid input for CPUID\n" 155 " jl 1f # We dont have the CPUID instruction\n" 156 " xorl %%eax,%%eax \n" 157 " incl %%eax \n" 158 " cpuid # Get family/model/stepping/features\n" 159 " movl %%edx,%0 \n" 160 "1: \n" 161 " movl %%edi,%%ebx\n" 162 : "=m" (features) 163 : 164 : "%eax", "%ecx", "%edx", "%edi" 165 ); 166 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) 167 __asm { 168 xor eax, eax ; Set up for CPUID instruction 169 cpuid ; Get and save vendor ID 170 cmp eax, 1 ; Make sure 1 is valid input for CPUID 171 jl done ; We dont have the CPUID instruction 172 xor eax, eax 173 inc eax 174 cpuid ; Get family/model/stepping/features 175 mov features, edx 176 done: 177 } 178 #elif defined(__sun) && (defined(__i386) || defined(__amd64)) 179 __asm( 180 " movl %ebx,%edi\n" 181 " xorl %eax,%eax \n" 182 " cpuid \n" 183 " cmpl $1,%eax \n" 184 " jl 1f \n" 185 " xorl %eax,%eax \n" 186 " incl %eax \n" 187 " cpuid \n" 188 #ifdef __i386 189 " movl %edx,-8(%ebp) \n" 190 #else 191 " movl %edx,-8(%rbp) \n" 192 #endif 193 "1: \n" 194 " movl %edi,%ebx\n" ); 195 #endif 196 return features; 197 } 198 199 static __inline__ int CPU_getCPUIDFeaturesExt(void) 200 { 201 int features = 0; 202 #if defined(__GNUC__) && (defined(i386) || defined (__x86_64__) ) 203 __asm__ ( 204 " movl %%ebx,%%edi\n" 205 " movl $0x80000000,%%eax # Query for extended functions \n" 206 " cpuid # Get extended function limit \n" 207 " cmpl $0x80000001,%%eax \n" 208 " jl 1f # Nope, we dont have function 800000001h\n" 209 " movl $0x80000001,%%eax # Setup extended function 800000001h\n" 210 " cpuid # and get the information \n" 211 " movl %%edx,%0 \n" 212 "1: \n" 213 " movl %%edi,%%ebx\n" 214 : "=m" (features) 215 : 216 : "%eax", "%ecx", "%edx", "%edi" 217 ); 218 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) 219 __asm { 220 mov eax,80000000h ; Query for extended functions 221 cpuid ; Get extended function limit 222 cmp eax,80000001h 223 jl done ; Nope, we dont have function 800000001h 224 mov eax,80000001h ; Setup extended function 800000001h 225 cpuid ; and get the information 226 mov features,edx 227 done: 228 } 229 #elif defined(__sun) && ( defined(__i386) || defined(__amd64) ) 230 __asm ( 231 " movl %ebx,%edi\n" 232 " movl $0x80000000,%eax \n" 233 " cpuid \n" 234 " cmpl $0x80000001,%eax \n" 235 " jl 1f \n" 236 " movl $0x80000001,%eax \n" 237 " cpuid \n" 238 #ifdef __i386 239 " movl %edx,-8(%ebp) \n" 240 #else 241 " movl %edx,-8(%rbp) \n" 242 #endif 243 "1: \n" 244 " movl %edi,%ebx\n" 245 ); 246 #endif 247 return features; 248 } 249 250 static __inline__ int CPU_haveRDTSC(void) 251 { 252 if ( CPU_haveCPUID() ) { 253 return (CPU_getCPUIDFeatures() & 0x00000010); 254 } 255 return 0; 256 } 257 258 static __inline__ int CPU_haveMMX(void) 259 { 260 if ( CPU_haveCPUID() ) { 261 return (CPU_getCPUIDFeatures() & 0x00800000); 262 } 263 return 0; 264 } 265 266 static __inline__ int CPU_haveMMXExt(void) 267 { 268 if ( CPU_haveCPUID() ) { 269 return (CPU_getCPUIDFeaturesExt() & 0x00400000); 270 } 271 return 0; 272 } 273 274 static __inline__ int CPU_have3DNow(void) 275 { 276 if ( CPU_haveCPUID() ) { 277 return (CPU_getCPUIDFeaturesExt() & 0x80000000); 278 } 279 return 0; 280 } 281 282 static __inline__ int CPU_have3DNowExt(void) 283 { 284 if ( CPU_haveCPUID() ) { 285 return (CPU_getCPUIDFeaturesExt() & 0x40000000); 286 } 287 return 0; 288 } 289 290 static __inline__ int CPU_haveSSE(void) 291 { 292 if ( CPU_haveCPUID() ) { 293 return (CPU_getCPUIDFeatures() & 0x02000000); 294 } 295 return 0; 296 } 297 298 static __inline__ int CPU_haveSSE2(void) 299 { 300 if ( CPU_haveCPUID() ) { 301 return (CPU_getCPUIDFeatures() & 0x04000000); 302 } 303 return 0; 304 } 305 306 static __inline__ int CPU_haveAltiVec(void) 307 { 308 volatile int altivec = 0; 309 #if defined(__MACOSX__) && defined(__ppc__) 310 int selectors[2] = { CTL_HW, HW_VECTORUNIT }; 311 int hasVectorUnit = 0; 312 size_t length = sizeof(hasVectorUnit); 313 int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0); 314 if( 0 == error ) 315 altivec = (hasVectorUnit != 0); 316 #elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP 317 void (*handler)(int sig); 318 handler = signal(SIGILL, illegal_instruction); 319 if ( setjmp(jmpbuf) == 0 ) { 320 asm volatile ("mtspr 256, %0\n\t" 321 "vand %%v0, %%v0, %%v0" 322 : 323 : "r" (-1)); 324 altivec = 1; 325 } 326 signal(SIGILL, handler); 327 #endif 328 return altivec; 329 } 330 331 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF; 332 333 static Uint32 SDL_GetCPUFeatures(void) 334 { 335 if ( SDL_CPUFeatures == 0xFFFFFFFF ) { 336 SDL_CPUFeatures = 0; 337 if ( CPU_haveRDTSC() ) { 338 SDL_CPUFeatures |= CPU_HAS_RDTSC; 339 } 340 if ( CPU_haveMMX() ) { 341 SDL_CPUFeatures |= CPU_HAS_MMX; 342 } 343 if ( CPU_haveMMXExt() ) { 344 SDL_CPUFeatures |= CPU_HAS_MMXEXT; 345 } 346 if ( CPU_have3DNow() ) { 347 SDL_CPUFeatures |= CPU_HAS_3DNOW; 348 } 349 if ( CPU_have3DNowExt() ) { 350 SDL_CPUFeatures |= CPU_HAS_3DNOWEXT; 351 } 352 if ( CPU_haveSSE() ) { 353 SDL_CPUFeatures |= CPU_HAS_SSE; 354 } 355 if ( CPU_haveSSE2() ) { 356 SDL_CPUFeatures |= CPU_HAS_SSE2; 357 } 358 if ( CPU_haveAltiVec() ) { 359 SDL_CPUFeatures |= CPU_HAS_ALTIVEC; 360 } 361 } 362 return SDL_CPUFeatures; 363 } 364 365 SDL_bool SDL_HasRDTSC(void) 366 { 367 if ( SDL_GetCPUFeatures() & CPU_HAS_RDTSC ) { 368 return SDL_TRUE; 369 } 370 return SDL_FALSE; 371 } 372 373 SDL_bool SDL_HasMMX(void) 374 { 375 if ( SDL_GetCPUFeatures() & CPU_HAS_MMX ) { 376 return SDL_TRUE; 377 } 378 return SDL_FALSE; 379 } 380 381 SDL_bool SDL_HasMMXExt(void) 382 { 383 if ( SDL_GetCPUFeatures() & CPU_HAS_MMXEXT ) { 384 return SDL_TRUE; 385 } 386 return SDL_FALSE; 387 } 388 389 SDL_bool SDL_Has3DNow(void) 390 { 391 if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOW ) { 392 return SDL_TRUE; 393 } 394 return SDL_FALSE; 395 } 396 397 SDL_bool SDL_Has3DNowExt(void) 398 { 399 if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOWEXT ) { 400 return SDL_TRUE; 401 } 402 return SDL_FALSE; 403 } 404 405 SDL_bool SDL_HasSSE(void) 406 { 407 if ( SDL_GetCPUFeatures() & CPU_HAS_SSE ) { 408 return SDL_TRUE; 409 } 410 return SDL_FALSE; 411 } 412 413 SDL_bool SDL_HasSSE2(void) 414 { 415 if ( SDL_GetCPUFeatures() & CPU_HAS_SSE2 ) { 416 return SDL_TRUE; 417 } 418 return SDL_FALSE; 419 } 420 421 SDL_bool SDL_HasAltiVec(void) 422 { 423 if ( SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC ) { 424 return SDL_TRUE; 425 } 426 return SDL_FALSE; 427 } 428 429 #ifdef TEST_MAIN 430 431 #include <stdio.h> 432 433 int main() 434 { 435 printf("RDTSC: %d\n", SDL_HasRDTSC()); 436 printf("MMX: %d\n", SDL_HasMMX()); 437 printf("MMXExt: %d\n", SDL_HasMMXExt()); 438 printf("3DNow: %d\n", SDL_Has3DNow()); 439 printf("3DNowExt: %d\n", SDL_Has3DNowExt()); 440 printf("SSE: %d\n", SDL_HasSSE()); 441 printf("SSE2: %d\n", SDL_HasSSE2()); 442 printf("AltiVec: %d\n", SDL_HasAltiVec()); 443 return 0; 444 } 445 446 #endif /* TEST_MAIN */ 447