#include <sys/types.h>
#include <libcd.h>
#include <libetc.h>
#include <libgte.h>
#include <libgpu.h>

#define TRUE 1 
#define FALSE 0
#define DATA_BUFF (u_char *)0x80100000
#define MAXROM 128 
#define SST_ID 0xBF
#define WIN_ID 0xDA
#define ATM_ID 0x1F
#define ATM_29C010A  0xD5
#define ATM_29LV010A 0x35
#define ATM_29C020   0xDA
#define ATM_29C040A  0xA4
#define ATM_29xV040A 0xC4
#define ATM_29BV020  0xBA
#define SST_29EE010  0x07
#define SST_29xE010  0x08
#define SST_29EE010A 0x22
#define SST_29xE010A 0x23
#define SST_29EE020  0x10
#define SST_29xE020  0x12
#define SST_29EE020A 0x24
#define SST_29xE020A 0x25
#define SST_28SF040  0x04    
#define WIN_29EE01x  0xC1
#define WIN_29C020   0x45
#define WIN_29C040   0x46
#define ARPRO_BANK0 3
#define ARPRO_BANK1 1
#define FX_BANK0    0
#define FX_BANK1    0x20
#define ARPRO 1 
#define FX    2 
#define FX_1  1 
#define FX_2  2 
#define FX_3  3 

struct EEPROM
{
	u_char id1;
	u_char id2;
	u_int  romsize;
	u_int  rowsize;
	u_char ms;
	char   desc[50];
} EepromArray[] = 
{ 
	ATM_ID, ATM_29C010A,  128, 128, 10, "ATMEL 29C010A",
	ATM_ID, ATM_29LV010A, 128, 128, 20, "ATMEL 29LV010A",
	ATM_ID, ATM_29C020,   256, 256, 10, "ATMEL 29C020",
	ATM_ID, ATM_29BV020,  256, 256, 20, "ATMEL 29BV020",
	ATM_ID, ATM_29C040A,  512, 256, 10, "ATMEL 29C040A",
	ATM_ID, ATM_29xV040A, 512, 256, 20, "ATMEL 29xV040A",
	SST_ID, SST_29EE010,  128, 128, 10, "SST 29EE010",
	SST_ID, SST_29xE010,  128, 128, 10, "SST 29xE010",
	SST_ID, SST_29EE010A, 128, 128, 10, "SST 29EE010A",
	SST_ID, SST_29xE010A, 128, 128, 10, "SST 29xE010A",
	SST_ID, SST_29EE020,  256, 128, 10, "SST 29EE020",
	SST_ID, SST_29xE020,  256, 128, 10, "SST 29xE020",
	SST_ID, SST_29EE020A, 256, 128, 10, "SST 29EE020A",
	SST_ID, SST_29xE020A, 256, 128, 10, "SST 2xEE020A",
	SST_ID, SST_28SF040,  512, 256, 00, "SST 28SF040",
	WIN_ID, WIN_29EE01x,  128, 128, 10, "WINBOND 29EE01x",
	WIN_ID, WIN_29C020,   256, 128, 10, "WINBOND 29C020",
	WIN_ID, WIN_29C040,   512, 256, 10, "WINBOND 29C040",
	0,0,0,0,0,"end" 
};

struct ROMFILEDATA
{
	u_long length;
	u_long offset;
	u_long checksum;
	char   desc[50];
} romFileData[MAXROM];

/* Xplorer Communication */
void HandleCommand ();
void CheckComm ();
void pcSendByte (char);
char pcGetByte ();
void pcComm47 ();
void pcComm57 ();
void pcComm55 ();
void pcComm53 ();
void pcComm54 ();
void pcComm10 ();
void pcComm42 ();
void PsxReset ();
u_long GetLong (int);
inline u_char xpGetBit();
void (*xjump)(void);

/* Eeprom Functions */
char FlashNeeded (char *Src, char *Dst);
void FindEepromInfo ();
void EepromWrite (char *Src, char *Dst);
void EepromWrite2nd (char *Src, char *Dst);
void Write_28SF040 (u_char *Src, u_char *Dst);
void Check_Toggle_Ready (char *Dst);
void Check_Data_Polling (char  *Dst, char TrueData);
void DelayMs (u_long ms);
void EepromDetect ();
int EepromDetect3rdGenFX();
void Flash (u_long, u_long);
char ValidateFlash(u_long address,u_long length);
void Enable_Chip_Data_Protection_Asm();
void Disable_Chip_Data_Protection_Asm();

/* Other Functions */
void InitDisp (int, int);
void DrawDisp ();
void Progress (int, int, int, int, int);
void WaitScreen (char *text);
int  LoadROM(int);
void DrawMenuBorder ();
void CopyRom ();
void GetInfoROM ();
void MenuROM ();
void Credits();

