src/cpuinfo/SDL_cpuinfo.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 24 Jan 2004 05:57:56 +0000
changeset 786 e1e0a0a94570
parent 785 ca06a994f03c
child 787 07760c8854d1
permissions -rw-r--r--
*** empty log message ***
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2004 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 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     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 #ifdef SAVE_RCSID
    24 static char rcsid =
    25  "@(#) $Id$";
    26 #endif
    27 
    28 /* CPU feature detection for SDL */
    29 
    30 #include "SDL.h"
    31 #include "SDL_cpuinfo.h"
    32 
    33 #ifdef MACOSX
    34 #include <sys/sysctl.h> /* For AltiVec check */
    35 #endif
    36 
    37 #define CPU_HAS_RDTSC	0x00000001
    38 #define CPU_HAS_MMX	0x00000002
    39 #define CPU_HAS_MMXEXT	0x00000004
    40 #define CPU_HAS_3DNOW	0x00000010
    41 #define CPU_HAS_3DNOWEXT 0x00000020
    42 #define CPU_HAS_SSE	0x00000040
    43 #define CPU_HAS_SSE2	0x00000080
    44 #define CPU_HAS_ALTIVEC	0x00000100
    45 
    46 static __inline__ int CPU_haveCPUID()
    47 {
    48 	int has_CPUID = 0;
    49 #if defined(__GNUC__) && defined(i386)
    50 	__asm__ (
    51 "        pushfl                      # Get original EFLAGS             \n"
    52 "        popl    %%eax                                                 \n"
    53 "        movl    %%eax,%%ecx                                           \n"
    54 "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n"
    55 "        pushl   %%eax               # Save new EFLAGS value on stack  \n"
    56 "        popfl                       # Replace current EFLAGS value    \n"
    57 "        pushfl                      # Get new EFLAGS                  \n"
    58 "        popl    %%eax               # Store new EFLAGS in EAX         \n"
    59 "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n"
    60 "        jz      1f                  # Processor=80486                 \n"
    61 "        movl    $1,%0               # We have CPUID support           \n"
    62 "1:                                                                    \n"
    63 	: "=m" (has_CPUID)
    64 	:
    65 	: "%eax", "%ecx"
    66 	);
    67 #elif defined(_MSC_VER)
    68 	__asm {
    69         pushfd                      ; Get original EFLAGS
    70         pop     eax
    71         mov     ecx, eax
    72         xor     eax, 200000h        ; Flip ID bit in EFLAGS
    73         push    eax                 ; Save new EFLAGS value on stack
    74         popfd                       ; Replace current EFLAGS value
    75         pushfd                      ; Get new EFLAGS
    76         pop     eax                 ; Store new EFLAGS in EAX
    77         xor     eax, ecx            ; Can not toggle ID bit,
    78         jz      done                ; Processor=80486
    79         mov     has_CPUID,1         ; We have CPUID support
    80 done:
    81 	}
    82 #endif
    83 	return has_CPUID;
    84 }
    85 
    86 static __inline__ int CPU_getCPUIDFeatures()
    87 {
    88 	int features = 0;
    89 #if defined(__GNUC__) && defined(i386)
    90 	__asm__ (
    91 "        movl    %%ebx,%%edi\n"
    92 "        xorl    %%eax,%%eax         # Set up for CPUID instruction    \n"
    93 "        cpuid                       # Get and save vendor ID          \n"
    94 "        cmpl    $1,%%eax            # Make sure 1 is valid input for CPUID\n"
    95 "        jl      1f                  # We dont have the CPUID instruction\n"
    96 "        xorl    %%eax,%%eax                                           \n"
    97 "        incl    %%eax                                                 \n"
    98 "        cpuid                       # Get family/model/stepping/features\n"
    99 "        movl    %%edx,%0                                              \n"
   100 "1:                                                                    \n"
   101 "        movl    %%edi,%%ebx\n"
   102 	: "=m" (features)
   103 	:
   104 	: "%eax", "%ebx", "%ecx", "%edx", "%edi"
   105 	);
   106 #elif defined(_MSC_VER)
   107 	__asm {
   108         xor     eax, eax            ; Set up for CPUID instruction
   109         cpuid                       ; Get and save vendor ID
   110         cmp     eax, 1              ; Make sure 1 is valid input for CPUID
   111         jl      done                ; We dont have the CPUID instruction
   112         xor     eax, eax
   113         inc     eax
   114         cpuid                       ; Get family/model/stepping/features
   115         mov     features, edx
   116 done:
   117 	}
   118 #endif
   119 	return features;
   120 }
   121 
   122 static __inline__ int CPU_getCPUIDFeaturesExt()
   123 {
   124 	int features = 0;
   125 #if defined(__GNUC__) && defined(i386)
   126 	__asm__ (
   127 "        movl    %%ebx,%%edi\n"
   128 "        movl    $0x80000000,%%eax   # Query for extended functions    \n"
   129 "        cpuid                       # Get extended function limit     \n"
   130 "        cmpl    $0x80000001,%%eax                                     \n"
   131 "        jbe     1f                  # Nope, we dont have function 800000001h\n"
   132 "        movl    $0x80000001,%%eax   # Setup extended function 800000001h\n"
   133 "        cpuid                       # and get the information         \n"
   134 "        movl    %%edx,%0                                              \n"
   135 "1:                                                                    \n"
   136 "        movl    %%edi,%%ebx\n"
   137 	: "=m" (features)
   138 	:
   139 	: "%eax", "%ebx", "%ecx", "%edx", "%edi"
   140 	);
   141 #elif defined(_MSC_VER)
   142 	__asm {
   143         mov     eax,80000000h       ; Query for extended functions
   144         cpuid                       ; Get extended function limit
   145         cmp     eax,80000001h
   146         jbe     done                ; Nope, we dont have function 800000001h
   147         mov     eax,80000001h       ; Setup extended function 800000001h
   148         cpuid                       ; and get the information
   149         mov     features,edx
   150 done:
   151 	}
   152 #endif
   153 	return features;
   154 }
   155 
   156 static __inline__ int CPU_haveRDTSC()
   157 {
   158 	if ( CPU_haveCPUID() ) {
   159 		return (CPU_getCPUIDFeatures() & 0x00000010);
   160 	}
   161 	return 0;
   162 }
   163 
   164 static __inline__ int CPU_haveMMX()
   165 {
   166 	if ( CPU_haveCPUID() ) {
   167 		return (CPU_getCPUIDFeatures() & 0x00800000);
   168 	}
   169 	return 0;
   170 }
   171 
   172 static __inline__ int CPU_haveMMXExt()
   173 {
   174 	if ( CPU_haveCPUID() ) {
   175 		return (CPU_getCPUIDFeaturesExt() & 0x00400000);
   176 	}
   177 	return 0;
   178 }
   179 
   180 static __inline__ int CPU_have3DNow()
   181 {
   182 	if ( CPU_haveCPUID() ) {
   183 		return (CPU_getCPUIDFeaturesExt() & 0x80000000);
   184 	}
   185 	return 0;
   186 }
   187 
   188 static __inline__ int CPU_have3DNowExt()
   189 {
   190 	if ( CPU_haveCPUID() ) {
   191 		return (CPU_getCPUIDFeaturesExt() & 0x40000000);
   192 	}
   193 	return 0;
   194 }
   195 
   196 static __inline__ int CPU_haveSSE()
   197 {
   198 	if ( CPU_haveCPUID() ) {
   199 		return (CPU_getCPUIDFeatures() & 0x02000000);
   200 	}
   201 	return 0;
   202 }
   203 
   204 static __inline__ int CPU_haveSSE2()
   205 {
   206 	if ( CPU_haveCPUID() ) {
   207 		return (CPU_getCPUIDFeatures() & 0x04000000);
   208 	}
   209 	return 0;
   210 }
   211 
   212 static __inline__ int CPU_haveAltiVec()
   213 {
   214 #ifdef MACOSX
   215 	/* TODO: This check works on OS X. It would be nice to detect AltiVec
   216 	   properly on for example Linux/PPC, too. But I don't know how that
   217 	   is done in Linux (or FreeBSD, or whatever other OS you run PPC :-)
   218 	 */
   219 	int selectors[2] = { CTL_HW, HW_VECTORUNIT }; 
   220 	int hasVectorUnit = 0; 
   221 	size_t length = sizeof(hasVectorUnit); 
   222 	int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0); 
   223 	if( 0 == error )
   224 		return hasVectorUnit != 0; 
   225 #endif
   226 	return 0; 
   227 }
   228 
   229 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
   230 
   231 static Uint32 SDL_GetCPUFeatures()
   232 {
   233 	if ( SDL_CPUFeatures == 0xFFFFFFFF ) {
   234 		SDL_CPUFeatures = 0;
   235 		if ( CPU_haveRDTSC() ) {
   236 			SDL_CPUFeatures |= CPU_HAS_RDTSC;
   237 		}
   238 		if ( CPU_haveMMX() ) {
   239 			SDL_CPUFeatures |= CPU_HAS_MMX;
   240 		}
   241 		if ( CPU_haveMMXExt() ) {
   242 			SDL_CPUFeatures |= CPU_HAS_MMXEXT;
   243 		}
   244 		if ( CPU_have3DNow() ) {
   245 			SDL_CPUFeatures |= CPU_HAS_3DNOW;
   246 		}
   247 		if ( CPU_have3DNowExt() ) {
   248 			SDL_CPUFeatures |= CPU_HAS_3DNOWEXT;
   249 		}
   250 		if ( CPU_haveSSE() ) {
   251 			SDL_CPUFeatures |= CPU_HAS_SSE;
   252 		}
   253 		if ( CPU_haveSSE2() ) {
   254 			SDL_CPUFeatures |= CPU_HAS_SSE2;
   255 		}
   256 		if ( CPU_haveAltiVec() ) {
   257 			SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
   258 		}
   259 	}
   260 	return SDL_CPUFeatures;
   261 }
   262 
   263 SDL_bool SDL_HasRDTSC()
   264 {
   265 	if ( SDL_GetCPUFeatures() & CPU_HAS_RDTSC ) {
   266 		return SDL_TRUE;
   267 	}
   268 	return SDL_FALSE;
   269 }
   270 
   271 SDL_bool SDL_HasMMX()
   272 {
   273 	if ( SDL_GetCPUFeatures() & CPU_HAS_MMX ) {
   274 		return SDL_TRUE;
   275 	}
   276 	return SDL_FALSE;
   277 }
   278 
   279 SDL_bool SDL_Has3DNow()
   280 {
   281 	if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOW ) {
   282 		return SDL_TRUE;
   283 	}
   284 	return SDL_FALSE;
   285 }
   286 
   287 SDL_bool SDL_HasSSE()
   288 {
   289 	if ( SDL_GetCPUFeatures() & CPU_HAS_SSE ) {
   290 		return SDL_TRUE;
   291 	}
   292 	return SDL_FALSE;
   293 }
   294 
   295 SDL_bool SDL_HasAltiVec()
   296 {
   297 	if ( SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC ) {
   298 		return SDL_TRUE;
   299 	}
   300 	return SDL_FALSE;
   301 }
   302 
   303 SDL_bool SDL_HasMMXExt()
   304 {
   305    if ( SDL_GetCPUFeatures() & CPU_HAS_MMXEXT ) {
   306       return SDL_TRUE;
   307    }
   308    return SDL_FALSE;
   309 }
   310 
   311 SDL_bool SDL_Has3DNowExt()
   312 {
   313    if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOWEXT ) {
   314       return SDL_TRUE;
   315    }
   316    return SDL_FALSE;
   317 }
   318 
   319 SDL_bool SDL_HasSSE2()
   320 {
   321    if ( SDL_GetCPUFeatures() & CPU_HAS_SSE2 ) {
   322       return SDL_TRUE;
   323    }
   324    return SDL_FALSE;
   325 }
   326 
   327 #ifdef TEST_MAIN
   328 
   329 #include <stdio.h>
   330 
   331 int main()
   332 {
   333 	printf("RDTSC: %d\n", SDL_HasRDTSC());
   334 	printf("MMX: %d\n", SDL_HasMMX());
   335 	printf("MMXExt: %d\n", SDL_HasMMXExt());
   336 	printf("3DNow: %d\n", SDL_Has3DNow());
   337 	printf("3DNowExt: %d\n", SDL_Has3DNowExt());
   338 	printf("SSE: %d\n", SDL_HasSSE());
   339 	printf("SSE2: %d\n", SDL_HasSSE2());
   340 	printf("AltiVec: %d\n", SDL_HasAltiVec());
   341 	return 0;
   342 }
   343 
   344 #endif /* TEST_MAIN */