#include "types.h"
#include "nes.h"
#include "nes_mapper.h"
#include "045.h"

/////////////////////////////////////////////////////////////////////
// Mapper 45
void NES_mapper45::Reset()
{
  patch = 0;

  prg0 = 0;
  prg1 = 1;
  prg2 = num_8k_ROM_banks-2;
  prg3 = num_8k_ROM_banks-1;

  if(parent_NES->crc32() == 0x58bcacf6 || // Kunio 8-in-1 (Pirate Cart)
     parent_NES->crc32() == 0x9103cfd6 || // HIK 7-in-1 (Pirate Cart)
     parent_NES->crc32() == 0xc082e6d3)   // Super 8-in-1 (Pirate Cart)
  {
    patch = 1;
    prg2 = 62;
    prg3 = 63;
  }

  set_CPU_banks(prg0,prg1,prg2,prg3);
  p[0] = prg0;
  p[1] = prg1;
  p[2] = prg2;
  p[3] = prg3;

  // set PPU bank pointers
  set_PPU_banks(0,1,2,3,4,5,6,7);
  chr0 = 0; chr1 = 1; chr2 = 2; chr3 = 3;
  chr4 = 4; chr5 = 5; chr6 = 6; chr7 = 7;
  c[0] = chr0; c[1] = chr1; c[2] = chr2; c[3] = chr3;
  c[4] = chr4; c[5] = chr5; c[6] = chr6; c[7] = chr7;

  irq_enabled = 0;
  irq_counter = 0;
  irq_latch = 0;

  regs[0] = regs[1] = regs[2] = regs[3] = 0;
  regs[4] = regs[5] = regs[6] = 0;
}

void NES_mapper45::MemoryWriteSaveRAM(uint32 addr, uint8 data)
{
  if(addr == 0x6000)
  {
    regs[regs[5]] = data;
    regs[5]= (regs[5]+1) & 0x03;
    MAP45_set_CPU_bank4(prg0);
    MAP45_set_CPU_bank5(prg1);
    MAP45_set_CPU_bank6(prg2);
    MAP45_set_CPU_bank7(prg3);
    MAP45_set_PPU_banks();
  }
}

void NES_mapper45::MemoryWrite(uint32 addr, uint8 data)
{
  switch(addr & 0xE001)
  {
    case 0x8000:
      {
        uint32 swap;
        if((data & 0x40) != (regs[6] & 0x40))
        {
          swap = prg0; prg0 = prg2; prg2 = swap;
          swap = p[0]; p[0] = p[2]; p[2] = swap;
          set_CPU_bank4(p[0]);
          set_CPU_bank6(p[2]);
        }
        if(num_8k_ROM_banks)
        {
          if((data & 0x80) != (regs[6] & 0x80))
          {
            swap = chr4; chr4 = chr0; chr0 = swap;
            swap = chr5; chr5 = chr1; chr1 = swap;
            swap = chr6; chr6 = chr2; chr2 = swap;
            swap = chr7; chr7 = chr3; chr3 = swap;
            swap = c[4]; c[4] = c[0]; c[0] = swap;
            swap = c[5]; c[5] = c[1]; c[1] = swap;
            swap = c[6]; c[6] = c[2]; c[2] = swap;
            swap = c[7]; c[7] = c[3]; c[3] = swap;
            set_PPU_banks(c[0],c[1],c[2],c[3],c[4],c[5],c[6],c[7]);
          }
        }
        regs[6] = data;
      }
      break;

    case 0x8001:
      {
        switch(regs[6] & 0x07)
        {
          case 0x00:
            {
              chr0 = (data & 0xFE)+0;
              chr1 = (data & 0xFE)+1;
              MAP45_set_PPU_banks();
            }
            break;

          case 0x01:
            {
              chr2 = (data & 0xFE)+0;
              chr3 = (data & 0xFE)+1;
              MAP45_set_PPU_banks();
            }
            break;

          case 0x02:
            {
              chr4 = data;
              MAP45_set_PPU_banks();
            }
            break;

          case 0x03:
            {
              chr5 = data;
              MAP45_set_PPU_banks();
            }
            break;

          case 0x04:
            {
              chr6 = data;
              MAP45_set_PPU_banks();
            }
            break;

          case 0x05:
            {
              chr7 = data;
              MAP45_set_PPU_banks();
            }
            break;

          case 0x06:
            {
              if(regs[6] & 0x40)
              {
                prg2 = data & 0x3F;
                MAP45_set_CPU_bank6(data);
              }
              else
              {
                prg0 = data & 0x3F;
                MAP45_set_CPU_bank4(data);
              }
            }
            break;

          case 0x07:
            {
              prg1 = data & 0x3F;
              MAP45_set_CPU_bank5(data);
            }
            break;
        }
      }
      break;

    case 0xA000:
      {
        if(data & 0x01)
        {
          set_mirroring(NES_PPU::MIRROR_HORIZ);
        }
        else
        {
          set_mirroring(NES_PPU::MIRROR_VERT);
        }
      }
      break;

    case 0xC000:
      {
        irq_counter = data;
      }
      break;

    case 0xC001:
      {
        irq_latch = data;
      }
      break;

    case 0xE000:
      {
        irq_enabled = 0;
      }
      break;

    case 0xE001:
      {
        irq_enabled = 1;
      }
      break;
  }
}

