// mbc.cpp: implementation of the CMBC class.
//
//////////////////////////////////////////////////////////////////////

#include "mbc.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CMBC::CMBC(EMBCType eMBCType, CMemory* pMemory)
{
	m_enMBCType = eMBCType;
	
	m_pMemory= pMemory;

	m_enMBCMode = MBC_ROM_MODE;
}

CMBC::~CMBC()
{

}

//////////////////////////////////////////////////////////////////////////
//CMBC1

void CMBC1::Write(u32 addr, u32 value)
{	
	u32 nROM;
	u32 nRAM;

    switch(addr >> 12)
    {
	case 0x0:
	case 0x1: //RAM Enable
		//if the value's lower 4bit is A then enable, or disable.
		m_pMemory->ExternRAMEnabled( (value & 0x0F) == 0x0A );

		if(m_pMemory->RAMBankNum() == 0) 
			m_pMemory->ExternRAMEnabled(false);

		break;
	case 0x2:
	case 0x3: //ROM Bank Number - lower 5 bits
		value &= 0x1F;
		if(value == 0) // both 0 and 1 symbol 1.
			value = 1;

		nROM = m_pMemory->SelectedROMIndex();
		nROM &= ~0x1F;
		nROM |= value;
		nROM &= m_pMemory->ROMBankNum() - 1;

		m_pMemory->SelectedROMIndex(nROM);
		
		m_pMemory->m_ROM1.SetBank(nROM);
		break;
	case 0x4:
	case 0x5: //RAM Bank Number - or - Upper Bits of ROM Bank Number
		value &= 0x3;
		if(MBCMode() == MBC_ROM_MODE) //ROM MODE
		{
			nROM = m_pMemory->SelectedROMIndex();
			nROM &= 0x1F;
			nROM |= value << 5;
			nROM &= m_pMemory->ROMBankNum() - 1;

			m_pMemory->SelectedROMIndex(nROM);
			
			m_pMemory->m_ROM1.SetBank(nROM);
		}
		else //RAM MODE
		{
			nRAM = value;
			nRAM &= m_pMemory->RAMBankNum() - 1;
			m_pMemory->SelectedRAMIndex(nRAM);
			m_pMemory->m_EXRAM.SetBank(nRAM);
		}
		break;
	case 0x6:
	case 0x7: //ROM/RAM Mode Select
		//MBCMode(value & 1 ? MBC_RAM_MODE : MBC_ROM_MODE);
		m_enMBCMode = value & 1 ? MBC_RAM_MODE : MBC_ROM_MODE;
		break;
	case 0xA:
	case 0xB:
		if(!m_pMemory->ExternRAMEnabled()) 
			return;
		else 
			m_pMemory->m_EXRAM[addr-0xA000] = value && 0xFF;
		break;
	default:
		break;
    }	
}

u32 CMBC1::Read(u32 addr)
{
	if(m_pMemory->RAMBankNum() == 0 || !m_pMemory->ExternRAMEnabled())
		return 0xff;

	return m_pMemory->m_EXRAM[addr - 0xA000];	
}


//////////////////////////////////////////////////////////////////////////
//CMBC2
void CMBC2::Write(u32 addr, u32 value)
{
	u32 nROM;
	//u32 nRAM;

	switch(addr >> 12)
	{
	case 0x0:
	case 0x1: //RAM Enable
		if((addr & 0x0100) == 0)
		{
			m_pMemory->ExternRAMEnabled((value & 0xF) == 0xA);

			if(m_pMemory->RAMBankNum() == 0) 
				m_pMemory->ExternRAMEnabled(false);
		}
		break;
	case 0x2:
	case 0x3: //ROM Bank Number
		//	if(address & 0x0100)
		{
			if(value == 0) 
				value = 1;
			nROM = value & (m_pMemory->ROMBankNum() - 1);
			if(nROM == 0) 
				nROM ++;
			m_pMemory->SelectedROMIndex(nROM);
			m_pMemory->m_ROM1.SetBank(nROM);
		}
		break;
	case 0x4:
	case 0x5:
	case 0x6:
	case 0x7:
		break;
	case 0xA:
	case 0xB:
		if(!m_pMemory->ExternRAMEnabled()) 
			return;
		else 
			m_pMemory->m_EXRAM[addr-0xA000] = value & 0x0F;
		break;
	default:
		//DebugMessage("MBC2 WROTE - %02x to %04x",value,address);
		break;
	}
}
u32 CMBC2::Read(u32 addr)
{
	if(m_pMemory->RAMBankNum() == 0 || !m_pMemory->ExternRAMEnabled()) 
		return 0xFF;
	return ( (m_pMemory->m_EXRAM[addr-0xA000]) & 0x0F );
}

//////////////////////////////////////////////////////////////////////////
//CMBC3


//////////////////////////////////////////////////////////////////////////
//CMBC5
void CMBC5::Write(u32 addr, u32 value)
{
	u32 nROM;
	u32 nRAM;

	switch(addr >> 12)
	{
	case 0x0:
	case 0x1:
		m_pMemory->ExternRAMEnabled((value & 0x0F) == 0x0A);
		if(m_pMemory->RAMBankNum() == 0)
			m_pMemory->ExternRAMEnabled(false);
		break;
	case 0x2:
		nROM = m_pMemory->SelectedROMIndex();
		nROM &= 0xFF00;
		nROM |= (value&0xFF);
		nROM &= m_pMemory->ROMBankNum() - 1;
		m_pMemory->SelectedROMIndex(nROM);
		m_pMemory->m_ROM1.SetBank(nROM);
		break;
	case 0x3:
		nROM &= 0xFF;
		nROM |= value << 8;
		nROM &= m_pMemory->ROMBankNum() - 1;
		m_pMemory->SelectedROMIndex(nROM);
		m_pMemory->m_ROM1.SetBank(nROM);
		break;
	case 0x4:
	case 0x5:
		if(m_bHasRumble)
		{
			if(value & 0x08) 
				m_bHasRumble = true;
			else 
				m_bHasRumble = false;
			value &= 0x7;
		}
		nRAM = value & (m_pMemory->RAMBankNum() - 1);
		m_pMemory->SelectedRAMIndex(nRAM);
		m_pMemory->m_EXRAM.SetBank(nRAM);
		break;
	case 0x6: //Why do some games write 0 and 1 here? For MBC1 compatibility?
	case 0x7:
		break;
	case 0xA:
	case 0xB:
		if(!m_pMemory->ExternRAMEnabled()) 
			return;
		else
			m_pMemory->m_EXRAM[addr-0xA000] = value;
		break;
	default:
		//DebugMessage("MBC5 WROTE - %02x to %04x",value,address);
		break;
	}
}

u32 CMBC5::Read(u32 addr)
{
	if(m_pMemory->RAMBankNum() == 0 || !m_pMemory->ExternRAMEnabled())
		return 0xff;

	return m_pMemory->m_EXRAM[addr - 0xA000];	
}

