#include "pch.hpp"
//===========================================================================//
// File:	 Cpu.cpp														 //
// Project:  GameOS					                                         //
// Contents: CPU detection code												 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 06/10/99  AG     Created													 // 
//																			 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "ErrorHandler.hpp"
#include "DirectX.hpp"
#include "Windows.hpp"


float L2SpeedR, L2SpeedW, L2SpeedRW, MainSpeedR, MainSpeedW, MainSpeedRW, VidMemR, VidMemW, VidMemRW, AGPMemR, AGPMemW, AGPMemRW;
char SpeedBuffer[16];			// ASCII MHz
float ProcessorSpeed;			// Processor speed
float OneOverProcessorSpeed;
extern DWORD gDisableRDTSC;
char CacheInformation[128];
int	HasAMD=0;
int	HasCYRIX=0;
int	HasINTEL=0;



/****************** CPU types ************************************/
/* Enumerated cpu types, change these if you want to whatever    */
/* convention suites you                                         */
#define	UNKNOWN_CPU		0x00	// default
#define	INTEL_UNKNOWN	0x10	// unidentified Intel
#define	INTEL_486		0x11	// 486
#define INTEL_P5		0x12	// origional Pentium (P60 & P90)
#define INTEL_P54C		0x13	// socket 7 Pentium
#define INTEL_P55C		0x14	// MMX Pentium
#define INTEL_P6		0x15	// Pentium Pro
#define INTEL_P2		0x16	// PII
#define INTEL_P2_DECH	0x17	// Dechutes (had FXSAVE/FXRESTORE)
#define INTEL_CELERON	0x18	// First Celeron w/o L2
#define INTEL_CELERON_A	0x19	// Celeron with backside L2
#define INTEL_P2_XEON	0x1a	// 1M or larger L2 cache
#define INTEL_P3		0x1b	// PIII w/SSE
#define INTEL_P3_XEON	0x1c	// 1M or larger L2 cache
#define AMD_UNKNOWN		0x20	// unidentified AMD
#define AMD_486			0x21	// 486 or Am5x86
#define AMD_K5			0x22	// K5
#define AMD_K6			0x23	// K6 w/MMX
#define AMD_K6_2		0x24	// K6 w/3DNow
#define AMD_K6_3		0x25	// K6 with backside L2
#define AMD_K7			0x26	// K7
#define CYRIX_UNKNOWN	0x30	// unidentified Cyrix
#define CYRIX_486		0x31	// 486
#define CYRIX_586		0x32	// 5x86
#define CYRIX_GX		0x33	// MediaGX
#define CYRIX_GXM		0x34	// MediaGX w/MMX
#define CYRIX_M1		0x35	// 6x86
#define CYRIX_M2		0x36	// 6x86MX/MII
#define CYRIX_MX		0x37	// Cayenne core
#define CENTAUR_UNKNOWN	0x40	// unidentified Centaur
#define CENTAUR_C6		0x41	// Winchip
#define CENTAUR_C6_2	0x42	// Winchip 2 w/3DNow

/****************** CPU Signatures ************************************/
/* These are the family/model/rev codes returned by CPUID-1           */
// Intel
#define	SIG_P5			0x0510	// P5 - Socket 4
#define	SIG_P54C		0x0520	// P54C - Socket 5/7
#define	SIG_P5OD		0x1510	// P5 Overdrive
#define	SIG_P54COD		0x1520	// P54C Overdrive
#define	SIG_P5OD486		0x1530	// P5 Overdrive - 486 socket
#define	SIG_P5_			0x0570	// P54C Mobile
#define	SIG_P55C		0x0540	// P55C - first mmx
#define	SIG_P55COD		0x1540	// P55C Overdrive
#define	SIG_P55CM		0x0580	// P55C Mobile
#define	SIG_P6			0x0610	// PPro
#define	SIG_P6OD		0x1630	// PPro Overdrive
#define	SIG_P2			0x0630	// P2
#define	SIG_P2XC		0x0650	// Xeon & Celeron
#define	SIG_CELA		0x0660	// Celeron-A/Mobile PII
#define	SIG_P3			0x0670	// P3
#define	SIG_P3M			0x0680	// P3
// Amd
#define SIG_K5M0		0x0500	// K5 - Model 0
#define SIG_K5M1		0x0510	// K5 - Model 1
#define SIG_K5M2		0x0520	// K5 - Model 2
#define SIG_K5M3		0x0530	// K5 - Model 3
#define SIG_K6M6		0x0560	// K6 - Model 6
#define SIG_K6M7		0x0570	// K6 - Model 7
#define SIG_K62			0x0580	// K6-2 with 3DNow
#define SIG_K63			0x0590	// K6-3
#define SIG_K7			0x0600	// K7 - Model 0
#define SIG_K71			0x0610	// K7 - Model 1
// Centaur
#define SIG_C6			0x0540	// Winchip
#define SIG_C62			0x0560	// Winchip 2 w/3DNow
// Cyrix
#define SIG_GX			0x0440	// MediaGX
#define SIG_GXM			0x0540	// MediaGX with MMX
#define SIG_M9			0x0500	// 5x86 ???
#define SIG_M1			0x0520	// 6x86
#define SIG_M2			0x0600	// 6x86MX and M2
#define SIG_MX			0x0620	// Cayenne
#define DIR_M5			0x00	// M5R (SLC/DLC/SRx/DRx2)
#define DIR_M7			0x10	// M7 (486DX/DX2)
#define DIR_M9			0x20	// M9/M1sc (5x86)
#define DIR_M1			0x30	// M1 (6x86/6x86L)
#define DIR_GX			0x40	// GX/GXm
#define DIR_M2			0x50	// M2 (6x86MX/MII)


