/*****************************************************************************\
     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 _CPUMACRO_H_
#define _CPUMACRO_H_

#define rOP8(OP, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
	uint8	val = OpenBus = S9xGetByte(ADDR(READ)); \
	FUNC(val); \
}

#define rOP16(OP, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
	uint16	val = S9xGetWord(ADDR(READ), WRAP); \
	OpenBus = (uint8) (val >> 8); \
	FUNC(val); \
}

#define rOPC(OP, COND, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
	if (Check##COND()) \
	{ \
		uint8	val = OpenBus = S9xGetByte(ADDR(READ)); \
		FUNC(val); \
	} \
	else \
	{ \
		uint16	val = S9xGetWord(ADDR(READ), WRAP); \
		OpenBus = (uint8) (val >> 8); \
		FUNC(val); \
	} \
}

#define rOPM(OP, ADDR, WRAP, FUNC) \
rOPC(OP, Memory, ADDR, WRAP, FUNC)

#define rOPX(OP, ADDR, WRAP, FUNC) \
rOPC(OP, Index, ADDR, WRAP, FUNC)

#define wOP8(OP, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
	FUNC##8(ADDR(WRITE)); \
}

#define wOP16(OP, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
	FUNC##16(ADDR(WRITE), WRAP); \
}

#define wOPC(OP, COND, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
	if (Check##COND()) \
		FUNC##8(ADDR(WRITE)); \
	else \
		FUNC##16(ADDR(WRITE), WRAP); \
}

#define wOPM(OP, ADDR, WRAP, FUNC) \
wOPC(OP, Memory, ADDR, WRAP, FUNC)

#define wOPX(OP, ADDR, WRAP, FUNC) \
wOPC(OP, Index, ADDR, WRAP, FUNC)

#define mOP8(OP, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
	FUNC##8(ADDR(MODIFY)); \
}

#define mOP16(OP, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
	FUNC##16(ADDR(MODIFY), WRAP); \
}

#define mOPC(OP, COND, ADDR, WRAP, FUNC) \
static void Op##OP (void) \
{ \
	if (Check##COND()) \
		FUNC##8(ADDR(MODIFY)); \
	else \
		FUNC##16(ADDR(MODIFY), WRAP); \
}

#define mOPM(OP, ADDR, WRAP, FUNC) \
mOPC(OP, Memory, ADDR, WRAP, FUNC)

#define bOP(OP, REL, COND, CHK, E) \
static void Op##OP (void) \
{ \
	pair	newPC; \
	newPC.W = REL(JUMP); \
	if (COND) \
	{ \
		AddCycles(ONE_CYCLE); \
		if (E && Registers.PCh != newPC.B.h) \
			AddCycles(ONE_CYCLE); \
		if ((Registers.PCw & ~MEMMAP_MASK) != (newPC.W & ~MEMMAP_MASK)) \
			S9xSetPCBase(ICPU.ShiftedPB + newPC.W); \
		else \
			Registers.PCw = newPC.W; \
	} \
}


static inline void SetZN (uint16 Work16)
{
	ICPU._Zero = Work16 != 0;
	ICPU._Negative = (uint8) (Work16 >> 8);
}

static inline void SetZN (uint8 Work8)
{
	ICPU._Zero = Work8;
	ICPU._Negative = Work8;
}

static inline void ADC (uint16 Work16)
{
	if (CheckDecimal())
	{
		uint32 result;
		uint32 carry = CheckCarry();

		result = (Registers.A.W & 0x000F) + (Work16 & 0x000F) + carry;
		if (result > 0x0009)
			result += 0x0006;
		carry = (result > 0x000F);

		result = (Registers.A.W & 0x00F0) + (Work16 & 0x00F0) + (result & 0x000F) + carry * 0x10;
		if (result > 0x009F)
			result += 0x0060;
		carry = (result > 0x00FF);

		result = (Registers.A.W & 0x0F00) + (Work16 & 0x0F00) + (result & 0x00FF) + carry * 0x100;
		if (result > 0x09FF)
			result += 0x0600;
		carry = (result > 0x0FFF);

		result = (Registers.A.W & 0xF000) + (Work16 & 0xF000) + (result & 0x0FFF) + carry * 0x1000;

		if ((Registers.A.W & 0x8000) == (Work16 & 0x8000) && (Registers.A.W & 0x8000) != (result & 0x8000))
			SetOverflow();
		else
			ClearOverflow();

		if (result > 0x9FFF)
			result += 0x6000;

		if (result > 0xFFFF)
			SetCarry();
		else
			ClearCarry();

		Registers.A.W = result & 0xFFFF;
		SetZN(Registers.A.W);
	}
	else
	{
		uint32	Ans32 = Registers.A.W + Work16 + CheckCarry();

		ICPU._Carry = Ans32 >= 0x10000;

		if (~(Registers.A.W ^ Work16) & (Work16 ^ (uint16) Ans32) & 0x8000)
			SetOverflow();
		else
			ClearOverflow();

		Registers.A.W = (uint16) Ans32;
		SetZN(Registers.A.W);
	}
}

static inline void ADC (uint8 Work8)
{
	if (CheckDecimal())
	{
		uint32 result;
		uint32 carry = CheckCarry();

		result = (Registers.AL & 0x0F) + (Work8 & 0x0F) + carry;
		if ( result > 0x09 )
			result += 0x06;
		carry = (result > 0x0F);

		result = (Registers.AL & 0xF0) + (Work8 & 0xF0) + (result & 0x0F) + (carry * 0x10);

		if ((Registers.AL & 0x80) == (Work8 & 0x80) && (Registers.AL & 0x80) != (result & 0x80))
			SetOverflow();
		else
			ClearOverflow();

		if (result > 0x9F)
			result += 0x60;

		if (result > 0xFF)
			SetCarry();
		else
			ClearCarry();

		Registers.AL = result & 0xFF;
		SetZN(Registers.AL);
	}
	else
	{
		uint16	Ans16 = Registers.AL + Work8 + CheckCarry();

		ICPU._Carry = Ans16 >= 0x100;

		if (~(Registers.AL ^ Work8) & (Work8 ^ (uint8) Ans16) & 0x80)
			SetOverflow();
		else
			ClearOverflow();

		Registers.AL = (uint8) Ans16;
		SetZN(Registers.AL);
	}
}

static inline void AND (uint16 Work16)
{
	Registers.A.W &= Work16;
	SetZN(Registers.A.W);
}

static inline void AND (uint8 Work8)
{
	Registers.AL &= Work8;
	SetZN(Registers.AL);
}

static inline void ASL16 (uint32 OpAddress, s9xwrap_t w)
{
	uint16	Work16 = S9xGetWord(OpAddress, w);
	ICPU._Carry = (Work16 & 0x8000) != 0;
	Work16 <<= 1;
	AddCycles(ONE_CYCLE);
	S9xSetWord(Work16, OpAddress, w, WRITE_10);
	OpenBus = Work16 & 0xff;
	SetZN(Work16);
}

static inline void ASL8 (uint32 OpAddress)
{
	uint8	Work8 = S9xGetByte(OpAddress);
	ICPU._Carry = (Work8 & 0x80) != 0;
	Work8 <<= 1;
	AddCycles(ONE_CYCLE);
	S9xSetByte(Work8, OpAddress);
	OpenBus = Work8;
	SetZN(Work8);
}

static inline void BIT (uint16 Work16)
{
	ICPU._Overflow = (Work16 & 0x4000) != 0;
	ICPU._Negative = (uint8) (Work16 >> 8);
	ICPU._Zero = (Work16 & Registers.A.W) != 0;
}

static inline void BIT (uint8 Work8)
{
	ICPU._Overflow = (Work8 & 0x40) != 0;
	ICPU._Negative = Work8;
	ICPU._Zero = Work8 & Registers.AL;
}

static inline void CMP (uint16 val)
{
	int32	Int32 = (int32) Registers.A.W - (int32) val;
	ICPU._Carry = Int32 >= 0;
	SetZN((uint16) Int32);
}

static inline void CMP (uint8 val)
{
	int16	Int16 = (int16) Registers.AL - (int16) val;
	ICPU._Carry = Int16 >= 0;
	SetZN((uint8) Int16);
}

static inline void CPX (uint16 val)
{
	int32	Int32 = (int32) Registers.X.W - (int32) val;
	ICPU._Carry = Int32 >= 0;
	SetZN((uint16) Int32);
}

static inline void CPX (uint8 val)
{
	int16	Int16 = (int16) Registers.XL - (int16) val;
	ICPU._Carry = Int16 >= 0;
	SetZN((uint8) Int16);
}

static inline void CPY (uint16 val)
{
	int32	Int32 = (int32) Registers.Y.W - (int32) val;
	ICPU._Carry = Int32 >= 0;
	SetZN((uint16) Int32);
}

static inline void CPY (uint8 val)
{
	int16	Int16 = (int16) Registers.YL - (int16) val;
	ICPU._Carry = Int16 >= 0;
	SetZN((uint8) Int16);
}

static inline void DEC16 (uint32 OpAddress, s9xwrap_t w)
{
	uint16	Work16 = S9xGetWord(OpAddress, w) - 1;
	AddCycles(ONE_CYCLE);
	S9xSetWord(Work16, OpAddress, w, WRITE_10);
	OpenBus = Work16 & 0xff;
	SetZN(Work16);
}

static inline void DEC8 (uint32 OpAddress)
{
	uint8	Work8 = S9xGetByte(OpAddress) - 1;
	AddCycles(ONE_CYCLE);
	S9xSetByte(Work8, OpAddress);
	OpenBus = Work8;
	SetZN(Work8);
}

static inline void EOR (uint16 val)
{
	Registers.A.W ^= val;
	SetZN(Registers.A.W);
}

static inline void EOR (uint8 val)
{
	Registers.AL ^= val;
	SetZN(Registers.AL);
}

static inline void INC16 (uint32 OpAddress, s9xwrap_t w)
{
	uint16	Work16 = S9xGetWord(OpAddress, w) + 1;
	AddCycles(ONE_CYCLE);
	S9xSetWord(Work16, OpAddress, w, WRITE_10);
	OpenBus = Work16 & 0xff;
	SetZN(Work16);
}

static inline void INC8 (uint32 OpAddress)
{
	uint8	Work8 = S9xGetByte(OpAddress) + 1;
	AddCycles(ONE_CYCLE);
	S9xSetByte(Work8, OpAddress);
	OpenBus = Work8;
	SetZN(Work8);
}

static inline void LDA (uint16 val)
{
	Registers.A.W = val;
	SetZN(Registers.A.W);
}

static inline void LDA (uint8 val)
{
	Registers.AL = val;
	SetZN(Registers.AL);
}

static inline void LDX (uint16 val)
{
	Registers.X.W = val;
	SetZN(Registers.X.W);
}

static inline void LDX (uint8 val)
{
	Registers.XL = val;
	SetZN(Registers.XL);
}

static inline void LDY (uint16 val)
{
	Registers.Y.W = val;
	SetZN(Registers.Y.W);
}

static inline void LDY (uint8 val)
{
	Registers.YL = val;
	SetZN(Registers.YL);
}

static inline void LSR16 (uint32 OpAddress, s9xwrap_t w)
{
	uint16	Work16 = S9xGetWord(OpAddress, w);
	ICPU._Carry = Work16 & 1;
	Work16 >>= 1;
	AddCycles(ONE_CYCLE);
	S9xSetWord(Work16, OpAddress, w, WRITE_10);
	OpenBus = Work16 & 0xff;
	SetZN(Work16);
}

static inline void LSR8 (uint32 OpAddress)
{
	uint8	Work8 = S9xGetByte(OpAddress);
	ICPU._Carry = Work8 & 1;
	Work8 >>= 1;
	AddCycles(ONE_CYCLE);
	S9xSetByte(Work8, OpAddress);
	OpenBus = Work8;
	SetZN(Work8);
}

static inline void ORA (uint16 val)
{
	Registers.A.W |= val;
	SetZN(Registers.A.W);
}

static inline void ORA (uint8 val)
{
	Registers.AL |= val;
	SetZN(Registers.AL);
}

static inline void ROL16 (uint32 OpAddress, s9xwrap_t w)
{
	uint32	Work32 = (((uint32) S9xGetWord(OpAddress, w)) << 1) | CheckCarry();
	ICPU._Carry = Work32 >= 0x10000;
	AddCycles(ONE_CYCLE);
	S9xSetWord((uint16) Work32, OpAddress, w, WRITE_10);
	OpenBus = Work32 & 0xff;
	SetZN((uint16) Work32);
}

static inline void ROL8 (uint32 OpAddress)
{
	uint16	Work16 = (((uint16) S9xGetByte(OpAddress)) << 1) | CheckCarry();
	ICPU._Carry = Work16 >= 0x100;
	AddCycles(ONE_CYCLE);
	S9xSetByte((uint8) Work16, OpAddress);
	OpenBus = Work16 & 0xff;
	SetZN((uint8) Work16);
}

static inline void ROR16 (uint32 OpAddress, s9xwrap_t w)
{
	uint32	Work32 = ((uint32) S9xGetWord(OpAddress, w)) | (((uint32) CheckCarry()) << 16);
	ICPU._Carry = Work32 & 1;
	Work32 >>= 1;
	AddCycles(ONE_CYCLE);
	S9xSetWord((uint16) Work32, OpAddress, w, WRITE_10);
	OpenBus = Work32 & 0xff;
	SetZN((uint16) Work32);
}

static inline void ROR8 (uint32 OpAddress)
{
	uint16	Work16 = ((uint16) S9xGetByte(OpAddress)) | (((uint16) CheckCarry()) << 8);
	ICPU._Carry = Work16 & 1;
	Work16 >>= 1;
	AddCycles(ONE_CYCLE);
	S9xSetByte((uint8) Work16, OpAddress);
	OpenBus = Work16 & 0xff;
	SetZN((uint8) Work16);
}

static inline void SBC (uint16 Work16)
{
	if (CheckDecimal())
	{
		int result;
		int carry = CheckCarry();

		Work16 ^= 0xFFFF;

		result = (Registers.A.W & 0x000F) + (Work16 & 0x000F) + carry;
		if (result < 0x0010)
			result -= 0x0006;
		carry = (result > 0x000F);

		result = (Registers.A.W & 0x00F0) + (Work16 & 0x00F0) + (result & 0x000F) + carry * 0x10;
		if (result < 0x0100)
			result -= 0x0060;
		carry = (result > 0x00FF);

		result = (Registers.A.W & 0x0F00) + (Work16 & 0x0F00) + (result & 0x00FF) + carry * 0x100;
		if (result < 0x1000)
			result -= 0x0600;
		carry = (result > 0x0FFF);

		result = (Registers.A.W & 0xF000) + (Work16 & 0xF000) + (result & 0x0FFF) + carry * 0x1000;

		if (((Registers.A.W ^ Work16) & 0x8000) == 0 && ((Registers.A.W ^ result) & 0x8000))
			SetOverflow();
		else
			ClearOverflow();

		if (result < 0x10000)
			result -= 0x6000;

		if (result > 0xFFFF)
			SetCarry();
		else
			ClearCarry();

		Registers.A.W = result & 0xFFFF;
		SetZN(Registers.A.W);
	}
	else
	{
		int32	Int32 = (int32) Registers.A.W - (int32) Work16 + (int32) CheckCarry() - 1;

		ICPU._Carry = Int32 >= 0;

		if ((Registers.A.W ^ Work16) & (Registers.A.W ^ (uint16) Int32) & 0x8000)
			SetOverflow();
		else
			ClearOverflow();

		Registers.A.W = (uint16) Int32;
		SetZN(Registers.A.W);
	}
}

static inline void SBC (uint8 Work8)
{
	if (CheckDecimal())
	{
		int result;
		int carry = CheckCarry();

		Work8 ^= 0xFF;

		result = (Registers.AL & 0x0F) + (Work8 & 0x0F) + carry;
		if (result < 0x10)
			result -= 0x06;
		carry = (result > 0x0F);

		result = (Registers.AL & 0xF0) + (Work8 & 0xF0) + (result & 0x0F) + carry * 0x10;

		if ((Registers.AL & 0x80) == (Work8 & 0x80) && (Registers.AL & 0x80) != (result & 0x80))
			SetOverflow();
		else
			ClearOverflow();

		if (result < 0x100 )
			result -= 0x60;

		if (result > 0xFF)
			SetCarry();
		else
			ClearCarry();

		Registers.AL = result & 0xFF;
		SetZN(Registers.AL);
	}
	else
	{
		int16	Int16 = (int16) Registers.AL - (int16) Work8 + (int16) CheckCarry() - 1;

		ICPU._Carry = Int16 >= 0;

		if ((Registers.AL ^ Work8) & (Registers.AL ^ (uint8) Int16) & 0x80)
			SetOverflow();
		else
			ClearOverflow();

		Registers.AL = (uint8) Int16;
		SetZN(Registers.AL);
	}
}

static inline void STA16 (uint32 OpAddress, enum s9xwrap_t w)
{
	S9xSetWord(Registers.A.W, OpAddress, w);
	OpenBus = Registers.AH;
}

static inline void STA8 (uint32 OpAddress)
{
	S9xSetByte(Registers.AL, OpAddress);
	OpenBus = Registers.AL;
}

static inline void STX16 (uint32 OpAddress, enum s9xwrap_t w)
{
	S9xSetWord(Registers.X.W, OpAddress, w);
	OpenBus = Registers.XH;
}

static inline void STX8 (uint32 OpAddress)
{
	S9xSetByte(Registers.XL, OpAddress);
	OpenBus = Registers.XL;
}

static inline void STY16 (uint32 OpAddress, enum s9xwrap_t w)
{
	S9xSetWord(Registers.Y.W, OpAddress, w);
	OpenBus = Registers.YH;
}

static inline void STY8 (uint32 OpAddress)
{
	S9xSetByte(Registers.YL, OpAddress);
	OpenBus = Registers.YL;
}

static inline void STZ16 (uint32 OpAddress, enum s9xwrap_t w)
{
	S9xSetWord(0, OpAddress, w);
	OpenBus = 0;
}

static inline void STZ8 (uint32 OpAddress)
{
	S9xSetByte(0, OpAddress);
	OpenBus = 0;
}

static inline void TSB16 (uint32 OpAddress, enum s9xwrap_t w)
{
	uint16	Work16 = S9xGetWord(OpAddress, w);
	ICPU._Zero = (Work16 & Registers.A.W) != 0;
	Work16 |= Registers.A.W;
	AddCycles(ONE_CYCLE);
	S9xSetWord(Work16, OpAddress, w, WRITE_10);
	OpenBus = Work16 & 0xff;
}

static inline void TSB8 (uint32 OpAddress)
{
	uint8	Work8 = S9xGetByte(OpAddress);
	ICPU._Zero = Work8 & Registers.AL;
	Work8 |= Registers.AL;
	AddCycles(ONE_CYCLE);
	S9xSetByte(Work8, OpAddress);
	OpenBus = Work8;
}

static inline void TRB16 (uint32 OpAddress, enum s9xwrap_t w)
{
	uint16	Work16 = S9xGetWord(OpAddress, w);
	ICPU._Zero = (Work16 & Registers.A.W) != 0;
	Work16 &= ~Registers.A.W;
	AddCycles(ONE_CYCLE);
	S9xSetWord(Work16, OpAddress, w, WRITE_10);
	OpenBus = Work16 & 0xff;
}

static inline void TRB8 (uint32 OpAddress)
{
	uint8	Work8 = S9xGetByte(OpAddress);
	ICPU._Zero = Work8 & Registers.AL;
	Work8 &= ~Registers.AL;
	AddCycles(ONE_CYCLE);
	S9xSetByte(Work8, OpAddress);
	OpenBus = Work8;
}

#endif