/**
 ** Supermodel
 ** A Sega Model 3 Arcade Emulator.
 ** Copyright 2011 Bart Trzynadlowski, Nik Henson
 **
 ** This file is part of Supermodel.
 **
 ** Supermodel is free software: you can redistribute it and/or modify it under
 ** the terms of the GNU General Public License as published by the Free 
 ** Software Foundation, either version 3 of the License, or (at your option)
 ** any later version.
 **
 ** Supermodel is distributed in the hope that it will be useful, but WITHOUT
 ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 ** FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 ** more details.
 **
 ** You should have received a copy of the GNU General Public License along
 ** with Supermodel.  If not, see <http://www.gnu.org/licenses/>.
 **/

/*
 * CPUDebug.h
 */

#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_CPUDEBUG_H
#define INCLUDED_CPUDEBUG_H

#include <stdio.h>
#include <vector>
#include <algorithm>
using namespace std;

#include "Types.h"

#include "CodeAnalyser.h"
#include "AddressTable.h"
#include "Breakpoint.h"
#include "Debugger.h"
#include "Exception.h"
#include "Interrupt.h"
#include "IO.h"
#include "Register.h"
#include "Watch.h"

#ifdef DEBUGGER_HASBLOCKFILE
#include "BlockFile.h"
#endif // DEBUGGER_HASBLOCKFILE

#ifdef DEBUGGER_HASTHREAD
#include "OSD/Thread.h"
#endif // DEBUGGER_HASTHREAD

#define MAX_EXCEPTIONS 255
#define MAX_INTERRUPTS 255
#define MAX_IOPORTS 255

namespace Debugger
{
	class CRegion;
	class CLabel;
	class CComment;

	enum EStepMode
	{
		StepInto = 0,
		StepOver,
		StepOut
	};

	enum EOpFlags
	{
		NormalOp    = 0,
		JumpSimple  = 1,
		JumpLoop    = 2,
		JumpSub     = 4,
		JumpEx      = 8,
		ReturnSub   = 16,
		ReturnEx    = 32,
		HaltExec    = 64,
		Relative    = 128,
		Conditional = 256,
		NotFixed    = 512
	};

	/*
	 * Base class that facilitates the debugging of a particular emulated CPU.
	 * It holds the debugging hooks that the CPU should call to implement debugging.
	 * Also contains CPU-specific information and provides access to CPU-specific objects such as CRegister, CException etc.
	 */
	class CCPUDebug
	{
	friend class CDebugger;

	private:
		bool m_enabled;
		bool m_break;
#ifdef DEBUGGER_HASTHREAD
		bool m_breakWait;
#endif // DEBUGGER_HASTHREAD
		bool m_breakUser;
		bool m_halted;
		bool m_step;
		EStepMode m_stepMode;
		bool m_stepBreak;
		bool m_steppingOver;
		UINT32 m_stepOverAddr;
		bool m_steppingOut;
		UINT32 m_stepOutAddr;
		int m_count;
		bool m_until;
		UINT32 m_untilAddr;
		UINT32 m_execAndMask;
		UINT32 m_execOrMask;
		UINT32 m_mem8AndMask;
		UINT32 m_mem8OrMask;
		UINT32 m_mem16AndMask;
		UINT32 m_mem16OrMask;
		UINT32 m_mem32AndMask;
		UINT32 m_mem32OrMask;
		UINT32 m_mem64AndMask;
		UINT32 m_mem64OrMask;

#ifdef DEBUGGER_HASTHREAD
		CMutex *m_mutex;
		CCondVar *m_condVar;
#endif // DEBUGGER_HASTHREAD

		CException *m_exArray[MAX_EXCEPTIONS];
		CInterrupt *m_intArray[MAX_INTERRUPTS];
		CPortIO *m_portArray[MAX_IOPORTS];

		CAddressTable *m_mappedIOTable;
		CAddressTable *m_memWatchTable;
		CAddressTable *m_bpTable;

		int m_numRegMons;
		CRegMonitor **m_regMonArray;

		bool ShiftAddress(UINT32 &addr, unsigned &dataSize, UINT64 &data, CAddressRef *ref);

