e14743d1 |
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 */ |