// CPU.h: interface for the CCPU class.
//


#pragma once


#include "Config.h"
#include <queue>
using namespace std;
#define SWAP_BYTE(A) ((((A) & 0xFF00) >> 8) | (((A) & 0x00FF) << 8))

/*формат регистра PSW: PPPTNZVC
PPP - приоритет
если приоритет установленный на данный момент для ЦП ниже, чем установленный приоритет
ЗАПРОСА НА ПРЕРЫВАНИЕ, то ЦП прервет работу и обработает прерывание.
Если же приоритет текущей задачи равен или выше приоритета запроса, то ЦП сперва закончит вычисления,
а затем обработает прерывание
У приоритета реально используется только бит 7. остальные биты ни на что не влияют.
T - флаг трассировки
N - отрицательность
Z - ноль
V - арифметический перенос, т.е. при операции со знаковыми числами, происходит перенос в знаковый разряд,
который самый старший
C - перенос, который происходит за пределы разрядности операндов
*/

// PSW bits
#define PSW_C_POS           0
#define PSW_V_POS           1
#define PSW_Z_POS           2
#define PSW_N_POS           3
#define PSW_T_POS           4
#define PSW_P_POS           7
//10й бит, маскирует IRQ1, IRQ2, IRQ3, VIRQ
#define PSW_MASKI_POS       10
//11й бит - маскирует только IRQ1
#define PSW_HALT_POS        11
/*
При установленном в PSW бите 10 запрещены все прерывания, включая IRQ1.
Кроме того, при установленном бите 10 прерывание зависания вместо Trap_to_4
вызывает прерывание особого вида - HALT_Trap.

При установленном бите 11 запрещено только прерывание IRQ1, но данный
режим реализован немного кривовато, так как сбрасывается командами RTI и RTT
(которые всегда пишут 0 в старший байт PSW), а значит имеет смысл только при
дополнительно запрещённых обычных прерываниях и не использовании программных прерываний.
*/
class CMotherBoard;

#pragma inline_recursion(on)

class CCPU
{
		static const int timing_Misk_10[13];
		static const int timing_OneOps_TST_10[8];
		static const int timing_OneOps_CLR_10[8];
		static const int timing_OneOps_MTPS_10[8];
		static const int timing_OneOps_XOR_10[8];
		static const int timing_OneOps_JMP_10[8];
		static const int timing_OneOps_JSR_10[8];
		static const int timing_TwoOps_MOV_10[8][8];
		static const int timing_TwoOps_CMP_10[8][8];
		static const int timing_TwoOps_BIS_10[8][8];

		static const int timing_Misk_11[13];
		static const int timing_OneOps_TST_11[8];
		static const int timing_OneOps_CLR_11[8];
		static const int timing_OneOps_MTPS_11[8];
		static const int timing_OneOps_XOR_11[8];
		static const int timing_OneOps_JMP_11[8];
		static const int timing_OneOps_JSR_11[8];
		static const int timing_TwoOps_MOV_11[8][8];
		static const int timing_TwoOps_CMP_11[8][8];
		static const int timing_TwoOps_BIS_11[8][8];

		const int *timing_Misk;
		const int *timing_OneOps_TST;
		const int *timing_OneOps_CLR;
		const int *timing_OneOps_MTPS;
		const int *timing_OneOps_XOR;
		const int *timing_OneOps_JMP;
		const int *timing_OneOps_JSR;
		const int *timing_TwoOps_MOV;
		const int *timing_TwoOps_CMP;
		const int *timing_TwoOps_BIS;


