#include "cps.h"
// CPS - Read/Write

// Input bits
#define INP(nnn) unsigned char CpsInp##nnn[8];
CPSINPSET
#undef  INP

// Bytes to return from ports
#define INP(nnn) static unsigned char Inp##nnn;
CPSINPSET
#undef  INP

unsigned char CpsInp055[4] = {0, 0x20, 0, 0};
unsigned char CpsInp05d[4] = {0, 0x20, 0, 0};
static int nDial055, nDial05d;

int PangEEP = 0;
int Forgottn = 0;
int Cps1QsHack = 0;

static int nCalc[2] = {0, 0};

// CPS1 Slammasters protection
int slamPro = 0;
unsigned char *CpsEncZRom = NULL;

#define INP(nnnn) unsigned char CpsInp##nnnn[8];
CPSINPEX
#undef  INP

#define INP(nnnn) static unsigned char Inp##nnnn;
CPSINPEX
#undef  INP


// Read input port 0x000-0x1ff
inline static unsigned char CpsReadPort(unsigned int ia)
{
	unsigned char d = 0xFF;

	if (ia == 0x000) {
		d = (unsigned char)~Inp000;
		return d;
	}
	if (ia == 0x001) {
		d = (unsigned char)~Inp001;
		return d;
	}
	if (ia == 0x010) {
		d = (unsigned char)~Inp010;
		return d;
	}
	if (ia == 0x011) {
		d = (unsigned char)~Inp011;
		return d;
	}

	if (ia == 0x018) {
		d = (unsigned char)~Inp018;
		return d;
	}

	if (ia == 0x019) {
		d = (unsigned char)~Inp019;
		return d;
	}

	if (ia == 0x01A) {
		d = (unsigned char)~Cpi01A;
		return d;
	}
	if (ia == 0x01C) {
		d = (unsigned char)~Cpi01C;
		return d;
	}
	if (ia == 0x01E) {
		d = (unsigned char)~Cpi01E;
		return d;
	}


	if (Cps == 2) {
		// Used on CPS2 only I think
		if (ia == 0x020) {
			d = (unsigned char)~Inp020;
			return d;
		}
		if (ia == 0x021) {
			d = (unsigned char)~Inp021;
			d &= 0xFE;
			d |= EEPROMRead();
			return d;
		}

		// CPS2 Volume control
		if (ia == 0x030) {
			d = 0xE0;
			return d;
		}
		if (ia == 0x031) {
			d = 0x21;
			return d;
		}
	
		if (ia >= 0x0100 && ia < 0x0200) {
			static int nRasterLine;
//			printf("Register %03X read, PC: %06X, line %3i.\n", ia & 0xFF, SekPc(-1), (nCpsCyclesSegment - m68k_ICount) / (nCpsCycles / 0x0106));

			// The linecounters seem to return the line at which the last IRQ triggered by this counter is scheduled minus the current line		
			if ((ia & 0x0FE) == 0x50) {
				if ((ia & 1) == 0) {
					nRasterLine = nIrqLine50 - (nCpsCyclesSegment - m68k_ICount) / (nCpsCycles / 0x0106);
					return nRasterLine >> 8;
				} else {
					return nRasterLine & 0xFF;
				}
			}
			if ((ia & 0x0FE) == 0x52) {
				if ((ia & 1) == 0) {
					nRasterLine = nIrqLine52 - (nCpsCyclesSegment - m68k_ICount) / (nCpsCycles / 0x0106);
					return nRasterLine >> 8;
				} else {
					return nRasterLine & 0xFF;
				}
			}

		}

	} else {
		// Board ID
		if (ia == 0x100 + CpsBID[0]) {
			d = (unsigned char)CpsBID[1];
			return d;
		}
		if (ia == 0x100 + (CpsBID[0] + 1)) {
			d = (unsigned char)CpsBID[2];
			return d;
		}
		
		if (Cps1Qs == 1) {
			// CPS1 EEPROM read
			if (ia == 0xC007) {
				return EEPROMRead();
			}
			// CPS1 Player 3
			if (ia == 0xC000) {
				d = (unsigned char)~Inpc000;
				return d;
			}
			if (ia == 0xC001) {
				d = (unsigned char)~Inpc001;
				return d;
			}
			// CPS1 Player 4
			if (ia == 0xC002) {
				d = (unsigned char)~Inpc002;
				return d;
			}
			if (ia == 0xC003) {
				d = (unsigned char)~Inpc003;
				return d;
			}
		} else {
			if (Forgottn) {
				if (ia == 0x053) {
					return nDial055 & 0xFF;
				}
				if (ia == 0x055) {
					return (nDial055 >> 8) & 0xFF;
				}
				if (ia == 0x05B) {
					return nDial05d & 0xFF;
				}
				if (ia == 0x05D) {
					return (nDial05d >> 8) & 0xFF;
				}
			}

			if (ia == 0x176) {
				d = (unsigned char)~Inp176;
				return d;
			}
			if (ia == 0x177) {
				d = (unsigned char)~Inp177;
				return d;
			}
			if (ia == 0x179) {
				d = (unsigned char)~Inp179;
				return d;
			}

			// Treble Winner - sf2ue Input Ports
			if (ia == 0x1FD) {
				d=(unsigned char)~Inp1fd;
				return d;
			}
		}
		
		if (PangEEP == 1) {
			// Pang3 EEPROM
			if (ia == 0x17B) {
				return EEPROMRead();
			}
		}
	}

	return d;
}