char *bankArPro  = (char*)0x1F060030;
char *bankFX = (char*)0x1F060001;
char *addr1 = (char*)0x1F060001;
char *addr2 = (char*)0x1F060002;
char comm57 = FALSE, comms = FALSE;
char loadinfo = TRUE;
char EepromDesc[50];
char special=0,special2=0;
DRAWENV  draw[2];        
DISPENV  disp[2];        
long printID;
int ROW_SIZE, DELAY, id = 0, maxrom, pos = 0;
u_long ROM_SIZE;
char romfile[] = "\\ROMFILE.DAT;1";
u_char rom_id1, rom_id2;

int main()
{
	u_long padRead;
	char result;
	
	InitDisp(512, 240);
	PadInit(0);
	EepromDetect();
	FindEepromInfo();
	
	while(1)
	{
		padRead = PadRead(0);
		if (padRead&PADselect) 
		{ 
			while (PadRead(0)&PADselect); 
			Credits(); 
		}
		
		if (padRead&PADR1)
		{ 
			while (PadRead(0)&PADR1); 
			
			if (comms) 
			{
				comms = FALSE; 
			}
			else 
			{
				comms = TRUE; 
			}
		}
		
		if (padRead&PADR2)
		{ 
			while (PadRead(0)&PADR2); 
			
			EepromDetect(); 
			FindEepromInfo(); 
		}
		
		if (padRead&PADstart)
		{
			while (PadRead(0)&PADstart);
			
			MenuROM();
		}
		
		if (padRead&PADRright) 
		{
			PsxReset();
		}
		
		if (padRead&PADL1)
		{ 
			while (PadRead(0)&PADL1); 
			
			CopyRom(); 
		}
		
		if (padRead&PADL2)
		{ 
			while (PadRead(0)&PADL2); 
			
			CdInit(); 
			CdControl(CdlStop, 0, 0); 
		}
		
		FntPrint(printID,"X-Flash v1.2 (02/07/2000) - BETA 5\n");
		FntPrint(printID,"\n\n\n\nDetected : %s\n\n\n",EepromDesc);
		
		if (comms)
		{
			FntPrint(printID,"    <R1> - Disable comms\n\n");
		}
		else
		{
			FntPrint(printID,"    <R1> - Enable comms\n\n");
		}
		
		FntPrint(printID,"    <R2> - Detect EEPROM\n\n");
		FntPrint(printID,"    <L1> - Copy eeprom to main mem (512kb)\n\n");
		FntPrint(printID," <START> - ROM selection menu\n\n");
		FntPrint(printID,"<SELECT> - Credits\n\n");
		FntPrint(printID,"     <O> - Reset Playstation\n\n");
		FntPrint(printID,"\n\n(c) Tim Schuerewegen / FireFly\n\n");
		FntPrint(printID,"E-Mail : Tim.Schuerewegen@village.uunet.be\n\n");
		FntPrint(printID,"Homepage : http://www.cherryroms.com/firefly\n\n");
		
		CheckComm();
		DrawDisp();
	}
	
	return 0;
}

u_char ToHex(u_char x)
{
	if (x < 10)
	{
		x = x + 48;
	}
	else
	{
		x = x + 55;
	}
	
	return x;
}

void FindEepromInfo()
{
	u_char i, found = FALSE, ok;
	
	special = 0;
	i=0;
	
	while ((EepromArray[i].id1!=0) && (!found))
	{
		if ((EepromArray[i].id1==rom_id1) && (EepromArray[i].id2==rom_id2))
		{
			found = TRUE;
			ROW_SIZE = EepromArray[i].rowsize;
			ROM_SIZE = EepromArray[i].romsize * 1024;
			DELAY = EepromArray[i].ms;
			sprintf(EepromDesc,"%s - %d KB",EepromArray[i].desc,EepromArray[i].romsize);
			
			if (rom_id2==SST_28SF040)
			{
				special = ARPRO;
				strcat(EepromDesc," (AR/GS Pro)");
			}
			
			if (rom_id2==ATM_29C040A)
			{
				special = FX;
				special2 = FX_1;
				strcat(EepromDesc," (Xplorer FX 2)");
			}
			
			if (rom_id2==WIN_29C040)
			{
				special = FX;
				special2 = FX_2;
				strcat(EepromDesc," (Xplorer FX 1)");
			}
			
			if ( (rom_id2==SST_29xE020) || (rom_id2==SST_29EE020) )
			{
				WaitScreen("Possible 3rd gen Xplorer FX found");
				ok = EepromDetect3rdGenFX();
				
				if (ok)
				{
					strcat(EepromDesc," x 2 (Xplorer FX 3)");
					special = FX;
					special2 = FX_3;
					ROM_SIZE = ROM_SIZE * 2;
				}
				else
				{
					strcat(EepromDesc," (Xplorer)");
				}
			}
		}
		i++;
	}
	
	if (!found)
	{
		sprintf(EepromDesc,"UNKNOWN EEPROM - %02X %02X",rom_id1,rom_id2);
		ROW_SIZE = 0;
	}
}

