/*===================================================================*/
/*                                                                   */
/*                       Mapper 45 (Pirates)                         */
/*                                                                   */
/*===================================================================*/

BYTE  Map45_Regs[7];
DWORD Map45_P[4],Map45_Prg0,Map45_Prg1,Map45_Prg2,Map45_Prg3;
DWORD Map45_C[4],Map45_Chr0, Map45_Chr1,Map45_Chr2, Map45_Chr3;
DWORD Map45_Chr4, Map45_Chr5, Map45_Chr6, Map45_Chr7;

BYTE Map45_IRQ_Enable;
BYTE Map45_IRQ_Cnt;
BYTE Map45_IRQ_Latch;

/*-------------------------------------------------------------------*/
/*  Initialize Mapper 45                                             */
/*-------------------------------------------------------------------*/
void Map45_Init()
{
  /* Initialize Mapper */
  MapperInit = Map45_Init;

  /* Write to Mapper */
  MapperWrite = Map45_Write;

  /* Write to SRAM */
  MapperSram = Map45_Sram;

  /* Write to APU */
  MapperApu = Map0_Apu;

  /* Read from APU */
  MapperReadApu = Map0_ReadApu;

  /* Callback at VSync */
  MapperVSync = Map0_VSync;

  /* Callback at HSync */
  MapperHSync = Map45_HSync;

  /* Callback at PPU */
  MapperPPU = Map0_PPU;

  /* Callback at Rendering Screen ( 1:BG, 0:Sprite ) */
  MapperRenderScreen = Map0_RenderScreen;

  /* Set SRAM Banks */
  SRAMBANK = SRAM;

  /* Set ROM Banks */
  Map45_Prg0 = 0;
  Map45_Prg1 = 1;
  Map45_Prg2 = NesHeader.byRomSize * 2 - 2;
  Map45_Prg3 = NesHeader.byRomSize * 2 - 1;

  ROMBANK0 = ROMPAGE( Map45_Prg0 );  Map45_P[0] = Map45_Prg0;
  ROMBANK1 = ROMPAGE( Map45_Prg1 );  Map45_P[1] = Map45_Prg1;
  ROMBANK2 = ROMPAGE( Map45_Prg2 );  Map45_P[2] = Map45_Prg2;
  ROMBANK3 = ROMPAGE( Map45_Prg3 );  Map45_P[3] = Map45_Prg3;
  
  /* Set PPU Banks */
  Map45_Chr0 = 0;  Map45_C[0] = Map45_Chr0;
  Map45_Chr1 = 1;  Map45_C[1] = Map45_Chr1;
  Map45_Chr2 = 2;  Map45_C[2] = Map45_Chr2;
  Map45_Chr3 = 3;  Map45_C[3] = Map45_Chr3;
  Map45_Chr4 = 4;  Map45_C[4] = Map45_Chr4;
  Map45_Chr5 = 5;  Map45_C[5] = Map45_Chr5;
  Map45_Chr6 = 6;  Map45_C[6] = Map45_Chr6;
  Map45_Chr7 = 7;  Map45_C[7] = Map45_Chr7;

  for ( int nPage = 0; nPage < 8; ++nPage ) {
    PPUBANK[ nPage ] = VROMPAGE( nPage );
  }
  InfoNES_SetupChr();
  
  /* Initialize IRQ Registers */
  Map45_IRQ_Enable = 0;
  Map45_IRQ_Cnt = 0;
  Map45_IRQ_Latch = 0;

  Map45_Regs[0] = Map45_Regs[1] = Map45_Regs[2] = Map45_Regs[3] = 0;
  Map45_Regs[4] = Map45_Regs[5] = Map45_Regs[6] = 0;

  /* Set up wiring of the interrupt pin */
  K6502_Set_Int_Wiring( 1, 1 ); 
}

/*-------------------------------------------------------------------*/
/*  Mapper 45 Write to Sram Function                                 */
/*-------------------------------------------------------------------*/
void Map45_Sram( WORD wAddr, BYTE byData )
{
	if(wAddr == 0x6000)
	{
		Map45_Regs[Map45_Regs[5]] = byData;
		Map45_Regs[5]= (Map45_Regs[5]+1) & 0x03;
		Map45_Set_CPU_Bank4((BYTE)Map45_Prg0);
		Map45_Set_CPU_Bank5((BYTE)Map45_Prg1);
		Map45_Set_CPU_Bank6((BYTE)Map45_Prg2);
		Map45_Set_CPU_Bank7((BYTE)Map45_Prg3);
		Map45_Set_PPU_Banks();
  }
}

