
/*
 *             Automatically Tuned Linear Algebra Software v3.7.11
 *                      (C) Copyright 2005 Dean Gaudet
 *
 * Code contributers : Dean Gaudet, R. Clint Whaley
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions, and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *   3. The name of the ATLAS group or the names of its contributers may
 *      not be used to endorse or promote products derived from this
 *      software without specific written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ATLAS GROUP OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
/*
 * This code adapted for ATLAS use by R. Clint Whaley based on code and info
 * submitted by Dean Gaudet
 */
/* see <http://www.sandpile.org/ia32/cpuid.htm> for example */

#include <stdint.h>
#include <stdio.h>
#include <string.h>


/* verbatim code from Dean */
/* cpuid with abcd string order */
static void do_cpuid(uint32_t *dest, uint32_t level)
{
#ifdef WIN32
        _asm {
                mov eax, level
                cpuid
                mov edi, dest
                mov [edi], eax
                mov [edi + 4], ebx
                mov [edi + 8], ecx
                mov [edi + 12], edx
        }
#elif defined(__GNUC__) || defined(__ICC)
     uint32_t *dst = dest;
     __asm("cpuid": "=a" (dst[0]), "=b" (dst[1]), "=c" (dst[2]), "=d" (dst[3])
            : "a" (level));
#else
#error "lacking asm cpuid support for your compiler"
#endif
}

/* My driver, based on his */
int ProbeArch(char *vendor, unsigned *family, unsigned *model, int *x86_64)
/*
 * Returns 0 on success, non-zero on error
 */
{
   uint32_t r[4];
   uint32_t max_level;
   uint32_t *vp = (uint32_t*) vendor;

   *x86_64 = 0;
   do_cpuid(r, 0);
   max_level = r[0];
   if (!max_level)
      return(1);
   *vp = r[1];
   vp[1] = r[3];
   vp[2] = r[2];
   vendor[12] = '\0';
   do_cpuid(r, 1);
   *family = (r[0] >> 8) & 0xf;
   if (*family == 0xf) /* extended family is added in */
       *family += ((r[0] >> 20) & 0xff);

   *model = (r[0] >> 4) & 0xf;
   if (*model == 0xf)/* extended model is concatenated ... no idea why */
      *model |= ((r[0] >> 12) & 0xf0);
   do_cpuid(r, 0x80000000);
   if (r[0] >= 0x80000001)
   {
      do_cpuid(r, 0x80000001);
      *x86_64 = (r[3] & (1<<29)) != 0;
   }
   return(0);
}

void PrintUsage(char *name)
{
   fprintf(stderr, "USAGE: %s -c (cpu) -b (@ bits)\n", name);
   exit(1);
}

enum WHATPROBE{Pcpu=1, P64=2};
int GetFlags(int nargs, char **args)
{
   int i, flag = 0;
   for (i=1; i < nargs; i++)
   {
      if (args[i][0] != '-') PrintUsage(args[0]);
      switch(args[i][1])
      {
      case 'c':
         flag |= Pcpu;
         break;
      case 'b':
         flag |= P64;
         break;
      default:
         PrintUsage(args[0]);
      }
   }
   if (!flag)
     flag = Pcpu | P64;
   return(flag);
}

main(int nargs, char **args)
{
   int iret, x86_64, flags;
   unsigned family, model;
   char *cpu="UNKNOWN", vendor[13];

   flags = GetFlags(nargs, args);
   cpu = NULL;
   vendor[0] = '\0';
   iret = ProbeArch(vendor, &family, &model, &x86_64);
   if (!iret)
   {
      switch(family)
      {
      case 15:  /* P4 or Opteron */
         if (strstr(vendor, "AuthenticAMD"))
         {
            if (model == 5) cpu = "Hammer";
            else iret = 10;
         }
         else if (strstr(vendor, "GenuineIntel"))
         {
            if (model == 0 || model == 2) cpu = "Pentium 4";
            else if (model == 3)
            {
               if (x86_64) cpu = "Pentium 4E64";
               else cpu = "Pentium 4E";
            }
            else iret = 11;
         }
         else if (strstr(vendor, "GenuineTMx86"))
         {
            if (model == 2) cpu = "Efficeon";
            else iret = 12;
         }
         else iret = 13;
         break;
      case 6:   /* P3,p2,ppro, athlon */
         if (strstr(vendor, "AuthenticAMD"))
         {
            if (model >= 4 && model <= 6) cpu = "Athlon";
            else iret = 20;
         }
         else if (strstr(vendor, "GenuineIntel"))
         {
            if (model == 5) cpu = "Pentium 2";
            else if (model == 7 || model == 8) cpu = "Pentium 3";
            else if (model == 8) cpu = "Pentium M";
            else iret = 21;
         }
         else iret = 22;
         break;
      case 5:
         if (strstr(vendor, "GenuineTMx86"))
         {
            if (model == 4)
               cpu = "Crusoe";
            else iret = 30;
         }
         else iret = 31;
         break;
      default:
         iret = 100;
      }
   }
   if (iret)
      printf("ERROR %d: vendor='%s', family=%d, model=%d, x86_64=%d\n",
             iret, vendor, family, model, x86_64);
   else
   {
      if (flags == Pcpu)
         printf("cpu: %s\n", cpu);
      else if (flags == P64)
         printf("Pointer width: %d\n", x86_64 ? 64 : 32);
      else
         printf("family=%d, model=%d, cpu='%s', Ptr bits=%d\n",
                family, model, cpu, x86_64?64:32);
   }
   exit(iret);
}
