/* Mednafen - Multi-system Emulator
 *
 * 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 <stdint.h>
#include <string.h>
#include <boolean.h>

#include "pce.h"
#include "vdc.h"
#include "../state_helpers.h"

bool PCE_IsCD;
int pce_overclocked;
static uint8 dummy_bank[8192 + 8192];  // + 8192 for PC-as-ptr safety padding
struct HuC6280 HuCPU;

#define HU_PC              PC_local //HuCPU.PC
#define HU_PC_base	 HuCPU.PC_base
#define HU_A               HuCPU.A
#define HU_X               X_local	//HuCPU.X
#define HU_Y               Y_local	//HuCPU.Y
#define HU_S               HuCPU.S
#define HU_P               P_local	//HuCPU.P
#define HU_PI              HuCPU.mooPI
#define HU_IRQlow          HuCPU.IRQlow
#define HU_Page1		Page1_local
//HuCPU.Page1
//Page1_local	//HuCPU.Page1

#ifdef HUC6280_LAZY_FLAGS
 #define HU_ZNFlags	 HuCPU.ZNFlags
#endif


#ifdef HUC6280_CRAZY_VERSION
#define LOAD_LOCALS_PC()        uintptr_t PC_local = HuCPU.PC;
#else
#define LOAD_LOCALS_PC()        uint32 PC_local /*asm ("edi")*/ = HuCPU.PC; // asm ("edi") = HuCPU.PC;
#endif

#define LOAD_LOCALS()				\

#define SAVE_LOCALS()	HuCPU.PC = PC_local;	\
			HuCPU.X = X_local;	\
			HuCPU.Y = Y_local;	\
			HuCPU.P = P_local;	\
			HuCPU.Page1 = Page1_local;

#ifdef HUC6280_LAZY_FLAGS
 #define COMPRESS_FLAGS()	HU_P &= ~(N_FLAG | Z_FLAG); HU_P |= ((HU_ZNFlags >> 24) & 0x80) | ((HU_ZNFlags & 0xFF) ? 0 : Z_FLAG);
 //((((HU_ZNFlags & 0xFF) - 1) >> 8) & Z_FLAG);
 #define EXPAND_FLAGS()	HU_ZNFlags = (HU_P << 24) | ((HU_P & Z_FLAG) ^ Z_FLAG);
#else
 #define COMPRESS_FLAGS()
 #define EXPAND_FLAGS()
#endif

#ifdef HUC6280_CRAZY_VERSION
 #define GetRealPC() ((unsigned int)(HU_PC - HU_PC_base))
 #define GetRealPC_EXTERNAL() ((unsigned int)(HuCPU.PC - HuCPU.PC_base))
#else
 #define GetRealPC() (HU_PC)
 #define GetRealPC_EXTERNAL() (HuCPU.PC)
#endif

#ifdef HUC6280_CRAZY_VERSION
 #define SetPC(value) { unsigned int tempmoo = value; HU_PC = HuCPU.FastPageR[tempmoo >> 13] + tempmoo; 	\
	HU_PC_base = HU_PC - tempmoo; }
 #define SetPC_EXTERNAL(value) { unsigned int tempmoo = value; 	\
	HuCPU.PC = HuCPU.FastPageR[tempmoo >> 13] + tempmoo; HuCPU.PC_base = HuCPU.PC - tempmoo; }
#else
 #define SetPC(value) { HU_PC = (value); }
 #define SetPC_EXTERNAL(value) { HuCPU.PC = (value); }
#endif

// Page change PC, GET IT?!
#ifdef HUC6280_CRAZY_VERSION
 #define FixPC_PC() SetPC(GetRealPC());
#else
 #define FixPC_PC()
#endif

#define IncPC() HU_PC++;

#ifdef HUC6280_CRAZY_VERSION
 #define RdAtPC() (*(uint8*)HU_PC)
#else
 #define RdAtPC() RdOp(HU_PC)
#endif

// If we change this definition, we'll need to also fix HuC6280_StealCycle() in huc6280.h
#define ADDCYC(x) { HuCPU.timestamp += x; }

