#include "glover.h"

int   _ramsize=0x00200000; /*  2MB RAM for production Playstation   */
int _stacksize=0x00008000; /* 16KB stack is more than enough        */

unsigned char shakeScreen = 0;
int xOffset[2];
int yOffset[2];

UBYTE	scr_toggle=0;
SHORT	scrcenterx=0;
SHORT	scrcentery=0;
SHORT	screenwidth;
SHORT	screenheight;
RGBCD  	clscol={0,0,0,GPU_COM_F4};
UBYTE *PacketBuildList;

OBJECTPOLYLIST *defaultpolylist=0;
OBJECTPOLYLIST *CurrentPolyList=0;
GsOTA *PolyList;

char too_many_polys_flag = 0;

volatile LONG finisheddrawing;

#define CHECKGPU NO

ULONG stack;
ULONG shouldcrash=0;
ULONG stacktmp;
ULONG stacklimit=64;

LONG mainlooptimerbase=0;
LONG mainlooptimer=0;

DRAWENV		drawenv[2];			/* drawing environment */
DISPENV		dispenv[2];			/* display environment */
int		projection=PROJECTION1;
int		cinemascopeToggle=FALSE;

#if PALMODE==NO
	int		yScale=NTSC_TV;
#else
	int		yScale=PAL_TV;
#endif

volatile int watchdog=0;

int interrupt_cyclebar = 0;

/*****************************************************************************/
ULONG	frame;	/* frame counter */
ULONG	activeframe;	/* frame counter that pauses when paused */

//int scratchpadsize=0;
int  	oldSp;
volatile ULONG	vblank = 0;
volatile ULONG	iframe = 0;
volatile ULONG	ovblank=0;
USHORT	framerate = 1;
int oldstack;
int		stackinfastmode=NO;
int		frameLock = 3;	//2;	// Sorry folks, but we need to be realistic about how far this is going to optimise in the next 3 days...
//int		showProgress=0, currProgress=0;
/***********************************************************************************/
void rts(){}
/***********************************************************************************/
void Check_Stack(UBYTE num)
{
LONG j=0;
	while ((*((ULONG*)(DCACHE+SCRATCHPADSIZE+(j*4)))==0)&&(j<(255-SCRATCHPADSIZE/4)))j++;
	*((ULONG*)(DCACHE+(SCRATCHPADSIZE)+(j*4)))=0;
	*((ULONG*)(DCACHE+(SCRATCHPADSIZE)+((j-1)*4)))=0;
	if (j<32)DB("MAX FAST STACK SPACE USED %d, legal is %d,\n",(DCACHETOP-DCACHE-(SCRATCHPADSIZE))-j,DCACHETOP-DCACHE-(SCRATCHPADSIZE)); 
}
/***********************************************************************************/

void
Start_Loop(){
	TIMER_START(TIMER_TOTAL);
  //	 TIMER_START(TIMER_DRAWSYNC);
	TIMER_START(TIMER_GPU)
	too_many_polys_flag = 0;
	 modelctrl.polysdrawn=0;
	modelctrl.preclipped=0;
//	if (*((ULONG*)(STACKBASE+24))!=0){debugPrintf("slow stack close!\n");*((ULONG*)(STACKBASE+24))=0;} //allow for 4k stack
  //	if (*dodgeypolys!=0){debugPrintf("polys ran over illegally!\n");}
	scr_toggle=(scr_toggle^1)&1;
	GsOUT_PACKET_P=(UBYTE*)PacketBuildList+(scr_toggle*PACKETMAX);
	Do_ObjectPolyList(defaultpolylist);

	padHandler();

// In-Cameo control-disable, that was here, is now in gameflow.

}
/***********************************************************************************/
void
Start_Poly_Lists(){
		polylist.dblbuf=scr_toggle; //^1;
}
/***********************************************************************************/
void MyClearScreen(void)
{
	POLY_F4 *si = (POLY_F4 *) GsOUT_PACKET_P; 
	register	GsOTA 	*otptr=(GsOTA*)(PolyList->org+MAXPOLYDEPTH);

	if(TOOMANYPOLYS(sizeof(POLY_F4),"CLEAR SCREEN"))return;

	*(ULONG*)&si->r0=*(ULONG*)&clscol.r;
	si->x0=si->x2=0-320;
	si->x1=si->x3=640-320;
	si->y0=si->y1=0-120-PALMODE*8;
	si->y2=si->y3=240+(PALMODE*8)-120;

	PUTPACKETINTABLE(si,otptr,POLYF4_LEN);
	GsOUT_PACKET_P+=sizeof(POLY_F4);
}
/***********************************************************************************/
void endpoly()
{
	#if (GOLDCD==NO) && (RELEASE==NO)
		Check_Stack(0);
	#endif
  	if (clscol.r!=254) 		MyClearScreen();

 	PolyList=&defaultpolylist->Wot[defaultpolylist->dblbuf];
	
   	DrawSyncCallback(&enddrawing);
  	finisheddrawing=NO;

   DrawOTag((ULONG*)(PolyList->end));

   CurrentPolyList=defaultpolylist;
   PolyList=&defaultpolylist->Wot[defaultpolylist->dblbuf];
}					   
/***********************************************************************************/
void enddrawing(){
	finisheddrawing=YES;
#if (CHECKGPU==YES)
ULONG j;
		SCREENOFF
		for (j=0;j<1000;j++)rts();
		SCREENON
#endif

		 TIMER_STOP(TIMER_GPU);
}
/****************************/
void
solidscreenonoff(){
LONG j;							
	SCREENOFF
   	for (j=0;j<1000;j++)rts();
	SCREENON
}
/*****************************************************************************/
void
brokenscreenoffon(){
LONG j;
	for (j=0;j<10;j++){
  		SCREENOFF
  		SCREENON
	}
}
/**********************************************************************************/

