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


#pragma once

#include "DisasmCtrl.h"
#include "BreakPoint.h"
#include "CPU.h"

#define COLORED_TAG "<HLC>"
#define COLORED_TAG_LENGTH 5


#define SRC             2
#define DST             0

enum : int
{
	SYS_PORT_177660 = 0,
	SYS_PORT_177662_IN,
	SYS_PORT_177662_OUT,
	SYS_PORT_177664,
	SYS_PORT_177700,
	SYS_PORT_177702,
	SYS_PORT_177704,
	SYS_PORT_177706,
	SYS_PORT_177710,
	SYS_PORT_177712,
	SYS_PORT_177714_IN,
	SYS_PORT_177714_OUT,
	SYS_PORT_177716_IN,
	SYS_PORT_177716_OUT_TAP,
	SYS_PORT_177716_OUT_MEM
};


enum {MARK_COLUMN = 0, ADDR_COLUMN, INSTR_COLUMN, ARG_COLUMN, COM_COLUMN};
//массив цветов для подсветки синтаксиса
enum { HLCOLOR_DEFAULT = 0, HLCOLOR_ADDRESS, HLCOLOR_MNEMONIC, HLCOLOR_REGISTER, HLCOLOR_NUMBER, HLCOLOR_SYMBOL };
extern const COLORREF g_crDebugColorHighLighting[];


class CMotherBoard;

class CDebugger
{
		using CalcInstrLenRef = int (CDebugger::*)(); //в качестве аргумента - значение в m_wInstr, результат - возвращаемое значение
		using CalcNextAddrRef = bool (CDebugger::*)(); //в качестве аргумента - значение в m_wInstr, результат - в m_wNextAddr, возвращает: true - m_wNextAddr актуально, false - нет
		using DisassembleInstrRef = int (CDebugger::*)(WORD *codes); //в качестве аргумента - значение в m_wInstr и m_wPC, результат - в m_strInstr и m_strArg, возвращает кол-во доп.слов инструкции
		struct InstrFuncRefs
		{
			CString Mnemonic;
			CalcInstrLenRef InstrLenRef;
			CalcNextAddrRef NextAddrRef;
			DisassembleInstrRef DisasmInstrRef;
		};
		InstrFuncRefs *m_pInstrRefsMap;
		void RegisterMethodRef(WORD start, WORD end, CString &mnemonic, CalcInstrLenRef ilenmref, CalcNextAddrRef nxamref, DisassembleInstrRef dsimref);
		void InitMaps();

		HICON               m_hBPIcon, m_hCurrIcon;

