/*****************************************************************************\
     Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
                This file is licensed under the Snes9x License.
   For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/

#ifndef _SA1_H_
#define _SA1_H_

struct SSA1Registers
{
	uint8	DB;
	pair	P;
	pair	A;
	pair	D;
	pair	S;
	pair	X;
	pair	Y;
	PC_t	PC;
};

struct SSA1
{
	struct SOpcodes	*S9xOpcodes;
	uint8	*S9xOpLengths;
	uint8	_Carry;
	uint8	_Zero;
	uint8	_Negative;
	uint8	_Overflow;
	uint32	ShiftedPB;
	uint32	ShiftedDB;

	uint32	Flags;
	int32	Cycles;
	int32	PrevCycles;
	uint8	*PCBase;
	bool8	WaitingForInterrupt;

	uint8	*Map[MEMMAP_NUM_BLOCKS];
	uint8	*WriteMap[MEMMAP_NUM_BLOCKS];
	uint8	*BWRAM;

	bool8	in_char_dma;
	bool8	TimerIRQLastState;
	uint16	HTimerIRQPos;
	uint16	VTimerIRQPos;
	int16	HCounter;
	int16	VCounter;
	int16	PrevHCounter;
	int32	MemSpeed;
	int32	MemSpeedx2;
	int32	arithmetic_op;
	uint16	op1;
	uint16	op2;
	uint64	sum;
	bool8	overflow;
	uint8	VirtualBitmapFormat;
	uint8	variable_bit_pos;
};

#define SA1CheckCarry()		(SA1._Carry)
#define SA1CheckZero()		(SA1._Zero == 0)
#define SA1CheckIRQ()		(SA1Registers.PL & IRQ)
#define SA1CheckDecimal()	(SA1Registers.PL & Decimal)
#define SA1CheckIndex()		(SA1Registers.PL & IndexFlag)
#define SA1CheckMemory()	(SA1Registers.PL & MemoryFlag)
#define SA1CheckOverflow()	(SA1._Overflow)
#define SA1CheckNegative()	(SA1._Negative & 0x80)
#define SA1CheckEmulation()	(SA1Registers.P.W & Emulation)

#define SA1SetFlags(f)		(SA1Registers.P.W |= (f))
#define SA1ClearFlags(f)	(SA1Registers.P.W &= ~(f))
#define SA1CheckFlag(f)		(SA1Registers.PL & (f))

extern struct SSA1Registers	SA1Registers;
extern struct SSA1			SA1;
extern uint8				SA1OpenBus;
extern struct SOpcodes		S9xSA1OpcodesM1X1[256];
extern struct SOpcodes		S9xSA1OpcodesM1X0[256];
extern struct SOpcodes		S9xSA1OpcodesM0X1[256];
extern struct SOpcodes		S9xSA1OpcodesM0X0[256];
extern uint8				S9xOpLengthsM1X1[256];
extern uint8				S9xOpLengthsM1X0[256];
extern uint8				S9xOpLengthsM0X1[256];
extern uint8				S9xOpLengthsM0X0[256];

uint8 S9xSA1GetByte (uint32);
void S9xSA1SetByte (uint8, uint32);
uint16 S9xSA1GetWord (uint32, enum s9xwrap_t w = WRAP_NONE);
void S9xSA1SetWord (uint16, uint32, enum s9xwrap_t w = WRAP_NONE, enum s9xwriteorder_t o = WRITE_01);
void S9xSA1SetPCBase (uint32);
uint8 S9xGetSA1 (uint32);
void S9xSetSA1 (uint8, uint32);
void S9xSA1Init (void);
void S9xSA1MainLoop (void);
void S9xSA1PostLoadState (void);

static inline void S9xSA1UnpackStatus (void)
{
	SA1._Zero = (SA1Registers.PL & Zero) == 0;
	SA1._Negative = (SA1Registers.PL & Negative);
	SA1._Carry = (SA1Registers.PL & Carry);
	SA1._Overflow = (SA1Registers.PL & Overflow) >> 6;
}

static inline void S9xSA1PackStatus (void)
{
	SA1Registers.PL &= ~(Zero | Negative | Carry | Overflow);
	SA1Registers.PL |= SA1._Carry | ((SA1._Zero == 0) << 1) | (SA1._Negative & 0x80) | (SA1._Overflow << 6);
}

static inline void S9xSA1FixCycles (void)
{
	if (SA1CheckEmulation())
	{
		SA1.S9xOpcodes = S9xSA1OpcodesM1X1;
		SA1.S9xOpLengths = S9xOpLengthsM1X1;
	}
	else
	if (SA1CheckMemory())
	{
		if (SA1CheckIndex())
		{
			SA1.S9xOpcodes = S9xSA1OpcodesM1X1;
			SA1.S9xOpLengths = S9xOpLengthsM1X1;
		}
		else
		{
			SA1.S9xOpcodes = S9xSA1OpcodesM1X0;
			SA1.S9xOpLengths = S9xOpLengthsM1X0;
		}
	}
	else
	{
		if (SA1CheckIndex())
		{
			SA1.S9xOpcodes = S9xSA1OpcodesM0X1;
			SA1.S9xOpLengths = S9xOpLengthsM0X1;
		}
		else
		{
			SA1.S9xOpcodes = S9xSA1OpcodesM0X0;
			SA1.S9xOpLengths = S9xOpLengthsM0X0;
		}
	}
}

#endif