void
End_Loop(ULONG forcframe)
{
#if RELEASE==NO
   	if (pad[1] & PAD_L1)
   		Show_VRAM();
#endif

#if (GOLDCD == NO) && (RELEASE == NO)
	if(debounce[1]&PAD_SELECT)
	{// save a snapshot of the game
		SnapShot();
	}
#endif

	if(cinemascopeToggle)
	{
		if(cinemascopeToggle==2)
		{
			projection=PROJECTION2;
			yScale=NTSC_CINEMA;
		}
		else
		{
			projection=PROJECTION1;
#if PALMODE==NO
			yScale=NTSC_TV;
#else
			yScale=PAL_TV;
#endif
		}

		DrawSync(0);
		ResetGraph(1); 
		GsInitGraph(256,240,GsOFSGPU,0,0);    /*set the mode */
		SetDispMask(1);   			/* 1=enable display 0=disable*/
		
		Start_Loop();
       	VSync(0);
		GsSwapDispBuff(); 	  			/* Exchange display buffer and drawing buffer */

		Start_Loop();
		Set_Up_Display();

		SetGeomOffset(0,0);
		GsSetProjection(projection);	//default distance of projection; alters perspective

		SCREENON;

		cinemascopeToggle=0;
	}

	frame++;
	if(gameCtrl.gameActive!=FALSE)
	{
		activeframe++;
	}

#if (CHECKGPU==YES)
	brokenscreenoffon();
#endif

	drawenv[scr_toggle].isbg = (clscol.r!=254);
//   	TIMER_START(TIMER_DRAWSYNC);
 	DrawSync(0);
//	TIMER_STOP(TIMER_DRAWSYNC);
	TIMER_STOP(TIMER_TOTAL);
	if(forcframe!=10)VSync(frameLock);					// Lock to 30Hz
	framerate = vblank-ovblank;
	ovblank = vblank;

	if(shakeScreen < 30)
	{
		dispenv[scr_toggle].screen.y = yOffset[scr_toggle] + randomInt(shakeScreen);
		dispenv[scr_toggle].screen.x = xOffset[scr_toggle] + randomInt(shakeScreen);
	}
	else
	{
		dispenv[scr_toggle].screen.y = yOffset[scr_toggle];
		dispenv[scr_toggle].screen.x = xOffset[scr_toggle];
	}

   	PutDispEnv(&dispenv[scr_toggle]); // update display environment
   	PutDrawEnv(&drawenv[scr_toggle]); // update drawing environment
	endpoly();
}
/************************************************************************************/

#define LOADBAR_Y (224)
#define LOADBAR_H (6)
#define LOADBAR_W (200)

#if GOLDCD==NO
	char no_pollhosts = 0;
#endif

void ShowScreen(int s)
{
	PutDispEnv(dispenv+s); // update display environment
}

