#include "tmnt.h"
// TMNT - Run module
// Based on DTMNT and MAME

unsigned char bTmntRecalcPal=0;

static unsigned char *Mem=NULL,*MemEnd=NULL;
static unsigned char *RamStart=NULL,*RamEnd=NULL;
static unsigned char *Rom,*Ram06,*Z80Rom,*Z80Ram,*TmntSpriteTemp;
unsigned char *TmntRam08,*TmntRam10,*TmntTile,*TmntSprite,*K051960Ram,*PriRam;
unsigned char bInt5=0; // 1 if we are doing int 5

static int nCyclesDone[2], nCyclesTotal[2];
static int nCyclesSegment;

unsigned char DrvReset=0;
inline static int CheckSleep(int)
{
	return 0;
}

// This routine is called first to determine how much memory is needed (MemEnd-(unsigned char *)0),
// and then to set up all the pointers
static int MemIndex()
{
	unsigned char *Next; Next=Mem;
		Rom  =Next;   Next+=0x060000; // 68000 program
    RamStart =Next;
   K051960Ram=Next;   Next+=0x000800; // spr ram
	TmntRam10=Next;   Next+=0x008000; // tile ram
	TmntRam08=Next;   Next+=0x001000; // palette
       PriRam=Next;	  Next+=0x0100;
        Ram06=Next;   Next+=0x004000;
    RamEnd   =Next;
    TmntTile =Next;   Next+=0x100000; // Tile bitmaps
  TmntSprite =Next;   Next+=0x400000; // Tile bitmaps
       MemEnd=Next;
  return 0;
}

static int Interleve2(unsigned char *pd,int i,int nLen)
{
	unsigned char *pt;
	unsigned short *pts,*ptd;
	int a;

	pt=(unsigned char *)malloc(nLen); if (pt==NULL) return 1;
    memset(pt,0,nLen);
    BurnLoadRom(pt,i,1);

	nLen>>=1; pts=(unsigned short *)pt;
	ptd=(unsigned short *)pd;
	for (a=0;a<nLen; a++, pts++, ptd+=2)
		*ptd=*pts;
    free(pt);
    return 0;
}

static int LoadRoms()
{
	int nRet=0;
	// Load program roms and byteswap
	nRet=BurnLoadRom(Rom+0x00001,0,2); if (nRet!=0) return 1;
	nRet=BurnLoadRom(Rom+0x00000,1,2); if (nRet!=0) return 1;
	nRet=BurnLoadRom(Rom+0x40001,2,2); if (nRet!=0) return 1;
	nRet=BurnLoadRom(Rom+0x40000,3,2); if (nRet!=0) return 1;

	nRet=BurnLoadRom(Z80Rom,10,1); // load z80 code rom

	Interleve2(TmntTile+0x000000,4,0x80000); // 8x8
	Interleve2(TmntTile+0x000002,5,0x80000); // ""

    TmntSpriteTemp=(unsigned char *)malloc(0x200000);
    if (TmntSpriteTemp==NULL) return 1;

	Interleve2(TmntSpriteTemp+0x000000,6,0x80000); // sprites
	Interleve2(TmntSpriteTemp+0x000002,7,0x80000); // ""
	Interleve2(TmntSpriteTemp+0x100000,8,0x80000); // ""
	Interleve2(TmntSpriteTemp+0x100002,9,0x80000); // ""

	for (int i = 0; i<0x200000;i++)
	{
		TmntSprite[i*2]=(TmntSpriteTemp[i^1] & 0x0F);///((TmntSpriteTemp[i] & 0xF0) >> 4);
		TmntSprite[(i*2)+1]=((TmntSpriteTemp[i^1] & 0xF0) >> 4);/////(TmntSpriteTemp[i] & 0x0F);

	}

	if (TmntSpriteTemp!=NULL)
		free(TmntSpriteTemp);
	TmntSpriteTemp=NULL;

	return 0;
}

// Z80 accesses
static unsigned char TMNTZIn(unsigned short addr)
{
	printf("\nZ80: read from port %x",addr);
    return 0;
}

static void TMNTZOut(unsigned short addr,unsigned char val)
{
	printf("\nZ80: Write %x to port %x",val,addr);
}


unsigned char TMNTZRead(unsigned short addr)
{
	if ((addr>=0x8000) && (addr<=0x87FF))
	{
	//	printf("\nZ80: read from ram addr %x",addr);
		return Z80Ram[addr&0x0FFF];
	}

	if (addr==0x9000)
	{
		//printf("\nZ80: tmnt_sres_r");
	}
	if (addr==0xa000)
	{
	//	printf("\nZ80: read from soundlatch");
	}
	if ((addr>=0xb000) && (addr<=0xb00d))
	{
	//	printf("\nZ80: read from K007232 %d",0xb000 - addr);
	}
	if (addr==0xc001)
	{
	//	printf("\nZ80: YM2151 status read");
	}
	if (addr==0xf000)
	{
	//	printf("\nZ80: UDP7759 busy read");
	}
	return 0;
}

