#if (defined(DUMMYCPU) && !defined(__DUMMYCPU__H)) || (!defined(DUMMYCPU) && !defined(__CPU__H))
#ifdef DUMMYCPU
#define __DUMMYCPU__H
#else
#define __CPU__H
#endif

#include "stdafx.h"
#include "Snapshotable.h"
#include "Types.h"

enum class NesModel;
class Console;
class MemoryManager;
class DummyCpu;

class CPU : public Snapshotable
{
#ifndef DUMMYCPU
	friend DummyCpu;
#endif

public:
	static constexpr uint16_t NMIVector = 0xFFFA;
	static constexpr uint16_t ResetVector = 0xFFFC;
	static constexpr uint16_t IRQVector = 0xFFFE;
	static constexpr uint32_t ClockRateNtsc = 1789773;
	static constexpr uint32_t ClockRatePal = 1662607;
	static constexpr uint32_t ClockRateDendy = 1773448;

private:
	typedef void(CPU::*Func)();

	uint64_t _cycleCount;
	uint64_t _masterClock;
	uint8_t _ppuOffset;
	uint8_t _startClockCount;
	uint8_t _endClockCount;
	uint16_t _operand;

	Func _opTable[256];
	AddrMode _addrMode[256];
	AddrMode _instAddrMode;

	bool _needHalt = false;
	bool _spriteDmaTransfer = false;
	bool _dmcDmaRunning = false;
	bool _needDummyRead = false;
	uint8_t _spriteDmaOffset;

	bool _cpuWrite = false;

	uint8_t _irqMask;

	State _state;
	shared_ptr<Console> _console;
	MemoryManager* _memoryManager;

	bool _prevRunIrq = false;
	bool _runIrq = false;
	
	bool _prevNmiFlag = false;
	bool _prevNeedNmi = false;
	bool _needNmi = false;

	uint64_t _lastCrashWarning = 0;

#ifdef DUMMYCPU
	uint32_t _writeCounter = 0;
	uint16_t _writeAddresses[10];
	uint8_t _writeValue[10];
	bool _isDummyWrite[10];

	uint32_t _readCounter = 0;
	uint16_t _readAddresses[10];
	uint8_t _readValue[10];
	bool _isDummyRead[10];
#endif

	__forceinline void StartCpuCycle(bool forRead);
	__forceinline void ProcessPendingDma(uint16_t readAddress);
	__forceinline uint16_t FetchOperand();
	__forceinline void EndCpuCycle(bool forRead);
	void IRQ();

	uint8_t GetOPCode()
	{
		uint8_t opCode = MemoryRead(_state.PC, MemoryOperationType::ExecOpCode);
		_state.PC++;
		return opCode;
	}

	void DummyRead()
	{
		MemoryRead(_state.PC, MemoryOperationType::DummyRead);
	}
	
	uint8_t ReadByte()
	{
		uint8_t value = MemoryRead(_state.PC, MemoryOperationType::ExecOperand);
		_state.PC++;
		return value;
	}

	uint16_t ReadWord()
	{
		uint16_t value = MemoryReadWord(_state.PC, MemoryOperationType::ExecOperand);
		_state.PC += 2;
		return value;
	}

	void ClearFlags(uint8_t flags)
	{
		_state.PS &= ~flags;
	}

	void SetFlags(uint8_t flags)
	{
		_state.PS |= flags;
	}

	bool CheckFlag(uint8_t flag)
	{
		return (_state.PS & flag) == flag;
	}

	void SetZeroNegativeFlags(uint8_t value)
	{
		if(value == 0) {
			SetFlags(PSFlags::Zero);
		} else if(value & 0x80) {
			SetFlags(PSFlags::Negative);
		}
	}

	bool CheckPageCrossed(uint16_t valA, int8_t valB)
	{
		return ((valA + valB) & 0xFF00) != (valA & 0xFF00);
	}

	bool CheckPageCrossed(uint16_t valA, uint8_t valB)
	{
		return ((valA + valB) & 0xFF00) != (valA & 0xFF00);
	}

	void MemoryWrite(uint16_t addr, uint8_t value, MemoryOperationType operationType = MemoryOperationType::Write);
	uint8_t MemoryRead(uint16_t addr, MemoryOperationType operationType = MemoryOperationType::Read);