void VblCallback()		  
{
	vblank++;

// iframe is 3000 per sec, whether or not we are running PAL or NTSC
	if(gameCtrl.gameActive)
	{
#if PALMODE==YES 
		iframe+=60;
#else
		iframe+=50;
#endif
	}

#if GOLDCD==NO

	if(!no_pollhosts)
	{
 		POLLHOST();
	}


#endif
/*
   	if (showProgress)
	{
		Start_Loop();
		progressMeter(2048);
		End_Loop(10);
	}
*/
	if(interrupt_cyclebar)
	{
		RECT rc;
		int colour;
		int pos;
		static int tick = 0;

		tick++;

		rc.x = 256-LOADBAR_W/2 -1;
		rc.y = LOADBAR_Y-1;
		rc.w = LOADBAR_W+2;
		rc.h = LOADBAR_H+2;
		ClearImage(&rc,0,0,0);

		for(pos = 0; pos < 20; pos++)
		{
			colour = ((tick + pos) * 20) % (256*6);
			colour = effectsGetPrimary(colour>>8,(colour &0xff),32);

			rc.x = 256-LOADBAR_W/2 + 10 * pos;
			rc.y = LOADBAR_Y;
			rc.w = 10;
			rc.h = LOADBAR_H;

			ClearImage(&rc,(colour & 0x7f)<<1,(colour >> 7)&0xfE,(colour>>15)&0xfe);
		}
	}
}
/*****************************************************************************/
/************************************************************************************/
void Cls(void){	 			//more than just CLS it initialises certain things
	SetDispMask(OFF);
	DrawSync(0);
	ResetGraph(1);
	Reset_Modelctrl();

	scr_toggle=1^GsGetActiveBuff();
	SCREENON;
	framerate = 1;
	pad[0]=pad[1]=debounce[0]=debounce[1]=0;
	finisheddrawing=YES;
	frame=0;
	activeframe = 0;
	polylist.dblbuf=scr_toggle;
}
/***********************************************************************************/
void
Turn_Screen_Off(){ //cleanly turns screen off at end of mainloops
	VSync(0);
	SCREENOFF;
}
/***********************************************************************************/
USHORT resolution=512;
void Set_Up_Display()
{
	VSync(0);
	ResetGraph(1);                 /* RESET GRAPHICS SUBSYSTEM (0:COLD, 1:WARM) */
	SCREENOFF;
	VSyncCallback(&VblCallback);   /* INCREMENTS VBLANK EVERY FRAME */
	VSync(0);

	padHandler();
  	if (PALMODE!=NO){
		SetVideoMode(MODE_PAL);
		GsInitGraph(resolution,yScale,4,1,0);	//N.B. can change style between Dithered & Non-dithered!
		GsDISPENV.screen.y =16;  //moves whole screen up & down! bigger=down
		GsDISPENV.screen.x =0;  //moves whole screen left & right
		scrcentery=128;
		screenheight=256;
	}else
	{
	   SetVideoMode(MODE_NTSC);
  		GsInitGraph(resolution,yScale,4,1,0);	//N.B. can change style between Dithered & Non-dithered!
	
		scrcentery=120;
		screenheight=240;
	}
 	GsDefDispBuff(0,256,0,0);
	GsInit3D();   

	screenwidth=resolution;
	scrcenterx=screenwidth/2;
	
	SetGeomOffset(resolution/2, scrcentery);	
 	FMEMCPY(&drawenv[0],&GsDRAWENV,sizeof(DRAWENV));
 	FMEMCPY(&drawenv[1],&GsDRAWENV,sizeof(DRAWENV));
 	FMEMCPY(&dispenv[0],&GsDISPENV,sizeof(DISPENV));
 	FMEMCPY(&dispenv[1],&GsDISPENV,sizeof(DISPENV));

	SetDefDrawEnv(&drawenv[0], 0,   0, resolution, 256);
	SetDefDrawEnv(&drawenv[1], 0, 256, resolution, 256);
	SetDefDispEnv(&dispenv[0], 0, 256, resolution, 256);
	SetDefDispEnv(&dispenv[1], 0, 0,   resolution, 256);

  	drawenv[0].ofs[0]=0+resolution/2;
  	drawenv[0].ofs[1]=0+120+PALMODE*8;
  	drawenv[1].ofs[0]=0+resolution/2;
  	drawenv[1].ofs[1]=0+256+120+PALMODE*8;

  	if (PALMODE){
  		dispenv[0].screen.y =dispenv[1].screen.y =16;  //moves whole screen up & down! bigger=down
  		dispenv[0].screen.h =dispenv[1].screen.h =256;  //moves whole screen up & down! bigger=down
  		dispenv[0].screen.x =dispenv[1].screen.x =4;  //moves whole left/right
	}

	yOffset[0] = dispenv[0].screen.y;
	yOffset[1] = dispenv[1].screen.y;
	xOffset[0] = dispenv[0].screen.x;
	xOffset[1] = dispenv[1].screen.x;
	GsSetProjection(projection);	//default distance of projection; alters perspective
}
/***************************************************************************************************************************/
USHORT *vmem=0;
void
Show_VRAM(void)
{
	LUSHORT slide=0;
	LUSHORT slidey=0;
		DrawSync(0);
		ResetGraph(1); 
		GsInitGraph(256,240,GsOFSGPU,0,0);    /*set the mode */
		SetDispMask(1);   			/* 1=enable display 0=disable*/
		
		while (((pad[0] | pad[1]) & PAD_START)==0)
		{
			Start_Loop();
			padHandler();
			GsDefDispBuff(slide,slidey,slide,slidey);
			if (((pad[0] | pad[1]) & PAD_LEFT)!=0) if (slide>3)slide-=4;
			if (((pad[0] | pad[1]) & PAD_RIGHT)!=0) if (slide<=(1024-256))slide+=4;
			if (((pad[0] | pad[1]) & PAD_UP)!=0) if (slidey>3)slidey-=4;
			if (((pad[0] | pad[1]) & PAD_DOWN)!=0) if (slidey<=(512-240))slidey+=4;
      	 			 	VSync(0);
			frame++;
			GsSwapDispBuff(); 	  			/* Exchange display buffer and drawing buffer */
		}
	   	Start_Loop();
		FREE(vmem);
		Set_Up_Display();

		SetGeomOffset(0,0);
		GsSetProjection(projection);	//default distance of projection; alters perspective

		SCREENON;
		pad[0] = pad[1] = 0;
}
/***************************************************************************/

