/*************************************************
*           BK Emulator v3.9 for Windows         *
*************************************************/

/*
File: CPU.h
Version: 1.0.0
Written by: Yuriy Kalmykov <kalmykov@stoik.com>
    Copyright (c) 2002-2004 Yuriy Kalmykov

    BK Emulator is a program emulated hardware environment for running
code for BK 0010(01) in different configurations.

    This code may be used in compiled form in any way you desire.
This file or it's parts can't be redistributed without the authors
written consent, but can be modified for your private needs.
    Providing that this notice and the authors name and all copyright
notices remains intact.

    Please, an email me to know how you are using it and where. You can
ask me for any information about this below code or any attendant
knowledge.

    This file is provided "as is" with no expressed or implied warranty.
The author accepts no liability for any damage or loss of business that
this product may cause.
*/


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


#pragma once


#include "Globals.h"
#include "Defines.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           1
#define PSW_V           2
#define PSW_Z           4
#define PSW_N           8
#define PSW_T           16
#define PSW_P           0200
//10й бит, маскирует IRQ1, IRQ2, IRQ3, VIRQ
#define PSW_MASKI       02000
//11й бит - маскирует только IRQ1
#define PSW_HALT        04000
/*
При установленном в 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,
			CPU_REGS_NUM
		};
	protected: // Statics
		typedef void (CCPU::*ExecuteMethodRef)();
		ExecuteMethodRef *m_pExecuteMethodMap;
		void            RegisterMethodRef(WORD start, WORD end, ExecuteMethodRef methodref);

		WORD            m_RON[CPU_REGS_NUM];
		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 void     SetRON(int reg, WORD value)
		{
			m_RON[reg] = value;
		}
		inline WORD     GetRON(int reg) const
		{
			return m_RON[reg];
		}

		//приём/передача внутренних системных регистров 177700-177712 от/в внешних(им) устройств (в MB функции)
		void        SetSysRegs(register WORD addr, register WORD value)
		{
			if (0177700 <= addr && addr < 0177714)
			{
				register int n = (addr >> 1) & 7;
				m_pSysRegs[n] = (m_pSysRegs[n] & ~m_pSysRegsMask[n]) | (value & m_pSysRegsMask[n]);

				//тут ещё и разные действия могут происходить, например
				if (n == 5)
				{
					//Если пишем в регистр управления таймером, то в счётчик сразу копируется начальное значение
					m_pSysRegs[SYS_REG_177710] = m_pSysRegs[SYS_REG_177706];
					m_nTimerSpeed = 0x40;
				}
			}
		}
		void        SetSysRegs(register WORD addr, register BYTE value)
		{
			if (0177700 <= addr && addr < 0177714)
			{
				register WORD v = (WORD)value;

				if (addr & 1)
				{
					v <<= 8;
				}

				SetSysRegs(addr, v);
			}
		}
		WORD        GetSysRegs(register WORD addr)
		{
			if (0177700 <= addr && addr < 0177714)
			{
				return m_pSysRegs[(addr >> 1) & 7];
			}

			return 0;
		}
		//простое сохранение значений регистров, используется во внутренних целях
		void        SetSysRegsInternal(register WORD addr, register WORD value)
		{
			if (0177700 <= addr && addr < 0177714)
			{
				register int n = (addr >> 1) & 7;
				m_pSysRegs[n] = value;
			}
		}
		void        SetSysRegsInternal(register WORD addr, register BYTE value)
		{
			if (0177700 <= addr && addr < 0177714)
			{
				register int n = (addr >> 1) & 7;
				register WORD v = (WORD)value;

				if (addr & 1)
				{
					v <<= 8;
					m_pSysRegs[n] &= 0377;
					m_pSysRegs[n] |= v;
				}
				else
				{
					m_pSysRegs[n] &= 0177400;
					m_pSysRegs[n] |= v;
				}
			}
		}

		inline WORD     GetCurrentInstruction()
		{
			return m_instruction;
		}

		bool            IsTrace();
};


inline void CCPU::SetC(register bool bFlag)
{
	if (bFlag)
	{
		m_RON[R_PSW] |= PSW_C;
	}
	else
	{
		m_RON[R_PSW] &= ~PSW_C;
	}
}


inline bool CCPU::GetC()
{
	return (m_RON[R_PSW] & PSW_C) != 0;
}


inline void CCPU::SetV(register bool bFlag)
{
	if (bFlag)
	{
		m_RON[R_PSW] |= PSW_V;
	}
	else
	{
		m_RON[R_PSW] &= ~PSW_V;
	}
}


inline bool CCPU::GetV()
{
	return (m_RON[R_PSW] & PSW_V) != 0;
}


inline void CCPU::SetZ(register bool bFlag)
{
	if (bFlag)
	{
		m_RON[R_PSW] |= PSW_Z;
	}
	else
	{
		m_RON[R_PSW] &= ~PSW_Z;
	}
}


inline bool CCPU::GetZ()
{
	return (m_RON[R_PSW] & PSW_Z) != 0;
}


inline void CCPU::SetN(register bool bFlag)
{
	if (bFlag)
	{
		m_RON[R_PSW] |= PSW_N;
	}
	else
	{
		m_RON[R_PSW] &= ~PSW_N;
	}
}


inline bool CCPU::GetN()
{
	return (m_RON[R_PSW] & PSW_N) != 0;
}

#pragma inline_recursion(off)