#define SET_MPR(arg_i, arg_v)				\
{							\
 const unsigned int wmpr = arg_i, wbank = arg_v;	\
 if(wmpr == 1)						\
 {							\
  HU_Page1 = HuCPU.FastMap[wbank];    \
 }							\
 HuCPU.MPR[wmpr] = wbank;					\
 HuCPU.FastPageR[wmpr] = (uintptr_t)HuCPU.FastMap[wbank] - wmpr * 8192;        \
}

void HuC6280_SetMPR(int i, int v)
{
   uint8 *Page1_local = HuCPU.Page1;

   SET_MPR(i, v);

   HuCPU.Page1 = Page1_local;
}


static void HuC6280_FlushMPRCache(void)
{
   int x;
   for(x = 0; x < 9; x++)
      HuC6280_SetMPR(x, HuCPU.MPR[x & 0x7]);
}

static INLINE uint8 RdMem(unsigned int A)
{
   uint8 wmpr = HuCPU.MPR[A >> 13];
 return(HuCPU.PCERead[wmpr]((wmpr << 13) | (A & 0x1FFF)));
}

static INLINE uint16 RdMem16(unsigned int A)
{
 uint16 ret;

 ret = RdMem(A);
 ret |= RdMem(A + 1) << 8;

 return(ret);
}

static INLINE void WrMem(unsigned int A, uint8 V)
{
   uint8 wmpr = HuCPU.MPR[A >> 13];
 HuCPU.PCEWrite[wmpr]((wmpr << 13) | (A & 0x1FFF), V);
}

static INLINE uint8 RdOp(unsigned int A)
{
 return *(uint8*)(HuCPU.FastPageR[A >> 13] + A);
}

#define PUSH(V) \
{       \
   HU_Page1[0x100 + HU_S] = V; \
   HU_S--;  \
}       

#define PUSH_PC()	\
{	\
   unsigned int real_pc = GetRealPC();	\
   PUSH(real_pc >> 8);	\
   PUSH(real_pc);		\
}

#define POP() HU_Page1[0x100 + ++HU_S]

#define POP_PC()	\
{	\
   unsigned int npc;	\
   npc = POP();	\
   npc |= POP() << 8;	\
   SetPC(npc);	\
}

// Hopefully we never RTS to 0x0000. ;)
#define POP_PC_AP()        \
{       \
   uint32 npc;      \
   npc = POP();   \
   npc |= POP() << 8;     \
   npc++;		\
   SetPC(npc);    \
}

/* Some of these operations will only make sense if you know what the flag
   constants are. */

#ifdef HUC6280_LAZY_FLAGS
#define X_ZN(zort)      { HU_ZNFlags = (int32)(int8)(uint8)(zort); }
#define X_ZN_BIT(opres, argie)	 { HU_ZNFlags = (opres) | ((argie) << 24); }
#else
static uint8 ZNTable[256];
#define X_ZN(zort)      HU_P&=~(Z_FLAG|N_FLAG);HU_P|=ZNTable[zort]
#define X_ZN_BIT(opres, argie)	{ HU_P &= ~(Z_FLAG | N_FLAG); HU_P |= ZNTable[opres] & Z_FLAG; HU_P |= argie & N_FLAG; }
#endif

#define JR(cond)        \
{               \
   if(cond)       \
   {      \
      int32 disp;   \
      disp = 1 + (int8)RdAtPC();      \
      ADDCYC(2);    \
      HU_PC+=disp;    \
   }      \
   else IncPC();  \
}

#define BRA            \
{                      \
   int32 disp;           \
   disp = 1 + (int8)RdAtPC();      \
   HU_PC+=disp;            \
}

#define BBRi(bitto) JR(!(x & (1 << bitto)))
#define BBSi(bitto) JR(x & (1 << bitto))

#define ST0 VDC_Write_ST(0, x)
#define ST1 VDC_Write_ST(2, x)
#define ST2 VDC_Write_ST(3, x)

