#include <string>
#include <sstream>

namespace CPUCode
{
/////////////////////////////////////////////////////////////////
/////////////// LOW-LEVEL ASSEMBLER OPERATIONS //////////////////
std::string GetEmbeddedCPUName()
{
   char  Name[48];

   Name[0] = NULL;

    _asm
    {
        lea edi, Name

        mov eax, 080000002h
        CPUID

        mov [edi], eax
        mov [edi+4], ebx
        mov [edi+8], ecx
        mov [edi+12], edx
        add edi, 16

        mov eax, 080000003h
        CPUID
        mov [edi], eax
        mov [edi+4], ebx
        mov [edi+8], ecx
        mov [edi+12], edx
        add edi, 16

        mov eax, 080000004h
        CPUID
        mov [edi], eax
        mov [edi+4], ebx
        mov [edi+8], ecx
        mov [edi+12], edx
    }

   return Name;
}

std::string GetCPUVendor()
{
	long RetValue = 0;
    long LargestExtendedFunction = 0;
    long Features = 0;
    char Vendor[13];
    char ProcessorType = ' ';
    char Family = ' ';
    char Model = ' ';
    char Stepping = ' ';

    memset (Vendor, 0, sizeof (Vendor));

    //get the Vendor string
    _asm
    {
        //lets see if we can execute CPUID by seeing if we can flip the 21st bit
        //of the flags register

        pushfd
        pop eax

        //save the original
        mov ecx, eax

        //swap the 21st bit
        xor eax, 200000h

        //push it back on the stack, set the flags
        push eax
        popfd

        //now get the flags
        pushfd
        pop eax

        //see if the values are the same
        xor eax, ecx
        jz NoCPUIDSupport
        
        //CPUID supported, go ahead and grab some info
        xor eax, eax
        CPUID

        //grab the vendor and see if we reconize it
        lea eax, Vendor
        mov [eax], ebx
        mov [eax+4], edx
        mov [eax+8], ecx

        //see if it is an Intel CPU and flag accordingly
        cmp [eax], 756E6547h        //Genu
        jne AMDCheck

        cmp [eax+4], 49656E69h      //ineI
        jne EndCPUCheck

        cmp [eax+8], 6C65746Eh      //ntel
        jne EndCPUCheck

        //Intel CPU
        mov RetValue, 1
        jmp short EndCPUCheck

    AMDCheck:
        cmp [eax], 68747541h        //Auth
        jne EndCPUCheck

        cmp [eax+4], 69746E65h      //enti
        jne EndCPUCheck

        cmp [eax+8], 444D4163h      //cAMD
        jne EndCPUCheck

        mov RetValue, 2

    EndCPUCheck:

    NoCPUIDSupport:
    }

	return std::string(Vendor);
}

// Calculates the running clock speed of the computer's CPU.
std::string GetCPUSpeed()
{
	LARGE_INTEGER ulFreq, ulTicks, ulValue, ulStartCounter, ulEAX_EDX;

	if (QueryPerformanceFrequency(&ulFreq))
	{
		QueryPerformanceCounter(&ulTicks);
		ulValue.QuadPart = ulTicks.QuadPart + ulFreq.QuadPart;

		__asm RDTSC
		__asm mov ulEAX_EDX.LowPart, EAX
		__asm mov ulEAX_EDX.HighPart, EDX

		ulStartCounter.QuadPart = ulEAX_EDX.QuadPart;

		do
		{
			QueryPerformanceCounter(&ulTicks);
		} while (ulTicks.QuadPart <= ulValue.QuadPart);

		__asm RDTSC
		__asm mov ulEAX_EDX.LowPart, EAX
		__asm mov ulEAX_EDX.HighPart, EDX
		
		unsigned int speed = (size_t) ((ulEAX_EDX.QuadPart - ulStartCounter.QuadPart) / 1000000);

		std::stringstream streamSpeed;
		streamSpeed << speed;
		return std::string(streamSpeed.str());
	}
	else
		return "0";			
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
std::string GetCPUString()	{ return "CPU: " + GetEmbeddedCPUName() + " (" + GetCPUVendor() + ")" + " ~" + GetCPUSpeed() + "mHz"; }

}