OBJECTPOLYLIST polylist;

UBYTE *dodgeypolys=0; //this is the address to check, if polys overrun
void
Init_PolyLists(){
	PacketBuildList=MALLOC((PACKETMAX*2)+40,"PACKET BUILD TABLE");
}		
/***************************************************************************/
ULONG endprimflag=0x00ffffff;
OBJECTPOLYLIST*		//dont initialise polylist and use within same frame
Register_ObjectPolyList(UBYTE bitdepth,UBYTE *id,ULONG packetbuildsize){
OBJECTPOLYLIST *ptr;
LUBYTE j;
	ptr=&polylist; //=ptr;
	memset(ptr,0,sizeof(OBJECTPOLYLIST));
   	ptr->dblbuf=scr_toggle;
	ptr->sorttable=MALLOC(8+sizeof(GsOT_TAG)*2*(1<<bitdepth),"GsOT_TAG");
	ptr->topofdrawlist[0]=PacketBuildList+PACKETMAX;
	ptr->topofdrawlist[1]=PacketBuildList+PACKETMAX*2;
	ptr->bottomofdrawlist[0]=0;
	ptr->bottomofdrawlist[1]=0;

	for (j=0;j<2;j++){
		ptr->Wot[j].length=bitdepth;
 		ptr->Wot[j].org=0;
 		
		ptr->Wot[j].org=ptr->sorttable+((j)*(1<<ptr->Wot[0].length));
		ptr->Wot[j].end=(ULONG*)(ptr->sorttable+((j+1)*(1<<ptr->Wot[0].length)))-1;
 		ptr->Wot[j].shift=12-bitdepth;
	}
   	return ptr;
}
/****************************/
#if (GOLDCD == NO) && (RELEASE == NO)
UBYTE TooManyPolys(LONG size,UBYTE *mess){
ULONG calc1;
 	calc1=(ULONG)GsOUT_PACKET_P;
	
	if((size+calc1) <(ULONG)(CurrentPolyList->topofdrawlist[CurrentPolyList->dblbuf]))
		return NO;

   	VSync(0);

	if(!too_many_polys_flag)
	{
		too_many_polys_flag = 1;	// gets reset at "start_loop"
  		psxPrintf("*** Potential poly over run (by %d bytes) in %s *** \n",
			(size+calc1)-((ULONG)(CurrentPolyList->topofdrawlist[CurrentPolyList->dblbuf])), mess);
	}
	return YES;
 return NO;
}
#endif
/****************************/
PACKET *oldGsOUT_PACKET_P;
void
Do_ObjectPolyList(OBJECTPOLYLIST *ptr){
//ULONG *tagend;
//ULONG j;
		CurrentPolyList=ptr;
   		ptr->dblbuf=ptr->dblbuf^1;
  	  	GsClearOt(0,0,(GsOT*)&ptr->Wot[ptr->dblbuf]); 					/* Ordering table is cleared */
   	   	PolyList=&ptr->Wot[ptr->dblbuf];
}
/************************************************************************
	FUNCTION:	SnapShot()
	PURPOSE:	Save screen snapshot to file C:\PSX\SNAPSHOT\GRABxxxx.SNP
	PARAMETERS:	
	RETURNS:
	NB. use SNP2BMP utility	
**************************************************************************/