	uint16_t MemoryReadWord(uint16_t addr, MemoryOperationType operationType = MemoryOperationType::Read) {
		uint8_t lo = MemoryRead(addr, operationType);
		uint8_t hi = MemoryRead(addr + 1, operationType);
		return lo | hi << 8;
	}

	void SetRegister(uint8_t &reg, uint8_t value) {
		ClearFlags(PSFlags::Zero | PSFlags::Negative);
		SetZeroNegativeFlags(value);
		reg = value;
	}

	void Push(uint8_t value) {
		MemoryWrite(SP() + 0x100, value);
		SetSP(SP() - 1);
	}

	void Push(uint16_t value) {
		Push((uint8_t)(value >> 8));
		Push((uint8_t)value);
	}

	uint8_t Pop() {
		SetSP(SP() + 1);
		return MemoryRead(0x100 + SP());
	}

	uint16_t PopWord() {
		uint8_t lo = Pop();
		uint8_t hi = Pop();
		
		return lo | hi << 8;
	}

	uint8_t A() { return _state.A; }
	void SetA(uint8_t value) { SetRegister(_state.A, value); }
	uint8_t X() { return _state.X; }
	void SetX(uint8_t value) { SetRegister(_state.X, value); }
	uint8_t Y() { return _state.Y; }
	void SetY(uint8_t value) { SetRegister(_state.Y, value); }
	uint8_t SP() { return _state.SP; }
	void SetSP(uint8_t value) { _state.SP = value; }
	uint8_t PS() { return _state.PS; }
	void SetPS(uint8_t value) { _state.PS = value & 0xCF; }
	uint16_t PC() { return _state.PC; }
	void SetPC(uint16_t value) { _state.PC = value; }

	uint16_t GetOperand()
	{
		return _operand;
	}

	uint8_t GetOperandValue()
	{
		if(_instAddrMode >= AddrMode::Zero) {
			return MemoryRead(GetOperand());
		} else {
			return (uint8_t)GetOperand();
		}
	}

	uint16_t GetIndAddr() { return ReadWord(); }
	uint8_t GetImmediate() { return ReadByte(); }
	uint8_t GetZeroAddr() { return ReadByte(); }
	uint8_t GetZeroXAddr() { 
		uint8_t value = ReadByte();
		MemoryRead(value, MemoryOperationType::DummyRead); //Dummy read
		return value + X();
	}
	uint8_t GetZeroYAddr() { 
		uint8_t value = ReadByte();
		MemoryRead(value, MemoryOperationType::DummyRead); //Dummy read
		return value + Y();
	}
	uint16_t GetAbsAddr() { return ReadWord(); }

	uint16_t GetAbsXAddr(bool dummyRead = true) { 
		uint16_t baseAddr = ReadWord();
		bool pageCrossed = CheckPageCrossed(baseAddr, X());

		if(pageCrossed || dummyRead) {
			//Dummy read done by the processor (only when page is crossed for READ instructions)
			MemoryRead(baseAddr + X() - (pageCrossed ? 0x100 : 0), MemoryOperationType::DummyRead);
		}
		return baseAddr + X(); 
	}

	uint16_t GetAbsYAddr(bool dummyRead = true) { 
		uint16_t baseAddr = ReadWord();
		bool pageCrossed = CheckPageCrossed(baseAddr, Y());
		
		if(pageCrossed || dummyRead) {
			//Dummy read done by the processor (only when page is crossed for READ instructions)
			MemoryRead(baseAddr + Y() - (pageCrossed ? 0x100 : 0), MemoryOperationType::DummyRead);
		}

		return baseAddr + Y(); 
	}

	uint16_t GetInd() { 
		uint16_t addr = GetOperand();
		if((addr & 0xFF) == 0xFF) {
			auto lo = MemoryRead(addr);
			auto hi = MemoryRead(addr - 0xFF);
			return (lo | hi << 8);
		} else {
			return MemoryReadWord(addr);
		}
	}

	uint16_t GetIndXAddr() {
		uint8_t zero = ReadByte();
		
		//Dummy read
		MemoryRead(zero, MemoryOperationType::DummyRead);

		zero += X();
		
		uint16_t addr;
		if(zero == 0xFF) {
			addr = MemoryRead(0xFF) | MemoryRead(0x00) << 8;
		} else {
			addr = MemoryReadWord(zero);
		}
		return addr;
	}

