/******************************************************************************/
/* Mainroutine that prints the processor model name and speed or IA32 machines*/
/******************************************************************************/
/* Compile with gcc -O -o cpuinfo.exe cpuinfo.c                               */
/******************************************************************************/
/* This file is copyright protected, see the notice at the end of the file    */
/******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "defs.h"
#include "clock.h"

#include <ctype.h>
#include <string.h>

/*----------------------------------------------------------------------------*/
/* 
 * Please fill in the following team struct 
 */
team_t team = {
    "unstable",			/* Team name */
    "Tjerk Kostelijk",		/* First member full name */
    "tkosteli@science.uva.nl",	/* First member email address */
    "Andreas van Cranenburgh",	/* Second member full name */
    "acranenb@science.uva.nl"	/* Second member email addr */
};

/*----------------------------------------------------------------------------*/
/* See http://www.sandpile.org/ia32/cpuid.htm for details of the assembly call*/
/* Hint: this can be ordered and packaged more logically                      */
/*----------------------------------------------------------------------------*/

#define TABLE_LENGTH 89

    /* subscripts: 4 families, 10 vendors, 16 models, 48 chars */
char model_name[4][10][16][49] = { {
    /* family 0: 486 */
    {"i80486DX-25/33",
    "i80486DX-50",
    "i80486SX",
    "i80486DX2",
    "i80486SL",
    "i80486SX2",
    "i80486DX2WB",
    "i80486DX4",
    "i80486DX4WB"},

    {"",
    "U5D",
    "U5S"},

    /* AMD */
    {"","",
    "80486DX2","","","",
    "80486DX2WB",
    "80486DX4",
    "80486DX4WB","","","","",
    "5x86",
    "5x86WB"},

    {"","","","","","","","","5x86"}},
     
	/* family 1: 586 */
    {{"P5 A-step",
    "P5",
    "P54C",
    "P24T Overdrive",
    "P55C","","",
    "P54C",
    "P55C (0.25µm)"},

    {},

    /* AMD */
    {"SSA5 (PR75, PR90, PR100)",
    "5k86 (PR120, PR133)",
    "5k86 (PR166)",
    "5k86 (PR200)",
    "","",
    "K6 (0.30 µm)",
    "K6 (0.25 µm)",
    "K6-2",
    "K6-III",
    "K6-2+ or K6-III+ (0.18 µm)"},
    
    /* Cyrix */ 
    {"6x86MX","",
    
    "6x86","",
  
    "GX, GXm",
    "Cyrix M2 core",
    "WinChip C5A core",
    "WinChip C5B/C core",
    "WinChip C5N core",
    "WinChip C5XL/P core"},
    
    /* NexGen */ {"Nx586 or Nx586FPU (only later ones)"},

    /* Centaur */
    {"","","","","C6",
    "","","","C2",
    "C3"},

    /* Rise */
    {"mP6 (0.25 µm)","",
    "mP6 (0.18 µm)"},
    
    /* SiS */
    {"55x"},

    /* transmeta */
    {"","","","","Crusoe TM3x00 and TM5x00"},
    /* NS Geode */
    {"","","","","","GX1, GXLV, GXm",
    "GX2"}},
	
    	/* family 2: 686 */
    {{"P6 A-step",
    "P6","",
    "P2 (0.28 µm)","",
    "P2 (0.25 µm)",
    "P2 with on-die L2 cache",
    "P3 (0.25 µm)",
    "P3 (0.18 µm) with 256 KB on-die L2 cache",
    "PM (0.13 µm) with 512 KB or 1 MB on-die L2 cache",
    "P3 (0.18 µm) with 1 or 2 MB on-die L2 cache",
    "P3 (0.13 µm) with 256 or 512 KB on-die L2 cache", ""
    "PM (0.09 µm) with 512 KB or 2 MB on-die L2 cache"},

    {}, /* no umc */
    
    {"","Athlon (0.25 µm)",
    "Athlon (0.18 µm)",
    "Duron (SF core)",
    "Athlon (TB core)","",
    "Athlon (PM core)",
    "Duron (MG core)",
    "Athlon (TH/AP core)",""
    "Athlon (BT core)"},
    
    {}, /* no cyrix */
    {}, /* no !@# */
    {"","","","","","","","","","VIA C3 Nehemiah"}},
    
	/* family 3: 786? */
    {{"","","","",
    "Athlon 64 (0.13 µm 754)",
    "Athlon 64 FX or Opteron (0.13 µm)", ""
    "Athlon 64 (0.13 µm 939)",
    "Athlon 64 (0.13 µm 754)","",""
    "Athlon 64 (0.13 µm 939)",
    "Athlon 64 (0.13 µm 754)","",
    "Athlon 64 (0.13 µm 754)",
    "Athlon 64 (0.13 µm 939)"},

    {"P4 (0.18 µm)",
    "P4 (0.18 µm)",
    "P4 (0.13 µm)",
    "P4 (0.09 µm)"},

    {"Merced (0.18 µm)"},

    {"McKinley (0.18 µm)",
    "Madison or Deerfield (0.13 µm)",
    "Madison 9M (0.13 µm)"}}
};

