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