/* 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 cmdin;
static uint8 cmd;
static uint8 regs[8];
static uint8 master,chrm;

static int32 IRQCount,IRQa;

static void DoPRG(void)
{
 if(master&0x80)
 {
  if(master&0x20)
  {
   setprg32(0x8000,(master&0xF)>>1);
  }
  else
  {
   setprg16(0x8000,master&0xF);
   setprg16(0xC000,master&0xF);  
  }
 }
 else
 {
  setprg8(0xA000,regs[4]);
  setprg8(0xE000,~0);
  if(cmd&0x40)
  {
   setprg8(0xC000,regs[2]);
   setprg8(0x8000,~1);
  }
  else
  {
   setprg8(0x8000,regs[2]);
   setprg8(0xC000,~1);
  }
 }
}

static void DoCHR(void)
{
 uint32 base=(cmd&0x80)<<5;
 int orie=(chrm&0x4)<<6;

 setchr2(0x0000^base,(orie|regs[0])>>1); //K
 setchr2(0x0800^base,(orie|regs[3])>>1); //43
 
 setchr1(0x1000,orie|regs[1]);
 setchr1(0x1400,orie|regs[5]);
 setchr1(0x1800,orie|regs[6]);
 setchr1(0x1c00,orie|regs[7]);
}

static DECLFW(UNL8237Write)
{
 switch(A&0xF000)
 {
  case 0xf000:IRQCount=V;break;
  case 0xE000:X6502_IRQEnd(FCEU_IQEXT);break;
 }
// if(A<0x8000)
//  printf("$%04x:$%02x, %d\n",A&0xFFFF,V,scanline);
 if(A==0x5000)
 {
  master=V;
  DoPRG();
  DoCHR();  
 }
 else if(A==0x5001)
 {
  chrm=V;
  DoCHR();
 }
 else
 switch(A&0xE000)
 {
  case 0x8000:setmirror(((V|(V>>7))&1)^1);break;
  case 0xa000:cmd=V;cmdin=1;DoPRG();DoCHR();break;
  case 0xC000:if(!cmdin) break;
	      regs[cmd&7]=V;
	      DoPRG();
	      DoCHR();
	      cmdin=0;
	      break;
 }
}

static void UNL8237Reset(void)
{
  int x;

  for(x=0;x<8;x++) regs[x]=0;  
  master=chrm=cmd=cmdin=IRQCount=IRQa=0;
  SetReadHandler(0x8000,0xFFFF,CartBR);
  SetWriteHandler(0x5000,0xFFFF,UNL8237Write);
  DoPRG();
  DoCHR();
}

static void hooko(void)
{
 if(IRQCount)
 {
  IRQCount--;
  if(!IRQCount)
  {
   X6502_IRQBegin(FCEU_IQEXT);
   //printf("IRQ: %d\n",scanline);
  }
 }
}

static void restoreo(int version)
{
 DoPRG();
 DoCHR();
}

void UNL8237_Init(CartInfo *info)
{
  GameStateRestore=restoreo;
  GameHBIRQHook=hooko;
  info->Power=UNL8237Reset;

  AddExState(regs, 8, 0, "REGS");
  AddExState(&IRQCount, 4, 1, "IRQC");
  AddExState(&IRQa, 1, 0, "IRQA");
  AddExState(&master, 1, 0, "MAST");
  AddExState(&chrm,1,0,"CHRM");
  AddExState(&cmd,1,0,"CMD");
  AddExState(&cmdin,1,0,"CMDI");
}