char *ltrim(char *str);

static char vendor_names[10][13] = {
	"GenuineIntel", /* 0.. */
	"UMC UMC UMC ",
	"AuthenticAMD",
	"CyrixInstead",
	"NexGenDriven",
	"CentaurHauls",
	"RiseRiseRise",
	"SiS SiS SiS ",
	"GenuineTMx86",
	"Geode by NSC"}; /* ...9 */


/*----------------------------------------------------------------------------*/

#define access_cpuid(n,w,x,y,z) \
  asm("cpuid" : "=eax" (w), "=ebx" (x), "=ecx" (y), "=edx" (z) : "0" (n))

#define standard    0x00000000UL	/* start of standard cpuid range     */
#define vendor_req  0x00000000UL	/* request the vendor string         */
#define feature_req 0x00000001UL	/* request processor type & features */
#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       */

/*----------------------------------------------------------------------------*/

int main(int argc, char **argv)
{
    ulong register_a, register_b, register_c, register_d;
    ulong level;
    ulong vendor_id[13];	/* 48 character string */
    ulong processor_family;
    ulong processor_model;
    ulong processor_stepping;
    ulong extended_family;
    ulong extended_model;
    int a, processor_vendor = 0;
    char cpuname[4][13];
    char *cpuname_p;
    ulong cpulong[13];


    /* get vendor string and maximum standard request */
    access_cpuid(vendor_req, register_a, register_b, register_c,
		 register_d);
    level = register_a;

    if (argc > 1) {
	printf("%8lx %08lx %08lx %08lx %08lx\n",
	       vendor_req, register_a, register_b, register_c, register_d);
	printf("Maximum supported standardlevel = %d\n", (int) level);
    }

    vendor_id[0] = register_b;	/* vendor ID, 12 characters */
    vendor_id[1] = register_d;
    vendor_id[2] = register_c;
    vendor_id[3] = 0;		/* terminate the string */
    
    for (a = 0; a < 11; a++) {
	if (strncmp(vendor_names[a], (char *)vendor_id, 48) == 0) {
		processor_vendor = a;
		break;
	}
    }

    if (level >= feature_req) {
	/* get processor type/family/model/stepping and feature flags */
	access_cpuid(feature_req, register_a, register_b, register_c,
		     register_d);

	if (argc > 1) {
	    printf("req: %8lx AX: %08lx BX: %08lx CX: %08lx DX: %08lx\n",
		   feature_req, register_a, register_b, register_c,
		   register_d);
	}

	/* register_a: processor type/family/model/stepping */
	processor_family = (register_a >> 8) & 0xf;
	processor_model = (register_a >> 4) & 0xf;
	processor_stepping = (register_a >> 0) & 0xf;
	extended_family = (register_a >> 20) & 0xf;
	extended_model = (register_a >> 16) & 0xf;
		
	/* register_b: brand ID/CLFLUSH/CPU count/APIC ID */
	/* register_c: feature flags */
	/* register_d: feature flags */
    } else {
	/* 80386 or older */
	processor_family = 3;
	processor_model = 0;
	processor_stepping = 0;
	extended_family = 0;
	extended_model = 0;
    }

    access_cpuid(extended, register_a, register_b, register_c, register_d);
    if (argc > 1)
	printf("Maximum supported extended level = %x\n", (int) register_a);

    if (register_a > name) {
        /* get processor name string, iterate over its three parts */
	for(a=0; a<3; a++) {
		access_cpuid(name + a, register_a, register_b, register_c, register_d);

		if(argc > 1) {
			printf("%8lx %08lx %08lx %08lx %08lx\n", 
					vendor_req, register_a, register_b, register_c, register_d);
		}
		cpulong[a*4+0] = register_a;
		cpulong[a*4+1] = register_b;
		cpulong[a*4+2] = register_c;
		cpulong[a*4+3] = register_d;	
	}
    	cpulong[12] = 0;
	/* convert the long array to a string */
        sprintf((char *) cpuname, "%s", (char *) cpulong);
	cpuname_p = (char *) cpuname;
    } else {
	/* sprintf("%s (%d, %d, %d)", (char *) model_name[processor_family - 4][processor_vendor][processor_model], (int) processor_family - 4, (int) processor_vendor, (int) processor_model); */
	cpuname_p = (char *) model_name[processor_family - 4][processor_vendor][processor_model];
	/* cpuname_p = (char *) vendor_id; */
    }

    if (argc > 1) {
	printf("vendor_id    : %s (%d)\n", (char *) vendor_id, processor_vendor);
	printf("cpu family   : %lX (%lX)\n", processor_family,
	       extended_family);
	printf("model        : %lX (%lX)\n", processor_model,
	       extended_model);
	printf("stepping     : %lX\n", processor_stepping);

        printf("CPU name: %s\n", cpuname_p);

	printf("Mhz: %.1f \n", mhz(0));
    } else {
	    printf("This computer has an \n%s processor running at %.1f MHz\n", cpuname_p, mhz(0));
    }

    return 0;
}