/****************** Cache Descriptors ************************************/
/* These are the values returned with CPUID-2.  They describe the        */
/* different cache sizes.  Since I'm only concerned with the cache       */
/* sizes, I left out the TLB info.                                       */
#define CD_I8K			0x06
#define CD_I16K			0x08
#define CD_D8K			0x0a
#define CD_D16K			0x0c
#define	CD_NOL2			0x40
#define	CD_128K			0x41
#define	CD_256K			0x42
#define	CD_512K			0x43
#define	CD_1M			0x44
#define	CD_2M			0x45









//
// This takes .25 seconds, so is done once at startup
//
// This also measures L2 and main memory read speed
//
//
void InitProcessorSpeed()
{
	__int64 time,qpc,compare,memory;

	BYTE* TestMemory=(BYTE*)malloc(1024*1024*2+65536);		// 2048K (will not be accurate for > 512K cache machines)
	BYTE* BaseAddress=(BYTE*)(((DWORD)TestMemory+31)&(~31));

	HANDLE hThread=GetCurrentThread();
	HANDLE hProcess=GetCurrentProcess();
	long OldPriority=GetThreadPriority(hThread);
	DWORD OldClass=GetPriorityClass(hProcess);
	Sleep(1);													// Give windows a chance to run background tasks
	SetPriorityClass(hProcess,REALTIME_PRIORITY_CLASS);
	SetThreadPriority(hThread,THREAD_PRIORITY_TIME_CRITICAL);	// Run code at realtime priority
	Sleep(1);													// Give windows a chance to run background tasks

	QueryPerformanceFrequency((LARGE_INTEGER*)&qpc);
	QueryPerformanceCounter((LARGE_INTEGER*)&compare);
	compare+=qpc/2;												// Find time .5 seconds ahead

	GetTimeStampStart(time);
//
// Timings from L2 (for read)
//
	_asm
	{
		mov eax,BaseAddress
		mov edx,0
lp1:	mov ebx,dword ptr [eax+edx]					// Get 128k of buffer into L2 (L1 contains last 8 or 16k of buffer)
		add edx,32
		cmp edx,128*1024
		jnz lp1
	}

	GetTimeStampStart(memory);

	_asm
	{
		mov eax,BaseAddress
		mov edx,0
		mov ecx,1024*16

lp2:	mov ebx,dword ptr [eax+edx+32* 0]
		mov ebx,dword ptr [eax+edx+32*15+4]
		mov ebx,dword ptr [eax+edx+32* 1]
		mov ebx,dword ptr [eax+edx+32*14+4]
		mov ebx,dword ptr [eax+edx+32* 2]
		mov ebx,dword ptr [eax+edx+32*13+4]
		mov ebx,dword ptr [eax+edx+32* 3]
		mov ebx,dword ptr [eax+edx+32*12+4]
		mov ebx,dword ptr [eax+edx+32* 4]			// Read 8Meg of cache lines of data from 1st 128K of buffer
		mov ebx,dword ptr [eax+edx+32*11+4]
		mov ebx,dword ptr [eax+edx+32* 5]
		mov ebx,dword ptr [eax+edx+32*12+4]
		mov ebx,dword ptr [eax+edx+32* 6]
		mov ebx,dword ptr [eax+edx+32*13+4]
		mov ebx,dword ptr [eax+edx+32* 7]
		mov ebx,dword ptr [eax+edx+32*14+4]

		add edx,32*16
		and edx,1024*128-1

		dec ecx
		jnz lp2
	}
	GetTimeStampEnd(memory);

	L2SpeedR=(float)(memory/(1024*16*16));

//
// Timings from main memory (for read)
//
	GetTimeStampStart(memory);
	_asm
	{
		mov eax,BaseAddress
		mov edx,1024*128+8192
		mov ecx,1024*8

lp3:	mov ebx,dword ptr [eax+edx+32* 0]
		mov ebx,dword ptr [eax+edx+32*15+4]
		mov ebx,dword ptr [eax+edx+32* 1]
		mov ebx,dword ptr [eax+edx+32*14+4]
		mov ebx,dword ptr [eax+edx+32* 2]
		mov ebx,dword ptr [eax+edx+32*13+4]
		mov ebx,dword ptr [eax+edx+32* 3]
		mov ebx,dword ptr [eax+edx+32*12+4]		// Read 4Meg of cache lines from past 128k in buffer (directly from main memory)
		mov ebx,dword ptr [eax+edx+32* 4]	
		mov ebx,dword ptr [eax+edx+32*11+4]
		mov ebx,dword ptr [eax+edx+32* 5]
		mov ebx,dword ptr [eax+edx+32*12+4]
		mov ebx,dword ptr [eax+edx+32* 6]
		mov ebx,dword ptr [eax+edx+32*13+4]
		mov ebx,dword ptr [eax+edx+32* 7]
		mov ebx,dword ptr [eax+edx+32*14+4]

		add edx,32*16
		and edx,1024*1024-1

		dec ecx
		jnz lp3
	}
	GetTimeStampEnd(memory);

	MainSpeedR=(float)(memory/(1024*8*16));
//
// Timings from L2 (for write)
//
	_asm
	{
		mov eax,BaseAddress
		add eax,1024*1024
		mov edx,0
lp0:	mov ebx,dword ptr [eax+edx]						// Read 128k of buffer into L2 (L1 contains last 8 or 16k of buffer)
		add edx,32
		cmp edx,128*1024
		jnz lp0
	}

	GetTimeStampStart(memory);
	_asm
	{
		mov eax,BaseAddress
		add eax,1024*1024
		mov edx,0
		mov ecx,1024*16

lp4:	mov dword ptr [eax+edx+32* 0],ecx
		mov dword ptr [eax+edx+32*15+4],ecx
		mov dword ptr [eax+edx+32* 1],ecx
		mov dword ptr [eax+edx+32*14+4],ecx
		mov dword ptr [eax+edx+32* 2],ecx
		mov dword ptr [eax+edx+32*13+4],ecx
		mov dword ptr [eax+edx+32* 3],ecx
		mov dword ptr [eax+edx+32*12+4],ecx
		mov dword ptr [eax+edx+32* 4],ecx
		mov dword ptr [eax+edx+32*11+4],ecx
		mov dword ptr [eax+edx+32* 5],ecx
		mov dword ptr [eax+edx+32*12+4],ecx
		mov dword ptr [eax+edx+32* 6],ecx
		mov dword ptr [eax+edx+32*13+4],ecx
		mov dword ptr [eax+edx+32* 7],ecx
		mov dword ptr [eax+edx+32*14+4],ecx

		add edx,32*16
		and edx,1024*128-1

		dec ecx
		jnz lp4
	}
	GetTimeStampEnd(memory);

	L2SpeedW=(float)(memory/(1024*16*16));
//
// Timings from main memory (but for write)
//
	GetTimeStampStart(memory);
	_asm
	{
		mov eax,BaseAddress
		add eax,1024*1024
		mov edx,1024*128+8192
		mov ecx,1024*8

lp5:	mov dword ptr [eax+edx+32* 0],ecx
		mov dword ptr [eax+edx+32*15+4],ecx
		mov dword ptr [eax+edx+32* 1],ecx
		mov dword ptr [eax+edx+32*14+4],ecx
		mov dword ptr [eax+edx+32* 2],ecx
		mov dword ptr [eax+edx+32*13+4],ecx
		mov dword ptr [eax+edx+32* 3],ecx
		mov dword ptr [eax+edx+32*12+4],ecx
		mov dword ptr [eax+edx+32* 4],ecx
		mov dword ptr [eax+edx+32*11+4],ecx
		mov dword ptr [eax+edx+32* 5],ecx
		mov dword ptr [eax+edx+32*12+4],ecx
		mov dword ptr [eax+edx+32* 6],ecx
		mov dword ptr [eax+edx+32*13+4],ecx
		mov dword ptr [eax+edx+32* 7],ecx
		mov dword ptr [eax+edx+32*14+4],ecx

		add edx,32*16
		and edx,1024*1024-1

		dec ecx
		jnz lp5
	}
	GetTimeStampEnd(memory);

	MainSpeedW=(float)(memory/(1024*8*16));
//
// Timings from L2 (for read/write)
//
	_asm
	{
		mov eax,BaseAddress
		mov edx,0
lp0a:	mov dword ptr [eax+edx],ebx						// Write 128k of buffer into L2 (L1 contains last 8 or 16k of buffer)
		add edx,32
		cmp edx,128*1024
		jnz lp0a
	}

	GetTimeStampStart(memory);
	_asm
	{
		mov eax,BaseAddress
		mov edx,0
		mov ecx,1024*16

lp6:	add dword ptr [eax+edx+32* 0],ecx
		add dword ptr [eax+edx+32*15+4],ecx
		add dword ptr [eax+edx+32* 1],ecx
		add dword ptr [eax+edx+32*14+4],ecx
		add dword ptr [eax+edx+32* 2],ecx
		add dword ptr [eax+edx+32*13+4],ecx
		add dword ptr [eax+edx+32* 3],ecx
		add dword ptr [eax+edx+32*12+4],ecx
		add dword ptr [eax+edx+32* 4],ecx
		add dword ptr [eax+edx+32*11+4],ecx
		add dword ptr [eax+edx+32* 5],ecx
		add dword ptr [eax+edx+32*12+4],ecx
		add dword ptr [eax+edx+32* 6],ecx
		add dword ptr [eax+edx+32*13+4],ecx
		add dword ptr [eax+edx+32* 7],ecx
		add dword ptr [eax+edx+32*14+4],ecx

		add edx,32*16
		and edx,1024*128-1

		dec ecx
		jnz lp6
	}
	GetTimeStampEnd(memory);

	L2SpeedRW=(float)(memory/(1024*16*16));
//
// Timings from main memory (for read/write)
//
	GetTimeStampStart(memory);
	_asm
	{
		mov eax,BaseAddress
		mov edx,1024*128+8192
		mov ecx,1024*8

lp7:	add dword ptr [eax+edx+32* 0],ecx
		add dword ptr [eax+edx+32*15+4],ecx
		add dword ptr [eax+edx+32* 1],ecx
		add dword ptr [eax+edx+32*14+4],ecx
		add dword ptr [eax+edx+32* 2],ecx
		add dword ptr [eax+edx+32*13+4],ecx
		add dword ptr [eax+edx+32* 3],ecx
		add dword ptr [eax+edx+32*12+4],ecx
		add dword ptr [eax+edx+32* 4],ecx
		add dword ptr [eax+edx+32*11+4],ecx
		add dword ptr [eax+edx+32* 5],ecx
		add dword ptr [eax+edx+32*12+4],ecx
		add dword ptr [eax+edx+32* 6],ecx
		add dword ptr [eax+edx+32*13+4],ecx
		add dword ptr [eax+edx+32* 7],ecx
		add dword ptr [eax+edx+32*14+4],ecx

		add edx,32*16
		and edx,1024*1024-1

		dec ecx
		jnz lp7
	}
	GetTimeStampEnd(memory);

	MainSpeedRW=(float)(memory/(1024*8*16));
//
// Check video memory is backbuffer present and in video memory
//
	VidMemR=VidMemW=VidMemRW=0;
	AGPMemR=AGPMemW=AGPMemRW=0;
	
	if( Platform!=Platform_DLL )
	{
		IDirectDrawSurface7*	BackBuffer = NULL;
		IDirectDraw7*			Object7=NULL;
		HRESULT result;
		AllowFail=TRUE;

		result=wDirectDrawCreateEx( NULL, (void**)&Object7, IID_IDirectDraw7, NULL );
		if( SUCCEEDED(result) )
		{
			wSetCooperativeLevel( Object7, hWindow, DDSCL_NORMAL );

			DDSURFACEDESC2 ddsd;
			memset(&ddsd,0,sizeof(ddsd));
			ddsd.dwSize = sizeof(ddsd);
			ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
			ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;
			ddsd.dwWidth = 384;
			ddsd.dwHeight = 384;
			result=wCreateSurface( Object7, &ddsd, &BackBuffer, NULL);
//
// Lock the back buffer to update pixel colors
//
			if( SUCCEEDED(result) )
			{
				DDSURFACEDESC2 BBddsd;
				BBddsd.dwSize = sizeof(BBddsd);
				result=wLock( BackBuffer, NULL, &BBddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL );
				if( SUCCEEDED(result) )
				{
					if( (BBddsd.ddsCaps.dwCaps&DDSCAPS_LOCALVIDMEM) && BBddsd.lpSurface && 0==IsBadReadPtr(BBddsd.lpSurface, 384*384)  )
					{
						BaseAddress=(BYTE*)BBddsd.lpSurface;
//
// Timings from video memory (for read)
//
						GetTimeStampStart(memory);
						_asm
						{
							mov eax,BaseAddress
							mov edx,0
							mov ecx,1024*8

vlp5:						mov ebx,dword ptr [eax+edx+32* 0]
							mov ebx,dword ptr [eax+edx+32*15+4]
							mov ebx,dword ptr [eax+edx+32* 1]
							mov ebx,dword ptr [eax+edx+32*14+4]
							mov ebx,dword ptr [eax+edx+32* 2]
							mov ebx,dword ptr [eax+edx+32*13+4]
							mov ebx,dword ptr [eax+edx+32* 3]
							mov ebx,dword ptr [eax+edx+32*12+4]
							mov ebx,dword ptr [eax+edx+32* 4]
							mov ebx,dword ptr [eax+edx+32*11+4]
							mov ebx,dword ptr [eax+edx+32* 5]
							mov ebx,dword ptr [eax+edx+32*12+4]
							mov ebx,dword ptr [eax+edx+32* 6]
							mov ebx,dword ptr [eax+edx+32*13+4]
							mov ebx,dword ptr [eax+edx+32* 7]
							mov ebx,dword ptr [eax+edx+32*14+4]

							add edx,32*16
							and edx,1024*128-1

							dec ecx
							jnz vlp5
						}
						GetTimeStampEnd(memory);

						VidMemR=(float)(memory/(1024*8*16));
//
// Timings from video memory (for write)
//
						GetTimeStampStart(memory);
						_asm
						{
							mov eax,BaseAddress
							mov edx,0
							mov ecx,1024*8

vlp6:						mov dword ptr [eax+edx+32* 0],ecx
							mov dword ptr [eax+edx+32*15+4],ecx
							mov dword ptr [eax+edx+32* 1],ecx
							mov dword ptr [eax+edx+32*14+4],ecx
							mov dword ptr [eax+edx+32* 2],ecx
							mov dword ptr [eax+edx+32*13+4],ecx
							mov dword ptr [eax+edx+32* 3],ecx
							mov dword ptr [eax+edx+32*12+4],ecx
							mov dword ptr [eax+edx+32* 4],ecx
							mov dword ptr [eax+edx+32*11+4],ecx
							mov dword ptr [eax+edx+32* 5],ecx
							mov dword ptr [eax+edx+32*12+4],ecx
							mov dword ptr [eax+edx+32* 6],ecx
							mov dword ptr [eax+edx+32*13+4],ecx
							mov dword ptr [eax+edx+32* 7],ecx
							mov dword ptr [eax+edx+32*14+4],ecx

							add edx,32*16
							and edx,1024*128-1

							dec ecx
							jnz vlp6
						}
						GetTimeStampEnd(memory);

						VidMemW=(float)(memory/(1024*8*16));
//
// Timings from video memory (for read/write)
//
						GetTimeStampStart(memory);
						_asm
						{
							mov eax,BaseAddress
							mov edx,0
							mov ecx,1024*8

vlp7:						add dword ptr [eax+edx+32* 0],ecx
							add dword ptr [eax+edx+32*15+4],ecx
							add dword ptr [eax+edx+32* 1],ecx
							add dword ptr [eax+edx+32*14+4],ecx
							add dword ptr [eax+edx+32* 2],ecx
							add dword ptr [eax+edx+32*13+4],ecx
							add dword ptr [eax+edx+32* 3],ecx
							add dword ptr [eax+edx+32*12+4],ecx
							add dword ptr [eax+edx+32* 4],ecx
							add dword ptr [eax+edx+32*11+4],ecx
							add dword ptr [eax+edx+32* 5],ecx
							add dword ptr [eax+edx+32*12+4],ecx
							add dword ptr [eax+edx+32* 6],ecx
							add dword ptr [eax+edx+32*13+4],ecx
							add dword ptr [eax+edx+32* 7],ecx
							add dword ptr [eax+edx+32*14+4],ecx

							add edx,32*16
							and edx,1024*128-1

							dec ecx
							jnz vlp7
						}
						GetTimeStampEnd(memory);

						VidMemRW=(float)(memory/(1024*8*16));
//
// Unlock back buffer
//
					}
					wUnlock( BackBuffer, NULL );
				}
				wRelease( BackBuffer );
			}
//
// Now time from AGP memory
//
			memset(&ddsd,0,sizeof(ddsd));
			ddsd.dwSize = sizeof(ddsd);
			ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
			ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM;
			ddsd.dwWidth = 384;
			ddsd.dwHeight = 384;
//	
// Some cards (PCI nVidia) lie about creating AGP surfaces - so check the cap first
//
			DDCAPS TempDDCaps;
			TempDDCaps.dwSize=sizeof(TempDDCaps);
			wGetCaps( Object7, &TempDDCaps, NULL );
			bool HasAGP=0!=(TempDDCaps.dwCaps2 & DDCAPS2_NONLOCALVIDMEM);
			if( HasAGP )
				result=wCreateSurface( Object7, &ddsd, &BackBuffer, NULL);
			else
				result=-1;
//
// Lock the back buffer to update pixel colors
//
			if( SUCCEEDED(result) )
			{
				DDSURFACEDESC2 BBddsd;
				BBddsd.dwSize = sizeof(BBddsd);
				result=wLock( BackBuffer, NULL, &BBddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL );
				if( SUCCEEDED(result) )
				{
					if( (BBddsd.ddsCaps.dwCaps&DDSCAPS_NONLOCALVIDMEM) && BBddsd.lpSurface && 0==IsBadReadPtr(BBddsd.lpSurface,384*384) )
					{
						BaseAddress=(BYTE*)BBddsd.lpSurface;
//
// Timings from video memory (for read)
//
						GetTimeStampStart(memory);
						_asm
						{
							mov eax,BaseAddress
							mov edx,0
							mov ecx,1024*8

avlp5:						mov ebx,dword ptr [eax+edx+32* 0]
							mov ebx,dword ptr [eax+edx+32*15+4]
							mov ebx,dword ptr [eax+edx+32* 1]
							mov ebx,dword ptr [eax+edx+32*14+4]
							mov ebx,dword ptr [eax+edx+32* 2]
							mov ebx,dword ptr [eax+edx+32*13+4]
							mov ebx,dword ptr [eax+edx+32* 3]
							mov ebx,dword ptr [eax+edx+32*12+4]
							mov ebx,dword ptr [eax+edx+32* 4]
							mov ebx,dword ptr [eax+edx+32*11+4]
							mov ebx,dword ptr [eax+edx+32* 5]
							mov ebx,dword ptr [eax+edx+32*12+4]
							mov ebx,dword ptr [eax+edx+32* 6]
							mov ebx,dword ptr [eax+edx+32*13+4]
							mov ebx,dword ptr [eax+edx+32* 7]
							mov ebx,dword ptr [eax+edx+32*14+4]

							add edx,32*16
							and edx,1024*128-1

							dec ecx
							jnz avlp5
						}
						GetTimeStampEnd(memory);

						AGPMemR=(float)(memory/(1024*8*16));
//
// Timings from video memory (for write)
//
						GetTimeStampStart(memory);
						_asm
						{
							mov eax,BaseAddress
							mov edx,0
							mov ecx,1024*8

avlp6:						mov dword ptr [eax+edx+32* 0],ecx
							mov dword ptr [eax+edx+32*15+4],ecx
							mov dword ptr [eax+edx+32* 1],ecx
							mov dword ptr [eax+edx+32*14+4],ecx
							mov dword ptr [eax+edx+32* 2],ecx
							mov dword ptr [eax+edx+32*13+4],ecx
							mov dword ptr [eax+edx+32* 3],ecx
							mov dword ptr [eax+edx+32*12+4],ecx
							mov dword ptr [eax+edx+32* 4],ecx
							mov dword ptr [eax+edx+32*11+4],ecx
							mov dword ptr [eax+edx+32* 5],ecx
							mov dword ptr [eax+edx+32*12+4],ecx
							mov dword ptr [eax+edx+32* 6],ecx
							mov dword ptr [eax+edx+32*13+4],ecx
							mov dword ptr [eax+edx+32* 7],ecx
							mov dword ptr [eax+edx+32*14+4],ecx

							add edx,32*16
							and edx,1024*128-1

							dec ecx
							jnz avlp6
						}
						GetTimeStampEnd(memory);

						AGPMemW=(float)(memory/(1024*8*16));
//
// Timings from video memory (for read/write)
//
						GetTimeStampStart(memory);
						_asm
						{
							mov eax,BaseAddress
							mov edx,0
							mov ecx,1024*8

avlp7:						add dword ptr [eax+edx+32* 0],ecx
							add dword ptr [eax+edx+32*15+4],ecx
							add dword ptr [eax+edx+32* 1],ecx
							add dword ptr [eax+edx+32*14+4],ecx
							add dword ptr [eax+edx+32* 2],ecx
							add dword ptr [eax+edx+32*13+4],ecx
							add dword ptr [eax+edx+32* 3],ecx
							add dword ptr [eax+edx+32*12+4],ecx
							add dword ptr [eax+edx+32* 4],ecx
							add dword ptr [eax+edx+32*11+4],ecx
							add dword ptr [eax+edx+32* 5],ecx
							add dword ptr [eax+edx+32*12+4],ecx
							add dword ptr [eax+edx+32* 6],ecx
							add dword ptr [eax+edx+32*13+4],ecx
							add dword ptr [eax+edx+32* 7],ecx
							add dword ptr [eax+edx+32*14+4],ecx

							add edx,32*16
							and edx,1024*128-1

							dec ecx
							jnz avlp7
						}
						GetTimeStampEnd(memory);

						AGPMemRW=(float)(memory/(1024*8*16));
//
// Unlock back buffer
//
					}
					wUnlock( BackBuffer, NULL );
				}
				wRelease( BackBuffer );
			}
			wRelease( Object7 );
		}
		AllowFail=FALSE;
	}
//
// If the memory test took longer than .5 seconds, restart it.
//
	QueryPerformanceCounter((LARGE_INTEGER*)&qpc);
	if( compare<qpc )
	{
		QueryPerformanceFrequency((LARGE_INTEGER*)&qpc);
		QueryPerformanceCounter((LARGE_INTEGER*)&compare);
		compare+=qpc/2;												// Find time .5 seconds ahead
		GetTimeStampStart(time);
	}
//
// Finish waiting for .5 seconds for MHz measuring
//
	do
	{
		QueryPerformanceCounter((LARGE_INTEGER*)&qpc);			// Spin until time equal
	}
	while( compare>qpc );
	GetTimeStampEnd(time);

	SetThreadPriority(hThread,OldPriority);
	SetPriorityClass(hProcess,OldClass);

	free(TestMemory);

	ProcessorSpeed=(float)(time/500000);						// Round MHz to nearest
	OneOverProcessorSpeed=1.0f/(ProcessorSpeed*1000);			// Used to multiply cycles to get milliseconds

	sprintf( SpeedBuffer, "%dMHz ",float2long(ProcessorSpeed) );
	return;
}
//
// Returns a string of information about the processor
//
char* GetProcessor()
{
	static char Buffer[128];

	if( !HasCPUID )
	{
		int Pro=0;

		_asm{
			mov Pro,0
			pushad
			pushfd
			pop eax
			mov ecx,eax
			xor eax,40000h
			push eax
			popfd
			pushfd
			pop eax
			cmp eax,ecx
			jz Exit					; not a 80486 or above class processor 

			mov Pro,1

			xor ax,ax
			sahf
			mov ax,5
			mov bx,2
			div bl
			lahf
			cmp ah,2
			jne Exit				; flags changed, not a Cyrix CPU 

			mov Pro,2
Exit:
			popad
			}
		if( Pro==0 )
			return "No CPUID - not a 486 or above processor";
		if( Pro==1 )
			return "No CPUID - At least a 486 or better processor";
		if( Pro==2 )
			return "No CPUID - Cyrix 6x86 processor";
	}
//
// Get CPUID info
//
	DWORD Stepping=CPUID1[0]&0xf;			// Stepping
	DWORD Model=CPUID1[0]>>4;				// Model
	DWORD Type=CPUID1[1]&0x3f;			// Type:Family
//
// Get Cache sizes
//
	DWORD L1Code=0;
	DWORD L1Data=0;
	DWORD L2Size=0;

	int Multiplier=(int)CPUID2[0];

	for( int i=1; i<16; i++ )
	{
		switch( CPUID2[i] )
		{
			case 0x06:
				L1Code+=8*Multiplier;
				break;
			case 0x08:
				L1Code+=16*Multiplier;
				break;
			case 0x0a:
				L1Data+=8*Multiplier;
				break;
			case 0x0c:
				L1Data+=16*Multiplier;
				break;
			case 0x41:
				L2Size+=128*Multiplier;
				break;
			case 0x42:
				L2Size+=256*Multiplier;
				break;
			case 0x43:
				L2Size+=512*Multiplier;
				break;
			case 0x44:
				L2Size+=1024*Multiplier;
				break;
			case 0x45:
				L2Size+=2048*Multiplier;
				break;
			case 0x81:
				L2Size+=128*Multiplier;
				break;
			case 0x82:
				L2Size+=256*Multiplier;
				break;
			case 0x83:
				L2Size+=512*Multiplier;
				break;
			case 0x84:
				L2Size+=1024*Multiplier;
				break;
			case 0x85:
				L2Size+=2048*Multiplier;
				break;
		}
	}
	if( 0==strncmp( (char *)&CPUID0+4, "GenuineIntel", 12) )
		sprintf( CacheInformation, "L1: %dK code / %dK data  L2: %dK",L1Code,L1Data,L2Size );
	else
		sprintf( CacheInformation, "Unknown" );
//
// Get processor speed
//
	strcpy( Buffer, SpeedBuffer );
//
// Get the processor name
//
	if( 0==strncmp( (char *)&CPUID0+4, "GenuineIntel", 12) )
	{
		HasINTEL=1;
		if( ((*(DWORD*)&CPUID1[0]) & 0xf00) < 0x400 )
		{													// all 486's
			strcat( Buffer, "Intel 486 " );
		}
		else
		{													// Pentiums
			switch ((*(DWORD*)&CPUID1[0]) & 0x3ff0)
			{
				case SIG_P5:
				case SIG_P5OD:
				case SIG_P5OD486:
					strcat( Buffer, "Intel Pentium (P5) " );
					break;
				case SIG_P5_:
				case SIG_P54C:
				case SIG_P54COD:
					strcat( Buffer, "Intel Pentium (P54) " );
					break;
				case SIG_P55CM:
				case SIG_P55C:
				case SIG_P55COD:
					strcat( Buffer, "Intel Pentium (P55) " );
					break;
				case SIG_P6:
				case SIG_P6OD:
					strcat( Buffer, "Intel Pentium Pro (P6) " );
					break;
				case SIG_P2:
				case SIG_P2XC:
				case SIG_CELA:
					switch (L2Size)
					{
						case 0:			
							strcat( Buffer, "Intel Celeron " );
							break;
						case 131072:	// 128K
							strcat( Buffer, "Intel Celeron-A " );
							break;
						case 262144:	// 256K
						case 524288:	// 512K
							if (L2Size >= 524288)
								strcat( Buffer, "Intel PII (Possibly Xeon) " );
							else
								strcat( Buffer, "Intel PII " );
							break;
						case 1048576:	// 1M
						case 2097152:	// 2M
							strcat( Buffer, "Intel PII-Xeon " );
							break;
						default:
							strcat( Buffer, "Intel PII " );
							break;
					}
					break;
				case SIG_P3:
				case SIG_P3M:
					if (L2Size >= 1048576)
					{
						strcat( Buffer, "Intel PIII-Xeon " );
					}
					else
					{
						if (L2Size >= 524288)
							strcat( Buffer, "Intel PIII (Possibly Xeon) " );
						else
							strcat( Buffer, "Intel PIII " );
					}
					break;
				case 0x0f20:
				{
					strcat( Buffer, "Intel P4 " );
					break;
				}
				
				default:
				{
					char Temp[64];
					sprintf( Temp, "Intel Unknown (0x%x) ",((*(DWORD*)&CPUID1[0]) & 0x3ff0) );
					strcat( Buffer, Temp );
				}
			}
		}	// done with intel
	}
	else if( 0==strncmp ((char *)&CPUID0+4, "AuthenticAMD", 12) )
	{
		HasAMD=1;
		if( (*(DWORD*)&CPUID1[0] & 0xf00) < 0x500 )
		{														// all 486's
			strcat( Buffer, "AMD 486 " );
		}
		else
		{
			switch (*(DWORD*)&CPUID1[0] & 0x0ff0)
			{
				case SIG_K5M0:
				case SIG_K5M1:
				case SIG_K5M2:
				case SIG_K5M3:
					strcat( Buffer, "AMD K5 " );
					break;
				case SIG_K6M6:
				case SIG_K6M7:
					strcat( Buffer, "AMD K6 " );
					break;
				case SIG_K62:
					strcat( Buffer, "AMD K6_2 " );
					break;
				case SIG_K63:
					strcat( Buffer, "AMD K6_3 " );
					break;
				case SIG_K7:
				case SIG_K71:
				case 0x640:
					strcat( Buffer, "AMD Athlon " );
					break;
				default:
				{
					strcat( Buffer, "AMD Unknown " );
				}
			}
		}	// done with amd
	}
	else if( 0==strncmp( (char *)&CPUID0+4, "CyrixInstead", 12) )
	{
		HasCYRIX=1;
		if( *(DWORD*)&CPUID1[0] < 0x440 )
		{													// 486's
			strcat( Buffer, "Cyrix 486 " );
		}
		else if( (*(DWORD*)&CPUID1[0] & 0xf00) < 0x500 )
		{													// MediaGX
			strcat( Buffer, "Cyrix MediaGX " );
		}
		else
		{
			switch( *(DWORD*)&CPUID1[0] & 0x0ff0 )
			{
				case SIG_GXM:
					strcat( Buffer, "Cyrix GXM " );
					break;
				case SIG_M9:
					strcat( Buffer, "Cyrix 5x86 " );
					break;
				case SIG_M1:
					strcat( Buffer, "Cyrix 6x86/L " );
					break;
				case SIG_M2:
					strcat( Buffer, "Cyrix 6x86MX / MII " );
					break;
				case SIG_MX:
					strcat( Buffer, "Cyrix MX " );
					break;
				default:
					strcat( Buffer, "Cyrix Unknown " );
			}
		}
	}
	else if( 0==strncmp( (char *)&CPUID0+4, "CentaurHauls", 12) )
	{
		switch (*(DWORD*)&CPUID1[0] & 0x0ff0)
		{
			case SIG_C6:
				strcat( Buffer, "Centaur C6 (Winchip) " );
				break;
			case SIG_C62:
				strcat( Buffer, "Centaur C6-2 (Winchip2) " );
				break;
			default:
				strcat( Buffer, "Centaur Unknown " );
		}
	}
	else
	{
		strcat( Buffer, "Unknown Chip " );
	}
//
// Check for MMX
//
	if( HasMMX )
		strcat( Buffer, "MMX " );
	else
		strcat( Buffer, "NON-MMX " );
//
// Check for 3DNow
//
	if( Has3DNow )
		strcat( Buffer, "3DNow! " );
//
// Check for Katmai (called SSE)
//
	if( HasKatmai )
		strcat( Buffer, "SSE " );
//
// Append stepping
//
	char StepBuffer[64];
	sprintf( StepBuffer, "Model=%d, Type=%d, Stepping=%d ",Model, Type, Stepping );
	strcat( Buffer, StepBuffer );
//
// Append number of processors
//
	SYSTEM_INFO Si;
	GetSystemInfo(&Si);							// Get the system page size
	if( Si.dwNumberOfProcessors>1 )
	{
		if( 2==Si.dwNumberOfProcessors )
			strcat( Buffer, "(Two Processors) " );
		else if( 3==Si.dwNumberOfProcessors )
			strcat( Buffer, "(Three Processors) " );
		else if( 4==Si.dwNumberOfProcessors )
			strcat( Buffer, "(Four Processors) " );
		else
		{
			char TempBuf[32];
			sprintf( TempBuf, "(%d Processors) ",Si.dwNumberOfProcessors );
			strcat( Buffer, TempBuf );
		}
	}
	return Buffer;
}






