#pragma once
#include "defmacros.h"
#include "CommonBus.h"
#include "Debugger.h"

#define VIDEOLOG_ON 0
#define OUTLOG 0

//совмещённый ВП1-037 и ВП1-014

struct VP1_037_014_REGS
{
	//ВП1-037
	// ----
	//ВП1-014
	uint16_t Y_in;      //reg [7:1]
	bool    PRESS;      //reg
	bool    READY;
	bool    ready_fc;   //reg
	bool    BUSY;
	bool    release_fc; //reg


	void clear()
	{
		memset(this, 0, sizeof(VP1_037_014_REGS));
	};
	VP1_037_014_REGS &operator = (const VP1_037_014_REGS &src)
	{
		memcpy(this, &src, sizeof(VP1_037_014_REGS));
		return *this;
	};

	bool operator != (const VP1_037_014_REGS &src)
	{
		int n = memcmp(this, &src, sizeof(VP1_037_014_REGS));
		return (n != 0);
	};
	bool operator == (const VP1_037_014_REGS &src)
	{
		int n = memcmp(this, &src, sizeof(VP1_037_014_REGS));
		return (n == 0);
	};
};

//input-output, набор сигналов устройства, которых нет в МПИ, но которые есть у устройства
struct VP1_037_014_IO
{
	//ВП1-037
	uint16_t PIN_A;         //output[6:0]
	uint16_t PIN_CAS;       //output[1:0]
	bool    PIN_RAS;        //output не нужен
	bool    PIN_WE;         //output
	bool    PIN_WTI;        //output
	bool    PIN_WTD;        //output
	bool    PIN_SINCO;      //output

	bool    PIN_nE;         //output      выборка верхних адресов 177600 по чтению (пока единственный инверсный выход)

	//ВП1-014
	uint16_t PIN_Y;         //input[7:0] на входе число 0..7 - номер линии Y, если 0 - не задействована ни одна линия (т.к. Y[0] в модели не используется)
	uint16_t PIN_X;         //input[9:0] на входе число 0..9 - номер линии X, если -1 (т.е. >= 10) - не задействована ни одна линия

	bool    PIN_CTRL;       //input
	bool    PIN_SHIFT;      //input
	bool    PIN_EC1;        //input вход переключателя Загл./Стр.
	bool    PIN_EC2;        //input вход АР2

	bool    PIN_RP1;        //input
	bool    PIN_RP2;        //input

	//выходные сигналы для МПИ и обшей шины
	CCommonBus::CMNB_DATASET PIN_nAD;
	CCommonBus::CMNB_LINESET PIN_nRPLY;
	CCommonBus::CMNB_LINESET PIN_nVIRQ;
	CCommonBus::CMNB_LINESET PIN_nIAKO;
	//дополнительно для БК11
	CCommonBus::CMNB_LINESET PIN_nIRQ2;  //выдача прерывания 50Гц.


	void clear()
	{
		memset(this, 0, sizeof(VP1_037_014_IO));
		PIN_X = -1; //для X 0, означает, что нажата клавиша из линии 0, поэтому начальное значение должно быть >=10
		//выходные сигналы - инверсные
	};
};

class CScreen;

class CVP1_037_014
{
	protected:
		CCommonBus *m_pCMNB;        //указатель на внешний объект МПИ
		uint32_t m_nClk;
		uint16_t m_nStartAddress;
		//wires !С целью симуляции псевдослучайных состояний сигналов, умышленно используем неинициализированные переменные
		//ВП1-037
		bool    wire_sync_in, wire_sync_in_prev, wire_sync_in_rise, wire_sync_in_fall;
		bool    wire_din_in, wire_din_in_prev, wire_din_in_rise, wire_din_in_fall;
		bool    wire_dout_in, wire_dout_in_prev, wire_dout_in_rise, wire_dout_in_fall;
		bool    wire_bus_io;        //wire      флаг наличия операции чтения/записи
//		bool    wire_rply_out;      //wire      флаг выдачи RPLY при операциях с памятью
		bool    wire_rply_reg;      //wire      флаг выдачи RPLY при операциях с регистрами
		//ВП1-014
		bool    wire_iaki_in;       //wire
		bool    wire_init_in;       //wire
		bool    wire_irq_ena;       //wire

		uint16_t wire_X;            //wire [9:0]
		uint16_t wire_Y;            //wire [7:1]

		enum CHIP_REG {NO_REG, CSR_REG, DAT_REG, SCR_REG, SEL1_REG};

		CHIP_REG wire_RegNum, wire_RegRD, wire_RegWR;
		bool    wire_IACK;          //wire
		bool    wire_iako_out;

		//regs, которые можно так использовать, они используются только в assignах
		// в принципе, так можно почти все регистры использовать, но придётся тщательно следить
		// за порядком расположения конструкций в alwaysах, что немного напряжно
		//ВП1-037
		uint16_t reg_A;             //reg [15:0]   Q-bus address latch
		bool    reg_wtbt_rc;        //reg       BYTE flag
		bool    reg_VTSYN;          //reg
		uint16_t reg_VA;            //reg [13:1]   video address counter
		uint16_t reg_LC;            //reg [7:0]    Декрементный счётчик строк
		uint16_t reg_RA;            //reg [7:0] 177664 register start address
		uint16_t reg_M256;          //reg       1/4 screen mode
		uint16_t reg_PC;            //reg [2:0]    pixel counter
		bool    reg_RASEL;          //reg
		bool    reg_TRPLY;
		int     nHGateCounter;      //флаг-счётчик служебных слов в строке.
		int     nVGateCounter;      //флаг-счётчик служебных строк.
		//ВП1-014
		bool    reg_regRus;         //reg //регистр раскладки РУС/ЛАТ изначально работать не должен! как в оригинале.
		uint16_t reg_data;          //reg [6:0]
		uint8_t reg_vect_rc;        //reg //тип вектора прерывания - 0 - 060, 0200 - 0274
		bool    reg_IEN;            //reg   разрешение прерывания от клавиатуры
		bool    reg_irq_rc;         //reg
		bool    reg_irq_fc;         //reg

