#include "precomp.h"
#include <oprahcom.h>
#include <regentry.h>
#include "mperror.h"
#include <sehcall.h>
#define LEGACY_DIVISOR	8

extern "C" WORD _cdecl is_cyrix(void);
extern "C" DWORD _cdecl get_nxcpu_type(void);


#ifndef _M_IX86
DWORD WINAPI CallWithSEH(EXCEPTPROC pfn, void *pv, INEXCEPTION InException)
{
	// we don't have a native version of SEH for the alpha,
	// use __try and __except
	pfn(pv);
    return 0;
}
#endif



#ifdef _M_IX86

DWORD NMINTERNAL FindTSC (LPVOID pvRefData)
{
	   _asm
	   {
		   mov     eax,1
		   _emit   00Fh     ;; CPUID
		   _emit   0A2h

    // The ref data is 2 DWORDS, the first is the flags,
    // the second the family
		   mov     ecx,pvRefData
		   mov     [ecx],edx
		   mov	   [ecx][4],eax
	   }

	   return 1;
}

DWORD NMINTERNAL NoCPUID (LPEXCEPTION_RECORD per,PCONTEXT pctx)
{
    return 0;
}
//
//  GetProcessorSpeed(dwFamily)
//
//  get the processor speed in MHz, only works on Pentium or better
//  machines.
//
//  Will put 3, or 4 in dwFamily for 386/486, but no speed.
//  returns speed and family for 586+
//
//  - thanks to toddla, modified by mikeg
//

int NMINTERNAL GetProcessorSpeed(int *pdwFamily)
{
    SYSTEM_INFO si;
    __int64	start, end, freq;
    int 	flags,family;
    int 	time;
    int 	clocks;
    DWORD	oldclass;
    HANDLE      hprocess;
    int     pRef[2];

    ZeroMemory(&si, sizeof(si));
    GetSystemInfo(&si);

    //Set the family. If wProcessorLevel is not specified, dig it out of dwProcessorType
    //Because wProcessor level is not implemented on Win95
    if (si.wProcessorLevel) {
	*pdwFamily=si.wProcessorLevel;
    }else {
    	//Ok, we're on Win95
    	switch (si.dwProcessorType) {
    	       case PROCESSOR_INTEL_386:
    		   *pdwFamily=3;
    		   break;

    	       case PROCESSOR_INTEL_486:
    		   *pdwFamily=4;
    		   break;
    	       default:
    		   *pdwFamily=0;
    		   break;
    	}
    		
    }

    //
    // make sure this is a INTEL Pentium (or clone) or higher.
    //
    if (si.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL)
        return 0;

    if (si.dwProcessorType < PROCESSOR_INTEL_PENTIUM)
        return 0;

    //
    // see if this chip supports rdtsc before using it.
    //
    if (!CallWithSEH (FindTSC,&pRef,NoCPUID))     {
        flags=0;
    } else {
    // The ref data is 2 DWORDS, the first is the flags,
    // the second the family. Pull them out and use them
        flags=pRef[0];
        family=pRef[1];
    }

    if (!(flags & 0x10))
        return 0;


    //If we don't have a family, set it now
    //Family is bits 11:8 of eax from CPU, with eax=1
    if (!(*pdwFamily)) {
       *pdwFamily=(family& 0x0F00) >> 8;
    }


    hprocess = GetCurrentProcess();
    oldclass = GetPriorityClass(hprocess);
    SetPriorityClass(hprocess, REALTIME_PRIORITY_CLASS);
    Sleep(10);

    QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
    QueryPerformanceCounter((LARGE_INTEGER*)&start);
    _asm
    {
        _emit   0Fh     ;; RDTSC
        _emit   31h
        mov     ecx,100000
x:      dec     ecx
        jnz     x
        mov     ebx,eax
        _emit   0Fh     ;; RDTSC
        _emit   31h
        sub     eax,ebx
        mov     dword ptr clocks[0],eax
    }
    QueryPerformanceCounter((LARGE_INTEGER*)&end);
    SetPriorityClass(hprocess, oldclass);

    time = MulDiv((int)(end-start),1000000,(int)freq);

    return (clocks + time/2) / time;
}



HRESULT NMINTERNAL GetNormalizedCPUSpeed (int *pdwNormalizedSpeed, int *dwFamily)
{
   int dwProcessorSpeed;

   dwProcessorSpeed=GetProcessorSpeed (dwFamily);

   *pdwNormalizedSpeed=dwProcessorSpeed;

   if (*dwFamily > 5) {
       //Ok, TWO things.
       // ONE DO NOT DO FP!
       // Two for the same Mhz assume a 686 is 1.3 times as fast as a 586 and a 786 is 1.6 times, etc.
       *pdwNormalizedSpeed=(ULONG) (((10+3*(*dwFamily-5))*dwProcessorSpeed)/10);
   }

   if (*dwFamily < 5) {
	  //bugbug until we have 386/486 timing code, assume
	  //486=50,386=37
      if (*dwFamily > 3) {
           //Cyrix, (5x86)? check before making default assignment
           if (is_cyrix()) {
               if (*pdwNormalizedSpeed==0) {
                   *dwFamily=5;
                   *pdwNormalizedSpeed=100;
                   return hrSuccess;
               }
           }
      }

	  *pdwNormalizedSpeed= (*dwFamily*100)/LEGACY_DIVISOR;

      if (get_nxcpu_type ()) {
        //Double the perceived value on a NexGen
        *pdwNormalizedSpeed *=2;
      }


   }



   return hrSuccess;
}
#endif //_M_IX86





BOOL WINAPI IsFloatingPointEmulated(void)
{
	long lRegValue;
	SYSTEM_INFO si;
	OSVERSIONINFO osi;
	BOOL fEmulation, bNT;
	

	// are we a Pentium
	ZeroMemory(&si, sizeof(si));
	GetSystemInfo(&si);
	if (si.dwProcessorType != PROCESSOR_INTEL_PENTIUM) 
	{
		return FALSE;
	}

	// Which OS: NT or 95 ?
	ZeroMemory(&osi, sizeof(osi));
	osi.dwOSVersionInfoSize = sizeof(osi);
	GetVersionEx(&osi);
	bNT = (osi.dwPlatformId == VER_PLATFORM_WIN32_NT);

	// Windows NT
	if (bNT)
	{
		RegEntry re(TEXT("System\\CurrentControlSet\\Control\\Session Manager"),
		            HKEY_LOCAL_MACHINE, FALSE);

		// try to get a definitive answer from the registry
		lRegValue = re.GetNumber(TEXT("ForceNpxEmulation"), -1);

		// registry: 0: no 
		//           1: conditional (not definitive!)
		//           2: yes

		if (lRegValue == 2)
		{
			return TRUE;
		}

		// we could load "IsProcessorFeaturePresent from kernel32.dll,
		// but the version that shipped with NT 4 has a bug in it that
		// returns the exact opposite of what it should be.  It was
		// fixed in NT 5. Since this API isn't the same across platforms,
		// we won't use it.

		return FALSE;
	}

	// Windows 95 - to be added later
	return FALSE;

}