// Write output port 0x000-0x1ff
static void CpsWritePort(unsigned int ia,unsigned char d)
{

	if ((Cps & 1) && Cps1Qs == 0) {
		if (ia == 0x181) {
			// CPS1 sound code
			PsndCode = d;
			return;
		}

		// CPS1 sound fade
		if (ia == 0x189) {
			PsndFade = d;
			return;
		}

		if (ia == 0x041) {
			nDial055 = 0;
		}
		if (ia == 0x049) {
			nDial05d = 0;
		}
	}

	if (Cps == 1 && Cps1QsHack == 1) {
		if (ia == 0x181) {
			// Pass the Sound Code to the Q-Sound Shared Ram
			CpsZRamC0[0x001] = d;
		}
	}

	// CPS registers
	if (ia >= 0x100 && ia < 0x200) {
		//Pang3 EEPROM
		if (PangEEP == 1 && ia == 0x17B) {
			EEPROMWrite(d & 0x40, d & 0x80, d & 0x01);
			return;
		}
		CpsReg[(ia ^ 1) & 0xFF] = d;
		return;
	}

	if (Cps == 2) {
		if (ia == 0x40) {
			EEPROMWrite(d & 0x20, d & 0x40, d & 0x10);
			
			return;
		}

		// Coin control, Z80 reset, LEDs
//		if (ia == 0x41) {
//			return;
//		}

		// CPS2 object bank select
		if ((ia & 0x1FF) == 0x0E1) {			
//			printf("%i (%i)\n", d & 1, (nCpsCyclesSegment - m68k_ICount) / (nCpsCycles / 0x0106));
			CpsMapObjectBanks(d & 1);
			return;
		}
	}

	if (Cps1Qs == 1) {
		//CPS1 EEPROM write
		if (ia == 0xc007) {
			EEPROMWrite(d & 0x40, d & 0x80, d & 0x01);
			return;
		}
	}
}

unsigned char __fastcall CpsReadByte(unsigned int a)
{
//	a &= 0xFFFFFF;

	// Input ports mirrored between 0x800000 and 0x807fff
	if ((a & 0xFF8000) == 0x800000) {
		return CpsReadPort(a & 0x1FF);
	}

	if (Cps == 2) {
		// QSound Z80
		if (a >= 0x618000 && a < 0x61A000) {
			return CpsZRamC0[(a >> 1) & 0x0FFF];
		}
	}

	if (Cps1Qs == 1) {
		// CPS1 EEPROM & Player 3/4
		if (a >= 0xF1C000 && a <= 0xF1C007) {
			return CpsReadPort(a & 0xC00F);
		}
		// CPS1 QSOUND
		if (a <= 0xF19FFF && a >= 0xF18000) {
			return  CpsZRamC0[(a >> 1) & 0x0FFF];
		}
		if (a >= 0xF1E000 && a <= 0xF1FFFF) {
			return CpsZRamF0[(a >> 1) & 0x0FFF];
		}
	}
	
	return 0x00;
}

void __fastcall CpsWriteByte(unsigned int a,unsigned char d)
{
	// Output ports mirrored between 0x800000 and 0x807fff
	if ((a & 0xFF8000) == 0x800000) {
		CpsWritePort(a & 0x1FF, d);
		return;
	}

	if (Cps == 2) {
		// QSound Z80
		if (a >= 0x618000 && a < 0x61A000) {
			CpsZRamC0[(a >> 1) & 0x0FFF] = d;
			return;
		}
		// 0x400000 registers
		if ((a & 0xFFFFF0) == 0x400000)	{
			CpsFrg[a & 0x0F] = d;
		}
	}

	if (Cps1Qs == 1) {
		// CPS1 EEPROM
		if (a == 0xf1c007) {
			CpsWritePort(a & 0xC00F, d);
			return;
		}
		
		// CPS1 QSOUND
		if (a <= 0xF19FFF && a >= 0xf18000) {
			CpsZRamC0[(a >> 1) & 0x0FFF] = d;
			return;
		}
		if (a >= 0xF1E000 && a <= 0xF1FFFF) {
			CpsZRamF0[(a >> 1) & 0x0FFF] = d;
			return;
		}
	}

}

