/***************************************************************************************************************************
VMM written by GogoAckman - 03/06/05 
last update 18/10/05

explication :
This VMM base is a LIFO stack, it would be compliated to say which frame is more used, there would more and more code for an
incertain result and it would probably be slower.
- bool indic[256] indicates if the frame is in RAM or on file on HD (you ll remark we are limited (for optimization reasons
to 256*pagesize on this VMM, mostly on neogeo this VMM is used for graphics roms (8*8MB = 64Mb = 256*256K)
- uint8 adress[256] indicates where on the file is our bloc (the char* + adress may be considered as char [adress]) - 256 max
thus 0 to 255, uint8 is enough
- plusgrand is the pointer of the stack, it starts from numberoframes - 1 available on RAM and decrease then return to 
numberoframes - 1
- adfix[256] is a pointer to the number of the adress, that way we don't need to find it with a for (256 max thus 0 to 255)
- optimization is based on timing, when turned on, the swap will not be used when the frame has reached a critical point of
time (1frame*(40/100)), it is used only on uint8* code because it is the one used while ingame

the rest is understandable easily
****************************************************************************************************************************/
#include <xtl.h>
#include <stdio.h>

#define uint8  unsigned __int8
#define uint16 unsigned __int16
#define uint32 unsigned __int32
#define uint64 unsigned __int64

#include "memdumper.h"

unsigned char *donnees;
static bool indic[256];
static uint8 adress[256];
static uint8 adfix[256];
bool missframe = false;
//
uint32 pagesize;  // 256*1024 -> uint32
uint16 nombreframes; // 256 max -> uint8 = 255 max -> uint16
uint8 powerof2;
uint8 powerof2files;
uint8 findpowerof2(uint32 pagesize);

extern LARGE_INTEGER qwLastTime;
extern FLOAT fSecsPerTick;
LARGE_INTEGER qwTimevmm,qwElapsedTimevmm;
FLOAT m_fElapsedTimevmm;
FLOAT m_fMaxTime = ((1.0f*3.0f)/(5.0f*60.0f));

HANDLE g_temp;

char g_PageFileName[32];
extern "C" bool PagingActive = false;
static uint8 plusgrand;
static DWORD dwread;
extern "C" int dprintf(char *format, ...);
extern void* osd_malloc(int n);

void InitSpritePaging(char *szPageFile, uint32 size , uint32 pageSize, uint16 noFrames)
{ 
	//dprintf("InitSpritePaging Start\n");
	//dprintf("szPageFile:  %s\n", szPageFile);
	MEMORYSTATUS memStat;
	uint32 phys;
	uint16 availphys;
	GlobalMemoryStatus(&memStat);
	phys=memStat.dwAvailPhys/1024;
	availphys = phys-(1024);     
	uint16 noMaxFrames = size/pageSize;
	XSetFileCacheSize(256*1024);

	if (noFrames == 76)
	{
		//if (pagingkilled) {
		//	noFrames=(availphys/(pageSize/1024))+10;  // so 2048 + 512K still available - seems ok
		//}
		//else {
			//if (nCurrentFilter == 14)
			//	noFrames=(availphys/(pageSize/1024))-5;  // so 2048 + 512K still available - seems ok
			//else
				noFrames=(availphys/(pageSize/1024))-4;  // so 2048 + 512K still available - seems ok
		//}
				//dprintf("noFrames: %d\n", noFrames);
				//dprintf("noMaxFrames: %d\n", noMaxFrames);
		if (noFrames > noMaxFrames) noFrames = noMaxFrames;
	}

	nombreframes = noFrames;
	pagesize = pageSize;
	
	//dprintf("nombreframes 1: %d\n", nombreframes);
	int e = sizeof(*donnees)  *  nombreframes * (pagesize);
	MEMORYSTATUS stat;
	GlobalMemoryStatus(&stat);
	unsigned int t = (stat.dwAvailPhys - (4 * 1024 * 1024));
	while(e > t) {
		nombreframes--;
		e = sizeof(*donnees)  *  nombreframes * (pagesize);
	}
	//dprintf("nombreframes 2: %d\n", nombreframes);
	

	strcpy(g_PageFileName, szPageFile);
	//dprintf("InitSpritePaging End\n");
}

void InitPageAndFrameTables()
{
	//dprintf("InitPageAndFrameTables Start\n");
	PagingActive = true;
	g_temp = CreateFile(g_PageFileName,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_READONLY,NULL);
	donnees = (uint8 *) osd_malloc(sizeof(*donnees)  *  nombreframes * (pagesize));

	// all indication frame to false

	for (uint16 i=0;i<256;i++){ 
		indic[i]=false;
	}
	// copy first frames
	for (uint16 i=0;i<nombreframes;i++){  
		SetFilePointer(g_temp,(i*pagesize),NULL,FILE_BEGIN);
		ReadFile(g_temp,(donnees+(i*pagesize)),(pagesize),&dwread,NULL);
		indic[i] = true;
		adress[i] = i;
		adfix[i] = i;
	} 
	powerof2 = findpowerof2(pagesize);
	plusgrand = nombreframes-1;
	//dprintf("InitPageAndFrameTables End\n");
}