#define LDA	   HU_A=x;X_ZN(HU_A)
#define LDX	   HU_X=x;X_ZN(HU_X)
#define LDY        HU_Y=x;X_ZN(HU_Y)

/*  All of the freaky arithmetic operations. */
#define AND        HU_A&=x;X_ZN(HU_A);

#define BIT        HU_P&=~V_FLAG; X_ZN_BIT(x & HU_A, x); HU_P |= x & V_FLAG;
#define EOR        HU_A^=x;X_ZN(HU_A);
#define ORA        HU_A|=x;X_ZN(HU_A);

#define ADC  {	\
   if(HU_P & D_FLAG)	\
   {		\
		uint32 tmp;				\
		tmp = (HU_A & 0x0F) + (x & 0x0F) + (HU_P & 1);	\
		if(tmp >= 0x0A)				\
		 tmp += 0x06;				\
		tmp += (HU_A & 0xF0) + (x & 0xF0);	\
		if(tmp >= 0xA0)				\
		 tmp += 0x60;				\
		HU_P &= ~C_FLAG;			\
		if(tmp & 0xFF00)			\
		 HU_P |= C_FLAG;			\
		HU_A = tmp;				\
		X_ZN(HU_A);				\
   }	\
   else	\
   {	\
      uint32 l=HU_A+x+(HU_P&1);	\
      HU_P&=~(C_FLAG|V_FLAG);	\
      HU_P|=((((HU_A^x)&0x80)^0x80) & ((HU_A^l)&0x80))>>1;	\
      HU_P|=(l>>8)&C_FLAG;	\
      HU_A=l;	\
      X_ZN(HU_A);	\
   }	\
}

#define SBC  if(HU_P & D_FLAG)	\
{		\
	      const uint8 m = (HU_A & 0xF) - (x & 0xF) - ((HU_P & 1) ^ 1);	\
    	      const uint8 n = (HU_A >> 4) - (x >> 4) - ((m >> 4) & 1);	\
	      uint8 res = (n << 4) | (m & 0xF);				\
	      if(m & 0x10)						\
	       res -= 0x06;						\
	      if(n & 0x10)						\
	       res -= 0x60;						\
	      HU_A = res;						\
	      HU_P &= ~C_FLAG;						\
	      HU_P |= ((n >> 4) & 0x1) ^ 1;				\
	      X_ZN(HU_A);						\
}	else {	\
   uint32 l=HU_A-x-((HU_P&1)^1);	\
   HU_P&=~(C_FLAG|V_FLAG);	\
   HU_P|=((HU_A^l)&(HU_A^x)&0x80)>>1;	\
   HU_P|=((l>>8)&C_FLAG)^C_FLAG;	\
   HU_A=l;	\
   X_ZN(HU_A);	\
}

#define CMPL(a1,a2) {	\
   uint32 t=a1-a2;	\
   X_ZN(t&0xFF);	\
   HU_P&=~C_FLAG;	\
   HU_P|=((t>>8)&C_FLAG)^C_FLAG;	\
}

#define TAM \
   for(i = 0; i < 8; i ++) {               \
   if(x & (1 << i))        \
   {	\
      SET_MPR(i, HU_A);	\
   }	\
} SET_MPR(8, HuCPU.MPR[0]);

#define TMA	\
   for(i = 0; i < 8; i ++) {		\
   if(x & (1 << i))	\
   HU_A = HuCPU.MPR[i];	\
}	

#define CSL	
#define CSH	

#define RMB(bitto)	x &= ~(1 << (bitto & 7))
#define SMB(bitto)	x |= 1 << (bitto & 7)

#define TSB   { HU_P &= ~V_FLAG; X_ZN_BIT(x | HU_A, x); HU_P |= x & V_FLAG; x |= HU_A; }
#define TRB     { HU_P &= ~V_FLAG; X_ZN_BIT(x & ~HU_A, x); HU_P |= x & V_FLAG; x &= ~HU_A; }
#define TST	{ HU_P &= ~V_FLAG; X_ZN_BIT(x & zoomhack, x); HU_P |= x & V_FLAG; }