	uint16_t GetIndYAddr(bool dummyRead = true) {
		uint8_t zero = ReadByte();
		
		uint16_t addr;
		if(zero == 0xFF) {
			addr = MemoryRead(0xFF) | MemoryRead(0x00) << 8;
		} else {
			addr = MemoryReadWord(zero);
		}

		bool pageCrossed = CheckPageCrossed(addr, Y());			
		if(pageCrossed || dummyRead) {
			//Dummy read done by the processor (only when page is crossed for READ instructions)
			MemoryRead(addr + Y() - (pageCrossed ? 0x100 : 0), MemoryOperationType::DummyRead);
		}
		return addr + Y();
	}

	void AND() { SetA(A() & GetOperandValue()); }
	void EOR() { SetA(A() ^ GetOperandValue()); }
	void ORA() { SetA(A() | GetOperandValue()); }

	void ADD(uint8_t value)
	{
		uint16_t result = (uint16_t)A() + (uint16_t)value + (CheckFlag(PSFlags::Carry) ? PSFlags::Carry : 0x00);
		
		ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Overflow | PSFlags::Zero);
		SetZeroNegativeFlags((uint8_t)result);
		if(~(A() ^ value) & (A() ^ result) & 0x80) {
			SetFlags(PSFlags::Overflow);
		}
		if(result > 0xFF) {
			SetFlags(PSFlags::Carry);
		}
		SetA((uint8_t)result);
	}

	void ADC() { ADD(GetOperandValue()); }
	void SBC() { ADD(GetOperandValue() ^ 0xFF); }

	void CMP(uint8_t reg, uint8_t value) 
	{
		ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);

		auto result = reg - value;

		if(reg >= value) {
			SetFlags(PSFlags::Carry);
		}
		if(reg == value) {
			SetFlags(PSFlags::Zero);
		}
		if((result & 0x80) == 0x80) {
			SetFlags(PSFlags::Negative);
		}
	}

	void CPA() { CMP(A(), GetOperandValue()); }
	void CPX() { CMP(X(), GetOperandValue()); }
	void CPY() { CMP(Y(), GetOperandValue()); }

	void INC() 
	{
		uint16_t addr = GetOperand();
		ClearFlags(PSFlags::Negative | PSFlags::Zero);
		uint8_t value = MemoryRead(addr);		
		
		MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
		
		value++;
		SetZeroNegativeFlags(value);
		MemoryWrite(addr, value);
	}

	void DEC() 
	{
		uint16_t addr = GetOperand();
		ClearFlags(PSFlags::Negative | PSFlags::Zero);
		uint8_t value = MemoryRead(addr);
		MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
		
		value--;
		SetZeroNegativeFlags(value);
		MemoryWrite(addr, value);
	}

	uint8_t ASL(uint8_t value)
	{
		ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);
		if(value & 0x80) {
			SetFlags(PSFlags::Carry);
		}

		uint8_t result = value << 1;
		SetZeroNegativeFlags(result);
		return result;
	}

	uint8_t LSR(uint8_t value) {
		ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);
		if(value & 0x01) {
			SetFlags(PSFlags::Carry);
		}

		uint8_t result = value >> 1;
		SetZeroNegativeFlags(result);
		return result;
	}

	uint8_t ROL(uint8_t value) {
		bool carryFlag = CheckFlag(PSFlags::Carry);
		ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);

		if(value & 0x80) {
			SetFlags(PSFlags::Carry);
		}

		uint8_t result = (value << 1 | (carryFlag ? 0x01 : 0x00));
		SetZeroNegativeFlags(result);
		return result;
	}

	uint8_t ROR(uint8_t value) {
		bool carryFlag = CheckFlag(PSFlags::Carry);
		ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);
		if(value & 0x01) {
			SetFlags(PSFlags::Carry);
		}

		uint8_t result = (value >> 1 | (carryFlag ? 0x80 : 0x00));
		SetZeroNegativeFlags(result);
		return result;
	}

	void ASLAddr() {
		uint16_t addr = GetOperand();
		uint8_t value = MemoryRead(addr);
		MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
		MemoryWrite(addr, ASL(value));
	}

	void LSRAddr() {
		uint16_t addr = GetOperand();
		uint8_t value = MemoryRead(addr);
		MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
		MemoryWrite(addr, LSR(value));
	}

	void ROLAddr() {
		uint16_t addr = GetOperand();
		uint8_t value = MemoryRead(addr);
		MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
		MemoryWrite(addr, ROL(value));
	}

	void RORAddr() {
		uint16_t addr = GetOperand();
		uint8_t value = MemoryRead(addr);
		MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
		MemoryWrite(addr, ROR(value));
	}

	void JMP(uint16_t addr) {
		SetPC(addr);
	}

	void BranchRelative(bool branch) {
		int8_t offset = (int8_t)GetOperand();
		if(branch) {
			//"a taken non-page-crossing branch ignores IRQ/NMI during its last clock, so that next instruction executes before the IRQ"
			//Fixes "branch_delays_irq" test
			if(_runIrq && !_prevRunIrq) {
				_runIrq = false;
			}
			DummyRead();

			if(CheckPageCrossed(PC(), offset)) {
				DummyRead();
			}

			SetPC(PC() + offset);
		}
	}

	void BIT() {
		uint8_t value = GetOperandValue();
		ClearFlags(PSFlags::Zero | PSFlags::Overflow | PSFlags::Negative);
		if((A() & value) == 0) {
			SetFlags(PSFlags::Zero);
		}
		if(value & 0x40) {
			SetFlags(PSFlags::Overflow);
		}
		if(value & 0x80) {
			SetFlags(PSFlags::Negative);
		}
	}

	//OP Codes
	void LDA() { SetA(GetOperandValue()); }
	void LDX() { SetX(GetOperandValue()); }
	void LDY() { SetY(GetOperandValue()); }

	void STA() { MemoryWrite(GetOperand(), A()); }
	void STX() { MemoryWrite(GetOperand(), X()); }
	void STY() { MemoryWrite(GetOperand(), Y()); }

	void TAX() { SetX(A()); }
	void TAY() { SetY(A()); }
	void TSX() { SetX(SP()); }
	void TXA() { SetA(X()); }
	void TXS() { SetSP(X()); }
	void TYA() { SetA(Y()); }

	void PHA() { Push(A()); }
	void PHP() {
		uint8_t flags = PS() | PSFlags::Break | PSFlags::Reserved;
		Push((uint8_t)flags);
	}
	void PLA() { 
		DummyRead();
		SetA(Pop()); 
	}
	void PLP() { 
		DummyRead();
		SetPS(Pop()); 
	}

	void INX() { SetX(X() + 1); }
	void INY() { SetY(Y() + 1); }

	void DEX() { SetX(X() - 1); }
	void DEY() { SetY(Y() - 1); }

	void ASL_Acc() { SetA(ASL(A())); }
	void ASL_Memory() { ASLAddr(); }

	void LSR_Acc() { SetA(LSR(A())); }
	void LSR_Memory() { LSRAddr(); }

	void ROL_Acc() { SetA(ROL(A())); }
	void ROL_Memory() { ROLAddr(); }

	void ROR_Acc() { SetA(ROR(A())); }
	void ROR_Memory() { RORAddr(); }

	void JMP_Abs() {
		JMP(GetOperand());
	}
	void JMP_Ind() { JMP(GetInd()); }
	void JSR() {
		uint16_t addr = GetOperand();
		DummyRead();
		Push((uint16_t)(PC() - 1));
		JMP(addr);
	}
	void RTS() {
		uint16_t addr = PopWord();
		DummyRead();
		DummyRead();
		SetPC(addr + 1);
	}

	void BCC() {
		BranchRelative(!CheckFlag(PSFlags::Carry));
	}

	void BCS() {
		BranchRelative(CheckFlag(PSFlags::Carry));
	}

	void BEQ() {
		BranchRelative(CheckFlag(PSFlags::Zero));
	}

	void BMI() {
		BranchRelative(CheckFlag(PSFlags::Negative));
	}

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

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

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

	void BVS() {
		BranchRelative(CheckFlag(PSFlags::Overflow));
	}

	void CLC() { ClearFlags(PSFlags::Carry); }
	void CLD() { ClearFlags(PSFlags::Decimal); }
	void CLI() { ClearFlags(PSFlags::Interrupt); }
	void CLV() { ClearFlags(PSFlags::Overflow); }
	void SEC() { SetFlags(PSFlags::Carry); }
	void SED() { SetFlags(PSFlags::Decimal); }
	void SEI() { SetFlags(PSFlags::Interrupt); }

	void BRK();
	
	void RTI() {
		DummyRead();
		SetPS(Pop());
		SetPC(PopWord());
	}

	void NOP() {
		//Make sure the nop operation takes as many cycles as meant to
		GetOperandValue();
	}

	
	//Unofficial OpCodes
	void SLO()
	{
		//ASL & ORA
		uint8_t value = GetOperandValue();
		MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
		uint8_t shiftedValue = ASL(value);
		SetA(A() | shiftedValue);
		MemoryWrite(GetOperand(), shiftedValue);
	}
	
	void SRE()
	{
		//ROL & AND
		uint8_t value = GetOperandValue();
		MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
		uint8_t shiftedValue = LSR(value);
		SetA(A() ^ shiftedValue);
		MemoryWrite(GetOperand(), shiftedValue);
	}
	
	void RLA()
	{
		//LSR & EOR
		uint8_t value = GetOperandValue();
		MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
		uint8_t shiftedValue = ROL(value);
		SetA(A() & shiftedValue);
		MemoryWrite(GetOperand(), shiftedValue);
	}

	void RRA()
	{
		//ROR & ADC
		uint8_t value = GetOperandValue();
		MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
		uint8_t shiftedValue = ROR(value);
		ADD(shiftedValue);
		MemoryWrite(GetOperand(), shiftedValue);
	}

	void SAX()
	{
		//STA & STX
		MemoryWrite(GetOperand(), A() & X());
	}

	void LAX()
	{
		//LDA & LDX
		uint8_t value = GetOperandValue();
		SetX(value);
		SetA(value);
	}

	void DCP()
	{
		//DEC & CMP
		uint8_t value = GetOperandValue();
		MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
		value--;
		CMP(A(), value);
		MemoryWrite(GetOperand(), value);
	}

	void ISB()
	{
		//INC & SBC
		uint8_t value = GetOperandValue();
		MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
		value++;
		ADD(value ^ 0xFF);
		MemoryWrite(GetOperand(), value);
	}

	void AAC()
	{
		SetA(A() & GetOperandValue());

		ClearFlags(PSFlags::Carry);
		if(CheckFlag(PSFlags::Negative)) {
			SetFlags(PSFlags::Carry);
		}
	}

	void ASR()
	{
		ClearFlags(PSFlags::Carry);
		SetA(A() & GetOperandValue());
		if(A() & 0x01) {
			SetFlags(PSFlags::Carry);
		}
		SetA(A() >> 1);
	}

	void ARR()
	{
		SetA(((A() & GetOperandValue()) >> 1) | (CheckFlag(PSFlags::Carry) ? 0x80 : 0x00));
		ClearFlags(PSFlags::Carry | PSFlags::Overflow);
		if(A() & 0x40) {
			SetFlags(PSFlags::Carry);
		}
		if((CheckFlag(PSFlags::Carry) ? 0x01 : 0x00) ^ ((A() >> 5) & 0x01)) {
			SetFlags(PSFlags::Overflow);
		}
	}

	void ATX()
	{
		//LDA & TAX
		uint8_t value = GetOperandValue();
		SetA(value); //LDA
		SetX(A()); //TAX
		SetA(A()); //Update flags based on A
	}

	void AXS()
	{
		//CMP & DEX
		uint8_t opValue = GetOperandValue();
		uint8_t value = (A() & X()) - opValue;
		
		ClearFlags(PSFlags::Carry);
		if((A() & X()) >= opValue) {
			SetFlags(PSFlags::Carry);
		}

		SetX(value);
	}

	void SYA()
	{
		uint8_t addrHigh = GetOperand() >> 8;
		uint8_t addrLow = GetOperand() & 0xFF;
		uint8_t value = Y() & (addrHigh + 1);
		
		//From here: http://forums.nesdev.com/viewtopic.php?f=3&t=3831&start=30
		//Unsure if this is accurate or not
		//"the target address for e.g. SYA becomes ((y & (addr_high + 1)) << 8) | addr_low instead of the normal ((addr_high + 1) << 8) | addr_low"
		MemoryWrite(((Y() & (addrHigh + 1)) << 8) | addrLow, value);
	}

	void SXA()
	{
		uint8_t addrHigh = GetOperand() >> 8;
		uint8_t addrLow = GetOperand() & 0xFF;
		uint8_t value = X() & (addrHigh + 1);
		MemoryWrite(((X() & (addrHigh + 1)) << 8) | addrLow, value);
	}
	
	//Unimplemented/Incorrect Unofficial OP codes
	void HLT()
	{
		//normally freezes the cpu, we can probably assume nothing will ever call this
		GetOperandValue();
	}

	void UNK()
	{
		//Make sure we take the right amount of cycles (not reliable for operations that write to memory, etc.)
		GetOperandValue();
	}

	void AXA()
	{
		uint16_t addr = GetOperand();
		
		//"This opcode stores the result of A AND X AND the high byte of the target address of the operand +1 in memory."	
		//This may not be the actual behavior, but the read/write operations are needed for proper cycle counting
		MemoryWrite(GetOperand(), ((addr >> 8) + 1) & A() & X());
	}

	void TAS()
	{
		//"AND X register with accumulator and store result in stack
		//pointer, then AND stack pointer with the high byte of the
		//target address of the argument + 1. Store result in memory."
		uint16_t addr = GetOperand();
		SetSP(X() & A());
		MemoryWrite(addr, SP() & ((addr >> 8) + 1));
	}

	void LAS()
	{
		//"AND memory with stack pointer, transfer result to accumulator, X register and stack pointer."
		uint8_t value = GetOperandValue();
		SetA(value & SP());
		SetX(A());
		SetSP(A());
	}

