#include "pch.hpp"
//==========================================================================//
// File:	 PERF.cpp														//
// Project:	 GameOS															//
// Contents: VxD performance register API									//
//--------------------------------------------------------------------------//
// Date		Who		Modification											//
// 06/28/99	MJL		Created													//
//--------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.										//
// All Rights reserved worldwide											//
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL				//
//==========================================================================//

#include "perf.h"

extern char AssetsDirectory1[MAX_PATH];
extern char AssetsDirectory2[MAX_PATH];

//========================================================================
// external functions used in this module
//========================================================================
extern HANDLE CreateVXD(void);
extern BOOL KillVXD(void);

//========================================================================
// VxD functions used
//========================================================================
#define GameOSVXD_WriteMSR	1
#define GameOSVXD_ReadMSR	2

//========================================================================
// machine specific registers
//========================================================================
#define MSR_PERFEVTSEL0			0x0186
#define MSR_PERFEVTSEL1			0x0187
#define MSR_PERFCTR0			0x00C1
#define MSR_PERFCTR1			0x00C2
#define MSR_CESR				0x0011
#define MSR_CTR0				0x0012
#define MSR_CTR1				0x0013

								
//========================================================================
// performance events with unit masks
//========================================================================
#define PERF_EVENT_L2_LD		0x029
#define PERF_EVENT_L2_ST		0x02A
#define PERF_UNIT_MASK_L2_LD	0x000


//========================================================================
// flags for perf monitoring events
//========================================================================
#define PERF_DECODE_ADDREG		1
#define	PERF_DECODE_COUNT1		3
#define	PERF_DECODE_COUNT2		4


//========================================================================
// performance parameters and macros for readability
//========================================================================
#define PERF_RPL_NONE			0x0
#define PERF_RPL_USR			0x1
#define PERF_RPL_OS				0x2
#define PERF_RPL_ALL			0x3
								
#define UNIT_MASK(x)			(x)

#define PERF_EDGE_DETECT		0x1
#define PERF_EDGE_IGNORE		0x0

#define PERF_PINCONTROL_ON		0x1
#define PERF_PINCONTROL_OFF		0x0

#define PERF_OVERFLOW_INTERRUPT	0x1
#define PERF_OVERFLOW_IGNORE	0x0

#define PERF_ENABLE				0x1
#define PERF_DISABLE			0x0

#define PERF_INVERT				0x1
#define PERF_NOINVERT			0x0

#define PERF_CTR_MASK(x)		(x)

//========================================================================
// CPU types that affect performance register reading
//========================================================================
enum PERF_CPUTYPE
{
	PERF_CPU_OTHER = 0,
	PERF_CPU_P5,
	PERF_CPU_P6,
};


//========================================================================
// performance control MSR macros
//========================================================================
#define MAKE_PERFEVTSEL(event,unitmask,rpl,edge,pin,interrupt,enable,invert,ctrmask) (\
	((event & 0xFF) << 0) | \
	((unitmask & 0xFF) << 8) | \
	((rpl & 0x3) << 16) | \
	((edge & 0x1) << 18) | \
	((pin & 0x1) << 19) | \
	((interrupt & 0x1) << 20) | \
	((0 & 0x1) << 21) | \
	((enable & 0x1) << 22) | \
	((invert & 0x1) << 23) | \
	((ctrmask & 0xFF) << 24))


//========================================================================
// machine specific register structure
//========================================================================
#pragma pack(4)
typedef struct tagMSR
{
	DWORD	m_dwMSR;
	DWORD	m_dwValueHi;
	DWORD	m_dwValueLo;
} MSR;
#pragma pack()

//========================================================================
// PERFEVENT structure, which defines an event that can be monitored.
//
// Each PERFEVENT object includes a description, information about which
// cpu it runs on, the MSR's that program the event, the event to program,
// the MSR's that represent the counters themselves (which are needed
// at initialization to clear it), and flags.
//========================================================================
typedef struct tagPERFEVENT
{
	char *			m_szDescription;
	DWORD			m_dwFlags;
	PERF_CPUTYPE	m_cputype;
	DWORD			m_dwMsr0;
	DWORD			m_dwVal0;
	DWORD			m_dwMsr1;
	DWORD			m_dwVal1;
	DWORD			m_dwMsrCtr0;
	DWORD			m_dwMsrCtr1;
} PERFEVENT;