#define CMP		CMPL(HU_A,x)
#define CPX		CMPL(HU_X,x)
#define CPY	      	CMPL(HU_Y,x)

/* The following operations modify the byte being worked on. */
#define DEC       	x--;X_ZN(x)
#define INC		x++;X_ZN(x)

#define ASL        HU_P&=~C_FLAG;HU_P|=x>>7;x<<=1;X_ZN(x)
#define LSR	HU_P&=~C_FLAG;HU_P|=x&1;x>>=1;X_ZN(x)

#define ROL	{	\
   uint8 l=x>>7;	\
   x<<=1;	\
   x|=HU_P&C_FLAG;	\
   HU_P&=~C_FLAG;	\
   HU_P|=l;	\
   X_ZN(x);	\
}
#define ROR	{	\
   uint8 l=x&1;	\
   x>>=1;	\
   x|=(HU_P&C_FLAG)<<7;	\
   HU_P&=~C_FLAG;	\
   HU_P|=l;	\
   X_ZN(x);	\
}

/* Absolute */
#define GetAB(target)   \
{       		\
   target=RdAtPC();       \
   IncPC();       	\
   target|=RdAtPC()<<8;   \
   IncPC();       	\
}

/* Absolute Indexed(for reads) */
#define GetABI(target, i)	\
{				\
   unsigned int tmp;		\
   GetAB(tmp);			\
   target=tmp;			\
   target+=i;			\
}

/* Zero Page */
#define GetZP(target)	\
{			\
   target=RdAtPC(); 	\
   IncPC();		\
}

/* Zero Page Indexed */
#define GetZPI(target,i)	\
{				\
   target=i+RdAtPC();		\
   IncPC();			\
}

/* Indirect */
#define GetIND(target)		\
{       			\
   uint8 tmp;			\
   tmp=RdAtPC();			\
   IncPC();			\
   target=HU_Page1[tmp];		\
   tmp++;				\
   target|=HU_Page1[tmp]<<8;       	\
}


/* Indexed Indirect */
#define GetIX(target)		\
{				\
   uint8 tmp;			\
   tmp=RdAtPC();			\
   IncPC();			\
   tmp+=HU_X;			\
   target=HU_Page1[tmp];		\
   tmp++;				\
   target|=HU_Page1[tmp] <<8;	\
}

/* Indirect Indexed(for reads) */
#define GetIY(target)	\
{			\
   unsigned int rt;	\
   uint8 tmp;		\
   tmp=RdAtPC();		\
   rt=HU_Page1[tmp];	\
   tmp++;			\
   rt|=HU_Page1[tmp]<<8;	\
   target = (rt + HU_Y);	\
   IncPC();		\
}

/* Now come the macros to wrap up all of the above stuff addressing mode functions
   and operation macros.  Note that operation macros will always operate(redundant
   redundant) on the variable "x".
   */

#define RMW_A(op) {uint8 x=HU_A; op; HU_A=x; break; } /* Meh... */
#define RMW_AB(op) {unsigned int EA; uint8 x; GetAB(EA); x=RdMem(EA); op; WrMem(EA,x); break; }
#define RMW_ABI(reg,op) {unsigned int EA; uint8 x; GetABI(EA,reg); x=RdMem(EA); op; WrMem(EA,x); break; }
#define RMW_ABX(op)	RMW_ABI(HU_X,op)
#define RMW_ABY(op)	RMW_ABI(HU_Y,op)
#define RMW_IND(op) { unsigned int EA; uint8 x; GetIND(EA); x = RdMem(EA); op; WrMem(EA, x); break; }
#define RMW_IX(op)  { unsigned int EA; uint8 x; GetIX(EA); x=RdMem(EA); op; WrMem(EA,x); break; }
#define RMW_IY(op)  { unsigned int EA; uint8 x; GetIY(EA); x=RdMem(EA); op; WrMem(EA,x); break; }
#define RMW_ZP(op)  { uint8 EA; uint8 x; GetZP(EA); x=HU_Page1[EA]; op; HU_Page1[EA] = x; break; }
#define RMW_ZPX(op) { uint8 EA; uint8 x; GetZPI(EA,HU_X); x=HU_Page1[EA]; op; HU_Page1[EA] = x; break;}

