//////////////////////////////////////////////////////////////////////////
// Mapper121                                                            //
//////////////////////////////////////////////////////////////////////////

void	Mapper121::Reset()
{
	EXPREGS[3] = 0x80;
	EXPREGS[5] = 0;
	IRQReload = IRQCount = IRQLatch = IRQa = 0;
	MMC3cmd = A000B = A001B = 0;

	DRegBuf[0] = 0;
	DRegBuf[1] = 2;
	DRegBuf[2] = 4;
	DRegBuf[3] = 5;
	DRegBuf[4] = 6;
	DRegBuf[5] = 7;
	DRegBuf[6] = 0;
	DRegBuf[7] = 1;

	Fix121MMC3PRG(MMC3cmd);
	Fix121MMC3CHR(MMC3cmd);
}

BYTE	Mapper121::ReadLow( WORD addr )
{
	DEBUGOUT( "MPRWR A=%04X L=%3d CYC=%d\n", addr&0xFFFF, nes->GetScanline(), nes->cpu->GetTotalCycles() );
	if((addr>=0x5000)&&(addr<=0x5FFF)) return EXPREGS[4];
	if(addr>=0x6000) return CPU_MEM_BANK[addr>>13][addr&0x1FFF];
	return	Mapper::ReadLow( addr );
}

void	Mapper121::WriteLow( WORD addr, BYTE data )
{
	DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes->GetScanline(), nes->cpu->GetTotalCycles() );
	if((addr>=0x5000)&&(addr<=0x5FFF)){
		const uint8 prot_array[4] = { 0x83, 0x83, 0x42, 0x00 };
		EXPREGS[4] = prot_array[data & 3];
		if ((addr & 0x5180) == 0x5180) {
			EXPREGS[3] = data;
			Fix121MMC3PRG(MMC3cmd);
			Fix121MMC3CHR(MMC3cmd);
		}
	}
	if(addr>=0x6000) CPU_MEM_BANK[addr>>13][addr&0x1FFF] = data;
}

void	Mapper121::Write( WORD addr, BYTE data )
{
	DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes->GetScanline(), nes->cpu->GetTotalCycles() );
	if(addr<0xA000){
		switch (addr & 0xE003) {
		case 0x8000:
			MMC3CMDWrite(addr, data);
			Fix121MMC3PRG(MMC3cmd);
			break;
		case 0x8001:
			EXPREGS[6] = ((data&1)<<5)|((data&2)<<3)|((data&4)<<1)|((data&8)>>1)|((data&0x10)>>3)|((data&0x20)>>5);
			if (!EXPREGS[7]) SetDATA();
			MMC3CMDWrite(addr, data);
			Fix121MMC3PRG(MMC3cmd);
			break;
		case 0x8003:
			EXPREGS[5] = data;
			SetDATA();
			MMC3CMDWrite(0x8000, data);
			Fix121MMC3PRG(MMC3cmd);
			break;
		}
	}else{
		MMC3MIRWrite(addr, data);
		MMC3IRQWrite(addr, data);
	}
}

void Mapper121::MMC3CMDWrite(WORD A, BYTE V)
{
	switch (A & 0xE001) {
	case 0x8000:
		if ((V & 0x40) != (MMC3cmd & 0x40))
			Fix121MMC3PRG(V);
		if ((V & 0x80) != (MMC3cmd & 0x80))
			Fix121MMC3CHR(V);
		MMC3cmd = V;
		break;
	case 0x8001:
	{
		int cbase = (MMC3cmd & 0x80) << 5;
		DRegBuf[MMC3cmd & 0x7] = V;
		switch (MMC3cmd & 0x07) {
		case 0:
			PPUSW((cbase ^ 0x000), V & (~1));
			PPUSW((cbase ^ 0x400), V | 1);
			break;
		case 1:
			PPUSW((cbase ^ 0x800), V & (~1));
			PPUSW((cbase ^ 0xC00), V | 1);
			break;
		case 2:
			PPUSW(cbase ^ 0x1000, V);
			break;
		case 3:
			PPUSW(cbase ^ 0x1400, V);
			break;
		case 4:
			PPUSW(cbase ^ 0x1800, V);
			break;
		case 5:
			PPUSW(cbase ^ 0x1C00, V);
			break;
		case 6:
			if (MMC3cmd&0x40) CPUSW(0xC000, V);
			else		   CPUSW(0x8000, V);
			break;
		case 7:
			CPUSW(0xA000, V);
			break;
		}
		break;
	}
	}
}

void Mapper121::MMC3MIRWrite(WORD A, BYTE V)
{
	switch (A & 0xE001) {
		case 0xA000:
			A000B = V;
			if (A000B & 0x01)	SetVRAM_Mirror(VRAM_HMIRROR);
			else				SetVRAM_Mirror(VRAM_VMIRROR);
			break;
		case 0xA001:
			A001B = V;
			break;
	}
}

