/* FCE Ultra - NES/Famicom Emulator
 *
 * Copyright notice for this file:
 *  Copyright (C) 2002 Xodnizel
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "mapinc.h"

static uint8 bus_conflict = 0;
static uint8 latche, latcheinit;
static uint16 addrreg0, addrreg1;
static void(*WSync)(void);

static DECLFW(LatchWrite)
{
//  FCEU_printf("bs %04x %02x\n",A,V);
  if(bus_conflict)
    latche=V&CartBR(A);
  else
  latche=V;
  WSync();
}

static void LatchPower(void)
{
  latche=latcheinit;
  WSync();
  SetReadHandler(0x8000,0xFFFF,CartBR);
  SetWriteHandler(addrreg0,addrreg1,LatchWrite);
}

static void StateRestore(int version)
{
  WSync();
}

static void Latch_Init(CartInfo *info, void (*proc)(void), uint8 init, uint16 adr0, uint16 adr1)
{
  latcheinit=init;
  addrreg0=adr0;
  addrreg1=adr1;
  WSync=proc;
  info->Power=LatchPower;
  GameStateRestore=StateRestore;
  AddExState(&latche, 1, 0, "LATC");
  AddExState(&bus_conflict, 1, 0, "BUSC");
}

//------------------ CPROM ---------------------------

static void CPROMSync(void)
{
  setchr4(0x0000,0);
  setchr4(0x1000,latche&3);
  setprg16(0x8000,0);
  setprg16(0xC000,1);
}

void CPROM_Init(CartInfo *info)
{
  Latch_Init(info, CPROMSync, 0, 0x8000, 0xFFFF);
}

//------------------ Map 184 ---------------------------

static void M184Sync(void)
{
  setchr4(0x0000,latche);
  setchr4(0x1000,latche>>4);
  setprg16(0x8000,0);
  setprg16(0xC000,1);
}

void Mapper184_Init(CartInfo *info)
{
  Latch_Init(info, M184Sync, 0, 0x6000, 0x7FFF);
}

//------------------ CNROM ---------------------------

static void CNROMSync(void)
{
	//mbg 8/10/08 - fixed this so that large homebrew roms would work.
	//setchr8(latche&3);
	setchr8(latche);
	setprg16(0x8000,0);
	setprg16(0xC000,1);
}

void CNROM_Init(CartInfo *info)
{
  bus_conflict = 1;
  Latch_Init(info, CNROMSync, 0, 0x8000, 0xFFFF);
}

//------------------ ANROM ---------------------------

static void ANROMSync()
{
  setprg32(0x8000,latche&0xf);
  setmirror(MI_0+((latche>>4)&1));
  setchr8(0);
}

void ANROM_Init(CartInfo *info)
{
  Latch_Init(info, ANROMSync, 0, 0x8000, 0xFFFF);
}

//------------------ Map 70 ---------------------------

static void M70Sync()
{
  setprg16(0x8000,latche>>4);
  setprg16(0xc000,~0);
  setchr8(latche&0xf);
}

void Mapper70_Init(CartInfo *info)
{
  Latch_Init(info, M70Sync, 0, 0x8000, 0xFFFF);
}

//------------------ Map 152 ---------------------------

static void M152Sync()
{
  setprg16(0x8000,(latche>>4)&7);
  setprg16(0xc000,~0);
  setchr8(latche&0xf);
  setmirror(MI_0+((latche>>7)&1));         /* Saint Seiya...hmm. */
}

void Mapper152_Init(CartInfo *info)
{
  Latch_Init(info, M152Sync, 0, 0x8000, 0xFFFF);
}

//------------------ Map 78 ---------------------------
/* Should be two separate emulation functions for this "mapper".  Sigh.  URGE TO KILL RISING. */
static void M78Sync()
{
  setprg16(0x8000,(latche&7));
  setprg16(0xc000,~0);
  setchr8(latche>>4);
  setmirror(MI_0+((latche>>3)&1));
}

void Mapper78_Init(CartInfo *info)
{
  Latch_Init(info, M78Sync, 0, 0x8000, 0xFFFF);
}

//------------------ MHROM ---------------------------

static void MHROMSync(void)
{
  setprg32(0x8000,latche>>4);
  setchr8(latche&0xf);
}

void MHROM_Init(CartInfo *info)
{
  Latch_Init(info, MHROMSync, 0, 0x8000, 0xFFFF);
}

void Mapper140_Init(CartInfo *info)
{
  Latch_Init(info, MHROMSync, 0, 0x6000, 0x7FFF);
}

void Mapper240_Init(CartInfo *info)
{
  Latch_Init(info, MHROMSync, 0, 0x4020, 0x5FFF);
  // need SRAM.
}

//------------------ Map 87 ---------------------------

static void M87Sync(void)
{
  setprg16(0x8000,0);
  setprg16(0xC000,1);
  setchr8(((latche>>1)&1)|((latche<<1)&2));
//  setchr8(latche);
}

void Mapper87_Init(CartInfo *info)
{
  Latch_Init(info, M87Sync, ~0, 0x6000, 0xFFFF);
}