#define LD_IM(op)	{ uint8 x; x=RdAtPC(); IncPC(); op; break; }
#define LD_ZP(op)	{ uint8 EA; uint8 x; GetZP(EA); x=HU_Page1[EA]; op; break; }
#define LD_ZPX(op) 	{ uint8 EA; uint8 x; GetZPI(EA,HU_X); x=HU_Page1[EA]; op; break; }
#define LD_ZPY(op)  	{ uint8 EA; uint8 x; GetZPI(EA,HU_Y); x=HU_Page1[EA]; op; break; }
#define LD_AB(op)	{ unsigned int EA; uint8 x; GetAB(EA); x=RdMem(EA); op; break; }
#define LD_ABI(reg,op)  { unsigned int EA; uint8 x; GetABI(EA,reg); x=RdMem(EA); op; break; }
#define LD_ABX(op)	LD_ABI(HU_X,op)
#define LD_ABY(op)	LD_ABI(HU_Y,op)

#define LD_IND(op)	{ unsigned int EA; uint8 x; GetIND(EA); x=RdMem(EA); op; break; }
#define LD_IX(op)	{ unsigned int EA; uint8 x; GetIX(EA); x=RdMem(EA); op; break; }
#define LD_IY(op)	{ unsigned int EA; uint8 x; GetIY(EA); x=RdMem(EA); op; break; }

#define BMT_PREHONK(pork) HuCPU.in_block_move = IBM_##pork;
#define BMT_HONKHONK(pork) if(HuCPU.timestamp >= next_user_event) goto GetOutBMT; continue_the_##pork:

#define BMT_TDD	BMT_PREHONK(TDD); do { ADDCYC(6); WrMem(HuCPU.bmt_dest, RdMem(HuCPU.bmt_src)); HuCPU.bmt_src--; HuCPU.bmt_dest--; BMT_HONKHONK(TDD); HuCPU.bmt_length--; } while(HuCPU.bmt_length);
#define BMT_TAI BMT_PREHONK(TAI); {HuCPU.bmt_alternate = 0; do { ADDCYC(6); WrMem(HuCPU.bmt_dest, RdMem(HuCPU.bmt_src + HuCPU.bmt_alternate)); HuCPU.bmt_dest++; HuCPU.bmt_alternate ^= 1; BMT_HONKHONK(TAI); HuCPU.bmt_length--; } while(HuCPU.bmt_length); }
#define BMT_TIA BMT_PREHONK(TIA); {HuCPU.bmt_alternate = 0; do { ADDCYC(6); WrMem(HuCPU.bmt_dest + HuCPU.bmt_alternate, RdMem(HuCPU.bmt_src)); HuCPU.bmt_src++; HuCPU.bmt_alternate ^= 1; BMT_HONKHONK(TIA); HuCPU.bmt_length--; } while(HuCPU.bmt_length); } 
#define BMT_TII BMT_PREHONK(TII); do { ADDCYC(6); WrMem(HuCPU.bmt_dest, RdMem(HuCPU.bmt_src)); HuCPU.bmt_src++; HuCPU.bmt_dest++; BMT_HONKHONK(TII); HuCPU.bmt_length--; } while(HuCPU.bmt_length); 
#define BMT_TIN BMT_PREHONK(TIN); do { ADDCYC(6); WrMem(HuCPU.bmt_dest, RdMem(HuCPU.bmt_src)); HuCPU.bmt_src++; BMT_HONKHONK(TIN); HuCPU.bmt_length--; } while(HuCPU.bmt_length);

// Block memory transfer load
#define LD_BMT(op)	{ PUSH(HU_Y); PUSH(HU_A); PUSH(HU_X); GetAB(HuCPU.bmt_src); GetAB(HuCPU.bmt_dest); GetAB(HuCPU.bmt_length); op; HuCPU.in_block_move = 0; HU_X = POP(); HU_A = POP(); HU_Y = POP(); break; }

