• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
49 static jmp_buf jmpbuf;
illegal_instruction(int sig)50 static void illegal_instruction(int sig)
51 {
52 	longjmp(jmpbuf, 1);
53 }
54 #endif /* HAVE_SETJMP */
55 
CPU_haveCPUID(void)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 
CPU_getCPUIDFeatures(void)146 static __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
201 done:
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 
226 static __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
278 done:
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 
303 static __inline__ int CPU_haveRDTSC(void)
304 {
305 	if ( CPU_haveCPUID() ) {
306 		return (CPU_getCPUIDFeatures() & 0x00000010);
307 	}
308 	return 0;
309 }
310 
311 static __inline__ int CPU_haveMMX(void)
312 {
313 	if ( CPU_haveCPUID() ) {
314 		return (CPU_getCPUIDFeatures() & 0x00800000);
315 	}
316 	return 0;
317 }
318 
319 static __inline__ int CPU_haveMMXExt(void)
320 {
321 	if ( CPU_haveCPUID() ) {
322 		return (CPU_getCPUIDFeaturesExt() & 0x00400000);
323 	}
324 	return 0;
325 }
326 
327 static __inline__ int CPU_have3DNow(void)
328 {
329 	if ( CPU_haveCPUID() ) {
330 		return (CPU_getCPUIDFeaturesExt() & 0x80000000);
331 	}
332 	return 0;
333 }
334 
335 static __inline__ int CPU_have3DNowExt(void)
336 {
337 	if ( CPU_haveCPUID() ) {
338 		return (CPU_getCPUIDFeaturesExt() & 0x40000000);
339 	}
340 	return 0;
341 }
342 
343 static __inline__ int CPU_haveSSE(void)
344 {
345 	if ( CPU_haveCPUID() ) {
346 		return (CPU_getCPUIDFeatures() & 0x02000000);
347 	}
348 	return 0;
349 }
350 
351 static __inline__ int CPU_haveSSE2(void)
352 {
353 	if ( CPU_haveCPUID() ) {
354 		return (CPU_getCPUIDFeatures() & 0x04000000);
355 	}
356 	return 0;
357 }
358 
359 static __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 
384 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
385 
386 static 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 
418 SDL_bool SDL_HasRDTSC(void)
419 {
420 	if ( SDL_GetCPUFeatures() & CPU_HAS_RDTSC ) {
421 		return SDL_TRUE;
422 	}
423 	return SDL_FALSE;
424 }
425 
426 SDL_bool SDL_HasMMX(void)
427 {
428 	if ( SDL_GetCPUFeatures() & CPU_HAS_MMX ) {
429 		return SDL_TRUE;
430 	}
431 	return SDL_FALSE;
432 }
433 
434 SDL_bool SDL_HasMMXExt(void)
435 {
436 	if ( SDL_GetCPUFeatures() & CPU_HAS_MMXEXT ) {
437 		return SDL_TRUE;
438 	}
439 	return SDL_FALSE;
440 }
441 
442 SDL_bool SDL_Has3DNow(void)
443 {
444 	if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOW ) {
445 		return SDL_TRUE;
446 	}
447 	return SDL_FALSE;
448 }
449 
450 SDL_bool SDL_Has3DNowExt(void)
451 {
452 	if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOWEXT ) {
453 		return SDL_TRUE;
454 	}
455 	return SDL_FALSE;
456 }
457 
458 SDL_bool SDL_HasSSE(void)
459 {
460 	if ( SDL_GetCPUFeatures() & CPU_HAS_SSE ) {
461 		return SDL_TRUE;
462 	}
463 	return SDL_FALSE;
464 }
465 
466 SDL_bool SDL_HasSSE2(void)
467 {
468 	if ( SDL_GetCPUFeatures() & CPU_HAS_SSE2 ) {
469 		return SDL_TRUE;
470 	}
471 	return SDL_FALSE;
472 }
473 
474 SDL_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 
486 int 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