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