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