// Debugger.h: interface for the CDebugger class.
//


#pragma once

#include "BreakPoint.h"
#include "CPU.h"
#include "DebuggerItemParam.h"

#define COLORED_TAG "<C>"
#define COLORED_TAG_LENGTH 3

namespace SYS_PORT
{
	enum SYS_PORT : int
	{
		// эту штуку не получается сделать class.
		PNONE = -1,
		P177660 = 0,
		P177662_IN,
		P177662_OUT,
		P177664,
		P177700,
		P177702,
		P177704,
		P177706,
		P177710,
		P177712,
		P177714_IN,
		P177714_OUT,
		P177716_IN,
		P177716_OUT_TAP,
		P177716_OUT_MEM
	};
}

// массив цветов для подсветки синтаксиса
namespace HLCOLOR
{
	enum HLCOLOR : int
	{
		DEFAULT = 0,
		ADDRESS,
		MNEMONIC,
		REGISTER,
		NUMBER,
		SYMBOL,
		MNEMONICFPU,
		REGISTERFPU,
		NUM_COLORS    // количество цветов
	};
}
extern const COLORREF g_crDebugColorHighLighting[HLCOLOR::NUM_COLORS];
constexpr int MNEMONIX_NUM = 113;

constexpr uint32_t IDBG_FLG_NONE = 0;        // нет флагов
constexpr uint32_t IDBG_FLG_OVER = (1 << 0); // флаг инструкции обхода
constexpr uint32_t IDBG_FLG_OUTI = (1 << 1); // флаг инструкции захода
constexpr uint32_t IDBG_FLG_OUTO = (1 << 2); // флаг инструкции выхода
constexpr uint32_t IDBG_FLG_SOB  = (1 << 3); // флаг инструкции обхода

class CDebugger
{
	public:
		enum class STEP_STATUS : int { NONE = 0, GOINTO = 1, GOOUT = 2, GOOVER = 4 };

	private:
		// в качестве аргумента - значение опкода в m_wInstr,
		// результат - длина всей команды с аргументами в словах
		using CalcInstrLenRef     = int (CDebugger::*)() const;
		// в качестве аргумента - значение в m_wInstr, m_wPC - адрес за опкодом,
		// результат - следующий адрес, или BREAK_ADDRESS::ADDRESS_NONE, если рассчитать невозможно
		using CalcNextAddrRef     = uint16_t (CDebugger::*)();
		// в качестве аргумента - значение в m_wInstr и m_wPC, и структура параметров для анализа
		// результат - в m_strInstr и m_strArg
		using DisassembleInstrRef = void (CDebugger::*)(CDbgItemParam &);

		struct InstrFuncRefs
		{
			const CString      *pMnemonic;
			CalcInstrLenRef     InstrLenRef;
			CalcNextAddrRef     NextAddrRef;
			DisassembleInstrRef DisasmInstrRef;
			uint32_t            nFlags;
		};

		static std::unique_ptr<InstrFuncRefs[]> m_pInstrRefsMap;
		void RegisterMethodRef(uint16_t start, uint16_t end,
		                       const CString *mnemonic, CalcInstrLenRef ilenmref, CalcNextAddrRef nxamref, DisassembleInstrRef dsimref,
		                       uint32_t flg);

		static std::vector<DbgStackPoint_t> m_outLevel;
		static STEP_STATUS                  m_StepStatus;
		static const CString m_strRegNames[8];
		static const CString m_strRegNamesFPU[8];
		static const CString m_strAddrFormat[8];
		static const CString m_strAddrFormat_PC[8];
		static const CString m_strArgFormat_Addr;
		static const CString m_strArgFormat_Number;
		static const CString m_strArgFormat_Comma;

		static const CString m_strMnemonix[MNEMONIX_NUM];

//		CDisasmView        *m_pDisasmDlg;
		CBreakPointList     m_breakpointList;

		bool                m_bPrevCmdC;    // флаг, как дизассемблировать BCC/BHIS BCS/BLO
		bool                m_bPrevCmdCp;   // после команды CMP - сравнение, иначе - битС