		CMotherBoard   *m_pChip;
		int             m_nInternalTick; // количество тактов, выполняемой инструкции
		int             m_nCmdTicks;     //счётчик тактов для обработки встроенного таймера
		enum CPU_PORTS
		{
			SYS_REG_177700,
			SYS_REG_177702,
			SYS_REG_177704,
			SYS_REG_177706,
			SYS_REG_177710,
			SYS_REG_177712,
			SYS_REG_NUM
		};
	public:
		enum CPU_REGS
		{
			R_R0,
			R_R1,
			R_R2,
			R_R3,
			R_R4,
			R_R5,
			R_SP,
			R_PC,
			R_PSW
		};
	protected: // Statics
		using ExecuteMethodRef = void (CCPU::*)();
		ExecuteMethodRef *m_pExecuteMethodMap;
		void            RegisterMethodRef(WORD start, WORD end, ExecuteMethodRef methodref);

		WORD            m_RON[R_PSW];
		bool            m_PSW[16];
		WORD            m_pSysRegs[SYS_REG_NUM];   //массив внутренних системных регистров процессора 1777700..1777712
		WORD            m_pSysRegsMask[SYS_REG_NUM]; //массив масок битов, которые доступны по записи

		WORD            m_instruction;  //текущая инструкция
		WORD            m_nSrcAddr;     //адрес источника
		WORD            m_nDstAddr;     //адрес приёмника
		DWORD           m_datarg;       //аргумент источника
		DWORD           m_ALU;          //приёмник
		DWORD           m_Nbit;
		bool            m_bSign;
		int             m_nMethSrc;
		int             m_nMethDst;
		int             m_nRegSrc;
		int             m_nRegDst;


		bool            m_bTrace_RTT;
		bool            m_bWaitMode;    // WAIT
		bool            m_bStepMode;
		bool            m_bIRQ1rq;      // импульс ~\_ для срабатывания IRQ1
		bool            m_bIRQ1LL;      // кнопка СТОП
		bool            m_bGetVector;   // отлов ошибки передачи вектора.
		bool            m_bTwiceHangup; // отлов двойного зависания - это когда при прерывании по вектору 4 при записи в стек случается m_bRPLYrq

		bool            m_bRPLYrq;      // Прерывание по зависанию - отсутствие ответа RPLY
		bool            m_bRSVDrq;      // Прерывание по резервной инструкции
		bool            m_bTBITrq;      // Прерывание по T-биту
		bool            m_bACLOrq;      // Прерывание по аварии питания аналогового
		bool            m_bDCLOrq;      // Прерывание по аварии питания цифрового
		bool            m_bHALTrq;      // Прерывание по команде HALT
		bool            m_bErrInstrRq;  // прерывание по неправильной команде (jmp r0 или jsr r0,r0)
		bool            m_bRPL2rq;      // Прерывание по двойному зависанию
		bool            m_bVIRQErrrq;   // Прерывание по ошибке передачи вектора
		bool            m_bIRQ2rq;      // Прерывание от таймера 50Гц
		bool            m_bIRQ3rq;      // Прерывание по вектору 270
		bool            m_bBPTrq;       // Прерывание по команде BPT
		bool            m_bIOTrq;       // Прерывание по команде IOT
		bool            m_bEMTrq;       // Прерывание по команде EMT
		bool            m_bTRAPrq;      // Прерывание по команде TRAP
		queue<WORD>     m_qVIRQ;        // очередь векторов прерываний VIRQ, это излишество, но всё же, пусть будет

		int             m_nTimerSpeed;   //скорость счётчика

		//функции чтения/записи слова/байта с перехватом адресов внутренних системных регистров,
		//дальше CPU они не идут, поэтому нужны функции получения их значений.
		BYTE            GetByte(register WORD addr);
		WORD            GetWord(register WORD addr);
		void            SetByte(register WORD addr, register BYTE value);
		void            SetWord(register WORD addr, register WORD value);

		void            get_src_arg();
		void            get_dst_arg();
		void            set_dst_arg();
		void            get_src_addr();
		void            get_dst_addr();
		WORD            get_arg_addr(int meth, int reg);