		void CheckRead(UINT32 addr, unsigned dataSize, UINT64 data);

		void CheckWrite(UINT32 addr, unsigned dataSize, UINT64 data);
		
		void MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead);

		void IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isRead);

		void AttachToDebugger(CDebugger *theDebugger);

		void DetachFromDebugger(CDebugger *theDebugger);
		
		void UpdateRegMonArray();

		void UpdateIOWatchNums();
	
		void UpdateMemWatchNums();

		void UpdateExecMasks();

		void UpdateMemMasks();

		bool CheckExecute(UINT32 newPC, UINT32 newOpcode, UINT32 lastCycles);
		
	protected:
		CCodeAnalyser *m_analyser;

		bool m_stateUpdated;
		CException *m_exRaised;
		CException *m_exTrapped;
		CInterrupt *m_intRaised;
		CInterrupt *m_intTrapped;
		CBreakpoint *m_bpReached;
		CWatch *m_memWatchTriggered;
		CWatch *m_ioWatchTriggered;
		CRegMonitor *m_regMonTriggered;

		UINT64 m_prevTotalCycles;

		CCPUDebug(const char *cpuType, const char *cpuName, 
			UINT8 cpuMinInstrLen, UINT8 cpuMaxInstrLen, bool cpuBigEndian, UINT8 cpuMemBusWidth, UINT8 cpuMaxMnemLen);

		//
		// Protected virtual methods for sub-class to implement
		//

		virtual bool UpdatePC(UINT32 pc) = 0;
		
		virtual bool ForceException(CException *ex) = 0;

		virtual bool ForceInterrupt(CInterrupt *in) = 0;

	public:
		const char *type;
		const char *name;
		
		const UINT8 minInstrLen;
		const UINT8 maxInstrLen;
		const bool bigEndian;
		const UINT8 memBusWidth;
		const UINT8 maxMnemLen;
		
		EFormat addrFmt;
		EFormat portFmt;
		EFormat dataFmt;

		CDebugger *debugger;

		UINT16 numExCodes;
		UINT16 numIntCodes;
		UINT16 numPorts;
		UINT32 memSize;

		bool active;
		UINT64 instrCount;
		UINT64 totalCycles;
		UINT64 cyclesPerPoll;
		UINT32 pc;
		UINT32 opcode;

		vector<CRegister*> regs;
		vector<CException*> exceps;
		vector<CInterrupt*> inters;
		vector<CIO*> ios;
		// TODO - should use map<UINT32,T*> for T=CRegion,CLabel&CComment so that look-ups via address are faster
		vector<CRegion*> regions;
		vector<CLabel*> labels;
		vector<CComment*> comments;
		vector<CWatch*> memWatches;
		vector<CWatch*> ioWatches;
		vector<CBreakpoint*> bps;
		vector<CRegMonitor*> regMons;

		virtual ~CCPUDebug();

		//
		// Methods to define CPU registers (must be called before attached to debugger)

		CPCRegister *AddPCRegister(const char *name, const char *group);

		CAddrRegister *AddAddrRegister(const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc = NULL);

		CIntRegister *AddInt8Register(const char *name, const char *group, unsigned id, GetInt8FPtr getFunc, SetInt8FPtr setFunc = NULL);

		CIntRegister *AddInt16Register(const char *name, const char *group, unsigned id, GetInt16FPtr getFunc, SetInt16FPtr setFunc = NULL);

		CIntRegister *AddInt32Register(const char *name, const char *group, unsigned id, GetInt32FPtr getFunc, SetInt32FPtr setFunc = NULL);

		CIntRegister *AddInt64Register(const char *name, const char *group, unsigned id, GetInt64FPtr getFunc, SetInt64FPtr setFunc = NULL);

		CStatusRegister *AddStatus8Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt8FPtr getFunc, SetInt8FPtr setFunc = NULL);
		
		CStatusRegister *AddStatus16Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt16FPtr getFunc, SetInt16FPtr setFunc = NULL);

		CStatusRegister *AddStatus32Register(const char *name, const char *group, unsigned id, const char *bitStr, GetInt32FPtr getFunc, SetInt32FPtr setFunc = NULL);

		CFPointRegister *AddFPointRegister(const char *name, const char *group, unsigned id, GetFPointFPtr getFunc, SetFPointFPtr setFunc = NULL);

		//
		// Methods to add exceptions and interrupts (must be called before attached to debugger)
		//

		CException *AddException(const char *id, UINT16 code, const char *name);

		CInterrupt *AddInterrupt(const char *id, UINT16 code, const char *name);

		//
		// Methods to define memory layout (must be called before attached to debugger)
		//

		CRegion *AddRegion(UINT32 start, UINT32 end, bool isCode, bool isReadOnly, const char *name);

		CPortIO *AddPortIO(UINT16 portNum, unsigned dataSize, const char *name, const char *group);

		CMappedIO *AddMappedIO(UINT32 addr, unsigned dataSize, const char *name, const char *group);

		//
		// Parsing and formatting methods
		//

		bool ParseData(const char *str, unsigned dataSize, UINT64 *data, bool registers = true);

		void FormatData(char *str,  unsigned dataSize, UINT64 data);

		bool ParseAddress(const char *str, UINT32 *addr, bool customLabels = true, bool autoLabels = true, bool registers = true);

		void FormatAddress(char *str, UINT32 addr, bool customLabels = false, ELabelFlags autoLabelFlags = LFNone);

		void FormatJumpAddress(char *str, UINT32 jumpAddr, EOpFlags opFlags);

		bool ParsePortNum(const char *str, UINT16 *portNum, bool customLabels = true, bool registers = true);

		void FormatPortNum(char *str, UINT16 portNum, bool customLabels = false);

		//
		// Checking methods that hook into CPU emulation code
		//

		/*
		 * Should be called after every 8-bit read.
		 */
		void CheckRead8(UINT32 addr, UINT8 data);

		/*
		 * Should be called after every 16-bit read.
		 */
		void CheckRead16(UINT32 addr, UINT16 data);
		
		/*
		 * Should be called after every 32-bit read.
		 */
		void CheckRead32(UINT32 addr, UINT32 data);

		/*
		 * Should be called after every 64-bit read.
		 */
		void CheckRead64(UINT32 addr, UINT64 data);

		/*
		 * Should be called after every 8-bit write.
		 */
		void CheckWrite8(UINT32 addr, UINT8 data);

		/*
		 * Should be called after every 16-bit write.
		 */
		void CheckWrite16(UINT32 addr, UINT16 data);
		
		/*
		 * Should be called after every 32-bit write.
		 */
		void CheckWrite32(UINT32 addr, UINT32 data);

		/*
		 * Should be called after every 64-bit write.
		 */
		void CheckWrite64(UINT32 addr, UINT64 data);

		/*
		 * Should be called after every read from an I/O port.
		 */
		void CheckPortInput(UINT16 portNum, UINT64 data);
		
		/*
		 * Should be called after every write to an I/O port.
		 */
		void CheckPortOutput(UINT16 portNum, UINT64 data);

		/*
		 * Should be called by CPU when it becomes active and starts executing an instruction loop.
		 * This call is needed so that the debugger can handle multi-threading of CPUs.
		 */
		void CPUActive();

		/*
		 * Should be called by CPU when it becomes inactive after having finished executing an instruction loop.
		 * This call is needed so that the debugger can handle multi-threading of CPUs.
		 */
		void CPUInactive();

		/*
		 * Should be called by CPU before every instruction.
		 * If returns true, then PC may have been changed by user and/or an exception/interrupt may have forced, so CPU core should 
		 * check for this and handle appropriately.  Otherwise, it can continue to execute as normal.
		 */
		bool CPUExecute(UINT32 newPC, UINT32 newOpcode, UINT32 lastCycles);

		/*
		 * Should be called by CPU whenever a CPU exception is raised (and before the exception handler is executed).
		 */
		virtual void CPUException(UINT16 exCode);

		/*
		 * Should be called by CPU whenever a CPU interrupt is raised (and before the interrupt handler is executed).
		 */
		virtual void CPUInterrupt(UINT16 intCode);

		void WaitCommand(EHaltReason reason);