#define WIDTH 512
#define HEIGHT (240+(PALMODE*8))

void SnapShot(void)
{
#if (GOLDCD==NO) && (RELEASE==NO)
	RECT            rect;
	unsigned char   *grabbuffer; 
	unsigned long   file;
	char			filename[80];
	int				loop;

	for(loop=0; loop<=9999; loop++)
	{
		sprintf(filename, "C:\\PSX\\SNAPSHOT\\GRAB%04d.SNP", loop);
		if ((file=PCopen(filename,1,0))==-1)
			break;
		else
			PCclose(file);
	}
	if (loop>9999)
	{
		debugPrintf("Error trying to save %s\n", filename);
		return;
	}
	if ((file = PCcreat(filename,1))==-1)
	{
		debugPrintf("Error creating file %s\n", filename);
		return;
	}
	grabbuffer = MALLOC(WIDTH*2, "Grabbuffer");
	rect.x = 0;
	rect.y = 0;
	rect.w = WIDTH;
	rect.h = 1;
	for(loop=0; loop<HEIGHT; loop++)
	{
		rect.y = loop;
		StoreImage(&rect, (unsigned long*)grabbuffer);
		PCwrite(file, grabbuffer, WIDTH*2);
		//PCwrite(file, grabbuffer, WIDTH*2);
	}
	PCclose(file);
	debugPrintf("Grabbed screen %s\n", filename);
	FREE(grabbuffer);
#endif
}





//////////////////////////////////////////
/************************************************************************************/
/*
void setProgress(int per)
{
	showProgress = PER2FIX(per);
}
 
void progressMeter(int foo)
{
	POLY_G4	*si = (POLY_G4 *) GsOUT_PACKET_P; 

	currProgress += (showProgress-currProgress)/4;

	si->code = GPU_COM_G4;
	si->x0 = si->x2 = -320;
	si->y0 = si->y1 = -(120+PALMODE*8);
	si->x1 = si->x3 = 320;
	si->y2 = si->y3 = 120+PALMODE*8;

	SETRGBC(si->r0,0,0,0,GPU_COM_G4);
	SETRGBC(si->r1,70,20,0,GPU_COM_G4);
	SETRGBC(si->r2,130,40,0,GPU_COM_G4);
	SETRGBC(si->r3,220,70,0,GPU_COM_G4);

	PUTPACKETINTABLE(si, PolyList->org+2, POLYG4_LEN);
	si++;

	si->code = GPU_COM_G4;
	si->x0 = si->x2 = -105;
	si->y0 = si->y1 =  -10;
	si->x1 = si->x3 =  105;
	si->y2 = si->y3 =   10;
	SETRGBC(si->r0,0,0,0,GPU_COM_G4);
	SETRGBC(si->r1,30,0,0,GPU_COM_G4);
	SETRGBC(si->r2,60,0,0,GPU_COM_G4);
	SETRGBC(si->r3,120,0,0,GPU_COM_G4);
	PUTPACKETINTABLE(si, PolyList->org+1, POLYG4_LEN);
	si++;

	si->code = GPU_COM_G4;
	si->x0 = si->x2 = -100;
	si->y0 = si->y1 =   -8;
	si->x1 = si->x3 = -100+((currProgress*200)/4096);
	si->y2 = si->y3 =    8;

 	SETRGBC(si->r0,50,25,0,GPU_COM_G4);
	SETRGBC(si->r1,200,100,0,GPU_COM_G4);
	SETRGBC(si->r2,50,25,0,GPU_COM_G4);
	SETRGBC(si->r3,200,100,0,GPU_COM_G4);

	PUTPACKETINTABLE(si, PolyList->org, POLYG4_LEN);
	si++;

	(POLY_G4*)GsOUT_PACKET_P = si;
}
*/
/*************************************************************************************************/


char	timerActive = 0;
#if TIMERS==ON
int 	globalTimer[TIMER_NUMTIMERS];