#define ST_ZP(r)	{uint8 EA; GetZP(EA); HU_Page1[EA] = r; break;}
#define ST_ZPX(r)	{uint8 EA; GetZPI(EA,HU_X); HU_Page1[EA] = r; break;}
#define ST_ZPY(r)	{uint8 EA; GetZPI(EA,HU_Y); HU_Page1[EA] = r; break;}
#define ST_AB(r)	{unsigned int EA; GetAB(EA); WrMem(EA, r); break;}
#define ST_ABI(reg,r)	{unsigned int EA; GetABI(EA,reg); WrMem(EA,r); break; }
#define ST_ABX(r)	ST_ABI(HU_X,r)
#define ST_ABY(r)	ST_ABI(HU_Y,r)

#define ST_IND(r)	{unsigned int EA; GetIND(EA); WrMem(EA,r); break; }
#define ST_IX(r)	{unsigned int EA; GetIX(EA); WrMem(EA,r); break; }
#define ST_IY(r)	{unsigned int EA; GetIY(EA); WrMem(EA,r); break; }

static const uint8 CycTable[256] =
{                             
   /*0x00*/ 8, 7, 3, 4, 6, 4, 6, 7, 3, 2, 2, 2, 7, 5, 7, 6, 
   /*0x10*/ 2, 7, 7, 4, 6, 4, 6, 7, 2, 5, 2, 2, 7, 5, 7, 6, 
   /*0x20*/ 7, 7, 3, 4, 4, 4, 6, 7, 4, 2, 2, 2, 5, 5, 7, 6, 
   /*0x30*/ 2, 7, 7, 2, 4, 4, 6, 7, 2, 5, 2, 2, 5, 5, 7, 6, 
   /*0x40*/ 7, 7, 3, 4, 8, 4, 6, 7, 3, 2, 2, 2, 4, 5, 7, 6, 
   /*0x50*/ 2, 7, 7, 5, 3, 4, 6, 7, 2, 5, 3, 2, 2, 5, 7, 6, 
   /*0x60*/ 7, 7, 2, 2, 4, 4, 6, 7, 4, 2, 2, 2, 7, 5, 7, 6, 
   /*0x70*/ 2, 7, 7, 17, 4, 4, 6, 7, 2, 5, 4, 2, 7, 5, 7, 6, 

   /*0x80*/ 4, 7, 2, 7, 4, 4, 4, 7, 2, 2, 2, 2, 5, 5, 5, 6, 
   /*0x90*/ 2, 7, 7, 8, 4, 4, 4, 7, 2, 5, 2, 2, 5, 5, 5, 6, 
   /*0xA0*/ 2, 7, 2, 7, 4, 4, 4, 7, 2, 2, 2, 2, 5, 5, 5, 6, 
   /*0xB0*/ 2, 7, 7, 8, 4, 4, 4, 7, 2, 5, 2, 2, 5, 5, 5, 6, 
   /*0xC0*/ 2, 7, 2, 17, 4, 4, 6, 7, 2, 2, 2, 2, 5, 5, 7, 6, 
   /*0xD0*/ 2, 7, 7, 17, 3, 4, 6, 7, 2, 5, 3, 2, 2, 5, 7, 6, 
   /*0xE0*/ 2, 7, 2, 17, 4, 4, 6, 7, 2, 2, 2, 2, 5, 5, 7, 6, 
   /*0xF0*/ 2, 7, 7, 17, 2, 4, 6, 7, 2, 5, 4, 2, 2, 5, 7, 6, 
};

