/************************
Add/substract operations
*************************/
void Cpu::Add8(uint8_t value)
{
	uint32_t result;
	if(CheckFlag(ProcFlags::Decimal)) {
		result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry);
		if(result > 0x09) result += 0x06;
		result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F);
	} else {
		result = (_state.A & 0xFF) + value + (_state.PS & ProcFlags::Carry);
	}

	if(~(_state.A ^ value) & (_state.A ^ result) & 0x80) {
		SetFlags(ProcFlags::Overflow);
	} else {
		ClearFlags(ProcFlags::Overflow);
	}

	if(CheckFlag(ProcFlags::Decimal) && result > 0x9F) {
		result += 0x60;
	}

	ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero);
	SetZeroNegativeFlags((uint8_t)result);

	if(result > 0xFF) {
		SetFlags(ProcFlags::Carry);
	}

	_state.A = (_state.A & 0xFF00) | (uint8_t)result;
}

void Cpu::Add16(uint16_t value)
{
	uint32_t result;
	if(CheckFlag(ProcFlags::Decimal)) {
		result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry);

		if(result > 0x09) result += 0x06;
		result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F);

		if(result > 0x9F) result += 0x60;
		result = (_state.A & 0xF00) + (value & 0xF00) + (result > 0xFF ? 0x100 : 0) + (result & 0xFF);

		if(result > 0x9FF) result += 0x600;
		result = (_state.A & 0xF000) + (value & 0xF000) + (result > 0xFFF ? 0x1000 : 0) + (result & 0xFFF);
	} else {
		result = _state.A + value + (_state.PS & ProcFlags::Carry);
	}

	if(~(_state.A ^ value) & (_state.A ^ result) & 0x8000) {
		SetFlags(ProcFlags::Overflow);
	} else {
		ClearFlags(ProcFlags::Overflow);
	}

	if(CheckFlag(ProcFlags::Decimal) && result > 0x9FFF) {
		result += 0x6000;
	}

	ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero);
	SetZeroNegativeFlags((uint16_t)result);

	if(result > 0xFFFF) {
		SetFlags(ProcFlags::Carry);
	}

	_state.A = (uint16_t)result;
}

void Cpu::ADC()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		Add8(GetByteValue());
	} else {
		Add16(GetWordValue());
	}
}

void Cpu::Sub8(uint8_t value)
{
	int32_t result;
	if(CheckFlag(ProcFlags::Decimal)) {
		result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry);
		if(result <= 0x0F) result -= 0x06;
		result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F);
	} else {
		result = (_state.A & 0xFF) + value + (_state.PS & ProcFlags::Carry);
	}

	if(~(_state.A ^ value) & (_state.A ^ result) & 0x80) {
		SetFlags(ProcFlags::Overflow);
	} else {
		ClearFlags(ProcFlags::Overflow);
	}

	if(CheckFlag(ProcFlags::Decimal) && result <= 0xFF) {
		result -= 0x60;
	}

	ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero);
	SetZeroNegativeFlags((uint8_t)result);

	if(result > 0xFF) {
		SetFlags(ProcFlags::Carry);
	}

	_state.A = (_state.A & 0xFF00) | (uint8_t)result;
}

void Cpu::Sub16(uint16_t value)
{
	int32_t result;
	if(CheckFlag(ProcFlags::Decimal)) {
		result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry);

		if(result <= 0x0F) result -= 0x06;
		result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F);

		if(result <= 0xFF) result -= 0x60;
		result = (_state.A & 0xF00) + (value & 0xF00) + (result > 0xFF ? 0x100 : 0) + (result & 0xFF);

		if(result <= 0xFFF) result -= 0x600;
		result = (_state.A & 0xF000) + (value & 0xF000) + (result > 0xFFF ? 0x1000 : 0) + (result & 0xFFF);
	} else {
		result = _state.A + value + (_state.PS & ProcFlags::Carry);
	}

	if(~(_state.A ^ value) & (_state.A ^ result) & 0x8000) {
		SetFlags(ProcFlags::Overflow);
	} else {
		ClearFlags(ProcFlags::Overflow);
	}

	if(CheckFlag(ProcFlags::Decimal) && result <= 0xFFFF) {
		result -= 0x6000;
	}

	ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero);
	SetZeroNegativeFlags((uint16_t)result);

	if(result > 0xFFFF) {
		SetFlags(ProcFlags::Carry);
	}

	_state.A = (uint16_t)result;
}