#ifdef DEBUGGER_HASTHREAD
		void ForceWait();

		void WaitForHalt();

		void ClearWait();
#endif // DEBUGGER_HASTHREAD

		//
		// Execution control
		//
	
		bool IsEnabled();

		void SetEnabled(bool enabled);

		void ForceBreak(bool user);

		void ClearBreak();

		void SetContinue();

		void SetStepMode(EStepMode stepMode);

		void SetCount(int count);

		void SetUntil(UINT32 untilAddr);

		bool SetPC(UINT32 newPC);

		bool GenerateException(CException *ex);

		bool GenerateInterrupt(CInterrupt *in);

		//
		// Register access
		//

		CRegister *GetRegister(const char *name);

		//
		// Exception access
		//

		CException *GetException(const char *id);

		CException *GetException(UINT16 code);

		//
		// Interrupt access
		//

		CInterrupt *GetInterrupt(const char *id);

		CInterrupt *GetInterrupt(UINT16 code);

		//
		// I/O access
		//

		CPortIO *GetPortIO(UINT16 portNum);

		CMappedIO *GetMappedIO(UINT32 addr);

		//
		// Region access
		//

		CRegion *GetRegion(UINT32 addr);

		//
		// Label handling
		//

		CLabel *AddLabel(UINT32 addr, const char *name);

		CLabel *GetLabel(UINT32 addr);

		CLabel *GetLabel(const char *name);

		bool RemoveLabel(const char *name);

		bool RemoveLabel(UINT32 addr);

		bool RemoveLabel(CLabel *label);

		bool RemoveAllLabels();

		//
		// Comment handling
		//

		CComment *AddComment(UINT32 addr, const char *commentText);

		CComment *GetComment(UINT32 addr);

		bool RemoveComment(UINT32 addr);

		bool RemoveComment(CComment *comment);

		bool RemoveAllComments();

		//
		// Watch handling
		//

		CSimpleWatch *AddSimpleMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite);

		CCountWatch *AddCountMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, unsigned count);

		CMatchWatch *AddMatchMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, vector<UINT64> &dataSeq);

		CPrintWatch *AddPrintMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite);

		CCaptureWatch *AddCaptureMemWatch(UINT32 addr, UINT32 size, bool trigRead, bool trigWrite, unsigned maxLen);

		bool RemoveAllMemWatches();

		bool RemoveAllIOWatches();

		bool RemoveMemWatch(UINT32 addr, unsigned size);

		CWatch *GetMemWatch(UINT32 addr, unsigned size);

		void AddWatch(CWatch *watch);

		bool RemoveWatch(CWatch *watch);

		//
		// Breakpoint handling
		//

		CSimpleBreakpoint *AddSimpleBreakpoint(UINT32 addr);

		CCountBreakpoint *AddCountBreakpoint(UINT32 addr, unsigned count);

		CPrintBreakpoint *AddPrintBreakpoint(UINT32 addr);

		void AddBreakpoint(CBreakpoint *bp);

		CBreakpoint *GetBreakpoint(UINT32 addr);

		bool RemoveBreakpoint(UINT32 addr);

		bool RemoveBreakpoint(CBreakpoint *bp);

		bool RemoveAllBreakpoints();

		void UpdateBreakpointNums();

		//
		// Register monitor handling
		//

		CRegMonitor *AddRegMonitor(const char *regName);

		CRegMonitor *GetRegMonitor(const char *regName);

		void AddRegMonitor(CRegMonitor *regMon);

		bool RemoveRegMonitor(const char *regName);

		bool RemoveRegMonitor(CRegMonitor *regMon);

		bool RemoveAllRegMonitors();

		//
		// Code analyser
		//

		CCodeAnalyser *GetCodeAnalyser();

		//
		// Memory searching
		//

		bool FindInMem(UINT32 start, UINT32 end, const char *str, bool matchCase, UINT32 &findAddr);

		bool FindInMem(UINT32 start, UINT32 end, UINT8 *bytes, unsigned length, UINT32 &findAddr); 

		//
		// Debugger state loading/saving
		//