//
//
// Get processor details, type, mmx support etc...
//
//
void GetProcessorInfo()
{
	_asm{
	push eax
	push ebx
	push ecx
	push edx

	mov Has3DNow,0
	mov HasCPUID,0
	mov HasMMX,0
	mov HasRDTSC,0

	pushfd
	pop eax
	mov ebx,eax
	xor eax,00200000h
	push eax
	popfd
	pushfd
	pop eax
	cmp eax,ebx					// Use flags to see if CPU support CPUID
	jz Exit

	mov HasCPUID,1

	xor eax,eax
	cpuid
	mov dword ptr CPUID0,eax
	mov dword ptr CPUID0+4,ebx
	mov dword ptr CPUID0+8,edx	// Goofy order is correct ("Genuine Intel")
	mov dword ptr CPUID0+12,ecx
	mov byte ptr CPUID0+16,0

	mov eax,1
	cpuid
	mov dword ptr CPUID1,eax
	mov dword ptr CPUID1+4,ebx
	mov dword ptr CPUID1+8,ecx
	mov dword ptr CPUID1+12,edx

	test edx,(1<<25)			// Check for Katmai instructions
	jz notkatmai
	mov HasKatmai,1

notkatmai:
	test edx,10h
	jz notrdtsc
	mov HasRDTSC,1				// Check for RDTSC instruction

notrdtsc:
	test edx,00800000h
	jz notmmx
	mov HasMMX,1				// Check for MMX instructions

notmmx:
	mov eax,2
	cpuid
	mov dword ptr CPUID2,eax
	mov dword ptr CPUID2+4,ebx	// Get cache information
	mov dword ptr CPUID2+8,ecx
	mov dword ptr CPUID2+12,edx

	mov eax,80000000h
	cpuid
	cmp eax,80000000h			// Check for AMD cpuid extensions
	jbe not3DNow
	mov eax,80000001h
	cpuid
	test edx,80000000h			// Check for 3DNow! instructions
	jz not3DNow
	mov Has3DNow,1

not3DNow:

Exit:
	pop edx
	pop ecx
	pop ebx
	pop eax
	};
//
// Command line switch can disable RDTSC
//
	if( gDisableRDTSC )
		HasRDTSC=0;
//
// In RELEASE builds, disable RDTSC
//
	#ifndef LAB_ONLY
	HasRDTSC=0;
	#endif
}