unsigned short __fastcall CpsReadWord(unsigned int a)
{
//	a &= 0xFFFFFF;

	if ((a & 0xFF8FFF) == 0x800100 + CpsMProt[3]) {
		return (unsigned short)((nCalc[0] * nCalc[1]) >> 16);
	}
	// ports mirrored between 0x800000 and 0x807fff
	if ((a & 0xFF8FFF) == 0x800100 + CpsMProt[2]) {
		return (unsigned short)((nCalc[0] * nCalc[1]));
	}

	SEK_DEF_READ_WORD(a)
}

void __fastcall CpsWriteWord(unsigned int a, unsigned short d)
{
	// ports mirrored between 0x800000 and 0x807fff
	if ((a & 0xFF8FFF) == 0x800100 + CpsMProt[0])
		nCalc[0] = d;
	if ((a & 0xFF8FFF) == 0x800100 + CpsMProt[1])
		nCalc[1] = d;

	if (a == 0x804040) {
		if ((d & 0x0008) == 0) {
			ZetReset();
		}
	}
	
	SEK_DEF_WRITE_WORD(a,d)
}

// Reset all inputs to zero
static int InpBlank()
{
#define INP(nnn) Inp##nnn = 0; memset(CpsInp##nnn, 0, sizeof(CpsInp##nnn));
	CPSINPSET
#undef INP

#define INP(nnnn) Inp##nnnn = 0; memset(CpsInp##nnnn, 0, sizeof(CpsInp##nnnn));
	CPSINPEX
#undef INP

	CpsInp055[0] = CpsInp055[2] = CpsInp055[3] = CpsInp05d[0] = CpsInp05d[2] = CpsInp05d[3] = 0;

	return 0;
}

int CpsRwInit()
{
	InpBlank();
	return 0;
}

int CpsRwExit()
{
	InpBlank();
	return 0;
}

inline static void StopOpposite(unsigned char* pInput)
{
	if ((*pInput & 0x03) == 0x03) {
		*pInput &= ~0x03;
	}
	if ((*pInput & 0x0C) == 0x0C) {
		*pInput &= ~0x0C;
	}
}

int CpsRwGetInp()
{
	// Compile separate buttons into Inpxxx
#define INP(nnn) \
  { int i = 0; Inp##nnn = 0; \
    for (i = 0; i < 8; i++) { Inp##nnn |= (CpsInp##nnn[i] & 1) << i; }  }
	CPSINPSET
#undef INP


#define INP(nnnn) \
  { int i = 0; Inp##nnnn = 0; \
    for (i = 0; i < 8; i++) { Inp##nnnn |= (CpsInp##nnnn[i] & 1) << i; }  }
	CPSINPEX
#undef INP

	if (Forgottn) {
		
		// Handle analog controls
		nDial055 += (int)(CpsInp055[0] - 128) * (256 - CpsInp055[1]) / 256;
		nDial05d += (int)(CpsInp05d[0] - 128) * (256 - CpsInp05d[1]) / 256;
		
		if (CpsInp055[2]) {
			nDial055 += CpsInp055[1];
		}
		if (CpsInp055[3]) {
			nDial055 -= CpsInp055[1];
		}
		if (CpsInp05d[2]) {
			nDial05d += CpsInp05d[1];
		}
		if (CpsInp05d[3]) {
			nDial05d -= CpsInp05d[1];
		}
	}

	StopOpposite(&Inp000);
	StopOpposite(&Inp001);
	
	if (nMaxPlayers > 2) {
		if (Cps == 2) {
			StopOpposite(&Inp011);
			if (nMaxPlayers == 4) {
				StopOpposite(&Inp010);
			}
		} else {
			StopOpposite(&Inp177);
			if (nMaxPlayers == 4) {
				StopOpposite(&Inp179);
			}
			if (Cps1Qs) {
				StopOpposite(&Inpc001);
				if (nMaxPlayers == 4) {
					StopOpposite(&Inpc003);
				}
			}
		}
	}

	return 0;
}
