#include <stdio.h>
#include <windows.h>

#define TCL_OK          0
#define TCL_ERROR       1

//
// From tclWin32Dll.c
//
/*
 *------------------------------------------------------------------------
 *
 * TclWinCPUID --
 *
 * Get CPU ID information on an Intel box under Windows
 *
 * Results:
 * Returns TCL_OK if successful, TCL_ERROR if CPUID is not supported or
 * fails.
 *
 * Side effects:
 * If successful, stores EAX, EBX, ECX and EDX registers after the CPUID
 * instruction in the four integers designated by 'regsPtr'
 *
 *----------------------------------------------------------------------
 */

int
TclWinCPUID(
    unsigned int index,    /* Which CPUID value to retrieve. */
    unsigned int *regsPtr) /* Registers after the CPUID. */
{
#ifdef HAVE_NO_SEH
    EXCEPTION_REGISTRATION registration;
#endif
    int status = TCL_ERROR;

#if defined(__GNUC__) && !defined(_WIN64)
    /*
     * Execute the CPUID instruction with the given index, and store results
     * off 'regPtr'.
     */

    __asm__ __volatile__(
   /*
    * Construct an EXCEPTION_REGISTRATION to protect the CPUID
    * instruction (early 486's don't have CPUID)
    */

   "leal %[registration], %%edx"    "\n\t"
   "movl %%fs:0,     %%eax"      "\n\t"
   "movl %%eax,      0x0(%%edx)" "\n\t" /* link */
   "leal 1f,      %%eax"      "\n\t"
   "movl %%eax,      0x4(%%edx)" "\n\t" /* handler */
   "movl %%ebp,      0x8(%%edx)" "\n\t" /* ebp */
   "movl %%esp,      0xc(%%edx)" "\n\t" /* esp */
   "movl %[error],   0x10(%%edx)"   "\n\t" /* status */

   /*
    * Link the EXCEPTION_REGISTRATION on the chain
    */

   "movl %%edx,      %%fs:0"     "\n\t"

   /*
    * Do the CPUID instruction, and save the results in the 'regsPtr'
    * area.
    */

   "movl %[rptr], %%edi"      "\n\t"
   "movl %[index],   %%eax"      "\n\t"
   "cpuid"              "\n\t"
   "movl %%eax,      0x0(%%edi)" "\n\t"
   "movl %%ebx,      0x4(%%edi)" "\n\t"
   "movl %%ecx,      0x8(%%edi)" "\n\t"
   "movl %%edx,      0xc(%%edi)" "\n\t"

   /*
    * Come here on a normal exit. Recover the EXCEPTION_REGISTRATION and
    * store a TCL_OK status.
    */

   "movl %%fs:0,     %%edx"      "\n\t"
   "movl %[ok],      %%eax"      "\n\t"
   "movl %%eax,      0x10(%%edx)"   "\n\t"
   "jmp  2f"            "\n"

   /*
    * Come here on an exception. Get the EXCEPTION_REGISTRATION that we
    * previously put on the chain.
    */

   "1:"              "\t"
   "movl %%fs:0,     %%edx"      "\n\t"
   "movl 0x8(%%edx), %%edx"      "\n\t"

   /*
    * Come here however we exited. Restore context from the
    * EXCEPTION_REGISTRATION in case the stack is unbalanced.
    */

   "2:"              "\t"
   "movl 0xc(%%edx), %%esp"      "\n\t"
   "movl 0x8(%%edx), %%ebp"      "\n\t"
   "movl 0x0(%%edx), %%eax"      "\n\t"
   "movl %%eax,      %%fs:0"     "\n\t"

   :
   /* No outputs */
   :
   [index]     "m"   (index),
   [rptr]      "m"   (regsPtr),
   [registration] "m"   (registration),
   [ok]     "i"   (TCL_OK),
   [error]     "i"   (TCL_ERROR)
   :
   "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
    status = registration.status;

#elif defined(_MSC_VER) && !defined(_WIN64)
    /*
     * Define a structure in the stack frame to hold the registers.
     */
    _try {
	_asm {
	    push    ebx
	    push    ecx
	    push    edx
	    mov	    eax, regs.dw0
	    cpuid
	    mov	    regs.dw0, eax
	    mov	    regs.dw1, ebx
	    mov	    regs.dw2, ecx
	    mov	    regs.dw3, edx
	    pop	    edx
	    pop	    ecx
	    pop	    ebx
	}

	/*
	 * Copy regs back out to the caller.
	 */
	regsPtr[0] = regs.dw0;
	regsPtr[1] = regs.dw1;
	regsPtr[2] = regs.dw2;
	regsPtr[3] = regs.dw3;

	status = TCL_OK;
    } __except(EXCEPTION_EXECUTE_HANDLER) {
	/* do nothing */
    }

#elif defined(__BORLANDC__)
// Code from
// http://groups.google.com/group/borland.public.cppbuilder.language/browse_thread/thread/b497c052f5554d96/6ae833b0f70b8e84?q=cpuid+OR+cpu-id+group:borland.public.cppbuilder*#6ae833b0f70b8e84
    __asm {
            PUSH    EBX             // Save affected register
            PUSH    EDI
            MOV     EDI,regsPtr     // destination
            MOV     EAX,index
            DW      0xA20F          // CPUID Command}
            STOSD                   // CPUID[1]
            MOV     EAX,EBX
            STOSD                   // CPUID[2]
            MOV     EAX,ECX
            STOSD                   // CPUID[3]
            MOV     EAX,EDX
            STOSD                   // CPUID[4]
            POP     EDI             // Restore registers
            POP     EBX
        }
   status = TCL_OK;
#else
    /*
     * Don't know how to do assembly code for this compiler and/or
     * architecture.
     */
#endif
    return status;
}

// Adapted from tclWinTime.c
int main(void) {
   unsigned int regs[5], tmp, res;
   LARGE_INTEGER frequ;

   // Show the counter's frequency
   if (! QueryPerformanceFrequency(&frequ)) {
      printf("No performance counter.\n");
   } else {
      printf("Performance counter's frequency: %lu Hz.\n", frequ.QuadPart);
   }
   // Info for code '0'
   if (TclWinCPUID(0, regs) != TCL_OK) {
      printf("TclWinCPUID with code '0' failed.\n");
      return 1;
   }
   // Show info: Have to re-arrange the content of 'regs'
   regs[4] = 0;         // delimit string
   // swap regs[2] & regs[3]
   tmp = regs[2];
   regs[2] = regs[3];
   regs[3] = tmp;
   printf("Results of CPUID(0): %s\n", &regs[1]);
   res = (strcmp((const char *)&regs[1], "GenuineIntel") == 0);
   printf("'performance counter available' (test part 1) (1 == ok)? %d\n", res);

   // Info for code '1'
   if (TclWinCPUID(1, regs) != TCL_OK) {
      printf("TclWinCPUID with code '1' failed.\n");
      return 1;
   }
   // Apply the test from NativeGetTime (2nd part)
                        /* Pentium 4 */
   res = (regs[0]&0x00000F00) == 0x00000F00
              /* Extended family             Hyperthread */
          || ((regs[0] & 0x00F00000) && (regs[3] & 0x10000000));
   printf("Results of CPUID(1)\n");
   printf("'performance counter available' (test part 2) (1 == ok)? %d\n", res);
   printf("Detailed results\n");
   printf("regs[0] = 0x%08X, regs[1] = 0x%08X, regs[3] = 0x%08X\n",
           regs[0], regs[1], regs[3]);
   return 0;
}
