text: /* CpuArch.c -- CPU specific code
  2018-02-18: Igor Pavlov : Public domain */
  
  #include "Precomp.h"
  
  #include "CpuArch.h"
  
  #ifdef MY_CPU_X86_OR_AMD64
  
  #if (defined(_MSC_VER) && !defined(MY_CPU_AMD64)) || defined(__GNUC__)
  #define USE_ASM
  #endif
  
  #if !defined(USE_ASM) && _MSC_VER >= 1500
  #include <intrin.h>
  #endif
  
  #if defined(USE_ASM) && !defined(MY_CPU_AMD64)
  static UInt32 CheckFlag(UInt32 flag)
  {
    #ifdef _MSC_VER
    __asm pushfd;
    __asm pop EAX;
    __asm mov EDX, EAX;
    __asm xor EAX, flag;
    __asm push EAX;
    __asm popfd;
    __asm pushfd;
    __asm pop EAX;
    __asm xor EAX, EDX;
    __asm push EDX;
    __asm popfd;
    __asm and flag, EAX;
    #else
    __asm__ __volatile__ (
      "pushf\n\t"
      "pop  %%EAX\n\t"
      "movl %%EAX,%%EDX\n\t"
      "xorl %0,%%EAX\n\t"
      "push %%EAX\n\t"
      "popf\n\t"
      "pushf\n\t"
      "pop  %%EAX\n\t"
      "xorl %%EDX,%%EAX\n\t"
      "push %%EDX\n\t"
      "popf\n\t"
      "andl %%EAX, %0\n\t":
      "=c" (flag) : "c" (flag) :
      "%eax", "%edx");
    #endif
    return flag;
  }
  #define CHECK_CPUID_IS_SUPPORTED if (CheckFlag(1 << 18) == 0 || CheckFlag(1 << 21) == 0) return False;
  #else
  #define CHECK_CPUID_IS_SUPPORTED
  #endif
  
  void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d)
  {
    #ifdef USE_ASM
  
    #ifdef _MSC_VER
  
    UInt32 a2, b2, c2, d2;
    __asm xor EBX, EBX;
    __asm xor ECX, ECX;
    __asm xor EDX, EDX;
    __asm mov EAX, function;
    __asm cpuid;
    __asm mov a2, EAX;
    __asm mov b2, EBX;
    __asm mov c2, ECX;
    __asm mov d2, EDX;
  
    *a = a2;
    *b = b2;
    *c = c2;
    *d = d2;
  
    #else
  
    __asm__ __volatile__ (
    #if defined(MY_CPU_AMD64) && defined(__PIC__)
      "mov %%rbx, %%rdi;"
      "cpuid;"
      "xchg %%rbx, %%rdi;"
      : "=a" (*a) ,
        "=D" (*b) ,
    #elif defined(MY_CPU_X86) && defined(__PIC__)
      "mov %%ebx, %%edi;"
      "cpuid;"
      "xchgl %%ebx, %%edi;"
      : "=a" (*a) ,
        "=D" (*b) ,
    #else
      "cpuid"
      : "=a" (*a) ,
        "=b" (*b) ,
    #endif
        "=c" (*c) ,
        "=d" (*d)
      : "0" (function)) ;
  
    #endif
    
    #else
  
    int CPUInfo[4];
    __cpuid(CPUInfo, function);
    *a = CPUInfo[0];
    *b = CPUInfo[1];
    *c = CPUInfo[2];
    *d = CPUInfo[3];
  
    #endif
  }
  
  BoolInt x86cpuid_CheckAndRead(Cx86cpuid *p)
  {
    CHECK_CPUID_IS_SUPPORTED
    MyCPUID(0, &p->maxFunc, &p->vendor[0], &p->vendor[2], &p->vendor[1]);
    MyCPUID(1, &p->ver, &p->b, &p->c, &p->d);
    return True;
  }
  
  static const UInt32 kVendors[][3] =
  {
    { 0x756E6547, 0x49656E69, 0x6C65746E},
    { 0x68747541, 0x69746E65, 0x444D4163},
    { 0x746E6543, 0x48727561, 0x736C7561}
  };
  
  int x86cpuid_GetFirm(const Cx86cpuid *p)
  {
    unsigned i;
    for (i = 0; i < sizeof(kVendors) / sizeof(kVendors[i]); i++)
    {
      const UInt32 *v = kVendors[i];
      if (v[0] == p->vendor[0] &&
          v[1] == p->vendor[1] &&
          v[2] == p->vendor[2])
        return (int)i;
    }
    return -1;
  }
  
  BoolInt CPU_Is_InOrder(void)
  {
    Cx86cpuid p;
    int firm;
    UInt32 family, model;
    if (!x86cpuid_CheckAndRead(&p))
      return True;
  
    family = x86cpuid_GetFamily(p.ver);
    model = x86cpuid_GetModel(p.ver);
    
    firm = x86cpuid_GetFirm(&p);
  
    switch (firm)
    {
      case CPU_FIRM_INTEL: return (family < 6 || (family == 6 && (
          /* In-Order Atom CPU */
             model == 0x1C  /* 45 nm, N4xx, D4xx, N5xx, D5xx, 230, 330 */
          || model == 0x26  /* 45 nm, Z6xx */
          || model == 0x27  /* 32 nm, Z2460 */
          || model == 0x35  /* 32 nm, Z2760 */
          || model == 0x36  /* 32 nm, N2xxx, D2xxx */
          )));
      case CPU_FIRM_AMD: return (family < 5 || (family == 5 && (model < 6 || model == 0xA)));
      case CPU_FIRM_VIA: return (family < 6 || (family == 6 && model < 0xF));
    }
    return True;
  }
  
  #if !defined(MY_CPU_AMD64) && defined(_WIN32)
  #include <windows.h>
  static BoolInt CPU_Sys_Is_SSE_Supported()
  {
    OSVERSIONINFO vi;
    vi.dwOSVersionInfoSize = sizeof(vi);
    if (!GetVersionEx(&vi))
      return False;
    return (vi.dwMajorVersion >= 5);
  }
  #define CHECK_SYS_SSE_SUPPORT if (!CPU_Sys_Is_SSE_Supported()) return False;
  #else
  #define CHECK_SYS_SSE_SUPPORT
  #endif
  
  BoolInt CPU_Is_Aes_Supported(void)
  {
    Cx86cpuid p;
    CHECK_SYS_SSE_SUPPORT
    if (!x86cpuid_CheckAndRead(&p))
      return False;
    return (p.c >> 25) & 1;
  }
  
  BoolInt CPU_IsSupported_PageGB(void)
  {
    Cx86cpuid cpuid;
    if (!x86cpuid_CheckAndRead(&cpuid))
      return False;
    {
      UInt32 d[4] = { 0 };
      MyCPUID(0x80000000, &d[0], &d[1], &d[2], &d[3]);
      if (d[0] < 0x80000001)
        return False;
    }
    {
      UInt32 d[4] = { 0 };
      MyCPUID(0x80000001, &d[0], &d[1], &d[2], &d[3]);
      return (d[3] >> 26) & 1;
    }
  }
  
  #endif
  