protected:
	void StreamState(bool saving) override;

public:
	CPU(shared_ptr<Console> console);
	
	uint64_t GetCycleCount() { return _cycleCount; }
	void SetMasterClockDivider(NesModel region);
	void SetNmiFlag() { _state.NMIFlag = true; }
	void ClearNmiFlag() { _state.NMIFlag = false; }
	void SetIrqMask(uint8_t mask) { _irqMask = mask; }
	void SetIrqSource(IRQSource source) { _state.IRQFlag |= (int)source; }
	bool HasIrqSource(IRQSource source) { return (_state.IRQFlag & (int)source) != 0; }
	void ClearIrqSource(IRQSource source) { _state.IRQFlag &= ~(int)source; }

	void RunDMATransfer(uint8_t offsetValue);
	void StartDmcTransfer();

	uint32_t GetClockRate(NesModel model);
	bool IsCpuWrite() { return _cpuWrite; }
		
	//Used by debugger for "Set Next Statement"
	void SetDebugPC(uint16_t value) {
		SetPC(value);
		_state.PreviousDebugPC = _state.DebugPC;
		_state.DebugPC = value;
	}

	void Reset(bool softReset, NesModel model);
	void Exec();

	void GetState(State &state) 
	{ 
		state = _state;
		state.CycleCount = _cycleCount;
	}

	uint16_t GetDebugPC() { return _state.DebugPC; }
	uint16_t GetPC() { return _state.PC; }

	void SetState(State state)
	{
		uint16_t originalPc = state.PC;
		uint16_t originalDebugPc = state.DebugPC;
		_state = state;
		_cycleCount = state.CycleCount;
		state.PC = originalPc;
		state.DebugPC = originalDebugPc;
	}