		// для того, чтобы не передавать функциям кучу лишней информации, заведём глобальные переменные

		uint16_t            m_wPC;          // текущий адрес текущей инструкции
		uint16_t            m_wInstr;       // сама инструкция
		bool                m_bCBug;        // флаг бага флага С
		uint16_t            m_wFreg;        // регистр флагов состояний NZVC с учётом бага
		CString             m_strInstr;     // мнемоника
		CString             m_strArg;       // аргументы, если есть

		//флаги дизассемблирования FPU
		bool                m_bFD, m_bFL;


		inline bool         GetFREGBit(const PSW_BIT::PSW_BIT pos) const
		{
			return !!(m_wFreg & (1 << pos));
		}

		int                 ConvertArgToString(const int arg, uint16_t pc, CString &strSrc, CDbgItemParam &p) const;
		int                 ConvertArgToStringFPU(const int arg, uint16_t pc, CString &strSrc, CDbgItemParam &p) const;


		uint16_t            GetArgD(int pos);
		uint16_t            GetArgAddrD(int meth, const CPUREG::REGS reg) const;
		int                 CalcArgLength(int pos) const;

		inline int          CalcLenOneWord() const;
		inline int          CalcLenTwoFields() const;
		inline int          CalcLenFourFields() const;
		inline int          CalcLenFIS() const;

		uint16_t            CalcNextAddrRegular4();
		uint16_t            CalcNextAddrRegular2();
		uint16_t            CalcNextAddrRegular();
		uint16_t            CalcNextAddrUNKNOWN();
		uint16_t            CalcNextAddrRTI();
		uint16_t            CalcNextAddrRTS();
		uint16_t            CalcNextAddrJMP();
		uint16_t            CalcNextAddrMARK();
		uint16_t            CalcNextAddrSOB();
		uint16_t            CalcNextAddrBR();
		uint16_t            CalcNextAddrBNE();
		uint16_t            CalcNextAddrBEQ();
		uint16_t            CalcNextAddrBGE();
		uint16_t            CalcNextAddrBLT();
		uint16_t            CalcNextAddrBGT();
		uint16_t            CalcNextAddrBLE();
		uint16_t            CalcNextAddrBPL();
		uint16_t            CalcNextAddrBMI();
		uint16_t            CalcNextAddrBHI();
		uint16_t            CalcNextAddrBLOS();
		uint16_t            CalcNextAddrBVC();
		uint16_t            CalcNextAddrBVS();
		uint16_t            CalcNextAddrBCC();
		uint16_t            CalcNextAddrBCS();
		uint16_t            CalcNextAddrSWAB_MFPS();
		uint16_t            CalcNextAddrMOVB();

		void                DisassembleNoArgs(CDbgItemParam &p);
		void                DisassembleRTI(CDbgItemParam &p);
		void                DisassembleUnknown(CDbgItemParam &p);
		void                DisassembleCLS(CDbgItemParam &p);
		void                DisassembleSET(CDbgItemParam &p);
		void                DisassembleRTS(CDbgItemParam &p);
		void                DisassembleTwoField(CDbgItemParam &p);
		void                DisassembleSWAB(CDbgItemParam &p);
		void                DisassembleMxPD(CDbgItemParam &p);
		void                DisassembleFourField(CDbgItemParam &p);
		void                DisassembleMARK(CDbgItemParam &p);
		void                DisassembleEMT(CDbgItemParam &p);
		void                DisassembleBR(CDbgItemParam &p);
		void                DisassembleBCC(CDbgItemParam &p);
		void                DisassembleBCS(CDbgItemParam &p);
		void                DisassembleJSR(CDbgItemParam &p);
		void                DisassembleEISExt(CDbgItemParam &p);
		void                DisassembleXOR(CDbgItemParam &p);
		void                DisassembleFIS(CDbgItemParam &p);
		void                DisassembleSOB(CDbgItemParam &p);
		void                DisassembleCMP(CDbgItemParam &p);
		void                DisassembleSUB(CDbgItemParam &p);
		void                DisassembleSETF(CDbgItemParam &p);
		void                DisassembleSETD(CDbgItemParam &p);
		void                DisassembleSETL(CDbgItemParam &p);
		void                DisassembleSETI(CDbgItemParam &p);
		void                DisassembleTwoFieldFPUD(CDbgItemParam &p);
		void                DisassembleACDFieldFPUD(CDbgItemParam &p);
		void                DisassembleACDFieldLDX(CDbgItemParam &p);
		void                DisassembleACDFieldLDEXP(CDbgItemParam &p);
		void                DisassembleACDFieldLDCJX(CDbgItemParam &p);
		void                DisassembleACDFieldLDCXY(CDbgItemParam &p);
		void                DisassembleACSFieldFPUD(CDbgItemParam &p);
		void                DisassembleACSFieldSTX(CDbgItemParam &p);
		void                DisassembleACSFieldSTEXP(CDbgItemParam &p);
		void                DisassembleACSFieldSTCXJ(CDbgItemParam &p);
		void                DisassembleACSFieldSTCXY(CDbgItemParam &p);

//////////////////////////////////////////////////////////////////////////
//
// Тут будет всё для ассемблирования команды.
//
//////////////////////////////////////////////////////////////////////////

