src/cpuinfo/SDL_cpuinfo.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 06 Jan 2004 17:18:38 +0000
changeset 778 8ac3f46f9d09
parent 769 b8d311d90021
child 784 a2dde6aff60e
permissions -rw-r--r--
Date: Tue, 6 Jan 2004 12:42:19 +0100
From: Max Horn
Subject: SDL_HasAltiVec; BUGS file

the attached patch adds SDL_HasAltiVec to SDL CVS. Note that at this
point, this only works on MacOSX (and maybe darwin). I don't know how
to properly add a test for e.g. Linux/PPC at this point. I found an
email which might help in doing so:
http://zebra.fh-weingarten.de/~maxi/html/mplayer-dev-eng/2003-01msg00783.html
However, since I have no way to test on a non-OSX PowerPC system, I am
not comfortable blindly adding such code... I just hope that if
somebody from the Linux/PPC (or FreeBSD/PPC, or whatever) community
notices this, they'll jump up and provide a patch for us ;-)
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2004 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 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     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 #ifdef SAVE_RCSID
    24 static char rcsid =
    25  "@(#) $Id$";
    26 #endif
    27 
    28 /* CPU feature detection for SDL */
    29 
    30 #include "SDL.h"
    31 #include "SDL_cpuinfo.h"
    32 
    33 #ifdef MACOSX
    34 #include <sys/sysctl.h> /* For AltiVec check */
    35 #endif
    36 
    37 #define CPU_HAS_RDTSC	0x00000001
    38 #define CPU_HAS_MMX	0x00000002
    39 #define CPU_HAS_3DNOW	0x00000004
    40 #define CPU_HAS_SSE	0x00000008
    41 #define CPU_HAS_ALTIVEC	0x00000010
    42 
    43 static __inline__ int CPU_haveCPUID()
    44 {
    45 	int has_CPUID = 0;
    46 #if defined(__GNUC__) && defined(i386)
    47 	__asm__ (
    48 "push %%ecx\n"
    49 "        pushfl                      # Get original EFLAGS             \n"
    50 "        popl    %%eax                                                 \n"
    51 "        movl    %%eax,%%ecx                                           \n"
    52 "        xorl    $0x200000,%%eax     # Flip ID bit in EFLAGS           \n"
    53 "        pushl   %%eax               # Save new EFLAGS value on stack  \n"
    54 "        popfl                       # Replace current EFLAGS value    \n"
    55 "        pushfl                      # Get new EFLAGS                  \n"
    56 "        popl    %%eax               # Store new EFLAGS in EAX         \n"
    57 "        xorl    %%ecx,%%eax         # Can not toggle ID bit,          \n"
    58 "        jz      1f                  # Processor=80486                 \n"
    59 "        movl    $1,%0               # We have CPUID support           \n"
    60 "1:                                                                    \n"
    61 "pop %%ecx\n"
    62 	: "=r" (has_CPUID)
    63 	:
    64 	: "%eax", "%ecx"
    65 	);
    66 #elif defined(_MSC_VER)
    67 	__asm {
    68         pushfd                      ; Get original EFLAGS
    69         pop     eax
    70         mov     ecx, eax
    71         xor     eax, 200000h        ; Flip ID bit in EFLAGS
    72         push    eax                 ; Save new EFLAGS value on stack
    73         popfd                       ; Replace current EFLAGS value
    74         pushfd                      ; Get new EFLAGS
    75         pop     eax                 ; Store new EFLAGS in EAX
    76         xor     eax, ecx            ; Can not toggle ID bit,
    77         jz      done                ; Processor=80486
    78         mov     has_CPUID,1         ; We have CPUID support
    79 done:
    80 	}
    81 #endif
    82 	return has_CPUID;
    83 }
    84 
    85 static __inline__ int CPU_getCPUIDFeatures()
    86 {
    87 	int features = 0;
    88 #if defined(__GNUC__) && defined(i386)
    89 	__asm__ (
    90 "push %%ebx\n"
    91 "push %%ecx\n"
    92 "push %%edx\n"
    93 "        xorl    %%eax,%%eax         # Set up for CPUID instruction    \n"
    94 "        cpuid                       # Get and save vendor ID          \n"
    95 "        cmpl    $1,%%eax            # Make sure 1 is valid input for CPUID\n"
    96 "        jl      1f                  # We dont have the CPUID instruction\n"
    97 "        xorl    %%eax,%%eax                                           \n"
    98 "        incl    %%eax                                                 \n"
    99 "        cpuid                       # Get family/model/stepping/features\n"
   100 "        movl    %%edx,%0                                              \n"
   101 "1:                                                                    \n"
   102 "pop %%edx\n"
   103 "pop %%ecx\n"
   104 "pop %%ebx\n"
   105 	: "=r" (features)
   106 	:
   107 	: "%eax", "%ebx", "%ecx", "%edx"
   108 	);
   109 #elif defined(_MSC_VER)
   110 	__asm {
   111         xor     eax, eax            ; Set up for CPUID instruction
   112         cpuid                       ; Get and save vendor ID
   113         cmp     eax, 1              ; Make sure 1 is valid input for CPUID
   114         jl      done                ; We dont have the CPUID instruction
   115         xor     eax, eax
   116         inc     eax
   117         cpuid                       ; Get family/model/stepping/features
   118         mov     features, edx
   119 done:
   120 	}
   121 #endif
   122 	return features;
   123 }
   124 
   125 static __inline__ int CPU_haveRDTSC()
   126 {
   127 	if ( CPU_haveCPUID() ) {
   128 		return (CPU_getCPUIDFeatures() & 0x00000010);
   129 	}
   130 	return 0;
   131 }
   132 
   133 static __inline__ int CPU_haveMMX()
   134 {
   135 	if ( CPU_haveCPUID() ) {
   136 		return (CPU_getCPUIDFeatures() & 0x00800000);
   137 	}
   138 	return 0;
   139 }
   140 
   141 static __inline__ int CPU_have3DNow()
   142 {
   143 	int has_3DNow = 0;
   144 	if ( !CPU_haveCPUID() ) {
   145 		return 0;
   146 	}
   147 #if defined(__GNUC__) && defined(i386)
   148 	__asm__ (
   149 "push %%ebx\n"
   150 "push %%ecx\n"
   151 "push %%edx\n"
   152 "        movl    $0x80000000,%%eax   # Query for extended functions    \n"
   153 "        cpuid                       # Get extended function limit     \n"
   154 "        cmpl    $0x80000001,%%eax                                     \n"
   155 "        jbe     1f                  # Nope, we dont have function 800000001h\n"
   156 "        movl    $0x80000001,%%eax   # Setup extended function 800000001h\n"
   157 "        cpuid                       # and get the information         \n"
   158 "        testl   $0x80000000,%%edx   # Bit 31 is set if 3DNow! present \n"
   159 "        jz      1f                  # Nope, we dont have 3DNow support\n"
   160 "        movl    $1,%0               # Yep, we have 3DNow! support!    \n"
   161 "1:                                                                    \n"
   162 "pop %%edx\n"
   163 "pop %%ecx\n"
   164 "pop %%ebx\n"
   165 	: "=r" (has_3DNow)
   166 	:
   167 	: "%eax", "%ebx", "%ecx", "%edx"
   168 	);
   169 #elif defined(_MSC_VER)
   170 	__asm {
   171         mov     eax,80000000h       ; Query for extended functions
   172         cpuid                       ; Get extended function limit
   173         cmp     eax,80000001h
   174         jbe     done                ; Nope, we dont have function 800000001h
   175         mov     eax,80000001h       ; Setup extended function 800000001h
   176         cpuid                       ; and get the information
   177         test    edx,80000000h       ; Bit 31 is set if 3DNow! present
   178         jz      done                ; Nope, we dont have 3DNow support
   179         mov     has_3DNow,1         ; Yep, we have 3DNow! support!
   180 done:
   181 	}
   182 #endif
   183 	return has_3DNow;
   184 }
   185 
   186 static __inline__ int CPU_haveSSE()
   187 {
   188 	if ( CPU_haveCPUID() ) {
   189 		return (CPU_getCPUIDFeatures() & 0x02000000);
   190 	}
   191 	return 0;
   192 }
   193 
   194 static __inline__ int CPU_haveAltiVec()
   195 {
   196 #ifdef MACOSX
   197 	/* TODO: This check works on OS X. It would be nice to detect AltiVec
   198 	   properly on for example Linux/PPC, too. But I don't know how that
   199 	   is done in Linux (or FreeBSD, or whatever other OS you run PPC :-)
   200 	 */
   201 	int selectors[2] = { CTL_HW, HW_VECTORUNIT }; 
   202 	int hasVectorUnit = 0; 
   203 	size_t length = sizeof(hasVectorUnit); 
   204 	int error = sysctl(selectors, 2, &hasVectorUnit, &length, NULL, 0); 
   205 	if( 0 == error )
   206 		return hasVectorUnit != 0; 
   207 #endif
   208 	return 0; 
   209 }
   210 
   211 static Uint32 SDL_CPUFeatures = 0xFFFFFFFF;
   212 
   213 static Uint32 SDL_GetCPUFeatures()
   214 {
   215 	if ( SDL_CPUFeatures == 0xFFFFFFFF ) {
   216 		SDL_CPUFeatures = 0;
   217 		if ( CPU_haveRDTSC() ) {
   218 			SDL_CPUFeatures |= CPU_HAS_RDTSC;
   219 		}
   220 		if ( CPU_haveMMX() ) {
   221 			SDL_CPUFeatures |= CPU_HAS_MMX;
   222 		}
   223 		if ( CPU_have3DNow() ) {
   224 			SDL_CPUFeatures |= CPU_HAS_3DNOW;
   225 		}
   226 		if ( CPU_haveSSE() ) {
   227 			SDL_CPUFeatures |= CPU_HAS_SSE;
   228 		}
   229 		if ( CPU_haveAltiVec() ) {
   230 			SDL_CPUFeatures |= CPU_HAS_ALTIVEC;
   231 		}
   232 	}
   233 	return SDL_CPUFeatures;
   234 }
   235 
   236 SDL_bool SDL_HasRDTSC()
   237 {
   238 	if ( SDL_GetCPUFeatures() & CPU_HAS_RDTSC ) {
   239 		return SDL_TRUE;
   240 	}
   241 	return SDL_FALSE;
   242 }
   243 
   244 SDL_bool SDL_HasMMX()
   245 {
   246 	if ( SDL_GetCPUFeatures() & CPU_HAS_MMX ) {
   247 		return SDL_TRUE;
   248 	}
   249 	return SDL_FALSE;
   250 }
   251 
   252 SDL_bool SDL_Has3DNow()
   253 {
   254 	if ( SDL_GetCPUFeatures() & CPU_HAS_3DNOW ) {
   255 		return SDL_TRUE;
   256 	}
   257 	return SDL_FALSE;
   258 }
   259 
   260 SDL_bool SDL_HasSSE()
   261 {
   262 	if ( SDL_GetCPUFeatures() & CPU_HAS_SSE ) {
   263 		return SDL_TRUE;
   264 	}
   265 	return SDL_FALSE;
   266 }
   267 
   268 SDL_bool SDL_HasAltiVec()
   269 {
   270 	if ( SDL_GetCPUFeatures() & CPU_HAS_ALTIVEC ) {
   271 		return SDL_TRUE;
   272 	}
   273 	return SDL_FALSE;
   274 }
   275 
   276 #ifdef TEST_MAIN
   277 
   278 #include <stdio.h>
   279 
   280 int main()
   281 {
   282 	printf("RDTSC: %d\n", SDL_HasRDTSC());
   283 	printf("MMX: %d\n", SDL_HasMMX());
   284 	printf("3DNow: %d\n", SDL_Has3DNow());
   285 	printf("SSE: %d\n", SDL_HasSSE());
   286 	printf("AltiVec: %d\n", SDL_HasAltiVec());
   287 	return 0;
   288 }
   289 
   290 #endif /* TEST_MAIN */