		static int          m_outLevel;
		static const CString m_strRegNames[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;

		CMotherBoard       *m_pChip;
		CDisasmCtrl        *m_pCtrl;
		CBreakPointList     m_breakpointList;

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

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

		WORD                m_wPC;          //текущий адрес текущей инструкции
		WORD                m_wInstr;       //сама инструкция
		WORD                m_wNextAddr;    //расчётный следующий адрес
		CString             m_strInstr;     //мнемоника
		CString             m_strArg;       //аргументы, если есть

		int                 ConvertArgToString(int arg, WORD pc, CString &strSrc, WORD &code);
		void                DrawMarker(WORD address, CDC *pDC, CRect &rc);

		// Breakpoint managment methods
		bool                IsBpeakpointExists(CBreakPoint &breakpoint);
		bool                IsBpeakpointAtAddress(WORD addr);

		int                 GetArg(int type);
		int                 GetArgAddr(int meth, int reg);
		int                 CalcArgLength(int pos);

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

		bool                CalcNextAddrRegular();
		bool                CalcNextAddrUNKNOWN();
		bool                CalcNextAddrRTI();
		bool                CalcNextAddrRTS();
		bool                CalcNextAddrJMP();
		bool                CalcNextAddrMARK();
		bool                CalcNextAddrSOB();

		bool                CalcNextAddrBR();
		bool                CalcNextAddrBNE();
		bool                CalcNextAddrBEQ();
		bool                CalcNextAddrBGE();
		bool                CalcNextAddrBLT();
		bool                CalcNextAddrBGT();
		bool                CalcNextAddrBLE();
		bool                CalcNextAddrBPL();
		bool                CalcNextAddrBMI();
		bool                CalcNextAddrBHI();
		bool                CalcNextAddrBLOS();
		bool                CalcNextAddrBVC();
		bool                CalcNextAddrBVS();
		bool                CalcNextAddrBCC();
		bool                CalcNextAddrBCS();

		int                 DisassembleNoArgs(WORD *codes);
		int                 DisassembleUnknown(WORD *codes);
		int                 DisassembleCLS(WORD *codes);
		int                 DisassembleSET(WORD *codes);
		int                 DisassembleRTS(WORD *codes);
		int                 DisassembleOneField(WORD *codes);
		int                 DisassembleTwoField(WORD *codes);
		int                 DisassembleFourField(WORD *codes);
		int                 DisassembleMARK(WORD *codes);
		int                 DisassembleEMT(WORD *codes);
		int                 DisassembleBR(WORD *codes);
		int                 DisassembleBCC(WORD *codes);
		int                 DisassembleBCS(WORD *codes);
		int                 DisassembleJSR(WORD *codes);
		int                 DisassembleMUL(WORD *codes);
		int                 DisassembleXOR(WORD *codes);
		int                 DisassembleSOB(WORD *codes);
		int                 DisassembleCMP(WORD *codes);

	public:
		CDebugger();
		virtual ~CDebugger();
		inline CMotherBoard *GetChip()
		{
			return m_pChip;
		}
		bool                GetDebugPCBreak(WORD addr);

		bool                CalcNextAddr(WORD pc, WORD &addr);

		int                 CalcInstructionLength(WORD instr);
		static void         InitOutMode()
		{
			m_outLevel = 0;
		}
		static bool         IsInstructionOut(WORD instruction);
		bool                IsInstructionOver(WORD instruction);

		void                AttachChip(CMotherBoard *pChip);
		inline void         AttachWnd(CDisasmCtrl *pCtrl)
		{
			m_pCtrl = pCtrl;
		}
		inline void         SetLinesNum(int nNum)
		{
			m_nLines = nNum;
		}
		inline int          GetLinesNum()
		{
			return m_nLines;
		}
		void                SetCurrentAddress(WORD address)
		{
			m_wAddress = address;
			m_pCtrl->Invalidate(FALSE);
		}
		inline WORD         GetCurrentAddress()
		{
			return m_wAddress;
		}

		WORD                GetLineAddress(int nNum);
		int                 DebugInstruction(WORD pc, CString &strInstr, CString &strArg, WORD *codes);
		void                DrawDebuggerLine(int nNum, CDC *pDC, CRect *pRcSubs);
		void                DrawColoredText(CDC *pDC, CRect &rect, CString &str);

		void                StepForward();
		void                StepBackward();

		WORD                GetCursorAddress();
		WORD                GetBottomAddress();

		WORD                GetRegister(int reg);
		WORD                GetPortValue(int addr);
		WORD                GetAltProData(int reg);
		WORD                GetFDDData(int reg);
		WORD                GetDebugHDDRegs(int nDrive, int num, bool bReadMode);
		WORD                GetDebugMemDumpWord(WORD addr);
		BYTE                GetDebugMemDumpByte(WORD addr);

		bool                OnDebugModify_Regs(int nAddress, WORD nValue);
		bool                OnDebugModify_Ports(int nAddress, WORD nValue);
		bool                OnDebugModify_Memory(int nAddress, WORD nValue);
		bool                OnDebugModify_Memory(int nAddress, BYTE nValue);
		bool                OnDebugModify_AltProData(int nAddress, WORD nValue);
		// Breakpoint managment methods
		bool                SetSimpleBreakpoint(WORD addr);
		bool                SetSimpleBreakpoint();
		bool                RemoveBreakpoint(WORD addr);
		bool                RemoveBreakpoint();
		void                ClearBreakpointList();
};

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

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