/*-------------------------------------------------------------------*/
/*  Mapper 45 Write Function                                         */
/*-------------------------------------------------------------------*/
void Map45_Write( WORD wAddr, BYTE byData )
{
  DWORD swap;

	switch(wAddr & 0xE001) 
  {
    case 0x8000:
			if((byData & 0x40) != (Map45_Regs[6] & 0x40))
			{
				swap = Map45_Prg0; Map45_Prg0 = Map45_Prg2; Map45_Prg2 = swap;
				swap = Map45_P[0]; Map45_P[0] = Map45_P[2]; Map45_P[2] = swap;
        ROMBANK0 = ROMPAGE( Map45_P[0] % ( NesHeader.byRomSize << 1) );
        ROMBANK2 = ROMPAGE( Map45_P[2] % ( NesHeader.byRomSize << 1) );
			}
			if (NesHeader.byRomSize > 0)
			{
				if((byData & 0x80) != (Map45_Regs[6] & 0x80))
				{
					swap = Map45_Chr4; Map45_Chr4 = Map45_Chr0; Map45_Chr0 = swap;
					swap = Map45_Chr5; Map45_Chr5 = Map45_Chr1; Map45_Chr1 = swap;
					swap = Map45_Chr6; Map45_Chr6 = Map45_Chr2; Map45_Chr2 = swap;
					swap = Map45_Chr7; Map45_Chr7 = Map45_Chr3; Map45_Chr3 = swap;
					swap = Map45_C[4]; Map45_C[4] = Map45_C[0]; Map45_C[0] = swap;
					swap = Map45_C[5]; Map45_C[5] = Map45_C[1]; Map45_C[1] = swap;
					swap = Map45_C[6]; Map45_C[6] = Map45_C[2]; Map45_C[2] = swap;
					swap = Map45_C[7]; Map45_C[7] = Map45_C[3]; Map45_C[3] = swap;
					
          PPUBANK[ 0 ] = VROMPAGE( Map45_C[0] % ( NesHeader.byVRomSize << 3 ) );
          PPUBANK[ 1 ] = VROMPAGE( Map45_C[1] % ( NesHeader.byVRomSize << 3 ) );
          PPUBANK[ 2 ] = VROMPAGE( Map45_C[2] % ( NesHeader.byVRomSize << 3 ) );
          PPUBANK[ 3 ] = VROMPAGE( Map45_C[3] % ( NesHeader.byVRomSize << 3 ) );
          PPUBANK[ 4 ] = VROMPAGE( Map45_C[4] % ( NesHeader.byVRomSize << 3 ) );
          PPUBANK[ 5 ] = VROMPAGE( Map45_C[5] % ( NesHeader.byVRomSize << 3 ) );
          PPUBANK[ 6 ] = VROMPAGE( Map45_C[6] % ( NesHeader.byVRomSize << 3 ) );
          PPUBANK[ 7 ] = VROMPAGE( Map45_C[7] % ( NesHeader.byVRomSize << 3 ) );
          InfoNES_SetupChr();
				}
			}
			Map45_Regs[6] = byData;
		  break;

	case 0x8001:
	  switch(Map45_Regs[6] & 0x07)
		{
			case 0x00:
				Map45_Chr0 = (byData & 0xFE)+0;
				Map45_Chr1 = (byData & 0xFE)+1;
				Map45_Set_PPU_Banks();
				break;

			case 0x01:
			  Map45_Chr2 = (byData & 0xFE)+0;
				Map45_Chr3 = (byData & 0xFE)+1;
				Map45_Set_PPU_Banks();
				break;

			case 0x02:
				Map45_Chr4 = byData;
				Map45_Set_PPU_Banks();
				break;

			case 0x03:
				Map45_Chr5 = byData;
				Map45_Set_PPU_Banks();
				break;

			case 0x04:
				Map45_Chr6 = byData;
				Map45_Set_PPU_Banks();
				break;

			case 0x05:
				Map45_Chr7 = byData;
				Map45_Set_PPU_Banks();
				break;

      case 0x06:
				if(Map45_Regs[6] & 0x40)
				{
				  Map45_Prg2 = byData & 0x3F;
					Map45_Set_CPU_Bank6(byData);
				}
				else
				{
					Map45_Prg0 = byData & 0x3F;
					Map45_Set_CPU_Bank4(byData);
				}
				break;

			case 0x07:
				Map45_Prg1 = byData & 0x3F;
				Map45_Set_CPU_Bank5(byData);
				break;
		}
		break;

	case 0xA000:
     if ( byData & 0x01 )
     {
       InfoNES_Mirroring( 0 );
     } else {
       InfoNES_Mirroring( 1 );
     }
		break;

	case 0xC000:
	  Map45_IRQ_Cnt = byData;
		break;

	case 0xC001:
		Map45_IRQ_Latch = byData;
		break;

	case 0xE000:
		Map45_IRQ_Enable = 0;
		break;

	case 0xE001:
		Map45_IRQ_Enable = 1;
		break;
	}
}