/******************************************************************************/
/*                                                                            */
/* This file is copyright protected.                                          */
/*                                                                            */
/* Copyright (c) 2004 University of Amsterdam.                                */
/* This software or any part thereof may only be used for non-commercial      */
/* research or purposes, as long as the author and University are mentioned.  */
/* Commercial use without explicit prior written consent by the University    */
/* of Amsterdam is strictly prohibited.                                       */
/*                                                                            */
/* This copyright notice must be included with any copy of this software or   */
/* any part thereof.                                                          */
/*                                                                            */
/******************************************************************************/
/* cpuinfo.c                                                        /\/\      */
/* Last Modification:  September 2004                               \  /      */
/*                                                                  /  \      */
/* Author: Arnoud Visser                                         _  \/\/  _   */
/*         University of Amsterdam                              | |      | |  */
/*         Dept. of Computer Systems                            | | /\/\ | |  */
/*         Kruislaan 403, NL 1098 SJ Amsterdam                  | | \  / | |  */
/*         THE NETHERLANDS                                      | | /  \ | |  */
/*         arnoud@science.uva.nl                                | | \/\/ | |  */
/*                                                              | \______/ |  */
/* This software has been written for the Computer Systems       \________/   */
/* course at our department. No representations are made about                */
/* the suitability of this software for any purpose other           /\/\      */
/* than research and education.                                     \  /      */
/*                                                                  /  \      */
/* Release note: The processor model and speed are selected         \/\/      */
/*               randomly. It is an education assignment to select            */
/*               the correct model and speed.                                 */
/******************************************************************************/