void HuC6280_Reset(void)
{
   int i;
   unsigned int npc;
   HuCPU.timer_next_timestamp = HuCPU.timestamp + 1024;

   HuCPU.timer_load = 0;
   HuCPU.timer_value = 0;
   HuCPU.timer_status = 0;
   HuCPU.in_block_move = 0;

   HuCPU.IRQMask = HuCPU.IRQMaskDelay = 7;

   HuC6280_SetMPR(0, 0xFF);
   HuC6280_SetMPR(8, 0xFF);
   HuC6280_SetMPR(1, 0xF8);

   for(i = 2; i < 8; i++)
      HuC6280_SetMPR(i, 0);

   npc = RdMem16(0xFFFE);

#define PC_local HuCPU.PC
   SetPC(npc);
#undef PC_local

   HuCPU.mooPI = I_FLAG;
   HuCPU.P = I_FLAG;

   HU_IRQlow = 0;
}

void HuC6280_Init(void)
{
   int x;
   unsigned i;
   memset(&HuCPU,0,sizeof(HuCPU));

#ifdef HUC6280_LAZY_FLAGS
#else
   for(x=0; x < 256; x++)
   {
      if(!x) ZNTable[x]=Z_FLAG;
      else if (x&0x80) ZNTable[x]=N_FLAG;
      else ZNTable[x]=0;
   }
#endif

   for (i = 0; i < 0x100; i++)
      HuCPU.FastMap[i] = dummy_bank;
}

void HuC6280_Power(void)
{
   int i;
   memset(dummy_bank, 0, sizeof(dummy_bank));

   HuCPU.IRQlow = 0;

   HuCPU.A = 0;
   HuCPU.X = 0;
   HuCPU.Y = 0;
   HuCPU.S = 0;
   HuCPU.P = 0;
   HuCPU.mooPI = 0;

   HuCPU.PC = 0;

   HuCPU.timestamp = 0;

   for (i = 0; i < 9; i++)
   {
      HuCPU.MPR[i] = 0;
      HuCPU.FastPageR[i] = 0;
   }  
   HuC6280_Reset();
}

void HuC6280_Run(int32 cycles)
{
   int i;
   int32 next_event;
   const int32 next_user_event = HuCPU.previous_next_user_event + cycles * pce_overclocked;

   HuCPU.previous_next_user_event = next_user_event;

   {
#ifdef HUC6280_CRAZY_VERSION
      uintptr_t PC_local = HuCPU.PC;
#else
      uint32 PC_local = HuCPU.PC;
#endif
      uint8 X_local = HuCPU.X;
      uint8 Y_local = HuCPU.Y;
      uint8 P_local = HuCPU.P;
      uint8 *Page1_local = HuCPU.Page1;

      if(HuCPU.timestamp >= next_user_event)
         return;

      if(HuCPU.in_block_move)
      {
         next_event = (next_user_event < HuCPU.timer_next_timestamp) ? next_user_event : HuCPU.timer_next_timestamp;

         switch(HuCPU.in_block_move)
         {
            case IBM_TIA: goto continue_the_TIA;
            case IBM_TAI: goto continue_the_TAI;
            case IBM_TDD: goto continue_the_TDD;
            case IBM_TII: goto continue_the_TII;
            case IBM_TIN:	goto continue_the_TIN;
         }
      }

      while(MDFN_LIKELY(HuCPU.timestamp < next_user_event))
      {
         next_event = (next_user_event < HuCPU.timer_next_timestamp) ? next_user_event : HuCPU.timer_next_timestamp;

         while(MDFN_LIKELY(HuCPU.timestamp < next_event))
         {
            uint8 b1;

            if(HU_IRQlow)
            {
               if(!(HU_PI&I_FLAG))
               {
                  uint32 tmpa = 0;

                  if(HU_IRQlow & MDFN_IQTIMER & HuCPU.IRQMaskDelay)
                     tmpa = 0xFFFA;
                  else if((HU_IRQlow & MDFN_IQIRQ1 & HuCPU.IRQMaskDelay) || ((HU_IRQlow >> 8) & MDFN_IQIRQ1 & HuCPU.IRQMaskDelay))
                     tmpa = 0xFFF8;
                  else if(HU_IRQlow & MDFN_IQIRQ2 & HuCPU.IRQMaskDelay)
                     tmpa = 0xFFF6;

                  if(tmpa)
                  {
                     unsigned int npc;

                     ADDCYC(8);
                     PUSH_PC();

                     COMPRESS_FLAGS();
                     PUSH((HU_P&~B_FLAG));
                     HU_P |= I_FLAG;
                     HU_P &= ~(T_FLAG | D_FLAG);
                     HU_PI = HU_P;

                     npc = RdMem16(tmpa);
                     SetPC(npc);

                     if(tmpa == 0xFFF8)
                        HU_IRQlow &= ~0x200;

                     continue;
                  }
               }
            }	// end if(HU_IRQlow)

            HU_PI = HU_P;
            HuCPU.IRQMaskDelay = HuCPU.IRQMask;

            b1 = RdAtPC();

            ADDCYC(CycTable[b1]);

            IncPC();

            switch(b1)
            {
#include "huc6280_ops.h"
            } 

#ifndef HUC6280_EXTRA_CRAZY
            FixPC_PC();
#endif
         }	// end while(HuCPU.timestamp < next_event)

         while(HuCPU.timestamp >= HuCPU.timer_next_timestamp)
         {
            HuCPU.timer_next_timestamp += 1024 * pce_overclocked;

            if(HuCPU.timer_status)
            {
               HuCPU.timer_value --;
               if(HuCPU.timer_value < 0)
               {
                  HuCPU.timer_value = HuCPU.timer_load;
                  HuC6280_IRQBegin(MDFN_IQTIMER);
               }
            }
         }
      } // end while(HuCPU.timestamp < next_user_event)

GetOutBMT:

      SAVE_LOCALS();
   }
}