void Mapper121::MMC3IRQWrite(WORD A, BYTE V)
{
	switch (A & 0xE001) {
		case 0xC000: IRQReload = 0;IRQCount = V; break;
		case 0xC001: IRQReload = 0;IRQLatch = V; break;
		case 0xE000: IRQReload = 0;IRQa = 0;nes->cpu->ClrIRQ(IRQ_MAPPER); break;
		case 0xE001: IRQReload = 0;IRQa = 1; break;
	}
}

void	Mapper121::SetDATA()
{
	switch (EXPREGS[5] & 0x3F) {
	case 0x20: EXPREGS[7] = 1; EXPREGS[0] = EXPREGS[6]; break;
	case 0x29: EXPREGS[7] = 1; EXPREGS[0] = EXPREGS[6]; break;
	case 0x26: EXPREGS[7] = 0; EXPREGS[0] = EXPREGS[6]; break;
	case 0x2B: EXPREGS[7] = 1; EXPREGS[0] = EXPREGS[6]; break;
	case 0x2C: EXPREGS[7] = 1; if (EXPREGS[6]) EXPREGS[0] = EXPREGS[6]; break;
	case 0x3C:
	case 0x3F: EXPREGS[7] = 1; EXPREGS[0] = EXPREGS[6]; break;
	case 0x28: EXPREGS[7] = 0; EXPREGS[1] = EXPREGS[6]; break;
	case 0x2A: EXPREGS[7] = 0; EXPREGS[2] = EXPREGS[6]; break;
	case 0x2F: break;
	default:   EXPREGS[5] = 0; break;
	}
}

void	Mapper121::PPUSW( WORD addr, BYTE data )
{
	if ((nes->rom->GetPROM_SIZE()*2)==nes->rom->GetVROM_SIZE()) {
		SetVROM_1K_Bank(addr>>10, data | ((EXPREGS[3] & 0x80) << 1));
	} else {
		if ((addr & 0x1000) == ((MMC3cmd & 0x80) << 5))
			SetVROM_1K_Bank(addr>>10, data | 0x100);
		else
			SetVROM_1K_Bank(addr>>10, data);
	}
}

void	Mapper121::CPUSW( WORD addr, BYTE data )
{
	if (EXPREGS[5] & 0x3F) {
		SetPROM_8K_Bank(addr>>13, (data & 0x1F) | ((EXPREGS[3] & 0x80) >> 2));
		SetPROM_8K_Bank(7, (EXPREGS[0]) | ((EXPREGS[3] & 0x80) >> 2));
		SetPROM_8K_Bank(6, (EXPREGS[1]) | ((EXPREGS[3] & 0x80) >> 2));
		SetPROM_8K_Bank(5, (EXPREGS[2]) | ((EXPREGS[3] & 0x80) >> 2));
	} else {
		SetPROM_8K_Bank(addr>>13, (data & 0x1F) | ((EXPREGS[3] & 0x80) >> 2));
	}
}

void	Mapper121::Fix121MMC3PRG(BYTE data)
{
	if (data & 0x40) {
		CPUSW(0xC000, DRegBuf[6]);
		CPUSW(0x8000, PROM_8K_SIZE-2);
	} else {
		CPUSW(0x8000, DRegBuf[6]);
		CPUSW(0xC000, PROM_8K_SIZE-2);
	}
	CPUSW(0xA000, DRegBuf[7]);
	CPUSW(0xE000, PROM_8K_SIZE-1);
}

void	Mapper121::Fix121MMC3CHR(BYTE data)
{
	int cbase = (data & 0x80) << 5;
	PPUSW((cbase ^ 0x0000), DRegBuf[0] & (~1));
	PPUSW((cbase ^ 0x0400), DRegBuf[0] | 1);
	PPUSW((cbase ^ 0x0800), DRegBuf[1] & (~1));
	PPUSW((cbase ^ 0x0C00), DRegBuf[1] | 1);
	PPUSW((cbase ^ 0x1000), DRegBuf[2]);
	PPUSW((cbase ^ 0x1400), DRegBuf[3]);
	PPUSW((cbase ^ 0x1800), DRegBuf[4]);
	PPUSW((cbase ^ 0x1c00), DRegBuf[5]);

	if (A000B & 0x01)	SetVRAM_Mirror(VRAM_HMIRROR);
	else				SetVRAM_Mirror(VRAM_VMIRROR);
}

void	Mapper121::HSync(INT scanline)
{
	if ((scanline >= 0 && scanline <= 239)) {
		if (nes->ppu->IsDispON())
		{
			if (IRQa && !IRQReload) {
				if (scanline == 0) {
					if (IRQCount) {
						IRQCount -= 1;
					}
				}
				if (!(IRQCount)){
					IRQReload = 0xFF;
					IRQCount = IRQLatch;
					nes->cpu->SetIRQ(IRQ_MAPPER);
				}
				IRQCount--;
			}
		}
	}
}