uint8 findpowerof2(uint32 pagesize) 
{
	uint8 power=0;
	for (int i=0;;i++){
		pagesize = pagesize/2;
		if (pagesize==0) break;
		power++;
	}
	return power;
}
uint8 *ReadUBYTEFromROM(uint32 location)
{
	uint8 numero = (location >> powerof2);  // 255 max
	uint32 adresstemp = (location - (numero << powerof2 ));
	// see if it is on RAM or in the file
	if (indic[numero] == false) {
		// adress of frame to change (FILO stack)
		adress[numero] = plusgrand;
		// change indication and adressfix
		indic[adfix[plusgrand]] = false;
		indic[numero]=true;
		adfix[plusgrand]=numero;
		// copy
		SetFilePointer(g_temp,(numero << powerof2),NULL,FILE_BEGIN);
		ReadFile(g_temp,(donnees+(plusgrand << powerof2)),(pagesize),&dwread,NULL);
		// action sur plusgrand - boucle décrémentation
		plusgrand--; if(plusgrand > (nombreframes-1)) plusgrand = nombreframes-1;
		//
	}
	// return information
	return (uint8*) (donnees + (adress[numero] << powerof2) + adresstemp);
}

uint8 ReadUBYTEFromROM2(uint32 location)
{
	uint8 numero = (location >> powerof2);  // 255 max
	uint32 adresstemp = (location - (numero << powerof2 ));
	// see if it is on RAM or in the file
	if (indic[numero] == false) {
		// adress of frame to change (FILO stack)
		adress[numero] = plusgrand;
		// change indication and adressfix
		indic[adfix[plusgrand]] = false;
		indic[numero]=true;
		adfix[plusgrand]=numero;
		// copy
		SetFilePointer(g_temp,(numero << powerof2),NULL,FILE_BEGIN);
		ReadFile(g_temp,(donnees+(plusgrand << powerof2)),(pagesize),&dwread,NULL);
		// action sur plusgrand - boucle décrémentation
		plusgrand--; if(plusgrand > (nombreframes-1)) plusgrand = nombreframes-1;
		//
	}
	// return information
	return *(uint8*) (donnees + (adress[numero] << powerof2) + adresstemp);
}

uint32 ReadUWORDFromROM(uint32 location)
{
	uint8 numero = (location >> powerof2);  // 255 max
	uint32 adresstemp = (location - (numero << powerof2 ));
	// see if it is on RAM or in the file
	if (indic[numero] == false) {
		// adress of frame to change (FILO stack)
		adress[numero] = plusgrand;
		// change indication and adressfix
		indic[adfix[plusgrand]] = false;
		indic[numero]=true;
		adfix[plusgrand]=numero;
		// copy
		SetFilePointer(g_temp,(numero << powerof2),NULL,FILE_BEGIN);
		ReadFile(g_temp,(donnees+(plusgrand << powerof2)),(pagesize),&dwread,NULL);
		// action sur plusgrand - boucle décrémentation
		plusgrand--; if(plusgrand > (nombreframes-1)) plusgrand = nombreframes-1;
		//
	}
	// return information
	return *(uint32*) (donnees + (adress[numero] << powerof2) + adresstemp);
}


uint32 *ReadUWORDFromROM2(uint32 location)
{
	uint8 numero = (location >> powerof2);  // 255 max
	uint32 adresstemp = (location - (numero << powerof2 ));
	// see if it is on RAM or in the file
	if (indic[numero] == false) {
		// adress of frame to change (FILO stack)
		adress[numero] = plusgrand;
		// change indication and adressfix
		indic[adfix[plusgrand]] = false;
		indic[numero]=true;
		adfix[plusgrand]=numero;
		// copy
		SetFilePointer(g_temp,(numero << powerof2),NULL,FILE_BEGIN);
		ReadFile(g_temp,(donnees+(plusgrand << powerof2)),(pagesize),&dwread,NULL);
		// action sur plusgrand - boucle décrémentation
		plusgrand--; if(plusgrand > (nombreframes-1)) plusgrand = nombreframes-1;
		//
	}
	// return information
	return (uint32*) (donnees + (adress[numero] << powerof2) + adresstemp);
}

void KillSpritePaging(bool bKeepFile)
{
	if (donnees) {
		free(donnees);
		donnees = NULL;
	}
	PagingActive = false;
	if (g_temp)
	{
		CloseHandle(g_temp);
		g_temp = NULL;
		if (!bKeepFile)
			DeleteFile(g_PageFileName);
	}
}