void HuC6280_ResetTS(void)
{
   HuCPU.timer_next_timestamp -= HuCPU.timestamp;
   HuCPU.previous_next_user_event -= HuCPU.timestamp;
   HuCPU.timestamp = 0;
}

int HuC6280_StateAction(StateMem *sm, int load, int data_only)
{
   int ret;
   uint16 tmp_PC = GetRealPC_EXTERNAL();

#define P_local HuCPU.P
   COMPRESS_FLAGS();

   {
      SFORMAT SFCPU[]=
      {
         SFVARN(tmp_PC, "PC"),
         SFVARN(HuCPU.A, "A"),
         SFVARN(HuCPU.P, "P"),
         SFVARN(HuCPU.X, "X"),
         SFVARN(HuCPU.Y, "Y"),
         SFVARN(HuCPU.S, "S"),
         SFVARN(HuCPU.mooPI, "PI"),

         SFVARN(HuCPU.IRQMask, "IRQMask"),
         SFVARN(HuCPU.IRQMaskDelay, "IRQMaskDelay"),
         SFARRAYN(HuCPU.MPR, 8, "MPR"),
         SFVARN(HuCPU.timer_status, "timer_status"),
         SFVARN(HuCPU.timer_value, "timer_value"),
         SFVARN(HuCPU.timer_load, "timer_load"),


         SFVARN(HuCPU.IRQlow, "IRQlow"),
         SFVARN(HuCPU.in_block_move, "IBM"),
         SFVARN(HuCPU.bmt_src, "IBM_SRC"),
         SFVARN(HuCPU.bmt_dest, "IBM_DEST"),
         SFVARN(HuCPU.bmt_length, "IBM_LENGTH"),
         SFVARN(HuCPU.bmt_alternate, "IBM_ALTERNATE"),

         SFVARN(HuCPU.timestamp, "timestamp"),
         SFVARN(HuCPU.timer_next_timestamp, "timer_next_timestamp"),
         SFVARN(HuCPU.previous_next_user_event, "previous_next_user_event"),

         SFEND
      };

      ret = MDFNSS_StateAction(sm, load, data_only, SFCPU, "CPU", false);

      if(load)
      {
         // Update MPR cache
         HuC6280_FlushMPRCache();

         // This must be after the MPR cache is updated:
         SetPC_EXTERNAL(tmp_PC);
      }

      EXPAND_FLAGS();
#undef P_local
   }

   return(ret);
}