void Credits()
{
	u_long padRead;
	
	do
	{
		padRead = PadRead(0);
		FntPrint(printID,"                          AUTHOR\n");
		FntPrint(printID,"                          ------\n\n");
		FntPrint(printID,"                     Tim Schuerewegen\n\n");
		FntPrint(printID,"\n");
		FntPrint(printID,"                       BETA TESTERS\n");
		FntPrint(printID,"                       ------------\n\n");
		FntPrint(printID,"                           Neal\n\n");
		FntPrint(printID,"                        Melonhead\n\n");
		FntPrint(printID,"                       Nachbrenner\n\n");
		FntPrint(printID,"                        Snakebite\n\n");
		FntPrint(printID,"\n");
		FntPrint(printID,"                      SPECIAL THANKS\n");
		FntPrint(printID,"                      --------------\n\n");
		FntPrint(printID,"                    Fire International\n\n");
		FntPrint(printID,"                           Blaze\n\n");
		FntPrint(printID,"\n\n");
		FntPrint(printID,"                 Press <SELECT> to go back\n");
		DrawDisp();
	} 
	while (!(padRead&PADselect));
  
	while (PadRead(0)&PADselect);
}


void MenuROM ()
{
	u_long padRead, time;
	int romsel=0, i, x;
	char arrow[5];
	char title[]="                 X-Flash ROM selection menu\n\n\n", text[100];
	
	if (loadinfo)
	{
		DrawMenuBorder();
		FntPrint(printID,title);
		FntPrint(printID,"\n\n\n\n                        Please wait\n");
		DrawDisp();
		DrawMenuBorder();
		FntPrint(printID,title);
		FntPrint(printID,"\n\n\n\n                        Please wait\n");
		DrawDisp();
		CdInit();
		GetInfoROM();
		loadinfo = FALSE;
	}
	
	do
	{
		DrawMenuBorder();
		padRead = PadRead(0);
		
		if ( (padRead&PADLdown) && (romsel<maxrom-1) )
		{
			time = 0;
			
			while ((PadRead(0)&PADLdown) && (time++<20000));
			
			if (PadRead(0)&PADLdown) 
			{
				romsel++;
			}
		}
		
		if ( (padRead&PADLup) && (romsel>0) )
		{
			time = 0;
			
			while ((PadRead(0)&PADLup) && (time++<20000));
			
			if (PadRead(0)&PADLup) 
			{
				romsel--;
			}
		}
		
		if ( (padRead&PADR2) && (romsel>0) )
		{
			while (PadRead(0)&PADR2);
			
			romsel = romsel - 10;
			
			if (romsel<0) 
			{
				romsel = 0;
			}
		}
		
		if ( (padRead&PADR1) && (romsel<maxrom-1) )
		{
			while (PadRead(0)&PADR1);
			
			romsel = romsel + 10;
			
			if (romsel>maxrom-1) 
			{
				romsel = maxrom-1;
			}
		}
		
		if ((padRead&PADRdown))
		{
			EepromDetect();
			FindEepromInfo();
			
			while (PadRead(0)&PADRdown);
			
			if (ROW_SIZE == 0)
			{
				WaitScreen("Eeprom not detected !");
			}
			else if (ROM_SIZE < romFileData[romsel].length)
			{
				WaitScreen("Selected ROM file is too big for the detected eeprom !");
			}
			else
			{  
				i = LoadROM(romsel);
				
				if (i==1) 
				{
					Flash((u_long)DATA_BUFF,romFileData[romsel].length);
				}
			}
		}
		
		FntPrint(printID,title);
		
		for (i=0; i<10; i++)
		{
			x = i;
			
			if (romsel>9) 
			{
				x=i+(romsel-9);
			}
			
			if (romsel==x) 
			{
				strcpy(arrow,"-->"); 
			}
			else 
			{
				strcpy(arrow,"   ");
			}
			
			if ( (maxrom==0) && (i==4) )
			{
				FntPrint(printID,"                      No ROM's found !\n");
			}
			else
			{
				if (x<maxrom)
				{
					FntPrint(printID,"  %s %d%d  %s\n",arrow,(x+1)/10,(x+1)%10,romFileData[x].desc);
				}
				else
				{
					FntPrint(printID,"\n");
				}
			}
		}
		
		FntPrint(printID,"\n\n\n ROM TOTAL : %d\n\n",maxrom);
		FntPrint(printID," Press <START> to exit this menu\n");
		
		if (maxrom>0) 
		{
			FntPrint(printID,"\n Press <X> to upgrade your cartridge with the selected ROM\n");
		}
		
		DrawDisp();
	} 
	while (!(padRead&PADstart));
	
	while (PadRead(0)&PADstart);
}

