/*----------------------------------------------------------------------------*/

/*
 * Performance-Monitoring Counters Library, for Intel/AMD Processors and Linux
 * Author:  Don Heller, dheller@scl.ameslab.gov
 * Last revised:  1 June 2001
 */

/*----------------------------------------------------------------------------*/

/*
 * Inspect the results of the cpuid instruction.
 *   Identical output lines are suppressed.
 *   The last line of each range is printed to verify proper termination.
 * Compile with gcc -O -o cpuid cpuid.c
 * Run with     date ; cpuid ; date
 *
 * Pentium, 66 MHz, about 39 minutes
 *   input    eax      ebx      ecx      edx
 *          0 00000001 756e6547 6c65746e 49656e69 GenuineIntel
 *          1 00000515 00000000 00000000 000001bf
 *          2 00000000 00000000 00000000 00000000
 *   80000000 00000000 00000000 00000000 00000000
 *   ffffffff 00000000 00000000 00000000 00000000
 *
 * Pentium Pro, 200 MHz, about 30 minutes
 *   input    eax      ebx      ecx      edx
 *          0 00000002 756e6547 6c65746e 49656e69 GenuineIntel
 *          1 00000617 00000000 00000000 0000f9ff
 *          2 03020101 00000000 00000000 06040a42
 *   80000000 03020101 00000000 00000000 06040a42
 *   ffffffff 03020101 00000000 00000000 06040a42
 *
 * Pentium Pro, 200 MHz, about 30 minutes
 *   input    eax      ebx      ecx      edx
 *          0 00000002 756e6547 6c65746e 49656e69 GenuineIntel
 *          1 00000619 00000000 00000000 0000fbff
 *          2 03020101 00000000 00000000 06040a42
 *   80000000 03020101 00000000 00000000 06040a42
 *   ffffffff 03020101 00000000 00000000 06040a42
 *
 * Pentium II, model 5, 400 MHz, about 18 minutes
 *   input    eax      ebx      ecx      edx
 *          0 00000002 756e6547 6c65746e 49656e69 GenuineIntel
 *          1 00000651 00000000 00000000 0183fbff
 *          2 03020101 00000000 00000000 0c040843
 *   80000000 03020101 00000000 00000000 0c040843
 *   ffffffff 03020101 00000000 00000000 0c040843
 *
 * Pentium II, model 5, 450 MHz, about 18 minutes
 *   input    eax      ebx      ecx      edx
 *          0 00000002 756e6547 6c65746e 49656e69 GenuineIntel
 *          1 00000652 00000000 00000000 0183fbff
 *          2 03020101 00000000 00000000 0c040843
 *   80000000 03020101 00000000 00000000 0c040843
 *   ffffffff 03020101 00000000 00000000 0c040843
 *
 * Pentium III, model 7, 450 MHz, about 23 minutes
 *   input    eax      ebx      ecx      edx
 *          0 00000002 756e6547 6c65746e 49656e69 GenuineIntel
 *          1 00000672 00000000 00000000 0383fbff
 *          2 03020101 00000000 00000000 0c040843
 *   80000000 03020101 00000000 00000000 0c040843
 *   ffffffff 03020101 00000000 00000000 0c040843
 *
 * Pentium III, model 7, 600 MHz, about 18 minutes
 *   input    eax      ebx      ecx      edx
 *          0 00000002 756e6547 6c65746e 49656e69 GenuineIntel
 *          1 00000673 00000000 00000000 0383f9ff
 *          2 03020101 00000000 00000000 0c040843
 *   80000000 03020101 00000000 00000000 0c040843
 *   ffffffff 03020101 00000000 00000000 0c040843
 *
 * Pentium III, model 8, 800 MHz, about 14 minutes
 *   input    eax      ebx      ecx      edx
 *          0 00000002 756e6547 6c65746e 49656e69 GenuineIntel
 *          1 00000683 00000002 00000000 0383f9ff
 *          2 03020101 00000000 00000000 0c040882
 *   80000000 03020101 00000000 00000000 0c040882
 *   ffffffff 03020101 00000000 00000000 0c040882
 *
 * Pentium 4, 1500 MHz, about 30 minutes
 *   input    eax      ebx      ecx      edx
 *          0 00000002 756e6547 6c65746e 49656e69 GenuineIntel
 *          1 00000f07 00010808 00000000 3febf9ff
 *          2 665b5001 00000000 00000000 007a7040
 *   80000000 80000004 00000000 00000000 00000000
 *   80000001 00000000 00000000 00000000 00000000
 *   80000002 20202020 20202020 20202020 6e492020               Intel(R) Pentium(R) 4 CPU 1500MHz
 *   80000003 286c6574 50202952 69746e65 52286d75
 *   80000004 20342029 20555043 30303531 007a484d
 *   80000005 665b5001 00000000 00000000 007a7040
 *   ffffffff 665b5001 00000000 00000000 007a7040
 *
 * AMD Athlon, model 2, 800 MHz, about 6 minutes
 *   input    eax      ebx      ecx      edx
 *          0 00000001 68747541 444d4163 69746e65 AuthenticAMD
 *          1 00000621 00000000 00000000 0183f9ff
 *          2 00000000 00000000 00000000 00000000
 *   80000000 80000006 68747541 444d4163 69746e65 AuthenticAMD
 *   80000001 00000721 00000000 00000000 c1c3f9ff
 *   80000002 20444d41 6c687441 74286e6f 5020296d AMD Athlon(tm) Processor
 *   80000003 65636f72 726f7373 00000000 00000000
 *   80000004 00000000 00000000 00000000 00000000
 *   80000005 0408ff08 ff18ff10 40020140 40020140
 *   80000006 00000000 41004100 02002140 00000000
 *   80000007 00000000 00000000 00000000 00000000
 *   ffffffff 00000000 00000000 00000000 00000000
 */