		enum class CPUCmdGroup
		{
			NOOPS,
			CBRANCH,
			EIS,
			TRAP,
			SOB,
			MARK,
			TWOOPREG,
			FIS,
			PUSH,
			ONEOPS,
			TWOOPS,
			FPUCLASS1,
			FPUCLASS2,
			FPUCLASS2_1,
		};

		enum class OpcodeType
		{
			REGULAR,
			MMG,
			EIS,
			FIS,
			FPU
		};

		struct CPUCommandStruct
		{
			CString     strName;    // Имя мнемоники
			uint16_t    nOpcode;    // генерируемый опкод
			CPUCmdGroup nGroup;     // группа, к которой принадлежит команда
			OpcodeType  nType;      // тип инструкции, чтобы вык/выкл нужные группы инструкций
		};
		struct Registers
		{
			CString name;
			int nNum;
		};

		static const CPUCommandStruct m_pCPUCommands[];
		static const Registers m_pCPURegs[];
		static const Registers m_pFPURegs[];

		bool                m_bOperandType;

		bool                SkipWhitespace(TCHAR **pch) const;
		bool                ReadToken(CString &str, TCHAR **pch, const bool bNoSkip) const;
		bool                ReadOctalNumber(int &num, TCHAR **pch, const bool bNoSkip) const;
		bool                isEoln(TCHAR **pch) const;
		bool                NeedChar(TCHAR ch, TCHAR **pch, const bool bNoSkip) const;

		bool                assembleBR(int &pc, uint16_t *buf, int &len, TCHAR **pch) const;
		bool                assembleTRAP(uint16_t *buf, int &len, TCHAR **pch) const;
		bool                assembleMARK(uint16_t *buf, int &len, TCHAR **pch) const;
		bool                assemble2ROP(int &pc, uint16_t *buf, int &len, TCHAR **pch);
		bool                assemble1OP(int &pc, uint16_t *buf, int &len, TCHAR **pch) const;
		bool                assemble1OPFPU(int &pc, uint16_t *buf, int &len, TCHAR **pch) const;
		bool                assemble2OP(int &pc, uint16_t *buf, int &len, TCHAR **pch);
		bool                assemble1OPR(int &pc, uint16_t *buf, int &len, TCHAR **pch) const;
		bool                assemble1OPRFPU(int &pc, uint16_t *buf, int &len, TCHAR **pch) const;
		bool                assemble2OPR(int &pc, uint16_t *buf, int &len, TCHAR **pch);
		bool                assemble2OPRFPU(int &pc, uint16_t *buf, int &len, TCHAR **pch, bool bFPU);
		bool                assembleSOB(int &pc, uint16_t *buf, int &len, TCHAR **pch);
		bool                ReadRegName(uint16_t *buf, TCHAR **pch, const bool bNoSkip) const;
		bool                ReadFPURegName(uint16_t *buf, TCHAR **pch, bool bShort, const bool bNoSkip) const;
		void                SetAddrReg(uint16_t *buf, uint16_t val) const;
		bool                Operand_analyse(int &pc, uint16_t *buf, int &len, TCHAR **pch, bool bFPU) const;