void Cpu::SBC()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		Sub8(~GetByteValue());
	} else {
		Sub16(~GetWordValue());
	}
}

/****************
Branch operations
****************/
void Cpu::BCC()
{
	BranchRelative(!CheckFlag(ProcFlags::Carry));
}

void Cpu::BCS()
{
	BranchRelative(CheckFlag(ProcFlags::Carry));
}

void Cpu::BEQ()
{
	BranchRelative(CheckFlag(ProcFlags::Zero));
}

void Cpu::BMI()
{
	BranchRelative(CheckFlag(ProcFlags::Negative));
}

void Cpu::BNE()
{
	BranchRelative(!CheckFlag(ProcFlags::Zero));
}

void Cpu::BPL()
{
	BranchRelative(!CheckFlag(ProcFlags::Negative));
}

void Cpu::BRA()
{
	BranchRelative(true);
}

void Cpu::BRL()
{
	_state.PC = (uint16_t)(_state.PC + (int16_t)_operand);
	IdleTakeBranch();
}

void Cpu::BVC()
{
	BranchRelative(!CheckFlag(ProcFlags::Overflow));
}

void Cpu::BVS()
{
	BranchRelative(CheckFlag(ProcFlags::Overflow));
}

void Cpu::BranchRelative(bool branch)
{
	if(branch) {
		int8_t offset = _operand;
		Idle();
		if(_state.EmulationMode && ((uint16_t)(_state.PC + offset) & 0xFF00) != (_state.PC & 0xFF00)) {
			//Extra cycle in emulation mode if crossing a page
			Idle();
		}
		_state.PC = (uint16_t)(_state.PC + offset);
		IdleTakeBranch();
	}
}

/***************************
Set/clear flag instructions
****************************/
void Cpu::CLC()
{
	ClearFlags(ProcFlags::Carry);
}

void Cpu::CLD()
{
	ClearFlags(ProcFlags::Decimal);
}

void Cpu::CLI()
{
	ClearFlags(ProcFlags::IrqDisable);
}

void Cpu::CLV()
{
	ClearFlags(ProcFlags::Overflow);
}

void Cpu::SEC()
{
	SetFlags(ProcFlags::Carry);
}

void Cpu::SED()
{
	SetFlags(ProcFlags::Decimal);
}

void Cpu::SEI()
{
	SetFlags(ProcFlags::IrqDisable);
}

void Cpu::REP()
{
	Idle();
	ClearFlags((uint8_t)_operand);
}

void Cpu::SEP()
{
	Idle();
	SetFlags((uint8_t)_operand);
	if(CheckFlag(ProcFlags::IndexMode8)) {
		//Truncate X/Y when 8-bit indexes are enabled
		_state.Y &= 0xFF;
		_state.X &= 0xFF;
	}
}

/******************************
Increment/decrement operations
*******************************/
void Cpu::DEX()
{
	IncDecReg(_state.X, -1);
}

void Cpu::DEY()
{
	IncDecReg(_state.Y, -1);
}

void Cpu::INX()
{
	IncDecReg(_state.X, 1);
}

void Cpu::INY()
{
	IncDecReg(_state.Y, 1);
}

void Cpu::DEC()
{
	IncDec(-1);
}

void Cpu::INC()
{
	IncDec(1);
}

void Cpu::DEC_Acc()
{
	SetRegister(_state.A, _state.A - 1, CheckFlag(ProcFlags::MemoryMode8));
}

