src/cpuinfo/SDL_cpuinfo.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 29 Jan 2004 16:15:12 +0000
changeset 795 275708f2e838
parent 793 c20f08c4f437
child 796 dec19b813b04
permissions -rw-r--r--
Check for altivec assembly support before trying to use it. :)
     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 #ifdef unix /* FIXME: Better setjmp detection? */
    31 #define USE_SETJMP
    32 #include <signal.h>
    33 #include <setjmp.h>
    34 #endif
    35 
    36 #include "SDL.h"
    37 #include "SDL_cpuinfo.h"
    38 
    39 #ifdef MACOSX
    40 #include <sys/sysctl.h> /* For AltiVec check */
    41 #endif
    42 
    43 #define CPU_HAS_RDTSC	0x00000001
    44 #define CPU_HAS_MMX	0x00000002
    45 #define CPU_HAS_MMXEXT	0x00000004
    46 #define CPU_HAS_3DNOW	0x00000010
    47 #define CPU_HAS_3DNOWEXT 0x00000020
    48 #define CPU_HAS_SSE	0x00000040
    49 #define CPU_HAS_SSE2	0x00000080
    50 #define CPU_HAS_ALTIVEC	0x00000100
    51 
    52 #ifdef USE_SETJMP
    53 /* This is the brute force way of detecting instruction sets...
    54    the idea is borrowed from the libmpeg2 library - thanks!
    55  */
    56 static jmp_buf jmpbuf;
    57 static void illegal_instruction(int sig)
    58 {
    59 	longjmp(jmpbuf, 1);
    60 }
    61 #endif // USE_SETJMP
    62 
    63 static __inline__ int CPU_haveCPUID()
    64 {
    65 	int has_CPUID = 0;
    66 #if defined(__GNUC__) && defined(i386)
    67 	__asm__ (
    68 "        pushfl                      # Get original EFLAGS             \n"
    69 "        popl    %%eax                                                 \n"
    70 "        movl    %%eax,%%ecx                                           \n"
    71 "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n"
    72 "        pushl   %%eax               # Save new EFLAGS value on stack  \n"
    73 "        popfl                       # Replace current EFLAGS value    \n"
    74 "        pushfl                      # Get new EFLAGS                  \n"
    75 "        popl    %%eax               # Store new EFLAGS in EAX         \n"
    76 "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n"
    77 "        jz      1f                  # Processor=80486                 \n"
    78 "        movl    $1,%0               # We have CPUID support           \n"
    79 "1:                                                                    \n"
    80 	: "=m" (has_CPUID)
    81 	:
    82 	: "%eax", "%ecx"
    83 	);
    84 #elif defined(_MSC_VER)
    85 	__asm {
    86         pushfd                      ; Get original EFLAGS
    87         pop     eax
    88         mov     ecx, eax
    89         xor     eax, 200000h        ; Flip ID bit in EFLAGS
    90         push    eax                 ; Save new EFLAGS value on stack
    91         popfd                       ; Replace current EFLAGS value
    92         pushfd                      ; Get new EFLAGS
    93         pop     eax                 ; Store new EFLAGS in EAX
    94         xor     eax, ecx            ; Can not toggle ID bit,
    95         jz      done                ; Processor=80486
    96         mov     has_CPUID,1         ; We have CPUID support
    97 done:
    98 	}
    99 #endif
   100 	return has_CPUID;
   101 }
   102 
   103 static __inline__ int CPU_getCPUIDFeatures()
   104 {
   105 	int features = 0;
   106 #if defined(__GNUC__) && defined(i386)
   107 	__asm__ (
   108 "        movl    %%ebx,%%edi\n"
   109 "        xorl    %%eax,%%eax         # Set up for CPUID instruction    \n"
   110 "        cpuid                       # Get and save vendor ID          \n"
   111 "        cmpl    $1,%%eax            # Make sure 1 is valid input for CPUID\n"
   112 "        jl      1f                  # We dont have the CPUID instruction\n"
   113 "        xorl    %%eax,%%eax                                           \n"
   114 "        incl    %%eax                                                 \n"
   115 "        cpuid                       # Get family/model/stepping/features\n"
   116 "        movl    %%edx,%0                                              \n"
   117 "1:                                                                    \n"
   118 "        movl    %%edi,%%ebx\n"
   119 	: "=m" (features)
   120 	:
   121 	: "%eax", "%ebx", "%ecx", "%edx", "%edi"
   122 	);
   123 #elif defined(_MSC_VER)
   124 	__asm {
   125         xor     eax, eax            ; Set up for CPUID instruction
   126         cpuid                       ; Get and save vendor ID
   127         cmp     eax, 1              ; Make sure 1 is valid input for CPUID
   128         jl      done                ; We dont have the CPUID instruction
   129         xor     eax, eax
   130         inc     eax
   131         cpuid                       ; Get family/model/stepping/features
   132         mov     features, edx
   133 done:
   134 	}
   135 #endif
   136 	return features;
   137 }
   138 
   139 static __inline__ int CPU_getCPUIDFeaturesExt()
   140 {
   141 	int features = 0;
   142 #if defined(__GNUC__) && defined(i386)
   143 	__asm__ (
   144 "        movl    %%ebx,%%edi\n"
   145 "        movl    $0x80000000,%%eax   # Query for extended functions    \n"
   146 "        cpuid                       # Get extended function limit     \n"
   147 "        cmpl    $0x80000001,%%eax                                     \n"
   148 "        jl      1f                  # Nope, we dont have function 800000001h\n"
   149 "        movl    $0x80000001,%%eax   # Setup extended function 800000001h\n"
   150 "        cpuid                       # and get the information         \n"
   151 "        movl    %%edx,%0                                              \n"
   152 "1:                                                                    \n"
   153 "        movl    %%edi,%%ebx\n"
   154 	: "=m" (features)
   155 	:
   156 	: "%eax", "%ebx", "%ecx", "%edx", "%edi"
   157 	);
   158 #elif defined(_MSC_VER)
   159 	__asm {
   160         mov     eax,80000000h       ; Query for extended functions
   161         cpuid                       ; Get extended function limit
   162         cmp     eax,80000001h
   163         jl      done                ; Nope, we dont have function 800000001h
   164         mov     eax,80000001h       ; Setup extended function 800000001h
   165         cpuid                       ; and get the information
   166         mov     features,edx
   167 done:
   168 	}
   169 #endif
   170 	return features;
   171 }
   172 
   173 static __inline__ int CPU_haveRDTSC()
   174 {
   175 	if ( CPU_haveCPUID() ) {
   176 		return (CPU_getCPUIDFeatures() & 0x00000010);
   177 	}
   178 	return 0;
   179 }
   180 
   181 static __inline__ int CPU_haveMMX()
   182 {
   183 	if ( CPU_haveCPUID() ) {
   184 		return (CPU_getCPUIDFeatures() & 0x00800000);
   185 	}
   186 	return 0;
   187 }
   188 
   189 static __inline__ int CPU_haveMMXExt()
   190 {
   191 	if ( CPU_haveCPUID() ) {
   192 		return (CPU_getCPUIDFeaturesExt() & 0x00400000);
   193 	}
   194 	return 0;
   195 }
   196 
   197 static __inline__ int CPU_have3DNow()
   198 {
   199 	if ( CPU_haveCPUID() ) {
   200 		return (CPU_getCPUIDFeaturesExt() & 0x80000000);
   201 	}
   202 	return 0;
   203 }
   204 
   205 static __inline__ int CPU_have3DNowExt()
   206 {
   207 	if ( CPU_haveCPUID() ) {
   208 		return (CPU_getCPUIDFeaturesExt() & 0x40000000);
   209 	}
   210 	return 0;
   211 }
   212 
   213 static __inline__ int CPU_haveSSE()
   214 {
   215 	if ( CPU_haveCPUID() ) {
   216 		return (CPU_getCPUIDFeatures() & 0x02000000);
   217 	}
   218 	return 0;
   219 }
   220 
   221 static __inline__ int CPU_haveSSE2()
   222 {
   223 	if ( CPU_haveCPUID() ) {
   224 		return (CPU_getCPUIDFeatures() & 0x04000000);
   225 	}
   226 	return 0;
   227 }
   228 
   229 static __inline__ int CPU_haveAltiVec()
   230 {
   231 	int altivec = 0;
   232 #ifdef MACOSX
   233 	int selectors[2] = { CTL_HW, HW_VECTORUNIT }; 
   234 	int hasVectorUnit = 0; 
   235 	size_t length = sizeof(hasVectorUnit); 
   236 	int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0); 
   237 	if( 0 == error )
   238 		altivec = (hasVectorUnit != 0); 
   239 #elif defined(USE_SETJMP) && defined(GCC_ALTIVEC)
   240 	void (*handler)(int sig);
   241 	handler = signal(SIGILL, illegal_instruction);
   242 	if ( setjmp(jmpbuf) == 0 ) {
   243 		asm volatile ("mtspr 256, %0\n\t"
   244 			      "vand %%v0, %%v0, %%v0"
   245 			      :
   246 			      : "r" (-1));
   247 		altivec = 1;
   248 	}
   249 	signal(SIGILL, handler);
   250 #endif
   251 	return altivec; 
   252 }
   253 
   254 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
   255 
   256 static Uint32 SDL_GetCPUFeatures()
   257 {
   258 	if ( SDL_CPUFeatures == 0xFFFFFFFF ) {
   259 		SDL_CPUFeatures = 0;
   260 		if ( CPU_haveRDTSC() ) {
   261 			SDL_CPUFeatures |= CPU_HAS_RDTSC;
   262 		}
   263 		if ( CPU_haveMMX() ) {
   264 			SDL_CPUFeatures |= CPU_HAS_MMX;
   265 		}
   266 		if ( CPU_haveMMXExt() ) {
   267 			SDL_CPUFeatures |= CPU_HAS_MMXEXT;
   268 		}
   269 		if ( CPU_have3DNow() ) {
   270 			SDL_CPUFeatures |= CPU_HAS_3DNOW;
   271 		}
   272 		if ( CPU_have3DNowExt() ) {
   273 			SDL_CPUFeatures |= CPU_HAS_3DNOWEXT;
   274 		}
   275 		if ( CPU_haveSSE() ) {
   276 			SDL_CPUFeatures |= CPU_HAS_SSE;
   277 		}
   278 		if ( CPU_haveSSE2() ) {
   279 			SDL_CPUFeatures |= CPU_HAS_SSE2;
   280 		}
   281 		if ( CPU_haveAltiVec() ) {
   282 			SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
   283 		}
   284 	}
   285 	return SDL_CPUFeatures;
   286 }
   287 
   288 SDL_bool SDL_HasRDTSC()
   289 {
   290 	if ( SDL_GetCPUFeatures() & CPU_HAS_RDTSC ) {
   291 		return SDL_TRUE;
   292 	}
   293 	return SDL_FALSE;
   294 }
   295 
   296 SDL_bool SDL_HasMMX()
   297 {
   298 	if ( SDL_GetCPUFeatures() & CPU_HAS_MMX ) {
   299 		return SDL_TRUE;
   300 	}
   301 	return SDL_FALSE;
   302 }
   303 
   304 SDL_bool SDL_Has3DNow()
   305 {
   306 	if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOW ) {
   307 		return SDL_TRUE;
   308 	}
   309 	return SDL_FALSE;
   310 }
   311 
   312 SDL_bool SDL_HasSSE()
   313 {
   314 	if ( SDL_GetCPUFeatures() & CPU_HAS_SSE ) {
   315 		return SDL_TRUE;
   316 	}
   317 	return SDL_FALSE;
   318 }
   319 
   320 SDL_bool SDL_HasAltiVec()
   321 {
   322 	if ( SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC ) {
   323 		return SDL_TRUE;
   324 	}
   325 	return SDL_FALSE;
   326 }
   327 
   328 SDL_bool SDL_HasMMXExt()
   329 {
   330    if ( SDL_GetCPUFeatures() & CPU_HAS_MMXEXT ) {
   331       return SDL_TRUE;
   332    }
   333    return SDL_FALSE;
   334 }
   335 
   336 SDL_bool SDL_Has3DNowExt()
   337 {
   338    if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOWEXT ) {
   339       return SDL_TRUE;
   340    }
   341    return SDL_FALSE;
   342 }
   343 
   344 SDL_bool SDL_HasSSE2()
   345 {
   346    if ( SDL_GetCPUFeatures() & CPU_HAS_SSE2 ) {
   347       return SDL_TRUE;
   348    }
   349    return SDL_FALSE;
   350 }
   351 
   352 #ifdef TEST_MAIN
   353 
   354 #include <stdio.h>
   355 
   356 int main()
   357 {
   358 	printf("RDTSC: %d\n", SDL_HasRDTSC());
   359 	printf("MMX: %d\n", SDL_HasMMX());
   360 	printf("MMXExt: %d\n", SDL_HasMMXExt());
   361 	printf("3DNow: %d\n", SDL_Has3DNow());
   362 	printf("3DNowExt: %d\n", SDL_Has3DNowExt());
   363 	printf("SSE: %d\n", SDL_HasSSE());
   364 	printf("SSE2: %d\n", SDL_HasSSE2());
   365 	printf("AltiVec: %d\n", SDL_HasAltiVec());
   366 	return 0;
   367 }
   368 
   369 #endif /* TEST_MAIN */