#ifndef _HuC6280H
#define _HuC6280H

#define HUC6280_CRAZY_VERSION
//#define HUC6280_EXTRA_CRAZY

#define HUC6280_LAZY_FLAGS

#include <retro_inline.h>

#ifdef __cplusplus
extern "C" {
#endif

struct HuC6280
{
	#ifdef HUC6280_CRAZY_VERSION
        uintptr_t PC, PC_base;
	#else
	uint16 PC;
	#endif

        uint8 A,X,Y,S,P,mooPI;
	uint8 IRQMask, IRQMaskDelay;
	#ifdef HUC6280_LAZY_FLAGS
 	 uint32 ZNFlags;
	#endif
	uint8 MPR[9];		// 8, + 1 for PC overflow from $ffff to $10000
	uint8 timer_status;
	uintptr_t FastPageR[9];
	uint8 *Page1;
	//uint8 *PAGE1_W;
	//const uint8 *PAGE1_R;

        uint32 IRQlow;          /* Simulated IRQ pin held low(or is it high?).
                                   And other junk hooked on for speed reasons.*/
	int32 timestamp;

	int32 timer_value, timer_load;
        int32 timer_next_timestamp;

	uint32 in_block_move;
	uint16 bmt_src, bmt_dest, bmt_length;
	uint32 bmt_alternate;
	#define IBM_TIA	1
	#define IBM_TAI	2
	#define IBM_TDD 3
	#define IBM_TII 4
	#define IBM_TIN 5

	int32 previous_next_user_event;

	uint8 *FastMap[0x100];

	readfunc PCERead[0x100];
	writefunc PCEWrite[0x100];
};

void HuC6280_Run(int32 cycles);
void HuC6280_ResetTS(void);

extern struct HuC6280 HuCPU;

#define N_FLAG  0x80
#define V_FLAG  0x40
#define T_FLAG  0x20
#define B_FLAG  0x10
#define D_FLAG  0x08
#define I_FLAG  0x04
#define Z_FLAG  0x02
#define C_FLAG  0x01

#define NTSC_CPU 1789772.7272727272727272
#define PAL_CPU  1662607.125

#define MDFN_IQIRQ1	0x002
#define MDFN_IQIRQ2	0x001
#define MDFN_IQTIMER	0x004
#define MDFN_IQRESET    0x020

void HuC6280_Init(void) MDFN_COLD;
void HuC6280_Reset(void) MDFN_COLD;
void HuC6280_Power(void) MDFN_COLD;

static INLINE void HuC6280_IRQBegin(int w)
{
    HuCPU.IRQlow |= w;
}

static INLINE void HuC6280_IRQEnd(int w)
{
    HuCPU.IRQlow &= ~w;
}

int HuC6280_StateAction(StateMem *sm, int load, int data_only);

static INLINE void HuC6280_StealCycle(void)
{
 HuCPU.timestamp++;
}

static INLINE uint8 HuC6280_TimerRead(unsigned int A)
{
 uint8 tvr = HuCPU.timer_value;

 if(HuCPU.timer_next_timestamp == HuCPU.timestamp)
  tvr = (tvr - 1) & 0x7F;

 return(tvr | (PCEIODataBuffer & 0x80));
}

static INLINE void HuC6280_TimerWrite(unsigned int A, uint8 V)
{
 switch(A & 1)
 {
  case 0: HuCPU.timer_load = (V & 0x7F); break;
  case 1: if(V & 1) // Enable counter
          {
                if(HuCPU.timer_status == 0)
                {
                        HuCPU.timer_next_timestamp = HuCPU.timestamp + 1024;
                        HuCPU.timer_value = HuCPU.timer_load;
                }
          }
          HuCPU.timer_status = V & 1;
          break;
 }
}

static INLINE uint8 HuC6280_IRQStatusRead(unsigned int A)
{
 if(!(A & 2))
  return(PCEIODataBuffer);

 switch(A & 1)
 {
  case 0:
         HuC6280_IRQEnd(MDFN_IQTIMER);
         return(HuCPU.IRQMask ^ 0x7);
  case 1:
        {
         int status = 0;
         if(HuCPU.IRQlow & MDFN_IQIRQ1) status |= 2;
         if(HuCPU.IRQlow & MDFN_IQIRQ2) status |= 1;
         if(HuCPU.IRQlow & MDFN_IQTIMER) status |= 4;
         return(status | (PCEIODataBuffer & ~(1 | 2 | 4)));
        }
 }
 return(PCEIODataBuffer);
}

static INLINE void HuC6280_IRQStatusWrite(unsigned int A, uint8 V)
{
 if(!(A & 2)) return;
 switch(A & 1)
 {
  case 0: HuCPU.IRQMask = (V & 0x7) ^ 0x7; break;
  case 1: HuC6280_IRQEnd(MDFN_IQTIMER); break;
 }
}

#ifdef __cplusplus
}
#endif

#endif