#ifdef DEBUGGER_HASBLOCKFILE
		bool LoadState(CBlockFile *state);

		bool SaveState(CBlockFile *state);
#endif // DEBUGGER_HASBLOCKFILE

		//
		// Public virtual methods for sub-class to implement
		//

		virtual void AttachToCPU() = 0;

		virtual void DetachFromCPU() = 0;

		virtual void DebuggerReset();

		virtual void DebuggerPolled();

		virtual UINT32 GetResetAddr() = 0;

		virtual UINT64 ReadMem(UINT32 addr, unsigned dataSize);

		virtual bool WriteMem(UINT32 addr, unsigned dataSize, UINT64 data);

		virtual UINT64 ReadPort(UINT16 portNum);

		virtual bool WritePort(UINT16 portNum, UINT64 data);

		virtual int Disassemble(UINT32 addr, char *mnemonic, char *operands) = 0;

		virtual int GetOpLength(UINT32 addr);

		// Returns head (no more than 32-bits or min instr length) of full opcode for use with following methods
		virtual UINT32 GetOpcode(UINT32 addr);

		virtual EOpFlags GetOpFlags(UINT32 addr, UINT32 opcode) = 0;

		virtual bool GetJumpAddr(UINT32 addr, UINT32 opcode, UINT32 &jumpAddr) = 0;
		
		virtual bool GetJumpRetAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) = 0;

		virtual bool GetReturnAddr(UINT32 addr, UINT32 opcode, UINT32 &retAddr) = 0;

		virtual bool GetHandlerAddr(CException *ex, UINT32 &handlerAddr) = 0;

		virtual bool GetHandlerAddr(CInterrupt *in, UINT32 &handlerAddr) = 0;
	};

	//
	// Inlined methods
	//

	inline bool CCPUDebug::ShiftAddress(UINT32 &addr, unsigned &dataSize, UINT64 &data, CAddressRef *ref)
	{
		UINT32 refAddr = ref->addr;
		unsigned refSize = ref->size;
		unsigned offset;
		if (refAddr >= addr)
		{
			offset = refAddr - addr;
			// dataSize will always be >= offset
			dataSize -= offset;
			if (dataSize <= refSize)
				return false;
			addr = refAddr + refSize;
			dataSize -= refSize;
			//data <<= (8 * (offset + refSize));
			return true;
		}
		else
		{
			offset = refAddr + refSize - addr;
			if (dataSize <= offset)
				return false;
			addr += offset;
			dataSize -= offset;
			//data <<= (8 * offset); 
			return true;
		}
	}

	inline void CCPUDebug::CheckRead(UINT32 addr, unsigned dataSize, UINT64 data)
	{
		// TODO - currently assumes big-endian - should act according to this->bigEndian
		
		// For reads larger than 1 byte, care is taken with mapped I/O or watches that overlap within the read region
		while (dataSize > 0)
		{
			// Check if address is mapped I/O
			CMappedIO *mappedIO = NULL;
			if (m_mappedIOTable != NULL)
			{
				mappedIO = (CMappedIO*)m_mappedIOTable->Get(addr, dataSize);
				if (mappedIO != NULL)
				{
					// Check with I/O if its watch is triggered
					if (mappedIO->CheckInput(addr, dataSize, data))
						IOWatchTriggered(mappedIO->watch, mappedIO, data, true);

					// See if still have remaining data to check
					if (!ShiftAddress(addr, dataSize, data, mappedIO))
						return;
				}
			}

			// Check if address has a watch
			CWatch *watch = NULL;
			if (m_memWatchTable != NULL)
			{
				watch = (CWatch*)m_memWatchTable->Get(addr, dataSize);
				if (watch != NULL)
				{
					// Check if watch is triggered
					if (watch->CheckRead(addr, dataSize, data))
						MemWatchTriggered(watch, addr, dataSize, data, true);

					// See if still have remaining data to check
					if (!ShiftAddress(addr, dataSize, data, watch))
						return;
				}
				else if (mappedIO == NULL)
					return;
			}
			else if (mappedIO == NULL)
				return;
		}
	}

	inline void CCPUDebug::CheckWrite(UINT32 addr, unsigned dataSize, UINT64 data)
	{
		// TODO - currently assumes big-endian - should act according to this->bigEndian
		
		// For writes larger than 1 byte, care is taken with mapped I/O or watches that overlap within the written region
		while (dataSize > 0)
		{
			// Check if writing to mapped I/O address
			CMappedIO *mappedIO = NULL;
			if (m_mappedIOTable != NULL)
			{
				mappedIO = (CMappedIO*)m_mappedIOTable->Get(addr, dataSize);
				if (mappedIO != NULL)
				{
					// Check with I/O if its watch is triggered
					if (mappedIO->CheckOutput(addr, dataSize, data))
						IOWatchTriggered(mappedIO->watch, mappedIO, data, false);

					// See if still have remaining data to check
					if (!ShiftAddress(addr, dataSize, data, mappedIO))
						return;
				}
			}

			// Check if address has a watch
			CWatch *watch = NULL;
			if (m_memWatchTable != NULL)
			{
				watch = (CWatch*)m_memWatchTable->Get(addr, dataSize);
				if (watch != NULL)
				{
					// Check if watch is triggered
					if (watch->CheckWrite(addr, dataSize, data))
						MemWatchTriggered(watch, addr, dataSize, data, false);

					// See if still have remaining data to check
					if (!ShiftAddress(addr, dataSize, data, watch))
						return;			
				}
				else if (mappedIO == NULL)
					return;
			}
			else if (mappedIO == NULL)
				return;
		}
	}

	inline void CCPUDebug::MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool read)
	{
		m_memWatchTriggered = watch;
		debugger->MemWatchTriggered(watch, addr, dataSize, data, read);
		watch->Reset();
		m_break = true;
		UpdateExecMasks();
	}

	inline void CCPUDebug::IOWatchTriggered(CWatch* watch, CIO *io, UINT64 data, bool read)
	{
		m_ioWatchTriggered = watch;
		debugger->IOWatchTriggered(watch, io, data, read);
		watch->Reset();
		m_break = true;
		UpdateExecMasks();
	}

	inline void CCPUDebug::CheckRead8(UINT32 addr, UINT8 data)
	{
		if ((addr&m_mem8AndMask) != m_mem8AndMask || (addr&m_mem8OrMask) != 0)
			return;

		// Check if reading from mapped I/O address
		unsigned dataSize = 1;
		if (m_mappedIOTable != NULL)
		{
			CMappedIO *mappedIO = (CMappedIO*)m_mappedIOTable->Get(addr);
			if (mappedIO != NULL)
			{
				if (mappedIO->CheckInput(addr, dataSize, data))
					IOWatchTriggered(mappedIO->watch, mappedIO, data, true);
				return;
			}
		}
		// Check if address has a watch
		if (m_memWatchTable == NULL)
			return;
		CWatch *watch = (CWatch*)m_memWatchTable->Get(addr);
		if (watch != NULL && watch->CheckRead(addr, dataSize, data))
			MemWatchTriggered(watch, addr, dataSize, data, true);
	}

	inline void CCPUDebug::CheckRead16(UINT32 addr, UINT16 data)
	{
		if ((addr&m_mem16AndMask) == m_mem16AndMask && (addr&m_mem16OrMask) == 0)
			CheckRead(addr, 2, data);
	}

	inline void CCPUDebug::CheckRead32(UINT32 addr, UINT32 data)
	{
		if ((addr&m_mem32AndMask) == m_mem32AndMask && (addr&m_mem32OrMask) == 0)
			CheckRead(addr, 4, data);
	}

	inline void CCPUDebug::CheckRead64(UINT32 addr, UINT64 data)
	{
		if ((addr&m_mem64AndMask) == m_mem64AndMask && (addr&m_mem64OrMask) == 0)
			CheckRead(addr, 8, data);
	}

	inline void CCPUDebug::CheckWrite8(UINT32 addr, UINT8 data)
	{
		if ((addr&m_mem8AndMask) != m_mem8AndMask || (addr&m_mem8OrMask) != 0)
			return;

		// Check if writing to mapped I/O address
		unsigned dataSize = 1;
		if (m_mappedIOTable != NULL)
		{
			CMappedIO *mappedIO = (CMappedIO*)m_mappedIOTable->Get(addr);
			if (mappedIO != NULL)
			{
				if (mappedIO->CheckOutput(addr, dataSize, data))
					IOWatchTriggered(mappedIO->watch, mappedIO, data, false);
				return;
			}
		}
		// Check if address has a watch
		if (m_memWatchTable == NULL)
			return;
		CWatch *watch = (CWatch*)m_memWatchTable->Get(addr);
		if (watch != NULL && watch->CheckWrite(addr, dataSize, data))
			MemWatchTriggered(watch, addr, dataSize, data, false);
	}

	inline void CCPUDebug::CheckWrite16(UINT32 addr, UINT16 data)
	{
		if ((addr&m_mem16AndMask) == m_mem16AndMask && (addr&m_mem16OrMask) == 0)
			CheckWrite(addr, 2, data);
	}

	inline void CCPUDebug::CheckWrite32(UINT32 addr, UINT32 data)
	{
		if ((addr&m_mem32AndMask) == m_mem32AndMask && (addr&m_mem32OrMask) == 0)
			CheckWrite(addr, 4, data);
	}

	inline void CCPUDebug::CheckWrite64(UINT32 addr, UINT64 data)
	{
		if ((addr&m_mem64AndMask) == m_mem64AndMask && (addr&m_mem64OrMask) == 0)
			CheckWrite(addr, 8, data);
	}

	inline void CCPUDebug::CheckPortInput(UINT16 portNum, UINT64 data)
	{
		if (portNum >= numPorts)
			return;
		CPortIO *port = m_portArray[portNum];
		if (port != NULL && port->CheckInput(data))
			IOWatchTriggered(port->watch, port, data, true);
	}

	inline void CCPUDebug::CheckPortOutput(UINT16 portNum, UINT64 data)
	{
		if (portNum >= numPorts)
			return;
		CPortIO *port = m_portArray[portNum];
		if (port != NULL && port->CheckOutput(data))
			IOWatchTriggered(port->watch, port, data, false);
	}	

	inline bool CCPUDebug::CPUExecute(UINT32 newPC, UINT32 newOpcode, UINT32 lastCycles)
	{
		// Check if should check execution flow
		if ((newPC&m_execAndMask) == m_execAndMask && (newPC&m_execOrMask) == 0)
			return CheckExecute(newPC, newOpcode, lastCycles);
		else
		{
			// If not, then just update instruction count, total cycles counts, pc and opcode
			instrCount++;
			totalCycles += lastCycles;
			pc = newPC;
			opcode = newOpcode;		
			return false;
		}
	}
	
	inline bool CCPUDebug::CheckExecute(UINT32 newPC, UINT32 newOpcode, UINT32 lastCycles)
	{
		// Check not just updating state
		bool stepBreak, countBreak, untilBreak;
		if (!m_stateUpdated)
		{
			// If so, then first check to see if any registers have changed from previous instruction before updating pc and opcode
			if (m_numRegMons > 0)
			{
				for (int i = 0; i < m_numRegMons; i++)
				{
					CRegMonitor *regMon = m_regMonArray[i];
					if (regMon->HasChanged())
					{
						m_regMonTriggered = regMon;
						debugger->MonitorTriggered(regMon);
						regMon->Reset();
						m_break = true;
					}
				}
			}

			// Now update instruction count, total cycles count, pc and opcode
			instrCount++;
			totalCycles += lastCycles;
			pc = newPC;
			opcode = newOpcode;		

			// Next check for breakpoints at new pc and opcode
			if (m_bpTable != NULL)
			{
				CBreakpoint *bp = (CBreakpoint*)m_bpTable->Get(pc);
				if (bp != NULL && bp->Check(pc, opcode))
				{
					m_bpReached = bp;
					debugger->BreakpointReached(bp);
					m_break = true;
				}
			}
			
			// Check if currently implementing a step mode
			if (m_step)
			{
				if (instrCount == 1)
					stepBreak = true;
				else
				{
					switch (m_stepMode)
					{
						case StepInto: 
							// Step-into always breaks
							stepBreak = true; 
							break;
						case StepOver:
							// Step-over breaks if was not stepping over a jump or was stepping over and has returned from jump
							stepBreak = !m_steppingOver || pc == m_stepOverAddr;
							break;
						case StepOut:
							// Step-out steps over any jumps and breaks when it reaches a return or an exception/interrupt is raised
							if (m_exRaised != NULL || m_intRaised != NULL)
								stepBreak = true;
							else if (m_steppingOver)
							{
								if (pc == m_stepOverAddr)
									m_steppingOver = false;
								stepBreak = false;
							}
							else
								stepBreak = m_steppingOut && pc == m_stepOutAddr;
							break;
						default:
							stepBreak = false;
							break;
					}
				}
			}
			else
				stepBreak = false;
			
			// Lastly, check if counting instructions or if running until a given address
			countBreak = m_count > 0 && --m_count == 0;
			untilBreak = m_until && pc == m_untilAddr;
		}
		else
		{
			// Just updating state so update pc and opcode, but do not update instruction count
			pc = newPC;
			opcode = newOpcode;		

			stepBreak  = false;
			countBreak = false;
			untilBreak = false;
		}

		// If a break has been forced, state was updated, stepping through code or have run a set number of instructions or to a 
		// given address then break now and wait for command from user
		if (m_break || m_stateUpdated || stepBreak || countBreak || untilBreak)
		{
			// See if execution halt was caused by user manually breaking execution in some manner
			EHaltReason reason = HaltNone;
			if (m_stateUpdated) reason = (EHaltReason)(reason | HaltState);
			if (m_breakUser)    reason = (EHaltReason)(reason | HaltUser);
			if (stepBreak)      reason = (EHaltReason)(reason | HaltStep);
			if (countBreak)     reason = (EHaltReason)(reason | HaltCount);
			if (untilBreak)     reason = (EHaltReason)(reason | HaltUntil);
			
			// Keep hold of breakpoint, if any, and its address so that can reset it later
			UINT32 bpAddr = 0;
			CBreakpoint *bpToReset = NULL;
			if (m_bpReached != NULL)
			{
				bpAddr = m_bpReached->addr;
				bpToReset = m_bpReached;
			}

			// Reset all control flags
			m_stateUpdated = false;
			m_step = false;
			m_steppingOver = false;
			m_steppingOut = false;
			m_count = 0;
			m_until = false;
			m_exRaised = NULL;
			m_exTrapped = NULL;
			m_intRaised = NULL;
			m_intTrapped = NULL;
			m_bpReached = NULL;
			m_memWatchTriggered = NULL;
			m_ioWatchTriggered = NULL;
			m_regMonTriggered = NULL;
			UpdateExecMasks();
		
			// Wait for instruction from user
			WaitCommand(reason);
			
			// Reset breakpoint, if any
			if (bpToReset != NULL && m_bpTable != NULL)
			{
				// Check breakpoint to reset was not deleted by user
				CBreakpoint *bp = (CBreakpoint*)m_bpTable->Get(bpAddr);
				if (bp == bpToReset)
					bp->Reset();
			}

			// If stepping over, get step over address if available
			if (m_step && m_stepMode == StepOver)
				m_steppingOver = GetJumpRetAddr(pc, opcode, m_stepOverAddr);
		}

		// If stepping out, then make sure step over any jumps and if encounter a return instruction then break at return address
		if (m_step && m_stepMode == StepOut)
		{
			if (!m_steppingOver)
				m_steppingOver = GetJumpRetAddr(pc, opcode, m_stepOverAddr);
			if (!m_steppingOver)
				m_steppingOut = GetReturnAddr(pc, opcode, m_stepOutAddr);
		}

		return m_stateUpdated;
	}
}

#endif	// INCLUDED_CPUDEBUG_H
#endif  // SUPERMODEL_DEBUGGER