int 	TimerStarted[TIMER_NUMTIMERS];
int 	prevTimer[TIMER_NUMTIMERS];
char	*timerName[TIMER_NUMTIMERS] = {TIMER_NAMES};


/**************************************************************************
	FUNCTION:	timerDisplay()
	PURPOSE:	Display timer readings if enabled
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

void timerDisplay()
{
	int		loop, subtotal = 0;
	char	str[20];

	TEXTSETFONT(fontList[1]);

	TIMER_START(TIMER_TIMERS);
	if (timerActive)
	{
		for(loop=1; loop<TIMER_NUMTIMERS; loop++)
		{

// changed to be "absolute times", rather than %ges of the red bar, so we can see
// how much time things are actually taking


			sprintf(str, "%d", prevTimer[loop]);
			TEXTPRINTAT(140,(-90+loop*15+136),timerName[loop]);
			TEXTPRINTAT(140+60,(-90+loop*15+136),str);

			dispGraph(-50,-87+loop*15, (100*prevTimer[loop])/((PALMODE)?(620):(520)),8, 255,255,0);
			dispGraph(-52,-88+loop*15, 104,10, 0,0,0);
			subtotal += prevTimer[loop];
		}

// no-one understands the orange bar (!)

		if(timerActive ==1)
		{
				TEXTPRINTAT((-140+320),(-90+loop*15+136), "CPU");
				sprintf(str, "%d", prevTimer[TIMER_TOTAL]);
				TEXTPRINTAT((100+320),(-90+loop*15+136), str);
		}
		dispGraph(-50,-87+loop*15, (100*prevTimer[TIMER_TOTAL])/((PALMODE)?(620):(520)),8, 255,0,0);
		dispGraph(-52,-88+loop*15, 104,10, 0,0,0);

	}
	TIMER_STOP(TIMER_TIMERS);
}
/*********************************************************************************************/
void dispGraph(short x,short y, short w, short h, UBYTE r, UBYTE g, UBYTE b)
{
	POLY_F4 	*si = (POLY_F4 *) GsOUT_PACKET_P; 

	if (TOOMANYPOLYS(5*MAXPACKETSIZE,"dispGraph"))
		return;

	si->x0 = x;
	si->y0 = y;
	si->x1 = x+w;
	si->y1 = y;
	si->x2 = x;
	si->y2 = y+h;
	si->x3 = x+w;
	si->y3 = y+h;
	SETRGBC(si->r0,r,g,b,GPU_COM_F4);
	PUTPACKETINTABLE(si, (GsOTA*)(PolyList->org), POLYF4_LEN);
	si++;
	(POLY_F4*)GsOUT_PACKET_P = si;
}

#endif //TIMERS

/*******************************************************************************************/

// also defined in glover.h
//#define STACKBASE  0x001ff000 /*4k stack*/
//#define STACKTOP   0x001ffffc
//#define DCACHE  0x1f800000
//#define DCACHETOP 0x1f800400 /*1k stack!*/


void CheckStack(void)
{
	static ULONG sp;

	// Get stack pointer
	EnterCriticalSection();
	sp=SetSp(0);
	SetSp(sp);
	ExitCriticalSection();

	DB("Stack address is: %p\n", sp);

	if(sp<STACKBASE)
	{
		DB("ERK! Stack pointer points to code/data memory! size=%d\n",STACKTOP-sp);
		CRASH;
	}
	
	if(sp<STACKTOP)
	{
		DB("OK. Stack pointer points to valid 'slow memory' size=%d\n",STACKTOP-sp);
		return;
	}

	if(sp<DCACHE + SCRATCHPADSIZE)
	{
		DB("ERK! Stack pointer points to invalid memory!size=%d\n",DCACHETOP-sp);
		CRASH;
	}

	if(sp<DCACHETOP)
	{
		DB("OK. Stack pointer points to valid 'fast memory'size=%d\n",DCACHETOP-sp);
		DB("(It's %d bytes from the end)\n", sp-DCACHE);
		return;
	}

	if(sp>DCACHE)
	{
		DB("ERK! Stack pointer is above valid address space! (I think)\n");
		CRASH;
	}

}
/***************************************************************************************/
//#define STACKCHECK {EnterCriticalSection(); \
//					stacktmp=SetSp(oldSp); \
//					SetSp(stacktmp); \
//					stack=stacktmp-DCACHE; \
//					ExitCriticalSection(); \
//					if (stack<stacklimit)CRASH;}