/*----------------------------------------------------------------------------*/

#include <stdio.h>

/*----------------------------------------------------------------------------*/

typedef unsigned long int ulong;

#define standard 0UL		/* start of standard cpuid range     */
#define extended 0x80000000UL	/* start of extended cpuid range     */
#define name     0x80000002UL	/* start of name string if available */
#define final    0xffffffffUL	/* end of extended cpuid range       */

/*----------------------------------------------------------------------------*/

/* note the side-effect of cpuid() writing to a,b,c,d */

#define cpuid(n) \
  a = b = c = d = 0xbabef00d; \
  asm("cpuid" : "=eax" (a), "=ebx" (b), "=ecx" (c), "=edx" (d) : "0" (n))

#define read_cpuid(n,w,x,y,z) \
  asm("cpuid" : "=eax" (w), "=ebx" (x), "=ecx" (y), "=edx" (z) : "0" (n))

/*----------------------------------------------------------------------------*/

int main(int argc, char ** argv)
{
  ulong a, b, c, d;
  ulong A, B, C, D, i, level, extended_level;
  ulong v[13];	/* 48 character string */

  printf("input    eax      ebx      ecx      edx\n");

  /* standard levels */
  cpuid(0);

  level = a;
  v[0] = b;	/* vendor ID, 12 characters */
  v[1] = d;
  v[2] = c;
  v[3] = 0;	/* terminate the string */

  printf("%8lx %08lx %08lx %08lx %08lx %s\n", 0UL, a, b, c, d, (char *) v);

  for (i = 1; i < extended; i++) {
    A = a; B = b; C = c; D = d;
    cpuid(i);
    if (A != a || B != b || C != c || D != d)
      { printf("%8lx %08lx %08lx %08lx %08lx\n", i, a, b, c, d); }
  }

  /* extended levels */
  cpuid(extended);

  extended_level = a;
  v[0] = b;	/* vendor ID, 12 characters */
  v[1] = d;
  v[2] = c;
  v[3] = 0;	/* terminate the string */

  printf("%8lx %08lx %08lx %08lx %08lx", extended, a, b, c, d);
  if (extended_level > extended) { printf(" %s", (char *) v); }
  printf("\n");

  for (i = extended+1; i < name; i++) {
    A = a; B = b; C = c; D = d;
    cpuid(i);
    if (A != a || B != b || C != c || D != d)
      { printf("%8lx %08lx %08lx %08lx %08lx\n", i, a, b, c, d); }
  }

  if (extended_level >= name+2)
    {
      /* name string, up to 48 characters */
      read_cpuid(name,   v[0], v[1], v[2],  v[3]);
      read_cpuid(name+1, v[4], v[5], v[6],  v[7]);
      read_cpuid(name+2, v[8], v[9], v[10], v[11]);
      v[12] = 0;	/* terminate the string */

      cpuid(name);
      printf("%8lx %08lx %08lx %08lx %08lx %s\n", name, a, b, c, d, (char *) v);
      i++;
    }

  for (; i < final; i++) {
    A = a; B = b; C = c; D = d;
    cpuid(i);
    if (A != a || B != b || C != c || D != d)
      { printf("%8lx %08lx %08lx %08lx %08lx\n", i, a, b, c, d); }
  }

  cpuid(final);
  printf("%8lx %08lx %08lx %08lx %08lx\n", final, a, b, c, d);

  return 0;
}