static void TMNTZWrite(unsigned short addr,unsigned char val)
{
	if ((addr>=0x8000) && (addr<=0x87FF))
	{
//		printf("\nZ80: write %x to addr %x",val,addr);
		Z80Ram[addr&0x0FFF]=val;
	}
	if (addr==0x9000)
	{
	//	printf("\nZ80: Tmnt_sres_w %d",val);
	}
	if ((addr>=0xb000) && (addr<=0xb00d))
	{
//		printf("\nZ80: write %d to K007232 %d",val,0xb000 - addr);
	}
	if (addr==0xc000)
	{
//		printf("\nZ80: write %d to YM2151 register port 0", val);
	}
	if (addr==0xc001)
	{
//		printf("\nZ80: write %d to YM2151 data port 0", val);
	}
	if (addr==0xd000)
	{
//		printf("\nZ80: write %d to UPD7759_0_message", val);
	}
	if (addr==0xe000)
	{
//		printf("\nZ80: write %d to UPD7759_0_start", val);
	}
}

// end of z80 accesses

static unsigned char __fastcall TmntReadByte(unsigned int a)
{
	if (a>=0xa0000 && a<0xa0020)
		return (unsigned char)(~TmntAoo[(a>>1)&0xf]);
	return 0xff;
}

static void __fastcall TmntWriteByte(unsigned int a,unsigned char d)
{
	unsigned int ab;
	ab=(a>>16)&0xff;
	if (ab==0x08)
	{
		TmntPalWrite(a,d);
		return;
	} // Palette write
	if (a==0xa0001)
	{
		if (d&0x20) bInt5=1;
		else bInt5=0;
		return;
	}
}

int TmntInit()
{
	int nRet=0; int nLen=0;

	// Find out how much memory is needed
	Mem=NULL;
	MemIndex();
	nLen=MemEnd-(unsigned char *)0;
	Mem=(unsigned char *)malloc(nLen);
	if (Mem==NULL)
		return 1;
	memset(Mem,0,nLen); // blank all memory
	MemIndex(); // Index the allocated memory

    Z80Rom=(unsigned char *)malloc(0x08000);
    if (Z80Rom==NULL) return 1;
    memset(Z80Rom,0x11,0x08000);

	Z80Ram=(unsigned char *)malloc(0x0800);
    if (Z80Ram==NULL) return 1;
    memset(Z80Ram,0x00,0x0800);


	nRet=LoadRoms();
	if (nRet!=0)
		return 1; // Load the roms into memory

	SekInit(1); // Allocate 1 68000

	SekOpen(0);
	// Map in memory:
	// ----------------- Cpu 1 ------------------------
	SekMemory(    Rom  ,0x000000,0x05FFFF,SM_ROM); // 68000 Rom
	SekMemory(    Ram06,0x060000,0x063FFF,SM_RAM);
	SekMemory(TmntRam08,0x080000,0x080FFF,SM_ROM); // Write goes through handler
	SekMemory(   PriRam,0x0c0000,0x0c00FF,SM_RAM);
	SekMemory(TmntRam10,0x100000,0x107FFF,SM_RAM);
	SekMemory(K051960Ram,0x140400,0x1407FF,SM_RAM);
	SekExt[0].ReadByte =TmntReadByte;
	SekExt[0].WriteByte=TmntWriteByte;
	// ------------------------------------------------
	SekReset();
	SekClose();

	//----------------- Z80 ---------------------------
	ZetIn=TMNTZIn;
	ZetOut=TMNTZOut;
	ZetRead=TMNTZRead;
	ZetWrite=TMNTZWrite;
	ZetInit();
	ZetMapArea    (0x0000,0x7fff,0,Z80Rom); // Direct Read from ROM
	ZetMapArea    (0x0000,0x7fff,2,Z80Rom); // Direct Fetch from ROM
	ZetMemEnd();
	//-------------------------------------------------
	ZetReset();
	bInt5=0;
	TmntPalInit();
	return 0;
}

int TmntExit()
{
	TmntPalExit();

	SekExit(); // Deallocate 68000

	// Deallocate all used memory
	if (Mem!=NULL)
		free(Mem);
	Mem=NULL;
	if (Z80Rom!=NULL)
		free(Z80Rom);
	Z80Rom=NULL;
	if (Z80Ram!=NULL)
		free(Z80Ram);
	Z80Ram=NULL;
	return 0;
}

static int TmntDraw()
{
	TmntPalUpdate(bTmntRecalcPal); // Recalc whole pal if needed
	bTmntRecalcPal=0;
	BurnClearScreen();
	TmntTileDraw();

	return 0;
}