		inline void     Set_NZ()
		{
			//установка N
			SetN((m_ALU & m_Nbit) != 0);

			//установка Z
			if (m_bSign)
			{
				SetZ(LOBYTE(m_ALU) == 0);
			}
			else
			{
				SetZ(LOWORD(m_ALU) == 0);
			}
		}
		inline void     Set_V(register DWORD old)
		{
			//установка V - это смена значения знакового бита. с 0 на 1 (для сложений)
			SetV((~old & m_ALU & m_Nbit) != 0);
		}
		inline void     Set_IV(register DWORD old)
		{
			//установка V - это смена значения знакового бита. с 1 на 0 (для вычитаний)
			SetV((old & ~m_ALU & m_Nbit) != 0);
		}
		inline void     Set_C()
		{
			//установка C
			SetC((m_ALU & ((m_bSign) ? 0xffffff00 : 0xffff0000)) != 0);
		}

		void            SetC(bool bFlag);
		bool            GetC();
		void            SetV(bool bFlag);
		bool            GetV();
		void            SetN(bool bFlag);
		bool            GetN();
		void            SetZ(bool bFlag);
		bool            GetZ();

		void            ExecuteUNKNOWN();
		// No fields
		void            ExecuteHALT();
		void            ExecuteWAIT();
		void            ExecuteRTI();
		void            ExecuteBPT();
		void            ExecuteIOT();
		void            ExecuteRESET();
		void            ExecuteRTT();
		void            ExecuteSTART();
		void            ExecuteSTEP();
		void            ExecuteCLS();
		void            ExecuteSET();

		// One fields
		void            ExecuteRTS();

		// Two fields
		void            ExecuteJMP();
		void            ExecuteSWAB();
		void            ExecuteCLR();
		void            ExecuteCOM();
		void            ExecuteINC();
		void            ExecuteDEC();
		void            ExecuteNEG();
		void            ExecuteADC();
		void            ExecuteSBC();
		void            ExecuteTST();
		void            ExecuteROR();
		void            ExecuteROL();
		void            ExecuteASR();
		void            ExecuteASL();
		void            ExecuteMARK();
		void            ExecuteSXT();
		void            ExecuteMTPS();
		void            ExecuteMFPS();

		// Branches & interrupts
		void            ExecuteBR();
		void            ExecuteBNE();
		void            ExecuteBEQ();
		void            ExecuteBGE();
		void            ExecuteBLT();
		void            ExecuteBGT();
		void            ExecuteBLE();
		void            ExecuteBPL();
		void            ExecuteBMI();
		void            ExecuteBHI();
		void            ExecuteBLOS();
		void            ExecuteBVC();
		void            ExecuteBVS();
		void            ExecuteBHIS();
		void            ExecuteBLO();

		void            ExecuteEMT();
		void            ExecuteTRAP();

		// Three fields
		void            ExecuteJSR();
		void            ExecuteXOR();
		void            ExecuteSOB();

		// Four fields
		void            ExecuteMOV();
		void            ExecuteCMP();
		void            ExecuteBIT();
		void            ExecuteBIC();
		void            ExecuteBIS();

		void            ExecuteADD();
		void            ExecuteSUB();

	public:
		CCPU();
		virtual ~CCPU();
		void            InitVars(); // инициализация переменных
		void            InitCPU();  // Инициализация таблицы декодироания команд
		void            DoneCPU();  // удаление таблицы декодироания команд и освобождение памяти

		void            ResetCPU(); //инициализация внутренних системных регистров, PSW, PC

		void            Timerprocess();  // внутренний таймер
		void            ResetTimer();  // инициализация таймера
		inline int      GetTimerSpeedInternal()
		{
			return m_nTimerSpeed;
		}
		inline void     SetTimerSpeedInternal(int nTSpd)
		{
			m_nTimerSpeed = nTSpd;
		}
		void            AttachChip(CMotherBoard *pChip);