void WaitScreen (char *text)
{
	u_long padRead;
	
	do
	{
		padRead = PadRead(0);
		FntPrint(printID,text);
		FntPrint(printID,"\n\nPress <X> to continue");
		DrawDisp();
		CheckComm();
	} 
	while (!(padRead&PADRdown));
	
	while (PadRead(0)&PADRdown);
}

void DrawMenuBorder()
{
	TILE t;
	int hor=500, ver=160, bx=10, by=7, top=45;
	int r=50, g=150, b=150;
	
	SetTile (&t);
	setXY0 (&t,20,top); 
	
	t.w = hor-40; 
	t.h = by;
	
	setRGB0 (&t,r,g,b);
	DrawPrim (&t);
	
	setXY0 (&t,20,ver-20); 
	t.w = hor-40; 
	t.h = by;
	
	setRGB0 (&t,r,g,b);
	
	DrawPrim (&t);
	
	setXY0 (&t,hor-20,top); 
	
	t.w = bx; 
	t.h = ver - 20 - top + by;
	
	setRGB0 (&t,r,g,b);
	
	DrawPrim (&t);
	
	setXY0 (&t,20,top); 
	
	t.w = bx; 
	t.h = ver-20-top;
	
	setRGB0 (&t,r,g,b);
	
	DrawPrim (&t);
}

void DrawDisp()
{
	id = id ? 0: 1;
	
	if (draw[id].dfe)
	{ 
		DrawSync(0); 
		VSync(0); 
	}
	else
	{ 
		VSync(0); 
		ResetGraph(1); 
	}
	
	PutDrawEnv(&draw[id]);
	PutDispEnv(&disp[id]);
	FntFlush(printID);
}

void InitDisp(int w, int h)
{
	if (*(char *)0xbfc7ff52=='E')
	{
		SetVideoMode(MODE_PAL);
	}
	else
	{
		SetVideoMode(MODE_NTSC);
	}
	
	ResetGraph(0);
	
	SetDefDrawEnv(&draw[0], 0, 0, w, h);
	SetDefDrawEnv(&draw[1], 0, h, w, h);
	SetDefDispEnv(&disp[0], 0, h, w, h);
	SetDefDispEnv(&disp[1], 0, 0, w, h);
	
	draw[0].isbg = draw[1].isbg = 1;
	
	setRGB0(&draw[0], 0, 0, 0);
	setRGB0(&draw[1], 0, 0, 0); 
	
	FntLoad(960, 256);
	printID = FntOpen(16, 32, w, h, 0, 2048);
	SetDumpFnt(printID);
	SetDispMask(1);
}

void Progress(int x, int y, int w, int h, int value)
{
	TILE t;
	char d = 2;
	
	SetTile (&t);
	setXY0 (&t, x, y); 
	
	t.w = w; 
	t.h = h;
	
	setRGB0 (&t,255,255,255);
	DrawPrim (&t);
	
	setXY0 (&t,x+d,y+d); 
	
	t.w = w-d*2; 
	t.h = h-d*2;
	
	setRGB0 (&t,0,0,0);
	DrawPrim (&t);
	
	setXY0 (&t,x+d,y+d); 
	
	t.w = (w-d*2)*value/100; 
	t.h = h-d*2;
	
	setRGB0 (&t,255,128,64);
	DrawPrim (&t);
}

void CopyRom()
{
	char *src = (char*)0x1F000000;
	char *dst = DATA_BUFF, val;
	u_long i, padval;
  
	if (special==ARPRO)
	{
		*bankArPro = ARPRO_BANK0; 
		DelayMs(100);
		
		for (i=0;i<0x40000;i++) 
		{
			*dst++ = *src++;
		}
		
		*bankArPro = ARPRO_BANK1; 
		DelayMs(100);
		src = (char*)0x1F000000;
		
		for (i=0;i<0x40000;i++) 
		{
			*dst++ = *src++;
		}
		
		*bankArPro = ARPRO_BANK0; DelayMs(100);
	}
	
	if (special==FX)
	{
		*bankFX = FX_BANK0; DelayMs(100);
		
		for (i=0;i<0x60000;i++) 
		{
			*dst++ = *src++;
		}
		
		*bankFX = FX_BANK1; DelayMs(100);
		src = (char*)0x1F040000;
		
		for (i=0;i<0x20000;i++) 
		{
			*dst++ = *src++;
		}
		
		*bankFX = FX_BANK0; DelayMs(100);
	}
	
	if (special==0)
	{
		for (i=0;i<0x80000;i++) 
		{
			*dst++ = *src++;
		}
	}
}