//========================================================================
// static variables
//========================================================================
static HANDLE s_hMutex					= NULL;
static HANDLE s_hDevice					= NULL;
static const char s_szPerfMutexName[]	= "GameOS_CoreTech_Performance_Monitoring_Mutex";
static PERF_CPUTYPE s_cputype			= PERF_CPU_OTHER;
static DWORD s_dwHowToDecode			= 0;

//========================================================================
// the performance monitoring event table, listing everything that
// we can optionally monitor.
//
// right now, there are no P5 events listed, and as such, if you want
// to watch any performance registers, you must be on a P6.
//========================================================================
static PERFEVENT s_PerfEvents[] = {
	{	"L1 Data references",
		PERF_DECODE_COUNT1,
		PERF_CPU_P6,
		MSR_PERFEVTSEL0,
		MAKE_PERFEVTSEL(
			0x43,
			PERF_UNIT_MASK_L2_LD,
			PERF_RPL_ALL,
			PERF_EDGE_IGNORE,
			PERF_PINCONTROL_OFF,
			PERF_OVERFLOW_IGNORE,
			PERF_ENABLE,
			PERF_NOINVERT,
			PERF_CTR_MASK(0)),
		MSR_PERFEVTSEL1,
		MAKE_PERFEVTSEL(
			PERF_EVENT_L2_LD,
			PERF_UNIT_MASK_L2_LD,
			PERF_RPL_ALL,
			PERF_EDGE_IGNORE,
			PERF_PINCONTROL_OFF,
			PERF_OVERFLOW_IGNORE,
			PERF_ENABLE,
			PERF_NOINVERT,
			PERF_CTR_MASK(0)),
		MSR_PERFCTR0,
		MSR_PERFCTR1,
	},
	{	"Misaligned data references",
		PERF_DECODE_COUNT1,
		PERF_CPU_P6,
		MSR_PERFEVTSEL0,
		MAKE_PERFEVTSEL(
			0x5,
			PERF_UNIT_MASK_L2_LD,
			PERF_RPL_ALL,
			PERF_EDGE_IGNORE,
			PERF_PINCONTROL_OFF,
			PERF_OVERFLOW_IGNORE,
			PERF_ENABLE,
			PERF_NOINVERT,
			PERF_CTR_MASK(0)),
		MSR_PERFEVTSEL1,
		MAKE_PERFEVTSEL(
			PERF_EVENT_L2_LD,
			PERF_UNIT_MASK_L2_LD,
			PERF_RPL_ALL,
			PERF_EDGE_IGNORE,
			PERF_PINCONTROL_OFF,
			PERF_OVERFLOW_IGNORE,
			PERF_ENABLE,
			PERF_NOINVERT,
			PERF_CTR_MASK(0)),
		MSR_PERFCTR0,
		MSR_PERFCTR1,
	},
	{	"Executed floating point operations",
		PERF_DECODE_COUNT1,
		PERF_CPU_P6,
		MSR_PERFEVTSEL0,
		MAKE_PERFEVTSEL(
			0x10,
			PERF_UNIT_MASK_L2_LD,
			PERF_RPL_ALL,
			PERF_EDGE_IGNORE,
			PERF_PINCONTROL_OFF,
			PERF_OVERFLOW_IGNORE,
			PERF_ENABLE,
			PERF_NOINVERT,
			PERF_CTR_MASK(0)),
		MSR_PERFEVTSEL1,
		MAKE_PERFEVTSEL(
			PERF_EVENT_L2_LD,
			PERF_UNIT_MASK_L2_LD,
			PERF_RPL_ALL,
			PERF_EDGE_IGNORE,
			PERF_PINCONTROL_OFF,
			PERF_OVERFLOW_IGNORE,
			PERF_ENABLE,
			PERF_NOINVERT,
			PERF_CTR_MASK(0)),
		MSR_PERFCTR0,
		MSR_PERFCTR1,
	},
	{	"L2 data bus waiting cycles",				PERF_DECODE_COUNT1,PERF_CPU_P6,MSR_PERFEVTSEL0,MAKE_PERFEVTSEL(0x22,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFEVTSEL1,MAKE_PERFEVTSEL(PERF_EVENT_L2_LD,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFCTR0,MSR_PERFCTR1 },
	{	"L2 data bus transfer cycles",				PERF_DECODE_COUNT1,PERF_CPU_P6,MSR_PERFEVTSEL0,MAKE_PERFEVTSEL(0x23,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFEVTSEL1,MAKE_PERFEVTSEL(PERF_EVENT_L2_LD,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFCTR0,MSR_PERFCTR1 },
	{	"L2 data bus allocated lines",				PERF_DECODE_COUNT1,PERF_CPU_P6,MSR_PERFEVTSEL0,MAKE_PERFEVTSEL(0x24,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFEVTSEL1,MAKE_PERFEVTSEL(PERF_EVENT_L2_LD,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFCTR0,MSR_PERFCTR1 },
	{	"L2 data bus allocated modified lines",		PERF_DECODE_COUNT1,PERF_CPU_P6,MSR_PERFEVTSEL0,MAKE_PERFEVTSEL(0x25,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFEVTSEL1,MAKE_PERFEVTSEL(PERF_EVENT_L2_LD,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFCTR0,MSR_PERFCTR1 },
	{	"L2 data bus removed lines",				PERF_DECODE_COUNT1,PERF_CPU_P6,MSR_PERFEVTSEL0,MAKE_PERFEVTSEL(0x26,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFEVTSEL1,MAKE_PERFEVTSEL(PERF_EVENT_L2_LD,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFCTR0,MSR_PERFCTR1 },
	{	"L2 data bus removed modified lines",		PERF_DECODE_COUNT1,PERF_CPU_P6,MSR_PERFEVTSEL0,MAKE_PERFEVTSEL(0x27,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFEVTSEL1,MAKE_PERFEVTSEL(PERF_EVENT_L2_LD,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFCTR0,MSR_PERFCTR1 },

	{	"L2 instruction feteches",					PERF_DECODE_COUNT1,PERF_CPU_P6,MSR_PERFEVTSEL0,MAKE_PERFEVTSEL(0x28,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFEVTSEL1,MAKE_PERFEVTSEL(PERF_EVENT_L2_LD,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFCTR0,MSR_PERFCTR1 },
	{	"L2 loads",									PERF_DECODE_COUNT1,PERF_CPU_P6,MSR_PERFEVTSEL0,MAKE_PERFEVTSEL(0x29,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFEVTSEL1,MAKE_PERFEVTSEL(PERF_EVENT_L2_LD,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFCTR0,MSR_PERFCTR1 },
	{	"L2 stores",								PERF_DECODE_COUNT1,PERF_CPU_P6,MSR_PERFEVTSEL0,MAKE_PERFEVTSEL(0x2A,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFEVTSEL1,MAKE_PERFEVTSEL(PERF_EVENT_L2_LD,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFCTR0,MSR_PERFCTR1 },
	{	"MMX instructions executed",				PERF_DECODE_COUNT1,PERF_CPU_P6,MSR_PERFEVTSEL0,MAKE_PERFEVTSEL(0xb0,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFEVTSEL1,MAKE_PERFEVTSEL(PERF_EVENT_L2_LD,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFCTR0,MSR_PERFCTR1 },
	{	"Branches predicted",						PERF_DECODE_COUNT1,PERF_CPU_P6,MSR_PERFEVTSEL0,MAKE_PERFEVTSEL(0xc4,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFEVTSEL1,MAKE_PERFEVTSEL(0xc9,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFCTR0,MSR_PERFCTR1 },
	{	"Branches mispredicted",					PERF_DECODE_ADDREG,PERF_CPU_P6,MSR_PERFEVTSEL0,MAKE_PERFEVTSEL(0xc5,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFEVTSEL1,MAKE_PERFEVTSEL(0xca,PERF_UNIT_MASK_L2_LD,PERF_RPL_ALL,PERF_EDGE_IGNORE,PERF_PINCONTROL_OFF,PERF_OVERFLOW_IGNORE,PERF_ENABLE,PERF_NOINVERT,PERF_CTR_MASK(0)),MSR_PERFCTR0,MSR_PERFCTR1 },
	{	"Multiplies executed",
		PERF_DECODE_COUNT2,
		PERF_CPU_P6,
		MSR_PERFEVTSEL0,
		MAKE_PERFEVTSEL(
			0x12,
			PERF_UNIT_MASK_L2_LD,
			PERF_RPL_ALL,
			PERF_EDGE_IGNORE,
			PERF_PINCONTROL_OFF,
			PERF_OVERFLOW_IGNORE,
			PERF_ENABLE,
			PERF_NOINVERT,
			PERF_CTR_MASK(0)),
		MSR_PERFEVTSEL1,
		MAKE_PERFEVTSEL(
			0x12,
			PERF_UNIT_MASK_L2_LD,
			PERF_RPL_ALL,
			PERF_EDGE_IGNORE,
			PERF_PINCONTROL_OFF,
			PERF_OVERFLOW_IGNORE,
			PERF_ENABLE,
			PERF_NOINVERT,
			PERF_CTR_MASK(0)),
		MSR_PERFCTR0,
		MSR_PERFCTR1,
	},
	{	"Divides executed",
		PERF_DECODE_COUNT2,
		PERF_CPU_P6,
		MSR_PERFEVTSEL0,
		MAKE_PERFEVTSEL(
			0x13,
			PERF_UNIT_MASK_L2_LD,
			PERF_RPL_ALL,
			PERF_EDGE_IGNORE,
			PERF_PINCONTROL_OFF,
			PERF_OVERFLOW_IGNORE,
			PERF_ENABLE,
			PERF_NOINVERT,
			PERF_CTR_MASK(0)),
		MSR_PERFEVTSEL1,
		MAKE_PERFEVTSEL(
			0x13,
			PERF_UNIT_MASK_L2_LD,
			PERF_RPL_ALL,
			PERF_EDGE_IGNORE,
			PERF_PINCONTROL_OFF,
			PERF_OVERFLOW_IGNORE,
			PERF_ENABLE,
			PERF_NOINVERT,
			PERF_CTR_MASK(0)),
		MSR_PERFCTR0,
		MSR_PERFCTR1,
	},
};




//========================================================================
// Grab a mutex to prevent multiple apps from accessing the VxD at
// the same time, load the VxD, make sure the CPU is valid, etc.
//========================================================================
HRESULT WINAPI InitPerformanceMonitorLibrary(void)
{
	// make sure we're not already initialized
	if (s_hMutex)
	{
		return PERF_ERROR_ALREADYINITIALIZED;
	}

	// get a handle to the performance mutex
	s_hMutex = CreateMutex(NULL, FALSE, s_szPerfMutexName);
	if (!s_hMutex)
	{
		return PERF_ERROR_CANTCREATEMUTEX;
	}

	// see if we can get the mutex
	if (WaitForSingleObject(s_hMutex, 0) == WAIT_TIMEOUT)
	{
		// some other app owns the performance monitoring
		CloseHandle(s_hMutex);
		s_hMutex = NULL;
		return PERF_ERROR_OWNED_BY_ANOTHER_APP;
	}

	// get the processor type
	__asm
	{
		// see if we can execute the CPUID intruction
		pushfd							; push extended flags
		pop		eax						; store eflags into eax
		mov		ebx, eax				; save EBX for testing later
		xor		eax, (1<<21)			; switch bit 21
		push	eax						; push eflags
		popfd							; pop them again
		pushfd							; push extended flags
		pop		eax						; store eflags into eax
		cmp		eax, ebx				; see if bit 21 has changed
		jz		no_cpuid				; make sure it's now on

		// make sure we are intel
		mov		eax, 0					; maximum CPUID func
		cpuid							; get values
		cmp		ebx, 'uneG'				; make sure ebx equals
		jne		no_cpuid				; 'Genu'
		cmp		edx, 'Ieni'				; make sure edx equals
		jne		no_cpuid				; 'ineI'
		cmp		ecx, 'letn'				; make sure ecx equals
		jne		no_cpuid				; 'ntel'
		cmp		eax, 1					; max CPUID param
		jl		no_cpuid				; can't do it

		// get family
		mov		eax, 1					; get version info
		cpuid							; get values
		lea		ebx, s_cputype			; prepare to store cpu type
		and		eax, 0x0F00				; just want family
		cmp		eax, 0x0600				; P6
		jz		label_P6				; go to P6 area
		cmp		eax, 0x0500				; P5
		jnz		no_cpuid				; not even a P5
		and		edx, 1<<23				; make sure P5 has MMX
		jz		no_cpuid				; no MMX means no ring-3 readable perf counters
		mov		[ebx], PERF_CPU_P5		; store P5 information
		jmp		label_ok				; we're good to go

label_P6:
		mov		[ebx], PERF_CPU_P6		; store P6 information

label_ok:

	}

	s_hDevice = CreateVXD();

	if( s_hDevice == INVALID_HANDLE_VALUE )
	{
		s_hDevice = NULL;
		TermPerformanceMonitorLibrary();
		return PERF_ERROR_CANT_LOAD_VXD;
	}

	return S_OK;

no_cpuid:
	TermPerformanceMonitorLibrary();
	return PERF_ERROR_INVALID_CPU;
}

//========================================================================
//========================================================================
HRESULT WINAPI TermPerformanceMonitorLibrary(void)
{
	HRESULT hr;

	if (s_hMutex)
	{
		ReleaseMutex(s_hMutex);
		CloseHandle(s_hMutex);
		s_hMutex = NULL;
		hr = S_OK;
	}
	else
		hr = PERF_ERROR_NOTINITIALIZED;

	s_cputype = PERF_CPU_OTHER;

	if (s_hDevice)
	{
		KillVXD();
		CloseHandle(s_hDevice);
		s_hDevice = NULL;
	}

	return hr;
}

//========================================================================
//========================================================================
HRESULT WINAPI EnumeratePerformanceMonitors(PERFMONCALLBACK lpfCallback)
{
	if (!s_hMutex)
		return PERF_ERROR_NOTINITIALIZED;

	for (int i=0 ; i<sizeof(s_PerfEvents)/sizeof(s_PerfEvents[0]) ; i++)
	{
		if (s_PerfEvents[i].m_cputype == s_cputype)
		{
			if (FAILED(lpfCallback(s_PerfEvents[i].m_szDescription, s_PerfEvents[i].m_dwFlags, i)))
				break;
		}
	}

	return S_OK;
}

//========================================================================
// pass in an id passed to your enumeration
//========================================================================
HRESULT WINAPI SetPerformanceMonitor(DWORD dwId)
{
	if (!s_hMutex)
		return PERF_ERROR_NOTINITIALIZED;

	if (s_cputype != s_PerfEvents[dwId].m_cputype)
		return PERF_ERROR_INVALID_CPU;

	DWORD dwSize;
	MSR msr;

	msr.m_dwMSR		= s_PerfEvents[dwId].m_dwMsr0;
	msr.m_dwValueHi	= 0;
	msr.m_dwValueLo	= s_PerfEvents[dwId].m_dwVal0;

	if (!DeviceIoControl(s_hDevice, GameOSVXD_WriteMSR, (LPVOID)&msr, sizeof(msr), (LPVOID)&msr, sizeof(msr), &dwSize, NULL))
		return PERF_ERROR_SET_FAILED;

	msr.m_dwMSR		= s_PerfEvents[dwId].m_dwMsr1;
	msr.m_dwValueHi	= 0;
	msr.m_dwValueLo	= s_PerfEvents[dwId].m_dwVal1;

	if (!DeviceIoControl(s_hDevice, GameOSVXD_WriteMSR, (LPVOID)&msr, sizeof(msr), (LPVOID)&msr, sizeof(msr), &dwSize, NULL))
		return PERF_ERROR_SET_FAILED;

	msr.m_dwMSR		= s_PerfEvents[dwId].m_dwMsrCtr0;
	msr.m_dwValueHi	= 0;
	msr.m_dwValueLo	= 0;

	if (!DeviceIoControl(s_hDevice, GameOSVXD_WriteMSR, (LPVOID)&msr, sizeof(msr), (LPVOID)&msr, sizeof(msr), &dwSize, NULL))
		return PERF_ERROR_SET_FAILED;

	msr.m_dwMSR		= s_PerfEvents[dwId].m_dwMsrCtr1;
	msr.m_dwValueHi	= 0;
	msr.m_dwValueLo	= 0;

	if (!DeviceIoControl(s_hDevice, GameOSVXD_WriteMSR, (LPVOID)&msr, sizeof(msr), (LPVOID)&msr, sizeof(msr), &dwSize, NULL))
		return PERF_ERROR_SET_FAILED;

	s_dwHowToDecode=s_PerfEvents[dwId].m_dwFlags;
	
	return S_OK;
}


//
// Given a pointer to the two performance registers, return a double with the 'result'
//
double DecodePerformanceMonitors( __int64 *Registers )
{
	double result=0;

	switch( s_dwHowToDecode )
	{
		case PERF_DECODE_ADDREG:
			result=(double)(Registers[0]+Registers[1]);
			break;
		case PERF_DECODE_COUNT1:
			result=(double)Registers[0];
			break;
		case PERF_DECODE_COUNT2:	
			result=(double)Registers[1];
			break;
	}
	return result;
}



//========================================================================
//========================================================================
BOOL WINAPI ReadPerformanceMonitorRegisters(__int64 *result)
{
	if (!s_hMutex || !result)
		return FALSE;

	__asm
	{
		mov		esi, result				; esi points to result

		xor		ecx,ecx					; first counter
		rdpmc
		mov		[esi+ 0], eax
		mov		[esi+ 4], edx

		mov		ecx, 1					; second counter
		rdpmc
		mov		[esi+ 8], eax
		mov		[esi+12], edx
	}

	return TRUE;
}