void NES_mapper45::HSync(uint32 scanline)
{
  if(irq_enabled)
  {
    if((scanline >= 0) && (scanline <= 239))
    {
      if(attached_ppu->bg_or_spr_enabled())
      {
        if(!(irq_counter--))
        {
          irq_counter = irq_latch;
          parent_NES->cpu->DoIRQ();
        }
      }
    }
  }
}

void NES_mapper45::MAP45_set_CPU_bank4(uint8 data)
{
  data &= (regs[3] & 0x3F) ^ 0xFF;
  data &= 0x3F;
  data |= regs[1];
  set_CPU_bank4(data);
  p[0] = data;
}

void NES_mapper45::MAP45_set_CPU_bank5(uint8 data)
{
  data &= (regs[3] & 0x3F) ^ 0xFF;
  data &= 0x3F;
  data |= regs[1];
  set_CPU_bank5(data);
  p[1] = data;
}
void NES_mapper45::MAP45_set_CPU_bank6(uint8 data)
{
  data &= (regs[3] & 0x3F) ^ 0xFF;
  data &= 0x3F;
  data |= regs[1];
  set_CPU_bank6(data);
  p[2] = data;
}
void NES_mapper45::MAP45_set_CPU_bank7(uint8 data)
{
  data &= (regs[3] & 0x3F) ^ 0xFF;
  data &= 0x3F;
  data |= regs[1];
  set_CPU_bank7(data);
  p[3] = data;
}

void NES_mapper45::MAP45_set_PPU_banks()
{
  uint8 table[16] =
  {
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF
  };
  c[0] = chr0;
  c[1] = chr1;
  c[2] = chr2;
  c[3] = chr3;
  c[4] = chr4;
  c[5] = chr5;
  c[6] = chr6;
  c[7] = chr7;
  for(uint8 i = 0; i < 8; i++)
  {
    c[i] &= table[regs[2] & 0x0F];
    c[i] |= regs[0] & ((patch == 0) ? 0xff : 0xC0);
    c[i] += (uint32)(regs[2] & ((patch == 0) ? 0x10 : 0x30)) << 4;
  }
  if(regs[6] & 0x80)
  {
    set_PPU_banks(c[4],c[5],c[6],c[7],c[0],c[1],c[2],c[3]);
  }
  else
  {
    set_PPU_banks(c[0],c[1],c[2],c[3],c[4],c[5],c[6],c[7]);
  }
}
/////////////////////////////////////////////////////////////////////