void Flash (u_long address, u_long length)
{
	char *flashrom, *data, flashok;
	u_long x, padread;
	int new,i;
	char text[100];
	
	if ((special==FX) && (special2==FX_3) && (length>0x60000)) 
	{
		length=0x60000;
	}
	
	do
	{
		flashok = TRUE;
		flashrom = (char*)0x1F000000;
		data = (char*)address;
		x=0;
		
		if (special==ARPRO) 
		{
			*bankArPro = ARPRO_BANK0;
		}
		
		if (special==FX) 
		{
			*bankFX = FX_BANK0;
		}
		
		while (x<length)
		{
			new = (x*100/length)+1;
			
			if ((new%10)==0)
			{
				FntPrint(printID,"\n\n\n                       UPDATING EEPROM");
				Progress(50,80,412,20,new);
				DrawDisp();
			}
			
			if ((special==ARPRO) && ((u_long)flashrom==0x1F040000))
			{
				*bankArPro = ARPRO_BANK1; DelayMs(100);
				flashrom = (char*)0x1F000000;
			}
			
			if ((special==FX) && ((u_long)flashrom==0x1F060000))
			{
				*bankFX = FX_BANK1; DelayMs(100);
				flashrom = (char*)0x1F040000;
			}
			
			for (i=0;i<5;i++)
			{
				if (FlashNeeded(data,flashrom))
				{
					if (special==ARPRO)
					{
						Write_28SF040 (data,flashrom);
					}
					else if ((special==FX) && (special2==FX_3) && 
							((u_long)flashrom>=0x1F040000))
					{
						EepromWrite2nd (data,flashrom);
					}
					else
					{
						EepromWrite (data,flashrom);
					}
				}
			}
			
			if (flashok) 
			{
				flashok = !FlashNeeded(data,flashrom);
			}
			
			flashrom += ROW_SIZE;
			data += ROW_SIZE;
			x += ROW_SIZE;
		}
		
		if (!flashok)
		{
			do
			{
				FntPrint(printID,"\n\n UPDATE WAS NOT SUCCESFULL !!\n");
				FntPrint(printID,"\n Press <X> to abort");
				FntPrint(printID,"\n Press <O> to retry");
				
				DrawDisp();
				padread = PadRead(0);
			}
			while ( (!(padread&PADRdown)) && (!(padread&PADRright)));
			
			if (padread&PADRright)
			{
				while (PadRead(0)&PADRright);
			}
			
			if (padread&PADRdown) 
			{ 
				while (PadRead(0)&PADRdown); 
				
				flashok = TRUE; 
			}
		}
	}
	while (!flashok);
	
	if (special==ARPRO)
	{
		*bankArPro = ARPRO_BANK0;
	}
	
	if (special==FX) 
	{
		*bankFX = FX_BANK0;
	}
}

void EepromDetect()
{
	u_char *Temp = (u_char*) 0x1F000000;
	
	Temp[0x5555] = 0xAA;
	Temp[0x2AAA] = 0x55;
	Temp[0x5555] = 0x90;
	DelayMs(100);
	
	rom_id1 = Temp[0];
	rom_id2 = Temp[1];
	
	Temp[0x5555] = 0xAA;
	Temp[0x2AAA] = 0x55;
	Temp[0x5555] = 0xF0;
	DelayMs(100);
}

int EepromDetect3rdGenFX()
{
	char txt[50];
	u_char found,xid1,xid2;
	char *src1 = (char*)0x1F000000;
	char *src2 = (char*)0x1F040000;
	int diff = FALSE, ret = FALSE;
	unsigned long i;
  
	for (i=0;(i<0x20000) && (!diff);i++)
	{
		if (src1[i] != src2[i])
		{
			diff = TRUE;
		}
	}
	
	if (diff)
	{
		WaitScreen("First 3rd gen Xplorer check OK");
		src2[0x5555] = 0xAA;
		src2[0x2AAA] = 0x55;
		src2[0x5555] = 0x90;
		
		DelayMs(100);
		
		xid1 = src2[0];
		xid2 = src2[1];
		
		src2[0x5555] = 0xAA;
		src2[0x2AAA] = 0x55;
		src2[0x5555] = 0xF0;
		
		DelayMs(100);
		
		sprintf(txt,"EEPROM %02X %02X",xid1,xid2);
		WaitScreen(txt);
		
		if ( ((xid1==SST_ID) && (xid2==SST_29xE020)) ||
			( (xid1==SST_ID) && (xid2==SST_29EE020))) 
		{
			ret = TRUE;
		}
		
		if (ret == TRUE) 
		{
			WaitScreen("Second 3rd gen Xplorer check OK");
		}
	}
	
	return ret;
}

void DelayMs(u_long ms)
{
	u_long x,y;
	
	for (x=0; x<ms; x++);
	
	for (y=0; y<500; y++);
}