/*-------------------------------------------------------------------*/
/*  Mapper 45 H-Sync Function                                        */
/*-------------------------------------------------------------------*/
void Map45_HSync()
{
/*
 *  Callback at HSync
 *
 */
  if(Map45_IRQ_Enable)
  {
    if ( /* 0 <= PPU_Scanline && */ PPU_Scanline <= 239 )
    {
      if ( PPU_R1 & R1_SHOW_SCR || PPU_R1 & R1_SHOW_SP )
			{
				if(!(Map45_IRQ_Cnt--))
				{
					Map45_IRQ_Cnt = Map45_IRQ_Latch;
          IRQ_REQ;
				}
			}
		}
	}
}

/*-------------------------------------------------------------------*/
/*  Mapper 45 Set CPU Banks Function                                 */
/*-------------------------------------------------------------------*/

void Map45_Set_CPU_Bank4(BYTE byData)
{
	byData &= (Map45_Regs[3] & 0x3F) ^ 0xFF;
	byData &= 0x3F;
	byData |= Map45_Regs[1];
  ROMBANK0 = ROMPAGE( byData % ( NesHeader.byRomSize << 1) );
	Map45_P[0] = byData;
}

void Map45_Set_CPU_Bank5(BYTE byData)
{
	byData &= (Map45_Regs[3] & 0x3F) ^ 0xFF;
	byData &= 0x3F;
	byData |= Map45_Regs[1];
  ROMBANK1 = ROMPAGE( byData % ( NesHeader.byRomSize << 1) );
	Map45_P[1] = byData;
}

void Map45_Set_CPU_Bank6(BYTE byData)
{
	byData &= (Map45_Regs[3] & 0x3F) ^ 0xFF;
	byData &= 0x3F;
	byData |= Map45_Regs[1];
  ROMBANK2 = ROMPAGE( byData % ( NesHeader.byRomSize << 1) );
	Map45_P[2] = byData;
}

void Map45_Set_CPU_Bank7(BYTE byData)
{
	byData &= (Map45_Regs[3] & 0x3F) ^ 0xFF;
	byData &= 0x3F;
	byData |= Map45_Regs[1];
  ROMBANK3 = ROMPAGE( byData % ( NesHeader.byRomSize << 1) );
	Map45_P[3] = byData;
}

/*-------------------------------------------------------------------*/
/*  Mapper 45 Set PPU Banks Function                                 */
/*-------------------------------------------------------------------*/
void Map45_Set_PPU_Banks()
{
	BYTE table[16] =
	    {
	        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	        0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF
	    };
	Map45_C[0] = Map45_Chr0;
	Map45_C[1] = Map45_Chr1;
	Map45_C[2] = Map45_Chr2;
	Map45_C[3] = Map45_Chr3;
	Map45_C[4] = Map45_Chr4;
	Map45_C[5] = Map45_Chr5;
	Map45_C[6] = Map45_Chr6;
	Map45_C[7] = Map45_Chr7;
	for(BYTE i = 0; i < 8; i++)
	{
		Map45_C[i] &= table[Map45_Regs[2] & 0x0F];
		Map45_C[i] |= Map45_Regs[0] & 0xff;
		Map45_C[i] += (BYTE)(Map45_Regs[2] & 0x10) << 4;
	}
	if(Map45_Regs[6] & 0x80)
	{
    PPUBANK[ 0 ] = VROMPAGE( Map45_C[4] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 1 ] = VROMPAGE( Map45_C[5] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 2 ] = VROMPAGE( Map45_C[6] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 3 ] = VROMPAGE( Map45_C[7] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 4 ] = VROMPAGE( Map45_C[0] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 5 ] = VROMPAGE( Map45_C[1] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 6 ] = VROMPAGE( Map45_C[2] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 7 ] = VROMPAGE( Map45_C[3] % ( NesHeader.byVRomSize << 3 ) );
    InfoNES_SetupChr();
	}
	else
	{
    PPUBANK[ 0 ] = VROMPAGE( Map45_C[0] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 1 ] = VROMPAGE( Map45_C[1] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 2 ] = VROMPAGE( Map45_C[2] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 3 ] = VROMPAGE( Map45_C[3] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 4 ] = VROMPAGE( Map45_C[4] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 5 ] = VROMPAGE( Map45_C[5] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 6 ] = VROMPAGE( Map45_C[6] % ( NesHeader.byVRomSize << 3 ) );
    PPUBANK[ 7 ] = VROMPAGE( Map45_C[7] % ( NesHeader.byVRomSize << 3 ) );
    InfoNES_SetupChr();
	}
}