void Cpu::INC_Acc()
{
	SetRegister(_state.A, _state.A + 1, CheckFlag(ProcFlags::MemoryMode8));
}

void Cpu::IncDecReg(uint16_t &reg, int8_t offset)
{
	SetRegister(reg, reg + offset, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::IncDec(int8_t offset)
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		uint8_t value = GetByteValue() + offset;
		SetZeroNegativeFlags(value);
		Idle();
		Write(_operand, value);
	} else {
		uint16_t value = GetWordValue() + offset;
		SetZeroNegativeFlags(value);
		Idle();
		WriteWord(_operand, value);
	}
}

/********************
Compare instructions
*********************/
void Cpu::Compare(uint16_t reg, bool eightBitMode)
{
	if(eightBitMode) {
		uint8_t value = GetByteValue();
		if((uint8_t)reg >= value) {
			SetFlags(ProcFlags::Carry);
		} else {
			ClearFlags(ProcFlags::Carry);
		}
		uint8_t result = (uint8_t)reg - value;
		SetZeroNegativeFlags(result);
	} else {
		uint16_t value = GetWordValue();
		if(reg >= value) {
			SetFlags(ProcFlags::Carry);
		} else {
			ClearFlags(ProcFlags::Carry);
		}

		uint16_t result = reg - value;
		SetZeroNegativeFlags(result);
	}
}

void Cpu::CMP()
{
	Compare(_state.A, CheckFlag(ProcFlags::MemoryMode8));
}

void Cpu::CPX()
{
	Compare(_state.X, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::CPY()
{
	Compare(_state.Y, CheckFlag(ProcFlags::IndexMode8));
}

/*****************
Jump instructions
******************/
void Cpu::JML()
{
	_state.K = (_operand >> 16) & 0xFF;
	_state.PC = (uint16_t)_operand;
	IdleEndJump();
}

void Cpu::JMP()
{
	_state.PC = (uint16_t)_operand;
	IdleEndJump();
}

void Cpu::JSL()
{
	PushByte(_state.K);
	Idle();
	PushWord(_state.PC - 1);
	_state.K = (_operand >> 16) & 0xFF;
	_state.PC = (uint16_t)_operand;
	IdleEndJump();
}

void Cpu::JSR()
{
	PushWord(_state.PC - 1);
	_state.PC = (uint16_t)_operand;
	IdleEndJump();
}

void Cpu::RTI()
{
	Idle();
	Idle();

	if(_state.EmulationMode) {
		SetPS(PopByte());
		_state.PC = PopWord();
	} else {
		SetPS(PopByte());
		_state.PC = PopWord();
		_state.K = PopByte();
	}
	IdleEndJump();
}

void Cpu::RTL()
{
	Idle();
	Idle();

	_state.PC = PopWord();
	_state.PC++;
	_state.K = PopByte();
	IdleEndJump();
}

void Cpu::RTS()
{
	Idle();
	Idle();

	_state.PC = PopWord();
	Idle();
	_state.PC++;
	IdleEndJump();
}

/**********
Interrupts
***********/
void Cpu::ProcessInterrupt(uint16_t vector, bool forHardwareInterrupt)
{
	if(forHardwareInterrupt) {
		//IRQ/NMI waste 2 cycles here.  BRK/COP do not (because they do those 2 cycles while loading the OP code + signature byte)
		ReadCode(_state.PC);
		Idle();
	}

	if(_state.EmulationMode) {
		PushWord(_state.PC);
		PushByte(_state.PS | 0x20);

		SetFlags(ProcFlags::IrqDisable);
		ClearFlags(ProcFlags::Decimal);

		_state.K = 0;
		_state.PC = ReadVector(vector);
	} else {
		PushByte(_state.K);
		PushWord(_state.PC);
		PushByte(_state.PS);

		SetFlags(ProcFlags::IrqDisable);
		ClearFlags(ProcFlags::Decimal);

		_state.K = 0;
		_state.PC = ReadVector(vector);
	}
}

void Cpu::BRK()
{
	ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyIrqVector : Cpu::BreakVector, false);
}

void Cpu::COP()
{
	ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyCoprocessorVector : Cpu::CoprocessorVector, false);
}