void EepromWrite2nd (char *Src, char *Dst)
{
	char *Temp, *SourceBuf, *DestBuf;
	int Index;
	
	SourceBuf = Src;
	DestBuf = Dst;
	
	if (ROW_SIZE==0x80)
	{
		Temp =  (char*)0x1F045555;  *Temp = 0xAA;
		Temp =  (char*)0x1F042AAA;  *Temp = 0x55;
		Temp =  (char*)0x1F045555;  *Temp = 0xA0;
		
		for (Index = 0; Index < 0x80; Index++)
		{
			*DestBuf++ = *SourceBuf++;
		}
		
		DelayMs(1);
		Check_Toggle_Ready(Dst);
	}
	
	if (ROW_SIZE==0x100)
	{
		Temp =  (char*)0x1F045555;  *Temp = 0xAA;
		Temp =  (char*)0x1F042AAA;  *Temp = 0x55;
		Temp =  (char*)0x1F045555;  *Temp = 0xA0;
		
		for (Index = 0; Index < 0x100; Index++)
		{
			*DestBuf++ = *SourceBuf++;
		}
		
		DelayMs(1);
		Check_Toggle_Ready(Dst);
	}
}

void EepromWrite (char *Src, char *Dst)
{
	char *Temp, *SourceBuf, *DestBuf;
	int Index;
	
	SourceBuf = Src;
	DestBuf = Dst;
	
	if (ROW_SIZE==0x80)
	{
		Temp =  (char*)0x1F005555;  *Temp = 0xAA;
		Temp =  (char*)0x1F002AAA;  *Temp = 0x55;
		Temp =  (char*)0x1F005555;  *Temp = 0xA0;
		
		for (Index = 0; Index < 0x80; Index++)
		{
			*DestBuf++ = *SourceBuf++;
		}
		
		DelayMs(1);
		Check_Toggle_Ready(Dst);
	}
	
	if (ROW_SIZE==0x100)
	{
		Temp =  (char*)0x1F005555;  *Temp = 0xAA;
		Temp =  (char*)0x1F002AAA;  *Temp = 0x55;
		Temp =  (char*)0x1F005555;  *Temp = 0xA0;
		
		for (Index = 0; Index < 0x100; Index++)
		{
			*DestBuf++ = *SourceBuf++;
		}
		
		DelayMs(1);
		Check_Toggle_Ready(Dst);
	}
}

