michael@0: /******************************************************************** michael@0: * * michael@0: * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * michael@0: * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * michael@0: * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * michael@0: * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * michael@0: * * michael@0: * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * michael@0: * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * michael@0: * * michael@0: ******************************************************************** michael@0: michael@0: CPU capability detection for x86 processors. michael@0: Originally written by Rudolf Marek. michael@0: michael@0: function: michael@0: last mod: $Id: x86cpu.c 17410 2010-09-21 21:53:48Z tterribe $ michael@0: michael@0: ********************************************************************/ michael@0: michael@0: #include "x86cpu.h" michael@0: michael@0: #if !defined(OC_X86_ASM) michael@0: ogg_uint32_t oc_cpu_flags_get(void){ michael@0: return 0; michael@0: } michael@0: #else michael@0: # if defined(__amd64__)||defined(__x86_64__) michael@0: /*On x86-64, gcc seems to be able to figure out how to save %rbx for us when michael@0: compiling with -fPIC.*/ michael@0: # define cpuid(_op,_eax,_ebx,_ecx,_edx) \ michael@0: __asm__ __volatile__( \ michael@0: "cpuid\n\t" \ michael@0: :[eax]"=a"(_eax),[ebx]"=b"(_ebx),[ecx]"=c"(_ecx),[edx]"=d"(_edx) \ michael@0: :"a"(_op) \ michael@0: :"cc" \ michael@0: ) michael@0: # else michael@0: /*On x86-32, not so much.*/ michael@0: # define cpuid(_op,_eax,_ebx,_ecx,_edx) \ michael@0: __asm__ __volatile__( \ michael@0: "xchgl %%ebx,%[ebx]\n\t" \ michael@0: "cpuid\n\t" \ michael@0: "xchgl %%ebx,%[ebx]\n\t" \ michael@0: :[eax]"=a"(_eax),[ebx]"=r"(_ebx),[ecx]"=c"(_ecx),[edx]"=d"(_edx) \ michael@0: :"a"(_op) \ michael@0: :"cc" \ michael@0: ) michael@0: # endif michael@0: michael@0: static ogg_uint32_t oc_parse_intel_flags(ogg_uint32_t _edx,ogg_uint32_t _ecx){ michael@0: ogg_uint32_t flags; michael@0: /*If there isn't even MMX, give up.*/ michael@0: if(!(_edx&0x00800000))return 0; michael@0: flags=OC_CPU_X86_MMX; michael@0: if(_edx&0x02000000)flags|=OC_CPU_X86_MMXEXT|OC_CPU_X86_SSE; michael@0: if(_edx&0x04000000)flags|=OC_CPU_X86_SSE2; michael@0: if(_ecx&0x00000001)flags|=OC_CPU_X86_PNI; michael@0: if(_ecx&0x00000100)flags|=OC_CPU_X86_SSSE3; michael@0: if(_ecx&0x00080000)flags|=OC_CPU_X86_SSE4_1; michael@0: if(_ecx&0x00100000)flags|=OC_CPU_X86_SSE4_2; michael@0: return flags; michael@0: } michael@0: michael@0: static ogg_uint32_t oc_parse_amd_flags(ogg_uint32_t _edx,ogg_uint32_t _ecx){ michael@0: ogg_uint32_t flags; michael@0: /*If there isn't even MMX, give up.*/ michael@0: if(!(_edx&0x00800000))return 0; michael@0: flags=OC_CPU_X86_MMX; michael@0: if(_edx&0x00400000)flags|=OC_CPU_X86_MMXEXT; michael@0: if(_edx&0x80000000)flags|=OC_CPU_X86_3DNOW; michael@0: if(_edx&0x40000000)flags|=OC_CPU_X86_3DNOWEXT; michael@0: if(_ecx&0x00000040)flags|=OC_CPU_X86_SSE4A; michael@0: if(_ecx&0x00000800)flags|=OC_CPU_X86_SSE5; michael@0: return flags; michael@0: } michael@0: michael@0: ogg_uint32_t oc_cpu_flags_get(void){ michael@0: ogg_uint32_t flags; michael@0: ogg_uint32_t eax; michael@0: ogg_uint32_t ebx; michael@0: ogg_uint32_t ecx; michael@0: ogg_uint32_t edx; michael@0: # if !defined(__amd64__)&&!defined(__x86_64__) michael@0: /*Not all x86-32 chips support cpuid, so we have to check.*/ michael@0: __asm__ __volatile__( michael@0: "pushfl\n\t" michael@0: "pushfl\n\t" michael@0: "popl %[a]\n\t" michael@0: "movl %[a],%[b]\n\t" michael@0: "xorl $0x200000,%[a]\n\t" michael@0: "pushl %[a]\n\t" michael@0: "popfl\n\t" michael@0: "pushfl\n\t" michael@0: "popl %[a]\n\t" michael@0: "popfl\n\t" michael@0: :[a]"=r"(eax),[b]"=r"(ebx) michael@0: : michael@0: :"cc" michael@0: ); michael@0: /*No cpuid.*/ michael@0: if(eax==ebx)return 0; michael@0: # endif michael@0: cpuid(0,eax,ebx,ecx,edx); michael@0: /* l e t n I e n i u n e G*/ michael@0: if(ecx==0x6C65746E&&edx==0x49656E69&&ebx==0x756E6547|| michael@0: /* 6 8 x M T e n i u n e G*/ michael@0: ecx==0x3638784D&&edx==0x54656E69&&ebx==0x756E6547){ michael@0: int family; michael@0: int model; michael@0: /*Intel, Transmeta (tested with Crusoe TM5800):*/ michael@0: cpuid(1,eax,ebx,ecx,edx); michael@0: flags=oc_parse_intel_flags(edx,ecx); michael@0: family=(eax>>8)&0xF; michael@0: model=(eax>>4)&0xF; michael@0: /*The SSE unit on the Pentium M and Core Duo is much slower than the MMX michael@0: unit, so don't use it.*/ michael@0: if(family==6&&(model==9||model==13||model==14)){ michael@0: flags&=~(OC_CPU_X86_SSE2|OC_CPU_X86_PNI); michael@0: } michael@0: } michael@0: /* D M A c i t n e h t u A*/ michael@0: else if(ecx==0x444D4163&&edx==0x69746E65&&ebx==0x68747541|| michael@0: /* C S N y b e d o e G*/ michael@0: ecx==0x43534e20&&edx==0x79622065&&ebx==0x646f6547){ michael@0: /*AMD, Geode:*/ michael@0: cpuid(0x80000000,eax,ebx,ecx,edx); michael@0: if(eax<0x80000001)flags=0; michael@0: else{ michael@0: cpuid(0x80000001,eax,ebx,ecx,edx); michael@0: flags=oc_parse_amd_flags(edx,ecx); michael@0: } michael@0: /*Also check for SSE.*/ michael@0: cpuid(1,eax,ebx,ecx,edx); michael@0: flags|=oc_parse_intel_flags(edx,ecx); michael@0: } michael@0: /*Technically some VIA chips can be configured in the BIOS to return any michael@0: string here the user wants. michael@0: There is a special detection method that can be used to identify such michael@0: processors, but in my opinion, if the user really wants to change it, they michael@0: deserve what they get.*/ michael@0: /* s l u a H r u a t n e C*/ michael@0: else if(ecx==0x736C7561&&edx==0x48727561&&ebx==0x746E6543){ michael@0: /*VIA:*/ michael@0: /*I only have documentation for the C7 (Esther) and Isaiah (forthcoming) michael@0: chips (thanks to the engineers from Centaur Technology who provided it). michael@0: These chips support Intel-like cpuid info. michael@0: The C3-2 (Nehemiah) cores appear to, as well.*/ michael@0: cpuid(1,eax,ebx,ecx,edx); michael@0: flags=oc_parse_intel_flags(edx,ecx); michael@0: if(eax>=0x80000001){ michael@0: /*The (non-Nehemiah) C3 processors support AMD-like cpuid info. michael@0: We need to check this even if the Intel test succeeds to pick up 3DNow! michael@0: support on these processors. michael@0: Unlike actual AMD processors, we cannot _rely_ on this info, since michael@0: some cores (e.g., the 693 stepping of the Nehemiah) claim to support michael@0: this function, yet return edx=0, despite the Intel test indicating michael@0: MMX support. michael@0: Therefore the features detected here are strictly added to those michael@0: detected by the Intel test.*/ michael@0: /*TODO: How about earlier chips?*/ michael@0: cpuid(0x80000001,eax,ebx,ecx,edx); michael@0: /*Note: As of the C7, this function returns Intel-style extended feature michael@0: flags, not AMD-style. michael@0: Currently, this only defines bits 11, 20, and 29 (0x20100800), which michael@0: do not conflict with any of the AMD flags we inspect. michael@0: For the remaining bits, Intel tells us, "Do not count on their value", michael@0: but VIA assures us that they will all be zero (at least on the C7 and michael@0: Isaiah chips). michael@0: In the (unlikely) event a future processor uses bits 18, 19, 30, or 31 michael@0: (0xC0C00000) for something else, we will have to add code to detect michael@0: the model to decide when it is appropriate to inspect them.*/ michael@0: flags|=oc_parse_amd_flags(edx,ecx); michael@0: } michael@0: } michael@0: else{ michael@0: /*Implement me.*/ michael@0: flags=0; michael@0: } michael@0: return flags; michael@0: } michael@0: #endif