#ifdef DUMMYCPU
#undef CPU
	void SetDummyState(CPU *c)
	{
#define CPU DummyCpu
		_writeCounter = 0;
		_readCounter = 0;

		_state = c->_state;

		_cycleCount = c->_cycleCount;
		_operand = c->_operand;
		_spriteDmaTransfer = c->_spriteDmaTransfer;
		_needHalt = c->_needHalt;
		_dmcDmaRunning = c->_dmcDmaRunning;
		_cpuWrite = c->_cpuWrite;
		_needDummyRead = c->_needDummyRead;
		_needHalt = c->_needHalt;
		_spriteDmaOffset = c->_spriteDmaOffset;
		_irqMask = c->_irqMask;
		_prevRunIrq = c->_prevRunIrq;
		_runIrq = c->_runIrq;
		_cycleCount = c->_cycleCount;
	}

	uint32_t GetWriteCount()
	{
		return _writeCounter;
	}

	uint32_t GetReadCount()
	{
		return _readCounter;
	}

	void GetWriteAddrValue(uint32_t index, uint16_t &addr, uint8_t &value, bool &isDummyWrite)
	{
		addr = _writeAddresses[index];
		value = _writeValue[index];
		isDummyWrite = _isDummyWrite[index];
	}

	void GetReadAddr(uint32_t index, uint16_t &addr, uint8_t &value, bool &isDummyRead)
	{
		addr = _readAddresses[index];
		value = _readValue[index];
		isDummyRead = _isDummyRead[index];
	}
#endif
};

#endif