int TmntFrame()
{
	int nInterleave = 4;
	if (DrvReset)
		{
		SekOpen(0);
		SekReset();
		SekClose();
		ZetReset();
		return 0;
		}
	TmntInpMake();

	nCyclesTotal[0] = 8000000/60;							
	nCyclesTotal[1] = 3579545/60;
	nCyclesDone[0] = nCyclesDone[1] = 0;

	SekOpen(0);
    if (bInt5)
	   SekInterrupt(5);
	for (int i = 0; i < nInterleave; i++) {
    	int nCurrentCPU;
		int nNext;
		// Run 68000
		nCurrentCPU = 0;
		nNext = (i + 1) * nCyclesTotal[nCurrentCPU] / nInterleave;

		nCyclesSegment = nNext - nCyclesDone[nCurrentCPU];
		if (!CheckSleep(nCurrentCPU)) {					// See if this CPU is busywaiting
			nCyclesDone[nCurrentCPU] += SekRun(nCyclesSegment);
		} else {
			nCyclesDone[nCurrentCPU] += nCyclesSegment;
		}
		// Run Z80
		nCurrentCPU = 1;
		nNext = (i + 1) * nCyclesTotal[nCurrentCPU] / nInterleave;
		nCyclesSegment = nNext - nCyclesDone[nCurrentCPU];
		nCyclesSegment = ZetRun(nCyclesSegment);
		nCyclesDone[nCurrentCPU] += nCyclesSegment;
	}
	SekClose();

	if (pBurnDraw) {
		TmntDraw();														// Draw screen if needed
	}

	return 0;
}

int TmntScan(int nAction,int *pnMin)
{
	if (nAction&4) // Scan volatile ram
	{
		struct BurnArea ba;
		if (pnMin!=NULL)
			*pnMin=0x010601; // Return minimum compatible version

		memset(&ba,0,sizeof(ba));
		ba.Data=RamStart;
		ba.nLen=RamEnd-RamStart;
		ba.szName="All Ram";
		BurnAcb(&ba);

		SekScan(nAction&3); // scan 68000 states
		SCAN_VAR(bInt5)
	}
	return 0;
}



//// MIA specific

// This routine is called first to determine how much memory is needed (MemEnd-(unsigned char *)0),
// and then to set up all the pointers
static int MiaIndex()
{
  unsigned char *Next; Next=Mem;
      Rom  =Next;   Next+=0x040000; // 68000 program
  RamStart =Next;
      K051960Ram=Next;   Next+=0x000800; // spr ram
  TmntRam10=Next;   Next+=0x008000; // tile ram
  TmntRam08=Next;   Next+=0x001000; // palette
	 PriRam=Next;	Next+=0x0100;
	  Ram06=Next;   Next+=0x004000;
  RamEnd   =Next;
  TmntTile =Next;    Next+=0x040000; // Tile bitmaps
  TmntSprite=Next;   Next+=0x100000; // sprite bitmaps

  MemEnd=Next;
  return 0;
}



static int MiaLoadRoms()
{
	int nRet=0;
	// Load program roms and byteswap
	nRet=BurnLoadRom(Rom+0x00001,0,2); if (nRet!=0) return 1;
	nRet=BurnLoadRom(Rom+0x00000,1,2); if (nRet!=0) return 1;

	Interleve2(TmntTile+0x000000,2,0x10000);
	Interleve2(TmntTile+0x000002,3,0x10000);
	Interleve2(TmntTile+0x020000,4,0x10000);
	Interleve2(TmntTile+0x020002,5,0x10000);
	Interleve2(TmntSprite+0x000000,6,0x80000);
	Interleve2(TmntSprite+0x000002,7,0x80000);
	return 0;
}



int MiaInit()
{
	int nRet=0;
	int nLen=0;

  // Find out how much memory is needed
	Mem=NULL;
	MiaIndex();
	nLen=MemEnd-(unsigned char *)0;
	Mem=(unsigned char *)malloc(nLen);
	if (Mem==NULL)
		return 1;
	memset(Mem,0,nLen); // blank all memory
	MiaIndex(); // Index the allocated memory

	nRet=MiaLoadRoms();
	if (nRet!=0)
		return 1; // Load the roms into memory

	SekInit(1); // Allocate 1 68000

	SekOpen(0);
	// Map in memory:
	// ----------------- Cpu 1 ------------------------
	SekMemory(    Rom  ,0x000000,0x03FFFF,SM_ROM); // 68000 Rom
	SekMemory(    Ram06,0x040000,0x063FFF,SM_RAM);
	SekMemory(TmntRam08,0x080000,0x080FFF,SM_ROM); // Write goes through handler
	SekMemory(   PriRam,0x0c0000,0x0c00FF,SM_RAM);
	SekMemory(TmntRam10,0x100000,0x107FFF,SM_RAM);
	SekMemory(    K051960Ram,0x140000,0x1407FF,SM_RAM);
	SekExt[0].ReadByte =TmntReadByte;
	SekExt[0].WriteByte=TmntWriteByte;
	// ------------------------------------------------
	SekReset(); bInt5=0;
	SekClose();

	TmntPalInit();
	return 0;
}

int MiaFrame()
{
	TmntInpMake();

	SekOpen(0);
	if (bInt5) SekInterrupt(5);
	SekRun(8000000/60); // 8mhz
	SekClose();
	if (pBurnDraw!=NULL)
		TmntDraw();
	 return 0;
}



