src/cpuinfo/SDL_cpuinfo.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 29 May 2006 04:04:35 +0000
branchSDL-1.3
changeset 1668 4da1ee79c9af
parent 1662 782fd950bd46
child 1691 c901fd2a42da
permissions -rw-r--r--
more tweaking indent options
     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@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
    51 illegal_instruction(int sig)
    52 {
    53     longjmp(jmpbuf, 1);
    54 }
    55 #endif /* HAVE_SETJMP */
    56 
    57 static __inline__ int
    58 CPU_haveCPUID(void)
    59 {
    60     int has_CPUID = 0;
    61 #if defined(__GNUC__) && defined(i386)
    62   __asm__("        pushfl                      # Get original EFLAGS             \n" "        popl    %%eax                                                 \n" "        movl    %%eax,%%ecx                                           \n" "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n" "        pushl   %%eax               # Save new EFLAGS value on stack  \n" "        popfl                       # Replace current EFLAGS value    \n" "        pushfl                      # Get new EFLAGS                  \n" "        popl    %%eax               # Store new EFLAGS in EAX         \n" "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n" "        jz      1f                  # Processor=80486                 \n" "        movl    $1,%0               # We have CPUID support           \n" "1:                                                                    \n": "=m"(has_CPUID):
    63   :"%eax", "%ecx");
    64 #elif defined(__GNUC__) && defined(__x86_64__)
    65 /* Technically, if this is being compiled under __x86_64__ then it has 
    66 CPUid by definition.  But it's nice to be able to prove it.  :)      */
    67   __asm__("        pushfq                      # Get original EFLAGS             \n" "        popq    %%rax                                                 \n" "        movq    %%rax,%%rcx                                           \n" "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n" "        pushq   %%rax               # Save new EFLAGS value on stack  \n" "        popfq                       # Replace current EFLAGS value    \n" "        pushfq                      # Get new EFLAGS                  \n" "        popq    %%rax               # Store new EFLAGS in EAX         \n" "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n" "        jz      1f                  # Processor=80486                 \n" "        movl    $1,%0               # We have CPUID support           \n" "1:                                                                    \n": "=m"(has_CPUID):
    68   :"%rax", "%rcx");
    69 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
    70     __asm {
    71         pushfd;
    72         Get original EFLAGS pop eax mov ecx, eax xor eax, 200000 h;
    73         Flip ID bit in EFLAGS push eax;
    74         Save new EFLAGS value on stack popfd;
    75         Replace current EFLAGS value pushfd;
    76         Get new EFLAGS pop eax;
    77         Store new EFLAGS in EAX xor eax, ecx;
    78         Can not toggle ID bit, jz done;
    79         Processor = 80486 mov has_CPUID, 1;
    80     We have CPUID support done:}
    81 #elif defined(__sun) && defined(__x86)
    82     __asm("       pushfl                 \n"
    83           "	popl    %eax           \n"
    84           "	movl    %eax,%ecx      \n"
    85           "	xorl    $0x200000,%eax \n"
    86           "	pushl   %eax           \n"
    87           "	popfl                  \n"
    88           "	pushfl                 \n"
    89           "	popl    %eax           \n"
    90           "	xorl    %ecx,%eax      \n"
    91           "	jz      1f             \n"
    92           "	movl    $1,-8(%ebp)    \n"
    93           "1:                            \n");
    94 #elif defined(__sun) && defined(__amd64)
    95     __asm("       pushfq                 \n"
    96           "       popq    %rax           \n"
    97           "       movq    %rax,%rcx      \n"
    98           "       xorl    $0x200000,%eax \n"
    99           "       pushq   %rax           \n"
   100           "       popfq                  \n"
   101           "       pushfq                 \n"
   102           "       popq    %rax           \n"
   103           "       xorl    %ecx,%eax      \n"
   104           "       jz      1f             \n"
   105           "       movl    $1,-8(%rbp)    \n"
   106           "1:                            \n");
   107 #endif
   108     return has_CPUID;
   109 }
   110 
   111 static __inline__ int
   112 CPU_getCPUIDFeatures(void)
   113 {
   114     int features = 0;
   115 #if defined(__GNUC__) && ( defined(i386) || defined(__x86_64__) )
   116   __asm__("        movl    %%ebx,%%edi\n" "        xorl    %%eax,%%eax         # Set up for CPUID instruction    \n" "        cpuid                       # Get and save vendor ID          \n" "        cmpl    $1,%%eax            # Make sure 1 is valid input for CPUID\n" "        jl      1f                  # We dont have the CPUID instruction\n" "        xorl    %%eax,%%eax                                           \n" "        incl    %%eax                                                 \n" "        cpuid                       # Get family/model/stepping/features\n" "        movl    %%edx,%0                                              \n" "1:                                                                    \n" "        movl    %%edi,%%ebx\n": "=m"(features):
   117   :"%eax", "%ecx", "%edx", "%edi");
   118 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
   119     __asm {
   120         xor eax, eax;
   121     Set up for CPUID instruction
   122             cpuid; Get and save vendor ID
   123             cmp eax, 1; Make sure 1 is valid input for CPUID
   124             jl done; We dont have the CPUID instruction
   125             xor eax, eax
   126             inc eax
   127             cpuid; Get family / model / stepping / features
   128             mov features, edx done:}
   129 #elif defined(__sun) && (defined(__x86) || defined(__amd64))
   130     __asm
   131         ("        movl    %ebx,%edi\n"
   132          "        xorl    %eax,%eax         \n"
   133          "        cpuid                     \n"
   134          "        cmpl    $1,%eax           \n"
   135          "        jl      1f                \n"
   136          "        xorl    %eax,%eax         \n"
   137          "        incl    %eax              \n"
   138          "        cpuid                     \n"
   139 #ifdef __i386
   140          "        movl    %edx,-8(%ebp)     \n"
   141 #else
   142          "        movl    %edx,-8(%rbp)     \n"
   143 #endif
   144          "1:                                \n"
   145          "        movl    %edi,%ebx\n");
   146 #endif
   147     return features;
   148 }
   149 
   150 static __inline__ int
   151 CPU_getCPUIDFeaturesExt(void)
   152 {
   153     int features = 0;
   154 #if defined(__GNUC__) && (defined(i386) || defined (__x86_64__) )
   155   __asm__("        movl    %%ebx,%%edi\n" "        movl    $0x80000000,%%eax   # Query for extended functions    \n" "        cpuid                       # Get extended function limit     \n" "        cmpl    $0x80000001,%%eax                                     \n" "        jl      1f                  # Nope, we dont have function 800000001h\n" "        movl    $0x80000001,%%eax   # Setup extended function 800000001h\n" "        cpuid                       # and get the information         \n" "        movl    %%edx,%0                                              \n" "1:                                                                    \n" "        movl    %%edi,%%ebx\n": "=m"(features):
   156   :"%eax", "%ecx", "%edx", "%edi");
   157 #elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
   158     __asm {
   159         mov eax, 80000000 h;
   160         Query for extended functions
   161             cpuid; Get extended function limit
   162             cmp eax, 80000001 h jl done; Nope
   163             , we dont have function 800000001 h mov eax, 80000001 h;
   164         Setup extended function 800000001 h cpuid;
   165     and get the information mov features, edx done:}
   166 #elif defined(__sun) && ( defined(__i386) || defined(__amd64) )
   167     __asm("        movl    %ebx,%edi\n"
   168           "        movl    $0x80000000,%eax \n"
   169           "        cpuid                    \n"
   170           "        cmpl    $0x80000001,%eax \n"
   171           "        jl      1f               \n"
   172           "        movl    $0x80000001,%eax \n"
   173           "        cpuid                    \n"
   174 #ifdef __i386
   175           "        movl    %edx,-8(%ebp)   \n"
   176 #else
   177           "        movl    %edx,-8(%rbp)   \n"
   178 #endif
   179           "1:                               \n"
   180           "        movl    %edi,%ebx\n");
   181 #endif
   182     return features;
   183 }
   184 
   185 static __inline__ int
   186 CPU_haveRDTSC(void)
   187 {
   188     if (CPU_haveCPUID()) {
   189         return (CPU_getCPUIDFeatures() & 0x00000010);
   190     }
   191     return 0;
   192 }
   193 
   194 static __inline__ int
   195 CPU_haveMMX(void)
   196 {
   197     if (CPU_haveCPUID()) {
   198         return (CPU_getCPUIDFeatures() & 0x00800000);
   199     }
   200     return 0;
   201 }
   202 
   203 static __inline__ int
   204 CPU_haveMMXExt(void)
   205 {
   206     if (CPU_haveCPUID()) {
   207         return (CPU_getCPUIDFeaturesExt() & 0x00400000);
   208     }
   209     return 0;
   210 }
   211 
   212 static __inline__ int
   213 CPU_have3DNow(void)
   214 {
   215     if (CPU_haveCPUID()) {
   216         return (CPU_getCPUIDFeaturesExt() & 0x80000000);
   217     }
   218     return 0;
   219 }
   220 
   221 static __inline__ int
   222 CPU_have3DNowExt(void)
   223 {
   224     if (CPU_haveCPUID()) {
   225         return (CPU_getCPUIDFeaturesExt() & 0x40000000);
   226     }
   227     return 0;
   228 }
   229 
   230 static __inline__ int
   231 CPU_haveSSE(void)
   232 {
   233     if (CPU_haveCPUID()) {
   234         return (CPU_getCPUIDFeatures() & 0x02000000);
   235     }
   236     return 0;
   237 }
   238 
   239 static __inline__ int
   240 CPU_haveSSE2(void)
   241 {
   242     if (CPU_haveCPUID()) {
   243         return (CPU_getCPUIDFeatures() & 0x04000000);
   244     }
   245     return 0;
   246 }
   247 
   248 static __inline__ int
   249 CPU_haveAltiVec(void)
   250 {
   251     volatile int altivec = 0;
   252 #if defined(__MACOSX__) && defined(__ppc__)
   253     int selectors[2] = { CTL_HW, HW_VECTORUNIT };
   254     int hasVectorUnit = 0;
   255     size_t length = sizeof(hasVectorUnit);
   256     int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0);
   257     if (0 == error)
   258         altivec = (hasVectorUnit != 0);
   259 #elif SDL_ALTIVEC_BLITTERS && HAVE_SETJMP
   260     void (*handler) (int sig);
   261     handler = signal(SIGILL, illegal_instruction);
   262     if (setjmp(jmpbuf) == 0) {
   263         asm volatile ("mtspr 256, %0\n\t" "vand %%v0, %%v0, %%v0"::"r" (-1));
   264         altivec = 1;
   265     }
   266     signal(SIGILL, handler);
   267 #endif
   268     return altivec;
   269 }
   270 
   271 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
   272 
   273 static Uint32
   274 SDL_GetCPUFeatures(void)
   275 {
   276     if (SDL_CPUFeatures == 0xFFFFFFFF) {
   277         SDL_CPUFeatures = 0;
   278         if (CPU_haveRDTSC()) {
   279             SDL_CPUFeatures |= CPU_HAS_RDTSC;
   280         }
   281         if (CPU_haveMMX()) {
   282             SDL_CPUFeatures |= CPU_HAS_MMX;
   283         }
   284         if (CPU_haveMMXExt()) {
   285             SDL_CPUFeatures |= CPU_HAS_MMXEXT;
   286         }
   287         if (CPU_have3DNow()) {
   288             SDL_CPUFeatures |= CPU_HAS_3DNOW;
   289         }
   290         if (CPU_have3DNowExt()) {
   291             SDL_CPUFeatures |= CPU_HAS_3DNOWEXT;
   292         }
   293         if (CPU_haveSSE()) {
   294             SDL_CPUFeatures |= CPU_HAS_SSE;
   295         }
   296         if (CPU_haveSSE2()) {
   297             SDL_CPUFeatures |= CPU_HAS_SSE2;
   298         }
   299         if (CPU_haveAltiVec()) {
   300             SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
   301         }
   302     }
   303     return SDL_CPUFeatures;
   304 }
   305 
   306 SDL_bool
   307 SDL_HasRDTSC(void)
   308 {
   309     if (SDL_GetCPUFeatures() & CPU_HAS_RDTSC) {
   310         return SDL_TRUE;
   311     }
   312     return SDL_FALSE;
   313 }
   314 
   315 SDL_bool
   316 SDL_HasMMX(void)
   317 {
   318     if (SDL_GetCPUFeatures() & CPU_HAS_MMX) {
   319         return SDL_TRUE;
   320     }
   321     return SDL_FALSE;
   322 }
   323 
   324 SDL_bool
   325 SDL_HasMMXExt(void)
   326 {
   327     if (SDL_GetCPUFeatures() & CPU_HAS_MMXEXT) {
   328         return SDL_TRUE;
   329     }
   330     return SDL_FALSE;
   331 }
   332 
   333 SDL_bool
   334 SDL_Has3DNow(void)
   335 {
   336     if (SDL_GetCPUFeatures() & CPU_HAS_3DNOW) {
   337         return SDL_TRUE;
   338     }
   339     return SDL_FALSE;
   340 }
   341 
   342 SDL_bool
   343 SDL_Has3DNowExt(void)
   344 {
   345     if (SDL_GetCPUFeatures() & CPU_HAS_3DNOWEXT) {
   346         return SDL_TRUE;
   347     }
   348     return SDL_FALSE;
   349 }
   350 
   351 SDL_bool
   352 SDL_HasSSE(void)
   353 {
   354     if (SDL_GetCPUFeatures() & CPU_HAS_SSE) {
   355         return SDL_TRUE;
   356     }
   357     return SDL_FALSE;
   358 }
   359 
   360 SDL_bool
   361 SDL_HasSSE2(void)
   362 {
   363     if (SDL_GetCPUFeatures() & CPU_HAS_SSE2) {
   364         return SDL_TRUE;
   365     }
   366     return SDL_FALSE;
   367 }
   368 
   369 SDL_bool
   370 SDL_HasAltiVec(void)
   371 {
   372     if (SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC) {
   373         return SDL_TRUE;
   374     }
   375     return SDL_FALSE;
   376 }
   377 
   378 #ifdef TEST_MAIN
   379 
   380 #include <stdio.h>
   381 
   382 int
   383 main()
   384 {
   385     printf("RDTSC: %d\n", SDL_HasRDTSC());
   386     printf("MMX: %d\n", SDL_HasMMX());
   387     printf("MMXExt: %d\n", SDL_HasMMXExt());
   388     printf("3DNow: %d\n", SDL_Has3DNow());
   389     printf("3DNowExt: %d\n", SDL_Has3DNowExt());
   390     printf("SSE: %d\n", SDL_HasSSE());
   391     printf("SSE2: %d\n", SDL_HasSSE2());
   392     printf("AltiVec: %d\n", SDL_HasAltiVec());
   393     return 0;
   394 }
   395 
   396 #endif /* TEST_MAIN */
   397 /* vi: set ts=4 sw=4 expandtab: */