		//доп. флаги и переменные
		bool    m_bBusOut, m_bRegScrReaded;
		bool    m_bRP1_in_prev;
		bool    m_bRP2_in_prev;
		//регистры
		VP1_037_014_REGS    reg, snap_reg;

		/*преобразование числа в битовый массив
		вход: число
		указатель на массив
		ко-во бит
		*/
		template <typename T> void data2bita(T d, bool *pBa, int nLen)
		{
			register T dd = d;

			for (register int i = 0; i < nLen; ++i)
			{
				*pBa++ = !!(dd & 1);
				dd >>= 1;
			}
		}

		//и наоборот
		template <typename T> void bita2data(bool *pBa, T &d, int nLen)
		{
			register T dd = 0;
			pBa += nLen;

			for (register int i = 0; i < nLen; ++i)
			{
				dd <<= 1;

				if (*--pBa)
				{
					dd++;
				}
			}

			d = dd;
		}


		uint8_t        *m_pCombo;
		void            calculateCombo();

		virtual void    assign_in();
		virtual void    assign_func();

		virtual void    assign_out();

		virtual void    always_all();
		virtual void    always_a();
		virtual void    always_posedge_clk();
		virtual void    always_negedge_clk();

		virtual void    always_posedge_reset();

		void            assign_in_kbd();
		void            always_all_kbd();

		// ------- модель памяти ------------

		uint16_t       *m_pMemory; //память 128кб
		uint16_t        m_nMemProcAddress;
		uint16_t        m_nMemProcOperation; //тип операции: 0 - нет операции, 1 - мл. байт, 2 - ст. байт, 3 - слово

		enum MEM_STATES
		{
			MS_WAIT_CAS,
			MS_WAIT_WRITE_CAS_CANCEL,
			MS_OPERATE,
			MS_WAIT_CAS_CANCEL
		};

		MEM_STATES      m_nMemProcState;
		bool            m_bWait_WTD;

		// --------- модель экрана, неотделима от памяти.

		bool            m_bVSynced;    //флаг синхронизации
		bool            m_bDrawing;    //флаг вывода данных на экран
		int             m_nSincoLength; //длина синхроимпульса
		int             m_nVideoStringCount; //счётчик видеострок в PAL кадре
		int             m_nLineNum; //счётчик строк экрана 0..255
		int             m_nLineByteNum; //счётчик байтов экрана 0..63

	public:
		CVP1_037_014();
		virtual ~CVP1_037_014();

		VP1_037_014_IO  m_IOPins;   //предполагается, что данные на ножках будут меняться извне, перед вызовами eval_p, eval_n
		//а обрабатываться - кем-то внешним, после этих вызовов.

		virtual void    RegisterDevice(CCommonBus *pMPI); //регистрация устройства на шине МПИ
		virtual void    eval_p(int nClk);  //обработка переднего фронта
		virtual void    eval_n(int nClk);  //обработка заднего фронта
		virtual void    eval(int nClk);    //упрощенный вариант для вызова раз в такт
		virtual void    memPostProc(CScreen *pScr);
		virtual void    videoProc(CScreen *pScr);

//---------------- тестовая часть ------------------------
		void AttachDebugger(CDebugger *pDbg)
		{
			pDbg->AttachMemoryVP1037(reinterpret_cast<uint8_t *>(m_pMemory));
		}
		FILE *pLog;
		FILE *pVideoLog;
#if (OUTLOG)
		virtual void OutLog(int nClk, const char *str = "");
#endif
};


//обвязка вокруг ВП1-037 на БК11
//гораздо проще её внедрить внутрь ВП1-037, возни меньше

//временная тестовая структура, чтобы было  откуда данные брать
struct VP1_037_014_IO_BK11
{
	uint16_t REG_177716Mem;
	uint16_t reg_177662W; //регистр 177662 по записи
};


class CVP1_037_014_BK11 : public CVP1_037_014
{
	protected:
		//wires дополнительные
		bool            wire_D28C_A;
		bool            wire_D28C_A_prev;
		bool            m_bIOPIN_SINCO_prev;
		//regs дополнительные
		bool            reg_D28Q_A;
		uint16_t        reg_Window; //адрес окна БК11, в которое попадает текущий обрабатываемый адрес

		virtual void    assign_out() override;
		virtual void    always_all() override;
		virtual void    always_a() override;
		virtual void    always_posedge_clk() override;

	public:
		CVP1_037_014_BK11();
		virtual ~CVP1_037_014_BK11() override;

		VP1_037_014_IO_BK11 m_IOPinsBK11;
#if (OUTLOG)
		virtual void OutLog(int nClk, const char *str = "") override;
#endif
};

