1/* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2012 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@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__) || defined(__ppc64__)) 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 */ 49static jmp_buf jmpbuf; 50static void illegal_instruction(int sig) 51{ 52 longjmp(jmpbuf, 1); 53} 54#endif /* HAVE_SETJMP */ 55 56static __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 79CPUid 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 110done: 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 146static __inline__ int CPU_getCPUIDFeatures(void) 147{ 148 int features = 0; 149#if defined(__GNUC__) && defined(i386) 150 __asm__ ( 151" xorl %%eax,%%eax # Set up for CPUID instruction \n" 152" pushl %%ebx \n" 153" cpuid # Get and save vendor ID \n" 154" popl %%ebx \n" 155" cmpl $1,%%eax # Make sure 1 is valid input for CPUID\n" 156" jl 1f # We dont have the CPUID instruction\n" 157" xorl %%eax,%%eax \n" 158" incl %%eax \n" 159" pushl %%ebx \n" 160" cpuid # Get family/model/stepping/features\n" 161" popl %%ebx \n" 162" movl %%edx,%0 \n" 163"1: \n" 164 : "=m" (features) 165 : 166 : "%eax", "%ecx", "%edx" 167 ); 168#elif defined(__GNUC__) && defined(__x86_64__) 169 __asm__ ( 170" xorl %%eax,%%eax # Set up for CPUID instruction \n" 171" pushq %%rbx \n" 172" cpuid # Get and save vendor ID \n" 173" popq %%rbx \n" 174" cmpl $1,%%eax # Make sure 1 is valid input for CPUID\n" 175" jl 1f # We dont have the CPUID instruction\n" 176" xorl %%eax,%%eax \n" 177" incl %%eax \n" 178" pushq %%rbx \n" 179" cpuid # Get family/model/stepping/features\n" 180" popq %%rbx \n" 181" movl %%edx,%0 \n" 182"1: \n" 183 : "=m" (features) 184 : 185 : "%rax", "%rcx", "%rdx" 186 ); 187#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) 188 __asm { 189 xor eax, eax ; Set up for CPUID instruction 190 push ebx 191 cpuid ; Get and save vendor ID 192 pop ebx 193 cmp eax, 1 ; Make sure 1 is valid input for CPUID 194 jl done ; We dont have the CPUID instruction 195 xor eax, eax 196 inc eax 197 push ebx 198 cpuid ; Get family/model/stepping/features 199 pop ebx 200 mov features, edx 201done: 202 } 203#elif defined(__sun) && (defined(__i386) || defined(__amd64)) 204 __asm( 205" xorl %eax,%eax \n" 206" pushl %ebx \n" 207" cpuid \n" 208" popl %ebx \n" 209" cmpl $1,%eax \n" 210" jl 1f \n" 211" xorl %eax,%eax \n" 212" incl %eax \n" 213" pushl %ebx \n" 214" cpuid \n" 215" popl %ebx \n" 216#ifdef __i386 217" movl %edx,-8(%ebp) \n" 218#else 219" movl %edx,-8(%rbp) \n" 220#endif 221"1: \n" 222#endif 223 return features; 224} 225 226static __inline__ int CPU_getCPUIDFeaturesExt(void) 227{ 228 int features = 0; 229#if defined(__GNUC__) && defined(i386) 230 __asm__ ( 231" movl $0x80000000,%%eax # Query for extended functions \n" 232" pushl %%ebx \n" 233" cpuid # Get extended function limit \n" 234" popl %%ebx \n" 235" cmpl $0x80000001,%%eax \n" 236" jl 1f # Nope, we dont have function 800000001h\n" 237" movl $0x80000001,%%eax # Setup extended function 800000001h\n" 238" pushl %%ebx \n" 239" cpuid # and get the information \n" 240" popl %%ebx \n" 241" movl %%edx,%0 \n" 242"1: \n" 243 : "=m" (features) 244 : 245 : "%eax", "%ecx", "%edx" 246 ); 247#elif defined(__GNUC__) && defined (__x86_64__) 248 __asm__ ( 249" movl $0x80000000,%%eax # Query for extended functions \n" 250" pushq %%rbx \n" 251" cpuid # Get extended function limit \n" 252" popq %%rbx \n" 253" cmpl $0x80000001,%%eax \n" 254" jl 1f # Nope, we dont have function 800000001h\n" 255" movl $0x80000001,%%eax # Setup extended function 800000001h\n" 256" pushq %%rbx \n" 257" cpuid # and get the information \n" 258" popq %%rbx \n" 259" movl %%edx,%0 \n" 260"1: \n" 261 : "=m" (features) 262 : 263 : "%rax", "%rcx", "%rdx" 264 ); 265#elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) 266 __asm { 267 mov eax,80000000h ; Query for extended functions 268 push ebx 269 cpuid ; Get extended function limit 270 pop ebx 271 cmp eax,80000001h 272 jl done ; Nope, we dont have function 800000001h 273 mov eax,80000001h ; Setup extended function 800000001h 274 push ebx 275 cpuid ; and get the information 276 pop ebx 277 mov features,edx 278done: 279 } 280#elif defined(__sun) && ( defined(__i386) || defined(__amd64) ) 281 __asm ( 282" movl $0x80000000,%eax \n" 283" pushl %ebx \n" 284" cpuid \n" 285" popl %ebx \n" 286" cmpl $0x80000001,%eax \n" 287" jl 1f \n" 288" movl $0x80000001,%eax \n" 289" pushl %ebx \n" 290" cpuid \n" 291" popl %ebx \n" 292#ifdef __i386 293" movl %edx,-8(%ebp) \n" 294#else 295" movl %edx,-8(%rbp) \n" 296#endif 297"1: \n" 298 ); 299#endif 300 return features; 301} 302 303static __inline__ int CPU_haveRDTSC(void) 304{ 305 if ( CPU_haveCPUID() ) { 306 return (CPU_getCPUIDFeatures() & 0x00000010); 307 } 308 return 0; 309} 310 311static __inline__ int CPU_haveMMX(void) 312{ 313 if ( CPU_haveCPUID() ) { 314 return (CPU_getCPUIDFeatures() & 0x00800000); 315 } 316 return 0; 317} 318 319static __inline__ int CPU_haveMMXExt(void) 320{ 321 if ( CPU_haveCPUID() ) { 322 return (CPU_getCPUIDFeaturesExt() & 0x00400000); 323 } 324 return 0; 325} 326 327static __inline__ int CPU_have3DNow(void) 328{ 329 if ( CPU_haveCPUID() ) { 330 return (CPU_getCPUIDFeaturesExt() & 0x80000000); 331 } 332 return 0; 333} 334 335static __inline__ int CPU_have3DNowExt(void) 336{ 337 if ( CPU_haveCPUID() ) { 338 return (CPU_getCPUIDFeaturesExt() & 0x40000000); 339 } 340 return 0; 341} 342 343static __inline__ int CPU_haveSSE(void) 344{ 345 if ( CPU_haveCPUID() ) { 346 return (CPU_getCPUIDFeatures() & 0x02000000); 347 } 348 return 0; 349} 350 351static __inline__ int CPU_haveSSE2(void) 352{ 353 if ( CPU_haveCPUID() ) { 354 return (CPU_getCPUIDFeatures() & 0x04000000); 355 } 356 return 0; 357} 358 359static __inline__ int CPU_haveAltiVec(void) 360{ 361 volatile int altivec = 0; 362#if defined(__MACOSX__) && (defined(__ppc__) || defined(__ppc64__)) 363 int selectors[2] = { CTL_HW, HW_VECTORUNIT }; 364 int hasVectorUnit = 0; 365 size_t length = sizeof(hasVectorUnit); 366 int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0); 367 if( 0 == error ) 368 altivec = (hasVectorUnit != 0); 369#elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP 370 void (*handler)(int sig); 371 handler = signal(SIGILL, illegal_instruction); 372 if ( setjmp(jmpbuf) == 0 ) { 373 asm volatile ("mtspr 256, %0\n\t" 374 "vand %%v0, %%v0, %%v0" 375 : 376 : "r" (-1)); 377 altivec = 1; 378 } 379 signal(SIGILL, handler); 380#endif 381 return altivec; 382} 383 384static Uint32 SDL_CPUFeatures = 0xFFFFFFFF; 385 386static Uint32 SDL_GetCPUFeatures(void) 387{ 388 if ( SDL_CPUFeatures == 0xFFFFFFFF ) { 389 SDL_CPUFeatures = 0; 390 if ( CPU_haveRDTSC() ) { 391 SDL_CPUFeatures |= CPU_HAS_RDTSC; 392 } 393 if ( CPU_haveMMX() ) { 394 SDL_CPUFeatures |= CPU_HAS_MMX; 395 } 396 if ( CPU_haveMMXExt() ) { 397 SDL_CPUFeatures |= CPU_HAS_MMXEXT; 398 } 399 if ( CPU_have3DNow() ) { 400 SDL_CPUFeatures |= CPU_HAS_3DNOW; 401 } 402 if ( CPU_have3DNowExt() ) { 403 SDL_CPUFeatures |= CPU_HAS_3DNOWEXT; 404 } 405 if ( CPU_haveSSE() ) { 406 SDL_CPUFeatures |= CPU_HAS_SSE; 407 } 408 if ( CPU_haveSSE2() ) { 409 SDL_CPUFeatures |= CPU_HAS_SSE2; 410 } 411 if ( CPU_haveAltiVec() ) { 412 SDL_CPUFeatures |= CPU_HAS_ALTIVEC; 413 } 414 } 415 return SDL_CPUFeatures; 416} 417 418SDL_bool SDL_HasRDTSC(void) 419{ 420 if ( SDL_GetCPUFeatures() & CPU_HAS_RDTSC ) { 421 return SDL_TRUE; 422 } 423 return SDL_FALSE; 424} 425 426SDL_bool SDL_HasMMX(void) 427{ 428 if ( SDL_GetCPUFeatures() & CPU_HAS_MMX ) { 429 return SDL_TRUE; 430 } 431 return SDL_FALSE; 432} 433 434SDL_bool SDL_HasMMXExt(void) 435{ 436 if ( SDL_GetCPUFeatures() & CPU_HAS_MMXEXT ) { 437 return SDL_TRUE; 438 } 439 return SDL_FALSE; 440} 441 442SDL_bool SDL_Has3DNow(void) 443{ 444 if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOW ) { 445 return SDL_TRUE; 446 } 447 return SDL_FALSE; 448} 449 450SDL_bool SDL_Has3DNowExt(void) 451{ 452 if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOWEXT ) { 453 return SDL_TRUE; 454 } 455 return SDL_FALSE; 456} 457 458SDL_bool SDL_HasSSE(void) 459{ 460 if ( SDL_GetCPUFeatures() & CPU_HAS_SSE ) { 461 return SDL_TRUE; 462 } 463 return SDL_FALSE; 464} 465 466SDL_bool SDL_HasSSE2(void) 467{ 468 if ( SDL_GetCPUFeatures() & CPU_HAS_SSE2 ) { 469 return SDL_TRUE; 470 } 471 return SDL_FALSE; 472} 473 474SDL_bool SDL_HasAltiVec(void) 475{ 476 if ( SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC ) { 477 return SDL_TRUE; 478 } 479 return SDL_FALSE; 480} 481 482#ifdef TEST_MAIN 483 484#include <stdio.h> 485 486int main() 487{ 488 printf("RDTSC: %d\n", SDL_HasRDTSC()); 489 printf("MMX: %d\n", SDL_HasMMX()); 490 printf("MMXExt: %d\n", SDL_HasMMXExt()); 491 printf("3DNow: %d\n", SDL_Has3DNow()); 492 printf("3DNowExt: %d\n", SDL_Has3DNowExt()); 493 printf("SSE: %d\n", SDL_HasSSE()); 494 printf("SSE2: %d\n", SDL_HasSSE2()); 495 printf("AltiVec: %d\n", SDL_HasAltiVec()); 496 return 0; 497} 498 499#endif /* TEST_MAIN */ 500