//------------------ Map 101 ---------------------------

static void M101Sync(void)
{
  setprg16(0x8000,0);
  setprg16(0xC000,1);
  setchr8(latche);
}

void Mapper101_Init(CartInfo *info)
{
  Latch_Init(info, M101Sync, ~0, 0x6000, 0x7FFF);
}

//------------------ Map 11 ---------------------------

static void M11Sync(void)
{
  setprg32(0x8000,latche&0xf);
  setchr8(latche>>4);
}

void Mapper11_Init(CartInfo *info)
{
  Latch_Init(info, M11Sync, 0, 0x8000, 0xFFFF);
}

void Mapper144_Init(CartInfo *info)
{
  Latch_Init(info, M11Sync, 0, 0x8001, 0xFFFF);
}

//------------------ Map 38 ---------------------------

static void M38Sync(void)
{
  setprg32(0x8000,latche&3);
  setchr8(latche>>2);
}

void Mapper38_Init(CartInfo *info)
{
  Latch_Init(info, M38Sync, 0, 0x7000, 0x7FFF);
}

//------------------ Map 36 ---------------------------

static void M36Sync(void)
{
  setprg32(0x8000,latche>>4);
  setchr8((latche)&0xF);
}

void Mapper36_Init(CartInfo *info)
{
  Latch_Init(info, M36Sync, 0, 0x8400, 0xfffe);
}
//------------------ UNROM ---------------------------

static void UNROMSync(void)
{
  setprg16(0x8000,latche);
  setprg16(0xc000,~0);
  setchr8(0);
}

void UNROM_Init(CartInfo *info)
{
  bus_conflict = 1;
  Latch_Init(info, UNROMSync, 0, 0x8000, 0xFFFF);
}

//------------------ Map 93 ---------------------------

static void SSUNROMSync(void)
{
  setprg16(0x8000,latche>>4);
  setprg16(0xc000,~0);
  setchr8(0);
}

void SUNSOFT_UNROM_Init(CartInfo *info)
{
  Latch_Init(info, SSUNROMSync, 0, 0x8000, 0xFFFF);
}

//------------------ Map 94 ---------------------------

static void M94Sync(void)
{
  setprg16(0x8000,latche>>2);
  setprg16(0xc000,~0);
  setchr8(0);
}

void Mapper94_Init(CartInfo *info)
{
  Latch_Init(info, M94Sync, 0, 0x8000, 0xFFFF);
}

//------------------ Map 180 ---------------------------

static void M180Sync(void)
{
  setprg16(0x8000,0);
  setprg16(0xc000,latche);
  setchr8(0);
}

void Mapper180_Init(CartInfo *info)
{
  Latch_Init(info, M180Sync, 0, 0x8000, 0xFFFF);
}

//------------------ Map 107 ---------------------------

static void M107Sync(void)
{
  setprg32(0x8000,(latche>>1)&3);
  setchr8(latche&7);
}

void Mapper107_Init(CartInfo *info)
{
  Latch_Init(info, M107Sync, ~0, 0x8000, 0xFFFF);
}

//------------------ Map 113 ---------------------------

static void M113Sync(void)
{
  setprg32(0x8000,(latche>>3)&7);
  setchr8(((latche>>3)&8)|(latche&7));
//  setmirror(latche>>7); // only for HES 6in1
}

void Mapper113_Init(CartInfo *info)
{
  Latch_Init(info, M113Sync, 0, 0x4100, 0x7FFF);
}

//------------------ A65AS ---------------------------

// actually, there is two cart in one... First have extra mirroring
// mode (one screen) and 32K bankswitching, second one have only
// 16 bankswitching mode and normal mirroring... But there is no any
// correlations between modes and they can be used in one mapper code.

static void BMCA65ASSync(void)
{
  if(latche&0x40)
    setprg32(0x8000,(latche>>1)&0x0F);
  else
  {
    setprg16(0x8000,((latche&0x30)>>1)|(latche&7));
    setprg16(0xC000,((latche&0x30)>>1)|7);
  }
  setchr8(0);
  if(latche&0x80)
    setmirror(MI_0+(((latche>>5)&1)));
  else
    setmirror(((latche>>3)&1)^1);
}

void BMCA65AS_Init(CartInfo *info)
{
  Latch_Init(info, BMCA65ASSync, 0, 0x8000, 0xFFFF);
}

//------------------ NROM ---------------------------

#ifdef DEBUG_MAPPER
static DECLFW(WriteHandler)
{
 FCEU_printf("bs %04x %02x\n",A,V);
}
#endif

static void NROMPower(void)
{
  setprg16(0x8000,0);
  setprg16(0xC000,~0);
  setchr8(0);
  SetReadHandler(0x8000,0xFFFF,CartBR);
  #ifdef DEBUG_MAPPER
  SetWriteHandler(0x4020,0xFFFF,WriteHandler);
  #endif
}

void NROM_Init(CartInfo *info)
{
  info->Power=NROMPower;
}