/******************
Bitwise operations
*******************/
void Cpu::AND()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		SetRegister(_state.A, _state.A & GetByteValue(), true);
	} else {
		SetRegister(_state.A, _state.A & GetWordValue(), false);
	}
}

void Cpu::EOR()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		SetRegister(_state.A, _state.A ^ GetByteValue(), true);
	} else {
		SetRegister(_state.A, _state.A ^ GetWordValue(), false);
	}
}

void Cpu::ORA()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		SetRegister(_state.A, _state.A | GetByteValue(), true);
	} else {
		SetRegister(_state.A, _state.A | GetWordValue(), false);
	}
}

/****************
Shift operations
*****************/
template<typename T> T Cpu::ShiftLeft(T value)
{
	T result = value << 1;
	if(value & (1 << (sizeof(T) * 8 - 1))) {
		SetFlags(ProcFlags::Carry);
	} else {
		ClearFlags(ProcFlags::Carry);
	}
	SetZeroNegativeFlags(result);
	return result;
}

template<typename T> T Cpu::RollLeft(T value)
{
	T result = value << 1 | (_state.PS & ProcFlags::Carry);
	if(value & (1 << (sizeof(T) * 8 - 1))) {
		SetFlags(ProcFlags::Carry);
	} else {
		ClearFlags(ProcFlags::Carry);
	}
	SetZeroNegativeFlags(result);
	return result;
}

template<typename T> T Cpu::ShiftRight(T value)
{
	T result = value >> 1;
	if(value & 0x01) {
		SetFlags(ProcFlags::Carry);
	} else {
		ClearFlags(ProcFlags::Carry);
	}
	SetZeroNegativeFlags(result);
	return result;
}

template<typename T> T Cpu::RollRight(T value)
{
	T result = value >> 1 | ((_state.PS & 0x01) << (sizeof(T) * 8 - 1));
	if(value & 0x01) {
		SetFlags(ProcFlags::Carry);
	} else {
		ClearFlags(ProcFlags::Carry);
	}
	SetZeroNegativeFlags(result);
	return result;
}

void Cpu::ASL_Acc()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		_state.A = (_state.A & 0xFF00) | (ShiftLeft<uint8_t>((uint8_t)_state.A));
	} else {
		_state.A = ShiftLeft<uint16_t>(_state.A);
	}
}

void Cpu::ASL()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		uint8_t value = GetByteValue();
		Idle();
		Write(_operand, ShiftLeft<uint8_t>(value));
	} else {
		uint16_t value = GetWordValue();
		Idle();
		WriteWord(_operand, ShiftLeft<uint16_t>(value));
	}
}

void Cpu::LSR_Acc()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		_state.A = (_state.A & 0xFF00) | ShiftRight<uint8_t>((uint8_t)_state.A);
	} else {
		_state.A = ShiftRight<uint16_t>(_state.A);
	}
}

void Cpu::LSR()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		uint8_t value = GetByteValue();
		Idle();
		Write(_operand, ShiftRight<uint8_t>(value));
	} else {
		uint16_t value = GetWordValue();
		Idle();
		WriteWord(_operand, ShiftRight<uint16_t>(value));
	}
}

void Cpu::ROL_Acc()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		_state.A = (_state.A & 0xFF00) | RollLeft<uint8_t>((uint8_t)_state.A);
	} else {
		_state.A = RollLeft<uint16_t>(_state.A);
	}
}

void Cpu::ROL()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		uint8_t value = GetByteValue();
		Idle();
		Write(_operand, RollLeft<uint8_t>(value));
	} else {
		uint16_t value = GetWordValue();
		Idle();
		WriteWord(_operand, RollLeft<uint16_t>(value));
	}
}