		int             TranslateInstruction();
		inline bool     IsWaitMode()
		{
			return m_bWaitMode;
		}
		inline void     SetIRQ1()
		{
			//обработка нажатия на кнопку СТОП, или каким либо другим способом установка сигнала в IRQ1
			if (!m_bIRQ1LL)
			{
				m_bIRQ1LL = true;
				m_bIRQ1rq = true;
			}
		}
		inline void     UnsetIRQ1()
		{
			m_bIRQ1LL = false;
		}

		inline void     TickIRQ2()
		{
			m_bIRQ2rq = true;
		}
		inline void     TickIRQ3()
		{
			m_bIRQ3rq = true;
		}
		inline void     ACPowerFail()
		{
			m_bACLOrq = true;
		}
		inline void     DCPowerFail()
		{
			m_bDCLOrq = true;
		}
		void            ReplyError();
		void            InterruptVIRQ(WORD interrupt);

		//приём/передача регистров общего назначения и PSW от/в внешних(им) устройств (в MB функции)

		inline bool     GetPSWBit(int pos)
		{
			return m_PSW[pos];
		}
		inline void     SetPSWBit(int pos, bool val)
		{
			m_PSW[pos] = val;
		}
		WORD            GetPSW() const
		{
			register WORD w = 0;

			for (int i = 7; i >= 0; i--)
			{
				w <<= 1;

				if (m_PSW[i])
				{
					w++;
				}
			}

			if (m_PSW[PSW_MASKI_POS])
			{
				w |= 1 << PSW_MASKI_POS;
			}

			if (m_PSW[PSW_HALT_POS])
			{
				w |= 1 << PSW_HALT_POS;
			}

			return w;
		}
		void            SetPSW(register WORD value)
		{
			for (int i = 0; i < 8; ++i)
			{
				m_PSW[i] = value & 1;
				value >>= 1;
			}

			m_PSW[PSW_MASKI_POS] = (value >> (PSW_MASKI_POS - 8)) & 1;
			m_PSW[PSW_HALT_POS] = (value >> (PSW_HALT_POS - 8)) & 1;
		}
		inline void     SetRON(int reg, WORD value)
		{
			if (reg == R_PSW)
			{
				SetPSW(value);
			}
			else
			{
				m_RON[reg] = value;
			}
		}
		inline WORD     GetRON(int reg) const
		{
			if (reg == R_PSW)
			{
				return GetPSW();
			}

			return m_RON[reg];
		}

		//приём/передача внутренних системных регистров 177700-177712 от/к внешних(им) устройств(ам) (в MB функции)
		WORD            GetSysRegs(register WORD addr);
		void            SetSysRegs(register WORD addr, register WORD value);
		void            SetSysRegs(register WORD addr, register BYTE value);
		//простое сохранение значений регистров, используется во внутренних целях
		void            SetSysRegsInternal(register WORD addr, register WORD value);
		void            SetSysRegsInternal(register WORD addr, register BYTE value);

		inline WORD     GetCurrentInstruction()
		{
			return m_instruction;
		}

		bool            IsTrace();
};


inline void CCPU::SetC(register bool bFlag)
{
	m_PSW[PSW_C_POS] = bFlag;
}


inline bool CCPU::GetC()
{
	return m_PSW[PSW_C_POS];
}


inline void CCPU::SetV(register bool bFlag)
{
	m_PSW[PSW_V_POS] = bFlag;
}


inline bool CCPU::GetV()
{
	return m_PSW[PSW_V_POS];
}


inline void CCPU::SetZ(register bool bFlag)
{
	m_PSW[PSW_Z_POS] = bFlag;
}


inline bool CCPU::GetZ()
{
	return m_PSW[PSW_Z_POS];
}


inline void CCPU::SetN(register bool bFlag)
{
	m_PSW[PSW_N_POS] = bFlag;
}


inline bool CCPU::GetN()
{
	return m_PSW[PSW_N_POS];
}

#pragma inline_recursion(off)