	public:
		CDebugger();
		virtual ~CDebugger();
		void                InitMaps(bool bReinit = false);

		uint16_t            CalcNextAddr(const uint16_t pc);

		int                 CalcInstructionLength_i(const uint16_t instruction);
		int                 CalcInstructionLength_pc(const uint16_t pc);
		static void         InitOutMode()
		{
			m_outLevel.clear();
		}

		static void         SetDbgStepStatus(STEP_STATUS status)
		{
			m_StepStatus = status;
		}
		static STEP_STATUS  GetDbgStepStatus()
		{
			return m_StepStatus;
		}
		static void         SetIntrToInstructionOut(const DbgStackPoint_t &p);
		static bool         IsInstructionOut(const DbgStackPoint_t &p);
		int                 IsInstructionOver(const uint16_t instruction) const;

		// Breakpoint managment methods
		mutable std::mutex  m_mutBptLst; // для защиты списка
		bool                IsBpeakpointExists(const CBreakPoint &breakpoint) const;
		int                 IsBpeakpointAtAddress(const uint16_t addr) const;
		bool                IsBreakedPoints();
		void                DropShowCondBreakpoints();
		void                UnbreakBreakedBreakpoint(const uint16_t addr);
		void                CheckBpeakpointAtAddress(const uint16_t addr, const uint32_t pc);
		void                CheckBpeakpointMemAccess(const uint16_t addr, const uint32_t pc, const uint32_t nAccess);
		bool                SetMemAccessBreakpoint(const uint16_t addr, const uint32_t nAccess);
		bool                SetSimpleBreakpoint(const uint16_t addr);
		bool                SetSimpleBreakpoint();
		bool                RemoveBreakpoint(const uint16_t addr);
		bool                RemoveBreakpoint();
		void                ClearBreakpointList();
		CBreakPointList    *GetBreakpointListPtr()
		{
			return &m_breakpointList;
		}

		int                 DebugInstruction(const uint16_t pc, CDbgItemParam &p);

		int                 AssembleCPUInstruction(int pc, uint16_t *buf, CString *pStr);

		uint16_t            GetRegister(const CPUREG::REGS reg) const;
		uint16_t            GetPortValue(const int addr) const;
		uint16_t            GetDebugMemDumpWord(const uint16_t addr) const;
		uint8_t             GetDebugMemDumpByte(const uint16_t addr) const;
		uint16_t            GetAltProData(const int reg) const;
		uint16_t            GetFDDData(const int reg) const;
		uint16_t            GetOPTOKData(const int reg) const;
		uint16_t            GetDebugHDDRegs(const int nDrive, const int num, const bool bReadMode) const;
		uint16_t            GetDebugAZBKRegsWord(const uint16_t addr) const;
		uint8_t             GetDebugAZBKRegsByte(const uint16_t addr) const;

		bool                SetDebugRegs(const CPUREG::REGS reg, const uint16_t nValue) const;
		bool                SetDebugPorts(const uint16_t nAddress, const uint16_t nValue) const;
		bool                SetDebugMemDump(const uint16_t nAddress, const uint16_t nValue) const;
		bool                SetDebugMemDump(const uint16_t nAddress, const uint8_t nValue) const;
		bool                SetDebugAltProData(const uint16_t nAddress, const uint16_t nValue) const;
		bool                SetDebugAZBKRegs(const uint16_t nAddress, const uint8_t nValue) const;
		bool                SetDebugAZBKRegs(const uint16_t nAddress, const uint16_t nValue) const;

};



/*
Перерисовка окна дизассемблера.
всё начинается с функции CMotherBoard::BreakCPU()
    в ней посылается сообщение WM_CPU_DEBUGBREAK,
    в результате срабатывает функция CMainFrame::OnCpuBreak
        там берётся текущий PC и передаётся функции CDebugger::SetCurrentAddress
            этот адрес делается начальным и перерисовывается список дизассемблера

Просто, примитивно и неудобно.
особенно, работа с курсором.
 */