void Cpu::ROR_Acc()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		_state.A = (_state.A & 0xFF00) | RollRight<uint8_t>((uint8_t)_state.A);
	} else {
		_state.A = RollRight<uint16_t>(_state.A);
	}
}

void Cpu::ROR()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		uint8_t value = GetByteValue();
		Idle();
		Write(_operand, RollRight<uint8_t>(value));
	} else {
		uint16_t value = GetWordValue();
		Idle();
		WriteWord(_operand, RollRight<uint16_t>(value));
	}
}

/***************
Move operations
****************/
void Cpu::MVN()
{
	_state.DBR = _operand & 0xFF;
	uint32_t destBank = _state.DBR << 16;
	uint32_t srcBank = (_operand << 8) & 0xFF0000;

	uint8_t value = ReadData(srcBank | _state.X);
	Write(destBank | _state.Y, value);

	Idle();
	Idle();

	_state.X++;
	_state.Y++;
	if(CheckFlag(ProcFlags::IndexMode8)) {
		_state.X &= 0xFF;
		_state.Y &= 0xFF;
	}

	_state.A--;

	if(_state.A != 0xFFFF) {
		//Operation isn't done, set the PC back to the start of the instruction
		_state.PC -= 3;
	}
}

void Cpu::MVP()
{
	_state.DBR = _operand & 0xFF;
	uint32_t destBank = _state.DBR << 16;
	uint32_t srcBank = (_operand << 8) & 0xFF0000;

	uint8_t value = ReadData(srcBank | _state.X);
	Write(destBank | _state.Y, value);

	Idle();
	Idle();

	_state.X--;
	_state.Y--;
	if(CheckFlag(ProcFlags::IndexMode8)) {
		_state.X &= 0xFF;
		_state.Y &= 0xFF;
	}

	_state.A--;
		
	if(_state.A != 0xFFFF) {
		//Operation isn't done, set the PC back to the start of the instruction
		_state.PC -= 3;
	}
}

/********************
Push/pull operations
*********************/
void Cpu::PEA()
{
	//Push Effective Address
	PushWord((uint16_t)_operand);
}

void Cpu::PEI()
{
	//Push Effective Indirect address
	PushWord(ReadDataWord(_operand));
}

void Cpu::PER()
{
	//Push Effective Relative address
	PushWord((uint16_t)((int16_t)_operand + _state.PC));
}

void Cpu::PHB()
{
	Idle();
	PushByte(_state.DBR);
}

void Cpu::PHD()
{
	Idle();
	PushWord(_state.D);
}

void Cpu::PHK()
{
	//"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack"
	Idle();
	PushByte(_state.K);
}

void Cpu::PHP()
{
	//"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack"
	Idle();
	PushByte(_state.PS);
}

void Cpu::PLB()
{
	//"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack"
	Idle();
	Idle();
	SetRegister(_state.DBR, PopByte());
}

void Cpu::PLD()
{
	//"PHD and PLD push and pull two bytes from the stack."
	Idle();
	Idle();
	SetRegister(_state.D, PopWord(), false);
}

void Cpu::PLP()
{
	//"For PLP, (all of) the flags are pulled from the stack. Note that when the e flag is 1, the m and x flag are forced to 1, so after the PLP, both flags will still be 1 no matter what value is pulled from the stack."
	Idle();
	Idle();
	if(_state.EmulationMode) {
		SetPS(PopByte() | ProcFlags::MemoryMode8 | ProcFlags::IndexMode8);
	} else {
		SetPS(PopByte());
	}
}

void Cpu::PHA()
{
	//"When the m flag is 0, PHA and PLA push and pull a 16-bit value, and when the m flag is 1, PHA and PLA push and pull an 8-bit value. "
	Idle();
	PushRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8));
}