void Write_28SF040 (u_char *Src, u_char *Dst)
{
	u_char *Temp, *SourceBuf, *DestBuf, TempByte, ProgrammedByte, SourceByte;
	int Index, count;
	int Continue;
	
	SourceBuf = Src;
	DestBuf = Dst;
	
	Check_Toggle_Ready(DestBuf);
	Disable_Chip_Data_Protection_Asm();
	*(u_long*)0xFFF00 = (u_long)DestBuf;
	
	asm ("
			subiu $29, 8
			sw    $4, 0($29)
			sw    $8, 4($29)
			lui   $4, 0x000F
			ori   $4, 0xFF00
			lw    $4, ($4)
			li    $8, 0x0020
			sb    $8, ($4)
			li    $8, 0x00D0
			sb    $8, ($4)
			lw    $8, 4($29)
			lw    $4, 0($29)
			addiu $29, 8
		");
	
	Check_Toggle_Ready(DestBuf);
	
	count = 0;
	Continue = TRUE;
	
	while ((count < ROW_SIZE) && (Continue))
	{
		SourceByte = *DestBuf++;
		
		if (SourceByte==0xFF)
		{
			count++;
		}
		else
		{
			Continue = FALSE;
		}
	}
	
	if (Continue)
	{
		SourceBuf = Src;
		DestBuf = Dst;
		
		for (Index = 0; Index < ROW_SIZE; Index++)
		{
			SourceByte = *SourceBuf++;
			
			if (SourceByte!=0xFF)
			{
				*(u_long*)0xFFF00 = (u_long)DestBuf;
				*(u_char*)0xFFF04 = (u_char)SourceByte;
				
				asm ("
						subiu $29, 12
						sw    $4, 0($29)
						sw    $8, 4($29)
						sw    $9, 8($29)
						lui   $4, 0x000F
						ori   $4, 0xFF00
						lbu   $9, 4($4)
						lw    $4, 0($4)
						li    $8, 0x10
						sb    $8, ($4)
						sb    $9, ($4)
						lw    $9, 8($29)
						lw    $8, 4($29)
						lw    $4, 0($29)
						addiu $29, 12
					");
				
				Check_Toggle_Ready(DestBuf);
				ProgrammedByte = *DestBuf;
				
				if (SourceByte != ProgrammedByte)
				{
					Continue = FALSE;
					break;
				}
			}
			DestBuf++;
		}
	}
	Enable_Chip_Data_Protection_Asm();
}


void Enable_Chip_Data_Protection_Asm()
{
	asm ("
			lui   $3, 0x1f00
			lbu   $2, 0x1823($3)
			lbu   $2, 0x1820($3)
			lbu   $2, 0x1822($3)
			lbu   $2, 0x0418($3)
			lbu   $2, 0x041b($3)
			lbu   $2, 0x0419($3)
			lbu   $2, 0x040a($3)
		");
}

void Disable_Chip_Data_Protection_Asm()
{
	asm (" 
			lui   $3, 0x1f00
			lbu   $2, 0x1823($3)
			lbu   $2, 0x1820($3)
			lbu   $2, 0x1822($3)
			lbu   $2, 0x0418($3)
			lbu   $2, 0x041b($3)
			lbu   $2, 0x0419($3)
			lbu   $2, 0x041a($3)
		");
}


char FlashNeeded (char *Src, char *Dst)
{
	char *SourceBuf, *DestBuf, diff=FALSE;
	int Index;
	
	SourceBuf = Src;
	DestBuf = Dst;
	
	for (Index = 0; Index < ROW_SIZE; Index++)
	{
		if (*DestBuf++ != *SourceBuf++) 
		{
			diff = TRUE;
		}
	}
	
	return diff;
}

void Check_Toggle_Ready (char *Dst)
{
	u_char Loop = TRUE, PreData, CurrData;
	u_long TimeOut = 0;
	
	PreData = *Dst & 0x40;
	
	while ((TimeOut< 0x07FFFFFF) && (Loop))
	{
		CurrData = *Dst & 0x40;
		
		if (PreData == CurrData)
		{
			Loop = FALSE;
		}
		
		PreData = CurrData;
		TimeOut++;
	}
	
	if (TimeOut==0x07FFFFFF) 
	{
		WaitScreen("TOGGLE TIME-OUT");
	}
}



int LoadROM (int nr)
{
	CdlFILE cdi;
	char result;
	u_long cs,i;
	
	CdIntToPos(pos + (romFileData[nr].offset/2048),&cdi.pos);
	CdControl(CdlSetloc,(u_char*)&cdi.pos,0);
	CdSync(0,&result);
	CdRead((romFileData[nr].length/2048)+1,(u_long*)DATA_BUFF,0);
	CdReadSync(0,&result);
	cs = 0;
	
	for (i=0;i<romFileData[nr].length;i++) 
	{
		cs += *(DATA_BUFF+i);
	}
	
	if (cs!=romFileData[nr].checksum)
	{
		WaitScreen("Checksum error !");
		
		return 0;
	}
	
	return 1;
}


void GetInfoROM()
{
	CdlFILE cdi,*t;
	char result;
	u_long i, maxaddress;
	
	maxrom = 0;
	t = CdSearchFile(&cdi,romfile);
	
	if (t != 0)
	{
		pos = CdPosToInt(&cdi.pos);
		CdControl(CdlSetloc,(u_char*)&cdi.pos,0);
		CdSync(0,&result);
		CdRead(4,(u_long*)DATA_BUFF,0); // read 4 sectors = 4 * 2048 = 8192 bytes
		CdReadSync(0,&result);
		maxaddress = *(u_long*)(DATA_BUFF+4);
		
		for (i=0;i<maxaddress;i+=0x40)
		{
			romFileData[maxrom].length = *(u_long*)(DATA_BUFF+i+0);
			romFileData[maxrom].offset = *(u_long*)(DATA_BUFF+i+4);
			romFileData[maxrom].checksum = *(u_long*)(DATA_BUFF+i+8);
			strcpy(romFileData[maxrom].desc,DATA_BUFF+i+0x10);
			
			if (romFileData[maxrom].length != 0) 
			{
				maxrom++;
			}
		}
	}
}





//*********************************************************************************************
// Xplorer Communication Routines
//*********************************************************************************************
void CheckComm()
{
	if (comms && (xpGetBit()==0))
	{
		if (!comm57)
		{
			if (*addr1==0x57)
			{
				HandleCommand();
				comm57 = TRUE;
			}
		}
		else
		{
			switch (*addr1)
			{
				case 0x57 : 
					pcComm57(); 
					comm57 = FALSE; 
					break;
				
				case 0x47 : 
					pcComm47(); 
					comm57 = FALSE; 
					break;
				
				case 0x55 : 
					pcComm55(); 
					comm57 = FALSE; 
					break;
				
				case 0x54 : 
					pcComm54(); 
					comm57 = FALSE; 
					break;
				
				case 0x53 : 
					pcComm53(); 
					comm57 = FALSE; 
					break;
				
				case 0x10 : 
					pcComm10(); 
					comm57 = FALSE; 
					break;
				
				case 0x42 : 
					pcComm42(); 
					comm57 = FALSE; 
					break;
				
				default : 
					break;
			}
		}
	}
}

void HandleCommand()
{
	*addr1 = 8;
	
	while (!xpGetBit());
	
	*addr1 = 0;
}

void pcComm57()
{
	*addr1 = 8;
	
	while (!xpGetBit());
	
	pcSendByte(0xF1);
}

void pcComm42()
{
	HandleCommand();
	PsxReset();
}

void PsxReset()
{
	PadStop();
	ResetGraph(3);
	
	xjump = (void*)0xBFC00000;
	xjump();
}

void pcComm10()
{
	u_long temp1, temp2, temp3, temp4;
	char byte, *addr3;
	
	HandleCommand();
	temp1 = GetLong(4);
	temp2 = GetLong(4);
	temp3 = GetLong(4);
	temp4 = GetLong(4);
	PadStop();
	ResetGraph(3);
	
	SetSp(temp3);
	xjump = (void*)temp4;
	xjump();
}

void pcComm47()
{
	u_long addr, length, i, cs1=0, cs2;
	char byte;
	char *temp;
	
	HandleCommand();
	addr = GetLong(4);
	length = GetLong(4);
	temp = (char*)addr;
	
	for (i=0;i<length;i++)
	{
		byte = *temp++;
		pcSendByte(byte);
		cs1 = cs1 + byte;
	}
	
	cs2 = pcGetByte();
	pcSendByte( (cs1>>8) & 0xFF );
	cs2 = cs2 << 8;
	cs2 = cs2 +  pcGetByte();
	pcSendByte ( cs1 & 0xFF);
	pcSendByte('O');
	pcSendByte('K');
}

void pcComm54()
{
/*  u_long addr, length, i, cs1=0, cs2;
  char byte;
  char *temp;

  HandleCommand();
  addr = GetLong(4);
  length = GetLong(4);
  for (i=0;i<1000;i++);
  while (*addr1 != 0);    *addr1 = 4;
  while (*addr1 != 0xEC); *addr1 = 8; */
}

void pcComm53()
{
	u_long addr, length, i, cs1=0, cs2;
	char byte;
	char *temp;
	
	HandleCommand();
	addr = GetLong(4);
	length = GetLong(4);
	temp = (char*)addr;
	
	for (i=0;i<length;i++)
	{
		byte = pcGetByte();
		*temp++ = byte;
		cs1 = cs1 + byte;
	}
	
	cs1 = cs1 & 0xFFFF;
	cs2 = pcGetByte();
	pcSendByte( (cs1>>8) & 0xFF );
	cs2 = cs2 << 8;
	cs2 = cs2 +  pcGetByte();
	pcSendByte ( cs1 & 0xFF);
	pcSendByte('O');
	pcSendByte('K');
}

u_long GetLong(int size)
{
	char i;
	u_long val=0;
	
	for (i=0;i<size;i++)
	{
		val = (val * 256) + pcGetByte();
	}
	
	return (val);
}

void pcComm55()
{
	u_long addr, length, i, cs1=0, cs2;
	char byte;
	char *temp;
	
	HandleCommand();
	addr = GetLong (4);
	length = GetLong (4);
	
	temp = (char*)addr;
	
	for (i=0;i<length;i++)
	{
		byte = pcGetByte();
		*temp++ = byte;
		cs1 = cs1 + byte;
	}
	
	cs1 = cs1 & 0xFFFF;
	cs2 = pcGetByte();
	pcSendByte( (cs1>>8) & 0xFF );
	cs2 = cs2 << 8;
	cs2 = cs2 +  pcGetByte();
	pcSendByte ( cs1 & 0xFF);
	
	if (cs1==cs2) 
	{
		Flash(addr,length);
	}
	
	pcSendByte('O');
	pcSendByte('K');
}

void pcSendByte(char byte)
{
	char temp;
	
	temp = ((byte>>6)&3) + 4;
	*addr1 = temp;
	*addr1 = temp + 8;
	
	while (xpGetBit()!=0);
	
	temp = (byte>>3)&7;
	*addr1 = temp + 8;
	*addr1 = temp;
	
	while (xpGetBit()==0);
	
	temp = (byte&7);  
	*addr1 = temp;
	*addr1 = temp + 8; 
	
	while (xpGetBit()!=0);
	
	*addr1 = 1;
	
	while (xpGetBit()==0);
}

char pcGetByte()
{
	char byte;
	
	while (xpGetBit()!=0);
	
	byte = *addr1;
	*addr1 = 8;
	
	while (xpGetBit()==0);
	
	*addr1 = 0;
	
	return byte;
}

inline u_char xpGetBit()
{  
	return ( (*addr2 & 1)^1 );
}