void Cpu::PHX()
{
	Idle();
	PushRegister(_state.X, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::PHY()
{
	Idle();
	PushRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::PLA()
{
	//"When the m flag is 0, PHA and PLA push and pull a 16-bit value, and when the m flag is 1, PHA and PLA push and pull an 8-bit value."
	Idle();
	Idle();
	PullRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8));
}

void Cpu::PLX()
{
	Idle();
	Idle();
	PullRegister(_state.X, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::PLY()
{
	Idle();
	Idle();
	PullRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::PushRegister(uint16_t reg, bool eightBitMode)
{
	//"When the x flag is 0, PHX, PHY, PLX, and PLY push and pull a 16-bit value, and when the x flag is 1, PHX, PHY, PLX, and PLY push and pull an 8-bit value."
	if(eightBitMode) {
		PushByte((uint8_t)reg);
	} else {
		PushWord(reg);
	}
}

void Cpu::PullRegister(uint16_t &reg, bool eightBitMode)
{
	//"When the x flag is 0, PHX, PHY, PLX, and PLY push and pull a 16-bit value, and when the x flag is 1, PHX, PHY, PLX, and PLY push and pull an 8-bit value."
	if(eightBitMode) {
		SetRegister(reg, PopByte(), true);
	} else {
		SetRegister(reg, PopWord(), false);
	}
}

/*********************
Store/load operations
**********************/
void Cpu::LoadRegister(uint16_t &reg, bool eightBitMode)
{
	if(eightBitMode) {
		SetRegister(reg, GetByteValue(), true);
	} else {
		SetRegister(reg, GetWordValue(), false);
	}
}

void Cpu::StoreRegister(uint16_t val, bool eightBitMode)
{
	if(eightBitMode) {
		Write(_operand, (uint8_t)val);
	} else {
		WriteWord(_operand, val);
	}
}

void Cpu::LDA()
{
	//"When the m flag is 0, LDA, STA, and STZ are 16-bit operations"
	LoadRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8));
}

void Cpu::LDX()
{
	//"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations"
	LoadRegister(_state.X, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::LDY()
{
	//"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations"
	LoadRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::STA()
{
	//"When the m flag is 0, LDA, STA, and STZ are 16-bit operations"
	StoreRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8));
}

void Cpu::STX()
{
	//"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations"
	StoreRegister(_state.X, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::STY()
{
	//"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations"
	StoreRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::STZ()
{
	//"When the m flag is 0, LDA, STA, and STZ are 16-bit operations"
	StoreRegister(0, CheckFlag(ProcFlags::MemoryMode8));
}

/*******************
Bit test operations
********************/
template<typename T> void Cpu::TestBits(T value, bool alterZeroFlagOnly)
{
	if(alterZeroFlagOnly) {
		//"Immediate addressing only affects the z flag (with the result of the bitwise And), but does not affect the n and v flags."
		if(((T)_state.A & value) == 0) {
			SetFlags(ProcFlags::Zero);
		} else {
			ClearFlags(ProcFlags::Zero);
		}
	} else {
		ClearFlags(ProcFlags::Zero | ProcFlags::Overflow | ProcFlags::Negative);

		if(((T)_state.A & value) == 0) {
			SetFlags(ProcFlags::Zero);
		}

		if(value & (1 << (sizeof(T) * 8 - 2))) {
			SetFlags(ProcFlags::Overflow);
		}
		if(value & (1 << (sizeof(T) * 8 - 1))) {
			SetFlags(ProcFlags::Negative);
		}
	}
}

void Cpu::BIT()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		TestBits<uint8_t>(GetByteValue(), _immediateMode);
	} else {
		TestBits<uint16_t>(GetWordValue(), _immediateMode);
	}
}

void Cpu::TRB()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		uint8_t value = GetByteValue();
		TestBits<uint8_t>(value, true);

		value &= ~_state.A;
		Idle();

		Write(_operand, value);
	} else {
		uint16_t value = GetWordValue();
		TestBits<uint16_t>(value, true);

		value &= ~_state.A;
		Idle();

		WriteWord(_operand, value);
	}
}

void Cpu::TSB()
{
	if(CheckFlag(ProcFlags::MemoryMode8)) {
		uint8_t value = GetByteValue();
		TestBits<uint8_t>(value, true);

		value |= _state.A;
		Idle();

		Write(_operand, value);
	} else {
		uint16_t value = GetWordValue();
		TestBits<uint16_t>(value, true);

		value |= _state.A;
		Idle();

		WriteWord(_operand, value);
	}
}

/******************
Transfer operations
*******************/
void Cpu::TAX()
{
	SetRegister(_state.X, _state.A, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::TAY()
{
	SetRegister(_state.Y, _state.A, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::TCD()
{
	SetRegister(_state.D, _state.A, false);
}

void Cpu::TCS()
{
	SetSP(_state.A);
}

void Cpu::TDC()
{
	SetRegister(_state.A, _state.D, false);
}

void Cpu::TSC()
{
	SetRegister(_state.A, _state.SP, false);
}

void Cpu::TSX()
{
	SetRegister(_state.X, _state.SP, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::TXA()
{
	SetRegister(_state.A, _state.X, CheckFlag(ProcFlags::MemoryMode8));
}

void Cpu::TXS()
{
	SetSP(_state.X);
}

void Cpu::TXY()
{
	SetRegister(_state.Y, _state.X, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::TYA()
{
	SetRegister(_state.A, _state.Y, CheckFlag(ProcFlags::MemoryMode8));
}

void Cpu::TYX()
{
	SetRegister(_state.X, _state.Y, CheckFlag(ProcFlags::IndexMode8));
}

void Cpu::XBA()
{
	Idle();
	_state.A = ((_state.A & 0xFF) << 8) | ((_state.A >> 8) & 0xFF);
	SetZeroNegativeFlags((uint8_t)_state.A);
}

void Cpu::XCE()
{
	bool carry = CheckFlag(ProcFlags::Carry);
	if(_state.EmulationMode) {
		SetFlags(ProcFlags::Carry);
	} else {
		ClearFlags(ProcFlags::Carry);
	}
	_state.EmulationMode = carry;

	if(_state.EmulationMode) {
		SetPS(_state.PS | ProcFlags::IndexMode8 | ProcFlags::MemoryMode8);
		_state.SP = 0x100 | (_state.SP & 0xFF);
	}
}

/*****************
No operation (NOP)
******************/
void Cpu::NOP()
{
	//1-byte NOP
}

void Cpu::WDM()
{
	//2-byte NOP
}

/****************
Misc. operations
*****************/
void Cpu::STP()
{
	//Stop the CPU
	_state.StopState = CpuStopState::Stopped;
}

void Cpu::WAI()
{
	//Wait for interrupt
	_state.StopState = CpuStopState::WaitingForIrq;
}

/****************
Addressing modes
*****************/
void Cpu::AddrMode_Abs()
{
	_operand = GetDataAddress(ReadOperandWord());
}

void Cpu::AddrMode_AbsIdxX(bool isWrite)
{
	uint32_t baseAddr = GetDataAddress(ReadOperandWord());
	_operand = (baseAddr + _state.X) & 0xFFFFFF;
	if(isWrite || !CheckFlag(ProcFlags::IndexMode8) || (_operand & 0xFF00) != (baseAddr & 0xFF00)) {
		Idle();
	}
}

void Cpu::AddrMode_AbsIdxY(bool isWrite)
{
	uint32_t baseAddr = GetDataAddress(ReadOperandWord());
	_operand = (baseAddr + _state.Y) & 0xFFFFFF;
	if(isWrite || !CheckFlag(ProcFlags::IndexMode8) || (_operand & 0xFF00) != (baseAddr & 0xFF00)) {
		Idle();
	}
}

void Cpu::AddrMode_AbsLng()
{
	_operand = ReadOperandLong();
}

void Cpu::AddrMode_AbsLngIdxX()
{
	_operand = (ReadOperandLong() + _state.X) & 0xFFFFFF;
}

void Cpu::AddrMode_AbsJmp()
{
	_operand = GetProgramAddress(ReadOperandWord());
}

void Cpu::AddrMode_AbsLngJmp()
{
	_operand = ReadOperandLong();
}

void Cpu::AddrMode_AbsIdxXInd()
{
	_operand = GetProgramAddress(ReadDataWord(GetProgramAddress(ReadOperandWord() + _state.X)));
	Idle();
}

void Cpu::AddrMode_AbsInd()
{
	_operand = ReadDataWord(ReadOperandWord());
}

void Cpu::AddrMode_AbsIndLng()
{
	_operand = ReadDataLong(ReadOperandWord());
}

void Cpu::AddrMode_Acc()
{
	IdleOrRead();
}

void Cpu::AddrMode_BlkMov()
{
	_operand = ReadOperandWord();
}

uint8_t Cpu::ReadDirectOperandByte()
{
	uint8_t value = ReadOperandByte();
	if(_state.D & 0xFF) {
		//Add 1 cycle for direct register low (DL) not equal 0
		Idle();
	}
	return value;
}

void Cpu::AddrMode_Dir()
{
	_operand = GetDirectAddress(ReadDirectOperandByte());
}

void Cpu::AddrMode_DirIdxX()
{
	_operand = GetDirectAddress(ReadDirectOperandByte() + _state.X);
	Idle();
}

void Cpu::AddrMode_DirIdxY()
{
	_operand = GetDirectAddress(ReadDirectOperandByte() + _state.Y);
	Idle();
}

void Cpu::AddrMode_DirInd()
{
	_operand = GetDataAddress(GetDirectAddressIndirectWord(ReadDirectOperandByte()));
}

void Cpu::AddrMode_DirIdxIndX()
{
	uint8_t operandByte = ReadDirectOperandByte();
	Idle();
	_operand = GetDataAddress(GetDirectAddressIndirectWord(operandByte + _state.X));
}

void Cpu::AddrMode_DirIndIdxY(bool isWrite)
{
	uint32_t baseAddr = GetDataAddress(GetDirectAddressIndirectWord(ReadDirectOperandByte()));
	_operand = (baseAddr + _state.Y) & 0xFFFFFF;
	
	if(isWrite || !CheckFlag(ProcFlags::IndexMode8) || (_operand & 0xFF00) != (baseAddr & 0xFF00)) {
		Idle();
	}
}

void Cpu::AddrMode_DirIndLng()
{
	_operand = GetDirectAddressIndirectLong(ReadDirectOperandByte());
}

void Cpu::AddrMode_DirIndLngIdxY()
{
	_operand = (GetDirectAddressIndirectLong(ReadDirectOperandByte()) + _state.Y) & 0xFFFFFF;
}

void Cpu::AddrMode_Imm8()
{
	_immediateMode = true;
	_operand = ReadOperandByte();
}

void Cpu::AddrMode_Imm16()
{
	_immediateMode = true;
	_operand = ReadOperandWord();
}

void Cpu::AddrMode_ImmX()
{
	_immediateMode = true;
	_operand = CheckFlag(ProcFlags::IndexMode8) ? ReadOperandByte() : ReadOperandWord();
}

void Cpu::AddrMode_ImmM()
{
	_immediateMode = true; 
	_operand = CheckFlag(ProcFlags::MemoryMode8) ? ReadOperandByte() : ReadOperandWord();
}

void Cpu::AddrMode_Imp()
{
	IdleOrRead();
}

void Cpu::AddrMode_RelLng()
{
	_operand = ReadOperandWord();
	Idle();
}

void Cpu::AddrMode_Rel()
{
	_operand = ReadOperandByte();
}

void Cpu::AddrMode_StkRel()
{
	_operand = (uint16_t)(ReadOperandByte() + _state.SP);
	Idle();
}

void Cpu::AddrMode_StkRelIndIdxY()
{
	uint16_t addr = (uint16_t)(ReadOperandByte() + _state.SP);
	Idle();
	_operand = (GetDataAddress(ReadDataWord(addr)) + _state.Y) & 0xFFFFFF;
	Idle();
}
